1d7e35d4aSPaolo Bonzini /* 2d7e35d4aSPaolo Bonzini * Luminary Micro Stellaris Ethernet Controller 3d7e35d4aSPaolo Bonzini * 4d7e35d4aSPaolo Bonzini * Copyright (c) 2007 CodeSourcery. 5d7e35d4aSPaolo Bonzini * Written by Paul Brook 6d7e35d4aSPaolo Bonzini * 7d7e35d4aSPaolo Bonzini * This code is licensed under the GPL. 8d7e35d4aSPaolo Bonzini */ 9d7e35d4aSPaolo Bonzini #include "hw/sysbus.h" 10d7e35d4aSPaolo Bonzini #include "net/net.h" 11d7e35d4aSPaolo Bonzini #include <zlib.h> 12d7e35d4aSPaolo Bonzini 13d7e35d4aSPaolo Bonzini //#define DEBUG_STELLARIS_ENET 1 14d7e35d4aSPaolo Bonzini 15d7e35d4aSPaolo Bonzini #ifdef DEBUG_STELLARIS_ENET 16d7e35d4aSPaolo Bonzini #define DPRINTF(fmt, ...) \ 17d7e35d4aSPaolo Bonzini do { printf("stellaris_enet: " fmt , ## __VA_ARGS__); } while (0) 18d7e35d4aSPaolo Bonzini #define BADF(fmt, ...) \ 19d7e35d4aSPaolo Bonzini do { fprintf(stderr, "stellaris_enet: error: " fmt , ## __VA_ARGS__); exit(1);} while (0) 20d7e35d4aSPaolo Bonzini #else 21d7e35d4aSPaolo Bonzini #define DPRINTF(fmt, ...) do {} while(0) 22d7e35d4aSPaolo Bonzini #define BADF(fmt, ...) \ 23d7e35d4aSPaolo Bonzini do { fprintf(stderr, "stellaris_enet: error: " fmt , ## __VA_ARGS__);} while (0) 24d7e35d4aSPaolo Bonzini #endif 25d7e35d4aSPaolo Bonzini 26d7e35d4aSPaolo Bonzini #define SE_INT_RX 0x01 27d7e35d4aSPaolo Bonzini #define SE_INT_TXER 0x02 28d7e35d4aSPaolo Bonzini #define SE_INT_TXEMP 0x04 29d7e35d4aSPaolo Bonzini #define SE_INT_FOV 0x08 30d7e35d4aSPaolo Bonzini #define SE_INT_RXER 0x10 31d7e35d4aSPaolo Bonzini #define SE_INT_MD 0x20 32d7e35d4aSPaolo Bonzini #define SE_INT_PHY 0x40 33d7e35d4aSPaolo Bonzini 34d7e35d4aSPaolo Bonzini #define SE_RCTL_RXEN 0x01 35d7e35d4aSPaolo Bonzini #define SE_RCTL_AMUL 0x02 36d7e35d4aSPaolo Bonzini #define SE_RCTL_PRMS 0x04 37d7e35d4aSPaolo Bonzini #define SE_RCTL_BADCRC 0x08 38d7e35d4aSPaolo Bonzini #define SE_RCTL_RSTFIFO 0x10 39d7e35d4aSPaolo Bonzini 40d7e35d4aSPaolo Bonzini #define SE_TCTL_TXEN 0x01 41d7e35d4aSPaolo Bonzini #define SE_TCTL_PADEN 0x02 42d7e35d4aSPaolo Bonzini #define SE_TCTL_CRC 0x04 43d7e35d4aSPaolo Bonzini #define SE_TCTL_DUPLEX 0x08 44d7e35d4aSPaolo Bonzini 452fa30abaSAndreas Färber #define TYPE_STELLARIS_ENET "stellaris_enet" 462fa30abaSAndreas Färber #define STELLARIS_ENET(obj) \ 472fa30abaSAndreas Färber OBJECT_CHECK(stellaris_enet_state, (obj), TYPE_STELLARIS_ENET) 482fa30abaSAndreas Färber 49d7e35d4aSPaolo Bonzini typedef struct { 502fa30abaSAndreas Färber SysBusDevice parent_obj; 512fa30abaSAndreas Färber 52d7e35d4aSPaolo Bonzini uint32_t ris; 53d7e35d4aSPaolo Bonzini uint32_t im; 54d7e35d4aSPaolo Bonzini uint32_t rctl; 55d7e35d4aSPaolo Bonzini uint32_t tctl; 56d7e35d4aSPaolo Bonzini uint32_t thr; 57d7e35d4aSPaolo Bonzini uint32_t mctl; 58d7e35d4aSPaolo Bonzini uint32_t mdv; 59d7e35d4aSPaolo Bonzini uint32_t mtxd; 60d7e35d4aSPaolo Bonzini uint32_t mrxd; 61d7e35d4aSPaolo Bonzini uint32_t np; 62d7e35d4aSPaolo Bonzini int tx_fifo_len; 63d7e35d4aSPaolo Bonzini uint8_t tx_fifo[2048]; 64d7e35d4aSPaolo Bonzini /* Real hardware has a 2k fifo, which works out to be at most 31 packets. 65d7e35d4aSPaolo Bonzini We implement a full 31 packet fifo. */ 66d7e35d4aSPaolo Bonzini struct { 67d7e35d4aSPaolo Bonzini uint8_t data[2048]; 68d7e35d4aSPaolo Bonzini int len; 69d7e35d4aSPaolo Bonzini } rx[31]; 70*889ac2a3SPeter Maydell int rx_fifo_offset; 71d7e35d4aSPaolo Bonzini int next_packet; 72d7e35d4aSPaolo Bonzini NICState *nic; 73d7e35d4aSPaolo Bonzini NICConf conf; 74d7e35d4aSPaolo Bonzini qemu_irq irq; 75d7e35d4aSPaolo Bonzini MemoryRegion mmio; 76d7e35d4aSPaolo Bonzini } stellaris_enet_state; 77d7e35d4aSPaolo Bonzini 78d7e35d4aSPaolo Bonzini static void stellaris_enet_update(stellaris_enet_state *s) 79d7e35d4aSPaolo Bonzini { 80d7e35d4aSPaolo Bonzini qemu_set_irq(s->irq, (s->ris & s->im) != 0); 81d7e35d4aSPaolo Bonzini } 82d7e35d4aSPaolo Bonzini 83c6fa443bSPeter Maydell /* Return the data length of the packet currently being assembled 84c6fa443bSPeter Maydell * in the TX fifo. 85c6fa443bSPeter Maydell */ 86c6fa443bSPeter Maydell static inline int stellaris_txpacket_datalen(stellaris_enet_state *s) 87c6fa443bSPeter Maydell { 88c6fa443bSPeter Maydell return s->tx_fifo[0] | (s->tx_fifo[1] << 8); 89c6fa443bSPeter Maydell } 90c6fa443bSPeter Maydell 91c6fa443bSPeter Maydell /* Return true if the packet currently in the TX FIFO is complete, 92c6fa443bSPeter Maydell * ie the FIFO holds enough bytes for the data length, ethernet header, 93c6fa443bSPeter Maydell * payload and optionally CRC. 94c6fa443bSPeter Maydell */ 95c6fa443bSPeter Maydell static inline bool stellaris_txpacket_complete(stellaris_enet_state *s) 96c6fa443bSPeter Maydell { 97c6fa443bSPeter Maydell int framelen = stellaris_txpacket_datalen(s); 98c6fa443bSPeter Maydell framelen += 16; 99c6fa443bSPeter Maydell if (!(s->tctl & SE_TCTL_CRC)) { 100c6fa443bSPeter Maydell framelen += 4; 101c6fa443bSPeter Maydell } 102c6fa443bSPeter Maydell /* Cover the corner case of a 2032 byte payload with auto-CRC disabled: 103c6fa443bSPeter Maydell * this requires more bytes than will fit in the FIFO. It's not totally 104c6fa443bSPeter Maydell * clear how the h/w handles this, but if using threshold-based TX 105c6fa443bSPeter Maydell * it will definitely try to transmit something. 106c6fa443bSPeter Maydell */ 107c6fa443bSPeter Maydell framelen = MIN(framelen, ARRAY_SIZE(s->tx_fifo)); 108c6fa443bSPeter Maydell return s->tx_fifo_len >= framelen; 109c6fa443bSPeter Maydell } 110c6fa443bSPeter Maydell 111a9171c4fSPeter Maydell /* Return true if the TX FIFO threshold is enabled and the FIFO 112a9171c4fSPeter Maydell * has filled enough to reach it. 113a9171c4fSPeter Maydell */ 114a9171c4fSPeter Maydell static inline bool stellaris_tx_thr_reached(stellaris_enet_state *s) 115a9171c4fSPeter Maydell { 116a9171c4fSPeter Maydell return (s->thr < 0x3f && 117a9171c4fSPeter Maydell (s->tx_fifo_len >= 4 * (s->thr * 8 + 1))); 118a9171c4fSPeter Maydell } 119a9171c4fSPeter Maydell 120c6fa443bSPeter Maydell /* Send the packet currently in the TX FIFO */ 121c6fa443bSPeter Maydell static void stellaris_enet_send(stellaris_enet_state *s) 122c6fa443bSPeter Maydell { 123c6fa443bSPeter Maydell int framelen = stellaris_txpacket_datalen(s); 124c6fa443bSPeter Maydell 125c6fa443bSPeter Maydell /* Ethernet header is in the FIFO but not in the datacount. 126c6fa443bSPeter Maydell * We don't implement explicit CRC, so just ignore any 127c6fa443bSPeter Maydell * CRC value in the FIFO. 128c6fa443bSPeter Maydell */ 129c6fa443bSPeter Maydell framelen += 14; 130c6fa443bSPeter Maydell if ((s->tctl & SE_TCTL_PADEN) && framelen < 60) { 131c6fa443bSPeter Maydell memset(&s->tx_fifo[framelen + 2], 0, 60 - framelen); 132c6fa443bSPeter Maydell framelen = 60; 133c6fa443bSPeter Maydell } 134c6fa443bSPeter Maydell /* This MIN will have no effect unless the FIFO data is corrupt 135c6fa443bSPeter Maydell * (eg bad data from an incoming migration); otherwise the check 136c6fa443bSPeter Maydell * on the datalen at the start of writing the data into the FIFO 137c6fa443bSPeter Maydell * will have caught this. Silently write a corrupt half-packet, 138c6fa443bSPeter Maydell * which is what the hardware does in FIFO underrun situations. 139c6fa443bSPeter Maydell */ 140c6fa443bSPeter Maydell framelen = MIN(framelen, ARRAY_SIZE(s->tx_fifo) - 2); 141c6fa443bSPeter Maydell qemu_send_packet(qemu_get_queue(s->nic), s->tx_fifo + 2, framelen); 142c6fa443bSPeter Maydell s->tx_fifo_len = 0; 143c6fa443bSPeter Maydell s->ris |= SE_INT_TXEMP; 144c6fa443bSPeter Maydell stellaris_enet_update(s); 145c6fa443bSPeter Maydell DPRINTF("Done TX\n"); 146c6fa443bSPeter Maydell } 147c6fa443bSPeter Maydell 148d7e35d4aSPaolo Bonzini /* TODO: Implement MAC address filtering. */ 149d7e35d4aSPaolo Bonzini static ssize_t stellaris_enet_receive(NetClientState *nc, const uint8_t *buf, size_t size) 150d7e35d4aSPaolo Bonzini { 151d7e35d4aSPaolo Bonzini stellaris_enet_state *s = qemu_get_nic_opaque(nc); 152d7e35d4aSPaolo Bonzini int n; 153d7e35d4aSPaolo Bonzini uint8_t *p; 154d7e35d4aSPaolo Bonzini uint32_t crc; 155d7e35d4aSPaolo Bonzini 156d7e35d4aSPaolo Bonzini if ((s->rctl & SE_RCTL_RXEN) == 0) 157d7e35d4aSPaolo Bonzini return -1; 158d7e35d4aSPaolo Bonzini if (s->np >= 31) { 159d7e35d4aSPaolo Bonzini DPRINTF("Packet dropped\n"); 160d7e35d4aSPaolo Bonzini return -1; 161d7e35d4aSPaolo Bonzini } 162d7e35d4aSPaolo Bonzini 163eacd606cSPeter Maydell DPRINTF("Received packet len=%zu\n", size); 164d7e35d4aSPaolo Bonzini n = s->next_packet + s->np; 165d7e35d4aSPaolo Bonzini if (n >= 31) 166d7e35d4aSPaolo Bonzini n -= 31; 167d7e35d4aSPaolo Bonzini s->np++; 168d7e35d4aSPaolo Bonzini 169d7e35d4aSPaolo Bonzini s->rx[n].len = size + 6; 170d7e35d4aSPaolo Bonzini p = s->rx[n].data; 171d7e35d4aSPaolo Bonzini *(p++) = (size + 6); 172d7e35d4aSPaolo Bonzini *(p++) = (size + 6) >> 8; 173d7e35d4aSPaolo Bonzini memcpy (p, buf, size); 174d7e35d4aSPaolo Bonzini p += size; 175d7e35d4aSPaolo Bonzini crc = crc32(~0, buf, size); 176d7e35d4aSPaolo Bonzini *(p++) = crc; 177d7e35d4aSPaolo Bonzini *(p++) = crc >> 8; 178d7e35d4aSPaolo Bonzini *(p++) = crc >> 16; 179d7e35d4aSPaolo Bonzini *(p++) = crc >> 24; 180d7e35d4aSPaolo Bonzini /* Clear the remaining bytes in the last word. */ 181d7e35d4aSPaolo Bonzini if ((size & 3) != 2) { 182d7e35d4aSPaolo Bonzini memset(p, 0, (6 - size) & 3); 183d7e35d4aSPaolo Bonzini } 184d7e35d4aSPaolo Bonzini 185d7e35d4aSPaolo Bonzini s->ris |= SE_INT_RX; 186d7e35d4aSPaolo Bonzini stellaris_enet_update(s); 187d7e35d4aSPaolo Bonzini 188d7e35d4aSPaolo Bonzini return size; 189d7e35d4aSPaolo Bonzini } 190d7e35d4aSPaolo Bonzini 191d7e35d4aSPaolo Bonzini static int stellaris_enet_can_receive(NetClientState *nc) 192d7e35d4aSPaolo Bonzini { 193d7e35d4aSPaolo Bonzini stellaris_enet_state *s = qemu_get_nic_opaque(nc); 194d7e35d4aSPaolo Bonzini 195d7e35d4aSPaolo Bonzini if ((s->rctl & SE_RCTL_RXEN) == 0) 196d7e35d4aSPaolo Bonzini return 1; 197d7e35d4aSPaolo Bonzini 198d7e35d4aSPaolo Bonzini return (s->np < 31); 199d7e35d4aSPaolo Bonzini } 200d7e35d4aSPaolo Bonzini 201d7e35d4aSPaolo Bonzini static uint64_t stellaris_enet_read(void *opaque, hwaddr offset, 202d7e35d4aSPaolo Bonzini unsigned size) 203d7e35d4aSPaolo Bonzini { 204d7e35d4aSPaolo Bonzini stellaris_enet_state *s = (stellaris_enet_state *)opaque; 205d7e35d4aSPaolo Bonzini uint32_t val; 206d7e35d4aSPaolo Bonzini 207d7e35d4aSPaolo Bonzini switch (offset) { 208d7e35d4aSPaolo Bonzini case 0x00: /* RIS */ 209d7e35d4aSPaolo Bonzini DPRINTF("IRQ status %02x\n", s->ris); 210d7e35d4aSPaolo Bonzini return s->ris; 211d7e35d4aSPaolo Bonzini case 0x04: /* IM */ 212d7e35d4aSPaolo Bonzini return s->im; 213d7e35d4aSPaolo Bonzini case 0x08: /* RCTL */ 214d7e35d4aSPaolo Bonzini return s->rctl; 215d7e35d4aSPaolo Bonzini case 0x0c: /* TCTL */ 216d7e35d4aSPaolo Bonzini return s->tctl; 217d7e35d4aSPaolo Bonzini case 0x10: /* DATA */ 218*889ac2a3SPeter Maydell { 219*889ac2a3SPeter Maydell uint8_t *rx_fifo; 220*889ac2a3SPeter Maydell 221d7e35d4aSPaolo Bonzini if (s->np == 0) { 222d7e35d4aSPaolo Bonzini BADF("RX underflow\n"); 223d7e35d4aSPaolo Bonzini return 0; 224d7e35d4aSPaolo Bonzini } 225*889ac2a3SPeter Maydell 226*889ac2a3SPeter Maydell rx_fifo = s->rx[s->next_packet].data + s->rx_fifo_offset; 227*889ac2a3SPeter Maydell 228*889ac2a3SPeter Maydell val = rx_fifo[0] | (rx_fifo[1] << 8) | (rx_fifo[2] << 16) 229*889ac2a3SPeter Maydell | (rx_fifo[3] << 24); 230*889ac2a3SPeter Maydell s->rx_fifo_offset += 4; 231*889ac2a3SPeter Maydell if (s->rx_fifo_offset >= s->rx[s->next_packet].len) { 232*889ac2a3SPeter Maydell s->rx_fifo_offset = 0; 233d7e35d4aSPaolo Bonzini s->next_packet++; 234d7e35d4aSPaolo Bonzini if (s->next_packet >= 31) 235d7e35d4aSPaolo Bonzini s->next_packet = 0; 236d7e35d4aSPaolo Bonzini s->np--; 237d7e35d4aSPaolo Bonzini DPRINTF("RX done np=%d\n", s->np); 238d7e35d4aSPaolo Bonzini } 239d7e35d4aSPaolo Bonzini return val; 240*889ac2a3SPeter Maydell } 241d7e35d4aSPaolo Bonzini case 0x14: /* IA0 */ 242d7e35d4aSPaolo Bonzini return s->conf.macaddr.a[0] | (s->conf.macaddr.a[1] << 8) 243106a73b6SPeter Maydell | (s->conf.macaddr.a[2] << 16) 244106a73b6SPeter Maydell | ((uint32_t)s->conf.macaddr.a[3] << 24); 245d7e35d4aSPaolo Bonzini case 0x18: /* IA1 */ 246d7e35d4aSPaolo Bonzini return s->conf.macaddr.a[4] | (s->conf.macaddr.a[5] << 8); 247d7e35d4aSPaolo Bonzini case 0x1c: /* THR */ 248d7e35d4aSPaolo Bonzini return s->thr; 249d7e35d4aSPaolo Bonzini case 0x20: /* MCTL */ 250d7e35d4aSPaolo Bonzini return s->mctl; 251d7e35d4aSPaolo Bonzini case 0x24: /* MDV */ 252d7e35d4aSPaolo Bonzini return s->mdv; 253d7e35d4aSPaolo Bonzini case 0x28: /* MADD */ 254d7e35d4aSPaolo Bonzini return 0; 255d7e35d4aSPaolo Bonzini case 0x2c: /* MTXD */ 256d7e35d4aSPaolo Bonzini return s->mtxd; 257d7e35d4aSPaolo Bonzini case 0x30: /* MRXD */ 258d7e35d4aSPaolo Bonzini return s->mrxd; 259d7e35d4aSPaolo Bonzini case 0x34: /* NP */ 260d7e35d4aSPaolo Bonzini return s->np; 261d7e35d4aSPaolo Bonzini case 0x38: /* TR */ 262d7e35d4aSPaolo Bonzini return 0; 263d7e35d4aSPaolo Bonzini case 0x3c: /* Undocuented: Timestamp? */ 264d7e35d4aSPaolo Bonzini return 0; 265d7e35d4aSPaolo Bonzini default: 266d7e35d4aSPaolo Bonzini hw_error("stellaris_enet_read: Bad offset %x\n", (int)offset); 267d7e35d4aSPaolo Bonzini return 0; 268d7e35d4aSPaolo Bonzini } 269d7e35d4aSPaolo Bonzini } 270d7e35d4aSPaolo Bonzini 271d7e35d4aSPaolo Bonzini static void stellaris_enet_write(void *opaque, hwaddr offset, 272d7e35d4aSPaolo Bonzini uint64_t value, unsigned size) 273d7e35d4aSPaolo Bonzini { 274d7e35d4aSPaolo Bonzini stellaris_enet_state *s = (stellaris_enet_state *)opaque; 275d7e35d4aSPaolo Bonzini 276d7e35d4aSPaolo Bonzini switch (offset) { 277d7e35d4aSPaolo Bonzini case 0x00: /* IACK */ 278d7e35d4aSPaolo Bonzini s->ris &= ~value; 279eacd606cSPeter Maydell DPRINTF("IRQ ack %02" PRIx64 "/%02x\n", value, s->ris); 280d7e35d4aSPaolo Bonzini stellaris_enet_update(s); 281d7e35d4aSPaolo Bonzini /* Clearing TXER also resets the TX fifo. */ 282c6fa443bSPeter Maydell if (value & SE_INT_TXER) { 283c6fa443bSPeter Maydell s->tx_fifo_len = 0; 284c6fa443bSPeter Maydell } 285d7e35d4aSPaolo Bonzini break; 286d7e35d4aSPaolo Bonzini case 0x04: /* IM */ 287eacd606cSPeter Maydell DPRINTF("IRQ mask %02" PRIx64 "/%02x\n", value, s->ris); 288d7e35d4aSPaolo Bonzini s->im = value; 289d7e35d4aSPaolo Bonzini stellaris_enet_update(s); 290d7e35d4aSPaolo Bonzini break; 291d7e35d4aSPaolo Bonzini case 0x08: /* RCTL */ 292d7e35d4aSPaolo Bonzini s->rctl = value; 293d7e35d4aSPaolo Bonzini if (value & SE_RCTL_RSTFIFO) { 294d7e35d4aSPaolo Bonzini s->np = 0; 295*889ac2a3SPeter Maydell s->rx_fifo_offset = 0; 296d7e35d4aSPaolo Bonzini stellaris_enet_update(s); 297d7e35d4aSPaolo Bonzini } 298d7e35d4aSPaolo Bonzini break; 299d7e35d4aSPaolo Bonzini case 0x0c: /* TCTL */ 300d7e35d4aSPaolo Bonzini s->tctl = value; 301d7e35d4aSPaolo Bonzini break; 302d7e35d4aSPaolo Bonzini case 0x10: /* DATA */ 303c6fa443bSPeter Maydell if (s->tx_fifo_len == 0) { 304c6fa443bSPeter Maydell /* The first word is special, it contains the data length */ 305c6fa443bSPeter Maydell int framelen = value & 0xffff; 306c6fa443bSPeter Maydell if (framelen > 2032) { 307c6fa443bSPeter Maydell DPRINTF("TX frame too long (%d)\n", framelen); 308d7e35d4aSPaolo Bonzini s->ris |= SE_INT_TXER; 309d7e35d4aSPaolo Bonzini stellaris_enet_update(s); 310c6fa443bSPeter Maydell break; 311d7e35d4aSPaolo Bonzini } 312c6fa443bSPeter Maydell } 313c6fa443bSPeter Maydell 3145c10495aSPeter Maydell if (s->tx_fifo_len + 4 <= ARRAY_SIZE(s->tx_fifo)) { 315d7e35d4aSPaolo Bonzini s->tx_fifo[s->tx_fifo_len++] = value; 316d7e35d4aSPaolo Bonzini s->tx_fifo[s->tx_fifo_len++] = value >> 8; 317d7e35d4aSPaolo Bonzini s->tx_fifo[s->tx_fifo_len++] = value >> 16; 318d7e35d4aSPaolo Bonzini s->tx_fifo[s->tx_fifo_len++] = value >> 24; 3195c10495aSPeter Maydell } 320c6fa443bSPeter Maydell 321a9171c4fSPeter Maydell if (stellaris_tx_thr_reached(s) && stellaris_txpacket_complete(s)) { 322c6fa443bSPeter Maydell stellaris_enet_send(s); 323d7e35d4aSPaolo Bonzini } 324d7e35d4aSPaolo Bonzini break; 325d7e35d4aSPaolo Bonzini case 0x14: /* IA0 */ 326d7e35d4aSPaolo Bonzini s->conf.macaddr.a[0] = value; 327d7e35d4aSPaolo Bonzini s->conf.macaddr.a[1] = value >> 8; 328d7e35d4aSPaolo Bonzini s->conf.macaddr.a[2] = value >> 16; 329d7e35d4aSPaolo Bonzini s->conf.macaddr.a[3] = value >> 24; 330d7e35d4aSPaolo Bonzini break; 331d7e35d4aSPaolo Bonzini case 0x18: /* IA1 */ 332d7e35d4aSPaolo Bonzini s->conf.macaddr.a[4] = value; 333d7e35d4aSPaolo Bonzini s->conf.macaddr.a[5] = value >> 8; 334d7e35d4aSPaolo Bonzini break; 335d7e35d4aSPaolo Bonzini case 0x1c: /* THR */ 336d7e35d4aSPaolo Bonzini s->thr = value; 337d7e35d4aSPaolo Bonzini break; 338d7e35d4aSPaolo Bonzini case 0x20: /* MCTL */ 339d7e35d4aSPaolo Bonzini s->mctl = value; 340d7e35d4aSPaolo Bonzini break; 341d7e35d4aSPaolo Bonzini case 0x24: /* MDV */ 342d7e35d4aSPaolo Bonzini s->mdv = value; 343d7e35d4aSPaolo Bonzini break; 344d7e35d4aSPaolo Bonzini case 0x28: /* MADD */ 345d7e35d4aSPaolo Bonzini /* ignored. */ 346d7e35d4aSPaolo Bonzini break; 347d7e35d4aSPaolo Bonzini case 0x2c: /* MTXD */ 348d7e35d4aSPaolo Bonzini s->mtxd = value & 0xff; 349d7e35d4aSPaolo Bonzini break; 350a9171c4fSPeter Maydell case 0x38: /* TR */ 351a9171c4fSPeter Maydell if (value & 1) { 352a9171c4fSPeter Maydell stellaris_enet_send(s); 353a9171c4fSPeter Maydell } 354a9171c4fSPeter Maydell break; 355d7e35d4aSPaolo Bonzini case 0x30: /* MRXD */ 356d7e35d4aSPaolo Bonzini case 0x34: /* NP */ 357d7e35d4aSPaolo Bonzini /* Ignored. */ 358d7e35d4aSPaolo Bonzini case 0x3c: /* Undocuented: Timestamp? */ 359d7e35d4aSPaolo Bonzini /* Ignored. */ 360d7e35d4aSPaolo Bonzini break; 361d7e35d4aSPaolo Bonzini default: 362d7e35d4aSPaolo Bonzini hw_error("stellaris_enet_write: Bad offset %x\n", (int)offset); 363d7e35d4aSPaolo Bonzini } 364d7e35d4aSPaolo Bonzini } 365d7e35d4aSPaolo Bonzini 366d7e35d4aSPaolo Bonzini static const MemoryRegionOps stellaris_enet_ops = { 367d7e35d4aSPaolo Bonzini .read = stellaris_enet_read, 368d7e35d4aSPaolo Bonzini .write = stellaris_enet_write, 369d7e35d4aSPaolo Bonzini .endianness = DEVICE_NATIVE_ENDIAN, 370d7e35d4aSPaolo Bonzini }; 371d7e35d4aSPaolo Bonzini 372d7e35d4aSPaolo Bonzini static void stellaris_enet_reset(stellaris_enet_state *s) 373d7e35d4aSPaolo Bonzini { 374d7e35d4aSPaolo Bonzini s->mdv = 0x80; 375d7e35d4aSPaolo Bonzini s->rctl = SE_RCTL_BADCRC; 376d7e35d4aSPaolo Bonzini s->im = SE_INT_PHY | SE_INT_MD | SE_INT_RXER | SE_INT_FOV | SE_INT_TXEMP 377d7e35d4aSPaolo Bonzini | SE_INT_TXER | SE_INT_RX; 378d7e35d4aSPaolo Bonzini s->thr = 0x3f; 379c6fa443bSPeter Maydell s->tx_fifo_len = 0; 380d7e35d4aSPaolo Bonzini } 381d7e35d4aSPaolo Bonzini 382d7e35d4aSPaolo Bonzini static void stellaris_enet_save(QEMUFile *f, void *opaque) 383d7e35d4aSPaolo Bonzini { 384d7e35d4aSPaolo Bonzini stellaris_enet_state *s = (stellaris_enet_state *)opaque; 385d7e35d4aSPaolo Bonzini int i; 386d7e35d4aSPaolo Bonzini 387d7e35d4aSPaolo Bonzini qemu_put_be32(f, s->ris); 388d7e35d4aSPaolo Bonzini qemu_put_be32(f, s->im); 389d7e35d4aSPaolo Bonzini qemu_put_be32(f, s->rctl); 390d7e35d4aSPaolo Bonzini qemu_put_be32(f, s->tctl); 391d7e35d4aSPaolo Bonzini qemu_put_be32(f, s->thr); 392d7e35d4aSPaolo Bonzini qemu_put_be32(f, s->mctl); 393d7e35d4aSPaolo Bonzini qemu_put_be32(f, s->mdv); 394d7e35d4aSPaolo Bonzini qemu_put_be32(f, s->mtxd); 395d7e35d4aSPaolo Bonzini qemu_put_be32(f, s->mrxd); 396d7e35d4aSPaolo Bonzini qemu_put_be32(f, s->np); 397d7e35d4aSPaolo Bonzini qemu_put_be32(f, s->tx_fifo_len); 398d7e35d4aSPaolo Bonzini qemu_put_buffer(f, s->tx_fifo, sizeof(s->tx_fifo)); 399d7e35d4aSPaolo Bonzini for (i = 0; i < 31; i++) { 400d7e35d4aSPaolo Bonzini qemu_put_be32(f, s->rx[i].len); 401d7e35d4aSPaolo Bonzini qemu_put_buffer(f, s->rx[i].data, sizeof(s->rx[i].data)); 402d7e35d4aSPaolo Bonzini 403d7e35d4aSPaolo Bonzini } 404d7e35d4aSPaolo Bonzini qemu_put_be32(f, s->next_packet); 405*889ac2a3SPeter Maydell qemu_put_be32(f, s->rx_fifo_offset); 406d7e35d4aSPaolo Bonzini } 407d7e35d4aSPaolo Bonzini 408d7e35d4aSPaolo Bonzini static int stellaris_enet_load(QEMUFile *f, void *opaque, int version_id) 409d7e35d4aSPaolo Bonzini { 410d7e35d4aSPaolo Bonzini stellaris_enet_state *s = (stellaris_enet_state *)opaque; 411d7e35d4aSPaolo Bonzini int i; 412d7e35d4aSPaolo Bonzini 413d7e35d4aSPaolo Bonzini if (version_id != 1) 414d7e35d4aSPaolo Bonzini return -EINVAL; 415d7e35d4aSPaolo Bonzini 416d7e35d4aSPaolo Bonzini s->ris = qemu_get_be32(f); 417d7e35d4aSPaolo Bonzini s->im = qemu_get_be32(f); 418d7e35d4aSPaolo Bonzini s->rctl = qemu_get_be32(f); 419d7e35d4aSPaolo Bonzini s->tctl = qemu_get_be32(f); 420d7e35d4aSPaolo Bonzini s->thr = qemu_get_be32(f); 421d7e35d4aSPaolo Bonzini s->mctl = qemu_get_be32(f); 422d7e35d4aSPaolo Bonzini s->mdv = qemu_get_be32(f); 423d7e35d4aSPaolo Bonzini s->mtxd = qemu_get_be32(f); 424d7e35d4aSPaolo Bonzini s->mrxd = qemu_get_be32(f); 425d7e35d4aSPaolo Bonzini s->np = qemu_get_be32(f); 426d7e35d4aSPaolo Bonzini s->tx_fifo_len = qemu_get_be32(f); 427d7e35d4aSPaolo Bonzini qemu_get_buffer(f, s->tx_fifo, sizeof(s->tx_fifo)); 428d7e35d4aSPaolo Bonzini for (i = 0; i < 31; i++) { 429d7e35d4aSPaolo Bonzini s->rx[i].len = qemu_get_be32(f); 430d7e35d4aSPaolo Bonzini qemu_get_buffer(f, s->rx[i].data, sizeof(s->rx[i].data)); 431d7e35d4aSPaolo Bonzini 432d7e35d4aSPaolo Bonzini } 433d7e35d4aSPaolo Bonzini s->next_packet = qemu_get_be32(f); 434*889ac2a3SPeter Maydell s->rx_fifo_offset = qemu_get_be32(f); 435d7e35d4aSPaolo Bonzini 436d7e35d4aSPaolo Bonzini return 0; 437d7e35d4aSPaolo Bonzini } 438d7e35d4aSPaolo Bonzini 439d7e35d4aSPaolo Bonzini static void stellaris_enet_cleanup(NetClientState *nc) 440d7e35d4aSPaolo Bonzini { 441d7e35d4aSPaolo Bonzini stellaris_enet_state *s = qemu_get_nic_opaque(nc); 442d7e35d4aSPaolo Bonzini 4430618db44SAndreas Färber s->nic = NULL; 444d7e35d4aSPaolo Bonzini } 445d7e35d4aSPaolo Bonzini 446d7e35d4aSPaolo Bonzini static NetClientInfo net_stellaris_enet_info = { 447d7e35d4aSPaolo Bonzini .type = NET_CLIENT_OPTIONS_KIND_NIC, 448d7e35d4aSPaolo Bonzini .size = sizeof(NICState), 449d7e35d4aSPaolo Bonzini .can_receive = stellaris_enet_can_receive, 450d7e35d4aSPaolo Bonzini .receive = stellaris_enet_receive, 451d7e35d4aSPaolo Bonzini .cleanup = stellaris_enet_cleanup, 452d7e35d4aSPaolo Bonzini }; 453d7e35d4aSPaolo Bonzini 4542fa30abaSAndreas Färber static int stellaris_enet_init(SysBusDevice *sbd) 455d7e35d4aSPaolo Bonzini { 4562fa30abaSAndreas Färber DeviceState *dev = DEVICE(sbd); 4572fa30abaSAndreas Färber stellaris_enet_state *s = STELLARIS_ENET(dev); 458d7e35d4aSPaolo Bonzini 459eedfac6fSPaolo Bonzini memory_region_init_io(&s->mmio, OBJECT(s), &stellaris_enet_ops, s, 460eedfac6fSPaolo Bonzini "stellaris_enet", 0x1000); 4612fa30abaSAndreas Färber sysbus_init_mmio(sbd, &s->mmio); 4622fa30abaSAndreas Färber sysbus_init_irq(sbd, &s->irq); 463d7e35d4aSPaolo Bonzini qemu_macaddr_default_if_unset(&s->conf.macaddr); 464d7e35d4aSPaolo Bonzini 465d7e35d4aSPaolo Bonzini s->nic = qemu_new_nic(&net_stellaris_enet_info, &s->conf, 4662fa30abaSAndreas Färber object_get_typename(OBJECT(dev)), dev->id, s); 467d7e35d4aSPaolo Bonzini qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); 468d7e35d4aSPaolo Bonzini 469d7e35d4aSPaolo Bonzini stellaris_enet_reset(s); 4702fa30abaSAndreas Färber register_savevm(dev, "stellaris_enet", -1, 1, 471d7e35d4aSPaolo Bonzini stellaris_enet_save, stellaris_enet_load, s); 472d7e35d4aSPaolo Bonzini return 0; 473d7e35d4aSPaolo Bonzini } 474d7e35d4aSPaolo Bonzini 4750618db44SAndreas Färber static void stellaris_enet_unrealize(DeviceState *dev, Error **errp) 4760618db44SAndreas Färber { 4770618db44SAndreas Färber stellaris_enet_state *s = STELLARIS_ENET(dev); 4780618db44SAndreas Färber 4790618db44SAndreas Färber unregister_savevm(DEVICE(s), "stellaris_enet", s); 4800618db44SAndreas Färber 4810618db44SAndreas Färber memory_region_destroy(&s->mmio); 4820618db44SAndreas Färber } 4830618db44SAndreas Färber 484d7e35d4aSPaolo Bonzini static Property stellaris_enet_properties[] = { 485d7e35d4aSPaolo Bonzini DEFINE_NIC_PROPERTIES(stellaris_enet_state, conf), 486d7e35d4aSPaolo Bonzini DEFINE_PROP_END_OF_LIST(), 487d7e35d4aSPaolo Bonzini }; 488d7e35d4aSPaolo Bonzini 489d7e35d4aSPaolo Bonzini static void stellaris_enet_class_init(ObjectClass *klass, void *data) 490d7e35d4aSPaolo Bonzini { 491d7e35d4aSPaolo Bonzini DeviceClass *dc = DEVICE_CLASS(klass); 492d7e35d4aSPaolo Bonzini SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); 493d7e35d4aSPaolo Bonzini 494d7e35d4aSPaolo Bonzini k->init = stellaris_enet_init; 4950618db44SAndreas Färber dc->unrealize = stellaris_enet_unrealize; 496d7e35d4aSPaolo Bonzini dc->props = stellaris_enet_properties; 497d7e35d4aSPaolo Bonzini } 498d7e35d4aSPaolo Bonzini 499d7e35d4aSPaolo Bonzini static const TypeInfo stellaris_enet_info = { 5002fa30abaSAndreas Färber .name = TYPE_STELLARIS_ENET, 501d7e35d4aSPaolo Bonzini .parent = TYPE_SYS_BUS_DEVICE, 502d7e35d4aSPaolo Bonzini .instance_size = sizeof(stellaris_enet_state), 503d7e35d4aSPaolo Bonzini .class_init = stellaris_enet_class_init, 504d7e35d4aSPaolo Bonzini }; 505d7e35d4aSPaolo Bonzini 506d7e35d4aSPaolo Bonzini static void stellaris_enet_register_types(void) 507d7e35d4aSPaolo Bonzini { 508d7e35d4aSPaolo Bonzini type_register_static(&stellaris_enet_info); 509d7e35d4aSPaolo Bonzini } 510d7e35d4aSPaolo Bonzini 511d7e35d4aSPaolo Bonzini type_init(stellaris_enet_register_types) 512