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