Linux Audio

Check our new training course

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