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