Separate gpio driver from pinctrl driver, and support v2 controller.
Jianqun Xu (7):
pinctrl/rockchip: separate struct rockchip_pin_bank to a head file
pinctrl/pinctrl-rockchip.h: add pinctrl device to gpio bank struct
gpio: separate gpio driver from pinctrl-rockchip driver
gpio/rockchip: use struct rockchip_gpio_regs for gpio controller
gpio/rockchip: support next version gpio controller
gpio/rockchip: always enable clock for gpio controller
gpio/rockchip: drop irq_gc_lock/irq_gc_unlock for irq set type
drivers/gpio/Kconfig | 8 +
drivers/gpio/Makefile | 1 +
drivers/gpio/gpio-rockchip.c | 758 ++++++++++++++++++++++++
drivers/pinctrl/pinctrl-rockchip.c | 910 +----------------------------
drivers/pinctrl/pinctrl-rockchip.h | 287 +++++++++
5 files changed, 1073 insertions(+), 891 deletions(-)
create mode 100644 drivers/gpio/gpio-rockchip.c
create mode 100644 drivers/pinctrl/pinctrl-rockchip.h
--
v5:
- change to devel branch
2.25.1
Separate the gpio driver from the pinctrl driver.
Signed-off-by: Jianqun Xu <[email protected]>
---
drivers/gpio/Kconfig | 8 +
drivers/gpio/Makefile | 1 +
drivers/gpio/gpio-rockchip.c | 657 +++++++++++++++++++++++++++
drivers/pinctrl/pinctrl-rockchip.c | 684 +----------------------------
4 files changed, 684 insertions(+), 666 deletions(-)
create mode 100644 drivers/gpio/gpio-rockchip.c
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index e3607ec4c2e8..702fb6db3e46 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -508,6 +508,14 @@ config GPIO_REG
A 32-bit single register GPIO fixed in/out implementation. This
can be used to represent any register as a set of GPIO signals.
+config GPIO_ROCKCHIP
+ tristate "Rockchip GPIO support"
+ depends on ARCH_ROCKCHIP || COMPILE_TEST
+ select GPIOLIB_IRQCHIP
+ default ARCH_ROCKCHIP
+ help
+ Say yes here to support GPIO on Rockchip SoCs.
+
config GPIO_SAMA5D2_PIOBU
tristate "SAMA5D2 PIOBU GPIO support"
depends on MFD_SYSCON
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index c58a90a3c3b1..1432694f3b3a 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -125,6 +125,7 @@ obj-$(CONFIG_GPIO_RCAR) += gpio-rcar.o
obj-$(CONFIG_GPIO_RDA) += gpio-rda.o
obj-$(CONFIG_GPIO_RDC321X) += gpio-rdc321x.o
obj-$(CONFIG_GPIO_REG) += gpio-reg.o
+obj-$(CONFIG_GPIO_ROCKCHIP) += gpio-rockchip.o
obj-$(CONFIG_ARCH_SA1100) += gpio-sa1100.o
obj-$(CONFIG_GPIO_SAMA5D2_PIOBU) += gpio-sama5d2-piobu.o
obj-$(CONFIG_GPIO_SCH311X) += gpio-sch311x.o
diff --git a/drivers/gpio/gpio-rockchip.c b/drivers/gpio/gpio-rockchip.c
new file mode 100644
index 000000000000..03a3d251faae
--- /dev/null
+++ b/drivers/gpio/gpio-rockchip.c
@@ -0,0 +1,657 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2013 MundoReader S.L.
+ * Author: Heiko Stuebner <[email protected]>
+ *
+ * Copyright (c) 2021 Rockchip Electronics Co. Ltd.
+ */
+
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/gpio/driver.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+#include <linux/regmap.h>
+
+#include "../pinctrl/core.h"
+#include "../pinctrl/pinctrl-rockchip.h"
+
+/* GPIO control registers */
+#define GPIO_SWPORT_DR 0x00
+#define GPIO_SWPORT_DDR 0x04
+#define GPIO_INTEN 0x30
+#define GPIO_INTMASK 0x34
+#define GPIO_INTTYPE_LEVEL 0x38
+#define GPIO_INT_POLARITY 0x3c
+#define GPIO_INT_STATUS 0x40
+#define GPIO_INT_RAWSTATUS 0x44
+#define GPIO_DEBOUNCE 0x48
+#define GPIO_PORTS_EOI 0x4c
+#define GPIO_EXT_PORT 0x50
+#define GPIO_LS_SYNC 0x60
+
+static int rockchip_gpio_get_direction(struct gpio_chip *chip,
+ unsigned int offset)
+{
+ struct rockchip_pin_bank *bank = gpiochip_get_data(chip);
+ u32 data;
+ int ret;
+
+ ret = clk_enable(bank->clk);
+ if (ret < 0) {
+ dev_err(bank->drvdata->dev,
+ "failed to enable clock for bank %s\n", bank->name);
+ return ret;
+ }
+ data = readl_relaxed(bank->reg_base + GPIO_SWPORT_DDR);
+ clk_disable(bank->clk);
+
+ if (data & BIT(offset))
+ return GPIO_LINE_DIRECTION_OUT;
+
+ return GPIO_LINE_DIRECTION_IN;
+}
+
+static int rockchip_gpio_set_direction(struct gpio_chip *chip,
+ unsigned int offset, bool input)
+{
+ struct rockchip_pin_bank *bank = gpiochip_get_data(chip);
+ unsigned long flags;
+ u32 data;
+
+ clk_enable(bank->clk);
+ raw_spin_lock_irqsave(&bank->slock, flags);
+
+ data = readl_relaxed(bank->reg_base + GPIO_SWPORT_DDR);
+ /* set bit to 1 for output, 0 for input */
+ if (!input)
+ data |= BIT(offset);
+ else
+ data &= ~BIT(offset);
+ writel_relaxed(data, bank->reg_base + GPIO_SWPORT_DDR);
+
+ raw_spin_unlock_irqrestore(&bank->slock, flags);
+ clk_disable(bank->clk);
+
+ return 0;
+}
+
+static void rockchip_gpio_set(struct gpio_chip *gc, unsigned int offset,
+ int value)
+{
+ struct rockchip_pin_bank *bank = gpiochip_get_data(gc);
+ void __iomem *reg = bank->reg_base + GPIO_SWPORT_DR;
+ unsigned long flags;
+ u32 data;
+
+ clk_enable(bank->clk);
+ raw_spin_lock_irqsave(&bank->slock, flags);
+
+ data = readl(reg);
+ data &= ~BIT(offset);
+ if (value)
+ data |= BIT(offset);
+ writel(data, reg);
+
+ raw_spin_unlock_irqrestore(&bank->slock, flags);
+ clk_disable(bank->clk);
+}
+
+static int rockchip_gpio_get(struct gpio_chip *gc, unsigned int offset)
+{
+ struct rockchip_pin_bank *bank = gpiochip_get_data(gc);
+ u32 data;
+
+ clk_enable(bank->clk);
+ data = readl(bank->reg_base + GPIO_EXT_PORT);
+ clk_disable(bank->clk);
+ data >>= offset;
+ data &= 1;
+ return data;
+}
+
+static void rockchip_gpio_set_debounce(struct gpio_chip *gc,
+ unsigned int offset, bool enable)
+{
+ struct rockchip_pin_bank *bank = gpiochip_get_data(gc);
+ void __iomem *reg = bank->reg_base + GPIO_DEBOUNCE;
+ unsigned long flags;
+ u32 data;
+
+ clk_enable(bank->clk);
+ raw_spin_lock_irqsave(&bank->slock, flags);
+
+ data = readl(reg);
+ if (enable)
+ data |= BIT(offset);
+ else
+ data &= ~BIT(offset);
+ writel(data, reg);
+
+ raw_spin_unlock_irqrestore(&bank->slock, flags);
+ clk_disable(bank->clk);
+}
+
+static int rockchip_gpio_direction_input(struct gpio_chip *gc,
+ unsigned int offset)
+{
+ return rockchip_gpio_set_direction(gc, offset, true);
+}
+
+static int rockchip_gpio_direction_output(struct gpio_chip *gc,
+ unsigned int offset, int value)
+{
+ rockchip_gpio_set(gc, offset, value);
+
+ return rockchip_gpio_set_direction(gc, offset, false);
+}
+
+/*
+ * gpiolib set_config callback function. The setting of the pin
+ * mux function as 'gpio output' will be handled by the pinctrl subsystem
+ * interface.
+ */
+static int rockchip_gpio_set_config(struct gpio_chip *gc, unsigned int offset,
+ unsigned long config)
+{
+ enum pin_config_param param = pinconf_to_config_param(config);
+
+ switch (param) {
+ case PIN_CONFIG_INPUT_DEBOUNCE:
+ rockchip_gpio_set_debounce(gc, offset, true);
+ /*
+ * Rockchip's gpio could only support up to one period
+ * of the debounce clock(pclk), which is far away from
+ * satisftying the requirement, as pclk is usually near
+ * 100MHz shared by all peripherals. So the fact is it
+ * has crippled debounce capability could only be useful
+ * to prevent any spurious glitches from waking up the system
+ * if the gpio is conguired as wakeup interrupt source. Let's
+ * still return -ENOTSUPP as before, to make sure the caller
+ * of gpiod_set_debounce won't change its behaviour.
+ */
+ return -ENOTSUPP;
+ default:
+ return -ENOTSUPP;
+ }
+}
+
+/*
+ * gpiolib gpio_to_irq callback function. Creates a mapping between a GPIO pin
+ * and a virtual IRQ, if not already present.
+ */
+static int rockchip_gpio_to_irq(struct gpio_chip *gc, unsigned int offset)
+{
+ struct rockchip_pin_bank *bank = gpiochip_get_data(gc);
+ unsigned int virq;
+
+ if (!bank->domain)
+ return -ENXIO;
+
+ clk_enable(bank->clk);
+ virq = irq_create_mapping(bank->domain, offset);
+ clk_disable(bank->clk);
+
+ return (virq) ? : -ENXIO;
+}
+
+static const struct gpio_chip rockchip_gpiolib_chip = {
+ .request = gpiochip_generic_request,
+ .free = gpiochip_generic_free,
+ .set = rockchip_gpio_set,
+ .get = rockchip_gpio_get,
+ .get_direction = rockchip_gpio_get_direction,
+ .direction_input = rockchip_gpio_direction_input,
+ .direction_output = rockchip_gpio_direction_output,
+ .set_config = rockchip_gpio_set_config,
+ .to_irq = rockchip_gpio_to_irq,
+ .owner = THIS_MODULE,
+};
+
+static void rockchip_irq_demux(struct irq_desc *desc)
+{
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+ struct rockchip_pin_bank *bank = irq_desc_get_handler_data(desc);
+ u32 pend;
+
+ dev_dbg(bank->dev, "got irq for bank %s\n", bank->name);
+
+ chained_irq_enter(chip, desc);
+
+ pend = readl_relaxed(bank->reg_base + GPIO_INT_STATUS);
+
+ while (pend) {
+ unsigned int irq, virq;
+
+ irq = __ffs(pend);
+ pend &= ~BIT(irq);
+ virq = irq_find_mapping(bank->domain, irq);
+
+ if (!virq) {
+ dev_err(bank->dev, "unmapped irq %d\n", irq);
+ continue;
+ }
+
+ dev_dbg(bank->dev, "handling irq %d\n", irq);
+
+ /*
+ * Triggering IRQ on both rising and falling edge
+ * needs manual intervention.
+ */
+ if (bank->toggle_edge_mode & BIT(irq)) {
+ u32 data, data_old, polarity;
+ unsigned long flags;
+
+ data = readl_relaxed(bank->reg_base + GPIO_EXT_PORT);
+ do {
+ raw_spin_lock_irqsave(&bank->slock, flags);
+
+ polarity = readl_relaxed(bank->reg_base +
+ GPIO_INT_POLARITY);
+ if (data & BIT(irq))
+ polarity &= ~BIT(irq);
+ else
+ polarity |= BIT(irq);
+ writel(polarity,
+ bank->reg_base + GPIO_INT_POLARITY);
+
+ raw_spin_unlock_irqrestore(&bank->slock, flags);
+
+ data_old = data;
+ data = readl_relaxed(bank->reg_base +
+ GPIO_EXT_PORT);
+ } while ((data & BIT(irq)) != (data_old & BIT(irq)));
+ }
+
+ generic_handle_irq(virq);
+ }
+
+ chained_irq_exit(chip, desc);
+}
+
+static int rockchip_irq_set_type(struct irq_data *d, unsigned int type)
+{
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+ struct rockchip_pin_bank *bank = gc->private;
+ u32 mask = BIT(d->hwirq);
+ u32 polarity;
+ u32 level;
+ u32 data;
+ unsigned long flags;
+
+ clk_enable(bank->clk);
+ raw_spin_lock_irqsave(&bank->slock, flags);
+
+ data = readl_relaxed(bank->reg_base + GPIO_SWPORT_DDR);
+ data &= ~mask;
+ writel_relaxed(data, bank->reg_base + GPIO_SWPORT_DDR);
+
+ raw_spin_unlock_irqrestore(&bank->slock, flags);
+
+ if (type & IRQ_TYPE_EDGE_BOTH)
+ irq_set_handler_locked(d, handle_edge_irq);
+ else
+ irq_set_handler_locked(d, handle_level_irq);
+
+ raw_spin_lock_irqsave(&bank->slock, flags);
+ irq_gc_lock(gc);
+
+ level = readl_relaxed(gc->reg_base + GPIO_INTTYPE_LEVEL);
+ polarity = readl_relaxed(gc->reg_base + GPIO_INT_POLARITY);
+
+ switch (type) {
+ case IRQ_TYPE_EDGE_BOTH:
+ bank->toggle_edge_mode |= mask;
+ level |= mask;
+
+ /*
+ * Determine gpio state. If 1 next interrupt should be falling
+ * otherwise rising.
+ */
+ data = readl(bank->reg_base + GPIO_EXT_PORT);
+ if (data & mask)
+ polarity &= ~mask;
+ else
+ polarity |= mask;
+ break;
+ case IRQ_TYPE_EDGE_RISING:
+ bank->toggle_edge_mode &= ~mask;
+ level |= mask;
+ polarity |= mask;
+ break;
+ case IRQ_TYPE_EDGE_FALLING:
+ bank->toggle_edge_mode &= ~mask;
+ level |= mask;
+ polarity &= ~mask;
+ break;
+ case IRQ_TYPE_LEVEL_HIGH:
+ bank->toggle_edge_mode &= ~mask;
+ level &= ~mask;
+ polarity |= mask;
+ break;
+ case IRQ_TYPE_LEVEL_LOW:
+ bank->toggle_edge_mode &= ~mask;
+ level &= ~mask;
+ polarity &= ~mask;
+ break;
+ default:
+ irq_gc_unlock(gc);
+ raw_spin_unlock_irqrestore(&bank->slock, flags);
+ clk_disable(bank->clk);
+ return -EINVAL;
+ }
+
+ writel_relaxed(level, gc->reg_base + GPIO_INTTYPE_LEVEL);
+ writel_relaxed(polarity, gc->reg_base + GPIO_INT_POLARITY);
+
+ irq_gc_unlock(gc);
+ raw_spin_unlock_irqrestore(&bank->slock, flags);
+ clk_disable(bank->clk);
+
+ return 0;
+}
+
+static void rockchip_irq_suspend(struct irq_data *d)
+{
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+ struct rockchip_pin_bank *bank = gc->private;
+
+ clk_enable(bank->clk);
+ bank->saved_masks = irq_reg_readl(gc, GPIO_INTMASK);
+ irq_reg_writel(gc, ~gc->wake_active, GPIO_INTMASK);
+ clk_disable(bank->clk);
+}
+
+static void rockchip_irq_resume(struct irq_data *d)
+{
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+ struct rockchip_pin_bank *bank = gc->private;
+
+ clk_enable(bank->clk);
+ irq_reg_writel(gc, bank->saved_masks, GPIO_INTMASK);
+ clk_disable(bank->clk);
+}
+
+static void rockchip_irq_enable(struct irq_data *d)
+{
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+ struct rockchip_pin_bank *bank = gc->private;
+
+ clk_enable(bank->clk);
+ irq_gc_mask_clr_bit(d);
+}
+
+static void rockchip_irq_disable(struct irq_data *d)
+{
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+ struct rockchip_pin_bank *bank = gc->private;
+
+ irq_gc_mask_set_bit(d);
+ clk_disable(bank->clk);
+}
+
+static int rockchip_interrupts_register(struct rockchip_pin_bank *bank)
+{
+ unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN;
+ struct irq_chip_generic *gc;
+ int ret;
+
+ ret = clk_enable(bank->clk);
+ if (ret) {
+ dev_err(bank->dev, "failed to enable clock for bank %s\n",
+ bank->name);
+ return -EINVAL;
+ }
+
+ bank->domain = irq_domain_add_linear(bank->of_node, 32,
+ &irq_generic_chip_ops, NULL);
+ if (!bank->domain) {
+ dev_warn(bank->dev, "could not init irq domain for bank %s\n",
+ bank->name);
+ clk_disable(bank->clk);
+ return -EINVAL;
+ }
+
+ ret = irq_alloc_domain_generic_chips(bank->domain, 32, 1,
+ "rockchip_gpio_irq",
+ handle_level_irq,
+ clr, 0, 0);
+ if (ret) {
+ dev_err(bank->dev, "could not alloc generic chips for bank %s\n",
+ bank->name);
+ irq_domain_remove(bank->domain);
+ clk_disable(bank->clk);
+ return -EINVAL;
+ }
+
+ gc = irq_get_domain_generic_chip(bank->domain, 0);
+ gc->reg_base = bank->reg_base;
+ gc->private = bank;
+ gc->chip_types[0].regs.mask = GPIO_INTMASK;
+ gc->chip_types[0].regs.ack = GPIO_PORTS_EOI;
+ gc->chip_types[0].chip.irq_ack = irq_gc_ack_set_bit;
+ gc->chip_types[0].chip.irq_mask = irq_gc_mask_set_bit;
+ gc->chip_types[0].chip.irq_unmask = irq_gc_mask_clr_bit;
+ gc->chip_types[0].chip.irq_enable = rockchip_irq_enable;
+ gc->chip_types[0].chip.irq_disable = rockchip_irq_disable;
+ gc->chip_types[0].chip.irq_set_wake = irq_gc_set_wake;
+ gc->chip_types[0].chip.irq_suspend = rockchip_irq_suspend;
+ gc->chip_types[0].chip.irq_resume = rockchip_irq_resume;
+ gc->chip_types[0].chip.irq_set_type = rockchip_irq_set_type;
+ gc->wake_enabled = IRQ_MSK(bank->nr_pins);
+
+ /*
+ * Linux assumes that all interrupts start out disabled/masked.
+ * Our driver only uses the concept of masked and always keeps
+ * things enabled, so for us that's all masked and all enabled.
+ */
+ writel_relaxed(0xffffffff, bank->reg_base + GPIO_INTMASK);
+ writel_relaxed(0xffffffff, bank->reg_base + GPIO_PORTS_EOI);
+ writel_relaxed(0xffffffff, bank->reg_base + GPIO_INTEN);
+ gc->mask_cache = 0xffffffff;
+
+ irq_set_chained_handler_and_data(bank->irq,
+ rockchip_irq_demux, bank);
+ clk_disable(bank->clk);
+
+ return 0;
+}
+
+static int rockchip_gpiolib_register(struct rockchip_pin_bank *bank)
+{
+ struct gpio_chip *gc;
+ int ret;
+
+ bank->gpio_chip = rockchip_gpiolib_chip;
+
+ gc = &bank->gpio_chip;
+ gc->base = bank->pin_base;
+ gc->ngpio = bank->nr_pins;
+ gc->label = bank->name;
+ gc->parent = bank->dev;
+#ifdef CONFIG_OF_GPIO
+ gc->of_node = of_node_get(bank->of_node);
+#endif
+
+ ret = gpiochip_add_data(gc, bank);
+ if (ret) {
+ dev_err(bank->dev, "failed to add gpiochip %s, %d\n",
+ gc->label, ret);
+ return ret;
+ }
+
+ /*
+ * For DeviceTree-supported systems, the gpio core checks the
+ * pinctrl's device node for the "gpio-ranges" property.
+ * If it is present, it takes care of adding the pin ranges
+ * for the driver. In this case the driver can skip ahead.
+ *
+ * In order to remain compatible with older, existing DeviceTree
+ * files which don't set the "gpio-ranges" property or systems that
+ * utilize ACPI the driver has to call gpiochip_add_pin_range().
+ */
+ if (!of_property_read_bool(bank->of_node, "gpio-ranges")) {
+ struct device_node *pctlnp = of_get_parent(bank->of_node);
+ struct pinctrl_dev *pctldev = NULL;
+
+ if (!pctlnp)
+ return -ENODATA;
+
+ pctldev = of_pinctrl_get(pctlnp);
+ if (!pctldev)
+ return -ENODEV;
+
+ ret = gpiochip_add_pin_range(gc, dev_name(pctldev->dev), 0,
+ gc->base, gc->ngpio);
+ if (ret) {
+ dev_err(bank->dev, "Failed to add pin range\n");
+ goto fail;
+ }
+ }
+
+ ret = rockchip_interrupts_register(bank);
+ if (ret) {
+ dev_err(bank->dev, "failed to register interrupt, %d\n", ret);
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ gpiochip_remove(&bank->gpio_chip);
+
+ return ret;
+}
+
+static int rockchip_get_bank_data(struct rockchip_pin_bank *bank)
+{
+ struct resource res;
+
+ if (of_address_to_resource(bank->of_node, 0, &res)) {
+ dev_err(bank->dev, "cannot find IO resource for bank\n");
+ return -ENOENT;
+ }
+
+ bank->reg_base = devm_ioremap_resource(bank->dev, &res);
+ if (IS_ERR(bank->reg_base))
+ return PTR_ERR(bank->reg_base);
+
+ bank->irq = irq_of_parse_and_map(bank->of_node, 0);
+
+ bank->clk = of_clk_get(bank->of_node, 0);
+ if (!IS_ERR(bank->clk))
+ return clk_prepare(bank->clk);
+
+ bank->clk = NULL;
+ return 0;
+}
+
+static struct rockchip_pin_bank *
+rockchip_gpio_find_bank(struct pinctrl_dev *pctldev, const char *name)
+{
+ struct rockchip_pinctrl *info;
+ struct rockchip_pin_bank *bank;
+ int i, found = 0;
+
+ info = pinctrl_dev_get_drvdata(pctldev);
+ bank = info->ctrl->pin_banks;
+ for (i = 0; i < info->ctrl->nr_banks; i++, bank++) {
+ if (!strcmp(bank->name, name)) {
+ found = 1;
+ break;
+ }
+ }
+
+ return found ? bank : NULL;
+}
+
+static int rockchip_gpio_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct device_node *pctlnp = of_get_parent(np);
+ struct pinctrl_dev *pctldev = NULL;
+ struct rockchip_pin_bank *bank = NULL;
+ int ret;
+
+ if (!np || !pctlnp)
+ return -ENODEV;
+
+ pctldev = of_pinctrl_get(pctlnp);
+ if (!pctldev)
+ return -EPROBE_DEFER;
+
+ bank = rockchip_gpio_find_bank(pctldev, np->name);
+ if (!bank)
+ return -EINVAL;
+
+ bank->dev = dev;
+ bank->of_node = np;
+
+ raw_spin_lock_init(&bank->slock);
+
+ ret = rockchip_get_bank_data(bank);
+ if (ret)
+ return ret;
+
+ ret = rockchip_gpiolib_register(bank);
+ if (ret) {
+ clk_disable_unprepare(bank->clk);
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, bank);
+ dev_info(dev, "probed %pOF\n", np);
+
+ return 0;
+}
+
+static int rockchip_gpio_remove(struct platform_device *pdev)
+{
+ struct rockchip_pin_bank *bank = platform_get_drvdata(pdev);
+
+ clk_disable_unprepare(bank->clk);
+ gpiochip_remove(&bank->gpio_chip);
+
+ return 0;
+}
+
+static const struct of_device_id rockchip_gpio_match[] = {
+ { .compatible = "rockchip,gpio-bank", },
+ { .compatible = "rockchip,rk3188-gpio-bank0" },
+ { },
+};
+
+static struct platform_driver rockchip_gpio_driver = {
+ .probe = rockchip_gpio_probe,
+ .remove = rockchip_gpio_remove,
+ .driver = {
+ .name = "rockchip-gpio",
+ .of_match_table = rockchip_gpio_match,
+ },
+};
+
+static int __init rockchip_gpio_init(void)
+{
+ return platform_driver_register(&rockchip_gpio_driver);
+}
+postcore_initcall(rockchip_gpio_init);
+
+static void __exit rockchip_gpio_exit(void)
+{
+ platform_driver_unregister(&rockchip_gpio_driver);
+}
+module_exit(rockchip_gpio_exit);
+
+MODULE_DESCRIPTION("Rockchip gpio driver");
+MODULE_ALIAS("platform:rockchip-gpio");
+MODULE_LICENSE("GPL v2");
+MODULE_DEVICE_TABLE(of, rockchip_gpio_match);
diff --git a/drivers/pinctrl/pinctrl-rockchip.c b/drivers/pinctrl/pinctrl-rockchip.c
index ae88045dc3e4..27ad2d58af34 100644
--- a/drivers/pinctrl/pinctrl-rockchip.c
+++ b/drivers/pinctrl/pinctrl-rockchip.c
@@ -20,9 +20,9 @@
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/bitops.h>
-#include <linux/gpio/driver.h>
-#include <linux/of_device.h>
+#include <linux/gpio.h>
#include <linux/of_address.h>
+#include <linux/of_device.h>
#include <linux/of_irq.h>
#include <linux/pinctrl/machine.h>
#include <linux/pinctrl/pinconf.h>
@@ -39,19 +39,6 @@
#include "pinconf.h"
#include "pinctrl-rockchip.h"
-/* GPIO control registers */
-#define GPIO_SWPORT_DR 0x00
-#define GPIO_SWPORT_DDR 0x04
-#define GPIO_INTEN 0x30
-#define GPIO_INTMASK 0x34
-#define GPIO_INTTYPE_LEVEL 0x38
-#define GPIO_INT_POLARITY 0x3c
-#define GPIO_INT_STATUS 0x40
-#define GPIO_INT_RAWSTATUS 0x44
-#define GPIO_DEBOUNCE 0x48
-#define GPIO_PORTS_EOI 0x4c
-#define GPIO_EXT_PORT 0x50
-#define GPIO_LS_SYNC 0x60
/**
* Generate a bitmask for setting a value (v) with a write mask bit in hiword
@@ -2071,86 +2058,11 @@ static int rockchip_pmx_set(struct pinctrl_dev *pctldev, unsigned selector,
return 0;
}
-static int rockchip_gpio_get_direction(struct gpio_chip *chip, unsigned offset)
-{
- struct rockchip_pin_bank *bank = gpiochip_get_data(chip);
- u32 data;
- int ret;
-
- ret = clk_enable(bank->clk);
- if (ret < 0) {
- dev_err(bank->drvdata->dev,
- "failed to enable clock for bank %s\n", bank->name);
- return ret;
- }
- data = readl_relaxed(bank->reg_base + GPIO_SWPORT_DDR);
- clk_disable(bank->clk);
-
- if (data & BIT(offset))
- return GPIO_LINE_DIRECTION_OUT;
-
- return GPIO_LINE_DIRECTION_IN;
-}
-
-/*
- * The calls to gpio_direction_output() and gpio_direction_input()
- * leads to this function call (via the pinctrl_gpio_direction_{input|output}()
- * function called from the gpiolib interface).
- */
-static int _rockchip_pmx_gpio_set_direction(struct gpio_chip *chip,
- int pin, bool input)
-{
- struct rockchip_pin_bank *bank;
- int ret;
- unsigned long flags;
- u32 data;
-
- bank = gpiochip_get_data(chip);
-
- ret = rockchip_set_mux(bank, pin, RK_FUNC_GPIO);
- if (ret < 0)
- return ret;
-
- clk_enable(bank->clk);
- raw_spin_lock_irqsave(&bank->slock, flags);
-
- data = readl_relaxed(bank->reg_base + GPIO_SWPORT_DDR);
- /* set bit to 1 for output, 0 for input */
- if (!input)
- data |= BIT(pin);
- else
- data &= ~BIT(pin);
- writel_relaxed(data, bank->reg_base + GPIO_SWPORT_DDR);
-
- raw_spin_unlock_irqrestore(&bank->slock, flags);
- clk_disable(bank->clk);
-
- return 0;
-}
-
-static int rockchip_pmx_gpio_set_direction(struct pinctrl_dev *pctldev,
- struct pinctrl_gpio_range *range,
- unsigned offset, bool input)
-{
- struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
- struct gpio_chip *chip;
- int pin;
-
- chip = range->gc;
- pin = offset - chip->base;
- dev_dbg(info->dev, "gpio_direction for pin %u as %s-%d to %s\n",
- offset, range->name, pin, input ? "input" : "output");
-
- return _rockchip_pmx_gpio_set_direction(chip, offset - chip->base,
- input);
-}
-
static const struct pinmux_ops rockchip_pmx_ops = {
.get_functions_count = rockchip_pmx_get_funcs_count,
.get_function_name = rockchip_pmx_get_func_name,
.get_function_groups = rockchip_pmx_get_groups,
.set_mux = rockchip_pmx_set,
- .gpio_set_direction = rockchip_pmx_gpio_set_direction,
};
/*
@@ -2181,15 +2093,13 @@ static bool rockchip_pinconf_pull_valid(struct rockchip_pin_ctrl *ctrl,
return false;
}
-static void rockchip_gpio_set(struct gpio_chip *gc, unsigned offset, int value);
-static int rockchip_gpio_get(struct gpio_chip *gc, unsigned offset);
-
/* set the pin config settings for a specified pin */
static int rockchip_pinconf_set(struct pinctrl_dev *pctldev, unsigned int pin,
unsigned long *configs, unsigned num_configs)
{
struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
struct rockchip_pin_bank *bank = pin_to_bank(info, pin);
+ struct gpio_chip *gpio = &bank->gpio_chip;
enum pin_config_param param;
u32 arg;
int i;
@@ -2222,10 +2132,13 @@ static int rockchip_pinconf_set(struct pinctrl_dev *pctldev, unsigned int pin,
return rc;
break;
case PIN_CONFIG_OUTPUT:
- rockchip_gpio_set(&bank->gpio_chip,
- pin - bank->pin_base, arg);
- rc = _rockchip_pmx_gpio_set_direction(&bank->gpio_chip,
- pin - bank->pin_base, false);
+ rc = rockchip_set_mux(bank, pin - bank->pin_base,
+ RK_FUNC_GPIO);
+ if (rc != RK_FUNC_GPIO)
+ return -EINVAL;
+
+ rc = gpio->direction_output(gpio, pin - bank->pin_base,
+ arg);
if (rc)
return rc;
break;
@@ -2263,6 +2176,7 @@ static int rockchip_pinconf_get(struct pinctrl_dev *pctldev, unsigned int pin,
{
struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
struct rockchip_pin_bank *bank = pin_to_bank(info, pin);
+ struct gpio_chip *gpio = &bank->gpio_chip;
enum pin_config_param param = pinconf_to_config_param(*config);
u16 arg;
int rc;
@@ -2291,7 +2205,7 @@ static int rockchip_pinconf_get(struct pinctrl_dev *pctldev, unsigned int pin,
if (rc != RK_FUNC_GPIO)
return -EINVAL;
- rc = rockchip_gpio_get(&bank->gpio_chip, pin - bank->pin_base);
+ rc = gpio->get(gpio, pin - bank->pin_base);
if (rc < 0)
return rc;
@@ -2529,7 +2443,7 @@ static int rockchip_pinctrl_register(struct platform_device *pdev,
ctrldesc->npins = info->ctrl->nr_pins;
pdesc = pindesc;
- for (bank = 0 , k = 0; bank < info->ctrl->nr_banks; bank++) {
+ for (bank = 0, k = 0; bank < info->ctrl->nr_banks; bank++) {
pin_bank = &info->ctrl->pin_banks[bank];
for (pin = 0; pin < pin_bank->nr_pins; pin++, k++) {
pdesc->number = k;
@@ -2549,553 +2463,9 @@ static int rockchip_pinctrl_register(struct platform_device *pdev,
return PTR_ERR(info->pctl_dev);
}
- for (bank = 0; bank < info->ctrl->nr_banks; ++bank) {
- pin_bank = &info->ctrl->pin_banks[bank];
- pin_bank->grange.name = pin_bank->name;
- pin_bank->grange.id = bank;
- pin_bank->grange.pin_base = pin_bank->pin_base;
- pin_bank->grange.base = pin_bank->gpio_chip.base;
- pin_bank->grange.npins = pin_bank->gpio_chip.ngpio;
- pin_bank->grange.gc = &pin_bank->gpio_chip;
- pinctrl_add_gpio_range(info->pctl_dev, &pin_bank->grange);
- }
-
- return 0;
-}
-
-/*
- * GPIO handling
- */
-
-static void rockchip_gpio_set(struct gpio_chip *gc, unsigned offset, int value)
-{
- struct rockchip_pin_bank *bank = gpiochip_get_data(gc);
- void __iomem *reg = bank->reg_base + GPIO_SWPORT_DR;
- unsigned long flags;
- u32 data;
-
- clk_enable(bank->clk);
- raw_spin_lock_irqsave(&bank->slock, flags);
-
- data = readl(reg);
- data &= ~BIT(offset);
- if (value)
- data |= BIT(offset);
- writel(data, reg);
-
- raw_spin_unlock_irqrestore(&bank->slock, flags);
- clk_disable(bank->clk);
-}
-
-/*
- * Returns the level of the pin for input direction and setting of the DR
- * register for output gpios.
- */
-static int rockchip_gpio_get(struct gpio_chip *gc, unsigned offset)
-{
- struct rockchip_pin_bank *bank = gpiochip_get_data(gc);
- u32 data;
-
- clk_enable(bank->clk);
- data = readl(bank->reg_base + GPIO_EXT_PORT);
- clk_disable(bank->clk);
- data >>= offset;
- data &= 1;
- return data;
-}
-
-/*
- * gpiolib gpio_direction_input callback function. The setting of the pin
- * mux function as 'gpio input' will be handled by the pinctrl subsystem
- * interface.
- */
-static int rockchip_gpio_direction_input(struct gpio_chip *gc, unsigned offset)
-{
- return pinctrl_gpio_direction_input(gc->base + offset);
-}
-
-/*
- * gpiolib gpio_direction_output callback function. The setting of the pin
- * mux function as 'gpio output' will be handled by the pinctrl subsystem
- * interface.
- */
-static int rockchip_gpio_direction_output(struct gpio_chip *gc,
- unsigned offset, int value)
-{
- rockchip_gpio_set(gc, offset, value);
- return pinctrl_gpio_direction_output(gc->base + offset);
-}
-
-static void rockchip_gpio_set_debounce(struct gpio_chip *gc,
- unsigned int offset, bool enable)
-{
- struct rockchip_pin_bank *bank = gpiochip_get_data(gc);
- void __iomem *reg = bank->reg_base + GPIO_DEBOUNCE;
- unsigned long flags;
- u32 data;
-
- clk_enable(bank->clk);
- raw_spin_lock_irqsave(&bank->slock, flags);
-
- data = readl(reg);
- if (enable)
- data |= BIT(offset);
- else
- data &= ~BIT(offset);
- writel(data, reg);
-
- raw_spin_unlock_irqrestore(&bank->slock, flags);
- clk_disable(bank->clk);
-}
-
-/*
- * gpiolib set_config callback function. The setting of the pin
- * mux function as 'gpio output' will be handled by the pinctrl subsystem
- * interface.
- */
-static int rockchip_gpio_set_config(struct gpio_chip *gc, unsigned int offset,
- unsigned long config)
-{
- enum pin_config_param param = pinconf_to_config_param(config);
-
- switch (param) {
- case PIN_CONFIG_INPUT_DEBOUNCE:
- rockchip_gpio_set_debounce(gc, offset, true);
- /*
- * Rockchip's gpio could only support up to one period
- * of the debounce clock(pclk), which is far away from
- * satisftying the requirement, as pclk is usually near
- * 100MHz shared by all peripherals. So the fact is it
- * has crippled debounce capability could only be useful
- * to prevent any spurious glitches from waking up the system
- * if the gpio is conguired as wakeup interrupt source. Let's
- * still return -ENOTSUPP as before, to make sure the caller
- * of gpiod_set_debounce won't change its behaviour.
- */
- return -ENOTSUPP;
- default:
- return -ENOTSUPP;
- }
-}
-
-/*
- * gpiolib gpio_to_irq callback function. Creates a mapping between a GPIO pin
- * and a virtual IRQ, if not already present.
- */
-static int rockchip_gpio_to_irq(struct gpio_chip *gc, unsigned offset)
-{
- struct rockchip_pin_bank *bank = gpiochip_get_data(gc);
- unsigned int virq;
-
- if (!bank->domain)
- return -ENXIO;
-
- clk_enable(bank->clk);
- virq = irq_create_mapping(bank->domain, offset);
- clk_disable(bank->clk);
-
- return (virq) ? : -ENXIO;
-}
-
-static const struct gpio_chip rockchip_gpiolib_chip = {
- .request = gpiochip_generic_request,
- .free = gpiochip_generic_free,
- .set = rockchip_gpio_set,
- .get = rockchip_gpio_get,
- .get_direction = rockchip_gpio_get_direction,
- .direction_input = rockchip_gpio_direction_input,
- .direction_output = rockchip_gpio_direction_output,
- .set_config = rockchip_gpio_set_config,
- .to_irq = rockchip_gpio_to_irq,
- .owner = THIS_MODULE,
-};
-
-/*
- * Interrupt handling
- */
-
-static void rockchip_irq_demux(struct irq_desc *desc)
-{
- struct irq_chip *chip = irq_desc_get_chip(desc);
- struct rockchip_pin_bank *bank = irq_desc_get_handler_data(desc);
- u32 pend;
-
- dev_dbg(bank->drvdata->dev, "got irq for bank %s\n", bank->name);
-
- chained_irq_enter(chip, desc);
-
- pend = readl_relaxed(bank->reg_base + GPIO_INT_STATUS);
-
- while (pend) {
- unsigned int irq, virq;
-
- irq = __ffs(pend);
- pend &= ~BIT(irq);
- virq = irq_find_mapping(bank->domain, irq);
-
- if (!virq) {
- dev_err(bank->drvdata->dev, "unmapped irq %d\n", irq);
- continue;
- }
-
- dev_dbg(bank->drvdata->dev, "handling irq %d\n", irq);
-
- /*
- * Triggering IRQ on both rising and falling edge
- * needs manual intervention.
- */
- if (bank->toggle_edge_mode & BIT(irq)) {
- u32 data, data_old, polarity;
- unsigned long flags;
-
- data = readl_relaxed(bank->reg_base + GPIO_EXT_PORT);
- do {
- raw_spin_lock_irqsave(&bank->slock, flags);
-
- polarity = readl_relaxed(bank->reg_base +
- GPIO_INT_POLARITY);
- if (data & BIT(irq))
- polarity &= ~BIT(irq);
- else
- polarity |= BIT(irq);
- writel(polarity,
- bank->reg_base + GPIO_INT_POLARITY);
-
- raw_spin_unlock_irqrestore(&bank->slock, flags);
-
- data_old = data;
- data = readl_relaxed(bank->reg_base +
- GPIO_EXT_PORT);
- } while ((data & BIT(irq)) != (data_old & BIT(irq)));
- }
-
- generic_handle_irq(virq);
- }
-
- chained_irq_exit(chip, desc);
-}
-
-static int rockchip_irq_set_type(struct irq_data *d, unsigned int type)
-{
- struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
- struct rockchip_pin_bank *bank = gc->private;
- u32 mask = BIT(d->hwirq);
- u32 polarity;
- u32 level;
- u32 data;
- unsigned long flags;
- int ret;
-
- /* make sure the pin is configured as gpio input */
- ret = rockchip_set_mux(bank, d->hwirq, RK_FUNC_GPIO);
- if (ret < 0)
- return ret;
-
- clk_enable(bank->clk);
- raw_spin_lock_irqsave(&bank->slock, flags);
-
- data = readl_relaxed(bank->reg_base + GPIO_SWPORT_DDR);
- data &= ~mask;
- writel_relaxed(data, bank->reg_base + GPIO_SWPORT_DDR);
-
- raw_spin_unlock_irqrestore(&bank->slock, flags);
-
- if (type & IRQ_TYPE_EDGE_BOTH)
- irq_set_handler_locked(d, handle_edge_irq);
- else
- irq_set_handler_locked(d, handle_level_irq);
-
- raw_spin_lock_irqsave(&bank->slock, flags);
- irq_gc_lock(gc);
-
- level = readl_relaxed(gc->reg_base + GPIO_INTTYPE_LEVEL);
- polarity = readl_relaxed(gc->reg_base + GPIO_INT_POLARITY);
-
- switch (type) {
- case IRQ_TYPE_EDGE_BOTH:
- bank->toggle_edge_mode |= mask;
- level |= mask;
-
- /*
- * Determine gpio state. If 1 next interrupt should be falling
- * otherwise rising.
- */
- data = readl(bank->reg_base + GPIO_EXT_PORT);
- if (data & mask)
- polarity &= ~mask;
- else
- polarity |= mask;
- break;
- case IRQ_TYPE_EDGE_RISING:
- bank->toggle_edge_mode &= ~mask;
- level |= mask;
- polarity |= mask;
- break;
- case IRQ_TYPE_EDGE_FALLING:
- bank->toggle_edge_mode &= ~mask;
- level |= mask;
- polarity &= ~mask;
- break;
- case IRQ_TYPE_LEVEL_HIGH:
- bank->toggle_edge_mode &= ~mask;
- level &= ~mask;
- polarity |= mask;
- break;
- case IRQ_TYPE_LEVEL_LOW:
- bank->toggle_edge_mode &= ~mask;
- level &= ~mask;
- polarity &= ~mask;
- break;
- default:
- irq_gc_unlock(gc);
- raw_spin_unlock_irqrestore(&bank->slock, flags);
- clk_disable(bank->clk);
- return -EINVAL;
- }
-
- writel_relaxed(level, gc->reg_base + GPIO_INTTYPE_LEVEL);
- writel_relaxed(polarity, gc->reg_base + GPIO_INT_POLARITY);
-
- irq_gc_unlock(gc);
- raw_spin_unlock_irqrestore(&bank->slock, flags);
- clk_disable(bank->clk);
-
- return 0;
-}
-
-static void rockchip_irq_suspend(struct irq_data *d)
-{
- struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
- struct rockchip_pin_bank *bank = gc->private;
-
- clk_enable(bank->clk);
- bank->saved_masks = irq_reg_readl(gc, GPIO_INTMASK);
- irq_reg_writel(gc, ~gc->wake_active, GPIO_INTMASK);
- clk_disable(bank->clk);
-}
-
-static void rockchip_irq_resume(struct irq_data *d)
-{
- struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
- struct rockchip_pin_bank *bank = gc->private;
-
- clk_enable(bank->clk);
- irq_reg_writel(gc, bank->saved_masks, GPIO_INTMASK);
- clk_disable(bank->clk);
-}
-
-static void rockchip_irq_enable(struct irq_data *d)
-{
- struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
- struct rockchip_pin_bank *bank = gc->private;
-
- clk_enable(bank->clk);
- irq_gc_mask_clr_bit(d);
-}
-
-static void rockchip_irq_disable(struct irq_data *d)
-{
- struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
- struct rockchip_pin_bank *bank = gc->private;
-
- irq_gc_mask_set_bit(d);
- clk_disable(bank->clk);
-}
-
-static int rockchip_interrupts_register(struct platform_device *pdev,
- struct rockchip_pinctrl *info)
-{
- struct rockchip_pin_ctrl *ctrl = info->ctrl;
- struct rockchip_pin_bank *bank = ctrl->pin_banks;
- unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN;
- struct irq_chip_generic *gc;
- int ret;
- int i;
-
- for (i = 0; i < ctrl->nr_banks; ++i, ++bank) {
- if (!bank->valid) {
- dev_warn(&pdev->dev, "bank %s is not valid\n",
- bank->name);
- continue;
- }
-
- ret = clk_enable(bank->clk);
- if (ret) {
- dev_err(&pdev->dev, "failed to enable clock for bank %s\n",
- bank->name);
- continue;
- }
-
- bank->domain = irq_domain_add_linear(bank->of_node, 32,
- &irq_generic_chip_ops, NULL);
- if (!bank->domain) {
- dev_warn(&pdev->dev, "could not initialize irq domain for bank %s\n",
- bank->name);
- clk_disable(bank->clk);
- continue;
- }
-
- ret = irq_alloc_domain_generic_chips(bank->domain, 32, 1,
- "rockchip_gpio_irq", handle_level_irq,
- clr, 0, 0);
- if (ret) {
- dev_err(&pdev->dev, "could not alloc generic chips for bank %s\n",
- bank->name);
- irq_domain_remove(bank->domain);
- clk_disable(bank->clk);
- continue;
- }
-
- gc = irq_get_domain_generic_chip(bank->domain, 0);
- gc->reg_base = bank->reg_base;
- gc->private = bank;
- gc->chip_types[0].regs.mask = GPIO_INTMASK;
- gc->chip_types[0].regs.ack = GPIO_PORTS_EOI;
- gc->chip_types[0].chip.irq_ack = irq_gc_ack_set_bit;
- gc->chip_types[0].chip.irq_mask = irq_gc_mask_set_bit;
- gc->chip_types[0].chip.irq_unmask = irq_gc_mask_clr_bit;
- gc->chip_types[0].chip.irq_enable = rockchip_irq_enable;
- gc->chip_types[0].chip.irq_disable = rockchip_irq_disable;
- gc->chip_types[0].chip.irq_set_wake = irq_gc_set_wake;
- gc->chip_types[0].chip.irq_suspend = rockchip_irq_suspend;
- gc->chip_types[0].chip.irq_resume = rockchip_irq_resume;
- gc->chip_types[0].chip.irq_set_type = rockchip_irq_set_type;
- gc->wake_enabled = IRQ_MSK(bank->nr_pins);
-
- /*
- * Linux assumes that all interrupts start out disabled/masked.
- * Our driver only uses the concept of masked and always keeps
- * things enabled, so for us that's all masked and all enabled.
- */
- writel_relaxed(0xffffffff, bank->reg_base + GPIO_INTMASK);
- writel_relaxed(0xffffffff, bank->reg_base + GPIO_PORTS_EOI);
- writel_relaxed(0xffffffff, bank->reg_base + GPIO_INTEN);
- gc->mask_cache = 0xffffffff;
-
- irq_set_chained_handler_and_data(bank->irq,
- rockchip_irq_demux, bank);
- clk_disable(bank->clk);
- }
-
- return 0;
-}
-
-static int rockchip_gpiolib_register(struct platform_device *pdev,
- struct rockchip_pinctrl *info)
-{
- struct rockchip_pin_ctrl *ctrl = info->ctrl;
- struct rockchip_pin_bank *bank = ctrl->pin_banks;
- struct gpio_chip *gc;
- int ret;
- int i;
-
- for (i = 0; i < ctrl->nr_banks; ++i, ++bank) {
- if (!bank->valid) {
- dev_warn(&pdev->dev, "bank %s is not valid\n",
- bank->name);
- continue;
- }
-
- bank->gpio_chip = rockchip_gpiolib_chip;
-
- gc = &bank->gpio_chip;
- gc->base = bank->pin_base;
- gc->ngpio = bank->nr_pins;
- gc->parent = &pdev->dev;
- gc->of_node = bank->of_node;
- gc->label = bank->name;
-
- ret = gpiochip_add_data(gc, bank);
- if (ret) {
- dev_err(&pdev->dev, "failed to register gpio_chip %s, error code: %d\n",
- gc->label, ret);
- goto fail;
- }
- }
-
- rockchip_interrupts_register(pdev, info);
-
- return 0;
-
-fail:
- for (--i, --bank; i >= 0; --i, --bank) {
- if (!bank->valid)
- continue;
- gpiochip_remove(&bank->gpio_chip);
- }
- return ret;
-}
-
-static int rockchip_gpiolib_unregister(struct platform_device *pdev,
- struct rockchip_pinctrl *info)
-{
- struct rockchip_pin_ctrl *ctrl = info->ctrl;
- struct rockchip_pin_bank *bank = ctrl->pin_banks;
- int i;
-
- for (i = 0; i < ctrl->nr_banks; ++i, ++bank) {
- if (!bank->valid)
- continue;
- gpiochip_remove(&bank->gpio_chip);
- }
-
return 0;
}
-static int rockchip_get_bank_data(struct rockchip_pin_bank *bank,
- struct rockchip_pinctrl *info)
-{
- struct resource res;
- void __iomem *base;
-
- if (of_address_to_resource(bank->of_node, 0, &res)) {
- dev_err(info->dev, "cannot find IO resource for bank\n");
- return -ENOENT;
- }
-
- bank->reg_base = devm_ioremap_resource(info->dev, &res);
- if (IS_ERR(bank->reg_base))
- return PTR_ERR(bank->reg_base);
-
- /*
- * special case, where parts of the pull setting-registers are
- * part of the PMU register space
- */
- if (of_device_is_compatible(bank->of_node,
- "rockchip,rk3188-gpio-bank0")) {
- struct device_node *node;
-
- node = of_parse_phandle(bank->of_node->parent,
- "rockchip,pmu", 0);
- if (!node) {
- if (of_address_to_resource(bank->of_node, 1, &res)) {
- dev_err(info->dev, "cannot find IO resource for bank\n");
- return -ENOENT;
- }
-
- base = devm_ioremap_resource(info->dev, &res);
- if (IS_ERR(base))
- return PTR_ERR(base);
- rockchip_regmap_config.max_register =
- resource_size(&res) - 4;
- rockchip_regmap_config.name =
- "rockchip,rk3188-gpio-bank0-pull";
- bank->regmap_pull = devm_regmap_init_mmio(info->dev,
- base,
- &rockchip_regmap_config);
- }
- of_node_put(node);
- }
-
- bank->irq = irq_of_parse_and_map(bank->of_node, 0);
-
- bank->clk = of_clk_get(bank->of_node, 0);
- if (IS_ERR(bank->clk))
- return PTR_ERR(bank->clk);
-
- return clk_prepare(bank->clk);
-}
-
static const struct of_device_id rockchip_pinctrl_dt_match[];
/* retrieve the soc specific data */
@@ -3105,7 +2475,6 @@ static struct rockchip_pin_ctrl *rockchip_pinctrl_get_soc_data(
{
const struct of_device_id *match;
struct device_node *node = pdev->dev.of_node;
- struct device_node *np;
struct rockchip_pin_ctrl *ctrl;
struct rockchip_pin_bank *bank;
int grf_offs, pmu_offs, drv_grf_offs, drv_pmu_offs, i, j;
@@ -3113,23 +2482,6 @@ static struct rockchip_pin_ctrl *rockchip_pinctrl_get_soc_data(
match = of_match_node(rockchip_pinctrl_dt_match, node);
ctrl = (struct rockchip_pin_ctrl *)match->data;
- for_each_child_of_node(node, np) {
- if (!of_find_property(np, "gpio-controller", NULL))
- continue;
-
- bank = ctrl->pin_banks;
- for (i = 0; i < ctrl->nr_banks; ++i, ++bank) {
- if (!strcmp(bank->name, np->name)) {
- bank->of_node = np;
-
- if (!rockchip_get_bank_data(bank, d))
- bank->valid = true;
-
- break;
- }
- }
- }
-
grf_offs = ctrl->grf_mux_offset;
pmu_offs = ctrl->pmu_mux_offset;
drv_pmu_offs = ctrl->pmu_drv_offset;
@@ -3347,18 +2699,18 @@ static int rockchip_pinctrl_probe(struct platform_device *pdev)
return PTR_ERR(info->regmap_pmu);
}
- ret = rockchip_gpiolib_register(pdev, info);
+ ret = rockchip_pinctrl_register(pdev, info);
if (ret)
return ret;
- ret = rockchip_pinctrl_register(pdev, info);
+ platform_set_drvdata(pdev, info);
+
+ ret = of_platform_populate(np, rockchip_bank_match, NULL, NULL);
if (ret) {
- rockchip_gpiolib_unregister(pdev, info);
+ dev_err(&pdev->dev, "failed to register gpio device\n");
return ret;
}
- platform_set_drvdata(pdev, info);
-
return 0;
}
--
2.25.1
There has spin lock for irq set type already, so drop irq_gc_lock and
irq_gc_unlock.
Signed-off-by: Jianqun Xu <[email protected]>
---
drivers/gpio/gpio-rockchip.c | 2 --
1 file changed, 2 deletions(-)
diff --git a/drivers/gpio/gpio-rockchip.c b/drivers/gpio/gpio-rockchip.c
index 048e7eecddba..c9c55614bbef 100644
--- a/drivers/gpio/gpio-rockchip.c
+++ b/drivers/gpio/gpio-rockchip.c
@@ -406,7 +406,6 @@ static int rockchip_irq_set_type(struct irq_data *d, unsigned int type)
irq_set_handler_locked(d, handle_level_irq);
raw_spin_lock_irqsave(&bank->slock, flags);
- irq_gc_lock(gc);
level = rockchip_gpio_readl(bank, bank->gpio_regs->int_type);
polarity = rockchip_gpio_readl(bank, bank->gpio_regs->int_polarity);
@@ -461,7 +460,6 @@ static int rockchip_irq_set_type(struct irq_data *d, unsigned int type)
rockchip_gpio_writel(bank, level, bank->gpio_regs->int_type);
rockchip_gpio_writel(bank, polarity, bank->gpio_regs->int_polarity);
out:
- irq_gc_unlock(gc);
raw_spin_unlock_irqrestore(&bank->slock, flags);
return ret;
--
2.25.1
Separate struct rockchip_pin_bank to pinctrl-rockchip.h file, which will
be used by gpio-rockchip driver in the future.
Signed-off-by: Jianqun Xu <[email protected]>
---
drivers/pinctrl/pinctrl-rockchip.c | 226 +-------------------------
drivers/pinctrl/pinctrl-rockchip.h | 245 +++++++++++++++++++++++++++++
2 files changed, 246 insertions(+), 225 deletions(-)
create mode 100644 drivers/pinctrl/pinctrl-rockchip.h
diff --git a/drivers/pinctrl/pinctrl-rockchip.c b/drivers/pinctrl/pinctrl-rockchip.c
index 94fa18cdcd8d..ae88045dc3e4 100644
--- a/drivers/pinctrl/pinctrl-rockchip.c
+++ b/drivers/pinctrl/pinctrl-rockchip.c
@@ -37,6 +37,7 @@
#include "core.h"
#include "pinconf.h"
+#include "pinctrl-rockchip.h"
/* GPIO control registers */
#define GPIO_SWPORT_DR 0x00
@@ -52,21 +53,6 @@
#define GPIO_EXT_PORT 0x50
#define GPIO_LS_SYNC 0x60
-enum rockchip_pinctrl_type {
- PX30,
- RV1108,
- RK2928,
- RK3066B,
- RK3128,
- RK3188,
- RK3288,
- RK3308,
- RK3368,
- RK3399,
- RK3568,
-};
-
-
/**
* Generate a bitmask for setting a value (v) with a write mask bit in hiword
* register 31:16 area.
@@ -84,103 +70,6 @@ enum rockchip_pinctrl_type {
#define IOMUX_WIDTH_3BIT BIT(4)
#define IOMUX_WIDTH_2BIT BIT(5)
-/**
- * struct rockchip_iomux
- * @type: iomux variant using IOMUX_* constants
- * @offset: if initialized to -1 it will be autocalculated, by specifying
- * an initial offset value the relevant source offset can be reset
- * to a new value for autocalculating the following iomux registers.
- */
-struct rockchip_iomux {
- int type;
- int offset;
-};
-
-/*
- * enum type index corresponding to rockchip_perpin_drv_list arrays index.
- */
-enum rockchip_pin_drv_type {
- DRV_TYPE_IO_DEFAULT = 0,
- DRV_TYPE_IO_1V8_OR_3V0,
- DRV_TYPE_IO_1V8_ONLY,
- DRV_TYPE_IO_1V8_3V0_AUTO,
- DRV_TYPE_IO_3V3_ONLY,
- DRV_TYPE_MAX
-};
-
-/*
- * enum type index corresponding to rockchip_pull_list arrays index.
- */
-enum rockchip_pin_pull_type {
- PULL_TYPE_IO_DEFAULT = 0,
- PULL_TYPE_IO_1V8_ONLY,
- PULL_TYPE_MAX
-};
-
-/**
- * struct rockchip_drv
- * @drv_type: drive strength variant using rockchip_perpin_drv_type
- * @offset: if initialized to -1 it will be autocalculated, by specifying
- * an initial offset value the relevant source offset can be reset
- * to a new value for autocalculating the following drive strength
- * registers. if used chips own cal_drv func instead to calculate
- * registers offset, the variant could be ignored.
- */
-struct rockchip_drv {
- enum rockchip_pin_drv_type drv_type;
- int offset;
-};
-
-/**
- * struct rockchip_pin_bank
- * @reg_base: register base of the gpio bank
- * @regmap_pull: optional separate register for additional pull settings
- * @clk: clock of the gpio bank
- * @irq: interrupt of the gpio bank
- * @saved_masks: Saved content of GPIO_INTEN at suspend time.
- * @pin_base: first pin number
- * @nr_pins: number of pins in this bank
- * @name: name of the bank
- * @bank_num: number of the bank, to account for holes
- * @iomux: array describing the 4 iomux sources of the bank
- * @drv: array describing the 4 drive strength sources of the bank
- * @pull_type: array describing the 4 pull type sources of the bank
- * @valid: is all necessary information present
- * @of_node: dt node of this bank
- * @drvdata: common pinctrl basedata
- * @domain: irqdomain of the gpio bank
- * @gpio_chip: gpiolib chip
- * @grange: gpio range
- * @slock: spinlock for the gpio bank
- * @toggle_edge_mode: bit mask to toggle (falling/rising) edge mode
- * @recalced_mask: bit mask to indicate a need to recalulate the mask
- * @route_mask: bits describing the routing pins of per bank
- */
-struct rockchip_pin_bank {
- void __iomem *reg_base;
- struct regmap *regmap_pull;
- struct clk *clk;
- int irq;
- u32 saved_masks;
- u32 pin_base;
- u8 nr_pins;
- char *name;
- u8 bank_num;
- struct rockchip_iomux iomux[4];
- struct rockchip_drv drv[4];
- enum rockchip_pin_pull_type pull_type[4];
- bool valid;
- struct device_node *of_node;
- struct rockchip_pinctrl *drvdata;
- struct irq_domain *domain;
- struct gpio_chip gpio_chip;
- struct pinctrl_gpio_range grange;
- raw_spinlock_t slock;
- u32 toggle_edge_mode;
- u32 recalced_mask;
- u32 route_mask;
-};
-
#define PIN_BANK(id, pins, label) \
{ \
.bank_num = id, \
@@ -320,119 +209,6 @@ struct rockchip_pin_bank {
#define RK_MUXROUTE_PMU(ID, PIN, FUNC, REG, VAL) \
PIN_BANK_MUX_ROUTE_FLAGS(ID, PIN, FUNC, REG, VAL, ROCKCHIP_ROUTE_PMU)
-/**
- * struct rockchip_mux_recalced_data: represent a pin iomux data.
- * @num: bank number.
- * @pin: pin number.
- * @bit: index at register.
- * @reg: register offset.
- * @mask: mask bit
- */
-struct rockchip_mux_recalced_data {
- u8 num;
- u8 pin;
- u32 reg;
- u8 bit;
- u8 mask;
-};
-
-enum rockchip_mux_route_location {
- ROCKCHIP_ROUTE_SAME = 0,
- ROCKCHIP_ROUTE_PMU,
- ROCKCHIP_ROUTE_GRF,
-};
-
-/**
- * struct rockchip_mux_recalced_data: represent a pin iomux data.
- * @bank_num: bank number.
- * @pin: index at register or used to calc index.
- * @func: the min pin.
- * @route_location: the mux route location (same, pmu, grf).
- * @route_offset: the max pin.
- * @route_val: the register offset.
- */
-struct rockchip_mux_route_data {
- u8 bank_num;
- u8 pin;
- u8 func;
- enum rockchip_mux_route_location route_location;
- u32 route_offset;
- u32 route_val;
-};
-
-struct rockchip_pin_ctrl {
- struct rockchip_pin_bank *pin_banks;
- u32 nr_banks;
- u32 nr_pins;
- char *label;
- enum rockchip_pinctrl_type type;
- int grf_mux_offset;
- int pmu_mux_offset;
- int grf_drv_offset;
- int pmu_drv_offset;
- struct rockchip_mux_recalced_data *iomux_recalced;
- u32 niomux_recalced;
- struct rockchip_mux_route_data *iomux_routes;
- u32 niomux_routes;
-
- void (*pull_calc_reg)(struct rockchip_pin_bank *bank,
- int pin_num, struct regmap **regmap,
- int *reg, u8 *bit);
- void (*drv_calc_reg)(struct rockchip_pin_bank *bank,
- int pin_num, struct regmap **regmap,
- int *reg, u8 *bit);
- int (*schmitt_calc_reg)(struct rockchip_pin_bank *bank,
- int pin_num, struct regmap **regmap,
- int *reg, u8 *bit);
-};
-
-struct rockchip_pin_config {
- unsigned int func;
- unsigned long *configs;
- unsigned int nconfigs;
-};
-
-/**
- * struct rockchip_pin_group: represent group of pins of a pinmux function.
- * @name: name of the pin group, used to lookup the group.
- * @pins: the pins included in this group.
- * @npins: number of pins included in this group.
- * @data: local pin configuration
- */
-struct rockchip_pin_group {
- const char *name;
- unsigned int npins;
- unsigned int *pins;
- struct rockchip_pin_config *data;
-};
-
-/**
- * struct rockchip_pmx_func: represent a pin function.
- * @name: name of the pin function, used to lookup the function.
- * @groups: one or more names of pin groups that provide this function.
- * @ngroups: number of groups included in @groups.
- */
-struct rockchip_pmx_func {
- const char *name;
- const char **groups;
- u8 ngroups;
-};
-
-struct rockchip_pinctrl {
- struct regmap *regmap_base;
- int reg_size;
- struct regmap *regmap_pull;
- struct regmap *regmap_pmu;
- struct device *dev;
- struct rockchip_pin_ctrl *ctrl;
- struct pinctrl_desc pctl;
- struct pinctrl_dev *pctl_dev;
- struct rockchip_pin_group *groups;
- unsigned int ngroups;
- struct rockchip_pmx_func *functions;
- unsigned int nfunctions;
-};
-
static struct regmap_config rockchip_regmap_config = {
.reg_bits = 32,
.val_bits = 32,
diff --git a/drivers/pinctrl/pinctrl-rockchip.h b/drivers/pinctrl/pinctrl-rockchip.h
new file mode 100644
index 000000000000..dba9e9540633
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-rockchip.h
@@ -0,0 +1,245 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2020-2021 Rockchip Electronics Co. Ltd.
+ *
+ * Copyright (c) 2013 MundoReader S.L.
+ * Author: Heiko Stuebner <[email protected]>
+ *
+ * With some ideas taken from pinctrl-samsung:
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ * Copyright (c) 2012 Linaro Ltd
+ * https://www.linaro.org
+ *
+ * and pinctrl-at91:
+ * Copyright (C) 2011-2012 Jean-Christophe PLAGNIOL-VILLARD <[email protected]>
+ */
+
+#ifndef _PINCTRL_ROCKCHIP_H
+#define _PINCTRL_ROCKCHIP_H
+
+enum rockchip_pinctrl_type {
+ PX30,
+ RV1108,
+ RK2928,
+ RK3066B,
+ RK3128,
+ RK3188,
+ RK3288,
+ RK3308,
+ RK3368,
+ RK3399,
+ RK3568,
+};
+
+/**
+ * struct rockchip_iomux
+ * @type: iomux variant using IOMUX_* constants
+ * @offset: if initialized to -1 it will be autocalculated, by specifying
+ * an initial offset value the relevant source offset can be reset
+ * to a new value for autocalculating the following iomux registers.
+ */
+struct rockchip_iomux {
+ int type;
+ int offset;
+};
+
+/*
+ * enum type index corresponding to rockchip_perpin_drv_list arrays index.
+ */
+enum rockchip_pin_drv_type {
+ DRV_TYPE_IO_DEFAULT = 0,
+ DRV_TYPE_IO_1V8_OR_3V0,
+ DRV_TYPE_IO_1V8_ONLY,
+ DRV_TYPE_IO_1V8_3V0_AUTO,
+ DRV_TYPE_IO_3V3_ONLY,
+ DRV_TYPE_MAX
+};
+
+/*
+ * enum type index corresponding to rockchip_pull_list arrays index.
+ */
+enum rockchip_pin_pull_type {
+ PULL_TYPE_IO_DEFAULT = 0,
+ PULL_TYPE_IO_1V8_ONLY,
+ PULL_TYPE_MAX
+};
+
+/**
+ * struct rockchip_drv
+ * @drv_type: drive strength variant using rockchip_perpin_drv_type
+ * @offset: if initialized to -1 it will be autocalculated, by specifying
+ * an initial offset value the relevant source offset can be reset
+ * to a new value for autocalculating the following drive strength
+ * registers. if used chips own cal_drv func instead to calculate
+ * registers offset, the variant could be ignored.
+ */
+struct rockchip_drv {
+ enum rockchip_pin_drv_type drv_type;
+ int offset;
+};
+
+/**
+ * struct rockchip_pin_bank
+ * @reg_base: register base of the gpio bank
+ * @regmap_pull: optional separate register for additional pull settings
+ * @clk: clock of the gpio bank
+ * @irq: interrupt of the gpio bank
+ * @saved_masks: Saved content of GPIO_INTEN at suspend time.
+ * @pin_base: first pin number
+ * @nr_pins: number of pins in this bank
+ * @name: name of the bank
+ * @bank_num: number of the bank, to account for holes
+ * @iomux: array describing the 4 iomux sources of the bank
+ * @drv: array describing the 4 drive strength sources of the bank
+ * @pull_type: array describing the 4 pull type sources of the bank
+ * @valid: is all necessary information present
+ * @of_node: dt node of this bank
+ * @drvdata: common pinctrl basedata
+ * @domain: irqdomain of the gpio bank
+ * @gpio_chip: gpiolib chip
+ * @grange: gpio range
+ * @slock: spinlock for the gpio bank
+ * @toggle_edge_mode: bit mask to toggle (falling/rising) edge mode
+ * @recalced_mask: bit mask to indicate a need to recalulate the mask
+ * @route_mask: bits describing the routing pins of per bank
+ */
+struct rockchip_pin_bank {
+ void __iomem *reg_base;
+ struct regmap *regmap_pull;
+ struct clk *clk;
+ int irq;
+ u32 saved_masks;
+ u32 pin_base;
+ u8 nr_pins;
+ char *name;
+ u8 bank_num;
+ struct rockchip_iomux iomux[4];
+ struct rockchip_drv drv[4];
+ enum rockchip_pin_pull_type pull_type[4];
+ bool valid;
+ struct device_node *of_node;
+ struct rockchip_pinctrl *drvdata;
+ struct irq_domain *domain;
+ struct gpio_chip gpio_chip;
+ struct pinctrl_gpio_range grange;
+ raw_spinlock_t slock;
+ u32 toggle_edge_mode;
+ u32 recalced_mask;
+ u32 route_mask;
+};
+
+/**
+ * struct rockchip_mux_recalced_data: represent a pin iomux data.
+ * @num: bank number.
+ * @pin: pin number.
+ * @bit: index at register.
+ * @reg: register offset.
+ * @mask: mask bit
+ */
+struct rockchip_mux_recalced_data {
+ u8 num;
+ u8 pin;
+ u32 reg;
+ u8 bit;
+ u8 mask;
+};
+
+enum rockchip_mux_route_location {
+ ROCKCHIP_ROUTE_SAME = 0,
+ ROCKCHIP_ROUTE_PMU,
+ ROCKCHIP_ROUTE_GRF,
+};
+
+/**
+ * struct rockchip_mux_recalced_data: represent a pin iomux data.
+ * @bank_num: bank number.
+ * @pin: index at register or used to calc index.
+ * @func: the min pin.
+ * @route_location: the mux route location (same, pmu, grf).
+ * @route_offset: the max pin.
+ * @route_val: the register offset.
+ */
+struct rockchip_mux_route_data {
+ u8 bank_num;
+ u8 pin;
+ u8 func;
+ enum rockchip_mux_route_location route_location;
+ u32 route_offset;
+ u32 route_val;
+};
+
+struct rockchip_pin_ctrl {
+ struct rockchip_pin_bank *pin_banks;
+ u32 nr_banks;
+ u32 nr_pins;
+ char *label;
+ enum rockchip_pinctrl_type type;
+ int grf_mux_offset;
+ int pmu_mux_offset;
+ int grf_drv_offset;
+ int pmu_drv_offset;
+ struct rockchip_mux_recalced_data *iomux_recalced;
+ u32 niomux_recalced;
+ struct rockchip_mux_route_data *iomux_routes;
+ u32 niomux_routes;
+
+ void (*pull_calc_reg)(struct rockchip_pin_bank *bank,
+ int pin_num, struct regmap **regmap,
+ int *reg, u8 *bit);
+ void (*drv_calc_reg)(struct rockchip_pin_bank *bank,
+ int pin_num, struct regmap **regmap,
+ int *reg, u8 *bit);
+ int (*schmitt_calc_reg)(struct rockchip_pin_bank *bank,
+ int pin_num, struct regmap **regmap,
+ int *reg, u8 *bit);
+};
+
+struct rockchip_pin_config {
+ unsigned int func;
+ unsigned long *configs;
+ unsigned int nconfigs;
+};
+
+/**
+ * struct rockchip_pin_group: represent group of pins of a pinmux function.
+ * @name: name of the pin group, used to lookup the group.
+ * @pins: the pins included in this group.
+ * @npins: number of pins included in this group.
+ * @data: local pin configuration
+ */
+struct rockchip_pin_group {
+ const char *name;
+ unsigned int npins;
+ unsigned int *pins;
+ struct rockchip_pin_config *data;
+};
+
+/**
+ * struct rockchip_pmx_func: represent a pin function.
+ * @name: name of the pin function, used to lookup the function.
+ * @groups: one or more names of pin groups that provide this function.
+ * @ngroups: number of groups included in @groups.
+ */
+struct rockchip_pmx_func {
+ const char *name;
+ const char **groups;
+ u8 ngroups;
+};
+
+struct rockchip_pinctrl {
+ struct regmap *regmap_base;
+ int reg_size;
+ struct regmap *regmap_pull;
+ struct regmap *regmap_pmu;
+ struct device *dev;
+ struct rockchip_pin_ctrl *ctrl;
+ struct pinctrl_desc pctl;
+ struct pinctrl_dev *pctl_dev;
+ struct rockchip_pin_group *groups;
+ unsigned int ngroups;
+ struct rockchip_pmx_func *functions;
+ unsigned int nfunctions;
+};
+
+#endif
--
2.25.1
Since gate and ungate pclk of gpio has very litte benifit for system
power consumption, just keep it always ungate.
Signed-off-by: Jianqun Xu <[email protected]>
---
drivers/gpio/gpio-rockchip.c | 68 +++++-------------------------------
1 file changed, 9 insertions(+), 59 deletions(-)
diff --git a/drivers/gpio/gpio-rockchip.c b/drivers/gpio/gpio-rockchip.c
index 92aaf1848449..048e7eecddba 100644
--- a/drivers/gpio/gpio-rockchip.c
+++ b/drivers/gpio/gpio-rockchip.c
@@ -139,17 +139,8 @@ static int rockchip_gpio_get_direction(struct gpio_chip *chip,
{
struct rockchip_pin_bank *bank = gpiochip_get_data(chip);
u32 data;
- int ret;
- ret = clk_enable(bank->clk);
- if (ret < 0) {
- dev_err(bank->drvdata->dev,
- "failed to enable clock for bank %s\n", bank->name);
- return ret;
- }
data = rockchip_gpio_readl_bit(bank, offset, bank->gpio_regs->port_ddr);
- clk_disable(bank->clk);
-
if (data & BIT(offset))
return GPIO_LINE_DIRECTION_OUT;
@@ -163,11 +154,9 @@ static int rockchip_gpio_set_direction(struct gpio_chip *chip,
unsigned long flags;
u32 data = input ? 0 : 1;
- clk_enable(bank->clk);
raw_spin_lock_irqsave(&bank->slock, flags);
rockchip_gpio_writel_bit(bank, offset, data, bank->gpio_regs->port_ddr);
raw_spin_unlock_irqrestore(&bank->slock, flags);
- clk_disable(bank->clk);
return 0;
}
@@ -178,11 +167,9 @@ static void rockchip_gpio_set(struct gpio_chip *gc, unsigned int offset,
struct rockchip_pin_bank *bank = gpiochip_get_data(gc);
unsigned long flags;
- clk_enable(bank->clk);
raw_spin_lock_irqsave(&bank->slock, flags);
rockchip_gpio_writel_bit(bank, offset, value, bank->gpio_regs->port_dr);
raw_spin_unlock_irqrestore(&bank->slock, flags);
- clk_disable(bank->clk);
}
static int rockchip_gpio_get(struct gpio_chip *gc, unsigned int offset)
@@ -190,11 +177,10 @@ static int rockchip_gpio_get(struct gpio_chip *gc, unsigned int offset)
struct rockchip_pin_bank *bank = gpiochip_get_data(gc);
u32 data;
- clk_enable(bank->clk);
data = readl(bank->reg_base + bank->gpio_regs->ext_port);
- clk_disable(bank->clk);
data >>= offset;
data &= 1;
+
return data;
}
@@ -315,9 +301,7 @@ static int rockchip_gpio_to_irq(struct gpio_chip *gc, unsigned int offset)
if (!bank->domain)
return -ENXIO;
- clk_enable(bank->clk);
virq = irq_create_mapping(bank->domain, offset);
- clk_disable(bank->clk);
return (virq) ? : -ENXIO;
}
@@ -409,7 +393,6 @@ static int rockchip_irq_set_type(struct irq_data *d, unsigned int type)
unsigned long flags;
int ret = 0;
- clk_enable(bank->clk);
raw_spin_lock_irqsave(&bank->slock, flags);
rockchip_gpio_writel_bit(bank, d->hwirq, 0,
@@ -480,7 +463,6 @@ static int rockchip_irq_set_type(struct irq_data *d, unsigned int type)
out:
irq_gc_unlock(gc);
raw_spin_unlock_irqrestore(&bank->slock, flags);
- clk_disable(bank->clk);
return ret;
}
@@ -490,10 +472,8 @@ static void rockchip_irq_suspend(struct irq_data *d)
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
struct rockchip_pin_bank *bank = gc->private;
- clk_enable(bank->clk);
bank->saved_masks = irq_reg_readl(gc, bank->gpio_regs->int_mask);
irq_reg_writel(gc, ~gc->wake_active, bank->gpio_regs->int_mask);
- clk_disable(bank->clk);
}
static void rockchip_irq_resume(struct irq_data *d)
@@ -501,27 +481,7 @@ static void rockchip_irq_resume(struct irq_data *d)
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
struct rockchip_pin_bank *bank = gc->private;
- clk_enable(bank->clk);
irq_reg_writel(gc, bank->saved_masks, bank->gpio_regs->int_mask);
- clk_disable(bank->clk);
-}
-
-static void rockchip_irq_enable(struct irq_data *d)
-{
- struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
- struct rockchip_pin_bank *bank = gc->private;
-
- clk_enable(bank->clk);
- irq_gc_mask_clr_bit(d);
-}
-
-static void rockchip_irq_disable(struct irq_data *d)
-{
- struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
- struct rockchip_pin_bank *bank = gc->private;
-
- irq_gc_mask_set_bit(d);
- clk_disable(bank->clk);
}
static int rockchip_interrupts_register(struct rockchip_pin_bank *bank)
@@ -530,19 +490,11 @@ static int rockchip_interrupts_register(struct rockchip_pin_bank *bank)
struct irq_chip_generic *gc;
int ret;
- ret = clk_enable(bank->clk);
- if (ret) {
- dev_err(bank->dev, "failed to enable clock for bank %s\n",
- bank->name);
- return -EINVAL;
- }
-
bank->domain = irq_domain_add_linear(bank->of_node, 32,
&irq_generic_chip_ops, NULL);
if (!bank->domain) {
dev_warn(bank->dev, "could not init irq domain for bank %s\n",
bank->name);
- clk_disable(bank->clk);
return -EINVAL;
}
@@ -554,7 +506,6 @@ static int rockchip_interrupts_register(struct rockchip_pin_bank *bank)
dev_err(bank->dev, "could not alloc generic chips for bank %s\n",
bank->name);
irq_domain_remove(bank->domain);
- clk_disable(bank->clk);
return -EINVAL;
}
@@ -571,8 +522,8 @@ static int rockchip_interrupts_register(struct rockchip_pin_bank *bank)
gc->chip_types[0].chip.irq_ack = irq_gc_ack_set_bit;
gc->chip_types[0].chip.irq_mask = irq_gc_mask_set_bit;
gc->chip_types[0].chip.irq_unmask = irq_gc_mask_clr_bit;
- gc->chip_types[0].chip.irq_enable = rockchip_irq_enable;
- gc->chip_types[0].chip.irq_disable = rockchip_irq_disable;
+ gc->chip_types[0].chip.irq_enable = irq_gc_mask_clr_bit;
+ gc->chip_types[0].chip.irq_disable = irq_gc_mask_set_bit;
gc->chip_types[0].chip.irq_set_wake = irq_gc_set_wake;
gc->chip_types[0].chip.irq_suspend = rockchip_irq_suspend;
gc->chip_types[0].chip.irq_resume = rockchip_irq_resume;
@@ -591,7 +542,6 @@ static int rockchip_interrupts_register(struct rockchip_pin_bank *bank)
irq_set_chained_handler_and_data(bank->irq,
rockchip_irq_demux, bank);
- clk_disable(bank->clk);
return 0;
}
@@ -695,7 +645,6 @@ static int rockchip_get_bank_data(struct rockchip_pin_bank *bank)
if (IS_ERR(bank->db_clk)) {
dev_err(bank->dev, "cannot find debounce clk\n");
bank->db_clk = NULL;
- clk_disable(bank->clk);
return -EINVAL;
}
} else {
@@ -703,7 +652,6 @@ static int rockchip_get_bank_data(struct rockchip_pin_bank *bank)
bank->gpio_type = GPIO_TYPE_V1;
}
- clk_disable(bank->clk);
return 0;
}
@@ -756,15 +704,17 @@ static int rockchip_gpio_probe(struct platform_device *pdev)
return ret;
ret = rockchip_gpiolib_register(bank);
- if (ret) {
- clk_disable_unprepare(bank->clk);
- return ret;
- }
+ if (ret)
+ goto err_clk;
platform_set_drvdata(pdev, bank);
dev_info(dev, "probed %pOF\n", np);
return 0;
+err_clk:
+ clk_disable_unprepare(bank->clk);
+
+ return ret;
}
static int rockchip_gpio_remove(struct platform_device *pdev)
--
2.25.1
The next version gpio controller on SoCs like rk3568 have more write
mask bits for registers.
Signed-off-by: Jianqun Xu <[email protected]>
---
drivers/gpio/gpio-rockchip.c | 280 ++++++++++++++++++++++-------
drivers/pinctrl/pinctrl-rockchip.h | 2 +
2 files changed, 215 insertions(+), 67 deletions(-)
diff --git a/drivers/gpio/gpio-rockchip.c b/drivers/gpio/gpio-rockchip.c
index b12db3a523d0..92aaf1848449 100644
--- a/drivers/gpio/gpio-rockchip.c
+++ b/drivers/gpio/gpio-rockchip.c
@@ -25,6 +25,7 @@
#include "../pinctrl/pinctrl-rockchip.h"
#define GPIO_TYPE_V1 (0) /* GPIO Version ID reserved */
+#define GPIO_TYPE_V2 (0x01000C2B) /* GPIO Version ID 0x01000C2B */
static const struct rockchip_gpio_regs gpio_regs_v1 = {
.port_dr = 0x00,
@@ -40,6 +41,99 @@ static const struct rockchip_gpio_regs gpio_regs_v1 = {
.ext_port = 0x50,
};
+static const struct rockchip_gpio_regs gpio_regs_v2 = {
+ .port_dr = 0x00,
+ .port_ddr = 0x08,
+ .int_en = 0x10,
+ .int_mask = 0x18,
+ .int_type = 0x20,
+ .int_polarity = 0x28,
+ .int_bothedge = 0x30,
+ .int_status = 0x50,
+ .int_rawstatus = 0x58,
+ .debounce = 0x38,
+ .dbclk_div_en = 0x40,
+ .dbclk_div_con = 0x48,
+ .port_eoi = 0x60,
+ .ext_port = 0x70,
+ .version_id = 0x78,
+};
+
+static inline void gpio_writel_v2(u32 val, void __iomem *reg)
+{
+ writel((val & 0xffff) | 0xffff0000, reg);
+ writel((val >> 16) | 0xffff0000, reg + 0x4);
+}
+
+static inline u32 gpio_readl_v2(void __iomem *reg)
+{
+ return readl(reg + 0x4) << 16 | readl(reg);
+}
+
+static inline void rockchip_gpio_writel(struct rockchip_pin_bank *bank,
+ u32 value, unsigned int offset)
+{
+ void __iomem *reg = bank->reg_base + offset;
+
+ if (bank->gpio_type == GPIO_TYPE_V2)
+ gpio_writel_v2(value, reg);
+ else
+ writel(value, reg);
+}
+
+static inline u32 rockchip_gpio_readl(struct rockchip_pin_bank *bank,
+ unsigned int offset)
+{
+ void __iomem *reg = bank->reg_base + offset;
+ u32 value;
+
+ if (bank->gpio_type == GPIO_TYPE_V2)
+ value = gpio_readl_v2(reg);
+ else
+ value = readl(reg);
+
+ return value;
+}
+
+static inline void rockchip_gpio_writel_bit(struct rockchip_pin_bank *bank,
+ u32 bit, u32 value,
+ unsigned int offset)
+{
+ void __iomem *reg = bank->reg_base + offset;
+ u32 data;
+
+ if (bank->gpio_type == GPIO_TYPE_V2) {
+ if (value)
+ data = BIT(bit % 16) | BIT(bit % 16 + 16);
+ else
+ data = BIT(bit % 16 + 16);
+ writel(data, bit >= 16 ? reg + 0x4 : reg);
+ } else {
+ data = readl(reg);
+ data &= ~BIT(bit);
+ if (value)
+ data |= BIT(bit);
+ writel(data, reg);
+ }
+}
+
+static inline u32 rockchip_gpio_readl_bit(struct rockchip_pin_bank *bank,
+ u32 bit, unsigned int offset)
+{
+ void __iomem *reg = bank->reg_base + offset;
+ u32 data;
+
+ if (bank->gpio_type == GPIO_TYPE_V2) {
+ data = readl(bit >= 16 ? reg + 0x4 : reg);
+ data >>= bit % 16;
+ } else {
+ data = readl(reg);
+ data >>= bit;
+ }
+
+ return data & (0x1);
+}
+
static int rockchip_gpio_get_direction(struct gpio_chip *chip,
unsigned int offset)
{
@@ -53,7 +147,7 @@ static int rockchip_gpio_get_direction(struct gpio_chip *chip,
"failed to enable clock for bank %s\n", bank->name);
return ret;
}
- data = readl_relaxed(bank->reg_base + bank->gpio_regs->port_ddr);
+ data = rockchip_gpio_readl_bit(bank, offset, bank->gpio_regs->port_ddr);
clk_disable(bank->clk);
if (data & BIT(offset))
@@ -67,19 +161,11 @@ static int rockchip_gpio_set_direction(struct gpio_chip *chip,
{
struct rockchip_pin_bank *bank = gpiochip_get_data(chip);
unsigned long flags;
- u32 data;
+ u32 data = input ? 0 : 1;
clk_enable(bank->clk);
raw_spin_lock_irqsave(&bank->slock, flags);
-
- data = readl_relaxed(bank->reg_base + bank->gpio_regs->port_ddr);
- /* set bit to 1 for output, 0 for input */
- if (!input)
- data |= BIT(offset);
- else
- data &= ~BIT(offset);
- writel_relaxed(data, bank->reg_base + bank->gpio_regs->port_ddr);
-
+ rockchip_gpio_writel_bit(bank, offset, data, bank->gpio_regs->port_ddr);
raw_spin_unlock_irqrestore(&bank->slock, flags);
clk_disable(bank->clk);
@@ -90,19 +176,11 @@ static void rockchip_gpio_set(struct gpio_chip *gc, unsigned int offset,
int value)
{
struct rockchip_pin_bank *bank = gpiochip_get_data(gc);
- void __iomem *reg = bank->reg_base + bank->gpio_regs->port_dr;
unsigned long flags;
- u32 data;
clk_enable(bank->clk);
raw_spin_lock_irqsave(&bank->slock, flags);
-
- data = readl(reg);
- data &= ~BIT(offset);
- if (value)
- data |= BIT(offset);
- writel(data, reg);
-
+ rockchip_gpio_writel_bit(bank, offset, value, bank->gpio_regs->port_dr);
raw_spin_unlock_irqrestore(&bank->slock, flags);
clk_disable(bank->clk);
}
@@ -120,26 +198,65 @@ static int rockchip_gpio_get(struct gpio_chip *gc, unsigned int offset)
return data;
}
-static void rockchip_gpio_set_debounce(struct gpio_chip *gc,
- unsigned int offset, bool enable)
+static int rockchip_gpio_set_debounce(struct gpio_chip *gc,
+ unsigned int offset,
+ unsigned int debounce)
{
struct rockchip_pin_bank *bank = gpiochip_get_data(gc);
- void __iomem *reg = bank->reg_base + bank->gpio_regs->debounce;
- unsigned long flags;
- u32 data;
+ const struct rockchip_gpio_regs *reg = bank->gpio_regs;
+ unsigned long flags, div_reg, freq, max_debounce;
+ bool div_debounce_support;
+ unsigned int cur_div_reg;
+ u64 div;
+
+ if (!IS_ERR(bank->db_clk)) {
+ div_debounce_support = true;
+ freq = clk_get_rate(bank->db_clk);
+ max_debounce = (GENMASK(23, 0) + 1) * 2 * 1000000 / freq;
+ if (debounce > max_debounce)
+ return -EINVAL;
+
+ div = debounce * freq;
+ div_reg = DIV_ROUND_CLOSEST_ULL(div, 2 * USEC_PER_SEC) - 1;
+ } else {
+ div_debounce_support = false;
+ }
- clk_enable(bank->clk);
raw_spin_lock_irqsave(&bank->slock, flags);
- data = readl(reg);
- if (enable)
- data |= BIT(offset);
- else
- data &= ~BIT(offset);
- writel(data, reg);
+ /* Only the v1 needs to configure div_en and div_con for dbclk */
+ if (debounce) {
+ if (div_debounce_support) {
+ /* Configure the max debounce from consumers */
+ cur_div_reg = readl(bank->reg_base +
+ reg->dbclk_div_con);
+ if (cur_div_reg < div_reg)
+ writel(div_reg, bank->reg_base +
+ reg->dbclk_div_con);
+ rockchip_gpio_writel_bit(bank, offset, 1,
+ reg->dbclk_div_en);
+ }
+
+ rockchip_gpio_writel_bit(bank, offset, 1, reg->debounce);
+ } else {
+ if (div_debounce_support)
+ rockchip_gpio_writel_bit(bank, offset, 0,
+ reg->dbclk_div_en);
+
+ rockchip_gpio_writel_bit(bank, offset, 0, reg->debounce);
+ }
raw_spin_unlock_irqrestore(&bank->slock, flags);
- clk_disable(bank->clk);
+
+ /* Enable or disable dbclk at last */
+ if (div_debounce_support) {
+ if (debounce)
+ clk_prepare_enable(bank->db_clk);
+ else
+ clk_disable_unprepare(bank->db_clk);
+ }
+
+ return 0;
}
static int rockchip_gpio_direction_input(struct gpio_chip *gc,
@@ -290,13 +407,13 @@ static int rockchip_irq_set_type(struct irq_data *d, unsigned int type)
u32 level;
u32 data;
unsigned long flags;
+ int ret = 0;
clk_enable(bank->clk);
raw_spin_lock_irqsave(&bank->slock, flags);
- data = readl_relaxed(bank->reg_base + bank->gpio_regs->port_ddr);
- data &= ~mask;
- writel_relaxed(data, bank->reg_base + bank->gpio_regs->port_ddr);
+ rockchip_gpio_writel_bit(bank, d->hwirq, 0,
+ bank->gpio_regs->port_ddr);
raw_spin_unlock_irqrestore(&bank->slock, flags);
@@ -308,23 +425,30 @@ static int rockchip_irq_set_type(struct irq_data *d, unsigned int type)
raw_spin_lock_irqsave(&bank->slock, flags);
irq_gc_lock(gc);
- level = readl_relaxed(gc->reg_base + bank->gpio_regs->int_type);
- polarity = readl_relaxed(gc->reg_base + bank->gpio_regs->int_polarity);
+ level = rockchip_gpio_readl(bank, bank->gpio_regs->int_type);
+ polarity = rockchip_gpio_readl(bank, bank->gpio_regs->int_polarity);
switch (type) {
case IRQ_TYPE_EDGE_BOTH:
- bank->toggle_edge_mode |= mask;
- level |= mask;
-
- /*
- * Determine gpio state. If 1 next interrupt should be falling
- * otherwise rising.
- */
- data = readl(bank->reg_base + bank->gpio_regs->ext_port);
- if (data & mask)
- polarity &= ~mask;
- else
- polarity |= mask;
+ if (bank->gpio_type == GPIO_TYPE_V2) {
+ bank->toggle_edge_mode &= ~mask;
+ rockchip_gpio_writel_bit(bank, d->hwirq, 1,
+ bank->gpio_regs->int_bothedge);
+ goto out;
+ } else {
+ bank->toggle_edge_mode |= mask;
+ level |= mask;
+
+ /*
+ * Determine gpio state. If 1 next interrupt should be
+ * falling otherwise rising.
+ */
+ data = readl(bank->reg_base + bank->gpio_regs->ext_port);
+ if (data & mask)
+ polarity &= ~mask;
+ else
+ polarity |= mask;
+ }
break;
case IRQ_TYPE_EDGE_RISING:
bank->toggle_edge_mode &= ~mask;
@@ -347,20 +471,18 @@ static int rockchip_irq_set_type(struct irq_data *d, unsigned int type)
polarity &= ~mask;
break;
default:
- irq_gc_unlock(gc);
- raw_spin_unlock_irqrestore(&bank->slock, flags);
- clk_disable(bank->clk);
- return -EINVAL;
+ ret = -EINVAL;
+ goto out;
}
- writel_relaxed(level, gc->reg_base + bank->gpio_regs->int_type);
- writel_relaxed(polarity, gc->reg_base + bank->gpio_regs->int_polarity);
-
+ rockchip_gpio_writel(bank, level, bank->gpio_regs->int_type);
+ rockchip_gpio_writel(bank, polarity, bank->gpio_regs->int_polarity);
+out:
irq_gc_unlock(gc);
raw_spin_unlock_irqrestore(&bank->slock, flags);
clk_disable(bank->clk);
- return 0;
+ return ret;
}
static void rockchip_irq_suspend(struct irq_data *d)
@@ -437,6 +559,11 @@ static int rockchip_interrupts_register(struct rockchip_pin_bank *bank)
}
gc = irq_get_domain_generic_chip(bank->domain, 0);
+ if (bank->gpio_type == GPIO_TYPE_V2) {
+ gc->reg_writel = gpio_writel_v2;
+ gc->reg_readl = gpio_readl_v2;
+ }
+
gc->reg_base = bank->reg_base;
gc->private = bank;
gc->chip_types[0].regs.mask = bank->gpio_regs->int_mask;
@@ -457,9 +584,9 @@ static int rockchip_interrupts_register(struct rockchip_pin_bank *bank)
* Our driver only uses the concept of masked and always keeps
* things enabled, so for us that's all masked and all enabled.
*/
- writel_relaxed(0xffffffff, bank->reg_base + bank->gpio_regs->int_mask);
- writel_relaxed(0xffffffff, bank->reg_base + bank->gpio_regs->port_eoi);
- writel_relaxed(0xffffffff, bank->reg_base + bank->gpio_regs->int_en);
+ rockchip_gpio_writel(bank, 0xffffffff, bank->gpio_regs->int_mask);
+ rockchip_gpio_writel(bank, 0xffffffff, bank->gpio_regs->port_eoi);
+ rockchip_gpio_writel(bank, 0xffffffff, bank->gpio_regs->int_en);
gc->mask_cache = 0xffffffff;
irq_set_chained_handler_and_data(bank->irq,
@@ -538,6 +665,7 @@ static int rockchip_gpiolib_register(struct rockchip_pin_bank *bank)
static int rockchip_get_bank_data(struct rockchip_pin_bank *bank)
{
struct resource res;
+ int id = 0;
if (of_address_to_resource(bank->of_node, 0, &res)) {
dev_err(bank->dev, "cannot find IO resource for bank\n");
@@ -549,15 +677,33 @@ static int rockchip_get_bank_data(struct rockchip_pin_bank *bank)
return PTR_ERR(bank->reg_base);
bank->irq = irq_of_parse_and_map(bank->of_node, 0);
-
- bank->gpio_regs = &gpio_regs_v1;
- bank->gpio_type = GPIO_TYPE_V1;
+ if (!bank->irq)
+ return -EINVAL;
bank->clk = of_clk_get(bank->of_node, 0);
- if (!IS_ERR(bank->clk))
- return clk_prepare(bank->clk);
+ if (IS_ERR(bank->clk))
+ return PTR_ERR(bank->clk);
+
+ clk_prepare_enable(bank->clk);
+ id = readl(bank->reg_base + gpio_regs_v2.version_id);
+
+ /* If not gpio v2, that is default to v1. */
+ if (id == GPIO_TYPE_V2) {
+ bank->gpio_regs = &gpio_regs_v2;
+ bank->gpio_type = GPIO_TYPE_V2;
+ bank->db_clk = of_clk_get(bank->of_node, 1);
+ if (IS_ERR(bank->db_clk)) {
+ dev_err(bank->dev, "cannot find debounce clk\n");
+ bank->db_clk = NULL;
+ clk_disable(bank->clk);
+ return -EINVAL;
+ }
+ } else {
+ bank->gpio_regs = &gpio_regs_v1;
+ bank->gpio_type = GPIO_TYPE_V1;
+ }
- bank->clk = NULL;
+ clk_disable(bank->clk);
return 0;
}
diff --git a/drivers/pinctrl/pinctrl-rockchip.h b/drivers/pinctrl/pinctrl-rockchip.h
index 1b774b6bbc3e..589d4d2a98c9 100644
--- a/drivers/pinctrl/pinctrl-rockchip.h
+++ b/drivers/pinctrl/pinctrl-rockchip.h
@@ -121,6 +121,7 @@ struct rockchip_drv {
* @reg_base: register base of the gpio bank
* @regmap_pull: optional separate register for additional pull settings
* @clk: clock of the gpio bank
+ * @db_clk: clock of the gpio debounce
* @irq: interrupt of the gpio bank
* @saved_masks: Saved content of GPIO_INTEN at suspend time.
* @pin_base: first pin number
@@ -146,6 +147,7 @@ struct rockchip_pin_bank {
void __iomem *reg_base;
struct regmap *regmap_pull;
struct clk *clk;
+ struct clk *db_clk;
int irq;
u32 saved_masks;
u32 pin_base;
--
2.25.1
Hi Jianqun,
Thanks a lot for the patch, and for pushing this work forward.
On Mon, 10 May 2021 at 03:36, Jianqun Xu <[email protected]> wrote:
>
> Separate gpio driver from pinctrl driver, and support v2 controller.
>
Usually, we expect to see a changelog here, with some information
about the changes from in v2, v3, etc. Also, note that only the cover
letter says "v4", and the individual patches are missing that. You can
use git-format-patch --subject-prefix="PATCH v4", and then all the patches
will get the same prefix.
However, the last patchset I can find in lore.kernel.org (or my
mailbox) is a v2:
https://lore.kernel.org/linux-gpio/[email protected]/
.. which was sent by Peter Geis (so you should have Cced him).
These are just some friendly tips on the submission process,
for you to keep in mind, you don't really need to resend the
series or do anything else.
Also, I added Bartosz the GPIO maintainer in Cc.
Thanks!
Ezequiel
> Jianqun Xu (7):
> pinctrl/rockchip: separate struct rockchip_pin_bank to a head file
> pinctrl/pinctrl-rockchip.h: add pinctrl device to gpio bank struct
> gpio: separate gpio driver from pinctrl-rockchip driver
> gpio/rockchip: use struct rockchip_gpio_regs for gpio controller
> gpio/rockchip: support next version gpio controller
> gpio/rockchip: always enable clock for gpio controller
> gpio/rockchip: drop irq_gc_lock/irq_gc_unlock for irq set type
>
> drivers/gpio/Kconfig | 8 +
> drivers/gpio/Makefile | 1 +
> drivers/gpio/gpio-rockchip.c | 758 ++++++++++++++++++++++++
> drivers/pinctrl/pinctrl-rockchip.c | 910 +----------------------------
> drivers/pinctrl/pinctrl-rockchip.h | 287 +++++++++
> 5 files changed, 1073 insertions(+), 891 deletions(-)
> create mode 100644 drivers/gpio/gpio-rockchip.c
> create mode 100644 drivers/pinctrl/pinctrl-rockchip.h
>
> --
> v5:
> - change to devel branch
>
> 2.25.1
>
>
>
On Tue, May 11, 2021 at 12:16 AM Ezequiel Garcia
<[email protected]> wrote:
>
> Hi Jianqun,
>
> Thanks a lot for the patch, and for pushing this work forward.
>
> On Mon, 10 May 2021 at 03:36, Jianqun Xu <[email protected]> wrote:
> >
> > Separate gpio driver from pinctrl driver, and support v2 controller.
> >
>
> Usually, we expect to see a changelog here, with some information
> about the changes from in v2, v3, etc. Also, note that only the cover
> letter says "v4", and the individual patches are missing that. You can
> use git-format-patch --subject-prefix="PATCH v4", and then all the patches
> will get the same prefix.
>
> However, the last patchset I can find in lore.kernel.org (or my
> mailbox) is a v2:
>
> https://lore.kernel.org/linux-gpio/[email protected]/
>
> .. which was sent by Peter Geis (so you should have Cced him).
>
> These are just some friendly tips on the submission process,
> for you to keep in mind, you don't really need to resend the
> series or do anything else.
>
> Also, I added Bartosz the GPIO maintainer in Cc.
>
Thanks. Jianqun: please resend the series so that I get it in my inbox.
Bart
Am Montag, 10. Mai 2021, 08:37:22 CEST schrieb Jianqun Xu:
> Since gate and ungate pclk of gpio has very litte benifit for system
> power consumption, just keep it always ungate.
>
> Signed-off-by: Jianqun Xu <[email protected]>
Reviewed-by: Heiko Stuebner <[email protected]>
I do agree with the reasoning and as an added benefit, we
also drop all the clk_enable calls that don't do proper error
handling right now ;-)
> ---
> drivers/gpio/gpio-rockchip.c | 68 +++++-------------------------------
> 1 file changed, 9 insertions(+), 59 deletions(-)
>
> diff --git a/drivers/gpio/gpio-rockchip.c b/drivers/gpio/gpio-rockchip.c
> index 92aaf1848449..048e7eecddba 100644
> --- a/drivers/gpio/gpio-rockchip.c
> +++ b/drivers/gpio/gpio-rockchip.c
> @@ -139,17 +139,8 @@ static int rockchip_gpio_get_direction(struct gpio_chip *chip,
> {
> struct rockchip_pin_bank *bank = gpiochip_get_data(chip);
> u32 data;
> - int ret;
>
> - ret = clk_enable(bank->clk);
> - if (ret < 0) {
> - dev_err(bank->drvdata->dev,
> - "failed to enable clock for bank %s\n", bank->name);
> - return ret;
> - }
> data = rockchip_gpio_readl_bit(bank, offset, bank->gpio_regs->port_ddr);
> - clk_disable(bank->clk);
> -
> if (data & BIT(offset))
> return GPIO_LINE_DIRECTION_OUT;
>
> @@ -163,11 +154,9 @@ static int rockchip_gpio_set_direction(struct gpio_chip *chip,
> unsigned long flags;
> u32 data = input ? 0 : 1;
>
> - clk_enable(bank->clk);
> raw_spin_lock_irqsave(&bank->slock, flags);
> rockchip_gpio_writel_bit(bank, offset, data, bank->gpio_regs->port_ddr);
> raw_spin_unlock_irqrestore(&bank->slock, flags);
> - clk_disable(bank->clk);
>
> return 0;
> }
> @@ -178,11 +167,9 @@ static void rockchip_gpio_set(struct gpio_chip *gc, unsigned int offset,
> struct rockchip_pin_bank *bank = gpiochip_get_data(gc);
> unsigned long flags;
>
> - clk_enable(bank->clk);
> raw_spin_lock_irqsave(&bank->slock, flags);
> rockchip_gpio_writel_bit(bank, offset, value, bank->gpio_regs->port_dr);
> raw_spin_unlock_irqrestore(&bank->slock, flags);
> - clk_disable(bank->clk);
> }
>
> static int rockchip_gpio_get(struct gpio_chip *gc, unsigned int offset)
> @@ -190,11 +177,10 @@ static int rockchip_gpio_get(struct gpio_chip *gc, unsigned int offset)
> struct rockchip_pin_bank *bank = gpiochip_get_data(gc);
> u32 data;
>
> - clk_enable(bank->clk);
> data = readl(bank->reg_base + bank->gpio_regs->ext_port);
> - clk_disable(bank->clk);
> data >>= offset;
> data &= 1;
> +
> return data;
> }
>
> @@ -315,9 +301,7 @@ static int rockchip_gpio_to_irq(struct gpio_chip *gc, unsigned int offset)
> if (!bank->domain)
> return -ENXIO;
>
> - clk_enable(bank->clk);
> virq = irq_create_mapping(bank->domain, offset);
> - clk_disable(bank->clk);
>
> return (virq) ? : -ENXIO;
> }
> @@ -409,7 +393,6 @@ static int rockchip_irq_set_type(struct irq_data *d, unsigned int type)
> unsigned long flags;
> int ret = 0;
>
> - clk_enable(bank->clk);
> raw_spin_lock_irqsave(&bank->slock, flags);
>
> rockchip_gpio_writel_bit(bank, d->hwirq, 0,
> @@ -480,7 +463,6 @@ static int rockchip_irq_set_type(struct irq_data *d, unsigned int type)
> out:
> irq_gc_unlock(gc);
> raw_spin_unlock_irqrestore(&bank->slock, flags);
> - clk_disable(bank->clk);
>
> return ret;
> }
> @@ -490,10 +472,8 @@ static void rockchip_irq_suspend(struct irq_data *d)
> struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
> struct rockchip_pin_bank *bank = gc->private;
>
> - clk_enable(bank->clk);
> bank->saved_masks = irq_reg_readl(gc, bank->gpio_regs->int_mask);
> irq_reg_writel(gc, ~gc->wake_active, bank->gpio_regs->int_mask);
> - clk_disable(bank->clk);
> }
>
> static void rockchip_irq_resume(struct irq_data *d)
> @@ -501,27 +481,7 @@ static void rockchip_irq_resume(struct irq_data *d)
> struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
> struct rockchip_pin_bank *bank = gc->private;
>
> - clk_enable(bank->clk);
> irq_reg_writel(gc, bank->saved_masks, bank->gpio_regs->int_mask);
> - clk_disable(bank->clk);
> -}
> -
> -static void rockchip_irq_enable(struct irq_data *d)
> -{
> - struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
> - struct rockchip_pin_bank *bank = gc->private;
> -
> - clk_enable(bank->clk);
> - irq_gc_mask_clr_bit(d);
> -}
> -
> -static void rockchip_irq_disable(struct irq_data *d)
> -{
> - struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
> - struct rockchip_pin_bank *bank = gc->private;
> -
> - irq_gc_mask_set_bit(d);
> - clk_disable(bank->clk);
> }
>
> static int rockchip_interrupts_register(struct rockchip_pin_bank *bank)
> @@ -530,19 +490,11 @@ static int rockchip_interrupts_register(struct rockchip_pin_bank *bank)
> struct irq_chip_generic *gc;
> int ret;
>
> - ret = clk_enable(bank->clk);
> - if (ret) {
> - dev_err(bank->dev, "failed to enable clock for bank %s\n",
> - bank->name);
> - return -EINVAL;
> - }
> -
> bank->domain = irq_domain_add_linear(bank->of_node, 32,
> &irq_generic_chip_ops, NULL);
> if (!bank->domain) {
> dev_warn(bank->dev, "could not init irq domain for bank %s\n",
> bank->name);
> - clk_disable(bank->clk);
> return -EINVAL;
> }
>
> @@ -554,7 +506,6 @@ static int rockchip_interrupts_register(struct rockchip_pin_bank *bank)
> dev_err(bank->dev, "could not alloc generic chips for bank %s\n",
> bank->name);
> irq_domain_remove(bank->domain);
> - clk_disable(bank->clk);
> return -EINVAL;
> }
>
> @@ -571,8 +522,8 @@ static int rockchip_interrupts_register(struct rockchip_pin_bank *bank)
> gc->chip_types[0].chip.irq_ack = irq_gc_ack_set_bit;
> gc->chip_types[0].chip.irq_mask = irq_gc_mask_set_bit;
> gc->chip_types[0].chip.irq_unmask = irq_gc_mask_clr_bit;
> - gc->chip_types[0].chip.irq_enable = rockchip_irq_enable;
> - gc->chip_types[0].chip.irq_disable = rockchip_irq_disable;
> + gc->chip_types[0].chip.irq_enable = irq_gc_mask_clr_bit;
> + gc->chip_types[0].chip.irq_disable = irq_gc_mask_set_bit;
> gc->chip_types[0].chip.irq_set_wake = irq_gc_set_wake;
> gc->chip_types[0].chip.irq_suspend = rockchip_irq_suspend;
> gc->chip_types[0].chip.irq_resume = rockchip_irq_resume;
> @@ -591,7 +542,6 @@ static int rockchip_interrupts_register(struct rockchip_pin_bank *bank)
>
> irq_set_chained_handler_and_data(bank->irq,
> rockchip_irq_demux, bank);
> - clk_disable(bank->clk);
>
> return 0;
> }
> @@ -695,7 +645,6 @@ static int rockchip_get_bank_data(struct rockchip_pin_bank *bank)
> if (IS_ERR(bank->db_clk)) {
> dev_err(bank->dev, "cannot find debounce clk\n");
> bank->db_clk = NULL;
> - clk_disable(bank->clk);
> return -EINVAL;
> }
> } else {
> @@ -703,7 +652,6 @@ static int rockchip_get_bank_data(struct rockchip_pin_bank *bank)
> bank->gpio_type = GPIO_TYPE_V1;
> }
>
> - clk_disable(bank->clk);
> return 0;
> }
>
> @@ -756,15 +704,17 @@ static int rockchip_gpio_probe(struct platform_device *pdev)
> return ret;
>
> ret = rockchip_gpiolib_register(bank);
> - if (ret) {
> - clk_disable_unprepare(bank->clk);
> - return ret;
> - }
> + if (ret)
> + goto err_clk;
>
> platform_set_drvdata(pdev, bank);
> dev_info(dev, "probed %pOF\n", np);
>
> return 0;
> +err_clk:
> + clk_disable_unprepare(bank->clk);
> +
> + return ret;
> }
>
> static int rockchip_gpio_remove(struct platform_device *pdev)
>
On Thu, May 13, 2021 at 10:49 PM Heiko Stuebner <[email protected]> wrote:
> Am Montag, 10. Mai 2021, 08:37:22 CEST schrieb Jianqun Xu:
> > Since gate and ungate pclk of gpio has very litte benifit for system
> > power consumption, just keep it always ungate.
> >
> > Signed-off-by: Jianqun Xu <[email protected]>
>
> Reviewed-by: Heiko Stuebner <[email protected]>
>
> I do agree with the reasoning and as an added benefit, we
> also drop all the clk_enable calls that don't do proper error
> handling right now ;-)
Heiko what is your general stance on the rest of the patches?
I was thinking I should merge them soon if you're not
against.
Yours,
Linus Walleij
Am Montag, 10. Mai 2021, 08:37:34 CEST schrieb Jianqun Xu:
> There has spin lock for irq set type already, so drop irq_gc_lock and
> irq_gc_unlock.
>
> Signed-off-by: Jianqun Xu <[email protected]>
I've looked that up, and yes the irq_gc_lock stuff really seems to be
redundant when there is already another spinlock in place, so
Reviewed-by: Heiko Stuebner <[email protected]>
Am Donnerstag, 20. Mai 2021, 01:18:24 CEST schrieb Linus Walleij:
> On Thu, May 13, 2021 at 10:49 PM Heiko Stuebner <[email protected]> wrote:
> > Am Montag, 10. Mai 2021, 08:37:22 CEST schrieb Jianqun Xu:
> > > Since gate and ungate pclk of gpio has very litte benifit for system
> > > power consumption, just keep it always ungate.
> > >
> > > Signed-off-by: Jianqun Xu <[email protected]>
> >
> > Reviewed-by: Heiko Stuebner <[email protected]>
> >
> > I do agree with the reasoning and as an added benefit, we
> > also drop all the clk_enable calls that don't do proper error
> > handling right now ;-)
>
> Heiko what is your general stance on the rest of the patches?
> I was thinking I should merge them soon if you're not
> against.
I'm definitly in favor of them :-)
I haven't seen any major issues, but sadly also haven't found the
time to really dig deeper into the other patches.
One thing pinctrl/gpio maintainer will need to check is if the
+#include "../pinctrl/core.h"
+#include "../pinctrl/pinctrl-rockchip.h"
(in patch 3)
is actually the right way to go.
Heiko
Hi Jianqun,
This series does not apply cleanly on kernel v5.13-rc1, can you rebase and
resend it so I can apply it? I hade some minor comment but overall it is very
good and I think we should apply it to move ahead with this series.
Yours,
Linus Walleij
On Mon, May 10, 2021 at 8:36 AM Jianqun Xu <[email protected]> wrote:
> Separate the gpio driver from the pinctrl driver.
>
> Signed-off-by: Jianqun Xu <[email protected]>
Overall this is very good and should be applied.
> +#include "../pinctrl/core.h"
> +#include "../pinctrl/pinctrl-rockchip.h"
Please explain in a comment exactly why you need to include
these files. I think it should be the goal to get rid of this dependency.
It seems that the driver can be further simplified using GPIO_GENERIC
but we can deal with this later, once it is separate.
> +static int rockchip_gpio_set_config(struct gpio_chip *gc, unsigned int offset,
> + unsigned long config)
> +{
> + enum pin_config_param param = pinconf_to_config_param(config);
> +
> + switch (param) {
> + case PIN_CONFIG_INPUT_DEBOUNCE:
> + rockchip_gpio_set_debounce(gc, offset, true);
(...)
> + .set_config = rockchip_gpio_set_config,
Can't you just use gpiochip_generic_config() and rely on the pinctrl
back-end to deal with this?
> + .to_irq = rockchip_gpio_to_irq,
Normally this should not be needed with GPIOLIB_IRQCHIP but
since you are refactoring an existing driver it is acceptable to
keep for now.
Yours,
Linus Walleij