diff options
author | Linus Walleij <linus.walleij@linaro.org> | 2018-08-29 15:10:57 +0300 |
---|---|---|
committer | Linus Walleij <linus.walleij@linaro.org> | 2018-08-29 15:10:57 +0300 |
commit | 888263947cd9f14e3f83be5b3e73ccabb71834d1 (patch) | |
tree | 9232e955814969890bc5ce9e57ca09c2fd97959a | |
parent | 62cdcb6c572ef0f55f4d6c06f008289f50eb516a (diff) | |
parent | 28d6eeb4f06634e5f7c98cad7b91c7981b82e3a6 (diff) | |
download | linux-888263947cd9f14e3f83be5b3e73ccabb71834d1.tar.xz |
Merge branch 'ib-ingenic' of ../linux-pinctrl into devel
-rw-r--r-- | Documentation/devicetree/bindings/gpio/ingenic,gpio.txt | 46 | ||||
-rw-r--r-- | Documentation/devicetree/bindings/pinctrl/ingenic,pinctrl.txt | 39 | ||||
-rw-r--r-- | drivers/gpio/Kconfig | 11 | ||||
-rw-r--r-- | drivers/gpio/Makefile | 1 | ||||
-rw-r--r-- | drivers/gpio/gpio-ingenic.c | 392 | ||||
-rw-r--r-- | drivers/pinctrl/Kconfig | 6 | ||||
-rw-r--r-- | drivers/pinctrl/pinctrl-ingenic.c | 462 |
7 files changed, 487 insertions, 470 deletions
diff --git a/Documentation/devicetree/bindings/gpio/ingenic,gpio.txt b/Documentation/devicetree/bindings/gpio/ingenic,gpio.txt deleted file mode 100644 index 7988aeb725f4..000000000000 --- a/Documentation/devicetree/bindings/gpio/ingenic,gpio.txt +++ /dev/null @@ -1,46 +0,0 @@ -Ingenic jz47xx GPIO controller - -That the Ingenic GPIO driver node must be a sub-node of the Ingenic pinctrl -driver node. - -Required properties: --------------------- - - - compatible: Must contain one of: - - "ingenic,jz4740-gpio" - - "ingenic,jz4770-gpio" - - "ingenic,jz4780-gpio" - - reg: The GPIO bank number. - - interrupt-controller: Marks the device node as an interrupt controller. - - interrupts: Interrupt specifier for the controllers interrupt. - - #interrupt-cells: Should be 2. Refer to - ../interrupt-controller/interrupts.txt for more details. - - gpio-controller: Marks the device node as a GPIO controller. - - #gpio-cells: Should be 2. The first cell is the GPIO number and the second - cell specifies GPIO flags, as defined in <dt-bindings/gpio/gpio.h>. Only the - GPIO_ACTIVE_HIGH and GPIO_ACTIVE_LOW flags are supported. - - gpio-ranges: Range of pins managed by the GPIO controller. Refer to - 'gpio.txt' in this directory for more details. - -Example: --------- - -&pinctrl { - #address-cells = <1>; - #size-cells = <0>; - - gpa: gpio@0 { - compatible = "ingenic,jz4740-gpio"; - reg = <0>; - - gpio-controller; - gpio-ranges = <&pinctrl 0 0 32>; - #gpio-cells = <2>; - - interrupt-controller; - #interrupt-cells = <2>; - - interrupt-parent = <&intc>; - interrupts = <28>; - }; -}; diff --git a/Documentation/devicetree/bindings/pinctrl/ingenic,pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/ingenic,pinctrl.txt index ca313a7aeaff..af20b0ec715c 100644 --- a/Documentation/devicetree/bindings/pinctrl/ingenic,pinctrl.txt +++ b/Documentation/devicetree/bindings/pinctrl/ingenic,pinctrl.txt @@ -20,16 +20,30 @@ Required properties: - compatible: One of: - "ingenic,jz4740-pinctrl" + - "ingenic,jz4725b-pinctrl" - "ingenic,jz4770-pinctrl" - "ingenic,jz4780-pinctrl" - reg: Address range of the pinctrl registers. -GPIO sub-nodes --------------- +Required properties for sub-nodes (GPIO chips): +----------------------------------------------- -The pinctrl node can have optional sub-nodes for the Ingenic GPIO driver; -please refer to ../gpio/ingenic,gpio.txt. + - compatible: Must contain one of: + - "ingenic,jz4740-gpio" + - "ingenic,jz4770-gpio" + - "ingenic,jz4780-gpio" + - reg: The GPIO bank number. + - interrupt-controller: Marks the device node as an interrupt controller. + - interrupts: Interrupt specifier for the controllers interrupt. + - #interrupt-cells: Should be 2. Refer to + ../interrupt-controller/interrupts.txt for more details. + - gpio-controller: Marks the device node as a GPIO controller. + - #gpio-cells: Should be 2. The first cell is the GPIO number and the second + cell specifies GPIO flags, as defined in <dt-bindings/gpio/gpio.h>. Only the + GPIO_ACTIVE_HIGH and GPIO_ACTIVE_LOW flags are supported. + - gpio-ranges: Range of pins managed by the GPIO controller. Refer to + ../gpio/gpio.txt for more details. Example: @@ -38,4 +52,21 @@ Example: pinctrl: pin-controller@10010000 { compatible = "ingenic,jz4740-pinctrl"; reg = <0x10010000 0x400>; + #address-cells = <1>; + #size-cells = <0>; + + gpa: gpio@0 { + compatible = "ingenic,jz4740-gpio"; + reg = <0>; + + gpio-controller; + gpio-ranges = <&pinctrl 0 0 32>; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + + interrupt-parent = <&intc>; + interrupts = <28>; + }; }; diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 8887ac4734ab..b8cbf5081389 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -268,17 +268,6 @@ config GPIO_ICH If unsure, say N. -config GPIO_INGENIC - tristate "Ingenic JZ47xx SoCs GPIO support" - depends on OF - depends on MACH_INGENIC || COMPILE_TEST - select GPIOLIB_IRQCHIP - help - Say yes here to support the GPIO functionality present on the - JZ4740 and JZ4780 SoCs from Ingenic. - - If unsure, say N. - config GPIO_IOP tristate "Intel IOP GPIO" depends on ARCH_IOP32X || ARCH_IOP33X || COMPILE_TEST diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index c256aff66a65..80d58c2de730 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -57,7 +57,6 @@ obj-$(CONFIG_GPIO_GRGPIO) += gpio-grgpio.o obj-$(CONFIG_GPIO_HLWD) += gpio-hlwd.o obj-$(CONFIG_HTC_EGPIO) += gpio-htc-egpio.o obj-$(CONFIG_GPIO_ICH) += gpio-ich.o -obj-$(CONFIG_GPIO_INGENIC) += gpio-ingenic.o obj-$(CONFIG_GPIO_IOP) += gpio-iop.o obj-$(CONFIG_GPIO_IT87) += gpio-it87.o obj-$(CONFIG_GPIO_JANZ_TTL) += gpio-janz-ttl.o diff --git a/drivers/gpio/gpio-ingenic.c b/drivers/gpio/gpio-ingenic.c deleted file mode 100644 index e738e384a5ca..000000000000 --- a/drivers/gpio/gpio-ingenic.c +++ /dev/null @@ -1,392 +0,0 @@ -/* - * Ingenic JZ47xx GPIO driver - * - * Copyright (c) 2017 Paul Cercueil <paul@crapouillou.net> - * - * License terms: GNU General Public License (GPL) version 2 - */ - -#include <linux/gpio/driver.h> -#include <linux/interrupt.h> -#include <linux/io.h> -#include <linux/module.h> -#include <linux/of_address.h> -#include <linux/of_device.h> -#include <linux/of_irq.h> -#include <linux/pinctrl/consumer.h> -#include <linux/regmap.h> - -#define GPIO_PIN 0x00 -#define GPIO_MSK 0x20 - -#define JZ4740_GPIO_DATA 0x10 -#define JZ4740_GPIO_SELECT 0x50 -#define JZ4740_GPIO_DIR 0x60 -#define JZ4740_GPIO_TRIG 0x70 -#define JZ4740_GPIO_FLAG 0x80 - -#define JZ4770_GPIO_INT 0x10 -#define JZ4770_GPIO_PAT1 0x30 -#define JZ4770_GPIO_PAT0 0x40 -#define JZ4770_GPIO_FLAG 0x50 - -#define REG_SET(x) ((x) + 0x4) -#define REG_CLEAR(x) ((x) + 0x8) - -enum jz_version { - ID_JZ4740, - ID_JZ4770, - ID_JZ4780, -}; - -struct ingenic_gpio_chip { - struct regmap *map; - struct gpio_chip gc; - struct irq_chip irq_chip; - unsigned int irq, reg_base; - enum jz_version version; -}; - -static u32 gpio_ingenic_read_reg(struct ingenic_gpio_chip *jzgc, u8 reg) -{ - unsigned int val; - - regmap_read(jzgc->map, jzgc->reg_base + reg, &val); - - return (u32) val; -} - -static void gpio_ingenic_set_bit(struct ingenic_gpio_chip *jzgc, - u8 reg, u8 offset, bool set) -{ - if (set) - reg = REG_SET(reg); - else - reg = REG_CLEAR(reg); - - regmap_write(jzgc->map, jzgc->reg_base + reg, BIT(offset)); -} - -static inline bool gpio_get_value(struct ingenic_gpio_chip *jzgc, u8 offset) -{ - unsigned int val = gpio_ingenic_read_reg(jzgc, GPIO_PIN); - - return !!(val & BIT(offset)); -} - -static void gpio_set_value(struct ingenic_gpio_chip *jzgc, u8 offset, int value) -{ - if (jzgc->version >= ID_JZ4770) - gpio_ingenic_set_bit(jzgc, JZ4770_GPIO_PAT0, offset, !!value); - else - gpio_ingenic_set_bit(jzgc, JZ4740_GPIO_DATA, offset, !!value); -} - -static void irq_set_type(struct ingenic_gpio_chip *jzgc, - u8 offset, unsigned int type) -{ - u8 reg1, reg2; - - if (jzgc->version >= ID_JZ4770) { - reg1 = JZ4770_GPIO_PAT1; - reg2 = JZ4770_GPIO_PAT0; - } else { - reg1 = JZ4740_GPIO_TRIG; - reg2 = JZ4740_GPIO_DIR; - } - - switch (type) { - case IRQ_TYPE_EDGE_RISING: - gpio_ingenic_set_bit(jzgc, reg2, offset, true); - gpio_ingenic_set_bit(jzgc, reg1, offset, true); - break; - case IRQ_TYPE_EDGE_FALLING: - gpio_ingenic_set_bit(jzgc, reg2, offset, false); - gpio_ingenic_set_bit(jzgc, reg1, offset, true); - break; - case IRQ_TYPE_LEVEL_HIGH: - gpio_ingenic_set_bit(jzgc, reg2, offset, true); - gpio_ingenic_set_bit(jzgc, reg1, offset, false); - break; - case IRQ_TYPE_LEVEL_LOW: - default: - gpio_ingenic_set_bit(jzgc, reg2, offset, false); - gpio_ingenic_set_bit(jzgc, reg1, offset, false); - break; - } -} - -static void ingenic_gpio_irq_mask(struct irq_data *irqd) -{ - struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd); - struct ingenic_gpio_chip *jzgc = gpiochip_get_data(gc); - - gpio_ingenic_set_bit(jzgc, GPIO_MSK, irqd->hwirq, true); -} - -static void ingenic_gpio_irq_unmask(struct irq_data *irqd) -{ - struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd); - struct ingenic_gpio_chip *jzgc = gpiochip_get_data(gc); - - gpio_ingenic_set_bit(jzgc, GPIO_MSK, irqd->hwirq, false); -} - -static void ingenic_gpio_irq_enable(struct irq_data *irqd) -{ - struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd); - struct ingenic_gpio_chip *jzgc = gpiochip_get_data(gc); - int irq = irqd->hwirq; - - if (jzgc->version >= ID_JZ4770) - gpio_ingenic_set_bit(jzgc, JZ4770_GPIO_INT, irq, true); - else - gpio_ingenic_set_bit(jzgc, JZ4740_GPIO_SELECT, irq, true); - - ingenic_gpio_irq_unmask(irqd); -} - -static void ingenic_gpio_irq_disable(struct irq_data *irqd) -{ - struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd); - struct ingenic_gpio_chip *jzgc = gpiochip_get_data(gc); - int irq = irqd->hwirq; - - ingenic_gpio_irq_mask(irqd); - - if (jzgc->version >= ID_JZ4770) - gpio_ingenic_set_bit(jzgc, JZ4770_GPIO_INT, irq, false); - else - gpio_ingenic_set_bit(jzgc, JZ4740_GPIO_SELECT, irq, false); -} - -static void ingenic_gpio_irq_ack(struct irq_data *irqd) -{ - struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd); - struct ingenic_gpio_chip *jzgc = gpiochip_get_data(gc); - int irq = irqd->hwirq; - bool high; - - if (irqd_get_trigger_type(irqd) == IRQ_TYPE_EDGE_BOTH) { - /* - * Switch to an interrupt for the opposite edge to the one that - * triggered the interrupt being ACKed. - */ - high = gpio_get_value(jzgc, irq); - if (high) - irq_set_type(jzgc, irq, IRQ_TYPE_EDGE_FALLING); - else - irq_set_type(jzgc, irq, IRQ_TYPE_EDGE_RISING); - } - - if (jzgc->version >= ID_JZ4770) - gpio_ingenic_set_bit(jzgc, JZ4770_GPIO_FLAG, irq, false); - else - gpio_ingenic_set_bit(jzgc, JZ4740_GPIO_DATA, irq, true); -} - -static int ingenic_gpio_irq_set_type(struct irq_data *irqd, unsigned int type) -{ - struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd); - struct ingenic_gpio_chip *jzgc = gpiochip_get_data(gc); - - switch (type) { - case IRQ_TYPE_EDGE_BOTH: - case IRQ_TYPE_EDGE_RISING: - case IRQ_TYPE_EDGE_FALLING: - irq_set_handler_locked(irqd, handle_edge_irq); - break; - case IRQ_TYPE_LEVEL_HIGH: - case IRQ_TYPE_LEVEL_LOW: - irq_set_handler_locked(irqd, handle_level_irq); - break; - default: - irq_set_handler_locked(irqd, handle_bad_irq); - } - - if (type == IRQ_TYPE_EDGE_BOTH) { - /* - * The hardware does not support interrupts on both edges. The - * best we can do is to set up a single-edge interrupt and then - * switch to the opposing edge when ACKing the interrupt. - */ - bool high = gpio_get_value(jzgc, irqd->hwirq); - - type = high ? IRQ_TYPE_EDGE_FALLING : IRQ_TYPE_EDGE_RISING; - } - - irq_set_type(jzgc, irqd->hwirq, type); - return 0; -} - -static int ingenic_gpio_irq_set_wake(struct irq_data *irqd, unsigned int on) -{ - struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd); - struct ingenic_gpio_chip *jzgc = gpiochip_get_data(gc); - - return irq_set_irq_wake(jzgc->irq, on); -} - -static void ingenic_gpio_irq_handler(struct irq_desc *desc) -{ - struct gpio_chip *gc = irq_desc_get_handler_data(desc); - struct ingenic_gpio_chip *jzgc = gpiochip_get_data(gc); - struct irq_chip *irq_chip = irq_data_get_irq_chip(&desc->irq_data); - unsigned long flag, i; - - chained_irq_enter(irq_chip, desc); - - if (jzgc->version >= ID_JZ4770) - flag = gpio_ingenic_read_reg(jzgc, JZ4770_GPIO_FLAG); - else - flag = gpio_ingenic_read_reg(jzgc, JZ4740_GPIO_FLAG); - - for_each_set_bit(i, &flag, 32) - generic_handle_irq(irq_linear_revmap(gc->irq.domain, i)); - chained_irq_exit(irq_chip, desc); -} - -static void ingenic_gpio_set(struct gpio_chip *gc, - unsigned int offset, int value) -{ - struct ingenic_gpio_chip *jzgc = gpiochip_get_data(gc); - - gpio_set_value(jzgc, offset, value); -} - -static int ingenic_gpio_get(struct gpio_chip *gc, unsigned int offset) -{ - struct ingenic_gpio_chip *jzgc = gpiochip_get_data(gc); - - return (int) gpio_get_value(jzgc, offset); -} - -static int ingenic_gpio_direction_input(struct gpio_chip *gc, - unsigned int offset) -{ - return pinctrl_gpio_direction_input(gc->base + offset); -} - -static int ingenic_gpio_direction_output(struct gpio_chip *gc, - unsigned int offset, int value) -{ - ingenic_gpio_set(gc, offset, value); - return pinctrl_gpio_direction_output(gc->base + offset); -} - -static const struct of_device_id ingenic_gpio_of_match[] = { - { .compatible = "ingenic,jz4740-gpio", .data = (void *)ID_JZ4740 }, - { .compatible = "ingenic,jz4770-gpio", .data = (void *)ID_JZ4770 }, - { .compatible = "ingenic,jz4780-gpio", .data = (void *)ID_JZ4780 }, - {}, -}; -MODULE_DEVICE_TABLE(of, ingenic_gpio_of_match); - -static int ingenic_gpio_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct ingenic_gpio_chip *jzgc; - u32 bank; - int err; - - jzgc = devm_kzalloc(dev, sizeof(*jzgc), GFP_KERNEL); - if (!jzgc) - return -ENOMEM; - - jzgc->map = dev_get_drvdata(dev->parent); - if (!jzgc->map) { - dev_err(dev, "Cannot get parent regmap\n"); - return -ENXIO; - } - - err = of_property_read_u32(dev->of_node, "reg", &bank); - if (err) { - dev_err(dev, "Cannot read \"reg\" property: %i\n", err); - return err; - } - - jzgc->reg_base = bank * 0x100; - - jzgc->gc.label = devm_kasprintf(dev, GFP_KERNEL, "GPIO%c", 'A' + bank); - if (!jzgc->gc.label) - return -ENOMEM; - - /* DO NOT EXPAND THIS: FOR BACKWARD GPIO NUMBERSPACE COMPATIBIBILITY - * ONLY: WORK TO TRANSITION CONSUMERS TO USE THE GPIO DESCRIPTOR API IN - * <linux/gpio/consumer.h> INSTEAD. - */ - jzgc->gc.base = bank * 32; - - jzgc->gc.ngpio = 32; - jzgc->gc.parent = dev; - jzgc->gc.of_node = dev->of_node; - jzgc->gc.owner = THIS_MODULE; - jzgc->version = (enum jz_version)of_device_get_match_data(dev); - - jzgc->gc.set = ingenic_gpio_set; - jzgc->gc.get = ingenic_gpio_get; - jzgc->gc.direction_input = ingenic_gpio_direction_input; - jzgc->gc.direction_output = ingenic_gpio_direction_output; - - if (of_property_read_bool(dev->of_node, "gpio-ranges")) { - jzgc->gc.request = gpiochip_generic_request; - jzgc->gc.free = gpiochip_generic_free; - } - - err = devm_gpiochip_add_data(dev, &jzgc->gc, jzgc); - if (err) - return err; - - jzgc->irq = irq_of_parse_and_map(dev->of_node, 0); - if (!jzgc->irq) - return -EINVAL; - - jzgc->irq_chip.name = jzgc->gc.label; - jzgc->irq_chip.irq_enable = ingenic_gpio_irq_enable; - jzgc->irq_chip.irq_disable = ingenic_gpio_irq_disable; - jzgc->irq_chip.irq_unmask = ingenic_gpio_irq_unmask; - jzgc->irq_chip.irq_mask = ingenic_gpio_irq_mask; - jzgc->irq_chip.irq_ack = ingenic_gpio_irq_ack; - jzgc->irq_chip.irq_set_type = ingenic_gpio_irq_set_type; - jzgc->irq_chip.irq_set_wake = ingenic_gpio_irq_set_wake; - jzgc->irq_chip.flags = IRQCHIP_MASK_ON_SUSPEND; - - err = gpiochip_irqchip_add(&jzgc->gc, &jzgc->irq_chip, 0, - handle_level_irq, IRQ_TYPE_NONE); - if (err) - return err; - - gpiochip_set_chained_irqchip(&jzgc->gc, &jzgc->irq_chip, - jzgc->irq, ingenic_gpio_irq_handler); - return 0; -} - -static int ingenic_gpio_remove(struct platform_device *pdev) -{ - return 0; -} - -static struct platform_driver ingenic_gpio_driver = { - .driver = { - .name = "gpio-ingenic", - .of_match_table = of_match_ptr(ingenic_gpio_of_match), - }, - .probe = ingenic_gpio_probe, - .remove = ingenic_gpio_remove, -}; - -static int __init ingenic_gpio_drv_register(void) -{ - return platform_driver_register(&ingenic_gpio_driver); -} -subsys_initcall(ingenic_gpio_drv_register); - -static void __exit ingenic_gpio_drv_unregister(void) -{ - platform_driver_unregister(&ingenic_gpio_driver); -} -module_exit(ingenic_gpio_drv_unregister); - -MODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>"); -MODULE_DESCRIPTION("Ingenic JZ47xx GPIO driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index e86752be1f19..f7be9e1455b5 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -309,12 +309,14 @@ config PINCTRL_ZYNQ config PINCTRL_INGENIC bool "Pinctrl driver for the Ingenic JZ47xx SoCs" - default y + default MACH_INGENIC depends on OF - depends on MACH_INGENIC || COMPILE_TEST + depends on MIPS || COMPILE_TEST select GENERIC_PINCONF select GENERIC_PINCTRL_GROUPS select GENERIC_PINMUX_FUNCTIONS + select GPIOLIB + select GPIOLIB_IRQCHIP select REGMAP_MMIO config PINCTRL_RK805 diff --git a/drivers/pinctrl/pinctrl-ingenic.c b/drivers/pinctrl/pinctrl-ingenic.c index 6a1b6058b991..97821514baf5 100644 --- a/drivers/pinctrl/pinctrl-ingenic.c +++ b/drivers/pinctrl/pinctrl-ingenic.c @@ -7,10 +7,11 @@ */ #include <linux/compiler.h> -#include <linux/gpio.h> +#include <linux/gpio/driver.h> #include <linux/interrupt.h> #include <linux/io.h> #include <linux/of_device.h> +#include <linux/of_irq.h> #include <linux/of_platform.h> #include <linux/pinctrl/pinctrl.h> #include <linux/pinctrl/pinmux.h> @@ -24,6 +25,9 @@ #include "pinconf.h" #include "pinmux.h" +#define GPIO_PIN 0x00 +#define GPIO_MSK 0x20 + #define JZ4740_GPIO_DATA 0x10 #define JZ4740_GPIO_PULL_DIS 0x30 #define JZ4740_GPIO_FUNC 0x40 @@ -33,7 +37,6 @@ #define JZ4740_GPIO_FLAG 0x80 #define JZ4770_GPIO_INT 0x10 -#define JZ4770_GPIO_MSK 0x20 #define JZ4770_GPIO_PAT1 0x30 #define JZ4770_GPIO_PAT0 0x40 #define JZ4770_GPIO_FLAG 0x50 @@ -46,6 +49,7 @@ enum jz_version { ID_JZ4740, + ID_JZ4725B, ID_JZ4770, ID_JZ4780, }; @@ -72,6 +76,13 @@ struct ingenic_pinctrl { const struct ingenic_chip_info *info; }; +struct ingenic_gpio_chip { + struct ingenic_pinctrl *jzpc; + struct gpio_chip gc; + struct irq_chip irq_chip; + unsigned int irq, reg_base; +}; + static const u32 jz4740_pull_ups[4] = { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, }; @@ -205,6 +216,99 @@ static const struct ingenic_chip_info jz4740_chip_info = { .pull_downs = jz4740_pull_downs, }; +static int jz4725b_mmc0_1bit_pins[] = { 0x48, 0x49, 0x5c, }; +static int jz4725b_mmc0_4bit_pins[] = { 0x5d, 0x5b, 0x56, }; +static int jz4725b_mmc1_1bit_pins[] = { 0x7a, 0x7b, 0x7c, }; +static int jz4725b_mmc1_4bit_pins[] = { 0x7d, 0x7e, 0x7f, }; +static int jz4725b_uart_data_pins[] = { 0x4c, 0x4d, }; +static int jz4725b_nand_cs1_pins[] = { 0x55, }; +static int jz4725b_nand_cs2_pins[] = { 0x56, }; +static int jz4725b_nand_cs3_pins[] = { 0x57, }; +static int jz4725b_nand_cs4_pins[] = { 0x58, }; +static int jz4725b_nand_cle_ale_pins[] = { 0x48, 0x49 }; +static int jz4725b_nand_fre_fwe_pins[] = { 0x5c, 0x5d }; +static int jz4725b_pwm_pwm0_pins[] = { 0x4a, }; +static int jz4725b_pwm_pwm1_pins[] = { 0x4b, }; +static int jz4725b_pwm_pwm2_pins[] = { 0x4c, }; +static int jz4725b_pwm_pwm3_pins[] = { 0x4d, }; +static int jz4725b_pwm_pwm4_pins[] = { 0x4e, }; +static int jz4725b_pwm_pwm5_pins[] = { 0x4f, }; + +static int jz4725b_mmc0_1bit_funcs[] = { 1, 1, 1, }; +static int jz4725b_mmc0_4bit_funcs[] = { 1, 0, 1, }; +static int jz4725b_mmc1_1bit_funcs[] = { 0, 0, 0, }; +static int jz4725b_mmc1_4bit_funcs[] = { 0, 0, 0, }; +static int jz4725b_uart_data_funcs[] = { 1, 1, }; +static int jz4725b_nand_cs1_funcs[] = { 0, }; +static int jz4725b_nand_cs2_funcs[] = { 0, }; +static int jz4725b_nand_cs3_funcs[] = { 0, }; +static int jz4725b_nand_cs4_funcs[] = { 0, }; +static int jz4725b_nand_cle_ale_funcs[] = { 0, 0, }; +static int jz4725b_nand_fre_fwe_funcs[] = { 0, 0, }; +static int jz4725b_pwm_pwm0_funcs[] = { 0, }; +static int jz4725b_pwm_pwm1_funcs[] = { 0, }; +static int jz4725b_pwm_pwm2_funcs[] = { 0, }; +static int jz4725b_pwm_pwm3_funcs[] = { 0, }; +static int jz4725b_pwm_pwm4_funcs[] = { 0, }; +static int jz4725b_pwm_pwm5_funcs[] = { 0, }; + +static const struct group_desc jz4725b_groups[] = { + INGENIC_PIN_GROUP("mmc0-1bit", jz4725b_mmc0_1bit), + INGENIC_PIN_GROUP("mmc0-4bit", jz4725b_mmc0_4bit), + INGENIC_PIN_GROUP("mmc1-1bit", jz4725b_mmc1_1bit), + INGENIC_PIN_GROUP("mmc1-4bit", jz4725b_mmc1_4bit), + INGENIC_PIN_GROUP("uart-data", jz4725b_uart_data), + INGENIC_PIN_GROUP("nand-cs1", jz4725b_nand_cs1), + INGENIC_PIN_GROUP("nand-cs2", jz4725b_nand_cs2), + INGENIC_PIN_GROUP("nand-cs3", jz4725b_nand_cs3), + INGENIC_PIN_GROUP("nand-cs4", jz4725b_nand_cs4), + INGENIC_PIN_GROUP("nand-cle-ale", jz4725b_nand_cle_ale), + INGENIC_PIN_GROUP("nand-fre-fwe", jz4725b_nand_fre_fwe), + INGENIC_PIN_GROUP("pwm0", jz4725b_pwm_pwm0), + INGENIC_PIN_GROUP("pwm1", jz4725b_pwm_pwm1), + INGENIC_PIN_GROUP("pwm2", jz4725b_pwm_pwm2), + INGENIC_PIN_GROUP("pwm3", jz4725b_pwm_pwm3), + INGENIC_PIN_GROUP("pwm4", jz4725b_pwm_pwm4), + INGENIC_PIN_GROUP("pwm5", jz4725b_pwm_pwm5), +}; + +static const char *jz4725b_mmc0_groups[] = { "mmc0-1bit", "mmc0-4bit", }; +static const char *jz4725b_mmc1_groups[] = { "mmc1-1bit", "mmc1-4bit", }; +static const char *jz4725b_uart_groups[] = { "uart-data", }; +static const char *jz4725b_nand_groups[] = { + "nand-cs1", "nand-cs2", "nand-cs3", "nand-cs4", + "nand-cle-ale", "nand-fre-fwe", +}; +static const char *jz4725b_pwm0_groups[] = { "pwm0", }; +static const char *jz4725b_pwm1_groups[] = { "pwm1", }; +static const char *jz4725b_pwm2_groups[] = { "pwm2", }; +static const char *jz4725b_pwm3_groups[] = { "pwm3", }; +static const char *jz4725b_pwm4_groups[] = { "pwm4", }; +static const char *jz4725b_pwm5_groups[] = { "pwm5", }; + +static const struct function_desc jz4725b_functions[] = { + { "mmc0", jz4725b_mmc0_groups, ARRAY_SIZE(jz4725b_mmc0_groups), }, + { "mmc1", jz4725b_mmc1_groups, ARRAY_SIZE(jz4725b_mmc1_groups), }, + { "uart", jz4725b_uart_groups, ARRAY_SIZE(jz4725b_uart_groups), }, + { "nand", jz4725b_nand_groups, ARRAY_SIZE(jz4725b_nand_groups), }, + { "pwm0", jz4725b_pwm0_groups, ARRAY_SIZE(jz4725b_pwm0_groups), }, + { "pwm1", jz4725b_pwm1_groups, ARRAY_SIZE(jz4725b_pwm1_groups), }, + { "pwm2", jz4725b_pwm2_groups, ARRAY_SIZE(jz4725b_pwm2_groups), }, + { "pwm3", jz4725b_pwm3_groups, ARRAY_SIZE(jz4725b_pwm3_groups), }, + { "pwm4", jz4725b_pwm4_groups, ARRAY_SIZE(jz4725b_pwm4_groups), }, + { "pwm5", jz4725b_pwm5_groups, ARRAY_SIZE(jz4725b_pwm5_groups), }, +}; + +static const struct ingenic_chip_info jz4725b_chip_info = { + .num_chips = 4, + .groups = jz4725b_groups, + .num_groups = ARRAY_SIZE(jz4725b_groups), + .functions = jz4725b_functions, + .num_functions = ARRAY_SIZE(jz4725b_functions), + .pull_ups = jz4740_pull_ups, + .pull_downs = jz4740_pull_downs, +}; + static const u32 jz4770_pull_ups[6] = { 0x3fffffff, 0xfff0030c, 0xffffffff, 0xffff4fff, 0xfffffb7c, 0xffa7f00f, }; @@ -438,6 +542,235 @@ static const struct ingenic_chip_info jz4770_chip_info = { .pull_downs = jz4770_pull_downs, }; +static u32 gpio_ingenic_read_reg(struct ingenic_gpio_chip *jzgc, u8 reg) +{ + unsigned int val; + + regmap_read(jzgc->jzpc->map, jzgc->reg_base + reg, &val); + + return (u32) val; +} + +static void gpio_ingenic_set_bit(struct ingenic_gpio_chip *jzgc, + u8 reg, u8 offset, bool set) +{ + if (set) + reg = REG_SET(reg); + else + reg = REG_CLEAR(reg); + + regmap_write(jzgc->jzpc->map, jzgc->reg_base + reg, BIT(offset)); +} + +static inline bool ingenic_gpio_get_value(struct ingenic_gpio_chip *jzgc, + u8 offset) +{ + unsigned int val = gpio_ingenic_read_reg(jzgc, GPIO_PIN); + + return !!(val & BIT(offset)); +} + +static void ingenic_gpio_set_value(struct ingenic_gpio_chip *jzgc, + u8 offset, int value) +{ + if (jzgc->jzpc->version >= ID_JZ4770) + gpio_ingenic_set_bit(jzgc, JZ4770_GPIO_PAT0, offset, !!value); + else + gpio_ingenic_set_bit(jzgc, JZ4740_GPIO_DATA, offset, !!value); +} + +static void irq_set_type(struct ingenic_gpio_chip *jzgc, + u8 offset, unsigned int type) +{ + u8 reg1, reg2; + + if (jzgc->jzpc->version >= ID_JZ4770) { + reg1 = JZ4770_GPIO_PAT1; + reg2 = JZ4770_GPIO_PAT0; + } else { + reg1 = JZ4740_GPIO_TRIG; + reg2 = JZ4740_GPIO_DIR; + } + + switch (type) { + case IRQ_TYPE_EDGE_RISING: + gpio_ingenic_set_bit(jzgc, reg2, offset, true); + gpio_ingenic_set_bit(jzgc, reg1, offset, true); + break; + case IRQ_TYPE_EDGE_FALLING: + gpio_ingenic_set_bit(jzgc, reg2, offset, false); + gpio_ingenic_set_bit(jzgc, reg1, offset, true); + break; + case IRQ_TYPE_LEVEL_HIGH: + gpio_ingenic_set_bit(jzgc, reg2, offset, true); + gpio_ingenic_set_bit(jzgc, reg1, offset, false); + break; + case IRQ_TYPE_LEVEL_LOW: + default: + gpio_ingenic_set_bit(jzgc, reg2, offset, false); + gpio_ingenic_set_bit(jzgc, reg1, offset, false); + break; + } +} + +static void ingenic_gpio_irq_mask(struct irq_data *irqd) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd); + struct ingenic_gpio_chip *jzgc = gpiochip_get_data(gc); + + gpio_ingenic_set_bit(jzgc, GPIO_MSK, irqd->hwirq, true); +} + +static void ingenic_gpio_irq_unmask(struct irq_data *irqd) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd); + struct ingenic_gpio_chip *jzgc = gpiochip_get_data(gc); + + gpio_ingenic_set_bit(jzgc, GPIO_MSK, irqd->hwirq, false); +} + +static void ingenic_gpio_irq_enable(struct irq_data *irqd) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd); + struct ingenic_gpio_chip *jzgc = gpiochip_get_data(gc); + int irq = irqd->hwirq; + + if (jzgc->jzpc->version >= ID_JZ4770) + gpio_ingenic_set_bit(jzgc, JZ4770_GPIO_INT, irq, true); + else + gpio_ingenic_set_bit(jzgc, JZ4740_GPIO_SELECT, irq, true); + + ingenic_gpio_irq_unmask(irqd); +} + +static void ingenic_gpio_irq_disable(struct irq_data *irqd) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd); + struct ingenic_gpio_chip *jzgc = gpiochip_get_data(gc); + int irq = irqd->hwirq; + + ingenic_gpio_irq_mask(irqd); + + if (jzgc->jzpc->version >= ID_JZ4770) + gpio_ingenic_set_bit(jzgc, JZ4770_GPIO_INT, irq, false); + else + gpio_ingenic_set_bit(jzgc, JZ4740_GPIO_SELECT, irq, false); +} + +static void ingenic_gpio_irq_ack(struct irq_data *irqd) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd); + struct ingenic_gpio_chip *jzgc = gpiochip_get_data(gc); + int irq = irqd->hwirq; + bool high; + + if (irqd_get_trigger_type(irqd) == IRQ_TYPE_EDGE_BOTH) { + /* + * Switch to an interrupt for the opposite edge to the one that + * triggered the interrupt being ACKed. + */ + high = ingenic_gpio_get_value(jzgc, irq); + if (high) + irq_set_type(jzgc, irq, IRQ_TYPE_EDGE_FALLING); + else + irq_set_type(jzgc, irq, IRQ_TYPE_EDGE_RISING); + } + + if (jzgc->jzpc->version >= ID_JZ4770) + gpio_ingenic_set_bit(jzgc, JZ4770_GPIO_FLAG, irq, false); + else + gpio_ingenic_set_bit(jzgc, JZ4740_GPIO_DATA, irq, true); +} + +static int ingenic_gpio_irq_set_type(struct irq_data *irqd, unsigned int type) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd); + struct ingenic_gpio_chip *jzgc = gpiochip_get_data(gc); + + switch (type) { + case IRQ_TYPE_EDGE_BOTH: + case IRQ_TYPE_EDGE_RISING: + case IRQ_TYPE_EDGE_FALLING: + irq_set_handler_locked(irqd, handle_edge_irq); + break; + case IRQ_TYPE_LEVEL_HIGH: + case IRQ_TYPE_LEVEL_LOW: + irq_set_handler_locked(irqd, handle_level_irq); + break; + default: + irq_set_handler_locked(irqd, handle_bad_irq); + } + + if (type == IRQ_TYPE_EDGE_BOTH) { + /* + * The hardware does not support interrupts on both edges. The + * best we can do is to set up a single-edge interrupt and then + * switch to the opposing edge when ACKing the interrupt. + */ + bool high = ingenic_gpio_get_value(jzgc, irqd->hwirq); + + type = high ? IRQ_TYPE_EDGE_FALLING : IRQ_TYPE_EDGE_RISING; + } + + irq_set_type(jzgc, irqd->hwirq, type); + return 0; +} + +static int ingenic_gpio_irq_set_wake(struct irq_data *irqd, unsigned int on) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd); + struct ingenic_gpio_chip *jzgc = gpiochip_get_data(gc); + + return irq_set_irq_wake(jzgc->irq, on); +} + +static void ingenic_gpio_irq_handler(struct irq_desc *desc) +{ + struct gpio_chip *gc = irq_desc_get_handler_data(desc); + struct ingenic_gpio_chip *jzgc = gpiochip_get_data(gc); + struct irq_chip *irq_chip = irq_data_get_irq_chip(&desc->irq_data); + unsigned long flag, i; + + chained_irq_enter(irq_chip, desc); + + if (jzgc->jzpc->version >= ID_JZ4770) + flag = gpio_ingenic_read_reg(jzgc, JZ4770_GPIO_FLAG); + else + flag = gpio_ingenic_read_reg(jzgc, JZ4740_GPIO_FLAG); + + for_each_set_bit(i, &flag, 32) + generic_handle_irq(irq_linear_revmap(gc->irq.domain, i)); + chained_irq_exit(irq_chip, desc); +} + +static void ingenic_gpio_set(struct gpio_chip *gc, + unsigned int offset, int value) +{ + struct ingenic_gpio_chip *jzgc = gpiochip_get_data(gc); + + ingenic_gpio_set_value(jzgc, offset, value); +} + +static int ingenic_gpio_get(struct gpio_chip *gc, unsigned int offset) +{ + struct ingenic_gpio_chip *jzgc = gpiochip_get_data(gc); + + return (int) ingenic_gpio_get_value(jzgc, offset); +} + +static int ingenic_gpio_direction_input(struct gpio_chip *gc, + unsigned int offset) +{ + return pinctrl_gpio_direction_input(gc->base + offset); +} + +static int ingenic_gpio_direction_output(struct gpio_chip *gc, + unsigned int offset, int value) +{ + ingenic_gpio_set(gc, offset, value); + return pinctrl_gpio_direction_output(gc->base + offset); +} + static inline void ingenic_config_pin(struct ingenic_pinctrl *jzpc, unsigned int pin, u8 reg, bool set) { @@ -460,6 +793,21 @@ static inline bool ingenic_get_pin_config(struct ingenic_pinctrl *jzpc, return val & BIT(idx); } +static int ingenic_gpio_get_direction(struct gpio_chip *gc, unsigned int offset) +{ + struct ingenic_gpio_chip *jzgc = gpiochip_get_data(gc); + struct ingenic_pinctrl *jzpc = jzgc->jzpc; + unsigned int pin = gc->base + offset; + + if (jzpc->version >= ID_JZ4770) + return ingenic_get_pin_config(jzpc, pin, JZ4770_GPIO_PAT1); + + if (ingenic_get_pin_config(jzpc, pin, JZ4740_GPIO_SELECT)) + return true; + + return !ingenic_get_pin_config(jzpc, pin, JZ4740_GPIO_DIR); +} + static const struct pinctrl_ops ingenic_pctlops = { .get_groups_count = pinctrl_generic_get_group_count, .get_group_name = pinctrl_generic_get_group_name, @@ -479,7 +827,7 @@ static int ingenic_pinmux_set_pin_fn(struct ingenic_pinctrl *jzpc, if (jzpc->version >= ID_JZ4770) { ingenic_config_pin(jzpc, pin, JZ4770_GPIO_INT, false); - ingenic_config_pin(jzpc, pin, JZ4770_GPIO_MSK, false); + ingenic_config_pin(jzpc, pin, GPIO_MSK, false); ingenic_config_pin(jzpc, pin, JZ4770_GPIO_PAT1, func & 0x2); ingenic_config_pin(jzpc, pin, JZ4770_GPIO_PAT0, func & 0x1); } else { @@ -532,7 +880,7 @@ static int ingenic_pinmux_gpio_set_direction(struct pinctrl_dev *pctldev, if (jzpc->version >= ID_JZ4770) { ingenic_config_pin(jzpc, pin, JZ4770_GPIO_INT, false); - ingenic_config_pin(jzpc, pin, JZ4770_GPIO_MSK, true); + ingenic_config_pin(jzpc, pin, GPIO_MSK, true); ingenic_config_pin(jzpc, pin, JZ4770_GPIO_PAT1, input); } else { ingenic_config_pin(jzpc, pin, JZ4740_GPIO_SELECT, false); @@ -712,12 +1060,95 @@ static const struct regmap_config ingenic_pinctrl_regmap_config = { static const struct of_device_id ingenic_pinctrl_of_match[] = { { .compatible = "ingenic,jz4740-pinctrl", .data = (void *) ID_JZ4740 }, + { .compatible = "ingenic,jz4725b-pinctrl", .data = (void *)ID_JZ4725B }, { .compatible = "ingenic,jz4770-pinctrl", .data = (void *) ID_JZ4770 }, { .compatible = "ingenic,jz4780-pinctrl", .data = (void *) ID_JZ4780 }, {}, }; -static int ingenic_pinctrl_probe(struct platform_device *pdev) +static const struct of_device_id ingenic_gpio_of_match[] __initconst = { + { .compatible = "ingenic,jz4740-gpio", }, + { .compatible = "ingenic,jz4770-gpio", }, + { .compatible = "ingenic,jz4780-gpio", }, + {}, +}; + +static int __init ingenic_gpio_probe(struct ingenic_pinctrl *jzpc, + struct device_node *node) +{ + struct ingenic_gpio_chip *jzgc; + struct device *dev = jzpc->dev; + unsigned int bank; + int err; + + err = of_property_read_u32(node, "reg", &bank); + if (err) { + dev_err(dev, "Cannot read \"reg\" property: %i\n", err); + return err; + } + + jzgc = devm_kzalloc(dev, sizeof(*jzgc), GFP_KERNEL); + if (!jzgc) + return -ENOMEM; + + jzgc->jzpc = jzpc; + jzgc->reg_base = bank * 0x100; + + jzgc->gc.label = devm_kasprintf(dev, GFP_KERNEL, "GPIO%c", 'A' + bank); + if (!jzgc->gc.label) + return -ENOMEM; + + /* DO NOT EXPAND THIS: FOR BACKWARD GPIO NUMBERSPACE COMPATIBIBILITY + * ONLY: WORK TO TRANSITION CONSUMERS TO USE THE GPIO DESCRIPTOR API IN + * <linux/gpio/consumer.h> INSTEAD. + */ + jzgc->gc.base = bank * 32; + + jzgc->gc.ngpio = 32; + jzgc->gc.parent = dev; + jzgc->gc.of_node = node; + jzgc->gc.owner = THIS_MODULE; + + jzgc->gc.set = ingenic_gpio_set; + jzgc->gc.get = ingenic_gpio_get; + jzgc->gc.direction_input = ingenic_gpio_direction_input; + jzgc->gc.direction_output = ingenic_gpio_direction_output; + jzgc->gc.get_direction = ingenic_gpio_get_direction; + + if (of_property_read_bool(node, "gpio-ranges")) { + jzgc->gc.request = gpiochip_generic_request; + jzgc->gc.free = gpiochip_generic_free; + } + + err = devm_gpiochip_add_data(dev, &jzgc->gc, jzgc); + if (err) + return err; + + jzgc->irq = irq_of_parse_and_map(node, 0); + if (!jzgc->irq) + return -EINVAL; + + jzgc->irq_chip.name = jzgc->gc.label; + jzgc->irq_chip.irq_enable = ingenic_gpio_irq_enable; + jzgc->irq_chip.irq_disable = ingenic_gpio_irq_disable; + jzgc->irq_chip.irq_unmask = ingenic_gpio_irq_unmask; + jzgc->irq_chip.irq_mask = ingenic_gpio_irq_mask; + jzgc->irq_chip.irq_ack = ingenic_gpio_irq_ack; + jzgc->irq_chip.irq_set_type = ingenic_gpio_irq_set_type; + jzgc->irq_chip.irq_set_wake = ingenic_gpio_irq_set_wake; + jzgc->irq_chip.flags = IRQCHIP_MASK_ON_SUSPEND; + + err = gpiochip_irqchip_add(&jzgc->gc, &jzgc->irq_chip, 0, + handle_level_irq, IRQ_TYPE_NONE); + if (err) + return err; + + gpiochip_set_chained_irqchip(&jzgc->gc, &jzgc->irq_chip, + jzgc->irq, ingenic_gpio_irq_handler); + return 0; +} + +static int __init ingenic_pinctrl_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct ingenic_pinctrl *jzpc; @@ -727,6 +1158,7 @@ static int ingenic_pinctrl_probe(struct platform_device *pdev) const struct of_device_id *of_id = of_match_device( ingenic_pinctrl_of_match, dev); const struct ingenic_chip_info *chip_info; + struct device_node *node; unsigned int i; int err; @@ -755,6 +1187,8 @@ static int ingenic_pinctrl_probe(struct platform_device *pdev) if (jzpc->version >= ID_JZ4770) chip_info = &jz4770_chip_info; + else if (jzpc->version >= ID_JZ4725B) + chip_info = &jz4725b_chip_info; else chip_info = &jz4740_chip_info; jzpc->info = chip_info; @@ -815,11 +1249,11 @@ static int ingenic_pinctrl_probe(struct platform_device *pdev) dev_set_drvdata(dev, jzpc->map); - if (dev->of_node) { - err = of_platform_populate(dev->of_node, NULL, NULL, dev); - if (err) { - dev_err(dev, "Failed to probe GPIO devices\n"); - return err; + for_each_child_of_node(dev->of_node, node) { + if (of_match_node(ingenic_gpio_of_match, node)) { + err = ingenic_gpio_probe(jzpc, node); + if (err) + return err; } } @@ -828,6 +1262,7 @@ static int ingenic_pinctrl_probe(struct platform_device *pdev) static const struct platform_device_id ingenic_pinctrl_ids[] = { { "jz4740-pinctrl", ID_JZ4740 }, + { "jz4725b-pinctrl", ID_JZ4725B }, { "jz4770-pinctrl", ID_JZ4770 }, { "jz4780-pinctrl", ID_JZ4780 }, {}, @@ -837,14 +1272,13 @@ static struct platform_driver ingenic_pinctrl_driver = { .driver = { .name = "pinctrl-ingenic", .of_match_table = of_match_ptr(ingenic_pinctrl_of_match), - .suppress_bind_attrs = true, }, - .probe = ingenic_pinctrl_probe, .id_table = ingenic_pinctrl_ids, }; static int __init ingenic_pinctrl_drv_register(void) { - return platform_driver_register(&ingenic_pinctrl_driver); + return platform_driver_probe(&ingenic_pinctrl_driver, + ingenic_pinctrl_probe); } -postcore_initcall(ingenic_pinctrl_drv_register); +subsys_initcall(ingenic_pinctrl_drv_register); |