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