|  | /* | 
|  | * 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"); |