rk30:phone:add pmu tps80032
author张晴 <zhangqing@rock-chips.com>
Wed, 2 May 2012 08:39:17 +0000 (16:39 +0800)
committer张晴 <zhangqing@rock-chips.com>
Wed, 2 May 2012 08:39:17 +0000 (16:39 +0800)
12 files changed:
arch/arm/mach-rk30/board-rk30-phone-loquat.c
arch/arm/mach-rk30/board-rk30-sdk-twl80032.c [new file with mode: 0755]
drivers/mfd/twl-core.c [changed mode: 0644->0755]
drivers/mfd/twl-core.h
drivers/mfd/twl6030-gpadc.c [new file with mode: 0644]
drivers/mfd/twl6030-irq.c
drivers/mfd/twl6030-madc.c [new file with mode: 0644]
drivers/mfd/twl6030-power.c [new file with mode: 0644]
drivers/mfd/twl6030-poweroff.c [new file with mode: 0644]
drivers/regulator/twl-regulator.c [changed mode: 0644->0755]
drivers/rtc/rtc-twl.c
include/linux/i2c/twl.h [changed mode: 0644->0755]

index 0fa69513d993dc52ff8f933fbab0227f1f35aaba..b34cb32c0e909d84b5c025836c5849f805e24353 100755 (executable)
@@ -1282,7 +1282,7 @@ static struct i2c_board_info __initdata i2c0_info[] = {
 
 #ifdef CONFIG_I2C1_RK30
 #include "board-rk30-sdk-wm8326.c"
-
+#include "board-rk30-sdk-twl80032.c"
 static struct i2c_board_info __initdata i2c1_info[] = {
 #if defined (CONFIG_MFD_WM831X_I2C)
        {
@@ -1293,6 +1293,17 @@ static struct i2c_board_info __initdata i2c1_info[] = {
                .platform_data = &wm831x_platdata,
        },
 #endif
+#if defined (CONFIG_TWL4030_CORE)
+       {
+               .type = "twl6032",
+               .addr = 0x48,
+               .flags = 0,
+               .irq            = RK30_PIN6_PA4, 
+               .platform_data = &tps80032_data,
+
+       },
+
+#endif
 };
 #endif
 
@@ -1320,7 +1331,10 @@ static struct i2c_board_info __initdata i2c2_info[] = {
 #endif
 
 #ifdef CONFIG_I2C3_RK30
+
 static struct i2c_board_info __initdata i2c3_info[] = {
+
+
 };
 #endif
 
diff --git a/arch/arm/mach-rk30/board-rk30-sdk-twl80032.c b/arch/arm/mach-rk30/board-rk30-sdk-twl80032.c
new file mode 100755 (executable)
index 0000000..e0c9989
--- /dev/null
@@ -0,0 +1,496 @@
+#include <linux/regulator/machine.h>
+#include <linux/i2c/twl.h>
+
+#include <mach/sram.h>
+
+#ifdef CONFIG_TWL4030_CORE
+#define VREG_VOLTAGE           3
+#define VREG_VOLTAGE_DVS_SMPS 3 
+static inline int twl_reg_read(unsigned base, unsigned slave_subgp, unsigned offset)
+{
+       u8 value;
+       int status;
+       status = twl_i2c_read_u8(slave_subgp,&value, base + offset);
+       return (status < 0) ? status : value;
+}
+
+
+static inline int twl_reg_write(unsigned base, unsigned slave_subgp, unsigned offset,
+                                                u8 value)
+{
+       return twl_i2c_write_u8(slave_subgp,value, base + offset);
+}
+
+int tps80032_pre_init(void){
+       int ret;
+       u8 value;
+       printk("--------------------\n");
+       printk("%s\n", __func__);
+
+       return 0;
+
+}
+int tps80032_set_init(void)
+{
+       struct regulator *dcdc;
+       struct regulator *ldo;
+       printk("++++++++++++++++++++++++++++++++++++++\n");
+       printk("%s\n", __func__);
+       
+       ldo = regulator_get(NULL, "tps_ldo1");  //vcca_33
+       regulator_set_voltage(ldo, 3300000, 3300000);
+       regulator_enable(ldo);
+       printk("%s set ldo1 vcca_33=%dmV end\n", __func__, regulator_get_voltage(ldo));
+       regulator_put(ldo);
+       udelay(100);
+       
+       ldo = regulator_get(NULL, "tps_ldo4");  // vdd_11
+       regulator_set_voltage(ldo, 1100000, 1100000);
+       regulator_enable(ldo);
+       printk("%s set ldo4 vdd_11=%dmV end\n", __func__, regulator_get_voltage(ldo));
+       regulator_put(ldo);
+       udelay(100);
+
+       dcdc = regulator_get(NULL, "tps_smps4");
+       regulator_set_voltage(dcdc,3000000,3000000);
+       regulator_enable(dcdc); 
+       printk("%s set dcdc4 vcc_io=%dmV end\n", __func__, regulator_get_voltage(dcdc));
+       regulator_put(dcdc);
+       udelay(100);
+
+       ldo = regulator_get(NULL, "tps_ldo2");  // vdd_usb11
+       regulator_set_voltage(ldo, 1100000, 1100000);
+       regulator_enable(ldo);
+       printk("%s set ldo2 vdd_usb11=%dmV end\n", __func__, regulator_get_voltage(ldo));
+       regulator_put(ldo);
+       udelay(100);
+
+       ldo = regulator_get(NULL, "tps_ldo5");  // vcc_25
+       regulator_set_voltage(ldo, 2500000, 2500000);
+       regulator_enable(ldo);
+       printk("%s set ldo5 vcc_25=%dmV end\n", __func__, regulator_get_voltage(ldo));
+       regulator_put(ldo);
+       udelay(100);
+
+       ldo = regulator_get(NULL, "tps_ldousb");        // vcc_usb33
+       regulator_set_voltage(ldo, 3300000, 3300000);
+       regulator_enable(ldo);
+       printk("%s set ldousb vcc_usb33=%dmV end\n", __func__, regulator_get_voltage(ldo));
+       regulator_put(ldo);
+       udelay(100);
+
+       dcdc = regulator_get(NULL, "tps_smps1"); // vdd_arm
+       regulator_set_voltage(dcdc,1100000,1100000);
+       regulator_enable(dcdc); 
+       printk("%s set dcdc1 vdd_cpu=%dmV end\n", __func__, regulator_get_voltage(dcdc));
+       regulator_put(dcdc);
+       udelay(100);
+
+       dcdc = regulator_get(NULL, "tps_smps2");  //vdd_log
+       regulator_set_voltage(dcdc,1100000,1100000);
+       regulator_enable(dcdc); 
+       printk("%s set dcdc2 vdd_core=%dmV end\n", __func__, regulator_get_voltage(dcdc));
+       regulator_put(dcdc);
+       udelay(100);
+
+       dcdc = regulator_get(NULL, "tps_smps3");  //vcc_ddr
+       regulator_set_voltage(dcdc,1500000,1500000);
+       regulator_enable(dcdc); 
+       printk("%s set dcdc3 vcc_ddr=%dmV end\n", __func__, regulator_get_voltage(dcdc));
+       regulator_put(dcdc);
+       udelay(100);
+/*
+       dcdc = regulator_get(NULL, "tps_smps5");
+       regulator_set_voltage(dcdc,1800000,1800000);
+//     regulator_set_suspend_voltage(dcdc, 1800000);
+       regulator_enable(dcdc); 
+       printk("%s set dcdc5 vcc_lpddr2_1v8=%dmV end\n", __func__, regulator_get_voltage(dcdc));
+       regulator_put(dcdc);
+       udelay(100);
+*/
+       
+       ldo = regulator_get(NULL, "tps_ldo3");  //vcc_nandflash
+       regulator_set_voltage(ldo, 3300000, 3300000);
+       regulator_enable(ldo);
+       printk("%s set ldo3 vcc_nandflash=%dmV end\n", __func__, regulator_get_voltage(ldo));
+       regulator_put(ldo);
+       udelay(100);
+       
+       ldo = regulator_get(NULL, "tps_ldo6");  //codecvdd_1v8
+       regulator_set_voltage(ldo, 1800000, 1800000);
+       regulator_enable(ldo);
+       printk("%s set ldo6 codecvdd_1v8=%dmV end\n", __func__, regulator_get_voltage(ldo));
+       regulator_put(ldo);
+       udelay(100);
+
+       ldo = regulator_get(NULL, "tps_ldo7");  //vcc_lcd
+       regulator_set_voltage(ldo, 3000000, 3000000);
+       regulator_enable(ldo);
+       printk("%s set ldo7 vcc_lcd=%dmV end\n", __func__, regulator_get_voltage(ldo));
+       regulator_put(ldo);
+       udelay(100);
+
+       ldo = regulator_get(NULL, "tps_ldoln");   //vcccodec_io
+       regulator_set_voltage(ldo, 3300000, 3300000);
+       regulator_enable(ldo);
+       printk("%s set ldoln vcccodec_io=%dmV end\n", __func__, regulator_get_voltage(ldo));
+       regulator_put(ldo);
+       udelay(100);
+/*
+       ldo = regulator_get(NULL, "tps_vana");  //vana_out
+       regulator_set_voltage(ldo, 2500000, 2500000);
+//     regulator_set_suspend_voltage(ldo, 2500000);
+       regulator_enable(ldo);
+       printk("%s set vana vana_out=%dmV end\n", __func__, regulator_get_voltage(ldo));
+       regulator_put(ldo);
+       udelay(100);
+*/
+       return 0;
+}
+
+
+static struct regulator_consumer_supply tps80032_smps1_supply[] = {
+       {
+               .supply = "tps_smps1",
+       },
+};
+static struct regulator_consumer_supply tps80032_smps2_supply[] = {
+       {
+               .supply = "tps_smps2",
+       },
+};
+static struct regulator_consumer_supply tps80032_smps3_supply[] = {
+       {
+               .supply = "tps_smps3",
+       },
+};
+static struct regulator_consumer_supply tps80032_smps4_supply[] = {
+       {
+               .supply = "tps_smps4",
+       },
+};
+static struct regulator_consumer_supply tps80032_smps5_supply[] = {
+       {
+               .supply = "tps_smps5",
+       },
+};
+static struct regulator_consumer_supply tps80032_ldo1_supply[] = {
+       {
+               .supply = "tps_ldo1",
+       },
+};
+static struct regulator_consumer_supply tps80032_ldo2_supply[] = {
+       {
+               .supply = "tps_ldo2",
+       },
+};
+
+static struct regulator_consumer_supply tps80032_ldo3_supply[] = {
+       {
+               .supply = "tps_ldo3",
+       },
+};
+static struct regulator_consumer_supply tps80032_ldo4_supply[] = {
+       {
+               .supply = "tps_ldo4",
+       },
+};
+static struct regulator_consumer_supply tps80032_ldo5_supply[] = {
+       {
+               .supply = "tps_ldo5",
+       },
+};
+static struct regulator_consumer_supply tps80032_ldo6_supply[] = {
+       {
+               .supply = "tps_ldo6",
+       },
+};
+static struct regulator_consumer_supply tps80032_ldo7_supply[] = {
+       {
+               .supply = "tps_ldo7",
+       },
+};
+
+static struct regulator_consumer_supply tps80032_ldoln_supply[] = {
+       {
+               .supply = "tps_ldoln",
+       },
+};
+static struct regulator_consumer_supply tps80032_ldousb_supply[] = {
+       {
+               .supply = "tps_ldousb",
+       },
+};
+static struct regulator_consumer_supply tps80032_ldovana_supply[] = {
+       {
+               .supply = "tps_vana",
+       },
+};
+/* */
+static struct regulator_init_data tps80032_smps1 = {
+       .constraints = {
+               .name           = "TPS_SMPS1",
+               .min_uV                 = 600000,
+               .max_uV                 = 2100000,
+               .apply_uV               = 1,
+               
+               .valid_ops_mask = REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_MODE,
+               .valid_modes_mask = REGULATOR_MODE_STANDBY | REGULATOR_MODE_NORMAL,
+
+       },
+       .num_consumer_supplies = ARRAY_SIZE(tps80032_smps1_supply),
+       .consumer_supplies =  tps80032_smps1_supply,
+};
+
+/* */
+static struct regulator_init_data tps80032_smps2 = {
+       .constraints = {
+               .name           = "TPS_SMPS2",
+               .min_uV                 = 600000,
+               .max_uV                 = 2100000,
+               .apply_uV               = 1,
+               
+               .valid_ops_mask = REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_MODE,
+               .valid_modes_mask = REGULATOR_MODE_STANDBY | REGULATOR_MODE_NORMAL,
+
+       },
+       .num_consumer_supplies = ARRAY_SIZE(tps80032_smps2_supply),
+       .consumer_supplies =  tps80032_smps2_supply,
+};
+
+
+
+/* */
+static struct regulator_init_data tps80032_smps3 = {
+       .constraints = {
+               .name           = "TPS_SMPS3",
+               .min_uV                 = 600000,
+               .max_uV                 = 2100000,
+               .apply_uV               = 1,
+               
+               .valid_ops_mask = REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_MODE,
+               .valid_modes_mask = REGULATOR_MODE_STANDBY | REGULATOR_MODE_NORMAL,
+
+       },
+       .num_consumer_supplies = ARRAY_SIZE(tps80032_smps3_supply),
+       .consumer_supplies =  tps80032_smps3_supply,
+};
+
+
+/* */
+static struct regulator_init_data tps80032_smps4 = {
+       .constraints = {
+               .name           = "TPS_SMPS4",
+               .min_uV                 = 600000,
+               .max_uV                 = 2100000,
+               .apply_uV               = 1,
+               
+               .valid_ops_mask = REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_MODE,
+               .valid_modes_mask = REGULATOR_MODE_STANDBY | REGULATOR_MODE_NORMAL,
+
+       },
+       .num_consumer_supplies = ARRAY_SIZE(tps80032_smps4_supply),
+       .consumer_supplies =  tps80032_smps4_supply,
+};
+/* */
+static struct regulator_init_data tps80032_smps5 = {
+       .constraints = {
+               .name           = "TPS_SMPS5",
+               .min_uV                 = 600000,
+               .max_uV                 = 2100000,
+               .apply_uV               = 1,
+               
+               .valid_ops_mask = REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_MODE,
+               .valid_modes_mask = REGULATOR_MODE_STANDBY | REGULATOR_MODE_NORMAL,
+
+       },
+       .num_consumer_supplies = ARRAY_SIZE(tps80032_smps5_supply),
+       .consumer_supplies =  tps80032_smps5_supply,
+};
+static struct regulator_init_data tps80032_ldo1 = {
+       .constraints = {
+               .name           = "TPS_LDO1",
+               .min_uV                 = 1200000,
+               .max_uV                 = 3000000,
+               .apply_uV               = 1,
+               
+               .valid_ops_mask = REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_MODE,
+               .valid_modes_mask = REGULATOR_MODE_STANDBY | REGULATOR_MODE_NORMAL,
+
+       },
+       .num_consumer_supplies = ARRAY_SIZE(tps80032_ldo1_supply),
+       .consumer_supplies =  tps80032_ldo1_supply,
+};
+
+/* */
+static struct regulator_init_data tps80032_ldo2 = {
+       .constraints = {
+               .name           = "TPS_LDO2",
+               .min_uV                 = 1200000,
+               .max_uV                 = 3000000,
+               .apply_uV               = 1,
+               
+               .valid_ops_mask = REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_MODE,
+               .valid_modes_mask = REGULATOR_MODE_STANDBY | REGULATOR_MODE_NORMAL,
+
+       },
+       .num_consumer_supplies = ARRAY_SIZE(tps80032_ldo2_supply),
+       .consumer_supplies =  tps80032_ldo2_supply,
+};
+
+/* */
+static struct regulator_init_data tps80032_ldo3 = {
+       .constraints = {
+               .name           = "TPS_LDO3",
+               .min_uV                 = 1200000,
+               .max_uV                 = 3000000,
+               .apply_uV               = 1,
+               
+               .valid_ops_mask = REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_MODE,
+               .valid_modes_mask = REGULATOR_MODE_STANDBY | REGULATOR_MODE_NORMAL,
+
+       },
+       .num_consumer_supplies = ARRAY_SIZE(tps80032_ldo3_supply),
+       .consumer_supplies =  tps80032_ldo3_supply,
+};
+
+/* */
+static struct regulator_init_data tps80032_ldo4 = {
+       .constraints = {
+               .name           = "TPS_LDO4",
+               .min_uV                 = 1200000,
+               .max_uV                 = 3000000,
+               .apply_uV               = 1,
+               
+               .valid_ops_mask = REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_MODE,
+               .valid_modes_mask = REGULATOR_MODE_STANDBY | REGULATOR_MODE_NORMAL,
+
+       },
+       .num_consumer_supplies = ARRAY_SIZE(tps80032_ldo4_supply),
+       .consumer_supplies =  tps80032_ldo4_supply,
+};
+
+/* */
+static struct regulator_init_data tps80032_ldo5 = {
+       .constraints = {
+               .name           = "TPS_LDO5",
+               .min_uV                 = 1200000,
+               .max_uV                 = 3000000,
+               .apply_uV               = 1,
+               
+               .valid_ops_mask = REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_MODE,
+               .valid_modes_mask = REGULATOR_MODE_STANDBY | REGULATOR_MODE_NORMAL,
+
+       },
+       .num_consumer_supplies = ARRAY_SIZE(tps80032_ldo5_supply),
+       .consumer_supplies =  tps80032_ldo5_supply,
+};
+
+/* */
+static struct regulator_init_data tps80032_ldo6 = {
+       .constraints = {
+               .name           = "TPS_LDO6",
+               .min_uV                 = 1200000,
+               .max_uV                 = 3000000,
+               .apply_uV               = 1,
+               
+               .valid_ops_mask = REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_MODE,
+               .valid_modes_mask = REGULATOR_MODE_STANDBY | REGULATOR_MODE_NORMAL,
+
+       },
+       .num_consumer_supplies = ARRAY_SIZE(tps80032_ldo6_supply),
+       .consumer_supplies =  tps80032_ldo6_supply,
+};
+
+/* */
+static struct regulator_init_data tps80032_ldo7 = {
+       .constraints = {
+               .name           = "TPS_LDO7",
+               .min_uV                 = 1200000,
+               .max_uV                 = 3000000,
+               .apply_uV               = 1,
+               
+               .valid_ops_mask = REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_MODE,
+               .valid_modes_mask = REGULATOR_MODE_STANDBY | REGULATOR_MODE_NORMAL,
+
+       },
+       .num_consumer_supplies = ARRAY_SIZE(tps80032_ldo7_supply),
+       .consumer_supplies =  tps80032_ldo7_supply,
+};
+
+/* */
+static struct regulator_init_data tps80032_ldoln = {
+       .constraints = {
+               .name           = "TPS_LDOLN",
+               .min_uV                 = 1200000,
+               .max_uV                 = 3000000,
+               .apply_uV               = 1,
+               
+               .valid_ops_mask = REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_MODE,
+               .valid_modes_mask = REGULATOR_MODE_STANDBY | REGULATOR_MODE_NORMAL,
+
+       },
+       .num_consumer_supplies = ARRAY_SIZE(tps80032_ldoln_supply),
+       .consumer_supplies =  tps80032_ldoln_supply,
+};
+
+/* */
+static struct regulator_init_data tps80032_ldousb = {
+       .constraints = {
+               .name           = "TPS_LDOUSB",
+               .min_uV                 = 3300000,
+               .max_uV                 = 3300000,
+               .apply_uV               = 1,
+               
+               .valid_ops_mask = REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_MODE,
+               .valid_modes_mask = REGULATOR_MODE_STANDBY | REGULATOR_MODE_NORMAL,
+
+       },
+       .num_consumer_supplies = ARRAY_SIZE(tps80032_ldousb_supply),
+       .consumer_supplies =  tps80032_ldousb_supply,
+};
+
+/* */
+static struct regulator_init_data tps80032_ldovana = {
+       .constraints = {
+               .name           = "TPS_LDOVANA",
+               .min_uV                 = 600000,
+               .max_uV                 = 2500000,
+               .apply_uV               = 1,
+               
+               .valid_ops_mask = REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_MODE,
+               .valid_modes_mask = REGULATOR_MODE_STANDBY | REGULATOR_MODE_NORMAL,
+
+       },
+       .num_consumer_supplies = ARRAY_SIZE(tps80032_ldovana_supply),
+       .consumer_supplies =  tps80032_ldovana_supply,
+};
+
+static struct twl4030_platform_data tps80032_data = {
+       .irq_base       = RK30_PIN0_PA1,
+       .irq_end        = RK30_PIN0_PA1,
+       //.irq            = RK29_PIN0_PA1,
+       .pre_init = tps80032_pre_init,
+       .set_init = tps80032_set_init,
+       
+       /* Regulators */
+       .ldo1           = &tps80032_ldo1,
+       .ldo2           = &tps80032_ldo2,
+       .ldo3           = &tps80032_ldo3,
+       .ldo4           = &tps80032_ldo4,
+       .ldo5           = &tps80032_ldo5,
+       .ldo6           = &tps80032_ldo6,
+       .ldo7           = &tps80032_ldo7,
+       .ldoln             = &tps80032_ldoln,
+       .ldousb           =&tps80032_ldousb,
+       .vana              = &tps80032_ldovana,
+       
+       .smps1 = &tps80032_smps1,
+       .smps2= &tps80032_smps2,
+       .smps3          = &tps80032_smps3,
+       .smps4          = &tps80032_smps4,
+       .smps5   = &tps80032_smps5,
+};
+
+#endif
old mode 100644 (file)
new mode 100755 (executable)
index b8f2a4e..26b4db7
 #include <linux/platform_device.h>
 #include <linux/clk.h>
 #include <linux/err.h>
+#include <linux/interrupt.h>
 
 #include <linux/regulator/machine.h>
 
 #include <linux/i2c.h>
 #include <linux/i2c/twl.h>
+#include "twl-core.h"
 
 #if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3)
 #include <plat/cpu.h>
 #define twl_has_madc() false
 #endif
 
-#ifdef CONFIG_TWL4030_POWER
+#if defined(CONFIG_TWL6030_GPADC) || defined(CONFIG_TWL6030_GPADC_MODULE)
+#define twl_has_gpadc()        true
+#else
+#define twl_has_gpadc()        false
+#endif
+
+#if defined(CONFIG_TWL4030_POWER) || defined(CONFIG_TWL6030_POWER)
 #define twl_has_power()        true
 #else
 #define twl_has_power()        false
 #define twl_has_codec()        false
 #endif
 
-#if defined(CONFIG_CHARGER_TWL4030) || defined(CONFIG_CHARGER_TWL4030_MODULE)
+#if defined(CONFIG_CHARGER_TWL4030) || \
+       defined(CONFIG_CHARGER_TWL4030_MODULE) || \
+       defined(CONFIG_TWL6030_BCI_BATTERY) || \
+       defined(CONFIG_TWL6030_BCI_BATTERY_MODULE)
 #define twl_has_bci()  true
 #else
 #define twl_has_bci()  false
 
 /* Last - for index max*/
 #define TWL4030_MODULE_LAST            TWL4030_MODULE_SECURED_REG
-
+#define TWL6030_MODULE_LAST            TWL6030_MODULE_SLAVE_RES  //xsf
+#define TWL6030_MODULE_LAST            TWL_MODULE_PM_DVS //add
 #define TWL_NUM_SLAVES         4
 
 #if defined(CONFIG_INPUT_TWL4030_PWRBUTTON) \
 #define twl_has_pwrbutton()    false
 #endif
 
+#if defined(CONFIG_INPUT_TWL6030_PWRBUTTON) \
+       || defined(CONFIG_INPUT_TWL6030_PWRBUTTON_MODULE)
+#define twl6030_has_pwrbutton()        true
+#else
+#define twl6030_has_pwrbutton()        false
+#endif
+
 #define SUB_CHIP_ID0 0
 #define SUB_CHIP_ID1 1
 #define SUB_CHIP_ID2 2
 #define SUB_CHIP_ID3 3
-
-#define TWL_MODULE_LAST TWL4030_MODULE_LAST
+#define SUB_DVS_ID     3            //add
+#define TWL_MODULE_LAST TWL6030_MODULE_LAST
 
 /* Base Address defns for twl4030_map[] */
 
 #define TWL6030_BASEADD_MEM            0x0017
 #define TWL6030_BASEADD_PM_MASTER      0x001F
 #define TWL6030_BASEADD_PM_SLAVE_MISC  0x0030 /* PM_RECEIVER */
+#define TWL6030_BASEADD_PM_SLAVE_RES   0x00AD
 #define TWL6030_BASEADD_PM_MISC                0x00E2
 #define TWL6030_BASEADD_PM_PUPD                0x00F0
 
 #define TWL6030_BASEADD_GASGAUGE       0x00C0
 #define TWL6030_BASEADD_PIH            0x00D0
 #define TWL6030_BASEADD_CHARGER                0x00E0
-#define TWL6025_BASEADD_CHARGER                0x00DA
+#define TWL6032_BASEADD_CHARGER                0x00DA
 
 /* subchip/slave 2 0x4A - DFT */
 #define TWL6030_BASEADD_DIEID          0x00C0
 #define TWL5031                        BIT(2)  /* twl5031 has different registers */
 #define TWL6030_CLASS          BIT(3)  /* TWL6030 class */
 
+/* need to access USB_PRODUCT_ID_LSB to identify which 6030 varient we are */
+#define USB_PRODUCT_ID_LSB     0x02
+
+/* need to check eeprom revision and jtagver number */
+#define TWL6030_REG_EPROM_REV  0xdf
+#define TWL6030_REG_JTAGVERNUM 0x87
+
 /*----------------------------------------------------------------------*/
 
 /* is driver active, bound to a chip? */
@@ -332,7 +359,11 @@ static struct twl_mapping twl6030_map[] = {
 
        { SUB_CHIP_ID0, TWL6030_BASEADD_RTC },
        { SUB_CHIP_ID0, TWL6030_BASEADD_MEM },
-       { SUB_CHIP_ID1, TWL6025_BASEADD_CHARGER },
+       { SUB_CHIP_ID1, TWL6032_BASEADD_CHARGER },
+       { SUB_CHIP_ID0, TWL6030_BASEADD_PM_SLAVE_RES },
+       { SUB_DVS_ID, TWL6030_BASEADD_PM_SLAVE_MISC }, //add
+       
+
 };
 
 /*----------------------------------------------------------------------*/
@@ -362,23 +393,38 @@ int twl_i2c_write(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes)
                pr_err("%s: invalid module number %d\n", DRIVER_NAME, mod_no);
                return -EPERM;
        }
-       sid = twl_map[mod_no].sid;
-       twl = &twl_modules[sid];
-
        if (unlikely(!inuse)) {
-               pr_err("%s: client %d is not initialized\n", DRIVER_NAME, sid);
+               pr_err("%s: not initialized\n", DRIVER_NAME);
                return -EPERM;
        }
+       sid = twl_map[mod_no].sid;
+       twl = &twl_modules[sid];
+
        mutex_lock(&twl->xfer_lock);
        /*
         * [MSG1]: fill the register address data
         * fill the data Tx buffer
         */
        msg = &twl->xfer_msg[0];
+       #if 1                    //add
+       if((reg == 0x26)||(reg == 0x2C)||(reg == 0x2A))
+               {
+               msg->addr = 0x12;
+               reg--;
+               }
+       else
+               {
+       msg->addr = twl->address;
+
+               }
+       #else
        msg->addr = twl->address;
+       #endif
+
        msg->len = num_bytes + 1;
        msg->flags = 0;
        msg->buf = value;
+       msg->scl_rate = 100*1000;  //add
        /* over write the first byte of buffer with the register address */
        *value = twl_map[mod_no].base + reg;
        ret = i2c_transfer(twl->client->adapter, twl->xfer_msg, 1);
@@ -386,8 +432,9 @@ int twl_i2c_write(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes)
 
        /* i2c_transfer returns number of messages transferred */
        if (ret != 1) {
-               pr_err("%s: i2c_write failed to transfer all messages\n",
-                       DRIVER_NAME);
+               pr_err("%s: i2c_write failed to transfer all messages "
+                       "(addr 0x%04x, reg %d, len %d)\n",
+                       DRIVER_NAME, twl->address, reg, msg->len);
                if (ret < 0)
                        return ret;
                else
@@ -414,18 +461,17 @@ int twl_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes)
        int sid;
        struct twl_client *twl;
        struct i2c_msg *msg;
-
        if (unlikely(mod_no > TWL_MODULE_LAST)) {
                pr_err("%s: invalid module number %d\n", DRIVER_NAME, mod_no);
                return -EPERM;
        }
-       sid = twl_map[mod_no].sid;
-       twl = &twl_modules[sid];
-
        if (unlikely(!inuse)) {
-               pr_err("%s: client %d is not initialized\n", DRIVER_NAME, sid);
+               pr_err("%s: not initialized\n", DRIVER_NAME);
                return -EPERM;
        }
+       sid = twl_map[mod_no].sid;
+       twl = &twl_modules[sid];
+
        mutex_lock(&twl->xfer_lock);
        /* [MSG1] fill the register address data */
        msg = &twl->xfer_msg[0];
@@ -434,6 +480,7 @@ int twl_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes)
        msg->flags = 0; /* Read the register value */
        val = twl_map[mod_no].base + reg;
        msg->buf = &val;
+       msg->scl_rate = 100*1000;  //add
        /* [MSG2] fill the data rx buffer */
        msg = &twl->xfer_msg[1];
        msg->addr = twl->address;
@@ -445,8 +492,9 @@ int twl_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes)
 
        /* i2c_transfer returns number of messages transferred */
        if (ret != 2) {
-               pr_err("%s: i2c_read failed to transfer all messages\n",
-                       DRIVER_NAME);
+               pr_err("%s: i2c_read failed to transfer all messages "
+                       "(addr 0x%04x, reg %d, len %d)\n",
+                       DRIVER_NAME, twl->address, reg, msg->len);
                if (ret < 0)
                        return ret;
                else
@@ -470,6 +518,20 @@ int twl_i2c_write_u8(u8 mod_no, u8 value, u8 reg)
 
        /* 2 bytes offset 1 contains the data offset 0 is used by i2c_write */
        u8 temp_buffer[2] = { 0 };
+       
+       #if 1                //add
+       if(reg == 0x26)
+       {
+               temp_buffer[1] = 0x43;
+                twl_i2c_write(mod_no, temp_buffer, 0x25, 1);
+
+               temp_buffer[1] = 0x07;
+               twl_i2c_write(mod_no, temp_buffer, reg, 1);     
+
+       }
+       
+       #endif
+
        /* offset 1 contains the data */
        temp_buffer[1] = value;
        return twl_i2c_write(mod_no, temp_buffer, reg, 1);
@@ -552,7 +614,6 @@ add_numbered_child(unsigned chip, const char *name, int num,
        struct platform_device  *pdev;
        struct twl_client       *twl = &twl_modules[chip];
        int                     status;
-
        pdev = platform_device_alloc(name, num);
        if (!pdev) {
                dev_dbg(&twl->client->dev, "can't alloc dev\n");
@@ -633,6 +694,9 @@ add_regulator(int num, struct regulator_init_data *pdata,
        return add_regulator_linked(num, pdata, NULL, 0, features);
 }
 
+#define SET_LDO_STATE_MEM(ldo, state) \
+       ldo->constraints.state_mem.disabled = state
+
 /*
  * NOTE:  We know the first 8 IRQs after pdata->base_irq are
  * for the PIH, and the next are for the PWR_INT SIH, since
@@ -644,6 +708,8 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features)
 {
        struct device   *child;
        unsigned sub_chip_id;
+       u8 eepromrev = 0;
+       u8 twlrev = 0;
 
        if (twl_has_gpio() && pdata->gpio) {
                child = add_child(SUB_CHIP_ID1, "twl4030_gpio",
@@ -660,6 +726,15 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features)
                if (IS_ERR(child))
                        return PTR_ERR(child);
        }
+       if (twl_has_bci() && pdata->bci) {
+               pdata->bci->features = features;
+               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) {
                child = add_child(2, "twl4030_madc",
@@ -669,6 +744,16 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features)
                        return PTR_ERR(child);
        }
 
+       if (twl_has_gpadc() && pdata->madc) {
+               pdata->madc->features = features;
+               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
@@ -750,28 +835,23 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features)
 
                static struct regulator_consumer_supply usb3v3;
                int regulator;
-
                if (twl_has_regulator()) {
-                       /* this is a template that gets copied */
-                       struct regulator_init_data usb_fixed = {
-                               .constraints.valid_modes_mask =
-                                       REGULATOR_MODE_NORMAL
-                                       | REGULATOR_MODE_STANDBY,
-                               .constraints.valid_ops_mask =
-                                       REGULATOR_CHANGE_MODE
-                                       | REGULATOR_CHANGE_STATUS,
-                       };
-
-                       if (features & TWL6025_SUBCLASS) {
+                       if (features & TWL6032_SUBCLASS) {
                                usb3v3.supply = "ldousb";
-                               regulator = TWL6025_REG_LDOUSB;
+                               regulator = TWL6032_REG_LDOUSB;
+                               child = add_regulator_linked(regulator,
+                                                            pdata->ldousb,
+                                                            &usb3v3, 1,
+                                                            features);
                        } else {
                                usb3v3.supply = "vusb";
                                regulator = TWL6030_REG_VUSB;
+                               child = add_regulator_linked(regulator,
+                                                            pdata->vusb,
+                                                            &usb3v3, 1,
+                                                            features);
                        }
-                       child = add_regulator_linked(regulator, &usb_fixed,
-                                                       &usb3v3, 1,
-                                                       features);
+
                        if (IS_ERR(child))
                                return PTR_ERR(child);
                }
@@ -791,8 +871,8 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features)
                if (twl_has_regulator() && child)
                        usb3v3.dev = child;
        } else if (twl_has_regulator() && twl_class_is_6030()) {
-               if (features & TWL6025_SUBCLASS)
-                       child = add_regulator(TWL6025_REG_LDOUSB,
+               if (features & TWL6032_SUBCLASS)
+                       child = add_regulator(TWL6032_REG_LDOUSB,
                                                pdata->ldousb, features);
                else
                        child = add_regulator(TWL6030_REG_VUSB,
@@ -815,6 +895,13 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features)
                        return PTR_ERR(child);
        }
 
+       if (twl6030_has_pwrbutton()) {
+               child = add_child(1, "twl6030_pwrbutton",
+                               NULL, 0, true, pdata->irq_base, 0);
+               if (IS_ERR(child))
+                       return PTR_ERR(child);
+       }
+
        if (twl_has_codec() && pdata->codec && twl_class_is_4030()) {
                sub_chip_id = twl_map[TWL_MODULE_AUDIO_VOICE].sid;
                child = add_child(sub_chip_id, "twl4030-audio",
@@ -827,7 +914,7 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features)
        /* Phoenix codec driver is probed directly atm */
        if (twl_has_codec() && pdata->codec && twl_class_is_6030()) {
                sub_chip_id = twl_map[TWL_MODULE_AUDIO_VOICE].sid;
-               child = add_child(sub_chip_id, "twl6040-codec",
+               child = add_child(sub_chip_id, "twl6040-audio",
                                pdata->codec, sizeof(*pdata->codec),
                                false, 0, 0);
                if (IS_ERR(child))
@@ -923,9 +1010,36 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features)
                        return PTR_ERR(child);
        }
 
+       if (twl_has_regulator() && twl_class_is_6030()) {
+               /*
+                * For TWL6032 revision < ES1.1 with EEPROM revision < rev56.0
+                * LDO6 and LDOLN must be always ON
+                * if LDO6 or LDOLN is always on then SYSEN must be always on
+                * for TWL6030 or TWL6032 revision >= ES1.1 with EEPROM
+                * revision >= rev56.0 those LDOs can be off in sleep-mode
+                */
+               if (features & TWL6032_SUBCLASS) {
+                       twl_i2c_read_u8(TWL6030_MODULE_ID2, &eepromrev,
+                                       TWL6030_REG_EPROM_REV);
+
+                       twl_i2c_read_u8(TWL6030_MODULE_ID2, &twlrev,
+                                       TWL6030_REG_JTAGVERNUM);
+
+                       if ((eepromrev < 56) && (twlrev < 1)) {
+                               SET_LDO_STATE_MEM(pdata->ldo6, false);
+                               SET_LDO_STATE_MEM(pdata->ldoln, false);
+                               SET_LDO_STATE_MEM(pdata->sysen, false);
+                               WARN(1, "This TWL6032 is an older revision that "
+                                               "does not support low power "
+                                               "measurements\n");
+                       }
+               }
+       }
+
        /* twl6030 regulators */
        if (twl_has_regulator() && twl_class_is_6030() &&
-                       !(features & TWL6025_SUBCLASS)) {
+                       !(features & TWL6032_SUBCLASS)) {
+                       
                child = add_regulator(TWL6030_REG_VMMC, pdata->vmmc,
                                        features);
                if (IS_ERR(child))
@@ -966,82 +1080,137 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features)
                if (IS_ERR(child))
                        return PTR_ERR(child);
 
-               child = add_regulator(TWL6030_REG_CLK32KG, pdata->clk32kg,
+               child = add_regulator(TWL6030_REG_VDD1, pdata->vdd1,
+                                       features);
+               if (IS_ERR(child))
+                       return PTR_ERR(child);
+
+               child = add_regulator(TWL6030_REG_VDD2, pdata->vdd2,
+                                       features);
+               if (IS_ERR(child))
+                       return PTR_ERR(child);
+
+               child = add_regulator(TWL6030_REG_VDD3, pdata->vdd3,
+                                       features);
+               if (IS_ERR(child))
+                       return PTR_ERR(child);
+
+               child = add_regulator(TWL6030_REG_VMEM, pdata->vmem,
                                        features);
                if (IS_ERR(child))
                        return PTR_ERR(child);
+
+               child = add_regulator(TWL6030_REG_V2V1, pdata->v2v1,
+                                       features);
+               if (IS_ERR(child))
+                       return PTR_ERR(child);  
        }
 
-       /* 6030 and 6025 share this regulator */
+       /* 6030 and 6032 share this regulator */
        if (twl_has_regulator() && twl_class_is_6030()) {
                child = add_regulator(TWL6030_REG_VANA, pdata->vana,
                                        features);
                if (IS_ERR(child))
                        return PTR_ERR(child);
+               child = add_regulator(TWL6030_REG_CLK32KG, pdata->clk32kg,
+                                       features);
+               if (IS_ERR(child))
+                       return PTR_ERR(child);
+
+               child = add_regulator(TWL6030_REG_CLK32KAUDIO,
+                               pdata->clk32kaudio, features);
+               if (IS_ERR(child))
+                       return PTR_ERR(child);
+
+               child = add_regulator(TWL6030_REG_SYSEN,
+                               pdata->sysen, features);
+               if (IS_ERR(child))
+                       return PTR_ERR(child);
+
+               child = add_regulator(TWL6030_REG_REGEN1,
+                               pdata->sysen, features);
+               if (IS_ERR(child))
+                       return PTR_ERR(child);
+                       
        }
 
-       /* twl6025 regulators */
+       /* twl6032 regulators */
        if (twl_has_regulator() && twl_class_is_6030() &&
-                       (features & TWL6025_SUBCLASS)) {
-               child = add_regulator(TWL6025_REG_LDO5, pdata->ldo5,
+                       (features & TWL6032_SUBCLASS)) {
+               child = add_regulator(TWL6032_REG_LDO5, pdata->ldo5,
                                        features);
                if (IS_ERR(child))
                        return PTR_ERR(child);
-
-               child = add_regulator(TWL6025_REG_LDO1, pdata->ldo1,
+       
+               child = add_regulator(TWL6032_REG_LDO1, pdata->ldo1,
+                                       features);
+               if (IS_ERR(child))
+                       return PTR_ERR(child);
+       
+               child = add_regulator(TWL6032_REG_LDO7, pdata->ldo7,
                                        features);
                if (IS_ERR(child))
                        return PTR_ERR(child);
 
-               child = add_regulator(TWL6025_REG_LDO7, pdata->ldo7,
+               child = add_regulator(TWL6032_REG_LDO6, pdata->ldo6,
                                        features);
                if (IS_ERR(child))
                        return PTR_ERR(child);
 
-               child = add_regulator(TWL6025_REG_LDO6, pdata->ldo6,
+               child = add_regulator(TWL6032_REG_LDOLN, pdata->ldoln,
                                        features);
                if (IS_ERR(child))
                        return PTR_ERR(child);
 
-               child = add_regulator(TWL6025_REG_LDOLN, pdata->ldoln,
+               child = add_regulator(TWL6032_REG_LDO2, pdata->ldo2,
                                        features);
                if (IS_ERR(child))
                        return PTR_ERR(child);
 
-               child = add_regulator(TWL6025_REG_LDO2, pdata->ldo2,
+               child = add_regulator(TWL6032_REG_LDO4, pdata->ldo4,
                                        features);
                if (IS_ERR(child))
                        return PTR_ERR(child);
 
-               child = add_regulator(TWL6025_REG_LDO4, pdata->ldo4,
+               child = add_regulator(TWL6032_REG_LDO3, pdata->ldo3,
                                        features);
                if (IS_ERR(child))
                        return PTR_ERR(child);
 
-               child = add_regulator(TWL6025_REG_LDO3, pdata->ldo3,
+               child = add_regulator(TWL6032_REG_SMPS3, pdata->smps3,
                                        features);
                if (IS_ERR(child))
                        return PTR_ERR(child);
 
-               child = add_regulator(TWL6025_REG_SMPS3, pdata->smps3,
+               child = add_regulator(TWL6032_REG_SMPS4, pdata->smps4,
                                        features);
                if (IS_ERR(child))
                        return PTR_ERR(child);
 
-               child = add_regulator(TWL6025_REG_SMPS4, pdata->smps4,
+               child = add_regulator(TWL6032_REG_SMPS1, pdata->smps1,
                                        features);
                if (IS_ERR(child))
                        return PTR_ERR(child);
 
-               child = add_regulator(TWL6025_REG_VIO, pdata->vio6025,
+               child = add_regulator(TWL6032_REG_SMPS2, pdata->smps2,
+                                       features);
+               if (IS_ERR(child))
+                       return PTR_ERR(child);
+               
+               child = add_regulator(TWL6032_REG_SMPS5, pdata->smps5,
                                        features);
                if (IS_ERR(child))
                        return PTR_ERR(child);
 
+
+               child = add_regulator(TWL6032_REG_VIO, pdata->vio6032,
+                                       features);
+               if (IS_ERR(child))
+                       return PTR_ERR(child);
        }
 
        if (twl_has_bci() && pdata->bci &&
-                       !(features & (TPS_SUBSET | TWL5031))) {
+                       !(features & (TPS_SUBSET | TWL5031)) && (features & TWL6030_CLASS)) {
                child = add_child(3, "twl4030_bci",
                                pdata->bci, sizeof(*pdata->bci), false,
                                /* irq0 = CHG_PRES, irq1 = BCI */
@@ -1146,13 +1315,22 @@ static void clocks_init(struct device *dev,
 
 /*----------------------------------------------------------------------*/
 
-int twl4030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end);
-int twl4030_exit_irq(void);
-int twl4030_init_chip_irq(const char *chip);
-int twl6030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end);
-int twl6030_exit_irq(void);
+#ifdef CONFIG_PM
+static int twl_suspend(struct i2c_client *client, pm_message_t mesg)
+{
+       return irq_set_irq_wake(client->irq, 1);
+}
 
-static int twl_remove(struct i2c_client *client)
+static int twl_resume(struct i2c_client *client)
+{
+       return irq_set_irq_wake(client->irq, 0);
+}
+#else
+#define twl_suspend    NULL
+#define twl_resume     NULL
+#endif
+
+static int __devexit twl_remove(struct i2c_client *client)
 {
        unsigned i;
        int status;
@@ -1184,7 +1362,7 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id)
        unsigned                        i;
        struct twl4030_platform_data    *pdata = client->dev.platform_data;
        u8 temp;
-       int ret = 0;
+       int ret = 0, features;
 
        if (!pdata) {
                dev_dbg(&client->dev, "no platform data?\n");
@@ -1203,8 +1381,16 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id)
 
        for (i = 0; i < TWL_NUM_SLAVES; i++) {
                struct twl_client       *twl = &twl_modules[i];
-
+                            
+#if 0
                twl->address = client->addr + i;
+#else
+               if( i <TWL_NUM_SLAVES-1 )
+                       twl->address = client->addr + i;
+               else
+                       twl->address = 0x12; //DVS i2s address
+#endif
+               
                if (i == 0)
                        twl->client = client;
                else {
@@ -1237,9 +1423,20 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id)
                WARN(ret < 0, "Error: reading twl_idcode register value\n");
        }
 
+       features = id->driver_data;
+       if (twl_class_is_6030()) {
+               twl_i2c_read_u8(TWL_MODULE_USB, &temp, USB_PRODUCT_ID_LSB);
+               if (temp == 0x32)
+                       features |= TWL6032_SUBCLASS;
+       }
+
        /* load power event scripts */
-       if (twl_has_power() && pdata->power)
-               twl4030_power_init(pdata->power);
+       if (twl_has_power()) {
+               if (twl_class_is_4030() && pdata->power)
+                       twl4030_power_init(pdata->power);
+               if (twl_class_is_6030())
+                       twl6030_power_init(pdata->power, features);
+       }
 
        /* Maybe init the T2 Interrupt subsystem */
        if (client->irq
@@ -1251,7 +1448,7 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id)
                        pdata->irq_end);
                } else {
                        status = twl6030_init_irq(client->irq, pdata->irq_base,
-                       pdata->irq_end);
+                       pdata->irq_end, features);
                }
 
                if (status < 0)
@@ -1269,8 +1466,23 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id)
                I2C_SDA_CTRL_PU | I2C_SCL_CTRL_PU);
                twl_i2c_write_u8(TWL4030_MODULE_INTBR, temp, REG_GPPUPDCTR1);
        }
+       
+       status = add_children(pdata, features);
 
-       status = add_children(pdata, id->driver_data);
+       if (pdata && pdata->pre_init) {
+               ret = pdata->pre_init();
+               if (ret != 0) {
+                       printk(" tps80032 pre_init() failed: %d\n");
+               }
+       }
+       
+       if (pdata && pdata->set_init) {
+               ret = pdata->set_init();
+               if (ret != 0) {
+                       printk(" tps80032 set_init() failed: %d\n");
+               }
+       }
+       
 fail:
        if (status < 0)
                twl_remove(client);
@@ -1285,7 +1497,7 @@ static const struct i2c_device_id twl_ids[] = {
        { "tps65930", TPS_SUBSET },     /* fewer LDOs and DACs; no charger */
        { "tps65920", TPS_SUBSET },     /* fewer LDOs; no codec or charger */
        { "twl6030", TWL6030_CLASS },   /* "Phoenix power chip" */
-       { "twl6025", TWL6030_CLASS | TWL6025_SUBCLASS }, /* "Phoenix lite" */
+       { "twl6032", TWL6030_CLASS | TWL6032_SUBCLASS }, /* Phoenix lite */
        { /* end of list */ },
 };
 MODULE_DEVICE_TABLE(i2c, twl_ids);
@@ -1295,14 +1507,16 @@ static struct i2c_driver twl_driver = {
        .driver.name    = DRIVER_NAME,
        .id_table       = twl_ids,
        .probe          = twl_probe,
-       .remove         = twl_remove,
+       .remove         = __devexit_p(twl_remove),
+       .suspend        = twl_suspend,
+       .resume         = twl_resume,
 };
 
 static int __init twl_init(void)
 {
        return i2c_add_driver(&twl_driver);
 }
-subsys_initcall(twl_init);
+subsys_initcall_sync(twl_init);
 
 static void __exit twl_exit(void)
 {
index 8c50a556e9866067eab7ec15c60d5e8736258fe6..00e6468a53b06b262fc2d56013065bea1a0f1b70 100644 (file)
@@ -1,7 +1,8 @@
 #ifndef __TWL_CORE_H__
 #define __TWL_CORE_H__
 
-extern int twl6030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end);
+extern int twl6030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end,
+                       unsigned long features);
 extern int twl6030_exit_irq(void);
 extern int twl4030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end);
 extern int twl4030_exit_irq(void);
diff --git a/drivers/mfd/twl6030-gpadc.c b/drivers/mfd/twl6030-gpadc.c
new file mode 100644 (file)
index 0000000..6457316
--- /dev/null
@@ -0,0 +1,1446 @@
+/*
+ * drivers/i2c/chips/twl6030-gpadc.c
+ *
+ * TWL6030 GPADC module driver
+ *
+ * Copyright (C) 2009 Texas Instruments Inc.
+ * Nishant Kamat <nskamat@ti.com>
+ *
+ * Based on twl4030-madc.c
+ * Copyright (C) 2008 Nokia Corporation
+ * Mikko Ylinen <mikko.k.ylinen@nokia.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.
+ *
+ * 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 St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/platform_device.h>
+#include <linux/miscdevice.h>
+#include <linux/slab.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/i2c/twl.h>
+#include <linux/i2c/twl6030-gpadc.h>
+
+#include <linux/uaccess.h>
+
+#define TWL6030_GPADC_PFX      "twl6030-gpadc: "
+#define ENABLE_GPADC   0x02
+#define REG_TOGGLE1    0x90
+#define GPADCS         (1 << 1)
+#define GPADCR         (1 << 0)
+
+#define SCALE                          (1 << 15)
+
+struct twl6030_chnl_calib {
+       s32 gain_error;
+       s32 offset_error;
+};
+
+struct twl6030_ideal_code {
+       s16 code1;
+       s16 code2;
+};
+
+struct twl6032_chnl_calib {
+       s32 gain;
+       s32 gain_error;
+       s32 offset_error;
+};
+
+struct twl6032_ideal_code {
+       s16 code1;
+       s16 code2;
+       s16 v1;
+       s16 v2;
+};
+
+static struct twl6030_chnl_calib
+       twl6030_calib_tbl[GPADC_MAX_CHANNELS];
+static const u32 calibration_bit_map = 0x47FF;
+
+/* Trim address where measured offset from ideal code is stored */
+static const u8 twl6030_trim_addr[GPADC_MAX_CHANNELS] = {
+       0xCD, /* CHANNEL 0 */
+       0xD1, /* CHANNEL 1 */
+       0xD9, /* CHANNEL 2 */
+       0xD1, /* CHANNEL 3 */
+       0xD1, /* CHANNEL 4 */
+       0xD1, /* CHANNEL 5 */
+       0xD1, /* CHANNEL 6 */
+       0xD3, /* CHANNEL 7 */
+       0xCF, /* CHANNEL 8 */
+       0xD5, /* CHANNEL 9 */
+       0xD7, /* CHANNEL 10 */
+       0x00, /* CHANNEL 11 */
+       0x00, /* CHANNEL 12 */
+       0x00, /* CHANNEL 13 */
+       0xDB, /* CHANNEL 14 */
+       0x00, /* CHANNEL 15 */
+       0x00, /* CHANNEL 16 */
+};
+
+#define TWL6032_GPADC_TRIM1    0xCD
+#define TWL6032_GPADC_TRIM2    0xCE
+#define TWL6032_GPADC_TRIM3    0xCF
+#define TWL6032_GPADC_TRIM4    0xD0
+#define TWL6032_GPADC_TRIM5    0xD1
+#define TWL6032_GPADC_TRIM6    0xD2
+#define TWL6032_GPADC_TRIM7    0xD3
+#define TWL6032_GPADC_TRIM8    0xD4
+#define TWL6032_GPADC_TRIM9    0xD5
+#define TWL6032_GPADC_TRIM10   0xD6
+#define TWL6032_GPADC_TRIM11   0xD7
+#define TWL6032_GPADC_TRIM12   0xD8
+#define TWL6032_GPADC_TRIM13   0xD9
+#define TWL6032_GPADC_TRIM14   0xDA
+#define TWL6032_GPADC_TRIM15   0xDB
+#define TWL6032_GPADC_TRIM16   0xDC
+#define TWL6032_GPADC_TRIM19   0xFD
+
+/*
+ * actual scaler gain is multiplied by 8 for fixed point operation
+ * 1.875 * 8 = 15
+ * For channels 0, 1, 3, 4, 5, 6, 12, 13
+ * 1.25 * 8 = 10
+ * is used, as scaler is Vref * divider
+ * Vref = 1.25
+ */
+static const u16 twl6030_gain[TWL6030_GPADC_MAX_CHANNELS] = {
+       10,     /* CHANNEL 0 */
+       10,     /* CHANNEL 1 */
+
+       /* 1.875 */
+       15,     /* CHANNEL 2 */
+
+       10,     /* CHANNEL 3 */
+       10,     /* CHANNEL 4 */
+       10,     /* CHANNEL 5 */
+       10,     /* CHANNEL 6 */
+
+       /* 5 */
+       40,     /* CHANNEL 7 */
+
+       /* 6.25 */
+       50,     /* CHANNEL 8 */
+
+       /* 11.25 */
+       90,     /* CHANNEL 9 */
+
+       /* 6.875 */
+       55,     /* CHANNEL 10 */
+
+       /* 1.875 */
+       15,     /* CHANNEL 11 */
+
+       10,     /* CHANNEL 12 */
+       10,     /* CHANNEL 13 */
+
+       /* 6.875 */
+       55,     /* CHANNEL 14 */
+
+       /* 6.25 */
+       50,     /* CHANNEL 15 */
+
+       /* 4.75 */
+       38,     /* CHANNEL 16 */
+};
+
+/*
+ * calibration not needed for channel 11, 12, 13, 15 and 16
+ * calibration offset is same for channel 1, 3, 4, 5
+ */
+static const struct twl6030_ideal_code
+       twl6030_ideal[GPADC_MAX_CHANNELS] = {
+       {116,   745},   /* CHANNEL 0 */
+       {82,    900},   /* CHANNEL 1 */
+       {55,    818},   /* CHANNEL 2 */
+       {82,    900},   /* CHANNEL 3 */
+       {82,    900},   /* CHANNEL 4 */
+       {82,    900},   /* CHANNEL 5 */
+       {82,    900},   /* CHANNEL 6 */
+       {614,   941},   /* CHANNEL 7 */
+       {82,    688},   /* CHANNEL 8 */
+       {182,   818},   /* CHANNEL 9 */
+       {149,   818},   /* CHANNEL 10 */
+       {0,     0},     /* CHANNEL 11 */
+       {0,     0},     /* CHANNEL 12 */
+       {0,     0},     /* CHANNEL 13 */
+       {48,    714},   /* CHANNEL 14 */
+       {0,     0},     /* CHANNEL 15 */
+       {0,     0},     /* CHANNEL 16 */
+};
+
+/* PhoenixLite has a different calibration sysem to the Phoenix */
+static const struct twl6032_ideal_code
+                       twl6032_ideal[GPADC_MAX_CHANNELS] = {
+       {       /* CHANNEL 0 */
+               .code1 = 1441,
+               .code2 = 3276,
+               .v1 = 440,
+               .v2 = 1000,
+       },
+       {       /* CHANNEL 1 */
+               .code1 = 1441,
+               .code2 = 3276,
+               .v1 = 440,
+               .v2 = 1000,
+       },
+       {       /* CHANNEL 2 */
+               .code1 = 1441,
+               .code2 = 3276,
+               .v1 = 660,
+               .v2 = 1500,
+       },
+       {       /* CHANNEL 3 */
+               .code1 = 1441,
+               .code2 = 3276,
+               .v1 = 440,
+               .v2 = 1000,
+       },
+       {       /* CHANNEL 4 */
+               .code1 = 1441,
+               .code2 = 3276,
+               .v1 = 440,
+               .v2 = 1000,
+       },
+       {       /* CHANNEL 5 */
+               .code1 = 1441,
+               .code2 = 3276,
+               .v1 = 440,
+               .v2 = 1000,
+       },
+       {       /* CHANNEL 6 */
+               .code1 = 1441,
+               .code2 = 3276,
+               .v1 = 440,
+               .v2 = 1000,
+       },
+       {       /* CHANNEL 7 */
+               .code1 = 1441,
+               .code2 = 3276,
+               .v1 = 2200,
+               .v2 = 5000,
+       },
+       {       /* CHANNEL 8 */
+               .code1 = 1441,
+               .code2 = 3276,
+               .v1 = 2200,
+               .v2 = 5000,
+       },
+       {       /* CHANNEL 9 */
+               .code1 = 1441,
+               .code2 = 3276,
+               .v1 = 3960,
+               .v2 = 9000,
+       },
+       {       /* CHANNEL 10 */
+               .code1 = 150,
+               .code2 = 751,
+               .v1 = 1000,
+               .v2 = 5000,
+       },
+       {       /* CHANNEL 11 */
+               .code1 = 1441,
+               .code2 = 3276,
+               .v1 = 660,
+               .v2 = 1500,
+       },
+       {       /* CHANNEL 12 */
+               .code1 = 1441,
+               .code2 = 3276,
+               .v1 = 440,
+               .v2 = 1000,
+       },
+       {       /* CHANNEL 13 */
+               .code1 = 1441,
+               .code2 = 3276,
+               .v1 = 440,
+               .v2 = 1000,
+       },
+       {       /* CHANNEL 14 */
+               .code1 = 1441,
+               .code2 = 3276,
+               .v1 = 2420,
+               .v2 = 5500,
+       },
+       {},     /* CHANNEL 15 - UNUSED */
+       {},     /* CHANNEL 16 - UNUSED */
+       {},     /* CHANNEL 17 - UNUSED */
+       {       /* CHANNEL 18 */
+               .code1 = 1441,
+               .code2 = 3276,
+               .v1 = 2200,
+               .v2 = 5000,
+       },
+};
+
+
+struct twl6030_gpadc_data {
+       struct device           *dev;
+       struct mutex            lock;
+       struct work_struct      ws;
+       struct twl6030_gpadc_request    requests[TWL6030_GPADC_NUM_METHODS];
+       int irq_n;
+       struct twl6032_chnl_calib *twl6032_cal_tbl;
+       unsigned long features;
+};
+
+static struct twl6030_gpadc_data *the_gpadc;
+
+static const
+struct twl6030_gpadc_conversion_method twl6030_conversion_methods_table[] = {
+       [TWL6030_GPADC_RT] = {
+               .sel    = TWL6030_GPADC_RTSELECT_LSB,
+               .rbase  = TWL6030_GPADC_RTCH0_LSB,
+               .mask   = TWL6030_GPADC_RT_SW1_EOC_MASK,
+       },
+       /*
+        * TWL6030_GPADC_SW1 is not supported as
+        * interrupt from RT and SW1 cannot be differentiated
+        */
+       [TWL6030_GPADC_SW2] = {
+               .rbase  = TWL6030_GPADC_GPCH0_LSB,
+               .ctrl   = TWL6030_GPADC_CTRL_P2,
+               .enable = TWL6030_GPADC_CTRL_P2_SP2,
+               .mask   = TWL6030_GPADC_SW2_EOC_MASK,
+       },
+};
+
+static const
+struct twl6030_gpadc_conversion_method twl6032_conversion_methods_table[] = {
+       [TWL6030_GPADC_RT] = {
+               .sel    = TWL6032_GPADC_RTSELECT_LSB,
+               .rbase  = TWL6032_RTCH0_LSB,
+               .mask   = TWL6032_GPADC_RT_EOC_MASK,
+       },
+       [TWL6030_GPADC_SW2] = {
+               .sel    = TWL6032_GPADC_GPSELECT_ISB,
+               .rbase  = TWL6032_GPCH0_LSB,
+               .ctrl   = TWL6032_GPADC_CTRL_P1,
+               .enable = TWL6030_GPADC_CTRL_P1_SP1,
+               .mask   = TWL6032_GPADC_SW_EOC_MASK,
+       },
+};
+
+static const
+struct twl6030_gpadc_conversion_method *twl6030_conversion_methods;
+
+static ssize_t show_gain(struct device *dev,
+               struct device_attribute *devattr, char *buf)
+{
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       int value;
+       int status;
+
+       value = twl6030_calib_tbl[attr->index].gain_error;
+
+       status = sprintf(buf, "%d\n", value);
+       return status;
+}
+
+static ssize_t set_gain(struct device *dev,
+       struct device_attribute *devattr, const char *buf, size_t count)
+{
+       long val;
+       int status = count;
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+
+       if ((strict_strtol(buf, 10, &val) < 0) || (val < 15000)
+                                                       || (val > 60000))
+               return -EINVAL;
+
+       twl6030_calib_tbl[attr->index].gain_error = val;
+
+       return status;
+}
+
+static ssize_t show_offset(struct device *dev,
+               struct device_attribute *devattr, char *buf)
+{
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       int value;
+       int status;
+
+       value = twl6030_calib_tbl[attr->index].offset_error;
+
+       status = sprintf(buf, "%d\n", value);
+       return status;
+}
+
+static ssize_t set_offset(struct device *dev,
+       struct device_attribute *devattr, const char *buf, size_t count)
+{
+       long val;
+       int status = count;
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+
+       if ((strict_strtol(buf, 10, &val) < 0) || (val < 15000)
+                                                       || (val > 60000))
+               return -EINVAL;
+
+       twl6030_calib_tbl[attr->index].offset_error = val;
+
+       return status;
+}
+
+static int twl6030_gpadc_read(struct twl6030_gpadc_data *gpadc, u8 reg)
+{
+       int ret;
+       u8 val = 0;
+
+       ret = twl_i2c_read_u8(TWL_MODULE_MADC, &val, reg);
+       if (ret) {
+               dev_dbg(gpadc->dev, "unable to read register 0x%X\n", reg);
+               return ret;
+       }
+
+       return val;
+}
+
+static void twl6030_gpadc_write(struct twl6030_gpadc_data *gpadc,
+                               u8 reg, u8 val)
+{
+       int ret;
+
+       ret = twl_i2c_write_u8(TWL_MODULE_MADC, val, reg);
+       if (ret)
+               dev_err(gpadc->dev, "unable to write register 0x%X\n", reg);
+}
+
+static int twl6030_gpadc_channel_raw_read(struct twl6030_gpadc_data *gpadc,
+                                         u8 reg)
+{
+       u8 msb, lsb;
+
+       /* For each ADC channel, we have MSB and LSB register pair.
+        * MSB address is always LSB address+1. reg parameter is the
+        * addr of LSB register
+        */
+       msb = twl6030_gpadc_read(gpadc, reg + 1);
+       lsb = twl6030_gpadc_read(gpadc, reg);
+       return (int)((msb << 8) | lsb);
+}
+
+static int twl6030_gpadc_read_channels(struct twl6030_gpadc_data *gpadc,
+               u8 reg_base, u32 channels, struct twl6030_gpadc_request *req)
+{
+       int count = 0;
+       u8 reg, i;
+       s32 gain_error;
+       s32 offset_error;
+       s32 raw_code;
+       s32 corrected_code;
+       s32 raw_channel_value;
+
+       channels = ~channels;
+       if (gpadc->features & TWL6032_SUBCLASS) {
+               for (i = 0; i < TWL6032_GPADC_MAX_CHANNELS; i++) {
+                       if (channels & BIT(i))
+                               continue;
+
+                       reg = reg_base + 2 * count;
+
+                       dev_dbg(gpadc->dev, "GPADC chn: %d\n", i);
+                       raw_code = twl6030_gpadc_channel_raw_read(gpadc, reg);
+                       dev_dbg(gpadc->dev, "GPADC raw: %d\n", raw_code);
+                       count++;
+                       req->buf[i].raw_code = raw_code;
+
+                       /* No correction for channels 15-17 */
+                       if (unlikely((i >= 15) && (i <= 17))) {
+                               raw_channel_value = raw_code;
+                               req->buf[i].code = raw_code;
+                               req->rbuf[i] = raw_code;
+                       } else {
+                               raw_channel_value = (raw_code *
+                                       gpadc->twl6032_cal_tbl[i].gain);
+
+                               /* Shift back into mV range */
+                               raw_channel_value /= 1000;
+
+                               req->buf[i].code = corrected_code =
+                               ((raw_code * 1000) -
+                               gpadc->twl6032_cal_tbl[i].offset_error) /
+                               gpadc->twl6032_cal_tbl[i].gain_error;
+
+                               dev_dbg(gpadc->dev, "GPADC cor: %d\n",
+                                       corrected_code);
+
+                               req->rbuf[i] = corrected_code *
+                                       gpadc->twl6032_cal_tbl[i].gain;
+
+                               /* Shift back into mV range */
+                               req->rbuf[i] /= 1000;
+                       }
+                       req->buf[i].raw_channel_value = raw_channel_value;
+                       dev_dbg(gpadc->dev, "GPADC val: %d\n", req->rbuf[i]);
+               }
+       } else {
+               for (i = 0; i < TWL6030_GPADC_MAX_CHANNELS; i++) {
+                       if (channels & BIT(i))
+                               continue;
+                       reg = reg_base + 2 * i;
+                       raw_code = twl6030_gpadc_channel_raw_read(gpadc, reg);
+                       req->buf[i].raw_code = raw_code;
+                       count++;
+                       /*
+                        * multiply by 1000 to convert the unit to milli
+                        * division by 1024 (>> 10) for 10 bit ADC
+                        * division by 8 (>> 3) for actual scaler gain
+                        */
+                       raw_channel_value = (raw_code * twl6030_gain[i]
+                                                               * 1000) >> 13;
+                       req->buf[i].raw_channel_value = raw_channel_value;
+
+                       if (~calibration_bit_map & BIT(i)) {
+                               req->buf[i].code = raw_code;
+                               req->rbuf[i] = raw_channel_value;
+                       } else {
+                               gain_error = twl6030_calib_tbl[i].gain_error;
+                               offset_error = twl6030_calib_tbl[i].offset_error;
+                               req->buf[i].code = corrected_code =
+                                       (raw_code * SCALE - offset_error) /
+                                               gain_error;
+                               req->rbuf[i] = (corrected_code * twl6030_gain[i]
+                                                               * 1000) >> 13;
+                       }
+                       dev_dbg(gpadc->dev, "GPADC val: %d", req->rbuf[i]);
+               }
+       }
+       return count;
+}
+
+static void twl6030_gpadc_enable_irq(u16 method)
+{
+       twl6030_interrupt_unmask(twl6030_conversion_methods[method].mask,
+                                               REG_INT_MSK_LINE_B);
+       twl6030_interrupt_unmask(twl6030_conversion_methods[method].mask,
+                                               REG_INT_MSK_STS_B);
+}
+
+static void twl6030_gpadc_disable_irq(u16 method)
+{
+       twl6030_interrupt_mask(twl6030_conversion_methods[method].mask,
+                                               REG_INT_MSK_LINE_B);
+       twl6030_interrupt_mask(twl6030_conversion_methods[method].mask,
+                                               REG_INT_MSK_STS_B);
+}
+
+static irqreturn_t twl6030_gpadc_irq_handler(int irq, void *_req)
+{
+       struct twl6030_gpadc_request *req = _req;
+
+#ifdef CONFIG_LOCKDEP
+       /* WORKAROUND for lockdep forcing IRQF_DISABLED on us, which
+        * we don't want and can't tolerate.  Although it might be
+        * friendlier not to borrow this thread context...
+        */
+       local_irq_enable();
+#endif
+
+       /* Find the cause of the interrupt and enable the pending
+          bit for the corresponding method */
+       twl6030_gpadc_disable_irq(req->method);
+       req->result_pending = 1;
+
+       schedule_work(&the_gpadc->ws);
+
+       return IRQ_HANDLED;
+}
+
+static void twl6030_gpadc_work(struct work_struct *ws)
+{
+       const struct twl6030_gpadc_conversion_method *method;
+       struct twl6030_gpadc_data *gpadc;
+       struct twl6030_gpadc_request *r;
+       int len, i;
+
+       gpadc = container_of(ws, struct twl6030_gpadc_data, ws);
+       mutex_lock(&gpadc->lock);
+
+       for (i = 0; i < TWL6030_GPADC_NUM_METHODS; i++) {
+
+               r = &gpadc->requests[i];
+
+               /* No pending results for this method, move to next one */
+               if (!r->result_pending)
+                       continue;
+
+               method = &twl6030_conversion_methods[r->method];
+
+               /* Read results */
+               len = twl6030_gpadc_read_channels(gpadc, method->rbase,
+                                                r->channels, r);
+
+               /* Return results to caller */
+               if (r->func_cb != NULL) {
+                       r->func_cb(r);
+                       r->func_cb = NULL;
+               }
+
+               /* Free request */
+               r->result_pending = 0;
+               r->active         = 0;
+       }
+
+       mutex_unlock(&gpadc->lock);
+}
+
+static int twl6030_gpadc_set_irq(struct twl6030_gpadc_data *gpadc,
+               struct twl6030_gpadc_request *req)
+{
+       struct twl6030_gpadc_request *p;
+
+       p = &gpadc->requests[req->method];
+       p->channels = req->channels;
+       p->method = req->method;
+       p->func_cb = req->func_cb;
+       p->type = req->type;
+
+       twl6030_gpadc_enable_irq(req->method);
+
+       return 0;
+}
+
+static inline void
+twl6030_gpadc_start_conversion(struct twl6030_gpadc_data *gpadc,
+                              int conv_method)
+{
+       const struct twl6030_gpadc_conversion_method *method;
+
+       method = &twl6030_conversion_methods[conv_method];
+       twl_i2c_write_u8(TWL6030_MODULE_ID1, GPADCS, REG_TOGGLE1);
+
+       switch (conv_method) {
+       case TWL6030_GPADC_SW2:
+               twl6030_gpadc_write(gpadc, method->ctrl, method->enable);
+               break;
+       case TWL6030_GPADC_RT:
+       default:
+               break;
+       }
+}
+
+static int twl6030_gpadc_wait_conversion_ready(
+               struct twl6030_gpadc_data *gpadc,
+               unsigned int timeout_ms, u8 status_reg)
+{
+       unsigned long timeout;
+
+       timeout = jiffies + msecs_to_jiffies(timeout_ms);
+       do {
+               u8 reg;
+
+               reg = twl6030_gpadc_read(gpadc, status_reg);
+               if (!(reg & TWL6030_GPADC_BUSY) && (reg & TWL6030_GPADC_EOC_SW))
+                       return 0;
+       } while (!time_after(jiffies, timeout));
+
+       return -EAGAIN;
+}
+
+/* locks held by caller */
+static int _twl6030_gpadc_conversion(struct twl6030_gpadc_request *req,
+       const struct twl6030_gpadc_conversion_method *method)
+{
+       u8 ch_msb, ch_lsb, ch_isb;
+       int ret = 0;
+
+       if (req->method == TWL6030_GPADC_RT) {
+               ch_msb = (req->channels >> 16) & 0x01;
+               ch_isb = (req->channels >> 8) & 0xff;
+               ch_lsb = req->channels & 0xff;
+               twl6030_gpadc_write(the_gpadc, method->sel + 2, ch_msb);
+               twl6030_gpadc_write(the_gpadc, method->sel + 1, ch_isb);
+               twl6030_gpadc_write(the_gpadc, method->sel, ch_lsb);
+       }
+
+       if ((req->type == TWL6030_GPADC_IRQ_ONESHOT) &&
+                (req->func_cb != NULL)) {
+               twl6030_gpadc_set_irq(the_gpadc, req);
+               twl6030_gpadc_start_conversion(the_gpadc, req->method);
+               the_gpadc->requests[req->method].active = 1;
+               ret = 0;
+               goto out;
+       }
+
+       /* With RT method we should not be here anymore */
+       if (req->method == TWL6030_GPADC_RT) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       twl6030_gpadc_start_conversion(the_gpadc, req->method);
+       the_gpadc->requests[req->method].active = 1;
+
+       /* Wait until conversion is ready (ctrl register returns EOC) */
+       ret = twl6030_gpadc_wait_conversion_ready(the_gpadc, 5, method->ctrl);
+       if (ret) {
+               dev_dbg(the_gpadc->dev, "conversion timeout!\n");
+               the_gpadc->requests[req->method].active = 0;
+               goto out;
+       }
+
+       ret = twl6030_gpadc_read_channels(the_gpadc, method->rbase,
+                                         req->channels, req);
+       the_gpadc->requests[req->method].active = 0;
+out:
+       return ret;
+}
+
+/* locks held by caller */
+static int _twl6032_gpadc_conversion(struct twl6030_gpadc_request *req,
+       const struct twl6030_gpadc_conversion_method *method)
+{
+       int i, ret, count = 0, channelcnt = 0;
+       u8 ch_msb, ch_lsb, ch_isb;
+
+       if ((req->type == TWL6030_GPADC_IRQ_ONESHOT) &&
+               (req->func_cb == NULL)) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       for (i = 0; i < TWL6032_GPADC_MAX_CHANNELS; i++)
+               if (req->channels & BIT(i))
+                       channelcnt++;
+
+       if (req->method == TWL6030_GPADC_RT) {
+               /*
+                * For the TWL6032 real time conversion
+                * maximum channels count is 2
+                */
+               if ((req->type != TWL6030_GPADC_IRQ_ONESHOT) ||
+                        (channelcnt > 2)) {
+                       ret = -EINVAL;
+                       goto out;
+               }
+
+               ch_msb = (req->channels >> 16) & 0x07;
+               ch_isb = (req->channels >> 8) & 0xff;
+               ch_lsb = req->channels & 0xff;
+               twl6030_gpadc_write(the_gpadc, method->sel + 2, ch_msb);
+               twl6030_gpadc_write(the_gpadc, method->sel + 1, ch_isb);
+               twl6030_gpadc_write(the_gpadc, method->sel, ch_lsb);
+       }
+
+       /*
+        * For the TWL6032 Asynchronous Conversion
+        * maximum channels count is 1
+        */
+       if ((req->method == TWL6030_GPADC_SW2) &&
+                (req->type == TWL6030_GPADC_IRQ_ONESHOT)) {
+               if (channelcnt > 1) {
+                       ret = -EINVAL;
+                       goto out;
+               }
+
+               for (i = 0; i < TWL6032_GPADC_MAX_CHANNELS; i++) {
+                       if (!(req->channels & BIT(i)))
+                               continue;
+
+                       /* select the ADC channel to be read */
+                       twl6030_gpadc_write(the_gpadc, method->sel, i);
+               }
+       }
+
+       if (req->type == TWL6030_GPADC_IRQ_ONESHOT) {
+               twl6030_gpadc_set_irq(the_gpadc, req);
+               twl6030_gpadc_start_conversion(the_gpadc, req->method);
+               the_gpadc->requests[req->method].active = 1;
+               ret = 0;
+               goto out;
+       }
+
+       for (i = 0; i < TWL6032_GPADC_MAX_CHANNELS; i++) {
+               if (!(req->channels & BIT(i)))
+                       continue;
+
+               /* select the ADC channel to be read */
+               twl6030_gpadc_write(the_gpadc, method->sel, i);
+
+               twl6030_gpadc_start_conversion(the_gpadc, req->method);
+               the_gpadc->requests[req->method].active = 1;
+
+               /* Wait until conversion is ready (ctrl register is EOC) */
+               ret = twl6030_gpadc_wait_conversion_ready(the_gpadc, 5,
+                               method->ctrl);
+               if (ret) {
+                       dev_dbg(the_gpadc->dev, "conversion timeout!\n");
+                       the_gpadc->requests[req->method].active = 0;
+                       goto out;
+               }
+
+               ret = twl6030_gpadc_read_channels(the_gpadc, method->rbase,
+                                       1 << i, req);
+               if (!ret)
+                       dev_err(the_gpadc->dev, "%s: channel error %d\n",
+                                       __func__, i);
+
+               count += ret;
+               the_gpadc->requests[req->method].active = 0;
+       }
+       ret = count;
+out:
+       return ret;
+}
+
+int twl6030_gpadc_conversion(struct twl6030_gpadc_request *req)
+{
+       const struct twl6030_gpadc_conversion_method *method;
+       int ret = 0;
+
+       if (unlikely(!req))
+               return -EINVAL;
+
+       if (!the_gpadc)
+               return -EAGAIN;
+
+       mutex_lock(&the_gpadc->lock);
+
+       if (req->method >= TWL6030_GPADC_NUM_METHODS) {
+               dev_err(the_gpadc->dev, "unsupported conversion method\n");
+               ret = -EINVAL;
+               goto out;
+       }
+
+       /* Do we have a conversion request ongoing */
+       if (the_gpadc->requests[req->method].active) {
+               ret = -EBUSY;
+               goto out;
+       }
+
+       method = &twl6030_conversion_methods[req->method];
+
+       if (the_gpadc->features & TWL6032_SUBCLASS)
+               ret = _twl6032_gpadc_conversion(req, method);
+       else
+               ret = _twl6030_gpadc_conversion(req, method);
+
+out:
+       mutex_unlock(&the_gpadc->lock);
+
+       return ret;
+}
+EXPORT_SYMBOL(twl6030_gpadc_conversion);
+
+static ssize_t show_channel(struct device *dev,
+               struct device_attribute *devattr, char *buf)
+{
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct twl6030_gpadc_request req;
+       int temp = 0;
+       int ret;
+
+       req.channels = (1 << attr->index);
+       req.method = TWL6030_GPADC_SW2;
+       req.active = 0;
+       req.func_cb = NULL;
+       ret = twl6030_gpadc_conversion(&req);
+       if (ret < 0)
+               return ret;
+
+       if (req.rbuf[attr->index] > 0)
+               temp = req.rbuf[attr->index];
+
+       ret = sprintf(buf, "%d\n", temp);
+
+       return ret;
+}
+
+static ssize_t show_raw_code(struct device *dev,
+               struct device_attribute *devattr, char *buf)
+{
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct twl6030_gpadc_request req;
+       int temp = 0;
+       int ret;
+
+       req.channels = (1 << attr->index);
+       req.method = TWL6030_GPADC_SW2;
+       req.active = 0;
+       req.func_cb = NULL;
+       ret = twl6030_gpadc_conversion(&req);
+       if (ret < 0)
+               return ret;
+
+       if (req.buf[attr->index].raw_channel_value > 0)
+               temp = req.buf[attr->index].raw_code;
+
+       ret = sprintf(buf, "%d\n", temp);
+
+       return ret;
+}
+
+#define in_gain(index) \
+static SENSOR_DEVICE_ATTR(in##index##_gain, S_IRUGO|S_IWUSR, show_gain, \
+       set_gain, index); \
+static SENSOR_DEVICE_ATTR(in##index##_offset, S_IRUGO|S_IWUSR, show_offset, \
+       set_offset, index)
+
+in_gain(0);
+in_gain(1);
+in_gain(2);
+in_gain(3);
+in_gain(4);
+in_gain(5);
+in_gain(6);
+in_gain(7);
+in_gain(8);
+in_gain(9);
+in_gain(10);
+in_gain(11);
+in_gain(12);
+in_gain(13);
+in_gain(14);
+in_gain(15);
+in_gain(16);
+
+#define in_channel(index) \
+static SENSOR_DEVICE_ATTR(in##index##_channel, S_IRUGO, show_channel, \
+       NULL, index); \
+static SENSOR_DEVICE_ATTR(in##index##_raw_code, S_IRUGO, show_raw_code, \
+       NULL, index)
+
+in_channel(0);
+in_channel(1);
+in_channel(2);
+in_channel(3);
+in_channel(4);
+in_channel(5);
+in_channel(6);
+in_channel(7);
+in_channel(8);
+in_channel(9);
+in_channel(10);
+in_channel(11);
+in_channel(12);
+in_channel(13);
+in_channel(14);
+in_channel(15);
+in_channel(16);
+in_channel(17);
+in_channel(18);
+
+#define IN_ATTRS(X)\
+       &sensor_dev_attr_in##X##_gain.dev_attr.attr,    \
+       &sensor_dev_attr_in##X##_offset.dev_attr.attr   \
+
+#define IN_ATTRS_CHANNEL(X)\
+       &sensor_dev_attr_in##X##_channel.dev_attr.attr,         \
+       &sensor_dev_attr_in##X##_raw_code.dev_attr.attr \
+
+static struct attribute *twl6030_gpadc_attributes[] = {
+       IN_ATTRS(0),
+       IN_ATTRS(1),
+       IN_ATTRS(2),
+       IN_ATTRS(3),
+       IN_ATTRS(4),
+       IN_ATTRS(5),
+       IN_ATTRS(6),
+       IN_ATTRS(7),
+       IN_ATTRS(8),
+       IN_ATTRS(9),
+       IN_ATTRS(10),
+       IN_ATTRS(11),
+       IN_ATTRS(12),
+       IN_ATTRS(13),
+       IN_ATTRS(14),
+       IN_ATTRS(15),
+       IN_ATTRS(16),
+       IN_ATTRS_CHANNEL(0),
+       IN_ATTRS_CHANNEL(1),
+       IN_ATTRS_CHANNEL(2),
+       IN_ATTRS_CHANNEL(3),
+       IN_ATTRS_CHANNEL(4),
+       IN_ATTRS_CHANNEL(5),
+       IN_ATTRS_CHANNEL(6),
+       IN_ATTRS_CHANNEL(7),
+       IN_ATTRS_CHANNEL(8),
+       IN_ATTRS_CHANNEL(9),
+       IN_ATTRS_CHANNEL(10),
+       IN_ATTRS_CHANNEL(11),
+       IN_ATTRS_CHANNEL(12),
+       IN_ATTRS_CHANNEL(13),
+       IN_ATTRS_CHANNEL(14),
+       IN_ATTRS_CHANNEL(15),
+       IN_ATTRS_CHANNEL(16),
+       IN_ATTRS_CHANNEL(17),
+       IN_ATTRS_CHANNEL(18),
+       NULL
+};
+
+static const struct attribute_group twl6030_gpadc_group = {
+       .attrs = twl6030_gpadc_attributes,
+};
+
+static long twl6030_gpadc_ioctl(struct file *filp, unsigned int cmd,
+                              unsigned long arg)
+{
+       struct twl6030_gpadc_user_parms par;
+       int val, ret;
+
+       ret = copy_from_user(&par, (void __user *) arg, sizeof(par));
+       if (ret) {
+               dev_dbg(the_gpadc->dev, "copy_from_user: %d\n", ret);
+               return -EACCES;
+       }
+
+       switch (cmd) {
+       case TWL6030_GPADC_IOCX_ADC_READ:
+       case TWL6030_GPADC_IOCX_ADC_RAW_READ: {
+               struct twl6030_gpadc_request req;
+               if (the_gpadc->features & TWL6032_SUBCLASS) {
+                       if (par.channel >= TWL6032_GPADC_MAX_CHANNELS)
+                               return -EINVAL;
+               } else {
+                       if (par.channel >= TWL6030_GPADC_MAX_CHANNELS)
+                               return -EINVAL;
+               }
+
+               req.channels = (1 << par.channel);
+               req.method      = TWL6030_GPADC_SW2;
+               req.func_cb     = NULL;
+
+               val = twl6030_gpadc_conversion(&req);
+               if (likely(val > 0)) {
+                       par.status = 0;
+                       if (cmd == TWL6030_GPADC_IOCX_ADC_READ)
+                               par.result = (u16)req.rbuf[par.channel];
+                       else
+                               par.result = (u16)req.buf[par.channel].raw_code;
+
+               } else if (val == 0) {
+                       par.status = -ENODATA;
+               } else {
+                       par.status = val;
+               }
+               break;
+                                            }
+       default:
+               return -EINVAL;
+       }
+
+       ret = copy_to_user((void __user *) arg, &par, sizeof(par));
+       if (ret) {
+               dev_dbg(the_gpadc->dev, "copy_to_user: %d\n", ret);
+               return -EACCES;
+       }
+
+       return 0;
+}
+
+static const struct file_operations twl6030_gpadc_fileops = {
+       .owner = THIS_MODULE,
+       .unlocked_ioctl = twl6030_gpadc_ioctl
+};
+
+static struct miscdevice twl6030_gpadc_device = {
+       .minor = MISC_DYNAMIC_MINOR,
+       .name = "twl6030-gpadc",
+       .fops = &twl6030_gpadc_fileops
+};
+
+static int twl6030_calibration(void)
+{
+       s8 delta_error1 = 0, delta_error2 = 0;
+       s16 ideal_code1, ideal_code2;
+       s32 gain_error_1;
+       s32 offset_error;
+       u8 index;
+       int ret;
+
+       for (index = 0; index < TWL6030_GPADC_MAX_CHANNELS; index++) {
+               if (~calibration_bit_map & (1 << index))
+                       continue;
+
+               ret = twl_i2c_read_u8(TWL6030_MODULE_ID2, &delta_error1,
+                                       twl6030_trim_addr[index]);
+               if (ret < 0)
+                       return ret;
+
+               twl_i2c_read_u8(TWL6030_MODULE_ID2, &delta_error2,
+                                       (twl6030_trim_addr[index] + 1));
+               if (ret < 0)
+                       return ret;
+
+               /* convert 7 bit to 8 bit signed number */
+               delta_error1 = ((s8)(delta_error1 << 1) >> 1);
+               delta_error2 = ((s8)(delta_error2 << 1) >> 1);
+               ideal_code1 = twl6030_ideal[index].code1;
+               ideal_code2 = twl6030_ideal[index].code2;
+
+               gain_error_1 = (delta_error2 - delta_error1) * SCALE
+                                               / (ideal_code2 - ideal_code1);
+               offset_error = delta_error1 * SCALE - gain_error_1
+                                                       *  ideal_code1;
+               twl6030_calib_tbl[index].gain_error = gain_error_1 + SCALE;
+               twl6030_calib_tbl[index].offset_error = offset_error;
+       }
+
+       return 0;
+}
+
+static int twl6032_calibration(struct twl6030_gpadc_data *gpadc)
+{
+       int chn, d1 = 0, d2 = 0, b, k, gain, x1, x2, temp;
+       u8 trim_regs[17];
+       int ret;
+
+       ret = twl_i2c_read(TWL6030_MODULE_ID2, trim_regs + 1,
+                       TWL6032_GPADC_TRIM1, 16);
+       if (ret < 0)
+               return ret;
+
+       /* Loop to calculate the value needed for returning voltages from
+        * GPADC not values.
+        *
+        * gain is calculated to 3 decimal places fixed point.
+        */
+       for (chn = 0; chn < TWL6032_GPADC_MAX_CHANNELS; chn++) {
+
+               switch (chn) {
+               case 0:
+               case 1:
+               case 2:
+               case 3:
+               case 4:
+               case 5:
+               case 6:
+               case 11:
+               case 12:
+               case 13:
+               case 14:
+                       /* D1 */
+                       d1 = (trim_regs[3] & 0x1F) << 2;
+                       d1 |= (trim_regs[1] & 0x06) >> 1;
+                       if (trim_regs[1] & 0x01)
+                               d1 = -d1;
+
+                       /* D2 */
+                       d2 = (trim_regs[4] & 0x3F) << 2;
+                       d2 |= (trim_regs[2] & 0x06) >> 1;
+                       if (trim_regs[2] & 0x01)
+                               d2 = -d2;
+                       break;
+               case 8:
+                       /* D1 */
+                       temp = (trim_regs[3] & 0x1F) << 2;
+                       temp |= (trim_regs[1] & 0x06) >> 1;
+                       if (trim_regs[1] & 0x01)
+                               temp = -temp;
+
+                       d1 = (trim_regs[8] & 0x18) << 1;
+                       d1 |= (trim_regs[7] & 0x1E) >> 1;
+                       if (trim_regs[7] & 0x01)
+                               d1 = -d1;
+
+                       d1 += temp;
+
+                       /* D2 */
+                       temp = (trim_regs[4] & 0x3F) << 2;
+                       temp |= (trim_regs[2] & 0x06) >> 1;
+                       if (trim_regs[2] & 0x01)
+                               temp = -temp;
+
+                       d2 = (trim_regs[10] & 0x1F) << 2;
+                       d2 |= (trim_regs[8] & 0x06) >> 1;
+                       if (trim_regs[8] & 0x01)
+                               d2 = -d2;
+
+                       d2 += temp;
+                       break;
+               case 9:
+                       /* D1 */
+                       temp = (trim_regs[3] & 0x1F) << 2;
+                       temp |= (trim_regs[1] & 0x06) >> 1;
+                       if (trim_regs[1] & 0x01)
+                               temp = -temp;
+
+                       d1 = (trim_regs[14] & 0x18) << 1;
+                       d1 |= (trim_regs[12] & 0x1E) >> 1;
+                       if (trim_regs[12] & 0x01)
+                               d1 = -d1;
+
+                       d1 += temp;
+
+                       /* D2 */
+                       temp = (trim_regs[4] & 0x3F) << 2;
+                       temp |= (trim_regs[2] & 0x06) >> 1;
+                       if (trim_regs[2] & 0x01)
+                               temp = -temp;
+
+                       d2 = (trim_regs[16] & 0x1F) << 2;
+                       d2 |= (trim_regs[14] & 0x06) >> 1;
+                       if (trim_regs[14] & 0x01)
+                               d2 = -d2;
+
+                       d2 += temp;
+               case 10:
+                       /* D1 */
+                       d1 = (trim_regs[11] & 0x0F) << 3;
+                       d1 |= (trim_regs[9] & 0x0E) >> 1;
+                       if (trim_regs[1] & 0x01)
+                               d1 = -d1;
+
+                       /* D2 */
+                       d2 = (trim_regs[15] & 0x0F) << 2;
+                       d2 |= (trim_regs[13] & 0x0E) >> 1;
+                       if (trim_regs[13] & 0x01)
+                               d2 = -d2;
+                       break;
+               case 7:
+               case 18:
+                       /* D1 */
+                       temp = (trim_regs[3] & 0x1F) << 2;
+                       temp |= (trim_regs[1] & 0x06) >> 1;
+                       if (trim_regs[1] & 0x01)
+                               temp = -temp;
+
+                       d1 = (trim_regs[1] & 0x7E) >> 1;
+                       if (trim_regs[12] & 0x01)
+                               d1 = -d1;
+
+                       d1 += temp;
+
+                       /* D2 */
+                       temp = (trim_regs[4] & 0x3F) << 2;
+                       temp |= (trim_regs[2] & 0x06) >> 1;
+                       if (trim_regs[2] & 0x01)
+                               temp = -temp;
+
+                       d2 = (trim_regs[6] & 0x7F) >> 1;
+                       if (trim_regs[14] & 0x01)
+                               d2 = -d2;
+
+                       d2 += temp;
+                       break;
+               default:
+                       /* No data for other channels */
+                       continue;
+               }
+
+               dev_dbg(gpadc->dev, "GPADC d1   for Chn: %d = %d\n", chn, d1);
+               dev_dbg(gpadc->dev, "GPADC d2   for Chn: %d = %d\n", chn, d2);
+
+               /* Gain */
+               gain = ((twl6032_ideal[chn].v2 -
+                       twl6032_ideal[chn].v1) * 1000) /
+                       ((twl6032_ideal[chn].code2 -
+                       twl6032_ideal[chn].code1));
+
+               x1 = twl6032_ideal[chn].code1;
+               x2 = twl6032_ideal[chn].code2;
+
+               /* k */
+               k = 1000 + (((d2 - d1) * 1000) / (x2 - x1));
+
+               /* b */
+               b = (d1 * 1000) - (k - 1000) * x1;
+
+               gpadc->twl6032_cal_tbl[chn].gain = gain;
+               gpadc->twl6032_cal_tbl[chn].gain_error = k;
+               gpadc->twl6032_cal_tbl[chn].offset_error = b;
+
+               dev_dbg(gpadc->dev, "GPADC x1   for Chn: %d = %d\n", chn, x1);
+               dev_dbg(gpadc->dev, "GPADC x2   for Chn: %d = %d\n", chn, x2);
+               dev_dbg(gpadc->dev, "GPADC Gain for Chn: %d = %d\n", chn, gain);
+               dev_dbg(gpadc->dev, "GPADC k    for Chn: %d = %d\n", chn, k);
+               dev_dbg(gpadc->dev, "GPADC b    for Chn: %d = %d\n", chn, b);
+
+       }
+
+       return 0;
+}
+
+static int __devinit twl6030_gpadc_probe(struct platform_device *pdev)
+{
+       struct twl6030_gpadc_data *gpadc;
+       struct twl4030_madc_platform_data *pdata = pdev->dev.platform_data;
+       int irq;
+       int irq_rt;
+       int ret = 0;
+
+       gpadc = kzalloc(sizeof *gpadc, GFP_KERNEL);
+       if (!gpadc)
+               return -ENOMEM;
+
+       if (!pdata) {
+               dev_dbg(&pdev->dev, "platform_data not available\n");
+               ret = -EINVAL;
+               goto err_pdata;
+       }
+
+       if (pdata->features & TWL6032_SUBCLASS) {
+               gpadc->twl6032_cal_tbl = kzalloc(
+                               sizeof(struct twl6032_chnl_calib) *
+                               TWL6032_GPADC_MAX_CHANNELS,
+                               GFP_KERNEL);
+               if (!gpadc->twl6032_cal_tbl) {
+                       ret = -ENOMEM;
+                       goto err_pdata;
+               }
+       }
+
+       gpadc->dev = &pdev->dev;
+
+       gpadc->features = pdata->features;
+
+       twl6030_conversion_methods = twl6030_conversion_methods_table;
+
+       if (gpadc->features & TWL6032_SUBCLASS)
+               twl6030_conversion_methods = twl6032_conversion_methods_table;
+
+       ret = misc_register(&twl6030_gpadc_device);
+       if (ret) {
+               dev_dbg(&pdev->dev, "could not register misc_device\n");
+               goto err_misc;
+       }
+
+       irq_rt = platform_get_irq(pdev, 0);
+       if (irq_rt < 0) {
+               dev_err(&pdev->dev, "failed to get irq\n");
+               goto err_irq;
+       }
+
+       ret = request_threaded_irq(irq_rt, NULL, twl6030_gpadc_irq_handler,
+               0, "twl6030_gpadc", &gpadc->requests[TWL6030_GPADC_RT]);
+       if (ret) {
+               dev_dbg(&pdev->dev, "could not request irq\n");
+               goto err_irq;
+       }
+
+       irq = platform_get_irq(pdev, 1);
+       if (irq < 0) {
+               dev_err(&pdev->dev, "failed to get irq\n");
+               goto err_irq_rt;
+       }
+
+       ret = request_threaded_irq(irq, NULL, twl6030_gpadc_irq_handler,
+               0, "twl6030_gpadc", &gpadc->requests[TWL6030_GPADC_SW2]);
+       if (ret) {
+               dev_dbg(&pdev->dev, "could not request irq\n");
+               goto err_irq_rt;
+       }
+
+       platform_set_drvdata(pdev, gpadc);
+       mutex_init(&gpadc->lock);
+       INIT_WORK(&gpadc->ws, twl6030_gpadc_work);
+
+       if (gpadc->features & TWL6032_SUBCLASS)
+               ret = twl6032_calibration(gpadc);
+       else
+               ret = twl6030_calibration();
+       if (ret < 0) {
+               dev_err(&pdev->dev, "Failed to read calibration registers\n");
+               goto err_calib;
+       }
+       the_gpadc = gpadc;
+
+       ret = sysfs_create_group(&pdev->dev.kobj, &twl6030_gpadc_group);
+       if (ret)
+               dev_err(&pdev->dev, "could not create sysfs files\n");
+
+       return 0;
+
+err_calib:
+       free_irq(irq, &gpadc->requests[TWL6030_GPADC_SW2]);
+err_irq_rt:
+       free_irq(irq_rt, &gpadc->requests[TWL6030_GPADC_RT]);
+err_irq:
+       misc_deregister(&twl6030_gpadc_device);
+
+err_misc:
+       if (pdata->features & TWL6032_SUBCLASS)
+               kfree(gpadc->twl6032_cal_tbl);
+err_pdata:
+       kfree(gpadc);
+
+       return ret;
+}
+
+static int __devexit twl6030_gpadc_remove(struct platform_device *pdev)
+{
+       struct twl6030_gpadc_data *gpadc = platform_get_drvdata(pdev);
+
+       twl6030_gpadc_disable_irq(TWL6030_GPADC_RT);
+       twl6030_gpadc_disable_irq(TWL6030_GPADC_SW2);
+       free_irq(platform_get_irq(pdev, 0), gpadc);
+       sysfs_remove_group(&pdev->dev.kobj, &twl6030_gpadc_group);
+       cancel_work_sync(&gpadc->ws);
+       misc_deregister(&twl6030_gpadc_device);
+
+       return 0;
+}
+
+static int twl6030_gpadc_suspend(struct device *pdev)
+{
+       int ret;
+
+       ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, GPADCR, REG_TOGGLE1);
+       if (ret)
+               pr_err("%s: Error reseting GPADC (%d)!\n", __func__, ret);
+
+       return 0;
+};
+
+static int twl6030_gpadc_resume(struct device *pdev)
+{
+       int ret;
+
+       ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, GPADCS, REG_TOGGLE1);
+       if (ret)
+               pr_err("%s: Error setting GPADC (%d)!\n", __func__, ret);
+
+       return 0;
+};
+static const struct dev_pm_ops twl6030_gpadc_pm_ops = {
+       .suspend = twl6030_gpadc_suspend,
+       .resume = twl6030_gpadc_resume,
+};
+
+static struct platform_driver twl6030_gpadc_driver = {
+       .probe          = twl6030_gpadc_probe,
+       .remove         = __devexit_p(twl6030_gpadc_remove),
+       .driver         = {
+               .name   = "twl6030_gpadc",
+               .owner  = THIS_MODULE,
+               .pm = &twl6030_gpadc_pm_ops,
+       },
+};
+
+static int __init twl6030_gpadc_init(void)
+{
+       return platform_driver_register(&twl6030_gpadc_driver);
+}
+module_init(twl6030_gpadc_init);
+
+static void __exit twl6030_gpadc_exit(void)
+{
+       platform_driver_unregister(&twl6030_gpadc_driver);
+}
+module_exit(twl6030_gpadc_exit);
+
+MODULE_ALIAS("platform:twl6030-gpadc");
+MODULE_AUTHOR("Texas Instruments Inc.");
+MODULE_DESCRIPTION("twl6030 ADC driver");
+MODULE_LICENSE("GPL");
index eb3b5f88e566c73e6599705649c83ec9b2758f2d..eaa741203bbdc2ea5c65a8a0e972b54ba82863cf 100644 (file)
@@ -37,6 +37,8 @@
 #include <linux/kthread.h>
 #include <linux/i2c/twl.h>
 #include <linux/platform_device.h>
+#include <linux/suspend.h>
+#include <linux/reboot.h>
 
 #include "twl-core.h"
 
  *
  */
 
-static int twl6030_interrupt_mapping[24] = {
+static int twl6030_interrupt_mapping_table[24] = {
        PWR_INTR_OFFSET,        /* Bit 0        PWRON                   */
        PWR_INTR_OFFSET,        /* Bit 1        RPWRON                  */
-       PWR_INTR_OFFSET,        /* Bit 2        BAT_VLOW                */
+       TWL_VLOW_INTR_OFFSET,   /* Bit 2        BAT_VLOW                */
        RTC_INTR_OFFSET,        /* Bit 3        RTC_ALARM               */
        RTC_INTR_OFFSET,        /* Bit 4        RTC_PERIOD              */
        HOTDIE_INTR_OFFSET,     /* Bit 5        HOT_DIE                 */
@@ -68,7 +70,7 @@ static int twl6030_interrupt_mapping[24] = {
        MMCDETECT_INTR_OFFSET,  /* Bit 11       MMC                     */
        RSV_INTR_OFFSET,        /* Bit 12       Reserved                */
        MADC_INTR_OFFSET,       /* Bit 13       GPADC_RT_EOC            */
-       MADC_INTR_OFFSET,       /* Bit 14       GPADC_SW_EOC            */
+       GPADCSW_INTR_OFFSET,    /* Bit 14       GPADC_SW_EOC            */
        GASGAUGE_INTR_OFFSET,   /* Bit 15       CC_AUTOCAL              */
 
        USBOTG_INTR_OFFSET,     /* Bit 16       ID_WKUP                 */
@@ -80,11 +82,85 @@ static int twl6030_interrupt_mapping[24] = {
        CHARGERFAULT_INTR_OFFSET,       /* Bit 22       INT_CHRG        */
        RSV_INTR_OFFSET,        /* Bit 23       Reserved                */
 };
+
+static int twl6032_interrupt_mapping_table[24] = {
+       PWR_INTR_OFFSET,        /* Bit 0        PWRON                   */
+       PWR_INTR_OFFSET,        /* Bit 1        RPWRON                  */
+       TWL_VLOW_INTR_OFFSET,   /* Bit 2        SYS_VLOW                */
+       RTC_INTR_OFFSET,        /* Bit 3        RTC_ALARM               */
+       RTC_INTR_OFFSET,        /* Bit 4        RTC_PERIOD              */
+       HOTDIE_INTR_OFFSET,     /* Bit 5        HOT_DIE                 */
+       SMPSLDO_INTR_OFFSET,    /* Bit 6        VXXX_SHORT              */
+       PWR_INTR_OFFSET,        /* Bit 7        SPDURATION              */
+
+       PWR_INTR_OFFSET,        /* Bit 8        WATCHDOG                */
+       BATDETECT_INTR_OFFSET,  /* Bit 9        BAT                     */
+       SIMDETECT_INTR_OFFSET,  /* Bit 10       SIM                     */
+       MMCDETECT_INTR_OFFSET,  /* Bit 11       MMC                     */
+       MADC_INTR_OFFSET,       /* Bit 12       GPADC_RT_EOC            */
+       GPADCSW_INTR_OFFSET,    /* Bit 13       GPADC_SW_EOC            */
+       GASGAUGE_INTR_OFFSET,   /* Bit 14       CC_EOC          */
+       GASGAUGE_INTR_OFFSET,   /* Bit 15       CC_AUTOCAL              */
+
+       USBOTG_INTR_OFFSET,     /* Bit 16       ID_WKUP                 */
+       USBOTG_INTR_OFFSET,     /* Bit 17       VBUS_WKUP               */
+       USBOTG_INTR_OFFSET,     /* Bit 18       ID                      */
+       USB_PRES_INTR_OFFSET,   /* Bit 19       VBUS                    */
+       CHARGER_INTR_OFFSET,    /* Bit 20       CHRG_CTRL               */
+       CHARGERFAULT_INTR_OFFSET,       /* Bit 21       EXT_CHRG        */
+       CHARGERFAULT_INTR_OFFSET,       /* Bit 22       INT_CHRG        */
+       RSV_INTR_OFFSET,        /* Bit 23       Reserved                */
+};
+
+static int *twl6030_interrupt_mapping = twl6030_interrupt_mapping_table;
 /*----------------------------------------------------------------------*/
 
-static unsigned twl6030_irq_base;
+static unsigned twl6030_irq_base, twl6030_irq_end;
+static int twl_irq;
+static bool twl_irq_wake_enabled;
 
+static struct task_struct *task;
 static struct completion irq_event;
+static atomic_t twl6030_wakeirqs = ATOMIC_INIT(0);
+
+static u8 vbatmin_hi_threshold;
+
+static int twl6030_irq_pm_notifier(struct notifier_block *notifier,
+                                  unsigned long pm_event, void *unused)
+{
+       int chained_wakeups;
+
+       switch (pm_event) {
+       case PM_SUSPEND_PREPARE:
+               chained_wakeups = atomic_read(&twl6030_wakeirqs);
+
+               if (chained_wakeups && !twl_irq_wake_enabled) {
+                       if (enable_irq_wake(twl_irq))
+                               pr_err("twl6030 IRQ wake enable failed\n");
+                       else
+                               twl_irq_wake_enabled = true;
+               } else if (!chained_wakeups && twl_irq_wake_enabled) {
+                       disable_irq_wake(twl_irq);
+                       twl_irq_wake_enabled = false;
+               }
+
+               disable_irq(twl_irq);
+               break;
+
+       case PM_POST_SUSPEND:
+               enable_irq(twl_irq);
+               break;
+
+       default:
+               break;
+       }
+
+       return NOTIFY_DONE;
+}
+
+static struct notifier_block twl6030_irq_pm_notifier_block = {
+       .notifier_call = twl6030_irq_pm_notifier,
+};
 
 /*
  * This thread processes interrupts reported by the Primary Interrupt Handler.
@@ -104,6 +180,7 @@ static int twl6030_irq_thread(void *data)
                u8 bytes[4];
                u32 int_sts;
                } sts;
+               u32 int_sts; /* sts.int_sts converted to CPU endianness */
 
                /* Wait for IRQ, then read PIH irq status (also blocking) */
                wait_for_completion_interruptible(&irq_event);
@@ -135,9 +212,10 @@ static int twl6030_irq_thread(void *data)
                if (sts.bytes[2] & 0x10)
                        sts.bytes[2] |= 0x08;
 
-               for (i = 0; sts.int_sts; sts.int_sts >>= 1, i++) {
+               int_sts = le32_to_cpu(sts.int_sts);
+               for (i = 0; int_sts; int_sts >>= 1, i++) {
                        local_irq_disable();
-                       if (sts.int_sts & 0x1) {
+                       if (int_sts & 0x1) {
                                int module_irq = twl6030_irq_base +
                                        twl6030_interrupt_mapping[i];
                                generic_handle_irq(module_irq);
@@ -145,8 +223,17 @@ static int twl6030_irq_thread(void *data)
                        }
                local_irq_enable();
                }
-               ret = twl_i2c_write(TWL_MODULE_PIH, sts.bytes,
-                               REG_INT_STS_A, 3); /* clear INT_STS_A */
+
+               /*
+                * NOTE:
+                * Simulation confirms that documentation is wrong w.r.t the
+                * interrupt status clear operation. A single *byte* write to
+                * any one of STS_A to STS_C register results in all three
+                * STS registers being reset. Since it does not matter which
+                * value is written, all three registers are cleared on a
+                * single byte write, so we just use 0x0 to clear.
+                */
+               ret = twl_i2c_write_u8(TWL_MODULE_PIH, 0x00, REG_INT_STS_A);
                if (ret)
                        pr_warning("twl6030: I2C error in clearing PIH ISR\n");
 
@@ -172,6 +259,26 @@ static irqreturn_t handle_twl6030_pih(int irq, void *devid)
        return IRQ_HANDLED;
 }
 
+/*
+ * handle_twl6030_vlow() is a threaded BAT_VLOW interrupt handler. BAT_VLOW
+ * is a secondary interrupt generated in twl6030_irq_thread().
+ */
+static irqreturn_t handle_twl6030_vlow(int irq, void *unused)
+{
+       pr_err("twl6030: BAT_VLOW interrupt; threshold=%dmV\n",
+              2300 + (vbatmin_hi_threshold - 0b110) * 50);
+
+#if 1 /* temporary */
+       pr_err("%s: disabling BAT_VLOW interrupt\n", __func__);
+       disable_irq_nosync(twl6030_irq_base + TWL_VLOW_INTR_OFFSET);
+       WARN_ON(1);
+#else
+       pr_emerg("handle_twl6030_vlow: kernel_power_off()\n");
+       kernel_power_off();
+#endif
+       return IRQ_HANDLED;
+}
+
 /*----------------------------------------------------------------------*/
 
 static inline void activate_irq(int irq)
@@ -187,6 +294,16 @@ static inline void activate_irq(int irq)
 #endif
 }
 
+int twl6030_irq_set_wake(struct irq_data *d, unsigned int on)
+{
+       if (on)
+               atomic_inc(&twl6030_wakeirqs);
+       else
+               atomic_dec(&twl6030_wakeirqs);
+
+       return 0;
+}
+
 /*----------------------------------------------------------------------*/
 
 static unsigned twl6030_irq_next;
@@ -261,6 +378,16 @@ int twl6030_mmc_card_detect_config(void)
                                                                        ret);
                return ret;
        }
+
+       ret = twl_i2c_write_u8(TWL6030_MODULE_ID0,
+                       (MMC_MINS_DEB_MASK | MMC_MEXT_DEB_MASK),
+                       TWL6030_MMCDEBOUNCING);
+       if (ret < 0){
+               pr_err("twl6030: Failed to write MMC_MEXT_DEB_MASK %d\n",
+                                                                       ret);
+               return ret;
+       }
+
        return 0;
 }
 EXPORT_SYMBOL(twl6030_mmc_card_detect_config);
@@ -290,16 +417,87 @@ int twl6030_mmc_card_detect(struct device *dev, int slot)
 }
 EXPORT_SYMBOL(twl6030_mmc_card_detect);
 
-int twl6030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end)
+int twl6030_vlow_init(int vlow_irq)
+{
+       int status;
+       u8 val;
+
+       status = twl_i2c_read_u8(TWL_MODULE_PM_SLAVE_RES, &val,
+                       REG_VBATMIN_HI_CFG_STATE);
+       if (status < 0) {
+               pr_err("twl6030: I2C err reading REG_VBATMIN_HI_CFG_STATE: %d\n",
+                               status);
+               return status;
+       }
+
+       status = twl_i2c_write_u8(TWL_MODULE_PM_SLAVE_RES,
+                       val | VBATMIN_VLOW_EN, REG_VBATMIN_HI_CFG_STATE);
+       if (status < 0) {
+               pr_err("twl6030: I2C err writing REG_VBATMIN_HI_CFG_STATE: %d\n",
+                               status);
+               return status;
+       }
+
+       status = twl_i2c_read_u8(TWL_MODULE_PIH, &val, REG_INT_MSK_LINE_A);
+       if (status < 0) {
+               pr_err("twl6030: I2C err reading REG_INT_MSK_LINE_A: %d\n",
+                               status);
+               return status;
+       }
+
+       status = twl_i2c_write_u8(TWL_MODULE_PIH, val & ~VLOW_INT_MASK,
+                       REG_INT_MSK_LINE_A);
+       if (status < 0) {
+               pr_err("twl6030: I2C err writing REG_INT_MSK_LINE_A: %d\n",
+                               status);
+               return status;
+       }
+
+       status = twl_i2c_read_u8(TWL_MODULE_PIH, &val, REG_INT_MSK_STS_A);
+       if (status < 0) {
+               pr_err("twl6030: I2C err reading REG_INT_MSK_STS_A: %d\n",
+                               status);
+               return status;
+       }
+
+       status = twl_i2c_write_u8(TWL_MODULE_PIH, val & ~VLOW_INT_MASK,
+               REG_INT_MSK_STS_A);
+       if (status < 0) {
+               pr_err("twl6030: I2C err writing REG_INT_MSK_STS_A: %d\n",
+                               status);
+               return status;
+       }
+
+       twl_i2c_read_u8(TWL_MODULE_PM_MASTER, &vbatmin_hi_threshold,
+                       TWL6030_VBATMIN_HI_THRESHOLD);
+
+       /* install an irq handler for vlow */
+       status = request_threaded_irq(vlow_irq, NULL, handle_twl6030_vlow,
+                       IRQF_ONESHOT,
+                       "TWL6030-VLOW", handle_twl6030_vlow);
+       if (status < 0) {
+               pr_err("twl6030: could not claim vlow irq %d: %d\n", vlow_irq,
+                               status);
+               return status;
+       }
+
+       return 0;
+}
+
+int twl6030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end,
+                       unsigned long features)
 {
 
        int     status = 0;
        int     i;
-       struct task_struct      *task;
        int ret;
        u8 mask[4];
 
        static struct irq_chip  twl6030_irq_chip;
+
+       if (features & TWL6032_SUBCLASS)
+               twl6030_interrupt_mapping = twl6032_interrupt_mapping_table;
+
        mask[1] = 0xFF;
        mask[2] = 0xFF;
        mask[3] = 0xFF;
@@ -311,6 +509,7 @@ int twl6030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end)
                        REG_INT_STS_A, 3); /* clear INT_STS_A,B,C */
 
        twl6030_irq_base = irq_base;
+       twl6030_irq_end = irq_end;
 
        /* install an irq handler for each of the modules;
         * clone dummy irq_chip since PIH can't *do* anything
@@ -318,10 +517,12 @@ int twl6030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end)
        twl6030_irq_chip = dummy_irq_chip;
        twl6030_irq_chip.name = "twl6030";
        twl6030_irq_chip.irq_set_type = NULL;
+       twl6030_irq_chip.irq_set_wake = twl6030_irq_set_wake;
 
        for (i = irq_base; i < irq_end; i++) {
                irq_set_chip_and_handler(i, &twl6030_irq_chip,
                                         handle_simple_irq);
+               irq_set_chip_data(i, (void *)irq_num);
                activate_irq(i);
        }
 
@@ -344,10 +545,22 @@ int twl6030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end)
                pr_err("twl6030: could not claim irq%d: %d\n", irq_num, status);
                goto fail_irq;
        }
+
+       twl_irq = irq_num;
+       register_pm_notifier(&twl6030_irq_pm_notifier_block);
+
+       status = twl6030_vlow_init(twl6030_irq_base + TWL_VLOW_INTR_OFFSET);
+       if (status < 0)
+               goto fail_vlow;
+
        return status;
-fail_irq:
+
+fail_vlow:
        free_irq(irq_num, &irq_event);
 
+fail_irq:
+       kthread_stop(task);
+
 fail_kthread:
        for (i = irq_base; i < irq_end; i++)
                irq_set_chip_and_handler(i, NULL, NULL);
@@ -356,11 +569,25 @@ fail_kthread:
 
 int twl6030_exit_irq(void)
 {
+       int i;
+       unregister_pm_notifier(&twl6030_irq_pm_notifier_block);
 
-       if (twl6030_irq_base) {
+       if (task)
+               kthread_stop(task);
+
+       if (!twl6030_irq_base || !twl6030_irq_end) {
                pr_err("twl6030: can't yet clean up IRQs?\n");
                return -ENOSYS;
        }
+
+       free_irq(twl6030_irq_base + TWL_VLOW_INTR_OFFSET,
+               handle_twl6030_vlow);
+
+       free_irq(twl_irq, &irq_event);
+
+       for (i = twl6030_irq_base; i < twl6030_irq_end; i++)
+               irq_set_chip_and_handler(i, NULL, NULL);
+
        return 0;
 }
 
diff --git a/drivers/mfd/twl6030-madc.c b/drivers/mfd/twl6030-madc.c
new file mode 100644 (file)
index 0000000..f537ba5
--- /dev/null
@@ -0,0 +1,354 @@
+/*
+ *
+ * TWL6030 MADC module driver-This driver only implements the ADC read
+ * functions
+ *
+ * Copyright (C) 2011 Samsung Telecommunications of America
+ *
+ * Based on twl4030-madc.c
+ * Copyright (C) 2008 Nokia Corporation
+ * Mikko Ylinen <mikko.k.ylinen@nokia.com>
+ *
+ * Amit Kucheria <amit.kucheria@canonical.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.
+ *
+ * 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 St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/i2c/twl.h>
+#include <linux/i2c/twl6030-madc.h>
+#include <linux/module.h>
+#include <linux/stddef.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/mutex.h>
+#include <linux/bitops.h>
+#include <linux/jiffies.h>
+#include <linux/types.h>
+#include <linux/gfp.h>
+#include <linux/err.h>
+#include <linux/wakelock.h>
+
+#define GPADCS         (1 << 1)
+#define GPADCR         (1 << 0)
+#define REG_TOGGLE1    0x90
+
+#define DRIVER_NAME    (twl6030_madc_driver.driver.name)
+static struct platform_driver twl6030_madc_driver;
+
+/*
+ * struct twl6030_madc_data - a container for madc info
+ * @dev - pointer to device structure for madc
+ * @lock - mutex protecting this data structure
+ */
+struct twl6030_madc_data {
+       struct device *dev;
+       struct mutex lock;
+       struct dentry           *file;
+       struct wake_lock wakelock;
+};
+
+static struct twl6030_madc_data *twl6030_madc;
+static u8 gpadc_ctrl_reg;
+
+static inline int twl6030_madc_start_conversion(struct twl6030_madc_data *madc)
+{
+       int ret;
+
+       ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, GPADCS, REG_TOGGLE1);
+       if (ret) {
+               dev_err(madc->dev, "unable to write register 0x%X\n",
+                       REG_TOGGLE1);
+               return ret;
+       }
+
+       udelay(100);
+       ret = twl_i2c_write_u8(TWL_MODULE_MADC, TWL6030_MADC_SP1,
+                               TWL6030_MADC_CTRL_P1);
+       if (ret) {
+               dev_err(madc->dev, "unable to write register 0x%X\n",
+                       TWL6030_MADC_CTRL_P1);
+               return ret;
+       }
+       return 0;
+}
+
+/*
+ * Function that waits for conversion to be ready
+ * @madc - pointer to twl4030_madc_data struct
+ * @timeout_ms - timeout value in milliseconds
+ * @status_reg - ctrl register
+ * returns 0 if succeeds else a negative error value
+ */
+static int twl6030_madc_wait_conversion_ready(struct twl6030_madc_data *madc,
+                                             unsigned int timeout_ms,
+                                             u8 status_reg)
+{
+       unsigned long timeout;
+       unsigned long delta;
+       u8 reg;
+       int ret;
+
+       delta = msecs_to_jiffies(timeout_ms);
+
+       if (delta < 2)
+               delta = 2;
+
+       wake_lock(&madc->wakelock);
+       timeout = jiffies + delta;
+       do {
+               ret = twl_i2c_read_u8(TWL6030_MODULE_MADC, &reg, status_reg);
+               if (ret) {
+                       dev_err(madc->dev,
+                               "unable to read status register 0x%X\n",
+                               status_reg);
+                       goto unlock;
+               }
+               if (!(reg & TWL6030_MADC_BUSY) && (reg & TWL6030_MADC_EOCP1)) {
+                       ret = 0;
+                       goto unlock;
+               }
+
+               if (time_after(jiffies, timeout))
+                       break;
+
+               usleep_range(500, 2000);
+       } while (1);
+
+       dev_err(madc->dev, "conversion timeout, ctrl_px=0x%08x\n", reg);
+       ret = -EAGAIN;
+
+unlock:
+       wake_unlock(&madc->wakelock);
+       return ret;
+}
+
+/*
+ * Function to read a particular channel value.
+ * @madc - pointer to struct twl6030_madc_data
+ * @reg - lsb of ADC Channel
+ * If the i2c read fails it returns an error else returns 0.
+ */
+static int twl6030_madc_channel_raw_read(struct twl6030_madc_data *madc,
+                                       u8 reg)
+{
+       u8 msb, lsb;
+       int ret;
+
+       mutex_lock(&madc->lock);
+       ret = twl6030_madc_start_conversion(twl6030_madc);
+       if (ret)
+               goto unlock;
+
+       ret = twl6030_madc_wait_conversion_ready(twl6030_madc, 5,
+                                               TWL6030_MADC_CTRL_P1);
+       if (ret)
+               goto unlock;
+
+       /*
+        * For each ADC channel, we have MSB and LSB register
+        * pair. MSB address is always LSB address+1. reg parameter is
+        * the address of LSB register
+        */
+       ret = twl_i2c_read_u8(TWL6030_MODULE_MADC, &msb, reg + 1);
+       if (ret) {
+               dev_err(madc->dev, "unable to read MSB register 0x%X\n",
+                       reg + 1);
+               goto unlock;
+       }
+       ret = twl_i2c_read_u8(TWL6030_MODULE_MADC, &lsb, reg);
+       if (ret) {
+               dev_err(madc->dev, "unable to read LSB register 0x%X\n", reg);
+               goto unlock;
+       }
+       ret = (int)((msb << 8) | lsb);
+unlock:
+       /* Disable GPADC for power savings. */
+       twl_i2c_write_u8(TWL6030_MODULE_ID1, GPADCR, REG_TOGGLE1);
+       mutex_unlock(&madc->lock);
+       return ret;
+}
+
+/*
+ * Return channel value
+ * Or < 0 on failure.
+ */
+int twl6030_get_madc_conversion(int channel_no)
+{
+       u8 reg = TWL6030_MADC_GPCH0_LSB + (2 * channel_no);
+       if (!twl6030_madc) {
+               pr_err("%s: No ADC device\n", __func__);
+               return -EINVAL;
+       }
+       if (channel_no >= TWL6030_MADC_MAX_CHANNELS) {
+               dev_err(twl6030_madc->dev,
+                       "%s: Channel number (%d) exceeds max (%d)\n",
+                       __func__, channel_no, TWL6030_MADC_MAX_CHANNELS);
+               return -EINVAL;
+       }
+
+       return twl6030_madc_channel_raw_read(twl6030_madc, reg);
+}
+EXPORT_SYMBOL_GPL(twl6030_get_madc_conversion);
+
+#ifdef CONFIG_DEBUG_FS
+
+static int debug_twl6030_madc_show(struct seq_file *s, void *_)
+{
+       int i, result;
+       for (i = 0; i < TWL6030_MADC_MAX_CHANNELS; i++) {
+               result = twl6030_get_madc_conversion(i);
+               seq_printf(s, "channel %3d returns result %d\n",
+                       i, result);
+       }
+       return 0;
+}
+
+static int debug_twl6030_madc_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, debug_twl6030_madc_show, inode->i_private);
+}
+
+static const struct file_operations debug_fops = {
+       .open           = debug_twl6030_madc_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+#define DEBUG_FOPS     (&debug_fops)
+
+#else
+#define DEBUG_FOPS     NULL
+#endif
+
+/*
+ * Initialize MADC
+ */
+static int __devinit twl6030_madc_probe(struct platform_device *pdev)
+{
+       struct twl6030_madc_data *madc;
+       struct twl4030_madc_platform_data *pdata = pdev->dev.platform_data;
+
+       if (!pdata) {
+               dev_err(&pdev->dev, "platform_data not available\n");
+               return -EINVAL;
+       }
+       madc = kzalloc(sizeof(*madc), GFP_KERNEL);
+       if (!madc)
+               return -ENOMEM;
+
+       platform_set_drvdata(pdev, madc);
+       madc->dev = &pdev->dev;
+       mutex_init(&madc->lock);
+       madc->file = debugfs_create_file(DRIVER_NAME, S_IRUGO, NULL,
+                                       madc, DEBUG_FOPS);
+       wake_lock_init(&madc->wakelock, WAKE_LOCK_SUSPEND, "twl6030 adc");
+       twl6030_madc = madc;
+       return 0;
+}
+
+static int __devexit twl6030_madc_remove(struct platform_device *pdev)
+{
+       struct twl6030_madc_data *madc = platform_get_drvdata(pdev);
+
+       wake_lock_destroy(&madc->wakelock);
+       mutex_destroy(&madc->lock);
+       free_irq(platform_get_irq(pdev, 0), madc);
+       platform_set_drvdata(pdev, NULL);
+       twl6030_madc = NULL;
+       debugfs_remove(madc->file);
+       kfree(madc);
+
+       return 0;
+}
+
+static int twl6030_madc_suspend(struct device *pdev)
+{
+       int ret;
+       u8 reg_val;
+
+       ret = twl_i2c_read_u8(TWL_MODULE_MADC, &reg_val, TWL6030_MADC_CTRL);
+       if (!ret) {
+               reg_val &= ~(TWL6030_MADC_TEMP1_EN);
+               ret = twl_i2c_write_u8(TWL_MODULE_MADC, reg_val,
+                                       TWL6030_MADC_CTRL);
+       }
+
+       if (ret) {
+               dev_err(twl6030_madc->dev, "unable to disable madc temp1!\n");
+               gpadc_ctrl_reg = TWL6030_MADC_TEMP1_EN;
+       } else
+               gpadc_ctrl_reg = reg_val;
+
+       return 0;
+};
+
+static int twl6030_madc_resume(struct device *pdev)
+{
+       int ret;
+
+       if (!(gpadc_ctrl_reg & TWL6030_MADC_TEMP1_EN)) {
+               gpadc_ctrl_reg |= TWL6030_MADC_TEMP1_EN;
+               ret = twl_i2c_write_u8(TWL_MODULE_MADC, gpadc_ctrl_reg,
+                                       TWL6030_MADC_CTRL);
+               if (ret)
+                       dev_err(twl6030_madc->dev,
+                               "unable to enable madc temp1!\n");
+       }
+
+       return 0;
+};
+
+static const struct dev_pm_ops twl6030_madc_pm_ops = {
+       .suspend = twl6030_madc_suspend,
+       .resume = twl6030_madc_resume,
+};
+
+static struct platform_driver twl6030_madc_driver = {
+       .probe = twl6030_madc_probe,
+       .remove = __exit_p(twl6030_madc_remove),
+       .driver = {
+                  .name = "twl6030_madc",
+                  .owner = THIS_MODULE,
+                  .pm = &twl6030_madc_pm_ops,
+                  },
+};
+
+static int __init twl6030_madc_init(void)
+{
+       return platform_driver_register(&twl6030_madc_driver);
+}
+
+module_init(twl6030_madc_init);
+
+static void __exit twl6030_madc_exit(void)
+{
+       platform_driver_unregister(&twl6030_madc_driver);
+}
+
+module_exit(twl6030_madc_exit);
+
+MODULE_DESCRIPTION("TWL6030 ADC driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("J Keerthy");
+MODULE_ALIAS("platform:twl6030_madc");
diff --git a/drivers/mfd/twl6030-power.c b/drivers/mfd/twl6030-power.c
new file mode 100644 (file)
index 0000000..918b3c2
--- /dev/null
@@ -0,0 +1,362 @@
+/*
+ * Handling for Resource Mapping for TWL6030 Family of chips
+ *
+ * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
+ *     Nishanth Menon
+ *
+ * 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.
+
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/pm.h>
+#include <linux/i2c/twl.h>
+#include <linux/platform_device.h>
+#include <linux/suspend.h>
+#include <linux/string.h>
+
+#include <asm/mach-types.h>
+
+#define VREG_GRP               0
+#define MSK_TRANSITION_APP_SHIFT       0x5
+
+static u8 dev_on_group;
+
+/**
+ * struct twl6030_resource_map - describe the resource mapping for TWL6030
+ * @name:      name of the resource
+ * @res_id:    resource ID
+ * @base_addr: base address for TWL6030
+ * @base_addr: type of the Resources Assignment register for TWL6032
+ * base_addr = 0 for PREQx_RES_ASS_A register
+ * base_addr = 1 for PREQx_RES_ASS_B register
+ * base_addr = 2 for PREQx_RES_ASS_C register
+ * @group: which device group can control this resource?
+ * @mask: unused for TWL6030
+ * @mask: bit mask of the resource in PREQx_RES_ASS_x registers for TWL6032
+ */
+struct twl6030_resource_map {
+       char *name;
+       u8 res_id;
+       u8 base_addr;
+       u8 group;
+       u8 mask;
+};
+
+#define TWL6030_RES_DATA(ID, NAME, BASE_ADDR, GROUP) \
+       {.res_id = ID, .name = NAME, .base_addr = BASE_ADDR,\
+       .group = GROUP, .mask = 0,}
+
+#define TWL6032_RES_DATA(ID, NAME, BASE_ADDR, GROUP, MASK) \
+       {.res_id = ID, .name = NAME, .base_addr = BASE_ADDR,\
+       .group = GROUP, .mask = MASK,}
+
+/* list of all s/w modifiable resources in TWL6030 */
+static __initdata struct twl6030_resource_map twl6030_res_map[] = {
+       TWL6030_RES_DATA(RES_V1V29, "V1V29", 0x40, DEV_GRP_P1),
+       TWL6030_RES_DATA(RES_V1V8, "V1V8", 0x46, DEV_GRP_P1),
+       TWL6030_RES_DATA(RES_V2V1, "V2V1", 0x4c, DEV_GRP_P1),
+       TWL6030_RES_DATA(RES_VDD1, "CORE1", 0x52, DEV_GRP_P1),
+       TWL6030_RES_DATA(RES_VDD2, "CORE2", 0x58, DEV_GRP_P1),
+       TWL6030_RES_DATA(RES_VDD3, "CORE3", 0x5e, DEV_GRP_P1),
+       TWL6030_RES_DATA(RES_VMEM, "VMEM", 0x64, DEV_GRP_P1),
+       /* VANA cannot be modified */
+       TWL6030_RES_DATA(RES_VUAX1, "VUAX1", 0x84, DEV_GRP_P1),
+       TWL6030_RES_DATA(RES_VAUX2, "VAUX2", 0x88, DEV_GRP_P1),
+       TWL6030_RES_DATA(RES_VAUX3, "VAUX3", 0x8c, DEV_GRP_P1),
+       TWL6030_RES_DATA(RES_VCXIO, "VCXIO", 0x90, DEV_GRP_P1),
+       TWL6030_RES_DATA(RES_VDAC, "VDAC", 0x94, DEV_GRP_P1),
+       TWL6030_RES_DATA(RES_VMMC1, "VMMC", 0x98, DEV_GRP_P1),
+       TWL6030_RES_DATA(RES_VPP, "VPP", 0x9c, DEV_GRP_P1),
+       /* VRTC cannot be modified */
+       TWL6030_RES_DATA(RES_VUSBCP, "VUSB", 0xa0, DEV_GRP_P1),
+       TWL6030_RES_DATA(RES_VSIM, "VSIM", 0xa4, DEV_GRP_P1),
+       TWL6030_RES_DATA(RES_REGEN, "REGEN1", 0xad, DEV_GRP_P1),
+       TWL6030_RES_DATA(RES_REGEN2, "REGEN2", 0xb0, DEV_GRP_P1),
+       TWL6030_RES_DATA(RES_SYSEN, "SYSEN", 0xb3, DEV_GRP_P1),
+       /* NRES_PWRON cannot be modified */
+       /* 32KCLKAO cannot be modified */
+       TWL6030_RES_DATA(RES_32KCLKG, "32KCLKG", 0xbc, DEV_GRP_P1),
+       TWL6030_RES_DATA(RES_32KCLKAUDIO, "32KCLKAUDIO", 0xbf, DEV_GRP_P1),
+       /* BIAS cannot be modified */
+       /* VBATMIN_HI cannot be modified */
+       /* RC6MHZ cannot be modified */
+       /* TEMP cannot be modified */
+};
+
+/* list of all s/w modifiable resources in TWL6032 */
+static __initdata struct twl6030_resource_map twl6032_res_map[] = {
+       /* PREQx_RES_ASS_A register resources */
+       TWL6032_RES_DATA(RES_LDOUSB, "VUSB", 0, DEV_GRP_P1, BIT(5)),
+       TWL6032_RES_DATA(RES_SMPS5, "SMPS5", 0, DEV_GRP_P1, BIT(4)),
+       TWL6032_RES_DATA(RES_SMPS5, "SMPS4", 0, DEV_GRP_P1, BIT(3)),
+       TWL6032_RES_DATA(RES_SMPS5, "SMPS3", 0, DEV_GRP_P1, BIT(2)),
+       TWL6032_RES_DATA(RES_SMPS5, "SMPS2", 0, DEV_GRP_P1, BIT(1)),
+       TWL6032_RES_DATA(RES_SMPS5, "SMPS1", 0, DEV_GRP_P1, BIT(0)),
+       /* PREQx_RES_ASS_B register resources */
+       TWL6032_RES_DATA(RES_LDOLN, "LDOLN", 1, DEV_GRP_P1, BIT(7)),
+       TWL6032_RES_DATA(RES_LDO7, "LDO7", 1, DEV_GRP_P1, BIT(6)),
+       TWL6032_RES_DATA(RES_LDO6, "LDO6", 1, DEV_GRP_P1, BIT(5)),
+       TWL6032_RES_DATA(RES_LDO5, "LDO5", 1, DEV_GRP_P1, BIT(4)),
+       TWL6032_RES_DATA(RES_LDO4, "LDO4", 1, DEV_GRP_P1, BIT(3)),
+       TWL6032_RES_DATA(RES_LDO3, "LDO3", 1, DEV_GRP_P1, BIT(2)),
+       TWL6032_RES_DATA(RES_LDO2, "LDO2", 1, DEV_GRP_P1, BIT(1)),
+       TWL6032_RES_DATA(RES_LDO1, "LDO1", 1, DEV_GRP_P1, BIT(0)),
+       /* PREQx_RES_ASS_C register resources */
+       TWL6032_RES_DATA(RES_VSYSMIN_HI, "VSYSMIN_HI", 2, DEV_GRP_P1, BIT(5)),
+       TWL6032_RES_DATA(RES_32KCLKG, "32KCLKG", 2, DEV_GRP_P1, BIT(4)),
+       TWL6032_RES_DATA(RES_32KCLKAUDIO, "32KCLKAUDIO", 2, DEV_GRP_P1, BIT(3)),
+       TWL6032_RES_DATA(RES_SYSEN, "SYSEN", 2, DEV_GRP_P1, BIT(2)),
+       TWL6032_RES_DATA(RES_REGEN2, "REGEN2", 2, DEV_GRP_P1, BIT(1)),
+       TWL6032_RES_DATA(RES_REGEN, "REGEN1", 2, DEV_GRP_P1, BIT(0)),
+};
+
+static struct twl4030_system_config twl6030_sys_config[] = {
+       {.name = "DEV_ON", .group =  DEV_GRP_P1,},
+};
+
+/* Actual power groups that TWL understands */
+#define P3_GRP_6030    BIT(2)          /* secondary processor, modem, etc */
+#define P2_GRP_6030    BIT(1)          /* "peripherals" */
+#define P1_GRP_6030    BIT(0)          /* CPU/Linux */
+
+static __init void twl6030_process_system_config(void)
+{
+       u8 grp;
+       int r;
+       bool i = false;
+
+       struct twl4030_system_config *sys_config;
+       sys_config = twl6030_sys_config;
+
+       while (sys_config && sys_config->name) {
+               if (!strcmp(sys_config->name, "DEV_ON")) {
+                       dev_on_group = sys_config->group;
+                       i = true;
+                       break;
+               }
+               sys_config++;
+       }
+       if (!i)
+               pr_err("%s: Couldn't find DEV_ON resource configuration!"
+                       " MOD & CON group would be kept active.\n", __func__);
+
+       if (dev_on_group) {
+               r = twl_i2c_read_u8(TWL_MODULE_PM_MASTER, &grp,
+                               TWL6030_PHOENIX_DEV_ON);
+               if (r) {
+                       pr_err("%s: Error(%d) reading  {addr=0x%02x}",
+                               __func__, r, TWL6030_PHOENIX_DEV_ON);
+                       /*
+                        * On error resetting to 0, so that all the process
+                        * groups are kept active.
+                        */
+                       dev_on_group = 0;
+               } else {
+                       /*
+                        * Unmapped processor groups are disabled by writing
+                        * 1 to corresponding group in DEV_ON.
+                        */
+                       grp |= (dev_on_group & DEV_GRP_P1) ? 0 : P1_GRP_6030;
+                       grp |= (dev_on_group & DEV_GRP_P2) ? 0 : P2_GRP_6030;
+                       grp |= (dev_on_group & DEV_GRP_P3) ? 0 : P3_GRP_6030;
+                       dev_on_group = grp;
+               }
+
+               /*
+                *  unmask PREQ transition Executes ACT2SLP and SLP2ACT sleep
+                *   sequence
+                */
+               r = twl_i2c_read_u8(TWL_MODULE_PM_MASTER, &grp,
+                               TWL6030_PM_MASTER_MSK_TRANSITION);
+               if (r) {
+                       pr_err("%s: Error (%d) reading"
+                               " TWL6030_MSK_TRANSITION\n", __func__, r);
+                       return;
+               }
+
+               grp &= (dev_on_group << MSK_TRANSITION_APP_SHIFT);
+
+               r = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, grp,
+                                       TWL6030_PM_MASTER_MSK_TRANSITION);
+               if (r)
+                       pr_err("%s: Error (%d) writing to"
+                               " TWL6030_MSK_TRANSITION\n", __func__, r);
+       }
+}
+
+#define DEV_GRP_P1_OFFSET      1
+#define DEV_GRP_P2_OFFSET      4
+#define DEV_GRP_P3_OFFSET      7
+
+static __init void twl6030_program_map(unsigned long features)
+{
+       struct twl6030_resource_map *res;
+       int r, i;
+
+       if (features & TWL6032_SUBCLASS) {
+               /**
+                * mask[0] = 0 for twl_i2c_write
+                * mask[1]-mask[3]: PREQ1_RES_ASS_A - PREQ1_RES_ASS_C
+                * mask[4]-mask[6]: PREQ2_RES_ASS_A - PREQ2_RES_ASS_C
+                * mask[7]-mask[9]: PREQ3_RES_ASS_A - PREQ3_RES_ASS_C
+                */
+               u8 mask[10];
+
+               res = twl6032_res_map;
+               memset(&mask[0], 0, 10);
+
+               for (i = 0; i < ARRAY_SIZE(twl6032_res_map); i++) {
+                       /* map back from generic device id to TWL6032 mask */
+                       mask[DEV_GRP_P1_OFFSET + res->base_addr] |= \
+                               (res->group & DEV_GRP_P1) ? res->mask : 0;
+                       mask[DEV_GRP_P2_OFFSET + res->base_addr] |= \
+                               (res->group & DEV_GRP_P2) ? res->mask : 0;
+                       mask[DEV_GRP_P3_OFFSET + res->base_addr] |= \
+                               (res->group & DEV_GRP_P3) ? res->mask : 0;
+                       res++;
+               }
+
+               r = twl_i2c_write(TWL6030_MODULE_ID0, &mask[0],
+                       TWL6032_PREQ1_RES_ASS_A, 9);
+
+               if (r)
+                       pr_err("%s: Error(%d) programming TWL6032 PREQ "
+                               "Assignment Registers {start addr=0xd7}\n",
+                               __func__, r);
+       } else {
+               res = twl6030_res_map;
+               for (i = 0; i < ARRAY_SIZE(twl6030_res_map); i++) {
+                       u8 grp = 0;
+
+                       /* map back from generic device id to TWL6030 ID */
+                       grp |= (res->group & DEV_GRP_P1) ? P1_GRP_6030 : 0;
+                       grp |= (res->group & DEV_GRP_P2) ? P2_GRP_6030 : 0;
+                       grp |= (res->group & DEV_GRP_P3) ? P3_GRP_6030 : 0;
+
+                       r = twl_i2c_write_u8(TWL6030_MODULE_ID0, res->group,
+                                            res->base_addr);
+                       if (r)
+                               pr_err("%s: Error(%d) programming map %s {"
+                                       "addr=0x%02x},grp=0x%02X\n", __func__,
+                                       r, res->name, res->base_addr,
+                                       res->group);
+                       res++;
+               }
+       }
+}
+
+static __init void twl6030_update_system_map
+                       (struct twl4030_system_config *sys_list)
+{
+       int i;
+       struct twl4030_system_config *sys_res;
+
+       while (sys_list && sys_list->name)  {
+               sys_res = twl6030_sys_config;
+               for (i = 0; i < ARRAY_SIZE(twl6030_sys_config); i++) {
+                       if (!strcmp(sys_res->name, sys_list->name))
+                               sys_res->group = sys_list->group &
+                                       (DEV_GRP_P1 | DEV_GRP_P2 | DEV_GRP_P3);
+                       sys_res++;
+               }
+               sys_list++;
+       }
+}
+
+static __init void twl6030_update_map(struct twl4030_resconfig *res_list, \
+                                       unsigned long features)
+{
+       int i, res_idx = 0;
+       struct twl6030_resource_map *res;
+       struct twl6030_resource_map *cur_twl6030_res = twl6030_res_map;
+       int twl6030_res_cnt = ARRAY_SIZE(twl6030_res_map);
+
+       if (features & TWL6032_SUBCLASS) {
+               cur_twl6030_res = twl6032_res_map;
+               twl6030_res_cnt = ARRAY_SIZE(twl6032_res_map);
+       }
+
+       while (res_list->resource != TWL4030_RESCONFIG_UNDEF) {
+               res = cur_twl6030_res;
+               for (i = 0; i < twl6030_res_cnt; i++) {
+                       if (res->res_id == res_list->resource) {
+                               res->group = res_list->devgroup &
+                                   (DEV_GRP_P1 | DEV_GRP_P2 | DEV_GRP_P3);
+                               break;
+                       }
+                       res++;
+               }
+
+               if (i == twl6030_res_cnt) {
+                       pr_err("%s: in platform_data resource index %d, cannot"
+                              " find match for resource 0x%02x. NO Update!\n",
+                              __func__, res_idx, res_list->resource);
+               }
+               res_list++;
+               res_idx++;
+       }
+}
+
+
+static int twl6030_power_notifier_cb(struct notifier_block *notifier,
+                                       unsigned long pm_event,  void *unused)
+{
+       int r = 0;
+
+       switch (pm_event) {
+       case PM_SUSPEND_PREPARE:
+               r = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, dev_on_group,
+                               TWL6030_PHOENIX_DEV_ON);
+               if (r)
+                       pr_err("%s: Error(%d) programming {addr=0x%02x}",
+                               __func__, r, TWL6030_PHOENIX_DEV_ON);
+               break;
+       }
+
+       return notifier_from_errno(r);
+}
+
+static struct notifier_block twl6030_power_pm_notifier = {
+       .notifier_call = twl6030_power_notifier_cb,
+};
+
+/**
+ * twl6030_power_init() - Update the power map to reflect connectivity of board
+ * @power_data:        power resource map to update (OPTIONAL) - use this if a resource
+ *             is used by other devices other than APP (DEV_GRP_P1)
+ */
+void __init twl6030_power_init(struct twl4030_power_data *power_data, \
+                                       unsigned long features)
+{
+       int r;
+
+       if (power_data && (!power_data->resource_config &&
+                                       !power_data->sys_config)) {
+               pr_err("%s: power data from platform without configuration!\n",
+                      __func__);
+               return;
+       }
+
+       if (power_data && power_data->resource_config)
+               twl6030_update_map(power_data->resource_config, features);
+
+       if (power_data && power_data->sys_config)
+               twl6030_update_system_map(power_data->sys_config);
+
+       twl6030_process_system_config();
+
+       twl6030_program_map(features);
+
+       r = register_pm_notifier(&twl6030_power_pm_notifier);
+       if (r)
+               pr_err("%s: twl6030 power registration failed!\n", __func__);
+
+       return;
+}
diff --git a/drivers/mfd/twl6030-poweroff.c b/drivers/mfd/twl6030-poweroff.c
new file mode 100644 (file)
index 0000000..3187806
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * /drivers/mfd/twl6030-poweroff.c
+ *
+ * Power off device
+ *
+ * Copyright (C) 2011 Texas Instruments Corporation
+ *
+ * Written by Rajeev Kulkarni <rajeevk@ti.com>
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License. See the file "COPYING" in the main directory of this
+ * archive for more details.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/module.h>
+#include <linux/pm.h>
+#include <linux/i2c/twl.h>
+
+#define APP_DEVOFF     (1<<0)
+#define CON_DEVOFF     (1<<1)
+#define MOD_DEVOFF     (1<<2)
+
+void twl6030_poweroff(void)
+{
+       u8 val = 0;
+       int err = 0;
+
+       err = twl_i2c_read_u8(TWL_MODULE_PM_MASTER, &val,
+                                 TWL6030_PHOENIX_DEV_ON);
+       if (err) {
+               pr_warning("I2C error %d reading PHOENIX_DEV_ON\n", err);
+               return;
+       }
+
+       val |= APP_DEVOFF | CON_DEVOFF | MOD_DEVOFF;
+
+       err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, val,
+                                  TWL6030_PHOENIX_DEV_ON);
+
+       if (err) {
+               pr_warning("I2C error %d writing PHOENIX_DEV_ON\n", err);
+               return;
+       }
+
+       return;
+}
+
+static int __init twl6030_poweroff_init(void)
+{
+       pm_power_off = twl6030_poweroff;
+
+       return 0;
+}
+
+static void __exit twl6030_poweroff_exit(void)
+{
+       pm_power_off = NULL;
+}
+
+module_init(twl6030_poweroff_init);
+module_exit(twl6030_poweroff_exit);
+
+MODULE_DESCRIPTION("TLW6030 device power off");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Rajeev Kulkarni");
old mode 100644 (file)
new mode 100755 (executable)
index 87fe0f7..3d18ea4
@@ -76,12 +76,25 @@ struct twlreg_info {
 #define VREG_STATE             2
 #define VREG_VOLTAGE           3
 #define VREG_VOLTAGE_SMPS      4
+#define VREG_VOLTAGE_DVS_SMPS 3      //add
 /* TWL6030 Misc register offsets */
 #define VREG_BC_ALL            1
 #define VREG_BC_REF            2
 #define VREG_BC_PROC           3
 #define VREG_BC_CLK_RST                4
 
+/* TWL6030 LDO register values for CFG_TRANS */
+#define TWL6030_CFG_TRANS_STATE_MASK   0x03
+#define TWL6030_CFG_TRANS_STATE_OFF    0x00
+/*
+ * Auto means the following:
+ * SMPS:       AUTO(PWM/PFM)
+ * LDO:                AMS(SLP/ACT)
+ * resource:   ON
+ */
+#define TWL6030_CFG_TRANS_STATE_AUTO   0x01
+#define TWL6030_CFG_TRANS_SLEEP_SHIFT  2
+
 /* TWL6030 LDO register values for CFG_STATE */
 #define TWL6030_CFG_STATE_OFF  0x00
 #define TWL6030_CFG_STATE_ON   0x01
@@ -89,7 +102,9 @@ struct twlreg_info {
 #define TWL6030_CFG_STATE_SLEEP        0x03
 #define TWL6030_CFG_STATE_GRP_SHIFT    5
 #define TWL6030_CFG_STATE_APP_SHIFT    2
-#define TWL6030_CFG_STATE_APP_MASK     (0x03 << TWL6030_CFG_STATE_APP_SHIFT)
+#define TWL6030_CFG_STATE_MASK         0x03
+#define TWL6030_CFG_STATE_APP_MASK     (TWL6030_CFG_STATE_MASK << \
+                                               TWL6030_CFG_STATE_APP_SHIFT)
 #define TWL6030_CFG_STATE_APP(v)       (((v) & TWL6030_CFG_STATE_APP_MASK) >>\
                                                TWL6030_CFG_STATE_APP_SHIFT)
 
@@ -97,19 +112,32 @@ struct twlreg_info {
 #define SMPS_OFFSET_EN         BIT(0)
 #define SMPS_EXTENDED_EN       BIT(1)
 
-/* twl6025 SMPS EPROM values */
+/* twl6032 SMPS EPROM values */
 #define TWL6030_SMPS_OFFSET            0xB0
 #define TWL6030_SMPS_MULT              0xB3
 #define SMPS_MULTOFFSET_SMPS4  BIT(0)
 #define SMPS_MULTOFFSET_VIO    BIT(1)
 #define SMPS_MULTOFFSET_SMPS3  BIT(6)
 
+
+
+/* TWL6030 VUSB supplemental config registers */
+#define TWL6030_MISC2          0xE5
+#define TWL6030_CFG_LDO_PD2    0xF5
+
+/*
+ * TWL603X SMPS has 6 bits xxxx_CFG_VOLTAGE.VSEL[5:0] to configure voltages and
+ * each bit combination corresponds to a particular voltage (value 63 is
+ * reserved).
+ */
+#define TWL603X_SMPS_VSEL_MASK 0x3F
+#define TWL603X_SMPS_NUMBER_VOLTAGES TWL603X_SMPS_VSEL_MASK
+
 static inline int
 twlreg_read(struct twlreg_info *info, unsigned slave_subgp, unsigned offset)
 {
        u8 value;
        int status;
-
        status = twl_i2c_read_u8(slave_subgp,
                        &value, info->base + offset);
        return (status < 0) ? status : value;
@@ -161,22 +189,49 @@ static int twl6030reg_is_enabled(struct regulator_dev *rdev)
        struct twlreg_info      *info = rdev_get_drvdata(rdev);
        int                     grp = 0, val;
 
-       if (!(twl_class_is_6030() && (info->features & TWL6025_SUBCLASS)))
+       if (!(info->features & TWL6032_SUBCLASS)) {
                grp = twlreg_read(info, TWL_MODULE_PM_RECEIVER, VREG_GRP);
-       if (grp < 0)
-               return grp;
+               if (grp < 0)
+                       return grp;
 
-       if (!(twl_class_is_6030() && (info->features & TWL6025_SUBCLASS)))
                grp &= P1_GRP_6030;
-       else
+               val = twlreg_read(info, TWL_MODULE_PM_RECEIVER, VREG_STATE);
+               val = TWL6030_CFG_STATE_APP(val);
+       } else {
+               val = twlreg_read(info, TWL_MODULE_PM_RECEIVER, VREG_STATE);
+               val &= TWL6030_CFG_STATE_MASK;
                grp = 1;
-
-       val = twlreg_read(info, TWL_MODULE_PM_RECEIVER, VREG_STATE);
-       val = TWL6030_CFG_STATE_APP(val);
+       }
 
        return grp && (val == TWL6030_CFG_STATE_ON);
 }
 
+static int twl6030reg_set_trans_state(struct regulator_dev *rdev,
+                                     u8 shift, u8 val)
+{
+       struct twlreg_info      *info = rdev_get_drvdata(rdev);
+       int                     rval;
+       u8                      mask;
+
+       /* Read CFG_TRANS register of TWL6030 */
+       rval = twlreg_read(info, TWL_MODULE_PM_RECEIVER, VREG_TRANS);
+
+       if (rval < 0)
+               return rval;
+
+       mask = TWL6030_CFG_TRANS_STATE_MASK << shift;
+       val = (val << shift) & mask;
+
+       /* If value is already set, no need to write to reg */
+       if (val == (rval & mask))
+               return 0;
+
+       rval &= ~mask;
+       rval |= val;
+
+       return twlreg_write(info, TWL_MODULE_PM_RECEIVER, VREG_TRANS, rval);
+}
+
 static int twl4030reg_enable(struct regulator_dev *rdev)
 {
        struct twlreg_info      *info = rdev_get_drvdata(rdev);
@@ -202,7 +257,7 @@ static int twl6030reg_enable(struct regulator_dev *rdev)
        int                     grp = 0;
        int                     ret;
 
-       if (!(twl_class_is_6030() && (info->features & TWL6025_SUBCLASS)))
+       if (!(twl_class_is_6030() && (info->features & TWL6032_SUBCLASS)))
                grp = twlreg_read(info, TWL_MODULE_PM_RECEIVER, VREG_GRP);
        if (grp < 0)
                return grp;
@@ -210,7 +265,14 @@ static int twl6030reg_enable(struct regulator_dev *rdev)
        ret = twlreg_write(info, TWL_MODULE_PM_RECEIVER, VREG_STATE,
                        grp << TWL6030_CFG_STATE_GRP_SHIFT |
                        TWL6030_CFG_STATE_ON);
-
+       /*
+        * Ensure it stays in Auto mode when we enter suspend state.
+        * (TWL6030 in sleep mode).
+        */
+       if (!ret)
+               ret = twl6030reg_set_trans_state(rdev,
+                               TWL6030_CFG_TRANS_SLEEP_SHIFT,
+                               TWL6030_CFG_TRANS_STATE_AUTO);
        udelay(info->delay);
 
        return ret;
@@ -239,7 +301,7 @@ static int twl6030reg_disable(struct regulator_dev *rdev)
        int                     grp = 0;
        int                     ret;
 
-       if (!(twl_class_is_6030() && (info->features & TWL6025_SUBCLASS)))
+       if (!(twl_class_is_6030() && (info->features & TWL6032_SUBCLASS)))
                grp = P1_GRP_6030 | P2_GRP_6030 | P3_GRP_6030;
 
        /* For 6030, set the off state for all grps enabled */
@@ -247,6 +309,11 @@ static int twl6030reg_disable(struct regulator_dev *rdev)
                        (grp) << TWL6030_CFG_STATE_GRP_SHIFT |
                        TWL6030_CFG_STATE_OFF);
 
+       /* Ensure it remains OFF when we enter suspend (TWL6030 in sleep). */
+       if (!ret)
+               ret = twl6030reg_set_trans_state(rdev,
+                               TWL6030_CFG_TRANS_SLEEP_SHIFT,
+                               TWL6030_CFG_TRANS_STATE_OFF);
        return ret;
 }
 
@@ -277,7 +344,12 @@ static int twl6030reg_get_status(struct regulator_dev *rdev)
 
        val = twlreg_read(info, TWL_MODULE_PM_RECEIVER, VREG_STATE);
 
-       switch (TWL6030_CFG_STATE_APP(val)) {
+       if (info->features & TWL6032_SUBCLASS)
+               val &= TWL6030_CFG_STATE_MASK;
+       else
+               val = TWL6030_CFG_STATE_APP(val);
+
+       switch (val) {
        case TWL6030_CFG_STATE_ON:
                return REGULATOR_STATUS_NORMAL;
 
@@ -333,7 +405,7 @@ static int twl6030reg_set_mode(struct regulator_dev *rdev, unsigned mode)
        int grp = 0;
        int val;
 
-       if (!(twl_class_is_6030() && (info->features & TWL6025_SUBCLASS)))
+       if (!(twl_class_is_6030() && (info->features & TWL6032_SUBCLASS)))
                grp = twlreg_read(info, TWL_MODULE_PM_RECEIVER, VREG_GRP);
 
        if (grp < 0)
@@ -357,6 +429,18 @@ static int twl6030reg_set_mode(struct regulator_dev *rdev, unsigned mode)
        return twlreg_write(info, TWL_MODULE_PM_RECEIVER, VREG_STATE, val);
 }
 
+static int twl6030ldo_suspend_enable(struct regulator_dev *rdev)
+{
+       return twl6030reg_set_trans_state(rdev, TWL6030_CFG_TRANS_SLEEP_SHIFT,
+                                       TWL6030_CFG_TRANS_STATE_AUTO);
+}
+
+static int twl6030ldo_suspend_disable(struct regulator_dev *rdev)
+{
+       return twl6030reg_set_trans_state(rdev, TWL6030_CFG_TRANS_SLEEP_SHIFT,
+                                       TWL6030_CFG_TRANS_STATE_OFF);
+}
+
 /*----------------------------------------------------------------------*/
 
 /*
@@ -570,6 +654,9 @@ static struct regulator_ops twl6030ldo_ops = {
        .set_mode       = twl6030reg_set_mode,
 
        .get_status     = twl6030reg_get_status,
+
+       .set_suspend_enable     = twl6030ldo_suspend_enable,
+       .set_suspend_disable    = twl6030ldo_suspend_disable,
 };
 
 /*----------------------------------------------------------------------*/
@@ -617,6 +704,9 @@ static struct regulator_ops twl6030fixed_ops = {
        .set_mode       = twl6030reg_set_mode,
 
        .get_status     = twl6030reg_get_status,
+
+       .set_suspend_enable     = twl6030ldo_suspend_enable,
+       .set_suspend_disable    = twl6030ldo_suspend_disable,
 };
 
 static struct regulator_ops twl6030_fixed_resource = {
@@ -806,7 +896,104 @@ twl6030smps_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV,
        return twlreg_write(info, TWL_MODULE_PM_RECEIVER, VREG_VOLTAGE_SMPS,
                                                        vsel);
 }
+//add
+#if 1
+static int twl6030dvssmps_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV,
+                       unsigned int *selector)
+{
+       struct twlreg_info      *info = rdev_get_drvdata(rdev);
+       int     vsel = 0;
 
+       switch (info->flags) {
+       case 0:
+               if (min_uV == 0)
+                       vsel = 0;
+               else if ((min_uV >= 600000) && (max_uV <= 1300000)) {
+                       vsel = (min_uV - 600000) / 125;
+                       if (vsel % 100)
+                               vsel += 100;
+                       vsel /= 100;
+                       vsel++;
+               }
+               /* Values 1..57 for vsel are linear and can be calculated
+                * values 58..62 are non linear.
+                */
+               else if ((min_uV > 1900000) && (max_uV >= 2100000))
+                       vsel = 62;
+               else if ((min_uV > 1800000) && (max_uV >= 1900000))
+                       vsel = 61;
+               else if ((min_uV > 1500000) && (max_uV >= 1800000))
+                       vsel = 60;
+               else if ((min_uV > 1350000) && (max_uV >= 1500000))
+                       vsel = 59;
+               else if ((min_uV > 1300000) && (max_uV >= 1350000))
+                       vsel = 58;
+               else
+                       return -EINVAL;
+               break;
+       case SMPS_OFFSET_EN:
+               if (min_uV == 0)
+                       vsel = 0;
+               else if ((min_uV >= 700000) && (max_uV <= 1420000)) {
+                       vsel = (min_uV - 700000) / 125;
+                       if (vsel % 100)
+                               vsel += 100;
+                       vsel /= 100;
+                       vsel++;
+               }
+               /* Values 1..57 for vsel are linear and can be calculated
+                * values 58..62 are non linear.
+                */
+               else if ((min_uV > 1900000) && (max_uV >= 2100000))
+                       vsel = 62;
+               else if ((min_uV > 1800000) && (max_uV >= 1900000))
+                       vsel = 61;
+               else if ((min_uV > 1350000) && (max_uV >= 1800000))
+                       vsel = 60;
+               else if ((min_uV > 1350000) && (max_uV >= 1500000))
+                       vsel = 59;
+               else if ((min_uV > 1300000) && (max_uV >= 1350000))
+                       vsel = 58;
+               else
+                       return -EINVAL;
+               break;
+       case SMPS_EXTENDED_EN:
+               if (min_uV == 0)
+                       vsel = 0;
+               else if ((min_uV >= 1852000) && (max_uV <= 4013600)) {
+                       vsel = (min_uV - 1852000) / 386;
+                       if (vsel % 100)
+                               vsel += 100;
+                       vsel /= 100;
+                       vsel++;
+               }
+               break;
+       case SMPS_OFFSET_EN|SMPS_EXTENDED_EN:
+               if (min_uV == 0)
+                       vsel = 0;
+               else if ((min_uV >= 2161000) && (max_uV <= 4321000)) {
+                       vsel = (min_uV - 1852000) / 386;
+                       if (vsel % 100)
+                               vsel += 100;
+                       vsel /= 100;
+                       vsel++;
+               }
+               break;
+       }
+
+       *selector = vsel;
+
+       return twlreg_write(info, TWL_MODULE_PM_DVS, VREG_VOLTAGE_DVS_SMPS,
+                                                       vsel);
+}
+
+static int twl6030dvssmps_get_voltage_sel(struct regulator_dev *rdev)
+{
+       struct twlreg_info      *info = rdev_get_drvdata(rdev);
+
+       return twlreg_read(info, TWL_MODULE_PM_DVS, VREG_VOLTAGE_DVS_SMPS);
+}
+#endif
 static int twl6030smps_get_voltage_sel(struct regulator_dev *rdev)
 {
        struct twlreg_info      *info = rdev_get_drvdata(rdev);
@@ -827,16 +1014,49 @@ static struct regulator_ops twlsmps_ops = {
        .set_mode               = twl6030reg_set_mode,
 
        .get_status             = twl6030reg_get_status,
+
+       .set_suspend_enable     = twl6030ldo_suspend_enable,
+       .set_suspend_disable    = twl6030ldo_suspend_disable,
+};
+
+static struct regulator_ops twl6030_external_control_pin_ops = {
+       .enable                 = twl6030reg_enable,
+       .disable                = twl6030reg_disable,
+       .is_enabled             = twl6030reg_is_enabled,
+
+       .set_mode               = twl6030reg_set_mode,
+
+       .get_status             = twl6030reg_get_status,
+
+       .set_suspend_enable     = twl6030ldo_suspend_enable,
+       .set_suspend_disable    = twl6030ldo_suspend_disable,
 };
+//add
+static struct regulator_ops twldvssmps_ops = {
+       .list_voltage           = twl6030smps_list_voltage,
+
+       .set_voltage            = twl6030dvssmps_set_voltage,
+       .get_voltage_sel        = twl6030dvssmps_get_voltage_sel,
 
+       .enable                 = twl6030reg_enable,
+       .disable                = twl6030reg_disable,
+       .is_enabled             = twl6030reg_is_enabled,
+
+       .set_mode               = twl6030reg_set_mode,
+
+       .get_status             = twl6030reg_get_status,
+
+       .set_suspend_enable     = twl6030ldo_suspend_enable,
+       .set_suspend_disable    = twl6030ldo_suspend_disable,
+};
 /*----------------------------------------------------------------------*/
 
 #define TWL4030_FIXED_LDO(label, offset, mVolts, num, turnon_delay, \
                        remap_conf) \
                TWL_FIXED_LDO(label, offset, mVolts, num, turnon_delay, \
                        remap_conf, TWL4030, twl4030fixed_ops)
-#define TWL6030_FIXED_LDO(label, offset, mVolts, num, turnon_delay) \
-               TWL_FIXED_LDO(label, offset, mVolts, num, turnon_delay, \
+#define TWL6030_FIXED_LDO(label, offset, mVolts, turnon_delay) \
+               TWL_FIXED_LDO(label, offset, mVolts, 0x0, turnon_delay, \
                        0x0, TWL6030, twl6030fixed_ops)
 
 #define TWL4030_ADJUSTABLE_LDO(label, offset, num, turnon_delay, remap_conf) { \
@@ -856,29 +1076,27 @@ static struct regulator_ops twlsmps_ops = {
                }, \
        }
 
-#define TWL6030_ADJUSTABLE_LDO(label, offset, min_mVolts, max_mVolts, num) { \
+#define TWL6030_ADJUSTABLE_LDO(label, offset, min_mVolts, max_mVolts) { \
        .base = offset, \
-       .id = num, \
        .min_mV = min_mVolts, \
        .max_mV = max_mVolts, \
        .desc = { \
                .name = #label, \
                .id = TWL6030_REG_##label, \
-               .n_voltages = (max_mVolts - min_mVolts)/100, \
+               .n_voltages = (max_mVolts - min_mVolts)/100 + 1, \
                .ops = &twl6030ldo_ops, \
                .type = REGULATOR_VOLTAGE, \
                .owner = THIS_MODULE, \
                }, \
        }
 
-#define TWL6025_ADJUSTABLE_LDO(label, offset, min_mVolts, max_mVolts, num) { \
+#define TWL6032_ADJUSTABLE_LDO(label, offset, min_mVolts, max_mVolts) { \
        .base = offset, \
-       .id = num, \
        .min_mV = min_mVolts, \
        .max_mV = max_mVolts, \
        .desc = { \
                .name = #label, \
-               .id = TWL6025_REG_##label, \
+               .id = TWL6032_REG_##label, \
                .n_voltages = ((max_mVolts - min_mVolts)/100) + 1, \
                .ops = &twl6030ldo_ops, \
                .type = REGULATOR_VOLTAGE, \
@@ -903,9 +1121,8 @@ static struct regulator_ops twlsmps_ops = {
                }, \
        }
 
-#define TWL6030_FIXED_RESOURCE(label, offset, num, turnon_delay) { \
+#define TWL6030_FIXED_RESOURCE(label, offset, turnon_delay) { \
        .base = offset, \
-       .id = num, \
        .delay = turnon_delay, \
        .desc = { \
                .name = #label, \
@@ -916,21 +1133,61 @@ static struct regulator_ops twlsmps_ops = {
                }, \
        }
 
-#define TWL6025_ADJUSTABLE_SMPS(label, offset, num) { \
+#define TWL6030_ADJUSTABLE_SMPS(label, offset, min_mVolts, max_mVolts) { \
+       .base = offset, \
+       .min_mV = min_mVolts, \
+       .max_mV = max_mVolts, \
+       .desc = { \
+               .name = #label, \
+               .id = TWL6030_REG_##label, \
+               .n_voltages = TWL603X_SMPS_NUMBER_VOLTAGES, \
+               .ops = &twlsmps_ops, \
+               .type = REGULATOR_VOLTAGE, \
+               .owner = THIS_MODULE, \
+               }, \
+       }
+
+#define TWL6032_ADJUSTABLE_SMPS(label, offset) { \
        .base = offset, \
-       .id = num, \
        .min_mV = 600, \
        .max_mV = 2100, \
        .desc = { \
                .name = #label, \
-               .id = TWL6025_REG_##label, \
-               .n_voltages = 63, \
+               .id = TWL6032_REG_##label, \
+               .n_voltages = TWL603X_SMPS_NUMBER_VOLTAGES, \
                .ops = &twlsmps_ops, \
                .type = REGULATOR_VOLTAGE, \
                .owner = THIS_MODULE, \
                }, \
        }
 
+#define TWL6030_EXTERNAL_CONTROL_PIN(label, offset, turnon_delay) { \
+       .base = offset, \
+       .delay = turnon_delay, \
+       .desc = { \
+               .name = #label, \
+               .id = TWL6030_REG_##label, \
+               .ops = &twl6030_external_control_pin_ops, \
+               .type = REGULATOR_VOLTAGE, \
+               .owner = THIS_MODULE, \
+               }, \
+       }
+       
+       //add
+#define TWL6032_ADJUSTABLE_DVSSMPS(label, offset) { \
+       .base = offset, \
+       .min_mV = 600, \
+       .max_mV = 2100, \
+       .desc = { \
+               .name = #label, \
+               .id = TWL6032_REG_##label, \
+               .n_voltages = TWL603X_SMPS_NUMBER_VOLTAGES, \
+               .ops = &twldvssmps_ops, \
+               .type = REGULATOR_VOLTAGE, \
+               .owner = THIS_MODULE, \
+               }, \
+       }
+
 /*
  * We list regulators here if systems need some level of
  * software control over them after boot.
@@ -961,32 +1218,46 @@ static struct twlreg_info twl_regs[] = {
        /* 6030 REG with base as PMC Slave Misc : 0x0030 */
        /* Turnon-delay and remap configuration values for 6030 are not
           verified since the specification is not public */
-       TWL6030_ADJUSTABLE_LDO(VAUX1_6030, 0x54, 1000, 3300, 1),
-       TWL6030_ADJUSTABLE_LDO(VAUX2_6030, 0x58, 1000, 3300, 2),
-       TWL6030_ADJUSTABLE_LDO(VAUX3_6030, 0x5c, 1000, 3300, 3),
-       TWL6030_ADJUSTABLE_LDO(VMMC, 0x68, 1000, 3300, 4),
-       TWL6030_ADJUSTABLE_LDO(VPP, 0x6c, 1000, 3300, 5),
-       TWL6030_ADJUSTABLE_LDO(VUSIM, 0x74, 1000, 3300, 7),
-       TWL6030_FIXED_LDO(VANA, 0x50, 2100, 15, 0),
-       TWL6030_FIXED_LDO(VCXIO, 0x60, 1800, 16, 0),
-       TWL6030_FIXED_LDO(VDAC, 0x64, 1800, 17, 0),
-       TWL6030_FIXED_LDO(VUSB, 0x70, 3300, 18, 0),
-       TWL6030_FIXED_RESOURCE(CLK32KG, 0x8C, 48, 0),
-
-       /* 6025 are renamed compared to 6030 versions */
-       TWL6025_ADJUSTABLE_LDO(LDO2, 0x54, 1000, 3300, 1),
-       TWL6025_ADJUSTABLE_LDO(LDO4, 0x58, 1000, 3300, 2),
-       TWL6025_ADJUSTABLE_LDO(LDO3, 0x5c, 1000, 3300, 3),
-       TWL6025_ADJUSTABLE_LDO(LDO5, 0x68, 1000, 3300, 4),
-       TWL6025_ADJUSTABLE_LDO(LDO1, 0x6c, 1000, 3300, 5),
-       TWL6025_ADJUSTABLE_LDO(LDO7, 0x74, 1000, 3300, 7),
-       TWL6025_ADJUSTABLE_LDO(LDO6, 0x60, 1000, 3300, 16),
-       TWL6025_ADJUSTABLE_LDO(LDOLN, 0x64, 1000, 3300, 17),
-       TWL6025_ADJUSTABLE_LDO(LDOUSB, 0x70, 1000, 3300, 18),
-
-       TWL6025_ADJUSTABLE_SMPS(SMPS3, 0x34, 1),
-       TWL6025_ADJUSTABLE_SMPS(SMPS4, 0x10, 2),
-       TWL6025_ADJUSTABLE_SMPS(VIO, 0x16, 3),
+       TWL6030_ADJUSTABLE_LDO(VAUX1_6030, 0x54, 1000, 3300),
+       TWL6030_ADJUSTABLE_LDO(VAUX2_6030, 0x58, 1000, 3300),
+       TWL6030_ADJUSTABLE_LDO(VAUX3_6030, 0x5c, 1000, 3300),
+       TWL6030_ADJUSTABLE_LDO(VMMC, 0x68, 1000, 3300),
+       TWL6030_ADJUSTABLE_LDO(VPP, 0x6c, 1000, 3300),
+       TWL6030_ADJUSTABLE_LDO(VUSIM, 0x74, 1000, 3300),
+       TWL6030_FIXED_LDO(VANA, 0x50, 2100, 0),
+       TWL6030_FIXED_LDO(VCXIO, 0x60, 1800, 0),
+       TWL6030_FIXED_LDO(VDAC, 0x64, 1800, 0),
+       TWL6030_FIXED_LDO(VUSB, 0x70, 3300, 0),
+       TWL6030_FIXED_RESOURCE(CLK32KG, 0x8C, 0),
+       TWL6030_FIXED_RESOURCE(CLK32KAUDIO, 0x8F, 0),
+       TWL6030_ADJUSTABLE_SMPS(VDD1, 0x22, 600, 4000),
+       TWL6030_ADJUSTABLE_SMPS(VDD2, 0x28, 600, 4000),
+       TWL6030_ADJUSTABLE_SMPS(VDD3, 0x2e, 600, 4000),
+       TWL6030_ADJUSTABLE_SMPS(VMEM, 0x34, 600, 4000),
+       TWL6030_ADJUSTABLE_SMPS(V2V1, 0x1c, 1800, 2100),
+
+       /* 6032 are renamed compared to 6030 versions */
+       TWL6032_ADJUSTABLE_LDO(LDO2, 0x54, 1000, 3300),
+       TWL6032_ADJUSTABLE_LDO(LDO4, 0x58, 1000, 3300),
+       TWL6032_ADJUSTABLE_LDO(LDO3, 0x5c, 1000, 3300),
+       TWL6032_ADJUSTABLE_LDO(LDO5, 0x68, 1000, 3300),
+       TWL6032_ADJUSTABLE_LDO(LDO1, 0x6c, 1000, 3300),
+       TWL6032_ADJUSTABLE_LDO(LDO7, 0x74, 1000, 3300),
+       TWL6032_ADJUSTABLE_LDO(LDO6, 0x60, 1000, 3300),
+       TWL6032_ADJUSTABLE_LDO(LDOLN, 0x64, 1000, 3300),
+       TWL6032_ADJUSTABLE_LDO(LDOUSB, 0x70, 1000, 3300),
+
+       TWL6032_ADJUSTABLE_SMPS(SMPS3, 0x34),
+       TWL6032_ADJUSTABLE_SMPS(SMPS4, 0x10),
+       TWL6032_ADJUSTABLE_SMPS(VIO, 0x16),
+       
+       TWL6032_ADJUSTABLE_DVSSMPS(SMPS1, 0x22),
+       TWL6032_ADJUSTABLE_DVSSMPS(SMPS2, 0x28),
+       TWL6032_ADJUSTABLE_DVSSMPS(SMPS5, 0x26),
+       
+       TWL6030_EXTERNAL_CONTROL_PIN(SYSEN, 0x83, 0),
+       TWL6030_EXTERNAL_CONTROL_PIN(REGEN1, 0x7d, 0),
+
 };
 
 static u8 twl_get_smps_offset(void)
@@ -1014,6 +1285,7 @@ static int __devinit twlreg_probe(struct platform_device *pdev)
        struct regulator_init_data      *initdata;
        struct regulation_constraints   *c;
        struct regulator_dev            *rdev;
+       int ret;
 
        for (i = 0, info = NULL; i < ARRAY_SIZE(twl_regs); i++) {
                if (twl_regs[i].desc.id != pdev->id)
@@ -1049,24 +1321,37 @@ static int __devinit twlreg_probe(struct platform_device *pdev)
        case TWL4030_REG_VINTDIG:
                c->always_on = true;
                break;
+       case TWL6030_REG_VUSB:
+               /* Program CFG_LDO_PD2 register and set VUSB bit */
+               ret = twl_i2c_write_u8(TWL6030_MODULE_ID0, 0x1,
+                               TWL6030_CFG_LDO_PD2);
+               if (ret < 0)
+                       return ret;
+
+               /* Program MISC2 register and set bit VUSB_IN_VBAT */
+               ret = twl_i2c_write_u8(TWL6030_MODULE_ID0, 0x10, TWL6030_MISC2);
+               if (ret < 0)
+                       return ret;
+               break;
        default:
                break;
        }
 
        switch (pdev->id) {
-       case TWL6025_REG_SMPS3:
+       case TWL6032_REG_SMPS3:
                if (twl_get_smps_mult() & SMPS_MULTOFFSET_SMPS3)
                        info->flags |= SMPS_EXTENDED_EN;
                if (twl_get_smps_offset() & SMPS_MULTOFFSET_SMPS3)
                        info->flags |= SMPS_OFFSET_EN;
                break;
-       case TWL6025_REG_SMPS4:
+       case TWL6032_REG_SMPS4:
                if (twl_get_smps_mult() & SMPS_MULTOFFSET_SMPS4)
                        info->flags |= SMPS_EXTENDED_EN;
                if (twl_get_smps_offset() & SMPS_MULTOFFSET_SMPS4)
                        info->flags |= SMPS_OFFSET_EN;
                break;
-       case TWL6025_REG_VIO:
+       
+       case TWL6032_REG_VIO:
                if (twl_get_smps_mult() & SMPS_MULTOFFSET_VIO)
                        info->flags |= SMPS_EXTENDED_EN;
                if (twl_get_smps_offset() & SMPS_MULTOFFSET_VIO)
@@ -1119,7 +1404,7 @@ static int __init twlreg_init(void)
 {
        return platform_driver_register(&twlreg_driver);
 }
-subsys_initcall(twlreg_init);
+subsys_initcall_sync(twlreg_init);
 
 static void __exit twlreg_exit(void)
 {
index f9a2799c44d6e177f154f23f938acb8de47db208..7226a2db300ddebe3e8629afabc639858957f91b 100644 (file)
@@ -362,14 +362,6 @@ static irqreturn_t twl_rtc_interrupt(int irq, void *rtc)
        int res;
        u8 rd_reg;
 
-#ifdef CONFIG_LOCKDEP
-       /* WORKAROUND for lockdep forcing IRQF_DISABLED on us, which
-        * we don't want and can't tolerate.  Although it might be
-        * friendlier not to borrow this thread context...
-        */
-       local_irq_enable();
-#endif
-
        res = twl_rtc_read_u8(&rd_reg, REG_RTC_STATUS_REG);
        if (res)
                goto out;
@@ -428,24 +420,12 @@ static struct rtc_class_ops twl_rtc_ops = {
 static int __devinit twl_rtc_probe(struct platform_device *pdev)
 {
        struct rtc_device *rtc;
-       int ret = 0;
+       int ret = -EINVAL;
        int irq = platform_get_irq(pdev, 0);
        u8 rd_reg;
 
        if (irq <= 0)
-               return -EINVAL;
-
-       rtc = rtc_device_register(pdev->name,
-                                 &pdev->dev, &twl_rtc_ops, THIS_MODULE);
-       if (IS_ERR(rtc)) {
-               ret = PTR_ERR(rtc);
-               dev_err(&pdev->dev, "can't register RTC device, err %ld\n",
-                       PTR_ERR(rtc));
-               goto out0;
-
-       }
-
-       platform_set_drvdata(pdev, rtc);
+               goto out1;
 
        ret = twl_rtc_read_u8(&rd_reg, REG_RTC_STATUS_REG);
        if (ret < 0)
@@ -462,14 +442,6 @@ static int __devinit twl_rtc_probe(struct platform_device *pdev)
        if (ret < 0)
                goto out1;
 
-       ret = request_irq(irq, twl_rtc_interrupt,
-                               IRQF_TRIGGER_RISING,
-                               dev_name(&rtc->dev), rtc);
-       if (ret < 0) {
-               dev_err(&pdev->dev, "IRQ is not free.\n");
-               goto out1;
-       }
-
        if (twl_class_is_6030()) {
                twl6030_interrupt_unmask(TWL6030_RTC_INT_MASK,
                        REG_INT_MSK_LINE_A);
@@ -480,28 +452,48 @@ static int __devinit twl_rtc_probe(struct platform_device *pdev)
        /* Check RTC module status, Enable if it is off */
        ret = twl_rtc_read_u8(&rd_reg, REG_RTC_CTRL_REG);
        if (ret < 0)
-               goto out2;
+               goto out1;
 
        if (!(rd_reg & BIT_RTC_CTRL_REG_STOP_RTC_M)) {
                dev_info(&pdev->dev, "Enabling TWL-RTC.\n");
                rd_reg = BIT_RTC_CTRL_REG_STOP_RTC_M;
                ret = twl_rtc_write_u8(rd_reg, REG_RTC_CTRL_REG);
                if (ret < 0)
-                       goto out2;
+                       goto out1;
        }
 
        /* init cached IRQ enable bits */
        ret = twl_rtc_read_u8(&rtc_irq_bits, REG_RTC_INTERRUPTS_REG);
        if (ret < 0)
+               goto out1;
+
+       rtc = rtc_device_register(pdev->name,
+                                 &pdev->dev, &twl_rtc_ops, THIS_MODULE);
+       if (IS_ERR(rtc)) {
+               ret = PTR_ERR(rtc);
+               dev_err(&pdev->dev, "can't register RTC device, err %ld\n",
+                       PTR_ERR(rtc));
+               goto out1;
+
+       }
+
+       ret = request_threaded_irq(irq, NULL, twl_rtc_interrupt,
+                                  IRQF_TRIGGER_RISING,
+                                  dev_name(&rtc->dev), rtc);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "IRQ is not free.\n");
                goto out2;
+       }
 
-       return ret;
+       if (enable_irq_wake(irq) < 0)
+               dev_warn(&pdev->dev, "Cannot enable wakeup for IRQ %d\n", irq);
+
+       platform_set_drvdata(pdev, rtc);
+       return 0;
 
 out2:
-       free_irq(irq, rtc);
-out1:
        rtc_device_unregister(rtc);
-out0:
+out1:
        return ret;
 }
 
old mode 100644 (file)
new mode 100755 (executable)
index ba4f886..accbd9a
@@ -71,6 +71,8 @@
 #define TWL4030_MODULE_PM_RECEIVER     0x15
 #define TWL4030_MODULE_RTC             0x16
 #define TWL4030_MODULE_SECURED_REG     0x17
+#define TWL6032_MODULE_CHARGER         0x18
+#define TWL6030_MODULE_SLAVE_RES       0x19
 
 #define TWL_MODULE_USB         TWL4030_MODULE_USB
 #define TWL_MODULE_AUDIO_VOICE TWL4030_MODULE_AUDIO_VOICE
 #define TWL_MODULE_PM_RECEIVER TWL4030_MODULE_PM_RECEIVER
 #define TWL_MODULE_RTC         TWL4030_MODULE_RTC
 #define TWL_MODULE_PWM         TWL4030_MODULE_PWM0
+#define TWL6030_MODULE_CHARGER TWL4030_MODULE_MAIN_CHARGE
+#define TWL_MODULE_PM_SLAVE_RES        TWL6030_MODULE_SLAVE_RES
+#define TWL_MODULE_PM_DVS                   0x1A  //add
 
+#define TWL6030_MODULE_GASGAUGE 0x0B
 #define TWL6030_MODULE_ID0     0x0D
 #define TWL6030_MODULE_ID1     0x0E
 #define TWL6030_MODULE_ID2     0x0F
  * Offset from TWL6030_IRQ_BASE / pdata->irq_base
  */
 #define PWR_INTR_OFFSET                0
+#define TWL_VLOW_INTR_OFFSET   6
 #define HOTDIE_INTR_OFFSET     12
 #define SMPSLDO_INTR_OFFSET    13
 #define BATDETECT_INTR_OFFSET  14
 #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 */
 #define SW_FC                          (0x1 << 2)
 #define STS_MMC                        0x1
 
+#define TWL6030_MMCDEBOUNCING  0xED
+#define MMC_DEB_BYPASS                 (0x1 << 7)
+#define MMC_MINS_DEB_MASK                      (0xF << 3)
+#define MMC_MEXT_DEB_MASK                      (0x7 << 0)
+
 #define TWL6030_CFG_INPUT_PUPD3        0xF2
 #define MMC_PU                         (0x1 << 3)
 #define MMC_PD                         (0x1 << 2)
 
+#define VLOW_INT_MASK                  (0x1 << 2)
+
 #define TWL_SIL_TYPE(rev)              ((rev) & 0x00FFFFFF)
 #define TWL_SIL_REV(rev)               ((rev) >> 24)
 #define TWL_SIL_5030                   0x09002F
@@ -171,7 +186,7 @@ static inline int twl_class_is_ ##class(void)       \
 TWL_CLASS_IS(4030, TWL4030_CLASS_ID)
 TWL_CLASS_IS(6030, TWL6030_CLASS_ID)
 
-#define TWL6025_SUBCLASS       BIT(4)  /* TWL6025 has changed registers */
+#define TWL6032_SUBCLASS       BIT(4)  /* Phoenix Lite is a varient*/
 
 /*
  * Read and write single 8-bit registers
@@ -230,6 +245,11 @@ static inline int twl6030_mmc_card_detect(struct device *dev, int slot)
 #define TWL4030_SIH_CTRL_PENDDIS_MASK  BIT(1)
 #define TWL4030_SIH_CTRL_COR_MASK      BIT(2)
 
+int twl6030_register_notifier(struct notifier_block *nb,
+                               unsigned int events);
+int twl6030_unregister_notifier(struct notifier_block *nb,
+                               unsigned int events);
+
 /*----------------------------------------------------------------------*/
 
 /*
@@ -450,6 +470,23 @@ static inline int twl6030_mmc_card_detect(struct device *dev, int slot)
 
 #define TWL4030_PM_MASTER_GLOBAL_TST           0xb6
 
+#define TWL6030_PHOENIX_DEV_ON                 0x06
+
+/*
+ * TWL6030 PM Master module register offsets (use TWL_MODULE_PM_MASTER)
+ */
+
+#define TWL6030_PM_MASTER_MSK_TRANSITION       0x01
+#define TWL6030_VBATMIN_HI_THRESHOLD           0x05
+
+/*
+ * PM Slave resource module register offsets (use TWL6030_MODULE_SLAVE_RES)
+ */
+
+#define REG_VBATMIN_HI_CFG_STATE               0x1D
+
+#define VBATMIN_VLOW_EN                                0x21
+
 /*----------------------------------------------------------------------*/
 
 /* Power bus message definitions */
@@ -522,6 +559,43 @@ static inline int twl6030_mmc_card_detect(struct device *dev, int slot)
 #define RES_MAIN_REF            28
 
 #define TOTAL_RESOURCES                28
+/* 6030 extra resources */
+#define RES_V1V29              29
+#define RES_V1V8               30
+#define RES_V2V1               31
+#define RES_VDD3               32
+#define RES_VMEM               33
+#define RES_VANA               34
+#define RES_VUAX1              35
+#define RES_VCXIO              36
+#define RES_VPP                        37
+#define RES_VRTC               38
+#define RES_REGEN2             39
+#define RES_32KCLKAO           40
+#define RES_32KCLKG            41
+#define RES_32KCLKAUDIO                42
+#define RES_BIAS               43
+#define RES_VBATMIN_HI         44
+#define RES_RC6MHZ             45
+#define RES_TEMP               46
+
+/* 6032 extra resources */
+#define RES_LDOUSB             47
+#define RES_SMPS5              48
+#define RES_SMPS4              49
+#define RES_SMPS3              50
+#define RES_SMPS2              51
+#define RES_SMPS1              52
+#define RES_LDOLN              53
+#define RES_LDO7               54
+#define RES_LDO6               55
+#define RES_LDO5               56
+#define RES_LDO4               57
+#define RES_LDO3               58
+#define RES_LDO2               59
+#define RES_LDO1               60
+#define RES_VSYSMIN_HI 61
+
 /*
  * Power Bus Message Format ... these can be sent individually by Linux,
  * but are usually part of downloaded scripts that are run when various
@@ -557,6 +631,20 @@ struct twl4030_clock_init_data {
 struct twl4030_bci_platform_data {
        int *battery_tmp_tbl;
        unsigned int 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;
+
+       unsigned int sense_resistor_mohm;
+
+       /* twl6032 */
+       unsigned long features;
 };
 
 /* TWL4030_GPIO_MAX (18) GPIOs, with interrupts */
@@ -589,6 +677,7 @@ struct twl4030_gpio_platform_data {
 
 struct twl4030_madc_platform_data {
        int             irq_line;
+       int             features;
 };
 
 /* Boards have unique mappings of {row, col} --> keycode.
@@ -641,21 +730,41 @@ struct twl4030_script {
 struct twl4030_resconfig {
        u8 resource;
        u8 devgroup;    /* Processor group that Power resource belongs to */
+       /* The following are used by TWL4030 only */
        u8 type;        /* Power resource addressed, 6 / broadcast message */
        u8 type2;       /* Power resource addressed, 3 / broadcast message */
        u8 remap_off;   /* off state remapping */
        u8 remap_sleep; /* sleep state remapping */
 };
 
+struct twl4030_system_config {
+       char *name;
+       u8 group;
+};
+
 struct twl4030_power_data {
-       struct twl4030_script **scripts;
-       unsigned num;
+       struct twl4030_script **scripts;        /* used in TWL4030 only */
+       unsigned num;                           /* used in TWL4030 only */
        struct twl4030_resconfig *resource_config;
+       struct twl4030_system_config *sys_config; /*system resources*/
 #define TWL4030_RESCONFIG_UNDEF        ((u8)-1)
 };
 
+#ifdef CONFIG_TWL4030_POWER
 extern void twl4030_power_init(struct twl4030_power_data *triton2_scripts);
 extern int twl4030_remove_script(u8 flags);
+#else
+static inline void twl4030_power_init(struct twl4030_power_data *triton2_scripts) { }
+static inline int twl4030_remove_script(u8 flags) { return -EINVAL; }
+#endif
+
+#ifdef CONFIG_TWL6030_POWER
+extern void twl6030_power_init(struct twl4030_power_data *power_data,\
+                                       unsigned long features);
+#else
+extern inline void twl6030_power_init(struct twl4030_power_data *power_data,\
+                                       unsigned long features) { }
+#endif
 
 struct twl4030_codec_audio_data {
        unsigned int digimic_delay; /* in ms */
@@ -664,11 +773,26 @@ struct twl4030_codec_audio_data {
        unsigned int check_defaults:1;
        unsigned int reset_registers:1;
        unsigned int hs_extmute:1;
+       u16 hs_left_step;
+       u16 hs_right_step;
+       u16 hf_left_step;
+       u16 hf_right_step;
+       u16 ep_step;
        void (*set_hs_extmute)(int mute);
+
+       /* twl6040 */
+       int vddhf_uV;
 };
 
 struct twl4030_codec_vibra_data {
        unsigned int    coexist;
+
+       /* timed-output based implementations */
+       int max_timeout;
+       int initial_vibrate;
+       int (*init)(void);
+       void (*exit)(void);
+       u8  voltage_raise_speed;
 };
 
 struct twl4030_codec_data {
@@ -676,13 +800,22 @@ struct twl4030_codec_data {
        struct twl4030_codec_audio_data         *audio;
        struct twl4030_codec_vibra_data         *vibra;
 
+       int (*init)(void);
+       void (*exit)(void);
+
        /* twl6040 */
        int audpwron_gpio;      /* audio power-on gpio */
        int naudint_irq;        /* audio interrupt */
+       unsigned int irq_base;
+       int (*get_ext_clk32k)(void);
+       void (*put_ext_clk32k)(void);
+       int (*set_ext_clk32k)(bool on);
 };
 
 struct twl4030_platform_data {
        unsigned                                irq_base, irq_end;
+       int(*pre_init)(void);
+       int(*set_init)(void);
        struct twl4030_clock_init_data          *clock;
        struct twl4030_bci_platform_data        *bci;
        struct twl4030_gpio_platform_data       *gpio;
@@ -710,6 +843,10 @@ struct twl4030_platform_data {
        struct regulator_init_data              *vintana1;
        struct regulator_init_data              *vintana2;
        struct regulator_init_data              *vintdig;
+       /* TWL6030 DCDC regulators */
+       struct regulator_init_data              *vdd3;
+       struct regulator_init_data              *vmem;
+       struct regulator_init_data              *v2v1;
        /* TWL6030 LDO regulators */
        struct regulator_init_data              *vmmc;
        struct regulator_init_data              *vpp;
@@ -718,7 +855,8 @@ struct twl4030_platform_data {
        struct regulator_init_data              *vcxio;
        struct regulator_init_data              *vusb;
        struct regulator_init_data              *clk32kg;
-       /* TWL6025 LDO regulators */
+       struct regulator_init_data              *clk32kaudio;
+       /* TWL6032 LDO regulators */
        struct regulator_init_data              *ldo1;
        struct regulator_init_data              *ldo2;
        struct regulator_init_data              *ldo3;
@@ -728,10 +866,21 @@ struct twl4030_platform_data {
        struct regulator_init_data              *ldo7;
        struct regulator_init_data              *ldoln;
        struct regulator_init_data              *ldousb;
-       /* TWL6025 DCDC regulators */
+       /* TWL6032 DCDC regulators */
        struct regulator_init_data              *smps3;
        struct regulator_init_data              *smps4;
-       struct regulator_init_data              *vio6025;
+       struct regulator_init_data              *vio6032;
+       
+       struct regulator_init_data            *smps1; //add 
+       struct regulator_init_data            *smps2; //add 
+       struct regulator_init_data            *smps5; //add 
+       
+       /* External control pins */
+       struct regulator_init_data              *sysen;
+       struct regulator_init_data              *regen1;
+
+
+       
 };
 
 /*----------------------------------------------------------------------*/
@@ -813,21 +962,33 @@ static inline int twl4030charger_usb_en(int enable) { return 0; }
 #define TWL6030_REG_VRTC       47
 #define TWL6030_REG_CLK32KG    48
 
-/* LDOs on 6025 have different names */
-#define TWL6025_REG_LDO2       49
-#define TWL6025_REG_LDO4       50
-#define TWL6025_REG_LDO3       51
-#define TWL6025_REG_LDO5       52
-#define TWL6025_REG_LDO1       53
-#define TWL6025_REG_LDO7       54
-#define TWL6025_REG_LDO6       55
-#define TWL6025_REG_LDOLN      56
-#define TWL6025_REG_LDOUSB     57
-
-/* 6025 DCDC supplies */
-#define TWL6025_REG_SMPS3      58
-#define TWL6025_REG_SMPS4      59
-#define TWL6025_REG_VIO                60
+/* LDOs on 6032 have different names */
+#define TWL6032_REG_LDO2       49
+#define TWL6032_REG_LDO4       50
+#define TWL6032_REG_LDO3       51
+#define TWL6032_REG_LDO5       52
+#define TWL6032_REG_LDO1       53
+#define TWL6032_REG_LDO7       54
+#define TWL6032_REG_LDO6       55
+#define TWL6032_REG_LDOLN      56
+#define TWL6032_REG_LDOUSB     57
+
+/* 6032 DCDC supplies */
+#define TWL6032_REG_SMPS3      58
+#define TWL6032_REG_SMPS4      59
+#define TWL6032_REG_VIO                60
+
+#define TWL6030_REG_CLK32KAUDIO        61
+
+/* External control pins */
+#define TWL6030_REG_SYSEN      62
+#define TWL6030_REG_REGEN1     63
+
+#define TWL6032_REG_SMPS1 64  //add 
+#define TWL6032_REG_SMPS2 65  //add 
+#define TWL6032_REG_SMPS5 66 //add 
+
+#define TWL6032_PREQ1_RES_ASS_A        0xd7
 
 
 #endif /* End of __TWL4030_H */