From 4d5270e70981c278307e6c25666c300653ed5f4c Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sun, 20 Dec 2020 20:06:10 -0800 Subject: [PATCH 01/49] lib/linear_ranges: fix repeated words & one typo Change "which which" to "for which" in 3 places. Change "ranges" to possessive "range's" in 1 place. Signed-off-by: Randy Dunlap Cc: Andrew Morton Cc: Mark Brown Cc: Matti Vaittinen Reviewed-by: Matti Vaittinen Link: https://lore.kernel.org/r/20201221040610.12809-1-rdunlap@infradead.org Signed-off-by: Mark Brown --- lib/linear_ranges.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/linear_ranges.c b/lib/linear_ranges.c index 9495ef3572b7..ced5c15d3f04 100644 --- a/lib/linear_ranges.c +++ b/lib/linear_ranges.c @@ -128,7 +128,7 @@ EXPORT_SYMBOL_GPL(linear_range_get_value_array); * @selector: address where found selector value is updated * @found: flag to indicate that given value was in the range * - * Return selector which which range value is closest match for given + * Return selector for which range value is closest match for given * input value. Value is matching if it is equal or smaller than given * value. If given value is in the range, then @found is set true. * @@ -168,11 +168,11 @@ EXPORT_SYMBOL_GPL(linear_range_get_selector_low); * @selector: address where found selector value is updated * @found: flag to indicate that given value was in the range * - * Scan array of ranges for selector which which range value matches given + * Scan array of ranges for selector for which range value matches given * input value. Value is matching if it is equal or smaller than given * value. If given value is found to be in a range scanning is stopped and * @found is set true. If a range with values smaller than given value is found - * but the range max is being smaller than given value, then the ranges + * but the range max is being smaller than given value, then the range's * biggest selector is updated to @selector but scanning ranges is continued * and @found is set to false. * @@ -209,7 +209,7 @@ EXPORT_SYMBOL_GPL(linear_range_get_selector_low_array); * @selector: address where found selector value is updated * @found: flag to indicate that given value was in the range * - * Return selector which which range value is closest match for given + * Return selector for which range value is closest match for given * input value. Value is matching if it is equal or higher than given * value. If given value is in the range, then @found is set true. * From ac6b559d818981406e587f4784777e9112d553f4 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Fri, 25 Dec 2020 10:50:04 -0800 Subject: [PATCH 02/49] regulator: qcom-rpmh: fix build after QCOM_COMMAND_DB is tristate Restrict REGULATOR_QCOM_RPMH to QCOM_COMMAND_DB it the latter is enabled. Fixes this build error: microblaze-linux-ld: drivers/regulator/qcom-rpmh-regulator.o: in function `rpmh_regulator_probe': (.text+0x354): undefined reference to `cmd_db_read_addr' Fixes: 778279f4f5e4 ("soc: qcom: cmd-db: allow loading as a module") Reported-by: kernel test robot Signed-off-by: Randy Dunlap Cc: Lina Iyer Cc: Liam Girdwood Cc: Mark Brown Reviewed-by: Lina Iyer Link: https://lore.kernel.org/r/20201225185004.20747-1-rdunlap@infradead.org Signed-off-by: Mark Brown --- drivers/regulator/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 53fa84f4d1e1..e1ca550379de 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -881,6 +881,7 @@ config REGULATOR_QCOM_RPM config REGULATOR_QCOM_RPMH tristate "Qualcomm Technologies, Inc. RPMh regulator driver" depends on QCOM_RPMH || (QCOM_RPMH=n && COMPILE_TEST) + depends on QCOM_COMMAND_DB || !QCOM_COMMAND_DB help This driver supports control of PMIC regulators via the RPMh hardware block found on Qualcomm Technologies Inc. SoCs. RPMh regulator From 934b05e818620e922151734b2d0e070e388e3c53 Mon Sep 17 00:00:00 2001 From: ChiYuan Huang Date: Thu, 17 Dec 2020 23:00:41 +0800 Subject: [PATCH 03/49] regulator: rt4831: Adds DT binding document for Richtek RT4831 DSV regulator Adds DT binding document for Richtek RT4831 DSV regulator. Signed-off-by: ChiYuan Huang Link: https://lore.kernel.org/r/1608217244-314-3-git-send-email-u0084500@gmail.com Signed-off-by: Mark Brown --- .../regulator/richtek,rt4831-regulator.yaml | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 Documentation/devicetree/bindings/regulator/richtek,rt4831-regulator.yaml diff --git a/Documentation/devicetree/bindings/regulator/richtek,rt4831-regulator.yaml b/Documentation/devicetree/bindings/regulator/richtek,rt4831-regulator.yaml new file mode 100644 index 000000000000..d9c23333e157 --- /dev/null +++ b/Documentation/devicetree/bindings/regulator/richtek,rt4831-regulator.yaml @@ -0,0 +1,35 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/regulator/richtek,rt4831-regulator.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Richtek RT4831 Display Bias Voltage Regulator + +maintainers: + - ChiYuan Huang + +description: | + RT4831 is a multifunctional device that can provide power to the LCD display + and LCD backlight. + + For Display Bias Voltage DSVP and DSVN, the output range is about 4V to 6.5V. + It is sufficient to meet the current LCD power requirement. + + DSVLCM is a boost regulator in IC internal as DSVP and DSVN input power. + Its voltage should be configured above 0.15V to 0.2V gap larger than the + voltage needed for DSVP and DSVN. Too much voltage gap could improve the + voltage drop from the heavy loading scenario. But it also make the power + efficiency worse. It's a trade-off. + + Datasheet is available at + https://www.richtek.com/assets/product_file/RT4831A/DS4831A-05.pdf + +patternProperties: + "^DSV(LCM|P|N)$": + type: object + $ref: regulator.yaml# + description: + Properties for single Display Bias Voltage regulator. + +additionalProperties: false From 9351ab8b0cb61ffbef30343d28d1855e329c98fb Mon Sep 17 00:00:00 2001 From: ChiYuan Huang Date: Thu, 17 Dec 2020 23:00:44 +0800 Subject: [PATCH 04/49] regulator: rt4831: Adds support for Richtek RT4831 DSV regulator Adds support for Richtek RT4831 DSV Regulator Signed-off-by: ChiYuan Huang Link: https://lore.kernel.org/r/1608217244-314-6-git-send-email-u0084500@gmail.com Signed-off-by: Mark Brown --- drivers/regulator/Kconfig | 10 ++ drivers/regulator/Makefile | 1 + drivers/regulator/rt4831-regulator.c | 198 +++++++++++++++++++++++++++ 3 files changed, 209 insertions(+) create mode 100644 drivers/regulator/rt4831-regulator.c diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index e1ca550379de..beecdbf572c9 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -969,6 +969,16 @@ config REGULATOR_RT4801 This adds support for voltage regulators in Richtek RT4801 Display Bias IC. The device supports two regulators (DSVP/DSVN). +config REGULATOR_RT4831 + tristate "Richtek RT4831 DSV Regulators" + depends on MFD_RT4831 + help + This adds support for voltage regulators in Richtek RT4831. + There are three regulators (VLCM/DSVP/DSVN). + VLCM is a virtual voltage input for DSVP/DSVN inside IC. + And DSVP/DSVN is the real Vout range from 4V to 6.5V. + It's common used to provide the power for the display panel. + config REGULATOR_RT5033 tristate "Richtek RT5033 Regulators" depends on MFD_RT5033 diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 680e539f6579..f8363ea0cb4c 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -118,6 +118,7 @@ obj-$(CONFIG_REGULATOR_RK808) += rk808-regulator.o obj-$(CONFIG_REGULATOR_RN5T618) += rn5t618-regulator.o obj-$(CONFIG_REGULATOR_ROHM) += rohm-regulator.o obj-$(CONFIG_REGULATOR_RT4801) += rt4801-regulator.o +obj-$(CONFIG_REGULATOR_RT4831) += rt4831-regulator.o obj-$(CONFIG_REGULATOR_RT5033) += rt5033-regulator.o obj-$(CONFIG_REGULATOR_RTMV20) += rtmv20-regulator.o obj-$(CONFIG_REGULATOR_S2MPA01) += s2mpa01.o diff --git a/drivers/regulator/rt4831-regulator.c b/drivers/regulator/rt4831-regulator.c new file mode 100644 index 000000000000..3d4695ded629 --- /dev/null +++ b/drivers/regulator/rt4831-regulator.c @@ -0,0 +1,198 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include +#include +#include +#include +#include +#include + +enum { + DSV_OUT_VLCM = 0, + DSV_OUT_VPOS, + DSV_OUT_VNEG, + DSV_OUT_MAX +}; + +#define RT4831_REG_DSVEN 0x09 +#define RT4831_REG_VLCM 0x0c +#define RT4831_REG_VPOS 0x0d +#define RT4831_REG_VNEG 0x0e +#define RT4831_REG_FLAGS 0x0f + +#define RT4831_VOLT_MASK GENMASK(5, 0) +#define RT4831_DSVMODE_SHIFT 5 +#define RT4831_DSVMODE_MASK GENMASK(7, 5) +#define RT4831_POSADEN_MASK BIT(4) +#define RT4831_NEGADEN_MASK BIT(3) +#define RT4831_POSEN_MASK BIT(2) +#define RT4831_NEGEN_MASK BIT(1) + +#define RT4831_OTP_MASK BIT(6) +#define RT4831_LCMOVP_MASK BIT(5) +#define RT4831_VPOSSCP_MASK BIT(3) +#define RT4831_VNEGSCP_MASK BIT(2) + +#define DSV_MODE_NORMAL (0x4 << RT4831_DSVMODE_SHIFT) +#define DSV_MODE_BYPASS (0x6 << RT4831_DSVMODE_SHIFT) +#define STEP_UV 50000 +#define VLCM_MIN_UV 4000000 +#define VLCM_MAX_UV 7150000 +#define VLCM_N_VOLTAGES ((VLCM_MAX_UV - VLCM_MIN_UV) / STEP_UV + 1) +#define VPN_MIN_UV 4000000 +#define VPN_MAX_UV 6500000 +#define VPN_N_VOLTAGES ((VPN_MAX_UV - VPN_MIN_UV) / STEP_UV + 1) + +static int rt4831_get_error_flags(struct regulator_dev *rdev, unsigned int *flags) +{ + struct regmap *regmap = rdev_get_regmap(rdev); + int rid = rdev_get_id(rdev); + unsigned int val, events = 0; + int ret; + + ret = regmap_read(regmap, RT4831_REG_FLAGS, &val); + if (ret) + return ret; + + if (val & RT4831_OTP_MASK) + events |= REGULATOR_ERROR_OVER_TEMP; + + if (rid == DSV_OUT_VLCM && (val & RT4831_LCMOVP_MASK)) + events |= REGULATOR_ERROR_OVER_CURRENT; + + if (rid == DSV_OUT_VPOS && (val & RT4831_VPOSSCP_MASK)) + events |= REGULATOR_ERROR_OVER_CURRENT; + + if (rid == DSV_OUT_VNEG && (val & RT4831_VNEGSCP_MASK)) + events |= REGULATOR_ERROR_OVER_CURRENT; + + *flags = events; + return 0; +} + +static const struct regulator_ops rt4831_dsvlcm_ops = { + .list_voltage = regulator_list_voltage_linear, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_bypass = regulator_set_bypass_regmap, + .get_bypass = regulator_get_bypass_regmap, + .get_error_flags = rt4831_get_error_flags, +}; + +static const struct regulator_ops rt4831_dsvpn_ops = { + .list_voltage = regulator_list_voltage_linear, + .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, + .set_active_discharge = regulator_set_active_discharge_regmap, + .get_error_flags = rt4831_get_error_flags, +}; + +static const struct regulator_desc rt4831_regulator_descs[] = { + { + .name = "DSVLCM", + .ops = &rt4831_dsvlcm_ops, + .of_match = of_match_ptr("DSVLCM"), + .regulators_node = of_match_ptr("regulators"), + .type = REGULATOR_VOLTAGE, + .id = DSV_OUT_VLCM, + .n_voltages = VLCM_N_VOLTAGES, + .min_uV = VLCM_MIN_UV, + .uV_step = STEP_UV, + .vsel_reg = RT4831_REG_VLCM, + .vsel_mask = RT4831_VOLT_MASK, + .bypass_reg = RT4831_REG_DSVEN, + .bypass_val_on = DSV_MODE_BYPASS, + .bypass_val_off = DSV_MODE_NORMAL, + }, + { + .name = "DSVP", + .ops = &rt4831_dsvpn_ops, + .of_match = of_match_ptr("DSVP"), + .regulators_node = of_match_ptr("regulators"), + .type = REGULATOR_VOLTAGE, + .id = DSV_OUT_VPOS, + .n_voltages = VPN_N_VOLTAGES, + .min_uV = VPN_MIN_UV, + .uV_step = STEP_UV, + .vsel_reg = RT4831_REG_VPOS, + .vsel_mask = RT4831_VOLT_MASK, + .enable_reg = RT4831_REG_DSVEN, + .enable_mask = RT4831_POSEN_MASK, + .active_discharge_reg = RT4831_REG_DSVEN, + .active_discharge_mask = RT4831_POSADEN_MASK, + }, + { + .name = "DSVN", + .ops = &rt4831_dsvpn_ops, + .of_match = of_match_ptr("DSVN"), + .regulators_node = of_match_ptr("regulators"), + .type = REGULATOR_VOLTAGE, + .id = DSV_OUT_VNEG, + .n_voltages = VPN_N_VOLTAGES, + .min_uV = VPN_MIN_UV, + .uV_step = STEP_UV, + .vsel_reg = RT4831_REG_VNEG, + .vsel_mask = RT4831_VOLT_MASK, + .enable_reg = RT4831_REG_DSVEN, + .enable_mask = RT4831_NEGEN_MASK, + .active_discharge_reg = RT4831_REG_DSVEN, + .active_discharge_mask = RT4831_NEGADEN_MASK, + } +}; + +static int rt4831_regulator_probe(struct platform_device *pdev) +{ + struct regmap *regmap; + struct regulator_dev *rdev; + struct regulator_config config = {}; + int i, ret; + + regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (IS_ERR(regmap)) { + dev_err(&pdev->dev, "Failed to init regmap\n"); + return PTR_ERR(regmap); + } + + /* Configure DSV mode to normal by default */ + ret = regmap_update_bits(regmap, RT4831_REG_DSVEN, RT4831_DSVMODE_MASK, DSV_MODE_NORMAL); + if (ret) { + dev_err(&pdev->dev, "Failed to configure dsv mode to normal\n"); + return ret; + } + + config.dev = pdev->dev.parent; + config.regmap = regmap; + + for (i = 0; i < DSV_OUT_MAX; i++) { + rdev = devm_regulator_register(&pdev->dev, rt4831_regulator_descs + i, &config); + if (IS_ERR(rdev)) { + dev_err(&pdev->dev, "Failed to register %d regulator\n", i); + return PTR_ERR(rdev); + } + } + + return 0; +} + +static const struct platform_device_id rt4831_regulator_match[] = { + { "rt4831-regulator", 0 }, + {} +}; +MODULE_DEVICE_TABLE(platform, rt4831_regulator_match); + +static struct platform_driver rt4831_regulator_driver = { + .driver = { + .name = "rt4831-regulator", + }, + .id_table = rt4831_regulator_match, + .probe = rt4831_regulator_probe, +}; +module_platform_driver(rt4831_regulator_driver); + +MODULE_AUTHOR("ChiYuan Huang "); +MODULE_LICENSE("GPL v2"); From 3b15ccac161aa61511561fa2a76cd9677563d22f Mon Sep 17 00:00:00 2001 From: Cristian Ciocaltea Date: Tue, 29 Dec 2020 19:31:19 +0200 Subject: [PATCH 05/49] regulator: Add regulator driver for ATC260x PMICs Add support for the DC-DC converters and LDO regulators found in the ATC2603C and ATC2609A chip variants of the Actions Semi ATC260x family of PMICs. Co-developed-by: Manivannan Sadhasivam Signed-off-by: Manivannan Sadhasivam Signed-off-by: Cristian Ciocaltea Link: https://lore.kernel.org/r/1117b1a01b3948446cb3cc407e52de3a5d4212b0.1609258905.git.cristian.ciocaltea@gmail.com Signed-off-by: Mark Brown --- drivers/regulator/Kconfig | 8 + drivers/regulator/Makefile | 1 + drivers/regulator/atc260x-regulator.c | 539 ++++++++++++++++++++++++++ 3 files changed, 548 insertions(+) create mode 100644 drivers/regulator/atc260x-regulator.c diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index beecdbf572c9..a7fbb9a87413 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -179,6 +179,14 @@ config REGULATOR_AS3722 AS3722 PMIC. This will enable support for all the software controllable DCDC/LDO regulators. +config REGULATOR_ATC260X + tristate "Actions Semi ATC260x PMIC Regulators" + depends on MFD_ATC260X + help + This driver provides support for the voltage regulators on the + ATC260x PMICs. This will enable support for all the software + controllable DCDC/LDO regulators. + config REGULATOR_AXP20X tristate "X-POWERS AXP20X PMIC Regulators" depends on MFD_AXP20X diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index f8363ea0cb4c..dec23fa5c5f2 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_REGULATOR_ARIZONA_MICSUPP) += arizona-micsupp.o obj-$(CONFIG_REGULATOR_ARM_SCMI) += scmi-regulator.o obj-$(CONFIG_REGULATOR_AS3711) += as3711-regulator.o obj-$(CONFIG_REGULATOR_AS3722) += as3722-regulator.o +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 diff --git a/drivers/regulator/atc260x-regulator.c b/drivers/regulator/atc260x-regulator.c new file mode 100644 index 000000000000..d8b429955d33 --- /dev/null +++ b/drivers/regulator/atc260x-regulator.c @@ -0,0 +1,539 @@ +// SPDX-License-Identifier: GPL-2.0+ +// +// Regulator driver for ATC260x PMICs +// +// Copyright (C) 2019 Manivannan Sadhasivam +// Copyright (C) 2020 Cristian Ciocaltea + +#include +#include +#include +#include +#include + +struct atc260x_regulator_data { + int voltage_time_dcdc; + int voltage_time_ldo; +}; + +static const struct linear_range atc2603c_dcdc_voltage_ranges[] = { + REGULATOR_LINEAR_RANGE(1300000, 0, 13, 50000), + REGULATOR_LINEAR_RANGE(1950000, 14, 15, 100000), +}; + +static const struct linear_range atc2609a_dcdc_voltage_ranges[] = { + REGULATOR_LINEAR_RANGE(600000, 0, 127, 6250), + REGULATOR_LINEAR_RANGE(1400000, 128, 232, 25000), +}; + +static const struct linear_range atc2609a_ldo_voltage_ranges0[] = { + REGULATOR_LINEAR_RANGE(700000, 0, 15, 100000), + REGULATOR_LINEAR_RANGE(2100000, 16, 28, 100000), +}; + +static const struct linear_range atc2609a_ldo_voltage_ranges1[] = { + REGULATOR_LINEAR_RANGE(850000, 0, 15, 100000), + REGULATOR_LINEAR_RANGE(2100000, 16, 27, 100000), +}; + +static const unsigned int atc260x_ldo_voltage_range_sel[] = { + 0x0, 0x1, +}; + +static int atc260x_dcdc_set_voltage_time_sel(struct regulator_dev *rdev, + unsigned int old_selector, + unsigned int new_selector) +{ + struct atc260x_regulator_data *data = rdev_get_drvdata(rdev); + + if (new_selector > old_selector) + return data->voltage_time_dcdc; + + return 0; +} + +static int atc260x_ldo_set_voltage_time_sel(struct regulator_dev *rdev, + unsigned int old_selector, + unsigned int new_selector) +{ + struct atc260x_regulator_data *data = rdev_get_drvdata(rdev); + + if (new_selector > old_selector) + return data->voltage_time_ldo; + + return 0; +} + +static const struct regulator_ops atc260x_dcdc_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 = atc260x_dcdc_set_voltage_time_sel, +}; + +static const struct regulator_ops atc260x_ldo_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 = atc260x_ldo_set_voltage_time_sel, +}; + +static const struct regulator_ops atc260x_ldo_bypass_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 = atc260x_ldo_set_voltage_time_sel, + .set_bypass = regulator_set_bypass_regmap, + .get_bypass = regulator_get_bypass_regmap, +}; + +static const struct regulator_ops atc260x_ldo_bypass_discharge_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 = atc260x_ldo_set_voltage_time_sel, + .set_bypass = regulator_set_bypass_regmap, + .get_bypass = regulator_get_bypass_regmap, + .set_active_discharge = regulator_set_active_discharge_regmap, +}; + +static const struct regulator_ops atc260x_dcdc_range_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .list_voltage = regulator_list_voltage_linear_range, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_time_sel = atc260x_dcdc_set_voltage_time_sel, +}; + +static const struct regulator_ops atc260x_ldo_range_pick_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .list_voltage = regulator_list_voltage_pickable_linear_range, + .set_voltage_sel = regulator_set_voltage_sel_pickable_regmap, + .get_voltage_sel = regulator_get_voltage_sel_pickable_regmap, + .set_voltage_time_sel = atc260x_ldo_set_voltage_time_sel, +}; + +static const struct regulator_ops atc260x_dcdc_fixed_ops = { + .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 = atc260x_dcdc_set_voltage_time_sel, +}; + +static const struct regulator_ops atc260x_ldo_fixed_ops = { + .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 = atc260x_ldo_set_voltage_time_sel, +}; + +static const struct regulator_ops atc260x_no_ops = { +}; + +/* + * Note LDO8 is not documented in datasheet (v2.4), but supported + * in the vendor's driver implementation (xapp-le-kernel). + */ +enum atc2603c_reg_ids { + ATC2603C_ID_DCDC1, + ATC2603C_ID_DCDC2, + ATC2603C_ID_DCDC3, + ATC2603C_ID_LDO1, + ATC2603C_ID_LDO2, + ATC2603C_ID_LDO3, + ATC2603C_ID_LDO5, + ATC2603C_ID_LDO6, + ATC2603C_ID_LDO7, + ATC2603C_ID_LDO8, + ATC2603C_ID_LDO11, + ATC2603C_ID_LDO12, + ATC2603C_ID_SWITCHLDO1, + ATC2603C_ID_MAX, +}; + +#define atc2603c_reg_desc_dcdc(num, min, step, n_volt, vsel_h, vsel_l) { \ + .name = "DCDC"#num, \ + .supply_name = "dcdc"#num, \ + .of_match = of_match_ptr("dcdc"#num), \ + .regulators_node = of_match_ptr("regulators"), \ + .id = ATC2603C_ID_DCDC##num, \ + .ops = &atc260x_dcdc_ops, \ + .type = REGULATOR_VOLTAGE, \ + .min_uV = min, \ + .uV_step = step, \ + .n_voltages = n_volt, \ + .vsel_reg = ATC2603C_PMU_DC##num##_CTL0, \ + .vsel_mask = GENMASK(vsel_h, vsel_l), \ + .enable_reg = ATC2603C_PMU_DC##num##_CTL0, \ + .enable_mask = BIT(15), \ + .enable_time = 800, \ + .owner = THIS_MODULE, \ +} + +#define atc2603c_reg_desc_dcdc_range(num, vsel_h, vsel_l) { \ + .name = "DCDC"#num, \ + .supply_name = "dcdc"#num, \ + .of_match = of_match_ptr("dcdc"#num), \ + .regulators_node = of_match_ptr("regulators"), \ + .id = ATC2603C_ID_DCDC##num, \ + .ops = &atc260x_dcdc_range_ops, \ + .type = REGULATOR_VOLTAGE, \ + .n_voltages = 16, \ + .linear_ranges = atc2603c_dcdc_voltage_ranges, \ + .n_linear_ranges = ARRAY_SIZE(atc2603c_dcdc_voltage_ranges), \ + .vsel_reg = ATC2603C_PMU_DC##num##_CTL0, \ + .vsel_mask = GENMASK(vsel_h, vsel_l), \ + .enable_reg = ATC2603C_PMU_DC##num##_CTL0, \ + .enable_mask = BIT(15), \ + .enable_time = 800, \ + .owner = THIS_MODULE, \ +} + +#define atc2603c_reg_desc_dcdc_fixed(num, min, step, n_volt, vsel_h, vsel_l) { \ + .name = "DCDC"#num, \ + .supply_name = "dcdc"#num, \ + .of_match = of_match_ptr("dcdc"#num), \ + .regulators_node = of_match_ptr("regulators"), \ + .id = ATC2603C_ID_DCDC##num, \ + .ops = &atc260x_dcdc_fixed_ops, \ + .type = REGULATOR_VOLTAGE, \ + .min_uV = min, \ + .uV_step = step, \ + .n_voltages = n_volt, \ + .vsel_reg = ATC2603C_PMU_DC##num##_CTL0, \ + .vsel_mask = GENMASK(vsel_h, vsel_l), \ + .enable_time = 800, \ + .owner = THIS_MODULE, \ +} + +#define atc2603c_reg_desc_ldo(num, min, step, n_volt, vsel_h, vsel_l) { \ + .name = "LDO"#num, \ + .supply_name = "ldo"#num, \ + .of_match = of_match_ptr("ldo"#num), \ + .regulators_node = of_match_ptr("regulators"), \ + .id = ATC2603C_ID_LDO##num, \ + .ops = &atc260x_ldo_ops, \ + .type = REGULATOR_VOLTAGE, \ + .min_uV = min, \ + .uV_step = step, \ + .n_voltages = n_volt, \ + .vsel_reg = ATC2603C_PMU_LDO##num##_CTL, \ + .vsel_mask = GENMASK(vsel_h, vsel_l), \ + .enable_reg = ATC2603C_PMU_LDO##num##_CTL, \ + .enable_mask = BIT(0), \ + .enable_time = 2000, \ + .owner = THIS_MODULE, \ +} + +#define atc2603c_reg_desc_ldo_fixed(num, min, step, n_volt, vsel_h, vsel_l) { \ + .name = "LDO"#num, \ + .supply_name = "ldo"#num, \ + .of_match = of_match_ptr("ldo"#num), \ + .regulators_node = of_match_ptr("regulators"), \ + .id = ATC2603C_ID_LDO##num, \ + .ops = &atc260x_ldo_fixed_ops, \ + .type = REGULATOR_VOLTAGE, \ + .min_uV = min, \ + .uV_step = step, \ + .n_voltages = n_volt, \ + .vsel_reg = ATC2603C_PMU_LDO##num##_CTL, \ + .vsel_mask = GENMASK(vsel_h, vsel_l), \ + .enable_time = 2000, \ + .owner = THIS_MODULE, \ +} + +#define atc2603c_reg_desc_ldo_noops(num, vfixed) { \ + .name = "LDO"#num, \ + .supply_name = "ldo"#num, \ + .of_match = of_match_ptr("ldo"#num), \ + .regulators_node = of_match_ptr("regulators"), \ + .id = ATC2603C_ID_LDO##num, \ + .ops = &atc260x_no_ops, \ + .type = REGULATOR_VOLTAGE, \ + .fixed_uV = vfixed, \ + .n_voltages = 1, \ + .owner = THIS_MODULE, \ +} + +#define atc2603c_reg_desc_ldo_switch(num, min, step, n_volt, vsel_h, vsel_l) { \ + .name = "SWITCHLDO"#num, \ + .supply_name = "switchldo"#num, \ + .of_match = of_match_ptr("switchldo"#num), \ + .regulators_node = of_match_ptr("regulators"), \ + .id = ATC2603C_ID_SWITCHLDO##num, \ + .ops = &atc260x_ldo_bypass_discharge_ops, \ + .type = REGULATOR_VOLTAGE, \ + .min_uV = min, \ + .uV_step = step, \ + .n_voltages = n_volt, \ + .vsel_reg = ATC2603C_PMU_SWITCH_CTL, \ + .vsel_mask = GENMASK(vsel_h, vsel_l), \ + .enable_reg = ATC2603C_PMU_SWITCH_CTL, \ + .enable_mask = BIT(15), \ + .enable_is_inverted = true, \ + .enable_time = 2000, \ + .bypass_reg = ATC2603C_PMU_SWITCH_CTL, \ + .bypass_mask = BIT(5), \ + .active_discharge_reg = ATC2603C_PMU_SWITCH_CTL, \ + .active_discharge_mask = BIT(1), \ + .owner = THIS_MODULE, \ +} + +static const struct regulator_desc atc2603c_reg[] = { + atc2603c_reg_desc_dcdc_fixed(1, 700000, 25000, 29, 11, 7), + atc2603c_reg_desc_dcdc_range(2, 12, 8), + atc2603c_reg_desc_dcdc_fixed(3, 2600000, 100000, 8, 11, 9), + atc2603c_reg_desc_ldo_fixed(1, 2600000, 100000, 8, 15, 13), + atc2603c_reg_desc_ldo_fixed(2, 2600000, 100000, 8, 15, 13), + atc2603c_reg_desc_ldo_fixed(3, 1500000, 100000, 6, 15, 13), + atc2603c_reg_desc_ldo(5, 2600000, 100000, 8, 15, 13), + atc2603c_reg_desc_ldo_fixed(6, 700000, 25000, 29, 15, 11), + atc2603c_reg_desc_ldo(7, 1500000, 100000, 6, 15, 13), + atc2603c_reg_desc_ldo(8, 2300000, 100000, 11, 15, 12), + atc2603c_reg_desc_ldo_fixed(11, 2600000, 100000, 8, 15, 13), + atc2603c_reg_desc_ldo_noops(12, 1800000), + atc2603c_reg_desc_ldo_switch(1, 3000000, 100000, 4, 4, 3), +}; + +static const struct regulator_desc atc2603c_reg_dcdc2_ver_b = + atc2603c_reg_desc_dcdc(2, 1000000, 50000, 18, 12, 8); + +enum atc2609a_reg_ids { + ATC2609A_ID_DCDC0, + ATC2609A_ID_DCDC1, + ATC2609A_ID_DCDC2, + ATC2609A_ID_DCDC3, + ATC2609A_ID_DCDC4, + ATC2609A_ID_LDO0, + ATC2609A_ID_LDO1, + ATC2609A_ID_LDO2, + ATC2609A_ID_LDO3, + ATC2609A_ID_LDO4, + ATC2609A_ID_LDO5, + ATC2609A_ID_LDO6, + ATC2609A_ID_LDO7, + ATC2609A_ID_LDO8, + ATC2609A_ID_LDO9, + ATC2609A_ID_MAX, +}; + +#define atc2609a_reg_desc_dcdc(num, en_bit) { \ + .name = "DCDC"#num, \ + .supply_name = "dcdc"#num, \ + .of_match = of_match_ptr("dcdc"#num), \ + .regulators_node = of_match_ptr("regulators"), \ + .id = ATC2609A_ID_DCDC##num, \ + .ops = &atc260x_dcdc_ops, \ + .type = REGULATOR_VOLTAGE, \ + .min_uV = 600000, \ + .uV_step = 6250, \ + .n_voltages = 256, \ + .vsel_reg = ATC2609A_PMU_DC##num##_CTL0, \ + .vsel_mask = GENMASK(15, 8), \ + .enable_reg = ATC2609A_PMU_DC_OSC, \ + .enable_mask = BIT(en_bit), \ + .enable_time = 800, \ + .owner = THIS_MODULE, \ +} + +#define atc2609a_reg_desc_dcdc_range(num, en_bit) { \ + .name = "DCDC"#num, \ + .supply_name = "dcdc"#num, \ + .of_match = of_match_ptr("dcdc"#num), \ + .regulators_node = of_match_ptr("regulators"), \ + .id = ATC2609A_ID_DCDC##num, \ + .ops = &atc260x_dcdc_range_ops, \ + .type = REGULATOR_VOLTAGE, \ + .n_voltages = 233, \ + .linear_ranges = atc2609a_dcdc_voltage_ranges, \ + .n_linear_ranges = ARRAY_SIZE(atc2609a_dcdc_voltage_ranges), \ + .vsel_reg = ATC2609A_PMU_DC##num##_CTL0, \ + .vsel_mask = GENMASK(15, 8), \ + .enable_reg = ATC2609A_PMU_DC_OSC, \ + .enable_mask = BIT(en_bit), \ + .enable_time = 800, \ + .owner = THIS_MODULE, \ +} + +#define atc2609a_reg_desc_ldo(num) { \ + .name = "LDO"#num, \ + .supply_name = "ldo"#num, \ + .of_match = of_match_ptr("ldo"#num), \ + .regulators_node = of_match_ptr("regulators"), \ + .id = ATC2609A_ID_LDO##num, \ + .ops = &atc260x_ldo_ops, \ + .type = REGULATOR_VOLTAGE, \ + .min_uV = 700000, \ + .uV_step = 100000, \ + .n_voltages = 16, \ + .vsel_reg = ATC2609A_PMU_LDO##num##_CTL0, \ + .vsel_mask = GENMASK(4, 1), \ + .enable_reg = ATC2609A_PMU_LDO##num##_CTL0, \ + .enable_mask = BIT(0), \ + .enable_time = 2000, \ + .owner = THIS_MODULE, \ +} + +#define atc2609a_reg_desc_ldo_bypass(num) { \ + .name = "LDO"#num, \ + .supply_name = "ldo"#num, \ + .of_match = of_match_ptr("ldo"#num), \ + .regulators_node = of_match_ptr("regulators"), \ + .id = ATC2609A_ID_LDO##num, \ + .ops = &atc260x_ldo_bypass_ops, \ + .type = REGULATOR_VOLTAGE, \ + .min_uV = 2300000, \ + .uV_step = 100000, \ + .n_voltages = 12, \ + .vsel_reg = ATC2609A_PMU_LDO##num##_CTL0, \ + .vsel_mask = GENMASK(5, 2), \ + .enable_reg = ATC2609A_PMU_LDO##num##_CTL0, \ + .enable_mask = BIT(0), \ + .enable_time = 2000, \ + .bypass_reg = ATC2609A_PMU_LDO##num##_CTL0, \ + .bypass_mask = BIT(1), \ + .owner = THIS_MODULE, \ +} + +#define atc2609a_reg_desc_ldo_range_pick(num, n_range) { \ + .name = "LDO"#num, \ + .supply_name = "ldo"#num, \ + .of_match = of_match_ptr("ldo"#num), \ + .regulators_node = of_match_ptr("regulators"), \ + .id = ATC2609A_ID_LDO##num, \ + .ops = &atc260x_ldo_range_pick_ops, \ + .type = REGULATOR_VOLTAGE, \ + .linear_ranges = atc2609a_ldo_voltage_ranges##n_range, \ + .n_linear_ranges = ARRAY_SIZE(atc2609a_ldo_voltage_ranges##n_range), \ + .vsel_reg = ATC2609A_PMU_LDO##num##_CTL0, \ + .vsel_mask = GENMASK(4, 1), \ + .vsel_range_reg = ATC2609A_PMU_LDO##num##_CTL0, \ + .vsel_range_mask = BIT(5), \ + .linear_range_selectors = atc260x_ldo_voltage_range_sel, \ + .enable_reg = ATC2609A_PMU_LDO##num##_CTL0, \ + .enable_mask = BIT(0), \ + .enable_time = 2000, \ + .owner = THIS_MODULE, \ +} + +#define atc2609a_reg_desc_ldo_fixed(num) { \ + .name = "LDO"#num, \ + .supply_name = "ldo"#num, \ + .of_match = of_match_ptr("ldo"#num), \ + .regulators_node = of_match_ptr("regulators"), \ + .id = ATC2609A_ID_LDO##num, \ + .ops = &atc260x_ldo_fixed_ops, \ + .type = REGULATOR_VOLTAGE, \ + .min_uV = 2600000, \ + .uV_step = 100000, \ + .n_voltages = 8, \ + .vsel_reg = ATC2609A_PMU_LDO##num##_CTL, \ + .vsel_mask = GENMASK(15, 13), \ + .enable_time = 2000, \ + .owner = THIS_MODULE, \ +} + +static const struct regulator_desc atc2609a_reg[] = { + atc2609a_reg_desc_dcdc(0, 4), + atc2609a_reg_desc_dcdc(1, 5), + atc2609a_reg_desc_dcdc(2, 6), + atc2609a_reg_desc_dcdc_range(3, 7), + atc2609a_reg_desc_dcdc(4, 8), + atc2609a_reg_desc_ldo_bypass(0), + atc2609a_reg_desc_ldo_bypass(1), + atc2609a_reg_desc_ldo_bypass(2), + atc2609a_reg_desc_ldo_range_pick(3, 0), + atc2609a_reg_desc_ldo_range_pick(4, 0), + atc2609a_reg_desc_ldo(5), + atc2609a_reg_desc_ldo_range_pick(6, 1), + atc2609a_reg_desc_ldo_range_pick(7, 0), + atc2609a_reg_desc_ldo_range_pick(8, 0), + atc2609a_reg_desc_ldo_fixed(9), +}; + +static int atc260x_regulator_probe(struct platform_device *pdev) +{ + struct atc260x *atc260x = dev_get_drvdata(pdev->dev.parent); + struct device *dev = atc260x->dev; + struct atc260x_regulator_data *atc260x_data; + struct regulator_config config = {}; + struct regulator_dev *atc260x_rdev; + const struct regulator_desc *regulators; + bool atc2603c_ver_b = false; + int i, nregulators; + + atc260x_data = devm_kzalloc(&pdev->dev, sizeof(*atc260x_data), GFP_KERNEL); + if (!atc260x_data) + return -ENOMEM; + + atc260x_data->voltage_time_dcdc = 350; + atc260x_data->voltage_time_ldo = 800; + + switch (atc260x->ic_type) { + case ATC2603C: + regulators = atc2603c_reg; + nregulators = ATC2603C_ID_MAX; + atc2603c_ver_b = atc260x->ic_ver == ATC260X_B; + break; + case ATC2609A: + atc260x_data->voltage_time_dcdc = 250; + regulators = atc2609a_reg; + nregulators = ATC2609A_ID_MAX; + break; + default: + dev_err(dev, "unsupported ATC260X ID %d\n", atc260x->ic_type); + return -EINVAL; + } + + config.dev = dev; + config.regmap = atc260x->regmap; + config.driver_data = atc260x_data; + + /* Instantiate the regulators */ + for (i = 0; i < nregulators; i++) { + if (atc2603c_ver_b && regulators[i].id == ATC2603C_ID_DCDC2) + atc260x_rdev = devm_regulator_register(&pdev->dev, + &atc2603c_reg_dcdc2_ver_b, + &config); + else + atc260x_rdev = devm_regulator_register(&pdev->dev, + ®ulators[i], + &config); + if (IS_ERR(atc260x_rdev)) { + dev_err(dev, "failed to register regulator: %d\n", i); + return PTR_ERR(atc260x_rdev); + } + } + + return 0; +} + +static struct platform_driver atc260x_regulator_driver = { + .probe = atc260x_regulator_probe, + .driver = { + .name = "atc260x-regulator", + }, +}; + +module_platform_driver(atc260x_regulator_driver); + +MODULE_DESCRIPTION("Regulator driver for ATC260x PMICs"); +MODULE_AUTHOR("Manivannan Sadhasivam "); +MODULE_AUTHOR("Cristian Ciocaltea "); +MODULE_LICENSE("GPL"); From 951384cabc5dfb09251d440dbc26058eba86f97e Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Thu, 31 Dec 2020 15:23:47 +0300 Subject: [PATCH 06/49] regulator: qcom-rpmh-regulator: add pm8009-1 chip revision PM8009 has special revision (P=1), which is to be used for sm8250 platform. The major difference is the S2 regulator which supplies 0.95 V instead of 2.848V. Declare regulators data to be used for this chip revision. The datasheet calls the chip just pm8009-1, so use the same name. Signed-off-by: Dmitry Baryshkov Fixes: 06369bcc15a1 ("regulator: qcom-rpmh: Add support for SM8150") Reviewed-by: Vinod Koul Link: https://lore.kernel.org/r/20201231122348.637917-4-dmitry.baryshkov@linaro.org Signed-off-by: Mark Brown --- drivers/regulator/qcom-rpmh-regulator.c | 26 +++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/drivers/regulator/qcom-rpmh-regulator.c b/drivers/regulator/qcom-rpmh-regulator.c index c395a8dda6f7..98320e1d8bf6 100644 --- a/drivers/regulator/qcom-rpmh-regulator.c +++ b/drivers/regulator/qcom-rpmh-regulator.c @@ -732,6 +732,15 @@ static const struct rpmh_vreg_hw_data pmic5_hfsmps515 = { .of_map_mode = rpmh_regulator_pmic4_smps_of_map_mode, }; +static const struct rpmh_vreg_hw_data pmic5_hfsmps515_1 = { + .regulator_type = VRM, + .ops = &rpmh_regulator_vrm_ops, + .voltage_range = REGULATOR_LINEAR_RANGE(900000, 0, 4, 16000), + .n_voltages = 5, + .pmic_mode_map = pmic_mode_map_pmic5_smps, + .of_map_mode = rpmh_regulator_pmic4_smps_of_map_mode, +}; + static const struct rpmh_vreg_hw_data pmic5_bob = { .regulator_type = VRM, .ops = &rpmh_regulator_vrm_bypass_ops, @@ -932,6 +941,19 @@ static const struct rpmh_vreg_init_data pm8009_vreg_data[] = { {}, }; +static const struct rpmh_vreg_init_data pm8009_1_vreg_data[] = { + RPMH_VREG("smps1", "smp%s1", &pmic5_hfsmps510, "vdd-s1"), + RPMH_VREG("smps2", "smp%s2", &pmic5_hfsmps515_1, "vdd-s2"), + RPMH_VREG("ldo1", "ldo%s1", &pmic5_nldo, "vdd-l1"), + RPMH_VREG("ldo2", "ldo%s2", &pmic5_nldo, "vdd-l2"), + RPMH_VREG("ldo3", "ldo%s3", &pmic5_nldo, "vdd-l3"), + RPMH_VREG("ldo4", "ldo%s4", &pmic5_nldo, "vdd-l4"), + RPMH_VREG("ldo5", "ldo%s5", &pmic5_pldo, "vdd-l5-l6"), + RPMH_VREG("ldo6", "ldo%s6", &pmic5_pldo, "vdd-l5-l6"), + RPMH_VREG("ldo7", "ldo%s6", &pmic5_pldo_lv, "vdd-l7"), + {}, +}; + static const struct rpmh_vreg_init_data pm6150_vreg_data[] = { RPMH_VREG("smps1", "smp%s1", &pmic5_ftsmps510, "vdd-s1"), RPMH_VREG("smps2", "smp%s2", &pmic5_ftsmps510, "vdd-s2"), @@ -1057,6 +1079,10 @@ static const struct of_device_id __maybe_unused rpmh_regulator_match_table[] = { .compatible = "qcom,pm8009-rpmh-regulators", .data = pm8009_vreg_data, }, + { + .compatible = "qcom,pm8009-1-rpmh-regulators", + .data = pm8009_1_vreg_data, + }, { .compatible = "qcom,pm8150-rpmh-regulators", .data = pm8150_vreg_data, From c3da02421230639bf6ee5462b70b58f5b7f3b7c6 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Thu, 31 Dec 2020 15:23:48 +0300 Subject: [PATCH 07/49] arm64: dts: qcom: qrb5165-rb5: fix pm8009 regulators Fix pm8009 compatibility string to reference pm8009 revision specific to sm8250 platform. Also add S2 regulator to be used for qca639x. Signed-off-by: Dmitry Baryshkov Fixes: b1d2674e6121 ("arm64: dts: qcom: Add basic devicetree support for QRB5165 RB5") Reviewed-by: Vinod Koul Link: https://lore.kernel.org/r/20201231122348.637917-5-dmitry.baryshkov@linaro.org Signed-off-by: Mark Brown --- arch/arm64/boot/dts/qcom/qrb5165-rb5.dts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/qcom/qrb5165-rb5.dts b/arch/arm64/boot/dts/qcom/qrb5165-rb5.dts index ce22d4fa383e..7917754c99d6 100644 --- a/arch/arm64/boot/dts/qcom/qrb5165-rb5.dts +++ b/arch/arm64/boot/dts/qcom/qrb5165-rb5.dts @@ -122,7 +122,7 @@ &apps_rsc { pm8009-rpmh-regulators { - compatible = "qcom,pm8009-rpmh-regulators"; + compatible = "qcom,pm8009-1-rpmh-regulators"; qcom,pmic-id = "f"; vdd-s1-supply = <&vph_pwr>; @@ -131,6 +131,13 @@ vdd-l5-l6-supply = <&vreg_bob>; vdd-l7-supply = <&vreg_s4a_1p8>; + vreg_s2f_0p95: smps2 { + regulator-name = "vreg_s2f_0p95"; + regulator-min-microvolt = <900000>; + regulator-max-microvolt = <952000>; + regulator-initial-mode = ; + }; + vreg_l1f_1p1: ldo1 { regulator-name = "vreg_l1f_1p1"; regulator-min-microvolt = <1104000>; From 90cf443d841e41af7fceb26aed6553e43670fe42 Mon Sep 17 00:00:00 2001 From: Daniel Scally Date: Sun, 3 Jan 2021 16:55:41 +0000 Subject: [PATCH 08/49] regulator: core.c: Replace references to non-existent function The function regulator_set_device_supply() is referenced a few times in comments in regulator/core.c; however this function was removed a long time ago by commit a5766f11cfd3 ("regulator: core - Rework machine API to remove string based functions."). Update those references to point to set_consumer_device_supply(), which replaced the old function. Signed-off-by: Daniel Scally Link: https://lore.kernel.org/r/20210103165541.784360-1-djrscally@gmail.com Signed-off-by: Mark Brown --- drivers/regulator/core.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index ca03d8e70bd1..fee924158091 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -2020,7 +2020,7 @@ struct regulator *_regulator_get(struct device *dev, const char *id, * Returns a struct regulator corresponding to the regulator producer, * or IS_ERR() condition containing errno. * - * Use of supply names configured via regulator_set_device_supply() is + * Use of supply names configured via set_consumer_device_supply() is * strongly encouraged. It is recommended that the supply name used * should match the name used for the supply and/or the relevant * device pins in the datasheet. @@ -2047,7 +2047,7 @@ EXPORT_SYMBOL_GPL(regulator_get); * regulator off for correct operation of the hardware they are * controlling. * - * Use of supply names configured via regulator_set_device_supply() is + * Use of supply names configured via set_consumer_device_supply() is * strongly encouraged. It is recommended that the supply name used * should match the name used for the supply and/or the relevant * device pins in the datasheet. @@ -2073,7 +2073,7 @@ EXPORT_SYMBOL_GPL(regulator_get_exclusive); * disrupting the operation of drivers that can handle absent * supplies. * - * Use of supply names configured via regulator_set_device_supply() is + * Use of supply names configured via set_consumer_device_supply() is * strongly encouraged. It is recommended that the supply name used * should match the name used for the supply and/or the relevant * device pins in the datasheet. From a0db6b0aa670ba040f959a000ef24dd4238e016b Mon Sep 17 00:00:00 2001 From: "henryc.chen" Date: Thu, 24 Dec 2020 14:08:52 +0800 Subject: [PATCH 09/49] regulator: Regulator driver for the Mediatek DVFSRC Driver for regulators exposed by the DVFSRC (dynamic voltage and frequency scaling resource collector) found in devices based on mt8183 and newer platforms. Signed-off-by: Henry Chen Link: https://lore.kernel.org/r/1608790134-27425-12-git-send-email-henryc.chen@mediatek.com Signed-off-by: Mark Brown --- drivers/regulator/Kconfig | 10 ++ drivers/regulator/Makefile | 1 + drivers/regulator/mtk-dvfsrc-regulator.c | 215 +++++++++++++++++++++++ 3 files changed, 226 insertions(+) create mode 100644 drivers/regulator/mtk-dvfsrc-regulator.c diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index b1d2103bfe57..eafa13be6e3e 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -785,6 +785,16 @@ config REGULATOR_MT6397 This driver supports the control of different power rails of device through regulator interface. +config REGULATOR_MTK_DVFSRC + tristate "MediaTek DVFSRC regulator driver" + depends on MTK_DVFSRC + help + Say y here to control regulator by DVFSRC (dynamic voltage + and frequency scaling resource collector). + This driver supports to control regulators via the DVFSRC + of Mediatek. It allows for voting on regulator state + between multiple users. + config REGULATOR_PALMAS tristate "TI Palmas PMIC Regulators" depends on MFD_PALMAS diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index dec23fa5c5f2..bd93fbd1b124 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -95,6 +95,7 @@ obj-$(CONFIG_REGULATOR_MT6358) += mt6358-regulator.o obj-$(CONFIG_REGULATOR_MT6360) += mt6360-regulator.o obj-$(CONFIG_REGULATOR_MT6380) += mt6380-regulator.o obj-$(CONFIG_REGULATOR_MT6397) += mt6397-regulator.o +obj-$(CONFIG_REGULATOR_MTK_DVFSRC) += mtk-dvfsrc-regulator.o obj-$(CONFIG_REGULATOR_QCOM_LABIBB) += qcom-labibb-regulator.o obj-$(CONFIG_REGULATOR_QCOM_RPM) += qcom_rpm-regulator.o obj-$(CONFIG_REGULATOR_QCOM_RPMH) += qcom-rpmh-regulator.o diff --git a/drivers/regulator/mtk-dvfsrc-regulator.c b/drivers/regulator/mtk-dvfsrc-regulator.c new file mode 100644 index 000000000000..d3d876198d6e --- /dev/null +++ b/drivers/regulator/mtk-dvfsrc-regulator.c @@ -0,0 +1,215 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright (c) 2020 MediaTek Inc. + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DVFSRC_ID_VCORE 0 +#define DVFSRC_ID_VSCP 1 + +#define MT_DVFSRC_REGULAR(match, _name, _volt_table) \ +[DVFSRC_ID_##_name] = { \ + .desc = { \ + .name = match, \ + .of_match = of_match_ptr(match), \ + .ops = &dvfsrc_vcore_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = DVFSRC_ID_##_name, \ + .owner = THIS_MODULE, \ + .n_voltages = ARRAY_SIZE(_volt_table), \ + .volt_table = _volt_table, \ + }, \ +} + +/* + * DVFSRC regulators' information + * + * @desc: standard fields of regulator description. + * @voltage_selector: Selector used for get_voltage_sel() and + * set_voltage_sel() callbacks + */ + +struct dvfsrc_regulator { + struct regulator_desc desc; +}; + +/* + * MTK DVFSRC regulators' init data + * + * @size: num of regulators + * @regulator_info: regulator info. + */ +struct dvfsrc_regulator_init_data { + u32 size; + struct dvfsrc_regulator *regulator_info; +}; + +static inline struct device *to_dvfsrc_dev(struct regulator_dev *rdev) +{ + return rdev_get_dev(rdev)->parent; +} + +static int dvfsrc_set_voltage_sel(struct regulator_dev *rdev, + unsigned int selector) +{ + struct device *dvfsrc_dev = to_dvfsrc_dev(rdev); + int id = rdev_get_id(rdev); + + if (id == DVFSRC_ID_VCORE) + mtk_dvfsrc_send_request(dvfsrc_dev, + MTK_DVFSRC_CMD_VCORE_REQUEST, + selector); + else if (id == DVFSRC_ID_VSCP) + mtk_dvfsrc_send_request(dvfsrc_dev, + MTK_DVFSRC_CMD_VSCP_REQUEST, + selector); + else + return -EINVAL; + + return 0; +} + +static int dvfsrc_get_voltage_sel(struct regulator_dev *rdev) +{ + struct device *dvfsrc_dev = to_dvfsrc_dev(rdev); + int id = rdev_get_id(rdev); + int val, ret; + + if (id == DVFSRC_ID_VCORE) + ret = mtk_dvfsrc_query_info(dvfsrc_dev, + MTK_DVFSRC_CMD_VCORE_LEVEL_QUERY, + &val); + else if (id == DVFSRC_ID_VSCP) + ret = mtk_dvfsrc_query_info(dvfsrc_dev, + MTK_DVFSRC_CMD_VSCP_LEVEL_QUERY, + &val); + else + return -EINVAL; + + if (ret != 0) + return ret; + + return val; +} + +static const struct regulator_ops dvfsrc_vcore_ops = { + .list_voltage = regulator_list_voltage_table, + .get_voltage_sel = dvfsrc_get_voltage_sel, + .set_voltage_sel = dvfsrc_set_voltage_sel, +}; + +static const unsigned int mt8183_voltages[] = { + 725000, + 800000, +}; + +static struct dvfsrc_regulator mt8183_regulators[] = { + MT_DVFSRC_REGULAR("dvfsrc-vcore", VCORE, + mt8183_voltages), +}; + +static const struct dvfsrc_regulator_init_data regulator_mt8183_data = { + .size = ARRAY_SIZE(mt8183_regulators), + .regulator_info = &mt8183_regulators[0], +}; + +static const unsigned int mt6873_voltages[] = { + 575000, + 600000, + 650000, + 725000, +}; + +static struct dvfsrc_regulator mt6873_regulators[] = { + MT_DVFSRC_REGULAR("dvfsrc-vcore", VCORE, + mt6873_voltages), + MT_DVFSRC_REGULAR("dvfsrc-vscp", VSCP, + mt6873_voltages), +}; + +static const struct dvfsrc_regulator_init_data regulator_mt6873_data = { + .size = ARRAY_SIZE(mt6873_regulators), + .regulator_info = &mt6873_regulators[0], +}; + +static const struct of_device_id mtk_dvfsrc_regulator_match[] = { + { + .compatible = "mediatek,mt8183-dvfsrc", + .data = ®ulator_mt8183_data, + }, { + .compatible = "mediatek,mt8192-dvfsrc", + .data = ®ulator_mt6873_data, + }, { + .compatible = "mediatek,mt6873-dvfsrc", + .data = ®ulator_mt6873_data, + }, { + /* sentinel */ + }, +}; +MODULE_DEVICE_TABLE(of, mtk_dvfsrc_regulator_match); + +static int dvfsrc_vcore_regulator_probe(struct platform_device *pdev) +{ + const struct of_device_id *match; + struct device *dev = &pdev->dev; + struct regulator_config config = { }; + struct regulator_dev *rdev; + const struct dvfsrc_regulator_init_data *regulator_init_data; + struct dvfsrc_regulator *mt_regulators; + int i; + + match = of_match_node(mtk_dvfsrc_regulator_match, dev->parent->of_node); + + if (!match) { + dev_err(dev, "invalid compatible string\n"); + return -ENODEV; + } + + regulator_init_data = match->data; + + mt_regulators = regulator_init_data->regulator_info; + for (i = 0; i < regulator_init_data->size; i++) { + config.dev = dev->parent; + config.driver_data = (mt_regulators + i); + rdev = devm_regulator_register(dev->parent, + &(mt_regulators + i)->desc, + &config); + if (IS_ERR(rdev)) { + dev_err(dev, "failed to register %s\n", + (mt_regulators + i)->desc.name); + return PTR_ERR(rdev); + } + } + + return 0; +} + +static struct platform_driver mtk_dvfsrc_regulator_driver = { + .driver = { + .name = "mtk-dvfsrc-regulator", + }, + .probe = dvfsrc_vcore_regulator_probe, +}; + +static int __init mtk_dvfsrc_regulator_init(void) +{ + return platform_driver_register(&mtk_dvfsrc_regulator_driver); +} +subsys_initcall(mtk_dvfsrc_regulator_init); + +static void __exit mtk_dvfsrc_regulator_exit(void) +{ + platform_driver_unregister(&mtk_dvfsrc_regulator_driver); +} +module_exit(mtk_dvfsrc_regulator_exit); + +MODULE_AUTHOR("Arvin wang "); +MODULE_LICENSE("GPL v2"); From 3c14dbd4b8eea2da607b65fc9caf7aec91d6ccbf Mon Sep 17 00:00:00 2001 From: Matti Vaittinen Date: Tue, 5 Jan 2021 15:02:21 +0200 Subject: [PATCH 10/49] regulator: ROHM bd7xxxx: Do not depend on parent driver data The ROHM PMIC regulator drivers only need the regmap pointer from the parent device. Regmap can be obtained via dev_get_regmap() so do not require parent to populate driver data for that. Signed-off-by: Matti Vaittinen Link: https://lore.kernel.org/r/20210105130221.GA3438042@localhost.localdomain Signed-off-by: Mark Brown --- drivers/regulator/bd70528-regulator.c | 11 +++-------- drivers/regulator/bd71828-regulator.c | 13 ++++--------- 2 files changed, 7 insertions(+), 17 deletions(-) diff --git a/drivers/regulator/bd70528-regulator.c b/drivers/regulator/bd70528-regulator.c index d44adf7e875a..1f5f9482b209 100644 --- a/drivers/regulator/bd70528-regulator.c +++ b/drivers/regulator/bd70528-regulator.c @@ -244,19 +244,14 @@ static const struct regulator_desc bd70528_desc[] = { static int bd70528_probe(struct platform_device *pdev) { - struct rohm_regmap_dev *bd70528; int i; struct regulator_config config = { .dev = pdev->dev.parent, }; - bd70528 = dev_get_drvdata(pdev->dev.parent); - if (!bd70528) { - dev_err(&pdev->dev, "No MFD driver data\n"); - return -EINVAL; - } - - config.regmap = bd70528->regmap; + config.regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!config.regmap) + return -ENODEV; for (i = 0; i < ARRAY_SIZE(bd70528_desc); i++) { struct regulator_dev *rdev; diff --git a/drivers/regulator/bd71828-regulator.c b/drivers/regulator/bd71828-regulator.c index 85c0b9000963..6b12e963ed8f 100644 --- a/drivers/regulator/bd71828-regulator.c +++ b/drivers/regulator/bd71828-regulator.c @@ -749,19 +749,14 @@ static const struct bd71828_regulator_data bd71828_rdata[] = { static int bd71828_probe(struct platform_device *pdev) { - struct rohm_regmap_dev *bd71828; int i, j, ret; struct regulator_config config = { .dev = pdev->dev.parent, }; - bd71828 = dev_get_drvdata(pdev->dev.parent); - if (!bd71828) { - dev_err(&pdev->dev, "No MFD driver data\n"); - return -EINVAL; - } - - config.regmap = bd71828->regmap; + config.regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!config.regmap) + return -ENODEV; for (i = 0; i < ARRAY_SIZE(bd71828_rdata); i++) { struct regulator_dev *rdev; @@ -777,7 +772,7 @@ static int bd71828_probe(struct platform_device *pdev) return PTR_ERR(rdev); } for (j = 0; j < rd->reg_init_amnt; j++) { - ret = regmap_update_bits(bd71828->regmap, + ret = regmap_update_bits(config.regmap, rd->reg_inits[j].reg, rd->reg_inits[j].mask, rd->reg_inits[j].val); From 3c42728c18d093e8951ad6caf7daa89fa2f54702 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Thu, 7 Jan 2021 16:15:26 +0200 Subject: [PATCH 11/49] regulator: mcp16502: lpm pin can be optional on some platforms On some platform (e.g. SAMA7G5) LPM pin should be optional as it can be controlled explicitly (via shutdown controller registers) in the platform specific power saving code to decrease the power consumption while suspended as this SoC pin may be connected to other devices that could take power saving actions based on its value. Signed-off-by: Claudiu Beznea Link: https://lore.kernel.org/r/1610028927-9842-3-git-send-email-claudiu.beznea@microchip.com Signed-off-by: Mark Brown --- drivers/regulator/mcp16502.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/regulator/mcp16502.c b/drivers/regulator/mcp16502.c index 74ad92dc664a..88c6bd5b6c78 100644 --- a/drivers/regulator/mcp16502.c +++ b/drivers/regulator/mcp16502.c @@ -550,7 +550,7 @@ static int mcp16502_probe(struct i2c_client *client, config.regmap = rmap; config.driver_data = mcp; - mcp->lpm = devm_gpiod_get(dev, "lpm", GPIOD_OUT_LOW); + mcp->lpm = devm_gpiod_get_optional(dev, "lpm", GPIOD_OUT_LOW); if (IS_ERR(mcp->lpm)) { dev_err(dev, "failed to get lpm pin: %ld\n", PTR_ERR(mcp->lpm)); return PTR_ERR(mcp->lpm); From 8aad7fabce6ad9491cc7d23f85d9798a4a0ce399 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Thu, 7 Jan 2021 16:15:27 +0200 Subject: [PATCH 12/49] MAINTAINERS: add myself as maintainer for mcp16502 Andrei is no longer with Microchip. Add myself as maintainer for MCP16502. Along with this change the status from maintained to supported. Signed-off-by: Claudiu Beznea Link: https://lore.kernel.org/r/1610028927-9842-4-git-send-email-claudiu.beznea@microchip.com Signed-off-by: Mark Brown --- MAINTAINERS | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 546aa66428c9..3259f0e0b630 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -11690,9 +11690,9 @@ F: drivers/video/fbdev/atmel_lcdfb.c F: include/video/atmel_lcdc.h MICROCHIP MCP16502 PMIC DRIVER -M: Andrei Stefanescu +M: Claudiu Beznea L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) -S: Maintained +S: Supported F: Documentation/devicetree/bindings/regulator/mcp16502-regulator.txt F: drivers/regulator/mcp16502.c From eea0b4e213232b28a25de5b88af9e25667e8d2f2 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Thu, 7 Jan 2021 16:15:25 +0200 Subject: [PATCH 13/49] regulator: mcp16502: document lpm as optional Document LPM pin as optional. Signed-off-by: Claudiu Beznea Link: https://lore.kernel.org/r/1610028927-9842-2-git-send-email-claudiu.beznea@microchip.com Signed-off-by: Mark Brown --- .../devicetree/bindings/regulator/mcp16502-regulator.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/regulator/mcp16502-regulator.txt b/Documentation/devicetree/bindings/regulator/mcp16502-regulator.txt index d86584ed4d93..451cc4e86b01 100644 --- a/Documentation/devicetree/bindings/regulator/mcp16502-regulator.txt +++ b/Documentation/devicetree/bindings/regulator/mcp16502-regulator.txt @@ -4,7 +4,8 @@ Required properties: - compatible: "microchip,mcp16502" - reg: I2C slave address - lpm-gpios: GPIO for LPM pin. Note that this GPIO *must* remain high during - suspend-to-ram, keeping the PMIC into HIBERNATE mode. + suspend-to-ram, keeping the PMIC into HIBERNATE mode; this + property is optional; - regulators: A node that houses a sub-node for each regulator within the device. Each sub-node is identified using the node's name. The content of each sub-node is defined by the From 907dfdc945aa3d183cdc6a81b963ee3b42ece306 Mon Sep 17 00:00:00 2001 From: Matti Vaittinen Date: Thu, 7 Jan 2021 14:23:55 +0200 Subject: [PATCH 14/49] regulator: bd718x7: Stop using parent data The ROHM PMIC regulator drivers only need the regmap pointer from the parent device. Regmap can be obtained via dev_get_regmap() so do not require parent to populate driver data for that. Signed-off-by: Matti Vaittinen Link: https://lore.kernel.org/r/20210107122355.GA35080@localhost.localdomain Signed-off-by: Mark Brown --- drivers/regulator/bd718x7-regulator.c | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/drivers/regulator/bd718x7-regulator.c b/drivers/regulator/bd718x7-regulator.c index 9309765d0450..8ff47ea522d6 100644 --- a/drivers/regulator/bd718x7-regulator.c +++ b/drivers/regulator/bd718x7-regulator.c @@ -1554,7 +1554,7 @@ err_out: static int bd718xx_probe(struct platform_device *pdev) { - struct bd718xx *mfd; + struct regmap *regmap; struct regulator_config config = { 0 }; int i, j, err, omit_enable; bool use_snvs; @@ -1563,11 +1563,10 @@ static int bd718xx_probe(struct platform_device *pdev) enum rohm_chip_type chip = platform_get_device_id(pdev)->driver_data; const struct regulator_ops **swops, **hwops; - mfd = dev_get_drvdata(pdev->dev.parent); - if (!mfd) { + regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!regmap) { dev_err(&pdev->dev, "No MFD driver data\n"); - err = -EINVAL; - goto err; + return -EINVAL; } switch (chip) { @@ -1590,7 +1589,7 @@ static int bd718xx_probe(struct platform_device *pdev) } /* Register LOCK release */ - err = regmap_update_bits(mfd->chip.regmap, BD718XX_REG_REGLOCK, + err = regmap_update_bits(regmap, BD718XX_REG_REGLOCK, (REGLOCK_PWRSEQ | REGLOCK_VREG), 0); if (err) { dev_err(&pdev->dev, "Failed to unlock PMIC (%d)\n", err); @@ -1609,8 +1608,7 @@ static int bd718xx_probe(struct platform_device *pdev) * bit allowing HW defaults for power rails to be used */ if (!use_snvs) { - err = regmap_update_bits(mfd->chip.regmap, - BD718XX_REG_TRANS_COND1, + err = regmap_update_bits(regmap, BD718XX_REG_TRANS_COND1, BD718XX_ON_REQ_POWEROFF_MASK | BD718XX_SWRESET_POWEROFF_MASK | BD718XX_WDOG_POWEROFF_MASK | @@ -1626,7 +1624,7 @@ static int bd718xx_probe(struct platform_device *pdev) } config.dev = pdev->dev.parent; - config.regmap = mfd->chip.regmap; + config.regmap = regmap; /* * There are cases when we want to leave the enable-control for * the HW state machine and use this driver only for voltage control. @@ -1685,7 +1683,7 @@ static int bd718xx_probe(struct platform_device *pdev) if (!no_enable_control && (!use_snvs || !rdev->constraints->always_on || !rdev->constraints->boot_on)) { - err = regmap_update_bits(mfd->chip.regmap, r->init.reg, + err = regmap_update_bits(regmap, r->init.reg, r->init.mask, r->init.val); if (err) { dev_err(&pdev->dev, @@ -1695,7 +1693,7 @@ static int bd718xx_probe(struct platform_device *pdev) } } for (j = 0; j < r->additional_init_amnt; j++) { - err = regmap_update_bits(mfd->chip.regmap, + err = regmap_update_bits(regmap, r->additional_inits[j].reg, r->additional_inits[j].mask, r->additional_inits[j].val); From c512150b266b5d173c5ba841e9c09e4830ea4eca Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Sat, 5 Dec 2020 01:40:56 +0100 Subject: [PATCH 15/49] regulator: ab8500: Remove unused platform data The struct ab8500_regulator_platform_data was a leftover since the days before we probed all regulators from the device tree. The ab8500-ext regulator was the only used, defining platform data and register intialization that was never used for anything, a copy of a boardfile no longer in use. Delete the ab8500_regulator_platform_data and make the ab8500-ext regulator reference the regulator init data in the local file directly. We are 100% device tree these days. Cc: Lee Jones Signed-off-by: Linus Walleij Link: https://lore.kernel.org/r/20201205004057.1712753-1-linus.walleij@linaro.org Signed-off-by: Mark Brown --- drivers/regulator/ab8500-ext.c | 417 +----------------------------- include/linux/mfd/abx500/ab8500.h | 3 - include/linux/regulator/ab8500.h | 10 - 3 files changed, 2 insertions(+), 428 deletions(-) diff --git a/drivers/regulator/ab8500-ext.c b/drivers/regulator/ab8500-ext.c index 8bb43a671ded..05f9531bd108 100644 --- a/drivers/regulator/ab8500-ext.c +++ b/drivers/regulator/ab8500-ext.c @@ -24,403 +24,6 @@ #include #include -static struct regulator_consumer_supply ab8500_vaux1_consumers[] = { - /* Main display, u8500 R3 uib */ - REGULATOR_SUPPLY("vddi", "mcde_disp_sony_acx424akp.0"), - /* Main display, u8500 uib and ST uib */ - REGULATOR_SUPPLY("vdd1", "samsung_s6d16d0.0"), - /* Secondary display, ST uib */ - REGULATOR_SUPPLY("vdd1", "samsung_s6d16d0.1"), - /* SFH7741 proximity sensor */ - REGULATOR_SUPPLY("vcc", "gpio-keys.0"), - /* BH1780GLS ambient light sensor */ - REGULATOR_SUPPLY("vcc", "2-0029"), - /* lsm303dlh accelerometer */ - REGULATOR_SUPPLY("vdd", "2-0018"), - /* lsm303dlhc accelerometer */ - REGULATOR_SUPPLY("vdd", "2-0019"), - /* lsm303dlh magnetometer */ - REGULATOR_SUPPLY("vdd", "2-001e"), - /* Rohm BU21013 Touchscreen devices */ - REGULATOR_SUPPLY("avdd", "3-005c"), - REGULATOR_SUPPLY("avdd", "3-005d"), - /* Synaptics RMI4 Touchscreen device */ - REGULATOR_SUPPLY("vdd", "3-004b"), - /* L3G4200D Gyroscope device */ - REGULATOR_SUPPLY("vdd", "2-0068"), - /* Ambient light sensor device */ - REGULATOR_SUPPLY("vdd", "3-0029"), - /* Pressure sensor device */ - REGULATOR_SUPPLY("vdd", "2-005c"), - /* Cypress TrueTouch Touchscreen device */ - REGULATOR_SUPPLY("vcpin", "spi8.0"), - /* Camera device */ - REGULATOR_SUPPLY("vaux12v5", "mmio_camera"), -}; - -static struct regulator_consumer_supply ab8500_vaux2_consumers[] = { - /* On-board eMMC power */ - REGULATOR_SUPPLY("vmmc", "sdi4"), - /* AB8500 audio codec */ - REGULATOR_SUPPLY("vcc-N2158", "ab8500-codec.0"), - /* AB8500 accessory detect 1 */ - REGULATOR_SUPPLY("vcc-N2158", "ab8500-acc-det.0"), - /* AB8500 Tv-out device */ - REGULATOR_SUPPLY("vcc-N2158", "mcde_tv_ab8500.4"), - /* AV8100 HDMI device */ - REGULATOR_SUPPLY("vcc-N2158", "av8100_hdmi.3"), -}; - -static struct regulator_consumer_supply ab8500_vaux3_consumers[] = { - REGULATOR_SUPPLY("v-SD-STM", "stm"), - /* External MMC slot power */ - REGULATOR_SUPPLY("vmmc", "sdi0"), -}; - -static struct regulator_consumer_supply ab8500_vtvout_consumers[] = { - /* TV-out DENC supply */ - REGULATOR_SUPPLY("vtvout", "ab8500-denc.0"), - /* Internal general-purpose ADC */ - REGULATOR_SUPPLY("vddadc", "ab8500-gpadc.0"), - /* ADC for charger */ - REGULATOR_SUPPLY("vddadc", "ab8500-charger.0"), - /* AB8500 Tv-out device */ - REGULATOR_SUPPLY("vtvout", "mcde_tv_ab8500.4"), -}; - -static struct regulator_consumer_supply ab8500_vaud_consumers[] = { - /* AB8500 audio-codec main supply */ - REGULATOR_SUPPLY("vaud", "ab8500-codec.0"), -}; - -static struct regulator_consumer_supply ab8500_vamic1_consumers[] = { - /* AB8500 audio-codec Mic1 supply */ - REGULATOR_SUPPLY("vamic1", "ab8500-codec.0"), -}; - -static struct regulator_consumer_supply ab8500_vamic2_consumers[] = { - /* AB8500 audio-codec Mic2 supply */ - REGULATOR_SUPPLY("vamic2", "ab8500-codec.0"), -}; - -static struct regulator_consumer_supply ab8500_vdmic_consumers[] = { - /* AB8500 audio-codec DMic supply */ - REGULATOR_SUPPLY("vdmic", "ab8500-codec.0"), -}; - -static struct regulator_consumer_supply ab8500_vintcore_consumers[] = { - /* SoC core supply, no device */ - REGULATOR_SUPPLY("v-intcore", NULL), - /* USB Transceiver */ - REGULATOR_SUPPLY("vddulpivio18", "ab8500-usb.0"), - /* Handled by abx500 clk driver */ - REGULATOR_SUPPLY("v-intcore", "abx500-clk.0"), -}; - -static struct regulator_consumer_supply ab8500_vana_consumers[] = { - /* DB8500 DSI */ - REGULATOR_SUPPLY("vdddsi1v2", "mcde"), - REGULATOR_SUPPLY("vdddsi1v2", "b2r2_core"), - REGULATOR_SUPPLY("vdddsi1v2", "b2r2_1_core"), - REGULATOR_SUPPLY("vdddsi1v2", "dsilink.0"), - REGULATOR_SUPPLY("vdddsi1v2", "dsilink.1"), - REGULATOR_SUPPLY("vdddsi1v2", "dsilink.2"), - /* DB8500 CSI */ - REGULATOR_SUPPLY("vddcsi1v2", "mmio_camera"), -}; - -/* ab8500 regulator register initialization */ -static struct ab8500_regulator_reg_init ab8500_reg_init[] = { - /* - * VanaRequestCtrl = HP/LP depending on VxRequest - * VextSupply1RequestCtrl = HP/LP depending on VxRequest - */ - INIT_REGULATOR_REGISTER(AB8500_REGUREQUESTCTRL2, 0xf0, 0x00), - /* - * VextSupply2RequestCtrl = HP/LP depending on VxRequest - * VextSupply3RequestCtrl = HP/LP depending on VxRequest - * Vaux1RequestCtrl = HP/LP depending on VxRequest - * Vaux2RequestCtrl = HP/LP depending on VxRequest - */ - INIT_REGULATOR_REGISTER(AB8500_REGUREQUESTCTRL3, 0xff, 0x00), - /* - * Vaux3RequestCtrl = HP/LP depending on VxRequest - * SwHPReq = Control through SWValid disabled - */ - INIT_REGULATOR_REGISTER(AB8500_REGUREQUESTCTRL4, 0x07, 0x00), - /* - * VanaSysClkReq1HPValid = disabled - * Vaux1SysClkReq1HPValid = disabled - * Vaux2SysClkReq1HPValid = disabled - * Vaux3SysClkReq1HPValid = disabled - */ - INIT_REGULATOR_REGISTER(AB8500_REGUSYSCLKREQ1HPVALID1, 0xe8, 0x00), - /* - * VextSupply1SysClkReq1HPValid = disabled - * VextSupply2SysClkReq1HPValid = disabled - * VextSupply3SysClkReq1HPValid = SysClkReq1 controlled - */ - INIT_REGULATOR_REGISTER(AB8500_REGUSYSCLKREQ1HPVALID2, 0x70, 0x40), - /* - * VanaHwHPReq1Valid = disabled - * Vaux1HwHPreq1Valid = disabled - * Vaux2HwHPReq1Valid = disabled - * Vaux3HwHPReqValid = disabled - */ - INIT_REGULATOR_REGISTER(AB8500_REGUHWHPREQ1VALID1, 0xe8, 0x00), - /* - * VextSupply1HwHPReq1Valid = disabled - * VextSupply2HwHPReq1Valid = disabled - * VextSupply3HwHPReq1Valid = disabled - */ - INIT_REGULATOR_REGISTER(AB8500_REGUHWHPREQ1VALID2, 0x07, 0x00), - /* - * VanaHwHPReq2Valid = disabled - * Vaux1HwHPReq2Valid = disabled - * Vaux2HwHPReq2Valid = disabled - * Vaux3HwHPReq2Valid = disabled - */ - INIT_REGULATOR_REGISTER(AB8500_REGUHWHPREQ2VALID1, 0xe8, 0x00), - /* - * VextSupply1HwHPReq2Valid = disabled - * VextSupply2HwHPReq2Valid = disabled - * VextSupply3HwHPReq2Valid = HWReq2 controlled - */ - INIT_REGULATOR_REGISTER(AB8500_REGUHWHPREQ2VALID2, 0x07, 0x04), - /* - * VanaSwHPReqValid = disabled - * Vaux1SwHPReqValid = disabled - */ - INIT_REGULATOR_REGISTER(AB8500_REGUSWHPREQVALID1, 0xa0, 0x00), - /* - * Vaux2SwHPReqValid = disabled - * Vaux3SwHPReqValid = disabled - * VextSupply1SwHPReqValid = disabled - * VextSupply2SwHPReqValid = disabled - * VextSupply3SwHPReqValid = disabled - */ - INIT_REGULATOR_REGISTER(AB8500_REGUSWHPREQVALID2, 0x1f, 0x00), - /* - * SysClkReq2Valid1 = SysClkReq2 controlled - * SysClkReq3Valid1 = disabled - * SysClkReq4Valid1 = SysClkReq4 controlled - * SysClkReq5Valid1 = disabled - * SysClkReq6Valid1 = SysClkReq6 controlled - * SysClkReq7Valid1 = disabled - * SysClkReq8Valid1 = disabled - */ - INIT_REGULATOR_REGISTER(AB8500_REGUSYSCLKREQVALID1, 0xfe, 0x2a), - /* - * SysClkReq2Valid2 = disabled - * SysClkReq3Valid2 = disabled - * SysClkReq4Valid2 = disabled - * SysClkReq5Valid2 = disabled - * SysClkReq6Valid2 = SysClkReq6 controlled - * SysClkReq7Valid2 = disabled - * SysClkReq8Valid2 = disabled - */ - INIT_REGULATOR_REGISTER(AB8500_REGUSYSCLKREQVALID2, 0xfe, 0x20), - /* - * VTVoutEna = disabled - * Vintcore12Ena = disabled - * Vintcore12Sel = 1.25 V - * Vintcore12LP = inactive (HP) - * VTVoutLP = inactive (HP) - */ - INIT_REGULATOR_REGISTER(AB8500_REGUMISC1, 0xfe, 0x10), - /* - * VaudioEna = disabled - * VdmicEna = disabled - * Vamic1Ena = disabled - * Vamic2Ena = disabled - */ - INIT_REGULATOR_REGISTER(AB8500_VAUDIOSUPPLY, 0x1e, 0x00), - /* - * Vamic1_dzout = high-Z when Vamic1 is disabled - * Vamic2_dzout = high-Z when Vamic2 is disabled - */ - INIT_REGULATOR_REGISTER(AB8500_REGUCTRL1VAMIC, 0x03, 0x00), - /* - * VPll = Hw controlled (NOTE! PRCMU bits) - * VanaRegu = force off - */ - INIT_REGULATOR_REGISTER(AB8500_VPLLVANAREGU, 0x0f, 0x02), - /* - * VrefDDREna = disabled - * VrefDDRSleepMode = inactive (no pulldown) - */ - INIT_REGULATOR_REGISTER(AB8500_VREFDDR, 0x03, 0x00), - /* - * VextSupply1Regu = force LP - * VextSupply2Regu = force OFF - * VextSupply3Regu = force HP (-> STBB2=LP and TPS=LP) - * ExtSupply2Bypass = ExtSupply12LPn ball is 0 when Ena is 0 - * ExtSupply3Bypass = ExtSupply3LPn ball is 0 when Ena is 0 - */ - INIT_REGULATOR_REGISTER(AB8500_EXTSUPPLYREGU, 0xff, 0x13), - /* - * Vaux1Regu = force HP - * Vaux2Regu = force off - */ - INIT_REGULATOR_REGISTER(AB8500_VAUX12REGU, 0x0f, 0x01), - /* - * Vaux3Regu = force off - */ - INIT_REGULATOR_REGISTER(AB8500_VRF1VAUX3REGU, 0x03, 0x00), - /* - * Vaux1Sel = 2.8 V - */ - INIT_REGULATOR_REGISTER(AB8500_VAUX1SEL, 0x0f, 0x0C), - /* - * Vaux2Sel = 2.9 V - */ - INIT_REGULATOR_REGISTER(AB8500_VAUX2SEL, 0x0f, 0x0d), - /* - * Vaux3Sel = 2.91 V - */ - INIT_REGULATOR_REGISTER(AB8500_VRF1VAUX3SEL, 0x07, 0x07), - /* - * VextSupply12LP = disabled (no LP) - */ - INIT_REGULATOR_REGISTER(AB8500_REGUCTRL2SPARE, 0x01, 0x00), - /* - * Vaux1Disch = short discharge time - * Vaux2Disch = short discharge time - * Vaux3Disch = short discharge time - * Vintcore12Disch = short discharge time - * VTVoutDisch = short discharge time - * VaudioDisch = short discharge time - */ - INIT_REGULATOR_REGISTER(AB8500_REGUCTRLDISCH, 0xfc, 0x00), - /* - * VanaDisch = short discharge time - * VdmicPullDownEna = pulldown disabled when Vdmic is disabled - * VdmicDisch = short discharge time - */ - INIT_REGULATOR_REGISTER(AB8500_REGUCTRLDISCH2, 0x16, 0x00), -}; - -/* AB8500 regulators */ -static struct regulator_init_data ab8500_regulators[AB8500_NUM_REGULATORS] = { - /* supplies to the display/camera */ - [AB8500_LDO_AUX1] = { - .supply_regulator = "ab8500-ext-supply3", - .constraints = { - .name = "V-DISPLAY", - .min_uV = 2800000, - .max_uV = 3300000, - .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | - REGULATOR_CHANGE_STATUS, - .boot_on = 1, /* display is on at boot */ - }, - .num_consumer_supplies = ARRAY_SIZE(ab8500_vaux1_consumers), - .consumer_supplies = ab8500_vaux1_consumers, - }, - /* supplies to the on-board eMMC */ - [AB8500_LDO_AUX2] = { - .supply_regulator = "ab8500-ext-supply3", - .constraints = { - .name = "V-eMMC1", - .min_uV = 1100000, - .max_uV = 3300000, - .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | - REGULATOR_CHANGE_STATUS | - REGULATOR_CHANGE_MODE, - .valid_modes_mask = REGULATOR_MODE_NORMAL | - REGULATOR_MODE_IDLE, - }, - .num_consumer_supplies = ARRAY_SIZE(ab8500_vaux2_consumers), - .consumer_supplies = ab8500_vaux2_consumers, - }, - /* supply for VAUX3, supplies to SDcard slots */ - [AB8500_LDO_AUX3] = { - .supply_regulator = "ab8500-ext-supply3", - .constraints = { - .name = "V-MMC-SD", - .min_uV = 1100000, - .max_uV = 3300000, - .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | - REGULATOR_CHANGE_STATUS | - REGULATOR_CHANGE_MODE, - .valid_modes_mask = REGULATOR_MODE_NORMAL | - REGULATOR_MODE_IDLE, - }, - .num_consumer_supplies = ARRAY_SIZE(ab8500_vaux3_consumers), - .consumer_supplies = ab8500_vaux3_consumers, - }, - /* supply for tvout, gpadc, TVOUT LDO */ - [AB8500_LDO_TVOUT] = { - .constraints = { - .name = "V-TVOUT", - .valid_ops_mask = REGULATOR_CHANGE_STATUS, - }, - .num_consumer_supplies = ARRAY_SIZE(ab8500_vtvout_consumers), - .consumer_supplies = ab8500_vtvout_consumers, - }, - /* supply for ab8500-vaudio, VAUDIO LDO */ - [AB8500_LDO_AUDIO] = { - .constraints = { - .name = "V-AUD", - .valid_ops_mask = REGULATOR_CHANGE_STATUS, - }, - .num_consumer_supplies = ARRAY_SIZE(ab8500_vaud_consumers), - .consumer_supplies = ab8500_vaud_consumers, - }, - /* supply for v-anamic1 VAMic1-LDO */ - [AB8500_LDO_ANAMIC1] = { - .constraints = { - .name = "V-AMIC1", - .valid_ops_mask = REGULATOR_CHANGE_STATUS, - }, - .num_consumer_supplies = ARRAY_SIZE(ab8500_vamic1_consumers), - .consumer_supplies = ab8500_vamic1_consumers, - }, - /* supply for v-amic2, VAMIC2 LDO, reuse constants for AMIC1 */ - [AB8500_LDO_ANAMIC2] = { - .constraints = { - .name = "V-AMIC2", - .valid_ops_mask = REGULATOR_CHANGE_STATUS, - }, - .num_consumer_supplies = ARRAY_SIZE(ab8500_vamic2_consumers), - .consumer_supplies = ab8500_vamic2_consumers, - }, - /* supply for v-dmic, VDMIC LDO */ - [AB8500_LDO_DMIC] = { - .constraints = { - .name = "V-DMIC", - .valid_ops_mask = REGULATOR_CHANGE_STATUS, - }, - .num_consumer_supplies = ARRAY_SIZE(ab8500_vdmic_consumers), - .consumer_supplies = ab8500_vdmic_consumers, - }, - /* supply for v-intcore12, VINTCORE12 LDO */ - [AB8500_LDO_INTCORE] = { - .constraints = { - .name = "V-INTCORE", - .min_uV = 1250000, - .max_uV = 1350000, - .input_uV = 1800000, - .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | - REGULATOR_CHANGE_STATUS | - REGULATOR_CHANGE_MODE | - REGULATOR_CHANGE_DRMS, - .valid_modes_mask = REGULATOR_MODE_NORMAL | - REGULATOR_MODE_IDLE, - }, - .num_consumer_supplies = ARRAY_SIZE(ab8500_vintcore_consumers), - .consumer_supplies = ab8500_vintcore_consumers, - }, - /* supply for U8500 CSI-DSI, VANA LDO */ - [AB8500_LDO_ANA] = { - .constraints = { - .name = "V-CSI-DSI", - .valid_ops_mask = REGULATOR_CHANGE_STATUS, - }, - .num_consumer_supplies = ARRAY_SIZE(ab8500_vana_consumers), - .consumer_supplies = ab8500_vana_consumers, - }, -}; - /* supply for VextSupply3 */ static struct regulator_consumer_supply ab8500_ext_supply3_consumers[] = { /* SIM supply for 3 V SIM cards */ @@ -465,15 +68,6 @@ static struct regulator_init_data ab8500_ext_regulators[] = { }, }; -static struct ab8500_regulator_platform_data ab8500_regulator_plat_data = { - .reg_init = ab8500_reg_init, - .num_reg_init = ARRAY_SIZE(ab8500_reg_init), - .regulator = ab8500_regulators, - .num_regulator = ARRAY_SIZE(ab8500_regulators), - .ext_regulator = ab8500_ext_regulators, - .num_ext_regulator = ARRAY_SIZE(ab8500_ext_regulators), -}; - /** * struct ab8500_ext_regulator_info - ab8500 regulator information * @dev: device pointer @@ -788,7 +382,6 @@ static struct ab8500_ext_regulator_info static int ab8500_ext_regulator_probe(struct platform_device *pdev) { struct ab8500 *ab8500 = dev_get_drvdata(pdev->dev.parent); - struct ab8500_regulator_platform_data *pdata = &ab8500_regulator_plat_data; struct regulator_config config = { }; struct regulator_dev *rdev; int i; @@ -798,12 +391,6 @@ static int ab8500_ext_regulator_probe(struct platform_device *pdev) return -EINVAL; } - /* make sure the platform data has the correct size */ - if (pdata->num_ext_regulator != ARRAY_SIZE(ab8500_ext_regulator_info)) { - dev_err(&pdev->dev, "Configuration error: size mismatch.\n"); - return -EINVAL; - } - /* check for AB8500 2.x */ if (is_ab8500_2p0_or_earlier(ab8500)) { struct ab8500_ext_regulator_info *info; @@ -823,11 +410,11 @@ static int ab8500_ext_regulator_probe(struct platform_device *pdev) info = &ab8500_ext_regulator_info[i]; info->dev = &pdev->dev; info->cfg = (struct ab8500_ext_regulator_cfg *) - pdata->ext_regulator[i].driver_data; + ab8500_ext_regulators[i].driver_data; config.dev = &pdev->dev; config.driver_data = info; - config.init_data = &pdata->ext_regulator[i]; + config.init_data = &ab8500_ext_regulators[i]; /* register regulator with framework */ rdev = devm_regulator_register(&pdev->dev, &info->desc, diff --git a/include/linux/mfd/abx500/ab8500.h b/include/linux/mfd/abx500/ab8500.h index 524a7e4702c2..302a330c5c84 100644 --- a/include/linux/mfd/abx500/ab8500.h +++ b/include/linux/mfd/abx500/ab8500.h @@ -368,7 +368,6 @@ struct ab8500 { int it_latchhier_num; }; -struct ab8500_regulator_platform_data; struct ab8500_codec_platform_data; struct ab8500_sysctrl_platform_data; @@ -376,11 +375,9 @@ struct ab8500_sysctrl_platform_data; * struct ab8500_platform_data - AB8500 platform data * @irq_base: start of AB8500 IRQs, AB8500_NR_IRQS will be used * @init: board-specific initialization after detection of ab8500 - * @regulator: machine-specific constraints for regulators */ struct ab8500_platform_data { void (*init) (struct ab8500 *); - struct ab8500_regulator_platform_data *regulator; struct ab8500_codec_platform_data *codec; struct ab8500_sysctrl_platform_data *sysctrl; }; diff --git a/include/linux/regulator/ab8500.h b/include/linux/regulator/ab8500.h index 3ab1ddf151a2..ecf7b4713df9 100644 --- a/include/linux/regulator/ab8500.h +++ b/include/linux/regulator/ab8500.h @@ -153,14 +153,4 @@ enum ab8500_ext_regulator_id { AB8500_NUM_EXT_REGULATORS, }; -/* AB8500 regulator platform data */ -struct ab8500_regulator_platform_data { - int num_reg_init; - struct ab8500_regulator_reg_init *reg_init; - int num_regulator; - struct regulator_init_data *regulator; - int num_ext_regulator; - struct regulator_init_data *ext_regulator; -}; - #endif From 3acb64c07e95a75dc0af0bc958f2d09a44a9fd0d Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Sat, 5 Dec 2020 01:40:57 +0100 Subject: [PATCH 16/49] regulator: ab8500: Decomission platform data header The platform data header was only used to pass platform data from board files. We now populate the regulators exclusively from device tree, so the header contents can be moved into the regulator drivers. Cc: Lee Jones Signed-off-by: Linus Walleij Link: https://lore.kernel.org/r/20201205004057.1712753-2-linus.walleij@linaro.org Signed-off-by: Mark Brown --- drivers/mfd/ab8500-core.c | 1 - drivers/regulator/ab8500-ext.c | 13 ++- drivers/regulator/ab8500.c | 116 ++++++++++++++++++++++- include/linux/regulator/ab8500.h | 156 ------------------------------- 4 files changed, 127 insertions(+), 159 deletions(-) delete mode 100644 include/linux/regulator/ab8500.h diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c index a3bac9da8cbb..3b2276f04a98 100644 --- a/drivers/mfd/ab8500-core.c +++ b/drivers/mfd/ab8500-core.c @@ -21,7 +21,6 @@ #include #include #include -#include #include #include diff --git a/drivers/regulator/ab8500-ext.c b/drivers/regulator/ab8500-ext.c index 05f9531bd108..4f26952caa56 100644 --- a/drivers/regulator/ab8500-ext.c +++ b/drivers/regulator/ab8500-ext.c @@ -22,7 +22,18 @@ #include #include #include -#include + +/* AB8500 external regulators */ +enum ab8500_ext_regulator_id { + AB8500_EXT_SUPPLY1, + AB8500_EXT_SUPPLY2, + AB8500_EXT_SUPPLY3, + AB8500_NUM_EXT_REGULATORS, +}; + +struct ab8500_ext_regulator_cfg { + bool hwreq; /* requires hw mode or high power mode */ +}; /* supply for VextSupply3 */ static struct regulator_consumer_supply ab8500_ext_supply3_consumers[] = { diff --git a/drivers/regulator/ab8500.c b/drivers/regulator/ab8500.c index 47b8b6f7b571..23a401734a98 100644 --- a/drivers/regulator/ab8500.c +++ b/drivers/regulator/ab8500.c @@ -25,9 +25,123 @@ #include #include #include -#include #include +/* AB8500 regulators */ +enum ab8500_regulator_id { + AB8500_LDO_AUX1, + AB8500_LDO_AUX2, + AB8500_LDO_AUX3, + AB8500_LDO_INTCORE, + AB8500_LDO_TVOUT, + AB8500_LDO_AUDIO, + AB8500_LDO_ANAMIC1, + AB8500_LDO_ANAMIC2, + AB8500_LDO_DMIC, + AB8500_LDO_ANA, + AB8500_NUM_REGULATORS, +}; + +/* AB8505 regulators */ +enum ab8505_regulator_id { + AB8505_LDO_AUX1, + AB8505_LDO_AUX2, + AB8505_LDO_AUX3, + AB8505_LDO_AUX4, + AB8505_LDO_AUX5, + AB8505_LDO_AUX6, + AB8505_LDO_INTCORE, + AB8505_LDO_ADC, + AB8505_LDO_AUDIO, + AB8505_LDO_ANAMIC1, + AB8505_LDO_ANAMIC2, + AB8505_LDO_AUX8, + AB8505_LDO_ANA, + AB8505_NUM_REGULATORS, +}; + +/* AB8500 registers */ +enum ab8500_regulator_reg { + AB8500_REGUREQUESTCTRL2, + AB8500_REGUREQUESTCTRL3, + AB8500_REGUREQUESTCTRL4, + AB8500_REGUSYSCLKREQ1HPVALID1, + AB8500_REGUSYSCLKREQ1HPVALID2, + AB8500_REGUHWHPREQ1VALID1, + AB8500_REGUHWHPREQ1VALID2, + AB8500_REGUHWHPREQ2VALID1, + AB8500_REGUHWHPREQ2VALID2, + AB8500_REGUSWHPREQVALID1, + AB8500_REGUSWHPREQVALID2, + AB8500_REGUSYSCLKREQVALID1, + AB8500_REGUSYSCLKREQVALID2, + AB8500_REGUMISC1, + AB8500_VAUDIOSUPPLY, + AB8500_REGUCTRL1VAMIC, + AB8500_VPLLVANAREGU, + AB8500_VREFDDR, + AB8500_EXTSUPPLYREGU, + AB8500_VAUX12REGU, + AB8500_VRF1VAUX3REGU, + AB8500_VAUX1SEL, + AB8500_VAUX2SEL, + AB8500_VRF1VAUX3SEL, + AB8500_REGUCTRL2SPARE, + AB8500_REGUCTRLDISCH, + AB8500_REGUCTRLDISCH2, + AB8500_NUM_REGULATOR_REGISTERS, +}; + +/* AB8505 registers */ +enum ab8505_regulator_reg { + AB8505_REGUREQUESTCTRL1, + AB8505_REGUREQUESTCTRL2, + AB8505_REGUREQUESTCTRL3, + AB8505_REGUREQUESTCTRL4, + AB8505_REGUSYSCLKREQ1HPVALID1, + AB8505_REGUSYSCLKREQ1HPVALID2, + AB8505_REGUHWHPREQ1VALID1, + AB8505_REGUHWHPREQ1VALID2, + AB8505_REGUHWHPREQ2VALID1, + AB8505_REGUHWHPREQ2VALID2, + AB8505_REGUSWHPREQVALID1, + AB8505_REGUSWHPREQVALID2, + AB8505_REGUSYSCLKREQVALID1, + AB8505_REGUSYSCLKREQVALID2, + AB8505_REGUVAUX4REQVALID, + AB8505_REGUMISC1, + AB8505_VAUDIOSUPPLY, + AB8505_REGUCTRL1VAMIC, + AB8505_VSMPSAREGU, + AB8505_VSMPSBREGU, + AB8505_VSAFEREGU, /* NOTE! PRCMU register */ + AB8505_VPLLVANAREGU, + AB8505_EXTSUPPLYREGU, + AB8505_VAUX12REGU, + AB8505_VRF1VAUX3REGU, + AB8505_VSMPSASEL1, + AB8505_VSMPSASEL2, + AB8505_VSMPSASEL3, + AB8505_VSMPSBSEL1, + AB8505_VSMPSBSEL2, + AB8505_VSMPSBSEL3, + AB8505_VSAFESEL1, /* NOTE! PRCMU register */ + AB8505_VSAFESEL2, /* NOTE! PRCMU register */ + AB8505_VSAFESEL3, /* NOTE! PRCMU register */ + AB8505_VAUX1SEL, + AB8505_VAUX2SEL, + AB8505_VRF1VAUX3SEL, + AB8505_VAUX4REQCTRL, + AB8505_VAUX4REGU, + AB8505_VAUX4SEL, + AB8505_REGUCTRLDISCH, + AB8505_REGUCTRLDISCH2, + AB8505_REGUCTRLDISCH3, + AB8505_CTRLVAUX5, + AB8505_CTRLVAUX6, + AB8505_NUM_REGULATOR_REGISTERS, +}; + /** * struct ab8500_shared_mode - is used when mode is shared between * two regulators. diff --git a/include/linux/regulator/ab8500.h b/include/linux/regulator/ab8500.h deleted file mode 100644 index ecf7b4713df9..000000000000 --- a/include/linux/regulator/ab8500.h +++ /dev/null @@ -1,156 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (C) ST-Ericsson SA 2010 - * - * Authors: Sundar Iyer for ST-Ericsson - * Bengt Jonsson for ST-Ericsson - * Daniel Willerud for ST-Ericsson - */ - -#ifndef __LINUX_MFD_AB8500_REGULATOR_H -#define __LINUX_MFD_AB8500_REGULATOR_H - -#include - -/* AB8500 regulators */ -enum ab8500_regulator_id { - AB8500_LDO_AUX1, - AB8500_LDO_AUX2, - AB8500_LDO_AUX3, - AB8500_LDO_INTCORE, - AB8500_LDO_TVOUT, - AB8500_LDO_AUDIO, - AB8500_LDO_ANAMIC1, - AB8500_LDO_ANAMIC2, - AB8500_LDO_DMIC, - AB8500_LDO_ANA, - AB8500_NUM_REGULATORS, -}; - -/* AB8505 regulators */ -enum ab8505_regulator_id { - AB8505_LDO_AUX1, - AB8505_LDO_AUX2, - AB8505_LDO_AUX3, - AB8505_LDO_AUX4, - AB8505_LDO_AUX5, - AB8505_LDO_AUX6, - AB8505_LDO_INTCORE, - AB8505_LDO_ADC, - AB8505_LDO_AUDIO, - AB8505_LDO_ANAMIC1, - AB8505_LDO_ANAMIC2, - AB8505_LDO_AUX8, - AB8505_LDO_ANA, - AB8505_NUM_REGULATORS, -}; - -/* AB8500 and AB8505 register initialization */ -struct ab8500_regulator_reg_init { - int id; - u8 mask; - u8 value; -}; - -#define INIT_REGULATOR_REGISTER(_id, _mask, _value) \ - { \ - .id = _id, \ - .mask = _mask, \ - .value = _value, \ - } - -/* AB8500 registers */ -enum ab8500_regulator_reg { - AB8500_REGUREQUESTCTRL2, - AB8500_REGUREQUESTCTRL3, - AB8500_REGUREQUESTCTRL4, - AB8500_REGUSYSCLKREQ1HPVALID1, - AB8500_REGUSYSCLKREQ1HPVALID2, - AB8500_REGUHWHPREQ1VALID1, - AB8500_REGUHWHPREQ1VALID2, - AB8500_REGUHWHPREQ2VALID1, - AB8500_REGUHWHPREQ2VALID2, - AB8500_REGUSWHPREQVALID1, - AB8500_REGUSWHPREQVALID2, - AB8500_REGUSYSCLKREQVALID1, - AB8500_REGUSYSCLKREQVALID2, - AB8500_REGUMISC1, - AB8500_VAUDIOSUPPLY, - AB8500_REGUCTRL1VAMIC, - AB8500_VPLLVANAREGU, - AB8500_VREFDDR, - AB8500_EXTSUPPLYREGU, - AB8500_VAUX12REGU, - AB8500_VRF1VAUX3REGU, - AB8500_VAUX1SEL, - AB8500_VAUX2SEL, - AB8500_VRF1VAUX3SEL, - AB8500_REGUCTRL2SPARE, - AB8500_REGUCTRLDISCH, - AB8500_REGUCTRLDISCH2, - AB8500_NUM_REGULATOR_REGISTERS, -}; - -/* AB8505 registers */ -enum ab8505_regulator_reg { - AB8505_REGUREQUESTCTRL1, - AB8505_REGUREQUESTCTRL2, - AB8505_REGUREQUESTCTRL3, - AB8505_REGUREQUESTCTRL4, - AB8505_REGUSYSCLKREQ1HPVALID1, - AB8505_REGUSYSCLKREQ1HPVALID2, - AB8505_REGUHWHPREQ1VALID1, - AB8505_REGUHWHPREQ1VALID2, - AB8505_REGUHWHPREQ2VALID1, - AB8505_REGUHWHPREQ2VALID2, - AB8505_REGUSWHPREQVALID1, - AB8505_REGUSWHPREQVALID2, - AB8505_REGUSYSCLKREQVALID1, - AB8505_REGUSYSCLKREQVALID2, - AB8505_REGUVAUX4REQVALID, - AB8505_REGUMISC1, - AB8505_VAUDIOSUPPLY, - AB8505_REGUCTRL1VAMIC, - AB8505_VSMPSAREGU, - AB8505_VSMPSBREGU, - AB8505_VSAFEREGU, /* NOTE! PRCMU register */ - AB8505_VPLLVANAREGU, - AB8505_EXTSUPPLYREGU, - AB8505_VAUX12REGU, - AB8505_VRF1VAUX3REGU, - AB8505_VSMPSASEL1, - AB8505_VSMPSASEL2, - AB8505_VSMPSASEL3, - AB8505_VSMPSBSEL1, - AB8505_VSMPSBSEL2, - AB8505_VSMPSBSEL3, - AB8505_VSAFESEL1, /* NOTE! PRCMU register */ - AB8505_VSAFESEL2, /* NOTE! PRCMU register */ - AB8505_VSAFESEL3, /* NOTE! PRCMU register */ - AB8505_VAUX1SEL, - AB8505_VAUX2SEL, - AB8505_VRF1VAUX3SEL, - AB8505_VAUX4REQCTRL, - AB8505_VAUX4REGU, - AB8505_VAUX4SEL, - AB8505_REGUCTRLDISCH, - AB8505_REGUCTRLDISCH2, - AB8505_REGUCTRLDISCH3, - AB8505_CTRLVAUX5, - AB8505_CTRLVAUX6, - AB8505_NUM_REGULATOR_REGISTERS, -}; - -/* AB8500 external regulators */ -struct ab8500_ext_regulator_cfg { - bool hwreq; /* requires hw mode or high power mode */ -}; - -enum ab8500_ext_regulator_id { - AB8500_EXT_SUPPLY1, - AB8500_EXT_SUPPLY2, - AB8500_EXT_SUPPLY3, - AB8500_NUM_EXT_REGULATORS, -}; - -#endif From 4d23b84d1fcd1eadbc5c6cd93e76b02a8d191d66 Mon Sep 17 00:00:00 2001 From: Adrien Grassein Date: Thu, 14 Jan 2021 18:47:09 +0100 Subject: [PATCH 17/49] regulator: pf8x00: add a doc for the module pf8x00 module build was not documented. Signed-off-by: Adrien Grassein Reviewed-by: Jagan Teki Link: https://lore.kernel.org/r/20210114174714.122561-2-adrien.grassein@gmail.com Signed-off-by: Mark Brown --- drivers/regulator/Kconfig | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index eafa13be6e3e..ba8c09d90701 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -846,6 +846,10 @@ config REGULATOR_PF8X00 Say y here to support the regulators found on the NXP PF8100/PF8121A/PF8200 PMIC. + Say M here if you want to support for the regulators found + on the NXP PF8100/PF8121A/PF8200 PMIC. The module will be named + "pf8x00-regulator". + config REGULATOR_PFUZE100 tristate "Freescale PFUZE100/200/3000/3001 regulator driver" depends on I2C && OF From 988d0d42509a2c1fad0844a6e8f9c7bce7c930dd Mon Sep 17 00:00:00 2001 From: Adrien Grassein Date: Thu, 14 Jan 2021 18:47:10 +0100 Subject: [PATCH 18/49] regulator: dt-bindings: pf8x00: fix nxp,phase-shift doc nxp,phase-shift is an enum so use enum format to describe it. Minimum and maximum values are also wrong. Signed-off-by: Adrien Grassein Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20210114174714.122561-3-adrien.grassein@gmail.com Signed-off-by: Mark Brown --- .../bindings/regulator/nxp,pf8x00-regulator.yaml | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/Documentation/devicetree/bindings/regulator/nxp,pf8x00-regulator.yaml b/Documentation/devicetree/bindings/regulator/nxp,pf8x00-regulator.yaml index 956156fe52a3..095cfdae7b67 100644 --- a/Documentation/devicetree/bindings/regulator/nxp,pf8x00-regulator.yaml +++ b/Documentation/devicetree/bindings/regulator/nxp,pf8x00-regulator.yaml @@ -73,21 +73,11 @@ properties: nxp,phase-shift: $ref: "/schemas/types.yaml#/definitions/uint32" - minimum: 45 - maximum: 0 + default: 0 + enum: [ 0, 45, 90, 135, 180, 225, 270, 315 ] description: BUCK regulators phase shift control in degrees. - Listed phase shift control values in degrees are, - 45 - 90 - 135 - 180 - 225 - 270 - 315 - 0 (default) - unevaluatedProperties: false "^vsnvs$": From 34b860aa0b6221b21eea6bac76357063f525b561 Mon Sep 17 00:00:00 2001 From: Adrien Grassein Date: Thu, 14 Jan 2021 18:47:11 +0100 Subject: [PATCH 19/49] regulator: dt-bindings: pf8x00: mark nxp,ilim-ma property as deprecated This property seems useless because we can use the regulator-max-microamp generic property to do the same and using generic code. As this property was already released in a kernel version, we can't remove it, just mark it as deprecated Signed-off-by: Adrien Grassein Link: https://lore.kernel.org/r/20210114174714.122561-4-adrien.grassein@gmail.com Signed-off-by: Mark Brown --- .../devicetree/bindings/regulator/nxp,pf8x00-regulator.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/regulator/nxp,pf8x00-regulator.yaml b/Documentation/devicetree/bindings/regulator/nxp,pf8x00-regulator.yaml index 095cfdae7b67..8761437ed8ad 100644 --- a/Documentation/devicetree/bindings/regulator/nxp,pf8x00-regulator.yaml +++ b/Documentation/devicetree/bindings/regulator/nxp,pf8x00-regulator.yaml @@ -62,8 +62,11 @@ properties: $ref: "/schemas/types.yaml#/definitions/uint32" minimum: 2100 maximum: 4500 + deprecated: true description: BUCK regulators current limit in mA. + This property is deprecated, please use + "regulator-max-microamp" instead. Listed current limits in mA are, 2100 (default) From 245f5f65229a6c6f5b04fa90221b44818a928916 Mon Sep 17 00:00:00 2001 From: Adrien Grassein Date: Thu, 14 Jan 2021 18:47:12 +0100 Subject: [PATCH 20/49] regulator: pf8x00: mark nxp,ilim-ma property as deprecated This property seems useless because we can use the regulator-max-microamp generic property to do the same and using generic code. As this property was already released in a kernel version, we can't remove it, just mark it as deprecated. Signed-off-by: Adrien Grassein Link: https://lore.kernel.org/r/20210114174714.122561-5-adrien.grassein@gmail.com Signed-off-by: Mark Brown --- drivers/regulator/pf8x00-regulator.c | 55 +++++++++++++++++++++------- 1 file changed, 41 insertions(+), 14 deletions(-) diff --git a/drivers/regulator/pf8x00-regulator.c b/drivers/regulator/pf8x00-regulator.c index af9918cd27aa..0c0f643b9c60 100644 --- a/drivers/regulator/pf8x00-regulator.c +++ b/drivers/regulator/pf8x00-regulator.c @@ -128,7 +128,6 @@ enum pf8x00_devid { struct pf8x00_regulator { struct regulator_desc desc; - u8 ilim; u8 phase_shift; }; @@ -150,6 +149,11 @@ static const int pf8x00_ldo_voltages[] = { 3100000, 3150000, 3200000, 3300000, 3350000, 1650000, 1700000, 5000000, }; +/* Output: 2.1A to 4.5A */ +static const unsigned int pf8x00_sw_current_table[] = { + 2100000, 2600000, 3000000, 4500000, +}; + #define SWV(i) (6250 * i + 400000) #define SWV_LINE(i) SWV(i*8+0), SWV(i*8+1), SWV(i*8+2), SWV(i*8+3), \ SWV(i*8+4), SWV(i*8+5), SWV(i*8+6), SWV(i*8+7) @@ -199,10 +203,10 @@ static struct pf8x00_regulator *desc_to_regulator(const struct regulator_desc *d return container_of(desc, struct pf8x00_regulator, desc); } -static void swxilim_select(const struct regulator_desc *desc, int ilim) +static void swxilim_select(struct pf8x00_chip *chip, int id, int ilim) { - struct pf8x00_regulator *data = desc_to_regulator(desc); u8 ilim_sel; + u8 reg = PF8X00_SW_BASE(id) + SW_CONFIG2; switch (ilim) { case 2100: @@ -222,7 +226,32 @@ static void swxilim_select(const struct regulator_desc *desc, int ilim) break; } - data->ilim = ilim_sel; + regmap_update_bits(chip->regmap, reg, + PF8X00_SWXILIM_MASK, + ilim_sel << PF8X00_SWXILIM_SHIFT); +} + +static void handle_ilim_property(struct device_node *np, + const struct regulator_desc *desc, + struct regulator_config *config) +{ + struct pf8x00_chip *chip = config->driver_data; + int ret; + int val; + + if ((desc->id >= PF8X00_BUCK1) && (desc->id <= PF8X00_BUCK7)) { + ret = of_property_read_u32(np, "nxp,ilim-ma", &val); + if (ret) { + dev_dbg(chip->dev, "unspecified ilim for BUCK%d, use value stored in OTP\n", + desc->id - PF8X00_LDO4); + return; + } + + dev_warn(chip->dev, "nxp,ilim-ma is deprecated, please use regulator-max-microamp\n"); + swxilim_select(chip, desc->id, val); + + } else + dev_warn(chip->dev, "nxp,ilim-ma used with incorrect regulator (%d)\n", desc->id); } static int pf8x00_of_parse_cb(struct device_node *np, @@ -235,12 +264,7 @@ static int pf8x00_of_parse_cb(struct device_node *np, int val; int ret; - ret = of_property_read_u32(np, "nxp,ilim-ma", &val); - if (ret) - dev_dbg(chip->dev, "unspecified ilim for BUCK%d, use 2100 mA\n", - desc->id - PF8X00_LDO4); - - swxilim_select(desc, val); + handle_ilim_property(np, desc, config); ret = of_property_read_u32(np, "nxp,phase-shift", &val); if (ret) { @@ -279,6 +303,8 @@ static const struct regulator_ops pf8x00_buck_ops = { .list_voltage = regulator_list_voltage_table, .set_voltage_sel = regulator_set_voltage_sel_regmap, .get_voltage_sel = regulator_get_voltage_sel_regmap, + .get_current_limit = regulator_get_current_limit_regmap, + .set_current_limit = regulator_set_current_limit_regmap, }; static const struct regulator_ops pf8x00_vsnvs_ops = { @@ -327,6 +353,11 @@ static const struct regulator_ops pf8x00_vsnvs_ops = { .volt_table = voltages, \ .vsel_reg = (base) + SW_RUN_VOLT, \ .vsel_mask = 0xff, \ + .curr_table = pf8x00_sw_current_table, \ + .n_current_limits = \ + ARRAY_SIZE(pf8x00_sw_current_table), \ + .csel_reg = (base) + SW_CONFIG2, \ + .csel_mask = PF8X00_SWXILIM_MASK, \ .enable_reg = (base) + SW_MODE1, \ .enable_val = 0x3, \ .disable_val = 0x0, \ @@ -458,10 +489,6 @@ static int pf8x00_i2c_probe(struct i2c_client *client) regmap_update_bits(chip->regmap, reg, PF8X00_SWXPHASE_MASK, data->phase_shift); - - regmap_update_bits(chip->regmap, reg, - PF8X00_SWXILIM_MASK, - data->ilim << PF8X00_SWXILIM_SHIFT); } } From 35a93349932e0e04c284f8a4954f3d1236c97d85 Mon Sep 17 00:00:00 2001 From: Adrien Grassein Date: Thu, 14 Jan 2021 18:47:13 +0100 Subject: [PATCH 21/49] regulator: pf8x00: use linear range for buck 1-6 Use a linear range to describe the voltages of the bucks 1-6 instead of listing it one by one (via a macro) Signed-off-by: Adrien Grassein Link: https://lore.kernel.org/r/20210114174714.122561-6-adrien.grassein@gmail.com Signed-off-by: Mark Brown --- drivers/regulator/pf8x00-regulator.c | 83 +++++++++++++++++----------- 1 file changed, 51 insertions(+), 32 deletions(-) diff --git a/drivers/regulator/pf8x00-regulator.c b/drivers/regulator/pf8x00-regulator.c index 0c0f643b9c60..866162fddd07 100644 --- a/drivers/regulator/pf8x00-regulator.c +++ b/drivers/regulator/pf8x00-regulator.c @@ -154,35 +154,11 @@ static const unsigned int pf8x00_sw_current_table[] = { 2100000, 2600000, 3000000, 4500000, }; -#define SWV(i) (6250 * i + 400000) -#define SWV_LINE(i) SWV(i*8+0), SWV(i*8+1), SWV(i*8+2), SWV(i*8+3), \ - SWV(i*8+4), SWV(i*8+5), SWV(i*8+6), SWV(i*8+7) - /* Output: 0.4V to 1.8V */ -static const int pf8x00_sw1_to_6_voltages[] = { - SWV_LINE(0), - SWV_LINE(1), - SWV_LINE(2), - SWV_LINE(3), - SWV_LINE(4), - SWV_LINE(5), - SWV_LINE(6), - SWV_LINE(7), - SWV_LINE(8), - SWV_LINE(9), - SWV_LINE(10), - SWV_LINE(11), - SWV_LINE(12), - SWV_LINE(13), - SWV_LINE(14), - SWV_LINE(15), - SWV_LINE(16), - SWV_LINE(17), - SWV_LINE(18), - SWV_LINE(19), - SWV_LINE(20), - SWV_LINE(21), - 1500000, 1800000, +#define PF8XOO_SW1_6_VOLTAGE_NUM 0xB2 +static const struct linear_range pf8x00_sw1_to_6_voltages[] = { + REGULATOR_LINEAR_RANGE(400000, 0x00, 0xB0, 6250), + REGULATOR_LINEAR_RANGE(1800000, 0xB1, 0xB1, 0), }; /* Output: 1.0V to 4.1V */ @@ -296,7 +272,19 @@ static const struct regulator_ops pf8x00_ldo_ops = { .get_voltage_sel = regulator_get_voltage_sel_regmap, }; -static const struct regulator_ops pf8x00_buck_ops = { + +static const struct regulator_ops pf8x00_buck1_6_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .list_voltage = regulator_list_voltage_linear_range, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .get_current_limit = regulator_get_current_limit_regmap, + .set_current_limit = regulator_set_current_limit_regmap, +}; + +static const struct regulator_ops pf8x00_buck7_ops = { .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, .is_enabled = regulator_is_enabled_regmap, @@ -345,11 +333,41 @@ static const struct regulator_ops pf8x00_vsnvs_ops = { .of_match = _name, \ .regulators_node = "regulators", \ .of_parse_cb = pf8x00_of_parse_cb, \ - .n_voltages = ARRAY_SIZE(voltages), \ - .ops = &pf8x00_buck_ops, \ + .n_voltages = PF8XOO_SW1_6_VOLTAGE_NUM, \ + .ops = &pf8x00_buck1_6_ops, \ .type = REGULATOR_VOLTAGE, \ .id = PF8X00_BUCK ## _id, \ .owner = THIS_MODULE, \ + .linear_ranges = pf8x00_sw1_to_6_voltages, \ + .n_linear_ranges = \ + ARRAY_SIZE(pf8x00_sw1_to_6_voltages), \ + .vsel_reg = (base) + SW_RUN_VOLT, \ + .vsel_mask = 0xff, \ + .curr_table = pf8x00_sw_current_table, \ + .n_current_limits = \ + ARRAY_SIZE(pf8x00_sw_current_table), \ + .csel_reg = (base) + SW_CONFIG2, \ + .csel_mask = PF8X00_SWXILIM_MASK, \ + .enable_reg = (base) + SW_MODE1, \ + .enable_val = 0x3, \ + .disable_val = 0x0, \ + .enable_mask = 0x3, \ + .enable_time = 500, \ + }, \ + } + +#define PF8X00BUCK7(_name, base, voltages) \ + [PF8X00_BUCK7] = { \ + .desc = { \ + .name = _name, \ + .of_match = _name, \ + .regulators_node = "regulators", \ + .of_parse_cb = pf8x00_of_parse_cb, \ + .n_voltages = ARRAY_SIZE(voltages), \ + .ops = &pf8x00_buck7_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = PF8X00_BUCK7, \ + .owner = THIS_MODULE, \ .volt_table = voltages, \ .vsel_reg = (base) + SW_RUN_VOLT, \ .vsel_mask = 0xff, \ @@ -366,6 +384,7 @@ static const struct regulator_ops pf8x00_vsnvs_ops = { }, \ } + #define PF8X00VSNVS(_name, base, voltages) \ [PF8X00_VSNVS] = { \ .desc = { \ @@ -394,7 +413,7 @@ static struct pf8x00_regulator pf8x00_regulators_data[PF8X00_MAX_REGULATORS] = { PF8X00BUCK(4, "buck4", PF8X00_SW_BASE(PF8X00_BUCK4), pf8x00_sw1_to_6_voltages), PF8X00BUCK(5, "buck5", PF8X00_SW_BASE(PF8X00_BUCK5), pf8x00_sw1_to_6_voltages), PF8X00BUCK(6, "buck6", PF8X00_SW_BASE(PF8X00_BUCK6), pf8x00_sw1_to_6_voltages), - PF8X00BUCK(7, "buck7", PF8X00_SW_BASE(PF8X00_BUCK7), pf8x00_sw7_voltages), + PF8X00BUCK7("buck7", PF8X00_SW_BASE(PF8X00_BUCK7), pf8x00_sw7_voltages), PF8X00VSNVS("vsnvs", PF8X00_VSNVS_CONFIG1, pf8x00_vsnvs_voltages), }; From 475a5d85ff62f7ca73f51f23977e7e3ec8c9f906 Mon Sep 17 00:00:00 2001 From: Adrien Grassein Date: Thu, 14 Jan 2021 18:47:14 +0100 Subject: [PATCH 22/49] regulator: pf8x00: fix nxp,phase-shift Fix the ternary condition which is a bad coding style in the kernel I also remove the defering configuration of the nxp,phase-shift. The configuration is now done at parsing time. It save some memory and it's better for comprehension. I also use the OTP default configuration when the parameter is wrong or not specified. I think that it's better to use the default configuration from the chip than an arbitrary value. Signed-off-by: Adrien Grassein Link: https://lore.kernel.org/r/20210114174714.122561-7-adrien.grassein@gmail.com Signed-off-by: Mark Brown --- drivers/regulator/pf8x00-regulator.c | 81 +++++++++++++++------------- 1 file changed, 43 insertions(+), 38 deletions(-) diff --git a/drivers/regulator/pf8x00-regulator.c b/drivers/regulator/pf8x00-regulator.c index 866162fddd07..1e5582d73405 100644 --- a/drivers/regulator/pf8x00-regulator.c +++ b/drivers/regulator/pf8x00-regulator.c @@ -114,7 +114,6 @@ enum swxilim_bits { #define PF8X00_SWXILIM_SHIFT 3 #define PF8X00_SWXILIM_MASK GENMASK(4, 3) #define PF8X00_SWXPHASE_MASK GENMASK(2, 0) -#define PF8X00_SWXPHASE_DEFAULT 0 #define PF8X00_SWXPHASE_SHIFT 7 enum pf8x00_devid { @@ -128,7 +127,6 @@ enum pf8x00_devid { struct pf8x00_regulator { struct regulator_desc desc; - u8 phase_shift; }; struct pf8x00_chip { @@ -174,11 +172,6 @@ static const int pf8x00_vsnvs_voltages[] = { 0, 1800000, 3000000, 3300000, }; -static struct pf8x00_regulator *desc_to_regulator(const struct regulator_desc *desc) -{ - return container_of(desc, struct pf8x00_regulator, desc); -} - static void swxilim_select(struct pf8x00_chip *chip, int id, int ilim) { u8 ilim_sel; @@ -230,35 +223,55 @@ static void handle_ilim_property(struct device_node *np, dev_warn(chip->dev, "nxp,ilim-ma used with incorrect regulator (%d)\n", desc->id); } +static void handle_shift_property(struct device_node *np, + const struct regulator_desc *desc, + struct regulator_config *config) +{ + unsigned char id = desc->id - PF8X00_LDO4; + unsigned char reg = PF8X00_SW_BASE(id) + SW_CONFIG2; + struct pf8x00_chip *chip = config->driver_data; + + int phase; + int val; + int ret; + if ((desc->id >= PF8X00_BUCK1) && (desc->id <= PF8X00_BUCK7)) { + ret = of_property_read_u32(np, "nxp,phase-shift", &val); + if (ret) { + dev_dbg(chip->dev, + "unspecified phase-shift for BUCK%d, using OTP configuration\n", + id); + return; + } + + if (val < 0 || val > 315 || val % 45 != 0) { + dev_warn(config->dev, + "invalid phase_shift %d for BUCK%d, using OTP configuration\n", + val, id); + return; + } + + phase = val / 45; + + if (phase >= 1) + phase -= 1; + else + phase = PF8X00_SWXPHASE_SHIFT; + + regmap_update_bits(chip->regmap, reg, + PF8X00_SWXPHASE_MASK, + phase); + } else + dev_warn(chip->dev, "nxp,phase-shift used with incorrect regulator (%d)\n", id); + +} + static int pf8x00_of_parse_cb(struct device_node *np, const struct regulator_desc *desc, struct regulator_config *config) { - struct pf8x00_regulator *data = desc_to_regulator(desc); - struct pf8x00_chip *chip = config->driver_data; - int phase; - int val; - int ret; handle_ilim_property(np, desc, config); - - ret = of_property_read_u32(np, "nxp,phase-shift", &val); - if (ret) { - dev_dbg(chip->dev, - "unspecified phase-shift for BUCK%d, use 0 degrees\n", - desc->id - PF8X00_LDO4); - val = PF8X00_SWXPHASE_DEFAULT; - } - - phase = val / 45; - if ((phase * 45) != val) { - dev_warn(config->dev, - "invalid phase_shift %d for BUCK%d, use 0 degrees\n", - (phase * 45), desc->id - PF8X00_LDO4); - phase = PF8X00_SWXPHASE_SHIFT; - } - - data->phase_shift = (phase >= 1) ? phase - 1 : PF8X00_SWXPHASE_SHIFT; + handle_shift_property(np, desc, config); return 0; } @@ -501,14 +514,6 @@ static int pf8x00_i2c_probe(struct i2c_client *client) "failed to register %s regulator\n", data->desc.name); return PTR_ERR(rdev); } - - if ((id >= PF8X00_BUCK1) && (id <= PF8X00_BUCK7)) { - u8 reg = PF8X00_SW_BASE(id) + SW_CONFIG2; - - regmap_update_bits(chip->regmap, reg, - PF8X00_SWXPHASE_MASK, - data->phase_shift); - } } return 0; From dd582369c6c1f39ec475af6191a934f3e57fda35 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Wed, 13 Jan 2021 20:42:08 +0100 Subject: [PATCH 23/49] regulator: qcom-labibb: Implement voltage selector ops Implement {get,set}_voltage_sel, list_voltage, map_voltage with the useful regulator regmap helpers in order to be able to manage the voltage of LAB (positive) and IBB (negative) regulators. In particular, the supported ranges are the following: - LAB (pos): 4600mV to 6100mV with 100mV stepping, - IBB (neg): -7700mV to -1400mV with 100mV stepping. Signed-off-by: AngeloGioacchino Del Regno Reviewed-by: Bjorn Andersson Link: https://lore.kernel.org/r/20210113194214.522238-2-angelogioacchino.delregno@somainline.org Signed-off-by: Mark Brown --- drivers/regulator/qcom-labibb-regulator.c | 28 +++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/drivers/regulator/qcom-labibb-regulator.c b/drivers/regulator/qcom-labibb-regulator.c index 8ccf572394a2..9f51c96f16fb 100644 --- a/drivers/regulator/qcom-labibb-regulator.c +++ b/drivers/regulator/qcom-labibb-regulator.c @@ -19,6 +19,12 @@ #define PMI8998_IBB_REG_BASE 0xdc00 #define REG_LABIBB_STATUS1 0x08 + +#define REG_LABIBB_VOLTAGE 0x41 + #define LABIBB_VOLTAGE_OVERRIDE_EN BIT(7) + #define LAB_VOLTAGE_SET_MASK GENMASK(3, 0) + #define IBB_VOLTAGE_SET_MASK GENMASK(5, 0) + #define REG_LABIBB_ENABLE_CTL 0x46 #define LABIBB_STATUS1_VREG_OK_BIT BIT(7) #define LABIBB_CONTROL_ENABLE BIT(7) @@ -51,6 +57,10 @@ static const struct regulator_ops qcom_labibb_ops = { .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, .is_enabled = regulator_is_enabled_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .list_voltage = regulator_list_voltage_linear_range, + .map_voltage = regulator_map_voltage_linear_range, }; static const struct regulator_desc pmi8998_lab_desc = { @@ -59,9 +69,18 @@ static const struct regulator_desc pmi8998_lab_desc = { .enable_val = LABIBB_CONTROL_ENABLE, .enable_time = LAB_ENABLE_TIME, .poll_enabled_time = LABIBB_POLL_ENABLED_TIME, + .vsel_reg = (PMI8998_LAB_REG_BASE + REG_LABIBB_VOLTAGE), + .vsel_mask = LAB_VOLTAGE_SET_MASK, + .apply_reg = (PMI8998_LAB_REG_BASE + REG_LABIBB_VOLTAGE), + .apply_bit = LABIBB_VOLTAGE_OVERRIDE_EN, .off_on_delay = LABIBB_OFF_ON_DELAY, .owner = THIS_MODULE, .type = REGULATOR_VOLTAGE, + .linear_ranges = (struct linear_range[]) { + REGULATOR_LINEAR_RANGE(4600000, 0, 15, 100000), + }, + .n_linear_ranges = 1, + .n_voltages = 16, .ops = &qcom_labibb_ops, }; @@ -71,9 +90,18 @@ static const struct regulator_desc pmi8998_ibb_desc = { .enable_val = LABIBB_CONTROL_ENABLE, .enable_time = IBB_ENABLE_TIME, .poll_enabled_time = LABIBB_POLL_ENABLED_TIME, + .vsel_reg = (PMI8998_IBB_REG_BASE + REG_LABIBB_VOLTAGE), + .vsel_mask = IBB_VOLTAGE_SET_MASK, + .apply_reg = (PMI8998_IBB_REG_BASE + REG_LABIBB_VOLTAGE), + .apply_bit = LABIBB_VOLTAGE_OVERRIDE_EN, .off_on_delay = LABIBB_OFF_ON_DELAY, .owner = THIS_MODULE, .type = REGULATOR_VOLTAGE, + .linear_ranges = (struct linear_range[]) { + REGULATOR_LINEAR_RANGE(1400000, 0, 63, 100000), + }, + .n_linear_ranges = 1, + .n_voltages = 64, .ops = &qcom_labibb_ops, }; From 9a12eb704ea27826ece4414cb8822388ec54883c Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Tue, 19 Jan 2021 18:44:15 +0100 Subject: [PATCH 24/49] regulator: qcom-labibb: Switch voltage ops from linear_range to linear The LAB and IBB regulator have just one range and it is useless to use linear_range ops, as these are used to express multiple linear ranges. Switch list_voltage and map_voltage callbacks to *_linear instead. Signed-off-by: AngeloGioacchino Del Regno Link: https://lore.kernel.org/r/20210119174421.226541-2-angelogioacchino.delregno@somainline.org Signed-off-by: Mark Brown --- drivers/regulator/qcom-labibb-regulator.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/drivers/regulator/qcom-labibb-regulator.c b/drivers/regulator/qcom-labibb-regulator.c index 9f51c96f16fb..0fe0f6bce4cf 100644 --- a/drivers/regulator/qcom-labibb-regulator.c +++ b/drivers/regulator/qcom-labibb-regulator.c @@ -59,8 +59,8 @@ static const struct regulator_ops qcom_labibb_ops = { .is_enabled = regulator_is_enabled_regmap, .set_voltage_sel = regulator_set_voltage_sel_regmap, .get_voltage_sel = regulator_get_voltage_sel_regmap, - .list_voltage = regulator_list_voltage_linear_range, - .map_voltage = regulator_map_voltage_linear_range, + .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, }; static const struct regulator_desc pmi8998_lab_desc = { @@ -76,10 +76,8 @@ static const struct regulator_desc pmi8998_lab_desc = { .off_on_delay = LABIBB_OFF_ON_DELAY, .owner = THIS_MODULE, .type = REGULATOR_VOLTAGE, - .linear_ranges = (struct linear_range[]) { - REGULATOR_LINEAR_RANGE(4600000, 0, 15, 100000), - }, - .n_linear_ranges = 1, + .min_uV = 4600000, + .uV_step = 100000, .n_voltages = 16, .ops = &qcom_labibb_ops, }; @@ -97,10 +95,8 @@ static const struct regulator_desc pmi8998_ibb_desc = { .off_on_delay = LABIBB_OFF_ON_DELAY, .owner = THIS_MODULE, .type = REGULATOR_VOLTAGE, - .linear_ranges = (struct linear_range[]) { - REGULATOR_LINEAR_RANGE(1400000, 0, 63, 100000), - }, - .n_linear_ranges = 1, + .min_uV = 1400000, + .uV_step = 100000, .n_voltages = 64, .ops = &qcom_labibb_ops, }; From 8056704ba948c1c54c7a67d78a8399a749f2d04b Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Tue, 19 Jan 2021 18:44:16 +0100 Subject: [PATCH 25/49] regulator: qcom-labibb: Implement current limiting LAB and IBB regulators can be current-limited by setting the appropriate registers, but this operation is granted only after sending an unlock code for secure access. Besides the secure access, it would be possible to use the regmap helper for get_current_limit, as there is no security blocking reads, but I chose not to as to avoid having a very big array containing current limits, especially for IBB. That said, these regulators support current limiting for: - LAB (pos): 200-1600mA, with 200mA per step (8 steps), - IBB (neg): 0-1550mA, with 50mA per step (32 steps). Signed-off-by: AngeloGioacchino Del Regno Link: https://lore.kernel.org/r/20210119174421.226541-3-angelogioacchino.delregno@somainline.org Signed-off-by: Mark Brown --- drivers/regulator/qcom-labibb-regulator.c | 92 +++++++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/drivers/regulator/qcom-labibb-regulator.c b/drivers/regulator/qcom-labibb-regulator.c index 0fe0f6bce4cf..0643713d6aad 100644 --- a/drivers/regulator/qcom-labibb-regulator.c +++ b/drivers/regulator/qcom-labibb-regulator.c @@ -29,6 +29,15 @@ #define LABIBB_STATUS1_VREG_OK_BIT BIT(7) #define LABIBB_CONTROL_ENABLE BIT(7) +#define REG_LABIBB_CURRENT_LIMIT 0x4b + #define LAB_CURRENT_LIMIT_MASK GENMASK(2, 0) + #define IBB_CURRENT_LIMIT_MASK GENMASK(4, 0) + #define LAB_CURRENT_LIMIT_OVERRIDE_EN BIT(3) + #define LABIBB_CURRENT_LIMIT_EN BIT(7) + +#define REG_LABIBB_SEC_ACCESS 0xd0 + #define LABIBB_SEC_UNLOCK_CODE 0xa5 + #define LAB_ENABLE_CTL_MASK BIT(7) #define IBB_ENABLE_CTL_MASK (BIT(7) | BIT(6)) @@ -37,11 +46,18 @@ #define IBB_ENABLE_TIME (LABIBB_OFF_ON_DELAY * 10) #define LABIBB_POLL_ENABLED_TIME 1000 +struct labibb_current_limits { + u32 uA_min; + u32 uA_step; + u8 ovr_val; +}; + struct labibb_regulator { struct regulator_desc desc; struct device *dev; struct regmap *regmap; struct regulator_dev *rdev; + struct labibb_current_limits uA_limits; u16 base; u8 type; }; @@ -53,6 +69,57 @@ struct labibb_regulator_data { const struct regulator_desc *desc; }; +static int qcom_labibb_set_current_limit(struct regulator_dev *rdev, + int min_uA, int max_uA) +{ + struct labibb_regulator *vreg = rdev_get_drvdata(rdev); + struct regulator_desc *desc = &vreg->desc; + struct labibb_current_limits *lim = &vreg->uA_limits; + u32 mask, val; + int i, ret, sel = -1; + + if (min_uA < lim->uA_min || max_uA < lim->uA_min) + return -EINVAL; + + for (i = 0; i < desc->n_current_limits; i++) { + int uA_limit = (lim->uA_step * i) + lim->uA_min; + + if (max_uA >= uA_limit && min_uA <= uA_limit) + sel = i; + } + if (sel < 0) + return -EINVAL; + + /* Current limit setting needs secure access */ + ret = regmap_write(vreg->regmap, vreg->base + REG_LABIBB_SEC_ACCESS, + LABIBB_SEC_UNLOCK_CODE); + if (ret) + return ret; + + mask = desc->csel_mask | lim->ovr_val; + mask |= LABIBB_CURRENT_LIMIT_EN; + val = (u32)sel | lim->ovr_val; + val |= LABIBB_CURRENT_LIMIT_EN; + + return regmap_update_bits(vreg->regmap, desc->csel_reg, mask, val); +} + +static int qcom_labibb_get_current_limit(struct regulator_dev *rdev) +{ + struct labibb_regulator *vreg = rdev_get_drvdata(rdev); + struct regulator_desc *desc = &vreg->desc; + struct labibb_current_limits *lim = &vreg->uA_limits; + unsigned int cur_step; + int ret; + + ret = regmap_read(vreg->regmap, desc->csel_reg, &cur_step); + if (ret) + return ret; + cur_step &= desc->csel_mask; + + return (cur_step * lim->uA_step) + lim->uA_min; +} + static const struct regulator_ops qcom_labibb_ops = { .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, @@ -61,6 +128,8 @@ static const struct regulator_ops qcom_labibb_ops = { .get_voltage_sel = regulator_get_voltage_sel_regmap, .list_voltage = regulator_list_voltage_linear, .map_voltage = regulator_map_voltage_linear, + .set_current_limit = qcom_labibb_set_current_limit, + .get_current_limit = qcom_labibb_get_current_limit, }; static const struct regulator_desc pmi8998_lab_desc = { @@ -73,6 +142,9 @@ static const struct regulator_desc pmi8998_lab_desc = { .vsel_mask = LAB_VOLTAGE_SET_MASK, .apply_reg = (PMI8998_LAB_REG_BASE + REG_LABIBB_VOLTAGE), .apply_bit = LABIBB_VOLTAGE_OVERRIDE_EN, + .csel_reg = (PMI8998_LAB_REG_BASE + REG_LABIBB_CURRENT_LIMIT), + .csel_mask = LAB_CURRENT_LIMIT_MASK, + .n_current_limits = 8, .off_on_delay = LABIBB_OFF_ON_DELAY, .owner = THIS_MODULE, .type = REGULATOR_VOLTAGE, @@ -92,6 +164,9 @@ static const struct regulator_desc pmi8998_ibb_desc = { .vsel_mask = IBB_VOLTAGE_SET_MASK, .apply_reg = (PMI8998_IBB_REG_BASE + REG_LABIBB_VOLTAGE), .apply_bit = LABIBB_VOLTAGE_OVERRIDE_EN, + .csel_reg = (PMI8998_IBB_REG_BASE + REG_LABIBB_CURRENT_LIMIT), + .csel_mask = IBB_CURRENT_LIMIT_MASK, + .n_current_limits = 32, .off_on_delay = LABIBB_OFF_ON_DELAY, .owner = THIS_MODULE, .type = REGULATOR_VOLTAGE, @@ -163,6 +238,23 @@ static int qcom_labibb_regulator_probe(struct platform_device *pdev) vreg->base = reg_data->base; vreg->type = reg_data->type; + switch (vreg->type) { + case QCOM_LAB_TYPE: + /* LAB Limits: 200-1600mA */ + vreg->uA_limits.uA_min = 200000; + vreg->uA_limits.uA_step = 200000; + vreg->uA_limits.ovr_val = LAB_CURRENT_LIMIT_OVERRIDE_EN; + break; + case QCOM_IBB_TYPE: + /* IBB Limits: 0-1550mA */ + vreg->uA_limits.uA_min = 0; + vreg->uA_limits.uA_step = 50000; + vreg->uA_limits.ovr_val = 0; /* No override bit */ + break; + default: + return -EINVAL; + } + memcpy(&vreg->desc, reg_data->desc, sizeof(vreg->desc)); vreg->desc.of_match = reg_data->name; vreg->desc.name = reg_data->name; From 3bc7cb99fb6eafae5a40bf71ded444df70a425f7 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Tue, 19 Jan 2021 18:44:17 +0100 Subject: [PATCH 26/49] regulator: qcom-labibb: Implement pull-down, softstart, active discharge Soft start is required to avoid inrush current during LAB ramp-up and IBB ramp-down, protecting connected hardware to which we supply voltage. Since soft start is configurable on both LAB and IBB regulators, it was necessary to add two DT properties, respectively "qcom,soft-start-us" to control LAB ramp-up and "qcom,discharge-resistor-kohms" to control the discharge resistor for IBB ramp-down, which obviously brought the need of implementing a of_parse callback for both regulators. Finally, also implement pull-down mode in order to avoid unpredictable behavior when the regulators are disabled (random voltage spikes etc). Signed-off-by: AngeloGioacchino Del Regno Reviewed-by: Bjorn Andersson Link: https://lore.kernel.org/r/20210119174421.226541-4-angelogioacchino.delregno@somainline.org Signed-off-by: Mark Brown --- drivers/regulator/qcom-labibb-regulator.c | 94 +++++++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/drivers/regulator/qcom-labibb-regulator.c b/drivers/regulator/qcom-labibb-regulator.c index 0643713d6aad..4d68b69b5a52 100644 --- a/drivers/regulator/qcom-labibb-regulator.c +++ b/drivers/regulator/qcom-labibb-regulator.c @@ -29,12 +29,23 @@ #define LABIBB_STATUS1_VREG_OK_BIT BIT(7) #define LABIBB_CONTROL_ENABLE BIT(7) +#define REG_LABIBB_PD_CTL 0x47 + #define LAB_PD_CTL_MASK GENMASK(1, 0) + #define IBB_PD_CTL_MASK (BIT(0) | BIT(7)) + #define LAB_PD_CTL_STRONG_PULL BIT(0) + #define IBB_PD_CTL_HALF_STRENGTH BIT(0) + #define IBB_PD_CTL_EN BIT(7) + #define REG_LABIBB_CURRENT_LIMIT 0x4b #define LAB_CURRENT_LIMIT_MASK GENMASK(2, 0) #define IBB_CURRENT_LIMIT_MASK GENMASK(4, 0) #define LAB_CURRENT_LIMIT_OVERRIDE_EN BIT(3) #define LABIBB_CURRENT_LIMIT_EN BIT(7) +#define REG_IBB_PWRUP_PWRDN_CTL_1 0x58 + #define IBB_CTL_1_DISCHARGE_EN BIT(2) + +#define REG_LABIBB_SOFT_START_CTL 0x5f #define REG_LABIBB_SEC_ACCESS 0xd0 #define LABIBB_SEC_UNLOCK_CODE 0xa5 @@ -60,6 +71,8 @@ struct labibb_regulator { struct labibb_current_limits uA_limits; u16 base; u8 type; + u8 dischg_sel; + u8 soft_start_sel; }; struct labibb_regulator_data { @@ -120,6 +133,70 @@ static int qcom_labibb_get_current_limit(struct regulator_dev *rdev) return (cur_step * lim->uA_step) + lim->uA_min; } +static int qcom_labibb_set_soft_start(struct regulator_dev *rdev) +{ + struct labibb_regulator *vreg = rdev_get_drvdata(rdev); + u32 val = 0; + + if (vreg->type == QCOM_IBB_TYPE) + val = vreg->dischg_sel; + else + val = vreg->soft_start_sel; + + return regmap_write(rdev->regmap, rdev->desc->soft_start_reg, val); +} + +static int qcom_labibb_get_table_sel(const int *table, int sz, u32 value) +{ + int i; + + for (i = 0; i < sz; i++) + if (table[i] == value) + return i; + return -EINVAL; +} + +/* IBB discharge resistor values in KOhms */ +static const int dischg_resistor_values[] = { 300, 64, 32, 16 }; + +/* Soft start time in microseconds */ +static const int soft_start_values[] = { 200, 400, 600, 800 }; + +static int qcom_labibb_of_parse_cb(struct device_node *np, + const struct regulator_desc *desc, + struct regulator_config *config) +{ + struct labibb_regulator *vreg = config->driver_data; + u32 dischg_kohms, soft_start_time; + int ret; + + ret = of_property_read_u32(np, "qcom,discharge-resistor-kohms", + &dischg_kohms); + if (ret) + dischg_kohms = 300; + + ret = qcom_labibb_get_table_sel(dischg_resistor_values, + ARRAY_SIZE(dischg_resistor_values), + dischg_kohms); + if (ret < 0) + return ret; + vreg->dischg_sel = (u8)ret; + + ret = of_property_read_u32(np, "qcom,soft-start-us", + &soft_start_time); + if (ret) + soft_start_time = 200; + + ret = qcom_labibb_get_table_sel(soft_start_values, + ARRAY_SIZE(soft_start_values), + soft_start_time); + if (ret < 0) + return ret; + vreg->soft_start_sel = (u8)ret; + + return 0; +} + static const struct regulator_ops qcom_labibb_ops = { .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, @@ -128,8 +205,11 @@ static const struct regulator_ops qcom_labibb_ops = { .get_voltage_sel = regulator_get_voltage_sel_regmap, .list_voltage = regulator_list_voltage_linear, .map_voltage = regulator_map_voltage_linear, + .set_active_discharge = regulator_set_active_discharge_regmap, + .set_pull_down = regulator_set_pull_down_regmap, .set_current_limit = qcom_labibb_set_current_limit, .get_current_limit = qcom_labibb_get_current_limit, + .set_soft_start = qcom_labibb_set_soft_start, }; static const struct regulator_desc pmi8998_lab_desc = { @@ -138,6 +218,10 @@ static const struct regulator_desc pmi8998_lab_desc = { .enable_val = LABIBB_CONTROL_ENABLE, .enable_time = LAB_ENABLE_TIME, .poll_enabled_time = LABIBB_POLL_ENABLED_TIME, + .soft_start_reg = (PMI8998_LAB_REG_BASE + REG_LABIBB_SOFT_START_CTL), + .pull_down_reg = (PMI8998_LAB_REG_BASE + REG_LABIBB_PD_CTL), + .pull_down_mask = LAB_PD_CTL_MASK, + .pull_down_val_on = LAB_PD_CTL_STRONG_PULL, .vsel_reg = (PMI8998_LAB_REG_BASE + REG_LABIBB_VOLTAGE), .vsel_mask = LAB_VOLTAGE_SET_MASK, .apply_reg = (PMI8998_LAB_REG_BASE + REG_LABIBB_VOLTAGE), @@ -152,6 +236,7 @@ static const struct regulator_desc pmi8998_lab_desc = { .uV_step = 100000, .n_voltages = 16, .ops = &qcom_labibb_ops, + .of_parse_cb = qcom_labibb_of_parse_cb, }; static const struct regulator_desc pmi8998_ibb_desc = { @@ -160,6 +245,14 @@ static const struct regulator_desc pmi8998_ibb_desc = { .enable_val = LABIBB_CONTROL_ENABLE, .enable_time = IBB_ENABLE_TIME, .poll_enabled_time = LABIBB_POLL_ENABLED_TIME, + .soft_start_reg = (PMI8998_IBB_REG_BASE + REG_LABIBB_SOFT_START_CTL), + .active_discharge_off = 0, + .active_discharge_on = IBB_CTL_1_DISCHARGE_EN, + .active_discharge_mask = IBB_CTL_1_DISCHARGE_EN, + .active_discharge_reg = (PMI8998_IBB_REG_BASE + REG_IBB_PWRUP_PWRDN_CTL_1), + .pull_down_reg = (PMI8998_IBB_REG_BASE + REG_LABIBB_PD_CTL), + .pull_down_mask = IBB_PD_CTL_MASK, + .pull_down_val_on = IBB_PD_CTL_HALF_STRENGTH | IBB_PD_CTL_EN, .vsel_reg = (PMI8998_IBB_REG_BASE + REG_LABIBB_VOLTAGE), .vsel_mask = IBB_VOLTAGE_SET_MASK, .apply_reg = (PMI8998_IBB_REG_BASE + REG_LABIBB_VOLTAGE), @@ -174,6 +267,7 @@ static const struct regulator_desc pmi8998_ibb_desc = { .uV_step = 100000, .n_voltages = 64, .ops = &qcom_labibb_ops, + .of_parse_cb = qcom_labibb_of_parse_cb, }; static const struct labibb_regulator_data pmi8998_labibb_data[] = { From 390af53e04114f790d60b63802a4de9d815ade03 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Tue, 19 Jan 2021 18:44:19 +0100 Subject: [PATCH 27/49] regulator: qcom-labibb: Implement short-circuit and over-current IRQs Short-Circuit Protection (SCP) and Over-Current Protection (OCP) are very important for regulators like LAB and IBB, which are designed to provide from very small to relatively big amounts of current to the device (normally, a display). Now that this regulator supports both voltage setting and current limiting in this driver, to me it looked like being somehow essential to provide support for SCP and OCP, for two reasons: 1. SCP is a drastic measure to prevent damaging "more" hardware in the worst situations, if any was damaged, preventing potentially drastic issues; 2. OCP is a great way to protect the hardware that we're powering through these regulators as if anything bad happens, the HW will draw more current than expected: in this case, the OCP interrupt will fire and the regulators will be immediately shut down, preventing hardware damage in many cases. Both interrupts were successfully tested in a "sort-of" controlled manner, with the following methodology: Short-Circuit Protection (SCP): 1. Set LAB/IBB to 4.6/-1.4V, current limit 200mA/50mA; 2. Connect a 10 KOhm resistor to LAB/IBB by poking the right traces on a FxTec Pro1 smartphone for a very brief time (in short words, "just a rapid touch with flying wires"); 3. The Short-Circuit protection trips: IRQ raises, regulators get cut. Recovery OK, test repeated without rebooting, OK. Over-Current Protection (OCP): 1. Set LAB/IBB to the expected voltage to power up the display of a Sony Xperia XZ Premium smartphone (Sharp LS055D1SX04), set current limit to LAB 200mA, IBB 50mA (the values that this display unit needs are 200/800mA); 2. Boot the kernel: OCP fires. Recovery never happens because the selected current limit is too low, but that's expected. Test OK. 3. Set LAB/IBB to the expected current limits for XZ Premium (LAB 200mA, IBB 800mA), but lower than expected voltage, specifically LAB 5.4V, IBB -5.6V (instead of 5.6, -5.8V); 4. Boot the kernel: OCP fires. Recovery never happens because the selected voltage (still in the working range limits) is producing a current draw of more than 200mA on LAB. Test OK. Signed-off-by: AngeloGioacchino Del Regno Link: https://lore.kernel.org/r/20210119174421.226541-6-angelogioacchino.delregno@somainline.org Signed-off-by: Mark Brown --- drivers/regulator/qcom-labibb-regulator.c | 518 +++++++++++++++++++++- 1 file changed, 515 insertions(+), 3 deletions(-) diff --git a/drivers/regulator/qcom-labibb-regulator.c b/drivers/regulator/qcom-labibb-regulator.c index 4d68b69b5a52..dbb4511c3c6d 100644 --- a/drivers/regulator/qcom-labibb-regulator.c +++ b/drivers/regulator/qcom-labibb-regulator.c @@ -17,8 +17,20 @@ #define PMI8998_LAB_REG_BASE 0xde00 #define PMI8998_IBB_REG_BASE 0xdc00 +#define PMI8998_IBB_LAB_REG_OFFSET 0x200 #define REG_LABIBB_STATUS1 0x08 + #define LABIBB_STATUS1_SC_BIT BIT(6) + #define LABIBB_STATUS1_VREG_OK_BIT BIT(7) + +#define REG_LABIBB_INT_SET_TYPE 0x11 +#define REG_LABIBB_INT_POLARITY_HIGH 0x12 +#define REG_LABIBB_INT_POLARITY_LOW 0x13 +#define REG_LABIBB_INT_LATCHED_CLR 0x14 +#define REG_LABIBB_INT_EN_SET 0x15 +#define REG_LABIBB_INT_EN_CLR 0x16 + #define LABIBB_INT_VREG_OK BIT(0) + #define LABIBB_INT_VREG_TYPE_LEVEL 0 #define REG_LABIBB_VOLTAGE 0x41 #define LABIBB_VOLTAGE_OVERRIDE_EN BIT(7) @@ -26,8 +38,7 @@ #define IBB_VOLTAGE_SET_MASK GENMASK(5, 0) #define REG_LABIBB_ENABLE_CTL 0x46 -#define LABIBB_STATUS1_VREG_OK_BIT BIT(7) -#define LABIBB_CONTROL_ENABLE BIT(7) + #define LABIBB_CONTROL_ENABLE BIT(7) #define REG_LABIBB_PD_CTL 0x47 #define LAB_PD_CTL_MASK GENMASK(1, 0) @@ -56,6 +67,11 @@ #define LAB_ENABLE_TIME (LABIBB_OFF_ON_DELAY * 2) #define IBB_ENABLE_TIME (LABIBB_OFF_ON_DELAY * 10) #define LABIBB_POLL_ENABLED_TIME 1000 +#define OCP_RECOVERY_INTERVAL_MS 500 +#define SC_RECOVERY_INTERVAL_MS 250 +#define LABIBB_MAX_OCP_COUNT 4 +#define LABIBB_MAX_SC_COUNT 3 +#define LABIBB_MAX_FATAL_COUNT 2 struct labibb_current_limits { u32 uA_min; @@ -69,10 +85,17 @@ struct labibb_regulator { struct regmap *regmap; struct regulator_dev *rdev; struct labibb_current_limits uA_limits; + struct delayed_work ocp_recovery_work; + struct delayed_work sc_recovery_work; u16 base; u8 type; u8 dischg_sel; u8 soft_start_sel; + int sc_irq; + int sc_count; + int ocp_irq; + int ocp_irq_count; + int fatal_count; }; struct labibb_regulator_data { @@ -82,6 +105,450 @@ struct labibb_regulator_data { const struct regulator_desc *desc; }; +static int qcom_labibb_ocp_hw_enable(struct regulator_dev *rdev) +{ + struct labibb_regulator *vreg = rdev_get_drvdata(rdev); + int ret; + + /* Clear irq latch status to avoid spurious event */ + ret = regmap_update_bits(rdev->regmap, + vreg->base + REG_LABIBB_INT_LATCHED_CLR, + LABIBB_INT_VREG_OK, 1); + if (ret) + return ret; + + /* Enable OCP HW interrupt */ + return regmap_update_bits(rdev->regmap, + vreg->base + REG_LABIBB_INT_EN_SET, + LABIBB_INT_VREG_OK, 1); +} + +static int qcom_labibb_ocp_hw_disable(struct regulator_dev *rdev) +{ + struct labibb_regulator *vreg = rdev_get_drvdata(rdev); + + return regmap_update_bits(rdev->regmap, + vreg->base + REG_LABIBB_INT_EN_CLR, + LABIBB_INT_VREG_OK, 1); +} + +/** + * qcom_labibb_check_ocp_status - Check the Over-Current Protection status + * @vreg: Main driver structure + * + * This function checks the STATUS1 register for the VREG_OK bit: if it is + * set, then there is no Over-Current event. + * + * Returns: Zero if there is no over-current, 1 if in over-current or + * negative number for error + */ +static int qcom_labibb_check_ocp_status(struct labibb_regulator *vreg) +{ + u32 cur_status; + int ret; + + ret = regmap_read(vreg->rdev->regmap, vreg->base + REG_LABIBB_STATUS1, + &cur_status); + if (ret) + return ret; + + return !(cur_status & LABIBB_STATUS1_VREG_OK_BIT); +} + +/** + * qcom_labibb_ocp_recovery_worker - Handle OCP event + * @work: OCP work structure + * + * This is the worker function to handle the Over Current Protection + * hardware event; This will check if the hardware is still + * signaling an over-current condition and will eventually stop + * the regulator if such condition is still signaled after + * LABIBB_MAX_OCP_COUNT times. + * + * If the driver that is consuming the regulator did not take action + * for the OCP condition, or the hardware did not stabilize, a cut + * of the LAB and IBB regulators will be forced (regulators will be + * disabled). + * + * As last, if the writes to shut down the LAB/IBB regulators fail + * for more than LABIBB_MAX_FATAL_COUNT, then a kernel panic will be + * triggered, as a last resort to protect the hardware from burning; + * this, however, is expected to never happen, but this is kept to + * try to further ensure that we protect the hardware at all costs. + */ +static void qcom_labibb_ocp_recovery_worker(struct work_struct *work) +{ + struct labibb_regulator *vreg; + const struct regulator_ops *ops; + int ret; + + vreg = container_of(work, struct labibb_regulator, + ocp_recovery_work.work); + ops = vreg->rdev->desc->ops; + + if (vreg->ocp_irq_count >= LABIBB_MAX_OCP_COUNT) { + /* + * If we tried to disable the regulator multiple times but + * we kept failing, there's only one last hope to save our + * hardware from the death: raise a kernel bug, reboot and + * hope that the bootloader kindly saves us. This, though + * is done only as paranoid checking, because failing the + * regmap write to disable the vreg is almost impossible, + * since we got here after multiple regmap R/W. + */ + BUG_ON(vreg->fatal_count > LABIBB_MAX_FATAL_COUNT); + dev_err(&vreg->rdev->dev, "LABIBB: CRITICAL: Disabling regulator\n"); + + /* Disable the regulator immediately to avoid damage */ + ret = ops->disable(vreg->rdev); + if (ret) { + vreg->fatal_count++; + goto reschedule; + } + enable_irq(vreg->ocp_irq); + vreg->fatal_count = 0; + return; + } + + ret = qcom_labibb_check_ocp_status(vreg); + if (ret != 0) { + vreg->ocp_irq_count++; + goto reschedule; + } + + ret = qcom_labibb_ocp_hw_enable(vreg->rdev); + if (ret) { + /* We cannot trust it without OCP enabled. */ + dev_err(vreg->dev, "Cannot enable OCP IRQ\n"); + vreg->ocp_irq_count++; + goto reschedule; + } + + enable_irq(vreg->ocp_irq); + /* Everything went fine: reset the OCP count! */ + vreg->ocp_irq_count = 0; + return; + +reschedule: + mod_delayed_work(system_wq, &vreg->ocp_recovery_work, + msecs_to_jiffies(OCP_RECOVERY_INTERVAL_MS)); +} + +/** + * qcom_labibb_ocp_isr - Interrupt routine for OverCurrent Protection + * @irq: Interrupt number + * @chip: Main driver structure + * + * Over Current Protection (OCP) will signal to the client driver + * that an over-current event has happened and then will schedule + * a recovery worker. + * + * Disabling and eventually re-enabling the regulator is expected + * to be done by the driver, as some hardware may be triggering an + * over-current condition only at first initialization or it may + * be expected only for a very brief amount of time, after which + * the attached hardware may be expected to stabilize its current + * draw. + * + * Returns: IRQ_HANDLED for success or IRQ_NONE for failure. + */ +static irqreturn_t qcom_labibb_ocp_isr(int irq, void *chip) +{ + struct labibb_regulator *vreg = chip; + const struct regulator_ops *ops = vreg->rdev->desc->ops; + int ret; + + /* If the regulator is not enabled, this is a fake event */ + if (!ops->is_enabled(vreg->rdev)) + return 0; + + /* If we tried to recover for too many times it's not getting better */ + if (vreg->ocp_irq_count > LABIBB_MAX_OCP_COUNT) + return IRQ_NONE; + + /* + * If we (unlikely) can't read this register, to prevent hardware + * damage at all costs, we assume that the overcurrent event was + * real; Moreover, if the status register is not signaling OCP, + * it was a spurious event, so it's all ok. + */ + ret = qcom_labibb_check_ocp_status(vreg); + if (ret == 0) { + vreg->ocp_irq_count = 0; + goto end; + } + vreg->ocp_irq_count++; + + /* + * Disable the interrupt temporarily, or it will fire continuously; + * we will re-enable it in the recovery worker function. + */ + disable_irq(irq); + + /* Warn the user for overcurrent */ + dev_warn(vreg->dev, "Over-Current interrupt fired!\n"); + + /* Disable the interrupt to avoid hogging */ + ret = qcom_labibb_ocp_hw_disable(vreg->rdev); + if (ret) + goto end; + + /* Signal overcurrent event to drivers */ + regulator_notifier_call_chain(vreg->rdev, + REGULATOR_EVENT_OVER_CURRENT, NULL); + +end: + /* Schedule the recovery work */ + schedule_delayed_work(&vreg->ocp_recovery_work, + msecs_to_jiffies(OCP_RECOVERY_INTERVAL_MS)); + if (ret) + return IRQ_NONE; + + return IRQ_HANDLED; +} + +static int qcom_labibb_set_ocp(struct regulator_dev *rdev) +{ + struct labibb_regulator *vreg = rdev_get_drvdata(rdev); + char *ocp_irq_name; + u32 irq_flags = IRQF_ONESHOT; + int irq_trig_low, ret; + + /* If there is no OCP interrupt, there's nothing to set */ + if (vreg->ocp_irq <= 0) + return -EINVAL; + + ocp_irq_name = devm_kasprintf(vreg->dev, GFP_KERNEL, "%s-over-current", + vreg->desc.name); + if (!ocp_irq_name) + return -ENOMEM; + + /* IRQ polarities - LAB: trigger-low, IBB: trigger-high */ + switch (vreg->type) { + case QCOM_LAB_TYPE: + irq_flags |= IRQF_TRIGGER_LOW; + irq_trig_low = 1; + break; + case QCOM_IBB_TYPE: + irq_flags |= IRQF_TRIGGER_HIGH; + irq_trig_low = 0; + break; + default: + return -EINVAL; + } + + /* Activate OCP HW level interrupt */ + ret = regmap_update_bits(rdev->regmap, + vreg->base + REG_LABIBB_INT_SET_TYPE, + LABIBB_INT_VREG_OK, + LABIBB_INT_VREG_TYPE_LEVEL); + if (ret) + return ret; + + /* Set OCP interrupt polarity */ + ret = regmap_update_bits(rdev->regmap, + vreg->base + REG_LABIBB_INT_POLARITY_HIGH, + LABIBB_INT_VREG_OK, !irq_trig_low); + if (ret) + return ret; + ret = regmap_update_bits(rdev->regmap, + vreg->base + REG_LABIBB_INT_POLARITY_LOW, + LABIBB_INT_VREG_OK, irq_trig_low); + if (ret) + return ret; + + ret = qcom_labibb_ocp_hw_enable(rdev); + if (ret) + return ret; + + return devm_request_threaded_irq(vreg->dev, vreg->ocp_irq, NULL, + qcom_labibb_ocp_isr, irq_flags, + ocp_irq_name, vreg); +} + +/** + * qcom_labibb_check_sc_status - Check the Short Circuit Protection status + * @vreg: Main driver structure + * + * This function checks the STATUS1 register on both LAB and IBB regulators + * for the ShortCircuit bit: if it is set on *any* of them, then we have + * experienced a short-circuit event. + * + * Returns: Zero if there is no short-circuit, 1 if in short-circuit or + * negative number for error + */ +static int qcom_labibb_check_sc_status(struct labibb_regulator *vreg) +{ + u32 ibb_status, ibb_reg, lab_status, lab_reg; + int ret; + + /* We have to work on both regulators due to PBS... */ + lab_reg = ibb_reg = vreg->base + REG_LABIBB_STATUS1; + if (vreg->type == QCOM_LAB_TYPE) + ibb_reg -= PMI8998_IBB_LAB_REG_OFFSET; + else + lab_reg += PMI8998_IBB_LAB_REG_OFFSET; + + ret = regmap_read(vreg->rdev->regmap, lab_reg, &lab_status); + if (ret) + return ret; + ret = regmap_read(vreg->rdev->regmap, ibb_reg, &ibb_status); + if (ret) + return ret; + + return !!(lab_status & LABIBB_STATUS1_SC_BIT) || + !!(ibb_status & LABIBB_STATUS1_SC_BIT); +} + +/** + * qcom_labibb_sc_recovery_worker - Handle Short Circuit event + * @work: SC work structure + * + * This is the worker function to handle the Short Circuit Protection + * hardware event; This will check if the hardware is still + * signaling a short-circuit condition and will eventually never + * re-enable the regulator if such condition is still signaled after + * LABIBB_MAX_SC_COUNT times. + * + * If the driver that is consuming the regulator did not take action + * for the SC condition, or the hardware did not stabilize, this + * worker will stop rescheduling, leaving the regulators disabled + * as already done by the Portable Batch System (PBS). + * + * Returns: IRQ_HANDLED for success or IRQ_NONE for failure. + */ +static void qcom_labibb_sc_recovery_worker(struct work_struct *work) +{ + struct labibb_regulator *vreg; + const struct regulator_ops *ops; + u32 lab_reg, ibb_reg, lab_val, ibb_val, val; + bool pbs_cut = false; + int i, sc, ret; + + vreg = container_of(work, struct labibb_regulator, + sc_recovery_work.work); + ops = vreg->rdev->desc->ops; + + /* + * If we tried to check the regulator status multiple times but we + * kept failing, then just bail out, as the Portable Batch System + * (PBS) will disable the vregs for us, preventing hardware damage. + */ + if (vreg->fatal_count > LABIBB_MAX_FATAL_COUNT) + return; + + /* Too many short-circuit events. Throw in the towel. */ + if (vreg->sc_count > LABIBB_MAX_SC_COUNT) + return; + + /* + * The Portable Batch System (PBS) automatically disables LAB + * and IBB when a short-circuit event is detected, so we have to + * check and work on both of them at the same time. + */ + lab_reg = ibb_reg = vreg->base + REG_LABIBB_ENABLE_CTL; + if (vreg->type == QCOM_LAB_TYPE) + ibb_reg -= PMI8998_IBB_LAB_REG_OFFSET; + else + lab_reg += PMI8998_IBB_LAB_REG_OFFSET; + + sc = qcom_labibb_check_sc_status(vreg); + if (sc) + goto reschedule; + + for (i = 0; i < LABIBB_MAX_SC_COUNT; i++) { + ret = regmap_read(vreg->regmap, lab_reg, &lab_val); + if (ret) { + vreg->fatal_count++; + goto reschedule; + } + + ret = regmap_read(vreg->regmap, ibb_reg, &ibb_val); + if (ret) { + vreg->fatal_count++; + goto reschedule; + } + val = lab_val & ibb_val; + + if (!(val & LABIBB_CONTROL_ENABLE)) { + pbs_cut = true; + break; + } + usleep_range(5000, 6000); + } + if (pbs_cut) + goto reschedule; + + + /* + * If we have reached this point, we either have successfully + * recovered from the SC condition or we had a spurious SC IRQ, + * which means that we can re-enable the regulators, if they + * have ever been disabled by the PBS. + */ + ret = ops->enable(vreg->rdev); + if (ret) + goto reschedule; + + /* Everything went fine: reset the OCP count! */ + vreg->sc_count = 0; + enable_irq(vreg->sc_irq); + return; + +reschedule: + /* + * Now that we have done basic handling of the short-circuit, + * reschedule this worker in the regular system workqueue, as + * taking action is not truly urgent anymore. + */ + vreg->sc_count++; + mod_delayed_work(system_wq, &vreg->sc_recovery_work, + msecs_to_jiffies(SC_RECOVERY_INTERVAL_MS)); +} + +/** + * qcom_labibb_sc_isr - Interrupt routine for Short Circuit Protection + * @irq: Interrupt number + * @chip: Main driver structure + * + * Short Circuit Protection (SCP) will signal to the client driver + * that a regulation-out event has happened and then will schedule + * a recovery worker. + * + * The LAB and IBB regulators will be automatically disabled by the + * Portable Batch System (PBS) and they will be enabled again by + * the worker function if the hardware stops signaling the short + * circuit event. + * + * Returns: IRQ_HANDLED for success or IRQ_NONE for failure. + */ +static irqreturn_t qcom_labibb_sc_isr(int irq, void *chip) +{ + struct labibb_regulator *vreg = chip; + + if (vreg->sc_count > LABIBB_MAX_SC_COUNT) + return IRQ_NONE; + + /* Warn the user for short circuit */ + dev_warn(vreg->dev, "Short-Circuit interrupt fired!\n"); + + /* + * Disable the interrupt temporarily, or it will fire continuously; + * we will re-enable it in the recovery worker function. + */ + disable_irq(irq); + + /* Signal out of regulation event to drivers */ + regulator_notifier_call_chain(vreg->rdev, + REGULATOR_EVENT_REGULATION_OUT, NULL); + + /* Schedule the short-circuit handling as high-priority work */ + mod_delayed_work(system_highpri_wq, &vreg->sc_recovery_work, + msecs_to_jiffies(SC_RECOVERY_INTERVAL_MS)); + return IRQ_HANDLED; +} + + static int qcom_labibb_set_current_limit(struct regulator_dev *rdev, int min_uA, int max_uA) { @@ -210,6 +677,7 @@ static const struct regulator_ops qcom_labibb_ops = { .set_current_limit = qcom_labibb_set_current_limit, .get_current_limit = qcom_labibb_get_current_limit, .set_soft_start = qcom_labibb_set_soft_start, + .set_over_current_protection = qcom_labibb_set_ocp, }; static const struct regulator_desc pmi8998_lab_desc = { @@ -287,7 +755,7 @@ static int qcom_labibb_regulator_probe(struct platform_device *pdev) struct labibb_regulator *vreg; struct device *dev = &pdev->dev; struct regulator_config cfg = {}; - + struct device_node *reg_node; const struct of_device_id *match; const struct labibb_regulator_data *reg_data; struct regmap *reg_regmap; @@ -305,6 +773,8 @@ static int qcom_labibb_regulator_probe(struct platform_device *pdev) return -ENODEV; for (reg_data = match->data; reg_data->name; reg_data++) { + char *sc_irq_name; + int irq = 0; /* Validate if the type of regulator is indeed * what's mentioned in DT. @@ -327,10 +797,44 @@ static int qcom_labibb_regulator_probe(struct platform_device *pdev) if (!vreg) return -ENOMEM; + sc_irq_name = devm_kasprintf(dev, GFP_KERNEL, + "%s-short-circuit", + reg_data->name); + if (!sc_irq_name) + return -ENOMEM; + + reg_node = of_get_child_by_name(pdev->dev.of_node, + reg_data->name); + if (!reg_node) + return -EINVAL; + + /* The Short Circuit interrupt is critical */ + irq = of_irq_get_byname(reg_node, "sc-err"); + if (irq <= 0) { + if (irq == 0) + irq = -EINVAL; + + return dev_err_probe(vreg->dev, irq, + "Short-circuit irq not found.\n"); + } + vreg->sc_irq = irq; + + /* OverCurrent Protection IRQ is optional */ + irq = of_irq_get_byname(reg_node, "ocp"); + vreg->ocp_irq = irq; + vreg->ocp_irq_count = 0; + of_node_put(reg_node); + vreg->regmap = reg_regmap; vreg->dev = dev; vreg->base = reg_data->base; vreg->type = reg_data->type; + INIT_DELAYED_WORK(&vreg->sc_recovery_work, + qcom_labibb_sc_recovery_worker); + + if (vreg->ocp_irq > 0) + INIT_DELAYED_WORK(&vreg->ocp_recovery_work, + qcom_labibb_ocp_recovery_worker); switch (vreg->type) { case QCOM_LAB_TYPE: @@ -365,6 +869,14 @@ static int qcom_labibb_regulator_probe(struct platform_device *pdev) reg_data->name, ret); return PTR_ERR(vreg->rdev); } + + ret = devm_request_threaded_irq(vreg->dev, vreg->sc_irq, NULL, + qcom_labibb_sc_isr, + IRQF_ONESHOT | + IRQF_TRIGGER_RISING, + sc_irq_name, vreg); + if (ret) + return ret; } return 0; From 5581304004659ddc8d0d45561c1f2abfe080b4d4 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Tue, 19 Jan 2021 18:44:18 +0100 Subject: [PATCH 28/49] regulator: qcom-labibb: Document soft start properties Document properties to configure soft start and discharge resistor for LAB and IBB respectively. Signed-off-by: AngeloGioacchino Del Regno Reviewed-by: Bjorn Andersson Link: https://lore.kernel.org/r/20210119174421.226541-5-angelogioacchino.delregno@somainline.org Signed-off-by: Mark Brown --- .../bindings/regulator/qcom-labibb-regulator.yaml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Documentation/devicetree/bindings/regulator/qcom-labibb-regulator.yaml b/Documentation/devicetree/bindings/regulator/qcom-labibb-regulator.yaml index 53853ec20fe2..7a507692f1ba 100644 --- a/Documentation/devicetree/bindings/regulator/qcom-labibb-regulator.yaml +++ b/Documentation/devicetree/bindings/regulator/qcom-labibb-regulator.yaml @@ -22,6 +22,11 @@ properties: type: object properties: + qcom,soft-start-us: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Regulator soft start time in microseconds. + enum: [200, 400, 600, 800] + default: 200 interrupts: maxItems: 1 @@ -35,6 +40,11 @@ properties: type: object properties: + qcom,discharge-resistor-kohms: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Discharge resistor value in KiloOhms. + enum: [300, 64, 32, 16] + default: 300 interrupts: maxItems: 1 From 9499200484669fe33c20c735a3d5a29a0dc0e9d4 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Tue, 19 Jan 2021 18:44:20 +0100 Subject: [PATCH 29/49] regulator: qcom-labibb: Document SCP/OCP interrupts Short-Circuit Protection (SCP) and Over-Current Protection (OCP) are now implemented in the driver: document the interrupts. This also fixes wrong documentation about the SCP interrupt for LAB. Signed-off-by: AngeloGioacchino Del Regno Reviewed-by: Bjorn Andersson Link: https://lore.kernel.org/r/20210119174421.226541-7-angelogioacchino.delregno@somainline.org Signed-off-by: Mark Brown --- .../regulator/qcom-labibb-regulator.yaml | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/Documentation/devicetree/bindings/regulator/qcom-labibb-regulator.yaml b/Documentation/devicetree/bindings/regulator/qcom-labibb-regulator.yaml index 7a507692f1ba..cf784bd1f5e5 100644 --- a/Documentation/devicetree/bindings/regulator/qcom-labibb-regulator.yaml +++ b/Documentation/devicetree/bindings/regulator/qcom-labibb-regulator.yaml @@ -29,9 +29,10 @@ properties: default: 200 interrupts: - maxItems: 1 + minItems: 1 + maxItems: 2 description: - Short-circuit interrupt for lab. + Short-circuit and over-current interrupts for lab. required: - interrupts @@ -47,9 +48,10 @@ properties: default: 300 interrupts: - maxItems: 1 + minItems: 1 + maxItems: 2 description: - Short-circuit interrupt for lab. + Short-circuit and over-current interrupts for ibb. required: - interrupts @@ -67,13 +69,15 @@ examples: compatible = "qcom,pmi8998-lab-ibb"; lab { - interrupts = <0x3 0x0 IRQ_TYPE_EDGE_RISING>; - interrupt-names = "sc-err"; + interrupts = <0x3 0xde 0x1 IRQ_TYPE_EDGE_RISING>, + <0x3 0xde 0x0 IRQ_TYPE_LEVEL_LOW>; + interrupt-names = "sc-err", "ocp"; }; ibb { - interrupts = <0x3 0x2 IRQ_TYPE_EDGE_RISING>; - interrupt-names = "sc-err"; + interrupts = <0x3 0xdc 0x2 IRQ_TYPE_EDGE_RISING>, + <0x3 0xdc 0x0 IRQ_TYPE_LEVEL_LOW>; + interrupt-names = "sc-err", "ocp"; }; }; From e78bf6be7edaacb39778f3a89416caddfc6c6d70 Mon Sep 17 00:00:00 2001 From: Pan Bian Date: Wed, 20 Jan 2021 04:33:13 -0800 Subject: [PATCH 30/49] regulator: axp20x: Fix reference cout leak Decrements the reference count of device node and its child node. Fixes: dfe7a1b058bb ("regulator: AXP20x: Add support for regulators subsystem") Signed-off-by: Pan Bian Link: https://lore.kernel.org/r/20210120123313.107640-1-bianpan2016@163.com Signed-off-by: Mark Brown --- drivers/regulator/axp20x-regulator.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/regulator/axp20x-regulator.c b/drivers/regulator/axp20x-regulator.c index 90cb8445f721..d260c442b788 100644 --- a/drivers/regulator/axp20x-regulator.c +++ b/drivers/regulator/axp20x-regulator.c @@ -1070,7 +1070,7 @@ static int axp20x_set_dcdc_freq(struct platform_device *pdev, u32 dcdcfreq) static int axp20x_regulator_parse_dt(struct platform_device *pdev) { struct device_node *np, *regulators; - int ret; + int ret = 0; u32 dcdcfreq = 0; np = of_node_get(pdev->dev.parent->of_node); @@ -1085,13 +1085,12 @@ static int axp20x_regulator_parse_dt(struct platform_device *pdev) ret = axp20x_set_dcdc_freq(pdev, dcdcfreq); if (ret < 0) { dev_err(&pdev->dev, "Error setting dcdc frequency: %d\n", ret); - return ret; } - of_node_put(regulators); } - return 0; + of_node_put(np); + return ret; } static int axp20x_set_dcdc_workmode(struct regulator_dev *rdev, int id, u32 workmode) From d93d6f52bd9e3aa6f37b7ea5657e4bee50a4b0a4 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 20 Jan 2021 16:12:24 +0100 Subject: [PATCH 31/49] regulator: remove ab3100 driver The ST-Ericsson U300 platform is getting removed, so this driver is no longer needed. Cc: Linus Walleij Signed-off-by: Arnd Bergmann Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20210120151307.1726876-1-arnd@kernel.org Signed-off-by: Mark Brown --- drivers/regulator/Kconfig | 9 - drivers/regulator/Makefile | 1 - drivers/regulator/ab3100.c | 724 ------------------------------------- 3 files changed, 734 deletions(-) delete mode 100644 drivers/regulator/ab3100.c diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index ba8c09d90701..88316b8a65d2 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -122,15 +122,6 @@ config REGULATOR_AAT2870 If you have a AnalogicTech AAT2870 say Y to enable the regulator driver. -config REGULATOR_AB3100 - tristate "ST-Ericsson AB3100 Regulator functions" - depends on AB3100_CORE - default y if AB3100_CORE - help - These regulators correspond to functionality in the - AB3100 analog baseband dealing with power regulators - for the system. - config REGULATOR_AB8500 bool "ST-Ericsson AB8500 Power Regulators" depends on AB8500_CORE diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index bd93fbd1b124..d1957629537b 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -16,7 +16,6 @@ obj-$(CONFIG_REGULATOR_88PM8607) += 88pm8607.o obj-$(CONFIG_REGULATOR_CROS_EC) += cros-ec-regulator.o obj-$(CONFIG_REGULATOR_CPCAP) += cpcap-regulator.o obj-$(CONFIG_REGULATOR_AAT2870) += aat2870-regulator.o -obj-$(CONFIG_REGULATOR_AB3100) += ab3100.o obj-$(CONFIG_REGULATOR_AB8500) += ab8500-ext.o ab8500.o obj-$(CONFIG_REGULATOR_ACT8865) += act8865-regulator.o obj-$(CONFIG_REGULATOR_ACT8945A) += act8945a-regulator.o diff --git a/drivers/regulator/ab3100.c b/drivers/regulator/ab3100.c deleted file mode 100644 index a544f45efe53..000000000000 --- a/drivers/regulator/ab3100.c +++ /dev/null @@ -1,724 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * drivers/regulator/ab3100.c - * - * Copyright (C) 2008-2009 ST-Ericsson AB - * Low-level control of the AB3100 IC Low Dropout (LDO) - * regulators, external regulator and buck converter - * Author: Mattias Wallin - * Author: Linus Walleij - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* LDO registers and some handy masking definitions for AB3100 */ -#define AB3100_LDO_A 0x40 -#define AB3100_LDO_C 0x41 -#define AB3100_LDO_D 0x42 -#define AB3100_LDO_E 0x43 -#define AB3100_LDO_E_SLEEP 0x44 -#define AB3100_LDO_F 0x45 -#define AB3100_LDO_G 0x46 -#define AB3100_LDO_H 0x47 -#define AB3100_LDO_H_SLEEP_MODE 0 -#define AB3100_LDO_H_SLEEP_EN 2 -#define AB3100_LDO_ON 4 -#define AB3100_LDO_H_VSEL_AC 5 -#define AB3100_LDO_K 0x48 -#define AB3100_LDO_EXT 0x49 -#define AB3100_BUCK 0x4A -#define AB3100_BUCK_SLEEP 0x4B -#define AB3100_REG_ON_MASK 0x10 - -/** - * struct ab3100_regulator - * A struct passed around the individual regulator functions - * @platform_device: platform device holding this regulator - * @dev: handle to the device - * @plfdata: AB3100 platform data passed in at probe time - * @regreg: regulator register number in the AB3100 - */ -struct ab3100_regulator { - struct device *dev; - struct ab3100_platform_data *plfdata; - u8 regreg; -}; - -/* The order in which registers are initialized */ -static const u8 ab3100_reg_init_order[AB3100_NUM_REGULATORS+2] = { - AB3100_LDO_A, - AB3100_LDO_C, - AB3100_LDO_E, - AB3100_LDO_E_SLEEP, - AB3100_LDO_F, - AB3100_LDO_G, - AB3100_LDO_H, - AB3100_LDO_K, - AB3100_LDO_EXT, - AB3100_BUCK, - AB3100_BUCK_SLEEP, - AB3100_LDO_D, -}; - -/* Preset (hardware defined) voltages for these regulators */ -#define LDO_A_VOLTAGE 2750000 -#define LDO_C_VOLTAGE 2650000 -#define LDO_D_VOLTAGE 2650000 - -static const unsigned int ldo_e_buck_typ_voltages[] = { - 1800000, - 1400000, - 1300000, - 1200000, - 1100000, - 1050000, - 900000, -}; - -static const unsigned int ldo_f_typ_voltages[] = { - 1800000, - 1400000, - 1300000, - 1200000, - 1100000, - 1050000, - 2500000, - 2650000, -}; - -static const unsigned int ldo_g_typ_voltages[] = { - 2850000, - 2750000, - 1800000, - 1500000, -}; - -static const unsigned int ldo_h_typ_voltages[] = { - 2750000, - 1800000, - 1500000, - 1200000, -}; - -static const unsigned int ldo_k_typ_voltages[] = { - 2750000, - 1800000, -}; - - -/* The regulator devices */ -static struct ab3100_regulator -ab3100_regulators[AB3100_NUM_REGULATORS] = { - { - .regreg = AB3100_LDO_A, - }, - { - .regreg = AB3100_LDO_C, - }, - { - .regreg = AB3100_LDO_D, - }, - { - .regreg = AB3100_LDO_E, - }, - { - .regreg = AB3100_LDO_F, - }, - { - .regreg = AB3100_LDO_G, - }, - { - .regreg = AB3100_LDO_H, - }, - { - .regreg = AB3100_LDO_K, - }, - { - .regreg = AB3100_LDO_EXT, - /* No voltages for the external regulator */ - }, - { - .regreg = AB3100_BUCK, - }, -}; - -/* - * General functions for enable, disable and is_enabled used for - * LDO: A,C,E,F,G,H,K,EXT and BUCK - */ -static int ab3100_enable_regulator(struct regulator_dev *reg) -{ - struct ab3100_regulator *abreg = rdev_get_drvdata(reg); - int err; - u8 regval; - - err = abx500_get_register_interruptible(abreg->dev, 0, abreg->regreg, - ®val); - if (err) { - dev_warn(®->dev, "failed to get regid %d value\n", - abreg->regreg); - return err; - } - - /* The regulator is already on, no reason to go further */ - if (regval & AB3100_REG_ON_MASK) - return 0; - - regval |= AB3100_REG_ON_MASK; - - err = abx500_set_register_interruptible(abreg->dev, 0, abreg->regreg, - regval); - if (err) { - dev_warn(®->dev, "failed to set regid %d value\n", - abreg->regreg); - return err; - } - - return 0; -} - -static int ab3100_disable_regulator(struct regulator_dev *reg) -{ - struct ab3100_regulator *abreg = rdev_get_drvdata(reg); - int err; - u8 regval; - - /* - * LDO D is a special regulator. When it is disabled, the entire - * system is shut down. So this is handled specially. - */ - pr_info("Called ab3100_disable_regulator\n"); - if (abreg->regreg == AB3100_LDO_D) { - dev_info(®->dev, "disabling LDO D - shut down system\n"); - /* Setting LDO D to 0x00 cuts the power to the SoC */ - return abx500_set_register_interruptible(abreg->dev, 0, - AB3100_LDO_D, 0x00U); - } - - /* - * All other regulators are handled here - */ - err = abx500_get_register_interruptible(abreg->dev, 0, abreg->regreg, - ®val); - if (err) { - dev_err(®->dev, "unable to get register 0x%x\n", - abreg->regreg); - return err; - } - regval &= ~AB3100_REG_ON_MASK; - return abx500_set_register_interruptible(abreg->dev, 0, abreg->regreg, - regval); -} - -static int ab3100_is_enabled_regulator(struct regulator_dev *reg) -{ - struct ab3100_regulator *abreg = rdev_get_drvdata(reg); - u8 regval; - int err; - - err = abx500_get_register_interruptible(abreg->dev, 0, abreg->regreg, - ®val); - if (err) { - dev_err(®->dev, "unable to get register 0x%x\n", - abreg->regreg); - return err; - } - - return regval & AB3100_REG_ON_MASK; -} - -static int ab3100_get_voltage_regulator(struct regulator_dev *reg) -{ - struct ab3100_regulator *abreg = rdev_get_drvdata(reg); - u8 regval; - int err; - - /* - * For variable types, read out setting and index into - * supplied voltage list. - */ - err = abx500_get_register_interruptible(abreg->dev, 0, - abreg->regreg, ®val); - if (err) { - dev_warn(®->dev, - "failed to get regulator value in register %02x\n", - abreg->regreg); - return err; - } - - /* The 3 highest bits index voltages */ - regval &= 0xE0; - regval >>= 5; - - if (regval >= reg->desc->n_voltages) { - dev_err(®->dev, - "regulator register %02x contains an illegal voltage setting\n", - abreg->regreg); - return -EINVAL; - } - - return reg->desc->volt_table[regval]; -} - -static int ab3100_set_voltage_regulator_sel(struct regulator_dev *reg, - unsigned selector) -{ - struct ab3100_regulator *abreg = rdev_get_drvdata(reg); - u8 regval; - int err; - - err = abx500_get_register_interruptible(abreg->dev, 0, - abreg->regreg, ®val); - if (err) { - dev_warn(®->dev, - "failed to get regulator register %02x\n", - abreg->regreg); - return err; - } - - /* The highest three bits control the variable regulators */ - regval &= ~0xE0; - regval |= (selector << 5); - - err = abx500_set_register_interruptible(abreg->dev, 0, - abreg->regreg, regval); - if (err) - dev_warn(®->dev, "failed to set regulator register %02x\n", - abreg->regreg); - - return err; -} - -static int ab3100_set_suspend_voltage_regulator(struct regulator_dev *reg, - int uV) -{ - struct ab3100_regulator *abreg = rdev_get_drvdata(reg); - u8 regval; - int err; - int bestindex; - u8 targetreg; - - if (abreg->regreg == AB3100_LDO_E) - targetreg = AB3100_LDO_E_SLEEP; - else if (abreg->regreg == AB3100_BUCK) - targetreg = AB3100_BUCK_SLEEP; - else - return -EINVAL; - - /* LDO E and BUCK have special suspend voltages you can set */ - bestindex = regulator_map_voltage_iterate(reg, uV, uV); - - err = abx500_get_register_interruptible(abreg->dev, 0, - targetreg, ®val); - if (err) { - dev_warn(®->dev, - "failed to get regulator register %02x\n", - targetreg); - return err; - } - - /* The highest three bits control the variable regulators */ - regval &= ~0xE0; - regval |= (bestindex << 5); - - err = abx500_set_register_interruptible(abreg->dev, 0, - targetreg, regval); - if (err) - dev_warn(®->dev, "failed to set regulator register %02x\n", - abreg->regreg); - - return err; -} - -/* - * The external regulator can just define a fixed voltage. - */ -static int ab3100_get_voltage_regulator_external(struct regulator_dev *reg) -{ - struct ab3100_regulator *abreg = rdev_get_drvdata(reg); - - if (abreg->plfdata) - return abreg->plfdata->external_voltage; - else - /* TODO: encode external voltage into device tree */ - return 0; -} - -static const struct regulator_ops regulator_ops_fixed = { - .enable = ab3100_enable_regulator, - .disable = ab3100_disable_regulator, - .is_enabled = ab3100_is_enabled_regulator, -}; - -static const struct regulator_ops regulator_ops_variable = { - .enable = ab3100_enable_regulator, - .disable = ab3100_disable_regulator, - .is_enabled = ab3100_is_enabled_regulator, - .get_voltage = ab3100_get_voltage_regulator, - .set_voltage_sel = ab3100_set_voltage_regulator_sel, - .list_voltage = regulator_list_voltage_table, -}; - -static const struct regulator_ops regulator_ops_variable_sleepable = { - .enable = ab3100_enable_regulator, - .disable = ab3100_disable_regulator, - .is_enabled = ab3100_is_enabled_regulator, - .get_voltage = ab3100_get_voltage_regulator, - .set_voltage_sel = ab3100_set_voltage_regulator_sel, - .set_suspend_voltage = ab3100_set_suspend_voltage_regulator, - .list_voltage = regulator_list_voltage_table, -}; - -/* - * LDO EXT is an external regulator so it is really - * not possible to set any voltage locally here, AB3100 - * is an on/off switch plain an simple. The external - * voltage is defined in the board set-up if any. - */ -static const struct regulator_ops regulator_ops_external = { - .enable = ab3100_enable_regulator, - .disable = ab3100_disable_regulator, - .is_enabled = ab3100_is_enabled_regulator, - .get_voltage = ab3100_get_voltage_regulator_external, -}; - -static const struct regulator_desc -ab3100_regulator_desc[AB3100_NUM_REGULATORS] = { - { - .name = "LDO_A", - .id = AB3100_LDO_A, - .ops = ®ulator_ops_fixed, - .n_voltages = 1, - .type = REGULATOR_VOLTAGE, - .owner = THIS_MODULE, - .fixed_uV = LDO_A_VOLTAGE, - .enable_time = 200, - }, - { - .name = "LDO_C", - .id = AB3100_LDO_C, - .ops = ®ulator_ops_fixed, - .n_voltages = 1, - .type = REGULATOR_VOLTAGE, - .owner = THIS_MODULE, - .fixed_uV = LDO_C_VOLTAGE, - .enable_time = 200, - }, - { - .name = "LDO_D", - .id = AB3100_LDO_D, - .ops = ®ulator_ops_fixed, - .n_voltages = 1, - .type = REGULATOR_VOLTAGE, - .owner = THIS_MODULE, - .fixed_uV = LDO_D_VOLTAGE, - .enable_time = 200, - }, - { - .name = "LDO_E", - .id = AB3100_LDO_E, - .ops = ®ulator_ops_variable_sleepable, - .n_voltages = ARRAY_SIZE(ldo_e_buck_typ_voltages), - .volt_table = ldo_e_buck_typ_voltages, - .type = REGULATOR_VOLTAGE, - .owner = THIS_MODULE, - .enable_time = 200, - }, - { - .name = "LDO_F", - .id = AB3100_LDO_F, - .ops = ®ulator_ops_variable, - .n_voltages = ARRAY_SIZE(ldo_f_typ_voltages), - .volt_table = ldo_f_typ_voltages, - .type = REGULATOR_VOLTAGE, - .owner = THIS_MODULE, - .enable_time = 600, - }, - { - .name = "LDO_G", - .id = AB3100_LDO_G, - .ops = ®ulator_ops_variable, - .n_voltages = ARRAY_SIZE(ldo_g_typ_voltages), - .volt_table = ldo_g_typ_voltages, - .type = REGULATOR_VOLTAGE, - .owner = THIS_MODULE, - .enable_time = 400, - }, - { - .name = "LDO_H", - .id = AB3100_LDO_H, - .ops = ®ulator_ops_variable, - .n_voltages = ARRAY_SIZE(ldo_h_typ_voltages), - .volt_table = ldo_h_typ_voltages, - .type = REGULATOR_VOLTAGE, - .owner = THIS_MODULE, - .enable_time = 200, - }, - { - .name = "LDO_K", - .id = AB3100_LDO_K, - .ops = ®ulator_ops_variable, - .n_voltages = ARRAY_SIZE(ldo_k_typ_voltages), - .volt_table = ldo_k_typ_voltages, - .type = REGULATOR_VOLTAGE, - .owner = THIS_MODULE, - .enable_time = 200, - }, - { - .name = "LDO_EXT", - .id = AB3100_LDO_EXT, - .ops = ®ulator_ops_external, - .type = REGULATOR_VOLTAGE, - .owner = THIS_MODULE, - }, - { - .name = "BUCK", - .id = AB3100_BUCK, - .ops = ®ulator_ops_variable_sleepable, - .n_voltages = ARRAY_SIZE(ldo_e_buck_typ_voltages), - .volt_table = ldo_e_buck_typ_voltages, - .type = REGULATOR_VOLTAGE, - .owner = THIS_MODULE, - .enable_time = 1000, - }, -}; - -static int ab3100_regulator_register(struct platform_device *pdev, - struct ab3100_platform_data *plfdata, - struct regulator_init_data *init_data, - struct device_node *np, - unsigned long id) -{ - const struct regulator_desc *desc; - struct ab3100_regulator *reg; - struct regulator_dev *rdev; - struct regulator_config config = { }; - int err, i; - - for (i = 0; i < AB3100_NUM_REGULATORS; i++) { - desc = &ab3100_regulator_desc[i]; - if (desc->id == id) - break; - } - if (desc->id != id) - return -ENODEV; - - /* Same index used for this array */ - reg = &ab3100_regulators[i]; - - /* - * Initialize per-regulator struct. - * Inherit platform data, this comes down from the - * i2c boarddata, from the machine. So if you want to - * see what it looks like for a certain machine, go - * into the machine I2C setup. - */ - reg->dev = &pdev->dev; - if (plfdata) { - reg->plfdata = plfdata; - config.init_data = &plfdata->reg_constraints[i]; - } else if (np) { - config.of_node = np; - config.init_data = init_data; - } - config.dev = &pdev->dev; - config.driver_data = reg; - - rdev = devm_regulator_register(&pdev->dev, desc, &config); - if (IS_ERR(rdev)) { - err = PTR_ERR(rdev); - dev_err(&pdev->dev, - "%s: failed to register regulator %s err %d\n", - __func__, desc->name, - err); - return err; - } - - return 0; -} - -static struct of_regulator_match ab3100_regulator_matches[] = { - { .name = "ab3100_ldo_a", .driver_data = (void *) AB3100_LDO_A, }, - { .name = "ab3100_ldo_c", .driver_data = (void *) AB3100_LDO_C, }, - { .name = "ab3100_ldo_d", .driver_data = (void *) AB3100_LDO_D, }, - { .name = "ab3100_ldo_e", .driver_data = (void *) AB3100_LDO_E, }, - { .name = "ab3100_ldo_f", .driver_data = (void *) AB3100_LDO_F }, - { .name = "ab3100_ldo_g", .driver_data = (void *) AB3100_LDO_G }, - { .name = "ab3100_ldo_h", .driver_data = (void *) AB3100_LDO_H }, - { .name = "ab3100_ldo_k", .driver_data = (void *) AB3100_LDO_K }, - { .name = "ab3100_ext", .driver_data = (void *) AB3100_LDO_EXT }, - { .name = "ab3100_buck", .driver_data = (void *) AB3100_BUCK }, -}; - -/* - * Initial settings of ab3100 registers. - * Common for below LDO regulator settings are that - * bit 7-5 controls voltage. Bit 4 turns regulator ON(1) or OFF(0). - * Bit 3-2 controls sleep enable and bit 1-0 controls sleep mode. - */ -/* LDO_A 0x16: 2.75V, ON, SLEEP_A, SLEEP OFF GND */ -#define LDO_A_SETTING 0x16 -/* LDO_C 0x10: 2.65V, ON, SLEEP_A or B, SLEEP full power */ -#define LDO_C_SETTING 0x10 -/* LDO_D 0x10: 2.65V, ON, sleep mode not used */ -#define LDO_D_SETTING 0x10 -/* LDO_E 0x10: 1.8V, ON, SLEEP_A or B, SLEEP full power */ -#define LDO_E_SETTING 0x10 -/* LDO_E SLEEP 0x00: 1.8V, not used, SLEEP_A or B, not used */ -#define LDO_E_SLEEP_SETTING 0x00 -/* LDO_F 0xD0: 2.5V, ON, SLEEP_A or B, SLEEP full power */ -#define LDO_F_SETTING 0xD0 -/* LDO_G 0x00: 2.85V, OFF, SLEEP_A or B, SLEEP full power */ -#define LDO_G_SETTING 0x00 -/* LDO_H 0x18: 2.75V, ON, SLEEP_B, SLEEP full power */ -#define LDO_H_SETTING 0x18 -/* LDO_K 0x00: 2.75V, OFF, SLEEP_A or B, SLEEP full power */ -#define LDO_K_SETTING 0x00 -/* LDO_EXT 0x00: Voltage not set, OFF, not used, not used */ -#define LDO_EXT_SETTING 0x00 -/* BUCK 0x7D: 1.2V, ON, SLEEP_A and B, SLEEP low power */ -#define BUCK_SETTING 0x7D -/* BUCK SLEEP 0xAC: 1.05V, Not used, SLEEP_A and B, Not used */ -#define BUCK_SLEEP_SETTING 0xAC - -static const u8 ab3100_reg_initvals[] = { - LDO_A_SETTING, - LDO_C_SETTING, - LDO_E_SETTING, - LDO_E_SLEEP_SETTING, - LDO_F_SETTING, - LDO_G_SETTING, - LDO_H_SETTING, - LDO_K_SETTING, - LDO_EXT_SETTING, - BUCK_SETTING, - BUCK_SLEEP_SETTING, - LDO_D_SETTING, -}; - -static int -ab3100_regulator_of_probe(struct platform_device *pdev, struct device_node *np) -{ - int err, i; - - /* - * Set up the regulator registers, as was previously done with - * platform data. - */ - /* Set up regulators */ - for (i = 0; i < ARRAY_SIZE(ab3100_reg_init_order); i++) { - err = abx500_set_register_interruptible(&pdev->dev, 0, - ab3100_reg_init_order[i], - ab3100_reg_initvals[i]); - if (err) { - dev_err(&pdev->dev, "regulator initialization failed with error %d\n", - err); - return err; - } - } - - for (i = 0; i < ARRAY_SIZE(ab3100_regulator_matches); i++) { - err = ab3100_regulator_register( - pdev, NULL, ab3100_regulator_matches[i].init_data, - ab3100_regulator_matches[i].of_node, - (unsigned long)ab3100_regulator_matches[i].driver_data); - if (err) - return err; - } - - return 0; -} - - -static int ab3100_regulators_probe(struct platform_device *pdev) -{ - struct ab3100_platform_data *plfdata = dev_get_platdata(&pdev->dev); - struct device_node *np = pdev->dev.of_node; - int err = 0; - u8 data; - int i; - - /* Check chip state */ - err = abx500_get_register_interruptible(&pdev->dev, 0, - AB3100_LDO_D, &data); - if (err) { - dev_err(&pdev->dev, "could not read initial status of LDO_D\n"); - return err; - } - if (data & 0x10) - dev_notice(&pdev->dev, - "chip is already in active mode (Warm start)\n"); - else - dev_notice(&pdev->dev, - "chip is in inactive mode (Cold start)\n"); - - if (np) { - err = of_regulator_match(&pdev->dev, np, - ab3100_regulator_matches, - ARRAY_SIZE(ab3100_regulator_matches)); - if (err < 0) { - dev_err(&pdev->dev, - "Error parsing regulator init data: %d\n", err); - return err; - } - return ab3100_regulator_of_probe(pdev, np); - } - - /* Set up regulators */ - for (i = 0; i < ARRAY_SIZE(ab3100_reg_init_order); i++) { - err = abx500_set_register_interruptible(&pdev->dev, 0, - ab3100_reg_init_order[i], - plfdata->reg_initvals[i]); - if (err) { - dev_err(&pdev->dev, "regulator initialization failed with error %d\n", - err); - return err; - } - } - - /* Register the regulators */ - for (i = 0; i < AB3100_NUM_REGULATORS; i++) { - const struct regulator_desc *desc = &ab3100_regulator_desc[i]; - - err = ab3100_regulator_register(pdev, plfdata, NULL, NULL, - desc->id); - if (err) - return err; - } - - return 0; -} - -static struct platform_driver ab3100_regulators_driver = { - .driver = { - .name = "ab3100-regulators", - }, - .probe = ab3100_regulators_probe, -}; - -static __init int ab3100_regulators_init(void) -{ - return platform_driver_register(&ab3100_regulators_driver); -} - -static __exit void ab3100_regulators_exit(void) -{ - platform_driver_unregister(&ab3100_regulators_driver); -} - -subsys_initcall(ab3100_regulators_init); -module_exit(ab3100_regulators_exit); - -MODULE_AUTHOR("Mattias Wallin "); -MODULE_DESCRIPTION("AB3100 Regulator driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:ab3100-regulators"); From dea6dd2ba63f8c8532addb8f32daf7b89a368a42 Mon Sep 17 00:00:00 2001 From: Pan Bian Date: Wed, 20 Jan 2021 19:27:56 -0800 Subject: [PATCH 32/49] regulator: s5m8767: Fix reference count leak Call of_node_put() to drop references of regulators_np and reg_np before returning error code. Fixes: 9ae5cc75ceaa ("regulator: s5m8767: Pass descriptor instead of GPIO number") Signed-off-by: Pan Bian Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20210121032756.49501-1-bianpan2016@163.com Signed-off-by: Mark Brown --- drivers/regulator/s5m8767.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/regulator/s5m8767.c b/drivers/regulator/s5m8767.c index 3fa472127e9a..48dd95b3ff45 100644 --- a/drivers/regulator/s5m8767.c +++ b/drivers/regulator/s5m8767.c @@ -573,10 +573,13 @@ static int s5m8767_pmic_dt_parse_pdata(struct platform_device *pdev, "s5m8767,pmic-ext-control", GPIOD_OUT_HIGH | GPIOD_FLAGS_BIT_NONEXCLUSIVE, "s5m8767"); - if (PTR_ERR(rdata->ext_control_gpiod) == -ENOENT) + if (PTR_ERR(rdata->ext_control_gpiod) == -ENOENT) { rdata->ext_control_gpiod = NULL; - else if (IS_ERR(rdata->ext_control_gpiod)) + } else if (IS_ERR(rdata->ext_control_gpiod)) { + of_node_put(reg_np); + of_node_put(regulators_np); return PTR_ERR(rdata->ext_control_gpiod); + } rdata->id = i; rdata->initdata = of_get_regulator_init_data( From 71ca776a8885aff469f2aa45382518513ecce883 Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Wed, 20 Jan 2021 14:49:00 -0800 Subject: [PATCH 33/49] regulator: qcom-rpmh: Add pmc8180 and pmc8180c Add RPMH regulator compatibles for two of the PMIC variants used on the SC8180x platform. Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20210120224901.1611232-1-bjorn.andersson@linaro.org Signed-off-by: Mark Brown --- .../devicetree/bindings/regulator/qcom,rpmh-regulator.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.txt b/Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.txt index 7d462b899473..ce1e04354006 100644 --- a/Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.txt +++ b/Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.txt @@ -50,6 +50,8 @@ First Level Nodes - PMIC "qcom,pm8350-rpmh-regulators" "qcom,pm8350c-rpmh-regulators" "qcom,pm8998-rpmh-regulators" + "qcom,pmc8180-rpmh-regulators" + "qcom,pmc8180c-rpmh-regulators" "qcom,pmi8998-rpmh-regulators" "qcom,pm6150-rpmh-regulators" "qcom,pm6150l-rpmh-regulators" From e46c52f7efa25a1cd72c7a7399af9fddc41f5a8e Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Wed, 20 Jan 2021 14:49:01 -0800 Subject: [PATCH 34/49] regulator: qcom-rpmh: Add pmc8180 and pmc8180c The regulators from PMC8180 and PMC8180C exposed by the RPMH in the Qualcomm SC8180X seems to be the same as PM8150 and PM8150L. Add compatibles for the two new PMICs and reuse the definition of the existing PMICs. Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20210120224901.1611232-2-bjorn.andersson@linaro.org Signed-off-by: Mark Brown --- drivers/regulator/qcom-rpmh-regulator.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/regulator/qcom-rpmh-regulator.c b/drivers/regulator/qcom-rpmh-regulator.c index 98320e1d8bf6..dbad80f904bd 100644 --- a/drivers/regulator/qcom-rpmh-regulator.c +++ b/drivers/regulator/qcom-rpmh-regulator.c @@ -1115,6 +1115,14 @@ static const struct of_device_id __maybe_unused rpmh_regulator_match_table[] = { .compatible = "qcom,pm6150l-rpmh-regulators", .data = pm6150l_vreg_data, }, + { + .compatible = "qcom,pmc8180-rpmh-regulators", + .data = pm8150_vreg_data, + }, + { + .compatible = "qcom,pmc8180c-rpmh-regulators", + .data = pm8150l_vreg_data, + }, { .compatible = "qcom,pmx55-rpmh-regulators", .data = pmx55_vreg_data, From a5872bd3398d0ff2ce4c77794bc7837899c69024 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 21 Jan 2021 16:59:14 +0100 Subject: [PATCH 35/49] regulator: s5m8767: Drop regulators OF node reference The device node reference obtained with of_get_child_by_name() should be dropped on error paths. Fixes: 26aec009f6b6 ("regulator: add device tree support for s5m8767") Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20210121155914.48034-1-krzk@kernel.org Signed-off-by: Mark Brown --- drivers/regulator/s5m8767.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/regulator/s5m8767.c b/drivers/regulator/s5m8767.c index 48dd95b3ff45..7c111bbdc2af 100644 --- a/drivers/regulator/s5m8767.c +++ b/drivers/regulator/s5m8767.c @@ -544,14 +544,18 @@ static int s5m8767_pmic_dt_parse_pdata(struct platform_device *pdev, rdata = devm_kcalloc(&pdev->dev, pdata->num_regulators, sizeof(*rdata), GFP_KERNEL); - if (!rdata) + if (!rdata) { + of_node_put(regulators_np); return -ENOMEM; + } rmode = devm_kcalloc(&pdev->dev, pdata->num_regulators, sizeof(*rmode), GFP_KERNEL); - if (!rmode) + if (!rmode) { + of_node_put(regulators_np); return -ENOMEM; + } pdata->regulators = rdata; pdata->opmode = rmode; From 24be0c715617ed9bfc63fa9483f8bda1214b9763 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Fri, 22 Jan 2021 20:43:11 +0300 Subject: [PATCH 36/49] regulator: Make regulator_sync_voltage() usable by coupled regulators Make regulator_sync_voltage() to re-balance voltage state of a coupled regulators instead of changing the voltage directly. Tested-by: Peter Geis # Ouya T30 Tested-by: Dmitry Osipenko # A500 T20 and Nexus7 T30 Tested-by: Nicolas Chauvet # PAZ00 T20 Tested-by: Matt Merhar # Ouya T30 Signed-off-by: Dmitry Osipenko Link: https://lore.kernel.org/r/20210122174311.28230-1-digetx@gmail.com Signed-off-by: Mark Brown --- drivers/regulator/core.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index fee924158091..1a4745ec0305 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -4131,7 +4131,11 @@ int regulator_sync_voltage(struct regulator *regulator) if (ret < 0) goto out; - ret = _regulator_do_set_voltage(rdev, min_uV, max_uV); + /* balance only, if regulator is coupled */ + if (rdev->coupling_desc.n_coupled > 1) + ret = regulator_balance_voltage(rdev, PM_SUSPEND_ON); + else + ret = _regulator_do_set_voltage(rdev, min_uV, max_uV); out: regulator_unlock(rdev); From 7fefe72b0f8c0d7e63af9810c4dcc88757eddcd5 Mon Sep 17 00:00:00 2001 From: Christoph Fritz Date: Sun, 17 Jan 2021 21:49:17 +0100 Subject: [PATCH 37/49] regulator: pf8x00: Add suspend support This patch adds suspend/resume support so that it is possible to configure the LDOs and BUCKs as on or off during suspend phase as well as to configure suspend specific voltages. Signed-off-by: Christoph Fritz Link: https://lore.kernel.org/r/4c2e79d4fa96befdc9a6c59c3ff27b0a34f9fb56.camel@googlemail.com Signed-off-by: Mark Brown --- drivers/regulator/pf8x00-regulator.c | 75 ++++++++++++++++++++++++++-- 1 file changed, 70 insertions(+), 5 deletions(-) diff --git a/drivers/regulator/pf8x00-regulator.c b/drivers/regulator/pf8x00-regulator.c index 1e5582d73405..02892e04acce 100644 --- a/drivers/regulator/pf8x00-regulator.c +++ b/drivers/regulator/pf8x00-regulator.c @@ -125,8 +125,12 @@ enum pf8x00_devid { #define PF8X00_DEVICE_FAM_MASK GENMASK(7, 4) #define PF8X00_DEVICE_ID_MASK GENMASK(3, 0) -struct pf8x00_regulator { +struct pf8x00_regulator_data { struct regulator_desc desc; + unsigned int suspend_enable_reg; + unsigned int suspend_enable_mask; + unsigned int suspend_voltage_reg; + unsigned int suspend_voltage_cache; }; struct pf8x00_chip { @@ -276,6 +280,53 @@ static int pf8x00_of_parse_cb(struct device_node *np, return 0; } +static int pf8x00_suspend_enable(struct regulator_dev *rdev) +{ + struct pf8x00_regulator_data *regl = rdev_get_drvdata(rdev); + struct regmap *rmap = rdev_get_regmap(rdev); + + return regmap_update_bits(rmap, regl->suspend_enable_reg, + regl->suspend_enable_mask, + regl->suspend_enable_mask); +} + +static int pf8x00_suspend_disable(struct regulator_dev *rdev) +{ + struct pf8x00_regulator_data *regl = rdev_get_drvdata(rdev); + struct regmap *rmap = rdev_get_regmap(rdev); + + return regmap_update_bits(rmap, regl->suspend_enable_reg, + regl->suspend_enable_mask, 0); +} + +static int pf8x00_set_suspend_voltage(struct regulator_dev *rdev, int uV) +{ + struct pf8x00_regulator_data *regl = rdev_get_drvdata(rdev); + int ret; + + if (regl->suspend_voltage_cache == uV) + return 0; + + ret = regulator_map_voltage_iterate(rdev, uV, uV); + if (ret < 0) { + dev_err(rdev_get_dev(rdev), "failed to map %i uV\n", uV); + return ret; + } + + dev_dbg(rdev_get_dev(rdev), "uV: %i, reg: 0x%x, msk: 0x%x, val: 0x%x\n", + uV, regl->suspend_voltage_reg, regl->desc.vsel_mask, ret); + ret = regmap_update_bits(rdev->regmap, regl->suspend_voltage_reg, + regl->desc.vsel_mask, ret); + if (ret < 0) { + dev_err(rdev_get_dev(rdev), "failed to set %i uV\n", uV); + return ret; + } + + regl->suspend_voltage_cache = uV; + + return 0; +} + static const struct regulator_ops pf8x00_ldo_ops = { .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, @@ -283,6 +334,9 @@ static const struct regulator_ops pf8x00_ldo_ops = { .list_voltage = regulator_list_voltage_table, .set_voltage_sel = regulator_set_voltage_sel_regmap, .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_suspend_enable = pf8x00_suspend_enable, + .set_suspend_disable = pf8x00_suspend_disable, + .set_suspend_voltage = pf8x00_set_suspend_voltage, }; @@ -295,6 +349,9 @@ static const struct regulator_ops pf8x00_buck1_6_ops = { .get_voltage_sel = regulator_get_voltage_sel_regmap, .get_current_limit = regulator_get_current_limit_regmap, .set_current_limit = regulator_set_current_limit_regmap, + .set_suspend_enable = pf8x00_suspend_enable, + .set_suspend_disable = pf8x00_suspend_disable, + .set_suspend_voltage = pf8x00_set_suspend_voltage, }; static const struct regulator_ops pf8x00_buck7_ops = { @@ -306,6 +363,8 @@ static const struct regulator_ops pf8x00_buck7_ops = { .get_voltage_sel = regulator_get_voltage_sel_regmap, .get_current_limit = regulator_get_current_limit_regmap, .set_current_limit = regulator_set_current_limit_regmap, + .set_suspend_enable = pf8x00_suspend_enable, + .set_suspend_disable = pf8x00_suspend_disable, }; static const struct regulator_ops pf8x00_vsnvs_ops = { @@ -337,6 +396,9 @@ static const struct regulator_ops pf8x00_vsnvs_ops = { .disable_val = 0x0, \ .enable_mask = 2, \ }, \ + .suspend_enable_reg = (base) + LDO_CONFIG2, \ + .suspend_enable_mask = 1, \ + .suspend_voltage_reg = (base) + LDO_STBY_VOLT, \ } #define PF8X00BUCK(_id, _name, base, voltages) \ @@ -367,6 +429,9 @@ static const struct regulator_ops pf8x00_vsnvs_ops = { .enable_mask = 0x3, \ .enable_time = 500, \ }, \ + .suspend_enable_reg = (base) + SW_MODE1, \ + .suspend_enable_mask = 0xc, \ + .suspend_voltage_reg = (base) + SW_STBY_VOLT, \ } #define PF8X00BUCK7(_name, base, voltages) \ @@ -415,7 +480,7 @@ static const struct regulator_ops pf8x00_vsnvs_ops = { }, \ } -static struct pf8x00_regulator pf8x00_regulators_data[PF8X00_MAX_REGULATORS] = { +static struct pf8x00_regulator_data pf8x00_regs_data[PF8X00_MAX_REGULATORS] = { PF8X00LDO(1, "ldo1", PF8X00_LDO_BASE(PF8X00_LDO1), pf8x00_ldo_voltages), PF8X00LDO(2, "ldo2", PF8X00_LDO_BASE(PF8X00_LDO2), pf8x00_ldo_voltages), PF8X00LDO(3, "ldo3", PF8X00_LDO_BASE(PF8X00_LDO3), pf8x00_ldo_voltages), @@ -500,12 +565,12 @@ static int pf8x00_i2c_probe(struct i2c_client *client) if (ret) return ret; - for (id = 0; id < ARRAY_SIZE(pf8x00_regulators_data); id++) { - struct pf8x00_regulator *data = &pf8x00_regulators_data[id]; + for (id = 0; id < ARRAY_SIZE(pf8x00_regs_data); id++) { + struct pf8x00_regulator_data *data = &pf8x00_regs_data[id]; struct regulator_dev *rdev; config.dev = chip->dev; - config.driver_data = chip; + config.driver_data = data; config.regmap = chip->regmap; rdev = devm_regulator_register(&client->dev, &data->desc, &config); From dbe954d8f1635f949a1d9a5d6e6fb749ae022b47 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 22 Jan 2021 19:32:50 +0100 Subject: [PATCH 38/49] regulator: core: Avoid debugfs: Directory ... already present! error Sometimes regulator_get() gets called twice for the same supply on the same device. This may happen e.g. when a framework / library is used which uses the regulator; and the driver itself also needs to enable the regulator in some cases where the framework will not enable it. Commit ff268b56ce8c ("regulator: core: Don't spew backtraces on duplicate sysfs") already takes care of the backtrace which would trigger when creating a duplicate consumer symlink under /sys/class/regulator/regulator.%d in this scenario. Commit c33d442328f5 ("debugfs: make error message a bit more verbose") causes a new error to get logged in this scenario: [ 26.938425] debugfs: Directory 'wm5102-codec-MICVDD' with parent 'spi-WM510204:00-MICVDD' already present! There is no _nowarn variant of debugfs_create_dir(), but we can detect and avoid this problem by checking the return value of the earlier sysfs_create_link_nowarn() call. Add a check for the earlier sysfs_create_link_nowarn() failing with -EEXIST and skip the debugfs_create_dir() call in that case, avoiding this error getting logged. Fixes: c33d442328f5 ("debugfs: make error message a bit more verbose") Cc: Charles Keepax Signed-off-by: Hans de Goede Reviewed-by: Charles Keepax Link: https://lore.kernel.org/r/20210122183250.370571-1-hdegoede@redhat.com Signed-off-by: Mark Brown --- drivers/regulator/core.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 1a4745ec0305..8f35a3dd4c30 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -1617,7 +1617,7 @@ static struct regulator *create_regulator(struct regulator_dev *rdev, const char *supply_name) { struct regulator *regulator; - int err; + int err = 0; if (dev) { char buf[REG_STR_SIZE]; @@ -1663,8 +1663,8 @@ static struct regulator *create_regulator(struct regulator_dev *rdev, } } - regulator->debugfs = debugfs_create_dir(supply_name, - rdev->debugfs); + if (err != -EEXIST) + regulator->debugfs = debugfs_create_dir(supply_name, rdev->debugfs); if (!regulator->debugfs) { rdev_dbg(rdev, "Failed to create debugfs directory\n"); } else { From 4288b4ccda966c2a49ec7c67100208378bdb34d2 Mon Sep 17 00:00:00 2001 From: Christoph Fritz Date: Mon, 25 Jan 2021 17:13:41 +0100 Subject: [PATCH 39/49] regulator: pf8x00: set ramp_delay for bucks This patch sets ramp_delay for bucks to the max value given by the datasheet. Signed-off-by: Christoph Fritz Reviewed-by: Adrien Grassein Link: https://lore.kernel.org/r/60d8eb8feefd26380cc9c6503f835e569be90465.camel@googlemail.com Signed-off-by: Mark Brown --- drivers/regulator/pf8x00-regulator.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/regulator/pf8x00-regulator.c b/drivers/regulator/pf8x00-regulator.c index 02892e04acce..827da25466cc 100644 --- a/drivers/regulator/pf8x00-regulator.c +++ b/drivers/regulator/pf8x00-regulator.c @@ -413,6 +413,7 @@ static const struct regulator_ops pf8x00_vsnvs_ops = { .type = REGULATOR_VOLTAGE, \ .id = PF8X00_BUCK ## _id, \ .owner = THIS_MODULE, \ + .ramp_delay = 19000, \ .linear_ranges = pf8x00_sw1_to_6_voltages, \ .n_linear_ranges = \ ARRAY_SIZE(pf8x00_sw1_to_6_voltages), \ @@ -446,6 +447,7 @@ static const struct regulator_ops pf8x00_vsnvs_ops = { .type = REGULATOR_VOLTAGE, \ .id = PF8X00_BUCK7, \ .owner = THIS_MODULE, \ + .ramp_delay = 19000, \ .volt_table = voltages, \ .vsel_reg = (base) + SW_RUN_VOLT, \ .vsel_mask = 0xff, \ From 64f09ea1b551189f491ffb626fdccc2c31fe2d70 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Thu, 28 Jan 2021 20:01:51 +0800 Subject: [PATCH 40/49] regulator: pf8x00: Fix typo for PF8200 chip name Trivial typo fix. Signed-off-by: Axel Lin Reviewed-by: Adrien Grassein Link: https://lore.kernel.org/r/20210128120151.554411-1-axel.lin@ingics.com Signed-off-by: Mark Brown --- drivers/regulator/pf8x00-regulator.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/regulator/pf8x00-regulator.c b/drivers/regulator/pf8x00-regulator.c index af9918cd27aa..e1dba70fb78f 100644 --- a/drivers/regulator/pf8x00-regulator.c +++ b/drivers/regulator/pf8x00-regulator.c @@ -399,7 +399,7 @@ static int pf8x00_identify(struct pf8x00_chip *chip) name = "PF8121A"; break; case PF8200: - name = "PF8100"; + name = "PF8200"; break; default: dev_err(chip->dev, "Unknown pf8x00 device id 0x%x\n", dev_id); From 337710b3121a4f4183c38ff056f6f9ef516cc34f Mon Sep 17 00:00:00 2001 From: Matti Vaittinen Date: Tue, 2 Feb 2021 09:37:58 +0200 Subject: [PATCH 41/49] regulator: qcom-labibb: Use disable_irq_nosync from isr Calling the disable_irq() from irq handler might be a bad idea as disable_irq() should wait for handlers to run. I don't see why this wouldn't deadlock in wait_event waiting for the threaded handler to complete. Use disable_irq_nosync() instead. Fixes: 390af53e04114 ("regulator: qcom-labibb: Implement short-circuit and over-current IRQs") Signed-off-by: Matti Vaittinen Reviewed-by: AngeloGioacchino Del Regno Link: https://lore.kernel.org/r/f2c4c88d90bf7473e1b84b8a99b7b33d7a081764.1612249657.git.matti.vaittinen@fi.rohmeurope.com Signed-off-by: Mark Brown --- drivers/regulator/qcom-labibb-regulator.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/regulator/qcom-labibb-regulator.c b/drivers/regulator/qcom-labibb-regulator.c index dbb4511c3c6d..de25e3279b4b 100644 --- a/drivers/regulator/qcom-labibb-regulator.c +++ b/drivers/regulator/qcom-labibb-regulator.c @@ -283,7 +283,7 @@ static irqreturn_t qcom_labibb_ocp_isr(int irq, void *chip) * Disable the interrupt temporarily, or it will fire continuously; * we will re-enable it in the recovery worker function. */ - disable_irq(irq); + disable_irq_nosync(irq); /* Warn the user for overcurrent */ dev_warn(vreg->dev, "Over-Current interrupt fired!\n"); @@ -536,7 +536,7 @@ static irqreturn_t qcom_labibb_sc_isr(int irq, void *chip) * Disable the interrupt temporarily, or it will fire continuously; * we will re-enable it in the recovery worker function. */ - disable_irq(irq); + disable_irq_nosync(irq); /* Signal out of regulation event to drivers */ regulator_notifier_call_chain(vreg->rdev, From 41a8a027f4d3f81d83b8942ef29f84223ca35ffc Mon Sep 17 00:00:00 2001 From: Timon Baetz Date: Sat, 30 Jan 2021 17:29:17 +0000 Subject: [PATCH 42/49] regulator: dt-bindings: Document charger-supply for max8997 Add charger-supply optional property to enable charging control. Signed-off-by: Timon Baetz Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20210130172747.2022977-2-timon.baetz@protonmail.com Signed-off-by: Mark Brown --- .../devicetree/bindings/regulator/max8997-regulator.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/regulator/max8997-regulator.txt b/Documentation/devicetree/bindings/regulator/max8997-regulator.txt index 6fe825b8ac1b..b53c5e2b335f 100644 --- a/Documentation/devicetree/bindings/regulator/max8997-regulator.txt +++ b/Documentation/devicetree/bindings/regulator/max8997-regulator.txt @@ -35,6 +35,7 @@ Optional properties: - interrupts: Interrupt specifiers for two interrupt sources. - First interrupt specifier is for 'irq1' interrupt. - Second interrupt specifier is for 'alert' interrupt. +- charger-supply: regulator node for charging current. - max8997,pmic-buck1-uses-gpio-dvs: 'buck1' can be controlled by gpio dvs. - max8997,pmic-buck2-uses-gpio-dvs: 'buck2' can be controlled by gpio dvs. - max8997,pmic-buck5-uses-gpio-dvs: 'buck5' can be controlled by gpio dvs. From 977fb5b58469c1367aa075e7e913c03cba7d466f Mon Sep 17 00:00:00 2001 From: Hsin-Hsiung Wang Date: Sun, 7 Feb 2021 14:14:15 +0800 Subject: [PATCH 43/49] regulator: document binding for MT6315 regulator Add device tree binding information for MT6315 regulator driver. Example bindings for MT6315 are added. Signed-off-by: Hsin-Hsiung Wang Link: https://lore.kernel.org/r/1612678457-11548-2-git-send-email-hsin-hsiung.wang@mediatek.com Signed-off-by: Mark Brown --- .../bindings/regulator/mt6315-regulator.yaml | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 Documentation/devicetree/bindings/regulator/mt6315-regulator.yaml diff --git a/Documentation/devicetree/bindings/regulator/mt6315-regulator.yaml b/Documentation/devicetree/bindings/regulator/mt6315-regulator.yaml new file mode 100644 index 000000000000..61dd5af80db6 --- /dev/null +++ b/Documentation/devicetree/bindings/regulator/mt6315-regulator.yaml @@ -0,0 +1,69 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/regulator/mt6315-regulator.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Mediatek MT6315 Regulator + +maintainers: + - Hsin-Hsiung Wang + +description: | + The MT6315 is a power management IC (PMIC) configurable with SPMI. + that contains 4 BUCKs output which can combine with each other + by different efuse settings. + +properties: + compatible: + const: mediatek,mt6315-regulator + + reg: + maxItems: 1 + + regulators: + type: object + description: List of regulators and its properties + + patternProperties: + "^vbuck[1-4]$": + type: object + $ref: "regulator.yaml#" + + properties: + regulator-name: + pattern: "^vbuck[1-4]$" + + additionalProperties: false + +required: + - compatible + - reg + - regulators + +additionalProperties: false + +examples: + - | + pmic@6 { + compatible = "mediatek,mt6315-regulator"; + reg = <0x6 0>; + + regulators { + vbuck1 { + regulator-compatible = "vbuck1"; + regulator-min-microvolt = <300000>; + regulator-max-microvolt = <1193750>; + regulator-enable-ramp-delay = <256>; + regulator-allowed-modes = <0 1 2 4>; + }; + + vbuck3 { + regulator-compatible = "vbuck3"; + regulator-min-microvolt = <300000>; + regulator-max-microvolt = <1193750>; + regulator-enable-ramp-delay = <256>; + regulator-allowed-modes = <0 1 2 4>; + }; + }; + }; From 7aa382cfe714f61b0c29f02c31d389c506b4e2ae Mon Sep 17 00:00:00 2001 From: Hsin-Hsiung Wang Date: Sun, 7 Feb 2021 14:14:16 +0800 Subject: [PATCH 44/49] regulator: mt6315: Add support for MT6315 regulator The MT6315 is a regulator found on boards based on MediaTek MT8192 and probably other SoCs. It connects as a slave to SoC using SPMI. Signed-off-by: Hsin-Hsiung Wang Link: https://lore.kernel.org/r/1612678457-11548-3-git-send-email-hsin-hsiung.wang@mediatek.com Signed-off-by: Mark Brown --- drivers/regulator/Kconfig | 10 + drivers/regulator/Makefile | 1 + drivers/regulator/mt6315-regulator.c | 299 +++++++++++++++++++++ include/linux/regulator/mt6315-regulator.h | 44 +++ 4 files changed, 354 insertions(+) create mode 100644 drivers/regulator/mt6315-regulator.c create mode 100644 include/linux/regulator/mt6315-regulator.h diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 88316b8a65d2..77c43134bc9e 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -731,6 +731,16 @@ config REGULATOR_MT6311 This driver supports the control of different power rails of device through regulator interface. +config REGULATOR_MT6315 + tristate "MediaTek MT6315 PMIC" + depends on SPMI + select REGMAP_SPMI + help + Say y here to select this option to enable the power regulator of + MediaTek MT6315 PMIC. + This driver supports the control of different power rails of device + through regulator interface. + config REGULATOR_MT6323 tristate "MediaTek MT6323 PMIC" depends on MFD_MT6397 diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index d1957629537b..44d2f8bf4b74 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -89,6 +89,7 @@ obj-$(CONFIG_REGULATOR_MP8859) += mp8859.o obj-$(CONFIG_REGULATOR_MP886X) += mp886x.o obj-$(CONFIG_REGULATOR_MPQ7920) += mpq7920.o obj-$(CONFIG_REGULATOR_MT6311) += mt6311-regulator.o +obj-$(CONFIG_REGULATOR_MT6315) += mt6315-regulator.o obj-$(CONFIG_REGULATOR_MT6323) += mt6323-regulator.o obj-$(CONFIG_REGULATOR_MT6358) += mt6358-regulator.o obj-$(CONFIG_REGULATOR_MT6360) += mt6360-regulator.o diff --git a/drivers/regulator/mt6315-regulator.c b/drivers/regulator/mt6315-regulator.c new file mode 100644 index 000000000000..d49a1534d8e9 --- /dev/null +++ b/drivers/regulator/mt6315-regulator.c @@ -0,0 +1,299 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright (c) 2021 MediaTek Inc. + +#include +#include +#include +#include +#include +#include +#include +#include + +#define MT6315_BUCK_MODE_AUTO 0 +#define MT6315_BUCK_MODE_FORCE_PWM 1 +#define MT6315_BUCK_MODE_LP 2 + +struct mt6315_regulator_info { + struct regulator_desc desc; + u32 status_reg; + u32 lp_mode_mask; + u32 lp_mode_shift; +}; + +struct mt_regulator_init_data { + u32 modeset_mask[MT6315_VBUCK_MAX]; +}; + +struct mt6315_chip { + struct device *dev; + struct regmap *regmap; +}; + +#define MT_BUCK(_name, _bid, _vsel) \ +[_bid] = { \ + .desc = { \ + .name = _name, \ + .of_match = of_match_ptr(_name), \ + .regulators_node = "regulators", \ + .ops = &mt6315_volt_range_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = _bid, \ + .owner = THIS_MODULE, \ + .n_voltages = 0xbf, \ + .linear_ranges = mt_volt_range1, \ + .n_linear_ranges = ARRAY_SIZE(mt_volt_range1), \ + .vsel_reg = _vsel, \ + .vsel_mask = 0xff, \ + .enable_reg = MT6315_BUCK_TOP_CON0, \ + .enable_mask = BIT(_bid), \ + .of_map_mode = mt6315_map_mode, \ + }, \ + .status_reg = _bid##_DBG4, \ + .lp_mode_mask = BIT(_bid), \ + .lp_mode_shift = _bid, \ +} + +static const struct linear_range mt_volt_range1[] = { + REGULATOR_LINEAR_RANGE(0, 0, 0xbf, 6250), +}; + +static unsigned int mt6315_map_mode(u32 mode) +{ + switch (mode) { + case MT6315_BUCK_MODE_AUTO: + return REGULATOR_MODE_NORMAL; + case MT6315_BUCK_MODE_FORCE_PWM: + return REGULATOR_MODE_FAST; + case MT6315_BUCK_MODE_LP: + return REGULATOR_MODE_IDLE; + default: + return -EINVAL; + } +} + +static unsigned int mt6315_regulator_get_mode(struct regulator_dev *rdev) +{ + struct mt_regulator_init_data *init = rdev_get_drvdata(rdev); + const struct mt6315_regulator_info *info; + int ret, regval; + u32 modeset_mask; + + info = container_of(rdev->desc, struct mt6315_regulator_info, desc); + modeset_mask = init->modeset_mask[rdev_get_id(rdev)]; + ret = regmap_read(rdev->regmap, MT6315_BUCK_TOP_4PHASE_ANA_CON42, ®val); + if (ret != 0) { + dev_notice(&rdev->dev, "Failed to get mode: %d\n", ret); + return ret; + } + + if ((regval & modeset_mask) == modeset_mask) + return REGULATOR_MODE_FAST; + + ret = regmap_read(rdev->regmap, MT6315_BUCK_TOP_CON1, ®val); + if (ret != 0) { + dev_notice(&rdev->dev, "Failed to get lp mode: %d\n", ret); + return ret; + } + + if (regval & info->lp_mode_mask) + return REGULATOR_MODE_IDLE; + else + return REGULATOR_MODE_NORMAL; +} + +static int mt6315_regulator_set_mode(struct regulator_dev *rdev, + u32 mode) +{ + struct mt_regulator_init_data *init = rdev_get_drvdata(rdev); + const struct mt6315_regulator_info *info; + int ret, val, curr_mode; + u32 modeset_mask; + + info = container_of(rdev->desc, struct mt6315_regulator_info, desc); + modeset_mask = init->modeset_mask[rdev_get_id(rdev)]; + curr_mode = mt6315_regulator_get_mode(rdev); + switch (mode) { + case REGULATOR_MODE_FAST: + ret = regmap_update_bits(rdev->regmap, + MT6315_BUCK_TOP_4PHASE_ANA_CON42, + modeset_mask, + modeset_mask); + break; + case REGULATOR_MODE_NORMAL: + if (curr_mode == REGULATOR_MODE_FAST) { + ret = regmap_update_bits(rdev->regmap, + MT6315_BUCK_TOP_4PHASE_ANA_CON42, + modeset_mask, + 0); + } else if (curr_mode == REGULATOR_MODE_IDLE) { + ret = regmap_update_bits(rdev->regmap, + MT6315_BUCK_TOP_CON1, + info->lp_mode_mask, + 0); + usleep_range(100, 110); + } else { + ret = -EINVAL; + } + break; + case REGULATOR_MODE_IDLE: + val = MT6315_BUCK_MODE_LP >> 1; + val <<= info->lp_mode_shift; + ret = regmap_update_bits(rdev->regmap, + MT6315_BUCK_TOP_CON1, + info->lp_mode_mask, + val); + break; + default: + ret = -EINVAL; + dev_notice(&rdev->dev, "Unsupported mode: %d\n", mode); + break; + } + + if (ret != 0) { + dev_notice(&rdev->dev, "Failed to set mode: %d\n", ret); + return ret; + } + + return 0; +} + +static int mt6315_get_status(struct regulator_dev *rdev) +{ + const struct mt6315_regulator_info *info; + int ret; + u32 regval; + + info = container_of(rdev->desc, struct mt6315_regulator_info, desc); + ret = regmap_read(rdev->regmap, info->status_reg, ®val); + if (ret < 0) { + dev_notice(&rdev->dev, "Failed to get enable reg: %d\n", ret); + return ret; + } + + return (regval & BIT(0)) ? REGULATOR_STATUS_ON : REGULATOR_STATUS_OFF; +} + +static const struct regulator_ops mt6315_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, + .set_voltage_time_sel = regulator_set_voltage_time_sel, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .get_status = mt6315_get_status, + .set_mode = mt6315_regulator_set_mode, + .get_mode = mt6315_regulator_get_mode, +}; + +static const struct mt6315_regulator_info mt6315_regulators[MT6315_VBUCK_MAX] = { + MT_BUCK("vbuck1", MT6315_VBUCK1, MT6315_BUCK_TOP_ELR0), + MT_BUCK("vbuck2", MT6315_VBUCK2, MT6315_BUCK_TOP_ELR2), + MT_BUCK("vbuck3", MT6315_VBUCK3, MT6315_BUCK_TOP_ELR4), + MT_BUCK("vbuck4", MT6315_VBUCK4, MT6315_BUCK_TOP_ELR6), +}; + +static const struct regmap_config mt6315_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .max_register = 0x16d0, + .fast_io = true, +}; + +static const struct of_device_id mt6315_of_match[] = { + { + .compatible = "mediatek,mt6315-regulator", + }, { + /* sentinel */ + }, +}; +MODULE_DEVICE_TABLE(of, mt6315_of_match); + +static int mt6315_regulator_probe(struct spmi_device *pdev) +{ + struct device *dev = &pdev->dev; + struct regmap *regmap; + struct mt6315_chip *chip; + struct mt_regulator_init_data *init_data; + struct regulator_config config = {}; + struct regulator_dev *rdev; + int i; + + regmap = devm_regmap_init_spmi_ext(pdev, &mt6315_regmap_config); + if (!regmap) + return -ENODEV; + + chip = devm_kzalloc(dev, sizeof(struct mt6315_chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + init_data = devm_kzalloc(dev, sizeof(struct mt_regulator_init_data), GFP_KERNEL); + if (!init_data) + return -ENOMEM; + + switch (pdev->usid) { + case MT6315_PP: + init_data->modeset_mask[MT6315_VBUCK1] = BIT(MT6315_VBUCK1) | BIT(MT6315_VBUCK2) | + BIT(MT6315_VBUCK4); + break; + case MT6315_SP: + case MT6315_RP: + init_data->modeset_mask[MT6315_VBUCK1] = BIT(MT6315_VBUCK1) | BIT(MT6315_VBUCK2); + break; + default: + init_data->modeset_mask[MT6315_VBUCK1] = BIT(MT6315_VBUCK1); + break; + } + for (i = MT6315_VBUCK2; i < MT6315_VBUCK_MAX; i++) + init_data->modeset_mask[i] = BIT(i); + + chip->dev = dev; + chip->regmap = regmap; + dev_set_drvdata(dev, chip); + + config.dev = dev; + config.regmap = regmap; + for (i = MT6315_VBUCK1; i < MT6315_VBUCK_MAX; i++) { + config.driver_data = init_data; + rdev = devm_regulator_register(dev, &mt6315_regulators[i].desc, &config); + if (IS_ERR(rdev)) { + dev_notice(dev, "Failed to register %s\n", mt6315_regulators[i].desc.name); + continue; + } + } + + return 0; +} + +static void mt6315_regulator_shutdown(struct spmi_device *pdev) +{ + struct mt6315_chip *chip = dev_get_drvdata(&pdev->dev); + int ret = 0; + + ret |= regmap_write(chip->regmap, MT6315_TOP_TMA_KEY_H, PROTECTION_KEY_H); + ret |= regmap_write(chip->regmap, MT6315_TOP_TMA_KEY, PROTECTION_KEY); + ret |= regmap_update_bits(chip->regmap, MT6315_TOP2_ELR7, 1, 1); + ret |= regmap_write(chip->regmap, MT6315_TOP_TMA_KEY, 0); + ret |= regmap_write(chip->regmap, MT6315_TOP_TMA_KEY_H, 0); + if (ret < 0) + dev_notice(&pdev->dev, "[%#x] Failed to enable power off sequence. %d\n", + pdev->usid, ret); +} + +static struct spmi_driver mt6315_regulator_driver = { + .driver = { + .name = "mt6315-regulator", + .of_match_table = mt6315_of_match, + }, + .probe = mt6315_regulator_probe, + .shutdown = mt6315_regulator_shutdown, +}; + +module_spmi_driver(mt6315_regulator_driver); + +MODULE_AUTHOR("Hsin-Hsiung Wang "); +MODULE_DESCRIPTION("Regulator Driver for MediaTek MT6315 PMIC"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/regulator/mt6315-regulator.h b/include/linux/regulator/mt6315-regulator.h new file mode 100644 index 000000000000..3b80d3f3910c --- /dev/null +++ b/include/linux/regulator/mt6315-regulator.h @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2021 MediaTek Inc. + */ + +#ifndef __LINUX_REGULATOR_MT6315_H +#define __LINUX_REGULATOR_MT6315_H + +#define MT6315_RP 3 +#define MT6315_PP 6 +#define MT6315_SP 7 + +enum { + MT6315_VBUCK1 = 0, + MT6315_VBUCK2, + MT6315_VBUCK3, + MT6315_VBUCK4, + MT6315_VBUCK_MAX, +}; + +/* Register */ +#define MT6315_TOP2_ELR7 0x139 +#define MT6315_TOP_TMA_KEY 0x39F +#define MT6315_TOP_TMA_KEY_H 0x3A0 +#define MT6315_BUCK_TOP_CON0 0x1440 +#define MT6315_BUCK_TOP_CON1 0x1443 +#define MT6315_BUCK_TOP_ELR0 0x1449 +#define MT6315_BUCK_TOP_ELR2 0x144B +#define MT6315_BUCK_TOP_ELR4 0x144D +#define MT6315_BUCK_TOP_ELR6 0x144F +#define MT6315_VBUCK1_DBG0 0x1499 +#define MT6315_VBUCK1_DBG4 0x149D +#define MT6315_VBUCK2_DBG0 0x1519 +#define MT6315_VBUCK2_DBG4 0x151D +#define MT6315_VBUCK3_DBG0 0x1599 +#define MT6315_VBUCK3_DBG4 0x159D +#define MT6315_VBUCK4_DBG0 0x1619 +#define MT6315_VBUCK4_DBG4 0x161D +#define MT6315_BUCK_TOP_4PHASE_ANA_CON42 0x16B1 + +#define PROTECTION_KEY_H 0x9C +#define PROTECTION_KEY 0xEA + +#endif /* __LINUX_REGULATOR_MT6315_H */ From 20ccc362c3d20da734af896e075b74222589f2c0 Mon Sep 17 00:00:00 2001 From: Jonathan Marek Date: Wed, 10 Feb 2021 22:49:35 -0500 Subject: [PATCH 45/49] regulator: qcom-rpmh: fix pm8009 ldo7 Use the correct name to avoid ldo7 commands being sent to ldo6's address. Fixes: 06369bcc15a1 ("regulator: qcom-rpmh: Add support for SM8150") Signed-off-by: Jonathan Marek Reviewed-by: Vinod Koul Link: https://lore.kernel.org/r/20210211034935.5622-1-jonathan@marek.ca Signed-off-by: Mark Brown --- drivers/regulator/qcom-rpmh-regulator.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/regulator/qcom-rpmh-regulator.c b/drivers/regulator/qcom-rpmh-regulator.c index c395a8dda6f7..38dae4813f3b 100644 --- a/drivers/regulator/qcom-rpmh-regulator.c +++ b/drivers/regulator/qcom-rpmh-regulator.c @@ -928,7 +928,7 @@ static const struct rpmh_vreg_init_data pm8009_vreg_data[] = { RPMH_VREG("ldo4", "ldo%s4", &pmic5_nldo, "vdd-l4"), RPMH_VREG("ldo5", "ldo%s5", &pmic5_pldo, "vdd-l5-l6"), RPMH_VREG("ldo6", "ldo%s6", &pmic5_pldo, "vdd-l5-l6"), - RPMH_VREG("ldo7", "ldo%s6", &pmic5_pldo_lv, "vdd-l7"), + RPMH_VREG("ldo7", "ldo%s7", &pmic5_pldo_lv, "vdd-l7"), {}, }; From 8c67a11bae889f51fe5054364c3c789dfae3ad73 Mon Sep 17 00:00:00 2001 From: Frieder Schrempf Date: Thu, 11 Feb 2021 11:55:28 +0100 Subject: [PATCH 46/49] regulator: pca9450: Add SD_VSEL GPIO for LDO5 LDO5 has two separate control registers. LDO5CTRL_L is used if the input signal SD_VSEL is low and LDO5CTRL_H if it is high. The current driver implementation only uses LDO5CTRL_H. To make this work on boards that have SD_VSEL connected to a GPIO, we add support for specifying an optional GPIO and setting it to high at probe time. In the future we might also want to add support for boards that have SD_VSEL set to a fixed low level. In this case we need to change the driver to be able to use the LDO5CTRL_L register. Signed-off-by: Frieder Schrempf Link: https://lore.kernel.org/r/20210211105534.38972-1-frieder.schrempf@kontron.de Signed-off-by: Mark Brown --- drivers/regulator/pca9450-regulator.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/regulator/pca9450-regulator.c b/drivers/regulator/pca9450-regulator.c index cb29421d745a..1bba8fdcb7b7 100644 --- a/drivers/regulator/pca9450-regulator.c +++ b/drivers/regulator/pca9450-regulator.c @@ -5,6 +5,7 @@ */ #include +#include #include #include #include @@ -32,6 +33,7 @@ struct pca9450_regulator_desc { struct pca9450 { struct device *dev; struct regmap *regmap; + struct gpio_desc *sd_vsel_gpio; enum pca9450_chip_type type; unsigned int rcnt; int irq; @@ -795,6 +797,18 @@ static int pca9450_i2c_probe(struct i2c_client *i2c, return ret; } + /* + * The driver uses the LDO5CTRL_H register to control the LDO5 regulator. + * This is only valid if the SD_VSEL input of the PMIC is high. Let's + * check if the pin is available as GPIO and set it to high. + */ + pca9450->sd_vsel_gpio = gpiod_get_optional(pca9450->dev, "sd-vsel", GPIOD_OUT_HIGH); + + if (IS_ERR(pca9450->sd_vsel_gpio)) { + dev_err(&i2c->dev, "Failed to get SD_VSEL GPIO\n"); + return ret; + } + dev_info(&i2c->dev, "%s probed.\n", type == PCA9450_TYPE_PCA9450A ? "pca9450a" : "pca9450bc"); From f7684f5a048febd2a7bc98ee81d6dce52f7268b8 Mon Sep 17 00:00:00 2001 From: Frieder Schrempf Date: Thu, 11 Feb 2021 11:55:30 +0100 Subject: [PATCH 47/49] regulator: pca9450: Enable system reset on WDOG_B assertion By default the PCA9450 doesn't handle the assertion of the WDOG_B signal, but this is required to guarantee that things like software resets triggered by the watchdog work reliably. As we don't want to rely on the bootloader to enable this, we tell the PMIC to issue a cold reset in case the WDOG_B signal is asserted (WDOG_B_CFG = 10), just as the NXP U-Boot code does. Signed-off-by: Frieder Schrempf Link: https://lore.kernel.org/r/20210211105534.38972-3-frieder.schrempf@kontron.de Signed-off-by: Mark Brown --- drivers/regulator/pca9450-regulator.c | 8 ++++++++ include/linux/regulator/pca9450.h | 7 +++++++ 2 files changed, 15 insertions(+) diff --git a/drivers/regulator/pca9450-regulator.c b/drivers/regulator/pca9450-regulator.c index 1bba8fdcb7b7..833d398c6aa2 100644 --- a/drivers/regulator/pca9450-regulator.c +++ b/drivers/regulator/pca9450-regulator.c @@ -797,6 +797,14 @@ static int pca9450_i2c_probe(struct i2c_client *i2c, return ret; } + /* Set reset behavior on assertion of WDOG_B signal */ + ret = regmap_update_bits(pca9450->regmap, PCA9450_REG_RESET_CTRL, + WDOG_B_CFG_MASK, WDOG_B_CFG_COLD_LDO12); + if (ret) { + dev_err(&i2c->dev, "Failed to set WDOG_B reset behavior\n"); + return ret; + } + /* * The driver uses the LDO5CTRL_H register to control the LDO5 regulator. * This is only valid if the SD_VSEL input of the PMIC is high. Let's diff --git a/include/linux/regulator/pca9450.h b/include/linux/regulator/pca9450.h index 1bbd3014f906..ccdb5320a240 100644 --- a/include/linux/regulator/pca9450.h +++ b/include/linux/regulator/pca9450.h @@ -216,4 +216,11 @@ enum { #define IRQ_THERM_105 0x02 #define IRQ_THERM_125 0x01 +/* PCA9450_REG_RESET_CTRL bits */ +#define WDOG_B_CFG_MASK 0xC0 +#define WDOG_B_CFG_NONE 0x00 +#define WDOG_B_CFG_WARM 0x40 +#define WDOG_B_CFG_COLD_LDO12 0x80 +#define WDOG_B_CFG_COLD 0xC0 + #endif /* __LINUX_REG_PCA9450_H__ */ From 27866e3e8a7e93494f8374f48061aa73ee46ceb2 Mon Sep 17 00:00:00 2001 From: Frieder Schrempf Date: Thu, 11 Feb 2021 11:55:29 +0100 Subject: [PATCH 48/49] regulator: pca9450: Add sd-vsel GPIO Add the binding documentation for the optional sd-vsel GPIO. Signed-off-by: Frieder Schrempf Link: https://lore.kernel.org/r/20210211105534.38972-2-frieder.schrempf@kontron.de Signed-off-by: Mark Brown --- .../devicetree/bindings/regulator/nxp,pca9450-regulator.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Documentation/devicetree/bindings/regulator/nxp,pca9450-regulator.yaml b/Documentation/devicetree/bindings/regulator/nxp,pca9450-regulator.yaml index c2b0a8b6da1e..f70f2e758a00 100644 --- a/Documentation/devicetree/bindings/regulator/nxp,pca9450-regulator.yaml +++ b/Documentation/devicetree/bindings/regulator/nxp,pca9450-regulator.yaml @@ -87,6 +87,11 @@ properties: additionalProperties: false + sd-vsel-gpios: + description: GPIO that is used to switch LDO5 between being configured by + LDO5CTRL_L or LDO5CTRL_H register. Use this if the SD_VSEL signal is + connected to a host GPIO. + required: - compatible - reg From c294554111a835598b557db789d9ad2379b512a2 Mon Sep 17 00:00:00 2001 From: Matti Vaittinen Date: Fri, 12 Feb 2021 10:00:23 +0200 Subject: [PATCH 49/49] regulator: bd718x7, bd71828, Fix dvs voltage levels The ROHM BD718x7 and BD71828 drivers support setting HW state specific voltages from device-tree. This is used also by various in-tree DTS files. These drivers do incorrectly try to compose bit-map using enum values. By a chance this works for first two valid levels having values 1 and 2 - but setting values for the rest of the levels do indicate capability of setting values for first levels as well. Luckily the regulators which support setting values for SUSPEND/LPSR do usually also support setting values for RUN and IDLE too - thus this has not been such a fatal issue. Fix this by defining the old enum values as bits and fixing the parsing code. This allows keeping existing IC specific drivers intact and only slightly changing the rohm-regulator.c Fixes: 21b72156ede8b ("regulator: bd718x7: Split driver to common and bd718x7 specific parts") Signed-off-by: Matti Vaittinen Acked-by: Lee Jones Link: https://lore.kernel.org/r/20210212080023.GA880728@localhost.localdomain Signed-off-by: Mark Brown --- drivers/regulator/rohm-regulator.c | 9 ++++++--- include/linux/mfd/rohm-generic.h | 14 ++++++-------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/drivers/regulator/rohm-regulator.c b/drivers/regulator/rohm-regulator.c index 399002383b28..5c558b153d55 100644 --- a/drivers/regulator/rohm-regulator.c +++ b/drivers/regulator/rohm-regulator.c @@ -52,9 +52,12 @@ int rohm_regulator_set_dvs_levels(const struct rohm_dvs_config *dvs, char *prop; unsigned int reg, mask, omask, oreg = desc->enable_reg; - for (i = 0; i < ROHM_DVS_LEVEL_MAX && !ret; i++) { - if (dvs->level_map & (1 << i)) { - switch (i + 1) { + for (i = 0; i < ROHM_DVS_LEVEL_VALID_AMOUNT && !ret; i++) { + int bit; + + bit = BIT(i); + if (dvs->level_map & bit) { + switch (bit) { case ROHM_DVS_LEVEL_RUN: prop = "rohm,dvs-run-voltage"; reg = dvs->run_reg; diff --git a/include/linux/mfd/rohm-generic.h b/include/linux/mfd/rohm-generic.h index 4283b5b33e04..2b85b9deb03a 100644 --- a/include/linux/mfd/rohm-generic.h +++ b/include/linux/mfd/rohm-generic.h @@ -20,14 +20,12 @@ struct rohm_regmap_dev { struct regmap *regmap; }; -enum { - ROHM_DVS_LEVEL_UNKNOWN, - ROHM_DVS_LEVEL_RUN, - ROHM_DVS_LEVEL_IDLE, - ROHM_DVS_LEVEL_SUSPEND, - ROHM_DVS_LEVEL_LPSR, - ROHM_DVS_LEVEL_MAX = ROHM_DVS_LEVEL_LPSR, -}; +#define ROHM_DVS_LEVEL_RUN BIT(0) +#define ROHM_DVS_LEVEL_IDLE BIT(1) +#define ROHM_DVS_LEVEL_SUSPEND BIT(2) +#define ROHM_DVS_LEVEL_LPSR BIT(3) +#define ROHM_DVS_LEVEL_VALID_AMOUNT 4 +#define ROHM_DVS_LEVEL_UNKNOWN 0 /** * struct rohm_dvs_config - dynamic voltage scaling register descriptions