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