summaryrefslogtreecommitdiff
path: root/nuttx/arch/arm/src/lpc17xx
diff options
context:
space:
mode:
authorGregory Nutt <gnutt@nuttx.org>2014-07-24 16:39:18 -0600
committerGregory Nutt <gnutt@nuttx.org>2014-07-24 16:39:18 -0600
commitc3a822c2aabdd04fdb9471932495b40912601359 (patch)
tree9ffacd9430f4a81cbc41a9c5f086aa24223d2215 /nuttx/arch/arm/src/lpc17xx
parent8a6a9af27e061d79a719b415d86e7ac10c326e0f (diff)
downloadnuttx-c3a822c2aabdd04fdb9471932495b40912601359.tar.gz
nuttx-c3a822c2aabdd04fdb9471932495b40912601359.tar.bz2
nuttx-c3a822c2aabdd04fdb9471932495b40912601359.zip
LPC17 Ethernet: Added option to use the kernel worker thread to do most of the workload with CONFIG_NET_WORKER_THREAD option in Kconfig. Eliminated a problem with PHY DP83848C : it doesn't need a specific initialization on mbed. Critical bufix: From time to time (after some hours) the Ethernet receiver would lose one receive interrupt and the IP stack never recover because there is no receive watchdog as the transmit watchdog. From Max
Diffstat (limited to 'nuttx/arch/arm/src/lpc17xx')
-rw-r--r--nuttx/arch/arm/src/lpc17xx/Kconfig13
-rw-r--r--nuttx/arch/arm/src/lpc17xx/lpc17_ethernet.c141
2 files changed, 133 insertions, 21 deletions
diff --git a/nuttx/arch/arm/src/lpc17xx/Kconfig b/nuttx/arch/arm/src/lpc17xx/Kconfig
index e17100b4c..0352eb087 100644
--- a/nuttx/arch/arm/src/lpc17xx/Kconfig
+++ b/nuttx/arch/arm/src/lpc17xx/Kconfig
@@ -517,6 +517,12 @@ config ADC_CHANLIST
(2) provide an array g_adc_chanlist[] with the channel numbers
matching the ADC0_MASK within the board-specific library.
+config ADC_BURSTMODE
+ bool "One interrupt at the end of all ADC cconversions"
+ default n
+ ---help---
+ Select this if you want to generate only one interrupt once all selected channels has been converted by the ADC
+
config ADC_NCHANNELS
int "ADC0 number of channels"
depends on ADC_CHANLIST
@@ -727,6 +733,13 @@ config NET_MULTICAST
Enable receipt of multicast (and unicast) frames. Automatically set
if NET_IGMP is selected.
+config NET_WORKER_THREAD
+ bool "Net worker thread enable"
+ default y
+ depends on SCHED_WORKQUEUE
+ ---help---
+ Enable use of ethernet worker thread (depends on CONFIG_SCHED_WORKQUEUE).
+
endmenu
menu "LCD device driver options"
diff --git a/nuttx/arch/arm/src/lpc17xx/lpc17_ethernet.c b/nuttx/arch/arm/src/lpc17xx/lpc17_ethernet.c
index 0f5495ed0..97a154722 100644
--- a/nuttx/arch/arm/src/lpc17xx/lpc17_ethernet.c
+++ b/nuttx/arch/arm/src/lpc17xx/lpc17_ethernet.c
@@ -52,6 +52,7 @@
#include <nuttx/irq.h>
#include <nuttx/arch.h>
+#include <nuttx/wqueue.h>
#include <nuttx/net/mii.h>
#include <nuttx/net/netconfig.h>
#include <nuttx/net/arp.h>
@@ -275,6 +276,12 @@ struct lpc17_driver_s
WDOG_ID lp_txpoll; /* TX poll timer */
WDOG_ID lp_txtimeout; /* TX timeout timer */
+#ifdef CONFIG_NET_WORKER_THREAD
+ struct work_s irqwork_txdone; /* Interrupt continuation work queue support */
+ struct work_s irqwork_rxdone; /* Interrupt continuation work queue support */
+ uint32_t status;
+#endif /*CONFIG_NET_WORKER_THREAD*/
+
#if defined(CONFIG_DEBUG) && defined(CONFIG_DEBUG_NET)
struct lpc17_statistics_s lp_stat;
#endif
@@ -332,6 +339,10 @@ static int lpc17_txpoll(struct net_driver_s *dev);
static void lpc17_response(struct lpc17_driver_s *priv);
static void lpc17_rxdone(struct lpc17_driver_s *priv);
static void lpc17_txdone(struct lpc17_driver_s *priv);
+#ifdef CONFIG_NET_WORKER_THREAD
+static void lpc17_eth_irqworker_txdone(FAR void *arg);
+static void lpc17_eth_irqworker_rxdone(FAR void *arg);
+#endif /*CONFIG_NET_WORKER_THREAD*/
static int lpc17_interrupt(int irq, void *context);
/* Watchdog timer expirations */
@@ -731,20 +742,20 @@ static void lpc17_response(struct lpc17_driver_s *priv)
ret = lpc17_txdesc(priv);
if (ret == OK)
{
- /* Yes.. queue the packet now. */
+ /* Yes.. queue the packet now. */
- lpc17_transmit(priv);
+ lpc17_transmit(priv);
}
else
{
- /* No.. mark the Tx as pending and halt further Tx interrupts */
+ /* No.. mark the Tx as pending and halt further Tx interrupts */
- DEBUGASSERT((priv->lp_inten & ETH_INT_TXDONE) != 0);
+ DEBUGASSERT((priv->lp_inten & ETH_INT_TXDONE) != 0);
- priv->lp_txpending = true;
- priv->lp_inten &= ~ETH_RXINTS;
- lpc17_putreg(priv->lp_inten, LPC17_ETH_INTEN);
- EMAC_STAT(priv, tx_pending);
+ priv->lp_txpending = true;
+ priv->lp_inten &= ~ETH_TXINTS;
+ lpc17_putreg(priv->lp_inten, LPC17_ETH_INTEN);
+ EMAC_STAT(priv, tx_pending);
}
}
@@ -982,6 +993,46 @@ static void lpc17_txdone(struct lpc17_driver_s *priv)
}
/****************************************************************************
+ * Function: lpc17_eth_irqworker_txdone and lpc17_eth_irqworker_rxdone
+ *
+ * Description:
+ * Perform interrupt handling logic outside of the interrupt handler (on
+ * the work queue thread).
+ *
+ * Parameters:
+ * arg - The reference to the driver structure (case to void*)
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_WORKER_THREAD
+static void lpc17_eth_irqworker_txdone(FAR void *arg)
+{
+ FAR struct lpc17_driver_s *priv = (FAR struct lpc17_driver_s *)arg;
+
+ DEBUGASSERT(priv);
+
+ lpc17_txdone(priv);
+
+ work_cancel(HPWORK, &priv->irqwork_txdone);
+}
+
+static void lpc17_eth_irqworker_rxdone(FAR void *arg)
+{
+ FAR struct lpc17_driver_s *priv = (FAR struct lpc17_driver_s *)arg;
+
+ DEBUGASSERT(priv);
+
+ lpc17_rxdone(priv);
+ work_cancel(HPWORK, &priv->irqwork_rxdone);
+}
+#endif /*CONFIG_NET_WORKER_THREAD */
+
+/****************************************************************************
* Function: lpc17_interrupt
*
* Description:
@@ -1078,28 +1129,30 @@ static int lpc17_interrupt(int irq, void *context)
/* RX FINISHED -- Triggered when all receive descriptors have
* been processed i.e. on the transition to the situation
* where ProduceIndex == ConsumeIndex.
- */
-
- if ((status & ETH_INT_RXFIN) != 0)
- {
- EMAC_STAT(priv, rx_finished);
-#if 0 /* REVISIT: Reported to cause false alarm assertions */
- DEBUGASSERT(lpc17_getreg(LPC17_ETH_RXPRODIDX) == lpc17_getreg(LPC17_ETH_RXCONSIDX));
-#endif
- }
-
- /* RX DONE -- Triggered when a receive descriptor has been
+ *
+ * Treated as INT_RX_DONE if ProduceIndex != ConsumeIndex so the
+ * packets are processed anyway.
+ *
+ * RX DONE -- Triggered when a receive descriptor has been
* processed while the Interrupt bit in the Control field of
* the descriptor was set.
*/
- if ((status & ETH_INT_RXDONE) != 0)
+ if ((status & ETH_INT_RXFIN) != 0 || (status & ETH_INT_RXDONE) != 0)
{
EMAC_STAT(priv, rx_done);
/* We have received at least one new incoming packet. */
+#ifdef CONFIG_NET_WORKER_THREAD
+ work_queue(HPWORK, &priv->irqwork_rxdone,
+ (worker_t)lpc17_eth_irqworker_rxdone,
+ (FAR void *)priv, 0);
+
+#else /*CONFIG_NET_WORKER_THREAD*/
lpc17_rxdone(priv);
+
+#endif /*CONFIG_NET_WORKER_THREAD*/
}
/* Check for Tx events ********************************************/
@@ -1136,7 +1189,19 @@ static int lpc17_interrupt(int irq, void *context)
/* A packet transmission just completed */
+#ifdef CONFIG_NET_WORKER_THREAD
+ /* Disable the Ethernet interrupt for now, will be re-enabled
+ * later
+ */
+
+ work_queue(HPWORK, &priv->irqwork_txdone,
+ (worker_t)lpc17_eth_irqworker_txdone,
+ (FAR void *)priv, 0);
+
+#else /*CONFIG_NET_WORKER_THREAD*/
lpc17_txdone(priv);
+
+#endif /*CONFIG_NET_WORKER_THREAD*/
}
}
}
@@ -1215,6 +1280,8 @@ static void lpc17_txtimeout(int argc, uint32_t arg, ...)
static void lpc17_polltimer(int argc, uint32_t arg, ...)
{
struct lpc17_driver_s *priv = (struct lpc17_driver_s *)arg;
+ unsigned int prodidx;
+ unsigned int considx;
/* Check if there is room in the send another TX packet. We cannot perform
* the TX poll if he are unable to accept another packet for transmission.
@@ -1230,6 +1297,27 @@ static void lpc17_polltimer(int argc, uint32_t arg, ...)
(void)devif_timer(&priv->lp_dev, lpc17_txpoll, LPC17_POLLHSEC);
}
+ /* Simulate a fake receive to relaunch the data exchanges when a receive
+ * interrupt has been lost and all the receive buffers are used.
+ */
+
+ /* Get the current producer and consumer indices */
+
+ considx = lpc17_getreg(LPC17_ETH_RXCONSIDX) & ETH_RXCONSIDX_MASK;
+ prodidx = lpc17_getreg(LPC17_ETH_RXPRODIDX) & ETH_RXPRODIDX_MASK;
+
+ if (considx != prodidx)
+ {
+#if CONFIG_NET_WORKER_THREAD
+ work_queue(HPWORK, &priv->irqwork_rxdone,
+ (worker_t)lpc17_eth_irqworker_rxdone,
+ (FAR void *)priv, 0);
+
+#else /*CONFIG_NET_WORKER_THREAD*/
+ lpc17_rxdone(priv);
+
+#endif /*CONFIG_NET_WORKER_THREAD*/
+
/* Setup the watchdog poll timer again */
(void)wd_start(priv->lp_txpoll, LPC17_WDDELAY, lpc17_polltimer, 1, arg);
@@ -1466,13 +1554,17 @@ static int lpc17_ifdown(struct net_driver_s *dev)
static int lpc17_txavail(struct net_driver_s *dev)
{
struct lpc17_driver_s *priv = (struct lpc17_driver_s *)dev->d_private;
+#ifndef CONFIG_NET_WORKER_THREAD
irqstate_t flags;
+#endif /*CONFIG_NET_WORKER_THREAD*/
/* Disable interrupts because this function may be called from interrupt
* level processing.
*/
+#ifndef CONFIG_NET_WORKER_THREAD
flags = irqsave();
+#endif /*CONFIG_NET_WORKER_THREAD*/
/* Ignore the notification if the interface is not yet up */
@@ -1488,7 +1580,9 @@ static int lpc17_txavail(struct net_driver_s *dev)
}
}
+#ifndef CONFIG_NET_WORKER_THREAD
irqrestore(flags);
+#endif /*CONFIG_NET_WORKER_THREAD*/
return OK;
}
@@ -1845,7 +1939,12 @@ static int lpc17_phymode(uint8_t phyaddr, uint8_t mode)
for (timeout = MII_BIG_TIMEOUT; timeout > 0; timeout--)
{
-#ifdef CONFIG_ETH0_PHY_DP83848C
+ /* REVISIT: This should not depend explicity on the board configuration.
+ * Rather, there should be some additional configuration option to
+ * suppress this DP83848C-specific behavior.
+ */
+
+#if defined(CONFIG_ETH0_PHY_DP83848C) && !defined(CONFIG_ARCH_BOARD_MBED)
phyreg = lpc17_phyread(phyaddr, MII_DP83848C_STS);
if ((phyreg & 0x0001) != 0)
{