1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * PDC early console support - use PDC firmware to dump text via boot console 4 * 5 * Copyright (C) 2001-2022 Helge Deller <deller@gmx.de> 6 */ 7 8 #include <linux/console.h> 9 #include <linux/init.h> 10 #include <linux/serial_core.h> 11 #include <linux/kgdb.h> 12 #include <asm/page.h> /* for PAGE0 */ 13 #include <asm/pdc.h> /* for iodc_call() proto and friends */ 14 15 static DEFINE_SPINLOCK(pdc_console_lock); 16 17 static void pdc_console_write(struct console *co, const char *s, unsigned count) 18 { 19 int i = 0; 20 unsigned long flags; 21 22 spin_lock_irqsave(&pdc_console_lock, flags); 23 do { 24 i += pdc_iodc_print(s + i, count - i); 25 } while (i < count); 26 spin_unlock_irqrestore(&pdc_console_lock, flags); 27 } 28 29 #ifdef CONFIG_KGDB 30 static int kgdb_pdc_read_char(void) 31 { 32 int c; 33 unsigned long flags; 34 35 spin_lock_irqsave(&pdc_console_lock, flags); 36 c = pdc_iodc_getc(); 37 spin_unlock_irqrestore(&pdc_console_lock, flags); 38 39 return (c <= 0) ? NO_POLL_CHAR : c; 40 } 41 42 static void kgdb_pdc_write_char(u8 chr) 43 { 44 if (PAGE0->mem_cons.cl_class != CL_DUPLEX) 45 pdc_console_write(NULL, &chr, 1); 46 } 47 48 static struct kgdb_io kgdb_pdc_io_ops = { 49 .name = "kgdb_pdc", 50 .read_char = kgdb_pdc_read_char, 51 .write_char = kgdb_pdc_write_char, 52 }; 53 #endif 54 55 static int __init pdc_earlycon_setup(struct earlycon_device *device, 56 const char *opt) 57 { 58 struct console *earlycon_console; 59 60 /* If the console is duplex then copy the COUT parameters to CIN. */ 61 if (PAGE0->mem_cons.cl_class == CL_DUPLEX) 62 memcpy(&PAGE0->mem_kbd, &PAGE0->mem_cons, sizeof(PAGE0->mem_cons)); 63 64 earlycon_console = device->con; 65 earlycon_console->write = pdc_console_write; 66 device->port.iotype = UPIO_MEM32BE; 67 68 #ifdef CONFIG_KGDB 69 kgdb_register_io_module(&kgdb_pdc_io_ops); 70 #endif 71 72 return 0; 73 } 74 75 EARLYCON_DECLARE(pdc, pdc_earlycon_setup); 76