stellaris_enet.c 15 KB

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