xref: /linux/drivers/regulator/fan53555.c (revision 30b38b805b36c03db3703ef62397111c783b5f3b)
16a1beee2SAxel Lin // SPDX-License-Identifier: GPL-2.0
26a1beee2SAxel Lin //
36a1beee2SAxel Lin // FAN53555 Fairchild Digitally Programmable TinyBuck Regulator Driver.
46a1beee2SAxel Lin //
56a1beee2SAxel Lin // Supported Part Numbers:
66a1beee2SAxel Lin // FAN53555UC00X/01X/03X/04X/05X
76a1beee2SAxel Lin //
86a1beee2SAxel Lin // Copyright (c) 2012 Marvell Technology Ltd.
96a1beee2SAxel Lin // Yunfan Zhang <yfzhang@marvell.com>
106a1beee2SAxel Lin 
1149d8c599SYunfan Zhang #include <linux/module.h>
1249d8c599SYunfan Zhang #include <linux/param.h>
1349d8c599SYunfan Zhang #include <linux/err.h>
1449d8c599SYunfan Zhang #include <linux/platform_device.h>
1549d8c599SYunfan Zhang #include <linux/regulator/driver.h>
1649d8c599SYunfan Zhang #include <linux/regulator/machine.h>
1791f23d8fSHeiko Stuebner #include <linux/regulator/of_regulator.h>
1891f23d8fSHeiko Stuebner #include <linux/of_device.h>
1949d8c599SYunfan Zhang #include <linux/i2c.h>
2049d8c599SYunfan Zhang #include <linux/slab.h>
2149d8c599SYunfan Zhang #include <linux/regmap.h>
2249d8c599SYunfan Zhang #include <linux/regulator/fan53555.h>
2349d8c599SYunfan Zhang 
2449d8c599SYunfan Zhang /* Voltage setting */
2549d8c599SYunfan Zhang #define FAN53555_VSEL0		0x00
2649d8c599SYunfan Zhang #define FAN53555_VSEL1		0x01
27914df8faSJoseph Chen 
28914df8faSJoseph Chen #define TCS4525_VSEL0		0x11
29914df8faSJoseph Chen #define TCS4525_VSEL1		0x10
30914df8faSJoseph Chen #define TCS4525_TIME		0x13
31914df8faSJoseph Chen #define TCS4525_COMMAND		0x14
32914df8faSJoseph Chen 
3349d8c599SYunfan Zhang /* Control register */
3449d8c599SYunfan Zhang #define FAN53555_CONTROL	0x02
3549d8c599SYunfan Zhang /* IC Type */
3649d8c599SYunfan Zhang #define FAN53555_ID1		0x03
3749d8c599SYunfan Zhang /* IC mask version */
3849d8c599SYunfan Zhang #define FAN53555_ID2		0x04
3949d8c599SYunfan Zhang /* Monitor register */
4049d8c599SYunfan Zhang #define FAN53555_MONITOR	0x05
4149d8c599SYunfan Zhang 
4249d8c599SYunfan Zhang /* VSEL bit definitions */
4349d8c599SYunfan Zhang #define VSEL_BUCK_EN	(1 << 7)
4449d8c599SYunfan Zhang #define VSEL_MODE		(1 << 6)
4549d8c599SYunfan Zhang /* Chip ID and Verison */
4649d8c599SYunfan Zhang #define DIE_ID		0x0F	/* ID1 */
4749d8c599SYunfan Zhang #define DIE_REV		0x0F	/* ID2 */
4849d8c599SYunfan Zhang /* Control bit definitions */
4949d8c599SYunfan Zhang #define CTL_OUTPUT_DISCHG	(1 << 7)
5049d8c599SYunfan Zhang #define CTL_SLEW_MASK		(0x7 << 4)
5149d8c599SYunfan Zhang #define CTL_SLEW_SHIFT		4
5249d8c599SYunfan Zhang #define CTL_RESET			(1 << 2)
53f2a9eb97SBjorn Andersson #define CTL_MODE_VSEL0_MODE	BIT(0)
54f2a9eb97SBjorn Andersson #define CTL_MODE_VSEL1_MODE	BIT(1)
5549d8c599SYunfan Zhang 
5649d8c599SYunfan Zhang #define FAN53555_NVOLTAGES	64	/* Numbers of voltages */
57f2a9eb97SBjorn Andersson #define FAN53526_NVOLTAGES	128
58914df8faSJoseph Chen 
59914df8faSJoseph Chen #define TCS_VSEL0_MODE		(1 << 7)
60914df8faSJoseph Chen #define TCS_VSEL1_MODE		(1 << 6)
61914df8faSJoseph Chen 
62914df8faSJoseph Chen #define TCS_SLEW_SHIFT		3
63914df8faSJoseph Chen #define TCS_SLEW_MASK		(0x3 < 3)
6449d8c599SYunfan Zhang 
65ee30928aSHeiko Stuebner enum fan53555_vendor {
66f2a9eb97SBjorn Andersson 	FAN53526_VENDOR_FAIRCHILD = 0,
67f2a9eb97SBjorn Andersson 	FAN53555_VENDOR_FAIRCHILD,
68ee30928aSHeiko Stuebner 	FAN53555_VENDOR_SILERGY,
69b3cc8ec0SPeter Geis 	FAN53526_VENDOR_TCS,
70ee30928aSHeiko Stuebner };
71ee30928aSHeiko Stuebner 
72f2a9eb97SBjorn Andersson enum {
73f2a9eb97SBjorn Andersson 	FAN53526_CHIP_ID_01 = 1,
74f2a9eb97SBjorn Andersson };
75f2a9eb97SBjorn Andersson 
76f2a9eb97SBjorn Andersson enum {
77f2a9eb97SBjorn Andersson 	FAN53526_CHIP_REV_08 = 8,
78f2a9eb97SBjorn Andersson };
79f2a9eb97SBjorn Andersson 
8049d8c599SYunfan Zhang /* IC Type */
8149d8c599SYunfan Zhang enum {
8249d8c599SYunfan Zhang 	FAN53555_CHIP_ID_00 = 0,
8349d8c599SYunfan Zhang 	FAN53555_CHIP_ID_01,
8449d8c599SYunfan Zhang 	FAN53555_CHIP_ID_02,
8549d8c599SYunfan Zhang 	FAN53555_CHIP_ID_03,
8649d8c599SYunfan Zhang 	FAN53555_CHIP_ID_04,
8749d8c599SYunfan Zhang 	FAN53555_CHIP_ID_05,
885e39cf49SWadim Egorov 	FAN53555_CHIP_ID_08 = 8,
8949d8c599SYunfan Zhang };
9049d8c599SYunfan Zhang 
91f9028dcdSPeter Geis enum {
92f9028dcdSPeter Geis 	TCS4525_CHIP_ID_12 = 12,
93f9028dcdSPeter Geis };
94f9028dcdSPeter Geis 
95e57cbb70SWadim Egorov /* IC mask revision */
96e57cbb70SWadim Egorov enum {
97e57cbb70SWadim Egorov 	FAN53555_CHIP_REV_00 = 0x3,
98e57cbb70SWadim Egorov 	FAN53555_CHIP_REV_13 = 0xf,
99e57cbb70SWadim Egorov };
100e57cbb70SWadim Egorov 
101ee30928aSHeiko Stuebner enum {
102ee30928aSHeiko Stuebner 	SILERGY_SYR82X = 8,
1035365e3dfSVasily Khoruzhick 	SILERGY_SYR83X = 9,
104ee30928aSHeiko Stuebner };
105ee30928aSHeiko Stuebner 
10649d8c599SYunfan Zhang struct fan53555_device_info {
107ee30928aSHeiko Stuebner 	enum fan53555_vendor vendor;
10849d8c599SYunfan Zhang 	struct device *dev;
10949d8c599SYunfan Zhang 	struct regulator_desc desc;
11049d8c599SYunfan Zhang 	struct regulator_init_data *regulator;
11149d8c599SYunfan Zhang 	/* IC Type and Rev */
11249d8c599SYunfan Zhang 	int chip_id;
11349d8c599SYunfan Zhang 	int chip_rev;
11449d8c599SYunfan Zhang 	/* Voltage setting register */
11549d8c599SYunfan Zhang 	unsigned int vol_reg;
11649d8c599SYunfan Zhang 	unsigned int sleep_reg;
11749d8c599SYunfan Zhang 	/* Voltage range and step(linear) */
11849d8c599SYunfan Zhang 	unsigned int vsel_min;
11949d8c599SYunfan Zhang 	unsigned int vsel_step;
120f2a9eb97SBjorn Andersson 	unsigned int vsel_count;
121f2a9eb97SBjorn Andersson 	/* Mode */
122f2a9eb97SBjorn Andersson 	unsigned int mode_reg;
123f2a9eb97SBjorn Andersson 	unsigned int mode_mask;
12449d8c599SYunfan Zhang 	/* Sleep voltage cache */
12549d8c599SYunfan Zhang 	unsigned int sleep_vol_cache;
126914df8faSJoseph Chen 	/* Slew rate */
127914df8faSJoseph Chen 	unsigned int slew_reg;
128914df8faSJoseph Chen 	unsigned int slew_mask;
129914df8faSJoseph Chen 	unsigned int slew_shift;
130914df8faSJoseph Chen 	unsigned int slew_rate;
13149d8c599SYunfan Zhang };
13249d8c599SYunfan Zhang 
13349d8c599SYunfan Zhang static int fan53555_set_suspend_voltage(struct regulator_dev *rdev, int uV)
13449d8c599SYunfan Zhang {
13549d8c599SYunfan Zhang 	struct fan53555_device_info *di = rdev_get_drvdata(rdev);
13649d8c599SYunfan Zhang 	int ret;
13749d8c599SYunfan Zhang 
13849d8c599SYunfan Zhang 	if (di->sleep_vol_cache == uV)
13949d8c599SYunfan Zhang 		return 0;
14049d8c599SYunfan Zhang 	ret = regulator_map_voltage_linear(rdev, uV, uV);
14149d8c599SYunfan Zhang 	if (ret < 0)
142145fe1e1SSachin Kamat 		return ret;
143a69929c7SAxel Lin 	ret = regmap_update_bits(rdev->regmap, di->sleep_reg,
144f2a9eb97SBjorn Andersson 				 di->desc.vsel_mask, ret);
14549d8c599SYunfan Zhang 	if (ret < 0)
146145fe1e1SSachin Kamat 		return ret;
14749d8c599SYunfan Zhang 	/* Cache the sleep voltage setting.
14849d8c599SYunfan Zhang 	 * Might not be the real voltage which is rounded */
14949d8c599SYunfan Zhang 	di->sleep_vol_cache = uV;
15049d8c599SYunfan Zhang 
15149d8c599SYunfan Zhang 	return 0;
15249d8c599SYunfan Zhang }
15349d8c599SYunfan Zhang 
154ab7cad33Szhangqing static int fan53555_set_suspend_enable(struct regulator_dev *rdev)
155ab7cad33Szhangqing {
156ab7cad33Szhangqing 	struct fan53555_device_info *di = rdev_get_drvdata(rdev);
157ab7cad33Szhangqing 
158a69929c7SAxel Lin 	return regmap_update_bits(rdev->regmap, di->sleep_reg,
159ab7cad33Szhangqing 				  VSEL_BUCK_EN, VSEL_BUCK_EN);
160ab7cad33Szhangqing }
161ab7cad33Szhangqing 
162ab7cad33Szhangqing static int fan53555_set_suspend_disable(struct regulator_dev *rdev)
163ab7cad33Szhangqing {
164ab7cad33Szhangqing 	struct fan53555_device_info *di = rdev_get_drvdata(rdev);
165ab7cad33Szhangqing 
166a69929c7SAxel Lin 	return regmap_update_bits(rdev->regmap, di->sleep_reg,
167ab7cad33Szhangqing 				  VSEL_BUCK_EN, 0);
168ab7cad33Szhangqing }
169ab7cad33Szhangqing 
17049d8c599SYunfan Zhang static int fan53555_set_mode(struct regulator_dev *rdev, unsigned int mode)
17149d8c599SYunfan Zhang {
17249d8c599SYunfan Zhang 	struct fan53555_device_info *di = rdev_get_drvdata(rdev);
17349d8c599SYunfan Zhang 
17449d8c599SYunfan Zhang 	switch (mode) {
17549d8c599SYunfan Zhang 	case REGULATOR_MODE_FAST:
176a69929c7SAxel Lin 		regmap_update_bits(rdev->regmap, di->mode_reg,
177f2a9eb97SBjorn Andersson 				   di->mode_mask, di->mode_mask);
17849d8c599SYunfan Zhang 		break;
17949d8c599SYunfan Zhang 	case REGULATOR_MODE_NORMAL:
180a69929c7SAxel Lin 		regmap_update_bits(rdev->regmap, di->vol_reg, di->mode_mask, 0);
18149d8c599SYunfan Zhang 		break;
18249d8c599SYunfan Zhang 	default:
18349d8c599SYunfan Zhang 		return -EINVAL;
18449d8c599SYunfan Zhang 	}
18549d8c599SYunfan Zhang 	return 0;
18649d8c599SYunfan Zhang }
18749d8c599SYunfan Zhang 
18849d8c599SYunfan Zhang static unsigned int fan53555_get_mode(struct regulator_dev *rdev)
18949d8c599SYunfan Zhang {
19049d8c599SYunfan Zhang 	struct fan53555_device_info *di = rdev_get_drvdata(rdev);
19149d8c599SYunfan Zhang 	unsigned int val;
19249d8c599SYunfan Zhang 	int ret = 0;
19349d8c599SYunfan Zhang 
194a69929c7SAxel Lin 	ret = regmap_read(rdev->regmap, di->mode_reg, &val);
19549d8c599SYunfan Zhang 	if (ret < 0)
19649d8c599SYunfan Zhang 		return ret;
197f2a9eb97SBjorn Andersson 	if (val & di->mode_mask)
19849d8c599SYunfan Zhang 		return REGULATOR_MODE_FAST;
19949d8c599SYunfan Zhang 	else
20049d8c599SYunfan Zhang 		return REGULATOR_MODE_NORMAL;
20149d8c599SYunfan Zhang }
20249d8c599SYunfan Zhang 
203121b567dSKrzysztof Kozlowski static const int slew_rates[] = {
204dd7e71fbSHeiko Stuebner 	64000,
205dd7e71fbSHeiko Stuebner 	32000,
206dd7e71fbSHeiko Stuebner 	16000,
207dd7e71fbSHeiko Stuebner 	 8000,
208dd7e71fbSHeiko Stuebner 	 4000,
209dd7e71fbSHeiko Stuebner 	 2000,
210dd7e71fbSHeiko Stuebner 	 1000,
211dd7e71fbSHeiko Stuebner 	  500,
212dd7e71fbSHeiko Stuebner };
213dd7e71fbSHeiko Stuebner 
214914df8faSJoseph Chen static const int tcs_slew_rates[] = {
215914df8faSJoseph Chen 	18700,
216914df8faSJoseph Chen 	 9300,
217914df8faSJoseph Chen 	 4600,
218914df8faSJoseph Chen 	 2300,
219914df8faSJoseph Chen };
220914df8faSJoseph Chen 
221dd7e71fbSHeiko Stuebner static int fan53555_set_ramp(struct regulator_dev *rdev, int ramp)
222dd7e71fbSHeiko Stuebner {
223dd7e71fbSHeiko Stuebner 	struct fan53555_device_info *di = rdev_get_drvdata(rdev);
224dd7e71fbSHeiko Stuebner 	int regval = -1, i;
225914df8faSJoseph Chen 	const int *slew_rate_t;
226914df8faSJoseph Chen 	int slew_rate_n;
227dd7e71fbSHeiko Stuebner 
228914df8faSJoseph Chen 	switch (di->vendor) {
229914df8faSJoseph Chen 	case FAN53526_VENDOR_FAIRCHILD:
230914df8faSJoseph Chen 	case FAN53555_VENDOR_FAIRCHILD:
231914df8faSJoseph Chen 	case FAN53555_VENDOR_SILERGY:
232914df8faSJoseph Chen 		slew_rate_t = slew_rates;
233914df8faSJoseph Chen 		slew_rate_n = ARRAY_SIZE(slew_rates);
234914df8faSJoseph Chen 		break;
235b3cc8ec0SPeter Geis 	case FAN53526_VENDOR_TCS:
236914df8faSJoseph Chen 		slew_rate_t = tcs_slew_rates;
237914df8faSJoseph Chen 		slew_rate_n = ARRAY_SIZE(tcs_slew_rates);
238914df8faSJoseph Chen 		break;
239914df8faSJoseph Chen 	default:
240914df8faSJoseph Chen 		return -EINVAL;
241914df8faSJoseph Chen 	}
242914df8faSJoseph Chen 
243914df8faSJoseph Chen 	for (i = 0; i < slew_rate_n; i++) {
244914df8faSJoseph Chen 		if (ramp <= slew_rate_t[i])
245dd7e71fbSHeiko Stuebner 			regval = i;
246dd7e71fbSHeiko Stuebner 		else
247dd7e71fbSHeiko Stuebner 			break;
248dd7e71fbSHeiko Stuebner 	}
249dd7e71fbSHeiko Stuebner 
250dd7e71fbSHeiko Stuebner 	if (regval < 0) {
251dd7e71fbSHeiko Stuebner 		dev_err(di->dev, "unsupported ramp value %d\n", ramp);
252dd7e71fbSHeiko Stuebner 		return -EINVAL;
253dd7e71fbSHeiko Stuebner 	}
254dd7e71fbSHeiko Stuebner 
255914df8faSJoseph Chen 	return regmap_update_bits(rdev->regmap, di->slew_reg,
256914df8faSJoseph Chen 				  di->slew_mask, regval << di->slew_shift);
257dd7e71fbSHeiko Stuebner }
258dd7e71fbSHeiko Stuebner 
25971880ab2SBhumika Goyal static const struct regulator_ops fan53555_regulator_ops = {
26049d8c599SYunfan Zhang 	.set_voltage_sel = regulator_set_voltage_sel_regmap,
26149d8c599SYunfan Zhang 	.get_voltage_sel = regulator_get_voltage_sel_regmap,
262fda87a42SHeiko Stuebner 	.set_voltage_time_sel = regulator_set_voltage_time_sel,
26349d8c599SYunfan Zhang 	.map_voltage = regulator_map_voltage_linear,
26449d8c599SYunfan Zhang 	.list_voltage = regulator_list_voltage_linear,
26549d8c599SYunfan Zhang 	.set_suspend_voltage = fan53555_set_suspend_voltage,
26649d8c599SYunfan Zhang 	.enable = regulator_enable_regmap,
26749d8c599SYunfan Zhang 	.disable = regulator_disable_regmap,
26849d8c599SYunfan Zhang 	.is_enabled = regulator_is_enabled_regmap,
26949d8c599SYunfan Zhang 	.set_mode = fan53555_set_mode,
27049d8c599SYunfan Zhang 	.get_mode = fan53555_get_mode,
271dd7e71fbSHeiko Stuebner 	.set_ramp_delay = fan53555_set_ramp,
272ab7cad33Szhangqing 	.set_suspend_enable = fan53555_set_suspend_enable,
273ab7cad33Szhangqing 	.set_suspend_disable = fan53555_set_suspend_disable,
27449d8c599SYunfan Zhang };
27549d8c599SYunfan Zhang 
276f2a9eb97SBjorn Andersson static int fan53526_voltages_setup_fairchild(struct fan53555_device_info *di)
277f2a9eb97SBjorn Andersson {
278f2a9eb97SBjorn Andersson 	/* Init voltage range and step */
279f2a9eb97SBjorn Andersson 	switch (di->chip_id) {
280f2a9eb97SBjorn Andersson 	case FAN53526_CHIP_ID_01:
281f2a9eb97SBjorn Andersson 		switch (di->chip_rev) {
282f2a9eb97SBjorn Andersson 		case FAN53526_CHIP_REV_08:
283f2a9eb97SBjorn Andersson 			di->vsel_min = 600000;
284f2a9eb97SBjorn Andersson 			di->vsel_step = 6250;
285f2a9eb97SBjorn Andersson 			break;
286f2a9eb97SBjorn Andersson 		default:
287f2a9eb97SBjorn Andersson 			dev_err(di->dev,
288f2a9eb97SBjorn Andersson 				"Chip ID %d with rev %d not supported!\n",
289f2a9eb97SBjorn Andersson 				di->chip_id, di->chip_rev);
290f2a9eb97SBjorn Andersson 			return -EINVAL;
291f2a9eb97SBjorn Andersson 		}
292f2a9eb97SBjorn Andersson 		break;
293f2a9eb97SBjorn Andersson 	default:
294f2a9eb97SBjorn Andersson 		dev_err(di->dev,
295f2a9eb97SBjorn Andersson 			"Chip ID %d not supported!\n", di->chip_id);
296f2a9eb97SBjorn Andersson 		return -EINVAL;
297f2a9eb97SBjorn Andersson 	}
298f2a9eb97SBjorn Andersson 
299*30b38b80SAxel Lin 	di->slew_reg = FAN53555_CONTROL;
300*30b38b80SAxel Lin 	di->slew_mask = CTL_SLEW_MASK;
301*30b38b80SAxel Lin 	di->slew_shift = CTL_SLEW_SHIFT;
302f2a9eb97SBjorn Andersson 	di->vsel_count = FAN53526_NVOLTAGES;
303f2a9eb97SBjorn Andersson 
304f2a9eb97SBjorn Andersson 	return 0;
305f2a9eb97SBjorn Andersson }
306f2a9eb97SBjorn Andersson 
307ee30928aSHeiko Stuebner static int fan53555_voltages_setup_fairchild(struct fan53555_device_info *di)
30849d8c599SYunfan Zhang {
30949d8c599SYunfan Zhang 	/* Init voltage range and step */
31049d8c599SYunfan Zhang 	switch (di->chip_id) {
31149d8c599SYunfan Zhang 	case FAN53555_CHIP_ID_00:
312e57cbb70SWadim Egorov 		switch (di->chip_rev) {
313e57cbb70SWadim Egorov 		case FAN53555_CHIP_REV_00:
314e57cbb70SWadim Egorov 			di->vsel_min = 600000;
315e57cbb70SWadim Egorov 			di->vsel_step = 10000;
316e57cbb70SWadim Egorov 			break;
317e57cbb70SWadim Egorov 		case FAN53555_CHIP_REV_13:
318e57cbb70SWadim Egorov 			di->vsel_min = 800000;
319e57cbb70SWadim Egorov 			di->vsel_step = 10000;
320e57cbb70SWadim Egorov 			break;
321e57cbb70SWadim Egorov 		default:
322e57cbb70SWadim Egorov 			dev_err(di->dev,
323e57cbb70SWadim Egorov 				"Chip ID %d with rev %d not supported!\n",
324e57cbb70SWadim Egorov 				di->chip_id, di->chip_rev);
325e57cbb70SWadim Egorov 			return -EINVAL;
326e57cbb70SWadim Egorov 		}
327e57cbb70SWadim Egorov 		break;
32849d8c599SYunfan Zhang 	case FAN53555_CHIP_ID_01:
32949d8c599SYunfan Zhang 	case FAN53555_CHIP_ID_03:
33049d8c599SYunfan Zhang 	case FAN53555_CHIP_ID_05:
3315e39cf49SWadim Egorov 	case FAN53555_CHIP_ID_08:
33249d8c599SYunfan Zhang 		di->vsel_min = 600000;
33349d8c599SYunfan Zhang 		di->vsel_step = 10000;
33449d8c599SYunfan Zhang 		break;
33549d8c599SYunfan Zhang 	case FAN53555_CHIP_ID_04:
33649d8c599SYunfan Zhang 		di->vsel_min = 603000;
33749d8c599SYunfan Zhang 		di->vsel_step = 12826;
33849d8c599SYunfan Zhang 		break;
33949d8c599SYunfan Zhang 	default:
34049d8c599SYunfan Zhang 		dev_err(di->dev,
341ee30928aSHeiko Stuebner 			"Chip ID %d not supported!\n", di->chip_id);
34249d8c599SYunfan Zhang 		return -EINVAL;
34349d8c599SYunfan Zhang 	}
344914df8faSJoseph Chen 	di->slew_reg = FAN53555_CONTROL;
345914df8faSJoseph Chen 	di->slew_mask = CTL_SLEW_MASK;
346914df8faSJoseph Chen 	di->slew_shift = CTL_SLEW_SHIFT;
347f2a9eb97SBjorn Andersson 	di->vsel_count = FAN53555_NVOLTAGES;
348f2a9eb97SBjorn Andersson 
349dd7e71fbSHeiko Stuebner 	return 0;
35049d8c599SYunfan Zhang }
35149d8c599SYunfan Zhang 
352ee30928aSHeiko Stuebner static int fan53555_voltages_setup_silergy(struct fan53555_device_info *di)
353ee30928aSHeiko Stuebner {
354ee30928aSHeiko Stuebner 	/* Init voltage range and step */
355ee30928aSHeiko Stuebner 	switch (di->chip_id) {
356ee30928aSHeiko Stuebner 	case SILERGY_SYR82X:
3575365e3dfSVasily Khoruzhick 	case SILERGY_SYR83X:
358ee30928aSHeiko Stuebner 		di->vsel_min = 712500;
359ee30928aSHeiko Stuebner 		di->vsel_step = 12500;
360ee30928aSHeiko Stuebner 		break;
361ee30928aSHeiko Stuebner 	default:
362ee30928aSHeiko Stuebner 		dev_err(di->dev,
363ee30928aSHeiko Stuebner 			"Chip ID %d not supported!\n", di->chip_id);
364ee30928aSHeiko Stuebner 		return -EINVAL;
365ee30928aSHeiko Stuebner 	}
366914df8faSJoseph Chen 	di->slew_reg = FAN53555_CONTROL;
367914df8faSJoseph Chen 	di->slew_mask = CTL_SLEW_MASK;
368914df8faSJoseph Chen 	di->slew_shift = CTL_SLEW_SHIFT;
369f2a9eb97SBjorn Andersson 	di->vsel_count = FAN53555_NVOLTAGES;
370f2a9eb97SBjorn Andersson 
371ee30928aSHeiko Stuebner 	return 0;
372ee30928aSHeiko Stuebner }
373ee30928aSHeiko Stuebner 
374b3cc8ec0SPeter Geis static int fan53526_voltages_setup_tcs(struct fan53555_device_info *di)
375914df8faSJoseph Chen {
376f9028dcdSPeter Geis 	switch (di->chip_id) {
377f9028dcdSPeter Geis 	case TCS4525_CHIP_ID_12:
378914df8faSJoseph Chen 		di->slew_reg = TCS4525_TIME;
379914df8faSJoseph Chen 		di->slew_mask = TCS_SLEW_MASK;
380a7f00314SAxel Lin 		di->slew_shift = TCS_SLEW_SHIFT;
381914df8faSJoseph Chen 
382914df8faSJoseph Chen 		/* Init voltage range and step */
383914df8faSJoseph Chen 		di->vsel_min = 600000;
384914df8faSJoseph Chen 		di->vsel_step = 6250;
385d4db69ebSPeter Geis 		di->vsel_count = FAN53526_NVOLTAGES;
386f9028dcdSPeter Geis 		break;
387f9028dcdSPeter Geis 	default:
388f9028dcdSPeter Geis 		dev_err(di->dev, "Chip ID %d not supported!\n", di->chip_id);
389f9028dcdSPeter Geis 		return -EINVAL;
390f9028dcdSPeter Geis 	}
391914df8faSJoseph Chen 
392914df8faSJoseph Chen 	return 0;
393914df8faSJoseph Chen }
394914df8faSJoseph Chen 
395ee30928aSHeiko Stuebner /* For 00,01,03,05 options:
396ee30928aSHeiko Stuebner  * VOUT = 0.60V + NSELx * 10mV, from 0.60 to 1.23V.
397ee30928aSHeiko Stuebner  * For 04 option:
398ee30928aSHeiko Stuebner  * VOUT = 0.603V + NSELx * 12.826mV, from 0.603 to 1.411V.
399ee30928aSHeiko Stuebner  * */
400ee30928aSHeiko Stuebner static int fan53555_device_setup(struct fan53555_device_info *di,
401ee30928aSHeiko Stuebner 				struct fan53555_platform_data *pdata)
402ee30928aSHeiko Stuebner {
403ee30928aSHeiko Stuebner 	int ret = 0;
404ee30928aSHeiko Stuebner 
405ee30928aSHeiko Stuebner 	/* Setup voltage control register */
406914df8faSJoseph Chen 	switch (di->vendor) {
407914df8faSJoseph Chen 	case FAN53526_VENDOR_FAIRCHILD:
408914df8faSJoseph Chen 	case FAN53555_VENDOR_FAIRCHILD:
409914df8faSJoseph Chen 	case FAN53555_VENDOR_SILERGY:
410ee30928aSHeiko Stuebner 		switch (pdata->sleep_vsel_id) {
411ee30928aSHeiko Stuebner 		case FAN53555_VSEL_ID_0:
412ee30928aSHeiko Stuebner 			di->sleep_reg = FAN53555_VSEL0;
413ee30928aSHeiko Stuebner 			di->vol_reg = FAN53555_VSEL1;
414ee30928aSHeiko Stuebner 			break;
415ee30928aSHeiko Stuebner 		case FAN53555_VSEL_ID_1:
416ee30928aSHeiko Stuebner 			di->sleep_reg = FAN53555_VSEL1;
417ee30928aSHeiko Stuebner 			di->vol_reg = FAN53555_VSEL0;
418ee30928aSHeiko Stuebner 			break;
419ee30928aSHeiko Stuebner 		default:
420ee30928aSHeiko Stuebner 			dev_err(di->dev, "Invalid VSEL ID!\n");
421ee30928aSHeiko Stuebner 			return -EINVAL;
422ee30928aSHeiko Stuebner 		}
423914df8faSJoseph Chen 		break;
424b3cc8ec0SPeter Geis 	case FAN53526_VENDOR_TCS:
425914df8faSJoseph Chen 		switch (pdata->sleep_vsel_id) {
426914df8faSJoseph Chen 		case FAN53555_VSEL_ID_0:
427914df8faSJoseph Chen 			di->sleep_reg = TCS4525_VSEL0;
428914df8faSJoseph Chen 			di->vol_reg = TCS4525_VSEL1;
429914df8faSJoseph Chen 			break;
430914df8faSJoseph Chen 		case FAN53555_VSEL_ID_1:
431914df8faSJoseph Chen 			di->sleep_reg = TCS4525_VSEL1;
432914df8faSJoseph Chen 			di->vol_reg = TCS4525_VSEL0;
433914df8faSJoseph Chen 			break;
434914df8faSJoseph Chen 		default:
435914df8faSJoseph Chen 			dev_err(di->dev, "Invalid VSEL ID!\n");
436914df8faSJoseph Chen 			return -EINVAL;
437914df8faSJoseph Chen 		}
438914df8faSJoseph Chen 		break;
439914df8faSJoseph Chen 	default:
440914df8faSJoseph Chen 		dev_err(di->dev, "vendor %d not supported!\n", di->vendor);
441914df8faSJoseph Chen 		return -EINVAL;
442914df8faSJoseph Chen 	}
443ee30928aSHeiko Stuebner 
444f2a9eb97SBjorn Andersson 	/* Setup mode control register */
445ee30928aSHeiko Stuebner 	switch (di->vendor) {
446f2a9eb97SBjorn Andersson 	case FAN53526_VENDOR_FAIRCHILD:
447f2a9eb97SBjorn Andersson 		di->mode_reg = FAN53555_CONTROL;
448f2a9eb97SBjorn Andersson 
449f2a9eb97SBjorn Andersson 		switch (pdata->sleep_vsel_id) {
450f2a9eb97SBjorn Andersson 		case FAN53555_VSEL_ID_0:
451f2a9eb97SBjorn Andersson 			di->mode_mask = CTL_MODE_VSEL1_MODE;
452f2a9eb97SBjorn Andersson 			break;
453f2a9eb97SBjorn Andersson 		case FAN53555_VSEL_ID_1:
454f2a9eb97SBjorn Andersson 			di->mode_mask = CTL_MODE_VSEL0_MODE;
455f2a9eb97SBjorn Andersson 			break;
456f2a9eb97SBjorn Andersson 		}
457f2a9eb97SBjorn Andersson 		break;
458f2a9eb97SBjorn Andersson 	case FAN53555_VENDOR_FAIRCHILD:
459f2a9eb97SBjorn Andersson 	case FAN53555_VENDOR_SILERGY:
460f2a9eb97SBjorn Andersson 		di->mode_reg = di->vol_reg;
461f2a9eb97SBjorn Andersson 		di->mode_mask = VSEL_MODE;
462f2a9eb97SBjorn Andersson 		break;
463b3cc8ec0SPeter Geis 	case FAN53526_VENDOR_TCS:
464914df8faSJoseph Chen 		di->mode_reg = TCS4525_COMMAND;
465914df8faSJoseph Chen 
466914df8faSJoseph Chen 		switch (pdata->sleep_vsel_id) {
467914df8faSJoseph Chen 		case FAN53555_VSEL_ID_0:
468914df8faSJoseph Chen 			di->mode_mask = TCS_VSEL1_MODE;
469914df8faSJoseph Chen 			break;
470914df8faSJoseph Chen 		case FAN53555_VSEL_ID_1:
471914df8faSJoseph Chen 			di->mode_mask = TCS_VSEL0_MODE;
472914df8faSJoseph Chen 			break;
473914df8faSJoseph Chen 		}
474914df8faSJoseph Chen 		break;
475f2a9eb97SBjorn Andersson 	default:
476f2a9eb97SBjorn Andersson 		dev_err(di->dev, "vendor %d not supported!\n", di->vendor);
477f2a9eb97SBjorn Andersson 		return -EINVAL;
478f2a9eb97SBjorn Andersson 	}
479f2a9eb97SBjorn Andersson 
480f2a9eb97SBjorn Andersson 	/* Setup voltage range */
481f2a9eb97SBjorn Andersson 	switch (di->vendor) {
482f2a9eb97SBjorn Andersson 	case FAN53526_VENDOR_FAIRCHILD:
483f2a9eb97SBjorn Andersson 		ret = fan53526_voltages_setup_fairchild(di);
484f2a9eb97SBjorn Andersson 		break;
485ee30928aSHeiko Stuebner 	case FAN53555_VENDOR_FAIRCHILD:
486ee30928aSHeiko Stuebner 		ret = fan53555_voltages_setup_fairchild(di);
487ee30928aSHeiko Stuebner 		break;
488ee30928aSHeiko Stuebner 	case FAN53555_VENDOR_SILERGY:
489ee30928aSHeiko Stuebner 		ret = fan53555_voltages_setup_silergy(di);
490ee30928aSHeiko Stuebner 		break;
491b3cc8ec0SPeter Geis 	case FAN53526_VENDOR_TCS:
492b3cc8ec0SPeter Geis 		ret = fan53526_voltages_setup_tcs(di);
493914df8faSJoseph Chen 		break;
494ee30928aSHeiko Stuebner 	default:
495fe230531SAxel Lin 		dev_err(di->dev, "vendor %d not supported!\n", di->vendor);
496ee30928aSHeiko Stuebner 		return -EINVAL;
497ee30928aSHeiko Stuebner 	}
498ee30928aSHeiko Stuebner 
499ee30928aSHeiko Stuebner 	return ret;
500ee30928aSHeiko Stuebner }
501ee30928aSHeiko Stuebner 
50249d8c599SYunfan Zhang static int fan53555_regulator_register(struct fan53555_device_info *di,
50349d8c599SYunfan Zhang 			struct regulator_config *config)
50449d8c599SYunfan Zhang {
50549d8c599SYunfan Zhang 	struct regulator_desc *rdesc = &di->desc;
506a69929c7SAxel Lin 	struct regulator_dev *rdev;
50749d8c599SYunfan Zhang 
50849d8c599SYunfan Zhang 	rdesc->name = "fan53555-reg";
5093415d601SHeiko Stuebner 	rdesc->supply_name = "vin";
51049d8c599SYunfan Zhang 	rdesc->ops = &fan53555_regulator_ops;
51149d8c599SYunfan Zhang 	rdesc->type = REGULATOR_VOLTAGE;
512f2a9eb97SBjorn Andersson 	rdesc->n_voltages = di->vsel_count;
51349d8c599SYunfan Zhang 	rdesc->enable_reg = di->vol_reg;
51449d8c599SYunfan Zhang 	rdesc->enable_mask = VSEL_BUCK_EN;
51549d8c599SYunfan Zhang 	rdesc->min_uV = di->vsel_min;
51649d8c599SYunfan Zhang 	rdesc->uV_step = di->vsel_step;
51749d8c599SYunfan Zhang 	rdesc->vsel_reg = di->vol_reg;
518f2a9eb97SBjorn Andersson 	rdesc->vsel_mask = di->vsel_count - 1;
51949d8c599SYunfan Zhang 	rdesc->owner = THIS_MODULE;
52049d8c599SYunfan Zhang 
521a69929c7SAxel Lin 	rdev = devm_regulator_register(di->dev, &di->desc, config);
522a69929c7SAxel Lin 	return PTR_ERR_OR_ZERO(rdev);
52349d8c599SYunfan Zhang }
52449d8c599SYunfan Zhang 
525121b567dSKrzysztof Kozlowski static const struct regmap_config fan53555_regmap_config = {
52649d8c599SYunfan Zhang 	.reg_bits = 8,
52749d8c599SYunfan Zhang 	.val_bits = 8,
52849d8c599SYunfan Zhang };
52949d8c599SYunfan Zhang 
53091f23d8fSHeiko Stuebner static struct fan53555_platform_data *fan53555_parse_dt(struct device *dev,
531072e78b1SJavier Martinez Canillas 					      struct device_node *np,
532072e78b1SJavier Martinez Canillas 					      const struct regulator_desc *desc)
53391f23d8fSHeiko Stuebner {
53491f23d8fSHeiko Stuebner 	struct fan53555_platform_data *pdata;
53591f23d8fSHeiko Stuebner 	int ret;
53691f23d8fSHeiko Stuebner 	u32 tmp;
53791f23d8fSHeiko Stuebner 
53891f23d8fSHeiko Stuebner 	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
53991f23d8fSHeiko Stuebner 	if (!pdata)
54091f23d8fSHeiko Stuebner 		return NULL;
54191f23d8fSHeiko Stuebner 
542072e78b1SJavier Martinez Canillas 	pdata->regulator = of_get_regulator_init_data(dev, np, desc);
54391f23d8fSHeiko Stuebner 
54491f23d8fSHeiko Stuebner 	ret = of_property_read_u32(np, "fcs,suspend-voltage-selector",
54591f23d8fSHeiko Stuebner 				   &tmp);
54691f23d8fSHeiko Stuebner 	if (!ret)
54791f23d8fSHeiko Stuebner 		pdata->sleep_vsel_id = tmp;
54891f23d8fSHeiko Stuebner 
54991f23d8fSHeiko Stuebner 	return pdata;
55091f23d8fSHeiko Stuebner }
55191f23d8fSHeiko Stuebner 
5525e97d7e8SJisheng Zhang static const struct of_device_id __maybe_unused fan53555_dt_ids[] = {
55391f23d8fSHeiko Stuebner 	{
554f2a9eb97SBjorn Andersson 		.compatible = "fcs,fan53526",
555f2a9eb97SBjorn Andersson 		.data = (void *)FAN53526_VENDOR_FAIRCHILD,
556f2a9eb97SBjorn Andersson 	}, {
55791f23d8fSHeiko Stuebner 		.compatible = "fcs,fan53555",
558ee30928aSHeiko Stuebner 		.data = (void *)FAN53555_VENDOR_FAIRCHILD
559ee30928aSHeiko Stuebner 	}, {
560ee30928aSHeiko Stuebner 		.compatible = "silergy,syr827",
561ee30928aSHeiko Stuebner 		.data = (void *)FAN53555_VENDOR_SILERGY,
562ee30928aSHeiko Stuebner 	}, {
563ee30928aSHeiko Stuebner 		.compatible = "silergy,syr828",
564ee30928aSHeiko Stuebner 		.data = (void *)FAN53555_VENDOR_SILERGY,
565914df8faSJoseph Chen 	}, {
566914df8faSJoseph Chen 		.compatible = "tcs,tcs4525",
567b3cc8ec0SPeter Geis 		.data = (void *)FAN53526_VENDOR_TCS
56891f23d8fSHeiko Stuebner 	},
56991f23d8fSHeiko Stuebner 	{ }
57091f23d8fSHeiko Stuebner };
57191f23d8fSHeiko Stuebner MODULE_DEVICE_TABLE(of, fan53555_dt_ids);
57291f23d8fSHeiko Stuebner 
573a5023574SBill Pemberton static int fan53555_regulator_probe(struct i2c_client *client,
57449d8c599SYunfan Zhang 				const struct i2c_device_id *id)
57549d8c599SYunfan Zhang {
57691f23d8fSHeiko Stuebner 	struct device_node *np = client->dev.of_node;
57749d8c599SYunfan Zhang 	struct fan53555_device_info *di;
57849d8c599SYunfan Zhang 	struct fan53555_platform_data *pdata;
57949d8c599SYunfan Zhang 	struct regulator_config config = { };
580a69929c7SAxel Lin 	struct regmap *regmap;
58149d8c599SYunfan Zhang 	unsigned int val;
58249d8c599SYunfan Zhang 	int ret;
58349d8c599SYunfan Zhang 
584072e78b1SJavier Martinez Canillas 	di = devm_kzalloc(&client->dev, sizeof(struct fan53555_device_info),
585072e78b1SJavier Martinez Canillas 					GFP_KERNEL);
586072e78b1SJavier Martinez Canillas 	if (!di)
587072e78b1SJavier Martinez Canillas 		return -ENOMEM;
588072e78b1SJavier Martinez Canillas 
589dff91d0bSJingoo Han 	pdata = dev_get_platdata(&client->dev);
59091f23d8fSHeiko Stuebner 	if (!pdata)
591072e78b1SJavier Martinez Canillas 		pdata = fan53555_parse_dt(&client->dev, np, &di->desc);
59291f23d8fSHeiko Stuebner 
59349d8c599SYunfan Zhang 	if (!pdata || !pdata->regulator) {
59449d8c599SYunfan Zhang 		dev_err(&client->dev, "Platform data not found!\n");
59549d8c599SYunfan Zhang 		return -ENODEV;
59649d8c599SYunfan Zhang 	}
59749d8c599SYunfan Zhang 
598e13426bfSAxel Lin 	di->regulator = pdata->regulator;
599ee30928aSHeiko Stuebner 	if (client->dev.of_node) {
600d110e3e9SJisheng Zhang 		di->vendor =
601d110e3e9SJisheng Zhang 			(unsigned long)of_device_get_match_data(&client->dev);
602ee30928aSHeiko Stuebner 	} else {
603dd7e71fbSHeiko Stuebner 		/* if no ramp constraint set, get the pdata ramp_delay */
604dd7e71fbSHeiko Stuebner 		if (!di->regulator->constraints.ramp_delay) {
60587919e0cSAxel Lin 			if (pdata->slew_rate >= ARRAY_SIZE(slew_rates)) {
60687919e0cSAxel Lin 				dev_err(&client->dev, "Invalid slew_rate\n");
60787919e0cSAxel Lin 				return -EINVAL;
60887919e0cSAxel Lin 			}
609dd7e71fbSHeiko Stuebner 
61091f23d8fSHeiko Stuebner 			di->regulator->constraints.ramp_delay
61187919e0cSAxel Lin 					= slew_rates[pdata->slew_rate];
61291f23d8fSHeiko Stuebner 		}
613ee30928aSHeiko Stuebner 
614ee30928aSHeiko Stuebner 		di->vendor = id->driver_data;
615dd7e71fbSHeiko Stuebner 	}
616dd7e71fbSHeiko Stuebner 
617a69929c7SAxel Lin 	regmap = devm_regmap_init_i2c(client, &fan53555_regmap_config);
618a69929c7SAxel Lin 	if (IS_ERR(regmap)) {
61949d8c599SYunfan Zhang 		dev_err(&client->dev, "Failed to allocate regmap!\n");
620a69929c7SAxel Lin 		return PTR_ERR(regmap);
62149d8c599SYunfan Zhang 	}
62249d8c599SYunfan Zhang 	di->dev = &client->dev;
62349d8c599SYunfan Zhang 	i2c_set_clientdata(client, di);
62449d8c599SYunfan Zhang 	/* Get chip ID */
625a69929c7SAxel Lin 	ret = regmap_read(regmap, FAN53555_ID1, &val);
62649d8c599SYunfan Zhang 	if (ret < 0) {
62749d8c599SYunfan Zhang 		dev_err(&client->dev, "Failed to get chip ID!\n");
628145fe1e1SSachin Kamat 		return ret;
62949d8c599SYunfan Zhang 	}
63049d8c599SYunfan Zhang 	di->chip_id = val & DIE_ID;
63149d8c599SYunfan Zhang 	/* Get chip revision */
632a69929c7SAxel Lin 	ret = regmap_read(regmap, FAN53555_ID2, &val);
63349d8c599SYunfan Zhang 	if (ret < 0) {
63449d8c599SYunfan Zhang 		dev_err(&client->dev, "Failed to get chip Rev!\n");
635145fe1e1SSachin Kamat 		return ret;
63649d8c599SYunfan Zhang 	}
63749d8c599SYunfan Zhang 	di->chip_rev = val & DIE_REV;
63849d8c599SYunfan Zhang 	dev_info(&client->dev, "FAN53555 Option[%d] Rev[%d] Detected!\n",
63949d8c599SYunfan Zhang 				di->chip_id, di->chip_rev);
64049d8c599SYunfan Zhang 	/* Device init */
64149d8c599SYunfan Zhang 	ret = fan53555_device_setup(di, pdata);
64249d8c599SYunfan Zhang 	if (ret < 0) {
64349d8c599SYunfan Zhang 		dev_err(&client->dev, "Failed to setup device!\n");
64449d8c599SYunfan Zhang 		return ret;
64549d8c599SYunfan Zhang 	}
64649d8c599SYunfan Zhang 	/* Register regulator */
64749d8c599SYunfan Zhang 	config.dev = di->dev;
64849d8c599SYunfan Zhang 	config.init_data = di->regulator;
649a69929c7SAxel Lin 	config.regmap = regmap;
65049d8c599SYunfan Zhang 	config.driver_data = di;
65191f23d8fSHeiko Stuebner 	config.of_node = np;
65291f23d8fSHeiko Stuebner 
65349d8c599SYunfan Zhang 	ret = fan53555_regulator_register(di, &config);
65449d8c599SYunfan Zhang 	if (ret < 0)
65549d8c599SYunfan Zhang 		dev_err(&client->dev, "Failed to register regulator!\n");
65649d8c599SYunfan Zhang 	return ret;
65749d8c599SYunfan Zhang 
65849d8c599SYunfan Zhang }
65949d8c599SYunfan Zhang 
66049d8c599SYunfan Zhang static const struct i2c_device_id fan53555_id[] = {
661ee30928aSHeiko Stuebner 	{
662f2a9eb97SBjorn Andersson 		.name = "fan53526",
663f2a9eb97SBjorn Andersson 		.driver_data = FAN53526_VENDOR_FAIRCHILD
664f2a9eb97SBjorn Andersson 	}, {
665ee30928aSHeiko Stuebner 		.name = "fan53555",
666ee30928aSHeiko Stuebner 		.driver_data = FAN53555_VENDOR_FAIRCHILD
667ee30928aSHeiko Stuebner 	}, {
668fc1111b8SGuillaume Tucker 		.name = "syr827",
669fc1111b8SGuillaume Tucker 		.driver_data = FAN53555_VENDOR_SILERGY
670fc1111b8SGuillaume Tucker 	}, {
671fc1111b8SGuillaume Tucker 		.name = "syr828",
672ee30928aSHeiko Stuebner 		.driver_data = FAN53555_VENDOR_SILERGY
673914df8faSJoseph Chen 	}, {
674914df8faSJoseph Chen 		.name = "tcs4525",
675b3cc8ec0SPeter Geis 		.driver_data = FAN53526_VENDOR_TCS
676ee30928aSHeiko Stuebner 	},
67749d8c599SYunfan Zhang 	{ },
67849d8c599SYunfan Zhang };
679e80c47bdSJavier Martinez Canillas MODULE_DEVICE_TABLE(i2c, fan53555_id);
68049d8c599SYunfan Zhang 
68149d8c599SYunfan Zhang static struct i2c_driver fan53555_regulator_driver = {
68249d8c599SYunfan Zhang 	.driver = {
68349d8c599SYunfan Zhang 		.name = "fan53555-regulator",
68491f23d8fSHeiko Stuebner 		.of_match_table = of_match_ptr(fan53555_dt_ids),
68549d8c599SYunfan Zhang 	},
68649d8c599SYunfan Zhang 	.probe = fan53555_regulator_probe,
68749d8c599SYunfan Zhang 	.id_table = fan53555_id,
68849d8c599SYunfan Zhang };
68949d8c599SYunfan Zhang 
69049d8c599SYunfan Zhang module_i2c_driver(fan53555_regulator_driver);
69149d8c599SYunfan Zhang 
69249d8c599SYunfan Zhang MODULE_AUTHOR("Yunfan Zhang <yfzhang@marvell.com>");
69349d8c599SYunfan Zhang MODULE_DESCRIPTION("FAN53555 regulator driver");
69449d8c599SYunfan Zhang MODULE_LICENSE("GPL v2");
695