pbrook | eea589c | 2007-11-24 03:13:04 +0000 | [diff] [blame] | 1 | /* |
| 2 | * Luminary Micro Stellaris Ethernet Controller |
| 3 | * |
| 4 | * Copyright (c) 2007 CodeSourcery. |
| 5 | * Written by Paul Brook |
| 6 | * |
Matthew Fernandez | 8e31bf3 | 2011-06-26 12:21:35 +1000 | [diff] [blame] | 7 | * This code is licensed under the GPL. |
pbrook | eea589c | 2007-11-24 03:13:04 +0000 | [diff] [blame] | 8 | */ |
Paul Brook | a558046 | 2009-05-14 22:35:07 +0100 | [diff] [blame] | 9 | #include "sysbus.h" |
pbrook | eea589c | 2007-11-24 03:13:04 +0000 | [diff] [blame] | 10 | #include "net.h" |
| 11 | #include <zlib.h> |
| 12 | |
| 13 | //#define DEBUG_STELLARIS_ENET 1 |
| 14 | |
| 15 | #ifdef DEBUG_STELLARIS_ENET |
Blue Swirl | 001faf3 | 2009-05-13 17:53:17 +0000 | [diff] [blame] | 16 | #define DPRINTF(fmt, ...) \ |
| 17 | do { printf("stellaris_enet: " fmt , ## __VA_ARGS__); } while (0) |
| 18 | #define BADF(fmt, ...) \ |
| 19 | do { fprintf(stderr, "stellaris_enet: error: " fmt , ## __VA_ARGS__); exit(1);} while (0) |
pbrook | eea589c | 2007-11-24 03:13:04 +0000 | [diff] [blame] | 20 | #else |
Blue Swirl | 001faf3 | 2009-05-13 17:53:17 +0000 | [diff] [blame] | 21 | #define DPRINTF(fmt, ...) do {} while(0) |
| 22 | #define BADF(fmt, ...) \ |
| 23 | do { fprintf(stderr, "stellaris_enet: error: " fmt , ## __VA_ARGS__);} while (0) |
pbrook | eea589c | 2007-11-24 03:13:04 +0000 | [diff] [blame] | 24 | #endif |
| 25 | |
| 26 | #define SE_INT_RX 0x01 |
| 27 | #define SE_INT_TXER 0x02 |
| 28 | #define SE_INT_TXEMP 0x04 |
| 29 | #define SE_INT_FOV 0x08 |
| 30 | #define SE_INT_RXER 0x10 |
| 31 | #define SE_INT_MD 0x20 |
| 32 | #define SE_INT_PHY 0x40 |
| 33 | |
| 34 | #define SE_RCTL_RXEN 0x01 |
| 35 | #define SE_RCTL_AMUL 0x02 |
| 36 | #define SE_RCTL_PRMS 0x04 |
| 37 | #define SE_RCTL_BADCRC 0x08 |
| 38 | #define SE_RCTL_RSTFIFO 0x10 |
| 39 | |
| 40 | #define SE_TCTL_TXEN 0x01 |
| 41 | #define SE_TCTL_PADEN 0x02 |
| 42 | #define SE_TCTL_CRC 0x04 |
| 43 | #define SE_TCTL_DUPLEX 0x08 |
| 44 | |
| 45 | typedef struct { |
Paul Brook | a558046 | 2009-05-14 22:35:07 +0100 | [diff] [blame] | 46 | SysBusDevice busdev; |
pbrook | eea589c | 2007-11-24 03:13:04 +0000 | [diff] [blame] | 47 | uint32_t ris; |
| 48 | uint32_t im; |
| 49 | uint32_t rctl; |
| 50 | uint32_t tctl; |
| 51 | uint32_t thr; |
| 52 | uint32_t mctl; |
| 53 | uint32_t mdv; |
| 54 | uint32_t mtxd; |
| 55 | uint32_t mrxd; |
| 56 | uint32_t np; |
| 57 | int tx_frame_len; |
| 58 | int tx_fifo_len; |
| 59 | uint8_t tx_fifo[2048]; |
| 60 | /* Real hardware has a 2k fifo, which works out to be at most 31 packets. |
| 61 | We implement a full 31 packet fifo. */ |
| 62 | struct { |
| 63 | uint8_t data[2048]; |
| 64 | int len; |
| 65 | } rx[31]; |
| 66 | uint8_t *rx_fifo; |
| 67 | int rx_fifo_len; |
| 68 | int next_packet; |
Mark McLoughlin | 8c9b63b | 2009-11-25 18:49:24 +0000 | [diff] [blame] | 69 | NICState *nic; |
Gerd Hoffmann | 540f006 | 2009-10-21 15:25:39 +0200 | [diff] [blame] | 70 | NICConf conf; |
pbrook | eea589c | 2007-11-24 03:13:04 +0000 | [diff] [blame] | 71 | qemu_irq irq; |
Avi Kivity | f070e1e | 2011-07-18 11:58:50 +0300 | [diff] [blame] | 72 | MemoryRegion mmio; |
pbrook | eea589c | 2007-11-24 03:13:04 +0000 | [diff] [blame] | 73 | } stellaris_enet_state; |
| 74 | |
| 75 | static void stellaris_enet_update(stellaris_enet_state *s) |
| 76 | { |
| 77 | qemu_set_irq(s->irq, (s->ris & s->im) != 0); |
| 78 | } |
| 79 | |
| 80 | /* TODO: Implement MAC address filtering. */ |
Mark McLoughlin | 8c9b63b | 2009-11-25 18:49:24 +0000 | [diff] [blame] | 81 | static ssize_t stellaris_enet_receive(VLANClientState *nc, const uint8_t *buf, size_t size) |
pbrook | eea589c | 2007-11-24 03:13:04 +0000 | [diff] [blame] | 82 | { |
Mark McLoughlin | 8c9b63b | 2009-11-25 18:49:24 +0000 | [diff] [blame] | 83 | stellaris_enet_state *s = DO_UPCAST(NICState, nc, nc)->opaque; |
pbrook | eea589c | 2007-11-24 03:13:04 +0000 | [diff] [blame] | 84 | int n; |
| 85 | uint8_t *p; |
| 86 | uint32_t crc; |
| 87 | |
| 88 | if ((s->rctl & SE_RCTL_RXEN) == 0) |
Mark McLoughlin | 4f1c942 | 2009-05-18 13:40:55 +0100 | [diff] [blame] | 89 | return -1; |
pbrook | eea589c | 2007-11-24 03:13:04 +0000 | [diff] [blame] | 90 | if (s->np >= 31) { |
| 91 | DPRINTF("Packet dropped\n"); |
Mark McLoughlin | 4f1c942 | 2009-05-18 13:40:55 +0100 | [diff] [blame] | 92 | return -1; |
pbrook | eea589c | 2007-11-24 03:13:04 +0000 | [diff] [blame] | 93 | } |
| 94 | |
| 95 | DPRINTF("Received packet len=%d\n", size); |
| 96 | n = s->next_packet + s->np; |
| 97 | if (n >= 31) |
| 98 | n -= 31; |
| 99 | s->np++; |
| 100 | |
| 101 | s->rx[n].len = size + 6; |
| 102 | p = s->rx[n].data; |
| 103 | *(p++) = (size + 6); |
| 104 | *(p++) = (size + 6) >> 8; |
| 105 | memcpy (p, buf, size); |
| 106 | p += size; |
| 107 | crc = crc32(~0, buf, size); |
| 108 | *(p++) = crc; |
| 109 | *(p++) = crc >> 8; |
| 110 | *(p++) = crc >> 16; |
| 111 | *(p++) = crc >> 24; |
| 112 | /* Clear the remaining bytes in the last word. */ |
| 113 | if ((size & 3) != 2) { |
| 114 | memset(p, 0, (6 - size) & 3); |
| 115 | } |
| 116 | |
| 117 | s->ris |= SE_INT_RX; |
| 118 | stellaris_enet_update(s); |
Mark McLoughlin | 4f1c942 | 2009-05-18 13:40:55 +0100 | [diff] [blame] | 119 | |
| 120 | return size; |
pbrook | eea589c | 2007-11-24 03:13:04 +0000 | [diff] [blame] | 121 | } |
| 122 | |
Mark McLoughlin | 8c9b63b | 2009-11-25 18:49:24 +0000 | [diff] [blame] | 123 | static int stellaris_enet_can_receive(VLANClientState *nc) |
pbrook | eea589c | 2007-11-24 03:13:04 +0000 | [diff] [blame] | 124 | { |
Mark McLoughlin | 8c9b63b | 2009-11-25 18:49:24 +0000 | [diff] [blame] | 125 | stellaris_enet_state *s = DO_UPCAST(NICState, nc, nc)->opaque; |
pbrook | eea589c | 2007-11-24 03:13:04 +0000 | [diff] [blame] | 126 | |
| 127 | if ((s->rctl & SE_RCTL_RXEN) == 0) |
| 128 | return 1; |
| 129 | |
| 130 | return (s->np < 31); |
| 131 | } |
| 132 | |
Avi Kivity | f070e1e | 2011-07-18 11:58:50 +0300 | [diff] [blame] | 133 | static uint64_t stellaris_enet_read(void *opaque, target_phys_addr_t offset, |
| 134 | unsigned size) |
pbrook | eea589c | 2007-11-24 03:13:04 +0000 | [diff] [blame] | 135 | { |
Paul Brook | 57b452a | 2009-06-11 13:22:27 +0100 | [diff] [blame] | 136 | stellaris_enet_state *s = (stellaris_enet_state *)opaque; |
pbrook | eea589c | 2007-11-24 03:13:04 +0000 | [diff] [blame] | 137 | uint32_t val; |
| 138 | |
pbrook | eea589c | 2007-11-24 03:13:04 +0000 | [diff] [blame] | 139 | switch (offset) { |
| 140 | case 0x00: /* RIS */ |
| 141 | DPRINTF("IRQ status %02x\n", s->ris); |
| 142 | return s->ris; |
| 143 | case 0x04: /* IM */ |
| 144 | return s->im; |
| 145 | case 0x08: /* RCTL */ |
| 146 | return s->rctl; |
| 147 | case 0x0c: /* TCTL */ |
| 148 | return s->tctl; |
| 149 | case 0x10: /* DATA */ |
| 150 | if (s->rx_fifo_len == 0) { |
| 151 | if (s->np == 0) { |
| 152 | BADF("RX underflow\n"); |
| 153 | return 0; |
| 154 | } |
| 155 | s->rx_fifo_len = s->rx[s->next_packet].len; |
| 156 | s->rx_fifo = s->rx[s->next_packet].data; |
| 157 | DPRINTF("RX FIFO start packet len=%d\n", s->rx_fifo_len); |
| 158 | } |
| 159 | val = s->rx_fifo[0] | (s->rx_fifo[1] << 8) | (s->rx_fifo[2] << 16) |
| 160 | | (s->rx_fifo[3] << 24); |
| 161 | s->rx_fifo += 4; |
| 162 | s->rx_fifo_len -= 4; |
| 163 | if (s->rx_fifo_len <= 0) { |
| 164 | s->rx_fifo_len = 0; |
| 165 | s->next_packet++; |
| 166 | if (s->next_packet >= 31) |
| 167 | s->next_packet = 0; |
| 168 | s->np--; |
| 169 | DPRINTF("RX done np=%d\n", s->np); |
| 170 | } |
| 171 | return val; |
| 172 | case 0x14: /* IA0 */ |
Gerd Hoffmann | 540f006 | 2009-10-21 15:25:39 +0200 | [diff] [blame] | 173 | return s->conf.macaddr.a[0] | (s->conf.macaddr.a[1] << 8) |
| 174 | | (s->conf.macaddr.a[2] << 16) | (s->conf.macaddr.a[3] << 24); |
pbrook | eea589c | 2007-11-24 03:13:04 +0000 | [diff] [blame] | 175 | case 0x18: /* IA1 */ |
Gerd Hoffmann | 540f006 | 2009-10-21 15:25:39 +0200 | [diff] [blame] | 176 | return s->conf.macaddr.a[4] | (s->conf.macaddr.a[5] << 8); |
pbrook | eea589c | 2007-11-24 03:13:04 +0000 | [diff] [blame] | 177 | case 0x1c: /* THR */ |
| 178 | return s->thr; |
| 179 | case 0x20: /* MCTL */ |
| 180 | return s->mctl; |
| 181 | case 0x24: /* MDV */ |
| 182 | return s->mdv; |
| 183 | case 0x28: /* MADD */ |
| 184 | return 0; |
| 185 | case 0x2c: /* MTXD */ |
| 186 | return s->mtxd; |
| 187 | case 0x30: /* MRXD */ |
| 188 | return s->mrxd; |
| 189 | case 0x34: /* NP */ |
| 190 | return s->np; |
| 191 | case 0x38: /* TR */ |
| 192 | return 0; |
| 193 | case 0x3c: /* Undocuented: Timestamp? */ |
| 194 | return 0; |
| 195 | default: |
Paul Brook | 2ac7117 | 2009-05-08 02:35:15 +0100 | [diff] [blame] | 196 | hw_error("stellaris_enet_read: Bad offset %x\n", (int)offset); |
pbrook | eea589c | 2007-11-24 03:13:04 +0000 | [diff] [blame] | 197 | return 0; |
| 198 | } |
| 199 | } |
| 200 | |
Anthony Liguori | c227f09 | 2009-10-01 16:12:16 -0500 | [diff] [blame] | 201 | static void stellaris_enet_write(void *opaque, target_phys_addr_t offset, |
Avi Kivity | f070e1e | 2011-07-18 11:58:50 +0300 | [diff] [blame] | 202 | uint64_t value, unsigned size) |
pbrook | eea589c | 2007-11-24 03:13:04 +0000 | [diff] [blame] | 203 | { |
| 204 | stellaris_enet_state *s = (stellaris_enet_state *)opaque; |
| 205 | |
pbrook | eea589c | 2007-11-24 03:13:04 +0000 | [diff] [blame] | 206 | switch (offset) { |
| 207 | case 0x00: /* IACK */ |
| 208 | s->ris &= ~value; |
| 209 | DPRINTF("IRQ ack %02x/%02x\n", value, s->ris); |
| 210 | stellaris_enet_update(s); |
| 211 | /* Clearing TXER also resets the TX fifo. */ |
| 212 | if (value & SE_INT_TXER) |
| 213 | s->tx_frame_len = -1; |
| 214 | break; |
| 215 | case 0x04: /* IM */ |
| 216 | DPRINTF("IRQ mask %02x/%02x\n", value, s->ris); |
| 217 | s->im = value; |
| 218 | stellaris_enet_update(s); |
| 219 | break; |
| 220 | case 0x08: /* RCTL */ |
| 221 | s->rctl = value; |
| 222 | if (value & SE_RCTL_RSTFIFO) { |
| 223 | s->rx_fifo_len = 0; |
| 224 | s->np = 0; |
| 225 | stellaris_enet_update(s); |
| 226 | } |
| 227 | break; |
| 228 | case 0x0c: /* TCTL */ |
| 229 | s->tctl = value; |
| 230 | break; |
| 231 | case 0x10: /* DATA */ |
| 232 | if (s->tx_frame_len == -1) { |
| 233 | s->tx_frame_len = value & 0xffff; |
| 234 | if (s->tx_frame_len > 2032) { |
| 235 | DPRINTF("TX frame too long (%d)\n", s->tx_frame_len); |
| 236 | s->tx_frame_len = 0; |
| 237 | s->ris |= SE_INT_TXER; |
| 238 | stellaris_enet_update(s); |
| 239 | } else { |
| 240 | DPRINTF("Start TX frame len=%d\n", s->tx_frame_len); |
| 241 | /* The value written does not include the ethernet header. */ |
| 242 | s->tx_frame_len += 14; |
| 243 | if ((s->tctl & SE_TCTL_CRC) == 0) |
| 244 | s->tx_frame_len += 4; |
| 245 | s->tx_fifo_len = 0; |
| 246 | s->tx_fifo[s->tx_fifo_len++] = value >> 16; |
| 247 | s->tx_fifo[s->tx_fifo_len++] = value >> 24; |
| 248 | } |
| 249 | } else { |
| 250 | s->tx_fifo[s->tx_fifo_len++] = value; |
| 251 | s->tx_fifo[s->tx_fifo_len++] = value >> 8; |
| 252 | s->tx_fifo[s->tx_fifo_len++] = value >> 16; |
| 253 | s->tx_fifo[s->tx_fifo_len++] = value >> 24; |
| 254 | if (s->tx_fifo_len >= s->tx_frame_len) { |
| 255 | /* We don't implement explicit CRC, so just chop it off. */ |
| 256 | if ((s->tctl & SE_TCTL_CRC) == 0) |
| 257 | s->tx_frame_len -= 4; |
| 258 | if ((s->tctl & SE_TCTL_PADEN) && s->tx_frame_len < 60) { |
| 259 | memset(&s->tx_fifo[s->tx_frame_len], 0, 60 - s->tx_frame_len); |
| 260 | s->tx_fifo_len = 60; |
| 261 | } |
Mark McLoughlin | 8c9b63b | 2009-11-25 18:49:24 +0000 | [diff] [blame] | 262 | qemu_send_packet(&s->nic->nc, s->tx_fifo, s->tx_frame_len); |
pbrook | eea589c | 2007-11-24 03:13:04 +0000 | [diff] [blame] | 263 | s->tx_frame_len = -1; |
| 264 | s->ris |= SE_INT_TXEMP; |
| 265 | stellaris_enet_update(s); |
| 266 | DPRINTF("Done TX\n"); |
| 267 | } |
| 268 | } |
| 269 | break; |
| 270 | case 0x14: /* IA0 */ |
Gerd Hoffmann | 540f006 | 2009-10-21 15:25:39 +0200 | [diff] [blame] | 271 | s->conf.macaddr.a[0] = value; |
| 272 | s->conf.macaddr.a[1] = value >> 8; |
| 273 | s->conf.macaddr.a[2] = value >> 16; |
| 274 | s->conf.macaddr.a[3] = value >> 24; |
pbrook | eea589c | 2007-11-24 03:13:04 +0000 | [diff] [blame] | 275 | break; |
| 276 | case 0x18: /* IA1 */ |
Gerd Hoffmann | 540f006 | 2009-10-21 15:25:39 +0200 | [diff] [blame] | 277 | s->conf.macaddr.a[4] = value; |
| 278 | s->conf.macaddr.a[5] = value >> 8; |
pbrook | eea589c | 2007-11-24 03:13:04 +0000 | [diff] [blame] | 279 | break; |
| 280 | case 0x1c: /* THR */ |
| 281 | s->thr = value; |
| 282 | break; |
| 283 | case 0x20: /* MCTL */ |
| 284 | s->mctl = value; |
| 285 | break; |
| 286 | case 0x24: /* MDV */ |
| 287 | s->mdv = value; |
| 288 | break; |
| 289 | case 0x28: /* MADD */ |
| 290 | /* ignored. */ |
| 291 | break; |
| 292 | case 0x2c: /* MTXD */ |
| 293 | s->mtxd = value & 0xff; |
| 294 | break; |
| 295 | case 0x30: /* MRXD */ |
| 296 | case 0x34: /* NP */ |
| 297 | case 0x38: /* TR */ |
| 298 | /* Ignored. */ |
| 299 | case 0x3c: /* Undocuented: Timestamp? */ |
| 300 | /* Ignored. */ |
| 301 | break; |
| 302 | default: |
Paul Brook | 2ac7117 | 2009-05-08 02:35:15 +0100 | [diff] [blame] | 303 | hw_error("stellaris_enet_write: Bad offset %x\n", (int)offset); |
pbrook | eea589c | 2007-11-24 03:13:04 +0000 | [diff] [blame] | 304 | } |
| 305 | } |
| 306 | |
Avi Kivity | f070e1e | 2011-07-18 11:58:50 +0300 | [diff] [blame] | 307 | static const MemoryRegionOps stellaris_enet_ops = { |
| 308 | .read = stellaris_enet_read, |
| 309 | .write = stellaris_enet_write, |
| 310 | .endianness = DEVICE_NATIVE_ENDIAN, |
pbrook | eea589c | 2007-11-24 03:13:04 +0000 | [diff] [blame] | 311 | }; |
| 312 | |
pbrook | eea589c | 2007-11-24 03:13:04 +0000 | [diff] [blame] | 313 | static void stellaris_enet_reset(stellaris_enet_state *s) |
| 314 | { |
| 315 | s->mdv = 0x80; |
| 316 | s->rctl = SE_RCTL_BADCRC; |
| 317 | s->im = SE_INT_PHY | SE_INT_MD | SE_INT_RXER | SE_INT_FOV | SE_INT_TXEMP |
| 318 | | SE_INT_TXER | SE_INT_RX; |
| 319 | s->thr = 0x3f; |
| 320 | s->tx_frame_len = -1; |
| 321 | } |
| 322 | |
pbrook | 23e3929 | 2008-07-02 16:48:32 +0000 | [diff] [blame] | 323 | static void stellaris_enet_save(QEMUFile *f, void *opaque) |
| 324 | { |
| 325 | stellaris_enet_state *s = (stellaris_enet_state *)opaque; |
| 326 | int i; |
| 327 | |
| 328 | qemu_put_be32(f, s->ris); |
| 329 | qemu_put_be32(f, s->im); |
| 330 | qemu_put_be32(f, s->rctl); |
| 331 | qemu_put_be32(f, s->tctl); |
| 332 | qemu_put_be32(f, s->thr); |
| 333 | qemu_put_be32(f, s->mctl); |
| 334 | qemu_put_be32(f, s->mdv); |
| 335 | qemu_put_be32(f, s->mtxd); |
| 336 | qemu_put_be32(f, s->mrxd); |
| 337 | qemu_put_be32(f, s->np); |
| 338 | qemu_put_be32(f, s->tx_frame_len); |
| 339 | qemu_put_be32(f, s->tx_fifo_len); |
| 340 | qemu_put_buffer(f, s->tx_fifo, sizeof(s->tx_fifo)); |
| 341 | for (i = 0; i < 31; i++) { |
| 342 | qemu_put_be32(f, s->rx[i].len); |
| 343 | qemu_put_buffer(f, s->rx[i].data, sizeof(s->rx[i].data)); |
| 344 | |
| 345 | } |
| 346 | qemu_put_be32(f, s->next_packet); |
| 347 | qemu_put_be32(f, s->rx_fifo - s->rx[s->next_packet].data); |
| 348 | qemu_put_be32(f, s->rx_fifo_len); |
| 349 | } |
| 350 | |
| 351 | static int stellaris_enet_load(QEMUFile *f, void *opaque, int version_id) |
| 352 | { |
| 353 | stellaris_enet_state *s = (stellaris_enet_state *)opaque; |
| 354 | int i; |
| 355 | |
| 356 | if (version_id != 1) |
| 357 | return -EINVAL; |
| 358 | |
| 359 | s->ris = qemu_get_be32(f); |
| 360 | s->im = qemu_get_be32(f); |
| 361 | s->rctl = qemu_get_be32(f); |
| 362 | s->tctl = qemu_get_be32(f); |
| 363 | s->thr = qemu_get_be32(f); |
| 364 | s->mctl = qemu_get_be32(f); |
| 365 | s->mdv = qemu_get_be32(f); |
| 366 | s->mtxd = qemu_get_be32(f); |
| 367 | s->mrxd = qemu_get_be32(f); |
| 368 | s->np = qemu_get_be32(f); |
| 369 | s->tx_frame_len = qemu_get_be32(f); |
| 370 | s->tx_fifo_len = qemu_get_be32(f); |
| 371 | qemu_get_buffer(f, s->tx_fifo, sizeof(s->tx_fifo)); |
| 372 | for (i = 0; i < 31; i++) { |
| 373 | s->rx[i].len = qemu_get_be32(f); |
| 374 | qemu_get_buffer(f, s->rx[i].data, sizeof(s->rx[i].data)); |
| 375 | |
| 376 | } |
| 377 | s->next_packet = qemu_get_be32(f); |
| 378 | s->rx_fifo = s->rx[s->next_packet].data + qemu_get_be32(f); |
| 379 | s->rx_fifo_len = qemu_get_be32(f); |
| 380 | |
| 381 | return 0; |
| 382 | } |
| 383 | |
Mark McLoughlin | 8c9b63b | 2009-11-25 18:49:24 +0000 | [diff] [blame] | 384 | static void stellaris_enet_cleanup(VLANClientState *nc) |
aliguori | b946a15 | 2009-04-17 17:11:08 +0000 | [diff] [blame] | 385 | { |
Mark McLoughlin | 8c9b63b | 2009-11-25 18:49:24 +0000 | [diff] [blame] | 386 | stellaris_enet_state *s = DO_UPCAST(NICState, nc, nc)->opaque; |
aliguori | b946a15 | 2009-04-17 17:11:08 +0000 | [diff] [blame] | 387 | |
Alex Williamson | 0be71e3 | 2010-06-25 11:09:07 -0600 | [diff] [blame] | 388 | unregister_savevm(&s->busdev.qdev, "stellaris_enet", s); |
aliguori | b946a15 | 2009-04-17 17:11:08 +0000 | [diff] [blame] | 389 | |
Avi Kivity | f070e1e | 2011-07-18 11:58:50 +0300 | [diff] [blame] | 390 | memory_region_destroy(&s->mmio); |
aliguori | b946a15 | 2009-04-17 17:11:08 +0000 | [diff] [blame] | 391 | |
Anthony Liguori | 7267c09 | 2011-08-20 22:09:37 -0500 | [diff] [blame] | 392 | g_free(s); |
aliguori | b946a15 | 2009-04-17 17:11:08 +0000 | [diff] [blame] | 393 | } |
| 394 | |
Mark McLoughlin | 8c9b63b | 2009-11-25 18:49:24 +0000 | [diff] [blame] | 395 | static NetClientInfo net_stellaris_enet_info = { |
| 396 | .type = NET_CLIENT_TYPE_NIC, |
| 397 | .size = sizeof(NICState), |
| 398 | .can_receive = stellaris_enet_can_receive, |
| 399 | .receive = stellaris_enet_receive, |
| 400 | .cleanup = stellaris_enet_cleanup, |
| 401 | }; |
| 402 | |
Gerd Hoffmann | 81a322d | 2009-08-14 10:36:05 +0200 | [diff] [blame] | 403 | static int stellaris_enet_init(SysBusDevice *dev) |
pbrook | eea589c | 2007-11-24 03:13:04 +0000 | [diff] [blame] | 404 | { |
Paul Brook | a558046 | 2009-05-14 22:35:07 +0100 | [diff] [blame] | 405 | stellaris_enet_state *s = FROM_SYSBUS(stellaris_enet_state, dev); |
pbrook | eea589c | 2007-11-24 03:13:04 +0000 | [diff] [blame] | 406 | |
Avi Kivity | f070e1e | 2011-07-18 11:58:50 +0300 | [diff] [blame] | 407 | memory_region_init_io(&s->mmio, &stellaris_enet_ops, s, "stellaris_enet", |
| 408 | 0x1000); |
Avi Kivity | 750ecd4 | 2011-11-27 11:38:10 +0200 | [diff] [blame] | 409 | sysbus_init_mmio(dev, &s->mmio); |
Paul Brook | a558046 | 2009-05-14 22:35:07 +0100 | [diff] [blame] | 410 | sysbus_init_irq(dev, &s->irq); |
Gerd Hoffmann | 540f006 | 2009-10-21 15:25:39 +0200 | [diff] [blame] | 411 | qemu_macaddr_default_if_unset(&s->conf.macaddr); |
pbrook | eea589c | 2007-11-24 03:13:04 +0000 | [diff] [blame] | 412 | |
Mark McLoughlin | 8c9b63b | 2009-11-25 18:49:24 +0000 | [diff] [blame] | 413 | s->nic = qemu_new_nic(&net_stellaris_enet_info, &s->conf, |
Anthony Liguori | f79f2bf | 2011-12-04 11:17:51 -0600 | [diff] [blame] | 414 | object_get_typename(OBJECT(dev)), dev->qdev.id, s); |
Mark McLoughlin | 8c9b63b | 2009-11-25 18:49:24 +0000 | [diff] [blame] | 415 | qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a); |
pbrook | eea589c | 2007-11-24 03:13:04 +0000 | [diff] [blame] | 416 | |
| 417 | stellaris_enet_reset(s); |
Alex Williamson | 0be71e3 | 2010-06-25 11:09:07 -0600 | [diff] [blame] | 418 | register_savevm(&s->busdev.qdev, "stellaris_enet", -1, 1, |
pbrook | 23e3929 | 2008-07-02 16:48:32 +0000 | [diff] [blame] | 419 | stellaris_enet_save, stellaris_enet_load, s); |
Gerd Hoffmann | 81a322d | 2009-08-14 10:36:05 +0200 | [diff] [blame] | 420 | return 0; |
pbrook | eea589c | 2007-11-24 03:13:04 +0000 | [diff] [blame] | 421 | } |
Paul Brook | a558046 | 2009-05-14 22:35:07 +0100 | [diff] [blame] | 422 | |
Anthony Liguori | 999e12b | 2012-01-24 13:12:29 -0600 | [diff] [blame] | 423 | static Property stellaris_enet_properties[] = { |
| 424 | DEFINE_NIC_PROPERTIES(stellaris_enet_state, conf), |
| 425 | DEFINE_PROP_END_OF_LIST(), |
| 426 | }; |
| 427 | |
| 428 | static void stellaris_enet_class_init(ObjectClass *klass, void *data) |
| 429 | { |
Anthony Liguori | 39bffca | 2011-12-07 21:34:16 -0600 | [diff] [blame] | 430 | DeviceClass *dc = DEVICE_CLASS(klass); |
Anthony Liguori | 999e12b | 2012-01-24 13:12:29 -0600 | [diff] [blame] | 431 | SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); |
| 432 | |
| 433 | k->init = stellaris_enet_init; |
Anthony Liguori | 39bffca | 2011-12-07 21:34:16 -0600 | [diff] [blame] | 434 | dc->props = stellaris_enet_properties; |
Anthony Liguori | 999e12b | 2012-01-24 13:12:29 -0600 | [diff] [blame] | 435 | } |
| 436 | |
Anthony Liguori | 39bffca | 2011-12-07 21:34:16 -0600 | [diff] [blame] | 437 | static TypeInfo stellaris_enet_info = { |
| 438 | .name = "stellaris_enet", |
| 439 | .parent = TYPE_SYS_BUS_DEVICE, |
| 440 | .instance_size = sizeof(stellaris_enet_state), |
| 441 | .class_init = stellaris_enet_class_init, |
Gerd Hoffmann | 540f006 | 2009-10-21 15:25:39 +0200 | [diff] [blame] | 442 | }; |
| 443 | |
Andreas Färber | 83f7d43 | 2012-02-09 15:20:55 +0100 | [diff] [blame] | 444 | static void stellaris_enet_register_types(void) |
Paul Brook | a558046 | 2009-05-14 22:35:07 +0100 | [diff] [blame] | 445 | { |
Anthony Liguori | 39bffca | 2011-12-07 21:34:16 -0600 | [diff] [blame] | 446 | type_register_static(&stellaris_enet_info); |
Paul Brook | a558046 | 2009-05-14 22:35:07 +0100 | [diff] [blame] | 447 | } |
| 448 | |
Andreas Färber | 83f7d43 | 2012-02-09 15:20:55 +0100 | [diff] [blame] | 449 | type_init(stellaris_enet_register_types) |