1*42fad570SMark Brown /* 2*42fad570SMark Brown * Regulator support for WM8400 3*42fad570SMark Brown * 4*42fad570SMark Brown * Copyright 2008 Wolfson Microelectronics PLC. 5*42fad570SMark Brown * 6*42fad570SMark Brown * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> 7*42fad570SMark Brown * 8*42fad570SMark Brown * This program is free software; you can redistribute it and/or 9*42fad570SMark Brown * modify it under the terms of the GNU General Public License as 10*42fad570SMark Brown * published by the Free Software Foundation; either version 2 of the 11*42fad570SMark Brown * License, or (at your option) any later version. 12*42fad570SMark Brown * 13*42fad570SMark Brown */ 14*42fad570SMark Brown 15*42fad570SMark Brown #include <linux/bug.h> 16*42fad570SMark Brown #include <linux/err.h> 17*42fad570SMark Brown #include <linux/kernel.h> 18*42fad570SMark Brown #include <linux/regulator/driver.h> 19*42fad570SMark Brown #include <linux/mfd/wm8400-private.h> 20*42fad570SMark Brown 21*42fad570SMark Brown static int wm8400_ldo_is_enabled(struct regulator_dev *dev) 22*42fad570SMark Brown { 23*42fad570SMark Brown struct wm8400 *wm8400 = rdev_get_drvdata(dev); 24*42fad570SMark Brown u16 val; 25*42fad570SMark Brown 26*42fad570SMark Brown val = wm8400_reg_read(wm8400, WM8400_LDO1_CONTROL + rdev_get_id(dev)); 27*42fad570SMark Brown return (val & WM8400_LDO1_ENA) != 0; 28*42fad570SMark Brown } 29*42fad570SMark Brown 30*42fad570SMark Brown static int wm8400_ldo_enable(struct regulator_dev *dev) 31*42fad570SMark Brown { 32*42fad570SMark Brown struct wm8400 *wm8400 = rdev_get_drvdata(dev); 33*42fad570SMark Brown 34*42fad570SMark Brown return wm8400_set_bits(wm8400, WM8400_LDO1_CONTROL + rdev_get_id(dev), 35*42fad570SMark Brown WM8400_LDO1_ENA, WM8400_LDO1_ENA); 36*42fad570SMark Brown } 37*42fad570SMark Brown 38*42fad570SMark Brown static int wm8400_ldo_disable(struct regulator_dev *dev) 39*42fad570SMark Brown { 40*42fad570SMark Brown struct wm8400 *wm8400 = rdev_get_drvdata(dev); 41*42fad570SMark Brown 42*42fad570SMark Brown return wm8400_set_bits(wm8400, WM8400_LDO1_CONTROL + rdev_get_id(dev), 43*42fad570SMark Brown WM8400_LDO1_ENA, 0); 44*42fad570SMark Brown } 45*42fad570SMark Brown 46*42fad570SMark Brown static int wm8400_ldo_get_voltage(struct regulator_dev *dev) 47*42fad570SMark Brown { 48*42fad570SMark Brown struct wm8400 *wm8400 = rdev_get_drvdata(dev); 49*42fad570SMark Brown u16 val; 50*42fad570SMark Brown 51*42fad570SMark Brown val = wm8400_reg_read(wm8400, WM8400_LDO1_CONTROL + rdev_get_id(dev)); 52*42fad570SMark Brown val &= WM8400_LDO1_VSEL_MASK; 53*42fad570SMark Brown 54*42fad570SMark Brown if (val < 15) 55*42fad570SMark Brown return 900000 + (val * 50000); 56*42fad570SMark Brown else 57*42fad570SMark Brown return 1600000 + ((val - 14) * 100000); 58*42fad570SMark Brown } 59*42fad570SMark Brown 60*42fad570SMark Brown static int wm8400_ldo_set_voltage(struct regulator_dev *dev, 61*42fad570SMark Brown int min_uV, int max_uV) 62*42fad570SMark Brown { 63*42fad570SMark Brown struct wm8400 *wm8400 = rdev_get_drvdata(dev); 64*42fad570SMark Brown u16 val; 65*42fad570SMark Brown 66*42fad570SMark Brown if (min_uV < 900000 || min_uV > 3300000) 67*42fad570SMark Brown return -EINVAL; 68*42fad570SMark Brown 69*42fad570SMark Brown if (min_uV < 1700000) { 70*42fad570SMark Brown /* Steps of 50mV from 900mV; */ 71*42fad570SMark Brown val = (min_uV - 850001) / 50000; 72*42fad570SMark Brown 73*42fad570SMark Brown if ((val * 50000) + 900000 > max_uV) 74*42fad570SMark Brown return -EINVAL; 75*42fad570SMark Brown BUG_ON((val * 50000) + 900000 < min_uV); 76*42fad570SMark Brown } else { 77*42fad570SMark Brown /* Steps of 100mV from 1700mV */ 78*42fad570SMark Brown val = ((min_uV - 1600001) / 100000); 79*42fad570SMark Brown 80*42fad570SMark Brown if ((val * 100000) + 1700000 > max_uV) 81*42fad570SMark Brown return -EINVAL; 82*42fad570SMark Brown BUG_ON((val * 100000) + 1700000 < min_uV); 83*42fad570SMark Brown 84*42fad570SMark Brown val += 0xf; 85*42fad570SMark Brown } 86*42fad570SMark Brown 87*42fad570SMark Brown return wm8400_set_bits(wm8400, WM8400_LDO1_CONTROL + rdev_get_id(dev), 88*42fad570SMark Brown WM8400_LDO1_VSEL_MASK, val); 89*42fad570SMark Brown } 90*42fad570SMark Brown 91*42fad570SMark Brown static struct regulator_ops wm8400_ldo_ops = { 92*42fad570SMark Brown .is_enabled = wm8400_ldo_is_enabled, 93*42fad570SMark Brown .enable = wm8400_ldo_enable, 94*42fad570SMark Brown .disable = wm8400_ldo_disable, 95*42fad570SMark Brown .get_voltage = wm8400_ldo_get_voltage, 96*42fad570SMark Brown .set_voltage = wm8400_ldo_set_voltage, 97*42fad570SMark Brown }; 98*42fad570SMark Brown 99*42fad570SMark Brown static int wm8400_dcdc_is_enabled(struct regulator_dev *dev) 100*42fad570SMark Brown { 101*42fad570SMark Brown struct wm8400 *wm8400 = rdev_get_drvdata(dev); 102*42fad570SMark Brown int offset = (rdev_get_id(dev) - WM8400_DCDC1) * 2; 103*42fad570SMark Brown u16 val; 104*42fad570SMark Brown 105*42fad570SMark Brown val = wm8400_reg_read(wm8400, WM8400_DCDC1_CONTROL_1 + offset); 106*42fad570SMark Brown return (val & WM8400_DC1_ENA) != 0; 107*42fad570SMark Brown } 108*42fad570SMark Brown 109*42fad570SMark Brown static int wm8400_dcdc_enable(struct regulator_dev *dev) 110*42fad570SMark Brown { 111*42fad570SMark Brown struct wm8400 *wm8400 = rdev_get_drvdata(dev); 112*42fad570SMark Brown int offset = (rdev_get_id(dev) - WM8400_DCDC1) * 2; 113*42fad570SMark Brown 114*42fad570SMark Brown return wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_1 + offset, 115*42fad570SMark Brown WM8400_DC1_ENA, WM8400_DC1_ENA); 116*42fad570SMark Brown } 117*42fad570SMark Brown 118*42fad570SMark Brown static int wm8400_dcdc_disable(struct regulator_dev *dev) 119*42fad570SMark Brown { 120*42fad570SMark Brown struct wm8400 *wm8400 = rdev_get_drvdata(dev); 121*42fad570SMark Brown int offset = (rdev_get_id(dev) - WM8400_DCDC1) * 2; 122*42fad570SMark Brown 123*42fad570SMark Brown return wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_1 + offset, 124*42fad570SMark Brown WM8400_DC1_ENA, 0); 125*42fad570SMark Brown } 126*42fad570SMark Brown 127*42fad570SMark Brown static int wm8400_dcdc_get_voltage(struct regulator_dev *dev) 128*42fad570SMark Brown { 129*42fad570SMark Brown struct wm8400 *wm8400 = rdev_get_drvdata(dev); 130*42fad570SMark Brown u16 val; 131*42fad570SMark Brown int offset = (rdev_get_id(dev) - WM8400_DCDC1) * 2; 132*42fad570SMark Brown 133*42fad570SMark Brown val = wm8400_reg_read(wm8400, WM8400_DCDC1_CONTROL_1 + offset); 134*42fad570SMark Brown val &= WM8400_DC1_VSEL_MASK; 135*42fad570SMark Brown 136*42fad570SMark Brown return 850000 + (25000 * val); 137*42fad570SMark Brown } 138*42fad570SMark Brown 139*42fad570SMark Brown static int wm8400_dcdc_set_voltage(struct regulator_dev *dev, 140*42fad570SMark Brown int min_uV, int max_uV) 141*42fad570SMark Brown { 142*42fad570SMark Brown struct wm8400 *wm8400 = rdev_get_drvdata(dev); 143*42fad570SMark Brown u16 val; 144*42fad570SMark Brown int offset = (rdev_get_id(dev) - WM8400_DCDC1) * 2; 145*42fad570SMark Brown 146*42fad570SMark Brown if (min_uV < 850000) 147*42fad570SMark Brown return -EINVAL; 148*42fad570SMark Brown 149*42fad570SMark Brown val = (min_uV - 825001) / 25000; 150*42fad570SMark Brown 151*42fad570SMark Brown if (850000 + (25000 * val) > max_uV) 152*42fad570SMark Brown return -EINVAL; 153*42fad570SMark Brown BUG_ON(850000 + (25000 * val) < min_uV); 154*42fad570SMark Brown 155*42fad570SMark Brown return wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_1 + offset, 156*42fad570SMark Brown WM8400_DC1_VSEL_MASK, val); 157*42fad570SMark Brown } 158*42fad570SMark Brown 159*42fad570SMark Brown static unsigned int wm8400_dcdc_get_mode(struct regulator_dev *dev) 160*42fad570SMark Brown { 161*42fad570SMark Brown struct wm8400 *wm8400 = rdev_get_drvdata(dev); 162*42fad570SMark Brown int offset = (rdev_get_id(dev) - WM8400_DCDC1) * 2; 163*42fad570SMark Brown u16 data[2]; 164*42fad570SMark Brown int ret; 165*42fad570SMark Brown 166*42fad570SMark Brown ret = wm8400_block_read(wm8400, WM8400_DCDC1_CONTROL_1 + offset, 2, 167*42fad570SMark Brown data); 168*42fad570SMark Brown if (ret != 0) 169*42fad570SMark Brown return 0; 170*42fad570SMark Brown 171*42fad570SMark Brown /* Datasheet: hibernate */ 172*42fad570SMark Brown if (data[0] & WM8400_DC1_SLEEP) 173*42fad570SMark Brown return REGULATOR_MODE_STANDBY; 174*42fad570SMark Brown 175*42fad570SMark Brown /* Datasheet: standby */ 176*42fad570SMark Brown if (!(data[0] & WM8400_DC1_ACTIVE)) 177*42fad570SMark Brown return REGULATOR_MODE_IDLE; 178*42fad570SMark Brown 179*42fad570SMark Brown /* Datasheet: active with or without force PWM */ 180*42fad570SMark Brown if (data[1] & WM8400_DC1_FRC_PWM) 181*42fad570SMark Brown return REGULATOR_MODE_FAST; 182*42fad570SMark Brown else 183*42fad570SMark Brown return REGULATOR_MODE_NORMAL; 184*42fad570SMark Brown } 185*42fad570SMark Brown 186*42fad570SMark Brown static int wm8400_dcdc_set_mode(struct regulator_dev *dev, unsigned int mode) 187*42fad570SMark Brown { 188*42fad570SMark Brown struct wm8400 *wm8400 = rdev_get_drvdata(dev); 189*42fad570SMark Brown int offset = (rdev_get_id(dev) - WM8400_DCDC1) * 2; 190*42fad570SMark Brown int ret; 191*42fad570SMark Brown 192*42fad570SMark Brown switch (mode) { 193*42fad570SMark Brown case REGULATOR_MODE_FAST: 194*42fad570SMark Brown /* Datasheet: active with force PWM */ 195*42fad570SMark Brown ret = wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_2 + offset, 196*42fad570SMark Brown WM8400_DC1_FRC_PWM, WM8400_DC1_FRC_PWM); 197*42fad570SMark Brown if (ret != 0) 198*42fad570SMark Brown return ret; 199*42fad570SMark Brown 200*42fad570SMark Brown return wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_1 + offset, 201*42fad570SMark Brown WM8400_DC1_ACTIVE | WM8400_DC1_SLEEP, 202*42fad570SMark Brown WM8400_DC1_ACTIVE); 203*42fad570SMark Brown 204*42fad570SMark Brown case REGULATOR_MODE_NORMAL: 205*42fad570SMark Brown /* Datasheet: active */ 206*42fad570SMark Brown ret = wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_2 + offset, 207*42fad570SMark Brown WM8400_DC1_FRC_PWM, 0); 208*42fad570SMark Brown if (ret != 0) 209*42fad570SMark Brown return ret; 210*42fad570SMark Brown 211*42fad570SMark Brown return wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_1 + offset, 212*42fad570SMark Brown WM8400_DC1_ACTIVE | WM8400_DC1_SLEEP, 213*42fad570SMark Brown WM8400_DC1_ACTIVE); 214*42fad570SMark Brown 215*42fad570SMark Brown case REGULATOR_MODE_IDLE: 216*42fad570SMark Brown /* Datasheet: standby */ 217*42fad570SMark Brown ret = wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_1 + offset, 218*42fad570SMark Brown WM8400_DC1_ACTIVE, 0); 219*42fad570SMark Brown if (ret != 0) 220*42fad570SMark Brown return ret; 221*42fad570SMark Brown return wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_1 + offset, 222*42fad570SMark Brown WM8400_DC1_SLEEP, 0); 223*42fad570SMark Brown 224*42fad570SMark Brown default: 225*42fad570SMark Brown return -EINVAL; 226*42fad570SMark Brown } 227*42fad570SMark Brown } 228*42fad570SMark Brown 229*42fad570SMark Brown static unsigned int wm8400_dcdc_get_optimum_mode(struct regulator_dev *dev, 230*42fad570SMark Brown int input_uV, int output_uV, 231*42fad570SMark Brown int load_uA) 232*42fad570SMark Brown { 233*42fad570SMark Brown return REGULATOR_MODE_NORMAL; 234*42fad570SMark Brown } 235*42fad570SMark Brown 236*42fad570SMark Brown static struct regulator_ops wm8400_dcdc_ops = { 237*42fad570SMark Brown .is_enabled = wm8400_dcdc_is_enabled, 238*42fad570SMark Brown .enable = wm8400_dcdc_enable, 239*42fad570SMark Brown .disable = wm8400_dcdc_disable, 240*42fad570SMark Brown .get_voltage = wm8400_dcdc_get_voltage, 241*42fad570SMark Brown .set_voltage = wm8400_dcdc_set_voltage, 242*42fad570SMark Brown .get_mode = wm8400_dcdc_get_mode, 243*42fad570SMark Brown .set_mode = wm8400_dcdc_set_mode, 244*42fad570SMark Brown .get_optimum_mode = wm8400_dcdc_get_optimum_mode, 245*42fad570SMark Brown }; 246*42fad570SMark Brown 247*42fad570SMark Brown static struct regulator_desc regulators[] = { 248*42fad570SMark Brown { 249*42fad570SMark Brown .name = "LDO1", 250*42fad570SMark Brown .id = WM8400_LDO1, 251*42fad570SMark Brown .ops = &wm8400_ldo_ops, 252*42fad570SMark Brown .type = REGULATOR_VOLTAGE, 253*42fad570SMark Brown .owner = THIS_MODULE, 254*42fad570SMark Brown }, 255*42fad570SMark Brown { 256*42fad570SMark Brown .name = "LDO2", 257*42fad570SMark Brown .id = WM8400_LDO2, 258*42fad570SMark Brown .ops = &wm8400_ldo_ops, 259*42fad570SMark Brown .type = REGULATOR_VOLTAGE, 260*42fad570SMark Brown .owner = THIS_MODULE, 261*42fad570SMark Brown }, 262*42fad570SMark Brown { 263*42fad570SMark Brown .name = "LDO3", 264*42fad570SMark Brown .id = WM8400_LDO3, 265*42fad570SMark Brown .ops = &wm8400_ldo_ops, 266*42fad570SMark Brown .type = REGULATOR_VOLTAGE, 267*42fad570SMark Brown .owner = THIS_MODULE, 268*42fad570SMark Brown }, 269*42fad570SMark Brown { 270*42fad570SMark Brown .name = "LDO4", 271*42fad570SMark Brown .id = WM8400_LDO4, 272*42fad570SMark Brown .ops = &wm8400_ldo_ops, 273*42fad570SMark Brown .type = REGULATOR_VOLTAGE, 274*42fad570SMark Brown .owner = THIS_MODULE, 275*42fad570SMark Brown }, 276*42fad570SMark Brown { 277*42fad570SMark Brown .name = "DCDC1", 278*42fad570SMark Brown .id = WM8400_DCDC1, 279*42fad570SMark Brown .ops = &wm8400_dcdc_ops, 280*42fad570SMark Brown .type = REGULATOR_VOLTAGE, 281*42fad570SMark Brown .owner = THIS_MODULE, 282*42fad570SMark Brown }, 283*42fad570SMark Brown { 284*42fad570SMark Brown .name = "DCDC2", 285*42fad570SMark Brown .id = WM8400_DCDC2, 286*42fad570SMark Brown .ops = &wm8400_dcdc_ops, 287*42fad570SMark Brown .type = REGULATOR_VOLTAGE, 288*42fad570SMark Brown .owner = THIS_MODULE, 289*42fad570SMark Brown }, 290*42fad570SMark Brown }; 291*42fad570SMark Brown 292*42fad570SMark Brown static int __init wm8400_regulator_probe(struct platform_device *pdev) 293*42fad570SMark Brown { 294*42fad570SMark Brown struct regulator_dev *rdev; 295*42fad570SMark Brown 296*42fad570SMark Brown rdev = regulator_register(®ulators[pdev->id], &pdev->dev, 297*42fad570SMark Brown pdev->dev.driver_data); 298*42fad570SMark Brown 299*42fad570SMark Brown if (IS_ERR(rdev)) 300*42fad570SMark Brown return PTR_ERR(rdev); 301*42fad570SMark Brown 302*42fad570SMark Brown return 0; 303*42fad570SMark Brown } 304*42fad570SMark Brown 305*42fad570SMark Brown static int __devexit wm8400_regulator_remove(struct platform_device *pdev) 306*42fad570SMark Brown { 307*42fad570SMark Brown struct regulator_dev *rdev = platform_get_drvdata(pdev); 308*42fad570SMark Brown 309*42fad570SMark Brown regulator_unregister(rdev); 310*42fad570SMark Brown 311*42fad570SMark Brown return 0; 312*42fad570SMark Brown } 313*42fad570SMark Brown 314*42fad570SMark Brown static struct platform_driver wm8400_regulator_driver = { 315*42fad570SMark Brown .driver = { 316*42fad570SMark Brown .name = "wm8400-regulator", 317*42fad570SMark Brown }, 318*42fad570SMark Brown .probe = wm8400_regulator_probe, 319*42fad570SMark Brown .remove = __devexit_p(wm8400_regulator_remove), 320*42fad570SMark Brown }; 321*42fad570SMark Brown 322*42fad570SMark Brown /** 323*42fad570SMark Brown * wm8400_register_regulator - enable software control of a WM8400 regulator 324*42fad570SMark Brown * 325*42fad570SMark Brown * This function enables software control of a WM8400 regulator via 326*42fad570SMark Brown * the regulator API. It is intended to be called from the 327*42fad570SMark Brown * platform_init() callback of the WM8400 MFD driver. 328*42fad570SMark Brown * 329*42fad570SMark Brown * @param dev The WM8400 device to operate on. 330*42fad570SMark Brown * @param reg The regulator to control. 331*42fad570SMark Brown * @param initdata Regulator initdata for the regulator. 332*42fad570SMark Brown */ 333*42fad570SMark Brown int wm8400_register_regulator(struct device *dev, int reg, 334*42fad570SMark Brown struct regulator_init_data *initdata) 335*42fad570SMark Brown { 336*42fad570SMark Brown struct wm8400 *wm8400 = dev->driver_data; 337*42fad570SMark Brown 338*42fad570SMark Brown if (wm8400->regulators[reg].name) 339*42fad570SMark Brown return -EBUSY; 340*42fad570SMark Brown 341*42fad570SMark Brown initdata->driver_data = wm8400; 342*42fad570SMark Brown 343*42fad570SMark Brown wm8400->regulators[reg].name = "wm8400-regulator"; 344*42fad570SMark Brown wm8400->regulators[reg].id = reg; 345*42fad570SMark Brown wm8400->regulators[reg].dev.parent = dev; 346*42fad570SMark Brown wm8400->regulators[reg].dev.driver_data = wm8400; 347*42fad570SMark Brown wm8400->regulators[reg].dev.platform_data = initdata; 348*42fad570SMark Brown 349*42fad570SMark Brown return platform_device_register(&wm8400->regulators[reg]); 350*42fad570SMark Brown } 351*42fad570SMark Brown EXPORT_SYMBOL_GPL(wm8400_register_regulator); 352*42fad570SMark Brown 353*42fad570SMark Brown static int __init wm8400_regulator_init(void) 354*42fad570SMark Brown { 355*42fad570SMark Brown return platform_driver_register(&wm8400_regulator_driver); 356*42fad570SMark Brown } 357*42fad570SMark Brown module_init(wm8400_regulator_init); 358*42fad570SMark Brown 359*42fad570SMark Brown static void __exit wm8400_regulator_exit(void) 360*42fad570SMark Brown { 361*42fad570SMark Brown platform_driver_unregister(&wm8400_regulator_driver); 362*42fad570SMark Brown } 363*42fad570SMark Brown module_exit(wm8400_regulator_exit); 364*42fad570SMark Brown 365*42fad570SMark Brown MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); 366*42fad570SMark Brown MODULE_DESCRIPTION("WM8400 regulator driver"); 367*42fad570SMark Brown MODULE_LICENSE("GPL"); 368*42fad570SMark Brown MODULE_ALIAS("platform:wm8400-regulator"); 369