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 98ade7515fSKim, Milo /* BUCK 1 ~ 4 voltage table */ 99ade7515fSKim, Milo static const int lp8788_buck_vtbl[] = { 100ade7515fSKim, Milo 500000, 800000, 850000, 900000, 950000, 1000000, 1050000, 1100000, 101ade7515fSKim, Milo 1150000, 1200000, 1250000, 1300000, 1350000, 1400000, 1450000, 1500000, 102ade7515fSKim, Milo 1550000, 1600000, 1650000, 1700000, 1750000, 1800000, 1850000, 1900000, 103ade7515fSKim, Milo 1950000, 2000000, 104ade7515fSKim, Milo }; 105ade7515fSKim, Milo 106ade7515fSKim, Milo static void lp8788_buck1_set_dvs(struct lp8788_buck *buck) 107ade7515fSKim, Milo { 108ade7515fSKim, Milo struct lp8788_buck1_dvs *dvs = (struct lp8788_buck1_dvs *)buck->dvs; 109ade7515fSKim, Milo enum lp8788_dvs_state pinstate; 110ade7515fSKim, Milo 111ade7515fSKim, Milo if (!dvs) 112ade7515fSKim, Milo return; 113ade7515fSKim, Milo 114ade7515fSKim, Milo pinstate = dvs->vsel == DVS_SEL_V0 ? DVS_LOW : DVS_HIGH; 115ade7515fSKim, Milo if (gpio_is_valid(dvs->gpio)) 116ade7515fSKim, Milo gpio_set_value(dvs->gpio, pinstate); 117ade7515fSKim, Milo } 118ade7515fSKim, Milo 119ade7515fSKim, Milo static void lp8788_buck2_set_dvs(struct lp8788_buck *buck) 120ade7515fSKim, Milo { 121ade7515fSKim, Milo struct lp8788_buck2_dvs *dvs = (struct lp8788_buck2_dvs *)buck->dvs; 122ade7515fSKim, Milo enum lp8788_dvs_state pin1, pin2; 123ade7515fSKim, Milo 124ade7515fSKim, Milo if (!dvs) 125ade7515fSKim, Milo return; 126ade7515fSKim, Milo 127ade7515fSKim, Milo switch (dvs->vsel) { 128ade7515fSKim, Milo case DVS_SEL_V0: 129ade7515fSKim, Milo pin1 = DVS_LOW; 130ade7515fSKim, Milo pin2 = DVS_LOW; 131ade7515fSKim, Milo break; 132ade7515fSKim, Milo case DVS_SEL_V1: 133ade7515fSKim, Milo pin1 = DVS_HIGH; 134ade7515fSKim, Milo pin2 = DVS_LOW; 135ade7515fSKim, Milo break; 136ade7515fSKim, Milo case DVS_SEL_V2: 137ade7515fSKim, Milo pin1 = DVS_LOW; 138ade7515fSKim, Milo pin2 = DVS_HIGH; 139ade7515fSKim, Milo break; 140ade7515fSKim, Milo case DVS_SEL_V3: 141ade7515fSKim, Milo pin1 = DVS_HIGH; 142ade7515fSKim, Milo pin2 = DVS_HIGH; 143ade7515fSKim, Milo break; 144ade7515fSKim, Milo default: 145ade7515fSKim, Milo return; 146ade7515fSKim, Milo } 147ade7515fSKim, Milo 148ade7515fSKim, Milo if (gpio_is_valid(dvs->gpio[0])) 149ade7515fSKim, Milo gpio_set_value(dvs->gpio[0], pin1); 150ade7515fSKim, Milo 151ade7515fSKim, Milo if (gpio_is_valid(dvs->gpio[1])) 152ade7515fSKim, Milo gpio_set_value(dvs->gpio[1], pin2); 153ade7515fSKim, Milo } 154ade7515fSKim, Milo 155ade7515fSKim, Milo static void lp8788_set_dvs(struct lp8788_buck *buck, enum lp8788_buck_id id) 156ade7515fSKim, Milo { 157ade7515fSKim, Milo switch (id) { 158ade7515fSKim, Milo case BUCK1: 159ade7515fSKim, Milo lp8788_buck1_set_dvs(buck); 160ade7515fSKim, Milo break; 161ade7515fSKim, Milo case BUCK2: 162ade7515fSKim, Milo lp8788_buck2_set_dvs(buck); 163ade7515fSKim, Milo break; 164ade7515fSKim, Milo default: 165ade7515fSKim, Milo break; 166ade7515fSKim, Milo } 167ade7515fSKim, Milo } 168ade7515fSKim, Milo 169ade7515fSKim, Milo static enum lp8788_dvs_mode 170ade7515fSKim, Milo lp8788_get_buck_dvs_ctrl_mode(struct lp8788_buck *buck, enum lp8788_buck_id id) 171ade7515fSKim, Milo { 172ade7515fSKim, Milo u8 val, mask; 173ade7515fSKim, Milo 174ade7515fSKim, Milo switch (id) { 175ade7515fSKim, Milo case BUCK1: 176ade7515fSKim, Milo mask = LP8788_BUCK1_DVS_SEL_M; 177ade7515fSKim, Milo break; 178ade7515fSKim, Milo case BUCK2: 179ade7515fSKim, Milo mask = LP8788_BUCK2_DVS_SEL_M; 180ade7515fSKim, Milo break; 181ade7515fSKim, Milo default: 182ade7515fSKim, Milo return REGISTER; 183ade7515fSKim, Milo } 184ade7515fSKim, Milo 185ade7515fSKim, Milo lp8788_read_byte(buck->lp, LP8788_BUCK_DVS_SEL, &val); 186ade7515fSKim, Milo 187ade7515fSKim, Milo return val & mask ? REGISTER : EXTPIN; 188ade7515fSKim, Milo } 189ade7515fSKim, Milo 190ade7515fSKim, Milo static bool lp8788_is_valid_buck_addr(u8 addr) 191ade7515fSKim, Milo { 192ade7515fSKim, Milo switch (addr) { 193ade7515fSKim, Milo case LP8788_BUCK1_VOUT0: 194ade7515fSKim, Milo case LP8788_BUCK1_VOUT1: 195ade7515fSKim, Milo case LP8788_BUCK1_VOUT2: 196ade7515fSKim, Milo case LP8788_BUCK1_VOUT3: 197ade7515fSKim, Milo case LP8788_BUCK2_VOUT0: 198ade7515fSKim, Milo case LP8788_BUCK2_VOUT1: 199ade7515fSKim, Milo case LP8788_BUCK2_VOUT2: 200ade7515fSKim, Milo case LP8788_BUCK2_VOUT3: 201ade7515fSKim, Milo return true; 202ade7515fSKim, Milo default: 203ade7515fSKim, Milo return false; 204ade7515fSKim, Milo } 205ade7515fSKim, Milo } 206ade7515fSKim, Milo 207ade7515fSKim, Milo static u8 lp8788_select_buck_vout_addr(struct lp8788_buck *buck, 208ade7515fSKim, Milo enum lp8788_buck_id id) 209ade7515fSKim, Milo { 210ade7515fSKim, Milo enum lp8788_dvs_mode mode = lp8788_get_buck_dvs_ctrl_mode(buck, id); 211ade7515fSKim, Milo struct lp8788_buck1_dvs *b1_dvs; 212ade7515fSKim, Milo struct lp8788_buck2_dvs *b2_dvs; 213ade7515fSKim, Milo u8 val, idx, addr; 214ade7515fSKim, Milo int pin1, pin2; 215ade7515fSKim, Milo 216ade7515fSKim, Milo switch (id) { 217ade7515fSKim, Milo case BUCK1: 218ade7515fSKim, Milo if (mode == EXTPIN) { 219ade7515fSKim, Milo b1_dvs = (struct lp8788_buck1_dvs *)buck->dvs; 220ade7515fSKim, Milo if (!b1_dvs) 221ade7515fSKim, Milo goto err; 222ade7515fSKim, Milo 223ade7515fSKim, Milo idx = gpio_get_value(b1_dvs->gpio) ? 1 : 0; 224ade7515fSKim, Milo } else { 225ade7515fSKim, Milo lp8788_read_byte(buck->lp, LP8788_BUCK_DVS_SEL, &val); 226ade7515fSKim, Milo idx = (val & LP8788_BUCK1_DVS_M) >> LP8788_BUCK1_DVS_S; 227ade7515fSKim, Milo } 228e69995d3SAxel Lin addr = LP8788_BUCK1_VOUT0 + idx; 229ade7515fSKim, Milo break; 230ade7515fSKim, Milo case BUCK2: 231ade7515fSKim, Milo if (mode == EXTPIN) { 232ade7515fSKim, Milo b2_dvs = (struct lp8788_buck2_dvs *)buck->dvs; 233ade7515fSKim, Milo if (!b2_dvs) 234ade7515fSKim, Milo goto err; 235ade7515fSKim, Milo 236ade7515fSKim, Milo pin1 = gpio_get_value(b2_dvs->gpio[0]); 237ade7515fSKim, Milo pin2 = gpio_get_value(b2_dvs->gpio[1]); 238ade7515fSKim, Milo 239ade7515fSKim, Milo if (pin1 == PIN_LOW && pin2 == PIN_LOW) 240ade7515fSKim, Milo idx = 0; 241ade7515fSKim, Milo else if (pin1 == PIN_LOW && pin2 == PIN_HIGH) 242ade7515fSKim, Milo idx = 2; 243ade7515fSKim, Milo else if (pin1 == PIN_HIGH && pin2 == PIN_LOW) 244ade7515fSKim, Milo idx = 1; 245ade7515fSKim, Milo else 246ade7515fSKim, Milo idx = 3; 247ade7515fSKim, Milo } else { 248ade7515fSKim, Milo lp8788_read_byte(buck->lp, LP8788_BUCK_DVS_SEL, &val); 249ade7515fSKim, Milo idx = (val & LP8788_BUCK2_DVS_M) >> LP8788_BUCK2_DVS_S; 250ade7515fSKim, Milo } 251e69995d3SAxel Lin addr = LP8788_BUCK2_VOUT0 + idx; 252ade7515fSKim, Milo break; 253ade7515fSKim, Milo default: 254ade7515fSKim, Milo goto err; 255ade7515fSKim, Milo } 256ade7515fSKim, Milo 257ade7515fSKim, Milo return addr; 258ade7515fSKim, Milo err: 259ade7515fSKim, Milo return INVALID_ADDR; 260ade7515fSKim, Milo } 261ade7515fSKim, Milo 262ade7515fSKim, Milo static int lp8788_buck12_set_voltage_sel(struct regulator_dev *rdev, 263ade7515fSKim, Milo unsigned selector) 264ade7515fSKim, Milo { 265ade7515fSKim, Milo struct lp8788_buck *buck = rdev_get_drvdata(rdev); 266ade7515fSKim, Milo enum lp8788_buck_id id = rdev_get_id(rdev); 267ade7515fSKim, Milo u8 addr; 268ade7515fSKim, Milo 269ade7515fSKim, Milo if (buck->dvs) 270ade7515fSKim, Milo lp8788_set_dvs(buck, id); 271ade7515fSKim, Milo 272ade7515fSKim, Milo addr = lp8788_select_buck_vout_addr(buck, id); 273ade7515fSKim, Milo if (!lp8788_is_valid_buck_addr(addr)) 274ade7515fSKim, Milo return -EINVAL; 275ade7515fSKim, Milo 276ade7515fSKim, Milo return lp8788_update_bits(buck->lp, addr, LP8788_VOUT_M, selector); 277ade7515fSKim, Milo } 278ade7515fSKim, Milo 279ade7515fSKim, Milo static int lp8788_buck12_get_voltage_sel(struct regulator_dev *rdev) 280ade7515fSKim, Milo { 281ade7515fSKim, Milo struct lp8788_buck *buck = rdev_get_drvdata(rdev); 282ade7515fSKim, Milo enum lp8788_buck_id id = rdev_get_id(rdev); 283ade7515fSKim, Milo int ret; 284ade7515fSKim, Milo u8 val, addr; 285ade7515fSKim, Milo 286ade7515fSKim, Milo addr = lp8788_select_buck_vout_addr(buck, id); 287ade7515fSKim, Milo if (!lp8788_is_valid_buck_addr(addr)) 288ade7515fSKim, Milo return -EINVAL; 289ade7515fSKim, Milo 290ade7515fSKim, Milo ret = lp8788_read_byte(buck->lp, addr, &val); 291ade7515fSKim, Milo if (ret) 292ade7515fSKim, Milo return ret; 293ade7515fSKim, Milo 294ade7515fSKim, Milo return val & LP8788_VOUT_M; 295ade7515fSKim, Milo } 296ade7515fSKim, Milo 297ade7515fSKim, Milo static int lp8788_buck_enable_time(struct regulator_dev *rdev) 298ade7515fSKim, Milo { 299ade7515fSKim, Milo struct lp8788_buck *buck = rdev_get_drvdata(rdev); 300ade7515fSKim, Milo enum lp8788_buck_id id = rdev_get_id(rdev); 301ade7515fSKim, Milo u8 val, addr = LP8788_BUCK1_TIMESTEP + id; 302ade7515fSKim, Milo 303ade7515fSKim, Milo if (lp8788_read_byte(buck->lp, addr, &val)) 304ade7515fSKim, Milo return -EINVAL; 305ade7515fSKim, Milo 306ade7515fSKim, Milo val = (val & LP8788_STARTUP_TIME_M) >> LP8788_STARTUP_TIME_S; 307ade7515fSKim, Milo 308ade7515fSKim, Milo return ENABLE_TIME_USEC * val; 309ade7515fSKim, Milo } 310ade7515fSKim, Milo 311ade7515fSKim, Milo static int lp8788_buck_set_mode(struct regulator_dev *rdev, unsigned int mode) 312ade7515fSKim, Milo { 313ade7515fSKim, Milo struct lp8788_buck *buck = rdev_get_drvdata(rdev); 3140f4c46d2SAxel Lin enum lp8788_buck_id id = rdev_get_id(rdev); 3150f4c46d2SAxel Lin u8 mask, val; 316ade7515fSKim, Milo 3170f4c46d2SAxel Lin mask = BUCK_FPWM_MASK(id); 318ade7515fSKim, Milo switch (mode) { 319ade7515fSKim, Milo case REGULATOR_MODE_FAST: 3200f4c46d2SAxel Lin val = LP8788_FORCE_PWM << BUCK_FPWM_SHIFT(id); 321ade7515fSKim, Milo break; 322ade7515fSKim, Milo case REGULATOR_MODE_NORMAL: 3230f4c46d2SAxel Lin val = LP8788_AUTO_PWM << BUCK_FPWM_SHIFT(id); 324ade7515fSKim, Milo break; 325ade7515fSKim, Milo default: 326ade7515fSKim, Milo return -EINVAL; 327ade7515fSKim, Milo } 328ade7515fSKim, Milo 3290f4c46d2SAxel Lin return lp8788_update_bits(buck->lp, LP8788_BUCK_PWM, mask, val); 330ade7515fSKim, Milo } 331ade7515fSKim, Milo 332ade7515fSKim, Milo static unsigned int lp8788_buck_get_mode(struct regulator_dev *rdev) 333ade7515fSKim, Milo { 334ade7515fSKim, Milo struct lp8788_buck *buck = rdev_get_drvdata(rdev); 3350f4c46d2SAxel Lin enum lp8788_buck_id id = rdev_get_id(rdev); 336ade7515fSKim, Milo u8 val; 337ade7515fSKim, Milo int ret; 338ade7515fSKim, Milo 339ade7515fSKim, Milo ret = lp8788_read_byte(buck->lp, LP8788_BUCK_PWM, &val); 340ade7515fSKim, Milo if (ret) 341ade7515fSKim, Milo return ret; 342ade7515fSKim, Milo 3430f4c46d2SAxel Lin return val & BUCK_FPWM_MASK(id) ? 3440f4c46d2SAxel Lin REGULATOR_MODE_FAST : REGULATOR_MODE_NORMAL; 345ade7515fSKim, Milo } 346ade7515fSKim, Milo 34795dfead1SJulia Lawall static const struct regulator_ops lp8788_buck12_ops = { 348ade7515fSKim, Milo .list_voltage = regulator_list_voltage_table, 3496dcbc200SAxel Lin .map_voltage = regulator_map_voltage_ascend, 350ade7515fSKim, Milo .set_voltage_sel = lp8788_buck12_set_voltage_sel, 351ade7515fSKim, Milo .get_voltage_sel = lp8788_buck12_get_voltage_sel, 352ade7515fSKim, Milo .enable = regulator_enable_regmap, 353ade7515fSKim, Milo .disable = regulator_disable_regmap, 354ade7515fSKim, Milo .is_enabled = regulator_is_enabled_regmap, 355ade7515fSKim, Milo .enable_time = lp8788_buck_enable_time, 356ade7515fSKim, Milo .set_mode = lp8788_buck_set_mode, 357ade7515fSKim, Milo .get_mode = lp8788_buck_get_mode, 358ade7515fSKim, Milo }; 359ade7515fSKim, Milo 36095dfead1SJulia Lawall static const struct regulator_ops lp8788_buck34_ops = { 361ade7515fSKim, Milo .list_voltage = regulator_list_voltage_table, 3626dcbc200SAxel Lin .map_voltage = regulator_map_voltage_ascend, 363ade7515fSKim, Milo .set_voltage_sel = regulator_set_voltage_sel_regmap, 364ade7515fSKim, Milo .get_voltage_sel = regulator_get_voltage_sel_regmap, 365ade7515fSKim, Milo .enable = regulator_enable_regmap, 366ade7515fSKim, Milo .disable = regulator_disable_regmap, 367ade7515fSKim, Milo .is_enabled = regulator_is_enabled_regmap, 368ade7515fSKim, Milo .enable_time = lp8788_buck_enable_time, 369ade7515fSKim, Milo .set_mode = lp8788_buck_set_mode, 370ade7515fSKim, Milo .get_mode = lp8788_buck_get_mode, 371ade7515fSKim, Milo }; 372ade7515fSKim, Milo 373*b133305cSAxel Lin static const struct regulator_desc lp8788_buck_desc[] = { 374ade7515fSKim, Milo { 375ade7515fSKim, Milo .name = "buck1", 376ade7515fSKim, Milo .id = BUCK1, 377ade7515fSKim, Milo .ops = &lp8788_buck12_ops, 378ade7515fSKim, Milo .n_voltages = ARRAY_SIZE(lp8788_buck_vtbl), 379ade7515fSKim, Milo .volt_table = lp8788_buck_vtbl, 380ade7515fSKim, Milo .type = REGULATOR_VOLTAGE, 381ade7515fSKim, Milo .owner = THIS_MODULE, 382ade7515fSKim, Milo .enable_reg = LP8788_EN_BUCK, 383ade7515fSKim, Milo .enable_mask = LP8788_EN_BUCK1_M, 384ade7515fSKim, Milo }, 385ade7515fSKim, Milo { 386ade7515fSKim, Milo .name = "buck2", 387ade7515fSKim, Milo .id = BUCK2, 388ade7515fSKim, Milo .ops = &lp8788_buck12_ops, 389ade7515fSKim, Milo .n_voltages = ARRAY_SIZE(lp8788_buck_vtbl), 390ade7515fSKim, Milo .volt_table = lp8788_buck_vtbl, 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, 400ade7515fSKim, Milo .n_voltages = ARRAY_SIZE(lp8788_buck_vtbl), 401ade7515fSKim, Milo .volt_table = lp8788_buck_vtbl, 402ade7515fSKim, Milo .type = REGULATOR_VOLTAGE, 403ade7515fSKim, Milo .owner = THIS_MODULE, 404ade7515fSKim, Milo .vsel_reg = LP8788_BUCK3_VOUT, 405ade7515fSKim, Milo .vsel_mask = LP8788_VOUT_M, 406ade7515fSKim, Milo .enable_reg = LP8788_EN_BUCK, 407ade7515fSKim, Milo .enable_mask = LP8788_EN_BUCK3_M, 408ade7515fSKim, Milo }, 409ade7515fSKim, Milo { 410ade7515fSKim, Milo .name = "buck4", 411ade7515fSKim, Milo .id = BUCK4, 412ade7515fSKim, Milo .ops = &lp8788_buck34_ops, 413ade7515fSKim, Milo .n_voltages = ARRAY_SIZE(lp8788_buck_vtbl), 414ade7515fSKim, Milo .volt_table = lp8788_buck_vtbl, 415ade7515fSKim, Milo .type = REGULATOR_VOLTAGE, 416ade7515fSKim, Milo .owner = THIS_MODULE, 417ade7515fSKim, Milo .vsel_reg = LP8788_BUCK4_VOUT, 418ade7515fSKim, Milo .vsel_mask = LP8788_VOUT_M, 419ade7515fSKim, Milo .enable_reg = LP8788_EN_BUCK, 420ade7515fSKim, Milo .enable_mask = LP8788_EN_BUCK4_M, 421ade7515fSKim, Milo }, 422ade7515fSKim, Milo }; 423ade7515fSKim, Milo 42438d8f67cSKim, Milo static int lp8788_dvs_gpio_request(struct platform_device *pdev, 42538d8f67cSKim, Milo struct lp8788_buck *buck, 426ade7515fSKim, Milo enum lp8788_buck_id id) 427ade7515fSKim, Milo { 428ade7515fSKim, Milo struct lp8788_platform_data *pdata = buck->lp->pdata; 429ade7515fSKim, Milo char *b1_name = "LP8788_B1_DVS"; 430ade7515fSKim, Milo char *b2_name[] = { "LP8788_B2_DVS1", "LP8788_B2_DVS2" }; 431ade7515fSKim, Milo int i, gpio, ret; 432ade7515fSKim, Milo 433ade7515fSKim, Milo switch (id) { 434ade7515fSKim, Milo case BUCK1: 435ade7515fSKim, Milo gpio = pdata->buck1_dvs->gpio; 43638d8f67cSKim, Milo ret = devm_gpio_request_one(&pdev->dev, gpio, DVS_LOW, 437131a5b9dSAxel Lin b1_name); 438ade7515fSKim, Milo if (ret) 439ade7515fSKim, Milo return ret; 440ade7515fSKim, Milo 441ade7515fSKim, Milo buck->dvs = pdata->buck1_dvs; 442ade7515fSKim, Milo break; 443ade7515fSKim, Milo case BUCK2: 444ade7515fSKim, Milo for (i = 0; i < LP8788_NUM_BUCK2_DVS; i++) { 445ade7515fSKim, Milo gpio = pdata->buck2_dvs->gpio[i]; 44638d8f67cSKim, Milo ret = devm_gpio_request_one(&pdev->dev, gpio, 447131a5b9dSAxel Lin DVS_LOW, b2_name[i]); 448ade7515fSKim, Milo if (ret) 449ade7515fSKim, Milo return ret; 450ade7515fSKim, Milo } 451ade7515fSKim, Milo buck->dvs = pdata->buck2_dvs; 452ade7515fSKim, Milo break; 453ade7515fSKim, Milo default: 454ade7515fSKim, Milo break; 455ade7515fSKim, Milo } 456ade7515fSKim, Milo 457ade7515fSKim, Milo return 0; 458ade7515fSKim, Milo } 459ade7515fSKim, Milo 46038d8f67cSKim, Milo static int lp8788_init_dvs(struct platform_device *pdev, 46138d8f67cSKim, Milo struct lp8788_buck *buck, enum lp8788_buck_id id) 462ade7515fSKim, Milo { 463ade7515fSKim, Milo struct lp8788_platform_data *pdata = buck->lp->pdata; 464ade7515fSKim, Milo u8 mask[] = { LP8788_BUCK1_DVS_SEL_M, LP8788_BUCK2_DVS_SEL_M }; 465ade7515fSKim, Milo u8 val[] = { LP8788_BUCK1_DVS_PIN, LP8788_BUCK2_DVS_PIN }; 466c42ea5cdSAxel Lin u8 default_dvs_mode[] = { LP8788_BUCK1_DVS_I2C, LP8788_BUCK2_DVS_I2C }; 467ade7515fSKim, Milo 468ade7515fSKim, Milo /* no dvs for buck3, 4 */ 469eb758de6SAxel Lin if (id > BUCK2) 470ade7515fSKim, Milo return 0; 471ade7515fSKim, Milo 472ade7515fSKim, Milo /* no dvs platform data, then dvs will be selected by I2C registers */ 473ade7515fSKim, Milo if (!pdata) 474ade7515fSKim, Milo goto set_default_dvs_mode; 475ade7515fSKim, Milo 476ade7515fSKim, Milo if ((id == BUCK1 && !pdata->buck1_dvs) || 477ade7515fSKim, Milo (id == BUCK2 && !pdata->buck2_dvs)) 478ade7515fSKim, Milo goto set_default_dvs_mode; 479ade7515fSKim, Milo 48038d8f67cSKim, Milo if (lp8788_dvs_gpio_request(pdev, buck, id)) 481ade7515fSKim, Milo goto set_default_dvs_mode; 482ade7515fSKim, Milo 483ade7515fSKim, Milo return lp8788_update_bits(buck->lp, LP8788_BUCK_DVS_SEL, mask[id], 484ade7515fSKim, Milo val[id]); 485ade7515fSKim, Milo 486ade7515fSKim, Milo set_default_dvs_mode: 487c42ea5cdSAxel Lin return lp8788_update_bits(buck->lp, LP8788_BUCK_DVS_SEL, mask[id], 488c42ea5cdSAxel Lin default_dvs_mode[id]); 489ade7515fSKim, Milo } 490ade7515fSKim, Milo 491a5023574SBill Pemberton static int lp8788_buck_probe(struct platform_device *pdev) 492ade7515fSKim, Milo { 493ade7515fSKim, Milo struct lp8788 *lp = dev_get_drvdata(pdev->dev.parent); 494ade7515fSKim, Milo int id = pdev->id; 495ade7515fSKim, Milo struct lp8788_buck *buck; 496ade7515fSKim, Milo struct regulator_config cfg = { }; 497ade7515fSKim, Milo struct regulator_dev *rdev; 498ade7515fSKim, Milo int ret; 499ade7515fSKim, Milo 500eb758de6SAxel Lin if (id >= LP8788_NUM_BUCKS) 501eb758de6SAxel Lin return -EINVAL; 502eb758de6SAxel Lin 5033b0e8f12SKim, Milo buck = devm_kzalloc(&pdev->dev, sizeof(struct lp8788_buck), GFP_KERNEL); 504ade7515fSKim, Milo if (!buck) 505ade7515fSKim, Milo return -ENOMEM; 506ade7515fSKim, Milo 507ade7515fSKim, Milo buck->lp = lp; 508ade7515fSKim, Milo 50938d8f67cSKim, Milo ret = lp8788_init_dvs(pdev, buck, id); 510ade7515fSKim, Milo if (ret) 511ade7515fSKim, Milo return ret; 512ade7515fSKim, Milo 5133b0e8f12SKim, Milo cfg.dev = pdev->dev.parent; 514ade7515fSKim, Milo cfg.init_data = lp->pdata ? lp->pdata->buck_data[id] : NULL; 515ade7515fSKim, Milo cfg.driver_data = buck; 516ade7515fSKim, Milo cfg.regmap = lp->regmap; 517ade7515fSKim, Milo 5183343fa17SJingoo Han rdev = devm_regulator_register(&pdev->dev, &lp8788_buck_desc[id], &cfg); 519ade7515fSKim, Milo if (IS_ERR(rdev)) { 520ade7515fSKim, Milo ret = PTR_ERR(rdev); 5213b0e8f12SKim, Milo dev_err(&pdev->dev, "BUCK%d regulator register err = %d\n", 522ade7515fSKim, Milo id + 1, ret); 523ade7515fSKim, Milo return ret; 524ade7515fSKim, Milo } 525ade7515fSKim, Milo 526ade7515fSKim, Milo buck->regulator = rdev; 527ade7515fSKim, Milo platform_set_drvdata(pdev, buck); 528ade7515fSKim, Milo 529ade7515fSKim, Milo return 0; 530ade7515fSKim, Milo } 531ade7515fSKim, Milo 532ade7515fSKim, Milo static struct platform_driver lp8788_buck_driver = { 533ade7515fSKim, Milo .probe = lp8788_buck_probe, 534ade7515fSKim, Milo .driver = { 535ade7515fSKim, Milo .name = LP8788_DEV_BUCK, 536ade7515fSKim, Milo }, 537ade7515fSKim, Milo }; 538ade7515fSKim, Milo 539ade7515fSKim, Milo static int __init lp8788_buck_init(void) 540ade7515fSKim, Milo { 541ade7515fSKim, Milo return platform_driver_register(&lp8788_buck_driver); 542ade7515fSKim, Milo } 543ade7515fSKim, Milo subsys_initcall(lp8788_buck_init); 544ade7515fSKim, Milo 545ade7515fSKim, Milo static void __exit lp8788_buck_exit(void) 546ade7515fSKim, Milo { 547ade7515fSKim, Milo platform_driver_unregister(&lp8788_buck_driver); 548ade7515fSKim, Milo } 549ade7515fSKim, Milo module_exit(lp8788_buck_exit); 550ade7515fSKim, Milo 551ade7515fSKim, Milo MODULE_DESCRIPTION("TI LP8788 BUCK Driver"); 552ade7515fSKim, Milo MODULE_AUTHOR("Milo Kim"); 553ade7515fSKim, Milo MODULE_LICENSE("GPL"); 554ade7515fSKim, Milo MODULE_ALIAS("platform:lp8788-buck"); 555