eth_at91.c
Go to the documentation of this file.
00001
00039 #include "cfg/cfg_eth.h"
00040
00041 #define LOG_LEVEL  ETH_LOG_LEVEL
00042 #define LOG_FORMAT ETH_LOG_FORMAT
00043 
00044 #include <cfg/log.h>
00045
00046 #include <cfg/debug.h>
00047 #include <cfg/log.h>
00048 #include <cfg/macros.h>
00049 #include <cfg/compiler.h>
00050
00051 #include <io/at91sam7.h>
00052 #include <io/arm.h>
00053
00054 #include <cpu/power.h>
00055 #include <cpu/types.h>
00056 #include <cpu/irq.h>
00057
00058 #include <drv/timer.h>
00059 #include <drv/eth.h>
00060
00061 #include <mware/event.h>
00062
00063 #include <string.h>
00064
00065 #include "eth_at91.h"
00066
00067 #define EMAC_RX_INTS    (BV(EMAC_RCOMP) | BV(EMAC_ROVR) | BV(EMAC_RXUBR))
00068 #define EMAC_TX_INTS    (BV(EMAC_TCOMP) | BV(EMAC_TXUBR) | BV(EMAC_RLEX))
00069 
00070 /* Silent Doxygen bug... */
00071 #ifndef __doxygen__
00072 /*
00073  * NOTE: this buffer should be declared as 'volatile' because it is read by the
00074  * hardware. However, this is accessed only via memcpy() that should guarantee
00075  * coherency when copying from/to buffers.
00076  */
00077 static uint8_t tx_buf[EMAC_TX_BUFFERS * EMAC_TX_BUFSIZ] ALIGNED(8);
00078 static volatile BufDescriptor tx_buf_tab[EMAC_TX_DESCRIPTORS] ALIGNED(8);
00079
00080 /*
00081  * NOTE: this buffer should be declared as 'volatile' because it is wrote by
00082  * the hardware. However, this is accessed only via memcpy() that should
00083  * guarantee coherency when copying from/to buffers.
00084  */
00085 static uint8_t rx_buf[EMAC_RX_BUFFERS * EMAC_RX_BUFSIZ] ALIGNED(8);
00086 static volatile BufDescriptor rx_buf_tab[EMAC_RX_DESCRIPTORS] ALIGNED(8);
00087 #endif
00088 
00089 static int tx_buf_idx;
00090 static int tx_buf_offset;
00091 static int rx_buf_idx;
00092
00093 static Event recv_wait, send_wait;
00094
00095 static DECLARE_ISR(emac_irqHandler)
00096 {
00097     /* Read interrupt status and disable interrupts. */
00098     uint32_t isr = EMAC_ISR;
00099
00100     /* Receiver interrupt */
00101     if ((isr & EMAC_RX_INTS))
00102     {
00103         if (isr & BV(EMAC_RCOMP))
00104             event_do(&recv_wait);
00105         EMAC_RSR = EMAC_RX_INTS;
00106     }
00107     /* Transmitter interrupt */
00108     if (isr & EMAC_TX_INTS)
00109     {
00110         if (isr & BV(EMAC_TCOMP))
00111             event_do(&send_wait);
00112         EMAC_TSR = EMAC_TX_INTS;
00113     }
00114     AIC_EOICR = 0;
00115 }
00116
00117 /*
00118  * \brief Read contents of PHY register.
00119  *
00120  * \param reg PHY register number.
00121  *
00122  * \return Contents of the specified register.
00123  */
00124 static uint16_t phy_hw_read(uint8_t reg)
00125 {
00126     // PHY read command.
00127     EMAC_MAN = EMAC_SOF | EMAC_RW_READ | (NIC_PHY_ADDR << EMAC_PHYA_SHIFT)
00128             | ((reg  << EMAC_REGA_SHIFT) & EMAC_REGA) | EMAC_CODE;
00129
00130     // Wait until PHY logic completed.
00131     while (!(EMAC_NSR & BV(EMAC_IDLE)))
00132         cpu_relax();
00133
00134     // Get data from PHY maintenance register.
00135     return (uint16_t)(EMAC_MAN & EMAC_DATA);
00136 }
00137
00138 /*
00139  * \brief Write value to PHY register.
00140  *
00141  * \param reg PHY register number.
00142  * \param val Value to write.
00143  */
00144 static void phy_hw_write(uint8_t reg, uint16_t val)
00145 {
00146     // PHY write command.
00147     EMAC_MAN = EMAC_SOF | EMAC_RW_WRITE | (NIC_PHY_ADDR << EMAC_PHYA_SHIFT)
00148             | ((reg  << EMAC_REGA_SHIFT) & EMAC_REGA) | EMAC_CODE | val;
00149
00150     // Wait until PHY logic completed.
00151     while (!(EMAC_NSR & BV(EMAC_IDLE)))
00152         cpu_relax();
00153 }
00154
00155 INLINE void phy_manageEnable(bool en)
00156 {
00157         if (en)
00158         {
00159             /* Enable management port. */
00160             EMAC_NCR |= BV(EMAC_MPE);
00161             EMAC_NCFGR |= EMAC_CLK_HCLK_32;
00162         }
00163         else
00164         {
00165             /* Disable management port */
00166             EMAC_NCR &= ~BV(EMAC_MPE);
00167         }
00168 }
00169
00170 INLINE void phy_resetPulse(void)
00171 {
00172     /* Toggle external hardware reset pin. */
00173     RSTC_MR = RSTC_KEY | (1 << RSTC_ERSTL_SHIFT) | BV(RSTC_URSTEN);
00174     RSTC_CR = RSTC_KEY | BV(RSTC_EXTRST);
00175
00176     while ((RSTC_SR & BV(RSTC_NRSTL)) == 0)
00177         cpu_relax();
00178 }
00179
00180 INLINE void phy_pinThreeState(void)
00181 {
00182     PIOB_PUDR = PHY_MII_PINS;
00183     PIOB_ODR = PHY_MII_PINS;
00184     PIOB_PER = PHY_MII_PINS;
00185 }
00186
00187 INLINE void phy_pinGpio(void)
00188 {
00189     PIOB_PUDR = PHY_MII_PINS;
00190     PIOB_OWER = PHY_MII_PINS;
00191     PIOB_OER  = PHY_MII_PINS;
00192     PIOB_PER  = PHY_MII_PINS;
00193 }
00194
00195 INLINE void phy_pinMac(void)
00196 {
00197     PIOB_ODR = PHY_MII_PINS;
00198     PIOB_OWDR = PHY_MII_PINS;
00199     PIOB_ASR = PHY_MII_PINS;
00200     PIOB_BSR = 0;
00201     PIOB_PUDR = PHY_MII_PINS;
00202     PIOB_PDR = PHY_MII_PINS;
00203 }
00204
00205 INLINE void phy_pinSet(uint32_t state)
00206 {
00207     PIOB_ODSR = state;
00208 }
00209
00210 #define AUTONEGOTIATION_TIMEOUT 5000
00211 
00212 static void emac_reset(void)
00213 {
00214     /* Enable devices */
00215     PMC_PCER = BV(EMAC_ID);
00216
00217     /* Enable receive and transmit clocks. */
00218     EMAC_USRIO = BV(EMAC_CLKEN);
00219
00220     /* Set local MAC address. */
00221     EMAC_SA1L = (mac_addr[3] << 24) | (mac_addr[2] << 16) |
00222                 (mac_addr[1] << 8) | mac_addr[0];
00223     EMAC_SA1H = (mac_addr[5] << 8) | mac_addr[4];
00224     phy_manageEnable(true);
00225
00226     PHY_HW_INIT();
00227
00228     PHY_INIT();
00229     phy_pinMac();
00230
00231     /* Clear MII isolate. */
00232     uint16_t phy_cr = phy_hw_read(NIC_PHY_BMCR);
00233
00234     phy_cr &= ~NIC_PHY_BMCR_ISOLATE;
00235     phy_hw_write(NIC_PHY_BMCR, phy_cr);
00236
00237     uint32_t phy_id = phy_hw_read(NIC_PHY_ID1) << 16
00238         | phy_hw_read(NIC_PHY_ID2);
00239     //ASSERT((phy_id & 0xFFFFFFF0) == (NIC_PHY_ID & 0xFFFFFFF0));
00240     LOG_INFO("PHY ID %#08lx\n", phy_id);
00241
00242     ticks_t start = timer_clock();
00243     /* Wait for auto negotiation completed. */
00244     while (1)
00245     {
00246         if (phy_hw_read(NIC_PHY_BMSR) & NIC_PHY_BMSR_ANCOMPL)
00247             break;
00248         cpu_relax();
00249         if (timer_clock() - start > ms_to_ticks(AUTONEGOTIATION_TIMEOUT))
00250         {
00251             LOG_ERR("Autonegotiation timeout\n");
00252             break;
00253         }
00254     }
00255 }
00256
00257 static void emac_start(void)
00258 {
00259     uint32_t addr;
00260     int i;
00261
00262     for (i = 0; i < EMAC_RX_DESCRIPTORS; i++)
00263     {
00264         addr = (uint32_t)(rx_buf + (i * EMAC_RX_BUFSIZ));
00265         rx_buf_tab[i].addr = addr & BUF_ADDRMASK;
00266     }
00267     rx_buf_tab[EMAC_RX_DESCRIPTORS - 1].addr |= RXBUF_WRAP;
00268
00269     for (i = 0; i < EMAC_TX_DESCRIPTORS; i++)
00270     {
00271         addr = (uint32_t)(tx_buf + (i * EMAC_TX_BUFSIZ));
00272         tx_buf_tab[i].addr = addr & BUF_ADDRMASK;
00273         tx_buf_tab[i].stat = TXS_USED;
00274     }
00275     tx_buf_tab[EMAC_TX_DESCRIPTORS - 1].stat = TXS_USED | TXS_WRAP;
00276
00277     /* Tell the EMAC where to find the descriptors. */
00278     EMAC_RBQP = (uint32_t)rx_buf_tab;
00279     EMAC_TBQP = (uint32_t)tx_buf_tab;
00280
00281     /* Clear receiver status. */
00282     EMAC_RSR = BV(EMAC_OVR) | BV(EMAC_REC) | BV(EMAC_BNA);
00283
00284     /* Copy all frames and discard FCS. */
00285     EMAC_NCFGR |= BV(EMAC_CAF) | BV(EMAC_DRFCS);
00286
00287     /* Enable receiver, transmitter and statistics. */
00288     EMAC_NCR |= BV(EMAC_TE) | BV(EMAC_RE) | BV(EMAC_WESTAT);
00289 }
00290
00291 ssize_t eth_putFrame(const uint8_t *buf, size_t len)
00292 {
00293     size_t wr_len;
00294
00295     if (UNLIKELY(!len))
00296         return -1;
00297     ASSERT(len <= sizeof(tx_buf));
00298
00299         /* Check if the transmit buffer is available */
00300     while (!(tx_buf_tab[tx_buf_idx].stat & TXS_USED))
00301         event_wait(&send_wait);
00302
00303         /* Copy the data into the buffer and prepare descriptor */
00304         wr_len = MIN(len, (size_t)EMAC_TX_BUFSIZ - tx_buf_offset);
00305         memcpy((uint8_t *)tx_buf_tab[tx_buf_idx].addr + tx_buf_offset,
00306                 buf, wr_len);
00307     tx_buf_offset += wr_len;
00308
00309     return wr_len;
00310 }
00311
00312 void eth_sendFrame(void)
00313 {
00314         tx_buf_tab[tx_buf_idx].stat = (tx_buf_offset & TXS_LENGTH_FRAME) |
00315             TXS_LAST_BUFF |
00316             ((tx_buf_idx == EMAC_TX_DESCRIPTORS - 1) ?  TXS_WRAP : 0);
00317     EMAC_NCR |= BV(EMAC_TSTART);
00318
00319     tx_buf_offset = 0;
00320     if (++tx_buf_idx >= EMAC_TX_DESCRIPTORS)
00321         tx_buf_idx = 0;
00322 }
00323
00324 ssize_t eth_send(const uint8_t *buf, size_t len)
00325  {
00326     if (UNLIKELY(!len))
00327         return -1;
00328
00329     len = eth_putFrame(buf, len);
00330     eth_sendFrame();
00331
00332     return len;
00333 }
00334
00335 static void eth_buf_realign(int idx)
00336 {
00337     /* Empty buffer found. Realign. */
00338     do {
00339         rx_buf_tab[rx_buf_idx].addr &= ~RXBUF_OWNERSHIP;
00340         if (++rx_buf_idx >= EMAC_RX_BUFFERS)
00341             rx_buf_idx = 0;
00342     } while (idx != rx_buf_idx);
00343 }
00344
00345 static size_t __eth_getFrameLen(void)
00346 {
00347     int idx, n = EMAC_RX_BUFFERS;
00348
00349 skip:
00350     /* Skip empty buffers */
00351     while ((n > 0) && !(rx_buf_tab[rx_buf_idx].addr & RXBUF_OWNERSHIP))
00352     {
00353         if (++rx_buf_idx >= EMAC_RX_BUFFERS)
00354             rx_buf_idx = 0;
00355         n--;
00356     }
00357     if (UNLIKELY(!n))
00358     {
00359         LOG_INFO("no frame found\n");
00360         return 0;
00361     }
00362     /* Search the start of frame and cleanup fragments */
00363     while ((n > 0) && (rx_buf_tab[rx_buf_idx].addr & RXBUF_OWNERSHIP) &&
00364             !(rx_buf_tab[rx_buf_idx].stat & RXS_SOF))
00365     {
00366         rx_buf_tab[rx_buf_idx].addr &= ~RXBUF_OWNERSHIP;
00367         if (++rx_buf_idx >= EMAC_RX_BUFFERS)
00368             rx_buf_idx = 0;
00369         n--;
00370     }
00371     if (UNLIKELY(!n))
00372     {
00373         LOG_INFO("no SOF found\n");
00374         return 0;
00375     }
00376     /* Search end of frame to evaluate the total frame size */
00377     idx = rx_buf_idx;
00378 restart:
00379     while (n > 0)
00380     {
00381         if (UNLIKELY(!(rx_buf_tab[idx].addr & RXBUF_OWNERSHIP)))
00382         {
00383             /* Empty buffer found. Realign. */
00384             eth_buf_realign(idx);
00385             goto skip;
00386         }
00387         if (rx_buf_tab[idx].stat & RXS_EOF)
00388             return rx_buf_tab[idx].stat & RXS_LENGTH_FRAME;
00389         if (UNLIKELY((idx != rx_buf_idx) &&
00390                 (rx_buf_tab[idx].stat & RXS_SOF)))
00391         {
00392             /* Another start of frame found. Realign. */
00393             eth_buf_realign(idx);
00394             goto restart;
00395         }
00396         if (++idx >= EMAC_RX_BUFFERS)
00397             idx = 0;
00398         n--;
00399     }
00400     LOG_INFO("no EOF found\n");
00401     return 0;
00402 }
00403
00404 size_t eth_getFrameLen(void)
00405 {
00406     size_t len;
00407
00408     /* Check if there is at least one available frame in the buffer */
00409     while (1)
00410     {
00411         len = __eth_getFrameLen();
00412         if (LIKELY(len))
00413             break;
00414         /* Wait for RX interrupt */
00415         event_wait(&recv_wait);
00416     }
00417     return len;
00418 }
00419
00420 ssize_t eth_getFrame(uint8_t *buf, size_t len)
00421 {
00422     uint8_t *addr;
00423     size_t rd_len = 0;
00424
00425     if (UNLIKELY(!len))
00426         return -1;
00427     ASSERT(len <= sizeof(rx_buf));
00428
00429     /* Copy data from the RX buffer */
00430     addr = (uint8_t *)(rx_buf_tab[rx_buf_idx].addr & BUF_ADDRMASK);
00431     if (addr + len > &rx_buf[countof(rx_buf)])
00432     {
00433         size_t count = &rx_buf[countof(rx_buf)] - addr;
00434
00435         memcpy(buf, addr, count);
00436         memcpy(buf + count, rx_buf, len - count);
00437     }
00438     else
00439     {
00440         memcpy(buf, addr, len);
00441     }
00442     /* Update descriptors */
00443     while (rd_len < len)
00444     {
00445         if (len - rd_len >= EMAC_RX_BUFSIZ)
00446             rd_len += EMAC_RX_BUFSIZ;
00447         else
00448             rd_len += len - rd_len;
00449         if (UNLIKELY(!(rx_buf_tab[rx_buf_idx].addr & RXBUF_OWNERSHIP)))
00450         {
00451             LOG_INFO("bad frame found\n");
00452             return 0;
00453         }
00454         rx_buf_tab[rx_buf_idx].addr &= ~RXBUF_OWNERSHIP;
00455         if (++rx_buf_idx >= EMAC_RX_DESCRIPTORS)
00456             rx_buf_idx = 0;
00457     }
00458
00459     return rd_len;
00460 }
00461
00462 ssize_t eth_recv(uint8_t *buf, size_t len)
00463 {
00464     if (UNLIKELY(!len))
00465         return -1;
00466     len = MIN(len, eth_getFrameLen());
00467     return len ? eth_getFrame(buf, len) : 0;
00468 }
00469
00470 int eth_init()
00471 {
00472     cpu_flags_t flags;
00473
00474     emac_reset();
00475     emac_start();
00476
00477     event_initGeneric(&recv_wait);
00478     event_initGeneric(&send_wait);
00479
00480     // Register interrupt vector
00481     IRQ_SAVE_DISABLE(flags);
00482
00483     /* Disable all emac interrupts */
00484     EMAC_IDR = 0xFFFFFFFF;
00485
00486     /* Set the vector. */
00487     AIC_SVR(EMAC_ID) = emac_irqHandler;
00488     /* Initialize to edge triggered with defined priority. */
00489     AIC_SMR(EMAC_ID) = AIC_SRCTYPE_INT_EDGE_TRIGGERED | 6;
00490     /* Clear pending interrupt */
00491     AIC_ICCR = BV(EMAC_ID);
00492     /* Enable the system IRQ */
00493     AIC_IECR = BV(EMAC_ID);
00494
00495     /* Enable interrupts */
00496     EMAC_IER = EMAC_RX_INTS | EMAC_TX_INTS;
00497
00498     IRQ_RESTORE(flags);
00499
00500     return 0;
00501 }