diff options
author | Gregory Nutt <gnutt@nuttx.org> | 2015-02-09 15:26:05 -0600 |
---|---|---|
committer | Gregory Nutt <gnutt@nuttx.org> | 2015-02-09 15:26:05 -0600 |
commit | 28801220aee251b542a9f31223a159b5fb95c6f4 (patch) | |
tree | 58e3903b2e74f976a73f3e11de6bb6eeba894627 | |
parent | 5367a46ad6b117631f0d7ef96eaf243911a086bf (diff) | |
download | px4-nuttx-28801220aee251b542a9f31223a159b5fb95c6f4.tar.gz px4-nuttx-28801220aee251b542a9f31223a159b5fb95c6f4.tar.bz2 px4-nuttx-28801220aee251b542a9f31223a159b5fb95c6f4.zip |
SAMA5D Ethernet: Add support for CONFIG_NET_NOTINTS so that the driver can operate from the work queue thread instead of doing everything from the interrupt level.
-rw-r--r-- | nuttx/arch/arm/src/sama5/sam_emacb.c | 447 |
1 files changed, 406 insertions, 41 deletions
diff --git a/nuttx/arch/arm/src/sama5/sam_emacb.c b/nuttx/arch/arm/src/sama5/sam_emacb.c index bf01cba3f..caffa8c4b 100644 --- a/nuttx/arch/arm/src/sama5/sam_emacb.c +++ b/nuttx/arch/arm/src/sama5/sam_emacb.c @@ -79,6 +79,11 @@ #include <nuttx/irq.h> #include <nuttx/wdog.h> #include <nuttx/kmalloc.h> + +#ifdef CONFIG_NET_NOINTS +# include <nuttx/wqueue.h> +#endif + #include <nuttx/net/mii.h> #include <nuttx/net/arp.h> #include <nuttx/net/netdev.h> @@ -107,6 +112,13 @@ * Pre-processor Definitions ****************************************************************************/ /* EMAC0 Configuration ******************************************************/ +/* If processing is not done at the interrupt level, then high priority + * work queue support is required. + */ + +#if defined(CONFIG_NET_NOINTS) && !defined(CONFIG_SCHED_HPWORK) +# error High priority work queue support is required +#endif #ifdef CONFIG_SAMA5_EMAC0 /* Number of buffers for RX */ @@ -403,6 +415,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 uIP/NuttX */ @@ -468,6 +483,10 @@ 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(struct sam_emac_s *priv); #ifdef CONFIG_SAMA5_EMAC0 static int sam_emac0_interrupt(int irq, void *context); @@ -478,14 +497,29 @@ static int sam_emac1_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) static unsigned int sam_hashindx(const uint8_t *mac); static int sam_addmac(struct net_driver_s *dev, const uint8_t *mac); @@ -1114,7 +1148,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 @@ -1760,22 +1794,24 @@ static void sam_txdone(struct sam_emac_s *priv) } /**************************************************************************** - * Function: sam_emac_interrupt + * Function: sam_interrupt_process * * Description: - * Common hardware interrupt handler + * Interrupt processing. This may be performed either within the interrupt + * handler or on the worker thread, depending upon the configuration * * Parameters: - * priv - Reference to the EMAC private state structure + * priv - Reference to the driver state structure * * Returned Value: - * OK on success + * None * * Assumptions: + * Ethernet interrupts are disabled * ****************************************************************************/ -static int sam_emac_interrupt(struct sam_emac_s *priv) +static inline void sam_interrupt_process(FAR struct sam_emac_s *priv) { uint32_t isr; uint32_t rsr; @@ -1940,6 +1976,103 @@ static int sam_emac_interrupt(struct sam_emac_s *priv) nlldbg("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(priv->attr->irq); +} +#endif + +/**************************************************************************** + * Function: sam_emac_interrupt + * + * Description: + * Common hardware interrupt handler + * + * Parameters: + * priv - Reference to the EMAC private state structure + * + * Returned Value: + * OK on success + * + * Assumptions: + * + ****************************************************************************/ + +static int sam_emac_interrupt(struct sam_emac_s *priv) +{ +#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(priv->attr->irq); + + /* 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_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); + } + + /* Cancel any pending poll work */ + + work_cancel(HPWORK, &priv->work); + + /* Schedule to perform the interrupt processing on the worker thread. */ + + work_queue(HPWORK, &priv->work, sam_interrupt_work, priv, 0); + +#else + /* Process the interrupt now */ + + sam_interrupt_process(priv); +#endif return OK; } @@ -1976,15 +2109,16 @@ static int sam_emac1_interrupt(int irq, void *context) #endif /**************************************************************************** - * Function: sam_txtimeout + * Function: sam_txtimeout_process * * Description: - * Our TX watchdog timed out. Called from the timer interrupt handler. - * The last TX never completed. Reset the hardware and start again. + * 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: - * argc - The number of available arguments - * arg - The first argument + * priv - Reference to the driver state structure * * Returned Value: * None @@ -1994,15 +2128,11 @@ static int sam_emac1_interrupt(int irq, void *context) * ****************************************************************************/ -static void sam_txtimeout(int argc, uint32_t arg, ...) +static inline void sam_txtimeout_process(FAR struct sam_emac_s *priv) { - struct sam_emac_s *priv = (struct sam_emac_s *)arg; - nlldbg("Timeout!\n"); - /* Then reset the hardware. Just take the interface down, then back - * up again. - */ + /* Reset the hardware. Just take the interface down, then back up again. */ sam_ifdown(&priv->dev); sam_ifup(&priv->dev); @@ -2013,10 +2143,42 @@ static void sam_txtimeout(int argc, uint32_t arg, ...) } /**************************************************************************** - * Function: sam_polltimer + * Function: sam_txtimeout_work * * Description: - * Periodic timer handler. Called from the timer interrupt handler. + * 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. + * The last TX never completed. Reset the hardware and start again. * * Parameters: * argc - The number of available arguments @@ -2030,10 +2192,54 @@ static void sam_txtimeout(int argc, uint32_t arg, ...) * ****************************************************************************/ -static void sam_polltimer(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; - struct net_driver_s *dev = &priv->dev; + FAR struct sam_emac_s *priv = (FAR struct sam_emac_s *)arg; + +#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. + */ + + up_disable_irq(priv->attr->irq); + + /* Cancel any pending poll or interrupt work. This will have no effect + * on work that has already been started. + */ + + work_cancel(HPWORK, &priv->work); + + /* Schedule to perform the TX timeout processing on the worker thread. */ + + work_queue(HPWORK, &priv->work, sam_txtimeout_work, priv, 0); +#else + /* Process the timeout now */ + + sam_txtimeout_process(priv); +#endif +} + +/**************************************************************************** + * 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. @@ -2048,7 +2254,87 @@ static void sam_polltimer(int argc, uint32_t arg, ...) /* Setup the watchdog poll timer again */ - (void)wd_start(priv->txpoll, SAM_WDDELAY, sam_polltimer, 1, arg); + (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. + * + * Parameters: + * argc - The number of available arguments + * arg - The first argument + * + * Returned Value: + * None + * + * Assumptions: + * Global interrupts are disabled by the watchdog logic. + * + ****************************************************************************/ + +static void sam_poll_expiry(int argc, uint32_t arg, ...) +{ + FAR struct sam_emac_s *priv = (FAR struct sam_emac_s *)arg; + +#ifdef CONFIG_NET_NOINTS + /* Is our single work structure available? It may not be if there are + * pending interrupt actions. + */ + + if (work_available(&priv->work)) + { + /* Schedule to perform the interrupt processing on the worker thread. */ + + work_queue(HPWORK, &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); + } + +#else + /* Process the interrupt now */ + + sam_poll_process(priv); +#endif } /**************************************************************************** @@ -2073,9 +2359,17 @@ static int sam_ifup(struct net_driver_s *dev) struct sam_emac_s *priv = (struct sam_emac_s *)dev->d_private; int ret; - nlldbg("Bringing up: %d.%d.%d.%d\n", - dev->d_ipaddr & 0xff, (dev->d_ipaddr >> 8) & 0xff, - (dev->d_ipaddr >> 16) & 0xff, dev->d_ipaddr >> 24); +#ifdef CONFIG_NET_IPv4 + ndbg("Bringing up: %d.%d.%d.%d\n", + dev->d_ipaddr & 0xff, (dev->d_ipaddr >> 8) & 0xff, + (dev->d_ipaddr >> 16) & 0xff, dev->d_ipaddr >> 24); +#endif +#ifdef CONFIG_NET_IPv6 + ndbg("Bringing up: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n", + dev->d_ipv6addr[0], dev->d_ipv6addr[1], dev->d_ipv6addr[2], + dev->d_ipv6addr[3], dev->d_ipv6addr[4], dev->d_ipv6addr[5], + dev->d_ipv6addr[6], dev->d_ipv6addr[7]); +#endif /* Configure the EMAC interface for normal operation. */ @@ -2119,7 +2413,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 */ @@ -2176,6 +2470,68 @@ static int sam_ifdown(struct net_driver_s *dev) } /**************************************************************************** + * 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) +{ + nllvdbg("ifup: %d\n", priv->ifup); + + /* Ignore the notification if the interface is not yet up */ + + if (priv->ifup) + { + /* Poll uIP 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 * * Description: @@ -2184,7 +2540,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 @@ -2196,10 +2552,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; - nllvdbg("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(HPWORK, &priv->work, sam_txavail_work, priv, 0); + } + +#else + irqstate_t flags; /* Disable interrupts because this function may be called from interrupt * level processing. @@ -2207,16 +2576,12 @@ static int sam_txavail(struct net_driver_s *dev) flags = irqsave(); - /* Ignore the notification if the interface is not yet up */ - - if (priv->ifup) - { - /* Poll uIP for new XMIT data */ - - sam_dopoll(priv); - } + /* Perform the out-of-cycle poll now */ + sam_txavail_process(priv); irqrestore(flags); +#endif + return OK; } |