This is an automated email from the ASF dual-hosted git repository. gnutt pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/incubator-nuttx.git
The following commit(s) were added to refs/heads/master by this push: new 5cbebda kinetis:Ethernet fixed & better interrupt management 5cbebda is described below commit 5cbebda133bddc987de84b8c1ba756be3a70691b Author: David Sidrane <david.sidr...@nscdg.com> AuthorDate: Mon Jun 15 10:59:04 2020 -0700 kinetis:Ethernet fixed & better interrupt management When any error was detected the buffers descriptors were blindly initialized. This caused the TX of the MAC to be in a bad state. The correct thing to do, we disable the MAC, init the bufffers and re-eanable the MAC. The interrupts were being throlled at the NVIC. This been cleaned up. --- arch/arm/src/kinetis/kinetis_enet.c | 110 +++++++++++++++++++++--------------- 1 file changed, 65 insertions(+), 45 deletions(-) diff --git a/arch/arm/src/kinetis/kinetis_enet.c b/arch/arm/src/kinetis/kinetis_enet.c index c78d98e..ff3cdf1 100644 --- a/arch/arm/src/kinetis/kinetis_enet.c +++ b/arch/arm/src/kinetis/kinetis_enet.c @@ -204,7 +204,9 @@ #define ERROR_INTERRUPTS (ENET_INT_UN | ENET_INT_RL | ENET_INT_LC | \ ENET_INT_EBERR | ENET_INT_BABT | ENET_INT_BABR) -/* This is a helper pointer for accessing the contents of the Ethernet header */ +/* This is a helper pointer for accessing the contents of the Ethernet + * header + */ #define BUF ((struct eth_hdr_s *)priv->dev.d_buf) @@ -237,6 +239,7 @@ struct kinetis_driver_s uint8_t phyaddr; /* Selected PHY address */ WDOG_ID txpoll; /* TX poll timer */ WDOG_ID txtimeout; /* TX timeout timer */ + uint32_t ints; /* Enabled interrupts */ struct work_s irqwork; /* For deferring interrupt work to the work queue */ struct work_s pollwork; /* For deferring poll work to the work queue */ struct enet_desc_s *txdesc; /* A pointer to the list of TX descriptor */ @@ -447,7 +450,6 @@ static bool kinetis_txringfull(FAR struct kinetis_driver_s *priv) static int kinetis_transmit(FAR struct kinetis_driver_s *priv) { struct enet_desc_s *txdesc; - uint32_t regval; uint8_t *buf; /* Since this can be called from kinetis_receive, it is possible that @@ -513,9 +515,8 @@ static int kinetis_transmit(FAR struct kinetis_driver_s *priv) /* Enable TX interrupts */ - regval = getreg32(KINETIS_ENET_EIMR); - regval |= TX_INTERRUPTS; - putreg32(regval, KINETIS_ENET_EIMR); + priv->ints |= TX_INTERRUPTS; + modifyreg32(KINETIS_ENET_EIMR, 0, TX_INTERRUPTS); /* Setup the TX timeout watchdog (perhaps restarting the timer) */ @@ -644,7 +645,9 @@ static void kinetis_receive(FAR struct kinetis_driver_s *priv) (uint8_t *)kinesis_swap32((uint32_t)priv->rxdesc[priv->rxtail].data); #ifdef CONFIG_NET_PKT - /* When packet sockets are enabled, feed the frame into the packet tap */ + /* When packet sockets are enabled, feed the frame into the packet + * tap + */ pkt_input(&priv->dev); #endif @@ -796,14 +799,12 @@ static void kinetis_receive(FAR struct kinetis_driver_s *priv) static void kinetis_txdone(FAR struct kinetis_driver_s *priv) { - uint32_t regval; - /* Verify that the oldest descriptor descriptor completed */ while (((priv->txdesc[priv->txtail].status1 & TXDESC_R) == 0) && (priv->txtail != priv->txhead)) { - /* Yes.. bump up the tail pointer, making space for a new TX descriptor */ + /* Yes. Bump the tail pointer, making space for a new TX descriptor */ priv->txtail++; if (priv->txtail >= CONFIG_KINETIS_ENETNTXBUFFERS) @@ -823,10 +824,8 @@ static void kinetis_txdone(FAR struct kinetis_driver_s *priv) /* No.. Cancel the TX timeout and disable further Tx interrupts. */ wd_cancel(priv->txtimeout); - - regval = getreg32(KINETIS_ENET_EIMR); - regval &= ~TX_INTERRUPTS; - putreg32(regval, KINETIS_ENET_EIMR); + priv->ints &= ~TX_INTERRUPTS; + modifyreg32(KINETIS_ENET_EIMR, TX_INTERRUPTS, priv->ints); } /* There should be space for a new TX in any event. Poll the network for @@ -864,7 +863,7 @@ static void kinetis_interrupt_work(FAR void *arg) /* Get the set of unmasked, pending interrupt. */ - pending = getreg32(KINETIS_ENET_EIR) & getreg32(KINETIS_ENET_EIMR); + pending = getreg32(KINETIS_ENET_EIR) & priv->ints; /* Clear the pending interrupts */ @@ -899,13 +898,35 @@ static void kinetis_interrupt_work(FAR void *arg) { /* An error has occurred, update statistics */ + nerr("pending %0xd ints %0xd\n", pending, priv->ints); + NETDEV_ERRORS(&priv->dev); + /* Shutdown the MAC to keep it out of the descriptors */ + + modifyreg32(KINETIS_ENET_ECR, ENET_ECR_ETHEREN, 0); + /* Reinitialize all buffers. */ kinetis_initbuffers(priv); - /* Indicate that there have been empty receive buffers produced */ + /* Set the RX buffer size */ + + putreg32(KINETIS_BUF_SIZE, KINETIS_ENET_MRBR); + + /* Point to the start of the circular RX buffer descriptor queue */ + + putreg32((uint32_t)priv->rxdesc, KINETIS_ENET_RDSR); + + /* Point to the start of the circular TX buffer descriptor queue */ + + putreg32((uint32_t)priv->txdesc, KINETIS_ENET_TDSR); + + /* Enable MAC */ + + modifyreg32(KINETIS_ENET_ECR, 0, ENET_ECR_ETHEREN); + + /* Receive buffers available */ putreg32(ENET_RDAR, KINETIS_ENET_RDAR); } @@ -914,12 +935,7 @@ static void kinetis_interrupt_work(FAR void *arg) /* 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); + putreg32(priv->ints, KINETIS_ENET_EIMR); } /**************************************************************************** @@ -951,10 +967,7 @@ static int kinetis_interrupt(int irq, FAR void *context, FAR void *arg) * 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); + putreg32(0, KINETIS_ENET_EIMR); /* TODO: Determine if a TX transfer just completed */ @@ -999,8 +1012,8 @@ static void kinetis_txtimeout_work(FAR void *arg) net_lock(); NETDEV_TXTIMEOUTS(&priv->dev); - /* Take the interface down and bring it back up. The is the most aggressive - * hardware reset. + /* Take the interface down and bring it back up. The is the most + * aggressive hardware reset. */ kinetis_ifdown(&priv->dev); @@ -1040,10 +1053,8 @@ static void kinetis_txtimeout_expiry(int argc, uint32_t arg, ...) * 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); + putreg32(0, KINETIS_ENET_EIMR); + priv->ints = 0; /* Schedule to perform the TX timeout processing on the worker thread, * canceling any pending interrupt work. @@ -1237,26 +1248,30 @@ static int kinetis_ifup(struct net_driver_s *dev) wd_start(priv->txpoll, KINETIS_WDDELAY, kinetis_polltimer_expiry, 1, (wdparm_t)priv); + putreg32(0, KINETIS_ENET_EIMR); + /* Clear all pending ENET interrupt */ putreg32(0xffffffff, KINETIS_ENET_EIR); - /* Enable RX and error interrupts at the controller (TX interrupts are - * still disabled). - */ - - putreg32(RX_INTERRUPTS | ERROR_INTERRUPTS, - KINETIS_ENET_EIMR); - - /* Mark the interrupt "up" and enable interrupts at the NVIC */ - - priv->bifup = true; #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); + + /* Mark the interrupt "up" and enable interrupts at the NVIC */ + + priv->bifup = true; + + /* Enable RX and error interrupts at the controller (TX interrupts are + * still disabled). + */ + + priv->ints = RX_INTERRUPTS | ERROR_INTERRUPTS; + modifyreg32(KINETIS_ENET_EIMR, TX_INTERRUPTS, priv->ints); + return OK; } @@ -1285,11 +1300,14 @@ static int kinetis_ifdown(struct net_driver_s *dev) /* Disable the Ethernet interrupts at the NVIC */ flags = enter_critical_section(); + + priv->ints = 0; + putreg32(priv->ints, KINETIS_ENET_EIMR); + up_disable_irq(KINETIS_IRQ_EMACTMR); up_disable_irq(KINETIS_IRQ_EMACTX); up_disable_irq(KINETIS_IRQ_EMACRX); up_disable_irq(KINETIS_IRQ_EMACMISC); - putreg32(0, KINETIS_ENET_EIMR); /* Cancel the TX poll timer and TX timeout timers */ @@ -1721,7 +1739,9 @@ static inline int kinetis_initphy(struct kinetis_driver_s *priv) } while ((ret < 0 || phydata == 0xffff) && ++retries < 3); - /* If we successfully read anything then break out, using this PHY address */ + /* If we successfully read anything then break out, using this PHY + * address + */ if (retries < 3) { @@ -1809,8 +1829,8 @@ static inline int kinetis_initphy(struct kinetis_driver_s *priv) * MCU whenever the link is ready. */ - ninfo("%s: Autonegotiation failed [%d] (is cable plugged-in ?), default to 10Mbs mode\n", \ - BOARD_PHY_NAME, retries); + ninfo("%s: Autonegotiation failed [%d] (is cable plugged-in ?)," + " default to 10Mbs mode\n", BOARD_PHY_NAME, retries); /* Stop auto negotiation */