18b908520SAxel Lin // SPDX-License-Identifier: GPL-2.0
28b908520SAxel Lin //
38b908520SAxel Lin // Device driver for regulators in Hi6421V530 IC
48b908520SAxel Lin //
58b908520SAxel Lin // Copyright (c) <2017> HiSilicon Technologies Co., Ltd.
68b908520SAxel Lin //              http://www.hisilicon.com
78b908520SAxel Lin // Copyright (c) <2017> Linaro Ltd.
82ca76b3eSAlexander A. Klimov //              https://www.linaro.org
98b908520SAxel Lin //
108b908520SAxel Lin // Author: Wang Xiaoyin <hw.wangxiaoyin@hisilicon.com>
118b908520SAxel Lin //         Guodong Xu <guodong.xu@linaro.org>
125c7024aeSWang Xiaoyin 
135c7024aeSWang Xiaoyin #include <linux/mfd/hi6421-pmic.h>
145c7024aeSWang Xiaoyin #include <linux/module.h>
155c7024aeSWang Xiaoyin #include <linux/of.h>
165c7024aeSWang Xiaoyin #include <linux/platform_device.h>
175c7024aeSWang Xiaoyin #include <linux/regmap.h>
185c7024aeSWang Xiaoyin #include <linux/regulator/driver.h>
195c7024aeSWang Xiaoyin 
205c7024aeSWang Xiaoyin /*
215c7024aeSWang Xiaoyin  * struct hi6421v530_regulator_info - hi6421v530 regulator information
225c7024aeSWang Xiaoyin  * @desc: regulator description
235c7024aeSWang Xiaoyin  * @mode_mask: ECO mode bitmask of LDOs; for BUCKs, this masks sleep
245c7024aeSWang Xiaoyin  * @eco_microamp: eco mode load upper limit (in uA), valid for LDOs only
255c7024aeSWang Xiaoyin  */
265c7024aeSWang Xiaoyin struct hi6421v530_regulator_info {
275c7024aeSWang Xiaoyin 	struct regulator_desc rdesc;
285c7024aeSWang Xiaoyin 	u8 mode_mask;
295c7024aeSWang Xiaoyin 	u32 eco_microamp;
305c7024aeSWang Xiaoyin };
315c7024aeSWang Xiaoyin 
325c7024aeSWang Xiaoyin /* HI6421v530 regulators */
335c7024aeSWang Xiaoyin enum hi6421v530_regulator_id {
345c7024aeSWang Xiaoyin 	HI6421V530_LDO3,
355c7024aeSWang Xiaoyin 	HI6421V530_LDO9,
365c7024aeSWang Xiaoyin 	HI6421V530_LDO11,
375c7024aeSWang Xiaoyin 	HI6421V530_LDO15,
385c7024aeSWang Xiaoyin 	HI6421V530_LDO16,
395c7024aeSWang Xiaoyin };
405c7024aeSWang Xiaoyin 
415c7024aeSWang Xiaoyin static const unsigned int ldo_3_voltages[] = {
425c7024aeSWang Xiaoyin 	1800000, 1825000, 1850000, 1875000,
435c7024aeSWang Xiaoyin 	1900000, 1925000, 1950000, 1975000,
445c7024aeSWang Xiaoyin 	2000000, 2025000, 2050000, 2075000,
455c7024aeSWang Xiaoyin 	2100000, 2125000, 2150000, 2200000,
465c7024aeSWang Xiaoyin };
475c7024aeSWang Xiaoyin 
485c7024aeSWang Xiaoyin static const unsigned int ldo_9_11_voltages[] = {
495c7024aeSWang Xiaoyin 	1750000, 1800000, 1825000, 2800000,
505c7024aeSWang Xiaoyin 	2850000, 2950000, 3000000, 3300000,
515c7024aeSWang Xiaoyin };
525c7024aeSWang Xiaoyin 
535c7024aeSWang Xiaoyin static const unsigned int ldo_15_16_voltages[] = {
545c7024aeSWang Xiaoyin 	1750000, 1800000, 2400000, 2600000,
555c7024aeSWang Xiaoyin 	2700000, 2850000, 2950000, 3000000,
565c7024aeSWang Xiaoyin };
575c7024aeSWang Xiaoyin 
585c7024aeSWang Xiaoyin static const struct regulator_ops hi6421v530_ldo_ops;
595c7024aeSWang Xiaoyin 
605c7024aeSWang Xiaoyin #define HI6421V530_LDO_ENABLE_TIME (350)
615c7024aeSWang Xiaoyin 
625c7024aeSWang Xiaoyin /*
635c7024aeSWang Xiaoyin  * _id - LDO id name string
645c7024aeSWang Xiaoyin  * v_table - voltage table
655c7024aeSWang Xiaoyin  * vreg - voltage select register
665c7024aeSWang Xiaoyin  * vmask - voltage select mask
675c7024aeSWang Xiaoyin  * ereg - enable register
685c7024aeSWang Xiaoyin  * emask - enable mask
695c7024aeSWang Xiaoyin  * odelay - off/on delay time in uS
705c7024aeSWang Xiaoyin  * ecomask - eco mode mask
715c7024aeSWang Xiaoyin  * ecoamp - eco mode load uppler limit in uA
725c7024aeSWang Xiaoyin  */
735c7024aeSWang Xiaoyin #define HI6421V530_LDO(_ID, v_table, vreg, vmask, ereg, emask,		\
745c7024aeSWang Xiaoyin 		   odelay, ecomask, ecoamp) {				\
755c7024aeSWang Xiaoyin 	.rdesc = {							\
765c7024aeSWang Xiaoyin 		.name		 = #_ID,				\
775c7024aeSWang Xiaoyin 		.of_match        = of_match_ptr(#_ID),			\
785c7024aeSWang Xiaoyin 		.regulators_node = of_match_ptr("regulators"),		\
795c7024aeSWang Xiaoyin 		.ops		 = &hi6421v530_ldo_ops,			\
805c7024aeSWang Xiaoyin 		.type		 = REGULATOR_VOLTAGE,			\
815c7024aeSWang Xiaoyin 		.id		 = HI6421V530_##_ID,			\
825c7024aeSWang Xiaoyin 		.owner		 = THIS_MODULE,				\
835c7024aeSWang Xiaoyin 		.n_voltages	 = ARRAY_SIZE(v_table),			\
845c7024aeSWang Xiaoyin 		.volt_table	 = v_table,				\
855c7024aeSWang Xiaoyin 		.vsel_reg	 = HI6421_REG_TO_BUS_ADDR(vreg),	\
865c7024aeSWang Xiaoyin 		.vsel_mask	 = vmask,				\
875c7024aeSWang Xiaoyin 		.enable_reg	 = HI6421_REG_TO_BUS_ADDR(ereg),	\
885c7024aeSWang Xiaoyin 		.enable_mask	 = emask,				\
895c7024aeSWang Xiaoyin 		.enable_time	 = HI6421V530_LDO_ENABLE_TIME,		\
905c7024aeSWang Xiaoyin 		.off_on_delay	 = odelay,				\
915c7024aeSWang Xiaoyin 	},								\
925c7024aeSWang Xiaoyin 	.mode_mask	= ecomask,					\
935c7024aeSWang Xiaoyin 	.eco_microamp	= ecoamp,					\
945c7024aeSWang Xiaoyin }
955c7024aeSWang Xiaoyin 
965c7024aeSWang Xiaoyin /* HI6421V530 regulator information */
975c7024aeSWang Xiaoyin 
985c7024aeSWang Xiaoyin static struct hi6421v530_regulator_info hi6421v530_regulator_info[] = {
995c7024aeSWang Xiaoyin 	HI6421V530_LDO(LDO3, ldo_3_voltages, 0x061, 0xf, 0x060, 0x2,
1005c7024aeSWang Xiaoyin 		   20000, 0x6, 8000),
1015c7024aeSWang Xiaoyin 	HI6421V530_LDO(LDO9, ldo_9_11_voltages, 0x06b, 0x7, 0x06a, 0x2,
1025c7024aeSWang Xiaoyin 		   40000, 0x6, 8000),
1035c7024aeSWang Xiaoyin 	HI6421V530_LDO(LDO11, ldo_9_11_voltages, 0x06f, 0x7, 0x06e, 0x2,
1045c7024aeSWang Xiaoyin 		   40000, 0x6, 8000),
1055c7024aeSWang Xiaoyin 	HI6421V530_LDO(LDO15, ldo_15_16_voltages, 0x077, 0x7, 0x076, 0x2,
1065c7024aeSWang Xiaoyin 		   40000, 0x6, 8000),
1075c7024aeSWang Xiaoyin 	HI6421V530_LDO(LDO16, ldo_15_16_voltages, 0x079, 0x7, 0x078, 0x2,
1085c7024aeSWang Xiaoyin 		   40000, 0x6, 8000),
1095c7024aeSWang Xiaoyin };
1105c7024aeSWang Xiaoyin 
hi6421v530_regulator_ldo_get_mode(struct regulator_dev * rdev)1115c7024aeSWang Xiaoyin static unsigned int hi6421v530_regulator_ldo_get_mode(
1125c7024aeSWang Xiaoyin 					struct regulator_dev *rdev)
1135c7024aeSWang Xiaoyin {
1145c7024aeSWang Xiaoyin 	struct hi6421v530_regulator_info *info;
1155c7024aeSWang Xiaoyin 	unsigned int reg_val;
1165c7024aeSWang Xiaoyin 
1175c7024aeSWang Xiaoyin 	info = rdev_get_drvdata(rdev);
1185c7024aeSWang Xiaoyin 	regmap_read(rdev->regmap, rdev->desc->enable_reg, &reg_val);
1195c7024aeSWang Xiaoyin 
1205c7024aeSWang Xiaoyin 	if (reg_val & (info->mode_mask))
1215c7024aeSWang Xiaoyin 		return REGULATOR_MODE_IDLE;
1225c7024aeSWang Xiaoyin 
1235c7024aeSWang Xiaoyin 	return REGULATOR_MODE_NORMAL;
1245c7024aeSWang Xiaoyin }
1255c7024aeSWang Xiaoyin 
hi6421v530_regulator_ldo_set_mode(struct regulator_dev * rdev,unsigned int mode)1265c7024aeSWang Xiaoyin static int hi6421v530_regulator_ldo_set_mode(struct regulator_dev *rdev,
1275c7024aeSWang Xiaoyin 						unsigned int mode)
1285c7024aeSWang Xiaoyin {
1295c7024aeSWang Xiaoyin 	struct hi6421v530_regulator_info *info;
1305c7024aeSWang Xiaoyin 	unsigned int new_mode;
1315c7024aeSWang Xiaoyin 
1325c7024aeSWang Xiaoyin 	info = rdev_get_drvdata(rdev);
1335c7024aeSWang Xiaoyin 	switch (mode) {
1345c7024aeSWang Xiaoyin 	case REGULATOR_MODE_NORMAL:
1355c7024aeSWang Xiaoyin 		new_mode = 0;
1365c7024aeSWang Xiaoyin 		break;
1375c7024aeSWang Xiaoyin 	case REGULATOR_MODE_IDLE:
1385c7024aeSWang Xiaoyin 		new_mode = info->mode_mask;
1395c7024aeSWang Xiaoyin 		break;
1405c7024aeSWang Xiaoyin 	default:
1415c7024aeSWang Xiaoyin 		return -EINVAL;
1425c7024aeSWang Xiaoyin 	}
1435c7024aeSWang Xiaoyin 
1445c7024aeSWang Xiaoyin 	regmap_update_bits(rdev->regmap, rdev->desc->enable_reg,
1455c7024aeSWang Xiaoyin 			   info->mode_mask, new_mode);
1465c7024aeSWang Xiaoyin 
1475c7024aeSWang Xiaoyin 	return 0;
1485c7024aeSWang Xiaoyin }
1495c7024aeSWang Xiaoyin 
1505c7024aeSWang Xiaoyin 
1515c7024aeSWang Xiaoyin static const struct regulator_ops hi6421v530_ldo_ops = {
1525c7024aeSWang Xiaoyin 	.is_enabled = regulator_is_enabled_regmap,
1535c7024aeSWang Xiaoyin 	.enable = regulator_enable_regmap,
1545c7024aeSWang Xiaoyin 	.disable = regulator_disable_regmap,
1555c7024aeSWang Xiaoyin 	.list_voltage = regulator_list_voltage_table,
1565c7024aeSWang Xiaoyin 	.map_voltage = regulator_map_voltage_ascend,
1575c7024aeSWang Xiaoyin 	.get_voltage_sel = regulator_get_voltage_sel_regmap,
1585c7024aeSWang Xiaoyin 	.set_voltage_sel = regulator_set_voltage_sel_regmap,
1595c7024aeSWang Xiaoyin 	.get_mode = hi6421v530_regulator_ldo_get_mode,
1605c7024aeSWang Xiaoyin 	.set_mode = hi6421v530_regulator_ldo_set_mode,
1615c7024aeSWang Xiaoyin };
1625c7024aeSWang Xiaoyin 
hi6421v530_regulator_probe(struct platform_device * pdev)1635c7024aeSWang Xiaoyin static int hi6421v530_regulator_probe(struct platform_device *pdev)
1645c7024aeSWang Xiaoyin {
1655c7024aeSWang Xiaoyin 	struct hi6421_pmic *pmic;
1665c7024aeSWang Xiaoyin 	struct regulator_dev *rdev;
1675c7024aeSWang Xiaoyin 	struct regulator_config config = { };
1685c7024aeSWang Xiaoyin 	unsigned int i;
1695c7024aeSWang Xiaoyin 
1705c7024aeSWang Xiaoyin 	pmic = dev_get_drvdata(pdev->dev.parent);
1715c7024aeSWang Xiaoyin 	if (!pmic) {
1725c7024aeSWang Xiaoyin 		dev_err(&pdev->dev, "no pmic in the regulator parent node\n");
1735c7024aeSWang Xiaoyin 		return -ENODEV;
1745c7024aeSWang Xiaoyin 	}
1755c7024aeSWang Xiaoyin 
1765c7024aeSWang Xiaoyin 	for (i = 0; i < ARRAY_SIZE(hi6421v530_regulator_info); i++) {
1775c7024aeSWang Xiaoyin 		config.dev = pdev->dev.parent;
1785c7024aeSWang Xiaoyin 		config.regmap = pmic->regmap;
1795c7024aeSWang Xiaoyin 		config.driver_data = &hi6421v530_regulator_info[i];
1805c7024aeSWang Xiaoyin 
1815c7024aeSWang Xiaoyin 		rdev = devm_regulator_register(&pdev->dev,
1825c7024aeSWang Xiaoyin 				&hi6421v530_regulator_info[i].rdesc,
1835c7024aeSWang Xiaoyin 				&config);
1845c7024aeSWang Xiaoyin 		if (IS_ERR(rdev)) {
1855c7024aeSWang Xiaoyin 			dev_err(&pdev->dev, "failed to register regulator %s\n",
1865c7024aeSWang Xiaoyin 				hi6421v530_regulator_info[i].rdesc.name);
1875c7024aeSWang Xiaoyin 			return PTR_ERR(rdev);
1885c7024aeSWang Xiaoyin 		}
1895c7024aeSWang Xiaoyin 	}
1905c7024aeSWang Xiaoyin 	return 0;
1915c7024aeSWang Xiaoyin }
1925c7024aeSWang Xiaoyin 
193ded7b2aeSGuodong Xu static const struct platform_device_id hi6421v530_regulator_table[] = {
194ded7b2aeSGuodong Xu 	{ .name = "hi6421v530-regulator" },
195ded7b2aeSGuodong Xu 	{},
196ded7b2aeSGuodong Xu };
197ded7b2aeSGuodong Xu MODULE_DEVICE_TABLE(platform, hi6421v530_regulator_table);
198ded7b2aeSGuodong Xu 
1995c7024aeSWang Xiaoyin static struct platform_driver hi6421v530_regulator_driver = {
200ded7b2aeSGuodong Xu 	.id_table = hi6421v530_regulator_table,
2015c7024aeSWang Xiaoyin 	.driver = {
2025c7024aeSWang Xiaoyin 		.name	= "hi6421v530-regulator",
203*259b93b2SDouglas Anderson 		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
2045c7024aeSWang Xiaoyin 	},
2055c7024aeSWang Xiaoyin 	.probe	= hi6421v530_regulator_probe,
2065c7024aeSWang Xiaoyin };
2075c7024aeSWang Xiaoyin module_platform_driver(hi6421v530_regulator_driver);
2085c7024aeSWang Xiaoyin 
2095c7024aeSWang Xiaoyin MODULE_AUTHOR("Wang Xiaoyin <hw.wangxiaoyin@hisilicon.com>");
2105c7024aeSWang Xiaoyin MODULE_DESCRIPTION("Hi6421v530 regulator driver");
2115c7024aeSWang Xiaoyin MODULE_LICENSE("GPL v2");
212