mirror of
https://github.com/apache/nuttx.git
synced 2026-06-01 07:45:16 +08:00
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.
This commit is contained in:
@@ -204,7 +204,9 @@
|
|||||||
#define ERROR_INTERRUPTS (ENET_INT_UN | ENET_INT_RL | ENET_INT_LC | \
|
#define ERROR_INTERRUPTS (ENET_INT_UN | ENET_INT_RL | ENET_INT_LC | \
|
||||||
ENET_INT_EBERR | ENET_INT_BABT | ENET_INT_BABR)
|
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)
|
#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
|
||||||
|
|
||||||
@@ -237,6 +239,7 @@ struct kinetis_driver_s
|
|||||||
uint8_t phyaddr; /* Selected PHY address */
|
uint8_t phyaddr; /* Selected PHY address */
|
||||||
WDOG_ID txpoll; /* TX poll timer */
|
WDOG_ID txpoll; /* TX poll timer */
|
||||||
WDOG_ID txtimeout; /* TX timeout 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 irqwork; /* For deferring interrupt work to the work queue */
|
||||||
struct work_s pollwork; /* For deferring poll 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 */
|
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)
|
static int kinetis_transmit(FAR struct kinetis_driver_s *priv)
|
||||||
{
|
{
|
||||||
struct enet_desc_s *txdesc;
|
struct enet_desc_s *txdesc;
|
||||||
uint32_t regval;
|
|
||||||
uint8_t *buf;
|
uint8_t *buf;
|
||||||
|
|
||||||
/* Since this can be called from kinetis_receive, it is possible that
|
/* 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 */
|
/* Enable TX interrupts */
|
||||||
|
|
||||||
regval = getreg32(KINETIS_ENET_EIMR);
|
priv->ints |= TX_INTERRUPTS;
|
||||||
regval |= TX_INTERRUPTS;
|
modifyreg32(KINETIS_ENET_EIMR, 0, TX_INTERRUPTS);
|
||||||
putreg32(regval, KINETIS_ENET_EIMR);
|
|
||||||
|
|
||||||
/* Setup the TX timeout watchdog (perhaps restarting the timer) */
|
/* 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);
|
(uint8_t *)kinesis_swap32((uint32_t)priv->rxdesc[priv->rxtail].data);
|
||||||
|
|
||||||
#ifdef CONFIG_NET_PKT
|
#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);
|
pkt_input(&priv->dev);
|
||||||
#endif
|
#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)
|
static void kinetis_txdone(FAR struct kinetis_driver_s *priv)
|
||||||
{
|
{
|
||||||
uint32_t regval;
|
|
||||||
|
|
||||||
/* Verify that the oldest descriptor descriptor completed */
|
/* Verify that the oldest descriptor descriptor completed */
|
||||||
|
|
||||||
while (((priv->txdesc[priv->txtail].status1 & TXDESC_R) == 0) &&
|
while (((priv->txdesc[priv->txtail].status1 & TXDESC_R) == 0) &&
|
||||||
(priv->txtail != priv->txhead))
|
(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++;
|
priv->txtail++;
|
||||||
if (priv->txtail >= CONFIG_KINETIS_ENETNTXBUFFERS)
|
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. */
|
/* No.. Cancel the TX timeout and disable further Tx interrupts. */
|
||||||
|
|
||||||
wd_cancel(priv->txtimeout);
|
wd_cancel(priv->txtimeout);
|
||||||
|
priv->ints &= ~TX_INTERRUPTS;
|
||||||
regval = getreg32(KINETIS_ENET_EIMR);
|
modifyreg32(KINETIS_ENET_EIMR, TX_INTERRUPTS, priv->ints);
|
||||||
regval &= ~TX_INTERRUPTS;
|
|
||||||
putreg32(regval, KINETIS_ENET_EIMR);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* There should be space for a new TX in any event. Poll the network for
|
/* 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. */
|
/* 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 */
|
/* Clear the pending interrupts */
|
||||||
|
|
||||||
@@ -899,13 +898,35 @@ static void kinetis_interrupt_work(FAR void *arg)
|
|||||||
{
|
{
|
||||||
/* An error has occurred, update statistics */
|
/* An error has occurred, update statistics */
|
||||||
|
|
||||||
|
nerr("pending %0xd ints %0xd\n", pending, priv->ints);
|
||||||
|
|
||||||
NETDEV_ERRORS(&priv->dev);
|
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. */
|
/* Reinitialize all buffers. */
|
||||||
|
|
||||||
kinetis_initbuffers(priv);
|
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);
|
putreg32(ENET_RDAR, KINETIS_ENET_RDAR);
|
||||||
}
|
}
|
||||||
@@ -914,12 +935,7 @@ static void kinetis_interrupt_work(FAR void *arg)
|
|||||||
|
|
||||||
/* Re-enable Ethernet interrupts */
|
/* Re-enable Ethernet interrupts */
|
||||||
|
|
||||||
#if 0
|
putreg32(priv->ints, KINETIS_ENET_EIMR);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
@@ -951,10 +967,7 @@ static int kinetis_interrupt(int irq, FAR void *context, FAR void *arg)
|
|||||||
* condition here.
|
* condition here.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
up_disable_irq(KINETIS_IRQ_EMACTMR);
|
putreg32(0, KINETIS_ENET_EIMR);
|
||||||
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 */
|
/* TODO: Determine if a TX transfer just completed */
|
||||||
|
|
||||||
@@ -999,8 +1012,8 @@ static void kinetis_txtimeout_work(FAR void *arg)
|
|||||||
net_lock();
|
net_lock();
|
||||||
NETDEV_TXTIMEOUTS(&priv->dev);
|
NETDEV_TXTIMEOUTS(&priv->dev);
|
||||||
|
|
||||||
/* Take the interface down and bring it back up. The is the most aggressive
|
/* Take the interface down and bring it back up. The is the most
|
||||||
* hardware reset.
|
* aggressive hardware reset.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
kinetis_ifdown(&priv->dev);
|
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.
|
* condition with interrupt work that is already queued and in progress.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
up_disable_irq(KINETIS_IRQ_EMACTMR);
|
putreg32(0, KINETIS_ENET_EIMR);
|
||||||
up_disable_irq(KINETIS_IRQ_EMACTX);
|
priv->ints = 0;
|
||||||
up_disable_irq(KINETIS_IRQ_EMACRX);
|
|
||||||
up_disable_irq(KINETIS_IRQ_EMACMISC);
|
|
||||||
|
|
||||||
/* Schedule to perform the TX timeout processing on the worker thread,
|
/* Schedule to perform the TX timeout processing on the worker thread,
|
||||||
* canceling any pending interrupt work.
|
* 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,
|
wd_start(priv->txpoll, KINETIS_WDDELAY, kinetis_polltimer_expiry, 1,
|
||||||
(wdparm_t)priv);
|
(wdparm_t)priv);
|
||||||
|
|
||||||
|
putreg32(0, KINETIS_ENET_EIMR);
|
||||||
|
|
||||||
/* Clear all pending ENET interrupt */
|
/* Clear all pending ENET interrupt */
|
||||||
|
|
||||||
putreg32(0xffffffff, KINETIS_ENET_EIR);
|
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
|
#if 0
|
||||||
up_enable_irq(KINETIS_IRQ_EMACTMR);
|
up_enable_irq(KINETIS_IRQ_EMACTMR);
|
||||||
#endif
|
#endif
|
||||||
up_enable_irq(KINETIS_IRQ_EMACTX);
|
up_enable_irq(KINETIS_IRQ_EMACTX);
|
||||||
up_enable_irq(KINETIS_IRQ_EMACRX);
|
up_enable_irq(KINETIS_IRQ_EMACRX);
|
||||||
up_enable_irq(KINETIS_IRQ_EMACMISC);
|
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;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1285,11 +1300,14 @@ static int kinetis_ifdown(struct net_driver_s *dev)
|
|||||||
/* Disable the Ethernet interrupts at the NVIC */
|
/* Disable the Ethernet interrupts at the NVIC */
|
||||||
|
|
||||||
flags = enter_critical_section();
|
flags = enter_critical_section();
|
||||||
|
|
||||||
|
priv->ints = 0;
|
||||||
|
putreg32(priv->ints, KINETIS_ENET_EIMR);
|
||||||
|
|
||||||
up_disable_irq(KINETIS_IRQ_EMACTMR);
|
up_disable_irq(KINETIS_IRQ_EMACTMR);
|
||||||
up_disable_irq(KINETIS_IRQ_EMACTX);
|
up_disable_irq(KINETIS_IRQ_EMACTX);
|
||||||
up_disable_irq(KINETIS_IRQ_EMACRX);
|
up_disable_irq(KINETIS_IRQ_EMACRX);
|
||||||
up_disable_irq(KINETIS_IRQ_EMACMISC);
|
up_disable_irq(KINETIS_IRQ_EMACMISC);
|
||||||
putreg32(0, KINETIS_ENET_EIMR);
|
|
||||||
|
|
||||||
/* Cancel the TX poll timer and TX timeout timers */
|
/* 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);
|
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)
|
if (retries < 3)
|
||||||
{
|
{
|
||||||
@@ -1809,8 +1829,8 @@ static inline int kinetis_initphy(struct kinetis_driver_s *priv)
|
|||||||
* MCU whenever the link is ready.
|
* MCU whenever the link is ready.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
ninfo("%s: Autonegotiation failed [%d] (is cable plugged-in ?), default to 10Mbs mode\n", \
|
ninfo("%s: Autonegotiation failed [%d] (is cable plugged-in ?),"
|
||||||
BOARD_PHY_NAME, retries);
|
" default to 10Mbs mode\n", BOARD_PHY_NAME, retries);
|
||||||
|
|
||||||
/* Stop auto negotiation */
|
/* Stop auto negotiation */
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user