diff options
Diffstat (limited to 'drivers/regulator')
36 files changed, 1683 insertions, 555 deletions
diff --git a/drivers/regulator/88pm800.c b/drivers/regulator/88pm800.c index 7fd4f511d78f..a62a89674fb5 100644 --- a/drivers/regulator/88pm800.c +++ b/drivers/regulator/88pm800.c @@ -78,7 +78,6 @@ struct pm800_regulator_info { }; struct pm800_regulators { - struct regulator_dev *regulators[PM800_ID_RG_MAX]; struct pm80x_chip *chip; struct regmap *map; }; @@ -92,14 +91,16 @@ struct pm800_regulators { * not the constant voltage table. * n_volt - Number of available selectors */ -#define PM800_BUCK(vreg, ereg, ebit, amax, volt_ranges, n_volt) \ +#define PM800_BUCK(match, vreg, ereg, ebit, amax, volt_ranges, n_volt) \ { \ .desc = { \ - .name = #vreg, \ - .ops = &pm800_volt_range_ops, \ - .type = REGULATOR_VOLTAGE, \ - .id = PM800_ID_##vreg, \ - .owner = THIS_MODULE, \ + .name = #vreg, \ + .of_match = of_match_ptr(#match), \ + .regulators_node = of_match_ptr("regulators"), \ + .ops = &pm800_volt_range_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = PM800_ID_##vreg, \ + .owner = THIS_MODULE, \ .n_voltages = n_volt, \ .linear_ranges = volt_ranges, \ .n_linear_ranges = ARRAY_SIZE(volt_ranges), \ @@ -108,7 +109,7 @@ struct pm800_regulators { .enable_reg = PM800_##ereg, \ .enable_mask = 1 << (ebit), \ }, \ - .max_ua = (amax), \ + .max_ua = (amax), \ } /* @@ -120,22 +121,24 @@ struct pm800_regulators { * For all the LDOes, there are too many ranges. Using volt_table will be * simpler and faster. */ -#define PM800_LDO(vreg, ereg, ebit, amax, ldo_volt_table) \ +#define PM800_LDO(match, vreg, ereg, ebit, amax, ldo_volt_table) \ { \ .desc = { \ - .name = #vreg, \ - .ops = &pm800_volt_table_ops, \ - .type = REGULATOR_VOLTAGE, \ - .id = PM800_ID_##vreg, \ - .owner = THIS_MODULE, \ - .n_voltages = ARRAY_SIZE(ldo_volt_table), \ - .vsel_reg = PM800_##vreg##_VOUT, \ - .vsel_mask = 0xf, \ - .enable_reg = PM800_##ereg, \ - .enable_mask = 1 << (ebit), \ - .volt_table = ldo_volt_table, \ + .name = #vreg, \ + .of_match = of_match_ptr(#match), \ + .regulators_node = of_match_ptr("regulators"), \ + .ops = &pm800_volt_table_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = PM800_ID_##vreg, \ + .owner = THIS_MODULE, \ + .n_voltages = ARRAY_SIZE(ldo_volt_table), \ + .vsel_reg = PM800_##vreg##_VOUT, \ + .vsel_mask = 0xf, \ + .enable_reg = PM800_##ereg, \ + .enable_mask = 1 << (ebit), \ + .volt_table = ldo_volt_table, \ }, \ - .max_ua = (amax), \ + .max_ua = (amax), \ } /* Ranges are sorted in ascending order. */ @@ -178,122 +181,66 @@ static int pm800_get_current_limit(struct regulator_dev *rdev) } static struct regulator_ops pm800_volt_range_ops = { - .list_voltage = regulator_list_voltage_linear_range, - .map_voltage = regulator_map_voltage_linear_range, - .set_voltage_sel = regulator_set_voltage_sel_regmap, - .get_voltage_sel = regulator_get_voltage_sel_regmap, - .enable = regulator_enable_regmap, - .disable = regulator_disable_regmap, - .is_enabled = regulator_is_enabled_regmap, - .get_current_limit = pm800_get_current_limit, + .list_voltage = regulator_list_voltage_linear_range, + .map_voltage = regulator_map_voltage_linear_range, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .get_current_limit = pm800_get_current_limit, }; static struct regulator_ops pm800_volt_table_ops = { - .list_voltage = regulator_list_voltage_table, - .map_voltage = regulator_map_voltage_iterate, - .set_voltage_sel = regulator_set_voltage_sel_regmap, - .get_voltage_sel = regulator_get_voltage_sel_regmap, - .enable = regulator_enable_regmap, - .disable = regulator_disable_regmap, - .is_enabled = regulator_is_enabled_regmap, - .get_current_limit = pm800_get_current_limit, + .list_voltage = regulator_list_voltage_table, + .map_voltage = regulator_map_voltage_iterate, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .get_current_limit = pm800_get_current_limit, }; /* The array is indexed by id(PM800_ID_XXX) */ static struct pm800_regulator_info pm800_regulator_info[] = { - PM800_BUCK(BUCK1, BUCK_ENA, 0, 3000000, buck1_volt_range, 0x55), - PM800_BUCK(BUCK2, BUCK_ENA, 1, 1200000, buck2_5_volt_range, 0x73), - PM800_BUCK(BUCK3, BUCK_ENA, 2, 1200000, buck2_5_volt_range, 0x73), - PM800_BUCK(BUCK4, BUCK_ENA, 3, 1200000, buck2_5_volt_range, 0x73), - PM800_BUCK(BUCK5, BUCK_ENA, 4, 1200000, buck2_5_volt_range, 0x73), - - PM800_LDO(LDO1, LDO_ENA1_1, 0, 200000, ldo1_volt_table), - PM800_LDO(LDO2, LDO_ENA1_1, 1, 10000, ldo2_volt_table), - PM800_LDO(LDO3, LDO_ENA1_1, 2, 300000, ldo3_17_volt_table), - PM800_LDO(LDO4, LDO_ENA1_1, 3, 300000, ldo3_17_volt_table), - PM800_LDO(LDO5, LDO_ENA1_1, 4, 300000, ldo3_17_volt_table), - PM800_LDO(LDO6, LDO_ENA1_1, 5, 300000, ldo3_17_volt_table), - PM800_LDO(LDO7, LDO_ENA1_1, 6, 300000, ldo3_17_volt_table), - PM800_LDO(LDO8, LDO_ENA1_1, 7, 300000, ldo3_17_volt_table), - PM800_LDO(LDO9, LDO_ENA1_2, 0, 300000, ldo3_17_volt_table), - PM800_LDO(LDO10, LDO_ENA1_2, 1, 300000, ldo3_17_volt_table), - PM800_LDO(LDO11, LDO_ENA1_2, 2, 300000, ldo3_17_volt_table), - PM800_LDO(LDO12, LDO_ENA1_2, 3, 300000, ldo3_17_volt_table), - PM800_LDO(LDO13, LDO_ENA1_2, 4, 300000, ldo3_17_volt_table), - PM800_LDO(LDO14, LDO_ENA1_2, 5, 300000, ldo3_17_volt_table), - PM800_LDO(LDO15, LDO_ENA1_2, 6, 300000, ldo3_17_volt_table), - PM800_LDO(LDO16, LDO_ENA1_2, 7, 300000, ldo3_17_volt_table), - PM800_LDO(LDO17, LDO_ENA1_3, 0, 300000, ldo3_17_volt_table), - PM800_LDO(LDO18, LDO_ENA1_3, 1, 200000, ldo18_19_volt_table), - PM800_LDO(LDO19, LDO_ENA1_3, 2, 200000, ldo18_19_volt_table), -}; - -#define PM800_REGULATOR_OF_MATCH(_name, _id) \ - [PM800_ID_##_id] = { \ - .name = #_name, \ - .driver_data = &pm800_regulator_info[PM800_ID_##_id], \ - } - -static struct of_regulator_match pm800_regulator_matches[] = { - PM800_REGULATOR_OF_MATCH(buck1, BUCK1), - PM800_REGULATOR_OF_MATCH(buck2, BUCK2), - PM800_REGULATOR_OF_MATCH(buck3, BUCK3), - PM800_REGULATOR_OF_MATCH(buck4, BUCK4), - PM800_REGULATOR_OF_MATCH(buck5, BUCK5), - PM800_REGULATOR_OF_MATCH(ldo1, LDO1), - PM800_REGULATOR_OF_MATCH(ldo2, LDO2), - PM800_REGULATOR_OF_MATCH(ldo3, LDO3), - PM800_REGULATOR_OF_MATCH(ldo4, LDO4), - PM800_REGULATOR_OF_MATCH(ldo5, LDO5), - PM800_REGULATOR_OF_MATCH(ldo6, LDO6), - PM800_REGULATOR_OF_MATCH(ldo7, LDO7), - PM800_REGULATOR_OF_MATCH(ldo8, LDO8), - PM800_REGULATOR_OF_MATCH(ldo9, LDO9), - PM800_REGULATOR_OF_MATCH(ldo10, LDO10), - PM800_REGULATOR_OF_MATCH(ldo11, LDO11), - PM800_REGULATOR_OF_MATCH(ldo12, LDO12), - PM800_REGULATOR_OF_MATCH(ldo13, LDO13), - PM800_REGULATOR_OF_MATCH(ldo14, LDO14), - PM800_REGULATOR_OF_MATCH(ldo15, LDO15), - PM800_REGULATOR_OF_MATCH(ldo16, LDO16), - PM800_REGULATOR_OF_MATCH(ldo17, LDO17), - PM800_REGULATOR_OF_MATCH(ldo18, LDO18), - PM800_REGULATOR_OF_MATCH(ldo19, LDO19), + PM800_BUCK(buck1, BUCK1, BUCK_ENA, 0, 3000000, buck1_volt_range, 0x55), + PM800_BUCK(buck2, BUCK2, BUCK_ENA, 1, 1200000, buck2_5_volt_range, 0x73), + PM800_BUCK(buck3, BUCK3, BUCK_ENA, 2, 1200000, buck2_5_volt_range, 0x73), + PM800_BUCK(buck4, BUCK4, BUCK_ENA, 3, 1200000, buck2_5_volt_range, 0x73), + PM800_BUCK(buck5, BUCK5, BUCK_ENA, 4, 1200000, buck2_5_volt_range, 0x73), + + PM800_LDO(ldo1, LDO1, LDO_ENA1_1, 0, 200000, ldo1_volt_table), + PM800_LDO(ldo2, LDO2, LDO_ENA1_1, 1, 10000, ldo2_volt_table), + PM800_LDO(ldo3, LDO3, LDO_ENA1_1, 2, 300000, ldo3_17_volt_table), + PM800_LDO(ldo4, LDO4, LDO_ENA1_1, 3, 300000, ldo3_17_volt_table), + PM800_LDO(ldo5, LDO5, LDO_ENA1_1, 4, 300000, ldo3_17_volt_table), + PM800_LDO(ldo6, LDO6, LDO_ENA1_1, 5, 300000, ldo3_17_volt_table), + PM800_LDO(ldo7, LDO7, LDO_ENA1_1, 6, 300000, ldo3_17_volt_table), + PM800_LDO(ldo8, LDO8, LDO_ENA1_1, 7, 300000, ldo3_17_volt_table), + PM800_LDO(ldo9, LDO9, LDO_ENA1_2, 0, 300000, ldo3_17_volt_table), + PM800_LDO(ldo10, LDO10, LDO_ENA1_2, 1, 300000, ldo3_17_volt_table), + PM800_LDO(ldo11, LDO11, LDO_ENA1_2, 2, 300000, ldo3_17_volt_table), + PM800_LDO(ldo12, LDO12, LDO_ENA1_2, 3, 300000, ldo3_17_volt_table), + PM800_LDO(ldo13, LDO13, LDO_ENA1_2, 4, 300000, ldo3_17_volt_table), + PM800_LDO(ldo14, LDO14, LDO_ENA1_2, 5, 300000, ldo3_17_volt_table), + PM800_LDO(ldo15, LDO15, LDO_ENA1_2, 6, 300000, ldo3_17_volt_table), + PM800_LDO(ldo16, LDO16, LDO_ENA1_2, 7, 300000, ldo3_17_volt_table), + PM800_LDO(ldo17, LDO17, LDO_ENA1_3, 0, 300000, ldo3_17_volt_table), + PM800_LDO(ldo18, LDO18, LDO_ENA1_3, 1, 200000, ldo18_19_volt_table), + PM800_LDO(ldo19, LDO19, LDO_ENA1_3, 2, 200000, ldo18_19_volt_table), }; -static int pm800_regulator_dt_init(struct platform_device *pdev) -{ - struct device_node *np = pdev->dev.of_node; - int ret; - - ret = of_regulator_match(&pdev->dev, np, - pm800_regulator_matches, - ARRAY_SIZE(pm800_regulator_matches)); - if (ret < 0) - return ret; - - return 0; -} - static int pm800_regulator_probe(struct platform_device *pdev) { struct pm80x_chip *chip = dev_get_drvdata(pdev->dev.parent); struct pm80x_platform_data *pdata = dev_get_platdata(pdev->dev.parent); struct pm800_regulators *pm800_data; - struct pm800_regulator_info *info; struct regulator_config config = { }; struct regulator_init_data *init_data; int i, ret; - if (!pdata || pdata->num_regulators == 0) { - if (IS_ENABLED(CONFIG_OF)) { - ret = pm800_regulator_dt_init(pdev); - if (ret) - return ret; - } else { - return -ENODEV; - } - } else if (pdata->num_regulators) { + if (pdata && pdata->num_regulators) { unsigned int count = 0; /* Check whether num_regulator is valid. */ @@ -303,8 +250,6 @@ static int pm800_regulator_probe(struct platform_device *pdev) } if (count != pdata->num_regulators) return -EINVAL; - } else { - return -EINVAL; } pm800_data = devm_kzalloc(&pdev->dev, sizeof(*pm800_data), @@ -317,30 +262,27 @@ static int pm800_regulator_probe(struct platform_device *pdev) platform_set_drvdata(pdev, pm800_data); + config.dev = chip->dev; + config.regmap = pm800_data->map; for (i = 0; i < PM800_ID_RG_MAX; i++) { - if (!pdata || pdata->num_regulators == 0) - init_data = pm800_regulator_matches[i].init_data; - else + struct regulator_dev *regulator; + + if (pdata && pdata->num_regulators) { init_data = pdata->regulators[i]; - if (!init_data) - continue; - info = pm800_regulator_matches[i].driver_data; - config.dev = &pdev->dev; - config.init_data = init_data; - config.driver_data = info; - config.regmap = pm800_data->map; - config.of_node = pm800_regulator_matches[i].of_node; - - pm800_data->regulators[i] = - regulator_register(&info->desc, &config); - if (IS_ERR(pm800_data->regulators[i])) { - ret = PTR_ERR(pm800_data->regulators[i]); - dev_err(&pdev->dev, "Failed to register %s\n", - info->desc.name); + if (!init_data) + continue; - while (--i >= 0) - regulator_unregister(pm800_data->regulators[i]); + config.init_data = init_data; + } + + config.driver_data = &pm800_regulator_info[i]; + regulator = devm_regulator_register(&pdev->dev, + &pm800_regulator_info[i].desc, &config); + if (IS_ERR(regulator)) { + ret = PTR_ERR(regulator); + dev_err(&pdev->dev, "Failed to register %s\n", + pm800_regulator_info[i].desc.name); return ret; } } @@ -348,23 +290,11 @@ static int pm800_regulator_probe(struct platform_device *pdev) return 0; } -static int pm800_regulator_remove(struct platform_device *pdev) -{ - struct pm800_regulators *pm800_data = platform_get_drvdata(pdev); - int i; - - for (i = 0; i < PM800_ID_RG_MAX; i++) - regulator_unregister(pm800_data->regulators[i]); - - return 0; -} - static struct platform_driver pm800_regulator_driver = { .driver = { .name = "88pm80x-regulator", }, .probe = pm800_regulator_probe, - .remove = pm800_regulator_remove, }; module_platform_driver(pm800_regulator_driver); diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index bef3bde6971b..64bccff557be 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -209,13 +209,13 @@ config REGULATOR_DA9210 interface. config REGULATOR_DA9211 - tristate "Dialog Semiconductor DA9211/DA9212/DA9213/DA9214 regulator" + tristate "Dialog Semiconductor DA9211/DA9212/DA9213/DA9214/DA9215 regulator" depends on I2C select REGMAP_I2C help Say y here to support for the Dialog Semiconductor DA9211/DA9212 - /DA9213/DA9214. - The DA9211/DA9212/DA9213/DA9214 is a multi-phase synchronous + /DA9213/DA9214/DA9215. + The DA9211/DA9212/DA9213/DA9214/DA9215 is a multi-phase synchronous step down converter 12A or 16A DC-DC Buck controlled through an I2C interface. @@ -407,13 +407,13 @@ config REGULATOR_MAX77686 Exynos-4 chips to control VARM and VINT voltages. config REGULATOR_MAX77693 - tristate "Maxim MAX77693 regulator" - depends on MFD_MAX77693 + tristate "Maxim 77693/77843 regulator" + depends on (MFD_MAX77693 || MFD_MAX77843) help - This driver controls a Maxim 77693 regulator via I2C bus. + This driver controls a Maxim 77693/77843 regulators via I2C bus. The regulators include two LDOs, 'SAFEOUT1', 'SAFEOUT2' and one current regulator 'CHARGER'. This is suitable for - Exynos-4x12 chips. + Exynos-4x12 (MAX77693) or Exynos5433 (MAX77843) SoC chips. config REGULATOR_MAX77802 tristate "Maxim 77802 regulator" @@ -424,14 +424,6 @@ config REGULATOR_MAX77802 Exynos5420/Exynos5800 SoCs to control various voltages. It includes support for control of voltage and ramp speed. -config REGULATOR_MAX77843 - tristate "Maxim 77843 regulator" - depends on MFD_MAX77843 - help - This driver controls a Maxim 77843 regulator. - The regulator include two 'SAFEOUT' for USB(Universal Serial Bus) - This is suitable for Exynos5433 SoC chips. - config REGULATOR_MC13XXX_CORE tristate @@ -451,6 +443,15 @@ config REGULATOR_MC13892 Say y here to support the regulators found on the Freescale MC13892 PMIC. +config REGULATOR_MT6311 + tristate "MediaTek MT6311 PMIC" + depends on I2C + help + Say y here to select this option to enable the power regulator of + MediaTek MT6311 PMIC. + This driver supports the control of different power rails of device + through regulator interface. + config REGULATOR_MT6397 tristate "MediaTek MT6397 PMIC" depends on MFD_MT6397 @@ -522,6 +523,18 @@ config REGULATOR_QCOM_RPM Qualcomm RPM as a module. The module will be named "qcom_rpm-regulator". +config REGULATOR_QCOM_SMD_RPM + tristate "Qualcomm SMD based RPM regulator driver" + depends on QCOM_SMD_RPM + help + If you say yes to this option, support will be included for the + regulators exposed by the Resource Power Manager found in Qualcomm + 8974 based devices. + + Say M here if you want to include support for the regulators on the + Qualcomm RPM as a module. The module will be named + "qcom_smd-regulator". + config REGULATOR_QCOM_SPMI tristate "Qualcomm SPMI regulator driver" depends on SPMI || COMPILE_TEST diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 91bf76267404..0f8174913c17 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -56,12 +56,13 @@ obj-$(CONFIG_REGULATOR_MAX8998) += max8998.o obj-$(CONFIG_REGULATOR_MAX77686) += max77686.o obj-$(CONFIG_REGULATOR_MAX77693) += max77693.o obj-$(CONFIG_REGULATOR_MAX77802) += max77802.o -obj-$(CONFIG_REGULATOR_MAX77843) += max77843.o obj-$(CONFIG_REGULATOR_MC13783) += mc13783-regulator.o obj-$(CONFIG_REGULATOR_MC13892) += mc13892-regulator.o obj-$(CONFIG_REGULATOR_MC13XXX_CORE) += mc13xxx-regulator-core.o +obj-$(CONFIG_REGULATOR_MT6311) += mt6311-regulator.o obj-$(CONFIG_REGULATOR_MT6397) += mt6397-regulator.o obj-$(CONFIG_REGULATOR_QCOM_RPM) += qcom_rpm-regulator.o +obj-$(CONFIG_REGULATOR_QCOM_SMD_RPM) += qcom_smd-regulator.o obj-$(CONFIG_REGULATOR_QCOM_SPMI) += qcom_spmi-regulator.o obj-$(CONFIG_REGULATOR_PALMAS) += palmas-regulator.o obj-$(CONFIG_REGULATOR_PFUZE100) += pfuze100-regulator.o diff --git a/drivers/regulator/act8865-regulator.c b/drivers/regulator/act8865-regulator.c index 2ff73d72ca34..896db168e4bd 100644 --- a/drivers/regulator/act8865-regulator.c +++ b/drivers/regulator/act8865-regulator.c @@ -530,7 +530,6 @@ MODULE_DEVICE_TABLE(i2c, act8865_ids); static struct i2c_driver act8865_pmic_driver = { .driver = { .name = "act8865", - .owner = THIS_MODULE, }, .probe = act8865_pmic_probe, .id_table = act8865_ids, diff --git a/drivers/regulator/ad5398.c b/drivers/regulator/ad5398.c index 48016a050d5f..ea50a886ba63 100644 --- a/drivers/regulator/ad5398.c +++ b/drivers/regulator/ad5398.c @@ -275,4 +275,3 @@ module_exit(ad5398_exit); MODULE_DESCRIPTION("AD5398 and AD5821 current regulator driver"); MODULE_AUTHOR("Sonic Zhang"); MODULE_LICENSE("GPL"); -MODULE_ALIAS("i2c:ad5398-regulator"); diff --git a/drivers/regulator/axp20x-regulator.c b/drivers/regulator/axp20x-regulator.c index 646829132b59..01bf3476a791 100644 --- a/drivers/regulator/axp20x-regulator.c +++ b/drivers/regulator/axp20x-regulator.c @@ -405,3 +405,4 @@ module_platform_driver(axp20x_regulator_driver); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Carlo Caione <carlo@caione.org>"); MODULE_DESCRIPTION("Regulator Driver for AXP20X PMIC"); +MODULE_ALIAS("platform:axp20x-regulator"); diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 78387a6cbae5..de9f272a0faf 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -111,6 +111,11 @@ static struct regulator *create_regulator(struct regulator_dev *rdev, const char *supply_name); static void _regulator_put(struct regulator *regulator); +static struct regulator_dev *dev_to_rdev(struct device *dev) +{ + return container_of(dev, struct regulator_dev, dev); +} + static const char *rdev_get_name(struct regulator_dev *rdev) { if (rdev->constraints && rdev->constraints->name) @@ -296,7 +301,7 @@ static int regulator_check_drms(struct regulator_dev *rdev) return -ENODEV; } if (!(rdev->constraints->valid_ops_mask & REGULATOR_CHANGE_DRMS)) { - rdev_err(rdev, "operation not allowed\n"); + rdev_dbg(rdev, "operation not allowed\n"); return -EPERM; } return 0; @@ -641,6 +646,8 @@ static int drms_uA_update(struct regulator_dev *rdev) int current_uA = 0, output_uV, input_uV, err; unsigned int mode; + lockdep_assert_held_once(&rdev->mutex); + /* * first check to see if we can set modes at all, otherwise just * tell the consumer everything is OK. @@ -761,6 +768,8 @@ 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; @@ -1082,6 +1091,15 @@ static int set_machine_constraints(struct regulator_dev *rdev, } } + if (rdev->constraints->over_current_protection + && ops->set_over_current_protection) { + ret = ops->set_over_current_protection(rdev); + if (ret < 0) { + rdev_err(rdev, "failed to set over current protection\n"); + goto out; + } + } + print_constraints(rdev); return 0; out: @@ -1595,9 +1613,11 @@ static void _regulator_put(struct regulator *regulator) { struct regulator_dev *rdev; - if (regulator == NULL || IS_ERR(regulator)) + if (IS_ERR_OR_NULL(regulator)) return; + lockdep_assert_held_once(®ulator_list_mutex); + rdev = regulator->rdev; debugfs_remove_recursive(regulator->debugfs); @@ -1606,14 +1626,15 @@ static void _regulator_put(struct regulator *regulator) if (regulator->dev) sysfs_remove_link(&rdev->dev.kobj, regulator->supply_name); mutex_lock(&rdev->mutex); - kfree(regulator->supply_name); list_del(®ulator->list); - kfree(regulator); rdev->open_count--; rdev->exclusive = 0; mutex_unlock(&rdev->mutex); + kfree(regulator->supply_name); + kfree(regulator); + module_put(rdev->owner); } @@ -1976,6 +1997,8 @@ static int _regulator_enable(struct regulator_dev *rdev) { int ret; + lockdep_assert_held_once(&rdev->mutex); + /* check voltage and requested load before enabling */ if (rdev->constraints && (rdev->constraints->valid_ops_mask & REGULATOR_CHANGE_DRMS)) @@ -2076,6 +2099,8 @@ static int _regulator_disable(struct regulator_dev *rdev) { int ret = 0; + lockdep_assert_held_once(&rdev->mutex); + if (WARN(rdev->use_count <= 0, "unbalanced disables for %s\n", rdev_get_name(rdev))) return -EIO; @@ -2154,6 +2179,8 @@ static int _regulator_force_disable(struct regulator_dev *rdev) { int ret = 0; + lockdep_assert_held_once(&rdev->mutex); + ret = _notifier_call_chain(rdev, REGULATOR_EVENT_FORCE_DISABLE | REGULATOR_EVENT_PRE_DISABLE, NULL); if (ret & NOTIFY_STOP_MASK) @@ -2722,7 +2749,7 @@ int regulator_set_voltage(struct regulator *regulator, int min_uV, int max_uV) goto out; /* If we're trying to set a range that overlaps the current voltage, - * return succesfully even though the regulator does not support + * return successfully even though the regulator does not support * changing the voltage. */ if (!(rdev->constraints->valid_ops_mask & REGULATOR_CHANGE_VOLTAGE)) { @@ -3450,6 +3477,8 @@ EXPORT_SYMBOL_GPL(regulator_bulk_free); int regulator_notifier_call_chain(struct regulator_dev *rdev, unsigned long event, void *data) { + lockdep_assert_held_once(&rdev->mutex); + _notifier_call_chain(rdev, event, data); return NOTIFY_DONE; @@ -3594,6 +3623,9 @@ static const struct attribute_group *regulator_dev_groups[] = { static void regulator_dev_release(struct device *dev) { struct regulator_dev *rdev = dev_get_drvdata(dev); + + kfree(rdev->constraints); + of_node_put(rdev->dev.of_node); kfree(rdev); } @@ -3824,11 +3856,9 @@ void regulator_unregister(struct regulator_dev *rdev) WARN_ON(rdev->open_count); unset_regulator_supplies(rdev); list_del(&rdev->list); - kfree(rdev->constraints); + mutex_unlock(®ulator_list_mutex); regulator_ena_gpio_free(rdev); - of_node_put(rdev->dev.of_node); device_unregister(&rdev->dev); - mutex_unlock(®ulator_list_mutex); } EXPORT_SYMBOL_GPL(regulator_unregister); @@ -4147,13 +4177,57 @@ static int __init regulator_init(void) /* init early to allow our consumers to complete system booting */ core_initcall(regulator_init); -static int __init regulator_init_complete(void) +static int __init regulator_late_cleanup(struct device *dev, void *data) { - struct regulator_dev *rdev; - const struct regulator_ops *ops; - struct regulation_constraints *c; + struct regulator_dev *rdev = dev_to_rdev(dev); + const struct regulator_ops *ops = rdev->desc->ops; + struct regulation_constraints *c = rdev->constraints; int enabled, ret; + if (c && c->always_on) + return 0; + + if (c && !(c->valid_ops_mask & REGULATOR_CHANGE_STATUS)) + return 0; + + mutex_lock(&rdev->mutex); + + if (rdev->use_count) + goto unlock; + + /* If we can't read the status assume it's on. */ + if (ops->is_enabled) + enabled = ops->is_enabled(rdev); + else + enabled = 1; + + if (!enabled) + goto unlock; + + if (have_full_constraints()) { + /* We log since this may kill the system if it goes + * wrong. */ + rdev_info(rdev, "disabling\n"); + ret = _regulator_do_disable(rdev); + if (ret != 0) + rdev_err(rdev, "couldn't disable: %d\n", ret); + } else { + /* The intention is that in future we will + * assume that full constraints are provided + * so warn even if we aren't going to do + * anything here. + */ + rdev_warn(rdev, "incomplete constraints, leaving on\n"); + } + +unlock: + mutex_unlock(&rdev->mutex); + + return 0; +} + +static int __init regulator_init_complete(void) +{ /* * Since DT doesn't provide an idiomatic mechanism for * enabling full constraints and since it's much more natural @@ -4163,58 +4237,13 @@ static int __init regulator_init_complete(void) if (of_have_populated_dt()) has_full_constraints = true; - mutex_lock(®ulator_list_mutex); - /* If we have a full configuration then disable any regulators * we have permission to change the status for and which are * not in use or always_on. This is effectively the default * for DT and ACPI as they have full constraints. */ - list_for_each_entry(rdev, ®ulator_list, list) { - ops = rdev->desc->ops; - c = rdev->constraints; - - if (c && c->always_on) - continue; - - if (c && !(c->valid_ops_mask & REGULATOR_CHANGE_STATUS)) - continue; - - mutex_lock(&rdev->mutex); - - if (rdev->use_count) - goto unlock; - - /* If we can't read the status assume it's on. */ - if (ops->is_enabled) - enabled = ops->is_enabled(rdev); - else - enabled = 1; - - if (!enabled) - goto unlock; - - if (have_full_constraints()) { - /* We log since this may kill the system if it - * goes wrong. */ - rdev_info(rdev, "disabling\n"); - ret = _regulator_do_disable(rdev); - if (ret != 0) - rdev_err(rdev, "couldn't disable: %d\n", ret); - } else { - /* The intention is that in future we will - * assume that full constraints are provided - * so warn even if we aren't going to do - * anything here. - */ - rdev_warn(rdev, "incomplete constraints, leaving on\n"); - } - -unlock: - mutex_unlock(&rdev->mutex); - } - - mutex_unlock(®ulator_list_mutex); + class_for_each_device(®ulator_class, NULL, NULL, + regulator_late_cleanup); return 0; } diff --git a/drivers/regulator/da9062-regulator.c b/drivers/regulator/da9062-regulator.c index dd76da09b3c7..5638fe8d759d 100644 --- a/drivers/regulator/da9062-regulator.c +++ b/drivers/regulator/da9062-regulator.c @@ -818,7 +818,6 @@ static int da9062_regulator_probe(struct platform_device *pdev) static struct platform_driver da9062_regulator_driver = { .driver = { .name = "da9062-regulators", - .owner = THIS_MODULE, }, .probe = da9062_regulator_probe, }; diff --git a/drivers/regulator/da9210-regulator.c b/drivers/regulator/da9210-regulator.c index f0489cb9018b..b3517830edb6 100644 --- a/drivers/regulator/da9210-regulator.c +++ b/drivers/regulator/da9210-regulator.c @@ -22,6 +22,8 @@ #include <linux/i2c.h> #include <linux/module.h> #include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/irq.h> #include <linux/slab.h> #include <linux/regulator/driver.h> #include <linux/regulator/machine.h> @@ -120,6 +122,55 @@ static int da9210_get_current_limit(struct regulator_dev *rdev) return da9210_buck_limits[sel]; } +static irqreturn_t da9210_irq_handler(int irq, void *data) +{ + struct da9210 *chip = data; + unsigned int val, handled = 0; + int error, ret = IRQ_NONE; + + error = regmap_read(chip->regmap, DA9210_REG_EVENT_B, &val); + if (error < 0) + goto error_i2c; + + if (val & DA9210_E_OVCURR) { + regulator_notifier_call_chain(chip->rdev, + REGULATOR_EVENT_OVER_CURRENT, + NULL); + handled |= DA9210_E_OVCURR; + } + if (val & DA9210_E_NPWRGOOD) { + regulator_notifier_call_chain(chip->rdev, + REGULATOR_EVENT_UNDER_VOLTAGE, + NULL); + handled |= DA9210_E_NPWRGOOD; + } + if (val & (DA9210_E_TEMP_WARN | DA9210_E_TEMP_CRIT)) { + regulator_notifier_call_chain(chip->rdev, + REGULATOR_EVENT_OVER_TEMP, NULL); + handled |= val & (DA9210_E_TEMP_WARN | DA9210_E_TEMP_CRIT); + } + if (val & DA9210_E_VMAX) { + regulator_notifier_call_chain(chip->rdev, + REGULATOR_EVENT_REGULATION_OUT, + NULL); + handled |= DA9210_E_VMAX; + } + if (handled) { + /* Clear handled events */ + error = regmap_write(chip->regmap, DA9210_REG_EVENT_B, handled); + if (error < 0) + goto error_i2c; + + ret = IRQ_HANDLED; + } + + return ret; + +error_i2c: + dev_err(regmap_get_device(chip->regmap), "I2C error : %d\n", error); + return ret; +} + /* * I2C driver interface functions */ @@ -168,6 +219,30 @@ static int da9210_i2c_probe(struct i2c_client *i2c, } chip->rdev = rdev; + if (i2c->irq) { + error = devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL, + da9210_irq_handler, + IRQF_TRIGGER_LOW | + IRQF_ONESHOT | IRQF_SHARED, + "da9210", chip); + if (error) { + dev_err(&i2c->dev, "Failed to request IRQ%u: %d\n", + i2c->irq, error); + return error; + } + + error = regmap_update_bits(chip->regmap, DA9210_REG_MASK_B, + DA9210_M_OVCURR | DA9210_M_NPWRGOOD | + DA9210_M_TEMP_WARN | + DA9210_M_TEMP_CRIT | DA9210_M_VMAX, 0); + if (error < 0) { + dev_err(&i2c->dev, "Failed to update mask reg: %d\n", + error); + return error; + } + } else { + dev_warn(&i2c->dev, "No IRQ configured\n"); + } i2c_set_clientdata(i2c, chip); @@ -184,7 +259,6 @@ MODULE_DEVICE_TABLE(i2c, da9210_i2c_id); static struct i2c_driver da9210_regulator_driver = { .driver = { .name = "da9210", - .owner = THIS_MODULE, }, .probe = da9210_i2c_probe, .id_table = da9210_i2c_id, diff --git a/drivers/regulator/da9211-regulator.c b/drivers/regulator/da9211-regulator.c index df79e4b1946e..04ef65b7eb3d 100644 --- a/drivers/regulator/da9211-regulator.c +++ b/drivers/regulator/da9211-regulator.c @@ -1,6 +1,6 @@ /* - * da9211-regulator.c - Regulator device driver for DA9211/DA9213 - * Copyright (C) 2014 Dialog Semiconductor Ltd. + * da9211-regulator.c - Regulator device driver for DA9211/DA9213/DA9215 + * Copyright (C) 2015 Dialog Semiconductor Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -32,6 +32,7 @@ /* DEVICE IDs */ #define DA9211_DEVICE_ID 0x22 #define DA9213_DEVICE_ID 0x23 +#define DA9215_DEVICE_ID 0x24 #define DA9211_BUCK_MODE_SLEEP 1 #define DA9211_BUCK_MODE_SYNC 2 @@ -90,6 +91,13 @@ static const int da9213_current_limits[] = { 3000000, 3200000, 3400000, 3600000, 3800000, 4000000, 4200000, 4400000, 4600000, 4800000, 5000000, 5200000, 5400000, 5600000, 5800000, 6000000 }; +/* Current limits for DA9215 buck (uA) indices + * corresponds with register values + */ +static const int da9215_current_limits[] = { + 4000000, 4200000, 4400000, 4600000, 4800000, 5000000, 5200000, 5400000, + 5600000, 5800000, 6000000, 6200000, 6400000, 6600000, 6800000, 7000000 +}; static unsigned int da9211_buck_get_mode(struct regulator_dev *rdev) { @@ -157,6 +165,10 @@ static int da9211_set_current_limit(struct regulator_dev *rdev, int min, current_limits = da9213_current_limits; max_size = ARRAY_SIZE(da9213_current_limits)-1; break; + case DA9215: + current_limits = da9215_current_limits; + max_size = ARRAY_SIZE(da9215_current_limits)-1; + break; default: return -EINVAL; } @@ -189,6 +201,9 @@ static int da9211_get_current_limit(struct regulator_dev *rdev) case DA9213: current_limits = da9213_current_limits; break; + case DA9215: + current_limits = da9215_current_limits; + break; default: return -EINVAL; } @@ -350,13 +365,11 @@ static int da9211_regulator_init(struct da9211 *chip) /* If configuration for 1/2 bucks is different between platform data * and the register, driver should exit. */ - if ((chip->pdata->num_buck == 2 && data == 0x40) - || (chip->pdata->num_buck == 1 && data == 0x00)) { - if (data == 0) - chip->num_regulator = 1; - else - chip->num_regulator = 2; - } else { + if (chip->pdata->num_buck == 1 && data == 0x00) + chip->num_regulator = 1; + else if (chip->pdata->num_buck == 2 && data != 0x00) + chip->num_regulator = 2; + else { dev_err(chip->dev, "Configuration is mismatched\n"); return -EINVAL; } @@ -438,6 +451,9 @@ static int da9211_i2c_probe(struct i2c_client *i2c, case DA9213_DEVICE_ID: chip->chip_id = DA9213; break; + case DA9215_DEVICE_ID: + chip->chip_id = DA9215; + break; default: dev_err(chip->dev, "Unsupported device id = 0x%x.\n", data); return -ENODEV; @@ -478,6 +494,7 @@ static int da9211_i2c_probe(struct i2c_client *i2c, static const struct i2c_device_id da9211_i2c_id[] = { {"da9211", DA9211}, {"da9213", DA9213}, + {"da9215", DA9215}, {}, }; MODULE_DEVICE_TABLE(i2c, da9211_i2c_id); @@ -486,6 +503,7 @@ MODULE_DEVICE_TABLE(i2c, da9211_i2c_id); static const struct of_device_id da9211_dt_ids[] = { { .compatible = "dlg,da9211", .data = &da9211_i2c_id[0] }, { .compatible = "dlg,da9213", .data = &da9211_i2c_id[1] }, + { .compatible = "dlg,da9215", .data = &da9211_i2c_id[2] }, {}, }; MODULE_DEVICE_TABLE(of, da9211_dt_ids); @@ -494,7 +512,6 @@ MODULE_DEVICE_TABLE(of, da9211_dt_ids); static struct i2c_driver da9211_regulator_driver = { .driver = { .name = "da9211", - .owner = THIS_MODULE, .of_match_table = of_match_ptr(da9211_dt_ids), }, .probe = da9211_i2c_probe, @@ -504,5 +521,5 @@ static struct i2c_driver da9211_regulator_driver = { module_i2c_driver(da9211_regulator_driver); MODULE_AUTHOR("James Ban <James.Ban.opensource@diasemi.com>"); -MODULE_DESCRIPTION("Regulator device driver for Dialog DA9211/DA9213"); -MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Regulator device driver for Dialog DA9211/DA9213/DA9215"); +MODULE_LICENSE("GPL"); diff --git a/drivers/regulator/da9211-regulator.h b/drivers/regulator/da9211-regulator.h index 93fa9df2721c..d6ad96fc64d3 100644 --- a/drivers/regulator/da9211-regulator.h +++ b/drivers/regulator/da9211-regulator.h @@ -1,16 +1,16 @@ /* - * da9211-regulator.h - Regulator definitions for DA9211/DA9213 - * Copyright (C) 2014 Dialog Semiconductor Ltd. + * da9211-regulator.h - Regulator definitions for DA9211/DA9213/DA9215 + * Copyright (C) 2015 Dialog Semiconductor Ltd. * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library 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 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 library is distributed in the hope that it will be useful, + * 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 - * Library General Public License for more details. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. */ #ifndef __DA9211_REGISTERS_H__ diff --git a/drivers/regulator/fan53555.c b/drivers/regulator/fan53555.c index 42865681c00b..4940e8287df6 100644 --- a/drivers/regulator/fan53555.c +++ b/drivers/regulator/fan53555.c @@ -439,6 +439,7 @@ static const struct i2c_device_id fan53555_id[] = { }, { }, }; +MODULE_DEVICE_TABLE(i2c, fan53555_id); static struct i2c_driver fan53555_regulator_driver = { .driver = { diff --git a/drivers/regulator/isl6271a-regulator.c b/drivers/regulator/isl6271a-regulator.c index 6e5da95fa025..4abd8e9c81e5 100644 --- a/drivers/regulator/isl6271a-regulator.c +++ b/drivers/regulator/isl6271a-regulator.c @@ -156,7 +156,6 @@ MODULE_DEVICE_TABLE(i2c, isl6271a_id); static struct i2c_driver isl6271a_i2c_driver = { .driver = { .name = "isl6271a", - .owner = THIS_MODULE, }, .probe = isl6271a_probe, .id_table = isl6271a_id, diff --git a/drivers/regulator/isl9305.c b/drivers/regulator/isl9305.c index 6e3a15fe00f1..257c1943e753 100644 --- a/drivers/regulator/isl9305.c +++ b/drivers/regulator/isl9305.c @@ -183,6 +183,7 @@ static const struct of_device_id isl9305_dt_ids[] = { { .compatible = "isil,isl9305h" }, {}, }; +MODULE_DEVICE_TABLE(of, isl9305_dt_ids); #endif static const struct i2c_device_id isl9305_i2c_id[] = { @@ -195,7 +196,6 @@ MODULE_DEVICE_TABLE(i2c, isl9305_i2c_id); static struct i2c_driver isl9305_regulator_driver = { .driver = { .name = "isl9305", - .owner = THIS_MODULE, .of_match_table = of_match_ptr(isl9305_dt_ids), }, .probe = isl9305_i2c_probe, diff --git a/drivers/regulator/lp3971.c b/drivers/regulator/lp3971.c index 66fd2330dca0..15c25c622edf 100644 --- a/drivers/regulator/lp3971.c +++ b/drivers/regulator/lp3971.c @@ -452,7 +452,6 @@ MODULE_DEVICE_TABLE(i2c, lp3971_i2c_id); static struct i2c_driver lp3971_i2c_driver = { .driver = { .name = "LP3971", - .owner = THIS_MODULE, }, .probe = lp3971_i2c_probe, .id_table = lp3971_i2c_id, diff --git a/drivers/regulator/lp3972.c b/drivers/regulator/lp3972.c index aea485afcc1a..3a7e96e2c7b3 100644 --- a/drivers/regulator/lp3972.c +++ b/drivers/regulator/lp3972.c @@ -550,7 +550,6 @@ MODULE_DEVICE_TABLE(i2c, lp3972_i2c_id); static struct i2c_driver lp3972_i2c_driver = { .driver = { .name = "lp3972", - .owner = THIS_MODULE, }, .probe = lp3972_i2c_probe, .id_table = lp3972_i2c_id, diff --git a/drivers/regulator/lp872x.c b/drivers/regulator/lp872x.c index 3de328ab41f3..e5af07208f9d 100644 --- a/drivers/regulator/lp872x.c +++ b/drivers/regulator/lp872x.c @@ -849,7 +849,7 @@ static struct lp872x_platform_data pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) - goto out; + return ERR_PTR(-ENOMEM); of_property_read_u8(np, "ti,general-config", &pdata->general_config); if (of_find_property(np, "ti,update-config", NULL)) @@ -857,7 +857,7 @@ static struct lp872x_platform_data pdata->dvs = devm_kzalloc(dev, sizeof(struct lp872x_dvs), GFP_KERNEL); if (!pdata->dvs) - goto out; + return ERR_PTR(-ENOMEM); pdata->dvs->gpio = of_get_named_gpio(np, "ti,dvs-gpio", 0); of_property_read_u8(np, "ti,dvs-vsel", (u8 *)&pdata->dvs->vsel); @@ -903,15 +903,21 @@ static struct lp872x_platform_data static int lp872x_probe(struct i2c_client *cl, const struct i2c_device_id *id) { struct lp872x *lp; + struct lp872x_platform_data *pdata; int ret; const int lp872x_num_regulators[] = { [LP8720] = LP8720_NUM_REGULATORS, [LP8725] = LP8725_NUM_REGULATORS, }; - if (cl->dev.of_node) - cl->dev.platform_data = lp872x_populate_pdata_from_dt(&cl->dev, + if (cl->dev.of_node) { + pdata = lp872x_populate_pdata_from_dt(&cl->dev, (enum lp872x_id)id->driver_data); + if (IS_ERR(pdata)) + return PTR_ERR(pdata); + } else { + pdata = dev_get_platdata(&cl->dev); + } lp = devm_kzalloc(&cl->dev, sizeof(struct lp872x), GFP_KERNEL); if (!lp) @@ -927,7 +933,7 @@ static int lp872x_probe(struct i2c_client *cl, const struct i2c_device_id *id) } lp->dev = &cl->dev; - lp->pdata = dev_get_platdata(&cl->dev); + lp->pdata = pdata; lp->chipid = id->driver_data; i2c_set_clientdata(cl, lp); @@ -955,7 +961,6 @@ MODULE_DEVICE_TABLE(i2c, lp872x_ids); static struct i2c_driver lp872x_driver = { .driver = { .name = "lp872x", - .owner = THIS_MODULE, .of_match_table = of_match_ptr(lp872x_dt_ids), }, .probe = lp872x_probe, diff --git a/drivers/regulator/ltc3589.c b/drivers/regulator/ltc3589.c index 0ce8e4e0fa73..972c386b2690 100644 --- a/drivers/regulator/ltc3589.c +++ b/drivers/regulator/ltc3589.c @@ -378,7 +378,7 @@ static bool ltc3589_volatile_reg(struct device *dev, unsigned int reg) return false; } -static struct reg_default ltc3589_reg_defaults[] = { +static const struct reg_default ltc3589_reg_defaults[] = { { LTC3589_SCR1, 0x00 }, { LTC3589_OVEN, 0x00 }, { LTC3589_SCR2, 0x00 }, @@ -542,7 +542,6 @@ MODULE_DEVICE_TABLE(i2c, ltc3589_i2c_id); static struct i2c_driver ltc3589_driver = { .driver = { .name = DRIVER_NAME, - .owner = THIS_MODULE, }, .probe = ltc3589_probe, .id_table = ltc3589_i2c_id, @@ -552,4 +551,3 @@ module_i2c_driver(ltc3589_driver); MODULE_AUTHOR("Philipp Zabel <p.zabel@pengutronix.de>"); MODULE_DESCRIPTION("Regulator driver for Linear Technology LTC3589(-1,2)"); MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("i2c:ltc3589"); diff --git a/drivers/regulator/max1586.c b/drivers/regulator/max1586.c index d2a8c64cae42..2c1228d5796a 100644 --- a/drivers/regulator/max1586.c +++ b/drivers/regulator/max1586.c @@ -304,7 +304,6 @@ static struct i2c_driver max1586_pmic_driver = { .probe = max1586_pmic_probe, .driver = { .name = "max1586", - .owner = THIS_MODULE, .of_match_table = of_match_ptr(max1586_of_match), }, .id_table = max1586_id, diff --git a/drivers/regulator/max77693.c b/drivers/regulator/max77693.c index 38722c8311a5..de730fd3f8a5 100644 --- a/drivers/regulator/max77693.c +++ b/drivers/regulator/max77693.c @@ -1,8 +1,9 @@ /* - * max77693.c - Regulator driver for the Maxim 77693 + * max77693.c - Regulator driver for the Maxim 77693 and 77843 * - * Copyright (C) 2013 Samsung Electronics + * Copyright (C) 2013-2015 Samsung Electronics * Jonghwa Lee <jonghwa3.lee@samsung.com> + * Krzysztof Kozlowski <k.kozlowski.k@gmail.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 @@ -29,38 +30,64 @@ #include <linux/regulator/driver.h> #include <linux/regulator/machine.h> #include <linux/mfd/max77693.h> +#include <linux/mfd/max77693-common.h> #include <linux/mfd/max77693-private.h> +#include <linux/mfd/max77843-private.h> #include <linux/regulator/of_regulator.h> #include <linux/regmap.h> -#define CHGIN_ILIM_STEP_20mA 20000 +/* + * ID for MAX77843 regulators. + * There is no need for such for MAX77693. + */ +enum max77843_regulator_type { + MAX77843_SAFEOUT1 = 0, + MAX77843_SAFEOUT2, + MAX77843_CHARGER, + + MAX77843_NUM, +}; + +/* Register differences between chargers: MAX77693 and MAX77843 */ +struct chg_reg_data { + unsigned int linear_reg; + unsigned int linear_mask; + unsigned int uA_step; + unsigned int min_sel; +}; /* - * CHARGER regulator - Min : 20mA, Max : 2580mA, step : 20mA + * MAX77693 CHARGER regulator - Min : 20mA, Max : 2580mA, step : 20mA * 0x00, 0x01, 0x2, 0x03 = 60 mA * 0x04 ~ 0x7E = (60 + (X - 3) * 20) mA + * Actually for MAX77693 the driver manipulates the maximum input current, + * not the fast charge current (output). This should be fixed. + * + * On MAX77843 the calculation formula is the same (except values). + * Fortunately it properly manipulates the fast charge current. */ static int max77693_chg_get_current_limit(struct regulator_dev *rdev) { + const struct chg_reg_data *reg_data = rdev_get_drvdata(rdev); unsigned int chg_min_uA = rdev->constraints->min_uA; unsigned int chg_max_uA = rdev->constraints->max_uA; unsigned int reg, sel; unsigned int val; int ret; - ret = regmap_read(rdev->regmap, MAX77693_CHG_REG_CHG_CNFG_09, ®); + ret = regmap_read(rdev->regmap, reg_data->linear_reg, ®); if (ret < 0) return ret; - sel = reg & CHG_CNFG_09_CHGIN_ILIM_MASK; + sel = reg & reg_data->linear_mask; /* the first four codes for charger current are all 60mA */ - if (sel <= 3) + if (sel <= reg_data->min_sel) sel = 0; else - sel -= 3; + sel -= reg_data->min_sel; - val = chg_min_uA + CHGIN_ILIM_STEP_20mA * sel; + val = chg_min_uA + reg_data->uA_step * sel; if (val > chg_max_uA) return -EINVAL; @@ -70,23 +97,43 @@ static int max77693_chg_get_current_limit(struct regulator_dev *rdev) static int max77693_chg_set_current_limit(struct regulator_dev *rdev, int min_uA, int max_uA) { + const struct chg_reg_data *reg_data = rdev_get_drvdata(rdev); unsigned int chg_min_uA = rdev->constraints->min_uA; int sel = 0; - while (chg_min_uA + CHGIN_ILIM_STEP_20mA * sel < min_uA) + while (chg_min_uA + reg_data->uA_step * sel < min_uA) sel++; - if (chg_min_uA + CHGIN_ILIM_STEP_20mA * sel > max_uA) + if (chg_min_uA + reg_data->uA_step * sel > max_uA) return -EINVAL; /* the first four codes for charger current are all 60mA */ - sel += 3; + sel += reg_data->min_sel; - return regmap_write(rdev->regmap, - MAX77693_CHG_REG_CHG_CNFG_09, sel); + return regmap_write(rdev->regmap, reg_data->linear_reg, sel); } /* end of CHARGER regulator ops */ +/* Returns regmap suitable for given regulator on chosen device */ +static struct regmap *max77693_get_regmap(enum max77693_types type, + struct max77693_dev *max77693, + int reg_id) +{ + if (type == TYPE_MAX77693) + return max77693->regmap; + + /* Else: TYPE_MAX77843 */ + switch (reg_id) { + case MAX77843_SAFEOUT1: + case MAX77843_SAFEOUT2: + return max77693->regmap; + case MAX77843_CHARGER: + return max77693->regmap_chg; + default: + return max77693->regmap; + } +} + static const unsigned int max77693_safeout_table[] = { 4850000, 4900000, @@ -111,7 +158,7 @@ static struct regulator_ops max77693_charger_ops = { .set_current_limit = max77693_chg_set_current_limit, }; -#define regulator_desc_esafeout(_num) { \ +#define max77693_regulator_desc_esafeout(_num) { \ .name = "ESAFEOUT"#_num, \ .id = MAX77693_ESAFEOUT##_num, \ .of_match = of_match_ptr("ESAFEOUT"#_num), \ @@ -127,9 +174,9 @@ static struct regulator_ops max77693_charger_ops = { .enable_mask = SAFEOUT_CTRL_ENSAFEOUT##_num##_MASK , \ } -static const struct regulator_desc regulators[] = { - regulator_desc_esafeout(1), - regulator_desc_esafeout(2), +static const struct regulator_desc max77693_supported_regulators[] = { + max77693_regulator_desc_esafeout(1), + max77693_regulator_desc_esafeout(2), { .name = "CHARGER", .id = MAX77693_CHARGER, @@ -145,18 +192,86 @@ static const struct regulator_desc regulators[] = { }, }; +static const struct chg_reg_data max77693_chg_reg_data = { + .linear_reg = MAX77693_CHG_REG_CHG_CNFG_09, + .linear_mask = CHG_CNFG_09_CHGIN_ILIM_MASK, + .uA_step = 20000, + .min_sel = 3, +}; + +#define max77843_regulator_desc_esafeout(num) { \ + .name = "SAFEOUT" # num, \ + .id = MAX77843_SAFEOUT ## num, \ + .ops = &max77693_safeout_ops, \ + .of_match = of_match_ptr("SAFEOUT" # num), \ + .regulators_node = of_match_ptr("regulators"), \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .n_voltages = ARRAY_SIZE(max77693_safeout_table), \ + .volt_table = max77693_safeout_table, \ + .enable_reg = MAX77843_SYS_REG_SAFEOUTCTRL, \ + .enable_mask = MAX77843_REG_SAFEOUTCTRL_ENSAFEOUT ## num, \ + .vsel_reg = MAX77843_SYS_REG_SAFEOUTCTRL, \ + .vsel_mask = MAX77843_REG_SAFEOUTCTRL_SAFEOUT ## num ## _MASK, \ +} + +static const struct regulator_desc max77843_supported_regulators[] = { + [MAX77843_SAFEOUT1] = max77843_regulator_desc_esafeout(1), + [MAX77843_SAFEOUT2] = max77843_regulator_desc_esafeout(2), + [MAX77843_CHARGER] = { + .name = "CHARGER", + .id = MAX77843_CHARGER, + .ops = &max77693_charger_ops, + .of_match = of_match_ptr("CHARGER"), + .regulators_node = of_match_ptr("regulators"), + .type = REGULATOR_CURRENT, + .owner = THIS_MODULE, + .enable_reg = MAX77843_CHG_REG_CHG_CNFG_00, + .enable_mask = MAX77843_CHG_MASK, + .enable_val = MAX77843_CHG_MASK, + }, +}; + +static const struct chg_reg_data max77843_chg_reg_data = { + .linear_reg = MAX77843_CHG_REG_CHG_CNFG_02, + .linear_mask = MAX77843_CHG_FAST_CHG_CURRENT_MASK, + .uA_step = MAX77843_CHG_FAST_CHG_CURRENT_STEP, + .min_sel = 2, +}; + static int max77693_pmic_probe(struct platform_device *pdev) { + enum max77693_types type = platform_get_device_id(pdev)->driver_data; struct max77693_dev *iodev = dev_get_drvdata(pdev->dev.parent); + const struct regulator_desc *regulators; + unsigned int regulators_size; int i; struct regulator_config config = { }; config.dev = iodev->dev; - config.regmap = iodev->regmap; - for (i = 0; i < ARRAY_SIZE(regulators); i++) { + switch (type) { + case TYPE_MAX77693: + regulators = max77693_supported_regulators; + regulators_size = ARRAY_SIZE(max77693_supported_regulators); + config.driver_data = (void *)&max77693_chg_reg_data; + break; + case TYPE_MAX77843: + regulators = max77843_supported_regulators; + regulators_size = ARRAY_SIZE(max77843_supported_regulators); + config.driver_data = (void *)&max77843_chg_reg_data; + break; + default: + dev_err(&pdev->dev, "Unsupported device type: %u\n", type); + return -ENODEV; + } + + for (i = 0; i < regulators_size; i++) { struct regulator_dev *rdev; + config.regmap = max77693_get_regmap(type, iodev, + regulators[i].id); + rdev = devm_regulator_register(&pdev->dev, ®ulators[i], &config); if (IS_ERR(rdev)) { @@ -170,7 +285,8 @@ static int max77693_pmic_probe(struct platform_device *pdev) } static const struct platform_device_id max77693_pmic_id[] = { - {"max77693-pmic", 0}, + { "max77693-pmic", TYPE_MAX77693 }, + { "max77843-regulator", TYPE_MAX77843 }, {}, }; @@ -184,8 +300,19 @@ static struct platform_driver max77693_pmic_driver = { .id_table = max77693_pmic_id, }; -module_platform_driver(max77693_pmic_driver); +static int __init max77693_pmic_init(void) +{ + return platform_driver_register(&max77693_pmic_driver); +} +subsys_initcall(max77693_pmic_init); + +static void __exit max77693_pmic_cleanup(void) +{ + platform_driver_unregister(&max77693_pmic_driver); +} +module_exit(max77693_pmic_cleanup); -MODULE_DESCRIPTION("MAXIM MAX77693 regulator driver"); +MODULE_DESCRIPTION("MAXIM 77693/77843 regulator driver"); MODULE_AUTHOR("Jonghwa Lee <jonghwa3.lee@samsung.com>"); +MODULE_AUTHOR("Krzysztof Kozlowski <k.kozlowski.k@gmail.com>"); MODULE_LICENSE("GPL"); diff --git a/drivers/regulator/max77843.c b/drivers/regulator/max77843.c deleted file mode 100644 index f4fd0d3cfa6e..000000000000 --- a/drivers/regulator/max77843.c +++ /dev/null @@ -1,201 +0,0 @@ -/* - * max77843.c - Regulator driver for the Maxim MAX77843 - * - * Copyright (C) 2015 Samsung Electronics - * Author: Jaewon Kim <jaewon02.kim@samsung.com> - * Author: Beomho Seo <beomho.seo@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 - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - */ - -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/regulator/driver.h> -#include <linux/regulator/machine.h> -#include <linux/mfd/max77843-private.h> -#include <linux/regulator/of_regulator.h> - -enum max77843_regulator_type { - MAX77843_SAFEOUT1 = 0, - MAX77843_SAFEOUT2, - MAX77843_CHARGER, - - MAX77843_NUM, -}; - -static const unsigned int max77843_safeout_voltage_table[] = { - 4850000, - 4900000, - 4950000, - 3300000, -}; - -static int max77843_reg_get_current_limit(struct regulator_dev *rdev) -{ - struct regmap *regmap = rdev->regmap; - unsigned int chg_min_uA = rdev->constraints->min_uA; - unsigned int chg_max_uA = rdev->constraints->max_uA; - unsigned int val; - int ret; - unsigned int reg, sel; - - ret = regmap_read(regmap, MAX77843_CHG_REG_CHG_CNFG_02, ®); - if (ret) { - dev_err(&rdev->dev, "Failed to read charger register\n"); - return ret; - } - - sel = reg & MAX77843_CHG_FAST_CHG_CURRENT_MASK; - - if (sel < 0x03) - sel = 0; - else - sel -= 2; - - val = chg_min_uA + MAX77843_CHG_FAST_CHG_CURRENT_STEP * sel; - if (val > chg_max_uA) - return -EINVAL; - - return val; -} - -static int max77843_reg_set_current_limit(struct regulator_dev *rdev, - int min_uA, int max_uA) -{ - struct regmap *regmap = rdev->regmap; - unsigned int chg_min_uA = rdev->constraints->min_uA; - int sel = 0; - - while (chg_min_uA + MAX77843_CHG_FAST_CHG_CURRENT_STEP * sel < min_uA) - sel++; - - if (chg_min_uA + MAX77843_CHG_FAST_CHG_CURRENT_STEP * sel > max_uA) - return -EINVAL; - - sel += 2; - - return regmap_write(regmap, MAX77843_CHG_REG_CHG_CNFG_02, sel); -} - -static struct regulator_ops max77843_charger_ops = { - .is_enabled = regulator_is_enabled_regmap, - .enable = regulator_enable_regmap, - .disable = regulator_disable_regmap, - .get_current_limit = max77843_reg_get_current_limit, - .set_current_limit = max77843_reg_set_current_limit, -}; - -static struct regulator_ops max77843_regulator_ops = { - .is_enabled = regulator_is_enabled_regmap, - .enable = regulator_enable_regmap, - .disable = regulator_disable_regmap, - .list_voltage = regulator_list_voltage_table, - .get_voltage_sel = regulator_get_voltage_sel_regmap, - .set_voltage_sel = regulator_set_voltage_sel_regmap, -}; - -#define MAX77843_SAFEOUT(num) { \ - .name = "SAFEOUT" # num, \ - .id = MAX77843_SAFEOUT ## num, \ - .ops = &max77843_regulator_ops, \ - .of_match = of_match_ptr("SAFEOUT" # num), \ - .regulators_node = of_match_ptr("regulators"), \ - .type = REGULATOR_VOLTAGE, \ - .owner = THIS_MODULE, \ - .n_voltages = ARRAY_SIZE(max77843_safeout_voltage_table), \ - .volt_table = max77843_safeout_voltage_table, \ - .enable_reg = MAX77843_SYS_REG_SAFEOUTCTRL, \ - .enable_mask = MAX77843_REG_SAFEOUTCTRL_ENSAFEOUT ## num, \ - .vsel_reg = MAX77843_SYS_REG_SAFEOUTCTRL, \ - .vsel_mask = MAX77843_REG_SAFEOUTCTRL_SAFEOUT ## num ## _MASK, \ -} - -static const struct regulator_desc max77843_supported_regulators[] = { - [MAX77843_SAFEOUT1] = MAX77843_SAFEOUT(1), - [MAX77843_SAFEOUT2] = MAX77843_SAFEOUT(2), - [MAX77843_CHARGER] = { - .name = "CHARGER", - .id = MAX77843_CHARGER, - .ops = &max77843_charger_ops, - .of_match = of_match_ptr("CHARGER"), - .regulators_node = of_match_ptr("regulators"), - .type = REGULATOR_CURRENT, - .owner = THIS_MODULE, - .enable_reg = MAX77843_CHG_REG_CHG_CNFG_00, - .enable_mask = MAX77843_CHG_MASK | MAX77843_CHG_BUCK_MASK, - .enable_val = MAX77843_CHG_MASK | MAX77843_CHG_BUCK_MASK, - }, -}; - -static struct regmap *max77843_get_regmap(struct max77843 *max77843, int reg_id) -{ - switch (reg_id) { - case MAX77843_SAFEOUT1: - case MAX77843_SAFEOUT2: - return max77843->regmap; - case MAX77843_CHARGER: - return max77843->regmap_chg; - default: - return max77843->regmap; - } -} - -static int max77843_regulator_probe(struct platform_device *pdev) -{ - struct max77843 *max77843 = dev_get_drvdata(pdev->dev.parent); - struct regulator_config config = {}; - int i; - - config.dev = max77843->dev; - config.driver_data = max77843; - - for (i = 0; i < ARRAY_SIZE(max77843_supported_regulators); i++) { - struct regulator_dev *regulator; - - config.regmap = max77843_get_regmap(max77843, - max77843_supported_regulators[i].id); - - regulator = devm_regulator_register(&pdev->dev, - &max77843_supported_regulators[i], &config); - if (IS_ERR(regulator)) { - dev_err(&pdev->dev, - "Failed to regiser regulator-%d\n", i); - return PTR_ERR(regulator); - } - } - - return 0; -} - -static const struct platform_device_id max77843_regulator_id[] = { - { "max77843-regulator", }, - { /* sentinel */ }, -}; - -static struct platform_driver max77843_regulator_driver = { - .driver = { - .name = "max77843-regulator", - }, - .probe = max77843_regulator_probe, - .id_table = max77843_regulator_id, -}; - -static int __init max77843_regulator_init(void) -{ - return platform_driver_register(&max77843_regulator_driver); -} -subsys_initcall(max77843_regulator_init); - -static void __exit max77843_regulator_exit(void) -{ - platform_driver_unregister(&max77843_regulator_driver); -} -module_exit(max77843_regulator_exit); - -MODULE_AUTHOR("Jaewon Kim <jaewon02.kim@samsung.com>"); -MODULE_AUTHOR("Beomho Seo <beomho.seo@samsung.com>"); -MODULE_DESCRIPTION("Maxim MAX77843 regulator driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/regulator/max8660.c b/drivers/regulator/max8660.c index 4071d74fa828..b87f62dd484e 100644 --- a/drivers/regulator/max8660.c +++ b/drivers/regulator/max8660.c @@ -518,7 +518,6 @@ static struct i2c_driver max8660_driver = { .probe = max8660_probe, .driver = { .name = "max8660", - .owner = THIS_MODULE, }, .id_table = max8660_id, }; diff --git a/drivers/regulator/max8973-regulator.c b/drivers/regulator/max8973-regulator.c index e94ddcf97722..5b75b7c2e3ea 100644 --- a/drivers/regulator/max8973-regulator.c +++ b/drivers/regulator/max8973-regulator.c @@ -75,6 +75,7 @@ #define MAX8973_DISCH_ENBABLE BIT(5) #define MAX8973_FT_ENABLE BIT(4) +#define MAX8973_CKKADV_TRIP_MASK 0xC #define MAX8973_CKKADV_TRIP_DISABLE 0xC #define MAX8973_CKKADV_TRIP_75mV_PER_US 0x0 #define MAX8973_CKKADV_TRIP_150mV_PER_US 0x4 @@ -282,6 +283,55 @@ static int max8973_set_ramp_delay(struct regulator_dev *rdev, return ret; } +static int max8973_set_current_limit(struct regulator_dev *rdev, + int min_ua, int max_ua) +{ + struct max8973_chip *max = rdev_get_drvdata(rdev); + unsigned int val; + int ret; + + if (max_ua <= 9000000) + val = MAX8973_CKKADV_TRIP_75mV_PER_US; + else if (max_ua <= 12000000) + val = MAX8973_CKKADV_TRIP_150mV_PER_US; + else + val = MAX8973_CKKADV_TRIP_DISABLE; + + ret = regmap_update_bits(max->regmap, MAX8973_CONTROL2, + MAX8973_CKKADV_TRIP_MASK, val); + if (ret < 0) { + dev_err(max->dev, "register %d update failed: %d\n", + MAX8973_CONTROL2, ret); + return ret; + } + return 0; +} + +static int max8973_get_current_limit(struct regulator_dev *rdev) +{ + struct max8973_chip *max = rdev_get_drvdata(rdev); + unsigned int control2; + int ret; + + ret = regmap_read(max->regmap, MAX8973_CONTROL2, &control2); + if (ret < 0) { + dev_err(max->dev, "register %d read failed: %d\n", + MAX8973_CONTROL2, ret); + return ret; + } + switch (control2 & MAX8973_CKKADV_TRIP_MASK) { + case MAX8973_CKKADV_TRIP_DISABLE: + return 15000000; + case MAX8973_CKKADV_TRIP_150mV_PER_US: + return 12000000; + case MAX8973_CKKADV_TRIP_75mV_PER_US: + return 9000000; + default: + break; + } + return 9000000; +} + static const struct regulator_ops max8973_dcdc_ops = { .get_voltage_sel = max8973_dcdc_get_voltage_sel, .set_voltage_sel = max8973_dcdc_set_voltage_sel, @@ -421,6 +471,8 @@ static struct max8973_regulator_platform_data *max8973_parse_dt( struct device_node *np = dev->of_node; int ret; u32 pval; + bool etr_enable; + bool etr_sensitivity_high; pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) @@ -452,6 +504,23 @@ static struct max8973_regulator_platform_data *max8973_parse_dt( if (of_property_read_bool(np, "maxim,enable-bias-control")) pdata->control_flags |= MAX8973_CONTROL_BIAS_ENABLE; + etr_enable = of_property_read_bool(np, "maxim,enable-etr"); + etr_sensitivity_high = of_property_read_bool(np, + "maxim,enable-high-etr-sensitivity"); + if (etr_sensitivity_high) + etr_enable = true; + + if (etr_enable) { + if (etr_sensitivity_high) + pdata->control_flags |= + MAX8973_CONTROL_CLKADV_TRIP_75mV_PER_US; + else + pdata->control_flags |= + MAX8973_CONTROL_CLKADV_TRIP_150mV_PER_US; + } else { + pdata->control_flags |= MAX8973_CONTROL_CLKADV_TRIP_DISABLED; + } + return pdata; } @@ -568,6 +637,15 @@ static int max8973_probe(struct i2c_client *client, max->lru_index[i] = i; max->lru_index[0] = max->curr_vout_reg; max->lru_index[max->curr_vout_reg] = 0; + } else { + /* + * If there is no DVS GPIO, the VOUT register + * address is fixed. + */ + max->ops.set_voltage_sel = regulator_set_voltage_sel_regmap; + max->ops.get_voltage_sel = regulator_get_voltage_sel_regmap; + max->desc.vsel_reg = max->curr_vout_reg; + max->desc.vsel_mask = MAX8973_VOUT_MASK; } if (pdata_from_dt) @@ -613,6 +691,8 @@ static int max8973_probe(struct i2c_client *client, max->ops.enable = regulator_enable_regmap; max->ops.disable = regulator_disable_regmap; max->ops.is_enabled = regulator_is_enabled_regmap; + max->ops.set_current_limit = max8973_set_current_limit; + max->ops.get_current_limit = max8973_get_current_limit; break; default: break; @@ -652,7 +732,6 @@ static struct i2c_driver max8973_i2c_driver = { .driver = { .name = "max8973", .of_match_table = of_max8973_match_tbl, - .owner = THIS_MODULE, }, .probe = max8973_probe, .id_table = max8973_id, diff --git a/drivers/regulator/mt6311-regulator.c b/drivers/regulator/mt6311-regulator.c new file mode 100644 index 000000000000..02c4e5feca8e --- /dev/null +++ b/drivers/regulator/mt6311-regulator.c @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2015 MediaTek Inc. + * Author: Henry Chen <henryc.chen@mediatek.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 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/init.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> +#include <linux/regulator/of_regulator.h> +#include <linux/regulator/mt6311.h> +#include <linux/slab.h> +#include "mt6311-regulator.h" + +static const struct regmap_config mt6311_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = MT6311_FQMTR_CON4, +}; + +/* Default limits measured in millivolts and milliamps */ +#define MT6311_MIN_UV 600000 +#define MT6311_MAX_UV 1393750 +#define MT6311_STEP_UV 6250 + +static const struct regulator_linear_range buck_volt_range[] = { + REGULATOR_LINEAR_RANGE(MT6311_MIN_UV, 0, 0x7f, MT6311_STEP_UV), +}; + +static const struct regulator_ops mt6311_buck_ops = { + .list_voltage = regulator_list_voltage_linear_range, + .map_voltage = regulator_map_voltage_linear_range, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_time_sel = regulator_set_voltage_time_sel, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, +}; + +static const struct regulator_ops mt6311_ldo_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, +}; + +#define MT6311_BUCK(_id) \ +{\ + .name = #_id,\ + .ops = &mt6311_buck_ops,\ + .of_match = of_match_ptr(#_id),\ + .regulators_node = of_match_ptr("regulators"),\ + .type = REGULATOR_VOLTAGE,\ + .id = MT6311_ID_##_id,\ + .n_voltages = (MT6311_MAX_UV - MT6311_MIN_UV) / MT6311_STEP_UV + 1,\ + .min_uV = MT6311_MIN_UV,\ + .uV_step = MT6311_STEP_UV,\ + .owner = THIS_MODULE,\ + .linear_ranges = buck_volt_range, \ + .n_linear_ranges = ARRAY_SIZE(buck_volt_range), \ + .enable_reg = MT6311_VDVFS11_CON9,\ + .enable_mask = MT6311_PMIC_VDVFS11_EN_MASK,\ + .vsel_reg = MT6311_VDVFS11_CON12,\ + .vsel_mask = MT6311_PMIC_VDVFS11_VOSEL_MASK,\ +} + +#define MT6311_LDO(_id) \ +{\ + .name = #_id,\ + .ops = &mt6311_ldo_ops,\ + .of_match = of_match_ptr(#_id),\ + .regulators_node = of_match_ptr("regulators"),\ + .type = REGULATOR_VOLTAGE,\ + .id = MT6311_ID_##_id,\ + .owner = THIS_MODULE,\ + .enable_reg = MT6311_LDO_CON3,\ + .enable_mask = MT6311_PMIC_RG_VBIASN_EN_MASK,\ +} + +static const struct regulator_desc mt6311_regulators[] = { + MT6311_BUCK(VDVFS), + MT6311_LDO(VBIASN), +}; + +/* + * I2C driver interface functions + */ +static int mt6311_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct regulator_config config = { }; + struct regulator_dev *rdev; + struct regmap *regmap; + int i, ret; + unsigned int data; + + regmap = devm_regmap_init_i2c(i2c, &mt6311_regmap_config); + if (IS_ERR(regmap)) { + ret = PTR_ERR(regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + ret = regmap_read(regmap, MT6311_SWCID, &data); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to read DEVICE_ID reg: %d\n", ret); + return ret; + } + + switch (data) { + case MT6311_E1_CID_CODE: + case MT6311_E2_CID_CODE: + case MT6311_E3_CID_CODE: + break; + default: + dev_err(&i2c->dev, "Unsupported device id = 0x%x.\n", data); + return -ENODEV; + } + + for (i = 0; i < MT6311_MAX_REGULATORS; i++) { + config.dev = &i2c->dev; + config.regmap = regmap; + + rdev = devm_regulator_register(&i2c->dev, + &mt6311_regulators[i], &config); + if (IS_ERR(rdev)) { + dev_err(&i2c->dev, + "Failed to register MT6311 regulator\n"); + return PTR_ERR(rdev); + } + } + + return 0; +} + +static const struct i2c_device_id mt6311_i2c_id[] = { + {"mt6311", 0}, + {}, +}; +MODULE_DEVICE_TABLE(i2c, mt6311_i2c_id); + +#ifdef CONFIG_OF +static const struct of_device_id mt6311_dt_ids[] = { + { .compatible = "mediatek,mt6311-regulator", + .data = &mt6311_i2c_id[0] }, + {}, +}; +MODULE_DEVICE_TABLE(of, mt6311_dt_ids); +#endif + +static struct i2c_driver mt6311_regulator_driver = { + .driver = { + .name = "mt6311", + .of_match_table = of_match_ptr(mt6311_dt_ids), + }, + .probe = mt6311_i2c_probe, + .id_table = mt6311_i2c_id, +}; + +module_i2c_driver(mt6311_regulator_driver); + +MODULE_AUTHOR("Henry Chen <henryc.chen@mediatek.com>"); +MODULE_DESCRIPTION("Regulator device driver for Mediatek MT6311"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/regulator/mt6311-regulator.h b/drivers/regulator/mt6311-regulator.h new file mode 100644 index 000000000000..5218db46a798 --- /dev/null +++ b/drivers/regulator/mt6311-regulator.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2015 MediaTek Inc. + * Author: Henry Chen <henryc.chen@mediatek.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 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 __MT6311_REGULATOR_H__ +#define __MT6311_REGULATOR_H__ + +#define MT6311_SWCID 0x01 + +#define MT6311_TOP_INT_CON 0x18 +#define MT6311_TOP_INT_MON 0x19 + +#define MT6311_VDVFS11_CON0 0x87 +#define MT6311_VDVFS11_CON7 0x88 +#define MT6311_VDVFS11_CON8 0x89 +#define MT6311_VDVFS11_CON9 0x8A +#define MT6311_VDVFS11_CON10 0x8B +#define MT6311_VDVFS11_CON11 0x8C +#define MT6311_VDVFS11_CON12 0x8D +#define MT6311_VDVFS11_CON13 0x8E +#define MT6311_VDVFS11_CON14 0x8F +#define MT6311_VDVFS11_CON15 0x90 +#define MT6311_VDVFS11_CON16 0x91 +#define MT6311_VDVFS11_CON17 0x92 +#define MT6311_VDVFS11_CON18 0x93 +#define MT6311_VDVFS11_CON19 0x94 + +#define MT6311_LDO_CON0 0xCC +#define MT6311_LDO_OCFB0 0xCD +#define MT6311_LDO_CON2 0xCE +#define MT6311_LDO_CON3 0xCF +#define MT6311_LDO_CON4 0xD0 +#define MT6311_FQMTR_CON0 0xD1 +#define MT6311_FQMTR_CON1 0xD2 +#define MT6311_FQMTR_CON2 0xD3 +#define MT6311_FQMTR_CON3 0xD4 +#define MT6311_FQMTR_CON4 0xD5 + +#define MT6311_PMIC_RG_INT_POL_MASK 0x1 +#define MT6311_PMIC_RG_INT_EN_MASK 0x2 +#define MT6311_PMIC_RG_BUCK_OC_INT_STATUS_MASK 0x10 + +#define MT6311_PMIC_VDVFS11_EN_CTRL_MASK 0x1 +#define MT6311_PMIC_VDVFS11_VOSEL_CTRL_MASK 0x2 +#define MT6311_PMIC_VDVFS11_EN_SEL_MASK 0x3 +#define MT6311_PMIC_VDVFS11_VOSEL_SEL_MASK 0xc +#define MT6311_PMIC_VDVFS11_EN_MASK 0x1 +#define MT6311_PMIC_VDVFS11_VOSEL_MASK 0x7F +#define MT6311_PMIC_VDVFS11_VOSEL_ON_MASK 0x7F +#define MT6311_PMIC_VDVFS11_VOSEL_SLEEP_MASK 0x7F +#define MT6311_PMIC_NI_VDVFS11_VOSEL_MASK 0x7F + +#define MT6311_PMIC_RG_VBIASN_EN_MASK 0x1 + +#endif diff --git a/drivers/regulator/of_regulator.c b/drivers/regulator/of_regulator.c index b1c485b24ab2..250700c853bf 100644 --- a/drivers/regulator/of_regulator.c +++ b/drivers/regulator/of_regulator.c @@ -107,6 +107,9 @@ static void of_get_regulation_constraints(struct device_node *np, if (!of_property_read_u32(np, "regulator-system-load", &pval)) constraints->system_load = pval; + constraints->over_current_protection = of_property_read_bool(np, + "regulator-over-current-protection"); + for (i = 0; i < ARRAY_SIZE(regulator_states); i++) { switch (i) { case PM_SUSPEND_MEM: diff --git a/drivers/regulator/pbias-regulator.c b/drivers/regulator/pbias-regulator.c index bd2b75c0d1d1..4fa7bcaf454e 100644 --- a/drivers/regulator/pbias-regulator.c +++ b/drivers/regulator/pbias-regulator.c @@ -30,6 +30,7 @@ struct pbias_reg_info { u32 enable; u32 enable_mask; + u32 disable_val; u32 vmode; unsigned int enable_time; char *name; @@ -62,6 +63,7 @@ static const struct pbias_reg_info pbias_mmc_omap2430 = { .enable = BIT(1), .enable_mask = BIT(1), .vmode = BIT(0), + .disable_val = 0, .enable_time = 100, .name = "pbias_mmc_omap2430" }; @@ -77,6 +79,7 @@ static const struct pbias_reg_info pbias_sim_omap3 = { static const struct pbias_reg_info pbias_mmc_omap4 = { .enable = BIT(26) | BIT(22), .enable_mask = BIT(26) | BIT(25) | BIT(22), + .disable_val = BIT(25), .vmode = BIT(21), .enable_time = 100, .name = "pbias_mmc_omap4" @@ -85,6 +88,7 @@ static const struct pbias_reg_info pbias_mmc_omap4 = { static const struct pbias_reg_info pbias_mmc_omap5 = { .enable = BIT(27) | BIT(26), .enable_mask = BIT(27) | BIT(25) | BIT(26), + .disable_val = BIT(25), .vmode = BIT(21), .enable_time = 100, .name = "pbias_mmc_omap5" @@ -159,6 +163,7 @@ static int pbias_regulator_probe(struct platform_device *pdev) 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; + drvdata[data_idx].desc.disable_val = info->disable_val; 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 8cc8d1877c44..2a44e5dd9c2a 100644 --- a/drivers/regulator/pfuze100-regulator.c +++ b/drivers/regulator/pfuze100-regulator.c @@ -643,7 +643,6 @@ static struct i2c_driver pfuze_driver = { .id_table = pfuze_device_id, .driver = { .name = "pfuze100-regulator", - .owner = THIS_MODULE, .of_match_table = pfuze_dt_ids, }, .probe = pfuze100_regulator_probe, @@ -653,4 +652,3 @@ module_i2c_driver(pfuze_driver); MODULE_AUTHOR("Robin Gong <b38343@freescale.com>"); MODULE_DESCRIPTION("Regulator Driver for Freescale PFUZE100/PFUZE200 PMIC"); MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("i2c:pfuze100-regulator"); diff --git a/drivers/regulator/pwm-regulator.c b/drivers/regulator/pwm-regulator.c index ffa96124a5e7..fc3166dfcbfa 100644 --- a/drivers/regulator/pwm-regulator.c +++ b/drivers/regulator/pwm-regulator.c @@ -10,6 +10,7 @@ * published by the Free Software Foundation. */ +#include <linux/delay.h> #include <linux/module.h> #include <linux/init.h> #include <linux/err.h> @@ -21,9 +22,15 @@ #include <linux/pwm.h> struct pwm_regulator_data { - struct pwm_voltages *duty_cycle_table; + /* Shared */ struct pwm_device *pwm; + + /* Voltage table */ + struct pwm_voltages *duty_cycle_table; int state; + + /* Continuous voltage */ + int volt_uV; }; struct pwm_voltages { @@ -31,6 +38,9 @@ struct pwm_voltages { unsigned int dutycycle; }; +/** + * Voltage table call-backs + */ static int pwm_regulator_get_voltage_sel(struct regulator_dev *rdev) { struct pwm_regulator_data *drvdata = rdev_get_drvdata(rdev); @@ -79,29 +89,129 @@ static int pwm_regulator_list_voltage(struct regulator_dev *rdev, return drvdata->duty_cycle_table[selector].uV; } -static struct regulator_ops pwm_regulator_voltage_ops = { +/** + * Continuous voltage call-backs + */ +static int pwm_voltage_to_duty_cycle_percentage(struct regulator_dev *rdev, int req_uV) +{ + int min_uV = rdev->constraints->min_uV; + int max_uV = rdev->constraints->max_uV; + int diff = max_uV - min_uV; + + return 100 - (((req_uV * 100) - (min_uV * 100)) / diff); +} + +static int pwm_regulator_get_voltage(struct regulator_dev *rdev) +{ + struct pwm_regulator_data *drvdata = rdev_get_drvdata(rdev); + + return drvdata->volt_uV; +} + +static int pwm_regulator_set_voltage(struct regulator_dev *rdev, + int min_uV, int max_uV, + unsigned *selector) +{ + struct pwm_regulator_data *drvdata = rdev_get_drvdata(rdev); + unsigned int ramp_delay = rdev->constraints->ramp_delay; + unsigned int period = pwm_get_period(drvdata->pwm); + int duty_cycle; + int ret; + + duty_cycle = pwm_voltage_to_duty_cycle_percentage(rdev, min_uV); + + ret = pwm_config(drvdata->pwm, (period / 100) * duty_cycle, period); + if (ret) { + dev_err(&rdev->dev, "Failed to configure PWM\n"); + return ret; + } + + ret = pwm_enable(drvdata->pwm); + if (ret) { + dev_err(&rdev->dev, "Failed to enable PWM\n"); + return ret; + } + drvdata->volt_uV = min_uV; + + /* Delay required by PWM regulator to settle to the new voltage */ + usleep_range(ramp_delay, ramp_delay + 1000); + + return 0; +} + +static struct regulator_ops pwm_regulator_voltage_table_ops = { .set_voltage_sel = pwm_regulator_set_voltage_sel, .get_voltage_sel = pwm_regulator_get_voltage_sel, .list_voltage = pwm_regulator_list_voltage, .map_voltage = regulator_map_voltage_iterate, }; +static struct regulator_ops pwm_regulator_voltage_continuous_ops = { + .get_voltage = pwm_regulator_get_voltage, + .set_voltage = pwm_regulator_set_voltage, +}; + static struct regulator_desc pwm_regulator_desc = { .name = "pwm-regulator", - .ops = &pwm_regulator_voltage_ops, .type = REGULATOR_VOLTAGE, .owner = THIS_MODULE, .supply_name = "pwm", }; +static int pwm_regulator_init_table(struct platform_device *pdev, + struct pwm_regulator_data *drvdata) +{ + struct device_node *np = pdev->dev.of_node; + struct pwm_voltages *duty_cycle_table; + unsigned int length = 0; + int ret; + + of_find_property(np, "voltage-table", &length); + + if ((length < sizeof(*duty_cycle_table)) || + (length % sizeof(*duty_cycle_table))) { + dev_err(&pdev->dev, + "voltage-table length(%d) is invalid\n", + length); + return -EINVAL; + } + + duty_cycle_table = devm_kzalloc(&pdev->dev, length, GFP_KERNEL); + if (!duty_cycle_table) + return -ENOMEM; + + ret = of_property_read_u32_array(np, "voltage-table", + (u32 *)duty_cycle_table, + length / sizeof(u32)); + if (ret) { + dev_err(&pdev->dev, "Failed to read voltage-table\n"); + return ret; + } + + drvdata->duty_cycle_table = duty_cycle_table; + pwm_regulator_desc.ops = &pwm_regulator_voltage_table_ops; + pwm_regulator_desc.n_voltages = length / sizeof(*duty_cycle_table); + + return 0; +} + +static int pwm_regulator_init_continuous(struct platform_device *pdev, + struct pwm_regulator_data *drvdata) +{ + pwm_regulator_desc.ops = &pwm_regulator_voltage_continuous_ops; + pwm_regulator_desc.continuous_voltage_range = true; + + return 0; +} + static int pwm_regulator_probe(struct platform_device *pdev) { + const struct regulator_init_data *init_data; struct pwm_regulator_data *drvdata; - struct property *prop; struct regulator_dev *regulator; struct regulator_config config = { }; struct device_node *np = pdev->dev.of_node; - int length, ret; + int ret; if (!np) { dev_err(&pdev->dev, "Device Tree node missing\n"); @@ -112,44 +222,22 @@ static int pwm_regulator_probe(struct platform_device *pdev) if (!drvdata) return -ENOMEM; - /* determine the number of voltage-table */ - prop = of_find_property(np, "voltage-table", &length); - if (!prop) { - dev_err(&pdev->dev, "No voltage-table\n"); - return -EINVAL; - } - - if ((length < sizeof(*drvdata->duty_cycle_table)) || - (length % sizeof(*drvdata->duty_cycle_table))) { - dev_err(&pdev->dev, "voltage-table length(%d) is invalid\n", - length); - return -EINVAL; - } - - pwm_regulator_desc.n_voltages = length / sizeof(*drvdata->duty_cycle_table); - - drvdata->duty_cycle_table = devm_kzalloc(&pdev->dev, - length, GFP_KERNEL); - if (!drvdata->duty_cycle_table) - return -ENOMEM; - - /* read voltage table from DT property */ - ret = of_property_read_u32_array(np, "voltage-table", - (u32 *)drvdata->duty_cycle_table, - length / sizeof(u32)); - if (ret < 0) { - dev_err(&pdev->dev, "read voltage-table failed\n"); + if (of_find_property(np, "voltage-table", NULL)) + ret = pwm_regulator_init_table(pdev, drvdata); + else + ret = pwm_regulator_init_continuous(pdev, drvdata); + if (ret) return ret; - } - config.init_data = of_get_regulator_init_data(&pdev->dev, np, - &pwm_regulator_desc); - if (!config.init_data) + init_data = of_get_regulator_init_data(&pdev->dev, np, + &pwm_regulator_desc); + if (!init_data) return -ENOMEM; config.of_node = np; config.dev = &pdev->dev; config.driver_data = drvdata; + config.init_data = init_data; drvdata->pwm = devm_pwm_get(&pdev->dev, NULL); if (IS_ERR(drvdata->pwm)) { diff --git a/drivers/regulator/qcom_smd-regulator.c b/drivers/regulator/qcom_smd-regulator.c new file mode 100644 index 000000000000..9c6167dd2c8b --- /dev/null +++ b/drivers/regulator/qcom_smd-regulator.c @@ -0,0 +1,350 @@ +/* + * Copyright (c) 2015, Sony Mobile Communications AB. + * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> +#include <linux/regulator/of_regulator.h> +#include <linux/soc/qcom/smd-rpm.h> + +struct qcom_rpm_reg { + struct device *dev; + + struct qcom_smd_rpm *rpm; + + u32 type; + u32 id; + + struct regulator_desc desc; + + int is_enabled; + int uV; +}; + +struct rpm_regulator_req { + u32 key; + u32 nbytes; + u32 value; +}; + +#define RPM_KEY_SWEN 0x6e657773 /* "swen" */ +#define RPM_KEY_UV 0x00007675 /* "uv" */ +#define RPM_KEY_MA 0x0000616d /* "ma" */ + +static int rpm_reg_write_active(struct qcom_rpm_reg *vreg, + struct rpm_regulator_req *req, + size_t size) +{ + return qcom_rpm_smd_write(vreg->rpm, + QCOM_SMD_RPM_ACTIVE_STATE, + vreg->type, + vreg->id, + req, size); +} + +static int rpm_reg_enable(struct regulator_dev *rdev) +{ + struct qcom_rpm_reg *vreg = rdev_get_drvdata(rdev); + struct rpm_regulator_req req; + int ret; + + req.key = RPM_KEY_SWEN; + req.nbytes = sizeof(u32); + req.value = 1; + + ret = rpm_reg_write_active(vreg, &req, sizeof(req)); + if (!ret) + vreg->is_enabled = 1; + + return ret; +} + +static int rpm_reg_is_enabled(struct regulator_dev *rdev) +{ + struct qcom_rpm_reg *vreg = rdev_get_drvdata(rdev); + + return vreg->is_enabled; +} + +static int rpm_reg_disable(struct regulator_dev *rdev) +{ + struct qcom_rpm_reg *vreg = rdev_get_drvdata(rdev); + struct rpm_regulator_req req; + int ret; + + req.key = RPM_KEY_SWEN; + req.nbytes = sizeof(u32); + req.value = 0; + + ret = rpm_reg_write_active(vreg, &req, sizeof(req)); + if (!ret) + vreg->is_enabled = 0; + + return ret; +} + +static int rpm_reg_get_voltage(struct regulator_dev *rdev) +{ + struct qcom_rpm_reg *vreg = rdev_get_drvdata(rdev); + + return vreg->uV; +} + +static int rpm_reg_set_voltage(struct regulator_dev *rdev, + int min_uV, + int max_uV, + unsigned *selector) +{ + struct qcom_rpm_reg *vreg = rdev_get_drvdata(rdev); + struct rpm_regulator_req req; + int ret = 0; + + req.key = RPM_KEY_UV; + req.nbytes = sizeof(u32); + req.value = min_uV; + + ret = rpm_reg_write_active(vreg, &req, sizeof(req)); + if (!ret) + vreg->uV = min_uV; + + return ret; +} + +static int rpm_reg_set_load(struct regulator_dev *rdev, int load_uA) +{ + struct qcom_rpm_reg *vreg = rdev_get_drvdata(rdev); + struct rpm_regulator_req req; + + req.key = RPM_KEY_MA; + req.nbytes = sizeof(u32); + req.value = load_uA; + + return rpm_reg_write_active(vreg, &req, sizeof(req)); +} + +static const struct regulator_ops rpm_smps_ldo_ops = { + .enable = rpm_reg_enable, + .disable = rpm_reg_disable, + .is_enabled = rpm_reg_is_enabled, + + .get_voltage = rpm_reg_get_voltage, + .set_voltage = rpm_reg_set_voltage, + + .set_load = rpm_reg_set_load, +}; + +static const struct regulator_ops rpm_switch_ops = { + .enable = rpm_reg_enable, + .disable = rpm_reg_disable, + .is_enabled = rpm_reg_is_enabled, +}; + +static const struct regulator_desc pm8x41_hfsmps = { + .linear_ranges = (struct regulator_linear_range[]) { + REGULATOR_LINEAR_RANGE( 375000, 0, 95, 12500), + REGULATOR_LINEAR_RANGE(1550000, 96, 158, 25000), + }, + .n_linear_ranges = 2, + .n_voltages = 159, + .ops = &rpm_smps_ldo_ops, +}; + +static const struct regulator_desc pm8841_ftsmps = { + .linear_ranges = (struct regulator_linear_range[]) { + REGULATOR_LINEAR_RANGE(350000, 0, 184, 5000), + REGULATOR_LINEAR_RANGE(700000, 185, 339, 10000), + }, + .n_linear_ranges = 2, + .n_voltages = 340, + .ops = &rpm_smps_ldo_ops, +}; + +static const struct regulator_desc pm8941_boost = { + .linear_ranges = (struct regulator_linear_range[]) { + REGULATOR_LINEAR_RANGE(4000000, 0, 15, 100000), + }, + .n_linear_ranges = 1, + .n_voltages = 16, + .ops = &rpm_smps_ldo_ops, +}; + +static const struct regulator_desc pm8941_pldo = { + .linear_ranges = (struct regulator_linear_range[]) { + REGULATOR_LINEAR_RANGE( 750000, 0, 30, 25000), + REGULATOR_LINEAR_RANGE(1500000, 31, 99, 50000), + }, + .n_linear_ranges = 2, + .n_voltages = 100, + .ops = &rpm_smps_ldo_ops, +}; + +static const struct regulator_desc pm8941_nldo = { + .linear_ranges = (struct regulator_linear_range[]) { + REGULATOR_LINEAR_RANGE(750000, 0, 63, 12500), + }, + .n_linear_ranges = 1, + .n_voltages = 64, + .ops = &rpm_smps_ldo_ops, +}; + +static const struct regulator_desc pm8941_lnldo = { + .fixed_uV = 1740000, + .n_voltages = 1, + .ops = &rpm_smps_ldo_ops, +}; + +static const struct regulator_desc pm8941_switch = { + .ops = &rpm_switch_ops, +}; + +struct rpm_regulator_data { + const char *name; + u32 type; + u32 id; + const struct regulator_desc *desc; + const char *supply; +}; + +static const struct rpm_regulator_data rpm_pm8841_regulators[] = { + { "s1", QCOM_SMD_RPM_SMPB, 1, &pm8x41_hfsmps, "vdd_s1" }, + { "s2", QCOM_SMD_RPM_SMPB, 2, &pm8841_ftsmps, "vdd_s2" }, + { "s3", QCOM_SMD_RPM_SMPB, 3, &pm8x41_hfsmps, "vdd_s3" }, + { "s4", QCOM_SMD_RPM_SMPB, 4, &pm8841_ftsmps, "vdd_s4" }, + { "s5", QCOM_SMD_RPM_SMPB, 5, &pm8841_ftsmps, "vdd_s5" }, + { "s6", QCOM_SMD_RPM_SMPB, 6, &pm8841_ftsmps, "vdd_s6" }, + { "s7", QCOM_SMD_RPM_SMPB, 7, &pm8841_ftsmps, "vdd_s7" }, + { "s8", QCOM_SMD_RPM_SMPB, 8, &pm8841_ftsmps, "vdd_s8" }, + {} +}; + +static const struct rpm_regulator_data rpm_pm8941_regulators[] = { + { "s1", QCOM_SMD_RPM_SMPA, 1, &pm8x41_hfsmps, "vdd_s1" }, + { "s2", QCOM_SMD_RPM_SMPA, 2, &pm8x41_hfsmps, "vdd_s2" }, + { "s3", QCOM_SMD_RPM_SMPA, 3, &pm8x41_hfsmps, "vdd_s3" }, + { "s4", QCOM_SMD_RPM_BOOST, 1, &pm8941_boost }, + + { "l1", QCOM_SMD_RPM_LDOA, 1, &pm8941_nldo, "vdd_l1_l3" }, + { "l2", QCOM_SMD_RPM_LDOA, 2, &pm8941_nldo, "vdd_l2_lvs1_2_3" }, + { "l3", QCOM_SMD_RPM_LDOA, 3, &pm8941_nldo, "vdd_l1_l3" }, + { "l4", QCOM_SMD_RPM_LDOA, 4, &pm8941_nldo, "vdd_l4_l11" }, + { "l5", QCOM_SMD_RPM_LDOA, 5, &pm8941_lnldo, "vdd_l5_l7" }, + { "l6", QCOM_SMD_RPM_LDOA, 6, &pm8941_pldo, "vdd_l6_l12_l14_l15" }, + { "l7", QCOM_SMD_RPM_LDOA, 7, &pm8941_lnldo, "vdd_l5_l7" }, + { "l8", QCOM_SMD_RPM_LDOA, 8, &pm8941_pldo, "vdd_l8_l16_l18_l19" }, + { "l9", QCOM_SMD_RPM_LDOA, 9, &pm8941_pldo, "vdd_l9_l10_l17_l22" }, + { "l10", QCOM_SMD_RPM_LDOA, 10, &pm8941_pldo, "vdd_l9_l10_l17_l22" }, + { "l11", QCOM_SMD_RPM_LDOA, 11, &pm8941_nldo, "vdd_l4_l11" }, + { "l12", QCOM_SMD_RPM_LDOA, 12, &pm8941_pldo, "vdd_l6_l12_l14_l15" }, + { "l13", QCOM_SMD_RPM_LDOA, 13, &pm8941_pldo, "vdd_l13_l20_l23_l24" }, + { "l14", QCOM_SMD_RPM_LDOA, 14, &pm8941_pldo, "vdd_l6_l12_l14_l15" }, + { "l15", QCOM_SMD_RPM_LDOA, 15, &pm8941_pldo, "vdd_l6_l12_l14_l15" }, + { "l16", QCOM_SMD_RPM_LDOA, 16, &pm8941_pldo, "vdd_l8_l16_l18_l19" }, + { "l17", QCOM_SMD_RPM_LDOA, 17, &pm8941_pldo, "vdd_l9_l10_l17_l22" }, + { "l18", QCOM_SMD_RPM_LDOA, 18, &pm8941_pldo, "vdd_l8_l16_l18_l19" }, + { "l19", QCOM_SMD_RPM_LDOA, 19, &pm8941_pldo, "vdd_l8_l16_l18_l19" }, + { "l20", QCOM_SMD_RPM_LDOA, 20, &pm8941_pldo, "vdd_l13_l20_l23_l24" }, + { "l21", QCOM_SMD_RPM_LDOA, 21, &pm8941_pldo, "vdd_l21" }, + { "l22", QCOM_SMD_RPM_LDOA, 22, &pm8941_pldo, "vdd_l9_l10_l17_l22" }, + { "l23", QCOM_SMD_RPM_LDOA, 23, &pm8941_pldo, "vdd_l13_l20_l23_l24" }, + { "l24", QCOM_SMD_RPM_LDOA, 24, &pm8941_pldo, "vdd_l13_l20_l23_l24" }, + + { "lvs1", QCOM_SMD_RPM_VSA, 1, &pm8941_switch, "vdd_l2_lvs1_2_3" }, + { "lvs2", QCOM_SMD_RPM_VSA, 2, &pm8941_switch, "vdd_l2_lvs1_2_3" }, + { "lvs3", QCOM_SMD_RPM_VSA, 3, &pm8941_switch, "vdd_l2_lvs1_2_3" }, + + { "5vs1", QCOM_SMD_RPM_VSA, 4, &pm8941_switch, "vin_5vs" }, + { "5vs2", QCOM_SMD_RPM_VSA, 5, &pm8941_switch, "vin_5vs" }, + + {} +}; + +static const struct of_device_id rpm_of_match[] = { + { .compatible = "qcom,rpm-pm8841-regulators", .data = &rpm_pm8841_regulators }, + { .compatible = "qcom,rpm-pm8941-regulators", .data = &rpm_pm8941_regulators }, + {} +}; +MODULE_DEVICE_TABLE(of, rpm_of_match); + +static int rpm_reg_probe(struct platform_device *pdev) +{ + const struct rpm_regulator_data *reg; + const struct of_device_id *match; + struct regulator_config config = { }; + struct regulator_dev *rdev; + struct qcom_rpm_reg *vreg; + struct qcom_smd_rpm *rpm; + + rpm = dev_get_drvdata(pdev->dev.parent); + if (!rpm) { + dev_err(&pdev->dev, "unable to retrieve handle to rpm\n"); + return -ENODEV; + } + + match = of_match_device(rpm_of_match, &pdev->dev); + for (reg = match->data; reg->name; reg++) { + vreg = devm_kzalloc(&pdev->dev, sizeof(*vreg), GFP_KERNEL); + if (!vreg) + return -ENOMEM; + + vreg->dev = &pdev->dev; + vreg->type = reg->type; + vreg->id = reg->id; + vreg->rpm = rpm; + + memcpy(&vreg->desc, reg->desc, sizeof(vreg->desc)); + + vreg->desc.id = -1; + vreg->desc.owner = THIS_MODULE; + vreg->desc.type = REGULATOR_VOLTAGE; + vreg->desc.name = reg->name; + vreg->desc.supply_name = reg->supply; + vreg->desc.of_match = reg->name; + + config.dev = &pdev->dev; + config.driver_data = vreg; + rdev = devm_regulator_register(&pdev->dev, &vreg->desc, &config); + if (IS_ERR(rdev)) { + dev_err(&pdev->dev, "failed to register %s\n", reg->name); + return PTR_ERR(rdev); + } + } + + return 0; +} + +static struct platform_driver rpm_reg_driver = { + .probe = rpm_reg_probe, + .driver = { + .name = "qcom_rpm_smd_regulator", + .of_match_table = rpm_of_match, + }, +}; + +static int __init rpm_reg_init(void) +{ + return platform_driver_register(&rpm_reg_driver); +} +subsys_initcall(rpm_reg_init); + +static void __exit rpm_reg_exit(void) +{ + platform_driver_unregister(&rpm_reg_driver); +} +module_exit(rpm_reg_exit) + +MODULE_DESCRIPTION("Qualcomm RPM regulator driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/regulator/qcom_spmi-regulator.c b/drivers/regulator/qcom_spmi-regulator.c index 850a30a95b5b..88a5dc88badc 100644 --- a/drivers/regulator/qcom_spmi-regulator.c +++ b/drivers/regulator/qcom_spmi-regulator.c @@ -26,6 +26,70 @@ #include <linux/regmap.h> #include <linux/list.h> +/* Pin control enable input pins. */ +#define SPMI_REGULATOR_PIN_CTRL_ENABLE_NONE 0x00 +#define SPMI_REGULATOR_PIN_CTRL_ENABLE_EN0 0x01 +#define SPMI_REGULATOR_PIN_CTRL_ENABLE_EN1 0x02 +#define SPMI_REGULATOR_PIN_CTRL_ENABLE_EN2 0x04 +#define SPMI_REGULATOR_PIN_CTRL_ENABLE_EN3 0x08 +#define SPMI_REGULATOR_PIN_CTRL_ENABLE_HW_DEFAULT 0x10 + +/* Pin control high power mode input pins. */ +#define SPMI_REGULATOR_PIN_CTRL_HPM_NONE 0x00 +#define SPMI_REGULATOR_PIN_CTRL_HPM_EN0 0x01 +#define SPMI_REGULATOR_PIN_CTRL_HPM_EN1 0x02 +#define SPMI_REGULATOR_PIN_CTRL_HPM_EN2 0x04 +#define SPMI_REGULATOR_PIN_CTRL_HPM_EN3 0x08 +#define SPMI_REGULATOR_PIN_CTRL_HPM_SLEEP_B 0x10 +#define SPMI_REGULATOR_PIN_CTRL_HPM_HW_DEFAULT 0x20 + +/* + * Used with enable parameters to specify that hardware default register values + * should be left unaltered. + */ +#define SPMI_REGULATOR_USE_HW_DEFAULT 2 + +/* Soft start strength of a voltage switch type regulator */ +enum spmi_vs_soft_start_str { + SPMI_VS_SOFT_START_STR_0P05_UA = 0, + SPMI_VS_SOFT_START_STR_0P25_UA, + SPMI_VS_SOFT_START_STR_0P55_UA, + SPMI_VS_SOFT_START_STR_0P75_UA, + SPMI_VS_SOFT_START_STR_HW_DEFAULT, +}; + +/** + * struct spmi_regulator_init_data - spmi-regulator initialization data + * @pin_ctrl_enable: Bit mask specifying which hardware pins should be + * used to enable the regulator, if any + * Value should be an ORing of + * SPMI_REGULATOR_PIN_CTRL_ENABLE_* constants. If + * the bit specified by + * SPMI_REGULATOR_PIN_CTRL_ENABLE_HW_DEFAULT is + * set, then pin control enable hardware registers + * will not be modified. + * @pin_ctrl_hpm: Bit mask specifying which hardware pins should be + * used to force the regulator into high power + * mode, if any + * Value should be an ORing of + * SPMI_REGULATOR_PIN_CTRL_HPM_* constants. If + * the bit specified by + * SPMI_REGULATOR_PIN_CTRL_HPM_HW_DEFAULT is + * set, then pin control mode hardware registers + * will not be modified. + * @vs_soft_start_strength: This parameter sets the soft start strength for + * voltage switch type regulators. Its value + * should be one of SPMI_VS_SOFT_START_STR_*. If + * its value is SPMI_VS_SOFT_START_STR_HW_DEFAULT, + * then the soft start strength will be left at its + * default hardware value. + */ +struct spmi_regulator_init_data { + unsigned pin_ctrl_enable; + unsigned pin_ctrl_hpm; + enum spmi_vs_soft_start_str vs_soft_start_strength; +}; + /* These types correspond to unique register layouts. */ enum spmi_regulator_logical_type { SPMI_REGULATOR_LOGICAL_TYPE_SMPS, @@ -458,6 +522,14 @@ static int spmi_regulator_vs_enable(struct regulator_dev *rdev) return spmi_regulator_common_enable(rdev); } +static int spmi_regulator_vs_ocp(struct regulator_dev *rdev) +{ + struct spmi_regulator *vreg = rdev_get_drvdata(rdev); + u8 reg = SPMI_VS_OCP_OVERRIDE; + + return spmi_vreg_write(vreg, SPMI_VS_REG_OCP, ®, 1); +} + static int spmi_regulator_common_disable(struct regulator_dev *rdev) { struct spmi_regulator *vreg = rdev_get_drvdata(rdev); @@ -504,8 +576,7 @@ static int spmi_regulator_select_voltage(struct spmi_regulator *vreg, * Force uV to be an allowed set point by applying a ceiling function to * the uV value. */ - *voltage_sel = (uV - range->min_uV + range->step_uV - 1) - / range->step_uV; + *voltage_sel = DIV_ROUND_UP(uV - range->min_uV, range->step_uV); uV = *voltage_sel * range->step_uV + range->min_uV; if (uV > max_uV) { @@ -792,6 +863,9 @@ static unsigned int spmi_regulator_common_get_mode(struct regulator_dev *rdev) if (reg & SPMI_COMMON_MODE_HPM_MASK) return REGULATOR_MODE_NORMAL; + if (reg & SPMI_COMMON_MODE_AUTO_MASK) + return REGULATOR_MODE_FAST; + return REGULATOR_MODE_IDLE; } @@ -799,11 +873,13 @@ static int spmi_regulator_common_set_mode(struct regulator_dev *rdev, unsigned int mode) { struct spmi_regulator *vreg = rdev_get_drvdata(rdev); - u8 mask = SPMI_COMMON_MODE_HPM_MASK; + u8 mask = SPMI_COMMON_MODE_HPM_MASK | SPMI_COMMON_MODE_AUTO_MASK; u8 val = 0; if (mode == REGULATOR_MODE_NORMAL) - val = mask; + val = SPMI_COMMON_MODE_HPM_MASK; + else if (mode == REGULATOR_MODE_FAST) + val = SPMI_COMMON_MODE_AUTO_MASK; return spmi_vreg_update_bits(vreg, SPMI_COMMON_REG_MODE, val, mask); } @@ -973,6 +1049,7 @@ static struct regulator_ops spmi_vs_ops = { .is_enabled = spmi_regulator_common_is_enabled, .set_pull_down = spmi_regulator_common_set_pull_down, .set_soft_start = spmi_regulator_common_set_soft_start, + .set_over_current_protection = spmi_regulator_vs_ocp, }; static struct regulator_ops spmi_boost_ops = { @@ -1203,10 +1280,111 @@ static int spmi_regulator_ftsmps_init_slew_rate(struct spmi_regulator *vreg) return ret; } +static int spmi_regulator_init_registers(struct spmi_regulator *vreg, + const struct spmi_regulator_init_data *data) +{ + int ret; + enum spmi_regulator_logical_type type; + u8 ctrl_reg[8], reg, mask; + + type = vreg->logical_type; + + ret = spmi_vreg_read(vreg, SPMI_COMMON_REG_VOLTAGE_RANGE, ctrl_reg, 8); + if (ret) + return ret; + + /* Set up enable pin control. */ + if ((type == SPMI_REGULATOR_LOGICAL_TYPE_SMPS + || type == SPMI_REGULATOR_LOGICAL_TYPE_LDO + || type == SPMI_REGULATOR_LOGICAL_TYPE_VS) + && !(data->pin_ctrl_enable + & SPMI_REGULATOR_PIN_CTRL_ENABLE_HW_DEFAULT)) { + ctrl_reg[SPMI_COMMON_IDX_ENABLE] &= + ~SPMI_COMMON_ENABLE_FOLLOW_ALL_MASK; + ctrl_reg[SPMI_COMMON_IDX_ENABLE] |= + data->pin_ctrl_enable & SPMI_COMMON_ENABLE_FOLLOW_ALL_MASK; + } + + /* Set up mode pin control. */ + if ((type == SPMI_REGULATOR_LOGICAL_TYPE_SMPS + || type == SPMI_REGULATOR_LOGICAL_TYPE_LDO) + && !(data->pin_ctrl_hpm + & SPMI_REGULATOR_PIN_CTRL_HPM_HW_DEFAULT)) { + ctrl_reg[SPMI_COMMON_IDX_MODE] &= + ~SPMI_COMMON_MODE_FOLLOW_ALL_MASK; + ctrl_reg[SPMI_COMMON_IDX_MODE] |= + data->pin_ctrl_hpm & SPMI_COMMON_MODE_FOLLOW_ALL_MASK; + } + + if (type == SPMI_REGULATOR_LOGICAL_TYPE_VS + && !(data->pin_ctrl_hpm & SPMI_REGULATOR_PIN_CTRL_HPM_HW_DEFAULT)) { + ctrl_reg[SPMI_COMMON_IDX_MODE] &= + ~SPMI_COMMON_MODE_FOLLOW_AWAKE_MASK; + ctrl_reg[SPMI_COMMON_IDX_MODE] |= + data->pin_ctrl_hpm & SPMI_COMMON_MODE_FOLLOW_AWAKE_MASK; + } + + if ((type == SPMI_REGULATOR_LOGICAL_TYPE_ULT_LO_SMPS + || type == SPMI_REGULATOR_LOGICAL_TYPE_ULT_HO_SMPS + || type == SPMI_REGULATOR_LOGICAL_TYPE_ULT_LDO) + && !(data->pin_ctrl_hpm + & SPMI_REGULATOR_PIN_CTRL_HPM_HW_DEFAULT)) { + ctrl_reg[SPMI_COMMON_IDX_MODE] &= + ~SPMI_COMMON_MODE_FOLLOW_AWAKE_MASK; + ctrl_reg[SPMI_COMMON_IDX_MODE] |= + data->pin_ctrl_hpm & SPMI_COMMON_MODE_FOLLOW_AWAKE_MASK; + } + + /* Write back any control register values that were modified. */ + ret = spmi_vreg_write(vreg, SPMI_COMMON_REG_VOLTAGE_RANGE, ctrl_reg, 8); + if (ret) + return ret; + + /* Set soft start strength and over current protection for VS. */ + if (type == SPMI_REGULATOR_LOGICAL_TYPE_VS) { + if (data->vs_soft_start_strength + != SPMI_VS_SOFT_START_STR_HW_DEFAULT) { + reg = data->vs_soft_start_strength + & SPMI_VS_SOFT_START_SEL_MASK; + mask = SPMI_VS_SOFT_START_SEL_MASK; + return spmi_vreg_update_bits(vreg, + SPMI_VS_REG_SOFT_START, + reg, mask); + } + } + + return 0; +} + +static void spmi_regulator_get_dt_config(struct spmi_regulator *vreg, + struct device_node *node, struct spmi_regulator_init_data *data) +{ + /* + * Initialize configuration parameters to use hardware default in case + * no value is specified via device tree. + */ + data->pin_ctrl_enable = SPMI_REGULATOR_PIN_CTRL_ENABLE_HW_DEFAULT; + data->pin_ctrl_hpm = SPMI_REGULATOR_PIN_CTRL_HPM_HW_DEFAULT; + data->vs_soft_start_strength = SPMI_VS_SOFT_START_STR_HW_DEFAULT; + + /* These bindings are optional, so it is okay if they aren't found. */ + of_property_read_u32(node, "qcom,ocp-max-retries", + &vreg->ocp_max_retries); + of_property_read_u32(node, "qcom,ocp-retry-delay", + &vreg->ocp_retry_delay_ms); + of_property_read_u32(node, "qcom,pin-ctrl-enable", + &data->pin_ctrl_enable); + of_property_read_u32(node, "qcom,pin-ctrl-hpm", &data->pin_ctrl_hpm); + of_property_read_u32(node, "qcom,vs-soft-start-strength", + &data->vs_soft_start_strength); +} + static unsigned int spmi_regulator_of_map_mode(unsigned int mode) { - if (mode) + if (mode == 1) return REGULATOR_MODE_NORMAL; + if (mode == 2) + return REGULATOR_MODE_FAST; return REGULATOR_MODE_IDLE; } @@ -1215,12 +1393,23 @@ static int spmi_regulator_of_parse(struct device_node *node, const struct regulator_desc *desc, struct regulator_config *config) { + struct spmi_regulator_init_data data = { }; struct spmi_regulator *vreg = config->driver_data; struct device *dev = config->dev; int ret; - vreg->ocp_max_retries = SPMI_VS_OCP_DEFAULT_MAX_RETRIES; - vreg->ocp_retry_delay_ms = SPMI_VS_OCP_DEFAULT_RETRY_DELAY_MS; + spmi_regulator_get_dt_config(vreg, node, &data); + + if (!vreg->ocp_max_retries) + vreg->ocp_max_retries = SPMI_VS_OCP_DEFAULT_MAX_RETRIES; + if (!vreg->ocp_retry_delay_ms) + vreg->ocp_retry_delay_ms = SPMI_VS_OCP_DEFAULT_RETRY_DELAY_MS; + + ret = spmi_regulator_init_registers(vreg, &data); + if (ret) { + dev_err(dev, "common initialization failed, ret=%d\n", ret); + return ret; + } if (vreg->logical_type == SPMI_REGULATOR_LOGICAL_TYPE_FTSMPS) { ret = spmi_regulator_ftsmps_init_slew_rate(vreg); diff --git a/drivers/regulator/rk808-regulator.c b/drivers/regulator/rk808-regulator.c index 3fd44353cc80..d86a3dcd61e2 100644 --- a/drivers/regulator/rk808-regulator.c +++ b/drivers/regulator/rk808-regulator.c @@ -16,12 +16,16 @@ * more details. */ -#include <linux/module.h> +#include <linux/delay.h> +#include <linux/gpio.h> #include <linux/i2c.h> -#include <linux/mfd/rk808.h> +#include <linux/module.h> #include <linux/of_device.h> +#include <linux/of_gpio.h> +#include <linux/mfd/rk808.h> #include <linux/regulator/driver.h> #include <linux/regulator/of_regulator.h> +#include <linux/gpio/consumer.h> /* Field Definitions */ #define RK808_BUCK_VSEL_MASK 0x3f @@ -36,12 +40,25 @@ #define RK808_RAMP_RATE_6MV_PER_US (2 << RK808_RAMP_RATE_OFFSET) #define RK808_RAMP_RATE_10MV_PER_US (3 << RK808_RAMP_RATE_OFFSET) +#define RK808_DVS2_POL BIT(2) +#define RK808_DVS1_POL BIT(1) + /* Offset from XXX_ON_VSEL to XXX_SLP_VSEL */ #define RK808_SLP_REG_OFFSET 1 +/* Offset from XXX_ON_VSEL to XXX_DVS_VSEL */ +#define RK808_DVS_REG_OFFSET 2 + /* Offset from XXX_EN_REG to SLEEP_SET_OFF_XXX */ #define RK808_SLP_SET_OFF_REG_OFFSET 2 +/* max steps for increase voltage of Buck1/2, equal 100mv*/ +#define MAX_STEPS_ONE_TIME 8 + +struct rk808_regulator_data { + struct gpio_desc *dvs_gpio[2]; +}; + static const int rk808_buck_config_regs[] = { RK808_BUCK1_CONFIG_REG, RK808_BUCK2_CONFIG_REG, @@ -70,6 +87,131 @@ static const struct regulator_linear_range rk808_ldo6_voltage_ranges[] = { REGULATOR_LINEAR_RANGE(800000, 0, 17, 100000), }; +static int rk808_buck1_2_get_voltage_sel_regmap(struct regulator_dev *rdev) +{ + struct rk808_regulator_data *pdata = rdev_get_drvdata(rdev); + int id = rdev->desc->id - RK808_ID_DCDC1; + struct gpio_desc *gpio = pdata->dvs_gpio[id]; + unsigned int val; + int ret; + + if (!gpio || gpiod_get_value(gpio) == 0) + return regulator_get_voltage_sel_regmap(rdev); + + ret = regmap_read(rdev->regmap, + rdev->desc->vsel_reg + RK808_DVS_REG_OFFSET, + &val); + if (ret != 0) + return ret; + + val &= rdev->desc->vsel_mask; + val >>= ffs(rdev->desc->vsel_mask) - 1; + + return val; +} + +static int rk808_buck1_2_i2c_set_voltage_sel(struct regulator_dev *rdev, + unsigned sel) +{ + int ret, delta_sel; + unsigned int old_sel, tmp, val, mask = rdev->desc->vsel_mask; + + ret = regmap_read(rdev->regmap, rdev->desc->vsel_reg, &val); + if (ret != 0) + return ret; + + tmp = val & ~mask; + old_sel = val & mask; + old_sel >>= ffs(mask) - 1; + delta_sel = sel - old_sel; + + /* + * If directly modify the register to change the voltage, we will face + * the risk of overshoot. Put it into a multi-step, can effectively + * avoid this problem, a step is 100mv here. + */ + while (delta_sel > MAX_STEPS_ONE_TIME) { + old_sel += MAX_STEPS_ONE_TIME; + val = old_sel << (ffs(mask) - 1); + val |= tmp; + + /* + * i2c is 400kHz (2.5us per bit) and we must transmit _at least_ + * 3 bytes (24 bits) plus start and stop so 26 bits. So we've + * got more than 65 us between each voltage change and thus + * won't ramp faster than ~1500 uV / us. + */ + ret = regmap_write(rdev->regmap, rdev->desc->vsel_reg, val); + delta_sel = sel - old_sel; + } + + sel <<= ffs(mask) - 1; + val = tmp | sel; + ret = regmap_write(rdev->regmap, rdev->desc->vsel_reg, val); + + /* + * When we change the voltage register directly, the ramp rate is about + * 100000uv/us, wait 1us to make sure the target voltage to be stable, + * so we needn't wait extra time after that. + */ + udelay(1); + + return ret; +} + +static int rk808_buck1_2_set_voltage_sel(struct regulator_dev *rdev, + unsigned sel) +{ + struct rk808_regulator_data *pdata = rdev_get_drvdata(rdev); + int id = rdev->desc->id - RK808_ID_DCDC1; + struct gpio_desc *gpio = pdata->dvs_gpio[id]; + unsigned int reg = rdev->desc->vsel_reg; + unsigned old_sel; + int ret, gpio_level; + + if (!gpio) + return rk808_buck1_2_i2c_set_voltage_sel(rdev, sel); + + gpio_level = gpiod_get_value(gpio); + if (gpio_level == 0) { + reg += RK808_DVS_REG_OFFSET; + ret = regmap_read(rdev->regmap, rdev->desc->vsel_reg, &old_sel); + } else { + ret = regmap_read(rdev->regmap, + reg + RK808_DVS_REG_OFFSET, + &old_sel); + } + + if (ret != 0) + return ret; + + sel <<= ffs(rdev->desc->vsel_mask) - 1; + sel |= old_sel & ~rdev->desc->vsel_mask; + + ret = regmap_write(rdev->regmap, reg, sel); + if (ret) + return ret; + + gpiod_set_value(gpio, !gpio_level); + + return ret; +} + +static int rk808_buck1_2_set_voltage_time_sel(struct regulator_dev *rdev, + unsigned int old_selector, + unsigned int new_selector) +{ + struct rk808_regulator_data *pdata = rdev_get_drvdata(rdev); + int id = rdev->desc->id - RK808_ID_DCDC1; + struct gpio_desc *gpio = pdata->dvs_gpio[id]; + + /* if there is no dvs1/2 pin, we don't need wait extra time here. */ + if (!gpio) + return 0; + + return regulator_set_voltage_time_sel(rdev, old_selector, new_selector); +} + static int rk808_set_ramp_delay(struct regulator_dev *rdev, int ramp_delay) { unsigned int ramp_value = RK808_RAMP_RATE_10MV_PER_US; @@ -137,8 +279,9 @@ static int rk808_set_suspend_disable(struct regulator_dev *rdev) static struct regulator_ops rk808_buck1_2_ops = { .list_voltage = regulator_list_voltage_linear_range, .map_voltage = regulator_map_voltage_linear_range, - .get_voltage_sel = regulator_get_voltage_sel_regmap, - .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = rk808_buck1_2_get_voltage_sel_regmap, + .set_voltage_sel = rk808_buck1_2_set_voltage_sel, + .set_voltage_time_sel = rk808_buck1_2_set_voltage_time_sel, .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, .is_enabled = regulator_is_enabled_regmap, @@ -380,25 +523,69 @@ static struct of_regulator_match rk808_reg_matches[] = { [RK808_ID_SWITCH2] = { .name = "SWITCH_REG2" }, }; +static int rk808_regulator_dt_parse_pdata(struct device *dev, + struct device *client_dev, + struct regmap *map, + struct rk808_regulator_data *pdata) +{ + struct device_node *np; + int tmp, ret, i; + + np = of_get_child_by_name(client_dev->of_node, "regulators"); + if (!np) + return -ENXIO; + + ret = of_regulator_match(dev, np, rk808_reg_matches, + RK808_NUM_REGULATORS); + if (ret < 0) + goto dt_parse_end; + + for (i = 0; i < ARRAY_SIZE(pdata->dvs_gpio); i++) { + pdata->dvs_gpio[i] = + devm_gpiod_get_index_optional(client_dev, "dvs", i, + GPIOD_OUT_LOW); + if (IS_ERR(pdata->dvs_gpio[i])) { + ret = PTR_ERR(pdata->dvs_gpio[i]); + dev_err(dev, "failed to get dvs%d gpio (%d)\n", i, ret); + goto dt_parse_end; + } + + if (!pdata->dvs_gpio[i]) { + dev_warn(dev, "there is no dvs%d gpio\n", i); + continue; + } + + tmp = i ? RK808_DVS2_POL : RK808_DVS1_POL; + ret = regmap_update_bits(map, RK808_IO_POL_REG, tmp, + gpiod_is_active_low(pdata->dvs_gpio[i]) ? + 0 : tmp); + } + +dt_parse_end: + of_node_put(np); + return ret; +} + static int rk808_regulator_probe(struct platform_device *pdev) { struct rk808 *rk808 = dev_get_drvdata(pdev->dev.parent); struct i2c_client *client = rk808->i2c; - struct device_node *reg_np; struct regulator_config config = {}; struct regulator_dev *rk808_rdev; + struct rk808_regulator_data *pdata; int ret, i; - reg_np = of_get_child_by_name(client->dev.of_node, "regulators"); - if (!reg_np) - return -ENXIO; + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; - ret = of_regulator_match(&pdev->dev, reg_np, rk808_reg_matches, - RK808_NUM_REGULATORS); - of_node_put(reg_np); + ret = rk808_regulator_dt_parse_pdata(&pdev->dev, &client->dev, + rk808->regmap, pdata); if (ret < 0) return ret; + platform_set_drvdata(pdev, pdata); + /* Instantiate the regulators */ for (i = 0; i < RK808_NUM_REGULATORS; i++) { if (!rk808_reg_matches[i].init_data || @@ -406,7 +593,7 @@ static int rk808_regulator_probe(struct platform_device *pdev) continue; config.dev = &client->dev; - config.driver_data = rk808; + config.driver_data = pdata; config.regmap = rk808->regmap; config.of_node = rk808_reg_matches[i].of_node; config.init_data = rk808_reg_matches[i].init_data; @@ -427,6 +614,7 @@ static struct platform_driver rk808_regulator_driver = { .probe = rk808_regulator_probe, .driver = { .name = "rk808-regulator", + .owner = THIS_MODULE, }, }; diff --git a/drivers/regulator/tps51632-regulator.c b/drivers/regulator/tps51632-regulator.c index c213e37eb69e..572816e30095 100644 --- a/drivers/regulator/tps51632-regulator.c +++ b/drivers/regulator/tps51632-regulator.c @@ -362,7 +362,6 @@ MODULE_DEVICE_TABLE(i2c, tps51632_id); static struct i2c_driver tps51632_i2c_driver = { .driver = { .name = "tps51632", - .owner = THIS_MODULE, .of_match_table = of_match_ptr(tps51632_of_match), }, .probe = tps51632_probe, diff --git a/drivers/regulator/tps62360-regulator.c b/drivers/regulator/tps62360-regulator.c index a1fd626c6c96..f6a6d36a6533 100644 --- a/drivers/regulator/tps62360-regulator.c +++ b/drivers/regulator/tps62360-regulator.c @@ -515,7 +515,6 @@ MODULE_DEVICE_TABLE(i2c, tps62360_id); static struct i2c_driver tps62360_i2c_driver = { .driver = { .name = "tps62360", - .owner = THIS_MODULE, .of_match_table = of_match_ptr(tps62360_of_match), }, .probe = tps62360_probe, diff --git a/drivers/regulator/tps65023-regulator.c b/drivers/regulator/tps65023-regulator.c index b941e564b3f3..5cc19b44974a 100644 --- a/drivers/regulator/tps65023-regulator.c +++ b/drivers/regulator/tps65023-regulator.c @@ -410,7 +410,6 @@ MODULE_DEVICE_TABLE(i2c, tps_65023_id); static struct i2c_driver tps_65023_i2c_driver = { .driver = { .name = "tps65023", - .owner = THIS_MODULE, }, .probe = tps_65023_probe, .id_table = tps_65023_id, diff --git a/drivers/regulator/tps6586x-regulator.c b/drivers/regulator/tps6586x-regulator.c index 2852de05d64d..9e9d22038017 100644 --- a/drivers/regulator/tps6586x-regulator.c +++ b/drivers/regulator/tps6586x-regulator.c @@ -422,12 +422,12 @@ static struct tps6586x_platform_data *tps6586x_parse_regulator_dt( return NULL; for (i = 0; i < num; i++) { - int id; + uintptr_t id; if (!tps6586x_matches[i].init_data) continue; pdata->reg_init_data[i] = tps6586x_matches[i].init_data; - id = (int)tps6586x_matches[i].driver_data; + id = (uintptr_t)tps6586x_matches[i].driver_data; if (id == TPS6586X_ID_SYS) sys_rail = pdata->reg_init_data[i]->constraints.name; |