diff options
96 files changed, 3336 insertions, 825 deletions
diff --git a/Documentation/ABI/testing/sysfs-gpio b/Documentation/ABI/obsolete/sysfs-gpio index 55ffa2df1c10..867c1fab20e2 100644 --- a/Documentation/ABI/testing/sysfs-gpio +++ b/Documentation/ABI/obsolete/sysfs-gpio @@ -1,7 +1,7 @@ What: /sys/class/gpio/ Date: July 2008 KernelVersion: 2.6.27 -Contact: David Brownell <dbrownell@users.sourceforge.net> +Contact: Linus Walleij <linusw@kernel.org> Description: As a Kconfig option, individual GPIO signals may be accessed from @@ -26,3 +26,5 @@ Description: /label ... (r/o) descriptive, not necessarily unique /ngpio ... (r/o) number of GPIOs; numbered N to N + (ngpio - 1) + This ABI is deprecated and will be removed after 2020. It is + replaced with the GPIO character device. diff --git a/Documentation/ABI/testing/gpio-cdev b/Documentation/ABI/testing/gpio-cdev new file mode 100644 index 000000000000..7b265fbb47e3 --- /dev/null +++ b/Documentation/ABI/testing/gpio-cdev @@ -0,0 +1,26 @@ +What: /dev/gpiochip[0-9]+ +Date: November 2015 +KernelVersion: 4.4 +Contact: linux-gpio@vger.kernel.org +Description: + The character device files /dev/gpiochip* are the interface + between GPIO chips and userspace. + + The ioctl(2)-based ABI is defined and documented in + [include/uapi]<linux/gpio.h>. + + The following file operations are supported: + + open(2) + Currently the only useful flags are O_RDWR. + + ioctl(2) + Initiate various actions. + See the inline documentation in [include/uapi]<linux/gpio.h> + for descriptions of all ioctls. + + close(2) + Stops and free up the I/O contexts that was associated + with the file descriptor. + +Users: TBD diff --git a/Documentation/devicetree/bindings/gpio/gpio-ts4800.txt b/Documentation/devicetree/bindings/gpio/gpio-ts4800.txt new file mode 100644 index 000000000000..92ea9c8f6399 --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/gpio-ts4800.txt @@ -0,0 +1,20 @@ +* TS-4800 FPGA's GPIO controller bindings + +Required properties: +- compatible: Must be "technologic,ts4800-gpio". +- #gpio-cells: Should be two. The first cell is the pin number. +- reg: Physical base address of the controller and length + of memory mapped region. + +Optional property: +- ngpios: See "gpio.txt" + +Example: + +gpio1: gpio { + compatible = "technologic,ts4800-gpio"; + reg = <0x10020 0x6>; + ngpios = <8>; + gpio-controller; + #gpio-cells = <2>; +}; diff --git a/Documentation/devicetree/bindings/gpio/gpio-xgene-sb.txt b/Documentation/devicetree/bindings/gpio/gpio-xgene-sb.txt index dae130060537..5490c1d68981 100644 --- a/Documentation/devicetree/bindings/gpio/gpio-xgene-sb.txt +++ b/Documentation/devicetree/bindings/gpio/gpio-xgene-sb.txt @@ -1,10 +1,20 @@ APM X-Gene Standby GPIO controller bindings -This is a gpio controller in the standby domain. - -There are 20 GPIO pins from 0..21. There is no GPIO_DS14 or GPIO_DS15, -only GPIO_DS8..GPIO_DS13 support interrupts. The IRQ mapping -is currently 1-to-1 on interrupts 0x28 thru 0x2d. +This is a gpio controller in the standby domain. It also supports interrupt in +some particular pins which are sourced to its parent interrupt controller +as diagram below: + +-----------------+ + | X-Gene standby | + | GPIO controller +------ GPIO_0 ++------------+ | | ... +| Parent IRQ | EXT_INT_0 | +------ GPIO_8/EXT_INT_0 +| controller | (SPI40) | | ... +| (GICv2) +--------------+ +------ GPIO_[N+8]/EXT_INT_N +| | ... | | +| | EXT_INT_N | +------ GPIO_[N+9] +| | (SPI[40 + N])| | ... +| +--------------+ +------ GPIO_MAX ++------------+ +-----------------+ Required properties: - compatible: "apm,xgene-gpio-sb" for the X-Gene Standby GPIO controller @@ -15,10 +25,18 @@ Required properties: 0 = active high 1 = active low - gpio-controller: Marks the device node as a GPIO controller. -- interrupts: Shall contain exactly 6 interrupts. +- interrupts: The EXT_INT_0 parent interrupt resource must be listed first. +- interrupt-parent: Phandle of the parent interrupt controller. +- interrupt-cells: Should be two. + - first cell is 0-N coresponding for EXT_INT_0 to EXT_INT_N. + - second cell is used to specify flags. +- interrupt-controller: Marks the device node as an interrupt controller. +- apm,nr-gpios: Optional, specify number of gpios pin. +- apm,nr-irqs: Optional, specify number of interrupt pins. +- apm,irq-start: Optional, specify lowest gpio pin support interrupt. Example: - sbgpio: sbgpio@17001000 { + sbgpio: gpio@17001000{ compatible = "apm,xgene-gpio-sb"; reg = <0x0 0x17001000 0x0 0x400>; #gpio-cells = <2>; @@ -29,4 +47,19 @@ Example: <0x0 0x2b 0x1>, <0x0 0x2c 0x1>, <0x0 0x2d 0x1>; + interrupt-parent = <&gic>; + #interrupt-cells = <2>; + interrupt-controller; + apm,nr-gpios = <22>; + apm,nr-irqs = <6>; + apm,irq-start = <8>; + }; + + testuser { + compatible = "example,testuser"; + /* Use the GPIO_13/EXT_INT_5 line as an active high triggered + * level interrupt + */ + interrupts = <5 4>; + interrupt-parent = <&sbgpio>; }; diff --git a/MAINTAINERS b/MAINTAINERS index 30aca4aa5467..7b8c0583381a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -240,6 +240,12 @@ L: lm-sensors@lm-sensors.org S: Maintained F: drivers/hwmon/abituguru3.c +ACCES 104-DIO-48E GPIO DRIVER +M: William Breathitt Gray <vilhelm.gray@gmail.com> +L: linux-gpio@vger.kernel.org +S: Maintained +F: drivers/gpio/gpio-104-dio-48e.c + ACCES 104-IDI-48 GPIO DRIVER M: "William Breathitt Gray" <vilhelm.gray@gmail.com> L: linux-gpio@vger.kernel.org @@ -4815,10 +4821,14 @@ L: linux-gpio@vger.kernel.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-gpio.git S: Maintained F: Documentation/gpio/ +F: Documentation/ABI/testing/gpio-cdev +F: Documentation/ABI/obsolete/sysfs-gpio F: drivers/gpio/ F: include/linux/gpio/ F: include/linux/gpio.h F: include/asm-generic/gpio.h +F: include/uapi/linux/gpio.h +F: tools/gpio/ GRE DEMULTIPLEXER DRIVER M: Dmitry Kozlov <xeb@mail.ru> @@ -11871,6 +11881,12 @@ M: David Härdeman <david@hardeman.nu> S: Maintained F: drivers/media/rc/winbond-cir.c +WINSYSTEMS WS16C48 GPIO DRIVER +M: William Breathitt Gray <vilhelm.gray@gmail.com> +L: linux-gpio@vger.kernel.org +S: Maintained +F: drivers/gpio/gpio-ws16c48.c + WIMAX STACK M: Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> M: linux-wimax@intel.com diff --git a/arch/alpha/include/asm/gpio.h b/arch/alpha/include/asm/gpio.h deleted file mode 100644 index b3799d88ffcf..000000000000 --- a/arch/alpha/include/asm/gpio.h +++ /dev/null @@ -1,4 +0,0 @@ -#ifndef __LINUX_GPIO_H -#warning Include linux/gpio.h instead of asm/gpio.h -#include <linux/gpio.h> -#endif diff --git a/arch/arm/common/scoop.c b/arch/arm/common/scoop.c index e0df333202b8..9ba45ade5f48 100644 --- a/arch/arm/common/scoop.c +++ b/arch/arm/common/scoop.c @@ -69,7 +69,7 @@ static void __scoop_gpio_set(struct scoop_dev *sdev, static void scoop_gpio_set(struct gpio_chip *chip, unsigned offset, int value) { - struct scoop_dev *sdev = container_of(chip, struct scoop_dev, gpio); + struct scoop_dev *sdev = gpiochip_get_data(chip); unsigned long flags; spin_lock_irqsave(&sdev->scoop_lock, flags); @@ -81,7 +81,7 @@ static void scoop_gpio_set(struct gpio_chip *chip, unsigned offset, int value) static int scoop_gpio_get(struct gpio_chip *chip, unsigned offset) { - struct scoop_dev *sdev = container_of(chip, struct scoop_dev, gpio); + struct scoop_dev *sdev = gpiochip_get_data(chip); /* XXX: I'm unsure, but it seems so */ return !!(ioread16(sdev->base + SCOOP_GPRR) & (1 << (offset + 1))); @@ -90,7 +90,7 @@ static int scoop_gpio_get(struct gpio_chip *chip, unsigned offset) static int scoop_gpio_direction_input(struct gpio_chip *chip, unsigned offset) { - struct scoop_dev *sdev = container_of(chip, struct scoop_dev, gpio); + struct scoop_dev *sdev = gpiochip_get_data(chip); unsigned long flags; unsigned short gpcr; @@ -108,7 +108,7 @@ static int scoop_gpio_direction_input(struct gpio_chip *chip, static int scoop_gpio_direction_output(struct gpio_chip *chip, unsigned offset, int value) { - struct scoop_dev *sdev = container_of(chip, struct scoop_dev, gpio); + struct scoop_dev *sdev = gpiochip_get_data(chip); unsigned long flags; unsigned short gpcr; @@ -224,7 +224,7 @@ static int scoop_probe(struct platform_device *pdev) devptr->gpio.direction_input = scoop_gpio_direction_input; devptr->gpio.direction_output = scoop_gpio_direction_output; - ret = gpiochip_add(&devptr->gpio); + ret = gpiochip_add_data(&devptr->gpio, devptr); if (ret) goto err_gpio; } diff --git a/arch/arm/mach-gemini/gpio.c b/arch/arm/mach-gemini/gpio.c index 2478d9f4d92d..469a76ea0459 100644 --- a/arch/arm/mach-gemini/gpio.c +++ b/arch/arm/mach-gemini/gpio.c @@ -17,7 +17,7 @@ #include <linux/init.h> #include <linux/io.h> #include <linux/irq.h> -#include <linux/gpio.h> +#include <linux/gpio/driver.h> #include <mach/hardware.h> #include <mach/irqs.h> @@ -227,5 +227,5 @@ void __init gemini_gpio_init(void) (void *)i); } - BUG_ON(gpiochip_add(&gemini_gpio_chip)); + BUG_ON(gpiochip_add_data(&gemini_gpio_chip, NULL)); } diff --git a/arch/arm/mach-imx/mach-mx27ads.c b/arch/arm/mach-imx/mach-mx27ads.c index eb1c3477c48a..f510c43981d3 100644 --- a/arch/arm/mach-imx/mach-mx27ads.c +++ b/arch/arm/mach-imx/mach-mx27ads.c @@ -13,6 +13,8 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +#include <linux/gpio/driver.h> +/* Needed for gpio_to_irq() */ #include <linux/gpio.h> #include <linux/platform_device.h> #include <linux/mtd/mtd.h> @@ -243,7 +245,7 @@ static void __init mx27ads_regulator_init(void) vchip->ngpio = 1; vchip->direction_output = vgpio_dir_out; vchip->set = vgpio_set; - gpiochip_add(vchip); + gpiochip_add_data(vchip, NULL); platform_device_register_data(NULL, "reg-fixed-voltage", PLATFORM_DEVID_AUTO, diff --git a/arch/arm/mach-ixp4xx/common.c b/arch/arm/mach-ixp4xx/common.c index 1cb6f2f02880..26874f608ca9 100644 --- a/arch/arm/mach-ixp4xx/common.c +++ b/arch/arm/mach-ixp4xx/common.c @@ -27,7 +27,7 @@ #include <linux/clockchips.h> #include <linux/io.h> #include <linux/export.h> -#include <linux/gpio.h> +#include <linux/gpio/driver.h> #include <linux/cpu.h> #include <linux/pci.h> #include <linux/sched_clock.h> @@ -461,7 +461,7 @@ void __init ixp4xx_sys_init(void) platform_add_devices(ixp4xx_devices, ARRAY_SIZE(ixp4xx_devices)); - gpiochip_add(&ixp4xx_gpio_chip); + gpiochip_add_data(&ixp4xx_gpio_chip, NULL); if (cpu_is_ixp46x()) { int region; diff --git a/arch/arm/mach-s3c24xx/mach-h1940.c b/arch/arm/mach-s3c24xx/mach-h1940.c index 9f54300df4b3..7ed78619217c 100644 --- a/arch/arm/mach-s3c24xx/mach-h1940.c +++ b/arch/arm/mach-s3c24xx/mach-h1940.c @@ -664,7 +664,7 @@ static void __init h1940_map_io(void) /* Add latch gpio chip, set latch initial value */ h1940_latch_control(0, 0); - WARN_ON(gpiochip_add(&h1940_latch_gpiochip)); + WARN_ON(gpiochip_add_data(&h1940_latch_gpiochip, NULL)); } static void __init h1940_init_time(void) diff --git a/arch/arm/mach-sa1100/simpad.c b/arch/arm/mach-sa1100/simpad.c index d8965c682d2f..bb3ca9c763de 100644 --- a/arch/arm/mach-sa1100/simpad.c +++ b/arch/arm/mach-sa1100/simpad.c @@ -15,7 +15,7 @@ #include <linux/mtd/mtd.h> #include <linux/mtd/partitions.h> #include <linux/io.h> -#include <linux/gpio.h> +#include <linux/gpio/driver.h> #include <mach/hardware.h> #include <asm/setup.h> @@ -369,7 +369,7 @@ static int __init simpad_init(void) cs3_gpio.get = cs3_gpio_get; cs3_gpio.direction_input = cs3_gpio_direction_input; cs3_gpio.direction_output = cs3_gpio_direction_output; - ret = gpiochip_add(&cs3_gpio); + ret = gpiochip_add_data(&cs3_gpio, NULL); if (ret) printk(KERN_WARNING "simpad: Unable to register cs3 GPIO device"); diff --git a/arch/arm/mach-w90x900/gpio.c b/arch/arm/mach-w90x900/gpio.c index ba05aec7ea4b..55d1a00dbd28 100644 --- a/arch/arm/mach-w90x900/gpio.c +++ b/arch/arm/mach-w90x900/gpio.c @@ -20,7 +20,7 @@ #include <linux/list.h> #include <linux/module.h> #include <linux/io.h> -#include <linux/gpio.h> +#include <linux/gpio/driver.h> #include <mach/hardware.h> @@ -30,7 +30,6 @@ #define GPIO_IN (0x0C) #define GROUPINERV (0x10) #define GPIO_GPIO(Nb) (0x00000001 << (Nb)) -#define to_nuc900_gpio_chip(c) container_of(c, struct nuc900_gpio_chip, chip) #define NUC900_GPIO_CHIP(name, base_gpio, nr_gpio) \ { \ @@ -53,7 +52,7 @@ struct nuc900_gpio_chip { static int nuc900_gpio_get(struct gpio_chip *chip, unsigned offset) { - struct nuc900_gpio_chip *nuc900_gpio = to_nuc900_gpio_chip(chip); + struct nuc900_gpio_chip *nuc900_gpio = gpiochip_get_data(chip); void __iomem *pio = nuc900_gpio->regbase + GPIO_IN; unsigned int regval; @@ -65,7 +64,7 @@ static int nuc900_gpio_get(struct gpio_chip *chip, unsigned offset) static void nuc900_gpio_set(struct gpio_chip *chip, unsigned offset, int val) { - struct nuc900_gpio_chip *nuc900_gpio = to_nuc900_gpio_chip(chip); + struct nuc900_gpio_chip *nuc900_gpio = gpiochip_get_data(chip); void __iomem *pio = nuc900_gpio->regbase + GPIO_OUT; unsigned int regval; unsigned long flags; @@ -86,7 +85,7 @@ static void nuc900_gpio_set(struct gpio_chip *chip, unsigned offset, int val) static int nuc900_dir_input(struct gpio_chip *chip, unsigned offset) { - struct nuc900_gpio_chip *nuc900_gpio = to_nuc900_gpio_chip(chip); + struct nuc900_gpio_chip *nuc900_gpio = gpiochip_get_data(chip); void __iomem *pio = nuc900_gpio->regbase + GPIO_DIR; unsigned int regval; unsigned long flags; @@ -104,7 +103,7 @@ static int nuc900_dir_input(struct gpio_chip *chip, unsigned offset) static int nuc900_dir_output(struct gpio_chip *chip, unsigned offset, int val) { - struct nuc900_gpio_chip *nuc900_gpio = to_nuc900_gpio_chip(chip); + struct nuc900_gpio_chip *nuc900_gpio = gpiochip_get_data(chip); void __iomem *outreg = nuc900_gpio->regbase + GPIO_OUT; void __iomem *pio = nuc900_gpio->regbase + GPIO_DIR; unsigned int regval; @@ -149,6 +148,6 @@ void __init nuc900_init_gpio(int nr_group) gpio_chip = &nuc900_gpio[i]; spin_lock_init(&gpio_chip->gpio_lock); gpio_chip->regbase = GPIO_BASE + i * GROUPINERV; - gpiochip_add(&gpio_chip->chip); + gpiochip_add_data(&gpio_chip->chip, gpio_chip); } } diff --git a/arch/arm/plat-orion/gpio.c b/arch/arm/plat-orion/gpio.c index 7bd22d8e5b11..f74069386c13 100644 --- a/arch/arm/plat-orion/gpio.c +++ b/arch/arm/plat-orion/gpio.c @@ -154,8 +154,7 @@ err_out: */ static int orion_gpio_request(struct gpio_chip *chip, unsigned pin) { - struct orion_gpio_chip *ochip = - container_of(chip, struct orion_gpio_chip, chip); + struct orion_gpio_chip *ochip = gpiochip_get_data(chip); if (orion_gpio_is_valid(ochip, pin, GPIO_INPUT_OK) || orion_gpio_is_valid(ochip, pin, GPIO_OUTPUT_OK)) @@ -166,8 +165,7 @@ static int orion_gpio_request(struct gpio_chip *chip, unsigned pin) static int orion_gpio_direction_input(struct gpio_chip *chip, unsigned pin) { - struct orion_gpio_chip *ochip = - container_of(chip, struct orion_gpio_chip, chip); + struct orion_gpio_chip *ochip = gpiochip_get_data(chip); unsigned long flags; if (!orion_gpio_is_valid(ochip, pin, GPIO_INPUT_OK)) @@ -182,8 +180,7 @@ static int orion_gpio_direction_input(struct gpio_chip *chip, unsigned pin) static int orion_gpio_get(struct gpio_chip *chip, unsigned pin) { - struct orion_gpio_chip *ochip = - container_of(chip, struct orion_gpio_chip, chip); + struct orion_gpio_chip *ochip = gpiochip_get_data(chip); int val; if (readl(GPIO_IO_CONF(ochip)) & (1 << pin)) { @@ -198,8 +195,7 @@ static int orion_gpio_get(struct gpio_chip *chip, unsigned pin) static int orion_gpio_direction_output(struct gpio_chip *chip, unsigned pin, int value) { - struct orion_gpio_chip *ochip = - container_of(chip, struct orion_gpio_chip, chip); + struct orion_gpio_chip *ochip = gpiochip_get_data(chip); unsigned long flags; if (!orion_gpio_is_valid(ochip, pin, GPIO_OUTPUT_OK)) @@ -216,8 +212,7 @@ orion_gpio_direction_output(struct gpio_chip *chip, unsigned pin, int value) static void orion_gpio_set(struct gpio_chip *chip, unsigned pin, int value) { - struct orion_gpio_chip *ochip = - container_of(chip, struct orion_gpio_chip, chip); + struct orion_gpio_chip *ochip = gpiochip_get_data(chip); unsigned long flags; spin_lock_irqsave(&ochip->lock, flags); @@ -227,8 +222,7 @@ static void orion_gpio_set(struct gpio_chip *chip, unsigned pin, int value) static int orion_gpio_to_irq(struct gpio_chip *chip, unsigned pin) { - struct orion_gpio_chip *ochip = - container_of(chip, struct orion_gpio_chip, chip); + struct orion_gpio_chip *ochip = gpiochip_get_data(chip); return irq_create_mapping(ochip->domain, ochip->secondary_irq_base + pin); @@ -445,8 +439,8 @@ static void gpio_irq_handler(struct irq_desc *desc) static void orion_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) { - struct orion_gpio_chip *ochip = - container_of(chip, struct orion_gpio_chip, chip); + + struct orion_gpio_chip *ochip = gpiochip_get_data(chip); u32 out, io_conf, blink, in_pol, data_in, cause, edg_msk, lvl_msk; int i; @@ -567,7 +561,7 @@ void __init orion_gpio_init(struct device_node *np, ochip->mask_offset = mask_offset; ochip->secondary_irq_base = secondary_irq_base; - gpiochip_add(&ochip->chip); + gpiochip_add_data(&ochip->chip, ochip); /* * Mask and clear GPIO interrupts. diff --git a/arch/avr32/boards/merisc/setup.c b/arch/avr32/boards/merisc/setup.c index 83d896cc2aed..718a6d7eb808 100644 --- a/arch/avr32/boards/merisc/setup.c +++ b/arch/avr32/boards/merisc/setup.c @@ -27,7 +27,6 @@ #include <asm/io.h> #include <asm/setup.h> -#include <asm/gpio.h> #include <mach/at32ap700x.h> #include <mach/board.h> diff --git a/arch/avr32/mach-at32ap/pio.c b/arch/avr32/mach-at32ap/pio.c index 5020057ac7a2..83c2a0021b56 100644 --- a/arch/avr32/mach-at32ap/pio.c +++ b/arch/avr32/mach-at32ap/pio.c @@ -14,8 +14,8 @@ #include <linux/fs.h> #include <linux/platform_device.h> #include <linux/irq.h> +#include <linux/gpio.h> -#include <asm/gpio.h> #include <asm/io.h> #include <mach/portmux.h> diff --git a/arch/blackfin/kernel/bfin_gpio.c b/arch/blackfin/kernel/bfin_gpio.c index a017359c1826..c5d31287de01 100644 --- a/arch/blackfin/kernel/bfin_gpio.c +++ b/arch/blackfin/kernel/bfin_gpio.c @@ -11,6 +11,8 @@ #include <linux/err.h> #include <linux/proc_fs.h> #include <linux/seq_file.h> +#include <linux/gpio/driver.h> +/* FIXME: consumer API required for gpio_set_value() etc, get rid of this */ #include <linux/gpio.h> #include <linux/irq.h> @@ -1159,7 +1161,7 @@ static int bfin_gpiolib_direction_output(struct gpio_chip *chip, unsigned gpio, static int bfin_gpiolib_get_value(struct gpio_chip *chip, unsigned gpio) { - return bfin_gpio_get_value(gpio); + return !!bfin_gpio_get_value(gpio); } static void bfin_gpiolib_set_value(struct gpio_chip *chip, unsigned gpio, int value) @@ -1197,7 +1199,7 @@ static struct gpio_chip bfin_chip = { static int __init bfin_gpiolib_setup(void) { - return gpiochip_add(&bfin_chip); + return gpiochip_add_data(&bfin_chip, NULL); } arch_initcall(bfin_gpiolib_setup); #endif diff --git a/arch/blackfin/kernel/debug-mmrs.c b/arch/blackfin/kernel/debug-mmrs.c index 86b1cd3a0309..e272bca93c64 100644 --- a/arch/blackfin/kernel/debug-mmrs.c +++ b/arch/blackfin/kernel/debug-mmrs.c @@ -11,9 +11,9 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/i2c/bfin_twi.h> +#include <linux/gpio.h> #include <asm/blackfin.h> -#include <asm/gpio.h> #include <asm/gptimers.h> #include <asm/bfin_can.h> #include <asm/bfin_dma.h> diff --git a/arch/blackfin/mach-bf538/boards/ezkit.c b/arch/blackfin/mach-bf538/boards/ezkit.c index ae2fcbb00119..c423af36fa91 100644 --- a/arch/blackfin/mach-bf538/boards/ezkit.c +++ b/arch/blackfin/mach-bf538/boards/ezkit.c @@ -15,9 +15,9 @@ #include <linux/spi/flash.h> #include <linux/irq.h> #include <linux/interrupt.h> +#include <linux/gpio.h> #include <asm/bfin5xx_spi.h> #include <asm/dma.h> -#include <asm/gpio.h> #include <asm/nand.h> #include <asm/portmux.h> #include <asm/dpmc.h> diff --git a/arch/blackfin/mach-bf538/ext-gpio.c b/arch/blackfin/mach-bf538/ext-gpio.c index 471a9b184d5b..48c100228f2d 100644 --- a/arch/blackfin/mach-bf538/ext-gpio.c +++ b/arch/blackfin/mach-bf538/ext-gpio.c @@ -8,8 +8,8 @@ #include <linux/module.h> #include <linux/err.h> +#include <linux/gpio.h> #include <asm/blackfin.h> -#include <asm/gpio.h> #include <asm/portmux.h> #define DEFINE_REG(reg, off) \ @@ -116,9 +116,9 @@ static struct gpio_chip bf538_porte_chip = { static int __init bf538_extgpio_setup(void) { - return gpiochip_add(&bf538_portc_chip) | - gpiochip_add(&bf538_portd_chip) | - gpiochip_add(&bf538_porte_chip); + return gpiochip_add_data(&bf538_portc_chip, NULL) | + gpiochip_add_data(&bf538_portd_chip, NULL) | + gpiochip_add_data(&bf538_porte_chip, NULL); } arch_initcall(bf538_extgpio_setup); diff --git a/arch/blackfin/mach-bf548/boards/cm_bf548.c b/arch/blackfin/mach-bf548/boards/cm_bf548.c index 6d5ffdead067..120c9941c242 100644 --- a/arch/blackfin/mach-bf548/boards/cm_bf548.c +++ b/arch/blackfin/mach-bf548/boards/cm_bf548.c @@ -17,9 +17,9 @@ #include <linux/irq.h> #include <linux/interrupt.h> #include <linux/usb/musb.h> +#include <linux/gpio.h> #include <asm/bfin5xx_spi.h> #include <asm/dma.h> -#include <asm/gpio.h> #include <asm/nand.h> #include <asm/portmux.h> #include <asm/bfin_sdh.h> diff --git a/arch/blackfin/mach-bf548/boards/ezkit.c b/arch/blackfin/mach-bf548/boards/ezkit.c index 4204b9842532..3cdd4835a9f7 100644 --- a/arch/blackfin/mach-bf548/boards/ezkit.c +++ b/arch/blackfin/mach-bf548/boards/ezkit.c @@ -20,9 +20,9 @@ #include <linux/pinctrl/machine.h> #include <linux/pinctrl/pinconf-generic.h> #include <linux/platform_data/pinctrl-adi2.h> +#include <linux/gpio.h> #include <asm/bfin5xx_spi.h> #include <asm/dma.h> -#include <asm/gpio.h> #include <asm/nand.h> #include <asm/dpmc.h> #include <asm/bfin_sport.h> diff --git a/arch/blackfin/mach-bf609/boards/ezkit.c b/arch/blackfin/mach-bf609/boards/ezkit.c index c7928d8ebb82..aad5d7416886 100644 --- a/arch/blackfin/mach-bf609/boards/ezkit.c +++ b/arch/blackfin/mach-bf609/boards/ezkit.c @@ -21,8 +21,8 @@ #include <linux/pinctrl/pinconf-generic.h> #include <linux/platform_data/pinctrl-adi2.h> #include <linux/spi/adi_spi3.h> +#include <linux/gpio.h> #include <asm/dma.h> -#include <asm/gpio.h> #include <asm/nand.h> #include <asm/dpmc.h> #include <asm/portmux.h> diff --git a/arch/blackfin/mach-common/ints-priority.c b/arch/blackfin/mach-common/ints-priority.c index e8d4d748d0fd..4986b4fbcee9 100644 --- a/arch/blackfin/mach-common/ints-priority.c +++ b/arch/blackfin/mach-common/ints-priority.c @@ -17,13 +17,13 @@ #include <linux/irq.h> #include <linux/sched.h> #include <linux/syscore_ops.h> +#include <linux/gpio.h> #include <asm/delay.h> #ifdef CONFIG_IPIPE #include <linux/ipipe.h> #endif #include <asm/traps.h> #include <asm/blackfin.h> -#include <asm/gpio.h> #include <asm/irq_handler.h> #include <asm/dpmc.h> #include <asm/traps.h> diff --git a/arch/blackfin/mach-common/pm.c b/arch/blackfin/mach-common/pm.c index a66d979ec651..5ece38a5b758 100644 --- a/arch/blackfin/mach-common/pm.c +++ b/arch/blackfin/mach-common/pm.c @@ -15,9 +15,9 @@ #include <linux/io.h> #include <linux/irq.h> #include <linux/delay.h> +#include <linux/gpio.h> #include <asm/cplb.h> -#include <asm/gpio.h> #include <asm/dma.h> #include <asm/dpmc.h> #include <asm/pm.h> diff --git a/arch/ia64/include/asm/gpio.h b/arch/ia64/include/asm/gpio.h deleted file mode 100644 index b3799d88ffcf..000000000000 --- a/arch/ia64/include/asm/gpio.h +++ /dev/null @@ -1,4 +0,0 @@ -#ifndef __LINUX_GPIO_H -#warning Include linux/gpio.h instead of asm/gpio.h -#include <linux/gpio.h> -#endif diff --git a/arch/m68k/coldfire/gpio.c b/arch/m68k/coldfire/gpio.c index 37a83e27c7a6..8832083e1cb8 100644 --- a/arch/m68k/coldfire/gpio.c +++ b/arch/m68k/coldfire/gpio.c @@ -178,7 +178,7 @@ static struct gpio_chip mcfgpio_chip = { static int __init mcfgpio_sysinit(void) { - gpiochip_add(&mcfgpio_chip); + gpiochip_add_data(&mcfgpio_chip, NULL); return subsys_system_register(&mcfgpio_subsys, NULL); } diff --git a/arch/metag/include/asm/gpio.h b/arch/metag/include/asm/gpio.h deleted file mode 100644 index b3799d88ffcf..000000000000 --- a/arch/metag/include/asm/gpio.h +++ /dev/null @@ -1,4 +0,0 @@ -#ifndef __LINUX_GPIO_H -#warning Include linux/gpio.h instead of asm/gpio.h -#include <linux/gpio.h> -#endif diff --git a/arch/microblaze/include/asm/gpio.h b/arch/microblaze/include/asm/gpio.h deleted file mode 100644 index b3799d88ffcf..000000000000 --- a/arch/microblaze/include/asm/gpio.h +++ /dev/null @@ -1,4 +0,0 @@ -#ifndef __LINUX_GPIO_H -#warning Include linux/gpio.h instead of asm/gpio.h -#include <linux/gpio.h> -#endif diff --git a/arch/mips/alchemy/common/gpiolib.c b/arch/mips/alchemy/common/gpiolib.c index 84548f704035..e6b90e72c23f 100644 --- a/arch/mips/alchemy/common/gpiolib.c +++ b/arch/mips/alchemy/common/gpiolib.c @@ -160,14 +160,14 @@ static int __init alchemy_gpiochip_init(void) switch (alchemy_get_cputype()) { case ALCHEMY_CPU_AU1000: - ret = gpiochip_add(&alchemy_gpio_chip[0]); + ret = gpiochip_add_data(&alchemy_gpio_chip[0], NULL); break; case ALCHEMY_CPU_AU1500...ALCHEMY_CPU_AU1200: - ret = gpiochip_add(&alchemy_gpio_chip[0]); - ret |= gpiochip_add(&alchemy_gpio_chip[1]); + ret = gpiochip_add_data(&alchemy_gpio_chip[0], NULL); + ret |= gpiochip_add_data(&alchemy_gpio_chip[1], NULL); break; case ALCHEMY_CPU_AU1300: - ret = gpiochip_add(&au1300_gpiochip); + ret = gpiochip_add_data(&au1300_gpiochip, NULL); break; } return ret; diff --git a/arch/mips/ar7/gpio.c b/arch/mips/ar7/gpio.c index f969f583c68c..ed5b3d297caf 100644 --- a/arch/mips/ar7/gpio.c +++ b/arch/mips/ar7/gpio.c @@ -33,8 +33,7 @@ struct ar7_gpio_chip { static int ar7_gpio_get_value(struct gpio_chip *chip, unsigned gpio) { - struct ar7_gpio_chip *gpch = - container_of(chip, struct ar7_gpio_chip, chip); + struct ar7_gpio_chip *gpch = gpiochip_get_data(chip); void __iomem *gpio_in = gpch->regs + AR7_GPIO_INPUT; return !!(readl(gpio_in) & (1 << gpio)); @@ -42,8 +41,7 @@ static int ar7_gpio_get_value(struct gpio_chip *chip, unsigned gpio) static int titan_gpio_get_value(struct gpio_chip *chip, unsigned gpio) { - struct ar7_gpio_chip *gpch = - container_of(chip, struct ar7_gpio_chip, chip); + struct ar7_gpio_chip *gpch = gpiochip_get_data(chip); void __iomem *gpio_in0 = gpch->regs + TITAN_GPIO_INPUT_0; void __iomem *gpio_in1 = gpch->regs + TITAN_GPIO_INPUT_1; @@ -53,8 +51,7 @@ static int titan_gpio_get_value(struct gpio_chip *chip, unsigned gpio) static void ar7_gpio_set_value(struct gpio_chip *chip, unsigned gpio, int value) { - struct ar7_gpio_chip *gpch = - container_of(chip, struct ar7_gpio_chip, chip); + struct ar7_gpio_chip *gpch = gpiochip_get_data(chip); void __iomem *gpio_out = gpch->regs + AR7_GPIO_OUTPUT; unsigned tmp; @@ -67,8 +64,7 @@ static void ar7_gpio_set_value(struct gpio_chip *chip, static void titan_gpio_set_value(struct gpio_chip *chip, unsigned gpio, int value) { - struct ar7_gpio_chip *gpch = - container_of(chip, struct ar7_gpio_chip, chip); + struct ar7_gpio_chip *gpch = gpiochip_get_data(chip); void __iomem *gpio_out0 = gpch->regs + TITAN_GPIO_OUTPUT_0; void __iomem *gpio_out1 = gpch->regs + TITAN_GPIO_OUTPUT_1; unsigned tmp; @@ -81,8 +77,7 @@ static void titan_gpio_set_value(struct gpio_chip *chip, static int ar7_gpio_direction_input(struct gpio_chip *chip, unsigned gpio) { - struct ar7_gpio_chip *gpch = - container_of(chip, struct ar7_gpio_chip, chip); + struct ar7_gpio_chip *gpch = gpiochip_get_data(chip); void __iomem *gpio_dir = gpch->regs + AR7_GPIO_DIR; writel(readl(gpio_dir) | (1 << gpio), gpio_dir); @@ -92,8 +87,7 @@ static int ar7_gpio_direction_input(struct gpio_chip *chip, unsigned gpio) static int titan_gpio_direction_input(struct gpio_chip *chip, unsigned gpio) { - struct ar7_gpio_chip *gpch = - container_of(chip, struct ar7_gpio_chip, chip); + struct ar7_gpio_chip *gpch = gpiochip_get_data(chip); void __iomem *gpio_dir0 = gpch->regs + TITAN_GPIO_DIR_0; void __iomem *gpio_dir1 = gpch->regs + TITAN_GPIO_DIR_1; @@ -108,8 +102,7 @@ static int titan_gpio_direction_input(struct gpio_chip *chip, unsigned gpio) static int ar7_gpio_direction_output(struct gpio_chip *chip, unsigned gpio, int value) { - struct ar7_gpio_chip *gpch = - container_of(chip, struct ar7_gpio_chip, chip); + struct ar7_gpio_chip *gpch = gpiochip_get_data(chip); void __iomem *gpio_dir = gpch->regs + AR7_GPIO_DIR; ar7_gpio_set_value(chip, gpio, value); @@ -121,8 +114,7 @@ static int ar7_gpio_direction_output(struct gpio_chip *chip, static int titan_gpio_direction_output(struct gpio_chip *chip, unsigned gpio, int value) { - struct ar7_gpio_chip *gpch = - container_of(chip, struct ar7_gpio_chip, chip); + struct ar7_gpio_chip *gpch = gpiochip_get_data(chip); void __iomem *gpio_dir0 = gpch->regs + TITAN_GPIO_DIR_0; void __iomem *gpio_dir1 = gpch->regs + TITAN_GPIO_DIR_1; @@ -335,7 +327,7 @@ int __init ar7_gpio_init(void) return -ENOMEM; } - ret = gpiochip_add(&gpch->chip); + ret = gpiochip_add_data(&gpch->chip, gpch); if (ret) { printk(KERN_ERR "%s: failed to add gpiochip\n", gpch->chip.label); diff --git a/arch/mips/bcm63xx/gpio.c b/arch/mips/bcm63xx/gpio.c index 468bc7b99cd3..7c256dadb166 100644 --- a/arch/mips/bcm63xx/gpio.c +++ b/arch/mips/bcm63xx/gpio.c @@ -11,7 +11,7 @@ #include <linux/module.h> #include <linux/spinlock.h> #include <linux/platform_device.h> -#include <linux/gpio.h> +#include <linux/gpio/driver.h> #include <bcm63xx_cpu.h> #include <bcm63xx_gpio.h> @@ -147,5 +147,5 @@ int __init bcm63xx_gpio_init(void) bcm63xx_gpio_chip.ngpio = bcm63xx_gpio_count(); pr_info("registering %d GPIOs\n", bcm63xx_gpio_chip.ngpio); - return gpiochip_add(&bcm63xx_gpio_chip); + return gpiochip_add_data(&bcm63xx_gpio_chip, NULL); } diff --git a/arch/mips/jz4740/gpio.c b/arch/mips/jz4740/gpio.c index 8c6d76c9b2d6..b765773ab8aa 100644 --- a/arch/mips/jz4740/gpio.c +++ b/arch/mips/jz4740/gpio.c @@ -18,6 +18,8 @@ #include <linux/init.h> #include <linux/io.h> +#include <linux/gpio/driver.h> +/* FIXME: needed for gpio_request(), try to remove consumer API from driver */ #include <linux/gpio.h> #include <linux/delay.h> #include <linux/interrupt.h> @@ -91,9 +93,9 @@ static inline struct jz_gpio_chip *gpio_to_jz_gpio_chip(unsigned int gpio) return &jz4740_gpio_chips[gpio >> 5]; } -static inline struct jz_gpio_chip *gpio_chip_to_jz_gpio_chip(struct gpio_chip *gpio_chip) +static inline struct jz_gpio_chip *gpio_chip_to_jz_gpio_chip(struct gpio_chip *gc) { - return container_of(gpio_chip, struct jz_gpio_chip, gpio_chip); + return gpiochip_get_data(gc); } static inline struct jz_gpio_chip *irq_to_jz_gpio_chip(struct irq_data *data) @@ -234,7 +236,7 @@ static int jz_gpio_direction_input(struct gpio_chip *chip, unsigned gpio) static int jz_gpio_to_irq(struct gpio_chip *chip, unsigned gpio) { - struct jz_gpio_chip *jz_gpio = gpio_chip_to_jz_gpio_chip(chip); + struct jz_gpio_chip *jz_gpio = gpiochip_get_data(chip); return jz_gpio->irq_base + gpio; } @@ -270,7 +272,7 @@ uint32_t jz_gpio_port_get_value(int port, uint32_t mask) } EXPORT_SYMBOL(jz_gpio_port_get_value); -#define IRQ_TO_BIT(irq) BIT(irq_to_gpio(irq) & 0x1f) +#define IRQ_TO_BIT(irq) BIT((irq - JZ4740_IRQ_GPIO(0)) & 0x1f) static void jz_gpio_check_trigger_both(struct jz_gpio_chip *chip, unsigned int irq) { @@ -449,7 +451,7 @@ static void jz4740_gpio_chip_init(struct jz_gpio_chip *chip, unsigned int id) irq_setup_generic_chip(gc, IRQ_MSK(chip->gpio_chip.ngpio), IRQ_GC_INIT_NESTED_LOCK, 0, IRQ_NOPROBE | IRQ_LEVEL); - gpiochip_add(&chip->gpio_chip); + gpiochip_add_data(&chip->gpio_chip, chip); } static int __init jz4740_gpio_init(void) diff --git a/arch/mips/kernel/gpio_txx9.c b/arch/mips/kernel/gpio_txx9.c index 705be43c3533..cbd47f38073b 100644 --- a/arch/mips/kernel/gpio_txx9.c +++ b/arch/mips/kernel/gpio_txx9.c @@ -10,7 +10,7 @@ #include <linux/init.h> #include <linux/spinlock.h> -#include <linux/gpio.h> +#include <linux/gpio/driver.h> #include <linux/errno.h> #include <linux/io.h> #include <asm/txx9pio.h> @@ -85,5 +85,5 @@ int __init txx9_gpio_init(unsigned long baseaddr, return -ENODEV; txx9_gpio_chip.base = base; txx9_gpio_chip.ngpio = num; - return gpiochip_add(&txx9_gpio_chip); + return gpiochip_add_data(&txx9_gpio_chip, NULL); } diff --git a/arch/mips/rb532/gpio.c b/arch/mips/rb532/gpio.c index fd1108543a71..fdc704abc8d4 100644 --- a/arch/mips/rb532/gpio.c +++ b/arch/mips/rb532/gpio.c @@ -32,7 +32,7 @@ #include <linux/export.h> #include <linux/spinlock.h> #include <linux/platform_device.h> -#include <linux/gpio.h> +#include <linux/gpio/driver.h> #include <asm/mach-rc32434/rb.h> #include <asm/mach-rc32434/gpio.h> @@ -88,7 +88,7 @@ static int rb532_gpio_get(struct gpio_chip *chip, unsigned offset) { struct rb532_gpio_chip *gpch; - gpch = container_of(chip, struct rb532_gpio_chip, chip); + gpch = gpiochip_get_data(chip); return !!rb532_get_bit(offset, gpch->regbase + GPIOD); } @@ -100,7 +100,7 @@ static void rb532_gpio_set(struct gpio_chip *chip, { struct rb532_gpio_chip *gpch; - gpch = container_of(chip, struct rb532_gpio_chip, chip); + gpch = gpiochip_get_data(chip); rb532_set_bit(value, offset, gpch->regbase + GPIOD); } @@ -111,7 +111,7 @@ static int rb532_gpio_direction_input(struct gpio_chip *chip, unsigned offset) { struct rb532_gpio_chip *gpch; - gpch = container_of(chip, struct rb532_gpio_chip, chip); + gpch = gpiochip_get_data(chip); /* disable alternate function in case it's set */ rb532_set_bit(0, offset, gpch->regbase + GPIOFUNC); @@ -128,7 +128,7 @@ static int rb532_gpio_direction_output(struct gpio_chip *chip, { struct rb532_gpio_chip *gpch; - gpch = container_of(chip, struct rb532_gpio_chip, chip); + gpch = gpiochip_get_data(chip); /* disable alternate function in case it's set */ rb532_set_bit(0, offset, gpch->regbase + GPIOFUNC); @@ -200,7 +200,7 @@ int __init rb532_gpio_init(void) } /* Register our GPIO chip */ - gpiochip_add(&rb532_gpio_chip->chip); + gpiochip_add_data(&rb532_gpio_chip->chip, rb532_gpio_chip); return 0; } diff --git a/arch/mips/txx9/generic/setup.c b/arch/mips/txx9/generic/setup.c index 2fd350f31f4b..108f8a8d1640 100644 --- a/arch/mips/txx9/generic/setup.c +++ b/arch/mips/txx9/generic/setup.c @@ -17,7 +17,7 @@ #include <linux/module.h> #include <linux/clk.h> #include <linux/err.h> -#include <linux/gpio.h> +#include <linux/gpio/driver.h> #include <linux/platform_device.h> #include <linux/serial_core.h> #include <linux/mtd/physmap.h> @@ -687,16 +687,14 @@ struct txx9_iocled_data { static int txx9_iocled_get(struct gpio_chip *chip, unsigned int offset) { - struct txx9_iocled_data *data = - container_of(chip, struct txx9_iocled_data, chip); + struct txx9_iocled_data *data = gpiochip_get_data(chip); return !!(data->cur_val & (1 << offset)); } static void txx9_iocled_set(struct gpio_chip *chip, unsigned int offset, int value) { - struct txx9_iocled_data *data = - container_of(chip, struct txx9_iocled_data, chip); + struct txx9_iocled_data *data = gpiochip_get_data(chip); unsigned long flags; spin_lock_irqsave(&txx9_iocled_lock, flags); if (value) @@ -749,7 +747,7 @@ void __init txx9_iocled_init(unsigned long baseaddr, iocled->chip.label = "iocled"; iocled->chip.base = basenum; iocled->chip.ngpio = num; - if (gpiochip_add(&iocled->chip)) + if (gpiochip_add_data(&iocled->chip, iocled)) goto out_unmap; if (basenum < 0) basenum = iocled->chip.base; diff --git a/arch/mips/txx9/rbtx4938/setup.c b/arch/mips/txx9/rbtx4938/setup.c index c9afd05020e0..54de66837103 100644 --- a/arch/mips/txx9/rbtx4938/setup.c +++ b/arch/mips/txx9/rbtx4938/setup.c @@ -14,6 +14,7 @@ #include <linux/ioport.h> #include <linux/delay.h> #include <linux/platform_device.h> +#include <linux/gpio/driver.h> #include <linux/gpio.h> #include <linux/mtd/physmap.h> @@ -335,7 +336,7 @@ static void __init rbtx4938_mtd_init(void) static void __init rbtx4938_arch_init(void) { - gpiochip_add(&rbtx4938_spi_gpio_chip); + gpiochip_add_data(&rbtx4938_spi_gpio_chip, NULL); rbtx4938_pci_setup(); rbtx4938_spi_init(); } diff --git a/arch/openrisc/include/asm/gpio.h b/arch/openrisc/include/asm/gpio.h deleted file mode 100644 index b3799d88ffcf..000000000000 --- a/arch/openrisc/include/asm/gpio.h +++ /dev/null @@ -1,4 +0,0 @@ -#ifndef __LINUX_GPIO_H -#warning Include linux/gpio.h instead of asm/gpio.h -#include <linux/gpio.h> -#endif diff --git a/arch/powerpc/include/asm/gpio.h b/arch/powerpc/include/asm/gpio.h deleted file mode 100644 index b3799d88ffcf..000000000000 --- a/arch/powerpc/include/asm/gpio.h +++ /dev/null @@ -1,4 +0,0 @@ -#ifndef __LINUX_GPIO_H -#warning Include linux/gpio.h instead of asm/gpio.h -#include <linux/gpio.h> -#endif diff --git a/arch/powerpc/sysdev/ppc4xx_gpio.c b/arch/powerpc/sysdev/ppc4xx_gpio.c index fc65ad1b3293..d7a7ef135b9f 100644 --- a/arch/powerpc/sysdev/ppc4xx_gpio.c +++ b/arch/powerpc/sysdev/ppc4xx_gpio.c @@ -78,7 +78,7 @@ static int ppc4xx_gpio_get(struct gpio_chip *gc, unsigned int gpio) struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); struct ppc4xx_gpio __iomem *regs = mm_gc->regs; - return in_be32(®s->ir) & GPIO_MASK(gpio); + return !!(in_be32(®s->ir) & GPIO_MASK(gpio)); } static inline void diff --git a/arch/powerpc/sysdev/simple_gpio.c b/arch/powerpc/sysdev/simple_gpio.c index ff5e73230a36..56ce8ca3281b 100644 --- a/arch/powerpc/sysdev/simple_gpio.c +++ b/arch/powerpc/sysdev/simple_gpio.c @@ -46,7 +46,7 @@ static int u8_gpio_get(struct gpio_chip *gc, unsigned int gpio) { struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); - return in_8(mm_gc->regs) & u8_pin2mask(gpio); + return !!(in_8(mm_gc->regs) & u8_pin2mask(gpio)); } static void u8_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val) diff --git a/arch/sh/include/mach-common/mach/magicpanelr2.h b/arch/sh/include/mach-common/mach/magicpanelr2.h index 183a2f744251..eb0cf205176f 100644 --- a/arch/sh/include/mach-common/mach/magicpanelr2.h +++ b/arch/sh/include/mach-common/mach/magicpanelr2.h @@ -13,7 +13,7 @@ #ifndef __ASM_SH_MAGICPANELR2_H #define __ASM_SH_MAGICPANELR2_H -#include <asm/gpio.h> +#include <linux/gpio.h> #define __IO_PREFIX mpr2 #include <asm/io_generic.h> diff --git a/arch/sparc/include/asm/gpio.h b/arch/sparc/include/asm/gpio.h deleted file mode 100644 index b3799d88ffcf..000000000000 --- a/arch/sparc/include/asm/gpio.h +++ /dev/null @@ -1,4 +0,0 @@ -#ifndef __LINUX_GPIO_H -#warning Include linux/gpio.h instead of asm/gpio.h -#include <linux/gpio.h> -#endif diff --git a/arch/unicore32/kernel/gpio.c b/arch/unicore32/kernel/gpio.c index cb12ec39552c..5ab23794ea17 100644 --- a/arch/unicore32/kernel/gpio.c +++ b/arch/unicore32/kernel/gpio.c @@ -52,7 +52,7 @@ device_initcall(puv3_gpio_leds_init); static int puv3_gpio_get(struct gpio_chip *chip, unsigned offset) { - return readl(GPIO_GPLR) & GPIO_GPIO(offset); + return !!(readl(GPIO_GPLR) & GPIO_GPIO(offset)); } static void puv3_gpio_set(struct gpio_chip *chip, unsigned offset, int value) diff --git a/arch/x86/include/asm/gpio.h b/arch/x86/include/asm/gpio.h deleted file mode 100644 index b3799d88ffcf..000000000000 --- a/arch/x86/include/asm/gpio.h +++ /dev/null @@ -1,4 +0,0 @@ -#ifndef __LINUX_GPIO_H -#warning Include linux/gpio.h instead of asm/gpio.h -#include <linux/gpio.h> -#endif diff --git a/arch/xtensa/include/asm/gpio.h b/arch/xtensa/include/asm/gpio.h deleted file mode 100644 index b3799d88ffcf..000000000000 --- a/arch/xtensa/include/asm/gpio.h +++ /dev/null @@ -1,4 +0,0 @@ -#ifndef __LINUX_GPIO_H -#warning Include linux/gpio.h instead of asm/gpio.h -#include <linux/gpio.h> -#endif diff --git a/drivers/ata/pata_at91.c b/drivers/ata/pata_at91.c index ace0a4de3449..9f27b14009f9 100644 --- a/drivers/ata/pata_at91.c +++ b/drivers/ata/pata_at91.c @@ -30,8 +30,7 @@ #include <linux/ata_platform.h> #include <linux/platform_data/atmel.h> #include <linux/regmap.h> - -#include <asm/gpio.h> +#include <linux/gpio.h> #define DRV_NAME "pata_at91" #define DRV_VERSION "0.3" diff --git a/drivers/ata/pata_bf54x.c b/drivers/ata/pata_bf54x.c index dd7410019d15..ec748d31928d 100644 --- a/drivers/ata/pata_bf54x.c +++ b/drivers/ata/pata_bf54x.c @@ -36,8 +36,8 @@ #include <scsi/scsi_host.h> #include <linux/libata.h> #include <linux/platform_device.h> +#include <linux/gpio.h> #include <asm/dma.h> -#include <asm/gpio.h> #include <asm/portmux.h> #define DRV_NAME "pata-bf54x" diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index c88dd24a4b1f..ad226485a8e4 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -126,6 +126,16 @@ config GPIO_AMDPT driver for GPIO functionality on Promontory IOHub Require ACPI ASL code to enumerate as a platform device. +config GPIO_ATH79 + tristate "Atheros AR71XX/AR724X/AR913X GPIO support" + default y if ATH79 + depends on ATH79 || COMPILE_TEST + select GPIO_GENERIC + select GPIOLIB_IRQCHIP + help + Select this option to enable GPIO driver for + Atheros AR71XX/AR724X/AR913X SoC devices. + config GPIO_BCM_KONA bool "Broadcom Kona GPIO" depends on OF_GPIO && (ARCH_BCM_MOBILE || COMPILE_TEST) @@ -281,12 +291,14 @@ config GPIO_MPC5200 depends on PPC_MPC52xx config GPIO_MPC8XXX - bool "MPC512x/MPC8xxx GPIO support" + bool "MPC512x/MPC8xxx/QorIQ GPIO support" depends on PPC_MPC512x || PPC_MPC831x || PPC_MPC834x || PPC_MPC837x || \ - FSL_SOC_BOOKE || PPC_86xx + FSL_SOC_BOOKE || PPC_86xx || ARCH_LAYERSCAPE || ARM || \ + COMPILE_TEST + select GPIO_GENERIC help Say Y here if you're going to use hardware that connects to the - MPC512x/831x/834x/837x/8572/8610 GPIOs. + MPC512x/831x/834x/837x/8572/8610/QorIQ GPIOs. config GPIO_MVEBU def_bool y @@ -380,6 +392,13 @@ config GPIO_TB10X select GENERIC_IRQ_CHIP select OF_GPIO +config GPIO_TS4800 + tristate "TS-4800 DIO blocks and compatibles" + depends on OF_GPIO + select GPIO_GENERIC + help + This driver support TS-4800 FPGA GPIO controllers. + config GPIO_TZ1090 bool "Toumaz Xenif TZ1090 GPIO support" depends on SOC_TZ1090 @@ -487,6 +506,15 @@ endmenu menu "Port-mapped I/O GPIO drivers" depends on X86 # Unconditional I/O space access +config GPIO_104_DIO_48E + tristate "ACCES 104-DIO-48E GPIO support" + select GPIOLIB_IRQCHIP + help + Enables GPIO support for the ACCES 104-DIO-48E family. The base port + address for the device may be configured via the dio_48e_base module + parameter. The interrupt line number for the device may be configured + via the dio_48e_irq module parameter. + config GPIO_104_IDIO_16 tristate "ACCES 104-IDIO-16 GPIO support" select GPIOLIB_IRQCHIP @@ -506,10 +534,10 @@ config GPIO_104_IDI_48 via the idi_48_irq module parameter. config GPIO_F7188X - tristate "F71869, F71869A, F71882FG and F71889F GPIO support" + tristate "F71869, F71869A, F71882FG, F71889F and F81866 GPIO support" help This option enables support for GPIOs found on Fintek Super-I/O - chips F71869, F71869A, F71882FG and F71889F. + chips F71869, F71869A, F71882FG, F71889F and F81866. To compile this driver as a module, choose M here: the module will be called f7188x-gpio. @@ -570,6 +598,15 @@ config GPIO_TS5500 blocks of the TS-5500: DIO1, DIO2 and the LCD port, and the TS-5600 LCD port. +config GPIO_WS16C48 + tristate "WinSystems WS16C48 GPIO support" + select GPIOLIB_IRQCHIP + help + Enables GPIO support for the WinSystems WS16C48. The base port address + for the device may be configured via the ws16c48_base module + parameter. The interrupt line number for the device may be configured + via the ws16c48_irq module parameter. + endmenu menu "I2C GPIO expanders" @@ -702,6 +739,14 @@ config GPIO_SX150X 8 bits: sx1508q 16 bits: sx1509q +config GPIO_TPIC2810 + tristate "TPIC2810 8-Bit I2C GPO expander" + help + Say yes here to enable the GPO driver for the TI TPIC2810 chip. + + To compile this driver as a module, choose M here: the module will + be called gpio-tpic2810. + endmenu menu "MFD GPIO expanders" @@ -844,6 +889,13 @@ config GPIO_TIMBERDALE ---help--- Add support for the GPIO IP in the timberdale FPGA. +config GPIO_TPS65218 + tristate "TPS65218 GPIO" + depends on MFD_TPS65218 + help + Select this option to enable GPIO driver for the TPS65218 + chip family. + config GPIO_TPS6586X bool "TPS6586X GPIO" depends on MFD_TPS6586X @@ -1011,6 +1063,12 @@ config GPIO_MC33880 SPI driver for Freescale MC33880 high-side/low-side switch. This provides GPIO interface supporting inputs and outputs. +config GPIO_PISOSR + tristate "Generic parallel-in/serial-out shift register" + help + GPIO driver for SPI compatible parallel-in/serial-out shift + registers. These are input only devices. + endmenu menu "SPI or I2C GPIO expanders" diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index ece7d7cbdc80..f1d3d823d1bc 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_GPIO_ACPI) += gpiolib-acpi.o # Device drivers. Generally keep list sorted alphabetically obj-$(CONFIG_GPIO_GENERIC) += gpio-generic.o +obj-$(CONFIG_GPIO_104_DIO_48E) += gpio-104-dio-48e.o obj-$(CONFIG_GPIO_104_IDIO_16) += gpio-104-idio-16.o obj-$(CONFIG_GPIO_104_IDI_48) += gpio-104-idi-48.o obj-$(CONFIG_GPIO_74X164) += gpio-74x164.o @@ -23,7 +24,7 @@ obj-$(CONFIG_GPIO_ALTERA) += gpio-altera.o obj-$(CONFIG_GPIO_AMD8111) += gpio-amd8111.o obj-$(CONFIG_GPIO_AMDPT) += gpio-amdpt.o obj-$(CONFIG_GPIO_ARIZONA) += gpio-arizona.o -obj-$(CONFIG_ATH79) += gpio-ath79.o +obj-$(CONFIG_GPIO_ATH79) += gpio-ath79.o obj-$(CONFIG_GPIO_BCM_KONA) += gpio-bcm-kona.o obj-$(CONFIG_GPIO_BRCMSTB) += gpio-brcmstb.o obj-$(CONFIG_GPIO_BT8XX) += gpio-bt8xx.o @@ -75,6 +76,7 @@ obj-$(CONFIG_GPIO_OMAP) += gpio-omap.o obj-$(CONFIG_GPIO_PCA953X) += gpio-pca953x.o obj-$(CONFIG_GPIO_PCF857X) += gpio-pcf857x.o obj-$(CONFIG_GPIO_PCH) += gpio-pch.o +obj-$(CONFIG_GPIO_PISOSR) += gpio-pisosr.o obj-$(CONFIG_GPIO_PL061) += gpio-pl061.o obj-$(CONFIG_GPIO_PXA) += gpio-pxa.o obj-$(CONFIG_GPIO_RC5T583) += gpio-rc5t583.o @@ -95,9 +97,12 @@ obj-$(CONFIG_GPIO_TC3589X) += gpio-tc3589x.o obj-$(CONFIG_ARCH_TEGRA) += gpio-tegra.o obj-$(CONFIG_GPIO_TIMBERDALE) += gpio-timberdale.o obj-$(CONFIG_GPIO_PALMAS) += gpio-palmas.o +obj-$(CONFIG_GPIO_TPIC2810) += gpio-tpic2810.o +obj-$(CONFIG_GPIO_TPS65218) += gpio-tps65218.o obj-$(CONFIG_GPIO_TPS6586X) += gpio-tps6586x.o obj-$(CONFIG_GPIO_TPS65910) += gpio-tps65910.o obj-$(CONFIG_GPIO_TPS65912) += gpio-tps65912.o +obj-$(CONFIG_GPIO_TS4800) += gpio-ts4800.o obj-$(CONFIG_GPIO_TS5500) += gpio-ts5500.o obj-$(CONFIG_GPIO_TWL4030) += gpio-twl4030.o obj-$(CONFIG_GPIO_TWL6040) += gpio-twl6040.o @@ -111,6 +116,7 @@ obj-$(CONFIG_GPIO_VX855) += gpio-vx855.o obj-$(CONFIG_GPIO_WM831X) += gpio-wm831x.o obj-$(CONFIG_GPIO_WM8350) += gpio-wm8350.o obj-$(CONFIG_GPIO_WM8994) += gpio-wm8994.o +obj-$(CONFIG_GPIO_WS16C48) += gpio-ws16c48.o obj-$(CONFIG_GPIO_XGENE) += gpio-xgene.o obj-$(CONFIG_GPIO_XGENE_SB) += gpio-xgene-sb.o obj-$(CONFIG_GPIO_XILINX) += gpio-xilinx.o diff --git a/drivers/gpio/gpio-104-dio-48e.c b/drivers/gpio/gpio-104-dio-48e.c new file mode 100644 index 000000000000..448a903089ef --- /dev/null +++ b/drivers/gpio/gpio-104-dio-48e.c @@ -0,0 +1,430 @@ +/* + * GPIO driver for the ACCES 104-DIO-48E + * Copyright (C) 2016 William Breathitt Gray + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ +#include <linux/bitops.h> +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/gpio/driver.h> +#include <linux/io.h> +#include <linux/ioport.h> +#include <linux/interrupt.h> +#include <linux/irqdesc.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/platform_device.h> +#include <linux/spinlock.h> + +static unsigned dio_48e_base; +module_param(dio_48e_base, uint, 0); +MODULE_PARM_DESC(dio_48e_base, "ACCES 104-DIO-48E base address"); +static unsigned dio_48e_irq; +module_param(dio_48e_irq, uint, 0); +MODULE_PARM_DESC(dio_48e_irq, "ACCES 104-DIO-48E interrupt line number"); + +/** + * struct dio48e_gpio - GPIO device private data structure + * @chip: instance of the gpio_chip + * @io_state: bit I/O state (whether bit is set to input or output) + * @out_state: output bits state + * @control: Control registers state + * @lock: synchronization lock to prevent I/O race conditions + * @base: base port address of the GPIO device + * @irq: Interrupt line number + * @irq_mask: I/O bits affected by interrupts + */ +struct dio48e_gpio { + struct gpio_chip chip; + unsigned char io_state[6]; + unsigned char out_state[6]; + unsigned char control[2]; + spinlock_t lock; + unsigned base; + unsigned irq; + unsigned char irq_mask; +}; + +static int dio48e_gpio_get_direction(struct gpio_chip *chip, unsigned offset) +{ + struct dio48e_gpio *const dio48egpio = gpiochip_get_data(chip); + const unsigned port = offset / 8; + const unsigned mask = BIT(offset % 8); + + return !!(dio48egpio->io_state[port] & mask); +} + +static int dio48e_gpio_direction_input(struct gpio_chip *chip, unsigned offset) +{ + struct dio48e_gpio *const dio48egpio = gpiochip_get_data(chip); + const unsigned io_port = offset / 8; + const unsigned control_port = io_port / 2; + const unsigned control_addr = dio48egpio->base + 3 + control_port*4; + unsigned long flags; + unsigned control; + + spin_lock_irqsave(&dio48egpio->lock, flags); + + /* Check if configuring Port C */ + if (io_port == 2 || io_port == 5) { + /* Port C can be configured by nibble */ + if (offset % 8 > 3) { + dio48egpio->io_state[io_port] |= 0xF0; + dio48egpio->control[control_port] |= BIT(3); + } else { + dio48egpio->io_state[io_port] |= 0x0F; + dio48egpio->control[control_port] |= BIT(0); + } + } else { + dio48egpio->io_state[io_port] |= 0xFF; + if (io_port == 0 || io_port == 3) + dio48egpio->control[control_port] |= BIT(4); + else + dio48egpio->control[control_port] |= BIT(1); + } + + control = BIT(7) | dio48egpio->control[control_port]; + outb(control, control_addr); + control &= ~BIT(7); + outb(control, control_addr); + + spin_unlock_irqrestore(&dio48egpio->lock, flags); + + return 0; +} + +static int dio48e_gpio_direction_output(struct gpio_chip *chip, unsigned offset, + int value) +{ + struct dio48e_gpio *const dio48egpio = gpiochip_get_data(chip); + const unsigned io_port = offset / 8; + const unsigned control_port = io_port / 2; + const unsigned mask = BIT(offset % 8); + const unsigned control_addr = dio48egpio->base + 3 + control_port*4; + const unsigned out_port = (io_port > 2) ? io_port + 1 : io_port; + unsigned long flags; + unsigned control; + + spin_lock_irqsave(&dio48egpio->lock, flags); + + /* Check if configuring Port C */ + if (io_port == 2 || io_port == 5) { + /* Port C can be configured by nibble */ + if (offset % 8 > 3) { + dio48egpio->io_state[io_port] &= 0x0F; + dio48egpio->control[control_port] &= ~BIT(3); + } else { + dio48egpio->io_state[io_port] &= 0xF0; + dio48egpio->control[control_port] &= ~BIT(0); + } + } else { + dio48egpio->io_state[io_port] &= 0x00; + if (io_port == 0 || io_port == 3) + dio48egpio->control[control_port] &= ~BIT(4); + else + dio48egpio->control[control_port] &= ~BIT(1); + } + + if (value) + dio48egpio->out_state[io_port] |= mask; + else + dio48egpio->out_state[io_port] &= ~mask; + + control = BIT(7) | dio48egpio->control[control_port]; + outb(control, control_addr); + + outb(dio48egpio->out_state[io_port], dio48egpio->base + out_port); + + control &= ~BIT(7); + outb(control, control_addr); + + spin_unlock_irqrestore(&dio48egpio->lock, flags); + + return 0; +} + +static int dio48e_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + struct dio48e_gpio *const dio48egpio = gpiochip_get_data(chip); + const unsigned port = offset / 8; + const unsigned mask = BIT(offset % 8); + const unsigned in_port = (port > 2) ? port + 1 : port; + unsigned long flags; + unsigned port_state; + + spin_lock_irqsave(&dio48egpio->lock, flags); + + /* ensure that GPIO is set for input */ + if (!(dio48egpio->io_state[port] & mask)) { + spin_unlock_irqrestore(&dio48egpio->lock, flags); + return -EINVAL; + } + + port_state = inb(dio48egpio->base + in_port); + + spin_unlock_irqrestore(&dio48egpio->lock, flags); + + return !!(port_state & mask); +} + +static void dio48e_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +{ + struct dio48e_gpio *const dio48egpio = gpiochip_get_data(chip); + const unsigned port = offset / 8; + const unsigned mask = BIT(offset % 8); + const unsigned out_port = (port > 2) ? port + 1 : port; + unsigned long flags; + + spin_lock_irqsave(&dio48egpio->lock, flags); + + if (value) + dio48egpio->out_state[port] |= mask; + else + dio48egpio->out_state[port] &= ~mask; + + outb(dio48egpio->out_state[port], dio48egpio->base + out_port); + + spin_unlock_irqrestore(&dio48egpio->lock, flags); +} + +static void dio48e_irq_ack(struct irq_data *data) +{ +} + +static void dio48e_irq_mask(struct irq_data *data) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); + struct dio48e_gpio *const dio48egpio = gpiochip_get_data(chip); + const unsigned long offset = irqd_to_hwirq(data); + unsigned long flags; + + /* only bit 3 on each respective Port C supports interrupts */ + if (offset != 19 && offset != 43) + return; + + spin_lock_irqsave(&dio48egpio->lock, flags); + + if (offset == 19) + dio48egpio->irq_mask &= ~BIT(0); + else + dio48egpio->irq_mask &= ~BIT(1); + + if (!dio48egpio->irq_mask) + /* disable interrupts */ + inb(dio48egpio->base + 0xB); + + spin_unlock_irqrestore(&dio48egpio->lock, flags); +} + +static void dio48e_irq_unmask(struct irq_data *data) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); + struct dio48e_gpio *const dio48egpio = gpiochip_get_data(chip); + const unsigned long offset = irqd_to_hwirq(data); + unsigned long flags; + + /* only bit 3 on each respective Port C supports interrupts */ + if (offset != 19 && offset != 43) + return; + + spin_lock_irqsave(&dio48egpio->lock, flags); + + if (!dio48egpio->irq_mask) { + /* enable interrupts */ + outb(0x00, dio48egpio->base + 0xF); + outb(0x00, dio48egpio->base + 0xB); + } + + if (offset == 19) + dio48egpio->irq_mask |= BIT(0); + else + dio48egpio->irq_mask |= BIT(1); + + spin_unlock_irqrestore(&dio48egpio->lock, flags); +} + +static int dio48e_irq_set_type(struct irq_data *data, unsigned flow_type) +{ + const unsigned long offset = irqd_to_hwirq(data); + + /* only bit 3 on each respective Port C supports interrupts */ + if (offset != 19 && offset != 43) + return -EINVAL; + + if (flow_type != IRQ_TYPE_NONE && flow_type != IRQ_TYPE_EDGE_RISING) + return -EINVAL; + + return 0; +} + +static struct irq_chip dio48e_irqchip = { + .name = "104-dio-48e", + .irq_ack = dio48e_irq_ack, + .irq_mask = dio48e_irq_mask, + .irq_unmask = dio48e_irq_unmask, + .irq_set_type = dio48e_irq_set_type +}; + +static irqreturn_t dio48e_irq_handler(int irq, void *dev_id) +{ + struct dio48e_gpio *const dio48egpio = dev_id; + struct gpio_chip *const chip = &dio48egpio->chip; + const unsigned long irq_mask = dio48egpio->irq_mask; + unsigned long gpio; + + for_each_set_bit(gpio, &irq_mask, 2) + generic_handle_irq(irq_find_mapping(chip->irqdomain, + 19 + gpio*24)); + + spin_lock(&dio48egpio->lock); + + outb(0x00, dio48egpio->base + 0xF); + + spin_unlock(&dio48egpio->lock); + + return IRQ_HANDLED; +} + +static int __init dio48e_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct dio48e_gpio *dio48egpio; + const unsigned base = dio_48e_base; + const unsigned extent = 16; + const char *const name = dev_name(dev); + int err; + const unsigned irq = dio_48e_irq; + + dio48egpio = devm_kzalloc(dev, sizeof(*dio48egpio), GFP_KERNEL); + if (!dio48egpio) + return -ENOMEM; + + if (!devm_request_region(dev, base, extent, name)) { + dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n", + base, base + extent); + return -EBUSY; + } + + dio48egpio->chip.label = name; + dio48egpio->chip.parent = dev; + dio48egpio->chip.owner = THIS_MODULE; + dio48egpio->chip.base = -1; + dio48egpio->chip.ngpio = 48; + dio48egpio->chip.get_direction = dio48e_gpio_get_direction; + dio48egpio->chip.direction_input = dio48e_gpio_direction_input; + dio48egpio->chip.direction_output = dio48e_gpio_direction_output; + dio48egpio->chip.get = dio48e_gpio_get; + dio48egpio->chip.set = dio48e_gpio_set; + dio48egpio->base = base; + dio48egpio->irq = irq; + + spin_lock_init(&dio48egpio->lock); + + dev_set_drvdata(dev, dio48egpio); + + err = gpiochip_add_data(&dio48egpio->chip, dio48egpio); + if (err) { + dev_err(dev, "GPIO registering failed (%d)\n", err); + return err; + } + + /* initialize all GPIO as output */ + outb(0x80, base + 3); + outb(0x00, base); + outb(0x00, base + 1); + outb(0x00, base + 2); + outb(0x00, base + 3); + outb(0x80, base + 7); + outb(0x00, base + 4); + outb(0x00, base + 5); + outb(0x00, base + 6); + outb(0x00, base + 7); + + /* disable IRQ by default */ + inb(base + 0xB); + + err = gpiochip_irqchip_add(&dio48egpio->chip, &dio48e_irqchip, 0, + handle_edge_irq, IRQ_TYPE_NONE); + if (err) { + dev_err(dev, "Could not add irqchip (%d)\n", err); + goto err_gpiochip_remove; + } + + err = request_irq(irq, dio48e_irq_handler, 0, name, dio48egpio); + if (err) { + dev_err(dev, "IRQ handler registering failed (%d)\n", err); + goto err_gpiochip_remove; + } + + return 0; + +err_gpiochip_remove: + gpiochip_remove(&dio48egpio->chip); + return err; +} + +static int dio48e_remove(struct platform_device *pdev) +{ + struct dio48e_gpio *const dio48egpio = platform_get_drvdata(pdev); + + free_irq(dio48egpio->irq, dio48egpio); + gpiochip_remove(&dio48egpio->chip); + + return 0; +} + +static struct platform_device *dio48e_device; + +static struct platform_driver dio48e_driver = { + .driver = { + .name = "104-dio-48e" + }, + .remove = dio48e_remove +}; + +static void __exit dio48e_exit(void) +{ + platform_device_unregister(dio48e_device); + platform_driver_unregister(&dio48e_driver); +} + +static int __init dio48e_init(void) +{ + int err; + + dio48e_device = platform_device_alloc(dio48e_driver.driver.name, -1); + if (!dio48e_device) + return -ENOMEM; + + err = platform_device_add(dio48e_device); + if (err) + goto err_platform_device; + + err = platform_driver_probe(&dio48e_driver, dio48e_probe); + if (err) + goto err_platform_driver; + + return 0; + +err_platform_driver: + platform_device_del(dio48e_device); +err_platform_device: + platform_device_put(dio48e_device); + return err; +} + +module_init(dio48e_init); +module_exit(dio48e_exit); + +MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>"); +MODULE_DESCRIPTION("ACCES 104-DIO-48E GPIO driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpio/gpio-104-idi-48.c b/drivers/gpio/gpio-104-idi-48.c index 52eed328ce99..e37cd4cdda35 100644 --- a/drivers/gpio/gpio-104-idi-48.c +++ b/drivers/gpio/gpio-104-idi-48.c @@ -39,7 +39,6 @@ MODULE_PARM_DESC(idi_48_irq, "ACCES 104-IDI-48 interrupt line number"); * @ack_lock: synchronization lock to prevent IRQ handler race conditions * @irq_mask: input bits affected by interrupts * @base: base port address of the GPIO device - * @extent: extent of port address region of the GPIO device * @irq: Interrupt line number * @cos_enb: Change-Of-State IRQ enable boundaries mask */ @@ -49,7 +48,6 @@ struct idi_48_gpio { spinlock_t ack_lock; unsigned char irq_mask[6]; unsigned base; - unsigned extent; unsigned irq; unsigned char cos_enb; }; @@ -227,11 +225,10 @@ static int __init idi_48_probe(struct platform_device *pdev) if (!idi48gpio) return -ENOMEM; - if (!request_region(base, extent, name)) { - dev_err(dev, "Unable to lock %s port addresses (0x%X-0x%X)\n", - name, base, base + extent); - err = -EBUSY; - goto err_lock_io_port; + if (!devm_request_region(dev, base, extent, name)) { + dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n", + base, base + extent); + return -EBUSY; } idi48gpio->chip.label = name; @@ -243,7 +240,6 @@ static int __init idi_48_probe(struct platform_device *pdev) idi48gpio->chip.direction_input = idi_48_gpio_direction_input; idi48gpio->chip.get = idi_48_gpio_get; idi48gpio->base = base; - idi48gpio->extent = extent; idi48gpio->irq = irq; spin_lock_init(&idi48gpio->lock); @@ -253,7 +249,7 @@ static int __init idi_48_probe(struct platform_device *pdev) err = gpiochip_add_data(&idi48gpio->chip, idi48gpio); if (err) { dev_err(dev, "GPIO registering failed (%d)\n", err); - goto err_gpio_register; + return err; } /* Disable IRQ by default */ @@ -264,23 +260,20 @@ static int __init idi_48_probe(struct platform_device *pdev) handle_edge_irq, IRQ_TYPE_NONE); if (err) { dev_err(dev, "Could not add irqchip (%d)\n", err); - goto err_gpiochip_irqchip_add; + goto err_gpiochip_remove; } - err = request_irq(irq, idi_48_irq_handler, 0, name, idi48gpio); + err = request_irq(irq, idi_48_irq_handler, IRQF_SHARED, name, + idi48gpio); if (err) { dev_err(dev, "IRQ handler registering failed (%d)\n", err); - goto err_request_irq; + goto err_gpiochip_remove; } return 0; -err_request_irq: -err_gpiochip_irqchip_add: +err_gpiochip_remove: gpiochip_remove(&idi48gpio->chip); -err_gpio_register: - release_region(base, extent); -err_lock_io_port: return err; } @@ -290,7 +283,6 @@ static int idi_48_remove(struct platform_device *pdev) free_irq(idi48gpio->irq, idi48gpio); gpiochip_remove(&idi48gpio->chip); - release_region(idi48gpio->base, idi48gpio->extent); return 0; } @@ -340,4 +332,4 @@ module_exit(idi_48_exit); MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>"); MODULE_DESCRIPTION("ACCES 104-IDI-48 GPIO driver"); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpio/gpio-104-idio-16.c b/drivers/gpio/gpio-104-idio-16.c index 4d69b50b2d84..ecc85fe9323d 100644 --- a/drivers/gpio/gpio-104-idio-16.c +++ b/drivers/gpio/gpio-104-idio-16.c @@ -38,7 +38,6 @@ MODULE_PARM_DESC(idio_16_irq, "ACCES 104-IDIO-16 interrupt line number"); * @lock: synchronization lock to prevent I/O race conditions * @irq_mask: I/O bits affected by interrupts * @base: base port address of the GPIO device - * @extent: extent of port address region of the GPIO device * @irq: Interrupt line number * @out_state: output bits state */ @@ -47,7 +46,6 @@ struct idio_16_gpio { spinlock_t lock; unsigned long irq_mask; unsigned base; - unsigned extent; unsigned irq; unsigned out_state; }; @@ -201,11 +199,10 @@ static int __init idio_16_probe(struct platform_device *pdev) if (!idio16gpio) return -ENOMEM; - if (!request_region(base, extent, name)) { - dev_err(dev, "Unable to lock %s port addresses (0x%X-0x%X)\n", - name, base, base + extent); - err = -EBUSY; - goto err_lock_io_port; + if (!devm_request_region(dev, base, extent, name)) { + dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n", + base, base + extent); + return -EBUSY; } idio16gpio->chip.label = name; @@ -219,7 +216,6 @@ static int __init idio_16_probe(struct platform_device *pdev) idio16gpio->chip.get = idio_16_gpio_get; idio16gpio->chip.set = idio_16_gpio_set; idio16gpio->base = base; - idio16gpio->extent = extent; idio16gpio->irq = irq; idio16gpio->out_state = 0xFFFF; @@ -230,7 +226,7 @@ static int __init idio_16_probe(struct platform_device *pdev) err = gpiochip_add_data(&idio16gpio->chip, idio16gpio); if (err) { dev_err(dev, "GPIO registering failed (%d)\n", err); - goto err_gpio_register; + return err; } /* Disable IRQ by default */ @@ -241,23 +237,19 @@ static int __init idio_16_probe(struct platform_device *pdev) handle_edge_irq, IRQ_TYPE_NONE); if (err) { dev_err(dev, "Could not add irqchip (%d)\n", err); - goto err_gpiochip_irqchip_add; + goto err_gpiochip_remove; } err = request_irq(irq, idio_16_irq_handler, 0, name, idio16gpio); if (err) { dev_err(dev, "IRQ handler registering failed (%d)\n", err); - goto err_request_irq; + goto err_gpiochip_remove; } return 0; -err_request_irq: -err_gpiochip_irqchip_add: +err_gpiochip_remove: gpiochip_remove(&idio16gpio->chip); -err_gpio_register: - release_region(base, extent); -err_lock_io_port: return err; } @@ -267,7 +259,6 @@ static int idio_16_remove(struct platform_device *pdev) free_irq(idio16gpio->irq, idio16gpio); gpiochip_remove(&idio16gpio->chip); - release_region(idio16gpio->base, idio16gpio->extent); return 0; } @@ -317,4 +308,4 @@ module_exit(idio_16_exit); MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>"); MODULE_DESCRIPTION("ACCES 104-IDIO-16 GPIO driver"); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpio/gpio-amd8111.c b/drivers/gpio/gpio-amd8111.c index c7040fffc5b4..30ad7d7c1678 100644 --- a/drivers/gpio/gpio-amd8111.c +++ b/drivers/gpio/gpio-amd8111.c @@ -25,6 +25,7 @@ * License version 2. This program is licensed "as is" without any * warranty of any kind, whether express or implied. */ +#include <linux/ioport.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/gpio.h> @@ -204,7 +205,8 @@ found: gp.pmbase &= 0x0000FF00; if (gp.pmbase == 0) goto out; - if (!request_region(gp.pmbase + PMBASE_OFFSET, PMBASE_SIZE, "AMD GPIO")) { + if (!devm_request_region(&pdev->dev, gp.pmbase + PMBASE_OFFSET, + PMBASE_SIZE, "AMD GPIO")) { dev_err(&pdev->dev, "AMD GPIO region 0x%x already in use!\n", gp.pmbase + PMBASE_OFFSET); err = -EBUSY; @@ -213,7 +215,6 @@ found: gp.pm = ioport_map(gp.pmbase + PMBASE_OFFSET, PMBASE_SIZE); if (!gp.pm) { dev_err(&pdev->dev, "Couldn't map io port into io memory\n"); - release_region(gp.pmbase + PMBASE_OFFSET, PMBASE_SIZE); err = -ENOMEM; goto out; } @@ -228,7 +229,6 @@ found: printk(KERN_ERR "GPIO registering failed (%d)\n", err); ioport_unmap(gp.pm); - release_region(gp.pmbase + PMBASE_OFFSET, PMBASE_SIZE); goto out; } out: @@ -239,7 +239,6 @@ static void __exit amd_gpio_exit(void) { gpiochip_remove(&gp.chip); ioport_unmap(gp.pm); - release_region(gp.pmbase + PMBASE_OFFSET, PMBASE_SIZE); } module_init(amd_gpio_init); diff --git a/drivers/gpio/gpio-ath79.c b/drivers/gpio/gpio-ath79.c index d13dd133a907..018ea9d33b54 100644 --- a/drivers/gpio/gpio-ath79.c +++ b/drivers/gpio/gpio-ath79.c @@ -1,12 +1,11 @@ /* * Atheros AR71XX/AR724X/AR913X GPIO API support * + * Copyright (C) 2015 Alban Bedel <albeu@free.fr> * Copyright (C) 2010-2011 Jaiganesh Narayanan <jnarayanan@atheros.com> * Copyright (C) 2008-2011 Gabor Juhos <juhosg@openwrt.org> * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org> * - * Parts of this file are based on Atheros' 2.6.15/2.6.31 BSP - * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. @@ -15,118 +14,204 @@ #include <linux/gpio/driver.h> #include <linux/platform_data/gpio-ath79.h> #include <linux/of_device.h> +#include <linux/interrupt.h> +#include <linux/irq.h> + +#define AR71XX_GPIO_REG_OE 0x00 +#define AR71XX_GPIO_REG_IN 0x04 +#define AR71XX_GPIO_REG_SET 0x0c +#define AR71XX_GPIO_REG_CLEAR 0x10 -#include <asm/mach-ath79/ar71xx_regs.h> +#define AR71XX_GPIO_REG_INT_ENABLE 0x14 +#define AR71XX_GPIO_REG_INT_TYPE 0x18 +#define AR71XX_GPIO_REG_INT_POLARITY 0x1c +#define AR71XX_GPIO_REG_INT_PENDING 0x20 +#define AR71XX_GPIO_REG_INT_MASK 0x24 struct ath79_gpio_ctrl { - struct gpio_chip chip; + struct gpio_chip gc; void __iomem *base; spinlock_t lock; + unsigned long both_edges; }; -static void ath79_gpio_set_value(struct gpio_chip *chip, - unsigned gpio, int value) +static struct ath79_gpio_ctrl *irq_data_to_ath79_gpio(struct irq_data *data) { - struct ath79_gpio_ctrl *ctrl = gpiochip_get_data(chip); + struct gpio_chip *gc = irq_data_get_irq_chip_data(data); + + return container_of(gc, struct ath79_gpio_ctrl, gc); +} + +static u32 ath79_gpio_read(struct ath79_gpio_ctrl *ctrl, unsigned reg) +{ + return readl(ctrl->base + reg); +} - if (value) - __raw_writel(BIT(gpio), ctrl->base + AR71XX_GPIO_REG_SET); - else - __raw_writel(BIT(gpio), ctrl->base + AR71XX_GPIO_REG_CLEAR); +static void ath79_gpio_write(struct ath79_gpio_ctrl *ctrl, + unsigned reg, u32 val) +{ + return writel(val, ctrl->base + reg); } -static int ath79_gpio_get_value(struct gpio_chip *chip, unsigned gpio) +static bool ath79_gpio_update_bits( + struct ath79_gpio_ctrl *ctrl, unsigned reg, u32 mask, u32 bits) { - struct ath79_gpio_ctrl *ctrl = gpiochip_get_data(chip); + u32 old_val, new_val; + + old_val = ath79_gpio_read(ctrl, reg); + new_val = (old_val & ~mask) | (bits & mask); - return (__raw_readl(ctrl->base + AR71XX_GPIO_REG_IN) >> gpio) & 1; + if (new_val != old_val) + ath79_gpio_write(ctrl, reg, new_val); + + return new_val != old_val; } -static int ath79_gpio_direction_input(struct gpio_chip *chip, - unsigned offset) +static void ath79_gpio_irq_unmask(struct irq_data *data) { - struct ath79_gpio_ctrl *ctrl = gpiochip_get_data(chip); + struct ath79_gpio_ctrl *ctrl = irq_data_to_ath79_gpio(data); + u32 mask = BIT(irqd_to_hwirq(data)); unsigned long flags; spin_lock_irqsave(&ctrl->lock, flags); + ath79_gpio_update_bits(ctrl, AR71XX_GPIO_REG_INT_MASK, mask, mask); + spin_unlock_irqrestore(&ctrl->lock, flags); +} - __raw_writel( - __raw_readl(ctrl->base + AR71XX_GPIO_REG_OE) & ~BIT(offset), - ctrl->base + AR71XX_GPIO_REG_OE); +static void ath79_gpio_irq_mask(struct irq_data *data) +{ + struct ath79_gpio_ctrl *ctrl = irq_data_to_ath79_gpio(data); + u32 mask = BIT(irqd_to_hwirq(data)); + unsigned long flags; + spin_lock_irqsave(&ctrl->lock, flags); + ath79_gpio_update_bits(ctrl, AR71XX_GPIO_REG_INT_MASK, mask, 0); spin_unlock_irqrestore(&ctrl->lock, flags); - - return 0; } -static int ath79_gpio_direction_output(struct gpio_chip *chip, - unsigned offset, int value) +static void ath79_gpio_irq_enable(struct irq_data *data) { - struct ath79_gpio_ctrl *ctrl = gpiochip_get_data(chip); + struct ath79_gpio_ctrl *ctrl = irq_data_to_ath79_gpio(data); + u32 mask = BIT(irqd_to_hwirq(data)); unsigned long flags; spin_lock_irqsave(&ctrl->lock, flags); + ath79_gpio_update_bits(ctrl, AR71XX_GPIO_REG_INT_ENABLE, mask, mask); + ath79_gpio_update_bits(ctrl, AR71XX_GPIO_REG_INT_MASK, mask, mask); + spin_unlock_irqrestore(&ctrl->lock, flags); +} - if (value) - __raw_writel(BIT(offset), ctrl->base + AR71XX_GPIO_REG_SET); - else - __raw_writel(BIT(offset), ctrl->base + AR71XX_GPIO_REG_CLEAR); - - __raw_writel( - __raw_readl(ctrl->base + AR71XX_GPIO_REG_OE) | BIT(offset), - ctrl->base + AR71XX_GPIO_REG_OE); +static void ath79_gpio_irq_disable(struct irq_data *data) +{ + struct ath79_gpio_ctrl *ctrl = irq_data_to_ath79_gpio(data); + u32 mask = BIT(irqd_to_hwirq(data)); + unsigned long flags; + spin_lock_irqsave(&ctrl->lock, flags); + ath79_gpio_update_bits(ctrl, AR71XX_GPIO_REG_INT_MASK, mask, 0); + ath79_gpio_update_bits(ctrl, AR71XX_GPIO_REG_INT_ENABLE, mask, 0); spin_unlock_irqrestore(&ctrl->lock, flags); - - return 0; } -static int ar934x_gpio_direction_input(struct gpio_chip *chip, unsigned offset) +static int ath79_gpio_irq_set_type(struct irq_data *data, + unsigned int flow_type) { - struct ath79_gpio_ctrl *ctrl = gpiochip_get_data(chip); + struct ath79_gpio_ctrl *ctrl = irq_data_to_ath79_gpio(data); + u32 mask = BIT(irqd_to_hwirq(data)); + u32 type = 0, polarity = 0; unsigned long flags; + bool disabled; + + switch (flow_type) { + case IRQ_TYPE_EDGE_RISING: + polarity |= mask; + case IRQ_TYPE_EDGE_FALLING: + case IRQ_TYPE_EDGE_BOTH: + break; + + case IRQ_TYPE_LEVEL_HIGH: + polarity |= mask; + case IRQ_TYPE_LEVEL_LOW: + type |= mask; + break; + + default: + return -EINVAL; + } spin_lock_irqsave(&ctrl->lock, flags); - __raw_writel( - __raw_readl(ctrl->base + AR71XX_GPIO_REG_OE) | BIT(offset), - ctrl->base + AR71XX_GPIO_REG_OE); + if (flow_type == IRQ_TYPE_EDGE_BOTH) { + ctrl->both_edges |= mask; + polarity = ~ath79_gpio_read(ctrl, AR71XX_GPIO_REG_IN); + } else { + ctrl->both_edges &= ~mask; + } + + /* As the IRQ configuration can't be loaded atomically we + * have to disable the interrupt while the configuration state + * is invalid. + */ + disabled = ath79_gpio_update_bits( + ctrl, AR71XX_GPIO_REG_INT_ENABLE, mask, 0); + + ath79_gpio_update_bits( + ctrl, AR71XX_GPIO_REG_INT_TYPE, mask, type); + ath79_gpio_update_bits( + ctrl, AR71XX_GPIO_REG_INT_POLARITY, mask, polarity); + + if (disabled) + ath79_gpio_update_bits( + ctrl, AR71XX_GPIO_REG_INT_ENABLE, mask, mask); spin_unlock_irqrestore(&ctrl->lock, flags); return 0; } -static int ar934x_gpio_direction_output(struct gpio_chip *chip, unsigned offset, - int value) +static struct irq_chip ath79_gpio_irqchip = { + .name = "gpio-ath79", + .irq_enable = ath79_gpio_irq_enable, + .irq_disable = ath79_gpio_irq_disable, + .irq_mask = ath79_gpio_irq_mask, + .irq_unmask = ath79_gpio_irq_unmask, + .irq_set_type = ath79_gpio_irq_set_type, +}; + +static void ath79_gpio_irq_handler(struct irq_desc *desc) { - struct ath79_gpio_ctrl *ctrl = gpiochip_get_data(chip); - unsigned long flags; + struct gpio_chip *gc = irq_desc_get_handler_data(desc); + struct irq_chip *irqchip = irq_desc_get_chip(desc); + struct ath79_gpio_ctrl *ctrl = + container_of(gc, struct ath79_gpio_ctrl, gc); + unsigned long flags, pending; + u32 both_edges, state; + int irq; + + chained_irq_enter(irqchip, desc); spin_lock_irqsave(&ctrl->lock, flags); - if (value) - __raw_writel(BIT(offset), ctrl->base + AR71XX_GPIO_REG_SET); - else - __raw_writel(BIT(offset), ctrl->base + AR71XX_GPIO_REG_CLEAR); + pending = ath79_gpio_read(ctrl, AR71XX_GPIO_REG_INT_PENDING); - __raw_writel( - __raw_readl(ctrl->base + AR71XX_GPIO_REG_OE) & ~BIT(offset), - ctrl->base + AR71XX_GPIO_REG_OE); + /* Update the polarity of the both edges irqs */ + both_edges = ctrl->both_edges & pending; + if (both_edges) { + state = ath79_gpio_read(ctrl, AR71XX_GPIO_REG_IN); + ath79_gpio_update_bits(ctrl, AR71XX_GPIO_REG_INT_POLARITY, + both_edges, ~state); + } spin_unlock_irqrestore(&ctrl->lock, flags); - return 0; -} + if (pending) { + for_each_set_bit(irq, &pending, gc->ngpio) + generic_handle_irq( + irq_linear_revmap(gc->irqdomain, irq)); + } -static const struct gpio_chip ath79_gpio_chip = { - .label = "ath79", - .get = ath79_gpio_get_value, - .set = ath79_gpio_set_value, - .direction_input = ath79_gpio_direction_input, - .direction_output = ath79_gpio_direction_output, - .base = 0, -}; + chained_irq_exit(irqchip, desc); +} static const struct of_device_id ath79_gpio_of_match[] = { { .compatible = "qca,ar7100-gpio" }, @@ -147,6 +232,7 @@ static int ath79_gpio_probe(struct platform_device *pdev) ctrl = devm_kzalloc(&pdev->dev, sizeof(*ctrl), GFP_KERNEL); if (!ctrl) return -ENOMEM; + platform_set_drvdata(pdev, ctrl); if (np) { err = of_property_read_u32(np, "ngpios", &ath79_gpio_count); @@ -174,21 +260,53 @@ static int ath79_gpio_probe(struct platform_device *pdev) return -ENOMEM; spin_lock_init(&ctrl->lock); - memcpy(&ctrl->chip, &ath79_gpio_chip, sizeof(ctrl->chip)); - ctrl->chip.parent = &pdev->dev; - ctrl->chip.ngpio = ath79_gpio_count; - if (oe_inverted) { - ctrl->chip.direction_input = ar934x_gpio_direction_input; - ctrl->chip.direction_output = ar934x_gpio_direction_output; + err = bgpio_init(&ctrl->gc, &pdev->dev, 4, + ctrl->base + AR71XX_GPIO_REG_IN, + ctrl->base + AR71XX_GPIO_REG_SET, + ctrl->base + AR71XX_GPIO_REG_CLEAR, + oe_inverted ? NULL : ctrl->base + AR71XX_GPIO_REG_OE, + oe_inverted ? ctrl->base + AR71XX_GPIO_REG_OE : NULL, + 0); + if (err) { + dev_err(&pdev->dev, "bgpio_init failed\n"); + return err; } + /* Use base 0 to stay compatible with legacy platforms */ + ctrl->gc.base = 0; - err = gpiochip_add_data(&ctrl->chip, ctrl); + err = gpiochip_add_data(&ctrl->gc, ctrl); if (err) { dev_err(&pdev->dev, "cannot add AR71xx GPIO chip, error=%d", err); return err; } + if (np && !of_property_read_bool(np, "interrupt-controller")) + return 0; + + err = gpiochip_irqchip_add(&ctrl->gc, &ath79_gpio_irqchip, 0, + handle_simple_irq, IRQ_TYPE_NONE); + if (err) { + dev_err(&pdev->dev, "failed to add gpiochip_irqchip\n"); + goto gpiochip_remove; + } + + gpiochip_set_chained_irqchip(&ctrl->gc, &ath79_gpio_irqchip, + platform_get_irq(pdev, 0), + ath79_gpio_irq_handler); + + return 0; + +gpiochip_remove: + gpiochip_remove(&ctrl->gc); + return err; +} + +static int ath79_gpio_remove(struct platform_device *pdev) +{ + struct ath79_gpio_ctrl *ctrl = platform_get_drvdata(pdev); + + gpiochip_remove(&ctrl->gc); return 0; } @@ -198,6 +316,7 @@ static struct platform_driver ath79_gpio_driver = { .of_match_table = ath79_gpio_of_match, }, .probe = ath79_gpio_probe, + .remove = ath79_gpio_remove, }; module_platform_driver(ath79_gpio_driver); diff --git a/drivers/gpio/gpio-davinci.c b/drivers/gpio/gpio-davinci.c index cd007a67b302..dd262f00295d 100644 --- a/drivers/gpio/gpio-davinci.c +++ b/drivers/gpio/gpio-davinci.c @@ -258,6 +258,8 @@ static int davinci_gpio_probe(struct platform_device *pdev) spin_lock_init(&chips[i].lock); regs = gpio2regs(base); + if (!regs) + return -ENXIO; chips[i].regs = regs; chips[i].set_data = ®s->set_data; chips[i].clr_data = ®s->clr_data; @@ -433,8 +435,7 @@ static struct irq_chip *davinci_gpio_get_irq_chip(unsigned int irq) { static struct irq_chip_type gpio_unbanked; - gpio_unbanked = *container_of(irq_get_chip(irq), - struct irq_chip_type, chip); + gpio_unbanked = *irq_data_get_chip_type(irq_get_irq_data(irq)); return &gpio_unbanked.chip; }; diff --git a/drivers/gpio/gpio-f7188x.c b/drivers/gpio/gpio-f7188x.c index d62fd6bbaf82..0417798c45f1 100644 --- a/drivers/gpio/gpio-f7188x.c +++ b/drivers/gpio/gpio-f7188x.c @@ -1,5 +1,5 @@ /* - * GPIO driver for Fintek Super-I/O F71869, F71869A, F71882 and F71889 + * GPIO driver for Fintek Super-I/O F71869, F71869A, F71882, F71889 and F81866 * * Copyright (C) 2010-2013 LaCie * @@ -36,14 +36,16 @@ #define SIO_F71869A_ID 0x1007 /* F71869A chipset ID */ #define SIO_F71882_ID 0x0541 /* F71882 chipset ID */ #define SIO_F71889_ID 0x0909 /* F71889 chipset ID */ +#define SIO_F81866_ID 0x1010 /* F81866 chipset ID */ -enum chips { f71869, f71869a, f71882fg, f71889f }; +enum chips { f71869, f71869a, f71882fg, f71889f, f81866 }; static const char * const f7188x_names[] = { "f71869", "f71869a", "f71882fg", "f71889f", + "f81866", }; struct f7188x_sio { @@ -190,6 +192,18 @@ static struct f7188x_gpio_bank f71889_gpio_bank[] = { F7188X_GPIO_BANK(70, 8, 0x80), }; +static struct f7188x_gpio_bank f81866_gpio_bank[] = { + F7188X_GPIO_BANK(0, 8, 0xF0), + F7188X_GPIO_BANK(10, 8, 0xE0), + F7188X_GPIO_BANK(20, 8, 0xD0), + F7188X_GPIO_BANK(30, 8, 0xC0), + F7188X_GPIO_BANK(40, 8, 0xB0), + F7188X_GPIO_BANK(50, 8, 0xA0), + F7188X_GPIO_BANK(60, 8, 0x90), + F7188X_GPIO_BANK(70, 8, 0x80), + F7188X_GPIO_BANK(80, 8, 0x88), +}; + static int f7188x_gpio_direction_in(struct gpio_chip *chip, unsigned offset) { int err; @@ -318,6 +332,10 @@ static int f7188x_gpio_probe(struct platform_device *pdev) data->nr_bank = ARRAY_SIZE(f71889_gpio_bank); data->bank = f71889_gpio_bank; break; + case f81866: + data->nr_bank = ARRAY_SIZE(f81866_gpio_bank); + data->bank = f81866_gpio_bank; + break; default: return -ENODEV; } @@ -395,6 +413,9 @@ static int __init f7188x_find(int addr, struct f7188x_sio *sio) case SIO_F71889_ID: sio->type = f71889f; break; + case SIO_F81866_ID: + sio->type = f81866; + break; default: pr_info(DRVNAME ": Unsupported Fintek device 0x%04x\n", devid); goto err; @@ -485,6 +506,6 @@ static void __exit f7188x_gpio_exit(void) } module_exit(f7188x_gpio_exit); -MODULE_DESCRIPTION("GPIO driver for Super-I/O chips F71869, F71869A, F71882FG and F71889F"); +MODULE_DESCRIPTION("GPIO driver for Super-I/O chips F71869, F71869A, F71882FG, F71889F and F81866"); MODULE_AUTHOR("Simon Guinot <simon.guinot@sequanux.org>"); MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/gpio-ich.c b/drivers/gpio/gpio-ich.c index a4893386abbf..4f6d643516b7 100644 --- a/drivers/gpio/gpio-ich.c +++ b/drivers/gpio/gpio-ich.c @@ -20,6 +20,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include <linux/ioport.h> #include <linux/module.h> #include <linux/pci.h> #include <linux/gpio.h> @@ -384,8 +385,8 @@ static struct ichx_desc avoton_desc = { .use_outlvl_cache = true, }; -static int ichx_gpio_request_regions(struct resource *res_base, - const char *name, u8 use_gpio) +static int ichx_gpio_request_regions(struct device *dev, + struct resource *res_base, const char *name, u8 use_gpio) { int i; @@ -395,34 +396,12 @@ static int ichx_gpio_request_regions(struct resource *res_base, for (i = 0; i < ARRAY_SIZE(ichx_priv.desc->regs[0]); i++) { if (!(use_gpio & (1 << i))) continue; - if (!request_region( + if (!devm_request_region(dev, res_base->start + ichx_priv.desc->regs[0][i], ichx_priv.desc->reglen[i], name)) - goto request_err; + return -EBUSY; } return 0; - -request_err: - /* Clean up: release already requested regions, if any */ - for (i--; i >= 0; i--) { - if (!(use_gpio & (1 << i))) - continue; - release_region(res_base->start + ichx_priv.desc->regs[0][i], - ichx_priv.desc->reglen[i]); - } - return -EBUSY; -} - -static void ichx_gpio_release_regions(struct resource *res_base, u8 use_gpio) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(ichx_priv.desc->regs[0]); i++) { - if (!(use_gpio & (1 << i))) - continue; - release_region(res_base->start + ichx_priv.desc->regs[0][i], - ichx_priv.desc->reglen[i]); - } } static int ichx_gpio_probe(struct platform_device *pdev) @@ -468,7 +447,7 @@ static int ichx_gpio_probe(struct platform_device *pdev) spin_lock_init(&ichx_priv.lock); res_base = platform_get_resource(pdev, IORESOURCE_IO, ICH_RES_GPIO); ichx_priv.use_gpio = ich_info->use_gpio; - err = ichx_gpio_request_regions(res_base, pdev->name, + err = ichx_gpio_request_regions(&pdev->dev, res_base, pdev->name, ichx_priv.use_gpio); if (err) return err; @@ -489,8 +468,8 @@ static int ichx_gpio_probe(struct platform_device *pdev) goto init; } - if (!request_region(res_pm->start, resource_size(res_pm), - pdev->name)) { + if (!devm_request_region(&pdev->dev, res_pm->start, + resource_size(res_pm), pdev->name)) { pr_warn("ACPI BAR is busy, GPI 0 - 15 unavailable\n"); goto init; } @@ -502,31 +481,19 @@ init: err = gpiochip_add_data(&ichx_priv.chip, NULL); if (err) { pr_err("Failed to register GPIOs\n"); - goto add_err; + return err; } pr_info("GPIO from %d to %d on %s\n", ichx_priv.chip.base, ichx_priv.chip.base + ichx_priv.chip.ngpio - 1, DRV_NAME); return 0; - -add_err: - ichx_gpio_release_regions(ichx_priv.gpio_base, ichx_priv.use_gpio); - if (ichx_priv.pm_base) - release_region(ichx_priv.pm_base->start, - resource_size(ichx_priv.pm_base)); - return err; } static int ichx_gpio_remove(struct platform_device *pdev) { gpiochip_remove(&ichx_priv.chip); - ichx_gpio_release_regions(ichx_priv.gpio_base, ichx_priv.use_gpio); - if (ichx_priv.pm_base) - release_region(ichx_priv.pm_base->start, - resource_size(ichx_priv.pm_base)); - return 0; } diff --git a/drivers/gpio/gpio-ks8695.c b/drivers/gpio/gpio-ks8695.c index 9f86ed9c753b..179723d02f55 100644 --- a/drivers/gpio/gpio-ks8695.c +++ b/drivers/gpio/gpio-ks8695.c @@ -205,18 +205,6 @@ static int ks8695_gpio_to_irq(struct gpio_chip *gc, unsigned int pin) return gpio_irq[pin]; } -/* - * Map IRQ number to GPIO line. - */ -int irq_to_gpio(unsigned int irq) -{ - if ((irq < KS8695_IRQ_EXTERN0) || (irq > KS8695_IRQ_EXTERN3)) - return -EINVAL; - - return (irq - KS8695_IRQ_EXTERN0); -} -EXPORT_SYMBOL(irq_to_gpio); - /* GPIOLIB interface */ static struct gpio_chip ks8695_gpio_chip = { diff --git a/drivers/gpio/gpio-mcp23s08.c b/drivers/gpio/gpio-mcp23s08.c index c767879e4dd9..f8d4abcff573 100644 --- a/drivers/gpio/gpio-mcp23s08.c +++ b/drivers/gpio/gpio-mcp23s08.c @@ -803,6 +803,8 @@ static int mcp230xx_probe(struct i2c_client *client, pdata = devm_kzalloc(&client->dev, sizeof(struct mcp23s08_platform_data), GFP_KERNEL); + if (!pdata) + return -ENOMEM; pdata->base = -1; } } diff --git a/drivers/gpio/gpio-mpc5200.c b/drivers/gpio/gpio-mpc5200.c index 0e5a6709f27d..fc10cf59691c 100644 --- a/drivers/gpio/gpio-mpc5200.c +++ b/drivers/gpio/gpio-mpc5200.c @@ -25,7 +25,6 @@ #include <linux/of_platform.h> #include <linux/module.h> -#include <asm/gpio.h> #include <asm/mpc52xx.h> #include <sysdev/fsl_soc.h> diff --git a/drivers/gpio/gpio-mpc8xxx.c b/drivers/gpio/gpio-mpc8xxx.c index 9d40787e66c0..ecdb27ad9f1b 100644 --- a/drivers/gpio/gpio-mpc8xxx.c +++ b/drivers/gpio/gpio-mpc8xxx.c @@ -1,7 +1,8 @@ /* - * GPIOs on MPC512x/8349/8572/8610 and compatible + * GPIOs on MPC512x/8349/8572/8610/QorIQ and compatible * * Copyright (C) 2008 Peter Korsgaard <jacmet@sunsite.dk> + * Copyright (C) 2016 Freescale Semiconductor Inc. * * This file is licensed under the terms of the GNU General Public License * version 2. This program is licensed "as is" without any warranty of any @@ -14,11 +15,12 @@ #include <linux/io.h> #include <linux/of.h> #include <linux/of_gpio.h> +#include <linux/of_address.h> #include <linux/of_irq.h> #include <linux/of_platform.h> -#include <linux/gpio.h> #include <linux/slab.h> #include <linux/irq.h> +#include <linux/gpio/driver.h> #define MPC8XXX_GPIO_PINS 32 @@ -31,32 +33,20 @@ #define GPIO_ICR2 0x18 struct mpc8xxx_gpio_chip { - struct of_mm_gpio_chip mm_gc; + struct gpio_chip gc; + void __iomem *regs; raw_spinlock_t lock; - /* - * shadowed data register to be able to clear/set output pins in - * open drain mode safely - */ - u32 data; + unsigned long (*read_reg)(void __iomem *reg); + void (*write_reg)(void __iomem *reg, unsigned long data); + + int (*direction_output)(struct gpio_chip *chip, + unsigned offset, int value); + struct irq_domain *irq; unsigned int irqn; - const void *of_dev_id_data; }; -static inline u32 mpc8xxx_gpio2mask(unsigned int gpio) -{ - return 1u << (MPC8XXX_GPIO_PINS - 1 - gpio); -} - -static void mpc8xxx_gpio_save_regs(struct of_mm_gpio_chip *mm) -{ - struct mpc8xxx_gpio_chip *mpc8xxx_gc = - container_of(mm, struct mpc8xxx_gpio_chip, mm_gc); - - mpc8xxx_gc->data = in_be32(mm->regs + GPIO_DAT); -} - /* Workaround GPIO 1 errata on MPC8572/MPC8536. The status of GPIOs * defined as output cannot be determined by reading GPDAT register, * so we use shadow data register instead. The status of input pins @@ -65,117 +55,36 @@ static void mpc8xxx_gpio_save_regs(struct of_mm_gpio_chip *mm) static int mpc8572_gpio_get(struct gpio_chip *gc, unsigned int gpio) { u32 val; - struct of_mm_gpio_chip *mm = to_of_mm_gpio_chip(gc); struct mpc8xxx_gpio_chip *mpc8xxx_gc = gpiochip_get_data(gc); u32 out_mask, out_shadow; - out_mask = in_be32(mm->regs + GPIO_DIR); - - val = in_be32(mm->regs + GPIO_DAT) & ~out_mask; - out_shadow = mpc8xxx_gc->data & out_mask; + out_mask = mpc8xxx_gc->read_reg(mpc8xxx_gc->regs + GPIO_DIR); + val = mpc8xxx_gc->read_reg(mpc8xxx_gc->regs + GPIO_DAT) & ~out_mask; + out_shadow = gc->bgpio_data & out_mask; - return !!((val | out_shadow) & mpc8xxx_gpio2mask(gpio)); + return !!((val | out_shadow) & gc->pin2mask(gc, gpio)); } -static int mpc8xxx_gpio_get(struct gpio_chip *gc, unsigned int gpio) +static int mpc5121_gpio_dir_out(struct gpio_chip *gc, + unsigned int gpio, int val) { - struct of_mm_gpio_chip *mm = to_of_mm_gpio_chip(gc); - - return in_be32(mm->regs + GPIO_DAT) & mpc8xxx_gpio2mask(gpio); -} - -static void mpc8xxx_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val) -{ - struct of_mm_gpio_chip *mm = to_of_mm_gpio_chip(gc); struct mpc8xxx_gpio_chip *mpc8xxx_gc = gpiochip_get_data(gc); - unsigned long flags; - - raw_spin_lock_irqsave(&mpc8xxx_gc->lock, flags); - - if (val) - mpc8xxx_gc->data |= mpc8xxx_gpio2mask(gpio); - else - mpc8xxx_gc->data &= ~mpc8xxx_gpio2mask(gpio); - - out_be32(mm->regs + GPIO_DAT, mpc8xxx_gc->data); - - raw_spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags); -} - -static void mpc8xxx_gpio_set_multiple(struct gpio_chip *gc, - unsigned long *mask, unsigned long *bits) -{ - struct of_mm_gpio_chip *mm = to_of_mm_gpio_chip(gc); - struct mpc8xxx_gpio_chip *mpc8xxx_gc = gpiochip_get_data(gc); - unsigned long flags; - int i; - - raw_spin_lock_irqsave(&mpc8xxx_gc->lock, flags); - - for (i = 0; i < gc->ngpio; i++) { - if (*mask == 0) - break; - if (__test_and_clear_bit(i, mask)) { - if (test_bit(i, bits)) - mpc8xxx_gc->data |= mpc8xxx_gpio2mask(i); - else - mpc8xxx_gc->data &= ~mpc8xxx_gpio2mask(i); - } - } - - out_be32(mm->regs + GPIO_DAT, mpc8xxx_gc->data); - - raw_spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags); -} - -static int mpc8xxx_gpio_dir_in(struct gpio_chip *gc, unsigned int gpio) -{ - struct of_mm_gpio_chip *mm = to_of_mm_gpio_chip(gc); - struct mpc8xxx_gpio_chip *mpc8xxx_gc = gpiochip_get_data(gc); - unsigned long flags; - - raw_spin_lock_irqsave(&mpc8xxx_gc->lock, flags); - - clrbits32(mm->regs + GPIO_DIR, mpc8xxx_gpio2mask(gpio)); - - raw_spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags); - - return 0; -} - -static int mpc8xxx_gpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) -{ - struct of_mm_gpio_chip *mm = to_of_mm_gpio_chip(gc); - struct mpc8xxx_gpio_chip *mpc8xxx_gc = gpiochip_get_data(gc); - unsigned long flags; - - mpc8xxx_gpio_set(gc, gpio, val); - - raw_spin_lock_irqsave(&mpc8xxx_gc->lock, flags); - - setbits32(mm->regs + GPIO_DIR, mpc8xxx_gpio2mask(gpio)); - - raw_spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags); - - return 0; -} - -static int mpc5121_gpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) -{ /* GPIO 28..31 are input only on MPC5121 */ if (gpio >= 28) return -EINVAL; - return mpc8xxx_gpio_dir_out(gc, gpio, val); + return mpc8xxx_gc->direction_output(gc, gpio, val); } -static int mpc5125_gpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) +static int mpc5125_gpio_dir_out(struct gpio_chip *gc, + unsigned int gpio, int val) { + struct mpc8xxx_gpio_chip *mpc8xxx_gc = gpiochip_get_data(gc); /* GPIO 0..3 are input only on MPC5125 */ if (gpio <= 3) return -EINVAL; - return mpc8xxx_gpio_dir_out(gc, gpio, val); + return mpc8xxx_gc->direction_output(gc, gpio, val); } static int mpc8xxx_gpio_to_irq(struct gpio_chip *gc, unsigned offset) @@ -192,10 +101,10 @@ static void mpc8xxx_gpio_irq_cascade(struct irq_desc *desc) { struct mpc8xxx_gpio_chip *mpc8xxx_gc = irq_desc_get_handler_data(desc); struct irq_chip *chip = irq_desc_get_chip(desc); - struct of_mm_gpio_chip *mm = &mpc8xxx_gc->mm_gc; unsigned int mask; - mask = in_be32(mm->regs + GPIO_IER) & in_be32(mm->regs + GPIO_IMR); + mask = mpc8xxx_gc->read_reg(mpc8xxx_gc->regs + GPIO_IER) + & mpc8xxx_gc->read_reg(mpc8xxx_gc->regs + GPIO_IMR); if (mask) generic_handle_irq(irq_linear_revmap(mpc8xxx_gc->irq, 32 - ffs(mask))); @@ -206,12 +115,14 @@ static void mpc8xxx_gpio_irq_cascade(struct irq_desc *desc) static void mpc8xxx_irq_unmask(struct irq_data *d) { struct mpc8xxx_gpio_chip *mpc8xxx_gc = irq_data_get_irq_chip_data(d); - struct of_mm_gpio_chip *mm = &mpc8xxx_gc->mm_gc; + struct gpio_chip *gc = &mpc8xxx_gc->gc; unsigned long flags; raw_spin_lock_irqsave(&mpc8xxx_gc->lock, flags); - setbits32(mm->regs + GPIO_IMR, mpc8xxx_gpio2mask(irqd_to_hwirq(d))); + mpc8xxx_gc->write_reg(mpc8xxx_gc->regs + GPIO_IMR, + mpc8xxx_gc->read_reg(mpc8xxx_gc->regs + GPIO_IMR) + | gc->pin2mask(gc, irqd_to_hwirq(d))); raw_spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags); } @@ -219,12 +130,14 @@ static void mpc8xxx_irq_unmask(struct irq_data *d) static void mpc8xxx_irq_mask(struct irq_data *d) { struct mpc8xxx_gpio_chip *mpc8xxx_gc = irq_data_get_irq_chip_data(d); - struct of_mm_gpio_chip *mm = &mpc8xxx_gc->mm_gc; + struct gpio_chip *gc = &mpc8xxx_gc->gc; unsigned long flags; raw_spin_lock_irqsave(&mpc8xxx_gc->lock, flags); - clrbits32(mm->regs + GPIO_IMR, mpc8xxx_gpio2mask(irqd_to_hwirq(d))); + mpc8xxx_gc->write_reg(mpc8xxx_gc->regs + GPIO_IMR, + mpc8xxx_gc->read_reg(mpc8xxx_gc->regs + GPIO_IMR) + & ~(gc->pin2mask(gc, irqd_to_hwirq(d)))); raw_spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags); } @@ -232,29 +145,32 @@ static void mpc8xxx_irq_mask(struct irq_data *d) static void mpc8xxx_irq_ack(struct irq_data *d) { struct mpc8xxx_gpio_chip *mpc8xxx_gc = irq_data_get_irq_chip_data(d); - struct of_mm_gpio_chip *mm = &mpc8xxx_gc->mm_gc; + struct gpio_chip *gc = &mpc8xxx_gc->gc; - out_be32(mm->regs + GPIO_IER, mpc8xxx_gpio2mask(irqd_to_hwirq(d))); + mpc8xxx_gc->write_reg(mpc8xxx_gc->regs + GPIO_IER, + gc->pin2mask(gc, irqd_to_hwirq(d))); } static int mpc8xxx_irq_set_type(struct irq_data *d, unsigned int flow_type) { struct mpc8xxx_gpio_chip *mpc8xxx_gc = irq_data_get_irq_chip_data(d); - struct of_mm_gpio_chip *mm = &mpc8xxx_gc->mm_gc; + struct gpio_chip *gc = &mpc8xxx_gc->gc; unsigned long flags; switch (flow_type) { case IRQ_TYPE_EDGE_FALLING: raw_spin_lock_irqsave(&mpc8xxx_gc->lock, flags); - setbits32(mm->regs + GPIO_ICR, - mpc8xxx_gpio2mask(irqd_to_hwirq(d))); + mpc8xxx_gc->write_reg(mpc8xxx_gc->regs + GPIO_ICR, + mpc8xxx_gc->read_reg(mpc8xxx_gc->regs + GPIO_ICR) + | gc->pin2mask(gc, irqd_to_hwirq(d))); raw_spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags); break; case IRQ_TYPE_EDGE_BOTH: raw_spin_lock_irqsave(&mpc8xxx_gc->lock, flags); - clrbits32(mm->regs + GPIO_ICR, - mpc8xxx_gpio2mask(irqd_to_hwirq(d))); + mpc8xxx_gc->write_reg(mpc8xxx_gc->regs + GPIO_ICR, + mpc8xxx_gc->read_reg(mpc8xxx_gc->regs + GPIO_ICR) + & ~(gc->pin2mask(gc, irqd_to_hwirq(d)))); raw_spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags); break; @@ -268,17 +184,16 @@ static int mpc8xxx_irq_set_type(struct irq_data *d, unsigned int flow_type) static int mpc512x_irq_set_type(struct irq_data *d, unsigned int flow_type) { struct mpc8xxx_gpio_chip *mpc8xxx_gc = irq_data_get_irq_chip_data(d); - struct of_mm_gpio_chip *mm = &mpc8xxx_gc->mm_gc; unsigned long gpio = irqd_to_hwirq(d); void __iomem *reg; unsigned int shift; unsigned long flags; if (gpio < 16) { - reg = mm->regs + GPIO_ICR; + reg = mpc8xxx_gc->regs + GPIO_ICR; shift = (15 - gpio) * 2; } else { - reg = mm->regs + GPIO_ICR2; + reg = mpc8xxx_gc->regs + GPIO_ICR2; shift = (15 - (gpio % 16)) * 2; } @@ -286,20 +201,25 @@ static int mpc512x_irq_set_type(struct irq_data *d, unsigned int flow_type) case IRQ_TYPE_EDGE_FALLING: case IRQ_TYPE_LEVEL_LOW: raw_spin_lock_irqsave(&mpc8xxx_gc->lock, flags); - clrsetbits_be32(reg, 3 << shift, 2 << shift); + mpc8xxx_gc->write_reg(reg, + (mpc8xxx_gc->read_reg(reg) & ~(3 << shift)) + | (2 << shift)); raw_spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags); break; case IRQ_TYPE_EDGE_RISING: case IRQ_TYPE_LEVEL_HIGH: raw_spin_lock_irqsave(&mpc8xxx_gc->lock, flags); - clrsetbits_be32(reg, 3 << shift, 1 << shift); + mpc8xxx_gc->write_reg(reg, + (mpc8xxx_gc->read_reg(reg) & ~(3 << shift)) + | (1 << shift)); raw_spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags); break; case IRQ_TYPE_EDGE_BOTH: raw_spin_lock_irqsave(&mpc8xxx_gc->lock, flags); - clrbits32(reg, 3 << shift); + mpc8xxx_gc->write_reg(reg, + (mpc8xxx_gc->read_reg(reg) & ~(3 << shift))); raw_spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags); break; @@ -354,8 +274,6 @@ static const struct mpc8xxx_gpio_devtype mpc8572_gpio_devtype = { }; static const struct mpc8xxx_gpio_devtype mpc8xxx_gpio_devtype_default = { - .gpio_dir_out = mpc8xxx_gpio_dir_out, - .gpio_get = mpc8xxx_gpio_get, .irq_set_type = mpc8xxx_irq_set_type, }; @@ -374,9 +292,7 @@ static int mpc8xxx_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; struct mpc8xxx_gpio_chip *mpc8xxx_gc; - struct of_mm_gpio_chip *mm_gc; - struct gpio_chip *gc; - const struct of_device_id *id; + struct gpio_chip *gc; const struct mpc8xxx_gpio_devtype *devtype = of_device_get_match_data(&pdev->dev); int ret; @@ -389,12 +305,35 @@ static int mpc8xxx_probe(struct platform_device *pdev) raw_spin_lock_init(&mpc8xxx_gc->lock); - mm_gc = &mpc8xxx_gc->mm_gc; - gc = &mm_gc->gc; + mpc8xxx_gc->regs = of_iomap(np, 0); + if (!mpc8xxx_gc->regs) + return -ENOMEM; + + gc = &mpc8xxx_gc->gc; + + if (of_property_read_bool(np, "little-endian")) { + ret = bgpio_init(gc, &pdev->dev, 4, + mpc8xxx_gc->regs + GPIO_DAT, + NULL, NULL, + mpc8xxx_gc->regs + GPIO_DIR, NULL, + BGPIOF_BIG_ENDIAN); + if (ret) + goto err; + dev_dbg(&pdev->dev, "GPIO registers are LITTLE endian\n"); + } else { + ret = bgpio_init(gc, &pdev->dev, 4, + mpc8xxx_gc->regs + GPIO_DAT, + NULL, NULL, + mpc8xxx_gc->regs + GPIO_DIR, NULL, + BGPIOF_BIG_ENDIAN + | BGPIOF_BIG_ENDIAN_BYTE_ORDER); + if (ret) + goto err; + dev_dbg(&pdev->dev, "GPIO registers are BIG endian\n"); + } - mm_gc->save_regs = mpc8xxx_gpio_save_regs; - gc->ngpio = MPC8XXX_GPIO_PINS; - gc->direction_input = mpc8xxx_gpio_dir_in; + mpc8xxx_gc->read_reg = gc->read_reg; + mpc8xxx_gc->write_reg = gc->write_reg; if (!devtype) devtype = &mpc8xxx_gpio_devtype_default; @@ -405,18 +344,21 @@ static int mpc8xxx_probe(struct platform_device *pdev) */ mpc8xxx_irq_chip.irq_set_type = devtype->irq_set_type; - gc->direction_output = devtype->gpio_dir_out ?: mpc8xxx_gpio_dir_out; - gc->get = devtype->gpio_get ?: mpc8xxx_gpio_get; - gc->set = mpc8xxx_gpio_set; - gc->set_multiple = mpc8xxx_gpio_set_multiple; + gc->direction_output = devtype->gpio_dir_out ?: gc->direction_output; + gc->get = devtype->gpio_get ?: gc->get; gc->to_irq = mpc8xxx_gpio_to_irq; - ret = of_mm_gpiochip_add_data(np, mm_gc, mpc8xxx_gc); - if (ret) - return ret; + mpc8xxx_gc->direction_output = gc->direction_output; + + ret = gpiochip_add_data(gc, mpc8xxx_gc); + if (ret) { + pr_err("%s: GPIO chip registration failed with status %d\n", + np->full_name, ret); + goto err; + } mpc8xxx_gc->irqn = irq_of_parse_and_map(np, 0); - if (mpc8xxx_gc->irqn == NO_IRQ) + if (!mpc8xxx_gc->irqn) return 0; mpc8xxx_gc->irq = irq_domain_add_linear(np, MPC8XXX_GPIO_PINS, @@ -424,18 +366,16 @@ static int mpc8xxx_probe(struct platform_device *pdev) if (!mpc8xxx_gc->irq) return 0; - id = of_match_node(mpc8xxx_gpio_ids, np); - if (id) - mpc8xxx_gc->of_dev_id_data = id->data; - /* ack and mask all irqs */ - out_be32(mm_gc->regs + GPIO_IER, 0xffffffff); - out_be32(mm_gc->regs + GPIO_IMR, 0); + mpc8xxx_gc->write_reg(mpc8xxx_gc->regs + GPIO_IER, 0xffffffff); + mpc8xxx_gc->write_reg(mpc8xxx_gc->regs + GPIO_IMR, 0); irq_set_chained_handler_and_data(mpc8xxx_gc->irqn, mpc8xxx_gpio_irq_cascade, mpc8xxx_gc); - return 0; +err: + iounmap(mpc8xxx_gc->regs); + return ret; } static int mpc8xxx_remove(struct platform_device *pdev) @@ -447,7 +387,8 @@ static int mpc8xxx_remove(struct platform_device *pdev) irq_domain_remove(mpc8xxx_gc->irq); } - of_mm_gpiochip_remove(&mpc8xxx_gc->mm_gc); + gpiochip_remove(&mpc8xxx_gc->gc); + iounmap(mpc8xxx_gc->regs); return 0; } diff --git a/drivers/gpio/gpio-pisosr.c b/drivers/gpio/gpio-pisosr.c new file mode 100644 index 000000000000..f9f1074744ab --- /dev/null +++ b/drivers/gpio/gpio-pisosr.c @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/ + * Andrew F. Davis <afd@ti.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether expressed or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License version 2 for more details. + */ + +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/gpio/driver.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/spi/spi.h> + +#define DEFAULT_NGPIO 8 + +/** + * struct pisosr_gpio - GPIO driver data + * @chip: GPIO controller chip + * @spi: SPI device pointer + * @buffer: Buffer for device reads + * @buffer_size: Size of buffer + * @load_gpio: GPIO pin used to load input into device + * @lock: Protects read sequences + */ +struct pisosr_gpio { + struct gpio_chip chip; + struct spi_device *spi; + u8 *buffer; + size_t buffer_size; + struct gpio_desc *load_gpio; + struct mutex lock; +}; + +static int pisosr_gpio_refresh(struct pisosr_gpio *gpio) +{ + int ret; + + mutex_lock(&gpio->lock); + + if (gpio->load_gpio) { + gpiod_set_value(gpio->load_gpio, 1); + udelay(1); /* registers load time (~10ns) */ + gpiod_set_value(gpio->load_gpio, 0); + udelay(1); /* registers recovery time (~5ns) */ + } + + ret = spi_read(gpio->spi, gpio->buffer, gpio->buffer_size); + + mutex_unlock(&gpio->lock); + + return ret; +} + +static int pisosr_gpio_get_direction(struct gpio_chip *chip, + unsigned offset) +{ + /* This device always input */ + return 1; +} + +static int pisosr_gpio_direction_input(struct gpio_chip *chip, + unsigned offset) +{ + /* This device always input */ + return 0; +} + +static int pisosr_gpio_direction_output(struct gpio_chip *chip, + unsigned offset, int value) +{ + /* This device is input only */ + return -EINVAL; +} + +static int pisosr_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + struct pisosr_gpio *gpio = gpiochip_get_data(chip); + + /* Refresh may not always be needed */ + pisosr_gpio_refresh(gpio); + + return (gpio->buffer[offset / 8] >> (offset % 8)) & 0x1; +} + +static struct gpio_chip template_chip = { + .label = "pisosr-gpio", + .owner = THIS_MODULE, + .get_direction = pisosr_gpio_get_direction, + .direction_input = pisosr_gpio_direction_input, + .direction_output = pisosr_gpio_direction_output, + .get = pisosr_gpio_get, + .base = -1, + .ngpio = DEFAULT_NGPIO, + .can_sleep = true, +}; + +static int pisosr_gpio_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + struct pisosr_gpio *gpio; + int ret; + + gpio = devm_kzalloc(dev, sizeof(*gpio), GFP_KERNEL); + if (!gpio) + return -ENOMEM; + + spi_set_drvdata(spi, gpio); + + gpio->chip = template_chip; + gpio->chip.parent = dev; + of_property_read_u16(dev->of_node, "ngpios", &gpio->chip.ngpio); + + gpio->spi = spi; + + gpio->buffer_size = DIV_ROUND_UP(gpio->chip.ngpio, 8); + gpio->buffer = devm_kzalloc(dev, gpio->buffer_size, GFP_KERNEL); + if (!gpio->buffer) + return -ENOMEM; + + gpio->load_gpio = devm_gpiod_get(dev, "load", GPIOD_OUT_LOW); + if (IS_ERR(gpio->load_gpio)) { + ret = PTR_ERR(gpio->load_gpio); + if (ret != -ENOENT && ret != -ENOSYS) { + if (ret != -EPROBE_DEFER) + dev_err(dev, "Unable to allocate load GPIO\n"); + return ret; + } + gpio->load_gpio = NULL; + } + + mutex_init(&gpio->lock); + + ret = gpiochip_add_data(&gpio->chip, gpio); + if (ret < 0) { + dev_err(dev, "Unable to register gpiochip\n"); + return ret; + } + + return 0; +} + +static int pisosr_gpio_remove(struct spi_device *spi) +{ + struct pisosr_gpio *gpio = spi_get_drvdata(spi); + + gpiochip_remove(&gpio->chip); + + mutex_destroy(&gpio->lock); + + return 0; +} + +static const struct spi_device_id pisosr_gpio_id_table[] = { + { "pisosr-gpio", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(spi, pisosr_gpio_id_table); + +static const struct of_device_id pisosr_gpio_of_match_table[] = { + { .compatible = "pisosr-gpio", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, pisosr_gpio_of_match_table); + +static struct spi_driver pisosr_gpio_driver = { + .driver = { + .name = "pisosr-gpio", + .of_match_table = pisosr_gpio_of_match_table, + }, + .probe = pisosr_gpio_probe, + .remove = pisosr_gpio_remove, + .id_table = pisosr_gpio_id_table, +}; +module_spi_driver(pisosr_gpio_driver); + +MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>"); +MODULE_DESCRIPTION("SPI Compatible PISO Shift Register GPIO Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpio/gpio-sch311x.c b/drivers/gpio/gpio-sch311x.c index 1cbd77a04e7b..a03b38ee2e02 100644 --- a/drivers/gpio/gpio-sch311x.c +++ b/drivers/gpio/gpio-sch311x.c @@ -12,6 +12,7 @@ * (at your option) any later version. */ +#include <linux/ioport.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> @@ -228,7 +229,8 @@ static int sch311x_gpio_probe(struct platform_device *pdev) int err, i; /* we can register all GPIO data registers at once */ - if (!request_region(pdata->runtime_reg + GP1, 6, DRV_NAME)) { + if (!devm_request_region(&pdev->dev, pdata->runtime_reg + GP1, 6, + DRV_NAME)) { dev_err(&pdev->dev, "Failed to request region 0x%04x-0x%04x.\n", pdata->runtime_reg + GP1, pdata->runtime_reg + GP1 + 5); return -EBUSY; @@ -273,7 +275,6 @@ static int sch311x_gpio_probe(struct platform_device *pdev) return 0; exit_err: - release_region(pdata->runtime_reg + GP1, 6); /* release already registered chips */ for (--i; i >= 0; i--) gpiochip_remove(&priv->blocks[i].chip); @@ -282,12 +283,9 @@ exit_err: static int sch311x_gpio_remove(struct platform_device *pdev) { - struct sch311x_pdev_data *pdata = dev_get_platdata(&pdev->dev); struct sch311x_gpio_priv *priv = platform_get_drvdata(pdev); int i; - release_region(pdata->runtime_reg + GP1, 6); - for (i = 0; i < ARRAY_SIZE(priv->blocks); i++) { gpiochip_remove(&priv->blocks[i].chip); dev_info(&pdev->dev, diff --git a/drivers/gpio/gpio-tpic2810.c b/drivers/gpio/gpio-tpic2810.c new file mode 100644 index 000000000000..9f020aa4b067 --- /dev/null +++ b/drivers/gpio/gpio-tpic2810.c @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/ + * Andrew F. Davis <afd@ti.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether expressed or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License version 2 for more details. + */ + +#include <linux/gpio/driver.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/mutex.h> + +#define TPIC2810_WS_COMMAND 0x44 + +/** + * struct tpic2810 - GPIO driver data + * @chip: GPIO controller chip + * @client: I2C device pointer + * @buffer: Buffer for device register + * @lock: Protects write sequences + */ +struct tpic2810 { + struct gpio_chip chip; + struct i2c_client *client; + u8 buffer; + struct mutex lock; +}; + +static void tpic2810_set(struct gpio_chip *chip, unsigned offset, int value); + +static int tpic2810_get_direction(struct gpio_chip *chip, + unsigned offset) +{ + /* This device always output */ + return 0; +} + +static int tpic2810_direction_input(struct gpio_chip *chip, + unsigned offset) +{ + /* This device is output only */ + return -EINVAL; +} + +static int tpic2810_direction_output(struct gpio_chip *chip, + unsigned offset, int value) +{ + /* This device always output */ + tpic2810_set(chip, offset, value); + return 0; +} + +static void tpic2810_set(struct gpio_chip *chip, unsigned offset, int value) +{ + struct tpic2810 *gpio = gpiochip_get_data(chip); + + mutex_lock(&gpio->lock); + + if (value) + gpio->buffer |= BIT(offset); + else + gpio->buffer &= ~BIT(offset); + + i2c_smbus_write_byte_data(gpio->client, TPIC2810_WS_COMMAND, + gpio->buffer); + + mutex_unlock(&gpio->lock); +} + +static void tpic2810_set_multiple(struct gpio_chip *chip, unsigned long *mask, + unsigned long *bits) +{ + struct tpic2810 *gpio = gpiochip_get_data(chip); + + mutex_lock(&gpio->lock); + + /* clear bits under mask */ + gpio->buffer &= ~(*mask); + /* set bits under mask */ + gpio->buffer |= ((*mask) & (*bits)); + + i2c_smbus_write_byte_data(gpio->client, TPIC2810_WS_COMMAND, + gpio->buffer); + + mutex_unlock(&gpio->lock); +} + +static struct gpio_chip template_chip = { + .label = "tpic2810", + .owner = THIS_MODULE, + .get_direction = tpic2810_get_direction, + .direction_input = tpic2810_direction_input, + .direction_output = tpic2810_direction_output, + .set = tpic2810_set, + .set_multiple = tpic2810_set_multiple, + .base = -1, + .ngpio = 8, + .can_sleep = true, +}; + +static const struct of_device_id tpic2810_of_match_table[] = { + { .compatible = "ti,tpic2810" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, tpic2810_of_match_table); + +static int tpic2810_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct tpic2810 *gpio; + int ret; + + gpio = devm_kzalloc(&client->dev, sizeof(*gpio), GFP_KERNEL); + if (!gpio) + return -ENOMEM; + + i2c_set_clientdata(client, gpio); + + gpio->chip = template_chip; + gpio->chip.parent = &client->dev; + + gpio->client = client; + + mutex_init(&gpio->lock); + + ret = gpiochip_add_data(&gpio->chip, gpio); + if (ret < 0) { + dev_err(&client->dev, "Unable to register gpiochip\n"); + return ret; + } + + return 0; +} + +static int tpic2810_remove(struct i2c_client *client) +{ + struct tpic2810 *gpio = i2c_get_clientdata(client); + + gpiochip_remove(&gpio->chip); + + return 0; +} + +static const struct i2c_device_id tpic2810_id_table[] = { + { "tpic2810", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(i2c, tpic2810_id_table); + +static struct i2c_driver tpic2810_driver = { + .driver = { + .name = "tpic2810", + .of_match_table = tpic2810_of_match_table, + }, + .probe = tpic2810_probe, + .remove = tpic2810_remove, + .id_table = tpic2810_id_table, +}; +module_i2c_driver(tpic2810_driver); + +MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>"); +MODULE_DESCRIPTION("TPIC2810 8-Bit LED Driver GPIO Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpio/gpio-tps65218.c b/drivers/gpio/gpio-tps65218.c new file mode 100644 index 000000000000..313c0e484607 --- /dev/null +++ b/drivers/gpio/gpio-tps65218.c @@ -0,0 +1,222 @@ +/* + * Copyright 2015 Verifone Int. + * + * Author: Nicolas Saenz Julienne <nicolassaenzj@gmail.com> + * + * This program is free software; you can redistribute it and/or modify i t + * under the terms of the GNU General Public License as published by th e + * Free Software Foundation; either version 2 of the License, or (at you r + * option) any later version. + * + * This driver is based on the gpio-tps65912 implementation. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/gpio/driver.h> +#include <linux/platform_device.h> +#include <linux/mfd/tps65218.h> + +struct tps65218_gpio { + struct tps65218 *tps65218; + struct gpio_chip gpio_chip; +}; + +static int tps65218_gpio_get(struct gpio_chip *gc, unsigned offset) +{ + struct tps65218_gpio *tps65218_gpio = gpiochip_get_data(gc); + struct tps65218 *tps65218 = tps65218_gpio->tps65218; + unsigned int val; + int ret; + + ret = tps65218_reg_read(tps65218, TPS65218_REG_ENABLE2, &val); + if (ret) + return ret; + + return !!(val & (TPS65218_ENABLE2_GPIO1 << offset)); +} + +static void tps65218_gpio_set(struct gpio_chip *gc, unsigned offset, + int value) +{ + struct tps65218_gpio *tps65218_gpio = gpiochip_get_data(gc); + struct tps65218 *tps65218 = tps65218_gpio->tps65218; + + if (value) + tps65218_set_bits(tps65218, TPS65218_REG_ENABLE2, + TPS65218_ENABLE2_GPIO1 << offset, + TPS65218_ENABLE2_GPIO1 << offset, + TPS65218_PROTECT_L1); + else + tps65218_clear_bits(tps65218, TPS65218_REG_ENABLE2, + TPS65218_ENABLE2_GPIO1 << offset, + TPS65218_PROTECT_L1); +} + +static int tps65218_gpio_output(struct gpio_chip *gc, unsigned offset, + int value) +{ + /* Only drives GPOs */ + tps65218_gpio_set(gc, offset, value); + return 0; +} + +static int tps65218_gpio_input(struct gpio_chip *gc, unsigned offset) +{ + return -EPERM; +} + +static int tps65218_gpio_request(struct gpio_chip *gc, unsigned offset) +{ + struct tps65218_gpio *tps65218_gpio = gpiochip_get_data(gc); + struct tps65218 *tps65218 = tps65218_gpio->tps65218; + int ret; + + if (gpiochip_line_is_open_source(gc, offset)) { + dev_err(gc->parent, "can't work as open source\n"); + return -EINVAL; + } + + switch (offset) { + case 0: + if (!gpiochip_line_is_open_drain(gc, offset)) { + dev_err(gc->parent, "GPO1 works only as open drain\n"); + return -EINVAL; + } + + /* Disable sequencer for GPO1 */ + ret = tps65218_clear_bits(tps65218, TPS65218_REG_SEQ7, + TPS65218_SEQ7_GPO1_SEQ_MASK, + TPS65218_PROTECT_L1); + if (ret) + return ret; + + /* Setup GPO1 */ + ret = tps65218_clear_bits(tps65218, TPS65218_REG_CONFIG1, + TPS65218_CONFIG1_IO1_SEL, + TPS65218_PROTECT_L1); + if (ret) + return ret; + + break; + case 1: + /* GP02 is push-pull by default, can be set as open drain. */ + if (gpiochip_line_is_open_drain(gc, offset)) { + ret = tps65218_clear_bits(tps65218, + TPS65218_REG_CONFIG1, + TPS65218_CONFIG1_GPO2_BUF, + TPS65218_PROTECT_L1); + if (ret) + return ret; + } + + /* Setup GPO2 */ + ret = tps65218_clear_bits(tps65218, TPS65218_REG_CONFIG1, + TPS65218_CONFIG1_IO1_SEL, + TPS65218_PROTECT_L1); + if (ret) + return ret; + + break; + + case 2: + if (!gpiochip_line_is_open_drain(gc, offset)) { + dev_err(gc->parent, "GPO3 works only as open drain\n"); + return -EINVAL; + } + + /* Disable sequencer for GPO3 */ + ret = tps65218_clear_bits(tps65218, TPS65218_REG_SEQ7, + TPS65218_SEQ7_GPO3_SEQ_MASK, + TPS65218_PROTECT_L1); + if (ret) + return ret; + + /* Setup GPO3 */ + ret = tps65218_clear_bits(tps65218, TPS65218_REG_CONFIG2, + TPS65218_CONFIG2_DC12_RST, + TPS65218_PROTECT_L1); + if (ret) + return ret; + + break; + default: + return -EINVAL; + } + + return 0; +} + +static struct gpio_chip template_chip = { + .label = "gpio-tps65218", + .owner = THIS_MODULE, + .request = tps65218_gpio_request, + .direction_output = tps65218_gpio_output, + .direction_input = tps65218_gpio_input, + .get = tps65218_gpio_get, + .set = tps65218_gpio_set, + .can_sleep = true, + .ngpio = 3, + .base = -1, +}; + +static int tps65218_gpio_probe(struct platform_device *pdev) +{ + struct tps65218 *tps65218 = dev_get_drvdata(pdev->dev.parent); + struct tps65218_gpio *tps65218_gpio; + int ret; + + tps65218_gpio = devm_kzalloc(&pdev->dev, sizeof(*tps65218_gpio), + GFP_KERNEL); + if (!tps65218_gpio) + return -ENOMEM; + + tps65218_gpio->tps65218 = tps65218; + tps65218_gpio->gpio_chip = template_chip; + tps65218_gpio->gpio_chip.parent = &pdev->dev; +#ifdef CONFIG_OF_GPIO + tps65218_gpio->gpio_chip.of_node = pdev->dev.of_node; +#endif + + ret = gpiochip_add_data(&tps65218_gpio->gpio_chip, tps65218_gpio); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to register gpiochip, %d\n", ret); + return ret; + } + + platform_set_drvdata(pdev, tps65218_gpio); + + return ret; +} + +static int tps65218_gpio_remove(struct platform_device *pdev) +{ + struct tps65218_gpio *tps65218_gpio = platform_get_drvdata(pdev); + + gpiochip_remove(&tps65218_gpio->gpio_chip); + + return 0; +} + +static const struct of_device_id tps65218_dt_match[] = { + { .compatible = "ti,tps65218-gpio" }, + { } +}; +MODULE_DEVICE_TABLE(of, tps65218_dt_match); + +static struct platform_driver tps65218_gpio_driver = { + .driver = { + .name = "tps65218-gpio", + .of_match_table = of_match_ptr(tps65218_dt_match) + }, + .probe = tps65218_gpio_probe, + .remove = tps65218_gpio_remove, +}; + +module_platform_driver(tps65218_gpio_driver); + +MODULE_AUTHOR("Nicolas Saenz Julienne <nicolassaenzj@gmail.com>"); +MODULE_DESCRIPTION("GPO interface for TPS65218 PMICs"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:tps65218-gpio"); diff --git a/drivers/gpio/gpio-ts4800.c b/drivers/gpio/gpio-ts4800.c new file mode 100644 index 000000000000..9da79e4a67c5 --- /dev/null +++ b/drivers/gpio/gpio-ts4800.c @@ -0,0 +1,93 @@ +/* + * GPIO driver for the TS-4800 board + * + * Copyright (c) 2016 - Savoir-faire Linux + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/gpio/driver.h> +#include <linux/of_address.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> + +#define DEFAULT_PIN_NUMBER 16 +#define INPUT_REG_OFFSET 0x00 +#define OUTPUT_REG_OFFSET 0x02 +#define DIRECTION_REG_OFFSET 0x04 + +static int ts4800_gpio_probe(struct platform_device *pdev) +{ + struct device_node *node; + struct gpio_chip *chip; + struct resource *res; + void __iomem *base_addr; + int retval; + u32 ngpios; + + chip = devm_kzalloc(&pdev->dev, sizeof(struct gpio_chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base_addr = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base_addr)) + return PTR_ERR(base_addr); + + node = pdev->dev.of_node; + if (!node) + return -EINVAL; + + retval = of_property_read_u32(node, "ngpios", &ngpios); + if (retval == -EINVAL) + ngpios = DEFAULT_PIN_NUMBER; + else if (retval) + return retval; + + retval = bgpio_init(chip, &pdev->dev, 2, base_addr + INPUT_REG_OFFSET, + base_addr + OUTPUT_REG_OFFSET, NULL, + base_addr + DIRECTION_REG_OFFSET, NULL, 0); + if (retval) { + dev_err(&pdev->dev, "bgpio_init failed\n"); + return retval; + } + + chip->base = -1; + chip->label = dev_name(&pdev->dev); + chip->ngpio = ngpios; + + platform_set_drvdata(pdev, chip); + + return gpiochip_add_data(chip, NULL); +} + +static int ts4800_gpio_remove(struct platform_device *pdev) +{ + struct gpio_chip *chip = platform_get_drvdata(pdev); + + gpiochip_remove(chip); + + return 0; +} + +static const struct of_device_id ts4800_gpio_of_match[] = { + { .compatible = "technologic,ts4800-gpio", }, + {}, +}; + +static struct platform_driver ts4800_gpio_driver = { + .driver = { + .name = "ts4800-gpio", + .of_match_table = ts4800_gpio_of_match, + }, + .probe = ts4800_gpio_probe, + .remove = ts4800_gpio_remove, +}; + +module_platform_driver_probe(ts4800_gpio_driver, ts4800_gpio_probe); + +MODULE_AUTHOR("Julien Grossholtz <julien.grossholtz@savoirfairelinux.com>"); +MODULE_DESCRIPTION("TS4800 FPGA GPIO driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpio/gpio-ws16c48.c b/drivers/gpio/gpio-ws16c48.c new file mode 100644 index 000000000000..51f41e8fd21e --- /dev/null +++ b/drivers/gpio/gpio-ws16c48.c @@ -0,0 +1,427 @@ +/* + * GPIO driver for the WinSystems WS16C48 + * Copyright (C) 2016 William Breathitt Gray + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ +#include <linux/bitops.h> +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/gpio/driver.h> +#include <linux/io.h> +#include <linux/ioport.h> +#include <linux/interrupt.h> +#include <linux/irqdesc.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/platform_device.h> +#include <linux/spinlock.h> + +static unsigned ws16c48_base; +module_param(ws16c48_base, uint, 0); +MODULE_PARM_DESC(ws16c48_base, "WinSystems WS16C48 base address"); +static unsigned ws16c48_irq; +module_param(ws16c48_irq, uint, 0); +MODULE_PARM_DESC(ws16c48_irq, "WinSystems WS16C48 interrupt line number"); + +/** + * struct ws16c48_gpio - GPIO device private data structure + * @chip: instance of the gpio_chip + * @io_state: bit I/O state (whether bit is set to input or output) + * @out_state: output bits state + * @lock: synchronization lock to prevent I/O race conditions + * @irq_mask: I/O bits affected by interrupts + * @flow_mask: IRQ flow type mask for the respective I/O bits + * @base: base port address of the GPIO device + * @irq: Interrupt line number + */ +struct ws16c48_gpio { + struct gpio_chip chip; + unsigned char io_state[6]; + unsigned char out_state[6]; + spinlock_t lock; + unsigned long irq_mask; + unsigned long flow_mask; + unsigned base; + unsigned irq; +}; + +static int ws16c48_gpio_get_direction(struct gpio_chip *chip, unsigned offset) +{ + struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip); + const unsigned port = offset / 8; + const unsigned mask = BIT(offset % 8); + + return !!(ws16c48gpio->io_state[port] & mask); +} + +static int ws16c48_gpio_direction_input(struct gpio_chip *chip, unsigned offset) +{ + struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip); + const unsigned port = offset / 8; + const unsigned mask = BIT(offset % 8); + unsigned long flags; + + spin_lock_irqsave(&ws16c48gpio->lock, flags); + + ws16c48gpio->io_state[port] |= mask; + ws16c48gpio->out_state[port] &= ~mask; + outb(ws16c48gpio->out_state[port], ws16c48gpio->base + port); + + spin_unlock_irqrestore(&ws16c48gpio->lock, flags); + + return 0; +} + +static int ws16c48_gpio_direction_output(struct gpio_chip *chip, + unsigned offset, int value) +{ + struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip); + const unsigned port = offset / 8; + const unsigned mask = BIT(offset % 8); + unsigned long flags; + + spin_lock_irqsave(&ws16c48gpio->lock, flags); + + ws16c48gpio->io_state[port] &= ~mask; + if (value) + ws16c48gpio->out_state[port] |= mask; + else + ws16c48gpio->out_state[port] &= ~mask; + outb(ws16c48gpio->out_state[port], ws16c48gpio->base + port); + + spin_unlock_irqrestore(&ws16c48gpio->lock, flags); + + return 0; +} + +static int ws16c48_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip); + const unsigned port = offset / 8; + const unsigned mask = BIT(offset % 8); + unsigned long flags; + unsigned port_state; + + spin_lock_irqsave(&ws16c48gpio->lock, flags); + + /* ensure that GPIO is set for input */ + if (!(ws16c48gpio->io_state[port] & mask)) { + spin_unlock_irqrestore(&ws16c48gpio->lock, flags); + return -EINVAL; + } + + port_state = inb(ws16c48gpio->base + port); + + spin_unlock_irqrestore(&ws16c48gpio->lock, flags); + + return !!(port_state & mask); +} + +static void ws16c48_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +{ + struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip); + const unsigned port = offset / 8; + const unsigned mask = BIT(offset % 8); + unsigned long flags; + + spin_lock_irqsave(&ws16c48gpio->lock, flags); + + /* ensure that GPIO is set for output */ + if (ws16c48gpio->io_state[port] & mask) { + spin_unlock_irqrestore(&ws16c48gpio->lock, flags); + return; + } + + if (value) + ws16c48gpio->out_state[port] |= mask; + else + ws16c48gpio->out_state[port] &= ~mask; + outb(ws16c48gpio->out_state[port], ws16c48gpio->base + port); + + spin_unlock_irqrestore(&ws16c48gpio->lock, flags); +} + +static void ws16c48_irq_ack(struct irq_data *data) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); + struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip); + const unsigned long offset = irqd_to_hwirq(data); + const unsigned port = offset / 8; + const unsigned mask = BIT(offset % 8); + unsigned long flags; + unsigned port_state; + + /* only the first 3 ports support interrupts */ + if (port > 2) + return; + + spin_lock_irqsave(&ws16c48gpio->lock, flags); + + port_state = ws16c48gpio->irq_mask >> (8*port); + + outb(0x80, ws16c48gpio->base + 7); + outb(port_state & ~mask, ws16c48gpio->base + 8 + port); + outb(port_state | mask, ws16c48gpio->base + 8 + port); + outb(0xC0, ws16c48gpio->base + 7); + + spin_unlock_irqrestore(&ws16c48gpio->lock, flags); +} + +static void ws16c48_irq_mask(struct irq_data *data) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); + struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip); + const unsigned long offset = irqd_to_hwirq(data); + const unsigned long mask = BIT(offset); + const unsigned port = offset / 8; + unsigned long flags; + + /* only the first 3 ports support interrupts */ + if (port > 2) + return; + + spin_lock_irqsave(&ws16c48gpio->lock, flags); + + ws16c48gpio->irq_mask &= ~mask; + + outb(0x80, ws16c48gpio->base + 7); + outb(ws16c48gpio->irq_mask >> (8*port), ws16c48gpio->base + 8 + port); + outb(0xC0, ws16c48gpio->base + 7); + + spin_unlock_irqrestore(&ws16c48gpio->lock, flags); +} + +static void ws16c48_irq_unmask(struct irq_data *data) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); + struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip); + const unsigned long offset = irqd_to_hwirq(data); + const unsigned long mask = BIT(offset); + const unsigned port = offset / 8; + unsigned long flags; + + /* only the first 3 ports support interrupts */ + if (port > 2) + return; + + spin_lock_irqsave(&ws16c48gpio->lock, flags); + + ws16c48gpio->irq_mask |= mask; + + outb(0x80, ws16c48gpio->base + 7); + outb(ws16c48gpio->irq_mask >> (8*port), ws16c48gpio->base + 8 + port); + outb(0xC0, ws16c48gpio->base + 7); + + spin_unlock_irqrestore(&ws16c48gpio->lock, flags); +} + +static int ws16c48_irq_set_type(struct irq_data *data, unsigned flow_type) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); + struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip); + const unsigned long offset = irqd_to_hwirq(data); + const unsigned long mask = BIT(offset); + const unsigned port = offset / 8; + unsigned long flags; + + /* only the first 3 ports support interrupts */ + if (port > 2) + return -EINVAL; + + spin_lock_irqsave(&ws16c48gpio->lock, flags); + + switch (flow_type) { + case IRQ_TYPE_NONE: + break; + case IRQ_TYPE_EDGE_RISING: + ws16c48gpio->flow_mask |= mask; + break; + case IRQ_TYPE_EDGE_FALLING: + ws16c48gpio->flow_mask &= ~mask; + break; + default: + spin_unlock_irqrestore(&ws16c48gpio->lock, flags); + return -EINVAL; + } + + outb(0x40, ws16c48gpio->base + 7); + outb(ws16c48gpio->flow_mask >> (8*port), ws16c48gpio->base + 8 + port); + outb(0xC0, ws16c48gpio->base + 7); + + spin_unlock_irqrestore(&ws16c48gpio->lock, flags); + + return 0; +} + +static struct irq_chip ws16c48_irqchip = { + .name = "ws16c48", + .irq_ack = ws16c48_irq_ack, + .irq_mask = ws16c48_irq_mask, + .irq_unmask = ws16c48_irq_unmask, + .irq_set_type = ws16c48_irq_set_type +}; + +static irqreturn_t ws16c48_irq_handler(int irq, void *dev_id) +{ + struct ws16c48_gpio *const ws16c48gpio = dev_id; + struct gpio_chip *const chip = &ws16c48gpio->chip; + unsigned long int_pending; + unsigned long port; + unsigned long int_id; + unsigned long gpio; + + int_pending = inb(ws16c48gpio->base + 6) & 0x7; + if (!int_pending) + return IRQ_NONE; + + /* loop until all pending interrupts are handled */ + do { + for_each_set_bit(port, &int_pending, 3) { + int_id = inb(ws16c48gpio->base + 8 + port); + for_each_set_bit(gpio, &int_id, 8) + generic_handle_irq(irq_find_mapping( + chip->irqdomain, gpio + 8*port)); + } + + int_pending = inb(ws16c48gpio->base + 6) & 0x7; + } while (int_pending); + + return IRQ_HANDLED; +} + +static int __init ws16c48_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct ws16c48_gpio *ws16c48gpio; + const unsigned base = ws16c48_base; + const unsigned extent = 16; + const char *const name = dev_name(dev); + int err; + const unsigned irq = ws16c48_irq; + + ws16c48gpio = devm_kzalloc(dev, sizeof(*ws16c48gpio), GFP_KERNEL); + if (!ws16c48gpio) + return -ENOMEM; + + if (!devm_request_region(dev, base, extent, name)) { + dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n", + base, base + extent); + return -EBUSY; + } + + ws16c48gpio->chip.label = name; + ws16c48gpio->chip.parent = dev; + ws16c48gpio->chip.owner = THIS_MODULE; + ws16c48gpio->chip.base = -1; + ws16c48gpio->chip.ngpio = 48; + ws16c48gpio->chip.get_direction = ws16c48_gpio_get_direction; + ws16c48gpio->chip.direction_input = ws16c48_gpio_direction_input; + ws16c48gpio->chip.direction_output = ws16c48_gpio_direction_output; + ws16c48gpio->chip.get = ws16c48_gpio_get; + ws16c48gpio->chip.set = ws16c48_gpio_set; + ws16c48gpio->base = base; + ws16c48gpio->irq = irq; + + spin_lock_init(&ws16c48gpio->lock); + + dev_set_drvdata(dev, ws16c48gpio); + + err = gpiochip_add_data(&ws16c48gpio->chip, ws16c48gpio); + if (err) { + dev_err(dev, "GPIO registering failed (%d)\n", err); + return err; + } + + /* Disable IRQ by default */ + outb(0x80, base + 7); + outb(0, base + 8); + outb(0, base + 9); + outb(0, base + 10); + outb(0xC0, base + 7); + + err = gpiochip_irqchip_add(&ws16c48gpio->chip, &ws16c48_irqchip, 0, + handle_edge_irq, IRQ_TYPE_NONE); + if (err) { + dev_err(dev, "Could not add irqchip (%d)\n", err); + goto err_gpiochip_remove; + } + + err = request_irq(irq, ws16c48_irq_handler, IRQF_SHARED, name, + ws16c48gpio); + if (err) { + dev_err(dev, "IRQ handler registering failed (%d)\n", err); + goto err_gpiochip_remove; + } + + return 0; + +err_gpiochip_remove: + gpiochip_remove(&ws16c48gpio->chip); + return err; +} + +static int ws16c48_remove(struct platform_device *pdev) +{ + struct ws16c48_gpio *const ws16c48gpio = platform_get_drvdata(pdev); + + free_irq(ws16c48gpio->irq, ws16c48gpio); + gpiochip_remove(&ws16c48gpio->chip); + + return 0; +} + +static struct platform_device *ws16c48_device; + +static struct platform_driver ws16c48_driver = { + .driver = { + .name = "ws16c48" + }, + .remove = ws16c48_remove +}; + +static void __exit ws16c48_exit(void) +{ + platform_device_unregister(ws16c48_device); + platform_driver_unregister(&ws16c48_driver); +} + +static int __init ws16c48_init(void) +{ + int err; + + ws16c48_device = platform_device_alloc(ws16c48_driver.driver.name, -1); + if (!ws16c48_device) + return -ENOMEM; + + err = platform_device_add(ws16c48_device); + if (err) + goto err_platform_device; + + err = platform_driver_probe(&ws16c48_driver, ws16c48_probe); + if (err) + goto err_platform_driver; + + return 0; + +err_platform_driver: + platform_device_del(ws16c48_device); +err_platform_device: + platform_device_put(ws16c48_device); + return err; +} + +module_init(ws16c48_init); +module_exit(ws16c48_exit); + +MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>"); +MODULE_DESCRIPTION("WinSystems WS16C48 GPIO driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpio/gpio-xgene-sb.c b/drivers/gpio/gpio-xgene-sb.c index 282004deb5d4..b993b86958ed 100644 --- a/drivers/gpio/gpio-xgene-sb.c +++ b/drivers/gpio/gpio-xgene-sb.c @@ -2,8 +2,9 @@ * AppliedMicro X-Gene SoC GPIO-Standby Driver * * Copyright (c) 2014, Applied Micro Circuits Corporation - * Author: Tin Huynh <tnhuynh@apm.com>. - * Y Vo <yvo@apm.com>. + * Author: Tin Huynh <tnhuynh@apm.com>. + * Y Vo <yvo@apm.com>. + * Quan Nguyen <qnguyen@apm.com>. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -28,9 +29,14 @@ #include "gpiolib.h" -#define XGENE_MAX_GPIO_DS 22 -#define XGENE_MAX_GPIO_DS_IRQ 6 +/* Common property names */ +#define XGENE_NIRQ_PROPERTY "apm,nr-irqs" +#define XGENE_NGPIO_PROPERTY "apm,nr-gpios" +#define XGENE_IRQ_START_PROPERTY "apm,irq-start" +#define XGENE_DFLT_MAX_NGPIO 22 +#define XGENE_DFLT_MAX_NIRQ 6 +#define XGENE_DFLT_IRQ_START_PIN 8 #define GPIO_MASK(x) (1U << ((x) % 32)) #define MPA_GPIO_INT_LVL 0x0290 @@ -39,19 +45,32 @@ #define MPA_GPIO_IN_ADDR 0x02a4 #define MPA_GPIO_SEL_LO 0x0294 +#define GPIO_INT_LEVEL_H 0x000001 +#define GPIO_INT_LEVEL_L 0x000000 + /** * struct xgene_gpio_sb - GPIO-Standby private data structure. * @gc: memory-mapped GPIO controllers. - * @irq: Mapping GPIO pins and interrupt number - * nirq: Number of GPIO pins that supports interrupt + * @regs: GPIO register base offset + * @irq_domain: GPIO interrupt domain + * @irq_start: GPIO pin that start support interrupt + * @nirq: Number of GPIO pins that supports interrupt + * @parent_irq_base: Start parent HWIRQ */ struct xgene_gpio_sb { struct gpio_chip gc; - u32 *irq; - u32 nirq; + void __iomem *regs; + struct irq_domain *irq_domain; + u16 irq_start; + u16 nirq; + u16 parent_irq_base; }; -static void xgene_gpio_set_bit(struct gpio_chip *gc, void __iomem *reg, u32 gpio, int val) +#define HWIRQ_TO_GPIO(priv, hwirq) ((hwirq) + (priv)->irq_start) +#define GPIO_TO_HWIRQ(priv, gpio) ((gpio) - (priv)->irq_start) + +static void xgene_gpio_set_bit(struct gpio_chip *gc, + void __iomem *reg, u32 gpio, int val) { u32 data; @@ -63,23 +82,170 @@ static void xgene_gpio_set_bit(struct gpio_chip *gc, void __iomem *reg, u32 gpio gc->write_reg(reg, data); } -static int apm_gpio_sb_to_irq(struct gpio_chip *gc, u32 gpio) +static int xgene_gpio_sb_irq_set_type(struct irq_data *d, unsigned int type) +{ + struct xgene_gpio_sb *priv = irq_data_get_irq_chip_data(d); + int gpio = HWIRQ_TO_GPIO(priv, d->hwirq); + int lvl_type = GPIO_INT_LEVEL_H; + + switch (type & IRQ_TYPE_SENSE_MASK) { + case IRQ_TYPE_EDGE_RISING: + case IRQ_TYPE_LEVEL_HIGH: + lvl_type = GPIO_INT_LEVEL_H; + break; + case IRQ_TYPE_EDGE_FALLING: + case IRQ_TYPE_LEVEL_LOW: + lvl_type = GPIO_INT_LEVEL_L; + break; + default: + break; + } + + xgene_gpio_set_bit(&priv->gc, priv->regs + MPA_GPIO_SEL_LO, + gpio * 2, 1); + xgene_gpio_set_bit(&priv->gc, priv->regs + MPA_GPIO_INT_LVL, + d->hwirq, lvl_type); + + /* Propagate IRQ type setting to parent */ + if (type & IRQ_TYPE_EDGE_BOTH) + return irq_chip_set_type_parent(d, IRQ_TYPE_EDGE_RISING); + else + return irq_chip_set_type_parent(d, IRQ_TYPE_LEVEL_HIGH); +} + +static struct irq_chip xgene_gpio_sb_irq_chip = { + .name = "sbgpio", + .irq_eoi = irq_chip_eoi_parent, + .irq_mask = irq_chip_mask_parent, + .irq_unmask = irq_chip_unmask_parent, + .irq_set_type = xgene_gpio_sb_irq_set_type, +}; + +static int xgene_gpio_sb_to_irq(struct gpio_chip *gc, u32 gpio) { struct xgene_gpio_sb *priv = gpiochip_get_data(gc); + struct irq_fwspec fwspec; + + if ((gpio < priv->irq_start) || + (gpio > HWIRQ_TO_GPIO(priv, priv->nirq))) + return -ENXIO; + + if (gc->parent->of_node) + fwspec.fwnode = of_node_to_fwnode(gc->parent->of_node); + else + fwspec.fwnode = gc->parent->fwnode; + fwspec.param_count = 2; + fwspec.param[0] = GPIO_TO_HWIRQ(priv, gpio); + fwspec.param[1] = IRQ_TYPE_NONE; + return irq_create_fwspec_mapping(&fwspec); +} + +static void xgene_gpio_sb_domain_activate(struct irq_domain *d, + struct irq_data *irq_data) +{ + struct xgene_gpio_sb *priv = d->host_data; + u32 gpio = HWIRQ_TO_GPIO(priv, irq_data->hwirq); + + if (gpiochip_lock_as_irq(&priv->gc, gpio)) { + dev_err(priv->gc.parent, + "Unable to configure XGene GPIO standby pin %d as IRQ\n", + gpio); + return; + } + + xgene_gpio_set_bit(&priv->gc, priv->regs + MPA_GPIO_SEL_LO, + gpio * 2, 1); +} + +static void xgene_gpio_sb_domain_deactivate(struct irq_domain *d, + struct irq_data *irq_data) +{ + struct xgene_gpio_sb *priv = d->host_data; + u32 gpio = HWIRQ_TO_GPIO(priv, irq_data->hwirq); + + gpiochip_unlock_as_irq(&priv->gc, gpio); + xgene_gpio_set_bit(&priv->gc, priv->regs + MPA_GPIO_SEL_LO, + gpio * 2, 0); +} + +static int xgene_gpio_sb_domain_translate(struct irq_domain *d, + struct irq_fwspec *fwspec, + unsigned long *hwirq, + unsigned int *type) +{ + struct xgene_gpio_sb *priv = d->host_data; - if (priv->irq[gpio]) - return priv->irq[gpio]; + if ((fwspec->param_count != 2) || + (fwspec->param[0] >= priv->nirq)) + return -EINVAL; + *hwirq = fwspec->param[0]; + *type = fwspec->param[1]; + return 0; +} + +static int xgene_gpio_sb_domain_alloc(struct irq_domain *domain, + unsigned int virq, + unsigned int nr_irqs, void *data) +{ + struct irq_fwspec *fwspec = data; + struct irq_fwspec parent_fwspec; + struct xgene_gpio_sb *priv = domain->host_data; + irq_hw_number_t hwirq; + unsigned int i; + + hwirq = fwspec->param[0]; + for (i = 0; i < nr_irqs; i++) + irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i, + &xgene_gpio_sb_irq_chip, priv); + + parent_fwspec.fwnode = domain->parent->fwnode; + if (is_of_node(parent_fwspec.fwnode)) { + parent_fwspec.param_count = 3; + parent_fwspec.param[0] = 0;/* SPI */ + /* Skip SGIs and PPIs*/ + parent_fwspec.param[1] = hwirq + priv->parent_irq_base - 32; + parent_fwspec.param[2] = fwspec->param[1]; + } else if (is_fwnode_irqchip(parent_fwspec.fwnode)) { + parent_fwspec.param_count = 2; + parent_fwspec.param[0] = hwirq + priv->parent_irq_base; + parent_fwspec.param[1] = fwspec->param[1]; + } else + return -EINVAL; + + return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, + &parent_fwspec); +} + +static void xgene_gpio_sb_domain_free(struct irq_domain *domain, + unsigned int virq, + unsigned int nr_irqs) +{ + struct irq_data *d; + unsigned int i; - return -ENXIO; + for (i = 0; i < nr_irqs; i++) { + d = irq_domain_get_irq_data(domain, virq + i); + irq_domain_reset_irq_data(d); + } } +static const struct irq_domain_ops xgene_gpio_sb_domain_ops = { + .translate = xgene_gpio_sb_domain_translate, + .alloc = xgene_gpio_sb_domain_alloc, + .free = xgene_gpio_sb_domain_free, + .activate = xgene_gpio_sb_domain_activate, + .deactivate = xgene_gpio_sb_domain_deactivate, +}; + static int xgene_gpio_sb_probe(struct platform_device *pdev) { struct xgene_gpio_sb *priv; - u32 ret, i; - u32 default_lines[] = {0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D}; + u32 ret; struct resource *res; void __iomem *regs; + struct irq_domain *parent_domain = NULL; + struct fwnode_handle *fwnode; + u32 val32; priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) @@ -90,6 +256,18 @@ static int xgene_gpio_sb_probe(struct platform_device *pdev) if (IS_ERR(regs)) return PTR_ERR(regs); + priv->regs = regs; + + ret = platform_get_irq(pdev, 0); + if (ret > 0) { + priv->parent_irq_base = irq_get_irq_data(ret)->hwirq; + parent_domain = irq_get_irq_data(ret)->domain; + } + if (!parent_domain) { + dev_err(&pdev->dev, "unable to obtain parent domain\n"); + return -ENODEV; + } + ret = bgpio_init(&priv->gc, &pdev->dev, 4, regs + MPA_GPIO_IN_ADDR, regs + MPA_GPIO_OUT_ADDR, NULL, @@ -97,30 +275,51 @@ static int xgene_gpio_sb_probe(struct platform_device *pdev) if (ret) return ret; - priv->gc.to_irq = apm_gpio_sb_to_irq; - priv->gc.ngpio = XGENE_MAX_GPIO_DS; + priv->gc.to_irq = xgene_gpio_sb_to_irq; - priv->nirq = XGENE_MAX_GPIO_DS_IRQ; + /* Retrieve start irq pin, use default if property not found */ + priv->irq_start = XGENE_DFLT_IRQ_START_PIN; + if (!device_property_read_u32(&pdev->dev, + XGENE_IRQ_START_PROPERTY, &val32)) + priv->irq_start = val32; - priv->irq = devm_kzalloc(&pdev->dev, sizeof(u32) * XGENE_MAX_GPIO_DS, - GFP_KERNEL); - if (!priv->irq) - return -ENOMEM; + /* Retrieve number irqs, use default if property not found */ + priv->nirq = XGENE_DFLT_MAX_NIRQ; + if (!device_property_read_u32(&pdev->dev, XGENE_NIRQ_PROPERTY, &val32)) + priv->nirq = val32; - for (i = 0; i < priv->nirq; i++) { - priv->irq[default_lines[i]] = platform_get_irq(pdev, i); - xgene_gpio_set_bit(&priv->gc, regs + MPA_GPIO_SEL_LO, - default_lines[i] * 2, 1); - xgene_gpio_set_bit(&priv->gc, regs + MPA_GPIO_INT_LVL, i, 1); - } + /* Retrieve number gpio, use default if property not found */ + priv->gc.ngpio = XGENE_DFLT_MAX_NGPIO; + if (!device_property_read_u32(&pdev->dev, XGENE_NGPIO_PROPERTY, &val32)) + priv->gc.ngpio = val32; + + dev_info(&pdev->dev, "Support %d gpios, %d irqs start from pin %d\n", + priv->gc.ngpio, priv->nirq, priv->irq_start); platform_set_drvdata(pdev, priv); - ret = gpiochip_add_data(&priv->gc, priv); - if (ret) - dev_err(&pdev->dev, "failed to register X-Gene GPIO Standby driver\n"); + if (pdev->dev.of_node) + fwnode = of_node_to_fwnode(pdev->dev.of_node); else - dev_info(&pdev->dev, "X-Gene GPIO Standby driver registered\n"); + fwnode = pdev->dev.fwnode; + + priv->irq_domain = irq_domain_create_hierarchy(parent_domain, + 0, priv->nirq, fwnode, + &xgene_gpio_sb_domain_ops, priv); + if (!priv->irq_domain) + return -ENODEV; + + priv->gc.irqdomain = priv->irq_domain; + + ret = gpiochip_add_data(&priv->gc, priv); + if (ret) { + dev_err(&pdev->dev, + "failed to register X-Gene GPIO Standby driver\n"); + irq_domain_remove(priv->irq_domain); + return ret; + } + + dev_info(&pdev->dev, "X-Gene GPIO Standby driver registered\n"); if (priv->nirq > 0) { /* Register interrupt handlers for gpio signaled acpi events */ @@ -138,6 +337,8 @@ static int xgene_gpio_sb_remove(struct platform_device *pdev) acpi_gpiochip_free_interrupts(&priv->gc); } + irq_domain_remove(priv->irq_domain); + gpiochip_remove(&priv->gc); return 0; } diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index 540cbc88c7a2..682070d20f00 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -71,29 +71,29 @@ static int acpi_gpiochip_find(struct gpio_chip *gc, void *data) * controller uses pin controller and the mapping is not contiguous the * offset might be different. */ -static int acpi_gpiochip_pin_to_gpio_offset(struct gpio_chip *chip, int pin) +static int acpi_gpiochip_pin_to_gpio_offset(struct gpio_device *gdev, int pin) { struct gpio_pin_range *pin_range; /* If there are no ranges in this chip, use 1:1 mapping */ - if (list_empty(&chip->pin_ranges)) + if (list_empty(&gdev->pin_ranges)) return pin; - list_for_each_entry(pin_range, &chip->pin_ranges, node) { + list_for_each_entry(pin_range, &gdev->pin_ranges, node) { const struct pinctrl_gpio_range *range = &pin_range->range; int i; if (range->pins) { for (i = 0; i < range->npins; i++) { if (range->pins[i] == pin) - return range->base + i - chip->base; + return range->base + i - gdev->base; } } else { if (pin >= range->pin_base && pin < range->pin_base + range->npins) { unsigned gpio_base; - gpio_base = range->base - chip->base; + gpio_base = range->base - gdev->base; return gpio_base + pin - range->pin_base; } } @@ -102,7 +102,7 @@ static int acpi_gpiochip_pin_to_gpio_offset(struct gpio_chip *chip, int pin) return -EINVAL; } #else -static inline int acpi_gpiochip_pin_to_gpio_offset(struct gpio_chip *chip, +static inline int acpi_gpiochip_pin_to_gpio_offset(struct gpio_device *gdev, int pin) { return pin; @@ -134,7 +134,7 @@ static struct gpio_desc *acpi_get_gpiod(char *path, int pin) if (!chip) return ERR_PTR(-EPROBE_DEFER); - offset = acpi_gpiochip_pin_to_gpio_offset(chip, pin); + offset = acpi_gpiochip_pin_to_gpio_offset(chip->gpiodev, pin); if (offset < 0) return ERR_PTR(offset); @@ -202,7 +202,7 @@ static acpi_status acpi_gpiochip_request_interrupt(struct acpi_resource *ares, if (!handler) return AE_BAD_PARAMETER; - pin = acpi_gpiochip_pin_to_gpio_offset(chip, pin); + pin = acpi_gpiochip_pin_to_gpio_offset(chip->gpiodev, pin); if (pin < 0) return AE_BAD_PARAMETER; @@ -673,7 +673,7 @@ acpi_gpio_adr_space_handler(u32 function, acpi_physical_address address, struct gpio_desc *desc; bool found; - pin = acpi_gpiochip_pin_to_gpio_offset(chip, pin); + pin = acpi_gpiochip_pin_to_gpio_offset(chip->gpiodev, pin); if (pin < 0) { status = AE_BAD_PARAMETER; goto out; diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index 405dfcaadc4c..c56309491e8b 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -180,7 +180,7 @@ static int gpio_sysfs_request_irq(struct device *dev, unsigned char flags) * Remove this redundant call (along with the corresponding * unlock) when those drivers have been fixed. */ - ret = gpiochip_lock_as_irq(desc->chip, gpio_chip_hwgpio(desc)); + ret = gpiochip_lock_as_irq(desc->gdev->chip, gpio_chip_hwgpio(desc)); if (ret < 0) goto err_put_kn; @@ -194,7 +194,7 @@ static int gpio_sysfs_request_irq(struct device *dev, unsigned char flags) return 0; err_unlock: - gpiochip_unlock_as_irq(desc->chip, gpio_chip_hwgpio(desc)); + gpiochip_unlock_as_irq(desc->gdev->chip, gpio_chip_hwgpio(desc)); err_put_kn: sysfs_put(data->value_kn); @@ -212,7 +212,7 @@ static void gpio_sysfs_free_irq(struct device *dev) data->irq_flags = 0; free_irq(data->irq, data); - gpiochip_unlock_as_irq(desc->chip, gpio_chip_hwgpio(desc)); + gpiochip_unlock_as_irq(desc->gdev->chip, gpio_chip_hwgpio(desc)); sysfs_put(data->value_kn); } @@ -547,6 +547,7 @@ static struct class gpio_class = { int gpiod_export(struct gpio_desc *desc, bool direction_may_change) { struct gpio_chip *chip; + struct gpio_device *gdev; struct gpiod_data *data; unsigned long flags; int status; @@ -565,12 +566,13 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change) return -EINVAL; } - chip = desc->chip; + gdev = desc->gdev; + chip = gdev->chip; mutex_lock(&sysfs_lock); /* check if chip is being removed */ - if (!chip || !chip->cdev) { + if (!chip || !gdev->mockdev) { status = -ENODEV; goto err_unlock; } @@ -605,7 +607,7 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change) if (chip->names && chip->names[offset]) ioname = chip->names[offset]; - dev = device_create_with_groups(&gpio_class, chip->parent, + dev = device_create_with_groups(&gpio_class, &gdev->dev, MKDEV(0, 0), data, gpio_groups, ioname ? ioname : "gpio%u", desc_to_gpio(desc)); @@ -716,9 +718,10 @@ err_unlock: } EXPORT_SYMBOL_GPL(gpiod_unexport); -int gpiochip_sysfs_register(struct gpio_chip *chip) +int gpiochip_sysfs_register(struct gpio_device *gdev) { struct device *dev; + struct gpio_chip *chip = gdev->chip; /* * Many systems add gpio chips for SOC support very early, @@ -730,7 +733,7 @@ int gpiochip_sysfs_register(struct gpio_chip *chip) return 0; /* use chip->base for the ID; it's already known to be unique */ - dev = device_create_with_groups(&gpio_class, chip->parent, + dev = device_create_with_groups(&gpio_class, &gdev->dev, MKDEV(0, 0), chip, gpiochip_groups, "gpiochip%d", chip->base); @@ -738,30 +741,31 @@ int gpiochip_sysfs_register(struct gpio_chip *chip) return PTR_ERR(dev); mutex_lock(&sysfs_lock); - chip->cdev = dev; + gdev->mockdev = dev; mutex_unlock(&sysfs_lock); return 0; } -void gpiochip_sysfs_unregister(struct gpio_chip *chip) +void gpiochip_sysfs_unregister(struct gpio_device *gdev) { struct gpio_desc *desc; + struct gpio_chip *chip = gdev->chip; unsigned int i; - if (!chip->cdev) + if (!gdev->mockdev) return; - device_unregister(chip->cdev); + device_unregister(gdev->mockdev); /* prevent further gpiod exports */ mutex_lock(&sysfs_lock); - chip->cdev = NULL; + gdev->mockdev = NULL; mutex_unlock(&sysfs_lock); /* unregister gpiod class devices owned by sysfs */ for (i = 0; i < chip->ngpio; i++) { - desc = &chip->desc[i]; + desc = &gdev->descs[i]; if (test_and_clear_bit(FLAG_SYSFS, &desc->flags)) gpiod_free(desc); } @@ -771,7 +775,7 @@ static int __init gpiolib_sysfs_init(void) { int status; unsigned long flags; - struct gpio_chip *chip; + struct gpio_device *gdev; status = class_register(&gpio_class); if (status < 0) @@ -784,8 +788,8 @@ static int __init gpiolib_sysfs_init(void) * registered, and so arch_initcall() can always gpio_export(). */ spin_lock_irqsave(&gpio_lock, flags); - list_for_each_entry(chip, &gpio_chips, list) { - if (chip->cdev) + list_for_each_entry(gdev, &gpio_devices, list) { + if (gdev->mockdev) continue; /* @@ -798,12 +802,11 @@ static int __init gpiolib_sysfs_init(void) * gpio_lock prevents us from doing this. */ spin_unlock_irqrestore(&gpio_lock, flags); - status = gpiochip_sysfs_register(chip); + status = gpiochip_sysfs_register(gdev); spin_lock_irqsave(&gpio_lock, flags); } spin_unlock_irqrestore(&gpio_lock, flags); - return status; } postcore_initcall(gpiolib_sysfs_init); diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 5c1ba879f889..872774a404f1 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -16,6 +16,11 @@ #include <linux/gpio/driver.h> #include <linux/gpio/machine.h> #include <linux/pinctrl/consumer.h> +#include <linux/idr.h> +#include <linux/cdev.h> +#include <linux/fs.h> +#include <linux/uaccess.h> +#include <uapi/linux/gpio.h> #include "gpiolib.h" @@ -42,6 +47,14 @@ #define extra_checks 0 #endif +/* Device and char device-related information */ +static DEFINE_IDA(gpio_ida); +static dev_t gpio_devt; +#define GPIO_DEV_MAX 256 /* 256 GPIO chip devices supported */ +static struct bus_type gpio_bus_type = { + .name = "gpio", +}; + /* gpio_lock prevents conflicts during gpio_desc[] table updates. * While any GPIO is requested, its gpio_chip is not removable; * each GPIO's "requested" flag serves as a lock and refcount. @@ -50,8 +63,7 @@ DEFINE_SPINLOCK(gpio_lock); static DEFINE_MUTEX(gpio_lookup_lock); static LIST_HEAD(gpio_lookup_list); -LIST_HEAD(gpio_chips); - +LIST_HEAD(gpio_devices); static void gpiochip_free_hogs(struct gpio_chip *chip); static void gpiochip_irqchip_remove(struct gpio_chip *gpiochip); @@ -67,15 +79,16 @@ static inline void desc_set_label(struct gpio_desc *d, const char *label) */ struct gpio_desc *gpio_to_desc(unsigned gpio) { - struct gpio_chip *chip; + struct gpio_device *gdev; unsigned long flags; spin_lock_irqsave(&gpio_lock, flags); - list_for_each_entry(chip, &gpio_chips, list) { - if (chip->base <= gpio && chip->base + chip->ngpio > gpio) { + list_for_each_entry(gdev, &gpio_devices, list) { + if (gdev->base <= gpio && + gdev->base + gdev->ngpio > gpio) { spin_unlock_irqrestore(&gpio_lock, flags); - return &chip->desc[gpio - chip->base]; + return &gdev->descs[gpio - gdev->base]; } } @@ -94,10 +107,12 @@ EXPORT_SYMBOL_GPL(gpio_to_desc); struct gpio_desc *gpiochip_get_desc(struct gpio_chip *chip, u16 hwnum) { - if (hwnum >= chip->ngpio) + struct gpio_device *gdev = chip->gpiodev; + + if (hwnum >= gdev->ngpio) return ERR_PTR(-EINVAL); - return &chip->desc[hwnum]; + return &gdev->descs[hwnum]; } /** @@ -107,7 +122,7 @@ struct gpio_desc *gpiochip_get_desc(struct gpio_chip *chip, */ int desc_to_gpio(const struct gpio_desc *desc) { - return desc->chip->base + (desc - &desc->chip->desc[0]); + return desc->gdev->base + (desc - &desc->gdev->descs[0]); } EXPORT_SYMBOL_GPL(desc_to_gpio); @@ -118,23 +133,25 @@ EXPORT_SYMBOL_GPL(desc_to_gpio); */ struct gpio_chip *gpiod_to_chip(const struct gpio_desc *desc) { - return desc ? desc->chip : NULL; + if (!desc || !desc->gdev || !desc->gdev->chip) + return NULL; + return desc->gdev->chip; } EXPORT_SYMBOL_GPL(gpiod_to_chip); /* dynamic allocation of GPIOs, e.g. on a hotplugged device */ static int gpiochip_find_base(int ngpio) { - struct gpio_chip *chip; + struct gpio_device *gdev; int base = ARCH_NR_GPIOS - ngpio; - list_for_each_entry_reverse(chip, &gpio_chips, list) { + list_for_each_entry_reverse(gdev, &gpio_devices, list) { /* found a free space? */ - if (chip->base + chip->ngpio <= base) + if (gdev->base + gdev->ngpio <= base) break; else /* nope, check the space right before the chip */ - base = chip->base - ngpio; + base = gdev->base - ngpio; } if (gpio_is_valid(base)) { @@ -187,18 +204,21 @@ EXPORT_SYMBOL_GPL(gpiod_get_direction); * Return -EBUSY if the new chip overlaps with some other chip's integer * space. */ -static int gpiochip_add_to_list(struct gpio_chip *chip) +static int gpiodev_add_to_list(struct gpio_device *gdev) { - struct gpio_chip *iterator; - struct gpio_chip *previous = NULL; + struct gpio_device *iterator; + struct gpio_device *previous = NULL; - if (list_empty(&gpio_chips)) { - list_add_tail(&chip->list, &gpio_chips); + if (!gdev->chip) + return -EINVAL; + + if (list_empty(&gpio_devices)) { + list_add_tail(&gdev->list, &gpio_devices); return 0; } - list_for_each_entry(iterator, &gpio_chips, list) { - if (iterator->base >= chip->base + chip->ngpio) { + list_for_each_entry(iterator, &gpio_devices, list) { + if (iterator->base >= gdev->base + gdev->ngpio) { /* * Iterator is the first GPIO chip so there is no * previous one @@ -212,7 +232,7 @@ static int gpiochip_add_to_list(struct gpio_chip *chip) * and iterator chip. */ if (previous->base + previous->ngpio - <= chip->base) + <= gdev->base) goto found; } } @@ -225,18 +245,18 @@ static int gpiochip_add_to_list(struct gpio_chip *chip) * Let iterator point to the last chip in the list. */ - iterator = list_last_entry(&gpio_chips, struct gpio_chip, list); - if (iterator->base + iterator->ngpio <= chip->base) { - list_add(&chip->list, &iterator->list); + iterator = list_last_entry(&gpio_devices, struct gpio_device, list); + if (iterator->base + iterator->ngpio <= gdev->base) { + list_add(&gdev->list, &iterator->list); return 0; } - dev_err(chip->parent, + dev_err(&gdev->dev, "GPIO integer space overlap, cannot add chip\n"); return -EBUSY; found: - list_add_tail(&chip->list, &iterator->list); + list_add_tail(&gdev->list, &iterator->list); return 0; } @@ -245,23 +265,23 @@ found: */ static struct gpio_desc *gpio_name_to_desc(const char * const name) { - struct gpio_chip *chip; + struct gpio_device *gdev; unsigned long flags; spin_lock_irqsave(&gpio_lock, flags); - list_for_each_entry(chip, &gpio_chips, list) { + list_for_each_entry(gdev, &gpio_devices, list) { int i; - for (i = 0; i != chip->ngpio; ++i) { - struct gpio_desc *gpio = &chip->desc[i]; + for (i = 0; i != gdev->ngpio; ++i) { + struct gpio_desc *desc = &gdev->descs[i]; - if (!gpio->name || !name) + if (!desc->name || !name) continue; - if (!strcmp(gpio->name, name)) { + if (!strcmp(desc->name, name)) { spin_unlock_irqrestore(&gpio_lock, flags); - return gpio; + return desc; } } } @@ -279,6 +299,7 @@ static struct gpio_desc *gpio_name_to_desc(const char * const name) */ static int gpiochip_set_desc_names(struct gpio_chip *gc) { + struct gpio_device *gdev = gc->gpiodev; int i; if (!gc->names) @@ -290,19 +311,154 @@ static int gpiochip_set_desc_names(struct gpio_chip *gc) gpio = gpio_name_to_desc(gc->names[i]); if (gpio) - dev_warn(gc->parent, "Detected name collision for " - "GPIO name '%s'\n", + dev_warn(&gdev->dev, + "Detected name collision for GPIO name '%s'\n", gc->names[i]); } /* Then add all names to the GPIO descriptors */ for (i = 0; i != gc->ngpio; ++i) - gc->desc[i].name = gc->names[i]; + gdev->descs[i].name = gc->names[i]; + + return 0; +} + +/** + * gpio_ioctl() - ioctl handler for the GPIO chardev + */ +static long gpio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + struct gpio_device *gdev = filp->private_data; + struct gpio_chip *chip = gdev->chip; + int __user *ip = (int __user *)arg; + + /* We fail any subsequent ioctl():s when the chip is gone */ + if (!chip) + return -ENODEV; + /* Fill in the struct and pass to userspace */ + if (cmd == GPIO_GET_CHIPINFO_IOCTL) { + struct gpiochip_info chipinfo; + + strncpy(chipinfo.name, dev_name(&gdev->dev), + sizeof(chipinfo.name)); + chipinfo.name[sizeof(chipinfo.name)-1] = '\0'; + strncpy(chipinfo.label, gdev->label, + sizeof(chipinfo.label)); + chipinfo.label[sizeof(chipinfo.label)-1] = '\0'; + chipinfo.lines = gdev->ngpio; + if (copy_to_user(ip, &chipinfo, sizeof(chipinfo))) + return -EFAULT; + return 0; + } else if (cmd == GPIO_GET_LINEINFO_IOCTL) { + struct gpioline_info lineinfo; + struct gpio_desc *desc; + + if (copy_from_user(&lineinfo, ip, sizeof(lineinfo))) + return -EFAULT; + if (lineinfo.line_offset > gdev->ngpio) + return -EINVAL; + + desc = &gdev->descs[lineinfo.line_offset]; + if (desc->name) { + strncpy(lineinfo.name, desc->name, + sizeof(lineinfo.name)); + lineinfo.name[sizeof(lineinfo.name)-1] = '\0'; + } else { + lineinfo.name[0] = '\0'; + } + if (desc->label) { + strncpy(lineinfo.label, desc->label, + sizeof(lineinfo.label)); + lineinfo.label[sizeof(lineinfo.label)-1] = '\0'; + } else { + lineinfo.label[0] = '\0'; + } + + /* + * Userspace only need to know that the kernel is using + * this GPIO so it can't use it. + */ + lineinfo.flags = 0; + if (test_bit(FLAG_REQUESTED, &desc->flags) || + test_bit(FLAG_IS_HOGGED, &desc->flags) || + test_bit(FLAG_USED_AS_IRQ, &desc->flags) || + test_bit(FLAG_EXPORT, &desc->flags) || + test_bit(FLAG_SYSFS, &desc->flags)) + lineinfo.flags |= GPIOLINE_FLAG_KERNEL; + if (test_bit(FLAG_IS_OUT, &desc->flags)) + lineinfo.flags |= GPIOLINE_FLAG_IS_OUT; + if (test_bit(FLAG_ACTIVE_LOW, &desc->flags)) + lineinfo.flags |= GPIOLINE_FLAG_ACTIVE_LOW; + if (test_bit(FLAG_OPEN_DRAIN, &desc->flags)) + lineinfo.flags |= GPIOLINE_FLAG_OPEN_DRAIN; + if (test_bit(FLAG_OPEN_SOURCE, &desc->flags)) + lineinfo.flags |= GPIOLINE_FLAG_OPEN_SOURCE; + + if (copy_to_user(ip, &lineinfo, sizeof(lineinfo))) + return -EFAULT; + return 0; + } + return -EINVAL; +} + +/** + * gpio_chrdev_open() - open the chardev for ioctl operations + * @inode: inode for this chardev + * @filp: file struct for storing private data + * Returns 0 on success + */ +static int gpio_chrdev_open(struct inode *inode, struct file *filp) +{ + struct gpio_device *gdev = container_of(inode->i_cdev, + struct gpio_device, chrdev); + + /* Fail on open if the backing gpiochip is gone */ + if (!gdev || !gdev->chip) + return -ENODEV; + get_device(&gdev->dev); + filp->private_data = gdev; return 0; } /** + * gpio_chrdev_release() - close chardev after ioctl operations + * @inode: inode for this chardev + * @filp: file struct for storing private data + * Returns 0 on success + */ +static int gpio_chrdev_release(struct inode *inode, struct file *filp) +{ + struct gpio_device *gdev = container_of(inode->i_cdev, + struct gpio_device, chrdev); + + if (!gdev) + return -ENODEV; + put_device(&gdev->dev); + return 0; +} + + +static const struct file_operations gpio_fileops = { + .release = gpio_chrdev_release, + .open = gpio_chrdev_open, + .owner = THIS_MODULE, + .llseek = noop_llseek, + .unlocked_ioctl = gpio_ioctl, + .compat_ioctl = gpio_ioctl, +}; + +static void gpiodevice_release(struct device *dev) +{ + struct gpio_device *gdev = dev_get_drvdata(dev); + + cdev_del(&gdev->chrdev); + list_del(&gdev->list); + ida_simple_remove(&gpio_ida, gdev->id); + kfree(gdev); +} + +/** * gpiochip_add_data() - register a gpio_chip * @chip: the chip to register, with chip->base initialized * Context: potentially before irqs will work @@ -323,43 +479,107 @@ int gpiochip_add_data(struct gpio_chip *chip, void *data) { unsigned long flags; int status = 0; - unsigned id; + unsigned i; int base = chip->base; - struct gpio_desc *descs; + struct gpio_device *gdev; - descs = kcalloc(chip->ngpio, sizeof(descs[0]), GFP_KERNEL); - if (!descs) + /* + * First: allocate and populate the internal stat container, and + * set up the struct device. + */ + gdev = kzalloc(sizeof(*gdev), GFP_KERNEL); + if (!gdev) return -ENOMEM; + gdev->dev.bus = &gpio_bus_type; + gdev->chip = chip; + chip->gpiodev = gdev; + if (chip->parent) { + gdev->dev.parent = chip->parent; + gdev->dev.of_node = chip->parent->of_node; + } else { +#ifdef CONFIG_OF_GPIO + /* If the gpiochip has an assigned OF node this takes precedence */ + if (chip->of_node) + gdev->dev.of_node = chip->of_node; +#endif + } + gdev->id = ida_simple_get(&gpio_ida, 0, 0, GFP_KERNEL); + if (gdev->id < 0) { + status = gdev->id; + goto err_free_gdev; + } + dev_set_name(&gdev->dev, "gpiochip%d", gdev->id); + device_initialize(&gdev->dev); + dev_set_drvdata(&gdev->dev, gdev); + if (chip->parent && chip->parent->driver) + gdev->owner = chip->parent->driver->owner; + else if (chip->owner) + /* TODO: remove chip->owner */ + gdev->owner = chip->owner; + else + gdev->owner = THIS_MODULE; - chip->data = data; + gdev->descs = devm_kcalloc(&gdev->dev, chip->ngpio, + sizeof(gdev->descs[0]), GFP_KERNEL); + if (!gdev->descs) { + status = -ENOMEM; + goto err_free_gdev; + } if (chip->ngpio == 0) { chip_err(chip, "tried to insert a GPIO chip with zero lines\n"); - return -EINVAL; + status = -EINVAL; + goto err_free_gdev; } + if (chip->label) + gdev->label = devm_kstrdup(&gdev->dev, chip->label, GFP_KERNEL); + else + gdev->label = devm_kstrdup(&gdev->dev, "unknown", GFP_KERNEL); + if (!gdev->label) { + status = -ENOMEM; + goto err_free_gdev; + } + + gdev->ngpio = chip->ngpio; + gdev->data = data; + spin_lock_irqsave(&gpio_lock, flags); + /* + * TODO: this allocates a Linux GPIO number base in the global + * GPIO numberspace for this chip. In the long run we want to + * get *rid* of this numberspace and use only descriptors, but + * it may be a pipe dream. It will not happen before we get rid + * of the sysfs interface anyways. + */ if (base < 0) { base = gpiochip_find_base(chip->ngpio); if (base < 0) { status = base; spin_unlock_irqrestore(&gpio_lock, flags); - goto err_free_descs; + goto err_free_gdev; } + /* + * TODO: it should not be necessary to reflect the assigned + * base outside of the GPIO subsystem. Go over drivers and + * see if anyone makes use of this, else drop this and assign + * a poison instead. + */ chip->base = base; } + gdev->base = base; - status = gpiochip_add_to_list(chip); + status = gpiodev_add_to_list(gdev); if (status) { spin_unlock_irqrestore(&gpio_lock, flags); - goto err_free_descs; + goto err_free_gdev; } - for (id = 0; id < chip->ngpio; id++) { - struct gpio_desc *desc = &descs[id]; + for (i = 0; i < chip->ngpio; i++) { + struct gpio_desc *desc = &gdev->descs[i]; - desc->chip = chip; + desc->gdev = gdev; /* REVISIT: most hardware initializes GPIOs as inputs (often * with pullups enabled) so power usage is minimized. Linux @@ -370,17 +590,12 @@ int gpiochip_add_data(struct gpio_chip *chip, void *data) desc->flags = !chip->direction_input ? (1 << FLAG_IS_OUT) : 0; } - chip->desc = descs; - spin_unlock_irqrestore(&gpio_lock, flags); #ifdef CONFIG_PINCTRL - INIT_LIST_HEAD(&chip->pin_ranges); + INIT_LIST_HEAD(&gdev->pin_ranges); #endif - if (!chip->owner && chip->parent && chip->parent->driver) - chip->owner = chip->parent->driver->owner; - status = gpiochip_set_desc_names(chip); if (status) goto err_remove_from_list; @@ -391,37 +606,73 @@ int gpiochip_add_data(struct gpio_chip *chip, void *data) acpi_gpiochip_add(chip); - status = gpiochip_sysfs_register(chip); + /* + * By first adding the chardev, and then adding the device, + * we get a device node entry in sysfs under + * /sys/bus/gpio/devices/gpiochipN/dev that can be used for + * coldplug of device nodes and other udev business. + */ + cdev_init(&gdev->chrdev, &gpio_fileops); + gdev->chrdev.owner = THIS_MODULE; + gdev->chrdev.kobj.parent = &gdev->dev.kobj; + gdev->dev.devt = MKDEV(MAJOR(gpio_devt), gdev->id); + status = cdev_add(&gdev->chrdev, gdev->dev.devt, 1); + if (status < 0) + chip_warn(chip, "failed to add char device %d:%d\n", + MAJOR(gpio_devt), gdev->id); + else + chip_dbg(chip, "added GPIO chardev (%d:%d)\n", + MAJOR(gpio_devt), gdev->id); + status = device_add(&gdev->dev); + if (status) + goto err_remove_chardev; + + status = gpiochip_sysfs_register(gdev); if (status) - goto err_remove_chip; + goto err_remove_device; - pr_debug("%s: registered GPIOs %d to %d on device: %s\n", __func__, - chip->base, chip->base + chip->ngpio - 1, - chip->label ? : "generic"); + /* From this point, the .release() function cleans up gpio_device */ + gdev->dev.release = gpiodevice_release; + get_device(&gdev->dev); + pr_debug("%s: registered GPIOs %d to %d on device: %s (%s)\n", + __func__, gdev->base, gdev->base + gdev->ngpio - 1, + dev_name(&gdev->dev), chip->label ? : "generic"); return 0; +err_remove_device: + device_del(&gdev->dev); +err_remove_chardev: + cdev_del(&gdev->chrdev); err_remove_chip: acpi_gpiochip_remove(chip); gpiochip_free_hogs(chip); of_gpiochip_remove(chip); err_remove_from_list: spin_lock_irqsave(&gpio_lock, flags); - list_del(&chip->list); + list_del(&gdev->list); spin_unlock_irqrestore(&gpio_lock, flags); - chip->desc = NULL; -err_free_descs: - kfree(descs); - +err_free_gdev: + ida_simple_remove(&gpio_ida, gdev->id); /* failures here can mean systems won't boot... */ pr_err("%s: GPIOs %d..%d (%s) failed to register\n", __func__, - chip->base, chip->base + chip->ngpio - 1, - chip->label ? : "generic"); + gdev->base, gdev->base + gdev->ngpio - 1, + chip->label ? : "generic"); + kfree(gdev); return status; } EXPORT_SYMBOL_GPL(gpiochip_add_data); /** + * gpiochip_get_data() - get per-subdriver data for the chip + */ +void *gpiochip_get_data(struct gpio_chip *chip) +{ + return chip->gpiodev->data; +} +EXPORT_SYMBOL_GPL(gpiochip_get_data); + +/** * gpiochip_remove() - unregister a gpio_chip * @chip: the chip to unregister * @@ -429,36 +680,46 @@ EXPORT_SYMBOL_GPL(gpiochip_add_data); */ void gpiochip_remove(struct gpio_chip *chip) { + struct gpio_device *gdev = chip->gpiodev; struct gpio_desc *desc; unsigned long flags; - unsigned id; + unsigned i; bool requested = false; - gpiochip_sysfs_unregister(chip); - + /* FIXME: should the legacy sysfs handling be moved to gpio_device? */ + gpiochip_sysfs_unregister(gdev); + /* Numb the device, cancelling all outstanding operations */ + gdev->chip = NULL; gpiochip_irqchip_remove(chip); - acpi_gpiochip_remove(chip); gpiochip_remove_pin_ranges(chip); gpiochip_free_hogs(chip); of_gpiochip_remove(chip); + /* + * We accept no more calls into the driver from this point, so + * NULL the driver data pointer + */ + gdev->data = NULL; spin_lock_irqsave(&gpio_lock, flags); - for (id = 0; id < chip->ngpio; id++) { - desc = &chip->desc[id]; - desc->chip = NULL; + for (i = 0; i < gdev->ngpio; i++) { + desc = &gdev->descs[i]; if (test_bit(FLAG_REQUESTED, &desc->flags)) requested = true; } - list_del(&chip->list); spin_unlock_irqrestore(&gpio_lock, flags); if (requested) - dev_crit(chip->parent, + dev_crit(&gdev->dev, "REMOVING GPIOCHIP WITH GPIOS STILL REQUESTED\n"); - kfree(chip->desc); - chip->desc = NULL; + /* + * The gpiochip side puts its use of the device to rest here: + * if there are no userspace clients, the chardev and device will + * be removed, else it will be dangling until the last user is + * gone. + */ + put_device(&gdev->dev); } EXPORT_SYMBOL_GPL(gpiochip_remove); @@ -477,17 +738,21 @@ struct gpio_chip *gpiochip_find(void *data, int (*match)(struct gpio_chip *chip, void *data)) { + struct gpio_device *gdev; struct gpio_chip *chip; unsigned long flags; spin_lock_irqsave(&gpio_lock, flags); - list_for_each_entry(chip, &gpio_chips, list) - if (match(chip, data)) + list_for_each_entry(gdev, &gpio_devices, list) + if (match(gdev->chip, data)) break; /* No match? */ - if (&chip->list == &gpio_chips) + if (&gdev->list == &gpio_devices) chip = NULL; + else + chip = gdev->chip; + spin_unlock_irqrestore(&gpio_lock, flags); return chip; @@ -617,14 +882,14 @@ static int gpiochip_irq_reqres(struct irq_data *d) { struct gpio_chip *chip = irq_data_get_irq_chip_data(d); - if (!try_module_get(chip->owner)) + if (!try_module_get(chip->gpiodev->owner)) return -ENODEV; if (gpiochip_lock_as_irq(chip, d->hwirq)) { chip_err(chip, "unable to lock HW IRQ %lu for IRQ\n", d->hwirq); - module_put(chip->owner); + module_put(chip->gpiodev->owner); return -EINVAL; } return 0; @@ -635,7 +900,7 @@ static void gpiochip_irq_relres(struct irq_data *d) struct gpio_chip *chip = irq_data_get_irq_chip_data(d); gpiochip_unlock_as_irq(chip, d->hwirq); - module_put(chip->owner); + module_put(chip->gpiodev->owner); } static int gpiochip_to_irq(struct gpio_chip *chip, unsigned offset) @@ -785,7 +1050,7 @@ static void gpiochip_irqchip_remove(struct gpio_chip *gpiochip) {} */ int gpiochip_generic_request(struct gpio_chip *chip, unsigned offset) { - return pinctrl_request_gpio(chip->base + offset); + return pinctrl_request_gpio(chip->gpiodev->base + offset); } EXPORT_SYMBOL_GPL(gpiochip_generic_request); @@ -796,7 +1061,7 @@ EXPORT_SYMBOL_GPL(gpiochip_generic_request); */ void gpiochip_generic_free(struct gpio_chip *chip, unsigned offset) { - pinctrl_free_gpio(chip->base + offset); + pinctrl_free_gpio(chip->gpiodev->base + offset); } EXPORT_SYMBOL_GPL(gpiochip_generic_free); @@ -814,6 +1079,7 @@ int gpiochip_add_pingroup_range(struct gpio_chip *chip, unsigned int gpio_offset, const char *pin_group) { struct gpio_pin_range *pin_range; + struct gpio_device *gdev = chip->gpiodev; int ret; pin_range = kzalloc(sizeof(*pin_range), GFP_KERNEL); @@ -826,7 +1092,7 @@ int gpiochip_add_pingroup_range(struct gpio_chip *chip, pin_range->range.id = gpio_offset; pin_range->range.gc = chip; pin_range->range.name = chip->label; - pin_range->range.base = chip->base + gpio_offset; + pin_range->range.base = gdev->base + gpio_offset; pin_range->pctldev = pctldev; ret = pinctrl_get_group_pins(pctldev, pin_group, @@ -843,7 +1109,7 @@ int gpiochip_add_pingroup_range(struct gpio_chip *chip, gpio_offset, gpio_offset + pin_range->range.npins - 1, pinctrl_dev_get_devname(pctldev), pin_group); - list_add_tail(&pin_range->node, &chip->pin_ranges); + list_add_tail(&pin_range->node, &gdev->pin_ranges); return 0; } @@ -863,6 +1129,7 @@ int gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name, unsigned int npins) { struct gpio_pin_range *pin_range; + struct gpio_device *gdev = chip->gpiodev; int ret; pin_range = kzalloc(sizeof(*pin_range), GFP_KERNEL); @@ -875,7 +1142,7 @@ int gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name, pin_range->range.id = gpio_offset; pin_range->range.gc = chip; pin_range->range.name = chip->label; - pin_range->range.base = chip->base + gpio_offset; + pin_range->range.base = gdev->base + gpio_offset; pin_range->range.pin_base = pin_offset; pin_range->range.npins = npins; pin_range->pctldev = pinctrl_find_and_add_gpio_range(pinctl_name, @@ -891,7 +1158,7 @@ int gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name, pinctl_name, pin_offset, pin_offset + npins - 1); - list_add_tail(&pin_range->node, &chip->pin_ranges); + list_add_tail(&pin_range->node, &gdev->pin_ranges); return 0; } @@ -904,8 +1171,9 @@ EXPORT_SYMBOL_GPL(gpiochip_add_pin_range); void gpiochip_remove_pin_ranges(struct gpio_chip *chip) { struct gpio_pin_range *pin_range, *tmp; + struct gpio_device *gdev = chip->gpiodev; - list_for_each_entry_safe(pin_range, tmp, &chip->pin_ranges, node) { + list_for_each_entry_safe(pin_range, tmp, &gdev->pin_ranges, node) { list_del(&pin_range->node); pinctrl_remove_gpio_range(pin_range->pctldev, &pin_range->range); @@ -922,7 +1190,7 @@ EXPORT_SYMBOL_GPL(gpiochip_remove_pin_ranges); */ static int __gpiod_request(struct gpio_desc *desc, const char *label) { - struct gpio_chip *chip = desc->chip; + struct gpio_chip *chip = desc->gdev->chip; int status; unsigned long flags; @@ -971,27 +1239,50 @@ done: return status; } +/* + * This descriptor validation needs to be inserted verbatim into each + * function taking a descriptor, so we need to use a preprocessor + * macro to avoid endless duplication. + */ +#define VALIDATE_DESC(desc) do { \ + if (!desc || !desc->gdev) { \ + pr_warn("%s: invalid GPIO\n", __func__); \ + return -EINVAL; \ + } \ + if ( !desc->gdev->chip ) { \ + dev_warn(&desc->gdev->dev, \ + "%s: backing chip is gone\n", __func__); \ + return 0; \ + } } while (0) + +#define VALIDATE_DESC_VOID(desc) do { \ + if (!desc || !desc->gdev) { \ + pr_warn("%s: invalid GPIO\n", __func__); \ + return; \ + } \ + if (!desc->gdev->chip) { \ + dev_warn(&desc->gdev->dev, \ + "%s: backing chip is gone\n", __func__); \ + return; \ + } } while (0) + + int gpiod_request(struct gpio_desc *desc, const char *label) { int status = -EPROBE_DEFER; - struct gpio_chip *chip; + struct gpio_device *gdev; - if (!desc) { - pr_warn("%s: invalid GPIO\n", __func__); - return -EINVAL; - } + VALIDATE_DESC(desc); + gdev = desc->gdev; - chip = desc->chip; - if (!chip) - goto done; - - if (try_module_get(chip->owner)) { + if (try_module_get(gdev->owner)) { status = __gpiod_request(desc, label); if (status < 0) - module_put(chip->owner); + module_put(gdev->owner); + else + get_device(&gdev->dev); } -done: if (status) gpiod_dbg(desc, "%s: status %d\n", __func__, status); @@ -1010,7 +1301,7 @@ static bool __gpiod_free(struct gpio_desc *desc) spin_lock_irqsave(&gpio_lock, flags); - chip = desc->chip; + chip = desc->gdev->chip; if (chip && test_bit(FLAG_REQUESTED, &desc->flags)) { if (chip->free) { spin_unlock_irqrestore(&gpio_lock, flags); @@ -1033,10 +1324,12 @@ static bool __gpiod_free(struct gpio_desc *desc) void gpiod_free(struct gpio_desc *desc) { - if (desc && __gpiod_free(desc)) - module_put(desc->chip->owner); - else + if (desc && desc->gdev && __gpiod_free(desc)) { + module_put(desc->gdev->owner); + put_device(&desc->gdev->dev); + } else { WARN_ON(extra_checks); + } } /** @@ -1059,7 +1352,7 @@ const char *gpiochip_is_requested(struct gpio_chip *chip, unsigned offset) if (offset >= chip->ngpio) return NULL; - desc = &chip->desc[offset]; + desc = &chip->gpiodev->descs[offset]; if (test_bit(FLAG_REQUESTED, &desc->flags) == 0) return NULL; @@ -1111,7 +1404,8 @@ void gpiochip_free_own_desc(struct gpio_desc *desc) } EXPORT_SYMBOL_GPL(gpiochip_free_own_desc); -/* Drivers MUST set GPIO direction before making get/set calls. In +/* + * Drivers MUST set GPIO direction before making get/set calls. In * some cases this is done in early boot, before IRQs are enabled. * * As a rule these aren't called more than once (except for drivers @@ -1134,12 +1428,9 @@ int gpiod_direction_input(struct gpio_desc *desc) struct gpio_chip *chip; int status = -EINVAL; - if (!desc || !desc->chip) { - pr_warn("%s: invalid GPIO\n", __func__); - return -EINVAL; - } + VALIDATE_DESC(desc); + chip = desc->gdev->chip; - chip = desc->chip; if (!chip->get || !chip->direction_input) { gpiod_warn(desc, "%s: missing get() or direction_input() operations\n", @@ -1178,7 +1469,7 @@ static int _gpiod_direction_output_raw(struct gpio_desc *desc, int value) if (!value && test_bit(FLAG_OPEN_SOURCE, &desc->flags)) return gpiod_direction_input(desc); - chip = desc->chip; + chip = desc->gdev->chip; if (!chip->set || !chip->direction_output) { gpiod_warn(desc, "%s: missing set() or direction_output() operations\n", @@ -1207,10 +1498,7 @@ static int _gpiod_direction_output_raw(struct gpio_desc *desc, int value) */ int gpiod_direction_output_raw(struct gpio_desc *desc, int value) { - if (!desc || !desc->chip) { - pr_warn("%s: invalid GPIO\n", __func__); - return -EINVAL; - } + VALIDATE_DESC(desc); return _gpiod_direction_output_raw(desc, value); } EXPORT_SYMBOL_GPL(gpiod_direction_output_raw); @@ -1229,10 +1517,7 @@ EXPORT_SYMBOL_GPL(gpiod_direction_output_raw); */ int gpiod_direction_output(struct gpio_desc *desc, int value) { - if (!desc || !desc->chip) { - pr_warn("%s: invalid GPIO\n", __func__); - return -EINVAL; - } + VALIDATE_DESC(desc); if (test_bit(FLAG_ACTIVE_LOW, &desc->flags)) value = !value; return _gpiod_direction_output_raw(desc, value); @@ -1251,12 +1536,8 @@ int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce) { struct gpio_chip *chip; - if (!desc || !desc->chip) { - pr_warn("%s: invalid GPIO\n", __func__); - return -EINVAL; - } - - chip = desc->chip; + VALIDATE_DESC(desc); + chip = desc->gdev->chip; if (!chip->set || !chip->set_debounce) { gpiod_dbg(desc, "%s: missing set() or set_debounce() operations\n", @@ -1276,6 +1557,7 @@ EXPORT_SYMBOL_GPL(gpiod_set_debounce); */ int gpiod_is_active_low(const struct gpio_desc *desc) { + VALIDATE_DESC(desc); return test_bit(FLAG_ACTIVE_LOW, &desc->flags); } EXPORT_SYMBOL_GPL(gpiod_is_active_low); @@ -1308,7 +1590,7 @@ static int _gpiod_get_raw_value(const struct gpio_desc *desc) int offset; int value; - chip = desc->chip; + chip = desc->gdev->chip; offset = gpio_chip_hwgpio(desc); value = chip->get ? chip->get(chip, offset) : -EIO; value = value < 0 ? value : !!value; @@ -1328,10 +1610,9 @@ static int _gpiod_get_raw_value(const struct gpio_desc *desc) */ int gpiod_get_raw_value(const struct gpio_desc *desc) { - if (!desc) - return 0; + VALIDATE_DESC(desc); /* Should be using gpio_get_value_cansleep() */ - WARN_ON(desc->chip->can_sleep); + WARN_ON(desc->gdev->chip->can_sleep); return _gpiod_get_raw_value(desc); } EXPORT_SYMBOL_GPL(gpiod_get_raw_value); @@ -1349,10 +1630,10 @@ EXPORT_SYMBOL_GPL(gpiod_get_raw_value); int gpiod_get_value(const struct gpio_desc *desc) { int value; - if (!desc) - return 0; + + VALIDATE_DESC(desc); /* Should be using gpio_get_value_cansleep() */ - WARN_ON(desc->chip->can_sleep); + WARN_ON(desc->gdev->chip->can_sleep); value = _gpiod_get_raw_value(desc); if (value < 0) @@ -1373,7 +1654,7 @@ EXPORT_SYMBOL_GPL(gpiod_get_value); static void _gpio_set_open_drain_value(struct gpio_desc *desc, bool value) { int err = 0; - struct gpio_chip *chip = desc->chip; + struct gpio_chip *chip = desc->gdev->chip; int offset = gpio_chip_hwgpio(desc); if (value) { @@ -1400,7 +1681,7 @@ static void _gpio_set_open_drain_value(struct gpio_desc *desc, bool value) static void _gpio_set_open_source_value(struct gpio_desc *desc, bool value) { int err = 0; - struct gpio_chip *chip = desc->chip; + struct gpio_chip *chip = desc->gdev->chip; int offset = gpio_chip_hwgpio(desc); if (value) { @@ -1423,7 +1704,7 @@ static void _gpiod_set_raw_value(struct gpio_desc *desc, bool value) { struct gpio_chip *chip; - chip = desc->chip; + chip = desc->gdev->chip; trace_gpio_value(desc_to_gpio(desc), 0, value); if (test_bit(FLAG_OPEN_DRAIN, &desc->flags)) _gpio_set_open_drain_value(desc, value); @@ -1471,7 +1752,7 @@ static void gpiod_set_array_value_priv(bool raw, bool can_sleep, int i = 0; while (i < array_size) { - struct gpio_chip *chip = desc_array[i]->chip; + struct gpio_chip *chip = desc_array[i]->gdev->chip; unsigned long mask[BITS_TO_LONGS(chip->ngpio)]; unsigned long bits[BITS_TO_LONGS(chip->ngpio)]; int count = 0; @@ -1505,7 +1786,8 @@ static void gpiod_set_array_value_priv(bool raw, bool can_sleep, count++; } i++; - } while ((i < array_size) && (desc_array[i]->chip == chip)); + } while ((i < array_size) && + (desc_array[i]->gdev->chip == chip)); /* push collected bits to outputs */ if (count != 0) gpio_chip_set_multiple(chip, mask, bits); @@ -1525,10 +1807,9 @@ static void gpiod_set_array_value_priv(bool raw, bool can_sleep, */ void gpiod_set_raw_value(struct gpio_desc *desc, int value) { - if (!desc) - return; + VALIDATE_DESC_VOID(desc); /* Should be using gpio_set_value_cansleep() */ - WARN_ON(desc->chip->can_sleep); + WARN_ON(desc->gdev->chip->can_sleep); _gpiod_set_raw_value(desc, value); } EXPORT_SYMBOL_GPL(gpiod_set_raw_value); @@ -1546,10 +1827,9 @@ EXPORT_SYMBOL_GPL(gpiod_set_raw_value); */ void gpiod_set_value(struct gpio_desc *desc, int value) { - if (!desc) - return; + VALIDATE_DESC_VOID(desc); /* Should be using gpio_set_value_cansleep() */ - WARN_ON(desc->chip->can_sleep); + WARN_ON(desc->gdev->chip->can_sleep); if (test_bit(FLAG_ACTIVE_LOW, &desc->flags)) value = !value; _gpiod_set_raw_value(desc, value); @@ -1607,9 +1887,8 @@ EXPORT_SYMBOL_GPL(gpiod_set_array_value); */ int gpiod_cansleep(const struct gpio_desc *desc) { - if (!desc) - return 0; - return desc->chip->can_sleep; + VALIDATE_DESC(desc); + return desc->gdev->chip->can_sleep; } EXPORT_SYMBOL_GPL(gpiod_cansleep); @@ -1625,9 +1904,8 @@ int gpiod_to_irq(const struct gpio_desc *desc) struct gpio_chip *chip; int offset; - if (!desc) - return -EINVAL; - chip = desc->chip; + VALIDATE_DESC(desc); + chip = desc->gdev->chip; offset = gpio_chip_hwgpio(desc); return chip->to_irq ? chip->to_irq(chip, offset) : -ENXIO; } @@ -1646,14 +1924,14 @@ int gpiochip_lock_as_irq(struct gpio_chip *chip, unsigned int offset) if (offset >= chip->ngpio) return -EINVAL; - if (test_bit(FLAG_IS_OUT, &chip->desc[offset].flags)) { + if (test_bit(FLAG_IS_OUT, &chip->gpiodev->descs[offset].flags)) { chip_err(chip, "%s: tried to flag a GPIO set as output for IRQ\n", __func__); return -EIO; } - set_bit(FLAG_USED_AS_IRQ, &chip->desc[offset].flags); + set_bit(FLAG_USED_AS_IRQ, &chip->gpiodev->descs[offset].flags); return 0; } EXPORT_SYMBOL_GPL(gpiochip_lock_as_irq); @@ -1671,10 +1949,37 @@ void gpiochip_unlock_as_irq(struct gpio_chip *chip, unsigned int offset) if (offset >= chip->ngpio) return; - clear_bit(FLAG_USED_AS_IRQ, &chip->desc[offset].flags); + clear_bit(FLAG_USED_AS_IRQ, &chip->gpiodev->descs[offset].flags); } EXPORT_SYMBOL_GPL(gpiochip_unlock_as_irq); +bool gpiochip_line_is_irq(struct gpio_chip *chip, unsigned int offset) +{ + if (offset >= chip->ngpio) + return false; + + return test_bit(FLAG_USED_AS_IRQ, &chip->gpiodev->descs[offset].flags); +} +EXPORT_SYMBOL_GPL(gpiochip_line_is_irq); + +bool gpiochip_line_is_open_drain(struct gpio_chip *chip, unsigned int offset) +{ + if (offset >= chip->ngpio) + return false; + + return test_bit(FLAG_OPEN_DRAIN, &chip->gpiodev->descs[offset].flags); +} +EXPORT_SYMBOL_GPL(gpiochip_line_is_open_drain); + +bool gpiochip_line_is_open_source(struct gpio_chip *chip, unsigned int offset) +{ + if (offset >= chip->ngpio) + return false; + + return test_bit(FLAG_OPEN_SOURCE, &chip->gpiodev->descs[offset].flags); +} +EXPORT_SYMBOL_GPL(gpiochip_line_is_open_source); + /** * gpiod_get_raw_value_cansleep() - return a gpio's raw value * @desc: gpio whose value will be returned @@ -1687,8 +1992,7 @@ EXPORT_SYMBOL_GPL(gpiochip_unlock_as_irq); int gpiod_get_raw_value_cansleep(const struct gpio_desc *desc) { might_sleep_if(extra_checks); - if (!desc) - return 0; + VALIDATE_DESC(desc); return _gpiod_get_raw_value(desc); } EXPORT_SYMBOL_GPL(gpiod_get_raw_value_cansleep); @@ -1707,9 +2011,7 @@ int gpiod_get_value_cansleep(const struct gpio_desc *desc) int value; might_sleep_if(extra_checks); - if (!desc) - return 0; - + VALIDATE_DESC(desc); value = _gpiod_get_raw_value(desc); if (value < 0) return value; @@ -1734,8 +2036,7 @@ EXPORT_SYMBOL_GPL(gpiod_get_value_cansleep); void gpiod_set_raw_value_cansleep(struct gpio_desc *desc, int value) { might_sleep_if(extra_checks); - if (!desc) - return; + VALIDATE_DESC_VOID(desc); _gpiod_set_raw_value(desc, value); } EXPORT_SYMBOL_GPL(gpiod_set_raw_value_cansleep); @@ -1753,9 +2054,7 @@ EXPORT_SYMBOL_GPL(gpiod_set_raw_value_cansleep); void gpiod_set_value_cansleep(struct gpio_desc *desc, int value) { might_sleep_if(extra_checks); - if (!desc) - return; - + VALIDATE_DESC_VOID(desc); if (test_bit(FLAG_ACTIVE_LOW, &desc->flags)) value = !value; _gpiod_set_raw_value(desc, value); @@ -2358,8 +2657,8 @@ static void gpiochip_free_hogs(struct gpio_chip *chip) int id; for (id = 0; id < chip->ngpio; id++) { - if (test_bit(FLAG_IS_HOGGED, &chip->desc[id].flags)) - gpiochip_free_own_desc(&chip->desc[id]); + if (test_bit(FLAG_IS_HOGGED, &chip->gpiodev->descs[id].flags)) + gpiochip_free_own_desc(&chip->gpiodev->descs[id]); } } @@ -2456,17 +2755,38 @@ void gpiod_put_array(struct gpio_descs *descs) } EXPORT_SYMBOL_GPL(gpiod_put_array); +static int __init gpiolib_dev_init(void) +{ + int ret; + + /* Register GPIO sysfs bus */ + ret = bus_register(&gpio_bus_type); + if (ret < 0) { + pr_err("gpiolib: could not register GPIO bus type\n"); + return ret; + } + + ret = alloc_chrdev_region(&gpio_devt, 0, GPIO_DEV_MAX, "gpiochip"); + if (ret < 0) { + pr_err("gpiolib: failed to allocate char dev region\n"); + bus_unregister(&gpio_bus_type); + } + return ret; +} +core_initcall(gpiolib_dev_init); + #ifdef CONFIG_DEBUG_FS -static void gpiolib_dbg_show(struct seq_file *s, struct gpio_chip *chip) +static void gpiolib_dbg_show(struct seq_file *s, struct gpio_device *gdev) { unsigned i; - unsigned gpio = chip->base; - struct gpio_desc *gdesc = &chip->desc[0]; + struct gpio_chip *chip = gdev->chip; + unsigned gpio = gdev->base; + struct gpio_desc *gdesc = &gdev->descs[0]; int is_out; int is_irq; - for (i = 0; i < chip->ngpio; i++, gpio++, gdesc++) { + for (i = 0; i < gdev->ngpio; i++, gpio++, gdesc++) { if (!test_bit(FLAG_REQUESTED, &gdesc->flags)) { if (gdesc->name) { seq_printf(s, " gpio-%-3d (%-20.20s)\n", @@ -2492,16 +2812,16 @@ static void gpiolib_dbg_show(struct seq_file *s, struct gpio_chip *chip) static void *gpiolib_seq_start(struct seq_file *s, loff_t *pos) { unsigned long flags; - struct gpio_chip *chip = NULL; + struct gpio_device *gdev = NULL; loff_t index = *pos; s->private = ""; spin_lock_irqsave(&gpio_lock, flags); - list_for_each_entry(chip, &gpio_chips, list) + list_for_each_entry(gdev, &gpio_devices, list) if (index-- == 0) { spin_unlock_irqrestore(&gpio_lock, flags); - return chip; + return gdev; } spin_unlock_irqrestore(&gpio_lock, flags); @@ -2511,14 +2831,14 @@ static void *gpiolib_seq_start(struct seq_file *s, loff_t *pos) static void *gpiolib_seq_next(struct seq_file *s, void *v, loff_t *pos) { unsigned long flags; - struct gpio_chip *chip = v; + struct gpio_device *gdev = v; void *ret = NULL; spin_lock_irqsave(&gpio_lock, flags); - if (list_is_last(&chip->list, &gpio_chips)) + if (list_is_last(&gdev->list, &gpio_devices)) ret = NULL; else - ret = list_entry(chip->list.next, struct gpio_chip, list); + ret = list_entry(gdev->list.next, struct gpio_device, list); spin_unlock_irqrestore(&gpio_lock, flags); s->private = "\n"; @@ -2533,15 +2853,24 @@ static void gpiolib_seq_stop(struct seq_file *s, void *v) static int gpiolib_seq_show(struct seq_file *s, void *v) { - struct gpio_chip *chip = v; - struct device *dev; + struct gpio_device *gdev = v; + struct gpio_chip *chip = gdev->chip; + struct device *parent; + + if (!chip) { + seq_printf(s, "%s%s: (dangling chip)", (char *)s->private, + dev_name(&gdev->dev)); + return 0; + } - seq_printf(s, "%sGPIOs %d-%d", (char *)s->private, - chip->base, chip->base + chip->ngpio - 1); - dev = chip->parent; - if (dev) - seq_printf(s, ", %s/%s", dev->bus ? dev->bus->name : "no-bus", - dev_name(dev)); + seq_printf(s, "%s%s: GPIOs %d-%d", (char *)s->private, + dev_name(&gdev->dev), + gdev->base, gdev->base + gdev->ngpio - 1); + parent = chip->parent; + if (parent) + seq_printf(s, ", parent: %s/%s", + parent->bus ? parent->bus->name : "no-bus", + dev_name(parent)); if (chip->label) seq_printf(s, ", %s", chip->label); if (chip->can_sleep) @@ -2551,7 +2880,7 @@ static int gpiolib_seq_show(struct seq_file *s, void *v) if (chip->dbg_show) chip->dbg_show(s, chip); else - gpiolib_dbg_show(s, chip); + gpiolib_dbg_show(s, gdev); return 0; } diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index 99ed3b00ffe9..e30e5fdb1214 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -12,14 +12,67 @@ #ifndef GPIOLIB_H #define GPIOLIB_H +#include <linux/gpio/driver.h> #include <linux/err.h> #include <linux/device.h> +#include <linux/module.h> +#include <linux/cdev.h> enum of_gpio_flags; enum gpiod_flags; struct acpi_device; /** + * struct gpio_device - internal state container for GPIO devices + * @id: numerical ID number for the GPIO chip + * @dev: the GPIO device struct + * @chrdev: character device for the GPIO device + * @mockdev: class device used by the deprecated sysfs interface (may be + * NULL) + * @owner: helps prevent removal of modules exporting active GPIOs + * @chip: pointer to the corresponding gpiochip, holding static + * data for this device + * @descs: array of ngpio descriptors. + * @ngpio: the number of GPIO lines on this GPIO device, equal to the size + * of the @descs array. + * @base: GPIO base in the DEPRECATED global Linux GPIO numberspace, assigned + * at device creation time. + * @label: a descriptive name for the GPIO device, such as the part number + * or name of the IP component in a System on Chip. + * @data: per-instance data assigned by the driver + * @list: links gpio_device:s together for traversal + * + * This state container holds most of the runtime variable data + * for a GPIO device and can hold references and live on after the + * GPIO chip has been removed, if it is still being used from + * userspace. + */ +struct gpio_device { + int id; + struct device dev; + struct cdev chrdev; + struct device *mockdev; + struct module *owner; + struct gpio_chip *chip; + struct gpio_desc *descs; + int base; + u16 ngpio; + char *label; + void *data; + struct list_head list; + +#ifdef CONFIG_PINCTRL + /* + * If CONFIG_PINCTRL is enabled, then gpio controllers can optionally + * describe the actual pin range which they serve in an SoC. This + * information would be used by pinctrl subsystem to configure + * corresponding pins for gpio usage. + */ + struct list_head pin_ranges; +#endif +}; + +/** * struct acpi_gpio_info - ACPI GPIO specific information * @gpioint: if %true this GPIO is of type GpioInt otherwise type is GpioIo * @active_low: in case of @gpioint, the pin is active low @@ -90,10 +143,10 @@ struct gpio_desc *of_get_named_gpiod_flags(struct device_node *np, struct gpio_desc *gpiochip_get_desc(struct gpio_chip *chip, u16 hwnum); extern struct spinlock gpio_lock; -extern struct list_head gpio_chips; +extern struct list_head gpio_devices; struct gpio_desc { - struct gpio_chip *chip; + struct gpio_device *gdev; unsigned long flags; /* flag symbols are bit numbers */ #define FLAG_REQUESTED 0 @@ -122,7 +175,7 @@ int gpiod_hog(struct gpio_desc *desc, const char *name, */ static int __maybe_unused gpio_chip_hwgpio(const struct gpio_desc *desc) { - return desc - &desc->chip->desc[0]; + return desc - &desc->gdev->descs[0]; } /* With descriptor prefix */ @@ -149,31 +202,31 @@ static int __maybe_unused gpio_chip_hwgpio(const struct gpio_desc *desc) /* With chip prefix */ #define chip_emerg(chip, fmt, ...) \ - pr_emerg("GPIO chip %s: " fmt, chip->label, ##__VA_ARGS__) + dev_emerg(&chip->gpiodev->dev, "(%s): " fmt, chip->label, ##__VA_ARGS__) #define chip_crit(chip, fmt, ...) \ - pr_crit("GPIO chip %s: " fmt, chip->label, ##__VA_ARGS__) + dev_crit(&chip->gpiodev->dev, "(%s): " fmt, chip->label, ##__VA_ARGS__) #define chip_err(chip, fmt, ...) \ - pr_err("GPIO chip %s: " fmt, chip->label, ##__VA_ARGS__) + dev_err(&chip->gpiodev->dev, "(%s): " fmt, chip->label, ##__VA_ARGS__) #define chip_warn(chip, fmt, ...) \ - pr_warn("GPIO chip %s: " fmt, chip->label, ##__VA_ARGS__) + dev_warn(&chip->gpiodev->dev, "(%s): " fmt, chip->label, ##__VA_ARGS__) #define chip_info(chip, fmt, ...) \ - pr_info("GPIO chip %s: " fmt, chip->label, ##__VA_ARGS__) + dev_info(&chip->gpiodev->dev, "(%s): " fmt, chip->label, ##__VA_ARGS__) #define chip_dbg(chip, fmt, ...) \ - pr_debug("GPIO chip %s: " fmt, chip->label, ##__VA_ARGS__) + dev_dbg(&chip->gpiodev->dev, "(%s): " fmt, chip->label, ##__VA_ARGS__) #ifdef CONFIG_GPIO_SYSFS -int gpiochip_sysfs_register(struct gpio_chip *chip); -void gpiochip_sysfs_unregister(struct gpio_chip *chip); +int gpiochip_sysfs_register(struct gpio_device *gdev); +void gpiochip_sysfs_unregister(struct gpio_device *gdev); #else -static inline int gpiochip_sysfs_register(struct gpio_chip *chip) +static inline int gpiochip_sysfs_register(struct gpio_device *gdev) { return 0; } -static inline void gpiochip_sysfs_unregister(struct gpio_chip *chip) +static inline void gpiochip_sysfs_unregister(struct gpio_device *gdev) { } diff --git a/drivers/mfd/menelaus.c b/drivers/mfd/menelaus.c index 3ac36f5ccd3e..a4a8f1ec3fb6 100644 --- a/drivers/mfd/menelaus.c +++ b/drivers/mfd/menelaus.c @@ -42,10 +42,10 @@ #include <linux/bcd.h> #include <linux/slab.h> #include <linux/mfd/menelaus.h> +#include <linux/gpio.h> #include <asm/mach/irq.h> -#include <asm/gpio.h> #define DRIVER_NAME "menelaus" diff --git a/drivers/mtd/onenand/omap2.c b/drivers/mtd/onenand/omap2.c index 0aacf125938b..24a1388d3031 100644 --- a/drivers/mtd/onenand/omap2.c +++ b/drivers/mtd/onenand/omap2.c @@ -35,10 +35,10 @@ #include <linux/io.h> #include <linux/slab.h> #include <linux/regulator/consumer.h> +#include <linux/gpio.h> #include <asm/mach/flash.h> #include <linux/platform_data/mtd-onenand-omap2.h> -#include <asm/gpio.h> #include <linux/omap-dma.h> diff --git a/drivers/pcmcia/bfin_cf_pcmcia.c b/drivers/pcmcia/bfin_cf_pcmcia.c index bba1dcbb8075..8b0923fd76c6 100644 --- a/drivers/pcmcia/bfin_cf_pcmcia.c +++ b/drivers/pcmcia/bfin_cf_pcmcia.c @@ -36,10 +36,10 @@ #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/io.h> +#include <linux/gpio.h> #include <pcmcia/ss.h> #include <pcmcia/cisreg.h> -#include <asm/gpio.h> #define SZ_1K 0x00000400 #define SZ_8K 0x00002000 diff --git a/drivers/pcmcia/pxa2xx_vpac270.c b/drivers/pcmcia/pxa2xx_vpac270.c index a47dcd24a26a..33c5b8823367 100644 --- a/drivers/pcmcia/pxa2xx_vpac270.c +++ b/drivers/pcmcia/pxa2xx_vpac270.c @@ -17,7 +17,6 @@ #include <asm/mach-types.h> -#include <asm/gpio.h> #include <mach/vpac270.h> #include "soc_common.h" diff --git a/drivers/pinctrl/bcm/pinctrl-iproc-gpio.c b/drivers/pinctrl/bcm/pinctrl-iproc-gpio.c index 3b2ac8f771ed..d530ab4b9d85 100644 --- a/drivers/pinctrl/bcm/pinctrl-iproc-gpio.c +++ b/drivers/pinctrl/bcm/pinctrl-iproc-gpio.c @@ -26,7 +26,7 @@ #include <linux/slab.h> #include <linux/interrupt.h> #include <linux/io.h> -#include <linux/gpio.h> +#include <linux/gpio/driver.h> #include <linux/ioport.h> #include <linux/of_device.h> #include <linux/of_irq.h> @@ -98,11 +98,6 @@ struct iproc_gpio { struct pinctrl_desc pctldesc; }; -static inline struct iproc_gpio *to_iproc_gpio(struct gpio_chip *gc) -{ - return container_of(gc, struct iproc_gpio, gc); -} - /* * Mapping from PINCONF pins to GPIO pins is 1-to-1 */ @@ -147,7 +142,7 @@ static inline bool iproc_get_bit(struct iproc_gpio *chip, unsigned int reg, static void iproc_gpio_irq_handler(struct irq_desc *desc) { struct gpio_chip *gc = irq_desc_get_handler_data(desc); - struct iproc_gpio *chip = to_iproc_gpio(gc); + struct iproc_gpio *chip = gpiochip_get_data(gc); struct irq_chip *irq_chip = irq_desc_get_chip(desc); int i, bit; @@ -180,7 +175,7 @@ static void iproc_gpio_irq_handler(struct irq_desc *desc) static void iproc_gpio_irq_ack(struct irq_data *d) { struct gpio_chip *gc = irq_data_get_irq_chip_data(d); - struct iproc_gpio *chip = to_iproc_gpio(gc); + struct iproc_gpio *chip = gpiochip_get_data(gc); unsigned gpio = d->hwirq; unsigned int offset = IPROC_GPIO_REG(gpio, IPROC_GPIO_INT_CLR_OFFSET); @@ -199,7 +194,7 @@ static void iproc_gpio_irq_ack(struct irq_data *d) static void iproc_gpio_irq_set_mask(struct irq_data *d, bool unmask) { struct gpio_chip *gc = irq_data_get_irq_chip_data(d); - struct iproc_gpio *chip = to_iproc_gpio(gc); + struct iproc_gpio *chip = gpiochip_get_data(gc); unsigned gpio = d->hwirq; iproc_set_bit(chip, IPROC_GPIO_INT_MSK_OFFSET, gpio, unmask); @@ -208,7 +203,7 @@ static void iproc_gpio_irq_set_mask(struct irq_data *d, bool unmask) static void iproc_gpio_irq_mask(struct irq_data *d) { struct gpio_chip *gc = irq_data_get_irq_chip_data(d); - struct iproc_gpio *chip = to_iproc_gpio(gc); + struct iproc_gpio *chip = gpiochip_get_data(gc); unsigned long flags; spin_lock_irqsave(&chip->lock, flags); @@ -219,7 +214,7 @@ static void iproc_gpio_irq_mask(struct irq_data *d) static void iproc_gpio_irq_unmask(struct irq_data *d) { struct gpio_chip *gc = irq_data_get_irq_chip_data(d); - struct iproc_gpio *chip = to_iproc_gpio(gc); + struct iproc_gpio *chip = gpiochip_get_data(gc); unsigned long flags; spin_lock_irqsave(&chip->lock, flags); @@ -230,7 +225,7 @@ static void iproc_gpio_irq_unmask(struct irq_data *d) static int iproc_gpio_irq_set_type(struct irq_data *d, unsigned int type) { struct gpio_chip *gc = irq_data_get_irq_chip_data(d); - struct iproc_gpio *chip = to_iproc_gpio(gc); + struct iproc_gpio *chip = gpiochip_get_data(gc); unsigned gpio = d->hwirq; bool level_triggered = false; bool dual_edge = false; @@ -292,7 +287,7 @@ static struct irq_chip iproc_gpio_irq_chip = { */ static int iproc_gpio_request(struct gpio_chip *gc, unsigned offset) { - struct iproc_gpio *chip = to_iproc_gpio(gc); + struct iproc_gpio *chip = gpiochip_get_data(gc); unsigned gpio = gc->base + offset; /* not all Iproc GPIO pins can be muxed individually */ @@ -304,7 +299,7 @@ static int iproc_gpio_request(struct gpio_chip *gc, unsigned offset) static void iproc_gpio_free(struct gpio_chip *gc, unsigned offset) { - struct iproc_gpio *chip = to_iproc_gpio(gc); + struct iproc_gpio *chip = gpiochip_get_data(gc); unsigned gpio = gc->base + offset; if (!chip->pinmux_is_supported) @@ -315,7 +310,7 @@ static void iproc_gpio_free(struct gpio_chip *gc, unsigned offset) static int iproc_gpio_direction_input(struct gpio_chip *gc, unsigned gpio) { - struct iproc_gpio *chip = to_iproc_gpio(gc); + struct iproc_gpio *chip = gpiochip_get_data(gc); unsigned long flags; spin_lock_irqsave(&chip->lock, flags); @@ -330,7 +325,7 @@ static int iproc_gpio_direction_input(struct gpio_chip *gc, unsigned gpio) static int iproc_gpio_direction_output(struct gpio_chip *gc, unsigned gpio, int val) { - struct iproc_gpio *chip = to_iproc_gpio(gc); + struct iproc_gpio *chip = gpiochip_get_data(gc); unsigned long flags; spin_lock_irqsave(&chip->lock, flags); @@ -345,7 +340,7 @@ static int iproc_gpio_direction_output(struct gpio_chip *gc, unsigned gpio, static void iproc_gpio_set(struct gpio_chip *gc, unsigned gpio, int val) { - struct iproc_gpio *chip = to_iproc_gpio(gc); + struct iproc_gpio *chip = gpiochip_get_data(gc); unsigned long flags; spin_lock_irqsave(&chip->lock, flags); @@ -357,7 +352,7 @@ static void iproc_gpio_set(struct gpio_chip *gc, unsigned gpio, int val) static int iproc_gpio_get(struct gpio_chip *gc, unsigned gpio) { - struct iproc_gpio *chip = to_iproc_gpio(gc); + struct iproc_gpio *chip = gpiochip_get_data(gc); unsigned int offset = IPROC_GPIO_REG(gpio, IPROC_GPIO_DATA_IN_OFFSET); unsigned int shift = IPROC_GPIO_SHIFT(gpio); @@ -706,7 +701,7 @@ static int iproc_gpio_probe(struct platform_device *pdev) chip->pinmux_is_supported = of_property_read_bool(dev->of_node, "gpio-ranges"); - ret = gpiochip_add(gc); + ret = gpiochip_add_data(gc, chip); if (ret < 0) { dev_err(dev, "unable to add GPIO chip\n"); return ret; diff --git a/drivers/pinctrl/sirf/pinctrl-atlas7.c b/drivers/pinctrl/sirf/pinctrl-atlas7.c index beb024c31a5d..3d233fc3448a 100644 --- a/drivers/pinctrl/sirf/pinctrl-atlas7.c +++ b/drivers/pinctrl/sirf/pinctrl-atlas7.c @@ -338,7 +338,6 @@ struct atlas7_pinctrl_data { #define ATLAS7_GPIO_CTL_DATAIN_MASK BIT(7) struct atlas7_gpio_bank { - struct pinctrl_dev *pctldev; int id; int irq; void __iomem *base; @@ -6070,7 +6069,6 @@ static int atlas7_gpio_probe(struct platform_device *pdev) } for (idx = 0; idx < nbank; idx++) { - struct gpio_pin_range *pin_range; struct atlas7_gpio_bank *bank; bank = &a7gc->banks[idx]; @@ -6088,22 +6086,6 @@ static int atlas7_gpio_probe(struct platform_device *pdev) gpiochip_set_chained_irqchip(chip, &atlas7_gpio_irq_chip, bank->irq, atlas7_gpio_handle_irq); - - /* Records gpio_pin_range to a7gc */ - list_for_each_entry(pin_range, &chip->pin_ranges, node) { - struct pinctrl_gpio_range *range; - - range = &pin_range->range; - if (range->id == NGPIO_OF_BANK * idx) { - bank->gpio_offset = range->id; - bank->ngpio = range->npins; - bank->gpio_pins = range->pins; - bank->pctldev = pin_range->pctldev; - break; - } - } - - BUG_ON(!bank->pctldev); } platform_set_drvdata(pdev, a7gc); diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.c b/drivers/pinctrl/sunxi/pinctrl-sunxi.c index 7a2465f5e71e..3e95bfe66a06 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c +++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c @@ -457,8 +457,8 @@ static int sunxi_pinctrl_gpio_get(struct gpio_chip *chip, unsigned offset) struct sunxi_pinctrl *pctl = gpiochip_get_data(chip); u32 reg = sunxi_data_reg(offset); u8 index = sunxi_data_offset(offset); - u32 set_mux = pctl->desc->irq_read_needs_mux && - test_bit(FLAG_USED_AS_IRQ, &chip->desc[offset].flags); + bool set_mux = pctl->desc->irq_read_needs_mux && + gpiochip_line_is_irq(chip, offset); u32 val; if (set_mux) diff --git a/drivers/soc/fsl/qe/gpio.c b/drivers/soc/fsl/qe/gpio.c index aa5c11acf212..65845712571c 100644 --- a/drivers/soc/fsl/qe/gpio.c +++ b/drivers/soc/fsl/qe/gpio.c @@ -63,7 +63,7 @@ static int qe_gpio_get(struct gpio_chip *gc, unsigned int gpio) struct qe_pio_regs __iomem *regs = mm_gc->regs; u32 pin_mask = 1 << (QE_PIO_PINS - 1 - gpio); - return in_be32(®s->cpdata) & pin_mask; + return !!(in_be32(®s->cpdata) & pin_mask); } static void qe_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val) diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c index 8755b2c2aada..dbde1149c218 100644 --- a/drivers/usb/gadget/udc/atmel_usba_udc.c +++ b/drivers/usb/gadget/udc/atmel_usba_udc.c @@ -25,8 +25,6 @@ #include <linux/of.h> #include <linux/of_gpio.h> -#include <asm/gpio.h> - #include "atmel_usba_udc.h" #ifdef CONFIG_USB_GADGET_DEBUG_FS diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c index 8c6e15bd6ff0..95c8ddde0725 100644 --- a/drivers/usb/host/ohci-at91.c +++ b/drivers/usb/host/ohci-at91.c @@ -24,8 +24,6 @@ #include <linux/usb.h> #include <linux/usb/hcd.h> -#include <asm/gpio.h> - #include "ohci.h" #define valid_port(index) ((index) >= 0 && (index) < AT91_MAX_USBH_PORTS) diff --git a/drivers/video/fbdev/atmel_lcdfb.c b/drivers/video/fbdev/atmel_lcdfb.c index 19eb42b57d87..e13b9e41957a 100644 --- a/drivers/video/fbdev/atmel_lcdfb.c +++ b/drivers/video/fbdev/atmel_lcdfb.c @@ -26,8 +26,6 @@ #include <linux/regulator/consumer.h> #include <video/videomode.h> -#include <asm/gpio.h> - #include <video/atmel_lcdc.h> struct atmel_lcdfb_config { diff --git a/drivers/video/fbdev/omap/lcd_h3.c b/drivers/video/fbdev/omap/lcd_h3.c index a0729d0200d0..21512b027ff7 100644 --- a/drivers/video/fbdev/omap/lcd_h3.c +++ b/drivers/video/fbdev/omap/lcd_h3.c @@ -22,8 +22,8 @@ #include <linux/module.h> #include <linux/platform_device.h> #include <linux/i2c/tps65010.h> +#include <linux/gpio.h> -#include <asm/gpio.h> #include "omapfb.h" #define MODULE_NAME "omapfb-lcd_h3" diff --git a/drivers/video/fbdev/omap/lcd_osk.c b/drivers/video/fbdev/omap/lcd_osk.c index c3ddebf934b2..b56886c7055e 100644 --- a/drivers/video/fbdev/omap/lcd_osk.c +++ b/drivers/video/fbdev/omap/lcd_osk.c @@ -22,8 +22,7 @@ #include <linux/module.h> #include <linux/platform_device.h> - -#include <asm/gpio.h> +#include <linux/gpio.h> #include <mach/hardware.h> #include <mach/mux.h> diff --git a/drivers/video/fbdev/omap/lcd_palmtt.c b/drivers/video/fbdev/omap/lcd_palmtt.c index 3d0ea04ec248..1a936d5c7b6f 100644 --- a/drivers/video/fbdev/omap/lcd_palmtt.c +++ b/drivers/video/fbdev/omap/lcd_palmtt.c @@ -28,8 +28,8 @@ GPIO13 - screen blanking #include <linux/platform_device.h> #include <linux/module.h> #include <linux/io.h> +#include <linux/gpio.h> -#include <asm/gpio.h> #include "omapfb.h" static int palmtt_panel_init(struct lcd_panel *panel, diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h index 40ec1433f05d..8ca627dcea11 100644 --- a/include/asm-generic/gpio.h +++ b/include/asm-generic/gpio.h @@ -26,8 +26,12 @@ */ #ifndef ARCH_NR_GPIOS +#if defined(CONFIG_ARCH_NR_GPIO) && CONFIG_ARCH_NR_GPIO > 0 +#define ARCH_NR_GPIOS CONFIG_ARCH_NR_GPIO +#else #define ARCH_NR_GPIOS 512 #endif +#endif /* * "valid" GPIO numbers are nonnegative and may be passed to diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index 82fda487453f..639607658ed8 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -1,6 +1,7 @@ #ifndef __LINUX_GPIO_DRIVER_H #define __LINUX_GPIO_DRIVER_H +#include <linux/device.h> #include <linux/types.h> #include <linux/module.h> #include <linux/irq.h> @@ -10,22 +11,21 @@ #include <linux/pinctrl/pinctrl.h> #include <linux/kconfig.h> -struct device; struct gpio_desc; struct of_phandle_args; struct device_node; struct seq_file; +struct gpio_device; #ifdef CONFIG_GPIOLIB /** * struct gpio_chip - abstract a GPIO controller - * @label: for diagnostics + * @label: a functional name for the GPIO device, such as a part + * number or the name of the SoC IP-block implementing it. + * @gpiodev: the internal state holder, opaque struct * @parent: optional parent device providing the GPIOs - * @cdev: class device used by sysfs interface (may be NULL) * @owner: helps prevent removal of modules exporting active GPIOs - * @data: per-instance data assigned by the driver - * @list: links gpio_chips together for traversal * @request: optional hook for chip-specific activation, such as * enabling module power and clock; may sleep * @free: optional hook for chip-specific deactivation, such as @@ -52,7 +52,6 @@ struct seq_file; * get rid of the static GPIO number space in the long run. * @ngpio: the number of GPIOs handled by this controller; the last GPIO * handled is (base + ngpio - 1). - * @desc: array of ngpio descriptors. Private. * @names: if set, must be an array of strings to use as alternative * names for the GPIOs in this chip. Any entry in the array * may be NULL if there is no alias for the GPIO, however the @@ -107,11 +106,9 @@ struct seq_file; */ struct gpio_chip { const char *label; + struct gpio_device *gpiodev; struct device *parent; - struct device *cdev; struct module *owner; - void *data; - struct list_head list; int (*request)(struct gpio_chip *chip, unsigned offset); @@ -141,7 +138,6 @@ struct gpio_chip { struct gpio_chip *chip); int base; u16 ngpio; - struct gpio_desc *desc; const char *const *names; bool can_sleep; bool irq_not_threaded; @@ -184,15 +180,6 @@ struct gpio_chip { int (*of_xlate)(struct gpio_chip *gc, const struct of_phandle_args *gpiospec, u32 *flags); #endif -#ifdef CONFIG_PINCTRL - /* - * If CONFIG_PINCTRL is enabled, then gpio controllers can optionally - * describe the actual pin range which they serve in an SoC. This - * information would be used by pinctrl subsystem to configure - * corresponding pins for gpio usage. - */ - struct list_head pin_ranges; -#endif }; extern const char *gpiochip_is_requested(struct gpio_chip *chip, @@ -211,12 +198,14 @@ extern struct gpio_chip *gpiochip_find(void *data, /* lock/unlock as IRQ */ int gpiochip_lock_as_irq(struct gpio_chip *chip, unsigned int offset); void gpiochip_unlock_as_irq(struct gpio_chip *chip, unsigned int offset); +bool gpiochip_line_is_irq(struct gpio_chip *chip, unsigned int offset); + +/* Line status inquiry for drivers */ +bool gpiochip_line_is_open_drain(struct gpio_chip *chip, unsigned int offset); +bool gpiochip_line_is_open_source(struct gpio_chip *chip, unsigned int offset); /* get driver data */ -static inline void *gpiochip_get_data(struct gpio_chip *chip) -{ - return chip->data; -} +void *gpiochip_get_data(struct gpio_chip *chip); struct gpio_chip *gpiod_to_chip(const struct gpio_desc *desc); diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild index ebd10e624598..5c9ae6a9b7f5 100644 --- a/include/uapi/linux/Kbuild +++ b/include/uapi/linux/Kbuild @@ -138,6 +138,7 @@ header-y += genetlink.h header-y += gen_stats.h header-y += gfs2_ondisk.h header-y += gigaset_dev.h +header-y += gpio.h header-y += gsmmux.h header-y += hdlcdrv.h header-y += hdlc.h diff --git a/include/uapi/linux/gpio.h b/include/uapi/linux/gpio.h new file mode 100644 index 000000000000..416ce47f2291 --- /dev/null +++ b/include/uapi/linux/gpio.h @@ -0,0 +1,56 @@ +/* + * <linux/gpio.h> - userspace ABI for the GPIO character devices + * + * Copyright (C) 2015 Linus Walleij + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ +#ifndef _UAPI_GPIO_H_ +#define _UAPI_GPIO_H_ + +#include <linux/ioctl.h> +#include <linux/types.h> + +/** + * struct gpiochip_info - Information about a certain GPIO chip + * @name: the name of this GPIO chip + * @label: a functional name for this GPIO chip + * @lines: number of GPIO lines on this chip + */ +struct gpiochip_info { + char name[32]; + char label[32]; + __u32 lines; +}; + +/* Line is in use by the kernel */ +#define GPIOLINE_FLAG_KERNEL (1UL << 0) +#define GPIOLINE_FLAG_IS_OUT (1UL << 1) +#define GPIOLINE_FLAG_ACTIVE_LOW (1UL << 2) +#define GPIOLINE_FLAG_OPEN_DRAIN (1UL << 3) +#define GPIOLINE_FLAG_OPEN_SOURCE (1UL << 4) + +/** + * struct gpioline_info - Information about a certain GPIO line + * @line_offset: the local offset on this GPIO device, fill in when + * requesting information from the kernel + * @flags: various flags for this line + * @name: the name of this GPIO line + * @label: a functional name for this GPIO line + * @kernel: this GPIO is in use by the kernel + * @out: this GPIO is an output line (false means it is an input) + * @active_low: this GPIO is active low + */ +struct gpioline_info { + __u32 line_offset; + __u32 flags; + char name[32]; + char label[32]; +}; + +#define GPIO_GET_CHIPINFO_IOCTL _IOR('o', 0x01, struct gpiochip_info) +#define GPIO_GET_LINEINFO_IOCTL _IOWR('o', 0x02, struct gpioline_info) + +#endif /* _UAPI_GPIO_H_ */ diff --git a/tools/Makefile b/tools/Makefile index 6339f6ac3ccb..f41e7c6ea23e 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -13,6 +13,7 @@ help: @echo ' cpupower - a tool for all things x86 CPU power' @echo ' firewire - the userspace part of nosy, an IEEE-1394 traffic sniffer' @echo ' freefall - laptop accelerometer program for disk protection' + @echo ' gpio - GPIO tools' @echo ' hv - tools used when in Hyper-V clients' @echo ' iio - IIO tools' @echo ' lguest - a minimal 32-bit x86 hypervisor' @@ -53,7 +54,7 @@ acpi: FORCE cpupower: FORCE $(call descend,power/$@) -cgroup firewire hv guest spi usb virtio vm net iio: FORCE +cgroup firewire hv guest spi usb virtio vm net iio gpio: FORCE $(call descend,$@) liblockdep: FORCE @@ -119,7 +120,7 @@ acpi_clean: cpupower_clean: $(call descend,power/cpupower,clean) -cgroup_clean hv_clean firewire_clean lguest_clean spi_clean usb_clean virtio_clean vm_clean net_clean iio_clean: +cgroup_clean hv_clean firewire_clean lguest_clean spi_clean usb_clean virtio_clean vm_clean net_clean iio_clean gpio_clean: $(call descend,$(@:_clean=),clean) liblockdep_clean: @@ -155,6 +156,7 @@ build_clean: clean: acpi_clean cgroup_clean cpupower_clean hv_clean firewire_clean lguest_clean \ perf_clean selftests_clean turbostat_clean spi_clean usb_clean virtio_clean \ vm_clean net_clean iio_clean x86_energy_perf_policy_clean tmon_clean \ - freefall_clean build_clean libbpf_clean libsubcmd_clean liblockdep_clean + freefall_clean build_clean libbpf_clean libsubcmd_clean liblockdep_clean \ + gpio_clean .PHONY: FORCE diff --git a/tools/gpio/Makefile b/tools/gpio/Makefile new file mode 100644 index 000000000000..4d198d5c4203 --- /dev/null +++ b/tools/gpio/Makefile @@ -0,0 +1,12 @@ +CC = $(CROSS_COMPILE)gcc +CFLAGS += -Wall -g -D_GNU_SOURCE + +all: lsgpio + +lsgpio: lsgpio.o gpio-utils.o + +%.o: %.c gpio-utils.h + +.PHONY: clean +clean: + rm -f *.o lsgpio diff --git a/tools/gpio/gpio-utils.c b/tools/gpio/gpio-utils.c new file mode 100644 index 000000000000..8208718f2c99 --- /dev/null +++ b/tools/gpio/gpio-utils.c @@ -0,0 +1,11 @@ +/* + * GPIO tools - helpers library for the GPIO tools + * + * Copyright (C) 2015 Linus Walleij + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#include "gpio-utils.h" diff --git a/tools/gpio/gpio-utils.h b/tools/gpio/gpio-utils.h new file mode 100644 index 000000000000..5f57133b8c04 --- /dev/null +++ b/tools/gpio/gpio-utils.h @@ -0,0 +1,27 @@ +/* + * GPIO tools - utility helpers library for the GPIO tools + * + * Copyright (C) 2015 Linus Walleij + * + * Portions copied from iio_utils and lssio: + * Copyright (c) 2010 Manuel Stahl <manuel.stahl@iis.fraunhofer.de> + * Copyright (c) 2008 Jonathan Cameron + * * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ +#ifndef _GPIO_UTILS_H_ +#define _GPIO_UTILS_H_ + +#include <string.h> + +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) + +static inline int check_prefix(const char *str, const char *prefix) +{ + return strlen(str) > strlen(prefix) && + strncmp(str, prefix, strlen(prefix)) == 0; +} + +#endif /* _GPIO_UTILS_H_ */ diff --git a/tools/gpio/lsgpio.c b/tools/gpio/lsgpio.c new file mode 100644 index 000000000000..5535ce81f8f7 --- /dev/null +++ b/tools/gpio/lsgpio.c @@ -0,0 +1,195 @@ +/* + * lsgpio - example on how to list the GPIO lines on a system + * + * Copyright (C) 2015 Linus Walleij + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * Usage: + * lsgpio <-n device-name> + */ + +#include <unistd.h> +#include <stdlib.h> +#include <stdbool.h> +#include <stdio.h> +#include <dirent.h> +#include <errno.h> +#include <string.h> +#include <poll.h> +#include <fcntl.h> +#include <getopt.h> +#include <sys/ioctl.h> +#include <linux/gpio.h> + +#include "gpio-utils.h" + +struct gpio_flag { + char *name; + unsigned long mask; +}; + +struct gpio_flag flagnames[] = { + { + .name = "kernel", + .mask = GPIOLINE_FLAG_KERNEL, + }, + { + .name = "output", + .mask = GPIOLINE_FLAG_IS_OUT, + }, + { + .name = "active-low", + .mask = GPIOLINE_FLAG_ACTIVE_LOW, + }, + { + .name = "open-drain", + .mask = GPIOLINE_FLAG_OPEN_DRAIN, + }, + { + .name = "open-source", + .mask = GPIOLINE_FLAG_OPEN_SOURCE, + }, +}; + +void print_flags(unsigned long flags) +{ + int i; + int printed = 0; + + for (i = 0; i < ARRAY_SIZE(flagnames); i++) { + if (flags & flagnames[i].mask) { + if (printed) + fprintf(stdout, " "); + fprintf(stdout, "%s", flagnames[i].name); + printed++; + } + } +} + +int list_device(const char *device_name) +{ + struct gpiochip_info cinfo; + char *chrdev_name; + int fd; + int ret; + int i; + + ret = asprintf(&chrdev_name, "/dev/%s", device_name); + if (ret < 0) + return -ENOMEM; + + fd = open(chrdev_name, 0); + if (fd == -1) { + ret = -errno; + fprintf(stderr, "Failed to open %s\n", chrdev_name); + goto exit_close_error; + } + + /* Inspect this GPIO chip */ + ret = ioctl(fd, GPIO_GET_CHIPINFO_IOCTL, &cinfo); + if (ret == -1) { + ret = -errno; + perror("Failed to issue CHIPINFO IOCTL\n"); + goto exit_close_error; + } + fprintf(stdout, "GPIO chip: %s, \"%s\", %u GPIO lines\n", + cinfo.name, cinfo.label, cinfo.lines); + + /* Loop over the lines and print info */ + for (i = 0; i < cinfo.lines; i++) { + struct gpioline_info linfo; + + memset(&linfo, 0, sizeof(linfo)); + linfo.line_offset = i; + + ret = ioctl(fd, GPIO_GET_LINEINFO_IOCTL, &linfo); + if (ret == -1) { + ret = -errno; + perror("Failed to issue LINEINFO IOCTL\n"); + goto exit_close_error; + } + fprintf(stdout, "\tline %d:", linfo.line_offset); + if (linfo.name[0]) + fprintf(stdout, " %s", linfo.name); + else + fprintf(stdout, " unnamed"); + if (linfo.label[0]) + fprintf(stdout, " \"%s\"", linfo.label); + else + fprintf(stdout, " unlabeled"); + if (linfo.flags) { + fprintf(stdout, " ["); + print_flags(linfo.flags); + fprintf(stdout, "]"); + } + fprintf(stdout, "\n"); + + } + +exit_close_error: + if (close(fd) == -1) + perror("Failed to close GPIO character device file"); + free(chrdev_name); + return ret; +} + +void print_usage(void) +{ + fprintf(stderr, "Usage: lsgpio [options]...\n" + "List GPIO chips, lines and states\n" + " -n <name> List GPIOs on a named device\n" + " -? This helptext\n" + ); +} + +int main(int argc, char **argv) +{ + const char *device_name; + int ret; + int c; + + while ((c = getopt(argc, argv, "n:")) != -1) { + switch (c) { + case 'n': + device_name = optarg; + break; + case '?': + print_usage(); + return -1; + } + } + + if (device_name) + ret = list_device(device_name); + else { + const struct dirent *ent; + DIR *dp; + + /* List all GPIO devices one at a time */ + dp = opendir("/dev"); + if (!dp) { + ret = -errno; + goto error_out; + } + + ret = -ENOENT; + while (ent = readdir(dp), ent) { + if (check_prefix(ent->d_name, "gpiochip")) { + ret = list_device(ent->d_name); + if (ret) + break; + } + } + + ret = 0; + if (closedir(dp) == -1) { + perror("scanning devices: Failed to close directory"); + ret = -errno; + } + } +error_out: + return ret; +} |