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(reg8_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(reg8_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 static int emac_reset(void)
00156 {
00157     uint16_t phy_cr;
00158 
00159     // Enable devices
00160     PMC_PCER = BV(PIOA_ID);
00161     PMC_PCER = BV(PIOB_ID);
00162     PMC_PCER = BV(EMAC_ID);
00163 
00164     // Disable RMII and TESTMODE by disabling pull-ups.
00165     PIOB_PUDR = BV(PHY_COL_RMII_BIT) | BV(PHY_RXDV_TESTMODE_BIT);
00166 
00167     // Disable PHY power down.
00168     PIOB_PER  = BV(PHY_PWRDN_BIT);
00169     PIOB_OER  = BV(PHY_PWRDN_BIT);
00170     PIOB_CODR = BV(PHY_PWRDN_BIT);
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     // Configure MII port.
00180     PIOB_ASR = PHY_MII_PINS;
00181     PIOB_BSR = 0;
00182     PIOB_PDR = PHY_MII_PINS;
00183 
00184     // Enable receive and transmit clocks.
00185     EMAC_USRIO = BV(EMAC_CLKEN);
00186 
00187     // Enable management port.
00188     EMAC_NCR |= BV(EMAC_MPE);
00189     EMAC_NCFGR |= EMAC_CLK_HCLK_32;
00190 
00191     // Set local MAC address.
00192     EMAC_SA1L = (mac_addr[3] << 24) | (mac_addr[2] << 16) |
00193                 (mac_addr[1] << 8) | mac_addr[0];
00194     EMAC_SA1H = (mac_addr[5] << 8) | mac_addr[4];
00195 
00196     // Wait for PHY ready
00197     timer_delay(255);
00198 
00199     // Clear MII isolate.
00200     phy_hw_read(NIC_PHY_BMCR);
00201     phy_cr = phy_hw_read(NIC_PHY_BMCR);
00202 
00203     phy_cr &= ~NIC_PHY_BMCR_ISOLATE;
00204     phy_hw_write(NIC_PHY_BMCR, phy_cr);
00205 
00206     phy_cr = phy_hw_read(NIC_PHY_BMCR);
00207 
00208     LOG_INFO("%s: PHY ID %#04x %#04x\n",
00209         __func__,
00210         phy_hw_read(NIC_PHY_ID1), phy_hw_read(NIC_PHY_ID2));
00211 
00212     // Wait for auto negotiation completed.
00213     phy_hw_read(NIC_PHY_BMSR);
00214     for (;;)
00215     {
00216         if (phy_hw_read(NIC_PHY_BMSR) & NIC_PHY_BMSR_ANCOMPL)
00217             break;
00218         cpu_relax();
00219     }
00220 
00221     // Disable management port.
00222     EMAC_NCR &= ~BV(EMAC_MPE);
00223 
00224     return 0;
00225 }
00226 
00227 static int emac_start(void)
00228 {
00229     uint32_t addr;
00230     int i;
00231 
00232     for (i = 0; i < EMAC_RX_DESCRIPTORS; i++)
00233     {
00234         addr = (uint32_t)(rx_buf + (i * EMAC_RX_BUFSIZ));
00235         rx_buf_tab[i].addr = addr & BUF_ADDRMASK;
00236     }
00237     rx_buf_tab[EMAC_RX_DESCRIPTORS - 1].addr |= RXBUF_WRAP;
00238 
00239     for (i = 0; i < EMAC_TX_DESCRIPTORS; i++)
00240     {
00241         addr = (uint32_t)(tx_buf + (i * EMAC_TX_BUFSIZ));
00242         tx_buf_tab[i].addr = addr & BUF_ADDRMASK;
00243         tx_buf_tab[i].stat = TXS_USED;
00244     }
00245     tx_buf_tab[EMAC_TX_DESCRIPTORS - 1].stat = TXS_USED | TXS_WRAP;
00246 
00247     /* Tell the EMAC where to find the descriptors. */
00248     EMAC_RBQP = (uint32_t)rx_buf_tab;
00249     EMAC_TBQP = (uint32_t)tx_buf_tab;
00250 
00251     /* Clear receiver status. */
00252     EMAC_RSR = BV(EMAC_OVR) | BV(EMAC_REC) | BV(EMAC_BNA);
00253 
00254     /* Copy all frames and discard FCS. */
00255     EMAC_NCFGR |= BV(EMAC_CAF) | BV(EMAC_DRFCS);
00256 
00257     /* Enable receiver, transmitter and statistics. */
00258     EMAC_NCR |= BV(EMAC_TE) | BV(EMAC_RE) | BV(EMAC_WESTAT);
00259 
00260     return 0;
00261 }
00262 
00263 ssize_t eth_putFrame(const uint8_t *buf, size_t len)
00264 {
00265     size_t wr_len;
00266 
00267     if (UNLIKELY(!len))
00268         return -1;
00269     ASSERT(len <= sizeof(tx_buf));
00270 
00271         /* Check if the transmit buffer is available */
00272     while (!(tx_buf_tab[tx_buf_idx].stat & TXS_USED))
00273         event_wait(&send_wait);
00274 
00275         /* Copy the data into the buffer and prepare descriptor */
00276         wr_len = MIN(len, (size_t)EMAC_TX_BUFSIZ - tx_buf_offset);
00277         memcpy((uint8_t *)tx_buf_tab[tx_buf_idx].addr + tx_buf_offset,
00278                 buf, wr_len);
00279     tx_buf_offset += wr_len;
00280 
00281     return wr_len;
00282 }
00283 
00284 void eth_sendFrame(void)
00285 {
00286         tx_buf_tab[tx_buf_idx].stat = (tx_buf_offset & TXS_LENGTH_FRAME) |
00287             TXS_LAST_BUFF |
00288             ((tx_buf_idx == EMAC_TX_DESCRIPTORS - 1) ?  TXS_WRAP : 0);
00289     EMAC_NCR |= BV(EMAC_TSTART);
00290 
00291     tx_buf_offset = 0;
00292     if (++tx_buf_idx >= EMAC_TX_DESCRIPTORS)
00293         tx_buf_idx = 0;
00294 }
00295 
00296 ssize_t eth_send(const uint8_t *buf, size_t len)
00297  {
00298     if (UNLIKELY(!len))
00299         return -1;
00300 
00301     len = eth_putFrame(buf, len);
00302     eth_sendFrame();
00303 
00304     return len;
00305 }
00306 
00307 static void eth_buf_realign(int idx)
00308 {
00309     /* Empty buffer found. Realign. */
00310     do {
00311         rx_buf_tab[rx_buf_idx].addr &= ~RXBUF_OWNERSHIP;
00312         if (++rx_buf_idx >= EMAC_RX_BUFFERS)
00313             rx_buf_idx = 0;
00314     } while (idx != rx_buf_idx);
00315 }
00316 
00317 static size_t __eth_getFrameLen(void)
00318 {
00319     int idx, n = EMAC_RX_BUFFERS;
00320 
00321 skip:
00322     /* Skip empty buffers */
00323     while ((n > 0) && !(rx_buf_tab[rx_buf_idx].addr & RXBUF_OWNERSHIP))
00324     {
00325         if (++rx_buf_idx >= EMAC_RX_BUFFERS)
00326             rx_buf_idx = 0;
00327         n--;
00328     }
00329     if (UNLIKELY(!n))
00330     {
00331         LOG_INFO("no frame found\n");
00332         return 0;
00333     }
00334     /* Search the start of frame and cleanup fragments */
00335     while ((n > 0) && (rx_buf_tab[rx_buf_idx].addr & RXBUF_OWNERSHIP) &&
00336             !(rx_buf_tab[rx_buf_idx].stat & RXS_SOF))
00337     {
00338         rx_buf_tab[rx_buf_idx].addr &= ~RXBUF_OWNERSHIP;
00339         if (++rx_buf_idx >= EMAC_RX_BUFFERS)
00340             rx_buf_idx = 0;
00341         n--;
00342     }
00343     if (UNLIKELY(!n))
00344     {
00345         LOG_INFO("no SOF found\n");
00346         return 0;
00347     }
00348     /* Search end of frame to evaluate the total frame size */
00349     idx = rx_buf_idx;
00350 restart:
00351     while (n > 0)
00352     {
00353         if (UNLIKELY(!(rx_buf_tab[idx].addr & RXBUF_OWNERSHIP)))
00354         {
00355             /* Empty buffer found. Realign. */
00356             eth_buf_realign(idx);
00357             goto skip;
00358         }
00359         if (rx_buf_tab[idx].stat & RXS_EOF)
00360             return rx_buf_tab[idx].stat & RXS_LENGTH_FRAME;
00361         if (UNLIKELY((idx != rx_buf_idx) &&
00362                 (rx_buf_tab[idx].stat & RXS_SOF)))
00363         {
00364             /* Another start of frame found. Realign. */
00365             eth_buf_realign(idx);
00366             goto restart;
00367         }
00368         if (++idx >= EMAC_RX_BUFFERS)
00369             idx = 0;
00370         n--;
00371     }
00372     LOG_INFO("no EOF found\n");
00373     return 0;
00374 }
00375 
00376 size_t eth_getFrameLen(void)
00377 {
00378     size_t len;
00379 
00380     /* Check if there is at least one available frame in the buffer */
00381     while (1)
00382     {
00383         len = __eth_getFrameLen();
00384         if (LIKELY(len))
00385             break;
00386         /* Wait for RX interrupt */
00387         event_wait(&recv_wait);
00388     }
00389     return len;
00390 }
00391 
00392 ssize_t eth_getFrame(uint8_t *buf, size_t len)
00393 {
00394     uint8_t *addr;
00395     size_t rd_len = 0;
00396 
00397     if (UNLIKELY(!len))
00398         return -1;
00399     ASSERT(len <= sizeof(rx_buf));
00400 
00401     /* Copy data from the RX buffer */
00402     addr = (uint8_t *)(rx_buf_tab[rx_buf_idx].addr & BUF_ADDRMASK);
00403     if (addr + len > &rx_buf[countof(rx_buf)])
00404     {
00405         size_t count = &rx_buf[countof(rx_buf)] - addr;
00406 
00407         memcpy(buf, addr, count);
00408         memcpy(buf + count, rx_buf, len - count);
00409     }
00410     else
00411     {
00412         memcpy(buf, addr, len);
00413     }
00414     /* Update descriptors */
00415     while (rd_len < len)
00416     {
00417         if (len - rd_len >= EMAC_RX_BUFSIZ)
00418             rd_len += EMAC_RX_BUFSIZ;
00419         else
00420             rd_len += len - rd_len;
00421         if (UNLIKELY(!(rx_buf_tab[rx_buf_idx].addr & RXBUF_OWNERSHIP)))
00422         {
00423             LOG_INFO("bad frame found\n");
00424             return 0;
00425         }
00426         rx_buf_tab[rx_buf_idx].addr &= ~RXBUF_OWNERSHIP;
00427         if (++rx_buf_idx >= EMAC_RX_DESCRIPTORS)
00428             rx_buf_idx = 0;
00429     }
00430 
00431     return rd_len;
00432 }
00433 
00434 ssize_t eth_recv(uint8_t *buf, size_t len)
00435 {
00436     if (UNLIKELY(!len))
00437         return -1;
00438     len = MIN(len, eth_getFrameLen());
00439     return len ? eth_getFrame(buf, len) : 0;
00440 }
00441 
00442 int eth_init()
00443 {
00444     cpu_flags_t flags;
00445 
00446     emac_reset();
00447     emac_start();
00448 
00449     event_initGeneric(&recv_wait);
00450     event_initGeneric(&send_wait);
00451 
00452     // Register interrupt vector
00453     IRQ_SAVE_DISABLE(flags);
00454 
00455     /* Disable all emac interrupts */
00456     EMAC_IDR = 0xFFFFFFFF;
00457 
00458     /* Set the vector. */
00459     AIC_SVR(EMAC_ID) = emac_irqHandler;
00460     /* Initialize to edge triggered with defined priority. */
00461     AIC_SMR(EMAC_ID) = AIC_SRCTYPE_INT_EDGE_TRIGGERED;
00462     /* Clear pending interrupt */
00463     AIC_ICCR = BV(EMAC_ID);
00464     /* Enable the system IRQ */
00465     AIC_IECR = BV(EMAC_ID);
00466 
00467     /* Enable interrupts */
00468     EMAC_IER = EMAC_RX_INTS | EMAC_TX_INTS;
00469 
00470     IRQ_RESTORE(flags);
00471 
00472     return 0;
00473 }