19351ab8bSChiYuan Huang // SPDX-License-Identifier: GPL-2.0-only
29351ab8bSChiYuan Huang 
39351ab8bSChiYuan Huang #include <linux/bitops.h>
49351ab8bSChiYuan Huang #include <linux/kernel.h>
59351ab8bSChiYuan Huang #include <linux/module.h>
69351ab8bSChiYuan Huang #include <linux/of.h>
79351ab8bSChiYuan Huang #include <linux/platform_device.h>
89351ab8bSChiYuan Huang #include <linux/regmap.h>
99351ab8bSChiYuan Huang #include <linux/regulator/consumer.h>
109351ab8bSChiYuan Huang #include <linux/regulator/driver.h>
119351ab8bSChiYuan Huang 
129351ab8bSChiYuan Huang enum {
139351ab8bSChiYuan Huang 	DSV_OUT_VLCM = 0,
149351ab8bSChiYuan Huang 	DSV_OUT_VPOS,
159351ab8bSChiYuan Huang 	DSV_OUT_VNEG,
169351ab8bSChiYuan Huang 	DSV_OUT_MAX
179351ab8bSChiYuan Huang };
189351ab8bSChiYuan Huang 
199351ab8bSChiYuan Huang #define RT4831_REG_DSVEN	0x09
209351ab8bSChiYuan Huang #define RT4831_REG_VLCM		0x0c
219351ab8bSChiYuan Huang #define RT4831_REG_VPOS		0x0d
229351ab8bSChiYuan Huang #define RT4831_REG_VNEG		0x0e
239351ab8bSChiYuan Huang #define RT4831_REG_FLAGS	0x0f
249351ab8bSChiYuan Huang 
259351ab8bSChiYuan Huang #define RT4831_VOLT_MASK	GENMASK(5, 0)
269351ab8bSChiYuan Huang #define RT4831_DSVMODE_SHIFT	5
279351ab8bSChiYuan Huang #define RT4831_DSVMODE_MASK	GENMASK(7, 5)
289351ab8bSChiYuan Huang #define RT4831_POSADEN_MASK	BIT(4)
299351ab8bSChiYuan Huang #define RT4831_NEGADEN_MASK	BIT(3)
309351ab8bSChiYuan Huang #define RT4831_POSEN_MASK	BIT(2)
319351ab8bSChiYuan Huang #define RT4831_NEGEN_MASK	BIT(1)
329351ab8bSChiYuan Huang 
339351ab8bSChiYuan Huang #define RT4831_OTP_MASK		BIT(6)
349351ab8bSChiYuan Huang #define RT4831_LCMOVP_MASK	BIT(5)
359351ab8bSChiYuan Huang #define RT4831_VPOSSCP_MASK	BIT(3)
369351ab8bSChiYuan Huang #define RT4831_VNEGSCP_MASK	BIT(2)
379351ab8bSChiYuan Huang 
389351ab8bSChiYuan Huang #define DSV_MODE_NORMAL		(0x4 << RT4831_DSVMODE_SHIFT)
399351ab8bSChiYuan Huang #define DSV_MODE_BYPASS		(0x6 << RT4831_DSVMODE_SHIFT)
409351ab8bSChiYuan Huang #define STEP_UV			50000
419351ab8bSChiYuan Huang #define VLCM_MIN_UV		4000000
429351ab8bSChiYuan Huang #define VLCM_MAX_UV		7150000
439351ab8bSChiYuan Huang #define VLCM_N_VOLTAGES		((VLCM_MAX_UV - VLCM_MIN_UV) / STEP_UV + 1)
449351ab8bSChiYuan Huang #define VPN_MIN_UV		4000000
459351ab8bSChiYuan Huang #define VPN_MAX_UV		6500000
469351ab8bSChiYuan Huang #define VPN_N_VOLTAGES		((VPN_MAX_UV - VPN_MIN_UV) / STEP_UV + 1)
479351ab8bSChiYuan Huang 
rt4831_get_error_flags(struct regulator_dev * rdev,unsigned int * flags)489351ab8bSChiYuan Huang static int rt4831_get_error_flags(struct regulator_dev *rdev, unsigned int *flags)
499351ab8bSChiYuan Huang {
509351ab8bSChiYuan Huang 	struct regmap *regmap = rdev_get_regmap(rdev);
519351ab8bSChiYuan Huang 	int rid = rdev_get_id(rdev);
529351ab8bSChiYuan Huang 	unsigned int val, events = 0;
539351ab8bSChiYuan Huang 	int ret;
549351ab8bSChiYuan Huang 
559351ab8bSChiYuan Huang 	ret = regmap_read(regmap, RT4831_REG_FLAGS, &val);
569351ab8bSChiYuan Huang 	if (ret)
579351ab8bSChiYuan Huang 		return ret;
589351ab8bSChiYuan Huang 
599351ab8bSChiYuan Huang 	if (val & RT4831_OTP_MASK)
609351ab8bSChiYuan Huang 		events |= REGULATOR_ERROR_OVER_TEMP;
619351ab8bSChiYuan Huang 
629351ab8bSChiYuan Huang 	if (rid == DSV_OUT_VLCM && (val & RT4831_LCMOVP_MASK))
639351ab8bSChiYuan Huang 		events |= REGULATOR_ERROR_OVER_CURRENT;
649351ab8bSChiYuan Huang 
659351ab8bSChiYuan Huang 	if (rid == DSV_OUT_VPOS && (val & RT4831_VPOSSCP_MASK))
669351ab8bSChiYuan Huang 		events |= REGULATOR_ERROR_OVER_CURRENT;
679351ab8bSChiYuan Huang 
689351ab8bSChiYuan Huang 	if (rid == DSV_OUT_VNEG && (val & RT4831_VNEGSCP_MASK))
699351ab8bSChiYuan Huang 		events |= REGULATOR_ERROR_OVER_CURRENT;
709351ab8bSChiYuan Huang 
719351ab8bSChiYuan Huang 	*flags = events;
729351ab8bSChiYuan Huang 	return 0;
739351ab8bSChiYuan Huang }
749351ab8bSChiYuan Huang 
759351ab8bSChiYuan Huang static const struct regulator_ops rt4831_dsvlcm_ops = {
769351ab8bSChiYuan Huang 	.list_voltage = regulator_list_voltage_linear,
779351ab8bSChiYuan Huang 	.set_voltage_sel = regulator_set_voltage_sel_regmap,
789351ab8bSChiYuan Huang 	.get_voltage_sel = regulator_get_voltage_sel_regmap,
799351ab8bSChiYuan Huang 	.set_bypass = regulator_set_bypass_regmap,
809351ab8bSChiYuan Huang 	.get_bypass = regulator_get_bypass_regmap,
819351ab8bSChiYuan Huang 	.get_error_flags = rt4831_get_error_flags,
829351ab8bSChiYuan Huang };
839351ab8bSChiYuan Huang 
849351ab8bSChiYuan Huang static const struct regulator_ops rt4831_dsvpn_ops = {
859351ab8bSChiYuan Huang 	.list_voltage = regulator_list_voltage_linear,
869351ab8bSChiYuan Huang 	.set_voltage_sel = regulator_set_voltage_sel_regmap,
879351ab8bSChiYuan Huang 	.get_voltage_sel = regulator_get_voltage_sel_regmap,
889351ab8bSChiYuan Huang 	.enable = regulator_enable_regmap,
899351ab8bSChiYuan Huang 	.disable = regulator_disable_regmap,
909351ab8bSChiYuan Huang 	.is_enabled = regulator_is_enabled_regmap,
919351ab8bSChiYuan Huang 	.set_active_discharge = regulator_set_active_discharge_regmap,
929351ab8bSChiYuan Huang 	.get_error_flags = rt4831_get_error_flags,
939351ab8bSChiYuan Huang };
949351ab8bSChiYuan Huang 
959351ab8bSChiYuan Huang static const struct regulator_desc rt4831_regulator_descs[] = {
969351ab8bSChiYuan Huang 	{
979351ab8bSChiYuan Huang 		.name = "DSVLCM",
989351ab8bSChiYuan Huang 		.ops = &rt4831_dsvlcm_ops,
999351ab8bSChiYuan Huang 		.of_match = of_match_ptr("DSVLCM"),
1009351ab8bSChiYuan Huang 		.regulators_node = of_match_ptr("regulators"),
1019351ab8bSChiYuan Huang 		.type = REGULATOR_VOLTAGE,
1029351ab8bSChiYuan Huang 		.id = DSV_OUT_VLCM,
1039351ab8bSChiYuan Huang 		.n_voltages = VLCM_N_VOLTAGES,
1049351ab8bSChiYuan Huang 		.min_uV = VLCM_MIN_UV,
1059351ab8bSChiYuan Huang 		.uV_step = STEP_UV,
1069351ab8bSChiYuan Huang 		.vsel_reg = RT4831_REG_VLCM,
1079351ab8bSChiYuan Huang 		.vsel_mask = RT4831_VOLT_MASK,
1089351ab8bSChiYuan Huang 		.bypass_reg = RT4831_REG_DSVEN,
109184ae0e3SChiYuan Huang 		.bypass_mask = RT4831_DSVMODE_MASK,
1109351ab8bSChiYuan Huang 		.bypass_val_on = DSV_MODE_BYPASS,
1119351ab8bSChiYuan Huang 		.bypass_val_off = DSV_MODE_NORMAL,
1125c42903eSAxel Lin 		.owner = THIS_MODULE,
1139351ab8bSChiYuan Huang 	},
1149351ab8bSChiYuan Huang 	{
1159351ab8bSChiYuan Huang 		.name = "DSVP",
1169351ab8bSChiYuan Huang 		.ops = &rt4831_dsvpn_ops,
1179351ab8bSChiYuan Huang 		.of_match = of_match_ptr("DSVP"),
1189351ab8bSChiYuan Huang 		.regulators_node = of_match_ptr("regulators"),
1199351ab8bSChiYuan Huang 		.type = REGULATOR_VOLTAGE,
1209351ab8bSChiYuan Huang 		.id = DSV_OUT_VPOS,
1219351ab8bSChiYuan Huang 		.n_voltages = VPN_N_VOLTAGES,
1229351ab8bSChiYuan Huang 		.min_uV = VPN_MIN_UV,
1239351ab8bSChiYuan Huang 		.uV_step = STEP_UV,
1249351ab8bSChiYuan Huang 		.vsel_reg = RT4831_REG_VPOS,
1259351ab8bSChiYuan Huang 		.vsel_mask = RT4831_VOLT_MASK,
1269351ab8bSChiYuan Huang 		.enable_reg = RT4831_REG_DSVEN,
1279351ab8bSChiYuan Huang 		.enable_mask = RT4831_POSEN_MASK,
1289351ab8bSChiYuan Huang 		.active_discharge_reg = RT4831_REG_DSVEN,
1299351ab8bSChiYuan Huang 		.active_discharge_mask = RT4831_POSADEN_MASK,
130aefe5fc3SChiYuan Huang 		.active_discharge_on = RT4831_POSADEN_MASK,
1315c42903eSAxel Lin 		.owner = THIS_MODULE,
1329351ab8bSChiYuan Huang 	},
1339351ab8bSChiYuan Huang 	{
1349351ab8bSChiYuan Huang 		.name = "DSVN",
1359351ab8bSChiYuan Huang 		.ops = &rt4831_dsvpn_ops,
1369351ab8bSChiYuan Huang 		.of_match = of_match_ptr("DSVN"),
1379351ab8bSChiYuan Huang 		.regulators_node = of_match_ptr("regulators"),
1389351ab8bSChiYuan Huang 		.type = REGULATOR_VOLTAGE,
1399351ab8bSChiYuan Huang 		.id = DSV_OUT_VNEG,
1409351ab8bSChiYuan Huang 		.n_voltages = VPN_N_VOLTAGES,
1419351ab8bSChiYuan Huang 		.min_uV = VPN_MIN_UV,
1429351ab8bSChiYuan Huang 		.uV_step = STEP_UV,
1439351ab8bSChiYuan Huang 		.vsel_reg = RT4831_REG_VNEG,
1449351ab8bSChiYuan Huang 		.vsel_mask = RT4831_VOLT_MASK,
1459351ab8bSChiYuan Huang 		.enable_reg = RT4831_REG_DSVEN,
1469351ab8bSChiYuan Huang 		.enable_mask = RT4831_NEGEN_MASK,
1479351ab8bSChiYuan Huang 		.active_discharge_reg = RT4831_REG_DSVEN,
1489351ab8bSChiYuan Huang 		.active_discharge_mask = RT4831_NEGADEN_MASK,
149aefe5fc3SChiYuan Huang 		.active_discharge_on = RT4831_NEGADEN_MASK,
1505c42903eSAxel Lin 		.owner = THIS_MODULE,
1519351ab8bSChiYuan Huang 	}
1529351ab8bSChiYuan Huang };
1539351ab8bSChiYuan Huang 
rt4831_regulator_probe(struct platform_device * pdev)1549351ab8bSChiYuan Huang static int rt4831_regulator_probe(struct platform_device *pdev)
1559351ab8bSChiYuan Huang {
1569351ab8bSChiYuan Huang 	struct regmap *regmap;
1579351ab8bSChiYuan Huang 	struct regulator_dev *rdev;
1589351ab8bSChiYuan Huang 	struct regulator_config config = {};
1599351ab8bSChiYuan Huang 	int i, ret;
1609351ab8bSChiYuan Huang 
1619351ab8bSChiYuan Huang 	regmap = dev_get_regmap(pdev->dev.parent, NULL);
162e9e7fce0SWei Yongjun 	if (!regmap) {
1639351ab8bSChiYuan Huang 		dev_err(&pdev->dev, "Failed to init regmap\n");
164e9e7fce0SWei Yongjun 		return -ENODEV;
1659351ab8bSChiYuan Huang 	}
1669351ab8bSChiYuan Huang 
1679351ab8bSChiYuan Huang 	/* Configure DSV mode to normal by default */
1689351ab8bSChiYuan Huang 	ret = regmap_update_bits(regmap, RT4831_REG_DSVEN, RT4831_DSVMODE_MASK, DSV_MODE_NORMAL);
1699351ab8bSChiYuan Huang 	if (ret) {
1709351ab8bSChiYuan Huang 		dev_err(&pdev->dev, "Failed to configure dsv mode to normal\n");
1719351ab8bSChiYuan Huang 		return ret;
1729351ab8bSChiYuan Huang 	}
1739351ab8bSChiYuan Huang 
1749351ab8bSChiYuan Huang 	config.dev = pdev->dev.parent;
1759351ab8bSChiYuan Huang 	config.regmap = regmap;
1769351ab8bSChiYuan Huang 
1779351ab8bSChiYuan Huang 	for (i = 0; i < DSV_OUT_MAX; i++) {
1789351ab8bSChiYuan Huang 		rdev = devm_regulator_register(&pdev->dev, rt4831_regulator_descs + i, &config);
1799351ab8bSChiYuan Huang 		if (IS_ERR(rdev)) {
1809351ab8bSChiYuan Huang 			dev_err(&pdev->dev, "Failed to register %d regulator\n", i);
1819351ab8bSChiYuan Huang 			return PTR_ERR(rdev);
1829351ab8bSChiYuan Huang 		}
1839351ab8bSChiYuan Huang 	}
1849351ab8bSChiYuan Huang 
1859351ab8bSChiYuan Huang 	return 0;
1869351ab8bSChiYuan Huang }
1879351ab8bSChiYuan Huang 
1889351ab8bSChiYuan Huang static const struct platform_device_id rt4831_regulator_match[] = {
1899351ab8bSChiYuan Huang 	{ "rt4831-regulator", 0 },
1909351ab8bSChiYuan Huang 	{}
1919351ab8bSChiYuan Huang };
1929351ab8bSChiYuan Huang MODULE_DEVICE_TABLE(platform, rt4831_regulator_match);
1939351ab8bSChiYuan Huang 
1949351ab8bSChiYuan Huang static struct platform_driver rt4831_regulator_driver = {
1959351ab8bSChiYuan Huang 	.driver = {
1969351ab8bSChiYuan Huang 		.name = "rt4831-regulator",
197*46600ab1SDouglas Anderson 		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
1989351ab8bSChiYuan Huang 	},
1999351ab8bSChiYuan Huang 	.id_table = rt4831_regulator_match,
2009351ab8bSChiYuan Huang 	.probe = rt4831_regulator_probe,
2019351ab8bSChiYuan Huang };
2029351ab8bSChiYuan Huang module_platform_driver(rt4831_regulator_driver);
2039351ab8bSChiYuan Huang 
2049351ab8bSChiYuan Huang MODULE_AUTHOR("ChiYuan Huang <cy_huang@richtek.com>");
2059351ab8bSChiYuan Huang MODULE_LICENSE("GPL v2");
206