xref: /openbmc/qemu/hw/net/ne2000.c (revision d328fef93ae757a0dd65ed786a4086e27952eef3)
149ab747fSPaolo Bonzini /*
249ab747fSPaolo Bonzini  * QEMU NE2000 emulation
349ab747fSPaolo Bonzini  *
449ab747fSPaolo Bonzini  * Copyright (c) 2003-2004 Fabrice Bellard
549ab747fSPaolo Bonzini  *
649ab747fSPaolo Bonzini  * Permission is hereby granted, free of charge, to any person obtaining a copy
749ab747fSPaolo Bonzini  * of this software and associated documentation files (the "Software"), to deal
849ab747fSPaolo Bonzini  * in the Software without restriction, including without limitation the rights
949ab747fSPaolo Bonzini  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1049ab747fSPaolo Bonzini  * copies of the Software, and to permit persons to whom the Software is
1149ab747fSPaolo Bonzini  * furnished to do so, subject to the following conditions:
1249ab747fSPaolo Bonzini  *
1349ab747fSPaolo Bonzini  * The above copyright notice and this permission notice shall be included in
1449ab747fSPaolo Bonzini  * all copies or substantial portions of the Software.
1549ab747fSPaolo Bonzini  *
1649ab747fSPaolo Bonzini  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1749ab747fSPaolo Bonzini  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1849ab747fSPaolo Bonzini  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
1949ab747fSPaolo Bonzini  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2049ab747fSPaolo Bonzini  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2149ab747fSPaolo Bonzini  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
2249ab747fSPaolo Bonzini  * THE SOFTWARE.
2349ab747fSPaolo Bonzini  */
240b8fa32fSMarkus Armbruster 
25e8d40465SPeter Maydell #include "qemu/osdep.h"
26084e2b11SMark Cave-Ayland #include "net/eth.h"
270b8fa32fSMarkus Armbruster #include "qemu/module.h"
28d4842052SMarkus Armbruster #include "exec/memory.h"
2964552b6bSMarkus Armbruster #include "hw/irq.h"
30d6454270SMarkus Armbruster #include "migration/vmstate.h"
3147b43a1fSPaolo Bonzini #include "ne2000.h"
32cd4479a9SPhilippe Mathieu-Daudé #include "trace.h"
3349ab747fSPaolo Bonzini 
3449ab747fSPaolo Bonzini /* debug NE2000 card */
3549ab747fSPaolo Bonzini //#define DEBUG_NE2000
3649ab747fSPaolo Bonzini 
3749ab747fSPaolo Bonzini #define MAX_ETH_FRAME_SIZE 1514
3849ab747fSPaolo Bonzini 
3949ab747fSPaolo Bonzini #define E8390_CMD       0x00    /* The command register (for all pages) */
4049ab747fSPaolo Bonzini /* Page 0 register offsets. */
4149ab747fSPaolo Bonzini #define EN0_CLDALO      0x01    /* Low byte of current local dma addr  RD */
4249ab747fSPaolo Bonzini #define EN0_STARTPG     0x01    /* Starting page of ring bfr WR */
4349ab747fSPaolo Bonzini #define EN0_CLDAHI      0x02    /* High byte of current local dma addr  RD */
4449ab747fSPaolo Bonzini #define EN0_STOPPG      0x02    /* Ending page +1 of ring bfr WR */
4549ab747fSPaolo Bonzini #define EN0_BOUNDARY    0x03    /* Boundary page of ring bfr RD WR */
4649ab747fSPaolo Bonzini #define EN0_TSR         0x04    /* Transmit status reg RD */
4749ab747fSPaolo Bonzini #define EN0_TPSR        0x04    /* Transmit starting page WR */
4849ab747fSPaolo Bonzini #define EN0_NCR         0x05    /* Number of collision reg RD */
4949ab747fSPaolo Bonzini #define EN0_TCNTLO      0x05    /* Low  byte of tx byte count WR */
5049ab747fSPaolo Bonzini #define EN0_FIFO        0x06    /* FIFO RD */
5149ab747fSPaolo Bonzini #define EN0_TCNTHI      0x06    /* High byte of tx byte count WR */
5249ab747fSPaolo Bonzini #define EN0_ISR         0x07    /* Interrupt status reg RD WR */
5349ab747fSPaolo Bonzini #define EN0_CRDALO      0x08    /* low byte of current remote dma address RD */
5449ab747fSPaolo Bonzini #define EN0_RSARLO      0x08    /* Remote start address reg 0 */
5549ab747fSPaolo Bonzini #define EN0_CRDAHI      0x09    /* high byte, current remote dma address RD */
5649ab747fSPaolo Bonzini #define EN0_RSARHI      0x09    /* Remote start address reg 1 */
5749ab747fSPaolo Bonzini #define EN0_RCNTLO      0x0a    /* Remote byte count reg WR */
5849ab747fSPaolo Bonzini #define EN0_RTL8029ID0  0x0a    /* Realtek ID byte #1 RD */
5949ab747fSPaolo Bonzini #define EN0_RCNTHI      0x0b    /* Remote byte count reg WR */
6049ab747fSPaolo Bonzini #define EN0_RTL8029ID1  0x0b    /* Realtek ID byte #2 RD */
6149ab747fSPaolo Bonzini #define EN0_RSR         0x0c    /* rx status reg RD */
6249ab747fSPaolo Bonzini #define EN0_RXCR        0x0c    /* RX configuration reg WR */
6349ab747fSPaolo Bonzini #define EN0_TXCR        0x0d    /* TX configuration reg WR */
6449ab747fSPaolo Bonzini #define EN0_COUNTER0    0x0d    /* Rcv alignment error counter RD */
6549ab747fSPaolo Bonzini #define EN0_DCFG        0x0e    /* Data configuration reg WR */
6649ab747fSPaolo Bonzini #define EN0_COUNTER1    0x0e    /* Rcv CRC error counter RD */
6749ab747fSPaolo Bonzini #define EN0_IMR         0x0f    /* Interrupt mask reg WR */
6849ab747fSPaolo Bonzini #define EN0_COUNTER2    0x0f    /* Rcv missed frame error counter RD */
6949ab747fSPaolo Bonzini 
7049ab747fSPaolo Bonzini #define EN1_PHYS        0x11
7149ab747fSPaolo Bonzini #define EN1_CURPAG      0x17
7249ab747fSPaolo Bonzini #define EN1_MULT        0x18
7349ab747fSPaolo Bonzini 
7449ab747fSPaolo Bonzini #define EN2_STARTPG     0x21    /* Starting page of ring bfr RD */
7549ab747fSPaolo Bonzini #define EN2_STOPPG      0x22    /* Ending page +1 of ring bfr RD */
7649ab747fSPaolo Bonzini 
7749ab747fSPaolo Bonzini #define EN3_CONFIG0     0x33
7849ab747fSPaolo Bonzini #define EN3_CONFIG1     0x34
7949ab747fSPaolo Bonzini #define EN3_CONFIG2     0x35
8049ab747fSPaolo Bonzini #define EN3_CONFIG3     0x36
8149ab747fSPaolo Bonzini 
8249ab747fSPaolo Bonzini /*  Register accessed at EN_CMD, the 8390 base addr.  */
8349ab747fSPaolo Bonzini #define E8390_STOP      0x01    /* Stop and reset the chip */
8449ab747fSPaolo Bonzini #define E8390_START     0x02    /* Start the chip, clear reset */
8549ab747fSPaolo Bonzini #define E8390_TRANS     0x04    /* Transmit a frame */
8649ab747fSPaolo Bonzini #define E8390_RREAD     0x08    /* Remote read */
8749ab747fSPaolo Bonzini #define E8390_RWRITE    0x10    /* Remote write  */
8849ab747fSPaolo Bonzini #define E8390_NODMA     0x20    /* Remote DMA */
8949ab747fSPaolo Bonzini #define E8390_PAGE0     0x00    /* Select page chip registers */
9049ab747fSPaolo Bonzini #define E8390_PAGE1     0x40    /* using the two high-order bits */
9149ab747fSPaolo Bonzini #define E8390_PAGE2     0x80    /* Page 3 is invalid. */
9249ab747fSPaolo Bonzini 
9349ab747fSPaolo Bonzini /* Bits in EN0_ISR - Interrupt status register */
9449ab747fSPaolo Bonzini #define ENISR_RX        0x01    /* Receiver, no error */
9549ab747fSPaolo Bonzini #define ENISR_TX        0x02    /* Transmitter, no error */
9649ab747fSPaolo Bonzini #define ENISR_RX_ERR    0x04    /* Receiver, with error */
9749ab747fSPaolo Bonzini #define ENISR_TX_ERR    0x08    /* Transmitter, with error */
9849ab747fSPaolo Bonzini #define ENISR_OVER      0x10    /* Receiver overwrote the ring */
9949ab747fSPaolo Bonzini #define ENISR_COUNTERS  0x20    /* Counters need emptying */
10049ab747fSPaolo Bonzini #define ENISR_RDC       0x40    /* remote dma complete */
10149ab747fSPaolo Bonzini #define ENISR_RESET     0x80    /* Reset completed */
10249ab747fSPaolo Bonzini #define ENISR_ALL       0x3f    /* Interrupts we will enable */
10349ab747fSPaolo Bonzini 
10449ab747fSPaolo Bonzini /* Bits in received packet status byte and EN0_RSR*/
10549ab747fSPaolo Bonzini #define ENRSR_RXOK      0x01    /* Received a good packet */
10649ab747fSPaolo Bonzini #define ENRSR_CRC       0x02    /* CRC error */
10749ab747fSPaolo Bonzini #define ENRSR_FAE       0x04    /* frame alignment error */
10849ab747fSPaolo Bonzini #define ENRSR_FO        0x08    /* FIFO overrun */
10949ab747fSPaolo Bonzini #define ENRSR_MPA       0x10    /* missed pkt */
11049ab747fSPaolo Bonzini #define ENRSR_PHY       0x20    /* physical/multicast address */
11149ab747fSPaolo Bonzini #define ENRSR_DIS       0x40    /* receiver disable. set in monitor mode */
11249ab747fSPaolo Bonzini #define ENRSR_DEF       0x80    /* deferring */
11349ab747fSPaolo Bonzini 
11449ab747fSPaolo Bonzini /* Transmitted packet status, EN0_TSR. */
11549ab747fSPaolo Bonzini #define ENTSR_PTX 0x01  /* Packet transmitted without error */
11649ab747fSPaolo Bonzini #define ENTSR_ND  0x02  /* The transmit wasn't deferred. */
11749ab747fSPaolo Bonzini #define ENTSR_COL 0x04  /* The transmit collided at least once. */
11849ab747fSPaolo Bonzini #define ENTSR_ABT 0x08  /* The transmit collided 16 times, and was deferred. */
11949ab747fSPaolo Bonzini #define ENTSR_CRS 0x10  /* The carrier sense was lost. */
12049ab747fSPaolo Bonzini #define ENTSR_FU  0x20  /* A "FIFO underrun" occurred during transmit. */
12149ab747fSPaolo Bonzini #define ENTSR_CDH 0x40  /* The collision detect "heartbeat" signal was lost. */
12249ab747fSPaolo Bonzini #define ENTSR_OWC 0x80  /* There was an out-of-window collision. */
12349ab747fSPaolo Bonzini 
ne2000_reset(NE2000State * s)12449ab747fSPaolo Bonzini void ne2000_reset(NE2000State *s)
12549ab747fSPaolo Bonzini {
12649ab747fSPaolo Bonzini     int i;
12749ab747fSPaolo Bonzini 
12849ab747fSPaolo Bonzini     s->isr = ENISR_RESET;
12949ab747fSPaolo Bonzini     memcpy(s->mem, &s->c.macaddr, 6);
13049ab747fSPaolo Bonzini     s->mem[14] = 0x57;
13149ab747fSPaolo Bonzini     s->mem[15] = 0x57;
13249ab747fSPaolo Bonzini 
13349ab747fSPaolo Bonzini     /* duplicate prom data */
13449ab747fSPaolo Bonzini     for(i = 15;i >= 0; i--) {
13549ab747fSPaolo Bonzini         s->mem[2 * i] = s->mem[i];
13649ab747fSPaolo Bonzini         s->mem[2 * i + 1] = s->mem[i];
13749ab747fSPaolo Bonzini     }
13849ab747fSPaolo Bonzini }
13949ab747fSPaolo Bonzini 
ne2000_update_irq(NE2000State * s)14049ab747fSPaolo Bonzini static void ne2000_update_irq(NE2000State *s)
14149ab747fSPaolo Bonzini {
14249ab747fSPaolo Bonzini     int isr;
14349ab747fSPaolo Bonzini     isr = (s->isr & s->imr) & 0x7f;
14449ab747fSPaolo Bonzini #if defined(DEBUG_NE2000)
14549ab747fSPaolo Bonzini     printf("NE2000: Set IRQ to %d (%02x %02x)\n",
14649ab747fSPaolo Bonzini            isr ? 1 : 0, s->isr, s->imr);
14749ab747fSPaolo Bonzini #endif
14849ab747fSPaolo Bonzini     qemu_set_irq(s->irq, (isr != 0));
14949ab747fSPaolo Bonzini }
15049ab747fSPaolo Bonzini 
ne2000_buffer_full(NE2000State * s)15149ab747fSPaolo Bonzini static int ne2000_buffer_full(NE2000State *s)
15249ab747fSPaolo Bonzini {
15349ab747fSPaolo Bonzini     int avail, index, boundary;
15449ab747fSPaolo Bonzini 
155415ab35aSPrasad J Pandit     if (s->stop <= s->start) {
156415ab35aSPrasad J Pandit         return 1;
157415ab35aSPrasad J Pandit     }
158415ab35aSPrasad J Pandit 
15949ab747fSPaolo Bonzini     index = s->curpag << 8;
16049ab747fSPaolo Bonzini     boundary = s->boundary << 8;
16149ab747fSPaolo Bonzini     if (index < boundary)
16249ab747fSPaolo Bonzini         avail = boundary - index;
16349ab747fSPaolo Bonzini     else
16449ab747fSPaolo Bonzini         avail = (s->stop - s->start) - (index - boundary);
16549ab747fSPaolo Bonzini     if (avail < (MAX_ETH_FRAME_SIZE + 4))
16649ab747fSPaolo Bonzini         return 1;
16749ab747fSPaolo Bonzini     return 0;
16849ab747fSPaolo Bonzini }
16949ab747fSPaolo Bonzini 
ne2000_receive(NetClientState * nc,const uint8_t * buf,size_t size_)17049ab747fSPaolo Bonzini ssize_t ne2000_receive(NetClientState *nc, const uint8_t *buf, size_t size_)
17149ab747fSPaolo Bonzini {
17249ab747fSPaolo Bonzini     NE2000State *s = qemu_get_nic_opaque(nc);
173fdc89e90SJason Wang     size_t size = size_;
17449ab747fSPaolo Bonzini     uint8_t *p;
17549ab747fSPaolo Bonzini     unsigned int total_len, next, avail, len, index, mcast_idx;
17649ab747fSPaolo Bonzini     static const uint8_t broadcast_macaddr[6] =
17749ab747fSPaolo Bonzini         { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
17849ab747fSPaolo Bonzini 
17949ab747fSPaolo Bonzini #if defined(DEBUG_NE2000)
180fdc89e90SJason Wang     printf("NE2000: received len=%zu\n", size);
18149ab747fSPaolo Bonzini #endif
18249ab747fSPaolo Bonzini 
18349ab747fSPaolo Bonzini     if (s->cmd & E8390_STOP || ne2000_buffer_full(s))
18449ab747fSPaolo Bonzini         return -1;
18549ab747fSPaolo Bonzini 
18649ab747fSPaolo Bonzini     /* XXX: check this */
18749ab747fSPaolo Bonzini     if (s->rxcr & 0x10) {
18849ab747fSPaolo Bonzini         /* promiscuous: receive all */
18949ab747fSPaolo Bonzini     } else {
19049ab747fSPaolo Bonzini         if (!memcmp(buf,  broadcast_macaddr, 6)) {
19149ab747fSPaolo Bonzini             /* broadcast address */
19249ab747fSPaolo Bonzini             if (!(s->rxcr & 0x04))
19349ab747fSPaolo Bonzini                 return size;
19449ab747fSPaolo Bonzini         } else if (buf[0] & 0x01) {
19549ab747fSPaolo Bonzini             /* multicast */
19649ab747fSPaolo Bonzini             if (!(s->rxcr & 0x08))
19749ab747fSPaolo Bonzini                 return size;
198084e2b11SMark Cave-Ayland             mcast_idx = net_crc32(buf, ETH_ALEN) >> 26;
19949ab747fSPaolo Bonzini             if (!(s->mult[mcast_idx >> 3] & (1 << (mcast_idx & 7))))
20049ab747fSPaolo Bonzini                 return size;
20149ab747fSPaolo Bonzini         } else if (s->mem[0] == buf[0] &&
20249ab747fSPaolo Bonzini                    s->mem[2] == buf[1] &&
20349ab747fSPaolo Bonzini                    s->mem[4] == buf[2] &&
20449ab747fSPaolo Bonzini                    s->mem[6] == buf[3] &&
20549ab747fSPaolo Bonzini                    s->mem[8] == buf[4] &&
20649ab747fSPaolo Bonzini                    s->mem[10] == buf[5]) {
20749ab747fSPaolo Bonzini             /* match */
20849ab747fSPaolo Bonzini         } else {
20949ab747fSPaolo Bonzini             return size;
21049ab747fSPaolo Bonzini         }
21149ab747fSPaolo Bonzini     }
21249ab747fSPaolo Bonzini 
21349ab747fSPaolo Bonzini     index = s->curpag << 8;
2149bbdbc66SP J P     if (index >= NE2000_PMEM_END) {
2159bbdbc66SP J P         index = s->start;
2169bbdbc66SP J P     }
21749ab747fSPaolo Bonzini     /* 4 bytes for header */
21849ab747fSPaolo Bonzini     total_len = size + 4;
21949ab747fSPaolo Bonzini     /* address for next packet (4 bytes for CRC) */
22049ab747fSPaolo Bonzini     next = index + ((total_len + 4 + 255) & ~0xff);
22149ab747fSPaolo Bonzini     if (next >= s->stop)
22249ab747fSPaolo Bonzini         next -= (s->stop - s->start);
22349ab747fSPaolo Bonzini     /* prepare packet header */
22449ab747fSPaolo Bonzini     p = s->mem + index;
22549ab747fSPaolo Bonzini     s->rsr = ENRSR_RXOK; /* receive status */
22649ab747fSPaolo Bonzini     /* XXX: check this */
22749ab747fSPaolo Bonzini     if (buf[0] & 0x01)
22849ab747fSPaolo Bonzini         s->rsr |= ENRSR_PHY;
22949ab747fSPaolo Bonzini     p[0] = s->rsr;
23049ab747fSPaolo Bonzini     p[1] = next >> 8;
23149ab747fSPaolo Bonzini     p[2] = total_len;
23249ab747fSPaolo Bonzini     p[3] = total_len >> 8;
23349ab747fSPaolo Bonzini     index += 4;
23449ab747fSPaolo Bonzini 
23549ab747fSPaolo Bonzini     /* write packet data */
23649ab747fSPaolo Bonzini     while (size > 0) {
23749ab747fSPaolo Bonzini         if (index <= s->stop)
23849ab747fSPaolo Bonzini             avail = s->stop - index;
23949ab747fSPaolo Bonzini         else
240737d2b3cSP J P             break;
24149ab747fSPaolo Bonzini         len = size;
24249ab747fSPaolo Bonzini         if (len > avail)
24349ab747fSPaolo Bonzini             len = avail;
24449ab747fSPaolo Bonzini         memcpy(s->mem + index, buf, len);
24549ab747fSPaolo Bonzini         buf += len;
24649ab747fSPaolo Bonzini         index += len;
24749ab747fSPaolo Bonzini         if (index == s->stop)
24849ab747fSPaolo Bonzini             index = s->start;
24949ab747fSPaolo Bonzini         size -= len;
25049ab747fSPaolo Bonzini     }
25149ab747fSPaolo Bonzini     s->curpag = next >> 8;
25249ab747fSPaolo Bonzini 
25349ab747fSPaolo Bonzini     /* now we can signal we have received something */
25449ab747fSPaolo Bonzini     s->isr |= ENISR_RX;
25549ab747fSPaolo Bonzini     ne2000_update_irq(s);
25649ab747fSPaolo Bonzini 
25749ab747fSPaolo Bonzini     return size_;
25849ab747fSPaolo Bonzini }
25949ab747fSPaolo Bonzini 
ne2000_ioport_write(void * opaque,uint32_t addr,uint32_t val)26049ab747fSPaolo Bonzini static void ne2000_ioport_write(void *opaque, uint32_t addr, uint32_t val)
26149ab747fSPaolo Bonzini {
26249ab747fSPaolo Bonzini     NE2000State *s = opaque;
26349ab747fSPaolo Bonzini     int offset, page, index;
26449ab747fSPaolo Bonzini 
26549ab747fSPaolo Bonzini     addr &= 0xf;
266a816b625SPhilippe Mathieu-Daudé     trace_ne2000_ioport_write(addr, val);
26749ab747fSPaolo Bonzini     if (addr == E8390_CMD) {
26849ab747fSPaolo Bonzini         /* control register */
26949ab747fSPaolo Bonzini         s->cmd = val;
27049ab747fSPaolo Bonzini         if (!(val & E8390_STOP)) { /* START bit makes no sense on RTL8029... */
27149ab747fSPaolo Bonzini             s->isr &= ~ENISR_RESET;
27249ab747fSPaolo Bonzini             /* test specific case: zero length transfer */
27349ab747fSPaolo Bonzini             if ((val & (E8390_RREAD | E8390_RWRITE)) &&
27449ab747fSPaolo Bonzini                 s->rcnt == 0) {
27549ab747fSPaolo Bonzini                 s->isr |= ENISR_RDC;
27649ab747fSPaolo Bonzini                 ne2000_update_irq(s);
27749ab747fSPaolo Bonzini             }
27849ab747fSPaolo Bonzini             if (val & E8390_TRANS) {
27949ab747fSPaolo Bonzini                 index = (s->tpsr << 8);
28049ab747fSPaolo Bonzini                 /* XXX: next 2 lines are a hack to make netware 3.11 work */
28149ab747fSPaolo Bonzini                 if (index >= NE2000_PMEM_END)
28249ab747fSPaolo Bonzini                     index -= NE2000_PMEM_SIZE;
28349ab747fSPaolo Bonzini                 /* fail safe: check range on the transmitted length  */
28449ab747fSPaolo Bonzini                 if (index + s->tcnt <= NE2000_PMEM_END) {
28549ab747fSPaolo Bonzini                     qemu_send_packet(qemu_get_queue(s->nic), s->mem + index,
28649ab747fSPaolo Bonzini                                      s->tcnt);
28749ab747fSPaolo Bonzini                 }
28849ab747fSPaolo Bonzini                 /* signal end of transfer */
28949ab747fSPaolo Bonzini                 s->tsr = ENTSR_PTX;
29049ab747fSPaolo Bonzini                 s->isr |= ENISR_TX;
29149ab747fSPaolo Bonzini                 s->cmd &= ~E8390_TRANS;
29249ab747fSPaolo Bonzini                 ne2000_update_irq(s);
29349ab747fSPaolo Bonzini             }
29449ab747fSPaolo Bonzini         }
29549ab747fSPaolo Bonzini     } else {
29649ab747fSPaolo Bonzini         page = s->cmd >> 6;
29749ab747fSPaolo Bonzini         offset = addr | (page << 4);
29849ab747fSPaolo Bonzini         switch(offset) {
29949ab747fSPaolo Bonzini         case EN0_STARTPG:
3009bbdbc66SP J P             if (val << 8 <= NE2000_PMEM_END) {
30149ab747fSPaolo Bonzini                 s->start = val << 8;
3029bbdbc66SP J P             }
30349ab747fSPaolo Bonzini             break;
30449ab747fSPaolo Bonzini         case EN0_STOPPG:
3059bbdbc66SP J P             if (val << 8 <= NE2000_PMEM_END) {
30649ab747fSPaolo Bonzini                 s->stop = val << 8;
3079bbdbc66SP J P             }
30849ab747fSPaolo Bonzini             break;
30949ab747fSPaolo Bonzini         case EN0_BOUNDARY:
3109bbdbc66SP J P             if (val << 8 < NE2000_PMEM_END) {
31149ab747fSPaolo Bonzini                 s->boundary = val;
3129bbdbc66SP J P             }
31349ab747fSPaolo Bonzini             break;
31449ab747fSPaolo Bonzini         case EN0_IMR:
31549ab747fSPaolo Bonzini             s->imr = val;
31649ab747fSPaolo Bonzini             ne2000_update_irq(s);
31749ab747fSPaolo Bonzini             break;
31849ab747fSPaolo Bonzini         case EN0_TPSR:
31949ab747fSPaolo Bonzini             s->tpsr = val;
32049ab747fSPaolo Bonzini             break;
32149ab747fSPaolo Bonzini         case EN0_TCNTLO:
32249ab747fSPaolo Bonzini             s->tcnt = (s->tcnt & 0xff00) | val;
32349ab747fSPaolo Bonzini             break;
32449ab747fSPaolo Bonzini         case EN0_TCNTHI:
32549ab747fSPaolo Bonzini             s->tcnt = (s->tcnt & 0x00ff) | (val << 8);
32649ab747fSPaolo Bonzini             break;
32749ab747fSPaolo Bonzini         case EN0_RSARLO:
32849ab747fSPaolo Bonzini             s->rsar = (s->rsar & 0xff00) | val;
32949ab747fSPaolo Bonzini             break;
33049ab747fSPaolo Bonzini         case EN0_RSARHI:
33149ab747fSPaolo Bonzini             s->rsar = (s->rsar & 0x00ff) | (val << 8);
33249ab747fSPaolo Bonzini             break;
33349ab747fSPaolo Bonzini         case EN0_RCNTLO:
33449ab747fSPaolo Bonzini             s->rcnt = (s->rcnt & 0xff00) | val;
33549ab747fSPaolo Bonzini             break;
33649ab747fSPaolo Bonzini         case EN0_RCNTHI:
33749ab747fSPaolo Bonzini             s->rcnt = (s->rcnt & 0x00ff) | (val << 8);
33849ab747fSPaolo Bonzini             break;
33949ab747fSPaolo Bonzini         case EN0_RXCR:
34049ab747fSPaolo Bonzini             s->rxcr = val;
34149ab747fSPaolo Bonzini             break;
34249ab747fSPaolo Bonzini         case EN0_DCFG:
34349ab747fSPaolo Bonzini             s->dcfg = val;
34449ab747fSPaolo Bonzini             break;
34549ab747fSPaolo Bonzini         case EN0_ISR:
34649ab747fSPaolo Bonzini             s->isr &= ~(val & 0x7f);
34749ab747fSPaolo Bonzini             ne2000_update_irq(s);
34849ab747fSPaolo Bonzini             break;
34949ab747fSPaolo Bonzini         case EN1_PHYS ... EN1_PHYS + 5:
35049ab747fSPaolo Bonzini             s->phys[offset - EN1_PHYS] = val;
35149ab747fSPaolo Bonzini             break;
35249ab747fSPaolo Bonzini         case EN1_CURPAG:
3539bbdbc66SP J P             if (val << 8 < NE2000_PMEM_END) {
35449ab747fSPaolo Bonzini                 s->curpag = val;
3559bbdbc66SP J P             }
35649ab747fSPaolo Bonzini             break;
35749ab747fSPaolo Bonzini         case EN1_MULT ... EN1_MULT + 7:
35849ab747fSPaolo Bonzini             s->mult[offset - EN1_MULT] = val;
35949ab747fSPaolo Bonzini             break;
36049ab747fSPaolo Bonzini         }
36149ab747fSPaolo Bonzini     }
36249ab747fSPaolo Bonzini }
36349ab747fSPaolo Bonzini 
ne2000_ioport_read(void * opaque,uint32_t addr)36449ab747fSPaolo Bonzini static uint32_t ne2000_ioport_read(void *opaque, uint32_t addr)
36549ab747fSPaolo Bonzini {
36649ab747fSPaolo Bonzini     NE2000State *s = opaque;
36749ab747fSPaolo Bonzini     int offset, page, ret;
36849ab747fSPaolo Bonzini 
36949ab747fSPaolo Bonzini     addr &= 0xf;
37049ab747fSPaolo Bonzini     if (addr == E8390_CMD) {
37149ab747fSPaolo Bonzini         ret = s->cmd;
37249ab747fSPaolo Bonzini     } else {
37349ab747fSPaolo Bonzini         page = s->cmd >> 6;
37449ab747fSPaolo Bonzini         offset = addr | (page << 4);
37549ab747fSPaolo Bonzini         switch(offset) {
37649ab747fSPaolo Bonzini         case EN0_TSR:
37749ab747fSPaolo Bonzini             ret = s->tsr;
37849ab747fSPaolo Bonzini             break;
37949ab747fSPaolo Bonzini         case EN0_BOUNDARY:
38049ab747fSPaolo Bonzini             ret = s->boundary;
38149ab747fSPaolo Bonzini             break;
38249ab747fSPaolo Bonzini         case EN0_ISR:
38349ab747fSPaolo Bonzini             ret = s->isr;
38449ab747fSPaolo Bonzini             break;
38549ab747fSPaolo Bonzini         case EN0_RSARLO:
38649ab747fSPaolo Bonzini             ret = s->rsar & 0x00ff;
38749ab747fSPaolo Bonzini             break;
38849ab747fSPaolo Bonzini         case EN0_RSARHI:
38949ab747fSPaolo Bonzini             ret = s->rsar >> 8;
39049ab747fSPaolo Bonzini             break;
39149ab747fSPaolo Bonzini         case EN1_PHYS ... EN1_PHYS + 5:
39249ab747fSPaolo Bonzini             ret = s->phys[offset - EN1_PHYS];
39349ab747fSPaolo Bonzini             break;
39449ab747fSPaolo Bonzini         case EN1_CURPAG:
39549ab747fSPaolo Bonzini             ret = s->curpag;
39649ab747fSPaolo Bonzini             break;
39749ab747fSPaolo Bonzini         case EN1_MULT ... EN1_MULT + 7:
39849ab747fSPaolo Bonzini             ret = s->mult[offset - EN1_MULT];
39949ab747fSPaolo Bonzini             break;
40049ab747fSPaolo Bonzini         case EN0_RSR:
40149ab747fSPaolo Bonzini             ret = s->rsr;
40249ab747fSPaolo Bonzini             break;
40349ab747fSPaolo Bonzini         case EN2_STARTPG:
40449ab747fSPaolo Bonzini             ret = s->start >> 8;
40549ab747fSPaolo Bonzini             break;
40649ab747fSPaolo Bonzini         case EN2_STOPPG:
40749ab747fSPaolo Bonzini             ret = s->stop >> 8;
40849ab747fSPaolo Bonzini             break;
40949ab747fSPaolo Bonzini         case EN0_RTL8029ID0:
41049ab747fSPaolo Bonzini             ret = 0x50;
41149ab747fSPaolo Bonzini             break;
41249ab747fSPaolo Bonzini         case EN0_RTL8029ID1:
41349ab747fSPaolo Bonzini             ret = 0x43;
41449ab747fSPaolo Bonzini             break;
41549ab747fSPaolo Bonzini         case EN3_CONFIG0:
41649ab747fSPaolo Bonzini             ret = 0;          /* 10baseT media */
41749ab747fSPaolo Bonzini             break;
41849ab747fSPaolo Bonzini         case EN3_CONFIG2:
41949ab747fSPaolo Bonzini             ret = 0x40;       /* 10baseT active */
42049ab747fSPaolo Bonzini             break;
42149ab747fSPaolo Bonzini         case EN3_CONFIG3:
42249ab747fSPaolo Bonzini             ret = 0x40;       /* Full duplex */
42349ab747fSPaolo Bonzini             break;
42449ab747fSPaolo Bonzini         default:
42549ab747fSPaolo Bonzini             ret = 0x00;
42649ab747fSPaolo Bonzini             break;
42749ab747fSPaolo Bonzini         }
42849ab747fSPaolo Bonzini     }
429a816b625SPhilippe Mathieu-Daudé     trace_ne2000_ioport_read(addr, ret);
43049ab747fSPaolo Bonzini     return ret;
43149ab747fSPaolo Bonzini }
43249ab747fSPaolo Bonzini 
ne2000_mem_writeb(NE2000State * s,uint32_t addr,uint32_t val)43349ab747fSPaolo Bonzini static inline void ne2000_mem_writeb(NE2000State *s, uint32_t addr,
43449ab747fSPaolo Bonzini                                      uint32_t val)
43549ab747fSPaolo Bonzini {
43649ab747fSPaolo Bonzini     if (addr < 32 ||
43749ab747fSPaolo Bonzini         (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) {
43849ab747fSPaolo Bonzini         s->mem[addr] = val;
43949ab747fSPaolo Bonzini     }
44049ab747fSPaolo Bonzini }
44149ab747fSPaolo Bonzini 
ne2000_mem_writew(NE2000State * s,uint32_t addr,uint32_t val)44249ab747fSPaolo Bonzini static inline void ne2000_mem_writew(NE2000State *s, uint32_t addr,
44349ab747fSPaolo Bonzini                                      uint32_t val)
44449ab747fSPaolo Bonzini {
44549ab747fSPaolo Bonzini     addr &= ~1; /* XXX: check exact behaviour if not even */
44649ab747fSPaolo Bonzini     if (addr < 32 ||
44749ab747fSPaolo Bonzini         (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) {
44849ab747fSPaolo Bonzini         *(uint16_t *)(s->mem + addr) = cpu_to_le16(val);
44949ab747fSPaolo Bonzini     }
45049ab747fSPaolo Bonzini }
45149ab747fSPaolo Bonzini 
ne2000_mem_writel(NE2000State * s,uint32_t addr,uint32_t val)45249ab747fSPaolo Bonzini static inline void ne2000_mem_writel(NE2000State *s, uint32_t addr,
45349ab747fSPaolo Bonzini                                      uint32_t val)
45449ab747fSPaolo Bonzini {
45549ab747fSPaolo Bonzini     addr &= ~1; /* XXX: check exact behaviour if not even */
456aa7f9966SPrasad J Pandit     if (addr < 32
457aa7f9966SPrasad J Pandit         || (addr >= NE2000_PMEM_START
458aa7f9966SPrasad J Pandit             && addr + sizeof(uint32_t) <= NE2000_MEM_SIZE)) {
4596e931878SPeter Maydell         stl_le_p(s->mem + addr, val);
46049ab747fSPaolo Bonzini     }
46149ab747fSPaolo Bonzini }
46249ab747fSPaolo Bonzini 
ne2000_mem_readb(NE2000State * s,uint32_t addr)46349ab747fSPaolo Bonzini static inline uint32_t ne2000_mem_readb(NE2000State *s, uint32_t addr)
46449ab747fSPaolo Bonzini {
46549ab747fSPaolo Bonzini     if (addr < 32 ||
46649ab747fSPaolo Bonzini         (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) {
46749ab747fSPaolo Bonzini         return s->mem[addr];
46849ab747fSPaolo Bonzini     } else {
46949ab747fSPaolo Bonzini         return 0xff;
47049ab747fSPaolo Bonzini     }
47149ab747fSPaolo Bonzini }
47249ab747fSPaolo Bonzini 
ne2000_mem_readw(NE2000State * s,uint32_t addr)47349ab747fSPaolo Bonzini static inline uint32_t ne2000_mem_readw(NE2000State *s, uint32_t addr)
47449ab747fSPaolo Bonzini {
47549ab747fSPaolo Bonzini     addr &= ~1; /* XXX: check exact behaviour if not even */
47649ab747fSPaolo Bonzini     if (addr < 32 ||
47749ab747fSPaolo Bonzini         (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) {
47849ab747fSPaolo Bonzini         return le16_to_cpu(*(uint16_t *)(s->mem + addr));
47949ab747fSPaolo Bonzini     } else {
48049ab747fSPaolo Bonzini         return 0xffff;
48149ab747fSPaolo Bonzini     }
48249ab747fSPaolo Bonzini }
48349ab747fSPaolo Bonzini 
ne2000_mem_readl(NE2000State * s,uint32_t addr)48449ab747fSPaolo Bonzini static inline uint32_t ne2000_mem_readl(NE2000State *s, uint32_t addr)
48549ab747fSPaolo Bonzini {
48649ab747fSPaolo Bonzini     addr &= ~1; /* XXX: check exact behaviour if not even */
487aa7f9966SPrasad J Pandit     if (addr < 32
488aa7f9966SPrasad J Pandit         || (addr >= NE2000_PMEM_START
489aa7f9966SPrasad J Pandit             && addr + sizeof(uint32_t) <= NE2000_MEM_SIZE)) {
490f567656aSPeter Maydell         return ldl_le_p(s->mem + addr);
49149ab747fSPaolo Bonzini     } else {
49249ab747fSPaolo Bonzini         return 0xffffffff;
49349ab747fSPaolo Bonzini     }
49449ab747fSPaolo Bonzini }
49549ab747fSPaolo Bonzini 
ne2000_dma_update(NE2000State * s,int len)49649ab747fSPaolo Bonzini static inline void ne2000_dma_update(NE2000State *s, int len)
49749ab747fSPaolo Bonzini {
49849ab747fSPaolo Bonzini     s->rsar += len;
49949ab747fSPaolo Bonzini     /* wrap */
50049ab747fSPaolo Bonzini     /* XXX: check what to do if rsar > stop */
50149ab747fSPaolo Bonzini     if (s->rsar == s->stop)
50249ab747fSPaolo Bonzini         s->rsar = s->start;
50349ab747fSPaolo Bonzini 
50449ab747fSPaolo Bonzini     if (s->rcnt <= len) {
50549ab747fSPaolo Bonzini         s->rcnt = 0;
50649ab747fSPaolo Bonzini         /* signal end of transfer */
50749ab747fSPaolo Bonzini         s->isr |= ENISR_RDC;
50849ab747fSPaolo Bonzini         ne2000_update_irq(s);
50949ab747fSPaolo Bonzini     } else {
51049ab747fSPaolo Bonzini         s->rcnt -= len;
51149ab747fSPaolo Bonzini     }
51249ab747fSPaolo Bonzini }
51349ab747fSPaolo Bonzini 
ne2000_asic_ioport_write(void * opaque,uint32_t addr,uint32_t val)51449ab747fSPaolo Bonzini static void ne2000_asic_ioport_write(void *opaque, uint32_t addr, uint32_t val)
51549ab747fSPaolo Bonzini {
51649ab747fSPaolo Bonzini     NE2000State *s = opaque;
51749ab747fSPaolo Bonzini 
51849ab747fSPaolo Bonzini #ifdef DEBUG_NE2000
51949ab747fSPaolo Bonzini     printf("NE2000: asic write val=0x%04x\n", val);
52049ab747fSPaolo Bonzini #endif
52149ab747fSPaolo Bonzini     if (s->rcnt == 0)
52249ab747fSPaolo Bonzini         return;
52349ab747fSPaolo Bonzini     if (s->dcfg & 0x01) {
52449ab747fSPaolo Bonzini         /* 16 bit access */
52549ab747fSPaolo Bonzini         ne2000_mem_writew(s, s->rsar, val);
52649ab747fSPaolo Bonzini         ne2000_dma_update(s, 2);
52749ab747fSPaolo Bonzini     } else {
52849ab747fSPaolo Bonzini         /* 8 bit access */
52949ab747fSPaolo Bonzini         ne2000_mem_writeb(s, s->rsar, val);
53049ab747fSPaolo Bonzini         ne2000_dma_update(s, 1);
53149ab747fSPaolo Bonzini     }
53249ab747fSPaolo Bonzini }
53349ab747fSPaolo Bonzini 
ne2000_asic_ioport_read(void * opaque,uint32_t addr)53449ab747fSPaolo Bonzini static uint32_t ne2000_asic_ioport_read(void *opaque, uint32_t addr)
53549ab747fSPaolo Bonzini {
53649ab747fSPaolo Bonzini     NE2000State *s = opaque;
53749ab747fSPaolo Bonzini     int ret;
53849ab747fSPaolo Bonzini 
53949ab747fSPaolo Bonzini     if (s->dcfg & 0x01) {
54049ab747fSPaolo Bonzini         /* 16 bit access */
54149ab747fSPaolo Bonzini         ret = ne2000_mem_readw(s, s->rsar);
54249ab747fSPaolo Bonzini         ne2000_dma_update(s, 2);
54349ab747fSPaolo Bonzini     } else {
54449ab747fSPaolo Bonzini         /* 8 bit access */
54549ab747fSPaolo Bonzini         ret = ne2000_mem_readb(s, s->rsar);
54649ab747fSPaolo Bonzini         ne2000_dma_update(s, 1);
54749ab747fSPaolo Bonzini     }
54849ab747fSPaolo Bonzini #ifdef DEBUG_NE2000
54949ab747fSPaolo Bonzini     printf("NE2000: asic read val=0x%04x\n", ret);
55049ab747fSPaolo Bonzini #endif
55149ab747fSPaolo Bonzini     return ret;
55249ab747fSPaolo Bonzini }
55349ab747fSPaolo Bonzini 
ne2000_asic_ioport_writel(void * opaque,uint32_t addr,uint32_t val)55449ab747fSPaolo Bonzini static void ne2000_asic_ioport_writel(void *opaque, uint32_t addr, uint32_t val)
55549ab747fSPaolo Bonzini {
55649ab747fSPaolo Bonzini     NE2000State *s = opaque;
55749ab747fSPaolo Bonzini 
55849ab747fSPaolo Bonzini #ifdef DEBUG_NE2000
55949ab747fSPaolo Bonzini     printf("NE2000: asic writel val=0x%04x\n", val);
56049ab747fSPaolo Bonzini #endif
56149ab747fSPaolo Bonzini     if (s->rcnt == 0)
56249ab747fSPaolo Bonzini         return;
56349ab747fSPaolo Bonzini     /* 32 bit access */
56449ab747fSPaolo Bonzini     ne2000_mem_writel(s, s->rsar, val);
56549ab747fSPaolo Bonzini     ne2000_dma_update(s, 4);
56649ab747fSPaolo Bonzini }
56749ab747fSPaolo Bonzini 
ne2000_asic_ioport_readl(void * opaque,uint32_t addr)56849ab747fSPaolo Bonzini static uint32_t ne2000_asic_ioport_readl(void *opaque, uint32_t addr)
56949ab747fSPaolo Bonzini {
57049ab747fSPaolo Bonzini     NE2000State *s = opaque;
57149ab747fSPaolo Bonzini     int ret;
57249ab747fSPaolo Bonzini 
57349ab747fSPaolo Bonzini     /* 32 bit access */
57449ab747fSPaolo Bonzini     ret = ne2000_mem_readl(s, s->rsar);
57549ab747fSPaolo Bonzini     ne2000_dma_update(s, 4);
57649ab747fSPaolo Bonzini #ifdef DEBUG_NE2000
57749ab747fSPaolo Bonzini     printf("NE2000: asic readl val=0x%04x\n", ret);
57849ab747fSPaolo Bonzini #endif
57949ab747fSPaolo Bonzini     return ret;
58049ab747fSPaolo Bonzini }
58149ab747fSPaolo Bonzini 
ne2000_reset_ioport_write(void * opaque,uint32_t addr,uint32_t val)58249ab747fSPaolo Bonzini static void ne2000_reset_ioport_write(void *opaque, uint32_t addr, uint32_t val)
58349ab747fSPaolo Bonzini {
58449ab747fSPaolo Bonzini     /* nothing to do (end of reset pulse) */
58549ab747fSPaolo Bonzini }
58649ab747fSPaolo Bonzini 
ne2000_reset_ioport_read(void * opaque,uint32_t addr)58749ab747fSPaolo Bonzini static uint32_t ne2000_reset_ioport_read(void *opaque, uint32_t addr)
58849ab747fSPaolo Bonzini {
58949ab747fSPaolo Bonzini     NE2000State *s = opaque;
59049ab747fSPaolo Bonzini     ne2000_reset(s);
59149ab747fSPaolo Bonzini     return 0;
59249ab747fSPaolo Bonzini }
59349ab747fSPaolo Bonzini 
ne2000_post_load(void * opaque,int version_id)59449ab747fSPaolo Bonzini static int ne2000_post_load(void* opaque, int version_id)
59549ab747fSPaolo Bonzini {
59649ab747fSPaolo Bonzini     NE2000State* s = opaque;
59749ab747fSPaolo Bonzini 
59849ab747fSPaolo Bonzini     if (version_id < 2) {
59949ab747fSPaolo Bonzini         s->rxcr = 0x0c;
60049ab747fSPaolo Bonzini     }
60149ab747fSPaolo Bonzini     return 0;
60249ab747fSPaolo Bonzini }
60349ab747fSPaolo Bonzini 
60449ab747fSPaolo Bonzini const VMStateDescription vmstate_ne2000 = {
60549ab747fSPaolo Bonzini     .name = "ne2000",
60649ab747fSPaolo Bonzini     .version_id = 2,
60749ab747fSPaolo Bonzini     .minimum_version_id = 0,
60849ab747fSPaolo Bonzini     .post_load = ne2000_post_load,
609*1de81b42SRichard Henderson     .fields = (const VMStateField[]) {
61049ab747fSPaolo Bonzini         VMSTATE_UINT8_V(rxcr, NE2000State, 2),
61149ab747fSPaolo Bonzini         VMSTATE_UINT8(cmd, NE2000State),
61249ab747fSPaolo Bonzini         VMSTATE_UINT32(start, NE2000State),
61349ab747fSPaolo Bonzini         VMSTATE_UINT32(stop, NE2000State),
61449ab747fSPaolo Bonzini         VMSTATE_UINT8(boundary, NE2000State),
61549ab747fSPaolo Bonzini         VMSTATE_UINT8(tsr, NE2000State),
61649ab747fSPaolo Bonzini         VMSTATE_UINT8(tpsr, NE2000State),
61749ab747fSPaolo Bonzini         VMSTATE_UINT16(tcnt, NE2000State),
61849ab747fSPaolo Bonzini         VMSTATE_UINT16(rcnt, NE2000State),
61949ab747fSPaolo Bonzini         VMSTATE_UINT32(rsar, NE2000State),
62049ab747fSPaolo Bonzini         VMSTATE_UINT8(rsr, NE2000State),
62149ab747fSPaolo Bonzini         VMSTATE_UINT8(isr, NE2000State),
62249ab747fSPaolo Bonzini         VMSTATE_UINT8(dcfg, NE2000State),
62349ab747fSPaolo Bonzini         VMSTATE_UINT8(imr, NE2000State),
62449ab747fSPaolo Bonzini         VMSTATE_BUFFER(phys, NE2000State),
62549ab747fSPaolo Bonzini         VMSTATE_UINT8(curpag, NE2000State),
62649ab747fSPaolo Bonzini         VMSTATE_BUFFER(mult, NE2000State),
62749ab747fSPaolo Bonzini         VMSTATE_UNUSED(4), /* was irq */
62849ab747fSPaolo Bonzini         VMSTATE_BUFFER(mem, NE2000State),
62949ab747fSPaolo Bonzini         VMSTATE_END_OF_LIST()
63049ab747fSPaolo Bonzini     }
63149ab747fSPaolo Bonzini };
63249ab747fSPaolo Bonzini 
ne2000_read(void * opaque,hwaddr addr,unsigned size)63349ab747fSPaolo Bonzini static uint64_t ne2000_read(void *opaque, hwaddr addr,
63449ab747fSPaolo Bonzini                             unsigned size)
63549ab747fSPaolo Bonzini {
63649ab747fSPaolo Bonzini     NE2000State *s = opaque;
637cd4479a9SPhilippe Mathieu-Daudé     uint64_t val;
63849ab747fSPaolo Bonzini 
63949ab747fSPaolo Bonzini     if (addr < 0x10 && size == 1) {
640cd4479a9SPhilippe Mathieu-Daudé         val = ne2000_ioport_read(s, addr);
64149ab747fSPaolo Bonzini     } else if (addr == 0x10) {
64249ab747fSPaolo Bonzini         if (size <= 2) {
643cd4479a9SPhilippe Mathieu-Daudé             val = ne2000_asic_ioport_read(s, addr);
64449ab747fSPaolo Bonzini         } else {
645cd4479a9SPhilippe Mathieu-Daudé             val = ne2000_asic_ioport_readl(s, addr);
64649ab747fSPaolo Bonzini         }
64749ab747fSPaolo Bonzini     } else if (addr == 0x1f && size == 1) {
648cd4479a9SPhilippe Mathieu-Daudé         val = ne2000_reset_ioport_read(s, addr);
649cd4479a9SPhilippe Mathieu-Daudé     } else {
650cd4479a9SPhilippe Mathieu-Daudé         val = ((uint64_t)1 << (size * 8)) - 1;
65149ab747fSPaolo Bonzini     }
652cd4479a9SPhilippe Mathieu-Daudé     trace_ne2000_read(addr, val);
653cd4479a9SPhilippe Mathieu-Daudé 
654cd4479a9SPhilippe Mathieu-Daudé     return val;
65549ab747fSPaolo Bonzini }
65649ab747fSPaolo Bonzini 
ne2000_write(void * opaque,hwaddr addr,uint64_t data,unsigned size)65749ab747fSPaolo Bonzini static void ne2000_write(void *opaque, hwaddr addr,
65849ab747fSPaolo Bonzini                          uint64_t data, unsigned size)
65949ab747fSPaolo Bonzini {
66049ab747fSPaolo Bonzini     NE2000State *s = opaque;
66149ab747fSPaolo Bonzini 
662cd4479a9SPhilippe Mathieu-Daudé     trace_ne2000_write(addr, data);
66349ab747fSPaolo Bonzini     if (addr < 0x10 && size == 1) {
66449ab747fSPaolo Bonzini         ne2000_ioport_write(s, addr, data);
66549ab747fSPaolo Bonzini     } else if (addr == 0x10) {
66649ab747fSPaolo Bonzini         if (size <= 2) {
66749ab747fSPaolo Bonzini             ne2000_asic_ioport_write(s, addr, data);
66849ab747fSPaolo Bonzini         } else {
66949ab747fSPaolo Bonzini             ne2000_asic_ioport_writel(s, addr, data);
67049ab747fSPaolo Bonzini         }
67149ab747fSPaolo Bonzini     } else if (addr == 0x1f && size == 1) {
67249ab747fSPaolo Bonzini         ne2000_reset_ioport_write(s, addr, data);
67349ab747fSPaolo Bonzini     }
67449ab747fSPaolo Bonzini }
67549ab747fSPaolo Bonzini 
67649ab747fSPaolo Bonzini static const MemoryRegionOps ne2000_ops = {
67749ab747fSPaolo Bonzini     .read = ne2000_read,
67849ab747fSPaolo Bonzini     .write = ne2000_write,
67945d883dcSAurelien Jarno     .endianness = DEVICE_LITTLE_ENDIAN,
68049ab747fSPaolo Bonzini };
68149ab747fSPaolo Bonzini 
68249ab747fSPaolo Bonzini /***********************************************************/
68349ab747fSPaolo Bonzini /* PCI NE2000 definitions */
68449ab747fSPaolo Bonzini 
ne2000_setup_io(NE2000State * s,DeviceState * dev,unsigned size)685dcb117bfSPaolo Bonzini void ne2000_setup_io(NE2000State *s, DeviceState *dev, unsigned size)
68649ab747fSPaolo Bonzini {
687dcb117bfSPaolo Bonzini     memory_region_init_io(&s->io, OBJECT(dev), &ne2000_ops, s, "ne2000", size);
68849ab747fSPaolo Bonzini }
689