diff options
-rw-r--r-- | Documentation/devicetree/bindings/gpio/gpio-ts4800.txt | 20 | ||||
-rw-r--r-- | MAINTAINERS | 12 | ||||
-rw-r--r-- | arch/blackfin/kernel/bfin_gpio.c | 2 | ||||
-rw-r--r-- | arch/powerpc/sysdev/ppc4xx_gpio.c | 2 | ||||
-rw-r--r-- | arch/powerpc/sysdev/simple_gpio.c | 2 | ||||
-rw-r--r-- | arch/unicore32/kernel/gpio.c | 2 | ||||
-rw-r--r-- | drivers/gpio/Kconfig | 37 | ||||
-rw-r--r-- | drivers/gpio/Makefile | 3 | ||||
-rw-r--r-- | drivers/gpio/gpio-104-dio-48e.c | 439 | ||||
-rw-r--r-- | drivers/gpio/gpio-104-idi-48.c | 3 | ||||
-rw-r--r-- | drivers/gpio/gpio-davinci.c | 3 | ||||
-rw-r--r-- | drivers/gpio/gpio-f7188x.c | 27 | ||||
-rw-r--r-- | drivers/gpio/gpio-mpc8xxx.c | 259 | ||||
-rw-r--r-- | drivers/gpio/gpio-ts4800.c | 94 | ||||
-rw-r--r-- | drivers/gpio/gpio-ws16c48.c | 436 | ||||
-rw-r--r-- | drivers/soc/fsl/qe/gpio.c | 2 |
16 files changed, 1168 insertions, 175 deletions
diff --git a/Documentation/devicetree/bindings/gpio/gpio-ts4800.txt b/Documentation/devicetree/bindings/gpio/gpio-ts4800.txt new file mode 100644 index 0000000..92ea9c8 --- /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/MAINTAINERS b/MAINTAINERS index 7df638b..7b8c058 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 @@ -11875,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/blackfin/kernel/bfin_gpio.c b/arch/blackfin/kernel/bfin_gpio.c index a017359..84baa62 100644 --- a/arch/blackfin/kernel/bfin_gpio.c +++ b/arch/blackfin/kernel/bfin_gpio.c @@ -1159,7 +1159,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) diff --git a/arch/powerpc/sysdev/ppc4xx_gpio.c b/arch/powerpc/sysdev/ppc4xx_gpio.c index fc65ad1..d7a7ef13 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 ff5e732..56ce8ca 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/unicore32/kernel/gpio.c b/arch/unicore32/kernel/gpio.c index cb12ec3..5ab2379 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/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index c88dd24..77df866 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -281,12 +281,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 +382,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 +496,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 +524,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 +588,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" diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index ece7d7c..2f5852d 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 @@ -98,6 +99,7 @@ obj-$(CONFIG_GPIO_PALMAS) += gpio-palmas.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 +113,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 0000000..279de39 --- /dev/null +++ b/drivers/gpio/gpio-104-dio-48e.c @@ -0,0 +1,439 @@ +/* + * 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 + * @extent: extent of port address region 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 extent; + 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 (!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; + } + + 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->extent = extent; + 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); + goto err_gpio_register; + } + + /* 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_irqchip_add; + } + + err = request_irq(irq, dio48e_irq_handler, 0, name, dio48egpio); + if (err) { + dev_err(dev, "IRQ handler registering failed (%d)\n", err); + goto err_request_irq; + } + + return 0; + +err_request_irq: +err_gpiochip_irqchip_add: + gpiochip_remove(&dio48egpio->chip); +err_gpio_register: + release_region(base, extent); +err_lock_io_port: + 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); + release_region(dio48egpio->base, dio48egpio->extent); + + 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"); diff --git a/drivers/gpio/gpio-104-idi-48.c b/drivers/gpio/gpio-104-idi-48.c index 52eed32..285a075 100644 --- a/drivers/gpio/gpio-104-idi-48.c +++ b/drivers/gpio/gpio-104-idi-48.c @@ -267,7 +267,8 @@ static int __init idi_48_probe(struct platform_device *pdev) goto err_gpiochip_irqchip_add; } - 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; diff --git a/drivers/gpio/gpio-davinci.c b/drivers/gpio/gpio-davinci.c index ec58f42..845edff 100644 --- a/drivers/gpio/gpio-davinci.c +++ b/drivers/gpio/gpio-davinci.c @@ -432,8 +432,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 d62fd6b..0417798 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-mpc8xxx.c b/drivers/gpio/gpio-mpc8xxx.c index 9d40787..ecdb27a 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-ts4800.c b/drivers/gpio/gpio-ts4800.c new file mode 100644 index 0000000..c4908a4 --- /dev/null +++ b/drivers/gpio/gpio-ts4800.c @@ -0,0 +1,94 @@ +/* + * 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, + BGPIOF_BIG_ENDIAN & BGPIOF_BIG_ENDIAN_BYTE_ORDER); + 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 0000000..9dd9479 --- /dev/null +++ b/drivers/gpio/gpio-ws16c48.c @@ -0,0 +1,436 @@ +/* + * 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 + * @extent: extent of port address region 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 extent; + 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 (!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; + } + + 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->extent = extent; + 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); + goto err_gpio_register; + } + + /* 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_irqchip_add; + } + + 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_request_irq; + } + + return 0; + +err_request_irq: +err_gpiochip_irqchip_add: + gpiochip_remove(&ws16c48gpio->chip); +err_gpio_register: + release_region(base, extent); +err_lock_io_port: + 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); + release_region(ws16c48gpio->base, ws16c48gpio->extent); + + 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"); diff --git a/drivers/soc/fsl/qe/gpio.c b/drivers/soc/fsl/qe/gpio.c index aa5c11ac..6584571 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) |