summaryrefslogtreecommitdiffstats
path: root/drivers/gpio
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpio')
-rw-r--r--drivers/gpio/Kconfig72
-rw-r--r--drivers/gpio/Makefile9
-rw-r--r--drivers/gpio/gpiolib.c567
-rw-r--r--drivers/gpio/mcp23s08.c357
-rw-r--r--drivers/gpio/pca9539.c271
-rw-r--r--drivers/gpio/pcf857x.c330
6 files changed, 1606 insertions, 0 deletions
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
new file mode 100644
index 000000000000..74fac0f5c348
--- /dev/null
+++ b/drivers/gpio/Kconfig
@@ -0,0 +1,72 @@
+#
+# GPIO infrastructure and expanders
+#
+
+config HAVE_GPIO_LIB
+ bool
+ help
+ Platforms select gpiolib if they use this infrastructure
+ for all their GPIOs, usually starting with ones integrated
+ into SOC processors.
+
+menu "GPIO Support"
+ depends on HAVE_GPIO_LIB
+
+config DEBUG_GPIO
+ bool "Debug GPIO calls"
+ depends on DEBUG_KERNEL
+ help
+ Say Y here to add some extra checks and diagnostics to GPIO calls.
+ The checks help ensure that GPIOs have been properly initialized
+ before they are used and that sleeping calls aren not made from
+ nonsleeping contexts. They can make bitbanged serial protocols
+ slower. The diagnostics help catch the type of setup errors
+ that are most common when setting up new platforms or boards.
+
+# put expanders in the right section, in alphabetical order
+
+comment "I2C GPIO expanders:"
+
+config GPIO_PCA9539
+ tristate "PCA9539 16-bit I/O port"
+ depends on I2C
+ help
+ Say yes here to support the PCA9539 16-bit I/O port. These
+ parts are made by NXP and TI.
+
+ This driver can also be built as a module. If so, the module
+ will be called pca9539.
+
+config GPIO_PCF857X
+ tristate "PCF857x, PCA857x, and PCA967x I2C GPIO expanders"
+ depends on I2C
+ help
+ Say yes here to provide access to most "quasi-bidirectional" I2C
+ GPIO expanders used for additional digital outputs or inputs.
+ Most of these parts are from NXP, though TI is a second source for
+ some of them. Compatible models include:
+
+ 8 bits: pcf8574, pcf8574a, pca8574, pca8574a,
+ pca9670, pca9672, pca9674, pca9674a
+
+ 16 bits: pcf8575, pcf8575c, pca8575,
+ pca9671, pca9673, pca9675
+
+ Your board setup code will need to declare the expanders in
+ use, and assign numbers to the GPIOs they expose. Those GPIOs
+ can then be used from drivers and other kernel code, just like
+ other GPIOs, but only accessible from task contexts.
+
+ This driver provides an in-kernel interface to those GPIOs using
+ platform-neutral GPIO calls.
+
+comment "SPI GPIO expanders:"
+
+config GPIO_MCP23S08
+ tristate "Microchip MCP23S08 I/O expander"
+ depends on SPI_MASTER
+ help
+ SPI driver for Microchip MCP23S08 I/O expander. This provides
+ a GPIO interface supporting inputs and outputs.
+
+endmenu
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
new file mode 100644
index 000000000000..470ecd6aa778
--- /dev/null
+++ b/drivers/gpio/Makefile
@@ -0,0 +1,9 @@
+# gpio support: dedicated expander chips, etc
+
+ccflags-$(CONFIG_DEBUG_GPIO) += -DDEBUG
+
+obj-$(CONFIG_HAVE_GPIO_LIB) += gpiolib.o
+
+obj-$(CONFIG_GPIO_MCP23S08) += mcp23s08.o
+obj-$(CONFIG_GPIO_PCA9539) += pca9539.o
+obj-$(CONFIG_GPIO_PCF857X) += pcf857x.o
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
new file mode 100644
index 000000000000..d8db2f8ee411
--- /dev/null
+++ b/drivers/gpio/gpiolib.c
@@ -0,0 +1,567 @@
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/irq.h>
+#include <linux/spinlock.h>
+
+#include <asm/gpio.h>
+
+
+/* Optional implementation infrastructure for GPIO interfaces.
+ *
+ * Platforms may want to use this if they tend to use very many GPIOs
+ * that aren't part of a System-On-Chip core; or across I2C/SPI/etc.
+ *
+ * When kernel footprint or instruction count is an issue, simpler
+ * implementations may be preferred. The GPIO programming interface
+ * allows for inlining speed-critical get/set operations for common
+ * cases, so that access to SOC-integrated GPIOs can sometimes cost
+ * only an instruction or two per bit.
+ */
+
+
+/* When debugging, extend minimal trust to callers and platform code.
+ * Also emit diagnostic messages that may help initial bringup, when
+ * board setup or driver bugs are most common.
+ *
+ * Otherwise, minimize overhead in what may be bitbanging codepaths.
+ */
+#ifdef DEBUG
+#define extra_checks 1
+#else
+#define extra_checks 0
+#endif
+
+/* gpio_lock prevents conflicts during gpio_desc[] table updates.
+ * While any GPIO is requested, its gpio_chip is not removable;
+ * each GPIO's "requested" flag serves as a lock and refcount.
+ */
+static DEFINE_SPINLOCK(gpio_lock);
+
+struct gpio_desc {
+ struct gpio_chip *chip;
+ unsigned long flags;
+/* flag symbols are bit numbers */
+#define FLAG_REQUESTED 0
+#define FLAG_IS_OUT 1
+
+#ifdef CONFIG_DEBUG_FS
+ const char *label;
+#endif
+};
+static struct gpio_desc gpio_desc[ARCH_NR_GPIOS];
+
+static inline void desc_set_label(struct gpio_desc *d, const char *label)
+{
+#ifdef CONFIG_DEBUG_FS
+ d->label = label;
+#endif
+}
+
+/* Warn when drivers omit gpio_request() calls -- legal but ill-advised
+ * when setting direction, and otherwise illegal. Until board setup code
+ * and drivers use explicit requests everywhere (which won't happen when
+ * those calls have no teeth) we can't avoid autorequesting. This nag
+ * message should motivate switching to explicit requests...
+ */
+static void gpio_ensure_requested(struct gpio_desc *desc)
+{
+ if (test_and_set_bit(FLAG_REQUESTED, &desc->flags) == 0) {
+ pr_warning("GPIO-%d autorequested\n", (int)(desc - gpio_desc));
+ desc_set_label(desc, "[auto]");
+ }
+}
+
+/* caller holds gpio_lock *OR* gpio is marked as requested */
+static inline struct gpio_chip *gpio_to_chip(unsigned gpio)
+{
+ return gpio_desc[gpio].chip;
+}
+
+/**
+ * gpiochip_add() - register a gpio_chip
+ * @chip: the chip to register, with chip->base initialized
+ * Context: potentially before irqs or kmalloc will work
+ *
+ * Returns a negative errno if the chip can't be registered, such as
+ * because the chip->base is invalid or already associated with a
+ * different chip. Otherwise it returns zero as a success code.
+ */
+int gpiochip_add(struct gpio_chip *chip)
+{
+ unsigned long flags;
+ int status = 0;
+ unsigned id;
+
+ /* NOTE chip->base negative is reserved to mean a request for
+ * dynamic allocation. We don't currently support that.
+ */
+
+ if (chip->base < 0 || (chip->base + chip->ngpio) >= ARCH_NR_GPIOS) {
+ status = -EINVAL;
+ goto fail;
+ }
+
+ spin_lock_irqsave(&gpio_lock, flags);
+
+ /* these GPIO numbers must not be managed by another gpio_chip */
+ for (id = chip->base; id < chip->base + chip->ngpio; id++) {
+ if (gpio_desc[id].chip != NULL) {
+ status = -EBUSY;
+ break;
+ }
+ }
+ if (status == 0) {
+ for (id = chip->base; id < chip->base + chip->ngpio; id++) {
+ gpio_desc[id].chip = chip;
+ gpio_desc[id].flags = 0;
+ }
+ }
+
+ spin_unlock_irqrestore(&gpio_lock, flags);
+fail:
+ /* failures here can mean systems won't boot... */
+ if (status)
+ pr_err("gpiochip_add: gpios %d..%d (%s) not registered\n",
+ chip->base, chip->base + chip->ngpio,
+ chip->label ? : "generic");
+ return status;
+}
+EXPORT_SYMBOL_GPL(gpiochip_add);
+
+/**
+ * gpiochip_remove() - unregister a gpio_chip
+ * @chip: the chip to unregister
+ *
+ * A gpio_chip with any GPIOs still requested may not be removed.
+ */
+int gpiochip_remove(struct gpio_chip *chip)
+{
+ unsigned long flags;
+ int status = 0;
+ unsigned id;
+
+ spin_lock_irqsave(&gpio_lock, flags);
+
+ for (id = chip->base; id < chip->base + chip->ngpio; id++) {
+ if (test_bit(FLAG_REQUESTED, &gpio_desc[id].flags)) {
+ status = -EBUSY;
+ break;
+ }
+ }
+ if (status == 0) {
+ for (id = chip->base; id < chip->base + chip->ngpio; id++)
+ gpio_desc[id].chip = NULL;
+ }
+
+ spin_unlock_irqrestore(&gpio_lock, flags);
+ return status;
+}
+EXPORT_SYMBOL_GPL(gpiochip_remove);
+
+
+/* These "optional" allocation calls help prevent drivers from stomping
+ * on each other, and help provide better diagnostics in debugfs.
+ * They're called even less than the "set direction" calls.
+ */
+int gpio_request(unsigned gpio, const char *label)
+{
+ struct gpio_desc *desc;
+ int status = -EINVAL;
+ unsigned long flags;
+
+ spin_lock_irqsave(&gpio_lock, flags);
+
+ if (gpio >= ARCH_NR_GPIOS)
+ goto done;
+ desc = &gpio_desc[gpio];
+ if (desc->chip == NULL)
+ goto done;
+
+ /* NOTE: gpio_request() can be called in early boot,
+ * before IRQs are enabled.
+ */
+
+ if (test_and_set_bit(FLAG_REQUESTED, &desc->flags) == 0) {
+ desc_set_label(desc, label ? : "?");
+ status = 0;
+ } else
+ status = -EBUSY;
+
+done:
+ if (status)
+ pr_debug("gpio_request: gpio-%d (%s) status %d\n",
+ gpio, label ? : "?", status);
+ spin_unlock_irqrestore(&gpio_lock, flags);
+ return status;
+}
+EXPORT_SYMBOL_GPL(gpio_request);
+
+void gpio_free(unsigned gpio)
+{
+ unsigned long flags;
+ struct gpio_desc *desc;
+
+ if (gpio >= ARCH_NR_GPIOS) {
+ WARN_ON(extra_checks);
+ return;
+ }
+
+ spin_lock_irqsave(&gpio_lock, flags);
+
+ desc = &gpio_desc[gpio];
+ if (desc->chip && test_and_clear_bit(FLAG_REQUESTED, &desc->flags))
+ desc_set_label(desc, NULL);
+ else
+ WARN_ON(extra_checks);
+
+ spin_unlock_irqrestore(&gpio_lock, flags);
+}
+EXPORT_SYMBOL_GPL(gpio_free);
+
+
+/**
+ * gpiochip_is_requested - return string iff signal was requested
+ * @chip: controller managing the signal
+ * @offset: of signal within controller's 0..(ngpio - 1) range
+ *
+ * Returns NULL if the GPIO is not currently requested, else a string.
+ * If debugfs support is enabled, the string returned is the label passed
+ * to gpio_request(); otherwise it is a meaningless constant.
+ *
+ * This function is for use by GPIO controller drivers. The label can
+ * help with diagnostics, and knowing that the signal is used as a GPIO
+ * can help avoid accidentally multiplexing it to another controller.
+ */
+const char *gpiochip_is_requested(struct gpio_chip *chip, unsigned offset)
+{
+ unsigned gpio = chip->base + offset;
+
+ if (gpio >= ARCH_NR_GPIOS || gpio_desc[gpio].chip != chip)
+ return NULL;
+ if (test_bit(FLAG_REQUESTED, &gpio_desc[gpio].flags) == 0)
+ return NULL;
+#ifdef CONFIG_DEBUG_FS
+ return gpio_desc[gpio].label;
+#else
+ return "?";
+#endif
+}
+EXPORT_SYMBOL_GPL(gpiochip_is_requested);
+
+
+/* Drivers MUST set GPIO direction before making get/set calls. In
+ * some cases this is done in early boot, before IRQs are enabled.
+ *
+ * As a rule these aren't called more than once (except for drivers
+ * using the open-drain emulation idiom) so these are natural places
+ * to accumulate extra debugging checks. Note that we can't (yet)
+ * rely on gpio_request() having been called beforehand.
+ */
+
+int gpio_direction_input(unsigned gpio)
+{
+ unsigned long flags;
+ struct gpio_chip *chip;
+ struct gpio_desc *desc = &gpio_desc[gpio];
+ int status = -EINVAL;
+
+ spin_lock_irqsave(&gpio_lock, flags);
+
+ if (gpio >= ARCH_NR_GPIOS)
+ goto fail;
+ chip = desc->chip;
+ if (!chip || !chip->get || !chip->direction_input)
+ goto fail;
+ gpio -= chip->base;
+ if (gpio >= chip->ngpio)
+ goto fail;
+ gpio_ensure_requested(desc);
+
+ /* now we know the gpio is valid and chip won't vanish */
+
+ spin_unlock_irqrestore(&gpio_lock, flags);
+
+ might_sleep_if(extra_checks && chip->can_sleep);
+
+ status = chip->direction_input(chip, gpio);
+ if (status == 0)
+ clear_bit(FLAG_IS_OUT, &desc->flags);
+ return status;
+fail:
+ spin_unlock_irqrestore(&gpio_lock, flags);
+ if (status)
+ pr_debug("%s: gpio-%d status %d\n",
+ __FUNCTION__, gpio, status);
+ return status;
+}
+EXPORT_SYMBOL_GPL(gpio_direction_input);
+
+int gpio_direction_output(unsigned gpio, int value)
+{
+ unsigned long flags;
+ struct gpio_chip *chip;
+ struct gpio_desc *desc = &gpio_desc[gpio];
+ int status = -EINVAL;
+
+ spin_lock_irqsave(&gpio_lock, flags);
+
+ if (gpio >= ARCH_NR_GPIOS)
+ goto fail;
+ chip = desc->chip;
+ if (!chip || !chip->set || !chip->direction_output)
+ goto fail;
+ gpio -= chip->base;
+ if (gpio >= chip->ngpio)
+ goto fail;
+ gpio_ensure_requested(desc);
+
+ /* now we know the gpio is valid and chip won't vanish */
+
+ spin_unlock_irqrestore(&gpio_lock, flags);
+
+ might_sleep_if(extra_checks && chip->can_sleep);
+
+ status = chip->direction_output(chip, gpio, value);
+ if (status == 0)
+ set_bit(FLAG_IS_OUT, &desc->flags);
+ return status;
+fail:
+ spin_unlock_irqrestore(&gpio_lock, flags);
+ if (status)
+ pr_debug("%s: gpio-%d status %d\n",
+ __FUNCTION__, gpio, status);
+ return status;
+}
+EXPORT_SYMBOL_GPL(gpio_direction_output);
+
+
+/* I/O calls are only valid after configuration completed; the relevant
+ * "is this a valid GPIO" error checks should already have been done.
+ *
+ * "Get" operations are often inlinable as reading a pin value register,
+ * and masking the relevant bit in that register.
+ *
+ * When "set" operations are inlinable, they involve writing that mask to
+ * one register to set a low value, or a different register to set it high.
+ * Otherwise locking is needed, so there may be little value to inlining.
+ *
+ *------------------------------------------------------------------------
+ *
+ * IMPORTANT!!! The hot paths -- get/set value -- assume that callers
+ * have requested the GPIO. That can include implicit requesting by
+ * a direction setting call. Marking a gpio as requested locks its chip
+ * in memory, guaranteeing that these table lookups need no more locking
+ * and that gpiochip_remove() will fail.
+ *
+ * REVISIT when debugging, consider adding some instrumentation to ensure
+ * that the GPIO was actually requested.
+ */
+
+/**
+ * __gpio_get_value() - return a gpio's value
+ * @gpio: gpio whose value will be returned
+ * Context: any
+ *
+ * This is used directly or indirectly to implement gpio_get_value().
+ * It returns the zero or nonzero value provided by the associated
+ * gpio_chip.get() method; or zero if no such method is provided.
+ */
+int __gpio_get_value(unsigned gpio)
+{
+ struct gpio_chip *chip;
+
+ chip = gpio_to_chip(gpio);
+ WARN_ON(extra_checks && chip->can_sleep);
+ return chip->get ? chip->get(chip, gpio - chip->base) : 0;
+}
+EXPORT_SYMBOL_GPL(__gpio_get_value);
+
+/**
+ * __gpio_set_value() - assign a gpio's value
+ * @gpio: gpio whose value will be assigned
+ * @value: value to assign
+ * Context: any
+ *
+ * This is used directly or indirectly to implement gpio_set_value().
+ * It invokes the associated gpio_chip.set() method.
+ */
+void __gpio_set_value(unsigned gpio, int value)
+{
+ struct gpio_chip *chip;
+
+ chip = gpio_to_chip(gpio);
+ WARN_ON(extra_checks && chip->can_sleep);
+ chip->set(chip, gpio - chip->base, value);
+}
+EXPORT_SYMBOL_GPL(__gpio_set_value);
+
+/**
+ * __gpio_cansleep() - report whether gpio value access will sleep
+ * @gpio: gpio in question
+ * Context: any
+ *
+ * This is used directly or indirectly to implement gpio_cansleep(). It
+ * returns nonzero if access reading or writing the GPIO value can sleep.
+ */
+int __gpio_cansleep(unsigned gpio)
+{
+ struct gpio_chip *chip;
+
+ /* only call this on GPIOs that are valid! */
+ chip = gpio_to_chip(gpio);
+
+ return chip->can_sleep;
+}
+EXPORT_SYMBOL_GPL(__gpio_cansleep);
+
+
+
+/* There's no value in making it easy to inline GPIO calls that may sleep.
+ * Common examples include ones connected to I2C or SPI chips.
+ */
+
+int gpio_get_value_cansleep(unsigned gpio)
+{
+ struct gpio_chip *chip;
+
+ might_sleep_if(extra_checks);
+ chip = gpio_to_chip(gpio);
+ return chip->get(chip, gpio - chip->base);
+}
+EXPORT_SYMBOL_GPL(gpio_get_value_cansleep);
+
+void gpio_set_value_cansleep(unsigned gpio, int value)
+{
+ struct gpio_chip *chip;
+
+ might_sleep_if(extra_checks);
+ chip = gpio_to_chip(gpio);
+ chip->set(chip, gpio - chip->base, value);
+}
+EXPORT_SYMBOL_GPL(gpio_set_value_cansleep);
+
+
+#ifdef CONFIG_DEBUG_FS
+
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+
+
+static void gpiolib_dbg_show(struct seq_file *s, struct gpio_chip *chip)
+{
+ unsigned i;
+ unsigned gpio = chip->base;
+ struct gpio_desc *gdesc = &gpio_desc[gpio];
+ int is_out;
+
+ for (i = 0; i < chip->ngpio; i++, gpio++, gdesc++) {
+ if (!test_bit(FLAG_REQUESTED, &gdesc->flags))
+ continue;
+
+ is_out = test_bit(FLAG_IS_OUT, &gdesc->flags);
+ seq_printf(s, " gpio-%-3d (%-12s) %s %s",
+ gpio, gdesc->label,
+ is_out ? "out" : "in ",
+ chip->get
+ ? (chip->get(chip, i) ? "hi" : "lo")
+ : "? ");
+
+ if (!is_out) {
+ int irq = gpio_to_irq(gpio);
+ struct irq_desc *desc = irq_desc + irq;
+
+ /* This races with request_irq(), set_irq_type(),
+ * and set_irq_wake() ... but those are "rare".
+ *
+ * More significantly, trigger type flags aren't
+ * currently maintained by genirq.
+ */
+ if (irq >= 0 && desc->action) {
+ char *trigger;
+
+ switch (desc->status & IRQ_TYPE_SENSE_MASK) {
+ case IRQ_TYPE_NONE:
+ trigger = "(default)";
+ break;
+ case IRQ_TYPE_EDGE_FALLING:
+ trigger = "edge-falling";
+ break;
+ case IRQ_TYPE_EDGE_RISING:
+ trigger = "edge-rising";
+ break;
+ case IRQ_TYPE_EDGE_BOTH:
+ trigger = "edge-both";
+ break;
+ case IRQ_TYPE_LEVEL_HIGH:
+ trigger = "level-high";
+ break;
+ case IRQ_TYPE_LEVEL_LOW:
+ trigger = "level-low";
+ break;
+ default:
+ trigger = "?trigger?";
+ break;
+ }
+
+ seq_printf(s, " irq-%d %s%s",
+ irq, trigger,
+ (desc->status & IRQ_WAKEUP)
+ ? " wakeup" : "");
+ }
+ }
+
+ seq_printf(s, "\n");
+ }
+}
+
+static int gpiolib_show(struct seq_file *s, void *unused)
+{
+ struct gpio_chip *chip = NULL;
+ unsigned gpio;
+ int started = 0;
+
+ /* REVISIT this isn't locked against gpio_chip removal ... */
+
+ for (gpio = 0; gpio < ARCH_NR_GPIOS; gpio++) {
+ if (chip == gpio_desc[gpio].chip)
+ continue;
+ chip = gpio_desc[gpio].chip;
+ if (!chip)
+ continue;
+
+ seq_printf(s, "%sGPIOs %d-%d, %s%s:\n",
+ started ? "\n" : "",
+ chip->base, chip->base + chip->ngpio - 1,
+ chip->label ? : "generic",
+ chip->can_sleep ? ", can sleep" : "");
+ started = 1;
+ if (chip->dbg_show)
+ chip->dbg_show(s, chip);
+ else
+ gpiolib_dbg_show(s, chip);
+ }
+ return 0;
+}
+
+static int gpiolib_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, gpiolib_show, NULL);
+}
+
+static struct file_operations gpiolib_operations = {
+ .open = gpiolib_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int __init gpiolib_debugfs_init(void)
+{
+ /* /sys/kernel/debug/gpio */
+ (void) debugfs_create_file("gpio", S_IFREG | S_IRUGO,
+ NULL, NULL, &gpiolib_operations);
+ return 0;
+}
+subsys_initcall(gpiolib_debugfs_init);
+
+#endif /* DEBUG_FS */
diff --git a/drivers/gpio/mcp23s08.c b/drivers/gpio/mcp23s08.c
new file mode 100644
index 000000000000..bb60e8c1a1f0
--- /dev/null
+++ b/drivers/gpio/mcp23s08.c
@@ -0,0 +1,357 @@
+/*
+ * mcp23s08.c - SPI gpio expander driver
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/workqueue.h>
+#include <linux/mutex.h>
+
+#include <linux/spi/spi.h>
+#include <linux/spi/mcp23s08.h>
+
+#include <asm/gpio.h>
+
+
+/* Registers are all 8 bits wide.
+ *
+ * The mcp23s17 has twice as many bits, and can be configured to work
+ * with either 16 bit registers or with two adjacent 8 bit banks.
+ *
+ * Also, there are I2C versions of both chips.
+ */
+#define MCP_IODIR 0x00 /* init/reset: all ones */
+#define MCP_IPOL 0x01
+#define MCP_GPINTEN 0x02
+#define MCP_DEFVAL 0x03
+#define MCP_INTCON 0x04
+#define MCP_IOCON 0x05
+# define IOCON_SEQOP (1 << 5)
+# define IOCON_HAEN (1 << 3)
+# define IOCON_ODR (1 << 2)
+# define IOCON_INTPOL (1 << 1)
+#define MCP_GPPU 0x06
+#define MCP_INTF 0x07
+#define MCP_INTCAP 0x08
+#define MCP_GPIO 0x09
+#define MCP_OLAT 0x0a
+
+struct mcp23s08 {
+ struct spi_device *spi;
+ u8 addr;
+
+ /* lock protects the cached values */
+ struct mutex lock;
+ u8 cache[11];
+
+ struct gpio_chip chip;
+
+ struct work_struct work;
+};
+
+static int mcp23s08_read(struct mcp23s08 *mcp, unsigned reg)
+{
+ u8 tx[2], rx[1];
+ int status;
+
+ tx[0] = mcp->addr | 0x01;
+ tx[1] = reg;
+ status = spi_write_then_read(mcp->spi, tx, sizeof tx, rx, sizeof rx);
+ return (status < 0) ? status : rx[0];
+}
+
+static int mcp23s08_write(struct mcp23s08 *mcp, unsigned reg, u8 val)
+{
+ u8 tx[3];
+
+ tx[0] = mcp->addr;
+ tx[1] = reg;
+ tx[2] = val;
+ return spi_write_then_read(mcp->spi, tx, sizeof tx, NULL, 0);
+}
+
+static int
+mcp23s08_read_regs(struct mcp23s08 *mcp, unsigned reg, u8 *vals, unsigned n)
+{
+ u8 tx[2];
+
+ if ((n + reg) > sizeof mcp->cache)
+ return -EINVAL;
+ tx[0] = mcp->addr | 0x01;
+ tx[1] = reg;
+ return spi_write_then_read(mcp->spi, tx, sizeof tx, vals, n);
+}
+
+/*----------------------------------------------------------------------*/
+
+static int mcp23s08_direction_input(struct gpio_chip *chip, unsigned offset)
+{
+ struct mcp23s08 *mcp = container_of(chip, struct mcp23s08, chip);
+ int status;
+
+ mutex_lock(&mcp->lock);
+ mcp->cache[MCP_IODIR] |= (1 << offset);
+ status = mcp23s08_write(mcp, MCP_IODIR, mcp->cache[MCP_IODIR]);
+ mutex_unlock(&mcp->lock);
+ return status;
+}
+
+static int mcp23s08_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct mcp23s08 *mcp = container_of(chip, struct mcp23s08, chip);
+ int status;
+
+ mutex_lock(&mcp->lock);
+
+ /* REVISIT reading this clears any IRQ ... */
+ status = mcp23s08_read(mcp, MCP_GPIO);
+ if (status < 0)
+ status = 0;
+ else {
+ mcp->cache[MCP_GPIO] = status;
+ status = !!(status & (1 << offset));
+ }
+ mutex_unlock(&mcp->lock);
+ return status;
+}
+
+static int __mcp23s08_set(struct mcp23s08 *mcp, unsigned mask, int value)
+{
+ u8 olat = mcp->cache[MCP_OLAT];
+
+ if (value)
+ olat |= mask;
+ else
+ olat &= ~mask;
+ mcp->cache[MCP_OLAT] = olat;
+ return mcp23s08_write(mcp, MCP_OLAT, olat);
+}
+
+static void mcp23s08_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ struct mcp23s08 *mcp = container_of(chip, struct mcp23s08, chip);
+ u8 mask = 1 << offset;
+
+ mutex_lock(&mcp->lock);
+ __mcp23s08_set(mcp, mask, value);
+ mutex_unlock(&mcp->lock);
+}
+
+static int
+mcp23s08_direction_output(struct gpio_chip *chip, unsigned offset, int value)
+{
+ struct mcp23s08 *mcp = container_of(chip, struct mcp23s08, chip);
+ u8 mask = 1 << offset;
+ int status;
+
+ mutex_lock(&mcp->lock);
+ status = __mcp23s08_set(mcp, mask, value);
+ if (status == 0) {
+ mcp->cache[MCP_IODIR] &= ~mask;
+ status = mcp23s08_write(mcp, MCP_IODIR, mcp->cache[MCP_IODIR]);
+ }
+ mutex_unlock(&mcp->lock);
+ return status;
+}
+
+/*----------------------------------------------------------------------*/
+
+#ifdef CONFIG_DEBUG_FS
+
+#include <linux/seq_file.h>
+
+/*
+ * This shows more info than the generic gpio dump code:
+ * pullups, deglitching, open drain drive.
+ */
+static void mcp23s08_dbg_show(struct seq_file *s, struct gpio_chip *chip)
+{
+ struct mcp23s08 *mcp;
+ char bank;
+ unsigned t;
+ unsigned mask;
+
+ mcp = container_of(chip, struct mcp23s08, chip);
+
+ /* NOTE: we only handle one bank for now ... */
+ bank = '0' + ((mcp->addr >> 1) & 0x3);
+
+ mutex_lock(&mcp->lock);
+ t = mcp23s08_read_regs(mcp, 0, mcp->cache, sizeof mcp->cache);
+ if (t < 0) {
+ seq_printf(s, " I/O ERROR %d\n", t);
+ goto done;
+ }
+
+ for (t = 0, mask = 1; t < 8; t++, mask <<= 1) {
+ const char *label;
+
+ label = gpiochip_is_requested(chip, t);
+ if (!label)
+ continue;
+
+ seq_printf(s, " gpio-%-3d P%c.%d (%-12s) %s %s %s",
+ chip->base + t, bank, t, label,
+ (mcp->cache[MCP_IODIR] & mask) ? "in " : "out",
+ (mcp->cache[MCP_GPIO] & mask) ? "hi" : "lo",
+ (mcp->cache[MCP_GPPU] & mask) ? " " : "up");
+ /* NOTE: ignoring the irq-related registers */
+ seq_printf(s, "\n");
+ }
+done:
+ mutex_unlock(&mcp->lock);
+}
+
+#else
+#define mcp23s08_dbg_show NULL
+#endif
+
+/*----------------------------------------------------------------------*/
+
+static int mcp23s08_probe(struct spi_device *spi)
+{
+ struct mcp23s08 *mcp;
+ struct mcp23s08_platform_data *pdata;
+ int status;
+ int do_update = 0;
+
+ pdata = spi->dev.platform_data;
+ if (!pdata || pdata->slave > 3 || !pdata->base)
+ return -ENODEV;
+
+ mcp = kzalloc(sizeof *mcp, GFP_KERNEL);
+ if (!mcp)
+ return -ENOMEM;
+
+ mutex_init(&mcp->lock);
+
+ mcp->spi = spi;
+ mcp->addr = 0x40 | (pdata->slave << 1);
+
+ mcp->chip.label = "mcp23s08",
+
+ mcp->chip.direction_input = mcp23s08_direction_input;
+ mcp->chip.get = mcp23s08_get;
+ mcp->chip.direction_output = mcp23s08_direction_output;
+ mcp->chip.set = mcp23s08_set;
+ mcp->chip.dbg_show = mcp23s08_dbg_show;
+
+ mcp->chip.base = pdata->base;
+ mcp->chip.ngpio = 8;
+ mcp->chip.can_sleep = 1;
+
+ spi_set_drvdata(spi, mcp);
+
+ /* verify MCP_IOCON.SEQOP = 0, so sequential reads work */
+ status = mcp23s08_read(mcp, MCP_IOCON);
+ if (status < 0)
+ goto fail;
+ if (status & IOCON_SEQOP) {
+ status &= ~IOCON_SEQOP;
+ status = mcp23s08_write(mcp, MCP_IOCON, (u8) status);
+ if (status < 0)
+ goto fail;
+ }
+
+ /* configure ~100K pullups */
+ status = mcp23s08_write(mcp, MCP_GPPU, pdata->pullups);
+ if (status < 0)
+ goto fail;
+
+ status = mcp23s08_read_regs(mcp, 0, mcp->cache, sizeof mcp->cache);
+ if (status < 0)
+ goto fail;
+
+ /* disable inverter on input */
+ if (mcp->cache[MCP_IPOL] != 0) {
+ mcp->cache[MCP_IPOL] = 0;
+ do_update = 1;
+ }
+
+ /* disable irqs */
+ if (mcp->cache[MCP_GPINTEN] != 0) {
+ mcp->cache[MCP_GPINTEN] = 0;
+ do_update = 1;
+ }
+
+ if (do_update) {
+ u8 tx[4];
+
+ tx[0] = mcp->addr;
+ tx[1] = MCP_IPOL;
+ memcpy(&tx[2], &mcp->cache[MCP_IPOL], sizeof(tx) - 2);
+ status = spi_write_then_read(mcp->spi, tx, sizeof tx, NULL, 0);
+
+ /* FIXME check status... */
+ }
+
+ status = gpiochip_add(&mcp->chip);
+
+ /* NOTE: these chips have a relatively sane IRQ framework, with
+ * per-signal masking and level/edge triggering. It's not yet
+ * handled here...
+ */
+
+ if (pdata->setup) {
+ status = pdata->setup(spi, mcp->chip.base,
+ mcp->chip.ngpio, pdata->context);
+ if (status < 0)
+ dev_dbg(&spi->dev, "setup --> %d\n", status);
+ }
+
+ return 0;
+
+fail:
+ kfree(mcp);
+ return status;
+}
+
+static int mcp23s08_remove(struct spi_device *spi)
+{
+ struct mcp23s08 *mcp = spi_get_drvdata(spi);
+ struct mcp23s08_platform_data *pdata = spi->dev.platform_data;
+ int status = 0;
+
+ if (pdata->teardown) {
+ status = pdata->teardown(spi,
+ mcp->chip.base, mcp->chip.ngpio,
+ pdata->context);
+ if (status < 0) {
+ dev_err(&spi->dev, "%s --> %d\n", "teardown", status);
+ return status;
+ }
+ }
+
+ status = gpiochip_remove(&mcp->chip);
+ if (status == 0)
+ kfree(mcp);
+ else
+ dev_err(&spi->dev, "%s --> %d\n", "remove", status);
+ return status;
+}
+
+static struct spi_driver mcp23s08_driver = {
+ .probe = mcp23s08_probe,
+ .remove = mcp23s08_remove,
+ .driver = {
+ .name = "mcp23s08",
+ .owner = THIS_MODULE,
+ },
+};
+
+/*----------------------------------------------------------------------*/
+
+static int __init mcp23s08_init(void)
+{
+ return spi_register_driver(&mcp23s08_driver);
+}
+module_init(mcp23s08_init);
+
+static void __exit mcp23s08_exit(void)
+{
+ spi_unregister_driver(&mcp23s08_driver);
+}
+module_exit(mcp23s08_exit);
+
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/gpio/pca9539.c b/drivers/gpio/pca9539.c
new file mode 100644
index 000000000000..3e85c92a7d59
--- /dev/null
+++ b/drivers/gpio/pca9539.c
@@ -0,0 +1,271 @@
+/*
+ * pca9539.c - 16-bit I/O port with interrupt and reset
+ *
+ * Copyright (C) 2005 Ben Gardner <bgardner@wabtec.com>
+ * Copyright (C) 2007 Marvell International Ltd.
+ *
+ * Derived from drivers/i2c/chips/pca9539.c
+ *
+ * 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; version 2 of the License.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/i2c/pca9539.h>
+
+#include <asm/gpio.h>
+
+
+#define NR_PCA9539_GPIOS 16
+
+#define PCA9539_INPUT 0
+#define PCA9539_OUTPUT 2
+#define PCA9539_INVERT 4
+#define PCA9539_DIRECTION 6
+
+struct pca9539_chip {
+ unsigned gpio_start;
+ uint16_t reg_output;
+ uint16_t reg_direction;
+
+ struct i2c_client *client;
+ struct gpio_chip gpio_chip;
+};
+
+/* NOTE: we can't currently rely on fault codes to come from SMBus
+ * calls, so we map all errors to EIO here and return zero otherwise.
+ */
+static int pca9539_write_reg(struct pca9539_chip *chip, int reg, uint16_t val)
+{
+ if (i2c_smbus_write_word_data(chip->client, reg, val) < 0)
+ return -EIO;
+ else
+ return 0;
+}
+
+static int pca9539_read_reg(struct pca9539_chip *chip, int reg, uint16_t *val)
+{
+ int ret;
+
+ ret = i2c_smbus_read_word_data(chip->client, reg);
+ if (ret < 0) {
+ dev_err(&chip->client->dev, "failed reading register\n");
+ return -EIO;
+ }
+
+ *val = (uint16_t)ret;
+ return 0;
+}
+
+static int pca9539_gpio_direction_input(struct gpio_chip *gc, unsigned off)
+{
+ struct pca9539_chip *chip;
+ uint16_t reg_val;
+ int ret;
+
+ chip = container_of(gc, struct pca9539_chip, gpio_chip);
+
+ reg_val = chip->reg_direction | (1u << off);
+ ret = pca9539_write_reg(chip, PCA9539_DIRECTION, reg_val);
+ if (ret)
+ return ret;
+
+ chip->reg_direction = reg_val;
+ return 0;
+}
+
+static int pca9539_gpio_direction_output(struct gpio_chip *gc,
+ unsigned off, int val)
+{
+ struct pca9539_chip *chip;
+ uint16_t reg_val;
+ int ret;
+
+ chip = container_of(gc, struct pca9539_chip, gpio_chip);
+
+ /* set output level */
+ if (val)
+ reg_val = chip->reg_output | (1u << off);
+ else
+ reg_val = chip->reg_output & ~(1u << off);
+
+ ret = pca9539_write_reg(chip, PCA9539_OUTPUT, reg_val);
+ if (ret)
+ return ret;
+
+ chip->reg_output = reg_val;
+
+ /* then direction */
+ reg_val = chip->reg_direction & ~(1u << off);
+ ret = pca9539_write_reg(chip, PCA9539_DIRECTION, reg_val);
+ if (ret)
+ return ret;
+
+ chip->reg_direction = reg_val;
+ return 0;
+}
+
+static int pca9539_gpio_get_value(struct gpio_chip *gc, unsigned off)
+{
+ struct pca9539_chip *chip;
+ uint16_t reg_val;
+ int ret;
+
+ chip = container_of(gc, struct pca9539_chip, gpio_chip);
+
+ ret = pca9539_read_reg(chip, PCA9539_INPUT, &reg_val);
+ if (ret < 0) {
+ /* NOTE: diagnostic already emitted; that's all we should
+ * do unless gpio_*_value_cansleep() calls become different
+ * from their nonsleeping siblings (and report faults).
+ */
+ return 0;
+ }
+
+ return (reg_val & (1u << off)) ? 1 : 0;
+}
+
+static void pca9539_gpio_set_value(struct gpio_chip *gc, unsigned off, int val)
+{
+ struct pca9539_chip *chip;
+ uint16_t reg_val;
+ int ret;
+
+ chip = container_of(gc, struct pca9539_chip, gpio_chip);
+
+ if (val)
+ reg_val = chip->reg_output | (1u << off);
+ else
+ reg_val = chip->reg_output & ~(1u << off);
+
+ ret = pca9539_write_reg(chip, PCA9539_OUTPUT, reg_val);
+ if (ret)
+ return;
+
+ chip->reg_output = reg_val;
+}
+
+static int pca9539_init_gpio(struct pca9539_chip *chip)
+{
+ struct gpio_chip *gc;
+
+ gc = &chip->gpio_chip;
+
+ gc->direction_input = pca9539_gpio_direction_input;
+ gc->direction_output = pca9539_gpio_direction_output;
+ gc->get = pca9539_gpio_get_value;
+ gc->set = pca9539_gpio_set_value;
+
+ gc->base = chip->gpio_start;
+ gc->ngpio = NR_PCA9539_GPIOS;
+ gc->label = "pca9539";
+
+ return gpiochip_add(gc);
+}
+
+static int __devinit pca9539_probe(struct i2c_client *client)
+{
+ struct pca9539_platform_data *pdata;
+ struct pca9539_chip *chip;
+ int ret;
+
+ pdata = client->dev.platform_data;
+ if (pdata == NULL)
+ return -ENODEV;
+
+ chip = kzalloc(sizeof(struct pca9539_chip), GFP_KERNEL);
+ if (chip == NULL)
+ return -ENOMEM;
+
+ chip->client = client;
+
+ chip->gpio_start = pdata->gpio_base;
+
+ /* initialize cached registers from their original values.
+ * we can't share this chip with another i2c master.
+ */
+ ret = pca9539_read_reg(chip, PCA9539_OUTPUT, &chip->reg_output);
+ if (ret)
+ goto out_failed;
+
+ ret = pca9539_read_reg(chip, PCA9539_DIRECTION, &chip->reg_direction);
+ if (ret)
+ goto out_failed;
+
+ /* set platform specific polarity inversion */
+ ret = pca9539_write_reg(chip, PCA9539_INVERT, pdata->invert);
+ if (ret)
+ goto out_failed;
+
+ ret = pca9539_init_gpio(chip);
+ if (ret)
+ goto out_failed;
+
+ if (pdata->setup) {
+ ret = pdata->setup(client, chip->gpio_chip.base,
+ chip->gpio_chip.ngpio, pdata->context);
+ if (ret < 0)
+ dev_warn(&client->dev, "setup failed, %d\n", ret);
+ }
+
+ i2c_set_clientdata(client, chip);
+ return 0;
+
+out_failed:
+ kfree(chip);
+ return ret;
+}
+
+static int pca9539_remove(struct i2c_client *client)
+{
+ struct pca9539_platform_data *pdata = client->dev.platform_data;
+ struct pca9539_chip *chip = i2c_get_clientdata(client);
+ int ret = 0;
+
+ if (pdata->teardown) {
+ ret = pdata->teardown(client, chip->gpio_chip.base,
+ chip->gpio_chip.ngpio, pdata->context);
+ if (ret < 0) {
+ dev_err(&client->dev, "%s failed, %d\n",
+ "teardown", ret);
+ return ret;
+ }
+ }
+
+ ret = gpiochip_remove(&chip->gpio_chip);
+ if (ret) {
+ dev_err(&client->dev, "%s failed, %d\n",
+ "gpiochip_remove()", ret);
+ return ret;
+ }
+
+ kfree(chip);
+ return 0;
+}
+
+static struct i2c_driver pca9539_driver = {
+ .driver = {
+ .name = "pca9539",
+ },
+ .probe = pca9539_probe,
+ .remove = pca9539_remove,
+};
+
+static int __init pca9539_init(void)
+{
+ return i2c_add_driver(&pca9539_driver);
+}
+module_init(pca9539_init);
+
+static void __exit pca9539_exit(void)
+{
+ i2c_del_driver(&pca9539_driver);
+}
+module_exit(pca9539_exit);
+
+MODULE_AUTHOR("eric miao <eric.miao@marvell.com>");
+MODULE_DESCRIPTION("GPIO expander driver for PCA9539");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpio/pcf857x.c b/drivers/gpio/pcf857x.c
new file mode 100644
index 000000000000..c6b3b5378384
--- /dev/null
+++ b/drivers/gpio/pcf857x.c
@@ -0,0 +1,330 @@
+/*
+ * pcf857x - driver for pcf857x, pca857x, and pca967x I2C GPIO expanders
+ *
+ * Copyright (C) 2007 David Brownell
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c/pcf857x.h>
+
+#include <asm/gpio.h>
+
+
+/*
+ * The pcf857x, pca857x, and pca967x chips only expose one read and one
+ * write register. Writing a "one" bit (to match the reset state) lets
+ * that pin be used as an input; it's not an open-drain model, but acts
+ * a bit like one. This is described as "quasi-bidirectional"; read the
+ * chip documentation for details.
+ *
+ * Many other I2C GPIO expander chips (like the pca953x models) have
+ * more complex register models and more conventional circuitry using
+ * push/pull drivers. They often use the same 0x20..0x27 addresses as
+ * pcf857x parts, making the "legacy" I2C driver model problematic.
+ */
+struct pcf857x {
+ struct gpio_chip chip;
+ struct i2c_client *client;
+ unsigned out; /* software latch */
+};
+
+/*-------------------------------------------------------------------------*/
+
+/* Talk to 8-bit I/O expander */
+
+static int pcf857x_input8(struct gpio_chip *chip, unsigned offset)
+{
+ struct pcf857x *gpio = container_of(chip, struct pcf857x, chip);
+
+ gpio->out |= (1 << offset);
+ return i2c_smbus_write_byte(gpio->client, gpio->out);
+}
+
+static int pcf857x_get8(struct gpio_chip *chip, unsigned offset)
+{
+ struct pcf857x *gpio = container_of(chip, struct pcf857x, chip);
+ s32 value;
+
+ value = i2c_smbus_read_byte(gpio->client);
+ return (value < 0) ? 0 : (value & (1 << offset));
+}
+
+static int pcf857x_output8(struct gpio_chip *chip, unsigned offset, int value)
+{
+ struct pcf857x *gpio = container_of(chip, struct pcf857x, chip);
+ unsigned bit = 1 << offset;
+
+ if (value)
+ gpio->out |= bit;
+ else
+ gpio->out &= ~bit;
+ return i2c_smbus_write_byte(gpio->client, gpio->out);
+}
+
+static void pcf857x_set8(struct gpio_chip *chip, unsigned offset, int value)
+{
+ pcf857x_output8(chip, offset, value);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* Talk to 16-bit I/O expander */
+
+static int i2c_write_le16(struct i2c_client *client, u16 word)
+{
+ u8 buf[2] = { word & 0xff, word >> 8, };
+ int status;
+
+ status = i2c_master_send(client, buf, 2);
+ return (status < 0) ? status : 0;
+}
+
+static int i2c_read_le16(struct i2c_client *client)
+{
+ u8 buf[2];
+ int status;
+
+ status = i2c_master_recv(client, buf, 2);
+ if (status < 0)
+ return status;
+ return (buf[1] << 8) | buf[0];
+}
+
+static int pcf857x_input16(struct gpio_chip *chip, unsigned offset)
+{
+ struct pcf857x *gpio = container_of(chip, struct pcf857x, chip);
+
+ gpio->out |= (1 << offset);
+ return i2c_write_le16(gpio->client, gpio->out);
+}
+
+static int pcf857x_get16(struct gpio_chip *chip, unsigned offset)
+{
+ struct pcf857x *gpio = container_of(chip, struct pcf857x, chip);
+ int value;
+
+ value = i2c_read_le16(gpio->client);
+ return (value < 0) ? 0 : (value & (1 << offset));
+}
+
+static int pcf857x_output16(struct gpio_chip *chip, unsigned offset, int value)
+{
+ struct pcf857x *gpio = container_of(chip, struct pcf857x, chip);
+ unsigned bit = 1 << offset;
+
+ if (value)
+ gpio->out |= bit;
+ else
+ gpio->out &= ~bit;
+ return i2c_write_le16(gpio->client, gpio->out);
+}
+
+static void pcf857x_set16(struct gpio_chip *chip, unsigned offset, int value)
+{
+ pcf857x_output16(chip, offset, value);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int pcf857x_probe(struct i2c_client *client)
+{
+ struct pcf857x_platform_data *pdata;
+ struct pcf857x *gpio;
+ int status;
+
+ pdata = client->dev.platform_data;
+ if (!pdata)
+ return -ENODEV;
+
+ /* Allocate, initialize, and register this gpio_chip. */
+ gpio = kzalloc(sizeof *gpio, GFP_KERNEL);
+ if (!gpio)
+ return -ENOMEM;
+
+ gpio->chip.base = pdata->gpio_base;
+ gpio->chip.can_sleep = 1;
+
+ /* NOTE: the OnSemi jlc1562b is also largely compatible with
+ * these parts, notably for output. It has a low-resolution
+ * DAC instead of pin change IRQs; and its inputs can be the
+ * result of comparators.
+ */
+
+ /* 8574 addresses are 0x20..0x27; 8574a uses 0x38..0x3f;
+ * 9670, 9672, 9764, and 9764a use quite a variety.
+ *
+ * NOTE: we don't distinguish here between *4 and *4a parts.
+ */
+ if (strcmp(client->name, "pcf8574") == 0
+ || strcmp(client->name, "pca8574") == 0
+ || strcmp(client->name, "pca9670") == 0
+ || strcmp(client->name, "pca9672") == 0
+ || strcmp(client->name, "pca9674") == 0
+ ) {
+ gpio->chip.ngpio = 8;
+ gpio->chip.direction_input = pcf857x_input8;
+ gpio->chip.get = pcf857x_get8;
+ gpio->chip.direction_output = pcf857x_output8;
+ gpio->chip.set = pcf857x_set8;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_BYTE))
+ status = -EIO;
+
+ /* fail if there's no chip present */
+ else
+ status = i2c_smbus_read_byte(client);
+
+ /* '75/'75c addresses are 0x20..0x27, just like the '74;
+ * the '75c doesn't have a current source pulling high.
+ * 9671, 9673, and 9765 use quite a variety of addresses.
+ *
+ * NOTE: we don't distinguish here between '75 and '75c parts.
+ */
+ } else if (strcmp(client->name, "pcf8575") == 0
+ || strcmp(client->name, "pca8575") == 0
+ || strcmp(client->name, "pca9671") == 0
+ || strcmp(client->name, "pca9673") == 0
+ || strcmp(client->name, "pca9675") == 0
+ ) {
+ gpio->chip.ngpio = 16;
+ gpio->chip.direction_input = pcf857x_input16;
+ gpio->chip.get = pcf857x_get16;
+ gpio->chip.direction_output = pcf857x_output16;
+ gpio->chip.set = pcf857x_set16;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+ status = -EIO;
+
+ /* fail if there's no chip present */
+ else
+ status = i2c_read_le16(client);
+
+ } else
+ status = -ENODEV;
+
+ if (status < 0)
+ goto fail;
+
+ gpio->chip.label = client->name;
+
+ gpio->client = client;
+ i2c_set_clientdata(client, gpio);
+
+ /* NOTE: these chips have strange "quasi-bidirectional" I/O pins.
+ * We can't actually know whether a pin is configured (a) as output
+ * and driving the signal low, or (b) as input and reporting a low
+ * value ... without knowing the last value written since the chip
+ * came out of reset (if any). We can't read the latched output.
+ *
+ * In short, the only reliable solution for setting up pin direction
+ * is to do it explicitly. The setup() method can do that, but it
+ * may cause transient glitching since it can't know the last value
+ * written (some pins may need to be driven low).
+ *
+ * Using pdata->n_latch avoids that trouble. When left initialized
+ * to zero, our software copy of the "latch" then matches the chip's
+ * all-ones reset state. Otherwise it flags pins to be driven low.
+ */
+ gpio->out = ~pdata->n_latch;
+
+ status = gpiochip_add(&gpio->chip);
+ 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, "gpios %d..%d on a %s%s\n",
+ gpio->chip.base,
+ gpio->chip.base + gpio->chip.ngpio - 1,
+ client->name,
+ client->irq ? " (irq ignored)" : "");
+
+ /* Let platform code set up the GPIOs and their users.
+ * Now is the first time anyone could use them.
+ */
+ if (pdata->setup) {
+ status = pdata->setup(client,
+ gpio->chip.base, gpio->chip.ngpio,
+ pdata->context);
+ if (status < 0)
+ dev_warn(&client->dev, "setup --> %d\n", status);
+ }
+
+ return 0;
+
+fail:
+ dev_dbg(&client->dev, "probe error %d for '%s'\n",
+ status, client->name);
+ kfree(gpio);
+ return status;
+}
+
+static int pcf857x_remove(struct i2c_client *client)
+{
+ struct pcf857x_platform_data *pdata = client->dev.platform_data;
+ struct pcf857x *gpio = i2c_get_clientdata(client);
+ int status = 0;
+
+ if (pdata->teardown) {
+ status = pdata->teardown(client,
+ gpio->chip.base, gpio->chip.ngpio,
+ pdata->context);
+ if (status < 0) {
+ dev_err(&client->dev, "%s --> %d\n",
+ "teardown", status);
+ return status;
+ }
+ }
+
+ status = gpiochip_remove(&gpio->chip);
+ if (status == 0)
+ kfree(gpio);
+ else
+ dev_err(&client->dev, "%s --> %d\n", "remove", status);
+ return status;
+}
+
+static struct i2c_driver pcf857x_driver = {
+ .driver = {
+ .name = "pcf857x",
+ .owner = THIS_MODULE,
+ },
+ .probe = pcf857x_probe,
+ .remove = pcf857x_remove,
+};
+
+static int __init pcf857x_init(void)
+{
+ return i2c_add_driver(&pcf857x_driver);
+}
+module_init(pcf857x_init);
+
+static void __exit pcf857x_exit(void)
+{
+ i2c_del_driver(&pcf857x_driver);
+}
+module_exit(pcf857x_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("David Brownell");