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