#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)
{
.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
#endif
#ifdef CONFIG_I2C3_RK30
+
static struct i2c_board_info __initdata i2c3_info[] = {
+
+
};
#endif
--- /dev/null
+#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
#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? */
{ 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
+
+
};
/*----------------------------------------------------------------------*/
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);
/* 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
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];
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;
/* 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
/* 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);
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");
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
{
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",
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",
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
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);
}
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,
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",
/* 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))
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))
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 */
/*----------------------------------------------------------------------*/
-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;
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");
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 {
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
pdata->irq_end);
} else {
status = twl6030_init_irq(client->irq, pdata->irq_base,
- pdata->irq_end);
+ pdata->irq_end, features);
}
if (status < 0)
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);
{ "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);
.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)
{
#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);
--- /dev/null
+/*
+ * 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");
#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 */
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 */
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.
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);
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);
}
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");
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)
#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;
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);
}
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;
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
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);
}
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);
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;
}
--- /dev/null
+/*
+ *
+ * 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, ®, 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, ®_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");
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * /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");
#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
#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)
#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;
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);
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;
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;
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 */
(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;
}
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;
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)
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);
+}
+
/*----------------------------------------------------------------------*/
/*
.set_mode = twl6030reg_set_mode,
.get_status = twl6030reg_get_status,
+
+ .set_suspend_enable = twl6030ldo_suspend_enable,
+ .set_suspend_disable = twl6030ldo_suspend_disable,
};
/*----------------------------------------------------------------------*/
.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 = {
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);
.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) { \
}, \
}
-#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, \
}, \
}
-#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, \
}, \
}
-#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.
/* 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)
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)
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)
{
return platform_driver_register(&twlreg_driver);
}
-subsys_initcall(twlreg_init);
+subsys_initcall_sync(twlreg_init);
static void __exit twlreg_exit(void)
{
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;
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)
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);
/* 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;
}
#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
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
#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);
+
/*----------------------------------------------------------------------*/
/*
#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 */
#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
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 */
struct twl4030_madc_platform_data {
int irq_line;
+ int features;
};
/* Boards have unique mappings of {row, col} --> keycode.
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 */
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 {
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;
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;
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;
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;
+
+
+
};
/*----------------------------------------------------------------------*/
#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 */