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