summaryrefslogtreecommitdiff
path: root/nuttx/arch/arm/src/lm3s/lm3s_ethernet.c
diff options
context:
space:
mode:
Diffstat (limited to 'nuttx/arch/arm/src/lm3s/lm3s_ethernet.c')
-rw-r--r--nuttx/arch/arm/src/lm3s/lm3s_ethernet.c301
1 files changed, 252 insertions, 49 deletions
diff --git a/nuttx/arch/arm/src/lm3s/lm3s_ethernet.c b/nuttx/arch/arm/src/lm3s/lm3s_ethernet.c
index cc8e3be37..4445af193 100644
--- a/nuttx/arch/arm/src/lm3s/lm3s_ethernet.c
+++ b/nuttx/arch/arm/src/lm3s/lm3s_ethernet.c
@@ -61,6 +61,72 @@
* Definitions
****************************************************************************/
+/* Half duplex can be forced if CONFIG_LM3S_ETHHDUPLEX is defined. */
+
+#ifdef CONFIG_LM3S_ETHHDUPLEX
+# define LM3S_DUPLEX_SETBITS 0
+# define LM3S_DUPLEX_CLRBITS MAC_TCTL_DUPLEX
+#else
+# define LM3S_DUPLEX_SETBITS MAC_TCTL_DUPLEX
+# define LM3S_DUPLEX_CLRBITS 0
+#endif
+
+/* Auto CRC generation can be suppressed if CONFIG_LM3S_ETHNOAUTOCRC is definde */
+
+#ifdef CONFIG_LM3S_ETHNOAUTOCRC
+# define LM3S_CRC_SETBITS 0
+# define LM3S_CRC_CLRBITS MAC_TCTL_CRC
+#else
+# define LM3S_CRC_SETBITS MAC_TCTL_CRC
+# define LM3S_CRC_CLRBITS 0
+#endif
+
+/* Tx padding can be suppressed if CONFIG_LM3S_ETHNOPAD is defined */
+
+#ifdef CONFIG_LM3S_ETHNOPAD
+# define LM3S_PADEN_SETBITS 0
+# define LM3S_PADEN_CLRBITS MAC_TCTL_PADEN
+#else
+# define LM3S_PADEN_SETBITS MAC_TCTL_PADEN
+# define LM3S_PADEN_CLRBITS 0
+#endif
+
+#define LM3S_TCTCL_SETBITS (LM3S_DUPLEX_SETBITS|LM3S_CRC_SETBITS|LM3S_PADEN_SETBITS)
+#define LM3S_TCTCL_CLRBITS (LM3S_DUPLEX_CLRBITS|LM3S_CRC_CLRBITS|LM3S_PADEN_CLRBITS)
+
+/* Multicast frames can be enabled by defining CONFIG_LM3S_MULTICAST */
+
+#ifdef CONFIG_LM3S_MULTICAST
+# define LM3S_AMUL_SETBITS MAC_RCTL_AMUL
+# define LM3S_AMUL_CLRBITS 0
+#else
+# define LM3S_AMUL_SETBITS 0
+# define LM3S_AMUL_CLRBITS MAC_RCTL_AMUL
+#endif
+
+/* Promiscuous mode can be enabled by defining CONFIG_LM3S_PROMISCUOUS */
+
+#ifdef CONFIG_LM3S_PROMISCUOUS
+# define LM3S_PRMS_SETBITS MAC_RCTL_PRMS
+# define LM3S_PRMS_CLRBITS 0
+#else
+# define LM3S_PRMS_SETBITS 0
+# define LM3S_PRMS_CLRBITS MAC_RCTL_PRMS
+#endif
+
+/* Bad CRC rejection can be enabled by define CONFIG_LM3S_BADCRC */
+
+#ifdef CONFIG_LM3S_BADCRC
+# define LM3S_BADCRC_SETBITS MAC_RCTL_BADCRC
+# define LM3S_BADCRC_CLRBITS 0
+#else
+# define LM3S_BADCRC_SETBITS 0
+# define LM3S_BADCRC_CLRBITS MAC_RCTL_BADCRC
+#endif
+
+#define LM3S_RCTCL_SETBITS (LM3S_AMUL_SETBITS|LM3S_PRMS_SETBITS|LM3S_BADCRC_SETBITS)
+#define LM3S_RCTCL_CLRBITS (LM3S_AMUL_CLRBITS|LM3S_PRMS_CLRBITS|LM3S_BADCRC_CLRBITS)
+
/* TX poll deley = 1 seconds. CLK_TCK is the number of clock ticks per second */
#define LM3S_WDDELAY (1*CLK_TCK)
@@ -151,7 +217,9 @@ static inline uint32 lm3s_ethin(struct lm3s_driver_s *priv, int offset);
static inline void lm3s_ethout(struct lm3s_driver_s *priv, int offset, uint32 value);
#endif
static void lm3s_ethreset(struct lm3s_driver_s *priv);
+#if 0 /* Not used */
static void lm3s_phywrite(struct lm3s_driver_s *priv, int regaddr, uint16 value);
+#endif
static uint16 lm3s_phyread(struct lm3s_driver_s *priv, int regaddr);
/* Common TX logic */
@@ -216,7 +284,7 @@ static inline uint32 lm3s_ethin(struct lm3s_driver_s *priv, int offset)
* Parameters:
* priv - Reference to the driver state structure
* offset - Byte offset of the register from the ethernet base address
- * value - The value to write the the Ethernet register
+ * value - The value to write the Ethernet register
*
* Returned Value:
* None
@@ -323,13 +391,32 @@ static void lm3s_ethreset(struct lm3s_driver_s *priv)
*
****************************************************************************/
+#if 0 /* Not used */
static void lm3s_phywrite(struct lm3s_driver_s *priv, int regaddr, uint16 value)
{
-# warning "Missing Logic"
+ /* Wait for any MII transactions in progress to complete */
+
+ while ((lm3s_ethin(priv, LM3S_MAC_MCTL_OFFSET) & MAC_MCTL_START) != 0);
+
+ /* Set up the data to be written */
+
+ DEBUGASSERT(value < MAC_MTXD_MASK);
+ lm3s_ethout(priv, LM3S_MAC_MTXD_OFFSET, value);
+
+ /* Set up the PHY register address and start the write operation */
+
+ regaddr <<= MAC_MCTL_REGADR_SHIFT;
+ DEBUGASSERT((regaddr & MAC_MTXD_MASK) == regaddr);
+ lm3s_ethout(priv, LM3S_MAC_MCTL_OFFSET, regaddr | MAC_MCTL_WRITE | MAC_MCTL_START);
+
+ /* Wait for the write transaction to complete */
+
+ while ((lm3s_ethin(priv, LM3S_MAC_MCTL_OFFSET) & MAC_MCTL_START) != 0);
}
+#endif
/****************************************************************************
- * Function: lm3s_phywrite
+ * Function: lm3s_phyread
*
* Description:
* Write a 16-bit word to a PHY register
@@ -346,8 +433,23 @@ static void lm3s_phywrite(struct lm3s_driver_s *priv, int regaddr, uint16 value)
static uint16 lm3s_phyread(struct lm3s_driver_s *priv, int regaddr)
{
-# warning "Missing Logic"
- return 0;
+ /* Wait for any MII transactions in progress to complete */
+
+ while ((lm3s_ethin(priv, LM3S_MAC_MCTL_OFFSET) & MAC_MCTL_START) != 0);
+
+ /* Set up the PHY register address and start the read operation */
+
+ regaddr <<= MAC_MCTL_REGADR_SHIFT;
+ DEBUGASSERT((regaddr & MAC_MTXD_MASK) == regaddr);
+ lm3s_ethout(priv, LM3S_MAC_MCTL_OFFSET, regaddr | MAC_MCTL_START);
+
+ /* Wait for the write transaction to complete */
+
+ while ((lm3s_ethin(priv, LM3S_MAC_MCTL_OFFSET) & MAC_MCTL_START) != 0);
+
+ /* Read and return the PHY data */
+
+ return (uint16)(lm3s_ethin(priv, LM3S_MAC_MRXD_OFFSET) & MAC_MTRD_MASK);
}
/****************************************************************************
@@ -367,20 +469,85 @@ static uint16 lm3s_phyread(struct lm3s_driver_s *priv, int regaddr)
static int lm3s_transmit(struct lm3s_driver_s *priv)
{
+ irqstate_t flags;
+ uint32 regval;
+ ubyte *dbuf;
+ int pktlen;
+ int bytesleft;
+ int ret = -EBUSY;
+
/* Verify that the hardware is ready to send another packet */
-#warning "Missing logic"
- /* Increment statistics */
- /* Disable Ethernet interrupts */
+ flags = irqsave();
+ if ((lm3s_ethin(priv, LM3S_MAC_TR_OFFSET) & MAC_TR_NEWTX) == 0)
+ {
+ /* Increment statistics */
+
+ EMAC_STAT(priv, tx_packets);
- /* Send the packet: address=priv->ld_dev.d_buf, length=priv->ld_dev.d_len */
+ /* Transfer the packet into the Tx FIFO. The LS 16-bits of the first
+ * 32-bit word written to the Tx FIFO contains the Ethernet payload
+ * data length. That is the full length of the message (d_len) minus
+ * the size of the Ethernet header (14).
+ */
- /* Restore Ethernet interrupts */
+ pktlen = priv->ld_dev.d_len;
+ nvdbg("Sending packet, pktlen: %d\n", pktlen);
+ DEBUGASSERT(pktlen > UIP_LLH_LEN);
- /* Setup the TX timeout watchdog (perhaps restarting the timer) */
+ dbuf = priv->ld_dev.d_buf;
+ regval = (uint32)(pktlen - 4);
+ regval |= ((uint32)(*dbuf++) << 16);
+ regval |= ((uint32)(*dbuf++) << 24);
+ lm3s_ethout(priv, LM3S_MAC_DATA_OFFSET, regval);
- (void)wd_start(priv->ld_txtimeout, LM3S_TXTIMEOUT, lm3s_txtimeout, 1, (uint32)priv);
- return OK;
+ /* Write all of the whole, 32-bit values in the middle of the packet */
+
+ for (bytesleft = pktlen - 2; bytesleft > 3; bytesleft -= 4, dbuf += 4)
+ {
+ /* Transfer a whole word from the user buffer. Note, the user
+ * buffer may be un-aligned.
+ */
+
+ lm3s_ethout(priv, LM3S_MAC_DATA_OFFSET, *(uint32*)dbuf);
+ }
+
+ /* Write the last, partial word in the FIFO */
+
+ if (bytesleft > 0)
+ {
+ /* Write the last word */
+
+ regval = 0;
+ switch (bytesleft)
+ {
+ case 0:
+ default:
+ break;
+
+ case 3:
+ regval |= ((uint32)dbuf[2] << 16);
+ case 2:
+ regval |= ((uint32)dbuf[1] << 8);
+ case 1:
+ regval |= (uint32)dbuf[0];
+ break;
+ }
+ lm3s_ethout(priv, LM3S_MAC_DATA_OFFSET, regval);
+ }
+
+ /* Activate the transmitter */
+
+ lm3s_ethout(priv, LM3S_MAC_TR_OFFSET, MAC_TR_NEWTX);
+
+ /* Setup the TX timeout watchdog (perhaps restarting the timer) */
+
+ (void)wd_start(priv->ld_txtimeout, LM3S_TXTIMEOUT, lm3s_txtimeout, 1, (uint32)priv);
+ ret = OK;
+ }
+
+ irqrestore(flags);
+ return ret;
}
/****************************************************************************
@@ -407,27 +574,29 @@ static int lm3s_transmit(struct lm3s_driver_s *priv)
static int lm3s_uiptxpoll(struct uip_driver_s *dev)
{
struct lm3s_driver_s *priv = (struct lm3s_driver_s *)dev->d_private;
+ int ret = OK;
/* If the polling resulted in data that should be sent out on the network,
* the field d_len is set to a value > 0.
*/
+ nvdbg("Poll result: d_len=%d\n", priv->dev.d_len);
if (priv->ld_dev.d_len > 0)
{
- uip_arp_out(&priv->ld_dev);
- lm3s_transmit(priv);
-
- /* Check if there is room in the device to hold another packet. If not,
- * return a non-zero value to terminate the poll.
+ /* Send the packet. lm3s_transmit() will return zero if the
+ * packet was successfully handled.
*/
-#warning "Missing logic"
+
+ DEBUGASSERT((lm3s_ethin(priv, LM3S_MAC_TR_OFFSET) & MAC_TR_NEWTX) == 0)
+ uip_arp_out(&priv->ld_dev);
+ ret =lm3s_transmit(priv);
}
/* If zero is returned, the polling will continue until all connections have
* been examined.
*/
- return 0;
+ return ret;
}
/****************************************************************************
@@ -476,6 +645,7 @@ static void lm3s_receive(struct lm3s_driver_s *priv)
regval = lm3s_ethin(priv, LM3S_MAC_DATA_OFFSET);
pktlen = (int)(regval & 0x0000ffff);
+ nvdbg("Receiving packet, pktlen: %d\n", pktlen);
/* Check if the pktlen is valid. It should be large enough to
* hold an Ethernet header and small enough to fit entirely in
@@ -512,13 +682,14 @@ static void lm3s_receive(struct lm3s_driver_s *priv)
*dbuf++ = (ubyte)((regval >> 16) & 0xff);
*dbuf++ = (ubyte)((regval >> 24) & 0xff);
- bytesleft = pktlen - 2;
/* Read all of the whole, 32-bit values in the middle of the packet */
- for (; bytesleft > 3; bytesleft -= 4, dbuf += 4)
+ for (bytesleft = pktlen - 2; bytesleft > 3; bytesleft -= 4, dbuf += 4)
{
- /* Read a whole word */
+ /* Transfer a whole word to the user buffer. Note, the user
+ * buffer may be un-aligned.
+ */
*(uint32*)dbuf = lm3s_ethin(priv, LM3S_MAC_DATA_OFFSET);
}
@@ -619,13 +790,20 @@ static void lm3s_receive(struct lm3s_driver_s *priv)
static void lm3s_txdone(struct lm3s_driver_s *priv)
{
- /* Check for errors and update statistics */
-#warning "Missing logic"
-
- /* If no further xmits are pending, then cancel the TX timeout */
+ /* Cancel the TX timeout */
wd_cancel(priv->ld_txtimeout);
+ /* Verify that the Tx FIFO is not in use. The NEWTX bit initiates an
+ * Ethernet transmission once the packet has been placed in the TX FIFO.
+ * This bit is cleared once the transmission has been completed. Since
+ * we get here because of of TXEMP which indicates that the packet was
+ * transmitted and that the TX FIFO is empty, NEWTX should always be zero
+ * at this point.
+ */
+
+ DEBUGASSERT((lm3s_ethin(priv, LM3S_MAC_TR_OFFSET) & MAC_TR_NEWTX) == 0)
+
/* Then poll uIP for new XMIT data */
(void)uip_poll(&priv->ld_dev, lm3s_uiptxpoll);
@@ -741,10 +919,15 @@ static void lm3s_txtimeout(int argc, uint32 arg, ...)
/* Increment statistics and dump debug info */
+ ndbg("Tx timeout\n");
EMAC_STAT(priv, tx_timeouts);
/* Then reset the hardware */
+ DEBUGASSERT(priv->ld_bifup);
+ lm3s_ifdown(&priv->ld_dev);
+ lm3s_ifup(&priv->ld_dev);
+
/* Then poll uIP for new XMIT data */
(void)uip_poll(&priv->ld_dev, lm3s_uiptxpoll);
@@ -771,16 +954,24 @@ static void lm3s_polltimer(int argc, uint32 arg, ...)
{
struct lm3s_driver_s *priv = (struct lm3s_driver_s *)arg;
- /* Check if there is room in the send another Tx packet. */
-#warning "Missing logic"
+ /* Check if we can send another Tx packet now. The NEWTX bit initiates an
+ * Ethernet transmission once the packet has been placed in the TX FIFO.
+ * This bit is cleared once the transmission has been completed.
+ *
+ * NOTE: This can cause missing poll cycles and, hence, some timing
+ * inaccuracies.
+ */
- /* If so, update TCP timing states and poll uIP for new XMIT data */
+ if ((lm3s_ethin(priv, LM3S_MAC_TR_OFFSET) & MAC_TR_NEWTX) == 0)
+ {
+ /* If so, update TCP timing states and poll uIP for new XMIT data */
- (void)uip_timer(&priv->ld_dev, lm3s_uiptxpoll, LM3S_POLLHSEC);
+ (void)uip_timer(&priv->ld_dev, lm3s_uiptxpoll, LM3S_POLLHSEC);
- /* Setup the watchdog poll timer again */
+ /* Setup the watchdog poll timer again */
- (void)wd_start(priv->ld_txpoll, LM3S_WDDELAY, lm3s_polltimer, 1, arg);
+ (void)wd_start(priv->ld_txpoll, LM3S_WDDELAY, lm3s_polltimer, 1, arg);
+ }
}
/****************************************************************************
@@ -837,19 +1028,19 @@ static int lm3s_ifup(struct uip_driver_s *dev)
* TX Padding Enabled).
*/
-# warning "These should be configurable items"
regval = lm3s_ethin(priv, LM3S_MAC_TCTL_OFFSET);
- regval |= (MAC_TCTL_DUPLEX|MAC_TCTL_CRC|MAC_TCTL_PADEN);
+ regval &= ~LM3S_TCTCL_CLRBITS;
+ regval |= LM3S_TCTCL_SETBITS;
lm3s_ethout(priv, LM3S_MAC_TCTL_OFFSET, regval);
nvdbg("TCTL: %08x\n", regval);
/* Setup the receive control register (Disable multicast frames, disable
- * promiscuous mode, disable bad CRC rejection). >>> These should be configurable items <<<
+ * promiscuous mode, disable bad CRC rejection).
*/
-# warning "These should be configurable items"
regval = lm3s_ethin(priv, LM3S_MAC_RCTL_OFFSET);
- regval &= ~(MAC_RCTL_BADCRC | MAC_RCTL_PRMS | MAC_RCTL_AMUL);
+ regval &= ~LM3S_RCTCL_CLRBITS;
+ regval |= LM3S_RCTCL_SETBITS;
lm3s_ethout(priv, LM3S_MAC_RCTL_OFFSET, regval);
nvdbg("RCTL: %08x\n", regval);
@@ -866,9 +1057,11 @@ static int lm3s_ifup(struct uip_driver_s *dev)
nvdbg("TS: %08x\n", regval);
#endif
- /* Wait for the link to come up */
-
-# warning "This should use a semaphore and an interrupt"
+ /* Wait for the link to come up. This following is not very conservative
+ * of system resources -- it really should wait gracefully on a semaphore
+ * and the interrupt handler should post the semaphore when LINKSTATUS is
+ * set
+ */
ndbg("Waiting for link\n");
do
@@ -960,6 +1153,10 @@ static int lm3s_ifdown(struct uip_driver_s *dev)
irqstate_t flags;
uint32 regval;
+ ndbg("Taking down: %d.%d.%d.%d\n",
+ dev->d_ipaddr & 0xff, (dev->d_ipaddr >> 8) & 0xff,
+ (dev->d_ipaddr >> 16) & 0xff, dev->d_ipaddr >> 24 );
+
/* Cancel the TX poll timer and TX timeout timers */
flags = irqsave();
@@ -1040,16 +1237,20 @@ static int lm3s_txavail(struct uip_driver_s *dev)
struct lm3s_driver_s *priv = (struct lm3s_driver_s *)dev->d_private;
irqstate_t flags;
- flags = irqsave();
-
- /* Ignore the notification if the interface is not yet up */
+ /* Ignore the notification if the interface is not yet up or if the Tx FIFO
+ * hardware is not available at this time. The NEWTX bit initiates an
+ * Ethernet transmission once the packet has been placed in the TX FIFO.
+ * This bit is cleared once the transmission has been completed. When the
+ * transmission completes, lm3s_txdone() will be called and the Tx polling
+ * will occur at that time.
+ */
- if (priv->ld_bifup)
+ flags = irqsave();
+ if (priv->ld_bifup && (lm3s_ethin(priv, LM3S_MAC_TR_OFFSET) & MAC_TR_NEWTX) == 0)
{
- /* Check if there is room in the hardware to hold another outgoing packet. */
-#warning "Missing logic"
-
- /* If so, then poll uIP for new XMIT data */
+ /* If the interface is up and we can use the Tx FIFO, then poll uIP
+ * for new Tx data
+ */
(void)uip_poll(&priv->ld_dev, lm3s_uiptxpoll);
}
@@ -1089,6 +1290,8 @@ static inline int lm3s_initialize(int intf)
/* Check if the Ethernet module is present */
+ ndbg("Setting up eth%d\n", intf);
+
#if LMS_NETHCONTROLLERS > 1
# error "This debug check only works with one interface"
#else