419 lines · c
1// SPDX-License-Identifier: GPL-2.0+2//3// arizona-ldo1.c -- LDO1 supply for Arizona devices4//5// Copyright 2012 Wolfson Microelectronics PLC.6//7// Author: Mark Brown <broonie@opensource.wolfsonmicro.com>8 9#include <linux/module.h>10#include <linux/moduleparam.h>11#include <linux/init.h>12#include <linux/bitops.h>13#include <linux/err.h>14#include <linux/of.h>15#include <linux/gpio/consumer.h>16#include <linux/platform_device.h>17#include <linux/regulator/driver.h>18#include <linux/regulator/machine.h>19#include <linux/regulator/of_regulator.h>20#include <linux/slab.h>21 22#include <linux/regulator/arizona-ldo1.h>23 24#include <linux/mfd/arizona/core.h>25#include <linux/mfd/arizona/pdata.h>26#include <linux/mfd/arizona/registers.h>27 28#include <linux/mfd/madera/core.h>29#include <linux/mfd/madera/pdata.h>30#include <linux/mfd/madera/registers.h>31 32struct arizona_ldo1 {33 struct regulator_dev *regulator;34 struct regmap *regmap;35 36 struct regulator_consumer_supply supply;37 struct regulator_init_data init_data;38 39 struct gpio_desc *ena_gpiod;40};41 42static int arizona_ldo1_hc_set_voltage_sel(struct regulator_dev *rdev,43 unsigned sel)44{45 struct regmap *regmap = rdev_get_regmap(rdev);46 unsigned int val;47 int ret;48 49 if (sel == rdev->desc->n_voltages - 1)50 val = ARIZONA_LDO1_HI_PWR;51 else52 val = 0;53 54 ret = regmap_update_bits(regmap, ARIZONA_LDO1_CONTROL_2,55 ARIZONA_LDO1_HI_PWR, val);56 if (ret != 0)57 return ret;58 59 if (val)60 return 0;61 62 return regulator_set_voltage_sel_regmap(rdev, sel);63}64 65static int arizona_ldo1_hc_get_voltage_sel(struct regulator_dev *rdev)66{67 struct regmap *regmap = rdev_get_regmap(rdev);68 unsigned int val;69 int ret;70 71 ret = regmap_read(regmap, ARIZONA_LDO1_CONTROL_2, &val);72 if (ret != 0)73 return ret;74 75 if (val & ARIZONA_LDO1_HI_PWR)76 return rdev->desc->n_voltages - 1;77 78 return regulator_get_voltage_sel_regmap(rdev);79}80 81static const struct regulator_ops arizona_ldo1_hc_ops = {82 .list_voltage = regulator_list_voltage_linear_range,83 .map_voltage = regulator_map_voltage_linear_range,84 .get_voltage_sel = arizona_ldo1_hc_get_voltage_sel,85 .set_voltage_sel = arizona_ldo1_hc_set_voltage_sel,86 .get_bypass = regulator_get_bypass_regmap,87 .set_bypass = regulator_set_bypass_regmap,88};89 90static const struct linear_range arizona_ldo1_hc_ranges[] = {91 REGULATOR_LINEAR_RANGE(900000, 0, 0x6, 50000),92 REGULATOR_LINEAR_RANGE(1800000, 0x7, 0x7, 0),93};94 95static const struct regulator_desc arizona_ldo1_hc = {96 .name = "LDO1",97 .supply_name = "LDOVDD",98 .type = REGULATOR_VOLTAGE,99 .ops = &arizona_ldo1_hc_ops,100 101 .vsel_reg = ARIZONA_LDO1_CONTROL_1,102 .vsel_mask = ARIZONA_LDO1_VSEL_MASK,103 .bypass_reg = ARIZONA_LDO1_CONTROL_1,104 .bypass_mask = ARIZONA_LDO1_BYPASS,105 .linear_ranges = arizona_ldo1_hc_ranges,106 .n_linear_ranges = ARRAY_SIZE(arizona_ldo1_hc_ranges),107 .n_voltages = 8,108 .enable_time = 1500,109 .ramp_delay = 24000,110 111 .owner = THIS_MODULE,112};113 114static const struct regulator_ops arizona_ldo1_ops = {115 .list_voltage = regulator_list_voltage_linear,116 .map_voltage = regulator_map_voltage_linear,117 .get_voltage_sel = regulator_get_voltage_sel_regmap,118 .set_voltage_sel = regulator_set_voltage_sel_regmap,119};120 121static const struct regulator_desc arizona_ldo1 = {122 .name = "LDO1",123 .supply_name = "LDOVDD",124 .type = REGULATOR_VOLTAGE,125 .ops = &arizona_ldo1_ops,126 127 .vsel_reg = ARIZONA_LDO1_CONTROL_1,128 .vsel_mask = ARIZONA_LDO1_VSEL_MASK,129 .min_uV = 900000,130 .uV_step = 25000,131 .n_voltages = 13,132 .enable_time = 500,133 .ramp_delay = 24000,134 135 .owner = THIS_MODULE,136};137 138static const struct regulator_init_data arizona_ldo1_dvfs = {139 .constraints = {140 .min_uV = 1200000,141 .max_uV = 1800000,142 .valid_ops_mask = REGULATOR_CHANGE_STATUS |143 REGULATOR_CHANGE_VOLTAGE,144 },145 .num_consumer_supplies = 1,146};147 148static const struct regulator_init_data arizona_ldo1_default = {149 .constraints = {150 .valid_ops_mask = REGULATOR_CHANGE_STATUS,151 },152 .num_consumer_supplies = 1,153};154 155static const struct regulator_init_data arizona_ldo1_wm5110 = {156 .constraints = {157 .min_uV = 1175000,158 .max_uV = 1200000,159 .valid_ops_mask = REGULATOR_CHANGE_STATUS |160 REGULATOR_CHANGE_VOLTAGE,161 },162 .num_consumer_supplies = 1,163};164 165static const struct regulator_desc madera_ldo1 = {166 .name = "LDO1",167 .supply_name = "LDOVDD",168 .type = REGULATOR_VOLTAGE,169 .ops = &arizona_ldo1_ops,170 171 .vsel_reg = MADERA_LDO1_CONTROL_1,172 .vsel_mask = MADERA_LDO1_VSEL_MASK,173 .min_uV = 900000,174 .uV_step = 25000,175 .n_voltages = 13,176 .enable_time = 3000,177 178 .owner = THIS_MODULE,179};180 181static const struct regulator_init_data madera_ldo1_default = {182 .constraints = {183 .min_uV = 1200000,184 .max_uV = 1200000,185 .valid_ops_mask = REGULATOR_CHANGE_STATUS,186 },187 .num_consumer_supplies = 1,188};189 190static int arizona_ldo1_of_get_pdata(struct arizona_ldo1_pdata *pdata,191 struct regulator_config *config,192 const struct regulator_desc *desc,193 bool *external_dcvdd)194{195 struct arizona_ldo1 *ldo1 = config->driver_data;196 struct device_node *np = config->dev->of_node;197 struct device_node *init_node, *dcvdd_node;198 struct regulator_init_data *init_data;199 200 init_node = of_get_child_by_name(np, "ldo1");201 dcvdd_node = of_parse_phandle(np, "DCVDD-supply", 0);202 203 if (init_node) {204 config->of_node = init_node;205 206 init_data = of_get_regulator_init_data(config->dev, init_node,207 desc);208 if (init_data) {209 init_data->consumer_supplies = &ldo1->supply;210 init_data->num_consumer_supplies = 1;211 212 if (dcvdd_node && dcvdd_node != init_node)213 *external_dcvdd = true;214 215 pdata->init_data = init_data;216 }217 } else if (dcvdd_node) {218 *external_dcvdd = true;219 }220 221 of_node_put(dcvdd_node);222 223 return 0;224}225 226static int arizona_ldo1_common_init(struct platform_device *pdev,227 struct arizona_ldo1 *ldo1,228 const struct regulator_desc *desc,229 struct arizona_ldo1_pdata *pdata,230 bool *external_dcvdd)231{232 struct device *parent_dev = pdev->dev.parent;233 struct regulator_config config = { };234 int ret;235 236 *external_dcvdd = false;237 238 ldo1->supply.supply = "DCVDD";239 ldo1->init_data.consumer_supplies = &ldo1->supply;240 ldo1->supply.dev_name = dev_name(parent_dev);241 242 config.dev = parent_dev;243 config.driver_data = ldo1;244 config.regmap = ldo1->regmap;245 246 if (IS_ENABLED(CONFIG_OF)) {247 if (!dev_get_platdata(parent_dev)) {248 ret = arizona_ldo1_of_get_pdata(pdata,249 &config, desc,250 external_dcvdd);251 if (ret < 0)252 return ret;253 }254 }255 256 /* We assume that high output = regulator off257 * Don't use devm, since we need to get against the parent device258 * so clean up would happen at the wrong time259 */260 config.ena_gpiod = gpiod_get_optional(parent_dev, "wlf,ldoena",261 GPIOD_OUT_LOW | GPIOD_FLAGS_BIT_NONEXCLUSIVE);262 if (IS_ERR(config.ena_gpiod))263 return PTR_ERR(config.ena_gpiod);264 265 ldo1->ena_gpiod = config.ena_gpiod;266 267 if (pdata->init_data)268 config.init_data = pdata->init_data;269 else270 config.init_data = &ldo1->init_data;271 272 /*273 * LDO1 can only be used to supply DCVDD so if it has no274 * consumers then DCVDD is supplied externally.275 */276 if (config.init_data->num_consumer_supplies == 0)277 *external_dcvdd = true;278 279 ldo1->regulator = devm_regulator_register(&pdev->dev, desc, &config);280 281 of_node_put(config.of_node);282 283 if (IS_ERR(ldo1->regulator)) {284 ret = PTR_ERR(ldo1->regulator);285 dev_err(&pdev->dev, "Failed to register LDO1 supply: %d\n",286 ret);287 return ret;288 }289 290 platform_set_drvdata(pdev, ldo1);291 292 return 0;293}294 295static int arizona_ldo1_probe(struct platform_device *pdev)296{297 struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);298 struct arizona_ldo1 *ldo1;299 const struct regulator_desc *desc;300 bool external_dcvdd;301 int ret;302 303 ldo1 = devm_kzalloc(&pdev->dev, sizeof(*ldo1), GFP_KERNEL);304 if (!ldo1)305 return -ENOMEM;306 307 ldo1->regmap = arizona->regmap;308 309 /*310 * Since the chip usually supplies itself we provide some311 * default init_data for it. This will be overridden with312 * platform data if provided.313 */314 switch (arizona->type) {315 case WM5102:316 case WM8997:317 case WM8998:318 case WM1814:319 desc = &arizona_ldo1_hc;320 ldo1->init_data = arizona_ldo1_dvfs;321 break;322 case WM5110:323 case WM8280:324 desc = &arizona_ldo1;325 ldo1->init_data = arizona_ldo1_wm5110;326 break;327 default:328 desc = &arizona_ldo1;329 ldo1->init_data = arizona_ldo1_default;330 break;331 }332 333 ret = arizona_ldo1_common_init(pdev, ldo1, desc,334 &arizona->pdata.ldo1,335 &external_dcvdd);336 if (ret == 0)337 arizona->external_dcvdd = external_dcvdd;338 339 return ret;340}341 342static void arizona_ldo1_remove(struct platform_device *pdev)343{344 struct arizona_ldo1 *ldo1 = platform_get_drvdata(pdev);345 346 if (ldo1->ena_gpiod)347 gpiod_put(ldo1->ena_gpiod);348}349 350static int madera_ldo1_probe(struct platform_device *pdev)351{352 struct madera *madera = dev_get_drvdata(pdev->dev.parent);353 struct arizona_ldo1 *ldo1;354 bool external_dcvdd;355 int ret;356 357 ldo1 = devm_kzalloc(&pdev->dev, sizeof(*ldo1), GFP_KERNEL);358 if (!ldo1)359 return -ENOMEM;360 361 ldo1->regmap = madera->regmap;362 363 ldo1->init_data = madera_ldo1_default;364 365 ret = arizona_ldo1_common_init(pdev, ldo1, &madera_ldo1,366 &madera->pdata.ldo1,367 &external_dcvdd);368 if (ret)369 return ret;370 371 madera->internal_dcvdd = !external_dcvdd;372 373 return 0;374}375 376static struct platform_driver arizona_ldo1_driver = {377 .probe = arizona_ldo1_probe,378 .remove_new = arizona_ldo1_remove,379 .driver = {380 .name = "arizona-ldo1",381 .probe_type = PROBE_FORCE_SYNCHRONOUS,382 },383};384 385static struct platform_driver madera_ldo1_driver = {386 .probe = madera_ldo1_probe,387 .remove_new = arizona_ldo1_remove,388 .driver = {389 .name = "madera-ldo1",390 .probe_type = PROBE_FORCE_SYNCHRONOUS,391 },392};393 394static struct platform_driver * const madera_ldo1_drivers[] = {395 &arizona_ldo1_driver,396 &madera_ldo1_driver,397};398 399static int __init arizona_ldo1_init(void)400{401 return platform_register_drivers(madera_ldo1_drivers,402 ARRAY_SIZE(madera_ldo1_drivers));403}404module_init(arizona_ldo1_init);405 406static void __exit madera_ldo1_exit(void)407{408 platform_unregister_drivers(madera_ldo1_drivers,409 ARRAY_SIZE(madera_ldo1_drivers));410}411module_exit(madera_ldo1_exit);412 413/* Module information */414MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");415MODULE_DESCRIPTION("Arizona LDO1 driver");416MODULE_LICENSE("GPL");417MODULE_ALIAS("platform:arizona-ldo1");418MODULE_ALIAS("platform:madera-ldo1");419