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, ®_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