Support Uno4kSE
[vuplus_openvuplus_3.0] / meta-bsp / recipes-kernel / linux / linux-vuplus-4.1.20 / bcmgenet-recovery-fix.patch
diff --git a/meta-bsp/recipes-kernel/linux/linux-vuplus-4.1.20/bcmgenet-recovery-fix.patch b/meta-bsp/recipes-kernel/linux/linux-vuplus-4.1.20/bcmgenet-recovery-fix.patch
new file mode 100644 (file)
index 0000000..409c53f
--- /dev/null
@@ -0,0 +1,138 @@
+diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c
+index 3d5c251..152774f 100644
+--- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c
++++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c
+@@ -129,7 +129,10 @@ static inline dma_addr_t dmadesc_get_addr(struct bcmgenet_priv *priv,
+ #define GENET_VER_FMT "%1d.%1d EPHY: 0x%04x"
+ #define GENET_MSG_DEFAULT     (NETIF_MSG_DRV | NETIF_MSG_PROBE | \
+-                              NETIF_MSG_LINK)
++                              NETIF_MSG_LINK | NETIF_MSG_INTR | \
++                NETIF_MSG_RX_ERR | NETIF_MSG_RX_STATUS | \
++                NETIF_MSG_TX_ERR | NETIF_MSG_TX_DONE | \
++                NETIF_MSG_IFUP | NETIF_MSG_IFDOWN)
+ static inline u32 bcmgenet_rbuf_ctrl_get(struct bcmgenet_priv *priv)
+ {
+@@ -3255,24 +3258,58 @@ static irqreturn_t bcmgenet_isr1(int irq, void *dev_id)
+       return IRQ_HANDLED;
+ }
++struct rbuf_ovfl_work_struct {
++    struct delayed_work queue;
++    struct bcmgenet_priv *priv;
++} rbuf_ovfl_work;
++
++static void rbuf_ovfl_wq_func(struct work_struct *work) {
++    struct delayed_work *dwork = to_delayed_work(work);
++    struct rbuf_ovfl_work_struct *rbo_work = container_of(dwork, struct rbuf_ovfl_work_struct, queue);
++    struct bcmgenet_priv *priv = rbo_work->priv;
++
++    unsigned int flags = dev_get_flags(priv->dev);
++
++    rtnl_lock();
++    netif_carrier_off(priv->dev);
++    dev_change_flags(priv->dev, flags & ~IFF_UP);
++    rtnl_unlock();
++
++    msleep(1000);
++
++    rtnl_lock();
++    dev_change_flags(priv->dev, flags | IFF_UP);
++    rtnl_unlock();
++
++    return;
++}
++
+ /* bcmgenet_isr0: handle Rx and Tx default queues + other stuff */
+ static irqreturn_t bcmgenet_isr0(int irq, void *dev_id)
+ {
+       struct bcmgenet_priv *priv = dev_id;
+       struct bcmgenet_rx_ring *rx_ring;
+       struct bcmgenet_tx_ring *tx_ring;
+-      unsigned int status;
++      unsigned int status, status_unmask;
+       unsigned long flags;
+       /* Read irq status */
+-      status = bcmgenet_intrl2_0_readl(priv, INTRL2_CPU_STAT) &
+-              ~bcmgenet_intrl2_0_readl(priv, INTRL2_CPU_MASK_STATUS);
++      status_unmask = bcmgenet_intrl2_0_readl(priv, INTRL2_CPU_STAT);
++    status = status_unmask & ~bcmgenet_intrl2_0_readl(priv, INTRL2_CPU_MASK_STATUS);
++
++    if (status_unmask & (UMAC_IRQ_RBUF_OVERFLOW)) {
++        netdev_info(priv->dev,
++            "%s: %d pkts\n", __func__, bcmgenet_rbuf_readl(priv, RBUF_OVFL_CNT_V3PLUS));
++        status |= UMAC_IRQ_RBUF_OVERFLOW;
++        schedule_delayed_work(&(rbuf_ovfl_work.queue), HZ);
++    }
+       /* clear interrupts */
+       bcmgenet_intrl2_0_writel(priv, status, INTRL2_CPU_CLEAR);
+-      netif_dbg(priv, intr, priv->dev,
+-                "IRQ=0x%x\n", status);
++    if (status & ~(UMAC_IRQ_MDIO_DONE)) /* except for mdio event */
++      netif_dbg(priv, intr, priv->dev,
++              "%s: IRQ=0x%x\n", __func__, status);
+       if (status & UMAC_IRQ_RXDMA_DONE) {
+               rx_ring = &priv->rx_rings[DESC_INDEX];
+@@ -3281,6 +3318,10 @@ static irqreturn_t bcmgenet_isr0(int irq, void *dev_id)
+                       rx_ring->int_disable(rx_ring);
+                       __napi_schedule(&rx_ring->napi);
+               }
++        else {
++            netif_dbg(priv, intr, priv->dev, 
++                "%s: rx not sched. state=0x%08lx\n", __func__, rx_ring->napi.state);
++        }
+       }
+       if (status & UMAC_IRQ_TXDMA_DONE) {
+@@ -3306,6 +3347,8 @@ static irqreturn_t bcmgenet_isr0(int irq, void *dev_id)
+               spin_unlock_irqrestore(&priv->lock, flags);
+               schedule_work(&priv->bcmgenet_irq_work);
++      netif_dbg(priv, intr, priv->dev,
++              "%s: link event. status=0x%08x\n", __func__, status);
+       }
+       return IRQ_HANDLED;
+@@ -3351,6 +3394,20 @@ static void bcmgenet_umac_reset(struct bcmgenet_priv *priv)
+       udelay(10);
+ }
++static void bcmgenet_rbuf_reset(struct bcmgenet_priv *priv)
++{
++      u32 reg;
++
++      reg = bcmgenet_rbuf_ctrl_get(priv);
++      reg |= BIT(0);
++      bcmgenet_rbuf_ctrl_set(priv, reg);
++      udelay(100);
++
++      reg &= ~BIT(0);
++      bcmgenet_rbuf_ctrl_set(priv, reg);
++      udelay(100);
++}
++
+ static void bcmgenet_set_hw_addr(struct bcmgenet_priv *priv,
+                                unsigned char *addr)
+ {
+@@ -3434,6 +3491,7 @@ static int bcmgenet_open(struct net_device *dev)
+       /* take MAC out of reset */
+       bcmgenet_umac_reset(priv);
++    bcmgenet_rbuf_reset(priv);
+       ret = init_umac(priv);
+       if (ret)
+@@ -4070,6 +4128,8 @@ static int bcmgenet_probe(struct platform_device *pdev)
+       /* Always use RX_BUF_LENGTH (2KB) buffer for all chips */
+       priv->rx_buf_len = RX_BUF_LENGTH;
+       INIT_WORK(&priv->bcmgenet_irq_work, bcmgenet_irq_task);
++    rbuf_ovfl_work.priv = priv;
++    INIT_DELAYED_WORK(&(rbuf_ovfl_work.queue), rbuf_ovfl_wq_func);
+       priv->clk_wol = devm_clk_get(&priv->pdev->dev, "sw_genetwol");
+       if (IS_ERR(priv->clk_wol)) {