Fix SD init arguments.
[qemu] / hw / stellaris_enet.c
1 /*
2  * Luminary Micro Stellaris Ethernet Controller
3  *
4  * Copyright (c) 2007 CodeSourcery.
5  * Written by Paul Brook
6  *
7  * This code is licenced under the GPL.
8  */
9 #include "hw.h"
10 #include "arm-misc.h"
11 #include "net.h"
12 #include <zlib.h>
13
14 //#define DEBUG_STELLARIS_ENET 1
15
16 #ifdef DEBUG_STELLARIS_ENET
17 #define DPRINTF(fmt, args...) \
18 do { printf("stellaris_enet: " fmt , ##args); } while (0)
19 #define BADF(fmt, args...) \
20 do { fprintf(stderr, "stellaris_enet: error: " fmt , ##args); exit(1);} while (0)
21 #else
22 #define DPRINTF(fmt, args...) do {} while(0)
23 #define BADF(fmt, args...) \
24 do { fprintf(stderr, "stellaris_enet: error: " fmt , ##args);} while (0)
25 #endif
26
27 #define SE_INT_RX       0x01
28 #define SE_INT_TXER     0x02
29 #define SE_INT_TXEMP    0x04
30 #define SE_INT_FOV      0x08
31 #define SE_INT_RXER     0x10
32 #define SE_INT_MD       0x20
33 #define SE_INT_PHY      0x40
34
35 #define SE_RCTL_RXEN    0x01
36 #define SE_RCTL_AMUL    0x02
37 #define SE_RCTL_PRMS    0x04
38 #define SE_RCTL_BADCRC  0x08
39 #define SE_RCTL_RSTFIFO 0x10
40
41 #define SE_TCTL_TXEN    0x01
42 #define SE_TCTL_PADEN   0x02
43 #define SE_TCTL_CRC     0x04
44 #define SE_TCTL_DUPLEX  0x08
45
46 typedef struct {
47     uint32_t base;
48     uint32_t ris;
49     uint32_t im;
50     uint32_t rctl;
51     uint32_t tctl;
52     uint32_t thr;
53     uint32_t mctl;
54     uint32_t mdv;
55     uint32_t mtxd;
56     uint32_t mrxd;
57     uint32_t np;
58     int tx_frame_len;
59     int tx_fifo_len;
60     uint8_t tx_fifo[2048];
61     /* Real hardware has a 2k fifo, which works out to be at most 31 packets.
62        We implement a full 31 packet fifo.  */
63     struct {
64         uint8_t data[2048];
65         int len;
66     } rx[31];
67     uint8_t *rx_fifo;
68     int rx_fifo_len;
69     int next_packet;
70     VLANClientState *vc;
71     qemu_irq irq;
72     uint8_t macaddr[6];
73 } stellaris_enet_state;
74
75 static void stellaris_enet_update(stellaris_enet_state *s)
76 {
77     qemu_set_irq(s->irq, (s->ris & s->im) != 0);
78 }
79
80 /* TODO: Implement MAC address filtering.  */
81 static void stellaris_enet_receive(void *opaque, const uint8_t *buf, int size)
82 {
83     stellaris_enet_state *s = (stellaris_enet_state *)opaque;
84     int n;
85     uint8_t *p;
86     uint32_t crc;
87
88     if ((s->rctl & SE_RCTL_RXEN) == 0)
89         return;
90     if (s->np >= 31) {
91         DPRINTF("Packet dropped\n");
92         return;
93     }
94
95     DPRINTF("Received packet len=%d\n", size);
96     n = s->next_packet + s->np;
97     if (n >= 31)
98         n -= 31;
99     s->np++;
100
101     s->rx[n].len = size + 6;
102     p = s->rx[n].data;
103     *(p++) = (size + 6);
104     *(p++) = (size + 6) >> 8;
105     memcpy (p, buf, size);
106     p += size;
107     crc = crc32(~0, buf, size);
108     *(p++) = crc;
109     *(p++) = crc >> 8;
110     *(p++) = crc >> 16;
111     *(p++) = crc >> 24;
112     /* Clear the remaining bytes in the last word.  */
113     if ((size & 3) != 2) {
114         memset(p, 0, (6 - size) & 3);
115     }
116
117     s->ris |= SE_INT_RX;
118     stellaris_enet_update(s);
119 }
120
121 static int stellaris_enet_can_receive(void *opaque)
122 {
123     stellaris_enet_state *s = (stellaris_enet_state *)opaque;
124
125     if ((s->rctl & SE_RCTL_RXEN) == 0)
126         return 1;
127
128     return (s->np < 31);
129 }
130
131 static uint32_t stellaris_enet_read(void *opaque, target_phys_addr_t offset)
132 {
133     stellaris_enet_state *s = (stellaris_enet_state *)opaque;
134     uint32_t val;
135
136     offset -= s->base;
137     switch (offset) {
138     case 0x00: /* RIS */
139         DPRINTF("IRQ status %02x\n", s->ris);
140         return s->ris;
141     case 0x04: /* IM */
142         return s->im;
143     case 0x08: /* RCTL */
144         return s->rctl;
145     case 0x0c: /* TCTL */
146         return s->tctl;
147     case 0x10: /* DATA */
148         if (s->rx_fifo_len == 0) {
149             if (s->np == 0) {
150                 BADF("RX underflow\n");
151                 return 0;
152             }
153             s->rx_fifo_len = s->rx[s->next_packet].len;
154             s->rx_fifo = s->rx[s->next_packet].data;
155             DPRINTF("RX FIFO start packet len=%d\n", s->rx_fifo_len);
156         }
157         val = s->rx_fifo[0] | (s->rx_fifo[1] << 8) | (s->rx_fifo[2] << 16)
158               | (s->rx_fifo[3] << 24);
159         s->rx_fifo += 4;
160         s->rx_fifo_len -= 4;
161         if (s->rx_fifo_len <= 0) {
162             s->rx_fifo_len = 0;
163             s->next_packet++;
164             if (s->next_packet >= 31)
165                 s->next_packet = 0;
166             s->np--;
167             DPRINTF("RX done np=%d\n", s->np);
168         }
169         return val;
170     case 0x14: /* IA0 */
171         return s->macaddr[0] | (s->macaddr[1] << 8)
172                | (s->macaddr[2] << 16) | (s->macaddr[3] << 24);
173     case 0x18: /* IA1 */
174         return s->macaddr[4] | (s->macaddr[5] << 8);
175     case 0x1c: /* THR */
176         return s->thr;
177     case 0x20: /* MCTL */
178         return s->mctl;
179     case 0x24: /* MDV */
180         return s->mdv;
181     case 0x28: /* MADD */
182         return 0;
183     case 0x2c: /* MTXD */
184         return s->mtxd;
185     case 0x30: /* MRXD */
186         return s->mrxd;
187     case 0x34: /* NP */
188         return s->np;
189     case 0x38: /* TR */
190         return 0;
191     case 0x3c: /* Undocuented: Timestamp? */
192         return 0;
193     default:
194         cpu_abort (cpu_single_env, "stellaris_enet_read: Bad offset %x\n",
195                    (int)offset);
196         return 0;
197     }
198 }
199
200 static void stellaris_enet_write(void *opaque, target_phys_addr_t offset,
201                         uint32_t value)
202 {
203     stellaris_enet_state *s = (stellaris_enet_state *)opaque;
204
205     offset -= s->base;
206     switch (offset) {
207     case 0x00: /* IACK */
208         s->ris &= ~value;
209         DPRINTF("IRQ ack %02x/%02x\n", value, s->ris);
210         stellaris_enet_update(s);
211         /* Clearing TXER also resets the TX fifo.  */
212         if (value & SE_INT_TXER)
213             s->tx_frame_len = -1;
214         break;
215     case 0x04: /* IM */
216         DPRINTF("IRQ mask %02x/%02x\n", value, s->ris);
217         s->im = value;
218         stellaris_enet_update(s);
219         break;
220     case 0x08: /* RCTL */
221         s->rctl = value;
222         if (value & SE_RCTL_RSTFIFO) {
223             s->rx_fifo_len = 0;
224             s->np = 0;
225             stellaris_enet_update(s);
226         }
227         break;
228     case 0x0c: /* TCTL */
229         s->tctl = value;
230         break;
231     case 0x10: /* DATA */
232         if (s->tx_frame_len == -1) {
233             s->tx_frame_len = value & 0xffff;
234             if (s->tx_frame_len > 2032) {
235                 DPRINTF("TX frame too long (%d)\n", s->tx_frame_len);
236                 s->tx_frame_len = 0;
237                 s->ris |= SE_INT_TXER;
238                 stellaris_enet_update(s);
239             } else {
240                 DPRINTF("Start TX frame len=%d\n", s->tx_frame_len);
241                 /* The value written does not include the ethernet header.  */
242                 s->tx_frame_len += 14;
243                 if ((s->tctl & SE_TCTL_CRC) == 0)
244                     s->tx_frame_len += 4;
245                 s->tx_fifo_len = 0;
246                 s->tx_fifo[s->tx_fifo_len++] = value >> 16;
247                 s->tx_fifo[s->tx_fifo_len++] = value >> 24;
248             }
249         } else {
250             s->tx_fifo[s->tx_fifo_len++] = value;
251             s->tx_fifo[s->tx_fifo_len++] = value >> 8;
252             s->tx_fifo[s->tx_fifo_len++] = value >> 16;
253             s->tx_fifo[s->tx_fifo_len++] = value >> 24;
254             if (s->tx_fifo_len >= s->tx_frame_len) {
255                 /* We don't implement explicit CRC, so just chop it off.  */
256                 if ((s->tctl & SE_TCTL_CRC) == 0)
257                     s->tx_frame_len -= 4;
258                 if ((s->tctl & SE_TCTL_PADEN) && s->tx_frame_len < 60) {
259                     memset(&s->tx_fifo[s->tx_frame_len], 0, 60 - s->tx_frame_len);
260                     s->tx_fifo_len = 60;
261                 }
262                 qemu_send_packet(s->vc, s->tx_fifo, s->tx_frame_len);
263                 s->tx_frame_len = -1;
264                 s->ris |= SE_INT_TXEMP;
265                 stellaris_enet_update(s);
266                 DPRINTF("Done TX\n");
267             }
268         }
269         break;
270     case 0x14: /* IA0 */
271         s->macaddr[0] = value;
272         s->macaddr[1] = value >> 8;
273         s->macaddr[2] = value >> 16;
274         s->macaddr[3] = value >> 24;
275         break;
276     case 0x18: /* IA1 */
277         s->macaddr[4] = value;
278         s->macaddr[5] = value >> 8;
279         break;
280     case 0x1c: /* THR */
281         s->thr = value;
282         break;
283     case 0x20: /* MCTL */
284         s->mctl = value;
285         break;
286     case 0x24: /* MDV */
287         s->mdv = value;
288         break;
289     case 0x28: /* MADD */
290         /* ignored.  */
291         break;
292     case 0x2c: /* MTXD */
293         s->mtxd = value & 0xff;
294         break;
295     case 0x30: /* MRXD */
296     case 0x34: /* NP */
297     case 0x38: /* TR */
298         /* Ignored.  */
299     case 0x3c: /* Undocuented: Timestamp? */
300         /* Ignored.  */
301         break;
302     default:
303         cpu_abort (cpu_single_env, "stellaris_enet_write: Bad offset %x\n",
304                    (int)offset);
305     }
306 }
307
308 static CPUReadMemoryFunc *stellaris_enet_readfn[] = {
309    stellaris_enet_read,
310    stellaris_enet_read,
311    stellaris_enet_read
312 };
313
314 static CPUWriteMemoryFunc *stellaris_enet_writefn[] = {
315    stellaris_enet_write,
316    stellaris_enet_write,
317    stellaris_enet_write
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 void stellaris_enet_init(NICInfo *nd, uint32_t base, qemu_irq irq)
330 {
331     stellaris_enet_state *s;
332     int iomemtype;
333
334     s = (stellaris_enet_state *)qemu_mallocz(sizeof(stellaris_enet_state));
335     iomemtype = cpu_register_io_memory(0, stellaris_enet_readfn,
336                                        stellaris_enet_writefn, s);
337     cpu_register_physical_memory(base, 0x00001000, iomemtype);
338     s->base = base;
339     s->irq = irq;
340     memcpy(s->macaddr, nd->macaddr, 6);
341
342     if (nd->vlan)
343         s->vc = qemu_new_vlan_client(nd->vlan, stellaris_enet_receive,
344                                      stellaris_enet_can_receive, s);
345
346     stellaris_enet_reset(s);
347 }