rk30☎️add pmu tps80032

This commit is contained in:
张晴 2012-05-02 16:39:17 +08:00
parent 5022feddeb
commit 50276f7c94
12 changed files with 3828 additions and 202 deletions

View File

@ -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

View File

@ -0,0 +1,496 @@
#include <linux/regulator/machine.h>
#include <linux/i2c/twl.h>
#include <mach/sram.h>
#ifdef CONFIG_TWL4030_CORE
#define VREG_VOLTAGE 3
#define VREG_VOLTAGE_DVS_SMPS 3
static inline int twl_reg_read(unsigned base, unsigned slave_subgp, unsigned offset)
{
u8 value;
int status;
status = twl_i2c_read_u8(slave_subgp,&value, base + offset);
return (status < 0) ? status : value;
}
static inline int twl_reg_write(unsigned base, unsigned slave_subgp, unsigned offset,
u8 value)
{
return twl_i2c_write_u8(slave_subgp,value, base + offset);
}
int tps80032_pre_init(void){
int ret;
u8 value;
printk("--------------------\n");
printk("%s\n", __func__);
return 0;
}
int tps80032_set_init(void)
{
struct regulator *dcdc;
struct regulator *ldo;
printk("++++++++++++++++++++++++++++++++++++++\n");
printk("%s\n", __func__);
ldo = regulator_get(NULL, "tps_ldo1"); //vcca_33
regulator_set_voltage(ldo, 3300000, 3300000);
regulator_enable(ldo);
printk("%s set ldo1 vcca_33=%dmV end\n", __func__, regulator_get_voltage(ldo));
regulator_put(ldo);
udelay(100);
ldo = regulator_get(NULL, "tps_ldo4"); // vdd_11
regulator_set_voltage(ldo, 1100000, 1100000);
regulator_enable(ldo);
printk("%s set ldo4 vdd_11=%dmV end\n", __func__, regulator_get_voltage(ldo));
regulator_put(ldo);
udelay(100);
dcdc = regulator_get(NULL, "tps_smps4");
regulator_set_voltage(dcdc,3000000,3000000);
regulator_enable(dcdc);
printk("%s set dcdc4 vcc_io=%dmV end\n", __func__, regulator_get_voltage(dcdc));
regulator_put(dcdc);
udelay(100);
ldo = regulator_get(NULL, "tps_ldo2"); // vdd_usb11
regulator_set_voltage(ldo, 1100000, 1100000);
regulator_enable(ldo);
printk("%s set ldo2 vdd_usb11=%dmV end\n", __func__, regulator_get_voltage(ldo));
regulator_put(ldo);
udelay(100);
ldo = regulator_get(NULL, "tps_ldo5"); // vcc_25
regulator_set_voltage(ldo, 2500000, 2500000);
regulator_enable(ldo);
printk("%s set ldo5 vcc_25=%dmV end\n", __func__, regulator_get_voltage(ldo));
regulator_put(ldo);
udelay(100);
ldo = regulator_get(NULL, "tps_ldousb"); // vcc_usb33
regulator_set_voltage(ldo, 3300000, 3300000);
regulator_enable(ldo);
printk("%s set ldousb vcc_usb33=%dmV end\n", __func__, regulator_get_voltage(ldo));
regulator_put(ldo);
udelay(100);
dcdc = regulator_get(NULL, "tps_smps1"); // vdd_arm
regulator_set_voltage(dcdc,1100000,1100000);
regulator_enable(dcdc);
printk("%s set dcdc1 vdd_cpu=%dmV end\n", __func__, regulator_get_voltage(dcdc));
regulator_put(dcdc);
udelay(100);
dcdc = regulator_get(NULL, "tps_smps2"); //vdd_log
regulator_set_voltage(dcdc,1100000,1100000);
regulator_enable(dcdc);
printk("%s set dcdc2 vdd_core=%dmV end\n", __func__, regulator_get_voltage(dcdc));
regulator_put(dcdc);
udelay(100);
dcdc = regulator_get(NULL, "tps_smps3"); //vcc_ddr
regulator_set_voltage(dcdc,1500000,1500000);
regulator_enable(dcdc);
printk("%s set dcdc3 vcc_ddr=%dmV end\n", __func__, regulator_get_voltage(dcdc));
regulator_put(dcdc);
udelay(100);
/*
dcdc = regulator_get(NULL, "tps_smps5");
regulator_set_voltage(dcdc,1800000,1800000);
// regulator_set_suspend_voltage(dcdc, 1800000);
regulator_enable(dcdc);
printk("%s set dcdc5 vcc_lpddr2_1v8=%dmV end\n", __func__, regulator_get_voltage(dcdc));
regulator_put(dcdc);
udelay(100);
*/
ldo = regulator_get(NULL, "tps_ldo3"); //vcc_nandflash
regulator_set_voltage(ldo, 3300000, 3300000);
regulator_enable(ldo);
printk("%s set ldo3 vcc_nandflash=%dmV end\n", __func__, regulator_get_voltage(ldo));
regulator_put(ldo);
udelay(100);
ldo = regulator_get(NULL, "tps_ldo6"); //codecvdd_1v8
regulator_set_voltage(ldo, 1800000, 1800000);
regulator_enable(ldo);
printk("%s set ldo6 codecvdd_1v8=%dmV end\n", __func__, regulator_get_voltage(ldo));
regulator_put(ldo);
udelay(100);
ldo = regulator_get(NULL, "tps_ldo7"); //vcc_lcd
regulator_set_voltage(ldo, 3000000, 3000000);
regulator_enable(ldo);
printk("%s set ldo7 vcc_lcd=%dmV end\n", __func__, regulator_get_voltage(ldo));
regulator_put(ldo);
udelay(100);
ldo = regulator_get(NULL, "tps_ldoln"); //vcccodec_io
regulator_set_voltage(ldo, 3300000, 3300000);
regulator_enable(ldo);
printk("%s set ldoln vcccodec_io=%dmV end\n", __func__, regulator_get_voltage(ldo));
regulator_put(ldo);
udelay(100);
/*
ldo = regulator_get(NULL, "tps_vana"); //vana_out
regulator_set_voltage(ldo, 2500000, 2500000);
// regulator_set_suspend_voltage(ldo, 2500000);
regulator_enable(ldo);
printk("%s set vana vana_out=%dmV end\n", __func__, regulator_get_voltage(ldo));
regulator_put(ldo);
udelay(100);
*/
return 0;
}
static struct regulator_consumer_supply tps80032_smps1_supply[] = {
{
.supply = "tps_smps1",
},
};
static struct regulator_consumer_supply tps80032_smps2_supply[] = {
{
.supply = "tps_smps2",
},
};
static struct regulator_consumer_supply tps80032_smps3_supply[] = {
{
.supply = "tps_smps3",
},
};
static struct regulator_consumer_supply tps80032_smps4_supply[] = {
{
.supply = "tps_smps4",
},
};
static struct regulator_consumer_supply tps80032_smps5_supply[] = {
{
.supply = "tps_smps5",
},
};
static struct regulator_consumer_supply tps80032_ldo1_supply[] = {
{
.supply = "tps_ldo1",
},
};
static struct regulator_consumer_supply tps80032_ldo2_supply[] = {
{
.supply = "tps_ldo2",
},
};
static struct regulator_consumer_supply tps80032_ldo3_supply[] = {
{
.supply = "tps_ldo3",
},
};
static struct regulator_consumer_supply tps80032_ldo4_supply[] = {
{
.supply = "tps_ldo4",
},
};
static struct regulator_consumer_supply tps80032_ldo5_supply[] = {
{
.supply = "tps_ldo5",
},
};
static struct regulator_consumer_supply tps80032_ldo6_supply[] = {
{
.supply = "tps_ldo6",
},
};
static struct regulator_consumer_supply tps80032_ldo7_supply[] = {
{
.supply = "tps_ldo7",
},
};
static struct regulator_consumer_supply tps80032_ldoln_supply[] = {
{
.supply = "tps_ldoln",
},
};
static struct regulator_consumer_supply tps80032_ldousb_supply[] = {
{
.supply = "tps_ldousb",
},
};
static struct regulator_consumer_supply tps80032_ldovana_supply[] = {
{
.supply = "tps_vana",
},
};
/* */
static struct regulator_init_data tps80032_smps1 = {
.constraints = {
.name = "TPS_SMPS1",
.min_uV = 600000,
.max_uV = 2100000,
.apply_uV = 1,
.valid_ops_mask = REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_MODE,
.valid_modes_mask = REGULATOR_MODE_STANDBY | REGULATOR_MODE_NORMAL,
},
.num_consumer_supplies = ARRAY_SIZE(tps80032_smps1_supply),
.consumer_supplies = tps80032_smps1_supply,
};
/* */
static struct regulator_init_data tps80032_smps2 = {
.constraints = {
.name = "TPS_SMPS2",
.min_uV = 600000,
.max_uV = 2100000,
.apply_uV = 1,
.valid_ops_mask = REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_MODE,
.valid_modes_mask = REGULATOR_MODE_STANDBY | REGULATOR_MODE_NORMAL,
},
.num_consumer_supplies = ARRAY_SIZE(tps80032_smps2_supply),
.consumer_supplies = tps80032_smps2_supply,
};
/* */
static struct regulator_init_data tps80032_smps3 = {
.constraints = {
.name = "TPS_SMPS3",
.min_uV = 600000,
.max_uV = 2100000,
.apply_uV = 1,
.valid_ops_mask = REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_MODE,
.valid_modes_mask = REGULATOR_MODE_STANDBY | REGULATOR_MODE_NORMAL,
},
.num_consumer_supplies = ARRAY_SIZE(tps80032_smps3_supply),
.consumer_supplies = tps80032_smps3_supply,
};
/* */
static struct regulator_init_data tps80032_smps4 = {
.constraints = {
.name = "TPS_SMPS4",
.min_uV = 600000,
.max_uV = 2100000,
.apply_uV = 1,
.valid_ops_mask = REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_MODE,
.valid_modes_mask = REGULATOR_MODE_STANDBY | REGULATOR_MODE_NORMAL,
},
.num_consumer_supplies = ARRAY_SIZE(tps80032_smps4_supply),
.consumer_supplies = tps80032_smps4_supply,
};
/* */
static struct regulator_init_data tps80032_smps5 = {
.constraints = {
.name = "TPS_SMPS5",
.min_uV = 600000,
.max_uV = 2100000,
.apply_uV = 1,
.valid_ops_mask = REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_MODE,
.valid_modes_mask = REGULATOR_MODE_STANDBY | REGULATOR_MODE_NORMAL,
},
.num_consumer_supplies = ARRAY_SIZE(tps80032_smps5_supply),
.consumer_supplies = tps80032_smps5_supply,
};
static struct regulator_init_data tps80032_ldo1 = {
.constraints = {
.name = "TPS_LDO1",
.min_uV = 1200000,
.max_uV = 3000000,
.apply_uV = 1,
.valid_ops_mask = REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_MODE,
.valid_modes_mask = REGULATOR_MODE_STANDBY | REGULATOR_MODE_NORMAL,
},
.num_consumer_supplies = ARRAY_SIZE(tps80032_ldo1_supply),
.consumer_supplies = tps80032_ldo1_supply,
};
/* */
static struct regulator_init_data tps80032_ldo2 = {
.constraints = {
.name = "TPS_LDO2",
.min_uV = 1200000,
.max_uV = 3000000,
.apply_uV = 1,
.valid_ops_mask = REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_MODE,
.valid_modes_mask = REGULATOR_MODE_STANDBY | REGULATOR_MODE_NORMAL,
},
.num_consumer_supplies = ARRAY_SIZE(tps80032_ldo2_supply),
.consumer_supplies = tps80032_ldo2_supply,
};
/* */
static struct regulator_init_data tps80032_ldo3 = {
.constraints = {
.name = "TPS_LDO3",
.min_uV = 1200000,
.max_uV = 3000000,
.apply_uV = 1,
.valid_ops_mask = REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_MODE,
.valid_modes_mask = REGULATOR_MODE_STANDBY | REGULATOR_MODE_NORMAL,
},
.num_consumer_supplies = ARRAY_SIZE(tps80032_ldo3_supply),
.consumer_supplies = tps80032_ldo3_supply,
};
/* */
static struct regulator_init_data tps80032_ldo4 = {
.constraints = {
.name = "TPS_LDO4",
.min_uV = 1200000,
.max_uV = 3000000,
.apply_uV = 1,
.valid_ops_mask = REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_MODE,
.valid_modes_mask = REGULATOR_MODE_STANDBY | REGULATOR_MODE_NORMAL,
},
.num_consumer_supplies = ARRAY_SIZE(tps80032_ldo4_supply),
.consumer_supplies = tps80032_ldo4_supply,
};
/* */
static struct regulator_init_data tps80032_ldo5 = {
.constraints = {
.name = "TPS_LDO5",
.min_uV = 1200000,
.max_uV = 3000000,
.apply_uV = 1,
.valid_ops_mask = REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_MODE,
.valid_modes_mask = REGULATOR_MODE_STANDBY | REGULATOR_MODE_NORMAL,
},
.num_consumer_supplies = ARRAY_SIZE(tps80032_ldo5_supply),
.consumer_supplies = tps80032_ldo5_supply,
};
/* */
static struct regulator_init_data tps80032_ldo6 = {
.constraints = {
.name = "TPS_LDO6",
.min_uV = 1200000,
.max_uV = 3000000,
.apply_uV = 1,
.valid_ops_mask = REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_MODE,
.valid_modes_mask = REGULATOR_MODE_STANDBY | REGULATOR_MODE_NORMAL,
},
.num_consumer_supplies = ARRAY_SIZE(tps80032_ldo6_supply),
.consumer_supplies = tps80032_ldo6_supply,
};
/* */
static struct regulator_init_data tps80032_ldo7 = {
.constraints = {
.name = "TPS_LDO7",
.min_uV = 1200000,
.max_uV = 3000000,
.apply_uV = 1,
.valid_ops_mask = REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_MODE,
.valid_modes_mask = REGULATOR_MODE_STANDBY | REGULATOR_MODE_NORMAL,
},
.num_consumer_supplies = ARRAY_SIZE(tps80032_ldo7_supply),
.consumer_supplies = tps80032_ldo7_supply,
};
/* */
static struct regulator_init_data tps80032_ldoln = {
.constraints = {
.name = "TPS_LDOLN",
.min_uV = 1200000,
.max_uV = 3000000,
.apply_uV = 1,
.valid_ops_mask = REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_MODE,
.valid_modes_mask = REGULATOR_MODE_STANDBY | REGULATOR_MODE_NORMAL,
},
.num_consumer_supplies = ARRAY_SIZE(tps80032_ldoln_supply),
.consumer_supplies = tps80032_ldoln_supply,
};
/* */
static struct regulator_init_data tps80032_ldousb = {
.constraints = {
.name = "TPS_LDOUSB",
.min_uV = 3300000,
.max_uV = 3300000,
.apply_uV = 1,
.valid_ops_mask = REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_MODE,
.valid_modes_mask = REGULATOR_MODE_STANDBY | REGULATOR_MODE_NORMAL,
},
.num_consumer_supplies = ARRAY_SIZE(tps80032_ldousb_supply),
.consumer_supplies = tps80032_ldousb_supply,
};
/* */
static struct regulator_init_data tps80032_ldovana = {
.constraints = {
.name = "TPS_LDOVANA",
.min_uV = 600000,
.max_uV = 2500000,
.apply_uV = 1,
.valid_ops_mask = REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_MODE,
.valid_modes_mask = REGULATOR_MODE_STANDBY | REGULATOR_MODE_NORMAL,
},
.num_consumer_supplies = ARRAY_SIZE(tps80032_ldovana_supply),
.consumer_supplies = tps80032_ldovana_supply,
};
static struct twl4030_platform_data tps80032_data = {
.irq_base = RK30_PIN0_PA1,
.irq_end = RK30_PIN0_PA1,
//.irq = RK29_PIN0_PA1,
.pre_init = tps80032_pre_init,
.set_init = tps80032_set_init,
/* Regulators */
.ldo1 = &tps80032_ldo1,
.ldo2 = &tps80032_ldo2,
.ldo3 = &tps80032_ldo3,
.ldo4 = &tps80032_ldo4,
.ldo5 = &tps80032_ldo5,
.ldo6 = &tps80032_ldo6,
.ldo7 = &tps80032_ldo7,
.ldoln = &tps80032_ldoln,
.ldousb =&tps80032_ldousb,
.vana = &tps80032_ldovana,
.smps1 = &tps80032_smps1,
.smps2= &tps80032_smps2,
.smps3 = &tps80032_smps3,
.smps4 = &tps80032_smps4,
.smps5 = &tps80032_smps5,
};
#endif

360
drivers/mfd/twl-core.c Normal file → Executable file
View File

@ -33,11 +33,13 @@
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/interrupt.h>
#include <linux/regulator/machine.h>
#include <linux/i2c.h>
#include <linux/i2c/twl.h>
#include "twl-core.h"
#if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3)
#include <plat/cpu.h>
@ -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;
}
if (unlikely(!inuse)) {
pr_err("%s: not initialized\n", DRIVER_NAME);
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);
return -EPERM;
}
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;
}
if (unlikely(!inuse)) {
pr_err("%s: not initialized\n", DRIVER_NAME);
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);
return -EPERM;
}
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(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_LDO1, pdata->ldo1,
child = add_regulator(TWL6032_REG_LDO6, pdata->ldo6,
features);
if (IS_ERR(child))
return PTR_ERR(child);
child = add_regulator(TWL6025_REG_LDO7, pdata->ldo7,
child = add_regulator(TWL6032_REG_LDOLN, pdata->ldoln,
features);
if (IS_ERR(child))
return PTR_ERR(child);
child = add_regulator(TWL6025_REG_LDO6, pdata->ldo6,
child = add_regulator(TWL6032_REG_LDO2, pdata->ldo2,
features);
if (IS_ERR(child))
return PTR_ERR(child);
child = add_regulator(TWL6025_REG_LDOLN, pdata->ldoln,
child = add_regulator(TWL6032_REG_LDO4, pdata->ldo4,
features);
if (IS_ERR(child))
return PTR_ERR(child);
child = add_regulator(TWL6025_REG_LDO2, pdata->ldo2,
child = add_regulator(TWL6032_REG_LDO3, pdata->ldo3,
features);
if (IS_ERR(child))
return PTR_ERR(child);
child = add_regulator(TWL6025_REG_LDO4, pdata->ldo4,
child = add_regulator(TWL6032_REG_SMPS3, pdata->smps3,
features);
if (IS_ERR(child))
return PTR_ERR(child);
child = add_regulator(TWL6025_REG_LDO3, pdata->ldo3,
child = add_regulator(TWL6032_REG_SMPS4, pdata->smps4,
features);
if (IS_ERR(child))
return PTR_ERR(child);
child = add_regulator(TWL6025_REG_SMPS3, pdata->smps3,
child = add_regulator(TWL6032_REG_SMPS1, pdata->smps1,
features);
if (IS_ERR(child))
return PTR_ERR(child);
child = add_regulator(TWL6025_REG_SMPS4, pdata->smps4,
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(TWL6025_REG_VIO, pdata->vio6025,
child = add_regulator(TWL6032_REG_VIO, pdata->vio6032,
features);
if (IS_ERR(child))
return PTR_ERR(child);
}
if (twl_has_bci() && pdata->bci &&
!(features & (TPS_SUBSET | TWL5031))) {
!(features & (TPS_SUBSET | TWL5031)) && (features & TWL6030_CLASS)) {
child = add_child(3, "twl4030_bci",
pdata->bci, sizeof(*pdata->bci), false,
/* irq0 = CHG_PRES, irq1 = BCI */
@ -1146,13 +1315,22 @@ static void clocks_init(struct device *dev,
/*----------------------------------------------------------------------*/
int twl4030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end);
int twl4030_exit_irq(void);
int twl4030_init_chip_irq(const char *chip);
int twl6030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end);
int twl6030_exit_irq(void);
#ifdef CONFIG_PM
static int twl_suspend(struct i2c_client *client, pm_message_t mesg)
{
return irq_set_irq_wake(client->irq, 1);
}
static int twl_remove(struct i2c_client *client)
static int twl_resume(struct i2c_client *client)
{
return irq_set_irq_wake(client->irq, 0);
}
#else
#define twl_suspend NULL
#define twl_resume NULL
#endif
static int __devexit twl_remove(struct i2c_client *client)
{
unsigned i;
int status;
@ -1184,7 +1362,7 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id)
unsigned i;
struct twl4030_platform_data *pdata = client->dev.platform_data;
u8 temp;
int ret = 0;
int ret = 0, features;
if (!pdata) {
dev_dbg(&client->dev, "no platform data?\n");
@ -1203,8 +1381,16 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id)
for (i = 0; i < TWL_NUM_SLAVES; i++) {
struct twl_client *twl = &twl_modules[i];
#if 0
twl->address = client->addr + i;
#else
if( i <TWL_NUM_SLAVES-1 )
twl->address = client->addr + i;
else
twl->address = 0x12; //DVS i2s address
#endif
if (i == 0)
twl->client = client;
else {
@ -1237,9 +1423,20 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id)
WARN(ret < 0, "Error: reading twl_idcode register value\n");
}
features = id->driver_data;
if (twl_class_is_6030()) {
twl_i2c_read_u8(TWL_MODULE_USB, &temp, USB_PRODUCT_ID_LSB);
if (temp == 0x32)
features |= TWL6032_SUBCLASS;
}
/* load power event scripts */
if (twl_has_power() && pdata->power)
twl4030_power_init(pdata->power);
if (twl_has_power()) {
if (twl_class_is_4030() && pdata->power)
twl4030_power_init(pdata->power);
if (twl_class_is_6030())
twl6030_power_init(pdata->power, features);
}
/* Maybe init the T2 Interrupt subsystem */
if (client->irq
@ -1251,7 +1448,7 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id)
pdata->irq_end);
} else {
status = twl6030_init_irq(client->irq, pdata->irq_base,
pdata->irq_end);
pdata->irq_end, features);
}
if (status < 0)
@ -1269,8 +1466,23 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id)
I2C_SDA_CTRL_PU | I2C_SCL_CTRL_PU);
twl_i2c_write_u8(TWL4030_MODULE_INTBR, temp, REG_GPPUPDCTR1);
}
status = add_children(pdata, features);
status = add_children(pdata, id->driver_data);
if (pdata && pdata->pre_init) {
ret = pdata->pre_init();
if (ret != 0) {
printk(" tps80032 pre_init() failed: %d\n");
}
}
if (pdata && pdata->set_init) {
ret = pdata->set_init();
if (ret != 0) {
printk(" tps80032 set_init() failed: %d\n");
}
}
fail:
if (status < 0)
twl_remove(client);
@ -1285,7 +1497,7 @@ static const struct i2c_device_id twl_ids[] = {
{ "tps65930", TPS_SUBSET }, /* fewer LDOs and DACs; no charger */
{ "tps65920", TPS_SUBSET }, /* fewer LDOs; no codec or charger */
{ "twl6030", TWL6030_CLASS }, /* "Phoenix power chip" */
{ "twl6025", TWL6030_CLASS | TWL6025_SUBCLASS }, /* "Phoenix lite" */
{ "twl6032", TWL6030_CLASS | TWL6032_SUBCLASS }, /* Phoenix lite */
{ /* end of list */ },
};
MODULE_DEVICE_TABLE(i2c, twl_ids);
@ -1295,14 +1507,16 @@ static struct i2c_driver twl_driver = {
.driver.name = DRIVER_NAME,
.id_table = twl_ids,
.probe = twl_probe,
.remove = twl_remove,
.remove = __devexit_p(twl_remove),
.suspend = twl_suspend,
.resume = twl_resume,
};
static int __init twl_init(void)
{
return i2c_add_driver(&twl_driver);
}
subsys_initcall(twl_init);
subsys_initcall_sync(twl_init);
static void __exit twl_exit(void)
{

View File

@ -1,7 +1,8 @@
#ifndef __TWL_CORE_H__
#define __TWL_CORE_H__
extern int twl6030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end);
extern int twl6030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end,
unsigned long features);
extern int twl6030_exit_irq(void);
extern int twl4030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end);
extern int twl4030_exit_irq(void);

1446
drivers/mfd/twl6030-gpadc.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -37,6 +37,8 @@
#include <linux/kthread.h>
#include <linux/i2c/twl.h>
#include <linux/platform_device.h>
#include <linux/suspend.h>
#include <linux/reboot.h>
#include "twl-core.h"
@ -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 @@ int twl6030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end)
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;
}

354
drivers/mfd/twl6030-madc.c Normal file
View File

@ -0,0 +1,354 @@
/*
*
* TWL6030 MADC module driver-This driver only implements the ADC read
* functions
*
* Copyright (C) 2011 Samsung Telecommunications of America
*
* Based on twl4030-madc.c
* Copyright (C) 2008 Nokia Corporation
* Mikko Ylinen <mikko.k.ylinen@nokia.com>
*
* Amit Kucheria <amit.kucheria@canonical.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#include <linux/init.h>
#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/i2c/twl.h>
#include <linux/i2c/twl6030-madc.h>
#include <linux/module.h>
#include <linux/stddef.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/mutex.h>
#include <linux/bitops.h>
#include <linux/jiffies.h>
#include <linux/types.h>
#include <linux/gfp.h>
#include <linux/err.h>
#include <linux/wakelock.h>
#define GPADCS (1 << 1)
#define GPADCR (1 << 0)
#define REG_TOGGLE1 0x90
#define DRIVER_NAME (twl6030_madc_driver.driver.name)
static struct platform_driver twl6030_madc_driver;
/*
* struct twl6030_madc_data - a container for madc info
* @dev - pointer to device structure for madc
* @lock - mutex protecting this data structure
*/
struct twl6030_madc_data {
struct device *dev;
struct mutex lock;
struct dentry *file;
struct wake_lock wakelock;
};
static struct twl6030_madc_data *twl6030_madc;
static u8 gpadc_ctrl_reg;
static inline int twl6030_madc_start_conversion(struct twl6030_madc_data *madc)
{
int ret;
ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, GPADCS, REG_TOGGLE1);
if (ret) {
dev_err(madc->dev, "unable to write register 0x%X\n",
REG_TOGGLE1);
return ret;
}
udelay(100);
ret = twl_i2c_write_u8(TWL_MODULE_MADC, TWL6030_MADC_SP1,
TWL6030_MADC_CTRL_P1);
if (ret) {
dev_err(madc->dev, "unable to write register 0x%X\n",
TWL6030_MADC_CTRL_P1);
return ret;
}
return 0;
}
/*
* Function that waits for conversion to be ready
* @madc - pointer to twl4030_madc_data struct
* @timeout_ms - timeout value in milliseconds
* @status_reg - ctrl register
* returns 0 if succeeds else a negative error value
*/
static int twl6030_madc_wait_conversion_ready(struct twl6030_madc_data *madc,
unsigned int timeout_ms,
u8 status_reg)
{
unsigned long timeout;
unsigned long delta;
u8 reg;
int ret;
delta = msecs_to_jiffies(timeout_ms);
if (delta < 2)
delta = 2;
wake_lock(&madc->wakelock);
timeout = jiffies + delta;
do {
ret = twl_i2c_read_u8(TWL6030_MODULE_MADC, &reg, status_reg);
if (ret) {
dev_err(madc->dev,
"unable to read status register 0x%X\n",
status_reg);
goto unlock;
}
if (!(reg & TWL6030_MADC_BUSY) && (reg & TWL6030_MADC_EOCP1)) {
ret = 0;
goto unlock;
}
if (time_after(jiffies, timeout))
break;
usleep_range(500, 2000);
} while (1);
dev_err(madc->dev, "conversion timeout, ctrl_px=0x%08x\n", reg);
ret = -EAGAIN;
unlock:
wake_unlock(&madc->wakelock);
return ret;
}
/*
* Function to read a particular channel value.
* @madc - pointer to struct twl6030_madc_data
* @reg - lsb of ADC Channel
* If the i2c read fails it returns an error else returns 0.
*/
static int twl6030_madc_channel_raw_read(struct twl6030_madc_data *madc,
u8 reg)
{
u8 msb, lsb;
int ret;
mutex_lock(&madc->lock);
ret = twl6030_madc_start_conversion(twl6030_madc);
if (ret)
goto unlock;
ret = twl6030_madc_wait_conversion_ready(twl6030_madc, 5,
TWL6030_MADC_CTRL_P1);
if (ret)
goto unlock;
/*
* For each ADC channel, we have MSB and LSB register
* pair. MSB address is always LSB address+1. reg parameter is
* the address of LSB register
*/
ret = twl_i2c_read_u8(TWL6030_MODULE_MADC, &msb, reg + 1);
if (ret) {
dev_err(madc->dev, "unable to read MSB register 0x%X\n",
reg + 1);
goto unlock;
}
ret = twl_i2c_read_u8(TWL6030_MODULE_MADC, &lsb, reg);
if (ret) {
dev_err(madc->dev, "unable to read LSB register 0x%X\n", reg);
goto unlock;
}
ret = (int)((msb << 8) | lsb);
unlock:
/* Disable GPADC for power savings. */
twl_i2c_write_u8(TWL6030_MODULE_ID1, GPADCR, REG_TOGGLE1);
mutex_unlock(&madc->lock);
return ret;
}
/*
* Return channel value
* Or < 0 on failure.
*/
int twl6030_get_madc_conversion(int channel_no)
{
u8 reg = TWL6030_MADC_GPCH0_LSB + (2 * channel_no);
if (!twl6030_madc) {
pr_err("%s: No ADC device\n", __func__);
return -EINVAL;
}
if (channel_no >= TWL6030_MADC_MAX_CHANNELS) {
dev_err(twl6030_madc->dev,
"%s: Channel number (%d) exceeds max (%d)\n",
__func__, channel_no, TWL6030_MADC_MAX_CHANNELS);
return -EINVAL;
}
return twl6030_madc_channel_raw_read(twl6030_madc, reg);
}
EXPORT_SYMBOL_GPL(twl6030_get_madc_conversion);
#ifdef CONFIG_DEBUG_FS
static int debug_twl6030_madc_show(struct seq_file *s, void *_)
{
int i, result;
for (i = 0; i < TWL6030_MADC_MAX_CHANNELS; i++) {
result = twl6030_get_madc_conversion(i);
seq_printf(s, "channel %3d returns result %d\n",
i, result);
}
return 0;
}
static int debug_twl6030_madc_open(struct inode *inode, struct file *file)
{
return single_open(file, debug_twl6030_madc_show, inode->i_private);
}
static const struct file_operations debug_fops = {
.open = debug_twl6030_madc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
#define DEBUG_FOPS (&debug_fops)
#else
#define DEBUG_FOPS NULL
#endif
/*
* Initialize MADC
*/
static int __devinit twl6030_madc_probe(struct platform_device *pdev)
{
struct twl6030_madc_data *madc;
struct twl4030_madc_platform_data *pdata = pdev->dev.platform_data;
if (!pdata) {
dev_err(&pdev->dev, "platform_data not available\n");
return -EINVAL;
}
madc = kzalloc(sizeof(*madc), GFP_KERNEL);
if (!madc)
return -ENOMEM;
platform_set_drvdata(pdev, madc);
madc->dev = &pdev->dev;
mutex_init(&madc->lock);
madc->file = debugfs_create_file(DRIVER_NAME, S_IRUGO, NULL,
madc, DEBUG_FOPS);
wake_lock_init(&madc->wakelock, WAKE_LOCK_SUSPEND, "twl6030 adc");
twl6030_madc = madc;
return 0;
}
static int __devexit twl6030_madc_remove(struct platform_device *pdev)
{
struct twl6030_madc_data *madc = platform_get_drvdata(pdev);
wake_lock_destroy(&madc->wakelock);
mutex_destroy(&madc->lock);
free_irq(platform_get_irq(pdev, 0), madc);
platform_set_drvdata(pdev, NULL);
twl6030_madc = NULL;
debugfs_remove(madc->file);
kfree(madc);
return 0;
}
static int twl6030_madc_suspend(struct device *pdev)
{
int ret;
u8 reg_val;
ret = twl_i2c_read_u8(TWL_MODULE_MADC, &reg_val, TWL6030_MADC_CTRL);
if (!ret) {
reg_val &= ~(TWL6030_MADC_TEMP1_EN);
ret = twl_i2c_write_u8(TWL_MODULE_MADC, reg_val,
TWL6030_MADC_CTRL);
}
if (ret) {
dev_err(twl6030_madc->dev, "unable to disable madc temp1!\n");
gpadc_ctrl_reg = TWL6030_MADC_TEMP1_EN;
} else
gpadc_ctrl_reg = reg_val;
return 0;
};
static int twl6030_madc_resume(struct device *pdev)
{
int ret;
if (!(gpadc_ctrl_reg & TWL6030_MADC_TEMP1_EN)) {
gpadc_ctrl_reg |= TWL6030_MADC_TEMP1_EN;
ret = twl_i2c_write_u8(TWL_MODULE_MADC, gpadc_ctrl_reg,
TWL6030_MADC_CTRL);
if (ret)
dev_err(twl6030_madc->dev,
"unable to enable madc temp1!\n");
}
return 0;
};
static const struct dev_pm_ops twl6030_madc_pm_ops = {
.suspend = twl6030_madc_suspend,
.resume = twl6030_madc_resume,
};
static struct platform_driver twl6030_madc_driver = {
.probe = twl6030_madc_probe,
.remove = __exit_p(twl6030_madc_remove),
.driver = {
.name = "twl6030_madc",
.owner = THIS_MODULE,
.pm = &twl6030_madc_pm_ops,
},
};
static int __init twl6030_madc_init(void)
{
return platform_driver_register(&twl6030_madc_driver);
}
module_init(twl6030_madc_init);
static void __exit twl6030_madc_exit(void)
{
platform_driver_unregister(&twl6030_madc_driver);
}
module_exit(twl6030_madc_exit);
MODULE_DESCRIPTION("TWL6030 ADC driver");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("J Keerthy");
MODULE_ALIAS("platform:twl6030_madc");

362
drivers/mfd/twl6030-power.c Normal file
View File

@ -0,0 +1,362 @@
/*
* Handling for Resource Mapping for TWL6030 Family of chips
*
* Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
* Nishanth Menon
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
* kind, whether express or implied; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/module.h>
#include <linux/pm.h>
#include <linux/i2c/twl.h>
#include <linux/platform_device.h>
#include <linux/suspend.h>
#include <linux/string.h>
#include <asm/mach-types.h>
#define VREG_GRP 0
#define MSK_TRANSITION_APP_SHIFT 0x5
static u8 dev_on_group;
/**
* struct twl6030_resource_map - describe the resource mapping for TWL6030
* @name: name of the resource
* @res_id: resource ID
* @base_addr: base address for TWL6030
* @base_addr: type of the Resources Assignment register for TWL6032
* base_addr = 0 for PREQx_RES_ASS_A register
* base_addr = 1 for PREQx_RES_ASS_B register
* base_addr = 2 for PREQx_RES_ASS_C register
* @group: which device group can control this resource?
* @mask: unused for TWL6030
* @mask: bit mask of the resource in PREQx_RES_ASS_x registers for TWL6032
*/
struct twl6030_resource_map {
char *name;
u8 res_id;
u8 base_addr;
u8 group;
u8 mask;
};
#define TWL6030_RES_DATA(ID, NAME, BASE_ADDR, GROUP) \
{.res_id = ID, .name = NAME, .base_addr = BASE_ADDR,\
.group = GROUP, .mask = 0,}
#define TWL6032_RES_DATA(ID, NAME, BASE_ADDR, GROUP, MASK) \
{.res_id = ID, .name = NAME, .base_addr = BASE_ADDR,\
.group = GROUP, .mask = MASK,}
/* list of all s/w modifiable resources in TWL6030 */
static __initdata struct twl6030_resource_map twl6030_res_map[] = {
TWL6030_RES_DATA(RES_V1V29, "V1V29", 0x40, DEV_GRP_P1),
TWL6030_RES_DATA(RES_V1V8, "V1V8", 0x46, DEV_GRP_P1),
TWL6030_RES_DATA(RES_V2V1, "V2V1", 0x4c, DEV_GRP_P1),
TWL6030_RES_DATA(RES_VDD1, "CORE1", 0x52, DEV_GRP_P1),
TWL6030_RES_DATA(RES_VDD2, "CORE2", 0x58, DEV_GRP_P1),
TWL6030_RES_DATA(RES_VDD3, "CORE3", 0x5e, DEV_GRP_P1),
TWL6030_RES_DATA(RES_VMEM, "VMEM", 0x64, DEV_GRP_P1),
/* VANA cannot be modified */
TWL6030_RES_DATA(RES_VUAX1, "VUAX1", 0x84, DEV_GRP_P1),
TWL6030_RES_DATA(RES_VAUX2, "VAUX2", 0x88, DEV_GRP_P1),
TWL6030_RES_DATA(RES_VAUX3, "VAUX3", 0x8c, DEV_GRP_P1),
TWL6030_RES_DATA(RES_VCXIO, "VCXIO", 0x90, DEV_GRP_P1),
TWL6030_RES_DATA(RES_VDAC, "VDAC", 0x94, DEV_GRP_P1),
TWL6030_RES_DATA(RES_VMMC1, "VMMC", 0x98, DEV_GRP_P1),
TWL6030_RES_DATA(RES_VPP, "VPP", 0x9c, DEV_GRP_P1),
/* VRTC cannot be modified */
TWL6030_RES_DATA(RES_VUSBCP, "VUSB", 0xa0, DEV_GRP_P1),
TWL6030_RES_DATA(RES_VSIM, "VSIM", 0xa4, DEV_GRP_P1),
TWL6030_RES_DATA(RES_REGEN, "REGEN1", 0xad, DEV_GRP_P1),
TWL6030_RES_DATA(RES_REGEN2, "REGEN2", 0xb0, DEV_GRP_P1),
TWL6030_RES_DATA(RES_SYSEN, "SYSEN", 0xb3, DEV_GRP_P1),
/* NRES_PWRON cannot be modified */
/* 32KCLKAO cannot be modified */
TWL6030_RES_DATA(RES_32KCLKG, "32KCLKG", 0xbc, DEV_GRP_P1),
TWL6030_RES_DATA(RES_32KCLKAUDIO, "32KCLKAUDIO", 0xbf, DEV_GRP_P1),
/* BIAS cannot be modified */
/* VBATMIN_HI cannot be modified */
/* RC6MHZ cannot be modified */
/* TEMP cannot be modified */
};
/* list of all s/w modifiable resources in TWL6032 */
static __initdata struct twl6030_resource_map twl6032_res_map[] = {
/* PREQx_RES_ASS_A register resources */
TWL6032_RES_DATA(RES_LDOUSB, "VUSB", 0, DEV_GRP_P1, BIT(5)),
TWL6032_RES_DATA(RES_SMPS5, "SMPS5", 0, DEV_GRP_P1, BIT(4)),
TWL6032_RES_DATA(RES_SMPS5, "SMPS4", 0, DEV_GRP_P1, BIT(3)),
TWL6032_RES_DATA(RES_SMPS5, "SMPS3", 0, DEV_GRP_P1, BIT(2)),
TWL6032_RES_DATA(RES_SMPS5, "SMPS2", 0, DEV_GRP_P1, BIT(1)),
TWL6032_RES_DATA(RES_SMPS5, "SMPS1", 0, DEV_GRP_P1, BIT(0)),
/* PREQx_RES_ASS_B register resources */
TWL6032_RES_DATA(RES_LDOLN, "LDOLN", 1, DEV_GRP_P1, BIT(7)),
TWL6032_RES_DATA(RES_LDO7, "LDO7", 1, DEV_GRP_P1, BIT(6)),
TWL6032_RES_DATA(RES_LDO6, "LDO6", 1, DEV_GRP_P1, BIT(5)),
TWL6032_RES_DATA(RES_LDO5, "LDO5", 1, DEV_GRP_P1, BIT(4)),
TWL6032_RES_DATA(RES_LDO4, "LDO4", 1, DEV_GRP_P1, BIT(3)),
TWL6032_RES_DATA(RES_LDO3, "LDO3", 1, DEV_GRP_P1, BIT(2)),
TWL6032_RES_DATA(RES_LDO2, "LDO2", 1, DEV_GRP_P1, BIT(1)),
TWL6032_RES_DATA(RES_LDO1, "LDO1", 1, DEV_GRP_P1, BIT(0)),
/* PREQx_RES_ASS_C register resources */
TWL6032_RES_DATA(RES_VSYSMIN_HI, "VSYSMIN_HI", 2, DEV_GRP_P1, BIT(5)),
TWL6032_RES_DATA(RES_32KCLKG, "32KCLKG", 2, DEV_GRP_P1, BIT(4)),
TWL6032_RES_DATA(RES_32KCLKAUDIO, "32KCLKAUDIO", 2, DEV_GRP_P1, BIT(3)),
TWL6032_RES_DATA(RES_SYSEN, "SYSEN", 2, DEV_GRP_P1, BIT(2)),
TWL6032_RES_DATA(RES_REGEN2, "REGEN2", 2, DEV_GRP_P1, BIT(1)),
TWL6032_RES_DATA(RES_REGEN, "REGEN1", 2, DEV_GRP_P1, BIT(0)),
};
static struct twl4030_system_config twl6030_sys_config[] = {
{.name = "DEV_ON", .group = DEV_GRP_P1,},
};
/* Actual power groups that TWL understands */
#define P3_GRP_6030 BIT(2) /* secondary processor, modem, etc */
#define P2_GRP_6030 BIT(1) /* "peripherals" */
#define P1_GRP_6030 BIT(0) /* CPU/Linux */
static __init void twl6030_process_system_config(void)
{
u8 grp;
int r;
bool i = false;
struct twl4030_system_config *sys_config;
sys_config = twl6030_sys_config;
while (sys_config && sys_config->name) {
if (!strcmp(sys_config->name, "DEV_ON")) {
dev_on_group = sys_config->group;
i = true;
break;
}
sys_config++;
}
if (!i)
pr_err("%s: Couldn't find DEV_ON resource configuration!"
" MOD & CON group would be kept active.\n", __func__);
if (dev_on_group) {
r = twl_i2c_read_u8(TWL_MODULE_PM_MASTER, &grp,
TWL6030_PHOENIX_DEV_ON);
if (r) {
pr_err("%s: Error(%d) reading {addr=0x%02x}",
__func__, r, TWL6030_PHOENIX_DEV_ON);
/*
* On error resetting to 0, so that all the process
* groups are kept active.
*/
dev_on_group = 0;
} else {
/*
* Unmapped processor groups are disabled by writing
* 1 to corresponding group in DEV_ON.
*/
grp |= (dev_on_group & DEV_GRP_P1) ? 0 : P1_GRP_6030;
grp |= (dev_on_group & DEV_GRP_P2) ? 0 : P2_GRP_6030;
grp |= (dev_on_group & DEV_GRP_P3) ? 0 : P3_GRP_6030;
dev_on_group = grp;
}
/*
* unmask PREQ transition Executes ACT2SLP and SLP2ACT sleep
* sequence
*/
r = twl_i2c_read_u8(TWL_MODULE_PM_MASTER, &grp,
TWL6030_PM_MASTER_MSK_TRANSITION);
if (r) {
pr_err("%s: Error (%d) reading"
" TWL6030_MSK_TRANSITION\n", __func__, r);
return;
}
grp &= (dev_on_group << MSK_TRANSITION_APP_SHIFT);
r = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, grp,
TWL6030_PM_MASTER_MSK_TRANSITION);
if (r)
pr_err("%s: Error (%d) writing to"
" TWL6030_MSK_TRANSITION\n", __func__, r);
}
}
#define DEV_GRP_P1_OFFSET 1
#define DEV_GRP_P2_OFFSET 4
#define DEV_GRP_P3_OFFSET 7
static __init void twl6030_program_map(unsigned long features)
{
struct twl6030_resource_map *res;
int r, i;
if (features & TWL6032_SUBCLASS) {
/**
* mask[0] = 0 for twl_i2c_write
* mask[1]-mask[3]: PREQ1_RES_ASS_A - PREQ1_RES_ASS_C
* mask[4]-mask[6]: PREQ2_RES_ASS_A - PREQ2_RES_ASS_C
* mask[7]-mask[9]: PREQ3_RES_ASS_A - PREQ3_RES_ASS_C
*/
u8 mask[10];
res = twl6032_res_map;
memset(&mask[0], 0, 10);
for (i = 0; i < ARRAY_SIZE(twl6032_res_map); i++) {
/* map back from generic device id to TWL6032 mask */
mask[DEV_GRP_P1_OFFSET + res->base_addr] |= \
(res->group & DEV_GRP_P1) ? res->mask : 0;
mask[DEV_GRP_P2_OFFSET + res->base_addr] |= \
(res->group & DEV_GRP_P2) ? res->mask : 0;
mask[DEV_GRP_P3_OFFSET + res->base_addr] |= \
(res->group & DEV_GRP_P3) ? res->mask : 0;
res++;
}
r = twl_i2c_write(TWL6030_MODULE_ID0, &mask[0],
TWL6032_PREQ1_RES_ASS_A, 9);
if (r)
pr_err("%s: Error(%d) programming TWL6032 PREQ "
"Assignment Registers {start addr=0xd7}\n",
__func__, r);
} else {
res = twl6030_res_map;
for (i = 0; i < ARRAY_SIZE(twl6030_res_map); i++) {
u8 grp = 0;
/* map back from generic device id to TWL6030 ID */
grp |= (res->group & DEV_GRP_P1) ? P1_GRP_6030 : 0;
grp |= (res->group & DEV_GRP_P2) ? P2_GRP_6030 : 0;
grp |= (res->group & DEV_GRP_P3) ? P3_GRP_6030 : 0;
r = twl_i2c_write_u8(TWL6030_MODULE_ID0, res->group,
res->base_addr);
if (r)
pr_err("%s: Error(%d) programming map %s {"
"addr=0x%02x},grp=0x%02X\n", __func__,
r, res->name, res->base_addr,
res->group);
res++;
}
}
}
static __init void twl6030_update_system_map
(struct twl4030_system_config *sys_list)
{
int i;
struct twl4030_system_config *sys_res;
while (sys_list && sys_list->name) {
sys_res = twl6030_sys_config;
for (i = 0; i < ARRAY_SIZE(twl6030_sys_config); i++) {
if (!strcmp(sys_res->name, sys_list->name))
sys_res->group = sys_list->group &
(DEV_GRP_P1 | DEV_GRP_P2 | DEV_GRP_P3);
sys_res++;
}
sys_list++;
}
}
static __init void twl6030_update_map(struct twl4030_resconfig *res_list, \
unsigned long features)
{
int i, res_idx = 0;
struct twl6030_resource_map *res;
struct twl6030_resource_map *cur_twl6030_res = twl6030_res_map;
int twl6030_res_cnt = ARRAY_SIZE(twl6030_res_map);
if (features & TWL6032_SUBCLASS) {
cur_twl6030_res = twl6032_res_map;
twl6030_res_cnt = ARRAY_SIZE(twl6032_res_map);
}
while (res_list->resource != TWL4030_RESCONFIG_UNDEF) {
res = cur_twl6030_res;
for (i = 0; i < twl6030_res_cnt; i++) {
if (res->res_id == res_list->resource) {
res->group = res_list->devgroup &
(DEV_GRP_P1 | DEV_GRP_P2 | DEV_GRP_P3);
break;
}
res++;
}
if (i == twl6030_res_cnt) {
pr_err("%s: in platform_data resource index %d, cannot"
" find match for resource 0x%02x. NO Update!\n",
__func__, res_idx, res_list->resource);
}
res_list++;
res_idx++;
}
}
static int twl6030_power_notifier_cb(struct notifier_block *notifier,
unsigned long pm_event, void *unused)
{
int r = 0;
switch (pm_event) {
case PM_SUSPEND_PREPARE:
r = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, dev_on_group,
TWL6030_PHOENIX_DEV_ON);
if (r)
pr_err("%s: Error(%d) programming {addr=0x%02x}",
__func__, r, TWL6030_PHOENIX_DEV_ON);
break;
}
return notifier_from_errno(r);
}
static struct notifier_block twl6030_power_pm_notifier = {
.notifier_call = twl6030_power_notifier_cb,
};
/**
* twl6030_power_init() - Update the power map to reflect connectivity of board
* @power_data: power resource map to update (OPTIONAL) - use this if a resource
* is used by other devices other than APP (DEV_GRP_P1)
*/
void __init twl6030_power_init(struct twl4030_power_data *power_data, \
unsigned long features)
{
int r;
if (power_data && (!power_data->resource_config &&
!power_data->sys_config)) {
pr_err("%s: power data from platform without configuration!\n",
__func__);
return;
}
if (power_data && power_data->resource_config)
twl6030_update_map(power_data->resource_config, features);
if (power_data && power_data->sys_config)
twl6030_update_system_map(power_data->sys_config);
twl6030_process_system_config();
twl6030_program_map(features);
r = register_pm_notifier(&twl6030_power_pm_notifier);
if (r)
pr_err("%s: twl6030 power registration failed!\n", __func__);
return;
}

View File

@ -0,0 +1,74 @@
/*
* /drivers/mfd/twl6030-poweroff.c
*
* Power off device
*
* Copyright (C) 2011 Texas Instruments Corporation
*
* Written by Rajeev Kulkarni <rajeevk@ti.com>
*
* This file is subject to the terms and conditions of the GNU General
* Public License. See the file "COPYING" in the main directory of this
* archive for more details.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/module.h>
#include <linux/pm.h>
#include <linux/i2c/twl.h>
#define APP_DEVOFF (1<<0)
#define CON_DEVOFF (1<<1)
#define MOD_DEVOFF (1<<2)
void twl6030_poweroff(void)
{
u8 val = 0;
int err = 0;
err = twl_i2c_read_u8(TWL_MODULE_PM_MASTER, &val,
TWL6030_PHOENIX_DEV_ON);
if (err) {
pr_warning("I2C error %d reading PHOENIX_DEV_ON\n", err);
return;
}
val |= APP_DEVOFF | CON_DEVOFF | MOD_DEVOFF;
err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, val,
TWL6030_PHOENIX_DEV_ON);
if (err) {
pr_warning("I2C error %d writing PHOENIX_DEV_ON\n", err);
return;
}
return;
}
static int __init twl6030_poweroff_init(void)
{
pm_power_off = twl6030_poweroff;
return 0;
}
static void __exit twl6030_poweroff_exit(void)
{
pm_power_off = NULL;
}
module_init(twl6030_poweroff_init);
module_exit(twl6030_poweroff_exit);
MODULE_DESCRIPTION("TLW6030 device power off");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Rajeev Kulkarni");

401
drivers/regulator/twl-regulator.c Normal file → Executable file
View File

@ -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),
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),
/* 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),
/* 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),
TWL6025_ADJUSTABLE_SMPS(SMPS3, 0x34, 1),
TWL6025_ADJUSTABLE_SMPS(SMPS4, 0x10, 2),
TWL6025_ADJUSTABLE_SMPS(VIO, 0x16, 3),
};
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)
{

View File

@ -362,14 +362,6 @@ static irqreturn_t twl_rtc_interrupt(int irq, void *rtc)
int res;
u8 rd_reg;
#ifdef CONFIG_LOCKDEP
/* WORKAROUND for lockdep forcing IRQF_DISABLED on us, which
* we don't want and can't tolerate. Although it might be
* friendlier not to borrow this thread context...
*/
local_irq_enable();
#endif
res = twl_rtc_read_u8(&rd_reg, REG_RTC_STATUS_REG);
if (res)
goto out;
@ -428,24 +420,12 @@ static struct rtc_class_ops twl_rtc_ops = {
static int __devinit twl_rtc_probe(struct platform_device *pdev)
{
struct rtc_device *rtc;
int ret = 0;
int ret = -EINVAL;
int irq = platform_get_irq(pdev, 0);
u8 rd_reg;
if (irq <= 0)
return -EINVAL;
rtc = rtc_device_register(pdev->name,
&pdev->dev, &twl_rtc_ops, THIS_MODULE);
if (IS_ERR(rtc)) {
ret = PTR_ERR(rtc);
dev_err(&pdev->dev, "can't register RTC device, err %ld\n",
PTR_ERR(rtc));
goto out0;
}
platform_set_drvdata(pdev, rtc);
goto out1;
ret = twl_rtc_read_u8(&rd_reg, REG_RTC_STATUS_REG);
if (ret < 0)
@ -462,14 +442,6 @@ static int __devinit twl_rtc_probe(struct platform_device *pdev)
if (ret < 0)
goto out1;
ret = request_irq(irq, twl_rtc_interrupt,
IRQF_TRIGGER_RISING,
dev_name(&rtc->dev), rtc);
if (ret < 0) {
dev_err(&pdev->dev, "IRQ is not free.\n");
goto out1;
}
if (twl_class_is_6030()) {
twl6030_interrupt_unmask(TWL6030_RTC_INT_MASK,
REG_INT_MSK_LINE_A);
@ -480,28 +452,48 @@ static int __devinit twl_rtc_probe(struct platform_device *pdev)
/* Check RTC module status, Enable if it is off */
ret = twl_rtc_read_u8(&rd_reg, REG_RTC_CTRL_REG);
if (ret < 0)
goto out2;
goto out1;
if (!(rd_reg & BIT_RTC_CTRL_REG_STOP_RTC_M)) {
dev_info(&pdev->dev, "Enabling TWL-RTC.\n");
rd_reg = BIT_RTC_CTRL_REG_STOP_RTC_M;
ret = twl_rtc_write_u8(rd_reg, REG_RTC_CTRL_REG);
if (ret < 0)
goto out2;
goto out1;
}
/* init cached IRQ enable bits */
ret = twl_rtc_read_u8(&rtc_irq_bits, REG_RTC_INTERRUPTS_REG);
if (ret < 0)
goto out2;
goto out1;
return ret;
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;
}
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;
}

201
include/linux/i2c/twl.h Normal file → Executable file
View File

@ -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
/* 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
/* 6025 DCDC supplies */
#define TWL6025_REG_SMPS3 58
#define TWL6025_REG_SMPS4 59
#define TWL6025_REG_VIO 60
/* 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 */