f7d58e126281da213208132529272bf2b8490136
[sdk/emulator/qemu.git] / hw / smc91c111.c
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 "sysbus.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     SysBusDevice busdev;
21     NICState *nic;
22     NICConf conf;
23     uint16_t tcr;
24     uint16_t rcr;
25     uint16_t cr;
26     uint16_t ctr;
27     uint16_t gpr;
28     uint16_t ptr;
29     uint16_t ercv;
30     qemu_irq irq;
31     int bank;
32     int packet_num;
33     int tx_alloc;
34     /* Bitmask of allocated packets.  */
35     int allocated;
36     int tx_fifo_len;
37     int tx_fifo[NUM_PACKETS];
38     int rx_fifo_len;
39     int rx_fifo[NUM_PACKETS];
40     int tx_fifo_done_len;
41     int tx_fifo_done[NUM_PACKETS];
42     /* Packet buffer memory.  */
43     uint8_t data[NUM_PACKETS][2048];
44     uint8_t int_level;
45     uint8_t int_mask;
46     int mmio_index;
47 } smc91c111_state;
48
49 #define RCR_SOFT_RST  0x8000
50 #define RCR_STRIP_CRC 0x0200
51 #define RCR_RXEN      0x0100
52
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
59
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
68
69 #define CTR_AUTO_RELEASE  0x0800
70 #define CTR_RELOAD        0x0002
71 #define CTR_STORE         0x0001
72
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
80
81 /* Update interrupt status.  */
82 static void smc91c111_update(smc91c111_state *s)
83 {
84     int level;
85
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);
92 }
93
94 /* Try to allocate a packet.  Returns 0x80 on failure.  */
95 static int smc91c111_allocate_packet(smc91c111_state *s)
96 {
97     int i;
98     if (s->allocated == (1 << NUM_PACKETS) - 1) {
99         return 0x80;
100     }
101
102     for (i = 0; i < NUM_PACKETS; i++) {
103         if ((s->allocated & (1 << i)) == 0)
104             break;
105     }
106     s->allocated |= 1 << i;
107     return i;
108 }
109
110
111 /* Process a pending TX allocate.  */
112 static void smc91c111_tx_alloc(smc91c111_state *s)
113 {
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);
119 }
120
121 /* Remove and item from the RX FIFO.  */
122 static void smc91c111_pop_rx_fifo(smc91c111_state *s)
123 {
124     int i;
125
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;
133     }
134     smc91c111_update(s);
135 }
136
137 /* Remove an item from the TX completion FIFO.  */
138 static void smc91c111_pop_tx_fifo_done(smc91c111_state *s)
139 {
140     int i;
141
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];
147 }
148
149 /* Release the memory allocated to a packet.  */
150 static void smc91c111_release_packet(smc91c111_state *s, int packet)
151 {
152     s->allocated &= ~(1 << packet);
153     if (s->tx_alloc == 0x80)
154         smc91c111_tx_alloc(s);
155 }
156
157 /* Flush the TX FIFO.  */
158 static void smc91c111_do_tx(smc91c111_state *s)
159 {
160     int i;
161     int len;
162     int control;
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         {
190             int add_crc;
191
192             /* The card is supposed to append the CRC to the frame.
193                However none of the other network traffic has the CRC
194                appended.  Suspect this is low level ethernet detail we
195                don't need to worry about.  */
196             add_crc = (control & 0x10) || (s->tcr & TCR_NOCRC) == 0;
197             if (add_crc) {
198                 uint32_t crc;
199
200                 crc = crc32(~0, p, len);
201                 memcpy(p + len, &crc, 4);
202                 len += 4;
203             }
204         }
205 #endif
206         if (s->ctr & CTR_AUTO_RELEASE)
207             /* Race?  */
208             smc91c111_release_packet(s, packetnum);
209         else if (s->tx_fifo_done_len < NUM_PACKETS)
210             s->tx_fifo_done[s->tx_fifo_done_len++] = packetnum;
211         qemu_send_packet(&s->nic->nc, p, len);
212     }
213     s->tx_fifo_len = 0;
214     smc91c111_update(s);
215 }
216
217 /* Add a packet to the TX FIFO.  */
218 static void smc91c111_queue_tx(smc91c111_state *s, int packet)
219 {
220     if (s->tx_fifo_len == NUM_PACKETS)
221         return;
222     s->tx_fifo[s->tx_fifo_len++] = packet;
223     smc91c111_do_tx(s);
224 }
225
226 static void smc91c111_reset(smc91c111_state *s)
227 {
228     s->bank = 0;
229     s->tx_fifo_len = 0;
230     s->tx_fifo_done_len = 0;
231     s->rx_fifo_len = 0;
232     s->allocated = 0;
233     s->packet_num = 0;
234     s->tx_alloc = 0;
235     s->tcr = 0;
236     s->rcr = 0;
237     s->cr = 0xa0b1;
238     s->ctr = 0x1210;
239     s->ptr = 0;
240     s->ercv = 0x1f;
241     s->int_level = INT_TX_EMPTY;
242     s->int_mask = 0;
243     smc91c111_update(s);
244 }
245
246 #define SET_LOW(name, val) s->name = (s->name & 0xff00) | val
247 #define SET_HIGH(name, val) s->name = (s->name & 0xff) | (val << 8)
248
249 static void smc91c111_writeb(void *opaque, target_phys_addr_t offset,
250                              uint32_t value)
251 {
252     smc91c111_state *s = (smc91c111_state *)opaque;
253
254     offset = offset & 0xf;
255     if (offset == 14) {
256         s->bank = value;
257         return;
258     }
259     if (offset == 15)
260         return;
261     switch (s->bank) {
262     case 0:
263         switch (offset) {
264         case 0: /* TCR */
265             SET_LOW(tcr, value);
266             return;
267         case 1:
268             SET_HIGH(tcr, value);
269             return;
270         case 4: /* RCR */
271             SET_LOW(rcr, value);
272             return;
273         case 5:
274             SET_HIGH(rcr, value);
275             if (s->rcr & RCR_SOFT_RST)
276                 smc91c111_reset(s);
277             return;
278         case 10: case 11: /* RPCR */
279             /* Ignored */
280             return;
281         case 12: case 13: /* Reserved */
282             return;
283         }
284         break;
285
286     case 1:
287         switch (offset) {
288         case 0: /* CONFIG */
289             SET_LOW(cr, value);
290             return;
291         case 1:
292             SET_HIGH(cr,value);
293             return;
294         case 2: case 3: /* BASE */
295         case 4: case 5: case 6: case 7: case 8: case 9: /* IA */
296             /* Not implemented.  */
297             return;
298         case 10: /* Genral Purpose */
299             SET_LOW(gpr, value);
300             return;
301         case 11:
302             SET_HIGH(gpr, value);
303             return;
304         case 12: /* Control */
305             if (value & 1)
306                 fprintf(stderr, "smc91c111:EEPROM store not implemented\n");
307             if (value & 2)
308                 fprintf(stderr, "smc91c111:EEPROM reload not implemented\n");
309             value &= ~3;
310             SET_LOW(ctr, value);
311             return;
312         case 13:
313             SET_HIGH(ctr, value);
314             return;
315         }
316         break;
317
318     case 2:
319         switch (offset) {
320         case 0: /* MMU Command */
321             switch (value >> 5) {
322             case 0: /* no-op */
323                 break;
324             case 1: /* Allocate for TX.  */
325                 s->tx_alloc = 0x80;
326                 s->int_level &= ~INT_ALLOC;
327                 smc91c111_update(s);
328                 smc91c111_tx_alloc(s);
329                 break;
330             case 2: /* Reset MMU.  */
331                 s->allocated = 0;
332                 s->tx_fifo_len = 0;
333                 s->tx_fifo_done_len = 0;
334                 s->rx_fifo_len = 0;
335                 s->tx_alloc = 0;
336                 break;
337             case 3: /* Remove from RX FIFO.  */
338                 smc91c111_pop_rx_fifo(s);
339                 break;
340             case 4: /* Remove from RX FIFO and release.  */
341                 if (s->rx_fifo_len > 0) {
342                     smc91c111_release_packet(s, s->rx_fifo[0]);
343                 }
344                 smc91c111_pop_rx_fifo(s);
345                 break;
346             case 5: /* Release.  */
347                 smc91c111_release_packet(s, s->packet_num);
348                 break;
349             case 6: /* Add to TX FIFO.  */
350                 smc91c111_queue_tx(s, s->packet_num);
351                 break;
352             case 7: /* Reset TX FIFO.  */
353                 s->tx_fifo_len = 0;
354                 s->tx_fifo_done_len = 0;
355                 break;
356             }
357             return;
358         case 1:
359             /* Ignore.  */
360             return;
361         case 2: /* Packet Number Register */
362             s->packet_num = value;
363             return;
364         case 3: case 4: case 5:
365             /* Should be readonly, but linux writes to them anyway. Ignore.  */
366             return;
367         case 6: /* Pointer */
368             SET_LOW(ptr, value);
369             return;
370         case 7:
371             SET_HIGH(ptr, value);
372             return;
373         case 8: case 9: case 10: case 11: /* Data */
374             {
375                 int p;
376                 int n;
377
378                 if (s->ptr & 0x8000)
379                     n = s->rx_fifo[0];
380                 else
381                     n = s->packet_num;
382                 p = s->ptr & 0x07ff;
383                 if (s->ptr & 0x4000) {
384                     s->ptr = (s->ptr & 0xf800) | ((s->ptr + 1) & 0x7ff);
385                 } else {
386                     p += (offset & 3);
387                 }
388                 s->data[n][p] = value;
389             }
390             return;
391         case 12: /* Interrupt ACK.  */
392             s->int_level &= ~(value & 0xd6);
393             if (value & INT_TX)
394                 smc91c111_pop_tx_fifo_done(s);
395             smc91c111_update(s);
396             return;
397         case 13: /* Interrupt mask.  */
398             s->int_mask = value;
399             smc91c111_update(s);
400             return;
401         }
402         break;;
403
404     case 3:
405         switch (offset) {
406         case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
407             /* Multicast table.  */
408             /* Not implemented.  */
409             return;
410         case 8: case 9: /* Management Interface.  */
411             /* Not implemented.  */
412             return;
413         case 12: /* Early receive.  */
414             s->ercv = value & 0x1f;
415         case 13:
416             /* Ignore.  */
417             return;
418         }
419         break;
420     }
421     hw_error("smc91c111_write: Bad reg %d:%x\n", s->bank, (int)offset);
422 }
423
424 static uint32_t smc91c111_readb(void *opaque, target_phys_addr_t offset)
425 {
426     smc91c111_state *s = (smc91c111_state *)opaque;
427
428     offset = offset & 0xf;
429     if (offset == 14) {
430         return s->bank;
431     }
432     if (offset == 15)
433         return 0x33;
434     switch (s->bank) {
435     case 0:
436         switch (offset) {
437         case 0: /* TCR */
438             return s->tcr & 0xff;
439         case 1:
440             return s->tcr >> 8;
441         case 2: /* EPH Status */
442             return 0;
443         case 3:
444             return 0x40;
445         case 4: /* RCR */
446             return s->rcr & 0xff;
447         case 5:
448             return s->rcr >> 8;
449         case 6: /* Counter */
450         case 7:
451             /* Not implemented.  */
452             return 0;
453         case 8: /* Memory size.  */
454             return NUM_PACKETS;
455         case 9: /* Free memory available.  */
456             {
457                 int i;
458                 int n;
459                 n = 0;
460                 for (i = 0; i < NUM_PACKETS; i++) {
461                     if (s->allocated & (1 << i))
462                         n++;
463                 }
464                 return n;
465             }
466         case 10: case 11: /* RPCR */
467             /* Not implemented.  */
468             return 0;
469         case 12: case 13: /* Reserved */
470             return 0;
471         }
472         break;
473
474     case 1:
475         switch (offset) {
476         case 0: /* CONFIG */
477             return s->cr & 0xff;
478         case 1:
479             return s->cr >> 8;
480         case 2: case 3: /* BASE */
481             /* Not implemented.  */
482             return 0;
483         case 4: case 5: case 6: case 7: case 8: case 9: /* IA */
484             return s->conf.macaddr.a[offset - 4];
485         case 10: /* General Purpose */
486             return s->gpr & 0xff;
487         case 11:
488             return s->gpr >> 8;
489         case 12: /* Control */
490             return s->ctr & 0xff;
491         case 13:
492             return s->ctr >> 8;
493         }
494         break;
495
496     case 2:
497         switch (offset) {
498         case 0: case 1: /* MMUCR Busy bit.  */
499             return 0;
500         case 2: /* Packet Number.  */
501             return s->packet_num;
502         case 3: /* Allocation Result.  */
503             return s->tx_alloc;
504         case 4: /* TX FIFO */
505             if (s->tx_fifo_done_len == 0)
506                 return 0x80;
507             else
508                 return s->tx_fifo_done[0];
509         case 5: /* RX FIFO */
510             if (s->rx_fifo_len == 0)
511                 return 0x80;
512             else
513                 return s->rx_fifo[0];
514         case 6: /* Pointer */
515             return s->ptr & 0xff;
516         case 7:
517             return (s->ptr >> 8) & 0xf7;
518         case 8: case 9: case 10: case 11: /* Data */
519             {
520                 int p;
521                 int n;
522
523                 if (s->ptr & 0x8000)
524                     n = s->rx_fifo[0];
525                 else
526                     n = s->packet_num;
527                 p = s->ptr & 0x07ff;
528                 if (s->ptr & 0x4000) {
529                     s->ptr = (s->ptr & 0xf800) | ((s->ptr + 1) & 0x07ff);
530                 } else {
531                     p += (offset & 3);
532                 }
533                 return s->data[n][p];
534             }
535         case 12: /* Interrupt status.  */
536             return s->int_level;
537         case 13: /* Interrupt mask.  */
538             return s->int_mask;
539         }
540         break;
541
542     case 3:
543         switch (offset) {
544         case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
545             /* Multicast table.  */
546             /* Not implemented.  */
547             return 0;
548         case 8: /* Management Interface.  */
549             /* Not implemented.  */
550             return 0x30;
551         case 9:
552             return 0x33;
553         case 10: /* Revision.  */
554             return 0x91;
555         case 11:
556             return 0x33;
557         case 12:
558             return s->ercv;
559         case 13:
560             return 0;
561         }
562         break;
563     }
564     hw_error("smc91c111_read: Bad reg %d:%x\n", s->bank, (int)offset);
565     return 0;
566 }
567
568 static void smc91c111_writew(void *opaque, target_phys_addr_t offset,
569                              uint32_t value)
570 {
571     smc91c111_writeb(opaque, offset, value & 0xff);
572     smc91c111_writeb(opaque, offset + 1, value >> 8);
573 }
574
575 static void smc91c111_writel(void *opaque, target_phys_addr_t offset,
576                              uint32_t value)
577 {
578     /* 32-bit writes to offset 0xc only actually write to the bank select
579        register (offset 0xe)  */
580     if (offset != 0xc)
581         smc91c111_writew(opaque, offset, value & 0xffff);
582     smc91c111_writew(opaque, offset + 2, value >> 16);
583 }
584
585 static uint32_t smc91c111_readw(void *opaque, target_phys_addr_t offset)
586 {
587     uint32_t val;
588     val = smc91c111_readb(opaque, offset);
589     val |= smc91c111_readb(opaque, offset + 1) << 8;
590     return val;
591 }
592
593 static uint32_t smc91c111_readl(void *opaque, target_phys_addr_t offset)
594 {
595     uint32_t val;
596     val = smc91c111_readw(opaque, offset);
597     val |= smc91c111_readw(opaque, offset + 2) << 16;
598     return val;
599 }
600
601 static int smc91c111_can_receive(VLANClientState *nc)
602 {
603     smc91c111_state *s = DO_UPCAST(NICState, nc, nc)->opaque;
604
605     if ((s->rcr & RCR_RXEN) == 0 || (s->rcr & RCR_SOFT_RST))
606         return 1;
607     if (s->allocated == (1 << NUM_PACKETS) - 1)
608         return 0;
609     return 1;
610 }
611
612 static ssize_t smc91c111_receive(VLANClientState *nc, const uint8_t *buf, size_t size)
613 {
614     smc91c111_state *s = DO_UPCAST(NICState, nc, nc)->opaque;
615     int status;
616     int packetsize;
617     uint32_t crc;
618     int packetnum;
619     uint8_t *p;
620
621     if ((s->rcr & RCR_RXEN) == 0 || (s->rcr & RCR_SOFT_RST))
622         return -1;
623     /* Short packets are padded with zeros.  Receiving a packet
624        < 64 bytes long is considered an error condition.  */
625     if (size < 64)
626         packetsize = 64;
627     else
628         packetsize = (size & ~1);
629     packetsize += 6;
630     crc = (s->rcr & RCR_STRIP_CRC) == 0;
631     if (crc)
632         packetsize += 4;
633     /* TODO: Flag overrun and receive errors.  */
634     if (packetsize > 2048)
635         return -1;
636     packetnum = smc91c111_allocate_packet(s);
637     if (packetnum == 0x80)
638         return -1;
639     s->rx_fifo[s->rx_fifo_len++] = packetnum;
640
641     p = &s->data[packetnum][0];
642     /* ??? Multicast packets?  */
643     status = 0;
644     if (size > 1518)
645         status |= RS_TOOLONG;
646     if (size & 1)
647         status |= RS_ODDFRAME;
648     *(p++) = status & 0xff;
649     *(p++) = status >> 8;
650     *(p++) = packetsize & 0xff;
651     *(p++) = packetsize >> 8;
652     memcpy(p, buf, size & ~1);
653     p += (size & ~1);
654     /* Pad short packets.  */
655     if (size < 64) {
656         int pad;
657
658         if (size & 1)
659             *(p++) = buf[size - 1];
660         pad = 64 - size;
661         memset(p, 0, pad);
662         p += pad;
663         size = 64;
664     }
665     /* It's not clear if the CRC should go before or after the last byte in
666        odd sized packets.  Linux disables the CRC, so that's no help.
667        The pictures in the documentation show the CRC aligned on a 16-bit
668        boundary before the last odd byte, so that's what we do.  */
669     if (crc) {
670         crc = crc32(~0, buf, size);
671         *(p++) = crc & 0xff; crc >>= 8;
672         *(p++) = crc & 0xff; crc >>= 8;
673         *(p++) = crc & 0xff; crc >>= 8;
674         *(p++) = crc & 0xff;
675     }
676     if (size & 1) {
677         *(p++) = buf[size - 1];
678         *p = 0x60;
679     } else {
680         *(p++) = 0;
681         *p = 0x40;
682     }
683     /* TODO: Raise early RX interrupt?  */
684     s->int_level |= INT_RCV;
685     smc91c111_update(s);
686
687     return size;
688 }
689
690 static CPUReadMemoryFunc * const smc91c111_readfn[] = {
691     smc91c111_readb,
692     smc91c111_readw,
693     smc91c111_readl
694 };
695
696 static CPUWriteMemoryFunc * const smc91c111_writefn[] = {
697     smc91c111_writeb,
698     smc91c111_writew,
699     smc91c111_writel
700 };
701
702 static void smc91c111_cleanup(VLANClientState *nc)
703 {
704     smc91c111_state *s = DO_UPCAST(NICState, nc, nc)->opaque;
705
706     s->nic = NULL;
707 }
708
709 static NetClientInfo net_smc91c111_info = {
710     .type = NET_CLIENT_TYPE_NIC,
711     .size = sizeof(NICState),
712     .can_receive = smc91c111_can_receive,
713     .receive = smc91c111_receive,
714     .cleanup = smc91c111_cleanup,
715 };
716
717 static int smc91c111_init1(SysBusDevice *dev)
718 {
719     smc91c111_state *s = FROM_SYSBUS(smc91c111_state, dev);
720
721     s->mmio_index = cpu_register_io_memory(smc91c111_readfn,
722                                            smc91c111_writefn, s);
723     sysbus_init_mmio(dev, 16, s->mmio_index);
724     sysbus_init_irq(dev, &s->irq);
725     qemu_macaddr_default_if_unset(&s->conf.macaddr);
726
727     smc91c111_reset(s);
728
729     s->nic = qemu_new_nic(&net_smc91c111_info, &s->conf,
730                           dev->qdev.info->name, dev->qdev.id, s);
731     qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a);
732     /* ??? Save/restore.  */
733     return 0;
734 }
735
736 static SysBusDeviceInfo smc91c111_info = {
737     .init = smc91c111_init1,
738     .qdev.name  = "smc91c111",
739     .qdev.size  = sizeof(smc91c111_state),
740     .qdev.props = (Property[]) {
741         DEFINE_NIC_PROPERTIES(smc91c111_state, conf),
742         DEFINE_PROP_END_OF_LIST(),
743     }
744 };
745
746 static void smc91c111_register_devices(void)
747 {
748     sysbus_register_withprop(&smc91c111_info);
749 }
750
751 /* Legacy helper function.  Should go away when machine config files are
752    implemented.  */
753 void smc91c111_init(NICInfo *nd, uint32_t base, qemu_irq irq)
754 {
755     DeviceState *dev;
756     SysBusDevice *s;
757
758     qemu_check_nic_model(nd, "smc91c111");
759     dev = qdev_create(NULL, "smc91c111");
760     qdev_set_nic_properties(dev, nd);
761     qdev_init_nofail(dev);
762     s = sysbus_from_qdev(dev);
763     sysbus_mmio_map(s, 0, base);
764     sysbus_connect_irq(s, 0, irq);
765 }
766
767 device_init(smc91c111_register_devices)