summaryrefslogtreecommitdiff
path: root/drivers/regulator
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/regulator')
-rw-r--r--drivers/regulator/Kconfig18
-rw-r--r--drivers/regulator/Makefile2
-rw-r--r--drivers/regulator/mp5416.c30
-rw-r--r--drivers/regulator/mt6370-regulator.c390
-rw-r--r--drivers/regulator/qcom_smd-regulator.c1
-rw-r--r--drivers/regulator/rpi-panel-attiny-regulator.c6
-rw-r--r--drivers/regulator/rt5120-regulator.c420
-rw-r--r--drivers/regulator/rt5190a-regulator.c3
-rw-r--r--drivers/regulator/scmi-regulator.c1
9 files changed, 860 insertions, 11 deletions
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index cbe0f96ca342..03e8a4ef2ed6 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -804,6 +804,14 @@ config REGULATOR_MT6360
2-channel buck with Thermal Shutdown and Overload Protection
6-channel High PSRR and Low Dropout LDO.
+config REGULATOR_MT6370
+ tristate "MT6370 SubPMIC Regulator"
+ depends on MFD_MT6370
+ help
+ Say Y here to enable MT6370 regulator support.
+ This driver supports the control for DisplayBias voltages and one
+ general purpose LDO which is commonly used to drive the vibrator.
+
config REGULATOR_MT6380
tristate "MediaTek MT6380 PMIC"
depends on MTK_PMIC_WRAP
@@ -1047,6 +1055,16 @@ config REGULATOR_RT5033
RT5033 PMIC. The device supports multiple regulators like
current source, LDO and Buck.
+config REGULATOR_RT5120
+ tristate "Richtek RT5120 PMIC Regulators"
+ depends on MFD_RT5120
+ help
+ This adds support for voltage regulator in Richtek RT5120 PMIC.
+ It integrates 4 channels buck controller, 1 channel LDO, 1 EXTEN
+ to control external power source. Only BUCK1 is adjustable from
+ 600mV to 1395mV, per step 6.250mV. The others are all fixed voltage
+ by external hardware circuit.
+
config REGULATOR_RT5190A
tristate "Richtek RT5190A PMIC"
depends on I2C
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index 8d3ee8b6d41d..a9c392587120 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -97,6 +97,7 @@ obj-$(CONFIG_REGULATOR_MT6323) += mt6323-regulator.o
obj-$(CONFIG_REGULATOR_MT6358) += mt6358-regulator.o
obj-$(CONFIG_REGULATOR_MT6359) += mt6359-regulator.o
obj-$(CONFIG_REGULATOR_MT6360) += mt6360-regulator.o
+obj-$(CONFIG_REGULATOR_MT6370) += mt6370-regulator.o
obj-$(CONFIG_REGULATOR_MT6380) += mt6380-regulator.o
obj-$(CONFIG_REGULATOR_MT6397) += mt6397-regulator.o
obj-$(CONFIG_REGULATOR_MTK_DVFSRC) += mtk-dvfsrc-regulator.o
@@ -126,6 +127,7 @@ obj-$(CONFIG_REGULATOR_ROHM) += rohm-regulator.o
obj-$(CONFIG_REGULATOR_RT4801) += rt4801-regulator.o
obj-$(CONFIG_REGULATOR_RT4831) += rt4831-regulator.o
obj-$(CONFIG_REGULATOR_RT5033) += rt5033-regulator.o
+obj-$(CONFIG_REGULATOR_RT5120) += rt5120-regulator.o
obj-$(CONFIG_REGULATOR_RT5190A) += rt5190a-regulator.o
obj-$(CONFIG_REGULATOR_RT5759) += rt5759-regulator.o
obj-$(CONFIG_REGULATOR_RT6160) += rt6160-regulator.o
diff --git a/drivers/regulator/mp5416.c b/drivers/regulator/mp5416.c
index 39cebec0edb6..82892d71c2c9 100644
--- a/drivers/regulator/mp5416.c
+++ b/drivers/regulator/mp5416.c
@@ -6,14 +6,14 @@
//
// Author: Saravanan Sekar <sravanhome@gmail.com>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/init.h>
#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/regulator/driver.h>
-#include <linux/i2c.h>
#define MP5416_REG_CTL0 0x00
#define MP5416_REG_CTL1 0x01
@@ -174,10 +174,22 @@ static struct regulator_desc mp5416_regulators_desc[MP5416_MAX_REGULATORS] = {
MP5416LDO("ldo4", 4, BIT(1)),
};
+static struct regulator_desc mp5496_regulators_desc[MP5416_MAX_REGULATORS] = {
+ MP5416BUCK("buck1", 1, mp5416_I_limits1, MP5416_REG_CTL1, BIT(0), 1),
+ MP5416BUCK("buck2", 2, mp5416_I_limits2, MP5416_REG_CTL1, BIT(1), 1),
+ MP5416BUCK("buck3", 3, mp5416_I_limits1, MP5416_REG_CTL1, BIT(2), 1),
+ MP5416BUCK("buck4", 4, mp5416_I_limits2, MP5416_REG_CTL2, BIT(5), 1),
+ MP5416LDO("ldo1", 1, BIT(4)),
+ MP5416LDO("ldo2", 2, BIT(3)),
+ MP5416LDO("ldo3", 3, BIT(2)),
+ MP5416LDO("ldo4", 4, BIT(1)),
+};
+
static int mp5416_i2c_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
struct regulator_config config = { NULL, };
+ static const struct regulator_desc *desc;
struct regulator_dev *rdev;
struct regmap *regmap;
int i;
@@ -188,12 +200,16 @@ static int mp5416_i2c_probe(struct i2c_client *client)
return PTR_ERR(regmap);
}
+ desc = of_device_get_match_data(dev);
+ if (!desc)
+ return -ENODEV;
+
config.dev = dev;
config.regmap = regmap;
for (i = 0; i < MP5416_MAX_REGULATORS; i++) {
rdev = devm_regulator_register(dev,
- &mp5416_regulators_desc[i],
+ &desc[i],
&config);
if (IS_ERR(rdev)) {
dev_err(dev, "Failed to register regulator!\n");
@@ -205,13 +221,15 @@ static int mp5416_i2c_probe(struct i2c_client *client)
}
static const struct of_device_id mp5416_of_match[] = {
- { .compatible = "mps,mp5416" },
+ { .compatible = "mps,mp5416", .data = &mp5416_regulators_desc },
+ { .compatible = "mps,mp5496", .data = &mp5496_regulators_desc },
{},
};
MODULE_DEVICE_TABLE(of, mp5416_of_match);
static const struct i2c_device_id mp5416_id[] = {
{ "mp5416", },
+ { "mp5496", },
{ },
};
MODULE_DEVICE_TABLE(i2c, mp5416_id);
diff --git a/drivers/regulator/mt6370-regulator.c b/drivers/regulator/mt6370-regulator.c
new file mode 100644
index 000000000000..e73f5a46cb9a
--- /dev/null
+++ b/drivers/regulator/mt6370-regulator.c
@@ -0,0 +1,390 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+#include <linux/bits.h>
+#include <linux/gpio/consumer.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+
+enum {
+ MT6370_IDX_DSVBOOST = 0,
+ MT6370_IDX_DSVPOS,
+ MT6370_IDX_DSVNEG,
+ MT6370_IDX_VIBLDO,
+ MT6370_MAX_IDX
+};
+
+#define MT6370_REG_LDO_CFG 0x180
+#define MT6370_REG_LDO_VOUT 0x181
+#define MT6370_REG_DB_CTRL1 0x1B0
+#define MT6370_REG_DB_CTRL2 0x1B1
+#define MT6370_REG_DB_VBST 0x1B2
+#define MT6370_REG_DB_VPOS 0x1B3
+#define MT6370_REG_DB_VNEG 0x1B4
+#define MT6370_REG_LDO_STAT 0x1DC
+#define MT6370_REG_DB_STAT 0x1DF
+
+#define MT6370_LDOOMS_MASK BIT(7)
+#define MT6370_LDOEN_MASK BIT(7)
+#define MT6370_LDOVOUT_MASK GENMASK(3, 0)
+#define MT6370_DBPERD_MASK (BIT(7) | BIT(4))
+#define MT6370_DBEXTEN_MASK BIT(0)
+#define MT6370_DBVPOSEN_MASK BIT(6)
+#define MT6370_DBVPOSDISG_MASK BIT(5)
+#define MT6370_DBVNEGEN_MASK BIT(3)
+#define MT6370_DBVNEGDISG_MASK BIT(2)
+#define MT6370_DBALLON_MASK (MT6370_DBVPOSEN_MASK | MT6370_DBVNEGEN_MASK)
+#define MT6370_DBSLEW_MASK GENMASK(7, 6)
+#define MT6370_DBVOUT_MASK GENMASK(5, 0)
+#define MT6370_LDOOC_EVT_MASK BIT(7)
+#define MT6370_POSSCP_EVT_MASK BIT(7)
+#define MT6370_NEGSCP_EVT_MASK BIT(6)
+#define MT6370_BSTOCP_EVT_MASK BIT(5)
+#define MT6370_POSOCP_EVT_MASK BIT(4)
+#define MT6370_NEGOCP_EVT_MASK BIT(3)
+
+#define MT6370_LDO_MINUV 1600000
+#define MT6370_LDO_STPUV 200000
+#define MT6370_LDO_N_VOLT 13
+#define MT6370_DBVBOOST_MINUV 4000000
+#define MT6370_DBVBOOST_STPUV 50000
+#define MT6370_DBVBOOST_N_VOLT 45
+#define MT6370_DBVOUT_MINUV 4000000
+#define MT6370_DBVOUT_STPUV 50000
+#define MT6370_DBVOUT_N_VOLT 41
+
+struct mt6370_priv {
+ struct device *dev;
+ struct regmap *regmap;
+ struct regulator_dev *rdev[MT6370_MAX_IDX];
+ bool use_external_ctrl;
+};
+
+static const unsigned int mt6370_vpos_ramp_tbl[] = { 8540, 5840, 4830, 3000 };
+static const unsigned int mt6370_vneg_ramp_tbl[] = { 10090, 6310, 5050, 3150 };
+
+static int mt6370_get_error_flags(struct regulator_dev *rdev,
+ unsigned int *flags)
+{
+ struct regmap *regmap = rdev_get_regmap(rdev);
+ unsigned int stat_reg, stat, rpt_flags = 0;
+ int rid = rdev_get_id(rdev), ret;
+
+ if (rid == MT6370_IDX_VIBLDO)
+ stat_reg = MT6370_REG_LDO_STAT;
+ else
+ stat_reg = MT6370_REG_DB_STAT;
+
+ ret = regmap_read(regmap, stat_reg, &stat);
+ if (ret)
+ return ret;
+
+ switch (rid) {
+ case MT6370_IDX_DSVBOOST:
+ if (stat & MT6370_BSTOCP_EVT_MASK)
+ rpt_flags |= REGULATOR_ERROR_OVER_CURRENT;
+ break;
+ case MT6370_IDX_DSVPOS:
+ if (stat & MT6370_POSSCP_EVT_MASK)
+ rpt_flags |= REGULATOR_ERROR_UNDER_VOLTAGE;
+
+ if (stat & MT6370_POSOCP_EVT_MASK)
+ rpt_flags |= REGULATOR_ERROR_OVER_CURRENT;
+ break;
+ case MT6370_IDX_DSVNEG:
+ if (stat & MT6370_NEGSCP_EVT_MASK)
+ rpt_flags |= REGULATOR_ERROR_UNDER_VOLTAGE;
+
+ if (stat & MT6370_NEGOCP_EVT_MASK)
+ rpt_flags |= REGULATOR_ERROR_OVER_CURRENT;
+ break;
+ default:
+ if (stat & MT6370_LDOOC_EVT_MASK)
+ rpt_flags |= REGULATOR_ERROR_OVER_CURRENT;
+ break;
+ }
+
+ *flags = rpt_flags;
+ return 0;
+}
+
+static const struct regulator_ops mt6370_dbvboost_ops = {
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .list_voltage = regulator_list_voltage_linear,
+ .get_bypass = regulator_get_bypass_regmap,
+ .set_bypass = regulator_set_bypass_regmap,
+ .get_error_flags = mt6370_get_error_flags,
+};
+
+static const struct regulator_ops mt6370_dbvout_ops = {
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .list_voltage = regulator_list_voltage_linear,
+ .is_enabled = regulator_is_enabled_regmap,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .set_active_discharge = regulator_set_active_discharge_regmap,
+ .set_ramp_delay = regulator_set_ramp_delay_regmap,
+ .get_error_flags = mt6370_get_error_flags,
+};
+
+static const struct regulator_ops mt6370_ldo_ops = {
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .list_voltage = regulator_list_voltage_linear,
+ .is_enabled = regulator_is_enabled_regmap,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .set_active_discharge = regulator_set_active_discharge_regmap,
+ .get_error_flags = mt6370_get_error_flags,
+};
+
+static int mt6370_of_parse_cb(struct device_node *np,
+ const struct regulator_desc *desc,
+ struct regulator_config *config)
+{
+ struct mt6370_priv *priv = config->driver_data;
+ struct gpio_desc *enable_gpio;
+ int ret;
+
+ enable_gpio = fwnode_gpiod_get_index(of_fwnode_handle(np), "enable", 0,
+ GPIOD_OUT_HIGH |
+ GPIOD_FLAGS_BIT_NONEXCLUSIVE,
+ desc->name);
+ if (IS_ERR(enable_gpio)) {
+ config->ena_gpiod = NULL;
+ return 0;
+ }
+
+ /*
+ * RG control by default
+ * Only if all are using external pin, change all by external control
+ */
+ if (priv->use_external_ctrl) {
+ ret = regmap_update_bits(priv->regmap, MT6370_REG_DB_CTRL1,
+ MT6370_DBEXTEN_MASK,
+ MT6370_DBEXTEN_MASK);
+ if (ret)
+ return ret;
+ }
+
+ config->ena_gpiod = enable_gpio;
+ priv->use_external_ctrl = true;
+ return 0;
+}
+
+static const struct regulator_desc mt6370_regulator_descs[] = {
+ {
+ .name = "mt6370-dsv-vbst",
+ .of_match = of_match_ptr("dsvbst"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = MT6370_IDX_DSVBOOST,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ .ops = &mt6370_dbvboost_ops,
+ .min_uV = MT6370_DBVBOOST_MINUV,
+ .uV_step = MT6370_DBVBOOST_STPUV,
+ .n_voltages = MT6370_DBVBOOST_N_VOLT,
+ .vsel_reg = MT6370_REG_DB_VBST,
+ .vsel_mask = MT6370_DBVOUT_MASK,
+ .bypass_reg = MT6370_REG_DB_CTRL1,
+ .bypass_mask = MT6370_DBPERD_MASK,
+ .bypass_val_on = MT6370_DBPERD_MASK,
+ },
+ {
+ .name = "mt6370-dsv-vpos",
+ .of_match = of_match_ptr("dsvpos"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = MT6370_IDX_DSVPOS,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ .of_parse_cb = mt6370_of_parse_cb,
+ .ops = &mt6370_dbvout_ops,
+ .min_uV = MT6370_DBVOUT_MINUV,
+ .uV_step = MT6370_DBVOUT_STPUV,
+ .n_voltages = MT6370_DBVOUT_N_VOLT,
+ .vsel_reg = MT6370_REG_DB_VPOS,
+ .vsel_mask = MT6370_DBVOUT_MASK,
+ .enable_reg = MT6370_REG_DB_CTRL2,
+ .enable_mask = MT6370_DBVPOSEN_MASK,
+ .ramp_reg = MT6370_REG_DB_VPOS,
+ .ramp_mask = MT6370_DBSLEW_MASK,
+ .ramp_delay_table = mt6370_vpos_ramp_tbl,
+ .n_ramp_values = ARRAY_SIZE(mt6370_vpos_ramp_tbl),
+ .active_discharge_reg = MT6370_REG_DB_CTRL2,
+ .active_discharge_mask = MT6370_DBVPOSDISG_MASK,
+ .active_discharge_on = MT6370_DBVPOSDISG_MASK,
+ },
+ {
+ .name = "mt6370-dsv-vneg",
+ .of_match = of_match_ptr("dsvneg"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = MT6370_IDX_DSVNEG,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ .of_parse_cb = mt6370_of_parse_cb,
+ .ops = &mt6370_dbvout_ops,
+ .min_uV = MT6370_DBVOUT_MINUV,
+ .uV_step = MT6370_DBVOUT_STPUV,
+ .n_voltages = MT6370_DBVOUT_N_VOLT,
+ .vsel_reg = MT6370_REG_DB_VNEG,
+ .vsel_mask = MT6370_DBVOUT_MASK,
+ .enable_reg = MT6370_REG_DB_CTRL2,
+ .enable_mask = MT6370_DBVNEGEN_MASK,
+ .ramp_reg = MT6370_REG_DB_VNEG,
+ .ramp_mask = MT6370_DBSLEW_MASK,
+ .ramp_delay_table = mt6370_vneg_ramp_tbl,
+ .n_ramp_values = ARRAY_SIZE(mt6370_vneg_ramp_tbl),
+ .active_discharge_reg = MT6370_REG_DB_CTRL2,
+ .active_discharge_mask = MT6370_DBVNEGDISG_MASK,
+ .active_discharge_on = MT6370_DBVNEGDISG_MASK,
+ },
+ {
+ .name = "mt6370-vib-ldo",
+ .of_match = of_match_ptr("vibldo"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = MT6370_IDX_VIBLDO,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ .ops = &mt6370_ldo_ops,
+ .min_uV = MT6370_LDO_MINUV,
+ .uV_step = MT6370_LDO_STPUV,
+ .n_voltages = MT6370_LDO_N_VOLT,
+ .vsel_reg = MT6370_REG_LDO_VOUT,
+ .vsel_mask = MT6370_LDOVOUT_MASK,
+ .enable_reg = MT6370_REG_LDO_VOUT,
+ .enable_mask = MT6370_LDOEN_MASK,
+ .active_discharge_reg = MT6370_REG_LDO_CFG,
+ .active_discharge_mask = MT6370_LDOOMS_MASK,
+ .active_discharge_on = MT6370_LDOOMS_MASK,
+ }
+};
+
+static irqreturn_t mt6370_scp_handler(int irq, void *data)
+{
+ struct regulator_dev *rdev = data;
+
+ regulator_notifier_call_chain(rdev, REGULATOR_EVENT_UNDER_VOLTAGE,
+ NULL);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t mt6370_ocp_handler(int irq, void *data)
+{
+ struct regulator_dev *rdev = data;
+
+ regulator_notifier_call_chain(rdev, REGULATOR_EVENT_OVER_CURRENT, NULL);
+ return IRQ_HANDLED;
+}
+
+static int mt6370_regulator_irq_register(struct mt6370_priv *priv)
+{
+ struct platform_device *pdev = to_platform_device(priv->dev);
+ static const struct {
+ const char *name;
+ int rid;
+ irq_handler_t handler;
+ } mt6370_irqs[] = {
+ { "db_vpos_scp", MT6370_IDX_DSVPOS, mt6370_scp_handler },
+ { "db_vneg_scp", MT6370_IDX_DSVNEG, mt6370_scp_handler },
+ { "db_vbst_ocp", MT6370_IDX_DSVBOOST, mt6370_ocp_handler },
+ { "db_vpos_ocp", MT6370_IDX_DSVPOS, mt6370_ocp_handler },
+ { "db_vneg_ocp", MT6370_IDX_DSVNEG, mt6370_ocp_handler },
+ { "ldo_oc", MT6370_IDX_VIBLDO, mt6370_ocp_handler }
+ };
+ struct regulator_dev *rdev;
+ int i, irq, ret;
+
+ for (i = 0; i < ARRAY_SIZE(mt6370_irqs); i++) {
+ irq = platform_get_irq_byname(pdev, mt6370_irqs[i].name);
+
+ rdev = priv->rdev[mt6370_irqs[i].rid];
+
+ ret = devm_request_threaded_irq(priv->dev, irq, NULL,
+ mt6370_irqs[i].handler, 0,
+ mt6370_irqs[i].name, rdev);
+ if (ret) {
+ dev_err(priv->dev,
+ "Failed to register (%d) interrupt\n", i);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int mt6370_regualtor_register(struct mt6370_priv *priv)
+{
+ struct regulator_dev *rdev;
+ struct regulator_config cfg = {};
+ struct device *parent = priv->dev->parent;
+ int i;
+
+ cfg.dev = parent;
+ cfg.driver_data = priv;
+
+ for (i = 0; i < MT6370_MAX_IDX; i++) {
+ rdev = devm_regulator_register(priv->dev,
+ mt6370_regulator_descs + i,
+ &cfg);
+ if (IS_ERR(rdev)) {
+ dev_err(priv->dev,
+ "Failed to register (%d) regulator\n", i);
+ return PTR_ERR(rdev);
+ }
+
+ priv->rdev[i] = rdev;
+ }
+
+ return 0;
+}
+
+static int mt6370_regulator_probe(struct platform_device *pdev)
+{
+ struct mt6370_priv *priv;
+ int ret;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->dev = &pdev->dev;
+
+ priv->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+ if (!priv->regmap) {
+ dev_err(&pdev->dev, "Failed to init regmap\n");
+ return -ENODEV;
+ }
+
+ ret = mt6370_regualtor_register(priv);
+ if (ret)
+ return ret;
+
+ return mt6370_regulator_irq_register(priv);
+}
+
+static const struct platform_device_id mt6370_devid_table[] = {
+ { "mt6370-regulator", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(platform, mt6370_devid_table);
+
+static struct platform_driver mt6370_regulator_driver = {
+ .driver = {
+ .name = "mt6370-regulator",
+ },
+ .id_table = mt6370_devid_table,
+ .probe = mt6370_regulator_probe,
+};
+module_platform_driver(mt6370_regulator_driver);
+
+MODULE_AUTHOR("ChiYuan Huang <cy_huang@richtek.com>");
+MODULE_DESCRIPTION("Mediatek MT6370 Regulator Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/regulator/qcom_smd-regulator.c b/drivers/regulator/qcom_smd-regulator.c
index 6b8cf7073e9b..59024c639141 100644
--- a/drivers/regulator/qcom_smd-regulator.c
+++ b/drivers/regulator/qcom_smd-regulator.c
@@ -205,6 +205,7 @@ static const struct regulator_ops rpm_mp5496_ops = {
.is_enabled = rpm_reg_is_enabled,
.list_voltage = regulator_list_voltage_linear_range,
+ .get_voltage = rpm_reg_get_voltage,
.set_voltage = rpm_reg_set_voltage,
};
diff --git a/drivers/regulator/rpi-panel-attiny-regulator.c b/drivers/regulator/rpi-panel-attiny-regulator.c
index fa8706a352ce..105f694a67e6 100644
--- a/drivers/regulator/rpi-panel-attiny-regulator.c
+++ b/drivers/regulator/rpi-panel-attiny-regulator.c
@@ -187,15 +187,11 @@ static int attiny_update_status(struct backlight_device *bl)
{
struct attiny_lcd *state = bl_get_data(bl);
struct regmap *regmap = state->regmap;
- int brightness = bl->props.brightness;
+ int brightness = backlight_get_brightness(bl);
int ret, i;
mutex_lock(&state->lock);
- if (bl->props.power != FB_BLANK_UNBLANK ||
- bl->props.fb_blank != FB_BLANK_UNBLANK)
- brightness = 0;
-
for (i = 0; i < 10; i++) {
ret = regmap_write(regmap, REG_PWM, brightness);
if (!ret)
diff --git a/drivers/regulator/rt5120-regulator.c b/drivers/regulator/rt5120-regulator.c
new file mode 100644
index 000000000000..8173ede09414
--- /dev/null
+++ b/drivers/regulator/rt5120-regulator.c
@@ -0,0 +1,420 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+#include <linux/bits.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+
+#define RT5120_REG_PGSTAT 0x03
+#define RT5120_REG_CH1VID 0x06
+#define RT5120_REG_CH1SLPVID 0x07
+#define RT5120_REG_ENABLE 0x08
+#define RT5120_REG_MODECTL 0x09
+#define RT5120_REG_UVOVPROT 0x0A
+#define RT5120_REG_SLPCTL 0x0C
+#define RT5120_REG_INTSTAT 0x1E
+#define RT5120_REG_DISCHG 0x1F
+
+#define RT5120_OUTPG_MASK(rid) BIT(rid + 1)
+#define RT5120_OUTUV_MASK(rid) BIT(rid + 9)
+#define RT5120_OUTOV_MASK(rid) BIT(rid + 16)
+#define RT5120_CH1VID_MASK GENMASK(6, 0)
+#define RT5120_RIDEN_MASK(rid) BIT(rid + 1)
+#define RT5120_RADEN_MASK(rid) BIT(rid)
+#define RT5120_FPWM_MASK(rid) BIT(rid + 1)
+#define RT5120_UVHICCUP_MASK BIT(1)
+#define RT5120_OVHICCUP_MASK BIT(0)
+#define RT5120_HOTDIE_MASK BIT(1)
+
+#define RT5120_BUCK1_MINUV 600000
+#define RT5120_BUCK1_MAXUV 1393750
+#define RT5120_BUCK1_STEPUV 6250
+#define RT5120_BUCK1_NUM_VOLT 0x80
+
+#define RT5120_AUTO_MODE 0
+#define RT5120_FPWM_MODE 1
+
+enum {
+ RT5120_REGULATOR_BUCK1 = 0,
+ RT5120_REGULATOR_BUCK2,
+ RT5120_REGULATOR_BUCK3,
+ RT5120_REGULATOR_BUCK4,
+ RT5120_REGULATOR_LDO,
+ RT5120_REGULATOR_EXTEN,
+ RT5120_MAX_REGULATOR
+};
+
+struct rt5120_priv {
+ struct device *dev;
+ struct regmap *regmap;
+ struct regulator_desc rdesc[RT5120_MAX_REGULATOR];
+};
+
+static int rt5120_buck_set_mode(struct regulator_dev *rdev, unsigned int mode)
+{
+ struct regmap *regmap = rdev_get_regmap(rdev);
+ int rid = rdev_get_id(rdev);
+ unsigned int mask = RT5120_FPWM_MASK(rid), val;
+
+ switch (mode) {
+ case REGULATOR_MODE_NORMAL:
+ val = 0;
+ break;
+ case REGULATOR_MODE_FAST:
+ val = RT5120_FPWM_MASK(rid);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return regmap_update_bits(regmap, RT5120_REG_MODECTL, mask, val);
+}
+
+static unsigned int rt5120_buck_get_mode(struct regulator_dev *rdev)
+{
+ struct regmap *regmap = rdev_get_regmap(rdev);
+ int ret, rid = rdev_get_id(rdev);
+ unsigned int val;
+
+ ret = regmap_read(regmap, RT5120_REG_MODECTL, &val);
+ if (ret)
+ return REGULATOR_MODE_INVALID;
+
+ if (val & RT5120_FPWM_MASK(rid))
+ return REGULATOR_MODE_FAST;
+
+ return REGULATOR_MODE_NORMAL;
+}
+
+static int rt5120_regulator_get_error_flags(struct regulator_dev *rdev,
+ unsigned int *flags)
+{
+ struct regmap *regmap = rdev_get_regmap(rdev);
+ unsigned int stat, hd_stat, cur_flags = 0;
+ int rid = rdev_get_id(rdev), ret;
+
+ /*
+ * reg 0x03/0x04/0x05 to indicate PG/UV/OV
+ * use block read to descrease I/O xfer time
+ */
+ ret = regmap_raw_read(regmap, RT5120_REG_PGSTAT, &stat, 3);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(regmap, RT5120_REG_INTSTAT, &hd_stat);
+ if (ret)
+ return ret;
+
+ if (!(stat & RT5120_OUTPG_MASK(rid))) {
+ if (stat & RT5120_OUTUV_MASK(rid))
+ cur_flags |= REGULATOR_ERROR_UNDER_VOLTAGE;
+
+ if (stat & RT5120_OUTOV_MASK(rid))
+ cur_flags |= REGULATOR_ERROR_REGULATION_OUT;
+ }
+
+ if (hd_stat & RT5120_HOTDIE_MASK)
+ cur_flags |= REGULATOR_ERROR_OVER_TEMP;
+
+ *flags = cur_flags;
+ return 0;
+}
+
+static int rt5120_buck1_set_suspend_voltage(struct regulator_dev *rdev, int uV)
+{
+ struct regmap *regmap = rdev_get_regmap(rdev);
+ int sel;
+
+ if (uV < RT5120_BUCK1_MINUV || uV > RT5120_BUCK1_MAXUV)
+ return -EINVAL;
+
+ sel = (uV - RT5120_BUCK1_MINUV) / RT5120_BUCK1_STEPUV;
+ return regmap_write(regmap, RT5120_REG_CH1SLPVID, sel);
+}
+
+static int rt5120_regulator_set_suspend_enable(struct regulator_dev *rdev)
+{
+ struct regmap *regmap = rdev_get_regmap(rdev);
+ int rid = rdev_get_id(rdev);
+ unsigned int mask = RT5120_RIDEN_MASK(rid);
+
+ return regmap_update_bits(regmap, RT5120_REG_SLPCTL, mask, mask);
+}
+
+static int rt5120_regulator_set_suspend_disable(struct regulator_dev *rdev)
+{
+ struct regmap *regmap = rdev_get_regmap(rdev);
+ int rid = rdev_get_id(rdev);
+ unsigned int mask = RT5120_RIDEN_MASK(rid);
+
+ return regmap_update_bits(regmap, RT5120_REG_SLPCTL, mask, 0);
+}
+
+static const struct regulator_ops rt5120_buck1_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .list_voltage = regulator_list_voltage_linear,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_active_discharge = regulator_set_active_discharge_regmap,
+ .set_mode = rt5120_buck_set_mode,
+ .get_mode = rt5120_buck_get_mode,
+ .get_error_flags = rt5120_regulator_get_error_flags,
+ .set_suspend_voltage = rt5120_buck1_set_suspend_voltage,
+ .set_suspend_enable = rt5120_regulator_set_suspend_enable,
+ .set_suspend_disable = rt5120_regulator_set_suspend_disable,
+};
+
+static const struct regulator_ops rt5120_buck234_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .set_active_discharge = regulator_set_active_discharge_regmap,
+ .set_mode = rt5120_buck_set_mode,
+ .get_mode = rt5120_buck_get_mode,
+ .get_error_flags = rt5120_regulator_get_error_flags,
+ .set_suspend_enable = rt5120_regulator_set_suspend_enable,
+ .set_suspend_disable = rt5120_regulator_set_suspend_disable,
+};
+
+static const struct regulator_ops rt5120_ldo_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .set_active_discharge = regulator_set_active_discharge_regmap,
+ .get_error_flags = rt5120_regulator_get_error_flags,
+ .set_suspend_enable = rt5120_regulator_set_suspend_enable,
+ .set_suspend_disable = rt5120_regulator_set_suspend_disable,
+};
+
+static const struct regulator_ops rt5120_exten_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .set_suspend_enable = rt5120_regulator_set_suspend_enable,
+ .set_suspend_disable = rt5120_regulator_set_suspend_disable,
+};
+
+static unsigned int rt5120_buck_of_map_mode(unsigned int mode)
+{
+ switch (mode) {
+ case RT5120_AUTO_MODE:
+ return REGULATOR_MODE_NORMAL;
+ case RT5120_FPWM_MODE:
+ return REGULATOR_MODE_FAST;
+ default:
+ return REGULATOR_MODE_INVALID;
+ }
+}
+
+static void rt5120_fillin_regulator_desc(struct regulator_desc *desc, int rid)
+{
+ static const char * const name[] = {
+ "buck1", "buck2", "buck3", "buck4", "ldo", "exten" };
+ static const char * const sname[] = {
+ "vin1", "vin2", "vin3", "vin4", "vinldo", NULL };
+
+ /* Common regulator property */
+ desc->name = name[rid];
+ desc->supply_name = sname[rid];
+ desc->owner = THIS_MODULE;
+ desc->type = REGULATOR_VOLTAGE;
+ desc->id = rid;
+ desc->enable_reg = RT5120_REG_ENABLE;
+ desc->enable_mask = RT5120_RIDEN_MASK(rid);
+ desc->active_discharge_reg = RT5120_REG_DISCHG;
+ desc->active_discharge_mask = RT5120_RADEN_MASK(rid);
+ desc->active_discharge_on = RT5120_RADEN_MASK(rid);
+ /* Config n_voltages to 1 for all*/
+ desc->n_voltages = 1;
+
+ /* Only buck support mode change */
+ if (rid >= RT5120_REGULATOR_BUCK1 && rid <= RT5120_REGULATOR_BUCK4)
+ desc->of_map_mode = rt5120_buck_of_map_mode;
+
+ /* RID specific property init */
+ switch (rid) {
+ case RT5120_REGULATOR_BUCK1:
+ /* Only buck1 support voltage change by I2C */
+ desc->n_voltages = RT5120_BUCK1_NUM_VOLT;
+ desc->min_uV = RT5120_BUCK1_MINUV;
+ desc->uV_step = RT5120_BUCK1_STEPUV;
+ desc->vsel_reg = RT5120_REG_CH1VID,
+ desc->vsel_mask = RT5120_CH1VID_MASK,
+ desc->ops = &rt5120_buck1_ops;
+ break;
+ case RT5120_REGULATOR_BUCK2 ... RT5120_REGULATOR_BUCK4:
+ desc->ops = &rt5120_buck234_ops;
+ break;
+ case RT5120_REGULATOR_LDO:
+ desc->ops = &rt5120_ldo_ops;
+ break;
+ default:
+ desc->ops = &rt5120_exten_ops;
+ }
+}
+
+static int rt5120_of_parse_cb(struct rt5120_priv *priv, int rid,
+ struct of_regulator_match *match)
+{
+ struct regulator_desc *desc = priv->rdesc + rid;
+ struct regulator_init_data *init_data = match->init_data;
+
+ if (!init_data || rid == RT5120_REGULATOR_BUCK1)
+ return 0;
+
+ if (init_data->constraints.min_uV != init_data->constraints.max_uV) {
+ dev_err(priv->dev, "Variable voltage for fixed regulator\n");
+ return -EINVAL;
+ }
+
+ desc->fixed_uV = init_data->constraints.min_uV;
+ return 0;
+}
+
+static struct of_regulator_match rt5120_regu_match[RT5120_MAX_REGULATOR] = {
+ [RT5120_REGULATOR_BUCK1] = { .name = "buck1", },
+ [RT5120_REGULATOR_BUCK2] = { .name = "buck2", },
+ [RT5120_REGULATOR_BUCK3] = { .name = "buck3", },
+ [RT5120_REGULATOR_BUCK4] = { .name = "buck4", },
+ [RT5120_REGULATOR_LDO] = { .name = "ldo", },
+ [RT5120_REGULATOR_EXTEN] = { .name = "exten", }
+};
+
+static int rt5120_parse_regulator_dt_data(struct rt5120_priv *priv)
+{
+ struct device *dev = priv->dev->parent;
+ struct device_node *reg_node;
+ int i, ret;
+
+ for (i = 0; i < RT5120_MAX_REGULATOR; i++) {
+ rt5120_fillin_regulator_desc(priv->rdesc + i, i);
+
+ rt5120_regu_match[i].desc = priv->rdesc + i;
+ }
+
+ reg_node = of_get_child_by_name(dev->of_node, "regulators");
+ if (!reg_node) {
+ dev_err(priv->dev, "Couldn't find 'regulators' node\n");
+ return -ENODEV;
+ }
+
+ ret = of_regulator_match(priv->dev, reg_node, rt5120_regu_match,
+ ARRAY_SIZE(rt5120_regu_match));
+
+ of_node_put(reg_node);
+
+ if (ret < 0) {
+ dev_err(priv->dev,
+ "Error parsing regulator init data (%d)\n", ret);
+ return ret;
+ }
+
+ for (i = 0; i < RT5120_MAX_REGULATOR; i++) {
+ ret = rt5120_of_parse_cb(priv, i, rt5120_regu_match + i);
+ if (ret) {
+ dev_err(priv->dev, "Failed in [%d] of_passe_cb\n", i);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int rt5120_device_property_init(struct rt5120_priv *priv)
+{
+ struct device *dev = priv->dev->parent;
+ struct device_node *np = dev->of_node;
+ bool prot_enable;
+ unsigned int prot_enable_val = 0;
+
+ /* Assign UV/OV HW protection behavior */
+ prot_enable = of_property_read_bool(np,
+ "richtek,enable-undervolt-hiccup");
+ if (prot_enable)
+ prot_enable_val |= RT5120_UVHICCUP_MASK;
+
+ prot_enable = of_property_read_bool(np,
+ "richtek,enable-overvolt-hiccup");
+ if (prot_enable)
+ prot_enable_val |= RT5120_OVHICCUP_MASK;
+
+ return regmap_update_bits(priv->regmap, RT5120_REG_UVOVPROT,
+ RT5120_UVHICCUP_MASK | RT5120_OVHICCUP_MASK,
+ prot_enable_val);
+}
+
+static int rt5120_regulator_probe(struct platform_device *pdev)
+{
+ struct rt5120_priv *priv;
+ struct regulator_dev *rdev;
+ struct regulator_config config = {};
+ int i, ret;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->dev = &pdev->dev;
+
+ priv->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+ if (!priv->regmap) {
+ dev_err(&pdev->dev, "Failed to init regmap\n");
+ return -ENODEV;
+ }
+
+ ret = rt5120_device_property_init(priv);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to do property init\n");
+ return ret;
+ }
+
+ ret = rt5120_parse_regulator_dt_data(priv);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to parse dt data\n");
+ return ret;
+ }
+
+ config.dev = &pdev->dev;
+ config.regmap = priv->regmap;
+
+ for (i = 0; i < RT5120_MAX_REGULATOR; i++) {
+ config.of_node = rt5120_regu_match[i].of_node;
+ config.init_data = rt5120_regu_match[i].init_data;
+
+ rdev = devm_regulator_register(&pdev->dev, priv->rdesc + i,
+ &config);
+ if (IS_ERR(rdev)) {
+ dev_err(&pdev->dev,
+ "Failed to register regulator [%d]\n", i);
+ return PTR_ERR(rdev);
+ }
+ }
+
+ return 0;
+}
+
+static const struct platform_device_id rt5120_regulator_dev_table[] = {
+ { "rt5120-regulator", 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(platform, rt5120_regulator_dev_table);
+
+static struct platform_driver rt5120_regulator_driver = {
+ .driver = {
+ .name = "rt5120-regulator",
+ },
+ .id_table = rt5120_regulator_dev_table,
+ .probe = rt5120_regulator_probe,
+};
+module_platform_driver(rt5120_regulator_driver);
+
+MODULE_AUTHOR("ChiYuan Huang <cy_huang@richtek.com>");
+MODULE_DESCRIPTION("Richtek RT5120 regulator driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/regulator/rt5190a-regulator.c b/drivers/regulator/rt5190a-regulator.c
index 155d4afd00b1..4a3397b32582 100644
--- a/drivers/regulator/rt5190a-regulator.c
+++ b/drivers/regulator/rt5190a-regulator.c
@@ -224,6 +224,9 @@ static int rt5190a_of_parse_cb(struct rt5190a_priv *priv, int rid,
bool latchup_enable;
unsigned int mask = RT5190A_RID_BITMASK(rid), val;
+ if (!init_data)
+ return 0;
+
switch (rid) {
case RT5190A_IDX_BUCK1:
case RT5190A_IDX_BUCK4:
diff --git a/drivers/regulator/scmi-regulator.c b/drivers/regulator/scmi-regulator.c
index 41ae7ac27ff6..b9918f4fd241 100644
--- a/drivers/regulator/scmi-regulator.c
+++ b/drivers/regulator/scmi-regulator.c
@@ -343,6 +343,7 @@ static int scmi_regulator_probe(struct scmi_device *sdev)
* plausible SCMI Voltage Domain number, all belonging to this SCMI
* platform instance node (handle->dev->of_node).
*/
+ of_node_get(handle->dev->of_node);
np = of_find_node_by_name(handle->dev->of_node, "regulators");
for_each_child_of_node(np, child) {
ret = process_scmi_regulator_of_node(sdev, ph, child, rinfo);