• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Rockchip IO Voltage Domain driver
3  *
4  * Copyright 2014 MundoReader S.L.
5  * Copyright 2014 Google, Inc.
6  *
7  * This software is licensed under the terms of the GNU General Public
8  * License version 2, as published by the Free Software Foundation, and
9  * may be copied, distributed, and modified under those terms.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  */
16 
17 #include <linux/kernel.h>
18 #include <linux/module.h>
19 #include <linux/err.h>
20 #include <linux/mfd/syscon.h>
21 #include <linux/of.h>
22 #include <linux/platform_device.h>
23 #include <linux/regmap.h>
24 #include <linux/regulator/consumer.h>
25 
26 #define MAX_SUPPLIES		16
27 
28 /*
29  * The max voltage for 1.8V and 3.3V come from the Rockchip datasheet under
30  * "Recommended Operating Conditions" for "Digital GPIO".   When the typical
31  * is 3.3V the max is 3.6V.  When the typical is 1.8V the max is 1.98V.
32  *
33  * They are used like this:
34  * - If the voltage on a rail is above the "1.8" voltage (1.98V) we'll tell the
35  *   SoC we're at 3.3.
36  * - If the voltage on a rail is above the "3.3" voltage (3.6V) we'll consider
37  *   that to be an error.
38  */
39 #define MAX_VOLTAGE_1_8		1980000
40 #define MAX_VOLTAGE_3_3		3600000
41 
42 #define RK3288_SOC_CON2			0x24c
43 #define RK3288_SOC_CON2_FLASH0		BIT(7)
44 #define RK3288_SOC_FLASH_SUPPLY_NUM	2
45 
46 struct rockchip_iodomain;
47 
48 /**
49  * @supplies: voltage settings matching the register bits.
50  */
51 struct rockchip_iodomain_soc_data {
52 	int grf_offset;
53 	const char *supply_names[MAX_SUPPLIES];
54 	void (*init)(struct rockchip_iodomain *iod);
55 };
56 
57 struct rockchip_iodomain_supply {
58 	struct rockchip_iodomain *iod;
59 	struct regulator *reg;
60 	struct notifier_block nb;
61 	int idx;
62 };
63 
64 struct rockchip_iodomain {
65 	struct device *dev;
66 	struct regmap *grf;
67 	struct rockchip_iodomain_soc_data *soc_data;
68 	struct rockchip_iodomain_supply supplies[MAX_SUPPLIES];
69 };
70 
rockchip_iodomain_write(struct rockchip_iodomain_supply * supply,int uV)71 static int rockchip_iodomain_write(struct rockchip_iodomain_supply *supply,
72 				   int uV)
73 {
74 	struct rockchip_iodomain *iod = supply->iod;
75 	u32 val;
76 	int ret;
77 
78 	/* set value bit */
79 	val = (uV > MAX_VOLTAGE_1_8) ? 0 : 1;
80 	val <<= supply->idx;
81 
82 	/* apply hiword-mask */
83 	val |= (BIT(supply->idx) << 16);
84 
85 	ret = regmap_write(iod->grf, iod->soc_data->grf_offset, val);
86 	if (ret)
87 		dev_err(iod->dev, "Couldn't write to GRF\n");
88 
89 	return ret;
90 }
91 
rockchip_iodomain_notify(struct notifier_block * nb,unsigned long event,void * data)92 static int rockchip_iodomain_notify(struct notifier_block *nb,
93 				    unsigned long event,
94 				    void *data)
95 {
96 	struct rockchip_iodomain_supply *supply =
97 			container_of(nb, struct rockchip_iodomain_supply, nb);
98 	int uV;
99 	int ret;
100 
101 	/*
102 	 * According to Rockchip it's important to keep the SoC IO domain
103 	 * higher than (or equal to) the external voltage.  That means we need
104 	 * to change it before external voltage changes happen in the case
105 	 * of an increase.
106 	 *
107 	 * Note that in the "pre" change we pick the max possible voltage that
108 	 * the regulator might end up at (the client requests a range and we
109 	 * don't know for certain the exact voltage).  Right now we rely on the
110 	 * slop in MAX_VOLTAGE_1_8 and MAX_VOLTAGE_3_3 to save us if clients
111 	 * request something like a max of 3.6V when they really want 3.3V.
112 	 * We could attempt to come up with better rules if this fails.
113 	 */
114 	if (event & REGULATOR_EVENT_PRE_VOLTAGE_CHANGE) {
115 		struct pre_voltage_change_data *pvc_data = data;
116 
117 		uV = max_t(unsigned long, pvc_data->old_uV, pvc_data->max_uV);
118 	} else if (event & (REGULATOR_EVENT_VOLTAGE_CHANGE |
119 			    REGULATOR_EVENT_ABORT_VOLTAGE_CHANGE)) {
120 		uV = (unsigned long)data;
121 	} else {
122 		return NOTIFY_OK;
123 	}
124 
125 	dev_dbg(supply->iod->dev, "Setting to %d\n", uV);
126 
127 	if (uV > MAX_VOLTAGE_3_3) {
128 		dev_err(supply->iod->dev, "Voltage too high: %d\n", uV);
129 
130 		if (event == REGULATOR_EVENT_PRE_VOLTAGE_CHANGE)
131 			return NOTIFY_BAD;
132 	}
133 
134 	ret = rockchip_iodomain_write(supply, uV);
135 	if (ret && event == REGULATOR_EVENT_PRE_VOLTAGE_CHANGE)
136 		return NOTIFY_BAD;
137 
138 	dev_info(supply->iod->dev, "Setting to %d done\n", uV);
139 	return NOTIFY_OK;
140 }
141 
rk3288_iodomain_init(struct rockchip_iodomain * iod)142 static void rk3288_iodomain_init(struct rockchip_iodomain *iod)
143 {
144 	int ret;
145 	u32 val;
146 
147 	/* if no flash supply we should leave things alone */
148 	if (!iod->supplies[RK3288_SOC_FLASH_SUPPLY_NUM].reg)
149 		return;
150 
151 	/*
152 	 * set flash0 iodomain to also use this framework
153 	 * instead of a special gpio.
154 	 */
155 	val = RK3288_SOC_CON2_FLASH0 | (RK3288_SOC_CON2_FLASH0 << 16);
156 	ret = regmap_write(iod->grf, RK3288_SOC_CON2, val);
157 	if (ret < 0)
158 		dev_warn(iod->dev, "couldn't update flash0 ctrl\n");
159 }
160 
161 /*
162  * On the rk3188 the io-domains are handled by a shared register with the
163  * lower 8 bits being still being continuing drive-strength settings.
164  */
165 static const struct rockchip_iodomain_soc_data soc_data_rk3188 = {
166 	.grf_offset = 0x104,
167 	.supply_names = {
168 		NULL,
169 		NULL,
170 		NULL,
171 		NULL,
172 		NULL,
173 		NULL,
174 		NULL,
175 		NULL,
176 		"ap0",
177 		"ap1",
178 		"cif",
179 		"flash",
180 		"vccio0",
181 		"vccio1",
182 		"lcdc0",
183 		"lcdc1",
184 	},
185 };
186 
187 static const struct rockchip_iodomain_soc_data soc_data_rk3288 = {
188 	.grf_offset = 0x380,
189 	.supply_names = {
190 		"lcdc",		/* LCDC_VDD */
191 		"dvp",		/* DVPIO_VDD */
192 		"flash0",	/* FLASH0_VDD (emmc) */
193 		"flash1",	/* FLASH1_VDD (sdio1) */
194 		"wifi",		/* APIO3_VDD  (sdio0) */
195 		"bb",		/* APIO5_VDD */
196 		"audio",	/* APIO4_VDD */
197 		"sdcard",	/* SDMMC0_VDD (sdmmc) */
198 		"gpio30",	/* APIO1_VDD */
199 		"gpio1830",	/* APIO2_VDD */
200 	},
201 	.init = rk3288_iodomain_init,
202 };
203 
204 static const struct of_device_id rockchip_iodomain_match[] = {
205 	{
206 		.compatible = "rockchip,rk3188-io-voltage-domain",
207 		.data = (void *)&soc_data_rk3188
208 	},
209 	{
210 		.compatible = "rockchip,rk3288-io-voltage-domain",
211 		.data = (void *)&soc_data_rk3288
212 	},
213 	{ /* sentinel */ },
214 };
215 
rockchip_iodomain_probe(struct platform_device * pdev)216 static int rockchip_iodomain_probe(struct platform_device *pdev)
217 {
218 	struct device_node *np = pdev->dev.of_node;
219 	const struct of_device_id *match;
220 	struct rockchip_iodomain *iod;
221 	int i, ret = 0;
222 
223 	if (!np)
224 		return -ENODEV;
225 
226 	iod = devm_kzalloc(&pdev->dev, sizeof(*iod), GFP_KERNEL);
227 	if (!iod)
228 		return -ENOMEM;
229 
230 	iod->dev = &pdev->dev;
231 	platform_set_drvdata(pdev, iod);
232 
233 	match = of_match_node(rockchip_iodomain_match, np);
234 	iod->soc_data = (struct rockchip_iodomain_soc_data *)match->data;
235 
236 	iod->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
237 	if (IS_ERR(iod->grf)) {
238 		dev_err(&pdev->dev, "couldn't find grf regmap\n");
239 		return PTR_ERR(iod->grf);
240 	}
241 
242 	for (i = 0; i < MAX_SUPPLIES; i++) {
243 		const char *supply_name = iod->soc_data->supply_names[i];
244 		struct rockchip_iodomain_supply *supply = &iod->supplies[i];
245 		struct regulator *reg;
246 		int uV;
247 
248 		if (!supply_name)
249 			continue;
250 
251 		reg = devm_regulator_get_optional(iod->dev, supply_name);
252 		if (IS_ERR(reg)) {
253 			ret = PTR_ERR(reg);
254 
255 			/* If a supply wasn't specified, that's OK */
256 			if (ret == -ENODEV)
257 				continue;
258 			else if (ret != -EPROBE_DEFER)
259 				dev_err(iod->dev, "couldn't get regulator %s\n",
260 					supply_name);
261 			goto unreg_notify;
262 		}
263 
264 		/* set initial correct value */
265 		uV = regulator_get_voltage(reg);
266 
267 		/* must be a regulator we can get the voltage of */
268 		if (uV < 0) {
269 			dev_err(iod->dev, "Can't determine voltage: %s\n",
270 				supply_name);
271 			goto unreg_notify;
272 		}
273 
274 		if (uV > MAX_VOLTAGE_3_3) {
275 			dev_crit(iod->dev,
276 				 "%d uV is too high. May damage SoC!\n",
277 				 uV);
278 			ret = -EINVAL;
279 			goto unreg_notify;
280 		}
281 
282 		/* setup our supply */
283 		supply->idx = i;
284 		supply->iod = iod;
285 		supply->reg = reg;
286 		supply->nb.notifier_call = rockchip_iodomain_notify;
287 
288 		ret = rockchip_iodomain_write(supply, uV);
289 		if (ret) {
290 			supply->reg = NULL;
291 			goto unreg_notify;
292 		}
293 
294 		/* register regulator notifier */
295 		ret = regulator_register_notifier(reg, &supply->nb);
296 		if (ret) {
297 			dev_err(&pdev->dev,
298 				"regulator notifier request failed\n");
299 			supply->reg = NULL;
300 			goto unreg_notify;
301 		}
302 	}
303 
304 	if (iod->soc_data->init)
305 		iod->soc_data->init(iod);
306 
307 	return 0;
308 
309 unreg_notify:
310 	for (i = MAX_SUPPLIES - 1; i >= 0; i--) {
311 		struct rockchip_iodomain_supply *io_supply = &iod->supplies[i];
312 
313 		if (io_supply->reg)
314 			regulator_unregister_notifier(io_supply->reg,
315 						      &io_supply->nb);
316 	}
317 
318 	return ret;
319 }
320 
rockchip_iodomain_remove(struct platform_device * pdev)321 static int rockchip_iodomain_remove(struct platform_device *pdev)
322 {
323 	struct rockchip_iodomain *iod = platform_get_drvdata(pdev);
324 	int i;
325 
326 	for (i = MAX_SUPPLIES - 1; i >= 0; i--) {
327 		struct rockchip_iodomain_supply *io_supply = &iod->supplies[i];
328 
329 		if (io_supply->reg)
330 			regulator_unregister_notifier(io_supply->reg,
331 						      &io_supply->nb);
332 	}
333 
334 	return 0;
335 }
336 
337 static struct platform_driver rockchip_iodomain_driver = {
338 	.probe   = rockchip_iodomain_probe,
339 	.remove  = rockchip_iodomain_remove,
340 	.driver  = {
341 		.name  = "rockchip-iodomain",
342 		.of_match_table = rockchip_iodomain_match,
343 	},
344 };
345 
346 module_platform_driver(rockchip_iodomain_driver);
347 
348 MODULE_DESCRIPTION("Rockchip IO-domain driver");
349 MODULE_AUTHOR("Heiko Stuebner <heiko@sntech.de>");
350 MODULE_AUTHOR("Doug Anderson <dianders@chromium.org>");
351 MODULE_LICENSE("GPL v2");
352