brintos

brintos / linux-shallow public Read only

0
0
Text · 10.0 KiB · 095c33a Raw
418 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * opal driver interface to hvc_console.c4 *5 * Copyright 2011 Benjamin Herrenschmidt <benh@kernel.crashing.org>, IBM Corp.6 */7 8#undef DEBUG9 10#include <linux/types.h>11#include <linux/init.h>12#include <linux/delay.h>13#include <linux/slab.h>14#include <linux/console.h>15#include <linux/of.h>16#include <linux/of_irq.h>17#include <linux/platform_device.h>18#include <linux/export.h>19#include <linux/interrupt.h>20 21#include <asm/hvconsole.h>22#include <asm/firmware.h>23#include <asm/hvsi.h>24#include <asm/udbg.h>25#include <asm/opal.h>26 27#include "hvc_console.h"28 29static const char hvc_opal_name[] = "hvc_opal";30 31static const struct of_device_id hvc_opal_match[] = {32	{ .name = "serial", .compatible = "ibm,opal-console-raw" },33	{ .name = "serial", .compatible = "ibm,opal-console-hvsi" },34	{ },35};36 37typedef enum hv_protocol {38	HV_PROTOCOL_RAW,39	HV_PROTOCOL_HVSI40} hv_protocol_t;41 42struct hvc_opal_priv {43	hv_protocol_t		proto;	/* Raw data or HVSI packets */44	struct hvsi_priv	hvsi;	/* HVSI specific data */45};46static struct hvc_opal_priv *hvc_opal_privs[MAX_NR_HVC_CONSOLES];47 48/* For early boot console */49static struct hvc_opal_priv hvc_opal_boot_priv;50static u32 hvc_opal_boot_termno;51 52static const struct hv_ops hvc_opal_raw_ops = {53	.get_chars = opal_get_chars,54	.put_chars = opal_put_chars,55	.flush = opal_flush_chars,56	.notifier_add = notifier_add_irq,57	.notifier_del = notifier_del_irq,58	.notifier_hangup = notifier_hangup_irq,59};60 61static ssize_t hvc_opal_hvsi_get_chars(uint32_t vtermno, u8 *buf, size_t count)62{63	struct hvc_opal_priv *pv = hvc_opal_privs[vtermno];64 65	if (WARN_ON(!pv))66		return -ENODEV;67 68	return hvsilib_get_chars(&pv->hvsi, buf, count);69}70 71static ssize_t hvc_opal_hvsi_put_chars(uint32_t vtermno, const u8 *buf,72				       size_t count)73{74	struct hvc_opal_priv *pv = hvc_opal_privs[vtermno];75 76	if (WARN_ON(!pv))77		return -ENODEV;78 79	return hvsilib_put_chars(&pv->hvsi, buf, count);80}81 82static int hvc_opal_hvsi_open(struct hvc_struct *hp, int data)83{84	struct hvc_opal_priv *pv = hvc_opal_privs[hp->vtermno];85	int rc;86 87	pr_devel("HVSI@%x: do open !\n", hp->vtermno);88 89	rc = notifier_add_irq(hp, data);90	if (rc)91		return rc;92 93	return hvsilib_open(&pv->hvsi, hp);94}95 96static void hvc_opal_hvsi_close(struct hvc_struct *hp, int data)97{98	struct hvc_opal_priv *pv = hvc_opal_privs[hp->vtermno];99 100	pr_devel("HVSI@%x: do close !\n", hp->vtermno);101 102	hvsilib_close(&pv->hvsi, hp);103 104	notifier_del_irq(hp, data);105}106 107static void hvc_opal_hvsi_hangup(struct hvc_struct *hp, int data)108{109	struct hvc_opal_priv *pv = hvc_opal_privs[hp->vtermno];110 111	pr_devel("HVSI@%x: do hangup !\n", hp->vtermno);112 113	hvsilib_close(&pv->hvsi, hp);114 115	notifier_hangup_irq(hp, data);116}117 118static int hvc_opal_hvsi_tiocmget(struct hvc_struct *hp)119{120	struct hvc_opal_priv *pv = hvc_opal_privs[hp->vtermno];121 122	if (!pv)123		return -EINVAL;124	return pv->hvsi.mctrl;125}126 127static int hvc_opal_hvsi_tiocmset(struct hvc_struct *hp, unsigned int set,128				unsigned int clear)129{130	struct hvc_opal_priv *pv = hvc_opal_privs[hp->vtermno];131 132	pr_devel("HVSI@%x: Set modem control, set=%x,clr=%x\n",133		 hp->vtermno, set, clear);134 135	if (set & TIOCM_DTR)136		hvsilib_write_mctrl(&pv->hvsi, 1);137	else if (clear & TIOCM_DTR)138		hvsilib_write_mctrl(&pv->hvsi, 0);139 140	return 0;141}142 143static const struct hv_ops hvc_opal_hvsi_ops = {144	.get_chars = hvc_opal_hvsi_get_chars,145	.put_chars = hvc_opal_hvsi_put_chars,146	.flush = opal_flush_chars,147	.notifier_add = hvc_opal_hvsi_open,148	.notifier_del = hvc_opal_hvsi_close,149	.notifier_hangup = hvc_opal_hvsi_hangup,150	.tiocmget = hvc_opal_hvsi_tiocmget,151	.tiocmset = hvc_opal_hvsi_tiocmset,152};153 154static int hvc_opal_probe(struct platform_device *dev)155{156	const struct hv_ops *ops;157	struct hvc_struct *hp;158	struct hvc_opal_priv *pv;159	hv_protocol_t proto;160	unsigned int termno, irq, boot = 0;161	const __be32 *reg;162 163	if (of_device_is_compatible(dev->dev.of_node, "ibm,opal-console-raw")) {164		proto = HV_PROTOCOL_RAW;165		ops = &hvc_opal_raw_ops;166	} else if (of_device_is_compatible(dev->dev.of_node,167					   "ibm,opal-console-hvsi")) {168		proto = HV_PROTOCOL_HVSI;169		ops = &hvc_opal_hvsi_ops;170	} else {171		pr_err("hvc_opal: Unknown protocol for %pOF\n",172		       dev->dev.of_node);173		return -ENXIO;174	}175 176	reg = of_get_property(dev->dev.of_node, "reg", NULL);177	termno = reg ? be32_to_cpup(reg) : 0;178 179	/* Is it our boot one ? */180	if (hvc_opal_privs[termno] == &hvc_opal_boot_priv) {181		pv = hvc_opal_privs[termno];182		boot = 1;183	} else if (hvc_opal_privs[termno] == NULL) {184		pv = kzalloc(sizeof(struct hvc_opal_priv), GFP_KERNEL);185		if (!pv)186			return -ENOMEM;187		pv->proto = proto;188		hvc_opal_privs[termno] = pv;189		if (proto == HV_PROTOCOL_HVSI) {190			/*191			 * We want put_chars to be atomic to avoid mangling of192			 * hvsi packets.193			 */194			hvsilib_init(&pv->hvsi,195				     opal_get_chars, opal_put_chars_atomic,196				     termno, 0);197		}198 199		/* Instanciate now to establish a mapping index==vtermno */200		hvc_instantiate(termno, termno, ops);201	} else {202		pr_err("hvc_opal: Device %pOF has duplicate terminal number #%d\n",203		       dev->dev.of_node, termno);204		return -ENXIO;205	}206 207	pr_info("hvc%d: %s protocol on %pOF%s\n", termno,208		proto == HV_PROTOCOL_RAW ? "raw" : "hvsi",209		dev->dev.of_node,210		boot ? " (boot console)" : "");211 212	irq = irq_of_parse_and_map(dev->dev.of_node, 0);213	if (!irq) {214		pr_info("hvc%d: No interrupts property, using OPAL event\n",215				termno);216		irq = opal_event_request(ilog2(OPAL_EVENT_CONSOLE_INPUT));217	}218 219	if (!irq) {220		pr_err("hvc_opal: Unable to map interrupt for device %pOF\n",221			dev->dev.of_node);222		return irq;223	}224 225	hp = hvc_alloc(termno, irq, ops, MAX_VIO_PUT_CHARS);226	if (IS_ERR(hp))227		return PTR_ERR(hp);228 229	/* hvc consoles on powernv may need to share a single irq */230	hp->flags = IRQF_SHARED;231	dev_set_drvdata(&dev->dev, hp);232 233	return 0;234}235 236static void hvc_opal_remove(struct platform_device *dev)237{238	struct hvc_struct *hp = dev_get_drvdata(&dev->dev);239	int termno;240 241	termno = hp->vtermno;242	hvc_remove(hp);243	if (hvc_opal_privs[termno] != &hvc_opal_boot_priv)244		kfree(hvc_opal_privs[termno]);245	hvc_opal_privs[termno] = NULL;246}247 248static struct platform_driver hvc_opal_driver = {249	.probe		= hvc_opal_probe,250	.remove_new	= hvc_opal_remove,251	.driver		= {252		.name	= hvc_opal_name,253		.of_match_table	= hvc_opal_match,254	}255};256 257static int __init hvc_opal_init(void)258{259	if (!firmware_has_feature(FW_FEATURE_OPAL))260		return -ENODEV;261 262	/* Register as a vio device to receive callbacks */263	return platform_driver_register(&hvc_opal_driver);264}265device_initcall(hvc_opal_init);266 267static void udbg_opal_putc(char c)268{269	unsigned int termno = hvc_opal_boot_termno;270	int count = -1;271 272	if (c == '\n')273		udbg_opal_putc('\r');274 275	do {276		switch(hvc_opal_boot_priv.proto) {277		case HV_PROTOCOL_RAW:278			count = opal_put_chars(termno, &c, 1);279			break;280		case HV_PROTOCOL_HVSI:281			count = hvc_opal_hvsi_put_chars(termno, &c, 1);282			break;283		}284 285		/* This is needed for the cosole to flush286		 * when there aren't any interrupts.287		 */288		opal_flush_console(termno);289	} while(count == 0 || count == -EAGAIN);290}291 292static int udbg_opal_getc_poll(void)293{294	unsigned int termno = hvc_opal_boot_termno;295	int rc = 0;296	char c;297 298	switch(hvc_opal_boot_priv.proto) {299	case HV_PROTOCOL_RAW:300		rc = opal_get_chars(termno, &c, 1);301		break;302	case HV_PROTOCOL_HVSI:303		rc = hvc_opal_hvsi_get_chars(termno, &c, 1);304		break;305	}306	if (!rc)307		return -1;308	return c;309}310 311static int udbg_opal_getc(void)312{313	int ch;314	for (;;) {315		ch = udbg_opal_getc_poll();316		if (ch != -1)317			return ch;318	}319}320 321static void udbg_init_opal_common(void)322{323	udbg_putc = udbg_opal_putc;324	udbg_getc = udbg_opal_getc;325	udbg_getc_poll = udbg_opal_getc_poll;326}327 328void __init hvc_opal_init_early(void)329{330	struct device_node *stdout_node = of_node_get(of_stdout);331	const __be32 *termno;332	const struct hv_ops *ops;333	u32 index;334 335	/* If the console wasn't in /chosen, try /ibm,opal */336	if (!stdout_node) {337		struct device_node *opal, *np;338 339		/* Current OPAL takeover doesn't provide the stdout340		 * path, so we hard wire it341		 */342		opal = of_find_node_by_path("/ibm,opal/consoles");343		if (opal) {344			pr_devel("hvc_opal: Found consoles in new location\n");345		} else {346			opal = of_find_node_by_path("/ibm,opal");347			if (opal)348				pr_devel("hvc_opal: "349					 "Found consoles in old location\n");350		}351		if (!opal)352			return;353		for_each_child_of_node(opal, np) {354			if (of_node_name_eq(np, "serial")) {355				stdout_node = np;356				break;357			}358		}359		of_node_put(opal);360	}361	if (!stdout_node)362		return;363	termno = of_get_property(stdout_node, "reg", NULL);364	index = termno ? be32_to_cpup(termno) : 0;365	if (index >= MAX_NR_HVC_CONSOLES)366		return;367	hvc_opal_privs[index] = &hvc_opal_boot_priv;368 369	/* Check the protocol */370	if (of_device_is_compatible(stdout_node, "ibm,opal-console-raw")) {371		hvc_opal_boot_priv.proto = HV_PROTOCOL_RAW;372		ops = &hvc_opal_raw_ops;373		pr_devel("hvc_opal: Found RAW console\n");374	}375	else if (of_device_is_compatible(stdout_node,"ibm,opal-console-hvsi")) {376		hvc_opal_boot_priv.proto = HV_PROTOCOL_HVSI;377		ops = &hvc_opal_hvsi_ops;378		hvsilib_init(&hvc_opal_boot_priv.hvsi,379			     opal_get_chars, opal_put_chars_atomic,380			     index, 1);381		/* HVSI, perform the handshake now */382		hvsilib_establish(&hvc_opal_boot_priv.hvsi);383		pr_devel("hvc_opal: Found HVSI console\n");384	} else385		goto out;386	hvc_opal_boot_termno = index;387	udbg_init_opal_common();388	add_preferred_console("hvc", index, NULL);389	hvc_instantiate(index, index, ops);390out:391	of_node_put(stdout_node);392}393 394#ifdef CONFIG_PPC_EARLY_DEBUG_OPAL_RAW395void __init udbg_init_debug_opal_raw(void)396{397	u32 index = CONFIG_PPC_EARLY_DEBUG_OPAL_VTERMNO;398	hvc_opal_privs[index] = &hvc_opal_boot_priv;399	hvc_opal_boot_priv.proto = HV_PROTOCOL_RAW;400	hvc_opal_boot_termno = index;401	udbg_init_opal_common();402}403#endif /* CONFIG_PPC_EARLY_DEBUG_OPAL_RAW */404 405#ifdef CONFIG_PPC_EARLY_DEBUG_OPAL_HVSI406void __init udbg_init_debug_opal_hvsi(void)407{408	u32 index = CONFIG_PPC_EARLY_DEBUG_OPAL_VTERMNO;409	hvc_opal_privs[index] = &hvc_opal_boot_priv;410	hvc_opal_boot_termno = index;411	udbg_init_opal_common();412	hvsilib_init(&hvc_opal_boot_priv.hvsi,413		     opal_get_chars, opal_put_chars_atomic,414		     index, 1);415	hvsilib_establish(&hvc_opal_boot_priv.hvsi);416}417#endif /* CONFIG_PPC_EARLY_DEBUG_OPAL_HVSI */418