From 1851e9e837db6775288346f2ed646e08205f9bae Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Fri, 2 Dec 2016 16:36:27 -0600 Subject: [PATCH] SAMA5D3: Add support for CONFIG_NET_NOINTS to EMACA and GMAC driver. --- TODO | 6 +- arch/arm/src/sama5/Kconfig | 40 ++ arch/arm/src/sama5/sam_emaca.c | 471 +++++++++++++++++++--- arch/arm/src/sama5/sam_gmac.c | 471 +++++++++++++++++++--- configs/sama5d3-xplained/bridge/defconfig | 14 +- 5 files changed, 901 insertions(+), 101 deletions(-) diff --git a/TODO b/TODO index cdb5fc5a1c5..2f930f21b1d 100644 --- a/TODO +++ b/TODO @@ -1,4 +1,4 @@ -NuttX TODO List (Last updated November 22, 2016) +NuttX TODO List (Last updated December 2, 2016) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ This file summarizes known NuttX bugs, limitations, inconsistencies with @@ -1067,9 +1067,9 @@ o Network (net/, drivers/net) PIC32 NO NO SAM3/4 YES YES SAMA5D ----------------------- ------ - EMACA NO YES (not tested) + EMACA YES YES (not tested) EMACB YES YES - GMAC NO YES (not tested) + GMAC YES YES (not tested) SAMV7 YES YES SIM N/A (No interrupts) NO diff --git a/arch/arm/src/sama5/Kconfig b/arch/arm/src/sama5/Kconfig index 1400aea5c96..d4ccc256be6 100644 --- a/arch/arm/src/sama5/Kconfig +++ b/arch/arm/src/sama5/Kconfig @@ -1437,6 +1437,26 @@ config SAMA5_GMAC_NBC ---help--- Select to disable receipt of broadcast packets. +choice + prompt "Work queue" + default SAMA5_GMAC_LPWORK if SCHED_LPWORK + default SAMA5_GMAC_HPWORK if !SCHED_LPWORK && SCHED_HPWORK + depends on SCHED_WORKQUEUE + ---help--- + Work queue support is required to use the Ethernet driver. If the + low priority work queue is available, then it should be used by the + driver. + +config SAMA5_GMAC_HPWORK + bool "High priority" + depends on SCHED_HPWORK + +config SAMA5_GMAC_LPWORK + bool "Low priority" + depends on SCHED_LPWORK + +endchoice # Work queue + config SAMA5_GMAC_PHYADDR int "PHY address" default 1 @@ -1675,6 +1695,26 @@ config SAMA5_EMACA_NBC ---help--- Select to disable receipt of broadcast packets. +choice + prompt "Work queue" + default SAMA5_EMACA_LPWORK if SCHED_LPWORK + default SAMA5_EMACA_HPWORK if !SCHED_LPWORK && SCHED_HPWORK + depends on SCHED_WORKQUEUE + ---help--- + Work queue support is required to use the Ethernet driver. If the + low priority work queue is available, then it should be used by the + driver. + +config SAMA5_EMACA_HPWORK + bool "High priority" + depends on SCHED_HPWORK + +config SAMA5_EMACA_LPWORK + bool "Low priority" + depends on SCHED_LPWORK + +endchoice # Work queue + config SAMA5_EMACA_REGDEBUG bool "Register-Level Debug" default n diff --git a/arch/arm/src/sama5/sam_emaca.c b/arch/arm/src/sama5/sam_emaca.c index 67715ee2045..c31ae128f88 100644 --- a/arch/arm/src/sama5/sam_emaca.c +++ b/arch/arm/src/sama5/sam_emaca.c @@ -4,7 +4,7 @@ * 10/100 Base-T Ethernet driver for the SAMA5D3. Denoted as 'A' to * distinguish it from the SAMA5D4 EMAC driver. * - * Copyright (C) 2013-2015 Gregory Nutt. All rights reserved. + * Copyright (C) 2013-2016 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * References: @@ -65,6 +65,11 @@ #include #include #include + +#ifdef CONFIG_NET_NOINTS +# include +#endif + #include #include #include @@ -94,6 +99,26 @@ ****************************************************************************/ /* Configuration ************************************************************/ +/* If processing is not done at the interrupt level, then work queue support + * is required. + */ + +#if defined(CONFIG_NET_NOINTS) && !defined(CONFIG_SCHED_WORKQUEUE) +# error Work queue support is required +#endif + +/* Select work queue */ + +#if defined(CONFIG_SCHED_WORKQUEUE) +# if defined(CONFIG_SAMA5_EMACA_HPWORK) +# define ETHWORK HPWORK +# elif defined(CONFIG_SAMA5_EMACA_LPWORK) +# define ETHWORK LPWORK +# else +# error Neither CONFIG_SAMA5_EMACA_HPWORK nor CONFIG_SAMA5_EMACA_LPWORK defined +# endif +#endif + /* Number of buffers for RX */ #ifndef CONFIG_SAMA5_EMAC_NRXBUFFERS @@ -255,6 +280,9 @@ struct sam_emac_s uint8_t ifup : 1; /* true:ifup false:ifdown */ WDOG_ID txpoll; /* TX poll timer */ WDOG_ID txtimeout; /* TX timeout timer */ +#ifdef CONFIG_NET_NOINTS + struct work_s work; /* For deferring work to the work queue */ +#endif /* This holds the information visible to the NuttX network */ @@ -363,17 +391,35 @@ static void sam_dopoll(struct sam_emac_s *priv); static int sam_recvframe(struct sam_emac_s *priv); static void sam_receive(struct sam_emac_s *priv); static void sam_txdone(struct sam_emac_s *priv); +static inline void sam_interrupt_process(FAR struct sam_emac_s *priv); +#ifdef CONFIG_NET_NOINTS +static void sam_interrupt_work(FAR void *arg); +#endif static int sam_emac_interrupt(int irq, void *context); /* Watchdog timer expirations */ -static void sam_polltimer(int argc, uint32_t arg, ...); -static void sam_txtimeout(int argc, uint32_t arg, ...); +static inline void sam_txtimeout_process(FAR struct sam_emac_s *priv); +#ifdef CONFIG_NET_NOINTS +static void sam_txtimeout_work(FAR void *arg); +#endif +static void sam_txtimeout_expiry(int argc, uint32_t arg, ...); + +static inline void sam_poll_process(FAR struct sam_emac_s *priv); +#ifdef CONFIG_NET_NOINTS +static void sam_poll_work(FAR void *arg); +#endif +static void sam_poll_expiry(int argc, uint32_t arg, ...); /* NuttX callback functions */ static int sam_ifup(struct net_driver_s *dev); static int sam_ifdown(struct net_driver_s *dev); + +static inline void sam_txavail_process(FAR struct sam_emac_s *priv); +#ifdef CONFIG_NET_NOINTS +static void sam_txavail_work(FAR void *arg); +#endif static int sam_txavail(struct net_driver_s *dev); #if defined(CONFIG_NET_IGMP) || defined(CONFIG_NET_ICMPv6) @@ -788,7 +834,7 @@ static int sam_transmit(struct sam_emac_s *priv) /* Setup the TX timeout watchdog (perhaps restarting the timer) */ - (void)wd_start(priv->txtimeout, SAM_TXTIMEOUT, sam_txtimeout, 1, + (void)wd_start(priv->txtimeout, SAM_TXTIMEOUT, sam_txtimeout_expiry, 1, (uint32_t)priv); /* Set d_len to zero meaning that the d_buf[] packet buffer is again @@ -903,7 +949,7 @@ static int sam_txpoll(struct net_driver_s *dev) * * 1. After completion of a transmission (sam_txdone), * 2. When new TX data is available (sam_txavail), and - * 3. After a TX timeout to restart the sending process (sam_txtimeout). + * 3. After a TX timeout to restart the sending process (sam_txtimeout_expiry). * * Parameters: * priv - Reference to the driver state structure @@ -1416,25 +1462,25 @@ static void sam_txdone(struct sam_emac_s *priv) } /**************************************************************************** - * Function: sam_emac_interrupt + * Function: sam_interrupt_process * * Description: - * Hardware interrupt handler + * Interrupt processing. This may be performed either within the interrupt + * handler or on the worker thread, depending upon the configuration * * Parameters: - * irq - Number of the IRQ that generated the interrupt - * context - Interrupt register state save info (architecture-specific) + * priv - Reference to the driver state structure * * Returned Value: - * OK on success + * None * * Assumptions: + * Ethernet interrupts are disabled * ****************************************************************************/ -static int sam_emac_interrupt(int irq, void *context) +static inline void sam_interrupt_process(FAR struct sam_emac_s *priv) { - struct sam_emac_s *priv = &g_emac; uint32_t isr; uint32_t rsr; uint32_t tsr; @@ -1597,12 +1643,184 @@ static int sam_emac_interrupt(int irq, void *context) nwarn("WARNING: Pause TO!\n"); } #endif +} + +/**************************************************************************** + * Function: sam_interrupt_work + * + * Description: + * Perform interrupt related work from the worker thread + * + * Parameters: + * arg - The argument passed when work_queue() was called. + * + * Returned Value: + * OK on success + * + * Assumptions: + * Ethernet interrupts are disabled + * + ****************************************************************************/ + +#ifdef CONFIG_NET_NOINTS +static void sam_interrupt_work(FAR void *arg) +{ + FAR struct sam_emac_s *priv = (FAR struct sam_emac_s *)arg; + net_lock_t state; + + /* Process pending Ethernet interrupts */ + + state = net_lock(); + sam_interrupt_process(priv); + net_unlock(state); + + /* Re-enable Ethernet interrupts */ + + up_enable_irq(SAM_IRQ_EMAC); +} +#endif + +/**************************************************************************** + * Function: sam_emac_interrupt + * + * Description: + * Hardware interrupt handler + * + * Parameters: + * irq - Number of the IRQ that generated the interrupt + * context - Interrupt register state save info (architecture-specific) + * + * Returned Value: + * OK on success + * + * Assumptions: + * + ****************************************************************************/ + +static int sam_emac_interrupt(int irq, void *context) +{ + struct sam_emac_s *priv = &g_emac; +#ifdef CONFIG_NET_NOINTS + uint32_t tsr; + + /* Disable further Ethernet interrupts. Because Ethernet interrupts are + * also disabled if the TX timeout event occurs, there can be no race + * condition here. + */ + + up_disable_irq(SAM_IRQ_EMAC); + + /* Check for the completion of a transmission. Careful: + * + * ISR:TCOMP is set when a frame has been transmitted. Cleared on read (so + * we cannot read it here). + * TSR:TXCOMP is set when a frame has been transmitted. Cleared by writing a + * one to this bit. + */ + + tsr = sam_getreg(priv, SAM_EMAC_TSR_OFFSET); + if ((tsr & EMAC_TSR_COMP) != 0) + { + /* If a TX transfer just completed, then cancel the TX timeout so + * there will be do race condition between any subsequent timeout + * expiration and the deferred interrupt processing. + */ + + wd_cancel(priv->txtimeout); + + /* Make sure that the TX poll timer is running (if it is already + * running, the following would restart it). This is necessary to + * avoid certain race conditions where the polling sequence can be + * interrupted. + */ + + (void)wd_start(priv->txpoll, SAM_WDDELAY, sam_poll_expiry, 1, priv); + } + + /* Cancel any pending poll work */ + + work_cancel(ETHWORK, &priv->work); + + /* Schedule to perform the interrupt processing on the worker thread. */ + + work_queue(ETHWORK, &priv->work, sam_interrupt_work, priv, 0); + +#else + /* Process the interrupt now */ + + sam_interrupt_process(priv); +#endif return OK; } /**************************************************************************** - * Function: sam_txtimeout + * Function: sam_txtimeout_process + * + * Description: + * Process a TX timeout. Called from the either the watchdog timer + * expiration logic or from the worker thread, depending upon the + * configuration. The timeout means that the last TX never completed. + * Reset the hardware and start again. + * + * Parameters: + * priv - Reference to the driver state structure + * + * Returned Value: + * None + * + * Assumptions: + * Global interrupts are disabled by the watchdog logic. + * + ****************************************************************************/ + +static inline void sam_txtimeout_process(FAR struct sam_emac_s *priv) +{ + nerr("ERROR: Timeout!\n"); + + /* Reset the hardware. Just take the interface down, then back up again. */ + + sam_ifdown(&priv->dev); + sam_ifup(&priv->dev); + + /* Then poll the network for new XMIT data */ + + sam_dopoll(priv); +} + +/**************************************************************************** + * Function: sam_txtimeout_work + * + * Description: + * Perform TX timeout related work from the worker thread + * + * Parameters: + * arg - The argument passed when work_queue() as called. + * + * Returned Value: + * OK on success + * + * Assumptions: + * Ethernet interrupts are disabled + * + ****************************************************************************/ + +#ifdef CONFIG_NET_NOINTS +static void sam_txtimeout_work(FAR void *arg) +{ + FAR struct sam_emac_s *priv = (FAR struct sam_emac_s *)arg; + net_lock_t state; + + /* Process pending Ethernet interrupts */ + + state = net_lock(); + sam_txtimeout_process(priv); + net_unlock(state); +} +#endif + +/**************************************************************************** + * Function: sam_txtimeout_expiry * * Description: * Our TX watchdog timed out. Called from the timer interrupt handler. @@ -1620,26 +1838,104 @@ static int sam_emac_interrupt(int irq, void *context) * ****************************************************************************/ -static void sam_txtimeout(int argc, uint32_t arg, ...) +static void sam_txtimeout_expiry(int argc, uint32_t arg, ...) { - struct sam_emac_s *priv = (struct sam_emac_s *)arg; + FAR struct sam_emac_s *priv = (FAR struct sam_emac_s *)arg; - nerr("ERROR: Timeout!\n"); - - /* Then reset the hardware. Just take the interface down, then back - * up again. +#ifdef CONFIG_NET_NOINTS + /* Disable further Ethernet interrupts. This will prevent some race + * conditions with interrupt work. There is still a potential race + * condition with interrupt work that is already queued and in progress. */ - sam_ifdown(&priv->dev); - sam_ifup(&priv->dev); + up_disable_irq(SAM_IRQ_EMAC); - /* Then poll the network for new XMIT data */ + /* Cancel any pending poll or interrupt work. This will have no effect + * on work that has already been started. + */ - sam_dopoll(priv); + work_cancel(ETHWORK, &priv->work); + + /* Schedule to perform the TX timeout processing on the worker thread. */ + + work_queue(ETHWORK, &priv->work, sam_txtimeout_work, priv, 0); +#else + /* Process the timeout now */ + + sam_txtimeout_process(priv); +#endif } /**************************************************************************** - * Function: sam_polltimer + * Function: sam_poll_process + * + * Description: + * Perform the periodic poll. This may be called either from watchdog + * timer logic or from the worker thread, depending upon the configuration. + * + * Parameters: + * priv - Reference to the driver state structure + * + * Returned Value: + * None + * + * Assumptions: + * + ****************************************************************************/ + +static inline void sam_poll_process(FAR struct sam_emac_s *priv) +{ + struct net_driver_s *dev = &priv->dev; + + /* Check if the there are any free TX descriptors. We cannot perform the + * TX poll if we do not have buffering for another packet. + */ + + if (sam_txfree(priv) > 0) + { + /* Update TCP timing states and poll the network for new XMIT data. */ + + (void)devif_timer(dev, sam_txpoll); + } + + /* Setup the watchdog poll timer again */ + + (void)wd_start(priv->txpoll, SAM_WDDELAY, sam_poll_expiry, 1, priv); +} + +/**************************************************************************** + * Function: sam_poll_work + * + * Description: + * Perform periodic polling from the worker thread + * + * Parameters: + * arg - The argument passed when work_queue() as called. + * + * Returned Value: + * OK on success + * + * Assumptions: + * Ethernet interrupts are disabled + * + ****************************************************************************/ + +#ifdef CONFIG_NET_NOINTS +static void sam_poll_work(FAR void *arg) +{ + FAR struct sam_emac_s *priv = (FAR struct sam_emac_s *)arg; + net_lock_t state; + + /* Perform the poll */ + + state = net_lock(); + sam_poll_process(priv); + net_unlock(state); +} +#endif + +/**************************************************************************** + * Function: sam_poll_expiry * * Description: * Periodic timer handler. Called from the timer interrupt handler. @@ -1656,25 +1952,35 @@ static void sam_txtimeout(int argc, uint32_t arg, ...) * ****************************************************************************/ -static void sam_polltimer(int argc, uint32_t arg, ...) +static void sam_poll_expiry(int argc, uint32_t arg, ...) { - struct sam_emac_s *priv = (struct sam_emac_s *)arg; - struct net_driver_s *dev = &priv->dev; + FAR struct sam_emac_s *priv = (FAR struct sam_emac_s *)arg; - /* Check if the there are any free TX descriptors. We cannot perform the - * TX poll if we do not have buffering for another packet. +#ifdef CONFIG_NET_NOINTS + /* Is our single work structure available? It may not be if there are + * pending interrupt actions. */ - if (sam_txfree(priv) > 0) + if (work_available(&priv->work)) { - /* Update TCP timing states and poll the network for new XMIT data. */ + /* Schedule to perform the interrupt processing on the worker thread. */ - (void)devif_timer(dev, sam_txpoll); + work_queue(ETHWORK, &priv->work, sam_poll_work, priv, 0); + } + else + { + /* No.. Just re-start the watchdog poll timer, missing one polling + * cycle. + */ + + (void)wd_start(priv->txpoll, SAM_WDDELAY, sam_poll_expiry, 1, arg); } - /* Setup the watchdog poll timer again */ +#else + /* Process the interrupt now */ - (void)wd_start(priv->txpoll, SAM_WDDELAY, sam_polltimer, 1, arg); + sam_poll_process(priv); +#endif } /**************************************************************************** @@ -1745,7 +2051,7 @@ static int sam_ifup(struct net_driver_s *dev) /* Set and activate a timer process */ - (void)wd_start(priv->txpoll, SAM_WDDELAY, sam_polltimer, 1, (uint32_t)priv); + (void)wd_start(priv->txpoll, SAM_WDDELAY, sam_poll_expiry, 1, (uint32_t)priv); /* Enable the EMAC interrupt */ @@ -1801,6 +2107,68 @@ static int sam_ifdown(struct net_driver_s *dev) return OK; } +/**************************************************************************** + * Function: sam_txavail_process + * + * Description: + * Perform an out-of-cycle poll. + * + * Parameters: + * dev - Reference to the NuttX driver state structure + * + * Returned Value: + * None + * + * Assumptions: + * Called in normal user mode + * + ****************************************************************************/ + +static inline void sam_txavail_process(FAR struct sam_emac_s *priv) +{ + ninfo("ifup: %d\n", priv->ifup); + + /* Ignore the notification if the interface is not yet up */ + + if (priv->ifup) + { + /* Poll the network for new XMIT data */ + + sam_dopoll(priv); + } +} + +/**************************************************************************** + * Function: sam_txavail_work + * + * Description: + * Perform an out-of-cycle poll on the worker thread. + * + * Parameters: + * arg - Reference to the NuttX driver state structure (cast to void*) + * + * Returned Value: + * None + * + * Assumptions: + * Called on the higher priority worker thread. + * + ****************************************************************************/ + +#ifdef CONFIG_NET_NOINTS +static void sam_txavail_work(FAR void *arg) +{ + FAR struct sam_emac_s *priv = (FAR struct sam_emac_s *)arg; + net_lock_t state; + + /* Perform the poll */ + + state = net_lock(); + sam_txavail_process(priv); + net_unlock(state); +} +#endif + /**************************************************************************** * Function: sam_txavail * @@ -1810,7 +2178,7 @@ static int sam_ifdown(struct net_driver_s *dev) * latency. * * Parameters: - * dev - Reference to the NuttX driver state structure + * dev - Reference to the NuttX driver state structure * * Returned Value: * None @@ -1822,10 +2190,23 @@ static int sam_ifdown(struct net_driver_s *dev) static int sam_txavail(struct net_driver_s *dev) { - struct sam_emac_s *priv = (struct sam_emac_s *)dev->d_private; - irqstate_t flags; + FAR struct sam_emac_s *priv = (FAR struct sam_emac_s *)dev->d_private; - ninfo("ifup: %d\n", priv->ifup); +#ifdef CONFIG_NET_NOINTS + /* Is our single work structure available? It may not be if there are + * pending interrupt actions and we will have to ignore the Tx + * availability action. + */ + + if (work_available(&priv->work)) + { + /* Schedule to serialize the poll on the worker thread. */ + + work_queue(ETHWORK, &priv->work, sam_txavail_work, priv, 0); + } + +#else + irqstate_t flags; /* Disable interrupts because this function may be called from interrupt * level processing. @@ -1833,16 +2214,12 @@ static int sam_txavail(struct net_driver_s *dev) flags = enter_critical_section(); - /* Ignore the notification if the interface is not yet up */ - - if (priv->ifup) - { - /* Poll the network for new XMIT data */ - - sam_dopoll(priv); - } + /* Perform the out-of-cycle poll now */ + sam_txavail_process(priv); leave_critical_section(flags); +#endif + return OK; } diff --git a/arch/arm/src/sama5/sam_gmac.c b/arch/arm/src/sama5/sam_gmac.c index d2642821176..d3450dc2be7 100644 --- a/arch/arm/src/sama5/sam_gmac.c +++ b/arch/arm/src/sama5/sam_gmac.c @@ -1,7 +1,7 @@ /**************************************************************************** * arch/arm/src/sama5/sam_gmac.c * - * Copyright (C) 2013-2015 Gregory Nutt. All rights reserved. + * Copyright (C) 2013-2016 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * References: @@ -62,6 +62,11 @@ #include #include #include + +#ifdef CONFIG_NET_NOINTS +# include +#endif + #include #include #include @@ -91,6 +96,26 @@ ****************************************************************************/ /* Configuration ************************************************************/ +/* If processing is not done at the interrupt level, then work queue support + * is required. + */ + +#if defined(CONFIG_NET_NOINTS) && !defined(CONFIG_SCHED_WORKQUEUE) +# error Work queue support is required +#endif + +/* Select work queue */ + +#if defined(CONFIG_SCHED_WORKQUEUE) +# if defined(CONFIG_SAMA5_GMAC_HPWORK) +# define ETHWORK HPWORK +# elif defined(CONFIG_SAMA5_GMAC_LPWORK) +# define ETHWORK LPWORK +# else +# error Neither CONFIG_SAMA5_GMAC_HPWORK nor CONFIG_SAMA5_GMAC_LPWORK defined +# endif +#endif + /* Number of buffer for RX */ #ifndef CONFIG_SAMA5_GMAC_NRXBUFFERS @@ -181,6 +206,9 @@ struct sam_gmac_s uint8_t ifup : 1; /* true:ifup false:ifdown */ WDOG_ID txpoll; /* TX poll timer */ WDOG_ID txtimeout; /* TX timeout timer */ +#ifdef CONFIG_NET_NOINTS + struct work_s work; /* For deferring work to the work queue */ +#endif /* This holds the information visible to the NuttX network */ @@ -288,17 +316,35 @@ static void sam_dopoll(struct sam_gmac_s *priv); static int sam_recvframe(struct sam_gmac_s *priv); static void sam_receive(struct sam_gmac_s *priv); static void sam_txdone(struct sam_gmac_s *priv); +static inline void sam_interrupt_process(FAR struct sam_gmac_s *priv); +#ifdef CONFIG_NET_NOINTS +static void sam_interrupt_work(FAR void *arg); +#endif static int sam_gmac_interrupt(int irq, void *context); /* Watchdog timer expirations */ -static void sam_polltimer(int argc, uint32_t arg, ...); -static void sam_txtimeout(int argc, uint32_t arg, ...); +static inline void sam_txtimeout_process(FAR struct sam_gmac_s *priv); +#ifdef CONFIG_NET_NOINTS +static void sam_txtimeout_work(FAR void *arg); +#endif +static void sam_txtimeout_expiry(int argc, uint32_t arg, ...); + +static inline void sam_poll_process(FAR struct sam_gmac_s *priv); +#ifdef CONFIG_NET_NOINTS +static void sam_poll_work(FAR void *arg); +#endif +static void sam_poll_expiry(int argc, uint32_t arg, ...); /* NuttX callback functions */ static int sam_ifup(struct net_driver_s *dev); static int sam_ifdown(struct net_driver_s *dev); + +static inline void sam_txavail_process(FAR struct sam_gmac_s *priv); +#ifdef CONFIG_NET_NOINTS +static void sam_txavail_work(FAR void *arg); +#endif static int sam_txavail(struct net_driver_s *dev); #if defined(CONFIG_NET_IGMP) || defined(CONFIG_NET_ICMPv6) @@ -720,7 +766,7 @@ static int sam_transmit(struct sam_gmac_s *priv) /* Setup the TX timeout watchdog (perhaps restarting the timer) */ - (void)wd_start(priv->txtimeout, SAM_TXTIMEOUT, sam_txtimeout, 1, + (void)wd_start(priv->txtimeout, SAM_TXTIMEOUT, sam_txtimeout_expiry, 1, (uint32_t)priv); /* Set d_len to zero meaning that the d_buf[] packet buffer is again @@ -835,7 +881,7 @@ static int sam_txpoll(struct net_driver_s *dev) * * 1. After completion of a transmission (sam_txdone), * 2. When new TX data is available (sam_txavail), and - * 3. After a TX timeout to restart the sending process (sam_txtimeout). + * 3. After a TX timeout to restart the sending process (sam_txtimeout_expiry). * * Parameters: * priv - Reference to the driver state structure @@ -1344,25 +1390,25 @@ static void sam_txdone(struct sam_gmac_s *priv) } /**************************************************************************** - * Function: sam_gmac_interrupt + * Function: sam_interrupt_process * * Description: - * Hardware interrupt handler + * Interrupt processing. This may be performed either within the interrupt + * handler or on the worker thread, depending upon the configuration * * Parameters: - * irq - Number of the IRQ that generated the interrupt - * context - Interrupt register state save info (architecture-specific) + * priv - Reference to the driver state structure * * Returned Value: - * OK on success + * None * * Assumptions: + * Ethernet interrupts are disabled * ****************************************************************************/ -static int sam_gmac_interrupt(int irq, void *context) +static inline void sam_interrupt_process(FAR struct sam_gmac_s *priv) { - struct sam_gmac_s *priv = &g_gmac; uint32_t isr; uint32_t rsr; uint32_t tsr; @@ -1549,12 +1595,184 @@ static int sam_gmac_interrupt(int irq, void *context) nwarn("WARNING: Pause TO!\n"); } #endif +} + +/**************************************************************************** + * Function: sam_interrupt_work + * + * Description: + * Perform interrupt related work from the worker thread + * + * Parameters: + * arg - The argument passed when work_queue() was called. + * + * Returned Value: + * OK on success + * + * Assumptions: + * Ethernet interrupts are disabled + * + ****************************************************************************/ + +#ifdef CONFIG_NET_NOINTS +static void sam_interrupt_work(FAR void *arg) +{ + FAR struct sam_gmac_s *priv = (FAR struct sam_gmac_s *)arg; + net_lock_t state; + + /* Process pending Ethernet interrupts */ + + state = net_lock(); + sam_interrupt_process(priv); + net_unlock(state); + + /* Re-enable Ethernet interrupts */ + + up_enable_irq(SAM_IRQ_GMAC); +} +#endif + +/**************************************************************************** + * Function: sam_gmac_interrupt + * + * Description: + * Hardware interrupt handler + * + * Parameters: + * irq - Number of the IRQ that generated the interrupt + * context - Interrupt register state save info (architecture-specific) + * + * Returned Value: + * OK on success + * + * Assumptions: + * + ****************************************************************************/ + +static int sam_gmac_interrupt(int irq, void *context) +{ + struct sam_gmac_s *priv = &g_gmac; +#ifdef CONFIG_NET_NOINTS + uint32_t tsr; + + /* Disable further Ethernet interrupts. Because Ethernet interrupts are + * also disabled if the TX timeout event occurs, there can be no race + * condition here. + */ + + up_disable_irq(SAM_IRQ_GMAC); + + /* Check for the completion of a transmission. Careful: + * + * ISR:TCOMP is set when a frame has been transmitted. Cleared on read (so + * we cannot read it here). + * TSR:TXCOMP is set when a frame has been transmitted. Cleared by writing a + * one to this bit. + */ + + tsr = sam_getreg(priv, SAM_GMAC_TSR_OFFSET); + if ((tsr & GMAC_TSR_TXCOMP) != 0) + { + /* If a TX transfer just completed, then cancel the TX timeout so + * there will be do race condition between any subsequent timeout + * expiration and the deferred interrupt processing. + */ + + wd_cancel(priv->txtimeout); + + /* Make sure that the TX poll timer is running (if it is already + * running, the following would restart it). This is necessary to + * avoid certain race conditions where the polling sequence can be + * interrupted. + */ + + (void)wd_start(priv->txpoll, SAM_WDDELAY, sam_poll_expiry, 1, priv); + } + + /* Cancel any pending poll work */ + + work_cancel(ETHWORK, &priv->work); + + /* Schedule to perform the interrupt processing on the worker thread. */ + + work_queue(ETHWORK, &priv->work, sam_interrupt_work, priv, 0); + +#else + /* Process the interrupt now */ + + sam_interrupt_process(priv); +#endif return OK; } /**************************************************************************** - * Function: sam_txtimeout + * Function: sam_txtimeout_process + * + * Description: + * Process a TX timeout. Called from the either the watchdog timer + * expiration logic or from the worker thread, depending upon the + * configuration. The timeout means that the last TX never completed. + * Reset the hardware and start again. + * + * Parameters: + * priv - Reference to the driver state structure + * + * Returned Value: + * None + * + * Assumptions: + * Global interrupts are disabled by the watchdog logic. + * + ****************************************************************************/ + +static inline void sam_txtimeout_process(FAR struct sam_gmac_s *priv) +{ + nerr("ERROR: Timeout!\n"); + + /* Reset the hardware. Just take the interface down, then back up again. */ + + sam_ifdown(&priv->dev); + sam_ifup(&priv->dev); + + /* Then poll the network for new XMIT data */ + + sam_dopoll(priv); +} + +/**************************************************************************** + * Function: sam_txtimeout_work + * + * Description: + * Perform TX timeout related work from the worker thread + * + * Parameters: + * arg - The argument passed when work_queue() as called. + * + * Returned Value: + * OK on success + * + * Assumptions: + * Ethernet interrupts are disabled + * + ****************************************************************************/ + +#ifdef CONFIG_NET_NOINTS +static void sam_txtimeout_work(FAR void *arg) +{ + FAR struct sam_gmac_s *priv = (FAR struct sam_gmac_s *)arg; + net_lock_t state; + + /* Process pending Ethernet interrupts */ + + state = net_lock(); + sam_txtimeout_process(priv); + net_unlock(state); +} +#endif + +/**************************************************************************** + * Function: sam_txtimeout_expiry * * Description: * Our TX watchdog timed out. Called from the timer interrupt handler. @@ -1572,26 +1790,104 @@ static int sam_gmac_interrupt(int irq, void *context) * ****************************************************************************/ -static void sam_txtimeout(int argc, uint32_t arg, ...) +static void sam_txtimeout_expiry(int argc, uint32_t arg, ...) { - struct sam_gmac_s *priv = (struct sam_gmac_s *)arg; + FAR struct sam_gmac_s *priv = (FAR struct sam_gmac_s *)arg; - nerr("ERROR: Timeout!\n"); - - /* Then reset the hardware. Just take the interface down, then back - * up again. +#ifdef CONFIG_NET_NOINTS + /* Disable further Ethernet interrupts. This will prevent some race + * conditions with interrupt work. There is still a potential race + * condition with interrupt work that is already queued and in progress. */ - sam_ifdown(&priv->dev); - sam_ifup(&priv->dev); + up_disable_irq(SAM_IRQ_GMAC); - /* Then poll the network for new XMIT data */ + /* Cancel any pending poll or interrupt work. This will have no effect + * on work that has already been started. + */ - sam_dopoll(priv); + work_cancel(ETHWORK, &priv->work); + + /* Schedule to perform the TX timeout processing on the worker thread. */ + + work_queue(ETHWORK, &priv->work, sam_txtimeout_work, priv, 0); +#else + /* Process the timeout now */ + + sam_txtimeout_process(priv); +#endif } /**************************************************************************** - * Function: sam_polltimer + * Function: sam_poll_process + * + * Description: + * Perform the periodic poll. This may be called either from watchdog + * timer logic or from the worker thread, depending upon the configuration. + * + * Parameters: + * priv - Reference to the driver state structure + * + * Returned Value: + * None + * + * Assumptions: + * + ****************************************************************************/ + +static inline void sam_poll_process(FAR struct sam_gmac_s *priv) +{ + struct net_driver_s *dev = &priv->dev; + + /* Check if the there are any free TX descriptors. We cannot perform the + * TX poll if we do not have buffering for another packet. + */ + + if (sam_txfree(priv) > 0) + { + /* Update TCP timing states and poll the network for new XMIT data. */ + + (void)devif_timer(dev, sam_txpoll); + } + + /* Setup the watchdog poll timer again */ + + (void)wd_start(priv->txpoll, SAM_WDDELAY, sam_poll_expiry, 1, priv); +} + +/**************************************************************************** + * Function: sam_poll_work + * + * Description: + * Perform periodic polling from the worker thread + * + * Parameters: + * arg - The argument passed when work_queue() as called. + * + * Returned Value: + * OK on success + * + * Assumptions: + * Ethernet interrupts are disabled + * + ****************************************************************************/ + +#ifdef CONFIG_NET_NOINTS +static void sam_poll_work(FAR void *arg) +{ + FAR struct sam_gmac_s *priv = (FAR struct sam_gmac_s *)arg; + net_lock_t state; + + /* Perform the poll */ + + state = net_lock(); + sam_poll_process(priv); + net_unlock(state); +} +#endif + +/**************************************************************************** + * Function: sam_poll_expiry * * Description: * Periodic timer handler. Called from the timer interrupt handler. @@ -1608,25 +1904,35 @@ static void sam_txtimeout(int argc, uint32_t arg, ...) * ****************************************************************************/ -static void sam_polltimer(int argc, uint32_t arg, ...) +static void sam_poll_expiry(int argc, uint32_t arg, ...) { - struct sam_gmac_s *priv = (struct sam_gmac_s *)arg; - struct net_driver_s *dev = &priv->dev; + FAR struct sam_gmac_s *priv = (FAR struct sam_gmac_s *)arg; - /* Check if the there are any free TX descriptors. We cannot perform the - * TX poll if we do not have buffering for another packet. +#ifdef CONFIG_NET_NOINTS + /* Is our single work structure available? It may not be if there are + * pending interrupt actions. */ - if (sam_txfree(priv) > 0) + if (work_available(&priv->work)) { - /* Update TCP timing states and poll the network for new XMIT data. */ + /* Schedule to perform the interrupt processing on the worker thread. */ - (void)devif_timer(dev, sam_txpoll); + work_queue(ETHWORK, &priv->work, sam_poll_work, priv, 0); + } + else + { + /* No.. Just re-start the watchdog poll timer, missing one polling + * cycle. + */ + + (void)wd_start(priv->txpoll, SAM_WDDELAY, sam_poll_expiry, 1, arg); } - /* Setup the watchdog poll timer again */ +#else + /* Process the interrupt now */ - (void)wd_start(priv->txpoll, SAM_WDDELAY, sam_polltimer, 1, arg); + sam_poll_process(priv); +#endif } /**************************************************************************** @@ -1700,7 +2006,7 @@ static int sam_ifup(struct net_driver_s *dev) /* Set and activate a timer process */ - (void)wd_start(priv->txpoll, SAM_WDDELAY, sam_polltimer, 1, (uint32_t)priv); + (void)wd_start(priv->txpoll, SAM_WDDELAY, sam_poll_expiry, 1, (uint32_t)priv); /* Enable the GMAC interrupt */ @@ -1756,6 +2062,68 @@ static int sam_ifdown(struct net_driver_s *dev) return OK; } +/**************************************************************************** + * Function: sam_txavail_process + * + * Description: + * Perform an out-of-cycle poll. + * + * Parameters: + * dev - Reference to the NuttX driver state structure + * + * Returned Value: + * None + * + * Assumptions: + * Called in normal user mode + * + ****************************************************************************/ + +static inline void sam_txavail_process(FAR struct sam_gmac_s *priv) +{ + ninfo("ifup: %d\n", priv->ifup); + + /* Ignore the notification if the interface is not yet up */ + + if (priv->ifup) + { + /* Poll the network for new XMIT data */ + + sam_dopoll(priv); + } +} + +/**************************************************************************** + * Function: sam_txavail_work + * + * Description: + * Perform an out-of-cycle poll on the worker thread. + * + * Parameters: + * arg - Reference to the NuttX driver state structure (cast to void*) + * + * Returned Value: + * None + * + * Assumptions: + * Called on the higher priority worker thread. + * + ****************************************************************************/ + +#ifdef CONFIG_NET_NOINTS +static void sam_txavail_work(FAR void *arg) +{ + FAR struct sam_gmac_s *priv = (FAR struct sam_gmac_s *)arg; + net_lock_t state; + + /* Perform the poll */ + + state = net_lock(); + sam_txavail_process(priv); + net_unlock(state); +} +#endif + /**************************************************************************** * Function: sam_txavail * @@ -1765,7 +2133,7 @@ static int sam_ifdown(struct net_driver_s *dev) * latency. * * Parameters: - * dev - Reference to the NuttX driver state structure + * dev - Reference to the NuttX driver state structure * * Returned Value: * None @@ -1777,10 +2145,23 @@ static int sam_ifdown(struct net_driver_s *dev) static int sam_txavail(struct net_driver_s *dev) { - struct sam_gmac_s *priv = (struct sam_gmac_s *)dev->d_private; - irqstate_t flags; + FAR struct sam_gmac_s *priv = (FAR struct sam_gmac_s *)dev->d_private; - ninfo("ifup: %d\n", priv->ifup); +#ifdef CONFIG_NET_NOINTS + /* Is our single work structure available? It may not be if there are + * pending interrupt actions and we will have to ignore the Tx + * availability action. + */ + + if (work_available(&priv->work)) + { + /* Schedule to serialize the poll on the worker thread. */ + + work_queue(ETHWORK, &priv->work, sam_txavail_work, priv, 0); + } + +#else + irqstate_t flags; /* Disable interrupts because this function may be called from interrupt * level processing. @@ -1788,16 +2169,12 @@ static int sam_txavail(struct net_driver_s *dev) flags = enter_critical_section(); - /* Ignore the notification if the interface is not yet up */ - - if (priv->ifup) - { - /* Poll the network for new XMIT data */ - - sam_dopoll(priv); - } + /* Perform the out-of-cycle poll now */ + sam_txavail_process(priv); leave_critical_section(flags); +#endif + return OK; } diff --git a/configs/sama5d3-xplained/bridge/defconfig b/configs/sama5d3-xplained/bridge/defconfig index a1a3f374923..ea5c1c4fc61 100644 --- a/configs/sama5d3-xplained/bridge/defconfig +++ b/configs/sama5d3-xplained/bridge/defconfig @@ -279,6 +279,7 @@ CONFIG_SAMA5_GMAC_NRXBUFFERS=16 CONFIG_SAMA5_GMAC_NTXBUFFERS=8 # CONFIG_SAMA5_GMAC_PREALLOCATE is not set # CONFIG_SAMA5_GMAC_NBC is not set +CONFIG_SAMA5_GMAC_HPWORK=y CONFIG_SAMA5_GMAC_PHYADDR=1 # CONFIG_SAMA5_GMAC_PHYINIT is not set CONFIG_SAMA5_GMAC_AUTONEG=y @@ -302,6 +303,7 @@ CONFIG_SAMA5_EMAC_PHYSR_10FD=0x5 CONFIG_SAMA5_EMAC_PHYSR_100FD=0x6 # CONFIG_SAMA5_EMACA_PREALLOCATE is not set # CONFIG_SAMA5_EMACA_NBC is not set +CONFIG_SAMA5_EMACA_HPWORK=y # CONFIG_SAMA5_EMAC_ISETH0 is not set CONFIG_SAMA5_GMAC_ISETH0=y @@ -481,6 +483,7 @@ CONFIG_NAME_MAX=32 # CONFIG_SCHED_STARTHOOK is not set # CONFIG_SCHED_ATEXIT is not set # CONFIG_SCHED_ONEXIT is not set +# CONFIG_SIG_EVTHREAD is not set # # Signal Numbers @@ -489,6 +492,7 @@ CONFIG_SIG_SIGUSR1=1 CONFIG_SIG_SIGUSR2=2 CONFIG_SIG_SIGALARM=3 CONFIG_SIG_SIGCONDTIMEDOUT=16 +CONFIG_SIG_SIGWORK=17 # # POSIX Message Queue Options @@ -500,8 +504,11 @@ CONFIG_MQ_MAXMSGSIZE=32 # # Work queue support # -# CONFIG_SCHED_WORKQUEUE is not set -# CONFIG_SCHED_HPWORK is not set +CONFIG_SCHED_WORKQUEUE=y +CONFIG_SCHED_HPWORK=y +CONFIG_SCHED_HPWORKPRIORITY=224 +CONFIG_SCHED_HPWORKPERIOD=50000 +CONFIG_SCHED_HPWORKSTACKSIZE=2048 # CONFIG_SCHED_LPWORK is not set # @@ -592,7 +599,6 @@ CONFIG_NETDEV_MULTINIC=y # CONFIG_NET_DM90x0 is not set # CONFIG_ENC28J60 is not set # CONFIG_ENCX24J600 is not set - # CONFIG_NET_SLIP is not set # CONFIG_NET_FTMAC100 is not set @@ -692,7 +698,7 @@ CONFIG_SYSLOG_CONSOLE=y CONFIG_ARCH_HAVE_NET=y CONFIG_ARCH_HAVE_PHY=y CONFIG_NET=y -# CONFIG_NET_NOINTS is not set +CONFIG_NET_NOINTS=y # CONFIG_NET_PROMISCUOUS is not set #