diff --git a/Documentation/devicetree/bindings/gpio/actions,owl-gpio.txt b/Documentation/devicetree/bindings/gpio/actions,owl-gpio.txt new file mode 100644 index 0000000000000000000000000000000000000000..34283e9195eab7b390d529145b14697240f205f7 --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/actions,owl-gpio.txt @@ -0,0 +1,87 @@ +* Actions Semi OWL GPIO controller bindings + +The GPIOs are organized as individual banks/ports with variable number +of GPIOs. Each bank is represented as an individual GPIO controller. + +Required properties: +- compatible : Should be "actions,s900-gpio" +- reg : Address and range of the GPIO controller registers. +- 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 is used to specify optional + parameters. +- ngpios : Specifies the number of GPIO lines. +- interrupt-controller : Marks the device node as an interrupt controller. +- #interrupt-cells : Specifies the number of cells needed to encode an + interrupt. Shall be set to 2. The first cell + defines the interrupt number, the second encodes + the trigger flags described in + bindings/interrupt-controller/interrupts.txt + +Optional properties: +- gpio-ranges : Mapping between GPIO and pinctrl + +Examples: + + gpioa: gpio@e01b0000 { + compatible = "actions,s900-gpio"; + reg = <0x0 0xe01b0000 0x0 0x000c>; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = <&pinctrl 0 0 32>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpiob: gpio@e01b000c { + compatible = "actions,s900-gpio"; + reg = <0x0 0xe01b000c 0x0 0x000c>; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = <&pinctrl 0 32 32>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpioc: gpio@e01b0018 { + compatible = "actions,s900-gpio"; + reg = <0x0 0xe01b0018 0x0 0x000c>; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = <&pinctrl 0 64 12>; + ngpios = <12>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpiod: gpio@e01b0024 { + compatible = "actions,s900-gpio"; + reg = <0x0 0xe01b0024 0x0 0x000c>; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = <&pinctrl 0 76 30>; + ngpios = <30>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpioe: gpio@e01b0030 { + compatible = "actions,s900-gpio"; + reg = <0x0 0xe01b0030 0x0 0x000c>; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = <&pinctrl 0 106 32>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpiof: gpio@e01b00f0 { + compatible = "actions,s900-gpio"; + reg = <0x0 0xe01b00f0 0x0 0x000c>; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = <&pinctrl 0 138 8>; + ngpios = <8>; + interrupt-controller; + #interrupt-cells = <2>; + }; diff --git a/Documentation/devicetree/bindings/gpio/renesas,gpio-rcar.txt b/Documentation/devicetree/bindings/gpio/renesas,gpio-rcar.txt index 9474138d776ed58da02dd925105dd89daa3c6cb4..9744d422d52e686fee3b9898a5412d8f023cbe2b 100644 --- a/Documentation/devicetree/bindings/gpio/renesas,gpio-rcar.txt +++ b/Documentation/devicetree/bindings/gpio/renesas,gpio-rcar.txt @@ -5,6 +5,7 @@ Required Properties: - compatible: should contain one or more of the following: - "renesas,gpio-r8a7743": for R8A7743 (RZ/G1M) compatible GPIO controller. - "renesas,gpio-r8a7745": for R8A7745 (RZ/G1E) compatible GPIO controller. + - "renesas,gpio-r8a77470": for R8A77470 (RZ/G1C) compatible GPIO controller. - "renesas,gpio-r8a7778": for R8A7778 (R-Car M1) compatible GPIO controller. - "renesas,gpio-r8a7779": for R8A7779 (R-Car H1) compatible GPIO controller. - "renesas,gpio-r8a7790": for R8A7790 (R-Car H2) compatible GPIO controller. @@ -14,6 +15,7 @@ Required Properties: - "renesas,gpio-r8a7794": for R8A7794 (R-Car E2) compatible GPIO controller. - "renesas,gpio-r8a7795": for R8A7795 (R-Car H3) compatible GPIO controller. - "renesas,gpio-r8a7796": for R8A7796 (R-Car M3-W) compatible GPIO controller. + - "renesas,gpio-r8a77965": for R8A77965 (R-Car M3-N) compatible GPIO controller. - "renesas,gpio-r8a77970": for R8A77970 (R-Car V3M) compatible GPIO controller. - "renesas,gpio-r8a77995": for R8A77995 (R-Car D3) compatible GPIO controller. - "renesas,rcar-gen1-gpio": for a generic R-Car Gen1 GPIO controller. diff --git a/Documentation/driver-api/gpio/board.rst b/Documentation/driver-api/gpio/board.rst index 25d62b2e9fd0b26968bbadcebf780d7d70c0a6a8..2c112553df841444730beee24d30485857d99145 100644 --- a/Documentation/driver-api/gpio/board.rst +++ b/Documentation/driver-api/gpio/board.rst @@ -177,3 +177,19 @@ mapping and is thus transparent to GPIO consumers. A set of functions such as gpiod_set_value() is available to work with the new descriptor-oriented interface. + +Boards using platform data can also hog GPIO lines by defining GPIO hog tables. + +.. code-block:: c + + struct gpiod_hog gpio_hog_table[] = { + GPIO_HOG("gpio.0", 10, "foo", GPIO_ACTIVE_LOW, GPIOD_OUT_HIGH), + { } + }; + +And the table can be added to the board code as follows:: + + gpiod_add_hogs(gpio_hog_table); + +The line will be hogged as soon as the gpiochip is created or - in case the +chip was created earlier - when the hog table is registered. diff --git a/Documentation/driver-api/gpio/drivers-on-gpio.rst b/Documentation/driver-api/gpio/drivers-on-gpio.rst index 7da0c1dd1f7a4936c1787022fbf4dfcd011fa584..f3a189320e11fc28705292db44150b44b224658d 100644 --- a/Documentation/driver-api/gpio/drivers-on-gpio.rst +++ b/Documentation/driver-api/gpio/drivers-on-gpio.rst @@ -85,6 +85,10 @@ hardware descriptions such as device tree or ACPI: any other serio bus to the system and makes it possible to connect drivers for e.g. keyboards and other PS/2 protocol based devices. +- cec-gpio: drivers/media/platform/cec-gpio/ is used to interact with a CEC + Consumer Electronics Control bus using only GPIO. It is used to communicate + with devices on the HDMI bus. + Apart from this there are special GPIO drivers in subsystems like MMC/SD to read card detect and write protect GPIO lines, and in the TTY serial subsystem to emulate MCTRL (modem control) signals CTS/RTS by using two GPIO lines. The diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index b960f6f35abd37c2b61d68e85e2c5290c5211245..9631b3a4a6940825134e69187056096a000e0f0a 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -383,6 +383,14 @@ config GPIO_OMAP help Say yes here to enable GPIO support for TI OMAP SoCs. +config GPIO_OWL + tristate "Actions Semi OWL GPIO support" + default ARCH_ACTIONS + depends on ARCH_ACTIONS || COMPILE_TEST + depends on OF_GPIO + help + Say yes here to enable GPIO support for Actions Semi OWL SoCs. + config GPIO_PL061 bool "PrimeCell PL061 GPIO support" depends on ARM_AMBA diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 1324c8f966a79db1609ab6aa5aedb30415260741..cfff11e9bb156677f1cc411af9a9f60fb1da9a7f 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -93,6 +93,7 @@ obj-$(CONFIG_GPIO_MXC) += gpio-mxc.o obj-$(CONFIG_GPIO_MXS) += gpio-mxs.o obj-$(CONFIG_GPIO_OCTEON) += gpio-octeon.o obj-$(CONFIG_GPIO_OMAP) += gpio-omap.o +obj-$(CONFIG_GPIO_OWL) += gpio-owl.o obj-$(CONFIG_GPIO_PCA953X) += gpio-pca953x.o obj-$(CONFIG_GPIO_PCF857X) += gpio-pcf857x.o obj-$(CONFIG_GPIO_PCH) += gpio-pch.o diff --git a/drivers/gpio/gpio-104-dio-48e.c b/drivers/gpio/gpio-104-dio-48e.c index 31e22c93e84484d6fbbd2269d02b76c55a26d885..9c4e07fcb74b5f8fb1286c209a3b3c47d5a412b3 100644 --- a/drivers/gpio/gpio-104-dio-48e.c +++ b/drivers/gpio/gpio-104-dio-48e.c @@ -188,7 +188,7 @@ static int dio48e_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask, { struct dio48e_gpio *const dio48egpio = gpiochip_get_data(chip); size_t i; - const size_t ports[] = { 0, 1, 2, 4, 5, 6 }; + static const size_t ports[] = { 0, 1, 2, 4, 5, 6 }; const unsigned int gpio_reg_size = 8; unsigned int bits_offset; size_t word_index; diff --git a/drivers/gpio/gpio-104-idi-48.c b/drivers/gpio/gpio-104-idi-48.c index f35632609379432440e9ca1df167f88dab1422a0..2c9738adb3a68434eb877d380954185342c13a37 100644 --- a/drivers/gpio/gpio-104-idi-48.c +++ b/drivers/gpio/gpio-104-idi-48.c @@ -94,7 +94,7 @@ static int idi_48_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask, { struct idi_48_gpio *const idi48gpio = gpiochip_get_data(chip); size_t i; - const size_t ports[] = { 0, 1, 2, 4, 5, 6 }; + static const size_t ports[] = { 0, 1, 2, 4, 5, 6 }; const unsigned int gpio_reg_size = 8; unsigned int bits_offset; size_t word_index; diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c index 77e485557498329193fd291194cc037de55c8160..6f693b7d5220a02b110ae28b8fb6dd5a997d631f 100644 --- a/drivers/gpio/gpio-aspeed.c +++ b/drivers/gpio/gpio-aspeed.c @@ -384,7 +384,7 @@ static void aspeed_gpio_irq_set_mask(struct irq_data *d, bool set) if (set) reg |= bit; else - reg &= bit; + reg &= ~bit; iowrite32(reg, addr); spin_unlock_irqrestore(&gpio->lock, flags); diff --git a/drivers/gpio/gpio-eic-sprd.c b/drivers/gpio/gpio-eic-sprd.c index de7dd939c04314bc54debf94c3c5943fe06debae..e0d6a0a7bc697d0de1e3f1b364a0dc4041ad9b2a 100644 --- a/drivers/gpio/gpio-eic-sprd.c +++ b/drivers/gpio/gpio-eic-sprd.c @@ -300,6 +300,7 @@ static int sprd_eic_irq_set_type(struct irq_data *data, unsigned int flow_type) struct gpio_chip *chip = irq_data_get_irq_chip_data(data); struct sprd_eic *sprd_eic = gpiochip_get_data(chip); u32 offset = irqd_to_hwirq(data); + int state; switch (sprd_eic->type) { case SPRD_EIC_DEBOUNCE: @@ -310,6 +311,17 @@ static int sprd_eic_irq_set_type(struct irq_data *data, unsigned int flow_type) case IRQ_TYPE_LEVEL_LOW: sprd_eic_update(chip, offset, SPRD_EIC_DBNC_IEV, 0); break; + case IRQ_TYPE_EDGE_RISING: + case IRQ_TYPE_EDGE_FALLING: + case IRQ_TYPE_EDGE_BOTH: + state = sprd_eic_get(chip, offset); + if (state) + sprd_eic_update(chip, offset, + SPRD_EIC_DBNC_IEV, 0); + else + sprd_eic_update(chip, offset, + SPRD_EIC_DBNC_IEV, 1); + break; default: return -ENOTSUPP; } @@ -324,6 +336,17 @@ static int sprd_eic_irq_set_type(struct irq_data *data, unsigned int flow_type) case IRQ_TYPE_LEVEL_LOW: sprd_eic_update(chip, offset, SPRD_EIC_LATCH_INTPOL, 1); break; + case IRQ_TYPE_EDGE_RISING: + case IRQ_TYPE_EDGE_FALLING: + case IRQ_TYPE_EDGE_BOTH: + state = sprd_eic_get(chip, offset); + if (state) + sprd_eic_update(chip, offset, + SPRD_EIC_LATCH_INTPOL, 0); + else + sprd_eic_update(chip, offset, + SPRD_EIC_LATCH_INTPOL, 1); + break; default: return -ENOTSUPP; } @@ -405,6 +428,55 @@ static int sprd_eic_irq_set_type(struct irq_data *data, unsigned int flow_type) return 0; } +static void sprd_eic_toggle_trigger(struct gpio_chip *chip, unsigned int irq, + unsigned int offset) +{ + struct sprd_eic *sprd_eic = gpiochip_get_data(chip); + struct irq_data *data = irq_get_irq_data(irq); + u32 trigger = irqd_get_trigger_type(data); + int state, post_state; + + /* + * The debounce EIC and latch EIC can only support level trigger, so we + * can toggle the level trigger to emulate the edge trigger. + */ + if ((sprd_eic->type != SPRD_EIC_DEBOUNCE && + sprd_eic->type != SPRD_EIC_LATCH) || + !(trigger & IRQ_TYPE_EDGE_BOTH)) + return; + + sprd_eic_irq_mask(data); + state = sprd_eic_get(chip, offset); + +retry: + switch (sprd_eic->type) { + case SPRD_EIC_DEBOUNCE: + if (state) + sprd_eic_update(chip, offset, SPRD_EIC_DBNC_IEV, 0); + else + sprd_eic_update(chip, offset, SPRD_EIC_DBNC_IEV, 1); + break; + case SPRD_EIC_LATCH: + if (state) + sprd_eic_update(chip, offset, SPRD_EIC_LATCH_INTPOL, 0); + else + sprd_eic_update(chip, offset, SPRD_EIC_LATCH_INTPOL, 1); + break; + default: + sprd_eic_irq_unmask(data); + return; + } + + post_state = sprd_eic_get(chip, offset); + if (state != post_state) { + dev_warn(chip->parent, "EIC level was changed.\n"); + state = post_state; + goto retry; + } + + sprd_eic_irq_unmask(data); +} + static int sprd_eic_match_chip_by_type(struct gpio_chip *chip, void *data) { enum sprd_eic_type type = *(enum sprd_eic_type *)data; @@ -448,6 +520,7 @@ static void sprd_eic_handle_one_type(struct gpio_chip *chip) bank * SPRD_EIC_PER_BANK_NR + n); generic_handle_irq(girq); + sprd_eic_toggle_trigger(chip, girq, n); } } } diff --git a/drivers/gpio/gpio-gpio-mm.c b/drivers/gpio/gpio-gpio-mm.c index d496cc56c2a2d03d60321980caae64fda6f5fe2a..b56ff2efbf3690129d3d781b5a39890e9c5d307d 100644 --- a/drivers/gpio/gpio-gpio-mm.c +++ b/drivers/gpio/gpio-gpio-mm.c @@ -177,7 +177,7 @@ static int gpiomm_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask, { struct gpiomm_gpio *const gpiommgpio = gpiochip_get_data(chip); size_t i; - const size_t ports[] = { 0, 1, 2, 4, 5, 6 }; + static const size_t ports[] = { 0, 1, 2, 4, 5, 6 }; const unsigned int gpio_reg_size = 8; unsigned int bits_offset; size_t word_index; diff --git a/drivers/gpio/gpio-loongson.c b/drivers/gpio/gpio-loongson.c index 92c4fe7b26773c9e7bf6eb8278a263b9b6e596d6..16cfbe9e72fe2bcbeffe8398548384f477bbd0d1 100644 --- a/drivers/gpio/gpio-loongson.c +++ b/drivers/gpio/gpio-loongson.c @@ -17,9 +17,11 @@ #include <linux/module.h> #include <linux/spinlock.h> #include <linux/err.h> +#include <linux/gpio/driver.h> +#include <linux/platform_device.h> +#include <linux/bitops.h> #include <asm/types.h> #include <loongson.h> -#include <linux/gpio.h> #define STLS2F_N_GPIO 4 #define STLS3A_N_GPIO 16 @@ -30,86 +32,108 @@ #define LOONGSON_N_GPIO STLS2F_N_GPIO #endif +/* + * Offset into the register where we read lines, we write them from offset 0. + * This offset is the only thing that stand between us and using + * GPIO_GENERIC. + */ #define LOONGSON_GPIO_IN_OFFSET 16 static DEFINE_SPINLOCK(gpio_lock); -static int loongson_gpio_direction_input(struct gpio_chip *chip, unsigned gpio) +static int loongson_gpio_get_value(struct gpio_chip *chip, unsigned gpio) { - u32 temp; - u32 mask; + u32 val; spin_lock(&gpio_lock); - mask = 1 << gpio; - temp = LOONGSON_GPIOIE; - temp |= mask; - LOONGSON_GPIOIE = temp; + val = LOONGSON_GPIODATA; spin_unlock(&gpio_lock); - return 0; + return !!(val & BIT(gpio + LOONGSON_GPIO_IN_OFFSET)); } -static int loongson_gpio_direction_output(struct gpio_chip *chip, - unsigned gpio, int level) +static void loongson_gpio_set_value(struct gpio_chip *chip, + unsigned gpio, int value) +{ + u32 val; + + spin_lock(&gpio_lock); + val = LOONGSON_GPIODATA; + if (value) + val |= BIT(gpio); + else + val &= ~BIT(gpio); + LOONGSON_GPIODATA = val; + spin_unlock(&gpio_lock); +} + +static int loongson_gpio_direction_input(struct gpio_chip *chip, unsigned gpio) { u32 temp; - u32 mask; - gpio_set_value(gpio, level); spin_lock(&gpio_lock); - mask = 1 << gpio; temp = LOONGSON_GPIOIE; - temp &= (~mask); + temp |= BIT(gpio); LOONGSON_GPIOIE = temp; spin_unlock(&gpio_lock); return 0; } -static int loongson_gpio_get_value(struct gpio_chip *chip, unsigned gpio) +static int loongson_gpio_direction_output(struct gpio_chip *chip, + unsigned gpio, int level) { - u32 val; - u32 mask; + u32 temp; - mask = 1 << (gpio + LOONGSON_GPIO_IN_OFFSET); + loongson_gpio_set_value(chip, gpio, level); spin_lock(&gpio_lock); - val = LOONGSON_GPIODATA; + temp = LOONGSON_GPIOIE; + temp &= ~BIT(gpio); + LOONGSON_GPIOIE = temp; spin_unlock(&gpio_lock); - return (val & mask) != 0; + return 0; } -static void loongson_gpio_set_value(struct gpio_chip *chip, - unsigned gpio, int value) +static int loongson_gpio_probe(struct platform_device *pdev) { - u32 val; - u32 mask; - - mask = 1 << gpio; - - spin_lock(&gpio_lock); - val = LOONGSON_GPIODATA; - if (value) - val |= mask; - else - val &= (~mask); - LOONGSON_GPIODATA = val; - spin_unlock(&gpio_lock); + struct gpio_chip *gc; + struct device *dev = &pdev->dev; + + gc = devm_kzalloc(dev, sizeof(*gc), GFP_KERNEL); + if (!gc) + return -ENOMEM; + + gc->label = "loongson-gpio-chip"; + gc->base = 0; + gc->ngpio = LOONGSON_N_GPIO; + gc->get = loongson_gpio_get_value; + gc->set = loongson_gpio_set_value; + gc->direction_input = loongson_gpio_direction_input; + gc->direction_output = loongson_gpio_direction_output; + + return gpiochip_add_data(gc, NULL); } -static struct gpio_chip loongson_chip = { - .label = "Loongson-gpio-chip", - .direction_input = loongson_gpio_direction_input, - .get = loongson_gpio_get_value, - .direction_output = loongson_gpio_direction_output, - .set = loongson_gpio_set_value, - .base = 0, - .ngpio = LOONGSON_N_GPIO, - .can_sleep = false, +static struct platform_driver loongson_gpio_driver = { + .driver = { + .name = "loongson-gpio", + }, + .probe = loongson_gpio_probe, }; static int __init loongson_gpio_setup(void) { - return gpiochip_add_data(&loongson_chip, NULL); + struct platform_device *pdev; + int ret; + + ret = platform_driver_register(&loongson_gpio_driver); + if (ret) { + pr_err("error registering loongson GPIO driver\n"); + return ret; + } + + pdev = platform_device_register_simple("loongson-gpio", -1, NULL, 0); + return PTR_ERR_OR_ZERO(pdev); } postcore_initcall(loongson_gpio_setup); diff --git a/drivers/gpio/gpio-lp3943.c b/drivers/gpio/gpio-lp3943.c index 6dc6725403ecd70f2b3d36deefd48a75df475648..c3a3b9b7b5539712273a8a92d612365fe1c6449a 100644 --- a/drivers/gpio/gpio-lp3943.c +++ b/drivers/gpio/gpio-lp3943.c @@ -12,7 +12,7 @@ #include <linux/bitops.h> #include <linux/err.h> -#include <linux/gpio.h> +#include <linux/gpio/driver.h> #include <linux/i2c.h> #include <linux/mfd/lp3943.h> #include <linux/module.h> diff --git a/drivers/gpio/gpio-lp873x.c b/drivers/gpio/gpio-lp873x.c index df0ad2cef0d2bfc3b3d80bd9aed01f3482379ff3..801995dd9b2605908f3ee1ea9ffdb02583d42c2f 100644 --- a/drivers/gpio/gpio-lp873x.c +++ b/drivers/gpio/gpio-lp873x.c @@ -14,7 +14,7 @@ * Based on the TPS65218 driver */ -#include <linux/gpio.h> +#include <linux/gpio/driver.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/regmap.h> diff --git a/drivers/gpio/gpio-lpc32xx.c b/drivers/gpio/gpio-lpc32xx.c index 92b3ae2a67357f7714faa224a10f86a440bd4257..aa74cc4d8b14f3ad9a80a63e51caeb74f39bbe91 100644 --- a/drivers/gpio/gpio-lpc32xx.c +++ b/drivers/gpio/gpio-lpc32xx.c @@ -20,9 +20,8 @@ #include <linux/init.h> #include <linux/io.h> #include <linux/errno.h> -#include <linux/gpio.h> +#include <linux/gpio/driver.h> #include <linux/of.h> -#include <linux/of_gpio.h> #include <linux/platform_device.h> #include <linux/module.h> diff --git a/drivers/gpio/gpio-lynxpoint.c b/drivers/gpio/gpio-lynxpoint.c index 1e557b10d73ed3c66e8f6806994e144d19f257c2..b5b5e500e72cfa3a854fe33c5174890783bb7a5c 100644 --- a/drivers/gpio/gpio-lynxpoint.c +++ b/drivers/gpio/gpio-lynxpoint.c @@ -25,7 +25,7 @@ #include <linux/types.h> #include <linux/bitops.h> #include <linux/interrupt.h> -#include <linux/gpio.h> +#include <linux/gpio/driver.h> #include <linux/slab.h> #include <linux/acpi.h> #include <linux/platform_device.h> diff --git a/drivers/gpio/gpio-max730x.c b/drivers/gpio/gpio-max730x.c index 946d09195598f4dacde27162193e1ee99490481f..198a36b07773dcae0ed4b360faa1a0bd8ee7efe7 100644 --- a/drivers/gpio/gpio-max730x.c +++ b/drivers/gpio/gpio-max730x.c @@ -35,7 +35,7 @@ #include <linux/platform_device.h> #include <linux/mutex.h> #include <linux/spi/max7301.h> -#include <linux/gpio.h> +#include <linux/gpio/driver.h> #include <linux/slab.h> /* diff --git a/drivers/gpio/gpio-mc33880.c b/drivers/gpio/gpio-mc33880.c index 0f0df795626404a340d4191b03d01ba0ecbe43d6..18a5a58d634afdb32eda5c9d844034db22de78af 100644 --- a/drivers/gpio/gpio-mc33880.c +++ b/drivers/gpio/gpio-mc33880.c @@ -24,7 +24,7 @@ #include <linux/mutex.h> #include <linux/spi/spi.h> #include <linux/spi/mc33880.h> -#include <linux/gpio.h> +#include <linux/gpio/driver.h> #include <linux/slab.h> #include <linux/module.h> diff --git a/drivers/gpio/gpio-mc9s08dz60.c b/drivers/gpio/gpio-mc9s08dz60.c index 2fcad5b9cca51233287ad3b0bac6474147904dc9..d8d846d2189acd2f81f380ded58e3a5299ab38a2 100644 --- a/drivers/gpio/gpio-mc9s08dz60.c +++ b/drivers/gpio/gpio-mc9s08dz60.c @@ -18,7 +18,7 @@ #include <linux/init.h> #include <linux/slab.h> #include <linux/i2c.h> -#include <linux/gpio.h> +#include <linux/gpio/driver.h> #define GPIO_GROUP_NUM 2 #define GPIO_NUM_PER_GROUP 8 diff --git a/drivers/gpio/gpio-ml-ioh.c b/drivers/gpio/gpio-ml-ioh.c index b3678bd1c120c8d18daff31f5489ba7909688dcb..e2bee27eb526d172be23be709a36be84604d7eac 100644 --- a/drivers/gpio/gpio-ml-ioh.c +++ b/drivers/gpio/gpio-ml-ioh.c @@ -18,7 +18,7 @@ #include <linux/kernel.h> #include <linux/slab.h> #include <linux/pci.h> -#include <linux/gpio.h> +#include <linux/gpio/driver.h> #include <linux/interrupt.h> #include <linux/irq.h> diff --git a/drivers/gpio/gpio-mm-lantiq.c b/drivers/gpio/gpio-mm-lantiq.c index b1cf76dd84bacb6310616226a3f39dd02d560be4..b0754fe69e772bf7f2e69fae68c5281fdbd02b5e 100644 --- a/drivers/gpio/gpio-mm-lantiq.c +++ b/drivers/gpio/gpio-mm-lantiq.c @@ -11,7 +11,7 @@ #include <linux/types.h> #include <linux/platform_device.h> #include <linux/mutex.h> -#include <linux/gpio.h> +#include <linux/gpio/driver.h> #include <linux/of.h> #include <linux/of_gpio.h> #include <linux/io.h> diff --git a/drivers/gpio/gpio-msic.c b/drivers/gpio/gpio-msic.c index 6cb67595d15f63b3f046512a1f1b4ee5e7b04301..3b34dbecef994e12264b79acce9172f9549dca06 100644 --- a/drivers/gpio/gpio-msic.c +++ b/drivers/gpio/gpio-msic.c @@ -24,7 +24,7 @@ #include <linux/slab.h> #include <linux/interrupt.h> #include <linux/init.h> -#include <linux/gpio.h> +#include <linux/gpio/driver.h> #include <linux/platform_device.h> #include <linux/mfd/intel_msic.h> diff --git a/drivers/gpio/gpio-mvebu.c b/drivers/gpio/gpio-mvebu.c index 45c65f805fd6ba5a3899b5c29a9651fa2d6dcc43..6e02148c208b2cc600d75263d1c87064db2ebc23 100644 --- a/drivers/gpio/gpio-mvebu.c +++ b/drivers/gpio/gpio-mvebu.c @@ -36,7 +36,8 @@ #include <linux/bitops.h> #include <linux/clk.h> #include <linux/err.h> -#include <linux/gpio.h> +#include <linux/gpio/driver.h> +#include <linux/gpio/consumer.h> #include <linux/init.h> #include <linux/io.h> #include <linux/irq.h> @@ -51,8 +52,6 @@ #include <linux/regmap.h> #include <linux/slab.h> -#include "gpiolib.h" - /* * GPIO unit register offsets. */ @@ -608,19 +607,16 @@ static int mvebu_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) if (mvpwm->gpiod) { ret = -EBUSY; } else { - desc = gpio_to_desc(mvchip->chip.base + pwm->hwpwm); - if (!desc) { - ret = -ENODEV; + desc = gpiochip_request_own_desc(&mvchip->chip, + pwm->hwpwm, "mvebu-pwm"); + if (IS_ERR(desc)) { + ret = PTR_ERR(desc); goto out; } - ret = gpiod_request(desc, "mvebu-pwm"); - if (ret) - goto out; - ret = gpiod_direction_output(desc, 0); if (ret) { - gpiod_free(desc); + gpiochip_free_own_desc(desc); goto out; } @@ -637,7 +633,7 @@ static void mvebu_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) unsigned long flags; spin_lock_irqsave(&mvpwm->lock, flags); - gpiod_free(mvpwm->gpiod); + gpiochip_free_own_desc(mvpwm->gpiod); mvpwm->gpiod = NULL; spin_unlock_irqrestore(&mvpwm->lock, flags); } diff --git a/drivers/gpio/gpio-mxc.c b/drivers/gpio/gpio-mxc.c index 5245a2fe62aeebe6fdd0d4f68ccaea2c22b6ddcc..11ec7228ab083554222d4a737d90332db59c6479 100644 --- a/drivers/gpio/gpio-mxc.c +++ b/drivers/gpio/gpio-mxc.c @@ -30,8 +30,6 @@ #include <linux/platform_device.h> #include <linux/slab.h> #include <linux/gpio/driver.h> -/* FIXME: for gpio_get_value() replace this with direct register read */ -#include <linux/gpio.h> #include <linux/of.h> #include <linux/of_device.h> #include <linux/bug.h> @@ -174,7 +172,6 @@ static int gpio_set_irq_type(struct irq_data *d, u32 type) struct mxc_gpio_port *port = gc->private; u32 bit, val; u32 gpio_idx = d->hwirq; - u32 gpio = port->gc.base + gpio_idx; int edge; void __iomem *reg = port->base; @@ -190,13 +187,13 @@ static int gpio_set_irq_type(struct irq_data *d, u32 type) if (GPIO_EDGE_SEL >= 0) { edge = GPIO_INT_BOTH_EDGES; } else { - val = gpio_get_value(gpio); + val = port->gc.get(&port->gc, gpio_idx); if (val) { edge = GPIO_INT_LOW_LEV; - pr_debug("mxc: set GPIO %d to low trigger\n", gpio); + pr_debug("mxc: set GPIO %d to low trigger\n", gpio_idx); } else { edge = GPIO_INT_HIGH_LEV; - pr_debug("mxc: set GPIO %d to high trigger\n", gpio); + pr_debug("mxc: set GPIO %d to high trigger\n", gpio_idx); } port->both_edges |= 1 << gpio_idx; } diff --git a/drivers/gpio/gpio-owl.c b/drivers/gpio/gpio-owl.c new file mode 100644 index 0000000000000000000000000000000000000000..354636229feee16813e3b6a912e8b74c2b4f2d88 --- /dev/null +++ b/drivers/gpio/gpio-owl.c @@ -0,0 +1,184 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * OWL SoC's GPIO driver + * + * Copyright (c) 2018 Linaro Ltd. + * Author: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org> + */ + +#include <linux/bitops.h> +#include <linux/err.h> +#include <linux/gpio/driver.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of_address.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/spinlock.h> + +#define GPIO_OUTEN 0x0000 +#define GPIO_INEN 0x0004 +#define GPIO_DAT 0x0008 + +struct owl_gpio { + struct gpio_chip gpio; + void __iomem *base; + spinlock_t lock; +}; + +static void owl_gpio_update_reg(void __iomem *base, unsigned int pin, int flag) +{ + u32 val; + + val = readl_relaxed(base); + + if (flag) + val |= BIT(pin); + else + val &= ~BIT(pin); + + writel_relaxed(val, base); +} + +static int owl_gpio_request(struct gpio_chip *chip, unsigned int offset) +{ + struct owl_gpio *gpio = gpiochip_get_data(chip); + + /* + * GPIOs have higher priority over other modules, so either setting + * them as OUT or IN is sufficient + */ + spin_lock(&gpio->lock); + owl_gpio_update_reg(gpio->base + GPIO_OUTEN, offset, true); + spin_unlock(&gpio->lock); + + return 0; +} + +static void owl_gpio_free(struct gpio_chip *chip, unsigned int offset) +{ + struct owl_gpio *gpio = gpiochip_get_data(chip); + + spin_lock(&gpio->lock); + /* disable gpio output */ + owl_gpio_update_reg(gpio->base + GPIO_OUTEN, offset, false); + + /* disable gpio input */ + owl_gpio_update_reg(gpio->base + GPIO_INEN, offset, false); + spin_unlock(&gpio->lock); +} + +static int owl_gpio_get(struct gpio_chip *chip, unsigned int offset) +{ + struct owl_gpio *gpio = gpiochip_get_data(chip); + u32 val; + + spin_lock(&gpio->lock); + val = readl_relaxed(gpio->base + GPIO_DAT); + spin_unlock(&gpio->lock); + + return !!(val & BIT(offset)); +} + +static void owl_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) +{ + struct owl_gpio *gpio = gpiochip_get_data(chip); + + spin_lock(&gpio->lock); + owl_gpio_update_reg(gpio->base + GPIO_DAT, offset, value); + spin_unlock(&gpio->lock); +} + +static int owl_gpio_direction_input(struct gpio_chip *chip, unsigned int offset) +{ + struct owl_gpio *gpio = gpiochip_get_data(chip); + + spin_lock(&gpio->lock); + owl_gpio_update_reg(gpio->base + GPIO_OUTEN, offset, false); + owl_gpio_update_reg(gpio->base + GPIO_INEN, offset, true); + spin_unlock(&gpio->lock); + + return 0; +} + +static int owl_gpio_direction_output(struct gpio_chip *chip, + unsigned int offset, int value) +{ + struct owl_gpio *gpio = gpiochip_get_data(chip); + + spin_lock(&gpio->lock); + owl_gpio_update_reg(gpio->base + GPIO_INEN, offset, false); + owl_gpio_update_reg(gpio->base + GPIO_OUTEN, offset, true); + owl_gpio_update_reg(gpio->base + GPIO_DAT, offset, value); + spin_unlock(&gpio->lock); + + return 0; +} + +static int owl_gpio_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct owl_gpio *gpio; + u32 ngpios; + int ret; + + gpio = devm_kzalloc(dev, sizeof(*gpio), GFP_KERNEL); + if (!gpio) + return -ENOMEM; + + gpio->base = of_iomap(dev->of_node, 0); + if (IS_ERR(gpio->base)) + return PTR_ERR(gpio->base); + + /* + * Get the number of gpio's for this bank. If none specified, + * then fall back to 32. + */ + ret = of_property_read_u32(dev->of_node, "ngpios", &ngpios); + if (ret) + ngpios = 32; + + spin_lock_init(&gpio->lock); + + gpio->gpio.request = owl_gpio_request; + gpio->gpio.free = owl_gpio_free; + gpio->gpio.get = owl_gpio_get; + gpio->gpio.set = owl_gpio_set; + gpio->gpio.direction_input = owl_gpio_direction_input; + gpio->gpio.direction_output = owl_gpio_direction_output; + + gpio->gpio.base = -1; + gpio->gpio.parent = dev; + gpio->gpio.label = dev_name(dev); + gpio->gpio.ngpio = ngpios; + + platform_set_drvdata(pdev, gpio); + + ret = devm_gpiochip_add_data(dev, &gpio->gpio, gpio); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to register gpiochip\n"); + return ret; + } + + return 0; +} + +static const struct of_device_id owl_gpio_of_match[] = { + { .compatible = "actions,s900-gpio", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, owl_gpio_of_match); + +static struct platform_driver owl_gpio_driver = { + .driver = { + .name = "owl-gpio", + .of_match_table = owl_gpio_of_match, + }, + .probe = owl_gpio_probe, +}; +module_platform_driver(owl_gpio_driver); + +MODULE_AUTHOR("Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>"); +MODULE_DESCRIPTION("Actions Semi OWL SoCs GPIO driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/gpio-pci-idio-16.c b/drivers/gpio/gpio-pci-idio-16.c index 1948724d8c369df7baab3e889138408c1e860db8..25d16b2af1c39b305830d837c5aff62ecaf95ae2 100644 --- a/drivers/gpio/gpio-pci-idio-16.c +++ b/drivers/gpio/gpio-pci-idio-16.c @@ -116,9 +116,9 @@ static int idio_16_gpio_get_multiple(struct gpio_chip *chip, unsigned long word_mask; const unsigned long port_mask = GENMASK(gpio_reg_size - 1, 0); unsigned long port_state; - u8 __iomem ports[] = { - idio16gpio->reg->out0_7, idio16gpio->reg->out8_15, - idio16gpio->reg->in0_7, idio16gpio->reg->in8_15, + void __iomem *ports[] = { + &idio16gpio->reg->out0_7, &idio16gpio->reg->out8_15, + &idio16gpio->reg->in0_7, &idio16gpio->reg->in8_15, }; /* clear bits array to a clean slate */ @@ -143,7 +143,7 @@ static int idio_16_gpio_get_multiple(struct gpio_chip *chip, } /* read bits from current gpio port */ - port_state = ioread8(ports + i); + port_state = ioread8(ports[i]); /* store acquired bits at respective bits array offset */ bits[word_index] |= port_state << word_offset; diff --git a/drivers/gpio/gpio-pcie-idio-24.c b/drivers/gpio/gpio-pcie-idio-24.c index 835607ecf658efc95af727767f81caaaf9180d9c..3e77c2a9a9fd4607014ba658fc342866a1baee51 100644 --- a/drivers/gpio/gpio-pcie-idio-24.c +++ b/drivers/gpio/gpio-pcie-idio-24.c @@ -206,10 +206,10 @@ static int idio_24_gpio_get_multiple(struct gpio_chip *chip, unsigned long word_mask; const unsigned long port_mask = GENMASK(gpio_reg_size - 1, 0); unsigned long port_state; - u8 __iomem ports[] = { - idio24gpio->reg->out0_7, idio24gpio->reg->out8_15, - idio24gpio->reg->out16_23, idio24gpio->reg->in0_7, - idio24gpio->reg->in8_15, idio24gpio->reg->in16_23, + void __iomem *ports[] = { + &idio24gpio->reg->out0_7, &idio24gpio->reg->out8_15, + &idio24gpio->reg->out16_23, &idio24gpio->reg->in0_7, + &idio24gpio->reg->in8_15, &idio24gpio->reg->in16_23, }; const unsigned long out_mode_mask = BIT(1); @@ -236,7 +236,7 @@ static int idio_24_gpio_get_multiple(struct gpio_chip *chip, /* read bits from current gpio port (port 6 is TTL GPIO) */ if (i < 6) - port_state = ioread8(ports + i); + port_state = ioread8(ports[i]); else if (ioread8(&idio24gpio->reg->ctl) & out_mode_mask) port_state = ioread8(&idio24gpio->reg->ttl_out0_7); else @@ -301,9 +301,9 @@ static void idio_24_gpio_set_multiple(struct gpio_chip *chip, const unsigned long port_mask = GENMASK(gpio_reg_size, 0); unsigned long flags; unsigned int out_state; - u8 __iomem ports[] = { - idio24gpio->reg->out0_7, idio24gpio->reg->out8_15, - idio24gpio->reg->out16_23 + void __iomem *ports[] = { + &idio24gpio->reg->out0_7, &idio24gpio->reg->out8_15, + &idio24gpio->reg->out16_23 }; const unsigned long out_mode_mask = BIT(1); const unsigned int ttl_offset = 48; @@ -327,9 +327,9 @@ static void idio_24_gpio_set_multiple(struct gpio_chip *chip, raw_spin_lock_irqsave(&idio24gpio->lock, flags); /* process output lines */ - out_state = ioread8(ports + i) & ~gpio_mask; + out_state = ioread8(ports[i]) & ~gpio_mask; out_state |= (*bits >> bits_offset) & gpio_mask; - iowrite8(out_state, ports + i); + iowrite8(out_state, ports[i]); raw_spin_unlock_irqrestore(&idio24gpio->lock, flags); } diff --git a/drivers/gpio/gpio-pmic-eic-sprd.c b/drivers/gpio/gpio-pmic-eic-sprd.c index 66d68d99116255afd9bf46f90d4eab995157b0ab..29e044ff4b17d0c607a6fbe1388aac18d527b5a6 100644 --- a/drivers/gpio/gpio-pmic-eic-sprd.c +++ b/drivers/gpio/gpio-pmic-eic-sprd.c @@ -178,6 +178,14 @@ static int sprd_pmic_eic_irq_set_type(struct irq_data *data, case IRQ_TYPE_LEVEL_LOW: pmic_eic->reg[REG_IEV] = 0; break; + case IRQ_TYPE_EDGE_RISING: + case IRQ_TYPE_EDGE_FALLING: + case IRQ_TYPE_EDGE_BOTH: + /* + * Will set the trigger level according to current EIC level + * in irq_bus_sync_unlock() interface, so here nothing to do. + */ + break; default: return -ENOTSUPP; } @@ -197,11 +205,22 @@ static void sprd_pmic_eic_bus_sync_unlock(struct irq_data *data) { struct gpio_chip *chip = irq_data_get_irq_chip_data(data); struct sprd_pmic_eic *pmic_eic = gpiochip_get_data(chip); + u32 trigger = irqd_get_trigger_type(data); u32 offset = irqd_to_hwirq(data); + int state; /* Set irq type */ - sprd_pmic_eic_update(chip, offset, SPRD_PMIC_EIC_IEV, - pmic_eic->reg[REG_IEV]); + if (trigger & IRQ_TYPE_EDGE_BOTH) { + state = sprd_pmic_eic_get(chip, offset); + if (state) + sprd_pmic_eic_update(chip, offset, SPRD_PMIC_EIC_IEV, 0); + else + sprd_pmic_eic_update(chip, offset, SPRD_PMIC_EIC_IEV, 1); + } else { + sprd_pmic_eic_update(chip, offset, SPRD_PMIC_EIC_IEV, + pmic_eic->reg[REG_IEV]); + } + /* Set irq unmask */ sprd_pmic_eic_update(chip, offset, SPRD_PMIC_EIC_IE, pmic_eic->reg[REG_IE]); @@ -212,6 +231,35 @@ static void sprd_pmic_eic_bus_sync_unlock(struct irq_data *data) mutex_unlock(&pmic_eic->buslock); } +static void sprd_pmic_eic_toggle_trigger(struct gpio_chip *chip, + unsigned int irq, unsigned int offset) +{ + u32 trigger = irq_get_trigger_type(irq); + int state, post_state; + + if (!(trigger & IRQ_TYPE_EDGE_BOTH)) + return; + + state = sprd_pmic_eic_get(chip, offset); +retry: + if (state) + sprd_pmic_eic_update(chip, offset, SPRD_PMIC_EIC_IEV, 0); + else + sprd_pmic_eic_update(chip, offset, SPRD_PMIC_EIC_IEV, 1); + + post_state = sprd_pmic_eic_get(chip, offset); + if (state != post_state) { + dev_warn(chip->parent, "PMIC EIC level was changed.\n"); + state = post_state; + goto retry; + } + + /* Set irq unmask */ + sprd_pmic_eic_update(chip, offset, SPRD_PMIC_EIC_IE, 1); + /* Generate trigger start pulse for debounce EIC */ + sprd_pmic_eic_update(chip, offset, SPRD_PMIC_EIC_TRIG, 1); +} + static irqreturn_t sprd_pmic_eic_irq_handler(int irq, void *data) { struct sprd_pmic_eic *pmic_eic = data; @@ -233,6 +281,12 @@ static irqreturn_t sprd_pmic_eic_irq_handler(int irq, void *data) girq = irq_find_mapping(chip->irq.domain, n); handle_nested_irq(girq); + + /* + * The PMIC EIC can only support level trigger, so we can + * toggle the level trigger to emulate the edge trigger. + */ + sprd_pmic_eic_toggle_trigger(chip, girq, n); } return IRQ_HANDLED; diff --git a/drivers/gpio/gpio-xra1403.c b/drivers/gpio/gpio-xra1403.c index 8d4c8e99b251968a4ece65012a97b72ac0ec6e40..8711a790756894a4d42a90d6190b7287e45411c5 100644 --- a/drivers/gpio/gpio-xra1403.c +++ b/drivers/gpio/gpio-xra1403.c @@ -39,6 +39,7 @@ #define XRA_REIR 0x10 /* Input Rising Edge Interrupt Enable */ #define XRA_FEIR 0x12 /* Input Falling Edge Interrupt Enable */ #define XRA_IFR 0x14 /* Input Filter Enable/Disable */ +#define XRA_LAST 0x15 /* Bounds */ struct xra1403 { struct gpio_chip chip; @@ -50,7 +51,7 @@ static const struct regmap_config xra1403_regmap_cfg = { .pad_bits = 1, .val_bits = 8, - .max_register = XRA_IFR | 0x01, + .max_register = XRA_LAST, }; static unsigned int to_reg(unsigned int reg, unsigned int offset) @@ -126,21 +127,16 @@ static void xra1403_dbg_show(struct seq_file *s, struct gpio_chip *chip) { int reg; struct xra1403 *xra = gpiochip_get_data(chip); - int *value; + int value[XRA_LAST]; int i; unsigned int gcr; unsigned int gsr; - value = kmalloc_array(xra1403_regmap_cfg.max_register, sizeof(*value), - GFP_KERNEL); - if (!value) - return; - seq_puts(s, "xra reg:"); - for (reg = 0; reg <= xra1403_regmap_cfg.max_register; reg++) + for (reg = 0; reg <= XRA_LAST; reg++) seq_printf(s, " %2.2x", reg); seq_puts(s, "\n value:"); - for (reg = 0; reg < xra1403_regmap_cfg.max_register; reg++) { + for (reg = 0; reg < XRA_LAST; reg++) { regmap_read(xra->regmap, reg, &value[reg]); seq_printf(s, " %2.2x", value[reg]); } @@ -159,7 +155,6 @@ static void xra1403_dbg_show(struct seq_file *s, struct gpio_chip *chip) (gcr & BIT(i)) ? "in" : "out", (gsr & BIT(i)) ? "hi" : "lo"); } - kfree(value); } #else #define xra1403_dbg_show NULL diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 43aeb07343ec76fe3eeef0283d8d52327530bfdf..2716c9e65131c5f76fc7db5ac3f62a3201550b8c 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -71,6 +71,9 @@ static DEFINE_MUTEX(gpio_lookup_lock); static LIST_HEAD(gpio_lookup_list); LIST_HEAD(gpio_devices); +static DEFINE_MUTEX(gpio_machine_hogs_mutex); +static LIST_HEAD(gpio_machine_hogs); + static void gpiochip_free_hogs(struct gpio_chip *chip); static int gpiochip_add_irqchip(struct gpio_chip *gpiochip, struct lock_class_key *lock_key, @@ -497,7 +500,7 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip) struct gpiohandle_request handlereq; struct linehandle_state *lh; struct file *file; - int fd, i, ret; + int fd, i, count = 0, ret; u32 lflags; if (copy_from_user(&handlereq, ip, sizeof(handlereq))) @@ -558,6 +561,7 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip) if (ret) goto out_free_descs; lh->descs[i] = desc; + count = i; if (lflags & GPIOHANDLE_REQUEST_ACTIVE_LOW) set_bit(FLAG_ACTIVE_LOW, &desc->flags); @@ -628,7 +632,7 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip) out_put_unused_fd: put_unused_fd(fd); out_free_descs: - for (; i >= 0; i--) + for (i = 0; i < count; i++) gpiod_free(lh->descs[i]); kfree(lh->label); out_free_lh: @@ -902,7 +906,7 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip) desc = &gdev->descs[offset]; ret = gpiod_request(desc, le->label); if (ret) - goto out_free_desc; + goto out_free_label; le->desc = desc; le->eflags = eflags; @@ -1171,6 +1175,41 @@ static int gpiochip_setup_dev(struct gpio_device *gdev) return status; } +static void gpiochip_machine_hog(struct gpio_chip *chip, struct gpiod_hog *hog) +{ + struct gpio_desc *desc; + int rv; + + desc = gpiochip_get_desc(chip, hog->chip_hwnum); + if (IS_ERR(desc)) { + pr_err("%s: unable to get GPIO desc: %ld\n", + __func__, PTR_ERR(desc)); + return; + } + + if (desc->flags & FLAG_IS_HOGGED) + return; + + rv = gpiod_hog(desc, hog->line_name, hog->lflags, hog->dflags); + if (rv) + pr_err("%s: unable to hog GPIO line (%s:%u): %d\n", + __func__, chip->label, hog->chip_hwnum, rv); +} + +static void machine_gpiochip_add(struct gpio_chip *chip) +{ + struct gpiod_hog *hog; + + mutex_lock(&gpio_machine_hogs_mutex); + + list_for_each_entry(hog, &gpio_machine_hogs, list) { + if (!strcmp(chip->label, hog->chip_label)) + gpiochip_machine_hog(chip, hog); + } + + mutex_unlock(&gpio_machine_hogs_mutex); +} + static void gpiochip_setup_devs(void) { struct gpio_device *gdev; @@ -1326,6 +1365,8 @@ int gpiochip_add_data_with_key(struct gpio_chip *chip, void *data, acpi_gpiochip_add(chip); + machine_gpiochip_add(chip); + /* * By first adding the chardev, and then adding the device, * we get a device node entry in sysfs under @@ -3462,6 +3503,33 @@ void gpiod_remove_lookup_table(struct gpiod_lookup_table *table) } EXPORT_SYMBOL_GPL(gpiod_remove_lookup_table); +/** + * gpiod_add_hogs() - register a set of GPIO hogs from machine code + * @hogs: table of gpio hog entries with a zeroed sentinel at the end + */ +void gpiod_add_hogs(struct gpiod_hog *hogs) +{ + struct gpio_chip *chip; + struct gpiod_hog *hog; + + mutex_lock(&gpio_machine_hogs_mutex); + + for (hog = &hogs[0]; hog->chip_label; hog++) { + list_add_tail(&hog->list, &gpio_machine_hogs); + + /* + * The chip may have been registered earlier, so check if it + * exists and, if so, try to hog the line now. + */ + chip = find_chip_by_name(hog->chip_label); + if (chip) + gpiochip_machine_hog(chip, hog); + } + + mutex_unlock(&gpio_machine_hogs_mutex); +} +EXPORT_SYMBOL_GPL(gpiod_add_hogs); + static struct gpiod_lookup_table *gpiod_find_lookup_table(struct device *dev) { const char *dev_id = dev ? dev_name(dev) : NULL; diff --git a/include/linux/gpio/machine.h b/include/linux/gpio/machine.h index b2f2dc638463ce9da4c0fc0feaf8f391d11927d3..daa44eac9241b008a3848ad76ef6b73dbf6495d0 100644 --- a/include/linux/gpio/machine.h +++ b/include/linux/gpio/machine.h @@ -39,6 +39,23 @@ struct gpiod_lookup_table { struct gpiod_lookup table[]; }; +/** + * struct gpiod_hog - GPIO line hog table + * @chip_label: name of the chip the GPIO belongs to + * @chip_hwnum: hardware number (i.e. relative to the chip) of the GPIO + * @line_name: consumer name for the hogged line + * @lflags: mask of GPIO lookup flags + * @dflags: GPIO flags used to specify the direction and value + */ +struct gpiod_hog { + struct list_head list; + const char *chip_label; + u16 chip_hwnum; + const char *line_name; + enum gpio_lookup_flags lflags; + int dflags; +}; + /* * Simple definition of a single GPIO under a con_id */ @@ -59,10 +76,23 @@ struct gpiod_lookup_table { .flags = _flags, \ } +/* + * Simple definition of a single GPIO hog in an array. + */ +#define GPIO_HOG(_chip_label, _chip_hwnum, _line_name, _lflags, _dflags) \ +{ \ + .chip_label = _chip_label, \ + .chip_hwnum = _chip_hwnum, \ + .line_name = _line_name, \ + .lflags = _lflags, \ + .dflags = _dflags, \ +} + #ifdef CONFIG_GPIOLIB void gpiod_add_lookup_table(struct gpiod_lookup_table *table); void gpiod_add_lookup_tables(struct gpiod_lookup_table **tables, size_t n); void gpiod_remove_lookup_table(struct gpiod_lookup_table *table); +void gpiod_add_hogs(struct gpiod_hog *hogs); #else static inline void gpiod_add_lookup_table(struct gpiod_lookup_table *table) {} @@ -70,6 +100,7 @@ static inline void gpiod_add_lookup_tables(struct gpiod_lookup_table **tables, size_t n) {} static inline void gpiod_remove_lookup_table(struct gpiod_lookup_table *table) {} +static inline void gpiod_add_hogs(struct gpiod_hog *hogs) {} #endif #endif /* __LINUX_GPIO_MACHINE_H */