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