summaryrefslogtreecommitdiff
path: root/drivers/regulator/mcp16502.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/regulator/mcp16502.c')
-rw-r--r--drivers/regulator/mcp16502.c146
1 files changed, 110 insertions, 36 deletions
diff --git a/drivers/regulator/mcp16502.c b/drivers/regulator/mcp16502.c
index 6d0ad74935b3..b34ae0bbba6f 100644
--- a/drivers/regulator/mcp16502.c
+++ b/drivers/regulator/mcp16502.c
@@ -8,7 +8,6 @@
//
// Inspired from tps65086-regulator.c
-#include <linux/gpio.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/kernel.h>
@@ -22,8 +21,9 @@
#define VDD_LOW_SEL 0x0D
#define VDD_HIGH_SEL 0x3F
-#define MCP16502_FLT BIT(7)
-#define MCP16502_ENS BIT(0)
+#define MCP16502_FLT BIT(7)
+#define MCP16502_DVSR GENMASK(3, 2)
+#define MCP16502_ENS BIT(0)
/*
* The PMIC has four sets of registers corresponding to four power modes:
@@ -54,13 +54,9 @@
* This function is useful for iterating over all regulators and accessing their
* registers in a generic way or accessing a regulator device by its id.
*/
-#define MCP16502_BASE(i) (((i) + 1) << 4)
+#define MCP16502_REG_BASE(i, r) ((((i) + 1) << 4) + MCP16502_REG_##r)
#define MCP16502_STAT_BASE(i) ((i) + 5)
-#define MCP16502_OFFSET_MODE_A 0
-#define MCP16502_OFFSET_MODE_LPM 1
-#define MCP16502_OFFSET_MODE_HIB 2
-
#define MCP16502_OPMODE_ACTIVE REGULATOR_MODE_NORMAL
#define MCP16502_OPMODE_LPM REGULATOR_MODE_IDLE
#define MCP16502_OPMODE_HIB REGULATOR_MODE_STANDBY
@@ -75,6 +71,34 @@
#define MCP16502_MIN_REG 0x0
#define MCP16502_MAX_REG 0x65
+/**
+ * enum mcp16502_reg - MCP16502 regulators's registers
+ * @MCP16502_REG_A: active state register
+ * @MCP16502_REG_LPM: low power mode state register
+ * @MCP16502_REG_HIB: hibernate state register
+ * @MCP16502_REG_HPM: high-performance mode register
+ * @MCP16502_REG_SEQ: startup sequence register
+ * @MCP16502_REG_CFG: configuration register
+ */
+enum mcp16502_reg {
+ MCP16502_REG_A,
+ MCP16502_REG_LPM,
+ MCP16502_REG_HIB,
+ MCP16502_REG_HPM,
+ MCP16502_REG_SEQ,
+ MCP16502_REG_CFG,
+};
+
+/* Ramp delay (uV/us) for buck1, ldo1, ldo2. */
+static const unsigned int mcp16502_ramp_b1l12[] = {
+ 6250, 3125, 2083, 1563
+};
+
+/* Ramp delay (uV/us) for buck2, buck3, buck4. */
+static const unsigned int mcp16502_ramp_b234[] = {
+ 3125, 1563, 1042, 781
+};
+
static unsigned int mcp16502_of_map_mode(unsigned int mode)
{
if (mode == REGULATOR_MODE_NORMAL || mode == REGULATOR_MODE_IDLE)
@@ -83,23 +107,29 @@ static unsigned int mcp16502_of_map_mode(unsigned int mode)
return REGULATOR_MODE_INVALID;
}
-#define MCP16502_REGULATOR(_name, _id, _ranges, _ops) \
+#define MCP16502_REGULATOR(_name, _id, _sn, _ranges, _ops, _ramp_table) \
[_id] = { \
.name = _name, \
- .regulators_node = of_match_ptr("regulators"), \
+ .supply_name = #_sn, \
+ .regulators_node = "regulators", \
.id = _id, \
.ops = &(_ops), \
.type = REGULATOR_VOLTAGE, \
.owner = THIS_MODULE, \
.n_voltages = MCP16502_VSEL + 1, \
.linear_ranges = _ranges, \
+ .linear_min_sel = VDD_LOW_SEL, \
.n_linear_ranges = ARRAY_SIZE(_ranges), \
- .of_match = of_match_ptr(_name), \
+ .of_match = _name, \
.of_map_mode = mcp16502_of_map_mode, \
.vsel_reg = (((_id) + 1) << 4), \
.vsel_mask = MCP16502_VSEL, \
.enable_reg = (((_id) + 1) << 4), \
.enable_mask = MCP16502_EN, \
+ .ramp_reg = MCP16502_REG_BASE(_id, CFG), \
+ .ramp_mask = MCP16502_DVSR, \
+ .ramp_delay_table = _ramp_table, \
+ .n_ramp_values = ARRAY_SIZE(_ramp_table), \
}
enum {
@@ -114,8 +144,6 @@ enum {
/*
* struct mcp16502 - PMIC representation
- * @rdev: the regulators belonging to this chip
- * @rmap: regmap to be used for I2C communication
* @lpm: LPM GPIO descriptor
*/
struct mcp16502 {
@@ -143,22 +171,20 @@ static void mcp16502_gpio_set_mode(struct mcp16502 *mcp, int mode)
}
/*
- * mcp16502_get_reg() - get the PMIC's configuration register for opmode
+ * mcp16502_get_reg() - get the PMIC's state configuration register for opmode
*
* @rdev: the regulator whose register we are searching
* @opmode: the PMIC's operating mode ACTIVE, Low-power, Hibernate
*/
-static int mcp16502_get_reg(struct regulator_dev *rdev, int opmode)
+static int mcp16502_get_state_reg(struct regulator_dev *rdev, int opmode)
{
- int reg = MCP16502_BASE(rdev_get_id(rdev));
-
switch (opmode) {
case MCP16502_OPMODE_ACTIVE:
- return reg + MCP16502_OFFSET_MODE_A;
+ return MCP16502_REG_BASE(rdev_get_id(rdev), A);
case MCP16502_OPMODE_LPM:
- return reg + MCP16502_OFFSET_MODE_LPM;
+ return MCP16502_REG_BASE(rdev_get_id(rdev), LPM);
case MCP16502_OPMODE_HIB:
- return reg + MCP16502_OFFSET_MODE_HIB;
+ return MCP16502_REG_BASE(rdev_get_id(rdev), HIB);
default:
return -EINVAL;
}
@@ -178,7 +204,7 @@ static unsigned int mcp16502_get_mode(struct regulator_dev *rdev)
unsigned int val;
int ret, reg;
- reg = mcp16502_get_reg(rdev, MCP16502_OPMODE_ACTIVE);
+ reg = mcp16502_get_state_reg(rdev, MCP16502_OPMODE_ACTIVE);
if (reg < 0)
return reg;
@@ -209,7 +235,7 @@ static int _mcp16502_set_mode(struct regulator_dev *rdev, unsigned int mode,
int val;
int reg;
- reg = mcp16502_get_reg(rdev, op_mode);
+ reg = mcp16502_get_state_reg(rdev, op_mode);
if (reg < 0)
return reg;
@@ -259,6 +285,44 @@ static int mcp16502_get_status(struct regulator_dev *rdev)
return REGULATOR_STATUS_UNDEFINED;
}
+static int mcp16502_set_voltage_time_sel(struct regulator_dev *rdev,
+ unsigned int old_sel,
+ unsigned int new_sel)
+{
+ static const u8 us_ramp[] = { 8, 16, 24, 32 };
+ int id = rdev_get_id(rdev);
+ unsigned int uV_delta, val;
+ int ret;
+
+ ret = regmap_read(rdev->regmap, MCP16502_REG_BASE(id, CFG), &val);
+ if (ret)
+ return ret;
+
+ val = (val & MCP16502_DVSR) >> 2;
+ uV_delta = abs(new_sel * rdev->desc->linear_ranges->step -
+ old_sel * rdev->desc->linear_ranges->step);
+ switch (id) {
+ case BUCK1:
+ case LDO1:
+ case LDO2:
+ ret = DIV_ROUND_CLOSEST(uV_delta * us_ramp[val],
+ mcp16502_ramp_b1l12[val]);
+ break;
+
+ case BUCK2:
+ case BUCK3:
+ case BUCK4:
+ ret = DIV_ROUND_CLOSEST(uV_delta * us_ramp[val],
+ mcp16502_ramp_b234[val]);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
#ifdef CONFIG_SUSPEND
/*
* mcp16502_suspend_get_target_reg() - get the reg of the target suspend PMIC
@@ -268,10 +332,10 @@ static int mcp16502_suspend_get_target_reg(struct regulator_dev *rdev)
{
switch (pm_suspend_target_state) {
case PM_SUSPEND_STANDBY:
- return mcp16502_get_reg(rdev, MCP16502_OPMODE_LPM);
+ return mcp16502_get_state_reg(rdev, MCP16502_OPMODE_LPM);
case PM_SUSPEND_ON:
case PM_SUSPEND_MEM:
- return mcp16502_get_reg(rdev, MCP16502_OPMODE_HIB);
+ return mcp16502_get_state_reg(rdev, MCP16502_OPMODE_HIB);
default:
dev_err(&rdev->dev, "invalid suspend target: %d\n",
pm_suspend_target_state);
@@ -353,6 +417,8 @@ static const struct regulator_ops mcp16502_buck_ops = {
.disable = regulator_disable_regmap,
.is_enabled = regulator_is_enabled_regmap,
.get_status = mcp16502_get_status,
+ .set_voltage_time_sel = mcp16502_set_voltage_time_sel,
+ .set_ramp_delay = regulator_set_ramp_delay_regmap,
.set_mode = mcp16502_set_mode,
.get_mode = mcp16502_get_mode,
@@ -377,6 +443,8 @@ static const struct regulator_ops mcp16502_ldo_ops = {
.disable = regulator_disable_regmap,
.is_enabled = regulator_is_enabled_regmap,
.get_status = mcp16502_get_status,
+ .set_voltage_time_sel = mcp16502_set_voltage_time_sel,
+ .set_ramp_delay = regulator_set_ramp_delay_regmap,
#ifdef CONFIG_SUSPEND
.set_suspend_voltage = mcp16502_set_suspend_voltage,
@@ -400,13 +468,19 @@ static const struct linear_range b234_ranges[] = {
};
static const struct regulator_desc mcp16502_desc[] = {
- /* MCP16502_REGULATOR(_name, _id, ranges, regulator_ops) */
- MCP16502_REGULATOR("VDD_IO", BUCK1, b1l12_ranges, mcp16502_buck_ops),
- MCP16502_REGULATOR("VDD_DDR", BUCK2, b234_ranges, mcp16502_buck_ops),
- MCP16502_REGULATOR("VDD_CORE", BUCK3, b234_ranges, mcp16502_buck_ops),
- MCP16502_REGULATOR("VDD_OTHER", BUCK4, b234_ranges, mcp16502_buck_ops),
- MCP16502_REGULATOR("LDO1", LDO1, b1l12_ranges, mcp16502_ldo_ops),
- MCP16502_REGULATOR("LDO2", LDO2, b1l12_ranges, mcp16502_ldo_ops)
+ /* MCP16502_REGULATOR(_name, _id, _sn, _ranges, _ops, _ramp_table) */
+ MCP16502_REGULATOR("VDD_IO", BUCK1, pvin1, b1l12_ranges, mcp16502_buck_ops,
+ mcp16502_ramp_b1l12),
+ MCP16502_REGULATOR("VDD_DDR", BUCK2, pvin2, b234_ranges, mcp16502_buck_ops,
+ mcp16502_ramp_b234),
+ MCP16502_REGULATOR("VDD_CORE", BUCK3, pvin3, b234_ranges, mcp16502_buck_ops,
+ mcp16502_ramp_b234),
+ MCP16502_REGULATOR("VDD_OTHER", BUCK4, pvin4, b234_ranges, mcp16502_buck_ops,
+ mcp16502_ramp_b234),
+ MCP16502_REGULATOR("LDO1", LDO1, lvin, b1l12_ranges, mcp16502_ldo_ops,
+ mcp16502_ramp_b1l12),
+ MCP16502_REGULATOR("LDO2", LDO2, lvin, b1l12_ranges, mcp16502_ldo_ops,
+ mcp16502_ramp_b1l12)
};
static const struct regmap_range mcp16502_ranges[] = {
@@ -427,8 +501,7 @@ static const struct regmap_config mcp16502_regmap_config = {
.wr_table = &mcp16502_yes_reg_table,
};
-static int mcp16502_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int mcp16502_probe(struct i2c_client *client)
{
struct regulator_config config = { };
struct regulator_dev *rdev;
@@ -455,7 +528,7 @@ static int mcp16502_probe(struct i2c_client *client,
config.regmap = rmap;
config.driver_data = mcp;
- mcp->lpm = devm_gpiod_get(dev, "lpm", GPIOD_OUT_LOW);
+ mcp->lpm = devm_gpiod_get_optional(dev, "lpm", GPIOD_OUT_LOW);
if (IS_ERR(mcp->lpm)) {
dev_err(dev, "failed to get lpm pin: %ld\n", PTR_ERR(mcp->lpm));
return PTR_ERR(mcp->lpm);
@@ -505,7 +578,7 @@ static const struct dev_pm_ops mcp16502_pm_ops = {
};
#endif
static const struct i2c_device_id mcp16502_i2c_id[] = {
- { "mcp16502", 0 },
+ { "mcp16502" },
{ }
};
MODULE_DEVICE_TABLE(i2c, mcp16502_i2c_id);
@@ -514,7 +587,8 @@ static struct i2c_driver mcp16502_drv = {
.probe = mcp16502_probe,
.driver = {
.name = "mcp16502-regulator",
- .of_match_table = of_match_ptr(mcp16502_ids),
+ .probe_type = PROBE_PREFER_ASYNCHRONOUS,
+ .of_match_table = mcp16502_ids,
#ifdef CONFIG_PM
.pm = &mcp16502_pm_ops,
#endif