]> git.kernelconcepts.de Git - karo-tx-redboot.git/blobdiff - packages/devs/eth/davicom/dm9000/v2_0/src/if_dm9000.c
unified MX27, MX25, MX37 trees
[karo-tx-redboot.git] / packages / devs / eth / davicom / dm9000 / v2_0 / src / if_dm9000.c
index 79c9993291c4aa2c55808d9cb3d8be5ff4297bc6..f59b1572937ece83d5879636bfc5a8ca36e17736 100644 (file)
@@ -53,6 +53,7 @@
 #include <pkgconf/system.h>
 #include <pkgconf/io_eth_drivers.h>
 #include <pkgconf/devs_eth_davicom_dm9000.h>
+
 #include <cyg/infra/cyg_type.h>
 #include <cyg/infra/cyg_ass.h>
 #include <cyg/hal/hal_arch.h>
@@ -77,6 +78,9 @@
 
 #define DM9000_PKT_MAX 1536
 
+//#define DEBUG
+//#define DEBUG_DUMP
+
 //
 // Control and Status register offsets
 //
 #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 +321,7 @@ static cyg_uint16 eeprom_read(struct dm9000 *p, int offset)
     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 +335,7 @@ static void eeprom_write(struct dm9000 *p, int offset, cyg_uint16 val)
     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 +345,7 @@ static void eeprom_reload(struct dm9000 *p)
     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 +374,21 @@ static void phy_write(struct dm9000 *p, int offset, cyg_uint16 val)
 
 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;
+
+    /* power on PHY */
+    putreg(p, DM_GPCR, 0x01);
+    putreg(p, DM_GPR,  0x00);
 
-    // release reset
-    putreg(p, DM_GPCR, 1);
-    putreg(p, DM_GPR, 0);  
+    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);
 }
 
 
@@ -424,6 +448,19 @@ static int initialize_nic(struct dm9000 *priv)
     return 1;
 }
 
+#ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED
+static cyg_uint32 dm9000_isr(cyg_vector_t vector, cyg_addrword_t data)
+{
+    struct eth_drv_sc *sc = (struct eth_drv_sc *)data;
+    struct dm9000 *priv = (struct dm9000 *)sc->driver_private;
+
+    cyg_drv_interrupt_mask(priv->interrupt);
+    cyg_drv_interrupt_acknowledge(priv->interrupt);
+
+    return CYG_ISR_HANDLED | CYG_ISR_CALL_DSR;
+}
+#endif
+
 
 // ------------------------------------------------------------------------
 //
@@ -457,6 +494,19 @@ dm9000_init(struct cyg_netdevtab_entry * ndp)
     if (id != 0x90000A46)
        return 0;
 
+#ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED
+    cyg_drv_interrupt_create(priv->interrupt,
+                             0,
+                             (cyg_addrword_t)sc,
+                             dm9000_isr,
+                             eth_drv_dsr,
+                             &priv->interrupt_handle,
+                             &priv->interrupt_object);
+    cyg_drv_interrupt_attach(priv->interrupt_handle);
+    cyg_drv_interrupt_acknowledge(priv->interrupt);
+    cyg_drv_interrupt_unmask(priv->interrupt);
+#endif // !CYGPKG_IO_ETH_DRIVERS_STAND_ALONE
+
     for (i = 0; i < 64; i++)
        u16tab[i] = eeprom_read(priv, i);
 
@@ -600,8 +650,7 @@ dm9000_recv( struct eth_drv_sc *sc, struct eth_drv_sg *sg_list, int sg_len )
        ++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 +686,32 @@ dm9000_send(struct eth_drv_sc *sc,
            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;
+
+    /* Disable all interrupts */
+    putreg(priv, DM_IMR, IMR_PAR);
 
     HAL_WRITE_UINT8(priv->io_addr, DM_MWCMD);
 
@@ -662,39 +721,41 @@ dm9000_send(struct eth_drv_sc *sc,
            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;
@@ -704,7 +765,8 @@ dm9000_send(struct eth_drv_sc *sc,
 
     putreg(priv, DM_TCR, TCR_TXREQ);
 
-    return;
+    /* Re-enable interrupt */
+    putreg(priv, DM_IMR, IMR_PAR | IMR_PTM | IMR_PRM);
 }
 
 // ------------------------------------------------------------------------
@@ -716,7 +778,7 @@ static void
 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 +791,20 @@ dm9000_poll(struct eth_drv_sc *sc)
 
     // check for rx done
     if (1 /*status & ISR_PRS*/) {
+        cyg_uint8 hdr[4]; /* 4 byte Rx pkt hdr */
+
+        getreg(priv, DM_MRCMDX); /* dummy read */
 
-       rx1 = getreg(priv, DM_MRCMDX);
-       HAL_READ_UINT8(priv->io_data, rxstat);
+        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, 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]);
+            HAL_WRITE_UINT8(priv->io_addr, DM_MRCMD);
+            for (i = 0; i < 4;)
+                i += priv->read_data(priv, hdr + i);
 
-#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 +814,7 @@ dm9000_poll(struct eth_drv_sc *sc)
                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 +822,7 @@ dm9000_poll(struct eth_drv_sc *sc)
                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;
@@ -833,7 +886,13 @@ dm9000_poll(struct eth_drv_sc *sc)
 static void
 dm9000_deliver(struct eth_drv_sc *sc)
 {
+    struct dm9000 *priv = (struct dm9000 *)sc->driver_private;
+
     dm9000_poll(sc);
+
+#ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED
+    cyg_drv_interrupt_unmask(priv->interrupt);
+#endif
 }
 
 // ------------------------------------------------------------------------
@@ -844,10 +903,9 @@ dm9000_deliver(struct eth_drv_sc *sc)
 static int
 dm9000_int_vector(struct eth_drv_sc *sc)
 {
-    struct dm9000 *priv;
-    priv = (struct dm9000 *)sc->driver_private;
+    struct dm9000 *priv = (struct dm9000 *)sc->driver_private;
 
-    return -1;
+    return priv->interrupt;
 }
 
 
@@ -860,6 +918,32 @@ static int
 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;
 }