Commit b43848a1005cec6e952ce2b3268725a688aa74c6

Authored by Edgar E. Iglesias
1 parent ee118d95

xilinx: Add ethlite emulation.

Signed-off-by: Edgar E. Iglesias <edgar.iglesias@gmail.com>
Showing 1 changed file with 235 additions and 0 deletions
hw/xilinx_ethlite.c 0 → 100644
  1 +/*
  2 + * QEMU model of the Xilinx Ethernet Lite MAC.
  3 + *
  4 + * Copyright (c) 2009 Edgar E. Iglesias.
  5 + *
  6 + * Permission is hereby granted, free of charge, to any person obtaining a copy
  7 + * of this software and associated documentation files (the "Software"), to deal
  8 + * in the Software without restriction, including without limitation the rights
  9 + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10 + * copies of the Software, and to permit persons to whom the Software is
  11 + * furnished to do so, subject to the following conditions:
  12 + *
  13 + * The above copyright notice and this permission notice shall be included in
  14 + * all copies or substantial portions of the Software.
  15 + *
  16 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  19 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21 + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22 + * THE SOFTWARE.
  23 + */
  24 +
  25 +#include "sysbus.h"
  26 +#include "hw.h"
  27 +#include "net.h"
  28 +
  29 +#define D(x)
  30 +#define R_TX_BUF0 0
  31 +#define R_TX_LEN0 (0x07f4 / 4)
  32 +#define R_TX_GIE0 (0x07f8 / 4)
  33 +#define R_TX_CTRL0 (0x07fc / 4)
  34 +#define R_TX_BUF1 (0x0800 / 4)
  35 +#define R_TX_LEN1 (0x0ff4 / 4)
  36 +#define R_TX_CTRL1 (0x0ffc / 4)
  37 +
  38 +#define R_RX_BUF0 (0x1000 / 4)
  39 +#define R_RX_CTRL0 (0x17fc / 4)
  40 +#define R_RX_BUF1 (0x1800 / 4)
  41 +#define R_RX_CTRL1 (0x1ffc / 4)
  42 +#define R_MAX (0x2000 / 4)
  43 +
  44 +#define GIE_GIE 0x80000000
  45 +
  46 +#define CTRL_I 0x8
  47 +#define CTRL_P 0x2
  48 +#define CTRL_S 0x1
  49 +
  50 +struct xlx_ethlite
  51 +{
  52 + SysBusDevice busdev;
  53 + qemu_irq irq;
  54 + VLANClientState *vc;
  55 +
  56 + unsigned int c_tx_pingpong;
  57 + unsigned int c_rx_pingpong;
  58 + unsigned int txbuf;
  59 + unsigned int rxbuf;
  60 +
  61 + uint8_t macaddr[6];
  62 + uint32_t regs[R_MAX];
  63 +};
  64 +
  65 +static inline void eth_pulse_irq(struct xlx_ethlite *s)
  66 +{
  67 + /* Only the first gie reg is active. */
  68 + if (s->regs[R_TX_GIE0] & GIE_GIE) {
  69 + qemu_irq_pulse(s->irq);
  70 + }
  71 +}
  72 +
  73 +static uint32_t eth_readl (void *opaque, target_phys_addr_t addr)
  74 +{
  75 + struct xlx_ethlite *s = opaque;
  76 + uint32_t r = 0;
  77 +
  78 + addr >>= 2;
  79 +
  80 + switch (addr)
  81 + {
  82 + case R_TX_GIE0:
  83 + case R_TX_LEN0:
  84 + case R_TX_LEN1:
  85 + case R_TX_CTRL1:
  86 + case R_TX_CTRL0:
  87 + case R_RX_CTRL1:
  88 + case R_RX_CTRL0:
  89 + r = s->regs[addr];
  90 + D(qemu_log("%s %x=%x\n", __func__, addr * 4, r));
  91 + break;
  92 +
  93 + /* Rx packet data is endian fixed at the way into the rx rams. This
  94 + * speeds things up because the ethlite MAC does not have a len
  95 + * register. That means the CPU will issue MMIO reads for the entire
  96 + * 2k rx buffer even for small packets.
  97 + */
  98 + default:
  99 + r = s->regs[addr];
  100 + break;
  101 + }
  102 + return r;
  103 +}
  104 +
  105 +static void
  106 +eth_writel (void *opaque, target_phys_addr_t addr, uint32_t value)
  107 +{
  108 + struct xlx_ethlite *s = opaque;
  109 + unsigned int base = 0;
  110 +
  111 + addr >>= 2;
  112 + switch (addr)
  113 + {
  114 + case R_TX_CTRL0:
  115 + case R_TX_CTRL1:
  116 + if (addr == R_TX_CTRL1)
  117 + base = 0x800 / 4;
  118 +
  119 + D(qemu_log("%s addr=%x val=%x\n", __func__, addr * 4, value));
  120 + if ((value & (CTRL_P | CTRL_S)) == CTRL_S) {
  121 + qemu_send_packet(s->vc,
  122 + (void *) &s->regs[base],
  123 + s->regs[base + R_TX_LEN0]);
  124 + D(qemu_log("eth_tx %d\n", s->regs[base + R_TX_LEN0]));
  125 + if (s->regs[base + R_TX_CTRL0] & CTRL_I)
  126 + eth_pulse_irq(s);
  127 + } else if ((value & (CTRL_P | CTRL_S)) == (CTRL_P | CTRL_S)) {
  128 + memcpy(&s->macaddr[0], &s->regs[base], 6);
  129 + if (s->regs[base + R_TX_CTRL0] & CTRL_I)
  130 + eth_pulse_irq(s);
  131 + }
  132 +
  133 + /* We are fast and get ready pretty much immediately so
  134 + we actually never flip the S nor P bits to one. */
  135 + s->regs[addr] = value & ~(CTRL_P | CTRL_S);
  136 + break;
  137 +
  138 + /* Keep these native. */
  139 + case R_TX_LEN0:
  140 + case R_TX_LEN1:
  141 + case R_TX_GIE0:
  142 + case R_RX_CTRL0:
  143 + case R_RX_CTRL1:
  144 + D(qemu_log("%s addr=%x val=%x\n", __func__, addr * 4, value));
  145 + s->regs[addr] = value;
  146 + break;
  147 +
  148 + /* Packet data, make sure it stays BE. */
  149 + default:
  150 + s->regs[addr] = cpu_to_be32(value);
  151 + break;
  152 + }
  153 +}
  154 +
  155 +static CPUReadMemoryFunc *eth_read[] = {
  156 + NULL, NULL, &eth_readl,
  157 +};
  158 +
  159 +static CPUWriteMemoryFunc *eth_write[] = {
  160 + NULL, NULL, &eth_writel,
  161 +};
  162 +
  163 +static int eth_can_rx(void *opaque)
  164 +{
  165 + struct xlx_ethlite *s = opaque;
  166 + int r;
  167 + r = !(s->regs[R_RX_CTRL0] & CTRL_S);
  168 + qemu_log("%s %d\n", __func__, r);
  169 + return r;
  170 +}
  171 +
  172 +static void eth_rx(void *opaque, const uint8_t *buf, int size)
  173 +{
  174 + struct xlx_ethlite *s = opaque;
  175 + unsigned int rxbase = s->rxbuf * (0x800 / 4);
  176 + int i;
  177 +
  178 + /* DA filter. */
  179 + if (!(buf[0] & 0x80) && memcmp(&s->macaddr[0], buf, 6))
  180 + return;
  181 +
  182 + if (s->regs[rxbase + R_RX_CTRL0] & CTRL_S) {
  183 + D(qemu_log("ethlite lost packet %x\n", s->regs[R_RX_CTRL0]));
  184 + return;
  185 + }
  186 +
  187 + D(qemu_log("%s %d rxbase=%x\n", __func__, size, rxbase));
  188 + memcpy(&s->regs[rxbase + R_RX_BUF0], buf, size);
  189 +
  190 + /* Bring it into host endianess. */
  191 + for (i = 0; i < ((size + 3) / 4); i++) {
  192 + uint32_t d = s->regs[rxbase + R_RX_BUF0 + i];
  193 + s->regs[rxbase + R_RX_BUF0 + i] = be32_to_cpu(d);
  194 + }
  195 +
  196 + s->regs[rxbase + R_RX_CTRL0] |= CTRL_S;
  197 + if (s->regs[rxbase + R_RX_CTRL0] & CTRL_I)
  198 + eth_pulse_irq(s);
  199 +
  200 + /* If c_rx_pingpong was set flip buffers. */
  201 + s->rxbuf ^= s->c_rx_pingpong;
  202 + return;
  203 +}
  204 +
  205 +static void eth_cleanup(VLANClientState *vc)
  206 +{
  207 + struct xlx_ethlite *s = vc->opaque;
  208 + qemu_free(s);
  209 +}
  210 +
  211 +static void xilinx_ethlite_init(SysBusDevice *dev)
  212 +{
  213 + struct xlx_ethlite *s = FROM_SYSBUS(typeof (*s), dev);
  214 + int regs;
  215 +
  216 + sysbus_init_irq(dev, &s->irq);
  217 + s->c_tx_pingpong = qdev_get_prop_int(&dev->qdev, "txpingpong", 1);
  218 + s->c_rx_pingpong = qdev_get_prop_int(&dev->qdev, "rxpingpong", 1);
  219 + s->rxbuf = 0;
  220 +
  221 + regs = cpu_register_io_memory(0, eth_read, eth_write, s);
  222 + sysbus_init_mmio(dev, R_MAX * 4, regs);
  223 +
  224 + qdev_get_macaddr(&dev->qdev, s->macaddr);
  225 + s->vc = qdev_get_vlan_client(&dev->qdev,
  226 + eth_rx, eth_can_rx, eth_cleanup, s);
  227 +}
  228 +
  229 +static void xilinx_ethlite_register(void)
  230 +{
  231 + sysbus_register_dev("xilinx,ethlite", sizeof (struct xlx_ethlite),
  232 + xilinx_ethlite_init);
  233 +}
  234 +
  235 +device_init(xilinx_ethlite_register)
... ...