162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci// 362306a36Sopenharmony_ci// Device driver for regulators in Hi655x IC 462306a36Sopenharmony_ci// 562306a36Sopenharmony_ci// Copyright (c) 2016 HiSilicon Ltd. 662306a36Sopenharmony_ci// 762306a36Sopenharmony_ci// Authors: 862306a36Sopenharmony_ci// Chen Feng <puck.chen@hisilicon.com> 962306a36Sopenharmony_ci// Fei Wang <w.f@huawei.com> 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/bitops.h> 1262306a36Sopenharmony_ci#include <linux/device.h> 1362306a36Sopenharmony_ci#include <linux/err.h> 1462306a36Sopenharmony_ci#include <linux/module.h> 1562306a36Sopenharmony_ci#include <linux/io.h> 1662306a36Sopenharmony_ci#include <linux/of.h> 1762306a36Sopenharmony_ci#include <linux/platform_device.h> 1862306a36Sopenharmony_ci#include <linux/regmap.h> 1962306a36Sopenharmony_ci#include <linux/regulator/driver.h> 2062306a36Sopenharmony_ci#include <linux/regulator/machine.h> 2162306a36Sopenharmony_ci#include <linux/regulator/of_regulator.h> 2262306a36Sopenharmony_ci#include <linux/mfd/hi655x-pmic.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_cistruct hi655x_regulator { 2562306a36Sopenharmony_ci unsigned int disable_reg; 2662306a36Sopenharmony_ci unsigned int status_reg; 2762306a36Sopenharmony_ci struct regulator_desc rdesc; 2862306a36Sopenharmony_ci}; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci/* LDO7 & LDO10 */ 3162306a36Sopenharmony_cistatic const unsigned int ldo7_voltages[] = { 3262306a36Sopenharmony_ci 1800000, 1850000, 2850000, 2900000, 3362306a36Sopenharmony_ci 3000000, 3100000, 3200000, 3300000, 3462306a36Sopenharmony_ci}; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistatic const unsigned int ldo19_voltages[] = { 3762306a36Sopenharmony_ci 1800000, 1850000, 1900000, 1750000, 3862306a36Sopenharmony_ci 2800000, 2850000, 2900000, 3000000, 3962306a36Sopenharmony_ci}; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistatic const unsigned int ldo22_voltages[] = { 4262306a36Sopenharmony_ci 900000, 1000000, 1050000, 1100000, 4362306a36Sopenharmony_ci 1150000, 1175000, 1185000, 1200000, 4462306a36Sopenharmony_ci}; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cienum hi655x_regulator_id { 4762306a36Sopenharmony_ci HI655X_LDO0, 4862306a36Sopenharmony_ci HI655X_LDO1, 4962306a36Sopenharmony_ci HI655X_LDO2, 5062306a36Sopenharmony_ci HI655X_LDO3, 5162306a36Sopenharmony_ci HI655X_LDO4, 5262306a36Sopenharmony_ci HI655X_LDO5, 5362306a36Sopenharmony_ci HI655X_LDO6, 5462306a36Sopenharmony_ci HI655X_LDO7, 5562306a36Sopenharmony_ci HI655X_LDO8, 5662306a36Sopenharmony_ci HI655X_LDO9, 5762306a36Sopenharmony_ci HI655X_LDO10, 5862306a36Sopenharmony_ci HI655X_LDO11, 5962306a36Sopenharmony_ci HI655X_LDO12, 6062306a36Sopenharmony_ci HI655X_LDO13, 6162306a36Sopenharmony_ci HI655X_LDO14, 6262306a36Sopenharmony_ci HI655X_LDO15, 6362306a36Sopenharmony_ci HI655X_LDO16, 6462306a36Sopenharmony_ci HI655X_LDO17, 6562306a36Sopenharmony_ci HI655X_LDO18, 6662306a36Sopenharmony_ci HI655X_LDO19, 6762306a36Sopenharmony_ci HI655X_LDO20, 6862306a36Sopenharmony_ci HI655X_LDO21, 6962306a36Sopenharmony_ci HI655X_LDO22, 7062306a36Sopenharmony_ci}; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistatic int hi655x_is_enabled(struct regulator_dev *rdev) 7362306a36Sopenharmony_ci{ 7462306a36Sopenharmony_ci unsigned int value = 0; 7562306a36Sopenharmony_ci const struct hi655x_regulator *regulator = rdev_get_drvdata(rdev); 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci regmap_read(rdev->regmap, regulator->status_reg, &value); 7862306a36Sopenharmony_ci return (value & rdev->desc->enable_mask); 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistatic int hi655x_disable(struct regulator_dev *rdev) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci const struct hi655x_regulator *regulator = rdev_get_drvdata(rdev); 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci return regmap_write(rdev->regmap, regulator->disable_reg, 8662306a36Sopenharmony_ci rdev->desc->enable_mask); 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic const struct regulator_ops hi655x_regulator_ops = { 9062306a36Sopenharmony_ci .enable = regulator_enable_regmap, 9162306a36Sopenharmony_ci .disable = hi655x_disable, 9262306a36Sopenharmony_ci .is_enabled = hi655x_is_enabled, 9362306a36Sopenharmony_ci .list_voltage = regulator_list_voltage_table, 9462306a36Sopenharmony_ci .get_voltage_sel = regulator_get_voltage_sel_regmap, 9562306a36Sopenharmony_ci .set_voltage_sel = regulator_set_voltage_sel_regmap, 9662306a36Sopenharmony_ci}; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistatic const struct regulator_ops hi655x_ldo_linear_ops = { 9962306a36Sopenharmony_ci .enable = regulator_enable_regmap, 10062306a36Sopenharmony_ci .disable = hi655x_disable, 10162306a36Sopenharmony_ci .is_enabled = hi655x_is_enabled, 10262306a36Sopenharmony_ci .list_voltage = regulator_list_voltage_linear, 10362306a36Sopenharmony_ci .get_voltage_sel = regulator_get_voltage_sel_regmap, 10462306a36Sopenharmony_ci .set_voltage_sel = regulator_set_voltage_sel_regmap, 10562306a36Sopenharmony_ci}; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci#define HI655X_LDO(_ID, vreg, vmask, ereg, dreg, \ 10862306a36Sopenharmony_ci sreg, cmask, vtable) { \ 10962306a36Sopenharmony_ci .rdesc = { \ 11062306a36Sopenharmony_ci .name = #_ID, \ 11162306a36Sopenharmony_ci .of_match = of_match_ptr(#_ID), \ 11262306a36Sopenharmony_ci .ops = &hi655x_regulator_ops, \ 11362306a36Sopenharmony_ci .regulators_node = of_match_ptr("regulators"), \ 11462306a36Sopenharmony_ci .type = REGULATOR_VOLTAGE, \ 11562306a36Sopenharmony_ci .id = HI655X_##_ID, \ 11662306a36Sopenharmony_ci .owner = THIS_MODULE, \ 11762306a36Sopenharmony_ci .n_voltages = ARRAY_SIZE(vtable), \ 11862306a36Sopenharmony_ci .volt_table = vtable, \ 11962306a36Sopenharmony_ci .vsel_reg = HI655X_BUS_ADDR(vreg), \ 12062306a36Sopenharmony_ci .vsel_mask = vmask, \ 12162306a36Sopenharmony_ci .enable_reg = HI655X_BUS_ADDR(ereg), \ 12262306a36Sopenharmony_ci .enable_mask = BIT(cmask), \ 12362306a36Sopenharmony_ci }, \ 12462306a36Sopenharmony_ci .disable_reg = HI655X_BUS_ADDR(dreg), \ 12562306a36Sopenharmony_ci .status_reg = HI655X_BUS_ADDR(sreg), \ 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci#define HI655X_LDO_LINEAR(_ID, vreg, vmask, ereg, dreg, \ 12962306a36Sopenharmony_ci sreg, cmask, minv, nvolt, vstep) { \ 13062306a36Sopenharmony_ci .rdesc = { \ 13162306a36Sopenharmony_ci .name = #_ID, \ 13262306a36Sopenharmony_ci .of_match = of_match_ptr(#_ID), \ 13362306a36Sopenharmony_ci .ops = &hi655x_ldo_linear_ops, \ 13462306a36Sopenharmony_ci .regulators_node = of_match_ptr("regulators"), \ 13562306a36Sopenharmony_ci .type = REGULATOR_VOLTAGE, \ 13662306a36Sopenharmony_ci .id = HI655X_##_ID, \ 13762306a36Sopenharmony_ci .owner = THIS_MODULE, \ 13862306a36Sopenharmony_ci .min_uV = minv, \ 13962306a36Sopenharmony_ci .n_voltages = nvolt, \ 14062306a36Sopenharmony_ci .uV_step = vstep, \ 14162306a36Sopenharmony_ci .vsel_reg = HI655X_BUS_ADDR(vreg), \ 14262306a36Sopenharmony_ci .vsel_mask = vmask, \ 14362306a36Sopenharmony_ci .enable_reg = HI655X_BUS_ADDR(ereg), \ 14462306a36Sopenharmony_ci .enable_mask = BIT(cmask), \ 14562306a36Sopenharmony_ci }, \ 14662306a36Sopenharmony_ci .disable_reg = HI655X_BUS_ADDR(dreg), \ 14762306a36Sopenharmony_ci .status_reg = HI655X_BUS_ADDR(sreg), \ 14862306a36Sopenharmony_ci} 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_cistatic const struct hi655x_regulator regulators[] = { 15162306a36Sopenharmony_ci HI655X_LDO_LINEAR(LDO2, 0x72, 0x07, 0x29, 0x2a, 0x2b, 0x01, 15262306a36Sopenharmony_ci 2500000, 8, 100000), 15362306a36Sopenharmony_ci HI655X_LDO(LDO7, 0x78, 0x07, 0x29, 0x2a, 0x2b, 0x06, ldo7_voltages), 15462306a36Sopenharmony_ci HI655X_LDO(LDO10, 0x78, 0x07, 0x29, 0x2a, 0x2b, 0x01, ldo7_voltages), 15562306a36Sopenharmony_ci HI655X_LDO_LINEAR(LDO13, 0x7e, 0x07, 0x2c, 0x2d, 0x2e, 0x04, 15662306a36Sopenharmony_ci 1600000, 8, 50000), 15762306a36Sopenharmony_ci HI655X_LDO_LINEAR(LDO14, 0x7f, 0x07, 0x2c, 0x2d, 0x2e, 0x05, 15862306a36Sopenharmony_ci 2500000, 8, 100000), 15962306a36Sopenharmony_ci HI655X_LDO_LINEAR(LDO15, 0x80, 0x07, 0x2c, 0x2d, 0x2e, 0x06, 16062306a36Sopenharmony_ci 1600000, 8, 50000), 16162306a36Sopenharmony_ci HI655X_LDO_LINEAR(LDO17, 0x82, 0x07, 0x2f, 0x30, 0x31, 0x00, 16262306a36Sopenharmony_ci 2500000, 8, 100000), 16362306a36Sopenharmony_ci HI655X_LDO(LDO19, 0x84, 0x07, 0x2f, 0x30, 0x31, 0x02, ldo19_voltages), 16462306a36Sopenharmony_ci HI655X_LDO_LINEAR(LDO21, 0x86, 0x07, 0x2f, 0x30, 0x31, 0x04, 16562306a36Sopenharmony_ci 1650000, 8, 50000), 16662306a36Sopenharmony_ci HI655X_LDO(LDO22, 0x87, 0x07, 0x2f, 0x30, 0x31, 0x05, ldo22_voltages), 16762306a36Sopenharmony_ci}; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cistatic int hi655x_regulator_probe(struct platform_device *pdev) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci unsigned int i; 17262306a36Sopenharmony_ci struct hi655x_pmic *pmic; 17362306a36Sopenharmony_ci struct regulator_config config = { }; 17462306a36Sopenharmony_ci struct regulator_dev *rdev; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci pmic = dev_get_drvdata(pdev->dev.parent); 17762306a36Sopenharmony_ci if (!pmic) { 17862306a36Sopenharmony_ci dev_err(&pdev->dev, "no pmic in the regulator parent node\n"); 17962306a36Sopenharmony_ci return -ENODEV; 18062306a36Sopenharmony_ci } 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci config.dev = pdev->dev.parent; 18362306a36Sopenharmony_ci config.regmap = pmic->regmap; 18462306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(regulators); i++) { 18562306a36Sopenharmony_ci config.driver_data = (void *) ®ulators[i]; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci rdev = devm_regulator_register(&pdev->dev, 18862306a36Sopenharmony_ci ®ulators[i].rdesc, 18962306a36Sopenharmony_ci &config); 19062306a36Sopenharmony_ci if (IS_ERR(rdev)) { 19162306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to register regulator %s\n", 19262306a36Sopenharmony_ci regulators[i].rdesc.name); 19362306a36Sopenharmony_ci return PTR_ERR(rdev); 19462306a36Sopenharmony_ci } 19562306a36Sopenharmony_ci } 19662306a36Sopenharmony_ci return 0; 19762306a36Sopenharmony_ci} 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_cistatic const struct platform_device_id hi655x_regulator_table[] = { 20062306a36Sopenharmony_ci { .name = "hi655x-regulator" }, 20162306a36Sopenharmony_ci {}, 20262306a36Sopenharmony_ci}; 20362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(platform, hi655x_regulator_table); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_cistatic struct platform_driver hi655x_regulator_driver = { 20662306a36Sopenharmony_ci .id_table = hi655x_regulator_table, 20762306a36Sopenharmony_ci .driver = { 20862306a36Sopenharmony_ci .name = "hi655x-regulator", 20962306a36Sopenharmony_ci .probe_type = PROBE_PREFER_ASYNCHRONOUS, 21062306a36Sopenharmony_ci }, 21162306a36Sopenharmony_ci .probe = hi655x_regulator_probe, 21262306a36Sopenharmony_ci}; 21362306a36Sopenharmony_cimodule_platform_driver(hi655x_regulator_driver); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ciMODULE_AUTHOR("Chen Feng <puck.chen@hisilicon.com>"); 21662306a36Sopenharmony_ciMODULE_DESCRIPTION("Hisilicon Hi655x regulator driver"); 21762306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 218