From: David Brownell Date: Fri, 29 Feb 2008 06:07:28 +0000 (-0800) Subject: ARM: OMAP: I2C: tps65010 driver converts to gpiolib X-Git-Tag: v2.6.26-rc1~1131^2~1^2~28 X-Git-Url: https://err.no/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=79966fd9b4781f9bd257312489ff511f2c01f210;p=linux-2.6 ARM: OMAP: I2C: tps65010 driver converts to gpiolib 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 Cc: i2c@lm-sensors.org Signed-off-by: Tony Lindgren --- diff --git a/drivers/i2c/chips/Kconfig b/drivers/i2c/chips/Kconfig index b21593f935..2da2edfa68 100644 --- a/drivers/i2c/chips/Kconfig +++ b/drivers/i2c/chips/Kconfig @@ -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 diff --git a/drivers/i2c/chips/tps65010.c b/drivers/i2c/chips/tps65010.c index 4154a91088..b67f69c2e7 100644 --- a/drivers/i2c/chips/tps65010.c +++ b/drivers/i2c/chips/tps65010.c @@ -30,9 +30,13 @@ #include #include #include +#include #include +#include + + /*-------------------------------------------------------------------------*/ #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); diff --git a/include/linux/i2c/tps65010.h b/include/linux/i2c/tps65010.h index 7021635ed6..918c5354d9 100644 --- a/include/linux/i2c/tps65010.h +++ b/include/linux/i2c/tps65010.h @@ -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 */