1 /* 2 * Regulator support for WM8400 3 * 4 * Copyright 2008 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 9 * modify it under the terms of the GNU General Public License as 10 * published by the Free Software Foundation; either version 2 of the 11 * License, or (at your option) any later version. 12 * 13 */ 14 15 #include <linux/bug.h> 16 #include <linux/err.h> 17 #include <linux/kernel.h> 18 #include <linux/module.h> 19 #include <linux/regulator/driver.h> 20 #include <linux/mfd/wm8400-private.h> 21 22 static const struct regulator_linear_range wm8400_ldo_ranges[] = { 23 { .min_uV = 900000, .max_uV = 1600000, .min_sel = 0, .max_sel = 14, 24 .uV_step = 50000 }, 25 { .min_uV = 1700000, .max_uV = 3300000, .min_sel = 15, .max_sel = 31, 26 .uV_step = 100000 }, 27 }; 28 29 static struct regulator_ops wm8400_ldo_ops = { 30 .is_enabled = regulator_is_enabled_regmap, 31 .enable = regulator_enable_regmap, 32 .disable = regulator_disable_regmap, 33 .list_voltage = regulator_list_voltage_linear_range, 34 .get_voltage_sel = regulator_get_voltage_sel_regmap, 35 .set_voltage_sel = regulator_set_voltage_sel_regmap, 36 .map_voltage = regulator_map_voltage_linear_range, 37 }; 38 39 static unsigned int wm8400_dcdc_get_mode(struct regulator_dev *dev) 40 { 41 struct wm8400 *wm8400 = rdev_get_drvdata(dev); 42 int offset = (rdev_get_id(dev) - WM8400_DCDC1) * 2; 43 u16 data[2]; 44 int ret; 45 46 ret = wm8400_block_read(wm8400, WM8400_DCDC1_CONTROL_1 + offset, 2, 47 data); 48 if (ret != 0) 49 return 0; 50 51 /* Datasheet: hibernate */ 52 if (data[0] & WM8400_DC1_SLEEP) 53 return REGULATOR_MODE_STANDBY; 54 55 /* Datasheet: standby */ 56 if (!(data[0] & WM8400_DC1_ACTIVE)) 57 return REGULATOR_MODE_IDLE; 58 59 /* Datasheet: active with or without force PWM */ 60 if (data[1] & WM8400_DC1_FRC_PWM) 61 return REGULATOR_MODE_FAST; 62 else 63 return REGULATOR_MODE_NORMAL; 64 } 65 66 static int wm8400_dcdc_set_mode(struct regulator_dev *dev, unsigned int mode) 67 { 68 struct wm8400 *wm8400 = rdev_get_drvdata(dev); 69 int offset = (rdev_get_id(dev) - WM8400_DCDC1) * 2; 70 int ret; 71 72 switch (mode) { 73 case REGULATOR_MODE_FAST: 74 /* Datasheet: active with force PWM */ 75 ret = wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_2 + offset, 76 WM8400_DC1_FRC_PWM, WM8400_DC1_FRC_PWM); 77 if (ret != 0) 78 return ret; 79 80 return wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_1 + offset, 81 WM8400_DC1_ACTIVE | WM8400_DC1_SLEEP, 82 WM8400_DC1_ACTIVE); 83 84 case REGULATOR_MODE_NORMAL: 85 /* Datasheet: active */ 86 ret = wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_2 + offset, 87 WM8400_DC1_FRC_PWM, 0); 88 if (ret != 0) 89 return ret; 90 91 return wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_1 + offset, 92 WM8400_DC1_ACTIVE | WM8400_DC1_SLEEP, 93 WM8400_DC1_ACTIVE); 94 95 case REGULATOR_MODE_IDLE: 96 /* Datasheet: standby */ 97 return wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_1 + offset, 98 WM8400_DC1_ACTIVE | WM8400_DC1_SLEEP, 0); 99 default: 100 return -EINVAL; 101 } 102 } 103 104 static unsigned int wm8400_dcdc_get_optimum_mode(struct regulator_dev *dev, 105 int input_uV, int output_uV, 106 int load_uA) 107 { 108 return REGULATOR_MODE_NORMAL; 109 } 110 111 static struct regulator_ops wm8400_dcdc_ops = { 112 .is_enabled = regulator_is_enabled_regmap, 113 .enable = regulator_enable_regmap, 114 .disable = regulator_disable_regmap, 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 .get_mode = wm8400_dcdc_get_mode, 120 .set_mode = wm8400_dcdc_set_mode, 121 .get_optimum_mode = wm8400_dcdc_get_optimum_mode, 122 }; 123 124 static struct regulator_desc regulators[] = { 125 { 126 .name = "LDO1", 127 .id = WM8400_LDO1, 128 .ops = &wm8400_ldo_ops, 129 .enable_reg = WM8400_LDO1_CONTROL, 130 .enable_mask = WM8400_LDO1_ENA, 131 .n_voltages = WM8400_LDO1_VSEL_MASK + 1, 132 .linear_ranges = wm8400_ldo_ranges, 133 .n_linear_ranges = ARRAY_SIZE(wm8400_ldo_ranges), 134 .vsel_reg = WM8400_LDO1_CONTROL, 135 .vsel_mask = WM8400_LDO1_VSEL_MASK, 136 .type = REGULATOR_VOLTAGE, 137 .owner = THIS_MODULE, 138 }, 139 { 140 .name = "LDO2", 141 .id = WM8400_LDO2, 142 .ops = &wm8400_ldo_ops, 143 .enable_reg = WM8400_LDO2_CONTROL, 144 .enable_mask = WM8400_LDO2_ENA, 145 .n_voltages = WM8400_LDO2_VSEL_MASK + 1, 146 .linear_ranges = wm8400_ldo_ranges, 147 .n_linear_ranges = ARRAY_SIZE(wm8400_ldo_ranges), 148 .type = REGULATOR_VOLTAGE, 149 .vsel_reg = WM8400_LDO2_CONTROL, 150 .vsel_mask = WM8400_LDO2_VSEL_MASK, 151 .owner = THIS_MODULE, 152 }, 153 { 154 .name = "LDO3", 155 .id = WM8400_LDO3, 156 .ops = &wm8400_ldo_ops, 157 .enable_reg = WM8400_LDO3_CONTROL, 158 .enable_mask = WM8400_LDO3_ENA, 159 .n_voltages = WM8400_LDO3_VSEL_MASK + 1, 160 .linear_ranges = wm8400_ldo_ranges, 161 .n_linear_ranges = ARRAY_SIZE(wm8400_ldo_ranges), 162 .vsel_reg = WM8400_LDO3_CONTROL, 163 .vsel_mask = WM8400_LDO3_VSEL_MASK, 164 .type = REGULATOR_VOLTAGE, 165 .owner = THIS_MODULE, 166 }, 167 { 168 .name = "LDO4", 169 .id = WM8400_LDO4, 170 .ops = &wm8400_ldo_ops, 171 .enable_reg = WM8400_LDO4_CONTROL, 172 .enable_mask = WM8400_LDO4_ENA, 173 .n_voltages = WM8400_LDO4_VSEL_MASK + 1, 174 .linear_ranges = wm8400_ldo_ranges, 175 .n_linear_ranges = ARRAY_SIZE(wm8400_ldo_ranges), 176 .vsel_reg = WM8400_LDO4_CONTROL, 177 .vsel_mask = WM8400_LDO4_VSEL_MASK, 178 .type = REGULATOR_VOLTAGE, 179 .owner = THIS_MODULE, 180 }, 181 { 182 .name = "DCDC1", 183 .id = WM8400_DCDC1, 184 .ops = &wm8400_dcdc_ops, 185 .enable_reg = WM8400_DCDC1_CONTROL_1, 186 .enable_mask = WM8400_DC1_ENA_MASK, 187 .n_voltages = WM8400_DC1_VSEL_MASK + 1, 188 .vsel_reg = WM8400_DCDC1_CONTROL_1, 189 .vsel_mask = WM8400_DC1_VSEL_MASK, 190 .min_uV = 850000, 191 .uV_step = 25000, 192 .type = REGULATOR_VOLTAGE, 193 .owner = THIS_MODULE, 194 }, 195 { 196 .name = "DCDC2", 197 .id = WM8400_DCDC2, 198 .ops = &wm8400_dcdc_ops, 199 .enable_reg = WM8400_DCDC2_CONTROL_1, 200 .enable_mask = WM8400_DC1_ENA_MASK, 201 .n_voltages = WM8400_DC2_VSEL_MASK + 1, 202 .vsel_reg = WM8400_DCDC2_CONTROL_1, 203 .vsel_mask = WM8400_DC2_VSEL_MASK, 204 .min_uV = 850000, 205 .uV_step = 25000, 206 .type = REGULATOR_VOLTAGE, 207 .owner = THIS_MODULE, 208 }, 209 }; 210 211 static int wm8400_regulator_probe(struct platform_device *pdev) 212 { 213 struct wm8400 *wm8400 = container_of(pdev, struct wm8400, regulators[pdev->id]); 214 struct regulator_config config = { }; 215 struct regulator_dev *rdev; 216 217 config.dev = &pdev->dev; 218 config.init_data = dev_get_platdata(&pdev->dev); 219 config.driver_data = wm8400; 220 config.regmap = wm8400->regmap; 221 222 rdev = regulator_register(®ulators[pdev->id], &config); 223 if (IS_ERR(rdev)) 224 return PTR_ERR(rdev); 225 226 platform_set_drvdata(pdev, rdev); 227 228 return 0; 229 } 230 231 static int wm8400_regulator_remove(struct platform_device *pdev) 232 { 233 struct regulator_dev *rdev = platform_get_drvdata(pdev); 234 235 regulator_unregister(rdev); 236 237 return 0; 238 } 239 240 static struct platform_driver wm8400_regulator_driver = { 241 .driver = { 242 .name = "wm8400-regulator", 243 }, 244 .probe = wm8400_regulator_probe, 245 .remove = wm8400_regulator_remove, 246 }; 247 248 /** 249 * wm8400_register_regulator - enable software control of a WM8400 regulator 250 * 251 * This function enables software control of a WM8400 regulator via 252 * the regulator API. It is intended to be called from the 253 * platform_init() callback of the WM8400 MFD driver. 254 * 255 * @param dev The WM8400 device to operate on. 256 * @param reg The regulator to control. 257 * @param initdata Regulator initdata for the regulator. 258 */ 259 int wm8400_register_regulator(struct device *dev, int reg, 260 struct regulator_init_data *initdata) 261 { 262 struct wm8400 *wm8400 = dev_get_drvdata(dev); 263 264 if (wm8400->regulators[reg].name) 265 return -EBUSY; 266 267 initdata->driver_data = wm8400; 268 269 wm8400->regulators[reg].name = "wm8400-regulator"; 270 wm8400->regulators[reg].id = reg; 271 wm8400->regulators[reg].dev.parent = dev; 272 wm8400->regulators[reg].dev.platform_data = initdata; 273 274 return platform_device_register(&wm8400->regulators[reg]); 275 } 276 EXPORT_SYMBOL_GPL(wm8400_register_regulator); 277 278 static int __init wm8400_regulator_init(void) 279 { 280 return platform_driver_register(&wm8400_regulator_driver); 281 } 282 subsys_initcall(wm8400_regulator_init); 283 284 static void __exit wm8400_regulator_exit(void) 285 { 286 platform_driver_unregister(&wm8400_regulator_driver); 287 } 288 module_exit(wm8400_regulator_exit); 289 290 MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); 291 MODULE_DESCRIPTION("WM8400 regulator driver"); 292 MODULE_LICENSE("GPL"); 293 MODULE_ALIAS("platform:wm8400-regulator"); 294