2 * SMSC 91C111 Ethernet interface emulation
4 * Copyright (c) 2005 CodeSourcery, LLC.
5 * Written by Paul Brook
7 * This code is licensed under the GPL
10 #include "hw/sysbus.h"
12 #include "hw/devices.h"
16 /* Number of 2k memory pages available. */
19 #define TYPE_SMC91C111 "smc91c111"
20 #define SMC91C111(obj) OBJECT_CHECK(smc91c111_state, (obj), TYPE_SMC91C111)
23 SysBusDevice parent_obj
;
38 /* Bitmask of allocated packets. */
41 int tx_fifo
[NUM_PACKETS
];
43 int rx_fifo
[NUM_PACKETS
];
45 int tx_fifo_done
[NUM_PACKETS
];
46 /* Packet buffer memory. */
47 uint8_t data
[NUM_PACKETS
][2048];
53 static const VMStateDescription vmstate_smc91c111
= {
56 .minimum_version_id
= 1,
57 .fields
= (VMStateField
[]) {
58 VMSTATE_UINT16(tcr
, smc91c111_state
),
59 VMSTATE_UINT16(rcr
, smc91c111_state
),
60 VMSTATE_UINT16(cr
, smc91c111_state
),
61 VMSTATE_UINT16(ctr
, smc91c111_state
),
62 VMSTATE_UINT16(gpr
, smc91c111_state
),
63 VMSTATE_UINT16(ptr
, smc91c111_state
),
64 VMSTATE_UINT16(ercv
, smc91c111_state
),
65 VMSTATE_INT32(bank
, smc91c111_state
),
66 VMSTATE_INT32(packet_num
, smc91c111_state
),
67 VMSTATE_INT32(tx_alloc
, smc91c111_state
),
68 VMSTATE_INT32(allocated
, smc91c111_state
),
69 VMSTATE_INT32(tx_fifo_len
, smc91c111_state
),
70 VMSTATE_INT32_ARRAY(tx_fifo
, smc91c111_state
, NUM_PACKETS
),
71 VMSTATE_INT32(rx_fifo_len
, smc91c111_state
),
72 VMSTATE_INT32_ARRAY(rx_fifo
, smc91c111_state
, NUM_PACKETS
),
73 VMSTATE_INT32(tx_fifo_done_len
, smc91c111_state
),
74 VMSTATE_INT32_ARRAY(tx_fifo_done
, smc91c111_state
, NUM_PACKETS
),
75 VMSTATE_BUFFER_UNSAFE(data
, smc91c111_state
, 0, NUM_PACKETS
* 2048),
76 VMSTATE_UINT8(int_level
, smc91c111_state
),
77 VMSTATE_UINT8(int_mask
, smc91c111_state
),
82 #define RCR_SOFT_RST 0x8000
83 #define RCR_STRIP_CRC 0x0200
84 #define RCR_RXEN 0x0100
86 #define TCR_EPH_LOOP 0x2000
87 #define TCR_NOCRC 0x0100
88 #define TCR_PAD_EN 0x0080
89 #define TCR_FORCOL 0x0004
90 #define TCR_LOOP 0x0002
91 #define TCR_TXEN 0x0001
96 #define INT_RX_OVRN 0x10
97 #define INT_ALLOC 0x08
98 #define INT_TX_EMPTY 0x04
102 #define CTR_AUTO_RELEASE 0x0800
103 #define CTR_RELOAD 0x0002
104 #define CTR_STORE 0x0001
106 #define RS_ALGNERR 0x8000
107 #define RS_BRODCAST 0x4000
108 #define RS_BADCRC 0x2000
109 #define RS_ODDFRAME 0x1000
110 #define RS_TOOLONG 0x0800
111 #define RS_TOOSHORT 0x0400
112 #define RS_MULTICAST 0x0001
114 /* Update interrupt status. */
115 static void smc91c111_update(smc91c111_state
*s
)
119 if (s
->tx_fifo_len
== 0)
120 s
->int_level
|= INT_TX_EMPTY
;
121 if (s
->tx_fifo_done_len
!= 0)
122 s
->int_level
|= INT_TX
;
123 level
= (s
->int_level
& s
->int_mask
) != 0;
124 qemu_set_irq(s
->irq
, level
);
127 static int smc91c111_can_receive(smc91c111_state
*s
)
129 if ((s
->rcr
& RCR_RXEN
) == 0 || (s
->rcr
& RCR_SOFT_RST
)) {
132 if (s
->allocated
== (1 << NUM_PACKETS
) - 1 ||
133 s
->rx_fifo_len
== NUM_PACKETS
) {
139 static inline void smc91c111_flush_queued_packets(smc91c111_state
*s
)
141 if (smc91c111_can_receive(s
)) {
142 qemu_flush_queued_packets(qemu_get_queue(s
->nic
));
146 /* Try to allocate a packet. Returns 0x80 on failure. */
147 static int smc91c111_allocate_packet(smc91c111_state
*s
)
150 if (s
->allocated
== (1 << NUM_PACKETS
) - 1) {
154 for (i
= 0; i
< NUM_PACKETS
; i
++) {
155 if ((s
->allocated
& (1 << i
)) == 0)
158 s
->allocated
|= 1 << i
;
163 /* Process a pending TX allocate. */
164 static void smc91c111_tx_alloc(smc91c111_state
*s
)
166 s
->tx_alloc
= smc91c111_allocate_packet(s
);
167 if (s
->tx_alloc
== 0x80)
169 s
->int_level
|= INT_ALLOC
;
173 /* Remove and item from the RX FIFO. */
174 static void smc91c111_pop_rx_fifo(smc91c111_state
*s
)
179 if (s
->rx_fifo_len
) {
180 for (i
= 0; i
< s
->rx_fifo_len
; i
++)
181 s
->rx_fifo
[i
] = s
->rx_fifo
[i
+ 1];
182 s
->int_level
|= INT_RCV
;
184 s
->int_level
&= ~INT_RCV
;
186 smc91c111_flush_queued_packets(s
);
190 /* Remove an item from the TX completion FIFO. */
191 static void smc91c111_pop_tx_fifo_done(smc91c111_state
*s
)
195 if (s
->tx_fifo_done_len
== 0)
197 s
->tx_fifo_done_len
--;
198 for (i
= 0; i
< s
->tx_fifo_done_len
; i
++)
199 s
->tx_fifo_done
[i
] = s
->tx_fifo_done
[i
+ 1];
202 /* Release the memory allocated to a packet. */
203 static void smc91c111_release_packet(smc91c111_state
*s
, int packet
)
205 s
->allocated
&= ~(1 << packet
);
206 if (s
->tx_alloc
== 0x80)
207 smc91c111_tx_alloc(s
);
208 smc91c111_flush_queued_packets(s
);
211 /* Flush the TX FIFO. */
212 static void smc91c111_do_tx(smc91c111_state
*s
)
220 if ((s
->tcr
& TCR_TXEN
) == 0)
222 if (s
->tx_fifo_len
== 0)
224 for (i
= 0; i
< s
->tx_fifo_len
; i
++) {
225 packetnum
= s
->tx_fifo
[i
];
226 p
= &s
->data
[packetnum
][0];
227 /* Set status word. */
231 len
|= ((int)*(p
++)) << 8;
233 control
= p
[len
+ 1];
236 /* ??? This overwrites the data following the buffer.
237 Don't know what real hardware does. */
238 if (len
< 64 && (s
->tcr
& TCR_PAD_EN
)) {
239 memset(p
+ len
, 0, 64 - len
);
246 /* The card is supposed to append the CRC to the frame.
247 However none of the other network traffic has the CRC
248 appended. Suspect this is low level ethernet detail we
249 don't need to worry about. */
250 add_crc
= (control
& 0x10) || (s
->tcr
& TCR_NOCRC
) == 0;
254 crc
= crc32(~0, p
, len
);
255 memcpy(p
+ len
, &crc
, 4);
260 if (s
->ctr
& CTR_AUTO_RELEASE
)
262 smc91c111_release_packet(s
, packetnum
);
263 else if (s
->tx_fifo_done_len
< NUM_PACKETS
)
264 s
->tx_fifo_done
[s
->tx_fifo_done_len
++] = packetnum
;
265 qemu_send_packet(qemu_get_queue(s
->nic
), p
, len
);
271 /* Add a packet to the TX FIFO. */
272 static void smc91c111_queue_tx(smc91c111_state
*s
, int packet
)
274 if (s
->tx_fifo_len
== NUM_PACKETS
)
276 s
->tx_fifo
[s
->tx_fifo_len
++] = packet
;
280 static void smc91c111_reset(DeviceState
*dev
)
282 smc91c111_state
*s
= SMC91C111(dev
);
286 s
->tx_fifo_done_len
= 0;
297 s
->int_level
= INT_TX_EMPTY
;
302 #define SET_LOW(name, val) s->name = (s->name & 0xff00) | val
303 #define SET_HIGH(name, val) s->name = (s->name & 0xff) | (val << 8)
305 static void smc91c111_writeb(void *opaque
, hwaddr offset
,
308 smc91c111_state
*s
= (smc91c111_state
*)opaque
;
310 offset
= offset
& 0xf;
324 SET_HIGH(tcr
, value
);
330 SET_HIGH(rcr
, value
);
331 if (s
->rcr
& RCR_SOFT_RST
) {
332 smc91c111_reset(DEVICE(s
));
335 case 10: case 11: /* RPCR */
338 case 12: case 13: /* Reserved */
351 case 2: case 3: /* BASE */
352 case 4: case 5: case 6: case 7: case 8: case 9: /* IA */
353 /* Not implemented. */
355 case 10: /* Genral Purpose */
359 SET_HIGH(gpr
, value
);
361 case 12: /* Control */
363 fprintf(stderr
, "smc91c111:EEPROM store not implemented\n");
365 fprintf(stderr
, "smc91c111:EEPROM reload not implemented\n");
370 SET_HIGH(ctr
, value
);
377 case 0: /* MMU Command */
378 switch (value
>> 5) {
381 case 1: /* Allocate for TX. */
383 s
->int_level
&= ~INT_ALLOC
;
385 smc91c111_tx_alloc(s
);
387 case 2: /* Reset MMU. */
390 s
->tx_fifo_done_len
= 0;
394 case 3: /* Remove from RX FIFO. */
395 smc91c111_pop_rx_fifo(s
);
397 case 4: /* Remove from RX FIFO and release. */
398 if (s
->rx_fifo_len
> 0) {
399 smc91c111_release_packet(s
, s
->rx_fifo
[0]);
401 smc91c111_pop_rx_fifo(s
);
403 case 5: /* Release. */
404 smc91c111_release_packet(s
, s
->packet_num
);
406 case 6: /* Add to TX FIFO. */
407 smc91c111_queue_tx(s
, s
->packet_num
);
409 case 7: /* Reset TX FIFO. */
411 s
->tx_fifo_done_len
= 0;
418 case 2: /* Packet Number Register */
419 s
->packet_num
= value
;
421 case 3: case 4: case 5:
422 /* Should be readonly, but linux writes to them anyway. Ignore. */
424 case 6: /* Pointer */
428 SET_HIGH(ptr
, value
);
430 case 8: case 9: case 10: case 11: /* Data */
440 if (s
->ptr
& 0x4000) {
441 s
->ptr
= (s
->ptr
& 0xf800) | ((s
->ptr
+ 1) & 0x7ff);
445 s
->data
[n
][p
] = value
;
448 case 12: /* Interrupt ACK. */
449 s
->int_level
&= ~(value
& 0xd6);
451 smc91c111_pop_tx_fifo_done(s
);
454 case 13: /* Interrupt mask. */
463 case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
464 /* Multicast table. */
465 /* Not implemented. */
467 case 8: case 9: /* Management Interface. */
468 /* Not implemented. */
470 case 12: /* Early receive. */
471 s
->ercv
= value
& 0x1f;
479 hw_error("smc91c111_write: Bad reg %d:%x\n", s
->bank
, (int)offset
);
482 static uint32_t smc91c111_readb(void *opaque
, hwaddr offset
)
484 smc91c111_state
*s
= (smc91c111_state
*)opaque
;
486 offset
= offset
& 0xf;
496 return s
->tcr
& 0xff;
499 case 2: /* EPH Status */
504 return s
->rcr
& 0xff;
507 case 6: /* Counter */
509 /* Not implemented. */
511 case 8: /* Memory size. */
513 case 9: /* Free memory available. */
518 for (i
= 0; i
< NUM_PACKETS
; i
++) {
519 if (s
->allocated
& (1 << i
))
524 case 10: case 11: /* RPCR */
525 /* Not implemented. */
527 case 12: case 13: /* Reserved */
538 case 2: case 3: /* BASE */
539 /* Not implemented. */
541 case 4: case 5: case 6: case 7: case 8: case 9: /* IA */
542 return s
->conf
.macaddr
.a
[offset
- 4];
543 case 10: /* General Purpose */
544 return s
->gpr
& 0xff;
547 case 12: /* Control */
548 return s
->ctr
& 0xff;
556 case 0: case 1: /* MMUCR Busy bit. */
558 case 2: /* Packet Number. */
559 return s
->packet_num
;
560 case 3: /* Allocation Result. */
562 case 4: /* TX FIFO */
563 if (s
->tx_fifo_done_len
== 0)
566 return s
->tx_fifo_done
[0];
567 case 5: /* RX FIFO */
568 if (s
->rx_fifo_len
== 0)
571 return s
->rx_fifo
[0];
572 case 6: /* Pointer */
573 return s
->ptr
& 0xff;
575 return (s
->ptr
>> 8) & 0xf7;
576 case 8: case 9: case 10: case 11: /* Data */
586 if (s
->ptr
& 0x4000) {
587 s
->ptr
= (s
->ptr
& 0xf800) | ((s
->ptr
+ 1) & 0x07ff);
591 return s
->data
[n
][p
];
593 case 12: /* Interrupt status. */
595 case 13: /* Interrupt mask. */
602 case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
603 /* Multicast table. */
604 /* Not implemented. */
606 case 8: /* Management Interface. */
607 /* Not implemented. */
611 case 10: /* Revision. */
622 hw_error("smc91c111_read: Bad reg %d:%x\n", s
->bank
, (int)offset
);
626 static void smc91c111_writew(void *opaque
, hwaddr offset
,
629 smc91c111_writeb(opaque
, offset
, value
& 0xff);
630 smc91c111_writeb(opaque
, offset
+ 1, value
>> 8);
633 static void smc91c111_writel(void *opaque
, hwaddr offset
,
636 /* 32-bit writes to offset 0xc only actually write to the bank select
637 register (offset 0xe) */
639 smc91c111_writew(opaque
, offset
, value
& 0xffff);
640 smc91c111_writew(opaque
, offset
+ 2, value
>> 16);
643 static uint32_t smc91c111_readw(void *opaque
, hwaddr offset
)
646 val
= smc91c111_readb(opaque
, offset
);
647 val
|= smc91c111_readb(opaque
, offset
+ 1) << 8;
651 static uint32_t smc91c111_readl(void *opaque
, hwaddr offset
)
654 val
= smc91c111_readw(opaque
, offset
);
655 val
|= smc91c111_readw(opaque
, offset
+ 2) << 16;
659 static int smc91c111_can_receive_nc(NetClientState
*nc
)
661 smc91c111_state
*s
= qemu_get_nic_opaque(nc
);
663 return smc91c111_can_receive(s
);
666 static ssize_t
smc91c111_receive(NetClientState
*nc
, const uint8_t *buf
, size_t size
)
668 smc91c111_state
*s
= qemu_get_nic_opaque(nc
);
675 if ((s
->rcr
& RCR_RXEN
) == 0 || (s
->rcr
& RCR_SOFT_RST
))
677 /* Short packets are padded with zeros. Receiving a packet
678 < 64 bytes long is considered an error condition. */
682 packetsize
= (size
& ~1);
684 crc
= (s
->rcr
& RCR_STRIP_CRC
) == 0;
687 /* TODO: Flag overrun and receive errors. */
688 if (packetsize
> 2048)
690 packetnum
= smc91c111_allocate_packet(s
);
691 if (packetnum
== 0x80)
693 s
->rx_fifo
[s
->rx_fifo_len
++] = packetnum
;
695 p
= &s
->data
[packetnum
][0];
696 /* ??? Multicast packets? */
699 status
|= RS_TOOLONG
;
701 status
|= RS_ODDFRAME
;
702 *(p
++) = status
& 0xff;
703 *(p
++) = status
>> 8;
704 *(p
++) = packetsize
& 0xff;
705 *(p
++) = packetsize
>> 8;
706 memcpy(p
, buf
, size
& ~1);
708 /* Pad short packets. */
713 *(p
++) = buf
[size
- 1];
719 /* It's not clear if the CRC should go before or after the last byte in
720 odd sized packets. Linux disables the CRC, so that's no help.
721 The pictures in the documentation show the CRC aligned on a 16-bit
722 boundary before the last odd byte, so that's what we do. */
724 crc
= crc32(~0, buf
, size
);
725 *(p
++) = crc
& 0xff; crc
>>= 8;
726 *(p
++) = crc
& 0xff; crc
>>= 8;
727 *(p
++) = crc
& 0xff; crc
>>= 8;
731 *(p
++) = buf
[size
- 1];
737 /* TODO: Raise early RX interrupt? */
738 s
->int_level
|= INT_RCV
;
744 static const MemoryRegionOps smc91c111_mem_ops
= {
745 /* The special case for 32 bit writes to 0xc means we can't just
746 * set .impl.min/max_access_size to 1, unfortunately
749 .read
= { smc91c111_readb
, smc91c111_readw
, smc91c111_readl
, },
750 .write
= { smc91c111_writeb
, smc91c111_writew
, smc91c111_writel
, },
752 .endianness
= DEVICE_NATIVE_ENDIAN
,
755 static NetClientInfo net_smc91c111_info
= {
756 .type
= NET_CLIENT_OPTIONS_KIND_NIC
,
757 .size
= sizeof(NICState
),
758 .can_receive
= smc91c111_can_receive_nc
,
759 .receive
= smc91c111_receive
,
762 static int smc91c111_init1(SysBusDevice
*sbd
)
764 DeviceState
*dev
= DEVICE(sbd
);
765 smc91c111_state
*s
= SMC91C111(dev
);
767 memory_region_init_io(&s
->mmio
, OBJECT(s
), &smc91c111_mem_ops
, s
,
768 "smc91c111-mmio", 16);
769 sysbus_init_mmio(sbd
, &s
->mmio
);
770 sysbus_init_irq(sbd
, &s
->irq
);
771 qemu_macaddr_default_if_unset(&s
->conf
.macaddr
);
772 s
->nic
= qemu_new_nic(&net_smc91c111_info
, &s
->conf
,
773 object_get_typename(OBJECT(dev
)), dev
->id
, s
);
774 qemu_format_nic_info_str(qemu_get_queue(s
->nic
), s
->conf
.macaddr
.a
);
775 /* ??? Save/restore. */
779 static Property smc91c111_properties
[] = {
780 DEFINE_NIC_PROPERTIES(smc91c111_state
, conf
),
781 DEFINE_PROP_END_OF_LIST(),
784 static void smc91c111_class_init(ObjectClass
*klass
, void *data
)
786 DeviceClass
*dc
= DEVICE_CLASS(klass
);
787 SysBusDeviceClass
*k
= SYS_BUS_DEVICE_CLASS(klass
);
789 k
->init
= smc91c111_init1
;
790 dc
->reset
= smc91c111_reset
;
791 dc
->vmsd
= &vmstate_smc91c111
;
792 dc
->props
= smc91c111_properties
;
795 static const TypeInfo smc91c111_info
= {
796 .name
= TYPE_SMC91C111
,
797 .parent
= TYPE_SYS_BUS_DEVICE
,
798 .instance_size
= sizeof(smc91c111_state
),
799 .class_init
= smc91c111_class_init
,
802 static void smc91c111_register_types(void)
804 type_register_static(&smc91c111_info
);
807 /* Legacy helper function. Should go away when machine config files are
809 void smc91c111_init(NICInfo
*nd
, uint32_t base
, qemu_irq irq
)
814 qemu_check_nic_model(nd
, "smc91c111");
815 dev
= qdev_create(NULL
, TYPE_SMC91C111
);
816 qdev_set_nic_properties(dev
, nd
);
817 qdev_init_nofail(dev
);
818 s
= SYS_BUS_DEVICE(dev
);
819 sysbus_mmio_map(s
, 0, base
);
820 sysbus_connect_irq(s
, 0, irq
);
823 type_init(smc91c111_register_types
)