xref: /linux/drivers/tty/hvc/hvc_dcc.c (revision c95baf12f5077419db01313ab61c2aac007d40cd)
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