#include "eth_at91.h"
-/*
- * MAC address configuration (please change this in your project!).
- *
- * TODO: make this paramater user-configurable from the Wizard.
- */
-uint8_t mac_addr[] = { 0x11, 0x22, 0x33, 0x44, 0x55, 0x66 };
+#define EMAC_RX_INTS (BV(EMAC_RCOMP) | BV(EMAC_ROVR) | BV(EMAC_RXUBR))
+#define EMAC_TX_INTS (BV(EMAC_TCOMP) | BV(EMAC_TXUBR) | BV(EMAC_RLEX))
/* Silent Doxygen bug... */
#ifndef __doxygen__
-static volatile BufDescriptor tx_buf_tab[EMAC_TX_DESCRIPTORS];
/*
* NOTE: this buffer should be declared as 'volatile' because it is read by the
* hardware. However, this is accessed only via memcpy() that should guarantee
* coherency when copying from/to buffers.
*/
static uint8_t tx_buf[EMAC_TX_BUFFERS * EMAC_TX_BUFSIZ] ALIGNED(8);
-static int tx_buf_idx = 0;
+static volatile BufDescriptor tx_buf_tab[EMAC_TX_DESCRIPTORS] ALIGNED(8);
-static volatile BufDescriptor rx_buf_tab[EMAC_RX_DESCRIPTORS];
/*
* NOTE: this buffer should be declared as 'volatile' because it is wrote by
* the hardware. However, this is accessed only via memcpy() that should
* guarantee coherency when copying from/to buffers.
*/
static uint8_t rx_buf[EMAC_RX_BUFFERS * EMAC_RX_BUFSIZ] ALIGNED(8);
-static int rx_buf_idx = 0;
+static volatile BufDescriptor rx_buf_tab[EMAC_RX_DESCRIPTORS] ALIGNED(8);
#endif
+static int tx_buf_idx;
+static int tx_buf_offset;
+static int rx_buf_idx;
+
static Event recv_wait, send_wait;
static DECLARE_ISR(emac_irqHandler)
/* Receiver interrupt */
if ((isr & EMAC_RX_INTS))
{
- event_do(&recv_wait);
+ if (isr & BV(EMAC_RCOMP))
+ event_do(&recv_wait);
EMAC_RSR = EMAC_RX_INTS;
}
/* Transmitter interrupt */
if (isr & EMAC_TX_INTS)
{
- event_do(&send_wait);
+ if (isr & BV(EMAC_TCOMP))
+ event_do(&send_wait);
EMAC_TSR = EMAC_TX_INTS;
}
AIC_EOICR = 0;
*
* \return Contents of the specified register.
*/
-static uint16_t phy_hw_read(reg8_t reg)
+static uint16_t phy_hw_read(uint8_t reg)
{
// PHY read command.
EMAC_MAN = EMAC_SOF | EMAC_RW_READ | (NIC_PHY_ADDR << EMAC_PHYA_SHIFT)
* \param reg PHY register number.
* \param val Value to write.
*/
-static void phy_hw_write(reg8_t reg, uint16_t val)
+static void phy_hw_write(uint8_t reg, uint16_t val)
{
// PHY write command.
EMAC_MAN = EMAC_SOF | EMAC_RW_WRITE | (NIC_PHY_ADDR << EMAC_PHYA_SHIFT)
cpu_relax();
}
-static int emac_reset(void)
+INLINE void phy_manageEnable(bool en)
{
- uint16_t phy_cr;
-
- // Enable devices
- PMC_PCER = BV(PIOA_ID);
- PMC_PCER = BV(PIOB_ID);
- PMC_PCER = BV(EMAC_ID);
-
- // Disable RMII and TESTMODE by disabling pull-ups.
- PIOB_PUDR = BV(PHY_COL_RMII_BIT) | BV(PHY_RXDV_TESTMODE_BIT);
-
- // Disable PHY power down.
- PIOB_PER = BV(PHY_PWRDN_BIT);
- PIOB_OER = BV(PHY_PWRDN_BIT);
- PIOB_CODR = BV(PHY_PWRDN_BIT);
+ if (en)
+ {
+ /* Enable management port. */
+ EMAC_NCR |= BV(EMAC_MPE);
+ EMAC_NCFGR |= EMAC_CLK_HCLK_32;
+ }
+ else
+ {
+ /* Disable management port */
+ EMAC_NCR &= ~BV(EMAC_MPE);
+ }
+}
- // Toggle external hardware reset pin.
+INLINE void phy_resetPulse(void)
+{
+ /* Toggle external hardware reset pin. */
RSTC_MR = RSTC_KEY | (1 << RSTC_ERSTL_SHIFT) | BV(RSTC_URSTEN);
RSTC_CR = RSTC_KEY | BV(RSTC_EXTRST);
while ((RSTC_SR & BV(RSTC_NRSTL)) == 0)
cpu_relax();
+}
- // Configure MII port.
+INLINE void phy_pinThreeState(void)
+{
+ PIOB_PUDR = PHY_MII_PINS;
+ PIOB_ODR = PHY_MII_PINS;
+ PIOB_PER = PHY_MII_PINS;
+}
+
+INLINE void phy_pinGpio(void)
+{
+ PIOB_PUDR = PHY_MII_PINS;
+ PIOB_OWER = PHY_MII_PINS;
+ PIOB_OER = PHY_MII_PINS;
+ PIOB_PER = PHY_MII_PINS;
+}
+
+INLINE void phy_pinMac(void)
+{
+ PIOB_ODR = PHY_MII_PINS;
+ PIOB_OWDR = PHY_MII_PINS;
PIOB_ASR = PHY_MII_PINS;
PIOB_BSR = 0;
+ PIOB_PUDR = PHY_MII_PINS;
PIOB_PDR = PHY_MII_PINS;
+}
- // Enable receive and transmit clocks.
- EMAC_USRIO = BV(EMAC_CLKEN);
+INLINE void phy_pinSet(uint32_t state)
+{
+ PIOB_ODSR = state;
+}
+
+#define AUTONEGOTIATION_TIMEOUT 5000
- // Enable management port.
- EMAC_NCR |= BV(EMAC_MPE);
- EMAC_NCFGR |= EMAC_CLK_HCLK_32;
+static void emac_reset(void)
+{
+ /* Enable devices */
+ PMC_PCER = BV(EMAC_ID);
- // Set local MAC address.
+ /* Enable receive and transmit clocks. */
+ EMAC_USRIO = BV(EMAC_CLKEN);
+
+ /* Set local MAC address. */
EMAC_SA1L = (mac_addr[3] << 24) | (mac_addr[2] << 16) |
(mac_addr[1] << 8) | mac_addr[0];
EMAC_SA1H = (mac_addr[5] << 8) | mac_addr[4];
+ phy_manageEnable(true);
+
+ PHY_HW_INIT();
- // Wait for PHY ready
- timer_delay(255);
+ PHY_INIT();
+ phy_pinMac();
- // Clear MII isolate.
- phy_hw_read(NIC_PHY_BMCR);
- phy_cr = phy_hw_read(NIC_PHY_BMCR);
+ /* Clear MII isolate. */
+ uint16_t phy_cr = phy_hw_read(NIC_PHY_BMCR);
phy_cr &= ~NIC_PHY_BMCR_ISOLATE;
phy_hw_write(NIC_PHY_BMCR, phy_cr);
- phy_cr = phy_hw_read(NIC_PHY_BMCR);
+ uint32_t phy_id = phy_hw_read(NIC_PHY_ID1) << 16
+ | phy_hw_read(NIC_PHY_ID2);
+ //ASSERT((phy_id & 0xFFFFFFF0) == (NIC_PHY_ID & 0xFFFFFFF0));
+ LOG_INFO("PHY ID %#08lx\n", phy_id);
- LOG_INFO("%s: PHY ID %#04x %#04x\n",
- __func__,
- phy_hw_read(NIC_PHY_ID1), phy_hw_read(NIC_PHY_ID2));
-
- // Wait for auto negotiation completed.
- phy_hw_read(NIC_PHY_BMSR);
- for (;;)
+ ticks_t start = timer_clock();
+ /* Wait for auto negotiation completed. */
+ while (1)
{
if (phy_hw_read(NIC_PHY_BMSR) & NIC_PHY_BMSR_ANCOMPL)
break;
cpu_relax();
+ if (timer_clock() - start > ms_to_ticks(AUTONEGOTIATION_TIMEOUT))
+ {
+ LOG_ERR("Autonegotiation timeout\n");
+ break;
+ }
}
-
- // Disable management port.
- EMAC_NCR &= ~BV(EMAC_MPE);
-
- return 0;
}
-static int emac_start(void)
+static void emac_start(void)
{
uint32_t addr;
int i;
/* Enable receiver, transmitter and statistics. */
EMAC_NCR |= BV(EMAC_TE) | BV(EMAC_RE) | BV(EMAC_WESTAT);
-
- return 0;
}
-ssize_t eth_send(const uint8_t *buf, size_t len)
- {
+ssize_t eth_putFrame(const uint8_t *buf, size_t len)
+{
size_t wr_len;
- int idx;
if (UNLIKELY(!len))
return -1;
ASSERT(len <= sizeof(tx_buf));
/* Check if the transmit buffer is available */
- idx = tx_buf_idx;
- while (!(tx_buf_tab[idx].stat & TXS_USED))
- event_wait(&send_wait);
+ while (!(tx_buf_tab[tx_buf_idx].stat & TXS_USED))
+ event_wait(&send_wait);
+ /* Copy the data into the buffer and prepare descriptor */
+ wr_len = MIN(len, (size_t)EMAC_TX_BUFSIZ - tx_buf_offset);
+ memcpy((uint8_t *)tx_buf_tab[tx_buf_idx].addr + tx_buf_offset,
+ buf, wr_len);
+ tx_buf_offset += wr_len;
+
+ return wr_len;
+}
+
+void eth_sendFrame(void)
+{
+ tx_buf_tab[tx_buf_idx].stat = (tx_buf_offset & TXS_LENGTH_FRAME) |
+ TXS_LAST_BUFF |
+ ((tx_buf_idx == EMAC_TX_DESCRIPTORS - 1) ? TXS_WRAP : 0);
+ EMAC_NCR |= BV(EMAC_TSTART);
+
+ tx_buf_offset = 0;
if (++tx_buf_idx >= EMAC_TX_DESCRIPTORS)
tx_buf_idx = 0;
+}
- /* Copy the data into the buffer and prepare descriptor */
- wr_len = MIN(len, (size_t)EMAC_TX_BUFSIZ);
- memcpy((uint8_t *)tx_buf_tab[idx].addr, buf, wr_len);
- tx_buf_tab[idx].stat = (wr_len & RXS_LENGTH_FRAME) |
- ((idx == EMAC_TX_DESCRIPTORS - 1) ?
- TXS_WRAP : 0) |
- TXS_LAST_BUFF;
- EMAC_NCR |= BV(EMAC_TSTART);
+ssize_t eth_send(const uint8_t *buf, size_t len)
+ {
+ if (UNLIKELY(!len))
+ return -1;
- return wr_len;
+ len = eth_putFrame(buf, len);
+ eth_sendFrame();
+
+ return len;
+}
+
+static void eth_buf_realign(int idx)
+{
+ /* Empty buffer found. Realign. */
+ do {
+ rx_buf_tab[rx_buf_idx].addr &= ~RXBUF_OWNERSHIP;
+ if (++rx_buf_idx >= EMAC_RX_BUFFERS)
+ rx_buf_idx = 0;
+ } while (idx != rx_buf_idx);
}
-static size_t eth_getFrameLen(void)
+static size_t __eth_getFrameLen(void)
{
int idx, n = EMAC_RX_BUFFERS;
+skip:
/* Skip empty buffers */
while ((n > 0) && !(rx_buf_tab[rx_buf_idx].addr & RXBUF_OWNERSHIP))
{
rx_buf_idx = 0;
n--;
}
- if (UNLIKELY(!(rx_buf_tab[rx_buf_idx].stat & RXS_SOF)))
+ if (UNLIKELY(!n))
{
LOG_INFO("no SOF found\n");
return 0;
}
/* Search end of frame to evaluate the total frame size */
idx = rx_buf_idx;
- while ((n > 0) && (rx_buf_tab[idx].addr & RXBUF_OWNERSHIP))
+restart:
+ while (n > 0)
{
+ if (UNLIKELY(!(rx_buf_tab[idx].addr & RXBUF_OWNERSHIP)))
+ {
+ /* Empty buffer found. Realign. */
+ eth_buf_realign(idx);
+ goto skip;
+ }
+ if (rx_buf_tab[idx].stat & RXS_EOF)
+ return rx_buf_tab[idx].stat & RXS_LENGTH_FRAME;
if (UNLIKELY((idx != rx_buf_idx) &&
(rx_buf_tab[idx].stat & RXS_SOF)))
{
/* Another start of frame found. Realign. */
- do {
- rx_buf_tab[rx_buf_idx].addr &= ~RXBUF_OWNERSHIP;
- if (++rx_buf_idx >= EMAC_RX_BUFFERS)
- rx_buf_idx = 0;
- } while (idx != rx_buf_idx);
+ eth_buf_realign(idx);
+ goto restart;
}
- if (rx_buf_tab[idx].stat & RXS_EOF)
- return rx_buf_tab[idx].stat & RXS_LENGTH_FRAME;
if (++idx >= EMAC_RX_BUFFERS)
idx = 0;
n--;
}
-
LOG_INFO("no EOF found\n");
return 0;
}
-ssize_t eth_recv(uint8_t *buf, size_t len)
+size_t eth_getFrameLen(void)
{
- uint8_t *addr;
- size_t rd_len = 0;
-
- if (UNLIKELY(!len))
- return -1;
- ASSERT(len <= sizeof(tx_buf));
+ size_t len;
/* Check if there is at least one available frame in the buffer */
while (1)
{
- size_t frame_len = MIN(len, eth_getFrameLen());
- if (frame_len)
- {
- len = frame_len;
+ len = __eth_getFrameLen();
+ if (LIKELY(len))
break;
- }
/* Wait for RX interrupt */
event_wait(&recv_wait);
}
+ return len;
+}
+
+ssize_t eth_getFrame(uint8_t *buf, size_t len)
+{
+ uint8_t *addr;
+ size_t rd_len = 0;
+
+ if (UNLIKELY(!len))
+ return -1;
+ ASSERT(len <= sizeof(rx_buf));
/* Copy data from the RX buffer */
addr = (uint8_t *)(rx_buf_tab[rx_buf_idx].addr & BUF_ADDRMASK);
rd_len += EMAC_RX_BUFSIZ;
else
rd_len += len - rd_len;
- ASSERT(rx_buf_tab[rx_buf_idx].addr & RXBUF_OWNERSHIP);
+ if (UNLIKELY(!(rx_buf_tab[rx_buf_idx].addr & RXBUF_OWNERSHIP)))
+ {
+ LOG_INFO("bad frame found\n");
+ return 0;
+ }
rx_buf_tab[rx_buf_idx].addr &= ~RXBUF_OWNERSHIP;
if (++rx_buf_idx >= EMAC_RX_DESCRIPTORS)
rx_buf_idx = 0;
}
+
return rd_len;
}
+ssize_t eth_recv(uint8_t *buf, size_t len)
+{
+ if (UNLIKELY(!len))
+ return -1;
+ len = MIN(len, eth_getFrameLen());
+ return len ? eth_getFrame(buf, len) : 0;
+}
+
int eth_init()
{
cpu_flags_t flags;
/* Set the vector. */
AIC_SVR(EMAC_ID) = emac_irqHandler;
/* Initialize to edge triggered with defined priority. */
- AIC_SMR(EMAC_ID) = AIC_SRCTYPE_INT_EDGE_TRIGGERED;
+ AIC_SMR(EMAC_ID) = AIC_SRCTYPE_INT_EDGE_TRIGGERED | 6;
/* Clear pending interrupt */
AIC_ICCR = BV(EMAC_ID);
/* Enable the system IRQ */