diff options
author | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2021-02-22 21:21:03 -0800 |
---|---|---|
committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2021-02-22 21:21:03 -0800 |
commit | 415e915fdfc775ad0c6675fde1008f6f43dd6251 (patch) | |
tree | 429851187c0e85daa78f5d2bb6853959a1f5545b /drivers/regulator | |
parent | e64123949e6c9581c97fc14594f1cf34bf1d87a8 (diff) | |
parent | f40ddce88593482919761f74910f42f4b84c004b (diff) | |
download | linux-stable-415e915fdfc775ad0c6675fde1008f6f43dd6251.tar.gz linux-stable-415e915fdfc775ad0c6675fde1008f6f43dd6251.tar.bz2 linux-stable-415e915fdfc775ad0c6675fde1008f6f43dd6251.zip |
Merge tag 'v5.11' into next
Merge with mainline to get latest APIs and device tree bindings.
Diffstat (limited to 'drivers/regulator')
72 files changed, 5597 insertions, 687 deletions
diff --git a/drivers/regulator/88pg86x.c b/drivers/regulator/88pg86x.c index 71cfa2c5de5e..e91d5885c5ef 100644 --- a/drivers/regulator/88pg86x.c +++ b/drivers/regulator/88pg86x.c @@ -84,7 +84,7 @@ static int pg86x_i2c_probe(struct i2c_client *i2c) return 0; } -static const struct of_device_id pg86x_dt_ids [] = { +static const struct of_device_id __maybe_unused pg86x_dt_ids[] = { { .compatible = "marvell,88pg867" }, { .compatible = "marvell,88pg868" }, { } diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index de17ef7e18f0..5abdd29fb9f3 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -155,6 +155,15 @@ config REGULATOR_ARIZONA_MICSUPP and Wolfson Microelectronic Arizona codecs devices. +config REGULATOR_ARM_SCMI + tristate "SCMI based regulator driver" + depends on ARM_SCMI_PROTOCOL && OF + help + This adds the regulator driver support for ARM platforms using SCMI + protocol for device voltage management. + This driver uses SCMI Message Protocol driver to interact with the + firmware providing the device Voltage functionality. + config REGULATOR_AS3711 tristate "AS3711 PMIC" depends on MFD_AS3711 @@ -231,6 +240,16 @@ config REGULATOR_BD9571MWV This driver can also be built as a module. If so, the module will be called bd9571mwv-regulator. +config REGULATOR_BD957XMUF + tristate "ROHM BD9576MUF and BD9573MUF Regulators" + depends on MFD_ROHM_BD957XMUF + help + This driver supports voltage regulators on ROHM BD9576MUF and + BD9573MUF PMICs. + + This driver can also be built as a module. If so, the module + will be called bd9576-regulator. + config REGULATOR_CPCAP tristate "Motorola CPCAP regulator" depends on MFD_CPCAP @@ -293,6 +312,26 @@ config REGULATOR_DA9063 This driver can also be built as a module. If so, the module will be called da9063-regulator. +config REGULATOR_DA9121 + tristate "Dialog Semiconductor DA9121/DA9122/DA9220/DA9217/DA9130/DA9131/DA9132 regulator" + depends on I2C && OF + select REGMAP_I2C + help + Say y here to support for the Dialog Semiconductor DA9121. The + DA9121 is a single channel dual-phase buck converter controlled + through an I2C interface. + + DA9121 Single-channel dual-phase 10A buck converter + DA9130 Single-channel dual-phase 10A buck converter (Automotive) + DA9217 Single-channel dual-phase 6A buck converter + DA9122 Dual-channel single-phase 5A buck converter + DA9131 Dual-channel single-phase 5A buck converter (Automotive) + DA9220 Dual-channel single-phase 3A buck converter + DA9132 Dual-channel single-phase 3A buck converter (Automotive) + + This driver can also be built as a module. If so, the module + will be called da9121-regulator. + config REGULATOR_DA9210 tristate "Dialog Semiconductor DA9210 regulator" depends on I2C @@ -500,7 +539,7 @@ config REGULATOR_MAX1586 config REGULATOR_MAX77620 tristate "Maxim 77620/MAX20024 voltage regulator" - depends on MFD_MAX77620 + depends on MFD_MAX77620 || COMPILE_TEST help This driver controls Maxim MAX77620 voltage output regulator via I2C bus. The provided regulator is suitable for Tegra @@ -509,7 +548,7 @@ config REGULATOR_MAX77620 config REGULATOR_MAX77650 tristate "Maxim MAX77650/77651 regulator support" - depends on MFD_MAX77650 + depends on MFD_MAX77650 || COMPILE_TEST help Regulator driver for MAX77650/77651 PMIC from Maxim Semiconductor. This device has a SIMO with three independent @@ -532,7 +571,7 @@ config REGULATOR_MAX8660 config REGULATOR_MAX8907 tristate "Maxim 8907 voltage regulator" - depends on MFD_MAX8907 + depends on MFD_MAX8907 || COMPILE_TEST help This driver controls a Maxim 8907 voltage output regulator via I2C bus. The provided regulator is suitable for Tegra @@ -582,7 +621,7 @@ config REGULATOR_MAX8998 config REGULATOR_MAX77686 tristate "Maxim 77686 regulator" - depends on MFD_MAX77686 + depends on MFD_MAX77686 || COMPILE_TEST help This driver controls a Maxim 77686 regulator via I2C bus. The provided regulator is suitable for @@ -590,7 +629,7 @@ config REGULATOR_MAX77686 config REGULATOR_MAX77693 tristate "Maxim 77693/77843 regulator" - depends on (MFD_MAX77693 || MFD_MAX77843) + depends on MFD_MAX77693 || MFD_MAX77843 || COMPILE_TEST help This driver controls a Maxim 77693/77843 regulators via I2C bus. The regulators include two LDOs, 'SAFEOUT1', 'SAFEOUT2' @@ -599,7 +638,7 @@ config REGULATOR_MAX77693 config REGULATOR_MAX77802 tristate "Maxim 77802 regulator" - depends on MFD_MAX77686 + depends on MFD_MAX77686 || COMPILE_TEST help This driver controls a Maxim 77802 regulator via I2C bus. The provided regulator is suitable for @@ -711,6 +750,15 @@ config REGULATOR_MT6358 This driver supports the control of different power rails of device through regulator interface. +config REGULATOR_MT6360 + tristate "MT6360 SubPMIC Regulator" + depends on MFD_MT6360 + help + Say Y here to enable MT6360 regulator support. + This is support MT6360 PMIC/LDO part include + 2-channel buck with Thermal Shutdown and Overload Protection + 6-channel High PSRR and Low Dropout LDO. + config REGULATOR_MT6380 tristate "MediaTek MT6380 PMIC" depends on MTK_PMIC_WRAP @@ -772,9 +820,17 @@ config REGULATOR_PCF50633 Say Y here to support the voltage regulators and converters on PCF50633 +config REGULATOR_PF8X00 + tristate "NXP PF8100/PF8121A/PF8200 regulator driver" + depends on I2C && OF + select REGMAP_I2C + help + Say y here to support the regulators found on the NXP + PF8100/PF8121A/PF8200 PMIC. + config REGULATOR_PFUZE100 tristate "Freescale PFUZE100/200/3000/3001 regulator driver" - depends on I2C + depends on I2C && OF select REGMAP_I2C help Say y here to support the regulators found on the Freescale @@ -824,7 +880,8 @@ config REGULATOR_QCOM_RPM config REGULATOR_QCOM_RPMH tristate "Qualcomm Technologies, Inc. RPMh regulator driver" - depends on QCOM_RPMH || COMPILE_TEST + depends on QCOM_RPMH || (QCOM_RPMH=n && COMPILE_TEST) + depends on QCOM_COMMAND_DB || (QCOM_COMMAND_DB=n && COMPILE_TEST) help This driver supports control of PMIC regulators via the RPMh hardware block found on Qualcomm Technologies Inc. SoCs. RPMh regulator @@ -864,6 +921,16 @@ config REGULATOR_QCOM_USB_VBUS Say M here if you want to include support for enabling the VBUS output as a module. The module will be named "qcom_usb_vbus_regulator". +config REGULATOR_RASPBERRYPI_TOUCHSCREEN_ATTINY + tristate "Raspberry Pi 7-inch touchscreen panel ATTINY regulator" + depends on BACKLIGHT_CLASS_DEVICE + depends on I2C + select REGMAP_I2C + help + This driver supports ATTINY regulator on the Raspberry Pi 7-inch + touchscreen unit. The regulator is used to enable power to the + TC358762, display and to control backlight. + config REGULATOR_RC5T583 tristate "RICOH RC5T583 Power regulators" depends on MFD_RC5T583 @@ -894,6 +961,14 @@ config REGULATOR_RN5T618 config REGULATOR_ROHM tristate +config REGULATOR_RT4801 + tristate "Richtek RT4801 Regulators" + depends on I2C + select REGMAP_I2C + help + This adds support for voltage regulators in Richtek RT4801 Display Bias IC. + The device supports two regulators (DSVP/DSVN). + config REGULATOR_RT5033 tristate "Richtek RT5033 Regulators" depends on MFD_RT5033 @@ -902,16 +977,25 @@ config REGULATOR_RT5033 RT5033 PMIC. The device supports multiple regulators like current source, LDO and Buck. +config REGULATOR_RTMV20 + tristate "RTMV20 Laser Diode Regulator" + depends on I2C + select REGMAP_I2C + help + This driver adds support for the load switch current regulator on + the Richtek RTMV20. It can support the load current up to 6A and + integrate strobe/vsync/fsin signal to synchronize the IR camera. + config REGULATOR_S2MPA01 tristate "Samsung S2MPA01 voltage regulator" - depends on MFD_SEC_CORE + depends on MFD_SEC_CORE || COMPILE_TEST help This driver controls Samsung S2MPA01 voltage output regulator via I2C bus. S2MPA01 has 10 Bucks and 26 LDO outputs. config REGULATOR_S2MPS11 tristate "Samsung S2MPS11/13/14/15/S2MPU02 voltage regulator" - depends on MFD_SEC_CORE + depends on MFD_SEC_CORE || COMPILE_TEST help This driver supports a Samsung S2MPS11/13/14/15/S2MPU02 voltage output regulator via I2C bus. The chip is comprised of high efficient @@ -920,7 +1004,7 @@ config REGULATOR_S2MPS11 config REGULATOR_S5M8767 tristate "Samsung S5M8767A voltage regulator" - depends on MFD_SEC_CORE + depends on MFD_SEC_CORE || COMPILE_TEST help This driver supports a Samsung S5M8767A voltage output regulator via I2C bus. S5M8767A have 9 Bucks and 28 LDOs output and diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index d8d3ecf526a8..680e539f6579 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -24,6 +24,7 @@ obj-$(CONFIG_REGULATOR_AD5398) += ad5398.o obj-$(CONFIG_REGULATOR_ANATOP) += anatop-regulator.o obj-$(CONFIG_REGULATOR_ARIZONA_LDO1) += arizona-ldo1.o 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_AXP20X) += axp20x-regulator.o @@ -32,11 +33,13 @@ obj-$(CONFIG_REGULATOR_BD70528) += bd70528-regulator.o obj-$(CONFIG_REGULATOR_BD71828) += bd71828-regulator.o obj-$(CONFIG_REGULATOR_BD718XX) += bd718x7-regulator.o obj-$(CONFIG_REGULATOR_BD9571MWV) += bd9571mwv-regulator.o +obj-$(CONFIG_REGULATOR_BD957XMUF) += bd9576-regulator.o obj-$(CONFIG_REGULATOR_DA903X) += da903x-regulator.o obj-$(CONFIG_REGULATOR_DA9052) += da9052-regulator.o obj-$(CONFIG_REGULATOR_DA9055) += da9055-regulator.o obj-$(CONFIG_REGULATOR_DA9062) += da9062-regulator.o obj-$(CONFIG_REGULATOR_DA9063) += da9063-regulator.o +obj-$(CONFIG_REGULATOR_DA9121) += da9121-regulator.o obj-$(CONFIG_REGULATOR_DA9210) += da9210-regulator.o obj-$(CONFIG_REGULATOR_DA9211) += da9211-regulator.o obj-$(CONFIG_REGULATOR_DBX500_PRCMU) += dbx500-prcmu.o @@ -88,6 +91,7 @@ obj-$(CONFIG_REGULATOR_MPQ7920) += mpq7920.o obj-$(CONFIG_REGULATOR_MT6311) += mt6311-regulator.o obj-$(CONFIG_REGULATOR_MT6323) += mt6323-regulator.o 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_QCOM_LABIBB) += qcom-labibb-regulator.o @@ -98,6 +102,7 @@ obj-$(CONFIG_REGULATOR_QCOM_SPMI) += qcom_spmi-regulator.o obj-$(CONFIG_REGULATOR_QCOM_USB_VBUS) += qcom_usb_vbus-regulator.o obj-$(CONFIG_REGULATOR_PALMAS) += palmas-regulator.o obj-$(CONFIG_REGULATOR_PCA9450) += pca9450-regulator.o +obj-$(CONFIG_REGULATOR_PF8X00) += pf8x00-regulator.o obj-$(CONFIG_REGULATOR_PFUZE100) += pfuze100-regulator.o obj-$(CONFIG_REGULATOR_PV88060) += pv88060-regulator.o obj-$(CONFIG_REGULATOR_PV88080) += pv88080-regulator.o @@ -107,11 +112,14 @@ obj-$(CONFIG_REGULATOR_TPS51632) += tps51632-regulator.o obj-$(CONFIG_REGULATOR_PBIAS) += pbias-regulator.o obj-$(CONFIG_REGULATOR_PCAP) += pcap-regulator.o obj-$(CONFIG_REGULATOR_PCF50633) += pcf50633-regulator.o +obj-$(CONFIG_REGULATOR_RASPBERRYPI_TOUCHSCREEN_ATTINY) += rpi-panel-attiny-regulator.o obj-$(CONFIG_REGULATOR_RC5T583) += rc5t583-regulator.o 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_RT5033) += rt5033-regulator.o +obj-$(CONFIG_REGULATOR_RTMV20) += rtmv20-regulator.o obj-$(CONFIG_REGULATOR_S2MPA01) += s2mpa01.o obj-$(CONFIG_REGULATOR_S2MPS11) += s2mps11.o obj-$(CONFIG_REGULATOR_S5M8767) += s5m8767.o diff --git a/drivers/regulator/as3722-regulator.c b/drivers/regulator/as3722-regulator.c index 33ca197860b3..7bebf9ce6271 100644 --- a/drivers/regulator/as3722-regulator.c +++ b/drivers/regulator/as3722-regulator.c @@ -455,7 +455,8 @@ static int as3722_sd_set_mode(struct regulator_dev *rdev, switch (mode) { case REGULATOR_MODE_FAST: val = as3722_reg_lookup[id].mode_mask; - case REGULATOR_MODE_NORMAL: /* fall down */ + fallthrough; + case REGULATOR_MODE_NORMAL: break; default: return -EINVAL; diff --git a/drivers/regulator/axp20x-regulator.c b/drivers/regulator/axp20x-regulator.c index cd1224182ad7..90cb8445f721 100644 --- a/drivers/regulator/axp20x-regulator.c +++ b/drivers/regulator/axp20x-regulator.c @@ -594,7 +594,7 @@ static const struct regulator_desc axp22x_regulators[] = { AXP22X_DLDO1_V_OUT, AXP22X_DLDO1_V_OUT_MASK, AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DLDO1_MASK), AXP_DESC(AXP22X, DLDO2, "dldo2", "dldoin", 700, 3300, 100, - AXP22X_DLDO2_V_OUT, AXP22X_PWR_OUT_DLDO2_MASK, + AXP22X_DLDO2_V_OUT, AXP22X_DLDO2_V_OUT_MASK, AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DLDO2_MASK), AXP_DESC(AXP22X, DLDO3, "dldo3", "dldoin", 700, 3300, 100, AXP22X_DLDO3_V_OUT, AXP22X_DLDO3_V_OUT_MASK, diff --git a/drivers/regulator/bd718x7-regulator.c b/drivers/regulator/bd718x7-regulator.c index 7b311389f925..9309765d0450 100644 --- a/drivers/regulator/bd718x7-regulator.c +++ b/drivers/regulator/bd718x7-regulator.c @@ -15,6 +15,69 @@ #include <linux/regulator/of_regulator.h> #include <linux/slab.h> +/* Typical regulator startup times as per data sheet in uS */ +#define BD71847_BUCK1_STARTUP_TIME 144 +#define BD71847_BUCK2_STARTUP_TIME 162 +#define BD71847_BUCK3_STARTUP_TIME 162 +#define BD71847_BUCK4_STARTUP_TIME 240 +#define BD71847_BUCK5_STARTUP_TIME 270 +#define BD71847_BUCK6_STARTUP_TIME 200 +#define BD71847_LDO1_STARTUP_TIME 440 +#define BD71847_LDO2_STARTUP_TIME 370 +#define BD71847_LDO3_STARTUP_TIME 310 +#define BD71847_LDO4_STARTUP_TIME 400 +#define BD71847_LDO5_STARTUP_TIME 530 +#define BD71847_LDO6_STARTUP_TIME 400 + +#define BD71837_BUCK1_STARTUP_TIME 160 +#define BD71837_BUCK2_STARTUP_TIME 180 +#define BD71837_BUCK3_STARTUP_TIME 180 +#define BD71837_BUCK4_STARTUP_TIME 180 +#define BD71837_BUCK5_STARTUP_TIME 160 +#define BD71837_BUCK6_STARTUP_TIME 240 +#define BD71837_BUCK7_STARTUP_TIME 220 +#define BD71837_BUCK8_STARTUP_TIME 200 +#define BD71837_LDO1_STARTUP_TIME 440 +#define BD71837_LDO2_STARTUP_TIME 370 +#define BD71837_LDO3_STARTUP_TIME 310 +#define BD71837_LDO4_STARTUP_TIME 400 +#define BD71837_LDO5_STARTUP_TIME 310 +#define BD71837_LDO6_STARTUP_TIME 400 +#define BD71837_LDO7_STARTUP_TIME 530 + +/* + * BD718(37/47/50) have two "enable control modes". ON/OFF can either be + * controlled by software - or by PMIC internal HW state machine. Whether + * regulator should be under SW or HW control can be defined from device-tree. + * Let's provide separate ops for regulators to use depending on the "enable + * control mode". + */ +#define BD718XX_HWOPNAME(swopname) swopname##_hwcontrol + +#define BD718XX_OPS(name, _list_voltage, _map_voltage, _set_voltage_sel, \ + _get_voltage_sel, _set_voltage_time_sel, _set_ramp_delay) \ +static const struct regulator_ops name = { \ + .enable = regulator_enable_regmap, \ + .disable = regulator_disable_regmap, \ + .is_enabled = regulator_is_enabled_regmap, \ + .list_voltage = (_list_voltage), \ + .map_voltage = (_map_voltage), \ + .set_voltage_sel = (_set_voltage_sel), \ + .get_voltage_sel = (_get_voltage_sel), \ + .set_voltage_time_sel = (_set_voltage_time_sel), \ + .set_ramp_delay = (_set_ramp_delay), \ +}; \ + \ +static const struct regulator_ops BD718XX_HWOPNAME(name) = { \ + .is_enabled = always_enabled_by_hwstate, \ + .list_voltage = (_list_voltage), \ + .map_voltage = (_map_voltage), \ + .set_voltage_sel = (_set_voltage_sel), \ + .get_voltage_sel = (_get_voltage_sel), \ + .set_voltage_time_sel = (_set_voltage_time_sel), \ + .set_ramp_delay = (_set_ramp_delay), \ +} \ + /* * BUCK1/2/3/4 * BUCK1RAMPRATE[1:0] BUCK1 DVS ramp rate setting @@ -55,6 +118,38 @@ static int bd718xx_buck1234_set_ramp_delay(struct regulator_dev *rdev, BUCK_RAMPRATE_MASK, ramp_value << 6); } +/* These functions are used when regulators are under HW state machine control. + * We assume PMIC is in RUN state because SW running and able to query the + * status. Most of the regulators have fixed ON or OFF state at RUN/IDLE so for + * them we just return a constant. BD71837 BUCK3 and BUCK4 are exceptions as + * they support configuring the ON/OFF state for RUN. + * + * Note for next hacker - these PMICs have a register where the HW state can be + * read. If assuming RUN appears to be false in your use-case - you can + * implement state reading (although that is not going to be atomic) before + * returning the enable state. + */ +static int always_enabled_by_hwstate(struct regulator_dev *rdev) +{ + return 1; +} + +static int never_enabled_by_hwstate(struct regulator_dev *rdev) +{ + return 0; +} + +static int bd71837_get_buck34_enable_hwctrl(struct regulator_dev *rdev) +{ + int ret; + unsigned int val; + + ret = regmap_read(rdev->regmap, rdev->desc->enable_reg, &val); + if (ret) + return ret; + + return !!(BD718XX_BUCK_RUN_ON & val); +} /* * On BD71837 (not on BD71847, BD71850, ...) * Bucks 1 to 4 support DVS. PWM mode is used when voltage is changed. @@ -71,7 +166,7 @@ static int bd718xx_buck1234_set_ramp_delay(struct regulator_dev *rdev, static int bd71837_set_voltage_sel_restricted(struct regulator_dev *rdev, unsigned int sel) { - if (regulator_is_enabled_regmap(rdev)) + if (rdev->desc->ops->is_enabled(rdev)) return -EBUSY; return regulator_set_voltage_sel_regmap(rdev, sel); @@ -113,7 +208,7 @@ static int voltage_change_prepare(struct regulator_dev *rdev, unsigned int sel, int ret; *mask = 0; - if (regulator_is_enabled_regmap(rdev)) { + if (rdev->desc->ops->is_enabled(rdev)) { int now, new; now = rdev->desc->ops->get_voltage_sel(rdev); @@ -195,133 +290,90 @@ static int bd718xx_set_voltage_sel_pickable_restricted( static int bd71837_set_voltage_sel_pickable_restricted( struct regulator_dev *rdev, unsigned int sel) { - if (regulator_is_enabled_regmap(rdev)) + if (rdev->desc->ops->is_enabled(rdev)) return -EBUSY; return regulator_set_voltage_sel_pickable_regmap(rdev, sel); } -static const struct regulator_ops bd718xx_pickable_range_ldo_ops = { - .enable = regulator_enable_regmap, - .disable = regulator_disable_regmap, - .is_enabled = regulator_is_enabled_regmap, +/* + * OPS common for BD71847 and BD71850 + */ +BD718XX_OPS(bd718xx_pickable_range_ldo_ops, + regulator_list_voltage_pickable_linear_range, NULL, + bd718xx_set_voltage_sel_pickable_restricted, + regulator_get_voltage_sel_pickable_regmap, NULL, NULL); + +/* BD71847 and BD71850 LDO 5 is by default OFF at RUN state */ +static const struct regulator_ops bd718xx_ldo5_ops_hwstate = { + .is_enabled = never_enabled_by_hwstate, .list_voltage = regulator_list_voltage_pickable_linear_range, .set_voltage_sel = bd718xx_set_voltage_sel_pickable_restricted, .get_voltage_sel = regulator_get_voltage_sel_pickable_regmap, - }; -static const struct regulator_ops bd71837_pickable_range_ldo_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 = bd71837_set_voltage_sel_pickable_restricted, - .get_voltage_sel = regulator_get_voltage_sel_pickable_regmap, -}; +BD718XX_OPS(bd718xx_pickable_range_buck_ops, + regulator_list_voltage_pickable_linear_range, NULL, + regulator_set_voltage_sel_pickable_regmap, + regulator_get_voltage_sel_pickable_regmap, + regulator_set_voltage_time_sel, NULL); -static const struct regulator_ops bd718xx_pickable_range_buck_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 = regulator_set_voltage_time_sel, -}; +BD718XX_OPS(bd718xx_ldo_regulator_ops, regulator_list_voltage_linear_range, + NULL, bd718xx_set_voltage_sel_restricted, + regulator_get_voltage_sel_regmap, NULL, NULL); -static const struct regulator_ops bd71837_pickable_range_buck_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 = bd71837_set_voltage_sel_pickable_restricted, - .get_voltage_sel = regulator_get_voltage_sel_pickable_regmap, - .set_voltage_time_sel = regulator_set_voltage_time_sel, -}; +BD718XX_OPS(bd718xx_ldo_regulator_nolinear_ops, regulator_list_voltage_table, + NULL, bd718xx_set_voltage_sel_restricted, + regulator_get_voltage_sel_regmap, NULL, NULL); -static const struct regulator_ops bd71837_ldo_regulator_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 = bd71837_set_voltage_sel_restricted, - .get_voltage_sel = regulator_get_voltage_sel_regmap, -}; - -static const struct regulator_ops bd718xx_ldo_regulator_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 = bd718xx_set_voltage_sel_restricted, - .get_voltage_sel = regulator_get_voltage_sel_regmap, -}; - -static const struct regulator_ops bd71837_ldo_regulator_nolinear_ops = { - .enable = regulator_enable_regmap, - .disable = regulator_disable_regmap, - .is_enabled = regulator_is_enabled_regmap, - .list_voltage = regulator_list_voltage_table, - .set_voltage_sel = bd71837_set_voltage_sel_restricted, - .get_voltage_sel = regulator_get_voltage_sel_regmap, -}; +BD718XX_OPS(bd718xx_buck_regulator_ops, regulator_list_voltage_linear_range, + NULL, regulator_set_voltage_sel_regmap, + regulator_get_voltage_sel_regmap, regulator_set_voltage_time_sel, + NULL); -static const struct regulator_ops bd718xx_ldo_regulator_nolinear_ops = { - .enable = regulator_enable_regmap, - .disable = regulator_disable_regmap, - .is_enabled = regulator_is_enabled_regmap, - .list_voltage = regulator_list_voltage_table, - .set_voltage_sel = bd718xx_set_voltage_sel_restricted, - .get_voltage_sel = regulator_get_voltage_sel_regmap, -}; +BD718XX_OPS(bd718xx_buck_regulator_nolinear_ops, regulator_list_voltage_table, + regulator_map_voltage_ascend, regulator_set_voltage_sel_regmap, + regulator_get_voltage_sel_regmap, regulator_set_voltage_time_sel, + NULL); -static const struct regulator_ops bd718xx_buck_regulator_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 = regulator_set_voltage_time_sel, -}; - -static const struct regulator_ops bd71837_buck_regulator_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 = bd71837_set_voltage_sel_restricted, - .get_voltage_sel = regulator_get_voltage_sel_regmap, - .set_voltage_time_sel = regulator_set_voltage_time_sel, -}; - -static const struct regulator_ops bd718xx_buck_regulator_nolinear_ops = { - .enable = regulator_enable_regmap, - .disable = regulator_disable_regmap, - .is_enabled = regulator_is_enabled_regmap, - .list_voltage = regulator_list_voltage_table, - .map_voltage = regulator_map_voltage_ascend, - .set_voltage_sel = regulator_set_voltage_sel_regmap, - .get_voltage_sel = regulator_get_voltage_sel_regmap, - .set_voltage_time_sel = regulator_set_voltage_time_sel, -}; - -static const struct regulator_ops bd71837_buck_regulator_nolinear_ops = { - .enable = regulator_enable_regmap, - .disable = regulator_disable_regmap, - .is_enabled = regulator_is_enabled_regmap, - .list_voltage = regulator_list_voltage_table, - .map_voltage = regulator_map_voltage_ascend, - .set_voltage_sel = bd718xx_set_voltage_sel_restricted, - .get_voltage_sel = regulator_get_voltage_sel_regmap, - .set_voltage_time_sel = regulator_set_voltage_time_sel, -}; - -static const struct regulator_ops bd718xx_dvs_buck_regulator_ops = { - .enable = regulator_enable_regmap, - .disable = regulator_disable_regmap, - .is_enabled = regulator_is_enabled_regmap, +/* + * OPS for BD71837 + */ +BD718XX_OPS(bd71837_pickable_range_ldo_ops, + regulator_list_voltage_pickable_linear_range, NULL, + bd71837_set_voltage_sel_pickable_restricted, + regulator_get_voltage_sel_pickable_regmap, NULL, NULL); + +BD718XX_OPS(bd71837_pickable_range_buck_ops, + regulator_list_voltage_pickable_linear_range, NULL, + bd71837_set_voltage_sel_pickable_restricted, + regulator_get_voltage_sel_pickable_regmap, + regulator_set_voltage_time_sel, NULL); + +BD718XX_OPS(bd71837_ldo_regulator_ops, regulator_list_voltage_linear_range, + NULL, bd71837_set_voltage_sel_restricted, + regulator_get_voltage_sel_regmap, NULL, NULL); + +BD718XX_OPS(bd71837_ldo_regulator_nolinear_ops, regulator_list_voltage_table, + NULL, bd71837_set_voltage_sel_restricted, + regulator_get_voltage_sel_regmap, NULL, NULL); + +BD718XX_OPS(bd71837_buck_regulator_ops, regulator_list_voltage_linear_range, + NULL, bd71837_set_voltage_sel_restricted, + regulator_get_voltage_sel_regmap, regulator_set_voltage_time_sel, + NULL); + +BD718XX_OPS(bd71837_buck_regulator_nolinear_ops, regulator_list_voltage_table, + regulator_map_voltage_ascend, bd718xx_set_voltage_sel_restricted, + regulator_get_voltage_sel_regmap, regulator_set_voltage_time_sel, + NULL); +/* + * BD71837 bucks 3 and 4 support defining their enable/disable state also + * when buck enable state is under HW state machine control. In that case the + * bit [2] in CTRL register is used to indicate if regulator should be ON. + */ +static const struct regulator_ops bd71837_buck34_ops_hwctrl = { + .is_enabled = bd71837_get_buck34_enable_hwctrl, .list_voltage = regulator_list_voltage_linear_range, .set_voltage_sel = regulator_set_voltage_sel_regmap, .get_voltage_sel = regulator_get_voltage_sel_regmap, @@ -330,6 +382,14 @@ static const struct regulator_ops bd718xx_dvs_buck_regulator_ops = { }; /* + * OPS for all of the ICs - BD718(37/47/50) + */ +BD718XX_OPS(bd718xx_dvs_buck_regulator_ops, regulator_list_voltage_linear_range, + NULL, regulator_set_voltage_sel_regmap, + regulator_get_voltage_sel_regmap, regulator_set_voltage_time_sel, + bd718xx_buck1234_set_ramp_delay); + +/* * BD71837 BUCK1/2/3/4 * BD71847 BUCK1/2 * 0.70 to 1.30V (10mV step) @@ -543,14 +603,37 @@ static int buck_set_hw_dvs_levels(struct device_node *np, return rohm_regulator_set_dvs_levels(&data->dvs, np, desc, cfg->regmap); } -static const struct bd718xx_regulator_data bd71847_regulators[] = { +static const struct regulator_ops *bd71847_swcontrol_ops[] = { + &bd718xx_dvs_buck_regulator_ops, &bd718xx_dvs_buck_regulator_ops, + &bd718xx_pickable_range_buck_ops, &bd718xx_pickable_range_buck_ops, + &bd718xx_buck_regulator_nolinear_ops, &bd718xx_buck_regulator_ops, + &bd718xx_pickable_range_ldo_ops, &bd718xx_ldo_regulator_nolinear_ops, + &bd718xx_ldo_regulator_ops, &bd718xx_ldo_regulator_ops, + &bd718xx_pickable_range_ldo_ops, &bd718xx_ldo_regulator_ops, +}; + +static const struct regulator_ops *bd71847_hwcontrol_ops[] = { + &BD718XX_HWOPNAME(bd718xx_dvs_buck_regulator_ops), + &BD718XX_HWOPNAME(bd718xx_dvs_buck_regulator_ops), + &BD718XX_HWOPNAME(bd718xx_pickable_range_buck_ops), + &BD718XX_HWOPNAME(bd718xx_pickable_range_buck_ops), + &BD718XX_HWOPNAME(bd718xx_buck_regulator_nolinear_ops), + &BD718XX_HWOPNAME(bd718xx_buck_regulator_ops), + &BD718XX_HWOPNAME(bd718xx_pickable_range_ldo_ops), + &BD718XX_HWOPNAME(bd718xx_ldo_regulator_nolinear_ops), + &BD718XX_HWOPNAME(bd718xx_ldo_regulator_ops), + &BD718XX_HWOPNAME(bd718xx_ldo_regulator_ops), + &bd718xx_ldo5_ops_hwstate, + &BD718XX_HWOPNAME(bd718xx_ldo_regulator_ops), +}; + +static struct bd718xx_regulator_data bd71847_regulators[] = { { .desc = { .name = "buck1", .of_match = of_match_ptr("BUCK1"), .regulators_node = of_match_ptr("regulators"), .id = BD718XX_BUCK1, - .ops = &bd718xx_dvs_buck_regulator_ops, .type = REGULATOR_VOLTAGE, .n_voltages = BD718XX_DVS_BUCK_VOLTAGE_NUM, .linear_ranges = bd718xx_dvs_buck_volts, @@ -560,6 +643,7 @@ static const struct bd718xx_regulator_data bd71847_regulators[] = { .vsel_mask = DVS_BUCK_RUN_MASK, .enable_reg = BD718XX_REG_BUCK1_CTRL, .enable_mask = BD718XX_BUCK_EN, + .enable_time = BD71847_BUCK1_STARTUP_TIME, .owner = THIS_MODULE, .of_parse_cb = buck_set_hw_dvs_levels, }, @@ -585,7 +669,6 @@ static const struct bd718xx_regulator_data bd71847_regulators[] = { .of_match = of_match_ptr("BUCK2"), .regulators_node = of_match_ptr("regulators"), .id = BD718XX_BUCK2, - .ops = &bd718xx_dvs_buck_regulator_ops, .type = REGULATOR_VOLTAGE, .n_voltages = BD718XX_DVS_BUCK_VOLTAGE_NUM, .linear_ranges = bd718xx_dvs_buck_volts, @@ -594,6 +677,7 @@ static const struct bd718xx_regulator_data bd71847_regulators[] = { .vsel_mask = DVS_BUCK_RUN_MASK, .enable_reg = BD718XX_REG_BUCK2_CTRL, .enable_mask = BD718XX_BUCK_EN, + .enable_time = BD71847_BUCK2_STARTUP_TIME, .owner = THIS_MODULE, .of_parse_cb = buck_set_hw_dvs_levels, }, @@ -616,7 +700,6 @@ static const struct bd718xx_regulator_data bd71847_regulators[] = { .of_match = of_match_ptr("BUCK3"), .regulators_node = of_match_ptr("regulators"), .id = BD718XX_BUCK3, - .ops = &bd718xx_pickable_range_buck_ops, .type = REGULATOR_VOLTAGE, .n_voltages = BD71847_BUCK3_VOLTAGE_NUM, .linear_ranges = bd71847_buck3_volts, @@ -629,6 +712,7 @@ static const struct bd718xx_regulator_data bd71847_regulators[] = { .linear_range_selectors = bd71847_buck3_volt_range_sel, .enable_reg = BD718XX_REG_1ST_NODVS_BUCK_CTRL, .enable_mask = BD718XX_BUCK_EN, + .enable_time = BD71847_BUCK3_STARTUP_TIME, .owner = THIS_MODULE, }, .init = { @@ -643,7 +727,6 @@ static const struct bd718xx_regulator_data bd71847_regulators[] = { .of_match = of_match_ptr("BUCK4"), .regulators_node = of_match_ptr("regulators"), .id = BD718XX_BUCK4, - .ops = &bd718xx_pickable_range_buck_ops, .type = REGULATOR_VOLTAGE, .n_voltages = BD71847_BUCK4_VOLTAGE_NUM, .linear_ranges = bd71847_buck4_volts, @@ -656,6 +739,7 @@ static const struct bd718xx_regulator_data bd71847_regulators[] = { .vsel_range_mask = BD71847_BUCK4_RANGE_MASK, .linear_range_selectors = bd71847_buck4_volt_range_sel, .enable_mask = BD718XX_BUCK_EN, + .enable_time = BD71847_BUCK4_STARTUP_TIME, .owner = THIS_MODULE, }, .init = { @@ -670,7 +754,6 @@ static const struct bd718xx_regulator_data bd71847_regulators[] = { .of_match = of_match_ptr("BUCK5"), .regulators_node = of_match_ptr("regulators"), .id = BD718XX_BUCK5, - .ops = &bd718xx_buck_regulator_nolinear_ops, .type = REGULATOR_VOLTAGE, .volt_table = &bd718xx_3rd_nodvs_buck_volts[0], .n_voltages = ARRAY_SIZE(bd718xx_3rd_nodvs_buck_volts), @@ -678,6 +761,7 @@ static const struct bd718xx_regulator_data bd71847_regulators[] = { .vsel_mask = BD718XX_3RD_NODVS_BUCK_MASK, .enable_reg = BD718XX_REG_3RD_NODVS_BUCK_CTRL, .enable_mask = BD718XX_BUCK_EN, + .enable_time = BD71847_BUCK5_STARTUP_TIME, .owner = THIS_MODULE, }, .init = { @@ -692,7 +776,6 @@ static const struct bd718xx_regulator_data bd71847_regulators[] = { .of_match = of_match_ptr("BUCK6"), .regulators_node = of_match_ptr("regulators"), .id = BD718XX_BUCK6, - .ops = &bd718xx_buck_regulator_ops, .type = REGULATOR_VOLTAGE, .n_voltages = BD718XX_4TH_NODVS_BUCK_VOLTAGE_NUM, .linear_ranges = bd718xx_4th_nodvs_buck_volts, @@ -702,6 +785,7 @@ static const struct bd718xx_regulator_data bd71847_regulators[] = { .vsel_mask = BD718XX_4TH_NODVS_BUCK_MASK, .enable_reg = BD718XX_REG_4TH_NODVS_BUCK_CTRL, .enable_mask = BD718XX_BUCK_EN, + .enable_time = BD71847_BUCK6_STARTUP_TIME, .owner = THIS_MODULE, }, .init = { @@ -716,7 +800,6 @@ static const struct bd718xx_regulator_data bd71847_regulators[] = { .of_match = of_match_ptr("LDO1"), .regulators_node = of_match_ptr("regulators"), .id = BD718XX_LDO1, - .ops = &bd718xx_pickable_range_ldo_ops, .type = REGULATOR_VOLTAGE, .n_voltages = BD718XX_LDO1_VOLTAGE_NUM, .linear_ranges = bd718xx_ldo1_volts, @@ -728,6 +811,7 @@ static const struct bd718xx_regulator_data bd71847_regulators[] = { .linear_range_selectors = bd718xx_ldo1_volt_range_sel, .enable_reg = BD718XX_REG_LDO1_VOLT, .enable_mask = BD718XX_LDO_EN, + .enable_time = BD71847_LDO1_STARTUP_TIME, .owner = THIS_MODULE, }, .init = { @@ -742,7 +826,6 @@ static const struct bd718xx_regulator_data bd71847_regulators[] = { .of_match = of_match_ptr("LDO2"), .regulators_node = of_match_ptr("regulators"), .id = BD718XX_LDO2, - .ops = &bd718xx_ldo_regulator_nolinear_ops, .type = REGULATOR_VOLTAGE, .volt_table = &ldo_2_volts[0], .vsel_reg = BD718XX_REG_LDO2_VOLT, @@ -750,6 +833,7 @@ static const struct bd718xx_regulator_data bd71847_regulators[] = { .n_voltages = ARRAY_SIZE(ldo_2_volts), .enable_reg = BD718XX_REG_LDO2_VOLT, .enable_mask = BD718XX_LDO_EN, + .enable_time = BD71847_LDO2_STARTUP_TIME, .owner = THIS_MODULE, }, .init = { @@ -764,7 +848,6 @@ static const struct bd718xx_regulator_data bd71847_regulators[] = { .of_match = of_match_ptr("LDO3"), .regulators_node = of_match_ptr("regulators"), .id = BD718XX_LDO3, - .ops = &bd718xx_ldo_regulator_ops, .type = REGULATOR_VOLTAGE, .n_voltages = BD718XX_LDO3_VOLTAGE_NUM, .linear_ranges = bd718xx_ldo3_volts, @@ -773,6 +856,7 @@ static const struct bd718xx_regulator_data bd71847_regulators[] = { .vsel_mask = BD718XX_LDO3_MASK, .enable_reg = BD718XX_REG_LDO3_VOLT, .enable_mask = BD718XX_LDO_EN, + .enable_time = BD71847_LDO3_STARTUP_TIME, .owner = THIS_MODULE, }, .init = { @@ -787,7 +871,6 @@ static const struct bd718xx_regulator_data bd71847_regulators[] = { .of_match = of_match_ptr("LDO4"), .regulators_node = of_match_ptr("regulators"), .id = BD718XX_LDO4, - .ops = &bd718xx_ldo_regulator_ops, .type = REGULATOR_VOLTAGE, .n_voltages = BD718XX_LDO4_VOLTAGE_NUM, .linear_ranges = bd718xx_ldo4_volts, @@ -796,6 +879,7 @@ static const struct bd718xx_regulator_data bd71847_regulators[] = { .vsel_mask = BD718XX_LDO4_MASK, .enable_reg = BD718XX_REG_LDO4_VOLT, .enable_mask = BD718XX_LDO_EN, + .enable_time = BD71847_LDO4_STARTUP_TIME, .owner = THIS_MODULE, }, .init = { @@ -810,7 +894,6 @@ static const struct bd718xx_regulator_data bd71847_regulators[] = { .of_match = of_match_ptr("LDO5"), .regulators_node = of_match_ptr("regulators"), .id = BD718XX_LDO5, - .ops = &bd718xx_pickable_range_ldo_ops, .type = REGULATOR_VOLTAGE, .n_voltages = BD71847_LDO5_VOLTAGE_NUM, .linear_ranges = bd71847_ldo5_volts, @@ -822,6 +905,7 @@ static const struct bd718xx_regulator_data bd71847_regulators[] = { .linear_range_selectors = bd71847_ldo5_volt_range_sel, .enable_reg = BD718XX_REG_LDO5_VOLT, .enable_mask = BD718XX_LDO_EN, + .enable_time = BD71847_LDO5_STARTUP_TIME, .owner = THIS_MODULE, }, .init = { @@ -836,7 +920,6 @@ static const struct bd718xx_regulator_data bd71847_regulators[] = { .of_match = of_match_ptr("LDO6"), .regulators_node = of_match_ptr("regulators"), .id = BD718XX_LDO6, - .ops = &bd718xx_ldo_regulator_ops, .type = REGULATOR_VOLTAGE, .n_voltages = BD718XX_LDO6_VOLTAGE_NUM, .linear_ranges = bd718xx_ldo6_volts, @@ -847,6 +930,7 @@ static const struct bd718xx_regulator_data bd71847_regulators[] = { .vsel_mask = BD718XX_LDO6_MASK, .enable_reg = BD718XX_REG_LDO6_VOLT, .enable_mask = BD718XX_LDO_EN, + .enable_time = BD71847_LDO6_STARTUP_TIME, .owner = THIS_MODULE, }, .init = { @@ -857,14 +941,41 @@ static const struct bd718xx_regulator_data bd71847_regulators[] = { }, }; -static const struct bd718xx_regulator_data bd71837_regulators[] = { +static const struct regulator_ops *bd71837_swcontrol_ops[] = { + &bd718xx_dvs_buck_regulator_ops, &bd718xx_dvs_buck_regulator_ops, + &bd718xx_dvs_buck_regulator_ops, &bd718xx_dvs_buck_regulator_ops, + &bd71837_pickable_range_buck_ops, &bd71837_buck_regulator_ops, + &bd71837_buck_regulator_nolinear_ops, &bd71837_buck_regulator_ops, + &bd71837_pickable_range_ldo_ops, &bd71837_ldo_regulator_nolinear_ops, + &bd71837_ldo_regulator_ops, &bd71837_ldo_regulator_ops, + &bd71837_ldo_regulator_ops, &bd71837_ldo_regulator_ops, + &bd71837_ldo_regulator_ops, +}; + +static const struct regulator_ops *bd71837_hwcontrol_ops[] = { + &BD718XX_HWOPNAME(bd718xx_dvs_buck_regulator_ops), + &BD718XX_HWOPNAME(bd718xx_dvs_buck_regulator_ops), + &bd71837_buck34_ops_hwctrl, &bd71837_buck34_ops_hwctrl, + &BD718XX_HWOPNAME(bd71837_pickable_range_buck_ops), + &BD718XX_HWOPNAME(bd71837_buck_regulator_ops), + &BD718XX_HWOPNAME(bd71837_buck_regulator_nolinear_ops), + &BD718XX_HWOPNAME(bd71837_buck_regulator_ops), + &BD718XX_HWOPNAME(bd71837_pickable_range_ldo_ops), + &BD718XX_HWOPNAME(bd71837_ldo_regulator_nolinear_ops), + &BD718XX_HWOPNAME(bd71837_ldo_regulator_ops), + &BD718XX_HWOPNAME(bd71837_ldo_regulator_ops), + &BD718XX_HWOPNAME(bd71837_ldo_regulator_ops), + &BD718XX_HWOPNAME(bd71837_ldo_regulator_ops), + &BD718XX_HWOPNAME(bd71837_ldo_regulator_ops), +}; + +static struct bd718xx_regulator_data bd71837_regulators[] = { { .desc = { .name = "buck1", .of_match = of_match_ptr("BUCK1"), .regulators_node = of_match_ptr("regulators"), .id = BD718XX_BUCK1, - .ops = &bd718xx_dvs_buck_regulator_ops, .type = REGULATOR_VOLTAGE, .n_voltages = BD718XX_DVS_BUCK_VOLTAGE_NUM, .linear_ranges = bd718xx_dvs_buck_volts, @@ -873,6 +984,7 @@ static const struct bd718xx_regulator_data bd71837_regulators[] = { .vsel_mask = DVS_BUCK_RUN_MASK, .enable_reg = BD718XX_REG_BUCK1_CTRL, .enable_mask = BD718XX_BUCK_EN, + .enable_time = BD71837_BUCK1_STARTUP_TIME, .owner = THIS_MODULE, .of_parse_cb = buck_set_hw_dvs_levels, }, @@ -898,7 +1010,6 @@ static const struct bd718xx_regulator_data bd71837_regulators[] = { .of_match = of_match_ptr("BUCK2"), .regulators_node = of_match_ptr("regulators"), .id = BD718XX_BUCK2, - .ops = &bd718xx_dvs_buck_regulator_ops, .type = REGULATOR_VOLTAGE, .n_voltages = BD718XX_DVS_BUCK_VOLTAGE_NUM, .linear_ranges = bd718xx_dvs_buck_volts, @@ -907,6 +1018,7 @@ static const struct bd718xx_regulator_data bd71837_regulators[] = { .vsel_mask = DVS_BUCK_RUN_MASK, .enable_reg = BD718XX_REG_BUCK2_CTRL, .enable_mask = BD718XX_BUCK_EN, + .enable_time = BD71837_BUCK2_STARTUP_TIME, .owner = THIS_MODULE, .of_parse_cb = buck_set_hw_dvs_levels, }, @@ -929,7 +1041,6 @@ static const struct bd718xx_regulator_data bd71837_regulators[] = { .of_match = of_match_ptr("BUCK3"), .regulators_node = of_match_ptr("regulators"), .id = BD718XX_BUCK3, - .ops = &bd718xx_dvs_buck_regulator_ops, .type = REGULATOR_VOLTAGE, .n_voltages = BD718XX_DVS_BUCK_VOLTAGE_NUM, .linear_ranges = bd718xx_dvs_buck_volts, @@ -938,6 +1049,7 @@ static const struct bd718xx_regulator_data bd71837_regulators[] = { .vsel_mask = DVS_BUCK_RUN_MASK, .enable_reg = BD71837_REG_BUCK3_CTRL, .enable_mask = BD718XX_BUCK_EN, + .enable_time = BD71837_BUCK3_STARTUP_TIME, .owner = THIS_MODULE, .of_parse_cb = buck_set_hw_dvs_levels, }, @@ -958,7 +1070,6 @@ static const struct bd718xx_regulator_data bd71837_regulators[] = { .of_match = of_match_ptr("BUCK4"), .regulators_node = of_match_ptr("regulators"), .id = BD718XX_BUCK4, - .ops = &bd718xx_dvs_buck_regulator_ops, .type = REGULATOR_VOLTAGE, .n_voltages = BD718XX_DVS_BUCK_VOLTAGE_NUM, .linear_ranges = bd718xx_dvs_buck_volts, @@ -967,6 +1078,7 @@ static const struct bd718xx_regulator_data bd71837_regulators[] = { .vsel_mask = DVS_BUCK_RUN_MASK, .enable_reg = BD71837_REG_BUCK4_CTRL, .enable_mask = BD718XX_BUCK_EN, + .enable_time = BD71837_BUCK4_STARTUP_TIME, .owner = THIS_MODULE, .of_parse_cb = buck_set_hw_dvs_levels, }, @@ -987,7 +1099,6 @@ static const struct bd718xx_regulator_data bd71837_regulators[] = { .of_match = of_match_ptr("BUCK5"), .regulators_node = of_match_ptr("regulators"), .id = BD718XX_BUCK5, - .ops = &bd71837_pickable_range_buck_ops, .type = REGULATOR_VOLTAGE, .n_voltages = BD71837_BUCK5_VOLTAGE_NUM, .linear_ranges = bd71837_buck5_volts, @@ -1000,6 +1111,7 @@ static const struct bd718xx_regulator_data bd71837_regulators[] = { .linear_range_selectors = bd71837_buck5_volt_range_sel, .enable_reg = BD718XX_REG_1ST_NODVS_BUCK_CTRL, .enable_mask = BD718XX_BUCK_EN, + .enable_time = BD71837_BUCK5_STARTUP_TIME, .owner = THIS_MODULE, }, .init = { @@ -1014,7 +1126,6 @@ static const struct bd718xx_regulator_data bd71837_regulators[] = { .of_match = of_match_ptr("BUCK6"), .regulators_node = of_match_ptr("regulators"), .id = BD718XX_BUCK6, - .ops = &bd71837_buck_regulator_ops, .type = REGULATOR_VOLTAGE, .n_voltages = BD71837_BUCK6_VOLTAGE_NUM, .linear_ranges = bd71837_buck6_volts, @@ -1024,6 +1135,7 @@ static const struct bd718xx_regulator_data bd71837_regulators[] = { .vsel_mask = BD71837_BUCK6_MASK, .enable_reg = BD718XX_REG_2ND_NODVS_BUCK_CTRL, .enable_mask = BD718XX_BUCK_EN, + .enable_time = BD71837_BUCK6_STARTUP_TIME, .owner = THIS_MODULE, }, .init = { @@ -1038,7 +1150,6 @@ static const struct bd718xx_regulator_data bd71837_regulators[] = { .of_match = of_match_ptr("BUCK7"), .regulators_node = of_match_ptr("regulators"), .id = BD718XX_BUCK7, - .ops = &bd71837_buck_regulator_nolinear_ops, .type = REGULATOR_VOLTAGE, .volt_table = &bd718xx_3rd_nodvs_buck_volts[0], .n_voltages = ARRAY_SIZE(bd718xx_3rd_nodvs_buck_volts), @@ -1046,6 +1157,7 @@ static const struct bd718xx_regulator_data bd71837_regulators[] = { .vsel_mask = BD718XX_3RD_NODVS_BUCK_MASK, .enable_reg = BD718XX_REG_3RD_NODVS_BUCK_CTRL, .enable_mask = BD718XX_BUCK_EN, + .enable_time = BD71837_BUCK7_STARTUP_TIME, .owner = THIS_MODULE, }, .init = { @@ -1060,7 +1172,6 @@ static const struct bd718xx_regulator_data bd71837_regulators[] = { .of_match = of_match_ptr("BUCK8"), .regulators_node = of_match_ptr("regulators"), .id = BD718XX_BUCK8, - .ops = &bd71837_buck_regulator_ops, .type = REGULATOR_VOLTAGE, .n_voltages = BD718XX_4TH_NODVS_BUCK_VOLTAGE_NUM, .linear_ranges = bd718xx_4th_nodvs_buck_volts, @@ -1070,6 +1181,7 @@ static const struct bd718xx_regulator_data bd71837_regulators[] = { .vsel_mask = BD718XX_4TH_NODVS_BUCK_MASK, .enable_reg = BD718XX_REG_4TH_NODVS_BUCK_CTRL, .enable_mask = BD718XX_BUCK_EN, + .enable_time = BD71837_BUCK8_STARTUP_TIME, .owner = THIS_MODULE, }, .init = { @@ -1084,7 +1196,6 @@ static const struct bd718xx_regulator_data bd71837_regulators[] = { .of_match = of_match_ptr("LDO1"), .regulators_node = of_match_ptr("regulators"), .id = BD718XX_LDO1, - .ops = &bd71837_pickable_range_ldo_ops, .type = REGULATOR_VOLTAGE, .n_voltages = BD718XX_LDO1_VOLTAGE_NUM, .linear_ranges = bd718xx_ldo1_volts, @@ -1096,6 +1207,7 @@ static const struct bd718xx_regulator_data bd71837_regulators[] = { .linear_range_selectors = bd718xx_ldo1_volt_range_sel, .enable_reg = BD718XX_REG_LDO1_VOLT, .enable_mask = BD718XX_LDO_EN, + .enable_time = BD71837_LDO1_STARTUP_TIME, .owner = THIS_MODULE, }, .init = { @@ -1110,7 +1222,6 @@ static const struct bd718xx_regulator_data bd71837_regulators[] = { .of_match = of_match_ptr("LDO2"), .regulators_node = of_match_ptr("regulators"), .id = BD718XX_LDO2, - .ops = &bd71837_ldo_regulator_nolinear_ops, .type = REGULATOR_VOLTAGE, .volt_table = &ldo_2_volts[0], .vsel_reg = BD718XX_REG_LDO2_VOLT, @@ -1118,6 +1229,7 @@ static const struct bd718xx_regulator_data bd71837_regulators[] = { .n_voltages = ARRAY_SIZE(ldo_2_volts), .enable_reg = BD718XX_REG_LDO2_VOLT, .enable_mask = BD718XX_LDO_EN, + .enable_time = BD71837_LDO2_STARTUP_TIME, .owner = THIS_MODULE, }, .init = { @@ -1132,7 +1244,6 @@ static const struct bd718xx_regulator_data bd71837_regulators[] = { .of_match = of_match_ptr("LDO3"), .regulators_node = of_match_ptr("regulators"), .id = BD718XX_LDO3, - .ops = &bd71837_ldo_regulator_ops, .type = REGULATOR_VOLTAGE, .n_voltages = BD718XX_LDO3_VOLTAGE_NUM, .linear_ranges = bd718xx_ldo3_volts, @@ -1141,6 +1252,7 @@ static const struct bd718xx_regulator_data bd71837_regulators[] = { .vsel_mask = BD718XX_LDO3_MASK, .enable_reg = BD718XX_REG_LDO3_VOLT, .enable_mask = BD718XX_LDO_EN, + .enable_time = BD71837_LDO3_STARTUP_TIME, .owner = THIS_MODULE, }, .init = { @@ -1155,7 +1267,6 @@ static const struct bd718xx_regulator_data bd71837_regulators[] = { .of_match = of_match_ptr("LDO4"), .regulators_node = of_match_ptr("regulators"), .id = BD718XX_LDO4, - .ops = &bd71837_ldo_regulator_ops, .type = REGULATOR_VOLTAGE, .n_voltages = BD718XX_LDO4_VOLTAGE_NUM, .linear_ranges = bd718xx_ldo4_volts, @@ -1164,6 +1275,7 @@ static const struct bd718xx_regulator_data bd71837_regulators[] = { .vsel_mask = BD718XX_LDO4_MASK, .enable_reg = BD718XX_REG_LDO4_VOLT, .enable_mask = BD718XX_LDO_EN, + .enable_time = BD71837_LDO4_STARTUP_TIME, .owner = THIS_MODULE, }, .init = { @@ -1178,7 +1290,6 @@ static const struct bd718xx_regulator_data bd71837_regulators[] = { .of_match = of_match_ptr("LDO5"), .regulators_node = of_match_ptr("regulators"), .id = BD718XX_LDO5, - .ops = &bd71837_ldo_regulator_ops, .type = REGULATOR_VOLTAGE, .n_voltages = BD71837_LDO5_VOLTAGE_NUM, .linear_ranges = bd71837_ldo5_volts, @@ -1189,6 +1300,7 @@ static const struct bd718xx_regulator_data bd71837_regulators[] = { .vsel_mask = BD71837_LDO5_MASK, .enable_reg = BD718XX_REG_LDO5_VOLT, .enable_mask = BD718XX_LDO_EN, + .enable_time = BD71837_LDO5_STARTUP_TIME, .owner = THIS_MODULE, }, .init = { @@ -1205,7 +1317,6 @@ static const struct bd718xx_regulator_data bd71837_regulators[] = { .of_match = of_match_ptr("LDO6"), .regulators_node = of_match_ptr("regulators"), .id = BD718XX_LDO6, - .ops = &bd71837_ldo_regulator_ops, .type = REGULATOR_VOLTAGE, .n_voltages = BD718XX_LDO6_VOLTAGE_NUM, .linear_ranges = bd718xx_ldo6_volts, @@ -1216,6 +1327,7 @@ static const struct bd718xx_regulator_data bd71837_regulators[] = { .vsel_mask = BD718XX_LDO6_MASK, .enable_reg = BD718XX_REG_LDO6_VOLT, .enable_mask = BD718XX_LDO_EN, + .enable_time = BD71837_LDO6_STARTUP_TIME, .owner = THIS_MODULE, }, .init = { @@ -1232,7 +1344,6 @@ static const struct bd718xx_regulator_data bd71837_regulators[] = { .of_match = of_match_ptr("LDO7"), .regulators_node = of_match_ptr("regulators"), .id = BD718XX_LDO7, - .ops = &bd71837_ldo_regulator_ops, .type = REGULATOR_VOLTAGE, .n_voltages = BD71837_LDO7_VOLTAGE_NUM, .linear_ranges = bd71837_ldo7_volts, @@ -1241,6 +1352,7 @@ static const struct bd718xx_regulator_data bd71837_regulators[] = { .vsel_mask = BD71837_LDO7_MASK, .enable_reg = BD71837_REG_LDO7_VOLT, .enable_mask = BD718XX_LDO_EN, + .enable_time = BD71837_LDO7_STARTUP_TIME, .owner = THIS_MODULE, }, .init = { @@ -1251,15 +1363,205 @@ static const struct bd718xx_regulator_data bd71837_regulators[] = { }, }; +static void mark_hw_controlled(struct device *dev, struct device_node *np, + struct bd718xx_regulator_data *reg_data, + unsigned int num_reg_data, int *info) +{ + int i; + + for (i = 1; i <= num_reg_data; i++) { + if (!of_node_name_eq(np, reg_data[i-1].desc.of_match)) + continue; + + *info |= 1 << (i - 1); + dev_dbg(dev, "regulator %d runlevel controlled\n", i); + return; + } + dev_warn(dev, "Bad regulator node\n"); +} + +/* + * Setups where regulator (especially the buck8) output voltage is scaled + * by adding external connection where some other regulator output is connected + * to feedback-pin (over suitable resistors) is getting popular amongst users + * of BD71837. (This allows for example scaling down the buck8 voltages to suit + * lover GPU voltages for projects where buck8 is (ab)used to supply power + * for GPU. Additionally some setups do allow DVS for buck8 but as this do + * produce voltage spikes the HW must be evaluated to be able to survive this + * - hence I keep the DVS disabled for non DVS bucks by default. I don't want + * to help you burn your proto board) + * + * So we allow describing this external connection from DT and scale the + * voltages accordingly. This is what the connection should look like: + * + * |------------| + * | buck 8 |-------+----->Vout + * | | | + * |------------| | + * | FB pin | + * | | + * +-------+--R2---+ + * | + * R1 + * | + * V FB-pull-up + * + * Here the buck output is sifted according to formula: + * + * Vout_o = Vo - (Vpu - Vo)*R2/R1 + * Linear_step = step_orig*(R1+R2)/R1 + * + * where: + * Vout_o is adjusted voltage output at vsel reg value 0 + * Vo is original voltage output at vsel reg value 0 + * Vpu is the pull-up voltage V FB-pull-up in the picture + * R1 and R2 are resistor values. + * + * As a real world example for buck8 and a specific GPU: + * VLDO = 1.6V (used as FB-pull-up) + * R1 = 1000ohms + * R2 = 150ohms + * VSEL 0x0 => 0.8V – (VLDO – 0.8) * R2 / R1 = 0.68V + * Linear Step = 10mV * (R1 + R2) / R1 = 11.5mV + */ +static int setup_feedback_loop(struct device *dev, struct device_node *np, + struct bd718xx_regulator_data *reg_data, + unsigned int num_reg_data, int fb_uv) +{ + int i, r1, r2, ret; + + /* + * We do adjust the values in the global desc based on DT settings. + * This may not be best approach as it can cause problems if more than + * one PMIC is controlled from same processor. I don't see such use-case + * for BD718x7 now - so we spare some bits. + * + * If this will point out to be a problem - then we can allocate new + * bd718xx_regulator_data array at probe and just use the global + * array as a template where we copy initial values. Then we can + * use allocated descs for regultor registration and do IC specific + * modifications to this copy while leaving other PMICs untouched. But + * that means allocating new array for each PMIC - and currently I see + * no need for that. + */ + + for (i = 0; i < num_reg_data; i++) { + struct regulator_desc *desc = ®_data[i].desc; + int j; + + if (!of_node_name_eq(np, desc->of_match)) + continue; + + pr_info("Looking at node '%s'\n", desc->of_match); + + /* The feedback loop connection does not make sense for LDOs */ + if (desc->id >= BD718XX_LDO1) + return -EINVAL; + + ret = of_property_read_u32(np, "rohm,feedback-pull-up-r1-ohms", + &r1); + if (ret) + return ret; + + if (!r1) + return -EINVAL; + + ret = of_property_read_u32(np, "rohm,feedback-pull-up-r2-ohms", + &r2); + if (ret) + return ret; + + if (desc->n_linear_ranges && desc->linear_ranges) { + struct linear_range *new; + + new = devm_kzalloc(dev, desc->n_linear_ranges * + sizeof(struct linear_range), + GFP_KERNEL); + if (!new) + return -ENOMEM; + + for (j = 0; j < desc->n_linear_ranges; j++) { + int min = desc->linear_ranges[j].min; + int step = desc->linear_ranges[j].step; + + min -= (fb_uv - min)*r2/r1; + step = step * (r1 + r2); + step /= r1; + + new[j].min = min; + new[j].step = step; + + dev_dbg(dev, "%s: old range min %d, step %d\n", + desc->name, desc->linear_ranges[j].min, + desc->linear_ranges[j].step); + dev_dbg(dev, "new range min %d, step %d\n", min, + step); + } + desc->linear_ranges = new; + } + dev_dbg(dev, "regulator '%s' has FB pull-up configured\n", + desc->name); + + return 0; + } + + return -ENODEV; +} + +static int get_special_regulators(struct device *dev, + struct bd718xx_regulator_data *reg_data, + unsigned int num_reg_data, int *info) +{ + int ret; + struct device_node *np; + struct device_node *nproot = dev->of_node; + int uv; + + *info = 0; + + nproot = of_get_child_by_name(nproot, "regulators"); + if (!nproot) { + dev_err(dev, "failed to find regulators node\n"); + return -ENODEV; + } + for_each_child_of_node(nproot, np) { + if (of_property_read_bool(np, "rohm,no-regulator-enable-control")) + mark_hw_controlled(dev, np, reg_data, num_reg_data, + info); + ret = of_property_read_u32(np, "rohm,fb-pull-up-microvolt", + &uv); + if (ret) { + if (ret == -EINVAL) + continue; + else + goto err_out; + } + + ret = setup_feedback_loop(dev, np, reg_data, num_reg_data, uv); + if (ret) + goto err_out; + } + + of_node_put(nproot); + return 0; + +err_out: + of_node_put(np); + of_node_put(nproot); + + return ret; +} + static int bd718xx_probe(struct platform_device *pdev) { struct bd718xx *mfd; struct regulator_config config = { 0 }; - int i, j, err; + int i, j, err, omit_enable; bool use_snvs; - const struct bd718xx_regulator_data *reg_data; + struct bd718xx_regulator_data *reg_data; unsigned int num_reg_data; 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) { @@ -1272,10 +1574,14 @@ static int bd718xx_probe(struct platform_device *pdev) case ROHM_CHIP_TYPE_BD71837: reg_data = bd71837_regulators; num_reg_data = ARRAY_SIZE(bd71837_regulators); + swops = &bd71837_swcontrol_ops[0]; + hwops = &bd71837_hwcontrol_ops[0]; break; case ROHM_CHIP_TYPE_BD71847: reg_data = bd71847_regulators; num_reg_data = ARRAY_SIZE(bd71847_regulators); + swops = &bd71847_swcontrol_ops[0]; + hwops = &bd71847_hwcontrol_ops[0]; break; default: dev_err(&pdev->dev, "Unsupported chip type\n"); @@ -1319,17 +1625,37 @@ static int bd718xx_probe(struct platform_device *pdev) } } + config.dev = pdev->dev.parent; + config.regmap = mfd->chip.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. + * One special case is when we use PMIC_STBY_REQ line from SoC to PMIC + * in order to set the system to SUSPEND state. + * + * If regulator is taken under SW control the regulator state will not + * be affected by PMIC state machine - Eg. regulator is likely to stay + * on even in SUSPEND + */ + err = get_special_regulators(pdev->dev.parent, reg_data, num_reg_data, + &omit_enable); + if (err) + return err; + for (i = 0; i < num_reg_data; i++) { - const struct regulator_desc *desc; + struct regulator_desc *desc; struct regulator_dev *rdev; - const struct bd718xx_regulator_data *r; + struct bd718xx_regulator_data *r; + int no_enable_control = omit_enable & (1 << i); r = ®_data[i]; desc = &r->desc; - config.dev = pdev->dev.parent; - config.regmap = mfd->chip.regmap; + if (no_enable_control) + desc->ops = hwops[i]; + else + desc->ops = swops[i]; rdev = devm_regulator_register(&pdev->dev, desc, &config); if (IS_ERR(rdev)) { @@ -1356,8 +1682,9 @@ static int bd718xx_probe(struct platform_device *pdev) * enable SW control for crucial regulators if snvs state is * used */ - if (!use_snvs || !rdev->constraints->always_on || - !rdev->constraints->boot_on) { + if (!no_enable_control && (!use_snvs || + !rdev->constraints->always_on || + !rdev->constraints->boot_on)) { err = regmap_update_bits(mfd->chip.regmap, r->init.reg, r->init.mask, r->init.val); if (err) { diff --git a/drivers/regulator/bd9576-regulator.c b/drivers/regulator/bd9576-regulator.c new file mode 100644 index 000000000000..a8b5832a5a1b --- /dev/null +++ b/drivers/regulator/bd9576-regulator.c @@ -0,0 +1,337 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2020 ROHM Semiconductors +// ROHM BD9576MUF/BD9573MUF regulator driver + +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/gpio/consumer.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/mfd/rohm-bd957x.h> +#include <linux/mfd/rohm-generic.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> +#include <linux/regulator/of_regulator.h> +#include <linux/slab.h> + +#define BD957X_VOUTS1_VOLT 3300000 +#define BD957X_VOUTS4_BASE_VOLT 1030000 +#define BD957X_VOUTS34_NUM_VOLT 32 + +static int vout1_volt_table[] = {5000000, 4900000, 4800000, 4700000, 4600000, + 4500000, 4500000, 4500000, 5000000, 5100000, + 5200000, 5300000, 5400000, 5500000, 5500000, + 5500000}; + +static int vout2_volt_table[] = {1800000, 1780000, 1760000, 1740000, 1720000, + 1700000, 1680000, 1660000, 1800000, 1820000, + 1840000, 1860000, 1880000, 1900000, 1920000, + 1940000}; + +static int voutl1_volt_table[] = {2500000, 2540000, 2580000, 2620000, 2660000, + 2700000, 2740000, 2780000, 2500000, 2460000, + 2420000, 2380000, 2340000, 2300000, 2260000, + 2220000}; + +struct bd957x_regulator_data { + struct regulator_desc desc; + int base_voltage; +}; + +static int bd957x_vout34_list_voltage(struct regulator_dev *rdev, + unsigned int selector) +{ + const struct regulator_desc *desc = rdev->desc; + int multiplier = selector & desc->vsel_mask & 0x7f; + int tune; + + /* VOUT3 and 4 has 10mV step */ + tune = multiplier * 10000; + + if (!(selector & 0x80)) + return desc->fixed_uV - tune; + + return desc->fixed_uV + tune; +} + +static int bd957x_list_voltage(struct regulator_dev *rdev, + unsigned int selector) +{ + const struct regulator_desc *desc = rdev->desc; + int index = selector & desc->vsel_mask & 0x7f; + + if (!(selector & 0x80)) + index += desc->n_voltages/2; + + if (index >= desc->n_voltages) + return -EINVAL; + + return desc->volt_table[index]; +} + +static const struct regulator_ops bd957x_vout34_ops = { + .is_enabled = regulator_is_enabled_regmap, + .list_voltage = bd957x_vout34_list_voltage, + .get_voltage_sel = regulator_get_voltage_sel_regmap, +}; + +static const struct regulator_ops bd957X_vouts1_regulator_ops = { + .is_enabled = regulator_is_enabled_regmap, +}; + +static const struct regulator_ops bd957x_ops = { + .is_enabled = regulator_is_enabled_regmap, + .list_voltage = bd957x_list_voltage, + .get_voltage_sel = regulator_get_voltage_sel_regmap, +}; + +static struct bd957x_regulator_data bd9576_regulators[] = { + { + .desc = { + .name = "VD50", + .of_match = of_match_ptr("regulator-vd50"), + .regulators_node = of_match_ptr("regulators"), + .id = BD957X_VD50, + .type = REGULATOR_VOLTAGE, + .ops = &bd957x_ops, + .volt_table = &vout1_volt_table[0], + .n_voltages = ARRAY_SIZE(vout1_volt_table), + .vsel_reg = BD957X_REG_VOUT1_TUNE, + .vsel_mask = BD957X_MASK_VOUT1_TUNE, + .enable_reg = BD957X_REG_POW_TRIGGER1, + .enable_mask = BD957X_REGULATOR_EN_MASK, + .enable_val = BD957X_REGULATOR_DIS_VAL, + .enable_is_inverted = true, + .owner = THIS_MODULE, + }, + }, + { + .desc = { + .name = "VD18", + .of_match = of_match_ptr("regulator-vd18"), + .regulators_node = of_match_ptr("regulators"), + .id = BD957X_VD18, + .type = REGULATOR_VOLTAGE, + .ops = &bd957x_ops, + .volt_table = &vout2_volt_table[0], + .n_voltages = ARRAY_SIZE(vout2_volt_table), + .vsel_reg = BD957X_REG_VOUT2_TUNE, + .vsel_mask = BD957X_MASK_VOUT2_TUNE, + .enable_reg = BD957X_REG_POW_TRIGGER2, + .enable_mask = BD957X_REGULATOR_EN_MASK, + .enable_val = BD957X_REGULATOR_DIS_VAL, + .enable_is_inverted = true, + .owner = THIS_MODULE, + }, + }, + { + .desc = { + .name = "VDDDR", + .of_match = of_match_ptr("regulator-vdddr"), + .regulators_node = of_match_ptr("regulators"), + .id = BD957X_VDDDR, + .ops = &bd957x_vout34_ops, + .type = REGULATOR_VOLTAGE, + .n_voltages = BD957X_VOUTS34_NUM_VOLT, + .vsel_reg = BD957X_REG_VOUT3_TUNE, + .vsel_mask = BD957X_MASK_VOUT3_TUNE, + .enable_reg = BD957X_REG_POW_TRIGGER3, + .enable_mask = BD957X_REGULATOR_EN_MASK, + .enable_val = BD957X_REGULATOR_DIS_VAL, + .enable_is_inverted = true, + .owner = THIS_MODULE, + }, + }, + { + .desc = { + .name = "VD10", + .of_match = of_match_ptr("regulator-vd10"), + .regulators_node = of_match_ptr("regulators"), + .id = BD957X_VD10, + .ops = &bd957x_vout34_ops, + .type = REGULATOR_VOLTAGE, + .fixed_uV = BD957X_VOUTS4_BASE_VOLT, + .n_voltages = BD957X_VOUTS34_NUM_VOLT, + .vsel_reg = BD957X_REG_VOUT4_TUNE, + .vsel_mask = BD957X_MASK_VOUT4_TUNE, + .enable_reg = BD957X_REG_POW_TRIGGER4, + .enable_mask = BD957X_REGULATOR_EN_MASK, + .enable_val = BD957X_REGULATOR_DIS_VAL, + .enable_is_inverted = true, + .owner = THIS_MODULE, + }, + }, + { + .desc = { + .name = "VOUTL1", + .of_match = of_match_ptr("regulator-voutl1"), + .regulators_node = of_match_ptr("regulators"), + .id = BD957X_VOUTL1, + .ops = &bd957x_ops, + .type = REGULATOR_VOLTAGE, + .volt_table = &voutl1_volt_table[0], + .n_voltages = ARRAY_SIZE(voutl1_volt_table), + .vsel_reg = BD957X_REG_VOUTL1_TUNE, + .vsel_mask = BD957X_MASK_VOUTL1_TUNE, + .enable_reg = BD957X_REG_POW_TRIGGERL1, + .enable_mask = BD957X_REGULATOR_EN_MASK, + .enable_val = BD957X_REGULATOR_DIS_VAL, + .enable_is_inverted = true, + .owner = THIS_MODULE, + }, + }, + { + .desc = { + .name = "VOUTS1", + .of_match = of_match_ptr("regulator-vouts1"), + .regulators_node = of_match_ptr("regulators"), + .id = BD957X_VOUTS1, + .ops = &bd957X_vouts1_regulator_ops, + .type = REGULATOR_VOLTAGE, + .n_voltages = 1, + .fixed_uV = BD957X_VOUTS1_VOLT, + .enable_reg = BD957X_REG_POW_TRIGGERS1, + .enable_mask = BD957X_REGULATOR_EN_MASK, + .enable_val = BD957X_REGULATOR_DIS_VAL, + .enable_is_inverted = true, + .owner = THIS_MODULE, + }, + }, +}; + +static int bd957x_probe(struct platform_device *pdev) +{ + struct regmap *regmap; + struct regulator_config config = { 0 }; + int i, err; + bool vout_mode, ddr_sel; + const struct bd957x_regulator_data *reg_data = &bd9576_regulators[0]; + unsigned int num_reg_data = ARRAY_SIZE(bd9576_regulators); + enum rohm_chip_type chip = platform_get_device_id(pdev)->driver_data; + + regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!regmap) { + dev_err(&pdev->dev, "No regmap\n"); + return -EINVAL; + } + vout_mode = of_property_read_bool(pdev->dev.parent->of_node, + "rohm,vout1-en-low"); + if (vout_mode) { + struct gpio_desc *en; + + dev_dbg(&pdev->dev, "GPIO controlled mode\n"); + + /* VOUT1 enable state judged by VOUT1_EN pin */ + /* See if we have GPIO defined */ + en = devm_gpiod_get_from_of_node(&pdev->dev, + pdev->dev.parent->of_node, + "rohm,vout1-en-gpios", 0, + GPIOD_OUT_LOW, "vout1-en"); + if (!IS_ERR(en)) { + /* VOUT1_OPS gpio ctrl */ + /* + * Regulator core prioritizes the ena_gpio over + * enable/disable/is_enabled callbacks so no need to + * clear them. We can still use same ops + */ + config.ena_gpiod = en; + } else { + /* + * In theory it is possible someone wants to set + * vout1-en LOW during OTP loading and set VOUT1 to be + * controlled by GPIO - but control the GPIO from some + * where else than this driver. For that to work we + * should unset the is_enabled callback here. + * + * I believe such case where rohm,vout1-en-low is set + * and vout1-en-gpios is not is likely to be a + * misconfiguration. So let's just err out for now. + */ + dev_err(&pdev->dev, + "Failed to get VOUT1 control GPIO\n"); + return PTR_ERR(en); + } + } + + /* + * If more than one PMIC needs to be controlled by same processor then + * allocate the regulator data array here and use bd9576_regulators as + * template. At the moment I see no such use-case so I spare some + * bytes and use bd9576_regulators directly for non-constant configs + * like DDR voltage selection. + */ + ddr_sel = of_property_read_bool(pdev->dev.parent->of_node, + "rohm,ddr-sel-low"); + if (ddr_sel) + bd9576_regulators[2].desc.fixed_uV = 1350000; + else + bd9576_regulators[2].desc.fixed_uV = 1500000; + + switch (chip) { + case ROHM_CHIP_TYPE_BD9576: + dev_dbg(&pdev->dev, "Found BD9576MUF\n"); + break; + case ROHM_CHIP_TYPE_BD9573: + dev_dbg(&pdev->dev, "Found BD9573MUF\n"); + break; + default: + dev_err(&pdev->dev, "Unsupported chip type\n"); + err = -EINVAL; + goto err; + } + + config.dev = pdev->dev.parent; + config.regmap = regmap; + + for (i = 0; i < num_reg_data; i++) { + + const struct regulator_desc *desc; + struct regulator_dev *rdev; + const struct bd957x_regulator_data *r; + + r = ®_data[i]; + desc = &r->desc; + + rdev = devm_regulator_register(&pdev->dev, desc, &config); + if (IS_ERR(rdev)) { + dev_err(&pdev->dev, + "failed to register %s regulator\n", + desc->name); + err = PTR_ERR(rdev); + goto err; + } + /* + * Clear the VOUT1 GPIO setting - rest of the regulators do not + * support GPIO control + */ + config.ena_gpiod = NULL; + } + +err: + return err; +} + +static const struct platform_device_id bd957x_pmic_id[] = { + { "bd9573-pmic", ROHM_CHIP_TYPE_BD9573 }, + { "bd9576-pmic", ROHM_CHIP_TYPE_BD9576 }, + { }, +}; +MODULE_DEVICE_TABLE(platform, bd957x_pmic_id); + +static struct platform_driver bd957x_regulator = { + .driver = { + .name = "bd957x-pmic", + }, + .probe = bd957x_probe, + .id_table = bd957x_pmic_id, +}; + +module_platform_driver(bd957x_regulator); + +MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>"); +MODULE_DESCRIPTION("ROHM BD9576/BD9573 voltage regulator driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:bd957x-pmic"); diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 7ff507ec875a..67a768fe5b2a 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -190,11 +190,10 @@ static inline int regulator_lock_nested(struct regulator_dev *rdev, * than the one, which initially locked the mutex, it will * wait on mutex. */ -void regulator_lock(struct regulator_dev *rdev) +static void regulator_lock(struct regulator_dev *rdev) { regulator_lock_nested(rdev, NULL); } -EXPORT_SYMBOL_GPL(regulator_lock); /** * regulator_unlock - unlock a single regulator @@ -203,7 +202,7 @@ EXPORT_SYMBOL_GPL(regulator_lock); * This function unlocks the mutex when the * reference counter reaches 0. */ -void regulator_unlock(struct regulator_dev *rdev) +static void regulator_unlock(struct regulator_dev *rdev) { mutex_lock(®ulator_nesting_mutex); @@ -216,7 +215,6 @@ void regulator_unlock(struct regulator_dev *rdev) mutex_unlock(®ulator_nesting_mutex); } -EXPORT_SYMBOL_GPL(regulator_unlock); static bool regulator_supply_is_couple(struct regulator_dev *rdev) { @@ -409,11 +407,11 @@ err_node_put: static struct device_node *of_get_regulator(struct device *dev, const char *supply) { struct device_node *regnode = NULL; - char prop_name[32]; /* 32 is max size of property name */ + char prop_name[64]; /* 64 is max size of property name */ dev_dbg(dev, "Looking up %s-supply from device tree\n", supply); - snprintf(prop_name, 32, "%s-supply", supply); + snprintf(prop_name, 64, "%s-supply", supply); regnode = of_parse_phandle(dev->of_node, prop_name, 0); if (!regnode) { @@ -568,6 +566,30 @@ regulator_get_suspend_state(struct regulator_dev *rdev, suspend_state_t state) } } +static const struct regulator_state * +regulator_get_suspend_state_check(struct regulator_dev *rdev, suspend_state_t state) +{ + const struct regulator_state *rstate; + + rstate = regulator_get_suspend_state(rdev, state); + if (rstate == NULL) + return NULL; + + /* If we have no suspend mode configuration don't set anything; + * only warn if the driver implements set_suspend_voltage or + * set_suspend_mode callback. + */ + if (rstate->enabled != ENABLE_IN_SUSPEND && + rstate->enabled != DISABLE_IN_SUSPEND) { + if (rdev->desc->ops->set_suspend_voltage || + rdev->desc->ops->set_suspend_mode) + rdev_warn(rdev, "No configuration\n"); + return NULL; + } + + return rstate; +} + static ssize_t regulator_uV_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -945,7 +967,8 @@ static int drms_uA_update(struct regulator_dev *rdev) /* set the optimum mode for our new total regulator load */ err = rdev->desc->ops->set_load(rdev, current_uA); if (err < 0) - rdev_err(rdev, "failed to set load %d\n", current_uA); + rdev_err(rdev, "failed to set load %d: %pe\n", + current_uA, ERR_PTR(err)); } else { /* get output voltage */ output_uV = regulator_get_voltage_rdev(rdev); @@ -972,40 +995,24 @@ static int drms_uA_update(struct regulator_dev *rdev) /* check the new mode is allowed */ err = regulator_mode_constrain(rdev, &mode); if (err < 0) { - rdev_err(rdev, "failed to get optimum mode @ %d uA %d -> %d uV\n", - current_uA, input_uV, output_uV); + rdev_err(rdev, "failed to get optimum mode @ %d uA %d -> %d uV: %pe\n", + current_uA, input_uV, output_uV, ERR_PTR(err)); return err; } err = rdev->desc->ops->set_mode(rdev, mode); if (err < 0) - rdev_err(rdev, "failed to set optimum mode %x\n", mode); + rdev_err(rdev, "failed to set optimum mode %x: %pe\n", + mode, ERR_PTR(err)); } return err; } -static int suspend_set_state(struct regulator_dev *rdev, - suspend_state_t state) +static int __suspend_set_state(struct regulator_dev *rdev, + const struct regulator_state *rstate) { int ret = 0; - struct regulator_state *rstate; - - rstate = regulator_get_suspend_state(rdev, state); - if (rstate == NULL) - return 0; - - /* If we have no suspend mode configuration don't set anything; - * only warn if the driver implements set_suspend_voltage or - * set_suspend_mode callback. - */ - if (rstate->enabled != ENABLE_IN_SUSPEND && - rstate->enabled != DISABLE_IN_SUSPEND) { - if (rdev->desc->ops->set_suspend_voltage || - rdev->desc->ops->set_suspend_mode) - rdev_warn(rdev, "No configuration\n"); - return 0; - } if (rstate->enabled == ENABLE_IN_SUSPEND && rdev->desc->ops->set_suspend_enable) @@ -1017,14 +1024,14 @@ static int suspend_set_state(struct regulator_dev *rdev, ret = 0; if (ret < 0) { - rdev_err(rdev, "failed to enabled/disable\n"); + rdev_err(rdev, "failed to enabled/disable: %pe\n", ERR_PTR(ret)); return ret; } if (rdev->desc->ops->set_suspend_voltage && rstate->uV > 0) { ret = rdev->desc->ops->set_suspend_voltage(rdev, rstate->uV); if (ret < 0) { - rdev_err(rdev, "failed to set voltage\n"); + rdev_err(rdev, "failed to set voltage: %pe\n", ERR_PTR(ret)); return ret; } } @@ -1032,7 +1039,7 @@ static int suspend_set_state(struct regulator_dev *rdev, if (rdev->desc->ops->set_suspend_mode && rstate->mode > 0) { ret = rdev->desc->ops->set_suspend_mode(rdev, rstate->mode); if (ret < 0) { - rdev_err(rdev, "failed to set mode\n"); + rdev_err(rdev, "failed to set mode: %pe\n", ERR_PTR(ret)); return ret; } } @@ -1040,7 +1047,20 @@ static int suspend_set_state(struct regulator_dev *rdev, return ret; } -static void print_constraints(struct regulator_dev *rdev) +static int suspend_set_initial_state(struct regulator_dev *rdev) +{ + const struct regulator_state *rstate; + + rstate = regulator_get_suspend_state_check(rdev, + rdev->constraints->initial_state); + if (!rstate) + return 0; + + return __suspend_set_state(rdev, rstate); +} + +#if defined(DEBUG) || defined(CONFIG_DYNAMIC_DEBUG) +static void print_constraints_debug(struct regulator_dev *rdev) { struct regulation_constraints *constraints = rdev->constraints; char buf[160] = ""; @@ -1097,12 +1117,27 @@ static void print_constraints(struct regulator_dev *rdev) if (constraints->valid_modes_mask & REGULATOR_MODE_IDLE) count += scnprintf(buf + count, len - count, "idle "); if (constraints->valid_modes_mask & REGULATOR_MODE_STANDBY) - count += scnprintf(buf + count, len - count, "standby"); + count += scnprintf(buf + count, len - count, "standby "); if (!count) - scnprintf(buf, len, "no parameters"); + count = scnprintf(buf, len, "no parameters"); + else + --count; + + count += scnprintf(buf + count, len - count, ", %s", + _regulator_is_enabled(rdev) ? "enabled" : "disabled"); rdev_dbg(rdev, "%s\n", buf); +} +#else /* !DEBUG && !CONFIG_DYNAMIC_DEBUG */ +static inline void print_constraints_debug(struct regulator_dev *rdev) {} +#endif /* !DEBUG && !CONFIG_DYNAMIC_DEBUG */ + +static void print_constraints(struct regulator_dev *rdev) +{ + struct regulation_constraints *constraints = rdev->constraints; + + print_constraints_debug(rdev); if ((constraints->min_uV != constraints->max_uV) && !regulator_ops_is_valid(rdev, REGULATOR_CHANGE_VOLTAGE)) @@ -1135,8 +1170,8 @@ static int machine_constraints_voltage(struct regulator_dev *rdev, if (current_uV < 0) { rdev_err(rdev, - "failed to get the current voltage(%d)\n", - current_uV); + "failed to get the current voltage: %pe\n", + ERR_PTR(current_uV)); return current_uV; } @@ -1165,8 +1200,8 @@ static int machine_constraints_voltage(struct regulator_dev *rdev, rdev, target_min, target_max); if (ret < 0) { rdev_err(rdev, - "failed to apply %d-%duV constraint(%d)\n", - target_min, target_max, ret); + "failed to apply %d-%duV constraint: %pe\n", + target_min, target_max, ERR_PTR(ret)); return ret; } } @@ -1280,7 +1315,6 @@ static int _regulator_do_enable(struct regulator_dev *rdev); /** * set_machine_constraints - sets regulator constraints * @rdev: regulator source - * @constraints: constraints to apply * * Allows platform initialisation code to define and constrain * regulator circuits e.g. valid voltage/current ranges, etc. NOTE: @@ -1288,21 +1322,11 @@ static int _regulator_do_enable(struct regulator_dev *rdev); * regulator operations to proceed i.e. set_voltage, set_current_limit, * set_mode. */ -static int set_machine_constraints(struct regulator_dev *rdev, - const struct regulation_constraints *constraints) +static int set_machine_constraints(struct regulator_dev *rdev) { int ret = 0; const struct regulator_ops *ops = rdev->desc->ops; - if (constraints) - rdev->constraints = kmemdup(constraints, sizeof(*constraints), - GFP_KERNEL); - else - rdev->constraints = kzalloc(sizeof(*constraints), - GFP_KERNEL); - if (!rdev->constraints) - return -ENOMEM; - ret = machine_constraints_voltage(rdev, rdev->constraints); if (ret != 0) return ret; @@ -1315,16 +1339,16 @@ static int set_machine_constraints(struct regulator_dev *rdev, ret = ops->set_input_current_limit(rdev, rdev->constraints->ilim_uA); if (ret < 0) { - rdev_err(rdev, "failed to set input limit\n"); + rdev_err(rdev, "failed to set input limit: %pe\n", ERR_PTR(ret)); return ret; } } /* do we need to setup our suspend state */ if (rdev->constraints->initial_state) { - ret = suspend_set_state(rdev, rdev->constraints->initial_state); + ret = suspend_set_initial_state(rdev); if (ret < 0) { - rdev_err(rdev, "failed to set suspend state\n"); + rdev_err(rdev, "failed to set suspend state: %pe\n", ERR_PTR(ret)); return ret; } } @@ -1337,7 +1361,7 @@ static int set_machine_constraints(struct regulator_dev *rdev, ret = ops->set_mode(rdev, rdev->constraints->initial_mode); if (ret < 0) { - rdev_err(rdev, "failed to set initial mode: %d\n", ret); + rdev_err(rdev, "failed to set initial mode: %pe\n", ERR_PTR(ret)); return ret; } } else if (rdev->constraints->system_load) { @@ -1352,7 +1376,7 @@ static int set_machine_constraints(struct regulator_dev *rdev, && ops->set_ramp_delay) { ret = ops->set_ramp_delay(rdev, rdev->constraints->ramp_delay); if (ret < 0) { - rdev_err(rdev, "failed to set ramp_delay\n"); + rdev_err(rdev, "failed to set ramp_delay: %pe\n", ERR_PTR(ret)); return ret; } } @@ -1360,7 +1384,7 @@ static int set_machine_constraints(struct regulator_dev *rdev, if (rdev->constraints->pull_down && ops->set_pull_down) { ret = ops->set_pull_down(rdev); if (ret < 0) { - rdev_err(rdev, "failed to set pull down\n"); + rdev_err(rdev, "failed to set pull down: %pe\n", ERR_PTR(ret)); return ret; } } @@ -1368,7 +1392,7 @@ static int set_machine_constraints(struct regulator_dev *rdev, if (rdev->constraints->soft_start && ops->set_soft_start) { ret = ops->set_soft_start(rdev); if (ret < 0) { - rdev_err(rdev, "failed to set soft start\n"); + rdev_err(rdev, "failed to set soft start: %pe\n", ERR_PTR(ret)); return ret; } } @@ -1377,7 +1401,8 @@ static int set_machine_constraints(struct regulator_dev *rdev, && ops->set_over_current_protection) { ret = ops->set_over_current_protection(rdev); if (ret < 0) { - rdev_err(rdev, "failed to set over current protection\n"); + rdev_err(rdev, "failed to set over current protection: %pe\n", + ERR_PTR(ret)); return ret; } } @@ -1388,7 +1413,7 @@ static int set_machine_constraints(struct regulator_dev *rdev, ret = ops->set_active_discharge(rdev, ad_state); if (ret < 0) { - rdev_err(rdev, "failed to set active discharge\n"); + rdev_err(rdev, "failed to set active discharge: %pe\n", ERR_PTR(ret)); return ret; } } @@ -1408,7 +1433,7 @@ static int set_machine_constraints(struct regulator_dev *rdev, ret = _regulator_do_enable(rdev); if (ret < 0 && ret != -EINVAL) { - rdev_err(rdev, "failed to enable\n"); + rdev_err(rdev, "failed to enable: %pe\n", ERR_PTR(ret)); return ret; } @@ -1632,8 +1657,8 @@ static struct regulator *create_regulator(struct regulator_dev *rdev, err = sysfs_create_link_nowarn(&rdev->dev.kobj, &dev->kobj, supply_name); if (err) { - rdev_dbg(rdev, "could not add device link %s err %d\n", - dev->kobj.name, err); + rdev_dbg(rdev, "could not add device link %s: %pe\n", + dev->kobj.name, ERR_PTR(err)); /* non-fatal */ } } @@ -1788,13 +1813,13 @@ static int regulator_resolve_supply(struct regulator_dev *rdev) { struct regulator_dev *r; struct device *dev = rdev->dev.parent; - int ret; + int ret = 0; /* No supply to resolve? */ if (!rdev->supply_name) return 0; - /* Supply already resolved? */ + /* Supply already resolved? (fast-path without locking contention) */ if (rdev->supply) return 0; @@ -1804,7 +1829,7 @@ static int regulator_resolve_supply(struct regulator_dev *rdev) /* Did the lookup explicitly defer for us? */ if (ret == -EPROBE_DEFER) - return ret; + goto out; if (have_full_constraints()) { r = dummy_regulator_rdev; @@ -1812,10 +1837,22 @@ static int regulator_resolve_supply(struct regulator_dev *rdev) } else { dev_err(dev, "Failed to resolve %s-supply for %s\n", rdev->supply_name, rdev->desc->name); - return -EPROBE_DEFER; + ret = -EPROBE_DEFER; + goto out; } } + if (r == rdev) { + dev_err(dev, "Supply for %s (%s) resolved to itself\n", + rdev->desc->name, rdev->supply_name); + if (!have_full_constraints()) { + ret = -EINVAL; + goto out; + } + r = dummy_regulator_rdev; + get_device(&r->dev); + } + /* * If the supply's parent device is not the same as the * regulator's parent device, then ensure the parent device @@ -1825,7 +1862,8 @@ static int regulator_resolve_supply(struct regulator_dev *rdev) if (r->dev.parent && r->dev.parent != rdev->dev.parent) { if (!device_is_bound(r->dev.parent)) { put_device(&r->dev); - return -EPROBE_DEFER; + ret = -EPROBE_DEFER; + goto out; } } @@ -1833,15 +1871,32 @@ static int regulator_resolve_supply(struct regulator_dev *rdev) ret = regulator_resolve_supply(r); if (ret < 0) { put_device(&r->dev); - return ret; + goto out; + } + + /* + * Recheck rdev->supply with rdev->mutex lock held to avoid a race + * between rdev->supply null check and setting rdev->supply in + * set_supply() from concurrent tasks. + */ + regulator_lock(rdev); + + /* Supply just resolved by a concurrent task? */ + if (rdev->supply) { + regulator_unlock(rdev); + put_device(&r->dev); + goto out; } ret = set_supply(rdev, r); if (ret < 0) { + regulator_unlock(rdev); put_device(&r->dev); - return ret; + goto out; } + regulator_unlock(rdev); + /* * In set_machine_constraints() we may have turned this regulator on * but we couldn't propagate to the supply if it hadn't been resolved @@ -1852,11 +1907,12 @@ static int regulator_resolve_supply(struct regulator_dev *rdev) if (ret < 0) { _regulator_put(rdev->supply); rdev->supply = NULL; - return ret; + goto out; } } - return 0; +out: + return ret; } /* Internal regulator request function */ @@ -2421,7 +2477,7 @@ static int _regulator_do_enable(struct regulator_dev *rdev) if (ret >= 0) { delay = ret; } else { - rdev_warn(rdev, "enable_time() failed: %d\n", ret); + rdev_warn(rdev, "enable_time() failed: %pe\n", ERR_PTR(ret)); delay = 0; } @@ -2610,7 +2666,7 @@ static int _regulator_enable(struct regulator *regulator) _notifier_call_chain(rdev, REGULATOR_EVENT_ENABLE, NULL); } else if (ret < 0) { - rdev_err(rdev, "is_enabled() failed: %d\n", ret); + rdev_err(rdev, "is_enabled() failed: %pe\n", ERR_PTR(ret)); goto err_consumer_disable; } /* Fallthrough on positive return values - already enabled */ @@ -2712,7 +2768,7 @@ static int _regulator_disable(struct regulator *regulator) ret = _regulator_do_disable(rdev); if (ret < 0) { - rdev_err(rdev, "failed to disable\n"); + rdev_err(rdev, "failed to disable: %pe\n", ERR_PTR(ret)); _notifier_call_chain(rdev, REGULATOR_EVENT_ABORT_DISABLE, NULL); @@ -2779,7 +2835,7 @@ static int _regulator_force_disable(struct regulator_dev *rdev) ret = _regulator_do_disable(rdev); if (ret < 0) { - rdev_err(rdev, "failed to force disable\n"); + rdev_err(rdev, "failed to force disable: %pe\n", ERR_PTR(ret)); _notifier_call_chain(rdev, REGULATOR_EVENT_FORCE_DISABLE | REGULATOR_EVENT_ABORT_DISABLE, NULL); return ret; @@ -2858,7 +2914,8 @@ static void regulator_disable_work(struct work_struct *work) for (i = 0; i < count; i++) { ret = _regulator_disable(regulator); if (ret != 0) - rdev_err(rdev, "Deferred disable failed: %d\n", ret); + rdev_err(rdev, "Deferred disable failed: %pe\n", + ERR_PTR(ret)); } } WARN_ON(!total_count); @@ -2923,6 +2980,8 @@ static int _regulator_list_voltage(struct regulator_dev *rdev, if (ops->list_voltage) { if (selector >= rdev->desc->n_voltages) return -EINVAL; + if (selector < rdev->desc->linear_min_sel) + return 0; if (lock) regulator_lock(rdev); ret = ops->list_voltage(rdev, selector); @@ -3051,7 +3110,7 @@ int regulator_get_hardware_vsel_register(struct regulator *regulator, *vsel_reg = rdev->desc->vsel_reg; *vsel_mask = rdev->desc->vsel_mask; - return 0; + return 0; } EXPORT_SYMBOL_GPL(regulator_get_hardware_vsel_register); @@ -3074,6 +3133,8 @@ int regulator_list_hardware_vsel(struct regulator *regulator, if (selector >= rdev->desc->n_voltages) return -EINVAL; + if (selector < rdev->desc->linear_min_sel) + return 0; if (ops->set_voltage_sel != regulator_set_voltage_sel_regmap) return -EOPNOTSUPP; @@ -3383,7 +3444,7 @@ static int _regulator_do_set_voltage(struct regulator_dev *rdev, } if (delay < 0) { - rdev_warn(rdev, "failed to get delay: %d\n", delay); + rdev_warn(rdev, "failed to get delay: %pe\n", ERR_PTR(delay)); delay = 0; } @@ -3535,8 +3596,8 @@ int regulator_set_voltage_rdev(struct regulator_dev *rdev, int min_uV, ret = regulator_set_voltage_unlocked(rdev->supply, best_supply_uV, INT_MAX, state); if (ret) { - dev_err(&rdev->dev, "Failed to increase supply voltage: %d\n", - ret); + dev_err(&rdev->dev, "Failed to increase supply voltage: %pe\n", + ERR_PTR(ret)); goto out; } } @@ -3553,8 +3614,8 @@ int regulator_set_voltage_rdev(struct regulator_dev *rdev, int min_uV, ret = regulator_set_voltage_unlocked(rdev->supply, best_supply_uV, INT_MAX, state); if (ret) - dev_warn(&rdev->dev, "Failed to decrease supply voltage: %d\n", - ret); + dev_warn(&rdev->dev, "Failed to decrease supply voltage: %pe\n", + ERR_PTR(ret)); /* No need to fail here */ ret = 0; } @@ -3995,6 +4056,12 @@ int regulator_set_voltage_time(struct regulator *regulator, for (i = 0; i < rdev->desc->n_voltages; i++) { /* We only look for exact voltage matches here */ + if (i < rdev->desc->linear_min_sel) + continue; + + if (old_sel >= 0 && new_sel >= 0) + break; + voltage = regulator_list_voltage(regulator, i); if (voltage < 0) return -EINVAL; @@ -4128,6 +4195,8 @@ int regulator_get_voltage_rdev(struct regulator_dev *rdev) ret = rdev->desc->fixed_uV; } else if (rdev->supply) { ret = regulator_get_voltage_rdev(rdev->supply->rdev); + } else if (rdev->supply_name) { + return -EPROBE_DEFER; } else { return -EINVAL; } @@ -4540,8 +4609,8 @@ int regulator_bulk_get(struct device *dev, int num_consumers, err: if (ret != -EPROBE_DEFER) - dev_err(dev, "Failed to get supply '%s': %d\n", - consumers[i].supply, ret); + dev_err(dev, "Failed to get supply '%s': %pe\n", + consumers[i].supply, ERR_PTR(ret)); else dev_dbg(dev, "Failed to get supply '%s', deferring\n", consumers[i].supply); @@ -4599,8 +4668,8 @@ int regulator_bulk_enable(int num_consumers, err: for (i = 0; i < num_consumers; i++) { if (consumers[i].ret < 0) - pr_err("Failed to enable %s: %d\n", consumers[i].supply, - consumers[i].ret); + pr_err("Failed to enable %s: %pe\n", consumers[i].supply, + ERR_PTR(consumers[i].ret)); else regulator_disable(consumers[i].consumer); } @@ -4636,12 +4705,12 @@ int regulator_bulk_disable(int num_consumers, return 0; err: - pr_err("Failed to disable %s: %d\n", consumers[i].supply, ret); + pr_err("Failed to disable %s: %pe\n", consumers[i].supply, ERR_PTR(ret)); for (++i; i < num_consumers; ++i) { r = regulator_enable(consumers[i].consumer); if (r != 0) - pr_err("Failed to re-enable %s: %d\n", - consumers[i].supply, r); + pr_err("Failed to re-enable %s: %pe\n", + consumers[i].supply, ERR_PTR(r)); } return ret; @@ -4709,14 +4778,11 @@ EXPORT_SYMBOL_GPL(regulator_bulk_free); * @data: callback-specific data. * * Called by regulator drivers to notify clients a regulator event has - * occurred. We also notify regulator clients downstream. - * Note lock must be held by caller. + * occurred. */ int regulator_notifier_call_chain(struct regulator_dev *rdev, unsigned long event, void *data) { - lockdep_assert_held_once(&rdev->mutex.base); - _notifier_call_chain(rdev, event, data); return NOTIFY_DONE; @@ -5023,8 +5089,8 @@ static void regulator_remove_coupling(struct regulator_dev *rdev) if (coupler && coupler->detach_regulator) { err = coupler->detach_regulator(coupler, rdev); if (err) - rdev_err(rdev, "failed to detach from coupler: %d\n", - err); + rdev_err(rdev, "failed to detach from coupler: %pe\n", + ERR_PTR(err)); } kfree(rdev->coupling_desc.coupled_rdevs); @@ -5033,20 +5099,20 @@ static void regulator_remove_coupling(struct regulator_dev *rdev) static int regulator_init_coupling(struct regulator_dev *rdev) { + struct regulator_dev **coupled; int err, n_phandles; - size_t alloc_size; if (!IS_ENABLED(CONFIG_OF)) n_phandles = 0; else n_phandles = of_get_n_coupled(rdev); - alloc_size = sizeof(*rdev) * (n_phandles + 1); - - rdev->coupling_desc.coupled_rdevs = kzalloc(alloc_size, GFP_KERNEL); - if (!rdev->coupling_desc.coupled_rdevs) + coupled = kcalloc(n_phandles + 1, sizeof(*coupled), GFP_KERNEL); + if (!coupled) return -ENOMEM; + rdev->coupling_desc.coupled_rdevs = coupled; + /* * Every regulator should always have coupling descriptor filled with * at least pointer to itself. @@ -5068,7 +5134,7 @@ static int regulator_init_coupling(struct regulator_dev *rdev) if (IS_ERR(rdev->coupling_desc.coupler)) { err = PTR_ERR(rdev->coupling_desc.coupler); - rdev_err(rdev, "failed to get coupler: %d\n", err); + rdev_err(rdev, "failed to get coupler: %pe\n", ERR_PTR(err)); return err; } @@ -5110,7 +5176,6 @@ struct regulator_dev * regulator_register(const struct regulator_desc *regulator_desc, const struct regulator_config *cfg) { - const struct regulation_constraints *constraints = NULL; const struct regulator_init_data *init_data; struct regulator_config *config = NULL; static atomic_t regulator_no = ATOMIC_INIT(-1); @@ -5231,8 +5296,8 @@ regulator_register(const struct regulator_desc *regulator_desc, if (config->ena_gpiod) { ret = regulator_ena_gpio_request(rdev, config); if (ret != 0) { - rdev_err(rdev, "Failed to request enable GPIO: %d\n", - ret); + rdev_err(rdev, "Failed to request enable GPIO: %pe\n", + ERR_PTR(ret)); goto clean; } /* The regulator core took over the GPIO descriptor */ @@ -5249,22 +5314,38 @@ regulator_register(const struct regulator_desc *regulator_desc, /* set regulator constraints */ if (init_data) - constraints = &init_data->constraints; + rdev->constraints = kmemdup(&init_data->constraints, + sizeof(*rdev->constraints), + GFP_KERNEL); + else + rdev->constraints = kzalloc(sizeof(*rdev->constraints), + GFP_KERNEL); + if (!rdev->constraints) { + ret = -ENOMEM; + goto wash; + } if (init_data && init_data->supply_regulator) rdev->supply_name = init_data->supply_regulator; else if (regulator_desc->supply_name) rdev->supply_name = regulator_desc->supply_name; - /* - * Attempt to resolve the regulator supply, if specified, - * but don't return an error if we fail because we will try - * to resolve it again later as more regulators are added. - */ - if (regulator_resolve_supply(rdev)) - rdev_dbg(rdev, "unable to resolve supply\n"); - - ret = set_machine_constraints(rdev, constraints); + ret = set_machine_constraints(rdev); + if (ret == -EPROBE_DEFER) { + /* Regulator might be in bypass mode and so needs its supply + * to set the constraints */ + /* FIXME: this currently triggers a chicken-and-egg problem + * when creating -SUPPLY symlink in sysfs to a regulator + * that is just being created */ + rdev_dbg(rdev, "will resolve supply early: %s\n", + rdev->supply_name); + ret = regulator_resolve_supply(rdev); + if (!ret) + ret = set_machine_constraints(rdev); + else + rdev_dbg(rdev, "unable to resolve supply early: %pe\n", + ERR_PTR(ret)); + } if (ret < 0) goto wash; @@ -5375,9 +5456,14 @@ static int regulator_suspend(struct device *dev) struct regulator_dev *rdev = dev_to_rdev(dev); suspend_state_t state = pm_suspend_target_state; int ret; + const struct regulator_state *rstate; + + rstate = regulator_get_suspend_state_check(rdev, state); + if (!rstate) + return 0; regulator_lock(rdev); - ret = suspend_set_state(rdev, state); + ret = __suspend_set_state(rdev, rstate); regulator_unlock(rdev); return ret; @@ -5394,11 +5480,14 @@ static int regulator_resume(struct device *dev) if (rstate == NULL) return 0; + /* Avoid grabbing the lock if we don't need to */ + if (!rdev->desc->ops->resume) + return 0; + regulator_lock(rdev); - if (rdev->desc->ops->resume && - (rstate->enabled == ENABLE_IN_SUSPEND || - rstate->enabled == DISABLE_IN_SUSPEND)) + if (rstate->enabled == ENABLE_IN_SUSPEND || + rstate->enabled == DISABLE_IN_SUSPEND) ret = rdev->desc->ops->resume(rdev); regulator_unlock(rdev); @@ -5482,7 +5571,7 @@ void regulator_set_drvdata(struct regulator *regulator, void *data) EXPORT_SYMBOL_GPL(regulator_set_drvdata); /** - * regulator_get_id - get regulator ID + * rdev_get_id - get regulator ID * @rdev: regulator */ int rdev_get_id(struct regulator_dev *rdev) @@ -5794,13 +5883,14 @@ static int regulator_late_cleanup(struct device *dev, void *data) if (rdev->use_count) goto unlock; - /* If we can't read the status assume it's on. */ + /* If we can't read the status assume it's always on. */ if (ops->is_enabled) enabled = ops->is_enabled(rdev); else enabled = 1; - if (!enabled) + /* But if reading the status failed, assume that it's off. */ + if (enabled <= 0) goto unlock; if (have_full_constraints()) { @@ -5809,7 +5899,7 @@ static int regulator_late_cleanup(struct device *dev, void *data) rdev_info(rdev, "disabling\n"); ret = _regulator_do_disable(rdev); if (ret != 0) - rdev_err(rdev, "couldn't disable: %d\n", ret); + rdev_err(rdev, "couldn't disable: %pe\n", ERR_PTR(ret)); } else { /* The intention is that in future we will * assume that full constraints are provided diff --git a/drivers/regulator/da9055-regulator.c b/drivers/regulator/da9055-regulator.c index c025ccb1a30a..73ff5fc7d8d7 100644 --- a/drivers/regulator/da9055-regulator.c +++ b/drivers/regulator/da9055-regulator.c @@ -485,10 +485,8 @@ static irqreturn_t da9055_ldo5_6_oc_irq(int irq, void *data) { struct da9055_regulator *regulator = data; - regulator_lock(regulator->rdev); regulator_notifier_call_chain(regulator->rdev, REGULATOR_EVENT_OVER_CURRENT, NULL); - regulator_unlock(regulator->rdev); return IRQ_HANDLED; } diff --git a/drivers/regulator/da9062-regulator.c b/drivers/regulator/da9062-regulator.c index d8112f56e94e..1a6324001027 100644 --- a/drivers/regulator/da9062-regulator.c +++ b/drivers/regulator/da9062-regulator.c @@ -907,10 +907,8 @@ static irqreturn_t da9062_ldo_lim_event(int irq, void *data) continue; if (BIT(regl->info->oc_event.lsb) & bits) { - regulator_lock(regl->rdev); regulator_notifier_call_chain(regl->rdev, REGULATOR_EVENT_OVER_CURRENT, NULL); - regulator_unlock(regl->rdev); handled = IRQ_HANDLED; } } diff --git a/drivers/regulator/da9063-regulator.c b/drivers/regulator/da9063-regulator.c index fe65b5acaf28..cf7d5341750e 100644 --- a/drivers/regulator/da9063-regulator.c +++ b/drivers/regulator/da9063-regulator.c @@ -574,10 +574,8 @@ static irqreturn_t da9063_ldo_lim_event(int irq, void *data) continue; if (BIT(regl->info->oc_event.lsb) & bits) { - regulator_lock(regl->rdev); regulator_notifier_call_chain(regl->rdev, REGULATOR_EVENT_OVER_CURRENT, NULL); - regulator_unlock(regl->rdev); } } diff --git a/drivers/regulator/da9121-regulator.c b/drivers/regulator/da9121-regulator.c new file mode 100644 index 000000000000..a2ede7d7897e --- /dev/null +++ b/drivers/regulator/da9121-regulator.c @@ -0,0 +1,1075 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// DA9121 Single-channel dual-phase 10A buck converter +// +// Copyright (C) 2020 Axis Communications AB +// +// DA9130 Single-channel dual-phase 10A buck converter (Automotive) +// DA9217 Single-channel dual-phase 6A buck converter +// DA9122 Dual-channel single-phase 5A buck converter +// DA9131 Dual-channel single-phase 5A buck converter (Automotive) +// DA9220 Dual-channel single-phase 3A buck converter +// DA9132 Dual-channel single-phase 3A buck converter (Automotive) +// +// Copyright (C) 2020 Dialog Semiconductor + +#include <linux/of_device.h> +#include <linux/of_gpio.h> +#include <linux/gpio/consumer.h> +#include <linux/regulator/of_regulator.h> +#include <linux/regulator/machine.h> +#include <linux/regulator/driver.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/regulator/da9121.h> +#include <linux/interrupt.h> +#include <linux/workqueue.h> + +#include "da9121-regulator.h" + +/* Chip data */ +struct da9121 { + struct device *dev; + struct delayed_work work; + struct da9121_pdata *pdata; + struct regmap *regmap; + struct regulator_dev *rdev[DA9121_IDX_MAX]; + unsigned int persistent[2]; + unsigned int passive_delay; + int chip_irq; + int variant_id; +}; + +/* Define ranges for different variants, enabling translation to/from + * registers. Maximums give scope to allow for transients. + */ +struct da9121_range { + int val_min; + int val_max; + int val_stp; + int reg_min; + int reg_max; +}; + +static struct da9121_range da9121_10A_2phase_current = { + .val_min = 7000000, + .val_max = 20000000, + .val_stp = 1000000, + .reg_min = 1, + .reg_max = 14, +}; + +static struct da9121_range da9121_6A_2phase_current = { + .val_min = 7000000, + .val_max = 12000000, + .val_stp = 1000000, + .reg_min = 1, + .reg_max = 6, +}; + +static struct da9121_range da9121_5A_1phase_current = { + .val_min = 3500000, + .val_max = 10000000, + .val_stp = 500000, + .reg_min = 1, + .reg_max = 14, +}; + +static struct da9121_range da9121_3A_1phase_current = { + .val_min = 3500000, + .val_max = 6000000, + .val_stp = 500000, + .reg_min = 1, + .reg_max = 6, +}; + +struct da9121_variant_info { + int num_bucks; + int num_phases; + struct da9121_range *current_range; +}; + +static const struct da9121_variant_info variant_parameters[] = { + { 1, 2, &da9121_10A_2phase_current }, //DA9121_TYPE_DA9121_DA9130 + { 2, 1, &da9121_3A_1phase_current }, //DA9121_TYPE_DA9220_DA9132 + { 2, 1, &da9121_5A_1phase_current }, //DA9121_TYPE_DA9122_DA9131 + { 1, 2, &da9121_6A_2phase_current }, //DA9121_TYPE_DA9217 +}; + +struct da9121_field { + unsigned int reg; + unsigned int msk; +}; + +static const struct da9121_field da9121_current_field[2] = { + { DA9121_REG_BUCK_BUCK1_2, DA9121_MASK_BUCK_BUCKx_2_CHx_ILIM }, + { DA9xxx_REG_BUCK_BUCK2_2, DA9121_MASK_BUCK_BUCKx_2_CHx_ILIM }, +}; + +static const struct da9121_field da9121_mode_field[2] = { + { DA9121_REG_BUCK_BUCK1_4, DA9121_MASK_BUCK_BUCKx_4_CHx_A_MODE }, + { DA9xxx_REG_BUCK_BUCK2_4, DA9121_MASK_BUCK_BUCKx_4_CHx_A_MODE }, +}; + +struct status_event_data { + int buck_id; /* 0=core, 1/2-buck */ + int reg_index; /* index for status/event/mask register selection */ + int status_bit; /* bit masks... */ + int event_bit; + int mask_bit; + unsigned long notification; /* Notification for status inception */ + char *warn; /* if NULL, notify - otherwise dev_warn this string */ +}; + +#define DA9121_STATUS(id, bank, name, notification, warning) \ + { id, bank, \ + DA9121_MASK_SYS_STATUS_##bank##_##name, \ + DA9121_MASK_SYS_EVENT_##bank##_E_##name, \ + DA9121_MASK_SYS_MASK_##bank##_M_##name, \ + notification, warning } + +/* For second buck related event bits that are specific to DA9122, DA9220 variants */ +#define DA9xxx_STATUS(id, bank, name, notification, warning) \ + { id, bank, \ + DA9xxx_MASK_SYS_STATUS_##bank##_##name, \ + DA9xxx_MASK_SYS_EVENT_##bank##_E_##name, \ + DA9xxx_MASK_SYS_MASK_##bank##_M_##name, \ + notification, warning } + +/* The status signals that may need servicing, depending on device variant. + * After assertion, they persist; so event is notified, the IRQ disabled, + * and status polled until clear again and IRQ is reenabled. + * + * SG/PG1/PG2 should be set when device first powers up and should never + * re-occur. When this driver starts, it is expected that these will have + * self-cleared for when the IRQs are enabled, so these should never be seen. + * If seen, the implication is that the device has reset. + * + * GPIO0/1/2 are not configured for use by default, so should not be seen. + */ +static const struct status_event_data status_event_handling[] = { + DA9xxx_STATUS(0, 0, SG, 0, "Handled E_SG\n"), + DA9121_STATUS(0, 0, TEMP_CRIT, (REGULATOR_EVENT_OVER_TEMP|REGULATOR_EVENT_DISABLE), NULL), + DA9121_STATUS(0, 0, TEMP_WARN, REGULATOR_EVENT_OVER_TEMP, NULL), + DA9121_STATUS(1, 1, PG1, 0, "Handled E_PG1\n"), + DA9121_STATUS(1, 1, OV1, REGULATOR_EVENT_REGULATION_OUT, NULL), + DA9121_STATUS(1, 1, UV1, REGULATOR_EVENT_UNDER_VOLTAGE, NULL), + DA9121_STATUS(1, 1, OC1, REGULATOR_EVENT_OVER_CURRENT, NULL), + DA9xxx_STATUS(2, 1, PG2, 0, "Handled E_PG2\n"), + DA9xxx_STATUS(2, 1, OV2, REGULATOR_EVENT_REGULATION_OUT, NULL), + DA9xxx_STATUS(2, 1, UV2, REGULATOR_EVENT_UNDER_VOLTAGE, NULL), + DA9xxx_STATUS(2, 1, OC2, REGULATOR_EVENT_OVER_CURRENT, NULL), + DA9121_STATUS(0, 2, GPIO0, 0, "Handled E_GPIO0\n"), + DA9121_STATUS(0, 2, GPIO1, 0, "Handled E_GPIO1\n"), + DA9121_STATUS(0, 2, GPIO2, 0, "Handled E_GPIO2\n"), +}; + +static int da9121_get_current_limit(struct regulator_dev *rdev) +{ + struct da9121 *chip = rdev_get_drvdata(rdev); + int id = rdev_get_id(rdev); + struct da9121_range *range = + variant_parameters[chip->variant_id].current_range; + unsigned int val = 0; + int ret = 0; + + ret = regmap_read(chip->regmap, da9121_current_field[id].reg, &val); + if (ret < 0) { + dev_err(chip->dev, "Cannot read BUCK register: %d\n", ret); + goto error; + } + + if (val < range->reg_min) { + ret = -EACCES; + goto error; + } + + if (val > range->reg_max) { + ret = -EINVAL; + goto error; + } + + return range->val_min + (range->val_stp * (val - range->reg_min)); +error: + return ret; +} + +static int da9121_ceiling_selector(struct regulator_dev *rdev, + int min, int max, + unsigned int *selector) +{ + struct da9121 *chip = rdev_get_drvdata(rdev); + struct da9121_range *range = + variant_parameters[chip->variant_id].current_range; + unsigned int level; + unsigned int i = 0; + unsigned int sel = 0; + int ret = 0; + + if (range->val_min > max || range->val_max < min) { + dev_err(chip->dev, + "Requested current out of regulator capability\n"); + ret = -EINVAL; + goto error; + } + + level = range->val_max; + for (i = range->reg_max; i >= range->reg_min; i--) { + if (level <= max) { + sel = i; + break; + } + level -= range->val_stp; + } + + if (level < min) { + dev_err(chip->dev, + "Best match falls below minimum requested current\n"); + ret = -EINVAL; + goto error; + } + + *selector = sel; +error: + return ret; +} + +static int da9121_set_current_limit(struct regulator_dev *rdev, + int min_ua, int max_ua) +{ + struct da9121 *chip = rdev_get_drvdata(rdev); + int id = rdev_get_id(rdev); + struct da9121_range *range = + variant_parameters[chip->variant_id].current_range; + unsigned int sel = 0; + int ret = 0; + + if (min_ua < range->val_min || + max_ua > range->val_max) { + ret = -EINVAL; + goto error; + } + + ret = da9121_ceiling_selector(rdev, min_ua, max_ua, &sel); + if (ret < 0) + goto error; + + ret = regmap_update_bits(chip->regmap, + da9121_current_field[id].reg, + da9121_current_field[id].msk, + (unsigned int)sel); + if (ret < 0) + dev_err(chip->dev, "Cannot update BUCK current limit, err: %d\n", ret); + +error: + return ret; +} + +static unsigned int da9121_map_mode(unsigned int mode) +{ + switch (mode) { + case DA9121_BUCK_MODE_FORCE_PWM: + return REGULATOR_MODE_FAST; + case DA9121_BUCK_MODE_FORCE_PWM_SHEDDING: + return REGULATOR_MODE_NORMAL; + case DA9121_BUCK_MODE_AUTO: + return REGULATOR_MODE_IDLE; + case DA9121_BUCK_MODE_FORCE_PFM: + return REGULATOR_MODE_STANDBY; + default: + return -EINVAL; + } +} + +static int da9121_buck_set_mode(struct regulator_dev *rdev, unsigned int mode) +{ + struct da9121 *chip = rdev_get_drvdata(rdev); + int id = rdev_get_id(rdev); + unsigned int val; + + switch (mode) { + case REGULATOR_MODE_FAST: + val = DA9121_BUCK_MODE_FORCE_PWM; + break; + case REGULATOR_MODE_NORMAL: + val = DA9121_BUCK_MODE_FORCE_PWM_SHEDDING; + break; + case REGULATOR_MODE_IDLE: + val = DA9121_BUCK_MODE_AUTO; + break; + case REGULATOR_MODE_STANDBY: + val = DA9121_BUCK_MODE_FORCE_PFM; + break; + default: + return -EINVAL; + } + + return regmap_update_bits(chip->regmap, + da9121_mode_field[id].reg, + da9121_mode_field[id].msk, + val); +} + +static unsigned int da9121_buck_get_mode(struct regulator_dev *rdev) +{ + struct da9121 *chip = rdev_get_drvdata(rdev); + int id = rdev_get_id(rdev); + unsigned int val; + int ret = 0; + + ret = regmap_read(chip->regmap, da9121_mode_field[id].reg, &val); + if (ret < 0) { + dev_err(chip->dev, "Cannot read BUCK register: %d\n", ret); + return -EINVAL; + } + + return da9121_map_mode(val & da9121_mode_field[id].msk); +} + +static const struct regulator_ops da9121_buck_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, + .get_current_limit = da9121_get_current_limit, + .set_current_limit = da9121_set_current_limit, + .set_mode = da9121_buck_set_mode, + .get_mode = da9121_buck_get_mode, +}; + +static struct of_regulator_match da9121_matches[] = { + [DA9121_IDX_BUCK1] = { .name = "buck1" }, + [DA9121_IDX_BUCK2] = { .name = "buck2" }, +}; + +static int da9121_of_parse_cb(struct device_node *np, + const struct regulator_desc *desc, + struct regulator_config *config) +{ + struct da9121 *chip = config->driver_data; + struct da9121_pdata *pdata; + struct gpio_desc *ena_gpiod; + + if (chip->pdata == NULL) { + pdata = devm_kzalloc(chip->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + } else { + pdata = chip->pdata; + } + + pdata->num_buck++; + + if (pdata->num_buck > variant_parameters[chip->variant_id].num_bucks) { + dev_err(chip->dev, "Error: excessive regulators for device\n"); + return -ENODEV; + } + + ena_gpiod = fwnode_gpiod_get_index(of_fwnode_handle(np), "enable", 0, + GPIOD_OUT_HIGH | + GPIOD_FLAGS_BIT_NONEXCLUSIVE, + "da9121-enable"); + if (!IS_ERR(ena_gpiod)) + config->ena_gpiod = ena_gpiod; + + if (variant_parameters[chip->variant_id].num_bucks == 2) { + uint32_t ripple_cancel; + uint32_t ripple_reg; + int ret; + + if (of_property_read_u32(da9121_matches[pdata->num_buck-1].of_node, + "dlg,ripple-cancel", &ripple_cancel)) { + if (pdata->num_buck > 1) + ripple_reg = DA9xxx_REG_BUCK_BUCK2_7; + else + ripple_reg = DA9121_REG_BUCK_BUCK1_7; + + ret = regmap_update_bits(chip->regmap, ripple_reg, + DA9xxx_MASK_BUCK_BUCKx_7_CHx_RIPPLE_CANCEL, + ripple_cancel); + if (ret < 0) + dev_err(chip->dev, "Cannot set ripple mode, err: %d\n", ret); + } + } + + return 0; +} + +#define DA9121_MIN_MV 300 +#define DA9121_MAX_MV 1900 +#define DA9121_STEP_MV 10 +#define DA9121_MIN_SEL (DA9121_MIN_MV / DA9121_STEP_MV) +#define DA9121_N_VOLTAGES (((DA9121_MAX_MV - DA9121_MIN_MV) / DA9121_STEP_MV) \ + + 1 + DA9121_MIN_SEL) + +static const struct regulator_desc da9121_reg = { + .id = DA9121_IDX_BUCK1, + .name = "da9121", + .of_match = "buck1", + .of_parse_cb = da9121_of_parse_cb, + .owner = THIS_MODULE, + .regulators_node = of_match_ptr("regulators"), + .of_map_mode = da9121_map_mode, + .ops = &da9121_buck_ops, + .type = REGULATOR_VOLTAGE, + .n_voltages = DA9121_N_VOLTAGES, + .min_uV = DA9121_MIN_MV * 1000, + .uV_step = DA9121_STEP_MV * 1000, + .linear_min_sel = DA9121_MIN_SEL, + .vsel_reg = DA9121_REG_BUCK_BUCK1_5, + .vsel_mask = DA9121_MASK_BUCK_BUCKx_5_CHx_A_VOUT, + .enable_reg = DA9121_REG_BUCK_BUCK1_0, + .enable_mask = DA9121_MASK_BUCK_BUCKx_0_CHx_EN, + /* Default value of BUCK_BUCK1_0.CH1_SRC_DVC_UP */ + .ramp_delay = 20000, + /* tBUCK_EN */ + .enable_time = 20, +}; + +static const struct regulator_desc da9220_reg[2] = { + { + .id = DA9121_IDX_BUCK1, + .name = "DA9220/DA9132 BUCK1", + .of_match = "buck1", + .of_parse_cb = da9121_of_parse_cb, + .owner = THIS_MODULE, + .regulators_node = of_match_ptr("regulators"), + .of_map_mode = da9121_map_mode, + .ops = &da9121_buck_ops, + .type = REGULATOR_VOLTAGE, + .n_voltages = DA9121_N_VOLTAGES, + .min_uV = DA9121_MIN_MV * 1000, + .uV_step = DA9121_STEP_MV * 1000, + .linear_min_sel = DA9121_MIN_SEL, + .enable_reg = DA9121_REG_BUCK_BUCK1_0, + .enable_mask = DA9121_MASK_BUCK_BUCKx_0_CHx_EN, + .vsel_reg = DA9121_REG_BUCK_BUCK1_5, + .vsel_mask = DA9121_MASK_BUCK_BUCKx_5_CHx_A_VOUT, + }, + { + .id = DA9121_IDX_BUCK2, + .name = "DA9220/DA9132 BUCK2", + .of_match = "buck2", + .of_parse_cb = da9121_of_parse_cb, + .owner = THIS_MODULE, + .regulators_node = of_match_ptr("regulators"), + .of_map_mode = da9121_map_mode, + .ops = &da9121_buck_ops, + .type = REGULATOR_VOLTAGE, + .n_voltages = DA9121_N_VOLTAGES, + .min_uV = DA9121_MIN_MV * 1000, + .uV_step = DA9121_STEP_MV * 1000, + .linear_min_sel = DA9121_MIN_SEL, + .enable_reg = DA9xxx_REG_BUCK_BUCK2_0, + .enable_mask = DA9121_MASK_BUCK_BUCKx_0_CHx_EN, + .vsel_reg = DA9xxx_REG_BUCK_BUCK2_5, + .vsel_mask = DA9121_MASK_BUCK_BUCKx_5_CHx_A_VOUT, + } +}; + +static const struct regulator_desc da9122_reg[2] = { + { + .id = DA9121_IDX_BUCK1, + .name = "DA9122/DA9131 BUCK1", + .of_match = "buck1", + .of_parse_cb = da9121_of_parse_cb, + .owner = THIS_MODULE, + .regulators_node = of_match_ptr("regulators"), + .of_map_mode = da9121_map_mode, + .ops = &da9121_buck_ops, + .type = REGULATOR_VOLTAGE, + .n_voltages = DA9121_N_VOLTAGES, + .min_uV = DA9121_MIN_MV * 1000, + .uV_step = DA9121_STEP_MV * 1000, + .linear_min_sel = DA9121_MIN_SEL, + .enable_reg = DA9121_REG_BUCK_BUCK1_0, + .enable_mask = DA9121_MASK_BUCK_BUCKx_0_CHx_EN, + .vsel_reg = DA9121_REG_BUCK_BUCK1_5, + .vsel_mask = DA9121_MASK_BUCK_BUCKx_5_CHx_A_VOUT, + }, + { + .id = DA9121_IDX_BUCK2, + .name = "DA9122/DA9131 BUCK2", + .of_match = "buck2", + .of_parse_cb = da9121_of_parse_cb, + .owner = THIS_MODULE, + .regulators_node = of_match_ptr("regulators"), + .of_map_mode = da9121_map_mode, + .ops = &da9121_buck_ops, + .type = REGULATOR_VOLTAGE, + .n_voltages = DA9121_N_VOLTAGES, + .min_uV = DA9121_MIN_MV * 1000, + .uV_step = DA9121_STEP_MV * 1000, + .linear_min_sel = DA9121_MIN_SEL, + .enable_reg = DA9xxx_REG_BUCK_BUCK2_0, + .enable_mask = DA9121_MASK_BUCK_BUCKx_0_CHx_EN, + .vsel_reg = DA9xxx_REG_BUCK_BUCK2_5, + .vsel_mask = DA9121_MASK_BUCK_BUCKx_5_CHx_A_VOUT, + } +}; + +static const struct regulator_desc da9217_reg = { + .id = DA9121_IDX_BUCK1, + .name = "DA9217 BUCK1", + .of_match = "buck1", + .of_parse_cb = da9121_of_parse_cb, + .owner = THIS_MODULE, + .regulators_node = of_match_ptr("regulators"), + .of_map_mode = da9121_map_mode, + .ops = &da9121_buck_ops, + .type = REGULATOR_VOLTAGE, + .n_voltages = DA9121_N_VOLTAGES, + .min_uV = DA9121_MIN_MV * 1000, + .uV_step = DA9121_STEP_MV * 1000, + .linear_min_sel = DA9121_MIN_SEL, + .enable_reg = DA9121_REG_BUCK_BUCK1_0, + .enable_mask = DA9121_MASK_BUCK_BUCKx_0_CHx_EN, + .vsel_reg = DA9121_REG_BUCK_BUCK1_5, + .vsel_mask = DA9121_MASK_BUCK_BUCKx_5_CHx_A_VOUT, +}; + +static const struct regulator_desc *local_da9121_regulators[][DA9121_IDX_MAX] = { + [DA9121_TYPE_DA9121_DA9130] = { &da9121_reg, NULL }, + [DA9121_TYPE_DA9220_DA9132] = { &da9220_reg[0], &da9220_reg[1] }, + [DA9121_TYPE_DA9122_DA9131] = { &da9122_reg[0], &da9122_reg[1] }, + [DA9121_TYPE_DA9217] = { &da9217_reg, NULL }, +}; + +static void da9121_status_poll_on(struct work_struct *work) +{ + struct da9121 *chip = container_of(work, struct da9121, work.work); + int status[3] = {0}; + int clear[3] = {0}; + unsigned long delay; + int i; + int ret; + + ret = regmap_bulk_read(chip->regmap, DA9121_REG_SYS_STATUS_0, status, 2); + if (ret < 0) { + dev_err(chip->dev, + "Failed to read STATUS registers: %d\n", ret); + goto error; + } + + /* Possible events are tested to be within range for the variant, potentially + * masked by the IRQ handler (not just warned about), as having been masked, + * and the respective state cleared - then flagged to unmask for next IRQ. + */ + for (i = 0; i < ARRAY_SIZE(status_event_handling); i++) { + const struct status_event_data *item = &status_event_handling[i]; + int reg_idx = item->reg_index; + bool relevant = (item->buck_id <= variant_parameters[chip->variant_id].num_bucks); + bool supported = (item->warn == NULL); + bool persisting = (chip->persistent[reg_idx] & item->event_bit); + bool now_cleared = !(status[reg_idx] & item->status_bit); + + if (relevant && supported && persisting && now_cleared) { + clear[reg_idx] |= item->mask_bit; + chip->persistent[reg_idx] &= ~item->event_bit; + } + } + + for (i = 0; i < 2; i++) { + if (clear[i]) { + unsigned int reg = DA9121_REG_SYS_MASK_0 + i; + unsigned int mbit = clear[i]; + + ret = regmap_update_bits(chip->regmap, reg, mbit, 0); + if (ret < 0) { + dev_err(chip->dev, + "Failed to unmask 0x%02x %d\n", + reg, ret); + goto error; + } + } + } + + if (chip->persistent[0] | chip->persistent[1]) { + delay = msecs_to_jiffies(chip->passive_delay); + queue_delayed_work(system_freezable_wq, &chip->work, delay); + } + +error: + return; +} + +static irqreturn_t da9121_irq_handler(int irq, void *data) +{ + struct da9121 *chip = data; + struct regulator_dev *rdev; + int event[3] = {0}; + int handled[3] = {0}; + int mask[3] = {0}; + int ret = IRQ_NONE; + int i; + int err; + + err = regmap_bulk_read(chip->regmap, DA9121_REG_SYS_EVENT_0, event, 3); + if (err < 0) { + dev_err(chip->dev, "Failed to read EVENT registers %d\n", err); + ret = IRQ_NONE; + goto error; + } + + err = regmap_bulk_read(chip->regmap, DA9121_REG_SYS_MASK_0, mask, 3); + if (err < 0) { + dev_err(chip->dev, + "Failed to read MASK registers: %d\n", ret); + ret = IRQ_NONE; + goto error; + } + + rdev = chip->rdev[DA9121_IDX_BUCK1]; + + /* Possible events are tested to be within range for the variant, currently + * enabled, and having triggered this IRQ. The event may then be notified, + * or a warning given for unexpected events - those from device POR, and + * currently unsupported GPIO configurations. + */ + for (i = 0; i < ARRAY_SIZE(status_event_handling); i++) { + const struct status_event_data *item = &status_event_handling[i]; + int reg_idx = item->reg_index; + bool relevant = (item->buck_id <= variant_parameters[chip->variant_id].num_bucks); + bool enabled = !(mask[reg_idx] & item->mask_bit); + bool active = (event[reg_idx] & item->event_bit); + bool notify = (item->warn == NULL); + + if (relevant && enabled && active) { + if (notify) { + chip->persistent[reg_idx] |= item->event_bit; + regulator_notifier_call_chain(rdev, item->notification, NULL); + } else { + dev_warn(chip->dev, item->warn); + handled[reg_idx] |= item->event_bit; + ret = IRQ_HANDLED; + } + } + } + + for (i = 0; i < 3; i++) { + if (event[i] != handled[i]) { + dev_warn(chip->dev, + "Unhandled event(s) in bank%d 0x%02x\n", i, + event[i] ^ handled[i]); + } + } + + /* Mask the interrupts for persistent events OV, OC, UV, WARN, CRIT */ + for (i = 0; i < 2; i++) { + if (handled[i]) { + unsigned int reg = DA9121_REG_SYS_MASK_0 + i; + unsigned int mbit = handled[i]; + + err = regmap_update_bits(chip->regmap, reg, mbit, mbit); + if (err < 0) { + dev_err(chip->dev, + "Failed to mask 0x%02x interrupt %d\n", + reg, err); + ret = IRQ_NONE; + goto error; + } + } + } + + /* clear the events */ + if (handled[0] | handled[1] | handled[2]) { + err = regmap_bulk_write(chip->regmap, DA9121_REG_SYS_EVENT_0, handled, 3); + if (err < 0) { + dev_err(chip->dev, "Fail to write EVENTs %d\n", err); + ret = IRQ_NONE; + goto error; + } + } + + queue_delayed_work(system_freezable_wq, &chip->work, 0); +error: + return ret; +} + +static int da9121_set_regulator_config(struct da9121 *chip) +{ + struct regulator_config config = { }; + unsigned int max_matches = variant_parameters[chip->variant_id].num_bucks; + int ret = 0; + int i; + + for (i = 0; i < max_matches; i++) { + const struct regulator_desc *regl_desc = + local_da9121_regulators[chip->variant_id][i]; + + config.dev = chip->dev; + config.driver_data = chip; + config.regmap = chip->regmap; + + chip->rdev[i] = devm_regulator_register(chip->dev, + regl_desc, &config); + if (IS_ERR(chip->rdev[i])) { + dev_err(chip->dev, "Failed to register regulator %s, %d/%d\n", + regl_desc->name, (i+1), max_matches); + ret = PTR_ERR(chip->rdev[i]); + goto error; + } + } + +error: + return ret; +} + +/* DA9121 chip register model */ +static const struct regmap_range da9121_1ch_readable_ranges[] = { + regmap_reg_range(DA9121_REG_SYS_STATUS_0, DA9121_REG_SYS_MASK_3), + regmap_reg_range(DA9121_REG_SYS_CONFIG_2, DA9121_REG_SYS_CONFIG_3), + regmap_reg_range(DA9121_REG_SYS_GPIO0_0, DA9121_REG_SYS_GPIO2_1), + regmap_reg_range(DA9121_REG_BUCK_BUCK1_0, DA9121_REG_BUCK_BUCK1_6), + regmap_reg_range(DA9121_REG_OTP_DEVICE_ID, DA9121_REG_OTP_CONFIG_ID), +}; + +static const struct regmap_access_table da9121_1ch_readable_table = { + .yes_ranges = da9121_1ch_readable_ranges, + .n_yes_ranges = ARRAY_SIZE(da9121_1ch_readable_ranges), +}; + +static const struct regmap_range da9121_2ch_readable_ranges[] = { + regmap_reg_range(DA9121_REG_SYS_STATUS_0, DA9121_REG_SYS_MASK_3), + regmap_reg_range(DA9121_REG_SYS_CONFIG_2, DA9121_REG_SYS_CONFIG_3), + regmap_reg_range(DA9121_REG_SYS_GPIO0_0, DA9121_REG_SYS_GPIO2_1), + regmap_reg_range(DA9121_REG_BUCK_BUCK1_0, DA9121_REG_BUCK_BUCK1_7), + regmap_reg_range(DA9xxx_REG_BUCK_BUCK2_0, DA9xxx_REG_BUCK_BUCK2_7), + regmap_reg_range(DA9121_REG_OTP_DEVICE_ID, DA9121_REG_OTP_CONFIG_ID), +}; + +static const struct regmap_access_table da9121_2ch_readable_table = { + .yes_ranges = da9121_2ch_readable_ranges, + .n_yes_ranges = ARRAY_SIZE(da9121_2ch_readable_ranges), +}; + +static const struct regmap_range da9121_1ch_writeable_ranges[] = { + regmap_reg_range(DA9121_REG_SYS_EVENT_0, DA9121_REG_SYS_MASK_3), + regmap_reg_range(DA9121_REG_SYS_CONFIG_2, DA9121_REG_SYS_CONFIG_3), + regmap_reg_range(DA9121_REG_SYS_GPIO0_0, DA9121_REG_SYS_GPIO2_1), + regmap_reg_range(DA9121_REG_BUCK_BUCK1_0, DA9121_REG_BUCK_BUCK1_2), + regmap_reg_range(DA9121_REG_BUCK_BUCK1_4, DA9121_REG_BUCK_BUCK1_6), +}; + +static const struct regmap_access_table da9121_1ch_writeable_table = { + .yes_ranges = da9121_1ch_writeable_ranges, + .n_yes_ranges = ARRAY_SIZE(da9121_1ch_writeable_ranges), +}; + +static const struct regmap_range da9121_2ch_writeable_ranges[] = { + regmap_reg_range(DA9121_REG_SYS_EVENT_0, DA9121_REG_SYS_MASK_3), + regmap_reg_range(DA9121_REG_SYS_CONFIG_2, DA9121_REG_SYS_CONFIG_3), + regmap_reg_range(DA9121_REG_SYS_GPIO0_0, DA9121_REG_SYS_GPIO2_1), + regmap_reg_range(DA9121_REG_BUCK_BUCK1_0, DA9121_REG_BUCK_BUCK1_2), + regmap_reg_range(DA9121_REG_BUCK_BUCK1_4, DA9121_REG_BUCK_BUCK1_7), + regmap_reg_range(DA9xxx_REG_BUCK_BUCK2_0, DA9xxx_REG_BUCK_BUCK2_2), + regmap_reg_range(DA9xxx_REG_BUCK_BUCK2_4, DA9xxx_REG_BUCK_BUCK2_7), +}; + +static const struct regmap_access_table da9121_2ch_writeable_table = { + .yes_ranges = da9121_2ch_writeable_ranges, + .n_yes_ranges = ARRAY_SIZE(da9121_2ch_writeable_ranges), +}; + + +static const struct regmap_range da9121_volatile_ranges[] = { + regmap_reg_range(DA9121_REG_SYS_STATUS_0, DA9121_REG_SYS_EVENT_2), + regmap_reg_range(DA9121_REG_SYS_GPIO0_0, DA9121_REG_SYS_GPIO2_1), + regmap_reg_range(DA9121_REG_BUCK_BUCK1_0, DA9121_REG_BUCK_BUCK1_6), +}; + +static const struct regmap_access_table da9121_volatile_table = { + .yes_ranges = da9121_volatile_ranges, + .n_yes_ranges = ARRAY_SIZE(da9121_volatile_ranges), +}; + +/* DA9121 regmap config for 1 channel variants */ +static struct regmap_config da9121_1ch_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = DA9121_REG_OTP_CONFIG_ID, + .rd_table = &da9121_1ch_readable_table, + .wr_table = &da9121_1ch_writeable_table, + .volatile_table = &da9121_volatile_table, + .cache_type = REGCACHE_RBTREE, +}; + +/* DA9121 regmap config for 2 channel variants */ +static struct regmap_config da9121_2ch_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = DA9121_REG_OTP_CONFIG_ID, + .rd_table = &da9121_2ch_readable_table, + .wr_table = &da9121_2ch_writeable_table, + .volatile_table = &da9121_volatile_table, + .cache_type = REGCACHE_RBTREE, +}; + +static int da9121_check_device_type(struct i2c_client *i2c, struct da9121 *chip) +{ + u32 device_id; + u8 chip_id = chip->variant_id; + u32 variant_id; + u8 variant_mrc, variant_vrc; + char *type; + bool config_match = false; + int ret = 0; + + ret = regmap_read(chip->regmap, DA9121_REG_OTP_DEVICE_ID, &device_id); + if (ret < 0) { + dev_err(chip->dev, "Cannot read device ID: %d\n", ret); + goto error; + } + + ret = regmap_read(chip->regmap, DA9121_REG_OTP_VARIANT_ID, &variant_id); + if (ret < 0) { + dev_err(chip->dev, "Cannot read variant ID: %d\n", ret); + goto error; + } + + if (device_id != DA9121_DEVICE_ID) { + dev_err(chip->dev, "Invalid device ID: 0x%02x\n", device_id); + ret = -ENODEV; + goto error; + } + + variant_vrc = variant_id & DA9121_MASK_OTP_VARIANT_ID_VRC; + + switch (variant_vrc) { + case DA9121_VARIANT_VRC: + type = "DA9121/DA9130"; + config_match = (chip_id == DA9121_TYPE_DA9121_DA9130); + break; + case DA9220_VARIANT_VRC: + type = "DA9220/DA9132"; + config_match = (chip_id == DA9121_TYPE_DA9220_DA9132); + break; + case DA9122_VARIANT_VRC: + type = "DA9122/DA9131"; + config_match = (chip_id == DA9121_TYPE_DA9122_DA9131); + break; + case DA9217_VARIANT_VRC: + type = "DA9217"; + config_match = (chip_id == DA9121_TYPE_DA9217); + break; + default: + type = "Unknown"; + break; + } + + dev_info(chip->dev, + "Device detected (device-ID: 0x%02X, var-ID: 0x%02X, %s)\n", + device_id, variant_id, type); + + if (!config_match) { + dev_err(chip->dev, "Device tree configuration does not match detected device.\n"); + ret = -EINVAL; + goto error; + } + + variant_mrc = (variant_id & DA9121_MASK_OTP_VARIANT_ID_MRC) + >> DA9121_SHIFT_OTP_VARIANT_ID_MRC; + + if ((device_id == DA9121_DEVICE_ID) && + (variant_mrc < DA9121_VARIANT_MRC_BASE)) { + dev_err(chip->dev, + "Cannot support variant MRC: 0x%02X\n", variant_mrc); + ret = -EINVAL; + } +error: + return ret; +} + +static int da9121_assign_chip_model(struct i2c_client *i2c, + struct da9121 *chip) +{ + struct regmap_config *regmap; + int ret = 0; + + chip->dev = &i2c->dev; + + switch (chip->variant_id) { + case DA9121_TYPE_DA9121_DA9130: + fallthrough; + case DA9121_TYPE_DA9217: + regmap = &da9121_1ch_regmap_config; + break; + case DA9121_TYPE_DA9122_DA9131: + fallthrough; + case DA9121_TYPE_DA9220_DA9132: + regmap = &da9121_2ch_regmap_config; + break; + } + + /* Set these up for of_regulator_match call which may want .of_map_modes */ + da9121_matches[0].desc = local_da9121_regulators[chip->variant_id][0]; + da9121_matches[1].desc = local_da9121_regulators[chip->variant_id][1]; + + chip->regmap = devm_regmap_init_i2c(i2c, regmap); + if (IS_ERR(chip->regmap)) { + ret = PTR_ERR(chip->regmap); + dev_err(chip->dev, "Failed to configure a register map: %d\n", + ret); + return ret; + } + + ret = da9121_check_device_type(i2c, chip); + + return ret; +} + +static int da9121_config_irq(struct i2c_client *i2c, + struct da9121 *chip) +{ + unsigned int p_delay = DA9121_DEFAULT_POLLING_PERIOD_MS; + const int mask_all[4] = { 0, 0, 0xFF, 0xFF }; + int ret = 0; + + chip->chip_irq = i2c->irq; + + if (chip->chip_irq != 0) { + if (!of_property_read_u32(chip->dev->of_node, + "dlg,irq-polling-delay-passive-ms", + &p_delay)) { + if (p_delay < DA9121_MIN_POLLING_PERIOD_MS || + p_delay > DA9121_MAX_POLLING_PERIOD_MS) { + dev_warn(chip->dev, + "Out-of-range polling period %d ms\n", + p_delay); + p_delay = DA9121_DEFAULT_POLLING_PERIOD_MS; + } + } + + chip->passive_delay = p_delay; + + ret = request_threaded_irq(chip->chip_irq, NULL, + da9121_irq_handler, + IRQF_TRIGGER_LOW|IRQF_ONESHOT, + "da9121", chip); + if (ret != 0) { + dev_err(chip->dev, "Failed IRQ request: %d\n", + chip->chip_irq); + goto error; + } + + ret = regmap_bulk_write(chip->regmap, DA9121_REG_SYS_MASK_0, mask_all, 4); + if (ret != 0) { + dev_err(chip->dev, "Failed to set IRQ masks: %d\n", + ret); + goto regmap_error; + } + + INIT_DELAYED_WORK(&chip->work, da9121_status_poll_on); + dev_info(chip->dev, "Interrupt polling period set at %d ms\n", + chip->passive_delay); + } +error: + return ret; +regmap_error: + free_irq(chip->chip_irq, chip); + return ret; +} + +static const struct of_device_id da9121_dt_ids[] = { + { .compatible = "dlg,da9121", .data = (void *) DA9121_TYPE_DA9121_DA9130 }, + { .compatible = "dlg,da9130", .data = (void *) DA9121_TYPE_DA9121_DA9130 }, + { .compatible = "dlg,da9217", .data = (void *) DA9121_TYPE_DA9217 }, + { .compatible = "dlg,da9122", .data = (void *) DA9121_TYPE_DA9122_DA9131 }, + { .compatible = "dlg,da9131", .data = (void *) DA9121_TYPE_DA9122_DA9131 }, + { .compatible = "dlg,da9220", .data = (void *) DA9121_TYPE_DA9220_DA9132 }, + { .compatible = "dlg,da9132", .data = (void *) DA9121_TYPE_DA9220_DA9132 }, + { } +}; +MODULE_DEVICE_TABLE(of, da9121_dt_ids); + +static inline int da9121_of_get_id(struct device *dev) +{ + const struct of_device_id *id = of_match_device(da9121_dt_ids, dev); + + if (!id) { + dev_err(dev, "%s: Failed\n", __func__); + return -EINVAL; + } + return (uintptr_t)id->data; +} + +static int da9121_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct da9121 *chip; + const int mask_all[4] = { 0xFF, 0xFF, 0xFF, 0xFF }; + int ret = 0; + + chip = devm_kzalloc(&i2c->dev, sizeof(struct da9121), GFP_KERNEL); + if (!chip) { + ret = -ENOMEM; + goto error; + } + + chip->pdata = i2c->dev.platform_data; + chip->variant_id = da9121_of_get_id(&i2c->dev); + + ret = da9121_assign_chip_model(i2c, chip); + if (ret < 0) + goto error; + + ret = regmap_bulk_write(chip->regmap, DA9121_REG_SYS_MASK_0, mask_all, 4); + if (ret != 0) { + dev_err(chip->dev, "Failed to set IRQ masks: %d\n", ret); + goto error; + } + + ret = da9121_set_regulator_config(chip); + if (ret < 0) + goto error; + + ret = da9121_config_irq(i2c, chip); + +error: + return ret; +} + +static int da9121_i2c_remove(struct i2c_client *i2c) +{ + struct da9121 *chip = i2c_get_clientdata(i2c); + const int mask_all[4] = { 0xFF, 0xFF, 0xFF, 0xFF }; + int ret = 0; + + free_irq(chip->chip_irq, chip); + cancel_delayed_work_sync(&chip->work); + + ret = regmap_bulk_write(chip->regmap, DA9121_REG_SYS_MASK_0, mask_all, 4); + if (ret != 0) + dev_err(chip->dev, "Failed to set IRQ masks: %d\n", ret); + return ret; +} + +static const struct i2c_device_id da9121_i2c_id[] = { + {"da9121", DA9121_TYPE_DA9121_DA9130}, + {"da9130", DA9121_TYPE_DA9121_DA9130}, + {"da9217", DA9121_TYPE_DA9217}, + {"da9122", DA9121_TYPE_DA9122_DA9131}, + {"da9131", DA9121_TYPE_DA9122_DA9131}, + {"da9220", DA9121_TYPE_DA9220_DA9132}, + {"da9132", DA9121_TYPE_DA9220_DA9132}, + {}, +}; +MODULE_DEVICE_TABLE(i2c, da9121_i2c_id); + +static struct i2c_driver da9121_regulator_driver = { + .driver = { + .name = "da9121", + .of_match_table = of_match_ptr(da9121_dt_ids), + }, + .probe = da9121_i2c_probe, + .remove = da9121_i2c_remove, + .id_table = da9121_i2c_id, +}; + +module_i2c_driver(da9121_regulator_driver); + +MODULE_LICENSE("GPL v2"); diff --git a/drivers/regulator/da9121-regulator.h b/drivers/regulator/da9121-regulator.h new file mode 100644 index 000000000000..3c34cb889ca8 --- /dev/null +++ b/drivers/regulator/da9121-regulator.h @@ -0,0 +1,291 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * DA9121 Single-channel dual-phase 10A buck converter + * DA9130 Single-channel dual-phase 10A buck converter (Automotive) + * DA9217 Single-channel dual-phase 6A buck converter + * DA9122 Dual-channel single-phase 5A buck converter + * DA9131 Dual-channel single-phase 5A buck converter (Automotive) + * DA9220 Dual-channel single-phase 3A buck converter + * DA9132 Dual-channel single-phase 3A buck converter (Automotive) + * + * Copyright (C) 2020 Dialog Semiconductor + * + * Authors: Steve Twiss, Dialog Semiconductor + * Adam Ward, Dialog Semiconductor + */ + +#ifndef __DA9121_REGISTERS_H__ +#define __DA9121_REGISTERS_H__ + +/* Values for: DA9121_REG_BUCK_BUCKx_4 registers, fields CHx_y_MODE + * DA9121_REG_BUCK_BUCKx_7 registers, fields CHx_RIPPLE_CANCEL + */ +#include <dt-bindings/regulator/dlg,da9121-regulator.h> + +enum da9121_variant { + DA9121_TYPE_DA9121_DA9130, + DA9121_TYPE_DA9220_DA9132, + DA9121_TYPE_DA9122_DA9131, + DA9121_TYPE_DA9217 +}; + +/* Minimum, maximum and default polling millisecond periods are provided + * here as an example. It is expected that any final implementation will + * include a modification of these settings to match the required + * application. + */ +#define DA9121_DEFAULT_POLLING_PERIOD_MS 3000 +#define DA9121_MAX_POLLING_PERIOD_MS 10000 +#define DA9121_MIN_POLLING_PERIOD_MS 1000 + +/* Registers */ + +#define DA9121_REG_SYS_STATUS_0 0x01 +#define DA9121_REG_SYS_STATUS_1 0x02 +#define DA9121_REG_SYS_STATUS_2 0x03 +#define DA9121_REG_SYS_EVENT_0 0x04 +#define DA9121_REG_SYS_EVENT_1 0x05 +#define DA9121_REG_SYS_EVENT_2 0x06 +#define DA9121_REG_SYS_MASK_0 0x07 +#define DA9121_REG_SYS_MASK_1 0x08 +#define DA9121_REG_SYS_MASK_2 0x09 +#define DA9121_REG_SYS_MASK_3 0x0A +#define DA9121_REG_SYS_CONFIG_0 0x0B +#define DA9121_REG_SYS_CONFIG_1 0x0C +#define DA9121_REG_SYS_CONFIG_2 0x0D +#define DA9121_REG_SYS_CONFIG_3 0x0E +#define DA9121_REG_SYS_GPIO0_0 0x10 +#define DA9121_REG_SYS_GPIO0_1 0x11 +#define DA9121_REG_SYS_GPIO1_0 0x12 +#define DA9121_REG_SYS_GPIO1_1 0x13 +#define DA9121_REG_SYS_GPIO2_0 0x14 +#define DA9121_REG_SYS_GPIO2_1 0x15 +#define DA9121_REG_BUCK_BUCK1_0 0x20 +#define DA9121_REG_BUCK_BUCK1_1 0x21 +#define DA9121_REG_BUCK_BUCK1_2 0x22 +#define DA9121_REG_BUCK_BUCK1_3 0x23 +#define DA9121_REG_BUCK_BUCK1_4 0x24 +#define DA9121_REG_BUCK_BUCK1_5 0x25 +#define DA9121_REG_BUCK_BUCK1_6 0x26 +#define DA9121_REG_BUCK_BUCK1_7 0x27 +#define DA9xxx_REG_BUCK_BUCK2_0 0x28 +#define DA9xxx_REG_BUCK_BUCK2_1 0x29 +#define DA9xxx_REG_BUCK_BUCK2_2 0x2A +#define DA9xxx_REG_BUCK_BUCK2_3 0x2B +#define DA9xxx_REG_BUCK_BUCK2_4 0x2C +#define DA9xxx_REG_BUCK_BUCK2_5 0x2D +#define DA9xxx_REG_BUCK_BUCK2_6 0x2E +#define DA9xxx_REG_BUCK_BUCK2_7 0x2F +#define DA9121_REG_OTP_DEVICE_ID 0x48 +#define DA9121_REG_OTP_VARIANT_ID 0x49 +#define DA9121_REG_OTP_CUSTOMER_ID 0x4A +#define DA9121_REG_OTP_CONFIG_ID 0x4B + +/* Register bits */ + +/* DA9121_REG_SYS_STATUS_0 */ + +#define DA9xxx_MASK_SYS_STATUS_0_SG BIT(2) +#define DA9121_MASK_SYS_STATUS_0_TEMP_CRIT BIT(1) +#define DA9121_MASK_SYS_STATUS_0_TEMP_WARN BIT(0) + +/* DA9121_REG_SYS_STATUS_1 */ + +#define DA9xxx_MASK_SYS_STATUS_1_PG2 BIT(7) +#define DA9xxx_MASK_SYS_STATUS_1_OV2 BIT(6) +#define DA9xxx_MASK_SYS_STATUS_1_UV2 BIT(5) +#define DA9xxx_MASK_SYS_STATUS_1_OC2 BIT(4) +#define DA9121_MASK_SYS_STATUS_1_PG1 BIT(3) +#define DA9121_MASK_SYS_STATUS_1_OV1 BIT(2) +#define DA9121_MASK_SYS_STATUS_1_UV1 BIT(1) +#define DA9121_MASK_SYS_STATUS_1_OC1 BIT(0) + +/* DA9121_REG_SYS_STATUS_2 */ + +#define DA9121_MASK_SYS_STATUS_2_GPIO2 BIT(2) +#define DA9121_MASK_SYS_STATUS_2_GPIO1 BIT(1) +#define DA9121_MASK_SYS_STATUS_2_GPIO0 BIT(0) + +/* DA9121_REG_SYS_EVENT_0 */ + +#define DA9xxx_MASK_SYS_EVENT_0_E_SG BIT(2) +#define DA9121_MASK_SYS_EVENT_0_E_TEMP_CRIT BIT(1) +#define DA9121_MASK_SYS_EVENT_0_E_TEMP_WARN BIT(0) + +/* DA9121_REG_SYS_EVENT_1 */ + +#define DA9xxx_MASK_SYS_EVENT_1_E_PG2 BIT(7) +#define DA9xxx_MASK_SYS_EVENT_1_E_OV2 BIT(6) +#define DA9xxx_MASK_SYS_EVENT_1_E_UV2 BIT(5) +#define DA9xxx_MASK_SYS_EVENT_1_E_OC2 BIT(4) +#define DA9121_MASK_SYS_EVENT_1_E_PG1 BIT(3) +#define DA9121_MASK_SYS_EVENT_1_E_OV1 BIT(2) +#define DA9121_MASK_SYS_EVENT_1_E_UV1 BIT(1) +#define DA9121_MASK_SYS_EVENT_1_E_OC1 BIT(0) + +/* DA9121_REG_SYS_EVENT_2 */ + +#define DA9121_MASK_SYS_EVENT_2_E_GPIO2 BIT(2) +#define DA9121_MASK_SYS_EVENT_2_E_GPIO1 BIT(1) +#define DA9121_MASK_SYS_EVENT_2_E_GPIO0 BIT(0) + +/* DA9121_REG_SYS_MASK_0 */ + +#define DA9xxx_MASK_SYS_MASK_0_M_SG BIT(2) +#define DA9121_MASK_SYS_MASK_0_M_TEMP_CRIT BIT(1) +#define DA9121_MASK_SYS_MASK_0_M_TEMP_WARN BIT(0) + +/* DA9121_REG_SYS_MASK_1 */ + +#define DA9xxx_MASK_SYS_MASK_1_M_PG2 BIT(7) +#define DA9xxx_MASK_SYS_MASK_1_M_OV2 BIT(6) +#define DA9xxx_MASK_SYS_MASK_1_M_UV2 BIT(5) +#define DA9xxx_MASK_SYS_MASK_1_M_OC2 BIT(4) +#define DA9121_MASK_SYS_MASK_1_M_PG1 BIT(3) +#define DA9121_MASK_SYS_MASK_1_M_OV1 BIT(2) +#define DA9121_MASK_SYS_MASK_1_M_UV1 BIT(1) +#define DA9121_MASK_SYS_MASK_1_M_OC1 BIT(0) + +/* DA9121_REG_SYS_MASK_2 */ + +#define DA9121_MASK_SYS_MASK_2_M_GPIO2 BIT(2) +#define DA9121_MASK_SYS_MASK_2_M_GPIO1 BIT(1) +#define DA9121_MASK_SYS_MASK_2_M_GPIO0 BIT(0) + +/* DA9122_REG_SYS_MASK_3 */ + +#define DA9121_MASK_SYS_MASK_3_M_VR_HOT BIT(3) +#define DA9xxx_MASK_SYS_MASK_3_M_SG_STAT BIT(2) +#define DA9xxx_MASK_SYS_MASK_3_M_PG2_STAT BIT(1) +#define DA9121_MASK_SYS_MASK_3_M_PG1_STAT BIT(0) + +/* DA9121_REG_SYS_CONFIG_0 */ + +#define DA9121_MASK_SYS_CONFIG_0_CH1_DIS_DLY 0xF0 +#define DA9121_MASK_SYS_CONFIG_0_CH1_EN_DLY 0x0F + +/* DA9xxx_REG_SYS_CONFIG_1 */ + +#define DA9xxx_MASK_SYS_CONFIG_1_CH2_DIS_DLY 0xF0 +#define DA9xxx_MASK_SYS_CONFIG_1_CH2_EN_DLY 0x0F + +/* DA9121_REG_SYS_CONFIG_2 */ + +#define DA9121_MASK_SYS_CONFIG_2_OC_LATCHOFF 0x60 +#define DA9121_MASK_SYS_CONFIG_2_OC_DVC_MASK BIT(4) +#define DA9121_MASK_SYS_CONFIG_2_PG_DVC_MASK 0x0C + +/* DA9121_REG_SYS_CONFIG_3 */ + +#define DA9121_MASK_SYS_CONFIG_3_OSC_TUNE 0X70 +#define DA9121_MASK_SYS_CONFIG_3_I2C_TIMEOUT BIT(1) + +/* DA9121_REG_SYS_GPIO0_0 */ + +#define DA9121_MASK_SYS_GPIO0_0_GPIO0_MODE 0X1E +#define DA9121_MASK_SYS_GPIO0_0_GPIO0_OBUF BIT(0) + +/* DA9121_REG_SYS_GPIO0_1 */ + +#define DA9121_MASK_SYS_GPIO0_1_GPIO0_DEB_FALL BIT(7) +#define DA9121_MASK_SYS_GPIO0_1_GPIO0_DEB_RISE BIT(6) +#define DA9121_MASK_SYS_GPIO0_1_GPIO0_DEB 0x30 +#define DA9121_MASK_SYS_GPIO0_1_GPIO0_PUPD BIT(3) +#define DA9121_MASK_SYS_GPIO0_1_GPIO0_POL BIT(2) +#define DA9121_MASK_SYS_GPIO0_1_GPIO0_TRIG 0x03 + +/* DA9121_REG_SYS_GPIO1_0 */ + +#define DA9121_MASK_SYS_GPIO1_0_GPIO1_MODE 0x1E +#define DA9121_MASK_SYS_GPIO1_0_GPIO1_OBUF BIT(0) + +/* DA9121_REG_SYS_GPIO1_1 */ + +#define DA9121_MASK_SYS_GPIO1_1_GPIO1_DEB_FALL BIT(7) +#define DA9121_MASK_SYS_GPIO1_1_GPIO1_DEB_RISE BIT(6) +#define DA9121_MASK_SYS_GPIO1_1_GPIO1_DEB 0x30 +#define DA9121_MASK_SYS_GPIO1_1_GPIO1_PUPD BIT(3) +#define DA9121_MASK_SYS_GPIO1_1_GPIO1_POL BIT(2) +#define DA9121_MASK_SYS_GPIO1_1_GPIO1_TRIG 0x03 + +/* DA9121_REG_SYS_GPIO2_0 */ + +#define DA9121_MASK_SYS_GPIO2_0_GPIO2_MODE 0x1E +#define DA9121_MASK_SYS_GPIO2_0_GPIO2_OBUF BIT(0) + +/* DA9121_REG_SYS_GPIO2_1 */ + +#define DA9121_MASK_SYS_GPIO2_1_GPIO2_DEB_FALL BIT(7) +#define DA9121_MASK_SYS_GPIO2_1_GPIO2_DEB_RISE BIT(6) +#define DA9121_MASK_SYS_GPIO2_1_GPIO2_DEB 0x30 +#define DA9121_MASK_SYS_GPIO2_1_GPIO2_PUPD BIT(3) +#define DA9121_MASK_SYS_GPIO2_1_GPIO2_POL BIT(2) +#define DA9121_MASK_SYS_GPIO2_1_GPIO2_TRIG 0x03 + +/* DA9121_REG_BUCK_BUCK1_0 / DA9xxx_REG_BUCK_BUCK2_0 */ + +#define DA9121_MASK_BUCK_BUCKx_0_CHx_SR_DVC_DWN 0x70 +#define DA9121_MASK_BUCK_BUCKx_0_CHx_SR_DVC_UP 0x0E +#define DA9121_MASK_BUCK_BUCKx_0_CHx_EN BIT(0) + +/* DA9121_REG_BUCK_BUCK1_1 / DA9xxx_REG_BUCK_BUCK2_1 */ + +#define DA9121_MASK_BUCK_BUCKx_1_CHx_SR_SHDN 0x70 +#define DA9121_MASK_BUCK_BUCKx_1_CHx_SR_STARTUP 0x0E +#define DA9121_MASK_BUCK_BUCKx_1_CHx_PD_DIS BIT(0) + +/* DA9121_REG_BUCK_BUCK1_2 / DA9xxx_REG_BUCK_BUCK2_2 */ + +#define DA9121_MASK_BUCK_BUCKx_2_CHx_ILIM 0x0F + +/* DA9121_REG_BUCK_BUCK1_3 / DA9xxx_REG_BUCK_BUCK2_3 */ + +#define DA9121_MASK_BUCK_BUCKx_3_CHx_VMAX 0xFF + +/* DA9121_REG_BUCK_BUCK1_4 / DA9xxx_REG_BUCK_BUCK2_4 */ + +#define DA9121_MASK_BUCK_BUCKx_4_CHx_VSEL BIT(4) +#define DA9121_MASK_BUCK_BUCKx_4_CHx_B_MODE 0x0C +#define DA9121_MASK_BUCK_BUCKx_4_CHx_A_MODE 0x03 + +/* DA9121_REG_BUCK_BUCK1_5 / DA9xxx_REG_BUCK_BUCK2_5 */ + +#define DA9121_MASK_BUCK_BUCKx_5_CHx_A_VOUT 0xFF + +/* DA9121_REG_BUCK_BUCK1_6 / DA9xxx_REG_BUCK_BUCK2_6 */ + +#define DA9121_MASK_BUCK_BUCKx_6_CHx_B_VOUT 0xFF + +/* DA9121_REG_BUCK_BUCK1_7 / DA9xxx_REG_BUCK_BUCK2_7 */ + +#define DA9xxx_MASK_BUCK_BUCKx_7_CHx_RIPPLE_CANCEL 0x03 + + +/* DA9121_REG_OTP_DEVICE_ID */ + +#define DA9121_MASK_OTP_DEVICE_ID_DEV_ID 0xFF + +#define DA9121_DEVICE_ID 0x05 + +/* DA9121_REG_OTP_VARIANT_ID */ + +#define DA9121_SHIFT_OTP_VARIANT_ID_MRC 4 +#define DA9121_MASK_OTP_VARIANT_ID_MRC 0xF0 +#define DA9121_SHIFT_OTP_VARIANT_ID_VRC 0 +#define DA9121_MASK_OTP_VARIANT_ID_VRC 0x0F + +#define DA9121_VARIANT_MRC_BASE 0x2 +#define DA9121_VARIANT_VRC 0x1 +#define DA9220_VARIANT_VRC 0x0 +#define DA9122_VARIANT_VRC 0x2 +#define DA9217_VARIANT_VRC 0x7 + +/* DA9121_REG_OTP_CUSTOMER_ID */ + +#define DA9121_MASK_OTP_CUSTOMER_ID_CUST_ID 0xFF + +/* DA9121_REG_OTP_CONFIG_ID */ + +#define DA9121_MASK_OTP_CONFIG_ID_CONFIG_REV 0xFF + +#endif /* __DA9121_REGISTERS_H__ */ diff --git a/drivers/regulator/da9210-regulator.c b/drivers/regulator/da9210-regulator.c index 0cdeb6186529..7493af0b5c04 100644 --- a/drivers/regulator/da9210-regulator.c +++ b/drivers/regulator/da9210-regulator.c @@ -77,8 +77,6 @@ static irqreturn_t da9210_irq_handler(int irq, void *data) if (error < 0) goto error_i2c; - regulator_lock(chip->rdev); - if (val & DA9210_E_OVCURR) { regulator_notifier_call_chain(chip->rdev, REGULATOR_EVENT_OVER_CURRENT, @@ -103,8 +101,6 @@ static irqreturn_t da9210_irq_handler(int irq, void *data) handled |= DA9210_E_VMAX; } - regulator_unlock(chip->rdev); - if (handled) { /* Clear handled events */ error = regmap_write(chip->regmap, DA9210_REG_EVENT_B, handled); @@ -125,7 +121,7 @@ error_i2c: * I2C driver interface functions */ -static const struct of_device_id da9210_dt_ids[] = { +static const struct of_device_id __maybe_unused da9210_dt_ids[] = { { .compatible = "dlg,da9210", }, { } }; diff --git a/drivers/regulator/da9211-regulator.c b/drivers/regulator/da9211-regulator.c index 297b3aa7c753..e01b32d1fa17 100644 --- a/drivers/regulator/da9211-regulator.c +++ b/drivers/regulator/da9211-regulator.c @@ -51,10 +51,24 @@ static const struct regmap_range_cfg da9211_regmap_range[] = { }, }; +static bool da9211_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case DA9211_REG_STATUS_A: + case DA9211_REG_STATUS_B: + case DA9211_REG_EVENT_A: + case DA9211_REG_EVENT_B: + return true; + } + return false; +} + static const struct regmap_config da9211_regmap_config = { .reg_bits = 8, .val_bits = 8, .max_register = 5 * 128, + .volatile_reg = da9211_volatile_reg, + .cache_type = REGCACHE_RBTREE, .ranges = da9211_regmap_range, .num_ranges = ARRAY_SIZE(da9211_regmap_range), }; @@ -332,10 +346,8 @@ static irqreturn_t da9211_irq_handler(int irq, void *data) goto error_i2c; if (reg_val & DA9211_E_OV_CURR_A) { - regulator_lock(chip->rdev[0]); regulator_notifier_call_chain(chip->rdev[0], REGULATOR_EVENT_OVER_CURRENT, NULL); - regulator_unlock(chip->rdev[0]); err = regmap_write(chip->regmap, DA9211_REG_EVENT_B, DA9211_E_OV_CURR_A); @@ -346,10 +358,8 @@ static irqreturn_t da9211_irq_handler(int irq, void *data) } if (reg_val & DA9211_E_OV_CURR_B) { - regulator_lock(chip->rdev[1]); regulator_notifier_call_chain(chip->rdev[1], REGULATOR_EVENT_OVER_CURRENT, NULL); - regulator_unlock(chip->rdev[1]); err = regmap_write(chip->regmap, DA9211_REG_EVENT_B, DA9211_E_OV_CURR_B); diff --git a/drivers/regulator/dbx500-prcmu.c b/drivers/regulator/dbx500-prcmu.c index c3ad6aa6b5d3..8b70bfe88019 100644 --- a/drivers/regulator/dbx500-prcmu.c +++ b/drivers/regulator/dbx500-prcmu.c @@ -67,8 +67,6 @@ static int power_state_active_get(void) static struct ux500_regulator_debug { struct dentry *dir; - struct dentry *status_file; - struct dentry *power_state_cnt_file; struct dbx500_regulator_info *regulator_array; int num_regulators; u8 *state_before_suspend; @@ -117,22 +115,14 @@ ux500_regulator_debug_init(struct platform_device *pdev, { /* create directory */ rdebug.dir = debugfs_create_dir("ux500-regulator", NULL); - if (!rdebug.dir) - goto exit_no_debugfs; /* create "status" file */ - rdebug.status_file = debugfs_create_file("status", - S_IRUGO, rdebug.dir, &pdev->dev, - &ux500_regulator_status_fops); - if (!rdebug.status_file) - goto exit_destroy_dir; + debugfs_create_file("status", S_IRUGO, rdebug.dir, &pdev->dev, + &ux500_regulator_status_fops); /* create "power-state-count" file */ - rdebug.power_state_cnt_file = debugfs_create_file("power-state-count", - S_IRUGO, rdebug.dir, &pdev->dev, - &ux500_regulator_power_state_cnt_fops); - if (!rdebug.power_state_cnt_file) - goto exit_destroy_status; + debugfs_create_file("power-state-count", S_IRUGO, rdebug.dir, + &pdev->dev, &ux500_regulator_power_state_cnt_fops); rdebug.regulator_array = regulator_info; rdebug.num_regulators = num_regulators; @@ -150,13 +140,7 @@ ux500_regulator_debug_init(struct platform_device *pdev, exit_free: kfree(rdebug.state_before_suspend); exit_destroy_power_state: - debugfs_remove(rdebug.power_state_cnt_file); -exit_destroy_status: - debugfs_remove(rdebug.status_file); -exit_destroy_dir: - debugfs_remove(rdebug.dir); -exit_no_debugfs: - dev_err(&pdev->dev, "failed to create debugfs entries.\n"); + debugfs_remove_recursive(rdebug.dir); return -ENOMEM; } diff --git a/drivers/regulator/dummy.c b/drivers/regulator/dummy.c index 74de6983c61a..d8059f596391 100644 --- a/drivers/regulator/dummy.c +++ b/drivers/regulator/dummy.c @@ -21,13 +21,13 @@ struct regulator_dev *dummy_regulator_rdev; -static struct regulator_init_data dummy_initdata = { +static const struct regulator_init_data dummy_initdata = { .constraints = { .always_on = 1, }, }; -static struct regulator_ops dummy_ops; +static const struct regulator_ops dummy_ops; static const struct regulator_desc dummy_desc = { .name = "regulator-dummy", diff --git a/drivers/regulator/fan53555.c b/drivers/regulator/fan53555.c index 00c83492f774..aa426183b6a1 100644 --- a/drivers/regulator/fan53555.c +++ b/drivers/regulator/fan53555.c @@ -436,7 +436,7 @@ static struct fan53555_platform_data *fan53555_parse_dt(struct device *dev, return pdata; } -static const struct of_device_id fan53555_dt_ids[] = { +static const struct of_device_id __maybe_unused fan53555_dt_ids[] = { { .compatible = "fcs,fan53526", .data = (void *)FAN53526_VENDOR_FAIRCHILD, diff --git a/drivers/regulator/fixed.c b/drivers/regulator/fixed.c index 142a70a89153..02ad83153e19 100644 --- a/drivers/regulator/fixed.c +++ b/drivers/regulator/fixed.c @@ -18,6 +18,8 @@ #include <linux/mutex.h> #include <linux/module.h> #include <linux/platform_device.h> +#include <linux/pm_domain.h> +#include <linux/pm_opp.h> #include <linux/regulator/driver.h> #include <linux/regulator/fixed.h> #include <linux/gpio/consumer.h> @@ -34,19 +36,13 @@ struct fixed_voltage_data { struct regulator_dev *dev; struct clk *enable_clock; - unsigned int clk_enable_counter; + unsigned int enable_counter; + int performance_state; }; struct fixed_dev_type { bool has_enable_clock; -}; - -static const struct fixed_dev_type fixed_voltage_data = { - .has_enable_clock = false, -}; - -static const struct fixed_dev_type fixed_clkenable_data = { - .has_enable_clock = true, + bool has_performance_state; }; static int reg_clock_enable(struct regulator_dev *rdev) @@ -58,7 +54,7 @@ static int reg_clock_enable(struct regulator_dev *rdev) if (ret) return ret; - priv->clk_enable_counter++; + priv->enable_counter++; return ret; } @@ -68,16 +64,41 @@ static int reg_clock_disable(struct regulator_dev *rdev) struct fixed_voltage_data *priv = rdev_get_drvdata(rdev); clk_disable_unprepare(priv->enable_clock); - priv->clk_enable_counter--; + priv->enable_counter--; return 0; } -static int reg_clock_is_enabled(struct regulator_dev *rdev) +static int reg_domain_enable(struct regulator_dev *rdev) { struct fixed_voltage_data *priv = rdev_get_drvdata(rdev); + struct device *dev = rdev->dev.parent; + int ret; - return priv->clk_enable_counter > 0; + ret = dev_pm_genpd_set_performance_state(dev, priv->performance_state); + if (ret) + return ret; + + priv->enable_counter++; + + return ret; +} + +static int reg_domain_disable(struct regulator_dev *rdev) +{ + struct fixed_voltage_data *priv = rdev_get_drvdata(rdev); + struct device *dev = rdev->dev.parent; + + priv->enable_counter--; + + return dev_pm_genpd_set_performance_state(dev, 0); +} + +static int reg_is_enabled(struct regulator_dev *rdev) +{ + struct fixed_voltage_data *priv = rdev_get_drvdata(rdev); + + return priv->enable_counter > 0; } @@ -131,13 +152,19 @@ of_get_fixed_voltage_config(struct device *dev, return config; } -static struct regulator_ops fixed_voltage_ops = { +static const struct regulator_ops fixed_voltage_ops = { }; -static struct regulator_ops fixed_voltage_clkenabled_ops = { +static const struct regulator_ops fixed_voltage_clkenabled_ops = { .enable = reg_clock_enable, .disable = reg_clock_disable, - .is_enabled = reg_clock_is_enabled, + .is_enabled = reg_is_enabled, +}; + +static const struct regulator_ops fixed_voltage_domain_ops = { + .enable = reg_domain_enable, + .disable = reg_domain_disable, + .is_enabled = reg_is_enabled, }; static int reg_fixed_voltage_probe(struct platform_device *pdev) @@ -185,6 +212,14 @@ static int reg_fixed_voltage_probe(struct platform_device *pdev) dev_err(dev, "Can't get enable-clock from devicetree\n"); return -ENOENT; } + } else if (drvtype && drvtype->has_performance_state) { + drvdata->desc.ops = &fixed_voltage_domain_ops; + + drvdata->performance_state = of_get_required_opp_performance_state(dev->of_node, 0); + if (drvdata->performance_state < 0) { + dev_err(dev, "Can't get performance state from devicetree\n"); + return drvdata->performance_state; + } } else { drvdata->desc.ops = &fixed_voltage_ops; } @@ -260,6 +295,18 @@ static int reg_fixed_voltage_probe(struct platform_device *pdev) } #if defined(CONFIG_OF) +static const struct fixed_dev_type fixed_voltage_data = { + .has_enable_clock = false, +}; + +static const struct fixed_dev_type fixed_clkenable_data = { + .has_enable_clock = true, +}; + +static const struct fixed_dev_type fixed_domain_data = { + .has_performance_state = true, +}; + static const struct of_device_id fixed_of_match[] = { { .compatible = "regulator-fixed", @@ -270,6 +317,10 @@ static const struct of_device_id fixed_of_match[] = { .data = &fixed_clkenable_data, }, { + .compatible = "regulator-fixed-domain", + .data = &fixed_domain_data, + }, + { }, }; MODULE_DEVICE_TABLE(of, fixed_of_match); diff --git a/drivers/regulator/helpers.c b/drivers/regulator/helpers.c index e4bb09bbd3fa..f42b394a0c46 100644 --- a/drivers/regulator/helpers.c +++ b/drivers/regulator/helpers.c @@ -649,6 +649,8 @@ int regulator_list_voltage_table(struct regulator_dev *rdev, if (selector >= rdev->desc->n_voltages) return -EINVAL; + if (selector < rdev->desc->linear_min_sel) + return 0; return rdev->desc->volt_table[selector]; } diff --git a/drivers/regulator/lochnagar-regulator.c b/drivers/regulator/lochnagar-regulator.c index 5ea3e4141684..cb71fa5f43c3 100644 --- a/drivers/regulator/lochnagar-regulator.c +++ b/drivers/regulator/lochnagar-regulator.c @@ -98,6 +98,7 @@ static const struct regulator_ops lochnagar_vddcore_ops = { }; static const struct linear_range lochnagar_vddcore_ranges[] = { + REGULATOR_LINEAR_RANGE(600000, 0, 0x7, 0), REGULATOR_LINEAR_RANGE(600000, 0x8, 0x41, 12500), }; diff --git a/drivers/regulator/lp872x.c b/drivers/regulator/lp872x.c index 303d7e2dc838..e84be29533f4 100644 --- a/drivers/regulator/lp872x.c +++ b/drivers/regulator/lp872x.c @@ -892,7 +892,7 @@ static int lp872x_probe(struct i2c_client *cl, const struct i2c_device_id *id) struct lp872x *lp; struct lp872x_platform_data *pdata; int ret; - const int lp872x_num_regulators[] = { + static const int lp872x_num_regulators[] = { [LP8720] = LP8720_NUM_REGULATORS, [LP8725] = LP8725_NUM_REGULATORS, }; diff --git a/drivers/regulator/lp8755.c b/drivers/regulator/lp8755.c index 4291df077c39..13c535711265 100644 --- a/drivers/regulator/lp8755.c +++ b/drivers/regulator/lp8755.c @@ -49,53 +49,15 @@ struct lp8755_chip { struct regulator_dev *rdev[LP8755_BUCK_MAX]; }; -/** - *lp8755_read : read a single register value from lp8755. - *@pchip : device to read from - *@reg : register to read from - *@val : pointer to store read value - */ -static int lp8755_read(struct lp8755_chip *pchip, unsigned int reg, - unsigned int *val) -{ - return regmap_read(pchip->regmap, reg, val); -} - -/** - *lp8755_write : write a single register value to lp8755. - *@pchip : device to write to - *@reg : register to write to - *@val : value to be written - */ -static int lp8755_write(struct lp8755_chip *pchip, unsigned int reg, - unsigned int val) -{ - return regmap_write(pchip->regmap, reg, val); -} - -/** - *lp8755_update_bits : set the values of bit fields in lp8755 register. - *@pchip : device to read from - *@reg : register to update - *@mask : bitmask to be changed - *@val : value for bitmask - */ -static int lp8755_update_bits(struct lp8755_chip *pchip, unsigned int reg, - unsigned int mask, unsigned int val) -{ - return regmap_update_bits(pchip->regmap, reg, mask, val); -} - static int lp8755_buck_enable_time(struct regulator_dev *rdev) { int ret; unsigned int regval; enum lp8755_bucks id = rdev_get_id(rdev); - struct lp8755_chip *pchip = rdev_get_drvdata(rdev); - ret = lp8755_read(pchip, 0x12 + id, ®val); + ret = regmap_read(rdev->regmap, 0x12 + id, ®val); if (ret < 0) { - dev_err(pchip->dev, "i2c access error %s\n", __func__); + dev_err(&rdev->dev, "i2c access error %s\n", __func__); return ret; } return (regval & 0xff) * 100; @@ -115,17 +77,17 @@ static int lp8755_buck_set_mode(struct regulator_dev *rdev, unsigned int mode) break; case REGULATOR_MODE_NORMAL: /* enable automatic pwm/pfm mode */ - ret = lp8755_update_bits(pchip, 0x08 + id, 0x20, 0x00); + ret = regmap_update_bits(rdev->regmap, 0x08 + id, 0x20, 0x00); if (ret < 0) goto err_i2c; break; case REGULATOR_MODE_IDLE: /* enable automatic pwm/pfm/lppfm mode */ - ret = lp8755_update_bits(pchip, 0x08 + id, 0x20, 0x20); + ret = regmap_update_bits(rdev->regmap, 0x08 + id, 0x20, 0x20); if (ret < 0) goto err_i2c; - ret = lp8755_update_bits(pchip, 0x10, 0x01, 0x01); + ret = regmap_update_bits(rdev->regmap, 0x10, 0x01, 0x01); if (ret < 0) goto err_i2c; break; @@ -135,12 +97,12 @@ static int lp8755_buck_set_mode(struct regulator_dev *rdev, unsigned int mode) regbval = (0x01 << id); } - ret = lp8755_update_bits(pchip, 0x06, 0x01 << id, regbval); + ret = regmap_update_bits(rdev->regmap, 0x06, 0x01 << id, regbval); if (ret < 0) goto err_i2c; return ret; err_i2c: - dev_err(pchip->dev, "i2c access error %s\n", __func__); + dev_err(&rdev->dev, "i2c access error %s\n", __func__); return ret; } @@ -149,9 +111,8 @@ static unsigned int lp8755_buck_get_mode(struct regulator_dev *rdev) int ret; unsigned int regval; enum lp8755_bucks id = rdev_get_id(rdev); - struct lp8755_chip *pchip = rdev_get_drvdata(rdev); - ret = lp8755_read(pchip, 0x06, ®val); + ret = regmap_read(rdev->regmap, 0x06, ®val); if (ret < 0) goto err_i2c; @@ -159,7 +120,7 @@ static unsigned int lp8755_buck_get_mode(struct regulator_dev *rdev) if (regval & (0x01 << id)) return REGULATOR_MODE_FAST; - ret = lp8755_read(pchip, 0x08 + id, ®val); + ret = regmap_read(rdev->regmap, 0x08 + id, ®val); if (ret < 0) goto err_i2c; @@ -171,7 +132,7 @@ static unsigned int lp8755_buck_get_mode(struct regulator_dev *rdev) return REGULATOR_MODE_NORMAL; err_i2c: - dev_err(pchip->dev, "i2c access error %s\n", __func__); + dev_err(&rdev->dev, "i2c access error %s\n", __func__); return 0; } @@ -180,7 +141,6 @@ static int lp8755_buck_set_ramp(struct regulator_dev *rdev, int ramp) int ret; unsigned int regval = 0x00; enum lp8755_bucks id = rdev_get_id(rdev); - struct lp8755_chip *pchip = rdev_get_drvdata(rdev); /* uV/us */ switch (ramp) { @@ -209,17 +169,17 @@ static int lp8755_buck_set_ramp(struct regulator_dev *rdev, int ramp) regval = 0x00; break; default: - dev_err(pchip->dev, + dev_err(&rdev->dev, "Not supported ramp value %d %s\n", ramp, __func__); return -EINVAL; } - ret = lp8755_update_bits(pchip, 0x07 + id, 0x07, regval); + ret = regmap_update_bits(rdev->regmap, 0x07 + id, 0x07, regval); if (ret < 0) goto err_i2c; return ret; err_i2c: - dev_err(pchip->dev, "i2c access error %s\n", __func__); + dev_err(&rdev->dev, "i2c access error %s\n", __func__); return ret; } @@ -278,7 +238,7 @@ static int lp8755_init_data(struct lp8755_chip *pchip) struct lp8755_platform_data *pdata = pchip->pdata; /* read back muti-phase configuration */ - ret = lp8755_read(pchip, 0x3D, ®val); + ret = regmap_read(pchip->regmap, 0x3D, ®val); if (ret < 0) goto out_i2c_error; pchip->mphase = regval & 0x0F; @@ -356,11 +316,11 @@ static irqreturn_t lp8755_irq_handler(int irq, void *data) struct lp8755_chip *pchip = data; /* read flag0 register */ - ret = lp8755_read(pchip, 0x0D, &flag0); + ret = regmap_read(pchip->regmap, 0x0D, &flag0); if (ret < 0) goto err_i2c; /* clear flag register to pull up int. pin */ - ret = lp8755_write(pchip, 0x0D, 0x00); + ret = regmap_write(pchip->regmap, 0x0D, 0x00); if (ret < 0) goto err_i2c; @@ -369,19 +329,17 @@ static irqreturn_t lp8755_irq_handler(int irq, void *data) if ((flag0 & (0x4 << icnt)) && (pchip->irqmask & (0x04 << icnt)) && (pchip->rdev[icnt] != NULL)) { - regulator_lock(pchip->rdev[icnt]); regulator_notifier_call_chain(pchip->rdev[icnt], LP8755_EVENT_PWR_FAULT, NULL); - regulator_unlock(pchip->rdev[icnt]); } /* read flag1 register */ - ret = lp8755_read(pchip, 0x0E, &flag1); + ret = regmap_read(pchip->regmap, 0x0E, &flag1); if (ret < 0) goto err_i2c; /* clear flag register to pull up int. pin */ - ret = lp8755_write(pchip, 0x0E, 0x00); + ret = regmap_write(pchip->regmap, 0x0E, 0x00); if (ret < 0) goto err_i2c; @@ -389,22 +347,18 @@ static irqreturn_t lp8755_irq_handler(int irq, void *data) if ((flag1 & 0x01) && (pchip->irqmask & 0x01)) for (icnt = 0; icnt < LP8755_BUCK_MAX; icnt++) if (pchip->rdev[icnt] != NULL) { - regulator_lock(pchip->rdev[icnt]); regulator_notifier_call_chain(pchip->rdev[icnt], LP8755_EVENT_OCP, NULL); - regulator_unlock(pchip->rdev[icnt]); } /* send OVP event to all regulator devices */ if ((flag1 & 0x02) && (pchip->irqmask & 0x02)) for (icnt = 0; icnt < LP8755_BUCK_MAX; icnt++) if (pchip->rdev[icnt] != NULL) { - regulator_lock(pchip->rdev[icnt]); regulator_notifier_call_chain(pchip->rdev[icnt], LP8755_EVENT_OVP, NULL); - regulator_unlock(pchip->rdev[icnt]); } return IRQ_HANDLED; @@ -423,7 +377,7 @@ static int lp8755_int_config(struct lp8755_chip *pchip) return 0; } - ret = lp8755_read(pchip, 0x0F, ®val); + ret = regmap_read(pchip->regmap, 0x0F, ®val); if (ret < 0) { dev_err(pchip->dev, "i2c access error %s\n", __func__); return ret; @@ -502,7 +456,7 @@ static int lp8755_probe(struct i2c_client *client, err: /* output disable */ for (icnt = 0; icnt < LP8755_BUCK_MAX; icnt++) - lp8755_write(pchip, icnt, 0x00); + regmap_write(pchip->regmap, icnt, 0x00); return ret; } @@ -513,7 +467,7 @@ static int lp8755_remove(struct i2c_client *client) struct lp8755_chip *pchip = i2c_get_clientdata(client); for (icnt = 0; icnt < LP8755_BUCK_MAX; icnt++) - lp8755_write(pchip, icnt, 0x00); + regmap_write(pchip->regmap, icnt, 0x00); return 0; } diff --git a/drivers/regulator/ltc3589.c b/drivers/regulator/ltc3589.c index 9a037fdc5fc5..38f7ccb63b52 100644 --- a/drivers/regulator/ltc3589.c +++ b/drivers/regulator/ltc3589.c @@ -357,22 +357,16 @@ static irqreturn_t ltc3589_isr(int irq, void *dev_id) if (irqstat & LTC3589_IRQSTAT_THERMAL_WARN) { event = REGULATOR_EVENT_OVER_TEMP; - for (i = 0; i < LTC3589_NUM_REGULATORS; i++) { - regulator_lock(ltc3589->regulators[i]); + for (i = 0; i < LTC3589_NUM_REGULATORS; i++) regulator_notifier_call_chain(ltc3589->regulators[i], event, NULL); - regulator_unlock(ltc3589->regulators[i]); - } } if (irqstat & LTC3589_IRQSTAT_UNDERVOLT_WARN) { event = REGULATOR_EVENT_UNDER_VOLTAGE; - for (i = 0; i < LTC3589_NUM_REGULATORS; i++) { - regulator_lock(ltc3589->regulators[i]); + for (i = 0; i < LTC3589_NUM_REGULATORS; i++) regulator_notifier_call_chain(ltc3589->regulators[i], event, NULL); - regulator_unlock(ltc3589->regulators[i]); - } } /* Clear warning condition */ @@ -457,7 +451,7 @@ static const struct i2c_device_id ltc3589_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, ltc3589_i2c_id); -static const struct of_device_id ltc3589_of_match[] = { +static const struct of_device_id __maybe_unused ltc3589_of_match[] = { { .compatible = "lltc,ltc3589", .data = (void *)LTC3589, diff --git a/drivers/regulator/ltc3676.c b/drivers/regulator/ltc3676.c index 093b3e4a6303..eb3d6bed6d54 100644 --- a/drivers/regulator/ltc3676.c +++ b/drivers/regulator/ltc3676.c @@ -276,23 +276,17 @@ static irqreturn_t ltc3676_isr(int irq, void *dev_id) if (irqstat & LTC3676_IRQSTAT_THERMAL_WARN) { dev_warn(dev, "Over-temperature Warning\n"); event = REGULATOR_EVENT_OVER_TEMP; - for (i = 0; i < LTC3676_NUM_REGULATORS; i++) { - regulator_lock(ltc3676->regulators[i]); + for (i = 0; i < LTC3676_NUM_REGULATORS; i++) regulator_notifier_call_chain(ltc3676->regulators[i], event, NULL); - regulator_unlock(ltc3676->regulators[i]); - } } if (irqstat & LTC3676_IRQSTAT_UNDERVOLT_WARN) { dev_info(dev, "Undervoltage Warning\n"); event = REGULATOR_EVENT_UNDER_VOLTAGE; - for (i = 0; i < LTC3676_NUM_REGULATORS; i++) { - regulator_lock(ltc3676->regulators[i]); + for (i = 0; i < LTC3676_NUM_REGULATORS; i++) regulator_notifier_call_chain(ltc3676->regulators[i], event, NULL); - regulator_unlock(ltc3676->regulators[i]); - } } /* Clear warning condition */ @@ -368,7 +362,7 @@ static const struct i2c_device_id ltc3676_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, ltc3676_i2c_id); -static const struct of_device_id ltc3676_of_match[] = { +static const struct of_device_id __maybe_unused ltc3676_of_match[] = { { .compatible = "lltc,ltc3676" }, { }, }; diff --git a/drivers/regulator/max14577-regulator.c b/drivers/regulator/max14577-regulator.c index e34face736f4..1d78b455cc48 100644 --- a/drivers/regulator/max14577-regulator.c +++ b/drivers/regulator/max14577-regulator.c @@ -269,3 +269,5 @@ module_exit(max14577_regulator_exit); MODULE_AUTHOR("Krzysztof Kozlowski <krzk@kernel.org>"); MODULE_DESCRIPTION("Maxim 14577/77836 regulator driver"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:max14577-regulator"); +MODULE_ALIAS("platform:max77836-regulator"); diff --git a/drivers/regulator/max1586.c b/drivers/regulator/max1586.c index f8941025780b..d4958394e608 100644 --- a/drivers/regulator/max1586.c +++ b/drivers/regulator/max1586.c @@ -201,7 +201,7 @@ static int of_get_max1586_platform_data(struct device *dev, return 0; } -static const struct of_device_id max1586_of_match[] = { +static const struct of_device_id __maybe_unused max1586_of_match[] = { { .compatible = "maxim,max1586", }, {}, }; diff --git a/drivers/regulator/max77826-regulator.c b/drivers/regulator/max77826-regulator.c index 502ab6afc814..f9e2e884ff54 100644 --- a/drivers/regulator/max77826-regulator.c +++ b/drivers/regulator/max77826-regulator.c @@ -274,7 +274,7 @@ static int max77826_i2c_probe(struct i2c_client *client) return max77826_read_device_id(regmap, dev); } -static const struct of_device_id max77826_of_match[] = { +static const struct of_device_id __maybe_unused max77826_of_match[] = { { .compatible = "maxim,max77826" }, { /* sentinel */ } }; diff --git a/drivers/regulator/mc13892-regulator.c b/drivers/regulator/mc13892-regulator.c index a731e826a037..5221f7a9df91 100644 --- a/drivers/regulator/mc13892-regulator.c +++ b/drivers/regulator/mc13892-regulator.c @@ -582,8 +582,8 @@ static int mc13892_regulator_probe(struct platform_device *pdev) /* update mc13892_vcam ops */ memcpy(&mc13892_vcam_ops, mc13892_regulators[MC13892_VCAM].desc.ops, sizeof(struct regulator_ops)); - mc13892_vcam_ops.set_mode = mc13892_vcam_set_mode, - mc13892_vcam_ops.get_mode = mc13892_vcam_get_mode, + mc13892_vcam_ops.set_mode = mc13892_vcam_set_mode; + mc13892_vcam_ops.get_mode = mc13892_vcam_get_mode; mc13892_regulators[MC13892_VCAM].desc.ops = &mc13892_vcam_ops; mc13xxx_data = mc13xxx_parse_regulators_dt(pdev, mc13892_regulators, diff --git a/drivers/regulator/mcp16502.c b/drivers/regulator/mcp16502.c index 6d0ad74935b3..74ad92dc664a 100644 --- a/drivers/regulator/mcp16502.c +++ b/drivers/regulator/mcp16502.c @@ -22,8 +22,9 @@ #define VDD_LOW_SEL 0x0D #define VDD_HIGH_SEL 0x3F -#define MCP16502_FLT BIT(7) -#define MCP16502_ENS BIT(0) +#define MCP16502_FLT BIT(7) +#define MCP16502_DVSR GENMASK(3, 2) +#define MCP16502_ENS BIT(0) /* * The PMIC has four sets of registers corresponding to four power modes: @@ -54,13 +55,9 @@ * This function is useful for iterating over all regulators and accessing their * registers in a generic way or accessing a regulator device by its id. */ -#define MCP16502_BASE(i) (((i) + 1) << 4) +#define MCP16502_REG_BASE(i, r) ((((i) + 1) << 4) + MCP16502_REG_##r) #define MCP16502_STAT_BASE(i) ((i) + 5) -#define MCP16502_OFFSET_MODE_A 0 -#define MCP16502_OFFSET_MODE_LPM 1 -#define MCP16502_OFFSET_MODE_HIB 2 - #define MCP16502_OPMODE_ACTIVE REGULATOR_MODE_NORMAL #define MCP16502_OPMODE_LPM REGULATOR_MODE_IDLE #define MCP16502_OPMODE_HIB REGULATOR_MODE_STANDBY @@ -75,6 +72,29 @@ #define MCP16502_MIN_REG 0x0 #define MCP16502_MAX_REG 0x65 +/** + * enum mcp16502_reg - MCP16502 regulators's registers + * @MCP16502_REG_A: active state register + * @MCP16502_REG_LPM: low power mode state register + * @MCP16502_REG_HIB: hibernate state register + * @MCP16502_REG_SEQ: startup sequence register + * @MCP16502_REG_CFG: configuration register + */ +enum mcp16502_reg { + MCP16502_REG_A, + MCP16502_REG_LPM, + MCP16502_REG_HIB, + MCP16502_REG_HPM, + MCP16502_REG_SEQ, + MCP16502_REG_CFG, +}; + +/* Ramp delay (uV/us) for buck1, ldo1, ldo2. */ +static const int mcp16502_ramp_b1l12[] = { 6250, 3125, 2083, 1563 }; + +/* Ramp delay (uV/us) for buck2, buck3, buck4. */ +static const int mcp16502_ramp_b234[] = { 3125, 1563, 1042, 781 }; + static unsigned int mcp16502_of_map_mode(unsigned int mode) { if (mode == REGULATOR_MODE_NORMAL || mode == REGULATOR_MODE_IDLE) @@ -93,6 +113,7 @@ static unsigned int mcp16502_of_map_mode(unsigned int mode) .owner = THIS_MODULE, \ .n_voltages = MCP16502_VSEL + 1, \ .linear_ranges = _ranges, \ + .linear_min_sel = VDD_LOW_SEL, \ .n_linear_ranges = ARRAY_SIZE(_ranges), \ .of_match = of_match_ptr(_name), \ .of_map_mode = mcp16502_of_map_mode, \ @@ -114,8 +135,6 @@ enum { /* * struct mcp16502 - PMIC representation - * @rdev: the regulators belonging to this chip - * @rmap: regmap to be used for I2C communication * @lpm: LPM GPIO descriptor */ struct mcp16502 { @@ -143,22 +162,20 @@ static void mcp16502_gpio_set_mode(struct mcp16502 *mcp, int mode) } /* - * mcp16502_get_reg() - get the PMIC's configuration register for opmode + * mcp16502_get_reg() - get the PMIC's state configuration register for opmode * * @rdev: the regulator whose register we are searching * @opmode: the PMIC's operating mode ACTIVE, Low-power, Hibernate */ -static int mcp16502_get_reg(struct regulator_dev *rdev, int opmode) +static int mcp16502_get_state_reg(struct regulator_dev *rdev, int opmode) { - int reg = MCP16502_BASE(rdev_get_id(rdev)); - switch (opmode) { case MCP16502_OPMODE_ACTIVE: - return reg + MCP16502_OFFSET_MODE_A; + return MCP16502_REG_BASE(rdev_get_id(rdev), A); case MCP16502_OPMODE_LPM: - return reg + MCP16502_OFFSET_MODE_LPM; + return MCP16502_REG_BASE(rdev_get_id(rdev), LPM); case MCP16502_OPMODE_HIB: - return reg + MCP16502_OFFSET_MODE_HIB; + return MCP16502_REG_BASE(rdev_get_id(rdev), HIB); default: return -EINVAL; } @@ -178,7 +195,7 @@ static unsigned int mcp16502_get_mode(struct regulator_dev *rdev) unsigned int val; int ret, reg; - reg = mcp16502_get_reg(rdev, MCP16502_OPMODE_ACTIVE); + reg = mcp16502_get_state_reg(rdev, MCP16502_OPMODE_ACTIVE); if (reg < 0) return reg; @@ -209,7 +226,7 @@ static int _mcp16502_set_mode(struct regulator_dev *rdev, unsigned int mode, int val; int reg; - reg = mcp16502_get_reg(rdev, op_mode); + reg = mcp16502_get_state_reg(rdev, op_mode); if (reg < 0) return reg; @@ -259,6 +276,80 @@ static int mcp16502_get_status(struct regulator_dev *rdev) return REGULATOR_STATUS_UNDEFINED; } +static int mcp16502_set_voltage_time_sel(struct regulator_dev *rdev, + unsigned int old_sel, + unsigned int new_sel) +{ + static const u8 us_ramp[] = { 8, 16, 24, 32 }; + int id = rdev_get_id(rdev); + unsigned int uV_delta, val; + int ret; + + ret = regmap_read(rdev->regmap, MCP16502_REG_BASE(id, CFG), &val); + if (ret) + return ret; + + val = (val & MCP16502_DVSR) >> 2; + uV_delta = abs(new_sel * rdev->desc->linear_ranges->step - + old_sel * rdev->desc->linear_ranges->step); + switch (id) { + case BUCK1: + case LDO1: + case LDO2: + ret = DIV_ROUND_CLOSEST(uV_delta * us_ramp[val], + mcp16502_ramp_b1l12[val]); + break; + + case BUCK2: + case BUCK3: + case BUCK4: + ret = DIV_ROUND_CLOSEST(uV_delta * us_ramp[val], + mcp16502_ramp_b234[val]); + break; + + default: + return -EINVAL; + } + + return ret; +} + +static int mcp16502_set_ramp_delay(struct regulator_dev *rdev, int ramp_delay) +{ + const int *ramp; + int id = rdev_get_id(rdev); + unsigned int i, size; + + switch (id) { + case BUCK1: + case LDO1: + case LDO2: + ramp = mcp16502_ramp_b1l12; + size = ARRAY_SIZE(mcp16502_ramp_b1l12); + break; + + case BUCK2: + case BUCK3: + case BUCK4: + ramp = mcp16502_ramp_b234; + size = ARRAY_SIZE(mcp16502_ramp_b234); + break; + + default: + return -EINVAL; + } + + for (i = 0; i < size; i++) { + if (ramp[i] == ramp_delay) + break; + } + if (i == size) + return -EINVAL; + + return regmap_update_bits(rdev->regmap, MCP16502_REG_BASE(id, CFG), + MCP16502_DVSR, (i << 2)); +} + #ifdef CONFIG_SUSPEND /* * mcp16502_suspend_get_target_reg() - get the reg of the target suspend PMIC @@ -268,10 +359,10 @@ static int mcp16502_suspend_get_target_reg(struct regulator_dev *rdev) { switch (pm_suspend_target_state) { case PM_SUSPEND_STANDBY: - return mcp16502_get_reg(rdev, MCP16502_OPMODE_LPM); + return mcp16502_get_state_reg(rdev, MCP16502_OPMODE_LPM); case PM_SUSPEND_ON: case PM_SUSPEND_MEM: - return mcp16502_get_reg(rdev, MCP16502_OPMODE_HIB); + return mcp16502_get_state_reg(rdev, MCP16502_OPMODE_HIB); default: dev_err(&rdev->dev, "invalid suspend target: %d\n", pm_suspend_target_state); @@ -353,6 +444,8 @@ static const struct regulator_ops mcp16502_buck_ops = { .disable = regulator_disable_regmap, .is_enabled = regulator_is_enabled_regmap, .get_status = mcp16502_get_status, + .set_voltage_time_sel = mcp16502_set_voltage_time_sel, + .set_ramp_delay = mcp16502_set_ramp_delay, .set_mode = mcp16502_set_mode, .get_mode = mcp16502_get_mode, @@ -377,6 +470,8 @@ static const struct regulator_ops mcp16502_ldo_ops = { .disable = regulator_disable_regmap, .is_enabled = regulator_is_enabled_regmap, .get_status = mcp16502_get_status, + .set_voltage_time_sel = mcp16502_set_voltage_time_sel, + .set_ramp_delay = mcp16502_set_ramp_delay, #ifdef CONFIG_SUSPEND .set_suspend_voltage = mcp16502_set_suspend_voltage, diff --git a/drivers/regulator/mp886x.c b/drivers/regulator/mp886x.c index d3d475f717f4..a84fd74081de 100644 --- a/drivers/regulator/mp886x.c +++ b/drivers/regulator/mp886x.c @@ -18,18 +18,70 @@ #define MP886X_V_BOOT (1 << 7) #define MP886X_SYSCNTLREG1 0x01 #define MP886X_MODE (1 << 0) +#define MP886X_SLEW_SHIFT 3 +#define MP886X_SLEW_MASK (0x7 << MP886X_SLEW_SHIFT) #define MP886X_GO (1 << 6) #define MP886X_EN (1 << 7) +#define MP8869_SYSCNTLREG2 0x02 + +struct mp886x_cfg_info { + const struct regulator_ops *rops; + const int slew_rates[8]; + const int switch_freq[4]; + const u8 fs_reg; + const u8 fs_shift; +}; struct mp886x_device_info { struct device *dev; struct regulator_desc desc; struct regulator_init_data *regulator; struct gpio_desc *en_gpio; + const struct mp886x_cfg_info *ci; u32 r[2]; unsigned int sel; }; +static int mp886x_set_ramp(struct regulator_dev *rdev, int ramp) +{ + struct mp886x_device_info *di = rdev_get_drvdata(rdev); + const struct mp886x_cfg_info *ci = di->ci; + int reg = -1, i; + + for (i = 0; i < ARRAY_SIZE(ci->slew_rates); i++) { + if (ramp <= ci->slew_rates[i]) + reg = i; + else + break; + } + + if (reg < 0) { + dev_err(di->dev, "unsupported ramp value %d\n", ramp); + return -EINVAL; + } + + return regmap_update_bits(rdev->regmap, MP886X_SYSCNTLREG1, + MP886X_SLEW_MASK, reg << MP886X_SLEW_SHIFT); +} + +static void mp886x_set_switch_freq(struct mp886x_device_info *di, + struct regmap *regmap, + u32 freq) +{ + const struct mp886x_cfg_info *ci = di->ci; + int i; + + for (i = 0; i < ARRAY_SIZE(ci->switch_freq); i++) { + if (freq == ci->switch_freq[i]) { + regmap_update_bits(regmap, ci->fs_reg, + 0x3 << ci->fs_shift, i << ci->fs_shift); + return; + } + } + + dev_err(di->dev, "invalid frequency %d\n", freq); +} + static int mp886x_set_mode(struct regulator_dev *rdev, unsigned int mode) { switch (mode) { @@ -117,6 +169,29 @@ static const struct regulator_ops mp8869_regulator_ops = { .is_enabled = regulator_is_enabled_regmap, .set_mode = mp886x_set_mode, .get_mode = mp886x_get_mode, + .set_ramp_delay = mp886x_set_ramp, +}; + +static const struct mp886x_cfg_info mp8869_ci = { + .rops = &mp8869_regulator_ops, + .slew_rates = { + 40000, + 30000, + 20000, + 10000, + 5000, + 2500, + 1250, + 625, + }, + .switch_freq = { + 500000, + 750000, + 1000000, + 1250000, + }, + .fs_reg = MP8869_SYSCNTLREG2, + .fs_shift = 4, }; static int mp8867_set_voltage_sel(struct regulator_dev *rdev, unsigned int sel) @@ -173,6 +248,29 @@ static const struct regulator_ops mp8867_regulator_ops = { .is_enabled = regulator_is_enabled_regmap, .set_mode = mp886x_set_mode, .get_mode = mp886x_get_mode, + .set_ramp_delay = mp886x_set_ramp, +}; + +static const struct mp886x_cfg_info mp8867_ci = { + .rops = &mp8867_regulator_ops, + .slew_rates = { + 64000, + 32000, + 16000, + 8000, + 4000, + 2000, + 1000, + 500, + }, + .switch_freq = { + 500000, + 750000, + 1000000, + 1500000, + }, + .fs_reg = MP886X_SYSCNTLREG1, + .fs_shift = 1, }; static int mp886x_regulator_register(struct mp886x_device_info *di, @@ -183,7 +281,7 @@ static int mp886x_regulator_register(struct mp886x_device_info *di, rdesc->name = "mp886x-reg"; rdesc->supply_name = "vin"; - rdesc->ops = of_device_get_match_data(di->dev); + rdesc->ops = di->ci->rops; rdesc->type = REGULATOR_VOLTAGE; rdesc->n_voltages = 128; rdesc->enable_reg = MP886X_SYSCNTLREG1; @@ -213,6 +311,7 @@ static int mp886x_i2c_probe(struct i2c_client *client) struct mp886x_device_info *di; struct regulator_config config = { }; struct regmap *regmap; + u32 freq; int ret; di = devm_kzalloc(dev, sizeof(struct mp886x_device_info), GFP_KERNEL); @@ -234,6 +333,7 @@ static int mp886x_i2c_probe(struct i2c_client *client) if (IS_ERR(di->en_gpio)) return PTR_ERR(di->en_gpio); + di->ci = of_device_get_match_data(dev); di->dev = dev; regmap = devm_regmap_init_i2c(client, &mp886x_regmap_config); @@ -249,6 +349,9 @@ static int mp886x_i2c_probe(struct i2c_client *client) config.driver_data = di; config.of_node = np; + if (!of_property_read_u32(np, "mps,switch-frequency-hz", &freq)) + mp886x_set_switch_freq(di, regmap, freq); + ret = mp886x_regulator_register(di, &config); if (ret < 0) dev_err(dev, "Failed to register regulator!\n"); @@ -258,11 +361,11 @@ static int mp886x_i2c_probe(struct i2c_client *client) static const struct of_device_id mp886x_dt_ids[] = { { .compatible = "mps,mp8867", - .data = &mp8867_regulator_ops + .data = &mp8867_ci }, { .compatible = "mps,mp8869", - .data = &mp8869_regulator_ops + .data = &mp8869_ci }, { } }; diff --git a/drivers/regulator/mt6360-regulator.c b/drivers/regulator/mt6360-regulator.c new file mode 100644 index 000000000000..15308ee29c13 --- /dev/null +++ b/drivers/regulator/mt6360-regulator.c @@ -0,0 +1,459 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright (C) 2020 MediaTek Inc. +// +// Author: Gene Chen <gene_chen@richtek.com> + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> + +#include <dt-bindings/regulator/mediatek,mt6360-regulator.h> + +enum { + MT6360_REGULATOR_BUCK1 = 0, + MT6360_REGULATOR_BUCK2, + MT6360_REGULATOR_LDO6, + MT6360_REGULATOR_LDO7, + MT6360_REGULATOR_LDO1, + MT6360_REGULATOR_LDO2, + MT6360_REGULATOR_LDO3, + MT6360_REGULATOR_LDO5, + MT6360_REGULATOR_MAX, +}; + +struct mt6360_irq_mapping { + const char *name; + irq_handler_t handler; +}; + +struct mt6360_regulator_desc { + const struct regulator_desc desc; + unsigned int mode_reg; + unsigned int mode_mask; + unsigned int state_reg; + unsigned int state_mask; + const struct mt6360_irq_mapping *irq_tables; + int irq_table_size; +}; + +struct mt6360_regulator_data { + struct device *dev; + struct regmap *regmap; +}; + +static irqreturn_t mt6360_pgb_event_handler(int irq, void *data) +{ + struct regulator_dev *rdev = data; + + regulator_notifier_call_chain(rdev, REGULATOR_EVENT_FAIL, NULL); + return IRQ_HANDLED; +} + +static irqreturn_t mt6360_oc_event_handler(int irq, void *data) +{ + struct regulator_dev *rdev = data; + + regulator_notifier_call_chain(rdev, REGULATOR_EVENT_OVER_CURRENT, NULL); + return IRQ_HANDLED; +} + +static irqreturn_t mt6360_ov_event_handler(int irq, void *data) +{ + struct regulator_dev *rdev = data; + + regulator_notifier_call_chain(rdev, REGULATOR_EVENT_REGULATION_OUT, NULL); + return IRQ_HANDLED; +} + +static irqreturn_t mt6360_uv_event_handler(int irq, void *data) +{ + struct regulator_dev *rdev = data; + + regulator_notifier_call_chain(rdev, REGULATOR_EVENT_UNDER_VOLTAGE, NULL); + return IRQ_HANDLED; +} + +static const struct mt6360_irq_mapping buck1_irq_tbls[] = { + { "buck1_pgb_evt", mt6360_pgb_event_handler }, + { "buck1_oc_evt", mt6360_oc_event_handler }, + { "buck1_ov_evt", mt6360_ov_event_handler }, + { "buck1_uv_evt", mt6360_uv_event_handler }, +}; + +static const struct mt6360_irq_mapping buck2_irq_tbls[] = { + { "buck2_pgb_evt", mt6360_pgb_event_handler }, + { "buck2_oc_evt", mt6360_oc_event_handler }, + { "buck2_ov_evt", mt6360_ov_event_handler }, + { "buck2_uv_evt", mt6360_uv_event_handler }, +}; + +static const struct mt6360_irq_mapping ldo6_irq_tbls[] = { + { "ldo6_pgb_evt", mt6360_pgb_event_handler }, + { "ldo6_oc_evt", mt6360_oc_event_handler }, +}; + +static const struct mt6360_irq_mapping ldo7_irq_tbls[] = { + { "ldo7_pgb_evt", mt6360_pgb_event_handler }, + { "ldo7_oc_evt", mt6360_oc_event_handler }, +}; + +static const struct mt6360_irq_mapping ldo1_irq_tbls[] = { + { "ldo1_pgb_evt", mt6360_pgb_event_handler }, + { "ldo1_oc_evt", mt6360_oc_event_handler }, +}; + +static const struct mt6360_irq_mapping ldo2_irq_tbls[] = { + { "ldo2_pgb_evt", mt6360_pgb_event_handler }, + { "ldo2_oc_evt", mt6360_oc_event_handler }, +}; + +static const struct mt6360_irq_mapping ldo3_irq_tbls[] = { + { "ldo3_pgb_evt", mt6360_pgb_event_handler }, + { "ldo3_oc_evt", mt6360_oc_event_handler }, +}; + +static const struct mt6360_irq_mapping ldo5_irq_tbls[] = { + { "ldo5_pgb_evt", mt6360_pgb_event_handler }, + { "ldo5_oc_evt", mt6360_oc_event_handler }, +}; + +static const struct linear_range buck_vout_ranges[] = { + REGULATOR_LINEAR_RANGE(300000, 0x00, 0xc7, 5000), + REGULATOR_LINEAR_RANGE(1300000, 0xc8, 0xff, 0), +}; + +static const struct linear_range ldo_vout_ranges1[] = { + REGULATOR_LINEAR_RANGE(500000, 0x00, 0x09, 10000), + REGULATOR_LINEAR_RANGE(600000, 0x0a, 0x10, 0), + REGULATOR_LINEAR_RANGE(610000, 0x11, 0x19, 10000), + REGULATOR_LINEAR_RANGE(700000, 0x1a, 0x20, 0), + REGULATOR_LINEAR_RANGE(710000, 0x21, 0x29, 10000), + REGULATOR_LINEAR_RANGE(800000, 0x2a, 0x30, 0), + REGULATOR_LINEAR_RANGE(810000, 0x31, 0x39, 10000), + REGULATOR_LINEAR_RANGE(900000, 0x3a, 0x40, 0), + REGULATOR_LINEAR_RANGE(910000, 0x41, 0x49, 10000), + REGULATOR_LINEAR_RANGE(1000000, 0x4a, 0x50, 0), + REGULATOR_LINEAR_RANGE(1010000, 0x51, 0x59, 10000), + REGULATOR_LINEAR_RANGE(1100000, 0x5a, 0x60, 0), + REGULATOR_LINEAR_RANGE(1110000, 0x61, 0x69, 10000), + REGULATOR_LINEAR_RANGE(1200000, 0x6a, 0x70, 0), + REGULATOR_LINEAR_RANGE(1210000, 0x71, 0x79, 10000), + REGULATOR_LINEAR_RANGE(1300000, 0x7a, 0x80, 0), + REGULATOR_LINEAR_RANGE(1310000, 0x81, 0x89, 10000), + REGULATOR_LINEAR_RANGE(1400000, 0x8a, 0x90, 0), + REGULATOR_LINEAR_RANGE(1410000, 0x91, 0x99, 10000), + REGULATOR_LINEAR_RANGE(1500000, 0x9a, 0xa0, 0), + REGULATOR_LINEAR_RANGE(1510000, 0xa1, 0xa9, 10000), + REGULATOR_LINEAR_RANGE(1600000, 0xaa, 0xb0, 0), + REGULATOR_LINEAR_RANGE(1610000, 0xb1, 0xb9, 10000), + REGULATOR_LINEAR_RANGE(1700000, 0xba, 0xc0, 0), + REGULATOR_LINEAR_RANGE(1710000, 0xc1, 0xc9, 10000), + REGULATOR_LINEAR_RANGE(1800000, 0xca, 0xd0, 0), + REGULATOR_LINEAR_RANGE(1810000, 0xd1, 0xd9, 10000), + REGULATOR_LINEAR_RANGE(1900000, 0xda, 0xe0, 0), + REGULATOR_LINEAR_RANGE(1910000, 0xe1, 0xe9, 10000), + REGULATOR_LINEAR_RANGE(2000000, 0xea, 0xf0, 0), + REGULATOR_LINEAR_RANGE(2010000, 0xf1, 0xf9, 10000), + REGULATOR_LINEAR_RANGE(2100000, 0xfa, 0xff, 0), +}; + +static const struct linear_range ldo_vout_ranges2[] = { + REGULATOR_LINEAR_RANGE(1200000, 0x00, 0x09, 10000), + REGULATOR_LINEAR_RANGE(1300000, 0x0a, 0x10, 0), + REGULATOR_LINEAR_RANGE(1310000, 0x11, 0x19, 10000), + REGULATOR_LINEAR_RANGE(1400000, 0x1a, 0x1f, 0), + REGULATOR_LINEAR_RANGE(1500000, 0x20, 0x29, 10000), + REGULATOR_LINEAR_RANGE(1600000, 0x2a, 0x2f, 0), + REGULATOR_LINEAR_RANGE(1700000, 0x30, 0x39, 10000), + REGULATOR_LINEAR_RANGE(1800000, 0x3a, 0x40, 0), + REGULATOR_LINEAR_RANGE(1810000, 0x41, 0x49, 10000), + REGULATOR_LINEAR_RANGE(1900000, 0x4a, 0x4f, 0), + REGULATOR_LINEAR_RANGE(2000000, 0x50, 0x59, 10000), + REGULATOR_LINEAR_RANGE(2100000, 0x5a, 0x60, 0), + REGULATOR_LINEAR_RANGE(2110000, 0x61, 0x69, 10000), + REGULATOR_LINEAR_RANGE(2200000, 0x6a, 0x6f, 0), + REGULATOR_LINEAR_RANGE(2500000, 0x70, 0x79, 10000), + REGULATOR_LINEAR_RANGE(2600000, 0x7a, 0x7f, 0), + REGULATOR_LINEAR_RANGE(2700000, 0x80, 0x89, 10000), + REGULATOR_LINEAR_RANGE(2800000, 0x8a, 0x90, 0), + REGULATOR_LINEAR_RANGE(2810000, 0x91, 0x99, 10000), + REGULATOR_LINEAR_RANGE(2900000, 0x9a, 0xa0, 0), + REGULATOR_LINEAR_RANGE(2910000, 0xa1, 0xa9, 10000), + REGULATOR_LINEAR_RANGE(3000000, 0xaa, 0xb0, 0), + REGULATOR_LINEAR_RANGE(3010000, 0xb1, 0xb9, 10000), + REGULATOR_LINEAR_RANGE(3100000, 0xba, 0xc0, 0), + REGULATOR_LINEAR_RANGE(3110000, 0xc1, 0xc9, 10000), + REGULATOR_LINEAR_RANGE(3200000, 0xca, 0xcf, 0), + REGULATOR_LINEAR_RANGE(3300000, 0xd0, 0xd9, 10000), + REGULATOR_LINEAR_RANGE(3400000, 0xda, 0xe0, 0), + REGULATOR_LINEAR_RANGE(3410000, 0xe1, 0xe9, 10000), + REGULATOR_LINEAR_RANGE(3500000, 0xea, 0xf0, 0), + REGULATOR_LINEAR_RANGE(3510000, 0xf1, 0xf9, 10000), + REGULATOR_LINEAR_RANGE(3600000, 0xfa, 0xff, 0), +}; + +static const struct linear_range ldo_vout_ranges3[] = { + REGULATOR_LINEAR_RANGE(2700000, 0x00, 0x09, 10000), + REGULATOR_LINEAR_RANGE(2800000, 0x0a, 0x10, 0), + REGULATOR_LINEAR_RANGE(2810000, 0x11, 0x19, 10000), + REGULATOR_LINEAR_RANGE(2900000, 0x1a, 0x20, 0), + REGULATOR_LINEAR_RANGE(2910000, 0x21, 0x29, 10000), + REGULATOR_LINEAR_RANGE(3000000, 0x2a, 0x30, 0), + REGULATOR_LINEAR_RANGE(3010000, 0x31, 0x39, 10000), + REGULATOR_LINEAR_RANGE(3100000, 0x3a, 0x40, 0), + REGULATOR_LINEAR_RANGE(3110000, 0x41, 0x49, 10000), + REGULATOR_LINEAR_RANGE(3200000, 0x4a, 0x4f, 0), + REGULATOR_LINEAR_RANGE(3300000, 0x50, 0x59, 10000), + REGULATOR_LINEAR_RANGE(3400000, 0x5a, 0x60, 0), + REGULATOR_LINEAR_RANGE(3410000, 0x61, 0x69, 10000), + REGULATOR_LINEAR_RANGE(3500000, 0x6a, 0x70, 0), + REGULATOR_LINEAR_RANGE(3510000, 0x71, 0x79, 10000), + REGULATOR_LINEAR_RANGE(3600000, 0x7a, 0x7f, 0), +}; + +static int mt6360_regulator_set_mode(struct regulator_dev *rdev, + unsigned int mode) +{ + const struct mt6360_regulator_desc *rdesc = (struct mt6360_regulator_desc *)rdev->desc; + struct regmap *regmap = rdev_get_regmap(rdev); + int shift = ffs(rdesc->mode_mask) - 1; + unsigned int val; + int ret; + + switch (mode) { + case REGULATOR_MODE_NORMAL: + val = MT6360_OPMODE_NORMAL; + break; + case REGULATOR_MODE_STANDBY: + val = MT6360_OPMODE_ULP; + break; + case REGULATOR_MODE_IDLE: + val = MT6360_OPMODE_LP; + break; + default: + return -EINVAL; + } + + ret = regmap_update_bits(regmap, rdesc->mode_reg, rdesc->mode_mask, val << shift); + if (ret) { + dev_err(&rdev->dev, "%s: fail (%d)\n", __func__, ret); + return ret; + } + + return 0; +} + +static unsigned int mt6360_regulator_get_mode(struct regulator_dev *rdev) +{ + const struct mt6360_regulator_desc *rdesc = (struct mt6360_regulator_desc *)rdev->desc; + struct regmap *regmap = rdev_get_regmap(rdev); + int shift = ffs(rdesc->mode_mask) - 1; + unsigned int val; + int ret; + + ret = regmap_read(regmap, rdesc->mode_reg, &val); + if (ret) + return ret; + + val &= rdesc->mode_mask; + val >>= shift; + + switch (val) { + case MT6360_OPMODE_LP: + return REGULATOR_MODE_IDLE; + case MT6360_OPMODE_ULP: + return REGULATOR_MODE_STANDBY; + case MT6360_OPMODE_NORMAL: + return REGULATOR_MODE_NORMAL; + default: + return -EINVAL; + } +} + +static int mt6360_regulator_get_status(struct regulator_dev *rdev) +{ + const struct mt6360_regulator_desc *rdesc = (struct mt6360_regulator_desc *)rdev->desc; + struct regmap *regmap = rdev_get_regmap(rdev); + unsigned int val; + int ret; + + ret = regmap_read(regmap, rdesc->state_reg, &val); + if (ret) + return ret; + + if (val & rdesc->state_mask) + return REGULATOR_STATUS_ON; + + return REGULATOR_STATUS_OFF; +} + +static const struct regulator_ops mt6360_regulator_ops = { + .list_voltage = regulator_list_voltage_linear_range, + .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, + .set_mode = mt6360_regulator_set_mode, + .get_mode = mt6360_regulator_get_mode, + .get_status = mt6360_regulator_get_status, +}; + +static unsigned int mt6360_regulator_of_map_mode(unsigned int hw_mode) +{ + switch (hw_mode) { + case MT6360_OPMODE_NORMAL: + return REGULATOR_MODE_NORMAL; + case MT6360_OPMODE_LP: + return REGULATOR_MODE_IDLE; + case MT6360_OPMODE_ULP: + return REGULATOR_MODE_STANDBY; + default: + return REGULATOR_MODE_INVALID; + } +} + +#define MT6360_REGULATOR_DESC(_name, _sname, ereg, emask, vreg, vmask, \ + mreg, mmask, streg, stmask, vranges, \ + vcnts, offon_delay, irq_tbls) \ +{ \ + .desc = { \ + .name = #_name, \ + .supply_name = #_sname, \ + .id = MT6360_REGULATOR_##_name, \ + .of_match = of_match_ptr(#_name), \ + .regulators_node = of_match_ptr("regulator"), \ + .of_map_mode = mt6360_regulator_of_map_mode, \ + .owner = THIS_MODULE, \ + .ops = &mt6360_regulator_ops, \ + .type = REGULATOR_VOLTAGE, \ + .vsel_reg = vreg, \ + .vsel_mask = vmask, \ + .enable_reg = ereg, \ + .enable_mask = emask, \ + .linear_ranges = vranges, \ + .n_linear_ranges = ARRAY_SIZE(vranges), \ + .n_voltages = vcnts, \ + .off_on_delay = offon_delay, \ + }, \ + .mode_reg = mreg, \ + .mode_mask = mmask, \ + .state_reg = streg, \ + .state_mask = stmask, \ + .irq_tables = irq_tbls, \ + .irq_table_size = ARRAY_SIZE(irq_tbls), \ +} + +static const struct mt6360_regulator_desc mt6360_regulator_descs[] = { + MT6360_REGULATOR_DESC(BUCK1, BUCK1_VIN, 0x117, 0x40, 0x110, 0xff, 0x117, 0x30, 0x117, 0x04, + buck_vout_ranges, 256, 0, buck1_irq_tbls), + MT6360_REGULATOR_DESC(BUCK2, BUCK2_VIN, 0x127, 0x40, 0x120, 0xff, 0x127, 0x30, 0x127, 0x04, + buck_vout_ranges, 256, 0, buck2_irq_tbls), + MT6360_REGULATOR_DESC(LDO6, LDO_VIN3, 0x137, 0x40, 0x13B, 0xff, 0x137, 0x30, 0x137, 0x04, + ldo_vout_ranges1, 256, 0, ldo6_irq_tbls), + MT6360_REGULATOR_DESC(LDO7, LDO_VIN3, 0x131, 0x40, 0x135, 0xff, 0x131, 0x30, 0x131, 0x04, + ldo_vout_ranges1, 256, 0, ldo7_irq_tbls), + MT6360_REGULATOR_DESC(LDO1, LDO_VIN1, 0x217, 0x40, 0x21B, 0xff, 0x217, 0x30, 0x217, 0x04, + ldo_vout_ranges2, 256, 0, ldo1_irq_tbls), + MT6360_REGULATOR_DESC(LDO2, LDO_VIN1, 0x211, 0x40, 0x215, 0xff, 0x211, 0x30, 0x211, 0x04, + ldo_vout_ranges2, 256, 0, ldo2_irq_tbls), + MT6360_REGULATOR_DESC(LDO3, LDO_VIN1, 0x205, 0x40, 0x209, 0xff, 0x205, 0x30, 0x205, 0x04, + ldo_vout_ranges2, 256, 100, ldo3_irq_tbls), + MT6360_REGULATOR_DESC(LDO5, LDO_VIN2, 0x20B, 0x40, 0x20F, 0x7f, 0x20B, 0x30, 0x20B, 0x04, + ldo_vout_ranges3, 128, 100, ldo5_irq_tbls), +}; + +static int mt6360_regulator_irq_register(struct platform_device *pdev, + struct regulator_dev *rdev, + const struct mt6360_irq_mapping *tbls, + int tbl_size) +{ + int i, irq, ret; + + for (i = 0; i < tbl_size; i++) { + const struct mt6360_irq_mapping *irq_desc = tbls + i; + + irq = platform_get_irq_byname(pdev, irq_desc->name); + if (irq < 0) { + dev_err(&pdev->dev, "Fail to get %s irq\n", irq_desc->name); + return irq; + } + + ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, irq_desc->handler, 0, + irq_desc->name, rdev); + if (ret) { + dev_err(&pdev->dev, "Fail to request %s irq\n", irq_desc->name); + return ret; + } + } + + return 0; +} + +static int mt6360_regulator_probe(struct platform_device *pdev) +{ + struct mt6360_regulator_data *mrd; + struct regulator_config config = {}; + int i, ret; + + mrd = devm_kzalloc(&pdev->dev, sizeof(*mrd), GFP_KERNEL); + if (!mrd) + return -ENOMEM; + + mrd->dev = &pdev->dev; + + mrd->regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!mrd->regmap) { + dev_err(&pdev->dev, "Failed to get parent regmap\n"); + return -ENODEV; + } + + config.dev = pdev->dev.parent; + config.driver_data = mrd; + config.regmap = mrd->regmap; + + for (i = 0; i < ARRAY_SIZE(mt6360_regulator_descs); i++) { + const struct mt6360_regulator_desc *rdesc = mt6360_regulator_descs + i; + struct regulator_dev *rdev; + + rdev = devm_regulator_register(&pdev->dev, &rdesc->desc, &config); + if (IS_ERR(rdev)) { + dev_err(&pdev->dev, "Failed to register %d regulator\n", i); + return PTR_ERR(rdev); + } + + ret = mt6360_regulator_irq_register(pdev, rdev, rdesc->irq_tables, + rdesc->irq_table_size); + if (ret) { + dev_err(&pdev->dev, "Failed to register %d regulator irqs\n", i); + return ret; + } + } + + return 0; +} + +static const struct platform_device_id mt6360_regulator_id_table[] = { + { "mt6360-regulator", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(platform, mt6360_regulator_id_table); + +static struct platform_driver mt6360_regulator_driver = { + .driver = { + .name = "mt6360-regulator", + }, + .probe = mt6360_regulator_probe, + .id_table = mt6360_regulator_id_table, +}; +module_platform_driver(mt6360_regulator_driver); + +MODULE_AUTHOR("Gene Chen <gene_chen@richtek.com>"); +MODULE_DESCRIPTION("MT6360 Regulator Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/regulator/of_regulator.c b/drivers/regulator/of_regulator.c index 06c0b15fe4c0..564f928eb1db 100644 --- a/drivers/regulator/of_regulator.c +++ b/drivers/regulator/of_regulator.c @@ -413,8 +413,12 @@ device_node *regulator_of_get_init_node(struct device *dev, for_each_available_child_of_node(search, child) { name = of_get_property(child, "regulator-compatible", NULL); - if (!name) - name = child->name; + if (!name) { + if (!desc->of_match_full_name) + name = child->name; + else + name = child->full_name; + } if (!strcmp(desc->of_match, name)) { of_node_put(search); diff --git a/drivers/regulator/pca9450-regulator.c b/drivers/regulator/pca9450-regulator.c index eb5822bf53e0..cb29421d745a 100644 --- a/drivers/regulator/pca9450-regulator.c +++ b/drivers/regulator/pca9450-regulator.c @@ -90,7 +90,7 @@ static int pca9450_dvs_set_ramp_delay(struct regulator_dev *rdev, BUCK1_RAMP_MASK, ramp_value << 6); } -static struct regulator_ops pca9450_dvs_buck_regulator_ops = { +static const struct regulator_ops pca9450_dvs_buck_regulator_ops = { .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, .is_enabled = regulator_is_enabled_regmap, @@ -101,7 +101,7 @@ static struct regulator_ops pca9450_dvs_buck_regulator_ops = { .set_ramp_delay = pca9450_dvs_set_ramp_delay, }; -static struct regulator_ops pca9450_buck_regulator_ops = { +static const struct regulator_ops pca9450_buck_regulator_ops = { .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, .is_enabled = regulator_is_enabled_regmap, @@ -111,7 +111,7 @@ static struct regulator_ops pca9450_buck_regulator_ops = { .set_voltage_time_sel = regulator_set_voltage_time_sel, }; -static struct regulator_ops pca9450_ldo_regulator_ops = { +static const struct regulator_ops pca9450_ldo_regulator_ops = { .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, .is_enabled = regulator_is_enabled_regmap, diff --git a/drivers/regulator/pf8x00-regulator.c b/drivers/regulator/pf8x00-regulator.c new file mode 100644 index 000000000000..af9918cd27aa --- /dev/null +++ b/drivers/regulator/pf8x00-regulator.c @@ -0,0 +1,500 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2017 NXP + * Copyright (C) 2019 Boundary Devices + * Copyright (C) 2020 Amarula Solutions(India) + */ + +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> + +/* registers */ +#define PF8X00_DEVICEID 0x00 +#define PF8X00_REVID 0x01 +#define PF8X00_EMREV 0x02 +#define PF8X00_PROGID 0x03 +#define PF8X00_IMS_INT 0x04 +#define PF8X00_IMS_THERM 0x07 +#define PF8X00_SW_MODE_INT 0x0a +#define PF8X00_SW_MODE_MASK 0x0b +#define PF8X00_IMS_SW_ILIM 0x12 +#define PF8X00_IMS_LDO_ILIM 0x15 +#define PF8X00_IMS_SW_UV 0x18 +#define PF8X00_IMS_SW_OV 0x1b +#define PF8X00_IMS_LDO_UV 0x1e +#define PF8X00_IMS_LDO_OV 0x21 +#define PF8X00_IMS_PWRON 0x24 +#define PF8X00_SYS_INT 0x27 +#define PF8X00_HARD_FAULT 0x29 +#define PF8X00_FSOB_FLAGS 0x2a +#define PF8X00_FSOB_SELECT 0x2b +#define PF8X00_ABIST_OV1 0x2c +#define PF8X00_ABIST_OV2 0x2d +#define PF8X00_ABIST_UV1 0x2e +#define PF8X00_ABIST_UV2 0x2f +#define PF8X00_TEST_FLAGS 0x30 +#define PF8X00_ABIST_RUN 0x31 +#define PF8X00_RANDOM_GEN 0x33 +#define PF8X00_RANDOM_CHK 0x34 +#define PF8X00_VMONEN1 0x35 +#define PF8X00_VMONEN2 0x36 +#define PF8X00_CTRL1 0x37 +#define PF8X00_CTRL2 0x38 +#define PF8X00_CTRL3 0x39 +#define PF8X00_PWRUP_CTRL 0x3a +#define PF8X00_RESETBMCU 0x3c +#define PF8X00_PGOOD 0x3d +#define PF8X00_PWRDN_DLY1 0x3e +#define PF8X00_PWRDN_DLY2 0x3f +#define PF8X00_FREQ_CTRL 0x40 +#define PF8X00_COINCELL_CTRL 0x41 +#define PF8X00_PWRON 0x42 +#define PF8X00_WD_CONFIG 0x43 +#define PF8X00_WD_CLEAR 0x44 +#define PF8X00_WD_EXPIRE 0x45 +#define PF8X00_WD_COUNTER 0x46 +#define PF8X00_FAULT_COUNTER 0x47 +#define PF8X00_FSAFE_COUNTER 0x48 +#define PF8X00_FAULT_TIMER 0x49 +#define PF8X00_AMUX 0x4a +#define PF8X00_SW1_CONFIG1 0x4d +#define PF8X00_LDO1_CONFIG1 0x85 +#define PF8X00_VSNVS_CONFIG1 0x9d +#define PF8X00_PAGE_SELECT 0x9f + +/* regulators */ +enum pf8x00_regulators { + PF8X00_LDO1, + PF8X00_LDO2, + PF8X00_LDO3, + PF8X00_LDO4, + PF8X00_BUCK1, + PF8X00_BUCK2, + PF8X00_BUCK3, + PF8X00_BUCK4, + PF8X00_BUCK5, + PF8X00_BUCK6, + PF8X00_BUCK7, + PF8X00_VSNVS, + + PF8X00_MAX_REGULATORS, +}; + +enum pf8x00_buck_states { + SW_CONFIG1, + SW_CONFIG2, + SW_PWRUP, + SW_MODE1, + SW_RUN_VOLT, + SW_STBY_VOLT, +}; +#define PF8X00_SW_BASE(i) (8 * (i - PF8X00_BUCK1) + PF8X00_SW1_CONFIG1) + +enum pf8x00_ldo_states { + LDO_CONFIG1, + LDO_CONFIG2, + LDO_PWRUP, + LDO_RUN_VOLT, + LDO_STBY_VOLT, +}; +#define PF8X00_LDO_BASE(i) (6 * (i - PF8X00_LDO1) + PF8X00_LDO1_CONFIG1) + +enum swxilim_bits { + SWXILIM_2100_MA, + SWXILIM_2600_MA, + SWXILIM_3000_MA, + SWXILIM_4500_MA, +}; +#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 { + PF8100 = 0x0, + PF8121A = BIT(1), + PF8200 = BIT(3), +}; +#define PF8X00_FAM BIT(6) +#define PF8X00_DEVICE_FAM_MASK GENMASK(7, 4) +#define PF8X00_DEVICE_ID_MASK GENMASK(3, 0) + +struct pf8x00_regulator { + struct regulator_desc desc; + u8 ilim; + u8 phase_shift; +}; + +struct pf8x00_chip { + struct regmap *regmap; + struct device *dev; +}; + +static const struct regmap_config pf8x00_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = PF8X00_PAGE_SELECT, + .cache_type = REGCACHE_RBTREE, +}; + +/* VLDOx output: 1.5V to 5.0V */ +static const int pf8x00_ldo_voltages[] = { + 1500000, 1600000, 1800000, 1850000, 2150000, 2500000, 2800000, 3000000, + 3100000, 3150000, 3200000, 3300000, 3350000, 1650000, 1700000, 5000000, +}; + +#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, +}; + +/* Output: 1.0V to 4.1V */ +static const int pf8x00_sw7_voltages[] = { + 1000000, 1100000, 1200000, 1250000, 1300000, 1350000, 1500000, 1600000, + 1800000, 1850000, 2000000, 2100000, 2150000, 2250000, 2300000, 2400000, + 2500000, 2800000, 3150000, 3200000, 3250000, 3300000, 3350000, 3400000, + 3500000, 3800000, 4000000, 4100000, 4100000, 4100000, 4100000, 4100000, +}; + +/* Output: 1.8V, 3.0V, or 3.3V */ +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(const struct regulator_desc *desc, int ilim) +{ + struct pf8x00_regulator *data = desc_to_regulator(desc); + u8 ilim_sel; + + switch (ilim) { + case 2100: + ilim_sel = SWXILIM_2100_MA; + break; + case 2600: + ilim_sel = SWXILIM_2600_MA; + break; + case 3000: + ilim_sel = SWXILIM_3000_MA; + break; + case 4500: + ilim_sel = SWXILIM_4500_MA; + break; + default: + ilim_sel = SWXILIM_2100_MA; + break; + } + + data->ilim = ilim_sel; +} + +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; + + 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); + + 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; + + return 0; +} + +static const struct regulator_ops pf8x00_ldo_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .list_voltage = regulator_list_voltage_table, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, +}; + +static const struct regulator_ops pf8x00_buck_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .list_voltage = regulator_list_voltage_table, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, +}; + +static const struct regulator_ops pf8x00_vsnvs_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .list_voltage = regulator_list_voltage_table, + .map_voltage = regulator_map_voltage_ascend, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, +}; + +#define PF8X00LDO(_id, _name, base, voltages) \ + [PF8X00_LDO ## _id] = { \ + .desc = { \ + .name = _name, \ + .of_match = _name, \ + .regulators_node = "regulators", \ + .n_voltages = ARRAY_SIZE(voltages), \ + .ops = &pf8x00_ldo_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = PF8X00_LDO ## _id, \ + .owner = THIS_MODULE, \ + .volt_table = voltages, \ + .vsel_reg = (base) + LDO_RUN_VOLT, \ + .vsel_mask = 0xff, \ + .enable_reg = (base) + LDO_CONFIG2, \ + .enable_val = 0x2, \ + .disable_val = 0x0, \ + .enable_mask = 2, \ + }, \ + } + +#define PF8X00BUCK(_id, _name, base, voltages) \ + [PF8X00_BUCK ## _id] = { \ + .desc = { \ + .name = _name, \ + .of_match = _name, \ + .regulators_node = "regulators", \ + .of_parse_cb = pf8x00_of_parse_cb, \ + .n_voltages = ARRAY_SIZE(voltages), \ + .ops = &pf8x00_buck_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = PF8X00_BUCK ## _id, \ + .owner = THIS_MODULE, \ + .volt_table = voltages, \ + .vsel_reg = (base) + SW_RUN_VOLT, \ + .vsel_mask = 0xff, \ + .enable_reg = (base) + SW_MODE1, \ + .enable_val = 0x3, \ + .disable_val = 0x0, \ + .enable_mask = 0x3, \ + .enable_time = 500, \ + }, \ + } + +#define PF8X00VSNVS(_name, base, voltages) \ + [PF8X00_VSNVS] = { \ + .desc = { \ + .name = _name, \ + .of_match = _name, \ + .regulators_node = "regulators", \ + .n_voltages = ARRAY_SIZE(voltages), \ + .ops = &pf8x00_vsnvs_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = PF8X00_VSNVS, \ + .owner = THIS_MODULE, \ + .volt_table = voltages, \ + .vsel_reg = (base), \ + .vsel_mask = 0x3, \ + }, \ + } + +static struct pf8x00_regulator pf8x00_regulators_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), + PF8X00LDO(4, "ldo4", PF8X00_LDO_BASE(PF8X00_LDO4), pf8x00_ldo_voltages), + PF8X00BUCK(1, "buck1", PF8X00_SW_BASE(PF8X00_BUCK1), pf8x00_sw1_to_6_voltages), + PF8X00BUCK(2, "buck2", PF8X00_SW_BASE(PF8X00_BUCK2), pf8x00_sw1_to_6_voltages), + PF8X00BUCK(3, "buck3", PF8X00_SW_BASE(PF8X00_BUCK3), pf8x00_sw1_to_6_voltages), + 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), + PF8X00VSNVS("vsnvs", PF8X00_VSNVS_CONFIG1, pf8x00_vsnvs_voltages), +}; + +static int pf8x00_identify(struct pf8x00_chip *chip) +{ + unsigned int value; + u8 dev_fam, dev_id; + const char *name = NULL; + int ret; + + ret = regmap_read(chip->regmap, PF8X00_DEVICEID, &value); + if (ret) { + dev_err(chip->dev, "failed to read chip family\n"); + return ret; + } + + dev_fam = value & PF8X00_DEVICE_FAM_MASK; + switch (dev_fam) { + case PF8X00_FAM: + break; + default: + dev_err(chip->dev, + "Chip 0x%x is not from PF8X00 family\n", dev_fam); + return ret; + } + + dev_id = value & PF8X00_DEVICE_ID_MASK; + switch (dev_id) { + case PF8100: + name = "PF8100"; + break; + case PF8121A: + name = "PF8121A"; + break; + case PF8200: + name = "PF8100"; + break; + default: + dev_err(chip->dev, "Unknown pf8x00 device id 0x%x\n", dev_id); + return -ENODEV; + } + + dev_info(chip->dev, "%s PMIC found.\n", name); + + return 0; +} + +static int pf8x00_i2c_probe(struct i2c_client *client) +{ + struct regulator_config config = { NULL, }; + struct pf8x00_chip *chip; + int id; + int ret; + + chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + i2c_set_clientdata(client, chip); + chip->dev = &client->dev; + + chip->regmap = devm_regmap_init_i2c(client, &pf8x00_regmap_config); + if (IS_ERR(chip->regmap)) { + ret = PTR_ERR(chip->regmap); + dev_err(&client->dev, + "regmap allocation failed with err %d\n", ret); + return ret; + } + + ret = pf8x00_identify(chip); + if (ret) + return ret; + + for (id = 0; id < ARRAY_SIZE(pf8x00_regulators_data); id++) { + struct pf8x00_regulator *data = &pf8x00_regulators_data[id]; + struct regulator_dev *rdev; + + config.dev = chip->dev; + config.driver_data = chip; + config.regmap = chip->regmap; + + rdev = devm_regulator_register(&client->dev, &data->desc, &config); + if (IS_ERR(rdev)) { + dev_err(&client->dev, + "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); + + regmap_update_bits(chip->regmap, reg, + PF8X00_SWXILIM_MASK, + data->ilim << PF8X00_SWXILIM_SHIFT); + } + } + + return 0; +} + +static const struct of_device_id pf8x00_dt_ids[] = { + { .compatible = "nxp,pf8100",}, + { .compatible = "nxp,pf8121a",}, + { .compatible = "nxp,pf8200",}, + { } +}; +MODULE_DEVICE_TABLE(of, pf8x00_dt_ids); + +static const struct i2c_device_id pf8x00_i2c_id[] = { + { "pf8100", 0 }, + { "pf8121a", 0 }, + { "pf8200", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(i2c, pf8x00_i2c_id); + +static struct i2c_driver pf8x00_regulator_driver = { + .id_table = pf8x00_i2c_id, + .driver = { + .name = "pf8x00", + .of_match_table = pf8x00_dt_ids, + }, + .probe_new = pf8x00_i2c_probe, +}; +module_i2c_driver(pf8x00_regulator_driver); + +MODULE_AUTHOR("Jagan Teki <jagan@amarulasolutions.com>"); +MODULE_AUTHOR("Troy Kisky <troy.kisky@boundarydevices.com>"); +MODULE_DESCRIPTION("Regulator Driver for NXP's PF8100/PF8121A/PF8200 PMIC"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/regulator/pfuze100-regulator.c b/drivers/regulator/pfuze100-regulator.c index 7e8ba9246167..d60d7d1b7fa2 100644 --- a/drivers/regulator/pfuze100-regulator.c +++ b/drivers/regulator/pfuze100-regulator.c @@ -105,15 +105,6 @@ static const int pfuze3000_sw2hi[] = { 2500000, 2800000, 2850000, 3000000, 3100000, 3150000, 3200000, 3300000, }; -static const struct i2c_device_id pfuze_device_id[] = { - {.name = "pfuze100", .driver_data = PFUZE100}, - {.name = "pfuze200", .driver_data = PFUZE200}, - {.name = "pfuze3000", .driver_data = PFUZE3000}, - {.name = "pfuze3001", .driver_data = PFUZE3001}, - { } -}; -MODULE_DEVICE_TABLE(i2c, pfuze_device_id); - static const struct of_device_id pfuze_dt_ids[] = { { .compatible = "fsl,pfuze100", .data = (void *)PFUZE100}, { .compatible = "fsl,pfuze200", .data = (void *)PFUZE200}, @@ -440,7 +431,6 @@ static struct pfuze_regulator pfuze3001_regulators[] = { PFUZE100_VGEN_REG(PFUZE3001, VLDO4, PFUZE100_VGEN6VOL, 1800000, 3300000, 100000), }; -#ifdef CONFIG_OF /* PFUZE100 */ static struct of_regulator_match pfuze100_matches[] = { { .name = "sw1ab", }, @@ -578,22 +568,6 @@ static inline struct device_node *match_of_node(int index) { return pfuze_matches[index].of_node; } -#else -static int pfuze_parse_regulators_dt(struct pfuze_chip *chip) -{ - return 0; -} - -static inline struct regulator_init_data *match_init_data(int index) -{ - return NULL; -} - -static inline struct device_node *match_of_node(int index) -{ - return NULL; -} -#endif static struct pfuze_chip *syspm_pfuze_chip; @@ -708,8 +682,6 @@ static int pfuze100_regulator_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct pfuze_chip *pfuze_chip; - struct pfuze_regulator_platform_data *pdata = - dev_get_platdata(&client->dev); struct regulator_config config = { }; int i, ret; const struct of_device_id *match; @@ -802,10 +774,7 @@ static int pfuze100_regulator_probe(struct i2c_client *client, desc = &pfuze_chip->regulator_descs[i].desc; - if (pdata) - init_data = pdata->init_data[i]; - else - init_data = match_init_data(i); + init_data = match_init_data(i); /* SW2~SW4 high bit check and modify the voltage value table */ if (i >= sw_check_start && i <= sw_check_end) { @@ -836,11 +805,14 @@ static int pfuze100_regulator_probe(struct i2c_client *client, * the switched regulator till yet. */ if (pfuze_chip->flags & PFUZE_FLAG_DISABLE_SW) { - if (pfuze_chip->regulator_descs[i].sw_reg) { - desc->ops = &pfuze100_sw_disable_regulator_ops; - desc->enable_val = 0x8; - desc->disable_val = 0x0; - desc->enable_time = 500; + if (pfuze_chip->chip_id == PFUZE100 || + pfuze_chip->chip_id == PFUZE200) { + if (pfuze_chip->regulator_descs[i].sw_reg) { + desc->ops = &pfuze100_sw_disable_regulator_ops; + desc->enable_val = 0x8; + desc->disable_val = 0x0; + desc->enable_time = 500; + } } } @@ -876,7 +848,6 @@ static int pfuze100_regulator_remove(struct i2c_client *client) } static struct i2c_driver pfuze_driver = { - .id_table = pfuze_device_id, .driver = { .name = "pfuze100-regulator", .of_match_table = pfuze_dt_ids, diff --git a/drivers/regulator/pv88060-regulator.c b/drivers/regulator/pv88060-regulator.c index 787ced918372..48238846f45c 100644 --- a/drivers/regulator/pv88060-regulator.c +++ b/drivers/regulator/pv88060-regulator.c @@ -233,13 +233,10 @@ static irqreturn_t pv88060_irq_handler(int irq, void *data) if (reg_val & PV88060_E_VDD_FLT) { for (i = 0; i < PV88060_MAX_REGULATORS; i++) { - if (chip->rdev[i] != NULL) { - regulator_lock(chip->rdev[i]); + if (chip->rdev[i] != NULL) regulator_notifier_call_chain(chip->rdev[i], REGULATOR_EVENT_UNDER_VOLTAGE, NULL); - regulator_unlock(chip->rdev[i]); - } } err = regmap_write(chip->regmap, PV88060_REG_EVENT_A, @@ -252,13 +249,10 @@ static irqreturn_t pv88060_irq_handler(int irq, void *data) if (reg_val & PV88060_E_OVER_TEMP) { for (i = 0; i < PV88060_MAX_REGULATORS; i++) { - if (chip->rdev[i] != NULL) { - regulator_lock(chip->rdev[i]); + if (chip->rdev[i] != NULL) regulator_notifier_call_chain(chip->rdev[i], REGULATOR_EVENT_OVER_TEMP, NULL); - regulator_unlock(chip->rdev[i]); - } } err = regmap_write(chip->regmap, PV88060_REG_EVENT_A, diff --git a/drivers/regulator/pv88080-regulator.c b/drivers/regulator/pv88080-regulator.c index a444f68af1a8..2a74cc05acfe 100644 --- a/drivers/regulator/pv88080-regulator.c +++ b/drivers/regulator/pv88080-regulator.c @@ -334,13 +334,10 @@ static irqreturn_t pv88080_irq_handler(int irq, void *data) if (reg_val & PV88080_E_VDD_FLT) { for (i = 0; i < PV88080_MAX_REGULATORS; i++) { - if (chip->rdev[i] != NULL) { - regulator_lock(chip->rdev[i]); + if (chip->rdev[i] != NULL) regulator_notifier_call_chain(chip->rdev[i], REGULATOR_EVENT_UNDER_VOLTAGE, NULL); - regulator_unlock(chip->rdev[i]); - } } err = regmap_write(chip->regmap, PV88080_REG_EVENT_A, @@ -353,13 +350,10 @@ static irqreturn_t pv88080_irq_handler(int irq, void *data) if (reg_val & PV88080_E_OVER_TEMP) { for (i = 0; i < PV88080_MAX_REGULATORS; i++) { - if (chip->rdev[i] != NULL) { - regulator_lock(chip->rdev[i]); + if (chip->rdev[i] != NULL) regulator_notifier_call_chain(chip->rdev[i], REGULATOR_EVENT_OVER_TEMP, NULL); - regulator_unlock(chip->rdev[i]); - } } err = regmap_write(chip->regmap, PV88080_REG_EVENT_A, diff --git a/drivers/regulator/pv88090-regulator.c b/drivers/regulator/pv88090-regulator.c index 784729ec2182..a80176bdf8ec 100644 --- a/drivers/regulator/pv88090-regulator.c +++ b/drivers/regulator/pv88090-regulator.c @@ -226,13 +226,10 @@ static irqreturn_t pv88090_irq_handler(int irq, void *data) if (reg_val & PV88090_E_VDD_FLT) { for (i = 0; i < PV88090_MAX_REGULATORS; i++) { - if (chip->rdev[i] != NULL) { - regulator_lock(chip->rdev[i]); + if (chip->rdev[i] != NULL) regulator_notifier_call_chain(chip->rdev[i], REGULATOR_EVENT_UNDER_VOLTAGE, NULL); - regulator_unlock(chip->rdev[i]); - } } err = regmap_write(chip->regmap, PV88090_REG_EVENT_A, @@ -245,13 +242,10 @@ static irqreturn_t pv88090_irq_handler(int irq, void *data) if (reg_val & PV88090_E_OVER_TEMP) { for (i = 0; i < PV88090_MAX_REGULATORS; i++) { - if (chip->rdev[i] != NULL) { - regulator_lock(chip->rdev[i]); + if (chip->rdev[i] != NULL) regulator_notifier_call_chain(chip->rdev[i], REGULATOR_EVENT_OVER_TEMP, NULL); - regulator_unlock(chip->rdev[i]); - } } err = regmap_write(chip->regmap, PV88090_REG_EVENT_A, diff --git a/drivers/regulator/pwm-regulator.c b/drivers/regulator/pwm-regulator.c index 990bd50771d8..7629476d94ae 100644 --- a/drivers/regulator/pwm-regulator.c +++ b/drivers/regulator/pwm-regulator.c @@ -390,7 +390,7 @@ static int pwm_regulator_probe(struct platform_device *pdev) return 0; } -static const struct of_device_id pwm_of_match[] = { +static const struct of_device_id __maybe_unused pwm_of_match[] = { { .compatible = "pwm-regulator" }, { }, }; diff --git a/drivers/regulator/qcom-labibb-regulator.c b/drivers/regulator/qcom-labibb-regulator.c index 8c7dd1928380..8ccf572394a2 100644 --- a/drivers/regulator/qcom-labibb-regulator.c +++ b/drivers/regulator/qcom-labibb-regulator.c @@ -44,16 +44,16 @@ struct labibb_regulator_data { const char *name; u8 type; u16 base; - struct regulator_desc *desc; + const struct regulator_desc *desc; }; -static struct regulator_ops qcom_labibb_ops = { +static const struct regulator_ops qcom_labibb_ops = { .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, .is_enabled = regulator_is_enabled_regmap, }; -static struct regulator_desc pmi8998_lab_desc = { +static const struct regulator_desc pmi8998_lab_desc = { .enable_mask = LAB_ENABLE_CTL_MASK, .enable_reg = (PMI8998_LAB_REG_BASE + REG_LABIBB_ENABLE_CTL), .enable_val = LABIBB_CONTROL_ENABLE, @@ -65,7 +65,7 @@ static struct regulator_desc pmi8998_lab_desc = { .ops = &qcom_labibb_ops, }; -static struct regulator_desc pmi8998_ibb_desc = { +static const struct regulator_desc pmi8998_ibb_desc = { .enable_mask = IBB_ENABLE_CTL_MASK, .enable_reg = (PMI8998_IBB_REG_BASE + REG_LABIBB_ENABLE_CTL), .enable_val = LABIBB_CONTROL_ENABLE, diff --git a/drivers/regulator/qcom-rpmh-regulator.c b/drivers/regulator/qcom-rpmh-regulator.c index 08dcc614efa7..c395a8dda6f7 100644 --- a/drivers/regulator/qcom-rpmh-regulator.c +++ b/drivers/regulator/qcom-rpmh-regulator.c @@ -726,7 +726,7 @@ static const struct rpmh_vreg_hw_data pmic5_ftsmps510 = { static const struct rpmh_vreg_hw_data pmic5_hfsmps515 = { .regulator_type = VRM, .ops = &rpmh_regulator_vrm_ops, - .voltage_range = REGULATOR_LINEAR_RANGE(2800000, 0, 4, 1600), + .voltage_range = REGULATOR_LINEAR_RANGE(2800000, 0, 4, 16000), .n_voltages = 5, .pmic_mode_map = pmic_mode_map_pmic5_smps, .of_map_mode = rpmh_regulator_pmic4_smps_of_map_mode, @@ -865,6 +865,60 @@ static const struct rpmh_vreg_init_data pm8150l_vreg_data[] = { {}, }; +static const struct rpmh_vreg_init_data pm8350_vreg_data[] = { + RPMH_VREG("smps1", "smp%s1", &pmic5_ftsmps510, "vdd-s1"), + RPMH_VREG("smps2", "smp%s2", &pmic5_ftsmps510, "vdd-s2"), + RPMH_VREG("smps3", "smp%s3", &pmic5_ftsmps510, "vdd-s3"), + RPMH_VREG("smps4", "smp%s4", &pmic5_ftsmps510, "vdd-s4"), + RPMH_VREG("smps5", "smp%s5", &pmic5_ftsmps510, "vdd-s5"), + RPMH_VREG("smps6", "smp%s6", &pmic5_ftsmps510, "vdd-s6"), + RPMH_VREG("smps7", "smp%s7", &pmic5_ftsmps510, "vdd-s7"), + RPMH_VREG("smps8", "smp%s8", &pmic5_ftsmps510, "vdd-s8"), + RPMH_VREG("smps9", "smp%s9", &pmic5_ftsmps510, "vdd-s9"), + RPMH_VREG("smps10", "smp%s10", &pmic5_hfsmps510, "vdd-s10"), + RPMH_VREG("smps11", "smp%s11", &pmic5_hfsmps510, "vdd-s11"), + RPMH_VREG("smps12", "smp%s12", &pmic5_hfsmps510, "vdd-s12"), + RPMH_VREG("ldo1", "ldo%s1", &pmic5_nldo, "vdd-l1-l4"), + RPMH_VREG("ldo2", "ldo%s2", &pmic5_pldo, "vdd-l2-l7"), + RPMH_VREG("ldo3", "ldo%s3", &pmic5_nldo, "vdd-l3-l5"), + RPMH_VREG("ldo4", "ldo%s4", &pmic5_nldo, "vdd-l1-l4"), + RPMH_VREG("ldo5", "ldo%s5", &pmic5_nldo, "vdd-l3-l5"), + RPMH_VREG("ldo6", "ldo%s6", &pmic5_nldo, "vdd-l6-l9-l10"), + RPMH_VREG("ldo7", "ldo%s7", &pmic5_pldo, "vdd-l2-l7"), + RPMH_VREG("ldo8", "ldo%s8", &pmic5_nldo, "vdd-l8"), + RPMH_VREG("ldo9", "ldo%s9", &pmic5_nldo, "vdd-l6-l9-l10"), + RPMH_VREG("ldo10", "ldo%s10", &pmic5_nldo, "vdd-l6-l9-l10"), + {}, +}; + +static const struct rpmh_vreg_init_data pm8350c_vreg_data[] = { + RPMH_VREG("smps1", "smp%s1", &pmic5_hfsmps510, "vdd-s1"), + RPMH_VREG("smps2", "smp%s2", &pmic5_ftsmps510, "vdd-s2"), + RPMH_VREG("smps3", "smp%s3", &pmic5_ftsmps510, "vdd-s3"), + RPMH_VREG("smps4", "smp%s4", &pmic5_ftsmps510, "vdd-s4"), + RPMH_VREG("smps5", "smp%s5", &pmic5_ftsmps510, "vdd-s5"), + RPMH_VREG("smps6", "smp%s6", &pmic5_ftsmps510, "vdd-s6"), + RPMH_VREG("smps7", "smp%s7", &pmic5_ftsmps510, "vdd-s7"), + RPMH_VREG("smps8", "smp%s8", &pmic5_ftsmps510, "vdd-s8"), + RPMH_VREG("smps9", "smp%s9", &pmic5_ftsmps510, "vdd-s9"), + RPMH_VREG("smps10", "smp%s10", &pmic5_ftsmps510, "vdd-s10"), + RPMH_VREG("ldo1", "ldo%s1", &pmic5_pldo_lv, "vdd-l1-l12"), + RPMH_VREG("ldo2", "ldo%s2", &pmic5_pldo_lv, "vdd-l2-l8"), + RPMH_VREG("ldo3", "ldo%s3", &pmic5_pldo, "vdd-l3-l4-l5-l7-l13"), + RPMH_VREG("ldo4", "ldo%s4", &pmic5_pldo, "vdd-l3-l4-l5-l7-l13"), + RPMH_VREG("ldo5", "ldo%s5", &pmic5_pldo, "vdd-l3-l4-l5-l7-l13"), + RPMH_VREG("ldo6", "ldo%s6", &pmic5_pldo, "vdd-l6-l9-l11"), + RPMH_VREG("ldo7", "ldo%s7", &pmic5_pldo, "vdd-l3-l4-l5-l7-l13"), + RPMH_VREG("ldo8", "ldo%s8", &pmic5_pldo_lv, "vdd-l2-l8"), + RPMH_VREG("ldo9", "ldo%s9", &pmic5_pldo, "vdd-l6-l9-l11"), + RPMH_VREG("ldo10", "ldo%s10", &pmic5_nldo, "vdd-l10"), + RPMH_VREG("ldo11", "ldo%s11", &pmic5_pldo, "vdd-l6-l9-l11"), + RPMH_VREG("ldo12", "ldo%s12", &pmic5_pldo_lv, "vdd-l1-l12"), + RPMH_VREG("ldo13", "ldo%s13", &pmic5_pldo, "vdd-l3-l4-l5-l7-l13"), + RPMH_VREG("bob", "bob%s1", &pmic5_bob, "vdd-bob"), + {}, +}; + static const struct rpmh_vreg_init_data pm8009_vreg_data[] = { RPMH_VREG("smps1", "smp%s1", &pmic5_hfsmps510, "vdd-s1"), RPMH_VREG("smps2", "smp%s2", &pmic5_hfsmps515, "vdd-s2"), @@ -930,6 +984,33 @@ static const struct rpmh_vreg_init_data pm6150l_vreg_data[] = { {}, }; +static const struct rpmh_vreg_init_data pmx55_vreg_data[] = { + RPMH_VREG("smps1", "smp%s1", &pmic5_ftsmps510, "vdd-s1"), + RPMH_VREG("smps2", "smp%s2", &pmic5_hfsmps510, "vdd-s2"), + RPMH_VREG("smps3", "smp%s3", &pmic5_hfsmps510, "vdd-s3"), + RPMH_VREG("smps4", "smp%s4", &pmic5_hfsmps510, "vdd-s4"), + RPMH_VREG("smps5", "smp%s5", &pmic5_hfsmps510, "vdd-s5"), + RPMH_VREG("smps6", "smp%s6", &pmic5_ftsmps510, "vdd-s6"), + RPMH_VREG("smps7", "smp%s7", &pmic5_hfsmps510, "vdd-s7"), + RPMH_VREG("ldo1", "ldo%s1", &pmic5_nldo, "vdd-l1-l2"), + RPMH_VREG("ldo2", "ldo%s2", &pmic5_nldo, "vdd-l1-l2"), + RPMH_VREG("ldo3", "ldo%s3", &pmic5_nldo, "vdd-l3-l9"), + RPMH_VREG("ldo4", "ldo%s4", &pmic5_nldo, "vdd-l4-l12"), + RPMH_VREG("ldo5", "ldo%s5", &pmic5_pldo, "vdd-l5-l6"), + RPMH_VREG("ldo6", "ldo%s6", &pmic5_pldo, "vdd-l5-l6"), + RPMH_VREG("ldo7", "ldo%s7", &pmic5_nldo, "vdd-l7-l8"), + RPMH_VREG("ldo8", "ldo%s8", &pmic5_nldo, "vdd-l7-l8"), + RPMH_VREG("ldo9", "ldo%s9", &pmic5_nldo, "vdd-l3-l9"), + RPMH_VREG("ldo10", "ldo%s10", &pmic5_pldo, "vdd-l10-l11-l13"), + RPMH_VREG("ldo11", "ldo%s11", &pmic5_pldo, "vdd-l10-l11-l13"), + RPMH_VREG("ldo12", "ldo%s12", &pmic5_nldo, "vdd-l4-l12"), + RPMH_VREG("ldo13", "ldo%s13", &pmic5_pldo, "vdd-l10-l11-l13"), + RPMH_VREG("ldo14", "ldo%s14", &pmic5_nldo, "vdd-l14"), + RPMH_VREG("ldo15", "ldo%s15", &pmic5_nldo, "vdd-l15"), + RPMH_VREG("ldo16", "ldo%s16", &pmic5_pldo, "vdd-l16"), + {}, +}; + static int rpmh_regulator_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -967,7 +1048,7 @@ static int rpmh_regulator_probe(struct platform_device *pdev) return 0; } -static const struct of_device_id rpmh_regulator_match_table[] = { +static const struct of_device_id __maybe_unused rpmh_regulator_match_table[] = { { .compatible = "qcom,pm8005-rpmh-regulators", .data = pm8005_vreg_data, @@ -985,6 +1066,14 @@ static const struct of_device_id rpmh_regulator_match_table[] = { .data = pm8150l_vreg_data, }, { + .compatible = "qcom,pm8350-rpmh-regulators", + .data = pm8350_vreg_data, + }, + { + .compatible = "qcom,pm8350c-rpmh-regulators", + .data = pm8350c_vreg_data, + }, + { .compatible = "qcom,pm8998-rpmh-regulators", .data = pm8998_vreg_data, }, @@ -1000,6 +1089,10 @@ static const struct of_device_id rpmh_regulator_match_table[] = { .compatible = "qcom,pm6150l-rpmh-regulators", .data = pm6150l_vreg_data, }, + { + .compatible = "qcom,pmx55-rpmh-regulators", + .data = pmx55_vreg_data, + }, {} }; MODULE_DEVICE_TABLE(of, rpmh_regulator_match_table); diff --git a/drivers/regulator/qcom_smd-regulator.c b/drivers/regulator/qcom_smd-regulator.c index a87b56bc29fa..bb944ee5fe3b 100644 --- a/drivers/regulator/qcom_smd-regulator.c +++ b/drivers/regulator/qcom_smd-regulator.c @@ -403,6 +403,24 @@ static const struct regulator_desc pm8950_pldo = { .ops = &rpm_smps_ldo_ops, }; +static const struct regulator_desc pm8953_lnldo = { + .linear_ranges = (struct linear_range[]) { + REGULATOR_LINEAR_RANGE(1380000, 8, 15, 120000), + REGULATOR_LINEAR_RANGE(690000, 0, 7, 60000), + }, + .n_linear_ranges = 2, + .n_voltages = 16, + .ops = &rpm_smps_ldo_ops, +}; + +static const struct regulator_desc pm8953_ult_nldo = { + .linear_ranges = (struct linear_range[]) { + REGULATOR_LINEAR_RANGE(375000, 0, 93, 12500), + }, + .n_linear_ranges = 1, + .n_voltages = 94, + .ops = &rpm_smps_ldo_ops, +}; static const struct regulator_desc pm8994_hfsmps = { .linear_ranges = (struct linear_range[]) { @@ -541,6 +559,69 @@ static const struct regulator_desc pmi8998_bob = { .ops = &rpm_bob_ops, }; +static const struct regulator_desc pm660_ftsmps = { + .linear_ranges = (struct linear_range[]) { + REGULATOR_LINEAR_RANGE(355000, 0, 199, 5000), + }, + .n_linear_ranges = 1, + .n_voltages = 200, + .ops = &rpm_smps_ldo_ops, +}; + +static const struct regulator_desc pm660_hfsmps = { + .linear_ranges = (struct linear_range[]) { + REGULATOR_LINEAR_RANGE(320000, 0, 216, 8000), + }, + .n_linear_ranges = 1, + .n_voltages = 217, + .ops = &rpm_smps_ldo_ops, +}; + +static const struct regulator_desc pm660_ht_nldo = { + .linear_ranges = (struct linear_range[]) { + REGULATOR_LINEAR_RANGE(312000, 0, 124, 8000), + }, + .n_linear_ranges = 1, + .n_voltages = 125, + .ops = &rpm_smps_ldo_ops, +}; + +static const struct regulator_desc pm660_ht_lvpldo = { + .linear_ranges = (struct linear_range[]) { + REGULATOR_LINEAR_RANGE(1504000, 0, 62, 8000), + }, + .n_linear_ranges = 1, + .n_voltages = 63, + .ops = &rpm_smps_ldo_ops, +}; + +static const struct regulator_desc pm660_nldo660 = { + .linear_ranges = (struct linear_range[]) { + REGULATOR_LINEAR_RANGE(320000, 0, 123, 8000), + }, + .n_linear_ranges = 1, + .n_voltages = 124, + .ops = &rpm_smps_ldo_ops, +}; + +static const struct regulator_desc pm660_pldo660 = { + .linear_ranges = (struct linear_range[]) { + REGULATOR_LINEAR_RANGE(1504000, 0, 255, 8000), + }, + .n_linear_ranges = 1, + .n_voltages = 256, + .ops = &rpm_smps_ldo_ops, +}; + +static const struct regulator_desc pm660l_bob = { + .linear_ranges = (struct linear_range[]) { + REGULATOR_LINEAR_RANGE(1800000, 0, 84, 32000), + }, + .n_linear_ranges = 1, + .n_voltages = 85, + .ops = &rpm_bob_ops, +}; + static const struct regulator_desc pms405_hfsmps3 = { .linear_ranges = (struct linear_range[]) { REGULATOR_LINEAR_RANGE(320000, 0, 215, 8000), @@ -791,6 +872,41 @@ static const struct rpm_regulator_data rpm_pm8950_regulators[] = { {} }; +static const struct rpm_regulator_data rpm_pm8953_regulators[] = { + { "s1", QCOM_SMD_RPM_SMPA, 1, &pm8998_hfsmps, "vdd_s1" }, + { "s2", QCOM_SMD_RPM_SMPA, 2, &pm8998_hfsmps, "vdd_s2" }, + { "s3", QCOM_SMD_RPM_SMPA, 3, &pm8998_hfsmps, "vdd_s3" }, + { "s4", QCOM_SMD_RPM_SMPA, 4, &pm8998_hfsmps, "vdd_s4" }, + { "s5", QCOM_SMD_RPM_SMPA, 5, &pm8950_ftsmps2p5, "vdd_s5" }, + { "s6", QCOM_SMD_RPM_SMPA, 6, &pm8950_ftsmps2p5, "vdd_s6" }, + { "s7", QCOM_SMD_RPM_SMPA, 7, &pm8998_hfsmps, "vdd_s7" }, + + { "l1", QCOM_SMD_RPM_LDOA, 1, &pm8953_ult_nldo, "vdd_l1" }, + { "l2", QCOM_SMD_RPM_LDOA, 2, &pm8953_ult_nldo, "vdd_l2_l3" }, + { "l3", QCOM_SMD_RPM_LDOA, 3, &pm8953_ult_nldo, "vdd_l2_l3" }, + { "l4", QCOM_SMD_RPM_LDOA, 4, &pm8950_ult_pldo, "vdd_l4_l5_l6_l7_l16_l19" }, + { "l5", QCOM_SMD_RPM_LDOA, 5, &pm8950_ult_pldo, "vdd_l4_l5_l6_l7_l16_l19" }, + { "l6", QCOM_SMD_RPM_LDOA, 6, &pm8950_ult_pldo, "vdd_l4_l5_l6_l7_l16_l19" }, + { "l7", QCOM_SMD_RPM_LDOA, 7, &pm8950_ult_pldo, "vdd_l4_l5_l6_l7_l16_l19" }, + { "l8", QCOM_SMD_RPM_LDOA, 8, &pm8950_ult_pldo, "vdd_l8_l11_l12_l13_l14_l15" }, + { "l9", QCOM_SMD_RPM_LDOA, 9, &pm8950_ult_pldo, "vdd_l9_l10_l17_l18_l22" }, + { "l10", QCOM_SMD_RPM_LDOA, 10, &pm8950_ult_pldo, "vdd_l9_l10_l17_l18_l22" }, + { "l11", QCOM_SMD_RPM_LDOA, 11, &pm8950_ult_pldo, "vdd_l8_l11_l12_l13_l14_l15" }, + { "l12", QCOM_SMD_RPM_LDOA, 12, &pm8950_ult_pldo, "vdd_l8_l11_l12_l13_l14_l15" }, + { "l13", QCOM_SMD_RPM_LDOA, 13, &pm8950_ult_pldo, "vdd_l8_l11_l12_l13_l14_l15" }, + { "l14", QCOM_SMD_RPM_LDOA, 14, &pm8950_ult_pldo, "vdd_l8_l11_l12_l13_l14_l15" }, + { "l15", QCOM_SMD_RPM_LDOA, 15, &pm8950_ult_pldo, "vdd_l8_l11_l12_l13_l14_l15" }, + { "l16", QCOM_SMD_RPM_LDOA, 16, &pm8950_ult_pldo, "vdd_l4_l5_l6_l7_l16_l19" }, + { "l17", QCOM_SMD_RPM_LDOA, 17, &pm8950_ult_pldo, "vdd_l9_l10_l17_l18_l22" }, + { "l18", QCOM_SMD_RPM_LDOA, 18, &pm8950_ult_pldo, "vdd_l9_l10_l17_l18_l22" }, + { "l19", QCOM_SMD_RPM_LDOA, 19, &pm8953_ult_nldo, "vdd_l4_l5_l6_l7_l16_l19" }, + { "l20", QCOM_SMD_RPM_LDOA, 20, &pm8953_lnldo, "vdd_l20" }, + { "l21", QCOM_SMD_RPM_LDOA, 21, &pm8953_lnldo, "vdd_l21" }, + { "l22", QCOM_SMD_RPM_LDOA, 22, &pm8950_ult_pldo, "vdd_l9_l10_l17_l18_l22" }, + { "l23", QCOM_SMD_RPM_LDOA, 23, &pm8953_ult_nldo, "vdd_l23" }, + {} +}; + static const struct rpm_regulator_data rpm_pm8994_regulators[] = { { "s1", QCOM_SMD_RPM_SMPA, 1, &pm8994_ftsmps, "vdd_s1" }, { "s2", QCOM_SMD_RPM_SMPA, 2, &pm8994_ftsmps, "vdd_s2" }, @@ -902,6 +1018,54 @@ static const struct rpm_regulator_data rpm_pmi8998_regulators[] = { {} }; +static const struct rpm_regulator_data rpm_pm660_regulators[] = { + { "s1", QCOM_SMD_RPM_SMPA, 1, &pm660_ftsmps, "vdd_s1" }, + { "s2", QCOM_SMD_RPM_SMPA, 2, &pm660_ftsmps, "vdd_s2" }, + { "s3", QCOM_SMD_RPM_SMPA, 3, &pm660_ftsmps, "vdd_s3" }, + { "s4", QCOM_SMD_RPM_SMPA, 4, &pm660_hfsmps, "vdd_s4" }, + { "s5", QCOM_SMD_RPM_SMPA, 5, &pm660_hfsmps, "vdd_s5" }, + { "s6", QCOM_SMD_RPM_SMPA, 6, &pm660_hfsmps, "vdd_s6" }, + { "l1", QCOM_SMD_RPM_LDOA, 1, &pm660_nldo660, "vdd_l1_l6_l7" }, + { "l2", QCOM_SMD_RPM_LDOA, 2, &pm660_ht_nldo, "vdd_l2_l3" }, + { "l3", QCOM_SMD_RPM_LDOA, 3, &pm660_nldo660, "vdd_l2_l3" }, + /* l4 is unaccessible on PM660 */ + { "l5", QCOM_SMD_RPM_LDOA, 5, &pm660_ht_nldo, "vdd_l5" }, + { "l6", QCOM_SMD_RPM_LDOA, 6, &pm660_ht_nldo, "vdd_l1_l6_l7" }, + { "l7", QCOM_SMD_RPM_LDOA, 7, &pm660_ht_nldo, "vdd_l1_l6_l7" }, + { "l8", QCOM_SMD_RPM_LDOA, 8, &pm660_ht_lvpldo, "vdd_l8_l9_l10_l11_l12_l13_l14" }, + { "l9", QCOM_SMD_RPM_LDOA, 9, &pm660_ht_lvpldo, "vdd_l8_l9_l10_l11_l12_l13_l14" }, + { "l10", QCOM_SMD_RPM_LDOA, 10, &pm660_ht_lvpldo, "vdd_l8_l9_l10_l11_l12_l13_l14" }, + { "l11", QCOM_SMD_RPM_LDOA, 11, &pm660_ht_lvpldo, "vdd_l8_l9_l10_l11_l12_l13_l14" }, + { "l12", QCOM_SMD_RPM_LDOA, 12, &pm660_ht_lvpldo, "vdd_l8_l9_l10_l11_l12_l13_l14" }, + { "l13", QCOM_SMD_RPM_LDOA, 13, &pm660_ht_lvpldo, "vdd_l8_l9_l10_l11_l12_l13_l14" }, + { "l14", QCOM_SMD_RPM_LDOA, 14, &pm660_ht_lvpldo, "vdd_l8_l9_l10_l11_l12_l13_l14" }, + { "l15", QCOM_SMD_RPM_LDOA, 15, &pm660_pldo660, "vdd_l15_l16_l17_l18_l19" }, + { "l16", QCOM_SMD_RPM_LDOA, 16, &pm660_pldo660, "vdd_l15_l16_l17_l18_l19" }, + { "l17", QCOM_SMD_RPM_LDOA, 17, &pm660_pldo660, "vdd_l15_l16_l17_l18_l19" }, + { "l18", QCOM_SMD_RPM_LDOA, 18, &pm660_pldo660, "vdd_l15_l16_l17_l18_l19" }, + { "l19", QCOM_SMD_RPM_LDOA, 19, &pm660_pldo660, "vdd_l15_l16_l17_l18_l19" }, + { } +}; + +static const struct rpm_regulator_data rpm_pm660l_regulators[] = { + { "s1", QCOM_SMD_RPM_SMPB, 1, &pm660_ftsmps, "vdd_s1" }, + { "s2", QCOM_SMD_RPM_SMPB, 2, &pm660_ftsmps, "vdd_s2" }, + { "s3", QCOM_SMD_RPM_RWCX, 0, &pm660_ftsmps, "vdd_s3_s4" }, + { "s5", QCOM_SMD_RPM_RWMX, 0, &pm660_ftsmps, "vdd_s5" }, + { "l1", QCOM_SMD_RPM_LDOB, 1, &pm660_nldo660, "vdd_l1_l9_l10" }, + { "l2", QCOM_SMD_RPM_LDOB, 2, &pm660_pldo660, "vdd_l2" }, + { "l3", QCOM_SMD_RPM_LDOB, 3, &pm660_pldo660, "vdd_l3_l5_l7_l8" }, + { "l4", QCOM_SMD_RPM_LDOB, 4, &pm660_pldo660, "vdd_l4_l6" }, + { "l5", QCOM_SMD_RPM_LDOB, 5, &pm660_pldo660, "vdd_l3_l5_l7_l8" }, + { "l6", QCOM_SMD_RPM_LDOB, 6, &pm660_pldo660, "vdd_l4_l6" }, + { "l7", QCOM_SMD_RPM_LDOB, 7, &pm660_pldo660, "vdd_l3_l5_l7_l8" }, + { "l8", QCOM_SMD_RPM_LDOB, 8, &pm660_pldo660, "vdd_l3_l5_l7_l8" }, + { "l9", QCOM_SMD_RPM_RWLC, 0, &pm660_ht_nldo, "vdd_l1_l9_l10" }, + { "l10", QCOM_SMD_RPM_RWLM, 0, &pm660_ht_nldo, "vdd_l1_l9_l10" }, + { "bob", QCOM_SMD_RPM_BOBB, 1, &pm660l_bob, "vdd_bob", }, + { } +}; + static const struct rpm_regulator_data rpm_pms405_regulators[] = { { "s1", QCOM_SMD_RPM_SMPA, 1, &pms405_hfsmps3, "vdd_s1" }, { "s2", QCOM_SMD_RPM_SMPA, 2, &pms405_hfsmps3, "vdd_s2" }, @@ -930,8 +1094,11 @@ static const struct of_device_id rpm_of_match[] = { { .compatible = "qcom,rpm-pm8916-regulators", .data = &rpm_pm8916_regulators }, { .compatible = "qcom,rpm-pm8941-regulators", .data = &rpm_pm8941_regulators }, { .compatible = "qcom,rpm-pm8950-regulators", .data = &rpm_pm8950_regulators }, + { .compatible = "qcom,rpm-pm8953-regulators", .data = &rpm_pm8953_regulators }, { .compatible = "qcom,rpm-pm8994-regulators", .data = &rpm_pm8994_regulators }, { .compatible = "qcom,rpm-pm8998-regulators", .data = &rpm_pm8998_regulators }, + { .compatible = "qcom,rpm-pm660-regulators", .data = &rpm_pm660_regulators }, + { .compatible = "qcom,rpm-pm660l-regulators", .data = &rpm_pm660l_regulators }, { .compatible = "qcom,rpm-pma8084-regulators", .data = &rpm_pma8084_regulators }, { .compatible = "qcom,rpm-pmi8994-regulators", .data = &rpm_pmi8994_regulators }, { .compatible = "qcom,rpm-pmi8998-regulators", .data = &rpm_pmi8998_regulators }, diff --git a/drivers/regulator/qcom_spmi-regulator.c b/drivers/regulator/qcom_spmi-regulator.c index 5ee7c5305d95..e62e1d72d943 100644 --- a/drivers/regulator/qcom_spmi-regulator.c +++ b/drivers/regulator/qcom_spmi-regulator.c @@ -135,6 +135,18 @@ enum spmi_regulator_subtype { SPMI_REGULATOR_SUBTYPE_LV_P600 = 0x2b, SPMI_REGULATOR_SUBTYPE_LV_P1200 = 0x2c, SPMI_REGULATOR_SUBTYPE_LV_P450 = 0x2d, + SPMI_REGULATOR_SUBTYPE_HT_N300_ST = 0x30, + SPMI_REGULATOR_SUBTYPE_HT_N600_ST = 0x31, + SPMI_REGULATOR_SUBTYPE_HT_N1200_ST = 0x32, + SPMI_REGULATOR_SUBTYPE_HT_LVP150 = 0x3b, + SPMI_REGULATOR_SUBTYPE_HT_LVP300 = 0x3c, + SPMI_REGULATOR_SUBTYPE_L660_N300_ST = 0x42, + SPMI_REGULATOR_SUBTYPE_L660_N600_ST = 0x43, + SPMI_REGULATOR_SUBTYPE_L660_P50 = 0x46, + SPMI_REGULATOR_SUBTYPE_L660_P150 = 0x47, + SPMI_REGULATOR_SUBTYPE_L660_P600 = 0x49, + SPMI_REGULATOR_SUBTYPE_L660_LVP150 = 0x4d, + SPMI_REGULATOR_SUBTYPE_L660_LVP600 = 0x4f, SPMI_REGULATOR_SUBTYPE_LV100 = 0x01, SPMI_REGULATOR_SUBTYPE_LV300 = 0x02, SPMI_REGULATOR_SUBTYPE_MV300 = 0x08, @@ -511,6 +523,22 @@ static struct spmi_voltage_range ult_pldo_ranges[] = { SPMI_VOLTAGE_RANGE(0, 1750000, 1750000, 3337500, 3337500, 12500), }; +static struct spmi_voltage_range pldo660_ranges[] = { + SPMI_VOLTAGE_RANGE(0, 1504000, 1504000, 3544000, 3544000, 8000), +}; + +static struct spmi_voltage_range nldo660_ranges[] = { + SPMI_VOLTAGE_RANGE(0, 320000, 320000, 1304000, 1304000, 8000), +}; + +static struct spmi_voltage_range ht_lvpldo_ranges[] = { + SPMI_VOLTAGE_RANGE(0, 1504000, 1504000, 2000000, 2000000, 8000), +}; + +static struct spmi_voltage_range ht_nldo_ranges[] = { + SPMI_VOLTAGE_RANGE(0, 312000, 312000, 1304000, 1304000, 8000), +}; + static struct spmi_voltage_range hfs430_ranges[] = { SPMI_VOLTAGE_RANGE(0, 320000, 320000, 2040000, 2040000, 8000), }; @@ -530,6 +558,10 @@ static DEFINE_SPMI_SET_POINTS(ult_lo_smps); static DEFINE_SPMI_SET_POINTS(ult_ho_smps); static DEFINE_SPMI_SET_POINTS(ult_nldo); static DEFINE_SPMI_SET_POINTS(ult_pldo); +static DEFINE_SPMI_SET_POINTS(pldo660); +static DEFINE_SPMI_SET_POINTS(nldo660); +static DEFINE_SPMI_SET_POINTS(ht_lvpldo); +static DEFINE_SPMI_SET_POINTS(ht_nldo); static DEFINE_SPMI_SET_POINTS(hfs430); static inline int spmi_vreg_read(struct spmi_regulator *vreg, u16 addr, u8 *buf, @@ -1443,6 +1475,30 @@ static const struct spmi_regulator_mapping supported_regulators[] = { SPMI_VREG(LDO, LV_P300, 0, INF, LDO, ldo, pldo, 10000), SPMI_VREG(LDO, LV_P600, 0, INF, LDO, ldo, pldo, 10000), SPMI_VREG(LDO, LV_P1200, 0, INF, LDO, ldo, pldo, 10000), + SPMI_VREG(LDO, HT_N300_ST, 0, INF, FTSMPS426, ftsmps426, + ht_nldo, 30000), + SPMI_VREG(LDO, HT_N600_ST, 0, INF, FTSMPS426, ftsmps426, + ht_nldo, 30000), + SPMI_VREG(LDO, HT_N1200_ST, 0, INF, FTSMPS426, ftsmps426, + ht_nldo, 30000), + SPMI_VREG(LDO, HT_LVP150, 0, INF, FTSMPS426, ftsmps426, + ht_lvpldo, 10000), + SPMI_VREG(LDO, HT_LVP300, 0, INF, FTSMPS426, ftsmps426, + ht_lvpldo, 10000), + SPMI_VREG(LDO, L660_N300_ST, 0, INF, FTSMPS426, ftsmps426, + nldo660, 10000), + SPMI_VREG(LDO, L660_N600_ST, 0, INF, FTSMPS426, ftsmps426, + nldo660, 10000), + SPMI_VREG(LDO, L660_P50, 0, INF, FTSMPS426, ftsmps426, + pldo660, 10000), + SPMI_VREG(LDO, L660_P150, 0, INF, FTSMPS426, ftsmps426, + pldo660, 10000), + SPMI_VREG(LDO, L660_P600, 0, INF, FTSMPS426, ftsmps426, + pldo660, 10000), + SPMI_VREG(LDO, L660_LVP150, 0, INF, FTSMPS426, ftsmps426, + ht_lvpldo, 10000), + SPMI_VREG(LDO, L660_LVP600, 0, INF, FTSMPS426, ftsmps426, + ht_lvpldo, 10000), SPMI_VREG_VS(LV100, 0, INF), SPMI_VREG_VS(LV300, 0, INF), SPMI_VREG_VS(MV300, 0, INF), @@ -1633,45 +1689,43 @@ static int spmi_regulator_init_registers(struct spmi_regulator *vreg, return ret; /* Set up enable pin control. */ - if ((type == SPMI_REGULATOR_LOGICAL_TYPE_SMPS - || type == SPMI_REGULATOR_LOGICAL_TYPE_LDO - || type == SPMI_REGULATOR_LOGICAL_TYPE_VS) - && !(data->pin_ctrl_enable - & SPMI_REGULATOR_PIN_CTRL_ENABLE_HW_DEFAULT)) { - ctrl_reg[SPMI_COMMON_IDX_ENABLE] &= - ~SPMI_COMMON_ENABLE_FOLLOW_ALL_MASK; - ctrl_reg[SPMI_COMMON_IDX_ENABLE] |= - data->pin_ctrl_enable & SPMI_COMMON_ENABLE_FOLLOW_ALL_MASK; + if (!(data->pin_ctrl_enable & SPMI_REGULATOR_PIN_CTRL_ENABLE_HW_DEFAULT)) { + switch (type) { + case SPMI_REGULATOR_LOGICAL_TYPE_SMPS: + case SPMI_REGULATOR_LOGICAL_TYPE_LDO: + case SPMI_REGULATOR_LOGICAL_TYPE_VS: + ctrl_reg[SPMI_COMMON_IDX_ENABLE] &= + ~SPMI_COMMON_ENABLE_FOLLOW_ALL_MASK; + ctrl_reg[SPMI_COMMON_IDX_ENABLE] |= + data->pin_ctrl_enable & SPMI_COMMON_ENABLE_FOLLOW_ALL_MASK; + break; + default: + break; + } } /* Set up mode pin control. */ - if ((type == SPMI_REGULATOR_LOGICAL_TYPE_SMPS - || type == SPMI_REGULATOR_LOGICAL_TYPE_LDO) - && !(data->pin_ctrl_hpm - & SPMI_REGULATOR_PIN_CTRL_HPM_HW_DEFAULT)) { - ctrl_reg[SPMI_COMMON_IDX_MODE] &= - ~SPMI_COMMON_MODE_FOLLOW_ALL_MASK; - ctrl_reg[SPMI_COMMON_IDX_MODE] |= - data->pin_ctrl_hpm & SPMI_COMMON_MODE_FOLLOW_ALL_MASK; - } - - if (type == SPMI_REGULATOR_LOGICAL_TYPE_VS - && !(data->pin_ctrl_hpm & SPMI_REGULATOR_PIN_CTRL_HPM_HW_DEFAULT)) { - ctrl_reg[SPMI_COMMON_IDX_MODE] &= - ~SPMI_COMMON_MODE_FOLLOW_AWAKE_MASK; - ctrl_reg[SPMI_COMMON_IDX_MODE] |= - data->pin_ctrl_hpm & SPMI_COMMON_MODE_FOLLOW_AWAKE_MASK; - } - - if ((type == SPMI_REGULATOR_LOGICAL_TYPE_ULT_LO_SMPS - || type == SPMI_REGULATOR_LOGICAL_TYPE_ULT_HO_SMPS - || type == SPMI_REGULATOR_LOGICAL_TYPE_ULT_LDO) - && !(data->pin_ctrl_hpm - & SPMI_REGULATOR_PIN_CTRL_HPM_HW_DEFAULT)) { - ctrl_reg[SPMI_COMMON_IDX_MODE] &= - ~SPMI_COMMON_MODE_FOLLOW_AWAKE_MASK; - ctrl_reg[SPMI_COMMON_IDX_MODE] |= - data->pin_ctrl_hpm & SPMI_COMMON_MODE_FOLLOW_AWAKE_MASK; + if (!(data->pin_ctrl_hpm & SPMI_REGULATOR_PIN_CTRL_HPM_HW_DEFAULT)) { + switch (type) { + case SPMI_REGULATOR_LOGICAL_TYPE_SMPS: + case SPMI_REGULATOR_LOGICAL_TYPE_LDO: + ctrl_reg[SPMI_COMMON_IDX_MODE] &= + ~SPMI_COMMON_MODE_FOLLOW_ALL_MASK; + ctrl_reg[SPMI_COMMON_IDX_MODE] |= + data->pin_ctrl_hpm & SPMI_COMMON_MODE_FOLLOW_ALL_MASK; + break; + case SPMI_REGULATOR_LOGICAL_TYPE_VS: + case SPMI_REGULATOR_LOGICAL_TYPE_ULT_LO_SMPS: + case SPMI_REGULATOR_LOGICAL_TYPE_ULT_HO_SMPS: + case SPMI_REGULATOR_LOGICAL_TYPE_ULT_LDO: + ctrl_reg[SPMI_COMMON_IDX_MODE] &= + ~SPMI_COMMON_MODE_FOLLOW_AWAKE_MASK; + ctrl_reg[SPMI_COMMON_IDX_MODE] |= + data->pin_ctrl_hpm & SPMI_COMMON_MODE_FOLLOW_AWAKE_MASK; + break; + default: + break; + } } /* Write back any control register values that were modified. */ @@ -1960,6 +2014,55 @@ static const struct spmi_regulator_data pmi8994_regulators[] = { { } }; +static const struct spmi_regulator_data pm660_regulators[] = { + { "s1", 0x1400, "vdd_s1", }, + { "s2", 0x1700, "vdd_s2", }, + { "s3", 0x1a00, "vdd_s3", }, + { "s4", 0x1d00, "vdd_s3", }, + { "s5", 0x2000, "vdd_s5", }, + { "s6", 0x2300, "vdd_s6", }, + { "l1", 0x4000, "vdd_l1_l6_l7", }, + { "l2", 0x4100, "vdd_l2_l3", }, + { "l3", 0x4200, "vdd_l2_l3", }, + /* l4 is unaccessible on PM660 */ + { "l5", 0x4400, "vdd_l5", }, + { "l6", 0x4500, "vdd_l1_l6_l7", }, + { "l7", 0x4600, "vdd_l1_l6_l7", }, + { "l8", 0x4700, "vdd_l8_l9_l10_l11_l12_l13_l14", }, + { "l9", 0x4800, "vdd_l8_l9_l10_l11_l12_l13_l14", }, + { "l10", 0x4900, "vdd_l8_l9_l10_l11_l12_l13_l14", }, + { "l11", 0x4a00, "vdd_l8_l9_l10_l11_l12_l13_l14", }, + { "l12", 0x4b00, "vdd_l8_l9_l10_l11_l12_l13_l14", }, + { "l13", 0x4c00, "vdd_l8_l9_l10_l11_l12_l13_l14", }, + { "l14", 0x4d00, "vdd_l8_l9_l10_l11_l12_l13_l14", }, + { "l15", 0x4e00, "vdd_l15_l16_l17_l18_l19", }, + { "l16", 0x4f00, "vdd_l15_l16_l17_l18_l19", }, + { "l17", 0x5000, "vdd_l15_l16_l17_l18_l19", }, + { "l18", 0x5100, "vdd_l15_l16_l17_l18_l19", }, + { "l19", 0x5200, "vdd_l15_l16_l17_l18_l19", }, + { } +}; + +static const struct spmi_regulator_data pm660l_regulators[] = { + { "s1", 0x1400, "vdd_s1", }, + { "s2", 0x1700, "vdd_s2", }, + { "s3", 0x1a00, "vdd_s3", }, + { "s4", 0x1d00, "vdd_s4", }, + { "s5", 0x2000, "vdd_s5", }, + { "l1", 0x4000, "vdd_l1_l9_l10", }, + { "l2", 0x4100, "vdd_l2", }, + { "l3", 0x4200, "vdd_l3_l5_l7_l8", }, + { "l4", 0x4300, "vdd_l4_l6", }, + { "l5", 0x4400, "vdd_l3_l5_l7_l8", }, + { "l6", 0x4500, "vdd_l4_l6", }, + { "l7", 0x4600, "vdd_l3_l5_l7_l8", }, + { "l8", 0x4700, "vdd_l3_l5_l7_l8", }, + { "l9", 0x4800, "vdd_l1_l9_l10", }, + { "l10", 0x4900, "vdd_l1_l9_l10", }, + { } +}; + + static const struct spmi_regulator_data pm8004_regulators[] = { { "s2", 0x1700, "vdd_s2", }, { "s5", 0x2000, "vdd_s5", }, @@ -1988,6 +2091,8 @@ static const struct of_device_id qcom_spmi_regulator_match[] = { { .compatible = "qcom,pm8950-regulators", .data = &pm8950_regulators }, { .compatible = "qcom,pm8994-regulators", .data = &pm8994_regulators }, { .compatible = "qcom,pmi8994-regulators", .data = &pmi8994_regulators }, + { .compatible = "qcom,pm660-regulators", .data = &pm660_regulators }, + { .compatible = "qcom,pm660l-regulators", .data = &pm660l_regulators }, { .compatible = "qcom,pms405-regulators", .data = &pms405_regulators }, { } }; diff --git a/drivers/regulator/qcom_usb_vbus-regulator.c b/drivers/regulator/qcom_usb_vbus-regulator.c index 8ba947f3585f..457788b50572 100644 --- a/drivers/regulator/qcom_usb_vbus-regulator.c +++ b/drivers/regulator/qcom_usb_vbus-regulator.c @@ -63,6 +63,7 @@ static int qcom_usb_vbus_regulator_probe(struct platform_device *pdev) qcom_usb_vbus_rdesc.enable_mask = OTG_EN; config.dev = dev; config.init_data = init_data; + config.of_node = dev->of_node; config.regmap = regmap; rdev = devm_regulator_register(dev, &qcom_usb_vbus_rdesc, &config); diff --git a/drivers/regulator/rpi-panel-attiny-regulator.c b/drivers/regulator/rpi-panel-attiny-regulator.c new file mode 100644 index 000000000000..ee46bfbf5eee --- /dev/null +++ b/drivers/regulator/rpi-panel-attiny-regulator.c @@ -0,0 +1,214 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020 Marek Vasut <marex@denx.de> + * + * Based on rpi_touchscreen.c by Eric Anholt <eric@anholt.net> + */ + +#include <linux/backlight.h> +#include <linux/err.h> +#include <linux/gpio.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> +#include <linux/regulator/of_regulator.h> +#include <linux/slab.h> + +/* I2C registers of the Atmel microcontroller. */ +#define REG_ID 0x80 +#define REG_PORTA 0x81 +#define REG_PORTA_HF BIT(2) +#define REG_PORTA_VF BIT(3) +#define REG_PORTB 0x82 +#define REG_POWERON 0x85 +#define REG_PWM 0x86 + +static const struct regmap_config attiny_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = REG_PWM, + .cache_type = REGCACHE_NONE, +}; + +static int attiny_lcd_power_enable(struct regulator_dev *rdev) +{ + unsigned int data; + + regmap_write(rdev->regmap, REG_POWERON, 1); + /* Wait for nPWRDWN to go low to indicate poweron is done. */ + regmap_read_poll_timeout(rdev->regmap, REG_PORTB, data, + data & BIT(0), 10, 1000000); + + /* Default to the same orientation as the closed source + * firmware used for the panel. Runtime rotation + * configuration will be supported using VC4's plane + * orientation bits. + */ + regmap_write(rdev->regmap, REG_PORTA, BIT(2)); + + return 0; +} + +static int attiny_lcd_power_disable(struct regulator_dev *rdev) +{ + regmap_write(rdev->regmap, REG_PWM, 0); + regmap_write(rdev->regmap, REG_POWERON, 0); + udelay(1); + return 0; +} + +static int attiny_lcd_power_is_enabled(struct regulator_dev *rdev) +{ + unsigned int data; + int ret; + + ret = regmap_read(rdev->regmap, REG_POWERON, &data); + if (ret < 0) + return ret; + + if (!(data & BIT(0))) + return 0; + + ret = regmap_read(rdev->regmap, REG_PORTB, &data); + if (ret < 0) + return ret; + + return data & BIT(0); +} + +static const struct regulator_init_data attiny_regulator_default = { + .constraints = { + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + }, +}; + +static const struct regulator_ops attiny_regulator_ops = { + .enable = attiny_lcd_power_enable, + .disable = attiny_lcd_power_disable, + .is_enabled = attiny_lcd_power_is_enabled, +}; + +static const struct regulator_desc attiny_regulator = { + .name = "tc358762-power", + .ops = &attiny_regulator_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, +}; + +static int attiny_update_status(struct backlight_device *bl) +{ + struct regmap *regmap = bl_get_data(bl); + int brightness = bl->props.brightness; + + if (bl->props.power != FB_BLANK_UNBLANK || + bl->props.fb_blank != FB_BLANK_UNBLANK) + brightness = 0; + + return regmap_write(regmap, REG_PWM, brightness); +} + +static int attiny_get_brightness(struct backlight_device *bl) +{ + struct regmap *regmap = bl_get_data(bl); + int ret, brightness; + + ret = regmap_read(regmap, REG_PWM, &brightness); + if (ret) + return ret; + + return brightness; +} + +static const struct backlight_ops attiny_bl = { + .update_status = attiny_update_status, + .get_brightness = attiny_get_brightness, +}; + +/* + * I2C driver interface functions + */ +static int attiny_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct backlight_properties props = { }; + struct regulator_config config = { }; + struct backlight_device *bl; + struct regulator_dev *rdev; + struct regmap *regmap; + unsigned int data; + int ret; + + regmap = devm_regmap_init_i2c(i2c, &attiny_regmap_config); + if (IS_ERR(regmap)) { + ret = PTR_ERR(regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + ret = regmap_read(regmap, REG_ID, &data); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to read REG_ID reg: %d\n", ret); + return ret; + } + + switch (data) { + case 0xde: /* ver 1 */ + case 0xc3: /* ver 2 */ + break; + default: + dev_err(&i2c->dev, "Unknown Atmel firmware revision: 0x%02x\n", data); + return -ENODEV; + } + + regmap_write(regmap, REG_POWERON, 0); + mdelay(1); + + config.dev = &i2c->dev; + config.regmap = regmap; + config.of_node = i2c->dev.of_node; + config.init_data = &attiny_regulator_default; + + rdev = devm_regulator_register(&i2c->dev, &attiny_regulator, &config); + if (IS_ERR(rdev)) { + dev_err(&i2c->dev, "Failed to register ATTINY regulator\n"); + return PTR_ERR(rdev); + } + + props.type = BACKLIGHT_RAW; + props.max_brightness = 0xff; + bl = devm_backlight_device_register(&i2c->dev, + "7inch-touchscreen-panel-bl", + &i2c->dev, regmap, &attiny_bl, + &props); + if (IS_ERR(bl)) + return PTR_ERR(bl); + + bl->props.brightness = 0xff; + + return 0; +} + +static const struct of_device_id attiny_dt_ids[] = { + { .compatible = "raspberrypi,7inch-touchscreen-panel-regulator" }, + {}, +}; +MODULE_DEVICE_TABLE(of, attiny_dt_ids); + +static struct i2c_driver attiny_regulator_driver = { + .driver = { + .name = "rpi_touchscreen_attiny", + .of_match_table = of_match_ptr(attiny_dt_ids), + }, + .probe = attiny_i2c_probe, +}; + +module_i2c_driver(attiny_regulator_driver); + +MODULE_AUTHOR("Marek Vasut <marex@denx.de>"); +MODULE_DESCRIPTION("Regulator device driver for Raspberry Pi 7-inch touchscreen"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/regulator/rt4801-regulator.c b/drivers/regulator/rt4801-regulator.c new file mode 100644 index 000000000000..2055a9cb13ba --- /dev/null +++ b/drivers/regulator/rt4801-regulator.c @@ -0,0 +1,223 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/regmap.h> +#include <linux/regulator/driver.h> + +#define RT4801_REG_VOP 0x00 +#define RT4801_REG_VON 0x01 +#define RT4801_REG_APPS 0x03 + +#define VOUT_MASK 0x1F + +#define MIN_UV 4000000 +#define STEP_UV 100000 +#define MAX_UV 6000000 +#define N_VOLTAGES ((MAX_UV - MIN_UV) / STEP_UV + 1) + +#define DSV_OUT_POS 0 +#define DSV_OUT_NEG 1 +#define DSV_OUT_MAX 2 + +#define DSVP_ENABLE BIT(0) +#define DSVN_ENABLE BIT(1) +#define DSVALL_ENABLE (DSVP_ENABLE | DSVN_ENABLE) + +struct rt4801_priv { + struct device *dev; + struct gpio_descs *enable_gpios; + unsigned int enable_flag; + unsigned int volt_sel[DSV_OUT_MAX]; +}; + +static int rt4801_set_voltage_sel(struct regulator_dev *rdev, unsigned int selector) +{ + struct rt4801_priv *priv = rdev_get_drvdata(rdev); + int id = rdev_get_id(rdev), ret; + + if (priv->enable_flag & BIT(id)) { + ret = regulator_set_voltage_sel_regmap(rdev, selector); + if (ret) + return ret; + } + + priv->volt_sel[id] = selector; + return 0; +} + +static int rt4801_get_voltage_sel(struct regulator_dev *rdev) +{ + struct rt4801_priv *priv = rdev_get_drvdata(rdev); + int id = rdev_get_id(rdev); + + if (priv->enable_flag & BIT(id)) + return regulator_get_voltage_sel_regmap(rdev); + + return priv->volt_sel[id]; +} + +static int rt4801_enable(struct regulator_dev *rdev) +{ + struct rt4801_priv *priv = rdev_get_drvdata(rdev); + struct gpio_descs *gpios = priv->enable_gpios; + int id = rdev_get_id(rdev), ret; + + if (gpios->ndescs <= id) { + dev_warn(&rdev->dev, "no dedicated gpio can control\n"); + goto bypass_gpio; + } + + gpiod_set_value(gpios->desc[id], 1); + +bypass_gpio: + ret = regmap_write(rdev->regmap, rdev->desc->vsel_reg, priv->volt_sel[id]); + if (ret) + return ret; + + priv->enable_flag |= BIT(id); + return 0; +} + +static int rt4801_disable(struct regulator_dev *rdev) +{ + struct rt4801_priv *priv = rdev_get_drvdata(rdev); + struct gpio_descs *gpios = priv->enable_gpios; + int id = rdev_get_id(rdev); + + if (gpios->ndescs <= id) { + dev_warn(&rdev->dev, "no dedicated gpio can control\n"); + goto bypass_gpio; + } + + gpiod_set_value(gpios->desc[id], 0); + +bypass_gpio: + priv->enable_flag &= ~BIT(id); + return 0; +} + +static int rt4801_is_enabled(struct regulator_dev *rdev) +{ + struct rt4801_priv *priv = rdev_get_drvdata(rdev); + int id = rdev_get_id(rdev); + + return !!(priv->enable_flag & BIT(id)); +} + +static const struct regulator_ops rt4801_regulator_ops = { + .list_voltage = regulator_list_voltage_linear, + .set_voltage_sel = rt4801_set_voltage_sel, + .get_voltage_sel = rt4801_get_voltage_sel, + .enable = rt4801_enable, + .disable = rt4801_disable, + .is_enabled = rt4801_is_enabled, +}; + +static const struct regulator_desc rt4801_regulator_descs[] = { + { + .name = "DSVP", + .ops = &rt4801_regulator_ops, + .of_match = of_match_ptr("DSVP"), + .type = REGULATOR_VOLTAGE, + .id = DSV_OUT_POS, + .min_uV = MIN_UV, + .uV_step = STEP_UV, + .n_voltages = N_VOLTAGES, + .owner = THIS_MODULE, + .vsel_reg = RT4801_REG_VOP, + .vsel_mask = VOUT_MASK, + }, + { + .name = "DSVN", + .ops = &rt4801_regulator_ops, + .of_match = of_match_ptr("DSVN"), + .type = REGULATOR_VOLTAGE, + .id = DSV_OUT_NEG, + .min_uV = MIN_UV, + .uV_step = STEP_UV, + .n_voltages = N_VOLTAGES, + .owner = THIS_MODULE, + .vsel_reg = RT4801_REG_VON, + .vsel_mask = VOUT_MASK, + }, +}; + +static const struct regmap_config rt4801_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = RT4801_REG_APPS, +}; + +static int rt4801_probe(struct i2c_client *i2c) +{ + struct rt4801_priv *priv; + struct regmap *regmap; + int i; + + priv = devm_kzalloc(&i2c->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->dev = &i2c->dev; + /* bootloader will on, driver only reconfigure enable to all output high */ + priv->enable_flag = DSVALL_ENABLE; + + regmap = devm_regmap_init_i2c(i2c, &rt4801_regmap_config); + if (IS_ERR(regmap)) { + dev_err(&i2c->dev, "Failed to init regmap\n"); + return PTR_ERR(regmap); + } + + priv->enable_gpios = devm_gpiod_get_array_optional(&i2c->dev, "enable", GPIOD_OUT_HIGH); + if (IS_ERR(priv->enable_gpios)) { + dev_err(&i2c->dev, "Failed to get gpios\n"); + return PTR_ERR(priv->enable_gpios); + } + + for (i = 0; i < DSV_OUT_MAX; i++) { + const struct regulator_desc *desc = rt4801_regulator_descs + i; + struct regulator_config config = { .dev = &i2c->dev, .driver_data = priv, + .regmap = regmap, }; + struct regulator_dev *rdev; + unsigned int val; + int ret; + + /* initialize volt_sel variable */ + ret = regmap_read(regmap, desc->vsel_reg, &val); + if (ret) + return ret; + + priv->volt_sel[i] = val & desc->vsel_mask; + + rdev = devm_regulator_register(&i2c->dev, desc, &config); + if (IS_ERR(rdev)) { + dev_err(&i2c->dev, "Failed to register [%d] regulator\n", i); + return PTR_ERR(rdev); + } + } + + return 0; +} + +static const struct of_device_id __maybe_unused rt4801_of_id[] = { + { .compatible = "richtek,rt4801", }, + { }, +}; +MODULE_DEVICE_TABLE(of, rt4801_of_id); + +static struct i2c_driver rt4801_driver = { + .driver = { + .name = "rt4801", + .of_match_table = of_match_ptr(rt4801_of_id), + }, + .probe_new = rt4801_probe, +}; +module_i2c_driver(rt4801_driver); + +MODULE_AUTHOR("ChiYuan Hwang <cy_huang@richtek.com>"); +MODULE_DESCRIPTION("Richtek RT4801 Display Bias Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/regulator/rtmv20-regulator.c b/drivers/regulator/rtmv20-regulator.c new file mode 100644 index 000000000000..852fb2596ffd --- /dev/null +++ b/drivers/regulator/rtmv20-regulator.c @@ -0,0 +1,397 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/property.h> +#include <linux/regmap.h> +#include <linux/regulator/driver.h> + +#define RTMV20_REG_DEVINFO 0x00 +#define RTMV20_REG_PULSEDELAY 0x01 +#define RTMV20_REG_PULSEWIDTH 0x03 +#define RTMV20_REG_LDCTRL1 0x05 +#define RTMV20_REG_ESPULSEWIDTH 0x06 +#define RTMV20_REG_ESLDCTRL1 0x08 +#define RTMV20_REG_LBP 0x0A +#define RTMV20_REG_LDCTRL2 0x0B +#define RTMV20_REG_FSIN1CTRL1 0x0D +#define RTMV20_REG_FSIN1CTRL3 0x0F +#define RTMV20_REG_FSIN2CTRL1 0x10 +#define RTMV20_REG_FSIN2CTRL3 0x12 +#define RTMV20_REG_ENCTRL 0x13 +#define RTMV20_REG_STRBVSYNDLYL 0x29 +#define RTMV20_REG_LDIRQ 0x30 +#define RTMV20_REG_LDSTAT 0x40 +#define RTMV20_REG_LDMASK 0x50 + +#define RTMV20_VID_MASK GENMASK(7, 4) +#define RICHTEK_VID 0x80 +#define RTMV20_LDCURR_MASK GENMASK(7, 0) +#define RTMV20_DELAY_MASK GENMASK(9, 0) +#define RTMV20_WIDTH_MASK GENMASK(13, 0) +#define RTMV20_WIDTH2_MASK GENMASK(7, 0) +#define RTMV20_LBPLVL_MASK GENMASK(3, 0) +#define RTMV20_LBPEN_MASK BIT(7) +#define RTMV20_STROBEPOL_MASK BIT(1) +#define RTMV20_VSYNPOL_MASK BIT(1) +#define RTMV20_FSINEN_MASK BIT(7) +#define RTMV20_ESEN_MASK BIT(6) +#define RTMV20_FSINOUT_MASK BIT(2) +#define LDENABLE_MASK (BIT(3) | BIT(0)) + +#define OTPEVT_MASK BIT(4) +#define SHORTEVT_MASK BIT(3) +#define OPENEVT_MASK BIT(2) +#define LBPEVT_MASK BIT(1) +#define OCPEVT_MASK BIT(0) +#define FAILEVT_MASK (SHORTEVT_MASK | OPENEVT_MASK | LBPEVT_MASK) + +#define RTMV20_LSW_MINUA 0 +#define RTMV20_LSW_MAXUA 6000000 +#define RTMV20_LSW_STEPUA 30000 + +#define RTMV20_LSW_DEFAULTUA 3000000 + +#define RTMV20_I2CRDY_TIMEUS 200 +#define RTMV20_CSRDY_TIMEUS 2000 + +struct rtmv20_priv { + struct device *dev; + struct regmap *regmap; + struct gpio_desc *enable_gpio; + struct regulator_dev *rdev; +}; + +static int rtmv20_lsw_enable(struct regulator_dev *rdev) +{ + struct rtmv20_priv *priv = rdev_get_drvdata(rdev); + int ret; + + gpiod_set_value(priv->enable_gpio, 1); + + /* Wait for I2C can be accessed */ + usleep_range(RTMV20_I2CRDY_TIMEUS, RTMV20_I2CRDY_TIMEUS + 100); + + /* HW re-enable, disable cache only and sync regcache here */ + regcache_cache_only(priv->regmap, false); + ret = regcache_sync(priv->regmap); + if (ret) + return ret; + + return regulator_enable_regmap(rdev); +} + +static int rtmv20_lsw_disable(struct regulator_dev *rdev) +{ + struct rtmv20_priv *priv = rdev_get_drvdata(rdev); + int ret; + + ret = regulator_disable_regmap(rdev); + if (ret) + return ret; + + /* Mark the regcache as dirty and cache only before HW disabled */ + regcache_cache_only(priv->regmap, true); + regcache_mark_dirty(priv->regmap); + + gpiod_set_value(priv->enable_gpio, 0); + + return 0; +} + +static const struct regulator_ops rtmv20_regulator_ops = { + .set_current_limit = regulator_set_current_limit_regmap, + .get_current_limit = regulator_get_current_limit_regmap, + .enable = rtmv20_lsw_enable, + .disable = rtmv20_lsw_disable, + .is_enabled = regulator_is_enabled_regmap, +}; + +static const struct regulator_desc rtmv20_lsw_desc = { + .name = "rtmv20,lsw", + .of_match = of_match_ptr("lsw"), + .type = REGULATOR_CURRENT, + .owner = THIS_MODULE, + .ops = &rtmv20_regulator_ops, + .csel_reg = RTMV20_REG_LDCTRL1, + .csel_mask = RTMV20_LDCURR_MASK, + .enable_reg = RTMV20_REG_ENCTRL, + .enable_mask = LDENABLE_MASK, + .enable_time = RTMV20_CSRDY_TIMEUS, +}; + +static irqreturn_t rtmv20_irq_handler(int irq, void *data) +{ + struct rtmv20_priv *priv = data; + unsigned int val; + int ret; + + ret = regmap_read(priv->regmap, RTMV20_REG_LDIRQ, &val); + if (ret) { + dev_err(priv->dev, "Failed to get irq flags\n"); + return IRQ_NONE; + } + + if (val & OTPEVT_MASK) + regulator_notifier_call_chain(priv->rdev, REGULATOR_EVENT_OVER_TEMP, NULL); + + if (val & OCPEVT_MASK) + regulator_notifier_call_chain(priv->rdev, REGULATOR_EVENT_OVER_CURRENT, NULL); + + if (val & FAILEVT_MASK) + regulator_notifier_call_chain(priv->rdev, REGULATOR_EVENT_FAIL, NULL); + + return IRQ_HANDLED; +} + +static u32 clamp_to_selector(u32 val, u32 min, u32 max, u32 step) +{ + u32 retval = clamp_val(val, min, max); + + return (retval - min) / step; +} + +static int rtmv20_properties_init(struct rtmv20_priv *priv) +{ + const struct { + const char *name; + u32 def; + u32 min; + u32 max; + u32 step; + u32 addr; + u32 mask; + } props[] = { + { "richtek,ld-pulse-delay-us", 0, 0, 100000, 100, RTMV20_REG_PULSEDELAY, + RTMV20_DELAY_MASK }, + { "richtek,ld-pulse-width-us", 1200, 0, 10000, 1, RTMV20_REG_PULSEWIDTH, + RTMV20_WIDTH_MASK }, + { "richtek,fsin1-delay-us", 23000, 0, 100000, 100, RTMV20_REG_FSIN1CTRL1, + RTMV20_DELAY_MASK }, + { "richtek,fsin1-width-us", 160, 40, 10000, 40, RTMV20_REG_FSIN1CTRL3, + RTMV20_WIDTH2_MASK }, + { "richtek,fsin2-delay-us", 23000, 0, 100000, 100, RTMV20_REG_FSIN2CTRL1, + RTMV20_DELAY_MASK }, + { "richtek,fsin2-width-us", 160, 40, 10000, 40, RTMV20_REG_FSIN2CTRL3, + RTMV20_WIDTH2_MASK }, + { "richtek,es-pulse-width-us", 1200, 0, 10000, 1, RTMV20_REG_ESPULSEWIDTH, + RTMV20_WIDTH_MASK }, + { "richtek,es-ld-current-microamp", 3000000, 0, 6000000, 30000, + RTMV20_REG_ESLDCTRL1, RTMV20_LDCURR_MASK }, + { "richtek,lbp-level-microvolt", 2700000, 2400000, 3700000, 100000, RTMV20_REG_LBP, + RTMV20_LBPLVL_MASK }, + { "richtek,lbp-enable", 0, 0, 1, 1, RTMV20_REG_LBP, RTMV20_LBPEN_MASK }, + { "richtek,strobe-polarity-high", 1, 0, 1, 1, RTMV20_REG_LDCTRL2, + RTMV20_STROBEPOL_MASK }, + { "richtek,vsync-polarity-high", 1, 0, 1, 1, RTMV20_REG_LDCTRL2, + RTMV20_VSYNPOL_MASK }, + { "richtek,fsin-enable", 0, 0, 1, 1, RTMV20_REG_ENCTRL, RTMV20_FSINEN_MASK }, + { "richtek,fsin-output", 0, 0, 1, 1, RTMV20_REG_ENCTRL, RTMV20_FSINOUT_MASK }, + { "richtek,es-enable", 0, 0, 1, 1, RTMV20_REG_ENCTRL, RTMV20_ESEN_MASK }, + }; + int i, ret; + + for (i = 0; i < ARRAY_SIZE(props); i++) { + __be16 bval16; + u16 val16; + u32 temp; + int significant_bit = fls(props[i].mask); + int shift = ffs(props[i].mask) - 1; + + if (props[i].max > 1) { + ret = device_property_read_u32(priv->dev, props[i].name, &temp); + if (ret) + temp = props[i].def; + } else + temp = device_property_read_bool(priv->dev, props[i].name); + + temp = clamp_to_selector(temp, props[i].min, props[i].max, props[i].step); + + /* If significant bit is over 8, two byte access, others one */ + if (significant_bit > 8) { + ret = regmap_raw_read(priv->regmap, props[i].addr, &bval16, sizeof(bval16)); + if (ret) + return ret; + + val16 = be16_to_cpu(bval16); + val16 &= ~props[i].mask; + val16 |= (temp << shift); + bval16 = cpu_to_be16(val16); + + ret = regmap_raw_write(priv->regmap, props[i].addr, &bval16, + sizeof(bval16)); + } else { + ret = regmap_update_bits(priv->regmap, props[i].addr, props[i].mask, + temp << shift); + } + + if (ret) + return ret; + } + + return 0; +} + +static int rtmv20_check_chip_exist(struct rtmv20_priv *priv) +{ + unsigned int val; + int ret; + + ret = regmap_read(priv->regmap, RTMV20_REG_DEVINFO, &val); + if (ret) + return ret; + + if ((val & RTMV20_VID_MASK) != RICHTEK_VID) + return -ENODEV; + + return 0; +} + +static bool rtmv20_is_accessible_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case RTMV20_REG_DEVINFO ... RTMV20_REG_STRBVSYNDLYL: + case RTMV20_REG_LDIRQ: + case RTMV20_REG_LDSTAT: + case RTMV20_REG_LDMASK: + return true; + } + return false; +} + +static bool rtmv20_is_volatile_reg(struct device *dev, unsigned int reg) +{ + if (reg == RTMV20_REG_LDIRQ || reg == RTMV20_REG_LDSTAT) + return true; + return false; +} + +static const struct regmap_config rtmv20_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .cache_type = REGCACHE_RBTREE, + .max_register = RTMV20_REG_LDMASK, + + .writeable_reg = rtmv20_is_accessible_reg, + .readable_reg = rtmv20_is_accessible_reg, + .volatile_reg = rtmv20_is_volatile_reg, +}; + +static int rtmv20_probe(struct i2c_client *i2c) +{ + struct rtmv20_priv *priv; + struct regulator_config config = {}; + int ret; + + priv = devm_kzalloc(&i2c->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->dev = &i2c->dev; + + /* Before regmap register, configure HW enable to make I2C accessible */ + priv->enable_gpio = devm_gpiod_get(&i2c->dev, "enable", GPIOD_OUT_HIGH); + if (IS_ERR(priv->enable_gpio)) { + dev_err(&i2c->dev, "Failed to get enable gpio\n"); + return PTR_ERR(priv->enable_gpio); + } + + /* Wait for I2C can be accessed */ + usleep_range(RTMV20_I2CRDY_TIMEUS, RTMV20_I2CRDY_TIMEUS + 100); + + priv->regmap = devm_regmap_init_i2c(i2c, &rtmv20_regmap_config); + if (IS_ERR(priv->regmap)) { + dev_err(&i2c->dev, "Failed to allocate register map\n"); + return PTR_ERR(priv->regmap); + } + + ret = rtmv20_check_chip_exist(priv); + if (ret) { + dev_err(&i2c->dev, "Chip vendor info is not matched\n"); + return ret; + } + + ret = rtmv20_properties_init(priv); + if (ret) { + dev_err(&i2c->dev, "Failed to init properties\n"); + return ret; + } + + /* + * keep in shutdown mode to minimize the current consumption + * and also mark regcache as dirty + */ + regcache_cache_only(priv->regmap, true); + regcache_mark_dirty(priv->regmap); + gpiod_set_value(priv->enable_gpio, 0); + + config.dev = &i2c->dev; + config.regmap = priv->regmap; + config.driver_data = priv; + priv->rdev = devm_regulator_register(&i2c->dev, &rtmv20_lsw_desc, &config); + if (IS_ERR(priv->rdev)) { + dev_err(&i2c->dev, "Failed to register regulator\n"); + return PTR_ERR(priv->rdev); + } + + /* Unmask all events before IRQ registered */ + ret = regmap_write(priv->regmap, RTMV20_REG_LDMASK, 0); + if (ret) + return ret; + + return devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL, rtmv20_irq_handler, + IRQF_ONESHOT, dev_name(&i2c->dev), priv); +} + +static int __maybe_unused rtmv20_suspend(struct device *dev) +{ + struct i2c_client *i2c = to_i2c_client(dev); + + /* + * When system suspend, disable irq to prevent interrupt trigger + * during I2C bus suspend + */ + disable_irq(i2c->irq); + if (device_may_wakeup(dev)) + enable_irq_wake(i2c->irq); + + return 0; +} + +static int __maybe_unused rtmv20_resume(struct device *dev) +{ + struct i2c_client *i2c = to_i2c_client(dev); + + /* Enable irq after I2C bus already resume */ + enable_irq(i2c->irq); + if (device_may_wakeup(dev)) + disable_irq_wake(i2c->irq); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(rtmv20_pm, rtmv20_suspend, rtmv20_resume); + +static const struct of_device_id __maybe_unused rtmv20_of_id[] = { + { .compatible = "richtek,rtmv20", }, + {} +}; +MODULE_DEVICE_TABLE(of, rtmv20_of_id); + +static struct i2c_driver rtmv20_driver = { + .driver = { + .name = "rtmv20", + .of_match_table = of_match_ptr(rtmv20_of_id), + .pm = &rtmv20_pm, + }, + .probe_new = rtmv20_probe, +}; +module_i2c_driver(rtmv20_driver); + +MODULE_AUTHOR("ChiYuan Huang <cy_huang@richtek.com>"); +MODULE_DESCRIPTION("Richtek RTMV20 Regulator Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/regulator/s5m8767.c b/drivers/regulator/s5m8767.c index 4abd3ed31f60..3fa472127e9a 100644 --- a/drivers/regulator/s5m8767.c +++ b/drivers/regulator/s5m8767.c @@ -1000,18 +1000,7 @@ static struct platform_driver s5m8767_pmic_driver = { .probe = s5m8767_pmic_probe, .id_table = s5m8767_pmic_id, }; - -static int __init s5m8767_pmic_init(void) -{ - return platform_driver_register(&s5m8767_pmic_driver); -} -subsys_initcall(s5m8767_pmic_init); - -static void __exit s5m8767_pmic_exit(void) -{ - platform_driver_unregister(&s5m8767_pmic_driver); -} -module_exit(s5m8767_pmic_exit); +module_platform_driver(s5m8767_pmic_driver); /* Module information */ MODULE_AUTHOR("Sangbeom Kim <sbkim73@samsung.com>"); diff --git a/drivers/regulator/scmi-regulator.c b/drivers/regulator/scmi-regulator.c new file mode 100644 index 000000000000..0e8b3caa8146 --- /dev/null +++ b/drivers/regulator/scmi-regulator.c @@ -0,0 +1,417 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// System Control and Management Interface (SCMI) based regulator driver +// +// Copyright (C) 2020 ARM Ltd. +// +// Implements a regulator driver on top of the SCMI Voltage Protocol. +// +// The ARM SCMI Protocol aims in general to hide as much as possible all the +// underlying operational details while providing an abstracted interface for +// its users to operate upon: as a consequence the resulting operational +// capabilities and configurability of this regulator device are much more +// limited than the ones usually available on a standard physical regulator. +// +// The supported SCMI regulator ops are restricted to the bare minimum: +// +// - 'status_ops': enable/disable/is_enabled +// - 'voltage_ops': get_voltage_sel/set_voltage_sel +// list_voltage/map_voltage +// +// Each SCMI regulator instance is associated, through the means of a proper DT +// entry description, to a specific SCMI Voltage Domain. + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/linear_range.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> +#include <linux/regulator/of_regulator.h> +#include <linux/scmi_protocol.h> +#include <linux/slab.h> +#include <linux/types.h> + +struct scmi_regulator { + u32 id; + struct scmi_device *sdev; + struct regulator_dev *rdev; + struct device_node *of_node; + struct regulator_desc desc; + struct regulator_config conf; +}; + +struct scmi_regulator_info { + int num_doms; + struct scmi_regulator **sregv; +}; + +static int scmi_reg_enable(struct regulator_dev *rdev) +{ + struct scmi_regulator *sreg = rdev_get_drvdata(rdev); + const struct scmi_handle *handle = sreg->sdev->handle; + + return handle->voltage_ops->config_set(handle, sreg->id, + SCMI_VOLTAGE_ARCH_STATE_ON); +} + +static int scmi_reg_disable(struct regulator_dev *rdev) +{ + struct scmi_regulator *sreg = rdev_get_drvdata(rdev); + const struct scmi_handle *handle = sreg->sdev->handle; + + return handle->voltage_ops->config_set(handle, sreg->id, + SCMI_VOLTAGE_ARCH_STATE_OFF); +} + +static int scmi_reg_is_enabled(struct regulator_dev *rdev) +{ + int ret; + u32 config; + struct scmi_regulator *sreg = rdev_get_drvdata(rdev); + const struct scmi_handle *handle = sreg->sdev->handle; + + ret = handle->voltage_ops->config_get(handle, sreg->id, + &config); + if (ret) { + dev_err(&sreg->sdev->dev, + "Error %d reading regulator %s status.\n", + ret, sreg->desc.name); + return ret; + } + + return config & SCMI_VOLTAGE_ARCH_STATE_ON; +} + +static int scmi_reg_get_voltage_sel(struct regulator_dev *rdev) +{ + int ret; + s32 volt_uV; + struct scmi_regulator *sreg = rdev_get_drvdata(rdev); + const struct scmi_handle *handle = sreg->sdev->handle; + + ret = handle->voltage_ops->level_get(handle, sreg->id, &volt_uV); + if (ret) + return ret; + + return sreg->desc.ops->map_voltage(rdev, volt_uV, volt_uV); +} + +static int scmi_reg_set_voltage_sel(struct regulator_dev *rdev, + unsigned int selector) +{ + s32 volt_uV; + struct scmi_regulator *sreg = rdev_get_drvdata(rdev); + const struct scmi_handle *handle = sreg->sdev->handle; + + volt_uV = sreg->desc.ops->list_voltage(rdev, selector); + if (volt_uV <= 0) + return -EINVAL; + + return handle->voltage_ops->level_set(handle, sreg->id, 0x0, volt_uV); +} + +static const struct regulator_ops scmi_reg_fixed_ops = { + .enable = scmi_reg_enable, + .disable = scmi_reg_disable, + .is_enabled = scmi_reg_is_enabled, +}; + +static const struct regulator_ops scmi_reg_linear_ops = { + .enable = scmi_reg_enable, + .disable = scmi_reg_disable, + .is_enabled = scmi_reg_is_enabled, + .get_voltage_sel = scmi_reg_get_voltage_sel, + .set_voltage_sel = scmi_reg_set_voltage_sel, + .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, +}; + +static const struct regulator_ops scmi_reg_discrete_ops = { + .enable = scmi_reg_enable, + .disable = scmi_reg_disable, + .is_enabled = scmi_reg_is_enabled, + .get_voltage_sel = scmi_reg_get_voltage_sel, + .set_voltage_sel = scmi_reg_set_voltage_sel, + .list_voltage = regulator_list_voltage_table, + .map_voltage = regulator_map_voltage_iterate, +}; + +static int +scmi_config_linear_regulator_mappings(struct scmi_regulator *sreg, + const struct scmi_voltage_info *vinfo) +{ + s32 delta_uV; + + /* + * Note that SCMI voltage domains describable by linear ranges + * (segments) {low, high, step} are guaranteed to come in one single + * triplet by the SCMI Voltage Domain protocol support itself. + */ + + delta_uV = (vinfo->levels_uv[SCMI_VOLTAGE_SEGMENT_HIGH] - + vinfo->levels_uv[SCMI_VOLTAGE_SEGMENT_LOW]); + + /* Rule out buggy negative-intervals answers from fw */ + if (delta_uV < 0) { + dev_err(&sreg->sdev->dev, + "Invalid volt-range %d-%duV for domain %d\n", + vinfo->levels_uv[SCMI_VOLTAGE_SEGMENT_LOW], + vinfo->levels_uv[SCMI_VOLTAGE_SEGMENT_HIGH], + sreg->id); + return -EINVAL; + } + + if (!delta_uV) { + /* Just one fixed voltage exposed by SCMI */ + sreg->desc.fixed_uV = + vinfo->levels_uv[SCMI_VOLTAGE_SEGMENT_LOW]; + sreg->desc.n_voltages = 1; + sreg->desc.ops = &scmi_reg_fixed_ops; + } else { + /* One simple linear mapping. */ + sreg->desc.min_uV = + vinfo->levels_uv[SCMI_VOLTAGE_SEGMENT_LOW]; + sreg->desc.uV_step = + vinfo->levels_uv[SCMI_VOLTAGE_SEGMENT_STEP]; + sreg->desc.linear_min_sel = 0; + sreg->desc.n_voltages = delta_uV / sreg->desc.uV_step; + sreg->desc.ops = &scmi_reg_linear_ops; + } + + return 0; +} + +static int +scmi_config_discrete_regulator_mappings(struct scmi_regulator *sreg, + const struct scmi_voltage_info *vinfo) +{ + /* Discrete non linear levels are mapped to volt_table */ + sreg->desc.n_voltages = vinfo->num_levels; + + if (sreg->desc.n_voltages > 1) { + sreg->desc.volt_table = (const unsigned int *)vinfo->levels_uv; + sreg->desc.ops = &scmi_reg_discrete_ops; + } else { + sreg->desc.fixed_uV = vinfo->levels_uv[0]; + sreg->desc.ops = &scmi_reg_fixed_ops; + } + + return 0; +} + +static int scmi_regulator_common_init(struct scmi_regulator *sreg) +{ + int ret; + const struct scmi_handle *handle = sreg->sdev->handle; + struct device *dev = &sreg->sdev->dev; + const struct scmi_voltage_info *vinfo; + + vinfo = handle->voltage_ops->info_get(handle, sreg->id); + if (!vinfo) { + dev_warn(dev, "Failure to get voltage domain %d\n", + sreg->id); + return -ENODEV; + } + + /* + * Regulator framework does not fully support negative voltages + * so we discard any voltage domain reported as supporting negative + * voltages: as a consequence each levels_uv entry is guaranteed to + * be non-negative from here on. + */ + if (vinfo->negative_volts_allowed) { + dev_warn(dev, "Negative voltages NOT supported...skip %s\n", + sreg->of_node->full_name); + return -EOPNOTSUPP; + } + + sreg->desc.name = devm_kasprintf(dev, GFP_KERNEL, "%s", vinfo->name); + if (!sreg->desc.name) + return -ENOMEM; + + sreg->desc.id = sreg->id; + sreg->desc.type = REGULATOR_VOLTAGE; + sreg->desc.owner = THIS_MODULE; + sreg->desc.of_match_full_name = true; + sreg->desc.of_match = sreg->of_node->full_name; + sreg->desc.regulators_node = "regulators"; + if (vinfo->segmented) + ret = scmi_config_linear_regulator_mappings(sreg, vinfo); + else + ret = scmi_config_discrete_regulator_mappings(sreg, vinfo); + if (ret) + return ret; + + /* + * Using the scmi device here to have DT searched from Voltage + * protocol node down. + */ + sreg->conf.dev = dev; + + /* Store for later retrieval via rdev_get_drvdata() */ + sreg->conf.driver_data = sreg; + + return 0; +} + +static int process_scmi_regulator_of_node(struct scmi_device *sdev, + struct device_node *np, + struct scmi_regulator_info *rinfo) +{ + u32 dom, ret; + + ret = of_property_read_u32(np, "reg", &dom); + if (ret) + return ret; + + if (dom >= rinfo->num_doms) + return -ENODEV; + + if (rinfo->sregv[dom]) { + dev_err(&sdev->dev, + "SCMI Voltage Domain %d already in use. Skipping: %s\n", + dom, np->full_name); + return -EINVAL; + } + + rinfo->sregv[dom] = devm_kzalloc(&sdev->dev, + sizeof(struct scmi_regulator), + GFP_KERNEL); + if (!rinfo->sregv[dom]) + return -ENOMEM; + + rinfo->sregv[dom]->id = dom; + rinfo->sregv[dom]->sdev = sdev; + + /* get hold of good nodes */ + of_node_get(np); + rinfo->sregv[dom]->of_node = np; + + dev_dbg(&sdev->dev, + "Found SCMI Regulator entry -- OF node [%d] -> %s\n", + dom, np->full_name); + + return 0; +} + +static int scmi_regulator_probe(struct scmi_device *sdev) +{ + int d, ret, num_doms; + struct device_node *np, *child; + const struct scmi_handle *handle = sdev->handle; + struct scmi_regulator_info *rinfo; + + if (!handle || !handle->voltage_ops) + return -ENODEV; + + num_doms = handle->voltage_ops->num_domains_get(handle); + if (num_doms <= 0) { + if (!num_doms) { + dev_err(&sdev->dev, + "number of voltage domains invalid\n"); + num_doms = -EINVAL; + } else { + dev_err(&sdev->dev, + "failed to get voltage domains - err:%d\n", + num_doms); + } + + return num_doms; + } + + rinfo = devm_kzalloc(&sdev->dev, sizeof(*rinfo), GFP_KERNEL); + if (!rinfo) + return -ENOMEM; + + /* Allocate pointers array for all possible domains */ + rinfo->sregv = devm_kcalloc(&sdev->dev, num_doms, + sizeof(void *), GFP_KERNEL); + if (!rinfo->sregv) + return -ENOMEM; + + rinfo->num_doms = num_doms; + + /* + * Start collecting into rinfo->sregv possibly good SCMI Regulators as + * described by a well-formed DT entry and associated with an existing + * plausible SCMI Voltage Domain number, all belonging to this SCMI + * platform instance node (handle->dev->of_node). + */ + np = of_find_node_by_name(handle->dev->of_node, "regulators"); + for_each_child_of_node(np, child) { + ret = process_scmi_regulator_of_node(sdev, child, rinfo); + /* abort on any mem issue */ + if (ret == -ENOMEM) + return ret; + } + + /* + * Register a regulator for each valid regulator-DT-entry that we + * can successfully reach via SCMI and has a valid associated voltage + * domain. + */ + for (d = 0; d < num_doms; d++) { + struct scmi_regulator *sreg = rinfo->sregv[d]; + + /* Skip empty slots */ + if (!sreg) + continue; + + ret = scmi_regulator_common_init(sreg); + /* Skip invalid voltage domains */ + if (ret) + continue; + + sreg->rdev = devm_regulator_register(&sdev->dev, &sreg->desc, + &sreg->conf); + if (IS_ERR(sreg->rdev)) { + sreg->rdev = NULL; + continue; + } + + dev_info(&sdev->dev, + "Regulator %s registered for domain [%d]\n", + sreg->desc.name, sreg->id); + } + + dev_set_drvdata(&sdev->dev, rinfo); + + return 0; +} + +static void scmi_regulator_remove(struct scmi_device *sdev) +{ + int d; + struct scmi_regulator_info *rinfo; + + rinfo = dev_get_drvdata(&sdev->dev); + if (!rinfo) + return; + + for (d = 0; d < rinfo->num_doms; d++) { + if (!rinfo->sregv[d]) + continue; + of_node_put(rinfo->sregv[d]->of_node); + } +} + +static const struct scmi_device_id scmi_regulator_id_table[] = { + { SCMI_PROTOCOL_VOLTAGE, "regulator" }, + { }, +}; +MODULE_DEVICE_TABLE(scmi, scmi_regulator_id_table); + +static struct scmi_driver scmi_drv = { + .name = "scmi-regulator", + .probe = scmi_regulator_probe, + .remove = scmi_regulator_remove, + .id_table = scmi_regulator_id_table, +}; + +module_scmi_driver(scmi_drv); + +MODULE_AUTHOR("Cristian Marussi <cristian.marussi@arm.com>"); +MODULE_DESCRIPTION("ARM SCMI regulator driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/regulator/slg51000-regulator.c b/drivers/regulator/slg51000-regulator.c index 87b020d0b958..75a941fb3c2b 100644 --- a/drivers/regulator/slg51000-regulator.c +++ b/drivers/regulator/slg51000-regulator.c @@ -386,10 +386,8 @@ static irqreturn_t slg51000_irq_handler(int irq, void *data) for (i = 0; i < SLG51000_MAX_REGULATORS; i++) { if (!(evt[i][R2] & SLG51000_IRQ_ILIM_FLAG_MASK) && (evt[i][R0] & SLG51000_EVT_ILIM_FLAG_MASK)) { - regulator_lock(chip->rdev[i]); regulator_notifier_call_chain(chip->rdev[i], REGULATOR_EVENT_OVER_CURRENT, NULL); - regulator_unlock(chip->rdev[i]); if (evt[i][R1] & SLG51000_STA_ILIM_FLAG_MASK) dev_warn(chip->dev, @@ -403,10 +401,8 @@ static irqreturn_t slg51000_irq_handler(int irq, void *data) for (i = 0; i < SLG51000_MAX_REGULATORS; i++) { if (!(evt[i][R1] & SLG51000_STA_ILIM_FLAG_MASK) && (evt[i][R1] & SLG51000_STA_VOUT_OK_FLAG_MASK)) { - regulator_lock(chip->rdev[i]); regulator_notifier_call_chain(chip->rdev[i], REGULATOR_EVENT_OVER_TEMP, NULL); - regulator_unlock(chip->rdev[i]); } } handled = IRQ_HANDLED; diff --git a/drivers/regulator/stm32-booster.c b/drivers/regulator/stm32-booster.c index 03f162ffd144..3136ea8a35d5 100644 --- a/drivers/regulator/stm32-booster.c +++ b/drivers/regulator/stm32-booster.c @@ -101,7 +101,7 @@ static int stm32_booster_probe(struct platform_device *pdev) return 0; } -static const struct of_device_id stm32_booster_of_match[] = { +static const struct of_device_id __maybe_unused stm32_booster_of_match[] = { { .compatible = "st,stm32h7-booster", .data = (void *)&stm32h7_booster_desc diff --git a/drivers/regulator/stm32-pwr.c b/drivers/regulator/stm32-pwr.c index e0e627b0106e..2a42acb7c24e 100644 --- a/drivers/regulator/stm32-pwr.c +++ b/drivers/regulator/stm32-pwr.c @@ -166,7 +166,7 @@ static int stm32_pwr_regulator_probe(struct platform_device *pdev) return ret; } -static const struct of_device_id stm32_pwr_of_match[] = { +static const struct of_device_id __maybe_unused stm32_pwr_of_match[] = { { .compatible = "st,stm32mp1,pwr-reg", }, {}, }; diff --git a/drivers/regulator/stm32-vrefbuf.c b/drivers/regulator/stm32-vrefbuf.c index 992bc18101ef..161622ea7259 100644 --- a/drivers/regulator/stm32-vrefbuf.c +++ b/drivers/regulator/stm32-vrefbuf.c @@ -284,7 +284,7 @@ static const struct dev_pm_ops stm32_vrefbuf_pm_ops = { NULL) }; -static const struct of_device_id stm32_vrefbuf_of_match[] = { +static const struct of_device_id __maybe_unused stm32_vrefbuf_of_match[] = { { .compatible = "st,stm32-vrefbuf", }, {}, }; diff --git a/drivers/regulator/stpmic1_regulator.c b/drivers/regulator/stpmic1_regulator.c index 73e0ab2baeaa..cf10fdb72e32 100644 --- a/drivers/regulator/stpmic1_regulator.c +++ b/drivers/regulator/stpmic1_regulator.c @@ -505,15 +505,11 @@ static irqreturn_t stpmic1_curlim_irq_handler(int irq, void *data) { struct regulator_dev *rdev = (struct regulator_dev *)data; - regulator_lock(rdev); - /* Send an overcurrent notification */ regulator_notifier_call_chain(rdev, REGULATOR_EVENT_OVER_CURRENT, NULL); - regulator_unlock(rdev); - return IRQ_HANDLED; } diff --git a/drivers/regulator/stw481x-vmmc.c b/drivers/regulator/stw481x-vmmc.c index 6dc2316daad3..127ab43add49 100644 --- a/drivers/regulator/stw481x-vmmc.c +++ b/drivers/regulator/stw481x-vmmc.c @@ -27,7 +27,7 @@ static const unsigned int stw481x_vmmc_voltages[] = { 3300000, }; -static struct regulator_ops stw481x_vmmc_ops = { +static const struct regulator_ops stw481x_vmmc_ops = { .list_voltage = regulator_list_voltage_table, .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, @@ -36,7 +36,7 @@ static struct regulator_ops stw481x_vmmc_ops = { .set_voltage_sel = regulator_set_voltage_sel_regmap, }; -static struct regulator_desc vmmc_regulator = { +static const struct regulator_desc vmmc_regulator = { .name = "VMMC", .id = 0, .ops = &stw481x_vmmc_ops, diff --git a/drivers/regulator/sy8106a-regulator.c b/drivers/regulator/sy8106a-regulator.c index 2222e739e62b..c119f85259a5 100644 --- a/drivers/regulator/sy8106a-regulator.c +++ b/drivers/regulator/sy8106a-regulator.c @@ -123,7 +123,7 @@ static int sy8106a_i2c_probe(struct i2c_client *i2c) return 0; } -static const struct of_device_id sy8106a_i2c_of_match[] = { +static const struct of_device_id __maybe_unused sy8106a_i2c_of_match[] = { { .compatible = "silergy,sy8106a" }, { }, }; diff --git a/drivers/regulator/sy8827n.c b/drivers/regulator/sy8827n.c index b207217f74d8..52e8c17afe24 100644 --- a/drivers/regulator/sy8827n.c +++ b/drivers/regulator/sy8827n.c @@ -156,6 +156,7 @@ static int sy8827n_i2c_probe(struct i2c_client *client) return ret; } +#ifdef CONFIG_OF static const struct of_device_id sy8827n_dt_ids[] = { { .compatible = "silergy,sy8827n", @@ -163,6 +164,7 @@ static const struct of_device_id sy8827n_dt_ids[] = { { } }; MODULE_DEVICE_TABLE(of, sy8827n_dt_ids); +#endif static const struct i2c_device_id sy8827n_id[] = { { "sy8827n", }, diff --git a/drivers/regulator/ti-abb-regulator.c b/drivers/regulator/ti-abb-regulator.c index af9abcd9c166..9f0a4d50cead 100644 --- a/drivers/regulator/ti-abb-regulator.c +++ b/drivers/regulator/ti-abb-regulator.c @@ -342,8 +342,17 @@ static int ti_abb_set_voltage_sel(struct regulator_dev *rdev, unsigned sel) return ret; } - /* If data is exactly the same, then just update index, no change */ info = &abb->info[sel]; + /* + * When Linux kernel is starting up, we are'nt sure of the + * Bias configuration that bootloader has configured. + * So, we get to know the actual setting the first time + * we are asked to transition. + */ + if (abb->current_info_idx == -EINVAL) + goto just_set_abb; + + /* If data is exactly the same, then just update index, no change */ oinfo = &abb->info[abb->current_info_idx]; if (!memcmp(info, oinfo, sizeof(*info))) { dev_dbg(dev, "%s: Same data new idx=%d, old idx=%d\n", __func__, @@ -351,6 +360,7 @@ static int ti_abb_set_voltage_sel(struct regulator_dev *rdev, unsigned sel) goto out; } +just_set_abb: ret = ti_abb_set_opp(rdev, abb, info); out: @@ -619,7 +629,7 @@ check_abb: return 0; } -static struct regulator_ops ti_abb_reg_ops = { +static const struct regulator_ops ti_abb_reg_ops = { .list_voltage = regulator_list_voltage_table, .set_voltage_sel = ti_abb_set_voltage_sel, diff --git a/drivers/regulator/tps51632-regulator.c b/drivers/regulator/tps51632-regulator.c index c139890c1514..a15e415e61d5 100644 --- a/drivers/regulator/tps51632-regulator.c +++ b/drivers/regulator/tps51632-regulator.c @@ -108,7 +108,7 @@ static int tps51632_dcdc_set_ramp_delay(struct regulator_dev *rdev, return ret; } -static struct regulator_ops tps51632_dcdc_ops = { +static const struct regulator_ops tps51632_dcdc_ops = { .get_voltage_sel = regulator_get_voltage_sel_regmap, .set_voltage_sel = regulator_set_voltage_sel_regmap, .list_voltage = regulator_list_voltage_linear, diff --git a/drivers/regulator/tps6105x-regulator.c b/drivers/regulator/tps6105x-regulator.c index f8939af0bd2c..a6469fe05635 100644 --- a/drivers/regulator/tps6105x-regulator.c +++ b/drivers/regulator/tps6105x-regulator.c @@ -26,7 +26,7 @@ static const unsigned int tps6105x_voltages[] = { 5000000, /* There is an additional 5V */ }; -static struct regulator_ops tps6105x_regulator_ops = { +static const struct regulator_ops tps6105x_regulator_ops = { .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, .is_enabled = regulator_is_enabled_regmap, diff --git a/drivers/regulator/tps62360-regulator.c b/drivers/regulator/tps62360-regulator.c index f6a6d36a6533..315cd5daf480 100644 --- a/drivers/regulator/tps62360-regulator.c +++ b/drivers/regulator/tps62360-regulator.c @@ -233,7 +233,7 @@ static unsigned int tps62360_get_mode(struct regulator_dev *rdev) REGULATOR_MODE_FAST : REGULATOR_MODE_NORMAL; } -static struct regulator_ops tps62360_dcdc_ops = { +static const struct regulator_ops tps62360_dcdc_ops = { .get_voltage_sel = tps62360_dcdc_get_voltage_sel, .set_voltage_sel = tps62360_dcdc_set_voltage_sel, .list_voltage = regulator_list_voltage_linear, diff --git a/drivers/regulator/tps65023-regulator.c b/drivers/regulator/tps65023-regulator.c index 795d459ff3cf..f25806531c7e 100644 --- a/drivers/regulator/tps65023-regulator.c +++ b/drivers/regulator/tps65023-regulator.c @@ -316,7 +316,7 @@ static int tps_65023_probe(struct i2c_client *client, return 0; } -static const struct of_device_id tps65023_of_match[] = { +static const struct of_device_id __maybe_unused tps65023_of_match[] = { { .compatible = "ti,tps65020", .data = &tps65020_drv_data}, { .compatible = "ti,tps65021", .data = &tps65021_drv_data}, { .compatible = "ti,tps65023", .data = &tps65023_drv_data}, diff --git a/drivers/regulator/tps65086-regulator.c b/drivers/regulator/tps65086-regulator.c index 23528475a962..070c956216b0 100644 --- a/drivers/regulator/tps65086-regulator.c +++ b/drivers/regulator/tps65086-regulator.c @@ -101,7 +101,7 @@ static const struct linear_range tps65086_ldoa23_ranges[] = { }; /* Operations permitted on regulators */ -static struct regulator_ops reg_ops = { +static const struct regulator_ops reg_ops = { .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, .is_enabled = regulator_is_enabled_regmap, @@ -112,7 +112,7 @@ static struct regulator_ops reg_ops = { }; /* Operations permitted on load switches */ -static struct regulator_ops switch_ops = { +static const struct regulator_ops switch_ops = { .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, .is_enabled = regulator_is_enabled_regmap, diff --git a/drivers/regulator/tps65090-regulator.c b/drivers/regulator/tps65090-regulator.c index f0b660e9f15f..1d2e04f452d4 100644 --- a/drivers/regulator/tps65090-regulator.c +++ b/drivers/regulator/tps65090-regulator.c @@ -47,7 +47,7 @@ struct tps65090_regulator { int overcurrent_wait; }; -static struct regulator_ops tps65090_ext_control_ops = { +static const struct regulator_ops tps65090_ext_control_ops = { }; /** @@ -167,19 +167,19 @@ err: return ret; } -static struct regulator_ops tps65090_reg_control_ops = { +static const struct regulator_ops tps65090_reg_control_ops = { .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, .is_enabled = regulator_is_enabled_regmap, }; -static struct regulator_ops tps65090_fet_control_ops = { +static const struct regulator_ops tps65090_fet_control_ops = { .enable = tps65090_fet_enable, .disable = regulator_disable_regmap, .is_enabled = regulator_is_enabled_regmap, }; -static struct regulator_ops tps65090_ldo_ops = { +static const struct regulator_ops tps65090_ldo_ops = { }; #define tps65090_REG_DESC(_id, _sname, _en_reg, _en_bits, _nvolt, _volt, _ops) \ diff --git a/drivers/regulator/tps6586x-regulator.c b/drivers/regulator/tps6586x-regulator.c index 09e994e1f9a9..18bf4b885b08 100644 --- a/drivers/regulator/tps6586x-regulator.c +++ b/drivers/regulator/tps6586x-regulator.c @@ -60,7 +60,7 @@ struct tps6586x_regulator { int enable_reg[2]; }; -static struct regulator_ops tps6586x_rw_regulator_ops = { +static const struct regulator_ops tps6586x_rw_regulator_ops = { .list_voltage = regulator_list_voltage_table, .map_voltage = regulator_map_voltage_ascend, .get_voltage_sel = regulator_get_voltage_sel_regmap, @@ -71,7 +71,7 @@ static struct regulator_ops tps6586x_rw_regulator_ops = { .disable = regulator_disable_regmap, }; -static struct regulator_ops tps6586x_rw_linear_regulator_ops = { +static const struct regulator_ops tps6586x_rw_linear_regulator_ops = { .list_voltage = regulator_list_voltage_linear, .get_voltage_sel = regulator_get_voltage_sel_regmap, .set_voltage_sel = regulator_set_voltage_sel_regmap, @@ -81,7 +81,7 @@ static struct regulator_ops tps6586x_rw_linear_regulator_ops = { .disable = regulator_disable_regmap, }; -static struct regulator_ops tps6586x_ro_regulator_ops = { +static const struct regulator_ops tps6586x_ro_regulator_ops = { .list_voltage = regulator_list_voltage_table, .map_voltage = regulator_map_voltage_ascend, .get_voltage_sel = regulator_get_voltage_sel_regmap, @@ -91,7 +91,7 @@ static struct regulator_ops tps6586x_ro_regulator_ops = { .disable = regulator_disable_regmap, }; -static struct regulator_ops tps6586x_sys_regulator_ops = { +static const struct regulator_ops tps6586x_sys_regulator_ops = { }; static const unsigned int tps6586x_ldo0_voltages[] = { diff --git a/drivers/regulator/tps65910-regulator.c b/drivers/regulator/tps65910-regulator.c index 4eb5b19d2344..1d5b0a1b86f7 100644 --- a/drivers/regulator/tps65910-regulator.c +++ b/drivers/regulator/tps65910-regulator.c @@ -390,8 +390,8 @@ static int tps65911_get_ctrl_register(int id) static int tps65910_set_mode(struct regulator_dev *dev, unsigned int mode) { struct tps65910_reg *pmic = rdev_get_drvdata(dev); - struct tps65910 *mfd = pmic->mfd; - int reg, value, id = rdev_get_id(dev); + struct regmap *regmap = rdev_get_regmap(dev); + int reg, id = rdev_get_id(dev); reg = pmic->get_ctrl_reg(id); if (reg < 0) @@ -399,14 +399,14 @@ static int tps65910_set_mode(struct regulator_dev *dev, unsigned int mode) switch (mode) { case REGULATOR_MODE_NORMAL: - return tps65910_reg_update_bits(pmic->mfd, reg, - LDO_ST_MODE_BIT | LDO_ST_ON_BIT, - LDO_ST_ON_BIT); + return regmap_update_bits(regmap, reg, + LDO_ST_MODE_BIT | LDO_ST_ON_BIT, + LDO_ST_ON_BIT); case REGULATOR_MODE_IDLE: - value = LDO_ST_ON_BIT | LDO_ST_MODE_BIT; - return tps65910_reg_set_bits(mfd, reg, value); + return regmap_set_bits(regmap, reg, + LDO_ST_ON_BIT | LDO_ST_MODE_BIT); case REGULATOR_MODE_STANDBY: - return tps65910_reg_clear_bits(mfd, reg, LDO_ST_ON_BIT); + return regmap_clear_bits(regmap, reg, LDO_ST_ON_BIT); } return -EINVAL; @@ -415,13 +415,14 @@ static int tps65910_set_mode(struct regulator_dev *dev, unsigned int mode) static unsigned int tps65910_get_mode(struct regulator_dev *dev) { struct tps65910_reg *pmic = rdev_get_drvdata(dev); + struct regmap *regmap = rdev_get_regmap(dev); int ret, reg, value, id = rdev_get_id(dev); reg = pmic->get_ctrl_reg(id); if (reg < 0) return reg; - ret = tps65910_reg_read(pmic->mfd, reg, &value); + ret = regmap_read(regmap, reg, &value); if (ret < 0) return ret; @@ -435,20 +436,20 @@ static unsigned int tps65910_get_mode(struct regulator_dev *dev) static int tps65910_get_voltage_dcdc_sel(struct regulator_dev *dev) { - struct tps65910_reg *pmic = rdev_get_drvdata(dev); + struct regmap *regmap = rdev_get_regmap(dev); int ret, id = rdev_get_id(dev); int opvsel = 0, srvsel = 0, vselmax = 0, mult = 0, sr = 0; switch (id) { case TPS65910_REG_VDD1: - ret = tps65910_reg_read(pmic->mfd, TPS65910_VDD1_OP, &opvsel); + ret = regmap_read(regmap, TPS65910_VDD1_OP, &opvsel); if (ret < 0) return ret; - ret = tps65910_reg_read(pmic->mfd, TPS65910_VDD1, &mult); + ret = regmap_read(regmap, TPS65910_VDD1, &mult); if (ret < 0) return ret; mult = (mult & VDD1_VGAIN_SEL_MASK) >> VDD1_VGAIN_SEL_SHIFT; - ret = tps65910_reg_read(pmic->mfd, TPS65910_VDD1_SR, &srvsel); + ret = regmap_read(regmap, TPS65910_VDD1_SR, &srvsel); if (ret < 0) return ret; sr = opvsel & VDD1_OP_CMD_MASK; @@ -457,14 +458,14 @@ static int tps65910_get_voltage_dcdc_sel(struct regulator_dev *dev) vselmax = 75; break; case TPS65910_REG_VDD2: - ret = tps65910_reg_read(pmic->mfd, TPS65910_VDD2_OP, &opvsel); + ret = regmap_read(regmap, TPS65910_VDD2_OP, &opvsel); if (ret < 0) return ret; - ret = tps65910_reg_read(pmic->mfd, TPS65910_VDD2, &mult); + ret = regmap_read(regmap, TPS65910_VDD2, &mult); if (ret < 0) return ret; mult = (mult & VDD2_VGAIN_SEL_MASK) >> VDD2_VGAIN_SEL_SHIFT; - ret = tps65910_reg_read(pmic->mfd, TPS65910_VDD2_SR, &srvsel); + ret = regmap_read(regmap, TPS65910_VDD2_SR, &srvsel); if (ret < 0) return ret; sr = opvsel & VDD2_OP_CMD_MASK; @@ -473,12 +474,10 @@ static int tps65910_get_voltage_dcdc_sel(struct regulator_dev *dev) vselmax = 75; break; case TPS65911_REG_VDDCTRL: - ret = tps65910_reg_read(pmic->mfd, TPS65911_VDDCTRL_OP, - &opvsel); + ret = regmap_read(regmap, TPS65911_VDDCTRL_OP, &opvsel); if (ret < 0) return ret; - ret = tps65910_reg_read(pmic->mfd, TPS65911_VDDCTRL_SR, - &srvsel); + ret = regmap_read(regmap, TPS65911_VDDCTRL_SR, &srvsel); if (ret < 0) return ret; sr = opvsel & VDDCTRL_OP_CMD_MASK; @@ -514,13 +513,14 @@ static int tps65910_get_voltage_dcdc_sel(struct regulator_dev *dev) static int tps65910_get_voltage_sel(struct regulator_dev *dev) { struct tps65910_reg *pmic = rdev_get_drvdata(dev); + struct regmap *regmap = rdev_get_regmap(dev); int ret, reg, value, id = rdev_get_id(dev); reg = pmic->get_ctrl_reg(id); if (reg < 0) return reg; - ret = tps65910_reg_read(pmic->mfd, reg, &value); + ret = regmap_read(regmap, reg, &value); if (ret < 0) return ret; @@ -556,12 +556,13 @@ static int tps65910_get_voltage_vdd3(struct regulator_dev *dev) static int tps65911_get_voltage_sel(struct regulator_dev *dev) { struct tps65910_reg *pmic = rdev_get_drvdata(dev); + struct regmap *regmap = rdev_get_regmap(dev); int ret, id = rdev_get_id(dev); unsigned int value, reg; reg = pmic->get_ctrl_reg(id); - ret = tps65910_reg_read(pmic->mfd, reg, &value); + ret = regmap_read(regmap, reg, &value); if (ret < 0) return ret; @@ -594,7 +595,7 @@ static int tps65911_get_voltage_sel(struct regulator_dev *dev) static int tps65910_set_voltage_dcdc_sel(struct regulator_dev *dev, unsigned selector) { - struct tps65910_reg *pmic = rdev_get_drvdata(dev); + struct regmap *regmap = rdev_get_regmap(dev); int id = rdev_get_id(dev), vsel; int dcdc_mult = 0; @@ -605,10 +606,9 @@ static int tps65910_set_voltage_dcdc_sel(struct regulator_dev *dev, dcdc_mult--; vsel = (selector % VDD1_2_NUM_VOLT_FINE) + 3; - tps65910_reg_update_bits(pmic->mfd, TPS65910_VDD1, - VDD1_VGAIN_SEL_MASK, - dcdc_mult << VDD1_VGAIN_SEL_SHIFT); - tps65910_reg_write(pmic->mfd, TPS65910_VDD1_OP, vsel); + regmap_update_bits(regmap, TPS65910_VDD1, VDD1_VGAIN_SEL_MASK, + dcdc_mult << VDD1_VGAIN_SEL_SHIFT); + regmap_write(regmap, TPS65910_VDD1_OP, vsel); break; case TPS65910_REG_VDD2: dcdc_mult = (selector / VDD1_2_NUM_VOLT_FINE) + 1; @@ -616,14 +616,14 @@ static int tps65910_set_voltage_dcdc_sel(struct regulator_dev *dev, dcdc_mult--; vsel = (selector % VDD1_2_NUM_VOLT_FINE) + 3; - tps65910_reg_update_bits(pmic->mfd, TPS65910_VDD2, - VDD1_VGAIN_SEL_MASK, - dcdc_mult << VDD2_VGAIN_SEL_SHIFT); - tps65910_reg_write(pmic->mfd, TPS65910_VDD2_OP, vsel); + regmap_update_bits(regmap, TPS65910_VDD2, VDD1_VGAIN_SEL_MASK, + dcdc_mult << VDD2_VGAIN_SEL_SHIFT); + regmap_write(regmap, TPS65910_VDD2_OP, vsel); break; case TPS65911_REG_VDDCTRL: vsel = selector + 3; - tps65910_reg_write(pmic->mfd, TPS65911_VDDCTRL_OP, vsel); + regmap_write(regmap, TPS65911_VDDCTRL_OP, vsel); + break; } return 0; @@ -633,6 +633,7 @@ static int tps65910_set_voltage_sel(struct regulator_dev *dev, unsigned selector) { struct tps65910_reg *pmic = rdev_get_drvdata(dev); + struct regmap *regmap = rdev_get_regmap(dev); int reg, id = rdev_get_id(dev); reg = pmic->get_ctrl_reg(id); @@ -649,11 +650,11 @@ static int tps65910_set_voltage_sel(struct regulator_dev *dev, case TPS65910_REG_VAUX2: case TPS65910_REG_VAUX33: case TPS65910_REG_VMMC: - return tps65910_reg_update_bits(pmic->mfd, reg, LDO_SEL_MASK, - selector << LDO_SEL_SHIFT); + return regmap_update_bits(regmap, reg, LDO_SEL_MASK, + selector << LDO_SEL_SHIFT); case TPS65910_REG_VBB: - return tps65910_reg_update_bits(pmic->mfd, reg, BBCH_BBSEL_MASK, - selector << BBCH_BBSEL_SHIFT); + return regmap_update_bits(regmap, reg, BBCH_BBSEL_MASK, + selector << BBCH_BBSEL_SHIFT); } return -EINVAL; @@ -663,6 +664,7 @@ static int tps65911_set_voltage_sel(struct regulator_dev *dev, unsigned selector) { struct tps65910_reg *pmic = rdev_get_drvdata(dev); + struct regmap *regmap = rdev_get_regmap(dev); int reg, id = rdev_get_id(dev); reg = pmic->get_ctrl_reg(id); @@ -673,21 +675,21 @@ static int tps65911_set_voltage_sel(struct regulator_dev *dev, case TPS65911_REG_LDO1: case TPS65911_REG_LDO2: case TPS65911_REG_LDO4: - return tps65910_reg_update_bits(pmic->mfd, reg, LDO1_SEL_MASK, - selector << LDO_SEL_SHIFT); + return regmap_update_bits(regmap, reg, LDO1_SEL_MASK, + selector << LDO_SEL_SHIFT); case TPS65911_REG_LDO3: case TPS65911_REG_LDO5: case TPS65911_REG_LDO6: case TPS65911_REG_LDO7: case TPS65911_REG_LDO8: - return tps65910_reg_update_bits(pmic->mfd, reg, LDO3_SEL_MASK, - selector << LDO_SEL_SHIFT); + return regmap_update_bits(regmap, reg, LDO3_SEL_MASK, + selector << LDO_SEL_SHIFT); case TPS65910_REG_VIO: - return tps65910_reg_update_bits(pmic->mfd, reg, LDO_SEL_MASK, - selector << LDO_SEL_SHIFT); + return regmap_update_bits(regmap, reg, LDO_SEL_MASK, + selector << LDO_SEL_SHIFT); case TPS65910_REG_VBB: - return tps65910_reg_update_bits(pmic->mfd, reg, BBCH_BBSEL_MASK, - selector << BBCH_BBSEL_SHIFT); + return regmap_update_bits(regmap, reg, BBCH_BBSEL_MASK, + selector << BBCH_BBSEL_SHIFT); } return -EINVAL; @@ -757,7 +759,7 @@ static int tps65911_list_voltage(struct regulator_dev *dev, unsigned selector) } /* Regulator ops (except VRTC) */ -static struct regulator_ops tps65910_ops_dcdc = { +static const struct regulator_ops tps65910_ops_dcdc = { .is_enabled = regulator_is_enabled_regmap, .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, @@ -770,7 +772,7 @@ static struct regulator_ops tps65910_ops_dcdc = { .map_voltage = regulator_map_voltage_ascend, }; -static struct regulator_ops tps65910_ops_vdd3 = { +static const struct regulator_ops tps65910_ops_vdd3 = { .is_enabled = regulator_is_enabled_regmap, .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, @@ -781,7 +783,7 @@ static struct regulator_ops tps65910_ops_vdd3 = { .map_voltage = regulator_map_voltage_ascend, }; -static struct regulator_ops tps65910_ops_vbb = { +static const struct regulator_ops tps65910_ops_vbb = { .is_enabled = regulator_is_enabled_regmap, .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, @@ -793,7 +795,7 @@ static struct regulator_ops tps65910_ops_vbb = { .map_voltage = regulator_map_voltage_iterate, }; -static struct regulator_ops tps65910_ops = { +static const struct regulator_ops tps65910_ops = { .is_enabled = regulator_is_enabled_regmap, .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, @@ -805,7 +807,7 @@ static struct regulator_ops tps65910_ops = { .map_voltage = regulator_map_voltage_ascend, }; -static struct regulator_ops tps65911_ops = { +static const struct regulator_ops tps65911_ops = { .is_enabled = regulator_is_enabled_regmap, .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, @@ -850,10 +852,10 @@ static int tps65910_set_ext_sleep_config(struct tps65910_reg *pmic, /* External EN1 control */ if (ext_sleep_config & TPS65910_SLEEP_CONTROL_EXT_INPUT_EN1) - ret = tps65910_reg_set_bits(mfd, + ret = regmap_set_bits(mfd->regmap, TPS65910_EN1_LDO_ASS + regoffs, bit_pos); else - ret = tps65910_reg_clear_bits(mfd, + ret = regmap_clear_bits(mfd->regmap, TPS65910_EN1_LDO_ASS + regoffs, bit_pos); if (ret < 0) { dev_err(mfd->dev, @@ -863,10 +865,10 @@ static int tps65910_set_ext_sleep_config(struct tps65910_reg *pmic, /* External EN2 control */ if (ext_sleep_config & TPS65910_SLEEP_CONTROL_EXT_INPUT_EN2) - ret = tps65910_reg_set_bits(mfd, + ret = regmap_set_bits(mfd->regmap, TPS65910_EN2_LDO_ASS + regoffs, bit_pos); else - ret = tps65910_reg_clear_bits(mfd, + ret = regmap_clear_bits(mfd->regmap, TPS65910_EN2_LDO_ASS + regoffs, bit_pos); if (ret < 0) { dev_err(mfd->dev, @@ -878,10 +880,10 @@ static int tps65910_set_ext_sleep_config(struct tps65910_reg *pmic, if ((tps65910_chip_id(mfd) == TPS65910) && (id >= TPS65910_REG_VDIG1)) { if (ext_sleep_config & TPS65910_SLEEP_CONTROL_EXT_INPUT_EN3) - ret = tps65910_reg_set_bits(mfd, + ret = regmap_set_bits(mfd->regmap, TPS65910_EN3_LDO_ASS + regoffs, bit_pos); else - ret = tps65910_reg_clear_bits(mfd, + ret = regmap_clear_bits(mfd->regmap, TPS65910_EN3_LDO_ASS + regoffs, bit_pos); if (ret < 0) { dev_err(mfd->dev, @@ -893,10 +895,10 @@ static int tps65910_set_ext_sleep_config(struct tps65910_reg *pmic, /* Return if no external control is selected */ if (!(ext_sleep_config & EXT_SLEEP_CONTROL)) { /* Clear all sleep controls */ - ret = tps65910_reg_clear_bits(mfd, + ret = regmap_clear_bits(mfd->regmap, TPS65910_SLEEP_KEEP_LDO_ON + regoffs, bit_pos); if (!ret) - ret = tps65910_reg_clear_bits(mfd, + ret = regmap_clear_bits(mfd->regmap, TPS65910_SLEEP_SET_LDO_OFF + regoffs, bit_pos); if (ret < 0) dev_err(mfd->dev, @@ -917,39 +919,38 @@ static int tps65910_set_ext_sleep_config(struct tps65910_reg *pmic, int sr_reg_add = pmic->get_ctrl_reg(id) + 2; int opvsel, srvsel; - ret = tps65910_reg_read(pmic->mfd, op_reg_add, &opvsel); + ret = regmap_read(mfd->regmap, op_reg_add, &opvsel); if (ret < 0) return ret; - ret = tps65910_reg_read(pmic->mfd, sr_reg_add, &srvsel); + ret = regmap_read(mfd->regmap, sr_reg_add, &srvsel); if (ret < 0) return ret; if (opvsel & VDD1_OP_CMD_MASK) { u8 reg_val = srvsel & VDD1_OP_SEL_MASK; - ret = tps65910_reg_write(pmic->mfd, op_reg_add, - reg_val); + ret = regmap_write(mfd->regmap, op_reg_add, reg_val); if (ret < 0) { dev_err(mfd->dev, "Error in configuring op register\n"); return ret; } } - ret = tps65910_reg_write(pmic->mfd, sr_reg_add, 0); + ret = regmap_write(mfd->regmap, sr_reg_add, 0); if (ret < 0) { dev_err(mfd->dev, "Error in setting sr register\n"); return ret; } } - ret = tps65910_reg_clear_bits(mfd, + ret = regmap_clear_bits(mfd->regmap, TPS65910_SLEEP_KEEP_LDO_ON + regoffs, bit_pos); if (!ret) { if (ext_sleep_config & TPS65911_SLEEP_CONTROL_EXT_INPUT_SLEEP) - ret = tps65910_reg_set_bits(mfd, + ret = regmap_set_bits(mfd->regmap, TPS65910_SLEEP_SET_LDO_OFF + regoffs, bit_pos); else - ret = tps65910_reg_clear_bits(mfd, + ret = regmap_clear_bits(mfd->regmap, TPS65910_SLEEP_SET_LDO_OFF + regoffs, bit_pos); } if (ret < 0) @@ -1097,7 +1098,7 @@ static int tps65910_probe(struct platform_device *pdev) platform_set_drvdata(pdev, pmic); /* Give control of all register to control port */ - err = tps65910_reg_set_bits(pmic->mfd, TPS65910_DEVCTRL, + err = regmap_set_bits(pmic->mfd->regmap, TPS65910_DEVCTRL, DEVCTRL_SR_CTL_I2C_SEL_MASK); if (err < 0) return err; @@ -1113,7 +1114,7 @@ static int tps65910_probe(struct platform_device *pdev) * voltage level can go higher than expected or crash * Workaround: use no synchronization of DCDC clocks */ - tps65910_reg_clear_bits(pmic->mfd, TPS65910_DCDCCTRL, + regmap_clear_bits(pmic->mfd->regmap, TPS65910_DCDCCTRL, DCDCCTRL_DCDCCKSYNC_MASK); break; case TPS65911: diff --git a/drivers/regulator/tps65912-regulator.c b/drivers/regulator/tps65912-regulator.c index 63d6bbd4969b..b52d4f2874b7 100644 --- a/drivers/regulator/tps65912-regulator.c +++ b/drivers/regulator/tps65912-regulator.c @@ -57,7 +57,7 @@ static const struct linear_range tps65912_ldo_ranges[] = { }; /* Operations permitted on DCDCx */ -static struct regulator_ops tps65912_ops_dcdc = { +static const struct regulator_ops tps65912_ops_dcdc = { .is_enabled = regulator_is_enabled_regmap, .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, @@ -67,7 +67,7 @@ static struct regulator_ops tps65912_ops_dcdc = { }; /* Operations permitted on LDOx */ -static struct regulator_ops tps65912_ops_ldo = { +static const struct regulator_ops tps65912_ops_ldo = { .is_enabled = regulator_is_enabled_regmap, .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, diff --git a/drivers/regulator/wm831x-dcdc.c b/drivers/regulator/wm831x-dcdc.c index ad2203d11a88..e43ed4d93f71 100644 --- a/drivers/regulator/wm831x-dcdc.c +++ b/drivers/regulator/wm831x-dcdc.c @@ -178,11 +178,9 @@ static irqreturn_t wm831x_dcdc_uv_irq(int irq, void *data) { struct wm831x_dcdc *dcdc = data; - regulator_lock(dcdc->regulator); regulator_notifier_call_chain(dcdc->regulator, REGULATOR_EVENT_UNDER_VOLTAGE, NULL); - regulator_unlock(dcdc->regulator); return IRQ_HANDLED; } @@ -191,11 +189,9 @@ static irqreturn_t wm831x_dcdc_oc_irq(int irq, void *data) { struct wm831x_dcdc *dcdc = data; - regulator_lock(dcdc->regulator); regulator_notifier_call_chain(dcdc->regulator, REGULATOR_EVENT_OVER_CURRENT, NULL); - regulator_unlock(dcdc->regulator); return IRQ_HANDLED; } diff --git a/drivers/regulator/wm831x-isink.c b/drivers/regulator/wm831x-isink.c index ff3d2bf50410..eade3ae3e333 100644 --- a/drivers/regulator/wm831x-isink.c +++ b/drivers/regulator/wm831x-isink.c @@ -99,11 +99,9 @@ static irqreturn_t wm831x_isink_irq(int irq, void *data) { struct wm831x_isink *isink = data; - regulator_lock(isink->regulator); regulator_notifier_call_chain(isink->regulator, REGULATOR_EVENT_OVER_CURRENT, NULL); - regulator_unlock(isink->regulator); return IRQ_HANDLED; } diff --git a/drivers/regulator/wm831x-ldo.c b/drivers/regulator/wm831x-ldo.c index 7b6cf4810cb7..e091b189ecc0 100644 --- a/drivers/regulator/wm831x-ldo.c +++ b/drivers/regulator/wm831x-ldo.c @@ -46,11 +46,9 @@ static irqreturn_t wm831x_ldo_uv_irq(int irq, void *data) { struct wm831x_ldo *ldo = data; - regulator_lock(ldo->regulator); regulator_notifier_call_chain(ldo->regulator, REGULATOR_EVENT_UNDER_VOLTAGE, NULL); - regulator_unlock(ldo->regulator); return IRQ_HANDLED; } diff --git a/drivers/regulator/wm8350-regulator.c b/drivers/regulator/wm8350-regulator.c index 2e7bfdf7c87b..6579bfdb0c26 100644 --- a/drivers/regulator/wm8350-regulator.c +++ b/drivers/regulator/wm8350-regulator.c @@ -1089,7 +1089,6 @@ static irqreturn_t pmic_uv_handler(int irq, void *data) { struct regulator_dev *rdev = (struct regulator_dev *)data; - regulator_lock(rdev); if (irq == WM8350_IRQ_CS1 || irq == WM8350_IRQ_CS2) regulator_notifier_call_chain(rdev, REGULATOR_EVENT_REGULATION_OUT, @@ -1098,7 +1097,6 @@ static irqreturn_t pmic_uv_handler(int irq, void *data) regulator_notifier_call_chain(rdev, REGULATOR_EVENT_UNDER_VOLTAGE, NULL); - regulator_unlock(rdev); return IRQ_HANDLED; } |