- updates from the linux network driver
authortobylorenz <tobylorenz>
Sun, 10 Apr 2005 15:24:24 +0000 (15:24 +0000)
committertobylorenz <tobylorenz>
Sun, 10 Apr 2005 15:24:24 +0000 (15:24 +0000)
- mdio error handling optimised
- phy auto detection and support list
- p2001_eth_mdio_hard_reset renamed to p2001_eth_phyreset
- MDIO clock change moved to probe function
- enhancements in comments

src/arch/armnommu/drivers/net/p2001_eth.c

index 81bc84c..7b80852 100644 (file)
@@ -1,10 +1,10 @@
 /**************************************************************************
-Etherboot -  BOOTP/TFTP Bootstrap Program
-P2001 NIC driver for Etherboot
-***************************************************************************/
+ * Etherboot -  BOOTP/TFTP Bootstrap Program
+ * P2001 NIC driver for Etherboot
+ **************************************************************************/
 
 /*
- *  Copyright (C) 2004 Tobias Lorenz
+ *  Copyright (C) 2005 Tobias Lorenz
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -19,37 +19,34 @@ P2001 NIC driver for Etherboot
 #include "isa.h"
 
 #include "hardware.h"
-#include "lxt971a.h"
+#include "mii.h"
 #include "timer.h"
 
 
 /* NIC specific static variables go here */
 static unsigned char MAC_HW_ADDR[6]={MAC_HW_ADDR_DRV};
 
+#define P2001_ETH_PHY_AUTO_DETECT
+
 /* DMA descriptors and buffers */
 #define NUM_RX_DESC     4      /* Number of Rx descriptor registers. */
 #define DMA_BUF_SIZE   2048    /* Buffer size */
 static DMA_DSC txd              __attribute__ ((__section__(".dma.desc")));
 static DMA_DSC rxd[NUM_RX_DESC] __attribute__ ((__section__(".dma.desc")));
-static unsigned char rxb[NUM_RX_DESC * DMA_BUF_SIZE] __attribute__ ((__section__(".dma.buffer")));
-static unsigned char txb[              DMA_BUF_SIZE] __attribute__ ((__section__(".dma.buffer")));
+static char rxb[NUM_RX_DESC * DMA_BUF_SIZE] __attribute__ ((__section__(".dma.buffer")));
+static char txb[              DMA_BUF_SIZE] __attribute__ ((__section__(".dma.buffer")));
 static unsigned int cur_rx;
 
 /* Device selectors */
 static unsigned int cur_channel;       // DMA channel    : 0..3
 static unsigned int cur_phy;           // PHY Address    : 0..31
 static P2001_ETH_regs_ptr EU;          // Ethernet Unit  : 0x0018_000 with _=0..3
-static P2001_ETH_regs_ptr MU;          // Management Unit: 0x00180000
-
-#define MDIO_MAXCOUNT 1000                     /* mdio abort */
-static unsigned int mdio_error;                        /* mdio error */
 
-/* Function prototypes */
-static void         p2001_eth_mdio_init ();
-static void         p2001_eth_mdio_write(unsigned int phyadr, unsigned int regadr, unsigned int data);
-static unsigned int p2001_eth_mdio_read (unsigned int phyadr, unsigned int regadr);
-extern unsigned int p2001_eth_mdio_error;
+/* mdio handling */
+static int          p2001_eth_mdio_read (int phy_id, int location);
+static void         p2001_eth_mdio_write(int phy_id, int location, int val);
 
+/* net_device functions */
 static int          p2001_eth_poll      (struct nic *nic, int retrieve);
 static void         p2001_eth_transmit  (struct nic *nic, const char *d,
                                        unsigned int t, unsigned int s, const char *p);
@@ -60,103 +57,106 @@ static void         p2001_eth_init      ();
 static void         p2001_eth_disable   (struct dev *dev);
 
 static int          p2001_eth_check_link(unsigned int phy);
+static void         p2001_eth_phyreset  ();
 static int          p2001_eth_probe     (struct dev *dev, unsigned short *probe_addrs __unused);
 
+/* Supported MII list */
+static struct mii_chip_info {
+       const char * name;
+       unsigned int physid;    // (MII_PHYSID2 << 16) | MII_PHYSID1
+} mii_chip_table[] = {
+       { "Intel LXT971A",      0x78e20013 },
+       { "Altera AC104-QF",    0x55410022 },
+       {NULL,0},
+};
+
+
 
 /**************************************************************************
-PHY MANAGEMENT UNIT - Read/write
-***************************************************************************/
-static void p2001_eth_mdio_init()
+ * PHY MANAGEMENT UNIT - Read/write
+ **************************************************************************/
+
+/**
+ *     mdio_read - read MII PHY register
+ *     @dev: the net device to read
+ *     @regadr: the phy register id to read
+ *
+ *     Read MII registers through MDIO and MDC
+ *     using MDIO management frame structure and protocol(defined by ISO/IEC).
+ */
+static int p2001_eth_mdio_read(int phy_id, int location)
 {
-       /* reset ethernet PHYs */
-       printf("Resetting PHYs...\n");
+       int result, boguscnt = 1000;
 
-       /* GPIO24/25: TX_ER2/TX_ER0 */
-       /* GPIO26/27: PHY_RESET/TX_ER1 */
-       P2001_GPIO->PIN_MUX |= 0x0018;
-       // 31-16: 0000 1111 0000 0000
-       P2001_GPIO->GPIO2_En |= 0x0400;
+       do {
+               /* Warten bis Hardware inaktiv (MIU = "0") */
+               while (P2001_MU->MU_CNTL & 0x8000)
+                       barrier();
 
-       P2001_GPIO->GPIO2_Out |= 0x04000000;
-       P2001_GPIO->GPIO2_Out &= ~0x0400;
-       mdelay(500);
-       P2001_GPIO->GPIO2_Out |= 0x0400;
+               /* Schreiben MU_CNTL */
+               P2001_MU->MU_CNTL = location + (phy_id<<5) + (2<<10);
 
-       /* set management unit clock divisor */
-       // max. MDIO CLK = 2.048 MHz (EU.doc)
-       // max. MDIO CLK = 8.000 MHz (LXT971A)
-       // sysclk/(2*(n+1)) = MDIO CLK <= 2.048 MHz
-       // n >= sysclk/4.096 MHz - 1
-#if SYSCLK == 73728000
-       P2001_MU->MU_DIV = 17;  // 73.728 MHZ =17=> 2.020 MHz
-#else
-       //MU->MU_DIV = (SYSCLK/4.096)-1;
-#error "Please define a proper MDIO CLK divisor for that sysclk."
-#endif
-       asm("nop \n nop");
+               /* Warten bis Hardware aktiv (MIU = "1") */
+               while ((P2001_MU->MU_CNTL & 0x8000) == 0)
+                       barrier();
+               //asm("nop \r\n nop");
+
+               /* Warten bis Hardware inaktiv (MIU = "0") */
+               while (P2001_MU->MU_CNTL & 0x8000)
+                       barrier();
+
+               /* Fehler, wenn MDIO Read Error (MRE = "1") */
+       } while ((P2001_MU->MU_CNTL & 0x4000) && (--boguscnt > 0));
+
+       /* Lesen MU_DATA */
+       result = P2001_MU->MU_DATA;
+
+       if (boguscnt == 0)
+               return 0;
+       if ((result & 0xffff) == 0xffff)
+               return 0;
+
+       return result & 0xffff;
 }
 
-static void p2001_eth_mdio_write(unsigned int phyadr, unsigned int regadr, unsigned int data)
-{
-       static unsigned int count;
-       count = 0;
 
+/**
+ *     mdio_write - write MII PHY register
+ *     @dev: the net device to write
+ *     @regadr: the phy register id to write
+ *     @value: the register value to write with
+ *
+ *     Write MII registers with @value through MDIO and MDC
+ *     using MDIO management frame structure and protocol(defined by ISO/IEC)
+ */
+static void p2001_eth_mdio_write(int phy_id, int location, int val)
+{
        /* Warten bis Hardware inaktiv (MIU = "0") */
-       while ((MU->MU_CNTL & 0x8000) && (count < MDIO_MAXCOUNT))
-               count++;
+       while (P2001_MU->MU_CNTL & 0x8000)
+               barrier();
 
        /* Schreiben MU_DATA */
-       MU->MU_DATA = data;
+       P2001_MU->MU_DATA = val;
 
        /* Schreiben MU_CNTL */
-       MU->MU_CNTL = regadr + (phyadr<<5) + (1<<10);
+       P2001_MU->MU_CNTL = location + (phy_id<<5) + (1<<10);
 
        /* Warten bis Hardware aktiv (MIU = "1") */
-       while (((MU->MU_CNTL & 0x8000) == 0) && (count < MDIO_MAXCOUNT))
-               count++;
+       while ((P2001_MU->MU_CNTL & 0x8000) == 0)
+               barrier();
        //asm("nop \r\n nop");
 
        /* Warten bis Hardware inaktiv (MIU = "0") */
-       while ((MU->MU_CNTL & 0x8000) && (count < MDIO_MAXCOUNT))
-               count++;
-
-       mdio_error = (count >= MDIO_MAXCOUNT);
+       while (P2001_MU->MU_CNTL & 0x8000)
+               barrier();
 }
 
-static unsigned int p2001_eth_mdio_read(unsigned int phyadr, unsigned int regadr)
-{
-       static unsigned int count;
-       count = 0;
-
-       do {
-               /* Warten bis Hardware inaktiv (MIU = "0") */
-               while ((MU->MU_CNTL & 0x8000) && (count < MDIO_MAXCOUNT))
-                       count++;
-
-               /* Schreiben MU_CNTL */
-               MU->MU_CNTL = regadr + (phyadr<<5) + (2<<10);
-
-               /* Warten bis Hardware aktiv (MIU = "1") */
-               while (((MU->MU_CNTL & 0x8000) == 0) && (count < MDIO_MAXCOUNT))
-                       count++;
-               //asm("nop \r\n nop");
-
-               /* Warten bis Hardware inaktiv (MIU = "0") */
-               while ((MU->MU_CNTL & 0x8000) && (count < MDIO_MAXCOUNT))
-                       count++;
-
-               /* Fehler, wenn MDIO Read Error (MRE = "1") */
-       } while ((MU->MU_CNTL & 0x4000) && (count < MDIO_MAXCOUNT));
-
-       /* Lesen MU_DATA */
-       mdio_error = (count >= MDIO_MAXCOUNT);
-       return MU->MU_DATA;
-}
 
 
 /**************************************************************************
-POLL - Wait for a frame
-***************************************************************************/
+ * POLL - Wait for a frame
+ **************************************************************************/
+
 /* Function: p2001_eth_poll
  *
  * Description: checks for a received packet and returns it if found.
@@ -231,9 +231,11 @@ static int p2001_eth_poll(struct nic *nic, int retrieve)
 }
 
 
+
 /**************************************************************************
-TRANSMIT - Transmit a frame
-***************************************************************************/
+ * TRANSMIT - Transmit a frame
+ **************************************************************************/
+
 /* Function: p2001_eth_transmit
  *
  * Description: transmits a packet and waits for completion or timeout.
@@ -294,9 +296,11 @@ static void p2001_eth_transmit(
 }
 
 
+
 /**************************************************************************
-IRQ - Enable, Disable or Force Interrupts
-***************************************************************************/
+ * IRQ - Enable, Disable or Force Interrupts
+ **************************************************************************/
+
 /* Function: p2001_eth_irq
  *
  * Description: Enable, Disable, or Force, interrupts
@@ -321,9 +325,11 @@ p2001_eth_irq(struct nic *nic __unused, irq_action_t action __unused)
 }
 
 
+
 /**************************************************************************
-INIT - Initialize device
-***************************************************************************/
+ * INIT - Initialize device
+ **************************************************************************/
+
 /* Function: p2001_init
  *
  * Description: resets the ethernet controller chip and various
@@ -353,7 +359,7 @@ static void p2001_eth_init()
 //     txd.stat = (1<<31) | (1<<30) | (1<<29);                 // DSC0 OWN|START|END
 //     txd.cntl = cur_channel << 16;                           // DSC1 CHANNEL
 //     txd.cntl |= DMA_BUF_SIZE;                               // DSC1 LEN
-       txd.buf = &txb;                                         // DSC2 BUFFER
+       txd.buf = (char *)&txb;                                 // DSC2 BUFFER
        txd.next = &txd;                                        // DSC3 NEXTDSC @self
        EU->TMAC_DMA_DESC = &txd;
 
@@ -384,9 +390,10 @@ static void p2001_eth_init()
 }
 
 
+
 /**************************************************************************
-DISABLE - Turn off ethernet interface
-***************************************************************************/
+ * DISABLE - Turn off ethernet interface
+ **************************************************************************/
 static void p2001_eth_disable(struct dev *dev __unused)
 {
        /* put the card in its initial state */
@@ -408,40 +415,52 @@ static void p2001_eth_disable(struct dev *dev __unused)
 }
 
 
+
 /**************************************************************************
-LINK - Check for valid link
-***************************************************************************/
+ * LINK - Check for valid link
+ **************************************************************************/
 static int p2001_eth_check_link(unsigned int phy)
 {
        static int status;
-       static unsigned int count;
-       count = 0;
+       static unsigned int i, physid;
+
+       /* print some information about out PHY */
+       physid = (p2001_eth_mdio_read(phy, MII_PHYSID2) << 16) |
+                 p2001_eth_mdio_read(phy, MII_PHYSID1);
+       printf("PHY %d, ID 0x%x ", phy, physid);
+       for (i = 0; mii_chip_table[i].physid; i++)
+               if (mii_chip_table[i].physid == physid) {
+                       printf("(%s).\n", mii_chip_table[i].name);
+                       break;
+               }
+       if (!mii_chip_table[i].physid)
+               printf("(unknown).\n");
 
        /* Use 0x3300 for restarting NWay */
-       printf("Starting auto-negotiation... ");
-       p2001_eth_mdio_write(phy, Adr_LXT971A_Control, 0x3300);
-       if (mdio_error)
-               goto failed;
+       printf("Starting auto-negotiation...\n");
+       p2001_eth_mdio_write(phy, MII_BMCR, 0x3300);
 
-       /* Bits 1.5 and 17.7 are set to 1 once the Auto-Negotiation process to completed. */
+       /* Bits 1.5 is set to 1 once the Auto-Negotiation process to completed. */
+       i = 0;
        do {
                mdelay(500);
-               status = p2001_eth_mdio_read(phy, Adr_LXT971A_Status1);
-               if (mdio_error || (count++ > 6))        // 6*500ms = 3s timeout
+               status = p2001_eth_mdio_read(phy, MII_BMSR);
+               if (!status || (i++ > 6))       // 6*500ms = 3s timeout
                        goto failed;
-       } while (!(status & 0x20));
-       
-       /* Bits 1.2 and 17.10 are set to 1 once the link is established. */
-       if (p2001_eth_mdio_read(phy, Adr_LXT971A_Status1) & 0x04) {
-               /* Bits 17.14 and 17.9 can be used to determine the link operation conditions (speed and duplex). */
-               printf("Valid link, operating at: %sMb-%s\n",
-                       (p2001_eth_mdio_read(phy, Adr_LXT971A_Status2) & 0x4000) ? "100" : "10",
-                       (p2001_eth_mdio_read(phy, Adr_LXT971A_Status2) & 0x0200) ? "FD" : "HD");
-                       return 1;
+       } while (!(status & BMSR_ANEGCOMPLETE));
+
+       /* Bits 1.2 is set to 1 once the link is established. */
+       if ((status = p2001_eth_mdio_read(phy, MII_BMSR)) & BMSR_LSTATUS) {
+               if (physid == 0x78e20013)
+                       /* Bits 17.14 and 17.9 can be used to determine the link operation conditions (speed and duplex). */
+                       printf("  Valid link, operating at: %sMb-%s\n",
+                               (p2001_eth_mdio_read(phy, 0x17) & (1<<14)) ? "100" : "10",
+                               (p2001_eth_mdio_read(phy, 0x17) & (1<< 9)) ? "FD" : "HD");
+               return 1;
        }
 
 failed:
-       if (mdio_error)
+       if (!status)
                printf("Failed\n");
        else
                printf("No valid link\n");
@@ -449,9 +468,34 @@ failed:
 }
 
 
+
 /**************************************************************************
-PROBE - Look for an adapter, this routine's visible to the outside
-***************************************************************************/
+ * PHYRESET - hardware reset all MII PHYs
+ **************************************************************************/
+
+/**
+ *     p2001_eth_phyreset - hardware reset all MII PHYs
+ */
+static void p2001_eth_phyreset()
+{
+       /* GPIO24/25: TX_ER2/TX_ER0 */
+       /* GPIO26/27: PHY_RESET/TX_ER1 */
+       P2001_GPIO->PIN_MUX |= 0x0018;
+       // 31-16: 0000 1111 0000 0000
+       P2001_GPIO->GPIO2_En |= 0x0400;
+
+       P2001_GPIO->GPIO2_Out |= 0x04000000;
+       P2001_GPIO->GPIO2_Out &= ~0x0400;
+       mdelay(500);
+       P2001_GPIO->GPIO2_Out |= 0x0400;
+}
+
+
+
+/**************************************************************************
+ * PROBE - Look for an adapter, this routine's visible to the outside
+ **************************************************************************/
+
 static int p2001_eth_probe(struct dev *dev, unsigned short *probe_addrs __unused)
 {
        struct nic *nic = (struct nic *)dev;
@@ -460,30 +504,40 @@ static int p2001_eth_probe(struct dev *dev, unsigned short *probe_addrs __unused
        static int valid_link;
 
        /* reset phys and configure mdio clk */
-       p2001_eth_mdio_init();
+       printf("Resetting PHYs...\n");
+       p2001_eth_phyreset();
+
+       /* set management unit clock divisor */
+       // max. MDIO CLK = 2.048 MHz (EU.doc)
+       // max. MDIO CLK = 8.000 MHz (LXT971A)
+       // sysclk/(2*(n+1)) = MDIO CLK <= 2.048 MHz
+       // n >= sysclk/4.096 MHz - 1
+       P2001_MU->MU_DIV = (SYSCLK/4096000)-1;  // 2.048 MHz
+       //asm("nop \n nop");
 
        /* find the correct PHY/DMA/MAC combination */
-       MU = P2001_MU;  // MU for all PHYs is only in EU0
        printf("Searching for P2001 NICs...\n");
+       cur_phy = -1;
        for (cur_channel=0; cur_channel<4; cur_channel++) {
-               switch(cur_channel) {
-                       case 0:
-                               EU = P2001_EU0;
-                               cur_phy = 0;
-                               break;
-                       case 1:
-                               EU = P2001_EU1;
-                               cur_phy = 1;
-                               break;
-                       case 2:
-                               EU = P2001_EU2;
-                               cur_phy = 2;
-                               break;
-                       case 3:
-                               EU = P2001_EU3;
-                               cur_phy = 3;
+               EU = P2001_EU(cur_channel);
+#ifdef P2001_ETH_PHY_AUTO_DETECT
+               while (++cur_phy < 16) {
+                       //printf("phy detect %d\n", cur_phy);
+                       if (p2001_eth_mdio_read(cur_phy, MII_BMSR) != 0)
                                break;
                }
+               if (cur_phy == 16) {
+                       printf("no more MII PHYs found\n");
+                       break;
+               }
+#else
+               cur_phy = cur_channel;          /* for MAZBR LPEC2001 */
+//             cur_phy = cur_channel | 4;      /* for ELMEG D@VOS */
+               if (p2001_eth_mdio_read(cur_phy, MII_BMSR) == 0) {
+                       printf("no more MII PHYs found\n");
+                       break;
+               }
+#endif
 
                /* first a non destructive test for initial value RMAC_TLEN=1518 */
                board_found = (EU->RMAC_TLEN == 1518);
@@ -507,7 +561,6 @@ static int p2001_eth_probe(struct dev *dev, unsigned short *probe_addrs __unused
 
                                /* Report the ISA pnp id of the board */
                                dev->devid.vendor_id = htons(GENERIC_ISAPNP_VENDOR);
-                               dev->devid.vendor_id = htons(0x1234);
                                return 1;
                        }
                }