This is the mail archive of the ecos-patches@sourceware.org mailing list for the eCos project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

DM9000 ethernet driver updates


Hi,

Attached is a patch to update the Davicom DM9000 ethernet driver.

- Handle ETH_DRV_GET_MAC_ADDRESS and ETH_DRV_SET_MAC_ADDRESS.
- Longer delays after reading/writing eeprom (200 us isn't enough).
- Turn on PHY before writing PHY registers.  Wait for auto negotiation
to be complete.
- Fixes for devices with 16 bit (and I think 8 bit) wide data busses.

David Vrabel
-- 
David Vrabel, Design Engineer

Arcom, Clifton Road           Tel: +44 (0)1223 411200 ext. 3233
Cambridge CB1 7EA, UK         Web: http://www.arcom.com/
Index: ecos-working/packages/devs/eth/davicom/dm9000/current/cdl/davicom_dm9000_eth_drivers.cdl
===================================================================
--- ecos-working.orig/packages/devs/eth/davicom/dm9000/current/cdl/davicom_dm9000_eth_drivers.cdl	2005-10-25 16:35:51.000000000 +0100
+++ ecos-working/packages/devs/eth/davicom/dm9000/current/cdl/davicom_dm9000_eth_drivers.cdl	2005-10-25 16:36:31.000000000 +0100
@@ -64,6 +64,18 @@
 
     compile       -library=libextras.a if_dm9000.c
 
+    cdl_option CYGSEM_DEVS_ETH_DAVICOM_DM9000_WRITE_EEPROM {
+        display "SIOCSIFHWADDR records ESA (MAC address) in EEPROM"
+        default_value 0
+        description   "
+            The ioctl() socket call with operand SIOCSIFHWADDR sets the
+            interface hardware address - the MAC address or Ethernet Station
+            Address (ESA).  This option causes the new MAC address to be
+            written into the EEPROM associated with the interface, so that the
+            new MAC address is permanently recorded.  Doing this should be a
+            carefully chosen decision, hence this option."
+    }
+
     cdl_option CYGNUM_DEVS_ETH_DAVICOM_DM9000_DEV_COUNT {
 	display "Number of supported interfaces."
 	calculated    { CYGINT_DEVS_ETH_DAVICOM_DM9000_REQUIRED }
Index: ecos-working/packages/devs/eth/davicom/dm9000/current/src/if_dm9000.c
===================================================================
--- ecos-working.orig/packages/devs/eth/davicom/dm9000/current/src/if_dm9000.c	2005-10-25 16:35:51.000000000 +0100
+++ ecos-working/packages/devs/eth/davicom/dm9000/current/src/if_dm9000.c	2005-10-25 16:36:31.000000000 +0100
@@ -77,6 +77,9 @@
 
 #define DM9000_PKT_MAX 1536
 
+//#define DEBUG
+//#define DEBUG_DUMP
+
 //
 // Control and Status register offsets
 //
@@ -225,6 +228,17 @@
 #define IMR_PTM       (1 << 1)
 #define IMR_PRM       (1 << 0)
 
+/* PHY registers */
+#define PHY_BMCR      0x00
+#define PHY_BMSR      0x01
+#define PHY_ANAR      0x04
+
+/* PHY BMCR (Basic Mode Control Register) */
+#define PHY_BMCR_AUTO_NEG_EN    (1 << 12)
+#define PHY_BMCR_AUTO_NEG_START (1 << 12)
+
+/* PHY BMSR (Basic Mode Status Register) */
+#define PHY_BMSR_AUTO_NEG_COMPLETE (1 << 5)
 
 // Read one datum from 8-bit bus
 static int read_data_8(struct dm9000 *p, cyg_uint8 *dest)
@@ -306,7 +320,7 @@
     putreg(p, DM_EPCR, EPCR_ERPRR);
     while (getreg(p, DM_EPCR) & EPCR_ERRE)
 	;
-    CYGACC_CALL_IF_DELAY_US(200);
+    CYGACC_CALL_IF_DELAY_US(8000);
     putreg(p, DM_EPCR, 0);
     return getreg(p, DM_EPDRL) | (getreg(p, DM_EPDRH) << 8);
 }
@@ -320,7 +334,7 @@
     putreg(p, DM_EPCR, EPCR_WEP | EPCR_ERPRW);
     while (getreg(p, DM_EPCR) & EPCR_ERRE)
 	;
-    CYGACC_CALL_IF_DELAY_US(200);
+    CYGACC_CALL_IF_DELAY_US(8000);
     putreg(p, DM_EPCR, 0);
 }
 
@@ -330,7 +344,7 @@
     putreg(p, DM_EPCR, EPCR_REEP);
     while (getreg(p, DM_EPCR) & EPCR_ERRE)
 	;
-    CYGACC_CALL_IF_DELAY_US(200);
+    CYGACC_CALL_IF_DELAY_US(8000);
     putreg(p, DM_EPCR, 0);
 }
 
@@ -359,12 +373,21 @@
 
 static void init_phy(struct dm9000 *p)
 {
-    phy_write(p, 4, 0x1e1); // Advertise 10/100 half/full duplex w/CSMA
-    phy_write(p, 0, 0x1200); // enable autoneg
+    int t = 0;
+    cyg_uint16 r;
 
-    // release reset
-    putreg(p, DM_GPCR, 1);
-    putreg(p, DM_GPR, 0);  
+    /* power on PHY */
+    putreg(p, DM_GPCR, 0x01);
+    putreg(p, DM_GPR,  0x00);
+
+    phy_write(p, PHY_ANAR, 0x1e1); // Advertise 10/100 half/full duplex w/CSMA
+    phy_write(p, PHY_BMCR, PHY_BMCR_AUTO_NEG_EN | PHY_BMCR_AUTO_NEG_START);
+
+    /* wait for autonegotiation to complete */
+    do {
+        CYGACC_CALL_IF_DELAY_US(1000);
+        r = phy_read(p, PHY_BMSR);
+    } while (!(r & PHY_BMSR_AUTO_NEG_COMPLETE) && t++ < 2000);
 }
 
 
@@ -600,8 +623,7 @@
 	++sg;
     } while (nread < total_len);
 
-#if 0
-    // dump packet
+#ifdef DEBUG_DUMP
     for (sg = sg_list; sg < (sg_list + sg_len); sg++) {
 	diag_printf("\n");
 	diag_dump_buf(sg->buf, sg->len);
@@ -637,22 +659,29 @@
 	    int total_len, unsigned long key)
 {
     struct dm9000 *priv = (struct dm9000 *)sc->driver_private;
-    struct eth_drv_sg *sg = sg_list;
+    struct eth_drv_sg *sg;
     cyg_uint8 tmpbuf[4];
-    int i, len, extra, n, save_len;
+    int i, len, n, save_len, tail_extra;
     char *p;
 
-    if (0) {
-	diag_printf("dm9000_send: NCR[%02x] NSR[%02x] TPL[%02x]\n",
-		    getreg(priv, DM_NCR), getreg(priv, DM_NSR),
-		    getreg(priv, DM_TRPAL) | (getreg(priv, DM_TRPAH) << 8)
-	    );
+#ifdef DEBUG
+    diag_printf("dm9000_send: NCR[%02x] NSR[%02x] TRPA[%04x]\n",
+                getreg(priv, DM_NCR), getreg(priv, DM_NSR),
+                getreg(priv, DM_TRPAL) | (getreg(priv, DM_TRPAH) << 8)
+        );
+#endif
+#ifdef DEBUG_DUMP
+    for (sg = sg_list; sg < (sg_list + sg_len); sg++) {
+	diag_printf("\n");
+	diag_dump_buf(sg->buf, sg->len);
     }
+#endif
 
     priv->txbusy = 1;
 
+    sg = sg_list;
     save_len = total_len;
-    extra = 0;
+    tail_extra = 0;
 
     HAL_WRITE_UINT8(priv->io_addr, DM_MWCMD);
 
@@ -662,39 +691,41 @@
 	    len = total_len;
 	p = (char *)sg->buf;
 
-	if (extra) {
-	    n = sizeof(tmpbuf) - extra;
-	    memcpy(tmpbuf + extra, p, n);
-	    p += n;
-	    len -= n;
+        /* write any left over partial words by combining them with the start
+         * of this sg block */
+        if (tail_extra) {
+            int head_extra = sizeof(tmpbuf) - tail_extra;
+            memcpy(tmpbuf + tail_extra, p, head_extra);
+            p += head_extra;
+            len -= head_extra;
 	    for (i = 0; i < sizeof(tmpbuf) && total_len > 0; i += n) {
 		n = priv->write_data(priv, tmpbuf + i);
 		total_len -= n;
 	    }
-	    extra = 0;
-	}
-	
-	while (len >= sizeof(tmpbuf) && total_len > 0) {
-	    n = priv->write_data(priv, p);
-	    len -= n;
-	    total_len -= n;
-	    p += n;
-	}
+            tail_extra = 0;
+        }
 
-	if (len > 0 && total_len > 0) {
-	    extra = len;
-	    memcpy(tmpbuf, p, extra);
-
-	    if ((total_len - extra) <= 0) {
-		// go ahead and write it now
-		for (i = 0; total_len > 0; i += n, total_len -= n) {
-		    n = priv->write_data(priv, tmpbuf + i);
-		    total_len = 0;
-		}
-		break;
-	    }
-	}
-	sg++;
+        /* write out whole words */
+        while (len >= priv->buswidth) {
+	    n = priv->write_data(priv, p);
+            len -= n;
+            total_len -= n;
+            p += n;
+        }
+
+        /* if we have some left over partial words... */
+        if (len > 0) {
+            /* combine them with the next sg block if available */
+            if (total_len > len ) {
+                tail_extra = len;
+                memcpy(tmpbuf, p, tail_extra);
+            } else {
+                /* otherwise just write this last partial word */
+                n = priv->write_data(priv, p);
+                total_len -= n;
+            }
+        }
+        sg++;
     }
 
     priv->txkey = key;
@@ -703,8 +734,6 @@
     putreg(priv, DM_TXPLH, save_len >> 8);
 
     putreg(priv, DM_TCR, TCR_TXREQ);
-
-    return;
 }
 
 // ------------------------------------------------------------------------
@@ -716,7 +745,7 @@
 dm9000_poll(struct eth_drv_sc *sc)
 {
     struct dm9000 *priv = (struct dm9000 *)sc->driver_private;
-    cyg_uint8 status, rxstat, rx1;
+    cyg_uint8 status, rxstat;
     cyg_uint16 pkt_stat, pkt_len;
     int i;
 
@@ -729,29 +758,20 @@
 
     // check for rx done
     if (1 /*status & ISR_PRS*/) {
+        cyg_uint8 hdr[4]; /* 4 byte Rx pkt hdr */
 
-	rx1 = getreg(priv, DM_MRCMDX);
-	HAL_READ_UINT8(priv->io_data, rxstat);
+        getreg(priv, DM_MRCMDX); /* dummy read */
+
+        HAL_READ_UINT8(priv->io_data, rxstat);
 
 	// check for packet ready
 	if (rxstat == 1) {
-	    cyg_uint16 u16[2];
-	    cyg_uint8 *cp;
+            HAL_WRITE_UINT8(priv->io_addr, DM_MRCMD);
+            for (i = 0; i < 4;)
+                i += priv->read_data(priv, hdr + i);
 
-	    HAL_WRITE_UINT8(priv->io_addr, DM_MRCMD);
-	    for (i = 0, cp = (cyg_uint8 *)u16; i < 4; )
-		i += priv->read_data(priv, cp + i);
-
-	    u16[0] = CYG_LE16_TO_CPU(u16[0]);
-	    u16[1] = CYG_LE16_TO_CPU(u16[1]);
-
-#if (CYG_BYTEORDER == CYG_MSBFIRST)
-	    pkt_stat = u16[0];
-	    pkt_len = u16[1];
-#else
-	    pkt_stat = u16[1];
-	    pkt_len = u16[0];
-#endif
+            pkt_stat = hdr[0] | (hdr[1] << 8);
+            pkt_len  = hdr[2] | (hdr[3] << 8);
 
 #ifdef DEBUG
 	    diag_printf("pkt_stat=%04x pkt_len=%04x\n", pkt_stat, pkt_len);
@@ -761,7 +781,7 @@
 		diag_printf("packet too short: %d (0x%04x)\n", pkt_len, pkt_len);
 		i = 0;
 		while (i < pkt_len)
-		    i += priv->read_data(priv, cp);
+		    i += priv->read_data(priv, hdr);
 	    } else if (pkt_len > 1536) {
 		priv->reset_pending = 1;
 		diag_printf("packet too long: %d (0x%04x)\n", pkt_len, pkt_len);
@@ -769,7 +789,7 @@
 		diag_printf("bad packet status: 0x%04x\n", pkt_stat);
 		i = 0;
 		while (i < pkt_len)
-		    i += priv->read_data(priv, cp);
+		    i += priv->read_data(priv, hdr);
 	    } else {
 		// receive packet
 		priv->rxlen = pkt_len;
@@ -860,6 +880,32 @@
 dm9000_ioctl(struct eth_drv_sc *sc, unsigned long key,
 	  void *data, int data_length)
 {
+    struct dm9000 *priv = (struct dm9000 *)sc->driver_private;
+    cyg_uint8 *esa = (cyg_uint8 *)data;
+    int i;
+
+    switch (key) {
+#ifdef ETH_DRV_GET_MAC_ADDRESS
+    case ETH_DRV_GET_MAC_ADDRESS:
+        memcpy(esa, priv->mac_address, sizeof(priv->mac_address));
+        return 0;
+#endif
+#ifdef ETH_DRV_SET_MAC_ADDRESS
+    case ETH_DRV_SET_MAC_ADDRESS:
+        for (i = 0; i < sizeof(priv->mac_address);  i++) {
+            priv->mac_address[i] = esa[i];
+            putreg(priv, DM_PAR + i, priv->mac_address[i]);
+        }
+#if defined(CYGSEM_DEVS_ETH_DAVICOM_DM9000_WRITE_EEPROM)
+        for (i = 0; i < sizeof(priv->mac_address) / 2; i++)
+            eeprom_write(priv, i, priv->mac_address[2*i] | (priv->mac_address[2*i+1] << 8));
+#else
+        diag_printf("dm9000: eeprom write disabled\n");
+#endif
+        return 0;
+#endif
+    }
+
     return -1;
 }
 
Index: ecos-working/packages/devs/eth/davicom/dm9000/current/ChangeLog
===================================================================
--- ecos-working.orig/packages/devs/eth/davicom/dm9000/current/ChangeLog	2004-09-06 13:58:18.000000000 +0100
+++ ecos-working/packages/devs/eth/davicom/dm9000/current/ChangeLog	2005-10-25 16:48:56.000000000 +0100
@@ -1,3 +1,20 @@
+2005-10-25  David Vrabel <dvrabel@arcom.com>
+
+	* src/if_dm9000.c (eeprom_read, eeprom_write, eeprom_reload):
+	Delay more when writing/reading eeprom (200 us isn't enough).
+	(phy_init): Turn on PHY before writing PHY registers.  Wait for
+	auto negotiation to be complete.
+	(dm9000_send): Correctly write last words to Tx SRAM when using a
+	8/16 bit device.  Clarify and comment code.
+	(dm9000_poll): Parse Rx packet header correctly on 8/16 bit
+	devices.
+	(dm9000_ioctl): Handle ETH_DRV_GET_MAC_ADDRESS and
+	ETH_DRV_SET_MAC_ADDRESS.
+
+	* cdl/davicom_dm9000_eth_drivers.cdl: New option
+	CYGSEM_DEVS_ETH_DAVICOM_DM9000_WRITE_EEPROM to enable/disable
+	writing to EEPROM.
+
 2004-09-05  Mark Salter <msalter@redhat.com>
 
 	Initial Checkin of DM9000 Ethernet driver (RedBoot only for now).

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]