diff options
Diffstat (limited to 'hw/net/xilinx_ethlite.c')
| -rw-r--r-- | hw/net/xilinx_ethlite.c | 314 |
1 files changed, 215 insertions, 99 deletions
diff --git a/hw/net/xilinx_ethlite.c b/hw/net/xilinx_ethlite.c index 4c0c7fcae3..14bf2b2e17 100644 --- a/hw/net/xilinx_ethlite.c +++ b/hw/net/xilinx_ethlite.c @@ -2,6 +2,7 @@ * QEMU model of the Xilinx Ethernet Lite MAC. * * Copyright (c) 2009 Edgar E. Iglesias. + * Copyright (c) 2024 Linaro, Ltd * * DS580: https://docs.amd.com/v/u/en-US/xps_ethernetlite * LogiCORE IP XPS Ethernet Lite Media Access Controller @@ -27,28 +28,34 @@ #include "qemu/osdep.h" #include "qemu/module.h" +#include "qemu/bitops.h" #include "qom/object.h" -#include "exec/tswap.h" +#include "qapi/error.h" #include "hw/sysbus.h" #include "hw/irq.h" #include "hw/qdev-properties.h" +#include "hw/misc/unimp.h" #include "net/net.h" #include "trace.h" -#define R_TX_BUF0 0 #define BUFSZ_MAX 0x07e4 -#define R_TX_LEN0 (0x07f4 / 4) -#define R_TX_GIE0 (0x07f8 / 4) -#define R_TX_CTRL0 (0x07fc / 4) -#define R_TX_BUF1 (0x0800 / 4) -#define R_TX_LEN1 (0x0ff4 / 4) -#define R_TX_CTRL1 (0x0ffc / 4) - -#define R_RX_BUF0 (0x1000 / 4) -#define R_RX_CTRL0 (0x17fc / 4) -#define R_RX_BUF1 (0x1800 / 4) -#define R_RX_CTRL1 (0x1ffc / 4) -#define R_MAX (0x2000 / 4) +#define A_MDIO_BASE 0x07e4 +#define A_TX_BASE0 0x07f4 +#define A_TX_BASE1 0x0ff4 +#define A_RX_BASE0 0x17fc +#define A_RX_BASE1 0x1ffc + +enum { + TX_LEN = 0, + TX_GIE = 1, + TX_CTRL = 2, + TX_MAX +}; + +enum { + RX_CTRL = 0, + RX_MAX +}; #define GIE_GIE 0x80000000 @@ -56,6 +63,21 @@ #define CTRL_P 0x2 #define CTRL_S 0x1 +typedef struct XlnxXpsEthLitePort { + MemoryRegion txio; + MemoryRegion rxio; + MemoryRegion txbuf; + MemoryRegion rxbuf; + + struct { + uint32_t tx_len; + uint32_t tx_gie; + uint32_t tx_ctrl; + + uint32_t rx_ctrl; + } reg; +} XlnxXpsEthLitePort; + #define TYPE_XILINX_ETHLITE "xlnx.xps-ethernetlite" OBJECT_DECLARE_SIMPLE_TYPE(XlnxXpsEthLite, XILINX_ETHLITE) @@ -63,7 +85,7 @@ struct XlnxXpsEthLite { SysBusDevice parent_obj; - MemoryRegion mmio; + MemoryRegion container; qemu_irq irq; NICState *nic; NICConf conf; @@ -72,125 +94,176 @@ struct XlnxXpsEthLite uint32_t c_rx_pingpong; unsigned int port_index; /* dual port RAM index */ - uint32_t regs[R_MAX]; + UnimplementedDeviceState rsvd; + UnimplementedDeviceState mdio; + XlnxXpsEthLitePort port[2]; }; static inline void eth_pulse_irq(XlnxXpsEthLite *s) { /* Only the first gie reg is active. */ - if (s->regs[R_TX_GIE0] & GIE_GIE) { + if (s->port[0].reg.tx_gie & GIE_GIE) { qemu_irq_pulse(s->irq); } } -static uint64_t -eth_read(void *opaque, hwaddr addr, unsigned int size) +static unsigned addr_to_port_index(hwaddr addr) +{ + return extract64(addr, 11, 1); +} + +static void *txbuf_ptr(XlnxXpsEthLite *s, unsigned port_index) +{ + return memory_region_get_ram_ptr(&s->port[port_index].txbuf); +} + +static void *rxbuf_ptr(XlnxXpsEthLite *s, unsigned port_index) +{ + return memory_region_get_ram_ptr(&s->port[port_index].rxbuf); +} + +static uint64_t port_tx_read(void *opaque, hwaddr addr, unsigned int size) { XlnxXpsEthLite *s = opaque; + unsigned port_index = addr_to_port_index(addr); uint32_t r = 0; - addr >>= 2; - - switch (addr) - { - case R_TX_GIE0: - case R_TX_LEN0: - case R_TX_LEN1: - case R_TX_CTRL1: - case R_TX_CTRL0: - case R_RX_CTRL1: - case R_RX_CTRL0: - r = s->regs[addr]; - break; - - default: - r = tswap32(s->regs[addr]); - break; + switch (addr >> 2) { + case TX_LEN: + r = s->port[port_index].reg.tx_len; + break; + case TX_GIE: + r = s->port[port_index].reg.tx_gie; + break; + case TX_CTRL: + r = s->port[port_index].reg.tx_ctrl; + break; + default: + g_assert_not_reached(); } + return r; } -static void -eth_write(void *opaque, hwaddr addr, - uint64_t val64, unsigned int size) +static void port_tx_write(void *opaque, hwaddr addr, uint64_t value, + unsigned int size) { XlnxXpsEthLite *s = opaque; - unsigned int base = 0; - uint32_t value = val64; - - addr >>= 2; - switch (addr) - { - case R_TX_CTRL0: - case R_TX_CTRL1: - if (addr == R_TX_CTRL1) - base = 0x800 / 4; - - if ((value & (CTRL_P | CTRL_S)) == CTRL_S) { - qemu_send_packet(qemu_get_queue(s->nic), - (void *) &s->regs[base], - s->regs[base + R_TX_LEN0]); - if (s->regs[base + R_TX_CTRL0] & CTRL_I) - eth_pulse_irq(s); - } else if ((value & (CTRL_P | CTRL_S)) == (CTRL_P | CTRL_S)) { - memcpy(&s->conf.macaddr.a[0], &s->regs[base], 6); - if (s->regs[base + R_TX_CTRL0] & CTRL_I) - eth_pulse_irq(s); + unsigned port_index = addr_to_port_index(addr); + + switch (addr >> 2) { + case TX_LEN: + s->port[port_index].reg.tx_len = value; + break; + case TX_GIE: + s->port[port_index].reg.tx_gie = value; + break; + case TX_CTRL: + if ((value & (CTRL_P | CTRL_S)) == CTRL_S) { + qemu_send_packet(qemu_get_queue(s->nic), + txbuf_ptr(s, port_index), + s->port[port_index].reg.tx_len); + if (s->port[port_index].reg.tx_ctrl & CTRL_I) { + eth_pulse_irq(s); + } + } else if ((value & (CTRL_P | CTRL_S)) == (CTRL_P | CTRL_S)) { + memcpy(&s->conf.macaddr.a[0], txbuf_ptr(s, port_index), 6); + if (s->port[port_index].reg.tx_ctrl & CTRL_I) { + eth_pulse_irq(s); } + } + /* + * We are fast and get ready pretty much immediately + * so we actually never flip the S nor P bits to one. + */ + s->port[port_index].reg.tx_ctrl = value & ~(CTRL_P | CTRL_S); + break; + default: + g_assert_not_reached(); + } +} + +static const MemoryRegionOps eth_porttx_ops = { + .read = port_tx_read, + .write = port_tx_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; - /* We are fast and get ready pretty much immediately so - we actually never flip the S nor P bits to one. */ - s->regs[addr] = value & ~(CTRL_P | CTRL_S); - break; +static uint64_t port_rx_read(void *opaque, hwaddr addr, unsigned int size) +{ + XlnxXpsEthLite *s = opaque; + unsigned port_index = addr_to_port_index(addr); + uint32_t r = 0; - /* Keep these native. */ - case R_RX_CTRL0: - case R_RX_CTRL1: - if (!(value & CTRL_S)) { - qemu_flush_queued_packets(qemu_get_queue(s->nic)); - } - /* fall through */ - case R_TX_LEN0: - case R_TX_LEN1: - case R_TX_GIE0: - s->regs[addr] = value; - break; - - default: - s->regs[addr] = tswap32(value); - break; + switch (addr >> 2) { + case RX_CTRL: + r = s->port[port_index].reg.rx_ctrl; + break; + default: + g_assert_not_reached(); } + + return r; } -static const MemoryRegionOps eth_ops = { - .read = eth_read, - .write = eth_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4 +static void port_rx_write(void *opaque, hwaddr addr, uint64_t value, + unsigned int size) +{ + XlnxXpsEthLite *s = opaque; + unsigned port_index = addr_to_port_index(addr); + + switch (addr >> 2) { + case RX_CTRL: + if (!(value & CTRL_S)) { + qemu_flush_queued_packets(qemu_get_queue(s->nic)); + } + s->port[port_index].reg.rx_ctrl = value; + break; + default: + g_assert_not_reached(); } +} + +static const MemoryRegionOps eth_portrx_ops = { + .read = port_rx_read, + .write = port_rx_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, }; static bool eth_can_rx(NetClientState *nc) { XlnxXpsEthLite *s = qemu_get_nic_opaque(nc); - unsigned int rxbase = s->port_index * (0x800 / 4); - return !(s->regs[rxbase + R_RX_CTRL0] & CTRL_S); + return !(s->port[s->port_index].reg.rx_ctrl & CTRL_S); } static ssize_t eth_rx(NetClientState *nc, const uint8_t *buf, size_t size) { XlnxXpsEthLite *s = qemu_get_nic_opaque(nc); - unsigned int rxbase = s->port_index * (0x800 / 4); + unsigned int port_index = s->port_index; /* DA filter. */ if (!(buf[0] & 0x80) && memcmp(&s->conf.macaddr.a[0], buf, 6)) return size; - if (s->regs[rxbase + R_RX_CTRL0] & CTRL_S) { - trace_ethlite_pkt_lost(s->regs[R_RX_CTRL0]); + if (s->port[port_index].reg.rx_ctrl & CTRL_S) { + trace_ethlite_pkt_lost(s->port[port_index].reg.rx_ctrl); return -1; } @@ -198,10 +271,10 @@ static ssize_t eth_rx(NetClientState *nc, const uint8_t *buf, size_t size) trace_ethlite_pkt_size_too_big(size); return -1; } - memcpy(&s->regs[rxbase + R_RX_BUF0], buf, size); + memcpy(rxbuf_ptr(s, port_index), buf, size); - s->regs[rxbase + R_RX_CTRL0] |= CTRL_S; - if (s->regs[R_RX_CTRL0] & CTRL_I) { + s->port[port_index].reg.rx_ctrl |= CTRL_S; + if (s->port[port_index].reg.rx_ctrl & CTRL_I) { eth_pulse_irq(s); } @@ -228,6 +301,52 @@ static void xilinx_ethlite_realize(DeviceState *dev, Error **errp) { XlnxXpsEthLite *s = XILINX_ETHLITE(dev); + memory_region_init(&s->container, OBJECT(dev), + "xlnx.xps-ethernetlite", 0x2000); + + object_initialize_child(OBJECT(dev), "ethlite.reserved", &s->rsvd, + TYPE_UNIMPLEMENTED_DEVICE); + qdev_prop_set_string(DEVICE(&s->rsvd), "name", "ethlite.reserved"); + qdev_prop_set_uint64(DEVICE(&s->rsvd), "size", + memory_region_size(&s->container)); + sysbus_realize(SYS_BUS_DEVICE(&s->rsvd), &error_fatal); + memory_region_add_subregion_overlap(&s->container, 0, + sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->rsvd), 0), + -1); + + object_initialize_child(OBJECT(dev), "ethlite.mdio", &s->mdio, + TYPE_UNIMPLEMENTED_DEVICE); + qdev_prop_set_string(DEVICE(&s->mdio), "name", "ethlite.mdio"); + qdev_prop_set_uint64(DEVICE(&s->mdio), "size", 4 * 4); + sysbus_realize(SYS_BUS_DEVICE(&s->mdio), &error_fatal); + memory_region_add_subregion(&s->container, A_MDIO_BASE, + sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->mdio), 0)); + + for (unsigned i = 0; i < 2; i++) { + memory_region_init_ram(&s->port[i].txbuf, OBJECT(dev), + i ? "ethlite.tx[1]buf" : "ethlite.tx[0]buf", + BUFSZ_MAX, &error_abort); + memory_region_add_subregion(&s->container, 0x0800 * i, &s->port[i].txbuf); + memory_region_init_io(&s->port[i].txio, OBJECT(dev), + ð_porttx_ops, s, + i ? "ethlite.tx[1]io" : "ethlite.tx[0]io", + 4 * TX_MAX); + memory_region_add_subregion(&s->container, i ? A_TX_BASE1 : A_TX_BASE0, + &s->port[i].txio); + + memory_region_init_ram(&s->port[i].rxbuf, OBJECT(dev), + i ? "ethlite.rx[1]buf" : "ethlite.rx[0]buf", + BUFSZ_MAX, &error_abort); + memory_region_add_subregion(&s->container, 0x1000 + 0x0800 * i, + &s->port[i].rxbuf); + memory_region_init_io(&s->port[i].rxio, OBJECT(dev), + ð_portrx_ops, s, + i ? "ethlite.rx[1]io" : "ethlite.rx[0]io", + 4 * RX_MAX); + memory_region_add_subregion(&s->container, i ? A_RX_BASE1 : A_RX_BASE0, + &s->port[i].rxio); + } + qemu_macaddr_default_if_unset(&s->conf.macaddr); s->nic = qemu_new_nic(&net_xilinx_ethlite_info, &s->conf, object_get_typename(OBJECT(dev)), dev->id, @@ -240,10 +359,7 @@ static void xilinx_ethlite_init(Object *obj) XlnxXpsEthLite *s = XILINX_ETHLITE(obj); sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq); - - memory_region_init_io(&s->mmio, obj, ð_ops, s, - "xlnx.xps-ethernetlite", R_MAX * 4); - sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->container); } static const Property xilinx_ethlite_properties[] = { |