Statistics
| Branch: | Revision:

root / hw / smc91c111.c @ 4870852c

History | View | Annotate | Download (19.1 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
    VLANClientState *vc;
21
    uint16_t tcr;
22
    uint16_t rcr;
23
    uint16_t cr;
24
    uint16_t ctr;
25
    uint16_t gpr;
26
    uint16_t ptr;
27
    uint16_t ercv;
28
    qemu_irq irq;
29
    int bank;
30
    int packet_num;
31
    int tx_alloc;
32
    /* Bitmask of allocated packets.  */
33
    int allocated;
34
    int tx_fifo_len;
35
    int tx_fifo[NUM_PACKETS];
36
    int rx_fifo_len;
37
    int rx_fifo[NUM_PACKETS];
38
    int tx_fifo_done_len;
39
    int tx_fifo_done[NUM_PACKETS];
40
    /* Packet buffer memory.  */
41
    uint8_t data[NUM_PACKETS][2048];
42
    uint8_t int_level;
43
    uint8_t int_mask;
44
    uint8_t macaddr[6];
45
    int mmio_index;
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
    if (offset == 14) {
253
        s->bank = value;
254
        return;
255
    }
256
    if (offset == 15)
257
        return;
258
    switch (s->bank) {
259
    case 0:
260
        switch (offset) {
261
        case 0: /* TCR */
262
            SET_LOW(tcr, value);
263
            return;
264
        case 1:
265
            SET_HIGH(tcr, value);
266
            return;
267
        case 4: /* RCR */
268
            SET_LOW(rcr, value);
269
            return;
270
        case 5:
271
            SET_HIGH(rcr, value);
272
            if (s->rcr & RCR_SOFT_RST)
273
                smc91c111_reset(s);
274
            return;
275
        case 10: case 11: /* RPCR */
276
            /* Ignored */
277
            return;
278
        }
279
        break;
280

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

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

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

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

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

    
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, (int)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
    /* 32-bit writes to offset 0xc only actually write to the bank select
573
       register (offset 0xe)  */
574
    if (offset != 0xc)
575
        smc91c111_writew(opaque, offset, value & 0xffff);
576
    smc91c111_writew(opaque, offset + 2, value >> 16);
577
}
578

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

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

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

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

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

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

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

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

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

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

    
694
static void smc91c111_cleanup(VLANClientState *vc)
695
{
696
    smc91c111_state *s = vc->opaque;
697

    
698
    cpu_unregister_io_memory(s->mmio_index);
699
    qemu_free(s);
700
}
701

    
702
void smc91c111_init(NICInfo *nd, uint32_t base, qemu_irq irq)
703
{
704
    smc91c111_state *s;
705

    
706
    qemu_check_nic_model(nd, "smc91c111");
707

    
708
    s = (smc91c111_state *)qemu_mallocz(sizeof(smc91c111_state));
709
    s->mmio_index = cpu_register_io_memory(0, smc91c111_readfn,
710
                                           smc91c111_writefn, s);
711
    cpu_register_physical_memory(base, 16, s->mmio_index);
712
    s->irq = irq;
713
    memcpy(s->macaddr, nd->macaddr, 6);
714

    
715
    smc91c111_reset(s);
716

    
717
    s->vc = qemu_new_vlan_client(nd->vlan, nd->model, nd->name,
718
                                 smc91c111_receive, smc91c111_can_receive,
719
                                 smc91c111_cleanup, s);
720
    qemu_format_nic_info_str(s->vc, s->macaddr);
721
    /* ??? Save/restore.  */
722
}