1 // SPDX-License-Identifier: GPL-2.0 2 /* Copyright (c) 2010, 2014 The Linux Foundation. All rights reserved. */ 3 4 #include <linux/console.h> 5 #include <linux/init.h> 6 #include <linux/serial.h> 7 #include <linux/serial_core.h> 8 9 #include <asm/dcc.h> 10 #include <asm/processor.h> 11 12 #include "hvc_console.h" 13 14 /* DCC Status Bits */ 15 #define DCC_STATUS_RX (1 << 30) 16 #define DCC_STATUS_TX (1 << 29) 17 18 static void dcc_uart_console_putchar(struct uart_port *port, unsigned char ch) 19 { 20 while (__dcc_getstatus() & DCC_STATUS_TX) 21 cpu_relax(); 22 23 __dcc_putchar(ch); 24 } 25 26 static void dcc_early_write(struct console *con, const char *s, unsigned n) 27 { 28 struct earlycon_device *dev = con->data; 29 30 uart_console_write(&dev->port, s, n, dcc_uart_console_putchar); 31 } 32 33 static int __init dcc_early_console_setup(struct earlycon_device *device, 34 const char *opt) 35 { 36 device->con->write = dcc_early_write; 37 38 return 0; 39 } 40 41 EARLYCON_DECLARE(dcc, dcc_early_console_setup); 42 43 static int hvc_dcc_put_chars(uint32_t vt, const char *buf, int count) 44 { 45 int i; 46 47 for (i = 0; i < count; i++) { 48 while (__dcc_getstatus() & DCC_STATUS_TX) 49 cpu_relax(); 50 51 __dcc_putchar(buf[i]); 52 } 53 54 return count; 55 } 56 57 static int hvc_dcc_get_chars(uint32_t vt, char *buf, int count) 58 { 59 int i; 60 61 for (i = 0; i < count; ++i) 62 if (__dcc_getstatus() & DCC_STATUS_RX) 63 buf[i] = __dcc_getchar(); 64 else 65 break; 66 67 return i; 68 } 69 70 static bool hvc_dcc_check(void) 71 { 72 unsigned long time = jiffies + (HZ / 10); 73 74 /* Write a test character to check if it is handled */ 75 __dcc_putchar('\n'); 76 77 while (time_is_after_jiffies(time)) { 78 if (!(__dcc_getstatus() & DCC_STATUS_TX)) 79 return true; 80 } 81 82 return false; 83 } 84 85 static const struct hv_ops hvc_dcc_get_put_ops = { 86 .get_chars = hvc_dcc_get_chars, 87 .put_chars = hvc_dcc_put_chars, 88 }; 89 90 static int __init hvc_dcc_console_init(void) 91 { 92 int ret; 93 94 if (!hvc_dcc_check()) 95 return -ENODEV; 96 97 /* Returns -1 if error */ 98 ret = hvc_instantiate(0, 0, &hvc_dcc_get_put_ops); 99 100 return ret < 0 ? -ENODEV : 0; 101 } 102 console_initcall(hvc_dcc_console_init); 103 104 static int __init hvc_dcc_init(void) 105 { 106 struct hvc_struct *p; 107 108 if (!hvc_dcc_check()) 109 return -ENODEV; 110 111 p = hvc_alloc(0, 0, &hvc_dcc_get_put_ops, 128); 112 113 return PTR_ERR_OR_ZERO(p); 114 } 115 device_initcall(hvc_dcc_init); 116