Merge branch 'buckets/mfd' into after-buckets
Dan Willemsen [Thu, 1 Dec 2011 05:45:12 +0000 (21:45 -0800)]
13 files changed:
drivers/mfd/Kconfig
drivers/mfd/Makefile
drivers/mfd/max8907c-irq.c [new file with mode: 0644]
drivers/mfd/max8907c.c [new file with mode: 0644]
drivers/mfd/tps6586x.c
drivers/mfd/tps6591x.c [new file with mode: 0644]
drivers/mfd/tps80031.c [new file with mode: 0644]
drivers/mfd/twl-core.c
include/linux/i2c/twl.h
include/linux/mfd/max8907c.h [new file with mode: 0644]
include/linux/mfd/tps6586x.h
include/linux/mfd/tps6591x.h [new file with mode: 0644]
include/linux/mfd/tps80031.h [new file with mode: 0644]

index 21574bd..cfeb061 100644 (file)
@@ -386,6 +386,16 @@ config MFD_MAX8998
          additional drivers must be enabled in order to use the functionality
          of the device.
 
+config MFD_MAX8907C
+       tristate "Maxim Semiconductor MAX8907C PMIC Support"
+       select MFD_CORE
+       depends on I2C
+       help
+         Say yes here to support for Maxim Semiconductor MAX8907C. This is
+         a Power Management IC. This driver provies common support for
+         accessing the device, additional drivers must be enabled in order
+         to use the functionality of the device.
+
 config MFD_WM8400
        tristate "Support Wolfson Microelectronics WM8400"
        select MFD_CORE
@@ -770,6 +780,28 @@ config MFD_AAT2870_CORE
          additional drivers must be enabled in order to use the
          functionality of the device.
 
+config MFD_TPS6591X
+       bool "TPS6591x Power Management chips"
+       depends on I2C && GPIOLIB && GENERIC_HARDIRQS
+       select MFD_CORE
+       help
+         If you say yes here you get support for the TPS6591X series of
+         Power Management chips.
+         This driver provides common support for accessing the device,
+         additional drivers must be enabled in order to use the
+         functionality of the device.
+
+config MFD_TPS80031
+       bool "TI TPS80031 Power Management chips"
+       depends on I2C && GPIOLIB && GENERIC_HARDIRQS
+       select MFD_CORE
+       help
+         If you say yes here you get support for the TPS80031 Power
+         Management chips.
+         This driver provides common support for accessing the device,
+         additional drivers must be enabled in order to use the
+         functionality of the device.
+
 endif # MFD_SUPPORT
 
 menu "Multimedia Capabilities Port drivers"
index c580203..185eb7a 100644 (file)
@@ -102,3 +102,7 @@ obj-$(CONFIG_MFD_PM8921_CORE)       += pm8921-core.o
 obj-$(CONFIG_MFD_PM8XXX_IRQ)   += pm8xxx-irq.o
 obj-$(CONFIG_TPS65911_COMPARATOR)      += tps65911-comparator.o
 obj-$(CONFIG_MFD_AAT2870_CORE) += aat2870-core.o
+obj-$(CONFIG_MFD_TPS6591X)     += tps6591x.o
+obj-$(CONFIG_MFD_TPS80031)     += tps80031.o
+obj-$(CONFIG_MFD_MAX8907C)     += max8907c.o
+obj-$(CONFIG_MFD_MAX8907C)     += max8907c-irq.o
diff --git a/drivers/mfd/max8907c-irq.c b/drivers/mfd/max8907c-irq.c
new file mode 100644 (file)
index 0000000..d49b186
--- /dev/null
@@ -0,0 +1,422 @@
+/*
+ * Battery driver for Maxim MAX8907C
+ *
+ * Copyright (c) 2011, NVIDIA Corporation.
+ * Based on driver/mfd/max8925-core.c, Copyright (C) 2009-2010 Marvell International Ltd.
+ *
+ * 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/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/max8907c.h>
+
+struct max8907c_irq_data {
+       int     reg;
+       int     mask_reg;
+       int     enable;         /* enable or not */
+       int     offs;           /* bit offset in mask register */
+       bool    is_rtc;
+       int     wake;
+};
+
+static struct max8907c_irq_data max8907c_irqs[] = {
+       [MAX8907C_IRQ_VCHG_DC_OVP] = {
+               .reg            = MAX8907C_REG_CHG_IRQ1,
+               .mask_reg       = MAX8907C_REG_CHG_IRQ1_MASK,
+               .offs           = 1 << 0,
+       },
+       [MAX8907C_IRQ_VCHG_DC_F] = {
+               .reg            = MAX8907C_REG_CHG_IRQ1,
+               .mask_reg       = MAX8907C_REG_CHG_IRQ1_MASK,
+               .offs           = 1 << 1,
+       },
+       [MAX8907C_IRQ_VCHG_DC_R] = {
+               .reg            = MAX8907C_REG_CHG_IRQ1,
+               .mask_reg       = MAX8907C_REG_CHG_IRQ1_MASK,
+               .offs           = 1 << 2,
+       },
+       [MAX8907C_IRQ_VCHG_THM_OK_R] = {
+               .reg            = MAX8907C_REG_CHG_IRQ2,
+               .mask_reg       = MAX8907C_REG_CHG_IRQ2_MASK,
+               .offs           = 1 << 0,
+       },
+       [MAX8907C_IRQ_VCHG_THM_OK_F] = {
+               .reg            = MAX8907C_REG_CHG_IRQ2,
+               .mask_reg       = MAX8907C_REG_CHG_IRQ2_MASK,
+               .offs           = 1 << 1,
+       },
+       [MAX8907C_IRQ_VCHG_MBATTLOW_F] = {
+               .reg            = MAX8907C_REG_CHG_IRQ2,
+               .mask_reg       = MAX8907C_REG_CHG_IRQ2_MASK,
+               .offs           = 1 << 2,
+       },
+       [MAX8907C_IRQ_VCHG_MBATTLOW_R] = {
+               .reg            = MAX8907C_REG_CHG_IRQ2,
+               .mask_reg       = MAX8907C_REG_CHG_IRQ2_MASK,
+               .offs           = 1 << 3,
+       },
+       [MAX8907C_IRQ_VCHG_RST] = {
+               .reg            = MAX8907C_REG_CHG_IRQ2,
+               .mask_reg       = MAX8907C_REG_CHG_IRQ2_MASK,
+               .offs           = 1 << 4,
+       },
+       [MAX8907C_IRQ_VCHG_DONE] = {
+               .reg            = MAX8907C_REG_CHG_IRQ2,
+               .mask_reg       = MAX8907C_REG_CHG_IRQ2_MASK,
+               .offs           = 1 << 5,
+       },
+       [MAX8907C_IRQ_VCHG_TOPOFF] = {
+               .reg            = MAX8907C_REG_CHG_IRQ2,
+               .mask_reg       = MAX8907C_REG_CHG_IRQ2_MASK,
+               .offs           = 1 << 6,
+       },
+       [MAX8907C_IRQ_VCHG_TMR_FAULT] = {
+               .reg            = MAX8907C_REG_CHG_IRQ2,
+               .mask_reg       = MAX8907C_REG_CHG_IRQ2_MASK,
+               .offs           = 1 << 7,
+       },
+       [MAX8907C_IRQ_GPM_RSTIN] = {
+               .reg            = MAX8907C_REG_ON_OFF_IRQ1,
+               .mask_reg       = MAX8907C_REG_ON_OFF_IRQ1_MASK,
+               .offs           = 1 << 0,
+       },
+       [MAX8907C_IRQ_GPM_MPL] = {
+               .reg            = MAX8907C_REG_ON_OFF_IRQ1,
+               .mask_reg       = MAX8907C_REG_ON_OFF_IRQ1_MASK,
+               .offs           = 1 << 1,
+       },
+       [MAX8907C_IRQ_GPM_SW_3SEC] = {
+               .reg            = MAX8907C_REG_ON_OFF_IRQ1,
+               .mask_reg       = MAX8907C_REG_ON_OFF_IRQ1_MASK,
+               .offs           = 1 << 2,
+       },
+       [MAX8907C_IRQ_GPM_EXTON_F] = {
+               .reg            = MAX8907C_REG_ON_OFF_IRQ1,
+               .mask_reg       = MAX8907C_REG_ON_OFF_IRQ1_MASK,
+               .offs           = 1 << 3,
+       },
+       [MAX8907C_IRQ_GPM_EXTON_R] = {
+               .reg            = MAX8907C_REG_ON_OFF_IRQ1,
+               .mask_reg       = MAX8907C_REG_ON_OFF_IRQ1_MASK,
+               .offs           = 1 << 4,
+       },
+       [MAX8907C_IRQ_GPM_SW_1SEC] = {
+               .reg            = MAX8907C_REG_ON_OFF_IRQ1,
+               .mask_reg       = MAX8907C_REG_ON_OFF_IRQ1_MASK,
+               .offs           = 1 << 5,
+       },
+       [MAX8907C_IRQ_GPM_SW_F] = {
+               .reg            = MAX8907C_REG_ON_OFF_IRQ1,
+               .mask_reg       = MAX8907C_REG_ON_OFF_IRQ1_MASK,
+               .offs           = 1 << 6,
+       },
+       [MAX8907C_IRQ_GPM_SW_R] = {
+               .reg            = MAX8907C_REG_ON_OFF_IRQ1,
+               .mask_reg       = MAX8907C_REG_ON_OFF_IRQ1_MASK,
+               .offs           = 1 << 7,
+       },
+       [MAX8907C_IRQ_GPM_SYSCKEN_F] = {
+               .reg            = MAX8907C_REG_ON_OFF_IRQ2,
+               .mask_reg       = MAX8907C_REG_ON_OFF_IRQ2_MASK,
+               .offs           = 1 << 0,
+       },
+       [MAX8907C_IRQ_GPM_SYSCKEN_R] = {
+               .reg            = MAX8907C_REG_ON_OFF_IRQ2,
+               .mask_reg       = MAX8907C_REG_ON_OFF_IRQ2_MASK,
+               .offs           = 1 << 1,
+       },
+       [MAX8907C_IRQ_RTC_ALARM1] = {
+               .reg            = MAX8907C_REG_RTC_IRQ,
+               .mask_reg       = MAX8907C_REG_RTC_IRQ_MASK,
+               .offs           = 1 << 2,
+               .is_rtc         = true,
+       },
+       [MAX8907C_IRQ_RTC_ALARM0] = {
+               .reg            = MAX8907C_REG_RTC_IRQ,
+               .mask_reg       = MAX8907C_REG_RTC_IRQ_MASK,
+               .offs           = 1 << 3,
+               .is_rtc         = true,
+       },
+};
+
+static inline struct max8907c_irq_data *irq_to_max8907c(struct max8907c *chip,
+                                                     int irq)
+{
+       return &max8907c_irqs[irq - chip->irq_base];
+}
+
+static irqreturn_t max8907c_irq(int irq, void *data)
+{
+       struct max8907c *chip = data;
+       struct max8907c_irq_data *irq_data;
+       struct i2c_client *i2c;
+       int read_reg = -1, value = 0;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(max8907c_irqs); i++) {
+               irq_data = &max8907c_irqs[i];
+
+               if (irq_data->is_rtc)
+                       i2c = chip->i2c_rtc;
+               else
+                       i2c = chip->i2c_power;
+
+               if (read_reg != irq_data->reg) {
+                       read_reg = irq_data->reg;
+                       value = max8907c_reg_read(i2c, irq_data->reg);
+               }
+
+               if (value & irq_data->enable)
+                       handle_nested_irq(chip->irq_base + i);
+       }
+       return IRQ_HANDLED;
+}
+
+static void max8907c_irq_lock(struct irq_data *data)
+{
+       struct max8907c *chip = irq_data_get_irq_chip_data(data);
+
+       mutex_lock(&chip->irq_lock);
+}
+
+static void max8907c_irq_sync_unlock(struct irq_data *data)
+{
+       struct max8907c *chip = irq_data_get_irq_chip_data(data);
+       struct max8907c_irq_data *irq_data;
+       unsigned char irq_chg[2], irq_on[2];
+       unsigned char irq_rtc;
+       int i;
+
+       irq_chg[0] = irq_chg[1] = irq_on[0] = irq_on[1] = irq_rtc = 0xFF;
+
+       for (i = 0; i < ARRAY_SIZE(max8907c_irqs); i++) {
+               irq_data = &max8907c_irqs[i];
+               /* 1 -- disable, 0 -- enable */
+               switch (irq_data->mask_reg) {
+               case MAX8907C_REG_CHG_IRQ1_MASK:
+                       irq_chg[0] &= ~irq_data->enable;
+                       break;
+               case MAX8907C_REG_CHG_IRQ2_MASK:
+                       irq_chg[1] &= ~irq_data->enable;
+                       break;
+               case MAX8907C_REG_ON_OFF_IRQ1_MASK:
+                       irq_on[0] &= ~irq_data->enable;
+                       break;
+               case MAX8907C_REG_ON_OFF_IRQ2_MASK:
+                       irq_on[1] &= ~irq_data->enable;
+                       break;
+               case MAX8907C_REG_RTC_IRQ_MASK:
+                       irq_rtc &= ~irq_data->enable;
+                       break;
+               default:
+                       dev_err(chip->dev, "wrong IRQ\n");
+                       break;
+               }
+       }
+       /* update mask into registers */
+       if (chip->cache_chg[0] != irq_chg[0]) {
+               chip->cache_chg[0] = irq_chg[0];
+               max8907c_reg_write(chip->i2c_power, MAX8907C_REG_CHG_IRQ1_MASK,
+                       irq_chg[0]);
+       }
+       if (chip->cache_chg[1] != irq_chg[1]) {
+               chip->cache_chg[1] = irq_chg[1];
+               max8907c_reg_write(chip->i2c_power, MAX8907C_REG_CHG_IRQ2_MASK,
+                       irq_chg[1]);
+       }
+       if (chip->cache_on[0] != irq_on[0]) {
+               chip->cache_on[0] = irq_on[0];
+               max8907c_reg_write(chip->i2c_power, MAX8907C_REG_ON_OFF_IRQ1_MASK,
+                               irq_on[0]);
+       }
+       if (chip->cache_on[1] != irq_on[1]) {
+               chip->cache_on[1] = irq_on[1];
+               max8907c_reg_write(chip->i2c_power, MAX8907C_REG_ON_OFF_IRQ2_MASK,
+                               irq_on[1]);
+       }
+       if (chip->cache_rtc != irq_rtc) {
+               chip->cache_rtc = irq_rtc;
+               max8907c_reg_write(chip->i2c_rtc, MAX8907C_REG_RTC_IRQ_MASK,
+                                  irq_rtc);
+       }
+
+       mutex_unlock(&chip->irq_lock);
+}
+
+static void max8907c_irq_enable(struct irq_data *data)
+{
+       struct max8907c *chip = irq_data_get_irq_chip_data(data);
+       max8907c_irqs[data->irq - chip->irq_base].enable
+               = max8907c_irqs[data->irq - chip->irq_base].offs;
+}
+
+static void max8907c_irq_disable(struct irq_data *data)
+{
+       struct max8907c *chip = irq_data_get_irq_chip_data(data);
+       max8907c_irqs[data->irq - chip->irq_base].enable = 0;
+}
+
+static int max8907c_irq_set_wake(struct irq_data *data, unsigned int on)
+{
+       struct max8907c *chip = irq_data_get_irq_chip_data(data);
+       if (on) {
+               max8907c_irqs[data->irq - chip->irq_base].wake
+                       = max8907c_irqs[data->irq - chip->irq_base].enable;
+       } else {
+               max8907c_irqs[data->irq - chip->irq_base].wake = 0;
+       }
+       return 0;
+}
+
+static struct irq_chip max8907c_irq_chip = {
+       .name                   = "max8907c",
+       .irq_bus_lock           = max8907c_irq_lock,
+       .irq_bus_sync_unlock    = max8907c_irq_sync_unlock,
+       .irq_enable             = max8907c_irq_enable,
+       .irq_disable            = max8907c_irq_disable,
+       .irq_set_wake           = max8907c_irq_set_wake,
+};
+
+int max8907c_irq_init(struct max8907c *chip, int irq, int irq_base)
+{
+       unsigned long flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT;
+       struct irq_desc *desc;
+       int i, ret;
+       int __irq;
+
+       if (!irq_base || !irq) {
+               dev_warn(chip->dev, "No interrupt support\n");
+               return -EINVAL;
+       }
+       /* clear all interrupts */
+       max8907c_reg_read(chip->i2c_power, MAX8907C_REG_CHG_IRQ1);
+       max8907c_reg_read(chip->i2c_power, MAX8907C_REG_CHG_IRQ2);
+       max8907c_reg_read(chip->i2c_power, MAX8907C_REG_ON_OFF_IRQ1);
+       max8907c_reg_read(chip->i2c_power, MAX8907C_REG_ON_OFF_IRQ2);
+       max8907c_reg_read(chip->i2c_rtc, MAX8907C_REG_RTC_IRQ);
+       /* mask all interrupts */
+       max8907c_reg_write(chip->i2c_rtc, MAX8907C_REG_ALARM0_CNTL, 0);
+       max8907c_reg_write(chip->i2c_rtc, MAX8907C_REG_ALARM1_CNTL, 0);
+       max8907c_reg_write(chip->i2c_power, MAX8907C_REG_CHG_IRQ1_MASK, 0xff);
+       max8907c_reg_write(chip->i2c_power, MAX8907C_REG_CHG_IRQ2_MASK, 0xff);
+       max8907c_reg_write(chip->i2c_power, MAX8907C_REG_ON_OFF_IRQ1_MASK, 0xff);
+       max8907c_reg_write(chip->i2c_power, MAX8907C_REG_ON_OFF_IRQ2_MASK, 0xff);
+       max8907c_reg_write(chip->i2c_rtc, MAX8907C_REG_RTC_IRQ_MASK, 0xff);
+
+       chip->cache_chg[0] = chip->cache_chg[1] =
+               chip->cache_on[0] = chip->cache_on[1] =
+               chip->cache_rtc = 0xFF;
+
+       mutex_init(&chip->irq_lock);
+       chip->core_irq = irq;
+       chip->irq_base = irq_base;
+       desc = irq_to_desc(chip->core_irq);
+
+       /* register with genirq */
+       for (i = 0; i < ARRAY_SIZE(max8907c_irqs); i++) {
+               __irq = i + chip->irq_base;
+               irq_set_chip_data(__irq, chip);
+               irq_set_chip_and_handler(__irq, &max8907c_irq_chip,
+                                        handle_edge_irq);
+               irq_set_nested_thread(__irq, 1);
+#ifdef CONFIG_ARM
+               irq_set_status_flags(__irq, IRQF_VALID);
+#else
+               irq_set_noprobe(__irq);
+#endif
+       }
+
+       ret = request_threaded_irq(irq, NULL, max8907c_irq, flags,
+                                  "max8907c", chip);
+       if (ret) {
+               dev_err(chip->dev, "Failed to request core IRQ: %d\n", ret);
+               chip->core_irq = 0;
+       }
+
+       device_init_wakeup(chip->dev, 1);
+
+       return ret;
+}
+
+int max8907c_suspend(struct i2c_client *i2c, pm_message_t state)
+{
+       struct max8907c *chip = i2c_get_clientdata(i2c);
+
+       struct max8907c_irq_data *irq_data;
+       unsigned char irq_chg[2], irq_on[2];
+       unsigned char irq_rtc;
+       int i;
+
+       irq_chg[0] = irq_chg[1] = irq_on[0] = irq_on[1] = irq_rtc = 0xFF;
+
+       for (i = 0; i < ARRAY_SIZE(max8907c_irqs); i++) {
+               irq_data = &max8907c_irqs[i];
+               /* 1 -- disable, 0 -- enable */
+               switch (irq_data->mask_reg) {
+               case MAX8907C_REG_CHG_IRQ1_MASK:
+                       irq_chg[0] &= ~irq_data->wake;
+                       break;
+               case MAX8907C_REG_CHG_IRQ2_MASK:
+                       irq_chg[1] &= ~irq_data->wake;
+                       break;
+               case MAX8907C_REG_ON_OFF_IRQ1_MASK:
+                       irq_on[0] &= ~irq_data->wake;
+                       break;
+               case MAX8907C_REG_ON_OFF_IRQ2_MASK:
+                       irq_on[1] &= ~irq_data->wake;
+                       break;
+               case MAX8907C_REG_RTC_IRQ_MASK:
+                       irq_rtc &= ~irq_data->wake;
+                       break;
+               default:
+                       dev_err(chip->dev, "wrong IRQ\n");
+                       break;
+               }
+       }
+
+       max8907c_reg_write(chip->i2c_power, MAX8907C_REG_CHG_IRQ1_MASK, irq_chg[0]);
+       max8907c_reg_write(chip->i2c_power, MAX8907C_REG_CHG_IRQ2_MASK, irq_chg[1]);
+       max8907c_reg_write(chip->i2c_power, MAX8907C_REG_ON_OFF_IRQ1_MASK, irq_on[0]);
+       max8907c_reg_write(chip->i2c_power, MAX8907C_REG_ON_OFF_IRQ2_MASK, irq_on[1]);
+       max8907c_reg_write(chip->i2c_rtc, MAX8907C_REG_RTC_IRQ_MASK, irq_rtc);
+
+       if (device_may_wakeup(chip->dev))
+               enable_irq_wake(chip->core_irq);
+       else
+               disable_irq(chip->core_irq);
+
+       return 0;
+}
+
+int max8907c_resume(struct i2c_client *i2c)
+{
+       struct max8907c *chip = i2c_get_clientdata(i2c);
+
+       if (device_may_wakeup(chip->dev))
+               disable_irq_wake(chip->core_irq);
+       else
+               enable_irq(chip->core_irq);
+
+       max8907c_reg_write(chip->i2c_power, MAX8907C_REG_CHG_IRQ1_MASK, chip->cache_chg[0]);
+       max8907c_reg_write(chip->i2c_power, MAX8907C_REG_CHG_IRQ2_MASK, chip->cache_chg[1]);
+       max8907c_reg_write(chip->i2c_power, MAX8907C_REG_ON_OFF_IRQ1_MASK, chip->cache_on[0]);
+       max8907c_reg_write(chip->i2c_power, MAX8907C_REG_ON_OFF_IRQ2_MASK, chip->cache_on[1]);
+       max8907c_reg_write(chip->i2c_rtc, MAX8907C_REG_RTC_IRQ_MASK, chip->cache_rtc);
+
+       return 0;
+}
+
+void max8907c_irq_free(struct max8907c *chip)
+{
+       if (chip->core_irq)
+               free_irq(chip->core_irq, chip);
+}
+
diff --git a/drivers/mfd/max8907c.c b/drivers/mfd/max8907c.c
new file mode 100644 (file)
index 0000000..7787c8a
--- /dev/null
@@ -0,0 +1,371 @@
+/*
+ * max8907c.c - mfd driver for MAX8907c
+ *
+ * Copyright (C) 2010 Gyungoh Yoo <jack.yoo@maxim-ic.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.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/max8907c.h>
+
+static struct mfd_cell cells[] = {
+       {.name = "max8907-regulator",},
+       {.name = "max8907c-rtc",},
+};
+
+static int max8907c_i2c_read(struct i2c_client *i2c, u8 reg, u8 count, u8 *dest)
+{
+       struct i2c_msg xfer[2];
+       int ret = 0;
+
+       xfer[0].addr = i2c->addr;
+       xfer[0].flags = I2C_M_NOSTART;
+       xfer[0].len = 1;
+       xfer[0].buf = &reg;
+
+       xfer[1].addr = i2c->addr;
+       xfer[1].flags = I2C_M_RD;
+       xfer[1].len = count;
+       xfer[1].buf = dest;
+
+       ret = i2c_transfer(i2c->adapter, xfer, 2);
+       if (ret < 0)
+               return ret;
+       if (ret != 2)
+               return -EIO;
+
+       return 0;
+}
+
+static int max8907c_i2c_write(struct i2c_client *i2c, u8 reg, u8 count, const u8 *src)
+{
+       u8 msg[0x100 + 1];
+       int ret = 0;
+
+       msg[0] = reg;
+       memcpy(&msg[1], src, count);
+
+       ret = i2c_master_send(i2c, msg, count + 1);
+       if (ret < 0)
+               return ret;
+       if (ret != count + 1)
+               return -EIO;
+
+       return 0;
+}
+
+int max8907c_reg_read(struct i2c_client *i2c, u8 reg)
+{
+       int ret;
+       u8 val;
+
+       ret = max8907c_i2c_read(i2c, reg, 1, &val);
+
+       pr_debug("max8907c: reg read  reg=%x, val=%x\n",
+                (unsigned int)reg, (unsigned int)val);
+
+       if (ret != 0)
+               pr_err("Failed to read max8907c I2C driver: %d\n", ret);
+       return val;
+}
+EXPORT_SYMBOL_GPL(max8907c_reg_read);
+
+int max8907c_reg_bulk_read(struct i2c_client *i2c, u8 reg, u8 count, u8 *val)
+{
+       int ret;
+
+       ret = max8907c_i2c_read(i2c, reg, count, val);
+
+       pr_debug("max8907c: reg read  reg=%x, val=%x\n",
+                (unsigned int)reg, (unsigned int)*val);
+
+       if (ret != 0)
+               pr_err("Failed to read max8907c I2C driver: %d\n", ret);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(max8907c_reg_bulk_read);
+
+int max8907c_reg_write(struct i2c_client *i2c, u8 reg, u8 val)
+{
+       struct max8907c *max8907c = i2c_get_clientdata(i2c);
+       int ret;
+
+       pr_debug("max8907c: reg write  reg=%x, val=%x\n",
+                (unsigned int)reg, (unsigned int)val);
+
+       mutex_lock(&max8907c->io_lock);
+       ret = max8907c_i2c_write(i2c, reg, 1, &val);
+       mutex_unlock(&max8907c->io_lock);
+
+       if (ret != 0)
+               pr_err("Failed to write max8907c I2C driver: %d\n", ret);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(max8907c_reg_write);
+
+int max8907c_reg_bulk_write(struct i2c_client *i2c, u8 reg, u8 count, u8 *val)
+{
+       struct max8907c *max8907c = i2c_get_clientdata(i2c);
+       int ret;
+
+       pr_debug("max8907c: reg write  reg=%x, val=%x\n",
+                (unsigned int)reg, (unsigned int)*val);
+
+       mutex_lock(&max8907c->io_lock);
+       ret = max8907c_i2c_write(i2c, reg, count, val);
+       mutex_unlock(&max8907c->io_lock);
+
+       if (ret != 0)
+               pr_err("Failed to write max8907c I2C driver: %d\n", ret);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(max8907c_reg_bulk_write);
+
+int max8907c_set_bits(struct i2c_client *i2c, u8 reg, u8 mask, u8 val)
+{
+       struct max8907c *max8907c = i2c_get_clientdata(i2c);
+       u8 tmp;
+       int ret;
+
+       pr_debug("max8907c: reg write  reg=%02X, val=%02X, mask=%02X\n",
+                (unsigned int)reg, (unsigned int)val, (unsigned int)mask);
+
+       mutex_lock(&max8907c->io_lock);
+       ret = max8907c_i2c_read(i2c, reg, 1, &tmp);
+       if (ret == 0) {
+               val = (tmp & ~mask) | (val & mask);
+               ret = max8907c_i2c_write(i2c, reg, 1, &val);
+       }
+       mutex_unlock(&max8907c->io_lock);
+
+       if (ret != 0)
+               pr_err("Failed to write max8907c I2C driver: %d\n", ret);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(max8907c_set_bits);
+
+static struct i2c_client *max8907c_client = NULL;
+int max8907c_power_off(void)
+{
+       if (!max8907c_client)
+               return -EINVAL;
+
+       return max8907c_set_bits(max8907c_client, MAX8907C_REG_RESET_CNFG,
+                                               MAX8907C_MASK_POWER_OFF, 0x40);
+}
+
+void max8907c_deep_sleep(int enter)
+{
+       if (!max8907c_client)
+               return;
+
+       if (enter) {
+               max8907c_reg_write(max8907c_client, MAX8907C_REG_SDSEQCNT1,
+                                               MAX8907C_POWER_UP_DELAY_CNT12);
+               max8907c_reg_write(max8907c_client, MAX8907C_REG_SDSEQCNT2,
+                                                       MAX8907C_DELAY_CNT0);
+               max8907c_reg_write(max8907c_client, MAX8907C_REG_SDCTL2,
+                                                       MAX8907C_SD_SEQ2);
+       } else {
+               max8907c_reg_write(max8907c_client, MAX8907C_REG_SDSEQCNT1,
+                                                       MAX8907C_DELAY_CNT0);
+               max8907c_reg_write(max8907c_client, MAX8907C_REG_SDCTL2,
+                                                       MAX8907C_SD_SEQ1);
+               max8907c_reg_write(max8907c_client, MAX8907C_REG_SDSEQCNT2,
+                               MAX8907C_POWER_UP_DELAY_CNT1 | MAX8907C_POWER_DOWN_DELAY_CNT12);
+       }
+}
+
+static int max8907c_remove_subdev(struct device *dev, void *unused)
+{
+       platform_device_unregister(to_platform_device(dev));
+       return 0;
+}
+
+static int max8907c_remove_subdevs(struct max8907c *max8907c)
+{
+       return device_for_each_child(max8907c->dev, NULL,
+                                    max8907c_remove_subdev);
+}
+
+static int max8097c_add_subdevs(struct max8907c *max8907c,
+                               struct max8907c_platform_data *pdata)
+{
+       struct platform_device *pdev;
+       int ret;
+       int i;
+
+       for (i = 0; i < pdata->num_subdevs; i++) {
+               pdev = platform_device_alloc(pdata->subdevs[i]->name,
+                                            pdata->subdevs[i]->id);
+
+               pdev->dev.parent = max8907c->dev;
+               pdev->dev.platform_data = pdata->subdevs[i]->dev.platform_data;
+
+               ret = platform_device_add(pdev);
+               if (ret)
+                       goto error;
+       }
+       return 0;
+
+error:
+       max8907c_remove_subdevs(max8907c);
+       return ret;
+}
+
+int max8907c_pwr_en_config(void)
+{
+       int ret;
+       u8 data;
+
+       if (!max8907c_client)
+               return -EINVAL;
+
+       /*
+        * Enable/disable PWREN h/w control mechanism (PWREN signal must be
+        * inactive = high at this time)
+        */
+       ret = max8907c_set_bits(max8907c_client, MAX8907C_REG_RESET_CNFG,
+                                       MAX8907C_MASK_PWR_EN, MAX8907C_PWR_EN);
+       if (ret != 0)
+               return ret;
+
+       /*
+        * When enabled, connect PWREN to SEQ2 by clearing SEQ2 configuration
+        * settings for silicon revision that requires s/w WAR. On other
+        * MAX8907B revisions PWREN is always connected to SEQ2.
+        */
+       data = max8907c_reg_read(max8907c_client, MAX8907C_REG_II2RR);
+
+       if (data == MAX8907B_II2RR_PWREN_WAR) {
+               data = 0x00;
+               ret = max8907c_reg_write(max8907c_client, MAX8907C_REG_SEQ2CNFG, data);
+       }
+       return ret;
+}
+
+int max8907c_pwr_en_attach(void)
+{
+       int ret;
+
+       if (!max8907c_client)
+               return -EINVAL;
+
+       /* No sequencer delay for CPU rail when it is attached */
+       ret = max8907c_reg_write(max8907c_client, MAX8907C_REG_SDSEQCNT1,
+                                                       MAX8907C_DELAY_CNT0);
+       if (ret != 0)
+               return ret;
+
+       return max8907c_set_bits(max8907c_client, MAX8907C_REG_SDCTL1,
+                                       MAX8907C_MASK_CTL_SEQ, MAX8907C_CTL_SEQ);
+}
+
+static int max8907c_i2c_probe(struct i2c_client *i2c,
+                             const struct i2c_device_id *id)
+{
+       struct max8907c *max8907c;
+       struct max8907c_platform_data *pdata = i2c->dev.platform_data;
+       int ret;
+       int i;
+
+       max8907c = kzalloc(sizeof(struct max8907c), GFP_KERNEL);
+       if (max8907c == NULL)
+               return -ENOMEM;
+
+       max8907c->dev = &i2c->dev;
+       dev_set_drvdata(max8907c->dev, max8907c);
+
+       max8907c->i2c_power = i2c;
+       i2c_set_clientdata(i2c, max8907c);
+
+       max8907c->i2c_rtc = i2c_new_dummy(i2c->adapter, RTC_I2C_ADDR);
+       i2c_set_clientdata(max8907c->i2c_rtc, max8907c);
+
+       mutex_init(&max8907c->io_lock);
+
+       for (i = 0; i < ARRAY_SIZE(cells); i++)
+               cells[i].mfd_data = max8907c;
+       ret = mfd_add_devices(max8907c->dev, -1, cells, ARRAY_SIZE(cells),
+                             NULL, 0);
+       if (ret != 0) {
+               i2c_unregister_device(max8907c->i2c_rtc);
+               kfree(max8907c);
+               pr_debug("max8907c: failed to add MFD devices   %X\n", ret);
+               return ret;
+       }
+
+       max8907c_client = i2c;
+
+       max8907c_irq_init(max8907c, i2c->irq, pdata->irq_base);
+
+       ret = max8097c_add_subdevs(max8907c, pdata);
+
+       if (pdata->max8907c_setup)
+               return pdata->max8907c_setup();
+
+       return ret;
+}
+
+static int max8907c_i2c_remove(struct i2c_client *i2c)
+{
+       struct max8907c *max8907c = i2c_get_clientdata(i2c);
+
+       max8907c_remove_subdevs(max8907c);
+       i2c_unregister_device(max8907c->i2c_rtc);
+       mfd_remove_devices(max8907c->dev);
+       max8907c_irq_free(max8907c);
+       kfree(max8907c);
+
+       return 0;
+}
+
+static const struct i2c_device_id max8907c_i2c_id[] = {
+       {"max8907c", 0},
+       {}
+};
+
+MODULE_DEVICE_TABLE(i2c, max8907c_i2c_id);
+
+static struct i2c_driver max8907c_i2c_driver = {
+       .driver = {
+                  .name = "max8907c",
+                  .owner = THIS_MODULE,
+                  },
+       .probe = max8907c_i2c_probe,
+       .remove = max8907c_i2c_remove,
+       .suspend = max8907c_suspend,
+       .resume = max8907c_resume,
+       .id_table = max8907c_i2c_id,
+};
+
+static int __init max8907c_i2c_init(void)
+{
+       int ret = -ENODEV;
+
+       ret = i2c_add_driver(&max8907c_i2c_driver);
+       if (ret != 0)
+               pr_err("Failed to register I2C driver: %d\n", ret);
+
+       return ret;
+}
+
+subsys_initcall(max8907c_i2c_init);
+
+static void __exit max8907c_i2c_exit(void)
+{
+       i2c_del_driver(&max8907c_i2c_driver);
+}
+
+module_exit(max8907c_i2c_exit);
+
+MODULE_DESCRIPTION("MAX8907C multi-function core driver");
+MODULE_AUTHOR("Gyungoh Yoo <jack.yoo@maxim-ic.com>");
+MODULE_LICENSE("GPL");
index bba26d9..6d842fa 100644 (file)
 #include <linux/mfd/core.h>
 #include <linux/mfd/tps6586x.h>
 
+#define TPS6586X_SUPPLYENE  0x14
+#define EXITSLREQ_BIT       BIT(1) /* Exit sleep mode request */
+#define SLEEP_MODE_BIT      BIT(3) /* Sleep mode */
+
 /* GPIO control registers */
 #define TPS6586X_GPIOSET1      0x5d
 #define TPS6586X_GPIOSET2      0x5e
@@ -251,6 +255,28 @@ out:
 }
 EXPORT_SYMBOL_GPL(tps6586x_update);
 
+static struct i2c_client *tps6586x_i2c_client = NULL;
+int tps6586x_power_off(void)
+{
+       struct device *dev = NULL;
+       int ret = -EINVAL;
+
+       if (!tps6586x_i2c_client)
+               return ret;
+
+       dev = &tps6586x_i2c_client->dev;
+
+       ret = tps6586x_clr_bits(dev, TPS6586X_SUPPLYENE, EXITSLREQ_BIT);
+       if (ret)
+               return ret;
+
+       ret = tps6586x_set_bits(dev, TPS6586X_SUPPLYENE, SLEEP_MODE_BIT);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
 static int tps6586x_gpio_get(struct gpio_chip *gc, unsigned offset)
 {
        struct tps6586x *tps6586x = container_of(gc, struct tps6586x, gpio);
@@ -274,13 +300,24 @@ static void tps6586x_gpio_set(struct gpio_chip *chip, unsigned offset,
                        value << offset, 1 << offset);
 }
 
+static int tps6586x_gpio_input(struct gpio_chip *gc, unsigned offset)
+{
+       /* FIXME: add handling of GPIOs as dedicated inputs */
+       return -ENOSYS;
+}
+
 static int tps6586x_gpio_output(struct gpio_chip *gc, unsigned offset,
                                int value)
 {
        struct tps6586x *tps6586x = container_of(gc, struct tps6586x, gpio);
        uint8_t val, mask;
+       int ret;
 
-       tps6586x_gpio_set(gc, offset, value);
+       val = value << offset;
+       mask = 0x1 << offset;
+       ret = tps6586x_update(tps6586x->dev, TPS6586X_GPIOSET2, val, mask);
+       if (ret)
+               return ret;
 
        val = 0x1 << (offset * 2);
        mask = 0x3 << (offset * 2);
@@ -300,7 +337,7 @@ static int tps6586x_gpio_init(struct tps6586x *tps6586x, int gpio_base)
        tps6586x->gpio.ngpio            = 4;
        tps6586x->gpio.can_sleep        = 1;
 
-       /* FIXME: add handling of GPIOs as dedicated inputs */
+       tps6586x->gpio.direction_input  = tps6586x_gpio_input;
        tps6586x->gpio.direction_output = tps6586x_gpio_output;
        tps6586x->gpio.set              = tps6586x_gpio_set;
        tps6586x->gpio.get              = tps6586x_gpio_get;
@@ -525,6 +562,8 @@ static int __devinit tps6586x_i2c_probe(struct i2c_client *client,
                goto err_add_devs;
        }
 
+       tps6586x_i2c_client = client;
+
        return 0;
 
 err_add_devs:
diff --git a/drivers/mfd/tps6591x.c b/drivers/mfd/tps6591x.c
new file mode 100644 (file)
index 0000000..156eacb
--- /dev/null
@@ -0,0 +1,915 @@
+/*
+ * driver/mfd/tps6591x.c
+ *
+ * Core driver for TI TPS6591x PMIC family
+ *
+ * Copyright (C) 2011 NVIDIA Corporation
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ */
+
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+
+#include <linux/mfd/core.h>
+#include <linux/mfd/tps6591x.h>
+
+/* device control registers */
+#define TPS6591X_DEVCTRL       0x3F
+#define DEVCTRL_PWR_OFF_SEQ    (1 << 7)
+#define DEVCTRL_DEV_ON         (1 << 2)
+#define DEVCTRL_DEV_SLP                (1 << 1)
+#define TPS6591X_DEVCTRL2      0x40
+
+/* device sleep on registers */
+#define TPS6591X_SLEEP_KEEP_ON 0x42
+#define SLEEP_KEEP_ON_THERM    (1 << 7)
+#define SLEEP_KEEP_ON_CLKOUT32K        (1 << 6)
+#define SLEEP_KEEP_ON_VRTC     (1 << 5)
+#define SLEEP_KEEP_ON_I2CHS    (1 << 4)
+
+/* interrupt status registers */
+#define TPS6591X_INT_STS       0x50
+#define TPS6591X_INT_STS2      0x52
+#define TPS6591X_INT_STS3      0x54
+
+/* interrupt mask registers */
+#define TPS6591X_INT_MSK       0x51
+#define TPS6591X_INT_MSK2      0x53
+#define TPS6591X_INT_MSK3      0x55
+
+/* GPIO register base address */
+#define TPS6591X_GPIO_BASE_ADDR        0x60
+
+/* silicon version number */
+#define TPS6591X_VERNUM                0x80
+
+#define TPS6591X_GPIO_SLEEP    7
+#define TPS6591X_GPIO_PDEN     3
+#define TPS6591X_GPIO_DIR      2
+
+enum irq_type {
+       EVENT,
+       GPIO,
+};
+
+struct tps6591x_irq_data {
+       u8              mask_reg;
+       u8              mask_pos;
+       enum irq_type   type;
+};
+
+#define TPS6591X_IRQ(_reg, _mask_pos, _type)   \
+       {                                       \
+               .mask_reg       = (_reg),       \
+               .mask_pos       = (_mask_pos),  \
+               .type           = (_type),      \
+       }
+
+static const struct tps6591x_irq_data tps6591x_irqs[] = {
+       [TPS6591X_INT_PWRHOLD_F]        = TPS6591X_IRQ(0, 0, EVENT),
+       [TPS6591X_INT_VMBHI]            = TPS6591X_IRQ(0, 1, EVENT),
+       [TPS6591X_INT_PWRON]            = TPS6591X_IRQ(0, 2, EVENT),
+       [TPS6591X_INT_PWRON_LP]         = TPS6591X_IRQ(0, 3, EVENT),
+       [TPS6591X_INT_PWRHOLD_R]        = TPS6591X_IRQ(0, 4, EVENT),
+       [TPS6591X_INT_HOTDIE]           = TPS6591X_IRQ(0, 5, EVENT),
+       [TPS6591X_INT_RTC_ALARM]        = TPS6591X_IRQ(0, 6, EVENT),
+       [TPS6591X_INT_RTC_PERIOD]       = TPS6591X_IRQ(0, 7, EVENT),
+       [TPS6591X_INT_GPIO0]            = TPS6591X_IRQ(1, 0, GPIO),
+       [TPS6591X_INT_GPIO1]            = TPS6591X_IRQ(1, 2, GPIO),
+       [TPS6591X_INT_GPIO2]            = TPS6591X_IRQ(1, 4, GPIO),
+       [TPS6591X_INT_GPIO3]            = TPS6591X_IRQ(1, 6, GPIO),
+       [TPS6591X_INT_GPIO4]            = TPS6591X_IRQ(2, 0, GPIO),
+       [TPS6591X_INT_GPIO5]            = TPS6591X_IRQ(2, 2, GPIO),
+       [TPS6591X_INT_WTCHDG]           = TPS6591X_IRQ(2, 4, EVENT),
+       [TPS6591X_INT_VMBCH2_H]         = TPS6591X_IRQ(2, 5, EVENT),
+       [TPS6591X_INT_VMBCH2_L]         = TPS6591X_IRQ(2, 6, EVENT),
+       [TPS6591X_INT_PWRDN]            = TPS6591X_IRQ(2, 7, EVENT),
+};
+
+struct tps6591x {
+       struct mutex            lock;
+       struct device           *dev;
+       struct i2c_client       *client;
+
+       struct gpio_chip        gpio;
+       struct irq_chip         irq_chip;
+       struct mutex            irq_lock;
+       int                     irq_base;
+       u32                     irq_en;
+       u8                      mask_cache[3];
+       u8                      mask_reg[3];
+};
+
+static inline int __tps6591x_read(struct i2c_client *client,
+                                 int reg, uint8_t *val)
+{
+       int ret;
+
+       ret = i2c_smbus_read_byte_data(client, reg);
+       if (ret < 0) {
+               dev_err(&client->dev, "failed reading at 0x%02x\n", reg);
+               return ret;
+       }
+
+       *val = (uint8_t)ret;
+
+       return 0;
+}
+
+static inline int __tps6591x_reads(struct i2c_client *client, int reg,
+                               int len, uint8_t *val)
+{
+       int ret;
+
+       ret = i2c_smbus_read_i2c_block_data(client, reg, len, val);
+       if (ret < 0) {
+               dev_err(&client->dev, "failed reading from 0x%02x\n", reg);
+               return ret;
+       }
+
+       return 0;
+}
+
+static inline int __tps6591x_write(struct i2c_client *client,
+                                int reg, uint8_t val)
+{
+       int ret;
+       ret = i2c_smbus_write_byte_data(client, reg, val);
+       if (ret < 0) {
+               dev_err(&client->dev, "failed writing 0x%02x to 0x%02x\n",
+                               val, reg);
+               return ret;
+       }
+
+       return 0;
+}
+
+static inline int __tps6591x_writes(struct i2c_client *client, int reg,
+                                 int len, uint8_t *val)
+{
+       int ret;
+
+       ret = i2c_smbus_write_i2c_block_data(client, reg, len, val);
+       if (ret < 0) {
+               dev_err(&client->dev, "failed writings to 0x%02x\n", reg);
+               return ret;
+       }
+
+       return 0;
+}
+
+int tps6591x_write(struct device *dev, int reg, uint8_t val)
+{
+       struct tps6591x *tps6591x = dev_get_drvdata(dev);
+       int ret = 0;
+
+       mutex_lock(&tps6591x->lock);
+       ret = __tps6591x_write(to_i2c_client(dev), reg, val);
+       mutex_unlock(&tps6591x->lock);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(tps6591x_write);
+
+int tps6591x_writes(struct device *dev, int reg, int len, uint8_t *val)
+{
+       struct tps6591x *tps6591x = dev_get_drvdata(dev);
+       int ret = 0;
+
+       mutex_lock(&tps6591x->lock);
+       ret = __tps6591x_writes(to_i2c_client(dev), reg, len, val);
+       mutex_unlock(&tps6591x->lock);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(tps6591x_writes);
+
+int tps6591x_read(struct device *dev, int reg, uint8_t *val)
+{
+       return __tps6591x_read(to_i2c_client(dev), reg, val);
+}
+EXPORT_SYMBOL_GPL(tps6591x_read);
+
+int tps6591x_reads(struct device *dev, int reg, int len, uint8_t *val)
+{
+       return __tps6591x_reads(to_i2c_client(dev), reg, len, val);
+}
+EXPORT_SYMBOL_GPL(tps6591x_reads);
+
+int tps6591x_set_bits(struct device *dev, int reg, uint8_t bit_mask)
+{
+       struct tps6591x *tps6591x = dev_get_drvdata(dev);
+       uint8_t reg_val;
+       int ret = 0;
+
+       mutex_lock(&tps6591x->lock);
+
+       ret = __tps6591x_read(to_i2c_client(dev), reg, &reg_val);
+       if (ret)
+               goto out;
+
+       if ((reg_val & bit_mask) != bit_mask) {
+               reg_val |= bit_mask;
+               ret = __tps6591x_write(to_i2c_client(dev), reg, reg_val);
+       }
+out:
+       mutex_unlock(&tps6591x->lock);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(tps6591x_set_bits);
+
+int tps6591x_clr_bits(struct device *dev, int reg, uint8_t bit_mask)
+{
+       struct tps6591x *tps6591x = dev_get_drvdata(dev);
+       uint8_t reg_val;
+       int ret = 0;
+
+       mutex_lock(&tps6591x->lock);
+
+       ret = __tps6591x_read(to_i2c_client(dev), reg, &reg_val);
+       if (ret)
+               goto out;
+
+       if (reg_val & bit_mask) {
+               reg_val &= ~bit_mask;
+               ret = __tps6591x_write(to_i2c_client(dev), reg, reg_val);
+       }
+out:
+       mutex_unlock(&tps6591x->lock);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(tps6591x_clr_bits);
+
+int tps6591x_update(struct device *dev, int reg, uint8_t val, uint8_t mask)
+{
+       struct tps6591x *tps6591x = dev_get_drvdata(dev);
+       uint8_t reg_val;
+       int ret = 0;
+
+       mutex_lock(&tps6591x->lock);
+
+       ret = __tps6591x_read(tps6591x->client, reg, &reg_val);
+       if (ret)
+               goto out;
+
+       if ((reg_val & mask) != val) {
+               reg_val = (reg_val & ~mask) | (val & mask);
+               ret = __tps6591x_write(tps6591x->client, reg, reg_val);
+       }
+out:
+       mutex_unlock(&tps6591x->lock);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(tps6591x_update);
+
+static struct i2c_client *tps6591x_i2c_client;
+int tps6591x_power_off(void)
+{
+       struct device *dev = NULL;
+       int ret;
+
+       if (!tps6591x_i2c_client)
+               return -EINVAL;
+
+       dev = &tps6591x_i2c_client->dev;
+
+       ret = tps6591x_set_bits(dev, TPS6591X_DEVCTRL, DEVCTRL_PWR_OFF_SEQ);
+       if (ret < 0)
+               return ret;
+
+       ret = tps6591x_clr_bits(dev, TPS6591X_DEVCTRL, DEVCTRL_DEV_ON);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+
+static int tps6591x_gpio_get(struct gpio_chip *gc, unsigned offset)
+{
+       struct tps6591x *tps6591x = container_of(gc, struct tps6591x, gpio);
+       uint8_t val;
+       int ret;
+
+       ret = __tps6591x_read(tps6591x->client, TPS6591X_GPIO_BASE_ADDR +
+                       offset, &val);
+       if (ret)
+               return ret;
+
+       if (val & 0x4)
+               return val & 0x1;
+       else
+               return (val & 0x2) ? 1 : 0;
+}
+
+static void tps6591x_gpio_set(struct gpio_chip *chip, unsigned offset,
+                       int value)
+{
+
+       struct tps6591x *tps6591x = container_of(chip, struct tps6591x, gpio);
+
+       tps6591x_update(tps6591x->dev, TPS6591X_GPIO_BASE_ADDR + offset,
+                       value, 0x1);
+}
+
+static int tps6591x_gpio_input(struct gpio_chip *gc, unsigned offset)
+{
+       struct tps6591x *tps6591x = container_of(gc, struct tps6591x, gpio);
+       uint8_t reg_val;
+       int ret;
+
+       ret = __tps6591x_read(tps6591x->client, TPS6591X_GPIO_BASE_ADDR +
+                       offset, &reg_val);
+       if (ret)
+               return ret;
+
+       reg_val &= ~0x4;
+       return __tps6591x_write(tps6591x->client, TPS6591X_GPIO_BASE_ADDR +
+                       offset, reg_val);
+}
+
+static int tps6591x_gpio_output(struct gpio_chip *gc, unsigned offset,
+                               int value)
+{
+       struct tps6591x *tps6591x = container_of(gc, struct tps6591x, gpio);
+       uint8_t reg_val, val;
+       int ret;
+
+       ret = __tps6591x_read(tps6591x->client, TPS6591X_GPIO_BASE_ADDR +
+                       offset, &reg_val);
+       if (ret)
+               return ret;
+
+       val = (value & 0x1) | 0x4;
+       reg_val = reg_val | val;
+       return __tps6591x_write(tps6591x->client, TPS6591X_GPIO_BASE_ADDR +
+                       offset, reg_val);
+}
+
+static int tps6591x_gpio_to_irq(struct gpio_chip *gc, unsigned off)
+{
+       struct tps6591x *tps6591x;
+       tps6591x = container_of(gc, struct tps6591x, gpio);
+
+       if ((off >= 0) && (off <= TPS6591X_INT_GPIO5 - TPS6591X_INT_GPIO0))
+               return tps6591x->irq_base + TPS6591X_INT_GPIO0 + off;
+
+       return -EIO;
+}
+
+static void tps6591x_gpio_init(struct tps6591x *tps6591x,
+                       struct tps6591x_platform_data *pdata)
+{
+       int ret;
+       int gpio_base = pdata->gpio_base;
+       int i;
+       u8 gpio_reg;
+       struct tps6591x_gpio_init_data *ginit;
+
+       if (gpio_base <= 0)
+               return;
+
+       for (i = 0; i < pdata->num_gpioinit_data; ++i) {
+               ginit = &pdata->gpio_init_data[i];
+               if (!ginit->init_apply)
+                       continue;
+               gpio_reg = (ginit->sleep_en << TPS6591X_GPIO_SLEEP) |
+                               (ginit->pulldn_en << TPS6591X_GPIO_PDEN) |
+                               (ginit->output_mode_en << TPS6591X_GPIO_DIR);
+
+               if (ginit->output_mode_en)
+                       gpio_reg |= ginit->output_val;
+
+               ret =  __tps6591x_write(tps6591x->client,
+                               TPS6591X_GPIO_BASE_ADDR + i, gpio_reg);
+               if (ret < 0)
+                       dev_err(&tps6591x->client->dev, "Gpio %d init "
+                               "configuration failed: %d\n", i, ret);
+       }
+
+       tps6591x->gpio.owner            = THIS_MODULE;
+       tps6591x->gpio.label            = tps6591x->client->name;
+       tps6591x->gpio.dev              = tps6591x->dev;
+       tps6591x->gpio.base             = gpio_base;
+       tps6591x->gpio.ngpio            = 9;
+       tps6591x->gpio.can_sleep        = 1;
+
+       tps6591x->gpio.direction_input  = tps6591x_gpio_input;
+       tps6591x->gpio.direction_output = tps6591x_gpio_output;
+       tps6591x->gpio.set              = tps6591x_gpio_set;
+       tps6591x->gpio.get              = tps6591x_gpio_get;
+       tps6591x->gpio.to_irq           = tps6591x_gpio_to_irq;
+
+       ret = gpiochip_add(&tps6591x->gpio);
+       if (ret)
+               dev_warn(tps6591x->dev, "GPIO registration failed: %d\n", ret);
+}
+
+static int __remove_subdev(struct device *dev, void *unused)
+{
+       platform_device_unregister(to_platform_device(dev));
+       return 0;
+}
+
+static int tps6591x_remove_subdevs(struct tps6591x *tps6591x)
+{
+       return device_for_each_child(tps6591x->dev, NULL, __remove_subdev);
+}
+
+static void tps6591x_irq_lock(struct irq_data *data)
+{
+       struct tps6591x *tps6591x = irq_data_get_irq_chip_data(data);
+
+       mutex_lock(&tps6591x->irq_lock);
+}
+
+static void tps6591x_irq_mask(struct irq_data *irq_data)
+{
+       struct tps6591x *tps6591x = irq_data_get_irq_chip_data(irq_data);
+       unsigned int __irq = irq_data->irq - tps6591x->irq_base;
+       const struct tps6591x_irq_data *data = &tps6591x_irqs[__irq];
+
+       if (data->type == EVENT)
+               tps6591x->mask_reg[data->mask_reg] |= (1 << data->mask_pos);
+       else
+               tps6591x->mask_reg[data->mask_reg] |= (3 << data->mask_pos);
+
+       tps6591x->irq_en &= ~(1 << __irq);
+}
+
+static void tps6591x_irq_unmask(struct irq_data *irq_data)
+{
+       struct tps6591x *tps6591x = irq_data_get_irq_chip_data(irq_data);
+
+       unsigned int __irq = irq_data->irq - tps6591x->irq_base;
+       const struct tps6591x_irq_data *data = &tps6591x_irqs[__irq];
+
+       if (data->type == EVENT) {
+               tps6591x->mask_reg[data->mask_reg] &= ~(1 << data->mask_pos);
+               tps6591x->irq_en |= (1 << __irq);
+       }
+}
+
+static void tps6591x_irq_sync_unlock(struct irq_data *data)
+{
+       struct tps6591x *tps6591x = irq_data_get_irq_chip_data(data);
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(tps6591x->mask_reg); i++) {
+               if (tps6591x->mask_reg[i] != tps6591x->mask_cache[i]) {
+                       if (!WARN_ON(tps6591x_write(tps6591x->dev,
+                                               TPS6591X_INT_MSK + 2*i,
+                                               tps6591x->mask_reg[i])))
+                               tps6591x->mask_cache[i] = tps6591x->mask_reg[i];
+               }
+       }
+
+       mutex_unlock(&tps6591x->irq_lock);
+}
+
+static int tps6591x_irq_set_type(struct irq_data *irq_data, unsigned int type)
+{
+       struct tps6591x *tps6591x = irq_data_get_irq_chip_data(irq_data);
+
+       unsigned int __irq = irq_data->irq - tps6591x->irq_base;
+       const struct tps6591x_irq_data *data = &tps6591x_irqs[__irq];
+
+       if (data->type == GPIO) {
+               if (type & IRQ_TYPE_EDGE_FALLING)
+                       tps6591x->mask_reg[data->mask_reg]
+                               &= ~(1 << data->mask_pos);
+               else
+                       tps6591x->mask_reg[data->mask_reg]
+                               |= (1 << data->mask_pos);
+
+               if (type & IRQ_TYPE_EDGE_RISING)
+                       tps6591x->mask_reg[data->mask_reg]
+                               &= ~(2 << data->mask_pos);
+               else
+                       tps6591x->mask_reg[data->mask_reg]
+                               |= (2 << data->mask_pos);
+
+               tps6591x->irq_en |= (1 << __irq);
+       }
+
+       return 0;
+}
+
+static irqreturn_t tps6591x_irq(int irq, void *data)
+{
+       struct tps6591x *tps6591x = data;
+       int ret = 0;
+       u8 tmp[3];
+       u32 acks, mask = 0;
+       int i;
+
+       for (i = 0; i < 3; i++) {
+               ret = tps6591x_read(tps6591x->dev, TPS6591X_INT_STS + 2*i,
+                               &tmp[i]);
+               if (ret < 0) {
+                       dev_err(tps6591x->dev,
+                               "failed to read interrupt status\n");
+                       return IRQ_NONE;
+               }
+               if (tmp[i]) {
+                       ret = tps6591x_write(tps6591x->dev,
+                                       TPS6591X_INT_STS + 2*i, tmp[i]);
+                       if (ret < 0) {
+                               dev_err(tps6591x->dev,
+                                       "failed to write interrupt status\n");
+                               return IRQ_NONE;
+                       }
+               }
+       }
+
+       acks = (tmp[2] << 16) | (tmp[1] << 8) | tmp[0];
+
+       for (i = 0; i < ARRAY_SIZE(tps6591x_irqs); i++) {
+               if (tps6591x_irqs[i].type == GPIO)
+                       mask = (3 << (tps6591x_irqs[i].mask_pos
+                                       + tps6591x_irqs[i].mask_reg*8));
+               else if (tps6591x_irqs[i].type == EVENT)
+                       mask = (1 << (tps6591x_irqs[i].mask_pos
+                                       + tps6591x_irqs[i].mask_reg*8));
+
+               if ((acks & mask) && (tps6591x->irq_en & (1 << i)))
+                       handle_nested_irq(tps6591x->irq_base + i);
+       }
+       return IRQ_HANDLED;
+}
+
+static int __devinit tps6591x_irq_init(struct tps6591x *tps6591x, int irq,
+                               int irq_base)
+{
+       int i, ret;
+
+       if (!irq_base) {
+               dev_warn(tps6591x->dev, "No interrupt support on IRQ base\n");
+               return -EINVAL;
+       }
+
+       mutex_init(&tps6591x->irq_lock);
+
+       tps6591x->mask_reg[0] = 0xFF;
+       tps6591x->mask_reg[1] = 0xFF;
+       tps6591x->mask_reg[2] = 0xFF;
+       for (i = 0; i < 3; i++) {
+               tps6591x->mask_cache[i] = tps6591x->mask_reg[i];
+               tps6591x_write(tps6591x->dev, TPS6591X_INT_MSK + 2*i,
+                                tps6591x->mask_cache[i]);
+       }
+
+       for (i = 0; i < 3; i++)
+               tps6591x_write(tps6591x->dev, TPS6591X_INT_STS + 2*i, 0xff);
+
+       tps6591x->irq_base = irq_base;
+
+       tps6591x->irq_chip.name = "tps6591x";
+       tps6591x->irq_chip.irq_mask = tps6591x_irq_mask;
+       tps6591x->irq_chip.irq_unmask = tps6591x_irq_unmask;
+       tps6591x->irq_chip.irq_bus_lock = tps6591x_irq_lock;
+       tps6591x->irq_chip.irq_bus_sync_unlock = tps6591x_irq_sync_unlock;
+       tps6591x->irq_chip.irq_set_type = tps6591x_irq_set_type;
+
+       for (i = 0; i < ARRAY_SIZE(tps6591x_irqs); i++) {
+               int __irq = i + tps6591x->irq_base;
+               irq_set_chip_data(__irq, tps6591x);
+               irq_set_chip_and_handler(__irq, &tps6591x->irq_chip,
+                                        handle_simple_irq);
+               irq_set_nested_thread(__irq, 1);
+#ifdef CONFIG_ARM
+               irq_set_status_flags(__irq, IRQF_VALID);
+#endif
+       }
+
+       ret = request_threaded_irq(irq, NULL, tps6591x_irq, IRQF_ONESHOT,
+                               "tps6591x", tps6591x);
+       if (!ret) {
+               device_init_wakeup(tps6591x->dev, 1);
+               enable_irq_wake(irq);
+       }
+
+       return ret;
+}
+
+static int __devinit tps6591x_add_subdevs(struct tps6591x *tps6591x,
+                                         struct tps6591x_platform_data *pdata)
+{
+       struct tps6591x_subdev_info *subdev;
+       struct platform_device *pdev;
+       int i, ret = 0;
+
+       for (i = 0; i < pdata->num_subdevs; i++) {
+               subdev = &pdata->subdevs[i];
+
+               pdev = platform_device_alloc(subdev->name, subdev->id);
+
+               pdev->dev.parent = tps6591x->dev;
+               pdev->dev.platform_data = subdev->platform_data;
+
+               ret = platform_device_add(pdev);
+               if (ret)
+                       goto failed;
+       }
+       return 0;
+
+failed:
+       tps6591x_remove_subdevs(tps6591x);
+       return ret;
+}
+#ifdef CONFIG_DEBUG_FS
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+static void print_regs(const char *header, struct seq_file *s,
+               struct i2c_client *client, int start_offset,
+               int end_offset)
+{
+       uint8_t reg_val;
+       int i;
+       int ret;
+
+       seq_printf(s, "%s\n", header);
+       for (i = start_offset; i <= end_offset; ++i) {
+               ret = __tps6591x_read(client, i, &reg_val);
+               if (ret >= 0)
+                       seq_printf(s, "Reg 0x%02x Value 0x%02x\n", i, reg_val);
+       }
+       seq_printf(s, "------------------\n");
+}
+
+static int dbg_tps_show(struct seq_file *s, void *unused)
+{
+       struct tps6591x *tps = s->private;
+       struct i2c_client *client = tps->client;
+
+       seq_printf(s, "TPS6591x Registers\n");
+       seq_printf(s, "------------------\n");
+
+       print_regs("Timing Regs",    s, client, 0x0, 0x6);
+       print_regs("Alarm Regs",     s, client, 0x8, 0xD);
+       print_regs("RTC Regs",       s, client, 0x10, 0x16);
+       print_regs("BCK Regs",       s, client, 0x17, 0x1B);
+       print_regs("PUADEN Regs",    s, client, 0x18, 0x18);
+       print_regs("REF Regs",       s, client, 0x1D, 0x1D);
+       print_regs("VDD Regs",       s, client, 0x1E, 0x29);
+       print_regs("LDO Regs",       s, client, 0x30, 0x37);
+       print_regs("THERM Regs",     s, client, 0x38, 0x38);
+       print_regs("BBCH Regs",      s, client, 0x39, 0x39);
+       print_regs("DCDCCNTRL Regs", s, client, 0x3E, 0x3E);
+       print_regs("DEV_CNTRL Regs", s, client, 0x3F, 0x40);
+       print_regs("SLEEP Regs",     s, client, 0x41, 0x44);
+       print_regs("EN1 Regs",       s, client, 0x45, 0x48);
+       print_regs("INT Regs",       s, client, 0x50, 0x55);
+       print_regs("GPIO Regs",      s, client, 0x60, 0x68);
+       print_regs("WATCHDOG Regs",  s, client, 0x69, 0x69);
+       print_regs("VMBCH Regs",     s, client, 0x6A, 0x6B);
+       print_regs("LED_CTRL Regs",  s, client, 0x6c, 0x6D);
+       print_regs("PWM_CTRL Regs",  s, client, 0x6E, 0x6F);
+       print_regs("SPARE Regs",     s, client, 0x70, 0x70);
+       print_regs("VERNUM Regs",    s, client, 0x80, 0x80);
+       return 0;
+}
+
+static int dbg_tps_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, dbg_tps_show, inode->i_private);
+}
+
+static const struct file_operations debug_fops = {
+       .open           = dbg_tps_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static void __init tps6591x_debuginit(struct tps6591x *tps)
+{
+       (void)debugfs_create_file("tps6591x", S_IRUGO, NULL,
+                       tps, &debug_fops);
+}
+#else
+static void __init tps6591x_debuginit(struct tps6591x *tpsi)
+{
+       return;
+}
+#endif
+
+static int __init tps6591x_sleepinit(struct tps6591x *tpsi,
+                                       struct tps6591x_platform_data *pdata)
+{
+       struct device *dev = NULL;
+       int ret = 0;
+
+       dev = tpsi->dev;
+
+       if (!pdata->dev_slp_en)
+               goto no_err_return;
+
+       /* pmu dev_slp_en is set. Make sure slp_keepon is available before
+        * allowing SLEEP device state */
+       if (!pdata->slp_keepon) {
+               dev_err(dev, "slp_keepon_data required for slp_en\n");
+               goto err_sleep_init;
+       }
+
+       /* enabling SLEEP device state */
+       ret = tps6591x_set_bits(dev, TPS6591X_DEVCTRL, DEVCTRL_DEV_SLP);
+       if (ret < 0) {
+               dev_err(dev, "set dev_slp failed: %d\n", ret);
+               goto err_sleep_init;
+       }
+
+       if (pdata->slp_keepon->therm_keepon) {
+               ret = tps6591x_set_bits(dev, TPS6591X_SLEEP_KEEP_ON,
+                                               SLEEP_KEEP_ON_THERM);
+               if (ret < 0) {
+                       dev_err(dev, "set therm_keepon failed: %d\n", ret);
+                       goto disable_dev_slp;
+               }
+       }
+
+       if (pdata->slp_keepon->clkout32k_keepon) {
+               ret = tps6591x_set_bits(dev, TPS6591X_SLEEP_KEEP_ON,
+                                               SLEEP_KEEP_ON_CLKOUT32K);
+               if (ret < 0) {
+                       dev_err(dev, "set clkout32k_keepon failed: %d\n", ret);
+                       goto disable_dev_slp;
+               }
+       }
+
+
+       if (pdata->slp_keepon->vrtc_keepon) {
+               ret = tps6591x_set_bits(dev, TPS6591X_SLEEP_KEEP_ON,
+                                               SLEEP_KEEP_ON_VRTC);
+               if (ret < 0) {
+                       dev_err(dev, "set vrtc_keepon failed: %d\n", ret);
+                       goto disable_dev_slp;
+               }
+       }
+
+       if (pdata->slp_keepon->i2chs_keepon) {
+               ret = tps6591x_set_bits(dev, TPS6591X_SLEEP_KEEP_ON,
+                                               SLEEP_KEEP_ON_I2CHS);
+               if (ret < 0) {
+                       dev_err(dev, "set i2chs_keepon failed: %d\n", ret);
+                       goto disable_dev_slp;
+               }
+       }
+
+no_err_return:
+       return 0;
+
+disable_dev_slp:
+       tps6591x_clr_bits(dev, TPS6591X_DEVCTRL, DEVCTRL_DEV_SLP);
+
+err_sleep_init:
+       return ret;
+}
+
+static int __devinit tps6591x_i2c_probe(struct i2c_client *client,
+                                       const struct i2c_device_id *id)
+{
+       struct tps6591x_platform_data *pdata = client->dev.platform_data;
+       struct tps6591x *tps6591x;
+       int ret;
+
+       if (!pdata) {
+               dev_err(&client->dev, "tps6591x requires platform data\n");
+               return -ENOTSUPP;
+       }
+
+       ret = i2c_smbus_read_byte_data(client, TPS6591X_VERNUM);
+       if (ret < 0) {
+               dev_err(&client->dev, "Silicon version number read"
+                               " failed: %d\n", ret);
+               return -EIO;
+       }
+
+       dev_info(&client->dev, "VERNUM is %02x\n", ret);
+
+       tps6591x = kzalloc(sizeof(struct tps6591x), GFP_KERNEL);
+       if (tps6591x == NULL)
+               return -ENOMEM;
+
+       tps6591x->client = client;
+       tps6591x->dev = &client->dev;
+       i2c_set_clientdata(client, tps6591x);
+
+       mutex_init(&tps6591x->lock);
+
+       if (client->irq) {
+               ret = tps6591x_irq_init(tps6591x, client->irq,
+                                       pdata->irq_base);
+               if (ret) {
+                       dev_err(&client->dev, "IRQ init failed: %d\n", ret);
+                       goto err_irq_init;
+               }
+       }
+
+       ret = tps6591x_add_subdevs(tps6591x, pdata);
+       if (ret) {
+               dev_err(&client->dev, "add devices failed: %d\n", ret);
+               goto err_add_devs;
+       }
+
+       tps6591x_gpio_init(tps6591x, pdata);
+
+       tps6591x_debuginit(tps6591x);
+
+       tps6591x_sleepinit(tps6591x, pdata);
+
+       tps6591x_i2c_client = client;
+
+       return 0;
+
+err_add_devs:
+       if (client->irq)
+               free_irq(client->irq, tps6591x);
+err_irq_init:
+       kfree(tps6591x);
+       return ret;
+}
+
+static int __devexit tps6591x_i2c_remove(struct i2c_client *client)
+{
+       struct tps6591x *tps6591x = i2c_get_clientdata(client);
+
+       if (client->irq)
+               free_irq(client->irq, tps6591x);
+
+       if (gpiochip_remove(&tps6591x->gpio) < 0)
+               dev_err(&client->dev, "Error in removing the gpio driver\n");
+
+       kfree(tps6591x);
+       return 0;
+}
+#ifdef CONFIG_PM
+static int tps6591x_i2c_suspend(struct i2c_client *client, pm_message_t state)
+{
+       if (client->irq)
+               disable_irq(client->irq);
+       return 0;
+}
+
+static int tps6591x_i2c_resume(struct i2c_client *client)
+{
+       if (client->irq)
+               enable_irq(client->irq);
+       return 0;
+}
+#endif
+
+
+static const struct i2c_device_id tps6591x_id_table[] = {
+       { "tps6591x", 0 },
+       { },
+};
+MODULE_DEVICE_TABLE(i2c, tps6591x_id_table);
+
+static struct i2c_driver tps6591x_driver = {
+       .driver = {
+               .name   = "tps6591x",
+               .owner  = THIS_MODULE,
+       },
+       .probe          = tps6591x_i2c_probe,
+       .remove         = __devexit_p(tps6591x_i2c_remove),
+#ifdef CONFIG_PM
+       .suspend        = tps6591x_i2c_suspend,
+       .resume         = tps6591x_i2c_resume,
+#endif
+       .id_table       = tps6591x_id_table,
+};
+
+static int __init tps6591x_init(void)
+{
+       return i2c_add_driver(&tps6591x_driver);
+}
+subsys_initcall(tps6591x_init);
+
+static void __exit tps6591x_exit(void)
+{
+       i2c_del_driver(&tps6591x_driver);
+}
+module_exit(tps6591x_exit);
+
+MODULE_DESCRIPTION("TPS6591X core driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/tps80031.c b/drivers/mfd/tps80031.c
new file mode 100644 (file)
index 0000000..10a7f05
--- /dev/null
@@ -0,0 +1,871 @@
+/*
+ * driver/mfd/tps80031.c
+ *
+ * Core driver for TI TPS80031
+ *
+ * Copyright (C) 2011 NVIDIA Corporation
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ */
+
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+
+#include <linux/mfd/core.h>
+#include <linux/mfd/tps80031.h>
+
+/* interrupt related registers */
+#define TPS80031_INT_STS_A             0xD0
+#define TPS80031_INT_STS_B             0xD1
+#define TPS80031_INT_STS_C             0xD2
+#define TPS80031_INT_MSK_LINE_A                0xD3
+#define TPS80031_INT_MSK_LINE_B                0xD4
+#define TPS80031_INT_MSK_LINE_C                0xD5
+#define TPS80031_INT_MSK_STS_A         0xD6
+#define TPS80031_INT_MSK_STS_B         0xD7
+#define TPS80031_INT_MSK_STS_C         0xD8
+
+/* Version number related register */
+#define TPS80031_JTAGVERNUM            0x87
+
+/* External control register */
+#define REGEN1_BASE_ADD                0xAE
+#define REGEN2_BASE_ADD                0xB1
+#define SYSEN_BASE_ADD         0xB4
+
+#define CLK32KAO_BASE_ADD      0xBA
+#define CLK32KG_BASE_ADD       0xBD
+#define CLK32KAUDIO_BASE_ADD   0xC0
+
+#define EXT_CONTROL_CFG_TRANS 0
+#define EXT_CONTROL_CFG_STATE 1
+
+#define STATE_OFF      0x00
+#define STATE_ON       0x01
+#define STATE_MASK     0x03
+
+#define TPS_NUM_SLAVES 4
+
+static u8 pmc_ext_control_base[] = {
+       REGEN1_BASE_ADD,
+       REGEN2_BASE_ADD,
+       SYSEN_BASE_ADD,
+};
+
+struct tps80031_irq_data {
+       u8      mask_reg;
+       u8      mask_mask;
+};
+
+#define TPS80031_IRQ(_reg, _mask)                              \
+       {                                                       \
+               .mask_reg = (TPS80031_INT_MSK_LINE_##_reg) -    \
+                               TPS80031_INT_MSK_LINE_A,        \
+               .mask_mask = (_mask),                           \
+       }
+
+static const struct tps80031_irq_data tps80031_irqs[] = {
+
+       [TPS80031_INT_PWRON]            = TPS80031_IRQ(A, 0),
+       [TPS80031_INT_RPWRON]           = TPS80031_IRQ(A, 1),
+       [TPS80031_INT_SYS_VLOW]         = TPS80031_IRQ(A, 2),
+       [TPS80031_INT_RTC_ALARM]        = TPS80031_IRQ(A, 3),
+       [TPS80031_INT_RTC_PERIOD]       = TPS80031_IRQ(A, 4),
+       [TPS80031_INT_HOT_DIE]          = TPS80031_IRQ(A, 5),
+       [TPS80031_INT_VXX_SHORT]        = TPS80031_IRQ(A, 6),
+       [TPS80031_INT_SPDURATION]       = TPS80031_IRQ(A, 7),
+       [TPS80031_INT_WATCHDOG]         = TPS80031_IRQ(B, 0),
+       [TPS80031_INT_BAT]              = TPS80031_IRQ(B, 1),
+       [TPS80031_INT_SIM]              = TPS80031_IRQ(B, 2),
+       [TPS80031_INT_MMC]              = TPS80031_IRQ(B, 3),
+       [TPS80031_INT_RES]              = TPS80031_IRQ(B, 4),
+       [TPS80031_INT_GPADC_RT]         = TPS80031_IRQ(B, 5),
+       [TPS80031_INT_GPADC_SW2_EOC]    = TPS80031_IRQ(B, 6),
+       [TPS80031_INT_CC_AUTOCAL]       = TPS80031_IRQ(B, 7),
+       [TPS80031_INT_ID_WKUP]          = TPS80031_IRQ(C, 0),
+       [TPS80031_INT_VBUSS_WKUP]       = TPS80031_IRQ(C, 1),
+       [TPS80031_INT_ID]               = TPS80031_IRQ(C, 2),
+       [TPS80031_INT_VBUS]             = TPS80031_IRQ(C, 3),
+       [TPS80031_INT_CHRG_CTRL]        = TPS80031_IRQ(C, 4),
+       [TPS80031_INT_EXT_CHRG]         = TPS80031_IRQ(C, 5),
+       [TPS80031_INT_INT_CHRG]         = TPS80031_IRQ(C, 6),
+       [TPS80031_INT_RES2]             = TPS80031_IRQ(C, 7),
+};
+
+/* Structure for TPS80031 Slaves */
+struct tps80031_client {
+       struct i2c_client *client;
+       struct mutex lock;
+       u8 addr;
+};
+
+struct tps80031 {
+       struct device           *dev;
+
+       struct gpio_chip        gpio;
+       struct irq_chip         irq_chip;
+       struct mutex            irq_lock;
+       int                     irq_base;
+       u32                     irq_en;
+       u8                      mask_cache[3];
+       u8                      mask_reg[3];
+       struct tps80031_client  tps_clients[TPS_NUM_SLAVES];
+};
+
+static inline int __tps80031_read(struct i2c_client *client,
+                                 int reg, uint8_t *val)
+{
+       int ret;
+
+       ret = i2c_smbus_read_byte_data(client, reg);
+       if (ret < 0) {
+               dev_err(&client->dev,
+                       "failed reading from addr 0x%02x, reg 0x%02x\n",
+                       client->addr, reg);
+               return ret;
+       }
+
+       *val = (uint8_t)ret;
+
+       return 0;
+}
+
+static inline int __tps80031_reads(struct i2c_client *client, int reg,
+                               int len, uint8_t *val)
+{
+       int ret;
+
+       ret = i2c_smbus_read_i2c_block_data(client, reg, len, val);
+       if (ret < 0) {
+               dev_err(&client->dev,
+                       "failed reading from addr 0x%02x, reg   0x%02x\n",
+                        client->addr, reg);
+               return ret;
+       }
+
+       return 0;
+}
+
+static inline int __tps80031_write(struct i2c_client *client,
+                                int reg, uint8_t val)
+{
+       int ret;
+       ret = i2c_smbus_write_byte_data(client, reg, val);
+       if (ret < 0) {
+               dev_err(&client->dev,
+                       "failed writing 0x%02x to 0x%02x\n", val, reg);
+               return ret;
+       }
+
+       return 0;
+}
+
+static inline int __tps80031_writes(struct i2c_client *client, int reg,
+                                 int len, uint8_t *val)
+{
+       int ret;
+
+       ret = i2c_smbus_write_i2c_block_data(client, reg, len, val);
+       if (ret < 0) {
+               dev_err(&client->dev, "failed writings to 0x%02x\n", reg);
+               return ret;
+       }
+
+       return 0;
+}
+
+int tps80031_write(struct device *dev, int sid, int reg, uint8_t val)
+{
+       struct tps80031 *tps80031 = dev_get_drvdata(dev);
+       struct tps80031_client *tps = &tps80031->tps_clients[sid];
+       int ret;
+
+       mutex_lock(&tps->lock);
+       ret = __tps80031_write(tps->client, reg, val);
+       mutex_unlock(&tps->lock);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(tps80031_write);
+
+int tps80031_writes(struct device *dev, int sid, int reg, int len, uint8_t *val)
+{
+       struct tps80031 *tps80031 = dev_get_drvdata(dev);
+       struct tps80031_client *tps = &tps80031->tps_clients[sid];
+       int ret;
+
+       mutex_lock(&tps->lock);
+       ret = __tps80031_writes(tps->client, reg, len, val);
+       mutex_unlock(&tps->lock);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(tps80031_writes);
+
+int tps80031_read(struct device *dev, int sid, int reg, uint8_t *val)
+{
+       struct tps80031 *tps80031 = dev_get_drvdata(dev);
+       struct tps80031_client *tps = &tps80031->tps_clients[sid];
+
+       return __tps80031_read(tps->client, reg, val);
+}
+EXPORT_SYMBOL_GPL(tps80031_read);
+
+int tps80031_reads(struct device *dev, int sid, int reg, int len, uint8_t *val)
+{
+       struct tps80031 *tps80031 = dev_get_drvdata(dev);
+       struct tps80031_client *tps = &tps80031->tps_clients[sid];
+
+       return __tps80031_reads(tps->client, reg, len, val);
+}
+EXPORT_SYMBOL_GPL(tps80031_reads);
+
+int tps80031_set_bits(struct device *dev, int sid, int reg, uint8_t bit_mask)
+{
+       struct tps80031 *tps80031 = dev_get_drvdata(dev);
+       struct tps80031_client *tps = &tps80031->tps_clients[sid];
+       uint8_t reg_val;
+       int ret = 0;
+
+       mutex_lock(&tps->lock);
+
+       ret = __tps80031_read(tps->client, reg, &reg_val);
+       if (ret)
+               goto out;
+
+       if ((reg_val & bit_mask) != bit_mask) {
+               reg_val |= bit_mask;
+               ret = __tps80031_write(tps->client, reg, reg_val);
+       }
+out:
+       mutex_unlock(&tps->lock);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(tps80031_set_bits);
+
+int tps80031_clr_bits(struct device *dev, int sid, int reg, uint8_t bit_mask)
+{
+       struct tps80031 *tps80031 = dev_get_drvdata(dev);
+       struct tps80031_client *tps = &tps80031->tps_clients[sid];
+       uint8_t reg_val;
+       int ret = 0;
+
+       mutex_lock(&tps->lock);
+
+       ret = __tps80031_read(tps->client, reg, &reg_val);
+       if (ret)
+               goto out;
+
+       if (reg_val & bit_mask) {
+               reg_val &= ~bit_mask;
+               ret = __tps80031_write(tps->client, reg, reg_val);
+       }
+out:
+       mutex_unlock(&tps->lock);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(tps80031_clr_bits);
+
+int tps80031_update(struct device *dev, int sid, int reg, uint8_t val,
+               uint8_t mask)
+{
+       struct tps80031 *tps80031 = dev_get_drvdata(dev);
+       struct tps80031_client *tps = &tps80031->tps_clients[sid];
+       uint8_t reg_val;
+       int ret = 0;
+
+       mutex_lock(&tps->lock);
+
+       ret = __tps80031_read(tps->client, reg, &reg_val);
+       if (ret)
+               goto out;
+
+       if ((reg_val & mask) != val) {
+               reg_val = (reg_val & ~mask) | (val & mask);
+               ret = __tps80031_write(tps->client, reg, reg_val);
+       }
+out:
+       mutex_unlock(&tps->lock);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(tps80031_update);
+
+static struct tps80031 *tps80031_dev;
+int tps80031_power_off(void)
+{
+       struct tps80031_client *tps = &tps80031_dev->tps_clients[SLAVE_ID1];
+       struct device *dev;
+
+       if (!tps->client)
+               return -EINVAL;
+
+       dev = &tps->client->dev;
+
+       /* FIXME!! Put the logic here to switch off pmu*/
+       return 0;
+}
+
+static int tps80031_gpio_get(struct gpio_chip *gc, unsigned offset)
+{
+       struct tps80031 *tps80031 = container_of(gc, struct tps80031, gpio);
+       struct tps80031_client *tps = &tps80031->tps_clients[SLAVE_ID1];
+       uint8_t state;
+       uint8_t trans;
+       int ret;
+
+       ret = __tps80031_read(tps->client,
+                       pmc_ext_control_base[offset] +
+                               EXT_CONTROL_CFG_STATE, &state);
+       if (ret)
+               return ret;
+
+       if (state != 0) {
+               ret = __tps80031_read(tps->client,
+                               pmc_ext_control_base[offset] +
+                                       EXT_CONTROL_CFG_TRANS, &trans);
+               if (ret)
+                       return ret;
+               return trans & 0x1;
+       }
+       return 0;
+}
+
+static void tps80031_gpio_set(struct gpio_chip *gc, unsigned offset,
+                       int value)
+{
+       struct tps80031 *tps80031 = container_of(gc, struct tps80031, gpio);
+
+       tps80031_update(tps80031->dev, SLAVE_ID1,
+               pmc_ext_control_base[offset] + EXT_CONTROL_CFG_TRANS,
+                       value, 0x1);
+}
+
+static int tps80031_gpio_input(struct gpio_chip *gc, unsigned offset)
+{
+       return -EIO;
+}
+
+static int tps80031_gpio_output(struct gpio_chip *gc, unsigned offset,
+                               int value)
+{
+       tps80031_gpio_set(gc, offset, value);
+       return 0;
+}
+
+static int tps80031_gpio_enable(struct gpio_chip *gc, unsigned offset)
+{
+       struct tps80031 *tps80031 = container_of(gc, struct tps80031, gpio);
+       int ret;
+
+       ret = tps80031_update(tps80031->dev, SLAVE_ID1,
+               pmc_ext_control_base[offset] + EXT_CONTROL_CFG_STATE,
+                                               STATE_ON, STATE_MASK);
+       if (ret)
+               return ret;
+
+       return tps80031_write(tps80031->dev, SLAVE_ID1,
+               pmc_ext_control_base[offset] + EXT_CONTROL_CFG_TRANS, 0x0);
+}
+
+static void tps80031_gpio_disable(struct gpio_chip *gc, unsigned offset)
+{
+       struct tps80031 *tps80031 = container_of(gc, struct tps80031, gpio);
+       tps80031_update(tps80031->dev, SLAVE_ID1,
+               pmc_ext_control_base[offset] + EXT_CONTROL_CFG_STATE,
+                                               STATE_OFF, STATE_MASK);
+}
+
+static void tps80031_gpio_init(struct tps80031 *tps80031,
+                       struct tps80031_platform_data *pdata)
+{
+       int ret;
+       int gpio_base = pdata->gpio_base;
+       struct tps80031_client *tps = &tps80031->tps_clients[SLAVE_ID1];
+
+       if (gpio_base <= 0)
+               return;
+
+       tps80031->gpio.owner            = THIS_MODULE;
+       tps80031->gpio.label            = tps->client->name;
+       tps80031->gpio.dev              = tps80031->dev;
+       tps80031->gpio.base             = gpio_base;
+       tps80031->gpio.ngpio            = 3;
+       tps80031->gpio.can_sleep        = 1;
+
+       tps80031->gpio.request          = tps80031_gpio_enable;
+       tps80031->gpio.free             = tps80031_gpio_disable;
+       tps80031->gpio.direction_input  = tps80031_gpio_input;
+       tps80031->gpio.direction_output = tps80031_gpio_output;
+       tps80031->gpio.set              = tps80031_gpio_set;
+       tps80031->gpio.get              = tps80031_gpio_get;
+
+       ret = gpiochip_add(&tps80031->gpio);
+       if (ret)
+               dev_warn(tps80031->dev, "GPIO registration failed: %d\n", ret);
+}
+
+static int __remove_subdev(struct device *dev, void *unused)
+{
+       platform_device_unregister(to_platform_device(dev));
+       return 0;
+}
+
+static int tps80031_remove_subdevs(struct tps80031 *tps80031)
+{
+       return device_for_each_child(tps80031->dev, NULL, __remove_subdev);
+}
+
+static void tps80031_irq_lock(unsigned int irq)
+{
+       struct tps80031 *tps80031 = get_irq_chip_data(irq);
+
+       mutex_lock(&tps80031->irq_lock);
+}
+
+static void tps80031_irq_enable(unsigned int irq)
+{
+       struct tps80031 *tps80031 = get_irq_chip_data(irq);
+       unsigned int __irq = irq - tps80031->irq_base;
+       const struct tps80031_irq_data *data = &tps80031_irqs[__irq];
+
+       tps80031->mask_reg[data->mask_reg] &= ~(1 << data->mask_mask);
+       tps80031->irq_en |= (1 << __irq);
+}
+
+static void tps80031_irq_disable(unsigned int irq)
+{
+       struct tps80031 *tps80031 = get_irq_chip_data(irq);
+
+       unsigned int __irq = irq - tps80031->irq_base;
+       const struct tps80031_irq_data *data = &tps80031_irqs[__irq];
+
+       tps80031->mask_reg[data->mask_reg] |= (1 << data->mask_mask);
+       tps80031->irq_en &= ~(1 << __irq);
+}
+
+static void tps80031_irq_sync_unlock(unsigned int irq)
+{
+       struct tps80031 *tps80031 = get_irq_chip_data(irq);
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(tps80031->mask_reg); i++) {
+               if (tps80031->mask_reg[i] != tps80031->mask_cache[i]) {
+                       if (!WARN_ON(tps80031_write(tps80031->dev, SLAVE_ID2,
+                                               TPS80031_INT_MSK_LINE_A + i,
+                                               tps80031->mask_reg[i])))
+                               if (!WARN_ON(tps80031_write(tps80031->dev,
+                                               SLAVE_ID2,
+                                               TPS80031_INT_MSK_STS_A + i,
+                                               tps80031->mask_reg[i])))
+                                       tps80031->mask_cache[i] =
+                                                       tps80031->mask_reg[i];
+               }
+       }
+
+       mutex_unlock(&tps80031->irq_lock);
+}
+
+static irqreturn_t tps80031_irq(int irq, void *data)
+{
+       struct tps80031 *tps80031 = data;
+       int ret = 0;
+       u8 tmp[3];
+       u32 acks;
+       int i;
+
+       for (i = 0; i < 3; i++) {
+               ret = tps80031_read(tps80031->dev, SLAVE_ID2,
+                       TPS80031_INT_STS_A + i, &tmp[i]);
+               if (ret < 0) {
+                       dev_err(tps80031->dev, "failed to read interrupt "
+                                                       "status\n");
+                       return IRQ_NONE;
+               }
+               if (tmp[i]) {
+                       ret = tps80031_write(tps80031->dev, SLAVE_ID2,
+                                       TPS80031_INT_STS_A + i, tmp[i]);
+                       if (ret < 0) {
+                               dev_err(tps80031->dev, "failed to write "
+                                                       "interrupt status\n");
+                               return IRQ_NONE;
+                       }
+               }
+       }
+       acks = (tmp[2] << 16) | (tmp[1] << 8) | tmp[0];
+       while (acks) {
+               i = __ffs(acks);
+               if (tps80031->irq_en & (1 << i))
+                       handle_nested_irq(tps80031->irq_base + i);
+               acks &= ~(1 << i);
+       }
+       return IRQ_HANDLED;
+}
+
+static int __devinit tps80031_irq_init(struct tps80031 *tps80031, int irq,
+                               int irq_base)
+{
+       int i, ret;
+
+       if (!irq_base) {
+               dev_warn(tps80031->dev, "No interrupt support on IRQ base\n");
+               return -EINVAL;
+       }
+
+       mutex_init(&tps80031->irq_lock);
+
+       for (i = 0; i < 3; i++) {
+               tps80031->mask_reg[i] = 0xFF;
+               tps80031->mask_cache[i] = tps80031->mask_reg[i];
+               tps80031_write(tps80031->dev, SLAVE_ID2,
+                                       TPS80031_INT_MSK_LINE_A + i,
+                                       tps80031->mask_cache[i]);
+               tps80031_write(tps80031->dev, SLAVE_ID2,
+                                       TPS80031_INT_MSK_STS_A + i, 0xFF);
+               tps80031_write(tps80031->dev, SLAVE_ID2,
+                                       TPS80031_INT_STS_A + i, 0xFF);
+       }
+
+       tps80031->irq_base = irq_base;
+
+       tps80031->irq_chip.name = "tps80031";
+       tps80031->irq_chip.enable = tps80031_irq_enable;
+       tps80031->irq_chip.disable = tps80031_irq_disable;
+       tps80031->irq_chip.bus_lock = tps80031_irq_lock;
+       tps80031->irq_chip.bus_sync_unlock = tps80031_irq_sync_unlock;
+
+       for (i = 0; i < ARRAY_SIZE(tps80031_irqs); i++) {
+               int __irq = i + tps80031->irq_base;
+               set_irq_chip_data(__irq, tps80031);
+               set_irq_chip_and_handler(__irq, &tps80031->irq_chip,
+                                        handle_simple_irq);
+               set_irq_nested_thread(__irq, 1);
+#ifdef CONFIG_ARM
+               set_irq_flags(__irq, IRQF_VALID);
+#endif
+       }
+
+       ret = request_threaded_irq(irq, NULL, tps80031_irq, IRQF_ONESHOT,
+                               "tps80031", tps80031);
+       if (!ret) {
+               device_init_wakeup(tps80031->dev, 1);
+               enable_irq_wake(irq);
+       }
+
+       return ret;
+}
+
+static void tps80031_clk32k_enable(struct tps80031 *tps80031, int base_add)
+{
+       int ret;
+       ret = tps80031_update(tps80031->dev, SLAVE_ID1,
+                       base_add + EXT_CONTROL_CFG_STATE, STATE_ON, STATE_MASK);
+       if (!ret)
+               ret = tps80031_update(tps80031->dev, SLAVE_ID1,
+                               base_add + EXT_CONTROL_CFG_TRANS,
+                               STATE_ON, STATE_MASK);
+       if (ret < 0)
+               dev_err(tps80031->dev, "Error in updating clock register\n");
+}
+
+static void tps80031_clk32k_init(struct tps80031 *tps80031,
+                       struct tps80031_platform_data *pdata)
+{
+       struct tps80031_32kclock_plat_data *clk32k_pdata;
+
+       if (!(pdata && pdata->clk32k_pdata))
+               return;
+
+       clk32k_pdata = pdata->clk32k_pdata;
+       if (clk32k_pdata->en_clk32kao)
+               tps80031_clk32k_enable(tps80031, CLK32KAO_BASE_ADD);
+
+       if (clk32k_pdata->en_clk32kg)
+               tps80031_clk32k_enable(tps80031, CLK32KG_BASE_ADD);
+
+       if (clk32k_pdata->en_clk32kaudio)
+               tps80031_clk32k_enable(tps80031, CLK32KAUDIO_BASE_ADD);
+}
+
+static int __devinit tps80031_add_subdevs(struct tps80031 *tps80031,
+                                         struct tps80031_platform_data *pdata)
+{
+       struct tps80031_subdev_info *subdev;
+       struct platform_device *pdev;
+       int i, ret = 0;
+
+       for (i = 0; i < pdata->num_subdevs; i++) {
+               subdev = &pdata->subdevs[i];
+
+               pdev = platform_device_alloc(subdev->name, subdev->id);
+
+               pdev->dev.parent = tps80031->dev;
+               pdev->dev.platform_data = subdev->platform_data;
+
+               ret = platform_device_add(pdev);
+               if (ret)
+                       goto failed;
+       }
+       return 0;
+
+failed:
+       tps80031_remove_subdevs(tps80031);
+       return ret;
+}
+
+#ifdef CONFIG_DEBUG_FS
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+static void print_regs(const char *header, struct seq_file *s,
+               int sid, int start_offset, int end_offset)
+{
+       struct tps80031 *tps80031 = s->private;
+       struct tps80031_client *tps = &tps80031->tps_clients[sid];
+       uint8_t reg_val;
+       int i;
+       int ret;
+
+       seq_printf(s, "%s\n", header);
+       for (i = start_offset; i <= end_offset; ++i) {
+               ret = __tps80031_read(tps->client, i, &reg_val);
+               if (ret >= 0)
+                       seq_printf(s, "Addr = 0x%02x Reg 0x%02x Value 0x%02x\n",
+                                               tps->client->addr, i, reg_val);
+       }
+       seq_printf(s, "------------------\n");
+}
+
+static int dbg_tps_show(struct seq_file *s, void *unused)
+{
+       seq_printf(s, "TPS80031 Registers\n");
+       seq_printf(s, "------------------\n");
+       print_regs("VIO Regs",       s, SLAVE_ID1, 0x47, 0x49);
+       print_regs("VIO Regs",       s, SLAVE_ID0, 0x49, 0x4A);
+       print_regs("SMPS1 Regs",     s, SLAVE_ID1, 0x53, 0x54);
+       print_regs("SMPS1 Regs",     s, SLAVE_ID0, 0x55, 0x56);
+       print_regs("SMPS1 Regs",     s, SLAVE_ID1, 0x57, 0x57);
+       print_regs("SMPS2 Regs",     s, SLAVE_ID1, 0x59, 0x5B);
+       print_regs("SMPS2 Regs",     s, SLAVE_ID0, 0x5B, 0x5C);
+       print_regs("SMPS2 Regs",     s, SLAVE_ID1, 0x5C, 0x5D);
+       print_regs("SMPS3 Regs",     s, SLAVE_ID1, 0x65, 0x68);
+       print_regs("SMPS4 Regs",     s, SLAVE_ID1, 0x41, 0x44);
+       print_regs("VANA Regs",      s, SLAVE_ID1, 0x81, 0x83);
+       print_regs("VRTC Regs",      s, SLAVE_ID1, 0xC3, 0xC4);
+       print_regs("LDO1 Regs",      s, SLAVE_ID1, 0x9D, 0x9F);
+       print_regs("LDO2 Regs",      s, SLAVE_ID1, 0x85, 0x87);
+       print_regs("LDO3 Regs",      s, SLAVE_ID1, 0x8D, 0x8F);
+       print_regs("LDO4 Regs",      s, SLAVE_ID1, 0x89, 0x8B);
+       print_regs("LDO5 Regs",      s, SLAVE_ID1, 0x99, 0x9B);
+       print_regs("LDO6 Regs",      s, SLAVE_ID1, 0x91, 0x93);
+       print_regs("LDO7 Regs",      s, SLAVE_ID1, 0xA5, 0xA7);
+       print_regs("LDOUSB Regs",    s, SLAVE_ID1, 0xA1, 0xA3);
+       print_regs("LDOLN Regs",     s, SLAVE_ID1, 0x95, 0x97);
+       print_regs("REGEN1 Regs",    s, SLAVE_ID1, 0xAE, 0xAF);
+       print_regs("REGEN2 Regs",    s, SLAVE_ID1, 0xB1, 0xB2);
+       print_regs("SYSEN Regs",     s, SLAVE_ID1, 0xB4, 0xB5);
+       print_regs("CLK32KAO Regs",  s, SLAVE_ID1, 0xBA, 0xBB);
+       print_regs("CLK32KG Regs",   s, SLAVE_ID1, 0xBD, 0xBE);
+       print_regs("CLK32KAUD Regs", s, SLAVE_ID1, 0xC0, 0xC1);
+       print_regs("INT Regs",       s, SLAVE_ID2, 0xD0, 0xD8);
+       print_regs("VERNUM Regs",    s, SLAVE_ID1, 0x87, 0x87);
+       return 0;
+}
+
+static int dbg_tps_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, dbg_tps_show, inode->i_private);
+}
+
+static const struct file_operations debug_fops = {
+       .open           = dbg_tps_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static void __init tps80031_debuginit(struct tps80031 *tps)
+{
+       (void)debugfs_create_file("tps80031", S_IRUGO, NULL,
+                       tps, &debug_fops);
+}
+#else
+static void __init tps80031_debuginit(struct tps80031 *tpsi)
+{
+       return;
+}
+#endif
+
+static int __devexit tps80031_i2c_remove(struct i2c_client *client)
+{
+       struct tps80031 *tps80031 = i2c_get_clientdata(client);
+       int i;
+
+       if (client->irq)
+               free_irq(client->irq, tps80031);
+
+       if (tps80031->gpio.owner != NULL)
+               if (gpiochip_remove(&tps80031->gpio) < 0)
+                       dev_err(&client->dev, "Error in removing the gpio driver\n");
+
+       for (i = 0; i < TPS_NUM_SLAVES; i++) {
+               struct tps80031_client *tps = &tps80031->tps_clients[i];
+               if (tps->client && tps->client != client)
+                       i2c_unregister_device(tps->client);
+               tps80031->tps_clients[i].client = NULL;
+               mutex_destroy(&tps->lock);
+       }
+
+       kfree(tps80031);
+       return 0;
+}
+
+static int __devinit tps80031_i2c_probe(struct i2c_client *client,
+                                       const struct i2c_device_id *id)
+{
+       struct tps80031_platform_data *pdata = client->dev.platform_data;
+       struct tps80031 *tps80031;
+       struct tps80031_client *tps;
+       int ret;
+       int i;
+
+       if (!pdata) {
+               dev_err(&client->dev, "tps80031 requires platform data\n");
+               return -ENOTSUPP;
+       }
+
+       ret = i2c_smbus_read_byte_data(client, TPS80031_JTAGVERNUM);
+       if (ret < 0) {
+               dev_err(&client->dev, "Silicon version number read"
+                               " failed: %d\n", ret);
+               return -EIO;
+       }
+
+       dev_info(&client->dev, "VERNUM is %02x\n", ret);
+
+       tps80031 = kzalloc(sizeof(struct tps80031), GFP_KERNEL);
+       if (tps80031 == NULL)
+               return -ENOMEM;
+
+       tps80031->dev = &client->dev;
+       i2c_set_clientdata(client, tps80031);
+
+       /* Set up slaves */
+       tps80031->tps_clients[SLAVE_ID0].addr = I2C_ID0_ADDR;
+       tps80031->tps_clients[SLAVE_ID1].addr = I2C_ID1_ADDR;
+       tps80031->tps_clients[SLAVE_ID2].addr = I2C_ID2_ADDR;
+       tps80031->tps_clients[SLAVE_ID3].addr = I2C_ID3_ADDR;
+       for (i = 0; i < TPS_NUM_SLAVES; i++) {
+               tps = &tps80031->tps_clients[i];
+               if (tps->addr == client->addr)
+                       tps->client = client;
+               else
+                       tps->client = i2c_new_dummy(client->adapter,
+                                               tps->addr);
+               if (!tps->client) {
+                       dev_err(&client->dev, "can't attach client %d\n", i);
+                       ret = -ENOMEM;
+                       goto fail;
+               }
+               i2c_set_clientdata(tps->client, tps80031);
+               mutex_init(&tps->lock);
+       }
+
+       if (client->irq) {
+               ret = tps80031_irq_init(tps80031, client->irq,
+                                       pdata->irq_base);
+               if (ret) {
+                       dev_err(&client->dev, "IRQ init failed: %d\n", ret);
+                       goto fail;
+               }
+       }
+
+       ret = tps80031_add_subdevs(tps80031, pdata);
+       if (ret) {
+               dev_err(&client->dev, "add devices failed: %d\n", ret);
+               goto fail;
+       }
+
+       tps80031_gpio_init(tps80031, pdata);
+
+       tps80031_clk32k_init(tps80031, pdata);
+
+       tps80031_debuginit(tps80031);
+
+       tps80031_dev = tps80031;
+
+       return 0;
+
+fail:
+       tps80031_i2c_remove(client);
+       return ret;
+}
+
+#ifdef CONFIG_PM
+static int tps80031_i2c_suspend(struct i2c_client *client, pm_message_t state)
+{
+       if (client->irq)
+               disable_irq(client->irq);
+       return 0;
+}
+
+static int tps80031_i2c_resume(struct i2c_client *client)
+{
+       if (client->irq)
+               enable_irq(client->irq);
+       return 0;
+}
+#endif
+
+
+static const struct i2c_device_id tps80031_id_table[] = {
+       { "tps80031", 0 },
+       { },
+};
+MODULE_DEVICE_TABLE(i2c, tps80031_id_table);
+
+static struct i2c_driver tps80031_driver = {
+       .driver = {
+               .name   = "tps80031",
+               .owner  = THIS_MODULE,
+       },
+       .probe          = tps80031_i2c_probe,
+       .remove         = __devexit_p(tps80031_i2c_remove),
+#ifdef CONFIG_PM
+       .suspend        = tps80031_i2c_suspend,
+       .resume         = tps80031_i2c_resume,
+#endif
+       .id_table       = tps80031_id_table,
+};
+
+static int __init tps80031_init(void)
+{
+       return i2c_add_driver(&tps80031_driver);
+}
+subsys_initcall(tps80031_init);
+
+static void __exit tps80031_exit(void)
+{
+       i2c_del_driver(&tps80031_driver);
+}
+module_exit(tps80031_exit);
+
+MODULE_DESCRIPTION("TPS80031 core driver");
+MODULE_LICENSE("GPL");
index b8eef46..a455771 100644 (file)
 
 #define DRIVER_NAME                    "twl"
 
+#if defined(CONFIG_TWL4030_BCI_BATTERY) || \
+       defined(CONFIG_TWL4030_BCI_BATTERY_MODULE) || \
+       defined(CONFIG_TWL6030_BCI_BATTERY) || \
+       defined(CONFIG_TWL6030_BCI_BATTERY_MODULE)
+#define twl_has_bci()          true
+#else
+#define twl_has_bci()          false
+#endif
 #if defined(CONFIG_KEYBOARD_TWL4030) || defined(CONFIG_KEYBOARD_TWL4030_MODULE)
 #define twl_has_keypad()       true
 #else
@@ -77,7 +85,8 @@
 #define twl_has_regulator()    false
 #endif
 
-#if defined(CONFIG_TWL4030_MADC) || defined(CONFIG_TWL4030_MADC_MODULE)
+#if defined(CONFIG_TWL4030_MADC) || defined(CONFIG_TWL4030_MADC_MODULE) ||\
+    defined(CONFIG_TWL6030_GPADC) || defined(CONFIG_TWL6030_GPADC_MODULE)
 #define twl_has_madc() true
 #else
 #define twl_has_madc() false
 /* Triton Core internal information (BEGIN) */
 
 /* Last - for index max*/
-#define TWL4030_MODULE_LAST            TWL4030_MODULE_SECURED_REG
+#define TWL4030_MODULE_LAST            TWL6025_MODULE_CHARGER
 
 #define TWL_NUM_SLAVES         4
 
 #define TWL6030_BASEADD_RSV            0x0000
 #define TWL6030_BASEADD_ZERO           0x0000
 
+/* twl6030 SMPS EPROM values */
+#define TWL6030_SMPS_OFFSET            0xB0
+#define TWL6030_SMPS_MULT              0xB3
+
+
 /* Few power values */
 #define R_CFG_BOOT                     0x05
 
 #define HIGH_PERF_SQ                   (1 << 3)
 #define CK32K_LOWPWR_EN                        (1 << 7)
 
+/* MPU80031 specific clock32 generation register */
+#define REG_CLK32KG_CFG_TRANS 0x8D
+#define REG_CLK32KG_CFG_STATE 0x8E
 
 /* chip-specific feature flags, for i2c_device_id.driver_data */
 #define TWL4030_VAUX2          BIT(0)  /* pre-5030 voltage ranges */
@@ -240,6 +257,33 @@ unsigned int twl_rev(void)
 }
 EXPORT_SYMBOL(twl_rev);
 
+static unsigned int twl_feat;
+unsigned int twl_features(void)
+{
+       return twl_feat;
+}
+EXPORT_SYMBOL(twl_features);
+
+u8 twl_get_smps_offset(void)
+{
+       u8 value;
+
+       twl_i2c_read_u8(TWL_MODULE_PM_RECEIVER, &value,
+                       TWL6030_SMPS_OFFSET);
+       return value;
+}
+EXPORT_SYMBOL(twl_get_smps_offset);
+
+u8 twl_get_smps_mult(void)
+{
+       u8 value;
+
+       twl_i2c_read_u8(TWL_MODULE_PM_RECEIVER, &value,
+                       TWL6030_SMPS_MULT);
+       return value;
+}
+EXPORT_SYMBOL(twl_get_smps_mult);
+
 /* Structure for each TWL4030/TWL6030 Slave */
 struct twl_client {
        struct i2c_client *client;
@@ -490,6 +534,19 @@ int twl_i2c_read_u8(u8 mod_no, u8 *value, u8 reg)
 }
 EXPORT_SYMBOL(twl_i2c_read_u8);
 
+
+void twl_reg_dump(int module, int start, int end)
+{
+       int i;
+       u8 val;
+
+       for (i = start; i < end; i++) {
+               twl_i2c_read_u8(module, &val, i);
+               printk(KERN_ERR "reg 0x%2x val 0x%2x\n", i, val);
+       }
+}
+EXPORT_SYMBOL(twl_reg_dump);
+
 /*----------------------------------------------------------------------*/
 
 /**
@@ -660,8 +717,16 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features)
                if (IS_ERR(child))
                        return PTR_ERR(child);
        }
+       if (twl_has_bci() && pdata->bci &&
+           (features & TWL6030_CLASS)) {
+               child = add_child(1, "twl6030_bci",
+                               pdata->bci, sizeof(*pdata->bci),
+                               false,
+                               pdata->irq_base + CHARGER_INTR_OFFSET,
+                               pdata->irq_base + CHARGERFAULT_INTR_OFFSET);
+       }
 
-       if (twl_has_madc() && pdata->madc) {
+       if (twl_has_madc() && pdata->madc && twl_class_is_4030()) {
                child = add_child(2, "twl4030_madc",
                                pdata->madc, sizeof(*pdata->madc),
                                true, pdata->irq_base + MADC_INTR_OFFSET, 0);
@@ -669,6 +734,15 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features)
                        return PTR_ERR(child);
        }
 
+       if (twl_has_madc() && pdata->madc && twl_class_is_6030()) {
+               child = add_child(1, "twl6030_gpadc",
+                               pdata->madc, sizeof(*pdata->madc),
+                               true, pdata->irq_base + MADC_INTR_OFFSET,
+                               pdata->irq_base + GPADCSW_INTR_OFFSET);
+               if (IS_ERR(child))
+                       return PTR_ERR(child);
+       }
+
        if (twl_has_rtc()) {
                /*
                 * REVISIT platform_data here currently might expose the
@@ -1084,13 +1158,29 @@ static inline int __init unprotect_pm_master(void)
 }
 
 static void clocks_init(struct device *dev,
-                       struct twl4030_clock_init_data *clock)
+                       struct twl4030_clock_init_data *clock,
+                       unsigned long features)
 {
        int e = 0;
        struct clk *osc;
        u32 rate;
        u8 ctrl = HFCLK_FREQ_26_MHZ;
 
+       if (features & MPU80031_SUBCLASS) {
+               if (clock && clock->clk32_active_state_on) {
+                       e = twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0x1,
+                                       REG_CLK32KG_CFG_TRANS);
+                       if (!e)
+                               e = twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0x1,
+                                       REG_CLK32KG_CFG_STATE);
+                       if (e) {
+                               dev_err(dev, "Error in initialization"
+                                               " of 32K output\n");
+                               return;
+                       }
+               }
+       }
+
 #if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3)
        if (cpu_is_omap2430())
                osc = clk_get(dev, "osc_ck");
@@ -1228,7 +1318,7 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id)
        }
 
        /* setup clock framework */
-       clocks_init(&client->dev, pdata->clock);
+       clocks_init(&client->dev, pdata->clock, id->driver_data);
 
        /* read TWL IDCODE Register */
        if (twl_id == TWL4030_CLASS_ID) {
@@ -1269,7 +1359,16 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id)
                twl_i2c_write_u8(TWL4030_MODULE_INTBR, temp, REG_GPPUPDCTR1);
        }
 
+       twl_feat = id->driver_data;
+
        status = add_children(pdata, id->driver_data);
+       if (status < 0 )
+               goto fail;
+
+       /* Board Specific Init Callback */
+       if(pdata->init)
+               status = pdata->init();
+
 fail:
        if (status < 0)
                twl_remove(client);
@@ -1287,6 +1386,7 @@ static const struct i2c_device_id twl_ids[] = {
                                           and vibrator. Charger in USB module*/
        { "twl6030", TWL6030_CLASS },   /* "Phoenix power chip" */
        { "twl6025", TWL6030_CLASS | TWL6025_SUBCLASS }, /* "Phoenix lite" */
+       { "mpu80031", TWL6030_CLASS | TWL6025_SUBCLASS | MPU80031_SUBCLASS},
        { /* end of list */ },
 };
 MODULE_DEVICE_TABLE(i2c, twl_ids);
index 114c0f6..bb92f0b 100644 (file)
 #define TWL_MODULE_RTC         TWL4030_MODULE_RTC
 #define TWL_MODULE_PWM         TWL4030_MODULE_PWM0
 
+#define TWL6030_MODULE_CHARGER TWL4030_MODULE_MAIN_CHARGE
+#define TWL6025_MODULE_CHARGER 0x18
+
+#define TWL6030_MODULE_GASGAUGE 0x0B
 #define TWL6030_MODULE_ID0     0x0D
 #define TWL6030_MODULE_ID1     0x0E
 #define TWL6030_MODULE_ID2     0x0F
 #define GASGAUGE_INTR_OFFSET   17
 #define USBOTG_INTR_OFFSET     4
 #define CHARGER_INTR_OFFSET    2
+#define GPADCSW_INTR_OFFSET    1
 #define RSV_INTR_OFFSET                0
 
 /* INT register offsets */
@@ -172,6 +177,14 @@ TWL_CLASS_IS(4030, TWL4030_CLASS_ID)
 TWL_CLASS_IS(6030, TWL6030_CLASS_ID)
 
 #define TWL6025_SUBCLASS       BIT(4)  /* TWL6025 has changed registers */
+#define MPU80031_SUBCLASS      BIT(5)  /* MPU80031 has changed registers */
+
+/* So we can recover the features in other parts of twl stack */
+unsigned int twl_features(void);
+
+/* so we can get at the EPROM SMPS OFFSET/MULT stuff */
+u8 twl_get_smps_offset(void);
+u8 twl_get_smps_mult(void);
 
 /*
  * Read and write single 8-bit registers
@@ -179,6 +192,8 @@ TWL_CLASS_IS(6030, TWL6030_CLASS_ID)
 int twl_i2c_write_u8(u8 mod_no, u8 val, u8 reg);
 int twl_i2c_read_u8(u8 mod_no, u8 *val, u8 reg);
 
+void twl_reg_dump(int module, int start, int end);
+
 /*
  * Read and write several 8-bit registers at once.
  *
@@ -215,6 +230,10 @@ static inline int twl6030_mmc_card_detect(struct device *dev, int slot)
        return -EIO;
 }
 #endif
+
+int twl6030_set_usb_charge_enable(int enable);
+int twl6030_set_usb_in_current(int currentmA);
+
 /*----------------------------------------------------------------------*/
 
 /*
@@ -552,11 +571,27 @@ static inline int twl6030_mmc_card_detect(struct device *dev, int slot)
 
 struct twl4030_clock_init_data {
        bool ck32k_lowpwr_enable;
+       bool clk32_active_state_on;
 };
 
 struct twl4030_bci_platform_data {
        int *battery_tmp_tbl;
-       unsigned int tblsize;
+       unsigned int battery_tmp_tblsize;
+       int *battery_volt_tbl;
+       unsigned int battery_volt_tblsize;
+       unsigned int monitoring_interval;
+
+       unsigned int max_charger_currentmA;
+       unsigned int max_charger_voltagemV;
+       unsigned int termination_currentmA;
+
+       unsigned int max_bat_voltagemV;
+       unsigned int low_bat_voltagemV;
+
+       /* twl6025 */
+       unsigned int use_hw_charger;
+       unsigned int use_eeprom_config;
+       unsigned int power_path;
 };
 
 /* TWL4030_GPIO_MAX (18) GPIOs, with interrupts */
@@ -621,6 +656,7 @@ struct twl4030_usb_data {
        int             (*phy_set_clock)(struct device *dev, int on);
        /* suspend/resume of phy */
        int             (*phy_suspend)(struct device *dev, int suspend);
+       int             (*board_control_power)(struct device *dev, int on);
 };
 
 struct twl4030_ins {
@@ -696,6 +732,10 @@ struct twl4030_audio_data {
 
 struct twl4030_platform_data {
        unsigned                                irq_base, irq_end;
+
+       /* Callback for boar regulator initialisation */
+       int (*init)(void);
+
        struct twl4030_clock_init_data          *clock;
        struct twl4030_bci_platform_data        *bci;
        struct twl4030_gpio_platform_data       *gpio;
@@ -822,6 +862,22 @@ static inline int twl4030charger_usb_en(int enable) { return 0; }
 #define TWL6030_REG_VDAC       45
 #define TWL6030_REG_VUSB       46
 
+/* These are renamed in 6025 but same registers */
+#define TWL6025_REG_LDO2       48
+#define TWL6025_REG_LDO4       49
+#define TWL6025_REG_LDO3       50
+#define TWL6025_REG_LDO5       51
+#define TWL6025_REG_LDO1       52
+#define TWL6025_REG_LDO7       53
+#define TWL6025_REG_LDO6       54
+#define TWL6025_REG_LDOLN      55
+#define TWL6025_REG_LDOUSB     56
+
+/* 6025 DCDC supplies */
+#define TWL6025_REG_SMPS3      57
+#define TWL6025_REG_SMPS4      58
+#define TWL6025_REG_VIO                59
+
 /* INTERNAL LDOs */
 #define TWL6030_REG_VRTC       47
 #define TWL6030_REG_CLK32KG    48
diff --git a/include/linux/mfd/max8907c.h b/include/linux/mfd/max8907c.h
new file mode 100644 (file)
index 0000000..76dbdcc
--- /dev/null
@@ -0,0 +1,259 @@
+/* linux/mfd/max8907c.h
+ *
+ * Functions to access MAX8907C power management chip.
+ *
+ * Copyright (C) 2010 Gyungoh Yoo <jack.yoo@maxim-ic.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 __LINUX_MFD_MAX8907C_H
+#define __LINUX_MFD_MAX8907C_H
+
+/* MAX8907C register map */
+#define MAX8907C_REG_SYSENSEL               0x00
+#define MAX8907C_REG_ON_OFF_IRQ1            0x01
+#define MAX8907C_REG_ON_OFF_IRQ1_MASK       0x02
+#define MAX8907C_REG_ON_OFF_STAT            0x03
+#define MAX8907C_REG_SDCTL1                 0x04
+#define MAX8907C_REG_SDSEQCNT1              0x05
+#define MAX8907C_REG_SDV1                   0x06
+#define MAX8907C_REG_SDCTL2                 0x07
+#define MAX8907C_REG_SDSEQCNT2              0x08
+#define MAX8907C_REG_SDV2                   0x09
+#define MAX8907C_REG_SDCTL3                 0x0A
+#define MAX8907C_REG_SDSEQCNT3              0x0B
+#define MAX8907C_REG_SDV3                   0x0C
+#define MAX8907C_REG_ON_OFF_IRQ2            0x0D
+#define MAX8907C_REG_ON_OFF_IRQ2_MASK       0x0E
+#define MAX8907C_REG_RESET_CNFG             0x0F
+#define MAX8907C_REG_LDOCTL16               0x10
+#define MAX8907C_REG_LDOSEQCNT16            0x11
+#define MAX8907C_REG_LDO16VOUT              0x12
+#define MAX8907C_REG_SDBYSEQCNT             0x13
+#define MAX8907C_REG_LDOCTL17               0x14
+#define MAX8907C_REG_LDOSEQCNT17            0x15
+#define MAX8907C_REG_LDO17VOUT              0x16
+#define MAX8907C_REG_LDOCTL1                0x18
+#define MAX8907C_REG_LDOSEQCNT1             0x19
+#define MAX8907C_REG_LDO1VOUT               0x1A
+#define MAX8907C_REG_LDOCTL2                0x1C
+#define MAX8907C_REG_LDOSEQCNT2             0x1D
+#define MAX8907C_REG_LDO2VOUT               0x1E
+#define MAX8907C_REG_LDOCTL3                0x20
+#define MAX8907C_REG_LDOSEQCNT3             0x21
+#define MAX8907C_REG_LDO3VOUT               0x22
+#define MAX8907C_REG_LDOCTL4                0x24
+#define MAX8907C_REG_LDOSEQCNT4             0x25
+#define MAX8907C_REG_LDO4VOUT               0x26
+#define MAX8907C_REG_LDOCTL5                0x28
+#define MAX8907C_REG_LDOSEQCNT5             0x29
+#define MAX8907C_REG_LDO5VOUT               0x2A
+#define MAX8907C_REG_LDOCTL6                0x2C
+#define MAX8907C_REG_LDOSEQCNT6             0x2D
+#define MAX8907C_REG_LDO6VOUT               0x2E
+#define MAX8907C_REG_LDOCTL7                0x30
+#define MAX8907C_REG_LDOSEQCNT7             0x31
+#define MAX8907C_REG_LDO7VOUT               0x32
+#define MAX8907C_REG_LDOCTL8                0x34
+#define MAX8907C_REG_LDOSEQCNT8             0x35
+#define MAX8907C_REG_LDO8VOUT               0x36
+#define MAX8907C_REG_LDOCTL9                0x38
+#define MAX8907C_REG_LDOSEQCNT9             0x39
+#define MAX8907C_REG_LDO9VOUT               0x3A
+#define MAX8907C_REG_LDOCTL10               0x3C
+#define MAX8907C_REG_LDOSEQCNT10            0x3D
+#define MAX8907C_REG_LDO10VOUT              0x3E
+#define MAX8907C_REG_LDOCTL11               0x40
+#define MAX8907C_REG_LDOSEQCNT11            0x41
+#define MAX8907C_REG_LDO11VOUT              0x42
+#define MAX8907C_REG_LDOCTL12               0x44
+#define MAX8907C_REG_LDOSEQCNT12            0x45
+#define MAX8907C_REG_LDO12VOUT              0x46
+#define MAX8907C_REG_LDOCTL13               0x48
+#define MAX8907C_REG_LDOSEQCNT13            0x49
+#define MAX8907C_REG_LDO13VOUT              0x4A
+#define MAX8907C_REG_LDOCTL14               0x4C
+#define MAX8907C_REG_LDOSEQCNT14            0x4D
+#define MAX8907C_REG_LDO14VOUT              0x4E
+#define MAX8907C_REG_LDOCTL15               0x50
+#define MAX8907C_REG_LDOSEQCNT15            0x51
+#define MAX8907C_REG_LDO15VOUT              0x52
+#define MAX8907C_REG_OUT5VEN                0x54
+#define MAX8907C_REG_OUT5VSEQ               0x55
+#define MAX8907C_REG_OUT33VEN               0x58
+#define MAX8907C_REG_OUT33VSEQ              0x59
+#define MAX8907C_REG_LDOCTL19               0x5C
+#define MAX8907C_REG_LDOSEQCNT19            0x5D
+#define MAX8907C_REG_LDO19VOUT              0x5E
+#define MAX8907C_REG_LBCNFG                 0x60
+#define MAX8907C_REG_SEQ1CNFG               0x64
+#define MAX8907C_REG_SEQ2CNFG               0x65
+#define MAX8907C_REG_SEQ3CNFG               0x66
+#define MAX8907C_REG_SEQ4CNFG               0x67
+#define MAX8907C_REG_SEQ5CNFG               0x68
+#define MAX8907C_REG_SEQ6CNFG               0x69
+#define MAX8907C_REG_SEQ7CNFG               0x6A
+#define MAX8907C_REG_LDOCTL18               0x72
+#define MAX8907C_REG_LDOSEQCNT18            0x73
+#define MAX8907C_REG_LDO18VOUT              0x74
+#define MAX8907C_REG_BBAT_CNFG              0x78
+#define MAX8907C_REG_CHG_CNTL1              0x7C
+#define MAX8907C_REG_CHG_CNTL2              0x7D
+#define MAX8907C_REG_CHG_IRQ1               0x7E
+#define MAX8907C_REG_CHG_IRQ2               0x7F
+#define MAX8907C_REG_CHG_IRQ1_MASK          0x80
+#define MAX8907C_REG_CHG_IRQ2_MASK          0x81
+#define MAX8907C_REG_CHG_STAT               0x82
+#define MAX8907C_REG_WLED_MODE_CNTL         0x84
+#define MAX8907C_REG_ILED_CNTL              0x84
+#define MAX8907C_REG_II1RR                  0x8E
+#define MAX8907C_REG_II2RR                  0x8F
+#define MAX8907C_REG_LDOCTL20               0x9C
+#define MAX8907C_REG_LDOSEQCNT20            0x9D
+#define MAX8907C_REG_LDO20VOUT              0x9E
+
+/* RTC register */
+#define MAX8907C_REG_RTC_SEC                0x00
+#define MAX8907C_REG_RTC_MIN                0x01
+#define MAX8907C_REG_RTC_HOURS              0x02
+#define MAX8907C_REG_RTC_WEEKDAY            0x03
+#define MAX8907C_REG_RTC_DATE               0x04
+#define MAX8907C_REG_RTC_MONTH              0x05
+#define MAX8907C_REG_RTC_YEAR1              0x06
+#define MAX8907C_REG_RTC_YEAR2              0x07
+#define MAX8907C_REG_ALARM0_SEC             0x08
+#define MAX8907C_REG_ALARM0_MIN             0x09
+#define MAX8907C_REG_ALARM0_HOURS           0x0A
+#define MAX8907C_REG_ALARM0_WEEKDAY         0x0B
+#define MAX8907C_REG_ALARM0_DATE            0x0C
+#define MAX8907C_REG_ALARM0_MONTH           0x0D
+#define MAX8907C_REG_ALARM0_YEAR1           0x0E
+#define MAX8907C_REG_ALARM0_YEAR2           0x0F
+#define MAX8907C_REG_ALARM1_SEC             0x10
+#define MAX8907C_REG_ALARM1_MIN             0x11
+#define MAX8907C_REG_ALARM1_HOURS           0x12
+#define MAX8907C_REG_ALARM1_WEEKDAY         0x13
+#define MAX8907C_REG_ALARM1_DATE            0x14
+#define MAX8907C_REG_ALARM1_MONTH           0x15
+#define MAX8907C_REG_ALARM1_YEAR1           0x16
+#define MAX8907C_REG_ALARM1_YEAR2           0x17
+#define MAX8907C_REG_ALARM0_CNTL            0x18
+#define MAX8907C_REG_ALARM1_CNTL            0x19
+#define MAX8907C_REG_RTC_STATUS             0x1A
+#define MAX8907C_REG_RTC_CNTL               0x1B
+#define MAX8907C_REG_RTC_IRQ                0x1C
+#define MAX8907C_REG_RTC_IRQ_MASK           0x1D
+#define MAX8907C_REG_MPL_CNTL               0x1E
+
+/* ADC and Touch Screen Controller register map */
+
+#define MAX8907C_CTL     0
+#define MAX8907C_SEQCNT  1
+#define MAX8907C_VOUT    2
+
+/* mask bit fields */
+#define MAX8907C_MASK_LDO_SEQ           0x1C
+#define MAX8907C_MASK_LDO_EN            0x01
+#define MAX8907C_MASK_VBBATTCV          0x03
+#define MAX8907C_MASK_OUT5V_VINEN       0x10
+#define MAX8907C_MASK_OUT5V_ENSRC       0x0E
+#define MAX8907C_MASK_OUT5V_EN          0x01
+
+/* Power off bit in RESET_CNFG reg */
+#define MAX8907C_MASK_POWER_OFF                0x40
+
+#define MAX8907C_MASK_PWR_EN           0x80
+#define MAX8907C_MASK_CTL_SEQ          0x1C
+
+#define MAX8907C_PWR_EN                        0x80
+#define MAX8907C_CTL_SEQ               0x04
+
+#define MAX8907C_SD_SEQ1               0x02
+#define MAX8907C_SD_SEQ2               0x06
+
+#define MAX8907C_DELAY_CNT0            0x00
+
+#define MAX8907C_POWER_UP_DELAY_CNT1   0x10
+#define MAX8907C_POWER_UP_DELAY_CNT12  0xC0
+
+#define MAX8907C_POWER_DOWN_DELAY_CNT12        0x0C
+
+#define RTC_I2C_ADDR                   0x68
+
+/*
+ * MAX8907B revision requires s/w WAR to connect PWREN input to
+ * sequencer 2 because of the bug in the silicon.
+ */
+#define MAX8907B_II2RR_PWREN_WAR               (0x12)
+
+/* Defines common for all supplies PWREN  sequencer selection */
+#define MAX8907B_SEQSEL_PWREN_LXX              1 /* SEQ2 (PWREN) */
+
+/* IRQ definitions */
+enum {
+       MAX8907C_IRQ_VCHG_DC_OVP,
+       MAX8907C_IRQ_VCHG_DC_F,
+       MAX8907C_IRQ_VCHG_DC_R,
+       MAX8907C_IRQ_VCHG_THM_OK_R,
+       MAX8907C_IRQ_VCHG_THM_OK_F,
+       MAX8907C_IRQ_VCHG_MBATTLOW_F,
+       MAX8907C_IRQ_VCHG_MBATTLOW_R,
+       MAX8907C_IRQ_VCHG_RST,
+       MAX8907C_IRQ_VCHG_DONE,
+       MAX8907C_IRQ_VCHG_TOPOFF,
+       MAX8907C_IRQ_VCHG_TMR_FAULT,
+       MAX8907C_IRQ_GPM_RSTIN,
+       MAX8907C_IRQ_GPM_MPL,
+       MAX8907C_IRQ_GPM_SW_3SEC,
+       MAX8907C_IRQ_GPM_EXTON_F,
+       MAX8907C_IRQ_GPM_EXTON_R,
+       MAX8907C_IRQ_GPM_SW_1SEC,
+       MAX8907C_IRQ_GPM_SW_F,
+       MAX8907C_IRQ_GPM_SW_R,
+       MAX8907C_IRQ_GPM_SYSCKEN_F,
+       MAX8907C_IRQ_GPM_SYSCKEN_R,
+       MAX8907C_IRQ_RTC_ALARM1,
+       MAX8907C_IRQ_RTC_ALARM0,
+       MAX8907C_NR_IRQS,
+};
+
+struct max8907c {
+       struct device           *dev;
+       struct mutex            io_lock;
+       struct mutex            irq_lock;
+       struct i2c_client       *i2c_power;
+       struct i2c_client       *i2c_rtc;
+       int                     irq_base;
+       int                     core_irq;
+
+       unsigned char           cache_chg[2];
+       unsigned char           cache_on[2];
+       unsigned char           cache_rtc;
+
+};
+
+struct max8907c_platform_data {
+       int num_subdevs;
+       struct platform_device **subdevs;
+       int irq_base;
+       int (*max8907c_setup)(void);
+};
+
+int max8907c_reg_read(struct i2c_client *i2c, u8 reg);
+int max8907c_reg_bulk_read(struct i2c_client *i2c, u8 reg, u8 count, u8 *val);
+int max8907c_reg_write(struct i2c_client *i2c, u8 reg, u8 val);
+int max8907c_reg_bulk_write(struct i2c_client *i2c, u8 reg, u8 count, u8 *val);
+int max8907c_set_bits(struct i2c_client *i2c, u8 reg, u8 mask, u8 val);
+
+int max8907c_irq_init(struct max8907c *chip, int irq, int irq_base);
+void max8907c_irq_free(struct max8907c *chip);
+int max8907c_suspend(struct i2c_client *i2c, pm_message_t state);
+int max8907c_resume(struct i2c_client *i2c);
+int max8907c_power_off(void);
+void max8907c_deep_sleep(int enter);
+int max8907c_pwr_en_config(void);
+int max8907c_pwr_en_attach(void);
+#endif
index d96fb3d..6d6ddc9 100644 (file)
@@ -1,6 +1,10 @@
 #ifndef __LINUX_MFD_TPS6586X_H
 #define __LINUX_MFD_TPS6586X_H
 
+#define SM0_PWM_BIT 0
+#define SM1_PWM_BIT 1
+#define SM2_PWM_BIT 2
+
 enum {
        TPS6586X_ID_SM_0,
        TPS6586X_ID_SM_1,
@@ -48,14 +52,43 @@ enum {
        TPS6586X_INT_RTC_ALM2,
 };
 
+enum pwm_pfm_mode {
+       PWM_ONLY,
+       AUTO_PWM_PFM,
+       NOT_CONFIGURABLE
+};
+
+struct tps6586x_settings {
+       /* SM0, SM1 and SM2 have PWM-only and auto PWM/PFM mode */
+       enum pwm_pfm_mode sm_pwm_mode;
+};
+
+enum {
+       TPS6586X_RTC_CL_SEL_1_5PF  = 0x0,
+       TPS6586X_RTC_CL_SEL_6_5PF  = 0x1,
+       TPS6586X_RTC_CL_SEL_7_5PF  = 0x2,
+       TPS6586X_RTC_CL_SEL_12_5PF = 0x3,
+};
+
 struct tps6586x_subdev_info {
        int             id;
        const char      *name;
        void            *platform_data;
 };
 
+struct tps6586x_epoch_start {
+       int year;
+       int month;
+       int day;
+       int hour;
+       int min;
+       int sec;
+};
+
 struct tps6586x_rtc_platform_data {
        int irq;
+       struct tps6586x_epoch_start start;
+       int cl_sel; /* internal XTAL capacitance, see TPS6586X_RTC_CL_SEL* */
 };
 
 struct tps6586x_platform_data {
@@ -78,5 +111,6 @@ extern int tps6586x_set_bits(struct device *dev, int reg, uint8_t bit_mask);
 extern int tps6586x_clr_bits(struct device *dev, int reg, uint8_t bit_mask);
 extern int tps6586x_update(struct device *dev, int reg, uint8_t val,
                           uint8_t mask);
+extern int tps6586x_power_off(void);
 
 #endif /*__LINUX_MFD_TPS6586X_H */
diff --git a/include/linux/mfd/tps6591x.h b/include/linux/mfd/tps6591x.h
new file mode 100644 (file)
index 0000000..2ecbedb
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * include/linux/mfd/tps6591x.c
+ * Core driver interface for TI TPS6591x PMIC family
+ *
+ * Copyright (C) 2011 NVIDIA Corporation
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ */
+
+#ifndef __LINUX_MFD_TPS6591X_H
+#define __LINUX_MFD_TPS6591X_H
+
+#include <linux/rtc.h>
+
+#define tps6591x_rails(_name) "tps6591x_"#_name
+
+enum {
+       TPS6591X_ID_VIO,
+       TPS6591X_ID_VDD_1,
+       TPS6591X_ID_VDD_2,
+       TPS6591X_ID_VDDCTRL,
+       TPS6591X_ID_LDO_1,
+       TPS6591X_ID_LDO_2,
+       TPS6591X_ID_LDO_3,
+       TPS6591X_ID_LDO_4,
+       TPS6591X_ID_LDO_5,
+       TPS6591X_ID_LDO_6,
+       TPS6591X_ID_LDO_7,
+       TPS6591X_ID_LDO_8,
+};
+
+enum {
+       TPS6591X_INT_PWRHOLD_F,
+       TPS6591X_INT_VMBHI,
+       TPS6591X_INT_PWRON,
+       TPS6591X_INT_PWRON_LP,
+       TPS6591X_INT_PWRHOLD_R,
+       TPS6591X_INT_HOTDIE,
+       TPS6591X_INT_RTC_ALARM,
+       TPS6591X_INT_RTC_PERIOD,
+       TPS6591X_INT_GPIO0,
+       TPS6591X_INT_GPIO1,
+       TPS6591X_INT_GPIO2,
+       TPS6591X_INT_GPIO3,
+       TPS6591X_INT_GPIO4,
+       TPS6591X_INT_GPIO5,
+       TPS6591X_INT_WTCHDG,
+       TPS6591X_INT_VMBCH2_H,
+       TPS6591X_INT_VMBCH2_L,
+       TPS6591X_INT_PWRDN,
+};
+
+struct tps6591x_subdev_info {
+       int             id;
+       const char      *name;
+       void            *platform_data;
+};
+
+struct tps6591x_rtc_platform_data {
+       int irq;
+       struct rtc_time time;
+};
+
+struct tps6591x_sleep_keepon_data {
+       /* set 1 to maintain the following on sleep mode */
+       unsigned therm_keepon:1;        /* themal monitoring */
+       unsigned clkout32k_keepon:1;    /* CLK32KOUT */
+       unsigned vrtc_keepon:1;         /* LD0 full load capability */
+       unsigned i2chs_keepon:1;        /* high speed internal clock */
+};
+
+struct tps6591x_gpio_init_data {
+       unsigned sleep_en:1;    /* Enable sleep mode */
+       unsigned pulldn_en:1;   /* Enable pull down */
+       unsigned output_mode_en:1; /* Enable output mode during init */
+       unsigned output_val:1;  /* Output value if it is in output mode */
+       unsigned init_apply:1;  /* Apply init data on configuring gpios*/
+};
+
+struct tps6591x_platform_data {
+       int gpio_base;
+       int irq_base;
+
+       int num_subdevs;
+       struct tps6591x_subdev_info *subdevs;
+
+       bool dev_slp_en;
+       struct tps6591x_sleep_keepon_data *slp_keepon;
+
+       struct tps6591x_gpio_init_data *gpio_init_data;
+       int num_gpioinit_data;
+};
+
+/*
+ * NOTE: the functions below are not intended for use outside
+ * of the TPS6591X sub-device drivers
+ */
+extern int tps6591x_write(struct device *dev, int reg, uint8_t val);
+extern int tps6591x_writes(struct device *dev, int reg, int len, uint8_t *val);
+extern int tps6591x_read(struct device *dev, int reg, uint8_t *val);
+extern int tps6591x_reads(struct device *dev, int reg, int len, uint8_t *val);
+extern int tps6591x_set_bits(struct device *dev, int reg, uint8_t bit_mask);
+extern int tps6591x_clr_bits(struct device *dev, int reg, uint8_t bit_mask);
+extern int tps6591x_update(struct device *dev, int reg, uint8_t val,
+                          uint8_t mask);
+extern int tps6591x_power_off(void);
+
+#endif /*__LINUX_MFD_TPS6591X_H */
diff --git a/include/linux/mfd/tps80031.h b/include/linux/mfd/tps80031.h
new file mode 100644 (file)
index 0000000..9438b46
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ * include/linux/mfd/tps80031.c
+ *
+ * Core driver interface for TI TPS80031 PMIC
+ *
+ * Copyright (C) 2011 NVIDIA Corporation
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ */
+
+#ifndef __LINUX_MFD_TPS80031_H
+#define __LINUX_MFD_TPS80031_H
+
+#define tps80031_rails(_name) "tps80031_"#_name
+
+enum {
+       TPS80031_ID_VIO,
+       TPS80031_ID_SMPS1,
+       TPS80031_ID_SMPS2,
+       TPS80031_ID_SMPS3,
+       TPS80031_ID_SMPS4,
+       TPS80031_ID_VANA,
+       TPS80031_ID_LDO1,
+       TPS80031_ID_LDO2,
+       TPS80031_ID_LDO3,
+       TPS80031_ID_LDO4,
+       TPS80031_ID_LDO5,
+       TPS80031_ID_LDO6,
+       TPS80031_ID_LDO7,
+       TPS80031_ID_LDOLN,
+       TPS80031_ID_LDOUSB,
+};
+
+enum {
+       TPS80031_INT_PWRON,
+       TPS80031_INT_RPWRON,
+       TPS80031_INT_SYS_VLOW,
+       TPS80031_INT_RTC_ALARM,
+       TPS80031_INT_RTC_PERIOD,
+       TPS80031_INT_HOT_DIE,
+       TPS80031_INT_VXX_SHORT,
+       TPS80031_INT_SPDURATION,
+       TPS80031_INT_WATCHDOG,
+       TPS80031_INT_BAT,
+       TPS80031_INT_SIM,
+       TPS80031_INT_MMC,
+       TPS80031_INT_RES,
+       TPS80031_INT_GPADC_RT,
+       TPS80031_INT_GPADC_SW2_EOC,
+       TPS80031_INT_CC_AUTOCAL,
+       TPS80031_INT_ID_WKUP,
+       TPS80031_INT_VBUSS_WKUP,
+       TPS80031_INT_ID,
+       TPS80031_INT_VBUS,
+       TPS80031_INT_CHRG_CTRL,
+       TPS80031_INT_EXT_CHRG,
+       TPS80031_INT_INT_CHRG,
+       TPS80031_INT_RES2,
+};
+
+enum {
+       SLAVE_ID0 = 0,
+       SLAVE_ID1 = 1,
+       SLAVE_ID2 = 2,
+       SLAVE_ID3 = 3,
+};
+
+enum {
+       I2C_ID0_ADDR = 0x12,
+       I2C_ID1_ADDR = 0x48,
+       I2C_ID2_ADDR = 0x49,
+       I2C_ID3_ADDR = 0x4A,
+};
+
+struct tps80031_subdev_info {
+       int             id;
+       const char      *name;
+       void            *platform_data;
+};
+
+struct tps80031_32kclock_plat_data {
+       unsigned en_clk32kao:1;
+       unsigned en_clk32kg:1;
+       unsigned en_clk32kaudio:1;
+};
+
+struct tps80031_platform_data {
+       int num_subdevs;
+       struct tps80031_subdev_info *subdevs;
+       int gpio_base;
+       int irq_base;
+       struct tps80031_32kclock_plat_data *clk32k_pdata;
+};
+
+/*
+ * NOTE: the functions below are not intended for use outside
+ * of the TPS80031 sub-device drivers
+ */
+extern int tps80031_write(struct device *dev, int sid, int reg, uint8_t val);
+extern int tps80031_writes(struct device *dev, int sid, int reg, int len,
+                               uint8_t *val);
+extern int tps80031_read(struct device *dev, int sid, int reg, uint8_t *val);
+extern int tps80031_reads(struct device *dev, int sid, int reg, int len,
+                               uint8_t *val);
+extern int tps80031_set_bits(struct device *dev, int sid, int reg,
+                               uint8_t bit_mask);
+extern int tps80031_clr_bits(struct device *dev, int sid, int reg,
+                               uint8_t bit_mask);
+extern int tps80031_update(struct device *dev, int sid, int reg, uint8_t val,
+                          uint8_t mask);
+extern int tps80031_power_off(void);
+
+#endif /*__LINUX_MFD_TPS80031_H */