Statistics
| Branch: | Revision:

root / hw / smc91c111.c @ 5fafdf24

History | View | Annotate | Download (18.9 kB)

1
/*
2
 * SMSC 91C111 Ethernet interface emulation
3
 *
4
 * Copyright (c) 2005 CodeSourcery, LLC.
5
 * Written by Paul Brook
6
 *
7
 * This code is licenced under the GPL
8
 */
9

    
10
#include "vl.h"
11
/* For crc32 */
12
#include <zlib.h>
13

    
14
/* Number of 2k memory pages available.  */
15
#define NUM_PACKETS 4
16

    
17
typedef struct {
18
    uint32_t base;
19
    VLANClientState *vc;
20
    uint16_t tcr;
21
    uint16_t rcr;
22
    uint16_t cr;
23
    uint16_t ctr;
24
    uint16_t gpr;
25
    uint16_t ptr;
26
    uint16_t ercv;
27
    qemu_irq irq;
28
    int bank;
29
    int packet_num;
30
    int tx_alloc;
31
    /* Bitmask of allocated packets.  */
32
    int allocated;
33
    int tx_fifo_len;
34
    int tx_fifo[NUM_PACKETS];
35
    int rx_fifo_len;
36
    int rx_fifo[NUM_PACKETS];
37
    int tx_fifo_done_len;
38
    int tx_fifo_done[NUM_PACKETS];
39
    /* Packet buffer memory.  */
40
    uint8_t data[NUM_PACKETS][2048];
41
    uint8_t int_level;
42
    uint8_t int_mask;
43
    uint8_t macaddr[6];
44
} smc91c111_state;
45

    
46
#define RCR_SOFT_RST  0x8000
47
#define RCR_STRIP_CRC 0x0200
48
#define RCR_RXEN      0x0100
49

    
50
#define TCR_EPH_LOOP  0x2000
51
#define TCR_NOCRC     0x0100
52
#define TCR_PAD_EN    0x0080
53
#define TCR_FORCOL    0x0004
54
#define TCR_LOOP      0x0002
55
#define TCR_TXEN      0x0001
56

    
57
#define INT_MD        0x80
58
#define INT_ERCV      0x40
59
#define INT_EPH       0x20
60
#define INT_RX_OVRN   0x10
61
#define INT_ALLOC     0x08
62
#define INT_TX_EMPTY  0x04
63
#define INT_TX        0x02
64
#define INT_RCV       0x01
65

    
66
#define CTR_AUTO_RELEASE  0x0800
67
#define CTR_RELOAD        0x0002
68
#define CTR_STORE         0x0001
69

    
70
#define RS_ALGNERR      0x8000
71
#define RS_BRODCAST     0x4000
72
#define RS_BADCRC       0x2000
73
#define RS_ODDFRAME     0x1000
74
#define RS_TOOLONG      0x0800
75
#define RS_TOOSHORT     0x0400
76
#define RS_MULTICAST    0x0001
77

    
78
/* Update interrupt status.  */
79
static void smc91c111_update(smc91c111_state *s)
80
{
81
    int level;
82

    
83
    if (s->tx_fifo_len == 0)
84
        s->int_level |= INT_TX_EMPTY;
85
    if (s->tx_fifo_done_len != 0)
86
        s->int_level |= INT_TX;
87
    level = (s->int_level & s->int_mask) != 0;
88
    qemu_set_irq(s->irq, level);
89
}
90

    
91
/* Try to allocate a packet.  Returns 0x80 on failure.  */
92
static int smc91c111_allocate_packet(smc91c111_state *s)
93
{
94
    int i;
95
    if (s->allocated == (1 << NUM_PACKETS) - 1) {
96
        return 0x80;
97
    }
98

    
99
    for (i = 0; i < NUM_PACKETS; i++) {
100
        if ((s->allocated & (1 << i)) == 0)
101
            break;
102
    }
103
    s->allocated |= 1 << i;
104
    return i;
105
}
106

    
107

    
108
/* Process a pending TX allocate.  */
109
static void smc91c111_tx_alloc(smc91c111_state *s)
110
{
111
    s->tx_alloc = smc91c111_allocate_packet(s);
112
    if (s->tx_alloc == 0x80)
113
        return;
114
    s->int_level |= INT_ALLOC;
115
    smc91c111_update(s);
116
}
117

    
118
/* Remove and item from the RX FIFO.  */
119
static void smc91c111_pop_rx_fifo(smc91c111_state *s)
120
{
121
    int i;
122

    
123
    s->rx_fifo_len--;
124
    if (s->rx_fifo_len) {
125
        for (i = 0; i < s->rx_fifo_len; i++)
126
            s->rx_fifo[i] = s->rx_fifo[i + 1];
127
        s->int_level |= INT_RCV;
128
    } else {
129
        s->int_level &= ~INT_RCV;
130
    }
131
    smc91c111_update(s);
132
}
133

    
134
/* Remove an item from the TX completion FIFO.  */
135
static void smc91c111_pop_tx_fifo_done(smc91c111_state *s)
136
{
137
    int i;
138

    
139
    if (s->tx_fifo_done_len == 0)
140
        return;
141
    s->tx_fifo_done_len--;
142
    for (i = 0; i < s->tx_fifo_done_len; i++)
143
        s->tx_fifo_done[i] = s->tx_fifo_done[i + 1];
144
}
145

    
146
/* Release the memory allocated to a packet.  */
147
static void smc91c111_release_packet(smc91c111_state *s, int packet)
148
{
149
    s->allocated &= ~(1 << packet);
150
    if (s->tx_alloc == 0x80)
151
        smc91c111_tx_alloc(s);
152
}
153

    
154
/* Flush the TX FIFO.  */
155
static void smc91c111_do_tx(smc91c111_state *s)
156
{
157
    int i;
158
    int len;
159
    int control;
160
    int add_crc;
161
    int packetnum;
162
    uint8_t *p;
163

    
164
    if ((s->tcr & TCR_TXEN) == 0)
165
        return;
166
    if (s->tx_fifo_len == 0)
167
        return;
168
    for (i = 0; i < s->tx_fifo_len; i++) {
169
        packetnum = s->tx_fifo[i];
170
        p = &s->data[packetnum][0];
171
        /* Set status word.  */
172
        *(p++) = 0x01;
173
        *(p++) = 0x40;
174
        len = *(p++);
175
        len |= ((int)*(p++)) << 8;
176
        len -= 6;
177
        control = p[len + 1];
178
        if (control & 0x20)
179
            len++;
180
        /* ??? This overwrites the data following the buffer.
181
           Don't know what real hardware does.  */
182
        if (len < 64 && (s->tcr & TCR_PAD_EN)) {
183
            memset(p + len, 0, 64 - len);
184
            len = 64;
185
        }
186
#if 0
187
        /* The card is supposed to append the CRC to the frame.  However
188
           none of the other network traffic has the CRC appended.
189
           Suspect this is low level ethernet detail we don't need to worry
190
           about.  */
191
        add_crc = (control & 0x10) || (s->tcr & TCR_NOCRC) == 0;
192
        if (add_crc) {
193
            uint32_t crc;
194

195
            crc = crc32(~0, p, len);
196
            memcpy(p + len, &crc, 4);
197
            len += 4;
198
        }
199
#else
200
        add_crc = 0;
201
#endif
202
        if (s->ctr & CTR_AUTO_RELEASE)
203
            /* Race?  */
204
            smc91c111_release_packet(s, packetnum);
205
        else if (s->tx_fifo_done_len < NUM_PACKETS)
206
            s->tx_fifo_done[s->tx_fifo_done_len++] = packetnum;
207
        qemu_send_packet(s->vc, p, len);
208
    }
209
    s->tx_fifo_len = 0;
210
    smc91c111_update(s);
211
}
212

    
213
/* Add a packet to the TX FIFO.  */
214
static void smc91c111_queue_tx(smc91c111_state *s, int packet)
215
{
216
    if (s->tx_fifo_len == NUM_PACKETS)
217
        return;
218
    s->tx_fifo[s->tx_fifo_len++] = packet;
219
    smc91c111_do_tx(s);
220
}
221

    
222
static void smc91c111_reset(smc91c111_state *s)
223
{
224
    s->bank = 0;
225
    s->tx_fifo_len = 0;
226
    s->tx_fifo_done_len = 0;
227
    s->rx_fifo_len = 0;
228
    s->allocated = 0;
229
    s->packet_num = 0;
230
    s->tx_alloc = 0;
231
    s->tcr = 0;
232
    s->rcr = 0;
233
    s->cr = 0xa0b1;
234
    s->ctr = 0x1210;
235
    s->ptr = 0;
236
    s->ercv = 0x1f;
237
    s->int_level = INT_TX_EMPTY;
238
    s->int_mask = 0;
239
    smc91c111_update(s);
240
}
241

    
242
#define SET_LOW(name, val) s->name = (s->name & 0xff00) | val
243
#define SET_HIGH(name, val) s->name = (s->name & 0xff) | (val << 8)
244

    
245
static void smc91c111_writeb(void *opaque, target_phys_addr_t offset,
246
                             uint32_t value)
247
{
248
    smc91c111_state *s = (smc91c111_state *)opaque;
249

    
250
    offset -= s->base;
251
    if (offset == 14) {
252
        s->bank = value;
253
        return;
254
    }
255
    if (offset == 15)
256
        return;
257
    switch (s->bank) {
258
    case 0:
259
        switch (offset) {
260
        case 0: /* TCR */
261
            SET_LOW(tcr, value);
262
            return;
263
        case 1:
264
            SET_HIGH(tcr, value);
265
            return;
266
        case 4: /* RCR */
267
            SET_LOW(rcr, value);
268
            return;
269
        case 5:
270
            SET_HIGH(rcr, value);
271
            if (s->rcr & RCR_SOFT_RST)
272
                smc91c111_reset(s);
273
            return;
274
        case 10: case 11: /* RPCR */
275
            /* Ignored */
276
            return;
277
        }
278
        break;
279

    
280
    case 1:
281
        switch (offset) {
282
        case 0: /* CONFIG */
283
            SET_LOW(cr, value);
284
            return;
285
        case 1:
286
            SET_HIGH(cr,value);
287
            return;
288
        case 2: case 3: /* BASE */
289
        case 4: case 5: case 6: case 7: case 8: case 9: /* IA */
290
            /* Not implemented.  */
291
            return;
292
        case 10: /* Genral Purpose */
293
            SET_LOW(gpr, value);
294
            return;
295
        case 11:
296
            SET_HIGH(gpr, value);
297
            return;
298
        case 12: /* Control */
299
            if (value & 1)
300
                fprintf(stderr, "smc91c111:EEPROM store not implemented\n");
301
            if (value & 2)
302
                fprintf(stderr, "smc91c111:EEPROM reload not implemented\n");
303
            value &= ~3;
304
            SET_LOW(ctr, value);
305
            return;
306
        case 13:
307
            SET_HIGH(ctr, value);
308
            return;
309
        }
310
        break;
311

    
312
    case 2:
313
        switch (offset) {
314
        case 0: /* MMU Command */
315
            switch (value >> 5) {
316
            case 0: /* no-op */
317
                break;
318
            case 1: /* Allocate for TX.  */
319
                s->tx_alloc = 0x80;
320
                s->int_level &= ~INT_ALLOC;
321
                smc91c111_update(s);
322
                smc91c111_tx_alloc(s);
323
                break;
324
            case 2: /* Reset MMU.  */
325
                s->allocated = 0;
326
                s->tx_fifo_len = 0;
327
                s->tx_fifo_done_len = 0;
328
                s->rx_fifo_len = 0;
329
                s->tx_alloc = 0;
330
                break;
331
            case 3: /* Remove from RX FIFO.  */
332
                smc91c111_pop_rx_fifo(s);
333
                break;
334
            case 4: /* Remove from RX FIFO and release.  */
335
                if (s->rx_fifo_len > 0) {
336
                    smc91c111_release_packet(s, s->rx_fifo[0]);
337
                }
338
                smc91c111_pop_rx_fifo(s);
339
                break;
340
            case 5: /* Release.  */
341
                smc91c111_release_packet(s, s->packet_num);
342
                break;
343
            case 6: /* Add to TX FIFO.  */
344
                smc91c111_queue_tx(s, s->packet_num);
345
                break;
346
            case 7: /* Reset TX FIFO.  */
347
                s->tx_fifo_len = 0;
348
                s->tx_fifo_done_len = 0;
349
                break;
350
            }
351
            return;
352
        case 1:
353
            /* Ignore.  */
354
            return;
355
        case 2: /* Packet Number Register */
356
            s->packet_num = value;
357
            return;
358
        case 3: case 4: case 5:
359
            /* Should be readonly, but linux writes to them anyway. Ignore.  */
360
            return;
361
        case 6: /* Pointer */
362
            SET_LOW(ptr, value);
363
            return;
364
        case 7:
365
            SET_HIGH(ptr, value);
366
            return;
367
        case 8: case 9: case 10: case 11: /* Data */
368
            {
369
                int p;
370
                int n;
371

    
372
                if (s->ptr & 0x8000)
373
                    n = s->rx_fifo[0];
374
                else
375
                    n = s->packet_num;
376
                p = s->ptr & 0x07ff;
377
                if (s->ptr & 0x4000) {
378
                    s->ptr = (s->ptr & 0xf800) | ((s->ptr + 1) & 0x7ff);
379
                } else {
380
                    p += (offset & 3);
381
                }
382
                s->data[n][p] = value;
383
            }
384
            return;
385
        case 12: /* Interrupt ACK.  */
386
            s->int_level &= ~(value & 0xd6);
387
            if (value & INT_TX)
388
                smc91c111_pop_tx_fifo_done(s);
389
            smc91c111_update(s);
390
            return;
391
        case 13: /* Interrupt mask.  */
392
            s->int_mask = value;
393
            smc91c111_update(s);
394
            return;
395
        }
396
        break;;
397

    
398
    case 3:
399
        switch (offset) {
400
        case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
401
            /* Multicast table.  */
402
            /* Not implemented.  */
403
            return;
404
        case 8: case 9: /* Management Interface.  */
405
            /* Not implemented.  */
406
            return;
407
        case 12: /* Early receive.  */
408
            s->ercv = value & 0x1f;
409
        case 13:
410
            /* Ignore.  */
411
            return;
412
        }
413
        break;
414
    }
415
    cpu_abort (cpu_single_env, "smc91c111_write: Bad reg %d:%x\n",
416
               s->bank, offset);
417
}
418

    
419
static uint32_t smc91c111_readb(void *opaque, target_phys_addr_t offset)
420
{
421
    smc91c111_state *s = (smc91c111_state *)opaque;
422

    
423
    offset -= s->base;
424
    if (offset == 14) {
425
        return s->bank;
426
    }
427
    if (offset == 15)
428
        return 0x33;
429
    switch (s->bank) {
430
    case 0:
431
        switch (offset) {
432
        case 0: /* TCR */
433
            return s->tcr & 0xff;
434
        case 1:
435
            return s->tcr >> 8;
436
        case 2: /* EPH Status */
437
            return 0;
438
        case 3:
439
            return 0x40;
440
        case 4: /* RCR */
441
            return s->rcr & 0xff;
442
        case 5:
443
            return s->rcr >> 8;
444
        case 6: /* Counter */
445
        case 7:
446
            /* Not implemented.  */
447
            return 0;
448
        case 8: /* Memory size.  */
449
            return NUM_PACKETS;
450
        case 9: /* Free memory available.  */
451
            {
452
                int i;
453
                int n;
454
                n = 0;
455
                for (i = 0; i < NUM_PACKETS; i++) {
456
                    if (s->allocated & (1 << i))
457
                        n++;
458
                }
459
                return n;
460
            }
461
        case 10: case 11: /* RPCR */
462
            /* Not implemented.  */
463
            return 0;
464
        }
465
        break;
466

    
467
    case 1:
468
        switch (offset) {
469
        case 0: /* CONFIG */
470
            return s->cr & 0xff;
471
        case 1:
472
            return s->cr >> 8;
473
        case 2: case 3: /* BASE */
474
            /* Not implemented.  */
475
            return 0;
476
        case 4: case 5: case 6: case 7: case 8: case 9: /* IA */
477
            return s->macaddr[offset - 4];
478
        case 10: /* General Purpose */
479
            return s->gpr & 0xff;
480
        case 11:
481
            return s->gpr >> 8;
482
        case 12: /* Control */
483
            return s->ctr & 0xff;
484
        case 13:
485
            return s->ctr >> 8;
486
        }
487
        break;
488

    
489
    case 2:
490
        switch (offset) {
491
        case 0: case 1: /* MMUCR Busy bit.  */
492
            return 0;
493
        case 2: /* Packet Number.  */
494
            return s->packet_num;
495
        case 3: /* Allocation Result.  */
496
            return s->tx_alloc;
497
        case 4: /* TX FIFO */
498
            if (s->tx_fifo_done_len == 0)
499
                return 0x80;
500
            else
501
                return s->tx_fifo_done[0];
502
        case 5: /* RX FIFO */
503
            if (s->rx_fifo_len == 0)
504
                return 0x80;
505
            else
506
                return s->rx_fifo[0];
507
        case 6: /* Pointer */
508
            return s->ptr & 0xff;
509
        case 7:
510
            return (s->ptr >> 8) & 0xf7;
511
        case 8: case 9: case 10: case 11: /* Data */
512
            {
513
                int p;
514
                int n;
515

    
516
                if (s->ptr & 0x8000)
517
                    n = s->rx_fifo[0];
518
                else
519
                    n = s->packet_num;
520
                p = s->ptr & 0x07ff;
521
                if (s->ptr & 0x4000) {
522
                    s->ptr = (s->ptr & 0xf800) | ((s->ptr + 1) & 0x07ff);
523
                } else {
524
                    p += (offset & 3);
525
                }
526
                return s->data[n][p];
527
            }
528
        case 12: /* Interrupt status.  */
529
            return s->int_level;
530
        case 13: /* Interrupt mask.  */
531
            return s->int_mask;
532
        }
533
        break;
534

    
535
    case 3:
536
        switch (offset) {
537
        case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
538
            /* Multicast table.  */
539
            /* Not implemented.  */
540
            return 0;
541
        case 8: /* Management Interface.  */
542
            /* Not implemented.  */
543
            return 0x30;
544
        case 9:
545
            return 0x33;
546
        case 10: /* Revision.  */
547
            return 0x91;
548
        case 11:
549
            return 0x33;
550
        case 12:
551
            return s->ercv;
552
        case 13:
553
            return 0;
554
        }
555
        break;
556
    }
557
    cpu_abort (cpu_single_env, "smc91c111_read: Bad reg %d:%x\n",
558
               s->bank, offset);
559
    return 0;
560
}
561

    
562
static void smc91c111_writew(void *opaque, target_phys_addr_t offset,
563
                             uint32_t value)
564
{
565
    smc91c111_writeb(opaque, offset, value & 0xff);
566
    smc91c111_writeb(opaque, offset + 1, value >> 8);
567
}
568

    
569
static void smc91c111_writel(void *opaque, target_phys_addr_t offset,
570
                             uint32_t value)
571
{
572
    smc91c111_state *s = (smc91c111_state *)opaque;
573
    /* 32-bit writes to offset 0xc only actually write to the bank select
574
       register (offset 0xe)  */
575
    if (offset != s->base + 0xc)
576
        smc91c111_writew(opaque, offset, value & 0xffff);
577
    smc91c111_writew(opaque, offset + 2, value >> 16);
578
}
579

    
580
static uint32_t smc91c111_readw(void *opaque, target_phys_addr_t offset)
581
{
582
    uint32_t val;
583
    val = smc91c111_readb(opaque, offset);
584
    val |= smc91c111_readb(opaque, offset + 1) << 8;
585
    return val;
586
}
587

    
588
static uint32_t smc91c111_readl(void *opaque, target_phys_addr_t offset)
589
{
590
    uint32_t val;
591
    val = smc91c111_readw(opaque, offset);
592
    val |= smc91c111_readw(opaque, offset + 2) << 16;
593
    return val;
594
}
595

    
596
static int smc91c111_can_receive(void *opaque)
597
{
598
    smc91c111_state *s = (smc91c111_state *)opaque;
599

    
600
    if ((s->rcr & RCR_RXEN) == 0 || (s->rcr & RCR_SOFT_RST))
601
        return 1;
602
    if (s->allocated == (1 << NUM_PACKETS) - 1)
603
        return 0;
604
    return 1;
605
}
606

    
607
static void smc91c111_receive(void *opaque, const uint8_t *buf, int size)
608
{
609
    smc91c111_state *s = (smc91c111_state *)opaque;
610
    int status;
611
    int packetsize;
612
    uint32_t crc;
613
    int packetnum;
614
    uint8_t *p;
615

    
616
    if ((s->rcr & RCR_RXEN) == 0 || (s->rcr & RCR_SOFT_RST))
617
        return;
618
    /* Short packets are padded with zeros.  Receiving a packet
619
       < 64 bytes long is considered an error condition.  */
620
    if (size < 64)
621
        packetsize = 64;
622
    else
623
        packetsize = (size & ~1);
624
    packetsize += 6;
625
    crc = (s->rcr & RCR_STRIP_CRC) == 0;
626
    if (crc)
627
        packetsize += 4;
628
    /* TODO: Flag overrun and receive errors.  */
629
    if (packetsize > 2048)
630
        return;
631
    packetnum = smc91c111_allocate_packet(s);
632
    if (packetnum == 0x80)
633
        return;
634
    s->rx_fifo[s->rx_fifo_len++] = packetnum;
635

    
636
    p = &s->data[packetnum][0];
637
    /* ??? Multicast packets?  */
638
    status = 0;
639
    if (size > 1518)
640
        status |= RS_TOOLONG;
641
    if (size & 1)
642
        status |= RS_ODDFRAME;
643
    *(p++) = status & 0xff;
644
    *(p++) = status >> 8;
645
    *(p++) = packetsize & 0xff;
646
    *(p++) = packetsize >> 8;
647
    memcpy(p, buf, size & ~1);
648
    p += (size & ~1);
649
    /* Pad short packets.  */
650
    if (size < 64) {
651
        int pad;
652
       
653
        if (size & 1)
654
            *(p++) = buf[size - 1];
655
        pad = 64 - size;
656
        memset(p, 0, pad);
657
        p += pad;
658
        size = 64;
659
    }
660
    /* It's not clear if the CRC should go before or after the last byte in
661
       odd sized packets.  Linux disables the CRC, so that's no help.
662
       The pictures in the documentation show the CRC aligned on a 16-bit
663
       boundary before the last odd byte, so that's what we do.  */
664
    if (crc) {
665
        crc = crc32(~0, buf, size);
666
        *(p++) = crc & 0xff; crc >>= 8;
667
        *(p++) = crc & 0xff; crc >>= 8;
668
        *(p++) = crc & 0xff; crc >>= 8;
669
        *(p++) = crc & 0xff; crc >>= 8;
670
    }
671
    if (size & 1) {
672
        *(p++) = buf[size - 1];
673
        *(p++) = 0x60;
674
    } else {
675
        *(p++) = 0;
676
        *(p++) = 0x40;
677
    }
678
    /* TODO: Raise early RX interrupt?  */
679
    s->int_level |= INT_RCV;
680
    smc91c111_update(s);
681
}
682

    
683
static CPUReadMemoryFunc *smc91c111_readfn[] = {
684
    smc91c111_readb,
685
    smc91c111_readw,
686
    smc91c111_readl
687
};
688

    
689
static CPUWriteMemoryFunc *smc91c111_writefn[] = {
690
    smc91c111_writeb,
691
    smc91c111_writew,
692
    smc91c111_writel
693
};
694

    
695
void smc91c111_init(NICInfo *nd, uint32_t base, qemu_irq irq)
696
{
697
    smc91c111_state *s;
698
    int iomemtype;
699

    
700
    s = (smc91c111_state *)qemu_mallocz(sizeof(smc91c111_state));
701
    iomemtype = cpu_register_io_memory(0, smc91c111_readfn,
702
                                       smc91c111_writefn, s);
703
    cpu_register_physical_memory(base, 16, iomemtype);
704
    s->base = base;
705
    s->irq = irq;
706
    memcpy(s->macaddr, nd->macaddr, 6);
707

    
708
    smc91c111_reset(s);
709

    
710
    s->vc = qemu_new_vlan_client(nd->vlan, smc91c111_receive,
711
                                 smc91c111_can_receive, s);
712
    /* ??? Save/restore.  */
713
}