diff --git a/Documentation/devicetree/bindings/gpio/spear_spics.txt b/Documentation/devicetree/bindings/gpio/spear_spics.txt new file mode 100644 index 000000000000..96c37eb15075 --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/spear_spics.txt @@ -0,0 +1,50 @@ +=== ST Microelectronics SPEAr SPI CS Driver === + +SPEAr platform provides a provision to control chipselects of ARM PL022 Prime +Cell spi controller through its system registers, which otherwise remains under +PL022 control. If chipselect remain under PL022 control then they would be +released as soon as transfer is over and TxFIFO becomes empty. This is not +desired by some of the device protocols above spi which expect (multiple) +transfers without releasing their chipselects. + +Chipselects can be controlled by software by turning them as GPIOs. SPEAr +provides another interface through system registers through which software can +directly control each PL022 chipselect. Hence, it is natural for SPEAr to export +the control of this interface as gpio. + +Required properties: + + * compatible: should be defined as "st,spear-spics-gpio" + * reg: mentioning address range of spics controller + * st-spics,peripcfg-reg: peripheral configuration register offset + * st-spics,sw-enable-bit: bit offset to enable sw control + * st-spics,cs-value-bit: bit offset to drive chipselect low or high + * st-spics,cs-enable-mask: chip select number bit mask + * st-spics,cs-enable-shift: chip select number program offset + * gpio-controller: Marks the device node as gpio controller + * #gpio-cells: should be 1 and will mention chip select number + +All the above bit offsets are within peripcfg register. + +Example: +------- +spics: spics@e0700000{ + compatible = "st,spear-spics-gpio"; + reg = <0xe0700000 0x1000>; + st-spics,peripcfg-reg = <0x3b0>; + st-spics,sw-enable-bit = <12>; + st-spics,cs-value-bit = <11>; + st-spics,cs-enable-mask = <3>; + st-spics,cs-enable-shift = <8>; + gpio-controller; + #gpio-cells = <2>; +}; + + +spi0: spi@e0100000 { + status = "okay"; + num-cs = <3>; + cs-gpios = <&gpio1 7 0>, <&spics 0>, + <&spics 1>; + ... +} diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index ade7e924bef5..b673d65449f8 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -364,6 +364,7 @@ config ARCH_CNS3XXX config ARCH_CLPS711X bool "Cirrus Logic CLPS711x/EP721x/EP731x-based" + select ARCH_REQUIRE_GPIOLIB select ARCH_USES_GETTIMEOFFSET select CLKDEV_LOOKUP select COMMON_CLK diff --git a/arch/arm/plat-spear/Kconfig b/arch/arm/plat-spear/Kconfig index f8db7b2deb36..87dbd81bdf51 100644 --- a/arch/arm/plat-spear/Kconfig +++ b/arch/arm/plat-spear/Kconfig @@ -12,6 +12,7 @@ config ARCH_SPEAR13XX bool "ST SPEAr13xx with Device Tree" select ARM_GIC select CPU_V7 + select GPIO_SPEAR_SPICS select HAVE_SMP select MIGHT_HAVE_CACHE_L2X0 select PINCTRL diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 47150f5ded04..b0e67b200aa0 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -86,11 +86,26 @@ config GPIO_DA9052 help Say yes here to enable the GPIO driver for the DA9052 chip. +config GPIO_DA9055 + tristate "Dialog Semiconductor DA9055 GPIO" + depends on MFD_DA9055 + help + Say yes here to enable the GPIO driver for the DA9055 chip. + + The Dialog DA9055 PMIC chip has 3 GPIO pins that can be + be controller by this driver. + + If driver is built as a module it will be called gpio-da9055. + config GPIO_MAX730X tristate comment "Memory mapped GPIO drivers:" +config GPIO_CLPS711X + def_bool y + depends on ARCH_CLPS711X + config GPIO_GENERIC_PLATFORM tristate "Generic memory-mapped GPIO controller support (MMIO platform device)" select GPIO_GENERIC @@ -181,6 +196,13 @@ config GPIO_PXA help Say yes here to support the PXA GPIO device +config GPIO_SPEAR_SPICS + bool "ST SPEAr13xx SPI Chip Select as GPIO support" + depends on PLAT_SPEAR + select GENERIC_IRQ_CHIP + help + Say yes here to support ST SPEAr SPI Chip Select as GPIO device + config GPIO_STA2X11 bool "STA2x11/ConneXt GPIO support" depends on MFD_STA2X11 diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 9aeed6707326..a268d99f4e43 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -16,8 +16,10 @@ obj-$(CONFIG_GPIO_ADP5588) += gpio-adp5588.o obj-$(CONFIG_GPIO_AMD8111) += gpio-amd8111.o obj-$(CONFIG_GPIO_ARIZONA) += gpio-arizona.o obj-$(CONFIG_GPIO_BT8XX) += gpio-bt8xx.o +obj-$(CONFIG_GPIO_CLPS711X) += gpio-clps711x.o obj-$(CONFIG_GPIO_CS5535) += gpio-cs5535.o obj-$(CONFIG_GPIO_DA9052) += gpio-da9052.o +obj-$(CONFIG_GPIO_DA9055) += gpio-da9055.o obj-$(CONFIG_ARCH_DAVINCI) += gpio-davinci.o obj-$(CONFIG_GPIO_EM) += gpio-em.o obj-$(CONFIG_GPIO_EP93XX) += gpio-ep93xx.o @@ -57,6 +59,7 @@ obj-$(CONFIG_PLAT_SAMSUNG) += gpio-samsung.o obj-$(CONFIG_ARCH_SA1100) += gpio-sa1100.o obj-$(CONFIG_GPIO_SCH) += gpio-sch.o obj-$(CONFIG_GPIO_SODAVILLE) += gpio-sodaville.o +obj-$(CONFIG_GPIO_SPEAR_SPICS) += gpio-spear-spics.o obj-$(CONFIG_GPIO_STA2X11) += gpio-sta2x11.o obj-$(CONFIG_GPIO_STMPE) += gpio-stmpe.o obj-$(CONFIG_GPIO_STP_XWAY) += gpio-stp-xway.o diff --git a/drivers/gpio/gpio-clps711x.c b/drivers/gpio/gpio-clps711x.c new file mode 100644 index 000000000000..ce63b75b13f5 --- /dev/null +++ b/drivers/gpio/gpio-clps711x.c @@ -0,0 +1,199 @@ +/* + * CLPS711X GPIO driver + * + * Copyright (C) 2012 Alexander Shiyan + * + * 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 Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include + +#include + +#define CLPS711X_GPIO_PORTS 5 +#define CLPS711X_GPIO_NAME "gpio-clps711x" + +struct clps711x_gpio { + struct gpio_chip chip[CLPS711X_GPIO_PORTS]; + spinlock_t lock; +}; + +static void __iomem *clps711x_ports[] = { + CLPS711X_VIRT_BASE + PADR, + CLPS711X_VIRT_BASE + PBDR, + CLPS711X_VIRT_BASE + PCDR, + CLPS711X_VIRT_BASE + PDDR, + CLPS711X_VIRT_BASE + PEDR, +}; + +static void __iomem *clps711x_pdirs[] = { + CLPS711X_VIRT_BASE + PADDR, + CLPS711X_VIRT_BASE + PBDDR, + CLPS711X_VIRT_BASE + PCDDR, + CLPS711X_VIRT_BASE + PDDDR, + CLPS711X_VIRT_BASE + PEDDR, +}; + +#define clps711x_port(x) clps711x_ports[x->base / 8] +#define clps711x_pdir(x) clps711x_pdirs[x->base / 8] + +static int gpio_clps711x_get(struct gpio_chip *chip, unsigned offset) +{ + return !!(readb(clps711x_port(chip)) & (1 << offset)); +} + +static void gpio_clps711x_set(struct gpio_chip *chip, unsigned offset, + int value) +{ + int tmp; + unsigned long flags; + struct clps711x_gpio *gpio = dev_get_drvdata(chip->dev); + + spin_lock_irqsave(&gpio->lock, flags); + tmp = readb(clps711x_port(chip)) & ~(1 << offset); + if (value) + tmp |= 1 << offset; + writeb(tmp, clps711x_port(chip)); + spin_unlock_irqrestore(&gpio->lock, flags); +} + +static int gpio_clps711x_dir_in(struct gpio_chip *chip, unsigned offset) +{ + int tmp; + unsigned long flags; + struct clps711x_gpio *gpio = dev_get_drvdata(chip->dev); + + spin_lock_irqsave(&gpio->lock, flags); + tmp = readb(clps711x_pdir(chip)) & ~(1 << offset); + writeb(tmp, clps711x_pdir(chip)); + spin_unlock_irqrestore(&gpio->lock, flags); + + return 0; +} + +static int gpio_clps711x_dir_out(struct gpio_chip *chip, unsigned offset, + int value) +{ + int tmp; + unsigned long flags; + struct clps711x_gpio *gpio = dev_get_drvdata(chip->dev); + + spin_lock_irqsave(&gpio->lock, flags); + tmp = readb(clps711x_pdir(chip)) | (1 << offset); + writeb(tmp, clps711x_pdir(chip)); + tmp = readb(clps711x_port(chip)) & ~(1 << offset); + if (value) + tmp |= 1 << offset; + writeb(tmp, clps711x_port(chip)); + spin_unlock_irqrestore(&gpio->lock, flags); + + return 0; +} + +static int gpio_clps711x_dir_in_inv(struct gpio_chip *chip, unsigned offset) +{ + int tmp; + unsigned long flags; + struct clps711x_gpio *gpio = dev_get_drvdata(chip->dev); + + spin_lock_irqsave(&gpio->lock, flags); + tmp = readb(clps711x_pdir(chip)) | (1 << offset); + writeb(tmp, clps711x_pdir(chip)); + spin_unlock_irqrestore(&gpio->lock, flags); + + return 0; +} + +static int gpio_clps711x_dir_out_inv(struct gpio_chip *chip, unsigned offset, + int value) +{ + int tmp; + unsigned long flags; + struct clps711x_gpio *gpio = dev_get_drvdata(chip->dev); + + spin_lock_irqsave(&gpio->lock, flags); + tmp = readb(clps711x_pdir(chip)) & ~(1 << offset); + writeb(tmp, clps711x_pdir(chip)); + tmp = readb(clps711x_port(chip)) & ~(1 << offset); + if (value) + tmp |= 1 << offset; + writeb(tmp, clps711x_port(chip)); + spin_unlock_irqrestore(&gpio->lock, flags); + + return 0; +} + +static struct { + char *name; + int nr; + int inv_dir; +} clps711x_gpio_ports[] __initconst = { + { "PORTA", 8, 0, }, + { "PORTB", 8, 0, }, + { "PORTC", 8, 0, }, + { "PORTD", 8, 1, }, + { "PORTE", 3, 0, }, +}; + +static int __init gpio_clps711x_init(void) +{ + int i; + struct platform_device *pdev; + struct clps711x_gpio *gpio; + + pdev = platform_device_alloc(CLPS711X_GPIO_NAME, 0); + if (!pdev) { + pr_err("Cannot create platform device: %s\n", + CLPS711X_GPIO_NAME); + return -ENOMEM; + } + + platform_device_add(pdev); + + gpio = devm_kzalloc(&pdev->dev, sizeof(struct clps711x_gpio), + GFP_KERNEL); + if (!gpio) { + dev_err(&pdev->dev, "GPIO allocating memory error\n"); + platform_device_unregister(pdev); + return -ENOMEM; + } + + platform_set_drvdata(pdev, gpio); + + spin_lock_init(&gpio->lock); + + for (i = 0; i < CLPS711X_GPIO_PORTS; i++) { + gpio->chip[i].owner = THIS_MODULE; + gpio->chip[i].dev = &pdev->dev; + gpio->chip[i].label = clps711x_gpio_ports[i].name; + gpio->chip[i].base = i * 8; + gpio->chip[i].ngpio = clps711x_gpio_ports[i].nr; + gpio->chip[i].get = gpio_clps711x_get; + gpio->chip[i].set = gpio_clps711x_set; + if (!clps711x_gpio_ports[i].inv_dir) { + gpio->chip[i].direction_input = gpio_clps711x_dir_in; + gpio->chip[i].direction_output = gpio_clps711x_dir_out; + } else { + gpio->chip[i].direction_input = gpio_clps711x_dir_in_inv; + gpio->chip[i].direction_output = gpio_clps711x_dir_out_inv; + } + WARN_ON(gpiochip_add(&gpio->chip[i])); + } + + dev_info(&pdev->dev, "GPIO driver initialized\n"); + + return 0; +} +arch_initcall(gpio_clps711x_init); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Alexander Shiyan "); +MODULE_DESCRIPTION("CLPS711X GPIO driver"); diff --git a/drivers/gpio/gpio-da9055.c b/drivers/gpio/gpio-da9055.c new file mode 100644 index 000000000000..55d83c7d9c7f --- /dev/null +++ b/drivers/gpio/gpio-da9055.c @@ -0,0 +1,204 @@ +/* + * GPIO Driver for Dialog DA9055 PMICs. + * + * Copyright(c) 2012 Dialog Semiconductor Ltd. + * + * Author: David Dajun Chen + * + * 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 + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ +#include +#include +#include + +#include +#include +#include + +#define DA9055_VDD_IO 0x0 +#define DA9055_PUSH_PULL 0x3 +#define DA9055_ACT_LOW 0x0 +#define DA9055_GPI 0x1 +#define DA9055_PORT_MASK 0x3 +#define DA9055_PORT_SHIFT(offset) (4 * (offset % 2)) + +#define DA9055_INPUT DA9055_GPI +#define DA9055_OUTPUT DA9055_PUSH_PULL +#define DA9055_IRQ_GPI0 3 + +struct da9055_gpio { + struct da9055 *da9055; + struct gpio_chip gp; +}; + +static inline struct da9055_gpio *to_da9055_gpio(struct gpio_chip *chip) +{ + return container_of(chip, struct da9055_gpio, gp); +} + +static int da9055_gpio_get(struct gpio_chip *gc, unsigned offset) +{ + struct da9055_gpio *gpio = to_da9055_gpio(gc); + int gpio_direction = 0; + int ret; + + /* Get GPIO direction */ + ret = da9055_reg_read(gpio->da9055, (offset >> 1) + DA9055_REG_GPIO0_1); + if (ret < 0) + return ret; + + gpio_direction = ret & (DA9055_PORT_MASK) << DA9055_PORT_SHIFT(offset); + gpio_direction >>= DA9055_PORT_SHIFT(offset); + switch (gpio_direction) { + case DA9055_INPUT: + ret = da9055_reg_read(gpio->da9055, DA9055_REG_STATUS_B); + if (ret < 0) + return ret; + break; + case DA9055_OUTPUT: + ret = da9055_reg_read(gpio->da9055, DA9055_REG_GPIO_MODE0_2); + if (ret < 0) + return ret; + } + + return ret & (1 << offset); + +} + +static void da9055_gpio_set(struct gpio_chip *gc, unsigned offset, int value) +{ + struct da9055_gpio *gpio = to_da9055_gpio(gc); + + da9055_reg_update(gpio->da9055, + DA9055_REG_GPIO_MODE0_2, + 1 << offset, + value << offset); +} + +static int da9055_gpio_direction_input(struct gpio_chip *gc, unsigned offset) +{ + struct da9055_gpio *gpio = to_da9055_gpio(gc); + unsigned char reg_byte; + + reg_byte = (DA9055_ACT_LOW | DA9055_GPI) + << DA9055_PORT_SHIFT(offset); + + return da9055_reg_update(gpio->da9055, (offset >> 1) + + DA9055_REG_GPIO0_1, + DA9055_PORT_MASK << + DA9055_PORT_SHIFT(offset), + reg_byte); +} + +static int da9055_gpio_direction_output(struct gpio_chip *gc, + unsigned offset, int value) +{ + struct da9055_gpio *gpio = to_da9055_gpio(gc); + unsigned char reg_byte; + int ret; + + reg_byte = (DA9055_VDD_IO | DA9055_PUSH_PULL) + << DA9055_PORT_SHIFT(offset); + + ret = da9055_reg_update(gpio->da9055, (offset >> 1) + + DA9055_REG_GPIO0_1, + DA9055_PORT_MASK << + DA9055_PORT_SHIFT(offset), + reg_byte); + if (ret < 0) + return ret; + + da9055_gpio_set(gc, offset, value); + + return 0; +} + +static int da9055_gpio_to_irq(struct gpio_chip *gc, u32 offset) +{ + struct da9055_gpio *gpio = to_da9055_gpio(gc); + struct da9055 *da9055 = gpio->da9055; + + return regmap_irq_get_virq(da9055->irq_data, + DA9055_IRQ_GPI0 + offset); +} + +static struct gpio_chip reference_gp __devinitdata = { + .label = "da9055-gpio", + .owner = THIS_MODULE, + .get = da9055_gpio_get, + .set = da9055_gpio_set, + .direction_input = da9055_gpio_direction_input, + .direction_output = da9055_gpio_direction_output, + .to_irq = da9055_gpio_to_irq, + .can_sleep = 1, + .ngpio = 3, + .base = -1, +}; + +static int __devinit da9055_gpio_probe(struct platform_device *pdev) +{ + struct da9055_gpio *gpio; + struct da9055_pdata *pdata; + int ret; + + gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL); + if (gpio == NULL) + return -ENOMEM; + + gpio->da9055 = dev_get_drvdata(pdev->dev.parent); + pdata = gpio->da9055->dev->platform_data; + + gpio->gp = reference_gp; + if (pdata && pdata->gpio_base) + gpio->gp.base = pdata->gpio_base; + + ret = gpiochip_add(&gpio->gp); + if (ret < 0) { + dev_err(&pdev->dev, "Could not register gpiochip, %d\n", ret); + goto err_mem; + } + + platform_set_drvdata(pdev, gpio); + + return 0; + +err_mem: + return ret; +} + +static int __devexit da9055_gpio_remove(struct platform_device *pdev) +{ + struct da9055_gpio *gpio = platform_get_drvdata(pdev); + + return gpiochip_remove(&gpio->gp); +} + +static struct platform_driver da9055_gpio_driver = { + .probe = da9055_gpio_probe, + .remove = __devexit_p(da9055_gpio_remove), + .driver = { + .name = "da9055-gpio", + .owner = THIS_MODULE, + }, +}; + +static int __init da9055_gpio_init(void) +{ + return platform_driver_register(&da9055_gpio_driver); +} +subsys_initcall(da9055_gpio_init); + +static void __exit da9055_gpio_exit(void) +{ + platform_driver_unregister(&da9055_gpio_driver); +} +module_exit(da9055_gpio_exit); + +MODULE_AUTHOR("David Dajun Chen "); +MODULE_DESCRIPTION("DA9055 GPIO Device Driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:da9055-gpio"); diff --git a/drivers/gpio/gpio-em.c b/drivers/gpio/gpio-em.c index efb4c2d0d132..b00706329d26 100644 --- a/drivers/gpio/gpio-em.c +++ b/drivers/gpio/gpio-em.c @@ -35,7 +35,6 @@ struct em_gio_priv { void __iomem *base0; void __iomem *base1; - unsigned int irq_base; spinlock_t sense_lock; struct platform_device *pdev; struct gpio_chip gpio_chip; @@ -214,7 +213,7 @@ static int em_gio_direction_output(struct gpio_chip *chip, unsigned offset, static int em_gio_to_irq(struct gpio_chip *chip, unsigned offset) { - return irq_find_mapping(gpio_to_priv(chip)->irq_domain, offset); + return irq_create_mapping(gpio_to_priv(chip)->irq_domain, offset); } static int em_gio_irq_domain_map(struct irq_domain *h, unsigned int virq, @@ -234,40 +233,6 @@ static struct irq_domain_ops em_gio_irq_domain_ops = { .map = em_gio_irq_domain_map, }; -static int __devinit em_gio_irq_domain_init(struct em_gio_priv *p) -{ - struct platform_device *pdev = p->pdev; - struct gpio_em_config *pdata = pdev->dev.platform_data; - - p->irq_base = irq_alloc_descs(pdata->irq_base, 0, - pdata->number_of_pins, numa_node_id()); - if (p->irq_base < 0) { - dev_err(&pdev->dev, "cannot get irq_desc\n"); - return p->irq_base; - } - pr_debug("gio: hw base = %d, nr = %d, sw base = %d\n", - pdata->gpio_base, pdata->number_of_pins, p->irq_base); - - p->irq_domain = irq_domain_add_legacy(pdev->dev.of_node, - pdata->number_of_pins, - p->irq_base, 0, - &em_gio_irq_domain_ops, p); - if (!p->irq_domain) { - irq_free_descs(p->irq_base, pdata->number_of_pins); - return -ENXIO; - } - - return 0; -} - -static void em_gio_irq_domain_cleanup(struct em_gio_priv *p) -{ - struct gpio_em_config *pdata = p->pdev->dev.platform_data; - - irq_free_descs(p->irq_base, pdata->number_of_pins); - /* FIXME: irq domain wants to be freed! */ -} - static int __devinit em_gio_probe(struct platform_device *pdev) { struct gpio_em_config *pdata = pdev->dev.platform_data; @@ -334,8 +299,11 @@ static int __devinit em_gio_probe(struct platform_device *pdev) irq_chip->irq_set_type = em_gio_irq_set_type; irq_chip->flags = IRQCHIP_SKIP_SET_WAKE; - ret = em_gio_irq_domain_init(p); - if (ret) { + p->irq_domain = irq_domain_add_linear(pdev->dev.of_node, + pdata->number_of_pins, + &em_gio_irq_domain_ops, p); + if (!p->irq_domain) { + ret = -ENXIO; dev_err(&pdev->dev, "cannot initialize irq domain\n"); goto err3; } @@ -364,7 +332,7 @@ err6: err5: free_irq(irq[0]->start, pdev); err4: - em_gio_irq_domain_cleanup(p); + irq_domain_remove(p->irq_domain); err3: iounmap(p->base1); err2: @@ -390,7 +358,7 @@ static int __devexit em_gio_remove(struct platform_device *pdev) free_irq(irq[1]->start, pdev); free_irq(irq[0]->start, pdev); - em_gio_irq_domain_cleanup(p); + irq_domain_remove(p->irq_domain); iounmap(p->base1); iounmap(p->base0); kfree(p); diff --git a/drivers/gpio/gpio-max730x.c b/drivers/gpio/gpio-max730x.c index 05e2dac60b3b..c4bf86abd4d8 100644 --- a/drivers/gpio/gpio-max730x.c +++ b/drivers/gpio/gpio-max730x.c @@ -167,10 +167,6 @@ int __devinit __max730x_probe(struct max7301 *ts) int i, ret; pdata = dev->platform_data; - if (!pdata || !pdata->base) { - dev_err(dev, "incorrect or missing platform data\n"); - return -EINVAL; - } mutex_init(&ts->lock); dev_set_drvdata(dev, ts); @@ -178,7 +174,12 @@ int __devinit __max730x_probe(struct max7301 *ts) /* Power up the chip and disable IRQ output */ ts->write(dev, 0x04, 0x01); - ts->input_pullup_active = pdata->input_pullup_active; + if (pdata) { + ts->input_pullup_active = pdata->input_pullup_active; + ts->chip.base = pdata->base; + } else { + ts->chip.base = -1; + } ts->chip.label = dev->driver->name; ts->chip.direction_input = max7301_direction_input; @@ -186,7 +187,6 @@ int __devinit __max730x_probe(struct max7301 *ts) ts->chip.direction_output = max7301_direction_output; ts->chip.set = max7301_set; - ts->chip.base = pdata->base; ts->chip.ngpio = PIN_NUMBER; ts->chip.can_sleep = 1; ts->chip.dev = dev; diff --git a/drivers/gpio/gpio-mvebu.c b/drivers/gpio/gpio-mvebu.c index be65c0451ad5..a515b9294e92 100644 --- a/drivers/gpio/gpio-mvebu.c +++ b/drivers/gpio/gpio-mvebu.c @@ -168,12 +168,12 @@ static void __iomem *mvebu_gpioreg_level_mask(struct mvebu_gpio_chip *mvchip) * Functions implementing the gpio_chip methods */ -int mvebu_gpio_request(struct gpio_chip *chip, unsigned pin) +static int mvebu_gpio_request(struct gpio_chip *chip, unsigned pin) { return pinctrl_request_gpio(chip->base + pin); } -void mvebu_gpio_free(struct gpio_chip *chip, unsigned pin) +static void mvebu_gpio_free(struct gpio_chip *chip, unsigned pin) { pinctrl_free_gpio(chip->base + pin); } @@ -546,6 +546,7 @@ static int __devinit mvebu_gpio_probe(struct platform_device *pdev) mvchip->chip.label = dev_name(&pdev->dev); mvchip->chip.dev = &pdev->dev; mvchip->chip.request = mvebu_gpio_request; + mvchip->chip.free = mvebu_gpio_free; mvchip->chip.direction_input = mvebu_gpio_direction_input; mvchip->chip.get = mvebu_gpio_get; mvchip->chip.direction_output = mvebu_gpio_direction_output; @@ -673,8 +674,8 @@ static int __devinit mvebu_gpio_probe(struct platform_device *pdev) IRQ_NOREQUEST, IRQ_LEVEL | IRQ_NOPROBE); /* Setup irq domain on top of the generic chip. */ - mvchip->domain = irq_domain_add_legacy(np, mvchip->chip.ngpio, - mvchip->irqbase, 0, + mvchip->domain = irq_domain_add_simple(np, mvchip->chip.ngpio, + mvchip->irqbase, &irq_domain_simple_ops, mvchip); if (!mvchip->domain) { diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c index d335af1d4d85..d71e5bdf7b97 100644 --- a/drivers/gpio/gpio-omap.c +++ b/drivers/gpio/gpio-omap.c @@ -1105,7 +1105,7 @@ static int __devinit omap_gpio_probe(struct platform_device *pdev) if (!pdata) return -EINVAL; - bank = devm_kzalloc(&pdev->dev, sizeof(struct gpio_bank), GFP_KERNEL); + bank = devm_kzalloc(dev, sizeof(struct gpio_bank), GFP_KERNEL); if (!bank) { dev_err(dev, "Memory alloc failed\n"); return -ENOMEM; diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index 9c693ae17956..0c5eaf5f4c90 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -83,6 +84,7 @@ struct pca953x_chip { u32 irq_trig_raise; u32 irq_trig_fall; int irq_base; + struct irq_domain *domain; #endif struct i2c_client *client; @@ -333,14 +335,14 @@ static void pca953x_irq_mask(struct irq_data *d) { struct pca953x_chip *chip = irq_data_get_irq_chip_data(d); - chip->irq_mask &= ~(1 << (d->irq - chip->irq_base)); + chip->irq_mask &= ~(1 << d->hwirq); } static void pca953x_irq_unmask(struct irq_data *d) { struct pca953x_chip *chip = irq_data_get_irq_chip_data(d); - chip->irq_mask |= 1 << (d->irq - chip->irq_base); + chip->irq_mask |= 1 << d->hwirq; } static void pca953x_irq_bus_lock(struct irq_data *d) @@ -372,8 +374,7 @@ static void pca953x_irq_bus_sync_unlock(struct irq_data *d) static int pca953x_irq_set_type(struct irq_data *d, unsigned int type) { struct pca953x_chip *chip = irq_data_get_irq_chip_data(d); - u32 level = d->irq - chip->irq_base; - u32 mask = 1 << level; + u32 mask = 1 << d->hwirq; if (!(type & IRQ_TYPE_EDGE_BOTH)) { dev_err(&chip->client->dev, "irq %d: unsupported type %d\n", @@ -454,7 +455,7 @@ static irqreturn_t pca953x_irq_handler(int irq, void *devid) do { level = __ffs(pending); - handle_nested_irq(level + chip->irq_base); + handle_nested_irq(irq_find_mapping(chip->domain, level)); pending &= ~(1 << level); } while (pending); @@ -499,6 +500,17 @@ static int pca953x_irq_setup(struct pca953x_chip *chip, if (chip->irq_base < 0) goto out_failed; + chip->domain = irq_domain_add_legacy(client->dev.of_node, + chip->gpio_chip.ngpio, + chip->irq_base, + 0, + &irq_domain_simple_ops, + NULL); + if (!chip->domain) { + ret = -ENODEV; + goto out_irqdesc_free; + } + for (lvl = 0; lvl < chip->gpio_chip.ngpio; lvl++) { int irq = lvl + chip->irq_base; @@ -521,7 +533,7 @@ static int pca953x_irq_setup(struct pca953x_chip *chip, if (ret) { dev_err(&client->dev, "failed to request irq %d\n", client->irq); - goto out_failed; + goto out_irqdesc_free; } chip->gpio_chip.to_irq = pca953x_gpio_to_irq; @@ -529,6 +541,8 @@ static int pca953x_irq_setup(struct pca953x_chip *chip, return 0; +out_irqdesc_free: + irq_free_descs(chip->irq_base, chip->gpio_chip.ngpio); out_failed: chip->irq_base = -1; return ret; @@ -751,9 +765,38 @@ static int pca953x_remove(struct i2c_client *client) return 0; } +static const struct of_device_id pca953x_dt_ids[] = { + { .compatible = "nxp,pca9534", }, + { .compatible = "nxp,pca9535", }, + { .compatible = "nxp,pca9536", }, + { .compatible = "nxp,pca9537", }, + { .compatible = "nxp,pca9538", }, + { .compatible = "nxp,pca9539", }, + { .compatible = "nxp,pca9554", }, + { .compatible = "nxp,pca9555", }, + { .compatible = "nxp,pca9556", }, + { .compatible = "nxp,pca9557", }, + { .compatible = "nxp,pca9574", }, + { .compatible = "nxp,pca9575", }, + + { .compatible = "maxim,max7310", }, + { .compatible = "maxim,max7312", }, + { .compatible = "maxim,max7313", }, + { .compatible = "maxim,max7315", }, + + { .compatible = "ti,pca6107", }, + { .compatible = "ti,tca6408", }, + { .compatible = "ti,tca6416", }, + { .compatible = "ti,tca6424", }, + { } +}; + +MODULE_DEVICE_TABLE(of, pca953x_dt_ids); + static struct i2c_driver pca953x_driver = { .driver = { .name = "pca953x", + .of_match_table = pca953x_dt_ids, }, .probe = pca953x_probe, .remove = pca953x_remove, diff --git a/drivers/gpio/gpio-pch.c b/drivers/gpio/gpio-pch.c index 4ad0c4f9171c..e3a14fef79e1 100644 --- a/drivers/gpio/gpio-pch.c +++ b/drivers/gpio/gpio-pch.c @@ -215,6 +215,7 @@ static void pch_gpio_setup(struct pch_gpio *chip) struct gpio_chip *gpio = &chip->gpio; gpio->label = dev_name(chip->dev); + gpio->dev = chip->dev; gpio->owner = THIS_MODULE; gpio->direction_input = pch_gpio_direction_input; gpio->get = pch_gpio_get; diff --git a/drivers/gpio/gpio-pl061.c b/drivers/gpio/gpio-pl061.c index b4b5da4fd2cc..31d9c9e79ea9 100644 --- a/drivers/gpio/gpio-pl061.c +++ b/drivers/gpio/gpio-pl061.c @@ -216,39 +216,34 @@ static void __init pl061_init_gc(struct pl061_gpio *chip, int irq_base) IRQ_GC_INIT_NESTED_LOCK, IRQ_NOREQUEST, 0); } -static int pl061_probe(struct amba_device *dev, const struct amba_id *id) +static int pl061_probe(struct amba_device *adev, const struct amba_id *id) { - struct pl061_platform_data *pdata; + struct device *dev = &adev->dev; + struct pl061_platform_data *pdata = dev->platform_data; struct pl061_gpio *chip; int ret, irq, i; - chip = kzalloc(sizeof(*chip), GFP_KERNEL); + chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); if (chip == NULL) return -ENOMEM; - pdata = dev->dev.platform_data; if (pdata) { chip->gc.base = pdata->gpio_base; chip->irq_base = pdata->irq_base; - } else if (dev->dev.of_node) { + } else if (adev->dev.of_node) { chip->gc.base = -1; chip->irq_base = 0; - } else { - ret = -ENODEV; - goto free_mem; - } + } else + return -ENODEV; - if (!request_mem_region(dev->res.start, - resource_size(&dev->res), "pl061")) { - ret = -EBUSY; - goto free_mem; - } + if (!devm_request_mem_region(dev, adev->res.start, + resource_size(&adev->res), "pl061")) + return -EBUSY; - chip->base = ioremap(dev->res.start, resource_size(&dev->res)); - if (chip->base == NULL) { - ret = -ENOMEM; - goto release_region; - } + chip->base = devm_ioremap(dev, adev->res.start, + resource_size(&adev->res)); + if (chip->base == NULL) + return -ENOMEM; spin_lock_init(&chip->lock); @@ -258,13 +253,13 @@ static int pl061_probe(struct amba_device *dev, const struct amba_id *id) chip->gc.set = pl061_set_value; chip->gc.to_irq = pl061_to_irq; chip->gc.ngpio = PL061_GPIO_NR; - chip->gc.label = dev_name(&dev->dev); - chip->gc.dev = &dev->dev; + chip->gc.label = dev_name(dev); + chip->gc.dev = dev; chip->gc.owner = THIS_MODULE; ret = gpiochip_add(&chip->gc); if (ret) - goto iounmap; + return ret; /* * irq_chip support @@ -276,11 +271,10 @@ static int pl061_probe(struct amba_device *dev, const struct amba_id *id) pl061_init_gc(chip, chip->irq_base); writeb(0, chip->base + GPIOIE); /* disable irqs */ - irq = dev->irq[0]; - if (irq < 0) { - ret = -ENODEV; - goto iounmap; - } + irq = adev->irq[0]; + if (irq < 0) + return -ENODEV; + irq_set_chained_handler(irq, pl061_irq_handler); irq_set_handler_data(irq, chip); @@ -294,18 +288,9 @@ static int pl061_probe(struct amba_device *dev, const struct amba_id *id) } } - amba_set_drvdata(dev, chip); + amba_set_drvdata(adev, chip); return 0; - -iounmap: - iounmap(chip->base); -release_region: - release_mem_region(dev->res.start, resource_size(&dev->res)); -free_mem: - kfree(chip); - - return ret; } #ifdef CONFIG_PM diff --git a/drivers/gpio/gpio-spear-spics.c b/drivers/gpio/gpio-spear-spics.c new file mode 100644 index 000000000000..5f45fc4ed5d1 --- /dev/null +++ b/drivers/gpio/gpio-spear-spics.c @@ -0,0 +1,217 @@ +/* + * SPEAr platform SPI chipselect abstraction over gpiolib + * + * Copyright (C) 2012 ST Microelectronics + * Shiraz Hashim + * + * 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 +#include +#include +#include +#include +#include +#include + +/* maximum chipselects */ +#define NUM_OF_GPIO 4 + +/* + * Provision is available on some SPEAr SoCs to control ARM PL022 spi cs + * through system registers. This register lies outside spi (pl022) + * address space into system registers. + * + * It provides control for spi chip select lines so that any chipselect + * (out of 4 possible chipselects in pl022) can be made low to select + * the particular slave. + */ + +/** + * struct spear_spics - represents spi chip select control + * @base: base address + * @perip_cfg: configuration register + * @sw_enable_bit: bit to enable s/w control over chipselects + * @cs_value_bit: bit to program high or low chipselect + * @cs_enable_mask: mask to select bits required to select chipselect + * @cs_enable_shift: bit pos of cs_enable_mask + * @use_count: use count of a spi controller cs lines + * @last_off: stores last offset caller of set_value() + * @chip: gpio_chip abstraction + */ +struct spear_spics { + void __iomem *base; + u32 perip_cfg; + u32 sw_enable_bit; + u32 cs_value_bit; + u32 cs_enable_mask; + u32 cs_enable_shift; + unsigned long use_count; + int last_off; + struct gpio_chip chip; +}; + +/* gpio framework specific routines */ +static int spics_get_value(struct gpio_chip *chip, unsigned offset) +{ + return -ENXIO; +} + +static void spics_set_value(struct gpio_chip *chip, unsigned offset, int value) +{ + struct spear_spics *spics = container_of(chip, struct spear_spics, + chip); + u32 tmp; + + /* select chip select from register */ + tmp = readl_relaxed(spics->base + spics->perip_cfg); + if (spics->last_off != offset) { + spics->last_off = offset; + tmp &= ~(spics->cs_enable_mask << spics->cs_enable_shift); + tmp |= offset << spics->cs_enable_shift; + } + + /* toggle chip select line */ + tmp &= ~(0x1 << spics->cs_value_bit); + tmp |= value << spics->cs_value_bit; + writel_relaxed(tmp, spics->base + spics->perip_cfg); +} + +static int spics_direction_input(struct gpio_chip *chip, unsigned offset) +{ + return -ENXIO; +} + +static int spics_direction_output(struct gpio_chip *chip, unsigned offset, + int value) +{ + spics_set_value(chip, offset, value); + return 0; +} + +static int spics_request(struct gpio_chip *chip, unsigned offset) +{ + struct spear_spics *spics = container_of(chip, struct spear_spics, + chip); + u32 tmp; + + if (!spics->use_count++) { + tmp = readl_relaxed(spics->base + spics->perip_cfg); + tmp |= 0x1 << spics->sw_enable_bit; + tmp |= 0x1 << spics->cs_value_bit; + writel_relaxed(tmp, spics->base + spics->perip_cfg); + } + + return 0; +} + +static void spics_free(struct gpio_chip *chip, unsigned offset) +{ + struct spear_spics *spics = container_of(chip, struct spear_spics, + chip); + u32 tmp; + + if (!--spics->use_count) { + tmp = readl_relaxed(spics->base + spics->perip_cfg); + tmp &= ~(0x1 << spics->sw_enable_bit); + writel_relaxed(tmp, spics->base + spics->perip_cfg); + } +} + +static int spics_gpio_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct spear_spics *spics; + struct resource *res; + int ret; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "invalid IORESOURCE_MEM\n"); + return -EBUSY; + } + + spics = devm_kzalloc(&pdev->dev, sizeof(*spics), GFP_KERNEL); + if (!spics) { + dev_err(&pdev->dev, "memory allocation fail\n"); + return -ENOMEM; + } + + spics->base = devm_request_and_ioremap(&pdev->dev, res); + if (!spics->base) { + dev_err(&pdev->dev, "request and ioremap fail\n"); + return -ENOMEM; + } + + if (of_property_read_u32(np, "st-spics,peripcfg-reg", + &spics->perip_cfg)) + goto err_dt_data; + if (of_property_read_u32(np, "st-spics,sw-enable-bit", + &spics->sw_enable_bit)) + goto err_dt_data; + if (of_property_read_u32(np, "st-spics,cs-value-bit", + &spics->cs_value_bit)) + goto err_dt_data; + if (of_property_read_u32(np, "st-spics,cs-enable-mask", + &spics->cs_enable_mask)) + goto err_dt_data; + if (of_property_read_u32(np, "st-spics,cs-enable-shift", + &spics->cs_enable_shift)) + goto err_dt_data; + + platform_set_drvdata(pdev, spics); + + spics->chip.ngpio = NUM_OF_GPIO; + spics->chip.base = -1; + spics->chip.request = spics_request; + spics->chip.free = spics_free; + spics->chip.direction_input = spics_direction_input; + spics->chip.direction_output = spics_direction_output; + spics->chip.get = spics_get_value; + spics->chip.set = spics_set_value; + spics->chip.label = dev_name(&pdev->dev); + spics->chip.dev = &pdev->dev; + spics->chip.owner = THIS_MODULE; + spics->last_off = -1; + + ret = gpiochip_add(&spics->chip); + if (ret) { + dev_err(&pdev->dev, "unable to add gpio chip\n"); + return ret; + } + + dev_info(&pdev->dev, "spear spics registered\n"); + return 0; + +err_dt_data: + dev_err(&pdev->dev, "DT probe failed\n"); + return -EINVAL; +} + +static const struct of_device_id spics_gpio_of_match[] = { + { .compatible = "st,spear-spics-gpio" }, + {} +}; +MODULE_DEVICE_TABLE(of, spics_gpio_of_match); + +static struct platform_driver spics_gpio_driver = { + .probe = spics_gpio_probe, + .driver = { + .owner = THIS_MODULE, + .name = "spear-spics-gpio", + .of_match_table = spics_gpio_of_match, + }, +}; + +static int __init spics_gpio_init(void) +{ + return platform_driver_register(&spics_gpio_driver); +} +subsys_initcall(spics_gpio_init); + +MODULE_AUTHOR("Shiraz Hashim "); +MODULE_DESCRIPTION("ST Microlectronics SPEAr SPI Chip Select Abstraction"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/gpio-tc3589x.c b/drivers/gpio/gpio-tc3589x.c index 1e48317e70fb..8c8447c7d2a8 100644 --- a/drivers/gpio/gpio-tc3589x.c +++ b/drivers/gpio/gpio-tc3589x.c @@ -292,17 +292,15 @@ static int tc3589x_gpio_irq_init(struct tc3589x_gpio *tc3589x_gpio, { int base = tc3589x_gpio->irq_base; - if (base) { - tc3589x_gpio->domain = irq_domain_add_legacy( - NULL, tc3589x_gpio->chip.ngpio, base, - 0, &tc3589x_irq_ops, tc3589x_gpio); - } - else { - tc3589x_gpio->domain = irq_domain_add_linear( - np, tc3589x_gpio->chip.ngpio, - &tc3589x_irq_ops, tc3589x_gpio); - } - + /* + * If this results in a linear domain, irq_create_mapping() will + * take care of allocating IRQ descriptors at runtime. When a base + * is provided, the IRQ descriptors will be allocated when the + * domain is instantiated. + */ + tc3589x_gpio->domain = irq_domain_add_simple(np, + tc3589x_gpio->chip.ngpio, base, &tc3589x_irq_ops, + tc3589x_gpio); if (!tc3589x_gpio->domain) { dev_err(tc3589x_gpio->dev, "Failed to create irqdomain\n"); return -ENOSYS; diff --git a/drivers/gpio/gpio-tegra.c b/drivers/gpio/gpio-tegra.c index d982593d7563..5389be8c2b51 100644 --- a/drivers/gpio/gpio-tegra.c +++ b/drivers/gpio/gpio-tegra.c @@ -27,6 +27,7 @@ #include #include #include +#include #include @@ -64,7 +65,7 @@ struct tegra_gpio_bank { int bank; int irq; spinlock_t lvl_lock[4]; -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP u32 cnf[4]; u32 out[4]; u32 oe[4]; @@ -109,20 +110,18 @@ static void tegra_gpio_enable(int gpio) { tegra_gpio_mask_write(GPIO_MSK_CNF(gpio), gpio, 1); } -EXPORT_SYMBOL_GPL(tegra_gpio_enable); static void tegra_gpio_disable(int gpio) { tegra_gpio_mask_write(GPIO_MSK_CNF(gpio), gpio, 0); } -EXPORT_SYMBOL_GPL(tegra_gpio_disable); -int tegra_gpio_request(struct gpio_chip *chip, unsigned offset) +static int tegra_gpio_request(struct gpio_chip *chip, unsigned offset) { return pinctrl_request_gpio(offset); } -void tegra_gpio_free(struct gpio_chip *chip, unsigned offset) +static void tegra_gpio_free(struct gpio_chip *chip, unsigned offset) { pinctrl_free_gpio(offset); tegra_gpio_disable(offset); @@ -135,6 +134,11 @@ static void tegra_gpio_set(struct gpio_chip *chip, unsigned offset, int value) static int tegra_gpio_get(struct gpio_chip *chip, unsigned offset) { + /* If gpio is in output mode then read from the out value */ + if ((tegra_gpio_readl(GPIO_OE(offset)) >> GPIO_BIT(offset)) & 1) + return (tegra_gpio_readl(GPIO_OUT(offset)) >> + GPIO_BIT(offset)) & 0x1; + return (tegra_gpio_readl(GPIO_IN(offset)) >> GPIO_BIT(offset)) & 0x1; } @@ -285,8 +289,8 @@ static void tegra_gpio_irq_handler(unsigned int irq, struct irq_desc *desc) } -#ifdef CONFIG_PM -void tegra_gpio_resume(void) +#ifdef CONFIG_PM_SLEEP +static int tegra_gpio_resume(struct device *dev) { unsigned long flags; int b; @@ -308,9 +312,10 @@ void tegra_gpio_resume(void) } local_irq_restore(flags); + return 0; } -void tegra_gpio_suspend(void) +static int tegra_gpio_suspend(struct device *dev) { unsigned long flags; int b; @@ -330,6 +335,7 @@ void tegra_gpio_suspend(void) } } local_irq_restore(flags); + return 0; } static int tegra_gpio_wake_enable(struct irq_data *d, unsigned int enable) @@ -345,11 +351,15 @@ static struct irq_chip tegra_gpio_irq_chip = { .irq_mask = tegra_gpio_irq_mask, .irq_unmask = tegra_gpio_irq_unmask, .irq_set_type = tegra_gpio_irq_set_type, -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP .irq_set_wake = tegra_gpio_wake_enable, #endif }; +static const struct dev_pm_ops tegra_gpio_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(tegra_gpio_suspend, tegra_gpio_resume) +}; + struct tegra_gpio_soc_config { u32 bank_stride; u32 upper_offset; @@ -380,7 +390,6 @@ static int __devinit tegra_gpio_probe(struct platform_device *pdev) { const struct of_device_id *match; struct tegra_gpio_soc_config *config; - int irq_base; struct resource *res; struct tegra_gpio_bank *bank; int gpio; @@ -417,14 +426,11 @@ static int __devinit tegra_gpio_probe(struct platform_device *pdev) return -ENODEV; } - irq_base = irq_alloc_descs(-1, 0, tegra_gpio_chip.ngpio, 0); - if (irq_base < 0) { - dev_err(&pdev->dev, "Couldn't allocate IRQ numbers\n"); - return -ENODEV; - } - irq_domain = irq_domain_add_legacy(pdev->dev.of_node, - tegra_gpio_chip.ngpio, irq_base, 0, + irq_domain = irq_domain_add_linear(pdev->dev.of_node, + tegra_gpio_chip.ngpio, &irq_domain_simple_ops, NULL); + if (!irq_domain) + return -ENODEV; for (i = 0; i < tegra_gpio_bank_count; i++) { res = platform_get_resource(pdev, IORESOURCE_IRQ, i); @@ -464,7 +470,7 @@ static int __devinit tegra_gpio_probe(struct platform_device *pdev) gpiochip_add(&tegra_gpio_chip); for (gpio = 0; gpio < tegra_gpio_chip.ngpio; gpio++) { - int irq = irq_find_mapping(irq_domain, gpio); + int irq = irq_create_mapping(irq_domain, gpio); /* No validity check; all Tegra GPIOs are valid IRQs */ bank = &tegra_gpio_banks[GPIO_BANK(gpio)]; @@ -493,6 +499,7 @@ static struct platform_driver tegra_gpio_driver = { .driver = { .name = "tegra-gpio", .owner = THIS_MODULE, + .pm = &tegra_gpio_pm_ops, .of_match_table = tegra_gpio_of_match, }, .probe = tegra_gpio_probe, diff --git a/drivers/gpio/gpio-twl4030.c b/drivers/gpio/gpio-twl4030.c index c5f8ca233e1f..d2138b0fd4ca 100644 --- a/drivers/gpio/gpio-twl4030.c +++ b/drivers/gpio/gpio-twl4030.c @@ -88,11 +88,15 @@ static inline int gpio_twl4030_write(u8 address, u8 data) /*----------------------------------------------------------------------*/ /* - * LED register offsets (use TWL4030_MODULE_{LED,PWMA,PWMB})) + * LED register offsets from TWL_MODULE_LED base * PWMs A and B are dedicated to LEDs A and B, respectively. */ -#define TWL4030_LED_LEDEN 0x0 +#define TWL4030_LED_LEDEN_REG 0x00 +#define TWL4030_PWMAON_REG 0x01 +#define TWL4030_PWMAOFF_REG 0x02 +#define TWL4030_PWMBON_REG 0x03 +#define TWL4030_PWMBOFF_REG 0x04 /* LEDEN bits */ #define LEDEN_LEDAON BIT(0) @@ -104,9 +108,6 @@ static inline int gpio_twl4030_write(u8 address, u8 data) #define LEDEN_PWM_LENGTHA BIT(6) #define LEDEN_PWM_LENGTHB BIT(7) -#define TWL4030_PWMx_PWMxON 0x0 -#define TWL4030_PWMx_PWMxOFF 0x1 - #define PWMxON_LENGTH BIT(7) /*----------------------------------------------------------------------*/ @@ -145,7 +146,7 @@ static void twl4030_led_set_value(int led, int value) else cached_leden |= mask; status = twl_i2c_write_u8(TWL4030_MODULE_LED, cached_leden, - TWL4030_LED_LEDEN); + TWL4030_LED_LEDEN_REG); mutex_unlock(&gpio_lock); } @@ -216,33 +217,33 @@ static int twl_request(struct gpio_chip *chip, unsigned offset) if (offset >= TWL4030_GPIO_MAX) { u8 ledclr_mask = LEDEN_LEDAON | LEDEN_LEDAEXT | LEDEN_LEDAPWM | LEDEN_PWM_LENGTHA; - u8 module = TWL4030_MODULE_PWMA; + u8 reg = TWL4030_PWMAON_REG; offset -= TWL4030_GPIO_MAX; if (offset) { ledclr_mask <<= 1; - module = TWL4030_MODULE_PWMB; + reg = TWL4030_PWMBON_REG; } /* initialize PWM to always-drive */ - status = twl_i2c_write_u8(module, 0x7f, - TWL4030_PWMx_PWMxOFF); + /* Configure PWM OFF register first */ + status = twl_i2c_write_u8(TWL4030_MODULE_LED, 0x7f, reg + 1); if (status < 0) goto done; - status = twl_i2c_write_u8(module, 0x7f, - TWL4030_PWMx_PWMxON); + + /* Followed by PWM ON register */ + status = twl_i2c_write_u8(TWL4030_MODULE_LED, 0x7f, reg); if (status < 0) goto done; /* init LED to not-driven (high) */ - module = TWL4030_MODULE_LED; - status = twl_i2c_read_u8(module, &cached_leden, - TWL4030_LED_LEDEN); + status = twl_i2c_read_u8(TWL4030_MODULE_LED, &cached_leden, + TWL4030_LED_LEDEN_REG); if (status < 0) goto done; cached_leden &= ~ledclr_mask; - status = twl_i2c_write_u8(module, cached_leden, - TWL4030_LED_LEDEN); + status = twl_i2c_write_u8(TWL4030_MODULE_LED, cached_leden, + TWL4030_LED_LEDEN_REG); if (status < 0) goto done; diff --git a/drivers/gpio/gpio-vt8500.c b/drivers/gpio/gpio-vt8500.c index bcd8e4aa7c7d..9ed2a2b347fa 100644 --- a/drivers/gpio/gpio-vt8500.c +++ b/drivers/gpio/gpio-vt8500.c @@ -96,6 +96,7 @@ static struct vt8500_gpio_data wm8505_data = { VT8500_BANK(0x5C, 0x84, 0xAC, 0xD4, 12), VT8500_BANK(0x60, 0x88, 0xB0, 0xD8, 16), VT8500_BANK(0x64, 0x8C, 0xB4, 0xDC, 22), + VT8500_BANK(0x500, 0x504, 0x508, 0x50C, 6), }, }; @@ -115,6 +116,7 @@ static struct vt8500_gpio_data wm8650_data = { VT8500_BANK(0x58, 0x98, 0xD8, 0x18, 32), VT8500_BANK(0x5C, 0x9C, 0xDC, 0x1C, 32), VT8500_BANK(0x7C, 0xBC, 0xFC, 0x3C, 32), + VT8500_BANK(0x500, 0x504, 0x508, 0x50C, 6), }, }; diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index c5f650095faa..b667f768c23b 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -191,6 +191,32 @@ err: return ret; } +/* caller ensures gpio is valid and requested, chip->get_direction may sleep */ +static int gpio_get_direction(unsigned gpio) +{ + struct gpio_chip *chip; + struct gpio_desc *desc = &gpio_desc[gpio]; + int status = -EINVAL; + + chip = gpio_to_chip(gpio); + gpio -= chip->base; + + if (!chip->get_direction) + return status; + + status = chip->get_direction(chip, gpio); + if (status > 0) { + /* GPIOF_DIR_IN, or other positive */ + status = 1; + clear_bit(FLAG_IS_OUT, &desc->flags); + } + if (status == 0) { + /* GPIOF_DIR_OUT */ + set_bit(FLAG_IS_OUT, &desc->flags); + } + return status; +} + #ifdef CONFIG_GPIO_SYSFS /* lock protects against unexport_gpio() being called while @@ -223,6 +249,7 @@ static ssize_t gpio_direction_show(struct device *dev, struct device_attribute *attr, char *buf) { const struct gpio_desc *desc = dev_get_drvdata(dev); + unsigned gpio = desc - gpio_desc; ssize_t status; mutex_lock(&sysfs_lock); @@ -230,6 +257,7 @@ static ssize_t gpio_direction_show(struct device *dev, if (!test_bit(FLAG_EXPORT, &desc->flags)) status = -EIO; else + gpio_get_direction(gpio); status = sprintf(buf, "%s\n", test_bit(FLAG_IS_OUT, &desc->flags) ? "out" : "in"); @@ -704,8 +732,9 @@ int gpio_export(unsigned gpio, bool direction_may_change) { unsigned long flags; struct gpio_desc *desc; - int status = -EINVAL; + int status; const char *ioname = NULL; + struct device *dev; /* can't export until sysfs is available ... */ if (!gpio_class.p) { @@ -713,59 +742,66 @@ int gpio_export(unsigned gpio, bool direction_may_change) return -ENOENT; } - if (!gpio_is_valid(gpio)) - goto done; + if (!gpio_is_valid(gpio)) { + pr_debug("%s: gpio %d is not valid\n", __func__, gpio); + return -EINVAL; + } mutex_lock(&sysfs_lock); spin_lock_irqsave(&gpio_lock, flags); desc = &gpio_desc[gpio]; - if (test_bit(FLAG_REQUESTED, &desc->flags) - && !test_bit(FLAG_EXPORT, &desc->flags)) { - status = 0; - if (!desc->chip->direction_input - || !desc->chip->direction_output) - direction_may_change = false; + if (!test_bit(FLAG_REQUESTED, &desc->flags) || + test_bit(FLAG_EXPORT, &desc->flags)) { + spin_unlock_irqrestore(&gpio_lock, flags); + pr_debug("%s: gpio %d unavailable (requested=%d, exported=%d)\n", + __func__, gpio, + test_bit(FLAG_REQUESTED, &desc->flags), + test_bit(FLAG_EXPORT, &desc->flags)); + status = -EPERM; + goto fail_unlock; } + + if (!desc->chip->direction_input || !desc->chip->direction_output) + direction_may_change = false; spin_unlock_irqrestore(&gpio_lock, flags); if (desc->chip->names && desc->chip->names[gpio - desc->chip->base]) ioname = desc->chip->names[gpio - desc->chip->base]; - if (status == 0) { - struct device *dev; - - dev = device_create(&gpio_class, desc->chip->dev, MKDEV(0, 0), - desc, ioname ? ioname : "gpio%u", gpio); - if (!IS_ERR(dev)) { - status = sysfs_create_group(&dev->kobj, - &gpio_attr_group); - - if (!status && direction_may_change) - status = device_create_file(dev, - &dev_attr_direction); - - if (!status && gpio_to_irq(gpio) >= 0 - && (direction_may_change - || !test_bit(FLAG_IS_OUT, - &desc->flags))) - status = device_create_file(dev, - &dev_attr_edge); - - if (status != 0) - device_unregister(dev); - } else - status = PTR_ERR(dev); - if (status == 0) - set_bit(FLAG_EXPORT, &desc->flags); + dev = device_create(&gpio_class, desc->chip->dev, MKDEV(0, 0), + desc, ioname ? ioname : "gpio%u", gpio); + if (IS_ERR(dev)) { + status = PTR_ERR(dev); + goto fail_unlock; } - mutex_unlock(&sysfs_lock); - -done: + status = sysfs_create_group(&dev->kobj, &gpio_attr_group); if (status) - pr_debug("%s: gpio%d status %d\n", __func__, gpio, status); + goto fail_unregister_device; + if (direction_may_change) { + status = device_create_file(dev, &dev_attr_direction); + if (status) + goto fail_unregister_device; + } + + if (gpio_to_irq(gpio) >= 0 && (direction_may_change || + !test_bit(FLAG_IS_OUT, &desc->flags))) { + status = device_create_file(dev, &dev_attr_edge); + if (status) + goto fail_unregister_device; + } + + set_bit(FLAG_EXPORT, &desc->flags); + mutex_unlock(&sysfs_lock); + return 0; + +fail_unregister_device: + device_unregister(dev); +fail_unlock: + mutex_unlock(&sysfs_lock); + pr_debug("%s: gpio%d status %d\n", __func__, gpio, status); return status; } EXPORT_SYMBOL_GPL(gpio_export); @@ -1075,6 +1111,7 @@ int gpiochip_add(struct gpio_chip *chip) * inputs (often with pullups enabled) so power * usage is minimized. Linux code should set the * gpio direction first thing; but until it does, + * and in case chip->get_direction is not set, * we may expose the wrong direction in sysfs. */ gpio_desc[id].flags = !chip->direction_input @@ -1274,9 +1311,15 @@ int gpio_request(unsigned gpio, const char *label) desc_set_label(desc, NULL); module_put(chip->owner); clear_bit(FLAG_REQUESTED, &desc->flags); + goto done; } } - + if (chip->get_direction) { + /* chip->get_direction may sleep */ + spin_unlock_irqrestore(&gpio_lock, flags); + gpio_get_direction(gpio); + spin_lock_irqsave(&gpio_lock, flags); + } done: if (status) pr_debug("gpio_request: gpio-%d (%s) status %d\n", @@ -1812,6 +1855,7 @@ static void gpiolib_dbg_show(struct seq_file *s, struct gpio_chip *chip) if (!test_bit(FLAG_REQUESTED, &gdesc->flags)) continue; + gpio_get_direction(gpio); is_out = test_bit(FLAG_IS_OUT, &gdesc->flags); seq_printf(s, " gpio-%-3d (%-20.20s) %s %s", gpio, gdesc->label, diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h index 2b84fc32fae2..b6516f32280d 100644 --- a/include/asm-generic/gpio.h +++ b/include/asm-generic/gpio.h @@ -57,6 +57,8 @@ struct device_node; * enabling module power and clock; may sleep * @free: optional hook for chip-specific deactivation, such as * disabling module power and clock; may sleep + * @get_direction: returns direction for signal "offset", 0=out, 1=in, + * (same as GPIOF_DIR_XXX), or negative error * @direction_input: configures signal "offset" as input, or returns error * @get: returns value for signal "offset"; for output signals this * returns either the value actually sensed, or zero @@ -101,7 +103,8 @@ struct gpio_chip { unsigned offset); void (*free)(struct gpio_chip *chip, unsigned offset); - + int (*get_direction)(struct gpio_chip *chip, + unsigned offset); int (*direction_input)(struct gpio_chip *chip, unsigned offset); int (*get)(struct gpio_chip *chip,