blob: 3373b3f1072dd2dee76d000cfd6881242918893b [file] [log] [blame]
b.liue9582032025-04-17 19:18:16 +08001/*
2 * Regulators driver for Marvell 88PM800
3 *
4 * Copyright (C) 2012 Marvell International Ltd.
5 * Joseph(Yossi) Hanin <yhanin@marvell.com>
6 * Yi Zhang <yizhang@marvell.com>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 */
12#include <linux/module.h>
13#include <linux/moduleparam.h>
14#include <linux/init.h>
15#include <linux/err.h>
16#include <linux/regmap.h>
17#include <linux/regulator/driver.h>
18#include <linux/regulator/machine.h>
19#include <linux/mfd/88pm80x.h>
20#include <linux/delay.h>
21#include <linux/io.h>
22#include <linux/of.h>
23#include <linux/platform_device.h>
24#include <linux/uaccess.h>
25#include <linux/string.h>
26#include <linux/ctype.h>
27
28/* BUCK1/4 with DVC[0..3] */
29#define PM8XX_BUCK1 (0x3C)
30#define PM8XX_BUCK3 (0x41)
31#define PM802_BUCK1 (0x29)
32#define PM802S_BUCK1 (0x2B)
33#define PM813_BUCK1 (0x2a)
34#define PM803_BUCK1 (0x2B)
35
36#define PM826_BUCK1 (0x16)
37
38#define PM8XX_WAKEUP1 (0x0D)
39#define PM8XX_DVC_EN (1 << 7)
40
41#define BUCK_MIN 600000
42#define BUCK_MAX 1587500
43#define BUCK_STEP 12500
44#define BUCK_MAX_DVC_LVL 4
45#define DVC_CONTROL_REG 0x4F
46#define DVC_SET_ADDR1 (1 << 0)
47#define DVC_SET_ADDR2 (1 << 1)
48
49#define PM826_BUCK_MIN 480000
50#define PM826_BUCK_STEP 10000
51
52struct pm8xx_dvc {
53 struct platform_device *pdev;
54 struct regmap *powermap;
55 struct regmap *basemap;
56 /* to support multi pmic, they have different affected buck */
57 u32 affectedbuck;
58};
59
60static struct pm80x_chip *pm8xx_dvc_gchip;
61static struct pm8xx_dvc *pm8xx_dvcdata;
62
63static u32 cached_dvc_val[BUCK_MAX_DVC_LVL];
64
65static inline int volt_to_reg(int uv)
66{
67#ifdef CONFIG_MFD_PM826
68 if (pm8xx_dvc_gchip && CHIP_PM826 == pm8xx_dvc_gchip->type)
69 return (uv - PM826_BUCK_MIN) / PM826_BUCK_STEP;
70 else
71#endif
72 return (uv - BUCK_MIN) / BUCK_STEP;
73}
74
75static inline int reg_to_volt(int regval)
76{
77#ifdef CONFIG_MFD_PM826
78 if (pm8xx_dvc_gchip && CHIP_PM826 == pm8xx_dvc_gchip->type)
79 return regval * PM826_BUCK_STEP + PM826_BUCK_MIN;
80 else
81#endif
82 return regval * BUCK_STEP + BUCK_MIN;
83}
84
85static int getchip_id(void)
86{
87 int val, chip_id, ret = 0;
88
89 ret = regmap_read(pm8xx_dvcdata->basemap, 0x0, &val);
90 chip_id = val & 0xff;
91
92 return chip_id;
93}
94
95int set_dvc_control_register(unsigned int lvl)
96{
97 int control_lvl, ret;
98 struct regmap *regmap = pm8xx_dvcdata->powermap;
99
100 control_lvl = lvl / 4;
101 ret = regmap_update_bits(regmap, DVC_CONTROL_REG,
102 DVC_SET_ADDR1 | DVC_SET_ADDR2, control_lvl);
103 if (ret < 0)
104 return ret;
105
106 return 0;
107}
108
109/*
110 * Example for usage: set buck1 vl1 as 1200mV
111 * pm8xx_dvc_setvolt(PM800_ID_BUCK1, 1, 1200 * mV2uV);
112 */
113int pm8xx_dvc_setvolt(unsigned int buckid, unsigned int lvl, int uv)
114{
115 struct platform_device *pdev;
116 struct regmap *regmap;
117 int ret = 0, idx;
118 u32 affectbuckbits;
119 u32 regbase = PM8XX_BUCK1;
120 int chip_id;
121
122 if (unlikely(!pm8xx_dvcdata)) {
123 cached_dvc_val[lvl] = uv;
124 pr_err("error: pm8xx_dvcdata not initialized, lvl%d: %dmv\n", lvl, uv);
125 return 0;
126 }
127
128 pdev = pm8xx_dvcdata->pdev;
129 regmap = pm8xx_dvcdata->powermap;
130 affectbuckbits = pm8xx_dvcdata->affectedbuck;
131
132 pdev = pm8xx_dvcdata->pdev;
133 regmap = pm8xx_dvcdata->powermap;
134 affectbuckbits = pm8xx_dvcdata->affectedbuck;
135
136 /* buck1 start at 0 */
137 if (!(affectbuckbits & (1 << buckid))) {
138 dev_err(&pdev->dev, "unsupported buck%d in DVC\n", buckid);
139 return -EINVAL;
140 }
141
142 if (uv < BUCK_MIN || uv > BUCK_MAX) {
143 dev_err(&pdev->dev, "Failed to allocate pm8xx_dvcdata\n");
144 return -EINVAL;
145 }
146 /*check chip id, 88pm860 support more dvc level than other pmic chip*/
147 chip_id = getchip_id();
148#ifdef CONFIG_MFD_PM802
149 if (chip_id >= CHIP_PM802_ID_A0 && chip_id < CHIP_PM803_ID)
150 regbase = PM802_BUCK1;
151 else if (CHIP_PM802S_ID_A0 == chip_id
152 || CHIP_PM802S_ID_A1 == chip_id
153 || CHIP_PM802S_ID_A2 == chip_id)
154 regbase = PM802S_BUCK1;
155#endif
156
157#ifdef CONFIG_MFD_PM803
158 if (chip_id == CHIP_PM803_ID)
159 regbase = PM803_BUCK1;
160#endif
161
162#ifdef CONFIG_MFD_PM813
163 if (chip_id == CHIP_PM813_ID || chip_id == CHIP_PM813S_A1_ID
164 || chip_id == CHIP_PM813S_A0_ID)
165 regbase = PM813_BUCK1;
166#endif
167
168#ifdef CONFIG_MFD_PM826
169 if (CHIP_PM826 == pm8xx_dvc_gchip->type)
170 regbase = PM826_BUCK1;
171#endif
172
173 if (lvl >= BUCK_MAX_DVC_LVL) {
174 dev_err(&pdev->dev, "DVC lvl: %d out of range\n", lvl);
175 BUG();
176 }
177
178 for (idx = 0; idx < buckid; idx++) {
179 if ((affectbuckbits >> idx) & 0x1)
180 regbase += BUCK_MAX_DVC_LVL;
181 else
182 regbase += 1;
183 }
184 ret = regmap_write(regmap, regbase + lvl, volt_to_reg(uv));
185 return ret;
186};
187EXPORT_SYMBOL(pm8xx_dvc_setvolt);
188
189int pm8xx_dvc_getvolt(unsigned int buckid, unsigned int lvl, int *uv)
190{
191 struct platform_device *pdev;
192 struct regmap *regmap;
193 int ret = 0, regval = 0, idx;
194 u32 affectbuckbits;
195 u32 regbase = PM8XX_BUCK1;
196 int chip_id;
197
198 if (unlikely(!pm8xx_dvcdata)) {
199 *uv = cached_dvc_val[lvl];
200 pr_err("error: pm8xx_dvcdata not inited, lvl%d: %dmv\n", lvl, *uv);
201 return 0;
202 }
203
204 pdev = pm8xx_dvcdata->pdev;
205 regmap = pm8xx_dvcdata->powermap;
206 affectbuckbits = pm8xx_dvcdata->affectedbuck;
207
208 *uv = 0;
209 if (!(affectbuckbits & (1 << buckid))) {
210 dev_err(&pdev->dev, "unsupported buck%d in DVC\n", buckid);
211 return -EINVAL;
212 }
213
214 /* check chip id, 88pm860 support more dvc level than other pmic chip */
215 chip_id = getchip_id();
216
217#ifdef CONFIG_MFD_PM802
218 if (chip_id >= CHIP_PM802_ID_A0 && chip_id < CHIP_PM803_ID)
219 regbase = PM802_BUCK1;
220 else if (CHIP_PM802S_ID_A0 == chip_id
221 || CHIP_PM802S_ID_A1 == chip_id
222 || CHIP_PM802S_ID_A2 == chip_id)
223 regbase = PM802S_BUCK1;
224#endif
225
226#ifdef CONFIG_MFD_PM803
227 if (chip_id == CHIP_PM803_ID)
228 regbase = PM803_BUCK1;
229#endif
230
231#ifdef CONFIG_MFD_PM813
232 if (chip_id == CHIP_PM813_ID || chip_id == CHIP_PM813S_A1_ID
233 || chip_id == CHIP_PM813S_A0_ID)
234 regbase = PM813_BUCK1;
235#endif
236
237#ifdef CONFIG_MFD_PM826
238 if (CHIP_PM826 == pm8xx_dvc_gchip->type)
239 regbase = PM826_BUCK1;
240#endif
241
242 if (lvl >= BUCK_MAX_DVC_LVL) {
243 dev_err(&pdev->dev, "DVC lvl: %d out of range\n", lvl);
244 BUG();
245 }
246
247 for (idx = 0; idx < buckid; idx++) {
248 if ((affectbuckbits >> idx) & 0x1)
249 regbase += BUCK_MAX_DVC_LVL;
250 else
251 regbase += 1;
252 }
253 ret = regmap_read(regmap, regbase + lvl, &regval);
254 if (!ret)
255 *uv = reg_to_volt(regval);
256 return ret;
257}
258EXPORT_SYMBOL(pm8xx_dvc_getvolt);
259
260/*
261 * buck1_info usage:
262 * 1. read buck1 voltage
263 * >> echo +[level] > /sys/kernel/debug/buck1_info
264 * >> cat /sys/kernel/debug/buck1_info
265 * 2. set buck1 voltage
266 * >> echo +[level] [voltage] > /sys/kernel/debug/buck1_info
267 */
268static int buck1_dvc_lvl;
269static ssize_t pm8xx_buck1_info_read(struct file *file, char __user *user_buf,
270 size_t count, loff_t *ppos)
271{
272 int ret, uv, len = 0;
273 char str[100];
274
275 if (buck1_dvc_lvl < 0 || buck1_dvc_lvl > 16) {
276 pr_err("dvc level error\n");
277 return -EINVAL;
278 }
279
280 ret = pm8xx_dvc_getvolt(PM800_ID_BUCK1, buck1_dvc_lvl, &uv);
281
282 if (ret < 0) {
283 pr_err("get buck1 dvc level %d error\n", buck1_dvc_lvl);
284 return -EINVAL;
285 } else
286 len = snprintf(str, sizeof(str),
287 "buck 1, level %d, voltage %uV\n", buck1_dvc_lvl, uv);
288
289 return simple_read_from_buffer(user_buf, count, ppos, str, len);
290}
291
292static ssize_t pm8xx_buck1_info_write(struct file *file, const char __user *user_buf,
293 size_t count, loff_t *ppos)
294{
295 int ret, volt;
296
297 ret = sscanf(user_buf, "+%d %d\n", &buck1_dvc_lvl, &volt);
298 if (ret < 1) {
299 pr_err("Right format: +[level] [voltage]\n");
300 return -EINVAL;
301 }
302
303 if (ret == 2)
304 pm8xx_dvc_setvolt(PM800_ID_BUCK1, buck1_dvc_lvl, volt);
305
306 pm8xx_dvc_getvolt(PM800_ID_BUCK1, buck1_dvc_lvl, &volt);
307 pr_info("buck 1, level %d, voltage %duV\n", buck1_dvc_lvl, volt);
308
309 return count;
310}
311
312static const struct file_operations pm8xx_buck1_info_ops = {
313 .owner = THIS_MODULE,
314 .open = simple_open,
315 .read = pm8xx_buck1_info_read,
316 .write = pm8xx_buck1_info_write,
317};
318
319static int pm8xx_dvc_probe(struct platform_device *pdev)
320{
321 struct pm80x_chip *chip = dev_get_drvdata(pdev->dev.parent);
322 struct pm8xx_dvc *dvcdata;
323 struct dentry *pm8xx_dvc_buck1;
324 int ret = 0, val = 0;
325
326#ifdef CONFIG_CPU_ASR1901
327 if (CHIP_PM826 != chip->type) {
328 dev_err(&pdev->dev, "asr190x doesn't support none-pm826 dvc\n");
329 return 0;
330 }
331#endif
332 if (pm8xx_dvcdata)
333 return 0;
334
335 dvcdata = devm_kzalloc(&pdev->dev, sizeof(*pm8xx_dvcdata), GFP_KERNEL);
336 if (!dvcdata) {
337 dev_err(&pdev->dev, "Failed to allocate pm8xx_dvcdata");
338 return -ENOMEM;
339 }
340
341 dvcdata->basemap = chip->regmap;
342 dvcdata->powermap = chip->subchip->regmap_power;
343 dvcdata->pdev = pdev;
344 platform_set_drvdata(pdev, dvcdata);
345 pm8xx_dvcdata = dvcdata;
346
347 /* get pmic affected buck 32bit */
348 if (IS_ENABLED(CONFIG_OF)) {
349 if (of_property_read_u32(pdev->dev.of_node,
350 "dvc-affected-buckbits",
351 &dvcdata->affectedbuck))
352 dev_dbg(&pdev->dev, "Failed to get affectedbuckbits\n");
353 } else {
354 /* by default only enable buck1 */
355 dvcdata->affectedbuck = 0x1;
356 }
357
358 /* enable PMIC dvc feature */
359 ret = regmap_update_bits(chip->regmap, PM8XX_WAKEUP1,
360 PM8XX_DVC_EN, PM8XX_DVC_EN);
361 if (ret < 0) {
362 dev_err(&pdev->dev, "Failed to enable pmic dvc feature!\n");
363 devm_kfree(&pdev->dev, dvcdata);
364 return ret;
365 }
366
367 regmap_read(chip->regmap, PM8XX_WAKEUP1, &val);
368 dev_info(&pdev->dev, "DVC power hold enabled %x! DVC buck %x\n",
369 val, dvcdata->affectedbuck);
370
371 pm8xx_dvc_buck1 = debugfs_create_file("buck1_info", S_IRUGO | S_IFREG,
372 NULL, NULL, &pm8xx_buck1_info_ops);
373 if (!pm8xx_dvc_buck1) {
374 pr_err("create buck1_info debugfs error!\n");
375 return -ENOENT;
376 } else if (pm8xx_dvc_buck1 == ERR_PTR(-ENODEV)) {
377 pr_err("CONFIG_DEBUG_FS is not enabled\n");
378 return -ENOENT;
379 }
380 pm8xx_dvc_gchip = chip;
381
382 return 0;
383}
384
385static int pm8xx_dvc_remove(struct platform_device *pdev)
386{
387 struct pm8xx_dvc *dvcdata =
388 (struct pm8xx_dvc *)platform_get_drvdata(pdev);
389 devm_kfree(&pdev->dev, dvcdata);
390 return 0;
391}
392
393static struct of_device_id pm8xx_dvc_of_match[] = {
394 {.compatible = "marvell,88pm8xx-dvc"},
395 /* add other pmic dvc device name here */
396 {},
397};
398
399MODULE_DEVICE_TABLE(of, pm8xx_dvc_of_match);
400
401static struct platform_driver pm8xx_dvc_driver = {
402 .driver = {
403 .name = "88pm8xx-dvc",
404 .owner = THIS_MODULE,
405 .of_match_table = of_match_ptr(pm8xx_dvc_of_match),
406 },
407 .probe = pm8xx_dvc_probe,
408 .remove = pm8xx_dvc_remove,
409};
410
411static int __init pm8xx_dvc_init(void)
412{
413 return platform_driver_register(&pm8xx_dvc_driver);
414}
415
416subsys_initcall(pm8xx_dvc_init);
417
418static void __exit pm8xx_dvc_exit(void)
419{
420 platform_driver_unregister(&pm8xx_dvc_driver);
421}
422
423module_exit(pm8xx_dvc_exit);
424
425MODULE_LICENSE("GPL");
426MODULE_DESCRIPTION("DVC Driver for Marvell 88PM8xx PMIC");
427MODULE_ALIAS("platform:88pm8xx-dvc");