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) | (s->conf.macaddr.a[3] << 24); 180 case 0x18: /* IA1 */ 181 return s->conf.macaddr.a[4] | (s->conf.macaddr.a[5] << 8); 182 case 0x1c: /* THR */ 183 return s->thr; 184 case 0x20: /* MCTL */ 185 return s->mctl; 186 case 0x24: /* MDV */ 187 return s->mdv; 188 case 0x28: /* MADD */ 189 return 0; 190 case 0x2c: /* MTXD */ 191 return s->mtxd; 192 case 0x30: /* MRXD */ 193 return s->mrxd; 194 case 0x34: /* NP */ 195 return s->np; 196 case 0x38: /* TR */ 197 return 0; 198 case 0x3c: /* Undocuented: Timestamp? */ 199 return 0; 200 default: 201 hw_error("stellaris_enet_read: Bad offset %x\n", (int)offset); 202 return 0; 203 } 204 } 205 206 static void stellaris_enet_write(void *opaque, hwaddr offset, 207 uint64_t value, unsigned size) 208 { 209 stellaris_enet_state *s = (stellaris_enet_state *)opaque; 210 211 switch (offset) { 212 case 0x00: /* IACK */ 213 s->ris &= ~value; 214 DPRINTF("IRQ ack %02x/%02x\n", value, s->ris); 215 stellaris_enet_update(s); 216 /* Clearing TXER also resets the TX fifo. */ 217 if (value & SE_INT_TXER) 218 s->tx_frame_len = -1; 219 break; 220 case 0x04: /* IM */ 221 DPRINTF("IRQ mask %02x/%02x\n", value, s->ris); 222 s->im = value; 223 stellaris_enet_update(s); 224 break; 225 case 0x08: /* RCTL */ 226 s->rctl = value; 227 if (value & SE_RCTL_RSTFIFO) { 228 s->rx_fifo_len = 0; 229 s->np = 0; 230 stellaris_enet_update(s); 231 } 232 break; 233 case 0x0c: /* TCTL */ 234 s->tctl = value; 235 break; 236 case 0x10: /* DATA */ 237 if (s->tx_frame_len == -1) { 238 s->tx_frame_len = value & 0xffff; 239 if (s->tx_frame_len > 2032) { 240 DPRINTF("TX frame too long (%d)\n", s->tx_frame_len); 241 s->tx_frame_len = 0; 242 s->ris |= SE_INT_TXER; 243 stellaris_enet_update(s); 244 } else { 245 DPRINTF("Start TX frame len=%d\n", s->tx_frame_len); 246 /* The value written does not include the ethernet header. */ 247 s->tx_frame_len += 14; 248 if ((s->tctl & SE_TCTL_CRC) == 0) 249 s->tx_frame_len += 4; 250 s->tx_fifo_len = 0; 251 s->tx_fifo[s->tx_fifo_len++] = value >> 16; 252 s->tx_fifo[s->tx_fifo_len++] = value >> 24; 253 } 254 } else { 255 s->tx_fifo[s->tx_fifo_len++] = value; 256 s->tx_fifo[s->tx_fifo_len++] = value >> 8; 257 s->tx_fifo[s->tx_fifo_len++] = value >> 16; 258 s->tx_fifo[s->tx_fifo_len++] = value >> 24; 259 if (s->tx_fifo_len >= s->tx_frame_len) { 260 /* We don't implement explicit CRC, so just chop it off. */ 261 if ((s->tctl & SE_TCTL_CRC) == 0) 262 s->tx_frame_len -= 4; 263 if ((s->tctl & SE_TCTL_PADEN) && s->tx_frame_len < 60) { 264 memset(&s->tx_fifo[s->tx_frame_len], 0, 60 - s->tx_frame_len); 265 s->tx_fifo_len = 60; 266 } 267 qemu_send_packet(qemu_get_queue(s->nic), s->tx_fifo, 268 s->tx_frame_len); 269 s->tx_frame_len = -1; 270 s->ris |= SE_INT_TXEMP; 271 stellaris_enet_update(s); 272 DPRINTF("Done TX\n"); 273 } 274 } 275 break; 276 case 0x14: /* IA0 */ 277 s->conf.macaddr.a[0] = value; 278 s->conf.macaddr.a[1] = value >> 8; 279 s->conf.macaddr.a[2] = value >> 16; 280 s->conf.macaddr.a[3] = value >> 24; 281 break; 282 case 0x18: /* IA1 */ 283 s->conf.macaddr.a[4] = value; 284 s->conf.macaddr.a[5] = value >> 8; 285 break; 286 case 0x1c: /* THR */ 287 s->thr = value; 288 break; 289 case 0x20: /* MCTL */ 290 s->mctl = value; 291 break; 292 case 0x24: /* MDV */ 293 s->mdv = value; 294 break; 295 case 0x28: /* MADD */ 296 /* ignored. */ 297 break; 298 case 0x2c: /* MTXD */ 299 s->mtxd = value & 0xff; 300 break; 301 case 0x30: /* MRXD */ 302 case 0x34: /* NP */ 303 case 0x38: /* TR */ 304 /* Ignored. */ 305 case 0x3c: /* Undocuented: Timestamp? */ 306 /* Ignored. */ 307 break; 308 default: 309 hw_error("stellaris_enet_write: Bad offset %x\n", (int)offset); 310 } 311 } 312 313 static const MemoryRegionOps stellaris_enet_ops = { 314 .read = stellaris_enet_read, 315 .write = stellaris_enet_write, 316 .endianness = DEVICE_NATIVE_ENDIAN, 317 }; 318 319 static void stellaris_enet_reset(stellaris_enet_state *s) 320 { 321 s->mdv = 0x80; 322 s->rctl = SE_RCTL_BADCRC; 323 s->im = SE_INT_PHY | SE_INT_MD | SE_INT_RXER | SE_INT_FOV | SE_INT_TXEMP 324 | SE_INT_TXER | SE_INT_RX; 325 s->thr = 0x3f; 326 s->tx_frame_len = -1; 327 } 328 329 static void stellaris_enet_save(QEMUFile *f, void *opaque) 330 { 331 stellaris_enet_state *s = (stellaris_enet_state *)opaque; 332 int i; 333 334 qemu_put_be32(f, s->ris); 335 qemu_put_be32(f, s->im); 336 qemu_put_be32(f, s->rctl); 337 qemu_put_be32(f, s->tctl); 338 qemu_put_be32(f, s->thr); 339 qemu_put_be32(f, s->mctl); 340 qemu_put_be32(f, s->mdv); 341 qemu_put_be32(f, s->mtxd); 342 qemu_put_be32(f, s->mrxd); 343 qemu_put_be32(f, s->np); 344 qemu_put_be32(f, s->tx_frame_len); 345 qemu_put_be32(f, s->tx_fifo_len); 346 qemu_put_buffer(f, s->tx_fifo, sizeof(s->tx_fifo)); 347 for (i = 0; i < 31; i++) { 348 qemu_put_be32(f, s->rx[i].len); 349 qemu_put_buffer(f, s->rx[i].data, sizeof(s->rx[i].data)); 350 351 } 352 qemu_put_be32(f, s->next_packet); 353 qemu_put_be32(f, s->rx_fifo - s->rx[s->next_packet].data); 354 qemu_put_be32(f, s->rx_fifo_len); 355 } 356 357 static int stellaris_enet_load(QEMUFile *f, void *opaque, int version_id) 358 { 359 stellaris_enet_state *s = (stellaris_enet_state *)opaque; 360 int i; 361 362 if (version_id != 1) 363 return -EINVAL; 364 365 s->ris = qemu_get_be32(f); 366 s->im = qemu_get_be32(f); 367 s->rctl = qemu_get_be32(f); 368 s->tctl = qemu_get_be32(f); 369 s->thr = qemu_get_be32(f); 370 s->mctl = qemu_get_be32(f); 371 s->mdv = qemu_get_be32(f); 372 s->mtxd = qemu_get_be32(f); 373 s->mrxd = qemu_get_be32(f); 374 s->np = qemu_get_be32(f); 375 s->tx_frame_len = qemu_get_be32(f); 376 s->tx_fifo_len = qemu_get_be32(f); 377 qemu_get_buffer(f, s->tx_fifo, sizeof(s->tx_fifo)); 378 for (i = 0; i < 31; i++) { 379 s->rx[i].len = qemu_get_be32(f); 380 qemu_get_buffer(f, s->rx[i].data, sizeof(s->rx[i].data)); 381 382 } 383 s->next_packet = qemu_get_be32(f); 384 s->rx_fifo = s->rx[s->next_packet].data + qemu_get_be32(f); 385 s->rx_fifo_len = qemu_get_be32(f); 386 387 return 0; 388 } 389 390 static void stellaris_enet_cleanup(NetClientState *nc) 391 { 392 stellaris_enet_state *s = qemu_get_nic_opaque(nc); 393 394 s->nic = NULL; 395 } 396 397 static NetClientInfo net_stellaris_enet_info = { 398 .type = NET_CLIENT_OPTIONS_KIND_NIC, 399 .size = sizeof(NICState), 400 .can_receive = stellaris_enet_can_receive, 401 .receive = stellaris_enet_receive, 402 .cleanup = stellaris_enet_cleanup, 403 }; 404 405 static int stellaris_enet_init(SysBusDevice *sbd) 406 { 407 DeviceState *dev = DEVICE(sbd); 408 stellaris_enet_state *s = STELLARIS_ENET(dev); 409 410 memory_region_init_io(&s->mmio, OBJECT(s), &stellaris_enet_ops, s, 411 "stellaris_enet", 0x1000); 412 sysbus_init_mmio(sbd, &s->mmio); 413 sysbus_init_irq(sbd, &s->irq); 414 qemu_macaddr_default_if_unset(&s->conf.macaddr); 415 416 s->nic = qemu_new_nic(&net_stellaris_enet_info, &s->conf, 417 object_get_typename(OBJECT(dev)), dev->id, s); 418 qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); 419 420 stellaris_enet_reset(s); 421 register_savevm(dev, "stellaris_enet", -1, 1, 422 stellaris_enet_save, stellaris_enet_load, s); 423 return 0; 424 } 425 426 static void stellaris_enet_unrealize(DeviceState *dev, Error **errp) 427 { 428 stellaris_enet_state *s = STELLARIS_ENET(dev); 429 430 unregister_savevm(DEVICE(s), "stellaris_enet", s); 431 432 memory_region_destroy(&s->mmio); 433 } 434 435 static Property stellaris_enet_properties[] = { 436 DEFINE_NIC_PROPERTIES(stellaris_enet_state, conf), 437 DEFINE_PROP_END_OF_LIST(), 438 }; 439 440 static void stellaris_enet_class_init(ObjectClass *klass, void *data) 441 { 442 DeviceClass *dc = DEVICE_CLASS(klass); 443 SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); 444 445 k->init = stellaris_enet_init; 446 dc->unrealize = stellaris_enet_unrealize; 447 dc->props = stellaris_enet_properties; 448 } 449 450 static const TypeInfo stellaris_enet_info = { 451 .name = TYPE_STELLARIS_ENET, 452 .parent = TYPE_SYS_BUS_DEVICE, 453 .instance_size = sizeof(stellaris_enet_state), 454 .class_init = stellaris_enet_class_init, 455 }; 456 457 static void stellaris_enet_register_types(void) 458 { 459 type_register_static(&stellaris_enet_info); 460 } 461 462 type_init(stellaris_enet_register_types) 463