1e3b3d0f5SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0 2a9f96f01SGreg Kroah-Hartman /* Copyright (c) 2010, 2014 The Linux Foundation. All rights reserved. */ 316c63f8eSDaniel Walker 4*d1a1af2cSMichal Simek #include <linux/console.h> 516c63f8eSDaniel Walker #include <linux/init.h> 6*d1a1af2cSMichal Simek #include <linux/serial.h> 7*d1a1af2cSMichal Simek #include <linux/serial_core.h> 816c63f8eSDaniel Walker 94061f498SChristopher Covington #include <asm/dcc.h> 1016c63f8eSDaniel Walker #include <asm/processor.h> 1116c63f8eSDaniel Walker 1216c63f8eSDaniel Walker #include "hvc_console.h" 1316c63f8eSDaniel Walker 1416c63f8eSDaniel Walker /* DCC Status Bits */ 1516c63f8eSDaniel Walker #define DCC_STATUS_RX (1 << 30) 1616c63f8eSDaniel Walker #define DCC_STATUS_TX (1 << 29) 1716c63f8eSDaniel Walker 18*d1a1af2cSMichal Simek static void dcc_uart_console_putchar(struct uart_port *port, int ch) 19*d1a1af2cSMichal Simek { 20*d1a1af2cSMichal Simek while (__dcc_getstatus() & DCC_STATUS_TX) 21*d1a1af2cSMichal Simek cpu_relax(); 22*d1a1af2cSMichal Simek 23*d1a1af2cSMichal Simek __dcc_putchar(ch); 24*d1a1af2cSMichal Simek } 25*d1a1af2cSMichal Simek 26*d1a1af2cSMichal Simek static void dcc_early_write(struct console *con, const char *s, unsigned n) 27*d1a1af2cSMichal Simek { 28*d1a1af2cSMichal Simek struct earlycon_device *dev = con->data; 29*d1a1af2cSMichal Simek 30*d1a1af2cSMichal Simek uart_console_write(&dev->port, s, n, dcc_uart_console_putchar); 31*d1a1af2cSMichal Simek } 32*d1a1af2cSMichal Simek 33*d1a1af2cSMichal Simek static int __init dcc_early_console_setup(struct earlycon_device *device, 34*d1a1af2cSMichal Simek const char *opt) 35*d1a1af2cSMichal Simek { 36*d1a1af2cSMichal Simek device->con->write = dcc_early_write; 37*d1a1af2cSMichal Simek 38*d1a1af2cSMichal Simek return 0; 39*d1a1af2cSMichal Simek } 40*d1a1af2cSMichal Simek 41*d1a1af2cSMichal Simek EARLYCON_DECLARE(dcc, dcc_early_console_setup); 42*d1a1af2cSMichal Simek 4316c63f8eSDaniel Walker static int hvc_dcc_put_chars(uint32_t vt, const char *buf, int count) 4416c63f8eSDaniel Walker { 4516c63f8eSDaniel Walker int i; 4616c63f8eSDaniel Walker 4716c63f8eSDaniel Walker for (i = 0; i < count; i++) { 4816c63f8eSDaniel Walker while (__dcc_getstatus() & DCC_STATUS_TX) 4916c63f8eSDaniel Walker cpu_relax(); 5016c63f8eSDaniel Walker 51bf73bd35SStephen Boyd __dcc_putchar(buf[i]); 5216c63f8eSDaniel Walker } 5316c63f8eSDaniel Walker 5416c63f8eSDaniel Walker return count; 5516c63f8eSDaniel Walker } 5616c63f8eSDaniel Walker 5716c63f8eSDaniel Walker static int hvc_dcc_get_chars(uint32_t vt, char *buf, int count) 5816c63f8eSDaniel Walker { 5916c63f8eSDaniel Walker int i; 6016c63f8eSDaniel Walker 61bf73bd35SStephen Boyd for (i = 0; i < count; ++i) 6216c63f8eSDaniel Walker if (__dcc_getstatus() & DCC_STATUS_RX) 63bf73bd35SStephen Boyd buf[i] = __dcc_getchar(); 64bf73bd35SStephen Boyd else 6516c63f8eSDaniel Walker break; 6616c63f8eSDaniel Walker 6716c63f8eSDaniel Walker return i; 6816c63f8eSDaniel Walker } 6916c63f8eSDaniel Walker 70f377775dSRob Herring static bool hvc_dcc_check(void) 71f377775dSRob Herring { 72f377775dSRob Herring unsigned long time = jiffies + (HZ / 10); 73f377775dSRob Herring 74f377775dSRob Herring /* Write a test character to check if it is handled */ 75f377775dSRob Herring __dcc_putchar('\n'); 76f377775dSRob Herring 77f377775dSRob Herring while (time_is_after_jiffies(time)) { 78f377775dSRob Herring if (!(__dcc_getstatus() & DCC_STATUS_TX)) 79f377775dSRob Herring return true; 80f377775dSRob Herring } 81f377775dSRob Herring 82f377775dSRob Herring return false; 83f377775dSRob Herring } 84f377775dSRob Herring 8516c63f8eSDaniel Walker static const struct hv_ops hvc_dcc_get_put_ops = { 8616c63f8eSDaniel Walker .get_chars = hvc_dcc_get_chars, 8716c63f8eSDaniel Walker .put_chars = hvc_dcc_put_chars, 8816c63f8eSDaniel Walker }; 8916c63f8eSDaniel Walker 9016c63f8eSDaniel Walker static int __init hvc_dcc_console_init(void) 9116c63f8eSDaniel Walker { 923d270701STimur Tabi int ret; 933d270701STimur Tabi 94f377775dSRob Herring if (!hvc_dcc_check()) 95f377775dSRob Herring return -ENODEV; 96f377775dSRob Herring 973d270701STimur Tabi /* Returns -1 if error */ 983d270701STimur Tabi ret = hvc_instantiate(0, 0, &hvc_dcc_get_put_ops); 993d270701STimur Tabi 1003d270701STimur Tabi return ret < 0 ? -ENODEV : 0; 10116c63f8eSDaniel Walker } 10216c63f8eSDaniel Walker console_initcall(hvc_dcc_console_init); 10316c63f8eSDaniel Walker 10416c63f8eSDaniel Walker static int __init hvc_dcc_init(void) 10516c63f8eSDaniel Walker { 1063d270701STimur Tabi struct hvc_struct *p; 1073d270701STimur Tabi 108f377775dSRob Herring if (!hvc_dcc_check()) 109f377775dSRob Herring return -ENODEV; 110f377775dSRob Herring 1113d270701STimur Tabi p = hvc_alloc(0, 0, &hvc_dcc_get_put_ops, 128); 1123d270701STimur Tabi 1133d270701STimur Tabi return PTR_ERR_OR_ZERO(p); 11416c63f8eSDaniel Walker } 11516c63f8eSDaniel Walker device_initcall(hvc_dcc_init); 116