From dee77a5dd9a7830cfe7494efe13393ebbbe64e37 Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Tue, 12 Jul 2016 16:17:35 -0600 Subject: [PATCH] Kinetis Ethernet: Add support for CONFIG_NET_NOINTS --- TODO | 1 + arch/arm/src/kinetis/kinetis_enet.c | 463 +++++++++++++++++++++++--- configs/freedom-k64f/netnsh/defconfig | 11 +- 3 files changed, 419 insertions(+), 56 deletions(-) diff --git a/TODO b/TODO index 5c67ae83ac..bbc8f7657a 100644 --- a/TODO +++ b/TODO @@ -937,6 +937,7 @@ o Network (net/, drivers/net) LM3S NO NO TM4C YES YES eZ80 NO NO + Kinetis YES YES (not tested) LPC17xx YES YES (not tested) LPC43xx YES YES (not tested) DMxxx NIC NO NO diff --git a/arch/arm/src/kinetis/kinetis_enet.c b/arch/arm/src/kinetis/kinetis_enet.c index 070da795f3..6c30bac415 100644 --- a/arch/arm/src/kinetis/kinetis_enet.c +++ b/arch/arm/src/kinetis/kinetis_enet.c @@ -57,6 +57,10 @@ #include #include +#ifdef CONFIG_NET_NOINTS +# include +#endif + #ifdef CONFIG_NET_PKT # include #endif @@ -76,6 +80,14 @@ * Pre-processor Definitions ****************************************************************************/ +/* 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 + /* CONFIG_KINETIS_ENETNETHIFS determines the number of physical interfaces * that will be supported. */ @@ -92,13 +104,16 @@ # error "Need at least one RX buffer" #endif -#define NENET_NBUFFERS (CONFIG_KINETIS_ENETNTXBUFFERS+CONFIG_KINETIS_ENETNRXBUFFERS) +#define NENET_NBUFFERS \ + (CONFIG_KINETIS_ENETNTXBUFFERS+CONFIG_KINETIS_ENETNRXBUFFERS) #ifndef CONFIG_NET_MULTIBUFFER # error "CONFIG_NET_MULTIBUFFER is required in the configuration" #endif -/* TX poll delay = 1 seconds. CLK_TCK is the number of clock ticks per second */ +/* TX poll delay = 1 seconds. CLK_TCK is the number of clock ticks per + * second. + */ #define KINETIS_WDDELAY (1*CLK_TCK) @@ -196,6 +211,9 @@ struct kinetis_driver_s uint8_t phyaddr; /* Selected PHY address */ 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 struct enet_desc_s *txdesc; /* A pointer to the list of TX descriptor */ struct enet_desc_s *rxdesc; /* A pointer to the list of RX descriptors */ @@ -252,22 +270,43 @@ static int kinetis_txpoll(struct net_driver_s *dev); static void kinetis_receive(FAR struct kinetis_driver_s *priv); static void kinetis_txdone(FAR struct kinetis_driver_s *priv); + +static inline void kinetis_interrupt_process(FAR struct kinetis_driver_s *priv); +#ifdef CONFIG_NET_NOINTS +static void kinetis_interrupt_work(FAR void *arg); +#endif static int kinetis_interrupt(int irq, FAR void *context); /* Watchdog timer expirations */ -static void kinetis_txtimeout(int argc, uint32_t arg, ...); -static void kinetis_polltimer(int argc, uint32_t arg, ...); +static inline void kinetis_txtimeout_process(FAR struct kinetis_driver_s *priv); +#ifdef CONFIG_NET_NOINTS +static void kinetis_txtimeout_work(FAR void *arg); +#endif +static void kinetis_txtimeout_expiry(int argc, uint32_t arg, ...); + +static inline void kinetis_poll_process(FAR struct kinetis_driver_s *priv); +#ifdef CONFIG_NET_NOINTS +static void kinetis_poll_work(FAR void *arg); +#endif +static void kinetis_polltimer_expiry(int argc, uint32_t arg, ...); /* NuttX callback functions */ static int kinetis_ifup(struct net_driver_s *dev); static int kinetis_ifdown(struct net_driver_s *dev); + +static inline void kinetis_txavail_process(FAR struct kinetis_driver_s *priv); +#ifdef CONFIG_NET_NOINTS +static void kinetis_txavail_work(FAR void *arg); +#endif static int kinetis_txavail(struct net_driver_s *dev); + #ifdef CONFIG_NET_IGMP static int kinetis_addmac(struct net_driver_s *dev, FAR const uint8_t *mac); static int kinetis_rmmac(struct net_driver_s *dev, FAR const uint8_t *mac); #endif + #ifdef CONFIG_NETDEV_PHY_IOCTL static int kinetis_ioctl(struct net_driver_s *dev, int cmd, long arg); #endif @@ -464,8 +503,8 @@ static int kinetis_transmit(FAR struct kinetis_driver_s *priv) /* Setup the TX timeout watchdog (perhaps restarting the timer) */ - (void)wd_start(priv->txtimeout, KINETIS_TXTIMEOUT, kinetis_txtimeout, 1, - (uint32_t)priv); + (void)wd_start(priv->txtimeout, KINETIS_TXTIMEOUT, kinetis_txtimeout_expiry, 1, + (wdparm_t)priv); return OK; } @@ -777,28 +816,25 @@ static void kinetis_txdone(FAR struct kinetis_driver_s *priv) } /**************************************************************************** - * Function: kinetis_interrupt + * Function: kinetis_interrupt_process * * Description: - * Three interrupt sources will vector this this function: - * 1. Ethernet MAC transmit interrupt handler - * 2. Ethernet MAC receive interrupt handler - * 3. + * 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: + * The network is locked. * ****************************************************************************/ -static int kinetis_interrupt(int irq, FAR void *context) +static inline void kinetis_interrupt_process(FAR struct kinetis_driver_s *priv) { - register FAR struct kinetis_driver_s *priv = &g_enet[0]; uint32_t pending; /* Get the set of unmasked, pending interrupt. */ @@ -848,33 +884,130 @@ static int kinetis_interrupt(int irq, FAR void *context) putreg32(ENET_RDAR, KINETIS_ENET_RDAR); } +} + +/**************************************************************************** + * Function: kinetis_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: + * The network is locked. + * + ****************************************************************************/ + +#ifdef CONFIG_NET_NOINTS +static void kinetis_interrupt_work(FAR void *arg) +{ + FAR struct kinetis_driver_s *priv = (FAR struct kinetis_driver_s *)arg; + net_lock_t state; + + /* Process pending Ethernet interrupts */ + + state = net_lock(); + kinetis_interrupt_process(priv); + net_unlock(state); + + /* Re-enable Ethernet interrupts */ + +#if 0 + up_enable_irq(KINETIS_IRQ_EMACTMR); +#endif + up_enable_irq(KINETIS_IRQ_EMACTX); + up_enable_irq(KINETIS_IRQ_EMACRX); + up_enable_irq(KINETIS_IRQ_EMACMISC); +} +#endif + +/**************************************************************************** + * Function: kinetis_interrupt + * + * Description: + * Three interrupt sources will vector this this function: + * 1. Ethernet MAC transmit interrupt handler + * 2. Ethernet MAC receive interrupt handler + * 3. + * + * 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 kinetis_interrupt(int irq, FAR void *context) +{ + register FAR struct kinetis_driver_s *priv = &g_enet[0]; + +#ifdef CONFIG_NET_NOINTS + /* 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(KINETIS_IRQ_EMACTMR); + up_disable_irq(KINETIS_IRQ_EMACTX); + up_disable_irq(KINETIS_IRQ_EMACRX); + up_disable_irq(KINETIS_IRQ_EMACMISC); + + /* TODO: Determine if a TX transfer just completed */ + + { + /* 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, kinetis_interrupt_work, priv, 0); + +#else + /* Process the interrupt now */ + + kinetis_interrupt_process(priv); +#endif return OK; } /**************************************************************************** - * Function: kinetis_txtimeout + * Function: kinetis_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 * - * Assumptions: - * Global interrupts are disabled by the watchdog logic. - * ****************************************************************************/ -static void kinetis_txtimeout(int argc, uint32_t arg, ...) +static inline void kinetis_txtimeout_process(FAR struct kinetis_driver_s *priv) { - FAR struct kinetis_driver_s *priv = (FAR struct kinetis_driver_s *)arg; - /* Increment statistics and dump debug info */ NETDEV_TXTIMEOUTS(&priv->dev); @@ -892,10 +1025,42 @@ static void kinetis_txtimeout(int argc, uint32_t arg, ...) } /**************************************************************************** - * Function: kinetis_polltimer + * Function: kinetis_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: + * The network is locked. + * + ****************************************************************************/ + +#ifdef CONFIG_NET_NOINTS +static void kinetis_txtimeout_work(FAR void *arg) +{ + FAR struct kinetis_driver_s *priv = (FAR struct kinetis_driver_s *)arg; + net_lock_t state; + + /* Process pending Ethernet interrupts */ + + state = net_lock(); + kinetis_txtimeout_process(priv); + net_unlock(state); +} +#endif + +/**************************************************************************** + * Function: kinetis_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 @@ -909,10 +1074,56 @@ static void kinetis_txtimeout(int argc, uint32_t arg, ...) * ****************************************************************************/ -static void kinetis_polltimer(int argc, uint32_t arg, ...) +static void kinetis_txtimeout_expiry(int argc, uint32_t arg, ...) { FAR struct kinetis_driver_s *priv = (FAR struct kinetis_driver_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(KINETIS_IRQ_EMACTMR); + up_disable_irq(KINETIS_IRQ_EMACTX); + up_disable_irq(KINETIS_IRQ_EMACRX); + up_disable_irq(KINETIS_IRQ_EMACMISC); + + /* 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, kinetis_txtimeout_work, priv, 0); +#else + /* Process the timeout now */ + + kinetis_txtimeout_process(priv); +#endif +} + +/**************************************************************************** + * Function: kinetis_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 kinetis_poll_process(FAR struct kinetis_driver_s *priv) +{ /* Check if there is there is a transmission in progress. We cannot perform * the TX poll if he are unable to accept another packet for transmission. */ @@ -929,7 +1140,89 @@ static void kinetis_polltimer(int argc, uint32_t arg, ...) /* Setup the watchdog poll timer again in any case */ - (void)wd_start(priv->txpoll, KINETIS_WDDELAY, kinetis_polltimer, 1, arg); + (void)wd_start(priv->txpoll, KINETIS_WDDELAY, kinetis_polltimer_expiry, + 1, (wdparm_t)priv); +} + +/**************************************************************************** + * Function: kinetis_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: + * The network is locked. + * + ****************************************************************************/ + +#ifdef CONFIG_NET_NOINTS +static void kinetis_poll_work(FAR void *arg) +{ + FAR struct kinetis_driver_s *priv = (FAR struct kinetis_driver_s *)arg; + net_lock_t state; + + /* Perform the poll */ + + state = net_lock(); + kinetis_poll_process(priv); + net_unlock(state); +} +#endif + +/**************************************************************************** + * Function: kinetis_polltimer_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 kinetis_polltimer_expiry(int argc, uint32_t arg, ...) +{ + FAR struct kinetis_driver_s *priv = (FAR struct kinetis_driver_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, kinetis_poll_work, priv, 0); + } + else + { + /* No.. Just re-start the watchdog poll timer, missing one polling + * cycle. + */ + + (void)wd_start(priv->txpoll, KINETIS_WDDELAY, kinetis_polltimer_expiry, + 1, (wdparm_t)arg); + } + +#else + /* Process the interrupt now */ + + kinetis_poll_process(priv); +#endif } /**************************************************************************** @@ -1039,8 +1332,8 @@ static int kinetis_ifup(struct net_driver_s *dev) /* Set and activate a timer process */ - (void)wd_start(priv->txpoll, KINETIS_WDDELAY, kinetis_polltimer, 1, - (uint32_t)priv); + (void)wd_start(priv->txpoll, KINETIS_WDDELAY, kinetis_polltimer_expiry, 1, + (wdparm_t)priv); /* Clear all pending ENET interrupt */ @@ -1115,6 +1408,77 @@ static int kinetis_ifdown(struct net_driver_s *dev) return OK; } +/**************************************************************************** + * Function: kinetis_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 kinetis_txavail_process(FAR struct kinetis_driver_s *priv) +{ + net_lock_t state; + + /* Ignore the notification if the interface is not yet up */ + + state = net_lock(); + if (priv->bifup) + { + /* Check if there is room in the hardware to hold another outgoing + * packet. + */ + + if (!kinetics_txringfull(priv)) + { + /* No, there is space for another transfer. Poll the network for new + * XMIT data. + */ + + (void)devif_poll(&priv->dev, kinetis_txpoll); + } + } + + net_unlock(state); +} + +/**************************************************************************** + * Function: kinetis_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 kinetis_txavail_work(FAR void *arg) +{ + FAR struct kinetis_driver_s *priv = (FAR struct kinetis_driver_s *)arg; + + /* Perform the poll */ + + kinetis_txavail_process(priv); +} +#endif + /**************************************************************************** * Function: kinetis_txavail * @@ -1138,33 +1502,26 @@ static int kinetis_txavail(struct net_driver_s *dev) { FAR struct kinetis_driver_s *priv = (FAR struct kinetis_driver_s *)dev->d_private; - irqstate_t flags; - /* Disable interrupts because this function may be called from interrupt - * level processing. +#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. */ - flags = enter_critical_section(); - - /* Ignore the notification if the interface is not yet up */ - - if (priv->bifup) + if (work_available(&priv->work)) { - /* Check if there is room in the hardware to hold another outgoing - * packet. - */ + /* Schedule to serialize the poll on the worker thread. */ - if (!kinetics_txringfull(priv)) - { - /* No, there is space for another transfer. Poll the network for new - * XMIT data. - */ - - (void)devif_poll(&priv->dev, kinetis_txpoll); - } + work_queue(HPWORK, &priv->work, kinetis_txavail_work, priv, 0); } - leave_critical_section(flags); +#else + /* Perform the out-of-cycle poll now */ + + kinetis_txavail_process(priv); +#endif + return OK; } diff --git a/configs/freedom-k64f/netnsh/defconfig b/configs/freedom-k64f/netnsh/defconfig index 05a73a3220..a4a8968bdd 100644 --- a/configs/freedom-k64f/netnsh/defconfig +++ b/configs/freedom-k64f/netnsh/defconfig @@ -411,6 +411,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 @@ -419,6 +420,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 @@ -430,8 +432,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 # @@ -623,7 +628,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 #