uboot/drivers/serial/serial_xen.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * (C) 2018 NXP
   4 * (C) 2020 EPAM Systems Inc.
   5 */
   6#include <common.h>
   7#include <cpu_func.h>
   8#include <dm.h>
   9#include <serial.h>
  10#include <watchdog.h>
  11#include <asm/global_data.h>
  12
  13#include <linux/bug.h>
  14
  15#include <xen/hvm.h>
  16#include <xen/events.h>
  17
  18#include <xen/interface/sched.h>
  19#include <xen/interface/hvm/hvm_op.h>
  20#include <xen/interface/hvm/params.h>
  21#include <xen/interface/io/console.h>
  22#include <xen/interface/io/ring.h>
  23
  24DECLARE_GLOBAL_DATA_PTR;
  25
  26u32 console_evtchn;
  27
  28/*
  29 * struct xen_uart_priv - Structure representing a Xen UART info
  30 * @intf:    Console I/O interface for Xen guest OSes
  31 * @evtchn:  Console event channel
  32 */
  33struct xen_uart_priv {
  34        struct xencons_interface *intf;
  35        u32 evtchn;
  36};
  37
  38int xen_serial_setbrg(struct udevice *dev, int baudrate)
  39{
  40        return 0;
  41}
  42
  43static int xen_serial_probe(struct udevice *dev)
  44{
  45        struct xen_uart_priv *priv = dev_get_priv(dev);
  46        u64 val = 0;
  47        unsigned long gfn;
  48        int ret;
  49
  50        ret = hvm_get_parameter(HVM_PARAM_CONSOLE_EVTCHN, &val);
  51        if (ret < 0 || val == 0)
  52                return ret;
  53
  54        priv->evtchn = val;
  55        console_evtchn = val;
  56
  57        ret = hvm_get_parameter(HVM_PARAM_CONSOLE_PFN, &val);
  58        if (ret < 0)
  59                return ret;
  60
  61        if (!val)
  62                return -EINVAL;
  63
  64        gfn = val;
  65        priv->intf = (struct xencons_interface *)(gfn << XEN_PAGE_SHIFT);
  66
  67        return 0;
  68}
  69
  70static int xen_serial_pending(struct udevice *dev, bool input)
  71{
  72        struct xen_uart_priv *priv = dev_get_priv(dev);
  73        struct xencons_interface *intf = priv->intf;
  74
  75        if (!input || intf->in_cons == intf->in_prod)
  76                return 0;
  77
  78        return 1;
  79}
  80
  81static int xen_serial_getc(struct udevice *dev)
  82{
  83        struct xen_uart_priv *priv = dev_get_priv(dev);
  84        struct xencons_interface *intf = priv->intf;
  85        XENCONS_RING_IDX cons;
  86        char c;
  87
  88        while (intf->in_cons == intf->in_prod)
  89                mb(); /* wait */
  90
  91        cons = intf->in_cons;
  92        mb();                   /* get pointers before reading ring */
  93
  94        c = intf->in[MASK_XENCONS_IDX(cons++, intf->in)];
  95
  96        mb();                   /* read ring before consuming */
  97        intf->in_cons = cons;
  98
  99        notify_remote_via_evtchn(priv->evtchn);
 100
 101        return c;
 102}
 103
 104static int __write_console(struct udevice *dev, const char *data, int len)
 105{
 106        struct xen_uart_priv *priv = dev_get_priv(dev);
 107        struct xencons_interface *intf = priv->intf;
 108        XENCONS_RING_IDX cons, prod;
 109        int sent = 0;
 110
 111        cons = intf->out_cons;
 112        prod = intf->out_prod;
 113        mb(); /* Update pointer */
 114
 115        WARN_ON((prod - cons) > sizeof(intf->out));
 116
 117        while ((sent < len) && ((prod - cons) < sizeof(intf->out)))
 118                intf->out[MASK_XENCONS_IDX(prod++, intf->out)] = data[sent++];
 119
 120        mb(); /* Update data before pointer */
 121        intf->out_prod = prod;
 122
 123        if (sent)
 124                notify_remote_via_evtchn(priv->evtchn);
 125
 126        return sent;
 127}
 128
 129static int write_console(struct udevice *dev, const char *data, int len)
 130{
 131        /*
 132         * Make sure the whole buffer is emitted, polling if
 133         * necessary.  We don't ever want to rely on the hvc daemon
 134         * because the most interesting console output is when the
 135         * kernel is crippled.
 136         */
 137        while (len) {
 138                int sent = __write_console(dev, data, len);
 139
 140                data += sent;
 141                len -= sent;
 142
 143                if (unlikely(len))
 144                        HYPERVISOR_sched_op(SCHEDOP_yield, NULL);
 145        }
 146
 147        return 0;
 148}
 149
 150static int xen_serial_putc(struct udevice *dev, const char ch)
 151{
 152        write_console(dev, &ch, 1);
 153
 154        return 0;
 155}
 156
 157static const struct dm_serial_ops xen_serial_ops = {
 158        .putc = xen_serial_putc,
 159        .getc = xen_serial_getc,
 160        .pending = xen_serial_pending,
 161};
 162
 163#if CONFIG_IS_ENABLED(OF_CONTROL)
 164static const struct udevice_id xen_serial_ids[] = {
 165        { .compatible = "xen,xen" },
 166        { }
 167};
 168#endif
 169
 170U_BOOT_DRIVER(serial_xen) = {
 171        .name                   = "serial_xen",
 172        .id                     = UCLASS_SERIAL,
 173#if CONFIG_IS_ENABLED(OF_CONTROL)
 174        .of_match               = xen_serial_ids,
 175#endif
 176        .priv_auto      = sizeof(struct xen_uart_priv),
 177        .probe                  = xen_serial_probe,
 178        .ops                    = &xen_serial_ops,
 179#if !CONFIG_IS_ENABLED(OF_CONTROL)
 180        .flags                  = DM_FLAG_PRE_RELOC,
 181#endif
 182};
 183