11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 21da177e4SLinus Torvalds /* 3*027c3d34SHelge Deller * PDC early console support - use PDC firmware to dump text via boot console 41da177e4SLinus Torvalds * 5*027c3d34SHelge Deller * Copyright (C) 2001-2022 Helge Deller <deller@gmx.de> 61da177e4SLinus Torvalds */ 71da177e4SLinus Torvalds 81da177e4SLinus Torvalds #include <linux/console.h> 91da177e4SLinus Torvalds #include <linux/init.h> 10*027c3d34SHelge Deller #include <linux/serial_core.h> 11*027c3d34SHelge Deller #include <linux/kgdb.h> 124a8a0788SRolf Eike Beer #include <asm/page.h> /* for PAGE0 */ 131da177e4SLinus Torvalds #include <asm/pdc.h> /* for iodc_call() proto and friends */ 141da177e4SLinus Torvalds 15aefa8b6bSJulia Lawall static DEFINE_SPINLOCK(pdc_console_lock); 161da177e4SLinus Torvalds 171da177e4SLinus Torvalds static void pdc_console_write(struct console *co, const char *s, unsigned count) 181da177e4SLinus Torvalds { 19ef1afd4dSKyle McMartin int i = 0; 20ef1afd4dSKyle McMartin unsigned long flags; 21ef1afd4dSKyle McMartin 22ef1afd4dSKyle McMartin spin_lock_irqsave(&pdc_console_lock, flags); 23ef1afd4dSKyle McMartin do { 24ef1afd4dSKyle McMartin i += pdc_iodc_print(s + i, count - i); 25ef1afd4dSKyle McMartin } while (i < count); 26ef1afd4dSKyle McMartin spin_unlock_irqrestore(&pdc_console_lock, flags); 271da177e4SLinus Torvalds } 281da177e4SLinus Torvalds 29*027c3d34SHelge Deller #ifdef CONFIG_KGDB 30*027c3d34SHelge Deller static int kgdb_pdc_read_char(void) 311da177e4SLinus Torvalds { 32ef1afd4dSKyle McMartin int c; 33ef1afd4dSKyle McMartin unsigned long flags; 34ef1afd4dSKyle McMartin 35ef1afd4dSKyle McMartin spin_lock_irqsave(&pdc_console_lock, flags); 36ef1afd4dSKyle McMartin c = pdc_iodc_getc(); 37ef1afd4dSKyle McMartin spin_unlock_irqrestore(&pdc_console_lock, flags); 38ef1afd4dSKyle McMartin 39*027c3d34SHelge Deller return (c <= 0) ? NO_POLL_CHAR : c; 401da177e4SLinus Torvalds } 411da177e4SLinus Torvalds 42*027c3d34SHelge Deller static void kgdb_pdc_write_char(u8 chr) 431da177e4SLinus Torvalds { 44*027c3d34SHelge Deller if (PAGE0->mem_cons.cl_class != CL_DUPLEX) 45*027c3d34SHelge Deller pdc_console_write(NULL, &chr, 1); 461da177e4SLinus Torvalds } 471da177e4SLinus Torvalds 48*027c3d34SHelge Deller static struct kgdb_io kgdb_pdc_io_ops = { 49*027c3d34SHelge Deller .name = "kgdb_pdc", 50*027c3d34SHelge Deller .read_char = kgdb_pdc_read_char, 51*027c3d34SHelge Deller .write_char = kgdb_pdc_write_char, 52650a35f8SGuy Martin }; 531da177e4SLinus Torvalds #endif 541da177e4SLinus Torvalds 55*027c3d34SHelge Deller static int __init pdc_earlycon_setup(struct earlycon_device *device, 56*027c3d34SHelge Deller const char *opt) 571da177e4SLinus Torvalds { 58*027c3d34SHelge Deller struct console *earlycon_console; 591da177e4SLinus Torvalds 601da177e4SLinus Torvalds /* If the console is duplex then copy the COUT parameters to CIN. */ 611da177e4SLinus Torvalds if (PAGE0->mem_cons.cl_class == CL_DUPLEX) 621da177e4SLinus Torvalds memcpy(&PAGE0->mem_kbd, &PAGE0->mem_cons, sizeof(PAGE0->mem_cons)); 631da177e4SLinus Torvalds 64*027c3d34SHelge Deller earlycon_console = device->con; 65*027c3d34SHelge Deller earlycon_console->write = pdc_console_write; 66*027c3d34SHelge Deller device->port.iotype = UPIO_MEM32BE; 671da177e4SLinus Torvalds 68*027c3d34SHelge Deller #ifdef CONFIG_KGDB 69*027c3d34SHelge Deller kgdb_register_io_module(&kgdb_pdc_io_ops); 701da177e4SLinus Torvalds #endif 71*027c3d34SHelge Deller 72*027c3d34SHelge Deller return 0; 731da177e4SLinus Torvalds } 741da177e4SLinus Torvalds 75*027c3d34SHelge Deller EARLYCON_DECLARE(pdc, pdc_earlycon_setup); 76