162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * IBM RTAS driver interface to hvc_console.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * (C) Copyright IBM Corporation 2001-2005
662306a36Sopenharmony_ci * (C) Copyright Red Hat, Inc. 2005
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Author(s): Maximino Augilar <IBM STI Design Center>
962306a36Sopenharmony_ci *	    : Ryan S. Arnold <rsa@us.ibm.com>
1062306a36Sopenharmony_ci *	    : Utz Bacher <utz.bacher@de.ibm.com>
1162306a36Sopenharmony_ci *	    : David Woodhouse <dwmw2@infradead.org>
1262306a36Sopenharmony_ci *
1362306a36Sopenharmony_ci *    inspired by drivers/char/hvc_console.c
1462306a36Sopenharmony_ci *    written by Anton Blanchard and Paul Mackerras
1562306a36Sopenharmony_ci */
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include <linux/console.h>
1862306a36Sopenharmony_ci#include <linux/delay.h>
1962306a36Sopenharmony_ci#include <linux/err.h>
2062306a36Sopenharmony_ci#include <linux/init.h>
2162306a36Sopenharmony_ci#include <linux/moduleparam.h>
2262306a36Sopenharmony_ci#include <linux/types.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#include <asm/irq.h>
2562306a36Sopenharmony_ci#include <asm/rtas.h>
2662306a36Sopenharmony_ci#include "hvc_console.h"
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#define hvc_rtas_cookie 0x67781e15
2962306a36Sopenharmony_cistatic struct hvc_struct *hvc_rtas_dev;
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cistatic int rtascons_put_char_token = RTAS_UNKNOWN_SERVICE;
3262306a36Sopenharmony_cistatic int rtascons_get_char_token = RTAS_UNKNOWN_SERVICE;
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_cistatic inline int hvc_rtas_write_console(uint32_t vtermno, const char *buf,
3562306a36Sopenharmony_ci		int count)
3662306a36Sopenharmony_ci{
3762306a36Sopenharmony_ci	int i;
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	for (i = 0; i < count; i++) {
4062306a36Sopenharmony_ci		if (rtas_call(rtascons_put_char_token, 1, 1, NULL, buf[i]))
4162306a36Sopenharmony_ci			break;
4262306a36Sopenharmony_ci	}
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	return i;
4562306a36Sopenharmony_ci}
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_cistatic int hvc_rtas_read_console(uint32_t vtermno, char *buf, int count)
4862306a36Sopenharmony_ci{
4962306a36Sopenharmony_ci	int i, c;
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	for (i = 0; i < count; i++) {
5262306a36Sopenharmony_ci		if (rtas_call(rtascons_get_char_token, 0, 2, &c))
5362306a36Sopenharmony_ci			break;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci		buf[i] = c;
5662306a36Sopenharmony_ci	}
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	return i;
5962306a36Sopenharmony_ci}
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_cistatic const struct hv_ops hvc_rtas_get_put_ops = {
6262306a36Sopenharmony_ci	.get_chars = hvc_rtas_read_console,
6362306a36Sopenharmony_ci	.put_chars = hvc_rtas_write_console,
6462306a36Sopenharmony_ci};
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_cistatic int __init hvc_rtas_init(void)
6762306a36Sopenharmony_ci{
6862306a36Sopenharmony_ci	struct hvc_struct *hp;
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	if (rtascons_put_char_token == RTAS_UNKNOWN_SERVICE)
7162306a36Sopenharmony_ci		rtascons_put_char_token = rtas_token("put-term-char");
7262306a36Sopenharmony_ci	if (rtascons_put_char_token == RTAS_UNKNOWN_SERVICE)
7362306a36Sopenharmony_ci		return -EIO;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	if (rtascons_get_char_token == RTAS_UNKNOWN_SERVICE)
7662306a36Sopenharmony_ci		rtascons_get_char_token = rtas_token("get-term-char");
7762306a36Sopenharmony_ci	if (rtascons_get_char_token == RTAS_UNKNOWN_SERVICE)
7862306a36Sopenharmony_ci		return -EIO;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	BUG_ON(hvc_rtas_dev);
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	/* Allocate an hvc_struct for the console device we instantiated
8362306a36Sopenharmony_ci	 * earlier.  Save off hp so that we can return it on exit */
8462306a36Sopenharmony_ci	hp = hvc_alloc(hvc_rtas_cookie, 0, &hvc_rtas_get_put_ops, 16);
8562306a36Sopenharmony_ci	if (IS_ERR(hp))
8662306a36Sopenharmony_ci		return PTR_ERR(hp);
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	hvc_rtas_dev = hp;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	return 0;
9162306a36Sopenharmony_ci}
9262306a36Sopenharmony_cidevice_initcall(hvc_rtas_init);
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci/* This will happen prior to module init.  There is no tty at this time? */
9562306a36Sopenharmony_cistatic int __init hvc_rtas_console_init(void)
9662306a36Sopenharmony_ci{
9762306a36Sopenharmony_ci	rtascons_put_char_token = rtas_token("put-term-char");
9862306a36Sopenharmony_ci	if (rtascons_put_char_token == RTAS_UNKNOWN_SERVICE)
9962306a36Sopenharmony_ci		return -EIO;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	rtascons_get_char_token = rtas_token("get-term-char");
10262306a36Sopenharmony_ci	if (rtascons_get_char_token == RTAS_UNKNOWN_SERVICE)
10362306a36Sopenharmony_ci		return -EIO;
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	hvc_instantiate(hvc_rtas_cookie, 0, &hvc_rtas_get_put_ops);
10662306a36Sopenharmony_ci	add_preferred_console("hvc", 0, NULL);
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	return 0;
10962306a36Sopenharmony_ci}
11062306a36Sopenharmony_ciconsole_initcall(hvc_rtas_console_init);
111