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