From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from eggs.gnu.org ([140.186.70.92]:35732) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1QzDSo-0004Gg-3C for qemu-devel@nongnu.org; Thu, 01 Sep 2011 16:02:16 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1QzDSl-0003ob-2o for qemu-devel@nongnu.org; Thu, 01 Sep 2011 16:02:14 -0400 Received: from nm2-vm0.bullet.mail.ukl.yahoo.com ([217.146.183.226]:46206) by eggs.gnu.org with smtp (Exim 4.71) (envelope-from ) id 1QzDSk-0003oU-6s for qemu-devel@nongnu.org; Thu, 01 Sep 2011 16:02:11 -0400 Message-ID: <1314907327.47841.YahooMailClassic@web27006.mail.ukl.yahoo.com> Date: Thu, 1 Sep 2011 21:02:07 +0100 (BST) From: bifferos In-Reply-To: <4E5F45C7.1050800@redhat.com> MIME-Version: 1.0 Content-Type: text/plain; charset=iso-8859-1 Content-Transfer-Encoding: quoted-printable Subject: Re: [Qemu-devel] [PATCH] Add support for r6040 NIC List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: Avi Kivity Cc: qemu-devel@nongnu.org --- On Thu, 1/9/11, Avi Kivity wrote:=0A> On 08/31/2011 04= :05 AM, bifferos=0A> wrote:=0A> > +=A0 =A0 pci_register_bar(&s->dev, 0,=0A>= 0x100, PCI_BASE_ADDRESS_SPACE_IO, r6040_map);=0A=0A> pci_register_bar() ha= s been converted to the memory=0A> API.=A0 Please rebase to the latest qemu= .git master and=0A> read docs/memory.txt and memory.h.=0A=0AHere you go.=0A= =0A=0ASigned-off-by: Mark Kelly =0Adiff --git a/Makefile= .objs b/Makefile.objs=0Aindex d1f3e5d..345a362 100644=0A--- a/Makefile.objs= =0A+++ b/Makefile.objs=0A@@ -253,6 +253,7 @@ hw-obj-$(CONFIG_PCNET_PCI) += =3D pcnet-pci.o=0A hw-obj-$(CONFIG_PCNET_COMMON) +=3D pcnet.o=0A hw-obj-$(C= ONFIG_E1000_PCI) +=3D e1000.o=0A hw-obj-$(CONFIG_RTL8139_PCI) +=3D rtl8139.= o=0A+hw-obj-$(CONFIG_R6040_PCI) +=3D r6040.o=0A =0A hw-obj-$(CONFIG_SMC91C1= 11) +=3D smc91c111.o=0A hw-obj-$(CONFIG_LAN9118) +=3D lan9118.o=0Adiff --gi= t a/default-configs/pci.mak b/default-configs/pci.mak=0Aindex 22bd350..d2ea= 7a2 100644=0A--- a/default-configs/pci.mak=0A+++ b/default-configs/pci.mak= =0A@@ -10,6 +10,7 @@ CONFIG_PCNET_PCI=3Dy=0A CONFIG_PCNET_COMMON=3Dy=0A CON= FIG_LSI_SCSI_PCI=3Dy=0A CONFIG_RTL8139_PCI=3Dy=0A+CONFIG_R6040_PCI=3Dy=0A C= ONFIG_E1000_PCI=3Dy=0A CONFIG_IDE_CORE=3Dy=0A CONFIG_IDE_QDEV=3Dy=0Adiff --= git a/hw/pci.c b/hw/pci.c=0Aindex 57ff7b1..827ce8e 100644=0A--- a/hw/pci.c= =0A+++ b/hw/pci.c=0A@@ -1515,6 +1515,7 @@ static const char * const pci_nic= _models[] =3D {=0A "rtl8139",=0A "e1000",=0A "pcnet",=0A+ "r= 6040",=0A "virtio",=0A NULL=0A };=0A@@ -1527,6 +1528,7 @@ static co= nst char * const pci_nic_names[] =3D {=0A "rtl8139",=0A "e1000",=0A= "pcnet",=0A+ "r6040",=0A "virtio-net-pci",=0A NULL=0A };=0A= diff --git a/hw/pci_ids.h b/hw/pci_ids.h=0Aindex 83f3893..fd35e58 100644=0A= --- a/hw/pci_ids.h=0A+++ b/hw/pci_ids.h=0A@@ -118,5 +118,8 @@=0A #define PC= I_DEVICE_ID_INTEL_82801I_EHCI1 0x293a=0A #define PCI_DEVICE_ID_INTEL_82801I= _EHCI2 0x293c=0A =0A+#define PCI_VENDOR_ID_RDC 0x17f3=0A+#de= fine PCI_DEVICE_ID_RDC_R6040 0x6040=0A+=0A #define PCI_VENDOR_ID_X= EN 0x5853=0A #define PCI_DEVICE_ID_XEN_PLATFORM 0x0001= =0Adiff --git a/hw/r6040.c b/hw/r6040.c=0Anew file mode 100644=0Aindex 0000= 000..258c8e6=0A--- /dev/null=0A+++ b/hw/r6040.c=0A@@ -0,0 +1,661 @@=0A+/*= =0A+ * Emulation of r6040 ethernet controller found in a number of SoCs.=0A= + * Copyright (c) 2011 Mark Kelly, mark@bifferos.com=0A+ *=0A+ * Permission= is hereby granted, free of charge, to any person obtaining a=0A+ * copy of= this software and associated documentation files (the "Software"),=0A+ * t= o deal in the Software without restriction, including without limitation=0A= + * the rights to use, copy, modify, merge, publish, distribute, sublicense= ,=0A+ * and/or sell copies of the Software, and to permit persons to whom t= he=0A+ * Software is furnished to do so, subject to the following condition= s:=0A+ *=0A+ * The above copyright notice and this permission notice shall = be included=0A+ * in all copies or substantial portions of the Software.=0A= + *=0A+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, E= XPRESS=0A+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF=0A+= * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. I= N=0A+ * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY C= LAIM,=0A+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, T= ORT OR=0A+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFT= WARE OR=0A+ * THE USE OR OTHER DEALINGS IN THE SOFTWARE.=0A+ *=0A+ * This h= as been written using the R8610[1] and ip101a[2] datasheets.=0A+ *=0A+ * IC= s with the embedded controller include R8610, R3210, AMRISC20000=0A+ * and = Vortex86SX=0A+ *=0A+ * The emulation seems good enough to fool Linux 2.6.37= .6. It is=0A+ * not perfect, but has proven useful.=0A+ *=0A+ * [1] http:/= /www.sima.com.tw/download/R8610_D06_20051003.pdf=0A+ * [2] http://www.icplu= s.com.tw/pp-IP101A.html=0A+ */=0A+=0A+#include "hw.h"=0A+#include "pci.h"= =0A+#include "net.h"=0A+#include "loader.h"=0A+#include "sysemu.h"=0A+#incl= ude "qemu-timer.h"=0A+=0A+/* #define DEBUG_R6040 1 */=0A+=0A+=0A+#if define= d DEBUG_R6040=0A+#define DPRINTF(fmt, ...) \=0A+ do { fprintf(stderr, "R= 6040: " fmt, ## __VA_ARGS__); } while (0)=0A+#else=0A+static inline GCC_FMT= _ATTR(1, 2) int DPRINTF(const char *fmt, ...)=0A+{=0A+ return 0;=0A+}=0A= +#endif=0A+=0A+=0A+/* Cast in order of appearance. _W suffix means it's us= ed to index the=0A+ register word array (regs_w)=0A+ */=0A+=0A+#define MP= SCCR_W (0x88 / 2)=0A+=0A+#define MAC0_W (0x68 / 2)=0A+#de= fine MAC1_W (0x6a / 2)=0A+#define MAC2_W (0x6c / 2)=0A+= =0A+=0A+#define RX_START_LOW_W (0x34 / 2)=0A+#define RX_START_HIGH_W (0x= 38 / 2)=0A+#define TX_PKT_COUNT_W (0x5a / 2)=0A+#define RX_PKT_COUNT_W = (0x50 / 2)=0A+=0A+=0A+#define MCR0_W (0x00 / 2)=0A+#define MCR1_W= (0x04 / 2)=0A+#define BIT_MRST (1 << 0)=0A+=0A+#define M= TPR_W (0x14 / 2)=0A+#define MRBSR_W (0x18 / 2)=0A+#defin= e MISR_W (0x3c / 2)=0A+#define MIER_W (0x40 / 2)=0A+=0A= +#define MMDIO_W (0x20 / 2)=0A+#define MDIO_READ_W (0x24 / 2)= =0A+#define MDIO_WRITE_W (0x28 / 2)=0A+=0A+#define MRCNT_W (0x= 50 / 2)=0A+#define MTCNT_W (0x5c / 2)=0A+=0A+=0A+#define MDIO_WRIT= E 0x4000=0A+#define MDIO_READ 0x2000=0A+=0A+=0A+typedef struct R= 6040State {=0A+ PCIDevice dev;=0A+ NICState *nic;=0A+ NICConf conf= ;=0A+=0A+ /* PHY related register sets */=0A+ uint16_t mid0[3];=0A+ = uint16_t phy_regs[32];=0A+ uint32_t phy_op_in_progress;=0A+=0A+ /* = Primary IO address space */=0A+ union {=0A+ uint8_t regs_b[0x100]= ; /* Byte access */=0A+ uint16_t regs_w[0x100/2]; /* word access = */=0A+ uint32_t regs_l[0x100/4]; /* DWORD access */=0A+ };=0A+= =0A+ MemoryRegion bar_io;=0A+} R6040State;=0A+=0A+=0A+/* some inlines to= help access above structure */=0A+static inline uint32_t TX_START(R6040Sta= te *s)=0A+{=0A+ uint32_t tmp =3D s->regs_w[0x2c/2];=0A+ return tmp | = (s->regs_w[0x30/2] << 16);=0A+}=0A+=0A+static inline void TX_START_SET(R604= 0State *s, uint32_t start)=0A+{=0A+ s->regs_w[0x2c/2] =3D start & 0xffff= ;=0A+ s->regs_w[0x30/2] =3D (start >> 16) & 0xffff;=0A+}=0A+=0A+static i= nline uint32_t RX_START(R6040State *s)=0A+{=0A+ uint32_t tmp =3D s->regs= _w[0x34/2];=0A+ return tmp | (s->regs_w[0x38/2] << 16);=0A+}=0A+=0A+stat= ic inline void RX_START_SET(R6040State *s, uint32_t start)=0A+{=0A+ s->r= egs_w[0x34/2] =3D start & 0xffff;=0A+ s->regs_w[0x38/2] =3D (start >> 16= ) & 0xffff;=0A+}=0A+=0A+=0A+static void r6040_update_irq(R6040State *s)=0A+= {=0A+ uint16_t isr =3D s->regs_w[MISR_W] & s->regs_w[MIER_W];=0A+=0A+ = qemu_set_irq(s->dev.irq[0], isr ? 1 : 0);=0A+}=0A+=0A+=0A+/* Mark auto-neg= complete, NIC up. */=0A+static void PhysicalLinkUp(void *opaque)=0A+{=0A+= R6040State *s =3D opaque;=0A+ s->phy_regs[1] |=3D (1 << 2);=0A+}=0A+= =0A+=0A+/* Transmit and receive descriptors are doubled up=0A+ One is a s= ubset of the other anyhow=0A+ */=0A+typedef struct Descriptor {=0A+ uint= 16_t dst;=0A+ uint16_t dlen;=0A+ uint32_t dbp;=0A+ uint32_t dnx;= =0A+ uint16_t hidx;=0A+ uint16_t reserved_1;=0A+ uint16_t reserved= _2;=0A+} Descriptor;=0A+=0A+=0A+/* Some debugging functions */=0A+=0A+#ifde= f DEBUG_R6040=0A+static void addr_dump16(const char *name, uint16_t val)=0A= +{=0A+ DPRINTF("%s: 0x%04x ", name, val);=0A+}=0A+=0A+static void addr_= dump32(const char *name, uint32_t val)=0A+{=0A+ DPRINTF("%s: 0x%x ", na= me, val);=0A+}=0A+=0A+static void hex_dump(const uint8_t *data, uint32_t le= n)=0A+{=0A+ uint8_t i;=0A+ DPRINTF("hex: ");=0A+ for (i =3D 0; i <= len; i++) {=0A+ fprintf(stderr, "%02x ", *data);=0A+ if (i &= & !(i % 0x20)) {=0A+ fprintf(stderr, "\n");=0A+ }=0A+ = data++;=0A+ }=0A+ fprintf(stderr, "\n");=0A+}=0A+=0A+static void = desc_dump(Descriptor *d, uint32_t addr)=0A+{=0A+ DPRINTF("\nDumping: 0x%= x\n", addr);=0A+ addr_dump16("DST", d->dst);=0A+ addr_dump16("DLEN", = d->dlen);=0A+ addr_dump32("DBP", (unsigned long)d->dbp);=0A+ addr_dum= p32("DNX", (unsigned long)d->dnx);=0A+ addr_dump16("HIDX", d->hidx);=0A+= printf("\n");=0A+}=0A+=0A+static void dump_phys_mem(uint32_t addr, int = len)=0A+{=0A+ uint8_t buffer[1024];=0A+ cpu_physical_memory_read(addr= , buffer, len);=0A+ hex_dump(buffer, len);=0A+}=0A+=0A+static void dump_= pci(uint8_t *pci_conf)=0A+{=0A+ uint32_t *p =3D (uint32_t *)pci_conf;=0A= + int i =3D 0;=0A+ for (i =3D 0; i < 0x40; i +=3D 4) {=0A+ DPR= INTF("Addr: 0x%08x, Data: 0x%08x\n", i, *p);=0A+ p++;=0A+ }=0A+}= =0A+#endif=0A+=0A+=0A+static const VMStateDescription vmstate_r6040 =3D {= =0A+ .name =3D "r6040",=0A+ .version_id =3D 3,=0A+ .minimum_versio= n_id =3D 2,=0A+ .minimum_version_id_old =3D 2,=0A+ .fields =3D (VMSta= teField[]) {=0A+ VMSTATE_PCI_DEVICE(dev, R6040State),=0A+ VMS= TATE_BUFFER(regs_b, R6040State),=0A+ VMSTATE_UINT16_ARRAY(mid0, R604= 0State, 3),=0A+ VMSTATE_UINT16_ARRAY(phy_regs, R6040State, 32),=0A+ = VMSTATE_UINT32(phy_op_in_progress, R6040State),=0A+ VMSTATE_M= ACADDR(conf.macaddr, R6040State),=0A+ VMSTATE_END_OF_LIST()=0A+ }= =0A+};=0A+=0A+=0A+static int TryToSendOnePacket(void *opaque)=0A+{=0A+ R= 6040State *s =3D opaque;=0A+ Descriptor d;=0A+ uint8_t pkt_buffer[200= 0];=0A+ uint32_t tocopy;=0A+=0A+ cpu_physical_memory_read(TX_START(s)= , (uint8_t *)&d, sizeof(d));=0A+=0A+ if (d.dst & 0x8000) { /* MAC own= s it? */=0A+ tocopy =3D d.dlen;=0A+ if (tocopy > sizeof(pkt_b= uffer)) {=0A+ tocopy =3D sizeof(pkt_buffer);=0A+ }=0A+ = /* copy the packet to send it */=0A+ cpu_physical_memory_read(d= .dbp, pkt_buffer, tocopy);=0A+=0A+ qemu_send_packet(&s->nic->nc, pkt= _buffer, tocopy);=0A+ s->regs_w[TX_PKT_COUNT_W]++;=0A+=0A+ /*= relinquish ownership, we're done with it */=0A+ d.dst &=3D ~0x8000;= =0A+=0A+ /* Copy the new version of the descriptor back */=0A+ = cpu_physical_memory_write(TX_START(s), (uint8_t *)&d, sizeof(d));=0A+=0A+= /* Advance to the next buffer if packet processed */=0A+ TX_= START_SET(s, d.dnx);=0A+=0A+ return 1;=0A+ }=0A+=0A+ return 0;= =0A+}=0A+=0A+=0A+static void r6040_transmit(void *opaque)=0A+{=0A+ R6040= State *s =3D opaque;=0A+ int count =3D 0;=0A+=0A+ while (TryToSendOne= Packet(s)) {=0A+ ++count;=0A+ }=0A+=0A+ if (count) {=0A+ = s->regs_w[MISR_W] |=3D 0x10;=0A+ r6040_update_irq(s);=0A+ }=0A+= }=0A+=0A+=0A+/* Whether to allow callback returning 1 for yes, can receive = */=0A+static int r6040_can_receive(VLANClientState *nc)=0A+{=0A+ R6040St= ate *s =3D DO_UPCAST(NICState, nc, nc)->opaque;=0A+ int tmp =3D (s->regs= _w[0] & (1 << 1)) ? 1 : 0;=0A+ return tmp;=0A+}=0A+=0A+=0A+static int Re= ceiveOnePacket(void *opaque, const uint8_t *buf, size_t len)=0A+{=0A+ R6= 040State *s =3D opaque;=0A+ uint32_t tocopy =3D len+4; /* include check= sum */=0A+ Descriptor d;=0A+=0A+ cpu_physical_memory_read(RX_START(s)= , (uint8_t *)&d, sizeof(Descriptor));=0A+ /*desc_dump(&d, 0);*/=0A+=0A+ = if (d.dst & 0x8000) { /* MAC owned? */=0A+=0A+ uint16_t max_bu= ffer =3D s->regs_w[MRBSR_W] & 0x07fc;=0A+ if (tocopy > max_buffer) {= =0A+ tocopy =3D max_buffer;=0A+ }=0A+=0A+ cpu_phys= ical_memory_write(d.dbp, buf, tocopy-4);=0A+=0A+ /* indicate receive= d OK */=0A+ d.dst |=3D (1 << 14);=0A+ d.dlen =3D tocopy;=0A+ = /* relinquish ownership */=0A+ d.dst &=3D ~0x8000;=0A+=0A+ = /* Copy the descriptor back */=0A+ cpu_physical_memory_write(RX= _START(s), (uint8_t *)&d,=0A+ sizeof(Desc= riptor));=0A+=0A+ s->regs_w[RX_PKT_COUNT_W]++;=0A+=0A+ s->reg= s_w[MISR_W] |=3D 1; /* received pkt interrupt */=0A+=0A+ r6040_upda= te_irq(s);=0A+=0A+ RX_START_SET(s, d.dnx); /* advance */=0A+=0A+ = return 0;=0A+ }=0A+ return -1;=0A+}=0A+=0A+=0A+/* called on inco= ming packets */=0A+static ssize_t r6040_receive(VLANClientState *nc, const = uint8_t *buf,=0A+ size_t len)=0A+{= =0A+ R6040State *s =3D DO_UPCAST(NICState, nc, nc)->opaque;=0A+ DPRIN= TF("Received incoming packet of len %ld\n", len);=0A+=0A+ if (0 =3D=3D R= eceiveOnePacket(s, buf, len)) {=0A+ return len; /* copied OK */=0A+= }=0A+=0A+ return 0;=0A+}=0A+=0A+=0A+static void r6040_cleanup(VLANCl= ientState *vc)=0A+{=0A+ DPRINTF("r6040_cleanup\n");=0A+}=0A+=0A+=0A+stat= ic inline int BIT_SET(uint16_t old, uint16_t new, uint16_t bit)=0A+{=0A+ = uint16_t before =3D (old & (1 << bit));=0A+ uint16_t after =3D (new & (= 1 << bit));=0A+ if (!before && after) {=0A+ return 1;=0A+ }=0A= + return 0;=0A+}=0A+=0A+=0A+static void r6040_ioport_writew(void *opaque= , uint32_t addr, uint32_t val)=0A+{=0A+ R6040State *s =3D opaque;=0A+ = uint16_t old;=0A+ addr &=3D 0xff; /* get relative to base address */= =0A+ addr /=3D 2; /* Get the offset into the word-array */=0A+ old= =3D s->regs_w[addr]; /* store the old value for future use */=0A+=0A+ = switch (addr) {=0A+ case MCR0_W: /* 0x00 */=0A+ if (BIT_SET(ol= d, val, 12)) {=0A+ r6040_transmit(opaque);=0A+ }=0A+ = break;=0A+ case MCR1_W: /* 0x04 */=0A+ if (val & BIT_MRST) { = /* reset request incoming */=0A+ /* reset requested, complete = it immediately, set this value to=0A+ default */=0A+ = val =3D 0x0010;=0A+ }=0A+ break;=0A+ case MTPR_W: /* T= X command reg, 0x14 */=0A+ if (val & 1) {=0A+ r6040_trans= mit(opaque);=0A+ val &=3D ~1;=0A+ }=0A+ break;=0A+= case MMDIO_W: /* MDIO control, 0x20 */=0A+ {=0A+ int= phy_exists =3D ((val & 0x1f00) =3D=3D 0x100) ? 1 : 0;=0A+ uint1= 6_t *phy =3D s->phy_regs;=0A+ phy +=3D (val & 0x1f);=0A+=0A+ = if (val & (1 << 13)) { /* read data */=0A+ if (ph= y_exists) {=0A+ s->regs_w[MDIO_READ_W] =3D *phy;=0A+ = } else {=0A+ s->regs_w[MDIO_READ_W] =3D 0xff= ff;=0A+ }=0A+ } else if (val & (1 << 14)) { /* w= rite data */=0A+ if (phy_exists) {=0A+ *p= hy =3D s->regs_w[MDIO_WRITE_W];=0A+ }=0A+ }=0A+= =0A+ /* Whether you request to read or write, both bits go high = while=0A+ the operation is in progress, e.g. tell it to read,= and the=0A+ write-in-progress flag also goes high */=0A+ = val |=3D 0x6000; /* signal operation has started */=0A+ = s->phy_op_in_progress =3D 1;=0A+=0A+ break;=0A+ }=0A+ = case MISR_W: /* interrupt status reg (read to clear), 0x3c */=0A+ = return;=0A+=0A+ case MIER_W: /* interrupt enable register, 0x40 */=0A+ = s->regs_w[MIER_W] =3D val;=0A+ r6040_update_irq(s);=0A+ = return;=0A+=0A+ case MRCNT_W: /* 0x50 */=0A+ case MTCNT_W: /* 0= x5c */=0A+ return; /* Can't write to pkt count registers, skip */= =0A+=0A+ }=0A+ s->regs_w[addr] =3D val & 0xFFFF;=0A+}=0A+=0A+=0A+stat= ic uint32_t r6040_ioport_readw(void *opaque, uint32_t addr)=0A+{=0A+ R60= 40State *s =3D opaque;=0A+ addr &=3D 0xff; /* get relative to base add= ress */=0A+ addr /=3D 2; /* Get the offset into the word-array */=0A+= uint32_t tmp =3D s->regs_w[addr]; /* get the value */=0A+=0A+ switc= h (addr) {=0A+=0A+ case MMDIO_W: /* MDIO control, 0x20 */=0A+ {= =0A+ /* Clear any in-progress MDIO activity for the next read=0A= + This simulates the polling of the MDIO operation status,=0A= + so the driver code always has to read the register twice=0A= + before it thinks the operation is complete. */=0A+ = if (s->phy_op_in_progress) {=0A+ s->regs_w[addr] &=3D ~0x= 6000;=0A+ s->phy_op_in_progress =3D 0;=0A+ }=0A+ = break;=0A+ }=0A+ case MISR_W: /* interrupt status reg= (read to clear) 0x3c */=0A+ s->regs_w[addr] =3D 0;=0A+ brea= k;=0A+ case MIER_W: /* interrupt enable reg 0x40 */=0A+ break;= =0A+ case MRCNT_W: /* 0x50 */=0A+ case MTCNT_W: /* 0x5c */=0A+ = s->regs_w[addr] =3D 0; /* read to clear */=0A+ break;=0A+ de= fault:=0A+ break;=0A+ }=0A+ return tmp;=0A+}=0A+=0A+=0A+/* byt= e and long access are routed via the word operation handlers */=0A+static v= oid r6040_ioport_writeb(void *opaque, uint32_t addr, uint32_t val)=0A+{=0A+= R6040State *s =3D opaque;=0A+ addr &=3D 0xFF;=0A+ val &=3D 0xFF;= =0A+ uint16_t old =3D s->regs_w[addr/2]; /* get the current value */=0A= + if (addr & 1) {=0A+ old &=3D 0xff;=0A+ old |=3D (val << = 8);=0A+ } else {=0A+ old &=3D 0xff00;=0A+ old |=3D val;=0A= + }=0A+=0A+ r6040_ioport_writew(opaque, addr, old); /* call the word= -based version */=0A+}=0A+=0A+static void r6040_ioport_writel(void *opaque,= uint32_t addr, uint32_t val)=0A+{=0A+ /* Set the low value */=0A+ r6= 040_ioport_writew(opaque, addr, val & 0xffff);=0A+ /* Set the high value= */=0A+ r6040_ioport_writew(opaque, addr+2, (val >> 16) & 0xffff);=0A+}= =0A+=0A+static uint32_t r6040_ioport_readb(void *opaque, uint32_t addr)=0A+= {=0A+ uint32_t tmp =3D r6040_ioport_readw(opaque, addr & ~1);=0A+ if = (addr & 1) {=0A+ return (tmp & 0xff00) >> 8;=0A+ }=0A+ return = tmp & 0xff;=0A+}=0A+=0A+static uint32_t r6040_ioport_readl(void *opaque, ui= nt32_t addr)=0A+{=0A+ uint32_t tmp =3D r6040_ioport_readw(opaque, addr);= =0A+ return tmp | (r6040_ioport_readw(opaque, addr+2) << 16);=0A+}=0A+= =0A+=0A+static const MemoryRegionPortio r6040_portio[] =3D {=0A+ { 0, 0x= 100, 1, .read =3D r6040_ioport_readb, },=0A+ { 0, 0x100, 1, .write =3D r= 6040_ioport_writeb, },=0A+ { 0, 0x100, 2, .read =3D r6040_ioport_readw, = },=0A+ { 0, 0x100, 2, .write =3D r6040_ioport_writew, },=0A+ { 0, 0x1= 00, 4, .read =3D r6040_ioport_readl, },=0A+ { 0, 0x100, 4, .write =3D r6= 040_ioport_writel, },=0A+ PORTIO_END_OF_LIST()=0A+};=0A+=0A+static const= MemoryRegionOps r6040_io_ops =3D {=0A+ .old_portio =3D r6040_portio,=0A= + .endianness =3D DEVICE_LITTLE_ENDIAN,=0A+};=0A+=0A+=0A+static void r60= 40_register_ioports(R6040State *s, pcibus_t addr)=0A+{=0A+ register_iopo= rt_write(addr, 0x100, 1, r6040_ioport_writeb, s);=0A+ register_ioport_re= ad(addr, 0x100, 1, r6040_ioport_readb, s);=0A+=0A+ register_ioport_writ= e(addr, 0x100, 2, r6040_ioport_writew, s);=0A+ register_ioport_read(addr= , 0x100, 2, r6040_ioport_readw, s);=0A+=0A+ register_ioport_write(addr,= 0x100, 4, r6040_ioport_writel, s);=0A+ register_ioport_read(addr, 0x100= , 4, r6040_ioport_readl, s);=0A+}=0A+=0A+=0A+static NetClientInfo net_r604= 0_info =3D {=0A+ .type =3D NET_CLIENT_TYPE_NIC,=0A+ .size =3D sizeof(= NICState),=0A+ .can_receive =3D r6040_can_receive,=0A+ .receive =3D r= 6040_receive,=0A+ .cleanup =3D r6040_cleanup,=0A+};=0A+=0A+=0A+static in= t r6040_init(PCIDevice *dev)=0A+{=0A+ QEMUTimer *timer;=0A+=0A+ R6040= State *s =3D DO_UPCAST(R6040State, dev, dev);=0A+ uint8_t *pci_conf;=0A+= =0A+ /* MAC PHYS status change register. Linux driver expects something= =0A+ sensible as default and if not will try to set it */=0A+ s->r= egs_w[MPSCCR_W] =3D 0x9f07;=0A+=0A+ /* Default value for maximum packet = size */=0A+ s->regs_w[MRBSR_W] =3D 0x600;=0A+=0A+ /* set the MAC, lin= ux driver reads this when it loads, it is=0A+ normally set by the BIO= S, but obviously Qemu BIOS isn't going=0A+ to do that */=0A+ s->re= gs_w[MAC0_W] =3D 0x5452;=0A+ s->regs_w[MAC1_W] =3D 0x1200;=0A+ s->reg= s_w[MAC2_W] =3D 0x5734;=0A+=0A+ /* Tell Qemu the same thing */=0A+ s-= >conf.macaddr.a[0] =3D s->regs_w[MAC0_W] & 0xff;=0A+ s->conf.macaddr.a[1= ] =3D (s->regs_w[MAC0_W] >> 8) & 0xff;=0A+ s->conf.macaddr.a[2] =3D s->r= egs_w[MAC1_W] & 0xff;=0A+ s->conf.macaddr.a[3] =3D (s->regs_w[MAC1_W] >>= 8) & 0xff;=0A+ s->conf.macaddr.a[4] =3D s->regs_w[MAC2_W] & 0xff;=0A+ = s->conf.macaddr.a[5] =3D (s->regs_w[MAC2_W] >> 8) & 0xff;=0A+=0A+ /* n= o commands running */=0A+ s->phy_op_in_progress =3D 0;=0A+=0A+ /* PHY= auto-neg in progress */=0A+ s->phy_regs[1] =3D 0x786d & ~(1 << 2);=0A+ = s->phy_regs[2] =3D 0x0243;=0A+ s->phy_regs[3] =3D 0x0c54;=0A+=0A+ = pci_conf =3D (uint8_t *)s->dev.config;=0A+=0A+ pci_conf[PCI_HEADER_TYPE]= =3D PCI_HEADER_TYPE_NORMAL; /* header_type */=0A+ pci_conf[PCI_INTERRUP= T_LINE] =3D 0xa; /* interrupt line */=0A+ pci_conf[PCI_INTERRUPT_PI= N] =3D 1; /* interrupt pin 0 */=0A+=0A+ memory_region_init_io(&s->b= ar_io, &r6040_io_ops, s, "r6040", 0x100);=0A+ pci_register_bar(&s->dev, = 0, PCI_BASE_ADDRESS_SPACE_IO, &s->bar_io);=0A+=0A+ s->nic =3D qemu_new_n= ic(&net_r6040_info, &s->conf,=0A+ dev->qdev.info= ->name, dev->qdev.id, s);=0A+=0A+ qemu_format_nic_info_str(&s->nic->nc, = s->conf.macaddr.a);=0A+=0A+ /* Simulate a delay of a couple of seconds f= or the link to come up.=0A+ Not required for Linux but very handy for= developing BIOS code.=0A+ */=0A+ timer =3D qemu_new_timer_ns(vm_clo= ck, PhysicalLinkUp, s);=0A+ qemu_mod_timer(timer, qemu_get_clock_ms(vm_c= lock) + 1500000000);=0A+=0A+ /* Register IO port access as well */=0A+ = r6040_register_ioports(s, 0xe800);=0A+=0A+ return 0;=0A+}=0A+=0A+=0A+s= tatic int r6040_exit(PCIDevice *dev)=0A+{=0A+ R6040State *s =3D DO_UPCAS= T(R6040State, dev, dev);=0A+ memory_region_destroy(&s->bar_io);=0A+ r= eturn 0;=0A+}=0A+=0A+=0A+static PCIDeviceInfo r6040_info =3D {=0A+ .qdev= .name =3D "r6040",=0A+ .qdev.size =3D sizeof(R6040State),=0A+ .qdev.v= msd =3D &vmstate_r6040,=0A+ .init =3D r6040_init,=0A+ .exit = =3D r6040_exit,=0A+ .vendor_id =3D PCI_VENDOR_ID_RDC,=0A+ .device_i= d =3D PCI_DEVICE_ID_RDC_R6040,=0A+ .class_id =3D PCI_CLASS_NETWORK_ETH= ERNET,=0A+ .qdev.props =3D (Property[]) {=0A+ DEFINE_NIC_PROPERTI= ES(R6040State, conf),=0A+ DEFINE_PROP_END_OF_LIST(),=0A+ }=0A+};= =0A+=0A+=0A+static void r6040_register(void)=0A+{=0A+ pci_qdev_register(= &r6040_info);=0A+}=0A+=0A+=0A+device_init(r6040_register)=0A