#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__
/*
/* 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;
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;
*
* \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)))
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
+ PIOB_PUDR = BV(PHY_RXDV_TESTMODE_BIT);
#endif
// Toggle external hardware reset pin.
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;
+
// 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);
return 0;
}
+
static int emac_start(void)
{
uint32_t addr;