1 // SPDX-License-Identifier: GPL-2.0+ 2 // 3 // arizona-ldo1.c -- LDO1 supply for Arizona devices 4 // 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 struct arizona_ldo1 { 29 struct regulator_dev *regulator; 30 struct regmap *regmap; 31 32 struct regulator_consumer_supply supply; 33 struct regulator_init_data init_data; 34 35 struct gpio_desc *ena_gpiod; 36 }; 37 38 static int arizona_ldo1_hc_set_voltage_sel(struct regulator_dev *rdev, 39 unsigned sel) 40 { 41 struct regmap *regmap = rdev_get_regmap(rdev); 42 unsigned int val; 43 int ret; 44 45 if (sel == rdev->desc->n_voltages - 1) 46 val = ARIZONA_LDO1_HI_PWR; 47 else 48 val = 0; 49 50 ret = regmap_update_bits(regmap, ARIZONA_LDO1_CONTROL_2, 51 ARIZONA_LDO1_HI_PWR, val); 52 if (ret != 0) 53 return ret; 54 55 if (val) 56 return 0; 57 58 return regulator_set_voltage_sel_regmap(rdev, sel); 59 } 60 61 static int arizona_ldo1_hc_get_voltage_sel(struct regulator_dev *rdev) 62 { 63 struct regmap *regmap = rdev_get_regmap(rdev); 64 unsigned int val; 65 int ret; 66 67 ret = regmap_read(regmap, ARIZONA_LDO1_CONTROL_2, &val); 68 if (ret != 0) 69 return ret; 70 71 if (val & ARIZONA_LDO1_HI_PWR) 72 return rdev->desc->n_voltages - 1; 73 74 return regulator_get_voltage_sel_regmap(rdev); 75 } 76 77 static const struct regulator_ops arizona_ldo1_hc_ops = { 78 .list_voltage = regulator_list_voltage_linear_range, 79 .map_voltage = regulator_map_voltage_linear_range, 80 .get_voltage_sel = arizona_ldo1_hc_get_voltage_sel, 81 .set_voltage_sel = arizona_ldo1_hc_set_voltage_sel, 82 .get_bypass = regulator_get_bypass_regmap, 83 .set_bypass = regulator_set_bypass_regmap, 84 }; 85 86 static const struct regulator_linear_range arizona_ldo1_hc_ranges[] = { 87 REGULATOR_LINEAR_RANGE(900000, 0, 0x6, 50000), 88 REGULATOR_LINEAR_RANGE(1800000, 0x7, 0x7, 0), 89 }; 90 91 static const struct regulator_desc arizona_ldo1_hc = { 92 .name = "LDO1", 93 .supply_name = "LDOVDD", 94 .type = REGULATOR_VOLTAGE, 95 .ops = &arizona_ldo1_hc_ops, 96 97 .vsel_reg = ARIZONA_LDO1_CONTROL_1, 98 .vsel_mask = ARIZONA_LDO1_VSEL_MASK, 99 .bypass_reg = ARIZONA_LDO1_CONTROL_1, 100 .bypass_mask = ARIZONA_LDO1_BYPASS, 101 .linear_ranges = arizona_ldo1_hc_ranges, 102 .n_linear_ranges = ARRAY_SIZE(arizona_ldo1_hc_ranges), 103 .n_voltages = 8, 104 .enable_time = 1500, 105 .ramp_delay = 24000, 106 107 .owner = THIS_MODULE, 108 }; 109 110 static const struct regulator_ops arizona_ldo1_ops = { 111 .list_voltage = regulator_list_voltage_linear, 112 .map_voltage = regulator_map_voltage_linear, 113 .get_voltage_sel = regulator_get_voltage_sel_regmap, 114 .set_voltage_sel = regulator_set_voltage_sel_regmap, 115 }; 116 117 static const struct regulator_desc arizona_ldo1 = { 118 .name = "LDO1", 119 .supply_name = "LDOVDD", 120 .type = REGULATOR_VOLTAGE, 121 .ops = &arizona_ldo1_ops, 122 123 .vsel_reg = ARIZONA_LDO1_CONTROL_1, 124 .vsel_mask = ARIZONA_LDO1_VSEL_MASK, 125 .min_uV = 900000, 126 .uV_step = 25000, 127 .n_voltages = 13, 128 .enable_time = 500, 129 .ramp_delay = 24000, 130 131 .owner = THIS_MODULE, 132 }; 133 134 static const struct regulator_init_data arizona_ldo1_dvfs = { 135 .constraints = { 136 .min_uV = 1200000, 137 .max_uV = 1800000, 138 .valid_ops_mask = REGULATOR_CHANGE_STATUS | 139 REGULATOR_CHANGE_VOLTAGE, 140 }, 141 .num_consumer_supplies = 1, 142 }; 143 144 static const struct regulator_init_data arizona_ldo1_default = { 145 .constraints = { 146 .valid_ops_mask = REGULATOR_CHANGE_STATUS, 147 }, 148 .num_consumer_supplies = 1, 149 }; 150 151 static const struct regulator_init_data arizona_ldo1_wm5110 = { 152 .constraints = { 153 .min_uV = 1175000, 154 .max_uV = 1200000, 155 .valid_ops_mask = REGULATOR_CHANGE_STATUS | 156 REGULATOR_CHANGE_VOLTAGE, 157 }, 158 .num_consumer_supplies = 1, 159 }; 160 161 static int arizona_ldo1_of_get_pdata(struct arizona_ldo1_pdata *pdata, 162 struct regulator_config *config, 163 const struct regulator_desc *desc, 164 bool *external_dcvdd) 165 { 166 struct arizona_ldo1 *ldo1 = config->driver_data; 167 struct device_node *np = config->dev->of_node; 168 struct device_node *init_node, *dcvdd_node; 169 struct regulator_init_data *init_data; 170 171 init_node = of_get_child_by_name(np, "ldo1"); 172 dcvdd_node = of_parse_phandle(np, "DCVDD-supply", 0); 173 174 if (init_node) { 175 config->of_node = init_node; 176 177 init_data = of_get_regulator_init_data(config->dev, init_node, 178 desc); 179 if (init_data) { 180 init_data->consumer_supplies = &ldo1->supply; 181 init_data->num_consumer_supplies = 1; 182 183 if (dcvdd_node && dcvdd_node != init_node) 184 *external_dcvdd = true; 185 186 pdata->init_data = init_data; 187 } 188 } else if (dcvdd_node) { 189 *external_dcvdd = true; 190 } 191 192 of_node_put(dcvdd_node); 193 194 return 0; 195 } 196 197 static int arizona_ldo1_common_init(struct platform_device *pdev, 198 struct arizona_ldo1 *ldo1, 199 const struct regulator_desc *desc, 200 struct arizona_ldo1_pdata *pdata, 201 bool *external_dcvdd) 202 { 203 struct device *parent_dev = pdev->dev.parent; 204 struct regulator_config config = { }; 205 int ret; 206 207 *external_dcvdd = false; 208 209 ldo1->supply.supply = "DCVDD"; 210 ldo1->init_data.consumer_supplies = &ldo1->supply; 211 ldo1->supply.dev_name = dev_name(parent_dev); 212 213 config.dev = parent_dev; 214 config.driver_data = ldo1; 215 config.regmap = ldo1->regmap; 216 217 if (IS_ENABLED(CONFIG_OF)) { 218 if (!dev_get_platdata(parent_dev)) { 219 ret = arizona_ldo1_of_get_pdata(pdata, 220 &config, desc, 221 external_dcvdd); 222 if (ret < 0) 223 return ret; 224 } 225 } 226 227 /* We assume that high output = regulator off 228 * Don't use devm, since we need to get against the parent device 229 * so clean up would happen at the wrong time 230 */ 231 config.ena_gpiod = gpiod_get_optional(parent_dev, "wlf,ldoena", 232 GPIOD_OUT_LOW | GPIOD_FLAGS_BIT_NONEXCLUSIVE); 233 if (IS_ERR(config.ena_gpiod)) 234 return PTR_ERR(config.ena_gpiod); 235 236 ldo1->ena_gpiod = config.ena_gpiod; 237 238 if (pdata->init_data) 239 config.init_data = pdata->init_data; 240 else 241 config.init_data = &ldo1->init_data; 242 243 /* 244 * LDO1 can only be used to supply DCVDD so if it has no 245 * consumers then DCVDD is supplied externally. 246 */ 247 if (config.init_data->num_consumer_supplies == 0) 248 *external_dcvdd = true; 249 250 ldo1->regulator = devm_regulator_register(&pdev->dev, desc, &config); 251 252 of_node_put(config.of_node); 253 254 if (IS_ERR(ldo1->regulator)) { 255 ret = PTR_ERR(ldo1->regulator); 256 dev_err(&pdev->dev, "Failed to register LDO1 supply: %d\n", 257 ret); 258 return ret; 259 } 260 261 platform_set_drvdata(pdev, ldo1); 262 263 return 0; 264 } 265 266 static int arizona_ldo1_probe(struct platform_device *pdev) 267 { 268 struct arizona *arizona = dev_get_drvdata(pdev->dev.parent); 269 struct arizona_ldo1 *ldo1; 270 const struct regulator_desc *desc; 271 bool external_dcvdd; 272 int ret; 273 274 ldo1 = devm_kzalloc(&pdev->dev, sizeof(*ldo1), GFP_KERNEL); 275 if (!ldo1) 276 return -ENOMEM; 277 278 ldo1->regmap = arizona->regmap; 279 280 /* 281 * Since the chip usually supplies itself we provide some 282 * default init_data for it. This will be overridden with 283 * platform data if provided. 284 */ 285 switch (arizona->type) { 286 case WM5102: 287 case WM8997: 288 case WM8998: 289 case WM1814: 290 desc = &arizona_ldo1_hc; 291 ldo1->init_data = arizona_ldo1_dvfs; 292 break; 293 case WM5110: 294 case WM8280: 295 desc = &arizona_ldo1; 296 ldo1->init_data = arizona_ldo1_wm5110; 297 break; 298 default: 299 desc = &arizona_ldo1; 300 ldo1->init_data = arizona_ldo1_default; 301 break; 302 } 303 304 ret = arizona_ldo1_common_init(pdev, ldo1, desc, 305 &arizona->pdata.ldo1, 306 &external_dcvdd); 307 if (ret == 0) 308 arizona->external_dcvdd = external_dcvdd; 309 310 return ret; 311 } 312 313 static int arizona_ldo1_remove(struct platform_device *pdev) 314 { 315 struct arizona_ldo1 *ldo1 = platform_get_drvdata(pdev); 316 317 if (ldo1->ena_gpiod) 318 gpiod_put(ldo1->ena_gpiod); 319 320 return 0; 321 } 322 323 static struct platform_driver arizona_ldo1_driver = { 324 .probe = arizona_ldo1_probe, 325 .remove = arizona_ldo1_remove, 326 .driver = { 327 .name = "arizona-ldo1", 328 }, 329 }; 330 331 module_platform_driver(arizona_ldo1_driver); 332 333 /* Module information */ 334 MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); 335 MODULE_DESCRIPTION("Arizona LDO1 driver"); 336 MODULE_LICENSE("GPL"); 337 MODULE_ALIAS("platform:arizona-ldo1"); 338