Rename hz to hertz to keep AIX happy
[qemu/mini2440.git] / hw / smc91c111.c
blob410051d3ccc80e047a257163a9a568f7ceb2c4e3
1 /*
2 * SMSC 91C111 Ethernet interface emulation
4 * Copyright (c) 2005 CodeSourcery, LLC.
5 * Written by Paul Brook
7 * This code is licenced under the GPL
8 */
10 #include "hw.h"
11 #include "net.h"
12 #include "devices.h"
13 /* For crc32 */
14 #include <zlib.h>
16 /* Number of 2k memory pages available. */
17 #define NUM_PACKETS 4
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;
48 #define RCR_SOFT_RST 0x8000
49 #define RCR_STRIP_CRC 0x0200
50 #define RCR_RXEN 0x0100
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
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
68 #define CTR_AUTO_RELEASE 0x0800
69 #define CTR_RELOAD 0x0002
70 #define CTR_STORE 0x0001
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
80 /* Update interrupt status. */
81 static void smc91c111_update(smc91c111_state *s)
83 int level;
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);
93 /* Try to allocate a packet. Returns 0x80 on failure. */
94 static int smc91c111_allocate_packet(smc91c111_state *s)
96 int i;
97 if (s->allocated == (1 << NUM_PACKETS) - 1) {
98 return 0x80;
101 for (i = 0; i < NUM_PACKETS; i++) {
102 if ((s->allocated & (1 << i)) == 0)
103 break;
105 s->allocated |= 1 << i;
106 return i;
110 /* Process a pending TX allocate. */
111 static void smc91c111_tx_alloc(smc91c111_state *s)
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);
120 /* Remove and item from the RX FIFO. */
121 static void smc91c111_pop_rx_fifo(smc91c111_state *s)
123 int i;
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;
133 smc91c111_update(s);
136 /* Remove an item from the TX completion FIFO. */
137 static void smc91c111_pop_tx_fifo_done(smc91c111_state *s)
139 int i;
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];
148 /* Release the memory allocated to a packet. */
149 static void smc91c111_release_packet(smc91c111_state *s, int packet)
151 s->allocated &= ~(1 << packet);
152 if (s->tx_alloc == 0x80)
153 smc91c111_tx_alloc(s);
156 /* Flush the TX FIFO. */
157 static void smc91c111_do_tx(smc91c111_state *s)
159 int i;
160 int len;
161 int control;
162 int add_crc;
163 int packetnum;
164 uint8_t *p;
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;
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;
197 crc = crc32(~0, p, len);
198 memcpy(p + len, &crc, 4);
199 len += 4;
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);
211 s->tx_fifo_len = 0;
212 smc91c111_update(s);
215 /* Add a packet to the TX FIFO. */
216 static void smc91c111_queue_tx(smc91c111_state *s, int packet)
218 if (s->tx_fifo_len == NUM_PACKETS)
219 return;
220 s->tx_fifo[s->tx_fifo_len++] = packet;
221 smc91c111_do_tx(s);
224 static void smc91c111_reset(smc91c111_state *s)
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);
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)
247 static void smc91c111_writeb(void *opaque, target_phys_addr_t offset,
248 uint32_t value)
250 smc91c111_state *s = (smc91c111_state *)opaque;
252 offset -= s->base;
253 if (offset == 14) {
254 s->bank = value;
255 return;
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;
280 break;
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;
312 break;
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]);
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;
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 */
371 int p;
372 int n;
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);
384 s->data[n][p] = value;
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;
398 break;;
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;
415 break;
417 cpu_abort (cpu_single_env, "smc91c111_write: Bad reg %d:%x\n",
418 s->bank, (int)offset);
421 static uint32_t smc91c111_readb(void *opaque, target_phys_addr_t offset)
423 smc91c111_state *s = (smc91c111_state *)opaque;
425 offset -= s->base;
426 if (offset == 14) {
427 return s->bank;
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. */
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++;
461 return n;
463 case 10: case 11: /* RPCR */
464 /* Not implemented. */
465 return 0;
467 break;
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;
489 break;
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 */
515 int p;
516 int n;
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);
528 return s->data[n][p];
530 case 12: /* Interrupt status. */
531 return s->int_level;
532 case 13: /* Interrupt mask. */
533 return s->int_mask;
535 break;
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;
557 break;
559 cpu_abort (cpu_single_env, "smc91c111_read: Bad reg %d:%x\n",
560 s->bank, (int)offset);
561 return 0;
564 static void smc91c111_writew(void *opaque, target_phys_addr_t offset,
565 uint32_t value)
567 smc91c111_writeb(opaque, offset, value & 0xff);
568 smc91c111_writeb(opaque, offset + 1, value >> 8);
571 static void smc91c111_writel(void *opaque, target_phys_addr_t offset,
572 uint32_t value)
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);
582 static uint32_t smc91c111_readw(void *opaque, target_phys_addr_t offset)
584 uint32_t val;
585 val = smc91c111_readb(opaque, offset);
586 val |= smc91c111_readb(opaque, offset + 1) << 8;
587 return val;
590 static uint32_t smc91c111_readl(void *opaque, target_phys_addr_t offset)
592 uint32_t val;
593 val = smc91c111_readw(opaque, offset);
594 val |= smc91c111_readw(opaque, offset + 2) << 16;
595 return val;
598 static int smc91c111_can_receive(void *opaque)
600 smc91c111_state *s = (smc91c111_state *)opaque;
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;
609 static void smc91c111_receive(void *opaque, const uint8_t *buf, int size)
611 smc91c111_state *s = (smc91c111_state *)opaque;
612 int status;
613 int packetsize;
614 uint32_t crc;
615 int packetnum;
616 uint8_t *p;
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;
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;
655 if (size & 1)
656 *(p++) = buf[size - 1];
657 pad = 64 - size;
658 memset(p, 0, pad);
659 p += pad;
660 size = 64;
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;
673 if (size & 1) {
674 *(p++) = buf[size - 1];
675 *(p++) = 0x60;
676 } else {
677 *(p++) = 0;
678 *(p++) = 0x40;
680 /* TODO: Raise early RX interrupt? */
681 s->int_level |= INT_RCV;
682 smc91c111_update(s);
685 static CPUReadMemoryFunc *smc91c111_readfn[] = {
686 smc91c111_readb,
687 smc91c111_readw,
688 smc91c111_readl
691 static CPUWriteMemoryFunc *smc91c111_writefn[] = {
692 smc91c111_writeb,
693 smc91c111_writew,
694 smc91c111_writel
697 void smc91c111_init(NICInfo *nd, uint32_t base, qemu_irq irq)
699 smc91c111_state *s;
700 int iomemtype;
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);
710 smc91c111_reset(s);
712 s->vc = qemu_new_vlan_client(nd->vlan, smc91c111_receive,
713 smc91c111_can_receive, s);
714 /* ??? Save/restore. */