brintos

brintos / linux-shallow public Read only

0
0
Text · 17.0 KiB · fd9fd31 Raw
759 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Rockchip IO Voltage Domain driver4 *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		1619 20/*21 * The max voltage for 1.8V and 3.3V come from the Rockchip datasheet under22 * "Recommended Operating Conditions" for "Digital GPIO".   When the typical23 * 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 the27 *   SoC we're at 3.3.28 * - If the voltage on a rail is above the "3.3" voltage (3.6V) we'll consider29 *   that to be an error.30 */31#define MAX_VOLTAGE_1_8		198000032#define MAX_VOLTAGE_3_3		360000033 34#define PX30_IO_VSEL			0x18035#define PX30_IO_VSEL_VCCIO6_SRC		BIT(0)36#define PX30_IO_VSEL_VCCIO6_SUPPLY_NUM	137 38#define RK3288_SOC_CON2			0x24c39#define RK3288_SOC_CON2_FLASH0		BIT(7)40#define RK3288_SOC_FLASH_SUPPLY_NUM	241 42#define RK3308_SOC_CON0			0x30043#define RK3308_SOC_CON0_VCCIO3		BIT(8)44#define RK3308_SOC_VCCIO3_SUPPLY_NUM	345 46#define RK3328_SOC_CON4			0x41047#define RK3328_SOC_CON4_VCCIO2		BIT(7)48#define RK3328_SOC_VCCIO2_SUPPLY_NUM	149 50#define RK3368_SOC_CON15		0x43c51#define RK3368_SOC_CON15_FLASH0		BIT(14)52#define RK3368_SOC_FLASH_SUPPLY_NUM	253 54#define RK3399_PMUGRF_CON0		0x18055#define RK3399_PMUGRF_CON0_VSEL		BIT(8)56#define RK3399_PMUGRF_VSEL_SUPPLY_NUM	957 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 domain159	 * higher than (or equal to) the external voltage.  That means we need160	 * to change it before external voltage changes happen in the case161	 * of an increase.162	 *163	 * Note that in the "pre" change we pick the max possible voltage that164	 * the regulator might end up at (the client requests a range and we165	 * don't know for certain the exact voltage).  Right now we rely on the166	 * slop in MAX_VOLTAGE_1_8 and MAX_VOLTAGE_3_3 to save us if clients167	 * 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 framework209	 * 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 framework228	 * 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 framework247	 * 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 framework266	 * 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 framework285	 * 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 framework304	 * 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 the351 * 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_px30550	},551	{552		.compatible = "rockchip,px30-pmu-io-voltage-domain",553		.data = (void *)&soc_data_px30_pmu554	},555	{556		.compatible = "rockchip,rk3188-io-voltage-domain",557		.data = &soc_data_rk3188558	},559	{560		.compatible = "rockchip,rk3228-io-voltage-domain",561		.data = &soc_data_rk3228562	},563	{564		.compatible = "rockchip,rk3288-io-voltage-domain",565		.data = &soc_data_rk3288566	},567	{568		.compatible = "rockchip,rk3308-io-voltage-domain",569		.data = &soc_data_rk3308570	},571	{572		.compatible = "rockchip,rk3328-io-voltage-domain",573		.data = &soc_data_rk3328574	},575	{576		.compatible = "rockchip,rk3368-io-voltage-domain",577		.data = &soc_data_rk3368578	},579	{580		.compatible = "rockchip,rk3368-pmu-io-voltage-domain",581		.data = &soc_data_rk3368_pmu582	},583	{584		.compatible = "rockchip,rk3399-io-voltage-domain",585		.data = &soc_data_rk3399586	},587	{588		.compatible = "rockchip,rk3399-pmu-io-voltage-domain",589		.data = &soc_data_rk3399_pmu590	},591	{592		.compatible = "rockchip,rk3568-pmu-io-voltage-domain",593		.data = &soc_data_rk3568_pmu594	},595	{596		.compatible = "rockchip,rv1108-io-voltage-domain",597		.data = &soc_data_rv1108598	},599	{600		.compatible = "rockchip,rv1108-pmu-io-voltage-domain",601		.data = &soc_data_rv1108_pmu602	},603	{604		.compatible = "rockchip,rv1126-pmu-io-voltage-domain",605		.data = &soc_data_rv1126_pmu606	},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	else635		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_new = 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");759