From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from eggs.gnu.org ([140.186.70.92]:54891) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1QyZQv-0000za-AU for qemu-devel@nongnu.org; Tue, 30 Aug 2011 21:17:39 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1QyZQs-0006Yo-Jy for qemu-devel@nongnu.org; Tue, 30 Aug 2011 21:17:37 -0400 Received: from mail-yi0-f45.google.com ([209.85.218.45]:44203) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1QyZQs-0006Yk-DC for qemu-devel@nongnu.org; Tue, 30 Aug 2011 21:17:34 -0400 Received: by yih10 with SMTP id 10so219978yih.4 for ; Tue, 30 Aug 2011 18:17:33 -0700 (PDT) Message-ID: <4E5D8BAA.9010302@codemonkey.ws> Date: Tue, 30 Aug 2011 20:17:30 -0500 From: Anthony Liguori MIME-Version: 1.0 References: <1314752751.84463.YahooMailClassic@web27003.mail.ukl.yahoo.com> In-Reply-To: <1314752751.84463.YahooMailClassic@web27003.mail.ukl.yahoo.com> Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 7bit Subject: Re: [Qemu-devel] [PATCH] Add support for r6040 NIC List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: bifferos Cc: qemu-devel@nongnu.org This won't even come close to passing checkpatch.pl Regards, Anthony Liguori On 08/30/2011 08:05 PM, bifferos wrote: > > Signed-off-by: Mark Kelly > diff --git a/Makefile.objs b/Makefile.objs > index 6991a9f..7d87503 100644 > --- a/Makefile.objs > +++ b/Makefile.objs > @@ -240,6 +240,7 @@ hw-obj-$(CONFIG_PCNET_PCI) += pcnet-pci.o > hw-obj-$(CONFIG_PCNET_COMMON) += pcnet.o > hw-obj-$(CONFIG_E1000_PCI) += e1000.o > hw-obj-$(CONFIG_RTL8139_PCI) += rtl8139.o > +hw-obj-$(CONFIG_R6040_PCI) += r6040.o > > hw-obj-$(CONFIG_SMC91C111) += smc91c111.o > hw-obj-$(CONFIG_LAN9118) += lan9118.o > diff --git a/default-configs/pci.mak b/default-configs/pci.mak > index 22bd350..d2ea7a2 100644 > --- a/default-configs/pci.mak > +++ b/default-configs/pci.mak > @@ -10,6 +10,7 @@ CONFIG_PCNET_PCI=y > CONFIG_PCNET_COMMON=y > CONFIG_LSI_SCSI_PCI=y > CONFIG_RTL8139_PCI=y > +CONFIG_R6040_PCI=y > CONFIG_E1000_PCI=y > CONFIG_IDE_CORE=y > CONFIG_IDE_QDEV=y > diff --git a/hw/pci.c b/hw/pci.c > index b904a4e..7e12935 100644 > --- a/hw/pci.c > +++ b/hw/pci.c > @@ -1527,6 +1527,7 @@ static const char * const pci_nic_models[] = { > "rtl8139", > "e1000", > "pcnet", > + "r6040", > "virtio", > NULL > }; > @@ -1539,6 +1540,7 @@ static const char * const pci_nic_names[] = { > "rtl8139", > "e1000", > "pcnet", > + "r6040", > "virtio-net-pci", > NULL > }; > diff --git a/hw/r6040.c b/hw/r6040.c > new file mode 100644 > index 0000000..83587e7 > --- /dev/null > +++ b/hw/r6040.c > @@ -0,0 +1,627 @@ > +/* > + Emulation of r6040 ethernet controller found in a number of SoCs. > + Copyright (c) 2011 Mark Kelly, mark@bifferos.com > + > + This has been written using the R8610[1] and ip101a[2] datasheets. > + > + ICs with the embedded controller include R8610, R3210, AMRISC20000 > + and Vortex86SX > + > + The emulation seems good enough to fool Linux 2.6.37.6. It is > + not perfect, but has proven useful. > + > + [1] http://www.sima.com.tw/download/R8610_D06_20051003.pdf > + [2] http://www.icplus.com.tw/pp-IP101A.html > + */ > + > +#include "hw.h" > +#include "pci.h" > +#include "net.h" > +#include "loader.h" > +#include "sysemu.h" > +#include "qemu-timer.h" > + > +/* #define DEBUG_R6040 1 */ > + > + > +#if defined DEBUG_R6040 > +#define DPRINTF(fmt, ...) \ > + do { fprintf(stderr, "R6040: " fmt, ## __VA_ARGS__); } while (0) > +#else > +static inline GCC_FMT_ATTR(1, 2) int DPRINTF(const char *fmt, ...) > +{ > + return 0; > +} > +#endif > + > + > +/* Cast in order of appearance. _W prevfix means it's used to index the > + register word array (RegsW) > + */ > + > +#define MPSCCR_W (0x88 / 2) > + > +#define MAC0_W (0x68 / 2) > +#define MAC1_W (0x6a / 2) > +#define MAC2_W (0x6c / 2) > + > + > +#define RX_START_LOW_W (0x34 / 2) > +#define RX_START_HIGH_W (0x38 / 2) > +#define TX_PKT_COUNT_W (0x5a / 2) > +#define RX_PKT_COUNT_W (0x50 / 2) > + > + > +#define MCR0_W (0x00 / 2) > +#define MCR1_W (0x04 / 2) > +#define BIT_MRST (1<< 0) > + > +#define MTPR_W (0x14 / 2) > +#define MRBSR_W (0x18 / 2) > +#define MISR_W (0x3c / 2) > +#define MIER_W (0x40 / 2) > + > +#define MMDIO_W (0x20 / 2) > +#define MDIO_READ_W (0x24 / 2) > +#define MDIO_WRITE_W (0x28 / 2) > + > +#define MRCNT_W (0x50 / 2) > +#define MTCNT_W (0x5c / 2) > + > + > +#define MDIO_WRITE 0x4000 > +#define MDIO_READ 0x2000 > + > + > +typedef struct R6040State { > + PCIDevice dev; > + NICState *nic; > + NICConf conf; > + > + /* PHY related register sets */ > + uint16_t MID0[3]; > + uint16_t phy_regs[32]; > + uint32_t phy_op_in_progress; > + > + /* Primary IO address space */ > + union { > + uint8_t RegsB[0x100]; /* Byte access */ > + uint16_t RegsW[0x100/2]; /* word access */ > + uint32_t RegsL[0x100/4]; /* DWORD access */ > + }; > + > +} R6040State; > + > + > +/* some inlines to help access above structure */ > +static inline uint32_t TX_START(R6040State *s) > +{ > + uint32_t tmp = s->RegsW[0x2c/2]; > + return tmp | (s->RegsW[0x30/2]<< 16); > +} > + > +static inline void TX_START_SET(R6040State *s, uint32_t start) > +{ > + s->RegsW[0x2c/2] = start& 0xffff; > + s->RegsW[0x30/2] = (start>> 16)& 0xffff; > +} > + > +static inline uint32_t RX_START(R6040State *s) > +{ > + uint32_t tmp = s->RegsW[0x34/2]; > + return tmp | (s->RegsW[0x38/2]<< 16); > +} > + > +static inline void RX_START_SET(R6040State *s, uint32_t start) > +{ > + s->RegsW[0x34/2] = start& 0xffff; > + s->RegsW[0x38/2] = (start>> 16)& 0xffff; > +} > + > + > +static void r6040_update_irq(R6040State *s) > +{ > + uint16_t isr = s->RegsW[MISR_W]& s->RegsW[MIER_W]; > + > + qemu_set_irq(s->dev.irq[0], isr ? 1 : 0); > +} > + > + > +/* Mark auto-neg complete, NIC up. */ > +static void PhysicalLinkUp(void *opaque) > +{ > + R6040State *s = opaque; > + s->phy_regs[1] |= (1<< 2); > +} > + > + > +/* Transmit and receive descriptors are doubled up > + One is a subset of the other anyhow > + */ > +typedef struct descriptor { > + uint16_t DST; > + uint16_t DLEN; > + uint32_t DBP; > + uint32_t DNX; > + uint16_t HIDX; > + uint16_t Reserved1; > + uint16_t Reserved2; > +} descriptor_t; > + > + > +/* Some debugging functions */ > + > +#ifdef DEBUG_R6040 > +static void addr_dump16(const char *name, uint16_t val) > +{ > + DPRINTF("%s: 0x%04x ", name, val); > +} > + > +static void addr_dump32(const char *name, uint32_t val) > +{ > + DPRINTF("%s: 0x%x ", name, val); > +} > + > +static void hex_dump(const uint8_t *data, uint32_t len) > +{ > + uint8_t i; > + DPRINTF("hex: "); > + for (i = 0; i< len; i++) { > + fprintf(stderr, "%02x ", *data); > + if (i&& !(i % 0x20)) { > + fprintf(stderr, "\n"); > + } > + data++; > + } > + fprintf(stderr, "\n"); > +} > + > +static void desc_dump(descriptor_t *d, uint32_t addr) > +{ > + DPRINTF("\nDumping: 0x%x\n", addr); > + addr_dump16("DST", d->DST); > + addr_dump16("DLEN", d->DLEN); > + addr_dump32("DBP", (unsigned long)d->DBP); > + addr_dump32("DNX", (unsigned long)d->DNX); > + addr_dump16("HIDX", d->HIDX); > + printf("\n"); > +} > + > +static void dump_phys_mem(uint32_t addr, int len) > +{ > + uint8_t buffer[1024]; > + cpu_physical_memory_read(addr, buffer, len); > + hex_dump(buffer, len); > +} > + > +static void dump_pci(uint8_t *pci_conf) > +{ > + uint32_t *p = (uint32_t *)pci_conf; > + int i = 0; > + for (i = 0; i< 0x40; i += 4) { > + DPRINTF("Addr: 0x%08x, Data: 0x%08x\n", i, *p); > + p++; > + } > +} > +#endif > + > + > +static const VMStateDescription vmstate_r6040 = { > + .name = "r6040", > + .version_id = 3, > + .minimum_version_id = 2, > + .minimum_version_id_old = 2, > + .fields = (VMStateField[]) { > + VMSTATE_PCI_DEVICE(dev, R6040State), > + VMSTATE_BUFFER(RegsB, R6040State), > + VMSTATE_UINT16_ARRAY(MID0, R6040State, 3), > + VMSTATE_UINT16_ARRAY(phy_regs, R6040State, 32), > + VMSTATE_UINT32(phy_op_in_progress, R6040State), > + VMSTATE_MACADDR(conf.macaddr, R6040State), > + VMSTATE_END_OF_LIST() > + } > +}; > + > + > +static int TryToSendOnePacket(void *opaque) > +{ > + R6040State *s = opaque; > + descriptor_t d; > + uint8_t pkt_buffer[2000]; > + uint32_t tocopy; > + > + cpu_physical_memory_read(TX_START(s), (uint8_t *)&d, sizeof(d)); > + > + if (d.DST& 0x8000) { /* MAC owns it? */ > + tocopy = d.DLEN; > + if (tocopy> sizeof(pkt_buffer)) { > + tocopy = sizeof(pkt_buffer); > + } > + /* copy the packet to send it */ > + cpu_physical_memory_read(d.DBP, pkt_buffer, tocopy); > + > + qemu_send_packet(&s->nic->nc, pkt_buffer, tocopy); > + s->RegsW[TX_PKT_COUNT_W]++; > + > + /* relinquish ownership, we're done with it */ > + d.DST&= ~0x8000; > + > + /* Copy the new version of the descriptor back */ > + cpu_physical_memory_write(TX_START(s), (uint8_t *)&d, sizeof(d)); > + > + /* Advance to the next buffer if packet processed */ > + TX_START_SET(s, d.DNX); > + > + return 1; > + } > + > + return 0; > +} > + > + > +static void r6040_transmit(void *opaque) > +{ > + R6040State *s = opaque; > + int count = 0; > + > + while (TryToSendOnePacket(s)) { > + ++count; > + } > + > + if (count) { > + s->RegsW[MISR_W] |= 0x10; > + r6040_update_irq(s); > + } > +} > + > + > +/* Whether to allow callback returning 1 for yes, can receive */ > +static int r6040_can_receive(VLANClientState *nc) > +{ > + R6040State *s = DO_UPCAST(NICState, nc, nc)->opaque; > + int tmp = (s->RegsW[0]& (1<< 1)) ? 1 : 0; > + return tmp; > +} > + > + > +static int ReceiveOnePacket(void *opaque, const uint8_t *buf, size_t len) > +{ > + R6040State *s = opaque; > + uint32_t tocopy = len+4; /* include checksum */ > + descriptor_t d; > + > + cpu_physical_memory_read(RX_START(s), (uint8_t *)&d, sizeof(descriptor_t)); > + /*desc_dump(&d, 0);*/ > + > + if (d.DST& 0x8000) { /* MAC owned? */ > + > + uint16_t max_buffer = s->RegsW[MRBSR_W]& 0x07fc; > + if (tocopy> max_buffer) { > + tocopy = max_buffer; > + } > + > + cpu_physical_memory_write(d.DBP, buf, tocopy-4); > + > + /* indicate received OK */ > + d.DST |= (1<< 14); > + d.DLEN = tocopy; > + /* relinquish ownership */ > + d.DST&= ~0x8000; > + > + /* Copy the descriptor back */ > + cpu_physical_memory_write(RX_START(s), (uint8_t *)&d, > + sizeof(descriptor_t)); > + > + s->RegsW[RX_PKT_COUNT_W]++; > + > + s->RegsW[MISR_W] |= 1; /* received pkt interrupt */ > + > + r6040_update_irq(s); > + > + RX_START_SET(s, d.DNX); /* advance */ > + > + return 0; > + } > + return -1; > +} > + > + > +/* called on incoming packets */ > +static ssize_t r6040_receive(VLANClientState *nc, const uint8_t *buf, > + size_t len) > +{ > + R6040State *s = DO_UPCAST(NICState, nc, nc)->opaque; > + DPRINTF("Received incoming packet of len %ld\n", len); > + > + if (0 == ReceiveOnePacket(s, buf, len)) { > + return len; /* copied OK */ > + } > + > + return 0; > +} > + > + > +static void r6040_cleanup(VLANClientState *vc) > +{ > + DPRINTF("r6040_cleanup\n"); > +} > + > + > +static inline int BIT_SET(uint16_t old, uint16_t new, uint16_t bit) > +{ > + uint16_t before = (old& (1<< bit)); > + uint16_t after = (new& (1<< bit)); > + if (!before&& after) { > + return 1; > + } > + return 0; > +} > + > + > +static void r6040_ioport_writew(void *opaque, uint32_t addr, uint32_t val) > +{ > + R6040State *s = opaque; > + uint16_t old; > + addr&= 0xff; /* get relative to base address */ > + addr /= 2; /* Get the offset into the word-array */ > + old = s->RegsW[addr]; /* store the old value for future use */ > + > + switch (addr) { > + case MCR0_W: /* 0x00 */ > + if (BIT_SET(old, val, 12)) { > + r6040_transmit(opaque); > + } > + break; > + case MCR1_W: /* 0x04 */ > + if (val& BIT_MRST) { /* reset request incoming */ > + /* reset requested, complete it immediately, set this value to > + default */ > + val = 0x0010; > + } > + break; > + case MTPR_W: /* TX command reg, 0x14 */ > + if (val& 1) { > + r6040_transmit(opaque); > + val&= ~1; > + } > + break; > + case MMDIO_W: /* MDIO control, 0x20 */ > + { > + int phy_exists = ((val& 0x1f00) == 0x100) ? 1 : 0; > + uint16_t *phy = s->phy_regs; > + phy += (val& 0x1f); > + > + if (val& (1<< 13)) { /* read data */ > + if (phy_exists) { > + s->RegsW[MDIO_READ_W] = *phy; > + } else { > + s->RegsW[MDIO_READ_W] = 0xffff; > + } > + } else if (val& (1<< 14)) { /* write data */ > + if (phy_exists) { > + *phy = s->RegsW[MDIO_WRITE_W]; > + } > + } > + > + /* Whether you request to read or write, both bits go high while > + the operation is in progress, e.g. tell it to read, and the > + write-in-progress flag also goes high */ > + val |= 0x6000; /* signal operation has started */ > + s->phy_op_in_progress = 1; > + > + break; > + } > + case MISR_W: /* interrupt status reg (read to clear), 0x3c */ > + return; > + > + case MIER_W: /* interrupt enable register, 0x40 */ > + s->RegsW[MIER_W] = val; > + r6040_update_irq(s); > + return; > + > + case MRCNT_W: /* 0x50 */ > + case MTCNT_W: /* 0x5c */ > + return; /* Can't write to pkt count registers, skip */ > + > + } > + s->RegsW[addr] = val& 0xFFFF; > +} > + > + > + > +static uint32_t r6040_ioport_readw(void *opaque, uint32_t addr) > +{ > + R6040State *s = opaque; > + addr&= 0xff; /* get relative to base address */ > + addr /= 2; /* Get the offset into the word-array */ > + uint32_t tmp = s->RegsW[addr]; /* get the value */ > + > + switch (addr) { > + > + case MMDIO_W: /* MDIO control, 0x20 */ > + { > + /* Clear any in-progress MDIO activity for the next read > + This simulates the polling of the MDIO operation status, > + so the driver code always has to read the register twice > + before it thinks the operation is complete. */ > + if (s->phy_op_in_progress) { > + s->RegsW[addr]&= ~0x6000; > + s->phy_op_in_progress = 0; > + } > + break; > + } > + case MISR_W: /* interrupt status reg (read to clear) 0x3c */ > + s->RegsW[addr] = 0; > + break; > + case MIER_W: /* interrupt enable reg 0x40 */ > + break; > + case MRCNT_W: /* 0x50 */ > + case MTCNT_W: /* 0x5c */ > + s->RegsW[addr] = 0; /* read to clear */ > + break; > + default: > + break; > + } > + return tmp; > +} > + > + > +/* byte and long access are routed via the word operation handlers */ > +static void r6040_ioport_writeb(void *opaque, uint32_t addr, uint32_t val) > +{ > + R6040State *s = opaque; > + addr&= 0xFF; > + val&= 0xFF; > + uint16_t old = s->RegsW[addr/2]; /* get the current value */ > + if (addr& 1) { > + old&= 0xff; > + old |= (val<< 8); > + } else { > + old&= 0xff00; > + old |= val; > + } > + > + r6040_ioport_writew(opaque, addr, old); /* call the word-based version */ > +} > + > +static void r6040_ioport_writel(void *opaque, uint32_t addr, uint32_t val) > +{ > + /* Set the low value */ > + r6040_ioport_writew(opaque, addr, val& 0xffff); > + /* Set the high value */ > + r6040_ioport_writew(opaque, addr+2, (val>> 16)& 0xffff); > +} > + > +static uint32_t r6040_ioport_readb(void *opaque, uint32_t addr) > +{ > + uint32_t tmp = r6040_ioport_readw(opaque, addr& ~1); > + if (addr& 1) { > + return (tmp& 0xff00)>> 8; > + } > + return tmp& 0xff; > +} > + > +static uint32_t r6040_ioport_readl(void *opaque, uint32_t addr) > +{ > + uint32_t tmp = r6040_ioport_readw(opaque, addr); > + return tmp | (r6040_ioport_readw(opaque, addr+2)<< 16); > +} > + > + > +static void r6040_register_ioports(R6040State *s, pcibus_t addr) > +{ > + register_ioport_write(addr, 0x100, 1, r6040_ioport_writeb, s); > + register_ioport_read(addr, 0x100, 1, r6040_ioport_readb, s); > + > + register_ioport_write(addr, 0x100, 2, r6040_ioport_writew, s); > + register_ioport_read(addr, 0x100, 2, r6040_ioport_readw, s); > + > + register_ioport_write(addr, 0x100, 4, r6040_ioport_writel, s); > + register_ioport_read(addr, 0x100, 4, r6040_ioport_readl, s); > +} > + > + > +static void r6040_map(PCIDevice *pci_dev, int region_num, > + pcibus_t addr, pcibus_t size, int type) > +{ > + R6040State *s = DO_UPCAST(R6040State, dev, pci_dev);; > + > + DPRINTF("## Mapping to address %lx\n", addr); > + r6040_register_ioports(s, addr); > +} > + > + > +static NetClientInfo net_r6040_info = { > + .type = NET_CLIENT_TYPE_NIC, > + .size = sizeof(NICState), > + .can_receive = r6040_can_receive, > + .receive = r6040_receive, > + .cleanup = r6040_cleanup, > +}; > + > + > +static int r6040_init_pci(PCIDevice *dev) > +{ > + QEMUTimer *timer; > + > + R6040State *s = DO_UPCAST(R6040State, dev, dev); > + uint8_t *pci_conf; > + > + /* MAC PHYS status change register. Linux driver expects something > + sensible as default and if not will try to set it */ > + s->RegsW[MPSCCR_W] = 0x9f07; > + > + /* Default value for maximum packet size */ > + s->RegsW[MRBSR_W] = 0x600; > + > + /* set the MAC, linux driver reads this when it loads, it is > + normally set by the BIOS, but obviously Qemu BIOS isn't going > + to do that */ > + s->RegsW[MAC0_W] = 0x5452; > + s->RegsW[MAC1_W] = 0x1200; > + s->RegsW[MAC2_W] = 0x5734; > + > + /* Tell Qemu the same thing */ > + s->conf.macaddr.a[0] = s->RegsW[MAC0_W]& 0xff; > + s->conf.macaddr.a[1] = (s->RegsW[MAC0_W]>> 8)& 0xff; > + s->conf.macaddr.a[2] = s->RegsW[MAC1_W]& 0xff; > + s->conf.macaddr.a[3] = (s->RegsW[MAC1_W]>> 8)& 0xff; > + s->conf.macaddr.a[4] = s->RegsW[MAC2_W]& 0xff; > + s->conf.macaddr.a[5] = (s->RegsW[MAC2_W]>> 8)& 0xff; > + > + /* no commands running */ > + s->phy_op_in_progress = 0; > + > + /* PHY auto-neg in progress */ > + s->phy_regs[1] = 0x786d& ~(1<< 2); > + s->phy_regs[2] = 0x0243; > + s->phy_regs[3] = 0x0c54; > + > + pci_conf = (uint8_t *)s->dev.config; > + > + pci_conf[PCI_HEADER_TYPE] = PCI_HEADER_TYPE_NORMAL; /* header_type */ > + pci_conf[PCI_INTERRUPT_LINE] = 0xa; /* interrupt line */ > + pci_conf[PCI_INTERRUPT_PIN] = 1; /* interrupt pin 0 */ > + > + pci_register_bar(&s->dev, 0, 0x100, PCI_BASE_ADDRESS_SPACE_IO, r6040_map); > + > + s->nic = qemu_new_nic(&net_r6040_info,&s->conf, > + dev->qdev.info->name, dev->qdev.id, s); > + > + qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a); > + > + /* Simulate a delay of a couple of seconds for the link to come up. > + Not required for Linux but very handy for developing BIOS code. > + */ > + timer = qemu_new_timer_ns(vm_clock, PhysicalLinkUp, s); > + qemu_mod_timer(timer, qemu_get_clock_ms(vm_clock) + 1500000000); > + > + /* Register IO port access as well */ > + r6040_register_ioports(s, 0xe800); > + > + return 0; > +} > + > + > +static PCIDeviceInfo r6040_info = { > + .qdev.name = "r6040", > + .qdev.size = sizeof(R6040State), > + .qdev.vmsd =&vmstate_r6040, > + .init = r6040_init_pci, > + .vendor_id = 0x17f3, /* RDC */ > + .device_id = 0x6040, /* r6040 nic */ > + .class_id = PCI_CLASS_NETWORK_ETHERNET, > + .qdev.props = (Property[]) { > + DEFINE_NIC_PROPERTIES(R6040State, conf), > + DEFINE_PROP_END_OF_LIST(), > + } > +}; > + > + > +static void r6040_register(void) > +{ > + pci_qdev_register(&r6040_info); > +} > + > + > +device_init(r6040_register) > >