Statistics
| Branch: | Revision:

root / hw / smc91c111.c @ 87ecb68b

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 "hw.h"
11
#include "net.h"
12
#include "devices.h"
13
/* For crc32 */
14
#include <zlib.h>
15

    
16
/* Number of 2k memory pages available.  */
17
#define NUM_PACKETS 4
18

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

    
48
#define RCR_SOFT_RST  0x8000
49
#define RCR_STRIP_CRC 0x0200
50
#define RCR_RXEN      0x0100
51

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

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

    
68
#define CTR_AUTO_RELEASE  0x0800
69
#define CTR_RELOAD        0x0002
70
#define CTR_STORE         0x0001
71

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

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

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

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

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

    
109

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
598
static int smc91c111_can_receive(void *opaque)
599
{
600
    smc91c111_state *s = (smc91c111_state *)opaque;
601

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

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

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

    
638
    p = &s->data[packetnum][0];
639
    /* ??? Multicast packets?  */
640
    status = 0;
641
    if (size > 1518)
642
        status |= RS_TOOLONG;
643
    if (size & 1)
644
        status |= RS_ODDFRAME;
645
    *(p++) = status & 0xff;
646
    *(p++) = status >> 8;
647
    *(p++) = packetsize & 0xff;
648
    *(p++) = packetsize >> 8;
649
    memcpy(p, buf, size & ~1);
650
    p += (size & ~1);
651
    /* Pad short packets.  */
652
    if (size < 64) {
653
        int pad;
654

    
655
        if (size & 1)
656
            *(p++) = buf[size - 1];
657
        pad = 64 - size;
658
        memset(p, 0, pad);
659
        p += pad;
660
        size = 64;
661
    }
662
    /* It's not clear if the CRC should go before or after the last byte in
663
       odd sized packets.  Linux disables the CRC, so that's no help.
664
       The pictures in the documentation show the CRC aligned on a 16-bit
665
       boundary before the last odd byte, so that's what we do.  */
666
    if (crc) {
667
        crc = crc32(~0, buf, size);
668
        *(p++) = crc & 0xff; crc >>= 8;
669
        *(p++) = crc & 0xff; crc >>= 8;
670
        *(p++) = crc & 0xff; crc >>= 8;
671
        *(p++) = crc & 0xff; crc >>= 8;
672
    }
673
    if (size & 1) {
674
        *(p++) = buf[size - 1];
675
        *(p++) = 0x60;
676
    } else {
677
        *(p++) = 0;
678
        *(p++) = 0x40;
679
    }
680
    /* TODO: Raise early RX interrupt?  */
681
    s->int_level |= INT_RCV;
682
    smc91c111_update(s);
683
}
684

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

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

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

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

    
710
    smc91c111_reset(s);
711

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