diff --git a/Documentation/devicetree/bindings/mfd/s2mps11.txt b/Documentation/devicetree/bindings/mfd/s2mps11.txt index 802e839b0829..d81ba30c0d8b 100644 --- a/Documentation/devicetree/bindings/mfd/s2mps11.txt +++ b/Documentation/devicetree/bindings/mfd/s2mps11.txt @@ -56,6 +56,20 @@ for a particular group of BUCKs. So provide same regulator-ramp-delay. Grouping of BUCKs sharing ramp rate setting is as follow : BUCK[1, 6], BUCK[3, 4], and BUCK[7, 8, 10] +On S2MPS14 the LDO10, LDO11 and LDO12 can be configured to external control +over GPIO. To turn this feature on this property must be added to the regulator +sub-node: + - samsung,ext-control-gpios: GPIO specifier for one GPIO + controlling this regulator (enable/disable); +Example: + LDO12 { + regulator-name = "V_EMMC_2.8V"; + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <2800000>; + samsung,ext-control-gpios = <&gpk0 2 0>; + }; + + The regulator constraints inside the regulator nodes use the standard regulator bindings which are documented elsewhere. diff --git a/drivers/regulator/palmas-regulator.c b/drivers/regulator/palmas-regulator.c index 671cb9f32dab..864ed02ce4b7 100644 --- a/drivers/regulator/palmas-regulator.c +++ b/drivers/regulator/palmas-regulator.c @@ -36,6 +36,18 @@ struct regs_info { int sleep_id; }; +static const struct regulator_linear_range smps_low_ranges[] = { + REGULATOR_LINEAR_RANGE(500000, 0x1, 0x6, 0), + REGULATOR_LINEAR_RANGE(510000, 0x7, 0x79, 10000), + REGULATOR_LINEAR_RANGE(1650000, 0x7A, 0x7f, 0), +}; + +static const struct regulator_linear_range smps_high_ranges[] = { + REGULATOR_LINEAR_RANGE(1000000, 0x1, 0x6, 0), + REGULATOR_LINEAR_RANGE(1020000, 0x7, 0x79, 20000), + REGULATOR_LINEAR_RANGE(3300000, 0x7A, 0x7f, 0), +}; + static const struct regs_info palmas_regs_info[] = { { .name = "SMPS12", @@ -280,54 +292,6 @@ static int palmas_ldo_write(struct palmas *palmas, unsigned int reg, return regmap_write(palmas->regmap[REGULATOR_SLAVE], addr, value); } -static int palmas_is_enabled_smps(struct regulator_dev *dev) -{ - struct palmas_pmic *pmic = rdev_get_drvdata(dev); - int id = rdev_get_id(dev); - unsigned int reg; - - palmas_smps_read(pmic->palmas, palmas_regs_info[id].ctrl_addr, ®); - - reg &= PALMAS_SMPS12_CTRL_STATUS_MASK; - reg >>= PALMAS_SMPS12_CTRL_STATUS_SHIFT; - - return !!(reg); -} - -static int palmas_enable_smps(struct regulator_dev *dev) -{ - struct palmas_pmic *pmic = rdev_get_drvdata(dev); - int id = rdev_get_id(dev); - unsigned int reg; - - palmas_smps_read(pmic->palmas, palmas_regs_info[id].ctrl_addr, ®); - - reg &= ~PALMAS_SMPS12_CTRL_MODE_ACTIVE_MASK; - if (pmic->current_reg_mode[id]) - reg |= pmic->current_reg_mode[id]; - else - reg |= SMPS_CTRL_MODE_ON; - - palmas_smps_write(pmic->palmas, palmas_regs_info[id].ctrl_addr, reg); - - return 0; -} - -static int palmas_disable_smps(struct regulator_dev *dev) -{ - struct palmas_pmic *pmic = rdev_get_drvdata(dev); - int id = rdev_get_id(dev); - unsigned int reg; - - palmas_smps_read(pmic->palmas, palmas_regs_info[id].ctrl_addr, ®); - - reg &= ~PALMAS_SMPS12_CTRL_MODE_ACTIVE_MASK; - - palmas_smps_write(pmic->palmas, palmas_regs_info[id].ctrl_addr, reg); - - return 0; -} - static int palmas_set_mode_smps(struct regulator_dev *dev, unsigned int mode) { struct palmas_pmic *pmic = rdev_get_drvdata(dev); @@ -382,81 +346,6 @@ static unsigned int palmas_get_mode_smps(struct regulator_dev *dev) return 0; } -static int palmas_list_voltage_smps(struct regulator_dev *dev, - unsigned selector) -{ - struct palmas_pmic *pmic = rdev_get_drvdata(dev); - int id = rdev_get_id(dev); - int mult = 1; - - /* Read the multiplier set in VSEL register to return - * the correct voltage. - */ - if (pmic->range[id]) - mult = 2; - - if (selector == 0) - return 0; - else if (selector < 6) - return 500000 * mult; - else - /* Voltage is linear mapping starting from selector 6, - * volt = (0.49V + ((selector - 5) * 0.01V)) * RANGE - * RANGE is either x1 or x2 - */ - return (490000 + ((selector - 5) * 10000)) * mult; -} - -static int palmas_map_voltage_smps(struct regulator_dev *rdev, - int min_uV, int max_uV) -{ - struct palmas_pmic *pmic = rdev_get_drvdata(rdev); - int id = rdev_get_id(rdev); - int ret, voltage; - - if (min_uV == 0) - return 0; - - if (pmic->range[id]) { /* RANGE is x2 */ - if (min_uV < 1000000) - min_uV = 1000000; - ret = DIV_ROUND_UP(min_uV - 1000000, 20000) + 6; - } else { /* RANGE is x1 */ - if (min_uV < 500000) - min_uV = 500000; - ret = DIV_ROUND_UP(min_uV - 500000, 10000) + 6; - } - - /* Map back into a voltage to verify we're still in bounds */ - voltage = palmas_list_voltage_smps(rdev, ret); - if (voltage < min_uV || voltage > max_uV) - return -EINVAL; - - return ret; -} - -static int palma_smps_set_voltage_smps_time_sel(struct regulator_dev *rdev, - unsigned int old_selector, unsigned int new_selector) -{ - struct palmas_pmic *pmic = rdev_get_drvdata(rdev); - int id = rdev_get_id(rdev); - int old_uv, new_uv; - unsigned int ramp_delay = pmic->ramp_delay[id]; - - if (!ramp_delay) - return 0; - - old_uv = palmas_list_voltage_smps(rdev, old_selector); - if (old_uv < 0) - return old_uv; - - new_uv = palmas_list_voltage_smps(rdev, new_selector); - if (new_uv < 0) - return new_uv; - - return DIV_ROUND_UP(abs(old_uv - new_uv), ramp_delay); -} - static int palmas_smps_set_ramp_delay(struct regulator_dev *rdev, int ramp_delay) { @@ -493,16 +382,16 @@ static int palmas_smps_set_ramp_delay(struct regulator_dev *rdev, } static struct regulator_ops palmas_ops_smps = { - .is_enabled = palmas_is_enabled_smps, - .enable = palmas_enable_smps, - .disable = palmas_disable_smps, + .is_enabled = regulator_is_enabled_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, .set_mode = palmas_set_mode_smps, .get_mode = palmas_get_mode_smps, .get_voltage_sel = regulator_get_voltage_sel_regmap, .set_voltage_sel = regulator_set_voltage_sel_regmap, - .list_voltage = palmas_list_voltage_smps, - .map_voltage = palmas_map_voltage_smps, - .set_voltage_time_sel = palma_smps_set_voltage_smps_time_sel, + .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 = palmas_smps_set_ramp_delay, }; @@ -511,9 +400,9 @@ static struct regulator_ops palmas_ops_ext_control_smps = { .get_mode = palmas_get_mode_smps, .get_voltage_sel = regulator_get_voltage_sel_regmap, .set_voltage_sel = regulator_set_voltage_sel_regmap, - .list_voltage = palmas_list_voltage_smps, - .map_voltage = palmas_map_voltage_smps, - .set_voltage_time_sel = palma_smps_set_voltage_smps_time_sel, + .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 = palmas_smps_set_ramp_delay, }; @@ -1042,12 +931,17 @@ static int palmas_regulators_probe(struct platform_device *pdev) * ranges. Read the current smps mode for later use. */ addr = palmas_regs_info[id].vsel_addr; + pmic->desc[id].n_linear_ranges = 3; ret = palmas_smps_read(pmic->palmas, addr, ®); if (ret) return ret; if (reg & PALMAS_SMPS12_VOLTAGE_RANGE) pmic->range[id] = 1; + if (pmic->range[id]) + pmic->desc[id].linear_ranges = smps_high_ranges; + else + pmic->desc[id].linear_ranges = smps_low_ranges; if (reg_init && reg_init->roof_floor) pmic->desc[id].ops = diff --git a/drivers/regulator/pbias-regulator.c b/drivers/regulator/pbias-regulator.c index 6d38be3d970c..6d02d68dfb46 100644 --- a/drivers/regulator/pbias-regulator.c +++ b/drivers/regulator/pbias-regulator.c @@ -49,33 +49,13 @@ static const unsigned int pbias_volt_table[] = { 3000000 }; -static int pbias_regulator_enable(struct regulator_dev *rdev) -{ - struct pbias_regulator_data *data = rdev_get_drvdata(rdev); - const struct pbias_reg_info *info = data->info; - - return regmap_update_bits(data->syscon, rdev->desc->enable_reg, - info->enable_mask, info->enable); -} - -static int pbias_regulator_is_enable(struct regulator_dev *rdev) -{ - struct pbias_regulator_data *data = rdev_get_drvdata(rdev); - const struct pbias_reg_info *info = data->info; - int value; - - regmap_read(data->syscon, rdev->desc->enable_reg, &value); - - return (value & info->enable_mask) == info->enable; -} - static struct regulator_ops pbias_regulator_voltage_ops = { .list_voltage = regulator_list_voltage_table, .get_voltage_sel = regulator_get_voltage_sel_regmap, .set_voltage_sel = regulator_set_voltage_sel_regmap, - .enable = pbias_regulator_enable, + .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, - .is_enabled = pbias_regulator_is_enable, + .is_enabled = regulator_is_enabled_regmap, }; static const struct pbias_reg_info pbias_mmc_omap2430 = { @@ -142,10 +122,8 @@ static int pbias_regulator_probe(struct platform_device *pdev) drvdata = devm_kzalloc(&pdev->dev, sizeof(struct pbias_regulator_data) * count, GFP_KERNEL); - if (drvdata == NULL) { - dev_err(&pdev->dev, "Failed to allocate device data\n"); + if (!drvdata) return -ENOMEM; - } syscon = syscon_regmap_lookup_by_phandle(np, "syscon"); if (IS_ERR(syscon)) @@ -180,6 +158,7 @@ static int pbias_regulator_probe(struct platform_device *pdev) drvdata[data_idx].desc.vsel_mask = info->vmode; drvdata[data_idx].desc.enable_reg = res->start; drvdata[data_idx].desc.enable_mask = info->enable_mask; + drvdata[data_idx].desc.enable_val = info->enable; cfg.init_data = pbias_matches[idx].init_data; cfg.driver_data = &drvdata[data_idx]; diff --git a/drivers/regulator/pfuze100-regulator.c b/drivers/regulator/pfuze100-regulator.c index 67e678c4301c..c879dff597ee 100644 --- a/drivers/regulator/pfuze100-regulator.c +++ b/drivers/regulator/pfuze100-regulator.c @@ -125,6 +125,9 @@ static struct regulator_ops pfuze100_ldo_regulator_ops = { }; static struct regulator_ops pfuze100_fixed_regulator_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, .list_voltage = regulator_list_voltage_linear, }; @@ -137,6 +140,8 @@ static struct regulator_ops pfuze100_sw_regulator_ops = { }; static struct regulator_ops pfuze100_swb_regulator_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, .list_voltage = regulator_list_voltage_table, .map_voltage = regulator_map_voltage_ascend, .set_voltage_sel = regulator_set_voltage_sel_regmap, @@ -189,6 +194,8 @@ static struct regulator_ops pfuze100_swb_regulator_ops = { .volt_table = voltages, \ .vsel_reg = (base), \ .vsel_mask = (mask), \ + .enable_reg = (base), \ + .enable_mask = 0x48, \ }, \ } @@ -502,6 +509,7 @@ static int pfuze100_regulator_probe(struct i2c_client *client, config.init_data = init_data; config.driver_data = pfuze_chip; config.of_node = match_of_node(i); + config.ena_gpio = -EINVAL; pfuze_chip->regulators[i] = devm_regulator_register(&client->dev, desc, &config); diff --git a/drivers/regulator/s2mpa01.c b/drivers/regulator/s2mpa01.c index a52cb1cec576..ee83b4876420 100644 --- a/drivers/regulator/s2mpa01.c +++ b/drivers/regulator/s2mpa01.c @@ -61,7 +61,7 @@ static int s2mpa01_regulator_set_voltage_time_sel(struct regulator_dev *rdev, unsigned int ramp_delay = 0; int old_volt, new_volt; - switch (rdev->desc->id) { + switch (rdev_get_id(rdev)) { case S2MPA01_BUCK2: case S2MPA01_BUCK4: ramp_delay = s2mpa01->ramp_delay24; @@ -102,7 +102,7 @@ static int s2mpa01_set_ramp_delay(struct regulator_dev *rdev, int ramp_delay) unsigned int ramp_enable = 1, enable_shift = 0; int ret; - switch (rdev->desc->id) { + switch (rdev_get_id(rdev)) { case S2MPA01_BUCK1: enable_shift = S2MPA01_BUCK1_RAMP_EN_SHIFT; if (!ramp_delay) { @@ -191,11 +191,15 @@ static int s2mpa01_set_ramp_delay(struct regulator_dev *rdev, int ramp_delay) if (!ramp_enable) goto ramp_disable; - ret = regmap_update_bits(rdev->regmap, S2MPA01_REG_RAMP1, - 1 << enable_shift, 1 << enable_shift); - if (ret) { - dev_err(&rdev->dev, "failed to enable ramp rate\n"); - return ret; + /* Ramp delay can be enabled/disabled only for buck[1234] */ + if (rdev_get_id(rdev) >= S2MPA01_BUCK1 && + rdev_get_id(rdev) <= S2MPA01_BUCK4) { + ret = regmap_update_bits(rdev->regmap, S2MPA01_REG_RAMP1, + 1 << enable_shift, 1 << enable_shift); + if (ret) { + dev_err(&rdev->dev, "failed to enable ramp rate\n"); + return ret; + } } ramp_val = get_ramp_delay(ramp_delay); diff --git a/drivers/regulator/s2mps11.c b/drivers/regulator/s2mps11.c index e713c162fbd4..02e2fb2fca66 100644 --- a/drivers/regulator/s2mps11.c +++ b/drivers/regulator/s2mps11.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -44,6 +45,8 @@ struct s2mps11_info { * was enabled. */ unsigned int s2mps14_suspend_state:30; + /* Array of size rdev_num with GPIO-s for external sleep control */ + int *ext_control_gpio; }; static int get_ramp_delay(int ramp_delay) @@ -202,11 +205,16 @@ static int s2mps11_set_ramp_delay(struct regulator_dev *rdev, int ramp_delay) if (!ramp_enable) goto ramp_disable; - ret = regmap_update_bits(rdev->regmap, S2MPS11_REG_RAMP, - 1 << enable_shift, 1 << enable_shift); - if (ret) { - dev_err(&rdev->dev, "failed to enable ramp rate\n"); - return ret; + /* Ramp delay can be enabled/disabled only for buck[2346] */ + if ((rdev_get_id(rdev) >= S2MPS11_BUCK2 && + rdev_get_id(rdev) <= S2MPS11_BUCK4) || + rdev_get_id(rdev) == S2MPS11_BUCK6) { + ret = regmap_update_bits(rdev->regmap, S2MPS11_REG_RAMP, + 1 << enable_shift, 1 << enable_shift); + if (ret) { + dev_err(&rdev->dev, "failed to enable ramp rate\n"); + return ret; + } } ramp_val = get_ramp_delay(ramp_delay); @@ -409,6 +417,8 @@ static int s2mps14_regulator_enable(struct regulator_dev *rdev) if (s2mps11->s2mps14_suspend_state & (1 << rdev_get_id(rdev))) val = S2MPS14_ENABLE_SUSPEND; + else if (gpio_is_valid(s2mps11->ext_control_gpio[rdev_get_id(rdev)])) + val = S2MPS14_ENABLE_EXT_CONTROL; else val = rdev->desc->enable_mask; @@ -565,12 +575,61 @@ static const struct regulator_desc s2mps14_regulators[] = { regulator_desc_s2mps14_buck1235(5), }; +static int s2mps14_pmic_enable_ext_control(struct s2mps11_info *s2mps11, + struct regulator_dev *rdev) +{ + return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg, + rdev->desc->enable_mask, S2MPS14_ENABLE_EXT_CONTROL); +} + +static void s2mps14_pmic_dt_parse_ext_control_gpio(struct platform_device *pdev, + struct of_regulator_match *rdata, struct s2mps11_info *s2mps11) +{ + int *gpio = s2mps11->ext_control_gpio; + unsigned int i; + unsigned int valid_regulators[3] = { S2MPS14_LDO10, S2MPS14_LDO11, + S2MPS14_LDO12 }; + + for (i = 0; i < ARRAY_SIZE(valid_regulators); i++) { + unsigned int reg = valid_regulators[i]; + + if (!rdata[reg].init_data || !rdata[reg].of_node) + continue; + + gpio[reg] = of_get_named_gpio(rdata[reg].of_node, + "samsung,ext-control-gpios", 0); + if (gpio_is_valid(gpio[reg])) + dev_dbg(&pdev->dev, "Using GPIO %d for ext-control over %d/%s\n", + gpio[reg], reg, rdata[reg].name); + } +} + +static int s2mps11_pmic_dt_parse(struct platform_device *pdev, + struct of_regulator_match *rdata, struct s2mps11_info *s2mps11, + enum sec_device_type dev_type) +{ + struct device_node *reg_np; + + reg_np = of_get_child_by_name(pdev->dev.parent->of_node, "regulators"); + if (!reg_np) { + dev_err(&pdev->dev, "could not find regulators sub-node\n"); + return -EINVAL; + } + + of_regulator_match(&pdev->dev, reg_np, rdata, s2mps11->rdev_num); + if (dev_type == S2MPS14X) + s2mps14_pmic_dt_parse_ext_control_gpio(pdev, rdata, s2mps11); + + of_node_put(reg_np); + + return 0; +} + static int s2mps11_pmic_probe(struct platform_device *pdev) { struct sec_pmic_dev *iodev = dev_get_drvdata(pdev->dev.parent); - struct sec_platform_data *pdata = iodev->pdata; + struct sec_platform_data *pdata = NULL; struct of_regulator_match *rdata = NULL; - struct device_node *reg_np = NULL; struct regulator_config config = { }; struct s2mps11_info *s2mps11; int i, ret = 0; @@ -597,8 +656,21 @@ static int s2mps11_pmic_probe(struct platform_device *pdev) return -EINVAL; }; + s2mps11->ext_control_gpio = devm_kzalloc(&pdev->dev, + sizeof(*s2mps11->ext_control_gpio) * s2mps11->rdev_num, + GFP_KERNEL); + if (!s2mps11->ext_control_gpio) + return -ENOMEM; + /* + * 0 is a valid GPIO so initialize all GPIO-s to negative value + * to indicate that external control won't be used for this regulator. + */ + for (i = 0; i < s2mps11->rdev_num; i++) + s2mps11->ext_control_gpio[i] = -EINVAL; + if (!iodev->dev->of_node) { - if (pdata) { + if (iodev->pdata) { + pdata = iodev->pdata; goto common_reg; } else { dev_err(pdev->dev.parent, @@ -614,15 +686,9 @@ static int s2mps11_pmic_probe(struct platform_device *pdev) for (i = 0; i < s2mps11->rdev_num; i++) rdata[i].name = regulators[i].name; - reg_np = of_get_child_by_name(iodev->dev->of_node, "regulators"); - if (!reg_np) { - dev_err(&pdev->dev, "could not find regulators sub-node\n"); - ret = -EINVAL; + ret = s2mps11_pmic_dt_parse(pdev, rdata, s2mps11, dev_type); + if (ret) goto out; - } - - of_regulator_match(&pdev->dev, reg_np, rdata, s2mps11->rdev_num); - of_node_put(reg_np); common_reg: platform_set_drvdata(pdev, s2mps11); @@ -630,16 +696,18 @@ common_reg: config.dev = &pdev->dev; config.regmap = iodev->regmap_pmic; config.driver_data = s2mps11; + config.ena_gpio_flags = GPIOF_OUT_INIT_HIGH; for (i = 0; i < s2mps11->rdev_num; i++) { struct regulator_dev *regulator; - if (!reg_np) { + if (pdata) { config.init_data = pdata->regulators[i].initdata; config.of_node = pdata->regulators[i].reg_node; } else { config.init_data = rdata[i].init_data; config.of_node = rdata[i].of_node; } + config.ena_gpio = s2mps11->ext_control_gpio[i]; regulator = devm_regulator_register(&pdev->dev, ®ulators[i], &config); @@ -649,6 +717,17 @@ common_reg: i); goto out; } + + if (gpio_is_valid(s2mps11->ext_control_gpio[i])) { + ret = s2mps14_pmic_enable_ext_control(s2mps11, + regulator); + if (ret < 0) { + dev_err(&pdev->dev, + "failed to enable GPIO control over %s: %d\n", + regulator->desc->name, ret); + goto out; + } + } } out: diff --git a/include/linux/mfd/samsung/s2mps14.h b/include/linux/mfd/samsung/s2mps14.h index 4b449b8ac548..900cd7a04314 100644 --- a/include/linux/mfd/samsung/s2mps14.h +++ b/include/linux/mfd/samsung/s2mps14.h @@ -148,6 +148,8 @@ enum s2mps14_regulators { #define S2MPS14_ENABLE_SHIFT 6 /* On/Off controlled by PWREN */ #define S2MPS14_ENABLE_SUSPEND (0x01 << S2MPS14_ENABLE_SHIFT) +/* On/Off controlled by LDO10EN or EMMCEN */ +#define S2MPS14_ENABLE_EXT_CONTROL (0x00 << S2MPS14_ENABLE_SHIFT) #define S2MPS14_LDO_N_VOLTAGES (S2MPS14_LDO_VSEL_MASK + 1) #define S2MPS14_BUCK_N_VOLTAGES (S2MPS14_BUCK_VSEL_MASK + 1)