xref: /qemu/hw/net/stellaris_enet.c (revision 64552b6be4758d3a774f7787b294543ccebd5358)
1eea589ccSpbrook /*
2eea589ccSpbrook  * Luminary Micro Stellaris Ethernet Controller
3eea589ccSpbrook  *
4eea589ccSpbrook  * Copyright (c) 2007 CodeSourcery.
5eea589ccSpbrook  * Written by Paul Brook
6eea589ccSpbrook  *
78e31bf38SMatthew Fernandez  * This code is licensed under the GPL.
8eea589ccSpbrook  */
90b8fa32fSMarkus Armbruster 
108ef94f0bSPeter Maydell #include "qemu/osdep.h"
11*64552b6bSMarkus Armbruster #include "hw/irq.h"
1283c9f4caSPaolo Bonzini #include "hw/sysbus.h"
131422e32dSPaolo Bonzini #include "net/net.h"
14f6de9957SPhilippe Mathieu-Daudé #include "qemu/log.h"
150b8fa32fSMarkus Armbruster #include "qemu/module.h"
16eea589ccSpbrook #include <zlib.h>
17eea589ccSpbrook 
18eea589ccSpbrook //#define DEBUG_STELLARIS_ENET 1
19eea589ccSpbrook 
20eea589ccSpbrook #ifdef DEBUG_STELLARIS_ENET
21001faf32SBlue Swirl #define DPRINTF(fmt, ...) \
22001faf32SBlue Swirl do { printf("stellaris_enet: " fmt , ## __VA_ARGS__); } while (0)
23001faf32SBlue Swirl #define BADF(fmt, ...) \
24001faf32SBlue Swirl do { fprintf(stderr, "stellaris_enet: error: " fmt , ## __VA_ARGS__); exit(1);} while (0)
25eea589ccSpbrook #else
26001faf32SBlue Swirl #define DPRINTF(fmt, ...) do {} while(0)
27001faf32SBlue Swirl #define BADF(fmt, ...) \
28001faf32SBlue Swirl do { fprintf(stderr, "stellaris_enet: error: " fmt , ## __VA_ARGS__);} while (0)
29eea589ccSpbrook #endif
30eea589ccSpbrook 
31eea589ccSpbrook #define SE_INT_RX       0x01
32eea589ccSpbrook #define SE_INT_TXER     0x02
33eea589ccSpbrook #define SE_INT_TXEMP    0x04
34eea589ccSpbrook #define SE_INT_FOV      0x08
35eea589ccSpbrook #define SE_INT_RXER     0x10
36eea589ccSpbrook #define SE_INT_MD       0x20
37eea589ccSpbrook #define SE_INT_PHY      0x40
38eea589ccSpbrook 
39eea589ccSpbrook #define SE_RCTL_RXEN    0x01
40eea589ccSpbrook #define SE_RCTL_AMUL    0x02
41eea589ccSpbrook #define SE_RCTL_PRMS    0x04
42eea589ccSpbrook #define SE_RCTL_BADCRC  0x08
43eea589ccSpbrook #define SE_RCTL_RSTFIFO 0x10
44eea589ccSpbrook 
45eea589ccSpbrook #define SE_TCTL_TXEN    0x01
46eea589ccSpbrook #define SE_TCTL_PADEN   0x02
47eea589ccSpbrook #define SE_TCTL_CRC     0x04
48eea589ccSpbrook #define SE_TCTL_DUPLEX  0x08
49eea589ccSpbrook 
502fa30abaSAndreas Färber #define TYPE_STELLARIS_ENET "stellaris_enet"
512fa30abaSAndreas Färber #define STELLARIS_ENET(obj) \
522fa30abaSAndreas Färber     OBJECT_CHECK(stellaris_enet_state, (obj), TYPE_STELLARIS_ENET)
532fa30abaSAndreas Färber 
54eea589ccSpbrook typedef struct {
552e119867SPeter Maydell     uint8_t data[2048];
562e119867SPeter Maydell     uint32_t len;
572e119867SPeter Maydell } StellarisEnetRxFrame;
582e119867SPeter Maydell 
592e119867SPeter Maydell typedef struct {
602fa30abaSAndreas Färber     SysBusDevice parent_obj;
612fa30abaSAndreas Färber 
62eea589ccSpbrook     uint32_t ris;
63eea589ccSpbrook     uint32_t im;
64eea589ccSpbrook     uint32_t rctl;
65eea589ccSpbrook     uint32_t tctl;
66eea589ccSpbrook     uint32_t thr;
67eea589ccSpbrook     uint32_t mctl;
68eea589ccSpbrook     uint32_t mdv;
69eea589ccSpbrook     uint32_t mtxd;
70eea589ccSpbrook     uint32_t mrxd;
71eea589ccSpbrook     uint32_t np;
722e119867SPeter Maydell     uint32_t tx_fifo_len;
73eea589ccSpbrook     uint8_t tx_fifo[2048];
74eea589ccSpbrook     /* Real hardware has a 2k fifo, which works out to be at most 31 packets.
75eea589ccSpbrook        We implement a full 31 packet fifo.  */
762e119867SPeter Maydell     StellarisEnetRxFrame rx[31];
772e119867SPeter Maydell     uint32_t rx_fifo_offset;
782e119867SPeter Maydell     uint32_t next_packet;
798c9b63b9SMark McLoughlin     NICState *nic;
80540f006aSGerd Hoffmann     NICConf conf;
81eea589ccSpbrook     qemu_irq irq;
82f070e1e2SAvi Kivity     MemoryRegion mmio;
83eea589ccSpbrook } stellaris_enet_state;
84eea589ccSpbrook 
852e119867SPeter Maydell static const VMStateDescription vmstate_rx_frame = {
862e119867SPeter Maydell     .name = "stellaris_enet/rx_frame",
872e119867SPeter Maydell     .version_id = 1,
882e119867SPeter Maydell     .minimum_version_id = 1,
892e119867SPeter Maydell     .fields = (VMStateField[]) {
902e119867SPeter Maydell         VMSTATE_UINT8_ARRAY(data, StellarisEnetRxFrame, 2048),
912e119867SPeter Maydell         VMSTATE_UINT32(len, StellarisEnetRxFrame),
922e119867SPeter Maydell         VMSTATE_END_OF_LIST()
932e119867SPeter Maydell     }
942e119867SPeter Maydell };
952e119867SPeter Maydell 
962e119867SPeter Maydell static int stellaris_enet_post_load(void *opaque, int version_id)
972e119867SPeter Maydell {
982e119867SPeter Maydell     stellaris_enet_state *s = opaque;
992e119867SPeter Maydell     int i;
1002e119867SPeter Maydell 
1012e119867SPeter Maydell     /* Sanitize inbound state. Note that next_packet is an index but
1022e119867SPeter Maydell      * np is a size; hence their valid upper bounds differ.
1032e119867SPeter Maydell      */
1042e119867SPeter Maydell     if (s->next_packet >= ARRAY_SIZE(s->rx)) {
1052e119867SPeter Maydell         return -1;
1062e119867SPeter Maydell     }
1072e119867SPeter Maydell 
1082e119867SPeter Maydell     if (s->np > ARRAY_SIZE(s->rx)) {
1092e119867SPeter Maydell         return -1;
1102e119867SPeter Maydell     }
1112e119867SPeter Maydell 
1122e119867SPeter Maydell     for (i = 0; i < ARRAY_SIZE(s->rx); i++) {
1132e119867SPeter Maydell         if (s->rx[i].len > ARRAY_SIZE(s->rx[i].data)) {
1142e119867SPeter Maydell             return -1;
1152e119867SPeter Maydell         }
1162e119867SPeter Maydell     }
1172e119867SPeter Maydell 
1182e119867SPeter Maydell     if (s->rx_fifo_offset > ARRAY_SIZE(s->rx[0].data) - 4) {
1192e119867SPeter Maydell         return -1;
1202e119867SPeter Maydell     }
1212e119867SPeter Maydell 
1222e119867SPeter Maydell     if (s->tx_fifo_len > ARRAY_SIZE(s->tx_fifo)) {
1232e119867SPeter Maydell         return -1;
1242e119867SPeter Maydell     }
1252e119867SPeter Maydell 
1262e119867SPeter Maydell     return 0;
1272e119867SPeter Maydell }
1282e119867SPeter Maydell 
1292e119867SPeter Maydell static const VMStateDescription vmstate_stellaris_enet = {
1302e119867SPeter Maydell     .name = "stellaris_enet",
1312e119867SPeter Maydell     .version_id = 2,
1322e119867SPeter Maydell     .minimum_version_id = 2,
1332e119867SPeter Maydell     .post_load = stellaris_enet_post_load,
1342e119867SPeter Maydell     .fields = (VMStateField[]) {
1352e119867SPeter Maydell         VMSTATE_UINT32(ris, stellaris_enet_state),
1362e119867SPeter Maydell         VMSTATE_UINT32(im, stellaris_enet_state),
1372e119867SPeter Maydell         VMSTATE_UINT32(rctl, stellaris_enet_state),
1382e119867SPeter Maydell         VMSTATE_UINT32(tctl, stellaris_enet_state),
1392e119867SPeter Maydell         VMSTATE_UINT32(thr, stellaris_enet_state),
1402e119867SPeter Maydell         VMSTATE_UINT32(mctl, stellaris_enet_state),
1412e119867SPeter Maydell         VMSTATE_UINT32(mdv, stellaris_enet_state),
1422e119867SPeter Maydell         VMSTATE_UINT32(mtxd, stellaris_enet_state),
1432e119867SPeter Maydell         VMSTATE_UINT32(mrxd, stellaris_enet_state),
1442e119867SPeter Maydell         VMSTATE_UINT32(np, stellaris_enet_state),
1452e119867SPeter Maydell         VMSTATE_UINT32(tx_fifo_len, stellaris_enet_state),
1462e119867SPeter Maydell         VMSTATE_UINT8_ARRAY(tx_fifo, stellaris_enet_state, 2048),
1472e119867SPeter Maydell         VMSTATE_STRUCT_ARRAY(rx, stellaris_enet_state, 31, 1,
1482e119867SPeter Maydell                              vmstate_rx_frame, StellarisEnetRxFrame),
1492e119867SPeter Maydell         VMSTATE_UINT32(rx_fifo_offset, stellaris_enet_state),
1502e119867SPeter Maydell         VMSTATE_UINT32(next_packet, stellaris_enet_state),
1512e119867SPeter Maydell         VMSTATE_END_OF_LIST()
1522e119867SPeter Maydell     }
1532e119867SPeter Maydell };
1542e119867SPeter Maydell 
155eea589ccSpbrook static void stellaris_enet_update(stellaris_enet_state *s)
156eea589ccSpbrook {
157eea589ccSpbrook     qemu_set_irq(s->irq, (s->ris & s->im) != 0);
158eea589ccSpbrook }
159eea589ccSpbrook 
160c6fa443bSPeter Maydell /* Return the data length of the packet currently being assembled
161c6fa443bSPeter Maydell  * in the TX fifo.
162c6fa443bSPeter Maydell  */
163c6fa443bSPeter Maydell static inline int stellaris_txpacket_datalen(stellaris_enet_state *s)
164c6fa443bSPeter Maydell {
165c6fa443bSPeter Maydell     return s->tx_fifo[0] | (s->tx_fifo[1] << 8);
166c6fa443bSPeter Maydell }
167c6fa443bSPeter Maydell 
168c6fa443bSPeter Maydell /* Return true if the packet currently in the TX FIFO is complete,
169c6fa443bSPeter Maydell * ie the FIFO holds enough bytes for the data length, ethernet header,
170c6fa443bSPeter Maydell * payload and optionally CRC.
171c6fa443bSPeter Maydell */
172c6fa443bSPeter Maydell static inline bool stellaris_txpacket_complete(stellaris_enet_state *s)
173c6fa443bSPeter Maydell {
174c6fa443bSPeter Maydell     int framelen = stellaris_txpacket_datalen(s);
175c6fa443bSPeter Maydell     framelen += 16;
176c6fa443bSPeter Maydell     if (!(s->tctl & SE_TCTL_CRC)) {
177c6fa443bSPeter Maydell         framelen += 4;
178c6fa443bSPeter Maydell     }
179c6fa443bSPeter Maydell     /* Cover the corner case of a 2032 byte payload with auto-CRC disabled:
180c6fa443bSPeter Maydell      * this requires more bytes than will fit in the FIFO. It's not totally
181c6fa443bSPeter Maydell      * clear how the h/w handles this, but if using threshold-based TX
182c6fa443bSPeter Maydell      * it will definitely try to transmit something.
183c6fa443bSPeter Maydell      */
184c6fa443bSPeter Maydell     framelen = MIN(framelen, ARRAY_SIZE(s->tx_fifo));
185c6fa443bSPeter Maydell     return s->tx_fifo_len >= framelen;
186c6fa443bSPeter Maydell }
187c6fa443bSPeter Maydell 
188a9171c4fSPeter Maydell /* Return true if the TX FIFO threshold is enabled and the FIFO
189a9171c4fSPeter Maydell  * has filled enough to reach it.
190a9171c4fSPeter Maydell  */
191a9171c4fSPeter Maydell static inline bool stellaris_tx_thr_reached(stellaris_enet_state *s)
192a9171c4fSPeter Maydell {
193a9171c4fSPeter Maydell     return (s->thr < 0x3f &&
194a9171c4fSPeter Maydell             (s->tx_fifo_len >= 4 * (s->thr * 8 + 1)));
195a9171c4fSPeter Maydell }
196a9171c4fSPeter Maydell 
197c6fa443bSPeter Maydell /* Send the packet currently in the TX FIFO */
198c6fa443bSPeter Maydell static void stellaris_enet_send(stellaris_enet_state *s)
199c6fa443bSPeter Maydell {
200c6fa443bSPeter Maydell     int framelen = stellaris_txpacket_datalen(s);
201c6fa443bSPeter Maydell 
202c6fa443bSPeter Maydell     /* Ethernet header is in the FIFO but not in the datacount.
203c6fa443bSPeter Maydell      * We don't implement explicit CRC, so just ignore any
204c6fa443bSPeter Maydell      * CRC value in the FIFO.
205c6fa443bSPeter Maydell      */
206c6fa443bSPeter Maydell     framelen += 14;
207c6fa443bSPeter Maydell     if ((s->tctl & SE_TCTL_PADEN) && framelen < 60) {
208c6fa443bSPeter Maydell         memset(&s->tx_fifo[framelen + 2], 0, 60 - framelen);
209c6fa443bSPeter Maydell         framelen = 60;
210c6fa443bSPeter Maydell     }
211c6fa443bSPeter Maydell     /* This MIN will have no effect unless the FIFO data is corrupt
212c6fa443bSPeter Maydell      * (eg bad data from an incoming migration); otherwise the check
213c6fa443bSPeter Maydell      * on the datalen at the start of writing the data into the FIFO
214c6fa443bSPeter Maydell      * will have caught this. Silently write a corrupt half-packet,
215c6fa443bSPeter Maydell      * which is what the hardware does in FIFO underrun situations.
216c6fa443bSPeter Maydell      */
217c6fa443bSPeter Maydell     framelen = MIN(framelen, ARRAY_SIZE(s->tx_fifo) - 2);
218c6fa443bSPeter Maydell     qemu_send_packet(qemu_get_queue(s->nic), s->tx_fifo + 2, framelen);
219c6fa443bSPeter Maydell     s->tx_fifo_len = 0;
220c6fa443bSPeter Maydell     s->ris |= SE_INT_TXEMP;
221c6fa443bSPeter Maydell     stellaris_enet_update(s);
222c6fa443bSPeter Maydell     DPRINTF("Done TX\n");
223c6fa443bSPeter Maydell }
224c6fa443bSPeter Maydell 
225eea589ccSpbrook /* TODO: Implement MAC address filtering.  */
2264e68f7a0SStefan Hajnoczi static ssize_t stellaris_enet_receive(NetClientState *nc, const uint8_t *buf, size_t size)
227eea589ccSpbrook {
228cc1f0f45SJason Wang     stellaris_enet_state *s = qemu_get_nic_opaque(nc);
229eea589ccSpbrook     int n;
230eea589ccSpbrook     uint8_t *p;
231eea589ccSpbrook     uint32_t crc;
232eea589ccSpbrook 
233eea589ccSpbrook     if ((s->rctl & SE_RCTL_RXEN) == 0)
2344f1c942bSMark McLoughlin         return -1;
235eea589ccSpbrook     if (s->np >= 31) {
2361ef4a606SFam Zheng         return 0;
237eea589ccSpbrook     }
238eea589ccSpbrook 
239eacd606cSPeter Maydell     DPRINTF("Received packet len=%zu\n", size);
240eea589ccSpbrook     n = s->next_packet + s->np;
241eea589ccSpbrook     if (n >= 31)
242eea589ccSpbrook         n -= 31;
243eea589ccSpbrook 
2443a15cc0eSPrasad J Pandit     if (size >= sizeof(s->rx[n].data) - 6) {
2453a15cc0eSPrasad J Pandit         /* If the packet won't fit into the
2463a15cc0eSPrasad J Pandit          * emulated 2K RAM, this is reported
2473a15cc0eSPrasad J Pandit          * as a FIFO overrun error.
2483a15cc0eSPrasad J Pandit          */
2493a15cc0eSPrasad J Pandit         s->ris |= SE_INT_FOV;
2503a15cc0eSPrasad J Pandit         stellaris_enet_update(s);
2513a15cc0eSPrasad J Pandit         return -1;
2523a15cc0eSPrasad J Pandit     }
2533a15cc0eSPrasad J Pandit 
2543a15cc0eSPrasad J Pandit     s->np++;
255eea589ccSpbrook     s->rx[n].len = size + 6;
256eea589ccSpbrook     p = s->rx[n].data;
257eea589ccSpbrook     *(p++) = (size + 6);
258eea589ccSpbrook     *(p++) = (size + 6) >> 8;
259eea589ccSpbrook     memcpy (p, buf, size);
260eea589ccSpbrook     p += size;
261eea589ccSpbrook     crc = crc32(~0, buf, size);
262eea589ccSpbrook     *(p++) = crc;
263eea589ccSpbrook     *(p++) = crc >> 8;
264eea589ccSpbrook     *(p++) = crc >> 16;
265eea589ccSpbrook     *(p++) = crc >> 24;
266eea589ccSpbrook     /* Clear the remaining bytes in the last word.  */
267eea589ccSpbrook     if ((size & 3) != 2) {
268eea589ccSpbrook         memset(p, 0, (6 - size) & 3);
269eea589ccSpbrook     }
270eea589ccSpbrook 
271eea589ccSpbrook     s->ris |= SE_INT_RX;
272eea589ccSpbrook     stellaris_enet_update(s);
2734f1c942bSMark McLoughlin 
2744f1c942bSMark McLoughlin     return size;
275eea589ccSpbrook }
276eea589ccSpbrook 
2771ef4a606SFam Zheng static int stellaris_enet_can_receive(stellaris_enet_state *s)
278eea589ccSpbrook {
279eea589ccSpbrook     return (s->np < 31);
280eea589ccSpbrook }
281eea589ccSpbrook 
282a8170e5eSAvi Kivity static uint64_t stellaris_enet_read(void *opaque, hwaddr offset,
283f070e1e2SAvi Kivity                                     unsigned size)
284eea589ccSpbrook {
28557b452a8SPaul Brook     stellaris_enet_state *s = (stellaris_enet_state *)opaque;
286eea589ccSpbrook     uint32_t val;
287eea589ccSpbrook 
288eea589ccSpbrook     switch (offset) {
289eea589ccSpbrook     case 0x00: /* RIS */
290eea589ccSpbrook         DPRINTF("IRQ status %02x\n", s->ris);
291eea589ccSpbrook         return s->ris;
292eea589ccSpbrook     case 0x04: /* IM */
293eea589ccSpbrook         return s->im;
294eea589ccSpbrook     case 0x08: /* RCTL */
295eea589ccSpbrook         return s->rctl;
296eea589ccSpbrook     case 0x0c: /* TCTL */
297eea589ccSpbrook         return s->tctl;
298eea589ccSpbrook     case 0x10: /* DATA */
299889ac2a3SPeter Maydell     {
300889ac2a3SPeter Maydell         uint8_t *rx_fifo;
301889ac2a3SPeter Maydell 
302eea589ccSpbrook         if (s->np == 0) {
303eea589ccSpbrook             BADF("RX underflow\n");
304eea589ccSpbrook             return 0;
305eea589ccSpbrook         }
306889ac2a3SPeter Maydell 
307889ac2a3SPeter Maydell         rx_fifo = s->rx[s->next_packet].data + s->rx_fifo_offset;
308889ac2a3SPeter Maydell 
309889ac2a3SPeter Maydell         val = rx_fifo[0] | (rx_fifo[1] << 8) | (rx_fifo[2] << 16)
310889ac2a3SPeter Maydell               | (rx_fifo[3] << 24);
311889ac2a3SPeter Maydell         s->rx_fifo_offset += 4;
312889ac2a3SPeter Maydell         if (s->rx_fifo_offset >= s->rx[s->next_packet].len) {
313889ac2a3SPeter Maydell             s->rx_fifo_offset = 0;
314eea589ccSpbrook             s->next_packet++;
315eea589ccSpbrook             if (s->next_packet >= 31)
316eea589ccSpbrook                 s->next_packet = 0;
317eea589ccSpbrook             s->np--;
318eea589ccSpbrook             DPRINTF("RX done np=%d\n", s->np);
3191ef4a606SFam Zheng             if (!s->np && stellaris_enet_can_receive(s)) {
3201ef4a606SFam Zheng                 qemu_flush_queued_packets(qemu_get_queue(s->nic));
3211ef4a606SFam Zheng             }
322eea589ccSpbrook         }
323eea589ccSpbrook         return val;
324889ac2a3SPeter Maydell     }
325eea589ccSpbrook     case 0x14: /* IA0 */
326540f006aSGerd Hoffmann         return s->conf.macaddr.a[0] | (s->conf.macaddr.a[1] << 8)
327106a73b6SPeter Maydell             | (s->conf.macaddr.a[2] << 16)
328106a73b6SPeter Maydell             | ((uint32_t)s->conf.macaddr.a[3] << 24);
329eea589ccSpbrook     case 0x18: /* IA1 */
330540f006aSGerd Hoffmann         return s->conf.macaddr.a[4] | (s->conf.macaddr.a[5] << 8);
331eea589ccSpbrook     case 0x1c: /* THR */
332eea589ccSpbrook         return s->thr;
333eea589ccSpbrook     case 0x20: /* MCTL */
334eea589ccSpbrook         return s->mctl;
335eea589ccSpbrook     case 0x24: /* MDV */
336eea589ccSpbrook         return s->mdv;
337eea589ccSpbrook     case 0x28: /* MADD */
338eea589ccSpbrook         return 0;
339eea589ccSpbrook     case 0x2c: /* MTXD */
340eea589ccSpbrook         return s->mtxd;
341eea589ccSpbrook     case 0x30: /* MRXD */
342eea589ccSpbrook         return s->mrxd;
343eea589ccSpbrook     case 0x34: /* NP */
344eea589ccSpbrook         return s->np;
345eea589ccSpbrook     case 0x38: /* TR */
346eea589ccSpbrook         return 0;
3475786e35dSPhilippe Mathieu-Daudé     case 0x3c: /* Undocumented: Timestamp? */
348eea589ccSpbrook         return 0;
349eea589ccSpbrook     default:
350f6de9957SPhilippe Mathieu-Daudé         qemu_log_mask(LOG_GUEST_ERROR, "stellaris_enet_rd%d: Illegal register"
351f6de9957SPhilippe Mathieu-Daudé                                        " 0x02%" HWADDR_PRIx "\n",
352f6de9957SPhilippe Mathieu-Daudé                       size * 8, offset);
353eea589ccSpbrook         return 0;
354eea589ccSpbrook     }
355eea589ccSpbrook }
356eea589ccSpbrook 
357a8170e5eSAvi Kivity static void stellaris_enet_write(void *opaque, hwaddr offset,
358f070e1e2SAvi Kivity                                  uint64_t value, unsigned size)
359eea589ccSpbrook {
360eea589ccSpbrook     stellaris_enet_state *s = (stellaris_enet_state *)opaque;
361eea589ccSpbrook 
362eea589ccSpbrook     switch (offset) {
363eea589ccSpbrook     case 0x00: /* IACK */
364eea589ccSpbrook         s->ris &= ~value;
365eacd606cSPeter Maydell         DPRINTF("IRQ ack %02" PRIx64 "/%02x\n", value, s->ris);
366eea589ccSpbrook         stellaris_enet_update(s);
367eea589ccSpbrook         /* Clearing TXER also resets the TX fifo.  */
368c6fa443bSPeter Maydell         if (value & SE_INT_TXER) {
369c6fa443bSPeter Maydell             s->tx_fifo_len = 0;
370c6fa443bSPeter Maydell         }
371eea589ccSpbrook         break;
372eea589ccSpbrook     case 0x04: /* IM */
373eacd606cSPeter Maydell         DPRINTF("IRQ mask %02" PRIx64 "/%02x\n", value, s->ris);
374eea589ccSpbrook         s->im = value;
375eea589ccSpbrook         stellaris_enet_update(s);
376eea589ccSpbrook         break;
377eea589ccSpbrook     case 0x08: /* RCTL */
378eea589ccSpbrook         s->rctl = value;
379eea589ccSpbrook         if (value & SE_RCTL_RSTFIFO) {
380eea589ccSpbrook             s->np = 0;
381889ac2a3SPeter Maydell             s->rx_fifo_offset = 0;
382eea589ccSpbrook             stellaris_enet_update(s);
383eea589ccSpbrook         }
384eea589ccSpbrook         break;
385eea589ccSpbrook     case 0x0c: /* TCTL */
386eea589ccSpbrook         s->tctl = value;
387eea589ccSpbrook         break;
388eea589ccSpbrook     case 0x10: /* DATA */
389c6fa443bSPeter Maydell         if (s->tx_fifo_len == 0) {
390c6fa443bSPeter Maydell             /* The first word is special, it contains the data length */
391c6fa443bSPeter Maydell             int framelen = value & 0xffff;
392c6fa443bSPeter Maydell             if (framelen > 2032) {
393c6fa443bSPeter Maydell                 DPRINTF("TX frame too long (%d)\n", framelen);
394eea589ccSpbrook                 s->ris |= SE_INT_TXER;
395eea589ccSpbrook                 stellaris_enet_update(s);
396c6fa443bSPeter Maydell                 break;
397eea589ccSpbrook             }
398c6fa443bSPeter Maydell         }
399c6fa443bSPeter Maydell 
4005c10495aSPeter Maydell         if (s->tx_fifo_len + 4 <= ARRAY_SIZE(s->tx_fifo)) {
401eea589ccSpbrook             s->tx_fifo[s->tx_fifo_len++] = value;
402eea589ccSpbrook             s->tx_fifo[s->tx_fifo_len++] = value >> 8;
403eea589ccSpbrook             s->tx_fifo[s->tx_fifo_len++] = value >> 16;
404eea589ccSpbrook             s->tx_fifo[s->tx_fifo_len++] = value >> 24;
4055c10495aSPeter Maydell         }
406c6fa443bSPeter Maydell 
407a9171c4fSPeter Maydell         if (stellaris_tx_thr_reached(s) && stellaris_txpacket_complete(s)) {
408c6fa443bSPeter Maydell             stellaris_enet_send(s);
409eea589ccSpbrook         }
410eea589ccSpbrook         break;
411eea589ccSpbrook     case 0x14: /* IA0 */
412540f006aSGerd Hoffmann         s->conf.macaddr.a[0] = value;
413540f006aSGerd Hoffmann         s->conf.macaddr.a[1] = value >> 8;
414540f006aSGerd Hoffmann         s->conf.macaddr.a[2] = value >> 16;
415540f006aSGerd Hoffmann         s->conf.macaddr.a[3] = value >> 24;
416eea589ccSpbrook         break;
417eea589ccSpbrook     case 0x18: /* IA1 */
418540f006aSGerd Hoffmann         s->conf.macaddr.a[4] = value;
419540f006aSGerd Hoffmann         s->conf.macaddr.a[5] = value >> 8;
420eea589ccSpbrook         break;
421eea589ccSpbrook     case 0x1c: /* THR */
422eea589ccSpbrook         s->thr = value;
423eea589ccSpbrook         break;
424eea589ccSpbrook     case 0x20: /* MCTL */
425d05a8628SMichael Davidsaver         /* TODO: MII registers aren't modelled.
426d05a8628SMichael Davidsaver          * Clear START, indicating that the operation completes immediately.
427d05a8628SMichael Davidsaver          */
428d05a8628SMichael Davidsaver         s->mctl = value & ~1;
429eea589ccSpbrook         break;
430eea589ccSpbrook     case 0x24: /* MDV */
431eea589ccSpbrook         s->mdv = value;
432eea589ccSpbrook         break;
433eea589ccSpbrook     case 0x28: /* MADD */
434eea589ccSpbrook         /* ignored.  */
435eea589ccSpbrook         break;
436eea589ccSpbrook     case 0x2c: /* MTXD */
437eea589ccSpbrook         s->mtxd = value & 0xff;
438eea589ccSpbrook         break;
439a9171c4fSPeter Maydell     case 0x38: /* TR */
440a9171c4fSPeter Maydell         if (value & 1) {
441a9171c4fSPeter Maydell             stellaris_enet_send(s);
442a9171c4fSPeter Maydell         }
443a9171c4fSPeter Maydell         break;
444eea589ccSpbrook     case 0x30: /* MRXD */
445eea589ccSpbrook     case 0x34: /* NP */
446eea589ccSpbrook         /* Ignored.  */
447eea589ccSpbrook     case 0x3c: /* Undocuented: Timestamp? */
448eea589ccSpbrook         /* Ignored.  */
449eea589ccSpbrook         break;
450eea589ccSpbrook     default:
451f6de9957SPhilippe Mathieu-Daudé         qemu_log_mask(LOG_GUEST_ERROR, "stellaris_enet_wr%d: Illegal register "
452f6de9957SPhilippe Mathieu-Daudé                                        "0x02%" HWADDR_PRIx " = 0x%" PRIx64 "\n",
453f6de9957SPhilippe Mathieu-Daudé                       size * 8, offset, value);
454eea589ccSpbrook     }
455eea589ccSpbrook }
456eea589ccSpbrook 
457f070e1e2SAvi Kivity static const MemoryRegionOps stellaris_enet_ops = {
458f070e1e2SAvi Kivity     .read = stellaris_enet_read,
459f070e1e2SAvi Kivity     .write = stellaris_enet_write,
460f070e1e2SAvi Kivity     .endianness = DEVICE_NATIVE_ENDIAN,
461eea589ccSpbrook };
462eea589ccSpbrook 
463b11ff5ecSCédric Le Goater static void stellaris_enet_reset(DeviceState *dev)
464eea589ccSpbrook {
465b11ff5ecSCédric Le Goater     stellaris_enet_state *s =  STELLARIS_ENET(dev);
466b11ff5ecSCédric Le Goater 
467eea589ccSpbrook     s->mdv = 0x80;
468eea589ccSpbrook     s->rctl = SE_RCTL_BADCRC;
469eea589ccSpbrook     s->im = SE_INT_PHY | SE_INT_MD | SE_INT_RXER | SE_INT_FOV | SE_INT_TXEMP
470eea589ccSpbrook             | SE_INT_TXER | SE_INT_RX;
471eea589ccSpbrook     s->thr = 0x3f;
472c6fa443bSPeter Maydell     s->tx_fifo_len = 0;
473eea589ccSpbrook }
474eea589ccSpbrook 
4758c9b63b9SMark McLoughlin static NetClientInfo net_stellaris_enet_info = {
476f394b2e2SEric Blake     .type = NET_CLIENT_DRIVER_NIC,
4778c9b63b9SMark McLoughlin     .size = sizeof(NICState),
4788c9b63b9SMark McLoughlin     .receive = stellaris_enet_receive,
4798c9b63b9SMark McLoughlin };
4808c9b63b9SMark McLoughlin 
481bb4d5850SCédric Le Goater static void stellaris_enet_realize(DeviceState *dev, Error **errp)
482eea589ccSpbrook {
483bb4d5850SCédric Le Goater     SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
4842fa30abaSAndreas Färber     stellaris_enet_state *s = STELLARIS_ENET(dev);
485eea589ccSpbrook 
486eedfac6fSPaolo Bonzini     memory_region_init_io(&s->mmio, OBJECT(s), &stellaris_enet_ops, s,
487eedfac6fSPaolo Bonzini                           "stellaris_enet", 0x1000);
4882fa30abaSAndreas Färber     sysbus_init_mmio(sbd, &s->mmio);
4892fa30abaSAndreas Färber     sysbus_init_irq(sbd, &s->irq);
490540f006aSGerd Hoffmann     qemu_macaddr_default_if_unset(&s->conf.macaddr);
491eea589ccSpbrook 
4928c9b63b9SMark McLoughlin     s->nic = qemu_new_nic(&net_stellaris_enet_info, &s->conf,
4932fa30abaSAndreas Färber                           object_get_typename(OBJECT(dev)), dev->id, s);
494b356f76dSJason Wang     qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
495eea589ccSpbrook }
496a5580466SPaul Brook 
497999e12bbSAnthony Liguori static Property stellaris_enet_properties[] = {
498540f006aSGerd Hoffmann     DEFINE_NIC_PROPERTIES(stellaris_enet_state, conf),
499540f006aSGerd Hoffmann     DEFINE_PROP_END_OF_LIST(),
500999e12bbSAnthony Liguori };
501999e12bbSAnthony Liguori 
502999e12bbSAnthony Liguori static void stellaris_enet_class_init(ObjectClass *klass, void *data)
503999e12bbSAnthony Liguori {
50439bffca2SAnthony Liguori     DeviceClass *dc = DEVICE_CLASS(klass);
505999e12bbSAnthony Liguori 
506bb4d5850SCédric Le Goater     dc->realize = stellaris_enet_realize;
507b11ff5ecSCédric Le Goater     dc->reset = stellaris_enet_reset;
50839bffca2SAnthony Liguori     dc->props = stellaris_enet_properties;
5092e119867SPeter Maydell     dc->vmsd = &vmstate_stellaris_enet;
510540f006aSGerd Hoffmann }
511999e12bbSAnthony Liguori 
5128c43a6f0SAndreas Färber static const TypeInfo stellaris_enet_info = {
5132fa30abaSAndreas Färber     .name          = TYPE_STELLARIS_ENET,
51439bffca2SAnthony Liguori     .parent        = TYPE_SYS_BUS_DEVICE,
51539bffca2SAnthony Liguori     .instance_size = sizeof(stellaris_enet_state),
516999e12bbSAnthony Liguori     .class_init    = stellaris_enet_class_init,
517540f006aSGerd Hoffmann };
518540f006aSGerd Hoffmann 
51983f7d43aSAndreas Färber static void stellaris_enet_register_types(void)
520a5580466SPaul Brook {
52139bffca2SAnthony Liguori     type_register_static(&stellaris_enet_info);
522a5580466SPaul Brook }
523a5580466SPaul Brook 
52483f7d43aSAndreas Färber type_init(stellaris_enet_register_types)
525