diff options
Diffstat (limited to 'drivers/regulator')
-rw-r--r-- | drivers/regulator/Kconfig | 20 | ||||
-rw-r--r-- | drivers/regulator/Makefile | 2 | ||||
-rw-r--r-- | drivers/regulator/bd71815-regulator.c | 652 | ||||
-rw-r--r-- | drivers/regulator/bd71828-regulator.c | 51 | ||||
-rw-r--r-- | drivers/regulator/bd718x7-regulator.c | 60 | ||||
-rw-r--r-- | drivers/regulator/hi6421v600-regulator.c | 299 | ||||
-rw-r--r-- | drivers/regulator/qcom_spmi-regulator.c | 34 | ||||
-rw-r--r-- | drivers/regulator/rohm-regulator.c | 23 | ||||
-rw-r--r-- | drivers/regulator/scmi-regulator.c | 42 |
9 files changed, 1070 insertions, 113 deletions
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 6120c5cf6ccc..3e7a38525cb3 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -204,6 +204,17 @@ config REGULATOR_BD70528 This driver can also be built as a module. If so, the module will be called bd70528-regulator. +config REGULATOR_BD71815 + tristate "ROHM BD71815 Power Regulator" + depends on MFD_ROHM_BD71828 + help + This driver supports voltage regulators on ROHM BD71815 PMIC. + This will enable support for the software controllable buck + and LDO regulators and a current regulator for LEDs. + + This driver can also be built as a module. If so, the module + will be called bd71815-regulator. + config REGULATOR_BD71828 tristate "ROHM BD71828 Power Regulator" depends on MFD_ROHM_BD71828 @@ -422,6 +433,15 @@ config REGULATOR_HI655X This driver provides support for the voltage regulators of the Hisilicon Hi655x PMIC device. +config REGULATOR_HI6421V600 + tristate "HiSilicon Hi6421v600 PMIC voltage regulator support" + depends on MFD_HI6421_SPMI && OF + select REGMAP + help + This driver provides support for the voltage regulators on + HiSilicon Hi6421v600 PMU / Codec IC. + This is used on Kirin 3670 boards, like HiKey 970. + config REGULATOR_ISL9305 tristate "Intersil ISL9305 regulator" depends on I2C diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 44d2f8bf4b74..580b015296ea 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -30,6 +30,7 @@ obj-$(CONFIG_REGULATOR_ATC260X) += atc260x-regulator.o obj-$(CONFIG_REGULATOR_AXP20X) += axp20x-regulator.o obj-$(CONFIG_REGULATOR_BCM590XX) += bcm590xx-regulator.o obj-$(CONFIG_REGULATOR_BD70528) += bd70528-regulator.o +obj-$(CONFIG_REGULATOR_BD71815) += bd71815-regulator.o obj-$(CONFIG_REGULATOR_BD71828) += bd71828-regulator.o obj-$(CONFIG_REGULATOR_BD718XX) += bd718x7-regulator.o obj-$(CONFIG_REGULATOR_BD9571MWV) += bd9571mwv-regulator.o @@ -49,6 +50,7 @@ obj-$(CONFIG_REGULATOR_FAN53880) += fan53880.o obj-$(CONFIG_REGULATOR_GPIO) += gpio-regulator.o obj-$(CONFIG_REGULATOR_HI6421) += hi6421-regulator.o obj-$(CONFIG_REGULATOR_HI6421V530) += hi6421v530-regulator.o +obj-$(CONFIG_REGULATOR_HI6421V600) += hi6421v600-regulator.o obj-$(CONFIG_REGULATOR_HI655X) += hi655x-regulator.o obj-$(CONFIG_REGULATOR_ISL6271A) += isl6271a-regulator.o obj-$(CONFIG_REGULATOR_ISL9305) += isl9305.o diff --git a/drivers/regulator/bd71815-regulator.c b/drivers/regulator/bd71815-regulator.c new file mode 100644 index 000000000000..a4e8d5e36b40 --- /dev/null +++ b/drivers/regulator/bd71815-regulator.c @@ -0,0 +1,652 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright 2014 Embest Technology Co. Ltd. Inc. +// bd71815-regulator.c ROHM BD71815 regulator driver +// +// Author: Tony Luo <luofc@embedinfo.com> +// +// Partially rewritten at 2021 by +// Matti Vaittinen <matti.vaitinen@fi.rohmeurope.com> + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/err.h> +#include <linux/platform_device.h> +#include <linux/regulator/driver.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/gpio.h> +#include <linux/mfd/rohm-generic.h> +#include <linux/mfd/rohm-bd71815.h> +#include <linux/regulator/of_regulator.h> + +struct bd71815_regulator { + struct regulator_desc desc; + const struct rohm_dvs_config *dvs; +}; + +struct bd71815_pmic { + struct bd71815_regulator descs[BD71815_REGULATOR_CNT]; + struct regmap *regmap; + struct device *dev; + struct gpio_descs *gps; + struct regulator_dev *rdev[BD71815_REGULATOR_CNT]; +}; + +static const int bd7181x_wled_currents[] = { + 10, 20, 30, 50, 70, 100, 200, 300, 500, 700, 1000, 2000, 3000, 4000, + 5000, 6000, 7000, 8000, 9000, 10000, 11000, 12000, 13000, 14000, 15000, + 16000, 17000, 18000, 19000, 20000, 21000, 22000, 23000, 24000, 25000, +}; + +static const struct rohm_dvs_config buck1_dvs = { + .level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_SNVS | + ROHM_DVS_LEVEL_SUSPEND | ROHM_DVS_LEVEL_LPSR, + .run_reg = BD71815_REG_BUCK1_VOLT_H, + .run_mask = BD71815_VOLT_MASK, + .run_on_mask = BD71815_BUCK_RUN_ON, + .snvs_on_mask = BD71815_BUCK_SNVS_ON, + .suspend_reg = BD71815_REG_BUCK1_VOLT_L, + .suspend_mask = BD71815_VOLT_MASK, + .suspend_on_mask = BD71815_BUCK_SUSP_ON, + .lpsr_reg = BD71815_REG_BUCK1_VOLT_L, + .lpsr_mask = BD71815_VOLT_MASK, + .lpsr_on_mask = BD71815_BUCK_LPSR_ON, +}; + +static const struct rohm_dvs_config buck2_dvs = { + .level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_SNVS | + ROHM_DVS_LEVEL_SUSPEND | ROHM_DVS_LEVEL_LPSR, + .run_reg = BD71815_REG_BUCK2_VOLT_H, + .run_mask = BD71815_VOLT_MASK, + .run_on_mask = BD71815_BUCK_RUN_ON, + .snvs_on_mask = BD71815_BUCK_SNVS_ON, + .suspend_reg = BD71815_REG_BUCK2_VOLT_L, + .suspend_mask = BD71815_VOLT_MASK, + .suspend_on_mask = BD71815_BUCK_SUSP_ON, + .lpsr_reg = BD71815_REG_BUCK2_VOLT_L, + .lpsr_mask = BD71815_VOLT_MASK, + .lpsr_on_mask = BD71815_BUCK_LPSR_ON, +}; + +static const struct rohm_dvs_config buck3_dvs = { + .level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_SNVS | + ROHM_DVS_LEVEL_SUSPEND | ROHM_DVS_LEVEL_LPSR, + .run_reg = BD71815_REG_BUCK3_VOLT, + .run_mask = BD71815_VOLT_MASK, + .run_on_mask = BD71815_BUCK_RUN_ON, + .snvs_on_mask = BD71815_BUCK_SNVS_ON, + .suspend_on_mask = BD71815_BUCK_SUSP_ON, + .lpsr_on_mask = BD71815_BUCK_LPSR_ON, +}; + +static const struct rohm_dvs_config buck4_dvs = { + .level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_SNVS | + ROHM_DVS_LEVEL_SUSPEND | ROHM_DVS_LEVEL_LPSR, + .run_reg = BD71815_REG_BUCK4_VOLT, + .run_mask = BD71815_VOLT_MASK, + .run_on_mask = BD71815_BUCK_RUN_ON, + .snvs_on_mask = BD71815_BUCK_SNVS_ON, + .suspend_on_mask = BD71815_BUCK_SUSP_ON, + .lpsr_on_mask = BD71815_BUCK_LPSR_ON, +}; + +static const struct rohm_dvs_config ldo1_dvs = { + .level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_SNVS | + ROHM_DVS_LEVEL_SUSPEND | ROHM_DVS_LEVEL_LPSR, + .run_reg = BD71815_REG_LDO_MODE1, + .run_mask = BD71815_VOLT_MASK, + .run_on_mask = LDO1_RUN_ON, + .snvs_on_mask = LDO1_SNVS_ON, + .suspend_on_mask = LDO1_SUSP_ON, + .lpsr_on_mask = LDO1_LPSR_ON, +}; + +static const struct rohm_dvs_config ldo2_dvs = { + .level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_SNVS | + ROHM_DVS_LEVEL_SUSPEND | ROHM_DVS_LEVEL_LPSR, + .run_reg = BD71815_REG_LDO_MODE2, + .run_mask = BD71815_VOLT_MASK, + .run_on_mask = LDO2_RUN_ON, + .snvs_on_mask = LDO2_SNVS_ON, + .suspend_on_mask = LDO2_SUSP_ON, + .lpsr_on_mask = LDO2_LPSR_ON, +}; + +static const struct rohm_dvs_config ldo3_dvs = { + .level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_SNVS | + ROHM_DVS_LEVEL_SUSPEND | ROHM_DVS_LEVEL_LPSR, + .run_reg = BD71815_REG_LDO_MODE2, + .run_mask = BD71815_VOLT_MASK, + .run_on_mask = LDO3_RUN_ON, + .snvs_on_mask = LDO3_SNVS_ON, + .suspend_on_mask = LDO3_SUSP_ON, + .lpsr_on_mask = LDO3_LPSR_ON, +}; + +static const struct rohm_dvs_config ldo4_dvs = { + .level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_SNVS | + ROHM_DVS_LEVEL_SUSPEND | ROHM_DVS_LEVEL_LPSR, + .run_reg = BD71815_REG_LDO_MODE3, + .run_mask = BD71815_VOLT_MASK, + .run_on_mask = LDO4_RUN_ON, + .snvs_on_mask = LDO4_SNVS_ON, + .suspend_on_mask = LDO4_SUSP_ON, + .lpsr_on_mask = LDO4_LPSR_ON, +}; + +static const struct rohm_dvs_config ldo5_dvs = { + .level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_SNVS | + ROHM_DVS_LEVEL_SUSPEND | ROHM_DVS_LEVEL_LPSR, + .run_reg = BD71815_REG_LDO_MODE3, + .run_mask = BD71815_VOLT_MASK, + .run_on_mask = LDO5_RUN_ON, + .snvs_on_mask = LDO5_SNVS_ON, + .suspend_on_mask = LDO5_SUSP_ON, + .lpsr_on_mask = LDO5_LPSR_ON, +}; + +static const struct rohm_dvs_config dvref_dvs = { + .level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_SNVS | + ROHM_DVS_LEVEL_SUSPEND | ROHM_DVS_LEVEL_LPSR, + .run_on_mask = DVREF_RUN_ON, + .snvs_on_mask = DVREF_SNVS_ON, + .suspend_on_mask = DVREF_SUSP_ON, + .lpsr_on_mask = DVREF_LPSR_ON, +}; + +static const struct rohm_dvs_config ldolpsr_dvs = { + .level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_SNVS | + ROHM_DVS_LEVEL_SUSPEND | ROHM_DVS_LEVEL_LPSR, + .run_on_mask = DVREF_RUN_ON, + .snvs_on_mask = DVREF_SNVS_ON, + .suspend_on_mask = DVREF_SUSP_ON, + .lpsr_on_mask = DVREF_LPSR_ON, +}; + +static const struct rohm_dvs_config buck5_dvs = { + .level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_SNVS | + ROHM_DVS_LEVEL_SUSPEND | ROHM_DVS_LEVEL_LPSR, + .run_reg = BD71815_REG_BUCK5_VOLT, + .run_mask = BD71815_VOLT_MASK, + .run_on_mask = BD71815_BUCK_RUN_ON, + .snvs_on_mask = BD71815_BUCK_SNVS_ON, + .suspend_on_mask = BD71815_BUCK_SUSP_ON, + .lpsr_on_mask = BD71815_BUCK_LPSR_ON, +}; + +static int set_hw_dvs_levels(struct device_node *np, + const struct regulator_desc *desc, + struct regulator_config *cfg) +{ + struct bd71815_regulator *data; + + data = container_of(desc, struct bd71815_regulator, desc); + return rohm_regulator_set_dvs_levels(data->dvs, np, desc, cfg->regmap); +} + +/* + * Bucks 1 and 2 have two voltage selection registers where selected + * voltage can be set. Which of the registers is used can be either controlled + * by a control bit in register - or by HW state. If HW state specific voltages + * are given - then we assume HW state based control should be used. + * + * If volatge value is updated to currently selected register - then output + * voltage is immediately changed no matter what is set as ramp rate. Thus we + * default changing voltage by writing new value to inactive register and + * then updating the 'register selection' bit. This naturally only works when + * HW state machine is not used to select the voltage. + */ +static int buck12_set_hw_dvs_levels(struct device_node *np, + const struct regulator_desc *desc, + struct regulator_config *cfg) +{ + struct bd71815_regulator *data; + int ret = 0, val; + + data = container_of(desc, struct bd71815_regulator, desc); + + if (of_find_property(np, "rohm,dvs-run-voltage", NULL) || + of_find_property(np, "rohm,dvs-suspend-voltage", NULL) || + of_find_property(np, "rohm,dvs-lpsr-voltage", NULL) || + of_find_property(np, "rohm,dvs-snvs-voltage", NULL)) { + ret = regmap_read(cfg->regmap, desc->vsel_reg, &val); + if (ret) + return ret; + + if (!(BD71815_BUCK_STBY_DVS & val) && + !(BD71815_BUCK_DVSSEL & val)) { + int val2; + + /* + * We are currently using voltage from _L. + * We'd better copy it to _H and switch to it to + * avoid shutting us down if LPSR or SUSPEND is set to + * disabled. _L value is at reg _H + 1 + */ + ret = regmap_read(cfg->regmap, desc->vsel_reg + 1, + &val2); + if (ret) + return ret; + + ret = regmap_update_bits(cfg->regmap, desc->vsel_reg, + BD71815_VOLT_MASK | + BD71815_BUCK_DVSSEL, + val2 | BD71815_BUCK_DVSSEL); + if (ret) + return ret; + } + ret = rohm_regulator_set_dvs_levels(data->dvs, np, desc, + cfg->regmap); + if (ret) + return ret; + /* + * DVS levels were given => use HW-state machine for voltage + * controls. NOTE: AFAIK, This means that if voltage is changed + * by SW the ramp-rate is not respected. Should we disable + * SW voltage control when the HW state machine is used? + */ + ret = regmap_update_bits(cfg->regmap, desc->vsel_reg, + BD71815_BUCK_STBY_DVS, + BD71815_BUCK_STBY_DVS); + } + + return ret; +} + +/* + * BUCK1/2 + * BUCK1RAMPRATE[1:0] BUCK1 DVS ramp rate setting + * 00: 10.00mV/usec 10mV 1uS + * 01: 5.00mV/usec 10mV 2uS + * 10: 2.50mV/usec 10mV 4uS + * 11: 1.25mV/usec 10mV 8uS + */ +static const unsigned int bd7181x_ramp_table[] = { 1250, 2500, 5000, 10000 }; + +static int bd7181x_led_set_current_limit(struct regulator_dev *rdev, + int min_uA, int max_uA) +{ + int ret; + int onstatus; + + onstatus = regulator_is_enabled_regmap(rdev); + + ret = regulator_set_current_limit_regmap(rdev, min_uA, max_uA); + if (!ret) { + int newstatus; + + newstatus = regulator_is_enabled_regmap(rdev); + if (onstatus != newstatus) { + /* + * HW FIX: spurious led status change detected. Toggle + * state as a workaround + */ + if (onstatus) + ret = regulator_enable_regmap(rdev); + else + ret = regulator_disable_regmap(rdev); + + if (ret) + dev_err(rdev_get_dev(rdev), + "failed to revert the LED state (%d)\n", + ret); + } + } + + return ret; +} + +static int bd7181x_buck12_get_voltage_sel(struct regulator_dev *rdev) +{ + struct bd71815_pmic *pmic = rdev_get_drvdata(rdev); + int rid = rdev_get_id(rdev); + int ret, regh, regl, val; + + regh = BD71815_REG_BUCK1_VOLT_H + rid * 0x2; + regl = BD71815_REG_BUCK1_VOLT_L + rid * 0x2; + + ret = regmap_read(pmic->regmap, regh, &val); + if (ret) + return ret; + + /* + * If we use HW state machine based voltage reg selection - then we + * return BD71815_REG_BUCK1_VOLT_H which is used at RUN. + * Else we do return the BD71815_REG_BUCK1_VOLT_H or + * BD71815_REG_BUCK1_VOLT_L depending on which is selected to be used + * by BD71815_BUCK_DVSSEL bit + */ + if ((!(val & BD71815_BUCK_STBY_DVS)) && (!(val & BD71815_BUCK_DVSSEL))) + ret = regmap_read(pmic->regmap, regl, &val); + + if (ret) + return ret; + + return val & BD71815_VOLT_MASK; +} + +/* + * For Buck 1/2. + */ +static int bd7181x_buck12_set_voltage_sel(struct regulator_dev *rdev, + unsigned int sel) +{ + struct bd71815_pmic *pmic = rdev_get_drvdata(rdev); + int rid = rdev_get_id(rdev); + int ret, val, reg, regh, regl; + + regh = BD71815_REG_BUCK1_VOLT_H + rid*0x2; + regl = BD71815_REG_BUCK1_VOLT_L + rid*0x2; + + ret = regmap_read(pmic->regmap, regh, &val); + if (ret) + return ret; + + /* + * If bucks 1 & 2 are controlled by state machine - then the RUN state + * voltage is set to BD71815_REG_BUCK1_VOLT_H. Changing SUSPEND/LPSR + * voltages at runtime is not supported by this driver. + */ + if (((val & BD71815_BUCK_STBY_DVS))) { + return regmap_update_bits(pmic->regmap, regh, BD71815_VOLT_MASK, + sel); + } + /* Update new voltage to the register which is not selected now */ + if (val & BD71815_BUCK_DVSSEL) + reg = regl; + else + reg = regh; + + ret = regmap_update_bits(pmic->regmap, reg, BD71815_VOLT_MASK, sel); + if (ret) + return ret; + + /* Select the other DVS register to be used */ + return regmap_update_bits(pmic->regmap, regh, BD71815_BUCK_DVSSEL, ~val); +} + +static const struct regulator_ops bd7181x_ldo_regulator_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .list_voltage = regulator_list_voltage_linear, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, +}; + +static const struct regulator_ops bd7181x_fixed_regulator_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .list_voltage = regulator_list_voltage_linear, +}; + +static const struct regulator_ops bd7181x_buck_regulator_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .list_voltage = regulator_list_voltage_linear, + .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, +}; + +static const struct regulator_ops bd7181x_buck12_regulator_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .list_voltage = regulator_list_voltage_linear, + .set_voltage_sel = bd7181x_buck12_set_voltage_sel, + .get_voltage_sel = bd7181x_buck12_get_voltage_sel, + .set_voltage_time_sel = regulator_set_voltage_time_sel, + .set_ramp_delay = regulator_set_ramp_delay_regmap, +}; + +static const struct regulator_ops bd7181x_led_regulator_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .set_current_limit = bd7181x_led_set_current_limit, + .get_current_limit = regulator_get_current_limit_regmap, +}; + +#define BD71815_FIXED_REG(_name, _id, ereg, emsk, voltage, _dvs) \ + [(_id)] = { \ + .desc = { \ + .name = #_name, \ + .of_match = of_match_ptr(#_name), \ + .regulators_node = of_match_ptr("regulators"), \ + .n_voltages = 1, \ + .ops = &bd7181x_fixed_regulator_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = (_id), \ + .owner = THIS_MODULE, \ + .min_uV = (voltage), \ + .enable_reg = (ereg), \ + .enable_mask = (emsk), \ + .of_parse_cb = set_hw_dvs_levels, \ + }, \ + .dvs = (_dvs), \ + } + +#define BD71815_BUCK_REG(_name, _id, vsel, ereg, min, max, step, _dvs) \ + [(_id)] = { \ + .desc = { \ + .name = #_name, \ + .of_match = of_match_ptr(#_name), \ + .regulators_node = of_match_ptr("regulators"), \ + .n_voltages = ((max) - (min)) / (step) + 1, \ + .ops = &bd7181x_buck_regulator_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = (_id), \ + .owner = THIS_MODULE, \ + .min_uV = (min), \ + .uV_step = (step), \ + .vsel_reg = (vsel), \ + .vsel_mask = BD71815_VOLT_MASK, \ + .enable_reg = (ereg), \ + .enable_mask = BD71815_BUCK_RUN_ON, \ + .of_parse_cb = set_hw_dvs_levels, \ + }, \ + .dvs = (_dvs), \ + } + +#define BD71815_BUCK12_REG(_name, _id, vsel, ereg, min, max, step, \ + _dvs) \ + [(_id)] = { \ + .desc = { \ + .name = #_name, \ + .of_match = of_match_ptr(#_name), \ + .regulators_node = of_match_ptr("regulators"), \ + .n_voltages = ((max) - (min)) / (step) + 1, \ + .ops = &bd7181x_buck12_regulator_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = (_id), \ + .owner = THIS_MODULE, \ + .min_uV = (min), \ + .uV_step = (step), \ + .vsel_reg = (vsel), \ + .vsel_mask = 0x3f, \ + .enable_reg = (ereg), \ + .enable_mask = 0x04, \ + .ramp_reg = (ereg), \ + .ramp_mask = BD71815_BUCK_RAMPRATE_MASK, \ + .ramp_delay_table = bd7181x_ramp_table, \ + .n_ramp_values = ARRAY_SIZE(bd7181x_ramp_table),\ + .of_parse_cb = buck12_set_hw_dvs_levels, \ + }, \ + .dvs = (_dvs), \ + } + +#define BD71815_LED_REG(_name, _id, csel, mask, ereg, emsk, currents) \ + [(_id)] = { \ + .desc = { \ + .name = #_name, \ + .of_match = of_match_ptr(#_name), \ + .regulators_node = of_match_ptr("regulators"), \ + .n_current_limits = ARRAY_SIZE(currents), \ + .ops = &bd7181x_led_regulator_ops, \ + .type = REGULATOR_CURRENT, \ + .id = (_id), \ + .owner = THIS_MODULE, \ + .curr_table = currents, \ + .csel_reg = (csel), \ + .csel_mask = (mask), \ + .enable_reg = (ereg), \ + .enable_mask = (emsk), \ + }, \ + } + +#define BD71815_LDO_REG(_name, _id, vsel, ereg, emsk, min, max, step, \ + _dvs) \ + [(_id)] = { \ + .desc = { \ + .name = #_name, \ + .of_match = of_match_ptr(#_name), \ + .regulators_node = of_match_ptr("regulators"), \ + .n_voltages = ((max) - (min)) / (step) + 1, \ + .ops = &bd7181x_ldo_regulator_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = (_id), \ + .owner = THIS_MODULE, \ + .min_uV = (min), \ + .uV_step = (step), \ + .vsel_reg = (vsel), \ + .vsel_mask = BD71815_VOLT_MASK, \ + .enable_reg = (ereg), \ + .enable_mask = (emsk), \ + .of_parse_cb = set_hw_dvs_levels, \ + }, \ + .dvs = (_dvs), \ + } + +static struct bd71815_regulator bd71815_regulators[] = { + BD71815_BUCK12_REG(buck1, BD71815_BUCK1, BD71815_REG_BUCK1_VOLT_H, + BD71815_REG_BUCK1_MODE, 800000, 2000000, 25000, + &buck1_dvs), + BD71815_BUCK12_REG(buck2, BD71815_BUCK2, BD71815_REG_BUCK2_VOLT_H, + BD71815_REG_BUCK2_MODE, 800000, 2000000, 25000, + &buck2_dvs), + BD71815_BUCK_REG(buck3, BD71815_BUCK3, BD71815_REG_BUCK3_VOLT, + BD71815_REG_BUCK3_MODE, 1200000, 2700000, 50000, + &buck3_dvs), + BD71815_BUCK_REG(buck4, BD71815_BUCK4, BD71815_REG_BUCK4_VOLT, + BD71815_REG_BUCK4_MODE, 1100000, 1850000, 25000, + &buck4_dvs), + BD71815_BUCK_REG(buck5, BD71815_BUCK5, BD71815_REG_BUCK5_VOLT, + BD71815_REG_BUCK5_MODE, 1800000, 3300000, 50000, + &buck5_dvs), + BD71815_LDO_REG(ldo1, BD71815_LDO1, BD71815_REG_LDO1_VOLT, + BD71815_REG_LDO_MODE1, LDO1_RUN_ON, 800000, 3300000, + 50000, &ldo1_dvs), + BD71815_LDO_REG(ldo2, BD71815_LDO2, BD71815_REG_LDO2_VOLT, + BD71815_REG_LDO_MODE2, LDO2_RUN_ON, 800000, 3300000, + 50000, &ldo2_dvs), + /* + * Let's default LDO3 to be enabled by SW. We can override ops if DT + * says LDO3 should be enabled by HW when DCIN is connected. + */ + BD71815_LDO_REG(ldo3, BD71815_LDO3, BD71815_REG_LDO3_VOLT, + BD71815_REG_LDO_MODE2, LDO3_RUN_ON, 800000, 3300000, + 50000, &ldo3_dvs), + BD71815_LDO_REG(ldo4, BD71815_LDO4, BD71815_REG_LDO4_VOLT, + BD71815_REG_LDO_MODE3, LDO4_RUN_ON, 800000, 3300000, + 50000, &ldo4_dvs), + BD71815_LDO_REG(ldo5, BD71815_LDO5, BD71815_REG_LDO5_VOLT_H, + BD71815_REG_LDO_MODE3, LDO5_RUN_ON, 800000, 3300000, + 50000, &ldo5_dvs), + BD71815_FIXED_REG(ldodvref, BD71815_LDODVREF, BD71815_REG_LDO_MODE4, + DVREF_RUN_ON, 3000000, &dvref_dvs), + BD71815_FIXED_REG(ldolpsr, BD71815_LDOLPSR, BD71815_REG_LDO_MODE4, + LDO_LPSR_RUN_ON, 1800000, &ldolpsr_dvs), + BD71815_LED_REG(wled, BD71815_WLED, BD71815_REG_LED_DIMM, LED_DIMM_MASK, + BD71815_REG_LED_CTRL, LED_RUN_ON, + bd7181x_wled_currents), +}; + +static int bd7181x_probe(struct platform_device *pdev) +{ + struct bd71815_pmic *pmic; + struct regulator_config config = {}; + int i, ret; + struct gpio_desc *ldo4_en; + + pmic = devm_kzalloc(&pdev->dev, sizeof(*pmic), GFP_KERNEL); + if (!pmic) + return -ENOMEM; + + memcpy(pmic->descs, bd71815_regulators, sizeof(pmic->descs)); + + pmic->dev = &pdev->dev; + pmic->regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!pmic->regmap) { + dev_err(pmic->dev, "No parent regmap\n"); + return -ENODEV; + } + platform_set_drvdata(pdev, pmic); + ldo4_en = devm_gpiod_get_from_of_node(&pdev->dev, + pdev->dev.parent->of_node, + "rohm,vsel-gpios", 0, + GPIOD_ASIS, "ldo4-en"); + + if (IS_ERR(ldo4_en)) { + ret = PTR_ERR(ldo4_en); + if (ret != -ENOENT) + return ret; + ldo4_en = NULL; + } + + /* Disable to go to ship-mode */ + ret = regmap_update_bits(pmic->regmap, BD71815_REG_PWRCTRL, + RESTARTEN, 0); + if (ret) + return ret; + + config.dev = pdev->dev.parent; + config.regmap = pmic->regmap; + + for (i = 0; i < BD71815_REGULATOR_CNT; i++) { + struct regulator_desc *desc; + struct regulator_dev *rdev; + + desc = &pmic->descs[i].desc; + if (i == BD71815_LDO4) + config.ena_gpiod = ldo4_en; + + config.driver_data = pmic; + + rdev = devm_regulator_register(&pdev->dev, desc, &config); + if (IS_ERR(rdev)) { + dev_err(&pdev->dev, + "failed to register %s regulator\n", + desc->name); + return PTR_ERR(rdev); + } + config.ena_gpiod = NULL; + pmic->rdev[i] = rdev; + } + return 0; +} + +static const struct platform_device_id bd7181x_pmic_id[] = { + { "bd71815-pmic", ROHM_CHIP_TYPE_BD71815 }, + { }, +}; +MODULE_DEVICE_TABLE(platform, bd7181x_pmic_id); + +static struct platform_driver bd7181x_regulator = { + .driver = { + .name = "bd7181x-pmic", + .owner = THIS_MODULE, + }, + .probe = bd7181x_probe, + .id_table = bd7181x_pmic_id, +}; +module_platform_driver(bd7181x_regulator); + +MODULE_AUTHOR("Tony Luo <luofc@embedinfo.com>"); +MODULE_DESCRIPTION("BD71815 voltage regulator driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:bd7181x-pmic"); diff --git a/drivers/regulator/bd71828-regulator.c b/drivers/regulator/bd71828-regulator.c index 6b12e963ed8f..a4f09a5a30ca 100644 --- a/drivers/regulator/bd71828-regulator.c +++ b/drivers/regulator/bd71828-regulator.c @@ -90,38 +90,7 @@ static const struct linear_range bd71828_ldo_volts[] = { REGULATOR_LINEAR_RANGE(3300000, 0x32, 0x3f, 0), }; -static int bd71828_set_ramp_delay(struct regulator_dev *rdev, int ramp_delay) -{ - unsigned int val; - - switch (ramp_delay) { - case 1 ... 2500: - val = 0; - break; - case 2501 ... 5000: - val = 1; - break; - case 5001 ... 10000: - val = 2; - break; - case 10001 ... 20000: - val = 3; - break; - default: - val = 3; - dev_err(&rdev->dev, - "ramp_delay: %d not supported, setting 20mV/uS", - ramp_delay); - } - - /* - * On BD71828 the ramp delay level control reg is at offset +2 to - * enable reg - */ - return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg + 2, - BD71828_MASK_RAMP_DELAY, - val << (ffs(BD71828_MASK_RAMP_DELAY) - 1)); -} +static const unsigned int bd71828_ramp_delay[] = { 2500, 5000, 10000, 20000 }; static int buck_set_hw_dvs_levels(struct device_node *np, const struct regulator_desc *desc, @@ -185,7 +154,7 @@ static const struct regulator_ops bd71828_dvs_buck_ops = { .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, - .set_ramp_delay = bd71828_set_ramp_delay, + .set_ramp_delay = regulator_set_ramp_delay_regmap, }; static const struct regulator_ops bd71828_ldo_ops = { @@ -219,6 +188,10 @@ static const struct bd71828_regulator_data bd71828_rdata[] = { .enable_mask = BD71828_MASK_RUN_EN, .vsel_reg = BD71828_REG_BUCK1_VOLT, .vsel_mask = BD71828_MASK_BUCK1267_VOLT, + .ramp_delay_table = bd71828_ramp_delay, + .n_ramp_values = ARRAY_SIZE(bd71828_ramp_delay), + .ramp_reg = BD71828_REG_BUCK1_MODE, + .ramp_mask = BD71828_MASK_RAMP_DELAY, .owner = THIS_MODULE, .of_parse_cb = buck_set_hw_dvs_levels, }, @@ -261,6 +234,10 @@ static const struct bd71828_regulator_data bd71828_rdata[] = { .enable_mask = BD71828_MASK_RUN_EN, .vsel_reg = BD71828_REG_BUCK2_VOLT, .vsel_mask = BD71828_MASK_BUCK1267_VOLT, + .ramp_delay_table = bd71828_ramp_delay, + .n_ramp_values = ARRAY_SIZE(bd71828_ramp_delay), + .ramp_reg = BD71828_REG_BUCK2_MODE, + .ramp_mask = BD71828_MASK_RAMP_DELAY, .owner = THIS_MODULE, .of_parse_cb = buck_set_hw_dvs_levels, }, @@ -421,6 +398,10 @@ static const struct bd71828_regulator_data bd71828_rdata[] = { .enable_mask = BD71828_MASK_RUN_EN, .vsel_reg = BD71828_REG_BUCK6_VOLT, .vsel_mask = BD71828_MASK_BUCK1267_VOLT, + .ramp_delay_table = bd71828_ramp_delay, + .n_ramp_values = ARRAY_SIZE(bd71828_ramp_delay), + .ramp_reg = BD71828_REG_BUCK6_MODE, + .ramp_mask = BD71828_MASK_RAMP_DELAY, .owner = THIS_MODULE, .of_parse_cb = buck_set_hw_dvs_levels, }, @@ -458,6 +439,10 @@ static const struct bd71828_regulator_data bd71828_rdata[] = { .enable_mask = BD71828_MASK_RUN_EN, .vsel_reg = BD71828_REG_BUCK7_VOLT, .vsel_mask = BD71828_MASK_BUCK1267_VOLT, + .ramp_delay_table = bd71828_ramp_delay, + .n_ramp_values = ARRAY_SIZE(bd71828_ramp_delay), + .ramp_reg = BD71828_REG_BUCK7_MODE, + .ramp_mask = BD71828_MASK_RAMP_DELAY, .owner = THIS_MODULE, .of_parse_cb = buck_set_hw_dvs_levels, }, diff --git a/drivers/regulator/bd718x7-regulator.c b/drivers/regulator/bd718x7-regulator.c index 5b6f4d3d1a14..b1eb46961993 100644 --- a/drivers/regulator/bd718x7-regulator.c +++ b/drivers/regulator/bd718x7-regulator.c @@ -86,37 +86,7 @@ static const struct regulator_ops BD718XX_HWOPNAME(name) = { \ * 10: 2.50mV/usec 10mV 4uS * 11: 1.25mV/usec 10mV 8uS */ -static int bd718xx_buck1234_set_ramp_delay(struct regulator_dev *rdev, - int ramp_delay) -{ - int id = rdev_get_id(rdev); - unsigned int ramp_value; - - dev_dbg(&rdev->dev, "Buck[%d] Set Ramp = %d\n", id + 1, - ramp_delay); - switch (ramp_delay) { - case 1 ... 1250: - ramp_value = BUCK_RAMPRATE_1P25MV; - break; - case 1251 ... 2500: - ramp_value = BUCK_RAMPRATE_2P50MV; - break; - case 2501 ... 5000: - ramp_value = BUCK_RAMPRATE_5P00MV; - break; - case 5001 ... 10000: - ramp_value = BUCK_RAMPRATE_10P00MV; - break; - default: - ramp_value = BUCK_RAMPRATE_10P00MV; - dev_err(&rdev->dev, - "%s: ramp_delay: %d not supported, setting 10000mV//us\n", - rdev->desc->name, ramp_delay); - } - - return regmap_update_bits(rdev->regmap, BD718XX_REG_BUCK1_CTRL + id, - BUCK_RAMPRATE_MASK, ramp_value << 6); -} +static const unsigned int bd718xx_ramp_delay[] = { 10000, 5000, 2500, 1250 }; /* These functions are used when regulators are under HW state machine control. * We assume PMIC is in RUN state because SW running and able to query the @@ -378,7 +348,7 @@ static const struct regulator_ops bd71837_buck34_ops_hwctrl = { .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, - .set_ramp_delay = bd718xx_buck1234_set_ramp_delay, + .set_ramp_delay = regulator_set_ramp_delay_regmap, }; /* @@ -387,7 +357,7 @@ static const struct regulator_ops bd71837_buck34_ops_hwctrl = { BD718XX_OPS(bd718xx_dvs_buck_regulator_ops, regulator_list_voltage_linear_range, NULL, regulator_set_voltage_sel_regmap, regulator_get_voltage_sel_regmap, regulator_set_voltage_time_sel, - bd718xx_buck1234_set_ramp_delay); + /* bd718xx_buck1234_set_ramp_delay */ regulator_set_ramp_delay_regmap); /* * BD71837 BUCK1/2/3/4 @@ -645,6 +615,10 @@ static struct bd718xx_regulator_data bd71847_regulators[] = { .enable_mask = BD718XX_BUCK_EN, .enable_time = BD71847_BUCK1_STARTUP_TIME, .owner = THIS_MODULE, + .ramp_delay_table = bd718xx_ramp_delay, + .n_ramp_values = ARRAY_SIZE(bd718xx_ramp_delay), + .ramp_reg = BD718XX_REG_BUCK1_CTRL, + .ramp_mask = BUCK_RAMPRATE_MASK, .of_parse_cb = buck_set_hw_dvs_levels, }, .dvs = { @@ -678,6 +652,10 @@ static struct bd718xx_regulator_data bd71847_regulators[] = { .enable_reg = BD718XX_REG_BUCK2_CTRL, .enable_mask = BD718XX_BUCK_EN, .enable_time = BD71847_BUCK2_STARTUP_TIME, + .ramp_delay_table = bd718xx_ramp_delay, + .n_ramp_values = ARRAY_SIZE(bd718xx_ramp_delay), + .ramp_reg = BD718XX_REG_BUCK2_CTRL, + .ramp_mask = BUCK_RAMPRATE_MASK, .owner = THIS_MODULE, .of_parse_cb = buck_set_hw_dvs_levels, }, @@ -985,6 +963,10 @@ static struct bd718xx_regulator_data bd71837_regulators[] = { .enable_reg = BD718XX_REG_BUCK1_CTRL, .enable_mask = BD718XX_BUCK_EN, .enable_time = BD71837_BUCK1_STARTUP_TIME, + .ramp_delay_table = bd718xx_ramp_delay, + .n_ramp_values = ARRAY_SIZE(bd718xx_ramp_delay), + .ramp_reg = BD718XX_REG_BUCK1_CTRL, + .ramp_mask = BUCK_RAMPRATE_MASK, .owner = THIS_MODULE, .of_parse_cb = buck_set_hw_dvs_levels, }, @@ -1019,6 +1001,10 @@ static struct bd718xx_regulator_data bd71837_regulators[] = { .enable_reg = BD718XX_REG_BUCK2_CTRL, .enable_mask = BD718XX_BUCK_EN, .enable_time = BD71837_BUCK2_STARTUP_TIME, + .ramp_delay_table = bd718xx_ramp_delay, + .n_ramp_values = ARRAY_SIZE(bd718xx_ramp_delay), + .ramp_reg = BD718XX_REG_BUCK2_CTRL, + .ramp_mask = BUCK_RAMPRATE_MASK, .owner = THIS_MODULE, .of_parse_cb = buck_set_hw_dvs_levels, }, @@ -1050,6 +1036,10 @@ static struct bd718xx_regulator_data bd71837_regulators[] = { .enable_reg = BD71837_REG_BUCK3_CTRL, .enable_mask = BD718XX_BUCK_EN, .enable_time = BD71837_BUCK3_STARTUP_TIME, + .ramp_delay_table = bd718xx_ramp_delay, + .n_ramp_values = ARRAY_SIZE(bd718xx_ramp_delay), + .ramp_reg = BD71837_REG_BUCK3_CTRL, + .ramp_mask = BUCK_RAMPRATE_MASK, .owner = THIS_MODULE, .of_parse_cb = buck_set_hw_dvs_levels, }, @@ -1079,6 +1069,10 @@ static struct bd718xx_regulator_data bd71837_regulators[] = { .enable_reg = BD71837_REG_BUCK4_CTRL, .enable_mask = BD718XX_BUCK_EN, .enable_time = BD71837_BUCK4_STARTUP_TIME, + .ramp_delay_table = bd718xx_ramp_delay, + .n_ramp_values = ARRAY_SIZE(bd718xx_ramp_delay), + .ramp_reg = BD71837_REG_BUCK4_CTRL, + .ramp_mask = BUCK_RAMPRATE_MASK, .owner = THIS_MODULE, .of_parse_cb = buck_set_hw_dvs_levels, }, diff --git a/drivers/regulator/hi6421v600-regulator.c b/drivers/regulator/hi6421v600-regulator.c new file mode 100644 index 000000000000..d6340bb49296 --- /dev/null +++ b/drivers/regulator/hi6421v600-regulator.c @@ -0,0 +1,299 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Device driver for regulators in Hisi IC +// +// Copyright (c) 2013 Linaro Ltd. +// Copyright (c) 2011 HiSilicon Ltd. +// Copyright (c) 2020-2021 Huawei Technologies Co., Ltd +// +// Guodong Xu <guodong.xu@linaro.org> + +#include <linux/delay.h> +#include <linux/mfd/hi6421-spmi-pmic.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/regulator/driver.h> +#include <linux/spmi.h> + +struct hi6421_spmi_reg_info { + struct regulator_desc desc; + struct hi6421_spmi_pmic *pmic; + u8 eco_mode_mask; + u32 eco_uA; + + /* Serialize regulator enable logic */ + struct mutex enable_mutex; +}; + +static const unsigned int ldo3_voltages[] = { + 1500000, 1550000, 1600000, 1650000, + 1700000, 1725000, 1750000, 1775000, + 1800000, 1825000, 1850000, 1875000, + 1900000, 1925000, 1950000, 2000000 +}; + +static const unsigned int ldo4_voltages[] = { + 1725000, 1750000, 1775000, 1800000, + 1825000, 1850000, 1875000, 1900000 +}; + +static const unsigned int ldo9_voltages[] = { + 1750000, 1800000, 1825000, 2800000, + 2850000, 2950000, 3000000, 3300000 +}; + +static const unsigned int ldo15_voltages[] = { + 1800000, 1850000, 2400000, 2600000, + 2700000, 2850000, 2950000, 3000000 +}; + +static const unsigned int ldo17_voltages[] = { + 2500000, 2600000, 2700000, 2800000, + 3000000, 3100000, 3200000, 3300000 +}; + +static const unsigned int ldo34_voltages[] = { + 2600000, 2700000, 2800000, 2900000, + 3000000, 3100000, 3200000, 3300000 +}; + +/** + * HI6421V600_LDO() - specify a LDO power line + * @_id: LDO id name string + * @vtable: voltage table + * @ereg: enable register + * @emask: enable mask + * @vreg: voltage select register + * @odelay: off/on delay time in uS + * @etime: enable time in uS + * @ecomask: eco mode mask + * @ecoamp: eco mode load uppler limit in uA + */ +#define HI6421V600_LDO(_id, vtable, ereg, emask, vreg, \ + odelay, etime, ecomask, ecoamp) \ + [HI6421V600_##_id] = { \ + .desc = { \ + .name = #_id, \ + .of_match = of_match_ptr(#_id), \ + .regulators_node = of_match_ptr("regulators"), \ + .ops = &hi6421_spmi_ldo_rops, \ + .type = REGULATOR_VOLTAGE, \ + .id = HI6421V600_##_id, \ + .owner = THIS_MODULE, \ + .volt_table = vtable, \ + .n_voltages = ARRAY_SIZE(vtable), \ + .vsel_mask = ARRAY_SIZE(vtable) - 1, \ + .vsel_reg = vreg, \ + .enable_reg = ereg, \ + .enable_mask = emask, \ + .enable_time = etime, \ + .ramp_delay = etime, \ + .off_on_delay = odelay, \ + }, \ + .eco_mode_mask = ecomask, \ + .eco_uA = ecoamp, \ + } + +static int hi6421_spmi_regulator_enable(struct regulator_dev *rdev) +{ + struct hi6421_spmi_reg_info *sreg = rdev_get_drvdata(rdev); + struct hi6421_spmi_pmic *pmic = sreg->pmic; + int ret; + + /* cannot enable more than one regulator at one time */ + mutex_lock(&sreg->enable_mutex); + + ret = regmap_update_bits(pmic->regmap, rdev->desc->enable_reg, + rdev->desc->enable_mask, + rdev->desc->enable_mask); + + /* Avoid powering up multiple devices at the same time */ + usleep_range(rdev->desc->off_on_delay, rdev->desc->off_on_delay + 60); + + mutex_unlock(&sreg->enable_mutex); + + return ret; +} + +static int hi6421_spmi_regulator_disable(struct regulator_dev *rdev) +{ + struct hi6421_spmi_reg_info *sreg = rdev_get_drvdata(rdev); + struct hi6421_spmi_pmic *pmic = sreg->pmic; + + return regmap_update_bits(pmic->regmap, rdev->desc->enable_reg, + rdev->desc->enable_mask, 0); +} + +static unsigned int hi6421_spmi_regulator_get_mode(struct regulator_dev *rdev) +{ + struct hi6421_spmi_reg_info *sreg = rdev_get_drvdata(rdev); + struct hi6421_spmi_pmic *pmic = sreg->pmic; + u32 reg_val; + + regmap_read(pmic->regmap, rdev->desc->enable_reg, ®_val); + + if (reg_val & sreg->eco_mode_mask) + return REGULATOR_MODE_IDLE; + + return REGULATOR_MODE_NORMAL; +} + +static int hi6421_spmi_regulator_set_mode(struct regulator_dev *rdev, + unsigned int mode) +{ + struct hi6421_spmi_reg_info *sreg = rdev_get_drvdata(rdev); + struct hi6421_spmi_pmic *pmic = sreg->pmic; + u32 val; + + switch (mode) { + case REGULATOR_MODE_NORMAL: + val = 0; + break; + case REGULATOR_MODE_IDLE: + val = sreg->eco_mode_mask << (ffs(sreg->eco_mode_mask) - 1); + break; + default: + return -EINVAL; + } + + return regmap_update_bits(pmic->regmap, rdev->desc->enable_reg, + sreg->eco_mode_mask, val); +} + +static unsigned int +hi6421_spmi_regulator_get_optimum_mode(struct regulator_dev *rdev, + int input_uV, int output_uV, + int load_uA) +{ + struct hi6421_spmi_reg_info *sreg = rdev_get_drvdata(rdev); + + if (!sreg->eco_uA || ((unsigned int)load_uA > sreg->eco_uA)) + return REGULATOR_MODE_NORMAL; + + return REGULATOR_MODE_IDLE; +} + +static const struct regulator_ops hi6421_spmi_ldo_rops = { + .is_enabled = regulator_is_enabled_regmap, + .enable = hi6421_spmi_regulator_enable, + .disable = hi6421_spmi_regulator_disable, + .list_voltage = regulator_list_voltage_table, + .map_voltage = regulator_map_voltage_iterate, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_mode = hi6421_spmi_regulator_get_mode, + .set_mode = hi6421_spmi_regulator_set_mode, + .get_optimum_mode = hi6421_spmi_regulator_get_optimum_mode, +}; + +/* HI6421v600 regulators with known registers */ +enum hi6421_spmi_regulator_id { + HI6421V600_LDO3, + HI6421V600_LDO4, + HI6421V600_LDO9, + HI6421V600_LDO15, + HI6421V600_LDO16, + HI6421V600_LDO17, + HI6421V600_LDO33, + HI6421V600_LDO34, +}; + +static struct hi6421_spmi_reg_info regulator_info[] = { + HI6421V600_LDO(LDO3, ldo3_voltages, + 0x16, 0x01, 0x51, + 20000, 120, + 0, 0), + HI6421V600_LDO(LDO4, ldo4_voltages, + 0x17, 0x01, 0x52, + 20000, 120, + 0x10, 10000), + HI6421V600_LDO(LDO9, ldo9_voltages, + 0x1c, 0x01, 0x57, + 20000, 360, + 0x10, 10000), + HI6421V600_LDO(LDO15, ldo15_voltages, + 0x21, 0x01, 0x5c, + 20000, 360, + 0x10, 10000), + HI6421V600_LDO(LDO16, ldo15_voltages, + 0x22, 0x01, 0x5d, + 20000, 360, + 0x10, 10000), + HI6421V600_LDO(LDO17, ldo17_voltages, + 0x23, 0x01, 0x5e, + 20000, 120, + 0x10, 10000), + HI6421V600_LDO(LDO33, ldo17_voltages, + 0x32, 0x01, 0x6d, + 20000, 120, + 0, 0), + HI6421V600_LDO(LDO34, ldo34_voltages, + 0x33, 0x01, 0x6e, + 20000, 120, + 0, 0), +}; + +static int hi6421_spmi_regulator_probe(struct platform_device *pdev) +{ + struct device *pmic_dev = pdev->dev.parent; + struct regulator_config config = { }; + struct hi6421_spmi_reg_info *sreg; + struct hi6421_spmi_reg_info *info; + struct device *dev = &pdev->dev; + struct hi6421_spmi_pmic *pmic; + struct regulator_dev *rdev; + int i; + + /* + * This driver is meant to be called by hi6421-spmi-core, + * which should first set drvdata. If this doesn't happen, hit + * a warn on and return. + */ + pmic = dev_get_drvdata(pmic_dev); + if (WARN_ON(!pmic)) + return -ENODEV; + + sreg = devm_kzalloc(dev, sizeof(*sreg), GFP_KERNEL); + if (!sreg) + return -ENOMEM; + + sreg->pmic = pmic; + mutex_init(&sreg->enable_mutex); + + for (i = 0; i < ARRAY_SIZE(regulator_info); i++) { + info = ®ulator_info[i]; + + config.dev = pdev->dev.parent; + config.driver_data = sreg; + config.regmap = pmic->regmap; + + rdev = devm_regulator_register(dev, &info->desc, &config); + if (IS_ERR(rdev)) { + dev_err(dev, "failed to register %s\n", + info->desc.name); + return PTR_ERR(rdev); + } + } + + return 0; +} + +static const struct platform_device_id hi6421_spmi_regulator_table[] = { + { .name = "hi6421v600-regulator" }, + {}, +}; +MODULE_DEVICE_TABLE(platform, hi6421_spmi_regulator_table); + +static struct platform_driver hi6421_spmi_regulator_driver = { + .id_table = hi6421_spmi_regulator_table, + .driver = { + .name = "hi6421v600-regulator", + }, + .probe = hi6421_spmi_regulator_probe, +}; +module_platform_driver(hi6421_spmi_regulator_driver); + +MODULE_DESCRIPTION("Hi6421v600 SPMI regulator driver"); +MODULE_LICENSE("GPL v2"); + diff --git a/drivers/regulator/qcom_spmi-regulator.c b/drivers/regulator/qcom_spmi-regulator.c index 00e1d8e9637e..95677c51c1fa 100644 --- a/drivers/regulator/qcom_spmi-regulator.c +++ b/drivers/regulator/qcom_spmi-regulator.c @@ -5,6 +5,7 @@ #include <linux/module.h> #include <linux/delay.h> +#include <linux/devm-helpers.h> #include <linux/err.h> #include <linux/kernel.h> #include <linux/interrupt.h> @@ -1844,7 +1845,10 @@ static int spmi_regulator_of_parse(struct device_node *node, return ret; } - INIT_DELAYED_WORK(&vreg->ocp_work, spmi_regulator_vs_ocp_work); + ret = devm_delayed_work_autocancel(dev, &vreg->ocp_work, + spmi_regulator_vs_ocp_work); + if (ret) + return ret; } return 0; @@ -2159,10 +2163,8 @@ static int qcom_spmi_regulator_probe(struct platform_device *pdev) vreg->regmap = regmap; if (reg->ocp) { vreg->ocp_irq = platform_get_irq_byname(pdev, reg->ocp); - if (vreg->ocp_irq < 0) { - ret = vreg->ocp_irq; - goto err; - } + if (vreg->ocp_irq < 0) + return vreg->ocp_irq; } vreg->desc.id = -1; vreg->desc.owner = THIS_MODULE; @@ -2205,8 +2207,7 @@ static int qcom_spmi_regulator_probe(struct platform_device *pdev) rdev = devm_regulator_register(dev, &vreg->desc, &config); if (IS_ERR(rdev)) { dev_err(dev, "failed to register %s\n", name); - ret = PTR_ERR(rdev); - goto err; + return PTR_ERR(rdev); } INIT_LIST_HEAD(&vreg->node); @@ -2214,24 +2215,6 @@ static int qcom_spmi_regulator_probe(struct platform_device *pdev) } return 0; - -err: - list_for_each_entry(vreg, vreg_list, node) - if (vreg->ocp_irq) - cancel_delayed_work_sync(&vreg->ocp_work); - return ret; -} - -static int qcom_spmi_regulator_remove(struct platform_device *pdev) -{ - struct spmi_regulator *vreg; - struct list_head *vreg_list = platform_get_drvdata(pdev); - - list_for_each_entry(vreg, vreg_list, node) - if (vreg->ocp_irq) - cancel_delayed_work_sync(&vreg->ocp_work); - - return 0; } static struct platform_driver qcom_spmi_regulator_driver = { @@ -2240,7 +2223,6 @@ static struct platform_driver qcom_spmi_regulator_driver = { .of_match_table = qcom_spmi_regulator_match, }, .probe = qcom_spmi_regulator_probe, - .remove = qcom_spmi_regulator_remove, }; module_platform_driver(qcom_spmi_regulator_driver); diff --git a/drivers/regulator/rohm-regulator.c b/drivers/regulator/rohm-regulator.c index 5c558b153d55..6e0d9c08ec1c 100644 --- a/drivers/regulator/rohm-regulator.c +++ b/drivers/regulator/rohm-regulator.c @@ -22,13 +22,26 @@ static int set_dvs_level(const struct regulator_desc *desc, return ret; return 0; } - + /* If voltage is set to 0 => disable */ if (uv == 0) { if (omask) return regmap_update_bits(regmap, oreg, omask, 0); } + /* Some setups don't allow setting own voltage but do allow enabling */ + if (!mask) { + if (omask) + return regmap_update_bits(regmap, oreg, omask, omask); + + return -EINVAL; + } for (i = 0; i < desc->n_voltages; i++) { - ret = regulator_desc_list_voltage_linear_range(desc, i); + /* NOTE to next hacker - Does not support pickable ranges */ + if (desc->linear_range_selectors) + return -EINVAL; + if (desc->n_linear_ranges) + ret = regulator_desc_list_voltage_linear_range(desc, i); + else + ret = regulator_desc_list_voltage_linear(desc, i); if (ret < 0) continue; if (ret == uv) { @@ -82,6 +95,12 @@ int rohm_regulator_set_dvs_levels(const struct rohm_dvs_config *dvs, mask = dvs->lpsr_mask; omask = dvs->lpsr_on_mask; break; + case ROHM_DVS_LEVEL_SNVS: + prop = "rohm,dvs-snvs-voltage"; + reg = dvs->snvs_reg; + mask = dvs->snvs_mask; + omask = dvs->snvs_on_mask; + break; default: return -EINVAL; } diff --git a/drivers/regulator/scmi-regulator.c b/drivers/regulator/scmi-regulator.c index 51eb97815c00..1f02f60ad136 100644 --- a/drivers/regulator/scmi-regulator.c +++ b/drivers/regulator/scmi-regulator.c @@ -2,7 +2,7 @@ // // System Control and Management Interface (SCMI) based regulator driver // -// Copyright (C) 2020 ARM Ltd. +// Copyright (C) 2020-2021 ARM Ltd. // // Implements a regulator driver on top of the SCMI Voltage Protocol. // @@ -33,9 +33,12 @@ #include <linux/slab.h> #include <linux/types.h> +static const struct scmi_voltage_proto_ops *voltage_ops; + struct scmi_regulator { u32 id; struct scmi_device *sdev; + struct scmi_protocol_handle *ph; struct regulator_dev *rdev; struct device_node *of_node; struct regulator_desc desc; @@ -50,19 +53,17 @@ struct scmi_regulator_info { static int scmi_reg_enable(struct regulator_dev *rdev) { struct scmi_regulator *sreg = rdev_get_drvdata(rdev); - const struct scmi_handle *handle = sreg->sdev->handle; - return handle->voltage_ops->config_set(handle, sreg->id, - SCMI_VOLTAGE_ARCH_STATE_ON); + return voltage_ops->config_set(sreg->ph, sreg->id, + SCMI_VOLTAGE_ARCH_STATE_ON); } static int scmi_reg_disable(struct regulator_dev *rdev) { struct scmi_regulator *sreg = rdev_get_drvdata(rdev); - const struct scmi_handle *handle = sreg->sdev->handle; - return handle->voltage_ops->config_set(handle, sreg->id, - SCMI_VOLTAGE_ARCH_STATE_OFF); + return voltage_ops->config_set(sreg->ph, sreg->id, + SCMI_VOLTAGE_ARCH_STATE_OFF); } static int scmi_reg_is_enabled(struct regulator_dev *rdev) @@ -70,10 +71,8 @@ static int scmi_reg_is_enabled(struct regulator_dev *rdev) int ret; u32 config; struct scmi_regulator *sreg = rdev_get_drvdata(rdev); - const struct scmi_handle *handle = sreg->sdev->handle; - ret = handle->voltage_ops->config_get(handle, sreg->id, - &config); + ret = voltage_ops->config_get(sreg->ph, sreg->id, &config); if (ret) { dev_err(&sreg->sdev->dev, "Error %d reading regulator %s status.\n", @@ -89,9 +88,8 @@ static int scmi_reg_get_voltage_sel(struct regulator_dev *rdev) int ret; s32 volt_uV; struct scmi_regulator *sreg = rdev_get_drvdata(rdev); - const struct scmi_handle *handle = sreg->sdev->handle; - ret = handle->voltage_ops->level_get(handle, sreg->id, &volt_uV); + ret = voltage_ops->level_get(sreg->ph, sreg->id, &volt_uV); if (ret) return ret; @@ -103,13 +101,12 @@ static int scmi_reg_set_voltage_sel(struct regulator_dev *rdev, { s32 volt_uV; struct scmi_regulator *sreg = rdev_get_drvdata(rdev); - const struct scmi_handle *handle = sreg->sdev->handle; volt_uV = sreg->desc.ops->list_voltage(rdev, selector); if (volt_uV <= 0) return -EINVAL; - return handle->voltage_ops->level_set(handle, sreg->id, 0x0, volt_uV); + return voltage_ops->level_set(sreg->ph, sreg->id, 0x0, volt_uV); } static const struct regulator_ops scmi_reg_fixed_ops = { @@ -204,11 +201,10 @@ scmi_config_discrete_regulator_mappings(struct scmi_regulator *sreg, static int scmi_regulator_common_init(struct scmi_regulator *sreg) { int ret; - const struct scmi_handle *handle = sreg->sdev->handle; struct device *dev = &sreg->sdev->dev; const struct scmi_voltage_info *vinfo; - vinfo = handle->voltage_ops->info_get(handle, sreg->id); + vinfo = voltage_ops->info_get(sreg->ph, sreg->id); if (!vinfo) { dev_warn(dev, "Failure to get voltage domain %d\n", sreg->id); @@ -257,6 +253,7 @@ static int scmi_regulator_common_init(struct scmi_regulator *sreg) } static int process_scmi_regulator_of_node(struct scmi_device *sdev, + struct scmi_protocol_handle *ph, struct device_node *np, struct scmi_regulator_info *rinfo) { @@ -284,6 +281,7 @@ static int process_scmi_regulator_of_node(struct scmi_device *sdev, rinfo->sregv[dom]->id = dom; rinfo->sregv[dom]->sdev = sdev; + rinfo->sregv[dom]->ph = ph; /* get hold of good nodes */ of_node_get(np); @@ -302,11 +300,17 @@ static int scmi_regulator_probe(struct scmi_device *sdev) struct device_node *np, *child; const struct scmi_handle *handle = sdev->handle; struct scmi_regulator_info *rinfo; + struct scmi_protocol_handle *ph; - if (!handle || !handle->voltage_ops) + if (!handle) return -ENODEV; - num_doms = handle->voltage_ops->num_domains_get(handle); + voltage_ops = handle->devm_protocol_get(sdev, + SCMI_PROTOCOL_VOLTAGE, &ph); + if (IS_ERR(voltage_ops)) + return PTR_ERR(voltage_ops); + + num_doms = voltage_ops->num_domains_get(ph); if (num_doms <= 0) { if (!num_doms) { dev_err(&sdev->dev, @@ -341,7 +345,7 @@ static int scmi_regulator_probe(struct scmi_device *sdev) */ np = of_find_node_by_name(handle->dev->of_node, "regulators"); for_each_child_of_node(np, child) { - ret = process_scmi_regulator_of_node(sdev, child, rinfo); + ret = process_scmi_regulator_of_node(sdev, ph, child, rinfo); /* abort on any mem issue */ if (ret == -ENOMEM) { of_node_put(child); |