1// SPDX-License-Identifier: GPL-2.0+
2
3// documentation of this device is available at
4// https://www.nxp.com/docs/en/data-sheet/PF5300.pdf
5
6#include <linux/delay.h>
7#include <linux/err.h>
8#include <linux/gpio/consumer.h>
9#include <linux/i2c.h>
10#include <linux/module.h>
11#include <linux/regmap.h>
12#include <linux/regulator/driver.h>
13#include <linux/regulator/consumer.h>
14#include <linux/regulator/machine.h>
15#include <linux/regulator/of_regulator.h>
16
17/* registers */
18#define PF530X_DEVICEID 0x00
19#define PF530X_REV 0x01
20#define PF530X_EMREV 0x02
21#define PF530X_PROGID 0x03
22#define PF530X_CONFIG1 0x04
23#define PF530X_INT_STATUS1 0x05
24#define PF530X_INT_SENSE1 0x06
25#define PF530X_INT_STATUS2 0x07
26#define PF530X_INT_SENSE2 0x08
27#define PF530X_BIST_STAT1 0x09
28#define PF530X_BIST_CTRL 0x0a
29#define PF530X_STATE 0x0b
30#define PF530X_STATE_CTRL 0x0c
31#define PF530X_SW1_VOLT 0x0d
32#define PF530X_SW1_STBY_VOLT 0x0e
33#define PF530X_SW1_CTRL1 0x0f
34#define PF530X_SW1_CTRL2 0x10
35#define PF530X_CLK_CTRL 0x11
36#define PF530X_SEQ_CTRL1 0x12
37#define PF530X_SEQ_CTRL2 0x13
38#define PF530X_RANDOM_CHK 0x14
39#define PF530X_RANDOM_GEN 0x15
40#define PF530X_WD_CTRL1 0x16
41#define PF530X_WD_SEED 0x17
42#define PF530X_WD_ANSWER 0x18
43#define PF530X_FLT_CNT1 0x19
44#define PF530X_FLT_CNT2 0x1a
45#define PF530X_OTP_MODE 0x2f
46
47enum pf530x_states {
48 PF530X_STATE_POF,
49 PF530X_STATE_FUSE_LOAD,
50 PF530X_STATE_LP_OFF,
51 PF530X_STATE_SELF_TEST,
52 PF530X_STATE_POWER_UP,
53 PF530X_STATE_INIT,
54 PF530X_STATE_IO_RELEASE,
55 PF530X_STATE_RUN,
56 PF530X_STATE_STANDBY,
57 PF530X_STATE_FAULT,
58 PF530X_STATE_FAILSAFE,
59 PF530X_STATE_POWER_DOWN,
60 PF530X_STATE_2MS_SELFTEST_RETRY,
61 PF530X_STATE_OFF_DLY,
62};
63
64#define PF530_FAM 0x50
65enum pf530x_devid {
66 PF5300 = 0x3,
67 PF5301 = 0x4,
68 PF5302 = 0x5,
69};
70
71#define PF530x_FAM 0x50
72#define PF530x_DEVICE_FAM_MASK GENMASK(7, 4)
73#define PF530x_DEVICE_ID_MASK GENMASK(3, 0)
74
75#define PF530x_STATE_MASK GENMASK(3, 0)
76#define PF530x_STATE_RUN 0x07
77#define PF530x_STATE_STANDBY 0x08
78#define PF530x_STATE_LP_OFF 0x02
79
80#define PF530X_OTP_STBY_MODE GENMASK(3, 2)
81#define PF530X_OTP_RUN_MODE GENMASK(1, 0)
82
83#define PF530X_INT_STATUS_OV BIT(1)
84#define PF530X_INT_STATUS_UV BIT(2)
85#define PF530X_INT_STATUS_ILIM BIT(3)
86
87#define SW1_ILIM_S BIT(0)
88#define VMON_UV_S BIT(1)
89#define VMON_OV_S BIT(2)
90#define VIN_OVLO_S BIT(3)
91#define BG_ERR_S BIT(6)
92
93#define THERM_155_S BIT(3)
94#define THERM_140_S BIT(2)
95#define THERM_125_S BIT(1)
96#define THERM_110_S BIT(0)
97
98struct pf530x_chip {
99 struct regmap *regmap;
100 struct device *dev;
101};
102
103static const struct regmap_config pf530x_regmap_config = {
104 .reg_bits = 8,
105 .val_bits = 8,
106 .max_register = PF530X_OTP_MODE,
107 .cache_type = REGCACHE_MAPLE,
108};
109
110static int pf530x_get_status(struct regulator_dev *rdev)
111{
112 unsigned int state;
113 int ret;
114
115 ret = regmap_read(map: rdev->regmap, PF530X_INT_SENSE1, val: &state);
116 if (ret != 0)
117 return ret;
118
119 if ((state & (BG_ERR_S | SW1_ILIM_S | VMON_UV_S | VMON_OV_S | VIN_OVLO_S))
120 != 0)
121 return REGULATOR_STATUS_ERROR;
122
123 // no errors, check if what non-error state we're in
124 ret = regmap_read(map: rdev->regmap, PF530X_STATE, val: &state);
125 if (ret != 0)
126 return ret;
127
128 state &= PF530x_STATE_MASK;
129
130 switch (state) {
131 case PF530x_STATE_RUN:
132 ret = REGULATOR_STATUS_NORMAL;
133 break;
134 case PF530x_STATE_STANDBY:
135 ret = REGULATOR_STATUS_STANDBY;
136 break;
137 case PF530x_STATE_LP_OFF:
138 ret = REGULATOR_STATUS_OFF;
139 break;
140 default:
141 ret = REGULATOR_STATUS_ERROR;
142 break;
143 }
144 return ret;
145}
146
147static int pf530x_get_error_flags(struct regulator_dev *rdev, unsigned int *flags)
148{
149 unsigned int status;
150 int ret;
151
152 ret = regmap_read(map: rdev->regmap, PF530X_INT_STATUS1, val: &status);
153
154 if (ret != 0)
155 return ret;
156
157 *flags = 0;
158
159 if (status & PF530X_INT_STATUS_OV)
160 *flags |= REGULATOR_ERROR_OVER_VOLTAGE_WARN;
161
162 if (status & PF530X_INT_STATUS_UV)
163 *flags |= REGULATOR_ERROR_UNDER_VOLTAGE;
164
165 if (status & PF530X_INT_STATUS_ILIM)
166 *flags |= REGULATOR_ERROR_OVER_CURRENT;
167
168 ret = regmap_read(map: rdev->regmap, PF530X_INT_SENSE2, val: &status);
169
170 if (ret != 0)
171 return ret;
172
173 if ((status & (THERM_155_S |
174 THERM_140_S |
175 THERM_125_S |
176 THERM_110_S)) != 0)
177 *flags |= REGULATOR_ERROR_OVER_TEMP_WARN;
178
179 return 0;
180}
181
182static const struct regulator_ops pf530x_regulator_ops = {
183 .enable = regulator_enable_regmap,
184 .disable = regulator_disable_regmap,
185 .is_enabled = regulator_is_enabled_regmap,
186 .map_voltage = regulator_map_voltage_linear_range,
187 .list_voltage = regulator_list_voltage_linear_range,
188 .set_voltage_sel = regulator_set_voltage_sel_regmap,
189 .get_voltage_sel = regulator_get_voltage_sel_regmap,
190 .get_status = pf530x_get_status,
191 .get_error_flags = pf530x_get_error_flags,
192 .set_bypass = regulator_set_bypass_regmap,
193 .get_bypass = regulator_get_bypass_regmap,
194};
195
196static const struct linear_range vrange = REGULATOR_LINEAR_RANGE(500000, 0, 140, 5000);
197
198static const struct regulator_desc pf530x_reg_desc = {
199 .name = "SW1",
200 .ops = &pf530x_regulator_ops,
201 .linear_ranges = &vrange,
202 .n_linear_ranges = 1,
203 .type = REGULATOR_VOLTAGE,
204 .id = 0,
205 .owner = THIS_MODULE,
206 .vsel_reg = PF530X_SW1_VOLT,
207 .vsel_mask = 0xFF,
208 .bypass_reg = PF530X_SW1_CTRL2,
209 .bypass_mask = 0x07,
210 .bypass_val_on = 0x07,
211 .bypass_val_off = 0x00,
212 .enable_reg = PF530X_SW1_CTRL1,
213 .enable_mask = GENMASK(5, 2),
214 .enable_val = GENMASK(5, 2),
215 .disable_val = 0,
216};
217
218static int pf530x_identify(struct pf530x_chip *chip)
219{
220 unsigned int value;
221 u8 dev_fam, dev_id, full_layer_rev, metal_layer_rev, prog_idh, prog_idl, emrev;
222 const char *name = NULL;
223 int ret;
224
225 ret = regmap_read(map: chip->regmap, PF530X_DEVICEID, val: &value);
226 if (ret) {
227 dev_err(chip->dev, "failed to read chip family\n");
228 return ret;
229 }
230
231 dev_fam = value & PF530x_DEVICE_FAM_MASK;
232 switch (dev_fam) {
233 case PF530x_FAM:
234 break;
235 default:
236 dev_err(chip->dev,
237 "Chip 0x%x is not from PF530X family\n", dev_fam);
238 return ret;
239 }
240
241 dev_id = value & PF530x_DEVICE_ID_MASK;
242 switch (dev_id) {
243 case PF5300:
244 name = "PF5300";
245 break;
246 case PF5301:
247 name = "PF5301";
248 break;
249 case PF5302:
250 name = "PF5302";
251 break;
252 default:
253 dev_err(chip->dev, "Unknown pf530x device id 0x%x\n", dev_id);
254 return -ENODEV;
255 }
256
257 ret = regmap_read(map: chip->regmap, PF530X_REV, val: &value);
258 if (ret) {
259 dev_err(chip->dev, "failed to read chip rev\n");
260 return ret;
261 }
262
263 full_layer_rev = ((value & 0xF0) == 0) ? '0' : ((((value & 0xF0) >> 4) - 1) + 'A');
264 metal_layer_rev = value & 0xF;
265
266 ret = regmap_read(map: chip->regmap, PF530X_EMREV, val: &value);
267 if (ret) {
268 dev_err(chip->dev, "failed to read chip emrev register\n");
269 return ret;
270 }
271
272 prog_idh = (value >> 4) + 'A';
273 // prog_idh skips 'O', per page 96 of the datasheet
274 if (prog_idh >= 'O')
275 prog_idh += 1;
276
277 emrev = value & 0x7;
278
279 ret = regmap_read(map: chip->regmap, PF530X_PROGID, val: &value);
280 if (ret) {
281 dev_err(chip->dev, "failed to read chip progid register\n");
282 return ret;
283 }
284
285 if (value >= 0x22) {
286 dev_err(chip->dev, "invalid value for progid register\n");
287 return -ENODEV;
288 } else if (value < 10) {
289 prog_idl = value + '0';
290 } else {
291 prog_idl = (value - 10) + 'A';
292 // prog_idh skips 'O', per page 97 of the datasheet
293 if (prog_idl >= 'O')
294 prog_idl += 1;
295 }
296
297 dev_info(chip->dev, "%s Regulator found (Rev %c%d ProgID %c%c EMREV %x).\n",
298 name, full_layer_rev, metal_layer_rev, prog_idh, prog_idl, emrev);
299
300 return 0;
301}
302
303static int pf530x_i2c_probe(struct i2c_client *client)
304{
305 struct regulator_config config = { NULL, };
306 struct pf530x_chip *chip;
307 int ret;
308 struct regulator_dev *rdev;
309 struct regulator_init_data *init_data;
310
311 chip = devm_kzalloc(dev: &client->dev, size: sizeof(*chip), GFP_KERNEL);
312 if (!chip)
313 return -ENOMEM;
314
315 i2c_set_clientdata(client, data: chip);
316 chip->dev = &client->dev;
317
318 chip->regmap = devm_regmap_init_i2c(client, &pf530x_regmap_config);
319 if (IS_ERR(ptr: chip->regmap)) {
320 ret = PTR_ERR(ptr: chip->regmap);
321 dev_err(&client->dev,
322 "regmap allocation failed with err %d\n", ret);
323 return ret;
324 }
325
326 ret = pf530x_identify(chip);
327 if (ret)
328 return ret;
329
330 init_data = of_get_regulator_init_data(dev: chip->dev, node: chip->dev->of_node, desc: &pf530x_reg_desc);
331 if (!init_data)
332 return -ENODATA;
333
334 config.dev = chip->dev;
335 config.of_node = chip->dev->of_node;
336 config.regmap = chip->regmap;
337 config.init_data = init_data;
338
339 // the config parameter gets copied, it's ok to pass a pointer on the stack here
340 rdev = devm_regulator_register(dev: &client->dev, regulator_desc: &pf530x_reg_desc, config: &config);
341 if (IS_ERR(ptr: rdev)) {
342 dev_err(&client->dev, "failed to register %s regulator\n", pf530x_reg_desc.name);
343 return PTR_ERR(ptr: rdev);
344 }
345
346 return 0;
347}
348
349static const struct of_device_id pf530x_dt_ids[] = {
350 { .compatible = "nxp,pf5300",},
351 { }
352};
353MODULE_DEVICE_TABLE(of, pf530x_dt_ids);
354
355static const struct i2c_device_id pf530x_i2c_id[] = {
356 { "pf5300", 0 },
357 { "pf5301", 0 },
358 { "pf5302", 0 },
359 {},
360};
361MODULE_DEVICE_TABLE(i2c, pf530x_i2c_id);
362
363static struct i2c_driver pf530x_regulator_driver = {
364 .id_table = pf530x_i2c_id,
365 .driver = {
366 .name = "pf530x",
367 .of_match_table = pf530x_dt_ids,
368 },
369 .probe = pf530x_i2c_probe,
370};
371module_i2c_driver(pf530x_regulator_driver);
372
373MODULE_AUTHOR("Woodrow Douglass <wdouglass@carnegierobotics.com>");
374MODULE_DESCRIPTION("Regulator Driver for NXP's PF5300/PF5301/PF5302 PMIC");
375MODULE_LICENSE("GPL");
376

source code of linux/drivers/regulator/pf530x-regulator.c