From: 张晴 Date: Wed, 2 May 2012 08:39:17 +0000 (+0800) Subject: rk30:phone:add pmu tps80032 X-Git-Tag: firefly_0821_release~9276 X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=50276f7c94d9298dc888fbe9329b2da7bd320fdc;p=firefly-linux-kernel-4.4.55.git rk30:phone:add pmu tps80032 --- diff --git a/arch/arm/mach-rk30/board-rk30-phone-loquat.c b/arch/arm/mach-rk30/board-rk30-phone-loquat.c index 0fa69513d993..b34cb32c0e90 100755 --- a/arch/arm/mach-rk30/board-rk30-phone-loquat.c +++ b/arch/arm/mach-rk30/board-rk30-phone-loquat.c @@ -1282,7 +1282,7 @@ static struct i2c_board_info __initdata i2c0_info[] = { #ifdef CONFIG_I2C1_RK30 #include "board-rk30-sdk-wm8326.c" - +#include "board-rk30-sdk-twl80032.c" static struct i2c_board_info __initdata i2c1_info[] = { #if defined (CONFIG_MFD_WM831X_I2C) { @@ -1293,6 +1293,17 @@ static struct i2c_board_info __initdata i2c1_info[] = { .platform_data = &wm831x_platdata, }, #endif +#if defined (CONFIG_TWL4030_CORE) + { + .type = "twl6032", + .addr = 0x48, + .flags = 0, + .irq = RK30_PIN6_PA4, + .platform_data = &tps80032_data, + + }, + +#endif }; #endif @@ -1320,7 +1331,10 @@ static struct i2c_board_info __initdata i2c2_info[] = { #endif #ifdef CONFIG_I2C3_RK30 + static struct i2c_board_info __initdata i2c3_info[] = { + + }; #endif diff --git a/arch/arm/mach-rk30/board-rk30-sdk-twl80032.c b/arch/arm/mach-rk30/board-rk30-sdk-twl80032.c new file mode 100755 index 000000000000..e0c99894abce --- /dev/null +++ b/arch/arm/mach-rk30/board-rk30-sdk-twl80032.c @@ -0,0 +1,496 @@ +#include +#include + +#include + +#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 diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c old mode 100644 new mode 100755 index b8f2a4e7f6e7..26b4db7a23c7 --- a/drivers/mfd/twl-core.c +++ b/drivers/mfd/twl-core.c @@ -33,11 +33,13 @@ #include #include #include +#include #include #include #include +#include "twl-core.h" #if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3) #include @@ -83,7 +85,13 @@ #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 @@ -116,7 +124,10 @@ #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 @@ -126,7 +137,8 @@ /* 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) \ @@ -136,12 +148,19 @@ #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[] */ @@ -187,6 +206,7 @@ #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 @@ -198,7 +218,7 @@ #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 @@ -225,6 +245,13 @@ #define TWL5031 BIT(2) /* twl5031 has different registers */ #define TWL6030_CLASS BIT(3) /* TWL6030 class */ +/* need to access USB_PRODUCT_ID_LSB to identify which 6030 varient we are */ +#define USB_PRODUCT_ID_LSB 0x02 + +/* need to check eeprom revision and jtagver number */ +#define TWL6030_REG_EPROM_REV 0xdf +#define TWL6030_REG_JTAGVERNUM 0x87 + /*----------------------------------------------------------------------*/ /* is driver active, bound to a chip? */ @@ -332,7 +359,11 @@ static struct twl_mapping twl6030_map[] = { { SUB_CHIP_ID0, TWL6030_BASEADD_RTC }, { SUB_CHIP_ID0, TWL6030_BASEADD_MEM }, - { SUB_CHIP_ID1, TWL6025_BASEADD_CHARGER }, + { SUB_CHIP_ID1, TWL6032_BASEADD_CHARGER }, + { SUB_CHIP_ID0, TWL6030_BASEADD_PM_SLAVE_RES }, + { SUB_DVS_ID, TWL6030_BASEADD_PM_SLAVE_MISC }, //add + + }; /*----------------------------------------------------------------------*/ @@ -362,23 +393,38 @@ int twl_i2c_write(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes) pr_err("%s: invalid module number %d\n", DRIVER_NAME, mod_no); return -EPERM; } - sid = twl_map[mod_no].sid; - twl = &twl_modules[sid]; - if (unlikely(!inuse)) { - pr_err("%s: client %d is not initialized\n", DRIVER_NAME, sid); + pr_err("%s: not initialized\n", DRIVER_NAME); return -EPERM; } + sid = twl_map[mod_no].sid; + twl = &twl_modules[sid]; + mutex_lock(&twl->xfer_lock); /* * [MSG1]: fill the register address data * fill the data Tx buffer */ msg = &twl->xfer_msg[0]; + #if 1 //add + if((reg == 0x26)||(reg == 0x2C)||(reg == 0x2A)) + { + msg->addr = 0x12; + reg--; + } + else + { + msg->addr = twl->address; + + } + #else msg->addr = twl->address; + #endif + msg->len = num_bytes + 1; msg->flags = 0; msg->buf = value; + msg->scl_rate = 100*1000; //add /* over write the first byte of buffer with the register address */ *value = twl_map[mod_no].base + reg; ret = i2c_transfer(twl->client->adapter, twl->xfer_msg, 1); @@ -386,8 +432,9 @@ int twl_i2c_write(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes) /* i2c_transfer returns number of messages transferred */ if (ret != 1) { - pr_err("%s: i2c_write failed to transfer all messages\n", - DRIVER_NAME); + pr_err("%s: i2c_write failed to transfer all messages " + "(addr 0x%04x, reg %d, len %d)\n", + DRIVER_NAME, twl->address, reg, msg->len); if (ret < 0) return ret; else @@ -414,18 +461,17 @@ int twl_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes) int sid; struct twl_client *twl; struct i2c_msg *msg; - if (unlikely(mod_no > TWL_MODULE_LAST)) { pr_err("%s: invalid module number %d\n", DRIVER_NAME, mod_no); return -EPERM; } - sid = twl_map[mod_no].sid; - twl = &twl_modules[sid]; - if (unlikely(!inuse)) { - pr_err("%s: client %d is not initialized\n", DRIVER_NAME, sid); + pr_err("%s: not initialized\n", DRIVER_NAME); return -EPERM; } + sid = twl_map[mod_no].sid; + twl = &twl_modules[sid]; + mutex_lock(&twl->xfer_lock); /* [MSG1] fill the register address data */ msg = &twl->xfer_msg[0]; @@ -434,6 +480,7 @@ int twl_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes) msg->flags = 0; /* Read the register value */ val = twl_map[mod_no].base + reg; msg->buf = &val; + msg->scl_rate = 100*1000; //add /* [MSG2] fill the data rx buffer */ msg = &twl->xfer_msg[1]; msg->addr = twl->address; @@ -445,8 +492,9 @@ int twl_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes) /* i2c_transfer returns number of messages transferred */ if (ret != 2) { - pr_err("%s: i2c_read failed to transfer all messages\n", - DRIVER_NAME); + pr_err("%s: i2c_read failed to transfer all messages " + "(addr 0x%04x, reg %d, len %d)\n", + DRIVER_NAME, twl->address, reg, msg->len); if (ret < 0) return ret; else @@ -470,6 +518,20 @@ int twl_i2c_write_u8(u8 mod_no, u8 value, u8 reg) /* 2 bytes offset 1 contains the data offset 0 is used by i2c_write */ u8 temp_buffer[2] = { 0 }; + + #if 1 //add + if(reg == 0x26) + { + temp_buffer[1] = 0x43; + twl_i2c_write(mod_no, temp_buffer, 0x25, 1); + + temp_buffer[1] = 0x07; + twl_i2c_write(mod_no, temp_buffer, reg, 1); + + } + + #endif + /* offset 1 contains the data */ temp_buffer[1] = value; return twl_i2c_write(mod_no, temp_buffer, reg, 1); @@ -552,7 +614,6 @@ add_numbered_child(unsigned chip, const char *name, int num, struct platform_device *pdev; struct twl_client *twl = &twl_modules[chip]; int status; - pdev = platform_device_alloc(name, num); if (!pdev) { dev_dbg(&twl->client->dev, "can't alloc dev\n"); @@ -633,6 +694,9 @@ add_regulator(int num, struct regulator_init_data *pdata, return add_regulator_linked(num, pdata, NULL, 0, features); } +#define SET_LDO_STATE_MEM(ldo, state) \ + ldo->constraints.state_mem.disabled = state + /* * NOTE: We know the first 8 IRQs after pdata->base_irq are * for the PIH, and the next are for the PWR_INT SIH, since @@ -644,6 +708,8 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features) { struct device *child; unsigned sub_chip_id; + u8 eepromrev = 0; + u8 twlrev = 0; if (twl_has_gpio() && pdata->gpio) { child = add_child(SUB_CHIP_ID1, "twl4030_gpio", @@ -660,6 +726,15 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features) if (IS_ERR(child)) return PTR_ERR(child); } + if (twl_has_bci() && pdata->bci) { + pdata->bci->features = features; + child = add_child(1, "twl6030_bci", + pdata->bci, sizeof(*pdata->bci), + false, + pdata->irq_base + CHARGER_INTR_OFFSET, + pdata->irq_base + CHARGERFAULT_INTR_OFFSET); + } + if (twl_has_madc() && pdata->madc) { child = add_child(2, "twl4030_madc", @@ -669,6 +744,16 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features) return PTR_ERR(child); } + if (twl_has_gpadc() && pdata->madc) { + pdata->madc->features = features; + child = add_child(1, "twl6030_gpadc", + pdata->madc, sizeof(*pdata->madc), + true, pdata->irq_base + MADC_INTR_OFFSET, + pdata->irq_base + GPADCSW_INTR_OFFSET); + if (IS_ERR(child)) + return PTR_ERR(child); + } + if (twl_has_rtc()) { /* * REVISIT platform_data here currently might expose the @@ -750,28 +835,23 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features) static struct regulator_consumer_supply usb3v3; int regulator; - if (twl_has_regulator()) { - /* this is a template that gets copied */ - struct regulator_init_data usb_fixed = { - .constraints.valid_modes_mask = - REGULATOR_MODE_NORMAL - | REGULATOR_MODE_STANDBY, - .constraints.valid_ops_mask = - REGULATOR_CHANGE_MODE - | REGULATOR_CHANGE_STATUS, - }; - - if (features & TWL6025_SUBCLASS) { + if (features & TWL6032_SUBCLASS) { usb3v3.supply = "ldousb"; - regulator = TWL6025_REG_LDOUSB; + regulator = TWL6032_REG_LDOUSB; + child = add_regulator_linked(regulator, + pdata->ldousb, + &usb3v3, 1, + features); } else { usb3v3.supply = "vusb"; regulator = TWL6030_REG_VUSB; + child = add_regulator_linked(regulator, + pdata->vusb, + &usb3v3, 1, + features); } - child = add_regulator_linked(regulator, &usb_fixed, - &usb3v3, 1, - features); + if (IS_ERR(child)) return PTR_ERR(child); } @@ -791,8 +871,8 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features) if (twl_has_regulator() && child) usb3v3.dev = child; } else if (twl_has_regulator() && twl_class_is_6030()) { - if (features & TWL6025_SUBCLASS) - child = add_regulator(TWL6025_REG_LDOUSB, + if (features & TWL6032_SUBCLASS) + child = add_regulator(TWL6032_REG_LDOUSB, pdata->ldousb, features); else child = add_regulator(TWL6030_REG_VUSB, @@ -815,6 +895,13 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features) return PTR_ERR(child); } + if (twl6030_has_pwrbutton()) { + child = add_child(1, "twl6030_pwrbutton", + NULL, 0, true, pdata->irq_base, 0); + if (IS_ERR(child)) + return PTR_ERR(child); + } + if (twl_has_codec() && pdata->codec && twl_class_is_4030()) { sub_chip_id = twl_map[TWL_MODULE_AUDIO_VOICE].sid; child = add_child(sub_chip_id, "twl4030-audio", @@ -827,7 +914,7 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features) /* Phoenix codec driver is probed directly atm */ if (twl_has_codec() && pdata->codec && twl_class_is_6030()) { sub_chip_id = twl_map[TWL_MODULE_AUDIO_VOICE].sid; - child = add_child(sub_chip_id, "twl6040-codec", + child = add_child(sub_chip_id, "twl6040-audio", pdata->codec, sizeof(*pdata->codec), false, 0, 0); if (IS_ERR(child)) @@ -923,9 +1010,36 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features) return PTR_ERR(child); } + if (twl_has_regulator() && twl_class_is_6030()) { + /* + * For TWL6032 revision < ES1.1 with EEPROM revision < rev56.0 + * LDO6 and LDOLN must be always ON + * if LDO6 or LDOLN is always on then SYSEN must be always on + * for TWL6030 or TWL6032 revision >= ES1.1 with EEPROM + * revision >= rev56.0 those LDOs can be off in sleep-mode + */ + if (features & TWL6032_SUBCLASS) { + twl_i2c_read_u8(TWL6030_MODULE_ID2, &eepromrev, + TWL6030_REG_EPROM_REV); + + twl_i2c_read_u8(TWL6030_MODULE_ID2, &twlrev, + TWL6030_REG_JTAGVERNUM); + + if ((eepromrev < 56) && (twlrev < 1)) { + SET_LDO_STATE_MEM(pdata->ldo6, false); + SET_LDO_STATE_MEM(pdata->ldoln, false); + SET_LDO_STATE_MEM(pdata->sysen, false); + WARN(1, "This TWL6032 is an older revision that " + "does not support low power " + "measurements\n"); + } + } + } + /* twl6030 regulators */ if (twl_has_regulator() && twl_class_is_6030() && - !(features & TWL6025_SUBCLASS)) { + !(features & TWL6032_SUBCLASS)) { + child = add_regulator(TWL6030_REG_VMMC, pdata->vmmc, features); if (IS_ERR(child)) @@ -966,82 +1080,137 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features) if (IS_ERR(child)) return PTR_ERR(child); - child = add_regulator(TWL6030_REG_CLK32KG, pdata->clk32kg, + child = add_regulator(TWL6030_REG_VDD1, pdata->vdd1, + features); + if (IS_ERR(child)) + return PTR_ERR(child); + + child = add_regulator(TWL6030_REG_VDD2, pdata->vdd2, + features); + if (IS_ERR(child)) + return PTR_ERR(child); + + child = add_regulator(TWL6030_REG_VDD3, pdata->vdd3, + features); + if (IS_ERR(child)) + return PTR_ERR(child); + + child = add_regulator(TWL6030_REG_VMEM, pdata->vmem, features); if (IS_ERR(child)) return PTR_ERR(child); + + child = add_regulator(TWL6030_REG_V2V1, pdata->v2v1, + features); + if (IS_ERR(child)) + return PTR_ERR(child); } - /* 6030 and 6025 share this regulator */ + /* 6030 and 6032 share this regulator */ if (twl_has_regulator() && twl_class_is_6030()) { child = add_regulator(TWL6030_REG_VANA, pdata->vana, features); if (IS_ERR(child)) return PTR_ERR(child); + child = add_regulator(TWL6030_REG_CLK32KG, pdata->clk32kg, + features); + if (IS_ERR(child)) + return PTR_ERR(child); + + child = add_regulator(TWL6030_REG_CLK32KAUDIO, + pdata->clk32kaudio, features); + if (IS_ERR(child)) + return PTR_ERR(child); + + child = add_regulator(TWL6030_REG_SYSEN, + pdata->sysen, features); + if (IS_ERR(child)) + return PTR_ERR(child); + + child = add_regulator(TWL6030_REG_REGEN1, + pdata->sysen, features); + if (IS_ERR(child)) + return PTR_ERR(child); + } - /* twl6025 regulators */ + /* twl6032 regulators */ if (twl_has_regulator() && twl_class_is_6030() && - (features & TWL6025_SUBCLASS)) { - child = add_regulator(TWL6025_REG_LDO5, pdata->ldo5, + (features & TWL6032_SUBCLASS)) { + child = add_regulator(TWL6032_REG_LDO5, pdata->ldo5, features); if (IS_ERR(child)) return PTR_ERR(child); - - child = add_regulator(TWL6025_REG_LDO1, pdata->ldo1, + + child = add_regulator(TWL6032_REG_LDO1, pdata->ldo1, + features); + if (IS_ERR(child)) + return PTR_ERR(child); + + child = add_regulator(TWL6032_REG_LDO7, pdata->ldo7, features); if (IS_ERR(child)) return PTR_ERR(child); - child = add_regulator(TWL6025_REG_LDO7, pdata->ldo7, + child = add_regulator(TWL6032_REG_LDO6, pdata->ldo6, features); if (IS_ERR(child)) return PTR_ERR(child); - child = add_regulator(TWL6025_REG_LDO6, pdata->ldo6, + child = add_regulator(TWL6032_REG_LDOLN, pdata->ldoln, features); if (IS_ERR(child)) return PTR_ERR(child); - child = add_regulator(TWL6025_REG_LDOLN, pdata->ldoln, + child = add_regulator(TWL6032_REG_LDO2, pdata->ldo2, features); if (IS_ERR(child)) return PTR_ERR(child); - child = add_regulator(TWL6025_REG_LDO2, pdata->ldo2, + child = add_regulator(TWL6032_REG_LDO4, pdata->ldo4, features); if (IS_ERR(child)) return PTR_ERR(child); - child = add_regulator(TWL6025_REG_LDO4, pdata->ldo4, + child = add_regulator(TWL6032_REG_LDO3, pdata->ldo3, features); if (IS_ERR(child)) return PTR_ERR(child); - child = add_regulator(TWL6025_REG_LDO3, pdata->ldo3, + child = add_regulator(TWL6032_REG_SMPS3, pdata->smps3, features); if (IS_ERR(child)) return PTR_ERR(child); - child = add_regulator(TWL6025_REG_SMPS3, pdata->smps3, + child = add_regulator(TWL6032_REG_SMPS4, pdata->smps4, features); if (IS_ERR(child)) return PTR_ERR(child); - child = add_regulator(TWL6025_REG_SMPS4, pdata->smps4, + child = add_regulator(TWL6032_REG_SMPS1, pdata->smps1, features); if (IS_ERR(child)) return PTR_ERR(child); - child = add_regulator(TWL6025_REG_VIO, pdata->vio6025, + child = add_regulator(TWL6032_REG_SMPS2, pdata->smps2, + features); + if (IS_ERR(child)) + return PTR_ERR(child); + + child = add_regulator(TWL6032_REG_SMPS5, pdata->smps5, features); if (IS_ERR(child)) return PTR_ERR(child); + + child = add_regulator(TWL6032_REG_VIO, pdata->vio6032, + features); + if (IS_ERR(child)) + return PTR_ERR(child); } if (twl_has_bci() && pdata->bci && - !(features & (TPS_SUBSET | TWL5031))) { + !(features & (TPS_SUBSET | TWL5031)) && (features & TWL6030_CLASS)) { child = add_child(3, "twl4030_bci", pdata->bci, sizeof(*pdata->bci), false, /* irq0 = CHG_PRES, irq1 = BCI */ @@ -1146,13 +1315,22 @@ static void clocks_init(struct device *dev, /*----------------------------------------------------------------------*/ -int twl4030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end); -int twl4030_exit_irq(void); -int twl4030_init_chip_irq(const char *chip); -int twl6030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end); -int twl6030_exit_irq(void); +#ifdef CONFIG_PM +static int twl_suspend(struct i2c_client *client, pm_message_t mesg) +{ + return irq_set_irq_wake(client->irq, 1); +} -static int twl_remove(struct i2c_client *client) +static int twl_resume(struct i2c_client *client) +{ + return irq_set_irq_wake(client->irq, 0); +} +#else +#define twl_suspend NULL +#define twl_resume NULL +#endif + +static int __devexit twl_remove(struct i2c_client *client) { unsigned i; int status; @@ -1184,7 +1362,7 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id) unsigned i; struct twl4030_platform_data *pdata = client->dev.platform_data; u8 temp; - int ret = 0; + int ret = 0, features; if (!pdata) { dev_dbg(&client->dev, "no platform data?\n"); @@ -1203,8 +1381,16 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id) for (i = 0; i < TWL_NUM_SLAVES; i++) { struct twl_client *twl = &twl_modules[i]; - + +#if 0 twl->address = client->addr + i; +#else + if( i address = client->addr + i; + else + twl->address = 0x12; //DVS i2s address +#endif + if (i == 0) twl->client = client; else { @@ -1237,9 +1423,20 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id) WARN(ret < 0, "Error: reading twl_idcode register value\n"); } + features = id->driver_data; + if (twl_class_is_6030()) { + twl_i2c_read_u8(TWL_MODULE_USB, &temp, USB_PRODUCT_ID_LSB); + if (temp == 0x32) + features |= TWL6032_SUBCLASS; + } + /* load power event scripts */ - if (twl_has_power() && pdata->power) - twl4030_power_init(pdata->power); + if (twl_has_power()) { + if (twl_class_is_4030() && pdata->power) + twl4030_power_init(pdata->power); + if (twl_class_is_6030()) + twl6030_power_init(pdata->power, features); + } /* Maybe init the T2 Interrupt subsystem */ if (client->irq @@ -1251,7 +1448,7 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id) pdata->irq_end); } else { status = twl6030_init_irq(client->irq, pdata->irq_base, - pdata->irq_end); + pdata->irq_end, features); } if (status < 0) @@ -1269,8 +1466,23 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id) I2C_SDA_CTRL_PU | I2C_SCL_CTRL_PU); twl_i2c_write_u8(TWL4030_MODULE_INTBR, temp, REG_GPPUPDCTR1); } + + status = add_children(pdata, features); - status = add_children(pdata, id->driver_data); + if (pdata && pdata->pre_init) { + ret = pdata->pre_init(); + if (ret != 0) { + printk(" tps80032 pre_init() failed: %d\n"); + } + } + + if (pdata && pdata->set_init) { + ret = pdata->set_init(); + if (ret != 0) { + printk(" tps80032 set_init() failed: %d\n"); + } + } + fail: if (status < 0) twl_remove(client); @@ -1285,7 +1497,7 @@ static const struct i2c_device_id twl_ids[] = { { "tps65930", TPS_SUBSET }, /* fewer LDOs and DACs; no charger */ { "tps65920", TPS_SUBSET }, /* fewer LDOs; no codec or charger */ { "twl6030", TWL6030_CLASS }, /* "Phoenix power chip" */ - { "twl6025", TWL6030_CLASS | TWL6025_SUBCLASS }, /* "Phoenix lite" */ + { "twl6032", TWL6030_CLASS | TWL6032_SUBCLASS }, /* Phoenix lite */ { /* end of list */ }, }; MODULE_DEVICE_TABLE(i2c, twl_ids); @@ -1295,14 +1507,16 @@ static struct i2c_driver twl_driver = { .driver.name = DRIVER_NAME, .id_table = twl_ids, .probe = twl_probe, - .remove = twl_remove, + .remove = __devexit_p(twl_remove), + .suspend = twl_suspend, + .resume = twl_resume, }; static int __init twl_init(void) { return i2c_add_driver(&twl_driver); } -subsys_initcall(twl_init); +subsys_initcall_sync(twl_init); static void __exit twl_exit(void) { diff --git a/drivers/mfd/twl-core.h b/drivers/mfd/twl-core.h index 8c50a556e986..00e6468a53b0 100644 --- a/drivers/mfd/twl-core.h +++ b/drivers/mfd/twl-core.h @@ -1,7 +1,8 @@ #ifndef __TWL_CORE_H__ #define __TWL_CORE_H__ -extern int twl6030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end); +extern int twl6030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end, + unsigned long features); extern int twl6030_exit_irq(void); extern int twl4030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end); extern int twl4030_exit_irq(void); diff --git a/drivers/mfd/twl6030-gpadc.c b/drivers/mfd/twl6030-gpadc.c new file mode 100644 index 000000000000..645731627f69 --- /dev/null +++ b/drivers/mfd/twl6030-gpadc.c @@ -0,0 +1,1446 @@ +/* + * drivers/i2c/chips/twl6030-gpadc.c + * + * TWL6030 GPADC module driver + * + * Copyright (C) 2009 Texas Instruments Inc. + * Nishant Kamat + * + * Based on twl4030-madc.c + * Copyright (C) 2008 Nokia Corporation + * Mikko Ylinen + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#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"); diff --git a/drivers/mfd/twl6030-irq.c b/drivers/mfd/twl6030-irq.c index eb3b5f88e566..eaa741203bbd 100644 --- a/drivers/mfd/twl6030-irq.c +++ b/drivers/mfd/twl6030-irq.c @@ -37,6 +37,8 @@ #include #include #include +#include +#include #include "twl-core.h" @@ -52,10 +54,10 @@ * */ -static int twl6030_interrupt_mapping[24] = { +static int twl6030_interrupt_mapping_table[24] = { PWR_INTR_OFFSET, /* Bit 0 PWRON */ PWR_INTR_OFFSET, /* Bit 1 RPWRON */ - PWR_INTR_OFFSET, /* Bit 2 BAT_VLOW */ + TWL_VLOW_INTR_OFFSET, /* Bit 2 BAT_VLOW */ RTC_INTR_OFFSET, /* Bit 3 RTC_ALARM */ RTC_INTR_OFFSET, /* Bit 4 RTC_PERIOD */ HOTDIE_INTR_OFFSET, /* Bit 5 HOT_DIE */ @@ -68,7 +70,7 @@ static int twl6030_interrupt_mapping[24] = { MMCDETECT_INTR_OFFSET, /* Bit 11 MMC */ RSV_INTR_OFFSET, /* Bit 12 Reserved */ MADC_INTR_OFFSET, /* Bit 13 GPADC_RT_EOC */ - MADC_INTR_OFFSET, /* Bit 14 GPADC_SW_EOC */ + GPADCSW_INTR_OFFSET, /* Bit 14 GPADC_SW_EOC */ GASGAUGE_INTR_OFFSET, /* Bit 15 CC_AUTOCAL */ USBOTG_INTR_OFFSET, /* Bit 16 ID_WKUP */ @@ -80,11 +82,85 @@ static int twl6030_interrupt_mapping[24] = { CHARGERFAULT_INTR_OFFSET, /* Bit 22 INT_CHRG */ RSV_INTR_OFFSET, /* Bit 23 Reserved */ }; + +static int twl6032_interrupt_mapping_table[24] = { + PWR_INTR_OFFSET, /* Bit 0 PWRON */ + PWR_INTR_OFFSET, /* Bit 1 RPWRON */ + TWL_VLOW_INTR_OFFSET, /* Bit 2 SYS_VLOW */ + RTC_INTR_OFFSET, /* Bit 3 RTC_ALARM */ + RTC_INTR_OFFSET, /* Bit 4 RTC_PERIOD */ + HOTDIE_INTR_OFFSET, /* Bit 5 HOT_DIE */ + SMPSLDO_INTR_OFFSET, /* Bit 6 VXXX_SHORT */ + PWR_INTR_OFFSET, /* Bit 7 SPDURATION */ + + PWR_INTR_OFFSET, /* Bit 8 WATCHDOG */ + BATDETECT_INTR_OFFSET, /* Bit 9 BAT */ + SIMDETECT_INTR_OFFSET, /* Bit 10 SIM */ + MMCDETECT_INTR_OFFSET, /* Bit 11 MMC */ + MADC_INTR_OFFSET, /* Bit 12 GPADC_RT_EOC */ + GPADCSW_INTR_OFFSET, /* Bit 13 GPADC_SW_EOC */ + GASGAUGE_INTR_OFFSET, /* Bit 14 CC_EOC */ + GASGAUGE_INTR_OFFSET, /* Bit 15 CC_AUTOCAL */ + + USBOTG_INTR_OFFSET, /* Bit 16 ID_WKUP */ + USBOTG_INTR_OFFSET, /* Bit 17 VBUS_WKUP */ + USBOTG_INTR_OFFSET, /* Bit 18 ID */ + USB_PRES_INTR_OFFSET, /* Bit 19 VBUS */ + CHARGER_INTR_OFFSET, /* Bit 20 CHRG_CTRL */ + CHARGERFAULT_INTR_OFFSET, /* Bit 21 EXT_CHRG */ + CHARGERFAULT_INTR_OFFSET, /* Bit 22 INT_CHRG */ + RSV_INTR_OFFSET, /* Bit 23 Reserved */ +}; + +static int *twl6030_interrupt_mapping = twl6030_interrupt_mapping_table; /*----------------------------------------------------------------------*/ -static unsigned twl6030_irq_base; +static unsigned twl6030_irq_base, twl6030_irq_end; +static int twl_irq; +static bool twl_irq_wake_enabled; +static struct task_struct *task; static struct completion irq_event; +static atomic_t twl6030_wakeirqs = ATOMIC_INIT(0); + +static u8 vbatmin_hi_threshold; + +static int twl6030_irq_pm_notifier(struct notifier_block *notifier, + unsigned long pm_event, void *unused) +{ + int chained_wakeups; + + switch (pm_event) { + case PM_SUSPEND_PREPARE: + chained_wakeups = atomic_read(&twl6030_wakeirqs); + + if (chained_wakeups && !twl_irq_wake_enabled) { + if (enable_irq_wake(twl_irq)) + pr_err("twl6030 IRQ wake enable failed\n"); + else + twl_irq_wake_enabled = true; + } else if (!chained_wakeups && twl_irq_wake_enabled) { + disable_irq_wake(twl_irq); + twl_irq_wake_enabled = false; + } + + disable_irq(twl_irq); + break; + + case PM_POST_SUSPEND: + enable_irq(twl_irq); + break; + + default: + break; + } + + return NOTIFY_DONE; +} + +static struct notifier_block twl6030_irq_pm_notifier_block = { + .notifier_call = twl6030_irq_pm_notifier, +}; /* * This thread processes interrupts reported by the Primary Interrupt Handler. @@ -104,6 +180,7 @@ static int twl6030_irq_thread(void *data) u8 bytes[4]; u32 int_sts; } sts; + u32 int_sts; /* sts.int_sts converted to CPU endianness */ /* Wait for IRQ, then read PIH irq status (also blocking) */ wait_for_completion_interruptible(&irq_event); @@ -135,9 +212,10 @@ static int twl6030_irq_thread(void *data) if (sts.bytes[2] & 0x10) sts.bytes[2] |= 0x08; - for (i = 0; sts.int_sts; sts.int_sts >>= 1, i++) { + int_sts = le32_to_cpu(sts.int_sts); + for (i = 0; int_sts; int_sts >>= 1, i++) { local_irq_disable(); - if (sts.int_sts & 0x1) { + if (int_sts & 0x1) { int module_irq = twl6030_irq_base + twl6030_interrupt_mapping[i]; generic_handle_irq(module_irq); @@ -145,8 +223,17 @@ static int twl6030_irq_thread(void *data) } local_irq_enable(); } - ret = twl_i2c_write(TWL_MODULE_PIH, sts.bytes, - REG_INT_STS_A, 3); /* clear INT_STS_A */ + + /* + * NOTE: + * Simulation confirms that documentation is wrong w.r.t the + * interrupt status clear operation. A single *byte* write to + * any one of STS_A to STS_C register results in all three + * STS registers being reset. Since it does not matter which + * value is written, all three registers are cleared on a + * single byte write, so we just use 0x0 to clear. + */ + ret = twl_i2c_write_u8(TWL_MODULE_PIH, 0x00, REG_INT_STS_A); if (ret) pr_warning("twl6030: I2C error in clearing PIH ISR\n"); @@ -172,6 +259,26 @@ static irqreturn_t handle_twl6030_pih(int irq, void *devid) return IRQ_HANDLED; } +/* + * handle_twl6030_vlow() is a threaded BAT_VLOW interrupt handler. BAT_VLOW + * is a secondary interrupt generated in twl6030_irq_thread(). + */ +static irqreturn_t handle_twl6030_vlow(int irq, void *unused) +{ + pr_err("twl6030: BAT_VLOW interrupt; threshold=%dmV\n", + 2300 + (vbatmin_hi_threshold - 0b110) * 50); + +#if 1 /* temporary */ + pr_err("%s: disabling BAT_VLOW interrupt\n", __func__); + disable_irq_nosync(twl6030_irq_base + TWL_VLOW_INTR_OFFSET); + WARN_ON(1); +#else + pr_emerg("handle_twl6030_vlow: kernel_power_off()\n"); + kernel_power_off(); +#endif + return IRQ_HANDLED; +} + /*----------------------------------------------------------------------*/ static inline void activate_irq(int irq) @@ -187,6 +294,16 @@ static inline void activate_irq(int irq) #endif } +int twl6030_irq_set_wake(struct irq_data *d, unsigned int on) +{ + if (on) + atomic_inc(&twl6030_wakeirqs); + else + atomic_dec(&twl6030_wakeirqs); + + return 0; +} + /*----------------------------------------------------------------------*/ static unsigned twl6030_irq_next; @@ -261,6 +378,16 @@ int twl6030_mmc_card_detect_config(void) ret); return ret; } + + ret = twl_i2c_write_u8(TWL6030_MODULE_ID0, + (MMC_MINS_DEB_MASK | MMC_MEXT_DEB_MASK), + TWL6030_MMCDEBOUNCING); + if (ret < 0){ + pr_err("twl6030: Failed to write MMC_MEXT_DEB_MASK %d\n", + ret); + return ret; + } + return 0; } EXPORT_SYMBOL(twl6030_mmc_card_detect_config); @@ -290,16 +417,87 @@ int twl6030_mmc_card_detect(struct device *dev, int slot) } EXPORT_SYMBOL(twl6030_mmc_card_detect); -int twl6030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end) +int twl6030_vlow_init(int vlow_irq) +{ + int status; + u8 val; + + status = twl_i2c_read_u8(TWL_MODULE_PM_SLAVE_RES, &val, + REG_VBATMIN_HI_CFG_STATE); + if (status < 0) { + pr_err("twl6030: I2C err reading REG_VBATMIN_HI_CFG_STATE: %d\n", + status); + return status; + } + + status = twl_i2c_write_u8(TWL_MODULE_PM_SLAVE_RES, + val | VBATMIN_VLOW_EN, REG_VBATMIN_HI_CFG_STATE); + if (status < 0) { + pr_err("twl6030: I2C err writing REG_VBATMIN_HI_CFG_STATE: %d\n", + status); + return status; + } + + status = twl_i2c_read_u8(TWL_MODULE_PIH, &val, REG_INT_MSK_LINE_A); + if (status < 0) { + pr_err("twl6030: I2C err reading REG_INT_MSK_LINE_A: %d\n", + status); + return status; + } + + status = twl_i2c_write_u8(TWL_MODULE_PIH, val & ~VLOW_INT_MASK, + REG_INT_MSK_LINE_A); + if (status < 0) { + pr_err("twl6030: I2C err writing REG_INT_MSK_LINE_A: %d\n", + status); + return status; + } + + status = twl_i2c_read_u8(TWL_MODULE_PIH, &val, REG_INT_MSK_STS_A); + if (status < 0) { + pr_err("twl6030: I2C err reading REG_INT_MSK_STS_A: %d\n", + status); + return status; + } + + status = twl_i2c_write_u8(TWL_MODULE_PIH, val & ~VLOW_INT_MASK, + REG_INT_MSK_STS_A); + if (status < 0) { + pr_err("twl6030: I2C err writing REG_INT_MSK_STS_A: %d\n", + status); + return status; + } + + twl_i2c_read_u8(TWL_MODULE_PM_MASTER, &vbatmin_hi_threshold, + TWL6030_VBATMIN_HI_THRESHOLD); + + /* install an irq handler for vlow */ + status = request_threaded_irq(vlow_irq, NULL, handle_twl6030_vlow, + IRQF_ONESHOT, + "TWL6030-VLOW", handle_twl6030_vlow); + if (status < 0) { + pr_err("twl6030: could not claim vlow irq %d: %d\n", vlow_irq, + status); + return status; + } + + return 0; +} + +int twl6030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end, + unsigned long features) { int status = 0; int i; - struct task_struct *task; int ret; u8 mask[4]; static struct irq_chip twl6030_irq_chip; + + if (features & TWL6032_SUBCLASS) + twl6030_interrupt_mapping = twl6032_interrupt_mapping_table; + mask[1] = 0xFF; mask[2] = 0xFF; mask[3] = 0xFF; @@ -311,6 +509,7 @@ int twl6030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end) REG_INT_STS_A, 3); /* clear INT_STS_A,B,C */ twl6030_irq_base = irq_base; + twl6030_irq_end = irq_end; /* install an irq handler for each of the modules; * clone dummy irq_chip since PIH can't *do* anything @@ -318,10 +517,12 @@ int twl6030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end) twl6030_irq_chip = dummy_irq_chip; twl6030_irq_chip.name = "twl6030"; twl6030_irq_chip.irq_set_type = NULL; + twl6030_irq_chip.irq_set_wake = twl6030_irq_set_wake; for (i = irq_base; i < irq_end; i++) { irq_set_chip_and_handler(i, &twl6030_irq_chip, handle_simple_irq); + irq_set_chip_data(i, (void *)irq_num); activate_irq(i); } @@ -344,10 +545,22 @@ int twl6030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end) pr_err("twl6030: could not claim irq%d: %d\n", irq_num, status); goto fail_irq; } + + twl_irq = irq_num; + register_pm_notifier(&twl6030_irq_pm_notifier_block); + + status = twl6030_vlow_init(twl6030_irq_base + TWL_VLOW_INTR_OFFSET); + if (status < 0) + goto fail_vlow; + return status; -fail_irq: + +fail_vlow: free_irq(irq_num, &irq_event); +fail_irq: + kthread_stop(task); + fail_kthread: for (i = irq_base; i < irq_end; i++) irq_set_chip_and_handler(i, NULL, NULL); @@ -356,11 +569,25 @@ fail_kthread: int twl6030_exit_irq(void) { + int i; + unregister_pm_notifier(&twl6030_irq_pm_notifier_block); - if (twl6030_irq_base) { + if (task) + kthread_stop(task); + + if (!twl6030_irq_base || !twl6030_irq_end) { pr_err("twl6030: can't yet clean up IRQs?\n"); return -ENOSYS; } + + free_irq(twl6030_irq_base + TWL_VLOW_INTR_OFFSET, + handle_twl6030_vlow); + + free_irq(twl_irq, &irq_event); + + for (i = twl6030_irq_base; i < twl6030_irq_end; i++) + irq_set_chip_and_handler(i, NULL, NULL); + return 0; } diff --git a/drivers/mfd/twl6030-madc.c b/drivers/mfd/twl6030-madc.c new file mode 100644 index 000000000000..f537ba5cbc87 --- /dev/null +++ b/drivers/mfd/twl6030-madc.c @@ -0,0 +1,354 @@ +/* + * + * TWL6030 MADC module driver-This driver only implements the ADC read + * functions + * + * Copyright (C) 2011 Samsung Telecommunications of America + * + * Based on twl4030-madc.c + * Copyright (C) 2008 Nokia Corporation + * Mikko Ylinen + * + * Amit Kucheria + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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"); diff --git a/drivers/mfd/twl6030-power.c b/drivers/mfd/twl6030-power.c new file mode 100644 index 000000000000..918b3c26b9a0 --- /dev/null +++ b/drivers/mfd/twl6030-power.c @@ -0,0 +1,362 @@ +/* + * Handling for Resource Mapping for TWL6030 Family of chips + * + * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/ + * Nishanth Menon + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include + +#include + +#define VREG_GRP 0 +#define MSK_TRANSITION_APP_SHIFT 0x5 + +static u8 dev_on_group; + +/** + * struct twl6030_resource_map - describe the resource mapping for TWL6030 + * @name: name of the resource + * @res_id: resource ID + * @base_addr: base address for TWL6030 + * @base_addr: type of the Resources Assignment register for TWL6032 + * base_addr = 0 for PREQx_RES_ASS_A register + * base_addr = 1 for PREQx_RES_ASS_B register + * base_addr = 2 for PREQx_RES_ASS_C register + * @group: which device group can control this resource? + * @mask: unused for TWL6030 + * @mask: bit mask of the resource in PREQx_RES_ASS_x registers for TWL6032 + */ +struct twl6030_resource_map { + char *name; + u8 res_id; + u8 base_addr; + u8 group; + u8 mask; +}; + +#define TWL6030_RES_DATA(ID, NAME, BASE_ADDR, GROUP) \ + {.res_id = ID, .name = NAME, .base_addr = BASE_ADDR,\ + .group = GROUP, .mask = 0,} + +#define TWL6032_RES_DATA(ID, NAME, BASE_ADDR, GROUP, MASK) \ + {.res_id = ID, .name = NAME, .base_addr = BASE_ADDR,\ + .group = GROUP, .mask = MASK,} + +/* list of all s/w modifiable resources in TWL6030 */ +static __initdata struct twl6030_resource_map twl6030_res_map[] = { + TWL6030_RES_DATA(RES_V1V29, "V1V29", 0x40, DEV_GRP_P1), + TWL6030_RES_DATA(RES_V1V8, "V1V8", 0x46, DEV_GRP_P1), + TWL6030_RES_DATA(RES_V2V1, "V2V1", 0x4c, DEV_GRP_P1), + TWL6030_RES_DATA(RES_VDD1, "CORE1", 0x52, DEV_GRP_P1), + TWL6030_RES_DATA(RES_VDD2, "CORE2", 0x58, DEV_GRP_P1), + TWL6030_RES_DATA(RES_VDD3, "CORE3", 0x5e, DEV_GRP_P1), + TWL6030_RES_DATA(RES_VMEM, "VMEM", 0x64, DEV_GRP_P1), + /* VANA cannot be modified */ + TWL6030_RES_DATA(RES_VUAX1, "VUAX1", 0x84, DEV_GRP_P1), + TWL6030_RES_DATA(RES_VAUX2, "VAUX2", 0x88, DEV_GRP_P1), + TWL6030_RES_DATA(RES_VAUX3, "VAUX3", 0x8c, DEV_GRP_P1), + TWL6030_RES_DATA(RES_VCXIO, "VCXIO", 0x90, DEV_GRP_P1), + TWL6030_RES_DATA(RES_VDAC, "VDAC", 0x94, DEV_GRP_P1), + TWL6030_RES_DATA(RES_VMMC1, "VMMC", 0x98, DEV_GRP_P1), + TWL6030_RES_DATA(RES_VPP, "VPP", 0x9c, DEV_GRP_P1), + /* VRTC cannot be modified */ + TWL6030_RES_DATA(RES_VUSBCP, "VUSB", 0xa0, DEV_GRP_P1), + TWL6030_RES_DATA(RES_VSIM, "VSIM", 0xa4, DEV_GRP_P1), + TWL6030_RES_DATA(RES_REGEN, "REGEN1", 0xad, DEV_GRP_P1), + TWL6030_RES_DATA(RES_REGEN2, "REGEN2", 0xb0, DEV_GRP_P1), + TWL6030_RES_DATA(RES_SYSEN, "SYSEN", 0xb3, DEV_GRP_P1), + /* NRES_PWRON cannot be modified */ + /* 32KCLKAO cannot be modified */ + TWL6030_RES_DATA(RES_32KCLKG, "32KCLKG", 0xbc, DEV_GRP_P1), + TWL6030_RES_DATA(RES_32KCLKAUDIO, "32KCLKAUDIO", 0xbf, DEV_GRP_P1), + /* BIAS cannot be modified */ + /* VBATMIN_HI cannot be modified */ + /* RC6MHZ cannot be modified */ + /* TEMP cannot be modified */ +}; + +/* list of all s/w modifiable resources in TWL6032 */ +static __initdata struct twl6030_resource_map twl6032_res_map[] = { + /* PREQx_RES_ASS_A register resources */ + TWL6032_RES_DATA(RES_LDOUSB, "VUSB", 0, DEV_GRP_P1, BIT(5)), + TWL6032_RES_DATA(RES_SMPS5, "SMPS5", 0, DEV_GRP_P1, BIT(4)), + TWL6032_RES_DATA(RES_SMPS5, "SMPS4", 0, DEV_GRP_P1, BIT(3)), + TWL6032_RES_DATA(RES_SMPS5, "SMPS3", 0, DEV_GRP_P1, BIT(2)), + TWL6032_RES_DATA(RES_SMPS5, "SMPS2", 0, DEV_GRP_P1, BIT(1)), + TWL6032_RES_DATA(RES_SMPS5, "SMPS1", 0, DEV_GRP_P1, BIT(0)), + /* PREQx_RES_ASS_B register resources */ + TWL6032_RES_DATA(RES_LDOLN, "LDOLN", 1, DEV_GRP_P1, BIT(7)), + TWL6032_RES_DATA(RES_LDO7, "LDO7", 1, DEV_GRP_P1, BIT(6)), + TWL6032_RES_DATA(RES_LDO6, "LDO6", 1, DEV_GRP_P1, BIT(5)), + TWL6032_RES_DATA(RES_LDO5, "LDO5", 1, DEV_GRP_P1, BIT(4)), + TWL6032_RES_DATA(RES_LDO4, "LDO4", 1, DEV_GRP_P1, BIT(3)), + TWL6032_RES_DATA(RES_LDO3, "LDO3", 1, DEV_GRP_P1, BIT(2)), + TWL6032_RES_DATA(RES_LDO2, "LDO2", 1, DEV_GRP_P1, BIT(1)), + TWL6032_RES_DATA(RES_LDO1, "LDO1", 1, DEV_GRP_P1, BIT(0)), + /* PREQx_RES_ASS_C register resources */ + TWL6032_RES_DATA(RES_VSYSMIN_HI, "VSYSMIN_HI", 2, DEV_GRP_P1, BIT(5)), + TWL6032_RES_DATA(RES_32KCLKG, "32KCLKG", 2, DEV_GRP_P1, BIT(4)), + TWL6032_RES_DATA(RES_32KCLKAUDIO, "32KCLKAUDIO", 2, DEV_GRP_P1, BIT(3)), + TWL6032_RES_DATA(RES_SYSEN, "SYSEN", 2, DEV_GRP_P1, BIT(2)), + TWL6032_RES_DATA(RES_REGEN2, "REGEN2", 2, DEV_GRP_P1, BIT(1)), + TWL6032_RES_DATA(RES_REGEN, "REGEN1", 2, DEV_GRP_P1, BIT(0)), +}; + +static struct twl4030_system_config twl6030_sys_config[] = { + {.name = "DEV_ON", .group = DEV_GRP_P1,}, +}; + +/* Actual power groups that TWL understands */ +#define P3_GRP_6030 BIT(2) /* secondary processor, modem, etc */ +#define P2_GRP_6030 BIT(1) /* "peripherals" */ +#define P1_GRP_6030 BIT(0) /* CPU/Linux */ + +static __init void twl6030_process_system_config(void) +{ + u8 grp; + int r; + bool i = false; + + struct twl4030_system_config *sys_config; + sys_config = twl6030_sys_config; + + while (sys_config && sys_config->name) { + if (!strcmp(sys_config->name, "DEV_ON")) { + dev_on_group = sys_config->group; + i = true; + break; + } + sys_config++; + } + if (!i) + pr_err("%s: Couldn't find DEV_ON resource configuration!" + " MOD & CON group would be kept active.\n", __func__); + + if (dev_on_group) { + r = twl_i2c_read_u8(TWL_MODULE_PM_MASTER, &grp, + TWL6030_PHOENIX_DEV_ON); + if (r) { + pr_err("%s: Error(%d) reading {addr=0x%02x}", + __func__, r, TWL6030_PHOENIX_DEV_ON); + /* + * On error resetting to 0, so that all the process + * groups are kept active. + */ + dev_on_group = 0; + } else { + /* + * Unmapped processor groups are disabled by writing + * 1 to corresponding group in DEV_ON. + */ + grp |= (dev_on_group & DEV_GRP_P1) ? 0 : P1_GRP_6030; + grp |= (dev_on_group & DEV_GRP_P2) ? 0 : P2_GRP_6030; + grp |= (dev_on_group & DEV_GRP_P3) ? 0 : P3_GRP_6030; + dev_on_group = grp; + } + + /* + * unmask PREQ transition Executes ACT2SLP and SLP2ACT sleep + * sequence + */ + r = twl_i2c_read_u8(TWL_MODULE_PM_MASTER, &grp, + TWL6030_PM_MASTER_MSK_TRANSITION); + if (r) { + pr_err("%s: Error (%d) reading" + " TWL6030_MSK_TRANSITION\n", __func__, r); + return; + } + + grp &= (dev_on_group << MSK_TRANSITION_APP_SHIFT); + + r = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, grp, + TWL6030_PM_MASTER_MSK_TRANSITION); + if (r) + pr_err("%s: Error (%d) writing to" + " TWL6030_MSK_TRANSITION\n", __func__, r); + } +} + +#define DEV_GRP_P1_OFFSET 1 +#define DEV_GRP_P2_OFFSET 4 +#define DEV_GRP_P3_OFFSET 7 + +static __init void twl6030_program_map(unsigned long features) +{ + struct twl6030_resource_map *res; + int r, i; + + if (features & TWL6032_SUBCLASS) { + /** + * mask[0] = 0 for twl_i2c_write + * mask[1]-mask[3]: PREQ1_RES_ASS_A - PREQ1_RES_ASS_C + * mask[4]-mask[6]: PREQ2_RES_ASS_A - PREQ2_RES_ASS_C + * mask[7]-mask[9]: PREQ3_RES_ASS_A - PREQ3_RES_ASS_C + */ + u8 mask[10]; + + res = twl6032_res_map; + memset(&mask[0], 0, 10); + + for (i = 0; i < ARRAY_SIZE(twl6032_res_map); i++) { + /* map back from generic device id to TWL6032 mask */ + mask[DEV_GRP_P1_OFFSET + res->base_addr] |= \ + (res->group & DEV_GRP_P1) ? res->mask : 0; + mask[DEV_GRP_P2_OFFSET + res->base_addr] |= \ + (res->group & DEV_GRP_P2) ? res->mask : 0; + mask[DEV_GRP_P3_OFFSET + res->base_addr] |= \ + (res->group & DEV_GRP_P3) ? res->mask : 0; + res++; + } + + r = twl_i2c_write(TWL6030_MODULE_ID0, &mask[0], + TWL6032_PREQ1_RES_ASS_A, 9); + + if (r) + pr_err("%s: Error(%d) programming TWL6032 PREQ " + "Assignment Registers {start addr=0xd7}\n", + __func__, r); + } else { + res = twl6030_res_map; + for (i = 0; i < ARRAY_SIZE(twl6030_res_map); i++) { + u8 grp = 0; + + /* map back from generic device id to TWL6030 ID */ + grp |= (res->group & DEV_GRP_P1) ? P1_GRP_6030 : 0; + grp |= (res->group & DEV_GRP_P2) ? P2_GRP_6030 : 0; + grp |= (res->group & DEV_GRP_P3) ? P3_GRP_6030 : 0; + + r = twl_i2c_write_u8(TWL6030_MODULE_ID0, res->group, + res->base_addr); + if (r) + pr_err("%s: Error(%d) programming map %s {" + "addr=0x%02x},grp=0x%02X\n", __func__, + r, res->name, res->base_addr, + res->group); + res++; + } + } +} + +static __init void twl6030_update_system_map + (struct twl4030_system_config *sys_list) +{ + int i; + struct twl4030_system_config *sys_res; + + while (sys_list && sys_list->name) { + sys_res = twl6030_sys_config; + for (i = 0; i < ARRAY_SIZE(twl6030_sys_config); i++) { + if (!strcmp(sys_res->name, sys_list->name)) + sys_res->group = sys_list->group & + (DEV_GRP_P1 | DEV_GRP_P2 | DEV_GRP_P3); + sys_res++; + } + sys_list++; + } +} + +static __init void twl6030_update_map(struct twl4030_resconfig *res_list, \ + unsigned long features) +{ + int i, res_idx = 0; + struct twl6030_resource_map *res; + struct twl6030_resource_map *cur_twl6030_res = twl6030_res_map; + int twl6030_res_cnt = ARRAY_SIZE(twl6030_res_map); + + if (features & TWL6032_SUBCLASS) { + cur_twl6030_res = twl6032_res_map; + twl6030_res_cnt = ARRAY_SIZE(twl6032_res_map); + } + + while (res_list->resource != TWL4030_RESCONFIG_UNDEF) { + res = cur_twl6030_res; + for (i = 0; i < twl6030_res_cnt; i++) { + if (res->res_id == res_list->resource) { + res->group = res_list->devgroup & + (DEV_GRP_P1 | DEV_GRP_P2 | DEV_GRP_P3); + break; + } + res++; + } + + if (i == twl6030_res_cnt) { + pr_err("%s: in platform_data resource index %d, cannot" + " find match for resource 0x%02x. NO Update!\n", + __func__, res_idx, res_list->resource); + } + res_list++; + res_idx++; + } +} + + +static int twl6030_power_notifier_cb(struct notifier_block *notifier, + unsigned long pm_event, void *unused) +{ + int r = 0; + + switch (pm_event) { + case PM_SUSPEND_PREPARE: + r = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, dev_on_group, + TWL6030_PHOENIX_DEV_ON); + if (r) + pr_err("%s: Error(%d) programming {addr=0x%02x}", + __func__, r, TWL6030_PHOENIX_DEV_ON); + break; + } + + return notifier_from_errno(r); +} + +static struct notifier_block twl6030_power_pm_notifier = { + .notifier_call = twl6030_power_notifier_cb, +}; + +/** + * twl6030_power_init() - Update the power map to reflect connectivity of board + * @power_data: power resource map to update (OPTIONAL) - use this if a resource + * is used by other devices other than APP (DEV_GRP_P1) + */ +void __init twl6030_power_init(struct twl4030_power_data *power_data, \ + unsigned long features) +{ + int r; + + if (power_data && (!power_data->resource_config && + !power_data->sys_config)) { + pr_err("%s: power data from platform without configuration!\n", + __func__); + return; + } + + if (power_data && power_data->resource_config) + twl6030_update_map(power_data->resource_config, features); + + if (power_data && power_data->sys_config) + twl6030_update_system_map(power_data->sys_config); + + twl6030_process_system_config(); + + twl6030_program_map(features); + + r = register_pm_notifier(&twl6030_power_pm_notifier); + if (r) + pr_err("%s: twl6030 power registration failed!\n", __func__); + + return; +} diff --git a/drivers/mfd/twl6030-poweroff.c b/drivers/mfd/twl6030-poweroff.c new file mode 100644 index 000000000000..3187806fceed --- /dev/null +++ b/drivers/mfd/twl6030-poweroff.c @@ -0,0 +1,74 @@ +/* + * /drivers/mfd/twl6030-poweroff.c + * + * Power off device + * + * Copyright (C) 2011 Texas Instruments Corporation + * + * Written by Rajeev Kulkarni + * + * 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 +#include +#include + +#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"); diff --git a/drivers/regulator/twl-regulator.c b/drivers/regulator/twl-regulator.c old mode 100644 new mode 100755 index 87fe0f75a56e..3d18ea4ce8a9 --- a/drivers/regulator/twl-regulator.c +++ b/drivers/regulator/twl-regulator.c @@ -76,12 +76,25 @@ struct twlreg_info { #define VREG_STATE 2 #define VREG_VOLTAGE 3 #define VREG_VOLTAGE_SMPS 4 +#define VREG_VOLTAGE_DVS_SMPS 3 //add /* TWL6030 Misc register offsets */ #define VREG_BC_ALL 1 #define VREG_BC_REF 2 #define VREG_BC_PROC 3 #define VREG_BC_CLK_RST 4 +/* TWL6030 LDO register values for CFG_TRANS */ +#define TWL6030_CFG_TRANS_STATE_MASK 0x03 +#define TWL6030_CFG_TRANS_STATE_OFF 0x00 +/* + * Auto means the following: + * SMPS: AUTO(PWM/PFM) + * LDO: AMS(SLP/ACT) + * resource: ON + */ +#define TWL6030_CFG_TRANS_STATE_AUTO 0x01 +#define TWL6030_CFG_TRANS_SLEEP_SHIFT 2 + /* TWL6030 LDO register values for CFG_STATE */ #define TWL6030_CFG_STATE_OFF 0x00 #define TWL6030_CFG_STATE_ON 0x01 @@ -89,7 +102,9 @@ struct twlreg_info { #define TWL6030_CFG_STATE_SLEEP 0x03 #define TWL6030_CFG_STATE_GRP_SHIFT 5 #define TWL6030_CFG_STATE_APP_SHIFT 2 -#define TWL6030_CFG_STATE_APP_MASK (0x03 << TWL6030_CFG_STATE_APP_SHIFT) +#define TWL6030_CFG_STATE_MASK 0x03 +#define TWL6030_CFG_STATE_APP_MASK (TWL6030_CFG_STATE_MASK << \ + TWL6030_CFG_STATE_APP_SHIFT) #define TWL6030_CFG_STATE_APP(v) (((v) & TWL6030_CFG_STATE_APP_MASK) >>\ TWL6030_CFG_STATE_APP_SHIFT) @@ -97,19 +112,32 @@ struct twlreg_info { #define SMPS_OFFSET_EN BIT(0) #define SMPS_EXTENDED_EN BIT(1) -/* twl6025 SMPS EPROM values */ +/* twl6032 SMPS EPROM values */ #define TWL6030_SMPS_OFFSET 0xB0 #define TWL6030_SMPS_MULT 0xB3 #define SMPS_MULTOFFSET_SMPS4 BIT(0) #define SMPS_MULTOFFSET_VIO BIT(1) #define SMPS_MULTOFFSET_SMPS3 BIT(6) + + +/* TWL6030 VUSB supplemental config registers */ +#define TWL6030_MISC2 0xE5 +#define TWL6030_CFG_LDO_PD2 0xF5 + +/* + * TWL603X SMPS has 6 bits xxxx_CFG_VOLTAGE.VSEL[5:0] to configure voltages and + * each bit combination corresponds to a particular voltage (value 63 is + * reserved). + */ +#define TWL603X_SMPS_VSEL_MASK 0x3F +#define TWL603X_SMPS_NUMBER_VOLTAGES TWL603X_SMPS_VSEL_MASK + static inline int twlreg_read(struct twlreg_info *info, unsigned slave_subgp, unsigned offset) { u8 value; int status; - status = twl_i2c_read_u8(slave_subgp, &value, info->base + offset); return (status < 0) ? status : value; @@ -161,22 +189,49 @@ static int twl6030reg_is_enabled(struct regulator_dev *rdev) struct twlreg_info *info = rdev_get_drvdata(rdev); int grp = 0, val; - if (!(twl_class_is_6030() && (info->features & TWL6025_SUBCLASS))) + if (!(info->features & TWL6032_SUBCLASS)) { grp = twlreg_read(info, TWL_MODULE_PM_RECEIVER, VREG_GRP); - if (grp < 0) - return grp; + if (grp < 0) + return grp; - if (!(twl_class_is_6030() && (info->features & TWL6025_SUBCLASS))) grp &= P1_GRP_6030; - else + val = twlreg_read(info, TWL_MODULE_PM_RECEIVER, VREG_STATE); + val = TWL6030_CFG_STATE_APP(val); + } else { + val = twlreg_read(info, TWL_MODULE_PM_RECEIVER, VREG_STATE); + val &= TWL6030_CFG_STATE_MASK; grp = 1; - - val = twlreg_read(info, TWL_MODULE_PM_RECEIVER, VREG_STATE); - val = TWL6030_CFG_STATE_APP(val); + } return grp && (val == TWL6030_CFG_STATE_ON); } +static int twl6030reg_set_trans_state(struct regulator_dev *rdev, + u8 shift, u8 val) +{ + struct twlreg_info *info = rdev_get_drvdata(rdev); + int rval; + u8 mask; + + /* Read CFG_TRANS register of TWL6030 */ + rval = twlreg_read(info, TWL_MODULE_PM_RECEIVER, VREG_TRANS); + + if (rval < 0) + return rval; + + mask = TWL6030_CFG_TRANS_STATE_MASK << shift; + val = (val << shift) & mask; + + /* If value is already set, no need to write to reg */ + if (val == (rval & mask)) + return 0; + + rval &= ~mask; + rval |= val; + + return twlreg_write(info, TWL_MODULE_PM_RECEIVER, VREG_TRANS, rval); +} + static int twl4030reg_enable(struct regulator_dev *rdev) { struct twlreg_info *info = rdev_get_drvdata(rdev); @@ -202,7 +257,7 @@ static int twl6030reg_enable(struct regulator_dev *rdev) int grp = 0; int ret; - if (!(twl_class_is_6030() && (info->features & TWL6025_SUBCLASS))) + if (!(twl_class_is_6030() && (info->features & TWL6032_SUBCLASS))) grp = twlreg_read(info, TWL_MODULE_PM_RECEIVER, VREG_GRP); if (grp < 0) return grp; @@ -210,7 +265,14 @@ static int twl6030reg_enable(struct regulator_dev *rdev) ret = twlreg_write(info, TWL_MODULE_PM_RECEIVER, VREG_STATE, grp << TWL6030_CFG_STATE_GRP_SHIFT | TWL6030_CFG_STATE_ON); - + /* + * Ensure it stays in Auto mode when we enter suspend state. + * (TWL6030 in sleep mode). + */ + if (!ret) + ret = twl6030reg_set_trans_state(rdev, + TWL6030_CFG_TRANS_SLEEP_SHIFT, + TWL6030_CFG_TRANS_STATE_AUTO); udelay(info->delay); return ret; @@ -239,7 +301,7 @@ static int twl6030reg_disable(struct regulator_dev *rdev) int grp = 0; int ret; - if (!(twl_class_is_6030() && (info->features & TWL6025_SUBCLASS))) + if (!(twl_class_is_6030() && (info->features & TWL6032_SUBCLASS))) grp = P1_GRP_6030 | P2_GRP_6030 | P3_GRP_6030; /* For 6030, set the off state for all grps enabled */ @@ -247,6 +309,11 @@ static int twl6030reg_disable(struct regulator_dev *rdev) (grp) << TWL6030_CFG_STATE_GRP_SHIFT | TWL6030_CFG_STATE_OFF); + /* Ensure it remains OFF when we enter suspend (TWL6030 in sleep). */ + if (!ret) + ret = twl6030reg_set_trans_state(rdev, + TWL6030_CFG_TRANS_SLEEP_SHIFT, + TWL6030_CFG_TRANS_STATE_OFF); return ret; } @@ -277,7 +344,12 @@ static int twl6030reg_get_status(struct regulator_dev *rdev) val = twlreg_read(info, TWL_MODULE_PM_RECEIVER, VREG_STATE); - switch (TWL6030_CFG_STATE_APP(val)) { + if (info->features & TWL6032_SUBCLASS) + val &= TWL6030_CFG_STATE_MASK; + else + val = TWL6030_CFG_STATE_APP(val); + + switch (val) { case TWL6030_CFG_STATE_ON: return REGULATOR_STATUS_NORMAL; @@ -333,7 +405,7 @@ static int twl6030reg_set_mode(struct regulator_dev *rdev, unsigned mode) int grp = 0; int val; - if (!(twl_class_is_6030() && (info->features & TWL6025_SUBCLASS))) + if (!(twl_class_is_6030() && (info->features & TWL6032_SUBCLASS))) grp = twlreg_read(info, TWL_MODULE_PM_RECEIVER, VREG_GRP); if (grp < 0) @@ -357,6 +429,18 @@ static int twl6030reg_set_mode(struct regulator_dev *rdev, unsigned mode) return twlreg_write(info, TWL_MODULE_PM_RECEIVER, VREG_STATE, val); } +static int twl6030ldo_suspend_enable(struct regulator_dev *rdev) +{ + return twl6030reg_set_trans_state(rdev, TWL6030_CFG_TRANS_SLEEP_SHIFT, + TWL6030_CFG_TRANS_STATE_AUTO); +} + +static int twl6030ldo_suspend_disable(struct regulator_dev *rdev) +{ + return twl6030reg_set_trans_state(rdev, TWL6030_CFG_TRANS_SLEEP_SHIFT, + TWL6030_CFG_TRANS_STATE_OFF); +} + /*----------------------------------------------------------------------*/ /* @@ -570,6 +654,9 @@ static struct regulator_ops twl6030ldo_ops = { .set_mode = twl6030reg_set_mode, .get_status = twl6030reg_get_status, + + .set_suspend_enable = twl6030ldo_suspend_enable, + .set_suspend_disable = twl6030ldo_suspend_disable, }; /*----------------------------------------------------------------------*/ @@ -617,6 +704,9 @@ static struct regulator_ops twl6030fixed_ops = { .set_mode = twl6030reg_set_mode, .get_status = twl6030reg_get_status, + + .set_suspend_enable = twl6030ldo_suspend_enable, + .set_suspend_disable = twl6030ldo_suspend_disable, }; static struct regulator_ops twl6030_fixed_resource = { @@ -806,7 +896,104 @@ twl6030smps_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV, return twlreg_write(info, TWL_MODULE_PM_RECEIVER, VREG_VOLTAGE_SMPS, vsel); } +//add +#if 1 +static int twl6030dvssmps_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV, + unsigned int *selector) +{ + struct twlreg_info *info = rdev_get_drvdata(rdev); + int vsel = 0; + switch (info->flags) { + case 0: + if (min_uV == 0) + vsel = 0; + else if ((min_uV >= 600000) && (max_uV <= 1300000)) { + vsel = (min_uV - 600000) / 125; + if (vsel % 100) + vsel += 100; + vsel /= 100; + vsel++; + } + /* Values 1..57 for vsel are linear and can be calculated + * values 58..62 are non linear. + */ + else if ((min_uV > 1900000) && (max_uV >= 2100000)) + vsel = 62; + else if ((min_uV > 1800000) && (max_uV >= 1900000)) + vsel = 61; + else if ((min_uV > 1500000) && (max_uV >= 1800000)) + vsel = 60; + else if ((min_uV > 1350000) && (max_uV >= 1500000)) + vsel = 59; + else if ((min_uV > 1300000) && (max_uV >= 1350000)) + vsel = 58; + else + return -EINVAL; + break; + case SMPS_OFFSET_EN: + if (min_uV == 0) + vsel = 0; + else if ((min_uV >= 700000) && (max_uV <= 1420000)) { + vsel = (min_uV - 700000) / 125; + if (vsel % 100) + vsel += 100; + vsel /= 100; + vsel++; + } + /* Values 1..57 for vsel are linear and can be calculated + * values 58..62 are non linear. + */ + else if ((min_uV > 1900000) && (max_uV >= 2100000)) + vsel = 62; + else if ((min_uV > 1800000) && (max_uV >= 1900000)) + vsel = 61; + else if ((min_uV > 1350000) && (max_uV >= 1800000)) + vsel = 60; + else if ((min_uV > 1350000) && (max_uV >= 1500000)) + vsel = 59; + else if ((min_uV > 1300000) && (max_uV >= 1350000)) + vsel = 58; + else + return -EINVAL; + break; + case SMPS_EXTENDED_EN: + if (min_uV == 0) + vsel = 0; + else if ((min_uV >= 1852000) && (max_uV <= 4013600)) { + vsel = (min_uV - 1852000) / 386; + if (vsel % 100) + vsel += 100; + vsel /= 100; + vsel++; + } + break; + case SMPS_OFFSET_EN|SMPS_EXTENDED_EN: + if (min_uV == 0) + vsel = 0; + else if ((min_uV >= 2161000) && (max_uV <= 4321000)) { + vsel = (min_uV - 1852000) / 386; + if (vsel % 100) + vsel += 100; + vsel /= 100; + vsel++; + } + break; + } + + *selector = vsel; + + return twlreg_write(info, TWL_MODULE_PM_DVS, VREG_VOLTAGE_DVS_SMPS, + vsel); +} + +static int twl6030dvssmps_get_voltage_sel(struct regulator_dev *rdev) +{ + struct twlreg_info *info = rdev_get_drvdata(rdev); + + return twlreg_read(info, TWL_MODULE_PM_DVS, VREG_VOLTAGE_DVS_SMPS); +} +#endif static int twl6030smps_get_voltage_sel(struct regulator_dev *rdev) { struct twlreg_info *info = rdev_get_drvdata(rdev); @@ -827,16 +1014,49 @@ static struct regulator_ops twlsmps_ops = { .set_mode = twl6030reg_set_mode, .get_status = twl6030reg_get_status, + + .set_suspend_enable = twl6030ldo_suspend_enable, + .set_suspend_disable = twl6030ldo_suspend_disable, +}; + +static struct regulator_ops twl6030_external_control_pin_ops = { + .enable = twl6030reg_enable, + .disable = twl6030reg_disable, + .is_enabled = twl6030reg_is_enabled, + + .set_mode = twl6030reg_set_mode, + + .get_status = twl6030reg_get_status, + + .set_suspend_enable = twl6030ldo_suspend_enable, + .set_suspend_disable = twl6030ldo_suspend_disable, }; +//add +static struct regulator_ops twldvssmps_ops = { + .list_voltage = twl6030smps_list_voltage, + + .set_voltage = twl6030dvssmps_set_voltage, + .get_voltage_sel = twl6030dvssmps_get_voltage_sel, + .enable = twl6030reg_enable, + .disable = twl6030reg_disable, + .is_enabled = twl6030reg_is_enabled, + + .set_mode = twl6030reg_set_mode, + + .get_status = twl6030reg_get_status, + + .set_suspend_enable = twl6030ldo_suspend_enable, + .set_suspend_disable = twl6030ldo_suspend_disable, +}; /*----------------------------------------------------------------------*/ #define TWL4030_FIXED_LDO(label, offset, mVolts, num, turnon_delay, \ remap_conf) \ TWL_FIXED_LDO(label, offset, mVolts, num, turnon_delay, \ remap_conf, TWL4030, twl4030fixed_ops) -#define TWL6030_FIXED_LDO(label, offset, mVolts, num, turnon_delay) \ - TWL_FIXED_LDO(label, offset, mVolts, num, turnon_delay, \ +#define TWL6030_FIXED_LDO(label, offset, mVolts, turnon_delay) \ + TWL_FIXED_LDO(label, offset, mVolts, 0x0, turnon_delay, \ 0x0, TWL6030, twl6030fixed_ops) #define TWL4030_ADJUSTABLE_LDO(label, offset, num, turnon_delay, remap_conf) { \ @@ -856,29 +1076,27 @@ static struct regulator_ops twlsmps_ops = { }, \ } -#define TWL6030_ADJUSTABLE_LDO(label, offset, min_mVolts, max_mVolts, num) { \ +#define TWL6030_ADJUSTABLE_LDO(label, offset, min_mVolts, max_mVolts) { \ .base = offset, \ - .id = num, \ .min_mV = min_mVolts, \ .max_mV = max_mVolts, \ .desc = { \ .name = #label, \ .id = TWL6030_REG_##label, \ - .n_voltages = (max_mVolts - min_mVolts)/100, \ + .n_voltages = (max_mVolts - min_mVolts)/100 + 1, \ .ops = &twl6030ldo_ops, \ .type = REGULATOR_VOLTAGE, \ .owner = THIS_MODULE, \ }, \ } -#define TWL6025_ADJUSTABLE_LDO(label, offset, min_mVolts, max_mVolts, num) { \ +#define TWL6032_ADJUSTABLE_LDO(label, offset, min_mVolts, max_mVolts) { \ .base = offset, \ - .id = num, \ .min_mV = min_mVolts, \ .max_mV = max_mVolts, \ .desc = { \ .name = #label, \ - .id = TWL6025_REG_##label, \ + .id = TWL6032_REG_##label, \ .n_voltages = ((max_mVolts - min_mVolts)/100) + 1, \ .ops = &twl6030ldo_ops, \ .type = REGULATOR_VOLTAGE, \ @@ -903,9 +1121,8 @@ static struct regulator_ops twlsmps_ops = { }, \ } -#define TWL6030_FIXED_RESOURCE(label, offset, num, turnon_delay) { \ +#define TWL6030_FIXED_RESOURCE(label, offset, turnon_delay) { \ .base = offset, \ - .id = num, \ .delay = turnon_delay, \ .desc = { \ .name = #label, \ @@ -916,21 +1133,61 @@ static struct regulator_ops twlsmps_ops = { }, \ } -#define TWL6025_ADJUSTABLE_SMPS(label, offset, num) { \ +#define TWL6030_ADJUSTABLE_SMPS(label, offset, min_mVolts, max_mVolts) { \ + .base = offset, \ + .min_mV = min_mVolts, \ + .max_mV = max_mVolts, \ + .desc = { \ + .name = #label, \ + .id = TWL6030_REG_##label, \ + .n_voltages = TWL603X_SMPS_NUMBER_VOLTAGES, \ + .ops = &twlsmps_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + }, \ + } + +#define TWL6032_ADJUSTABLE_SMPS(label, offset) { \ .base = offset, \ - .id = num, \ .min_mV = 600, \ .max_mV = 2100, \ .desc = { \ .name = #label, \ - .id = TWL6025_REG_##label, \ - .n_voltages = 63, \ + .id = TWL6032_REG_##label, \ + .n_voltages = TWL603X_SMPS_NUMBER_VOLTAGES, \ .ops = &twlsmps_ops, \ .type = REGULATOR_VOLTAGE, \ .owner = THIS_MODULE, \ }, \ } +#define TWL6030_EXTERNAL_CONTROL_PIN(label, offset, turnon_delay) { \ + .base = offset, \ + .delay = turnon_delay, \ + .desc = { \ + .name = #label, \ + .id = TWL6030_REG_##label, \ + .ops = &twl6030_external_control_pin_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + }, \ + } + + //add +#define TWL6032_ADJUSTABLE_DVSSMPS(label, offset) { \ + .base = offset, \ + .min_mV = 600, \ + .max_mV = 2100, \ + .desc = { \ + .name = #label, \ + .id = TWL6032_REG_##label, \ + .n_voltages = TWL603X_SMPS_NUMBER_VOLTAGES, \ + .ops = &twldvssmps_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + }, \ + } + /* * We list regulators here if systems need some level of * software control over them after boot. @@ -961,32 +1218,46 @@ static struct twlreg_info twl_regs[] = { /* 6030 REG with base as PMC Slave Misc : 0x0030 */ /* Turnon-delay and remap configuration values for 6030 are not verified since the specification is not public */ - TWL6030_ADJUSTABLE_LDO(VAUX1_6030, 0x54, 1000, 3300, 1), - TWL6030_ADJUSTABLE_LDO(VAUX2_6030, 0x58, 1000, 3300, 2), - TWL6030_ADJUSTABLE_LDO(VAUX3_6030, 0x5c, 1000, 3300, 3), - TWL6030_ADJUSTABLE_LDO(VMMC, 0x68, 1000, 3300, 4), - TWL6030_ADJUSTABLE_LDO(VPP, 0x6c, 1000, 3300, 5), - TWL6030_ADJUSTABLE_LDO(VUSIM, 0x74, 1000, 3300, 7), - TWL6030_FIXED_LDO(VANA, 0x50, 2100, 15, 0), - TWL6030_FIXED_LDO(VCXIO, 0x60, 1800, 16, 0), - TWL6030_FIXED_LDO(VDAC, 0x64, 1800, 17, 0), - TWL6030_FIXED_LDO(VUSB, 0x70, 3300, 18, 0), - TWL6030_FIXED_RESOURCE(CLK32KG, 0x8C, 48, 0), - - /* 6025 are renamed compared to 6030 versions */ - TWL6025_ADJUSTABLE_LDO(LDO2, 0x54, 1000, 3300, 1), - TWL6025_ADJUSTABLE_LDO(LDO4, 0x58, 1000, 3300, 2), - TWL6025_ADJUSTABLE_LDO(LDO3, 0x5c, 1000, 3300, 3), - TWL6025_ADJUSTABLE_LDO(LDO5, 0x68, 1000, 3300, 4), - TWL6025_ADJUSTABLE_LDO(LDO1, 0x6c, 1000, 3300, 5), - TWL6025_ADJUSTABLE_LDO(LDO7, 0x74, 1000, 3300, 7), - TWL6025_ADJUSTABLE_LDO(LDO6, 0x60, 1000, 3300, 16), - TWL6025_ADJUSTABLE_LDO(LDOLN, 0x64, 1000, 3300, 17), - TWL6025_ADJUSTABLE_LDO(LDOUSB, 0x70, 1000, 3300, 18), - - TWL6025_ADJUSTABLE_SMPS(SMPS3, 0x34, 1), - TWL6025_ADJUSTABLE_SMPS(SMPS4, 0x10, 2), - TWL6025_ADJUSTABLE_SMPS(VIO, 0x16, 3), + TWL6030_ADJUSTABLE_LDO(VAUX1_6030, 0x54, 1000, 3300), + TWL6030_ADJUSTABLE_LDO(VAUX2_6030, 0x58, 1000, 3300), + TWL6030_ADJUSTABLE_LDO(VAUX3_6030, 0x5c, 1000, 3300), + TWL6030_ADJUSTABLE_LDO(VMMC, 0x68, 1000, 3300), + TWL6030_ADJUSTABLE_LDO(VPP, 0x6c, 1000, 3300), + TWL6030_ADJUSTABLE_LDO(VUSIM, 0x74, 1000, 3300), + TWL6030_FIXED_LDO(VANA, 0x50, 2100, 0), + TWL6030_FIXED_LDO(VCXIO, 0x60, 1800, 0), + TWL6030_FIXED_LDO(VDAC, 0x64, 1800, 0), + TWL6030_FIXED_LDO(VUSB, 0x70, 3300, 0), + TWL6030_FIXED_RESOURCE(CLK32KG, 0x8C, 0), + TWL6030_FIXED_RESOURCE(CLK32KAUDIO, 0x8F, 0), + TWL6030_ADJUSTABLE_SMPS(VDD1, 0x22, 600, 4000), + TWL6030_ADJUSTABLE_SMPS(VDD2, 0x28, 600, 4000), + TWL6030_ADJUSTABLE_SMPS(VDD3, 0x2e, 600, 4000), + TWL6030_ADJUSTABLE_SMPS(VMEM, 0x34, 600, 4000), + TWL6030_ADJUSTABLE_SMPS(V2V1, 0x1c, 1800, 2100), + + /* 6032 are renamed compared to 6030 versions */ + TWL6032_ADJUSTABLE_LDO(LDO2, 0x54, 1000, 3300), + TWL6032_ADJUSTABLE_LDO(LDO4, 0x58, 1000, 3300), + TWL6032_ADJUSTABLE_LDO(LDO3, 0x5c, 1000, 3300), + TWL6032_ADJUSTABLE_LDO(LDO5, 0x68, 1000, 3300), + TWL6032_ADJUSTABLE_LDO(LDO1, 0x6c, 1000, 3300), + TWL6032_ADJUSTABLE_LDO(LDO7, 0x74, 1000, 3300), + TWL6032_ADJUSTABLE_LDO(LDO6, 0x60, 1000, 3300), + TWL6032_ADJUSTABLE_LDO(LDOLN, 0x64, 1000, 3300), + TWL6032_ADJUSTABLE_LDO(LDOUSB, 0x70, 1000, 3300), + + TWL6032_ADJUSTABLE_SMPS(SMPS3, 0x34), + TWL6032_ADJUSTABLE_SMPS(SMPS4, 0x10), + TWL6032_ADJUSTABLE_SMPS(VIO, 0x16), + + TWL6032_ADJUSTABLE_DVSSMPS(SMPS1, 0x22), + TWL6032_ADJUSTABLE_DVSSMPS(SMPS2, 0x28), + TWL6032_ADJUSTABLE_DVSSMPS(SMPS5, 0x26), + + TWL6030_EXTERNAL_CONTROL_PIN(SYSEN, 0x83, 0), + TWL6030_EXTERNAL_CONTROL_PIN(REGEN1, 0x7d, 0), + }; static u8 twl_get_smps_offset(void) @@ -1014,6 +1285,7 @@ static int __devinit twlreg_probe(struct platform_device *pdev) struct regulator_init_data *initdata; struct regulation_constraints *c; struct regulator_dev *rdev; + int ret; for (i = 0, info = NULL; i < ARRAY_SIZE(twl_regs); i++) { if (twl_regs[i].desc.id != pdev->id) @@ -1049,24 +1321,37 @@ static int __devinit twlreg_probe(struct platform_device *pdev) case TWL4030_REG_VINTDIG: c->always_on = true; break; + case TWL6030_REG_VUSB: + /* Program CFG_LDO_PD2 register and set VUSB bit */ + ret = twl_i2c_write_u8(TWL6030_MODULE_ID0, 0x1, + TWL6030_CFG_LDO_PD2); + if (ret < 0) + return ret; + + /* Program MISC2 register and set bit VUSB_IN_VBAT */ + ret = twl_i2c_write_u8(TWL6030_MODULE_ID0, 0x10, TWL6030_MISC2); + if (ret < 0) + return ret; + break; default: break; } switch (pdev->id) { - case TWL6025_REG_SMPS3: + case TWL6032_REG_SMPS3: if (twl_get_smps_mult() & SMPS_MULTOFFSET_SMPS3) info->flags |= SMPS_EXTENDED_EN; if (twl_get_smps_offset() & SMPS_MULTOFFSET_SMPS3) info->flags |= SMPS_OFFSET_EN; break; - case TWL6025_REG_SMPS4: + case TWL6032_REG_SMPS4: if (twl_get_smps_mult() & SMPS_MULTOFFSET_SMPS4) info->flags |= SMPS_EXTENDED_EN; if (twl_get_smps_offset() & SMPS_MULTOFFSET_SMPS4) info->flags |= SMPS_OFFSET_EN; break; - case TWL6025_REG_VIO: + + case TWL6032_REG_VIO: if (twl_get_smps_mult() & SMPS_MULTOFFSET_VIO) info->flags |= SMPS_EXTENDED_EN; if (twl_get_smps_offset() & SMPS_MULTOFFSET_VIO) @@ -1119,7 +1404,7 @@ static int __init twlreg_init(void) { return platform_driver_register(&twlreg_driver); } -subsys_initcall(twlreg_init); +subsys_initcall_sync(twlreg_init); static void __exit twlreg_exit(void) { diff --git a/drivers/rtc/rtc-twl.c b/drivers/rtc/rtc-twl.c index f9a2799c44d6..7226a2db300d 100644 --- a/drivers/rtc/rtc-twl.c +++ b/drivers/rtc/rtc-twl.c @@ -362,14 +362,6 @@ static irqreturn_t twl_rtc_interrupt(int irq, void *rtc) int res; u8 rd_reg; -#ifdef CONFIG_LOCKDEP - /* WORKAROUND for lockdep forcing IRQF_DISABLED on us, which - * we don't want and can't tolerate. Although it might be - * friendlier not to borrow this thread context... - */ - local_irq_enable(); -#endif - res = twl_rtc_read_u8(&rd_reg, REG_RTC_STATUS_REG); if (res) goto out; @@ -428,24 +420,12 @@ static struct rtc_class_ops twl_rtc_ops = { static int __devinit twl_rtc_probe(struct platform_device *pdev) { struct rtc_device *rtc; - int ret = 0; + int ret = -EINVAL; int irq = platform_get_irq(pdev, 0); u8 rd_reg; if (irq <= 0) - return -EINVAL; - - rtc = rtc_device_register(pdev->name, - &pdev->dev, &twl_rtc_ops, THIS_MODULE); - if (IS_ERR(rtc)) { - ret = PTR_ERR(rtc); - dev_err(&pdev->dev, "can't register RTC device, err %ld\n", - PTR_ERR(rtc)); - goto out0; - - } - - platform_set_drvdata(pdev, rtc); + goto out1; ret = twl_rtc_read_u8(&rd_reg, REG_RTC_STATUS_REG); if (ret < 0) @@ -462,14 +442,6 @@ static int __devinit twl_rtc_probe(struct platform_device *pdev) if (ret < 0) goto out1; - ret = request_irq(irq, twl_rtc_interrupt, - IRQF_TRIGGER_RISING, - dev_name(&rtc->dev), rtc); - if (ret < 0) { - dev_err(&pdev->dev, "IRQ is not free.\n"); - goto out1; - } - if (twl_class_is_6030()) { twl6030_interrupt_unmask(TWL6030_RTC_INT_MASK, REG_INT_MSK_LINE_A); @@ -480,28 +452,48 @@ static int __devinit twl_rtc_probe(struct platform_device *pdev) /* Check RTC module status, Enable if it is off */ ret = twl_rtc_read_u8(&rd_reg, REG_RTC_CTRL_REG); if (ret < 0) - goto out2; + goto out1; if (!(rd_reg & BIT_RTC_CTRL_REG_STOP_RTC_M)) { dev_info(&pdev->dev, "Enabling TWL-RTC.\n"); rd_reg = BIT_RTC_CTRL_REG_STOP_RTC_M; ret = twl_rtc_write_u8(rd_reg, REG_RTC_CTRL_REG); if (ret < 0) - goto out2; + goto out1; } /* init cached IRQ enable bits */ ret = twl_rtc_read_u8(&rtc_irq_bits, REG_RTC_INTERRUPTS_REG); if (ret < 0) + goto out1; + + rtc = rtc_device_register(pdev->name, + &pdev->dev, &twl_rtc_ops, THIS_MODULE); + if (IS_ERR(rtc)) { + ret = PTR_ERR(rtc); + dev_err(&pdev->dev, "can't register RTC device, err %ld\n", + PTR_ERR(rtc)); + goto out1; + + } + + ret = request_threaded_irq(irq, NULL, twl_rtc_interrupt, + IRQF_TRIGGER_RISING, + dev_name(&rtc->dev), rtc); + if (ret < 0) { + dev_err(&pdev->dev, "IRQ is not free.\n"); goto out2; + } - return ret; + if (enable_irq_wake(irq) < 0) + dev_warn(&pdev->dev, "Cannot enable wakeup for IRQ %d\n", irq); + + platform_set_drvdata(pdev, rtc); + return 0; out2: - free_irq(irq, rtc); -out1: rtc_device_unregister(rtc); -out0: +out1: return ret; } diff --git a/include/linux/i2c/twl.h b/include/linux/i2c/twl.h old mode 100644 new mode 100755 index ba4f88624fcd..accbd9a4c478 --- a/include/linux/i2c/twl.h +++ b/include/linux/i2c/twl.h @@ -71,6 +71,8 @@ #define TWL4030_MODULE_PM_RECEIVER 0x15 #define TWL4030_MODULE_RTC 0x16 #define TWL4030_MODULE_SECURED_REG 0x17 +#define TWL6032_MODULE_CHARGER 0x18 +#define TWL6030_MODULE_SLAVE_RES 0x19 #define TWL_MODULE_USB TWL4030_MODULE_USB #define TWL_MODULE_AUDIO_VOICE TWL4030_MODULE_AUDIO_VOICE @@ -81,7 +83,11 @@ #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 @@ -100,6 +106,7 @@ * 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 @@ -108,6 +115,7 @@ #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 */ @@ -147,10 +155,17 @@ #define SW_FC (0x1 << 2) #define STS_MMC 0x1 +#define TWL6030_MMCDEBOUNCING 0xED +#define MMC_DEB_BYPASS (0x1 << 7) +#define MMC_MINS_DEB_MASK (0xF << 3) +#define MMC_MEXT_DEB_MASK (0x7 << 0) + #define TWL6030_CFG_INPUT_PUPD3 0xF2 #define MMC_PU (0x1 << 3) #define MMC_PD (0x1 << 2) +#define VLOW_INT_MASK (0x1 << 2) + #define TWL_SIL_TYPE(rev) ((rev) & 0x00FFFFFF) #define TWL_SIL_REV(rev) ((rev) >> 24) #define TWL_SIL_5030 0x09002F @@ -171,7 +186,7 @@ static inline int twl_class_is_ ##class(void) \ TWL_CLASS_IS(4030, TWL4030_CLASS_ID) TWL_CLASS_IS(6030, TWL6030_CLASS_ID) -#define TWL6025_SUBCLASS BIT(4) /* TWL6025 has changed registers */ +#define TWL6032_SUBCLASS BIT(4) /* Phoenix Lite is a varient*/ /* * Read and write single 8-bit registers @@ -230,6 +245,11 @@ static inline int twl6030_mmc_card_detect(struct device *dev, int slot) #define TWL4030_SIH_CTRL_PENDDIS_MASK BIT(1) #define TWL4030_SIH_CTRL_COR_MASK BIT(2) +int twl6030_register_notifier(struct notifier_block *nb, + unsigned int events); +int twl6030_unregister_notifier(struct notifier_block *nb, + unsigned int events); + /*----------------------------------------------------------------------*/ /* @@ -450,6 +470,23 @@ static inline int twl6030_mmc_card_detect(struct device *dev, int slot) #define TWL4030_PM_MASTER_GLOBAL_TST 0xb6 +#define TWL6030_PHOENIX_DEV_ON 0x06 + +/* + * TWL6030 PM Master module register offsets (use TWL_MODULE_PM_MASTER) + */ + +#define TWL6030_PM_MASTER_MSK_TRANSITION 0x01 +#define TWL6030_VBATMIN_HI_THRESHOLD 0x05 + +/* + * PM Slave resource module register offsets (use TWL6030_MODULE_SLAVE_RES) + */ + +#define REG_VBATMIN_HI_CFG_STATE 0x1D + +#define VBATMIN_VLOW_EN 0x21 + /*----------------------------------------------------------------------*/ /* Power bus message definitions */ @@ -522,6 +559,43 @@ static inline int twl6030_mmc_card_detect(struct device *dev, int slot) #define RES_MAIN_REF 28 #define TOTAL_RESOURCES 28 +/* 6030 extra resources */ +#define RES_V1V29 29 +#define RES_V1V8 30 +#define RES_V2V1 31 +#define RES_VDD3 32 +#define RES_VMEM 33 +#define RES_VANA 34 +#define RES_VUAX1 35 +#define RES_VCXIO 36 +#define RES_VPP 37 +#define RES_VRTC 38 +#define RES_REGEN2 39 +#define RES_32KCLKAO 40 +#define RES_32KCLKG 41 +#define RES_32KCLKAUDIO 42 +#define RES_BIAS 43 +#define RES_VBATMIN_HI 44 +#define RES_RC6MHZ 45 +#define RES_TEMP 46 + +/* 6032 extra resources */ +#define RES_LDOUSB 47 +#define RES_SMPS5 48 +#define RES_SMPS4 49 +#define RES_SMPS3 50 +#define RES_SMPS2 51 +#define RES_SMPS1 52 +#define RES_LDOLN 53 +#define RES_LDO7 54 +#define RES_LDO6 55 +#define RES_LDO5 56 +#define RES_LDO4 57 +#define RES_LDO3 58 +#define RES_LDO2 59 +#define RES_LDO1 60 +#define RES_VSYSMIN_HI 61 + /* * Power Bus Message Format ... these can be sent individually by Linux, * but are usually part of downloaded scripts that are run when various @@ -557,6 +631,20 @@ struct twl4030_clock_init_data { struct twl4030_bci_platform_data { int *battery_tmp_tbl; unsigned int tblsize; + + unsigned int monitoring_interval; + + unsigned int max_charger_currentmA; + unsigned int max_charger_voltagemV; + unsigned int termination_currentmA; + + unsigned int max_bat_voltagemV; + unsigned int low_bat_voltagemV; + + unsigned int sense_resistor_mohm; + + /* twl6032 */ + unsigned long features; }; /* TWL4030_GPIO_MAX (18) GPIOs, with interrupts */ @@ -589,6 +677,7 @@ struct twl4030_gpio_platform_data { struct twl4030_madc_platform_data { int irq_line; + int features; }; /* Boards have unique mappings of {row, col} --> keycode. @@ -641,21 +730,41 @@ struct twl4030_script { struct twl4030_resconfig { u8 resource; u8 devgroup; /* Processor group that Power resource belongs to */ + /* The following are used by TWL4030 only */ u8 type; /* Power resource addressed, 6 / broadcast message */ u8 type2; /* Power resource addressed, 3 / broadcast message */ u8 remap_off; /* off state remapping */ u8 remap_sleep; /* sleep state remapping */ }; +struct twl4030_system_config { + char *name; + u8 group; +}; + struct twl4030_power_data { - struct twl4030_script **scripts; - unsigned num; + struct twl4030_script **scripts; /* used in TWL4030 only */ + unsigned num; /* used in TWL4030 only */ struct twl4030_resconfig *resource_config; + struct twl4030_system_config *sys_config; /*system resources*/ #define TWL4030_RESCONFIG_UNDEF ((u8)-1) }; +#ifdef CONFIG_TWL4030_POWER extern void twl4030_power_init(struct twl4030_power_data *triton2_scripts); extern int twl4030_remove_script(u8 flags); +#else +static inline void twl4030_power_init(struct twl4030_power_data *triton2_scripts) { } +static inline int twl4030_remove_script(u8 flags) { return -EINVAL; } +#endif + +#ifdef CONFIG_TWL6030_POWER +extern void twl6030_power_init(struct twl4030_power_data *power_data,\ + unsigned long features); +#else +extern inline void twl6030_power_init(struct twl4030_power_data *power_data,\ + unsigned long features) { } +#endif struct twl4030_codec_audio_data { unsigned int digimic_delay; /* in ms */ @@ -664,11 +773,26 @@ struct twl4030_codec_audio_data { unsigned int check_defaults:1; unsigned int reset_registers:1; unsigned int hs_extmute:1; + u16 hs_left_step; + u16 hs_right_step; + u16 hf_left_step; + u16 hf_right_step; + u16 ep_step; void (*set_hs_extmute)(int mute); + + /* twl6040 */ + int vddhf_uV; }; struct twl4030_codec_vibra_data { unsigned int coexist; + + /* timed-output based implementations */ + int max_timeout; + int initial_vibrate; + int (*init)(void); + void (*exit)(void); + u8 voltage_raise_speed; }; struct twl4030_codec_data { @@ -676,13 +800,22 @@ struct twl4030_codec_data { struct twl4030_codec_audio_data *audio; struct twl4030_codec_vibra_data *vibra; + int (*init)(void); + void (*exit)(void); + /* twl6040 */ int audpwron_gpio; /* audio power-on gpio */ int naudint_irq; /* audio interrupt */ + unsigned int irq_base; + int (*get_ext_clk32k)(void); + void (*put_ext_clk32k)(void); + int (*set_ext_clk32k)(bool on); }; struct twl4030_platform_data { unsigned irq_base, irq_end; + int(*pre_init)(void); + int(*set_init)(void); struct twl4030_clock_init_data *clock; struct twl4030_bci_platform_data *bci; struct twl4030_gpio_platform_data *gpio; @@ -710,6 +843,10 @@ struct twl4030_platform_data { struct regulator_init_data *vintana1; struct regulator_init_data *vintana2; struct regulator_init_data *vintdig; + /* TWL6030 DCDC regulators */ + struct regulator_init_data *vdd3; + struct regulator_init_data *vmem; + struct regulator_init_data *v2v1; /* TWL6030 LDO regulators */ struct regulator_init_data *vmmc; struct regulator_init_data *vpp; @@ -718,7 +855,8 @@ struct twl4030_platform_data { struct regulator_init_data *vcxio; struct regulator_init_data *vusb; struct regulator_init_data *clk32kg; - /* TWL6025 LDO regulators */ + struct regulator_init_data *clk32kaudio; + /* TWL6032 LDO regulators */ struct regulator_init_data *ldo1; struct regulator_init_data *ldo2; struct regulator_init_data *ldo3; @@ -728,10 +866,21 @@ struct twl4030_platform_data { struct regulator_init_data *ldo7; struct regulator_init_data *ldoln; struct regulator_init_data *ldousb; - /* TWL6025 DCDC regulators */ + /* TWL6032 DCDC regulators */ struct regulator_init_data *smps3; struct regulator_init_data *smps4; - struct regulator_init_data *vio6025; + struct regulator_init_data *vio6032; + + struct regulator_init_data *smps1; //add + struct regulator_init_data *smps2; //add + struct regulator_init_data *smps5; //add + + /* External control pins */ + struct regulator_init_data *sysen; + struct regulator_init_data *regen1; + + + }; /*----------------------------------------------------------------------*/ @@ -813,21 +962,33 @@ static inline int twl4030charger_usb_en(int enable) { return 0; } #define TWL6030_REG_VRTC 47 #define TWL6030_REG_CLK32KG 48 -/* LDOs on 6025 have different names */ -#define TWL6025_REG_LDO2 49 -#define TWL6025_REG_LDO4 50 -#define TWL6025_REG_LDO3 51 -#define TWL6025_REG_LDO5 52 -#define TWL6025_REG_LDO1 53 -#define TWL6025_REG_LDO7 54 -#define TWL6025_REG_LDO6 55 -#define TWL6025_REG_LDOLN 56 -#define TWL6025_REG_LDOUSB 57 - -/* 6025 DCDC supplies */ -#define TWL6025_REG_SMPS3 58 -#define TWL6025_REG_SMPS4 59 -#define TWL6025_REG_VIO 60 +/* LDOs on 6032 have different names */ +#define TWL6032_REG_LDO2 49 +#define TWL6032_REG_LDO4 50 +#define TWL6032_REG_LDO3 51 +#define TWL6032_REG_LDO5 52 +#define TWL6032_REG_LDO1 53 +#define TWL6032_REG_LDO7 54 +#define TWL6032_REG_LDO6 55 +#define TWL6032_REG_LDOLN 56 +#define TWL6032_REG_LDOUSB 57 + +/* 6032 DCDC supplies */ +#define TWL6032_REG_SMPS3 58 +#define TWL6032_REG_SMPS4 59 +#define TWL6032_REG_VIO 60 + +#define TWL6030_REG_CLK32KAUDIO 61 + +/* External control pins */ +#define TWL6030_REG_SYSEN 62 +#define TWL6030_REG_REGEN1 63 + +#define TWL6032_REG_SMPS1 64 //add +#define TWL6032_REG_SMPS2 65 //add +#define TWL6032_REG_SMPS5 66 //add + +#define TWL6032_PREQ1_RES_ASS_A 0xd7 #endif /* End of __TWL4030_H */