Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /*
0003  * opal driver interface to hvc_console.c
0004  *
0005  * Copyright 2011 Benjamin Herrenschmidt <benh@kernel.crashing.org>, IBM Corp.
0006  */
0007 
0008 #undef DEBUG
0009 
0010 #include <linux/types.h>
0011 #include <linux/init.h>
0012 #include <linux/delay.h>
0013 #include <linux/slab.h>
0014 #include <linux/console.h>
0015 #include <linux/of.h>
0016 #include <linux/of_irq.h>
0017 #include <linux/of_platform.h>
0018 #include <linux/export.h>
0019 #include <linux/interrupt.h>
0020 
0021 #include <asm/hvconsole.h>
0022 #include <asm/firmware.h>
0023 #include <asm/hvsi.h>
0024 #include <asm/udbg.h>
0025 #include <asm/opal.h>
0026 
0027 #include "hvc_console.h"
0028 
0029 static const char hvc_opal_name[] = "hvc_opal";
0030 
0031 static const struct of_device_id hvc_opal_match[] = {
0032     { .name = "serial", .compatible = "ibm,opal-console-raw" },
0033     { .name = "serial", .compatible = "ibm,opal-console-hvsi" },
0034     { },
0035 };
0036 
0037 typedef enum hv_protocol {
0038     HV_PROTOCOL_RAW,
0039     HV_PROTOCOL_HVSI
0040 } hv_protocol_t;
0041 
0042 struct hvc_opal_priv {
0043     hv_protocol_t       proto;  /* Raw data or HVSI packets */
0044     struct hvsi_priv    hvsi;   /* HVSI specific data */
0045 };
0046 static struct hvc_opal_priv *hvc_opal_privs[MAX_NR_HVC_CONSOLES];
0047 
0048 /* For early boot console */
0049 static struct hvc_opal_priv hvc_opal_boot_priv;
0050 static u32 hvc_opal_boot_termno;
0051 
0052 static const struct hv_ops hvc_opal_raw_ops = {
0053     .get_chars = opal_get_chars,
0054     .put_chars = opal_put_chars,
0055     .flush = opal_flush_chars,
0056     .notifier_add = notifier_add_irq,
0057     .notifier_del = notifier_del_irq,
0058     .notifier_hangup = notifier_hangup_irq,
0059 };
0060 
0061 static int hvc_opal_hvsi_get_chars(uint32_t vtermno, char *buf, int count)
0062 {
0063     struct hvc_opal_priv *pv = hvc_opal_privs[vtermno];
0064 
0065     if (WARN_ON(!pv))
0066         return -ENODEV;
0067 
0068     return hvsilib_get_chars(&pv->hvsi, buf, count);
0069 }
0070 
0071 static int hvc_opal_hvsi_put_chars(uint32_t vtermno, const char *buf, int count)
0072 {
0073     struct hvc_opal_priv *pv = hvc_opal_privs[vtermno];
0074 
0075     if (WARN_ON(!pv))
0076         return -ENODEV;
0077 
0078     return hvsilib_put_chars(&pv->hvsi, buf, count);
0079 }
0080 
0081 static int hvc_opal_hvsi_open(struct hvc_struct *hp, int data)
0082 {
0083     struct hvc_opal_priv *pv = hvc_opal_privs[hp->vtermno];
0084     int rc;
0085 
0086     pr_devel("HVSI@%x: do open !\n", hp->vtermno);
0087 
0088     rc = notifier_add_irq(hp, data);
0089     if (rc)
0090         return rc;
0091 
0092     return hvsilib_open(&pv->hvsi, hp);
0093 }
0094 
0095 static void hvc_opal_hvsi_close(struct hvc_struct *hp, int data)
0096 {
0097     struct hvc_opal_priv *pv = hvc_opal_privs[hp->vtermno];
0098 
0099     pr_devel("HVSI@%x: do close !\n", hp->vtermno);
0100 
0101     hvsilib_close(&pv->hvsi, hp);
0102 
0103     notifier_del_irq(hp, data);
0104 }
0105 
0106 static void hvc_opal_hvsi_hangup(struct hvc_struct *hp, int data)
0107 {
0108     struct hvc_opal_priv *pv = hvc_opal_privs[hp->vtermno];
0109 
0110     pr_devel("HVSI@%x: do hangup !\n", hp->vtermno);
0111 
0112     hvsilib_close(&pv->hvsi, hp);
0113 
0114     notifier_hangup_irq(hp, data);
0115 }
0116 
0117 static int hvc_opal_hvsi_tiocmget(struct hvc_struct *hp)
0118 {
0119     struct hvc_opal_priv *pv = hvc_opal_privs[hp->vtermno];
0120 
0121     if (!pv)
0122         return -EINVAL;
0123     return pv->hvsi.mctrl;
0124 }
0125 
0126 static int hvc_opal_hvsi_tiocmset(struct hvc_struct *hp, unsigned int set,
0127                 unsigned int clear)
0128 {
0129     struct hvc_opal_priv *pv = hvc_opal_privs[hp->vtermno];
0130 
0131     pr_devel("HVSI@%x: Set modem control, set=%x,clr=%x\n",
0132          hp->vtermno, set, clear);
0133 
0134     if (set & TIOCM_DTR)
0135         hvsilib_write_mctrl(&pv->hvsi, 1);
0136     else if (clear & TIOCM_DTR)
0137         hvsilib_write_mctrl(&pv->hvsi, 0);
0138 
0139     return 0;
0140 }
0141 
0142 static const struct hv_ops hvc_opal_hvsi_ops = {
0143     .get_chars = hvc_opal_hvsi_get_chars,
0144     .put_chars = hvc_opal_hvsi_put_chars,
0145     .flush = opal_flush_chars,
0146     .notifier_add = hvc_opal_hvsi_open,
0147     .notifier_del = hvc_opal_hvsi_close,
0148     .notifier_hangup = hvc_opal_hvsi_hangup,
0149     .tiocmget = hvc_opal_hvsi_tiocmget,
0150     .tiocmset = hvc_opal_hvsi_tiocmset,
0151 };
0152 
0153 static int hvc_opal_probe(struct platform_device *dev)
0154 {
0155     const struct hv_ops *ops;
0156     struct hvc_struct *hp;
0157     struct hvc_opal_priv *pv;
0158     hv_protocol_t proto;
0159     unsigned int termno, irq, boot = 0;
0160     const __be32 *reg;
0161 
0162     if (of_device_is_compatible(dev->dev.of_node, "ibm,opal-console-raw")) {
0163         proto = HV_PROTOCOL_RAW;
0164         ops = &hvc_opal_raw_ops;
0165     } else if (of_device_is_compatible(dev->dev.of_node,
0166                        "ibm,opal-console-hvsi")) {
0167         proto = HV_PROTOCOL_HVSI;
0168         ops = &hvc_opal_hvsi_ops;
0169     } else {
0170         pr_err("hvc_opal: Unknown protocol for %pOF\n",
0171                dev->dev.of_node);
0172         return -ENXIO;
0173     }
0174 
0175     reg = of_get_property(dev->dev.of_node, "reg", NULL);
0176     termno = reg ? be32_to_cpup(reg) : 0;
0177 
0178     /* Is it our boot one ? */
0179     if (hvc_opal_privs[termno] == &hvc_opal_boot_priv) {
0180         pv = hvc_opal_privs[termno];
0181         boot = 1;
0182     } else if (hvc_opal_privs[termno] == NULL) {
0183         pv = kzalloc(sizeof(struct hvc_opal_priv), GFP_KERNEL);
0184         if (!pv)
0185             return -ENOMEM;
0186         pv->proto = proto;
0187         hvc_opal_privs[termno] = pv;
0188         if (proto == HV_PROTOCOL_HVSI) {
0189             /*
0190              * We want put_chars to be atomic to avoid mangling of
0191              * hvsi packets.
0192              */
0193             hvsilib_init(&pv->hvsi,
0194                      opal_get_chars, opal_put_chars_atomic,
0195                      termno, 0);
0196         }
0197 
0198         /* Instanciate now to establish a mapping index==vtermno */
0199         hvc_instantiate(termno, termno, ops);
0200     } else {
0201         pr_err("hvc_opal: Device %pOF has duplicate terminal number #%d\n",
0202                dev->dev.of_node, termno);
0203         return -ENXIO;
0204     }
0205 
0206     pr_info("hvc%d: %s protocol on %pOF%s\n", termno,
0207         proto == HV_PROTOCOL_RAW ? "raw" : "hvsi",
0208         dev->dev.of_node,
0209         boot ? " (boot console)" : "");
0210 
0211     irq = irq_of_parse_and_map(dev->dev.of_node, 0);
0212     if (!irq) {
0213         pr_info("hvc%d: No interrupts property, using OPAL event\n",
0214                 termno);
0215         irq = opal_event_request(ilog2(OPAL_EVENT_CONSOLE_INPUT));
0216     }
0217 
0218     if (!irq) {
0219         pr_err("hvc_opal: Unable to map interrupt for device %pOF\n",
0220             dev->dev.of_node);
0221         return irq;
0222     }
0223 
0224     hp = hvc_alloc(termno, irq, ops, MAX_VIO_PUT_CHARS);
0225     if (IS_ERR(hp))
0226         return PTR_ERR(hp);
0227 
0228     /* hvc consoles on powernv may need to share a single irq */
0229     hp->flags = IRQF_SHARED;
0230     dev_set_drvdata(&dev->dev, hp);
0231 
0232     return 0;
0233 }
0234 
0235 static int hvc_opal_remove(struct platform_device *dev)
0236 {
0237     struct hvc_struct *hp = dev_get_drvdata(&dev->dev);
0238     int rc, termno;
0239 
0240     termno = hp->vtermno;
0241     rc = hvc_remove(hp);
0242     if (rc == 0) {
0243         if (hvc_opal_privs[termno] != &hvc_opal_boot_priv)
0244             kfree(hvc_opal_privs[termno]);
0245         hvc_opal_privs[termno] = NULL;
0246     }
0247     return rc;
0248 }
0249 
0250 static struct platform_driver hvc_opal_driver = {
0251     .probe      = hvc_opal_probe,
0252     .remove     = hvc_opal_remove,
0253     .driver     = {
0254         .name   = hvc_opal_name,
0255         .of_match_table = hvc_opal_match,
0256     }
0257 };
0258 
0259 static int __init hvc_opal_init(void)
0260 {
0261     if (!firmware_has_feature(FW_FEATURE_OPAL))
0262         return -ENODEV;
0263 
0264     /* Register as a vio device to receive callbacks */
0265     return platform_driver_register(&hvc_opal_driver);
0266 }
0267 device_initcall(hvc_opal_init);
0268 
0269 static void udbg_opal_putc(char c)
0270 {
0271     unsigned int termno = hvc_opal_boot_termno;
0272     int count = -1;
0273 
0274     if (c == '\n')
0275         udbg_opal_putc('\r');
0276 
0277     do {
0278         switch(hvc_opal_boot_priv.proto) {
0279         case HV_PROTOCOL_RAW:
0280             count = opal_put_chars(termno, &c, 1);
0281             break;
0282         case HV_PROTOCOL_HVSI:
0283             count = hvc_opal_hvsi_put_chars(termno, &c, 1);
0284             break;
0285         }
0286 
0287         /* This is needed for the cosole to flush
0288          * when there aren't any interrupts.
0289          */
0290         opal_flush_console(termno);
0291     } while(count == 0 || count == -EAGAIN);
0292 }
0293 
0294 static int udbg_opal_getc_poll(void)
0295 {
0296     unsigned int termno = hvc_opal_boot_termno;
0297     int rc = 0;
0298     char c;
0299 
0300     switch(hvc_opal_boot_priv.proto) {
0301     case HV_PROTOCOL_RAW:
0302         rc = opal_get_chars(termno, &c, 1);
0303         break;
0304     case HV_PROTOCOL_HVSI:
0305         rc = hvc_opal_hvsi_get_chars(termno, &c, 1);
0306         break;
0307     }
0308     if (!rc)
0309         return -1;
0310     return c;
0311 }
0312 
0313 static int udbg_opal_getc(void)
0314 {
0315     int ch;
0316     for (;;) {
0317         ch = udbg_opal_getc_poll();
0318         if (ch != -1)
0319             return ch;
0320     }
0321 }
0322 
0323 static void udbg_init_opal_common(void)
0324 {
0325     udbg_putc = udbg_opal_putc;
0326     udbg_getc = udbg_opal_getc;
0327     udbg_getc_poll = udbg_opal_getc_poll;
0328 }
0329 
0330 void __init hvc_opal_init_early(void)
0331 {
0332     struct device_node *stdout_node = of_node_get(of_stdout);
0333     const __be32 *termno;
0334     const struct hv_ops *ops;
0335     u32 index;
0336 
0337     /* If the console wasn't in /chosen, try /ibm,opal */
0338     if (!stdout_node) {
0339         struct device_node *opal, *np;
0340 
0341         /* Current OPAL takeover doesn't provide the stdout
0342          * path, so we hard wire it
0343          */
0344         opal = of_find_node_by_path("/ibm,opal/consoles");
0345         if (opal) {
0346             pr_devel("hvc_opal: Found consoles in new location\n");
0347         } else {
0348             opal = of_find_node_by_path("/ibm,opal");
0349             if (opal)
0350                 pr_devel("hvc_opal: "
0351                      "Found consoles in old location\n");
0352         }
0353         if (!opal)
0354             return;
0355         for_each_child_of_node(opal, np) {
0356             if (of_node_name_eq(np, "serial")) {
0357                 stdout_node = np;
0358                 break;
0359             }
0360         }
0361         of_node_put(opal);
0362     }
0363     if (!stdout_node)
0364         return;
0365     termno = of_get_property(stdout_node, "reg", NULL);
0366     index = termno ? be32_to_cpup(termno) : 0;
0367     if (index >= MAX_NR_HVC_CONSOLES)
0368         return;
0369     hvc_opal_privs[index] = &hvc_opal_boot_priv;
0370 
0371     /* Check the protocol */
0372     if (of_device_is_compatible(stdout_node, "ibm,opal-console-raw")) {
0373         hvc_opal_boot_priv.proto = HV_PROTOCOL_RAW;
0374         ops = &hvc_opal_raw_ops;
0375         pr_devel("hvc_opal: Found RAW console\n");
0376     }
0377     else if (of_device_is_compatible(stdout_node,"ibm,opal-console-hvsi")) {
0378         hvc_opal_boot_priv.proto = HV_PROTOCOL_HVSI;
0379         ops = &hvc_opal_hvsi_ops;
0380         hvsilib_init(&hvc_opal_boot_priv.hvsi,
0381                  opal_get_chars, opal_put_chars_atomic,
0382                  index, 1);
0383         /* HVSI, perform the handshake now */
0384         hvsilib_establish(&hvc_opal_boot_priv.hvsi);
0385         pr_devel("hvc_opal: Found HVSI console\n");
0386     } else
0387         goto out;
0388     hvc_opal_boot_termno = index;
0389     udbg_init_opal_common();
0390     add_preferred_console("hvc", index, NULL);
0391     hvc_instantiate(index, index, ops);
0392 out:
0393     of_node_put(stdout_node);
0394 }
0395 
0396 #ifdef CONFIG_PPC_EARLY_DEBUG_OPAL_RAW
0397 void __init udbg_init_debug_opal_raw(void)
0398 {
0399     u32 index = CONFIG_PPC_EARLY_DEBUG_OPAL_VTERMNO;
0400     hvc_opal_privs[index] = &hvc_opal_boot_priv;
0401     hvc_opal_boot_priv.proto = HV_PROTOCOL_RAW;
0402     hvc_opal_boot_termno = index;
0403     udbg_init_opal_common();
0404 }
0405 #endif /* CONFIG_PPC_EARLY_DEBUG_OPAL_RAW */
0406 
0407 #ifdef CONFIG_PPC_EARLY_DEBUG_OPAL_HVSI
0408 void __init udbg_init_debug_opal_hvsi(void)
0409 {
0410     u32 index = CONFIG_PPC_EARLY_DEBUG_OPAL_VTERMNO;
0411     hvc_opal_privs[index] = &hvc_opal_boot_priv;
0412     hvc_opal_boot_termno = index;
0413     udbg_init_opal_common();
0414     hvsilib_init(&hvc_opal_boot_priv.hvsi,
0415              opal_get_chars, opal_put_chars_atomic,
0416              index, 1);
0417     hvsilib_establish(&hvc_opal_boot_priv.hvsi);
0418 }
0419 #endif /* CONFIG_PPC_EARLY_DEBUG_OPAL_HVSI */