File Index
Files and directories
Sign in
FILE

io-domain.c

File preview with syntax highlighting.

linux-6.19.14/drivers/soc/rockchip/io-domain.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * Rockchip IO Voltage Domain driver
  4 *
  5 * Copyright 2014 MundoReader S.L.
  6 * Copyright 2014 Google, Inc.
  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 * The max voltage for 1.8V and 3.3V come from the Rockchip datasheet under
 22 * "Recommended Operating Conditions" for "Digital GPIO".   When the typical
 23 * is 3.3V the max is 3.6V.  When the typical is 1.8V the max is 1.98V.
 24 *
 25 * They are used like this:
 26 * - If the voltage on a rail is above the "1.8" voltage (1.98V) we'll tell the
 27 *   SoC we're at 3.3.
 28 * - If the voltage on a rail is above the "3.3" voltage (3.6V) we'll consider
 29 *   that to be an error.
 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 RK3308_SOC_CON0			0x300
 43#define RK3308_SOC_CON0_VCCIO3		BIT(8)
 44#define RK3308_SOC_VCCIO3_SUPPLY_NUM	3
 45
 46#define RK3328_SOC_CON4			0x410
 47#define RK3328_SOC_CON4_VCCIO2		BIT(7)
 48#define RK3328_SOC_VCCIO2_SUPPLY_NUM	1
 49
 50#define RK3368_SOC_CON15		0x43c
 51#define RK3368_SOC_CON15_FLASH0		BIT(14)
 52#define RK3368_SOC_FLASH_SUPPLY_NUM	2
 53
 54#define RK3399_PMUGRF_CON0		0x180
 55#define RK3399_PMUGRF_CON0_VSEL		BIT(8)
 56#define RK3399_PMUGRF_VSEL_SUPPLY_NUM	9
 57
 58#define RK3568_PMU_GRF_IO_VSEL0		(0x0140)
 59#define RK3568_PMU_GRF_IO_VSEL1		(0x0144)
 60#define RK3568_PMU_GRF_IO_VSEL2		(0x0148)
 61
 62struct rockchip_iodomain;
 63
 64struct rockchip_iodomain_supply {
 65	struct rockchip_iodomain *iod;
 66	struct regulator *reg;
 67	struct notifier_block nb;
 68	int idx;
 69};
 70
 71struct rockchip_iodomain_soc_data {
 72	int grf_offset;
 73	const char *supply_names[MAX_SUPPLIES];
 74	void (*init)(struct rockchip_iodomain *iod);
 75	int (*write)(struct rockchip_iodomain_supply *supply, int uV);
 76};
 77
 78struct rockchip_iodomain {
 79	struct device *dev;
 80	struct regmap *grf;
 81	const struct rockchip_iodomain_soc_data *soc_data;
 82	struct rockchip_iodomain_supply supplies[MAX_SUPPLIES];
 83	int (*write)(struct rockchip_iodomain_supply *supply, int uV);
 84};
 85
 86static int rk3568_iodomain_write(struct rockchip_iodomain_supply *supply, int uV)
 87{
 88	struct rockchip_iodomain *iod = supply->iod;
 89	u32 is_3v3 = uV > MAX_VOLTAGE_1_8;
 90	u32 val0, val1;
 91	int b;
 92
 93	switch (supply->idx) {
 94	case 0: /* pmuio1 */
 95		break;
 96	case 1: /* pmuio2 */
 97		b = supply->idx;
 98		val0 = BIT(16 + b) | (is_3v3 ? 0 : BIT(b));
 99		b = supply->idx + 4;
100		val1 = BIT(16 + b) | (is_3v3 ? BIT(b) : 0);
101
102		regmap_write(iod->grf, RK3568_PMU_GRF_IO_VSEL2, val0);
103		regmap_write(iod->grf, RK3568_PMU_GRF_IO_VSEL2, val1);
104		break;
105	case 3: /* vccio2 */
106		break;
107	case 2: /* vccio1 */
108	case 4: /* vccio3 */
109	case 5: /* vccio4 */
110	case 6: /* vccio5 */
111	case 7: /* vccio6 */
112	case 8: /* vccio7 */
113		b = supply->idx - 1;
114		val0 = BIT(16 + b) | (is_3v3 ? 0 : BIT(b));
115		val1 = BIT(16 + b) | (is_3v3 ? BIT(b) : 0);
116
117		regmap_write(iod->grf, RK3568_PMU_GRF_IO_VSEL0, val0);
118		regmap_write(iod->grf, RK3568_PMU_GRF_IO_VSEL1, val1);
119		break;
120	default:
121		return -EINVAL;
122	}
123
124	return 0;
125}
126
127static int rockchip_iodomain_write(struct rockchip_iodomain_supply *supply,
128				   int uV)
129{
130	struct rockchip_iodomain *iod = supply->iod;
131	u32 val;
132	int ret;
133
134	/* set value bit */
135	val = (uV > MAX_VOLTAGE_1_8) ? 0 : 1;
136	val <<= supply->idx;
137
138	/* apply hiword-mask */
139	val |= (BIT(supply->idx) << 16);
140
141	ret = regmap_write(iod->grf, iod->soc_data->grf_offset, val);
142	if (ret)
143		dev_err(iod->dev, "Couldn't write to GRF\n");
144
145	return ret;
146}
147
148static int rockchip_iodomain_notify(struct notifier_block *nb,
149				    unsigned long event,
150				    void *data)
151{
152	struct rockchip_iodomain_supply *supply =
153			container_of(nb, struct rockchip_iodomain_supply, nb);
154	int uV;
155	int ret;
156
157	/*
158	 * According to Rockchip it's important to keep the SoC IO domain
159	 * higher than (or equal to) the external voltage.  That means we need
160	 * to change it before external voltage changes happen in the case
161	 * of an increase.
162	 *
163	 * Note that in the "pre" change we pick the max possible voltage that
164	 * the regulator might end up at (the client requests a range and we
165	 * don't know for certain the exact voltage).  Right now we rely on the
166	 * slop in MAX_VOLTAGE_1_8 and MAX_VOLTAGE_3_3 to save us if clients
167	 * request something like a max of 3.6V when they really want 3.3V.
168	 * We could attempt to come up with better rules if this fails.
169	 */
170	if (event & REGULATOR_EVENT_PRE_VOLTAGE_CHANGE) {
171		struct pre_voltage_change_data *pvc_data = data;
172
173		uV = max_t(unsigned long, pvc_data->old_uV, pvc_data->max_uV);
174	} else if (event & (REGULATOR_EVENT_VOLTAGE_CHANGE |
175			    REGULATOR_EVENT_ABORT_VOLTAGE_CHANGE)) {
176		uV = (unsigned long)data;
177	} else {
178		return NOTIFY_OK;
179	}
180
181	dev_dbg(supply->iod->dev, "Setting to %d\n", uV);
182
183	if (uV > MAX_VOLTAGE_3_3) {
184		dev_err(supply->iod->dev, "Voltage too high: %d\n", uV);
185
186		if (event == REGULATOR_EVENT_PRE_VOLTAGE_CHANGE)
187			return NOTIFY_BAD;
188	}
189
190	ret = supply->iod->write(supply, uV);
191	if (ret && event == REGULATOR_EVENT_PRE_VOLTAGE_CHANGE)
192		return NOTIFY_BAD;
193
194	dev_dbg(supply->iod->dev, "Setting to %d done\n", uV);
195	return NOTIFY_OK;
196}
197
198static void px30_iodomain_init(struct rockchip_iodomain *iod)
199{
200	int ret;
201	u32 val;
202
203	/* if no VCCIO6 supply we should leave things alone */
204	if (!iod->supplies[PX30_IO_VSEL_VCCIO6_SUPPLY_NUM].reg)
205		return;
206
207	/*
208	 * set vccio6 iodomain to also use this framework
209	 * instead of a special gpio.
210	 */
211	val = PX30_IO_VSEL_VCCIO6_SRC | (PX30_IO_VSEL_VCCIO6_SRC << 16);
212	ret = regmap_write(iod->grf, PX30_IO_VSEL, val);
213	if (ret < 0)
214		dev_warn(iod->dev, "couldn't update vccio6 ctrl\n");
215}
216
217static void rk3288_iodomain_init(struct rockchip_iodomain *iod)
218{
219	int ret;
220	u32 val;
221
222	/* if no flash supply we should leave things alone */
223	if (!iod->supplies[RK3288_SOC_FLASH_SUPPLY_NUM].reg)
224		return;
225
226	/*
227	 * set flash0 iodomain to also use this framework
228	 * instead of a special gpio.
229	 */
230	val = RK3288_SOC_CON2_FLASH0 | (RK3288_SOC_CON2_FLASH0 << 16);
231	ret = regmap_write(iod->grf, RK3288_SOC_CON2, val);
232	if (ret < 0)
233		dev_warn(iod->dev, "couldn't update flash0 ctrl\n");
234}
235
236static void rk3308_iodomain_init(struct rockchip_iodomain *iod)
237{
238	int ret;
239	u32 val;
240
241	/* if no vccio3 supply we should leave things alone */
242	if (!iod->supplies[RK3308_SOC_VCCIO3_SUPPLY_NUM].reg)
243		return;
244
245	/*
246	 * set vccio3 iodomain to also use this framework
247	 * instead of a special gpio.
248	 */
249	val = RK3308_SOC_CON0_VCCIO3 | (RK3308_SOC_CON0_VCCIO3 << 16);
250	ret = regmap_write(iod->grf, RK3308_SOC_CON0, val);
251	if (ret < 0)
252		dev_warn(iod->dev, "couldn't update vccio3 vsel ctrl\n");
253}
254
255static void rk3328_iodomain_init(struct rockchip_iodomain *iod)
256{
257	int ret;
258	u32 val;
259
260	/* if no vccio2 supply we should leave things alone */
261	if (!iod->supplies[RK3328_SOC_VCCIO2_SUPPLY_NUM].reg)
262		return;
263
264	/*
265	 * set vccio2 iodomain to also use this framework
266	 * instead of a special gpio.
267	 */
268	val = RK3328_SOC_CON4_VCCIO2 | (RK3328_SOC_CON4_VCCIO2 << 16);
269	ret = regmap_write(iod->grf, RK3328_SOC_CON4, val);
270	if (ret < 0)
271		dev_warn(iod->dev, "couldn't update vccio2 vsel ctrl\n");
272}
273
274static void rk3368_iodomain_init(struct rockchip_iodomain *iod)
275{
276	int ret;
277	u32 val;
278
279	/* if no flash supply we should leave things alone */
280	if (!iod->supplies[RK3368_SOC_FLASH_SUPPLY_NUM].reg)
281		return;
282
283	/*
284	 * set flash0 iodomain to also use this framework
285	 * instead of a special gpio.
286	 */
287	val = RK3368_SOC_CON15_FLASH0 | (RK3368_SOC_CON15_FLASH0 << 16);
288	ret = regmap_write(iod->grf, RK3368_SOC_CON15, val);
289	if (ret < 0)
290		dev_warn(iod->dev, "couldn't update flash0 ctrl\n");
291}
292
293static void rk3399_pmu_iodomain_init(struct rockchip_iodomain *iod)
294{
295	int ret;
296	u32 val;
297
298	/* if no pmu io supply we should leave things alone */
299	if (!iod->supplies[RK3399_PMUGRF_VSEL_SUPPLY_NUM].reg)
300		return;
301
302	/*
303	 * set pmu io iodomain to also use this framework
304	 * instead of a special gpio.
305	 */
306	val = RK3399_PMUGRF_CON0_VSEL | (RK3399_PMUGRF_CON0_VSEL << 16);
307	ret = regmap_write(iod->grf, RK3399_PMUGRF_CON0, val);
308	if (ret < 0)
309		dev_warn(iod->dev, "couldn't update pmu io iodomain ctrl\n");
310}
311
312static const struct rockchip_iodomain_soc_data soc_data_px30 = {
313	.grf_offset = 0x180,
314	.supply_names = {
315		NULL,
316		"vccio6",
317		"vccio1",
318		"vccio2",
319		"vccio3",
320		"vccio4",
321		"vccio5",
322		"vccio-oscgpi",
323	},
324	.init = px30_iodomain_init,
325};
326
327static const struct rockchip_iodomain_soc_data soc_data_px30_pmu = {
328	.grf_offset = 0x100,
329	.supply_names = {
330		NULL,
331		NULL,
332		NULL,
333		NULL,
334		NULL,
335		NULL,
336		NULL,
337		NULL,
338		NULL,
339		NULL,
340		NULL,
341		NULL,
342		NULL,
343		NULL,
344		"pmuio1",
345		"pmuio2",
346	},
347};
348
349/*
350 * On the rk3188 the io-domains are handled by a shared register with the
351 * lower 8 bits being still being continuing drive-strength settings.
352 */
353static const struct rockchip_iodomain_soc_data soc_data_rk3188 = {
354	.grf_offset = 0x104,
355	.supply_names = {
356		NULL,
357		NULL,
358		NULL,
359		NULL,
360		NULL,
361		NULL,
362		NULL,
363		NULL,
364		"ap0",
365		"ap1",
366		"cif",
367		"flash",
368		"vccio0",
369		"vccio1",
370		"lcdc0",
371		"lcdc1",
372	},
373};
374
375static const struct rockchip_iodomain_soc_data soc_data_rk3228 = {
376	.grf_offset = 0x418,
377	.supply_names = {
378		"vccio1",
379		"vccio2",
380		"vccio3",
381		"vccio4",
382	},
383};
384
385static const struct rockchip_iodomain_soc_data soc_data_rk3288 = {
386	.grf_offset = 0x380,
387	.supply_names = {
388		"lcdc",		/* LCDC_VDD */
389		"dvp",		/* DVPIO_VDD */
390		"flash0",	/* FLASH0_VDD (emmc) */
391		"flash1",	/* FLASH1_VDD (sdio1) */
392		"wifi",		/* APIO3_VDD  (sdio0) */
393		"bb",		/* APIO5_VDD */
394		"audio",	/* APIO4_VDD */
395		"sdcard",	/* SDMMC0_VDD (sdmmc) */
396		"gpio30",	/* APIO1_VDD */
397		"gpio1830",	/* APIO2_VDD */
398	},
399	.init = rk3288_iodomain_init,
400};
401
402static const struct rockchip_iodomain_soc_data soc_data_rk3308 = {
403	.grf_offset = 0x300,
404	.supply_names = {
405		"vccio0",
406		"vccio1",
407		"vccio2",
408		"vccio3",
409		"vccio4",
410		"vccio5",
411	},
412	.init = rk3308_iodomain_init,
413};
414
415static const struct rockchip_iodomain_soc_data soc_data_rk3328 = {
416	.grf_offset = 0x410,
417	.supply_names = {
418		"vccio1",
419		"vccio2",
420		"vccio3",
421		"vccio4",
422		"vccio5",
423		"vccio6",
424		"pmuio",
425	},
426	.init = rk3328_iodomain_init,
427};
428
429static const struct rockchip_iodomain_soc_data soc_data_rk3368 = {
430	.grf_offset = 0x900,
431	.supply_names = {
432		NULL,		/* reserved */
433		"dvp",		/* DVPIO_VDD */
434		"flash0",	/* FLASH0_VDD (emmc) */
435		"wifi",		/* APIO2_VDD (sdio0) */
436		NULL,
437		"audio",	/* APIO3_VDD */
438		"sdcard",	/* SDMMC0_VDD (sdmmc) */
439		"gpio30",	/* APIO1_VDD */
440		"gpio1830",	/* APIO4_VDD (gpujtag) */
441	},
442	.init = rk3368_iodomain_init,
443};
444
445static const struct rockchip_iodomain_soc_data soc_data_rk3368_pmu = {
446	.grf_offset = 0x100,
447	.supply_names = {
448		NULL,
449		NULL,
450		NULL,
451		NULL,
452		"pmu",	        /*PMU IO domain*/
453		"vop",	        /*LCDC IO domain*/
454	},
455};
456
457static const struct rockchip_iodomain_soc_data soc_data_rk3399 = {
458	.grf_offset = 0xe640,
459	.supply_names = {
460		"bt656",		/* APIO2_VDD */
461		"audio",		/* APIO5_VDD */
462		"sdmmc",		/* SDMMC0_VDD */
463		"gpio1830",		/* APIO4_VDD */
464	},
465};
466
467static const struct rockchip_iodomain_soc_data soc_data_rk3399_pmu = {
468	.grf_offset = 0x180,
469	.supply_names = {
470		NULL,
471		NULL,
472		NULL,
473		NULL,
474		NULL,
475		NULL,
476		NULL,
477		NULL,
478		NULL,
479		"pmu1830",		/* PMUIO2_VDD */
480	},
481	.init = rk3399_pmu_iodomain_init,
482};
483
484static const struct rockchip_iodomain_soc_data soc_data_rk3568_pmu = {
485	.grf_offset = 0x140,
486	.supply_names = {
487		"pmuio1",
488		"pmuio2",
489		"vccio1",
490		"vccio2",
491		"vccio3",
492		"vccio4",
493		"vccio5",
494		"vccio6",
495		"vccio7",
496	},
497	.write = rk3568_iodomain_write,
498};
499
500static const struct rockchip_iodomain_soc_data soc_data_rv1108 = {
501	.grf_offset = 0x404,
502	.supply_names = {
503		NULL,
504		NULL,
505		NULL,
506		NULL,
507		NULL,
508		NULL,
509		NULL,
510		NULL,
511		NULL,
512		NULL,
513		NULL,
514		"vccio1",
515		"vccio2",
516		"vccio3",
517		"vccio5",
518		"vccio6",
519	},
520
521};
522
523static const struct rockchip_iodomain_soc_data soc_data_rv1108_pmu = {
524	.grf_offset = 0x104,
525	.supply_names = {
526		"pmu",
527	},
528};
529
530static const struct rockchip_iodomain_soc_data soc_data_rv1126_pmu = {
531	.grf_offset = 0x140,
532	.supply_names = {
533		NULL,
534		"vccio1",
535		"vccio2",
536		"vccio3",
537		"vccio4",
538		"vccio5",
539		"vccio6",
540		"vccio7",
541		"pmuio0",
542		"pmuio1",
543	},
544};
545
546static const struct of_device_id rockchip_iodomain_match[] = {
547	{
548		.compatible = "rockchip,px30-io-voltage-domain",
549		.data = (void *)&soc_data_px30
550	},
551	{
552		.compatible = "rockchip,px30-pmu-io-voltage-domain",
553		.data = (void *)&soc_data_px30_pmu
554	},
555	{
556		.compatible = "rockchip,rk3188-io-voltage-domain",
557		.data = &soc_data_rk3188
558	},
559	{
560		.compatible = "rockchip,rk3228-io-voltage-domain",
561		.data = &soc_data_rk3228
562	},
563	{
564		.compatible = "rockchip,rk3288-io-voltage-domain",
565		.data = &soc_data_rk3288
566	},
567	{
568		.compatible = "rockchip,rk3308-io-voltage-domain",
569		.data = &soc_data_rk3308
570	},
571	{
572		.compatible = "rockchip,rk3328-io-voltage-domain",
573		.data = &soc_data_rk3328
574	},
575	{
576		.compatible = "rockchip,rk3368-io-voltage-domain",
577		.data = &soc_data_rk3368
578	},
579	{
580		.compatible = "rockchip,rk3368-pmu-io-voltage-domain",
581		.data = &soc_data_rk3368_pmu
582	},
583	{
584		.compatible = "rockchip,rk3399-io-voltage-domain",
585		.data = &soc_data_rk3399
586	},
587	{
588		.compatible = "rockchip,rk3399-pmu-io-voltage-domain",
589		.data = &soc_data_rk3399_pmu
590	},
591	{
592		.compatible = "rockchip,rk3568-pmu-io-voltage-domain",
593		.data = &soc_data_rk3568_pmu
594	},
595	{
596		.compatible = "rockchip,rv1108-io-voltage-domain",
597		.data = &soc_data_rv1108
598	},
599	{
600		.compatible = "rockchip,rv1108-pmu-io-voltage-domain",
601		.data = &soc_data_rv1108_pmu
602	},
603	{
604		.compatible = "rockchip,rv1126-pmu-io-voltage-domain",
605		.data = &soc_data_rv1126_pmu
606	},
607	{ /* sentinel */ },
608};
609MODULE_DEVICE_TABLE(of, rockchip_iodomain_match);
610
611static int rockchip_iodomain_probe(struct platform_device *pdev)
612{
613	struct device_node *np = pdev->dev.of_node;
614	const struct of_device_id *match;
615	struct rockchip_iodomain *iod;
616	struct device *parent;
617	int i, ret = 0;
618
619	if (!np)
620		return -ENODEV;
621
622	iod = devm_kzalloc(&pdev->dev, sizeof(*iod), GFP_KERNEL);
623	if (!iod)
624		return -ENOMEM;
625
626	iod->dev = &pdev->dev;
627	platform_set_drvdata(pdev, iod);
628
629	match = of_match_node(rockchip_iodomain_match, np);
630	iod->soc_data = match->data;
631
632	if (iod->soc_data->write)
633		iod->write = iod->soc_data->write;
634	else
635		iod->write = rockchip_iodomain_write;
636
637	parent = pdev->dev.parent;
638	if (parent && parent->of_node) {
639		iod->grf = syscon_node_to_regmap(parent->of_node);
640	} else {
641		dev_dbg(&pdev->dev, "falling back to old binding\n");
642		iod->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
643	}
644
645	if (IS_ERR(iod->grf)) {
646		dev_err(&pdev->dev, "couldn't find grf regmap\n");
647		return PTR_ERR(iod->grf);
648	}
649
650	for (i = 0; i < MAX_SUPPLIES; i++) {
651		const char *supply_name = iod->soc_data->supply_names[i];
652		struct rockchip_iodomain_supply *supply = &iod->supplies[i];
653		struct regulator *reg;
654		int uV;
655
656		if (!supply_name)
657			continue;
658
659		reg = devm_regulator_get_optional(iod->dev, supply_name);
660		if (IS_ERR(reg)) {
661			ret = PTR_ERR(reg);
662
663			/* If a supply wasn't specified, that's OK */
664			if (ret == -ENODEV)
665				continue;
666			else if (ret != -EPROBE_DEFER)
667				dev_err(iod->dev, "couldn't get regulator %s\n",
668					supply_name);
669			goto unreg_notify;
670		}
671
672		/* set initial correct value */
673		uV = regulator_get_voltage(reg);
674
675		/* must be a regulator we can get the voltage of */
676		if (uV < 0) {
677			dev_err(iod->dev, "Can't determine voltage: %s\n",
678				supply_name);
679			ret = uV;
680			goto unreg_notify;
681		}
682
683		if (uV > MAX_VOLTAGE_3_3) {
684			dev_crit(iod->dev,
685				 "%d uV is too high. May damage SoC!\n",
686				 uV);
687			ret = -EINVAL;
688			goto unreg_notify;
689		}
690
691		/* setup our supply */
692		supply->idx = i;
693		supply->iod = iod;
694		supply->reg = reg;
695		supply->nb.notifier_call = rockchip_iodomain_notify;
696
697		ret = iod->write(supply, uV);
698		if (ret) {
699			supply->reg = NULL;
700			goto unreg_notify;
701		}
702
703		/* register regulator notifier */
704		ret = regulator_register_notifier(reg, &supply->nb);
705		if (ret) {
706			dev_err(&pdev->dev,
707				"regulator notifier request failed\n");
708			supply->reg = NULL;
709			goto unreg_notify;
710		}
711	}
712
713	if (iod->soc_data->init)
714		iod->soc_data->init(iod);
715
716	return 0;
717
718unreg_notify:
719	for (i = MAX_SUPPLIES - 1; i >= 0; i--) {
720		struct rockchip_iodomain_supply *io_supply = &iod->supplies[i];
721
722		if (io_supply->reg)
723			regulator_unregister_notifier(io_supply->reg,
724						      &io_supply->nb);
725	}
726
727	return ret;
728}
729
730static void rockchip_iodomain_remove(struct platform_device *pdev)
731{
732	struct rockchip_iodomain *iod = platform_get_drvdata(pdev);
733	int i;
734
735	for (i = MAX_SUPPLIES - 1; i >= 0; i--) {
736		struct rockchip_iodomain_supply *io_supply = &iod->supplies[i];
737
738		if (io_supply->reg)
739			regulator_unregister_notifier(io_supply->reg,
740						      &io_supply->nb);
741	}
742}
743
744static struct platform_driver rockchip_iodomain_driver = {
745	.probe = rockchip_iodomain_probe,
746	.remove = rockchip_iodomain_remove,
747	.driver = {
748		.name = "rockchip-iodomain",
749		.of_match_table = rockchip_iodomain_match,
750	},
751};
752
753module_platform_driver(rockchip_iodomain_driver);
754
755MODULE_DESCRIPTION("Rockchip IO-domain driver");
756MODULE_AUTHOR("Heiko Stuebner <heiko@sntech.de>");
757MODULE_AUTHOR("Doug Anderson <dianders@chromium.org>");
758MODULE_LICENSE("GPL v2");
~/ Read-only file preview.