* [Qemu-devel] [PATCH] Davicom DM9000 emulation
@ 2008-10-13 10:12 Daniel Silverstone
2008-10-13 12:47 ` Paul Brook
0 siblings, 1 reply; 9+ messages in thread
From: Daniel Silverstone @ 2008-10-13 10:12 UTC (permalink / raw)
To: qemu-devel
[-- Attachment #1: Type: text/plain, Size: 633 bytes --]
Hi,
Attached is a patch which provides Davicom DM9000E emulation support
including the ability to compile in a pcap-style network trace of the
device.
It is the first step along the way to getting a large number of Simtec
Electronics boards emulated (ARM based) and also therefore a step
towards other Samsung ARM9 SOC based systems. Future patches will bring
in more devices, and also ARM920T support, leading to an S3C2410x
emulation and then later S3C2440.
Regards,
Daniel.
--
Daniel Silverstone http://www.simtec.co.uk/
PGP mail accepted and encouraged. Key Id: 2BC8 4016 2068 7895
[-- Attachment #2: Davicom DM9000 Emulation --]
[-- Type: text/x-patch, Size: 23648 bytes --]
The Davicom DM9000 10/100 Ethernet controller.
This driver supplies an MMIO based DM9000 interface which can be
instantiated in the emulation's memory map to provide an ethernet
interface for ARM "local-bus" type systems.
Signed-off-by: Daniel Silverstone <dsilvers@simtec.co.uk>
Signed-off-by: Vincent Sanders <vince@simtec.co.uk>
Makefile.target | 3
hw/dm9000.c | 658 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
hw/dm9000.h | 28 ++
3 files changed, 689 insertions(+)
=== modified file 'Makefile.target'
--- Makefile.target 2008-10-13 03:14:31 +0000
+++ Makefile.target 2008-10-13 09:53:20 +0000
@@ -540,6 +540,9 @@
OBJS += rtl8139.o
OBJS += e1000.o
+# MMIO mapped network cards
+OBJS += dm9000.o
+
ifeq ($(TARGET_BASE_ARCH), i386)
# Hardware support
OBJS+= ide.o pckbd.o ps2.o vga.o $(SOUND_HW) dma.o
=== added file 'hw/dm9000.c'
--- hw/dm9000.c 1970-01-01 00:00:00 +0000
+++ hw/dm9000.c 2008-10-13 10:07:17 +0000
@@ -0,0 +1,658 @@
+/* hw/dm9000.c
+ *
+ * DM9000 Ethernet interface
+ *
+ * Copyright 2006, 2008 Daniel Silverstone and Vincent Sanders
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 only.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <string.h>
+#include "qemu-common.h"
+#include "hw/irq.h"
+#include "net.h"
+
+/* Comment this out if you don't want register debug on stderr */
+/* #define DM9000_DEBUG */
+
+/* Comment this out if you don't want a packet dump */
+/* #define DM9000_DUMP_FILENAME "/tmp/dm9k_dump" */
+
+#ifdef DM9000_DEBUG
+#define DM9000_DBF(X...) fprintf(stderr, X)
+#else
+#define DM9000_DBF(X...) if(0) fprintf(stderr, X)
+#endif
+
+#define DM9000_REG_NCR 0x00
+#define DM9000_REG_NSR 0x01
+#define DM9000_REG_TCR 0x02
+#define DM9000_REG_TSR1 0x03
+#define DM9000_REG_TSR2 0x04
+#define DM9000_REG_RCR 0x05
+#define DM9000_REG_RSR 0x06
+#define DM9000_REG_ROCR 0x07
+#define DM9000_REG_BPTR 0x08
+#define DM9000_REG_FCTR 0x09
+#define DM9000_REG_FCR 0x0A
+#define DM9000_REG_EPCR 0x0B
+#define DM9000_REG_EPAR 0x0C
+#define DM9000_REG_EPDRL 0x0D
+#define DM9000_REG_EPDRH 0x0E
+#define DM9000_REG_WCR 0x0F
+#define DM9000_REG_PAR0 0x10
+#define DM9000_REG_PAR1 0x11
+#define DM9000_REG_PAR2 0x12
+#define DM9000_REG_PAR3 0x13
+#define DM9000_REG_PAR4 0x14
+#define DM9000_REG_PAR5 0x15
+#define DM9000_REG_MAR0 0x16
+#define DM9000_REG_MAR1 0x17
+#define DM9000_REG_MAR2 0x18
+#define DM9000_REG_MAR3 0x19
+#define DM9000_REG_MAR4 0x1A
+#define DM9000_REG_MAR5 0x1B
+#define DM9000_REG_MAR6 0x1C
+#define DM9000_REG_MAR7 0x1D
+#define DM9000_REG_GPCR 0x1E
+#define DM9000_REG_GPR 0x1F
+#define DM9000_REG_TRPAL 0x22
+#define DM9000_REG_TRPAH 0x23
+#define DM9000_REG_RWPAL 0x24
+#define DM9000_REG_RWPAH 0x25
+#define DM9000_REG_VIDL 0x28
+#define DM9000_REG_VIDH 0x29
+#define DM9000_REG_PIDL 0x2A
+#define DM9000_REG_PIDH 0x2B
+#define DM9000_REG_CHIPR 0x2C
+#define DM9000_REG_SMCR 0x2F
+#define DM9000_REG_MRCMDX 0xF0
+#define DM9000_REG_MRCMD 0xF2
+#define DM9000_REG_MRRL 0xF4
+#define DM9000_REG_MRRH 0xF5
+#define DM9000_REG_MWCMDX 0xF6
+#define DM9000_REG_MWCMD 0xF8
+#define DM9000_REG_MWRL 0xFA
+#define DM9000_REG_MWRH 0xFB
+#define DM9000_REG_TXPLL 0xFC
+#define DM9000_REG_TXPLH 0xFD
+#define DM9000_REG_ISR 0xFE
+#define DM9000_REG_IMR 0xFF
+
+#define DM9000_NCR_RESET 0x01
+#define DM9000_NSR_TX1END 0x04
+#define DM9000_NSR_TX2END 0x08
+#define DM9000_TCR_TXREQ 0x01
+
+#define DM9000_IMR_AUTOWRAP 0x80
+
+#define DM9000_MII_READ 0x0C
+#define DM9000_MII_WRITE 0x0A
+
+#define DM9000_MII_REG_BMCR 0x00
+#define DM9000_MII_REG_STATUS 0x01
+#define DM9000_MII_REG_PHYID1 0x02
+#define DM9000_MII_REG_PHYID2 0x03
+#define DM9000_MII_REG_ANAR 0x04
+#define DM9000_MII_REG_ANLPAR 0x05
+#define DM9000_MII_REG_ANER 0x06
+#define DM9000_MII_REG_DSCR 0x10
+#define DM9000_MII_REG_DSCSR 0x11
+#define DM9000_MII_REG_10BTCSR 0x12
+
+
+typedef struct {
+ uint32_t addr; /* address port */
+ uint32_t data; /* data port */
+ VLANClientState *vc;
+ qemu_irq irq;
+ uint8_t macaddr[6];
+ uint8_t multihash[8]; /* multicast hash table */
+ uint8_t address; /* The internal magial address */
+ uint8_t packet_buffer[16 * 1024];
+ uint16_t dm9k_mrr, dm9k_mwr; /* Read and write address registers */
+ uint16_t dm9k_txpl; /* TX packet length */
+ uint16_t dm9k_trpa, dm9k_rwpa; /* TX Read ptr address, RX write ptr address */
+ uint8_t dm9k_imr, dm9k_isr; /* Interrupt mask register and status register*/
+ uint8_t dm9k_ncr, dm9k_nsr; /* Network control register, network status register */
+ uint8_t dm9k_wcr; /* Wakeup control */
+ uint8_t dm9k_tcr; /* Transmission control register */
+ uint8_t packet_copy_buffer[3 * 1024]; /* packet copy buffer */
+ unsigned int packet_index:1; /* 0 == packet I, 1 == packet II */
+
+ /* Internal MII PHY state */
+ uint8_t dm9k_epcr; /* EEPROM/PHY control register */
+ uint8_t dm9k_epar; /* EEPROM/PHY address register */
+ uint16_t dm9k_epdr; /* EEPROM/PHY data register */
+ /* MII Regs */
+ uint16_t dm9k_mii_bmcr;
+ uint16_t dm9k_mii_anar;
+ uint16_t dm9k_mii_dscr;
+
+} dm9000_state;
+
+
+#ifdef DM9000_DUMP_FILENAME
+#include <arpa/inet.h>
+static uint8_t pcap_header[24] = {
+ 0xA1, 0xB2, 0xC3, 0xD4, /* TCPDUMP Magic */
+ 0x00, 0x02, 0x00, 0x04, /* Major 2, Minor 4 */
+ 0x00, 0x00, 0x00, 0x00, /* Timezone offset */
+ 0x00, 0x00, 0x00, 0x01, /* Accuracy of timestamps */
+ 0x00, 0x00, 0x0C, 0x00, /* Snaplen 3KiB */
+ 0x00, 0x00, 0x00, 0x01, /* Ethernet frames */
+};
+static uint8_t nulls[8] = {0, 0, 0, 0, 0, 0, 0, 0};
+static void dm9k_dump_packet(uint8_t *buf, uint32_t size)
+{
+ FILE* dm9k_fileh = fopen(DM9000_DUMP_FILENAME, "ab+");
+ unsigned long bsize = htonl(size);
+ DM9000_DBF("Dumping packet at %08x (%d bytes)\n", buf, size);
+ fseek(dm9k_fileh, 0, SEEK_END);
+ if(ftell(dm9k_fileh)==0) fwrite(pcap_header, 1, 24, dm9k_fileh);
+ fwrite(nulls, 1, 8, dm9k_fileh);
+ fwrite(&bsize, 1, 4, dm9k_fileh);
+ fwrite(&bsize, 1, 4, dm9k_fileh);
+ fwrite(buf, 1, size, dm9k_fileh);
+ fclose(dm9k_fileh);
+}
+#else
+#define dm9k_dump_packet(X...) do { } while(0)
+#endif
+
+static void dm9000_raise_irq(dm9000_state *state)
+{
+ int level = ((state->dm9k_isr & state->dm9k_imr) & 0x03) != 0;
+ DM9000_DBF("DM9000: Set IRQ level %d\n", level);
+ qemu_set_irq(state->irq, level);
+}
+
+static void dm9000_soft_reset_mii(dm9000_state *state)
+{
+ state->dm9k_mii_bmcr = 0x3100; /* 100Mbps, AUTONEG, FULL DUPLEX */
+ state->dm9k_mii_anar = 0x01E1;
+ state->dm9k_mii_dscr = 0x0410;
+}
+
+static void dm9000_soft_reset(dm9000_state *state)
+{
+ DM9000_DBF("DM9000: Soft Reset\n");
+ state->dm9k_mrr = state->dm9k_mwr = state->dm9k_txpl = state->dm9k_trpa = 0x0000;
+ state->dm9k_rwpa = 0x0C04;
+ state->dm9k_imr = 0;
+ state->dm9k_isr = 0; /* 16 bit mode, no interrupts asserted */
+ state->dm9k_tcr = 0;
+ state->packet_index = 0;
+ memset(state->packet_buffer, 0, 16*1024);
+ memset(state->packet_copy_buffer, 0, 3*1024);
+ /* These registers have some bits "unaffected by software reset" */
+ /* Clear the reset bits */
+ state->dm9k_ncr &= 0xA0;
+ state->dm9k_nsr &= 0xD0;
+ /* Claim full duplex */
+ state->dm9k_ncr |= 1<<3;
+ /* Set link status to 1 */
+ state->dm9k_nsr |= 1<<6;
+ /* dm9k_wcr is unaffected or reserved, never reset */
+ /* MII control regs */
+ state->dm9k_epcr = 0x00;
+ state->dm9k_epar = 0x40;
+ /* reset the MII */
+ dm9000_soft_reset_mii(state);
+ dm9000_raise_irq(state); /* Clear any potentially pending IRQ */
+}
+
+static void dm9000_hard_reset(dm9000_state *state)
+{
+ state->dm9k_ncr = 0x00;
+ state->dm9k_nsr = 0x00;
+ state->dm9k_wcr = 0x00;
+ dm9000_soft_reset(state);
+}
+
+static void dm9000_do_transmit(dm9000_state *state)
+{
+ uint16_t idx, cnt, tptr;
+ idx = state->dm9k_trpa;
+ cnt = state->dm9k_txpl;
+ tptr = 0;
+ if( cnt > 3*1024 ) cnt = 3*1024; /* HARD CAP AT 3KiB */
+ DM9000_DBF("TX_Packet: %d bytes from %04x\n", cnt, idx);
+ while(cnt--) {
+ state->packet_copy_buffer[tptr++] = state->packet_buffer[idx++];
+ if( idx == 0x0C00 ) idx = 0;
+ }
+ /* DM9KNOTE: Assumes 16bit wiring */
+ idx = (idx+1) & ~1; /* Round up to nearest 16bit boundary */
+ if( idx == 0x0C00 ) idx = 0;
+ state->dm9k_trpa = idx;
+ dm9k_dump_packet(state->packet_copy_buffer, state->dm9k_txpl);
+ /* We have the copy buffer, now we do the transmit */
+ qemu_send_packet(state->vc, state->packet_copy_buffer, state->dm9k_txpl);
+ /* Clear the "please xmit" bit */
+ state->dm9k_tcr &= ~DM9000_TCR_TXREQ;
+ /* Set the TXEND bit */
+ state->dm9k_nsr |= 1<<(2+state->packet_index);
+ DM9000_DBF("TX: NSR=%02x PI=%d\n", state->dm9k_nsr, state->packet_index);
+ /* Claim a TX complete IRQ */
+ state->dm9k_isr |= 0x02; /* Packet transmitted latch */
+ /* And flip the next-packet bit */
+ state->packet_index = !state->packet_index;
+ dm9000_raise_irq(state);
+}
+
+static void dm9000_mii_read(dm9000_state *state)
+{
+ int mii_reg = (state->dm9k_epar) & 0x3f;
+ uint16_t ret = 0;
+ switch(mii_reg) {
+ case DM9000_MII_REG_BMCR:
+ ret = state->dm9k_mii_bmcr;
+ break;
+ case DM9000_MII_REG_STATUS:
+ ret = 0x782D; /* No 100/T4, Can 100/FD, Can 100/HD, Can 10/FD, Can 10/HD,
+ * No Preamble suppression, Autoneg complete, No remote fault,
+ * Can autoneg, link up, no jabber, extended capability */
+ break;
+ case DM9000_MII_REG_PHYID1:
+ ret = 0x0181;
+ break;
+ case DM9000_MII_REG_PHYID2:
+ ret = 0xB8C0;
+ break;
+ case DM9000_MII_REG_ANAR:
+ ret = state->dm9k_mii_anar;
+ break;
+ case DM9000_MII_REG_ANLPAR:
+ ret = 0x0400;
+ break;
+ case DM9000_MII_REG_ANER:
+ ret = 0x0001;
+ break;
+ case DM9000_MII_REG_DSCR:
+ ret = state->dm9k_mii_dscr;
+ break;
+ case DM9000_MII_REG_DSCSR:
+ ret = 0xF008;
+ break;
+ case DM9000_MII_REG_10BTCSR:
+ ret = 0x7800;
+ }
+ state->dm9k_epdr = ret;
+ DM9000_DBF("DM9000:MIIPHY: Read of MII reg %d gives %04x\n", mii_reg, state->dm9k_epdr);
+}
+
+static void dm9000_mii_write(dm9000_state *state)
+{
+ int mii_reg = (state->dm9k_epar) & 0x3f;
+ DM9000_DBF("DM9000:MIIPHY: Write of MII reg %d value %04x\n", mii_reg, state->dm9k_epdr);
+ switch(mii_reg) {
+ case DM9000_MII_REG_BMCR:
+ state->dm9k_mii_bmcr = (state->dm9k_epdr &~0x8000);
+ if( state->dm9k_epdr & 0x8000 ) dm9000_soft_reset_mii(state);
+ break;
+ case DM9000_MII_REG_ANAR:
+ state->dm9k_mii_anar = state->dm9k_epdr;
+ break;
+ case DM9000_MII_REG_DSCR:
+ state->dm9k_mii_dscr = state->dm9k_epdr & ~0x0008;
+ break;
+ }
+}
+
+static void dm9000_write(void *opaque, target_phys_addr_t address,
+ uint32_t value)
+{
+ dm9000_state *state = (dm9000_state *)opaque;
+#ifdef DM9000_DEBUG
+ int suppress_debug = 0;
+#endif
+
+ if (address == state->addr) {
+ if( (value != DM9000_REG_MRCMD) &&
+ (value != DM9000_REG_MWCMD) )
+ DM9000_DBF("DM9000: Address set to 0x%02x\n", value);
+ state->address = value;
+ return;
+ }
+
+ switch(state->address) {
+ case DM9000_REG_NCR:
+ state->dm9k_ncr = value & 0xDF;
+ if (state->dm9k_ncr & DM9000_NCR_RESET)
+ dm9000_soft_reset(state);
+ break;
+ case DM9000_REG_NSR:
+ state->dm9k_nsr &= ~(value & 0x2C);
+ break;
+ case DM9000_REG_TCR:
+ state->dm9k_tcr = value & 0xFF;
+ if( value & DM9000_TCR_TXREQ ) dm9000_do_transmit(state);
+ break;
+ case DM9000_REG_EPCR:
+ state->dm9k_epcr = value & 0xFF;
+ if( value & DM9000_MII_READ )
+ dm9000_mii_read(state);
+ else if( value & DM9000_MII_WRITE )
+ dm9000_mii_write(state);
+ break;
+ case DM9000_REG_EPAR:
+ state->dm9k_epar = value & 0xFF;
+ break;
+ case DM9000_REG_EPDRL:
+ state->dm9k_epdr &= 0xFF00;
+ state->dm9k_epdr |= value & 0xFF;
+ break;
+ case DM9000_REG_EPDRH:
+ state->dm9k_epdr &= 0xFF;
+ state->dm9k_epdr |= (value & 0xFF) << 8;
+ break;
+ case DM9000_REG_PAR0:
+ case DM9000_REG_PAR1:
+ case DM9000_REG_PAR2:
+ case DM9000_REG_PAR3:
+ case DM9000_REG_PAR4:
+ case DM9000_REG_PAR5:
+ state->macaddr[state->address - DM9000_REG_PAR0] = value & 0xFF;
+ break;
+ case DM9000_REG_MAR0:
+ case DM9000_REG_MAR1:
+ case DM9000_REG_MAR2:
+ case DM9000_REG_MAR3:
+ case DM9000_REG_MAR4:
+ case DM9000_REG_MAR5:
+ case DM9000_REG_MAR6:
+ case DM9000_REG_MAR7:
+ /* multicast hash setup */
+ state->multihash[state->address - DM9000_REG_MAR0] = value & 0xFF;
+ break;
+ case DM9000_REG_MRRL:
+ state->dm9k_mrr &= 0xFF00;
+ state->dm9k_mrr |= value & 0xFF;
+ break;
+ case DM9000_REG_MRRH:
+ state->dm9k_mrr &= 0xFF;
+ state->dm9k_mrr |= (value & 0xFF) << 8;
+ break;
+ case DM9000_REG_MWCMDX:
+ case DM9000_REG_MWCMD:
+ /* DM9KNOTE: This assumes a 16bit wide wiring */
+ state->packet_buffer[state->dm9k_mwr] = value & 0xFF;
+ state->packet_buffer[state->dm9k_mwr+1] = (value >> 8) & 0xFF;
+ if( state->address == DM9000_REG_MWCMD ) {
+ state->dm9k_mwr += 2;
+ if( state->dm9k_imr & DM9000_IMR_AUTOWRAP )
+ if( state->dm9k_mwr >= 0x0C00 )
+ state->dm9k_mwr -= 0x0C00;
+ }
+#ifdef DM9000_DEBUG
+ suppress_debug = 1;
+#endif
+ break;
+ case DM9000_REG_MWRL:
+ state->dm9k_mwr &= 0xFF00;
+ state->dm9k_mwr |= value & 0xFF;
+ break;
+ case DM9000_REG_MWRH:
+ state->dm9k_mwr &= 0xFF;
+ state->dm9k_mwr |= (value & 0xFF) << 8;
+ break;
+ case DM9000_REG_TXPLL:
+ state->dm9k_txpl &= 0xFF00;
+ state->dm9k_txpl |= value & 0xFF;
+ break;
+ case DM9000_REG_TXPLH:
+ state->dm9k_txpl &= 0xFF;
+ state->dm9k_txpl |= (value & 0xFF) << 8;
+ break;
+ case DM9000_REG_ISR:
+ state->dm9k_isr &= ~(value & 0x0F);
+ dm9000_raise_irq(state);
+ break;
+ case DM9000_REG_IMR:
+ if( !(state->dm9k_imr & DM9000_IMR_AUTOWRAP) &&
+ (value & DM9000_IMR_AUTOWRAP) )
+ state->dm9k_mrr = 0x0C00 | (state->dm9k_mrr & 0xFF);
+ state->dm9k_imr = value & 0xFF;
+ dm9000_raise_irq(state);
+ break;
+ }
+#ifdef DM9000_DEBUG
+ if(!suppress_debug) DM9000_DBF("DM9000: Write value %04x\n", value);
+#endif
+}
+
+static uint32_t dm9000_read(void *opaque, target_phys_addr_t address)
+{
+ dm9000_state *state = (dm9000_state *)opaque;
+ uint32_t ret = 0;
+#ifdef DM9000_DEBUG
+ int suppress_debug = 0;
+#endif
+
+ if (address == state->addr)
+ return state->address;
+ switch(state->address) {
+ case DM9000_REG_NCR:
+ ret = state->dm9k_ncr;
+ break;
+ case DM9000_REG_NSR:
+ ret = state->dm9k_nsr;
+ /* Note, TX1END and TX2END are *CLEAR ON READ* */
+ state->dm9k_nsr &= ~(DM9000_NSR_TX1END | DM9000_NSR_TX2END);
+ break;
+ case DM9000_REG_TCR:
+ ret = state->dm9k_tcr;
+ break;
+ case DM9000_REG_TSR1:
+ case DM9000_REG_TSR2:
+ ret = 0x00; /* No error, yay! */
+ break;
+ case DM9000_REG_EPCR:
+ ret = state->dm9k_epcr;
+ break;
+ case DM9000_REG_EPAR:
+ ret = state->dm9k_epar;
+ break;
+ case DM9000_REG_EPDRL:
+ ret = state->dm9k_epdr & 0xFF;
+ break;
+ case DM9000_REG_EPDRH:
+ ret = (state->dm9k_epdr >> 8) & 0xFF;
+ break;
+ case DM9000_REG_PAR0:
+ case DM9000_REG_PAR1:
+ case DM9000_REG_PAR2:
+ case DM9000_REG_PAR3:
+ case DM9000_REG_PAR4:
+ case DM9000_REG_PAR5:
+ ret = state->macaddr[state->address - DM9000_REG_PAR0];
+ break;
+ case DM9000_REG_MAR0:
+ case DM9000_REG_MAR1:
+ case DM9000_REG_MAR2:
+ case DM9000_REG_MAR3:
+ case DM9000_REG_MAR4:
+ case DM9000_REG_MAR5:
+ case DM9000_REG_MAR6:
+ case DM9000_REG_MAR7:
+ /* multicast hash */
+ ret = state->multihash[state->address - DM9000_REG_MAR0];
+ break;
+ case DM9000_REG_TRPAL:
+ ret = state->dm9k_trpa & 0xFF;
+ break;
+ case DM9000_REG_TRPAH:
+ ret = state->dm9k_trpa >> 8;
+ break;
+ case DM9000_REG_RWPAL:
+ ret = state->dm9k_rwpa & 0xFF;
+ break;
+ case DM9000_REG_RWPAH:
+ ret = state->dm9k_rwpa >> 8;
+ break;
+ case DM9000_REG_VIDL:
+ ret = 0x46;
+ break;
+ case DM9000_REG_VIDH:
+ ret = 0x0A;
+ break;
+ case DM9000_REG_PIDL:
+ ret = 0x00;
+ break;
+ case DM9000_REG_PIDH:
+ ret = 0x90;
+ break;
+ case DM9000_REG_CHIPR:
+ ret = 0x00;
+ break;
+ case DM9000_REG_MRCMDX:
+ case DM9000_REG_MRCMD:
+ /* DM9KNOTE: This assumes a 16bit wide wiring */
+ ret = state->packet_buffer[state->dm9k_mrr];
+ ret |= state->packet_buffer[state->dm9k_mrr+1] << 8;
+ if( state->address == DM9000_REG_MRCMD ) {
+ state->dm9k_mrr += 2;
+ if( state->dm9k_mrr >= (16*1024) ) state->dm9k_mrr -= (16*1024);
+ if( state->dm9k_imr & DM9000_IMR_AUTOWRAP )
+ if( state->dm9k_mrr < 0x0C00 )
+ state->dm9k_mrr += 0x0C00;
+ }
+#ifdef DM9000_DEBUG
+ if (state->address==DM9000_REG_MRCMD)
+ suppress_debug = 1;
+#endif
+ break;
+ case DM9000_REG_MRRL:
+ ret = state->dm9k_mrr & 0xFF;
+ break;
+ case DM9000_REG_MRRH:
+ ret = state->dm9k_mrr >> 8;
+ break;
+ case DM9000_REG_MWRL:
+ ret = state->dm9k_mwr & 0xFF;
+ break;
+ case DM9000_REG_MWRH:
+ ret = state->dm9k_mwr >> 8;
+ break;
+ case DM9000_REG_TXPLL:
+ ret = state->dm9k_txpl & 0xFF;
+ break;
+ case DM9000_REG_TXPLH:
+ ret = state->dm9k_txpl >> 8;
+ break;
+ case DM9000_REG_ISR:
+ ret = state->dm9k_isr;
+ break;
+ case DM9000_REG_IMR:
+ ret = state->dm9k_imr;
+ break;
+ default:
+ ret = 0;
+ }
+
+#ifdef DM9000_DEBUG
+ if(!suppress_debug) DM9000_DBF("DM9000: Read gives: %04x\n", ret);
+#endif
+ return ret;
+}
+
+
+
+static int dm9000_can_receive(void *opaque)
+{
+ dm9000_state *state = (dm9000_state *)opaque;
+ uint16_t rx_space;
+ if( state->dm9k_rwpa < state->dm9k_mrr )
+ rx_space = state->dm9k_mrr - state->dm9k_rwpa;
+ else
+ rx_space = (13*1024) - (state->dm9k_rwpa - state->dm9k_mrr);
+ DM9000_DBF("DM9000:RX_Packet: Asked about RX, rwpa=%d mrr=%d => space is %d bytes\n",
+ state->dm9k_rwpa, state->dm9k_mrr, rx_space);
+ if (rx_space > 2048) return 1;
+ return 0;
+}
+
+static void dm9000_receive(void *opaque, const uint8_t *buf, int size)
+{
+ dm9000_state *state = (dm9000_state *)opaque;
+ uint16_t rxptr = state->dm9k_rwpa;
+ uint8_t magic_padding = 4;
+ if( size > 2048 ) return; /* La La La, I can't hear you */
+ /* Fill out the magical header structure */
+ DM9000_DBF("DM9000:RX_Packet: %d bytes into buffer at %04x\n", size, rxptr);
+ dm9k_dump_packet(buf, size);
+ if( size < 64 ) magic_padding += (64 - size);
+ DM9000_DBF("DM9000:RX_Packet: Magical padding is %d bytes\n", magic_padding);
+ size += magic_padding; /* The magical CRC word */
+ state->packet_buffer[state->dm9k_rwpa-4] = 0x01; /* Packet read */
+ state->packet_buffer[state->dm9k_rwpa-3] = 0x00; /* Status OK */
+ state->packet_buffer[state->dm9k_rwpa-2] = size & 0xFF; /* Size LOW */
+ state->packet_buffer[state->dm9k_rwpa-1] = (size & 0xFF00)>>8; /* Size HIGH */
+ size += 4; /* The magical next header (which we zero for fun) */
+ while(size--) {
+ if( size > (magic_padding + 3) )
+ state->packet_buffer[rxptr++] = *buf++;
+ else
+ state->packet_buffer[rxptr++] = 0x00; /* Clear to the next header */
+ /* DM9KNOTE: Assumes 16 bit wired config */
+ if (size == 4) rxptr = (rxptr+1) & ~1; /* At end of packet, realign */
+ if( rxptr >= (16*1024) ) rxptr -= (16*1024);
+ if( rxptr < 0x0C00 ) rxptr += 0x0C00;
+ }
+ state->dm9k_rwpa = rxptr;
+ state->dm9k_isr |= 0x01; /* RX interrupt, yay */
+ dm9000_raise_irq(state);
+}
+
+
+static CPUReadMemoryFunc *dm9000_readfn[] = {
+ dm9000_read,
+ dm9000_read,
+ dm9000_read
+};
+
+static CPUWriteMemoryFunc *dm9000_writefn[] = {
+ dm9000_write,
+ dm9000_write,
+ dm9000_write
+};
+
+/* initialises a dm9000 ethernet controller
+ * The dm9k has a single 16bit wide address and data port through which all
+ * operations are multiplexed, there is a single IRQ
+ */
+void dm9000_init(NICInfo *nd, target_phys_addr_t base_addr,
+ uint32_t addr_offset, uint32_t data_offset,
+ qemu_irq irq)
+{
+ dm9000_state *state;
+ int iomemtype;
+
+ state = (dm9000_state *)qemu_mallocz(sizeof(dm9000_state));
+ iomemtype = cpu_register_io_memory(0, dm9000_readfn,
+ dm9000_writefn, state);
+ cpu_register_physical_memory(base_addr, MAX(addr_offset, data_offset) + 4, iomemtype);
+ state->addr = base_addr + addr_offset;
+ state->data = base_addr + data_offset;
+ state->irq = irq;
+ memcpy(state->macaddr, nd->macaddr, 6);
+
+ dm9000_hard_reset(state);
+
+ state->vc = qemu_new_vlan_client(nd->vlan, dm9000_receive,
+ dm9000_can_receive, state);
+
+}
=== added file 'hw/dm9000.h'
--- hw/dm9000.h 1970-01-01 00:00:00 +0000
+++ hw/dm9000.h 2008-10-13 10:06:18 +0000
@@ -0,0 +1,28 @@
+/* hw/dm9000.h
+ *
+ * DM9000 Ethernet interface
+ *
+ * Copyright 2006, 2008 Daniel Silverstone and Vincent Sanders
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 only.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#ifndef QEMU_HW_DM9000_H
+#define QEMU_HW_DM9000_H
+
+void dm9000_init(NICInfo *nd, target_phys_addr_t base_addr, uint32_t addr_offset,
+ uint32_t data_offset, qemu_irq irq);
+
+#endif
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [Qemu-devel] [PATCH] Davicom DM9000 emulation
2008-10-13 10:12 [Qemu-devel] [PATCH] Davicom DM9000 emulation Daniel Silverstone
@ 2008-10-13 12:47 ` Paul Brook
2008-10-13 14:01 ` Daniel Silverstone
0 siblings, 1 reply; 9+ messages in thread
From: Paul Brook @ 2008-10-13 12:47 UTC (permalink / raw)
To: qemu-devel, dsilvers
On Monday 13 October 2008, Daniel Silverstone wrote:
> Hi,
>
> Attached is a patch which provides Davicom DM9000E emulation support
> including the ability to compile in a pcap-style network trace of the
> device.
Network dumping does not belong in device specific code. It should be a
separate device that can be attached to the virtual network.
> +#define DM9000_DBF(X...) if(0) fprintf(stderr, X)
This is wrong. Use do {} while(0)
> +/* hw/dm9000.h
A new header file for one init function is excessive. dm9000_init should go in
the same place as smc91c111_init.
> +OBJS += dm9000.o
Likewise, this should be the same as smc91c111.o.
Paul
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [Qemu-devel] [PATCH] Davicom DM9000 emulation
2008-10-13 12:47 ` Paul Brook
@ 2008-10-13 14:01 ` Daniel Silverstone
2008-10-13 14:20 ` Daniel Silverstone
0 siblings, 1 reply; 9+ messages in thread
From: Daniel Silverstone @ 2008-10-13 14:01 UTC (permalink / raw)
To: qemu-devel
[-- Attachment #1: Type: text/plain, Size: 1572 bytes --]
On Mon, 2008-10-13 at 13:47 +0100, Paul Brook wrote:
> > Attached is a patch which provides Davicom DM9000E emulation support
> > including the ability to compile in a pcap-style network trace of the
> > device.
> Network dumping does not belong in device specific code. It should be a
> separate device that can be attached to the virtual network.
Accepted. I shall look into the plausiblity of providing such a device.
For now, I've removed the dump code from the dm9000 driver
> > +#define DM9000_DBF(X...) if(0) fprintf(stderr, X)
> This is wrong. Use do {} while(0)
This means that the debug statements do not get compile-checked when
debugging is disabled, which can lead to compile errors when debugging
is turned on. I've wrapped both the enabled and disabled variants in
do/while code so that they are guaranteed not to confuse condition
ladders.
> > +/* hw/dm9000.h
> A new header file for one init function is excessive. dm9000_init should go in
> the same place as smc91c111_init.
Accepted, moved to hw/devices.h
> > +OBJS += dm9000.o
> Likewise, this should be the same as smc91c111.o.
Migrated into the TARGET_ARCH=arm section of Makefile.target although
I'd like to suggest that eventually we would need a section which is
enabled for all MMIO-type systems, since dm9000 chips could be used on
MIPS, PPC etc which support MMIO.
Attached is the fixed patch with these changes.
Regards,
Daniel.
--
Daniel Silverstone http://www.simtec.co.uk/
PGP mail accepted and encouraged. Key Id: 2BC8 4016 2068 7895
[-- Attachment #2: Davicom DM9000 Support Patch --]
[-- Type: text/x-patch, Size: 21937 bytes --]
The Davicom DM9000 10/100 Ethernet controller.
This driver supplies an MMIO based DM9000 interface which can be
instantiated in the emulation's memory map to provide an ethernet
interface for ARM "local-bus" type systems.
Signed-off-by: Daniel Silverstone <dsilvers@simtec.co.uk>
Signed-off-by: Vincent Sanders <vince@simtec.co.uk>
Makefile.target | 2
hw/devices.h | 4
hw/dm9000.c | 624 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 629 insertions(+), 1 deletion(-)
=== modified file 'Makefile.target'
--- Makefile.target 2008-10-13 03:14:31 +0000
+++ Makefile.target 2008-10-13 13:55:37 +0000
@@ -599,7 +599,7 @@
ifeq ($(TARGET_BASE_ARCH), arm)
OBJS+= integratorcp.o versatilepb.o ps2.o smc91c111.o arm_pic.o arm_timer.o
OBJS+= arm_boot.o pl011.o pl031.o pl050.o pl080.o pl110.o pl181.o pl190.o
-OBJS+= versatile_pci.o ptimer.o
+OBJS+= versatile_pci.o ptimer.o dm9000.o
OBJS+= realview_gic.o realview.o arm_sysctl.o mpcore.o
OBJS+= armv7m.o armv7m_nvic.o stellaris.o pl022.o stellaris_enet.o
OBJS+= pl061.o
=== modified file 'hw/devices.h'
--- hw/devices.h 2008-06-09 00:03:13 +0000
+++ hw/devices.h 2008-10-13 13:54:52 +0000
@@ -3,6 +3,10 @@
/* Devices that have nowhere better to go. */
+/* dm9000.c */
+void dm9000_init(NICInfo *nd, target_phys_addr_t base_addr, uint32_t addr_offset,
+ uint32_t data_offset, qemu_irq irq);
+
/* smc91c111.c */
void smc91c111_init(NICInfo *, uint32_t, qemu_irq);
=== added file 'hw/dm9000.c'
--- hw/dm9000.c 1970-01-01 00:00:00 +0000
+++ hw/dm9000.c 2008-10-13 13:57:44 +0000
@@ -0,0 +1,624 @@
+/* hw/dm9000.c
+ *
+ * DM9000 Ethernet interface
+ *
+ * Copyright 2006, 2008 Daniel Silverstone and Vincent Sanders
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 only.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <string.h>
+#include "qemu-common.h"
+#include "hw/irq.h"
+#include "net.h"
+
+/* Comment this out if you don't want register debug on stderr */
+/* #define DM9000_DEBUG */
+
+#ifdef DM9000_DEBUG
+#define DM9000_DBF(X...) do { fprintf(stderr, X) } while(0)
+#else
+#define DM9000_DBF(X...) do { if (0) fprintf(stderr, X) } while (0)
+#endif
+
+#define DM9000_REG_NCR 0x00
+#define DM9000_REG_NSR 0x01
+#define DM9000_REG_TCR 0x02
+#define DM9000_REG_TSR1 0x03
+#define DM9000_REG_TSR2 0x04
+#define DM9000_REG_RCR 0x05
+#define DM9000_REG_RSR 0x06
+#define DM9000_REG_ROCR 0x07
+#define DM9000_REG_BPTR 0x08
+#define DM9000_REG_FCTR 0x09
+#define DM9000_REG_FCR 0x0A
+#define DM9000_REG_EPCR 0x0B
+#define DM9000_REG_EPAR 0x0C
+#define DM9000_REG_EPDRL 0x0D
+#define DM9000_REG_EPDRH 0x0E
+#define DM9000_REG_WCR 0x0F
+#define DM9000_REG_PAR0 0x10
+#define DM9000_REG_PAR1 0x11
+#define DM9000_REG_PAR2 0x12
+#define DM9000_REG_PAR3 0x13
+#define DM9000_REG_PAR4 0x14
+#define DM9000_REG_PAR5 0x15
+#define DM9000_REG_MAR0 0x16
+#define DM9000_REG_MAR1 0x17
+#define DM9000_REG_MAR2 0x18
+#define DM9000_REG_MAR3 0x19
+#define DM9000_REG_MAR4 0x1A
+#define DM9000_REG_MAR5 0x1B
+#define DM9000_REG_MAR6 0x1C
+#define DM9000_REG_MAR7 0x1D
+#define DM9000_REG_GPCR 0x1E
+#define DM9000_REG_GPR 0x1F
+#define DM9000_REG_TRPAL 0x22
+#define DM9000_REG_TRPAH 0x23
+#define DM9000_REG_RWPAL 0x24
+#define DM9000_REG_RWPAH 0x25
+#define DM9000_REG_VIDL 0x28
+#define DM9000_REG_VIDH 0x29
+#define DM9000_REG_PIDL 0x2A
+#define DM9000_REG_PIDH 0x2B
+#define DM9000_REG_CHIPR 0x2C
+#define DM9000_REG_SMCR 0x2F
+#define DM9000_REG_MRCMDX 0xF0
+#define DM9000_REG_MRCMD 0xF2
+#define DM9000_REG_MRRL 0xF4
+#define DM9000_REG_MRRH 0xF5
+#define DM9000_REG_MWCMDX 0xF6
+#define DM9000_REG_MWCMD 0xF8
+#define DM9000_REG_MWRL 0xFA
+#define DM9000_REG_MWRH 0xFB
+#define DM9000_REG_TXPLL 0xFC
+#define DM9000_REG_TXPLH 0xFD
+#define DM9000_REG_ISR 0xFE
+#define DM9000_REG_IMR 0xFF
+
+#define DM9000_NCR_RESET 0x01
+#define DM9000_NSR_TX1END 0x04
+#define DM9000_NSR_TX2END 0x08
+#define DM9000_TCR_TXREQ 0x01
+
+#define DM9000_IMR_AUTOWRAP 0x80
+
+#define DM9000_MII_READ 0x0C
+#define DM9000_MII_WRITE 0x0A
+
+#define DM9000_MII_REG_BMCR 0x00
+#define DM9000_MII_REG_STATUS 0x01
+#define DM9000_MII_REG_PHYID1 0x02
+#define DM9000_MII_REG_PHYID2 0x03
+#define DM9000_MII_REG_ANAR 0x04
+#define DM9000_MII_REG_ANLPAR 0x05
+#define DM9000_MII_REG_ANER 0x06
+#define DM9000_MII_REG_DSCR 0x10
+#define DM9000_MII_REG_DSCSR 0x11
+#define DM9000_MII_REG_10BTCSR 0x12
+
+
+typedef struct {
+ uint32_t addr; /* address port */
+ uint32_t data; /* data port */
+ VLANClientState *vc;
+ qemu_irq irq;
+ uint8_t macaddr[6];
+ uint8_t multihash[8]; /* multicast hash table */
+ uint8_t address; /* The internal magial address */
+ uint8_t packet_buffer[16 * 1024];
+ uint16_t dm9k_mrr, dm9k_mwr; /* Read and write address registers */
+ uint16_t dm9k_txpl; /* TX packet length */
+ uint16_t dm9k_trpa, dm9k_rwpa; /* TX Read ptr address, RX write ptr address */
+ uint8_t dm9k_imr, dm9k_isr; /* Interrupt mask register and status register*/
+ uint8_t dm9k_ncr, dm9k_nsr; /* Network control register, network status register */
+ uint8_t dm9k_wcr; /* Wakeup control */
+ uint8_t dm9k_tcr; /* Transmission control register */
+ uint8_t packet_copy_buffer[3 * 1024]; /* packet copy buffer */
+ unsigned int packet_index:1; /* 0 == packet I, 1 == packet II */
+
+ /* Internal MII PHY state */
+ uint8_t dm9k_epcr; /* EEPROM/PHY control register */
+ uint8_t dm9k_epar; /* EEPROM/PHY address register */
+ uint16_t dm9k_epdr; /* EEPROM/PHY data register */
+ /* MII Regs */
+ uint16_t dm9k_mii_bmcr;
+ uint16_t dm9k_mii_anar;
+ uint16_t dm9k_mii_dscr;
+
+} dm9000_state;
+
+static void dm9000_raise_irq(dm9000_state *state)
+{
+ int level = ((state->dm9k_isr & state->dm9k_imr) & 0x03) != 0;
+ DM9000_DBF("DM9000: Set IRQ level %d\n", level);
+ qemu_set_irq(state->irq, level);
+}
+
+static void dm9000_soft_reset_mii(dm9000_state *state)
+{
+ state->dm9k_mii_bmcr = 0x3100; /* 100Mbps, AUTONEG, FULL DUPLEX */
+ state->dm9k_mii_anar = 0x01E1;
+ state->dm9k_mii_dscr = 0x0410;
+}
+
+static void dm9000_soft_reset(dm9000_state *state)
+{
+ DM9000_DBF("DM9000: Soft Reset\n");
+ state->dm9k_mrr = state->dm9k_mwr = state->dm9k_txpl = state->dm9k_trpa = 0x0000;
+ state->dm9k_rwpa = 0x0C04;
+ state->dm9k_imr = 0;
+ state->dm9k_isr = 0; /* 16 bit mode, no interrupts asserted */
+ state->dm9k_tcr = 0;
+ state->packet_index = 0;
+ memset(state->packet_buffer, 0, 16*1024);
+ memset(state->packet_copy_buffer, 0, 3*1024);
+ /* These registers have some bits "unaffected by software reset" */
+ /* Clear the reset bits */
+ state->dm9k_ncr &= 0xA0;
+ state->dm9k_nsr &= 0xD0;
+ /* Claim full duplex */
+ state->dm9k_ncr |= 1<<3;
+ /* Set link status to 1 */
+ state->dm9k_nsr |= 1<<6;
+ /* dm9k_wcr is unaffected or reserved, never reset */
+ /* MII control regs */
+ state->dm9k_epcr = 0x00;
+ state->dm9k_epar = 0x40;
+ /* reset the MII */
+ dm9000_soft_reset_mii(state);
+ dm9000_raise_irq(state); /* Clear any potentially pending IRQ */
+}
+
+static void dm9000_hard_reset(dm9000_state *state)
+{
+ state->dm9k_ncr = 0x00;
+ state->dm9k_nsr = 0x00;
+ state->dm9k_wcr = 0x00;
+ dm9000_soft_reset(state);
+}
+
+static void dm9000_do_transmit(dm9000_state *state)
+{
+ uint16_t idx, cnt, tptr;
+ idx = state->dm9k_trpa;
+ cnt = state->dm9k_txpl;
+ tptr = 0;
+ if( cnt > 3*1024 ) cnt = 3*1024; /* HARD CAP AT 3KiB */
+ DM9000_DBF("TX_Packet: %d bytes from %04x\n", cnt, idx);
+ while(cnt--) {
+ state->packet_copy_buffer[tptr++] = state->packet_buffer[idx++];
+ if( idx == 0x0C00 ) idx = 0;
+ }
+ /* DM9KNOTE: Assumes 16bit wiring */
+ idx = (idx+1) & ~1; /* Round up to nearest 16bit boundary */
+ if( idx == 0x0C00 ) idx = 0;
+ state->dm9k_trpa = idx;
+ /* We have the copy buffer, now we do the transmit */
+ qemu_send_packet(state->vc, state->packet_copy_buffer, state->dm9k_txpl);
+ /* Clear the "please xmit" bit */
+ state->dm9k_tcr &= ~DM9000_TCR_TXREQ;
+ /* Set the TXEND bit */
+ state->dm9k_nsr |= 1<<(2+state->packet_index);
+ DM9000_DBF("TX: NSR=%02x PI=%d\n", state->dm9k_nsr, state->packet_index);
+ /* Claim a TX complete IRQ */
+ state->dm9k_isr |= 0x02; /* Packet transmitted latch */
+ /* And flip the next-packet bit */
+ state->packet_index = !state->packet_index;
+ dm9000_raise_irq(state);
+}
+
+static void dm9000_mii_read(dm9000_state *state)
+{
+ int mii_reg = (state->dm9k_epar) & 0x3f;
+ uint16_t ret = 0;
+ switch(mii_reg) {
+ case DM9000_MII_REG_BMCR:
+ ret = state->dm9k_mii_bmcr;
+ break;
+ case DM9000_MII_REG_STATUS:
+ ret = 0x782D; /* No 100/T4, Can 100/FD, Can 100/HD, Can 10/FD, Can 10/HD,
+ * No Preamble suppression, Autoneg complete, No remote fault,
+ * Can autoneg, link up, no jabber, extended capability */
+ break;
+ case DM9000_MII_REG_PHYID1:
+ ret = 0x0181;
+ break;
+ case DM9000_MII_REG_PHYID2:
+ ret = 0xB8C0;
+ break;
+ case DM9000_MII_REG_ANAR:
+ ret = state->dm9k_mii_anar;
+ break;
+ case DM9000_MII_REG_ANLPAR:
+ ret = 0x0400;
+ break;
+ case DM9000_MII_REG_ANER:
+ ret = 0x0001;
+ break;
+ case DM9000_MII_REG_DSCR:
+ ret = state->dm9k_mii_dscr;
+ break;
+ case DM9000_MII_REG_DSCSR:
+ ret = 0xF008;
+ break;
+ case DM9000_MII_REG_10BTCSR:
+ ret = 0x7800;
+ }
+ state->dm9k_epdr = ret;
+ DM9000_DBF("DM9000:MIIPHY: Read of MII reg %d gives %04x\n", mii_reg, state->dm9k_epdr);
+}
+
+static void dm9000_mii_write(dm9000_state *state)
+{
+ int mii_reg = (state->dm9k_epar) & 0x3f;
+ DM9000_DBF("DM9000:MIIPHY: Write of MII reg %d value %04x\n", mii_reg, state->dm9k_epdr);
+ switch(mii_reg) {
+ case DM9000_MII_REG_BMCR:
+ state->dm9k_mii_bmcr = (state->dm9k_epdr &~0x8000);
+ if( state->dm9k_epdr & 0x8000 ) dm9000_soft_reset_mii(state);
+ break;
+ case DM9000_MII_REG_ANAR:
+ state->dm9k_mii_anar = state->dm9k_epdr;
+ break;
+ case DM9000_MII_REG_DSCR:
+ state->dm9k_mii_dscr = state->dm9k_epdr & ~0x0008;
+ break;
+ }
+}
+
+static void dm9000_write(void *opaque, target_phys_addr_t address,
+ uint32_t value)
+{
+ dm9000_state *state = (dm9000_state *)opaque;
+#ifdef DM9000_DEBUG
+ int suppress_debug = 0;
+#endif
+
+ if (address == state->addr) {
+ if( (value != DM9000_REG_MRCMD) &&
+ (value != DM9000_REG_MWCMD) )
+ DM9000_DBF("DM9000: Address set to 0x%02x\n", value);
+ state->address = value;
+ return;
+ }
+
+ switch(state->address) {
+ case DM9000_REG_NCR:
+ state->dm9k_ncr = value & 0xDF;
+ if (state->dm9k_ncr & DM9000_NCR_RESET)
+ dm9000_soft_reset(state);
+ break;
+ case DM9000_REG_NSR:
+ state->dm9k_nsr &= ~(value & 0x2C);
+ break;
+ case DM9000_REG_TCR:
+ state->dm9k_tcr = value & 0xFF;
+ if( value & DM9000_TCR_TXREQ ) dm9000_do_transmit(state);
+ break;
+ case DM9000_REG_EPCR:
+ state->dm9k_epcr = value & 0xFF;
+ if( value & DM9000_MII_READ )
+ dm9000_mii_read(state);
+ else if( value & DM9000_MII_WRITE )
+ dm9000_mii_write(state);
+ break;
+ case DM9000_REG_EPAR:
+ state->dm9k_epar = value & 0xFF;
+ break;
+ case DM9000_REG_EPDRL:
+ state->dm9k_epdr &= 0xFF00;
+ state->dm9k_epdr |= value & 0xFF;
+ break;
+ case DM9000_REG_EPDRH:
+ state->dm9k_epdr &= 0xFF;
+ state->dm9k_epdr |= (value & 0xFF) << 8;
+ break;
+ case DM9000_REG_PAR0:
+ case DM9000_REG_PAR1:
+ case DM9000_REG_PAR2:
+ case DM9000_REG_PAR3:
+ case DM9000_REG_PAR4:
+ case DM9000_REG_PAR5:
+ state->macaddr[state->address - DM9000_REG_PAR0] = value & 0xFF;
+ break;
+ case DM9000_REG_MAR0:
+ case DM9000_REG_MAR1:
+ case DM9000_REG_MAR2:
+ case DM9000_REG_MAR3:
+ case DM9000_REG_MAR4:
+ case DM9000_REG_MAR5:
+ case DM9000_REG_MAR6:
+ case DM9000_REG_MAR7:
+ /* multicast hash setup */
+ state->multihash[state->address - DM9000_REG_MAR0] = value & 0xFF;
+ break;
+ case DM9000_REG_MRRL:
+ state->dm9k_mrr &= 0xFF00;
+ state->dm9k_mrr |= value & 0xFF;
+ break;
+ case DM9000_REG_MRRH:
+ state->dm9k_mrr &= 0xFF;
+ state->dm9k_mrr |= (value & 0xFF) << 8;
+ break;
+ case DM9000_REG_MWCMDX:
+ case DM9000_REG_MWCMD:
+ /* DM9KNOTE: This assumes a 16bit wide wiring */
+ state->packet_buffer[state->dm9k_mwr] = value & 0xFF;
+ state->packet_buffer[state->dm9k_mwr+1] = (value >> 8) & 0xFF;
+ if( state->address == DM9000_REG_MWCMD ) {
+ state->dm9k_mwr += 2;
+ if( state->dm9k_imr & DM9000_IMR_AUTOWRAP )
+ if( state->dm9k_mwr >= 0x0C00 )
+ state->dm9k_mwr -= 0x0C00;
+ }
+#ifdef DM9000_DEBUG
+ suppress_debug = 1;
+#endif
+ break;
+ case DM9000_REG_MWRL:
+ state->dm9k_mwr &= 0xFF00;
+ state->dm9k_mwr |= value & 0xFF;
+ break;
+ case DM9000_REG_MWRH:
+ state->dm9k_mwr &= 0xFF;
+ state->dm9k_mwr |= (value & 0xFF) << 8;
+ break;
+ case DM9000_REG_TXPLL:
+ state->dm9k_txpl &= 0xFF00;
+ state->dm9k_txpl |= value & 0xFF;
+ break;
+ case DM9000_REG_TXPLH:
+ state->dm9k_txpl &= 0xFF;
+ state->dm9k_txpl |= (value & 0xFF) << 8;
+ break;
+ case DM9000_REG_ISR:
+ state->dm9k_isr &= ~(value & 0x0F);
+ dm9000_raise_irq(state);
+ break;
+ case DM9000_REG_IMR:
+ if( !(state->dm9k_imr & DM9000_IMR_AUTOWRAP) &&
+ (value & DM9000_IMR_AUTOWRAP) )
+ state->dm9k_mrr = 0x0C00 | (state->dm9k_mrr & 0xFF);
+ state->dm9k_imr = value & 0xFF;
+ dm9000_raise_irq(state);
+ break;
+ }
+#ifdef DM9000_DEBUG
+ if(!suppress_debug) DM9000_DBF("DM9000: Write value %04x\n", value);
+#endif
+}
+
+static uint32_t dm9000_read(void *opaque, target_phys_addr_t address)
+{
+ dm9000_state *state = (dm9000_state *)opaque;
+ uint32_t ret = 0;
+#ifdef DM9000_DEBUG
+ int suppress_debug = 0;
+#endif
+
+ if (address == state->addr)
+ return state->address;
+ switch(state->address) {
+ case DM9000_REG_NCR:
+ ret = state->dm9k_ncr;
+ break;
+ case DM9000_REG_NSR:
+ ret = state->dm9k_nsr;
+ /* Note, TX1END and TX2END are *CLEAR ON READ* */
+ state->dm9k_nsr &= ~(DM9000_NSR_TX1END | DM9000_NSR_TX2END);
+ break;
+ case DM9000_REG_TCR:
+ ret = state->dm9k_tcr;
+ break;
+ case DM9000_REG_TSR1:
+ case DM9000_REG_TSR2:
+ ret = 0x00; /* No error, yay! */
+ break;
+ case DM9000_REG_EPCR:
+ ret = state->dm9k_epcr;
+ break;
+ case DM9000_REG_EPAR:
+ ret = state->dm9k_epar;
+ break;
+ case DM9000_REG_EPDRL:
+ ret = state->dm9k_epdr & 0xFF;
+ break;
+ case DM9000_REG_EPDRH:
+ ret = (state->dm9k_epdr >> 8) & 0xFF;
+ break;
+ case DM9000_REG_PAR0:
+ case DM9000_REG_PAR1:
+ case DM9000_REG_PAR2:
+ case DM9000_REG_PAR3:
+ case DM9000_REG_PAR4:
+ case DM9000_REG_PAR5:
+ ret = state->macaddr[state->address - DM9000_REG_PAR0];
+ break;
+ case DM9000_REG_MAR0:
+ case DM9000_REG_MAR1:
+ case DM9000_REG_MAR2:
+ case DM9000_REG_MAR3:
+ case DM9000_REG_MAR4:
+ case DM9000_REG_MAR5:
+ case DM9000_REG_MAR6:
+ case DM9000_REG_MAR7:
+ /* multicast hash */
+ ret = state->multihash[state->address - DM9000_REG_MAR0];
+ break;
+ case DM9000_REG_TRPAL:
+ ret = state->dm9k_trpa & 0xFF;
+ break;
+ case DM9000_REG_TRPAH:
+ ret = state->dm9k_trpa >> 8;
+ break;
+ case DM9000_REG_RWPAL:
+ ret = state->dm9k_rwpa & 0xFF;
+ break;
+ case DM9000_REG_RWPAH:
+ ret = state->dm9k_rwpa >> 8;
+ break;
+ case DM9000_REG_VIDL:
+ ret = 0x46;
+ break;
+ case DM9000_REG_VIDH:
+ ret = 0x0A;
+ break;
+ case DM9000_REG_PIDL:
+ ret = 0x00;
+ break;
+ case DM9000_REG_PIDH:
+ ret = 0x90;
+ break;
+ case DM9000_REG_CHIPR:
+ ret = 0x00;
+ break;
+ case DM9000_REG_MRCMDX:
+ case DM9000_REG_MRCMD:
+ /* DM9KNOTE: This assumes a 16bit wide wiring */
+ ret = state->packet_buffer[state->dm9k_mrr];
+ ret |= state->packet_buffer[state->dm9k_mrr+1] << 8;
+ if( state->address == DM9000_REG_MRCMD ) {
+ state->dm9k_mrr += 2;
+ if( state->dm9k_mrr >= (16*1024) ) state->dm9k_mrr -= (16*1024);
+ if( state->dm9k_imr & DM9000_IMR_AUTOWRAP )
+ if( state->dm9k_mrr < 0x0C00 )
+ state->dm9k_mrr += 0x0C00;
+ }
+#ifdef DM9000_DEBUG
+ if (state->address==DM9000_REG_MRCMD)
+ suppress_debug = 1;
+#endif
+ break;
+ case DM9000_REG_MRRL:
+ ret = state->dm9k_mrr & 0xFF;
+ break;
+ case DM9000_REG_MRRH:
+ ret = state->dm9k_mrr >> 8;
+ break;
+ case DM9000_REG_MWRL:
+ ret = state->dm9k_mwr & 0xFF;
+ break;
+ case DM9000_REG_MWRH:
+ ret = state->dm9k_mwr >> 8;
+ break;
+ case DM9000_REG_TXPLL:
+ ret = state->dm9k_txpl & 0xFF;
+ break;
+ case DM9000_REG_TXPLH:
+ ret = state->dm9k_txpl >> 8;
+ break;
+ case DM9000_REG_ISR:
+ ret = state->dm9k_isr;
+ break;
+ case DM9000_REG_IMR:
+ ret = state->dm9k_imr;
+ break;
+ default:
+ ret = 0;
+ }
+
+#ifdef DM9000_DEBUG
+ if(!suppress_debug) DM9000_DBF("DM9000: Read gives: %04x\n", ret);
+#endif
+ return ret;
+}
+
+
+
+static int dm9000_can_receive(void *opaque)
+{
+ dm9000_state *state = (dm9000_state *)opaque;
+ uint16_t rx_space;
+ if( state->dm9k_rwpa < state->dm9k_mrr )
+ rx_space = state->dm9k_mrr - state->dm9k_rwpa;
+ else
+ rx_space = (13*1024) - (state->dm9k_rwpa - state->dm9k_mrr);
+ DM9000_DBF("DM9000:RX_Packet: Asked about RX, rwpa=%d mrr=%d => space is %d bytes\n",
+ state->dm9k_rwpa, state->dm9k_mrr, rx_space);
+ if (rx_space > 2048) return 1;
+ return 0;
+}
+
+static void dm9000_receive(void *opaque, const uint8_t *buf, int size)
+{
+ dm9000_state *state = (dm9000_state *)opaque;
+ uint16_t rxptr = state->dm9k_rwpa;
+ uint8_t magic_padding = 4;
+ if( size > 2048 ) return; /* La La La, I can't hear you */
+ /* Fill out the magical header structure */
+ DM9000_DBF("DM9000:RX_Packet: %d bytes into buffer at %04x\n", size, rxptr);
+ if( size < 64 ) magic_padding += (64 - size);
+ DM9000_DBF("DM9000:RX_Packet: Magical padding is %d bytes\n", magic_padding);
+ size += magic_padding; /* The magical CRC word */
+ state->packet_buffer[state->dm9k_rwpa-4] = 0x01; /* Packet read */
+ state->packet_buffer[state->dm9k_rwpa-3] = 0x00; /* Status OK */
+ state->packet_buffer[state->dm9k_rwpa-2] = size & 0xFF; /* Size LOW */
+ state->packet_buffer[state->dm9k_rwpa-1] = (size & 0xFF00)>>8; /* Size HIGH */
+ size += 4; /* The magical next header (which we zero for fun) */
+ while(size--) {
+ if( size > (magic_padding + 3) )
+ state->packet_buffer[rxptr++] = *buf++;
+ else
+ state->packet_buffer[rxptr++] = 0x00; /* Clear to the next header */
+ /* DM9KNOTE: Assumes 16 bit wired config */
+ if (size == 4) rxptr = (rxptr+1) & ~1; /* At end of packet, realign */
+ if( rxptr >= (16*1024) ) rxptr -= (16*1024);
+ if( rxptr < 0x0C00 ) rxptr += 0x0C00;
+ }
+ state->dm9k_rwpa = rxptr;
+ state->dm9k_isr |= 0x01; /* RX interrupt, yay */
+ dm9000_raise_irq(state);
+}
+
+
+static CPUReadMemoryFunc *dm9000_readfn[] = {
+ dm9000_read,
+ dm9000_read,
+ dm9000_read
+};
+
+static CPUWriteMemoryFunc *dm9000_writefn[] = {
+ dm9000_write,
+ dm9000_write,
+ dm9000_write
+};
+
+/* initialises a dm9000 ethernet controller
+ * The dm9k has a single 16bit wide address and data port through which all
+ * operations are multiplexed, there is a single IRQ
+ */
+void dm9000_init(NICInfo *nd, target_phys_addr_t base_addr,
+ uint32_t addr_offset, uint32_t data_offset,
+ qemu_irq irq)
+{
+ dm9000_state *state;
+ int iomemtype;
+
+ state = (dm9000_state *)qemu_mallocz(sizeof(dm9000_state));
+ iomemtype = cpu_register_io_memory(0, dm9000_readfn,
+ dm9000_writefn, state);
+ cpu_register_physical_memory(base_addr, MAX(addr_offset, data_offset) + 4, iomemtype);
+ state->addr = base_addr + addr_offset;
+ state->data = base_addr + data_offset;
+ state->irq = irq;
+ memcpy(state->macaddr, nd->macaddr, 6);
+
+ dm9000_hard_reset(state);
+
+ state->vc = qemu_new_vlan_client(nd->vlan, dm9000_receive,
+ dm9000_can_receive, state);
+
+}
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [Qemu-devel] [PATCH] Davicom DM9000 emulation
2008-10-13 14:01 ` Daniel Silverstone
@ 2008-10-13 14:20 ` Daniel Silverstone
2008-10-13 14:34 ` Paul Brook
0 siblings, 1 reply; 9+ messages in thread
From: Daniel Silverstone @ 2008-10-13 14:20 UTC (permalink / raw)
To: qemu-devel
[-- Attachment #1: Type: text/plain, Size: 785 bytes --]
On Mon, 2008-10-13 at 15:01 +0100, Daniel Silverstone wrote:
> Attached is the fixed patch with these changes.
I have to apologise -- that patch lacked some semi-colons.
Also, it appears that some devices don't have the headers required for
target_phys_addr_t included before they include devices.h so I had to
use an #ifdef around the dm9000_init declaration. If this isn't right
then I need to know what the right thing to include in such drivers is.
It seems a bit unfortunate to force other drivers to include headers
they were otherwise managing without.
Attached is what I hope to be my final attempt at this.
Regards,
D.
--
Daniel Silverstone http://www.simtec.co.uk/
PGP mail accepted and encouraged. Key Id: 2BC8 4016 2068 7895
[-- Attachment #2: Davicom DM9000 Support Patch --]
[-- Type: text/x-patch, Size: 22044 bytes --]
The Davicom DM9000 10/100 Ethernet controller.
This driver supplies an MMIO based DM9000 interface which can be
instantiated in the emulation's memory map to provide an ethernet
interface for ARM "local-bus" type systems.
Signed-off-by: Daniel Silverstone <dsilvers@simtec.co.uk>
Signed-off-by: Vincent Sanders <vince@simtec.co.uk>
Makefile.target | 2
hw/devices.h | 4
hw/dm9000.c | 624 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 629 insertions(+), 1 deletion(-)
=== modified file 'Makefile.target'
--- Makefile.target 2008-10-13 03:14:31 +0000
+++ Makefile.target 2008-10-13 13:55:37 +0000
@@ -599,7 +599,7 @@
ifeq ($(TARGET_BASE_ARCH), arm)
OBJS+= integratorcp.o versatilepb.o ps2.o smc91c111.o arm_pic.o arm_timer.o
OBJS+= arm_boot.o pl011.o pl031.o pl050.o pl080.o pl110.o pl181.o pl190.o
-OBJS+= versatile_pci.o ptimer.o
+OBJS+= versatile_pci.o ptimer.o dm9000.o
OBJS+= realview_gic.o realview.o arm_sysctl.o mpcore.o
OBJS+= armv7m.o armv7m_nvic.o stellaris.o pl022.o stellaris_enet.o
OBJS+= pl061.o
=== modified file 'hw/devices.h'
--- hw/devices.h 2008-06-09 00:03:13 +0000
+++ hw/devices.h 2008-10-13 14:14:44 +0000
@@ -3,6 +3,13 @@
/* Devices that have nowhere better to go. */
+/* dm9000.c */
+#ifdef NEED_CPU_H
+/* This ifdef is present because target_phys_addr_t comes from cpu-defs.h */
+void dm9000_init(NICInfo *nd, target_phys_addr_t base_addr, uint32_t addr_offset,
+ uint32_t data_offset, qemu_irq irq);
+#endif
+
/* smc91c111.c */
void smc91c111_init(NICInfo *, uint32_t, qemu_irq);
=== added file 'hw/dm9000.c'
--- hw/dm9000.c 1970-01-01 00:00:00 +0000
+++ hw/dm9000.c 2008-10-13 14:08:33 +0000
@@ -0,0 +1,624 @@
+/* hw/dm9000.c
+ *
+ * DM9000 Ethernet interface
+ *
+ * Copyright 2006, 2008 Daniel Silverstone and Vincent Sanders
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 only.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <string.h>
+#include "qemu-common.h"
+#include "hw/irq.h"
+#include "net.h"
+
+/* Comment this out if you don't want register debug on stderr */
+/* #define DM9000_DEBUG */
+
+#ifdef DM9000_DEBUG
+#define DM9000_DBF(X...) do { fprintf(stderr, X); } while(0)
+#else
+#define DM9000_DBF(X...) do { if (0) fprintf(stderr, X); } while (0)
+#endif
+
+#define DM9000_REG_NCR 0x00
+#define DM9000_REG_NSR 0x01
+#define DM9000_REG_TCR 0x02
+#define DM9000_REG_TSR1 0x03
+#define DM9000_REG_TSR2 0x04
+#define DM9000_REG_RCR 0x05
+#define DM9000_REG_RSR 0x06
+#define DM9000_REG_ROCR 0x07
+#define DM9000_REG_BPTR 0x08
+#define DM9000_REG_FCTR 0x09
+#define DM9000_REG_FCR 0x0A
+#define DM9000_REG_EPCR 0x0B
+#define DM9000_REG_EPAR 0x0C
+#define DM9000_REG_EPDRL 0x0D
+#define DM9000_REG_EPDRH 0x0E
+#define DM9000_REG_WCR 0x0F
+#define DM9000_REG_PAR0 0x10
+#define DM9000_REG_PAR1 0x11
+#define DM9000_REG_PAR2 0x12
+#define DM9000_REG_PAR3 0x13
+#define DM9000_REG_PAR4 0x14
+#define DM9000_REG_PAR5 0x15
+#define DM9000_REG_MAR0 0x16
+#define DM9000_REG_MAR1 0x17
+#define DM9000_REG_MAR2 0x18
+#define DM9000_REG_MAR3 0x19
+#define DM9000_REG_MAR4 0x1A
+#define DM9000_REG_MAR5 0x1B
+#define DM9000_REG_MAR6 0x1C
+#define DM9000_REG_MAR7 0x1D
+#define DM9000_REG_GPCR 0x1E
+#define DM9000_REG_GPR 0x1F
+#define DM9000_REG_TRPAL 0x22
+#define DM9000_REG_TRPAH 0x23
+#define DM9000_REG_RWPAL 0x24
+#define DM9000_REG_RWPAH 0x25
+#define DM9000_REG_VIDL 0x28
+#define DM9000_REG_VIDH 0x29
+#define DM9000_REG_PIDL 0x2A
+#define DM9000_REG_PIDH 0x2B
+#define DM9000_REG_CHIPR 0x2C
+#define DM9000_REG_SMCR 0x2F
+#define DM9000_REG_MRCMDX 0xF0
+#define DM9000_REG_MRCMD 0xF2
+#define DM9000_REG_MRRL 0xF4
+#define DM9000_REG_MRRH 0xF5
+#define DM9000_REG_MWCMDX 0xF6
+#define DM9000_REG_MWCMD 0xF8
+#define DM9000_REG_MWRL 0xFA
+#define DM9000_REG_MWRH 0xFB
+#define DM9000_REG_TXPLL 0xFC
+#define DM9000_REG_TXPLH 0xFD
+#define DM9000_REG_ISR 0xFE
+#define DM9000_REG_IMR 0xFF
+
+#define DM9000_NCR_RESET 0x01
+#define DM9000_NSR_TX1END 0x04
+#define DM9000_NSR_TX2END 0x08
+#define DM9000_TCR_TXREQ 0x01
+
+#define DM9000_IMR_AUTOWRAP 0x80
+
+#define DM9000_MII_READ 0x0C
+#define DM9000_MII_WRITE 0x0A
+
+#define DM9000_MII_REG_BMCR 0x00
+#define DM9000_MII_REG_STATUS 0x01
+#define DM9000_MII_REG_PHYID1 0x02
+#define DM9000_MII_REG_PHYID2 0x03
+#define DM9000_MII_REG_ANAR 0x04
+#define DM9000_MII_REG_ANLPAR 0x05
+#define DM9000_MII_REG_ANER 0x06
+#define DM9000_MII_REG_DSCR 0x10
+#define DM9000_MII_REG_DSCSR 0x11
+#define DM9000_MII_REG_10BTCSR 0x12
+
+
+typedef struct {
+ uint32_t addr; /* address port */
+ uint32_t data; /* data port */
+ VLANClientState *vc;
+ qemu_irq irq;
+ uint8_t macaddr[6];
+ uint8_t multihash[8]; /* multicast hash table */
+ uint8_t address; /* The internal magial address */
+ uint8_t packet_buffer[16 * 1024];
+ uint16_t dm9k_mrr, dm9k_mwr; /* Read and write address registers */
+ uint16_t dm9k_txpl; /* TX packet length */
+ uint16_t dm9k_trpa, dm9k_rwpa; /* TX Read ptr address, RX write ptr address */
+ uint8_t dm9k_imr, dm9k_isr; /* Interrupt mask register and status register*/
+ uint8_t dm9k_ncr, dm9k_nsr; /* Network control register, network status register */
+ uint8_t dm9k_wcr; /* Wakeup control */
+ uint8_t dm9k_tcr; /* Transmission control register */
+ uint8_t packet_copy_buffer[3 * 1024]; /* packet copy buffer */
+ unsigned int packet_index:1; /* 0 == packet I, 1 == packet II */
+
+ /* Internal MII PHY state */
+ uint8_t dm9k_epcr; /* EEPROM/PHY control register */
+ uint8_t dm9k_epar; /* EEPROM/PHY address register */
+ uint16_t dm9k_epdr; /* EEPROM/PHY data register */
+ /* MII Regs */
+ uint16_t dm9k_mii_bmcr;
+ uint16_t dm9k_mii_anar;
+ uint16_t dm9k_mii_dscr;
+
+} dm9000_state;
+
+static void dm9000_raise_irq(dm9000_state *state)
+{
+ int level = ((state->dm9k_isr & state->dm9k_imr) & 0x03) != 0;
+ DM9000_DBF("DM9000: Set IRQ level %d\n", level);
+ qemu_set_irq(state->irq, level);
+}
+
+static void dm9000_soft_reset_mii(dm9000_state *state)
+{
+ state->dm9k_mii_bmcr = 0x3100; /* 100Mbps, AUTONEG, FULL DUPLEX */
+ state->dm9k_mii_anar = 0x01E1;
+ state->dm9k_mii_dscr = 0x0410;
+}
+
+static void dm9000_soft_reset(dm9000_state *state)
+{
+ DM9000_DBF("DM9000: Soft Reset\n");
+ state->dm9k_mrr = state->dm9k_mwr = state->dm9k_txpl = state->dm9k_trpa = 0x0000;
+ state->dm9k_rwpa = 0x0C04;
+ state->dm9k_imr = 0;
+ state->dm9k_isr = 0; /* 16 bit mode, no interrupts asserted */
+ state->dm9k_tcr = 0;
+ state->packet_index = 0;
+ memset(state->packet_buffer, 0, 16*1024);
+ memset(state->packet_copy_buffer, 0, 3*1024);
+ /* These registers have some bits "unaffected by software reset" */
+ /* Clear the reset bits */
+ state->dm9k_ncr &= 0xA0;
+ state->dm9k_nsr &= 0xD0;
+ /* Claim full duplex */
+ state->dm9k_ncr |= 1<<3;
+ /* Set link status to 1 */
+ state->dm9k_nsr |= 1<<6;
+ /* dm9k_wcr is unaffected or reserved, never reset */
+ /* MII control regs */
+ state->dm9k_epcr = 0x00;
+ state->dm9k_epar = 0x40;
+ /* reset the MII */
+ dm9000_soft_reset_mii(state);
+ dm9000_raise_irq(state); /* Clear any potentially pending IRQ */
+}
+
+static void dm9000_hard_reset(dm9000_state *state)
+{
+ state->dm9k_ncr = 0x00;
+ state->dm9k_nsr = 0x00;
+ state->dm9k_wcr = 0x00;
+ dm9000_soft_reset(state);
+}
+
+static void dm9000_do_transmit(dm9000_state *state)
+{
+ uint16_t idx, cnt, tptr;
+ idx = state->dm9k_trpa;
+ cnt = state->dm9k_txpl;
+ tptr = 0;
+ if( cnt > 3*1024 ) cnt = 3*1024; /* HARD CAP AT 3KiB */
+ DM9000_DBF("TX_Packet: %d bytes from %04x\n", cnt, idx);
+ while(cnt--) {
+ state->packet_copy_buffer[tptr++] = state->packet_buffer[idx++];
+ if( idx == 0x0C00 ) idx = 0;
+ }
+ /* DM9KNOTE: Assumes 16bit wiring */
+ idx = (idx+1) & ~1; /* Round up to nearest 16bit boundary */
+ if( idx == 0x0C00 ) idx = 0;
+ state->dm9k_trpa = idx;
+ /* We have the copy buffer, now we do the transmit */
+ qemu_send_packet(state->vc, state->packet_copy_buffer, state->dm9k_txpl);
+ /* Clear the "please xmit" bit */
+ state->dm9k_tcr &= ~DM9000_TCR_TXREQ;
+ /* Set the TXEND bit */
+ state->dm9k_nsr |= 1<<(2+state->packet_index);
+ DM9000_DBF("TX: NSR=%02x PI=%d\n", state->dm9k_nsr, state->packet_index);
+ /* Claim a TX complete IRQ */
+ state->dm9k_isr |= 0x02; /* Packet transmitted latch */
+ /* And flip the next-packet bit */
+ state->packet_index = !state->packet_index;
+ dm9000_raise_irq(state);
+}
+
+static void dm9000_mii_read(dm9000_state *state)
+{
+ int mii_reg = (state->dm9k_epar) & 0x3f;
+ uint16_t ret = 0;
+ switch(mii_reg) {
+ case DM9000_MII_REG_BMCR:
+ ret = state->dm9k_mii_bmcr;
+ break;
+ case DM9000_MII_REG_STATUS:
+ ret = 0x782D; /* No 100/T4, Can 100/FD, Can 100/HD, Can 10/FD, Can 10/HD,
+ * No Preamble suppression, Autoneg complete, No remote fault,
+ * Can autoneg, link up, no jabber, extended capability */
+ break;
+ case DM9000_MII_REG_PHYID1:
+ ret = 0x0181;
+ break;
+ case DM9000_MII_REG_PHYID2:
+ ret = 0xB8C0;
+ break;
+ case DM9000_MII_REG_ANAR:
+ ret = state->dm9k_mii_anar;
+ break;
+ case DM9000_MII_REG_ANLPAR:
+ ret = 0x0400;
+ break;
+ case DM9000_MII_REG_ANER:
+ ret = 0x0001;
+ break;
+ case DM9000_MII_REG_DSCR:
+ ret = state->dm9k_mii_dscr;
+ break;
+ case DM9000_MII_REG_DSCSR:
+ ret = 0xF008;
+ break;
+ case DM9000_MII_REG_10BTCSR:
+ ret = 0x7800;
+ }
+ state->dm9k_epdr = ret;
+ DM9000_DBF("DM9000:MIIPHY: Read of MII reg %d gives %04x\n", mii_reg, state->dm9k_epdr);
+}
+
+static void dm9000_mii_write(dm9000_state *state)
+{
+ int mii_reg = (state->dm9k_epar) & 0x3f;
+ DM9000_DBF("DM9000:MIIPHY: Write of MII reg %d value %04x\n", mii_reg, state->dm9k_epdr);
+ switch(mii_reg) {
+ case DM9000_MII_REG_BMCR:
+ state->dm9k_mii_bmcr = (state->dm9k_epdr &~0x8000);
+ if( state->dm9k_epdr & 0x8000 ) dm9000_soft_reset_mii(state);
+ break;
+ case DM9000_MII_REG_ANAR:
+ state->dm9k_mii_anar = state->dm9k_epdr;
+ break;
+ case DM9000_MII_REG_DSCR:
+ state->dm9k_mii_dscr = state->dm9k_epdr & ~0x0008;
+ break;
+ }
+}
+
+static void dm9000_write(void *opaque, target_phys_addr_t address,
+ uint32_t value)
+{
+ dm9000_state *state = (dm9000_state *)opaque;
+#ifdef DM9000_DEBUG
+ int suppress_debug = 0;
+#endif
+
+ if (address == state->addr) {
+ if( (value != DM9000_REG_MRCMD) &&
+ (value != DM9000_REG_MWCMD) )
+ DM9000_DBF("DM9000: Address set to 0x%02x\n", value);
+ state->address = value;
+ return;
+ }
+
+ switch(state->address) {
+ case DM9000_REG_NCR:
+ state->dm9k_ncr = value & 0xDF;
+ if (state->dm9k_ncr & DM9000_NCR_RESET)
+ dm9000_soft_reset(state);
+ break;
+ case DM9000_REG_NSR:
+ state->dm9k_nsr &= ~(value & 0x2C);
+ break;
+ case DM9000_REG_TCR:
+ state->dm9k_tcr = value & 0xFF;
+ if( value & DM9000_TCR_TXREQ ) dm9000_do_transmit(state);
+ break;
+ case DM9000_REG_EPCR:
+ state->dm9k_epcr = value & 0xFF;
+ if( value & DM9000_MII_READ )
+ dm9000_mii_read(state);
+ else if( value & DM9000_MII_WRITE )
+ dm9000_mii_write(state);
+ break;
+ case DM9000_REG_EPAR:
+ state->dm9k_epar = value & 0xFF;
+ break;
+ case DM9000_REG_EPDRL:
+ state->dm9k_epdr &= 0xFF00;
+ state->dm9k_epdr |= value & 0xFF;
+ break;
+ case DM9000_REG_EPDRH:
+ state->dm9k_epdr &= 0xFF;
+ state->dm9k_epdr |= (value & 0xFF) << 8;
+ break;
+ case DM9000_REG_PAR0:
+ case DM9000_REG_PAR1:
+ case DM9000_REG_PAR2:
+ case DM9000_REG_PAR3:
+ case DM9000_REG_PAR4:
+ case DM9000_REG_PAR5:
+ state->macaddr[state->address - DM9000_REG_PAR0] = value & 0xFF;
+ break;
+ case DM9000_REG_MAR0:
+ case DM9000_REG_MAR1:
+ case DM9000_REG_MAR2:
+ case DM9000_REG_MAR3:
+ case DM9000_REG_MAR4:
+ case DM9000_REG_MAR5:
+ case DM9000_REG_MAR6:
+ case DM9000_REG_MAR7:
+ /* multicast hash setup */
+ state->multihash[state->address - DM9000_REG_MAR0] = value & 0xFF;
+ break;
+ case DM9000_REG_MRRL:
+ state->dm9k_mrr &= 0xFF00;
+ state->dm9k_mrr |= value & 0xFF;
+ break;
+ case DM9000_REG_MRRH:
+ state->dm9k_mrr &= 0xFF;
+ state->dm9k_mrr |= (value & 0xFF) << 8;
+ break;
+ case DM9000_REG_MWCMDX:
+ case DM9000_REG_MWCMD:
+ /* DM9KNOTE: This assumes a 16bit wide wiring */
+ state->packet_buffer[state->dm9k_mwr] = value & 0xFF;
+ state->packet_buffer[state->dm9k_mwr+1] = (value >> 8) & 0xFF;
+ if( state->address == DM9000_REG_MWCMD ) {
+ state->dm9k_mwr += 2;
+ if( state->dm9k_imr & DM9000_IMR_AUTOWRAP )
+ if( state->dm9k_mwr >= 0x0C00 )
+ state->dm9k_mwr -= 0x0C00;
+ }
+#ifdef DM9000_DEBUG
+ suppress_debug = 1;
+#endif
+ break;
+ case DM9000_REG_MWRL:
+ state->dm9k_mwr &= 0xFF00;
+ state->dm9k_mwr |= value & 0xFF;
+ break;
+ case DM9000_REG_MWRH:
+ state->dm9k_mwr &= 0xFF;
+ state->dm9k_mwr |= (value & 0xFF) << 8;
+ break;
+ case DM9000_REG_TXPLL:
+ state->dm9k_txpl &= 0xFF00;
+ state->dm9k_txpl |= value & 0xFF;
+ break;
+ case DM9000_REG_TXPLH:
+ state->dm9k_txpl &= 0xFF;
+ state->dm9k_txpl |= (value & 0xFF) << 8;
+ break;
+ case DM9000_REG_ISR:
+ state->dm9k_isr &= ~(value & 0x0F);
+ dm9000_raise_irq(state);
+ break;
+ case DM9000_REG_IMR:
+ if( !(state->dm9k_imr & DM9000_IMR_AUTOWRAP) &&
+ (value & DM9000_IMR_AUTOWRAP) )
+ state->dm9k_mrr = 0x0C00 | (state->dm9k_mrr & 0xFF);
+ state->dm9k_imr = value & 0xFF;
+ dm9000_raise_irq(state);
+ break;
+ }
+#ifdef DM9000_DEBUG
+ if(!suppress_debug) DM9000_DBF("DM9000: Write value %04x\n", value);
+#endif
+}
+
+static uint32_t dm9000_read(void *opaque, target_phys_addr_t address)
+{
+ dm9000_state *state = (dm9000_state *)opaque;
+ uint32_t ret = 0;
+#ifdef DM9000_DEBUG
+ int suppress_debug = 0;
+#endif
+
+ if (address == state->addr)
+ return state->address;
+ switch(state->address) {
+ case DM9000_REG_NCR:
+ ret = state->dm9k_ncr;
+ break;
+ case DM9000_REG_NSR:
+ ret = state->dm9k_nsr;
+ /* Note, TX1END and TX2END are *CLEAR ON READ* */
+ state->dm9k_nsr &= ~(DM9000_NSR_TX1END | DM9000_NSR_TX2END);
+ break;
+ case DM9000_REG_TCR:
+ ret = state->dm9k_tcr;
+ break;
+ case DM9000_REG_TSR1:
+ case DM9000_REG_TSR2:
+ ret = 0x00; /* No error, yay! */
+ break;
+ case DM9000_REG_EPCR:
+ ret = state->dm9k_epcr;
+ break;
+ case DM9000_REG_EPAR:
+ ret = state->dm9k_epar;
+ break;
+ case DM9000_REG_EPDRL:
+ ret = state->dm9k_epdr & 0xFF;
+ break;
+ case DM9000_REG_EPDRH:
+ ret = (state->dm9k_epdr >> 8) & 0xFF;
+ break;
+ case DM9000_REG_PAR0:
+ case DM9000_REG_PAR1:
+ case DM9000_REG_PAR2:
+ case DM9000_REG_PAR3:
+ case DM9000_REG_PAR4:
+ case DM9000_REG_PAR5:
+ ret = state->macaddr[state->address - DM9000_REG_PAR0];
+ break;
+ case DM9000_REG_MAR0:
+ case DM9000_REG_MAR1:
+ case DM9000_REG_MAR2:
+ case DM9000_REG_MAR3:
+ case DM9000_REG_MAR4:
+ case DM9000_REG_MAR5:
+ case DM9000_REG_MAR6:
+ case DM9000_REG_MAR7:
+ /* multicast hash */
+ ret = state->multihash[state->address - DM9000_REG_MAR0];
+ break;
+ case DM9000_REG_TRPAL:
+ ret = state->dm9k_trpa & 0xFF;
+ break;
+ case DM9000_REG_TRPAH:
+ ret = state->dm9k_trpa >> 8;
+ break;
+ case DM9000_REG_RWPAL:
+ ret = state->dm9k_rwpa & 0xFF;
+ break;
+ case DM9000_REG_RWPAH:
+ ret = state->dm9k_rwpa >> 8;
+ break;
+ case DM9000_REG_VIDL:
+ ret = 0x46;
+ break;
+ case DM9000_REG_VIDH:
+ ret = 0x0A;
+ break;
+ case DM9000_REG_PIDL:
+ ret = 0x00;
+ break;
+ case DM9000_REG_PIDH:
+ ret = 0x90;
+ break;
+ case DM9000_REG_CHIPR:
+ ret = 0x00;
+ break;
+ case DM9000_REG_MRCMDX:
+ case DM9000_REG_MRCMD:
+ /* DM9KNOTE: This assumes a 16bit wide wiring */
+ ret = state->packet_buffer[state->dm9k_mrr];
+ ret |= state->packet_buffer[state->dm9k_mrr+1] << 8;
+ if( state->address == DM9000_REG_MRCMD ) {
+ state->dm9k_mrr += 2;
+ if( state->dm9k_mrr >= (16*1024) ) state->dm9k_mrr -= (16*1024);
+ if( state->dm9k_imr & DM9000_IMR_AUTOWRAP )
+ if( state->dm9k_mrr < 0x0C00 )
+ state->dm9k_mrr += 0x0C00;
+ }
+#ifdef DM9000_DEBUG
+ if (state->address==DM9000_REG_MRCMD)
+ suppress_debug = 1;
+#endif
+ break;
+ case DM9000_REG_MRRL:
+ ret = state->dm9k_mrr & 0xFF;
+ break;
+ case DM9000_REG_MRRH:
+ ret = state->dm9k_mrr >> 8;
+ break;
+ case DM9000_REG_MWRL:
+ ret = state->dm9k_mwr & 0xFF;
+ break;
+ case DM9000_REG_MWRH:
+ ret = state->dm9k_mwr >> 8;
+ break;
+ case DM9000_REG_TXPLL:
+ ret = state->dm9k_txpl & 0xFF;
+ break;
+ case DM9000_REG_TXPLH:
+ ret = state->dm9k_txpl >> 8;
+ break;
+ case DM9000_REG_ISR:
+ ret = state->dm9k_isr;
+ break;
+ case DM9000_REG_IMR:
+ ret = state->dm9k_imr;
+ break;
+ default:
+ ret = 0;
+ }
+
+#ifdef DM9000_DEBUG
+ if(!suppress_debug) DM9000_DBF("DM9000: Read gives: %04x\n", ret);
+#endif
+ return ret;
+}
+
+
+
+static int dm9000_can_receive(void *opaque)
+{
+ dm9000_state *state = (dm9000_state *)opaque;
+ uint16_t rx_space;
+ if( state->dm9k_rwpa < state->dm9k_mrr )
+ rx_space = state->dm9k_mrr - state->dm9k_rwpa;
+ else
+ rx_space = (13*1024) - (state->dm9k_rwpa - state->dm9k_mrr);
+ DM9000_DBF("DM9000:RX_Packet: Asked about RX, rwpa=%d mrr=%d => space is %d bytes\n",
+ state->dm9k_rwpa, state->dm9k_mrr, rx_space);
+ if (rx_space > 2048) return 1;
+ return 0;
+}
+
+static void dm9000_receive(void *opaque, const uint8_t *buf, int size)
+{
+ dm9000_state *state = (dm9000_state *)opaque;
+ uint16_t rxptr = state->dm9k_rwpa;
+ uint8_t magic_padding = 4;
+ if( size > 2048 ) return; /* La La La, I can't hear you */
+ /* Fill out the magical header structure */
+ DM9000_DBF("DM9000:RX_Packet: %d bytes into buffer at %04x\n", size, rxptr);
+ if( size < 64 ) magic_padding += (64 - size);
+ DM9000_DBF("DM9000:RX_Packet: Magical padding is %d bytes\n", magic_padding);
+ size += magic_padding; /* The magical CRC word */
+ state->packet_buffer[state->dm9k_rwpa-4] = 0x01; /* Packet read */
+ state->packet_buffer[state->dm9k_rwpa-3] = 0x00; /* Status OK */
+ state->packet_buffer[state->dm9k_rwpa-2] = size & 0xFF; /* Size LOW */
+ state->packet_buffer[state->dm9k_rwpa-1] = (size & 0xFF00)>>8; /* Size HIGH */
+ size += 4; /* The magical next header (which we zero for fun) */
+ while(size--) {
+ if( size > (magic_padding + 3) )
+ state->packet_buffer[rxptr++] = *buf++;
+ else
+ state->packet_buffer[rxptr++] = 0x00; /* Clear to the next header */
+ /* DM9KNOTE: Assumes 16 bit wired config */
+ if (size == 4) rxptr = (rxptr+1) & ~1; /* At end of packet, realign */
+ if( rxptr >= (16*1024) ) rxptr -= (16*1024);
+ if( rxptr < 0x0C00 ) rxptr += 0x0C00;
+ }
+ state->dm9k_rwpa = rxptr;
+ state->dm9k_isr |= 0x01; /* RX interrupt, yay */
+ dm9000_raise_irq(state);
+}
+
+
+static CPUReadMemoryFunc *dm9000_readfn[] = {
+ dm9000_read,
+ dm9000_read,
+ dm9000_read
+};
+
+static CPUWriteMemoryFunc *dm9000_writefn[] = {
+ dm9000_write,
+ dm9000_write,
+ dm9000_write
+};
+
+/* initialises a dm9000 ethernet controller
+ * The dm9k has a single 16bit wide address and data port through which all
+ * operations are multiplexed, there is a single IRQ
+ */
+void dm9000_init(NICInfo *nd, target_phys_addr_t base_addr,
+ uint32_t addr_offset, uint32_t data_offset,
+ qemu_irq irq)
+{
+ dm9000_state *state;
+ int iomemtype;
+
+ state = (dm9000_state *)qemu_mallocz(sizeof(dm9000_state));
+ iomemtype = cpu_register_io_memory(0, dm9000_readfn,
+ dm9000_writefn, state);
+ cpu_register_physical_memory(base_addr, MAX(addr_offset, data_offset) + 4, iomemtype);
+ state->addr = base_addr + addr_offset;
+ state->data = base_addr + data_offset;
+ state->irq = irq;
+ memcpy(state->macaddr, nd->macaddr, 6);
+
+ dm9000_hard_reset(state);
+
+ state->vc = qemu_new_vlan_client(nd->vlan, dm9000_receive,
+ dm9000_can_receive, state);
+
+}
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [Qemu-devel] [PATCH] Davicom DM9000 emulation
2008-10-13 14:20 ` Daniel Silverstone
@ 2008-10-13 14:34 ` Paul Brook
2008-10-13 14:37 ` Daniel Silverstone
0 siblings, 1 reply; 9+ messages in thread
From: Paul Brook @ 2008-10-13 14:34 UTC (permalink / raw)
To: qemu-devel, dsilvers
On Monday 13 October 2008, Daniel Silverstone wrote:
> On Mon, 2008-10-13 at 15:01 +0100, Daniel Silverstone wrote:
> > Attached is the fixed patch with these changes.
>
> I have to apologise -- that patch lacked some semi-colons.
>
> Also, it appears that some devices don't have the headers required for
> target_phys_addr_t included before they include devices.h so I had to
> use an #ifdef around the dm9000_init declaration. If this isn't right
> then I need to know what the right thing to include in such drivers is.
> It seems a bit unfortunate to force other drivers to include headers
> they were otherwise managing without.
Using target_phys_addr_t is a lie. Your device only implements 32 bits of
address space.
Paul
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [Qemu-devel] [PATCH] Davicom DM9000 emulation
2008-10-13 14:34 ` Paul Brook
@ 2008-10-13 14:37 ` Daniel Silverstone
2008-10-13 14:53 ` Paul Brook
0 siblings, 1 reply; 9+ messages in thread
From: Daniel Silverstone @ 2008-10-13 14:37 UTC (permalink / raw)
To: qemu-devel
On Mon, 2008-10-13 at 15:34 +0100, Paul Brook wrote:
> > Also, it appears that some devices don't have the headers required for
> > target_phys_addr_t included before they include devices.h so I had to
> > use an #ifdef around the dm9000_init declaration. If this isn't right
> > then I need to know what the right thing to include in such drivers is.
> > It seems a bit unfortunate to force other drivers to include headers
> > they were otherwise managing without.
> Using target_phys_addr_t is a lie. Your device only implements 32 bits of
> address space.
The implementation only allows for the two registers to be up to a
32-bit address space apart, but it allows for the base of the device to
be at any part of the target's physical address space. It's not
unreasonable IMO. If you think it is, then I can reduce it to uint32_t
but that loses semantic information regarding the base address (that it
is physical).
Regards,
Daniel.
--
Daniel Silverstone http://www.simtec.co.uk/
PGP mail accepted and encouraged. Key Id: 2BC8 4016 2068 7895
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [Qemu-devel] [PATCH] Davicom DM9000 emulation
2008-10-13 14:37 ` Daniel Silverstone
@ 2008-10-13 14:53 ` Paul Brook
2008-10-13 15:13 ` Daniel Silverstone
0 siblings, 1 reply; 9+ messages in thread
From: Paul Brook @ 2008-10-13 14:53 UTC (permalink / raw)
To: qemu-devel, dsilvers
On Monday 13 October 2008, Daniel Silverstone wrote:
> On Mon, 2008-10-13 at 15:34 +0100, Paul Brook wrote:
> > > Also, it appears that some devices don't have the headers required for
> > > target_phys_addr_t included before they include devices.h so I had to
> > > use an #ifdef around the dm9000_init declaration. If this isn't right
> > > then I need to know what the right thing to include in such drivers is.
> > > It seems a bit unfortunate to force other drivers to include headers
> > > they were otherwise managing without.
> >
> > Using target_phys_addr_t is a lie. Your device only implements 32 bits of
> > address space.
>
> The implementation only allows for the two registers to be up to a
> 32-bit address space apart, but it allows for the base of the device to
> be at any part of the target's physical address space.
No it doesn't.
+typedef struct {
+ uint32_t addr; /* address port */
[...]
+ state->addr = base_addr + addr_offset;
Will truncate the address to 32 bits.
On closer inspection I notice that the data member of the state structure is
never used, and the device will respond to any accesses within the range you
reserve, not just the data address.
Paul
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [Qemu-devel] [PATCH] Davicom DM9000 emulation
2008-10-13 14:53 ` Paul Brook
@ 2008-10-13 15:13 ` Daniel Silverstone
2008-10-21 8:43 ` Daniel Silverstone
0 siblings, 1 reply; 9+ messages in thread
From: Daniel Silverstone @ 2008-10-13 15:13 UTC (permalink / raw)
To: qemu-devel
[-- Attachment #1: Type: text/plain, Size: 1782 bytes --]
On Mon, 2008-10-13 at 15:53 +0100, Paul Brook wrote:
> > The implementation only allows for the two registers to be up to a
> > 32-bit address space apart, but it allows for the base of the device to
> > be at any part of the target's physical address space.
> No it doesn't.
> +typedef struct {
> + uint32_t addr; /* address port */
> [...]
> + state->addr = base_addr + addr_offset;
> Will truncate the address to 32 bits.
Argh, missed that bit when I changed it to target_phys_addr_t -- well
spotted. I've fixed up addr and data to be in target_phys_addr_t which
also appeals to my semantic sensibilities.
> On closer inspection I notice that the data member of the state structure is
> never used,
It was intended for debug, I think I lost the debug at one point. I've
reinstantated the debug which uses it. In theory I could predicate the
presence in the struct on the debug macro if you feel it's worthwhile.
> and the device will respond to any accesses within the range you
> reserve, not just the data address.
This was intentional based on the physical implementation of the device.
The chip has a CMD/nDATA pin, which is typically attached to some
address decode logic which results in CMD being high when the address
port is selected, and low when it is not. Then access to the chip is
predicated on the standard nCS type line.
Attached is a patch which correct the struct entries to be
target_phys_addr_t and reinstates the debug on accesses to anywhere
other than the address and data ports.
Thank you for taking the time to go through these patches. I am working
on the others.
Regards,
Daniel.
--
Daniel Silverstone http://www.simtec.co.uk/
PGP mail accepted and encouraged. Key Id: 2BC8 4016 2068 7895
[-- Attachment #2: Davicom DM9000 Support Patch --]
[-- Type: text/x-patch, Size: 22403 bytes --]
The Davicom DM9000 10/100 Ethernet controller.
This driver supplies an MMIO based DM9000 interface which can be
instantiated in the emulation's memory map to provide an ethernet
interface for ARM "local-bus" type systems.
Signed-off-by: Daniel Silverstone <dsilvers@simtec.co.uk>
Signed-off-by: Vincent Sanders <vince@simtec.co.uk>
Makefile.target | 2
hw/devices.h | 7
hw/dm9000.c | 631 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 639 insertions(+), 1 deletion(-)
=== modified file 'Makefile.target'
--- Makefile.target 2008-10-13 03:14:31 +0000
+++ Makefile.target 2008-10-13 14:59:29 +0000
@@ -599,7 +599,7 @@
ifeq ($(TARGET_BASE_ARCH), arm)
OBJS+= integratorcp.o versatilepb.o ps2.o smc91c111.o arm_pic.o arm_timer.o
OBJS+= arm_boot.o pl011.o pl031.o pl050.o pl080.o pl110.o pl181.o pl190.o
-OBJS+= versatile_pci.o ptimer.o
+OBJS+= versatile_pci.o ptimer.o dm9000.o
OBJS+= realview_gic.o realview.o arm_sysctl.o mpcore.o
OBJS+= armv7m.o armv7m_nvic.o stellaris.o pl022.o stellaris_enet.o
OBJS+= pl061.o
=== modified file 'hw/devices.h'
--- hw/devices.h 2008-06-09 00:03:13 +0000
+++ hw/devices.h 2008-10-13 14:14:44 +0000
@@ -3,6 +3,13 @@
/* Devices that have nowhere better to go. */
+/* dm9000.c */
+#ifdef NEED_CPU_H
+/* This ifdef is present because target_phys_addr_t comes from cpu-defs.h */
+void dm9000_init(NICInfo *nd, target_phys_addr_t base_addr, uint32_t addr_offset,
+ uint32_t data_offset, qemu_irq irq);
+#endif
+
/* smc91c111.c */
void smc91c111_init(NICInfo *, uint32_t, qemu_irq);
=== added file 'hw/dm9000.c'
--- hw/dm9000.c 1970-01-01 00:00:00 +0000
+++ hw/dm9000.c 2008-10-13 15:05:45 +0000
@@ -0,0 +1,631 @@
+/* hw/dm9000.c
+ *
+ * DM9000 Ethernet interface
+ *
+ * Copyright 2006, 2008 Daniel Silverstone and Vincent Sanders
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 only.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <string.h>
+#include "qemu-common.h"
+#include "hw/irq.h"
+#include "net.h"
+
+/* Comment this out if you don't want register debug on stderr */
+/* #define DM9000_DEBUG */
+
+#ifdef DM9000_DEBUG
+#define DM9000_DBF(X...) do { fprintf(stderr, X); } while(0)
+#else
+#define DM9000_DBF(X...) do { if (0) fprintf(stderr, X); } while (0)
+#endif
+
+#define DM9000_REG_NCR 0x00
+#define DM9000_REG_NSR 0x01
+#define DM9000_REG_TCR 0x02
+#define DM9000_REG_TSR1 0x03
+#define DM9000_REG_TSR2 0x04
+#define DM9000_REG_RCR 0x05
+#define DM9000_REG_RSR 0x06
+#define DM9000_REG_ROCR 0x07
+#define DM9000_REG_BPTR 0x08
+#define DM9000_REG_FCTR 0x09
+#define DM9000_REG_FCR 0x0A
+#define DM9000_REG_EPCR 0x0B
+#define DM9000_REG_EPAR 0x0C
+#define DM9000_REG_EPDRL 0x0D
+#define DM9000_REG_EPDRH 0x0E
+#define DM9000_REG_WCR 0x0F
+#define DM9000_REG_PAR0 0x10
+#define DM9000_REG_PAR1 0x11
+#define DM9000_REG_PAR2 0x12
+#define DM9000_REG_PAR3 0x13
+#define DM9000_REG_PAR4 0x14
+#define DM9000_REG_PAR5 0x15
+#define DM9000_REG_MAR0 0x16
+#define DM9000_REG_MAR1 0x17
+#define DM9000_REG_MAR2 0x18
+#define DM9000_REG_MAR3 0x19
+#define DM9000_REG_MAR4 0x1A
+#define DM9000_REG_MAR5 0x1B
+#define DM9000_REG_MAR6 0x1C
+#define DM9000_REG_MAR7 0x1D
+#define DM9000_REG_GPCR 0x1E
+#define DM9000_REG_GPR 0x1F
+#define DM9000_REG_TRPAL 0x22
+#define DM9000_REG_TRPAH 0x23
+#define DM9000_REG_RWPAL 0x24
+#define DM9000_REG_RWPAH 0x25
+#define DM9000_REG_VIDL 0x28
+#define DM9000_REG_VIDH 0x29
+#define DM9000_REG_PIDL 0x2A
+#define DM9000_REG_PIDH 0x2B
+#define DM9000_REG_CHIPR 0x2C
+#define DM9000_REG_SMCR 0x2F
+#define DM9000_REG_MRCMDX 0xF0
+#define DM9000_REG_MRCMD 0xF2
+#define DM9000_REG_MRRL 0xF4
+#define DM9000_REG_MRRH 0xF5
+#define DM9000_REG_MWCMDX 0xF6
+#define DM9000_REG_MWCMD 0xF8
+#define DM9000_REG_MWRL 0xFA
+#define DM9000_REG_MWRH 0xFB
+#define DM9000_REG_TXPLL 0xFC
+#define DM9000_REG_TXPLH 0xFD
+#define DM9000_REG_ISR 0xFE
+#define DM9000_REG_IMR 0xFF
+
+#define DM9000_NCR_RESET 0x01
+#define DM9000_NSR_TX1END 0x04
+#define DM9000_NSR_TX2END 0x08
+#define DM9000_TCR_TXREQ 0x01
+
+#define DM9000_IMR_AUTOWRAP 0x80
+
+#define DM9000_MII_READ 0x0C
+#define DM9000_MII_WRITE 0x0A
+
+#define DM9000_MII_REG_BMCR 0x00
+#define DM9000_MII_REG_STATUS 0x01
+#define DM9000_MII_REG_PHYID1 0x02
+#define DM9000_MII_REG_PHYID2 0x03
+#define DM9000_MII_REG_ANAR 0x04
+#define DM9000_MII_REG_ANLPAR 0x05
+#define DM9000_MII_REG_ANER 0x06
+#define DM9000_MII_REG_DSCR 0x10
+#define DM9000_MII_REG_DSCSR 0x11
+#define DM9000_MII_REG_10BTCSR 0x12
+
+
+typedef struct {
+ target_phys_addr_t addr; /* address port */
+ target_phys_addr_t data; /* data port */
+ VLANClientState *vc;
+ qemu_irq irq;
+ uint8_t macaddr[6];
+ uint8_t multihash[8]; /* multicast hash table */
+ uint8_t address; /* The internal magial address */
+ uint8_t packet_buffer[16 * 1024];
+ uint16_t dm9k_mrr, dm9k_mwr; /* Read and write address registers */
+ uint16_t dm9k_txpl; /* TX packet length */
+ uint16_t dm9k_trpa, dm9k_rwpa; /* TX Read ptr address, RX write ptr address */
+ uint8_t dm9k_imr, dm9k_isr; /* Interrupt mask register and status register*/
+ uint8_t dm9k_ncr, dm9k_nsr; /* Network control register, network status register */
+ uint8_t dm9k_wcr; /* Wakeup control */
+ uint8_t dm9k_tcr; /* Transmission control register */
+ uint8_t packet_copy_buffer[3 * 1024]; /* packet copy buffer */
+ unsigned int packet_index:1; /* 0 == packet I, 1 == packet II */
+
+ /* Internal MII PHY state */
+ uint8_t dm9k_epcr; /* EEPROM/PHY control register */
+ uint8_t dm9k_epar; /* EEPROM/PHY address register */
+ uint16_t dm9k_epdr; /* EEPROM/PHY data register */
+ /* MII Regs */
+ uint16_t dm9k_mii_bmcr;
+ uint16_t dm9k_mii_anar;
+ uint16_t dm9k_mii_dscr;
+} dm9000_state;
+
+static void dm9000_raise_irq(dm9000_state *state)
+{
+ int level = ((state->dm9k_isr & state->dm9k_imr) & 0x03) != 0;
+ DM9000_DBF("DM9000: Set IRQ level %d\n", level);
+ qemu_set_irq(state->irq, level);
+}
+
+static void dm9000_soft_reset_mii(dm9000_state *state)
+{
+ state->dm9k_mii_bmcr = 0x3100; /* 100Mbps, AUTONEG, FULL DUPLEX */
+ state->dm9k_mii_anar = 0x01E1;
+ state->dm9k_mii_dscr = 0x0410;
+}
+
+static void dm9000_soft_reset(dm9000_state *state)
+{
+ DM9000_DBF("DM9000: Soft Reset\n");
+ state->dm9k_mrr = state->dm9k_mwr = state->dm9k_txpl = state->dm9k_trpa = 0x0000;
+ state->dm9k_rwpa = 0x0C04;
+ state->dm9k_imr = 0;
+ state->dm9k_isr = 0; /* 16 bit mode, no interrupts asserted */
+ state->dm9k_tcr = 0;
+ state->packet_index = 0;
+ memset(state->packet_buffer, 0, 16*1024);
+ memset(state->packet_copy_buffer, 0, 3*1024);
+ /* These registers have some bits "unaffected by software reset" */
+ /* Clear the reset bits */
+ state->dm9k_ncr &= 0xA0;
+ state->dm9k_nsr &= 0xD0;
+ /* Claim full duplex */
+ state->dm9k_ncr |= 1<<3;
+ /* Set link status to 1 */
+ state->dm9k_nsr |= 1<<6;
+ /* dm9k_wcr is unaffected or reserved, never reset */
+ /* MII control regs */
+ state->dm9k_epcr = 0x00;
+ state->dm9k_epar = 0x40;
+ /* reset the MII */
+ dm9000_soft_reset_mii(state);
+ dm9000_raise_irq(state); /* Clear any potentially pending IRQ */
+}
+
+static void dm9000_hard_reset(dm9000_state *state)
+{
+ state->dm9k_ncr = 0x00;
+ state->dm9k_nsr = 0x00;
+ state->dm9k_wcr = 0x00;
+ dm9000_soft_reset(state);
+}
+
+static void dm9000_do_transmit(dm9000_state *state)
+{
+ uint16_t idx, cnt, tptr;
+ idx = state->dm9k_trpa;
+ cnt = state->dm9k_txpl;
+ tptr = 0;
+ if( cnt > 3*1024 ) cnt = 3*1024; /* HARD CAP AT 3KiB */
+ DM9000_DBF("TX_Packet: %d bytes from %04x\n", cnt, idx);
+ while(cnt--) {
+ state->packet_copy_buffer[tptr++] = state->packet_buffer[idx++];
+ if( idx == 0x0C00 ) idx = 0;
+ }
+ /* DM9KNOTE: Assumes 16bit wiring */
+ idx = (idx+1) & ~1; /* Round up to nearest 16bit boundary */
+ if( idx == 0x0C00 ) idx = 0;
+ state->dm9k_trpa = idx;
+ /* We have the copy buffer, now we do the transmit */
+ qemu_send_packet(state->vc, state->packet_copy_buffer, state->dm9k_txpl);
+ /* Clear the "please xmit" bit */
+ state->dm9k_tcr &= ~DM9000_TCR_TXREQ;
+ /* Set the TXEND bit */
+ state->dm9k_nsr |= 1<<(2+state->packet_index);
+ DM9000_DBF("TX: NSR=%02x PI=%d\n", state->dm9k_nsr, state->packet_index);
+ /* Claim a TX complete IRQ */
+ state->dm9k_isr |= 0x02; /* Packet transmitted latch */
+ /* And flip the next-packet bit */
+ state->packet_index = !state->packet_index;
+ dm9000_raise_irq(state);
+}
+
+static void dm9000_mii_read(dm9000_state *state)
+{
+ int mii_reg = (state->dm9k_epar) & 0x3f;
+ uint16_t ret = 0;
+ switch(mii_reg) {
+ case DM9000_MII_REG_BMCR:
+ ret = state->dm9k_mii_bmcr;
+ break;
+ case DM9000_MII_REG_STATUS:
+ ret = 0x782D; /* No 100/T4, Can 100/FD, Can 100/HD, Can 10/FD, Can 10/HD,
+ * No Preamble suppression, Autoneg complete, No remote fault,
+ * Can autoneg, link up, no jabber, extended capability */
+ break;
+ case DM9000_MII_REG_PHYID1:
+ ret = 0x0181;
+ break;
+ case DM9000_MII_REG_PHYID2:
+ ret = 0xB8C0;
+ break;
+ case DM9000_MII_REG_ANAR:
+ ret = state->dm9k_mii_anar;
+ break;
+ case DM9000_MII_REG_ANLPAR:
+ ret = 0x0400;
+ break;
+ case DM9000_MII_REG_ANER:
+ ret = 0x0001;
+ break;
+ case DM9000_MII_REG_DSCR:
+ ret = state->dm9k_mii_dscr;
+ break;
+ case DM9000_MII_REG_DSCSR:
+ ret = 0xF008;
+ break;
+ case DM9000_MII_REG_10BTCSR:
+ ret = 0x7800;
+ }
+ state->dm9k_epdr = ret;
+ DM9000_DBF("DM9000:MIIPHY: Read of MII reg %d gives %04x\n", mii_reg, state->dm9k_epdr);
+}
+
+static void dm9000_mii_write(dm9000_state *state)
+{
+ int mii_reg = (state->dm9k_epar) & 0x3f;
+ DM9000_DBF("DM9000:MIIPHY: Write of MII reg %d value %04x\n", mii_reg, state->dm9k_epdr);
+ switch(mii_reg) {
+ case DM9000_MII_REG_BMCR:
+ state->dm9k_mii_bmcr = (state->dm9k_epdr &~0x8000);
+ if( state->dm9k_epdr & 0x8000 ) dm9000_soft_reset_mii(state);
+ break;
+ case DM9000_MII_REG_ANAR:
+ state->dm9k_mii_anar = state->dm9k_epdr;
+ break;
+ case DM9000_MII_REG_DSCR:
+ state->dm9k_mii_dscr = state->dm9k_epdr & ~0x0008;
+ break;
+ }
+}
+
+static void dm9000_write(void *opaque, target_phys_addr_t address,
+ uint32_t value)
+{
+ dm9000_state *state = (dm9000_state *)opaque;
+#ifdef DM9000_DEBUG
+ int suppress_debug = 0;
+#endif
+
+ if (address == state->addr) {
+ if( (value != DM9000_REG_MRCMD) &&
+ (value != DM9000_REG_MWCMD) )
+ DM9000_DBF("DM9000: Address set to 0x%02x\n", value);
+ state->address = value;
+ return;
+ }
+
+ if (address != state->data) {
+ DM9000_DBF("DM9000: Write to location which is neither data nor address port: " TARGET_FMT_plx "\n", address);
+ }
+
+ switch(state->address) {
+ case DM9000_REG_NCR:
+ state->dm9k_ncr = value & 0xDF;
+ if (state->dm9k_ncr & DM9000_NCR_RESET)
+ dm9000_soft_reset(state);
+ break;
+ case DM9000_REG_NSR:
+ state->dm9k_nsr &= ~(value & 0x2C);
+ break;
+ case DM9000_REG_TCR:
+ state->dm9k_tcr = value & 0xFF;
+ if( value & DM9000_TCR_TXREQ ) dm9000_do_transmit(state);
+ break;
+ case DM9000_REG_EPCR:
+ state->dm9k_epcr = value & 0xFF;
+ if( value & DM9000_MII_READ )
+ dm9000_mii_read(state);
+ else if( value & DM9000_MII_WRITE )
+ dm9000_mii_write(state);
+ break;
+ case DM9000_REG_EPAR:
+ state->dm9k_epar = value & 0xFF;
+ break;
+ case DM9000_REG_EPDRL:
+ state->dm9k_epdr &= 0xFF00;
+ state->dm9k_epdr |= value & 0xFF;
+ break;
+ case DM9000_REG_EPDRH:
+ state->dm9k_epdr &= 0xFF;
+ state->dm9k_epdr |= (value & 0xFF) << 8;
+ break;
+ case DM9000_REG_PAR0:
+ case DM9000_REG_PAR1:
+ case DM9000_REG_PAR2:
+ case DM9000_REG_PAR3:
+ case DM9000_REG_PAR4:
+ case DM9000_REG_PAR5:
+ state->macaddr[state->address - DM9000_REG_PAR0] = value & 0xFF;
+ break;
+ case DM9000_REG_MAR0:
+ case DM9000_REG_MAR1:
+ case DM9000_REG_MAR2:
+ case DM9000_REG_MAR3:
+ case DM9000_REG_MAR4:
+ case DM9000_REG_MAR5:
+ case DM9000_REG_MAR6:
+ case DM9000_REG_MAR7:
+ /* multicast hash setup */
+ state->multihash[state->address - DM9000_REG_MAR0] = value & 0xFF;
+ break;
+ case DM9000_REG_MRRL:
+ state->dm9k_mrr &= 0xFF00;
+ state->dm9k_mrr |= value & 0xFF;
+ break;
+ case DM9000_REG_MRRH:
+ state->dm9k_mrr &= 0xFF;
+ state->dm9k_mrr |= (value & 0xFF) << 8;
+ break;
+ case DM9000_REG_MWCMDX:
+ case DM9000_REG_MWCMD:
+ /* DM9KNOTE: This assumes a 16bit wide wiring */
+ state->packet_buffer[state->dm9k_mwr] = value & 0xFF;
+ state->packet_buffer[state->dm9k_mwr+1] = (value >> 8) & 0xFF;
+ if( state->address == DM9000_REG_MWCMD ) {
+ state->dm9k_mwr += 2;
+ if( state->dm9k_imr & DM9000_IMR_AUTOWRAP )
+ if( state->dm9k_mwr >= 0x0C00 )
+ state->dm9k_mwr -= 0x0C00;
+ }
+#ifdef DM9000_DEBUG
+ suppress_debug = 1;
+#endif
+ break;
+ case DM9000_REG_MWRL:
+ state->dm9k_mwr &= 0xFF00;
+ state->dm9k_mwr |= value & 0xFF;
+ break;
+ case DM9000_REG_MWRH:
+ state->dm9k_mwr &= 0xFF;
+ state->dm9k_mwr |= (value & 0xFF) << 8;
+ break;
+ case DM9000_REG_TXPLL:
+ state->dm9k_txpl &= 0xFF00;
+ state->dm9k_txpl |= value & 0xFF;
+ break;
+ case DM9000_REG_TXPLH:
+ state->dm9k_txpl &= 0xFF;
+ state->dm9k_txpl |= (value & 0xFF) << 8;
+ break;
+ case DM9000_REG_ISR:
+ state->dm9k_isr &= ~(value & 0x0F);
+ dm9000_raise_irq(state);
+ break;
+ case DM9000_REG_IMR:
+ if( !(state->dm9k_imr & DM9000_IMR_AUTOWRAP) &&
+ (value & DM9000_IMR_AUTOWRAP) )
+ state->dm9k_mrr = 0x0C00 | (state->dm9k_mrr & 0xFF);
+ state->dm9k_imr = value & 0xFF;
+ dm9000_raise_irq(state);
+ break;
+ }
+#ifdef DM9000_DEBUG
+ if(!suppress_debug) DM9000_DBF("DM9000: Write value %04x\n", value);
+#endif
+}
+
+static uint32_t dm9000_read(void *opaque, target_phys_addr_t address)
+{
+ dm9000_state *state = (dm9000_state *)opaque;
+ uint32_t ret = 0;
+#ifdef DM9000_DEBUG
+ int suppress_debug = 0;
+#endif
+
+ if (address == state->addr)
+ return state->address;
+
+ if (address != state->data) {
+ DM9000_DBF("DM9000: Read from location which is neither data nor address port: " TARGET_FMT_plx "\n", address);
+ }
+
+ switch(state->address) {
+ case DM9000_REG_NCR:
+ ret = state->dm9k_ncr;
+ break;
+ case DM9000_REG_NSR:
+ ret = state->dm9k_nsr;
+ /* Note, TX1END and TX2END are *CLEAR ON READ* */
+ state->dm9k_nsr &= ~(DM9000_NSR_TX1END | DM9000_NSR_TX2END);
+ break;
+ case DM9000_REG_TCR:
+ ret = state->dm9k_tcr;
+ break;
+ case DM9000_REG_TSR1:
+ case DM9000_REG_TSR2:
+ ret = 0x00; /* No error, yay! */
+ break;
+ case DM9000_REG_EPCR:
+ ret = state->dm9k_epcr;
+ break;
+ case DM9000_REG_EPAR:
+ ret = state->dm9k_epar;
+ break;
+ case DM9000_REG_EPDRL:
+ ret = state->dm9k_epdr & 0xFF;
+ break;
+ case DM9000_REG_EPDRH:
+ ret = (state->dm9k_epdr >> 8) & 0xFF;
+ break;
+ case DM9000_REG_PAR0:
+ case DM9000_REG_PAR1:
+ case DM9000_REG_PAR2:
+ case DM9000_REG_PAR3:
+ case DM9000_REG_PAR4:
+ case DM9000_REG_PAR5:
+ ret = state->macaddr[state->address - DM9000_REG_PAR0];
+ break;
+ case DM9000_REG_MAR0:
+ case DM9000_REG_MAR1:
+ case DM9000_REG_MAR2:
+ case DM9000_REG_MAR3:
+ case DM9000_REG_MAR4:
+ case DM9000_REG_MAR5:
+ case DM9000_REG_MAR6:
+ case DM9000_REG_MAR7:
+ /* multicast hash */
+ ret = state->multihash[state->address - DM9000_REG_MAR0];
+ break;
+ case DM9000_REG_TRPAL:
+ ret = state->dm9k_trpa & 0xFF;
+ break;
+ case DM9000_REG_TRPAH:
+ ret = state->dm9k_trpa >> 8;
+ break;
+ case DM9000_REG_RWPAL:
+ ret = state->dm9k_rwpa & 0xFF;
+ break;
+ case DM9000_REG_RWPAH:
+ ret = state->dm9k_rwpa >> 8;
+ break;
+ case DM9000_REG_VIDL:
+ ret = 0x46;
+ break;
+ case DM9000_REG_VIDH:
+ ret = 0x0A;
+ break;
+ case DM9000_REG_PIDL:
+ ret = 0x00;
+ break;
+ case DM9000_REG_PIDH:
+ ret = 0x90;
+ break;
+ case DM9000_REG_CHIPR:
+ ret = 0x00;
+ break;
+ case DM9000_REG_MRCMDX:
+ case DM9000_REG_MRCMD:
+ /* DM9KNOTE: This assumes a 16bit wide wiring */
+ ret = state->packet_buffer[state->dm9k_mrr];
+ ret |= state->packet_buffer[state->dm9k_mrr+1] << 8;
+ if( state->address == DM9000_REG_MRCMD ) {
+ state->dm9k_mrr += 2;
+ if( state->dm9k_mrr >= (16*1024) ) state->dm9k_mrr -= (16*1024);
+ if( state->dm9k_imr & DM9000_IMR_AUTOWRAP )
+ if( state->dm9k_mrr < 0x0C00 )
+ state->dm9k_mrr += 0x0C00;
+ }
+#ifdef DM9000_DEBUG
+ if (state->address==DM9000_REG_MRCMD)
+ suppress_debug = 1;
+#endif
+ break;
+ case DM9000_REG_MRRL:
+ ret = state->dm9k_mrr & 0xFF;
+ break;
+ case DM9000_REG_MRRH:
+ ret = state->dm9k_mrr >> 8;
+ break;
+ case DM9000_REG_MWRL:
+ ret = state->dm9k_mwr & 0xFF;
+ break;
+ case DM9000_REG_MWRH:
+ ret = state->dm9k_mwr >> 8;
+ break;
+ case DM9000_REG_TXPLL:
+ ret = state->dm9k_txpl & 0xFF;
+ break;
+ case DM9000_REG_TXPLH:
+ ret = state->dm9k_txpl >> 8;
+ break;
+ case DM9000_REG_ISR:
+ ret = state->dm9k_isr;
+ break;
+ case DM9000_REG_IMR:
+ ret = state->dm9k_imr;
+ break;
+ default:
+ ret = 0;
+ }
+
+#ifdef DM9000_DEBUG
+ if(!suppress_debug) DM9000_DBF("DM9000: Read gives: %04x\n", ret);
+#endif
+ return ret;
+}
+
+
+
+static int dm9000_can_receive(void *opaque)
+{
+ dm9000_state *state = (dm9000_state *)opaque;
+ uint16_t rx_space;
+ if( state->dm9k_rwpa < state->dm9k_mrr )
+ rx_space = state->dm9k_mrr - state->dm9k_rwpa;
+ else
+ rx_space = (13*1024) - (state->dm9k_rwpa - state->dm9k_mrr);
+ DM9000_DBF("DM9000:RX_Packet: Asked about RX, rwpa=%d mrr=%d => space is %d bytes\n",
+ state->dm9k_rwpa, state->dm9k_mrr, rx_space);
+ if (rx_space > 2048) return 1;
+ return 0;
+}
+
+static void dm9000_receive(void *opaque, const uint8_t *buf, int size)
+{
+ dm9000_state *state = (dm9000_state *)opaque;
+ uint16_t rxptr = state->dm9k_rwpa;
+ uint8_t magic_padding = 4;
+ if( size > 2048 ) return; /* La La La, I can't hear you */
+ /* Fill out the magical header structure */
+ DM9000_DBF("DM9000:RX_Packet: %d bytes into buffer at %04x\n", size, rxptr);
+ if( size < 64 ) magic_padding += (64 - size);
+ DM9000_DBF("DM9000:RX_Packet: Magical padding is %d bytes\n", magic_padding);
+ size += magic_padding; /* The magical CRC word */
+ state->packet_buffer[state->dm9k_rwpa-4] = 0x01; /* Packet read */
+ state->packet_buffer[state->dm9k_rwpa-3] = 0x00; /* Status OK */
+ state->packet_buffer[state->dm9k_rwpa-2] = size & 0xFF; /* Size LOW */
+ state->packet_buffer[state->dm9k_rwpa-1] = (size & 0xFF00)>>8; /* Size HIGH */
+ size += 4; /* The magical next header (which we zero for fun) */
+ while(size--) {
+ if( size > (magic_padding + 3) )
+ state->packet_buffer[rxptr++] = *buf++;
+ else
+ state->packet_buffer[rxptr++] = 0x00; /* Clear to the next header */
+ /* DM9KNOTE: Assumes 16 bit wired config */
+ if (size == 4) rxptr = (rxptr+1) & ~1; /* At end of packet, realign */
+ if( rxptr >= (16*1024) ) rxptr -= (16*1024);
+ if( rxptr < 0x0C00 ) rxptr += 0x0C00;
+ }
+ state->dm9k_rwpa = rxptr;
+ state->dm9k_isr |= 0x01; /* RX interrupt, yay */
+ dm9000_raise_irq(state);
+}
+
+
+static CPUReadMemoryFunc *dm9000_readfn[] = {
+ dm9000_read,
+ dm9000_read,
+ dm9000_read
+};
+
+static CPUWriteMemoryFunc *dm9000_writefn[] = {
+ dm9000_write,
+ dm9000_write,
+ dm9000_write
+};
+
+/* initialises a dm9000 ethernet controller
+ * The dm9k has a single 16bit wide address and data port through which all
+ * operations are multiplexed, there is a single IRQ
+ */
+void dm9000_init(NICInfo *nd, target_phys_addr_t base_addr,
+ uint32_t addr_offset, uint32_t data_offset,
+ qemu_irq irq)
+{
+ dm9000_state *state;
+ int iomemtype;
+
+ state = (dm9000_state *)qemu_mallocz(sizeof(dm9000_state));
+ iomemtype = cpu_register_io_memory(0, dm9000_readfn,
+ dm9000_writefn, state);
+ cpu_register_physical_memory(base_addr, MAX(addr_offset, data_offset) + 4, iomemtype);
+ state->addr = base_addr + addr_offset;
+ state->data = base_addr + data_offset;
+ state->irq = irq;
+ memcpy(state->macaddr, nd->macaddr, 6);
+
+ dm9000_hard_reset(state);
+
+ state->vc = qemu_new_vlan_client(nd->vlan, dm9000_receive,
+ dm9000_can_receive, state);
+}
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [Qemu-devel] [PATCH] Davicom DM9000 emulation
2008-10-13 15:13 ` Daniel Silverstone
@ 2008-10-21 8:43 ` Daniel Silverstone
0 siblings, 0 replies; 9+ messages in thread
From: Daniel Silverstone @ 2008-10-21 8:43 UTC (permalink / raw)
To: qemu-devel
On Mon, 2008-10-13 at 16:13 +0100, Daniel Silverstone wrote:
> Attached is a patch which correct the struct entries to be
> target_phys_addr_t and reinstates the debug on accesses to anywhere
> other than the address and data ports.
My list subscription appears to have faltered due to bouncing spam. Has
anyone had a chance to look at the patch attached to the previous
message? I really want to get one patch up-to-snuff, so that I can
rework the other patches to match style etc.
Thanks,
Daniel.
--
Daniel Silverstone http://www.simtec.co.uk/
PGP mail accepted and encouraged. Key Id: 2BC8 4016 2068 7895
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2008-10-21 8:43 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-10-13 10:12 [Qemu-devel] [PATCH] Davicom DM9000 emulation Daniel Silverstone
2008-10-13 12:47 ` Paul Brook
2008-10-13 14:01 ` Daniel Silverstone
2008-10-13 14:20 ` Daniel Silverstone
2008-10-13 14:34 ` Paul Brook
2008-10-13 14:37 ` Daniel Silverstone
2008-10-13 14:53 ` Paul Brook
2008-10-13 15:13 ` Daniel Silverstone
2008-10-21 8:43 ` Daniel Silverstone
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).