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