1ade7515fSKim, Milo /* 2ade7515fSKim, Milo * TI LP8788 MFD - buck regulator driver 3ade7515fSKim, Milo * 4ade7515fSKim, Milo * Copyright 2012 Texas Instruments 5ade7515fSKim, Milo * 6ade7515fSKim, Milo * Author: Milo(Woogyom) Kim <milo.kim@ti.com> 7ade7515fSKim, Milo * 8ade7515fSKim, Milo * This program is free software; you can redistribute it and/or modify 9ade7515fSKim, Milo * it under the terms of the GNU General Public License version 2 as 10ade7515fSKim, Milo * published by the Free Software Foundation. 11ade7515fSKim, Milo * 12ade7515fSKim, Milo */ 13ade7515fSKim, Milo 14ade7515fSKim, Milo #include <linux/module.h> 15ade7515fSKim, Milo #include <linux/slab.h> 16ade7515fSKim, Milo #include <linux/err.h> 17ade7515fSKim, Milo #include <linux/platform_device.h> 18ade7515fSKim, Milo #include <linux/regulator/driver.h> 19ade7515fSKim, Milo #include <linux/mfd/lp8788.h> 20ade7515fSKim, Milo #include <linux/gpio.h> 21ade7515fSKim, Milo 22ade7515fSKim, Milo /* register address */ 23ade7515fSKim, Milo #define LP8788_EN_BUCK 0x0C 24ade7515fSKim, Milo #define LP8788_BUCK_DVS_SEL 0x1D 25ade7515fSKim, Milo #define LP8788_BUCK1_VOUT0 0x1E 26ade7515fSKim, Milo #define LP8788_BUCK1_VOUT1 0x1F 27ade7515fSKim, Milo #define LP8788_BUCK1_VOUT2 0x20 28ade7515fSKim, Milo #define LP8788_BUCK1_VOUT3 0x21 29ade7515fSKim, Milo #define LP8788_BUCK2_VOUT0 0x22 30ade7515fSKim, Milo #define LP8788_BUCK2_VOUT1 0x23 31ade7515fSKim, Milo #define LP8788_BUCK2_VOUT2 0x24 32ade7515fSKim, Milo #define LP8788_BUCK2_VOUT3 0x25 33ade7515fSKim, Milo #define LP8788_BUCK3_VOUT 0x26 34ade7515fSKim, Milo #define LP8788_BUCK4_VOUT 0x27 35ade7515fSKim, Milo #define LP8788_BUCK1_TIMESTEP 0x28 36ade7515fSKim, Milo #define LP8788_BUCK_PWM 0x2D 37ade7515fSKim, Milo 38ade7515fSKim, Milo /* mask/shift bits */ 39ade7515fSKim, Milo #define LP8788_EN_BUCK1_M BIT(0) /* Addr 0Ch */ 40ade7515fSKim, Milo #define LP8788_EN_BUCK2_M BIT(1) 41ade7515fSKim, Milo #define LP8788_EN_BUCK3_M BIT(2) 42ade7515fSKim, Milo #define LP8788_EN_BUCK4_M BIT(3) 43ade7515fSKim, Milo #define LP8788_BUCK1_DVS_SEL_M 0x04 /* Addr 1Dh */ 44ade7515fSKim, Milo #define LP8788_BUCK1_DVS_M 0x03 45ade7515fSKim, Milo #define LP8788_BUCK1_DVS_S 0 46ade7515fSKim, Milo #define LP8788_BUCK2_DVS_SEL_M 0x40 47ade7515fSKim, Milo #define LP8788_BUCK2_DVS_M 0x30 48ade7515fSKim, Milo #define LP8788_BUCK2_DVS_S 4 49ade7515fSKim, Milo #define LP8788_BUCK1_DVS_I2C BIT(2) 50ade7515fSKim, Milo #define LP8788_BUCK2_DVS_I2C BIT(6) 51ade7515fSKim, Milo #define LP8788_BUCK1_DVS_PIN (0 << 2) 52ade7515fSKim, Milo #define LP8788_BUCK2_DVS_PIN (0 << 6) 53ade7515fSKim, Milo #define LP8788_VOUT_M 0x1F /* Addr 1Eh ~ 27h */ 54ade7515fSKim, Milo #define LP8788_STARTUP_TIME_M 0xF8 /* Addr 28h ~ 2Bh */ 55ade7515fSKim, Milo #define LP8788_STARTUP_TIME_S 3 56ade7515fSKim, Milo #define LP8788_FPWM_BUCK1_M BIT(0) /* Addr 2Dh */ 57ade7515fSKim, Milo #define LP8788_FPWM_BUCK1_S 0 58ade7515fSKim, Milo #define LP8788_FPWM_BUCK2_M BIT(1) 59ade7515fSKim, Milo #define LP8788_FPWM_BUCK2_S 1 60ade7515fSKim, Milo #define LP8788_FPWM_BUCK3_M BIT(2) 61ade7515fSKim, Milo #define LP8788_FPWM_BUCK3_S 2 62ade7515fSKim, Milo #define LP8788_FPWM_BUCK4_M BIT(3) 63ade7515fSKim, Milo #define LP8788_FPWM_BUCK4_S 3 64ade7515fSKim, Milo 65ade7515fSKim, Milo #define INVALID_ADDR 0xFF 66ade7515fSKim, Milo #define LP8788_FORCE_PWM 1 67ade7515fSKim, Milo #define LP8788_AUTO_PWM 0 68ade7515fSKim, Milo #define PIN_LOW 0 69ade7515fSKim, Milo #define PIN_HIGH 1 70ade7515fSKim, Milo #define ENABLE_TIME_USEC 32 71ade7515fSKim, Milo 720f4c46d2SAxel Lin #define BUCK_FPWM_MASK(x) (1 << (x)) 730f4c46d2SAxel Lin #define BUCK_FPWM_SHIFT(x) (x) 740f4c46d2SAxel Lin 75ade7515fSKim, Milo enum lp8788_dvs_state { 76ade7515fSKim, Milo DVS_LOW = GPIOF_OUT_INIT_LOW, 77ade7515fSKim, Milo DVS_HIGH = GPIOF_OUT_INIT_HIGH, 78ade7515fSKim, Milo }; 79ade7515fSKim, Milo 80ade7515fSKim, Milo enum lp8788_dvs_mode { 81ade7515fSKim, Milo REGISTER, 82ade7515fSKim, Milo EXTPIN, 83ade7515fSKim, Milo }; 84ade7515fSKim, Milo 85ade7515fSKim, Milo enum lp8788_buck_id { 86ade7515fSKim, Milo BUCK1, 87ade7515fSKim, Milo BUCK2, 88ade7515fSKim, Milo BUCK3, 89ade7515fSKim, Milo BUCK4, 90ade7515fSKim, Milo }; 91ade7515fSKim, Milo 92ade7515fSKim, Milo struct lp8788_buck { 93ade7515fSKim, Milo struct lp8788 *lp; 94ade7515fSKim, Milo struct regulator_dev *regulator; 95ade7515fSKim, Milo void *dvs; 96ade7515fSKim, Milo }; 97ade7515fSKim, Milo 98*7932a880SAxel Lin /* BUCK 1 ~ 4 voltage ranges */ 99*7932a880SAxel Lin static const struct regulator_linear_range buck_volt_ranges[] = { 100*7932a880SAxel Lin REGULATOR_LINEAR_RANGE(500000, 0, 0, 0), 101*7932a880SAxel Lin REGULATOR_LINEAR_RANGE(800000, 1, 25, 50000), 102ade7515fSKim, Milo }; 103ade7515fSKim, Milo 104ade7515fSKim, Milo static void lp8788_buck1_set_dvs(struct lp8788_buck *buck) 105ade7515fSKim, Milo { 106ade7515fSKim, Milo struct lp8788_buck1_dvs *dvs = (struct lp8788_buck1_dvs *)buck->dvs; 107ade7515fSKim, Milo enum lp8788_dvs_state pinstate; 108ade7515fSKim, Milo 109ade7515fSKim, Milo if (!dvs) 110ade7515fSKim, Milo return; 111ade7515fSKim, Milo 112ade7515fSKim, Milo pinstate = dvs->vsel == DVS_SEL_V0 ? DVS_LOW : DVS_HIGH; 113ade7515fSKim, Milo if (gpio_is_valid(dvs->gpio)) 114ade7515fSKim, Milo gpio_set_value(dvs->gpio, pinstate); 115ade7515fSKim, Milo } 116ade7515fSKim, Milo 117ade7515fSKim, Milo static void lp8788_buck2_set_dvs(struct lp8788_buck *buck) 118ade7515fSKim, Milo { 119ade7515fSKim, Milo struct lp8788_buck2_dvs *dvs = (struct lp8788_buck2_dvs *)buck->dvs; 120ade7515fSKim, Milo enum lp8788_dvs_state pin1, pin2; 121ade7515fSKim, Milo 122ade7515fSKim, Milo if (!dvs) 123ade7515fSKim, Milo return; 124ade7515fSKim, Milo 125ade7515fSKim, Milo switch (dvs->vsel) { 126ade7515fSKim, Milo case DVS_SEL_V0: 127ade7515fSKim, Milo pin1 = DVS_LOW; 128ade7515fSKim, Milo pin2 = DVS_LOW; 129ade7515fSKim, Milo break; 130ade7515fSKim, Milo case DVS_SEL_V1: 131ade7515fSKim, Milo pin1 = DVS_HIGH; 132ade7515fSKim, Milo pin2 = DVS_LOW; 133ade7515fSKim, Milo break; 134ade7515fSKim, Milo case DVS_SEL_V2: 135ade7515fSKim, Milo pin1 = DVS_LOW; 136ade7515fSKim, Milo pin2 = DVS_HIGH; 137ade7515fSKim, Milo break; 138ade7515fSKim, Milo case DVS_SEL_V3: 139ade7515fSKim, Milo pin1 = DVS_HIGH; 140ade7515fSKim, Milo pin2 = DVS_HIGH; 141ade7515fSKim, Milo break; 142ade7515fSKim, Milo default: 143ade7515fSKim, Milo return; 144ade7515fSKim, Milo } 145ade7515fSKim, Milo 146ade7515fSKim, Milo if (gpio_is_valid(dvs->gpio[0])) 147ade7515fSKim, Milo gpio_set_value(dvs->gpio[0], pin1); 148ade7515fSKim, Milo 149ade7515fSKim, Milo if (gpio_is_valid(dvs->gpio[1])) 150ade7515fSKim, Milo gpio_set_value(dvs->gpio[1], pin2); 151ade7515fSKim, Milo } 152ade7515fSKim, Milo 153ade7515fSKim, Milo static void lp8788_set_dvs(struct lp8788_buck *buck, enum lp8788_buck_id id) 154ade7515fSKim, Milo { 155ade7515fSKim, Milo switch (id) { 156ade7515fSKim, Milo case BUCK1: 157ade7515fSKim, Milo lp8788_buck1_set_dvs(buck); 158ade7515fSKim, Milo break; 159ade7515fSKim, Milo case BUCK2: 160ade7515fSKim, Milo lp8788_buck2_set_dvs(buck); 161ade7515fSKim, Milo break; 162ade7515fSKim, Milo default: 163ade7515fSKim, Milo break; 164ade7515fSKim, Milo } 165ade7515fSKim, Milo } 166ade7515fSKim, Milo 167ade7515fSKim, Milo static enum lp8788_dvs_mode 168ade7515fSKim, Milo lp8788_get_buck_dvs_ctrl_mode(struct lp8788_buck *buck, enum lp8788_buck_id id) 169ade7515fSKim, Milo { 170ade7515fSKim, Milo u8 val, mask; 171ade7515fSKim, Milo 172ade7515fSKim, Milo switch (id) { 173ade7515fSKim, Milo case BUCK1: 174ade7515fSKim, Milo mask = LP8788_BUCK1_DVS_SEL_M; 175ade7515fSKim, Milo break; 176ade7515fSKim, Milo case BUCK2: 177ade7515fSKim, Milo mask = LP8788_BUCK2_DVS_SEL_M; 178ade7515fSKim, Milo break; 179ade7515fSKim, Milo default: 180ade7515fSKim, Milo return REGISTER; 181ade7515fSKim, Milo } 182ade7515fSKim, Milo 183ade7515fSKim, Milo lp8788_read_byte(buck->lp, LP8788_BUCK_DVS_SEL, &val); 184ade7515fSKim, Milo 185ade7515fSKim, Milo return val & mask ? REGISTER : EXTPIN; 186ade7515fSKim, Milo } 187ade7515fSKim, Milo 188ade7515fSKim, Milo static bool lp8788_is_valid_buck_addr(u8 addr) 189ade7515fSKim, Milo { 190ade7515fSKim, Milo switch (addr) { 191ade7515fSKim, Milo case LP8788_BUCK1_VOUT0: 192ade7515fSKim, Milo case LP8788_BUCK1_VOUT1: 193ade7515fSKim, Milo case LP8788_BUCK1_VOUT2: 194ade7515fSKim, Milo case LP8788_BUCK1_VOUT3: 195ade7515fSKim, Milo case LP8788_BUCK2_VOUT0: 196ade7515fSKim, Milo case LP8788_BUCK2_VOUT1: 197ade7515fSKim, Milo case LP8788_BUCK2_VOUT2: 198ade7515fSKim, Milo case LP8788_BUCK2_VOUT3: 199ade7515fSKim, Milo return true; 200ade7515fSKim, Milo default: 201ade7515fSKim, Milo return false; 202ade7515fSKim, Milo } 203ade7515fSKim, Milo } 204ade7515fSKim, Milo 205ade7515fSKim, Milo static u8 lp8788_select_buck_vout_addr(struct lp8788_buck *buck, 206ade7515fSKim, Milo enum lp8788_buck_id id) 207ade7515fSKim, Milo { 208ade7515fSKim, Milo enum lp8788_dvs_mode mode = lp8788_get_buck_dvs_ctrl_mode(buck, id); 209ade7515fSKim, Milo struct lp8788_buck1_dvs *b1_dvs; 210ade7515fSKim, Milo struct lp8788_buck2_dvs *b2_dvs; 211ade7515fSKim, Milo u8 val, idx, addr; 212ade7515fSKim, Milo int pin1, pin2; 213ade7515fSKim, Milo 214ade7515fSKim, Milo switch (id) { 215ade7515fSKim, Milo case BUCK1: 216ade7515fSKim, Milo if (mode == EXTPIN) { 217ade7515fSKim, Milo b1_dvs = (struct lp8788_buck1_dvs *)buck->dvs; 218ade7515fSKim, Milo if (!b1_dvs) 219ade7515fSKim, Milo goto err; 220ade7515fSKim, Milo 221ade7515fSKim, Milo idx = gpio_get_value(b1_dvs->gpio) ? 1 : 0; 222ade7515fSKim, Milo } else { 223ade7515fSKim, Milo lp8788_read_byte(buck->lp, LP8788_BUCK_DVS_SEL, &val); 224ade7515fSKim, Milo idx = (val & LP8788_BUCK1_DVS_M) >> LP8788_BUCK1_DVS_S; 225ade7515fSKim, Milo } 226e69995d3SAxel Lin addr = LP8788_BUCK1_VOUT0 + idx; 227ade7515fSKim, Milo break; 228ade7515fSKim, Milo case BUCK2: 229ade7515fSKim, Milo if (mode == EXTPIN) { 230ade7515fSKim, Milo b2_dvs = (struct lp8788_buck2_dvs *)buck->dvs; 231ade7515fSKim, Milo if (!b2_dvs) 232ade7515fSKim, Milo goto err; 233ade7515fSKim, Milo 234ade7515fSKim, Milo pin1 = gpio_get_value(b2_dvs->gpio[0]); 235ade7515fSKim, Milo pin2 = gpio_get_value(b2_dvs->gpio[1]); 236ade7515fSKim, Milo 237ade7515fSKim, Milo if (pin1 == PIN_LOW && pin2 == PIN_LOW) 238ade7515fSKim, Milo idx = 0; 239ade7515fSKim, Milo else if (pin1 == PIN_LOW && pin2 == PIN_HIGH) 240ade7515fSKim, Milo idx = 2; 241ade7515fSKim, Milo else if (pin1 == PIN_HIGH && pin2 == PIN_LOW) 242ade7515fSKim, Milo idx = 1; 243ade7515fSKim, Milo else 244ade7515fSKim, Milo idx = 3; 245ade7515fSKim, Milo } else { 246ade7515fSKim, Milo lp8788_read_byte(buck->lp, LP8788_BUCK_DVS_SEL, &val); 247ade7515fSKim, Milo idx = (val & LP8788_BUCK2_DVS_M) >> LP8788_BUCK2_DVS_S; 248ade7515fSKim, Milo } 249e69995d3SAxel Lin addr = LP8788_BUCK2_VOUT0 + idx; 250ade7515fSKim, Milo break; 251ade7515fSKim, Milo default: 252ade7515fSKim, Milo goto err; 253ade7515fSKim, Milo } 254ade7515fSKim, Milo 255ade7515fSKim, Milo return addr; 256ade7515fSKim, Milo err: 257ade7515fSKim, Milo return INVALID_ADDR; 258ade7515fSKim, Milo } 259ade7515fSKim, Milo 260ade7515fSKim, Milo static int lp8788_buck12_set_voltage_sel(struct regulator_dev *rdev, 261ade7515fSKim, Milo unsigned selector) 262ade7515fSKim, Milo { 263ade7515fSKim, Milo struct lp8788_buck *buck = rdev_get_drvdata(rdev); 264ade7515fSKim, Milo enum lp8788_buck_id id = rdev_get_id(rdev); 265ade7515fSKim, Milo u8 addr; 266ade7515fSKim, Milo 267ade7515fSKim, Milo if (buck->dvs) 268ade7515fSKim, Milo lp8788_set_dvs(buck, id); 269ade7515fSKim, Milo 270ade7515fSKim, Milo addr = lp8788_select_buck_vout_addr(buck, id); 271ade7515fSKim, Milo if (!lp8788_is_valid_buck_addr(addr)) 272ade7515fSKim, Milo return -EINVAL; 273ade7515fSKim, Milo 274ade7515fSKim, Milo return lp8788_update_bits(buck->lp, addr, LP8788_VOUT_M, selector); 275ade7515fSKim, Milo } 276ade7515fSKim, Milo 277ade7515fSKim, Milo static int lp8788_buck12_get_voltage_sel(struct regulator_dev *rdev) 278ade7515fSKim, Milo { 279ade7515fSKim, Milo struct lp8788_buck *buck = rdev_get_drvdata(rdev); 280ade7515fSKim, Milo enum lp8788_buck_id id = rdev_get_id(rdev); 281ade7515fSKim, Milo int ret; 282ade7515fSKim, Milo u8 val, addr; 283ade7515fSKim, Milo 284ade7515fSKim, Milo addr = lp8788_select_buck_vout_addr(buck, id); 285ade7515fSKim, Milo if (!lp8788_is_valid_buck_addr(addr)) 286ade7515fSKim, Milo return -EINVAL; 287ade7515fSKim, Milo 288ade7515fSKim, Milo ret = lp8788_read_byte(buck->lp, addr, &val); 289ade7515fSKim, Milo if (ret) 290ade7515fSKim, Milo return ret; 291ade7515fSKim, Milo 292ade7515fSKim, Milo return val & LP8788_VOUT_M; 293ade7515fSKim, Milo } 294ade7515fSKim, Milo 295ade7515fSKim, Milo static int lp8788_buck_enable_time(struct regulator_dev *rdev) 296ade7515fSKim, Milo { 297ade7515fSKim, Milo struct lp8788_buck *buck = rdev_get_drvdata(rdev); 298ade7515fSKim, Milo enum lp8788_buck_id id = rdev_get_id(rdev); 299ade7515fSKim, Milo u8 val, addr = LP8788_BUCK1_TIMESTEP + id; 300ade7515fSKim, Milo 301ade7515fSKim, Milo if (lp8788_read_byte(buck->lp, addr, &val)) 302ade7515fSKim, Milo return -EINVAL; 303ade7515fSKim, Milo 304ade7515fSKim, Milo val = (val & LP8788_STARTUP_TIME_M) >> LP8788_STARTUP_TIME_S; 305ade7515fSKim, Milo 306ade7515fSKim, Milo return ENABLE_TIME_USEC * val; 307ade7515fSKim, Milo } 308ade7515fSKim, Milo 309ade7515fSKim, Milo static int lp8788_buck_set_mode(struct regulator_dev *rdev, unsigned int mode) 310ade7515fSKim, Milo { 311ade7515fSKim, Milo struct lp8788_buck *buck = rdev_get_drvdata(rdev); 3120f4c46d2SAxel Lin enum lp8788_buck_id id = rdev_get_id(rdev); 3130f4c46d2SAxel Lin u8 mask, val; 314ade7515fSKim, Milo 3150f4c46d2SAxel Lin mask = BUCK_FPWM_MASK(id); 316ade7515fSKim, Milo switch (mode) { 317ade7515fSKim, Milo case REGULATOR_MODE_FAST: 3180f4c46d2SAxel Lin val = LP8788_FORCE_PWM << BUCK_FPWM_SHIFT(id); 319ade7515fSKim, Milo break; 320ade7515fSKim, Milo case REGULATOR_MODE_NORMAL: 3210f4c46d2SAxel Lin val = LP8788_AUTO_PWM << BUCK_FPWM_SHIFT(id); 322ade7515fSKim, Milo break; 323ade7515fSKim, Milo default: 324ade7515fSKim, Milo return -EINVAL; 325ade7515fSKim, Milo } 326ade7515fSKim, Milo 3270f4c46d2SAxel Lin return lp8788_update_bits(buck->lp, LP8788_BUCK_PWM, mask, val); 328ade7515fSKim, Milo } 329ade7515fSKim, Milo 330ade7515fSKim, Milo static unsigned int lp8788_buck_get_mode(struct regulator_dev *rdev) 331ade7515fSKim, Milo { 332ade7515fSKim, Milo struct lp8788_buck *buck = rdev_get_drvdata(rdev); 3330f4c46d2SAxel Lin enum lp8788_buck_id id = rdev_get_id(rdev); 334ade7515fSKim, Milo u8 val; 335ade7515fSKim, Milo int ret; 336ade7515fSKim, Milo 337ade7515fSKim, Milo ret = lp8788_read_byte(buck->lp, LP8788_BUCK_PWM, &val); 338ade7515fSKim, Milo if (ret) 339ade7515fSKim, Milo return ret; 340ade7515fSKim, Milo 3410f4c46d2SAxel Lin return val & BUCK_FPWM_MASK(id) ? 3420f4c46d2SAxel Lin REGULATOR_MODE_FAST : REGULATOR_MODE_NORMAL; 343ade7515fSKim, Milo } 344ade7515fSKim, Milo 34595dfead1SJulia Lawall static const struct regulator_ops lp8788_buck12_ops = { 346*7932a880SAxel Lin .list_voltage = regulator_list_voltage_linear_range, 347*7932a880SAxel Lin .map_voltage = regulator_map_voltage_linear_range, 348ade7515fSKim, Milo .set_voltage_sel = lp8788_buck12_set_voltage_sel, 349ade7515fSKim, Milo .get_voltage_sel = lp8788_buck12_get_voltage_sel, 350ade7515fSKim, Milo .enable = regulator_enable_regmap, 351ade7515fSKim, Milo .disable = regulator_disable_regmap, 352ade7515fSKim, Milo .is_enabled = regulator_is_enabled_regmap, 353ade7515fSKim, Milo .enable_time = lp8788_buck_enable_time, 354ade7515fSKim, Milo .set_mode = lp8788_buck_set_mode, 355ade7515fSKim, Milo .get_mode = lp8788_buck_get_mode, 356ade7515fSKim, Milo }; 357ade7515fSKim, Milo 35895dfead1SJulia Lawall static const struct regulator_ops lp8788_buck34_ops = { 359*7932a880SAxel Lin .list_voltage = regulator_list_voltage_linear_range, 360*7932a880SAxel Lin .map_voltage = regulator_map_voltage_linear_range, 361ade7515fSKim, Milo .set_voltage_sel = regulator_set_voltage_sel_regmap, 362ade7515fSKim, Milo .get_voltage_sel = regulator_get_voltage_sel_regmap, 363ade7515fSKim, Milo .enable = regulator_enable_regmap, 364ade7515fSKim, Milo .disable = regulator_disable_regmap, 365ade7515fSKim, Milo .is_enabled = regulator_is_enabled_regmap, 366ade7515fSKim, Milo .enable_time = lp8788_buck_enable_time, 367ade7515fSKim, Milo .set_mode = lp8788_buck_set_mode, 368ade7515fSKim, Milo .get_mode = lp8788_buck_get_mode, 369ade7515fSKim, Milo }; 370ade7515fSKim, Milo 371b133305cSAxel Lin static const struct regulator_desc lp8788_buck_desc[] = { 372ade7515fSKim, Milo { 373ade7515fSKim, Milo .name = "buck1", 374ade7515fSKim, Milo .id = BUCK1, 375ade7515fSKim, Milo .ops = &lp8788_buck12_ops, 376*7932a880SAxel Lin .n_voltages = 26, 377*7932a880SAxel Lin .linear_ranges = buck_volt_ranges, 378*7932a880SAxel Lin .n_linear_ranges = ARRAY_SIZE(buck_volt_ranges), 379ade7515fSKim, Milo .type = REGULATOR_VOLTAGE, 380ade7515fSKim, Milo .owner = THIS_MODULE, 381ade7515fSKim, Milo .enable_reg = LP8788_EN_BUCK, 382ade7515fSKim, Milo .enable_mask = LP8788_EN_BUCK1_M, 383ade7515fSKim, Milo }, 384ade7515fSKim, Milo { 385ade7515fSKim, Milo .name = "buck2", 386ade7515fSKim, Milo .id = BUCK2, 387ade7515fSKim, Milo .ops = &lp8788_buck12_ops, 388*7932a880SAxel Lin .n_voltages = 26, 389*7932a880SAxel Lin .linear_ranges = buck_volt_ranges, 390*7932a880SAxel Lin .n_linear_ranges = ARRAY_SIZE(buck_volt_ranges), 391ade7515fSKim, Milo .type = REGULATOR_VOLTAGE, 392ade7515fSKim, Milo .owner = THIS_MODULE, 393ade7515fSKim, Milo .enable_reg = LP8788_EN_BUCK, 394ade7515fSKim, Milo .enable_mask = LP8788_EN_BUCK2_M, 395ade7515fSKim, Milo }, 396ade7515fSKim, Milo { 397ade7515fSKim, Milo .name = "buck3", 398ade7515fSKim, Milo .id = BUCK3, 399ade7515fSKim, Milo .ops = &lp8788_buck34_ops, 400*7932a880SAxel Lin .n_voltages = 26, 401*7932a880SAxel Lin .linear_ranges = buck_volt_ranges, 402*7932a880SAxel Lin .n_linear_ranges = ARRAY_SIZE(buck_volt_ranges), 403ade7515fSKim, Milo .type = REGULATOR_VOLTAGE, 404ade7515fSKim, Milo .owner = THIS_MODULE, 405ade7515fSKim, Milo .vsel_reg = LP8788_BUCK3_VOUT, 406ade7515fSKim, Milo .vsel_mask = LP8788_VOUT_M, 407ade7515fSKim, Milo .enable_reg = LP8788_EN_BUCK, 408ade7515fSKim, Milo .enable_mask = LP8788_EN_BUCK3_M, 409ade7515fSKim, Milo }, 410ade7515fSKim, Milo { 411ade7515fSKim, Milo .name = "buck4", 412ade7515fSKim, Milo .id = BUCK4, 413ade7515fSKim, Milo .ops = &lp8788_buck34_ops, 414*7932a880SAxel Lin .n_voltages = 26, 415*7932a880SAxel Lin .linear_ranges = buck_volt_ranges, 416*7932a880SAxel Lin .n_linear_ranges = ARRAY_SIZE(buck_volt_ranges), 417ade7515fSKim, Milo .type = REGULATOR_VOLTAGE, 418ade7515fSKim, Milo .owner = THIS_MODULE, 419ade7515fSKim, Milo .vsel_reg = LP8788_BUCK4_VOUT, 420ade7515fSKim, Milo .vsel_mask = LP8788_VOUT_M, 421ade7515fSKim, Milo .enable_reg = LP8788_EN_BUCK, 422ade7515fSKim, Milo .enable_mask = LP8788_EN_BUCK4_M, 423ade7515fSKim, Milo }, 424ade7515fSKim, Milo }; 425ade7515fSKim, Milo 42638d8f67cSKim, Milo static int lp8788_dvs_gpio_request(struct platform_device *pdev, 42738d8f67cSKim, Milo struct lp8788_buck *buck, 428ade7515fSKim, Milo enum lp8788_buck_id id) 429ade7515fSKim, Milo { 430ade7515fSKim, Milo struct lp8788_platform_data *pdata = buck->lp->pdata; 431ade7515fSKim, Milo char *b1_name = "LP8788_B1_DVS"; 432ade7515fSKim, Milo char *b2_name[] = { "LP8788_B2_DVS1", "LP8788_B2_DVS2" }; 433ade7515fSKim, Milo int i, gpio, ret; 434ade7515fSKim, Milo 435ade7515fSKim, Milo switch (id) { 436ade7515fSKim, Milo case BUCK1: 437ade7515fSKim, Milo gpio = pdata->buck1_dvs->gpio; 43838d8f67cSKim, Milo ret = devm_gpio_request_one(&pdev->dev, gpio, DVS_LOW, 439131a5b9dSAxel Lin b1_name); 440ade7515fSKim, Milo if (ret) 441ade7515fSKim, Milo return ret; 442ade7515fSKim, Milo 443ade7515fSKim, Milo buck->dvs = pdata->buck1_dvs; 444ade7515fSKim, Milo break; 445ade7515fSKim, Milo case BUCK2: 446ade7515fSKim, Milo for (i = 0; i < LP8788_NUM_BUCK2_DVS; i++) { 447ade7515fSKim, Milo gpio = pdata->buck2_dvs->gpio[i]; 44838d8f67cSKim, Milo ret = devm_gpio_request_one(&pdev->dev, gpio, 449131a5b9dSAxel Lin DVS_LOW, b2_name[i]); 450ade7515fSKim, Milo if (ret) 451ade7515fSKim, Milo return ret; 452ade7515fSKim, Milo } 453ade7515fSKim, Milo buck->dvs = pdata->buck2_dvs; 454ade7515fSKim, Milo break; 455ade7515fSKim, Milo default: 456ade7515fSKim, Milo break; 457ade7515fSKim, Milo } 458ade7515fSKim, Milo 459ade7515fSKim, Milo return 0; 460ade7515fSKim, Milo } 461ade7515fSKim, Milo 46238d8f67cSKim, Milo static int lp8788_init_dvs(struct platform_device *pdev, 46338d8f67cSKim, Milo struct lp8788_buck *buck, enum lp8788_buck_id id) 464ade7515fSKim, Milo { 465ade7515fSKim, Milo struct lp8788_platform_data *pdata = buck->lp->pdata; 466ade7515fSKim, Milo u8 mask[] = { LP8788_BUCK1_DVS_SEL_M, LP8788_BUCK2_DVS_SEL_M }; 467ade7515fSKim, Milo u8 val[] = { LP8788_BUCK1_DVS_PIN, LP8788_BUCK2_DVS_PIN }; 468c42ea5cdSAxel Lin u8 default_dvs_mode[] = { LP8788_BUCK1_DVS_I2C, LP8788_BUCK2_DVS_I2C }; 469ade7515fSKim, Milo 470ade7515fSKim, Milo /* no dvs for buck3, 4 */ 471eb758de6SAxel Lin if (id > BUCK2) 472ade7515fSKim, Milo return 0; 473ade7515fSKim, Milo 474ade7515fSKim, Milo /* no dvs platform data, then dvs will be selected by I2C registers */ 475ade7515fSKim, Milo if (!pdata) 476ade7515fSKim, Milo goto set_default_dvs_mode; 477ade7515fSKim, Milo 478ade7515fSKim, Milo if ((id == BUCK1 && !pdata->buck1_dvs) || 479ade7515fSKim, Milo (id == BUCK2 && !pdata->buck2_dvs)) 480ade7515fSKim, Milo goto set_default_dvs_mode; 481ade7515fSKim, Milo 48238d8f67cSKim, Milo if (lp8788_dvs_gpio_request(pdev, buck, id)) 483ade7515fSKim, Milo goto set_default_dvs_mode; 484ade7515fSKim, Milo 485ade7515fSKim, Milo return lp8788_update_bits(buck->lp, LP8788_BUCK_DVS_SEL, mask[id], 486ade7515fSKim, Milo val[id]); 487ade7515fSKim, Milo 488ade7515fSKim, Milo set_default_dvs_mode: 489c42ea5cdSAxel Lin return lp8788_update_bits(buck->lp, LP8788_BUCK_DVS_SEL, mask[id], 490c42ea5cdSAxel Lin default_dvs_mode[id]); 491ade7515fSKim, Milo } 492ade7515fSKim, Milo 493a5023574SBill Pemberton static int lp8788_buck_probe(struct platform_device *pdev) 494ade7515fSKim, Milo { 495ade7515fSKim, Milo struct lp8788 *lp = dev_get_drvdata(pdev->dev.parent); 496ade7515fSKim, Milo int id = pdev->id; 497ade7515fSKim, Milo struct lp8788_buck *buck; 498ade7515fSKim, Milo struct regulator_config cfg = { }; 499ade7515fSKim, Milo struct regulator_dev *rdev; 500ade7515fSKim, Milo int ret; 501ade7515fSKim, Milo 502eb758de6SAxel Lin if (id >= LP8788_NUM_BUCKS) 503eb758de6SAxel Lin return -EINVAL; 504eb758de6SAxel Lin 5053b0e8f12SKim, Milo buck = devm_kzalloc(&pdev->dev, sizeof(struct lp8788_buck), GFP_KERNEL); 506ade7515fSKim, Milo if (!buck) 507ade7515fSKim, Milo return -ENOMEM; 508ade7515fSKim, Milo 509ade7515fSKim, Milo buck->lp = lp; 510ade7515fSKim, Milo 51138d8f67cSKim, Milo ret = lp8788_init_dvs(pdev, buck, id); 512ade7515fSKim, Milo if (ret) 513ade7515fSKim, Milo return ret; 514ade7515fSKim, Milo 5153b0e8f12SKim, Milo cfg.dev = pdev->dev.parent; 516ade7515fSKim, Milo cfg.init_data = lp->pdata ? lp->pdata->buck_data[id] : NULL; 517ade7515fSKim, Milo cfg.driver_data = buck; 518ade7515fSKim, Milo cfg.regmap = lp->regmap; 519ade7515fSKim, Milo 5203343fa17SJingoo Han rdev = devm_regulator_register(&pdev->dev, &lp8788_buck_desc[id], &cfg); 521ade7515fSKim, Milo if (IS_ERR(rdev)) { 522ade7515fSKim, Milo ret = PTR_ERR(rdev); 5233b0e8f12SKim, Milo dev_err(&pdev->dev, "BUCK%d regulator register err = %d\n", 524ade7515fSKim, Milo id + 1, ret); 525ade7515fSKim, Milo return ret; 526ade7515fSKim, Milo } 527ade7515fSKim, Milo 528ade7515fSKim, Milo buck->regulator = rdev; 529ade7515fSKim, Milo platform_set_drvdata(pdev, buck); 530ade7515fSKim, Milo 531ade7515fSKim, Milo return 0; 532ade7515fSKim, Milo } 533ade7515fSKim, Milo 534ade7515fSKim, Milo static struct platform_driver lp8788_buck_driver = { 535ade7515fSKim, Milo .probe = lp8788_buck_probe, 536ade7515fSKim, Milo .driver = { 537ade7515fSKim, Milo .name = LP8788_DEV_BUCK, 538ade7515fSKim, Milo }, 539ade7515fSKim, Milo }; 540ade7515fSKim, Milo 541ade7515fSKim, Milo static int __init lp8788_buck_init(void) 542ade7515fSKim, Milo { 543ade7515fSKim, Milo return platform_driver_register(&lp8788_buck_driver); 544ade7515fSKim, Milo } 545ade7515fSKim, Milo subsys_initcall(lp8788_buck_init); 546ade7515fSKim, Milo 547ade7515fSKim, Milo static void __exit lp8788_buck_exit(void) 548ade7515fSKim, Milo { 549ade7515fSKim, Milo platform_driver_unregister(&lp8788_buck_driver); 550ade7515fSKim, Milo } 551ade7515fSKim, Milo module_exit(lp8788_buck_exit); 552ade7515fSKim, Milo 553ade7515fSKim, Milo MODULE_DESCRIPTION("TI LP8788 BUCK Driver"); 554ade7515fSKim, Milo MODULE_AUTHOR("Milo Kim"); 555ade7515fSKim, Milo MODULE_LICENSE("GPL"); 556ade7515fSKim, Milo MODULE_ALIAS("platform:lp8788-buck"); 557