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 
347ade7515fSKim, Milo static 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 
360ade7515fSKim, Milo static 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 
373ade7515fSKim, Milo static 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 		.owner = THIS_MODULE,
537ade7515fSKim, Milo 	},
538ade7515fSKim, Milo };
539ade7515fSKim, Milo 
540ade7515fSKim, Milo static int __init lp8788_buck_init(void)
541ade7515fSKim, Milo {
542ade7515fSKim, Milo 	return platform_driver_register(&lp8788_buck_driver);
543ade7515fSKim, Milo }
544ade7515fSKim, Milo subsys_initcall(lp8788_buck_init);
545ade7515fSKim, Milo 
546ade7515fSKim, Milo static void __exit lp8788_buck_exit(void)
547ade7515fSKim, Milo {
548ade7515fSKim, Milo 	platform_driver_unregister(&lp8788_buck_driver);
549ade7515fSKim, Milo }
550ade7515fSKim, Milo module_exit(lp8788_buck_exit);
551ade7515fSKim, Milo 
552ade7515fSKim, Milo MODULE_DESCRIPTION("TI LP8788 BUCK Driver");
553ade7515fSKim, Milo MODULE_AUTHOR("Milo Kim");
554ade7515fSKim, Milo MODULE_LICENSE("GPL");
555ade7515fSKim, Milo MODULE_ALIAS("platform:lp8788-buck");
556