ARM: OMAP: I2C: tps65010 driver converts to gpiolib
authorDavid Brownell <david-b@pacbell.net>
Fri, 29 Feb 2008 06:07:28 +0000 (22:07 -0800)
committerTony Lindgren <tony@atomide.com>
Mon, 14 Apr 2008 16:57:06 +0000 (09:57 -0700)
Make the tps65010 driver use gpiolib to expose its GPIOs.

Note: This patch will get merged via omap tree instead of I2C
as it will cause some board updates. This has been discussed
at on the I2C list:

http://lists.lm-sensors.org/pipermail/i2c/2008-March/003031.html

Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Cc: i2c@lm-sensors.org
Signed-off-by: Tony Lindgren <tony@atomide.com>
drivers/i2c/chips/Kconfig
drivers/i2c/chips/tps65010.c
include/linux/i2c/tps65010.h

index b21593f93586adc191cf43178c06d1166bf0ed6b..2da2edfa68ec1ebf0954cfc48b9ea87fd3e50d26 100644 (file)
@@ -93,6 +93,7 @@ config ISP1301_OMAP
 
 config TPS65010
        tristate "TPS6501x Power Management chips"
+       depends on HAVE_GPIO_LIB
        default y if MACH_OMAP_H2 || MACH_OMAP_H3 || MACH_OMAP_OSK
        help
          If you say yes here you get support for the TPS6501x series of
index 4154a9108859845911c9ac68cdacf75eaac32b0f..b67f69c2e7f3850c9a80cf405dc78a3b29505d93 100644 (file)
 #include <linux/debugfs.h>
 #include <linux/seq_file.h>
 #include <linux/mutex.h>
+#include <linux/platform_device.h>
 
 #include <linux/i2c/tps65010.h>
 
+#include <asm/gpio.h>
+
+
 /*-------------------------------------------------------------------------*/
 
 #define        DRIVER_VERSION  "2 May 2005"
@@ -84,7 +88,9 @@ struct tps65010 {
        u8                      chgstatus, regstatus, chgconf;
        u8                      nmask1, nmask2;
 
-       /* not currently tracking GPIO state */
+       u8                      outmask;
+       struct gpio_chip        chip;
+       struct platform_device  *leds;
 };
 
 #define        POWER_POLL_DELAY        msecs_to_jiffies(5000)
@@ -447,6 +453,59 @@ static irqreturn_t tps65010_irq(int irq, void *_tps)
        return IRQ_HANDLED;
 }
 
+/*-------------------------------------------------------------------------*/
+
+/* offsets 0..3 == GPIO1..GPIO4
+ * offsets 4..5 == LED1/nPG, LED2 (we set one of the non-BLINK modes)
+ */
+static void
+tps65010_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+       if (offset < 4)
+               tps65010_set_gpio_out_value(offset + 1, value);
+       else
+               tps65010_set_led(offset - 3, value ? ON : OFF);
+}
+
+static int
+tps65010_output(struct gpio_chip *chip, unsigned offset, int value)
+{
+       /* GPIOs may be input-only */
+       if (offset < 4) {
+               struct tps65010         *tps;
+
+               tps = container_of(chip, struct tps65010, chip);
+               if (!(tps->outmask & (1 << offset)))
+                       return -EINVAL;
+               tps65010_set_gpio_out_value(offset + 1, value);
+       } else
+               tps65010_set_led(offset - 3, value ? ON : OFF);
+
+       return 0;
+}
+
+static int tps65010_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+       int                     value;
+       struct tps65010         *tps;
+
+       tps = container_of(chip, struct tps65010, chip);
+
+       if (offset < 4) {
+               value = i2c_smbus_read_byte_data(tps->client, TPS_DEFGPIO);
+               if (value < 0)
+                       return 0;
+               if (value & (1 << (offset + 4)))        /* output */
+                       return !(value & (1 << offset));
+               else                                    /* input */
+                       return (value & (1 << offset));
+       }
+
+       /* REVISIT we *could* report LED1/nPG and LED2 state ... */
+       return 0;
+}
+
+
 /*-------------------------------------------------------------------------*/
 
 static struct tps65010 *the_tps;
@@ -454,7 +513,14 @@ static struct tps65010 *the_tps;
 static int __exit tps65010_remove(struct i2c_client *client)
 {
        struct tps65010         *tps = i2c_get_clientdata(client);
+       struct tps65010_board   *board = client->dev.platform_data;
 
+       if (board && board->teardown) {
+               int status = board->teardown(client, board->context);
+               if (status < 0)
+                       dev_dbg(&client->dev, "board %s %s err %d\n",
+                               "teardown", client->name, status);
+       }
        if (client->irq > 0)
                free_irq(client->irq, tps);
        cancel_delayed_work(&tps->work);
@@ -469,6 +535,7 @@ static int tps65010_probe(struct i2c_client *client)
 {
        struct tps65010         *tps;
        int                     status;
+       struct tps65010_board   *board = client->dev.platform_data;
 
        if (the_tps) {
                dev_dbg(&client->dev, "only one tps6501x chip allowed\n");
@@ -577,6 +644,38 @@ static int tps65010_probe(struct i2c_client *client)
 
        tps->file = debugfs_create_file(DRIVER_NAME, S_IRUGO, NULL,
                                tps, DEBUG_FOPS);
+
+       /* optionally register GPIOs */
+       if (board && board->base > 0) {
+               tps->outmask = board->outmask;
+
+               tps->chip.label = client->name;
+
+               tps->chip.set = tps65010_gpio_set;
+               tps->chip.direction_output = tps65010_output;
+
+               /* NOTE:  only partial support for inputs; nyet IRQs */
+               tps->chip.get = tps65010_gpio_get;
+
+               tps->chip.base = board->base;
+               tps->chip.ngpio = 6;
+               tps->chip.can_sleep = 1;
+
+               status = gpiochip_add(&tps->chip);
+               if (status < 0)
+                       dev_err(&client->dev, "can't add gpiochip, err %d\n",
+                                       status);
+               else if (board->setup) {
+                       status = board->setup(client, board->context);
+                       if (status < 0) {
+                               dev_dbg(&client->dev,
+                                       "board %s %s err %d\n",
+                                       "setup", client->name, status);
+                               status = 0;
+                       }
+               }
+       }
+
        return 0;
 fail1:
        kfree(tps);
index 7021635ed6a00163b93e1a8b868eb9c5d3af24c3..918c5354d9b8967825c12882852803b0e6470785 100644 (file)
@@ -152,5 +152,35 @@ extern int tps65010_config_vregs1(unsigned value);
  */
 extern int tps65013_set_low_pwr(unsigned mode);
 
+
+struct i2c_client;
+
+/**
+ * struct tps65010_board - packages GPIO and LED lines
+ * @base: the GPIO number to assign to GPIO-1
+ * @outmask: bit (N-1) is set to allow GPIO-N to be used as an
+ *     (open drain) output
+ * @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()
+ *
+ * Board data may be used to package the GPIO (and LED) lines for use
+ * in by the generic GPIO and LED frameworks.  The first four GPIOs
+ * starting at gpio_base are GPIO1..GPIO4.  The next two are LED1/nPG
+ * and LED2 (with hardware blinking capability, not currently exposed).
+ *
+ * The @setup callback may be used with the kind of board-specific glue
+ * which hands the (now-valid) GPIOs to other drivers, or which puts
+ * devices in their initial states using these GPIOs.
+ */
+struct tps65010_board {
+       int                             base;
+       unsigned                        outmask;
+
+       int             (*setup)(struct i2c_client *client, void *context);
+       int             (*teardown)(struct i2c_client *client, void *context);
+       void            *context;
+};
+
 #endif /*  __LINUX_I2C_TPS65010_H */