diff --git a/arch/risc-v/src/mpfs/Kconfig b/arch/risc-v/src/mpfs/Kconfig index 75eaffaee27..9cc1b3c23f9 100644 --- a/arch/risc-v/src/mpfs/Kconfig +++ b/arch/risc-v/src/mpfs/Kconfig @@ -700,6 +700,14 @@ config MPFS_PHYINIT ---help--- call mpfs_phy_boardinitialize() on init +config MPFS_PHY_RX_TIMEOUT_WA + int "RX restart timeout workaround" + default 0 + ---help--- + This is a workaround for LAN8742A rev A and B silicon errata. + Reset ETH interface in case no RX packets have been received + in configured time. Set to 0 to disable. + config MPFS_ETH0_PHY_KSZ9477 bool "Use ksz9477 switch as an SGMII PHY for ETH0" default n diff --git a/arch/risc-v/src/mpfs/mpfs_ethernet.c b/arch/risc-v/src/mpfs/mpfs_ethernet.c index 809f38d077c..e1ff2199d39 100644 --- a/arch/risc-v/src/mpfs/mpfs_ethernet.c +++ b/arch/risc-v/src/mpfs/mpfs_ethernet.c @@ -171,6 +171,15 @@ #define MPFS_TXTIMEOUT (60 * CLK_TCK) +/* RX timeout: Workaround for LAN8742A rev A and B silicon errata + * "Cable diagnostics incorrectly returns Open cable connection for + * terminated cable" + */ + +#if CONFIG_MPFS_PHY_RX_TIMEOUT_WA != 0 +#define MPFS_RXTIMEOUT (CONFIG_MPFS_PHY_RX_TIMEOUT_WA * CLK_TCK) +#endif + /* PHY reset tim in loop counts */ #define PHY_RESET_WAIT_COUNT (10) @@ -268,6 +277,9 @@ struct mpfs_ethmac_s uint8_t phyaddr; /* PHY address */ #endif struct wdog_s txtimeout; /* TX timeout timer */ +#ifdef MPFS_RXTIMEOUT + struct wdog_s rxtimeout; /* RX timeout timer */ +#endif 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 timeoutwork; /* For managing timeouts */ @@ -390,6 +402,7 @@ static int mpfs_ethconfig(struct mpfs_ethmac_s *priv); static void mpfs_ethreset(struct mpfs_ethmac_s *priv); static void mpfs_interrupt_work(void *arg); +static void mpfs_txtimeout_expiry(wdparm_t arg); /**************************************************************************** * Private Functions @@ -471,6 +484,16 @@ static int mpfs_interrupt_0(int irq, void *context, void *arg) wd_cancel(&priv->txtimeout); } +#ifdef MPFS_RXTIMEOUT + if ((isr & INT_RX) != 0) + { + /* If a RX transfer just completed, restart the timeout */ + + wd_start(&priv->rxtimeout, MPFS_RXTIMEOUT, + mpfs_txtimeout_expiry, (wdparm_t)priv); + } +#endif + /* Schedule to perform the interrupt processing on the worker thread. */ work_queue(ETHWORK, &priv->irqwork, mpfs_interrupt_work, priv, 0); @@ -1559,6 +1582,15 @@ static int mpfs_ifup(struct net_driver_s *dev) up_enable_irq(priv->mac_q_int[2]); up_enable_irq(priv->mac_q_int[3]); +#ifdef MPFS_RXTIMEOUT + /* Set up the RX timeout. If we don't receive anything in time, try + * to re-initialize + */ + + wd_start(&priv->rxtimeout, MPFS_RXTIMEOUT, + mpfs_txtimeout_expiry, (wdparm_t)priv); +#endif + return OK; } @@ -1602,6 +1634,12 @@ static int mpfs_ifdown(struct net_driver_s *dev) wd_cancel(&priv->txtimeout); +#ifdef MPFS_RXTIMEOUT + /* Cancel the RX timeout timers */ + + wd_cancel(&priv->rxtimeout); +#endif + /* Put the MAC in its reset, non-operational state. This should be * a known configuration that will guarantee the mpfs_ifup() always * successfully brings the interface back up.