summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeng Fan <peng.fan@nxp.com>2018-11-15 13:18:53 +0800
committerPeng Fan <peng.fan@nxp.com>2020-05-27 15:37:54 +0800
commit00707e9398c8397b6a90acc50634c2b538f6ab67 (patch)
tree9855528e672d2cb59706c7c17935463313150460
parentc35bf3b38c416cb89111003d20eb8842b5b561ef (diff)
MLK-20373-5 serial: serial_xen: support normal uboot console
Support output/input using `xl console [domid]`. Signed-off-by: Peng Fan <peng.fan@nxp.com> Reviewed-by: Flynn xu <flynn.xu@nxp.com> (cherry picked from commit 75a9833506aa13c1d5a8641c81ac951c41ced55e) (cherry picked from commit 5a495c39679b6641d369fc0c9cfdadafd8a408f6) (cherry picked from commit 6f3cc8513ac1afe0cf679422ae82c4a5930a8c64)
-rw-r--r--drivers/serial/Kconfig5
-rw-r--r--drivers/serial/serial_xen.c204
2 files changed, 181 insertions, 28 deletions
diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
index 21597b7234..31cf28e864 100644
--- a/drivers/serial/Kconfig
+++ b/drivers/serial/Kconfig
@@ -187,6 +187,9 @@ choice
depends on DEBUG_UART
default DEBUG_UART_NS16550
+config DEBUG_UART_XEN
+ bool "debug uart xen"
+
config DEBUG_UART_ALTERA_JTAGUART
bool "Altera JTAG UART"
help
@@ -394,6 +397,7 @@ config DEBUG_UART_BASE
hex "Base address of UART"
depends on DEBUG_UART
default 0 if DEBUG_UART_SANDBOX
+ default 0 if DEBUG_UART_XEN
help
This is the base address of your UART for memory-mapped UARTs.
@@ -404,6 +408,7 @@ config DEBUG_UART_CLOCK
int "UART input clock"
depends on DEBUG_UART
default 0 if DEBUG_UART_SANDBOX
+ default 0 if DEBUG_UART_XEN
help
The UART input clock determines the speed of the internal UART
circuitry. The baud rate is derived from this by dividing the input
diff --git a/drivers/serial/serial_xen.c b/drivers/serial/serial_xen.c
index ce0c36319b..416d0ac191 100644
--- a/drivers/serial/serial_xen.c
+++ b/drivers/serial/serial_xen.c
@@ -5,66 +5,214 @@
*/
#include <common.h>
+#include <debug_uart.h>
#include <dm.h>
#include <errno.h>
+#include <fdtdec.h>
+#include <linux/compiler.h>
#include <serial.h>
-extern void xenprintf(const char *buf);
-extern void xenprintc(const char c);
-#ifndef CONFIG_DM_SERIAL
+#include <xen/events.h>
+#include <xen/hvm.h>
+#include <xen/interface/sched.h>
+#include <xen/interface/hvm/hvm_op.h>
+#include <xen/interface/hvm/params.h>
+#include <xen/interface/io/console.h>
+#include <xen/interface/io/ring.h>
-static void xen_debug_serial_putc(const char c)
+DECLARE_GLOBAL_DATA_PTR;
+
+#ifdef CONFIG_DM_SERIAL
+struct xen_uart_priv {
+ struct xencons_interface *intf;
+ u32 evtchn;
+ int vtermno;
+ struct hvc_struct *hvc;
+ grant_ref_t gntref;
+};
+
+int xen_serial_setbrg(struct udevice *dev, int baudrate)
{
- /* If \n, also do \r */
- if (c == '\n')
- serial_putc('\r');
+ return 0;
+}
- xenprintc(c);
+static int xen_serial_probe(struct udevice *dev)
+{
+ struct xen_uart_priv *priv = dev_get_priv(dev);
+ u64 v = 0;
+ unsigned long gfn;
+ int r;
+
+ r = hvm_get_parameter(HVM_PARAM_CONSOLE_EVTCHN, &v);
+ if (r < 0 || v == 0)
+ return r;
+
+ priv->evtchn = v;
+
+ r = hvm_get_parameter(HVM_PARAM_CONSOLE_PFN, &v);
+ if (r < 0 || v == 0)
+ return -ENODEV;
+
+ gfn = v;
+
+ priv->intf = (struct xencons_interface *)(gfn << XEN_PAGE_SHIFT);
+ if (!priv->intf)
+ return -EINVAL;
+
+ return 0;
}
-static void xen_debug_serial_puts(const char *buf)
+static int xen_serial_pending(struct udevice *dev, bool input)
{
- xenprintf(buf);
+ struct xen_uart_priv *priv = dev_get_priv(dev);
+ struct xencons_interface *intf = priv->intf;
+
+ if (!input || intf->in_cons == intf->in_prod)
+ return 0;
+
+ return 1;
}
-static int xen_debug_serial_start(void)
+static int xen_serial_getc(struct udevice *dev)
{
- return 0;
+ struct xen_uart_priv *priv = dev_get_priv(dev);
+ struct xencons_interface *intf = priv->intf;
+ XENCONS_RING_IDX cons;
+ char c;
+
+ while (intf->in_cons == intf->in_prod) {
+ mb(); /* wait */
+ }
+
+ cons = intf->in_cons;
+ mb(); /* get pointers before reading ring */
+
+ c = intf->in[MASK_XENCONS_IDX(cons++, intf->in)];
+
+ mb(); /* read ring before consuming */
+ intf->in_cons = cons;
+
+ notify_remote_via_evtchn(priv->evtchn);
+
+ return c;
}
-static void xen_debug_serial_setbrg(void)
+static int __write_console(struct udevice *dev, const char *data, int len)
{
+ struct xen_uart_priv *priv = dev_get_priv(dev);
+ struct xencons_interface *intf = priv->intf;
+ XENCONS_RING_IDX cons, prod;
+ int sent = 0;
+
+ cons = intf->out_cons;
+ prod = intf->out_prod;
+ mb(); /* Update pointer */
+
+ WARN_ON((prod - cons) > sizeof(intf->out));
+
+ while ((sent < len) && ((prod - cons) < sizeof(intf->out)))
+ intf->out[MASK_XENCONS_IDX(prod++, intf->out)] = data[sent++];
+
+ mb(); /* Update data before pointer */
+ intf->out_prod = prod;
+
+ if (sent)
+ notify_remote_via_evtchn(priv->evtchn);
+ if (data[sent - 1] == '\n')
+ serial_puts("\r");
+
+ return sent;
+}
+
+static int write_console(struct udevice *dev, const char *data, int len)
+{
+ /*
+ * Make sure the whole buffer is emitted, polling if
+ * necessary. We don't ever want to rely on the hvc daemon
+ * because the most interesting console output is when the
+ * kernel is crippled.
+ */
+ while (len) {
+ int sent = __write_console(dev, data, len);
+
+ data += sent;
+ len -= sent;
+
+ if (unlikely(len))
+ HYPERVISOR_sched_op(SCHEDOP_yield, NULL);
+ }
+
+ return 0;
}
-static int xen_debug_serial_getc(void)
+static int xen_serial_puts(struct udevice *dev, const char *str)
{
+#ifdef CONFIG_SPL_BUILD
+ (void)HYPERVISOR_console_io(CONSOLEIO_write, strlen(str), str);
+#else
+ write_console(dev, str, strlen(str));
+#endif
+
return 0;
}
-static int xen_debug_serial_tstc(void)
+static int xen_serial_putc(struct udevice *dev, const char ch)
{
+#ifdef CONFIG_SPL_BUILD
+ (void)HYPERVISOR_console_io(CONSOLEIO_write, 1, &ch);
+#else
+ write_console(dev, &ch, 1);
+#endif
+
return 0;
}
-static struct serial_device xen_debug_serial_drv = {
- .name = "xen_debug_serial",
- .start = xen_debug_serial_start,
- .stop = NULL,
- .setbrg = xen_debug_serial_setbrg,
- .putc = xen_debug_serial_putc,
- .puts = xen_debug_serial_puts,
- .getc = xen_debug_serial_getc,
- .tstc = xen_debug_serial_tstc,
+static const struct dm_serial_ops xen_serial_ops = {
+ .puts = xen_serial_puts,
+ .putc = xen_serial_putc,
+ .getc = xen_serial_getc,
+ .pending = xen_serial_pending,
+};
+
+static const struct udevice_id xen_serial_ids[] = {
+ { .compatible = "xen,xen" },
+ { }
};
-void xen_debug_serial_initialize(void)
+U_BOOT_DRIVER(serial_xen) = {
+ .name = "serial_xen",
+ .id = UCLASS_SERIAL,
+ .of_match = xen_serial_ids,
+ .priv_auto_alloc_size = sizeof(struct xen_uart_priv),
+ .probe = xen_serial_probe,
+ .ops = &xen_serial_ops,
+ .flags = DM_FLAG_PRE_RELOC | DM_FLAG_IGNORE_DEFAULT_CLKS,
+};
+#else
+__weak struct serial_device *default_serial_console(void)
{
- serial_register(&xen_debug_serial_drv);
+ return NULL;
}
-__weak struct serial_device *default_serial_console(void)
+#endif
+
+#ifdef CONFIG_DEBUG_UART_XEN
+void _debug_uart_init(void)
+{
+}
+
+void _debug_uart_putc(int ch)
{
- return &xen_debug_serial_drv;
+ /* If \n, also do \r */
+ if (ch == '\n')
+ serial_putc('\r');
+
+ (void)HYPERVISOR_console_io(CONSOLEIO_write, 1, &ch);
+
+ return;
}
+
+DEBUG_UART_FUNCS
+
#endif