Win32: Reduce section alignment for Windows.
[armpft.git] / hw / smc91c111.c
blobcf8d864e5b915e521e3c5741f4e05c41658dce08
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 "sysbus.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 SysBusDevice busdev;
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 int mmio_index;
47 } smc91c111_state;
49 #define RCR_SOFT_RST 0x8000
50 #define RCR_STRIP_CRC 0x0200
51 #define RCR_RXEN 0x0100
53 #define TCR_EPH_LOOP 0x2000
54 #define TCR_NOCRC 0x0100
55 #define TCR_PAD_EN 0x0080
56 #define TCR_FORCOL 0x0004
57 #define TCR_LOOP 0x0002
58 #define TCR_TXEN 0x0001
60 #define INT_MD 0x80
61 #define INT_ERCV 0x40
62 #define INT_EPH 0x20
63 #define INT_RX_OVRN 0x10
64 #define INT_ALLOC 0x08
65 #define INT_TX_EMPTY 0x04
66 #define INT_TX 0x02
67 #define INT_RCV 0x01
69 #define CTR_AUTO_RELEASE 0x0800
70 #define CTR_RELOAD 0x0002
71 #define CTR_STORE 0x0001
73 #define RS_ALGNERR 0x8000
74 #define RS_BRODCAST 0x4000
75 #define RS_BADCRC 0x2000
76 #define RS_ODDFRAME 0x1000
77 #define RS_TOOLONG 0x0800
78 #define RS_TOOSHORT 0x0400
79 #define RS_MULTICAST 0x0001
81 /* Update interrupt status. */
82 static void smc91c111_update(smc91c111_state *s)
84 int level;
86 if (s->tx_fifo_len == 0)
87 s->int_level |= INT_TX_EMPTY;
88 if (s->tx_fifo_done_len != 0)
89 s->int_level |= INT_TX;
90 level = (s->int_level & s->int_mask) != 0;
91 qemu_set_irq(s->irq, level);
94 /* Try to allocate a packet. Returns 0x80 on failure. */
95 static int smc91c111_allocate_packet(smc91c111_state *s)
97 int i;
98 if (s->allocated == (1 << NUM_PACKETS) - 1) {
99 return 0x80;
102 for (i = 0; i < NUM_PACKETS; i++) {
103 if ((s->allocated & (1 << i)) == 0)
104 break;
106 s->allocated |= 1 << i;
107 return i;
111 /* Process a pending TX allocate. */
112 static void smc91c111_tx_alloc(smc91c111_state *s)
114 s->tx_alloc = smc91c111_allocate_packet(s);
115 if (s->tx_alloc == 0x80)
116 return;
117 s->int_level |= INT_ALLOC;
118 smc91c111_update(s);
121 /* Remove and item from the RX FIFO. */
122 static void smc91c111_pop_rx_fifo(smc91c111_state *s)
124 int i;
126 s->rx_fifo_len--;
127 if (s->rx_fifo_len) {
128 for (i = 0; i < s->rx_fifo_len; i++)
129 s->rx_fifo[i] = s->rx_fifo[i + 1];
130 s->int_level |= INT_RCV;
131 } else {
132 s->int_level &= ~INT_RCV;
134 smc91c111_update(s);
137 /* Remove an item from the TX completion FIFO. */
138 static void smc91c111_pop_tx_fifo_done(smc91c111_state *s)
140 int i;
142 if (s->tx_fifo_done_len == 0)
143 return;
144 s->tx_fifo_done_len--;
145 for (i = 0; i < s->tx_fifo_done_len; i++)
146 s->tx_fifo_done[i] = s->tx_fifo_done[i + 1];
149 /* Release the memory allocated to a packet. */
150 static void smc91c111_release_packet(smc91c111_state *s, int packet)
152 s->allocated &= ~(1 << packet);
153 if (s->tx_alloc == 0x80)
154 smc91c111_tx_alloc(s);
157 /* Flush the TX FIFO. */
158 static void smc91c111_do_tx(smc91c111_state *s)
160 int i;
161 int len;
162 int control;
163 int add_crc;
164 int packetnum;
165 uint8_t *p;
167 if ((s->tcr & TCR_TXEN) == 0)
168 return;
169 if (s->tx_fifo_len == 0)
170 return;
171 for (i = 0; i < s->tx_fifo_len; i++) {
172 packetnum = s->tx_fifo[i];
173 p = &s->data[packetnum][0];
174 /* Set status word. */
175 *(p++) = 0x01;
176 *(p++) = 0x40;
177 len = *(p++);
178 len |= ((int)*(p++)) << 8;
179 len -= 6;
180 control = p[len + 1];
181 if (control & 0x20)
182 len++;
183 /* ??? This overwrites the data following the buffer.
184 Don't know what real hardware does. */
185 if (len < 64 && (s->tcr & TCR_PAD_EN)) {
186 memset(p + len, 0, 64 - len);
187 len = 64;
189 #if 0
190 /* The card is supposed to append the CRC to the frame. However
191 none of the other network traffic has the CRC appended.
192 Suspect this is low level ethernet detail we don't need to worry
193 about. */
194 add_crc = (control & 0x10) || (s->tcr & TCR_NOCRC) == 0;
195 if (add_crc) {
196 uint32_t crc;
198 crc = crc32(~0, p, len);
199 memcpy(p + len, &crc, 4);
200 len += 4;
202 #else
203 add_crc = 0;
204 #endif
205 if (s->ctr & CTR_AUTO_RELEASE)
206 /* Race? */
207 smc91c111_release_packet(s, packetnum);
208 else if (s->tx_fifo_done_len < NUM_PACKETS)
209 s->tx_fifo_done[s->tx_fifo_done_len++] = packetnum;
210 qemu_send_packet(s->vc, p, len);
212 s->tx_fifo_len = 0;
213 smc91c111_update(s);
216 /* Add a packet to the TX FIFO. */
217 static void smc91c111_queue_tx(smc91c111_state *s, int packet)
219 if (s->tx_fifo_len == NUM_PACKETS)
220 return;
221 s->tx_fifo[s->tx_fifo_len++] = packet;
222 smc91c111_do_tx(s);
225 static void smc91c111_reset(smc91c111_state *s)
227 s->bank = 0;
228 s->tx_fifo_len = 0;
229 s->tx_fifo_done_len = 0;
230 s->rx_fifo_len = 0;
231 s->allocated = 0;
232 s->packet_num = 0;
233 s->tx_alloc = 0;
234 s->tcr = 0;
235 s->rcr = 0;
236 s->cr = 0xa0b1;
237 s->ctr = 0x1210;
238 s->ptr = 0;
239 s->ercv = 0x1f;
240 s->int_level = INT_TX_EMPTY;
241 s->int_mask = 0;
242 smc91c111_update(s);
245 #define SET_LOW(name, val) s->name = (s->name & 0xff00) | val
246 #define SET_HIGH(name, val) s->name = (s->name & 0xff) | (val << 8)
248 static void smc91c111_writeb(void *opaque, target_phys_addr_t offset,
249 uint32_t value)
251 smc91c111_state *s = (smc91c111_state *)opaque;
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 hw_error("smc91c111_write: Bad reg %d:%x\n", s->bank, (int)offset);
420 static uint32_t smc91c111_readb(void *opaque, target_phys_addr_t offset)
422 smc91c111_state *s = (smc91c111_state *)opaque;
424 if (offset == 14) {
425 return s->bank;
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. */
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++;
459 return n;
461 case 10: case 11: /* RPCR */
462 /* Not implemented. */
463 return 0;
465 break;
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;
487 break;
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 */
513 int p;
514 int n;
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);
526 return s->data[n][p];
528 case 12: /* Interrupt status. */
529 return s->int_level;
530 case 13: /* Interrupt mask. */
531 return s->int_mask;
533 break;
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;
555 break;
557 hw_error("smc91c111_read: Bad reg %d:%x\n", s->bank, (int)offset);
558 return 0;
561 static void smc91c111_writew(void *opaque, target_phys_addr_t offset,
562 uint32_t value)
564 smc91c111_writeb(opaque, offset, value & 0xff);
565 smc91c111_writeb(opaque, offset + 1, value >> 8);
568 static void smc91c111_writel(void *opaque, target_phys_addr_t offset,
569 uint32_t value)
571 /* 32-bit writes to offset 0xc only actually write to the bank select
572 register (offset 0xe) */
573 if (offset != 0xc)
574 smc91c111_writew(opaque, offset, value & 0xffff);
575 smc91c111_writew(opaque, offset + 2, value >> 16);
578 static uint32_t smc91c111_readw(void *opaque, target_phys_addr_t offset)
580 uint32_t val;
581 val = smc91c111_readb(opaque, offset);
582 val |= smc91c111_readb(opaque, offset + 1) << 8;
583 return val;
586 static uint32_t smc91c111_readl(void *opaque, target_phys_addr_t offset)
588 uint32_t val;
589 val = smc91c111_readw(opaque, offset);
590 val |= smc91c111_readw(opaque, offset + 2) << 16;
591 return val;
594 static int smc91c111_can_receive(VLANClientState *vc)
596 smc91c111_state *s = vc->opaque;
598 if ((s->rcr & RCR_RXEN) == 0 || (s->rcr & RCR_SOFT_RST))
599 return 1;
600 if (s->allocated == (1 << NUM_PACKETS) - 1)
601 return 0;
602 return 1;
605 static ssize_t smc91c111_receive(VLANClientState *vc, const uint8_t *buf, size_t size)
607 smc91c111_state *s = vc->opaque;
608 int status;
609 int packetsize;
610 uint32_t crc;
611 int packetnum;
612 uint8_t *p;
614 if ((s->rcr & RCR_RXEN) == 0 || (s->rcr & RCR_SOFT_RST))
615 return -1;
616 /* Short packets are padded with zeros. Receiving a packet
617 < 64 bytes long is considered an error condition. */
618 if (size < 64)
619 packetsize = 64;
620 else
621 packetsize = (size & ~1);
622 packetsize += 6;
623 crc = (s->rcr & RCR_STRIP_CRC) == 0;
624 if (crc)
625 packetsize += 4;
626 /* TODO: Flag overrun and receive errors. */
627 if (packetsize > 2048)
628 return -1;
629 packetnum = smc91c111_allocate_packet(s);
630 if (packetnum == 0x80)
631 return -1;
632 s->rx_fifo[s->rx_fifo_len++] = packetnum;
634 p = &s->data[packetnum][0];
635 /* ??? Multicast packets? */
636 status = 0;
637 if (size > 1518)
638 status |= RS_TOOLONG;
639 if (size & 1)
640 status |= RS_ODDFRAME;
641 *(p++) = status & 0xff;
642 *(p++) = status >> 8;
643 *(p++) = packetsize & 0xff;
644 *(p++) = packetsize >> 8;
645 memcpy(p, buf, size & ~1);
646 p += (size & ~1);
647 /* Pad short packets. */
648 if (size < 64) {
649 int pad;
651 if (size & 1)
652 *(p++) = buf[size - 1];
653 pad = 64 - size;
654 memset(p, 0, pad);
655 p += pad;
656 size = 64;
658 /* It's not clear if the CRC should go before or after the last byte in
659 odd sized packets. Linux disables the CRC, so that's no help.
660 The pictures in the documentation show the CRC aligned on a 16-bit
661 boundary before the last odd byte, so that's what we do. */
662 if (crc) {
663 crc = crc32(~0, buf, size);
664 *(p++) = crc & 0xff; crc >>= 8;
665 *(p++) = crc & 0xff; crc >>= 8;
666 *(p++) = crc & 0xff; crc >>= 8;
667 *(p++) = crc & 0xff; crc >>= 8;
669 if (size & 1) {
670 *(p++) = buf[size - 1];
671 *(p++) = 0x60;
672 } else {
673 *(p++) = 0;
674 *(p++) = 0x40;
676 /* TODO: Raise early RX interrupt? */
677 s->int_level |= INT_RCV;
678 smc91c111_update(s);
680 return size;
683 static CPUReadMemoryFunc *smc91c111_readfn[] = {
684 smc91c111_readb,
685 smc91c111_readw,
686 smc91c111_readl
689 static CPUWriteMemoryFunc *smc91c111_writefn[] = {
690 smc91c111_writeb,
691 smc91c111_writew,
692 smc91c111_writel
695 static void smc91c111_cleanup(VLANClientState *vc)
697 smc91c111_state *s = vc->opaque;
699 cpu_unregister_io_memory(s->mmio_index);
700 qemu_free(s);
703 static void smc91c111_init1(SysBusDevice *dev)
705 smc91c111_state *s = FROM_SYSBUS(smc91c111_state, dev);
707 s->mmio_index = cpu_register_io_memory(smc91c111_readfn,
708 smc91c111_writefn, s);
709 sysbus_init_mmio(dev, 16, s->mmio_index);
710 sysbus_init_irq(dev, &s->irq);
711 qdev_get_macaddr(&dev->qdev, s->macaddr);
713 smc91c111_reset(s);
715 s->vc = qdev_get_vlan_client(&dev->qdev,
716 smc91c111_can_receive, smc91c111_receive, NULL,
717 smc91c111_cleanup, s);
718 qemu_format_nic_info_str(s->vc, s->macaddr);
719 /* ??? Save/restore. */
722 static void smc91c111_register_devices(void)
724 sysbus_register_dev("smc91c111", sizeof(smc91c111_state), smc91c111_init1);
727 /* Legacy helper function. Should go away when machine config files are
728 implemented. */
729 void smc91c111_init(NICInfo *nd, uint32_t base, qemu_irq irq)
731 DeviceState *dev;
732 SysBusDevice *s;
734 qemu_check_nic_model(nd, "smc91c111");
735 dev = qdev_create(NULL, "smc91c111");
736 qdev_set_netdev(dev, nd);
737 qdev_init(dev);
738 s = sysbus_from_qdev(dev);
739 sysbus_mmio_map(s, 0, base);
740 sysbus_connect_irq(s, 0, irq);
743 device_init(smc91c111_register_devices)