1d3795d63SJagan Teki // SPDX-License-Identifier: GPL-2.0+
2d3795d63SJagan Teki /*
3d3795d63SJagan Teki  * Copyright (C) 2017 NXP
4d3795d63SJagan Teki  * Copyright (C) 2019 Boundary Devices
5d3795d63SJagan Teki  * Copyright (C) 2020 Amarula Solutions(India)
6d3795d63SJagan Teki  */
7d3795d63SJagan Teki 
8d3795d63SJagan Teki #include <linux/delay.h>
9d3795d63SJagan Teki #include <linux/err.h>
10d3795d63SJagan Teki #include <linux/gpio/consumer.h>
11d3795d63SJagan Teki #include <linux/i2c.h>
12d3795d63SJagan Teki #include <linux/module.h>
13d3795d63SJagan Teki #include <linux/regmap.h>
14d3795d63SJagan Teki #include <linux/regulator/driver.h>
15d3795d63SJagan Teki #include <linux/regulator/machine.h>
16d3795d63SJagan Teki 
17d3795d63SJagan Teki /* registers */
18d3795d63SJagan Teki #define PF8X00_DEVICEID			0x00
19d3795d63SJagan Teki #define PF8X00_REVID			0x01
20d3795d63SJagan Teki #define PF8X00_EMREV			0x02
21d3795d63SJagan Teki #define PF8X00_PROGID			0x03
22d3795d63SJagan Teki #define PF8X00_IMS_INT			0x04
23d3795d63SJagan Teki #define PF8X00_IMS_THERM		0x07
24d3795d63SJagan Teki #define PF8X00_SW_MODE_INT		0x0a
25d3795d63SJagan Teki #define PF8X00_SW_MODE_MASK		0x0b
26d3795d63SJagan Teki #define PF8X00_IMS_SW_ILIM		0x12
27d3795d63SJagan Teki #define PF8X00_IMS_LDO_ILIM		0x15
28d3795d63SJagan Teki #define PF8X00_IMS_SW_UV		0x18
29d3795d63SJagan Teki #define PF8X00_IMS_SW_OV		0x1b
30d3795d63SJagan Teki #define PF8X00_IMS_LDO_UV		0x1e
31d3795d63SJagan Teki #define PF8X00_IMS_LDO_OV		0x21
32d3795d63SJagan Teki #define PF8X00_IMS_PWRON		0x24
33d3795d63SJagan Teki #define PF8X00_SYS_INT			0x27
34d3795d63SJagan Teki #define PF8X00_HARD_FAULT		0x29
35d3795d63SJagan Teki #define PF8X00_FSOB_FLAGS		0x2a
36d3795d63SJagan Teki #define PF8X00_FSOB_SELECT		0x2b
37d3795d63SJagan Teki #define PF8X00_ABIST_OV1		0x2c
38d3795d63SJagan Teki #define PF8X00_ABIST_OV2		0x2d
39d3795d63SJagan Teki #define PF8X00_ABIST_UV1		0x2e
40d3795d63SJagan Teki #define PF8X00_ABIST_UV2		0x2f
41d3795d63SJagan Teki #define PF8X00_TEST_FLAGS		0x30
42d3795d63SJagan Teki #define PF8X00_ABIST_RUN		0x31
43d3795d63SJagan Teki #define PF8X00_RANDOM_GEN		0x33
44d3795d63SJagan Teki #define PF8X00_RANDOM_CHK		0x34
45d3795d63SJagan Teki #define PF8X00_VMONEN1			0x35
46d3795d63SJagan Teki #define PF8X00_VMONEN2			0x36
47d3795d63SJagan Teki #define PF8X00_CTRL1			0x37
48d3795d63SJagan Teki #define PF8X00_CTRL2			0x38
49d3795d63SJagan Teki #define PF8X00_CTRL3			0x39
50d3795d63SJagan Teki #define PF8X00_PWRUP_CTRL		0x3a
51d3795d63SJagan Teki #define PF8X00_RESETBMCU		0x3c
52d3795d63SJagan Teki #define PF8X00_PGOOD			0x3d
53d3795d63SJagan Teki #define PF8X00_PWRDN_DLY1		0x3e
54d3795d63SJagan Teki #define PF8X00_PWRDN_DLY2		0x3f
55d3795d63SJagan Teki #define PF8X00_FREQ_CTRL		0x40
56d3795d63SJagan Teki #define PF8X00_COINCELL_CTRL		0x41
57d3795d63SJagan Teki #define PF8X00_PWRON			0x42
58d3795d63SJagan Teki #define PF8X00_WD_CONFIG		0x43
59d3795d63SJagan Teki #define PF8X00_WD_CLEAR			0x44
60d3795d63SJagan Teki #define PF8X00_WD_EXPIRE		0x45
61d3795d63SJagan Teki #define PF8X00_WD_COUNTER		0x46
62d3795d63SJagan Teki #define PF8X00_FAULT_COUNTER		0x47
63d3795d63SJagan Teki #define PF8X00_FSAFE_COUNTER		0x48
64d3795d63SJagan Teki #define PF8X00_FAULT_TIMER		0x49
65d3795d63SJagan Teki #define PF8X00_AMUX			0x4a
66d3795d63SJagan Teki #define PF8X00_SW1_CONFIG1		0x4d
67d3795d63SJagan Teki #define PF8X00_LDO1_CONFIG1		0x85
68d3795d63SJagan Teki #define PF8X00_VSNVS_CONFIG1		0x9d
69d3795d63SJagan Teki #define PF8X00_PAGE_SELECT		0x9f
70d3795d63SJagan Teki 
71d3795d63SJagan Teki /* regulators */
72d3795d63SJagan Teki enum pf8x00_regulators {
73d3795d63SJagan Teki 	PF8X00_LDO1,
74d3795d63SJagan Teki 	PF8X00_LDO2,
75d3795d63SJagan Teki 	PF8X00_LDO3,
76d3795d63SJagan Teki 	PF8X00_LDO4,
77d3795d63SJagan Teki 	PF8X00_BUCK1,
78d3795d63SJagan Teki 	PF8X00_BUCK2,
79d3795d63SJagan Teki 	PF8X00_BUCK3,
80d3795d63SJagan Teki 	PF8X00_BUCK4,
81d3795d63SJagan Teki 	PF8X00_BUCK5,
82d3795d63SJagan Teki 	PF8X00_BUCK6,
83d3795d63SJagan Teki 	PF8X00_BUCK7,
84d3795d63SJagan Teki 	PF8X00_VSNVS,
85d3795d63SJagan Teki 
86d3795d63SJagan Teki 	PF8X00_MAX_REGULATORS,
87d3795d63SJagan Teki };
88d3795d63SJagan Teki 
89d3795d63SJagan Teki enum pf8x00_buck_states {
90d3795d63SJagan Teki 	SW_CONFIG1,
91d3795d63SJagan Teki 	SW_CONFIG2,
92d3795d63SJagan Teki 	SW_PWRUP,
93d3795d63SJagan Teki 	SW_MODE1,
94d3795d63SJagan Teki 	SW_RUN_VOLT,
95d3795d63SJagan Teki 	SW_STBY_VOLT,
96d3795d63SJagan Teki };
97d3795d63SJagan Teki #define PF8X00_SW_BASE(i)		(8 * (i - PF8X00_BUCK1) + PF8X00_SW1_CONFIG1)
98d3795d63SJagan Teki 
99d3795d63SJagan Teki enum pf8x00_ldo_states {
100d3795d63SJagan Teki 	LDO_CONFIG1,
101d3795d63SJagan Teki 	LDO_CONFIG2,
102d3795d63SJagan Teki 	LDO_PWRUP,
103d3795d63SJagan Teki 	LDO_RUN_VOLT,
104d3795d63SJagan Teki 	LDO_STBY_VOLT,
105d3795d63SJagan Teki };
106d3795d63SJagan Teki #define PF8X00_LDO_BASE(i)		(6 * (i - PF8X00_LDO1) + PF8X00_LDO1_CONFIG1)
107d3795d63SJagan Teki 
108d3795d63SJagan Teki enum swxilim_bits {
109d3795d63SJagan Teki 	SWXILIM_2100_MA,
110d3795d63SJagan Teki 	SWXILIM_2600_MA,
111d3795d63SJagan Teki 	SWXILIM_3000_MA,
112d3795d63SJagan Teki 	SWXILIM_4500_MA,
113d3795d63SJagan Teki };
114d3795d63SJagan Teki #define PF8X00_SWXILIM_SHIFT		3
115d3795d63SJagan Teki #define PF8X00_SWXILIM_MASK		GENMASK(4, 3)
116d3795d63SJagan Teki #define PF8X00_SWXPHASE_MASK		GENMASK(2, 0)
117d3795d63SJagan Teki #define PF8X00_SWXPHASE_SHIFT		7
118d3795d63SJagan Teki 
119d3795d63SJagan Teki enum pf8x00_devid {
120d3795d63SJagan Teki 	PF8100			= 0x0,
121d3795d63SJagan Teki 	PF8121A			= BIT(1),
122d3795d63SJagan Teki 	PF8200			= BIT(3),
123d3795d63SJagan Teki };
124d3795d63SJagan Teki #define PF8X00_FAM			BIT(6)
125d3795d63SJagan Teki #define PF8X00_DEVICE_FAM_MASK		GENMASK(7, 4)
126d3795d63SJagan Teki #define PF8X00_DEVICE_ID_MASK		GENMASK(3, 0)
127d3795d63SJagan Teki 
1287fefe72bSChristoph Fritz struct pf8x00_regulator_data {
129d3795d63SJagan Teki 	struct regulator_desc desc;
1307fefe72bSChristoph Fritz 	unsigned int suspend_enable_reg;
1317fefe72bSChristoph Fritz 	unsigned int suspend_enable_mask;
1327fefe72bSChristoph Fritz 	unsigned int suspend_voltage_reg;
1337fefe72bSChristoph Fritz 	unsigned int suspend_voltage_cache;
134d3795d63SJagan Teki };
135d3795d63SJagan Teki 
136d3795d63SJagan Teki struct pf8x00_chip {
137d3795d63SJagan Teki 	struct regmap *regmap;
138d3795d63SJagan Teki 	struct device *dev;
139d3795d63SJagan Teki };
140d3795d63SJagan Teki 
141d3795d63SJagan Teki static const struct regmap_config pf8x00_regmap_config = {
142d3795d63SJagan Teki 	.reg_bits = 8,
143d3795d63SJagan Teki 	.val_bits = 8,
144d3795d63SJagan Teki 	.max_register = PF8X00_PAGE_SELECT,
145d3795d63SJagan Teki 	.cache_type = REGCACHE_RBTREE,
146d3795d63SJagan Teki };
147d3795d63SJagan Teki 
148d3795d63SJagan Teki /* VLDOx output: 1.5V to 5.0V */
149d3795d63SJagan Teki static const int pf8x00_ldo_voltages[] = {
150d3795d63SJagan Teki 	1500000, 1600000, 1800000, 1850000, 2150000, 2500000, 2800000, 3000000,
151d3795d63SJagan Teki 	3100000, 3150000, 3200000, 3300000, 3350000, 1650000, 1700000, 5000000,
152d3795d63SJagan Teki };
153d3795d63SJagan Teki 
154245f5f65SAdrien Grassein /* Output: 2.1A to 4.5A */
155245f5f65SAdrien Grassein static const unsigned int pf8x00_sw_current_table[] = {
156245f5f65SAdrien Grassein 	2100000, 2600000, 3000000, 4500000,
157245f5f65SAdrien Grassein };
158d3795d63SJagan Teki 
159d3795d63SJagan Teki /* Output: 0.4V to 1.8V */
16035a93349SAdrien Grassein #define PF8XOO_SW1_6_VOLTAGE_NUM 0xB2
16135a93349SAdrien Grassein static const struct linear_range pf8x00_sw1_to_6_voltages[] = {
16235a93349SAdrien Grassein 	REGULATOR_LINEAR_RANGE(400000, 0x00, 0xB0, 6250),
16335a93349SAdrien Grassein 	REGULATOR_LINEAR_RANGE(1800000, 0xB1, 0xB1, 0),
164d3795d63SJagan Teki };
165d3795d63SJagan Teki 
166d3795d63SJagan Teki /* Output: 1.0V to 4.1V */
167d3795d63SJagan Teki static const int pf8x00_sw7_voltages[] = {
168d3795d63SJagan Teki 	1000000, 1100000, 1200000, 1250000, 1300000, 1350000, 1500000, 1600000,
169d3795d63SJagan Teki 	1800000, 1850000, 2000000, 2100000, 2150000, 2250000, 2300000, 2400000,
170d3795d63SJagan Teki 	2500000, 2800000, 3150000, 3200000, 3250000, 3300000, 3350000, 3400000,
171d3795d63SJagan Teki 	3500000, 3800000, 4000000, 4100000, 4100000, 4100000, 4100000, 4100000,
172d3795d63SJagan Teki };
173d3795d63SJagan Teki 
174d3795d63SJagan Teki /* Output: 1.8V, 3.0V, or 3.3V */
175d3795d63SJagan Teki static const int pf8x00_vsnvs_voltages[] = {
176d3795d63SJagan Teki 	0, 1800000, 3000000, 3300000,
177d3795d63SJagan Teki };
178d3795d63SJagan Teki 
swxilim_select(struct pf8x00_chip * chip,int id,int ilim)179245f5f65SAdrien Grassein static void swxilim_select(struct pf8x00_chip *chip, int id, int ilim)
180d3795d63SJagan Teki {
181d3795d63SJagan Teki 	u8 ilim_sel;
182245f5f65SAdrien Grassein 	u8 reg = PF8X00_SW_BASE(id) + SW_CONFIG2;
183d3795d63SJagan Teki 
184d3795d63SJagan Teki 	switch (ilim) {
185d3795d63SJagan Teki 	case 2100:
186d3795d63SJagan Teki 		ilim_sel = SWXILIM_2100_MA;
187d3795d63SJagan Teki 		break;
188d3795d63SJagan Teki 	case 2600:
189d3795d63SJagan Teki 		ilim_sel = SWXILIM_2600_MA;
190d3795d63SJagan Teki 		break;
191d3795d63SJagan Teki 	case 3000:
192d3795d63SJagan Teki 		ilim_sel = SWXILIM_3000_MA;
193d3795d63SJagan Teki 		break;
194d3795d63SJagan Teki 	case 4500:
195d3795d63SJagan Teki 		ilim_sel = SWXILIM_4500_MA;
196d3795d63SJagan Teki 		break;
197d3795d63SJagan Teki 	default:
198d3795d63SJagan Teki 		ilim_sel = SWXILIM_2100_MA;
199d3795d63SJagan Teki 		break;
200d3795d63SJagan Teki 	}
201d3795d63SJagan Teki 
202245f5f65SAdrien Grassein 	regmap_update_bits(chip->regmap, reg,
203245f5f65SAdrien Grassein 					PF8X00_SWXILIM_MASK,
204245f5f65SAdrien Grassein 					ilim_sel << PF8X00_SWXILIM_SHIFT);
205245f5f65SAdrien Grassein }
206245f5f65SAdrien Grassein 
handle_ilim_property(struct device_node * np,const struct regulator_desc * desc,struct regulator_config * config)207245f5f65SAdrien Grassein static void handle_ilim_property(struct device_node *np,
208245f5f65SAdrien Grassein 			      const struct regulator_desc *desc,
209245f5f65SAdrien Grassein 			      struct regulator_config *config)
210245f5f65SAdrien Grassein {
211245f5f65SAdrien Grassein 	struct pf8x00_chip *chip = config->driver_data;
212245f5f65SAdrien Grassein 	int ret;
213245f5f65SAdrien Grassein 	int val;
214245f5f65SAdrien Grassein 
215245f5f65SAdrien Grassein 	if ((desc->id >= PF8X00_BUCK1) && (desc->id <= PF8X00_BUCK7)) {
216245f5f65SAdrien Grassein 		ret = of_property_read_u32(np, "nxp,ilim-ma", &val);
217245f5f65SAdrien Grassein 		if (ret) {
218245f5f65SAdrien Grassein 			dev_dbg(chip->dev, "unspecified ilim for BUCK%d, use value stored in OTP\n",
219245f5f65SAdrien Grassein 				desc->id - PF8X00_LDO4);
220245f5f65SAdrien Grassein 			return;
221245f5f65SAdrien Grassein 		}
222245f5f65SAdrien Grassein 
223245f5f65SAdrien Grassein 		dev_warn(chip->dev, "nxp,ilim-ma is deprecated, please use regulator-max-microamp\n");
224245f5f65SAdrien Grassein 		swxilim_select(chip, desc->id, val);
225245f5f65SAdrien Grassein 
226245f5f65SAdrien Grassein 	} else
227245f5f65SAdrien Grassein 		dev_warn(chip->dev, "nxp,ilim-ma used with incorrect regulator (%d)\n", desc->id);
228d3795d63SJagan Teki }
229d3795d63SJagan Teki 
handle_shift_property(struct device_node * np,const struct regulator_desc * desc,struct regulator_config * config)230475a5d85SAdrien Grassein static void handle_shift_property(struct device_node *np,
231475a5d85SAdrien Grassein 			      const struct regulator_desc *desc,
232475a5d85SAdrien Grassein 			      struct regulator_config *config)
233475a5d85SAdrien Grassein {
234475a5d85SAdrien Grassein 	unsigned char id = desc->id - PF8X00_LDO4;
235475a5d85SAdrien Grassein 	unsigned char reg = PF8X00_SW_BASE(id) + SW_CONFIG2;
236475a5d85SAdrien Grassein 	struct pf8x00_chip *chip = config->driver_data;
237475a5d85SAdrien Grassein 
238475a5d85SAdrien Grassein 	int phase;
239475a5d85SAdrien Grassein 	int val;
240475a5d85SAdrien Grassein 	int ret;
241475a5d85SAdrien Grassein 	if ((desc->id >= PF8X00_BUCK1) && (desc->id <= PF8X00_BUCK7)) {
242475a5d85SAdrien Grassein 		ret = of_property_read_u32(np, "nxp,phase-shift", &val);
243475a5d85SAdrien Grassein 		if (ret) {
244475a5d85SAdrien Grassein 			dev_dbg(chip->dev,
245475a5d85SAdrien Grassein 				"unspecified phase-shift for BUCK%d, using OTP configuration\n",
246475a5d85SAdrien Grassein 				id);
247475a5d85SAdrien Grassein 			return;
248475a5d85SAdrien Grassein 		}
249475a5d85SAdrien Grassein 
250475a5d85SAdrien Grassein 		if (val < 0 || val > 315 || val % 45 != 0) {
251475a5d85SAdrien Grassein 			dev_warn(config->dev,
252475a5d85SAdrien Grassein 				"invalid phase_shift %d for BUCK%d, using OTP configuration\n",
253475a5d85SAdrien Grassein 				val, id);
254475a5d85SAdrien Grassein 			return;
255475a5d85SAdrien Grassein 		}
256475a5d85SAdrien Grassein 
257475a5d85SAdrien Grassein 		phase = val / 45;
258475a5d85SAdrien Grassein 
259475a5d85SAdrien Grassein 		if (phase >= 1)
260475a5d85SAdrien Grassein 			phase -= 1;
261475a5d85SAdrien Grassein 		else
262475a5d85SAdrien Grassein 			phase = PF8X00_SWXPHASE_SHIFT;
263475a5d85SAdrien Grassein 
264475a5d85SAdrien Grassein 		regmap_update_bits(chip->regmap, reg,
265475a5d85SAdrien Grassein 				PF8X00_SWXPHASE_MASK,
266475a5d85SAdrien Grassein 				phase);
267475a5d85SAdrien Grassein 	} else
268475a5d85SAdrien Grassein 		dev_warn(chip->dev, "nxp,phase-shift used with incorrect regulator (%d)\n", id);
269475a5d85SAdrien Grassein 
270d3795d63SJagan Teki }
271d3795d63SJagan Teki 
pf8x00_of_parse_cb(struct device_node * np,const struct regulator_desc * desc,struct regulator_config * config)272d3795d63SJagan Teki static int pf8x00_of_parse_cb(struct device_node *np,
273d3795d63SJagan Teki 			      const struct regulator_desc *desc,
274d3795d63SJagan Teki 			      struct regulator_config *config)
275d3795d63SJagan Teki {
276d3795d63SJagan Teki 
277245f5f65SAdrien Grassein 	handle_ilim_property(np, desc, config);
278475a5d85SAdrien Grassein 	handle_shift_property(np, desc, config);
279d3795d63SJagan Teki 
280d3795d63SJagan Teki 	return 0;
281d3795d63SJagan Teki }
282d3795d63SJagan Teki 
pf8x00_suspend_enable(struct regulator_dev * rdev)2837fefe72bSChristoph Fritz static int pf8x00_suspend_enable(struct regulator_dev *rdev)
2847fefe72bSChristoph Fritz {
2857fefe72bSChristoph Fritz 	struct pf8x00_regulator_data *regl = rdev_get_drvdata(rdev);
2867fefe72bSChristoph Fritz 	struct regmap *rmap = rdev_get_regmap(rdev);
2877fefe72bSChristoph Fritz 
2887fefe72bSChristoph Fritz 	return regmap_update_bits(rmap, regl->suspend_enable_reg,
2897fefe72bSChristoph Fritz 				  regl->suspend_enable_mask,
2907fefe72bSChristoph Fritz 				  regl->suspend_enable_mask);
2917fefe72bSChristoph Fritz }
2927fefe72bSChristoph Fritz 
pf8x00_suspend_disable(struct regulator_dev * rdev)2937fefe72bSChristoph Fritz static int pf8x00_suspend_disable(struct regulator_dev *rdev)
2947fefe72bSChristoph Fritz {
2957fefe72bSChristoph Fritz 	struct pf8x00_regulator_data *regl = rdev_get_drvdata(rdev);
2967fefe72bSChristoph Fritz 	struct regmap *rmap = rdev_get_regmap(rdev);
2977fefe72bSChristoph Fritz 
2987fefe72bSChristoph Fritz 	return regmap_update_bits(rmap, regl->suspend_enable_reg,
2997fefe72bSChristoph Fritz 				  regl->suspend_enable_mask, 0);
3007fefe72bSChristoph Fritz }
3017fefe72bSChristoph Fritz 
pf8x00_set_suspend_voltage(struct regulator_dev * rdev,int uV)3027fefe72bSChristoph Fritz static int pf8x00_set_suspend_voltage(struct regulator_dev *rdev, int uV)
3037fefe72bSChristoph Fritz {
3047fefe72bSChristoph Fritz 	struct pf8x00_regulator_data *regl = rdev_get_drvdata(rdev);
305d3795d63SJagan Teki 	int ret;
306d3795d63SJagan Teki 
3077fefe72bSChristoph Fritz 	if (regl->suspend_voltage_cache == uV)
3087fefe72bSChristoph Fritz 		return 0;
309d3795d63SJagan Teki 
3107fefe72bSChristoph Fritz 	ret = regulator_map_voltage_iterate(rdev, uV, uV);
3117fefe72bSChristoph Fritz 	if (ret < 0) {
3127fefe72bSChristoph Fritz 		dev_err(rdev_get_dev(rdev), "failed to map %i uV\n", uV);
3137fefe72bSChristoph Fritz 		return ret;
314d3795d63SJagan Teki 	}
315d3795d63SJagan Teki 
3167fefe72bSChristoph Fritz 	dev_dbg(rdev_get_dev(rdev), "uV: %i, reg: 0x%x, msk: 0x%x, val: 0x%x\n",
3177fefe72bSChristoph Fritz 		uV, regl->suspend_voltage_reg, regl->desc.vsel_mask, ret);
3187fefe72bSChristoph Fritz 	ret = regmap_update_bits(rdev->regmap, regl->suspend_voltage_reg,
3197fefe72bSChristoph Fritz 				 regl->desc.vsel_mask, ret);
3207fefe72bSChristoph Fritz 	if (ret < 0) {
3217fefe72bSChristoph Fritz 		dev_err(rdev_get_dev(rdev), "failed to set %i uV\n", uV);
3227fefe72bSChristoph Fritz 		return ret;
323d3795d63SJagan Teki 	}
324d3795d63SJagan Teki 
3257fefe72bSChristoph Fritz 	regl->suspend_voltage_cache = uV;
326d3795d63SJagan Teki 
327d3795d63SJagan Teki 	return 0;
328d3795d63SJagan Teki }
329d3795d63SJagan Teki 
330d3795d63SJagan Teki static const struct regulator_ops pf8x00_ldo_ops = {
331d3795d63SJagan Teki 	.enable = regulator_enable_regmap,
332d3795d63SJagan Teki 	.disable = regulator_disable_regmap,
333d3795d63SJagan Teki 	.is_enabled = regulator_is_enabled_regmap,
334d3795d63SJagan Teki 	.list_voltage = regulator_list_voltage_table,
335d3795d63SJagan Teki 	.set_voltage_sel = regulator_set_voltage_sel_regmap,
336d3795d63SJagan Teki 	.get_voltage_sel = regulator_get_voltage_sel_regmap,
3377fefe72bSChristoph Fritz 	.set_suspend_enable = pf8x00_suspend_enable,
3387fefe72bSChristoph Fritz 	.set_suspend_disable = pf8x00_suspend_disable,
3397fefe72bSChristoph Fritz 	.set_suspend_voltage = pf8x00_set_suspend_voltage,
340d3795d63SJagan Teki };
341d3795d63SJagan Teki 
34235a93349SAdrien Grassein 
34335a93349SAdrien Grassein static const struct regulator_ops pf8x00_buck1_6_ops = {
34435a93349SAdrien Grassein 	.enable = regulator_enable_regmap,
34535a93349SAdrien Grassein 	.disable = regulator_disable_regmap,
34635a93349SAdrien Grassein 	.is_enabled = regulator_is_enabled_regmap,
34735a93349SAdrien Grassein 	.list_voltage = regulator_list_voltage_linear_range,
34835a93349SAdrien Grassein 	.set_voltage_sel = regulator_set_voltage_sel_regmap,
34935a93349SAdrien Grassein 	.get_voltage_sel = regulator_get_voltage_sel_regmap,
35035a93349SAdrien Grassein 	.get_current_limit = regulator_get_current_limit_regmap,
35135a93349SAdrien Grassein 	.set_current_limit = regulator_set_current_limit_regmap,
3527fefe72bSChristoph Fritz 	.set_suspend_enable = pf8x00_suspend_enable,
3537fefe72bSChristoph Fritz 	.set_suspend_disable = pf8x00_suspend_disable,
3547fefe72bSChristoph Fritz 	.set_suspend_voltage = pf8x00_set_suspend_voltage,
35535a93349SAdrien Grassein };
35635a93349SAdrien Grassein 
35735a93349SAdrien Grassein static const struct regulator_ops pf8x00_buck7_ops = {
358d3795d63SJagan Teki 	.enable = regulator_enable_regmap,
359d3795d63SJagan Teki 	.disable = regulator_disable_regmap,
360d3795d63SJagan Teki 	.is_enabled = regulator_is_enabled_regmap,
361d3795d63SJagan Teki 	.list_voltage = regulator_list_voltage_table,
3621e50433cSAxel Lin 	.map_voltage = regulator_map_voltage_ascend,
363d3795d63SJagan Teki 	.set_voltage_sel = regulator_set_voltage_sel_regmap,
364d3795d63SJagan Teki 	.get_voltage_sel = regulator_get_voltage_sel_regmap,
365245f5f65SAdrien Grassein 	.get_current_limit = regulator_get_current_limit_regmap,
366245f5f65SAdrien Grassein 	.set_current_limit = regulator_set_current_limit_regmap,
3677fefe72bSChristoph Fritz 	.set_suspend_enable = pf8x00_suspend_enable,
3687fefe72bSChristoph Fritz 	.set_suspend_disable = pf8x00_suspend_disable,
369d3795d63SJagan Teki };
370d3795d63SJagan Teki 
371d3795d63SJagan Teki static const struct regulator_ops pf8x00_vsnvs_ops = {
372d3795d63SJagan Teki 	.enable = regulator_enable_regmap,
373d3795d63SJagan Teki 	.disable = regulator_disable_regmap,
374d3795d63SJagan Teki 	.is_enabled = regulator_is_enabled_regmap,
375d3795d63SJagan Teki 	.list_voltage = regulator_list_voltage_table,
376d3795d63SJagan Teki 	.map_voltage = regulator_map_voltage_ascend,
377d3795d63SJagan Teki 	.set_voltage_sel = regulator_set_voltage_sel_regmap,
378d3795d63SJagan Teki 	.get_voltage_sel = regulator_get_voltage_sel_regmap,
379d3795d63SJagan Teki };
380d3795d63SJagan Teki 
381d3795d63SJagan Teki #define PF8X00LDO(_id, _name, base, voltages)			\
382d3795d63SJagan Teki 	[PF8X00_LDO ## _id] = {					\
383d3795d63SJagan Teki 		.desc = {					\
384d3795d63SJagan Teki 			.name = _name,				\
385d3795d63SJagan Teki 			.of_match = _name,			\
386d3795d63SJagan Teki 			.regulators_node = "regulators",	\
387d3795d63SJagan Teki 			.n_voltages = ARRAY_SIZE(voltages),	\
388d3795d63SJagan Teki 			.ops = &pf8x00_ldo_ops,			\
389d3795d63SJagan Teki 			.type = REGULATOR_VOLTAGE,		\
390d3795d63SJagan Teki 			.id = PF8X00_LDO ## _id,		\
391d3795d63SJagan Teki 			.owner = THIS_MODULE,			\
392d3795d63SJagan Teki 			.volt_table = voltages,			\
393d3795d63SJagan Teki 			.vsel_reg = (base) + LDO_RUN_VOLT,	\
394d3795d63SJagan Teki 			.vsel_mask = 0xff,			\
395d3795d63SJagan Teki 			.enable_reg = (base) + LDO_CONFIG2,	\
396d3795d63SJagan Teki 			.enable_val = 0x2,			\
397d3795d63SJagan Teki 			.disable_val = 0x0,			\
398d3795d63SJagan Teki 			.enable_mask = 2,			\
399d3795d63SJagan Teki 		},						\
4007fefe72bSChristoph Fritz 		.suspend_enable_reg = (base) + LDO_CONFIG2,	\
4017fefe72bSChristoph Fritz 		.suspend_enable_mask = 1,			\
4027fefe72bSChristoph Fritz 		.suspend_voltage_reg = (base) + LDO_STBY_VOLT,	\
403d3795d63SJagan Teki 	}
404d3795d63SJagan Teki 
405d3795d63SJagan Teki #define PF8X00BUCK(_id, _name, base, voltages)			\
406d3795d63SJagan Teki 	[PF8X00_BUCK ## _id] = {				\
407d3795d63SJagan Teki 		.desc = {					\
408d3795d63SJagan Teki 			.name = _name,				\
409d3795d63SJagan Teki 			.of_match = _name,			\
410d3795d63SJagan Teki 			.regulators_node = "regulators",	\
411d3795d63SJagan Teki 			.of_parse_cb = pf8x00_of_parse_cb,	\
41235a93349SAdrien Grassein 			.n_voltages = PF8XOO_SW1_6_VOLTAGE_NUM,	\
41335a93349SAdrien Grassein 			.ops = &pf8x00_buck1_6_ops,		\
414d3795d63SJagan Teki 			.type = REGULATOR_VOLTAGE,		\
415d3795d63SJagan Teki 			.id = PF8X00_BUCK ## _id,		\
416d3795d63SJagan Teki 			.owner = THIS_MODULE,			\
4174288b4ccSChristoph Fritz 			.ramp_delay = 19000,			\
41835a93349SAdrien Grassein 			.linear_ranges = pf8x00_sw1_to_6_voltages, \
41935a93349SAdrien Grassein 			.n_linear_ranges = \
42035a93349SAdrien Grassein 				ARRAY_SIZE(pf8x00_sw1_to_6_voltages), \
42135a93349SAdrien Grassein 			.vsel_reg = (base) + SW_RUN_VOLT,	\
42235a93349SAdrien Grassein 			.vsel_mask = 0xff,			\
42335a93349SAdrien Grassein 			.curr_table = pf8x00_sw_current_table, \
42435a93349SAdrien Grassein 			.n_current_limits = \
42535a93349SAdrien Grassein 				ARRAY_SIZE(pf8x00_sw_current_table), \
42635a93349SAdrien Grassein 			.csel_reg = (base) + SW_CONFIG2,	\
42735a93349SAdrien Grassein 			.csel_mask = PF8X00_SWXILIM_MASK,	\
42835a93349SAdrien Grassein 			.enable_reg = (base) + SW_MODE1,	\
42935a93349SAdrien Grassein 			.enable_val = 0x3,			\
43035a93349SAdrien Grassein 			.disable_val = 0x0,			\
43135a93349SAdrien Grassein 			.enable_mask = 0x3,			\
43235a93349SAdrien Grassein 			.enable_time = 500,			\
43335a93349SAdrien Grassein 		},						\
4347fefe72bSChristoph Fritz 		.suspend_enable_reg = (base) + SW_MODE1,	\
4357fefe72bSChristoph Fritz 		.suspend_enable_mask = 0xc,			\
4367fefe72bSChristoph Fritz 		.suspend_voltage_reg = (base) + SW_STBY_VOLT,	\
43735a93349SAdrien Grassein 	}
43835a93349SAdrien Grassein 
43935a93349SAdrien Grassein #define PF8X00BUCK7(_name, base, voltages)			\
44035a93349SAdrien Grassein 	[PF8X00_BUCK7] = {				\
44135a93349SAdrien Grassein 		.desc = {					\
44235a93349SAdrien Grassein 			.name = _name,				\
44335a93349SAdrien Grassein 			.of_match = _name,			\
44435a93349SAdrien Grassein 			.regulators_node = "regulators",	\
44535a93349SAdrien Grassein 			.of_parse_cb = pf8x00_of_parse_cb,	\
44635a93349SAdrien Grassein 			.n_voltages = ARRAY_SIZE(voltages),	\
44735a93349SAdrien Grassein 			.ops = &pf8x00_buck7_ops,		\
44835a93349SAdrien Grassein 			.type = REGULATOR_VOLTAGE,		\
44935a93349SAdrien Grassein 			.id = PF8X00_BUCK7,		\
45035a93349SAdrien Grassein 			.owner = THIS_MODULE,			\
4514288b4ccSChristoph Fritz 			.ramp_delay = 19000,			\
452d3795d63SJagan Teki 			.volt_table = voltages,			\
453d3795d63SJagan Teki 			.vsel_reg = (base) + SW_RUN_VOLT,	\
454d3795d63SJagan Teki 			.vsel_mask = 0xff,			\
455245f5f65SAdrien Grassein 			.curr_table = pf8x00_sw_current_table, \
456245f5f65SAdrien Grassein 			.n_current_limits = \
457245f5f65SAdrien Grassein 				ARRAY_SIZE(pf8x00_sw_current_table), \
458245f5f65SAdrien Grassein 			.csel_reg = (base) + SW_CONFIG2,	\
459245f5f65SAdrien Grassein 			.csel_mask = PF8X00_SWXILIM_MASK,	\
460d3795d63SJagan Teki 			.enable_reg = (base) + SW_MODE1,	\
461d3795d63SJagan Teki 			.enable_val = 0x3,			\
462d3795d63SJagan Teki 			.disable_val = 0x0,			\
463d3795d63SJagan Teki 			.enable_mask = 0x3,			\
464d3795d63SJagan Teki 			.enable_time = 500,			\
465d3795d63SJagan Teki 		},						\
466d3795d63SJagan Teki 	}
467d3795d63SJagan Teki 
46835a93349SAdrien Grassein 
469d3795d63SJagan Teki #define PF8X00VSNVS(_name, base, voltages)			\
470d3795d63SJagan Teki 	[PF8X00_VSNVS] = {					\
471d3795d63SJagan Teki 		.desc = {					\
472d3795d63SJagan Teki 			.name = _name,				\
473d3795d63SJagan Teki 			.of_match = _name,			\
474d3795d63SJagan Teki 			.regulators_node = "regulators",	\
475d3795d63SJagan Teki 			.n_voltages = ARRAY_SIZE(voltages),	\
476d3795d63SJagan Teki 			.ops = &pf8x00_vsnvs_ops,		\
477d3795d63SJagan Teki 			.type = REGULATOR_VOLTAGE,		\
478d3795d63SJagan Teki 			.id = PF8X00_VSNVS,			\
479d3795d63SJagan Teki 			.owner = THIS_MODULE,			\
480d3795d63SJagan Teki 			.volt_table = voltages,			\
481d3795d63SJagan Teki 			.vsel_reg = (base),			\
482d3795d63SJagan Teki 			.vsel_mask = 0x3,			\
483d3795d63SJagan Teki 		},						\
484d3795d63SJagan Teki 	}
485d3795d63SJagan Teki 
4867fefe72bSChristoph Fritz static struct pf8x00_regulator_data pf8x00_regs_data[PF8X00_MAX_REGULATORS] = {
487d3795d63SJagan Teki 	PF8X00LDO(1, "ldo1", PF8X00_LDO_BASE(PF8X00_LDO1), pf8x00_ldo_voltages),
488d3795d63SJagan Teki 	PF8X00LDO(2, "ldo2", PF8X00_LDO_BASE(PF8X00_LDO2), pf8x00_ldo_voltages),
489d3795d63SJagan Teki 	PF8X00LDO(3, "ldo3", PF8X00_LDO_BASE(PF8X00_LDO3), pf8x00_ldo_voltages),
490d3795d63SJagan Teki 	PF8X00LDO(4, "ldo4", PF8X00_LDO_BASE(PF8X00_LDO4), pf8x00_ldo_voltages),
491d3795d63SJagan Teki 	PF8X00BUCK(1, "buck1", PF8X00_SW_BASE(PF8X00_BUCK1), pf8x00_sw1_to_6_voltages),
492d3795d63SJagan Teki 	PF8X00BUCK(2, "buck2", PF8X00_SW_BASE(PF8X00_BUCK2), pf8x00_sw1_to_6_voltages),
493d3795d63SJagan Teki 	PF8X00BUCK(3, "buck3", PF8X00_SW_BASE(PF8X00_BUCK3), pf8x00_sw1_to_6_voltages),
494d3795d63SJagan Teki 	PF8X00BUCK(4, "buck4", PF8X00_SW_BASE(PF8X00_BUCK4), pf8x00_sw1_to_6_voltages),
495d3795d63SJagan Teki 	PF8X00BUCK(5, "buck5", PF8X00_SW_BASE(PF8X00_BUCK5), pf8x00_sw1_to_6_voltages),
496d3795d63SJagan Teki 	PF8X00BUCK(6, "buck6", PF8X00_SW_BASE(PF8X00_BUCK6), pf8x00_sw1_to_6_voltages),
49735a93349SAdrien Grassein 	PF8X00BUCK7("buck7", PF8X00_SW_BASE(PF8X00_BUCK7), pf8x00_sw7_voltages),
498d3795d63SJagan Teki 	PF8X00VSNVS("vsnvs", PF8X00_VSNVS_CONFIG1, pf8x00_vsnvs_voltages),
499d3795d63SJagan Teki };
500d3795d63SJagan Teki 
pf8x00_identify(struct pf8x00_chip * chip)501d3795d63SJagan Teki static int pf8x00_identify(struct pf8x00_chip *chip)
502d3795d63SJagan Teki {
503d3795d63SJagan Teki 	unsigned int value;
504d3795d63SJagan Teki 	u8 dev_fam, dev_id;
505d3795d63SJagan Teki 	const char *name = NULL;
506d3795d63SJagan Teki 	int ret;
507d3795d63SJagan Teki 
508d3795d63SJagan Teki 	ret = regmap_read(chip->regmap, PF8X00_DEVICEID, &value);
509d3795d63SJagan Teki 	if (ret) {
510d3795d63SJagan Teki 		dev_err(chip->dev, "failed to read chip family\n");
511d3795d63SJagan Teki 		return ret;
512d3795d63SJagan Teki 	}
513d3795d63SJagan Teki 
514d3795d63SJagan Teki 	dev_fam = value & PF8X00_DEVICE_FAM_MASK;
515d3795d63SJagan Teki 	switch (dev_fam) {
516d3795d63SJagan Teki 	case PF8X00_FAM:
517d3795d63SJagan Teki 		break;
518d3795d63SJagan Teki 	default:
519d3795d63SJagan Teki 		dev_err(chip->dev,
520d3795d63SJagan Teki 			"Chip 0x%x is not from PF8X00 family\n", dev_fam);
521d3795d63SJagan Teki 		return ret;
522d3795d63SJagan Teki 	}
523d3795d63SJagan Teki 
524d3795d63SJagan Teki 	dev_id = value & PF8X00_DEVICE_ID_MASK;
525d3795d63SJagan Teki 	switch (dev_id) {
526d3795d63SJagan Teki 	case PF8100:
527d3795d63SJagan Teki 		name = "PF8100";
528d3795d63SJagan Teki 		break;
529d3795d63SJagan Teki 	case PF8121A:
530d3795d63SJagan Teki 		name = "PF8121A";
531d3795d63SJagan Teki 		break;
532d3795d63SJagan Teki 	case PF8200:
53364f09ea1SAxel Lin 		name = "PF8200";
534d3795d63SJagan Teki 		break;
535d3795d63SJagan Teki 	default:
536d3795d63SJagan Teki 		dev_err(chip->dev, "Unknown pf8x00 device id 0x%x\n", dev_id);
537d3795d63SJagan Teki 		return -ENODEV;
538d3795d63SJagan Teki 	}
539d3795d63SJagan Teki 
540d3795d63SJagan Teki 	dev_info(chip->dev, "%s PMIC found.\n", name);
541d3795d63SJagan Teki 
542d3795d63SJagan Teki 	return 0;
543d3795d63SJagan Teki }
544d3795d63SJagan Teki 
pf8x00_i2c_probe(struct i2c_client * client)545d3795d63SJagan Teki static int pf8x00_i2c_probe(struct i2c_client *client)
546d3795d63SJagan Teki {
547d3795d63SJagan Teki 	struct regulator_config config = { NULL, };
548d3795d63SJagan Teki 	struct pf8x00_chip *chip;
549d3795d63SJagan Teki 	int id;
550d3795d63SJagan Teki 	int ret;
551d3795d63SJagan Teki 
552d3795d63SJagan Teki 	chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
553d3795d63SJagan Teki 	if (!chip)
554d3795d63SJagan Teki 		return -ENOMEM;
555d3795d63SJagan Teki 
556d3795d63SJagan Teki 	i2c_set_clientdata(client, chip);
557d3795d63SJagan Teki 	chip->dev = &client->dev;
558d3795d63SJagan Teki 
559d3795d63SJagan Teki 	chip->regmap = devm_regmap_init_i2c(client, &pf8x00_regmap_config);
560d3795d63SJagan Teki 	if (IS_ERR(chip->regmap)) {
561d3795d63SJagan Teki 		ret = PTR_ERR(chip->regmap);
562d3795d63SJagan Teki 		dev_err(&client->dev,
563d3795d63SJagan Teki 			"regmap allocation failed with err %d\n", ret);
564d3795d63SJagan Teki 		return ret;
565d3795d63SJagan Teki 	}
566d3795d63SJagan Teki 
567d3795d63SJagan Teki 	ret = pf8x00_identify(chip);
568d3795d63SJagan Teki 	if (ret)
569d3795d63SJagan Teki 		return ret;
570d3795d63SJagan Teki 
5717fefe72bSChristoph Fritz 	for (id = 0; id < ARRAY_SIZE(pf8x00_regs_data); id++) {
5727fefe72bSChristoph Fritz 		struct pf8x00_regulator_data *data = &pf8x00_regs_data[id];
573d3795d63SJagan Teki 		struct regulator_dev *rdev;
574d3795d63SJagan Teki 
575d3795d63SJagan Teki 		config.dev = chip->dev;
5767fefe72bSChristoph Fritz 		config.driver_data = data;
577d3795d63SJagan Teki 		config.regmap = chip->regmap;
578d3795d63SJagan Teki 
579d3795d63SJagan Teki 		rdev = devm_regulator_register(&client->dev, &data->desc, &config);
580d3795d63SJagan Teki 		if (IS_ERR(rdev)) {
581d3795d63SJagan Teki 			dev_err(&client->dev,
582d3795d63SJagan Teki 				"failed to register %s regulator\n", data->desc.name);
583d3795d63SJagan Teki 			return PTR_ERR(rdev);
584d3795d63SJagan Teki 		}
585d3795d63SJagan Teki 	}
586d3795d63SJagan Teki 
587d3795d63SJagan Teki 	return 0;
588d3795d63SJagan Teki }
589d3795d63SJagan Teki 
590d3795d63SJagan Teki static const struct of_device_id pf8x00_dt_ids[] = {
591df9716ecSMark Brown 	{ .compatible = "nxp,pf8100",},
592df9716ecSMark Brown 	{ .compatible = "nxp,pf8121a",},
593df9716ecSMark Brown 	{ .compatible = "nxp,pf8200",},
594d3795d63SJagan Teki 	{ }
595d3795d63SJagan Teki };
596d3795d63SJagan Teki MODULE_DEVICE_TABLE(of, pf8x00_dt_ids);
597d3795d63SJagan Teki 
598d3795d63SJagan Teki static const struct i2c_device_id pf8x00_i2c_id[] = {
599df9716ecSMark Brown 	{ "pf8100", 0 },
600df9716ecSMark Brown 	{ "pf8121a", 0 },
601df9716ecSMark Brown 	{ "pf8200", 0 },
602d3795d63SJagan Teki 	{},
603d3795d63SJagan Teki };
604d3795d63SJagan Teki MODULE_DEVICE_TABLE(i2c, pf8x00_i2c_id);
605d3795d63SJagan Teki 
606d3795d63SJagan Teki static struct i2c_driver pf8x00_regulator_driver = {
607d3795d63SJagan Teki 	.id_table = pf8x00_i2c_id,
608d3795d63SJagan Teki 	.driver = {
609d3795d63SJagan Teki 		.name = "pf8x00",
61046600ab1SDouglas Anderson 		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
611d3795d63SJagan Teki 		.of_match_table = pf8x00_dt_ids,
612d3795d63SJagan Teki 	},
613*964e1865SUwe Kleine-König 	.probe = pf8x00_i2c_probe,
614d3795d63SJagan Teki };
615d3795d63SJagan Teki module_i2c_driver(pf8x00_regulator_driver);
616d3795d63SJagan Teki 
617d3795d63SJagan Teki MODULE_AUTHOR("Jagan Teki <jagan@amarulasolutions.com>");
618d3795d63SJagan Teki MODULE_AUTHOR("Troy Kisky <troy.kisky@boundarydevices.com>");
619d3795d63SJagan Teki MODULE_DESCRIPTION("Regulator Driver for NXP's PF8100/PF8121A/PF8200 PMIC");
620d3795d63SJagan Teki MODULE_LICENSE("GPL v2");
621