rk3399_iomux.c revision 1.1.2.2 1 /* $NetBSD: rk3399_iomux.c,v 1.1.2.2 2018/09/06 06:55:27 pgoyette Exp $ */
2
3 /*-
4 * Copyright (c) 2018 Jared McNeill <jmcneill (at) invisible.ca>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29 //#define RK3399_IOMUX_DEBUG
30
31 #include <sys/cdefs.h>
32 __KERNEL_RCSID(0, "$NetBSD: rk3399_iomux.c,v 1.1.2.2 2018/09/06 06:55:27 pgoyette Exp $");
33
34 #include <sys/param.h>
35 #include <sys/bus.h>
36 #include <sys/device.h>
37 #include <sys/intr.h>
38 #include <sys/systm.h>
39 #include <sys/mutex.h>
40 #include <sys/kmem.h>
41 #include <sys/gpio.h>
42 #include <sys/lwp.h>
43
44 #include <dev/fdt/fdtvar.h>
45 #include <dev/fdt/syscon.h>
46
47 /* PU/PD control */
48 #define GRF_GPIO_P_CTL(_idx) (0x3 << (((_idx) & 7) * 2))
49 #define GRF_GPIO_P_WRITE_EN(_idx) (0x3 << (((_idx) & 7) * 2 + 16))
50 /* Different bias value mapping for GRF and PMU registers */
51 #define GRF_GPIO_P_CTL_Z 0
52 #define GRF_GPIO_P_CTL_PULLDOWN 1
53 #define GRF_GPIO_P_CTL_Z_ALT 2
54 #define GRF_GPIO_P_CTL_PULLUP 3
55 #define PMU_GPIO_P_CTL_Z 0
56 #define PMU_GPIO_P_CTL_PULLUP 1
57 #define PMU_GPIO_P_CTL_PULLDOWN 2
58 #define PMU_GPIO_P_CTL_RESERVED 3
59
60 /* Drive strength control */
61 /* Different drive strength value mapping for GRF and PMU registers */
62 #define GRF_GPIO_E_CTL_2MA 0
63 #define GRF_GPIO_E_CTL_4MA 1
64 #define GRF_GPIO_E_CTL_8MA 2
65 #define GRF_GPIO_E_CTL_12MA 3
66 #define PMU_GPIO_E_CTL_5MA 0
67 #define PMU_GPIO_E_CTL_10MA 1
68 #define PMU_GPIO_E_CTL_15MA 2
69 #define PMU_GPIO_E_CTL_20MA 3
70
71 enum rk3399_drv_type {
72 RK3399_DRV_TYPE_IO_DEFAULT,
73 RK3399_DRV_TYPE_IO_1V8_3V0,
74 RK3399_DRV_TYPE_IO_1V8,
75 RK3399_DRV_TYPE_IO_1V8_3V0_AUTO,
76 RK3399_DRV_TYPE_IO_3V3,
77 };
78
79 static int rk3399_drv_strength[5][9] = {
80 [RK3399_DRV_TYPE_IO_DEFAULT] = { 2, 4, 8, 12, -1 },
81 [RK3399_DRV_TYPE_IO_1V8_3V0] = { 3, 6, 9, 12, -1 },
82 [RK3399_DRV_TYPE_IO_1V8] = { 5, 10, 15, 20, -1 },
83 [RK3399_DRV_TYPE_IO_1V8_3V0_AUTO] = { 4, 6, 8, 10, 12, 14, 16, 18, -1 },
84 [RK3399_DRV_TYPE_IO_3V3] = { 4, 7, 10, 13, 16, 19, 22, 26, -1 },
85 };
86
87 struct rk3399_iomux {
88 enum rk3399_drv_type drv_type;
89 };
90
91 struct rk3399_iomux_bank {
92 struct rk3399_iomux iomux[5];
93 u_int regs;
94 #define RK_IOMUX_REGS_GRF 0
95 #define RK_IOMUX_REGS_PMU 1
96 };
97
98 static const struct rk3399_iomux_bank rk3399_iomux_banks[] = {
99 [0] = {
100 .regs = RK_IOMUX_REGS_PMU,
101 .iomux = {
102 [0] = { .drv_type = RK3399_DRV_TYPE_IO_1V8 },
103 [1] = { .drv_type = RK3399_DRV_TYPE_IO_1V8 },
104 [2] = { .drv_type = RK3399_DRV_TYPE_IO_DEFAULT },
105 [3] = { .drv_type = RK3399_DRV_TYPE_IO_DEFAULT },
106 },
107 },
108 [1] = {
109 .regs = RK_IOMUX_REGS_PMU,
110 .iomux = {
111 [0] = { .drv_type = RK3399_DRV_TYPE_IO_1V8_3V0 },
112 [1] = { .drv_type = RK3399_DRV_TYPE_IO_1V8_3V0 },
113 [2] = { .drv_type = RK3399_DRV_TYPE_IO_1V8_3V0 },
114 [3] = { .drv_type = RK3399_DRV_TYPE_IO_1V8_3V0 },
115 }
116 },
117 [2] = {
118 .regs = RK_IOMUX_REGS_GRF,
119 .iomux = {
120 [0] = { .drv_type = RK3399_DRV_TYPE_IO_1V8_3V0 },
121 [1] = { .drv_type = RK3399_DRV_TYPE_IO_1V8_3V0 },
122 [2] = { .drv_type = RK3399_DRV_TYPE_IO_1V8 },
123 [3] = { .drv_type = RK3399_DRV_TYPE_IO_1V8 },
124 },
125 },
126 [3] = {
127 .regs = RK_IOMUX_REGS_GRF,
128 .iomux = {
129 [0] = { .drv_type = RK3399_DRV_TYPE_IO_3V3 },
130 [1] = { .drv_type = RK3399_DRV_TYPE_IO_3V3 },
131 [2] = { .drv_type = RK3399_DRV_TYPE_IO_3V3 },
132 [3] = { .drv_type = RK3399_DRV_TYPE_IO_1V8_3V0 },
133 },
134 },
135 [4] = {
136 .regs = RK_IOMUX_REGS_GRF,
137 .iomux = {
138 [0] = { .drv_type = RK3399_DRV_TYPE_IO_1V8_3V0 },
139 [1] = { .drv_type = RK3399_DRV_TYPE_IO_1V8_3V0_AUTO },
140 [2] = { .drv_type = RK3399_DRV_TYPE_IO_1V8_3V0 },
141 [3] = { .drv_type = RK3399_DRV_TYPE_IO_1V8_3V0 },
142 },
143 },
144 };
145
146 #define RK3399_IOMUX_BANK_IS_PMU(_bank) (rk3399_iomux_banks[(_bank)].regs == RK_IOMUX_REGS_PMU)
147
148 struct rk3399_iomux_conf {
149 const struct rk3399_iomux_bank *banks;
150 u_int nbanks;
151 };
152
153 static const struct rk3399_iomux_conf rk3399_iomux_conf = {
154 .banks = rk3399_iomux_banks,
155 .nbanks = __arraycount(rk3399_iomux_banks),
156 };
157
158 static const struct of_compat_data compat_data[] = {
159 { "rockchip,rk3399-pinctrl", (uintptr_t)&rk3399_iomux_conf },
160 { NULL }
161 };
162
163 struct rk3399_iomux_softc {
164 device_t sc_dev;
165 struct syscon *sc_syscon[2];
166
167 const struct rk3399_iomux_conf *sc_conf;
168 };
169
170 #define LOCK(syscon) \
171 syscon_lock(syscon)
172 #define UNLOCK(syscon) \
173 syscon_unlock(syscon)
174 #define RD4(syscon, reg) \
175 syscon_read_4(syscon, (reg))
176 #define WR4(syscon, reg, val) \
177 syscon_write_4(syscon, (reg), (val))
178
179 static int rk3399_iomux_match(device_t, cfdata_t, void *);
180 static void rk3399_iomux_attach(device_t, device_t, void *);
181
182 CFATTACH_DECL_NEW(rk3399_iomux, sizeof(struct rk3399_iomux_softc),
183 rk3399_iomux_match, rk3399_iomux_attach, NULL, NULL);
184
185 static void
186 rk3399_iomux_set_bias(struct rk3399_iomux_softc *sc, u_int bank, u_int idx, int flags)
187 {
188 const struct rk3399_iomux_bank *banks = sc->sc_conf->banks;
189 bus_size_t reg;
190 u_int bias;
191
192 KASSERT(bank < sc->sc_conf->nbanks);
193
194 struct syscon * const syscon = sc->sc_syscon[banks[bank].regs];
195 if (RK3399_IOMUX_BANK_IS_PMU(bank)) {
196 reg = 0x00040 + (0x10 * bank);
197 } else {
198 reg = 0x0e040 + (0x10 * (bank - 2));
199 }
200 reg += 0x4 * (idx / 8);
201
202 if (flags == GPIO_PIN_PULLUP) {
203 bias = RK3399_IOMUX_BANK_IS_PMU(bank) ? PMU_GPIO_P_CTL_PULLUP : GRF_GPIO_P_CTL_PULLUP;
204 } else if (flags == GPIO_PIN_PULLDOWN) {
205 bias = RK3399_IOMUX_BANK_IS_PMU(bank) ? PMU_GPIO_P_CTL_PULLDOWN : GRF_GPIO_P_CTL_PULLDOWN;
206 } else {
207 bias = RK3399_IOMUX_BANK_IS_PMU(bank) ? PMU_GPIO_P_CTL_Z : GRF_GPIO_P_CTL_Z;
208 }
209
210 const uint32_t bias_val = __SHIFTIN(bias, GRF_GPIO_P_CTL(idx));
211 const uint32_t bias_mask = GRF_GPIO_P_WRITE_EN(idx);
212
213 #ifdef RK3399_IOMUX_DEBUG
214 printf("%s: bank %d idx %d flags %#x: %08x -> ", __func__, bank, idx, flags, RD4(syscon, reg));
215 #endif
216 WR4(syscon, reg, bias_val | bias_mask);
217 #ifdef RK3399_IOMUX_DEBUG
218 printf("%08x (reg %#lx)\n", RD4(syscon, reg), reg);
219 #endif
220 }
221
222 static int
223 rk3399_iomux_map_drive_strength(struct rk3399_iomux_softc *sc, enum rk3399_drv_type drv_type, u_int val)
224 {
225 for (int n = 0; rk3399_drv_strength[drv_type][n] != -1; n++)
226 if (rk3399_drv_strength[drv_type][n] == val)
227 return n;
228 return -1;
229 }
230
231 static int
232 rk3399_iomux_set_drive_strength(struct rk3399_iomux_softc *sc, u_int bank, u_int idx, u_int val)
233 {
234 const struct rk3399_iomux_bank *banks = sc->sc_conf->banks;
235 uint32_t drv_mask, drv_val;
236 bus_size_t reg;
237
238 KASSERT(bank < sc->sc_conf->nbanks);
239
240 if (idx >= 32)
241 return EINVAL;
242
243 const int drv = rk3399_iomux_map_drive_strength(sc, banks[bank].iomux[idx / 8].drv_type, val);
244 if (drv == -1)
245 return EINVAL;
246
247 struct syscon * const syscon = sc->sc_syscon[banks[bank].regs];
248 switch (bank) {
249 case 0:
250 case 1:
251 reg = 0x00040 + (0x10 * bank) + 0x4 * (idx / 4);
252 drv_mask = 0x3 << ((idx & 7) * 2);
253 break;
254 case 2:
255 reg = 0x0e100 + 0x4 * (idx / 4);
256 drv_mask = 0x3 << ((idx & 7) * 2);
257 break;
258 case 3:
259 switch (idx / 8) {
260 case 0:
261 case 1:
262 case 2:
263 reg = 0x0e110 + 0x8 * (idx / 4);
264 drv_mask = 0x7 << ((idx & 7) * 3);
265 break;
266 case 3:
267 reg = 0x0e128;
268 drv_mask = 0x3 << ((idx & 7) * 2);
269 break;
270 default:
271 return EINVAL;
272 }
273 break;
274 case 4:
275 switch (idx / 8) {
276 case 0:
277 reg = 0x0e12c;
278 drv_mask = 0x3 << ((idx & 7) * 2);
279 break;
280 case 1:
281 reg = 0x0e130;
282 drv_mask = 0x7 << ((idx & 7) * 3);
283 break;
284 case 2:
285 reg = 0x0e138;
286 drv_mask = 0x3 << ((idx & 7) * 2);
287 break;
288 case 3:
289 reg = 0x0e13c;
290 drv_mask = 0x3 << ((idx & 7) * 2);
291 break;
292 default:
293 return EINVAL;
294 }
295 break;
296 default:
297 return EINVAL;
298 }
299 drv_val = __SHIFTIN(val, drv_mask);
300
301 while (drv_mask) {
302 const uint32_t write_val = drv_val & 0xffff;
303 const uint32_t write_mask = (drv_mask & 0xffff) << 16;
304 if (write_mask) {
305 #ifdef RK3399_IOMUX_DEBUG
306 printf("%s: bank %d idx %d val %d: %08x -> ", __func__, bank, idx, val, RD4(syscon, reg));
307 #endif
308 WR4(syscon, reg, write_val | write_mask);
309 #ifdef RK3399_IOMUX_DEBUG
310 printf("%08x (reg %#lx)\n", RD4(syscon, reg), reg);
311 #endif
312 }
313 reg += 0x4;
314 drv_val >>= 16;
315 drv_mask >>= 16;
316 }
317
318 return 0;
319 }
320
321 static void
322 rk3399_iomux_set_mux(struct rk3399_iomux_softc *sc, u_int bank, u_int idx, u_int mux)
323 {
324 const struct rk3399_iomux_bank *banks = sc->sc_conf->banks;
325 bus_size_t reg;
326 uint32_t mask;
327
328 KASSERT(bank < sc->sc_conf->nbanks);
329
330 struct syscon * const syscon = sc->sc_syscon[banks[bank].regs];
331 if (RK3399_IOMUX_BANK_IS_PMU(bank)) {
332 reg = 0x00000 + (0x10 * bank);
333 } else {
334 reg = 0x0e000 + (0x10 * (bank - 2));
335 }
336 reg += 0x4 * (idx / 4);
337 mask = 3 << ((idx & 7) * 2);
338
339 #ifdef RK3399_IOMUX_DEBUG
340 printf("%s: bank %d idx %d mux %#x: %08x -> ", __func__, bank, idx, mux, RD4(syscon, reg));
341 #endif
342 WR4(syscon, reg, (mask << 16) | __SHIFTIN(mux, mask));
343 #ifdef RK3399_IOMUX_DEBUG
344 printf("%08x (reg %#lx)\n", RD4(syscon, reg), reg);
345 #endif
346 }
347
348 static int
349 rk3399_iomux_config(struct rk3399_iomux_softc *sc, const int phandle, u_int bank, u_int idx, u_int mux)
350 {
351 u_int drv;
352
353 if (of_hasprop(phandle, "bias-disable"))
354 rk3399_iomux_set_bias(sc, bank, idx, 0);
355 else if (of_hasprop(phandle, "bias-pull-up"))
356 rk3399_iomux_set_bias(sc, bank, idx, GPIO_PIN_PULLUP);
357 else if (of_hasprop(phandle, "bias-pull-down"))
358 rk3399_iomux_set_bias(sc, bank, idx, GPIO_PIN_PULLDOWN);
359
360 if (of_getprop_uint32(phandle, "drive-strength", &drv) == 0) {
361 if (rk3399_iomux_set_drive_strength(sc, bank, idx, drv) != 0)
362 return EINVAL;
363 }
364
365 #if notyet
366 if (of_hasprop(phandle, "input-enable"))
367 rk3399_iomux_set_direction(sc, bank, idx, GPIO_PIN_INPUT, -1);
368 else if (of_hasprop(phandle, "output-high"))
369 rk3399_iomux_set_direction(sc, bank, idx, GPIO_PIN_OUTPUT, GPIO_PIN_HIGH);
370 else if (of_hasprop(phandle, "output-low"))
371 rk3399_iomux_set_direction(sc, bank, idx, GPIO_PIN_OUTPUT, GPIO_PIN_LOW);
372 #endif
373
374 rk3399_iomux_set_mux(sc, bank, idx, mux);
375
376 return 0;
377 }
378
379 static int
380 rk3399_iomux_pinctrl_set_config(device_t dev, const void *data, size_t len)
381 {
382 struct rk3399_iomux_softc * const sc = device_private(dev);
383 const struct rk3399_iomux_bank *banks = sc->sc_conf->banks;
384 int pins_len;
385
386 if (len != 4)
387 return -1;
388
389 const int phandle = fdtbus_get_phandle_from_native(be32dec(data));
390 const u_int *pins = fdtbus_get_prop(phandle, "rockchip,pins", &pins_len);
391
392 while (pins_len >= 16) {
393 const u_int bank = be32toh(pins[0]);
394 const u_int idx = be32toh(pins[1]);
395 const u_int mux = be32toh(pins[2]);
396 const int cfg = fdtbus_get_phandle_from_native(be32toh(pins[3]));
397
398 struct syscon * const syscon = sc->sc_syscon[banks[bank].regs];
399 LOCK(syscon);
400 rk3399_iomux_config(sc, cfg, bank, idx, mux);
401 UNLOCK(syscon);
402
403 pins_len -= 16;
404 pins += 4;
405 }
406
407 return 0;
408 }
409
410 static struct fdtbus_pinctrl_controller_func rk3399_iomux_pinctrl_funcs = {
411 .set_config = rk3399_iomux_pinctrl_set_config,
412 };
413
414 static int
415 rk3399_iomux_match(device_t parent, cfdata_t cf, void *aux)
416 {
417 struct fdt_attach_args * const faa = aux;
418
419 return of_match_compat_data(faa->faa_phandle, compat_data);
420 }
421
422 static void
423 rk3399_iomux_attach(device_t parent, device_t self, void *aux)
424 {
425 struct rk3399_iomux_softc * const sc = device_private(self);
426 struct fdt_attach_args * const faa = aux;
427 const int phandle = faa->faa_phandle;
428 int child, sub;
429
430 sc->sc_dev = self;
431 sc->sc_syscon[RK_IOMUX_REGS_GRF] = fdtbus_syscon_acquire(phandle, "rockchip,grf");
432 if (sc->sc_syscon[RK_IOMUX_REGS_GRF] == NULL) {
433 aprint_error(": couldn't acquire grf syscon\n");
434 return;
435 }
436 sc->sc_syscon[RK_IOMUX_REGS_PMU] = fdtbus_syscon_acquire(phandle, "rockchip,pmu");
437 if (sc->sc_syscon[RK_IOMUX_REGS_PMU] == NULL) {
438 aprint_error(": couldn't acquire pmu syscon\n");
439 return;
440 }
441 sc->sc_conf = (void *)of_search_compatible(phandle, compat_data)->data;
442
443 aprint_naive("\n");
444 aprint_normal(": RK3399 IOMUX control\n");
445
446 for (child = OF_child(phandle); child; child = OF_peer(child)) {
447 for (sub = OF_child(child); sub; sub = OF_peer(sub)) {
448 if (!of_hasprop(sub, "rockchip,pins"))
449 continue;
450 fdtbus_register_pinctrl_config(self, sub, &rk3399_iomux_pinctrl_funcs);
451 }
452 }
453
454 fdtbus_pinctrl_configure();
455
456 for (child = OF_child(phandle); child; child = OF_peer(child)) {
457 struct fdt_attach_args cfaa = *faa;
458 cfaa.faa_phandle = child;
459 cfaa.faa_name = fdtbus_get_string(child, "name");
460 cfaa.faa_quiet = false;
461
462 config_found(self, &cfaa, NULL);
463 }
464 }
465