diff options
Diffstat (limited to 'drivers/regulator/mcp16502.c')
| -rw-r--r-- | drivers/regulator/mcp16502.c | 146 |
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 |
