aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/regulator/tps65910-regulator.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2012-03-21 10:34:56 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2012-03-21 10:34:56 -0700
commitd15d76448bb58c7832e954b6a8f1e301720b7866 (patch)
tree7891c9e0779f5df33840be9cdba1f0331459e97c /drivers/regulator/tps65910-regulator.c
parent0c2fe82a9b106f1c03719783134360586d718a69 (diff)
parent4992fa1fd425f1934f503ffa96b68e235b89db9a (diff)
downloadkernel_goldelico_gta04-d15d76448bb58c7832e954b6a8f1e301720b7866.zip
kernel_goldelico_gta04-d15d76448bb58c7832e954b6a8f1e301720b7866.tar.gz
kernel_goldelico_gta04-d15d76448bb58c7832e954b6a8f1e301720b7866.tar.bz2
Merge tag 'regulator-3.4' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regulator
Pull regulator updates for 3.4 from Mark Brown: "This has been a fairly quiet release from a regulator point of view, the only real framework features added were devm support and a convenience helper for setting up fixed voltage regulators. We also added a couple of drivers (but will drop the BQ240022 driver via the arm-soc tree as it's been replaced by the more generic gpio-regulator driver) and Axel Lin continued his relentless and generally awesome stream of fixes and cleanups." * tag 'regulator-3.4' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regulator: (93 commits) regulator: Fix up a confusing dev_warn when DT lookup fails regulator: Convert tps6507x to set_voltage_sel regulator: Refactor tps6507x to use one tps6507x_pmic_ops for all LDOs and DCDCs regulator: Make s5m8767_get_voltage_register always return correct register regulator: s5m8767: Check pdata->buck[2|3|4]_gpiodvs earlier regulator: tps65910: Provide settling time for DCDC voltage change regulator: Add Anatop regulator driver regulator: Simplify implementation of tps65912_get_voltage_dcdc regulator: Use tps65912_set_voltage_sel for both DCDCx and LDOx regulator: tps65910: Provide settling time for enabling rails regulator: max8925: Use DIV_ROUND_UP macro regulator: tps65912: Use simple equations to get register address regulator: Fix the logic of tps65910_get_mode regulator: Merge tps65217_pmic_ldo234_ops and tps65217_pmic_dcdc_ops to tps65217_pmic_ops regulator: Use DIV_ROUND_CLOSEST in wm8350_isink_get_current regulator: Use array to store dcdc_range settings for tps65912 regulator: Rename s5m8767_convert_voltage to s5m8767_convert_voltage_to_sel regulator: tps6524x: Remove unneeded comment for N_REGULATORS regulator: Rename set_voltage_sel callback function name to *_sel regulator: Fix s5m8767_set_voltage_time_sel calculation value ...
Diffstat (limited to 'drivers/regulator/tps65910-regulator.c')
-rw-r--r--drivers/regulator/tps65910-regulator.c398
1 files changed, 344 insertions, 54 deletions
diff --git a/drivers/regulator/tps65910-regulator.c b/drivers/regulator/tps65910-regulator.c
index 40ecf516..4a37c2b6 100644
--- a/drivers/regulator/tps65910-regulator.c
+++ b/drivers/regulator/tps65910-regulator.c
@@ -26,6 +26,10 @@
#include <linux/mfd/tps65910.h>
#define TPS65910_SUPPLY_STATE_ENABLED 0x1
+#define EXT_SLEEP_CONTROL (TPS65910_SLEEP_CONTROL_EXT_INPUT_EN1 | \
+ TPS65910_SLEEP_CONTROL_EXT_INPUT_EN2 | \
+ TPS65910_SLEEP_CONTROL_EXT_INPUT_EN3 | \
+ TPS65911_SLEEP_CONTROL_EXT_INPUT_SLEEP)
/* supported VIO voltages in milivolts */
static const u16 VIO_VSEL_table[] = {
@@ -83,161 +87,235 @@ struct tps_info {
const char *name;
unsigned min_uV;
unsigned max_uV;
- u8 table_len;
- const u16 *table;
+ u8 n_voltages;
+ const u16 *voltage_table;
+ int enable_time_us;
};
static struct tps_info tps65910_regs[] = {
{
.name = "VRTC",
+ .enable_time_us = 2200,
},
{
.name = "VIO",
.min_uV = 1500000,
.max_uV = 3300000,
- .table_len = ARRAY_SIZE(VIO_VSEL_table),
- .table = VIO_VSEL_table,
+ .n_voltages = ARRAY_SIZE(VIO_VSEL_table),
+ .voltage_table = VIO_VSEL_table,
+ .enable_time_us = 350,
},
{
.name = "VDD1",
.min_uV = 600000,
.max_uV = 4500000,
+ .enable_time_us = 350,
},
{
.name = "VDD2",
.min_uV = 600000,
.max_uV = 4500000,
+ .enable_time_us = 350,
},
{
.name = "VDD3",
.min_uV = 5000000,
.max_uV = 5000000,
- .table_len = ARRAY_SIZE(VDD3_VSEL_table),
- .table = VDD3_VSEL_table,
+ .n_voltages = ARRAY_SIZE(VDD3_VSEL_table),
+ .voltage_table = VDD3_VSEL_table,
+ .enable_time_us = 200,
},
{
.name = "VDIG1",
.min_uV = 1200000,
.max_uV = 2700000,
- .table_len = ARRAY_SIZE(VDIG1_VSEL_table),
- .table = VDIG1_VSEL_table,
+ .n_voltages = ARRAY_SIZE(VDIG1_VSEL_table),
+ .voltage_table = VDIG1_VSEL_table,
+ .enable_time_us = 100,
},
{
.name = "VDIG2",
.min_uV = 1000000,
.max_uV = 1800000,
- .table_len = ARRAY_SIZE(VDIG2_VSEL_table),
- .table = VDIG2_VSEL_table,
+ .n_voltages = ARRAY_SIZE(VDIG2_VSEL_table),
+ .voltage_table = VDIG2_VSEL_table,
+ .enable_time_us = 100,
},
{
.name = "VPLL",
.min_uV = 1000000,
.max_uV = 2500000,
- .table_len = ARRAY_SIZE(VPLL_VSEL_table),
- .table = VPLL_VSEL_table,
+ .n_voltages = ARRAY_SIZE(VPLL_VSEL_table),
+ .voltage_table = VPLL_VSEL_table,
+ .enable_time_us = 100,
},
{
.name = "VDAC",
.min_uV = 1800000,
.max_uV = 2850000,
- .table_len = ARRAY_SIZE(VDAC_VSEL_table),
- .table = VDAC_VSEL_table,
+ .n_voltages = ARRAY_SIZE(VDAC_VSEL_table),
+ .voltage_table = VDAC_VSEL_table,
+ .enable_time_us = 100,
},
{
.name = "VAUX1",
.min_uV = 1800000,
.max_uV = 2850000,
- .table_len = ARRAY_SIZE(VAUX1_VSEL_table),
- .table = VAUX1_VSEL_table,
+ .n_voltages = ARRAY_SIZE(VAUX1_VSEL_table),
+ .voltage_table = VAUX1_VSEL_table,
+ .enable_time_us = 100,
},
{
.name = "VAUX2",
.min_uV = 1800000,
.max_uV = 3300000,
- .table_len = ARRAY_SIZE(VAUX2_VSEL_table),
- .table = VAUX2_VSEL_table,
+ .n_voltages = ARRAY_SIZE(VAUX2_VSEL_table),
+ .voltage_table = VAUX2_VSEL_table,
+ .enable_time_us = 100,
},
{
.name = "VAUX33",
.min_uV = 1800000,
.max_uV = 3300000,
- .table_len = ARRAY_SIZE(VAUX33_VSEL_table),
- .table = VAUX33_VSEL_table,
+ .n_voltages = ARRAY_SIZE(VAUX33_VSEL_table),
+ .voltage_table = VAUX33_VSEL_table,
+ .enable_time_us = 100,
},
{
.name = "VMMC",
.min_uV = 1800000,
.max_uV = 3300000,
- .table_len = ARRAY_SIZE(VMMC_VSEL_table),
- .table = VMMC_VSEL_table,
+ .n_voltages = ARRAY_SIZE(VMMC_VSEL_table),
+ .voltage_table = VMMC_VSEL_table,
+ .enable_time_us = 100,
},
};
static struct tps_info tps65911_regs[] = {
{
+ .name = "VRTC",
+ .enable_time_us = 2200,
+ },
+ {
.name = "VIO",
.min_uV = 1500000,
.max_uV = 3300000,
- .table_len = ARRAY_SIZE(VIO_VSEL_table),
- .table = VIO_VSEL_table,
+ .n_voltages = ARRAY_SIZE(VIO_VSEL_table),
+ .voltage_table = VIO_VSEL_table,
+ .enable_time_us = 350,
},
{
.name = "VDD1",
.min_uV = 600000,
.max_uV = 4500000,
+ .n_voltages = 73,
+ .enable_time_us = 350,
},
{
.name = "VDD2",
.min_uV = 600000,
.max_uV = 4500000,
+ .n_voltages = 73,
+ .enable_time_us = 350,
},
{
.name = "VDDCTRL",
.min_uV = 600000,
.max_uV = 1400000,
+ .n_voltages = 65,
+ .enable_time_us = 900,
},
{
.name = "LDO1",
.min_uV = 1000000,
.max_uV = 3300000,
+ .n_voltages = 47,
+ .enable_time_us = 420,
},
{
.name = "LDO2",
.min_uV = 1000000,
.max_uV = 3300000,
+ .n_voltages = 47,
+ .enable_time_us = 420,
},
{
.name = "LDO3",
.min_uV = 1000000,
.max_uV = 3300000,
+ .n_voltages = 24,
+ .enable_time_us = 230,
},
{
.name = "LDO4",
.min_uV = 1000000,
.max_uV = 3300000,
+ .n_voltages = 47,
+ .enable_time_us = 230,
},
{
.name = "LDO5",
.min_uV = 1000000,
.max_uV = 3300000,
+ .n_voltages = 24,
+ .enable_time_us = 230,
},
{
.name = "LDO6",
.min_uV = 1000000,
.max_uV = 3300000,
+ .n_voltages = 24,
+ .enable_time_us = 230,
},
{
.name = "LDO7",
.min_uV = 1000000,
.max_uV = 3300000,
+ .n_voltages = 24,
+ .enable_time_us = 230,
},
{
.name = "LDO8",
.min_uV = 1000000,
.max_uV = 3300000,
+ .n_voltages = 24,
+ .enable_time_us = 230,
},
};
+#define EXT_CONTROL_REG_BITS(id, regs_offs, bits) (((regs_offs) << 8) | (bits))
+static unsigned int tps65910_ext_sleep_control[] = {
+ 0,
+ EXT_CONTROL_REG_BITS(VIO, 1, 0),
+ EXT_CONTROL_REG_BITS(VDD1, 1, 1),
+ EXT_CONTROL_REG_BITS(VDD2, 1, 2),
+ EXT_CONTROL_REG_BITS(VDD3, 1, 3),
+ EXT_CONTROL_REG_BITS(VDIG1, 0, 1),
+ EXT_CONTROL_REG_BITS(VDIG2, 0, 2),
+ EXT_CONTROL_REG_BITS(VPLL, 0, 6),
+ EXT_CONTROL_REG_BITS(VDAC, 0, 7),
+ EXT_CONTROL_REG_BITS(VAUX1, 0, 3),
+ EXT_CONTROL_REG_BITS(VAUX2, 0, 4),
+ EXT_CONTROL_REG_BITS(VAUX33, 0, 5),
+ EXT_CONTROL_REG_BITS(VMMC, 0, 0),
+};
+
+static unsigned int tps65911_ext_sleep_control[] = {
+ 0,
+ EXT_CONTROL_REG_BITS(VIO, 1, 0),
+ EXT_CONTROL_REG_BITS(VDD1, 1, 1),
+ EXT_CONTROL_REG_BITS(VDD2, 1, 2),
+ EXT_CONTROL_REG_BITS(VDDCTRL, 1, 3),
+ EXT_CONTROL_REG_BITS(LDO1, 0, 1),
+ EXT_CONTROL_REG_BITS(LDO2, 0, 2),
+ EXT_CONTROL_REG_BITS(LDO3, 0, 7),
+ EXT_CONTROL_REG_BITS(LDO4, 0, 6),
+ EXT_CONTROL_REG_BITS(LDO5, 0, 3),
+ EXT_CONTROL_REG_BITS(LDO6, 0, 0),
+ EXT_CONTROL_REG_BITS(LDO7, 0, 5),
+ EXT_CONTROL_REG_BITS(LDO8, 0, 4),
+};
+
struct tps65910_reg {
struct regulator_desc *desc;
struct tps65910 *mfd;
@@ -247,6 +325,8 @@ struct tps65910_reg {
int num_regulators;
int mode;
int (*get_ctrl_reg)(int);
+ unsigned int *ext_sleep_control;
+ unsigned int board_ext_control[TPS65910_NUM_REGS];
};
static inline int tps65910_read(struct tps65910_reg *pmic, u8 reg)
@@ -429,6 +509,12 @@ static int tps65910_disable(struct regulator_dev *dev)
return tps65910_clear_bits(mfd, reg, TPS65910_SUPPLY_STATE_ENABLED);
}
+static int tps65910_enable_time(struct regulator_dev *dev)
+{
+ struct tps65910_reg *pmic = rdev_get_drvdata(dev);
+ int id = rdev_get_id(dev);
+ return pmic->info[id]->enable_time_us;
+}
static int tps65910_set_mode(struct regulator_dev *dev, unsigned int mode)
{
@@ -467,7 +553,7 @@ static unsigned int tps65910_get_mode(struct regulator_dev *dev)
if (value < 0)
return value;
- if (value & LDO_ST_ON_BIT)
+ if (!(value & LDO_ST_ON_BIT))
return REGULATOR_MODE_STANDBY;
else if (value & LDO_ST_MODE_BIT)
return REGULATOR_MODE_IDLE;
@@ -475,10 +561,10 @@ static unsigned int tps65910_get_mode(struct regulator_dev *dev)
return REGULATOR_MODE_NORMAL;
}
-static int tps65910_get_voltage_dcdc(struct regulator_dev *dev)
+static int tps65910_get_voltage_dcdc_sel(struct regulator_dev *dev)
{
struct tps65910_reg *pmic = rdev_get_drvdata(dev);
- int id = rdev_get_id(dev), voltage = 0;
+ int id = rdev_get_id(dev);
int opvsel = 0, srvsel = 0, vselmax = 0, mult = 0, sr = 0;
switch (id) {
@@ -522,9 +608,7 @@ static int tps65910_get_voltage_dcdc(struct regulator_dev *dev)
srvsel = 3;
if (srvsel > vselmax)
srvsel = vselmax;
- srvsel -= 3;
-
- voltage = (srvsel * VDD1_2_OFFSET + VDD1_2_MIN_VOLT) * 100;
+ return srvsel - 3;
} else {
/* normalise to valid range*/
@@ -532,14 +616,9 @@ static int tps65910_get_voltage_dcdc(struct regulator_dev *dev)
opvsel = 3;
if (opvsel > vselmax)
opvsel = vselmax;
- opvsel -= 3;
-
- voltage = (opvsel * VDD1_2_OFFSET + VDD1_2_MIN_VOLT) * 100;
+ return opvsel - 3;
}
-
- voltage *= mult;
-
- return voltage;
+ return -EINVAL;
}
static int tps65910_get_voltage(struct regulator_dev *dev)
@@ -572,7 +651,7 @@ static int tps65910_get_voltage(struct regulator_dev *dev)
return -EINVAL;
}
- voltage = pmic->info[id]->table[value] * 1000;
+ voltage = pmic->info[id]->voltage_table[value] * 1000;
return voltage;
}
@@ -622,8 +701,9 @@ static int tps65911_get_voltage(struct regulator_dev *dev)
step_mv = 100;
break;
case TPS65910_REG_VIO:
- return pmic->info[id]->table[value] * 1000;
- break;
+ value &= LDO_SEL_MASK;
+ value >>= LDO_SEL_SHIFT;
+ return pmic->info[id]->voltage_table[value] * 1000;
default:
return -EINVAL;
}
@@ -631,8 +711,8 @@ static int tps65911_get_voltage(struct regulator_dev *dev)
return (LDO_MIN_VOLT + value * step_mv) * 1000;
}
-static int tps65910_set_voltage_dcdc(struct regulator_dev *dev,
- unsigned selector)
+static int tps65910_set_voltage_dcdc_sel(struct regulator_dev *dev,
+ unsigned selector)
{
struct tps65910_reg *pmic = rdev_get_drvdata(dev);
int id = rdev_get_id(dev), vsel;
@@ -669,7 +749,8 @@ static int tps65910_set_voltage_dcdc(struct regulator_dev *dev,
return 0;
}
-static int tps65910_set_voltage(struct regulator_dev *dev, unsigned selector)
+static int tps65910_set_voltage_sel(struct regulator_dev *dev,
+ unsigned selector)
{
struct tps65910_reg *pmic = rdev_get_drvdata(dev);
int reg, id = rdev_get_id(dev);
@@ -695,7 +776,8 @@ static int tps65910_set_voltage(struct regulator_dev *dev, unsigned selector)
return -EINVAL;
}
-static int tps65911_set_voltage(struct regulator_dev *dev, unsigned selector)
+static int tps65911_set_voltage_sel(struct regulator_dev *dev,
+ unsigned selector)
{
struct tps65910_reg *pmic = rdev_get_drvdata(dev);
int reg, id = rdev_get_id(dev);
@@ -715,9 +797,11 @@ static int tps65911_set_voltage(struct regulator_dev *dev, unsigned selector)
case TPS65911_REG_LDO6:
case TPS65911_REG_LDO7:
case TPS65911_REG_LDO8:
- case TPS65910_REG_VIO:
return tps65910_modify_bits(pmic, reg,
(selector << LDO_SEL_SHIFT), LDO3_SEL_MASK);
+ case TPS65910_REG_VIO:
+ return tps65910_modify_bits(pmic, reg,
+ (selector << LDO_SEL_SHIFT), LDO_SEL_MASK);
}
return -EINVAL;
@@ -756,10 +840,10 @@ static int tps65910_list_voltage(struct regulator_dev *dev,
if (id < TPS65910_REG_VIO || id > TPS65910_REG_VMMC)
return -EINVAL;
- if (selector >= pmic->info[id]->table_len)
+ if (selector >= pmic->info[id]->n_voltages)
return -EINVAL;
else
- voltage = pmic->info[id]->table[selector] * 1000;
+ voltage = pmic->info[id]->voltage_table[selector] * 1000;
return voltage;
}
@@ -795,7 +879,7 @@ static int tps65911_list_voltage(struct regulator_dev *dev, unsigned selector)
step_mv = 100;
break;
case TPS65910_REG_VIO:
- return pmic->info[id]->table[selector] * 1000;
+ return pmic->info[id]->voltage_table[selector] * 1000;
default:
return -EINVAL;
}
@@ -803,15 +887,42 @@ static int tps65911_list_voltage(struct regulator_dev *dev, unsigned selector)
return (LDO_MIN_VOLT + selector * step_mv) * 1000;
}
+static int tps65910_set_voltage_dcdc_time_sel(struct regulator_dev *dev,
+ unsigned int old_selector, unsigned int new_selector)
+{
+ int id = rdev_get_id(dev);
+ int old_volt, new_volt;
+
+ old_volt = tps65910_list_voltage_dcdc(dev, old_selector);
+ if (old_volt < 0)
+ return old_volt;
+
+ new_volt = tps65910_list_voltage_dcdc(dev, new_selector);
+ if (new_volt < 0)
+ return new_volt;
+
+ /* VDD1 and VDD2 are 12.5mV/us, VDDCTRL is 100mV/20us */
+ switch (id) {
+ case TPS65910_REG_VDD1:
+ case TPS65910_REG_VDD2:
+ return DIV_ROUND_UP(abs(old_volt - new_volt), 12500);
+ case TPS65911_REG_VDDCTRL:
+ return DIV_ROUND_UP(abs(old_volt - new_volt), 5000);
+ }
+ return -EINVAL;
+}
+
/* Regulator ops (except VRTC) */
static struct regulator_ops tps65910_ops_dcdc = {
.is_enabled = tps65910_is_enabled,
.enable = tps65910_enable,
.disable = tps65910_disable,
+ .enable_time = tps65910_enable_time,
.set_mode = tps65910_set_mode,
.get_mode = tps65910_get_mode,
- .get_voltage = tps65910_get_voltage_dcdc,
- .set_voltage_sel = tps65910_set_voltage_dcdc,
+ .get_voltage_sel = tps65910_get_voltage_dcdc_sel,
+ .set_voltage_sel = tps65910_set_voltage_dcdc_sel,
+ .set_voltage_time_sel = tps65910_set_voltage_dcdc_time_sel,
.list_voltage = tps65910_list_voltage_dcdc,
};
@@ -819,6 +930,7 @@ static struct regulator_ops tps65910_ops_vdd3 = {
.is_enabled = tps65910_is_enabled,
.enable = tps65910_enable,
.disable = tps65910_disable,
+ .enable_time = tps65910_enable_time,
.set_mode = tps65910_set_mode,
.get_mode = tps65910_get_mode,
.get_voltage = tps65910_get_voltage_vdd3,
@@ -829,10 +941,11 @@ static struct regulator_ops tps65910_ops = {
.is_enabled = tps65910_is_enabled,
.enable = tps65910_enable,
.disable = tps65910_disable,
+ .enable_time = tps65910_enable_time,
.set_mode = tps65910_set_mode,
.get_mode = tps65910_get_mode,
.get_voltage = tps65910_get_voltage,
- .set_voltage_sel = tps65910_set_voltage,
+ .set_voltage_sel = tps65910_set_voltage_sel,
.list_voltage = tps65910_list_voltage,
};
@@ -840,13 +953,147 @@ static struct regulator_ops tps65911_ops = {
.is_enabled = tps65910_is_enabled,
.enable = tps65910_enable,
.disable = tps65910_disable,
+ .enable_time = tps65910_enable_time,
.set_mode = tps65910_set_mode,
.get_mode = tps65910_get_mode,
.get_voltage = tps65911_get_voltage,
- .set_voltage_sel = tps65911_set_voltage,
+ .set_voltage_sel = tps65911_set_voltage_sel,
.list_voltage = tps65911_list_voltage,
};
+static int tps65910_set_ext_sleep_config(struct tps65910_reg *pmic,
+ int id, int ext_sleep_config)
+{
+ struct tps65910 *mfd = pmic->mfd;
+ u8 regoffs = (pmic->ext_sleep_control[id] >> 8) & 0xFF;
+ u8 bit_pos = (1 << pmic->ext_sleep_control[id] & 0xFF);
+ int ret;
+
+ /*
+ * Regulator can not be control from multiple external input EN1, EN2
+ * and EN3 together.
+ */
+ if (ext_sleep_config & EXT_SLEEP_CONTROL) {
+ int en_count;
+ en_count = ((ext_sleep_config &
+ TPS65910_SLEEP_CONTROL_EXT_INPUT_EN1) != 0);
+ en_count += ((ext_sleep_config &
+ TPS65910_SLEEP_CONTROL_EXT_INPUT_EN2) != 0);
+ en_count += ((ext_sleep_config &
+ TPS65910_SLEEP_CONTROL_EXT_INPUT_EN3) != 0);
+ en_count += ((ext_sleep_config &
+ TPS65911_SLEEP_CONTROL_EXT_INPUT_SLEEP) != 0);
+ if (en_count > 1) {
+ dev_err(mfd->dev,
+ "External sleep control flag is not proper\n");
+ return -EINVAL;
+ }
+ }
+
+ pmic->board_ext_control[id] = ext_sleep_config;
+
+ /* External EN1 control */
+ if (ext_sleep_config & TPS65910_SLEEP_CONTROL_EXT_INPUT_EN1)
+ ret = tps65910_set_bits(mfd,
+ TPS65910_EN1_LDO_ASS + regoffs, bit_pos);
+ else
+ ret = tps65910_clear_bits(mfd,
+ TPS65910_EN1_LDO_ASS + regoffs, bit_pos);
+ if (ret < 0) {
+ dev_err(mfd->dev,
+ "Error in configuring external control EN1\n");
+ return ret;
+ }
+
+ /* External EN2 control */
+ if (ext_sleep_config & TPS65910_SLEEP_CONTROL_EXT_INPUT_EN2)
+ ret = tps65910_set_bits(mfd,
+ TPS65910_EN2_LDO_ASS + regoffs, bit_pos);
+ else
+ ret = tps65910_clear_bits(mfd,
+ TPS65910_EN2_LDO_ASS + regoffs, bit_pos);
+ if (ret < 0) {
+ dev_err(mfd->dev,
+ "Error in configuring external control EN2\n");
+ return ret;
+ }
+
+ /* External EN3 control for TPS65910 LDO only */
+ if ((tps65910_chip_id(mfd) == TPS65910) &&
+ (id >= TPS65910_REG_VDIG1)) {
+ if (ext_sleep_config & TPS65910_SLEEP_CONTROL_EXT_INPUT_EN3)
+ ret = tps65910_set_bits(mfd,
+ TPS65910_EN3_LDO_ASS + regoffs, bit_pos);
+ else
+ ret = tps65910_clear_bits(mfd,
+ TPS65910_EN3_LDO_ASS + regoffs, bit_pos);
+ if (ret < 0) {
+ dev_err(mfd->dev,
+ "Error in configuring external control EN3\n");
+ return ret;
+ }
+ }
+
+ /* Return if no external control is selected */
+ if (!(ext_sleep_config & EXT_SLEEP_CONTROL)) {
+ /* Clear all sleep controls */
+ ret = tps65910_clear_bits(mfd,
+ TPS65910_SLEEP_KEEP_LDO_ON + regoffs, bit_pos);
+ if (!ret)
+ ret = tps65910_clear_bits(mfd,
+ TPS65910_SLEEP_SET_LDO_OFF + regoffs, bit_pos);
+ if (ret < 0)
+ dev_err(mfd->dev,
+ "Error in configuring SLEEP register\n");
+ return ret;
+ }
+
+ /*
+ * For regulator that has separate operational and sleep register make
+ * sure that operational is used and clear sleep register to turn
+ * regulator off when external control is inactive
+ */
+ if ((id == TPS65910_REG_VDD1) ||
+ (id == TPS65910_REG_VDD2) ||
+ ((id == TPS65911_REG_VDDCTRL) &&
+ (tps65910_chip_id(mfd) == TPS65911))) {
+ int op_reg_add = pmic->get_ctrl_reg(id) + 1;
+ int sr_reg_add = pmic->get_ctrl_reg(id) + 2;
+ int opvsel = tps65910_reg_read(pmic, op_reg_add);
+ int srvsel = tps65910_reg_read(pmic, sr_reg_add);
+ if (opvsel & VDD1_OP_CMD_MASK) {
+ u8 reg_val = srvsel & VDD1_OP_SEL_MASK;
+ ret = tps65910_reg_write(pmic, op_reg_add, reg_val);
+ if (ret < 0) {
+ dev_err(mfd->dev,
+ "Error in configuring op register\n");
+ return ret;
+ }
+ }
+ ret = tps65910_reg_write(pmic, sr_reg_add, 0);
+ if (ret < 0) {
+ dev_err(mfd->dev, "Error in settting sr register\n");
+ return ret;
+ }
+ }
+
+ ret = tps65910_clear_bits(mfd,
+ TPS65910_SLEEP_KEEP_LDO_ON + regoffs, bit_pos);
+ if (!ret) {
+ if (ext_sleep_config & TPS65911_SLEEP_CONTROL_EXT_INPUT_SLEEP)
+ ret = tps65910_set_bits(mfd,
+ TPS65910_SLEEP_SET_LDO_OFF + regoffs, bit_pos);
+ else
+ ret = tps65910_clear_bits(mfd,
+ TPS65910_SLEEP_SET_LDO_OFF + regoffs, bit_pos);
+ }
+ if (ret < 0)
+ dev_err(mfd->dev,
+ "Error in configuring SLEEP register\n");
+
+ return ret;
+}
+
static __devinit int tps65910_probe(struct platform_device *pdev)
{
struct tps65910 *tps65910 = dev_get_drvdata(pdev->dev.parent);
@@ -877,11 +1124,13 @@ static __devinit int tps65910_probe(struct platform_device *pdev)
case TPS65910:
pmic->get_ctrl_reg = &tps65910_get_ctrl_register;
pmic->num_regulators = ARRAY_SIZE(tps65910_regs);
+ pmic->ext_sleep_control = tps65910_ext_sleep_control;
info = tps65910_regs;
break;
case TPS65911:
pmic->get_ctrl_reg = &tps65911_get_ctrl_register;
pmic->num_regulators = ARRAY_SIZE(tps65911_regs);
+ pmic->ext_sleep_control = tps65911_ext_sleep_control;
info = tps65911_regs;
break;
default:
@@ -926,7 +1175,7 @@ static __devinit int tps65910_probe(struct platform_device *pdev)
pmic->desc[i].name = info->name;
pmic->desc[i].id = i;
- pmic->desc[i].n_voltages = info->table_len;
+ pmic->desc[i].n_voltages = info->n_voltages;
if (i == TPS65910_REG_VDD1 || i == TPS65910_REG_VDD2) {
pmic->desc[i].ops = &tps65910_ops_dcdc;
@@ -944,6 +1193,16 @@ static __devinit int tps65910_probe(struct platform_device *pdev)
pmic->desc[i].ops = &tps65911_ops;
}
+ err = tps65910_set_ext_sleep_config(pmic, i,
+ pmic_plat_data->regulator_ext_sleep_control[i]);
+ /*
+ * Failing on regulator for configuring externally control
+ * is not a serious issue, just throw warning.
+ */
+ if (err < 0)
+ dev_warn(tps65910->dev,
+ "Failed to initialise ext control config\n");
+
pmic->desc[i].type = REGULATOR_VOLTAGE;
pmic->desc[i].owner = THIS_MODULE;
@@ -990,6 +1249,36 @@ static int __devexit tps65910_remove(struct platform_device *pdev)
return 0;
}
+static void tps65910_shutdown(struct platform_device *pdev)
+{
+ struct tps65910_reg *pmic = platform_get_drvdata(pdev);
+ int i;
+
+ /*
+ * Before bootloader jumps to kernel, it makes sure that required
+ * external control signals are in desired state so that given rails
+ * can be configure accordingly.
+ * If rails are configured to be controlled from external control
+ * then before shutting down/rebooting the system, the external
+ * control configuration need to be remove from the rails so that
+ * its output will be available as per register programming even
+ * if external controls are removed. This is require when the POR
+ * value of the control signals are not in active state and before
+ * bootloader initializes it, the system requires the rail output
+ * to be active for booting.
+ */
+ for (i = 0; i < pmic->num_regulators; i++) {
+ int err;
+ if (!pmic->rdev[i])
+ continue;
+
+ err = tps65910_set_ext_sleep_config(pmic, i, 0);
+ if (err < 0)
+ dev_err(&pdev->dev,
+ "Error in clearing external control\n");
+ }
+}
+
static struct platform_driver tps65910_driver = {
.driver = {
.name = "tps65910-pmic",
@@ -997,6 +1286,7 @@ static struct platform_driver tps65910_driver = {
},
.probe = tps65910_probe,
.remove = __devexit_p(tps65910_remove),
+ .shutdown = tps65910_shutdown,
};
static int __init tps65910_init(void)
@@ -1012,6 +1302,6 @@ static void __exit tps65910_cleanup(void)
module_exit(tps65910_cleanup);
MODULE_AUTHOR("Graeme Gregory <gg@slimlogic.co.uk>");
-MODULE_DESCRIPTION("TPS6507x voltage regulator driver");
+MODULE_DESCRIPTION("TPS65910/TPS65911 voltage regulator driver");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:tps65910-pmic");