~vcs-imports/qemu/git

« back to all changes in this revision

Viewing changes to hw/stellaris_enet.c

  • Committer: pbrook
  • Date: 2006-10-22 00:18:54 UTC
  • Revision ID: git-v1:e6e5906b6e0a81718066ca43aef57515026c6624
ColdFire target.


git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@2196 c046a42c-6fe2-441c-8c8c-71466251a162

Show diffs side-by-side

added added

removed removed

Lines of Context:
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
 
}