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