18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * opal driver interface to hvc_console.c
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright 2011 Benjamin Herrenschmidt <benh@kernel.crashing.org>, IBM Corp.
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#undef DEBUG
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/types.h>
118c2ecf20Sopenharmony_ci#include <linux/init.h>
128c2ecf20Sopenharmony_ci#include <linux/delay.h>
138c2ecf20Sopenharmony_ci#include <linux/slab.h>
148c2ecf20Sopenharmony_ci#include <linux/console.h>
158c2ecf20Sopenharmony_ci#include <linux/of.h>
168c2ecf20Sopenharmony_ci#include <linux/of_platform.h>
178c2ecf20Sopenharmony_ci#include <linux/export.h>
188c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#include <asm/hvconsole.h>
218c2ecf20Sopenharmony_ci#include <asm/prom.h>
228c2ecf20Sopenharmony_ci#include <asm/firmware.h>
238c2ecf20Sopenharmony_ci#include <asm/hvsi.h>
248c2ecf20Sopenharmony_ci#include <asm/udbg.h>
258c2ecf20Sopenharmony_ci#include <asm/opal.h>
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#include "hvc_console.h"
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_cistatic const char hvc_opal_name[] = "hvc_opal";
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_cistatic const struct of_device_id hvc_opal_match[] = {
328c2ecf20Sopenharmony_ci	{ .name = "serial", .compatible = "ibm,opal-console-raw" },
338c2ecf20Sopenharmony_ci	{ .name = "serial", .compatible = "ibm,opal-console-hvsi" },
348c2ecf20Sopenharmony_ci	{ },
358c2ecf20Sopenharmony_ci};
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_citypedef enum hv_protocol {
388c2ecf20Sopenharmony_ci	HV_PROTOCOL_RAW,
398c2ecf20Sopenharmony_ci	HV_PROTOCOL_HVSI
408c2ecf20Sopenharmony_ci} hv_protocol_t;
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_cistruct hvc_opal_priv {
438c2ecf20Sopenharmony_ci	hv_protocol_t		proto;	/* Raw data or HVSI packets */
448c2ecf20Sopenharmony_ci	struct hvsi_priv	hvsi;	/* HVSI specific data */
458c2ecf20Sopenharmony_ci};
468c2ecf20Sopenharmony_cistatic struct hvc_opal_priv *hvc_opal_privs[MAX_NR_HVC_CONSOLES];
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci/* For early boot console */
498c2ecf20Sopenharmony_cistatic struct hvc_opal_priv hvc_opal_boot_priv;
508c2ecf20Sopenharmony_cistatic u32 hvc_opal_boot_termno;
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_cistatic const struct hv_ops hvc_opal_raw_ops = {
538c2ecf20Sopenharmony_ci	.get_chars = opal_get_chars,
548c2ecf20Sopenharmony_ci	.put_chars = opal_put_chars,
558c2ecf20Sopenharmony_ci	.flush = opal_flush_chars,
568c2ecf20Sopenharmony_ci	.notifier_add = notifier_add_irq,
578c2ecf20Sopenharmony_ci	.notifier_del = notifier_del_irq,
588c2ecf20Sopenharmony_ci	.notifier_hangup = notifier_hangup_irq,
598c2ecf20Sopenharmony_ci};
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_cistatic int hvc_opal_hvsi_get_chars(uint32_t vtermno, char *buf, int count)
628c2ecf20Sopenharmony_ci{
638c2ecf20Sopenharmony_ci	struct hvc_opal_priv *pv = hvc_opal_privs[vtermno];
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	if (WARN_ON(!pv))
668c2ecf20Sopenharmony_ci		return -ENODEV;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	return hvsilib_get_chars(&pv->hvsi, buf, count);
698c2ecf20Sopenharmony_ci}
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_cistatic int hvc_opal_hvsi_put_chars(uint32_t vtermno, const char *buf, int count)
728c2ecf20Sopenharmony_ci{
738c2ecf20Sopenharmony_ci	struct hvc_opal_priv *pv = hvc_opal_privs[vtermno];
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	if (WARN_ON(!pv))
768c2ecf20Sopenharmony_ci		return -ENODEV;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	return hvsilib_put_chars(&pv->hvsi, buf, count);
798c2ecf20Sopenharmony_ci}
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_cistatic int hvc_opal_hvsi_open(struct hvc_struct *hp, int data)
828c2ecf20Sopenharmony_ci{
838c2ecf20Sopenharmony_ci	struct hvc_opal_priv *pv = hvc_opal_privs[hp->vtermno];
848c2ecf20Sopenharmony_ci	int rc;
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	pr_devel("HVSI@%x: do open !\n", hp->vtermno);
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	rc = notifier_add_irq(hp, data);
898c2ecf20Sopenharmony_ci	if (rc)
908c2ecf20Sopenharmony_ci		return rc;
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	return hvsilib_open(&pv->hvsi, hp);
938c2ecf20Sopenharmony_ci}
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_cistatic void hvc_opal_hvsi_close(struct hvc_struct *hp, int data)
968c2ecf20Sopenharmony_ci{
978c2ecf20Sopenharmony_ci	struct hvc_opal_priv *pv = hvc_opal_privs[hp->vtermno];
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	pr_devel("HVSI@%x: do close !\n", hp->vtermno);
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	hvsilib_close(&pv->hvsi, hp);
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	notifier_del_irq(hp, data);
1048c2ecf20Sopenharmony_ci}
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_civoid hvc_opal_hvsi_hangup(struct hvc_struct *hp, int data)
1078c2ecf20Sopenharmony_ci{
1088c2ecf20Sopenharmony_ci	struct hvc_opal_priv *pv = hvc_opal_privs[hp->vtermno];
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	pr_devel("HVSI@%x: do hangup !\n", hp->vtermno);
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	hvsilib_close(&pv->hvsi, hp);
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	notifier_hangup_irq(hp, data);
1158c2ecf20Sopenharmony_ci}
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_cistatic int hvc_opal_hvsi_tiocmget(struct hvc_struct *hp)
1188c2ecf20Sopenharmony_ci{
1198c2ecf20Sopenharmony_ci	struct hvc_opal_priv *pv = hvc_opal_privs[hp->vtermno];
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	if (!pv)
1228c2ecf20Sopenharmony_ci		return -EINVAL;
1238c2ecf20Sopenharmony_ci	return pv->hvsi.mctrl;
1248c2ecf20Sopenharmony_ci}
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_cistatic int hvc_opal_hvsi_tiocmset(struct hvc_struct *hp, unsigned int set,
1278c2ecf20Sopenharmony_ci				unsigned int clear)
1288c2ecf20Sopenharmony_ci{
1298c2ecf20Sopenharmony_ci	struct hvc_opal_priv *pv = hvc_opal_privs[hp->vtermno];
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	pr_devel("HVSI@%x: Set modem control, set=%x,clr=%x\n",
1328c2ecf20Sopenharmony_ci		 hp->vtermno, set, clear);
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	if (set & TIOCM_DTR)
1358c2ecf20Sopenharmony_ci		hvsilib_write_mctrl(&pv->hvsi, 1);
1368c2ecf20Sopenharmony_ci	else if (clear & TIOCM_DTR)
1378c2ecf20Sopenharmony_ci		hvsilib_write_mctrl(&pv->hvsi, 0);
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	return 0;
1408c2ecf20Sopenharmony_ci}
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_cistatic const struct hv_ops hvc_opal_hvsi_ops = {
1438c2ecf20Sopenharmony_ci	.get_chars = hvc_opal_hvsi_get_chars,
1448c2ecf20Sopenharmony_ci	.put_chars = hvc_opal_hvsi_put_chars,
1458c2ecf20Sopenharmony_ci	.flush = opal_flush_chars,
1468c2ecf20Sopenharmony_ci	.notifier_add = hvc_opal_hvsi_open,
1478c2ecf20Sopenharmony_ci	.notifier_del = hvc_opal_hvsi_close,
1488c2ecf20Sopenharmony_ci	.notifier_hangup = hvc_opal_hvsi_hangup,
1498c2ecf20Sopenharmony_ci	.tiocmget = hvc_opal_hvsi_tiocmget,
1508c2ecf20Sopenharmony_ci	.tiocmset = hvc_opal_hvsi_tiocmset,
1518c2ecf20Sopenharmony_ci};
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_cistatic int hvc_opal_probe(struct platform_device *dev)
1548c2ecf20Sopenharmony_ci{
1558c2ecf20Sopenharmony_ci	const struct hv_ops *ops;
1568c2ecf20Sopenharmony_ci	struct hvc_struct *hp;
1578c2ecf20Sopenharmony_ci	struct hvc_opal_priv *pv;
1588c2ecf20Sopenharmony_ci	hv_protocol_t proto;
1598c2ecf20Sopenharmony_ci	unsigned int termno, irq, boot = 0;
1608c2ecf20Sopenharmony_ci	const __be32 *reg;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	if (of_device_is_compatible(dev->dev.of_node, "ibm,opal-console-raw")) {
1638c2ecf20Sopenharmony_ci		proto = HV_PROTOCOL_RAW;
1648c2ecf20Sopenharmony_ci		ops = &hvc_opal_raw_ops;
1658c2ecf20Sopenharmony_ci	} else if (of_device_is_compatible(dev->dev.of_node,
1668c2ecf20Sopenharmony_ci					   "ibm,opal-console-hvsi")) {
1678c2ecf20Sopenharmony_ci		proto = HV_PROTOCOL_HVSI;
1688c2ecf20Sopenharmony_ci		ops = &hvc_opal_hvsi_ops;
1698c2ecf20Sopenharmony_ci	} else {
1708c2ecf20Sopenharmony_ci		pr_err("hvc_opal: Unknown protocol for %pOF\n",
1718c2ecf20Sopenharmony_ci		       dev->dev.of_node);
1728c2ecf20Sopenharmony_ci		return -ENXIO;
1738c2ecf20Sopenharmony_ci	}
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	reg = of_get_property(dev->dev.of_node, "reg", NULL);
1768c2ecf20Sopenharmony_ci	termno = reg ? be32_to_cpup(reg) : 0;
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	/* Is it our boot one ? */
1798c2ecf20Sopenharmony_ci	if (hvc_opal_privs[termno] == &hvc_opal_boot_priv) {
1808c2ecf20Sopenharmony_ci		pv = hvc_opal_privs[termno];
1818c2ecf20Sopenharmony_ci		boot = 1;
1828c2ecf20Sopenharmony_ci	} else if (hvc_opal_privs[termno] == NULL) {
1838c2ecf20Sopenharmony_ci		pv = kzalloc(sizeof(struct hvc_opal_priv), GFP_KERNEL);
1848c2ecf20Sopenharmony_ci		if (!pv)
1858c2ecf20Sopenharmony_ci			return -ENOMEM;
1868c2ecf20Sopenharmony_ci		pv->proto = proto;
1878c2ecf20Sopenharmony_ci		hvc_opal_privs[termno] = pv;
1888c2ecf20Sopenharmony_ci		if (proto == HV_PROTOCOL_HVSI) {
1898c2ecf20Sopenharmony_ci			/*
1908c2ecf20Sopenharmony_ci			 * We want put_chars to be atomic to avoid mangling of
1918c2ecf20Sopenharmony_ci			 * hvsi packets.
1928c2ecf20Sopenharmony_ci			 */
1938c2ecf20Sopenharmony_ci			hvsilib_init(&pv->hvsi,
1948c2ecf20Sopenharmony_ci				     opal_get_chars, opal_put_chars_atomic,
1958c2ecf20Sopenharmony_ci				     termno, 0);
1968c2ecf20Sopenharmony_ci		}
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci		/* Instanciate now to establish a mapping index==vtermno */
1998c2ecf20Sopenharmony_ci		hvc_instantiate(termno, termno, ops);
2008c2ecf20Sopenharmony_ci	} else {
2018c2ecf20Sopenharmony_ci		pr_err("hvc_opal: Device %pOF has duplicate terminal number #%d\n",
2028c2ecf20Sopenharmony_ci		       dev->dev.of_node, termno);
2038c2ecf20Sopenharmony_ci		return -ENXIO;
2048c2ecf20Sopenharmony_ci	}
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	pr_info("hvc%d: %s protocol on %pOF%s\n", termno,
2078c2ecf20Sopenharmony_ci		proto == HV_PROTOCOL_RAW ? "raw" : "hvsi",
2088c2ecf20Sopenharmony_ci		dev->dev.of_node,
2098c2ecf20Sopenharmony_ci		boot ? " (boot console)" : "");
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	irq = irq_of_parse_and_map(dev->dev.of_node, 0);
2128c2ecf20Sopenharmony_ci	if (!irq) {
2138c2ecf20Sopenharmony_ci		pr_info("hvc%d: No interrupts property, using OPAL event\n",
2148c2ecf20Sopenharmony_ci				termno);
2158c2ecf20Sopenharmony_ci		irq = opal_event_request(ilog2(OPAL_EVENT_CONSOLE_INPUT));
2168c2ecf20Sopenharmony_ci	}
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	if (!irq) {
2198c2ecf20Sopenharmony_ci		pr_err("hvc_opal: Unable to map interrupt for device %pOF\n",
2208c2ecf20Sopenharmony_ci			dev->dev.of_node);
2218c2ecf20Sopenharmony_ci		return irq;
2228c2ecf20Sopenharmony_ci	}
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	hp = hvc_alloc(termno, irq, ops, MAX_VIO_PUT_CHARS);
2258c2ecf20Sopenharmony_ci	if (IS_ERR(hp))
2268c2ecf20Sopenharmony_ci		return PTR_ERR(hp);
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	/* hvc consoles on powernv may need to share a single irq */
2298c2ecf20Sopenharmony_ci	hp->flags = IRQF_SHARED;
2308c2ecf20Sopenharmony_ci	dev_set_drvdata(&dev->dev, hp);
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	return 0;
2338c2ecf20Sopenharmony_ci}
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_cistatic int hvc_opal_remove(struct platform_device *dev)
2368c2ecf20Sopenharmony_ci{
2378c2ecf20Sopenharmony_ci	struct hvc_struct *hp = dev_get_drvdata(&dev->dev);
2388c2ecf20Sopenharmony_ci	int rc, termno;
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	termno = hp->vtermno;
2418c2ecf20Sopenharmony_ci	rc = hvc_remove(hp);
2428c2ecf20Sopenharmony_ci	if (rc == 0) {
2438c2ecf20Sopenharmony_ci		if (hvc_opal_privs[termno] != &hvc_opal_boot_priv)
2448c2ecf20Sopenharmony_ci			kfree(hvc_opal_privs[termno]);
2458c2ecf20Sopenharmony_ci		hvc_opal_privs[termno] = NULL;
2468c2ecf20Sopenharmony_ci	}
2478c2ecf20Sopenharmony_ci	return rc;
2488c2ecf20Sopenharmony_ci}
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_cistatic struct platform_driver hvc_opal_driver = {
2518c2ecf20Sopenharmony_ci	.probe		= hvc_opal_probe,
2528c2ecf20Sopenharmony_ci	.remove		= hvc_opal_remove,
2538c2ecf20Sopenharmony_ci	.driver		= {
2548c2ecf20Sopenharmony_ci		.name	= hvc_opal_name,
2558c2ecf20Sopenharmony_ci		.of_match_table	= hvc_opal_match,
2568c2ecf20Sopenharmony_ci	}
2578c2ecf20Sopenharmony_ci};
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_cistatic int __init hvc_opal_init(void)
2608c2ecf20Sopenharmony_ci{
2618c2ecf20Sopenharmony_ci	if (!firmware_has_feature(FW_FEATURE_OPAL))
2628c2ecf20Sopenharmony_ci		return -ENODEV;
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	/* Register as a vio device to receive callbacks */
2658c2ecf20Sopenharmony_ci	return platform_driver_register(&hvc_opal_driver);
2668c2ecf20Sopenharmony_ci}
2678c2ecf20Sopenharmony_cidevice_initcall(hvc_opal_init);
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_cistatic void udbg_opal_putc(char c)
2708c2ecf20Sopenharmony_ci{
2718c2ecf20Sopenharmony_ci	unsigned int termno = hvc_opal_boot_termno;
2728c2ecf20Sopenharmony_ci	int count = -1;
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	if (c == '\n')
2758c2ecf20Sopenharmony_ci		udbg_opal_putc('\r');
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	do {
2788c2ecf20Sopenharmony_ci		switch(hvc_opal_boot_priv.proto) {
2798c2ecf20Sopenharmony_ci		case HV_PROTOCOL_RAW:
2808c2ecf20Sopenharmony_ci			count = opal_put_chars(termno, &c, 1);
2818c2ecf20Sopenharmony_ci			break;
2828c2ecf20Sopenharmony_ci		case HV_PROTOCOL_HVSI:
2838c2ecf20Sopenharmony_ci			count = hvc_opal_hvsi_put_chars(termno, &c, 1);
2848c2ecf20Sopenharmony_ci			break;
2858c2ecf20Sopenharmony_ci		}
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci		/* This is needed for the cosole to flush
2888c2ecf20Sopenharmony_ci		 * when there aren't any interrupts.
2898c2ecf20Sopenharmony_ci		 */
2908c2ecf20Sopenharmony_ci		opal_flush_console(termno);
2918c2ecf20Sopenharmony_ci	} while(count == 0 || count == -EAGAIN);
2928c2ecf20Sopenharmony_ci}
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_cistatic int udbg_opal_getc_poll(void)
2958c2ecf20Sopenharmony_ci{
2968c2ecf20Sopenharmony_ci	unsigned int termno = hvc_opal_boot_termno;
2978c2ecf20Sopenharmony_ci	int rc = 0;
2988c2ecf20Sopenharmony_ci	char c;
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	switch(hvc_opal_boot_priv.proto) {
3018c2ecf20Sopenharmony_ci	case HV_PROTOCOL_RAW:
3028c2ecf20Sopenharmony_ci		rc = opal_get_chars(termno, &c, 1);
3038c2ecf20Sopenharmony_ci		break;
3048c2ecf20Sopenharmony_ci	case HV_PROTOCOL_HVSI:
3058c2ecf20Sopenharmony_ci		rc = hvc_opal_hvsi_get_chars(termno, &c, 1);
3068c2ecf20Sopenharmony_ci		break;
3078c2ecf20Sopenharmony_ci	}
3088c2ecf20Sopenharmony_ci	if (!rc)
3098c2ecf20Sopenharmony_ci		return -1;
3108c2ecf20Sopenharmony_ci	return c;
3118c2ecf20Sopenharmony_ci}
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_cistatic int udbg_opal_getc(void)
3148c2ecf20Sopenharmony_ci{
3158c2ecf20Sopenharmony_ci	int ch;
3168c2ecf20Sopenharmony_ci	for (;;) {
3178c2ecf20Sopenharmony_ci		ch = udbg_opal_getc_poll();
3188c2ecf20Sopenharmony_ci		if (ch != -1)
3198c2ecf20Sopenharmony_ci			return ch;
3208c2ecf20Sopenharmony_ci	}
3218c2ecf20Sopenharmony_ci}
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_cistatic void udbg_init_opal_common(void)
3248c2ecf20Sopenharmony_ci{
3258c2ecf20Sopenharmony_ci	udbg_putc = udbg_opal_putc;
3268c2ecf20Sopenharmony_ci	udbg_getc = udbg_opal_getc;
3278c2ecf20Sopenharmony_ci	udbg_getc_poll = udbg_opal_getc_poll;
3288c2ecf20Sopenharmony_ci}
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_civoid __init hvc_opal_init_early(void)
3318c2ecf20Sopenharmony_ci{
3328c2ecf20Sopenharmony_ci	struct device_node *stdout_node = of_node_get(of_stdout);
3338c2ecf20Sopenharmony_ci	const __be32 *termno;
3348c2ecf20Sopenharmony_ci	const struct hv_ops *ops;
3358c2ecf20Sopenharmony_ci	u32 index;
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	/* If the console wasn't in /chosen, try /ibm,opal */
3388c2ecf20Sopenharmony_ci	if (!stdout_node) {
3398c2ecf20Sopenharmony_ci		struct device_node *opal, *np;
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci		/* Current OPAL takeover doesn't provide the stdout
3428c2ecf20Sopenharmony_ci		 * path, so we hard wire it
3438c2ecf20Sopenharmony_ci		 */
3448c2ecf20Sopenharmony_ci		opal = of_find_node_by_path("/ibm,opal/consoles");
3458c2ecf20Sopenharmony_ci		if (opal)
3468c2ecf20Sopenharmony_ci			pr_devel("hvc_opal: Found consoles in new location\n");
3478c2ecf20Sopenharmony_ci		if (!opal) {
3488c2ecf20Sopenharmony_ci			opal = of_find_node_by_path("/ibm,opal");
3498c2ecf20Sopenharmony_ci			if (opal)
3508c2ecf20Sopenharmony_ci				pr_devel("hvc_opal: "
3518c2ecf20Sopenharmony_ci					 "Found consoles in old location\n");
3528c2ecf20Sopenharmony_ci		}
3538c2ecf20Sopenharmony_ci		if (!opal)
3548c2ecf20Sopenharmony_ci			return;
3558c2ecf20Sopenharmony_ci		for_each_child_of_node(opal, np) {
3568c2ecf20Sopenharmony_ci			if (of_node_name_eq(np, "serial")) {
3578c2ecf20Sopenharmony_ci				stdout_node = np;
3588c2ecf20Sopenharmony_ci				break;
3598c2ecf20Sopenharmony_ci			}
3608c2ecf20Sopenharmony_ci		}
3618c2ecf20Sopenharmony_ci		of_node_put(opal);
3628c2ecf20Sopenharmony_ci	}
3638c2ecf20Sopenharmony_ci	if (!stdout_node)
3648c2ecf20Sopenharmony_ci		return;
3658c2ecf20Sopenharmony_ci	termno = of_get_property(stdout_node, "reg", NULL);
3668c2ecf20Sopenharmony_ci	index = termno ? be32_to_cpup(termno) : 0;
3678c2ecf20Sopenharmony_ci	if (index >= MAX_NR_HVC_CONSOLES)
3688c2ecf20Sopenharmony_ci		return;
3698c2ecf20Sopenharmony_ci	hvc_opal_privs[index] = &hvc_opal_boot_priv;
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	/* Check the protocol */
3728c2ecf20Sopenharmony_ci	if (of_device_is_compatible(stdout_node, "ibm,opal-console-raw")) {
3738c2ecf20Sopenharmony_ci		hvc_opal_boot_priv.proto = HV_PROTOCOL_RAW;
3748c2ecf20Sopenharmony_ci		ops = &hvc_opal_raw_ops;
3758c2ecf20Sopenharmony_ci		pr_devel("hvc_opal: Found RAW console\n");
3768c2ecf20Sopenharmony_ci	}
3778c2ecf20Sopenharmony_ci	else if (of_device_is_compatible(stdout_node,"ibm,opal-console-hvsi")) {
3788c2ecf20Sopenharmony_ci		hvc_opal_boot_priv.proto = HV_PROTOCOL_HVSI;
3798c2ecf20Sopenharmony_ci		ops = &hvc_opal_hvsi_ops;
3808c2ecf20Sopenharmony_ci		hvsilib_init(&hvc_opal_boot_priv.hvsi,
3818c2ecf20Sopenharmony_ci			     opal_get_chars, opal_put_chars_atomic,
3828c2ecf20Sopenharmony_ci			     index, 1);
3838c2ecf20Sopenharmony_ci		/* HVSI, perform the handshake now */
3848c2ecf20Sopenharmony_ci		hvsilib_establish(&hvc_opal_boot_priv.hvsi);
3858c2ecf20Sopenharmony_ci		pr_devel("hvc_opal: Found HVSI console\n");
3868c2ecf20Sopenharmony_ci	} else
3878c2ecf20Sopenharmony_ci		goto out;
3888c2ecf20Sopenharmony_ci	hvc_opal_boot_termno = index;
3898c2ecf20Sopenharmony_ci	udbg_init_opal_common();
3908c2ecf20Sopenharmony_ci	add_preferred_console("hvc", index, NULL);
3918c2ecf20Sopenharmony_ci	hvc_instantiate(index, index, ops);
3928c2ecf20Sopenharmony_ciout:
3938c2ecf20Sopenharmony_ci	of_node_put(stdout_node);
3948c2ecf20Sopenharmony_ci}
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC_EARLY_DEBUG_OPAL_RAW
3978c2ecf20Sopenharmony_civoid __init udbg_init_debug_opal_raw(void)
3988c2ecf20Sopenharmony_ci{
3998c2ecf20Sopenharmony_ci	u32 index = CONFIG_PPC_EARLY_DEBUG_OPAL_VTERMNO;
4008c2ecf20Sopenharmony_ci	hvc_opal_privs[index] = &hvc_opal_boot_priv;
4018c2ecf20Sopenharmony_ci	hvc_opal_boot_priv.proto = HV_PROTOCOL_RAW;
4028c2ecf20Sopenharmony_ci	hvc_opal_boot_termno = index;
4038c2ecf20Sopenharmony_ci	udbg_init_opal_common();
4048c2ecf20Sopenharmony_ci}
4058c2ecf20Sopenharmony_ci#endif /* CONFIG_PPC_EARLY_DEBUG_OPAL_RAW */
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC_EARLY_DEBUG_OPAL_HVSI
4088c2ecf20Sopenharmony_civoid __init udbg_init_debug_opal_hvsi(void)
4098c2ecf20Sopenharmony_ci{
4108c2ecf20Sopenharmony_ci	u32 index = CONFIG_PPC_EARLY_DEBUG_OPAL_VTERMNO;
4118c2ecf20Sopenharmony_ci	hvc_opal_privs[index] = &hvc_opal_boot_priv;
4128c2ecf20Sopenharmony_ci	hvc_opal_boot_termno = index;
4138c2ecf20Sopenharmony_ci	udbg_init_opal_common();
4148c2ecf20Sopenharmony_ci	hvsilib_init(&hvc_opal_boot_priv.hvsi,
4158c2ecf20Sopenharmony_ci		     opal_get_chars, opal_put_chars_atomic,
4168c2ecf20Sopenharmony_ci		     index, 1);
4178c2ecf20Sopenharmony_ci	hvsilib_establish(&hvc_opal_boot_priv.hvsi);
4188c2ecf20Sopenharmony_ci}
4198c2ecf20Sopenharmony_ci#endif /* CONFIG_PPC_EARLY_DEBUG_OPAL_HVSI */
420