aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/i2c/chips/Kconfig1
-rw-r--r--drivers/i2c/chips/tps65010.c101
-rw-r--r--include/linux/i2c/tps65010.h30
3 files changed, 131 insertions, 1 deletions
diff --git a/drivers/i2c/chips/Kconfig b/drivers/i2c/chips/Kconfig
index b21593f9358..2da2edfa68e 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 4154a910885..b67f69c2e7f 100644
--- a/drivers/i2c/chips/tps65010.c
+++ b/drivers/i2c/chips/tps65010.c
@@ -30,9 +30,13 @@
#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)
@@ -449,12 +455,72 @@ static irqreturn_t tps65010_irq(int irq, void *_tps)
/*-------------------------------------------------------------------------*/
+/* 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;
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 7021635ed6a..918c5354d9b 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 */