Merge branch 'master' of git://git.denx.de/u-boot
[platform/kernel/u-boot.git] / drivers / net / dm9000x.c
index 68901cd..fa95a73 100644 (file)
@@ -36,7 +36,24 @@ v1.2   03/18/2003       Weilun Huang <weilun_huang@davicom.com.tw>:
 
 --------------------------------------
 
-       12/15/2003       Initial port to u-boot by Sascha Hauer <saschahauer@web.de>
+       12/15/2003       Initial port to u-boot by
+                               Sascha Hauer <saschahauer@web.de>
+
+       06/03/2008      Remy Bohmer <linux@bohmer.net>
+                       - Fixed the driver to work with DM9000A.
+                         (check on ISR receive status bit before reading the
+                         FIFO as described in DM9000 programming guide and
+                         application notes)
+                       - Added autodetect of databus width.
+                       - Made debug code compile again.
+                       - Adapt eth_send such that it matches the DM9000*
+                         application notes. Needed to make it work properly
+                         for DM9000A.
+                       - Adapted reset procedure to match DM9000 application
+                         notes (i.e. double reset)
+                       - some minor code cleanups
+                       These changes are tested with DM9000{A,EP,E} together
+                       with a 200MHz Atmel AT91SAM92161 core
 
 TODO: Homerun NIC and longrun NIC are not functional, only internal at the
       moment.
@@ -59,10 +76,22 @@ TODO: Homerun NIC and longrun NIC are not functional, only internal at the
 /* #define CONFIG_DM9000_DEBUG */
 
 #ifdef CONFIG_DM9000_DEBUG
-#define DM9000_DBG(fmt,args...) printf(fmt ,##args)
-#else                          /*  */
+#define DM9000_DBG(fmt,args...) printf(fmt, ##args)
+#define DM9000_DMP_PACKET(func,packet,length)  \
+       do { \
+               int i;                                                  \
+               printf(func ": length: %d\n", length);                  \
+               for (i = 0; i < length; i++) {                          \
+                       if (i % 8 == 0)                                 \
+                               printf("\n%s: %02x: ", func, i);        \
+                       printf("%02x ", ((unsigned char *) packet)[i]); \
+               } printf("\n");                                         \
+       } while(0)
+#else
 #define DM9000_DBG(fmt,args...)
-#endif                         /*  */
+#define DM9000_DMP_PACKET(func,packet,length)
+#endif
+
 enum DM9000_PHY_mode { DM9000_10MHD = 0, DM9000_100MHD =
            1, DM9000_10MFD = 4, DM9000_100MFD = 5, DM9000_AUTO =
            8, DM9000_1M_HPNA = 0x10
@@ -84,8 +113,11 @@ typedef struct board_info {
        u8 device_wait_reset;   /* device state */
        u8 nic_type;            /* NIC type */
        unsigned char srom[128];
+       void (*outblk)(volatile void *data_ptr, int count);
+       void (*inblk)(void *data_ptr, int count);
+       void (*rx_status)(u16 *RxStatus, u16 *RxLen);
 } board_info_t;
-board_info_t dmfe_info;
+static board_info_t dm9000_info;
 
 /* For module input parameter */
 static int media_mode = DM9000_AUTO;
@@ -124,10 +156,85 @@ dump_regs(void)
        DM9000_DBG("TSRII (0x04): %02x\n", DM9000_ior(4));
        DM9000_DBG("RCR   (0x05): %02x\n", DM9000_ior(5));
        DM9000_DBG("RSR   (0x06): %02x\n", DM9000_ior(6));
-       DM9000_DBG("ISR   (0xFE): %02x\n", DM9000_ior(ISR));
+       DM9000_DBG("ISR   (0xFE): %02x\n", DM9000_ior(DM9000_ISR));
        DM9000_DBG("\n");
 }
-#endif                         /*  */
+#endif
+
+static void dm9000_outblk_8bit(volatile void *data_ptr, int count)
+{
+       int i;
+       for (i = 0; i < count; i++)
+               DM9000_outb((((u8 *) data_ptr)[i] & 0xff), DM9000_DATA);
+}
+
+static void dm9000_outblk_16bit(volatile void *data_ptr, int count)
+{
+       int i;
+       u32 tmplen = (count + 1) / 2;
+
+       for (i = 0; i < tmplen; i++)
+               DM9000_outw(((u16 *) data_ptr)[i], DM9000_DATA);
+}
+static void dm9000_outblk_32bit(volatile void *data_ptr, int count)
+{
+       int i;
+       u32 tmplen = (count + 3) / 4;
+
+       for (i = 0; i < tmplen; i++)
+               DM9000_outl(((u32 *) data_ptr)[i], DM9000_DATA);
+}
+
+static void dm9000_inblk_8bit(void *data_ptr, int count)
+{
+       int i;
+       for (i = 0; i < count; i++)
+               ((u8 *) data_ptr)[i] = DM9000_inb(DM9000_DATA);
+}
+
+static void dm9000_inblk_16bit(void *data_ptr, int count)
+{
+       int i;
+       u32 tmplen = (count + 1) / 2;
+
+       for (i = 0; i < tmplen; i++)
+               ((u16 *) data_ptr)[i] = DM9000_inw(DM9000_DATA);
+}
+static void dm9000_inblk_32bit(void *data_ptr, int count)
+{
+       int i;
+       u32 tmplen = (count + 3) / 4;
+
+       for (i = 0; i < tmplen; i++)
+               ((u32 *) data_ptr)[i] = DM9000_inl(DM9000_DATA);
+}
+
+static void dm9000_rx_status_32bit(u16 *RxStatus, u16 *RxLen)
+{
+       u32 tmpdata;
+
+       DM9000_outb(DM9000_MRCMD, DM9000_IO);
+
+       tmpdata = DM9000_inl(DM9000_DATA);
+       *RxStatus = tmpdata;
+       *RxLen = tmpdata >> 16;
+}
+
+static void dm9000_rx_status_16bit(u16 *RxStatus, u16 *RxLen)
+{
+       DM9000_outb(DM9000_MRCMD, DM9000_IO);
+
+       *RxStatus = DM9000_inw(DM9000_DATA);
+       *RxLen = DM9000_inw(DM9000_DATA);
+}
+
+static void dm9000_rx_status_8bit(u16 *RxStatus, u16 *RxLen)
+{
+       DM9000_outb(DM9000_MRCMD, DM9000_IO);
+
+       *RxStatus = DM9000_inb(DM9000_DATA) + (DM9000_inb(DM9000_DATA) << 8);
+       *RxLen = DM9000_inb(DM9000_DATA) + (DM9000_inb(DM9000_DATA) << 8);
+}
 
 /*
   Search DM9000 board, allocate space and register it
@@ -236,7 +343,7 @@ program_dm9802(void)
 static void
 identify_nic(void)
 {
-       struct board_info *db = &dmfe_info;     /* Point a board information structure */
+       struct board_info *db = &dm9000_info;
        u16 phy_reg3;
        DM9000_iow(DM9000_NCR, NCR_EXT_PHY);
        phy_reg3 = phy_read(3);
@@ -263,9 +370,35 @@ identify_nic(void)
 static void
 dm9000_reset(void)
 {
-       DM9000_DBG("resetting\n");
-       DM9000_iow(DM9000_NCR, NCR_RST);
-       udelay(1000);           /* delay 1ms */
+       DM9000_DBG("resetting DM9000\n");
+
+       /* Reset DM9000,
+          see DM9000 Application Notes V1.22 Jun 11, 2004 page 29 */
+
+       /* DEBUG: Make all GPIO pins outputs */
+       DM9000_iow(DM9000_GPCR, 0x0F);
+       /* Step 1: Power internal PHY by writing 0 to GPIO0 pin */
+       DM9000_iow(DM9000_GPR, 0);
+       /* Step 2: Software reset */
+       DM9000_iow(DM9000_NCR, 3);
+
+       do {
+               DM9000_DBG("resetting the DM9000, 1st reset\n");
+               udelay(25); /* Wait at least 20 us */
+       } while (DM9000_ior(DM9000_NCR) & 1);
+
+       DM9000_iow(DM9000_NCR, 0);
+       DM9000_iow(DM9000_NCR, 3); /* Issue a second reset */
+
+       do {
+               DM9000_DBG("resetting the DM9000, 2nd reset\n");
+               udelay(25); /* Wait at least 20 us */
+       } while (DM9000_ior(DM9000_NCR) & 1);
+
+       /* Check whether the ethernet controller is present */
+       if ((DM9000_ior(DM9000_PIDL) != 0x0) ||
+           (DM9000_ior(DM9000_PIDH) != 0x90))
+               printf("ERROR: resetting DM9000 -> not responding\n");
 }
 
 /* Initilize dm9000 board
@@ -274,12 +407,46 @@ int
 eth_init(bd_t * bd)
 {
        int i, oft, lnk;
+       u8 io_mode;
+       struct board_info *db = &dm9000_info;
+
        DM9000_DBG("eth_init()\n");
 
        /* RESET device */
        dm9000_reset();
        dm9000_probe();
 
+       /* Auto-detect 8/16/32 bit mode, ISR Bit 6+7 indicate bus width */
+       io_mode = DM9000_ior(DM9000_ISR) >> 6;
+
+       switch (io_mode) {
+       case 0x0:  /* 16-bit mode */
+               printf("DM9000: running in 16 bit mode\n");
+               db->outblk    = dm9000_outblk_16bit;
+               db->inblk     = dm9000_inblk_16bit;
+               db->rx_status = dm9000_rx_status_16bit;
+               break;
+       case 0x01:  /* 32-bit mode */
+               printf("DM9000: running in 32 bit mode\n");
+               db->outblk    = dm9000_outblk_32bit;
+               db->inblk     = dm9000_inblk_32bit;
+               db->rx_status = dm9000_rx_status_32bit;
+               break;
+       case 0x02: /* 8 bit mode */
+               printf("DM9000: running in 8 bit mode\n");
+               db->outblk    = dm9000_outblk_8bit;
+               db->inblk     = dm9000_inblk_8bit;
+               db->rx_status = dm9000_rx_status_8bit;
+               break;
+       default:
+               /* Assume 8 bit mode, will probably not work anyway */
+               printf("DM9000: Undefined IO-mode:0x%x\n", io_mode);
+               db->outblk    = dm9000_outblk_8bit;
+               db->inblk     = dm9000_inblk_8bit;
+               db->rx_status = dm9000_rx_status_8bit;
+               break;
+       }
+
        /* NIC Type: FASTETHER, HOMERUN, LONGRUN */
        identify_nic();
 
@@ -289,15 +456,22 @@ eth_init(bd_t * bd)
        /* Set PHY */
        set_PHY_mode();
 
-       /* Program operating register */
-       DM9000_iow(DM9000_NCR, 0x0);    /* only intern phy supported by now */
-       DM9000_iow(DM9000_TCR, 0);      /* TX Polling clear */
-       DM9000_iow(DM9000_BPTR, 0x3f);  /* Less 3Kb, 200us */
-       DM9000_iow(DM9000_FCTR, FCTR_HWOT(3) | FCTR_LWOT(8));   /* Flow Control : High/Low Water */
-       DM9000_iow(DM9000_FCR, 0x0);    /* SH FIXME: This looks strange! Flow Control */
-       DM9000_iow(DM9000_SMCR, 0);     /* Special Mode */
-       DM9000_iow(DM9000_NSR, NSR_WAKEST | NSR_TX2END | NSR_TX1END);   /* clear TX status */
-       DM9000_iow(DM9000_ISR, 0x0f);   /* Clear interrupt status */
+       /* Program operating register, only intern phy supported by now */
+       DM9000_iow(DM9000_NCR, 0x0);
+       /* TX Polling clear */
+       DM9000_iow(DM9000_TCR, 0);
+       /* Less 3Kb, 200us */
+       DM9000_iow(DM9000_BPTR, 0x3f);
+       /* Flow Control : High/Low Water */
+       DM9000_iow(DM9000_FCTR, FCTR_HWOT(3) | FCTR_LWOT(8));
+       /* SH FIXME: This looks strange! Flow Control */
+       DM9000_iow(DM9000_FCR, 0x0);
+       /* Special Mode */
+       DM9000_iow(DM9000_SMCR, 0);
+       /* clear TX status */
+       DM9000_iow(DM9000_NSR, NSR_WAKEST | NSR_TX2END | NSR_TX1END);
+       /* Clear interrupt status */
+       DM9000_iow(DM9000_ISR, 0x0f);
 
        /* Set Node address */
 #ifndef CONFIG_AT91SAM9261EK
@@ -333,8 +507,11 @@ eth_init(bd_t * bd)
        DM9000_DBG("\n");
 
        /* Activate DM9000 */
-       DM9000_iow(DM9000_RCR, RCR_DIS_LONG | RCR_DIS_CRC | RCR_RXEN);  /* RX enable */
-       DM9000_iow(DM9000_IMR, IMR_PAR);        /* Enable TX/RX interrupt mask */
+       /* RX enable */
+       DM9000_iow(DM9000_RCR, RCR_DIS_LONG | RCR_DIS_CRC | RCR_RXEN);
+       /* Enable TX/RX interrupt mask */
+       DM9000_iow(DM9000_IMR, IMR_PAR);
+
        i = 0;
        while (!(phy_read(1) & 0x20)) { /* autonegation complete bit */
                udelay(1000);
@@ -376,54 +553,37 @@ eth_init(bd_t * bd)
 int
 eth_send(volatile void *packet, int length)
 {
-       char *data_ptr;
-       u32 tmplen, i;
        int tmo;
-       DM9000_DBG("eth_send: length: %d\n", length);
-       for (i = 0; i < length; i++) {
-               if (i % 8 == 0)
-                       DM9000_DBG("\nSend: 02x: ", i);
-               DM9000_DBG("%02x ", ((unsigned char *) packet)[i]);
-       } DM9000_DBG("\n");
+       struct board_info *db = &dm9000_info;
 
-       /* Move data to DM9000 TX RAM */
-       data_ptr = (char *) packet;
-       DM9000_outb(DM9000_MWCMD, DM9000_IO);
+       DM9000_DMP_PACKET("eth_send", packet, length);
 
-#ifdef CONFIG_DM9000_USE_8BIT
-       /* Byte mode */
-       for (i = 0; i < length; i++)
-               DM9000_outb((data_ptr[i] & 0xff), DM9000_DATA);
+       DM9000_iow(DM9000_ISR, IMR_PTM); /* Clear Tx bit in ISR */
 
-#endif                         /*  */
-#ifdef CONFIG_DM9000_USE_16BIT
-       tmplen = (length + 1) / 2;
-       for (i = 0; i < tmplen; i++)
-               DM9000_outw(((u16 *) data_ptr)[i], DM9000_DATA);
-
-#endif                         /*  */
-#ifdef CONFIG_DM9000_USE_32BIT
-       tmplen = (length + 3) / 4;
-       for (i = 0; i < tmplen; i++)
-               DM9000_outl(((u32 *) data_ptr)[i], DM9000_DATA);
+       /* Move data to DM9000 TX RAM */
+       DM9000_outb(DM9000_MWCMD, DM9000_IO); /* Prepare for TX-data */
 
-#endif                         /*  */
+       /* push the data to the TX-fifo */
+       (db->outblk)(packet, length);
 
        /* Set TX length to DM9000 */
        DM9000_iow(DM9000_TXPLL, length & 0xff);
        DM9000_iow(DM9000_TXPLH, (length >> 8) & 0xff);
 
        /* Issue TX polling command */
-       DM9000_iow(DM9000_TCR, TCR_TXREQ);      /* Cleared after TX complete */
+       DM9000_iow(DM9000_TCR, TCR_TXREQ); /* Cleared after TX complete */
 
        /* wait for end of transmission */
        tmo = get_timer(0) + 5 * CFG_HZ;
-       while (DM9000_ior(DM9000_TCR) & TCR_TXREQ) {
+       while ( !(DM9000_ior(DM9000_NSR) & (NSR_TX1END | NSR_TX2END)) ||
+               !(DM9000_ior(DM9000_ISR) & IMR_PTM) ) {
                if (get_timer(0) >= tmo) {
                        printf("transmission timeout\n");
                        break;
                }
        }
+       DM9000_iow(DM9000_ISR, IMR_PTM); /* Clear Tx bit in ISR */
+
        DM9000_DBG("transmit done\n\n");
        return 0;
 }
@@ -452,86 +612,67 @@ eth_rx(void)
 {
        u8 rxbyte, *rdptr = (u8 *) NetRxPackets[0];
        u16 RxStatus, RxLen = 0;
-       u32 tmplen, i;
-#ifdef CONFIG_DM9000_USE_32BIT
-       u32 tmpdata;
-#endif
+       struct board_info *db = &dm9000_info;
 
-       /* Check packet ready or not */
-       DM9000_ior(DM9000_MRCMDX);      /* Dummy read */
-       rxbyte = DM9000_inb(DM9000_DATA);       /* Got most updated data */
-       if (rxbyte == 0)
+       /* Check packet ready or not, we must check
+          the ISR status first for DM9000A */
+       if (!(DM9000_ior(DM9000_ISR) & 0x01)) /* Rx-ISR bit must be set. */
                return 0;
 
-       /* Status check: this byte must be 0 or 1 */
-       if (rxbyte > 1) {
-               DM9000_iow(DM9000_RCR, 0x00);   /* Stop Device */
-               DM9000_iow(DM9000_ISR, 0x80);   /* Stop INT request */
-               DM9000_DBG("rx status check: %d\n", rxbyte);
-       }
-       DM9000_DBG("receiving packet\n");
-
-       /* A packet ready now  & Get status/length */
-       DM9000_outb(DM9000_MRCMD, DM9000_IO);
-
-#ifdef CONFIG_DM9000_USE_8BIT
-       RxStatus = DM9000_inb(DM9000_DATA) + (DM9000_inb(DM9000_DATA) << 8);
-       RxLen = DM9000_inb(DM9000_DATA) + (DM9000_inb(DM9000_DATA) << 8);
-
-#endif                         /*  */
-#ifdef CONFIG_DM9000_USE_16BIT
-       RxStatus = DM9000_inw(DM9000_DATA);
-       RxLen = DM9000_inw(DM9000_DATA);
-
-#endif                         /*  */
-#ifdef CONFIG_DM9000_USE_32BIT
-       tmpdata = DM9000_inl(DM9000_DATA);
-       RxStatus = tmpdata;
-       RxLen = tmpdata >> 16;
+       DM9000_iow(DM9000_ISR, 0x01); /* clear PR status latched in bit 0 */
 
-#endif                         /*  */
-       DM9000_DBG("rx status: 0x%04x rx len: %d\n", RxStatus, RxLen);
+       /* There is _at least_ 1 package in the fifo, read them all */
+       for (;;) {
+               DM9000_ior(DM9000_MRCMDX);      /* Dummy read */
 
-       /* Move data from DM9000 */
-       /* Read received packet from RX SRAM */
-#ifdef CONFIG_DM9000_USE_8BIT
-       for (i = 0; i < RxLen; i++)
-               rdptr[i] = DM9000_inb(DM9000_DATA);
+               /* Get most updated data,
+                  only look at bits 0:1, See application notes DM9000 */
+               rxbyte = DM9000_inb(DM9000_DATA) & 0x03;
 
-#endif                         /*  */
-#ifdef CONFIG_DM9000_USE_16BIT
-       tmplen = (RxLen + 1) / 2;
-       for (i = 0; i < tmplen; i++)
-               ((u16 *) rdptr)[i] = DM9000_inw(DM9000_DATA);
+               /* Status check: this byte must be 0 or 1 */
+               if (rxbyte > DM9000_PKT_RDY) {
+                       DM9000_iow(DM9000_RCR, 0x00);   /* Stop Device */
+                       DM9000_iow(DM9000_ISR, 0x80);   /* Stop INT request */
+                       printf("DM9000 error: status check fail: 0x%x\n",
+                               rxbyte);
+                       return 0;
+               }
 
-#endif                         /*  */
-#ifdef CONFIG_DM9000_USE_32BIT
-       tmplen = (RxLen + 3) / 4;
-       for (i = 0; i < tmplen; i++)
-               ((u32 *) rdptr)[i] = DM9000_inl(DM9000_DATA);
+               if (rxbyte != DM9000_PKT_RDY)
+                       return 0; /* No packet received, ignore */
+
+               DM9000_DBG("receiving packet\n");
+
+               /* A packet ready now  & Get status/length */
+               (db->rx_status)(&RxStatus, &RxLen);
+
+               DM9000_DBG("rx status: 0x%04x rx len: %d\n", RxStatus, RxLen);
+
+               /* Move data from DM9000 */
+               /* Read received packet from RX SRAM */
+               (db->inblk)(rdptr, RxLen);
+
+               if ((RxStatus & 0xbf00) || (RxLen < 0x40)
+                       || (RxLen > DM9000_PKT_MAX)) {
+                       if (RxStatus & 0x100) {
+                               printf("rx fifo error\n");
+                       }
+                       if (RxStatus & 0x200) {
+                               printf("rx crc error\n");
+                       }
+                       if (RxStatus & 0x8000) {
+                               printf("rx length error\n");
+                       }
+                       if (RxLen > DM9000_PKT_MAX) {
+                               printf("rx length too big\n");
+                               dm9000_reset();
+                       }
+               } else {
+                       DM9000_DMP_PACKET("eth_rx", rdptr, RxLen);
 
-#endif                         /*  */
-       if ((RxStatus & 0xbf00) || (RxLen < 0x40)
-           || (RxLen > DM9000_PKT_MAX)) {
-               if (RxStatus & 0x100) {
-                       printf("rx fifo error\n");
+                       DM9000_DBG("passing packet to upper layer\n");
+                       NetReceive(NetRxPackets[0], RxLen);
                }
-               if (RxStatus & 0x200) {
-                       printf("rx crc error\n");
-               }
-               if (RxStatus & 0x8000) {
-                       printf("rx length error\n");
-               }
-               if (RxLen > DM9000_PKT_MAX) {
-                       printf("rx length too big\n");
-                       dm9000_reset();
-               }
-       } else {
-
-               /* Pass to upper layer */
-               DM9000_DBG("passing packet to upper layer\n");
-               NetReceive(NetRxPackets[0], RxLen);
-               return RxLen;
        }
        return 0;
 }
@@ -592,12 +733,12 @@ phy_read(int reg)
        /* Fill the phyxcer register into REG_0C */
        DM9000_iow(DM9000_EPAR, DM9000_PHY | reg);
        DM9000_iow(DM9000_EPCR, 0xc);   /* Issue phyxcer read command */
-       udelay(100);            /* Wait read complete */
+       udelay(100);                    /* Wait read complete */
        DM9000_iow(DM9000_EPCR, 0x0);   /* Clear phyxcer read command */
        val = (DM9000_ior(DM9000_EPDRH) << 8) | DM9000_ior(DM9000_EPDRL);
 
        /* The read data keeps on REG_0D & REG_0E */
-       DM9000_DBG("phy_read(%d): %d\n", reg, val);
+       DM9000_DBG("phy_read(0x%x): 0x%x\n", reg, val);
        return val;
 }
 
@@ -615,8 +756,8 @@ phy_write(int reg, u16 value)
        DM9000_iow(DM9000_EPDRL, (value & 0xff));
        DM9000_iow(DM9000_EPDRH, ((value >> 8) & 0xff));
        DM9000_iow(DM9000_EPCR, 0xa);   /* Issue phyxcer write command */
-       udelay(500);            /* Wait write complete */
+       udelay(500);                    /* Wait write complete */
        DM9000_iow(DM9000_EPCR, 0x0);   /* Clear phyxcer write command */
-       DM9000_DBG("phy_write(reg:%d, value:%d)\n", reg, value);
+       DM9000_DBG("phy_write(reg:0x%x, value:0x%x)\n", reg, value);
 }
-#endif                         /* CONFIG_DRIVER_DM9000 */
+#endif /* CONFIG_DRIVER_DM9000 */