summaryrefslogtreecommitdiff
path: root/drivers/regulator
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/regulator')
-rw-r--r--drivers/regulator/Kconfig70
-rw-r--r--drivers/regulator/Makefile7
-rw-r--r--drivers/regulator/act8865-regulator.c2
-rw-r--r--drivers/regulator/bd718x7-regulator.c2
-rw-r--r--drivers/regulator/bq257xx-regulator.c186
-rw-r--r--drivers/regulator/core.c6
-rw-r--r--drivers/regulator/max77838-regulator.c221
-rw-r--r--drivers/regulator/pca9450-regulator.c13
-rw-r--r--drivers/regulator/pf0900-regulator.c975
-rw-r--r--drivers/regulator/pf530x-regulator.c375
-rw-r--r--drivers/regulator/qcom-pm8008-regulator.c2
-rw-r--r--drivers/regulator/qcom-refgen-regulator.c1
-rw-r--r--drivers/regulator/rpi-panel-attiny-regulator.c2
-rw-r--r--drivers/regulator/rt5133-regulator.c642
-rw-r--r--drivers/regulator/s2dos05-regulator.c165
-rw-r--r--drivers/regulator/scmi-regulator.c3
-rw-r--r--drivers/regulator/spacemit-p1.c157
-rw-r--r--drivers/regulator/sy7636a-regulator.c7
-rw-r--r--drivers/regulator/tps65219-regulator.c12
-rw-r--r--drivers/regulator/tps6524x-regulator.c1
-rw-r--r--drivers/regulator/tps6594-regulator.c2
21 files changed, 2823 insertions, 28 deletions
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index eaa6df1c9f80..d84f3d054c59 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -297,6 +297,14 @@ config REGULATOR_BD96801
This driver can also be built as a module. If so, the module
will be called bd96801-regulator.
+config REGULATOR_BQ257XX
+ tristate "TI BQ257XX regulator family"
+ depends on MFD_BQ257XX
+ depends on GPIOLIB || COMPILE_TEST
+ help
+ Say Y to enable support for the boost regulator function of
+ the BQ257XX family of charger circuits.
+
config REGULATOR_CPCAP
tristate "Motorola CPCAP regulator"
depends on MFD_CPCAP
@@ -777,6 +785,15 @@ config REGULATOR_MAX77826
It includes support for control of output voltage. This
regulator is found on the Samsung Galaxy S5 (klte) smartphone.
+config REGULATOR_MAX77838
+ tristate "Maxim 77838 regulator"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ This driver controls a Maxim 77838 regulator via I2C bus.
+ The regulator include 4 LDOs and a BUCK regulator. It's
+ present on the Samsung Galaxy S7 lineup of smartphones.
+
config REGULATOR_MC13XXX_CORE
tristate
@@ -1006,6 +1023,26 @@ config REGULATOR_PCAP
This driver provides support for the voltage regulators of the
PCAP2 PMIC.
+config REGULATOR_PF0900
+ tristate "NXP PF0900/PF0901/PF09XX regulator driver"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ Say y here to support the NXP PF0900/PF0901/PF09XX PMIC
+ regulator driver.
+
+config REGULATOR_PF530X
+ tristate "NXP PF5300/PF5301/PF5302 regulator driver"
+ depends on I2C && OF
+ select REGMAP_I2C
+ help
+ Say y here to support the regulators found on the NXP
+ PF5300/PF5301/PF5302 PMIC.
+
+ Say M here if you want to support for the regulators found
+ on the NXP PF5300/PF5301/PF5302 PMIC. The module will be named
+ "pf530x-regulator".
+
config REGULATOR_PF8X00
tristate "NXP PF8100/PF8121A/PF8200 regulator driver"
depends on I2C && OF
@@ -1240,6 +1277,18 @@ config REGULATOR_RT5120
600mV to 1395mV, per step 6.250mV. The others are all fixed voltage
by external hardware circuit.
+config REGULATOR_RT5133
+ tristate "Richtek RT5133 PMIC Regulators"
+ depends on I2C && GPIOLIB && OF
+ select REGMAP
+ select CRC8
+ select OF_GPIO
+ help
+ This driver adds support for RT5133 PMIC regulators.
+ RT5133 is an integrated chip. It includes 8 LDOs and 3 GPOs that
+ can be used to drive output high/low purpose. The dependency of the
+ GPO block is internally LDO1 Voltage.
+
config REGULATOR_RT5190A
tristate "Richtek RT5190A PMIC"
depends on I2C
@@ -1344,6 +1393,14 @@ config REGULATOR_RTQ2208
and two ldos. It features wide output voltage range from 0.4V to 2.05V
and the capability to configure the corresponding power stages.
+config REGULATOR_S2DOS05
+ tristate "Samsung S2DOS05 voltage regulator"
+ depends on MFD_SEC_CORE || COMPILE_TEST
+ help
+ This driver provides support for the voltage regulators of the S2DOS05.
+ The S2DOS05 is a companion power management IC for the smart phones.
+ The S2DOS05 has 4 LDOs and 1 BUCK outputs.
+
config REGULATOR_S2MPA01
tristate "Samsung S2MPA01 voltage regulator"
depends on MFD_SEC_CORE || COMPILE_TEST
@@ -1395,6 +1452,19 @@ config REGULATOR_SLG51000
The SLG51000 is seven compact and customizable low dropout
regulators.
+config REGULATOR_SPACEMIT_P1
+ tristate "SpacemiT P1 regulators"
+ depends on ARCH_SPACEMIT || COMPILE_TEST
+ depends on I2C
+ select MFD_SPACEMIT_P1
+ default ARCH_SPACEMIT
+ help
+ Enable support for regulators implemented by the SpacemiT P1
+ power controller. The P1 implements 6 high-efficiency buck
+ converters and 12 programmable LDO regulators. To compile this
+ driver as a module, choose M here. The module will be called
+ "spacemit-pmic".
+
config REGULATOR_STM32_BOOSTER
tristate "STMicroelectronics STM32 BOOSTER"
depends on ARCH_STM32 || COMPILE_TEST
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index be98b29d6675..b3101376029d 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -38,6 +38,7 @@ 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_BQ257XX) += bq257xx-regulator.o
obj-$(CONFIG_REGULATOR_DA903X) += da903x-regulator.o
obj-$(CONFIG_REGULATOR_BD96801) += bd96801-regulator.o
obj-$(CONFIG_REGULATOR_DA9052) += da9052-regulator.o
@@ -92,6 +93,7 @@ obj-$(CONFIG_REGULATOR_MAX77686) += max77686-regulator.o
obj-$(CONFIG_REGULATOR_MAX77693) += max77693-regulator.o
obj-$(CONFIG_REGULATOR_MAX77802) += max77802-regulator.o
obj-$(CONFIG_REGULATOR_MAX77826) += max77826-regulator.o
+obj-$(CONFIG_REGULATOR_MAX77838) += max77838-regulator.o
obj-$(CONFIG_REGULATOR_MAX77857) += max77857-regulator.o
obj-$(CONFIG_REGULATOR_MC13783) += mc13783-regulator.o
obj-$(CONFIG_REGULATOR_MC13892) += mc13892-regulator.o
@@ -124,7 +126,9 @@ 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_PF0900) += pf0900-regulator.o
obj-$(CONFIG_REGULATOR_PF9453) += pf9453-regulator.o
+obj-$(CONFIG_REGULATOR_PF530X) += pf530x-regulator.o
obj-$(CONFIG_REGULATOR_PF8X00) += pf8x00-regulator.o
obj-$(CONFIG_REGULATOR_PFUZE100) += pfuze100-regulator.o
obj-$(CONFIG_REGULATOR_PV88060) += pv88060-regulator.o
@@ -146,6 +150,7 @@ obj-$(CONFIG_REGULATOR_RT4803) += rt4803.o
obj-$(CONFIG_REGULATOR_RT4831) += rt4831-regulator.o
obj-$(CONFIG_REGULATOR_RT5033) += rt5033-regulator.o
obj-$(CONFIG_REGULATOR_RT5120) += rt5120-regulator.o
+obj-$(CONFIG_REGULATOR_RT5133) += rt5133-regulator.o
obj-$(CONFIG_REGULATOR_RT5190A) += rt5190a-regulator.o
obj-$(CONFIG_REGULATOR_RT5739) += rt5739.o
obj-$(CONFIG_REGULATOR_RT5759) += rt5759-regulator.o
@@ -156,12 +161,14 @@ obj-$(CONFIG_REGULATOR_RTMV20) += rtmv20-regulator.o
obj-$(CONFIG_REGULATOR_RTQ2134) += rtq2134-regulator.o
obj-$(CONFIG_REGULATOR_RTQ6752) += rtq6752-regulator.o
obj-$(CONFIG_REGULATOR_RTQ2208) += rtq2208-regulator.o
+obj-$(CONFIG_REGULATOR_S2DOS05) += s2dos05-regulator.o
obj-$(CONFIG_REGULATOR_S2MPA01) += s2mpa01.o
obj-$(CONFIG_REGULATOR_S2MPS11) += s2mps11.o
obj-$(CONFIG_REGULATOR_S5M8767) += s5m8767.o
obj-$(CONFIG_REGULATOR_SC2731) += sc2731-regulator.o
obj-$(CONFIG_REGULATOR_SKY81452) += sky81452-regulator.o
obj-$(CONFIG_REGULATOR_SLG51000) += slg51000-regulator.o
+obj-$(CONFIG_REGULATOR_SPACEMIT_P1) += spacemit-p1.o
obj-$(CONFIG_REGULATOR_STM32_BOOSTER) += stm32-booster.o
obj-$(CONFIG_REGULATOR_STM32_VREFBUF) += stm32-vrefbuf.o
obj-$(CONFIG_REGULATOR_STM32_PWR) += stm32-pwr.o
diff --git a/drivers/regulator/act8865-regulator.c b/drivers/regulator/act8865-regulator.c
index 0457af23c55a..b2a6ddc6f56d 100644
--- a/drivers/regulator/act8865-regulator.c
+++ b/drivers/regulator/act8865-regulator.c
@@ -643,7 +643,7 @@ static int act8600_charger_probe(struct device *dev, struct regmap *regmap)
struct power_supply *charger;
struct power_supply_config cfg = {
.drv_data = regmap,
- .of_node = dev->of_node,
+ .fwnode = dev_fwnode(dev),
};
charger = devm_power_supply_register(dev, &act8600_charger_desc, &cfg);
diff --git a/drivers/regulator/bd718x7-regulator.c b/drivers/regulator/bd718x7-regulator.c
index e803cc59d68a..022d98f3c32a 100644
--- a/drivers/regulator/bd718x7-regulator.c
+++ b/drivers/regulator/bd718x7-regulator.c
@@ -1598,7 +1598,7 @@ static int setup_feedback_loop(struct device *dev, struct device_node *np,
if (desc->n_linear_ranges && desc->linear_ranges) {
struct linear_range *new;
- new = devm_kzalloc(dev, desc->n_linear_ranges *
+ new = devm_kcalloc(dev, desc->n_linear_ranges,
sizeof(struct linear_range),
GFP_KERNEL);
if (!new)
diff --git a/drivers/regulator/bq257xx-regulator.c b/drivers/regulator/bq257xx-regulator.c
new file mode 100644
index 000000000000..fc1ccede4468
--- /dev/null
+++ b/drivers/regulator/bq257xx-regulator.c
@@ -0,0 +1,186 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * BQ257XX Battery Charger Driver
+ * Copyright (C) 2025 Chris Morgan <macromorgan@hotmail.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/mfd/bq257xx.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/of_regulator.h>
+
+struct bq257xx_reg_data {
+ struct bq257xx_device *bq;
+ struct regulator_dev *bq257xx_reg;
+ struct gpio_desc *otg_en_gpio;
+ struct regulator_desc desc;
+};
+
+static int bq25703_vbus_get_cur_limit(struct regulator_dev *rdev)
+{
+ struct bq257xx_reg_data *pdata = rdev_get_drvdata(rdev);
+ int ret;
+ unsigned int reg;
+
+ ret = regmap_read(pdata->bq->regmap, BQ25703_OTG_CURRENT, &reg);
+ if (ret)
+ return ret;
+ return FIELD_GET(BQ25703_OTG_CUR_MASK, reg) * BQ25703_OTG_CUR_STEP_UA;
+}
+
+/*
+ * Check if the minimum current and maximum current requested are
+ * sane values, then set the register accordingly.
+ */
+static int bq25703_vbus_set_cur_limit(struct regulator_dev *rdev,
+ int min_uA, int max_uA)
+{
+ struct bq257xx_reg_data *pdata = rdev_get_drvdata(rdev);
+ unsigned int reg;
+
+ if ((min_uA > BQ25703_OTG_CUR_MAX_UA) || (max_uA < 0))
+ return -EINVAL;
+
+ reg = (max_uA / BQ25703_OTG_CUR_STEP_UA);
+
+ /* Catch rounding errors since our step is 50000uA. */
+ if ((reg * BQ25703_OTG_CUR_STEP_UA) < min_uA)
+ return -EINVAL;
+
+ return regmap_write(pdata->bq->regmap, BQ25703_OTG_CURRENT,
+ FIELD_PREP(BQ25703_OTG_CUR_MASK, reg));
+}
+
+static int bq25703_vbus_enable(struct regulator_dev *rdev)
+{
+ struct bq257xx_reg_data *pdata = rdev_get_drvdata(rdev);
+
+ if (pdata->otg_en_gpio)
+ gpiod_set_value_cansleep(pdata->otg_en_gpio, 1);
+ return regulator_enable_regmap(rdev);
+}
+
+static int bq25703_vbus_disable(struct regulator_dev *rdev)
+{
+ struct bq257xx_reg_data *pdata = rdev_get_drvdata(rdev);
+
+ if (pdata->otg_en_gpio)
+ gpiod_set_value_cansleep(pdata->otg_en_gpio, 0);
+ return regulator_disable_regmap(rdev);
+}
+
+static const struct regulator_ops bq25703_vbus_ops = {
+ .enable = bq25703_vbus_enable,
+ .disable = bq25703_vbus_disable,
+ .is_enabled = regulator_is_enabled_regmap,
+ .list_voltage = regulator_list_voltage_linear,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_current_limit = bq25703_vbus_get_cur_limit,
+ .set_current_limit = bq25703_vbus_set_cur_limit,
+};
+
+static const struct regulator_desc bq25703_vbus_desc = {
+ .name = "vbus",
+ .of_match = of_match_ptr("vbus"),
+ .regulators_node = of_match_ptr("regulators"),
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ .ops = &bq25703_vbus_ops,
+ .min_uV = BQ25703_OTG_VOLT_MIN_UV,
+ .uV_step = BQ25703_OTG_VOLT_STEP_UV,
+ .n_voltages = BQ25703_OTG_VOLT_NUM_VOLT,
+ .enable_mask = BQ25703_EN_OTG_MASK,
+ .enable_reg = BQ25703_CHARGE_OPTION_3,
+ .enable_val = BQ25703_EN_OTG_MASK,
+ .disable_val = 0,
+ .vsel_reg = BQ25703_OTG_VOLT,
+ .vsel_mask = BQ25703_OTG_VOLT_MASK,
+};
+
+/* Get optional GPIO for OTG regulator enable. */
+static void bq257xx_reg_dt_parse_gpio(struct platform_device *pdev)
+{
+ struct device_node *child, *subchild;
+ struct bq257xx_reg_data *pdata = platform_get_drvdata(pdev);
+
+ child = of_get_child_by_name(pdev->dev.of_node,
+ pdata->desc.regulators_node);
+ if (!child)
+ return;
+
+ subchild = of_get_child_by_name(child, pdata->desc.of_match);
+ if (!subchild)
+ return;
+
+ of_node_put(child);
+
+ pdata->otg_en_gpio = devm_fwnode_gpiod_get_index(&pdev->dev,
+ of_fwnode_handle(subchild),
+ "enable", 0,
+ GPIOD_OUT_LOW,
+ pdata->desc.of_match);
+
+ of_node_put(subchild);
+
+ if (IS_ERR(pdata->otg_en_gpio)) {
+ dev_err(&pdev->dev, "Error getting enable gpio: %ld\n",
+ PTR_ERR(pdata->otg_en_gpio));
+ return;
+ }
+}
+
+static int bq257xx_regulator_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct bq257xx_device *bq = dev_get_drvdata(pdev->dev.parent);
+ struct bq257xx_reg_data *pdata;
+ struct device_node *np = dev->of_node;
+ struct regulator_config cfg = {};
+
+ pdev->dev.of_node = pdev->dev.parent->of_node;
+ pdev->dev.of_node_reused = true;
+
+ pdata = devm_kzalloc(&pdev->dev, sizeof(struct bq257xx_reg_data), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ pdata->bq = bq;
+ pdata->desc = bq25703_vbus_desc;
+
+ platform_set_drvdata(pdev, pdata);
+ bq257xx_reg_dt_parse_gpio(pdev);
+
+ cfg.dev = &pdev->dev;
+ cfg.driver_data = pdata;
+ cfg.of_node = np;
+ cfg.regmap = dev_get_regmap(pdev->dev.parent, NULL);
+ if (!cfg.regmap)
+ return -ENODEV;
+
+ pdata->bq257xx_reg = devm_regulator_register(dev, &pdata->desc, &cfg);
+ if (IS_ERR(pdata->bq257xx_reg)) {
+ return dev_err_probe(&pdev->dev, PTR_ERR(pdata->bq257xx_reg),
+ "error registering bq257xx regulator");
+ }
+
+ return 0;
+}
+
+static struct platform_driver bq257xx_reg_driver = {
+ .driver = {
+ .name = "bq257xx-regulator",
+ },
+ .probe = bq257xx_regulator_probe,
+};
+
+module_platform_driver(bq257xx_reg_driver);
+
+MODULE_DESCRIPTION("bq257xx regulator driver");
+MODULE_AUTHOR("Chris Morgan <macromorgan@hotmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index 8ed9b96518cf..dd7b10e768c0 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -1586,8 +1586,8 @@ static int set_machine_constraints(struct regulator_dev *rdev)
}
if (rdev->constraints->active_discharge && ops->set_active_discharge) {
- bool ad_state = (rdev->constraints->active_discharge ==
- REGULATOR_ACTIVE_DISCHARGE_ENABLE) ? true : false;
+ bool ad_state = rdev->constraints->active_discharge ==
+ REGULATOR_ACTIVE_DISCHARGE_ENABLE;
ret = ops->set_active_discharge(rdev, ad_state);
if (ret < 0) {
@@ -3884,7 +3884,7 @@ static int regulator_set_voltage_unlocked(struct regulator *regulator,
new_delta = ret;
/* check that voltage is converging quickly enough */
- if (new_delta - delta > rdev->constraints->max_uV_step) {
+ if (delta - new_delta < rdev->constraints->max_uV_step) {
ret = -EWOULDBLOCK;
goto out;
}
diff --git a/drivers/regulator/max77838-regulator.c b/drivers/regulator/max77838-regulator.c
new file mode 100644
index 000000000000..9faddbfd25fd
--- /dev/null
+++ b/drivers/regulator/max77838-regulator.c
@@ -0,0 +1,221 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+//
+// regulator driver for Maxim MAX77838
+//
+// based on max77826-regulator.c
+//
+// Copyright (c) 2025, Ivaylo Ivanov <ivo.ivanov.ivanov1@gmail.com>
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+
+enum max77838_registers {
+ MAX77838_REG_DEVICE_ID = 0x00,
+ MAX77838_REG_TOPSYS_STAT,
+ MAX77838_REG_STAT,
+ MAX77838_REG_EN,
+ MAX77838_REG_GPIO_PD_CTRL,
+ MAX77838_REG_UVLO_CFG1,
+ /* 0x06 - 0x0B: reserved */
+ MAX77838_REG_I2C_CFG = 0x0C,
+ /* 0x0D - 0x0F: reserved */
+ MAX77838_REG_LDO1_CFG = 0x10,
+ MAX77838_REG_LDO2_CFG,
+ MAX77838_REG_LDO3_CFG,
+ MAX77838_REG_LDO4_CFG,
+ /* 0x14 - 0x1F: reserved */
+ MAX77838_REG_BUCK_CFG1 = 0x20,
+ MAX77838_REG_BUCK_VOUT,
+};
+
+enum max77838_regulators {
+ MAX77838_LDO1 = 0,
+ MAX77838_LDO2,
+ MAX77838_LDO3,
+ MAX77838_LDO4,
+ MAX77838_BUCK,
+ MAX77838_MAX_REGULATORS,
+};
+
+#define MAX77838_MASK_LDO 0x7f
+#define MAX77838_MASK_BUCK 0xff
+
+#define MAX77838_LDO1_EN BIT(0)
+#define MAX77838_LDO2_EN BIT(1)
+#define MAX77838_LDO3_EN BIT(2)
+#define MAX77838_LDO4_EN BIT(3)
+#define MAX77838_BUCK_EN BIT(4)
+
+#define MAX77838_BUCK_AD BIT(3)
+#define MAX77838_LDO_AD BIT(7)
+
+#define MAX77838_LDO_VOLT_MIN 600000
+#define MAX77838_LDO_VOLT_MAX 3775000
+#define MAX77838_LDO_VOLT_STEP 25000
+
+#define MAX77838_BUCK_VOLT_MIN 500000
+#define MAX77838_BUCK_VOLT_MAX 2093750
+#define MAX77838_BUCK_VOLT_STEP 6250
+
+#define MAX77838_VOLT_RANGE(_type) \
+ ((MAX77838_ ## _type ## _VOLT_MAX - \
+ MAX77838_ ## _type ## _VOLT_MIN) / \
+ MAX77838_ ## _type ## _VOLT_STEP + 1)
+
+#define MAX77838_LDO(_id) \
+ [MAX77838_LDO ## _id] = { \
+ .id = MAX77838_LDO ## _id, \
+ .name = "ldo"#_id, \
+ .of_match = of_match_ptr("ldo"#_id), \
+ .regulators_node = "regulators", \
+ .ops = &max77838_regulator_ops, \
+ .min_uV = MAX77838_LDO_VOLT_MIN, \
+ .uV_step = MAX77838_LDO_VOLT_STEP, \
+ .n_voltages = MAX77838_VOLT_RANGE(LDO), \
+ .enable_reg = MAX77838_REG_EN, \
+ .enable_mask = MAX77838_LDO ## _id ## _EN, \
+ .vsel_reg = MAX77838_REG_LDO ## _id ## _CFG, \
+ .vsel_mask = MAX77838_MASK_LDO, \
+ .active_discharge_off = 0, \
+ .active_discharge_on = MAX77838_LDO_AD, \
+ .active_discharge_mask = MAX77838_LDO_AD, \
+ .active_discharge_reg = MAX77838_REG_LDO ## _id ## _CFG, \
+ .owner = THIS_MODULE, \
+ }
+
+#define MAX77838_BUCK_DESC \
+ [MAX77838_BUCK] = { \
+ .id = MAX77838_BUCK, \
+ .name = "buck", \
+ .of_match = of_match_ptr("buck"), \
+ .regulators_node = "regulators", \
+ .ops = &max77838_regulator_ops, \
+ .min_uV = MAX77838_BUCK_VOLT_MIN, \
+ .uV_step = MAX77838_BUCK_VOLT_STEP, \
+ .n_voltages = MAX77838_VOLT_RANGE(BUCK), \
+ .enable_reg = MAX77838_REG_EN, \
+ .enable_mask = MAX77838_BUCK_EN, \
+ .vsel_reg = MAX77838_REG_BUCK_VOUT, \
+ .vsel_mask = MAX77838_MASK_BUCK, \
+ .active_discharge_off = 0, \
+ .active_discharge_on = MAX77838_BUCK_AD, \
+ .active_discharge_mask = MAX77838_BUCK_AD, \
+ .active_discharge_reg = MAX77838_REG_BUCK_CFG1, \
+ .owner = THIS_MODULE, \
+ }
+
+struct max77838_regulator_info {
+ struct regmap *regmap;
+};
+
+static const struct regmap_config max77838_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = MAX77838_REG_BUCK_VOUT,
+};
+
+static const struct regulator_ops max77838_regulator_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .list_voltage = regulator_list_voltage_linear,
+ .map_voltage = regulator_map_voltage_linear,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .set_active_discharge = regulator_set_active_discharge_regmap,
+};
+
+static const struct regulator_desc max77838_regulators_desc[] = {
+ MAX77838_LDO(1),
+ MAX77838_LDO(2),
+ MAX77838_LDO(3),
+ MAX77838_LDO(4),
+ MAX77838_BUCK_DESC,
+};
+
+static int max77838_read_device_id(struct regmap *regmap, struct device *dev)
+{
+ unsigned int device_id;
+ int ret;
+
+ ret = regmap_read(regmap, MAX77838_REG_DEVICE_ID, &device_id);
+ if (!ret)
+ dev_dbg(dev, "DEVICE_ID: 0x%x\n", device_id);
+
+ return ret;
+}
+
+static int max77838_i2c_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct max77838_regulator_info *info;
+ struct regulator_config config = {};
+ struct regulator_dev *rdev;
+ struct regmap *regmap;
+ int i;
+
+ info = devm_kzalloc(dev, sizeof(struct max77838_regulator_info),
+ GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ regmap = devm_regmap_init_i2c(client, &max77838_regmap_config);
+ if (IS_ERR(regmap)) {
+ dev_err(dev, "Failed to allocate regmap!\n");
+ return PTR_ERR(regmap);
+ }
+
+ info->regmap = regmap;
+ i2c_set_clientdata(client, info);
+
+ config.dev = dev;
+ config.regmap = regmap;
+ config.driver_data = info;
+
+ for (i = 0; i < MAX77838_MAX_REGULATORS; i++) {
+ rdev = devm_regulator_register(dev,
+ &max77838_regulators_desc[i],
+ &config);
+ if (IS_ERR(rdev)) {
+ dev_err(dev, "Failed to register regulator!\n");
+ return PTR_ERR(rdev);
+ }
+ }
+
+ return max77838_read_device_id(regmap, dev);
+}
+
+static const struct of_device_id __maybe_unused max77838_of_match[] = {
+ { .compatible = "maxim,max77838" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, max77838_of_match);
+
+static const struct i2c_device_id max77838_id[] = {
+ { "max77838-regulator" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(i2c, max77838_id);
+
+static struct i2c_driver max77838_regulator_driver = {
+ .driver = {
+ .name = "max77838",
+ .probe_type = PROBE_PREFER_ASYNCHRONOUS,
+ .of_match_table = of_match_ptr(max77838_of_match),
+ },
+ .probe = max77838_i2c_probe,
+ .id_table = max77838_id,
+};
+module_i2c_driver(max77838_regulator_driver);
+
+MODULE_AUTHOR("Ivaylo Ivanov <ivo.ivanov.ivanov1@gmail.com>");
+MODULE_DESCRIPTION("MAX77838 PMIC regulator driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/regulator/pca9450-regulator.c b/drivers/regulator/pca9450-regulator.c
index feadb21a8f30..4be270f4d6c3 100644
--- a/drivers/regulator/pca9450-regulator.c
+++ b/drivers/regulator/pca9450-regulator.c
@@ -40,7 +40,6 @@ struct pca9450 {
struct device *dev;
struct regmap *regmap;
struct gpio_desc *sd_vsel_gpio;
- struct notifier_block restart_nb;
enum pca9450_chip_type type;
unsigned int rcnt;
int irq;
@@ -1100,10 +1099,9 @@ static irqreturn_t pca9450_irq_handler(int irq, void *data)
return IRQ_HANDLED;
}
-static int pca9450_i2c_restart_handler(struct notifier_block *nb,
- unsigned long action, void *data)
+static int pca9450_i2c_restart_handler(struct sys_off_data *data)
{
- struct pca9450 *pca9450 = container_of(nb, struct pca9450, restart_nb);
+ struct pca9450 *pca9450 = data->cb_data;
struct i2c_client *i2c = container_of(pca9450->dev, struct i2c_client, dev);
dev_dbg(&i2c->dev, "Restarting device..\n");
@@ -1261,10 +1259,9 @@ static int pca9450_i2c_probe(struct i2c_client *i2c)
pca9450->sd_vsel_fixed_low =
of_property_read_bool(ldo5->dev.of_node, "nxp,sd-vsel-fixed-low");
- pca9450->restart_nb.notifier_call = pca9450_i2c_restart_handler;
- pca9450->restart_nb.priority = PCA9450_RESTART_HANDLER_PRIORITY;
-
- if (register_restart_handler(&pca9450->restart_nb))
+ if (devm_register_sys_off_handler(&i2c->dev, SYS_OFF_MODE_RESTART,
+ PCA9450_RESTART_HANDLER_PRIORITY,
+ pca9450_i2c_restart_handler, pca9450))
dev_warn(&i2c->dev, "Failed to register restart handler\n");
dev_info(&i2c->dev, "%s probed.\n",
diff --git a/drivers/regulator/pf0900-regulator.c b/drivers/regulator/pf0900-regulator.c
new file mode 100644
index 000000000000..b5effee32917
--- /dev/null
+++ b/drivers/regulator/pf0900-regulator.c
@@ -0,0 +1,975 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright 2025 NXP.
+// NXP PF0900 pmic driver
+
+#include <linux/bitfield.h>
+#include <linux/crc8.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+
+enum pf0900_regulators {
+ PF0900_SW1 = 0,
+ PF0900_SW2,
+ PF0900_SW3,
+ PF0900_SW4,
+ PF0900_SW5,
+ PF0900_LDO1,
+ PF0900_LDO2,
+ PF0900_LDO3,
+ PF0900_VAON,
+ PF0900_REGULATOR_CNT,
+};
+
+enum {
+ PF0900_DVS_LEVEL_RUN = 0,
+ PF0900_DVS_LEVEL_STANDBY,
+ PF0900_DVS_LEVEL_MAX,
+};
+
+
+#define PF0900_VAON_VOLTAGE_NUM 0x03
+#define PF0900_SW_VOLTAGE_NUM 0x100
+#define PF0900_LDO_VOLTAGE_NUM 0x20
+
+#define REGU_SW_CNT 0x5
+#define REGU_LDO_VAON_CNT 0x4
+
+enum {
+ PF0900_REG_DEV_ID = 0x00,
+ PF0900_REG_DEV_FAM = 0x01,
+ PF0900_REG_REV_ID = 0x02,
+ PF0900_REG_PROG_ID1 = 0x03,
+ PF0900_REG_PROG_ID2 = 0x04,
+ PF0900_REG_SYSTEM_INT = 0x05,
+ PF0900_REG_STATUS1_INT = 0x06,
+ PF0900_REG_STATUS1_MSK = 0x07,
+ PF0900_REG_STATUS1_SNS = 0x08,
+ PF0900_REG_STATUS2_INT = 0x09,
+ PF0900_REG_STATUS2_MSK = 0x0A,
+ PF0900_REG_STATUS2_SNS = 0x0B,
+ PF0900_REG_STATUS3_INT = 0x0C,
+ PF0900_REG_STATUS3_MSK = 0x0D,
+ PF0900_REG_SW_MODE_INT = 0x0E,
+ PF0900_REG_SW_MODE_MSK = 0x0F,
+ PF0900_REG_SW_ILIM_INT = 0x10,
+ PF0900_REG_SW_ILIM_MSK = 0x11,
+ PF0900_REG_SW_ILIM_SNS = 0x12,
+ PF0900_REG_LDO_ILIM_INT = 0x13,
+ PF0900_REG_LDO_ILIM_MSK = 0x14,
+ PF0900_REG_LDO_ILIM_SNS = 0x15,
+ PF0900_REG_SW_UV_INT = 0x16,
+ PF0900_REG_SW_UV_MSK = 0x17,
+ PF0900_REG_SW_UV_SNS = 0x18,
+ PF0900_REG_SW_OV_INT = 0x19,
+ PF0900_REG_SW_OV_MSK = 0x1A,
+ PF0900_REG_SW_OV_SNS = 0x1B,
+ PF0900_REG_LDO_UV_INT = 0x1C,
+ PF0900_REG_LDO_UV_MSK = 0x1D,
+ PF0900_REG_LDO_UV_SNS = 0x1E,
+ PF0900_REG_LDO_OV_INT = 0x1F,
+ PF0900_REG_LDO_OV_MSK = 0x20,
+ PF0900_REG_LDO_OV_SNS = 0x21,
+ PF0900_REG_PWRON_INT = 0x22,
+ PF0900_REG_IO_INT = 0x24,
+ PF0900_REG_IO_MSK = 0x25,
+ PF0900_REG_IO_SNS = 0x26,
+ PF0900_REG_IOSHORT_SNS = 0x27,
+ PF0900_REG_ABIST_OV1 = 0x28,
+ PF0900_REG_ABIST_OV2 = 0x29,
+ PF0900_REG_ABIST_UV1 = 0x2A,
+ PF0900_REG_ABIST_UV2 = 0x2B,
+ PF0900_REG_ABIST_IO = 0x2C,
+ PF0900_REG_TEST_FLAGS = 0x2D,
+ PF0900_REG_HFAULT_FLAGS = 0x2E,
+ PF0900_REG_FAULT_FLAGS = 0x2F,
+ PF0900_REG_FS0B_CFG = 0x30,
+ PF0900_REG_FCCU_CFG = 0x31,
+ PF0900_REG_RSTB_CFG1 = 0x32,
+ PF0900_REG_SYSTEM_CMD = 0x33,
+ PF0900_REG_FS0B_CMD = 0x34,
+ PF0900_REG_SECURE_WR1 = 0x35,
+ PF0900_REG_SECURE_WR2 = 0x36,
+ PF0900_REG_VMON_CFG1 = 0x37,
+ PF0900_REG_SYS_CFG1 = 0x38,
+ PF0900_REG_GPO_CFG = 0x39,
+ PF0900_REG_GPO_CTRL = 0x3A,
+ PF0900_REG_PWRUP_CFG = 0x3B,
+ PF0900_REG_RSTB_PWRUP = 0x3C,
+ PF0900_REG_GPIO1_PWRUP = 0x3D,
+ PF0900_REG_GPIO2_PWRUP = 0x3E,
+ PF0900_REG_GPIO3_PWRUP = 0x3F,
+ PF0900_REG_GPIO4_PWRUP = 0x40,
+ PF0900_REG_VMON1_PWRUP = 0x41,
+ PF0900_REG_VMON2_PWRUP = 0x42,
+ PF0900_REG_SW1_PWRUP = 0x43,
+ PF0900_REG_SW2_PWRUP = 0x44,
+ PF0900_REG_SW3_PWRUP = 0x45,
+ PF0900_REG_SW4_PWRUP = 0x46,
+ PF0900_REG_SW5_PWRUP = 0x47,
+ PF0900_REG_LDO1_PWRUP = 0x48,
+ PF0900_REG_LDO2_PWRUP = 0x49,
+ PF0900_REG_LDO3_PWRUP = 0x4A,
+ PF0900_REG_VAON_PWRUP = 0x4B,
+ PF0900_REG_FREQ_CTRL = 0x4C,
+ PF0900_REG_PWRON_CFG = 0x4D,
+ PF0900_REG_WD_CTRL1 = 0x4E,
+ PF0900_REG_WD_CTRL2 = 0x4F,
+ PF0900_REG_WD_CFG1 = 0x50,
+ PF0900_REG_WD_CFG2 = 0x51,
+ PF0900_REG_WD_CNT1 = 0x52,
+ PF0900_REG_WD_CNT2 = 0x53,
+ PF0900_REG_FAULT_CFG = 0x54,
+ PF0900_REG_FAULT_CNT = 0x55,
+ PF0900_REG_DFS_CNT = 0x56,
+ PF0900_REG_AMUX_CFG = 0x57,
+ PF0900_REG_VMON1_RUN_CFG = 0x58,
+ PF0900_REG_VMON1_STBY_CFG = 0x59,
+ PF0900_REG_VMON1_CTRL = 0x5A,
+ PF0900_REG_VMON2_RUN_CFG = 0x5B,
+ PF0900_REG_VMON2_STBY_CFG = 0x5C,
+ PF0900_REG_VMON2_CTRL = 0x5D,
+ PF0900_REG_SW1_VRUN = 0x5E,
+ PF0900_REG_SW1_VSTBY = 0x5F,
+ PF0900_REG_SW1_MODE = 0x60,
+ PF0900_REG_SW1_CFG1 = 0x61,
+ PF0900_REG_SW1_CFG2 = 0x62,
+ PF0900_REG_SW2_VRUN = 0x63,
+ PF0900_REG_SW2_VSTBY = 0x64,
+ PF0900_REG_SW2_MODE = 0x65,
+ PF0900_REG_SW2_CFG1 = 0x66,
+ PF0900_REG_SW2_CFG2 = 0x67,
+ PF0900_REG_SW3_VRUN = 0x68,
+ PF0900_REG_SW3_VSTBY = 0x69,
+ PF0900_REG_SW3_MODE = 0x6A,
+ PF0900_REG_SW3_CFG1 = 0x6B,
+ PF0900_REG_SW3_CFG2 = 0x6C,
+ PF0900_REG_SW4_VRUN = 0x6D,
+ PF0900_REG_SW4_VSTBY = 0x6E,
+ PF0900_REG_SW4_MODE = 0x6F,
+ PF0900_REG_SW4_CFG1 = 0x70,
+ PF0900_REG_SW4_CFG2 = 0x71,
+ PF0900_REG_SW5_VRUN = 0x72,
+ PF0900_REG_SW5_VSTBY = 0x73,
+ PF0900_REG_SW5_MODE = 0x74,
+ PF0900_REG_SW5_CFG1 = 0x75,
+ PF0900_REG_SW5_CFG2 = 0x76,
+ PF0900_REG_LDO1_RUN = 0x77,
+ PF0900_REG_LDO1_STBY = 0x78,
+ PF0900_REG_LDO1_CFG2 = 0x79,
+ PF0900_REG_LDO2_RUN = 0x7A,
+ PF0900_REG_LDO2_STBY = 0x7B,
+ PF0900_REG_LDO2_CFG2 = 0x7C,
+ PF0900_REG_LDO3_RUN = 0x7D,
+ PF0900_REG_LDO3_STBY = 0x7E,
+ PF0900_REG_LDO3_CFG2 = 0x7F,
+ PF0900_REG_VAON_CFG1 = 0x80,
+ PF0900_REG_VAON_CFG2 = 0x81,
+ PF0900_REG_SYS_DIAG = 0x82,
+ PF0900_MAX_REGISTER,
+};
+
+/* PF0900 SW MODE */
+#define SW_RUN_MODE_OFF 0x00
+#define SW_RUN_MODE_PWM 0x01
+#define SW_RUN_MODE_PFM 0x02
+#define SW_STBY_MODE_OFF 0x00
+#define SW_STBY_MODE_PWM 0x04
+#define SW_STBY_MODE_PFM 0x08
+
+/* PF0900 SW MODE MASK */
+#define SW_RUN_MODE_MASK GENMASK(1, 0)
+#define SW_STBY_MODE_MASK GENMASK(3, 2)
+
+/* PF0900 SW VRUN/VSTBY MASK */
+#define PF0900_SW_VOL_MASK GENMASK(7, 0)
+
+/* PF0900_REG_VAON_CFG1 bits */
+#define PF0900_VAON_1P8V 0x01
+
+#define PF0900_VAON_MASK GENMASK(1, 0)
+
+/* PF0900_REG_SWX_CFG1 MASK */
+#define PF0900_SW_DVS_MASK GENMASK(4, 3)
+
+/* PF0900_REG_LDO_RUN MASK */
+#define VLDO_RUN_MASK GENMASK(4, 0)
+#define LDO_RUN_EN_MASK BIT(5)
+
+/* PF0900_REG_STATUS1_INT bits */
+#define PF0900_IRQ_PWRUP BIT(3)
+
+/* PF0900_REG_ILIM_INT bits */
+#define PF0900_IRQ_SW1_IL BIT(0)
+#define PF0900_IRQ_SW2_IL BIT(1)
+#define PF0900_IRQ_SW3_IL BIT(2)
+#define PF0900_IRQ_SW4_IL BIT(3)
+#define PF0900_IRQ_SW5_IL BIT(4)
+
+#define PF0900_IRQ_LDO1_IL BIT(0)
+#define PF0900_IRQ_LDO2_IL BIT(1)
+#define PF0900_IRQ_LDO3_IL BIT(2)
+
+/* PF0900_REG_UV_INT bits */
+#define PF0900_IRQ_SW1_UV BIT(0)
+#define PF0900_IRQ_SW2_UV BIT(1)
+#define PF0900_IRQ_SW3_UV BIT(2)
+#define PF0900_IRQ_SW4_UV BIT(3)
+#define PF0900_IRQ_SW5_UV BIT(4)
+
+#define PF0900_IRQ_LDO1_UV BIT(0)
+#define PF0900_IRQ_LDO2_UV BIT(1)
+#define PF0900_IRQ_LDO3_UV BIT(2)
+#define PF0900_IRQ_VAON_UV BIT(3)
+
+/* PF0900_REG_OV_INT bits */
+#define PF0900_IRQ_SW1_OV BIT(0)
+#define PF0900_IRQ_SW2_OV BIT(1)
+#define PF0900_IRQ_SW3_OV BIT(2)
+#define PF0900_IRQ_SW4_OV BIT(3)
+#define PF0900_IRQ_SW5_OV BIT(4)
+
+#define PF0900_IRQ_LDO1_OV BIT(0)
+#define PF0900_IRQ_LDO2_OV BIT(1)
+#define PF0900_IRQ_LDO3_OV BIT(2)
+#define PF0900_IRQ_VAON_OV BIT(3)
+
+struct pf0900_regulator_desc {
+ struct regulator_desc desc;
+ unsigned int suspend_enable_mask;
+ unsigned int suspend_voltage_reg;
+ unsigned int suspend_voltage_cache;
+};
+
+struct pf0900_drvdata {
+ const struct pf0900_regulator_desc *desc;
+ unsigned int rcnt;
+};
+
+struct pf0900 {
+ struct device *dev;
+ struct regmap *regmap;
+ const struct pf0900_drvdata *drvdata;
+ struct regulator_dev *rdevs[PF0900_REGULATOR_CNT];
+ int irq;
+ unsigned short addr;
+ bool crc_en;
+};
+
+enum pf0900_regulator_type {
+ PF0900_SW = 0,
+ PF0900_LDO,
+};
+
+#define PF0900_REGU_IRQ(_reg, _type, _event) \
+ { \
+ .reg = _reg, \
+ .type = _type, \
+ .event = _event, \
+ }
+
+struct pf0900_regulator_irq {
+ unsigned int reg;
+ unsigned int type;
+ unsigned int event;
+};
+
+static const struct regmap_range pf0900_range = {
+ .range_min = PF0900_REG_DEV_ID,
+ .range_max = PF0900_REG_SYS_DIAG,
+};
+
+static const struct regmap_access_table pf0900_volatile_regs = {
+ .yes_ranges = &pf0900_range,
+ .n_yes_ranges = 1,
+};
+
+static const struct regmap_config pf0900_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .volatile_table = &pf0900_volatile_regs,
+ .max_register = PF0900_MAX_REGISTER - 1,
+ .cache_type = REGCACHE_MAPLE,
+};
+
+static uint8_t crc8_j1850(unsigned short addr, unsigned int reg,
+ unsigned int val)
+{
+ uint8_t crcBuf[3];
+ uint8_t t_crc;
+ uint8_t i, j;
+
+ crcBuf[0] = addr;
+ crcBuf[1] = reg;
+ crcBuf[2] = val;
+ t_crc = 0xFF;
+
+ /*
+ * The CRC calculation is based on the standard CRC-8-SAE as
+ * defined in the SAE-J1850 specification with the following
+ * characteristics.
+ * Polynomial = 0x1D
+ * Initial Value = 0xFF
+ * The CRC byte is calculated by shifting 24-bit data through
+ * the CRC polynomial.The 24-bits package is built as follows:
+ * DEVICE_ADDR[b8] + REGISTER_ADDR [b8] +DATA[b8]
+ * The DEVICE_ADDR is calculated as the 7-bit slave address
+ * shifted left one space plus the corresponding read/write bit.
+ * (7Bit Address [b7] << 1 ) + R/W = DEVICE_ADDR[b8]
+ */
+ for (i = 0; i < sizeof(crcBuf); i++) {
+ t_crc ^= crcBuf[i];
+ for (j = 0; j < 8; j++) {
+ if ((t_crc & 0x80) != 0) {
+ t_crc <<= 1;
+ t_crc ^= 0x1D;
+ } else {
+ t_crc <<= 1;
+ }
+ }
+ }
+
+ return t_crc;
+}
+
+static int pf0900_regmap_read(void *context, unsigned int reg,
+ unsigned int *val)
+{
+ struct device *dev = context;
+ struct i2c_client *i2c = to_i2c_client(dev);
+ struct pf0900 *pf0900 = dev_get_drvdata(dev);
+ int ret;
+ u8 crc;
+
+ if (!pf0900 || !pf0900->dev)
+ return -EINVAL;
+
+ if (reg >= PF0900_MAX_REGISTER) {
+ dev_err(pf0900->dev, "Invalid register address: 0x%x\n", reg);
+ return -EINVAL;
+ }
+
+ if (pf0900->crc_en) {
+ ret = i2c_smbus_read_word_data(i2c, reg);
+ if (ret < 0) {
+ dev_err(pf0900->dev, "Read error at reg=0x%x: %d\n", reg, ret);
+ return ret;
+ }
+
+ *val = (u16)ret;
+ crc = crc8_j1850(pf0900->addr << 1 | 0x1, reg, FIELD_GET(GENMASK(7, 0), *val));
+ if (crc != FIELD_GET(GENMASK(15, 8), *val)) {
+ dev_err(pf0900->dev, "Crc check error!\n");
+ return -EINVAL;
+ }
+ *val = FIELD_GET(GENMASK(7, 0), *val);
+ } else {
+ ret = i2c_smbus_read_byte_data(i2c, reg);
+ if (ret < 0) {
+ dev_err(pf0900->dev, "Read error at reg=0x%x: %d\n", reg, ret);
+ return ret;
+ }
+ *val = ret;
+ }
+
+ return 0;
+}
+
+static int pf0900_regmap_write(void *context, unsigned int reg,
+ unsigned int val)
+{
+ struct device *dev = context;
+ struct i2c_client *i2c = to_i2c_client(dev);
+ struct pf0900 *pf0900 = dev_get_drvdata(dev);
+ uint8_t data[2];
+ int ret;
+
+ if (!pf0900 || !pf0900->dev)
+ return -EINVAL;
+
+ if (reg >= PF0900_MAX_REGISTER) {
+ dev_err(pf0900->dev, "Invalid register address: 0x%x\n", reg);
+ return -EINVAL;
+ }
+
+ data[0] = val;
+ if (pf0900->crc_en) {
+ /* Get CRC */
+ data[1] = crc8_j1850(pf0900->addr << 1, reg, data[0]);
+ val = FIELD_PREP(GENMASK(15, 8), data[1]) | data[0];
+ ret = i2c_smbus_write_word_data(i2c, reg, val);
+ } else {
+ ret = i2c_smbus_write_byte_data(i2c, reg, data[0]);
+ }
+
+ if (ret) {
+ dev_err(pf0900->dev, "Write reg=0x%x error!\n", reg);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int pf0900_suspend_enable(struct regulator_dev *rdev)
+{
+ struct pf0900_regulator_desc *rdata = rdev_get_drvdata(rdev);
+ struct regmap *rmap = rdev_get_regmap(rdev);
+
+ return regmap_update_bits(rmap, rdata->desc.enable_reg,
+ rdata->suspend_enable_mask, SW_STBY_MODE_PFM);
+}
+
+static int pf0900_suspend_disable(struct regulator_dev *rdev)
+{
+ struct pf0900_regulator_desc *rdata = rdev_get_drvdata(rdev);
+ struct regmap *rmap = rdev_get_regmap(rdev);
+
+ return regmap_update_bits(rmap, rdata->desc.enable_reg,
+ rdata->suspend_enable_mask, SW_STBY_MODE_OFF);
+}
+
+static int pf0900_set_suspend_voltage(struct regulator_dev *rdev, int uV)
+{
+ struct pf0900_regulator_desc *rdata = rdev_get_drvdata(rdev);
+ struct regmap *rmap = rdev_get_regmap(rdev);
+ int ret;
+
+ if (rdata->suspend_voltage_cache == uV)
+ return 0;
+
+ ret = regulator_map_voltage_iterate(rdev, uV, uV);
+ if (ret < 0) {
+ dev_err(rdev_get_dev(rdev), "failed to map %i uV\n", uV);
+ return ret;
+ }
+
+ dev_dbg(rdev_get_dev(rdev), "uV: %i, reg: 0x%x, msk: 0x%x, val: 0x%x\n",
+ uV, rdata->suspend_voltage_reg, rdata->desc.vsel_mask, ret);
+ ret = regmap_update_bits(rmap, rdata->suspend_voltage_reg,
+ rdata->desc.vsel_mask, ret);
+ if (ret < 0) {
+ dev_err(rdev_get_dev(rdev), "failed to set %i uV\n", uV);
+ return ret;
+ }
+
+ rdata->suspend_voltage_cache = uV;
+
+ return 0;
+}
+
+static const struct regmap_bus pf0900_regmap_bus = {
+ .reg_read = pf0900_regmap_read,
+ .reg_write = pf0900_regmap_write,
+};
+
+static const struct regulator_ops pf0900_avon_regulator_ops = {
+ .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 pf0900_dvs_sw_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,
+ .set_ramp_delay = regulator_set_ramp_delay_regmap,
+ .set_suspend_enable = pf0900_suspend_enable,
+ .set_suspend_disable = pf0900_suspend_disable,
+ .set_suspend_voltage = pf0900_set_suspend_voltage,
+};
+
+static const struct regulator_ops pf0900_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 = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+};
+
+/*
+ * SW1/2/3/4/5
+ * SW1_DVS[1:0] SW1 DVS ramp rate setting
+ * 00: 15.6mV/8usec
+ * 01: 15.6mV/4usec
+ * 10: 15.6mV/2usec
+ * 11: 15.6mV/1usec
+ */
+static const unsigned int pf0900_dvs_sw_ramp_table[] = {
+ 1950, 3900, 7800, 15600
+};
+
+/* VAON 1.8V, 3.0V, or 3.3V */
+static const int pf0900_vaon_voltages[] = {
+ 0, 1800000, 3000000, 3300000,
+};
+
+/*
+ * SW1 0.5V to 3.3V
+ * 0.5V to 1.35V (6.25mV step)
+ * 1.8V to 2.5V (125mV step)
+ * 2.8V to 3.3V (250mV step)
+ */
+static const struct linear_range pf0900_dvs_sw1_volts[] = {
+ REGULATOR_LINEAR_RANGE(0, 0x00, 0x08, 0),
+ REGULATOR_LINEAR_RANGE(500000, 0x09, 0x91, 6250),
+ REGULATOR_LINEAR_RANGE(0, 0x92, 0x9E, 0),
+ REGULATOR_LINEAR_RANGE(1500000, 0x9F, 0x9F, 0),
+ REGULATOR_LINEAR_RANGE(1800000, 0xA0, 0xD8, 12500),
+ REGULATOR_LINEAR_RANGE(0, 0xD9, 0xDF, 0),
+ REGULATOR_LINEAR_RANGE(2800000, 0xE0, 0xF4, 25000),
+ REGULATOR_LINEAR_RANGE(0, 0xF5, 0xFF, 0),
+};
+
+/*
+ * SW2/3/4/5 0.3V to 3.3V
+ * 0.45V to 1.35V (6.25mV step)
+ * 1.8V to 2.5V (125mV step)
+ * 2.8V to 3.3V (250mV step)
+ */
+static const struct linear_range pf0900_dvs_sw2345_volts[] = {
+ REGULATOR_LINEAR_RANGE(300000, 0x00, 0x00, 0),
+ REGULATOR_LINEAR_RANGE(450000, 0x01, 0x91, 6250),
+ REGULATOR_LINEAR_RANGE(0, 0x92, 0x9E, 0),
+ REGULATOR_LINEAR_RANGE(1500000, 0x9F, 0x9F, 0),
+ REGULATOR_LINEAR_RANGE(1800000, 0xA0, 0xD8, 12500),
+ REGULATOR_LINEAR_RANGE(0, 0xD9, 0xDF, 0),
+ REGULATOR_LINEAR_RANGE(2800000, 0xE0, 0xF4, 25000),
+ REGULATOR_LINEAR_RANGE(0, 0xF5, 0xFF, 0),
+};
+
+/*
+ * LDO1
+ * 0.75V to 3.3V
+ */
+static const struct linear_range pf0900_ldo1_volts[] = {
+ REGULATOR_LINEAR_RANGE(750000, 0x00, 0x0F, 50000),
+ REGULATOR_LINEAR_RANGE(1800000, 0x10, 0x1F, 100000),
+};
+
+/*
+ * LDO2/3
+ * 0.65V to 3.3V (50mV step)
+ */
+static const struct linear_range pf0900_ldo23_volts[] = {
+ REGULATOR_LINEAR_RANGE(650000, 0x00, 0x0D, 50000),
+ REGULATOR_LINEAR_RANGE(1400000, 0x0E, 0x0F, 100000),
+ REGULATOR_LINEAR_RANGE(1800000, 0x10, 0x1F, 100000),
+};
+
+static const struct pf0900_regulator_desc pf0900_regulators[] = {
+ {
+ .desc = {
+ .name = "sw1",
+ .of_match = of_match_ptr("sw1"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = PF0900_SW1,
+ .ops = &pf0900_dvs_sw_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .n_voltages = PF0900_SW_VOLTAGE_NUM,
+ .linear_ranges = pf0900_dvs_sw1_volts,
+ .n_linear_ranges = ARRAY_SIZE(pf0900_dvs_sw1_volts),
+ .vsel_reg = PF0900_REG_SW1_VRUN,
+ .vsel_mask = PF0900_SW_VOL_MASK,
+ .enable_reg = PF0900_REG_SW1_MODE,
+ .enable_mask = SW_RUN_MODE_MASK,
+ .enable_val = SW_RUN_MODE_PWM,
+ .ramp_reg = PF0900_REG_SW1_CFG1,
+ .ramp_mask = PF0900_SW_DVS_MASK,
+ .ramp_delay_table = pf0900_dvs_sw_ramp_table,
+ .n_ramp_values = ARRAY_SIZE(pf0900_dvs_sw_ramp_table),
+ .owner = THIS_MODULE,
+ },
+ .suspend_enable_mask = SW_STBY_MODE_MASK,
+ .suspend_voltage_reg = PF0900_REG_SW1_VSTBY,
+ },
+ {
+ .desc = {
+ .name = "sw2",
+ .of_match = of_match_ptr("sw2"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = PF0900_SW2,
+ .ops = &pf0900_dvs_sw_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .n_voltages = PF0900_SW_VOLTAGE_NUM,
+ .linear_ranges = pf0900_dvs_sw2345_volts,
+ .n_linear_ranges = ARRAY_SIZE(pf0900_dvs_sw2345_volts),
+ .vsel_reg = PF0900_REG_SW2_VRUN,
+ .vsel_mask = PF0900_SW_VOL_MASK,
+ .enable_reg = PF0900_REG_SW2_MODE,
+ .enable_mask = SW_RUN_MODE_MASK,
+ .enable_val = SW_RUN_MODE_PWM,
+ .ramp_reg = PF0900_REG_SW2_CFG1,
+ .ramp_mask = PF0900_SW_DVS_MASK,
+ .ramp_delay_table = pf0900_dvs_sw_ramp_table,
+ .n_ramp_values = ARRAY_SIZE(pf0900_dvs_sw_ramp_table),
+ .owner = THIS_MODULE,
+ },
+ .suspend_enable_mask = SW_STBY_MODE_MASK,
+ .suspend_voltage_reg = PF0900_REG_SW2_VSTBY,
+ },
+ {
+ .desc = {
+ .name = "sw3",
+ .of_match = of_match_ptr("sw3"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = PF0900_SW3,
+ .ops = &pf0900_dvs_sw_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .n_voltages = PF0900_SW_VOLTAGE_NUM,
+ .linear_ranges = pf0900_dvs_sw2345_volts,
+ .n_linear_ranges = ARRAY_SIZE(pf0900_dvs_sw2345_volts),
+ .vsel_reg = PF0900_REG_SW3_VRUN,
+ .vsel_mask = PF0900_SW_VOL_MASK,
+ .enable_reg = PF0900_REG_SW3_MODE,
+ .enable_mask = SW_RUN_MODE_MASK,
+ .enable_val = SW_RUN_MODE_PWM,
+ .ramp_reg = PF0900_REG_SW3_CFG1,
+ .ramp_mask = PF0900_SW_DVS_MASK,
+ .ramp_delay_table = pf0900_dvs_sw_ramp_table,
+ .n_ramp_values = ARRAY_SIZE(pf0900_dvs_sw_ramp_table),
+ .owner = THIS_MODULE,
+ },
+ .suspend_enable_mask = SW_STBY_MODE_MASK,
+ .suspend_voltage_reg = PF0900_REG_SW3_VSTBY,
+ },
+ {
+ .desc = {
+ .name = "sw4",
+ .of_match = of_match_ptr("sw4"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = PF0900_SW4,
+ .ops = &pf0900_dvs_sw_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .n_voltages = PF0900_SW_VOLTAGE_NUM,
+ .linear_ranges = pf0900_dvs_sw2345_volts,
+ .n_linear_ranges = ARRAY_SIZE(pf0900_dvs_sw2345_volts),
+ .vsel_reg = PF0900_REG_SW4_VRUN,
+ .vsel_mask = PF0900_SW_VOL_MASK,
+ .enable_reg = PF0900_REG_SW4_MODE,
+ .enable_mask = SW_RUN_MODE_MASK,
+ .enable_val = SW_RUN_MODE_PWM,
+ .ramp_reg = PF0900_REG_SW4_CFG1,
+ .ramp_mask = PF0900_SW_DVS_MASK,
+ .ramp_delay_table = pf0900_dvs_sw_ramp_table,
+ .n_ramp_values = ARRAY_SIZE(pf0900_dvs_sw_ramp_table),
+ .owner = THIS_MODULE,
+ },
+ .suspend_enable_mask = SW_STBY_MODE_MASK,
+ .suspend_voltage_reg = PF0900_REG_SW4_VSTBY,
+ },
+ {
+ .desc = {
+ .name = "sw5",
+ .of_match = of_match_ptr("sw5"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = PF0900_SW5,
+ .ops = &pf0900_dvs_sw_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .n_voltages = PF0900_SW_VOLTAGE_NUM,
+ .linear_ranges = pf0900_dvs_sw2345_volts,
+ .n_linear_ranges = ARRAY_SIZE(pf0900_dvs_sw2345_volts),
+ .vsel_reg = PF0900_REG_SW5_VRUN,
+ .vsel_mask = PF0900_SW_VOL_MASK,
+ .enable_reg = PF0900_REG_SW5_MODE,
+ .enable_mask = SW_RUN_MODE_MASK,
+ .enable_val = SW_RUN_MODE_PWM,
+ .ramp_reg = PF0900_REG_SW5_CFG1,
+ .ramp_mask = PF0900_SW_DVS_MASK,
+ .ramp_delay_table = pf0900_dvs_sw_ramp_table,
+ .n_ramp_values = ARRAY_SIZE(pf0900_dvs_sw_ramp_table),
+ .owner = THIS_MODULE,
+ },
+ .suspend_enable_mask = SW_STBY_MODE_MASK,
+ .suspend_voltage_reg = PF0900_REG_SW5_VSTBY,
+ },
+ {
+ .desc = {
+ .name = "ldo1",
+ .of_match = of_match_ptr("ldo1"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = PF0900_LDO1,
+ .ops = &pf0900_ldo_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .n_voltages = PF0900_LDO_VOLTAGE_NUM,
+ .linear_ranges = pf0900_ldo1_volts,
+ .n_linear_ranges = ARRAY_SIZE(pf0900_ldo1_volts),
+ .vsel_reg = PF0900_REG_LDO1_RUN,
+ .vsel_mask = VLDO_RUN_MASK,
+ .enable_reg = PF0900_REG_LDO1_RUN,
+ .enable_mask = LDO_RUN_EN_MASK,
+ .owner = THIS_MODULE,
+ },
+ },
+ {
+ .desc = {
+ .name = "ldo2",
+ .of_match = of_match_ptr("ldo2"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = PF0900_LDO2,
+ .ops = &pf0900_ldo_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .n_voltages = PF0900_LDO_VOLTAGE_NUM,
+ .linear_ranges = pf0900_ldo23_volts,
+ .n_linear_ranges = ARRAY_SIZE(pf0900_ldo23_volts),
+ .vsel_reg = PF0900_REG_LDO2_RUN,
+ .vsel_mask = VLDO_RUN_MASK,
+ .enable_reg = PF0900_REG_LDO2_RUN,
+ .enable_mask = LDO_RUN_EN_MASK,
+ .owner = THIS_MODULE,
+ },
+ },
+ {
+ .desc = {
+ .name = "ldo3",
+ .of_match = of_match_ptr("ldo3"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = PF0900_LDO3,
+ .ops = &pf0900_ldo_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .n_voltages = PF0900_LDO_VOLTAGE_NUM,
+ .linear_ranges = pf0900_ldo23_volts,
+ .n_linear_ranges = ARRAY_SIZE(pf0900_ldo23_volts),
+ .vsel_reg = PF0900_REG_LDO3_RUN,
+ .vsel_mask = VLDO_RUN_MASK,
+ .enable_reg = PF0900_REG_LDO3_RUN,
+ .enable_mask = LDO_RUN_EN_MASK,
+ .owner = THIS_MODULE,
+ },
+ },
+ {
+ .desc = {
+ .name = "vaon",
+ .of_match = of_match_ptr("vaon"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = PF0900_VAON,
+ .ops = &pf0900_avon_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .n_voltages = PF0900_VAON_VOLTAGE_NUM,
+ .volt_table = pf0900_vaon_voltages,
+ .enable_reg = PF0900_REG_VAON_CFG1,
+ .enable_mask = PF0900_VAON_MASK,
+ .enable_val = PF0900_VAON_1P8V,
+ .vsel_reg = PF0900_REG_VAON_CFG1,
+ .vsel_mask = PF0900_VAON_MASK,
+ .owner = THIS_MODULE,
+ },
+ },
+};
+
+struct pf0900_regulator_irq regu_irqs[] = {
+ PF0900_REGU_IRQ(PF0900_REG_SW_ILIM_INT, PF0900_SW, REGULATOR_ERROR_OVER_CURRENT_WARN),
+ PF0900_REGU_IRQ(PF0900_REG_LDO_ILIM_INT, PF0900_LDO, REGULATOR_ERROR_OVER_CURRENT_WARN),
+ PF0900_REGU_IRQ(PF0900_REG_SW_UV_INT, PF0900_SW, REGULATOR_ERROR_UNDER_VOLTAGE_WARN),
+ PF0900_REGU_IRQ(PF0900_REG_LDO_UV_INT, PF0900_LDO, REGULATOR_ERROR_UNDER_VOLTAGE_WARN),
+ PF0900_REGU_IRQ(PF0900_REG_SW_OV_INT, PF0900_SW, REGULATOR_ERROR_OVER_VOLTAGE_WARN),
+ PF0900_REGU_IRQ(PF0900_REG_LDO_OV_INT, PF0900_LDO, REGULATOR_ERROR_OVER_VOLTAGE_WARN),
+};
+
+static irqreturn_t pf0900_irq_handler(int irq, void *data)
+{
+ unsigned int val, regu, i, index;
+ struct pf0900 *pf0900 = data;
+ int ret;
+
+ for (i = 0; i < ARRAY_SIZE(regu_irqs); i++) {
+ ret = regmap_read(pf0900->regmap, regu_irqs[i].reg, &val);
+ if (ret < 0) {
+ dev_err(pf0900->dev, "Failed to read %d\n", ret);
+ return IRQ_NONE;
+ }
+ if (val) {
+ ret = regmap_write_bits(pf0900->regmap, regu_irqs[i].reg, val, val);
+ if (ret < 0) {
+ dev_err(pf0900->dev, "Failed to update %d\n", ret);
+ return IRQ_NONE;
+ }
+
+ if (regu_irqs[i].type == PF0900_SW) {
+ for (index = 0; index < REGU_SW_CNT; index++) {
+ if (val & BIT(index)) {
+ regu = (enum pf0900_regulators)index;
+ regulator_notifier_call_chain(pf0900->rdevs[regu],
+ regu_irqs[i].event,
+ NULL);
+ }
+ }
+ } else if (regu_irqs[i].type == PF0900_LDO) {
+ for (index = 0; index < REGU_LDO_VAON_CNT; index++) {
+ if (val & BIT(index)) {
+ regu = (enum pf0900_regulators)index + PF0900_LDO1;
+ regulator_notifier_call_chain(pf0900->rdevs[regu],
+ regu_irqs[i].event,
+ NULL);
+ }
+ }
+ }
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int pf0900_i2c_probe(struct i2c_client *i2c)
+{
+ const struct pf0900_regulator_desc *regulator_desc;
+ const struct pf0900_drvdata *drvdata = NULL;
+ struct device_node *np = i2c->dev.of_node;
+ unsigned int device_id, device_fam, i;
+ struct regulator_config config = { };
+ struct pf0900 *pf0900;
+ int ret;
+
+ if (!i2c->irq)
+ return dev_err_probe(&i2c->dev, -EINVAL, "No IRQ configured?\n");
+
+ pf0900 = devm_kzalloc(&i2c->dev, sizeof(struct pf0900), GFP_KERNEL);
+ if (!pf0900)
+ return -ENOMEM;
+
+ drvdata = device_get_match_data(&i2c->dev);
+ if (!drvdata)
+ return dev_err_probe(&i2c->dev, -EINVAL, "unable to find driver data\n");
+
+ regulator_desc = drvdata->desc;
+ pf0900->drvdata = drvdata;
+ pf0900->crc_en = of_property_read_bool(np, "nxp,i2c-crc-enable");
+ pf0900->irq = i2c->irq;
+ pf0900->dev = &i2c->dev;
+ pf0900->addr = i2c->addr;
+
+ dev_set_drvdata(&i2c->dev, pf0900);
+
+ pf0900->regmap = devm_regmap_init(&i2c->dev, &pf0900_regmap_bus, &i2c->dev,
+ &pf0900_regmap_config);
+ if (IS_ERR(pf0900->regmap))
+ return dev_err_probe(&i2c->dev, PTR_ERR(pf0900->regmap),
+ "regmap initialization failed\n");
+ ret = regmap_read(pf0900->regmap, PF0900_REG_DEV_ID, &device_id);
+ if (ret)
+ return dev_err_probe(&i2c->dev, ret, "Read device id error\n");
+
+ ret = regmap_read(pf0900->regmap, PF0900_REG_DEV_FAM, &device_fam);
+ if (ret)
+ return dev_err_probe(&i2c->dev, ret, "Read device fam error\n");
+
+ /* Check your board and dts for match the right pmic */
+ if (device_fam == 0x09 && (device_id & 0x1F) != 0x0)
+ return dev_err_probe(&i2c->dev, -EINVAL, "Device id(%x) mismatched\n",
+ device_id >> 4);
+
+ for (i = 0; i < drvdata->rcnt; i++) {
+ const struct regulator_desc *desc;
+ const struct pf0900_regulator_desc *r;
+
+ r = &regulator_desc[i];
+ desc = &r->desc;
+ config.regmap = pf0900->regmap;
+ config.driver_data = (void *)r;
+ config.dev = pf0900->dev;
+
+ pf0900->rdevs[i] = devm_regulator_register(pf0900->dev, desc, &config);
+ if (IS_ERR(pf0900->rdevs[i]))
+ return dev_err_probe(pf0900->dev, PTR_ERR(pf0900->rdevs[i]),
+ "Failed to register regulator(%s)\n", desc->name);
+ }
+
+ ret = devm_request_threaded_irq(pf0900->dev, pf0900->irq, NULL,
+ pf0900_irq_handler,
+ (IRQF_TRIGGER_FALLING | IRQF_ONESHOT),
+ "pf0900-irq", pf0900);
+
+ if (ret != 0)
+ return dev_err_probe(pf0900->dev, ret, "Failed to request IRQ: %d\n",
+ pf0900->irq);
+ /*
+ * The PWRUP_M is unmasked by default. When the device enter in RUN state,
+ * it will assert the PWRUP_I interrupt and assert the INTB pin to inform
+ * the MCU that it has finished the power up sequence properly.
+ */
+ ret = regmap_write_bits(pf0900->regmap, PF0900_REG_STATUS1_INT, PF0900_IRQ_PWRUP,
+ PF0900_IRQ_PWRUP);
+ if (ret)
+ return dev_err_probe(&i2c->dev, ret, "Clean PWRUP_I error\n");
+
+ /* mask interrupt PWRUP */
+ ret = regmap_update_bits(pf0900->regmap, PF0900_REG_STATUS1_MSK, PF0900_IRQ_PWRUP,
+ PF0900_IRQ_PWRUP);
+ if (ret)
+ return dev_err_probe(&i2c->dev, ret, "Unmask irq error\n");
+
+ ret = regmap_update_bits(pf0900->regmap, PF0900_REG_SW_ILIM_MSK, PF0900_IRQ_SW1_IL |
+ PF0900_IRQ_SW2_IL | PF0900_IRQ_SW3_IL | PF0900_IRQ_SW4_IL |
+ PF0900_IRQ_SW5_IL, 0);
+ if (ret)
+ return dev_err_probe(&i2c->dev, ret, "Unmask irq error\n");
+
+ ret = regmap_update_bits(pf0900->regmap, PF0900_REG_SW_UV_MSK, PF0900_IRQ_SW1_UV |
+ PF0900_IRQ_SW2_UV | PF0900_IRQ_SW3_UV | PF0900_IRQ_SW4_UV |
+ PF0900_IRQ_SW5_UV, 0);
+ if (ret)
+ return dev_err_probe(&i2c->dev, ret, "Unmask irq error\n");
+
+ ret = regmap_update_bits(pf0900->regmap, PF0900_REG_SW_OV_MSK, PF0900_IRQ_SW1_OV |
+ PF0900_IRQ_SW2_OV | PF0900_IRQ_SW3_OV | PF0900_IRQ_SW4_OV |
+ PF0900_IRQ_SW5_OV, 0);
+ if (ret)
+ return dev_err_probe(&i2c->dev, ret, "Unmask irq error\n");
+
+ ret = regmap_update_bits(pf0900->regmap, PF0900_REG_LDO_ILIM_MSK, PF0900_IRQ_LDO1_IL |
+ PF0900_IRQ_LDO2_IL | PF0900_IRQ_LDO3_IL, 0);
+ if (ret)
+ return dev_err_probe(&i2c->dev, ret, "Unmask irq error\n");
+
+ ret = regmap_update_bits(pf0900->regmap, PF0900_REG_LDO_UV_MSK, PF0900_IRQ_LDO1_UV |
+ PF0900_IRQ_LDO2_UV | PF0900_IRQ_LDO3_UV | PF0900_IRQ_VAON_UV, 0);
+ if (ret)
+ return dev_err_probe(&i2c->dev, ret, "Unmask irq error\n");
+
+ ret = regmap_update_bits(pf0900->regmap, PF0900_REG_LDO_OV_MSK, PF0900_IRQ_LDO1_OV |
+ PF0900_IRQ_LDO2_OV | PF0900_IRQ_LDO3_OV | PF0900_IRQ_VAON_OV, 0);
+ if (ret)
+ return dev_err_probe(&i2c->dev, ret, "Unmask irq error\n");
+
+ return 0;
+}
+
+static struct pf0900_drvdata pf0900_drvdata = {
+ .desc = pf0900_regulators,
+ .rcnt = ARRAY_SIZE(pf0900_regulators),
+};
+
+static const struct of_device_id pf0900_of_match[] = {
+ { .compatible = "nxp,pf0900", .data = &pf0900_drvdata},
+ { }
+};
+
+MODULE_DEVICE_TABLE(of, pf0900_of_match);
+
+static struct i2c_driver pf0900_i2c_driver = {
+ .driver = {
+ .name = "nxp-pf0900",
+ .of_match_table = pf0900_of_match,
+ },
+ .probe = pf0900_i2c_probe,
+};
+
+module_i2c_driver(pf0900_i2c_driver);
+
+MODULE_AUTHOR("Joy Zou <joy.zou@nxp.com>");
+MODULE_DESCRIPTION("NXP PF0900 Power Management IC driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/regulator/pf530x-regulator.c b/drivers/regulator/pf530x-regulator.c
new file mode 100644
index 000000000000..f789c4b6a499
--- /dev/null
+++ b/drivers/regulator/pf530x-regulator.c
@@ -0,0 +1,375 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+// documentation of this device is available at
+// https://www.nxp.com/docs/en/data-sheet/PF5300.pdf
+
+#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/consumer.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+
+/* registers */
+#define PF530X_DEVICEID 0x00
+#define PF530X_REV 0x01
+#define PF530X_EMREV 0x02
+#define PF530X_PROGID 0x03
+#define PF530X_CONFIG1 0x04
+#define PF530X_INT_STATUS1 0x05
+#define PF530X_INT_SENSE1 0x06
+#define PF530X_INT_STATUS2 0x07
+#define PF530X_INT_SENSE2 0x08
+#define PF530X_BIST_STAT1 0x09
+#define PF530X_BIST_CTRL 0x0a
+#define PF530X_STATE 0x0b
+#define PF530X_STATE_CTRL 0x0c
+#define PF530X_SW1_VOLT 0x0d
+#define PF530X_SW1_STBY_VOLT 0x0e
+#define PF530X_SW1_CTRL1 0x0f
+#define PF530X_SW1_CTRL2 0x10
+#define PF530X_CLK_CTRL 0x11
+#define PF530X_SEQ_CTRL1 0x12
+#define PF530X_SEQ_CTRL2 0x13
+#define PF530X_RANDOM_CHK 0x14
+#define PF530X_RANDOM_GEN 0x15
+#define PF530X_WD_CTRL1 0x16
+#define PF530X_WD_SEED 0x17
+#define PF530X_WD_ANSWER 0x18
+#define PF530X_FLT_CNT1 0x19
+#define PF530X_FLT_CNT2 0x1a
+#define PF530X_OTP_MODE 0x2f
+
+enum pf530x_states {
+ PF530X_STATE_POF,
+ PF530X_STATE_FUSE_LOAD,
+ PF530X_STATE_LP_OFF,
+ PF530X_STATE_SELF_TEST,
+ PF530X_STATE_POWER_UP,
+ PF530X_STATE_INIT,
+ PF530X_STATE_IO_RELEASE,
+ PF530X_STATE_RUN,
+ PF530X_STATE_STANDBY,
+ PF530X_STATE_FAULT,
+ PF530X_STATE_FAILSAFE,
+ PF530X_STATE_POWER_DOWN,
+ PF530X_STATE_2MS_SELFTEST_RETRY,
+ PF530X_STATE_OFF_DLY,
+};
+
+#define PF530_FAM 0x50
+enum pf530x_devid {
+ PF5300 = 0x3,
+ PF5301 = 0x4,
+ PF5302 = 0x5,
+};
+
+#define PF530x_FAM 0x50
+#define PF530x_DEVICE_FAM_MASK GENMASK(7, 4)
+#define PF530x_DEVICE_ID_MASK GENMASK(3, 0)
+
+#define PF530x_STATE_MASK GENMASK(3, 0)
+#define PF530x_STATE_RUN 0x07
+#define PF530x_STATE_STANDBY 0x08
+#define PF530x_STATE_LP_OFF 0x02
+
+#define PF530X_OTP_STBY_MODE GENMASK(3, 2)
+#define PF530X_OTP_RUN_MODE GENMASK(1, 0)
+
+#define PF530X_INT_STATUS_OV BIT(1)
+#define PF530X_INT_STATUS_UV BIT(2)
+#define PF530X_INT_STATUS_ILIM BIT(3)
+
+#define SW1_ILIM_S BIT(0)
+#define VMON_UV_S BIT(1)
+#define VMON_OV_S BIT(2)
+#define VIN_OVLO_S BIT(3)
+#define BG_ERR_S BIT(6)
+
+#define THERM_155_S BIT(3)
+#define THERM_140_S BIT(2)
+#define THERM_125_S BIT(1)
+#define THERM_110_S BIT(0)
+
+struct pf530x_chip {
+ struct regmap *regmap;
+ struct device *dev;
+};
+
+static const struct regmap_config pf530x_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = PF530X_OTP_MODE,
+ .cache_type = REGCACHE_MAPLE,
+};
+
+static int pf530x_get_status(struct regulator_dev *rdev)
+{
+ unsigned int state;
+ int ret;
+
+ ret = regmap_read(rdev->regmap, PF530X_INT_SENSE1, &state);
+ if (ret != 0)
+ return ret;
+
+ if ((state & (BG_ERR_S | SW1_ILIM_S | VMON_UV_S | VMON_OV_S | VIN_OVLO_S))
+ != 0)
+ return REGULATOR_STATUS_ERROR;
+
+ // no errors, check if what non-error state we're in
+ ret = regmap_read(rdev->regmap, PF530X_STATE, &state);
+ if (ret != 0)
+ return ret;
+
+ state &= PF530x_STATE_MASK;
+
+ switch (state) {
+ case PF530x_STATE_RUN:
+ ret = REGULATOR_STATUS_NORMAL;
+ break;
+ case PF530x_STATE_STANDBY:
+ ret = REGULATOR_STATUS_STANDBY;
+ break;
+ case PF530x_STATE_LP_OFF:
+ ret = REGULATOR_STATUS_OFF;
+ break;
+ default:
+ ret = REGULATOR_STATUS_ERROR;
+ break;
+ }
+ return ret;
+}
+
+static int pf530x_get_error_flags(struct regulator_dev *rdev, unsigned int *flags)
+{
+ unsigned int status;
+ int ret;
+
+ ret = regmap_read(rdev->regmap, PF530X_INT_STATUS1, &status);
+
+ if (ret != 0)
+ return ret;
+
+ *flags = 0;
+
+ if (status & PF530X_INT_STATUS_OV)
+ *flags |= REGULATOR_ERROR_OVER_VOLTAGE_WARN;
+
+ if (status & PF530X_INT_STATUS_UV)
+ *flags |= REGULATOR_ERROR_UNDER_VOLTAGE;
+
+ if (status & PF530X_INT_STATUS_ILIM)
+ *flags |= REGULATOR_ERROR_OVER_CURRENT;
+
+ ret = regmap_read(rdev->regmap, PF530X_INT_SENSE2, &status);
+
+ if (ret != 0)
+ return ret;
+
+ if ((status & (THERM_155_S |
+ THERM_140_S |
+ THERM_125_S |
+ THERM_110_S)) != 0)
+ *flags |= REGULATOR_ERROR_OVER_TEMP_WARN;
+
+ return 0;
+}
+
+static const struct regulator_ops pf530x_regulator_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .map_voltage = regulator_map_voltage_linear_range,
+ .list_voltage = regulator_list_voltage_linear_range,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .get_status = pf530x_get_status,
+ .get_error_flags = pf530x_get_error_flags,
+ .set_bypass = regulator_set_bypass_regmap,
+ .get_bypass = regulator_get_bypass_regmap,
+};
+
+static const struct linear_range vrange = REGULATOR_LINEAR_RANGE(500000, 0, 140, 5000);
+
+static const struct regulator_desc pf530x_reg_desc = {
+ .name = "SW1",
+ .ops = &pf530x_regulator_ops,
+ .linear_ranges = &vrange,
+ .n_linear_ranges = 1,
+ .type = REGULATOR_VOLTAGE,
+ .id = 0,
+ .owner = THIS_MODULE,
+ .vsel_reg = PF530X_SW1_VOLT,
+ .vsel_mask = 0xFF,
+ .bypass_reg = PF530X_SW1_CTRL2,
+ .bypass_mask = 0x07,
+ .bypass_val_on = 0x07,
+ .bypass_val_off = 0x00,
+ .enable_reg = PF530X_SW1_CTRL1,
+ .enable_mask = GENMASK(5, 2),
+ .enable_val = GENMASK(5, 2),
+ .disable_val = 0,
+};
+
+static int pf530x_identify(struct pf530x_chip *chip)
+{
+ unsigned int value;
+ u8 dev_fam, dev_id, full_layer_rev, metal_layer_rev, prog_idh, prog_idl, emrev;
+ const char *name = NULL;
+ int ret;
+
+ ret = regmap_read(chip->regmap, PF530X_DEVICEID, &value);
+ if (ret) {
+ dev_err(chip->dev, "failed to read chip family\n");
+ return ret;
+ }
+
+ dev_fam = value & PF530x_DEVICE_FAM_MASK;
+ switch (dev_fam) {
+ case PF530x_FAM:
+ break;
+ default:
+ dev_err(chip->dev,
+ "Chip 0x%x is not from PF530X family\n", dev_fam);
+ return ret;
+ }
+
+ dev_id = value & PF530x_DEVICE_ID_MASK;
+ switch (dev_id) {
+ case PF5300:
+ name = "PF5300";
+ break;
+ case PF5301:
+ name = "PF5301";
+ break;
+ case PF5302:
+ name = "PF5302";
+ break;
+ default:
+ dev_err(chip->dev, "Unknown pf530x device id 0x%x\n", dev_id);
+ return -ENODEV;
+ }
+
+ ret = regmap_read(chip->regmap, PF530X_REV, &value);
+ if (ret) {
+ dev_err(chip->dev, "failed to read chip rev\n");
+ return ret;
+ }
+
+ full_layer_rev = ((value & 0xF0) == 0) ? '0' : ((((value & 0xF0) >> 4) - 1) + 'A');
+ metal_layer_rev = value & 0xF;
+
+ ret = regmap_read(chip->regmap, PF530X_EMREV, &value);
+ if (ret) {
+ dev_err(chip->dev, "failed to read chip emrev register\n");
+ return ret;
+ }
+
+ prog_idh = (value >> 4) + 'A';
+ // prog_idh skips 'O', per page 96 of the datasheet
+ if (prog_idh >= 'O')
+ prog_idh += 1;
+
+ emrev = value & 0x7;
+
+ ret = regmap_read(chip->regmap, PF530X_PROGID, &value);
+ if (ret) {
+ dev_err(chip->dev, "failed to read chip progid register\n");
+ return ret;
+ }
+
+ if (value >= 0x22) {
+ dev_err(chip->dev, "invalid value for progid register\n");
+ return -ENODEV;
+ } else if (value < 10) {
+ prog_idl = value + '0';
+ } else {
+ prog_idl = (value - 10) + 'A';
+ // prog_idh skips 'O', per page 97 of the datasheet
+ if (prog_idl >= 'O')
+ prog_idl += 1;
+ }
+
+ dev_info(chip->dev, "%s Regulator found (Rev %c%d ProgID %c%c EMREV %x).\n",
+ name, full_layer_rev, metal_layer_rev, prog_idh, prog_idl, emrev);
+
+ return 0;
+}
+
+static int pf530x_i2c_probe(struct i2c_client *client)
+{
+ struct regulator_config config = { NULL, };
+ struct pf530x_chip *chip;
+ int ret;
+ struct regulator_dev *rdev;
+ struct regulator_init_data *init_data;
+
+ 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, &pf530x_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 = pf530x_identify(chip);
+ if (ret)
+ return ret;
+
+ init_data = of_get_regulator_init_data(chip->dev, chip->dev->of_node, &pf530x_reg_desc);
+ if (!init_data)
+ return -ENODATA;
+
+ config.dev = chip->dev;
+ config.of_node = chip->dev->of_node;
+ config.regmap = chip->regmap;
+ config.init_data = init_data;
+
+ // the config parameter gets copied, it's ok to pass a pointer on the stack here
+ rdev = devm_regulator_register(&client->dev, &pf530x_reg_desc, &config);
+ if (IS_ERR(rdev)) {
+ dev_err(&client->dev, "failed to register %s regulator\n", pf530x_reg_desc.name);
+ return PTR_ERR(rdev);
+ }
+
+ return 0;
+}
+
+static const struct of_device_id pf530x_dt_ids[] = {
+ { .compatible = "nxp,pf5300",},
+ { }
+};
+MODULE_DEVICE_TABLE(of, pf530x_dt_ids);
+
+static const struct i2c_device_id pf530x_i2c_id[] = {
+ { "pf5300", 0 },
+ { "pf5301", 0 },
+ { "pf5302", 0 },
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, pf530x_i2c_id);
+
+static struct i2c_driver pf530x_regulator_driver = {
+ .id_table = pf530x_i2c_id,
+ .driver = {
+ .name = "pf530x",
+ .of_match_table = pf530x_dt_ids,
+ },
+ .probe = pf530x_i2c_probe,
+};
+module_i2c_driver(pf530x_regulator_driver);
+
+MODULE_AUTHOR("Woodrow Douglass <wdouglass@carnegierobotics.com>");
+MODULE_DESCRIPTION("Regulator Driver for NXP's PF5300/PF5301/PF5302 PMIC");
+MODULE_LICENSE("GPL");
diff --git a/drivers/regulator/qcom-pm8008-regulator.c b/drivers/regulator/qcom-pm8008-regulator.c
index da017c1969d0..90c78ee1c37b 100644
--- a/drivers/regulator/qcom-pm8008-regulator.c
+++ b/drivers/regulator/qcom-pm8008-regulator.c
@@ -96,7 +96,7 @@ static int pm8008_regulator_get_voltage_sel(struct regulator_dev *rdev)
uV = le16_to_cpu(val) * 1000;
- return (uV - preg->desc.min_uV) / preg->desc.uV_step;
+ return regulator_map_voltage_linear_range(rdev, uV, INT_MAX);
}
static const struct regulator_ops pm8008_regulator_ops = {
diff --git a/drivers/regulator/qcom-refgen-regulator.c b/drivers/regulator/qcom-refgen-regulator.c
index cfa72ce85bc8..299ac3c8c3bc 100644
--- a/drivers/regulator/qcom-refgen-regulator.c
+++ b/drivers/regulator/qcom-refgen-regulator.c
@@ -94,7 +94,6 @@ static const struct regmap_config qcom_refgen_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
- .fast_io = true,
};
static int qcom_refgen_probe(struct platform_device *pdev)
diff --git a/drivers/regulator/rpi-panel-attiny-regulator.c b/drivers/regulator/rpi-panel-attiny-regulator.c
index 58dbf8bffa5d..3020839b9ef1 100644
--- a/drivers/regulator/rpi-panel-attiny-regulator.c
+++ b/drivers/regulator/rpi-panel-attiny-regulator.c
@@ -351,7 +351,7 @@ static int attiny_i2c_probe(struct i2c_client *i2c)
state->gc.base = -1;
state->gc.ngpio = NUM_GPIO;
- state->gc.set_rv = attiny_gpio_set;
+ state->gc.set = attiny_gpio_set;
state->gc.get_direction = attiny_gpio_get_direction;
state->gc.can_sleep = true;
diff --git a/drivers/regulator/rt5133-regulator.c b/drivers/regulator/rt5133-regulator.c
new file mode 100644
index 000000000000..129b1f13c880
--- /dev/null
+++ b/drivers/regulator/rt5133-regulator.c
@@ -0,0 +1,642 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2025 Richtek Technology Corp.
+// Author: ChiYuan Huang <cy_huang@richtek.com>
+// Author: ShihChia Chang <jeff_chang@richtek.com>
+
+#include <linux/crc8.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/gpio/driver.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <linux/regulator/driver.h>
+
+#define RT5133_REG_CHIP_INFO 0x00
+#define RT5133_REG_RST_CTRL 0x06
+#define RT5133_REG_BASE_CTRL 0x09
+#define RT5133_REG_GPIO_CTRL 0x0B
+#define RT5133_REG_BASE_EVT 0x10
+#define RT5133_REG_LDO_PGB_STAT 0x15
+#define RT5133_REG_BASE_MASK 0x16
+#define RT5133_REG_LDO_SHDN 0x19
+#define RT5133_REG_LDO_ON 0x1A
+#define RT5133_REG_LDO_OFF 0x1B
+#define RT5133_REG_LDO1_CTRL1 0x20
+#define RT5133_REG_LDO1_CTRL2 0x21
+#define RT5133_REG_LDO1_CTRL3 0x22
+#define RT5133_REG_LDO2_CTRL1 0x24
+#define RT5133_REG_LDO2_CTRL2 0x25
+#define RT5133_REG_LDO2_CTRL3 0x26
+#define RT5133_REG_LDO3_CTRL1 0x28
+#define RT5133_REG_LDO3_CTRL2 0x29
+#define RT5133_REG_LDO3_CTRL3 0x2A
+#define RT5133_REG_LDO4_CTRL1 0x2C
+#define RT5133_REG_LDO4_CTRL2 0x2D
+#define RT5133_REG_LDO4_CTRL3 0x2E
+#define RT5133_REG_LDO5_CTRL1 0x30
+#define RT5133_REG_LDO5_CTRL2 0x31
+#define RT5133_REG_LDO5_CTRL3 0x32
+#define RT5133_REG_LDO6_CTRL1 0x34
+#define RT5133_REG_LDO6_CTRL2 0x35
+#define RT5133_REG_LDO6_CTRL3 0x36
+#define RT5133_REG_LDO7_CTRL1 0x38
+#define RT5133_REG_LDO7_CTRL2 0x39
+#define RT5133_REG_LDO7_CTRL3 0x3A
+#define RT5133_REG_LDO8_CTRL1 0x3C
+#define RT5133_REG_LDO8_CTRL2 0x3D
+#define RT5133_REG_LDO8_CTRL3 0x3E
+#define RT5133_REG_LDO8_CTRL4 0x3F
+
+#define RT5133_LDO_REG_BASE(_id) (0x20 + ((_id) - 1) * 4)
+
+#define RT5133_VENDOR_ID_MASK GENMASK(7, 4)
+#define RT5133_RESET_CODE 0xB1
+
+#define RT5133_FOFF_BASE_MASK BIT(1)
+#define RT5133_OCSHDN_ALL_MASK BIT(7)
+#define RT5133_OCSHDN_ALL_SHIFT (7)
+#define RT5133_PGBSHDN_ALL_MASK BIT(6)
+#define RT5133_PGBSHDN_ALL_SHIFT (6)
+
+#define RT5133_OCPTSEL_MASK BIT(5)
+#define RT5133_PGBPTSEL_MASK BIT(4)
+#define RT5133_STBTDSEL_MASK GENMASK(1, 0)
+
+#define RT5133_LDO_ENABLE_MASK BIT(7)
+#define RT5133_LDO_VSEL_MASK GENMASK(7, 5)
+#define RT5133_LDO_AD_MASK BIT(2)
+#define RT5133_LDO_SOFT_START_MASK GENMASK(1, 0)
+
+#define RT5133_GPIO_NR 3
+
+#define RT5133_LDO_PGB_EVT_MASK GENMASK(23, 16)
+#define RT5133_LDO_PGB_EVT_SHIFT 16
+#define RT5133_LDO_OC_EVT_MASK GENMASK(15, 8)
+#define RT5133_LDO_OC_EVT_SHIFT 8
+#define RT5133_VREF_EVT_MASK BIT(6)
+#define RT5133_BASE_EVT_MASK GENMASK(7, 0)
+#define RT5133_INTR_CLR_MASK GENMASK(23, 0)
+#define RT5133_INTR_BYTE_NR 3
+
+#define RT5133_MAX_I2C_BLOCK_SIZE 1
+
+#define RT5133_CRC8_POLYNOMIAL 0x7
+
+#define RT5133_I2C_ADDR_LEN 1
+#define RT5133_PREDATA_LEN 2
+#define RT5133_I2C_CRC_LEN 1
+#define RT5133_REG_ADDR_LEN 1
+#define RT5133_I2C_DUMMY_LEN 1
+
+#define I2C_ADDR_XLATE_8BIT(_addr, _rw) ((((_addr) & 0x7F) << 1) | (_rw))
+
+enum {
+ RT5133_REGULATOR_BASE = 0,
+ RT5133_REGULATOR_LDO1,
+ RT5133_REGULATOR_LDO2,
+ RT5133_REGULATOR_LDO3,
+ RT5133_REGULATOR_LDO4,
+ RT5133_REGULATOR_LDO5,
+ RT5133_REGULATOR_LDO6,
+ RT5133_REGULATOR_LDO7,
+ RT5133_REGULATOR_LDO8,
+ RT5133_REGULATOR_MAX
+};
+
+struct chip_data {
+ const struct regulator_desc *regulators;
+ const u8 vendor_id;
+};
+
+struct rt5133_priv {
+ struct device *dev;
+ struct regmap *regmap;
+ struct gpio_desc *enable_gpio;
+ struct regulator_dev *rdev[RT5133_REGULATOR_MAX];
+ struct gpio_chip gc;
+ const struct chip_data *cdata;
+ unsigned int gpio_output_flag;
+ u8 crc8_tbls[CRC8_TABLE_SIZE];
+};
+
+static const unsigned int vout_type1_tables[] = {
+ 1800000, 2500000, 2700000, 2800000, 2900000, 3000000, 3100000, 3200000
+};
+
+static const unsigned int vout_type2_tables[] = {
+ 1700000, 1800000, 1900000, 2500000, 2700000, 2800000, 2900000, 3000000
+};
+
+static const unsigned int vout_type3_tables[] = {
+ 900000, 950000, 1000000, 1050000, 1100000, 1150000, 1200000, 1800000
+};
+
+static const unsigned int vout_type4_tables[] = {
+ 855000, 900000, 950000, 1000000, 1040000, 1090000, 1140000, 1710000
+};
+
+static const struct regulator_ops rt5133_regulator_ops = {
+ .list_voltage = regulator_list_voltage_table,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .set_active_discharge = regulator_set_active_discharge_regmap,
+};
+
+static const struct regulator_ops rt5133_base_regulator_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+};
+
+#define RT5133_REGULATOR_DESC(_name, _node_name, vtables, _supply) \
+{\
+ .name = #_name,\
+ .id = RT5133_REGULATOR_##_name,\
+ .of_match = of_match_ptr(#_node_name),\
+ .regulators_node = of_match_ptr("regulators"),\
+ .supply_name = _supply,\
+ .type = REGULATOR_VOLTAGE,\
+ .owner = THIS_MODULE,\
+ .ops = &rt5133_regulator_ops,\
+ .n_voltages = ARRAY_SIZE(vtables),\
+ .volt_table = vtables,\
+ .enable_reg = RT5133_REG_##_name##_CTRL1,\
+ .enable_mask = RT5133_LDO_ENABLE_MASK,\
+ .vsel_reg = RT5133_REG_##_name##_CTRL2,\
+ .vsel_mask = RT5133_LDO_VSEL_MASK,\
+ .active_discharge_reg = RT5133_REG_##_name##_CTRL3,\
+ .active_discharge_mask = RT5133_LDO_AD_MASK,\
+}
+
+static const struct regulator_desc rt5133_regulators[] = {
+ /* For digital part, base current control */
+ {
+ .name = "base",
+ .id = RT5133_REGULATOR_BASE,
+ .of_match = of_match_ptr("base"),
+ .regulators_node = of_match_ptr("regulators"),
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ .ops = &rt5133_base_regulator_ops,
+ .enable_reg = RT5133_REG_BASE_CTRL,
+ .enable_mask = RT5133_FOFF_BASE_MASK,
+ .enable_is_inverted = true,
+ },
+ RT5133_REGULATOR_DESC(LDO1, ldo1, vout_type1_tables, "base"),
+ RT5133_REGULATOR_DESC(LDO2, ldo2, vout_type1_tables, "base"),
+ RT5133_REGULATOR_DESC(LDO3, ldo3, vout_type2_tables, "base"),
+ RT5133_REGULATOR_DESC(LDO4, ldo4, vout_type2_tables, "base"),
+ RT5133_REGULATOR_DESC(LDO5, ldo5, vout_type2_tables, "base"),
+ RT5133_REGULATOR_DESC(LDO6, ldo6, vout_type2_tables, "base"),
+ RT5133_REGULATOR_DESC(LDO7, ldo7, vout_type3_tables, "vin"),
+ RT5133_REGULATOR_DESC(LDO8, ldo8, vout_type3_tables, "vin"),
+};
+
+static const struct regulator_desc rt5133a_regulators[] = {
+ /* For digital part, base current control */
+ {
+ .name = "base",
+ .id = RT5133_REGULATOR_BASE,
+ .of_match = of_match_ptr("base"),
+ .regulators_node = of_match_ptr("regulators"),
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ .ops = &rt5133_base_regulator_ops,
+ .enable_reg = RT5133_REG_BASE_CTRL,
+ .enable_mask = RT5133_FOFF_BASE_MASK,
+ .enable_is_inverted = true,
+ },
+ RT5133_REGULATOR_DESC(LDO1, ldo1, vout_type1_tables, "base"),
+ RT5133_REGULATOR_DESC(LDO2, ldo2, vout_type1_tables, "base"),
+ RT5133_REGULATOR_DESC(LDO3, ldo3, vout_type2_tables, "base"),
+ RT5133_REGULATOR_DESC(LDO4, ldo4, vout_type2_tables, "base"),
+ RT5133_REGULATOR_DESC(LDO5, ldo5, vout_type2_tables, "base"),
+ RT5133_REGULATOR_DESC(LDO6, ldo6, vout_type2_tables, "base"),
+ RT5133_REGULATOR_DESC(LDO7, ldo7, vout_type3_tables, "vin"),
+ RT5133_REGULATOR_DESC(LDO8, ldo8, vout_type4_tables, "vin"),
+};
+
+static const struct chip_data regulator_data[] = {
+ { rt5133_regulators, 0x70},
+ { rt5133a_regulators, 0x80},
+};
+
+static int rt5133_gpio_direction_output(struct gpio_chip *gpio,
+ unsigned int offset, int value)
+{
+ struct rt5133_priv *priv = gpiochip_get_data(gpio);
+
+ if (offset >= RT5133_GPIO_NR)
+ return -EINVAL;
+
+ return regmap_update_bits(priv->regmap, RT5133_REG_GPIO_CTRL,
+ BIT(7 - offset) | BIT(3 - offset),
+ value ? BIT(7 - offset) | BIT(3 - offset) : 0);
+}
+
+static int rt5133_gpio_get(struct gpio_chip *chip, unsigned int offset)
+{
+ struct rt5133_priv *priv = gpiochip_get_data(chip);
+
+ return !!(priv->gpio_output_flag & BIT(offset));
+}
+
+static int rt5133_get_gpioen_mask(unsigned int offset, unsigned int *mask)
+{
+ if (offset >= RT5133_GPIO_NR)
+ return -EINVAL;
+
+ *mask = (BIT(7 - offset) | BIT(3 - offset));
+
+ return 0;
+}
+
+static int rt5133_gpio_set(struct gpio_chip *chip, unsigned int offset, int set_val)
+{
+ struct rt5133_priv *priv = gpiochip_get_data(chip);
+ unsigned int mask = 0, val = 0, next_flag = priv->gpio_output_flag;
+ int ret = 0;
+
+ ret = rt5133_get_gpioen_mask(offset, &mask);
+ if (ret) {
+ dev_err(priv->dev, "%s get gpion en mask failed, offset(%d)\n", __func__, offset);
+ return ret;
+ }
+
+ val = set_val ? mask : 0;
+
+ if (set_val)
+ next_flag |= BIT(offset);
+ else
+ next_flag &= ~BIT(offset);
+
+ ret = regmap_update_bits(priv->regmap, RT5133_REG_GPIO_CTRL, mask, val);
+ if (ret) {
+ dev_err(priv->dev, "Failed to set gpio [%d] val %d\n", offset,
+ set_val);
+ return ret;
+ }
+
+ priv->gpio_output_flag = next_flag;
+ return 0;
+}
+
+static irqreturn_t rt5133_intr_handler(int irq_number, void *data)
+{
+ struct rt5133_priv *priv = data;
+ u32 intr_evts = 0, handle_evts;
+ int i, ret;
+
+ ret = regmap_bulk_read(priv->regmap, RT5133_REG_BASE_EVT, &intr_evts,
+ RT5133_INTR_BYTE_NR);
+ if (ret) {
+ dev_err(priv->dev, "%s, read event failed\n", __func__);
+ return IRQ_NONE;
+ }
+
+ handle_evts = intr_evts & RT5133_BASE_EVT_MASK;
+ /*
+ * VREF_EVT is a special case, if base off
+ * this event will also be trigger. Skip it
+ */
+ if (handle_evts & ~RT5133_VREF_EVT_MASK)
+ dev_dbg(priv->dev, "base event occurred [0x%02x]\n",
+ handle_evts);
+
+ handle_evts = (intr_evts & RT5133_LDO_OC_EVT_MASK) >>
+ RT5133_LDO_OC_EVT_SHIFT;
+
+ for (i = RT5133_REGULATOR_LDO1; i < RT5133_REGULATOR_MAX && handle_evts; i++) {
+ if (!(handle_evts & BIT(i - 1)))
+ continue;
+ regulator_notifier_call_chain(priv->rdev[i],
+ REGULATOR_EVENT_OVER_CURRENT,
+ &i);
+ }
+
+ handle_evts = (intr_evts & RT5133_LDO_PGB_EVT_MASK) >>
+ RT5133_LDO_PGB_EVT_SHIFT;
+ for (i = RT5133_REGULATOR_LDO1; i < RT5133_REGULATOR_MAX && handle_evts; i++) {
+ if (!(handle_evts & BIT(i - 1)))
+ continue;
+ regulator_notifier_call_chain(priv->rdev[i],
+ REGULATOR_EVENT_FAIL, &i);
+ }
+
+ ret = regmap_bulk_write(priv->regmap, RT5133_REG_BASE_EVT, &intr_evts,
+ RT5133_INTR_BYTE_NR);
+ if (ret)
+ dev_err(priv->dev, "%s, clear event failed\n", __func__);
+
+ return IRQ_HANDLED;
+}
+
+static int rt5133_enable_interrupts(int irq_no, struct rt5133_priv *priv)
+{
+ u32 mask = RT5133_INTR_CLR_MASK;
+ int ret;
+
+ /* Force to write clear all events */
+ ret = regmap_bulk_write(priv->regmap, RT5133_REG_BASE_EVT, &mask,
+ RT5133_INTR_BYTE_NR);
+ if (ret) {
+ dev_err(priv->dev, "Failed to clear all interrupts\n");
+ return ret;
+ }
+
+ /* Unmask all interrupts */
+ mask = 0;
+ ret = regmap_bulk_write(priv->regmap, RT5133_REG_BASE_MASK, &mask,
+ RT5133_INTR_BYTE_NR);
+ if (ret) {
+ dev_err(priv->dev, "Failed to unmask all interrupts\n");
+ return ret;
+ }
+
+ return devm_request_threaded_irq(priv->dev, irq_no, NULL,
+ rt5133_intr_handler, IRQF_ONESHOT,
+ dev_name(priv->dev), priv);
+}
+
+static int rt5133_regmap_hw_read(void *context, const void *reg_buf,
+ size_t reg_size, void *val_buf,
+ size_t val_size)
+{
+ struct rt5133_priv *priv = context;
+ struct i2c_client *client = to_i2c_client(priv->dev);
+ u8 reg = *(u8 *)reg_buf, crc;
+ u8 *buf;
+ int buf_len = RT5133_PREDATA_LEN + val_size + RT5133_I2C_CRC_LEN;
+ int read_len, ret;
+
+ buf = kzalloc(buf_len, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ buf[0] = I2C_ADDR_XLATE_8BIT(client->addr, I2C_SMBUS_READ);
+ buf[1] = reg;
+
+ read_len = val_size + RT5133_I2C_CRC_LEN;
+ ret = i2c_smbus_read_i2c_block_data(client, reg, read_len,
+ buf + RT5133_PREDATA_LEN);
+
+ if (ret < 0)
+ goto out_read_err;
+
+ if (ret != read_len) {
+ ret = -EIO;
+ goto out_read_err;
+ }
+
+ crc = crc8(priv->crc8_tbls, buf, RT5133_PREDATA_LEN + val_size, 0);
+ if (crc != buf[RT5133_PREDATA_LEN + val_size]) {
+ ret = -EIO;
+ goto out_read_err;
+ }
+
+ memcpy(val_buf, buf + RT5133_PREDATA_LEN, val_size);
+ dev_dbg(priv->dev, "%s, reg = 0x%02x, data = 0x%02x\n", __func__, reg, *(u8 *)val_buf);
+
+out_read_err:
+ kfree(buf);
+ return (ret < 0) ? ret : 0;
+}
+
+static int rt5133_regmap_hw_write(void *context, const void *data, size_t count)
+{
+ struct rt5133_priv *priv = context;
+ struct i2c_client *client = to_i2c_client(priv->dev);
+ u8 reg = *(u8 *)data, crc;
+ u8 *buf;
+ int buf_len = RT5133_I2C_ADDR_LEN + count + RT5133_I2C_CRC_LEN +
+ RT5133_I2C_DUMMY_LEN;
+ int write_len, ret;
+
+ buf = kzalloc(buf_len, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ buf[0] = I2C_ADDR_XLATE_8BIT(client->addr, I2C_SMBUS_WRITE);
+ buf[1] = reg;
+ memcpy(buf + RT5133_PREDATA_LEN, data + RT5133_REG_ADDR_LEN,
+ count - RT5133_REG_ADDR_LEN);
+
+ crc = crc8(priv->crc8_tbls, buf, RT5133_I2C_ADDR_LEN + count, 0);
+ buf[RT5133_I2C_ADDR_LEN + count] = crc;
+
+ write_len = count - RT5133_REG_ADDR_LEN + RT5133_I2C_CRC_LEN +
+ RT5133_I2C_DUMMY_LEN;
+ ret = i2c_smbus_write_i2c_block_data(client, reg, write_len,
+ buf + RT5133_PREDATA_LEN);
+
+ dev_dbg(priv->dev, "%s, reg = 0x%02x, data = 0x%02x\n", __func__, reg,
+ *(u8 *)(buf + RT5133_PREDATA_LEN));
+ kfree(buf);
+ return ret;
+}
+
+static const struct regmap_bus rt5133_regmap_bus = {
+ .read = rt5133_regmap_hw_read,
+ .write = rt5133_regmap_hw_write,
+ /* Due to crc, the block read/write length has the limit */
+ .max_raw_read = RT5133_MAX_I2C_BLOCK_SIZE,
+ .max_raw_write = RT5133_MAX_I2C_BLOCK_SIZE,
+};
+
+static bool rt5133_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case RT5133_REG_CHIP_INFO:
+ case RT5133_REG_BASE_EVT...RT5133_REG_LDO_PGB_STAT:
+ case RT5133_REG_LDO_ON...RT5133_REG_LDO_OFF:
+ case RT5133_REG_LDO1_CTRL1:
+ case RT5133_REG_LDO2_CTRL1:
+ case RT5133_REG_LDO3_CTRL1:
+ case RT5133_REG_LDO4_CTRL1:
+ case RT5133_REG_LDO5_CTRL1:
+ case RT5133_REG_LDO6_CTRL1:
+ case RT5133_REG_LDO7_CTRL1:
+ case RT5133_REG_LDO8_CTRL1:
+ return true;
+ default:
+ return false;
+ };
+}
+
+static const struct regmap_config rt5133_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = RT5133_REG_LDO8_CTRL4,
+ .cache_type = REGCACHE_FLAT,
+ .num_reg_defaults_raw = RT5133_REG_LDO8_CTRL4 + 1,
+ .volatile_reg = rt5133_is_volatile_reg,
+};
+
+static int rt5133_chip_reset(struct rt5133_priv *priv)
+{
+ int ret;
+
+ ret = regmap_write(priv->regmap, RT5133_REG_RST_CTRL,
+ RT5133_RESET_CODE);
+ if (ret)
+ return ret;
+
+ /* Wait for register reset to take effect */
+ udelay(2);
+
+ return 0;
+}
+
+static int rt5133_validate_vendor_info(struct rt5133_priv *priv)
+{
+ unsigned int val = 0;
+ int i, ret;
+
+ ret = regmap_read(priv->regmap, RT5133_REG_CHIP_INFO, &val);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < ARRAY_SIZE(regulator_data); i++) {
+ if ((val & RT5133_VENDOR_ID_MASK) ==
+ regulator_data[i].vendor_id){
+ priv->cdata = &regulator_data[i];
+ break;
+ }
+ }
+ if (!priv->cdata) {
+ dev_err(priv->dev, "Failed to find regulator match version\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int rt5133_parse_dt(struct rt5133_priv *priv)
+{
+ unsigned int val = 0;
+ int ret = 0;
+
+ if (!device_property_read_bool(priv->dev, "richtek,oc-shutdown-all"))
+ val = 0;
+ else
+ val = 1 << RT5133_OCSHDN_ALL_SHIFT;
+ ret = regmap_update_bits(priv->regmap, RT5133_REG_LDO_SHDN,
+ RT5133_OCSHDN_ALL_MASK, val);
+ if (ret)
+ return ret;
+
+ if (!device_property_read_bool(priv->dev, "richtek,pgb-shutdown-all"))
+ val = 0;
+ else
+ val = 1 << RT5133_PGBSHDN_ALL_SHIFT;
+ return regmap_update_bits(priv->regmap, RT5133_REG_LDO_SHDN,
+ RT5133_PGBSHDN_ALL_MASK, val);
+}
+
+static int rt5133_probe(struct i2c_client *i2c)
+{
+ struct rt5133_priv *priv;
+ struct regulator_config config = {0};
+ int i, ret;
+
+ priv = devm_kzalloc(&i2c->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->dev = &i2c->dev;
+ crc8_populate_msb(priv->crc8_tbls, RT5133_CRC8_POLYNOMIAL);
+
+ priv->enable_gpio = devm_gpiod_get_optional(&i2c->dev, "enable",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(priv->enable_gpio))
+ dev_err(&i2c->dev, "Failed to request HWEN gpio, check if default en=high\n");
+
+ priv->regmap = devm_regmap_init(&i2c->dev, &rt5133_regmap_bus, priv,
+ &rt5133_regmap_config);
+ if (IS_ERR(priv->regmap)) {
+ dev_err(&i2c->dev, "Failed to register regmap\n");
+ return PTR_ERR(priv->regmap);
+ }
+
+ ret = rt5133_validate_vendor_info(priv);
+ if (ret) {
+ dev_err(&i2c->dev, "Failed to check vendor info [%d]\n", ret);
+ return ret;
+ }
+
+ ret = rt5133_chip_reset(priv);
+ if (ret) {
+ dev_err(&i2c->dev, "Failed to execute sw reset\n");
+ return ret;
+ }
+
+ config.dev = &i2c->dev;
+ config.driver_data = priv;
+ config.regmap = priv->regmap;
+
+ for (i = 0; i < RT5133_REGULATOR_MAX; i++) {
+ priv->rdev[i] = devm_regulator_register(&i2c->dev,
+ priv->cdata->regulators + i,
+ &config);
+ if (IS_ERR(priv->rdev[i])) {
+ dev_err(&i2c->dev,
+ "Failed to register [%d] regulator\n", i);
+ return PTR_ERR(priv->rdev[i]);
+ }
+ }
+
+ ret = rt5133_parse_dt(priv);
+ if (ret) {
+ dev_err(&i2c->dev, "%s, Failed to parse dt\n", __func__);
+ return ret;
+ }
+
+ priv->gc.label = dev_name(&i2c->dev);
+ priv->gc.parent = &i2c->dev;
+ priv->gc.base = -1;
+ priv->gc.ngpio = RT5133_GPIO_NR;
+ priv->gc.set = rt5133_gpio_set;
+ priv->gc.get = rt5133_gpio_get;
+ priv->gc.direction_output = rt5133_gpio_direction_output;
+ priv->gc.can_sleep = true;
+
+ ret = devm_gpiochip_add_data(&i2c->dev, &priv->gc, priv);
+ if (ret)
+ return ret;
+
+ ret = rt5133_enable_interrupts(i2c->irq, priv);
+ if (ret) {
+ dev_err(&i2c->dev, "enable interrupt failed\n");
+ return ret;
+ }
+
+ i2c_set_clientdata(i2c, priv);
+
+ return ret;
+}
+
+static const struct of_device_id __maybe_unused rt5133_of_match_table[] = {
+ { .compatible = "richtek,rt5133", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, rt5133_of_match_table);
+
+static struct i2c_driver rt5133_driver = {
+ .driver = {
+ .name = "rt5133",
+ .probe_type = PROBE_PREFER_ASYNCHRONOUS,
+ .of_match_table = rt5133_of_match_table,
+ },
+ .probe = rt5133_probe,
+};
+module_i2c_driver(rt5133_driver);
+
+MODULE_DESCRIPTION("RT5133 Regulator Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/regulator/s2dos05-regulator.c b/drivers/regulator/s2dos05-regulator.c
new file mode 100644
index 000000000000..1463585c4565
--- /dev/null
+++ b/drivers/regulator/s2dos05-regulator.c
@@ -0,0 +1,165 @@
+// SPDX-License-Identifier: GPL-2.0+
+//
+// s2dos05.c - Regulator driver for the Samsung s2dos05
+//
+// Copyright (C) 2025 Dzmitry Sankouski <dsankouski@gmail.com>
+
+#include <linux/bug.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/mfd/samsung/core.h>
+#include <linux/regulator/s2dos05.h>
+#include <linux/i2c.h>
+
+struct s2dos05_data {
+ struct regmap *regmap;
+ struct device *dev;
+};
+
+#define _BUCK(macro) S2DOS05_BUCK##macro
+#define _buck_ops(num) s2dos05_ops##num
+#define _LDO(macro) S2DOS05_LDO##macro
+#define _REG(ctrl) S2DOS05_REG##ctrl
+#define _ldo_ops(num) s2dos05_ops##num
+#define _MASK(macro) S2DOS05_ENABLE_MASK##macro
+#define _TIME(macro) S2DOS05_ENABLE_TIME##macro
+
+#define BUCK_DESC(_name, _id, _ops, m, s, v, e, em, t, a) { \
+ .name = _name, \
+ .id = _id, \
+ .ops = _ops, \
+ .of_match = of_match_ptr(_name), \
+ .of_match_full_name = true, \
+ .regulators_node = of_match_ptr("regulators"), \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .min_uV = m, \
+ .uV_step = s, \
+ .n_voltages = S2DOS05_BUCK_N_VOLTAGES, \
+ .vsel_reg = v, \
+ .vsel_mask = S2DOS05_BUCK_VSEL_MASK, \
+ .enable_reg = e, \
+ .enable_mask = em, \
+ .enable_time = t, \
+ .active_discharge_off = 0, \
+ .active_discharge_on = S2DOS05_BUCK_FD_MASK, \
+ .active_discharge_reg = a, \
+ .active_discharge_mask = S2DOS05_BUCK_FD_MASK \
+}
+
+#define LDO_DESC(_name, _id, _ops, m, s, v, e, em, t, a) { \
+ .name = _name, \
+ .id = _id, \
+ .ops = _ops, \
+ .of_match = of_match_ptr(_name), \
+ .of_match_full_name = true, \
+ .regulators_node = of_match_ptr("regulators"), \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .min_uV = m, \
+ .uV_step = s, \
+ .n_voltages = S2DOS05_LDO_N_VOLTAGES, \
+ .vsel_reg = v, \
+ .vsel_mask = S2DOS05_LDO_VSEL_MASK, \
+ .enable_reg = e, \
+ .enable_mask = em, \
+ .enable_time = t, \
+ .active_discharge_off = 0, \
+ .active_discharge_on = S2DOS05_LDO_FD_MASK, \
+ .active_discharge_reg = a, \
+ .active_discharge_mask = S2DOS05_LDO_FD_MASK \
+}
+
+static const struct regulator_ops s2dos05_ops = {
+ .list_voltage = regulator_list_voltage_linear,
+ .map_voltage = regulator_map_voltage_linear,
+ .is_enabled = regulator_is_enabled_regmap,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .set_voltage_time_sel = regulator_set_voltage_time_sel,
+ .set_active_discharge = regulator_set_active_discharge_regmap,
+};
+
+static const struct regulator_desc regulators[S2DOS05_REGULATOR_MAX] = {
+ // name, id, ops, min_uv, uV_step, vsel_reg, enable_reg
+ LDO_DESC("ldo1", _LDO(1), &_ldo_ops(), _LDO(_MIN1),
+ _LDO(_STEP1), _REG(_LDO1_CFG),
+ _REG(_EN), _MASK(_L1), _TIME(_LDO), _REG(_LDO1_CFG)),
+ LDO_DESC("ldo2", _LDO(2), &_ldo_ops(), _LDO(_MIN1),
+ _LDO(_STEP1), _REG(_LDO2_CFG),
+ _REG(_EN), _MASK(_L2), _TIME(_LDO), _REG(_LDO2_CFG)),
+ LDO_DESC("ldo3", _LDO(3), &_ldo_ops(), _LDO(_MIN2),
+ _LDO(_STEP1), _REG(_LDO3_CFG),
+ _REG(_EN), _MASK(_L3), _TIME(_LDO), _REG(_LDO3_CFG)),
+ LDO_DESC("ldo4", _LDO(4), &_ldo_ops(), _LDO(_MIN2),
+ _LDO(_STEP1), _REG(_LDO4_CFG),
+ _REG(_EN), _MASK(_L4), _TIME(_LDO), _REG(_LDO4_CFG)),
+ BUCK_DESC("buck", _BUCK(1), &_buck_ops(), _BUCK(_MIN1),
+ _BUCK(_STEP1), _REG(_BUCK_VOUT),
+ _REG(_EN), _MASK(_B1), _TIME(_BUCK), _REG(_BUCK_CFG)),
+};
+
+static int s2dos05_pmic_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct sec_pmic_dev *iodev = dev_get_drvdata(pdev->dev.parent);
+ struct s2dos05_data *s2dos05;
+ struct regulator_config config = { };
+ unsigned int rdev_num = ARRAY_SIZE(regulators);
+
+ s2dos05 = devm_kzalloc(dev, sizeof(*s2dos05), GFP_KERNEL);
+ if (!s2dos05)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, s2dos05);
+
+ s2dos05->regmap = iodev->regmap_pmic;
+ s2dos05->dev = dev;
+ if (!dev->of_node)
+ dev->of_node = dev->parent->of_node;
+
+ config.dev = dev;
+ config.driver_data = s2dos05;
+
+ for (int i = 0; i < rdev_num; i++) {
+ struct regulator_dev *regulator;
+
+ regulator = devm_regulator_register(&pdev->dev,
+ &regulators[i], &config);
+ if (IS_ERR(regulator)) {
+ return dev_err_probe(&pdev->dev, PTR_ERR(regulator),
+ "regulator init failed for %d\n", i);
+ }
+ }
+
+ return 0;
+}
+
+static const struct platform_device_id s2dos05_pmic_id[] = {
+ { "s2dos05-regulator" },
+ { },
+};
+MODULE_DEVICE_TABLE(platform, s2dos05_pmic_id);
+
+static struct platform_driver s2dos05_platform_driver = {
+ .driver = {
+ .name = "s2dos05",
+ },
+ .probe = s2dos05_pmic_probe,
+ .id_table = s2dos05_pmic_id,
+};
+module_platform_driver(s2dos05_platform_driver);
+
+MODULE_AUTHOR("Dzmitry Sankouski <dsankouski@gmail.com>");
+MODULE_DESCRIPTION("Samsung S2DOS05 Regulator Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/regulator/scmi-regulator.c b/drivers/regulator/scmi-regulator.c
index 9df726f10ad1..6d609c42e479 100644
--- a/drivers/regulator/scmi-regulator.c
+++ b/drivers/regulator/scmi-regulator.c
@@ -257,7 +257,8 @@ static int process_scmi_regulator_of_node(struct scmi_device *sdev,
struct device_node *np,
struct scmi_regulator_info *rinfo)
{
- u32 dom, ret;
+ u32 dom;
+ int ret;
ret = of_property_read_u32(np, "reg", &dom);
if (ret)
diff --git a/drivers/regulator/spacemit-p1.c b/drivers/regulator/spacemit-p1.c
new file mode 100644
index 000000000000..d437e6738ea1
--- /dev/null
+++ b/drivers/regulator/spacemit-p1.c
@@ -0,0 +1,157 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for regulators found in the SpacemiT P1 PMIC
+ *
+ * Copyright (C) 2025 by RISCstar Solutions Corporation. All rights reserved.
+ * Derived from code from SpacemiT.
+ * Copyright (c) 2023, SPACEMIT Co., Ltd
+ */
+
+#include <linux/array_size.h>
+#include <linux/bits.h>
+#include <linux/device.h>
+#include <linux/linear_range.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+
+#define MOD_NAME "spacemit-p1-regulator"
+
+enum p1_regulator_id {
+ P1_BUCK1,
+ P1_BUCK2,
+ P1_BUCK3,
+ P1_BUCK4,
+ P1_BUCK5,
+ P1_BUCK6,
+
+ P1_ALDO1,
+ P1_ALDO2,
+ P1_ALDO3,
+ P1_ALDO4,
+
+ P1_DLDO1,
+ P1_DLDO2,
+ P1_DLDO3,
+ P1_DLDO4,
+ P1_DLDO5,
+ P1_DLDO6,
+ P1_DLDO7,
+};
+
+static const struct regulator_ops p1_regulator_ops = {
+ .list_voltage = regulator_list_voltage_linear_range,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .set_voltage_time_sel = regulator_set_voltage_time_sel,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+};
+
+/* Selector value 255 can be used to disable the buck converter on sleep */
+static const struct linear_range p1_buck_ranges[] = {
+ REGULATOR_LINEAR_RANGE(500000, 0, 170, 5000),
+ REGULATOR_LINEAR_RANGE(1375000, 171, 254, 25000),
+};
+
+/* Selector value 0 can be used for suspend */
+static const struct linear_range p1_ldo_ranges[] = {
+ REGULATOR_LINEAR_RANGE(500000, 11, 127, 25000),
+};
+
+/* These define the voltage selector field for buck and LDO regulators */
+#define BUCK_MASK GENMASK(7, 0)
+#define LDO_MASK GENMASK(6, 0)
+
+#define P1_ID(_TYPE, _n) P1_ ## _TYPE ## _n
+#define P1_ENABLE_REG(_off, _n) ((_off) + 3 * ((_n) - 1))
+
+#define P1_REG_DESC(_TYPE, _type, _n, _s, _off, _mask, _nv, _ranges) \
+ { \
+ .name = #_type #_n, \
+ .supply_name = _s, \
+ .of_match = of_match_ptr(#_type #_n), \
+ .regulators_node = of_match_ptr("regulators"), \
+ .id = P1_ID(_TYPE, _n), \
+ .n_voltages = _nv, \
+ .ops = &p1_regulator_ops, \
+ .owner = THIS_MODULE, \
+ .linear_ranges = _ranges, \
+ .n_linear_ranges = ARRAY_SIZE(_ranges), \
+ .vsel_reg = P1_ENABLE_REG(_off, _n) + 1, \
+ .vsel_mask = _mask, \
+ .enable_reg = P1_ENABLE_REG(_off, _n), \
+ .enable_mask = BIT(0), \
+ }
+
+#define P1_BUCK_DESC(_n) \
+ P1_REG_DESC(BUCK, buck, _n, "vcc", 0x47, BUCK_MASK, 254, p1_buck_ranges)
+
+#define P1_ALDO_DESC(_n) \
+ P1_REG_DESC(ALDO, aldo, _n, "vcc", 0x5b, LDO_MASK, 117, p1_ldo_ranges)
+
+#define P1_DLDO_DESC(_n) \
+ P1_REG_DESC(DLDO, dldo, _n, "buck5", 0x67, LDO_MASK, 117, p1_ldo_ranges)
+
+static const struct regulator_desc p1_regulator_desc[] = {
+ P1_BUCK_DESC(1),
+ P1_BUCK_DESC(2),
+ P1_BUCK_DESC(3),
+ P1_BUCK_DESC(4),
+ P1_BUCK_DESC(5),
+ P1_BUCK_DESC(6),
+
+ P1_ALDO_DESC(1),
+ P1_ALDO_DESC(2),
+ P1_ALDO_DESC(3),
+ P1_ALDO_DESC(4),
+
+ P1_DLDO_DESC(1),
+ P1_DLDO_DESC(2),
+ P1_DLDO_DESC(3),
+ P1_DLDO_DESC(4),
+ P1_DLDO_DESC(5),
+ P1_DLDO_DESC(6),
+ P1_DLDO_DESC(7),
+};
+
+static int p1_regulator_probe(struct platform_device *pdev)
+{
+ struct regulator_config config = { };
+ struct device *dev = &pdev->dev;
+ u32 i;
+
+ /*
+ * The parent device (PMIC) owns the regmap. Since we don't
+ * provide one in the config structure, that one will be used.
+ */
+ config.dev = dev->parent;
+
+ for (i = 0; i < ARRAY_SIZE(p1_regulator_desc); i++) {
+ const struct regulator_desc *desc = &p1_regulator_desc[i];
+ struct regulator_dev *rdev;
+
+ rdev = devm_regulator_register(dev, desc, &config);
+ if (IS_ERR(rdev))
+ return dev_err_probe(dev, PTR_ERR(rdev),
+ "error registering regulator %s\n",
+ desc->name);
+ }
+
+ return 0;
+}
+
+static struct platform_driver p1_regulator_driver = {
+ .probe = p1_regulator_probe,
+ .driver = {
+ .name = MOD_NAME,
+ },
+};
+
+module_platform_driver(p1_regulator_driver);
+
+MODULE_DESCRIPTION("SpacemiT P1 regulator driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" MOD_NAME);
diff --git a/drivers/regulator/sy7636a-regulator.c b/drivers/regulator/sy7636a-regulator.c
index d1e7ba1fb3e1..27e3d939b7bb 100644
--- a/drivers/regulator/sy7636a-regulator.c
+++ b/drivers/regulator/sy7636a-regulator.c
@@ -83,9 +83,11 @@ static int sy7636a_regulator_probe(struct platform_device *pdev)
if (!regmap)
return -EPROBE_DEFER;
- gdp = devm_gpiod_get(pdev->dev.parent, "epd-pwr-good", GPIOD_IN);
+ device_set_of_node_from_dev(&pdev->dev, pdev->dev.parent);
+
+ gdp = devm_gpiod_get(&pdev->dev, "epd-pwr-good", GPIOD_IN);
if (IS_ERR(gdp)) {
- dev_err(pdev->dev.parent, "Power good GPIO fault %ld\n", PTR_ERR(gdp));
+ dev_err(&pdev->dev, "Power good GPIO fault %ld\n", PTR_ERR(gdp));
return PTR_ERR(gdp);
}
@@ -105,7 +107,6 @@ static int sy7636a_regulator_probe(struct platform_device *pdev)
}
config.dev = &pdev->dev;
- config.dev->of_node = pdev->dev.parent->of_node;
config.regmap = regmap;
rdev = devm_regulator_register(&pdev->dev, &desc, &config);
diff --git a/drivers/regulator/tps65219-regulator.c b/drivers/regulator/tps65219-regulator.c
index 5e67fdc88f49..d77ca486879f 100644
--- a/drivers/regulator/tps65219-regulator.c
+++ b/drivers/regulator/tps65219-regulator.c
@@ -454,9 +454,9 @@ static int tps65219_regulator_probe(struct platform_device *pdev)
irq_type->irq_name,
irq_data);
if (error)
- return dev_err_probe(tps->dev, PTR_ERR(rdev),
- "Failed to request %s IRQ %d: %d\n",
- irq_type->irq_name, irq, error);
+ return dev_err_probe(tps->dev, error,
+ "Failed to request %s IRQ %d\n",
+ irq_type->irq_name, irq);
}
for (i = 0; i < pmic->dev_irq_size; ++i) {
@@ -477,9 +477,9 @@ static int tps65219_regulator_probe(struct platform_device *pdev)
irq_type->irq_name,
irq_data);
if (error)
- return dev_err_probe(tps->dev, PTR_ERR(rdev),
- "Failed to request %s IRQ %d: %d\n",
- irq_type->irq_name, irq, error);
+ return dev_err_probe(tps->dev, error,
+ "Failed to request %s IRQ %d\n",
+ irq_type->irq_name, irq);
}
return 0;
diff --git a/drivers/regulator/tps6524x-regulator.c b/drivers/regulator/tps6524x-regulator.c
index 3fee7e38c68b..6beb51293e8e 100644
--- a/drivers/regulator/tps6524x-regulator.c
+++ b/drivers/regulator/tps6524x-regulator.c
@@ -598,7 +598,6 @@ static int pmic_probe(struct spi_device *spi)
spi_set_drvdata(spi, hw);
- memset(hw, 0, sizeof(struct tps6524x));
hw->dev = dev;
hw->spi = spi;
mutex_init(&hw->lock);
diff --git a/drivers/regulator/tps6594-regulator.c b/drivers/regulator/tps6594-regulator.c
index ab882daec7c5..645e83462c64 100644
--- a/drivers/regulator/tps6594-regulator.c
+++ b/drivers/regulator/tps6594-regulator.c
@@ -647,7 +647,7 @@ static int tps6594_regulator_probe(struct platform_device *pdev)
default:
dev_err(tps->dev, "unknown chip_id %lu\n", tps->chip_id);
return -EINVAL;
- };
+ }
enum {
MULTI_BUCK12,