1 /*-
2 * SPDX-License-Identifier: BSD-3-Clause AND BSD-2-Clause
3 */
4 /*
5 * // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
6 *
7 * Freescale DPAA2 Platforms Console Driver
8 *
9 * Copyright 2015-2016 Freescale Semiconductor Inc.
10 * Copyright 2018 NXP
11 *
12 * git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/drivers/soc/fsl/dpaa2-console.c#8120bd469f5525da229953c1197f2b826c0109f4
13 */
14 /*
15 * Copyright (c) 2022-2023 Bjoern A. Zeeb
16 *
17 * Redistribution and use in source and binary forms, with or without
18 * modification, are permitted provided that the following conditions
19 * are met:
20 * 1. Redistributions of source code must retain the above copyright
21 * notice, this list of conditions and the following disclaimer.
22 * 2. Redistributions in binary form must reproduce the above copyright
23 * notice, this list of conditions and the following disclaimer in the
24 * documentation and/or other materials provided with the distribution.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 * SUCH DAMAGE.
37 */
38
39 /*
40 * Some docs are in:
41 * - https://www.nxp.com.cn/docs/en/application-note/AN13329.pdf
42 * - DPAA2UM
43 * - git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/drivers/soc/fsl/dpaa2-console.c
44 */
45
46 #include "opt_platform.h"
47 #ifdef __notyet__
48 #include "opt_acpi.h"
49 #endif
50
51 #include <sys/cdefs.h>
52 #include <sys/param.h>
53 #include <sys/bus.h>
54 #include <sys/conf.h>
55 #include <sys/fcntl.h>
56 #include <sys/kernel.h>
57 #include <sys/malloc.h>
58 #include <sys/module.h>
59 #include <sys/mutex.h>
60 #include <sys/rman.h>
61 #include <sys/stat.h>
62 #include <sys/uio.h>
63
64 #include <machine/bus.h>
65 #include <machine/resource.h>
66
67 #ifdef FDT
68 #include <dev/ofw/ofw_bus.h>
69 #include <dev/ofw/ofw_bus_subr.h>
70 #endif
71
72 /* Table 6-1. MC Memory Map */
73 #define MC_REG_MCFBA 0x20
74 #define MC_REG_MCFBALR_OFF 0x00
75 #define MC_REG_MCFBALR_MASK 0xe0000000
76 #define MC_REG_MCFBAHR_OFF 0x04
77 #define MC_REG_MCFBAHR_MASK 0x0001ffff
78
79 /* Firmware Consoles. */
80 #define MC_BUFFER_OFFSET 0x01000000
81 #define MC_BUFFER_SIZE (1024 * 1024 * 16)
82 #define MC_OFFSET_DELTA MC_BUFFER_OFFSET
83
84 #define AIOP_BUFFER_OFFSET 0x06000000
85 #define AIOP_BUFFER_SIZE (1024 * 1024 * 16)
86 #define AIOP_OFFSET_DELTA 0
87
88 /* MC and AIOP Magic words */
89 #define MAGIC_MC 0x4d430100
90 #define MAGIC_AIOP 0X41494f50
91
92 #define LOG_HEADER_FLAG_BUFFER_WRAPAROUND \
93 0x80000000
94 #define LAST_BYTE(a) \
95 ((a) & ~(LOG_HEADER_FLAG_BUFFER_WRAPAROUND))
96
97 struct dpaa2_cons_dev {
98 struct cdev *cdev;
99 struct mtx mtx;
100 size_t offset;
101 uint32_t magic;
102
103 uint32_t hdr_magic;
104 uint32_t hdr_eobyte;
105 uint32_t hdr_start;
106 uint32_t hdr_len;
107
108 uoff_t start;
109 uoff_t end;
110 uoff_t eod;
111 uoff_t cur;
112
113 bus_space_tag_t bst;
114 bus_space_handle_t bsh;
115 vm_size_t size;
116 };
117
118 struct dpaa2_cons_softc {
119 struct resource *res;
120 bus_space_tag_t bst;
121 uint64_t mcfba;
122 struct dpaa2_cons_dev mc_cd;
123 struct dpaa2_cons_dev aiop_cd;
124 };
125
126 struct dpaa2_cons_hdr {
127 uint32_t magic;
128 uint32_t _reserved;
129 uint32_t start;
130 uint32_t len;
131 uint32_t eobyte;
132 };
133
134 #define DPAA2_MC_READ_4(_sc, _r) bus_read_4((_sc)->res, (_r))
135
136 /* Management interface */
137 static d_open_t dpaa2_cons_open;
138 static d_close_t dpaa2_cons_close;
139 static d_read_t dpaa2_cons_read;
140
141 static struct cdevsw dpaa2_mc_cons_cdevsw = {
142 .d_version = D_VERSION,
143 .d_flags = 0,
144 .d_open = dpaa2_cons_open,
145 .d_close = dpaa2_cons_close,
146 .d_read = dpaa2_cons_read,
147 .d_name = "fsl_mc_console",
148 };
149
150 static struct cdevsw dpaa2_aiop_cons_cdevsw = {
151 .d_version = D_VERSION,
152 .d_flags = 0,
153 .d_open = dpaa2_cons_open,
154 .d_close = dpaa2_cons_close,
155 .d_read = dpaa2_cons_read,
156 .d_name = "fsl_aiop_console",
157 };
158
159 static size_t
dpaa2_cons_read_bs(struct dpaa2_cons_dev * cd,size_t offset,void * dst,size_t len)160 dpaa2_cons_read_bs(struct dpaa2_cons_dev *cd, size_t offset, void *dst, size_t len)
161 {
162 size_t count, l;
163 uint8_t *p;
164
165 count = 0;
166 p = dst;
167 l = offset % 8;
168 if (l != 0) {
169 bus_space_read_region_1(cd->bst, cd->bsh, offset + count, p + count, l);
170 count += l;
171 len -= l;
172 }
173
174 l = len / 8;
175 if (l != 0) {
176 bus_space_read_region_8(cd->bst, cd->bsh, offset + count, (uint64_t *)(p + count), l);
177 l *= 8;
178 count += l;
179 len -= l;
180 }
181 l = len / 4;
182 if (l != 0) {
183 bus_space_read_region_4(cd->bst, cd->bsh, offset + count, (uint32_t *)(p + count), l);
184 l *= 4;
185 count += l;
186 len -= l;
187 }
188 l = len / 2;
189 if (l != 0) {
190 bus_space_read_region_2(cd->bst, cd->bsh, offset + count, (uint16_t *)(p + count), l);
191 l *= 2;
192 count += l;
193 len -= l;
194 }
195 if (len != 0) {
196 bus_space_read_region_1(cd->bst, cd->bsh, offset + count, p + count, len);
197 count += len;
198 }
199
200 return (count);
201 }
202
203 static int
dpaa2_cons_open(struct cdev * cdev,int flags,int fmt,struct thread * td)204 dpaa2_cons_open(struct cdev *cdev, int flags, int fmt, struct thread *td)
205 {
206 struct dpaa2_cons_dev *cd;
207 struct dpaa2_cons_hdr hdr;
208 size_t rlen __diagused;
209 uint32_t wrapped;
210
211 if (flags & FWRITE)
212 return (EACCES);
213
214 cd = cdev->si_drv1;
215 if (cd->size == 0)
216 return (ENODEV);
217
218 mtx_lock(&cd->mtx);
219 rlen = dpaa2_cons_read_bs(cd, 0, &hdr, sizeof(hdr));
220 KASSERT(rlen == sizeof(hdr), ("%s:%d: rlen %zu != count %zu, cdev %p "
221 "cd %p\n", __func__, __LINE__, rlen, sizeof(hdr), cdev, cd));
222
223 cd->hdr_magic = hdr.magic;
224 if (cd->hdr_magic != cd->magic) {
225 mtx_unlock(&cd->mtx);
226 return (ENODEV);
227 }
228
229 cd->hdr_eobyte = hdr.eobyte;
230 cd->hdr_start = hdr.start;
231 cd->hdr_len = hdr.len;
232
233 cd->start = cd->hdr_start - cd->offset;
234 cd->end = cd->start + cd->hdr_len;
235
236 wrapped = cd->hdr_eobyte & LOG_HEADER_FLAG_BUFFER_WRAPAROUND;
237 cd->eod = cd->start + LAST_BYTE(cd->hdr_eobyte);
238
239 if (wrapped && cd->eod != cd->end)
240 cd->cur = cd->eod + 1;
241 else
242 cd->cur = cd->start;
243 mtx_unlock(&cd->mtx);
244
245 return (0);
246 }
247
248 static int
dpaa2_cons_close(struct cdev * cdev,int flags,int fmt,struct thread * td)249 dpaa2_cons_close(struct cdev *cdev, int flags, int fmt, struct thread *td)
250 {
251 struct dpaa2_cons_dev *cd;
252
253 cd = cdev->si_drv1;
254 mtx_lock(&cd->mtx);
255 cd->hdr_magic = cd->hdr_eobyte = cd->hdr_start = cd->hdr_len = 0;
256 cd->start = cd->end = 0;
257 mtx_unlock(&cd->mtx);
258
259 return (0);
260 }
261
262 static int
dpaa2_cons_read(struct cdev * cdev,struct uio * uio,int flag)263 dpaa2_cons_read(struct cdev *cdev, struct uio *uio, int flag)
264 {
265 char buf[128];
266 struct dpaa2_cons_dev *cd;
267 size_t len, size, count;
268 size_t rlen __diagused;
269 int error;
270
271 cd = cdev->si_drv1;
272 mtx_lock(&cd->mtx);
273 if (cd->cur == cd->eod) {
274 mtx_unlock(&cd->mtx);
275 return (0);
276 } else if (cd->cur > cd->eod) {
277 len = (cd->end - cd->cur) + (cd->eod - cd->start);
278 } else {
279 len = cd->eod - cd->cur;
280 }
281 size = cd->end - cd->cur;
282
283 if (len > size) {
284 /* dump cur [size] */
285 while (uio->uio_resid > 0) {
286 count = imin(sizeof(buf), uio->uio_resid);
287 if (count > size)
288 count = size;
289
290 rlen = dpaa2_cons_read_bs(cd, cd->cur, buf, count);
291 KASSERT(rlen == count, ("%s:%d: rlen %zu != count %zu, "
292 "cdev %p cd %p\n", __func__, __LINE__, rlen, count,
293 cdev, cd));
294
295 cd->cur += count;
296 len -= count;
297 size -= count;
298 mtx_unlock(&cd->mtx);
299 error = uiomove(buf, count, uio);
300 if (error != 0 || uio->uio_resid == 0)
301 return (error);
302 mtx_lock(&cd->mtx);
303 }
304 cd->cur = cd->start;
305 }
306
307 /* dump cur [len] */
308 while (len > 0 && uio->uio_resid > 0) {
309 count = imin(sizeof(buf), uio->uio_resid);
310 if (count > len)
311 count = len;
312
313 rlen = dpaa2_cons_read_bs(cd, cd->cur, buf, count);
314 KASSERT(rlen == count, ("%s:%d: rlen %zu != count %zu, cdev %p "
315 "cd %p\n", __func__, __LINE__, rlen, count, cdev, cd));
316
317 cd->cur += count;
318 len -= count;
319 mtx_unlock(&cd->mtx);
320 error = uiomove(buf, count, uio);
321 if (error != 0 || uio->uio_resid == 0)
322 return (error);
323 mtx_lock(&cd->mtx);
324 }
325 mtx_unlock(&cd->mtx);
326 return (0);
327 }
328
329 static int
dpaa2_cons_create_dev(device_t dev,bus_addr_t pa,size_t size,size_t offset,uint32_t magic,struct cdevsw * devsw,struct dpaa2_cons_dev * cd)330 dpaa2_cons_create_dev(device_t dev, bus_addr_t pa, size_t size,
331 size_t offset, uint32_t magic, struct cdevsw *devsw,
332 struct dpaa2_cons_dev *cd)
333 {
334 struct dpaa2_cons_softc *sc;
335 struct dpaa2_cons_hdr hdr;
336 struct make_dev_args md_args;
337 size_t len;
338 int error;
339
340 sc = device_get_softc(dev);
341
342 error = bus_space_map(sc->bst, pa, size, 0, &cd->bsh);
343 if (error != 0) {
344 device_printf(dev, "%s: failed to map bus space %#jx/%zu: %d\n",
345 __func__, (uintmax_t)pa, size, error);
346 return (error);
347 }
348
349 cd->bst = sc->bst;
350 cd->size = size;
351
352 len = dpaa2_cons_read_bs(cd, 0, &hdr, sizeof(hdr));
353 if (len != sizeof(hdr)) {
354 device_printf(dev, "%s: failed to read hdr for %#jx/%zu: %d\n",
355 __func__, (uintmax_t)pa, size, error);
356 bus_space_unmap(cd->bst, cd->bsh, cd->size);
357 cd->size = 0;
358 return (EIO);
359 }
360
361 /* This checks probably needs to be removed one day? */
362 if (hdr.magic != magic) {
363 if (bootverbose)
364 device_printf(dev, "%s: magic wrong for %#jx/%zu: "
365 "%#010x != %#010x, no console?\n", __func__,
366 (uintmax_t)pa, size, hdr.magic, magic);
367 bus_space_unmap(cd->bst, cd->bsh, cd->size);
368 cd->size = 0;
369 return (ENOENT);
370 }
371
372 if (hdr.start - offset > size) {
373 device_printf(dev, "%s: range wrong for %#jx/%zu: %u - %zu > %zu\n",
374 __func__, (uintmax_t)pa, size, hdr.start, offset, size);
375 bus_space_unmap(cd->bst, cd->bsh, cd->size);
376 cd->size = 0;
377 return (ERANGE);
378 }
379
380 cd->offset = offset;
381 cd->magic = magic;
382 mtx_init(&cd->mtx, "dpaa2 cons", NULL, MTX_DEF);
383
384 make_dev_args_init(&md_args);
385 md_args.mda_devsw = devsw;
386 md_args.mda_uid = 0;
387 md_args.mda_gid = 69;
388 md_args.mda_mode = S_IRUSR | S_IRGRP;
389 md_args.mda_unit = 0;
390 md_args.mda_si_drv1 = cd;
391 error = make_dev_s(&md_args, &cd->cdev, "%s", devsw->d_name);
392 if (error != 0) {
393 device_printf(dev, "%s: make_dev_s failed for %#jx/%zu: %d\n",
394 __func__, (uintmax_t)pa, size, error);
395 mtx_destroy(&cd->mtx);
396 bus_space_unmap(cd->bst, cd->bsh, cd->size);
397 cd->size = 0;
398 return (error);
399 }
400
401 if (bootverbose)
402 device_printf(dev, "dpaa2 console %s at pa %#jx size %#010zx "
403 "(%zu) offset %zu, hdr: magic %#010x start %#010x len "
404 "%#010x eobyte %#010x\n", devsw->d_name, (uintmax_t)pa,
405 size, size, offset, hdr.magic, hdr.start, hdr.len, hdr.eobyte);
406
407 return (0);
408 }
409
410 static int
dpaa2_cons_attach_common(device_t dev)411 dpaa2_cons_attach_common(device_t dev)
412 {
413 struct dpaa2_cons_softc *sc;
414
415 sc = device_get_softc(dev);
416
417 dpaa2_cons_create_dev(dev, (bus_addr_t)(sc->mcfba + MC_BUFFER_OFFSET),
418 MC_BUFFER_SIZE, MC_OFFSET_DELTA, MAGIC_MC,
419 &dpaa2_mc_cons_cdevsw, &sc->mc_cd);
420
421 dpaa2_cons_create_dev(dev, (bus_addr_t)(sc->mcfba + AIOP_BUFFER_OFFSET),
422 AIOP_BUFFER_SIZE, AIOP_OFFSET_DELTA, MAGIC_AIOP,
423 &dpaa2_aiop_cons_cdevsw, &sc->aiop_cd);
424
425 return (0);
426 }
427
428 static void
dpaa2_cons_detach_common(struct dpaa2_cons_dev * cd)429 dpaa2_cons_detach_common(struct dpaa2_cons_dev *cd)
430 {
431
432 bus_space_unmap(cd->bst, cd->bsh, cd->size);
433 mtx_destroy(&cd->mtx);
434 }
435
436 static int
dpaa2_cons_detach(device_t dev)437 dpaa2_cons_detach(device_t dev)
438 {
439 struct dpaa2_cons_softc *sc;
440
441 sc = device_get_softc(dev);
442
443 if (sc->aiop_cd.cdev != NULL)
444 destroy_dev(sc->aiop_cd.cdev);
445 if (sc->mc_cd.cdev != NULL)
446 destroy_dev(sc->mc_cd.cdev);
447
448 if (sc->aiop_cd.size != 0)
449 dpaa2_cons_detach_common(&sc->aiop_cd);
450 if (sc->mc_cd.size != 0)
451 dpaa2_cons_detach_common(&sc->mc_cd);
452
453 if (sc->res != NULL)
454 bus_release_resource(dev, SYS_RES_MEMORY, rman_get_rid(sc->res),
455 sc->res);
456
457 return (0);
458 }
459
460 #ifdef __notyet__
461 #ifdef ACPI
462 static int
dpaa2_cons_acpi_probe(device_t dev)463 dpaa2_cons_acpi_probe(device_t dev)
464 {
465
466 if (!ofw_bus_status_okay(dev))
467 return (ENXIO);
468
469 device_set_desc(dev, "DPAA2 Console Driver");
470 return (BUS_PROBE_DEFAULT);
471 }
472
473 static int
dpaa2_cons_acpi_attach(device_t dev)474 dpaa2_cons_acpi_attach(device_t dev)
475 {
476 struct dpaa2_cons_softc *sc;
477 uint32_t val;
478 int error, rid;
479
480 sc = device_get_softc(dev);
481
482 rid = 0;
483 sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE);
484 if (sc->res == NULL) {
485 device_printf(dev, "Could not allocate memory\n");
486 return (ENXIO);
487 }
488 sc->bst = rman_get_bustag(sc->res);
489
490 val = DPAA2_MC_READ_4(sc, MC_REG_MCFBALR_OFF);
491 val &= MC_REG_MCFBALR_MASK;
492 sc->mcfba |= val;
493 val = DPAA2_MC_READ_4(sc, MC_REG_MCFBAHR_OFF);
494 val &= MC_REG_MCFBAHR_MASK;
495 sc->mcfba |= ((uint64_t)val << 32);
496
497 error = dpaa2_cons_attach_common(dev);
498
499 return (error);
500 }
501
502 static device_method_t dpaa2_cons_acpi_methods[] = {
503 /* Device interface */
504 DEVMETHOD(device_probe, dpaa2_cons_acpi_probe),
505 DEVMETHOD(device_attach, dpaa2_cons_acpi_attach),
506 DEVMETHOD(device_detach, dpaa2_cons_detach),
507
508 DEVMETHOD_END
509 };
510
511 DEFINE_CLASS_0(dpaa2_cons_acpi, dpaa2_cons_acpi_driver, dpaa2_cons_acpi_methods,
512 sizeof(struct dpaa2_cons_softc));
513
514 DRIVER_MODULE(dpaa2_cons_acpi, simplebus, dpaa2_cons_acpi_driver, 0, 0);
515 MODULE_DEPEND(dpaa2_cons_acpi, dpaa2_mc, 1, 1, 1);
516 #endif
517 #endif /* 0 */
518
519 #ifdef FDT
520 static struct ofw_compat_data compat_data[] = {
521 { "fsl,dpaa2-console", 1 },
522 { NULL, 0 }
523 };
524
525 static int
dpaa2_cons_fdt_probe(device_t dev)526 dpaa2_cons_fdt_probe(device_t dev)
527 {
528
529 if (!ofw_bus_status_okay(dev))
530 return (ENXIO);
531
532 if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
533 return (ENXIO);
534
535 device_set_desc(dev, "DPAA2 Console Driver");
536 return (BUS_PROBE_DEFAULT);
537 }
538
539 static int
dpaa2_cons_fdt_attach(device_t dev)540 dpaa2_cons_fdt_attach(device_t dev)
541 {
542 struct dpaa2_cons_softc *sc;
543 uint32_t val;
544 int error, rid;
545
546 sc = device_get_softc(dev);
547
548 rid = 0;
549 sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE);
550 if (sc->res == NULL) {
551 device_printf(dev, "Could not allocate memory\n");
552 return (ENXIO);
553 }
554 sc->bst = rman_get_bustag(sc->res);
555
556 val = DPAA2_MC_READ_4(sc, MC_REG_MCFBALR_OFF);
557 val &= MC_REG_MCFBALR_MASK;
558 sc->mcfba |= val;
559 val = DPAA2_MC_READ_4(sc, MC_REG_MCFBAHR_OFF);
560 val &= MC_REG_MCFBAHR_MASK;
561 sc->mcfba |= ((uint64_t)val << 32);
562
563 error = dpaa2_cons_attach_common(dev);
564
565 return (error);
566 }
567
568 static device_method_t dpaa2_cons_fdt_methods[] = {
569 /* Device interface */
570 DEVMETHOD(device_probe, dpaa2_cons_fdt_probe),
571 DEVMETHOD(device_attach, dpaa2_cons_fdt_attach),
572 DEVMETHOD(device_detach, dpaa2_cons_detach),
573
574 DEVMETHOD_END
575 };
576
577 DEFINE_CLASS_0(dpaa2_cons_fdt, dpaa2_cons_fdt_driver, dpaa2_cons_fdt_methods,
578 sizeof(struct dpaa2_cons_softc));
579
580 DRIVER_MODULE(dpaa2_cons_fdt, simplebus, dpaa2_cons_fdt_driver, 0, 0);
581 #endif
582