13cad5fc8SAxel Lin // SPDX-License-Identifier: GPL-2.0+ 23cad5fc8SAxel Lin // 33cad5fc8SAxel Lin // wm831x-dcdc.c -- DC-DC buck converter driver for the WM831x series 43cad5fc8SAxel Lin // 53cad5fc8SAxel Lin // Copyright 2009 Wolfson Microelectronics PLC. 63cad5fc8SAxel Lin // 73cad5fc8SAxel Lin // Author: Mark Brown <broonie@opensource.wolfsonmicro.com> 8e4ee831fSMark Brown 9e4ee831fSMark Brown #include <linux/module.h> 10e4ee831fSMark Brown #include <linux/moduleparam.h> 11e4ee831fSMark Brown #include <linux/init.h> 12e4ee831fSMark Brown #include <linux/bitops.h> 13e4ee831fSMark Brown #include <linux/err.h> 14e4ee831fSMark Brown #include <linux/i2c.h> 15e4ee831fSMark Brown #include <linux/platform_device.h> 16e4ee831fSMark Brown #include <linux/regulator/driver.h> 17e24a04c4SMark Brown #include <linux/regulator/machine.h> 189a5ed0baSLinus Walleij #include <linux/gpio/consumer.h> 195a0e3ad6STejun Heo #include <linux/slab.h> 20e4ee831fSMark Brown 21e4ee831fSMark Brown #include <linux/mfd/wm831x/core.h> 22e4ee831fSMark Brown #include <linux/mfd/wm831x/regulator.h> 23e4ee831fSMark Brown #include <linux/mfd/wm831x/pdata.h> 24e4ee831fSMark Brown 25e4ee831fSMark Brown #define WM831X_BUCKV_MAX_SELECTOR 0x68 26e4ee831fSMark Brown #define WM831X_BUCKP_MAX_SELECTOR 0x66 27e4ee831fSMark Brown 28e4ee831fSMark Brown #define WM831X_DCDC_MODE_FAST 0 29e4ee831fSMark Brown #define WM831X_DCDC_MODE_NORMAL 1 30e4ee831fSMark Brown #define WM831X_DCDC_MODE_IDLE 2 31e4ee831fSMark Brown #define WM831X_DCDC_MODE_STANDBY 3 32e4ee831fSMark Brown 3382caa978SMark Brown #define WM831X_DCDC_MAX_NAME 9 34e4ee831fSMark Brown 35e4ee831fSMark Brown /* Register offsets in control block */ 36e4ee831fSMark Brown #define WM831X_DCDC_CONTROL_1 0 37e4ee831fSMark Brown #define WM831X_DCDC_CONTROL_2 1 38e4ee831fSMark Brown #define WM831X_DCDC_ON_CONFIG 2 39e4ee831fSMark Brown #define WM831X_DCDC_SLEEP_CONTROL 3 40e24a04c4SMark Brown #define WM831X_DCDC_DVS_CONTROL 4 41e4ee831fSMark Brown 42e4ee831fSMark Brown /* 43e4ee831fSMark Brown * Shared 44e4ee831fSMark Brown */ 45e4ee831fSMark Brown 46e4ee831fSMark Brown struct wm831x_dcdc { 47e4ee831fSMark Brown char name[WM831X_DCDC_MAX_NAME]; 4882caa978SMark Brown char supply_name[WM831X_DCDC_MAX_NAME]; 49e4ee831fSMark Brown struct regulator_desc desc; 50e4ee831fSMark Brown int base; 51e4ee831fSMark Brown struct wm831x *wm831x; 52e4ee831fSMark Brown struct regulator_dev *regulator; 539a5ed0baSLinus Walleij struct gpio_desc *dvs_gpiod; 54e24a04c4SMark Brown int dvs_gpio_state; 55e24a04c4SMark Brown int on_vsel; 56e24a04c4SMark Brown int dvs_vsel; 57e4ee831fSMark Brown }; 58e4ee831fSMark Brown 59e4ee831fSMark Brown static unsigned int wm831x_dcdc_get_mode(struct regulator_dev *rdev) 60e4ee831fSMark Brown 61e4ee831fSMark Brown { 62e4ee831fSMark Brown struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev); 63e4ee831fSMark Brown struct wm831x *wm831x = dcdc->wm831x; 64e4ee831fSMark Brown u16 reg = dcdc->base + WM831X_DCDC_ON_CONFIG; 65e4ee831fSMark Brown int val; 66e4ee831fSMark Brown 67e4ee831fSMark Brown val = wm831x_reg_read(wm831x, reg); 68e4ee831fSMark Brown if (val < 0) 69e4ee831fSMark Brown return val; 70e4ee831fSMark Brown 71e4ee831fSMark Brown val = (val & WM831X_DC1_ON_MODE_MASK) >> WM831X_DC1_ON_MODE_SHIFT; 72e4ee831fSMark Brown 73e4ee831fSMark Brown switch (val) { 74e4ee831fSMark Brown case WM831X_DCDC_MODE_FAST: 75e4ee831fSMark Brown return REGULATOR_MODE_FAST; 76e4ee831fSMark Brown case WM831X_DCDC_MODE_NORMAL: 77e4ee831fSMark Brown return REGULATOR_MODE_NORMAL; 78e4ee831fSMark Brown case WM831X_DCDC_MODE_STANDBY: 79e4ee831fSMark Brown return REGULATOR_MODE_STANDBY; 80e4ee831fSMark Brown case WM831X_DCDC_MODE_IDLE: 81e4ee831fSMark Brown return REGULATOR_MODE_IDLE; 82e4ee831fSMark Brown default: 83e4ee831fSMark Brown BUG(); 849ee291a4SMark Brown return -EINVAL; 85e4ee831fSMark Brown } 86e4ee831fSMark Brown } 87e4ee831fSMark Brown 88e4ee831fSMark Brown static int wm831x_dcdc_set_mode_int(struct wm831x *wm831x, int reg, 89e4ee831fSMark Brown unsigned int mode) 90e4ee831fSMark Brown { 91e4ee831fSMark Brown int val; 92e4ee831fSMark Brown 93e4ee831fSMark Brown switch (mode) { 94e4ee831fSMark Brown case REGULATOR_MODE_FAST: 95e4ee831fSMark Brown val = WM831X_DCDC_MODE_FAST; 96e4ee831fSMark Brown break; 97e4ee831fSMark Brown case REGULATOR_MODE_NORMAL: 98e4ee831fSMark Brown val = WM831X_DCDC_MODE_NORMAL; 99e4ee831fSMark Brown break; 100e4ee831fSMark Brown case REGULATOR_MODE_STANDBY: 101e4ee831fSMark Brown val = WM831X_DCDC_MODE_STANDBY; 102e4ee831fSMark Brown break; 103e4ee831fSMark Brown case REGULATOR_MODE_IDLE: 104e4ee831fSMark Brown val = WM831X_DCDC_MODE_IDLE; 105e4ee831fSMark Brown break; 106e4ee831fSMark Brown default: 107e4ee831fSMark Brown return -EINVAL; 108e4ee831fSMark Brown } 109e4ee831fSMark Brown 110e4ee831fSMark Brown return wm831x_set_bits(wm831x, reg, WM831X_DC1_ON_MODE_MASK, 111e4ee831fSMark Brown val << WM831X_DC1_ON_MODE_SHIFT); 112e4ee831fSMark Brown } 113e4ee831fSMark Brown 114e4ee831fSMark Brown static int wm831x_dcdc_set_mode(struct regulator_dev *rdev, unsigned int mode) 115e4ee831fSMark Brown { 116e4ee831fSMark Brown struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev); 117e4ee831fSMark Brown struct wm831x *wm831x = dcdc->wm831x; 118e4ee831fSMark Brown u16 reg = dcdc->base + WM831X_DCDC_ON_CONFIG; 119e4ee831fSMark Brown 120e4ee831fSMark Brown return wm831x_dcdc_set_mode_int(wm831x, reg, mode); 121e4ee831fSMark Brown } 122e4ee831fSMark Brown 123e4ee831fSMark Brown static int wm831x_dcdc_set_suspend_mode(struct regulator_dev *rdev, 124e4ee831fSMark Brown unsigned int mode) 125e4ee831fSMark Brown { 126e4ee831fSMark Brown struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev); 127e4ee831fSMark Brown struct wm831x *wm831x = dcdc->wm831x; 128e4ee831fSMark Brown u16 reg = dcdc->base + WM831X_DCDC_SLEEP_CONTROL; 129e4ee831fSMark Brown 130e4ee831fSMark Brown return wm831x_dcdc_set_mode_int(wm831x, reg, mode); 131e4ee831fSMark Brown } 132e4ee831fSMark Brown 133e4ee831fSMark Brown static int wm831x_dcdc_get_status(struct regulator_dev *rdev) 134e4ee831fSMark Brown { 135e4ee831fSMark Brown struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev); 136e4ee831fSMark Brown struct wm831x *wm831x = dcdc->wm831x; 137e4ee831fSMark Brown int ret; 138e4ee831fSMark Brown 139e4ee831fSMark Brown /* First, check for errors */ 140e4ee831fSMark Brown ret = wm831x_reg_read(wm831x, WM831X_DCDC_UV_STATUS); 141e4ee831fSMark Brown if (ret < 0) 142e4ee831fSMark Brown return ret; 143e4ee831fSMark Brown 144e4ee831fSMark Brown if (ret & (1 << rdev_get_id(rdev))) { 145e4ee831fSMark Brown dev_dbg(wm831x->dev, "DCDC%d under voltage\n", 146e4ee831fSMark Brown rdev_get_id(rdev) + 1); 147e4ee831fSMark Brown return REGULATOR_STATUS_ERROR; 148e4ee831fSMark Brown } 149e4ee831fSMark Brown 150e4ee831fSMark Brown /* DCDC1 and DCDC2 can additionally detect high voltage/current */ 151e4ee831fSMark Brown if (rdev_get_id(rdev) < 2) { 152e4ee831fSMark Brown if (ret & (WM831X_DC1_OV_STS << rdev_get_id(rdev))) { 153e4ee831fSMark Brown dev_dbg(wm831x->dev, "DCDC%d over voltage\n", 154e4ee831fSMark Brown rdev_get_id(rdev) + 1); 155e4ee831fSMark Brown return REGULATOR_STATUS_ERROR; 156e4ee831fSMark Brown } 157e4ee831fSMark Brown 158e4ee831fSMark Brown if (ret & (WM831X_DC1_HC_STS << rdev_get_id(rdev))) { 159e4ee831fSMark Brown dev_dbg(wm831x->dev, "DCDC%d over current\n", 160e4ee831fSMark Brown rdev_get_id(rdev) + 1); 161e4ee831fSMark Brown return REGULATOR_STATUS_ERROR; 162e4ee831fSMark Brown } 163e4ee831fSMark Brown } 164e4ee831fSMark Brown 165e4ee831fSMark Brown /* Is the regulator on? */ 166e4ee831fSMark Brown ret = wm831x_reg_read(wm831x, WM831X_DCDC_STATUS); 167e4ee831fSMark Brown if (ret < 0) 168e4ee831fSMark Brown return ret; 169e4ee831fSMark Brown if (!(ret & (1 << rdev_get_id(rdev)))) 170e4ee831fSMark Brown return REGULATOR_STATUS_OFF; 171e4ee831fSMark Brown 172e4ee831fSMark Brown /* TODO: When we handle hardware control modes so we can report the 173e4ee831fSMark Brown * current mode. */ 174e4ee831fSMark Brown return REGULATOR_STATUS_ON; 175e4ee831fSMark Brown } 176e4ee831fSMark Brown 177e4ee831fSMark Brown static irqreturn_t wm831x_dcdc_uv_irq(int irq, void *data) 178e4ee831fSMark Brown { 179e4ee831fSMark Brown struct wm831x_dcdc *dcdc = data; 180e4ee831fSMark Brown 181119c4f50SSteve Twiss regulator_lock(dcdc->regulator); 182e4ee831fSMark Brown regulator_notifier_call_chain(dcdc->regulator, 183e4ee831fSMark Brown REGULATOR_EVENT_UNDER_VOLTAGE, 184e4ee831fSMark Brown NULL); 185119c4f50SSteve Twiss regulator_unlock(dcdc->regulator); 186e4ee831fSMark Brown 187e4ee831fSMark Brown return IRQ_HANDLED; 188e4ee831fSMark Brown } 189e4ee831fSMark Brown 190e4ee831fSMark Brown static irqreturn_t wm831x_dcdc_oc_irq(int irq, void *data) 191e4ee831fSMark Brown { 192e4ee831fSMark Brown struct wm831x_dcdc *dcdc = data; 193e4ee831fSMark Brown 194119c4f50SSteve Twiss regulator_lock(dcdc->regulator); 195e4ee831fSMark Brown regulator_notifier_call_chain(dcdc->regulator, 196e4ee831fSMark Brown REGULATOR_EVENT_OVER_CURRENT, 197e4ee831fSMark Brown NULL); 198119c4f50SSteve Twiss regulator_unlock(dcdc->regulator); 199e4ee831fSMark Brown 200e4ee831fSMark Brown return IRQ_HANDLED; 201e4ee831fSMark Brown } 202e4ee831fSMark Brown 203e4ee831fSMark Brown /* 204e4ee831fSMark Brown * BUCKV specifics 205e4ee831fSMark Brown */ 206e4ee831fSMark Brown 20760ab7f41SMatti Vaittinen static const struct linear_range wm831x_buckv_ranges[] = { 208ccffcb8eSAxel Lin REGULATOR_LINEAR_RANGE(600000, 0, 0x7, 0), 209ccffcb8eSAxel Lin REGULATOR_LINEAR_RANGE(600000, 0x8, 0x68, 12500), 210ccffcb8eSAxel Lin }; 211e24a04c4SMark Brown 212e24a04c4SMark Brown static int wm831x_buckv_set_dvs(struct regulator_dev *rdev, int state) 213e24a04c4SMark Brown { 214e24a04c4SMark Brown struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev); 215e24a04c4SMark Brown 216e24a04c4SMark Brown if (state == dcdc->dvs_gpio_state) 217e24a04c4SMark Brown return 0; 218e24a04c4SMark Brown 219e24a04c4SMark Brown dcdc->dvs_gpio_state = state; 2209a5ed0baSLinus Walleij gpiod_set_value(dcdc->dvs_gpiod, state); 221e24a04c4SMark Brown 222e24a04c4SMark Brown /* Should wait for DVS state change to be asserted if we have 223e24a04c4SMark Brown * a GPIO for it, for now assume the device is configured 224e24a04c4SMark Brown * for the fastest possible transition. 225e24a04c4SMark Brown */ 226e24a04c4SMark Brown 227e24a04c4SMark Brown return 0; 228e4ee831fSMark Brown } 229e4ee831fSMark Brown 230b5fb77e0SAxel Lin static int wm831x_buckv_set_voltage_sel(struct regulator_dev *rdev, 231b5fb77e0SAxel Lin unsigned vsel) 232e4ee831fSMark Brown { 233e4ee831fSMark Brown struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev); 234e24a04c4SMark Brown struct wm831x *wm831x = dcdc->wm831x; 235e24a04c4SMark Brown int on_reg = dcdc->base + WM831X_DCDC_ON_CONFIG; 236e24a04c4SMark Brown int dvs_reg = dcdc->base + WM831X_DCDC_DVS_CONTROL; 237b5fb77e0SAxel Lin int ret; 2383a93f2a9SMark Brown 239e24a04c4SMark Brown /* If this value is already set then do a GPIO update if we can */ 2409a5ed0baSLinus Walleij if (dcdc->dvs_gpiod && dcdc->on_vsel == vsel) 241e24a04c4SMark Brown return wm831x_buckv_set_dvs(rdev, 0); 242e24a04c4SMark Brown 2439a5ed0baSLinus Walleij if (dcdc->dvs_gpiod && dcdc->dvs_vsel == vsel) 244e24a04c4SMark Brown return wm831x_buckv_set_dvs(rdev, 1); 245e24a04c4SMark Brown 246e24a04c4SMark Brown /* Always set the ON status to the minimum voltage */ 247e24a04c4SMark Brown ret = wm831x_set_bits(wm831x, on_reg, WM831X_DC1_ON_VSEL_MASK, vsel); 248e24a04c4SMark Brown if (ret < 0) 249e24a04c4SMark Brown return ret; 250e24a04c4SMark Brown dcdc->on_vsel = vsel; 251e24a04c4SMark Brown 2529a5ed0baSLinus Walleij if (!dcdc->dvs_gpiod) 253e24a04c4SMark Brown return ret; 254e24a04c4SMark Brown 255e24a04c4SMark Brown /* Kick the voltage transition now */ 256e24a04c4SMark Brown ret = wm831x_buckv_set_dvs(rdev, 0); 257e24a04c4SMark Brown if (ret < 0) 258e24a04c4SMark Brown return ret; 259e24a04c4SMark Brown 26088cda60eSMark Brown /* 26188cda60eSMark Brown * If this VSEL is higher than the last one we've seen then 26288cda60eSMark Brown * remember it as the DVS VSEL. This is optimised for CPUfreq 26388cda60eSMark Brown * usage where we want to get to the highest voltage very 26488cda60eSMark Brown * quickly. 26588cda60eSMark Brown */ 26688cda60eSMark Brown if (vsel > dcdc->dvs_vsel) { 26788cda60eSMark Brown ret = wm831x_set_bits(wm831x, dvs_reg, 26888cda60eSMark Brown WM831X_DC1_DVS_VSEL_MASK, 26913ae633cSMark Brown vsel); 270e24a04c4SMark Brown if (ret == 0) 271e24a04c4SMark Brown dcdc->dvs_vsel = vsel; 272e24a04c4SMark Brown else 27388cda60eSMark Brown dev_warn(wm831x->dev, 27488cda60eSMark Brown "Failed to set DCDC DVS VSEL: %d\n", ret); 27588cda60eSMark Brown } 276e24a04c4SMark Brown 277e24a04c4SMark Brown return 0; 278e4ee831fSMark Brown } 279e4ee831fSMark Brown 280e4ee831fSMark Brown static int wm831x_buckv_set_suspend_voltage(struct regulator_dev *rdev, 281e4ee831fSMark Brown int uV) 282e4ee831fSMark Brown { 283e4ee831fSMark Brown struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev); 284e24a04c4SMark Brown struct wm831x *wm831x = dcdc->wm831x; 285e4ee831fSMark Brown u16 reg = dcdc->base + WM831X_DCDC_SLEEP_CONTROL; 286e24a04c4SMark Brown int vsel; 287e4ee831fSMark Brown 288ccffcb8eSAxel Lin vsel = regulator_map_voltage_linear_range(rdev, uV, uV); 289e24a04c4SMark Brown if (vsel < 0) 290e24a04c4SMark Brown return vsel; 291e24a04c4SMark Brown 292e24a04c4SMark Brown return wm831x_set_bits(wm831x, reg, WM831X_DC1_SLP_VSEL_MASK, vsel); 293e4ee831fSMark Brown } 294e4ee831fSMark Brown 295afb8bb80SMark Brown static int wm831x_buckv_get_voltage_sel(struct regulator_dev *rdev) 296e4ee831fSMark Brown { 297e4ee831fSMark Brown struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev); 298e4ee831fSMark Brown 2999a5ed0baSLinus Walleij if (dcdc->dvs_gpiod && dcdc->dvs_gpio_state) 300afb8bb80SMark Brown return dcdc->dvs_vsel; 301e24a04c4SMark Brown else 302afb8bb80SMark Brown return dcdc->on_vsel; 303e4ee831fSMark Brown } 304e4ee831fSMark Brown 305e4ee831fSMark Brown /* Current limit options */ 306c25d4788SAxel Lin static const unsigned int wm831x_dcdc_ilim[] = { 307c25d4788SAxel Lin 125000, 250000, 375000, 500000, 625000, 750000, 875000, 1000000 308e4ee831fSMark Brown }; 309e4ee831fSMark Brown 310b0d6dd3bSJulia Lawall static const struct regulator_ops wm831x_buckv_ops = { 311b5fb77e0SAxel Lin .set_voltage_sel = wm831x_buckv_set_voltage_sel, 312afb8bb80SMark Brown .get_voltage_sel = wm831x_buckv_get_voltage_sel, 313ccffcb8eSAxel Lin .list_voltage = regulator_list_voltage_linear_range, 314ccffcb8eSAxel Lin .map_voltage = regulator_map_voltage_linear_range, 315e4ee831fSMark Brown .set_suspend_voltage = wm831x_buckv_set_suspend_voltage, 31620eb641eSAxel Lin .set_current_limit = regulator_set_current_limit_regmap, 31720eb641eSAxel Lin .get_current_limit = regulator_get_current_limit_regmap, 318e4ee831fSMark Brown 3193d138fccSMark Brown .is_enabled = regulator_is_enabled_regmap, 3203d138fccSMark Brown .enable = regulator_enable_regmap, 3213d138fccSMark Brown .disable = regulator_disable_regmap, 322e4ee831fSMark Brown .get_status = wm831x_dcdc_get_status, 323e4ee831fSMark Brown .get_mode = wm831x_dcdc_get_mode, 324e4ee831fSMark Brown .set_mode = wm831x_dcdc_set_mode, 325e4ee831fSMark Brown .set_suspend_mode = wm831x_dcdc_set_suspend_mode, 326e4ee831fSMark Brown }; 327e4ee831fSMark Brown 328e24a04c4SMark Brown /* 329e24a04c4SMark Brown * Set up DVS control. We just log errors since we can still run 330e24a04c4SMark Brown * (with reduced performance) if we fail. 331e24a04c4SMark Brown */ 3321aa20d27SMark Brown static void wm831x_buckv_dvs_init(struct platform_device *pdev, 3331aa20d27SMark Brown struct wm831x_dcdc *dcdc, 334e24a04c4SMark Brown struct wm831x_buckv_pdata *pdata) 335e24a04c4SMark Brown { 336e24a04c4SMark Brown struct wm831x *wm831x = dcdc->wm831x; 337e24a04c4SMark Brown int ret; 338e24a04c4SMark Brown u16 ctrl; 339e24a04c4SMark Brown 3409a5ed0baSLinus Walleij if (!pdata) 341e24a04c4SMark Brown return; 342e24a04c4SMark Brown 343e24a04c4SMark Brown /* gpiolib won't let us read the GPIO status so pick the higher 344e24a04c4SMark Brown * of the two existing voltages so we take it as platform data. 345e24a04c4SMark Brown */ 346e24a04c4SMark Brown dcdc->dvs_gpio_state = pdata->dvs_init_state; 347e24a04c4SMark Brown 3489a5ed0baSLinus Walleij dcdc->dvs_gpiod = devm_gpiod_get(&pdev->dev, "dvs", 3499a5ed0baSLinus Walleij dcdc->dvs_gpio_state ? GPIOD_OUT_HIGH : GPIOD_OUT_LOW); 3509a5ed0baSLinus Walleij if (IS_ERR(dcdc->dvs_gpiod)) { 3519a5ed0baSLinus Walleij dev_err(wm831x->dev, "Failed to get %s DVS GPIO: %ld\n", 3529a5ed0baSLinus Walleij dcdc->name, PTR_ERR(dcdc->dvs_gpiod)); 353e24a04c4SMark Brown return; 354e24a04c4SMark Brown } 355e24a04c4SMark Brown 356b47ba9fdSMark Brown switch (pdata->dvs_control_src) { 357b47ba9fdSMark Brown case 1: 358b47ba9fdSMark Brown ctrl = 2 << WM831X_DC1_DVS_SRC_SHIFT; 359b47ba9fdSMark Brown break; 360b47ba9fdSMark Brown case 2: 361b47ba9fdSMark Brown ctrl = 3 << WM831X_DC1_DVS_SRC_SHIFT; 362b47ba9fdSMark Brown break; 363b47ba9fdSMark Brown default: 364b47ba9fdSMark Brown dev_err(wm831x->dev, "Invalid DVS control source %d for %s\n", 365b47ba9fdSMark Brown pdata->dvs_control_src, dcdc->name); 366b47ba9fdSMark Brown return; 367b47ba9fdSMark Brown } 368b47ba9fdSMark Brown 369c439b8f4SMark Brown /* If DVS_VSEL is set to the minimum value then raise it to ON_VSEL 370c439b8f4SMark Brown * to make bootstrapping a bit smoother. 371c439b8f4SMark Brown */ 372c439b8f4SMark Brown if (!dcdc->dvs_vsel) { 373c439b8f4SMark Brown ret = wm831x_set_bits(wm831x, 374c439b8f4SMark Brown dcdc->base + WM831X_DCDC_DVS_CONTROL, 375c439b8f4SMark Brown WM831X_DC1_DVS_VSEL_MASK, dcdc->on_vsel); 376c439b8f4SMark Brown if (ret == 0) 377c439b8f4SMark Brown dcdc->dvs_vsel = dcdc->on_vsel; 378c439b8f4SMark Brown else 379c439b8f4SMark Brown dev_warn(wm831x->dev, "Failed to set DVS_VSEL: %d\n", 380c439b8f4SMark Brown ret); 381c439b8f4SMark Brown } 382c439b8f4SMark Brown 383b47ba9fdSMark Brown ret = wm831x_set_bits(wm831x, dcdc->base + WM831X_DCDC_DVS_CONTROL, 384b47ba9fdSMark Brown WM831X_DC1_DVS_SRC_MASK, ctrl); 385b47ba9fdSMark Brown if (ret < 0) { 386b47ba9fdSMark Brown dev_err(wm831x->dev, "Failed to set %s DVS source: %d\n", 387b47ba9fdSMark Brown dcdc->name, ret); 388b47ba9fdSMark Brown } 389e24a04c4SMark Brown } 390e24a04c4SMark Brown 391a5023574SBill Pemberton static int wm831x_buckv_probe(struct platform_device *pdev) 392e4ee831fSMark Brown { 393e4ee831fSMark Brown struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); 394dff91d0bSJingoo Han struct wm831x_pdata *pdata = dev_get_platdata(wm831x->dev); 395c172708dSMark Brown struct regulator_config config = { }; 396137a6354SMark Brown int id; 397e4ee831fSMark Brown struct wm831x_dcdc *dcdc; 398e4ee831fSMark Brown struct resource *res; 399e4ee831fSMark Brown int ret, irq; 400e4ee831fSMark Brown 401137a6354SMark Brown if (pdata && pdata->wm831x_num) 402137a6354SMark Brown id = (pdata->wm831x_num * 10) + 1; 403137a6354SMark Brown else 404137a6354SMark Brown id = 0; 405137a6354SMark Brown id = pdev->id - id; 406137a6354SMark Brown 407e4ee831fSMark Brown dev_dbg(&pdev->dev, "Probing DCDC%d\n", id + 1); 408e4ee831fSMark Brown 409fded2f4fSMark Brown dcdc = devm_kzalloc(&pdev->dev, sizeof(struct wm831x_dcdc), 410fded2f4fSMark Brown GFP_KERNEL); 4115730aa57SSachin Kamat if (!dcdc) 412e4ee831fSMark Brown return -ENOMEM; 413e4ee831fSMark Brown 414e4ee831fSMark Brown dcdc->wm831x = wm831x; 415e4ee831fSMark Brown 4165656098eSMark Brown res = platform_get_resource(pdev, IORESOURCE_REG, 0); 417e4ee831fSMark Brown if (res == NULL) { 4185656098eSMark Brown dev_err(&pdev->dev, "No REG resource\n"); 419e4ee831fSMark Brown ret = -EINVAL; 420e4ee831fSMark Brown goto err; 421e4ee831fSMark Brown } 422e4ee831fSMark Brown dcdc->base = res->start; 423e4ee831fSMark Brown 424e4ee831fSMark Brown snprintf(dcdc->name, sizeof(dcdc->name), "DCDC%d", id + 1); 425e4ee831fSMark Brown dcdc->desc.name = dcdc->name; 42682caa978SMark Brown 42782caa978SMark Brown snprintf(dcdc->supply_name, sizeof(dcdc->supply_name), 42882caa978SMark Brown "DC%dVDD", id + 1); 42982caa978SMark Brown dcdc->desc.supply_name = dcdc->supply_name; 43082caa978SMark Brown 431e4ee831fSMark Brown dcdc->desc.id = id; 432e4ee831fSMark Brown dcdc->desc.type = REGULATOR_VOLTAGE; 433e4ee831fSMark Brown dcdc->desc.n_voltages = WM831X_BUCKV_MAX_SELECTOR + 1; 434ccffcb8eSAxel Lin dcdc->desc.linear_ranges = wm831x_buckv_ranges; 435ccffcb8eSAxel Lin dcdc->desc.n_linear_ranges = ARRAY_SIZE(wm831x_buckv_ranges); 436e4ee831fSMark Brown dcdc->desc.ops = &wm831x_buckv_ops; 437e4ee831fSMark Brown dcdc->desc.owner = THIS_MODULE; 4383d138fccSMark Brown dcdc->desc.enable_reg = WM831X_DCDC_ENABLE; 4393d138fccSMark Brown dcdc->desc.enable_mask = 1 << id; 44020eb641eSAxel Lin dcdc->desc.csel_reg = dcdc->base + WM831X_DCDC_CONTROL_2; 44120eb641eSAxel Lin dcdc->desc.csel_mask = WM831X_DC1_HC_THR_MASK; 44220eb641eSAxel Lin dcdc->desc.n_current_limits = ARRAY_SIZE(wm831x_dcdc_ilim); 44320eb641eSAxel Lin dcdc->desc.curr_table = wm831x_dcdc_ilim; 444e4ee831fSMark Brown 445e24a04c4SMark Brown ret = wm831x_reg_read(wm831x, dcdc->base + WM831X_DCDC_ON_CONFIG); 446e24a04c4SMark Brown if (ret < 0) { 447e24a04c4SMark Brown dev_err(wm831x->dev, "Failed to read ON VSEL: %d\n", ret); 448e24a04c4SMark Brown goto err; 449e24a04c4SMark Brown } 450e24a04c4SMark Brown dcdc->on_vsel = ret & WM831X_DC1_ON_VSEL_MASK; 451e24a04c4SMark Brown 452a1b81dd3SMark Brown ret = wm831x_reg_read(wm831x, dcdc->base + WM831X_DCDC_DVS_CONTROL); 453e24a04c4SMark Brown if (ret < 0) { 454e24a04c4SMark Brown dev_err(wm831x->dev, "Failed to read DVS VSEL: %d\n", ret); 455e24a04c4SMark Brown goto err; 456e24a04c4SMark Brown } 457e24a04c4SMark Brown dcdc->dvs_vsel = ret & WM831X_DC1_DVS_VSEL_MASK; 458e24a04c4SMark Brown 459f0b067d9SMark Brown if (pdata && pdata->dcdc[id]) 4601aa20d27SMark Brown wm831x_buckv_dvs_init(pdev, dcdc, 4611aa20d27SMark Brown pdata->dcdc[id]->driver_data); 462e24a04c4SMark Brown 463c172708dSMark Brown config.dev = pdev->dev.parent; 464b7ca8788SMark Brown if (pdata) 465c172708dSMark Brown config.init_data = pdata->dcdc[id]; 466c172708dSMark Brown config.driver_data = dcdc; 4673d138fccSMark Brown config.regmap = wm831x->regmap; 468c172708dSMark Brown 469d73b4cb7SMark Brown dcdc->regulator = devm_regulator_register(&pdev->dev, &dcdc->desc, 470d73b4cb7SMark Brown &config); 471e4ee831fSMark Brown if (IS_ERR(dcdc->regulator)) { 472e4ee831fSMark Brown ret = PTR_ERR(dcdc->regulator); 473e4ee831fSMark Brown dev_err(wm831x->dev, "Failed to register DCDC%d: %d\n", 474e4ee831fSMark Brown id + 1, ret); 475e4ee831fSMark Brown goto err; 476e4ee831fSMark Brown } 477e4ee831fSMark Brown 478cd99758bSMark Brown irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "UV")); 479b0c4c0c6SMark Brown ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, 480b0c4c0c6SMark Brown wm831x_dcdc_uv_irq, 48129454738SFabio Estevam IRQF_TRIGGER_RISING | IRQF_ONESHOT, 48229454738SFabio Estevam dcdc->name, dcdc); 483e4ee831fSMark Brown if (ret != 0) { 484e4ee831fSMark Brown dev_err(&pdev->dev, "Failed to request UV IRQ %d: %d\n", 485e4ee831fSMark Brown irq, ret); 486d73b4cb7SMark Brown goto err; 487e4ee831fSMark Brown } 488e4ee831fSMark Brown 489cd99758bSMark Brown irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "HC")); 490b0c4c0c6SMark Brown ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, 491b0c4c0c6SMark Brown wm831x_dcdc_oc_irq, 49229454738SFabio Estevam IRQF_TRIGGER_RISING | IRQF_ONESHOT, 49329454738SFabio Estevam dcdc->name, dcdc); 494e4ee831fSMark Brown if (ret != 0) { 495e4ee831fSMark Brown dev_err(&pdev->dev, "Failed to request HC IRQ %d: %d\n", 496e4ee831fSMark Brown irq, ret); 497d73b4cb7SMark Brown goto err; 498e4ee831fSMark Brown } 499e4ee831fSMark Brown 500e4ee831fSMark Brown platform_set_drvdata(pdev, dcdc); 501e4ee831fSMark Brown 502e4ee831fSMark Brown return 0; 503e4ee831fSMark Brown 504e4ee831fSMark Brown err: 505e4ee831fSMark Brown return ret; 506e4ee831fSMark Brown } 507e4ee831fSMark Brown 508e4ee831fSMark Brown static struct platform_driver wm831x_buckv_driver = { 509e4ee831fSMark Brown .probe = wm831x_buckv_probe, 510e4ee831fSMark Brown .driver = { 511e4ee831fSMark Brown .name = "wm831x-buckv", 512e4ee831fSMark Brown }, 513e4ee831fSMark Brown }; 514e4ee831fSMark Brown 515e4ee831fSMark Brown /* 516e4ee831fSMark Brown * BUCKP specifics 517e4ee831fSMark Brown */ 518e4ee831fSMark Brown 519d580cb5eSAxel Lin static int wm831x_buckp_set_suspend_voltage(struct regulator_dev *rdev, int uV) 520e4ee831fSMark Brown { 521e4ee831fSMark Brown struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev); 522e4ee831fSMark Brown struct wm831x *wm831x = dcdc->wm831x; 523e4ee831fSMark Brown u16 reg = dcdc->base + WM831X_DCDC_SLEEP_CONTROL; 524d580cb5eSAxel Lin int sel; 525e4ee831fSMark Brown 526d580cb5eSAxel Lin sel = regulator_map_voltage_linear(rdev, uV, uV); 527d580cb5eSAxel Lin if (sel < 0) 528d580cb5eSAxel Lin return sel; 529d580cb5eSAxel Lin 530d580cb5eSAxel Lin return wm831x_set_bits(wm831x, reg, WM831X_DC3_ON_VSEL_MASK, sel); 531e4ee831fSMark Brown } 532e4ee831fSMark Brown 533b0d6dd3bSJulia Lawall static const struct regulator_ops wm831x_buckp_ops = { 534d580cb5eSAxel Lin .set_voltage_sel = regulator_set_voltage_sel_regmap, 535817436e7SMark Brown .get_voltage_sel = regulator_get_voltage_sel_regmap, 536c70ad9dcSAxel Lin .list_voltage = regulator_list_voltage_linear, 537d580cb5eSAxel Lin .map_voltage = regulator_map_voltage_linear, 538e4ee831fSMark Brown .set_suspend_voltage = wm831x_buckp_set_suspend_voltage, 539e4ee831fSMark Brown 5403d138fccSMark Brown .is_enabled = regulator_is_enabled_regmap, 5413d138fccSMark Brown .enable = regulator_enable_regmap, 5423d138fccSMark Brown .disable = regulator_disable_regmap, 543e4ee831fSMark Brown .get_status = wm831x_dcdc_get_status, 544e4ee831fSMark Brown .get_mode = wm831x_dcdc_get_mode, 545e4ee831fSMark Brown .set_mode = wm831x_dcdc_set_mode, 546e4ee831fSMark Brown .set_suspend_mode = wm831x_dcdc_set_suspend_mode, 547e4ee831fSMark Brown }; 548e4ee831fSMark Brown 549a5023574SBill Pemberton static int wm831x_buckp_probe(struct platform_device *pdev) 550e4ee831fSMark Brown { 551e4ee831fSMark Brown struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); 552dff91d0bSJingoo Han struct wm831x_pdata *pdata = dev_get_platdata(wm831x->dev); 553c172708dSMark Brown struct regulator_config config = { }; 554137a6354SMark Brown int id; 555e4ee831fSMark Brown struct wm831x_dcdc *dcdc; 556e4ee831fSMark Brown struct resource *res; 557e4ee831fSMark Brown int ret, irq; 558e4ee831fSMark Brown 559137a6354SMark Brown if (pdata && pdata->wm831x_num) 560137a6354SMark Brown id = (pdata->wm831x_num * 10) + 1; 561137a6354SMark Brown else 562137a6354SMark Brown id = 0; 563137a6354SMark Brown id = pdev->id - id; 564137a6354SMark Brown 565e4ee831fSMark Brown dev_dbg(&pdev->dev, "Probing DCDC%d\n", id + 1); 566e4ee831fSMark Brown 567fded2f4fSMark Brown dcdc = devm_kzalloc(&pdev->dev, sizeof(struct wm831x_dcdc), 568fded2f4fSMark Brown GFP_KERNEL); 5695730aa57SSachin Kamat if (!dcdc) 570e4ee831fSMark Brown return -ENOMEM; 571e4ee831fSMark Brown 572e4ee831fSMark Brown dcdc->wm831x = wm831x; 573e4ee831fSMark Brown 5745656098eSMark Brown res = platform_get_resource(pdev, IORESOURCE_REG, 0); 575e4ee831fSMark Brown if (res == NULL) { 5765656098eSMark Brown dev_err(&pdev->dev, "No REG resource\n"); 577e4ee831fSMark Brown ret = -EINVAL; 578e4ee831fSMark Brown goto err; 579e4ee831fSMark Brown } 580e4ee831fSMark Brown dcdc->base = res->start; 581e4ee831fSMark Brown 582e4ee831fSMark Brown snprintf(dcdc->name, sizeof(dcdc->name), "DCDC%d", id + 1); 583e4ee831fSMark Brown dcdc->desc.name = dcdc->name; 58482caa978SMark Brown 58582caa978SMark Brown snprintf(dcdc->supply_name, sizeof(dcdc->supply_name), 58682caa978SMark Brown "DC%dVDD", id + 1); 58782caa978SMark Brown dcdc->desc.supply_name = dcdc->supply_name; 58882caa978SMark Brown 589e4ee831fSMark Brown dcdc->desc.id = id; 590e4ee831fSMark Brown dcdc->desc.type = REGULATOR_VOLTAGE; 591e4ee831fSMark Brown dcdc->desc.n_voltages = WM831X_BUCKP_MAX_SELECTOR + 1; 592e4ee831fSMark Brown dcdc->desc.ops = &wm831x_buckp_ops; 593e4ee831fSMark Brown dcdc->desc.owner = THIS_MODULE; 594817436e7SMark Brown dcdc->desc.vsel_reg = dcdc->base + WM831X_DCDC_ON_CONFIG; 595817436e7SMark Brown dcdc->desc.vsel_mask = WM831X_DC3_ON_VSEL_MASK; 5963d138fccSMark Brown dcdc->desc.enable_reg = WM831X_DCDC_ENABLE; 5973d138fccSMark Brown dcdc->desc.enable_mask = 1 << id; 598c70ad9dcSAxel Lin dcdc->desc.min_uV = 850000; 599c70ad9dcSAxel Lin dcdc->desc.uV_step = 25000; 600e4ee831fSMark Brown 601c172708dSMark Brown config.dev = pdev->dev.parent; 602b7ca8788SMark Brown if (pdata) 603c172708dSMark Brown config.init_data = pdata->dcdc[id]; 604c172708dSMark Brown config.driver_data = dcdc; 605817436e7SMark Brown config.regmap = wm831x->regmap; 606c172708dSMark Brown 607d73b4cb7SMark Brown dcdc->regulator = devm_regulator_register(&pdev->dev, &dcdc->desc, 608d73b4cb7SMark Brown &config); 609e4ee831fSMark Brown if (IS_ERR(dcdc->regulator)) { 610e4ee831fSMark Brown ret = PTR_ERR(dcdc->regulator); 611e4ee831fSMark Brown dev_err(wm831x->dev, "Failed to register DCDC%d: %d\n", 612e4ee831fSMark Brown id + 1, ret); 613e4ee831fSMark Brown goto err; 614e4ee831fSMark Brown } 615e4ee831fSMark Brown 616cd99758bSMark Brown irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "UV")); 617b0c4c0c6SMark Brown ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, 618b0c4c0c6SMark Brown wm831x_dcdc_uv_irq, 61929454738SFabio Estevam IRQF_TRIGGER_RISING | IRQF_ONESHOT, 62029454738SFabio Estevam dcdc->name, dcdc); 621e4ee831fSMark Brown if (ret != 0) { 622e4ee831fSMark Brown dev_err(&pdev->dev, "Failed to request UV IRQ %d: %d\n", 623e4ee831fSMark Brown irq, ret); 624d73b4cb7SMark Brown goto err; 625e4ee831fSMark Brown } 626e4ee831fSMark Brown 627e4ee831fSMark Brown platform_set_drvdata(pdev, dcdc); 628e4ee831fSMark Brown 629e4ee831fSMark Brown return 0; 630e4ee831fSMark Brown 631e4ee831fSMark Brown err: 632e4ee831fSMark Brown return ret; 633e4ee831fSMark Brown } 634e4ee831fSMark Brown 635e4ee831fSMark Brown static struct platform_driver wm831x_buckp_driver = { 636e4ee831fSMark Brown .probe = wm831x_buckp_probe, 637e4ee831fSMark Brown .driver = { 638e4ee831fSMark Brown .name = "wm831x-buckp", 639e4ee831fSMark Brown }, 640e4ee831fSMark Brown }; 641e4ee831fSMark Brown 6428267a9baSMark Brown /* 6431304850dSMark Brown * DCDC boost convertors 6441304850dSMark Brown */ 6451304850dSMark Brown 6461304850dSMark Brown static int wm831x_boostp_get_status(struct regulator_dev *rdev) 6471304850dSMark Brown { 6481304850dSMark Brown struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev); 6491304850dSMark Brown struct wm831x *wm831x = dcdc->wm831x; 6501304850dSMark Brown int ret; 6511304850dSMark Brown 6521304850dSMark Brown /* First, check for errors */ 6531304850dSMark Brown ret = wm831x_reg_read(wm831x, WM831X_DCDC_UV_STATUS); 6541304850dSMark Brown if (ret < 0) 6551304850dSMark Brown return ret; 6561304850dSMark Brown 6571304850dSMark Brown if (ret & (1 << rdev_get_id(rdev))) { 6581304850dSMark Brown dev_dbg(wm831x->dev, "DCDC%d under voltage\n", 6591304850dSMark Brown rdev_get_id(rdev) + 1); 6601304850dSMark Brown return REGULATOR_STATUS_ERROR; 6611304850dSMark Brown } 6621304850dSMark Brown 6631304850dSMark Brown /* Is the regulator on? */ 6641304850dSMark Brown ret = wm831x_reg_read(wm831x, WM831X_DCDC_STATUS); 6651304850dSMark Brown if (ret < 0) 6661304850dSMark Brown return ret; 6671304850dSMark Brown if (ret & (1 << rdev_get_id(rdev))) 6681304850dSMark Brown return REGULATOR_STATUS_ON; 6691304850dSMark Brown else 6701304850dSMark Brown return REGULATOR_STATUS_OFF; 6711304850dSMark Brown } 6721304850dSMark Brown 673b0d6dd3bSJulia Lawall static const struct regulator_ops wm831x_boostp_ops = { 6741304850dSMark Brown .get_status = wm831x_boostp_get_status, 6751304850dSMark Brown 6763d138fccSMark Brown .is_enabled = regulator_is_enabled_regmap, 6773d138fccSMark Brown .enable = regulator_enable_regmap, 6783d138fccSMark Brown .disable = regulator_disable_regmap, 6791304850dSMark Brown }; 6801304850dSMark Brown 681a5023574SBill Pemberton static int wm831x_boostp_probe(struct platform_device *pdev) 6821304850dSMark Brown { 6831304850dSMark Brown struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); 684dff91d0bSJingoo Han struct wm831x_pdata *pdata = dev_get_platdata(wm831x->dev); 685c172708dSMark Brown struct regulator_config config = { }; 6861304850dSMark Brown int id = pdev->id % ARRAY_SIZE(pdata->dcdc); 6871304850dSMark Brown struct wm831x_dcdc *dcdc; 6881304850dSMark Brown struct resource *res; 6891304850dSMark Brown int ret, irq; 6901304850dSMark Brown 6911304850dSMark Brown dev_dbg(&pdev->dev, "Probing DCDC%d\n", id + 1); 6921304850dSMark Brown 6931304850dSMark Brown if (pdata == NULL || pdata->dcdc[id] == NULL) 6941304850dSMark Brown return -ENODEV; 6951304850dSMark Brown 6964c60165dSAxel Lin dcdc = devm_kzalloc(&pdev->dev, sizeof(struct wm831x_dcdc), GFP_KERNEL); 6975730aa57SSachin Kamat if (!dcdc) 6981304850dSMark Brown return -ENOMEM; 6991304850dSMark Brown 7001304850dSMark Brown dcdc->wm831x = wm831x; 7011304850dSMark Brown 7025656098eSMark Brown res = platform_get_resource(pdev, IORESOURCE_REG, 0); 7031304850dSMark Brown if (res == NULL) { 7045656098eSMark Brown dev_err(&pdev->dev, "No REG resource\n"); 705477b2bacSFabio Estevam return -EINVAL; 7061304850dSMark Brown } 7071304850dSMark Brown dcdc->base = res->start; 7081304850dSMark Brown 7091304850dSMark Brown snprintf(dcdc->name, sizeof(dcdc->name), "DCDC%d", id + 1); 7101304850dSMark Brown dcdc->desc.name = dcdc->name; 7111304850dSMark Brown dcdc->desc.id = id; 7121304850dSMark Brown dcdc->desc.type = REGULATOR_VOLTAGE; 7131304850dSMark Brown dcdc->desc.ops = &wm831x_boostp_ops; 7141304850dSMark Brown dcdc->desc.owner = THIS_MODULE; 7153d138fccSMark Brown dcdc->desc.enable_reg = WM831X_DCDC_ENABLE; 7163d138fccSMark Brown dcdc->desc.enable_mask = 1 << id; 7171304850dSMark Brown 718c172708dSMark Brown config.dev = pdev->dev.parent; 719f0b067d9SMark Brown if (pdata) 720c172708dSMark Brown config.init_data = pdata->dcdc[id]; 721c172708dSMark Brown config.driver_data = dcdc; 7223d138fccSMark Brown config.regmap = wm831x->regmap; 723c172708dSMark Brown 724d73b4cb7SMark Brown dcdc->regulator = devm_regulator_register(&pdev->dev, &dcdc->desc, 725d73b4cb7SMark Brown &config); 7261304850dSMark Brown if (IS_ERR(dcdc->regulator)) { 7271304850dSMark Brown ret = PTR_ERR(dcdc->regulator); 7281304850dSMark Brown dev_err(wm831x->dev, "Failed to register DCDC%d: %d\n", 7291304850dSMark Brown id + 1, ret); 730477b2bacSFabio Estevam return ret; 7311304850dSMark Brown } 7321304850dSMark Brown 733cd99758bSMark Brown irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "UV")); 734b0c4c0c6SMark Brown ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, 735b0c4c0c6SMark Brown wm831x_dcdc_uv_irq, 73629454738SFabio Estevam IRQF_TRIGGER_RISING | IRQF_ONESHOT, 73729454738SFabio Estevam dcdc->name, 7381304850dSMark Brown dcdc); 7391304850dSMark Brown if (ret != 0) { 7401304850dSMark Brown dev_err(&pdev->dev, "Failed to request UV IRQ %d: %d\n", 7411304850dSMark Brown irq, ret); 742477b2bacSFabio Estevam return ret; 7431304850dSMark Brown } 7441304850dSMark Brown 7451304850dSMark Brown platform_set_drvdata(pdev, dcdc); 7461304850dSMark Brown 7471304850dSMark Brown return 0; 7481304850dSMark Brown } 7491304850dSMark Brown 7501304850dSMark Brown static struct platform_driver wm831x_boostp_driver = { 7511304850dSMark Brown .probe = wm831x_boostp_probe, 7521304850dSMark Brown .driver = { 7531304850dSMark Brown .name = "wm831x-boostp", 7541304850dSMark Brown }, 7551304850dSMark Brown }; 7561304850dSMark Brown 7571304850dSMark Brown /* 7588267a9baSMark Brown * External Power Enable 7598267a9baSMark Brown * 7608267a9baSMark Brown * These aren't actually DCDCs but look like them in hardware so share 7618267a9baSMark Brown * code. 7628267a9baSMark Brown */ 7638267a9baSMark Brown 7648267a9baSMark Brown #define WM831X_EPE_BASE 6 7658267a9baSMark Brown 766b0d6dd3bSJulia Lawall static const struct regulator_ops wm831x_epe_ops = { 7673d138fccSMark Brown .is_enabled = regulator_is_enabled_regmap, 7683d138fccSMark Brown .enable = regulator_enable_regmap, 7693d138fccSMark Brown .disable = regulator_disable_regmap, 7708267a9baSMark Brown .get_status = wm831x_dcdc_get_status, 7718267a9baSMark Brown }; 7728267a9baSMark Brown 773a5023574SBill Pemberton static int wm831x_epe_probe(struct platform_device *pdev) 7748267a9baSMark Brown { 7758267a9baSMark Brown struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); 776dff91d0bSJingoo Han struct wm831x_pdata *pdata = dev_get_platdata(wm831x->dev); 777c172708dSMark Brown struct regulator_config config = { }; 7788267a9baSMark Brown int id = pdev->id % ARRAY_SIZE(pdata->epe); 7798267a9baSMark Brown struct wm831x_dcdc *dcdc; 7808267a9baSMark Brown int ret; 7818267a9baSMark Brown 7828267a9baSMark Brown dev_dbg(&pdev->dev, "Probing EPE%d\n", id + 1); 7838267a9baSMark Brown 7844c60165dSAxel Lin dcdc = devm_kzalloc(&pdev->dev, sizeof(struct wm831x_dcdc), GFP_KERNEL); 7855730aa57SSachin Kamat if (!dcdc) 7868267a9baSMark Brown return -ENOMEM; 7878267a9baSMark Brown 7888267a9baSMark Brown dcdc->wm831x = wm831x; 7898267a9baSMark Brown 7908267a9baSMark Brown /* For current parts this is correct; probably need to revisit 7918267a9baSMark Brown * in future. 7928267a9baSMark Brown */ 7938267a9baSMark Brown snprintf(dcdc->name, sizeof(dcdc->name), "EPE%d", id + 1); 7948267a9baSMark Brown dcdc->desc.name = dcdc->name; 7958267a9baSMark Brown dcdc->desc.id = id + WM831X_EPE_BASE; /* Offset in DCDC registers */ 7968267a9baSMark Brown dcdc->desc.ops = &wm831x_epe_ops; 7978267a9baSMark Brown dcdc->desc.type = REGULATOR_VOLTAGE; 7988267a9baSMark Brown dcdc->desc.owner = THIS_MODULE; 7993d138fccSMark Brown dcdc->desc.enable_reg = WM831X_DCDC_ENABLE; 8003d138fccSMark Brown dcdc->desc.enable_mask = 1 << dcdc->desc.id; 8018267a9baSMark Brown 802c172708dSMark Brown config.dev = pdev->dev.parent; 803b7ca8788SMark Brown if (pdata) 804c172708dSMark Brown config.init_data = pdata->epe[id]; 805c172708dSMark Brown config.driver_data = dcdc; 8063d138fccSMark Brown config.regmap = wm831x->regmap; 807c172708dSMark Brown 808d73b4cb7SMark Brown dcdc->regulator = devm_regulator_register(&pdev->dev, &dcdc->desc, 809d73b4cb7SMark Brown &config); 8108267a9baSMark Brown if (IS_ERR(dcdc->regulator)) { 8118267a9baSMark Brown ret = PTR_ERR(dcdc->regulator); 8128267a9baSMark Brown dev_err(wm831x->dev, "Failed to register EPE%d: %d\n", 8138267a9baSMark Brown id + 1, ret); 8148267a9baSMark Brown goto err; 8158267a9baSMark Brown } 8168267a9baSMark Brown 8178267a9baSMark Brown platform_set_drvdata(pdev, dcdc); 8188267a9baSMark Brown 8198267a9baSMark Brown return 0; 8208267a9baSMark Brown 8218267a9baSMark Brown err: 8228267a9baSMark Brown return ret; 8238267a9baSMark Brown } 8248267a9baSMark Brown 8258267a9baSMark Brown static struct platform_driver wm831x_epe_driver = { 8268267a9baSMark Brown .probe = wm831x_epe_probe, 8278267a9baSMark Brown .driver = { 8288267a9baSMark Brown .name = "wm831x-epe", 8298267a9baSMark Brown }, 8308267a9baSMark Brown }; 8318267a9baSMark Brown 83255e03e9cSThierry Reding static struct platform_driver * const drivers[] = { 83355e03e9cSThierry Reding &wm831x_buckv_driver, 83455e03e9cSThierry Reding &wm831x_buckp_driver, 83555e03e9cSThierry Reding &wm831x_boostp_driver, 83655e03e9cSThierry Reding &wm831x_epe_driver, 83755e03e9cSThierry Reding }; 83855e03e9cSThierry Reding 839e4ee831fSMark Brown static int __init wm831x_dcdc_init(void) 840e4ee831fSMark Brown { 84155e03e9cSThierry Reding return platform_register_drivers(drivers, ARRAY_SIZE(drivers)); 842e4ee831fSMark Brown } 843e4ee831fSMark Brown subsys_initcall(wm831x_dcdc_init); 844e4ee831fSMark Brown 845e4ee831fSMark Brown static void __exit wm831x_dcdc_exit(void) 846e4ee831fSMark Brown { 84755e03e9cSThierry Reding platform_unregister_drivers(drivers, ARRAY_SIZE(drivers)); 848e4ee831fSMark Brown } 849e4ee831fSMark Brown module_exit(wm831x_dcdc_exit); 850e4ee831fSMark Brown 851e4ee831fSMark Brown /* Module information */ 852e4ee831fSMark Brown MODULE_AUTHOR("Mark Brown"); 853e4ee831fSMark Brown MODULE_DESCRIPTION("WM831x DC-DC convertor driver"); 854e4ee831fSMark Brown MODULE_LICENSE("GPL"); 855e4ee831fSMark Brown MODULE_ALIAS("platform:wm831x-buckv"); 856e4ee831fSMark Brown MODULE_ALIAS("platform:wm831x-buckp"); 85743f1f216SAxel Lin MODULE_ALIAS("platform:wm831x-boostp"); 85824b43150SMark Brown MODULE_ALIAS("platform:wm831x-epe"); 859