]> git.proxmox.com Git - mirror_qemu.git/blame - hw/smc91c111.c
cpu_reset() fix (Paul Brook)
[mirror_qemu.git] / hw / smc91c111.c
CommitLineData
80337b66
FB
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
17typedef 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 void *pic;
28 int 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 /* Packet buffer memory. */
39 uint8_t data[2048][NUM_PACKETS];
40 uint8_t int_level;
41 uint8_t int_mask;
42 uint8_t macaddr[6];
43} smc91c111_state;
44
45#define RCR_SOFT_RST 0x8000
46#define RCR_STRIP_CRC 0x0200
47#define RCR_RXEN 0x0100
48
49#define TCR_EPH_LOOP 0x2000
50#define TCR_NOCRC 0x0100
51#define TCR_PAD_EN 0x0080
52#define TCR_FORCOL 0x0004
53#define TCR_LOOP 0x0002
54#define TCR_TXEN 0x0001
55
56#define INT_MD 0x80
57#define INT_ERCV 0x40
58#define INT_EPH 0x20
59#define INT_RX_OVRN 0x10
60#define INT_ALLOC 0x08
61#define INT_TX_EMPTY 0x04
62#define INT_TX 0x02
63#define INT_RCV 0x01
64
65#define CTR_AUTO_RELEASE 0x0800
66#define CTR_RELOAD 0x0002
67#define CTR_STORE 0x0001
68
69#define RS_ALGNERR 0x8000
70#define RS_BRODCAST 0x4000
71#define RS_BADCRC 0x2000
72#define RS_ODDFRAME 0x1000
73#define RS_TOOLONG 0x0800
74#define RS_TOOSHORT 0x0400
75#define RS_MULTICAST 0x0001
76
77/* Update interrupt status. */
78static void smc91c111_update(smc91c111_state *s)
79{
80 int level;
81
82 if (s->tx_fifo_len == 0)
83 s->int_level |= INT_TX_EMPTY;
84 level = (s->int_level & s->int_mask) != 0;
85 pic_set_irq_new(s->pic, s->irq, level);
86}
87
88/* Try to allocate a packet. Returns 0x80 on failure. */
89static int smc91c111_allocate_packet(smc91c111_state *s)
90{
91 int i;
92 if (s->allocated == (1 << NUM_PACKETS) - 1) {
93 return 0x80;
94 }
95
96 for (i = 0; i < NUM_PACKETS; i++) {
97 if ((s->allocated & (1 << i)) == 0)
98 break;
99 }
100 s->allocated |= 1 << i;
101 return i;
102}
103
104
105/* Process a pending TX allocate. */
106static void smc91c111_tx_alloc(smc91c111_state *s)
107{
108 s->tx_alloc = smc91c111_allocate_packet(s);
109 if (s->tx_alloc == 0x80)
110 return;
111 s->int_level |= INT_ALLOC;
112 smc91c111_update(s);
113}
114
115/* Remove and item from the RX FIFO. */
116static void smc91c111_pop_rx_fifo(smc91c111_state *s)
117{
118 int i;
119
120 s->rx_fifo_len--;
121 if (s->rx_fifo_len) {
122 for (i = 0; i < s->rx_fifo_len; i++)
123 s->rx_fifo[i] = s->rx_fifo[i + 1];
124 s->int_level |= INT_RCV;
125 } else {
126 s->int_level &= ~INT_RCV;
127 }
128 smc91c111_update(s);
129}
130
131/* Release the memory allocated to a packet. */
132static void smc91c111_release_packet(smc91c111_state *s, int packet)
133{
134 s->allocated &= ~(1 << packet);
135 if (s->tx_alloc == 0x80)
136 smc91c111_tx_alloc(s);
137}
138
139/* Flush the TX FIFO. */
140static void smc91c111_do_tx(smc91c111_state *s)
141{
142 int i;
143 int len;
144 int control;
145 int add_crc;
146 uint32_t crc;
147 int packetnum;
148 uint8_t *p;
149
150 if ((s->tcr & TCR_TXEN) == 0)
151 return;
152 if (s->tx_fifo_len == 0)
153 return;
154 for (i = 0; i < s->tx_fifo_len; i++) {
155 packetnum = s->tx_fifo[i];
156 p = &s->data[packetnum][0];
157 /* Set status word. */
158 *(p++) = 0x01;
159 *(p++) = 0x40;
160 len = *(p++);
161 len |= ((int)*(p++)) << 8;
162 len -= 6;
163 control = p[len + 1];
164 if (control & 0x20)
165 len++;
166 /* ??? This overwrites the data following the buffer.
167 Don't know what real hardware does. */
168 if (len < 64 && (s->tcr & TCR_PAD_EN)) {
169 memset(p + len, 0, 64 - len);
170 len = 64;
171 }
172#if 0
173 /* The card is supposed to append the CRC to the frame. However
174 none of the other network traffic has the CRC appended.
175 Suspect this is low level ethernet detail we don't need to worry
176 about. */
177 add_crc = (control & 0x10) || (s->tcr & TCR_NOCRC) == 0;
178 if (add_crc) {
179 crc = crc32(~0, p, len);
180 memcpy(p + len, &crc, 4);
181 len += 4;
182 }
183#else
184 add_crc = 0;
185#endif
186 if (s->ctr & CTR_AUTO_RELEASE)
187 smc91c111_release_packet(s, packetnum);
188 qemu_send_packet(s->vc, p, len);
189 }
190 s->tx_fifo_len = 0;
191 if ((s->ctr & CTR_AUTO_RELEASE) == 0)
192 s->int_level |= INT_TX;
193 smc91c111_update(s);
194}
195
196/* Add a packet to the TX FIFO. */
197static void smc91c111_queue_tx(smc91c111_state *s, int packet)
198{
199 if (s->tx_fifo_len == NUM_PACKETS)
200 return;
201 s->tx_fifo[s->tx_fifo_len++] = packet;
202 smc91c111_do_tx(s);
203}
204
205static void smc91c111_reset(smc91c111_state *s)
206{
207 s->bank = 0;
208 s->tx_fifo_len = 0;
209 s->rx_fifo_len = 0;
210 s->allocated = 0;
211 s->packet_num = 0;
212 s->tx_alloc = 0;
213 s->tcr = 0;
214 s->rcr = 0;
215 s->cr = 0xa0b1;
216 s->ctr = 0x1210;
217 s->ptr = 0;
218 s->ercv = 0x1f;
219 s->int_level = INT_TX_EMPTY;
220 s->int_mask = 0;
221 smc91c111_update(s);
222}
223
224#define SET_LOW(name, val) s->name = (s->name & 0xff00) | val
225#define SET_HIGH(name, val) s->name = (s->name & 0xff) | (val << 8)
226
227static void smc91c111_writeb(void *opaque, target_phys_addr_t offset,
228 uint32_t value)
229{
230 smc91c111_state *s = (smc91c111_state *)opaque;
231
232 offset -= s->base;
233 if (offset == 14) {
234 s->bank = value;
235 return;
236 }
237 if (offset == 15)
238 return;
239 switch (s->bank) {
240 case 0:
241 switch (offset) {
242 case 0: /* TCR */
243 SET_LOW(tcr, value);
244 return;
245 case 1:
246 SET_HIGH(tcr, value);
247 return;
248 case 4: /* RCR */
249 SET_LOW(rcr, value);
250 return;
251 case 5:
252 SET_HIGH(rcr, value);
253 if (s->rcr & RCR_SOFT_RST)
254 smc91c111_reset(s);
255 return;
256 case 10: case 11: /* RPCR */
257 /* Ignored */
258 return;
259 }
260 break;
261
262 case 1:
263 switch (offset) {
264 case 0: /* CONFIG */
265 SET_LOW(cr, value);
266 return;
267 case 1:
268 SET_HIGH(cr,value);
269 return;
270 case 2: case 3: /* BASE */
271 case 4: case 5: case 6: case 7: case 8: case 9: /* IA */
272 /* Not implemented. */
273 return;
274 case 10: /* Genral Purpose */
275 SET_LOW(gpr, value);
276 return;
277 case 11:
278 SET_HIGH(gpr, value);
279 return;
280 case 12: /* Control */
281 if (value & 1)
282 fprintf(stderr, "smc91c111:EEPROM store not implemented\n");
283 if (value & 2)
284 fprintf(stderr, "smc91c111:EEPROM reload not implemented\n");
285 value &= ~3;
286 SET_LOW(ctr, value);
287 return;
288 case 13:
289 SET_HIGH(ctr, value);
290 return;
291 }
292 break;
293
294 case 2:
295 switch (offset) {
296 case 0: /* MMU Command */
297 switch (value >> 5) {
298 case 0: /* no-op */
299 break;
300 case 1: /* Allocate for TX. */
301 s->tx_alloc = 0x80;
302 s->int_level &= ~INT_ALLOC;
303 smc91c111_update(s);
304 smc91c111_tx_alloc(s);
305 break;
306 case 2: /* Reset MMU. */
307 s->allocated = 0;
308 s->tx_fifo_len = 0;
309 s->rx_fifo_len = 0;
310 s->tx_alloc = 0;
311 break;
312 case 3: /* Remove from RX FIFO. */
313 smc91c111_pop_rx_fifo(s);
314 break;
315 case 4: /* Remove from RX FIFO and release. */
316 if (s->rx_fifo_len > 0) {
317 smc91c111_release_packet(s, s->rx_fifo[0]);
318 }
319 smc91c111_pop_rx_fifo(s);
320 break;
321 case 5: /* Release. */
322 smc91c111_release_packet(s, s->packet_num);
323 break;
324 case 6: /* Add to TX FIFO. */
325 smc91c111_queue_tx(s, s->packet_num);
326 break;
327 case 7: /* Reset TX FIFO. */
328 s->tx_fifo_len = 0;
329 break;
330 }
331 return;
332 case 1:
333 /* Ignore. */
334 return;
335 case 2: /* Packet Number Register */
336 s->packet_num = value;
337 return;
338 case 3: case 4: case 5:
339 /* Should be readonly, but linux writes to them anyway. Ignore. */
340 return;
341 case 6: /* Pointer */
342 SET_LOW(ptr, value);
343 return;
344 case 7:
345 SET_HIGH(ptr, value);
346 return;
347 case 8: case 9: case 10: case 11: /* Data */
348 {
349 int p;
350 int n;
351
352 if (s->ptr & 0x8000)
353 n = s->rx_fifo[0];
354 else
355 n = s->packet_num;
356 p = s->ptr & 0x07ff;
357 if (s->ptr & 0x4000) {
358 s->ptr = (s->ptr & 0xf800) | ((s->ptr + 1) & 0x7ff);
359 } else {
360 p += (offset & 3);
361 }
362 s->data[n][p] = value;
363 }
364 return;
365 case 12: /* Interrupt ACK. */
366 s->int_level &= ~(value & 0xd6);
367 smc91c111_update(s);
368 return;
369 case 13: /* Interrupt mask. */
370 s->int_mask = value;
371 smc91c111_update(s);
372 return;
373 }
374 break;;
375
376 case 3:
377 switch (offset) {
378 case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
379 /* Multicast table. */
380 /* Not implemented. */
381 return;
382 case 8: case 9: /* Management Interface. */
383 /* Not implemented. */
384 return;
385 case 12: /* Early receive. */
386 s->ercv = value & 0x1f;
387 case 13:
388 /* Ignore. */
389 return;
390 }
391 break;
392 }
393 cpu_abort (cpu_single_env, "smc91c111_write: Bad reg %d:%x\n",
394 s->bank, offset);
395}
396
397static uint32_t smc91c111_readb(void *opaque, target_phys_addr_t offset)
398{
399 smc91c111_state *s = (smc91c111_state *)opaque;
400
401 offset -= s->base;
402 if (offset == 14) {
403 return s->bank;
404 }
405 if (offset == 15)
406 return 0x33;
407 switch (s->bank) {
408 case 0:
409 switch (offset) {
410 case 0: /* TCR */
411 return s->tcr & 0xff;
412 case 1:
413 return s->tcr >> 8;
414 case 2: /* EPH Status */
415 return 0;
416 case 3:
417 return 0x40;
418 case 4: /* RCR */
419 return s->rcr & 0xff;
420 case 5:
421 return s->rcr >> 8;
422 case 6: /* Counter */
423 case 7:
424 /* Not implemented. */
425 return 0;
426 case 8: /* Free memory available. */
427 {
428 int i;
429 int n;
430 n = 0;
431 for (i = 0; i < NUM_PACKETS; i++) {
432 if (s->allocated & (1 << i))
433 n++;
434 }
435 return n;
436 }
437 case 9: /* Memory size. */
438 return NUM_PACKETS;
439 case 10: case 11: /* RPCR */
440 /* Not implemented. */
441 return 0;
442 }
443 break;
444
445 case 1:
446 switch (offset) {
447 case 0: /* CONFIG */
448 return s->cr & 0xff;
449 case 1:
450 return s->cr >> 8;
451 case 2: case 3: /* BASE */
452 /* Not implemented. */
453 return 0;
454 case 4: case 5: case 6: case 7: case 8: case 9: /* IA */
455 return s->macaddr[offset - 4];
456 case 10: /* General Purpose */
457 return s->gpr & 0xff;
458 case 11:
459 return s->gpr >> 8;
460 case 12: /* Control */
461 return s->ctr & 0xff;
462 case 13:
463 return s->ctr >> 8;
464 }
465 break;
466
467 case 2:
468 switch (offset) {
469 case 0: case 1: /* MMUCR Busy bit. */
470 return 0;
471 case 2: /* Packet Number. */
472 return s->packet_num;
473 case 3: /* Allocation Result. */
474 return s->tx_alloc;
475 case 4: /* TX FIFO */
476 if (s->tx_fifo_len == 0)
477 return 0x80;
478 else
479 return s->tx_fifo[0];
480 case 5: /* RX FIFO */
481 if (s->rx_fifo_len == 0)
482 return 0x80;
483 else
484 return s->rx_fifo[0];
485 case 6: /* Pointer */
486 return s->ptr & 0xff;
487 case 7:
488 return (s->ptr >> 8) & 0xf7;
489 case 8: case 9: case 10: case 11: /* Data */
490 {
491 int p;
492 int n;
493
494 if (s->ptr & 0x8000)
495 n = s->rx_fifo[0];
496 else
497 n = s->packet_num;
498 p = s->ptr & 0x07ff;
499 if (s->ptr & 0x4000) {
500 s->ptr = (s->ptr & 0xf800) | ((s->ptr + 1) & 0x07ff);
501 } else {
502 p += (offset & 3);
503 }
504 return s->data[n][p];
505 }
506 case 12: /* Interrupt status. */
507 return s->int_level;
508 case 13: /* Interrupt mask. */
509 return s->int_mask;
510 }
511 break;
512
513 case 3:
514 switch (offset) {
515 case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
516 /* Multicast table. */
517 /* Not implemented. */
518 return 0;
519 case 8: /* Management Interface. */
520 /* Not implemented. */
521 return 0x30;
522 case 9:
523 return 0x33;
524 case 10: /* Revision. */
525 return 0x91;
526 case 11:
527 return 0x33;
528 case 12:
529 return s->ercv;
530 case 13:
531 return 0;
532 }
533 break;
534 }
535 cpu_abort (cpu_single_env, "smc91c111_read: Bad reg %d:%x\n",
536 s->bank, offset);
537 return 0;
538}
539
540static void smc91c111_writew(void *opaque, target_phys_addr_t offset,
541 uint32_t value)
542{
543 smc91c111_writeb(opaque, offset, value & 0xff);
544 smc91c111_writeb(opaque, offset + 1, value >> 8);
545}
546
547static void smc91c111_writel(void *opaque, target_phys_addr_t offset,
548 uint32_t value)
549{
550 smc91c111_state *s = (smc91c111_state *)opaque;
551 /* 32-bit writes to offset 0xc only actually write to the bank select
552 register (offset 0xe) */
553 if (offset != s->base + 0xc)
554 smc91c111_writew(opaque, offset, value & 0xffff);
555 smc91c111_writew(opaque, offset + 2, value >> 16);
556}
557
558static uint32_t smc91c111_readw(void *opaque, target_phys_addr_t offset)
559{
560 uint32_t val;
561 val = smc91c111_readb(opaque, offset);
562 val |= smc91c111_readb(opaque, offset + 1) << 8;
563 return val;
564}
565
566static uint32_t smc91c111_readl(void *opaque, target_phys_addr_t offset)
567{
568 uint32_t val;
569 val = smc91c111_readw(opaque, offset);
570 val |= smc91c111_readw(opaque, offset + 2) << 16;
571 return val;
572}
573
574static void smc91c111_receive(void *opaque, const uint8_t *buf, int size)
575{
576 smc91c111_state *s = (smc91c111_state *)opaque;
577 int status;
578 int packetsize;
579 uint32_t crc;
580 int packetnum;
581 uint8_t *p;
582
583 if ((s->rcr & RCR_RXEN) == 0 || (s->rcr & RCR_SOFT_RST))
584 return;
585 /* Short packets are padded with zeros. Recieveing a packet
586 < 64 bytes long is considered an error condition. */
587 if (size < 64)
588 packetsize = 64;
589 else
590 packetsize = (size & ~1);
591 packetsize += 6;
592 crc = (s->rcr & RCR_STRIP_CRC) == 0;
593 if (crc)
594 packetsize += 4;
595 /* TODO: Flag overrun and receive errors. */
596 if (packetsize > 2048)
597 return;
598 packetnum = smc91c111_allocate_packet(s);
599 if (packetnum == 0x80)
600 return;
601 s->rx_fifo[s->rx_fifo_len++] = packetnum;
602
603 p = &s->data[packetnum][0];
604 /* ??? Multicast packets? */
605 status = 0;
606 if (size > 1518)
607 status |= RS_TOOLONG;
608 if (size & 1)
609 status |= RS_ODDFRAME;
610 *(p++) = status & 0xff;
611 *(p++) = status >> 8;
612 *(p++) = packetsize & 0xff;
613 *(p++) = packetsize >> 8;
614 memcpy(p, buf, size & ~1);
615 p += (size & ~1);
616 /* Pad short packets. */
617 if (size < 64) {
618 int pad;
619
620 if (size & 1)
621 *(p++) = buf[size - 1];
622 pad = 64 - size;
623 memset(p, 0, pad);
624 p += pad;
625 size = 64;
626 }
627 /* It's not clear if the CRC should go before or after the last byte in
628 odd sized packets. Linux disables the CRC, so that's no help.
629 The pictures in the documentation show the CRC aligned on a 16-bit
630 boundary before the last odd byte, so that's what we do. */
631 if (crc) {
632 crc = crc32(~0, buf, size);
633 *(p++) = crc & 0xff; crc >>= 8;
634 *(p++) = crc & 0xff; crc >>= 8;
635 *(p++) = crc & 0xff; crc >>= 8;
636 *(p++) = crc & 0xff; crc >>= 8;
637 }
638 if (size & 1) {
639 *(p++) = buf[size - 1];
640 *(p++) = 0x60;
641 } else {
642 *(p++) = 0;
643 *(p++) = 0x40;
644 }
645 /* TODO: Raise early RX interrupt? */
646 s->int_level |= INT_RCV;
647 smc91c111_update(s);
648}
649
650static CPUReadMemoryFunc *smc91c111_readfn[] = {
651 smc91c111_readb,
652 smc91c111_readw,
653 smc91c111_readl
654};
655
656static CPUWriteMemoryFunc *smc91c111_writefn[] = {
657 smc91c111_writeb,
658 smc91c111_writew,
659 smc91c111_writel
660};
661
662void smc91c111_init(NICInfo *nd, uint32_t base, void *pic, int irq)
663{
664 smc91c111_state *s;
665 int iomemtype;
666
667 s = (smc91c111_state *)qemu_mallocz(sizeof(smc91c111_state));
668 iomemtype = cpu_register_io_memory(0, smc91c111_readfn,
669 smc91c111_writefn, s);
670 cpu_register_physical_memory(base, 16, iomemtype);
671 s->base = base;
672 s->pic = pic;
673 s->irq = irq;
674 memcpy(s->macaddr, nd->macaddr, 6);
675
676 smc91c111_reset(s);
677
678 s->vc = qemu_new_vlan_client(nd->vlan, smc91c111_receive, s);
679 /* ??? Save/restore. */
680}