| /* | 
 |  * Device driver for regulators in Hi6421V530 IC | 
 |  * | 
 |  * Copyright (c) <2017> HiSilicon Technologies Co., Ltd. | 
 |  *              http://www.hisilicon.com | 
 |  * Copyright (c) <2017> Linaro Ltd. | 
 |  *              http://www.linaro.org | 
 |  * | 
 |  * Author: Wang Xiaoyin <hw.wangxiaoyin@hisilicon.com> | 
 |  *         Guodong Xu <guodong.xu@linaro.org> | 
 |  * | 
 |  * This program is free software; you can redistribute it and/or modify | 
 |  * it under the terms of the GNU General Public License version 2 as | 
 |  * published by the Free Software Foundation. | 
 |  */ | 
 |  | 
 | #include <linux/mfd/hi6421-pmic.h> | 
 | #include <linux/module.h> | 
 | #include <linux/of.h> | 
 | #include <linux/platform_device.h> | 
 | #include <linux/regmap.h> | 
 | #include <linux/regulator/driver.h> | 
 |  | 
 | /* | 
 |  * struct hi6421v530_regulator_info - hi6421v530 regulator information | 
 |  * @desc: regulator description | 
 |  * @mode_mask: ECO mode bitmask of LDOs; for BUCKs, this masks sleep | 
 |  * @eco_microamp: eco mode load upper limit (in uA), valid for LDOs only | 
 |  */ | 
 | struct hi6421v530_regulator_info { | 
 | 	struct regulator_desc rdesc; | 
 | 	u8 mode_mask; | 
 | 	u32 eco_microamp; | 
 | }; | 
 |  | 
 | /* HI6421v530 regulators */ | 
 | enum hi6421v530_regulator_id { | 
 | 	HI6421V530_LDO3, | 
 | 	HI6421V530_LDO9, | 
 | 	HI6421V530_LDO11, | 
 | 	HI6421V530_LDO15, | 
 | 	HI6421V530_LDO16, | 
 | }; | 
 |  | 
 | static const unsigned int ldo_3_voltages[] = { | 
 | 	1800000, 1825000, 1850000, 1875000, | 
 | 	1900000, 1925000, 1950000, 1975000, | 
 | 	2000000, 2025000, 2050000, 2075000, | 
 | 	2100000, 2125000, 2150000, 2200000, | 
 | }; | 
 |  | 
 | static const unsigned int ldo_9_11_voltages[] = { | 
 | 	1750000, 1800000, 1825000, 2800000, | 
 | 	2850000, 2950000, 3000000, 3300000, | 
 | }; | 
 |  | 
 | static const unsigned int ldo_15_16_voltages[] = { | 
 | 	1750000, 1800000, 2400000, 2600000, | 
 | 	2700000, 2850000, 2950000, 3000000, | 
 | }; | 
 |  | 
 | static const struct regulator_ops hi6421v530_ldo_ops; | 
 |  | 
 | #define HI6421V530_LDO_ENABLE_TIME (350) | 
 |  | 
 | /* | 
 |  * _id - LDO id name string | 
 |  * v_table - voltage table | 
 |  * vreg - voltage select register | 
 |  * vmask - voltage select mask | 
 |  * ereg - enable register | 
 |  * emask - enable mask | 
 |  * odelay - off/on delay time in uS | 
 |  * ecomask - eco mode mask | 
 |  * ecoamp - eco mode load uppler limit in uA | 
 |  */ | 
 | #define HI6421V530_LDO(_ID, v_table, vreg, vmask, ereg, emask,		\ | 
 | 		   odelay, ecomask, ecoamp) {				\ | 
 | 	.rdesc = {							\ | 
 | 		.name		 = #_ID,				\ | 
 | 		.of_match        = of_match_ptr(#_ID),			\ | 
 | 		.regulators_node = of_match_ptr("regulators"),		\ | 
 | 		.ops		 = &hi6421v530_ldo_ops,			\ | 
 | 		.type		 = REGULATOR_VOLTAGE,			\ | 
 | 		.id		 = HI6421V530_##_ID,			\ | 
 | 		.owner		 = THIS_MODULE,				\ | 
 | 		.n_voltages	 = ARRAY_SIZE(v_table),			\ | 
 | 		.volt_table	 = v_table,				\ | 
 | 		.vsel_reg	 = HI6421_REG_TO_BUS_ADDR(vreg),	\ | 
 | 		.vsel_mask	 = vmask,				\ | 
 | 		.enable_reg	 = HI6421_REG_TO_BUS_ADDR(ereg),	\ | 
 | 		.enable_mask	 = emask,				\ | 
 | 		.enable_time	 = HI6421V530_LDO_ENABLE_TIME,		\ | 
 | 		.off_on_delay	 = odelay,				\ | 
 | 	},								\ | 
 | 	.mode_mask	= ecomask,					\ | 
 | 	.eco_microamp	= ecoamp,					\ | 
 | } | 
 |  | 
 | /* HI6421V530 regulator information */ | 
 |  | 
 | static struct hi6421v530_regulator_info hi6421v530_regulator_info[] = { | 
 | 	HI6421V530_LDO(LDO3, ldo_3_voltages, 0x061, 0xf, 0x060, 0x2, | 
 | 		   20000, 0x6, 8000), | 
 | 	HI6421V530_LDO(LDO9, ldo_9_11_voltages, 0x06b, 0x7, 0x06a, 0x2, | 
 | 		   40000, 0x6, 8000), | 
 | 	HI6421V530_LDO(LDO11, ldo_9_11_voltages, 0x06f, 0x7, 0x06e, 0x2, | 
 | 		   40000, 0x6, 8000), | 
 | 	HI6421V530_LDO(LDO15, ldo_15_16_voltages, 0x077, 0x7, 0x076, 0x2, | 
 | 		   40000, 0x6, 8000), | 
 | 	HI6421V530_LDO(LDO16, ldo_15_16_voltages, 0x079, 0x7, 0x078, 0x2, | 
 | 		   40000, 0x6, 8000), | 
 | }; | 
 |  | 
 | static unsigned int hi6421v530_regulator_ldo_get_mode( | 
 | 					struct regulator_dev *rdev) | 
 | { | 
 | 	struct hi6421v530_regulator_info *info; | 
 | 	unsigned int reg_val; | 
 |  | 
 | 	info = rdev_get_drvdata(rdev); | 
 | 	regmap_read(rdev->regmap, rdev->desc->enable_reg, ®_val); | 
 |  | 
 | 	if (reg_val & (info->mode_mask)) | 
 | 		return REGULATOR_MODE_IDLE; | 
 |  | 
 | 	return REGULATOR_MODE_NORMAL; | 
 | } | 
 |  | 
 | static int hi6421v530_regulator_ldo_set_mode(struct regulator_dev *rdev, | 
 | 						unsigned int mode) | 
 | { | 
 | 	struct hi6421v530_regulator_info *info; | 
 | 	unsigned int new_mode; | 
 |  | 
 | 	info = rdev_get_drvdata(rdev); | 
 | 	switch (mode) { | 
 | 	case REGULATOR_MODE_NORMAL: | 
 | 		new_mode = 0; | 
 | 		break; | 
 | 	case REGULATOR_MODE_IDLE: | 
 | 		new_mode = info->mode_mask; | 
 | 		break; | 
 | 	default: | 
 | 		return -EINVAL; | 
 | 	} | 
 |  | 
 | 	regmap_update_bits(rdev->regmap, rdev->desc->enable_reg, | 
 | 			   info->mode_mask, new_mode); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 |  | 
 | static const struct regulator_ops hi6421v530_ldo_ops = { | 
 | 	.is_enabled = regulator_is_enabled_regmap, | 
 | 	.enable = regulator_enable_regmap, | 
 | 	.disable = regulator_disable_regmap, | 
 | 	.list_voltage = regulator_list_voltage_table, | 
 | 	.map_voltage = regulator_map_voltage_ascend, | 
 | 	.get_voltage_sel = regulator_get_voltage_sel_regmap, | 
 | 	.set_voltage_sel = regulator_set_voltage_sel_regmap, | 
 | 	.get_mode = hi6421v530_regulator_ldo_get_mode, | 
 | 	.set_mode = hi6421v530_regulator_ldo_set_mode, | 
 | }; | 
 |  | 
 | static int hi6421v530_regulator_probe(struct platform_device *pdev) | 
 | { | 
 | 	struct hi6421_pmic *pmic; | 
 | 	struct regulator_dev *rdev; | 
 | 	struct regulator_config config = { }; | 
 | 	unsigned int i; | 
 |  | 
 | 	pmic = dev_get_drvdata(pdev->dev.parent); | 
 | 	if (!pmic) { | 
 | 		dev_err(&pdev->dev, "no pmic in the regulator parent node\n"); | 
 | 		return -ENODEV; | 
 | 	} | 
 |  | 
 | 	for (i = 0; i < ARRAY_SIZE(hi6421v530_regulator_info); i++) { | 
 | 		config.dev = pdev->dev.parent; | 
 | 		config.regmap = pmic->regmap; | 
 | 		config.driver_data = &hi6421v530_regulator_info[i]; | 
 |  | 
 | 		rdev = devm_regulator_register(&pdev->dev, | 
 | 				&hi6421v530_regulator_info[i].rdesc, | 
 | 				&config); | 
 | 		if (IS_ERR(rdev)) { | 
 | 			dev_err(&pdev->dev, "failed to register regulator %s\n", | 
 | 				hi6421v530_regulator_info[i].rdesc.name); | 
 | 			return PTR_ERR(rdev); | 
 | 		} | 
 | 	} | 
 | 	return 0; | 
 | } | 
 |  | 
 | static const struct platform_device_id hi6421v530_regulator_table[] = { | 
 | 	{ .name = "hi6421v530-regulator" }, | 
 | 	{}, | 
 | }; | 
 | MODULE_DEVICE_TABLE(platform, hi6421v530_regulator_table); | 
 |  | 
 | static struct platform_driver hi6421v530_regulator_driver = { | 
 | 	.id_table = hi6421v530_regulator_table, | 
 | 	.driver = { | 
 | 		.name	= "hi6421v530-regulator", | 
 | 	}, | 
 | 	.probe	= hi6421v530_regulator_probe, | 
 | }; | 
 | module_platform_driver(hi6421v530_regulator_driver); | 
 |  | 
 | MODULE_AUTHOR("Wang Xiaoyin <hw.wangxiaoyin@hisilicon.com>"); | 
 | MODULE_DESCRIPTION("Hi6421v530 regulator driver"); | 
 | MODULE_LICENSE("GPL v2"); |