Disable uneeded assert.
[bertos.git] / bertos / cpu / arm / drv / eth_at91.c
index fbf3542b1eb35a99f65e1d727975dadb46103592..72d2a250169483070e831de748a4fbf6c16752c6 100644 (file)
 
 #include "eth_at91.h"
 
-/*
- * MAC address configuration (please change this in your project!).
- *
- * TODO: make this paramater user-configurable from the Wizard.
- */
-uint8_t mac_addr[] = { 0x11, 0x22, 0x33, 0x44, 0x55, 0x66 };
+#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))
 
-static volatile BufDescriptor tx_buf_tab[EMAC_TX_DESCRIPTORS];
+/* Silent Doxygen bug... */
+#ifndef __doxygen__
 /*
  * NOTE: this buffer should be declared as 'volatile' because it is read by the
  * hardware. However, this is accessed only via memcpy() that should guarantee
  * coherency when copying from/to buffers.
  */
 static uint8_t tx_buf[EMAC_TX_BUFFERS * EMAC_TX_BUFSIZ] ALIGNED(8);
-static int tx_buf_idx = 0;
+static volatile BufDescriptor tx_buf_tab[EMAC_TX_DESCRIPTORS] ALIGNED(8);
 
-static volatile BufDescriptor rx_buf_tab[EMAC_RX_DESCRIPTORS];
 /*
  * NOTE: this buffer should be declared as 'volatile' because it is wrote by
  * the hardware. However, this is accessed only via memcpy() that should
  * guarantee coherency when copying from/to buffers.
  */
 static uint8_t rx_buf[EMAC_RX_BUFFERS * EMAC_RX_BUFSIZ] ALIGNED(8);
-static int rx_buf_idx = 0;
+static volatile BufDescriptor rx_buf_tab[EMAC_RX_DESCRIPTORS] ALIGNED(8);
+#endif
+
+static int tx_buf_idx;
+static int tx_buf_offset;
+static int rx_buf_idx;
 
 static Event recv_wait, send_wait;
 
@@ -99,13 +100,15 @@ static DECLARE_ISR(emac_irqHandler)
        /* Receiver interrupt */
        if ((isr & EMAC_RX_INTS))
        {
-               event_do(&recv_wait);
+               if (isr & BV(EMAC_RCOMP))
+                       event_do(&recv_wait);
                EMAC_RSR = EMAC_RX_INTS;
        }
        /* Transmitter interrupt */
        if (isr & EMAC_TX_INTS)
        {
-               event_do(&send_wait);
+               if (isr & BV(EMAC_TCOMP))
+                       event_do(&send_wait);
                EMAC_TSR = EMAC_TX_INTS;
        }
        AIC_EOICR = 0;
@@ -118,7 +121,7 @@ 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 reg)
 {
        // PHY read command.
        EMAC_MAN = EMAC_SOF | EMAC_RW_READ | (NIC_PHY_ADDR << EMAC_PHYA_SHIFT)
@@ -138,7 +141,7 @@ static uint16_t phy_hw_read(reg8_t reg)
  * \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 reg, uint16_t val)
 {
        // PHY write command.
        EMAC_MAN = EMAC_SOF | EMAC_RW_WRITE | (NIC_PHY_ADDR << EMAC_PHYA_SHIFT)
@@ -149,79 +152,109 @@ static void phy_hw_write(reg8_t reg, uint16_t val)
                cpu_relax();
 }
 
-static int emac_reset(void)
+INLINE void phy_manageEnable(bool en)
 {
-       uint16_t phy_cr;
-
-       // Enable devices
-       PMC_PCER = BV(PIOA_ID);
-       PMC_PCER = BV(PIOB_ID);
-       PMC_PCER = BV(EMAC_ID);
-
-       // Disable RMII and TESTMODE by disabling pull-ups.
-       PIOB_PUDR = BV(PHY_COL_RMII_BIT) | BV(PHY_RXDV_TESTMODE_BIT);
-
-       // Disable PHY power down.
-       PIOB_PER  = BV(PHY_PWRDN_BIT);
-       PIOB_OER  = BV(PHY_PWRDN_BIT);
-       PIOB_CODR = BV(PHY_PWRDN_BIT);
+               if (en)
+               {
+                       /* Enable management port. */
+                       EMAC_NCR |= BV(EMAC_MPE);
+                       EMAC_NCFGR |= EMAC_CLK_HCLK_32;
+               }
+               else
+               {
+                       /* Disable management port */
+                       EMAC_NCR &= ~BV(EMAC_MPE);
+               }
+}
 
-       // Toggle external hardware reset pin.
+INLINE void phy_resetPulse(void)
+{
+       /* Toggle external hardware reset pin. */
        RSTC_MR = RSTC_KEY | (1 << RSTC_ERSTL_SHIFT) | BV(RSTC_URSTEN);
        RSTC_CR = RSTC_KEY | BV(RSTC_EXTRST);
 
        while ((RSTC_SR & BV(RSTC_NRSTL)) == 0)
                cpu_relax();
+}
 
-       // Configure MII port.
+INLINE void phy_pinThreeState(void)
+{
+       PIOB_PUDR = PHY_MII_PINS;
+       PIOB_ODR = PHY_MII_PINS;
+       PIOB_PER = PHY_MII_PINS;
+}
+
+INLINE void phy_pinGpio(void)
+{
+       PIOB_PUDR = PHY_MII_PINS;
+       PIOB_OWER = PHY_MII_PINS;
+       PIOB_OER  = PHY_MII_PINS;
+       PIOB_PER  = PHY_MII_PINS;
+}
+
+INLINE void phy_pinMac(void)
+{
+       PIOB_ODR = PHY_MII_PINS;
+       PIOB_OWDR = PHY_MII_PINS;
        PIOB_ASR = PHY_MII_PINS;
        PIOB_BSR = 0;
+       PIOB_PUDR = PHY_MII_PINS;
        PIOB_PDR = PHY_MII_PINS;
+}
 
-       // Enable receive and transmit clocks.
-       EMAC_USRIO = BV(EMAC_CLKEN);
+INLINE void phy_pinSet(uint32_t state)
+{
+       PIOB_ODSR = state;
+}
+
+#define AUTONEGOTIATION_TIMEOUT 5000
 
-       // Enable management port.
-       EMAC_NCR |= BV(EMAC_MPE);
-       EMAC_NCFGR |= EMAC_CLK_HCLK_32;
+static void emac_reset(void)
+{
+       /* Enable devices */
+       PMC_PCER = BV(EMAC_ID);
 
-       // Set local MAC address.
+       /* Enable receive and transmit clocks. */
+       EMAC_USRIO = BV(EMAC_CLKEN);
+
+       /* 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];
+       phy_manageEnable(true);
+
+       PHY_HW_INIT();
 
-       // Wait for PHY ready
-       timer_delay(255);
+       PHY_INIT();
+       phy_pinMac();
 
-       // Clear MII isolate.
-       phy_hw_read(NIC_PHY_BMCR);
-       phy_cr = phy_hw_read(NIC_PHY_BMCR);
+       /* Clear MII isolate. */
+       uint16_t 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);
+       uint32_t phy_id = phy_hw_read(NIC_PHY_ID1) << 16
+               | phy_hw_read(NIC_PHY_ID2);
+       //ASSERT((phy_id & 0xFFFFFFF0) == (NIC_PHY_ID & 0xFFFFFFF0));
+       LOG_INFO("PHY ID %#08lx\n", phy_id);
 
-       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 (;;)
+       ticks_t start = timer_clock();
+       /* Wait for auto negotiation completed. */
+       while (1)
        {
                if (phy_hw_read(NIC_PHY_BMSR) & NIC_PHY_BMSR_ANCOMPL)
                        break;
                cpu_relax();
+               if (timer_clock() - start > ms_to_ticks(AUTONEGOTIATION_TIMEOUT))
+               {
+                       LOG_ERR("Autonegotiation timeout\n");
+                       break;
+               }
        }
-
-       // Disable management port.
-       EMAC_NCR &= ~BV(EMAC_MPE);
-
-       return 0;
 }
 
-static int emac_start(void)
+static void emac_start(void)
 {
        uint32_t addr;
        int i;
@@ -253,43 +286,67 @@ static int emac_start(void)
 
        /* Enable receiver, transmitter and statistics. */
        EMAC_NCR |= BV(EMAC_TE) | BV(EMAC_RE) | BV(EMAC_WESTAT);
-
-       return 0;
 }
 
-ssize_t eth_send(const uint8_t *buf, size_t len)
- {
+ssize_t eth_putFrame(const uint8_t *buf, size_t len)
+{
        size_t wr_len;
-       int idx;
 
        if (UNLIKELY(!len))
                return -1;
        ASSERT(len <= sizeof(tx_buf));
 
         /* Check if the transmit buffer is available */
-       idx = tx_buf_idx;
-        while (!(tx_buf_tab[idx].stat & TXS_USED))
-                event_wait(&send_wait);
+       while (!(tx_buf_tab[tx_buf_idx].stat & TXS_USED))
+               event_wait(&send_wait);
 
+        /* Copy the data into the buffer and prepare descriptor */
+        wr_len = MIN(len, (size_t)EMAC_TX_BUFSIZ - tx_buf_offset);
+        memcpy((uint8_t *)tx_buf_tab[tx_buf_idx].addr + tx_buf_offset,
+                               buf, wr_len);
+       tx_buf_offset += wr_len;
+
+       return wr_len;
+}
+
+void eth_sendFrame(void)
+{
+        tx_buf_tab[tx_buf_idx].stat = (tx_buf_offset & TXS_LENGTH_FRAME) |
+                       TXS_LAST_BUFF |
+                       ((tx_buf_idx == EMAC_TX_DESCRIPTORS - 1) ?  TXS_WRAP : 0);
+       EMAC_NCR |= BV(EMAC_TSTART);
+
+       tx_buf_offset = 0;
        if (++tx_buf_idx >= EMAC_TX_DESCRIPTORS)
                tx_buf_idx = 0;
+}
 
-        /* Copy the data into the buffer and prepare descriptor */
-        wr_len = MIN(len, (size_t)EMAC_TX_BUFSIZ);
-        memcpy((uint8_t *)tx_buf_tab[idx].addr, buf, wr_len);
-        tx_buf_tab[idx].stat = (wr_len & RXS_LENGTH_FRAME) |
-                       ((idx == EMAC_TX_DESCRIPTORS - 1) ?
-                               TXS_WRAP : 0) |
-                       TXS_LAST_BUFF;
-       EMAC_NCR |= BV(EMAC_TSTART);
+ssize_t eth_send(const uint8_t *buf, size_t len)
+ {
+       if (UNLIKELY(!len))
+               return -1;
 
-       return wr_len;
+       len = eth_putFrame(buf, len);
+       eth_sendFrame();
+
+       return len;
+}
+
+static void eth_buf_realign(int idx)
+{
+       /* Empty buffer found. Realign. */
+       do {
+               rx_buf_tab[rx_buf_idx].addr &= ~RXBUF_OWNERSHIP;
+               if (++rx_buf_idx >= EMAC_RX_BUFFERS)
+                       rx_buf_idx = 0;
+       } while (idx != rx_buf_idx);
 }
 
-static size_t eth_getFrameLen(void)
+static size_t __eth_getFrameLen(void)
 {
        int idx, n = EMAC_RX_BUFFERS;
 
+skip:
        /* Skip empty buffers */
        while ((n > 0) && !(rx_buf_tab[rx_buf_idx].addr & RXBUF_OWNERSHIP))
        {
@@ -311,57 +368,63 @@ static size_t eth_getFrameLen(void)
                        rx_buf_idx = 0;
                n--;
        }
-       if (UNLIKELY(!(rx_buf_tab[rx_buf_idx].stat & RXS_SOF)))
+       if (UNLIKELY(!n))
        {
                LOG_INFO("no SOF found\n");
                return 0;
        }
        /* Search end of frame to evaluate the total frame size */
        idx = rx_buf_idx;
-       while ((n > 0) && (rx_buf_tab[idx].addr & RXBUF_OWNERSHIP))
+restart:
+       while (n > 0)
        {
+               if (UNLIKELY(!(rx_buf_tab[idx].addr & RXBUF_OWNERSHIP)))
+               {
+                       /* Empty buffer found. Realign. */
+                       eth_buf_realign(idx);
+                       goto skip;
+               }
+               if (rx_buf_tab[idx].stat & RXS_EOF)
+                       return rx_buf_tab[idx].stat & RXS_LENGTH_FRAME;
                if (UNLIKELY((idx != rx_buf_idx) &&
                                (rx_buf_tab[idx].stat & RXS_SOF)))
                {
                        /* Another start of frame found. Realign. */
-                       do {
-                               rx_buf_tab[rx_buf_idx].addr &= ~RXBUF_OWNERSHIP;
-                               if (++rx_buf_idx >= EMAC_RX_BUFFERS)
-                                       rx_buf_idx = 0;
-                       } while (idx != rx_buf_idx);
+                       eth_buf_realign(idx);
+                       goto restart;
                }
-               if (rx_buf_tab[idx].stat & RXS_EOF)
-                       return rx_buf_tab[idx].stat & RXS_LENGTH_FRAME;
                if (++idx >= EMAC_RX_BUFFERS)
                        idx = 0;
                n--;
        }
-
        LOG_INFO("no EOF found\n");
        return 0;
 }
 
-ssize_t eth_recv(uint8_t *buf, size_t len)
+size_t eth_getFrameLen(void)
 {
-       uint8_t *addr;
-       size_t rd_len = 0;
-
-       if (UNLIKELY(!len))
-               return -1;
-       ASSERT(len <= sizeof(tx_buf));
+       size_t len;
 
        /* Check if there is at least one available frame in the buffer */
        while (1)
        {
-               size_t frame_len = MIN(len, eth_getFrameLen());
-               if (frame_len)
-               {
-                       len = frame_len;
+               len = __eth_getFrameLen();
+               if (LIKELY(len))
                        break;
-               }
                /* Wait for RX interrupt */
                event_wait(&recv_wait);
        }
+       return len;
+}
+
+ssize_t eth_getFrame(uint8_t *buf, size_t len)
+{
+       uint8_t *addr;
+       size_t rd_len = 0;
+
+       if (UNLIKELY(!len))
+               return -1;
+       ASSERT(len <= sizeof(rx_buf));
 
        /* Copy data from the RX buffer */
        addr = (uint8_t *)(rx_buf_tab[rx_buf_idx].addr & BUF_ADDRMASK);
@@ -383,14 +446,27 @@ ssize_t eth_recv(uint8_t *buf, size_t len)
                        rd_len += EMAC_RX_BUFSIZ;
                else
                        rd_len += len - rd_len;
-               ASSERT(rx_buf_tab[rx_buf_idx].addr & RXBUF_OWNERSHIP);
+               if (UNLIKELY(!(rx_buf_tab[rx_buf_idx].addr & RXBUF_OWNERSHIP)))
+               {
+                       LOG_INFO("bad frame found\n");
+                       return 0;
+               }
                rx_buf_tab[rx_buf_idx].addr &= ~RXBUF_OWNERSHIP;
                if (++rx_buf_idx >= EMAC_RX_DESCRIPTORS)
                        rx_buf_idx = 0;
        }
+
        return rd_len;
 }
 
+ssize_t eth_recv(uint8_t *buf, size_t len)
+{
+       if (UNLIKELY(!len))
+               return -1;
+       len = MIN(len, eth_getFrameLen());
+       return len ? eth_getFrame(buf, len) : 0;
+}
+
 int eth_init()
 {
        cpu_flags_t flags;