Merge tag 'gpio-for-linus' of git://git.secretlab.ca/git/linux-2.6
authorLinus Torvalds <torvalds@linux-foundation.org>
Tue, 11 Dec 2012 21:00:56 +0000 (13:00 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 11 Dec 2012 21:00:56 +0000 (13:00 -0800)
Pull GPIO updates from Grant Likely:
 "GPIO follow up patch and type change for v3.5 merge window

  Primarily device driver additions, features and bug fixes.  Not much
  touching gpio common subsystem support.  Should not be scary."

* tag 'gpio-for-linus' of git://git.secretlab.ca/git/linux-2.6: (34 commits)
  gpio: Provide the STMPE GPIO driver with its own IRQ Domain
  gpio: add TS-5500 DIO blocks support
  gpio: pcf857x: use client->irq for gpio_to_irq()
  gpio: stmpe: Add DT support for stmpe gpio
  gpio: pl061 depends on ARM
  gpio/pl061: remove old comment
  gpio: SPEAr: add spi chipselect control driver
  gpio: gpio-max710x: Support device tree probing
  gpio: twl4030: Use only TWL4030_MODULE_LED for LED configuration
  gpio: tegra: read output value when gpio is set in direction_out
  gpio: pca953x: Add compatible strings to gpio-pca953x driver
  gpio: pca953x: Register an IRQ domain
  gpio: mvebu: Set free callback for gpio_chip
  gpio: tegra: Drop exporting static functions
  gpio: tegra: Staticize non-exported symbols
  gpio: tegra: fix suspend/resume apis
  gpio-pch: Set parent dev for gpio chip
  gpio: em: Fix build errors
  GPIO: clps711x: use platform_device_unregister in gpio_clps711x_init()
  gpio/tc3589x: convert to use the simple irqdomain
  ...

29 files changed:
Documentation/devicetree/bindings/gpio/gpio-stmpe.txt [new file with mode: 0644]
Documentation/devicetree/bindings/gpio/spear_spics.txt [new file with mode: 0644]
arch/arm/Kconfig
arch/arm/mach-shmobile/board-kzm9g.c
arch/arm/plat-spear/Kconfig
drivers/gpio/Kconfig
drivers/gpio/Makefile
drivers/gpio/gpio-clps711x.c [new file with mode: 0644]
drivers/gpio/gpio-da9055.c [new file with mode: 0644]
drivers/gpio/gpio-em.c
drivers/gpio/gpio-max730x.c
drivers/gpio/gpio-mvebu.c
drivers/gpio/gpio-omap.c
drivers/gpio/gpio-pca953x.c
drivers/gpio/gpio-pcf857x.c
drivers/gpio/gpio-pch.c
drivers/gpio/gpio-pl061.c
drivers/gpio/gpio-spear-spics.c [new file with mode: 0644]
drivers/gpio/gpio-stmpe.c
drivers/gpio/gpio-tc3589x.c
drivers/gpio/gpio-tegra.c
drivers/gpio/gpio-ts5500.c [new file with mode: 0644]
drivers/gpio/gpio-twl4030.c
drivers/gpio/gpio-vt8500.c
drivers/gpio/gpiolib.c
drivers/mfd/stmpe.c
include/asm-generic/gpio.h
include/linux/i2c/pcf857x.h
include/linux/platform_data/gpio-ts5500.h [new file with mode: 0644]

diff --git a/Documentation/devicetree/bindings/gpio/gpio-stmpe.txt b/Documentation/devicetree/bindings/gpio/gpio-stmpe.txt
new file mode 100644 (file)
index 0000000..a0e4cf8
--- /dev/null
@@ -0,0 +1,18 @@
+STMPE gpio
+----------
+
+Required properties:
+ - compatible: "st,stmpe-gpio"
+
+Optional properties:
+ - st,norequest-mask: bitmask specifying which GPIOs should _not_ be requestable
+   due to different usage (e.g. touch, keypad)
+
+Node name must be stmpe_gpio and should be child node of stmpe node to which it
+belongs.
+
+Example:
+       stmpe_gpio {
+               compatible = "st,stmpe-gpio";
+               st,norequest-mask = <0x20>;     //gpio 5 can't be used
+       };
diff --git a/Documentation/devicetree/bindings/gpio/spear_spics.txt b/Documentation/devicetree/bindings/gpio/spear_spics.txt
new file mode 100644 (file)
index 0000000..96c37eb
--- /dev/null
@@ -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>;
+       ...
+}
index 672e0a2..8a027f9 100644 (file)
@@ -366,6 +366,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
index 0a43f31..7a05de7 100644 (file)
@@ -548,7 +548,6 @@ static struct platform_device fsi_ak4648_device = {
 /* I2C */
 static struct pcf857x_platform_data pcf8575_pdata = {
        .gpio_base      = GPIO_PCF8575_BASE,
-       .irq            = intcs_evt2irq(0x3260), /* IRQ19 */
 };
 
 static struct i2c_board_info i2c0_devices[] = {
@@ -570,6 +569,7 @@ static struct i2c_board_info i2c1_devices[] = {
 static struct i2c_board_info i2c3_devices[] = {
        {
                I2C_BOARD_INFO("pcf8575", 0x20),
+               .irq            = intcs_evt2irq(0x3260), /* IRQ19 */
                .platform_data = &pcf8575_pdata,
        },
 };
index f8db7b2..87dbd81 100644 (file)
@@ -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
index f165576..14a6c29 100644 (file)
@@ -90,11 +90,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
@@ -174,7 +189,7 @@ config GPIO_MXS
 
 config GPIO_PL061
        bool "PrimeCell PL061 GPIO support"
-       depends on ARM_AMBA
+       depends on ARM && ARM_AMBA
        select GENERIC_IRQ_CHIP
        help
          Say yes here to support the PrimeCell PL061 GPIO device
@@ -185,6 +200,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
@@ -193,6 +215,14 @@ config GPIO_STA2X11
          Say yes here to support the STA2x11/ConneXt GPIO device.
          The GPIO module has 128 GPIO pins with alternate functions.
 
+config GPIO_TS5500
+       tristate "TS-5500 DIO blocks and compatibles"
+       help
+         This driver supports Digital I/O exposed by pin blocks found on some
+         Technologic Systems platforms. It includes, but is not limited to, 3
+         blocks of the TS-5500: DIO1, DIO2 and the LCD port, and the TS-5600
+         LCD port.
+
 config GPIO_VT8500
        bool "VIA/Wondermedia SoC GPIO Support"
        depends on ARCH_VT8500
index 420dbac..76b3446 100644 (file)
@@ -17,8 +17,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
@@ -58,6 +60,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
@@ -69,6 +72,7 @@ obj-$(CONFIG_ARCH_DAVINCI_TNETV107X) += gpio-tnetv107x.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_TS5500)      += gpio-ts5500.o
 obj-$(CONFIG_GPIO_TWL4030)     += gpio-twl4030.o
 obj-$(CONFIG_GPIO_TWL6040)     += gpio-twl6040.o
 obj-$(CONFIG_GPIO_UCB1400)     += gpio-ucb1400.o
diff --git a/drivers/gpio/gpio-clps711x.c b/drivers/gpio/gpio-clps711x.c
new file mode 100644 (file)
index 0000000..ce63b75
--- /dev/null
@@ -0,0 +1,199 @@
+/*
+ *  CLPS711X GPIO driver
+ *
+ *  Copyright (C) 2012 Alexander Shiyan <shc_work@mail.ru>
+ *
+ * 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 <linux/io.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/platform_device.h>
+
+#include <mach/hardware.h>
+
+#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 <shc_work@mail.ru>");
+MODULE_DESCRIPTION("CLPS711X GPIO driver");
diff --git a/drivers/gpio/gpio-da9055.c b/drivers/gpio/gpio-da9055.c
new file mode 100644 (file)
index 0000000..55d83c7
--- /dev/null
@@ -0,0 +1,204 @@
+/*
+ * GPIO Driver for Dialog DA9055 PMICs.
+ *
+ * Copyright(c) 2012 Dialog Semiconductor Ltd.
+ *
+ * Author: David Dajun Chen <dchen@diasemi.com>
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+
+#include <linux/mfd/da9055/core.h>
+#include <linux/mfd/da9055/reg.h>
+#include <linux/mfd/da9055/pdata.h>
+
+#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 <dchen@diasemi.com>");
+MODULE_DESCRIPTION("DA9055 GPIO Device Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:da9055-gpio");
index efb4c2d..b007063 100644 (file)
@@ -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);
index 05e2dac..c4bf86a 100644 (file)
@@ -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;
index be65c04..a515b92 100644 (file)
@@ -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) {
index d335af1..d71e5bd 100644 (file)
@@ -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;
index 9c693ae..0c5eaf5 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/gpio.h>
 #include <linux/interrupt.h>
 #include <linux/irq.h>
+#include <linux/irqdomain.h>
 #include <linux/i2c.h>
 #include <linux/i2c/pca953x.h>
 #include <linux/slab.h>
@@ -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,
index 16af35c..a19b745 100644 (file)
@@ -223,11 +223,11 @@ static void pcf857x_irq_domain_cleanup(struct pcf857x *gpio)
 
 static int pcf857x_irq_domain_init(struct pcf857x *gpio,
                                   struct pcf857x_platform_data *pdata,
-                                  struct device *dev)
+                                  struct i2c_client *client)
 {
        int status;
 
-       gpio->irq_domain = irq_domain_add_linear(dev->of_node,
+       gpio->irq_domain = irq_domain_add_linear(client->dev.of_node,
                                                 gpio->chip.ngpio,
                                                 &pcf857x_irq_domain_ops,
                                                 NULL);
@@ -235,15 +235,15 @@ static int pcf857x_irq_domain_init(struct pcf857x *gpio,
                goto fail;
 
        /* enable real irq */
-       status = request_irq(pdata->irq, pcf857x_irq_demux, 0,
-                            dev_name(dev), gpio);
+       status = request_irq(client->irq, pcf857x_irq_demux, 0,
+                            dev_name(&client->dev), gpio);
        if (status)
                goto fail;
 
        /* enable gpio_to_irq() */
        INIT_WORK(&gpio->work, pcf857x_irq_demux_work);
        gpio->chip.to_irq       = pcf857x_to_irq;
-       gpio->irq               = pdata->irq;
+       gpio->irq               = client->irq;
 
        return 0;
 
@@ -285,8 +285,8 @@ static int pcf857x_probe(struct i2c_client *client,
        gpio->chip.ngpio                = id->driver_data;
 
        /* enable gpio_to_irq() if platform has settings */
-       if (pdata && pdata->irq) {
-               status = pcf857x_irq_domain_init(gpio, pdata, &client->dev);
+       if (pdata && client->irq) {
+               status = pcf857x_irq_domain_init(gpio, pdata, client);
                if (status < 0) {
                        dev_err(&client->dev, "irq_domain init failed\n");
                        goto fail;
@@ -368,15 +368,6 @@ static int pcf857x_probe(struct i2c_client *client,
        if (status < 0)
                goto fail;
 
-       /* NOTE: these chips can issue "some pin-changed" IRQs, which we
-        * don't yet even try to use.  Among other issues, the relevant
-        * genirq state isn't available to modular drivers; and most irq
-        * methods can't be called from sleeping contexts.
-        */
-
-       dev_info(&client->dev, "%s\n",
-                       client->irq ? " (irq ignored)" : "");
-
        /* Let platform code set up the GPIOs and their users.
         * Now is the first time anyone could use them.
         */
@@ -388,13 +379,15 @@ static int pcf857x_probe(struct i2c_client *client,
                        dev_warn(&client->dev, "setup --> %d\n", status);
        }
 
+       dev_info(&client->dev, "probed\n");
+
        return 0;
 
 fail:
        dev_dbg(&client->dev, "probe error %d for '%s'\n",
                        status, client->name);
 
-       if (pdata && pdata->irq)
+       if (pdata && client->irq)
                pcf857x_irq_domain_cleanup(gpio);
 
        kfree(gpio);
@@ -418,7 +411,7 @@ static int pcf857x_remove(struct i2c_client *client)
                }
        }
 
-       if (pdata && pdata->irq)
+       if (pdata && client->irq)
                pcf857x_irq_domain_cleanup(gpio);
 
        status = gpiochip_remove(&gpio->chip);
index 4ad0c4f..e3a14fe 100644 (file)
@@ -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;
index b4b5da4..c1720de 100644 (file)
@@ -48,12 +48,7 @@ struct pl061_context_save_regs {
 #endif
 
 struct pl061_gpio {
-       /* Each of the two spinlocks protects a different set of hardware
-        * regiters and data structurs. This decouples the code of the IRQ from
-        * the GPIO code. This also makes the case of a GPIO routine call from
-        * the IRQ code simpler.
-        */
-       spinlock_t              lock;           /* GPIO registers */
+       spinlock_t              lock;
 
        void __iomem            *base;
        int                     irq_base;
@@ -216,39 +211,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 +248,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 +266,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 +283,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 (file)
index 0000000..5f45fc4
--- /dev/null
@@ -0,0 +1,217 @@
+/*
+ * SPEAr platform SPI chipselect abstraction over gpiolib
+ *
+ * Copyright (C) 2012 ST Microelectronics
+ * Shiraz Hashim <shiraz.hashim@st.com>
+ *
+ * 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/err.h>
+#include <linux/gpio.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/types.h>
+
+/* 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 <shiraz.hashim@st.com>");
+MODULE_DESCRIPTION("ST Microlectronics SPEAr SPI Chip Select Abstraction");
+MODULE_LICENSE("GPL");
index dce3472..3e1d398 100644 (file)
@@ -11,7 +11,9 @@
 #include <linux/slab.h>
 #include <linux/gpio.h>
 #include <linux/irq.h>
+#include <linux/irqdomain.h>
 #include <linux/interrupt.h>
+#include <linux/of.h>
 #include <linux/mfd/stmpe.h>
 
 /*
@@ -28,6 +30,7 @@ struct stmpe_gpio {
        struct stmpe *stmpe;
        struct device *dev;
        struct mutex irq_lock;
+       struct irq_domain *domain;
 
        int irq_base;
        unsigned norequest_mask;
@@ -103,7 +106,7 @@ static int stmpe_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
 {
        struct stmpe_gpio *stmpe_gpio = to_stmpe_gpio(chip);
 
-       return stmpe_gpio->irq_base + offset;
+       return irq_create_mapping(stmpe_gpio->domain, offset);
 }
 
 static int stmpe_gpio_request(struct gpio_chip *chip, unsigned offset)
@@ -132,7 +135,7 @@ static struct gpio_chip template_chip = {
 static int stmpe_gpio_irq_set_type(struct irq_data *d, unsigned int type)
 {
        struct stmpe_gpio *stmpe_gpio = irq_data_get_irq_chip_data(d);
-       int offset = d->irq - stmpe_gpio->irq_base;
+       int offset = d->hwirq;
        int regoffset = offset / 8;
        int mask = 1 << (offset % 8);
 
@@ -199,7 +202,7 @@ static void stmpe_gpio_irq_sync_unlock(struct irq_data *d)
 static void stmpe_gpio_irq_mask(struct irq_data *d)
 {
        struct stmpe_gpio *stmpe_gpio = irq_data_get_irq_chip_data(d);
-       int offset = d->irq - stmpe_gpio->irq_base;
+       int offset = d->hwirq;
        int regoffset = offset / 8;
        int mask = 1 << (offset % 8);
 
@@ -209,7 +212,7 @@ static void stmpe_gpio_irq_mask(struct irq_data *d)
 static void stmpe_gpio_irq_unmask(struct irq_data *d)
 {
        struct stmpe_gpio *stmpe_gpio = irq_data_get_irq_chip_data(d);
-       int offset = d->irq - stmpe_gpio->irq_base;
+       int offset = d->hwirq;
        int regoffset = offset / 8;
        int mask = 1 << (offset % 8);
 
@@ -251,8 +254,9 @@ static irqreturn_t stmpe_gpio_irq(int irq, void *dev)
                while (stat) {
                        int bit = __ffs(stat);
                        int line = bank * 8 + bit;
+                       int virq = irq_find_mapping(stmpe_gpio->domain, line);
 
-                       handle_nested_irq(stmpe_gpio->irq_base + line);
+                       handle_nested_irq(virq);
                        stat &= ~(1 << bit);
                }
 
@@ -267,43 +271,61 @@ static irqreturn_t stmpe_gpio_irq(int irq, void *dev)
        return IRQ_HANDLED;
 }
 
-static int __devinit stmpe_gpio_irq_init(struct stmpe_gpio *stmpe_gpio)
+int stmpe_gpio_irq_map(struct irq_domain *d, unsigned int virq,
+                      irq_hw_number_t hwirq)
 {
-       int base = stmpe_gpio->irq_base;
-       int irq;
+       struct stmpe_gpio *stmpe_gpio = d->host_data;
 
-       for (irq = base; irq < base + stmpe_gpio->chip.ngpio; irq++) {
-               irq_set_chip_data(irq, stmpe_gpio);
-               irq_set_chip_and_handler(irq, &stmpe_gpio_irq_chip,
-                                        handle_simple_irq);
-               irq_set_nested_thread(irq, 1);
+       if (!stmpe_gpio)
+               return -EINVAL;
+
+       irq_set_chip_data(hwirq, stmpe_gpio);
+       irq_set_chip_and_handler(hwirq, &stmpe_gpio_irq_chip,
+                                handle_simple_irq);
+       irq_set_nested_thread(hwirq, 1);
 #ifdef CONFIG_ARM
-               set_irq_flags(irq, IRQF_VALID);
+       set_irq_flags(hwirq, IRQF_VALID);
 #else
-               irq_set_noprobe(irq);
+       irq_set_noprobe(hwirq);
 #endif
-       }
 
        return 0;
 }
 
-static void stmpe_gpio_irq_remove(struct stmpe_gpio *stmpe_gpio)
+void stmpe_gpio_irq_unmap(struct irq_domain *d, unsigned int virq)
 {
-       int base = stmpe_gpio->irq_base;
-       int irq;
-
-       for (irq = base; irq < base + stmpe_gpio->chip.ngpio; irq++) {
 #ifdef CONFIG_ARM
-               set_irq_flags(irq, 0);
+       set_irq_flags(virq, 0);
 #endif
-               irq_set_chip_and_handler(irq, NULL, NULL);
-               irq_set_chip_data(irq, NULL);
+       irq_set_chip_and_handler(virq, NULL, NULL);
+       irq_set_chip_data(virq, NULL);
+}
+
+static const struct irq_domain_ops stmpe_gpio_irq_simple_ops = {
+       .unmap = stmpe_gpio_irq_unmap,
+       .map = stmpe_gpio_irq_map,
+       .xlate = irq_domain_xlate_twocell,
+};
+
+static int __devinit stmpe_gpio_irq_init(struct stmpe_gpio *stmpe_gpio)
+{
+       int base = stmpe_gpio->irq_base;
+
+       stmpe_gpio->domain = irq_domain_add_simple(NULL,
+                               stmpe_gpio->chip.ngpio, base,
+                               &stmpe_gpio_irq_simple_ops, stmpe_gpio);
+       if (!stmpe_gpio->domain) {
+               dev_err(stmpe_gpio->dev, "failed to create irqdomain\n");
+               return -ENOSYS;
        }
+
+       return 0;
 }
 
 static int __devinit stmpe_gpio_probe(struct platform_device *pdev)
 {
        struct stmpe *stmpe = dev_get_drvdata(pdev->dev.parent);
+       struct device_node *np = pdev->dev.of_node;
        struct stmpe_gpio_platform_data *pdata;
        struct stmpe_gpio *stmpe_gpio;
        int ret;
@@ -321,13 +343,17 @@ static int __devinit stmpe_gpio_probe(struct platform_device *pdev)
 
        stmpe_gpio->dev = &pdev->dev;
        stmpe_gpio->stmpe = stmpe;
-       stmpe_gpio->norequest_mask = pdata ? pdata->norequest_mask : 0;
-
        stmpe_gpio->chip = template_chip;
        stmpe_gpio->chip.ngpio = stmpe->num_gpios;
        stmpe_gpio->chip.dev = &pdev->dev;
        stmpe_gpio->chip.base = pdata ? pdata->gpio_base : -1;
 
+       if (pdata)
+               stmpe_gpio->norequest_mask = pdata->norequest_mask;
+       else if (np)
+               of_property_read_u32(np, "st,norequest-mask",
+                               &stmpe_gpio->norequest_mask);
+
        if (irq >= 0)
                stmpe_gpio->irq_base = stmpe->irq_base + STMPE_INT_GPIO(0);
        else
@@ -348,7 +374,7 @@ static int __devinit stmpe_gpio_probe(struct platform_device *pdev)
                                IRQF_ONESHOT, "stmpe-gpio", stmpe_gpio);
                if (ret) {
                        dev_err(&pdev->dev, "unable to get irq: %d\n", ret);
-                       goto out_removeirq;
+                       goto out_disable;
                }
        }
 
@@ -368,9 +394,6 @@ static int __devinit stmpe_gpio_probe(struct platform_device *pdev)
 out_freeirq:
        if (irq >= 0)
                free_irq(irq, stmpe_gpio);
-out_removeirq:
-       if (irq >= 0)
-               stmpe_gpio_irq_remove(stmpe_gpio);
 out_disable:
        stmpe_disable(stmpe, STMPE_BLOCK_GPIO);
 out_free:
@@ -398,10 +421,9 @@ static int __devexit stmpe_gpio_remove(struct platform_device *pdev)
 
        stmpe_disable(stmpe, STMPE_BLOCK_GPIO);
 
-       if (irq >= 0) {
+       if (irq >= 0)
                free_irq(irq, stmpe_gpio);
-               stmpe_gpio_irq_remove(stmpe_gpio);
-       }
+
        platform_set_drvdata(pdev, NULL);
        kfree(stmpe_gpio);
 
index 1e48317..8c8447c 100644 (file)
@@ -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;
index d982593..5389be8 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/module.h>
 #include <linux/irqdomain.h>
 #include <linux/pinctrl/consumer.h>
+#include <linux/pm.h>
 
 #include <asm/mach/irq.h>
 
@@ -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-ts5500.c b/drivers/gpio/gpio-ts5500.c
new file mode 100644 (file)
index 0000000..0634cee
--- /dev/null
@@ -0,0 +1,466 @@
+/*
+ * Digital I/O driver for Technologic Systems TS-5500
+ *
+ * Copyright (c) 2012 Savoir-faire Linux Inc.
+ *     Vivien Didelot <vivien.didelot@savoirfairelinux.com>
+ *
+ * Technologic Systems platforms have pin blocks, exposing several Digital
+ * Input/Output lines (DIO). This driver aims to support single pin blocks.
+ * In that sense, the support is not limited to the TS-5500 blocks.
+ * Actually, the following platforms have DIO support:
+ *
+ * TS-5500:
+ *   Documentation: http://wiki.embeddedarm.com/wiki/TS-5500
+ *   Blocks: DIO1, DIO2 and LCD port.
+ *
+ * TS-5600:
+ *   Documentation: http://wiki.embeddedarm.com/wiki/TS-5600
+ *   Blocks: LCD port (identical to TS-5500 LCD).
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/bitops.h>
+#include <linux/gpio.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_data/gpio-ts5500.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+/* List of supported Technologic Systems platforms DIO blocks */
+enum ts5500_blocks { TS5500_DIO1, TS5500_DIO2, TS5500_LCD, TS5600_LCD };
+
+struct ts5500_priv {
+       const struct ts5500_dio *pinout;
+       struct gpio_chip gpio_chip;
+       spinlock_t lock;
+       bool strap;
+       u8 hwirq;
+};
+
+/*
+ * Hex 7D is used to control several blocks (e.g. DIO2 and LCD port).
+ * This flag ensures that the region has been requested by this driver.
+ */
+static bool hex7d_reserved;
+
+/*
+ * This structure is used to describe capabilities of DIO lines,
+ * such as available directions and connected interrupt (if any).
+ */
+struct ts5500_dio {
+       const u8 value_addr;
+       const u8 value_mask;
+       const u8 control_addr;
+       const u8 control_mask;
+       const bool no_input;
+       const bool no_output;
+       const u8 irq;
+};
+
+#define TS5500_DIO_IN_OUT(vaddr, vbit, caddr, cbit)    \
+       {                                               \
+               .value_addr = vaddr,                    \
+               .value_mask = BIT(vbit),                \
+               .control_addr = caddr,                  \
+               .control_mask = BIT(cbit),              \
+       }
+
+#define TS5500_DIO_IN(addr, bit)               \
+       {                                       \
+               .value_addr = addr,             \
+               .value_mask = BIT(bit),         \
+               .no_output = true,              \
+       }
+
+#define TS5500_DIO_IN_IRQ(addr, bit, _irq)     \
+       {                                       \
+               .value_addr = addr,             \
+               .value_mask = BIT(bit),         \
+               .no_output = true,              \
+               .irq = _irq,                    \
+       }
+
+#define TS5500_DIO_OUT(addr, bit)              \
+       {                                       \
+               .value_addr = addr,             \
+               .value_mask = BIT(bit),         \
+               .no_input = true,               \
+       }
+
+/*
+ * Input/Output DIO lines are programmed in groups of 4. Their values are
+ * available through 4 consecutive bits in a value port, whereas the direction
+ * of these 4 lines is driven by only 1 bit in a control port.
+ */
+#define TS5500_DIO_GROUP(vaddr, vbitfrom, caddr, cbit)         \
+       TS5500_DIO_IN_OUT(vaddr, vbitfrom + 0, caddr, cbit),    \
+       TS5500_DIO_IN_OUT(vaddr, vbitfrom + 1, caddr, cbit),    \
+       TS5500_DIO_IN_OUT(vaddr, vbitfrom + 2, caddr, cbit),    \
+       TS5500_DIO_IN_OUT(vaddr, vbitfrom + 3, caddr, cbit)
+
+/*
+ * TS-5500 DIO1 block
+ *
+ *  value    control  dir    hw
+ *  addr bit addr bit in out irq name     pin offset
+ *
+ *  0x7b  0  0x7a  0  x   x      DIO1_0   1   0
+ *  0x7b  1  0x7a  0  x   x      DIO1_1   3   1
+ *  0x7b  2  0x7a  0  x   x      DIO1_2   5   2
+ *  0x7b  3  0x7a  0  x   x      DIO1_3   7   3
+ *  0x7b  4  0x7a  1  x   x      DIO1_4   9   4
+ *  0x7b  5  0x7a  1  x   x      DIO1_5   11  5
+ *  0x7b  6  0x7a  1  x   x      DIO1_6   13  6
+ *  0x7b  7  0x7a  1  x   x      DIO1_7   15  7
+ *  0x7c  0  0x7a  5  x   x      DIO1_8   4   8
+ *  0x7c  1  0x7a  5  x   x      DIO1_9   6   9
+ *  0x7c  2  0x7a  5  x   x      DIO1_10  8   10
+ *  0x7c  3  0x7a  5  x   x      DIO1_11  10  11
+ *  0x7c  4           x          DIO1_12  12  12
+ *  0x7c  5           x      7   DIO1_13  14  13
+ */
+static const struct ts5500_dio ts5500_dio1[] = {
+       TS5500_DIO_GROUP(0x7b, 0, 0x7a, 0),
+       TS5500_DIO_GROUP(0x7b, 4, 0x7a, 1),
+       TS5500_DIO_GROUP(0x7c, 0, 0x7a, 5),
+       TS5500_DIO_IN(0x7c, 4),
+       TS5500_DIO_IN_IRQ(0x7c, 5, 7),
+};
+
+/*
+ * TS-5500 DIO2 block
+ *
+ *  value    control  dir    hw
+ *  addr bit addr bit in out irq name     pin offset
+ *
+ *  0x7e  0  0x7d  0  x   x      DIO2_0   1   0
+ *  0x7e  1  0x7d  0  x   x      DIO2_1   3   1
+ *  0x7e  2  0x7d  0  x   x      DIO2_2   5   2
+ *  0x7e  3  0x7d  0  x   x      DIO2_3   7   3
+ *  0x7e  4  0x7d  1  x   x      DIO2_4   9   4
+ *  0x7e  5  0x7d  1  x   x      DIO2_5   11  5
+ *  0x7e  6  0x7d  1  x   x      DIO2_6   13  6
+ *  0x7e  7  0x7d  1  x   x      DIO2_7   15  7
+ *  0x7f  0  0x7d  5  x   x      DIO2_8   4   8
+ *  0x7f  1  0x7d  5  x   x      DIO2_9   6   9
+ *  0x7f  2  0x7d  5  x   x      DIO2_10  8   10
+ *  0x7f  3  0x7d  5  x   x      DIO2_11  10  11
+ *  0x7f  4           x      6   DIO2_13  14  12
+ */
+static const struct ts5500_dio ts5500_dio2[] = {
+       TS5500_DIO_GROUP(0x7e, 0, 0x7d, 0),
+       TS5500_DIO_GROUP(0x7e, 4, 0x7d, 1),
+       TS5500_DIO_GROUP(0x7f, 0, 0x7d, 5),
+       TS5500_DIO_IN_IRQ(0x7f, 4, 6),
+};
+
+/*
+ * TS-5500 LCD port used as DIO block
+ * TS-5600 LCD port is identical
+ *
+ *  value    control  dir    hw
+ *  addr bit addr bit in out irq name    pin offset
+ *
+ *  0x72  0  0x7d  2  x   x      LCD_0   8   0
+ *  0x72  1  0x7d  2  x   x      LCD_1   7   1
+ *  0x72  2  0x7d  2  x   x      LCD_2   10  2
+ *  0x72  3  0x7d  2  x   x      LCD_3   9   3
+ *  0x72  4  0x7d  3  x   x      LCD_4   12  4
+ *  0x72  5  0x7d  3  x   x      LCD_5   11  5
+ *  0x72  6  0x7d  3  x   x      LCD_6   14  6
+ *  0x72  7  0x7d  3  x   x      LCD_7   13  7
+ *  0x73  0               x      LCD_EN  5   8
+ *  0x73  6           x          LCD_WR  6   9
+ *  0x73  7           x      1   LCD_RS  3   10
+ */
+static const struct ts5500_dio ts5500_lcd[] = {
+       TS5500_DIO_GROUP(0x72, 0, 0x7d, 2),
+       TS5500_DIO_GROUP(0x72, 4, 0x7d, 3),
+       TS5500_DIO_OUT(0x73, 0),
+       TS5500_DIO_IN(0x73, 6),
+       TS5500_DIO_IN_IRQ(0x73, 7, 1),
+};
+
+static inline struct ts5500_priv *ts5500_gc_to_priv(struct gpio_chip *chip)
+{
+       return container_of(chip, struct ts5500_priv, gpio_chip);
+}
+
+static inline void ts5500_set_mask(u8 mask, u8 addr)
+{
+       u8 val = inb(addr);
+       val |= mask;
+       outb(val, addr);
+}
+
+static inline void ts5500_clear_mask(u8 mask, u8 addr)
+{
+       u8 val = inb(addr);
+       val &= ~mask;
+       outb(val, addr);
+}
+
+static int ts5500_gpio_input(struct gpio_chip *chip, unsigned offset)
+{
+       struct ts5500_priv *priv = ts5500_gc_to_priv(chip);
+       const struct ts5500_dio line = priv->pinout[offset];
+       unsigned long flags;
+
+       if (line.no_input)
+               return -ENXIO;
+
+       if (line.no_output)
+               return 0;
+
+       spin_lock_irqsave(&priv->lock, flags);
+       ts5500_clear_mask(line.control_mask, line.control_addr);
+       spin_unlock_irqrestore(&priv->lock, flags);
+
+       return 0;
+}
+
+static int ts5500_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+       struct ts5500_priv *priv = ts5500_gc_to_priv(chip);
+       const struct ts5500_dio line = priv->pinout[offset];
+
+       return !!(inb(line.value_addr) & line.value_mask);
+}
+
+static int ts5500_gpio_output(struct gpio_chip *chip, unsigned offset, int val)
+{
+       struct ts5500_priv *priv = ts5500_gc_to_priv(chip);
+       const struct ts5500_dio line = priv->pinout[offset];
+       unsigned long flags;
+
+       if (line.no_output)
+               return -ENXIO;
+
+       spin_lock_irqsave(&priv->lock, flags);
+       if (!line.no_input)
+               ts5500_set_mask(line.control_mask, line.control_addr);
+
+       if (val)
+               ts5500_set_mask(line.value_mask, line.value_addr);
+       else
+               ts5500_clear_mask(line.value_mask, line.value_addr);
+       spin_unlock_irqrestore(&priv->lock, flags);
+
+       return 0;
+}
+
+static void ts5500_gpio_set(struct gpio_chip *chip, unsigned offset, int val)
+{
+       struct ts5500_priv *priv = ts5500_gc_to_priv(chip);
+       const struct ts5500_dio line = priv->pinout[offset];
+       unsigned long flags;
+
+       spin_lock_irqsave(&priv->lock, flags);
+       if (val)
+               ts5500_set_mask(line.value_mask, line.value_addr);
+       else
+               ts5500_clear_mask(line.value_mask, line.value_addr);
+       spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+static int ts5500_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+       struct ts5500_priv *priv = ts5500_gc_to_priv(chip);
+       const struct ts5500_dio *block = priv->pinout;
+       const struct ts5500_dio line = block[offset];
+
+       /* Only one pin is connected to an interrupt */
+       if (line.irq)
+               return line.irq;
+
+       /* As this pin is input-only, we may strap it to another in/out pin */
+       if (priv->strap)
+               return priv->hwirq;
+
+       return -ENXIO;
+}
+
+static int ts5500_enable_irq(struct ts5500_priv *priv)
+{
+       int ret = 0;
+       unsigned long flags;
+
+       spin_lock_irqsave(&priv->lock, flags);
+       if (priv->hwirq == 7)
+               ts5500_set_mask(BIT(7), 0x7a); /* DIO1_13 on IRQ7 */
+       else if (priv->hwirq == 6)
+               ts5500_set_mask(BIT(7), 0x7d); /* DIO2_13 on IRQ6 */
+       else if (priv->hwirq == 1)
+               ts5500_set_mask(BIT(6), 0x7d); /* LCD_RS on IRQ1 */
+       else
+               ret = -EINVAL;
+       spin_unlock_irqrestore(&priv->lock, flags);
+
+       return ret;
+}
+
+static void ts5500_disable_irq(struct ts5500_priv *priv)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&priv->lock, flags);
+       if (priv->hwirq == 7)
+               ts5500_clear_mask(BIT(7), 0x7a); /* DIO1_13 on IRQ7 */
+       else if (priv->hwirq == 6)
+               ts5500_clear_mask(BIT(7), 0x7d); /* DIO2_13 on IRQ6 */
+       else if (priv->hwirq == 1)
+               ts5500_clear_mask(BIT(6), 0x7d); /* LCD_RS on IRQ1 */
+       else
+               dev_err(priv->gpio_chip.dev, "invalid hwirq %d\n", priv->hwirq);
+       spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+static int __devinit ts5500_dio_probe(struct platform_device *pdev)
+{
+       enum ts5500_blocks block = platform_get_device_id(pdev)->driver_data;
+       struct ts5500_dio_platform_data *pdata = pdev->dev.platform_data;
+       struct device *dev = &pdev->dev;
+       const char *name = dev_name(dev);
+       struct ts5500_priv *priv;
+       struct resource *res;
+       unsigned long flags;
+       int ret;
+
+       res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+       if (!res) {
+               dev_err(dev, "missing IRQ resource\n");
+               return -EINVAL;
+       }
+
+       priv = devm_kzalloc(dev, sizeof(struct ts5500_priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       platform_set_drvdata(pdev, priv);
+       priv->hwirq = res->start;
+       spin_lock_init(&priv->lock);
+
+       priv->gpio_chip.owner = THIS_MODULE;
+       priv->gpio_chip.label = name;
+       priv->gpio_chip.dev = dev;
+       priv->gpio_chip.direction_input = ts5500_gpio_input;
+       priv->gpio_chip.direction_output = ts5500_gpio_output;
+       priv->gpio_chip.get = ts5500_gpio_get;
+       priv->gpio_chip.set = ts5500_gpio_set;
+       priv->gpio_chip.to_irq = ts5500_gpio_to_irq;
+       priv->gpio_chip.base = -1;
+       if (pdata) {
+               priv->gpio_chip.base = pdata->base;
+               priv->strap = pdata->strap;
+       }
+
+       switch (block) {
+       case TS5500_DIO1:
+               priv->pinout = ts5500_dio1;
+               priv->gpio_chip.ngpio = ARRAY_SIZE(ts5500_dio1);
+
+               if (!devm_request_region(dev, 0x7a, 3, name)) {
+                       dev_err(dev, "failed to request %s ports\n", name);
+                       return -EBUSY;
+               }
+               break;
+       case TS5500_DIO2:
+               priv->pinout = ts5500_dio2;
+               priv->gpio_chip.ngpio = ARRAY_SIZE(ts5500_dio2);
+
+               if (!devm_request_region(dev, 0x7e, 2, name)) {
+                       dev_err(dev, "failed to request %s ports\n", name);
+                       return -EBUSY;
+               }
+
+               if (hex7d_reserved)
+                       break;
+
+               if (!devm_request_region(dev, 0x7d, 1, name)) {
+                       dev_err(dev, "failed to request %s 7D\n", name);
+                       return -EBUSY;
+               }
+
+               hex7d_reserved = true;
+               break;
+       case TS5500_LCD:
+       case TS5600_LCD:
+               priv->pinout = ts5500_lcd;
+               priv->gpio_chip.ngpio = ARRAY_SIZE(ts5500_lcd);
+
+               if (!devm_request_region(dev, 0x72, 2, name)) {
+                       dev_err(dev, "failed to request %s ports\n", name);
+                       return -EBUSY;
+               }
+
+               if (!hex7d_reserved) {
+                       if (!devm_request_region(dev, 0x7d, 1, name)) {
+                               dev_err(dev, "failed to request %s 7D\n", name);
+                               return -EBUSY;
+                       }
+
+                       hex7d_reserved = true;
+               }
+
+               /* Ensure usage of LCD port as DIO */
+               spin_lock_irqsave(&priv->lock, flags);
+               ts5500_clear_mask(BIT(4), 0x7d);
+               spin_unlock_irqrestore(&priv->lock, flags);
+               break;
+       }
+
+       ret = gpiochip_add(&priv->gpio_chip);
+       if (ret) {
+               dev_err(dev, "failed to register the gpio chip\n");
+               return ret;
+       }
+
+       ret = ts5500_enable_irq(priv);
+       if (ret) {
+               dev_err(dev, "invalid interrupt %d\n", priv->hwirq);
+               goto cleanup;
+       }
+
+       return 0;
+cleanup:
+       if (gpiochip_remove(&priv->gpio_chip))
+               dev_err(dev, "failed to remove gpio chip\n");
+       return ret;
+}
+
+static int __devexit ts5500_dio_remove(struct platform_device *pdev)
+{
+       struct ts5500_priv *priv = platform_get_drvdata(pdev);
+
+       ts5500_disable_irq(priv);
+       return gpiochip_remove(&priv->gpio_chip);
+}
+
+static struct platform_device_id ts5500_dio_ids[] = {
+       { "ts5500-dio1", TS5500_DIO1 },
+       { "ts5500-dio2", TS5500_DIO2 },
+       { "ts5500-dio-lcd", TS5500_LCD },
+       { "ts5600-dio-lcd", TS5600_LCD },
+       { }
+};
+MODULE_DEVICE_TABLE(platform, ts5500_dio_ids);
+
+static struct platform_driver ts5500_dio_driver = {
+       .driver = {
+               .name = "ts5500-dio",
+               .owner = THIS_MODULE,
+       },
+       .probe = ts5500_dio_probe,
+       .remove = __devexit_p(ts5500_dio_remove),
+       .id_table = ts5500_dio_ids,
+};
+
+module_platform_driver(ts5500_dio_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Savoir-faire Linux Inc. <kernel@savoirfairelinux.com>");
+MODULE_DESCRIPTION("Technologic Systems TS-5500 Digital I/O driver");
index c5f8ca2..d2138b0 100644 (file)
@@ -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;
 
index bcd8e4a..9ed2a2b 100644 (file)
@@ -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),
        },
 };
 
index 58b9838..199fca1 100644 (file)
@@ -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
@@ -1304,9 +1341,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",
@@ -1842,6 +1885,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,
index c94f521..79e88d1 100644 (file)
@@ -294,12 +294,14 @@ static struct resource stmpe_gpio_resources[] = {
 
 static struct mfd_cell stmpe_gpio_cell = {
        .name           = "stmpe-gpio",
+       .of_compatible  = "st,stmpe-gpio",
        .resources      = stmpe_gpio_resources,
        .num_resources  = ARRAY_SIZE(stmpe_gpio_resources),
 };
 
 static struct mfd_cell stmpe_gpio_cell_noirq = {
        .name           = "stmpe-gpio",
+       .of_compatible  = "st,stmpe-gpio",
        /* gpio cell resources consist of an irq only so no resources here */
 };
 
index 9fd3093..20ca766 100644 (file)
@@ -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,
index 781e6bd..0767a2a 100644 (file)
@@ -10,7 +10,6 @@
  * @setup: optional callback issued once the GPIOs are valid
  * @teardown: optional callback issued before the GPIOs are invalidated
  * @context: optional parameter passed to setup() and teardown()
- * @irq: optional interrupt number
  *
  * In addition to the I2C_BOARD_INFO() state appropriate to each chip,
  * the i2c_board_info used with the pcf875x driver must provide its
@@ -40,8 +39,6 @@ struct pcf857x_platform_data {
                                        int gpio, unsigned ngpio,
                                        void *context);
        void            *context;
-
-       int             irq;
 };
 
 #endif /* __LINUX_PCF857X_H */
diff --git a/include/linux/platform_data/gpio-ts5500.h b/include/linux/platform_data/gpio-ts5500.h
new file mode 100644 (file)
index 0000000..b10d11c
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * GPIO (DIO) header for Technologic Systems TS-5500
+ *
+ * Copyright (c) 2012 Savoir-faire Linux Inc.
+ *     Vivien Didelot <vivien.didelot@savoirfairelinux.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _PDATA_GPIO_TS5500_H
+#define _PDATA_GPIO_TS5500_H
+
+/**
+ * struct ts5500_dio_platform_data - TS-5500 pin block configuration
+ * @base:      The GPIO base number to use.
+ * @strap:     The only pin connected to an interrupt in a block is input-only.
+ *             If you need a bidirectional line which can trigger an IRQ, you
+ *             may strap it with an in/out pin. This flag indicates this case.
+ */
+struct ts5500_dio_platform_data {
+       int base;
+       bool strap;
+};
+
+#endif /* _PDATA_GPIO_TS5500_H */