OSDN Git Service

net: axienet: Clean up DMA start/stop and error handling
authorRobert Hancock <robert.hancock@calian.com>
Sat, 5 Mar 2022 02:24:39 +0000 (20:24 -0600)
committerDavid S. Miller <davem@davemloft.net>
Sat, 5 Mar 2022 11:12:08 +0000 (11:12 +0000)
Simplify the DMA error handling process, and remove some duplicated code
between the DMA error handling and the stop function.

Signed-off-by: Robert Hancock <robert.hancock@calian.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/xilinx/xilinx_axienet_main.c

index be17f6b..340c0a2 100644 (file)
@@ -227,6 +227,44 @@ static void axienet_dma_bd_release(struct net_device *ndev)
 }
 
 /**
+ * axienet_dma_start - Set up DMA registers and start DMA operation
+ * @lp:                Pointer to the axienet_local structure
+ */
+static void axienet_dma_start(struct axienet_local *lp)
+{
+       u32 rx_cr, tx_cr;
+
+       /* Start updating the Rx channel control register */
+       rx_cr = (lp->coalesce_count_rx << XAXIDMA_COALESCE_SHIFT) |
+               (XAXIDMA_DFT_RX_WAITBOUND << XAXIDMA_DELAY_SHIFT) |
+               XAXIDMA_IRQ_ALL_MASK;
+       axienet_dma_out32(lp, XAXIDMA_RX_CR_OFFSET, rx_cr);
+
+       /* Start updating the Tx channel control register */
+       tx_cr = (lp->coalesce_count_tx << XAXIDMA_COALESCE_SHIFT) |
+               (XAXIDMA_DFT_TX_WAITBOUND << XAXIDMA_DELAY_SHIFT) |
+               XAXIDMA_IRQ_ALL_MASK;
+       axienet_dma_out32(lp, XAXIDMA_TX_CR_OFFSET, tx_cr);
+
+       /* Populate the tail pointer and bring the Rx Axi DMA engine out of
+        * halted state. This will make the Rx side ready for reception.
+        */
+       axienet_dma_out_addr(lp, XAXIDMA_RX_CDESC_OFFSET, lp->rx_bd_p);
+       rx_cr |= XAXIDMA_CR_RUNSTOP_MASK;
+       axienet_dma_out32(lp, XAXIDMA_RX_CR_OFFSET, rx_cr);
+       axienet_dma_out_addr(lp, XAXIDMA_RX_TDESC_OFFSET, lp->rx_bd_p +
+                            (sizeof(*lp->rx_bd_v) * (lp->rx_bd_num - 1)));
+
+       /* Write to the RS (Run-stop) bit in the Tx channel control register.
+        * Tx channel is now ready to run. But only after we write to the
+        * tail pointer register that the Tx channel will start transmitting.
+        */
+       axienet_dma_out_addr(lp, XAXIDMA_TX_CDESC_OFFSET, lp->tx_bd_p);
+       tx_cr |= XAXIDMA_CR_RUNSTOP_MASK;
+       axienet_dma_out32(lp, XAXIDMA_TX_CR_OFFSET, tx_cr);
+}
+
+/**
  * axienet_dma_bd_init - Setup buffer descriptor rings for Axi DMA
  * @ndev:      Pointer to the net_device structure
  *
@@ -238,7 +276,6 @@ static void axienet_dma_bd_release(struct net_device *ndev)
  */
 static int axienet_dma_bd_init(struct net_device *ndev)
 {
-       u32 cr;
        int i;
        struct sk_buff *skb;
        struct axienet_local *lp = netdev_priv(ndev);
@@ -296,50 +333,7 @@ static int axienet_dma_bd_init(struct net_device *ndev)
                lp->rx_bd_v[i].cntrl = lp->max_frm_size;
        }
 
-       /* Start updating the Rx channel control register */
-       cr = axienet_dma_in32(lp, XAXIDMA_RX_CR_OFFSET);
-       /* Update the interrupt coalesce count */
-       cr = ((cr & ~XAXIDMA_COALESCE_MASK) |
-             ((lp->coalesce_count_rx) << XAXIDMA_COALESCE_SHIFT));
-       /* Update the delay timer count */
-       cr = ((cr & ~XAXIDMA_DELAY_MASK) |
-             (XAXIDMA_DFT_RX_WAITBOUND << XAXIDMA_DELAY_SHIFT));
-       /* Enable coalesce, delay timer and error interrupts */
-       cr |= XAXIDMA_IRQ_ALL_MASK;
-       /* Write to the Rx channel control register */
-       axienet_dma_out32(lp, XAXIDMA_RX_CR_OFFSET, cr);
-
-       /* Start updating the Tx channel control register */
-       cr = axienet_dma_in32(lp, XAXIDMA_TX_CR_OFFSET);
-       /* Update the interrupt coalesce count */
-       cr = (((cr & ~XAXIDMA_COALESCE_MASK)) |
-             ((lp->coalesce_count_tx) << XAXIDMA_COALESCE_SHIFT));
-       /* Update the delay timer count */
-       cr = (((cr & ~XAXIDMA_DELAY_MASK)) |
-             (XAXIDMA_DFT_TX_WAITBOUND << XAXIDMA_DELAY_SHIFT));
-       /* Enable coalesce, delay timer and error interrupts */
-       cr |= XAXIDMA_IRQ_ALL_MASK;
-       /* Write to the Tx channel control register */
-       axienet_dma_out32(lp, XAXIDMA_TX_CR_OFFSET, cr);
-
-       /* Populate the tail pointer and bring the Rx Axi DMA engine out of
-        * halted state. This will make the Rx side ready for reception.
-        */
-       axienet_dma_out_addr(lp, XAXIDMA_RX_CDESC_OFFSET, lp->rx_bd_p);
-       cr = axienet_dma_in32(lp, XAXIDMA_RX_CR_OFFSET);
-       axienet_dma_out32(lp, XAXIDMA_RX_CR_OFFSET,
-                         cr | XAXIDMA_CR_RUNSTOP_MASK);
-       axienet_dma_out_addr(lp, XAXIDMA_RX_TDESC_OFFSET, lp->rx_bd_p +
-                            (sizeof(*lp->rx_bd_v) * (lp->rx_bd_num - 1)));
-
-       /* Write to the RS (Run-stop) bit in the Tx channel control register.
-        * Tx channel is now ready to run. But only after we write to the
-        * tail pointer register that the Tx channel will start transmitting.
-        */
-       axienet_dma_out_addr(lp, XAXIDMA_TX_CDESC_OFFSET, lp->tx_bd_p);
-       cr = axienet_dma_in32(lp, XAXIDMA_TX_CR_OFFSET);
-       axienet_dma_out32(lp, XAXIDMA_TX_CR_OFFSET,
-                         cr | XAXIDMA_CR_RUNSTOP_MASK);
+       axienet_dma_start(lp);
 
        return 0;
 out:
@@ -531,6 +525,44 @@ static int __axienet_device_reset(struct axienet_local *lp)
 }
 
 /**
+ * axienet_dma_stop - Stop DMA operation
+ * @lp:                Pointer to the axienet_local structure
+ */
+static void axienet_dma_stop(struct axienet_local *lp)
+{
+       int count;
+       u32 cr, sr;
+
+       cr = axienet_dma_in32(lp, XAXIDMA_RX_CR_OFFSET);
+       cr &= ~(XAXIDMA_CR_RUNSTOP_MASK | XAXIDMA_IRQ_ALL_MASK);
+       axienet_dma_out32(lp, XAXIDMA_RX_CR_OFFSET, cr);
+       synchronize_irq(lp->rx_irq);
+
+       cr = axienet_dma_in32(lp, XAXIDMA_TX_CR_OFFSET);
+       cr &= ~(XAXIDMA_CR_RUNSTOP_MASK | XAXIDMA_IRQ_ALL_MASK);
+       axienet_dma_out32(lp, XAXIDMA_TX_CR_OFFSET, cr);
+       synchronize_irq(lp->tx_irq);
+
+       /* Give DMAs a chance to halt gracefully */
+       sr = axienet_dma_in32(lp, XAXIDMA_RX_SR_OFFSET);
+       for (count = 0; !(sr & XAXIDMA_SR_HALT_MASK) && count < 5; ++count) {
+               msleep(20);
+               sr = axienet_dma_in32(lp, XAXIDMA_RX_SR_OFFSET);
+       }
+
+       sr = axienet_dma_in32(lp, XAXIDMA_TX_SR_OFFSET);
+       for (count = 0; !(sr & XAXIDMA_SR_HALT_MASK) && count < 5; ++count) {
+               msleep(20);
+               sr = axienet_dma_in32(lp, XAXIDMA_TX_SR_OFFSET);
+       }
+
+       /* Do a reset to ensure DMA is really stopped */
+       axienet_lock_mii(lp);
+       __axienet_device_reset(lp);
+       axienet_unlock_mii(lp);
+}
+
+/**
  * axienet_device_reset - Reset and initialize the Axi Ethernet hardware.
  * @ndev:      Pointer to the net_device structure
  *
@@ -949,41 +981,27 @@ static void axienet_recv(struct net_device *ndev)
  */
 static irqreturn_t axienet_tx_irq(int irq, void *_ndev)
 {
-       u32 cr;
        unsigned int status;
        struct net_device *ndev = _ndev;
        struct axienet_local *lp = netdev_priv(ndev);
 
        status = axienet_dma_in32(lp, XAXIDMA_TX_SR_OFFSET);
-       if (status & (XAXIDMA_IRQ_IOC_MASK | XAXIDMA_IRQ_DELAY_MASK)) {
-               axienet_dma_out32(lp, XAXIDMA_TX_SR_OFFSET, status);
-               axienet_start_xmit_done(lp->ndev);
-               goto out;
-       }
+
        if (!(status & XAXIDMA_IRQ_ALL_MASK))
                return IRQ_NONE;
-       if (status & XAXIDMA_IRQ_ERROR_MASK) {
-               dev_err(&ndev->dev, "DMA Tx error 0x%x\n", status);
-               dev_err(&ndev->dev, "Current BD is at: 0x%x%08x\n",
-                       (lp->tx_bd_v[lp->tx_bd_ci]).phys_msb,
-                       (lp->tx_bd_v[lp->tx_bd_ci]).phys);
-
-               cr = axienet_dma_in32(lp, XAXIDMA_TX_CR_OFFSET);
-               /* Disable coalesce, delay timer and error interrupts */
-               cr &= (~XAXIDMA_IRQ_ALL_MASK);
-               /* Write to the Tx channel control register */
-               axienet_dma_out32(lp, XAXIDMA_TX_CR_OFFSET, cr);
-
-               cr = axienet_dma_in32(lp, XAXIDMA_RX_CR_OFFSET);
-               /* Disable coalesce, delay timer and error interrupts */
-               cr &= (~XAXIDMA_IRQ_ALL_MASK);
-               /* Write to the Rx channel control register */
-               axienet_dma_out32(lp, XAXIDMA_RX_CR_OFFSET, cr);
 
+       axienet_dma_out32(lp, XAXIDMA_TX_SR_OFFSET, status);
+
+       if (unlikely(status & XAXIDMA_IRQ_ERROR_MASK)) {
+               netdev_err(ndev, "DMA Tx error 0x%x\n", status);
+               netdev_err(ndev, "Current BD is at: 0x%x%08x\n",
+                          (lp->tx_bd_v[lp->tx_bd_ci]).phys_msb,
+                          (lp->tx_bd_v[lp->tx_bd_ci]).phys);
                schedule_work(&lp->dma_err_task);
-               axienet_dma_out32(lp, XAXIDMA_TX_SR_OFFSET, status);
+       } else {
+               axienet_start_xmit_done(lp->ndev);
        }
-out:
+
        return IRQ_HANDLED;
 }
 
@@ -999,41 +1017,27 @@ out:
  */
 static irqreturn_t axienet_rx_irq(int irq, void *_ndev)
 {
-       u32 cr;
        unsigned int status;
        struct net_device *ndev = _ndev;
        struct axienet_local *lp = netdev_priv(ndev);
 
        status = axienet_dma_in32(lp, XAXIDMA_RX_SR_OFFSET);
-       if (status & (XAXIDMA_IRQ_IOC_MASK | XAXIDMA_IRQ_DELAY_MASK)) {
-               axienet_dma_out32(lp, XAXIDMA_RX_SR_OFFSET, status);
-               axienet_recv(lp->ndev);
-               goto out;
-       }
+
        if (!(status & XAXIDMA_IRQ_ALL_MASK))
                return IRQ_NONE;
-       if (status & XAXIDMA_IRQ_ERROR_MASK) {
-               dev_err(&ndev->dev, "DMA Rx error 0x%x\n", status);
-               dev_err(&ndev->dev, "Current BD is at: 0x%x%08x\n",
-                       (lp->rx_bd_v[lp->rx_bd_ci]).phys_msb,
-                       (lp->rx_bd_v[lp->rx_bd_ci]).phys);
-
-               cr = axienet_dma_in32(lp, XAXIDMA_TX_CR_OFFSET);
-               /* Disable coalesce, delay timer and error interrupts */
-               cr &= (~XAXIDMA_IRQ_ALL_MASK);
-               /* Finally write to the Tx channel control register */
-               axienet_dma_out32(lp, XAXIDMA_TX_CR_OFFSET, cr);
-
-               cr = axienet_dma_in32(lp, XAXIDMA_RX_CR_OFFSET);
-               /* Disable coalesce, delay timer and error interrupts */
-               cr &= (~XAXIDMA_IRQ_ALL_MASK);
-               /* write to the Rx channel control register */
-               axienet_dma_out32(lp, XAXIDMA_RX_CR_OFFSET, cr);
 
+       axienet_dma_out32(lp, XAXIDMA_RX_SR_OFFSET, status);
+
+       if (unlikely(status & XAXIDMA_IRQ_ERROR_MASK)) {
+               netdev_err(ndev, "DMA Rx error 0x%x\n", status);
+               netdev_err(ndev, "Current BD is at: 0x%x%08x\n",
+                          (lp->rx_bd_v[lp->rx_bd_ci]).phys_msb,
+                          (lp->rx_bd_v[lp->rx_bd_ci]).phys);
                schedule_work(&lp->dma_err_task);
-               axienet_dma_out32(lp, XAXIDMA_RX_SR_OFFSET, status);
+       } else {
+               axienet_recv(lp->ndev);
        }
-out:
+
        return IRQ_HANDLED;
 }
 
@@ -1151,8 +1155,6 @@ err_tx_irq:
  */
 static int axienet_stop(struct net_device *ndev)
 {
-       u32 cr, sr;
-       int count;
        struct axienet_local *lp = netdev_priv(ndev);
 
        dev_dbg(&ndev->dev, "axienet_close()\n");
@@ -1163,34 +1165,10 @@ static int axienet_stop(struct net_device *ndev)
        axienet_setoptions(ndev, lp->options &
                           ~(XAE_OPTION_TXEN | XAE_OPTION_RXEN));
 
-       cr = axienet_dma_in32(lp, XAXIDMA_RX_CR_OFFSET);
-       cr &= ~(XAXIDMA_CR_RUNSTOP_MASK | XAXIDMA_IRQ_ALL_MASK);
-       axienet_dma_out32(lp, XAXIDMA_RX_CR_OFFSET, cr);
-
-       cr = axienet_dma_in32(lp, XAXIDMA_TX_CR_OFFSET);
-       cr &= ~(XAXIDMA_CR_RUNSTOP_MASK | XAXIDMA_IRQ_ALL_MASK);
-       axienet_dma_out32(lp, XAXIDMA_TX_CR_OFFSET, cr);
+       axienet_dma_stop(lp);
 
        axienet_iow(lp, XAE_IE_OFFSET, 0);
 
-       /* Give DMAs a chance to halt gracefully */
-       sr = axienet_dma_in32(lp, XAXIDMA_RX_SR_OFFSET);
-       for (count = 0; !(sr & XAXIDMA_SR_HALT_MASK) && count < 5; ++count) {
-               msleep(20);
-               sr = axienet_dma_in32(lp, XAXIDMA_RX_SR_OFFSET);
-       }
-
-       sr = axienet_dma_in32(lp, XAXIDMA_TX_SR_OFFSET);
-       for (count = 0; !(sr & XAXIDMA_SR_HALT_MASK) && count < 5; ++count) {
-               msleep(20);
-               sr = axienet_dma_in32(lp, XAXIDMA_TX_SR_OFFSET);
-       }
-
-       /* Do a reset to ensure DMA is really stopped */
-       axienet_lock_mii(lp);
-       __axienet_device_reset(lp);
-       axienet_unlock_mii(lp);
-
        cancel_work_sync(&lp->dma_err_task);
 
        if (lp->eth_irq > 0)
@@ -1690,22 +1668,17 @@ static const struct phylink_mac_ops axienet_phylink_ops = {
  */
 static void axienet_dma_err_handler(struct work_struct *work)
 {
+       u32 i;
        u32 axienet_status;
-       u32 cr, i;
+       struct axidma_bd *cur_p;
        struct axienet_local *lp = container_of(work, struct axienet_local,
                                                dma_err_task);
        struct net_device *ndev = lp->ndev;
-       struct axidma_bd *cur_p;
 
        axienet_setoptions(ndev, lp->options &
                           ~(XAE_OPTION_TXEN | XAE_OPTION_RXEN));
-       /* When we do an Axi Ethernet reset, it resets the complete core
-        * including the MDIO. MDIO must be disabled before resetting.
-        * Hold MDIO bus lock to avoid MDIO accesses during the reset.
-        */
-       axienet_lock_mii(lp);
-       __axienet_device_reset(lp);
-       axienet_unlock_mii(lp);
+
+       axienet_dma_stop(lp);
 
        for (i = 0; i < lp->tx_bd_num; i++) {
                cur_p = &lp->tx_bd_v[i];
@@ -1745,50 +1718,7 @@ static void axienet_dma_err_handler(struct work_struct *work)
        lp->tx_bd_tail = 0;
        lp->rx_bd_ci = 0;
 
-       /* Start updating the Rx channel control register */
-       cr = axienet_dma_in32(lp, XAXIDMA_RX_CR_OFFSET);
-       /* Update the interrupt coalesce count */
-       cr = ((cr & ~XAXIDMA_COALESCE_MASK) |
-             (XAXIDMA_DFT_RX_THRESHOLD << XAXIDMA_COALESCE_SHIFT));
-       /* Update the delay timer count */
-       cr = ((cr & ~XAXIDMA_DELAY_MASK) |
-             (XAXIDMA_DFT_RX_WAITBOUND << XAXIDMA_DELAY_SHIFT));
-       /* Enable coalesce, delay timer and error interrupts */
-       cr |= XAXIDMA_IRQ_ALL_MASK;
-       /* Finally write to the Rx channel control register */
-       axienet_dma_out32(lp, XAXIDMA_RX_CR_OFFSET, cr);
-
-       /* Start updating the Tx channel control register */
-       cr = axienet_dma_in32(lp, XAXIDMA_TX_CR_OFFSET);
-       /* Update the interrupt coalesce count */
-       cr = (((cr & ~XAXIDMA_COALESCE_MASK)) |
-             (XAXIDMA_DFT_TX_THRESHOLD << XAXIDMA_COALESCE_SHIFT));
-       /* Update the delay timer count */
-       cr = (((cr & ~XAXIDMA_DELAY_MASK)) |
-             (XAXIDMA_DFT_TX_WAITBOUND << XAXIDMA_DELAY_SHIFT));
-       /* Enable coalesce, delay timer and error interrupts */
-       cr |= XAXIDMA_IRQ_ALL_MASK;
-       /* Finally write to the Tx channel control register */
-       axienet_dma_out32(lp, XAXIDMA_TX_CR_OFFSET, cr);
-
-       /* Populate the tail pointer and bring the Rx Axi DMA engine out of
-        * halted state. This will make the Rx side ready for reception.
-        */
-       axienet_dma_out_addr(lp, XAXIDMA_RX_CDESC_OFFSET, lp->rx_bd_p);
-       cr = axienet_dma_in32(lp, XAXIDMA_RX_CR_OFFSET);
-       axienet_dma_out32(lp, XAXIDMA_RX_CR_OFFSET,
-                         cr | XAXIDMA_CR_RUNSTOP_MASK);
-       axienet_dma_out_addr(lp, XAXIDMA_RX_TDESC_OFFSET, lp->rx_bd_p +
-                            (sizeof(*lp->rx_bd_v) * (lp->rx_bd_num - 1)));
-
-       /* Write to the RS (Run-stop) bit in the Tx channel control register.
-        * Tx channel is now ready to run. But only after we write to the
-        * tail pointer register that the Tx channel will start transmitting
-        */
-       axienet_dma_out_addr(lp, XAXIDMA_TX_CDESC_OFFSET, lp->tx_bd_p);
-       cr = axienet_dma_in32(lp, XAXIDMA_TX_CR_OFFSET);
-       axienet_dma_out32(lp, XAXIDMA_TX_CR_OFFSET,
-                         cr | XAXIDMA_CR_RUNSTOP_MASK);
+       axienet_dma_start(lp);
 
        axienet_status = axienet_ior(lp, XAE_RCW1_OFFSET);
        axienet_status &= ~XAE_RCW1_RX_MASK;