Make MAC address configurable using hw files.
[bertos.git] / bertos / cpu / cortex-m3 / drv / eth_sam3.c
index 889a5c1b4ebdc715f58b8ac9930d637029b63532..f83dba7227d71d4ffe551045eb23d82fd2ab6462 100644 (file)
 #define EMAC_RX_INTS   (BV(EMAC_RCOMP) | BV(EMAC_ROVR) | BV(EMAC_RXUBR))
 #define EMAC_TX_INTS   (BV(EMAC_TCOMP) | BV(EMAC_TXUBR) | BV(EMAC_RLEX))
 
-/*
- * MAC address configuration (please change this in your project!).
- *
- * TODO: make this paramater user-configurable from the Wizard.
- */
-const uint8_t mac_addr[] = { 0x00, 0x23, 0x54, 0x6a, 0x77, 0x55 };
-
 /* Silent Doxygen bug... */
 #ifndef __doxygen__
 /*
@@ -109,12 +102,9 @@ static DECLARE_ISR(emac_irqHandler)
        /* Read interrupt status and disable interrupts. */
        uint32_t isr = EMAC_ISR;
 
-       kprintf("irq: %x\n", isr);
-
        /* Receiver interrupt */
        if ((isr & EMAC_RX_INTS))
        {
-               kprintf("emac: rx %x\n", isr);
                if (isr & BV(EMAC_RCOMP))
                        event_do(&recv_wait);
                EMAC_RSR = EMAC_RX_INTS;
@@ -123,12 +113,7 @@ static DECLARE_ISR(emac_irqHandler)
        if (isr & EMAC_TX_INTS)
        {
                if (isr & BV(EMAC_TCOMP))
-               {
-                       kprintf("emac: tcomp\n");
                        event_do(&send_wait);
-               }
-               if (isr & BV(EMAC_RLEX))
-                       kprintf("emac: rlex\n");
                EMAC_TSR = EMAC_TX_INTS;
        }
        //AIC_EOICR = 0;
@@ -141,11 +126,13 @@ static DECLARE_ISR(emac_irqHandler)
  *
  * \return Contents of the specified register.
  */
-static uint16_t phy_hw_read(reg8_t reg)
+static uint16_t phy_hw_read(uint8_t phy_addr, reg8_t reg)
 {
        // PHY read command.
-       EMAC_MAN = EMAC_SOF | EMAC_RW_READ | (NIC_PHY_ADDR << EMAC_PHYA_SHIFT)
-                       | ((reg  << EMAC_REGA_SHIFT) & EMAC_REGA) | EMAC_CODE;
+       EMAC_MAN = EMAC_SOF | EMAC_RW_READ
+               | ((phy_addr << EMAC_PHYA_SHIFT) & EMAC_PHYA)
+               | ((reg  << EMAC_REGA_SHIFT) & EMAC_REGA)
+               | EMAC_CODE;
 
        // Wait until PHY logic completed.
        while (!(EMAC_NSR & BV(EMAC_IDLE)))
@@ -155,46 +142,105 @@ static uint16_t phy_hw_read(reg8_t reg)
        return (uint16_t)(EMAC_MAN & EMAC_DATA);
 }
 
+#if 0
 /*
  * \brief Write value to PHY register.
  *
  * \param reg PHY register number.
  * \param val Value to write.
  */
-static void phy_hw_write(reg8_t reg, uint16_t val)
+static void phy_hw_write(uint8_t phy_addr, reg8_t reg, uint16_t val)
 {
        // PHY write command.
-       EMAC_MAN = EMAC_SOF | EMAC_RW_WRITE | (NIC_PHY_ADDR << EMAC_PHYA_SHIFT)
-                       | ((reg  << EMAC_REGA_SHIFT) & EMAC_REGA) | EMAC_CODE | val;
+       EMAC_MAN = EMAC_SOF | EMAC_RW_WRITE
+               | ((phy_addr << EMAC_PHYA_SHIFT) & EMAC_PHYA)
+               | ((reg  << EMAC_REGA_SHIFT) & EMAC_REGA)
+               | EMAC_CODE | val;
 
        // Wait until PHY logic completed.
        while (!(EMAC_NSR & BV(EMAC_IDLE)))
                cpu_relax();
 }
+#endif
 
-static int emac_reset(void)
+/*
+ * Check link speed and duplex as negotiated by the PHY
+ * and configure CPU EMAC accordingly.
+ * Requires active PHY maintenance mode.
+ */
+static void emac_autoNegotiation(void)
 {
-       uint16_t phy_cr;
+       uint16_t reg;
+       time_t start;
+
+       // Wait for auto-negotation to complete
+       start = timer_clock();
+       do {
+               reg = phy_hw_read(NIC_PHY_ADDR, NIC_PHY_BMSR);
+               if (timer_clock() - start > 2000)
+               {
+                       kprintf("eth error: auto-negotiation timeout\n");
+                       return;
+               }
+       }
+       while (!(reg & NIC_PHY_BMSR_ANCOMPL));
+
+       reg = phy_hw_read(NIC_PHY_ADDR, NIC_PHY_ANLPAR);
+
+       if ((reg & NIC_PHY_ANLPAR_TX_FDX) || (reg & NIC_PHY_ANLPAR_TX_HDX))
+       {
+               LOG_INFO("eth: 100BASE-TX\n");
+               EMAC_NCFGR |= BV(EMAC_SPD);
+       }
+       else
+       {
+               LOG_INFO("eth: 10BASE-T\n");
+               EMAC_NCFGR &= ~BV(EMAC_SPD);
+       }
+
+       if ((reg & NIC_PHY_ANLPAR_TX_FDX) || (reg & NIC_PHY_ANLPAR_10_FDX))
+       {
+               LOG_INFO("eth: full duplex\n");
+               EMAC_NCFGR |= BV(EMAC_FD);
+       }
+       else
+       {
+               LOG_INFO("eth: half duplex\n");
+               EMAC_NCFGR &= ~BV(EMAC_FD);
+       }
+}
 
+
+static int emac_reset(void)
+{
+#if CPU_ARM_AT91
        // Enable devices
-       //PMC_PCER = BV(PIOA_ID);
-       //PMC_PCER = BV(PIOB_ID);
-       //PMC_PCER = BV(EMAC_ID);
-       // TOOD: Implement in sam7x
-       pmc_periphEnable(PIOA_ID);
-       pmc_periphEnable(PIOB_ID);
-       pmc_periphEnable(EMAC_ID);
+       PMC_PCER = BV(PIOA_ID);
+       PMC_PCER = BV(PIOB_ID);
+       PMC_PCER = BV(EMAC_ID);
 
-       // Disable TESTMODE
+       // Disable TESTMODE and RMII
        PIOB_PUDR = BV(PHY_RXDV_TESTMODE_BIT);
-#if CPU_ARM_AT91
-       // Disable RMII
        PIOB_PUDR = BV(PHY_COL_RMII_BIT);
 
        // Disable PHY power down.
        PIOB_PER  = BV(PHY_PWRDN_BIT);
        PIOB_OER  = BV(PHY_PWRDN_BIT);
        PIOB_CODR = BV(PHY_PWRDN_BIT);
+#else
+       pmc_periphEnable(PIOA_ID);
+       pmc_periphEnable(PIOB_ID);
+       pmc_periphEnable(PIOC_ID);
+       pmc_periphEnable(PIOD_ID);
+       pmc_periphEnable(EMAC_ID);
+
+       // Disable TESTMODE and RMII
+       PIOC_PUDR = BV(PHY_RXDV_TESTMODE_BIT);
+
+       // Disable PHY power down.
+       PIOD_PER  = BV(PHY_PWRDN_BIT);
+       PIOD_OER  = BV(PHY_PWRDN_BIT);
+       PIOD_CODR = BV(PHY_PWRDN_BIT);
 #endif
 
        // Toggle external hardware reset pin.
@@ -209,48 +255,30 @@ static int emac_reset(void)
        PIOB_ASR = PHY_MII_PINS;
        PIOB_BSR = 0;
        PIOB_PDR = PHY_MII_PINS;
+
        // Enable receive and transmit clocks.
        EMAC_USRIO = BV(EMAC_CLKEN);
 #else
-       PIO_PERIPH_SEL(PIOB_BASE, PHY_MII_PINS, PIO_PERIPH_A);
-       PIOB_PDR = PHY_MII_PINS;
+       PIO_PERIPH_SEL(PIOB_BASE, PHY_MII_PINS_PORTB, PIO_PERIPH_A);
+       PIOB_PDR = PHY_MII_PINS_PORTB;
+
+       PIO_PERIPH_SEL(PIOC_BASE, PHY_MII_PINS_PORTC, PIO_PERIPH_A);
+       PIOC_PDR = PHY_MII_PINS_PORTC;
+
        // Enable receive, transmit clocks and RMII mode.
        EMAC_USRIO = BV(EMAC_CLKEN) | BV(EMAC_RMII);
 #endif
 
        // Enable management port.
        EMAC_NCR |= BV(EMAC_MPE);
-       EMAC_NCFGR |= EMAC_CLK_HCLK_32;
+       EMAC_NCFGR |= EMAC_CLK_HCLK_64;
 
        // Set local MAC address.
        EMAC_SA1L = (mac_addr[3] << 24) | (mac_addr[2] << 16) |
                                (mac_addr[1] << 8) | mac_addr[0];
        EMAC_SA1H = (mac_addr[5] << 8) | mac_addr[4];
 
-       // Wait for PHY ready
-       timer_delay(255);
-
-       // Clear MII isolate.
-       phy_hw_read(NIC_PHY_BMCR);
-       phy_cr = phy_hw_read(NIC_PHY_BMCR);
-
-       phy_cr &= ~NIC_PHY_BMCR_ISOLATE;
-       phy_hw_write(NIC_PHY_BMCR, phy_cr);
-
-       phy_cr = phy_hw_read(NIC_PHY_BMCR);
-
-       LOG_INFO("%s: PHY ID %#04x %#04x\n",
-               __func__,
-               phy_hw_read(NIC_PHY_ID1), phy_hw_read(NIC_PHY_ID2));
-
-       // Wait for auto negotiation completed.
-       phy_hw_read(NIC_PHY_BMSR);
-       for (;;)
-       {
-               if (phy_hw_read(NIC_PHY_BMSR) & NIC_PHY_BMSR_ANCOMPL)
-                       break;
-               cpu_relax();
-       }
+       emac_autoNegotiation();
 
        // Disable management port.
        EMAC_NCR &= ~BV(EMAC_MPE);
@@ -258,6 +286,7 @@ static int emac_reset(void)
        return 0;
 }
 
+
 static int emac_start(void)
 {
        uint32_t addr;