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 }
![(please configure the [header_logo] section in trac.ini)](/chrome/site/bertos_logo.png)