xref: /openbsd-src/sys/dev/fdt/rkpinctrl.c (revision ae3cb403620ab940fbaabb3055fac045a63d56b7)
1 /*	$OpenBSD: rkpinctrl.c,v 1.4 2017/07/23 17:08:29 kettenis Exp $	*/
2 /*
3  * Copyright (c) 2017 Mark Kettenis <kettenis@openbsd.org>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <sys/param.h>
19 #include <sys/systm.h>
20 #include <sys/device.h>
21 #include <sys/malloc.h>
22 
23 #include <machine/intr.h>
24 #include <machine/bus.h>
25 #include <machine/fdt.h>
26 
27 #include <dev/ofw/openfirm.h>
28 #include <dev/ofw/ofw_misc.h>
29 #include <dev/ofw/ofw_pinctrl.h>
30 #include <dev/ofw/fdt.h>
31 
32 #ifdef __armv7__
33 #include <arm/simplebus/simplebusvar.h>
34 #else
35 #include <arm64/dev/simplebusvar.h>
36 #endif
37 
38 /* RK3288 registers */
39 #define RK3288_GRF_GPIO1A_IOMUX		0x0000
40 #define RK3288_PMUGRF_GPIO0A_IOMUX	0x0084
41 
42 /* RK3399 registers */
43 #define RK3399_GRF_GPIO2A_IOMUX		0xe000
44 #define RK3399_PMUGRF_GPIO0A_IOMUX	0x0000
45 
46 struct rkpinctrl_softc {
47 	struct simplebus_softc	sc_sbus;
48 
49 	struct regmap		*sc_grf;
50 	struct regmap		*sc_pmu;
51 };
52 
53 int	rkpinctrl_match(struct device *, void *, void *);
54 void	rkpinctrl_attach(struct device *, struct device *, void *);
55 
56 struct cfattach	rkpinctrl_ca = {
57 	sizeof (struct rkpinctrl_softc), rkpinctrl_match, rkpinctrl_attach
58 };
59 
60 struct cfdriver rkpinctrl_cd = {
61 	NULL, "rkpinctrl", DV_DULL
62 };
63 
64 int	rk3288_pinctrl(uint32_t, void *);
65 int	rk3399_pinctrl(uint32_t, void *);
66 
67 int
68 rkpinctrl_match(struct device *parent, void *match, void *aux)
69 {
70 	struct fdt_attach_args *faa = aux;
71 
72 	return (OF_is_compatible(faa->fa_node, "rockchip,rk3288-pinctrl") ||
73 	    OF_is_compatible(faa->fa_node, "rockchip,rk3399-pinctrl"));
74 }
75 
76 void
77 rkpinctrl_attach(struct device *parent, struct device *self, void *aux)
78 {
79 	struct rkpinctrl_softc *sc = (struct rkpinctrl_softc *)self;
80 	struct fdt_attach_args *faa = aux;
81 	uint32_t grf, pmu;
82 
83 	grf = OF_getpropint(faa->fa_node, "rockchip,grf", 0);
84 	pmu = OF_getpropint(faa->fa_node, "rockchip,pmu", 0);
85 	sc->sc_grf = regmap_byphandle(grf);
86 	sc->sc_pmu = regmap_byphandle(pmu);
87 
88 	if (sc->sc_grf == NULL || sc->sc_pmu == NULL) {
89 		printf(": no registers\n");
90 		return;
91 	}
92 
93 	if (OF_is_compatible(faa->fa_node, "rockchip,rk3288-pinctrl"))
94 		pinctrl_register(faa->fa_node, rk3288_pinctrl, sc);
95 	else
96 		pinctrl_register(faa->fa_node, rk3399_pinctrl, sc);
97 
98 	/* Attach GPIO banks. */
99 	simplebus_attach(parent, &sc->sc_sbus.sc_dev, faa);
100 }
101 
102 /*
103  * Rockchip RK3288
104  */
105 
106 int
107 rk3288_pull(uint32_t bank, uint32_t idx, uint32_t phandle)
108 {
109 	int node;
110 
111 	node = OF_getnodebyphandle(phandle);
112 	if (node == 0)
113 		return -1;
114 
115 	/* XXX */
116 	if (bank == 0)
117 		return -1;
118 
119 	if (OF_getproplen(node, "bias-disable") == 0)
120 		return 0;
121 	if (OF_getproplen(node, "bias-pull-up") == 0)
122 		return 1;
123 	if (OF_getproplen(node, "bias-pull-down") == 0)
124 		return 2;
125 
126 	return -1;
127 }
128 
129 int
130 rk3288_strength(uint32_t bank, uint32_t idx, uint32_t phandle)
131 {
132 	int strength, level;
133 	int levels[4] = { 2, 4, 8, 12 };
134 	int node;
135 
136 	node = OF_getnodebyphandle(phandle);
137 	if (node == 0)
138 		return -1;
139 
140 	/* XXX */
141 	if (bank == 0)
142 		return -1;
143 
144 	strength = OF_getpropint(node, "drive-strength", -1);
145 	if (strength == -1)
146 		return -1;
147 
148 	/* Convert drive strength to level. */
149 	for (level = 3; level >= 0; level--) {
150 		if (strength >= levels[level])
151 			break;
152 	}
153 	return level;
154 }
155 
156 int
157 rk3288_pinctrl(uint32_t phandle, void *cookie)
158 {
159 	struct rkpinctrl_softc *sc = cookie;
160 	uint32_t *pins;
161 	int node, len, i;
162 
163 	node = OF_getnodebyphandle(phandle);
164 	if (node == 0)
165 		return -1;
166 
167 	len = OF_getproplen(node, "rockchip,pins");
168 	if (len <= 0)
169 		return -1;
170 
171 	pins = malloc(len, M_TEMP, M_WAITOK);
172 	if (OF_getpropintarray(node, "rockchip,pins", pins, len) != len)
173 		goto fail;
174 
175 	for (i = 0; i < len / sizeof(uint32_t); i += 4) {
176 		struct regmap *rm;
177 		bus_size_t base, off;
178 		uint32_t bank, idx, mux;
179 		int pull, strength;
180 		uint32_t mask, bits;
181 		int s;
182 
183 		bank = pins[i];
184 		idx = pins[i + 1];
185 		mux = pins[i + 2];
186 		pull = rk3288_pull(bank, idx, pins[i + 3]);
187 		strength = rk3288_strength(bank, idx, pins[i + 3]);
188 
189 		if (bank > 8 || idx > 32 || mux > 7)
190 			continue;
191 
192 		/* Bank 0 lives in the PMU. */
193 		if (bank < 1) {
194 			rm = sc->sc_pmu;
195 			base = RK3288_PMUGRF_GPIO0A_IOMUX;
196 		} else {
197 			rm = sc->sc_grf;
198 			base = RK3288_GRF_GPIO1A_IOMUX - 0x10;
199 		}
200 
201 		s = splhigh();
202 
203 		/* IOMUX control */
204 		off = bank * 0x10 + (idx / 8) * 0x04;
205 
206 		/* GPIO3D, GPIO4A and GPIO4B are special. */
207 		if ((bank == 3 && idx >= 24) || (bank == 4 && idx < 16)) {
208 			mask = (0x7 << ((idx % 4) * 4));
209 			bits = (mux << ((idx % 4) * 4));
210 		} else {
211 			mask = (0x3 << ((idx % 8) * 2));
212 			bits = (mux << ((idx % 8) * 2));
213 		}
214 		if (bank > 3 || (bank == 3 && idx >= 28))
215 			off += 0x04;
216 		if (bank > 4 || (bank == 4 && idx >= 4))
217 			off += 0x04;
218 		if (bank > 4 || (bank == 4 && idx >= 12))
219 			off += 0x04;
220 		regmap_write_4(rm, base + off, mask << 16 | bits);
221 
222 		/* GPIO pad pull down and pull up control */
223 		if (pull >= 0) {
224 			off = 0x140 + bank * 0x10 + (idx / 8) * 0x04;
225 			mask = (0x3 << ((idx % 8) * 2));
226 			bits = (pull << ((idx % 8) * 2));
227 			regmap_write_4(rm, base + off, mask << 16 | bits);
228 		}
229 
230 		/* GPIO drive strength control */
231 		if (strength >= 0) {
232 			off = 0x1c0 + bank * 0x10 + (idx / 8) * 0x04;
233 			mask = (0x3 << ((idx % 8) * 2));
234 			bits = (strength << ((idx % 8) * 2));
235 			regmap_write_4(rm, base + off, mask << 16 | bits);
236 		}
237 
238 		splx(s);
239 	}
240 
241 	free(pins, M_TEMP, len);
242 	return 0;
243 
244 fail:
245 	free(pins, M_TEMP, len);
246 	return -1;
247 }
248 
249 /*
250  * Rockchip RK3399
251  */
252 
253 int
254 rk3399_pull(uint32_t bank, uint32_t idx, uint32_t phandle)
255 {
256 	int pull_up, pull_down;
257 	int node;
258 
259 	node = OF_getnodebyphandle(phandle);
260 	if (node == 0)
261 		return -1;
262 
263 	if (bank == 2 && idx >= 16) {
264 		pull_up = 3;
265 		pull_down = 1;
266 	} else {
267 		pull_up = 1;
268 		pull_down = 2;
269 	}
270 
271 	if (OF_getproplen(node, "bias-disable") == 0)
272 		return 0;
273 	if (OF_getproplen(node, "bias-pull-up") == 0)
274 		return pull_up;
275 	if (OF_getproplen(node, "bias-pull-down") == 0)
276 		return pull_down;
277 
278 	return -1;
279 }
280 
281 /* Magic because the drive strength configurations vary wildly. */
282 
283 int rk3399_strength_levels[][8] = {
284 	{ 2, 4, 8, 12 },			/* default */
285 	{ 3, 6, 9, 12 },			/* 1.8V or 3.0V */
286 	{ 5, 10, 15, 20 },			/* 1.8V only */
287 	{ 4, 6, 8, 10, 12, 14, 16, 18 },	/* 1.8V or 3.0V auto */
288 	{ 4, 7, 10, 13, 16, 19, 22, 26 },	/* 3.3V */
289 };
290 
291 int rk3399_strength_types[][4] = {
292 	{ 2, 2, 0, 0 },
293 	{ 1, 1, 1, 1 },
294 	{ 1, 1, 2, 2 },
295 	{ 4, 4, 4, 1 },
296 	{ 1, 3, 1, 1 },
297 };
298 
299 int rk3399_strength_regs[][4] = {
300 	{ 0x0080, 0x0088, 0x0090, 0x0098 },
301 	{ 0x00a0, 0x00a8, 0x00b0, 0x00b8 },
302 	{ 0x0100, 0x0104, 0x0108, 0x010c },
303 	{ 0x0110, 0x0118, 0x0120, 0x0128 },
304 	{ 0x012c, 0x0130, 0x0138, 0x013c },
305 };
306 
307 int
308 rk3399_strength(uint32_t bank, uint32_t idx, uint32_t phandle)
309 {
310 	int strength, type, level;
311 	int *levels;
312 	int node;
313 
314 	node = OF_getnodebyphandle(phandle);
315 	if (node == 0)
316 		return -1;
317 
318 	strength = OF_getpropint(node, "drive-strength", -1);
319 	if (strength == -1)
320 		return -1;
321 
322 	/* Convert drive strength to level. */
323 	type = rk3399_strength_types[bank][idx / 8];
324 	levels = rk3399_strength_levels[type];
325 	for (level = 7; level >= 0; level--) {
326 		if (strength >= levels[level] && levels[level] > 0)
327 			break;
328 	}
329 	return level;
330 }
331 
332 int
333 rk3399_pinctrl(uint32_t phandle, void *cookie)
334 {
335 	struct rkpinctrl_softc *sc = cookie;
336 	uint32_t *pins;
337 	int node, len, i;
338 
339 	node = OF_getnodebyphandle(phandle);
340 	if (node == 0)
341 		return -1;
342 
343 	len = OF_getproplen(node, "rockchip,pins");
344 	if (len <= 0)
345 		return -1;
346 
347 	pins = malloc(len, M_TEMP, M_WAITOK);
348 	if (OF_getpropintarray(node, "rockchip,pins", pins, len) != len)
349 		goto fail;
350 
351 	for (i = 0; i < len / sizeof(uint32_t); i += 4) {
352 		struct regmap *rm;
353 		bus_size_t base, off;
354 		uint32_t bank, idx, mux;
355 		int pull, strength, type, shift;
356 		uint32_t mask, bits;
357 		int s;
358 
359 		bank = pins[i];
360 		idx = pins[i + 1];
361 		mux = pins[i + 2];
362 		pull = rk3399_pull(bank, idx, pins[i + 3]);
363 		strength = rk3399_strength(bank, idx, pins[i + 3]);
364 
365 		if (bank > 5 || idx > 32 || mux > 3)
366 			continue;
367 
368 		/* Bank 0 and 1 live in the PMU. */
369 		if (bank < 2) {
370 			rm = sc->sc_pmu;
371 			base = RK3399_PMUGRF_GPIO0A_IOMUX;
372 		} else {
373 			rm = sc->sc_grf;
374 			base = RK3399_GRF_GPIO2A_IOMUX - 0x20;
375 		}
376 
377 		s = splhigh();
378 
379 		/* IOMUX control */
380 		off = bank * 0x10 + (idx / 8) * 0x04;
381 		mask = (0x3 << ((idx % 8) * 2));
382 		bits = (mux << ((idx % 8) * 2));
383 		regmap_write_4(rm, base + off, mask << 16 | bits);
384 
385 		/* GPIO pad pull down and pull up control */
386 		if (pull >= 0) {
387 			off = 0x40 + bank * 0x10 + (idx / 8) * 0x04;
388 			mask = (0x3 << ((idx % 8) * 2));
389 			bits = (pull << ((idx % 8) * 2));
390 			regmap_write_4(rm, base + off, mask << 16 | bits);
391 		}
392 
393 		/* GPIO drive strength control */
394 		if (strength >= 0) {
395 			off = rk3399_strength_regs[bank][idx / 8];
396 			type = rk3399_strength_types[bank][idx / 8];
397 			shift = (type > 2) ? 3 : 2;
398 			mask = (((1 << shift) - 1) << ((idx % 8) * shift));
399 			bits = (strength << ((idx % 8) * shift));
400 			if (mask & 0x0000ffff) {
401 				regmap_write_4(rm, base + off,
402 				    mask << 16 | (bits & 0x0000ffff));
403 			}
404 			if (mask & 0xffff0000) {
405 				regmap_write_4(rm, base + off + 0x04,
406 				    (mask & 0xffff0000) | bits >> 16);
407 			}
408 		}
409 
410 		splx(s);
411 	}
412 
413 	free(pins, M_TEMP, len);
414 	return 0;
415 
416 fail:
417 	free(pins, M_TEMP, len);
418 	return -1;
419 }
420