diff options
Diffstat (limited to 'drivers/regulator')
24 files changed, 1253 insertions, 231 deletions
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 129359f775d0..144cbf5b3e5a 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -321,6 +321,15 @@ config REGULATOR_LP872X help This driver supports LP8720/LP8725 PMIC +config REGULATOR_LP873X + tristate "TI LP873X Power regulators" + depends on MFD_LP873X && OF + help + This driver supports LP873X voltage regulator chips. LP873X + provides two step-down converters and two general-purpose LDO + voltage regulators. It supports software based voltage control + for different voltage domains + config REGULATOR_LP8755 tristate "TI LP8755 High Performance PMU driver" depends on I2C @@ -549,6 +558,13 @@ config REGULATOR_PV88060 Say y here to support the voltage regulators and convertors PV88060 +config REGULATOR_PV88080 + tristate "Powerventure Semiconductor PV88080 regulator" + depends on I2C + select REGMAP_I2C + help + Say y here to support the buck convertors on PV88080 + config REGULATOR_PV88090 tristate "Powerventure Semiconductor PV88090 regulator" depends on I2C diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 61bfbb9d4a0c..85a1d44a3939 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -42,11 +42,12 @@ obj-$(CONFIG_REGULATOR_LM363X) += lm363x-regulator.o obj-$(CONFIG_REGULATOR_LP3971) += lp3971.o obj-$(CONFIG_REGULATOR_LP3972) += lp3972.o obj-$(CONFIG_REGULATOR_LP872X) += lp872x.o +obj-$(CONFIG_REGULATOR_LP873X) += lp873x-regulator.o obj-$(CONFIG_REGULATOR_LP8788) += lp8788-buck.o obj-$(CONFIG_REGULATOR_LP8788) += lp8788-ldo.o obj-$(CONFIG_REGULATOR_LP8755) += lp8755.o obj-$(CONFIG_REGULATOR_LTC3589) += ltc3589.o -obj-$(CONFIG_REGULATOR_MAX14577) += max14577.o +obj-$(CONFIG_REGULATOR_MAX14577) += max14577-regulator.o obj-$(CONFIG_REGULATOR_MAX1586) += max1586.o obj-$(CONFIG_REGULATOR_MAX77620) += max77620-regulator.o obj-$(CONFIG_REGULATOR_MAX8649) += max8649.o @@ -55,10 +56,10 @@ obj-$(CONFIG_REGULATOR_MAX8907) += max8907-regulator.o obj-$(CONFIG_REGULATOR_MAX8925) += max8925-regulator.o obj-$(CONFIG_REGULATOR_MAX8952) += max8952.o obj-$(CONFIG_REGULATOR_MAX8973) += max8973-regulator.o -obj-$(CONFIG_REGULATOR_MAX8997) += max8997.o +obj-$(CONFIG_REGULATOR_MAX8997) += max8997-regulator.o obj-$(CONFIG_REGULATOR_MAX8998) += max8998.o obj-$(CONFIG_REGULATOR_MAX77686) += max77686-regulator.o -obj-$(CONFIG_REGULATOR_MAX77693) += max77693.o +obj-$(CONFIG_REGULATOR_MAX77693) += max77693-regulator.o obj-$(CONFIG_REGULATOR_MAX77802) += max77802-regulator.o obj-$(CONFIG_REGULATOR_MC13783) += mc13783-regulator.o obj-$(CONFIG_REGULATOR_MC13892) += mc13892-regulator.o @@ -71,6 +72,7 @@ obj-$(CONFIG_REGULATOR_QCOM_SPMI) += qcom_spmi-regulator.o obj-$(CONFIG_REGULATOR_PALMAS) += palmas-regulator.o obj-$(CONFIG_REGULATOR_PFUZE100) += pfuze100-regulator.o obj-$(CONFIG_REGULATOR_PV88060) += pv88060-regulator.o +obj-$(CONFIG_REGULATOR_PV88080) += pv88080-regulator.o obj-$(CONFIG_REGULATOR_PV88090) += pv88090-regulator.o obj-$(CONFIG_REGULATOR_PWM) += pwm-regulator.o obj-$(CONFIG_REGULATOR_TPS51632) += tps51632-regulator.o diff --git a/drivers/regulator/act8865-regulator.c b/drivers/regulator/act8865-regulator.c index 000d566e32a4..a1cd0d4f8257 100644 --- a/drivers/regulator/act8865-regulator.c +++ b/drivers/regulator/act8865-regulator.c @@ -139,6 +139,74 @@ struct act8865 { int off_mask; }; +static const struct regmap_range act8600_reg_ranges[] = { + regmap_reg_range(0x00, 0x01), + regmap_reg_range(0x10, 0x10), + regmap_reg_range(0x12, 0x12), + regmap_reg_range(0x20, 0x20), + regmap_reg_range(0x22, 0x22), + regmap_reg_range(0x30, 0x30), + regmap_reg_range(0x32, 0x32), + regmap_reg_range(0x40, 0x41), + regmap_reg_range(0x50, 0x51), + regmap_reg_range(0x60, 0x61), + regmap_reg_range(0x70, 0x71), + regmap_reg_range(0x80, 0x81), + regmap_reg_range(0x91, 0x91), + regmap_reg_range(0xA1, 0xA1), + regmap_reg_range(0xA8, 0xAA), + regmap_reg_range(0xB0, 0xB0), + regmap_reg_range(0xB2, 0xB2), + regmap_reg_range(0xC1, 0xC1), +}; + +static const struct regmap_range act8600_reg_ro_ranges[] = { + regmap_reg_range(0xAA, 0xAA), + regmap_reg_range(0xC1, 0xC1), +}; + +static const struct regmap_range act8600_reg_volatile_ranges[] = { + regmap_reg_range(0x00, 0x01), + regmap_reg_range(0x12, 0x12), + regmap_reg_range(0x22, 0x22), + regmap_reg_range(0x32, 0x32), + regmap_reg_range(0x41, 0x41), + regmap_reg_range(0x51, 0x51), + regmap_reg_range(0x61, 0x61), + regmap_reg_range(0x71, 0x71), + regmap_reg_range(0x81, 0x81), + regmap_reg_range(0xA8, 0xA8), + regmap_reg_range(0xAA, 0xAA), + regmap_reg_range(0xB0, 0xB0), + regmap_reg_range(0xC1, 0xC1), +}; + +static const struct regmap_access_table act8600_write_ranges_table = { + .yes_ranges = act8600_reg_ranges, + .n_yes_ranges = ARRAY_SIZE(act8600_reg_ranges), + .no_ranges = act8600_reg_ro_ranges, + .n_no_ranges = ARRAY_SIZE(act8600_reg_ro_ranges), +}; + +static const struct regmap_access_table act8600_read_ranges_table = { + .yes_ranges = act8600_reg_ranges, + .n_yes_ranges = ARRAY_SIZE(act8600_reg_ranges), +}; + +static const struct regmap_access_table act8600_volatile_ranges_table = { + .yes_ranges = act8600_reg_volatile_ranges, + .n_yes_ranges = ARRAY_SIZE(act8600_reg_volatile_ranges), +}; + +static const struct regmap_config act8600_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0xFF, + .wr_table = &act8600_write_ranges_table, + .rd_table = &act8600_read_ranges_table, + .volatile_table = &act8600_volatile_ranges_table, +}; + static const struct regmap_config act8865_regmap_config = { .reg_bits = 8, .val_bits = 8, @@ -319,7 +387,6 @@ static struct of_regulator_match act8600_matches[] = { }; static int act8865_pdata_from_dt(struct device *dev, - struct device_node **of_node, struct act8865_platform_data *pdata, unsigned long type) { @@ -370,7 +437,7 @@ static int act8865_pdata_from_dt(struct device *dev, regulator->id = i; regulator->name = matches[i].name; regulator->init_data = matches[i].init_data; - of_node[i] = matches[i].of_node; + regulator->of_node = matches[i].of_node; regulator++; } @@ -378,7 +445,6 @@ static int act8865_pdata_from_dt(struct device *dev, } #else static inline int act8865_pdata_from_dt(struct device *dev, - struct device_node **of_node, struct act8865_platform_data *pdata, unsigned long type) { @@ -386,8 +452,8 @@ static inline int act8865_pdata_from_dt(struct device *dev, } #endif -static struct regulator_init_data -*act8865_get_init_data(int id, struct act8865_platform_data *pdata) +static struct act8865_regulator_data *act8865_get_regulator_data( + int id, struct act8865_platform_data *pdata) { int i; @@ -396,7 +462,7 @@ static struct regulator_init_data for (i = 0; i < pdata->num_regulators; i++) { if (pdata->regulators[i].id == id) - return pdata->regulators[i].init_data; + return &pdata->regulators[i]; } return NULL; @@ -418,9 +484,9 @@ static int act8865_pmic_probe(struct i2c_client *client, const struct regulator_desc *regulators; struct act8865_platform_data pdata_of, *pdata; struct device *dev = &client->dev; - struct device_node **of_node; int i, ret, num_regulators; struct act8865 *act8865; + const struct regmap_config *regmap_config; unsigned long type; int off_reg, off_mask; int voltage_select = 0; @@ -447,12 +513,14 @@ static int act8865_pmic_probe(struct i2c_client *client, case ACT8600: regulators = act8600_regulators; num_regulators = ARRAY_SIZE(act8600_regulators); + regmap_config = &act8600_regmap_config; off_reg = -1; off_mask = -1; break; case ACT8846: regulators = act8846_regulators; num_regulators = ARRAY_SIZE(act8846_regulators); + regmap_config = &act8865_regmap_config; off_reg = ACT8846_GLB_OFF_CTRL; off_mask = ACT8846_OFF_SYSMASK; break; @@ -464,6 +532,7 @@ static int act8865_pmic_probe(struct i2c_client *client, regulators = act8865_regulators; num_regulators = ARRAY_SIZE(act8865_regulators); } + regmap_config = &act8865_regmap_config; off_reg = ACT8865_SYS_CTRL; off_mask = ACT8865_MSTROFF; break; @@ -472,34 +541,22 @@ static int act8865_pmic_probe(struct i2c_client *client, return -EINVAL; } - of_node = devm_kzalloc(dev, sizeof(struct device_node *) * - num_regulators, GFP_KERNEL); - if (!of_node) - return -ENOMEM; - if (dev->of_node && !pdata) { - ret = act8865_pdata_from_dt(dev, of_node, &pdata_of, type); + ret = act8865_pdata_from_dt(dev, &pdata_of, type); if (ret < 0) return ret; pdata = &pdata_of; } - if (pdata->num_regulators > num_regulators) { - dev_err(dev, "too many regulators: %d\n", - pdata->num_regulators); - return -EINVAL; - } - act8865 = devm_kzalloc(dev, sizeof(struct act8865), GFP_KERNEL); if (!act8865) return -ENOMEM; - act8865->regmap = devm_regmap_init_i2c(client, &act8865_regmap_config); + act8865->regmap = devm_regmap_init_i2c(client, regmap_config); if (IS_ERR(act8865->regmap)) { ret = PTR_ERR(act8865->regmap); - dev_err(&client->dev, "Failed to allocate register map: %d\n", - ret); + dev_err(dev, "Failed to allocate register map: %d\n", ret); return ret; } @@ -518,15 +575,20 @@ static int act8865_pmic_probe(struct i2c_client *client, for (i = 0; i < num_regulators; i++) { const struct regulator_desc *desc = ®ulators[i]; struct regulator_config config = { }; + struct act8865_regulator_data *rdata; struct regulator_dev *rdev; config.dev = dev; - config.init_data = act8865_get_init_data(desc->id, pdata); - config.of_node = of_node[i]; config.driver_data = act8865; config.regmap = act8865->regmap; - rdev = devm_regulator_register(&client->dev, desc, &config); + rdata = act8865_get_regulator_data(desc->id, pdata); + if (rdata) { + config.init_data = rdata->init_data; + config.of_node = rdata->of_node; + } + + rdev = devm_regulator_register(dev, desc, &config); if (IS_ERR(rdev)) { dev_err(dev, "failed to register %s\n", desc->name); return PTR_ERR(rdev); @@ -534,7 +596,6 @@ static int act8865_pmic_probe(struct i2c_client *client, } i2c_set_clientdata(client, act8865); - devm_kfree(dev, of_node); return 0; } diff --git a/drivers/regulator/as3722-regulator.c b/drivers/regulator/as3722-regulator.c index 8b046eec6ae0..66337e12719b 100644 --- a/drivers/regulator/as3722-regulator.c +++ b/drivers/regulator/as3722-regulator.c @@ -372,7 +372,7 @@ static int as3722_ldo_set_current_limit(struct regulator_dev *rdev, AS3722_LDO_ILIMIT_MASK, reg); } -static struct regulator_ops as3722_ldo0_ops = { +static const struct regulator_ops as3722_ldo0_ops = { .is_enabled = regulator_is_enabled_regmap, .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, @@ -383,7 +383,7 @@ static struct regulator_ops as3722_ldo0_ops = { .set_current_limit = as3722_ldo_set_current_limit, }; -static struct regulator_ops as3722_ldo0_extcntrl_ops = { +static const struct regulator_ops as3722_ldo0_extcntrl_ops = { .list_voltage = regulator_list_voltage_linear, .get_voltage_sel = regulator_get_voltage_sel_regmap, .set_voltage_sel = regulator_set_voltage_sel_regmap, @@ -415,7 +415,7 @@ static int as3722_ldo3_get_current_limit(struct regulator_dev *rdev) return 150000; } -static struct regulator_ops as3722_ldo3_ops = { +static const struct regulator_ops as3722_ldo3_ops = { .is_enabled = regulator_is_enabled_regmap, .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, @@ -425,20 +425,45 @@ static struct regulator_ops as3722_ldo3_ops = { .get_current_limit = as3722_ldo3_get_current_limit, }; -static struct regulator_ops as3722_ldo3_extcntrl_ops = { +static const struct regulator_ops as3722_ldo3_extcntrl_ops = { .list_voltage = regulator_list_voltage_linear, .get_voltage_sel = regulator_get_voltage_sel_regmap, .set_voltage_sel = regulator_set_voltage_sel_regmap, .get_current_limit = as3722_ldo3_get_current_limit, }; +static const struct regulator_ops as3722_ldo6_ops = { + .is_enabled = regulator_is_enabled_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .map_voltage = regulator_map_voltage_linear_range, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .list_voltage = regulator_list_voltage_linear_range, + .get_current_limit = as3722_ldo_get_current_limit, + .set_current_limit = as3722_ldo_set_current_limit, + .get_bypass = regulator_get_bypass_regmap, + .set_bypass = regulator_set_bypass_regmap, +}; + +static const struct regulator_ops as3722_ldo6_extcntrl_ops = { + .map_voltage = regulator_map_voltage_linear_range, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .list_voltage = regulator_list_voltage_linear_range, + .get_current_limit = as3722_ldo_get_current_limit, + .set_current_limit = as3722_ldo_set_current_limit, + .get_bypass = regulator_get_bypass_regmap, + .set_bypass = regulator_set_bypass_regmap, +}; + static const struct regulator_linear_range as3722_ldo_ranges[] = { REGULATOR_LINEAR_RANGE(0, 0x00, 0x00, 0), REGULATOR_LINEAR_RANGE(825000, 0x01, 0x24, 25000), REGULATOR_LINEAR_RANGE(1725000, 0x40, 0x7F, 25000), }; -static struct regulator_ops as3722_ldo_ops = { +static const struct regulator_ops as3722_ldo_ops = { .is_enabled = regulator_is_enabled_regmap, .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, @@ -450,7 +475,7 @@ static struct regulator_ops as3722_ldo_ops = { .set_current_limit = as3722_ldo_set_current_limit, }; -static struct regulator_ops as3722_ldo_extcntrl_ops = { +static const struct regulator_ops as3722_ldo_extcntrl_ops = { .map_voltage = regulator_map_voltage_linear_range, .set_voltage_sel = regulator_set_voltage_sel_regmap, .get_voltage_sel = regulator_get_voltage_sel_regmap, @@ -616,7 +641,7 @@ static const struct regulator_linear_range as3722_sd2345_ranges[] = { REGULATOR_LINEAR_RANGE(2650000, 0x71, 0x7F, 50000), }; -static struct regulator_ops as3722_sd016_ops = { +static const struct regulator_ops as3722_sd016_ops = { .is_enabled = regulator_is_enabled_regmap, .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, @@ -630,7 +655,7 @@ static struct regulator_ops as3722_sd016_ops = { .set_mode = as3722_sd_set_mode, }; -static struct regulator_ops as3722_sd016_extcntrl_ops = { +static const struct regulator_ops as3722_sd016_extcntrl_ops = { .list_voltage = regulator_list_voltage_linear, .map_voltage = regulator_map_voltage_linear, .get_voltage_sel = regulator_get_voltage_sel_regmap, @@ -641,7 +666,7 @@ static struct regulator_ops as3722_sd016_extcntrl_ops = { .set_mode = as3722_sd_set_mode, }; -static struct regulator_ops as3722_sd2345_ops = { +static const struct regulator_ops as3722_sd2345_ops = { .is_enabled = regulator_is_enabled_regmap, .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, @@ -653,7 +678,7 @@ static struct regulator_ops as3722_sd2345_ops = { .set_mode = as3722_sd_set_mode, }; -static struct regulator_ops as3722_sd2345_extcntrl_ops = { +static const struct regulator_ops as3722_sd2345_extcntrl_ops = { .list_voltage = regulator_list_voltage_linear_range, .map_voltage = regulator_map_voltage_linear_range, .set_voltage_sel = regulator_set_voltage_sel_regmap, @@ -760,7 +785,7 @@ static int as3722_regulator_probe(struct platform_device *pdev) struct as3722_regulator_config_data *reg_config; struct regulator_dev *rdev; struct regulator_config config = { }; - struct regulator_ops *ops; + const struct regulator_ops *ops; int id; int ret; @@ -829,6 +854,24 @@ static int as3722_regulator_probe(struct platform_device *pdev) } } break; + case AS3722_REGULATOR_ID_LDO6: + if (reg_config->ext_control) + ops = &as3722_ldo6_extcntrl_ops; + else + ops = &as3722_ldo6_ops; + as3722_regs->desc[id].enable_time = 500; + as3722_regs->desc[id].bypass_reg = + AS3722_LDO6_VOLTAGE_REG; + as3722_regs->desc[id].bypass_mask = + AS3722_LDO_VSEL_MASK; + as3722_regs->desc[id].bypass_val_on = + AS3722_LDO6_VSEL_BYPASS; + as3722_regs->desc[id].bypass_val_off = + AS3722_LDO6_VSEL_BYPASS; + as3722_regs->desc[id].linear_ranges = as3722_ldo_ranges; + as3722_regs->desc[id].n_linear_ranges = + ARRAY_SIZE(as3722_ldo_ranges); + break; case AS3722_REGULATOR_ID_SD0: case AS3722_REGULATOR_ID_SD1: case AS3722_REGULATOR_ID_SD6: diff --git a/drivers/regulator/axp20x-regulator.c b/drivers/regulator/axp20x-regulator.c index 40cd894e4df5..514a5e8fdbab 100644 --- a/drivers/regulator/axp20x-regulator.c +++ b/drivers/regulator/axp20x-regulator.c @@ -157,7 +157,9 @@ static struct regulator_ops axp20x_ops_sw = { static const struct regulator_linear_range axp20x_ldo4_ranges[] = { REGULATOR_LINEAR_RANGE(1250000, 0x0, 0x0, 0), REGULATOR_LINEAR_RANGE(1300000, 0x1, 0x8, 100000), - REGULATOR_LINEAR_RANGE(2500000, 0x9, 0xf, 100000), + REGULATOR_LINEAR_RANGE(2500000, 0x9, 0x9, 0), + REGULATOR_LINEAR_RANGE(2700000, 0xa, 0xb, 100000), + REGULATOR_LINEAR_RANGE(3000000, 0xc, 0xf, 100000), }; static const struct regulator_desc axp20x_regulators[] = { @@ -215,10 +217,14 @@ static const struct regulator_desc axp22x_regulators[] = { AXP22X_ELDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(1)), AXP_DESC(AXP22X, ELDO3, "eldo3", "eldoin", 700, 3300, 100, AXP22X_ELDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(2)), - AXP_DESC_IO(AXP22X, LDO_IO0, "ldo_io0", "ips", 1800, 3300, 100, + /* Note the datasheet only guarantees reliable operation up to + * 3.3V, this needs to be enforced via dts provided constraints */ + AXP_DESC_IO(AXP22X, LDO_IO0, "ldo_io0", "ips", 700, 3800, 100, AXP22X_LDO_IO0_V_OUT, 0x1f, AXP20X_GPIO0_CTRL, 0x07, AXP22X_IO_ENABLED, AXP22X_IO_DISABLED), - AXP_DESC_IO(AXP22X, LDO_IO1, "ldo_io1", "ips", 1800, 3300, 100, + /* Note the datasheet only guarantees reliable operation up to + * 3.3V, this needs to be enforced via dts provided constraints */ + AXP_DESC_IO(AXP22X, LDO_IO1, "ldo_io1", "ips", 700, 3800, 100, AXP22X_LDO_IO1_V_OUT, 0x1f, AXP20X_GPIO1_CTRL, 0x07, AXP22X_IO_ENABLED, AXP22X_IO_DISABLED), AXP_DESC_FIXED(AXP22X, RTC_LDO, "rtc_ldo", "ips", 3000), diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index e0b764284773..ec8184d53f13 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -132,6 +132,19 @@ static bool have_full_constraints(void) return has_full_constraints || of_have_populated_dt(); } +static bool regulator_ops_is_valid(struct regulator_dev *rdev, int ops) +{ + if (!rdev->constraints) { + rdev_err(rdev, "no constraints\n"); + return false; + } + + if (rdev->constraints->valid_ops_mask & ops) + return true; + + return false; +} + static inline struct regulator_dev *rdev_get_supply(struct regulator_dev *rdev) { if (rdev && rdev->supply) @@ -198,28 +211,13 @@ static struct device_node *of_get_regulator(struct device *dev, const char *supp return regnode; } -static int _regulator_can_change_status(struct regulator_dev *rdev) -{ - if (!rdev->constraints) - return 0; - - if (rdev->constraints->valid_ops_mask & REGULATOR_CHANGE_STATUS) - return 1; - else - return 0; -} - /* Platform voltage constraint check */ static int regulator_check_voltage(struct regulator_dev *rdev, int *min_uV, int *max_uV) { BUG_ON(*min_uV > *max_uV); - if (!rdev->constraints) { - rdev_err(rdev, "no constraints\n"); - return -ENODEV; - } - if (!(rdev->constraints->valid_ops_mask & REGULATOR_CHANGE_VOLTAGE)) { + if (!regulator_ops_is_valid(rdev, REGULATOR_CHANGE_VOLTAGE)) { rdev_err(rdev, "voltage operation not allowed\n"); return -EPERM; } @@ -275,11 +273,7 @@ static int regulator_check_current_limit(struct regulator_dev *rdev, { BUG_ON(*min_uA > *max_uA); - if (!rdev->constraints) { - rdev_err(rdev, "no constraints\n"); - return -ENODEV; - } - if (!(rdev->constraints->valid_ops_mask & REGULATOR_CHANGE_CURRENT)) { + if (!regulator_ops_is_valid(rdev, REGULATOR_CHANGE_CURRENT)) { rdev_err(rdev, "current operation not allowed\n"); return -EPERM; } @@ -312,11 +306,7 @@ static int regulator_mode_constrain(struct regulator_dev *rdev, int *mode) return -EINVAL; } - if (!rdev->constraints) { - rdev_err(rdev, "no constraints\n"); - return -ENODEV; - } - if (!(rdev->constraints->valid_ops_mask & REGULATOR_CHANGE_MODE)) { + if (!regulator_ops_is_valid(rdev, REGULATOR_CHANGE_MODE)) { rdev_err(rdev, "mode operation not allowed\n"); return -EPERM; } @@ -333,20 +323,6 @@ static int regulator_mode_constrain(struct regulator_dev *rdev, int *mode) return -EINVAL; } -/* dynamic regulator mode switching constraint check */ -static int regulator_check_drms(struct regulator_dev *rdev) -{ - if (!rdev->constraints) { - rdev_err(rdev, "no constraints\n"); - return -ENODEV; - } - if (!(rdev->constraints->valid_ops_mask & REGULATOR_CHANGE_DRMS)) { - rdev_dbg(rdev, "drms operation not allowed\n"); - return -EPERM; - } - return 0; -} - static ssize_t regulator_uV_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -692,8 +668,7 @@ static int drms_uA_update(struct regulator_dev *rdev) * first check to see if we can set modes at all, otherwise just * tell the consumer everything is OK. */ - err = regulator_check_drms(rdev); - if (err < 0) + if (!regulator_ops_is_valid(rdev, REGULATOR_CHANGE_DRMS)) return 0; if (!rdev->desc->ops->get_optimum_mode && @@ -808,8 +783,6 @@ static int suspend_set_state(struct regulator_dev *rdev, /* locks held by caller */ static int suspend_prepare(struct regulator_dev *rdev, suspend_state_t state) { - lockdep_assert_held_once(&rdev->mutex); - if (!rdev->constraints) return -EINVAL; @@ -893,7 +866,7 @@ static void print_constraints(struct regulator_dev *rdev) rdev_dbg(rdev, "%s\n", buf); if ((constraints->min_uV != constraints->max_uV) && - !(constraints->valid_ops_mask & REGULATOR_CHANGE_VOLTAGE)) + !regulator_ops_is_valid(rdev, REGULATOR_CHANGE_VOLTAGE)) rdev_warn(rdev, "Voltage range but no REGULATOR_CHANGE_VOLTAGE\n"); } @@ -906,7 +879,8 @@ static int machine_constraints_voltage(struct regulator_dev *rdev, /* do we need to apply the constraint voltage */ if (rdev->constraints->apply_uV && - rdev->constraints->min_uV == rdev->constraints->max_uV) { + rdev->constraints->min_uV && rdev->constraints->max_uV) { + int target_min, target_max; int current_uV = _regulator_get_voltage(rdev); if (current_uV < 0) { rdev_err(rdev, @@ -914,15 +888,34 @@ static int machine_constraints_voltage(struct regulator_dev *rdev, current_uV); return current_uV; } - if (current_uV < rdev->constraints->min_uV || - current_uV > rdev->constraints->max_uV) { + + /* + * If we're below the minimum voltage move up to the + * minimum voltage, if we're above the maximum voltage + * then move down to the maximum. + */ + target_min = current_uV; + target_max = current_uV; + + if (current_uV < rdev->constraints->min_uV) { + target_min = rdev->constraints->min_uV; + target_max = rdev->constraints->min_uV; + } + + if (current_uV > rdev->constraints->max_uV) { + target_min = rdev->constraints->max_uV; + target_max = rdev->constraints->max_uV; + } + + if (target_min != current_uV || target_max != current_uV) { + rdev_info(rdev, "Bringing %duV into %d-%duV\n", + current_uV, target_min, target_max); ret = _regulator_do_set_voltage( - rdev, rdev->constraints->min_uV, - rdev->constraints->max_uV); + rdev, target_min, target_max); if (ret < 0) { rdev_err(rdev, - "failed to apply %duV constraint(%d)\n", - rdev->constraints->min_uV, ret); + "failed to apply %d-%duV constraint(%d)\n", + target_min, target_max, ret); return ret; } } @@ -1150,17 +1143,6 @@ static int set_machine_constraints(struct regulator_dev *rdev, } } - if (rdev->constraints->active_discharge && ops->set_active_discharge) { - bool ad_state = (rdev->constraints->active_discharge == - REGULATOR_ACTIVE_DISCHARGE_ENABLE) ? true : false; - - ret = ops->set_active_discharge(rdev, ad_state); - if (ret < 0) { - rdev_err(rdev, "failed to set active discharge\n"); - return ret; - } - } - print_constraints(rdev); return 0; } @@ -1272,6 +1254,55 @@ static void unset_regulator_supplies(struct regulator_dev *rdev) } } +#ifdef CONFIG_DEBUG_FS +static ssize_t constraint_flags_read_file(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + const struct regulator *regulator = file->private_data; + const struct regulation_constraints *c = regulator->rdev->constraints; + char *buf; + ssize_t ret; + + if (!c) + return 0; + + buf = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ret = snprintf(buf, PAGE_SIZE, + "always_on: %u\n" + "boot_on: %u\n" + "apply_uV: %u\n" + "ramp_disable: %u\n" + "soft_start: %u\n" + "pull_down: %u\n" + "over_current_protection: %u\n", + c->always_on, + c->boot_on, + c->apply_uV, + c->ramp_disable, + c->soft_start, + c->pull_down, + c->over_current_protection); + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret); + kfree(buf); + + return ret; +} + +#endif + +static const struct file_operations constraint_flags_fops = { +#ifdef CONFIG_DEBUG_FS + .open = simple_open, + .read = constraint_flags_read_file, + .llseek = default_llseek, +#endif +}; + #define REG_STR_SIZE 64 static struct regulator *create_regulator(struct regulator_dev *rdev, @@ -1327,6 +1358,9 @@ static struct regulator *create_regulator(struct regulator_dev *rdev, ®ulator->min_uV); debugfs_create_u32("max_uV", 0444, regulator->debugfs, ®ulator->max_uV); + debugfs_create_file("constraint_flags", 0444, + regulator->debugfs, regulator, + &constraint_flags_fops); } /* @@ -1334,7 +1368,7 @@ static struct regulator *create_regulator(struct regulator_dev *rdev, * it is then we don't need to do nearly so much work for * enable/disable calls. */ - if (!_regulator_can_change_status(rdev) && + if (!regulator_ops_is_valid(rdev, REGULATOR_CHANGE_STATUS) && _regulator_is_enabled(rdev)) regulator->always_on = true; @@ -1532,10 +1566,11 @@ static int regulator_resolve_supply(struct regulator_dev *rdev) } /* Cascade always-on state to supply */ - if (_regulator_is_enabled(rdev) && rdev->supply) { + if (_regulator_is_enabled(rdev)) { ret = regulator_enable(rdev->supply); if (ret < 0) { _regulator_put(rdev->supply); + rdev->supply = NULL; return ret; } } @@ -2111,15 +2146,15 @@ static int _regulator_enable(struct regulator_dev *rdev) lockdep_assert_held_once(&rdev->mutex); /* check voltage and requested load before enabling */ - if (rdev->constraints && - (rdev->constraints->valid_ops_mask & REGULATOR_CHANGE_DRMS)) + if (regulator_ops_is_valid(rdev, REGULATOR_CHANGE_DRMS)) drms_uA_update(rdev); if (rdev->use_count == 0) { /* The regulator may on if it's not switchable or left on */ ret = _regulator_is_enabled(rdev); if (ret == -EINVAL || ret == 0) { - if (!_regulator_can_change_status(rdev)) + if (!regulator_ops_is_valid(rdev, + REGULATOR_CHANGE_STATUS)) return -EPERM; ret = _regulator_do_enable(rdev); @@ -2221,7 +2256,7 @@ static int _regulator_disable(struct regulator_dev *rdev) (rdev->constraints && !rdev->constraints->always_on)) { /* we are last user */ - if (_regulator_can_change_status(rdev)) { + if (regulator_ops_is_valid(rdev, REGULATOR_CHANGE_STATUS)) { ret = _notifier_call_chain(rdev, REGULATOR_EVENT_PRE_DISABLE, NULL); @@ -2242,10 +2277,7 @@ static int _regulator_disable(struct regulator_dev *rdev) rdev->use_count = 0; } else if (rdev->use_count > 1) { - - if (rdev->constraints && - (rdev->constraints->valid_ops_mask & - REGULATOR_CHANGE_DRMS)) + if (regulator_ops_is_valid(rdev, REGULATOR_CHANGE_DRMS)) drms_uA_update(rdev); rdev->use_count--; @@ -2489,8 +2521,7 @@ int regulator_can_change_voltage(struct regulator *regulator) { struct regulator_dev *rdev = regulator->rdev; - if (rdev->constraints && - (rdev->constraints->valid_ops_mask & REGULATOR_CHANGE_VOLTAGE)) { + if (regulator_ops_is_valid(rdev, REGULATOR_CHANGE_VOLTAGE)) { if (rdev->desc->n_voltages - rdev->desc->linear_min_sel > 1) return 1; @@ -2644,7 +2675,7 @@ int regulator_is_supported_voltage(struct regulator *regulator, int i, voltages, ret; /* If we can't change voltage check the current voltage */ - if (!(rdev->constraints->valid_ops_mask & REGULATOR_CHANGE_VOLTAGE)) { + if (!regulator_ops_is_valid(rdev, REGULATOR_CHANGE_VOLTAGE)) { ret = regulator_get_voltage(regulator); if (ret >= 0) return min_uV <= ret && ret <= max_uV; @@ -2850,7 +2881,7 @@ static int regulator_set_voltage_unlocked(struct regulator *regulator, * return successfully even though the regulator does not support * changing the voltage. */ - if (!(rdev->constraints->valid_ops_mask & REGULATOR_CHANGE_VOLTAGE)) { + if (!regulator_ops_is_valid(rdev, REGULATOR_CHANGE_VOLTAGE)) { current_uV = _regulator_get_voltage(rdev); if (min_uV <= current_uV && current_uV <= max_uV) { regulator->min_uV = min_uV; @@ -3109,6 +3140,23 @@ EXPORT_SYMBOL_GPL(regulator_sync_voltage); static int _regulator_get_voltage(struct regulator_dev *rdev) { int sel, ret; + bool bypassed; + + if (rdev->desc->ops->get_bypass) { + ret = rdev->desc->ops->get_bypass(rdev, &bypassed); + if (ret < 0) + return ret; + if (bypassed) { + /* if bypassed the regulator must have a supply */ + if (!rdev->supply) { + rdev_err(rdev, + "bypassed regulator has no supply!\n"); + return -EPROBE_DEFER; + } + + return _regulator_get_voltage(rdev->supply->rdev); + } + } if (rdev->desc->ops->get_voltage_sel) { sel = rdev->desc->ops->get_voltage_sel(rdev); @@ -3365,8 +3413,7 @@ int regulator_allow_bypass(struct regulator *regulator, bool enable) if (!rdev->desc->ops->set_bypass) return 0; - if (rdev->constraints && - !(rdev->constraints->valid_ops_mask & REGULATOR_CHANGE_BYPASS)) + if (!regulator_ops_is_valid(rdev, REGULATOR_CHANGE_BYPASS)) return 0; mutex_lock(&rdev->mutex); @@ -3840,6 +3887,16 @@ static void rdev_init_debugfs(struct regulator_dev *rdev) &rdev->bypass_count); } +static int regulator_register_resolve_supply(struct device *dev, void *data) +{ + struct regulator_dev *rdev = dev_to_rdev(dev); + + if (regulator_resolve_supply(rdev)) + rdev_dbg(rdev, "unable to resolve supply\n"); + + return 0; +} + /** * regulator_register - register regulator * @regulator_desc: regulator to register @@ -3911,8 +3968,6 @@ regulator_register(const struct regulator_desc *regulator_desc, rdev->dev.of_node = of_node_get(config->of_node); } - mutex_lock(®ulator_list_mutex); - mutex_init(&rdev->mutex); rdev->reg_data = config->driver_data; rdev->owner = regulator_desc->owner; @@ -3937,7 +3992,9 @@ regulator_register(const struct regulator_desc *regulator_desc, if ((config->ena_gpio || config->ena_gpio_initialized) && gpio_is_valid(config->ena_gpio)) { + mutex_lock(®ulator_list_mutex); ret = regulator_ena_gpio_request(rdev, config); + mutex_unlock(®ulator_list_mutex); if (ret != 0) { rdev_err(rdev, "Failed to request enable GPIO%d: %d\n", config->ena_gpio, ret); @@ -3950,63 +4007,73 @@ regulator_register(const struct regulator_desc *regulator_desc, rdev->dev.parent = dev; dev_set_name(&rdev->dev, "regulator.%lu", (unsigned long) atomic_inc_return(®ulator_no)); - ret = device_register(&rdev->dev); - if (ret != 0) { - put_device(&rdev->dev); - goto wash; - } - - dev_set_drvdata(&rdev->dev, rdev); /* set regulator constraints */ if (init_data) constraints = &init_data->constraints; - ret = set_machine_constraints(rdev, constraints); - if (ret < 0) - goto scrub; - if (init_data && init_data->supply_regulator) rdev->supply_name = init_data->supply_regulator; else if (regulator_desc->supply_name) rdev->supply_name = regulator_desc->supply_name; + /* + * Attempt to resolve the regulator supply, if specified, + * but don't return an error if we fail because we will try + * to resolve it again later as more regulators are added. + */ + if (regulator_resolve_supply(rdev)) + rdev_dbg(rdev, "unable to resolve supply\n"); + + ret = set_machine_constraints(rdev, constraints); + if (ret < 0) + goto wash; + /* add consumers devices */ if (init_data) { + mutex_lock(®ulator_list_mutex); for (i = 0; i < init_data->num_consumer_supplies; i++) { ret = set_consumer_device_supply(rdev, init_data->consumer_supplies[i].dev_name, init_data->consumer_supplies[i].supply); if (ret < 0) { + mutex_unlock(®ulator_list_mutex); dev_err(dev, "Failed to set supply %s\n", init_data->consumer_supplies[i].supply); goto unset_supplies; } } + mutex_unlock(®ulator_list_mutex); + } + + ret = device_register(&rdev->dev); + if (ret != 0) { + put_device(&rdev->dev); + goto unset_supplies; } + dev_set_drvdata(&rdev->dev, rdev); rdev_init_debugfs(rdev); -out: - mutex_unlock(®ulator_list_mutex); + + /* try to resolve regulators supply since a new one was registered */ + class_for_each_device(®ulator_class, NULL, NULL, + regulator_register_resolve_supply); kfree(config); return rdev; unset_supplies: + mutex_lock(®ulator_list_mutex); unset_regulator_supplies(rdev); - -scrub: - regulator_ena_gpio_free(rdev); - device_unregister(&rdev->dev); - /* device core frees rdev */ - rdev = ERR_PTR(ret); - goto out; - + mutex_unlock(®ulator_list_mutex); wash: + kfree(rdev->constraints); + mutex_lock(®ulator_list_mutex); regulator_ena_gpio_free(rdev); + mutex_unlock(®ulator_list_mutex); clean: kfree(rdev); - rdev = ERR_PTR(ret); - goto out; + kfree(config); + return ERR_PTR(ret); } EXPORT_SYMBOL_GPL(regulator_register); @@ -4032,8 +4099,8 @@ void regulator_unregister(struct regulator_dev *rdev) WARN_ON(rdev->open_count); unset_regulator_supplies(rdev); list_del(&rdev->list); - mutex_unlock(®ulator_list_mutex); regulator_ena_gpio_free(rdev); + mutex_unlock(®ulator_list_mutex); device_unregister(&rdev->dev); } EXPORT_SYMBOL_GPL(regulator_unregister); @@ -4386,7 +4453,7 @@ static int __init regulator_late_cleanup(struct device *dev, void *data) if (c && c->always_on) return 0; - if (c && !(c->valid_ops_mask & REGULATOR_CHANGE_STATUS)) + if (!regulator_ops_is_valid(rdev, REGULATOR_CHANGE_STATUS)) return 0; mutex_lock(&rdev->mutex); diff --git a/drivers/regulator/da9063-regulator.c b/drivers/regulator/da9063-regulator.c index ed9e7e96f877..c6af343f54ea 100644 --- a/drivers/regulator/da9063-regulator.c +++ b/drivers/regulator/da9063-regulator.c @@ -900,4 +900,4 @@ module_exit(da9063_regulator_cleanup); MODULE_AUTHOR("Krystian Garbaciak <krystian.garbaciak@diasemi.com>"); MODULE_DESCRIPTION("DA9063 regulators driver"); MODULE_LICENSE("GPL"); -MODULE_ALIAS("paltform:" DA9063_DRVNAME_REGULATORS); +MODULE_ALIAS("platform:" DA9063_DRVNAME_REGULATORS); diff --git a/drivers/regulator/fan53555.c b/drivers/regulator/fan53555.c index 2cb5cc311610..d7da81a875cf 100644 --- a/drivers/regulator/fan53555.c +++ b/drivers/regulator/fan53555.c @@ -65,6 +65,13 @@ enum { FAN53555_CHIP_ID_03, FAN53555_CHIP_ID_04, FAN53555_CHIP_ID_05, + FAN53555_CHIP_ID_08 = 8, +}; + +/* IC mask revision */ +enum { + FAN53555_CHIP_REV_00 = 0x3, + FAN53555_CHIP_REV_13 = 0xf, }; enum { @@ -217,9 +224,26 @@ static int fan53555_voltages_setup_fairchild(struct fan53555_device_info *di) /* Init voltage range and step */ switch (di->chip_id) { case FAN53555_CHIP_ID_00: + switch (di->chip_rev) { + case FAN53555_CHIP_REV_00: + di->vsel_min = 600000; + di->vsel_step = 10000; + break; + case FAN53555_CHIP_REV_13: + di->vsel_min = 800000; + di->vsel_step = 10000; + break; + default: + dev_err(di->dev, + "Chip ID %d with rev %d not supported!\n", + di->chip_id, di->chip_rev); + return -EINVAL; + } + break; case FAN53555_CHIP_ID_01: case FAN53555_CHIP_ID_03: case FAN53555_CHIP_ID_05: + case FAN53555_CHIP_ID_08: di->vsel_min = 600000; di->vsel_step = 10000; break; diff --git a/drivers/regulator/gpio-regulator.c b/drivers/regulator/gpio-regulator.c index a8718e98674a..83e89e5d4752 100644 --- a/drivers/regulator/gpio-regulator.c +++ b/drivers/regulator/gpio-regulator.c @@ -162,6 +162,8 @@ of_get_gpio_regulator_config(struct device *dev, struct device_node *np, of_property_read_u32(np, "startup-delay-us", &config->startup_delay); config->enable_gpio = of_get_named_gpio(np, "enable-gpio", 0); + if (config->enable_gpio == -EPROBE_DEFER) + return ERR_PTR(-EPROBE_DEFER); /* Fetch GPIOs. - optional property*/ ret = of_gpio_count(np); diff --git a/drivers/regulator/helpers.c b/drivers/regulator/helpers.c index b1e32e7482e9..bcf38fd5106a 100644 --- a/drivers/regulator/helpers.c +++ b/drivers/regulator/helpers.c @@ -460,7 +460,7 @@ int regulator_get_bypass_regmap(struct regulator_dev *rdev, bool *enable) if (ret != 0) return ret; - *enable = val & rdev->desc->bypass_mask; + *enable = (val & rdev->desc->bypass_mask) == rdev->desc->bypass_val_on; return 0; } diff --git a/drivers/regulator/lp3971.c b/drivers/regulator/lp3971.c index 15c25c622edf..204b5c5270e0 100644 --- a/drivers/regulator/lp3971.c +++ b/drivers/regulator/lp3971.c @@ -365,8 +365,8 @@ static int lp3971_set_bits(struct lp3971 *lp3971, u8 reg, u16 mask, u16 val) mutex_lock(&lp3971->io_lock); ret = lp3971_i2c_read(lp3971->i2c, reg, 1, &tmp); - tmp = (tmp & ~mask) | val; if (ret == 0) { + tmp = (tmp & ~mask) | val; ret = lp3971_i2c_write(lp3971->i2c, reg, 1, &tmp); dev_dbg(lp3971->dev, "reg write 0x%02x -> 0x%02x\n", (int)reg, (unsigned)val&0xff); diff --git a/drivers/regulator/lp3972.c b/drivers/regulator/lp3972.c index 3a7e96e2c7b3..ff0c275f902e 100644 --- a/drivers/regulator/lp3972.c +++ b/drivers/regulator/lp3972.c @@ -211,8 +211,8 @@ static int lp3972_set_bits(struct lp3972 *lp3972, u8 reg, u16 mask, u16 val) mutex_lock(&lp3972->io_lock); ret = lp3972_i2c_read(lp3972->i2c, reg, 1, &tmp); - tmp = (tmp & ~mask) | val; if (ret == 0) { + tmp = (tmp & ~mask) | val; ret = lp3972_i2c_write(lp3972->i2c, reg, 1, &tmp); dev_dbg(lp3972->dev, "reg write 0x%02x -> 0x%02x\n", (int)reg, (unsigned)val & 0xff); diff --git a/drivers/regulator/lp873x-regulator.c b/drivers/regulator/lp873x-regulator.c new file mode 100644 index 000000000000..b4ffd113ba21 --- /dev/null +++ b/drivers/regulator/lp873x-regulator.c @@ -0,0 +1,241 @@ +/* + * Regulator driver for LP873X PMIC + * + * Copyright (C) 2016 Texas Instruments Incorporated - http://www.ti.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 "as is" WITHOUT ANY WARRANTY of any + * kind, whether expressed or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License version 2 for more details. + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> + +#include <linux/mfd/lp873x.h> + +#define LP873X_REGULATOR(_name, _id, _of, _ops, _n, _vr, _vm, _er, _em, \ + _delay, _lr, _nlr, _cr) \ + [_id] = { \ + .desc = { \ + .name = _name, \ + .id = _id, \ + .of_match = of_match_ptr(_of), \ + .regulators_node = of_match_ptr("regulators"),\ + .ops = &_ops, \ + .n_voltages = _n, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .vsel_reg = _vr, \ + .vsel_mask = _vm, \ + .enable_reg = _er, \ + .enable_mask = _em, \ + .ramp_delay = _delay, \ + .linear_ranges = _lr, \ + .n_linear_ranges = _nlr, \ + }, \ + .ctrl2_reg = _cr, \ + } + +struct lp873x_regulator { + struct regulator_desc desc; + unsigned int ctrl2_reg; +}; + +static const struct lp873x_regulator regulators[]; + +static const struct regulator_linear_range buck0_buck1_ranges[] = { + REGULATOR_LINEAR_RANGE(0, 0x0, 0x13, 0), + REGULATOR_LINEAR_RANGE(700000, 0x14, 0x17, 10000), + REGULATOR_LINEAR_RANGE(735000, 0x18, 0x9d, 5000), + REGULATOR_LINEAR_RANGE(1420000, 0x9e, 0xff, 20000), +}; + +static const struct regulator_linear_range ldo0_ldo1_ranges[] = { + REGULATOR_LINEAR_RANGE(800000, 0x0, 0x19, 100000), +}; + +static unsigned int lp873x_buck_ramp_delay[] = { + 30000, 15000, 10000, 7500, 3800, 1900, 940, 470 +}; + +/* LP873X BUCK current limit */ +static const unsigned int lp873x_buck_uA[] = { + 1500000, 2000000, 2500000, 3000000, 3500000, 4000000, +}; + +static int lp873x_buck_set_ramp_delay(struct regulator_dev *rdev, + int ramp_delay) +{ + int id = rdev_get_id(rdev); + struct lp873x *lp873 = rdev_get_drvdata(rdev); + unsigned int reg; + int ret; + + if (ramp_delay <= 470) + reg = 7; + else if (ramp_delay <= 940) + reg = 6; + else if (ramp_delay <= 1900) + reg = 5; + else if (ramp_delay <= 3800) + reg = 4; + else if (ramp_delay <= 7500) + reg = 3; + else if (ramp_delay <= 10000) + reg = 2; + else if (ramp_delay <= 15000) + reg = 1; + else + reg = 0; + + ret = regmap_update_bits(lp873->regmap, regulators[id].ctrl2_reg, + LP873X_BUCK0_CTRL_2_BUCK0_SLEW_RATE, + reg << __ffs(LP873X_BUCK0_CTRL_2_BUCK0_SLEW_RATE)); + if (ret) { + dev_err(lp873->dev, "SLEW RATE write failed: %d\n", ret); + return ret; + } + + rdev->constraints->ramp_delay = lp873x_buck_ramp_delay[reg]; + + return 0; +} + +static int lp873x_buck_set_current_limit(struct regulator_dev *rdev, + int min_uA, int max_uA) +{ + int id = rdev_get_id(rdev); + struct lp873x *lp873 = rdev_get_drvdata(rdev); + int i; + + for (i = ARRAY_SIZE(lp873x_buck_uA) - 1; i >= 0; i--) { + if (lp873x_buck_uA[i] >= min_uA && + lp873x_buck_uA[i] <= max_uA) + return regmap_update_bits(lp873->regmap, + regulators[id].ctrl2_reg, + LP873X_BUCK0_CTRL_2_BUCK0_ILIM, + i << __ffs(LP873X_BUCK0_CTRL_2_BUCK0_ILIM)); + } + + return -EINVAL; +} + +static int lp873x_buck_get_current_limit(struct regulator_dev *rdev) +{ + int id = rdev_get_id(rdev); + struct lp873x *lp873 = rdev_get_drvdata(rdev); + int ret; + unsigned int val; + + ret = regmap_read(lp873->regmap, regulators[id].ctrl2_reg, &val); + if (ret) + return ret; + + val = (val & LP873X_BUCK0_CTRL_2_BUCK0_ILIM) >> + __ffs(LP873X_BUCK0_CTRL_2_BUCK0_ILIM); + + return (val < ARRAY_SIZE(lp873x_buck_uA)) ? + lp873x_buck_uA[val] : -EINVAL; +} + +/* Operations permitted on BUCK0, BUCK1 */ +static struct regulator_ops lp873x_buck01_ops = { + .is_enabled = regulator_is_enabled_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .list_voltage = regulator_list_voltage_linear_range, + .map_voltage = regulator_map_voltage_linear_range, + .set_voltage_time_sel = regulator_set_voltage_time_sel, + .set_ramp_delay = lp873x_buck_set_ramp_delay, + .set_current_limit = lp873x_buck_set_current_limit, + .get_current_limit = lp873x_buck_get_current_limit, +}; + +/* Operations permitted on LDO0 and LDO1 */ +static struct regulator_ops lp873x_ldo01_ops = { + .is_enabled = regulator_is_enabled_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .list_voltage = regulator_list_voltage_linear_range, + .map_voltage = regulator_map_voltage_linear_range, +}; + +static const struct lp873x_regulator regulators[] = { + LP873X_REGULATOR("BUCK0", LP873X_BUCK_0, "buck0", lp873x_buck01_ops, + 256, LP873X_REG_BUCK0_VOUT, + LP873X_BUCK0_VOUT_BUCK0_VSET, LP873X_REG_BUCK0_CTRL_1, + LP873X_BUCK0_CTRL_1_BUCK0_EN, 10000, + buck0_buck1_ranges, 4, LP873X_REG_BUCK0_CTRL_2), + LP873X_REGULATOR("BUCK1", LP873X_BUCK_1, "buck1", lp873x_buck01_ops, + 256, LP873X_REG_BUCK1_VOUT, + LP873X_BUCK1_VOUT_BUCK1_VSET, LP873X_REG_BUCK1_CTRL_1, + LP873X_BUCK1_CTRL_1_BUCK1_EN, 10000, + buck0_buck1_ranges, 4, LP873X_REG_BUCK1_CTRL_2), + LP873X_REGULATOR("LDO0", LP873X_LDO_0, "ldo0", lp873x_ldo01_ops, 26, + LP873X_REG_LDO0_VOUT, LP873X_LDO0_VOUT_LDO0_VSET, + LP873X_REG_LDO0_CTRL, + LP873X_LDO0_CTRL_LDO0_EN, 0, ldo0_ldo1_ranges, 1, + 0xFF), + LP873X_REGULATOR("LDO1", LP873X_LDO_1, "ldo1", lp873x_ldo01_ops, 26, + LP873X_REG_LDO1_VOUT, LP873X_LDO1_VOUT_LDO1_VSET, + LP873X_REG_LDO1_CTRL, + LP873X_LDO1_CTRL_LDO1_EN, 0, ldo0_ldo1_ranges, 1, + 0xFF), +}; + +static int lp873x_regulator_probe(struct platform_device *pdev) +{ + struct lp873x *lp873 = dev_get_drvdata(pdev->dev.parent); + struct regulator_config config = { }; + struct regulator_dev *rdev; + int i; + + platform_set_drvdata(pdev, lp873); + + config.dev = &pdev->dev; + config.dev->of_node = lp873->dev->of_node; + config.driver_data = lp873; + config.regmap = lp873->regmap; + + for (i = 0; i < ARRAY_SIZE(regulators); i++) { + rdev = devm_regulator_register(&pdev->dev, ®ulators[i].desc, + &config); + if (IS_ERR(rdev)) { + dev_err(lp873->dev, "failed to register %s regulator\n", + pdev->name); + return PTR_ERR(rdev); + } + } + + return 0; +} + +static const struct platform_device_id lp873x_regulator_id_table[] = { + { "lp873x-regulator", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(platform, lp873x_regulator_id_table); + +static struct platform_driver lp873x_regulator_driver = { + .driver = { + .name = "lp873x-pmic", + }, + .probe = lp873x_regulator_probe, + .id_table = lp873x_regulator_id_table, +}; +module_platform_driver(lp873x_regulator_driver); + +MODULE_AUTHOR("J Keerthy <j-keerthy@ti.com>"); +MODULE_DESCRIPTION("LP873X voltage regulator driver"); +MODULE_ALIAS("platform:lp873x-pmic"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/regulator/max14577.c b/drivers/regulator/max14577-regulator.c index b2daa6641417..b2daa6641417 100644 --- a/drivers/regulator/max14577.c +++ b/drivers/regulator/max14577-regulator.c diff --git a/drivers/regulator/max77620-regulator.c b/drivers/regulator/max77620-regulator.c index 73a3356a5c19..321e804aeab0 100644 --- a/drivers/regulator/max77620-regulator.c +++ b/drivers/regulator/max77620-regulator.c @@ -81,6 +81,7 @@ struct max77620_regulator_pdata { int suspend_fps_pd_slot; int suspend_fps_pu_slot; int current_mode; + int ramp_rate_setting; }; struct max77620_regulator { @@ -307,6 +308,43 @@ static int max77620_read_slew_rate(struct max77620_regulator *pmic, int id) return 0; } +static int max77620_set_slew_rate(struct max77620_regulator *pmic, int id, + int slew_rate) +{ + struct max77620_regulator_info *rinfo = pmic->rinfo[id]; + unsigned int val; + int ret; + u8 mask; + + if (rinfo->type == MAX77620_REGULATOR_TYPE_SD) { + if (slew_rate <= 13750) + val = 0; + else if (slew_rate <= 27500) + val = 1; + else if (slew_rate <= 55000) + val = 2; + else + val = 3; + val <<= MAX77620_SD_SR_SHIFT; + mask = MAX77620_SD_SR_MASK; + } else { + if (slew_rate <= 5000) + val = 1; + else + val = 0; + mask = MAX77620_LDO_SLEW_RATE_MASK; + } + + ret = regmap_update_bits(pmic->rmap, rinfo->cfg_addr, mask, val); + if (ret < 0) { + dev_err(pmic->dev, "Regulator %d slew rate set failed: %d\n", + id, ret); + return ret; + } + + return 0; +} + static int max77620_init_pmic(struct max77620_regulator *pmic, int id) { struct max77620_regulator_pdata *rpdata = &pmic->reg_pdata[id]; @@ -351,6 +389,13 @@ static int max77620_init_pmic(struct max77620_regulator *pmic, int id) if (ret < 0) return ret; + if (rpdata->ramp_rate_setting) { + ret = max77620_set_slew_rate(pmic, id, + rpdata->ramp_rate_setting); + if (ret < 0) + return ret; + } + return 0; } @@ -502,35 +547,16 @@ static int max77620_regulator_set_ramp_delay(struct regulator_dev *rdev, { struct max77620_regulator *pmic = rdev_get_drvdata(rdev); int id = rdev_get_id(rdev); - struct max77620_regulator_info *rinfo = pmic->rinfo[id]; - int ret, val; - u8 mask; - - if (rinfo->type == MAX77620_REGULATOR_TYPE_SD) { - if (ramp_delay <= 13750) - val = 0; - else if (ramp_delay <= 27500) - val = 1; - else if (ramp_delay <= 55000) - val = 2; - else - val = 3; - val <<= MAX77620_SD_SR_SHIFT; - mask = MAX77620_SD_SR_MASK; - } else { - if (ramp_delay <= 5000) - val = 1; - else - val = 0; - mask = MAX77620_LDO_SLEW_RATE_MASK; - } + struct max77620_regulator_pdata *rpdata = &pmic->reg_pdata[id]; - ret = regmap_update_bits(pmic->rmap, rinfo->cfg_addr, mask, val); - if (ret < 0) - dev_err(pmic->dev, "Reg 0x%02x update failed: %d\n", - rinfo->cfg_addr, ret); + /* Device specific ramp rate setting tells that platform has + * different ramp rate from advertised value. In this case, + * do not configure anything and just return success. + */ + if (rpdata->ramp_rate_setting) + return 0; - return ret; + return max77620_set_slew_rate(pmic, id, ramp_delay); } static int max77620_of_parse_cb(struct device_node *np, @@ -563,6 +589,9 @@ static int max77620_of_parse_cb(struct device_node *np, np, "maxim,suspend-fps-power-down-slot", &pval); rpdata->suspend_fps_pd_slot = (!ret) ? pval : -1; + ret = of_property_read_u32(np, "maxim,ramp-rate-setting", &pval); + rpdata->ramp_rate_setting = (!ret) ? pval : 0; + return max77620_init_pmic(pmic, desc->id); } diff --git a/drivers/regulator/max77686-regulator.c b/drivers/regulator/max77686-regulator.c index 17ccf365a9c0..ac4fa581e0a5 100644 --- a/drivers/regulator/max77686-regulator.c +++ b/drivers/regulator/max77686-regulator.c @@ -41,6 +41,8 @@ #define MAX77686_LDO_LOW_UVSTEP 25000 #define MAX77686_BUCK_MINUV 750000 #define MAX77686_BUCK_UVSTEP 50000 +#define MAX77686_BUCK_ENABLE_TIME 40 /* us */ +#define MAX77686_DVS_ENABLE_TIME 22 /* us */ #define MAX77686_RAMP_DELAY 100000 /* uV/us */ #define MAX77686_DVS_RAMP_DELAY 27500 /* uV/us */ #define MAX77686_DVS_MINUV 600000 @@ -422,6 +424,7 @@ static struct regulator_ops max77686_buck_dvs_ops = { .min_uV = MAX77686_BUCK_MINUV, \ .uV_step = MAX77686_BUCK_UVSTEP, \ .ramp_delay = MAX77686_RAMP_DELAY, \ + .enable_time = MAX77686_BUCK_ENABLE_TIME, \ .n_voltages = MAX77686_VSEL_MASK + 1, \ .vsel_reg = MAX77686_REG_BUCK5OUT + (num - 5) * 2, \ .vsel_mask = MAX77686_VSEL_MASK, \ @@ -439,6 +442,7 @@ static struct regulator_ops max77686_buck_dvs_ops = { .min_uV = MAX77686_BUCK_MINUV, \ .uV_step = MAX77686_BUCK_UVSTEP, \ .ramp_delay = MAX77686_RAMP_DELAY, \ + .enable_time = MAX77686_BUCK_ENABLE_TIME, \ .n_voltages = MAX77686_VSEL_MASK + 1, \ .vsel_reg = MAX77686_REG_BUCK1OUT, \ .vsel_mask = MAX77686_VSEL_MASK, \ @@ -456,6 +460,7 @@ static struct regulator_ops max77686_buck_dvs_ops = { .min_uV = MAX77686_DVS_MINUV, \ .uV_step = MAX77686_DVS_UVSTEP, \ .ramp_delay = MAX77686_DVS_RAMP_DELAY, \ + .enable_time = MAX77686_DVS_ENABLE_TIME, \ .n_voltages = MAX77686_DVS_VSEL_MASK + 1, \ .vsel_reg = MAX77686_REG_BUCK2DVS1 + (num - 2) * 10, \ .vsel_mask = MAX77686_DVS_VSEL_MASK, \ @@ -553,17 +558,7 @@ static struct platform_driver max77686_pmic_driver = { .id_table = max77686_pmic_id, }; -static int __init max77686_pmic_init(void) -{ - return platform_driver_register(&max77686_pmic_driver); -} -subsys_initcall(max77686_pmic_init); - -static void __exit max77686_pmic_cleanup(void) -{ - platform_driver_unregister(&max77686_pmic_driver); -} -module_exit(max77686_pmic_cleanup); +module_platform_driver(max77686_pmic_driver); MODULE_DESCRIPTION("MAXIM 77686 Regulator Driver"); MODULE_AUTHOR("Chiwoong Byun <woong.byun@samsung.com>"); diff --git a/drivers/regulator/max77693.c b/drivers/regulator/max77693-regulator.c index de730fd3f8a5..de730fd3f8a5 100644 --- a/drivers/regulator/max77693.c +++ b/drivers/regulator/max77693-regulator.c diff --git a/drivers/regulator/max77802-regulator.c b/drivers/regulator/max77802-regulator.c index c07ee13bd470..1d3539324d9a 100644 --- a/drivers/regulator/max77802-regulator.c +++ b/drivers/regulator/max77802-regulator.c @@ -5,7 +5,7 @@ * Simon Glass <sjg@chromium.org> * * Copyright (C) 2012 Samsung Electronics - * Chiwoong Byun <woong.byun@smasung.com> + * Chiwoong Byun <woong.byun@samsung.com> * Jonghwa Lee <jonghwa3.lee@samsung.com> * * This program is free software; you can redistribute it and/or modify diff --git a/drivers/regulator/max8997.c b/drivers/regulator/max8997-regulator.c index ea0196d4496b..efabc0ea0e96 100644 --- a/drivers/regulator/max8997.c +++ b/drivers/regulator/max8997-regulator.c @@ -2,7 +2,7 @@ * max8997.c - Regulator driver for the Maxim 8997/8966 * * Copyright (C) 2011 Samsung Electronics - * MyungJoo Ham <myungjoo.ham@smasung.com> + * MyungJoo Ham <myungjoo.ham@samsung.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/drivers/regulator/of_regulator.c b/drivers/regulator/of_regulator.c index 6b0aa80b22fd..cd828dbf9d52 100644 --- a/drivers/regulator/of_regulator.c +++ b/drivers/regulator/of_regulator.c @@ -45,9 +45,9 @@ static void of_get_regulation_constraints(struct device_node *np, /* Voltage change possible? */ if (constraints->min_uV != constraints->max_uV) constraints->valid_ops_mask |= REGULATOR_CHANGE_VOLTAGE; - /* Only one voltage? Then make sure it's set. */ - if (constraints->min_uV && constraints->max_uV && - constraints->min_uV == constraints->max_uV) + + /* Do we have a voltage range, if so try to apply it? */ + if (constraints->min_uV && constraints->max_uV) constraints->apply_uV = true; if (!of_property_read_u32(np, "regulator-microvolt-offset", &pval)) diff --git a/drivers/regulator/palmas-regulator.c b/drivers/regulator/palmas-regulator.c index 6efc7ee8aea3..f11d41dad9c1 100644 --- a/drivers/regulator/palmas-regulator.c +++ b/drivers/regulator/palmas-regulator.c @@ -944,6 +944,8 @@ static int palmas_ldo_registration(struct palmas_pmic *pmic, if (id == PALMAS_REG_LDO9) { desc->ops = &palmas_ops_ldo9; desc->bypass_reg = desc->enable_reg; + desc->bypass_val_on = + PALMAS_LDO9_CTRL_LDO_BYPASS_EN; desc->bypass_mask = PALMAS_LDO9_CTRL_LDO_BYPASS_EN; } @@ -1055,6 +1057,8 @@ static int tps65917_ldo_registration(struct palmas_pmic *pmic, id == TPS65917_REG_LDO2) { desc->ops = &tps65917_ops_ldo_1_2; desc->bypass_reg = desc->enable_reg; + desc->bypass_val_on = + TPS65917_LDO1_CTRL_BYPASS_EN; desc->bypass_mask = TPS65917_LDO1_CTRL_BYPASS_EN; } @@ -1206,6 +1210,7 @@ static int palmas_smps_registration(struct palmas_pmic *pmic, desc->enable_mask = SMPS10_BOOST_EN; desc->bypass_reg = PALMAS_BASE_TO_REG(PALMAS_SMPS_BASE, PALMAS_SMPS10_CTRL); + desc->bypass_val_on = SMPS10_BYPASS_EN; desc->bypass_mask = SMPS10_BYPASS_EN; desc->min_uV = 3750000; desc->uV_step = 1250000; @@ -1462,10 +1467,10 @@ static struct palmas_pmic_driver_data tps65917_ddata = { .ldo_register = tps65917_ldo_registration, }; -static void palmas_dt_to_pdata(struct device *dev, - struct device_node *node, - struct palmas_pmic_platform_data *pdata, - struct palmas_pmic_driver_data *ddata) +static int palmas_dt_to_pdata(struct device *dev, + struct device_node *node, + struct palmas_pmic_platform_data *pdata, + struct palmas_pmic_driver_data *ddata) { struct device_node *regulators; u32 prop; @@ -1474,7 +1479,7 @@ static void palmas_dt_to_pdata(struct device *dev, regulators = of_get_child_by_name(node, "regulators"); if (!regulators) { dev_info(dev, "regulator node not found\n"); - return; + return 0; } ret = of_regulator_match(dev, regulators, ddata->palmas_matches, @@ -1482,25 +1487,29 @@ static void palmas_dt_to_pdata(struct device *dev, of_node_put(regulators); if (ret < 0) { dev_err(dev, "Error parsing regulator init data: %d\n", ret); - return; + return 0; } for (idx = 0; idx < ddata->max_reg; idx++) { - if (!ddata->palmas_matches[idx].init_data || - !ddata->palmas_matches[idx].of_node) - continue; + static struct of_regulator_match *match; + struct palmas_reg_init *rinit; + struct device_node *np; - pdata->reg_data[idx] = ddata->palmas_matches[idx].init_data; + match = &ddata->palmas_matches[idx]; + np = match->of_node; - pdata->reg_init[idx] = devm_kzalloc(dev, - sizeof(struct palmas_reg_init), GFP_KERNEL); + if (!match->init_data || !np) + continue; + + rinit = devm_kzalloc(dev, sizeof(*rinit), GFP_KERNEL); + if (!rinit) + return -ENOMEM; - pdata->reg_init[idx]->warm_reset = - of_property_read_bool(ddata->palmas_matches[idx].of_node, - "ti,warm-reset"); + pdata->reg_data[idx] = match->init_data; + pdata->reg_init[idx] = rinit; - ret = of_property_read_u32(ddata->palmas_matches[idx].of_node, - "ti,roof-floor", &prop); + rinit->warm_reset = of_property_read_bool(np, "ti,warm-reset"); + ret = of_property_read_u32(np, "ti,roof-floor", &prop); /* EINVAL: Property not found */ if (ret != -EINVAL) { int econtrol; @@ -1522,31 +1531,29 @@ static void palmas_dt_to_pdata(struct device *dev, WARN_ON(1); dev_warn(dev, "%s: Invalid roof-floor option: %u\n", - palmas_matches[idx].name, prop); + match->name, prop); break; } } - pdata->reg_init[idx]->roof_floor = econtrol; + rinit->roof_floor = econtrol; } - ret = of_property_read_u32(ddata->palmas_matches[idx].of_node, - "ti,mode-sleep", &prop); + ret = of_property_read_u32(np, "ti,mode-sleep", &prop); if (!ret) - pdata->reg_init[idx]->mode_sleep = prop; + rinit->mode_sleep = prop; - ret = of_property_read_bool(ddata->palmas_matches[idx].of_node, - "ti,smps-range"); + ret = of_property_read_bool(np, "ti,smps-range"); if (ret) - pdata->reg_init[idx]->vsel = - PALMAS_SMPS12_VOLTAGE_RANGE; + rinit->vsel = PALMAS_SMPS12_VOLTAGE_RANGE; if (idx == PALMAS_REG_LDO8) pdata->enable_ldo8_tracking = of_property_read_bool( - ddata->palmas_matches[idx].of_node, - "ti,enable-ldo8-tracking"); + np, "ti,enable-ldo8-tracking"); } pdata->ldo6_vibrator = of_property_read_bool(node, "ti,ldo6-vibrator"); + + return 0; } static const struct of_device_id of_palmas_match_tbl[] = { @@ -1628,7 +1635,9 @@ static int palmas_regulators_probe(struct platform_device *pdev) platform_set_drvdata(pdev, pmic); pmic->palmas->pmic_ddata = driver_data; - palmas_dt_to_pdata(&pdev->dev, node, pdata, driver_data); + ret = palmas_dt_to_pdata(&pdev->dev, node, pdata, driver_data); + if (ret) + return ret; ret = palmas_smps_read(palmas, PALMAS_SMPS_CTRL, ®); if (ret) diff --git a/drivers/regulator/pv88080-regulator.c b/drivers/regulator/pv88080-regulator.c new file mode 100644 index 000000000000..d7107566c429 --- /dev/null +++ b/drivers/regulator/pv88080-regulator.c @@ -0,0 +1,419 @@ +/* + * pv88080-regulator.c - Regulator device driver for PV88080 + * Copyright (C) 2016 Powerventure Semiconductor Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * 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. + */ + +#include <linux/err.h> +#include <linux/gpio.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> +#include <linux/regmap.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/regulator/of_regulator.h> +#include <linux/proc_fs.h> +#include <linux/uaccess.h> +#include "pv88080-regulator.h" + +#define PV88080_MAX_REGULATORS 3 + +/* PV88080 REGULATOR IDs */ +enum { + /* BUCKs */ + PV88080_ID_BUCK1, + PV88080_ID_BUCK2, + PV88080_ID_BUCK3, +}; + +struct pv88080_regulator { + struct regulator_desc desc; + /* Current limiting */ + unsigned int n_current_limits; + const int *current_limits; + unsigned int limit_mask; + unsigned int conf; + unsigned int conf2; + unsigned int conf5; +}; + +struct pv88080 { + struct device *dev; + struct regmap *regmap; + struct regulator_dev *rdev[PV88080_MAX_REGULATORS]; +}; + +struct pv88080_buck_voltage { + int min_uV; + int max_uV; + int uV_step; +}; + +static const struct regmap_config pv88080_regmap_config = { + .reg_bits = 8, + .val_bits = 8, +}; + +/* Current limits array (in uA) for BUCK1, BUCK2, BUCK3. + * Entry indexes corresponds to register values. + */ + +static const int pv88080_buck1_limits[] = { + 3230000, 5130000, 6960000, 8790000 +}; + +static const int pv88080_buck23_limits[] = { + 1496000, 2393000, 3291000, 4189000 +}; + +static const struct pv88080_buck_voltage pv88080_buck_vol[2] = { + { + .min_uV = 600000, + .max_uV = 1393750, + .uV_step = 6250, + }, + { + .min_uV = 1400000, + .max_uV = 2193750, + .uV_step = 6250, + }, +}; + +static unsigned int pv88080_buck_get_mode(struct regulator_dev *rdev) +{ + struct pv88080_regulator *info = rdev_get_drvdata(rdev); + unsigned int data; + int ret, mode = 0; + + ret = regmap_read(rdev->regmap, info->conf, &data); + if (ret < 0) + return ret; + + switch (data & PV88080_BUCK1_MODE_MASK) { + case PV88080_BUCK_MODE_SYNC: + mode = REGULATOR_MODE_FAST; + break; + case PV88080_BUCK_MODE_AUTO: + mode = REGULATOR_MODE_NORMAL; + break; + case PV88080_BUCK_MODE_SLEEP: + mode = REGULATOR_MODE_STANDBY; + break; + default: + return -EINVAL; + } + + return mode; +} + +static int pv88080_buck_set_mode(struct regulator_dev *rdev, + unsigned int mode) +{ + struct pv88080_regulator *info = rdev_get_drvdata(rdev); + int val = 0; + + switch (mode) { + case REGULATOR_MODE_FAST: + val = PV88080_BUCK_MODE_SYNC; + break; + case REGULATOR_MODE_NORMAL: + val = PV88080_BUCK_MODE_AUTO; + break; + case REGULATOR_MODE_STANDBY: + val = PV88080_BUCK_MODE_SLEEP; + break; + default: + return -EINVAL; + } + + return regmap_update_bits(rdev->regmap, info->conf, + PV88080_BUCK1_MODE_MASK, val); +} + +static int pv88080_set_current_limit(struct regulator_dev *rdev, int min, + int max) +{ + struct pv88080_regulator *info = rdev_get_drvdata(rdev); + int i; + + /* search for closest to maximum */ + for (i = info->n_current_limits; i >= 0; i--) { + if (min <= info->current_limits[i] + && max >= info->current_limits[i]) { + return regmap_update_bits(rdev->regmap, + info->conf, + info->limit_mask, + i << PV88080_BUCK1_ILIM_SHIFT); + } + } + + return -EINVAL; +} + +static int pv88080_get_current_limit(struct regulator_dev *rdev) +{ + struct pv88080_regulator *info = rdev_get_drvdata(rdev); + unsigned int data; + int ret; + + ret = regmap_read(rdev->regmap, info->conf, &data); + if (ret < 0) + return ret; + + data = (data & info->limit_mask) >> PV88080_BUCK1_ILIM_SHIFT; + return info->current_limits[data]; +} + +static struct regulator_ops pv88080_buck_ops = { + .get_mode = pv88080_buck_get_mode, + .set_mode = pv88080_buck_set_mode, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .list_voltage = regulator_list_voltage_linear, + .set_current_limit = pv88080_set_current_limit, + .get_current_limit = pv88080_get_current_limit, +}; + +#define PV88080_BUCK(chip, regl_name, min, step, max, limits_array) \ +{\ + .desc = {\ + .id = chip##_ID_##regl_name,\ + .name = __stringify(chip##_##regl_name),\ + .of_match = of_match_ptr(#regl_name),\ + .regulators_node = of_match_ptr("regulators"),\ + .type = REGULATOR_VOLTAGE,\ + .owner = THIS_MODULE,\ + .ops = &pv88080_buck_ops,\ + .min_uV = min, \ + .uV_step = step, \ + .n_voltages = ((max) - (min))/(step) + 1, \ + .enable_reg = PV88080_REG_##regl_name##_CONF0, \ + .enable_mask = PV88080_##regl_name##_EN, \ + .vsel_reg = PV88080_REG_##regl_name##_CONF0, \ + .vsel_mask = PV88080_V##regl_name##_MASK, \ + },\ + .current_limits = limits_array, \ + .n_current_limits = ARRAY_SIZE(limits_array), \ + .limit_mask = PV88080_##regl_name##_ILIM_MASK, \ + .conf = PV88080_REG_##regl_name##_CONF1, \ + .conf2 = PV88080_REG_##regl_name##_CONF2, \ + .conf5 = PV88080_REG_##regl_name##_CONF5, \ +} + +static struct pv88080_regulator pv88080_regulator_info[] = { + PV88080_BUCK(PV88080, BUCK1, 600000, 6250, 1393750, + pv88080_buck1_limits), + PV88080_BUCK(PV88080, BUCK2, 600000, 6250, 1393750, + pv88080_buck23_limits), + PV88080_BUCK(PV88080, BUCK3, 600000, 6250, 1393750, + pv88080_buck23_limits), +}; + +static irqreturn_t pv88080_irq_handler(int irq, void *data) +{ + struct pv88080 *chip = data; + int i, reg_val, err, ret = IRQ_NONE; + + err = regmap_read(chip->regmap, PV88080_REG_EVENT_A, ®_val); + if (err < 0) + goto error_i2c; + + if (reg_val & PV88080_E_VDD_FLT) { + for (i = 0; i < PV88080_MAX_REGULATORS; i++) { + if (chip->rdev[i] != NULL) { + regulator_notifier_call_chain(chip->rdev[i], + REGULATOR_EVENT_UNDER_VOLTAGE, + NULL); + } + } + + err = regmap_write(chip->regmap, PV88080_REG_EVENT_A, + PV88080_E_VDD_FLT); + if (err < 0) + goto error_i2c; + + ret = IRQ_HANDLED; + } + + if (reg_val & PV88080_E_OVER_TEMP) { + for (i = 0; i < PV88080_MAX_REGULATORS; i++) { + if (chip->rdev[i] != NULL) { + regulator_notifier_call_chain(chip->rdev[i], + REGULATOR_EVENT_OVER_TEMP, + NULL); + } + } + + err = regmap_write(chip->regmap, PV88080_REG_EVENT_A, + PV88080_E_OVER_TEMP); + if (err < 0) + goto error_i2c; + + ret = IRQ_HANDLED; + } + + return ret; + +error_i2c: + dev_err(chip->dev, "I2C error : %d\n", err); + return IRQ_NONE; +} + +/* + * I2C driver interface functions + */ +static int pv88080_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct regulator_init_data *init_data = dev_get_platdata(&i2c->dev); + struct pv88080 *chip; + struct regulator_config config = { }; + int i, error, ret; + unsigned int conf2, conf5; + + chip = devm_kzalloc(&i2c->dev, sizeof(struct pv88080), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->dev = &i2c->dev; + chip->regmap = devm_regmap_init_i2c(i2c, &pv88080_regmap_config); + if (IS_ERR(chip->regmap)) { + error = PTR_ERR(chip->regmap); + dev_err(chip->dev, "Failed to allocate register map: %d\n", + error); + return error; + } + + i2c_set_clientdata(i2c, chip); + + if (i2c->irq != 0) { + ret = regmap_write(chip->regmap, PV88080_REG_MASK_A, 0xFF); + if (ret < 0) { + dev_err(chip->dev, + "Failed to mask A reg: %d\n", ret); + return ret; + } + ret = regmap_write(chip->regmap, PV88080_REG_MASK_B, 0xFF); + if (ret < 0) { + dev_err(chip->dev, + "Failed to mask B reg: %d\n", ret); + return ret; + } + ret = regmap_write(chip->regmap, PV88080_REG_MASK_C, 0xFF); + if (ret < 0) { + dev_err(chip->dev, + "Failed to mask C reg: %d\n", ret); + return ret; + } + + ret = devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL, + pv88080_irq_handler, + IRQF_TRIGGER_LOW|IRQF_ONESHOT, + "pv88080", chip); + if (ret != 0) { + dev_err(chip->dev, "Failed to request IRQ: %d\n", + i2c->irq); + return ret; + } + + ret = regmap_update_bits(chip->regmap, PV88080_REG_MASK_A, + PV88080_M_VDD_FLT | PV88080_M_OVER_TEMP, 0); + if (ret < 0) { + dev_err(chip->dev, + "Failed to update mask reg: %d\n", ret); + return ret; + } + + } else { + dev_warn(chip->dev, "No IRQ configured\n"); + } + + config.dev = chip->dev; + config.regmap = chip->regmap; + + for (i = 0; i < PV88080_MAX_REGULATORS; i++) { + if (init_data) + config.init_data = &init_data[i]; + + ret = regmap_read(chip->regmap, + pv88080_regulator_info[i].conf2, &conf2); + if (ret < 0) + return ret; + + conf2 = ((conf2 >> PV88080_BUCK_VDAC_RANGE_SHIFT) & + PV88080_BUCK_VDAC_RANGE_MASK); + + ret = regmap_read(chip->regmap, + pv88080_regulator_info[i].conf5, &conf5); + if (ret < 0) + return ret; + + conf5 = ((conf5 >> PV88080_BUCK_VRANGE_GAIN_SHIFT) & + PV88080_BUCK_VRANGE_GAIN_MASK); + + pv88080_regulator_info[i].desc.min_uV = + pv88080_buck_vol[conf2].min_uV * (conf5+1); + pv88080_regulator_info[i].desc.uV_step = + pv88080_buck_vol[conf2].uV_step * (conf5+1); + pv88080_regulator_info[i].desc.n_voltages = + ((pv88080_buck_vol[conf2].max_uV * (conf5+1)) + - (pv88080_regulator_info[i].desc.min_uV)) + /(pv88080_regulator_info[i].desc.uV_step) + 1; + + config.driver_data = (void *)&pv88080_regulator_info[i]; + chip->rdev[i] = devm_regulator_register(chip->dev, + &pv88080_regulator_info[i].desc, &config); + if (IS_ERR(chip->rdev[i])) { + dev_err(chip->dev, + "Failed to register PV88080 regulator\n"); + return PTR_ERR(chip->rdev[i]); + } + } + + return 0; +} + +static const struct i2c_device_id pv88080_i2c_id[] = { + {"pv88080", 0}, + {}, +}; +MODULE_DEVICE_TABLE(i2c, pv88080_i2c_id); + +#ifdef CONFIG_OF +static const struct of_device_id pv88080_dt_ids[] = { + { .compatible = "pvs,pv88080", .data = &pv88080_i2c_id[0] }, + {}, +}; +MODULE_DEVICE_TABLE(of, pv88080_dt_ids); +#endif + +static struct i2c_driver pv88080_regulator_driver = { + .driver = { + .name = "pv88080", + .of_match_table = of_match_ptr(pv88080_dt_ids), + }, + .probe = pv88080_i2c_probe, + .id_table = pv88080_i2c_id, +}; + +module_i2c_driver(pv88080_regulator_driver); + +MODULE_AUTHOR("James Ban <James.Ban.opensource@diasemi.com>"); +MODULE_DESCRIPTION("Regulator device driver for Powerventure PV88080"); +MODULE_LICENSE("GPL"); diff --git a/drivers/regulator/pv88080-regulator.h b/drivers/regulator/pv88080-regulator.h new file mode 100644 index 000000000000..5e9afde606f4 --- /dev/null +++ b/drivers/regulator/pv88080-regulator.h @@ -0,0 +1,92 @@ +/* + * pv88080-regulator.h - Regulator definitions for PV88080 + * Copyright (C) 2016 Powerventure Semiconductor Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * 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. + */ + +#ifndef __PV88080_REGISTERS_H__ +#define __PV88080_REGISTERS_H__ + +/* System Control and Event Registers */ +#define PV88080_REG_EVENT_A 0x04 +#define PV88080_REG_MASK_A 0x09 +#define PV88080_REG_MASK_B 0x0a +#define PV88080_REG_MASK_C 0x0b + +/* Regulator Registers */ +#define PV88080_REG_BUCK1_CONF0 0x27 +#define PV88080_REG_BUCK1_CONF1 0x28 +#define PV88080_REG_BUCK1_CONF2 0x59 +#define PV88080_REG_BUCK1_CONF5 0x5c +#define PV88080_REG_BUCK2_CONF0 0x29 +#define PV88080_REG_BUCK2_CONF1 0x2a +#define PV88080_REG_BUCK2_CONF2 0x61 +#define PV88080_REG_BUCK2_CONF5 0x64 +#define PV88080_REG_BUCK3_CONF0 0x2b +#define PV88080_REG_BUCK3_CONF1 0x2c +#define PV88080_REG_BUCK3_CONF2 0x69 +#define PV88080_REG_BUCK3_CONF5 0x6c + +/* PV88080_REG_EVENT_A (addr=0x04) */ +#define PV88080_E_VDD_FLT 0x01 +#define PV88080_E_OVER_TEMP 0x02 + +/* PV88080_REG_MASK_A (addr=0x09) */ +#define PV88080_M_VDD_FLT 0x01 +#define PV88080_M_OVER_TEMP 0x02 + +/* PV88080_REG_BUCK1_CONF0 (addr=0x27) */ +#define PV88080_BUCK1_EN 0x80 +#define PV88080_VBUCK1_MASK 0x7F +/* PV88080_REG_BUCK2_CONF0 (addr=0x29) */ +#define PV88080_BUCK2_EN 0x80 +#define PV88080_VBUCK2_MASK 0x7F +/* PV88080_REG_BUCK3_CONF0 (addr=0x2b) */ +#define PV88080_BUCK3_EN 0x80 +#define PV88080_VBUCK3_MASK 0x7F + +/* PV88080_REG_BUCK1_CONF1 (addr=0x28) */ +#define PV88080_BUCK1_ILIM_SHIFT 2 +#define PV88080_BUCK1_ILIM_MASK 0x0C +#define PV88080_BUCK1_MODE_MASK 0x03 + +/* PV88080_REG_BUCK2_CONF1 (addr=0x2a) */ +#define PV88080_BUCK2_ILIM_SHIFT 2 +#define PV88080_BUCK2_ILIM_MASK 0x0C +#define PV88080_BUCK2_MODE_MASK 0x03 + +/* PV88080_REG_BUCK3_CONF1 (addr=0x2c) */ +#define PV88080_BUCK3_ILIM_SHIFT 2 +#define PV88080_BUCK3_ILIM_MASK 0x0C +#define PV88080_BUCK3_MODE_MASK 0x03 + +#define PV88080_BUCK_MODE_SLEEP 0x00 +#define PV88080_BUCK_MODE_AUTO 0x01 +#define PV88080_BUCK_MODE_SYNC 0x02 + +/* PV88080_REG_BUCK2_CONF2 (addr=0x61) */ +/* PV88080_REG_BUCK3_CONF2 (addr=0x69) */ +#define PV88080_BUCK_VDAC_RANGE_SHIFT 7 +#define PV88080_BUCK_VDAC_RANGE_MASK 0x01 + +#define PV88080_BUCK_VDAC_RANGE_1 0x00 +#define PV88080_BUCK_VDAC_RANGE_2 0x01 + +/* PV88080_REG_BUCK2_CONF5 (addr=0x64) */ +/* PV88080_REG_BUCK3_CONF5 (addr=0x6c) */ +#define PV88080_BUCK_VRANGE_GAIN_SHIFT 0 +#define PV88080_BUCK_VRANGE_GAIN_MASK 0x01 + +#define PV88080_BUCK_VRANGE_GAIN_1 0x00 +#define PV88080_BUCK_VRANGE_GAIN_2 0x01 + +#endif /* __PV88080_REGISTERS_H__ */ diff --git a/drivers/regulator/s2mps11.c b/drivers/regulator/s2mps11.c index d24e2c783dc5..6dfa3502e1f1 100644 --- a/drivers/regulator/s2mps11.c +++ b/drivers/regulator/s2mps11.c @@ -308,7 +308,7 @@ static struct regulator_ops s2mps11_buck_ops = { .enable_mask = S2MPS11_ENABLE_MASK \ } -#define regulator_desc_s2mps11_buck6_10(num, min, step) { \ +#define regulator_desc_s2mps11_buck67810(num, min, step) { \ .name = "BUCK"#num, \ .id = S2MPS11_BUCK##num, \ .ops = &s2mps11_buck_ops, \ @@ -324,6 +324,22 @@ static struct regulator_ops s2mps11_buck_ops = { .enable_mask = S2MPS11_ENABLE_MASK \ } +#define regulator_desc_s2mps11_buck9 { \ + .name = "BUCK9", \ + .id = S2MPS11_BUCK9, \ + .ops = &s2mps11_buck_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .min_uV = MIN_3000_MV, \ + .uV_step = STEP_25_MV, \ + .n_voltages = S2MPS11_BUCK9_N_VOLTAGES, \ + .ramp_delay = S2MPS11_RAMP_DELAY, \ + .vsel_reg = S2MPS11_REG_B9CTRL2, \ + .vsel_mask = S2MPS11_BUCK9_VSEL_MASK, \ + .enable_reg = S2MPS11_REG_B9CTRL1, \ + .enable_mask = S2MPS11_ENABLE_MASK \ +} + static const struct regulator_desc s2mps11_regulators[] = { regulator_desc_s2mps11_ldo(1, STEP_25_MV), regulator_desc_s2mps11_ldo(2, STEP_50_MV), @@ -368,11 +384,11 @@ static const struct regulator_desc s2mps11_regulators[] = { regulator_desc_s2mps11_buck1_4(3), regulator_desc_s2mps11_buck1_4(4), regulator_desc_s2mps11_buck5, - regulator_desc_s2mps11_buck6_10(6, MIN_600_MV, STEP_6_25_MV), - regulator_desc_s2mps11_buck6_10(7, MIN_600_MV, STEP_6_25_MV), - regulator_desc_s2mps11_buck6_10(8, MIN_600_MV, STEP_6_25_MV), - regulator_desc_s2mps11_buck6_10(9, MIN_3000_MV, STEP_25_MV), - regulator_desc_s2mps11_buck6_10(10, MIN_750_MV, STEP_12_5_MV), + regulator_desc_s2mps11_buck67810(6, MIN_600_MV, STEP_6_25_MV), + regulator_desc_s2mps11_buck67810(7, MIN_600_MV, STEP_6_25_MV), + regulator_desc_s2mps11_buck67810(8, MIN_600_MV, STEP_6_25_MV), + regulator_desc_s2mps11_buck9, + regulator_desc_s2mps11_buck67810(10, MIN_750_MV, STEP_12_5_MV), }; static struct regulator_ops s2mps14_reg_ops; |