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

This commit is contained in:
Gregory Nutt 2014-07-24 16:39:18 -06:00
parent fdff663e57
commit 7f5b88dbcd
2 changed files with 133 additions and 21 deletions

View File

@ -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"

View File

@ -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);
}
}
@ -981,6 +992,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
*
@ -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)
{