1
2
3
4
5
6
7
8
9#include <linux/kernel.h>
10#include <linux/module.h>
11#include <linux/err.h>
12#include <linux/mfd/syscon.h>
13#include <linux/of.h>
14#include <linux/platform_device.h>
15#include <linux/regmap.h>
16#include <linux/regulator/consumer.h>
17
18#define MAX_SUPPLIES 16
19
20
21
22
23
24
25
26
27
28
29
30
31#define MAX_VOLTAGE_1_8 1980000
32#define MAX_VOLTAGE_3_3 3600000
33
34#define PX30_IO_VSEL 0x180
35#define PX30_IO_VSEL_VCCIO6_SRC BIT(0)
36#define PX30_IO_VSEL_VCCIO6_SUPPLY_NUM 1
37
38#define RK3288_SOC_CON2 0x24c
39#define RK3288_SOC_CON2_FLASH0 BIT(7)
40#define RK3288_SOC_FLASH_SUPPLY_NUM 2
41
42#define RK3328_SOC_CON4 0x410
43#define RK3328_SOC_CON4_VCCIO2 BIT(7)
44#define RK3328_SOC_VCCIO2_SUPPLY_NUM 1
45
46#define RK3368_SOC_CON15 0x43c
47#define RK3368_SOC_CON15_FLASH0 BIT(14)
48#define RK3368_SOC_FLASH_SUPPLY_NUM 2
49
50#define RK3399_PMUGRF_CON0 0x180
51#define RK3399_PMUGRF_CON0_VSEL BIT(8)
52#define RK3399_PMUGRF_VSEL_SUPPLY_NUM 9
53
54#define RK3568_PMU_GRF_IO_VSEL0 (0x0140)
55#define RK3568_PMU_GRF_IO_VSEL1 (0x0144)
56#define RK3568_PMU_GRF_IO_VSEL2 (0x0148)
57
58struct rockchip_iodomain;
59
60struct rockchip_iodomain_supply {
61 struct rockchip_iodomain *iod;
62 struct regulator *reg;
63 struct notifier_block nb;
64 int idx;
65};
66
67struct rockchip_iodomain_soc_data {
68 int grf_offset;
69 const char *supply_names[MAX_SUPPLIES];
70 void (*init)(struct rockchip_iodomain *iod);
71 int (*write)(struct rockchip_iodomain_supply *supply, int uV);
72};
73
74struct rockchip_iodomain {
75 struct device *dev;
76 struct regmap *grf;
77 const struct rockchip_iodomain_soc_data *soc_data;
78 struct rockchip_iodomain_supply supplies[MAX_SUPPLIES];
79 int (*write)(struct rockchip_iodomain_supply *supply, int uV);
80};
81
82static int rk3568_iodomain_write(struct rockchip_iodomain_supply *supply, int uV)
83{
84 struct rockchip_iodomain *iod = supply->iod;
85 u32 is_3v3 = uV > MAX_VOLTAGE_1_8;
86 u32 val0, val1;
87 int b;
88
89 switch (supply->idx) {
90 case 0:
91 break;
92 case 1:
93 b = supply->idx;
94 val0 = BIT(16 + b) | (is_3v3 ? 0 : BIT(b));
95 b = supply->idx + 4;
96 val1 = BIT(16 + b) | (is_3v3 ? BIT(b) : 0);
97
98 regmap_write(iod->grf, RK3568_PMU_GRF_IO_VSEL2, val0);
99 regmap_write(iod->grf, RK3568_PMU_GRF_IO_VSEL2, val1);
100 break;
101 case 3:
102 break;
103 case 2:
104 case 4:
105 case 5:
106 case 6:
107 case 7:
108 case 8:
109 b = supply->idx - 1;
110 val0 = BIT(16 + b) | (is_3v3 ? 0 : BIT(b));
111 val1 = BIT(16 + b) | (is_3v3 ? BIT(b) : 0);
112
113 regmap_write(iod->grf, RK3568_PMU_GRF_IO_VSEL0, val0);
114 regmap_write(iod->grf, RK3568_PMU_GRF_IO_VSEL1, val1);
115 break;
116 default:
117 return -EINVAL;
118 }
119
120 return 0;
121}
122
123static int rockchip_iodomain_write(struct rockchip_iodomain_supply *supply,
124 int uV)
125{
126 struct rockchip_iodomain *iod = supply->iod;
127 u32 val;
128 int ret;
129
130
131 val = (uV > MAX_VOLTAGE_1_8) ? 0 : 1;
132 val <<= supply->idx;
133
134
135 val |= (BIT(supply->idx) << 16);
136
137 ret = regmap_write(iod->grf, iod->soc_data->grf_offset, val);
138 if (ret)
139 dev_err(iod->dev, "Couldn't write to GRF\n");
140
141 return ret;
142}
143
144static int rockchip_iodomain_notify(struct notifier_block *nb,
145 unsigned long event,
146 void *data)
147{
148 struct rockchip_iodomain_supply *supply =
149 container_of(nb, struct rockchip_iodomain_supply, nb);
150 int uV;
151 int ret;
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166 if (event & REGULATOR_EVENT_PRE_VOLTAGE_CHANGE) {
167 struct pre_voltage_change_data *pvc_data = data;
168
169 uV = max_t(unsigned long, pvc_data->old_uV, pvc_data->max_uV);
170 } else if (event & (REGULATOR_EVENT_VOLTAGE_CHANGE |
171 REGULATOR_EVENT_ABORT_VOLTAGE_CHANGE)) {
172 uV = (unsigned long)data;
173 } else {
174 return NOTIFY_OK;
175 }
176
177 dev_dbg(supply->iod->dev, "Setting to %d\n", uV);
178
179 if (uV > MAX_VOLTAGE_3_3) {
180 dev_err(supply->iod->dev, "Voltage too high: %d\n", uV);
181
182 if (event == REGULATOR_EVENT_PRE_VOLTAGE_CHANGE)
183 return NOTIFY_BAD;
184 }
185
186 ret = supply->iod->write(supply, uV);
187 if (ret && event == REGULATOR_EVENT_PRE_VOLTAGE_CHANGE)
188 return NOTIFY_BAD;
189
190 dev_dbg(supply->iod->dev, "Setting to %d done\n", uV);
191 return NOTIFY_OK;
192}
193
194static void px30_iodomain_init(struct rockchip_iodomain *iod)
195{
196 int ret;
197 u32 val;
198
199
200 if (!iod->supplies[PX30_IO_VSEL_VCCIO6_SUPPLY_NUM].reg)
201 return;
202
203
204
205
206
207 val = PX30_IO_VSEL_VCCIO6_SRC | (PX30_IO_VSEL_VCCIO6_SRC << 16);
208 ret = regmap_write(iod->grf, PX30_IO_VSEL, val);
209 if (ret < 0)
210 dev_warn(iod->dev, "couldn't update vccio6 ctrl\n");
211}
212
213static void rk3288_iodomain_init(struct rockchip_iodomain *iod)
214{
215 int ret;
216 u32 val;
217
218
219 if (!iod->supplies[RK3288_SOC_FLASH_SUPPLY_NUM].reg)
220 return;
221
222
223
224
225
226 val = RK3288_SOC_CON2_FLASH0 | (RK3288_SOC_CON2_FLASH0 << 16);
227 ret = regmap_write(iod->grf, RK3288_SOC_CON2, val);
228 if (ret < 0)
229 dev_warn(iod->dev, "couldn't update flash0 ctrl\n");
230}
231
232static void rk3328_iodomain_init(struct rockchip_iodomain *iod)
233{
234 int ret;
235 u32 val;
236
237
238 if (!iod->supplies[RK3328_SOC_VCCIO2_SUPPLY_NUM].reg)
239 return;
240
241
242
243
244
245 val = RK3328_SOC_CON4_VCCIO2 | (RK3328_SOC_CON4_VCCIO2 << 16);
246 ret = regmap_write(iod->grf, RK3328_SOC_CON4, val);
247 if (ret < 0)
248 dev_warn(iod->dev, "couldn't update vccio2 vsel ctrl\n");
249}
250
251static void rk3368_iodomain_init(struct rockchip_iodomain *iod)
252{
253 int ret;
254 u32 val;
255
256
257 if (!iod->supplies[RK3368_SOC_FLASH_SUPPLY_NUM].reg)
258 return;
259
260
261
262
263
264 val = RK3368_SOC_CON15_FLASH0 | (RK3368_SOC_CON15_FLASH0 << 16);
265 ret = regmap_write(iod->grf, RK3368_SOC_CON15, val);
266 if (ret < 0)
267 dev_warn(iod->dev, "couldn't update flash0 ctrl\n");
268}
269
270static void rk3399_pmu_iodomain_init(struct rockchip_iodomain *iod)
271{
272 int ret;
273 u32 val;
274
275
276 if (!iod->supplies[RK3399_PMUGRF_VSEL_SUPPLY_NUM].reg)
277 return;
278
279
280
281
282
283 val = RK3399_PMUGRF_CON0_VSEL | (RK3399_PMUGRF_CON0_VSEL << 16);
284 ret = regmap_write(iod->grf, RK3399_PMUGRF_CON0, val);
285 if (ret < 0)
286 dev_warn(iod->dev, "couldn't update pmu io iodomain ctrl\n");
287}
288
289static const struct rockchip_iodomain_soc_data soc_data_px30 = {
290 .grf_offset = 0x180,
291 .supply_names = {
292 NULL,
293 "vccio6",
294 "vccio1",
295 "vccio2",
296 "vccio3",
297 "vccio4",
298 "vccio5",
299 "vccio-oscgpi",
300 },
301 .init = px30_iodomain_init,
302};
303
304static const struct rockchip_iodomain_soc_data soc_data_px30_pmu = {
305 .grf_offset = 0x100,
306 .supply_names = {
307 NULL,
308 NULL,
309 NULL,
310 NULL,
311 NULL,
312 NULL,
313 NULL,
314 NULL,
315 NULL,
316 NULL,
317 NULL,
318 NULL,
319 NULL,
320 NULL,
321 "pmuio1",
322 "pmuio2",
323 },
324};
325
326
327
328
329
330static const struct rockchip_iodomain_soc_data soc_data_rk3188 = {
331 .grf_offset = 0x104,
332 .supply_names = {
333 NULL,
334 NULL,
335 NULL,
336 NULL,
337 NULL,
338 NULL,
339 NULL,
340 NULL,
341 "ap0",
342 "ap1",
343 "cif",
344 "flash",
345 "vccio0",
346 "vccio1",
347 "lcdc0",
348 "lcdc1",
349 },
350};
351
352static const struct rockchip_iodomain_soc_data soc_data_rk3228 = {
353 .grf_offset = 0x418,
354 .supply_names = {
355 "vccio1",
356 "vccio2",
357 "vccio3",
358 "vccio4",
359 },
360};
361
362static const struct rockchip_iodomain_soc_data soc_data_rk3288 = {
363 .grf_offset = 0x380,
364 .supply_names = {
365 "lcdc",
366 "dvp",
367 "flash0",
368 "flash1",
369 "wifi",
370 "bb",
371 "audio",
372 "sdcard",
373 "gpio30",
374 "gpio1830",
375 },
376 .init = rk3288_iodomain_init,
377};
378
379static const struct rockchip_iodomain_soc_data soc_data_rk3328 = {
380 .grf_offset = 0x410,
381 .supply_names = {
382 "vccio1",
383 "vccio2",
384 "vccio3",
385 "vccio4",
386 "vccio5",
387 "vccio6",
388 "pmuio",
389 },
390 .init = rk3328_iodomain_init,
391};
392
393static const struct rockchip_iodomain_soc_data soc_data_rk3368 = {
394 .grf_offset = 0x900,
395 .supply_names = {
396 NULL,
397 "dvp",
398 "flash0",
399 "wifi",
400 NULL,
401 "audio",
402 "sdcard",
403 "gpio30",
404 "gpio1830",
405 },
406 .init = rk3368_iodomain_init,
407};
408
409static const struct rockchip_iodomain_soc_data soc_data_rk3368_pmu = {
410 .grf_offset = 0x100,
411 .supply_names = {
412 NULL,
413 NULL,
414 NULL,
415 NULL,
416 "pmu",
417 "vop",
418 },
419};
420
421static const struct rockchip_iodomain_soc_data soc_data_rk3399 = {
422 .grf_offset = 0xe640,
423 .supply_names = {
424 "bt656",
425 "audio",
426 "sdmmc",
427 "gpio1830",
428 },
429};
430
431static const struct rockchip_iodomain_soc_data soc_data_rk3399_pmu = {
432 .grf_offset = 0x180,
433 .supply_names = {
434 NULL,
435 NULL,
436 NULL,
437 NULL,
438 NULL,
439 NULL,
440 NULL,
441 NULL,
442 NULL,
443 "pmu1830",
444 },
445 .init = rk3399_pmu_iodomain_init,
446};
447
448static const struct rockchip_iodomain_soc_data soc_data_rk3568_pmu = {
449 .grf_offset = 0x140,
450 .supply_names = {
451 "pmuio1",
452 "pmuio2",
453 "vccio1",
454 "vccio2",
455 "vccio3",
456 "vccio4",
457 "vccio5",
458 "vccio6",
459 "vccio7",
460 },
461 .write = rk3568_iodomain_write,
462};
463
464static const struct rockchip_iodomain_soc_data soc_data_rv1108 = {
465 .grf_offset = 0x404,
466 .supply_names = {
467 NULL,
468 NULL,
469 NULL,
470 NULL,
471 NULL,
472 NULL,
473 NULL,
474 NULL,
475 NULL,
476 NULL,
477 NULL,
478 "vccio1",
479 "vccio2",
480 "vccio3",
481 "vccio5",
482 "vccio6",
483 },
484
485};
486
487static const struct rockchip_iodomain_soc_data soc_data_rv1108_pmu = {
488 .grf_offset = 0x104,
489 .supply_names = {
490 "pmu",
491 },
492};
493
494static const struct of_device_id rockchip_iodomain_match[] = {
495 {
496 .compatible = "rockchip,px30-io-voltage-domain",
497 .data = (void *)&soc_data_px30
498 },
499 {
500 .compatible = "rockchip,px30-pmu-io-voltage-domain",
501 .data = (void *)&soc_data_px30_pmu
502 },
503 {
504 .compatible = "rockchip,rk3188-io-voltage-domain",
505 .data = &soc_data_rk3188
506 },
507 {
508 .compatible = "rockchip,rk3228-io-voltage-domain",
509 .data = &soc_data_rk3228
510 },
511 {
512 .compatible = "rockchip,rk3288-io-voltage-domain",
513 .data = &soc_data_rk3288
514 },
515 {
516 .compatible = "rockchip,rk3328-io-voltage-domain",
517 .data = &soc_data_rk3328
518 },
519 {
520 .compatible = "rockchip,rk3368-io-voltage-domain",
521 .data = &soc_data_rk3368
522 },
523 {
524 .compatible = "rockchip,rk3368-pmu-io-voltage-domain",
525 .data = &soc_data_rk3368_pmu
526 },
527 {
528 .compatible = "rockchip,rk3399-io-voltage-domain",
529 .data = &soc_data_rk3399
530 },
531 {
532 .compatible = "rockchip,rk3399-pmu-io-voltage-domain",
533 .data = &soc_data_rk3399_pmu
534 },
535 {
536 .compatible = "rockchip,rk3568-pmu-io-voltage-domain",
537 .data = &soc_data_rk3568_pmu
538 },
539 {
540 .compatible = "rockchip,rv1108-io-voltage-domain",
541 .data = &soc_data_rv1108
542 },
543 {
544 .compatible = "rockchip,rv1108-pmu-io-voltage-domain",
545 .data = &soc_data_rv1108_pmu
546 },
547 { },
548};
549MODULE_DEVICE_TABLE(of, rockchip_iodomain_match);
550
551static int rockchip_iodomain_probe(struct platform_device *pdev)
552{
553 struct device_node *np = pdev->dev.of_node;
554 const struct of_device_id *match;
555 struct rockchip_iodomain *iod;
556 struct device *parent;
557 int i, ret = 0;
558
559 if (!np)
560 return -ENODEV;
561
562 iod = devm_kzalloc(&pdev->dev, sizeof(*iod), GFP_KERNEL);
563 if (!iod)
564 return -ENOMEM;
565
566 iod->dev = &pdev->dev;
567 platform_set_drvdata(pdev, iod);
568
569 match = of_match_node(rockchip_iodomain_match, np);
570 iod->soc_data = match->data;
571
572 if (iod->soc_data->write)
573 iod->write = iod->soc_data->write;
574 else
575 iod->write = rockchip_iodomain_write;
576
577 parent = pdev->dev.parent;
578 if (parent && parent->of_node) {
579 iod->grf = syscon_node_to_regmap(parent->of_node);
580 } else {
581 dev_dbg(&pdev->dev, "falling back to old binding\n");
582 iod->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
583 }
584
585 if (IS_ERR(iod->grf)) {
586 dev_err(&pdev->dev, "couldn't find grf regmap\n");
587 return PTR_ERR(iod->grf);
588 }
589
590 for (i = 0; i < MAX_SUPPLIES; i++) {
591 const char *supply_name = iod->soc_data->supply_names[i];
592 struct rockchip_iodomain_supply *supply = &iod->supplies[i];
593 struct regulator *reg;
594 int uV;
595
596 if (!supply_name)
597 continue;
598
599 reg = devm_regulator_get_optional(iod->dev, supply_name);
600 if (IS_ERR(reg)) {
601 ret = PTR_ERR(reg);
602
603
604 if (ret == -ENODEV)
605 continue;
606 else if (ret != -EPROBE_DEFER)
607 dev_err(iod->dev, "couldn't get regulator %s\n",
608 supply_name);
609 goto unreg_notify;
610 }
611
612
613 uV = regulator_get_voltage(reg);
614
615
616 if (uV < 0) {
617 dev_err(iod->dev, "Can't determine voltage: %s\n",
618 supply_name);
619 ret = uV;
620 goto unreg_notify;
621 }
622
623 if (uV > MAX_VOLTAGE_3_3) {
624 dev_crit(iod->dev,
625 "%d uV is too high. May damage SoC!\n",
626 uV);
627 ret = -EINVAL;
628 goto unreg_notify;
629 }
630
631
632 supply->idx = i;
633 supply->iod = iod;
634 supply->reg = reg;
635 supply->nb.notifier_call = rockchip_iodomain_notify;
636
637 ret = iod->write(supply, uV);
638 if (ret) {
639 supply->reg = NULL;
640 goto unreg_notify;
641 }
642
643
644 ret = regulator_register_notifier(reg, &supply->nb);
645 if (ret) {
646 dev_err(&pdev->dev,
647 "regulator notifier request failed\n");
648 supply->reg = NULL;
649 goto unreg_notify;
650 }
651 }
652
653 if (iod->soc_data->init)
654 iod->soc_data->init(iod);
655
656 return 0;
657
658unreg_notify:
659 for (i = MAX_SUPPLIES - 1; i >= 0; i--) {
660 struct rockchip_iodomain_supply *io_supply = &iod->supplies[i];
661
662 if (io_supply->reg)
663 regulator_unregister_notifier(io_supply->reg,
664 &io_supply->nb);
665 }
666
667 return ret;
668}
669
670static int rockchip_iodomain_remove(struct platform_device *pdev)
671{
672 struct rockchip_iodomain *iod = platform_get_drvdata(pdev);
673 int i;
674
675 for (i = MAX_SUPPLIES - 1; i >= 0; i--) {
676 struct rockchip_iodomain_supply *io_supply = &iod->supplies[i];
677
678 if (io_supply->reg)
679 regulator_unregister_notifier(io_supply->reg,
680 &io_supply->nb);
681 }
682
683 return 0;
684}
685
686static struct platform_driver rockchip_iodomain_driver = {
687 .probe = rockchip_iodomain_probe,
688 .remove = rockchip_iodomain_remove,
689 .driver = {
690 .name = "rockchip-iodomain",
691 .of_match_table = rockchip_iodomain_match,
692 },
693};
694
695module_platform_driver(rockchip_iodomain_driver);
696
697MODULE_DESCRIPTION("Rockchip IO-domain driver");
698MODULE_AUTHOR("Heiko Stuebner <heiko@sntech.de>");
699MODULE_AUTHOR("Doug Anderson <dianders@chromium.org>");
700MODULE_LICENSE("GPL v2");
701