hvc_dcc.c 2.2 KB
Newer Older
1
// SPDX-License-Identifier: GPL-2.0
2
/* Copyright (c) 2010, 2014 The Linux Foundation. All rights reserved.  */
3

M
Michal Simek 已提交
4
#include <linux/console.h>
5
#include <linux/init.h>
M
Michal Simek 已提交
6 7
#include <linux/serial.h>
#include <linux/serial_core.h>
8

9
#include <asm/dcc.h>
10 11 12 13 14 15 16 17
#include <asm/processor.h>

#include "hvc_console.h"

/* DCC Status Bits */
#define DCC_STATUS_RX		(1 << 30)
#define DCC_STATUS_TX		(1 << 29)

M
Michal Simek 已提交
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
static void dcc_uart_console_putchar(struct uart_port *port, int ch)
{
	while (__dcc_getstatus() & DCC_STATUS_TX)
		cpu_relax();

	__dcc_putchar(ch);
}

static void dcc_early_write(struct console *con, const char *s, unsigned n)
{
	struct earlycon_device *dev = con->data;

	uart_console_write(&dev->port, s, n, dcc_uart_console_putchar);
}

static int __init dcc_early_console_setup(struct earlycon_device *device,
					  const char *opt)
{
	device->con->write = dcc_early_write;

	return 0;
}

EARLYCON_DECLARE(dcc, dcc_early_console_setup);

43 44 45 46 47 48 49 50
static int hvc_dcc_put_chars(uint32_t vt, const char *buf, int count)
{
	int i;

	for (i = 0; i < count; i++) {
		while (__dcc_getstatus() & DCC_STATUS_TX)
			cpu_relax();

51
		__dcc_putchar(buf[i]);
52 53 54 55 56 57 58 59 60
	}

	return count;
}

static int hvc_dcc_get_chars(uint32_t vt, char *buf, int count)
{
	int i;

61
	for (i = 0; i < count; ++i)
62
		if (__dcc_getstatus() & DCC_STATUS_RX)
63 64
			buf[i] = __dcc_getchar();
		else
65 66 67 68 69
			break;

	return i;
}

70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
static bool hvc_dcc_check(void)
{
	unsigned long time = jiffies + (HZ / 10);

	/* Write a test character to check if it is handled */
	__dcc_putchar('\n');

	while (time_is_after_jiffies(time)) {
		if (!(__dcc_getstatus() & DCC_STATUS_TX))
			return true;
	}

	return false;
}

85 86 87 88 89 90 91
static const struct hv_ops hvc_dcc_get_put_ops = {
	.get_chars = hvc_dcc_get_chars,
	.put_chars = hvc_dcc_put_chars,
};

static int __init hvc_dcc_console_init(void)
{
92 93
	int ret;

94 95 96
	if (!hvc_dcc_check())
		return -ENODEV;

97 98 99 100
	/* Returns -1 if error */
	ret = hvc_instantiate(0, 0, &hvc_dcc_get_put_ops);

	return ret < 0 ? -ENODEV : 0;
101 102 103 104 105
}
console_initcall(hvc_dcc_console_init);

static int __init hvc_dcc_init(void)
{
106 107
	struct hvc_struct *p;

108 109 110
	if (!hvc_dcc_check())
		return -ENODEV;

111 112 113
	p = hvc_alloc(0, 0, &hvc_dcc_get_put_ops, 128);

	return PTR_ERR_OR_ZERO(p);
114 115
}
device_initcall(hvc_dcc_init);