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