]> git.proxmox.com Git - mirror_ubuntu-eoan-kernel.git/commitdiff
net: axienet: Add optional support for Ethernet core interrupt
authorRobert Hancock <hancock@sedsystems.ca>
Thu, 6 Jun 2019 22:28:16 +0000 (16:28 -0600)
committerDavid S. Miller <davem@davemloft.net>
Thu, 6 Jun 2019 23:24:29 +0000 (16:24 -0700)
Previously this driver only handled interrupts from the DMA RX and TX
blocks, not from the Ethernet core itself. Add optional support for
the Ethernet core interrupt, which is used to detect rx_missed and
framing errors signalled by the hardware. In order to use this
interrupt, a third interrupt needs to be specified in the device tree.

Signed-off-by: Robert Hancock <hancock@sedsystems.ca>
Signed-off-by: David S. Miller <davem@davemloft.net>
Documentation/devicetree/bindings/net/xilinx_axienet.txt
drivers/net/ethernet/xilinx/xilinx_axienet.h
drivers/net/ethernet/xilinx/xilinx_axienet_main.c

index 2dea90332ebb5f0d8f0619a5a6e30d619ccd892e..0be335c7abede290a74967276f592a4471311acd 100644 (file)
@@ -18,7 +18,8 @@ Required properties:
 - compatible   : Must be one of "xlnx,axi-ethernet-1.00.a",
                  "xlnx,axi-ethernet-1.01.a", "xlnx,axi-ethernet-2.01.a"
 - reg          : Address and length of the IO space.
-- interrupts   : Should be a list of two interrupt, TX and RX.
+- interrupts   : Should be a list of 2 or 3 interrupts: TX DMA, RX DMA,
+                 and optionally Ethernet core.
 - phy-handle   : Should point to the external phy device.
                  See ethernet.txt file in the same directory.
 - xlnx,rxmem   : Set to allocated memory buffer for Rx/Tx in the hardware
index 6b6d28ff014fa211b4a8febf0d040c2baefb6b78..8e605a84b24d2428f4cd972fc2c5929ee8b5e0ca 100644 (file)
@@ -435,6 +435,7 @@ struct axienet_local {
 
        int tx_irq;
        int rx_irq;
+       int eth_irq;
        phy_interface_t phy_mode;
 
        u32 options;                    /* Current options word */
index f733a7a1d07acc48d361c4d6d57c8b59949382e9..aa51a6eed15a96cb4fbf853c9bc1a7bbd21ccc78 100644 (file)
@@ -502,6 +502,8 @@ static void axienet_device_reset(struct net_device *ndev)
        axienet_status = axienet_ior(lp, XAE_IP_OFFSET);
        if (axienet_status & XAE_INT_RXRJECT_MASK)
                axienet_iow(lp, XAE_IS_OFFSET, XAE_INT_RXRJECT_MASK);
+       axienet_iow(lp, XAE_IE_OFFSET, lp->eth_irq > 0 ?
+                   XAE_INT_RECV_ERROR_MASK : 0);
 
        axienet_iow(lp, XAE_FCC_OFFSET, XAE_FCC_FCRX_MASK);
 
@@ -902,6 +904,35 @@ out:
        return IRQ_HANDLED;
 }
 
+/**
+ * axienet_eth_irq - Ethernet core Isr.
+ * @irq:       irq number
+ * @_ndev:     net_device pointer
+ *
+ * Return: IRQ_HANDLED if device generated a core interrupt, IRQ_NONE otherwise.
+ *
+ * Handle miscellaneous conditions indicated by Ethernet core IRQ.
+ */
+static irqreturn_t axienet_eth_irq(int irq, void *_ndev)
+{
+       struct net_device *ndev = _ndev;
+       struct axienet_local *lp = netdev_priv(ndev);
+       unsigned int pending;
+
+       pending = axienet_ior(lp, XAE_IP_OFFSET);
+       if (!pending)
+               return IRQ_NONE;
+
+       if (pending & XAE_INT_RXFIFOOVR_MASK)
+               ndev->stats.rx_missed_errors++;
+
+       if (pending & XAE_INT_RXRJECT_MASK)
+               ndev->stats.rx_frame_errors++;
+
+       axienet_iow(lp, XAE_IS_OFFSET, pending);
+       return IRQ_HANDLED;
+}
+
 static void axienet_dma_err_handler(unsigned long data);
 
 /**
@@ -962,9 +993,18 @@ static int axienet_open(struct net_device *ndev)
                          ndev->name, ndev);
        if (ret)
                goto err_rx_irq;
+       /* Enable interrupts for Axi Ethernet core (if defined) */
+       if (lp->eth_irq > 0) {
+               ret = request_irq(lp->eth_irq, axienet_eth_irq, IRQF_SHARED,
+                                 ndev->name, ndev);
+               if (ret)
+                       goto err_eth_irq;
+       }
 
        return 0;
 
+err_eth_irq:
+       free_irq(lp->rx_irq, ndev);
 err_rx_irq:
        free_irq(lp->tx_irq, ndev);
 err_tx_irq:
@@ -1028,6 +1068,8 @@ static int axienet_stop(struct net_device *ndev)
 
        tasklet_kill(&lp->dma_err_tasklet);
 
+       if (lp->eth_irq > 0)
+               free_irq(lp->eth_irq, ndev);
        free_irq(lp->tx_irq, ndev);
        free_irq(lp->rx_irq, ndev);
 
@@ -1491,6 +1533,8 @@ static void axienet_dma_err_handler(unsigned long data)
        axienet_status = axienet_ior(lp, XAE_IP_OFFSET);
        if (axienet_status & XAE_INT_RXRJECT_MASK)
                axienet_iow(lp, XAE_IS_OFFSET, XAE_INT_RXRJECT_MASK);
+       axienet_iow(lp, XAE_IE_OFFSET, lp->eth_irq > 0 ?
+                   XAE_INT_RECV_ERROR_MASK : 0);
        axienet_iow(lp, XAE_FCC_OFFSET, XAE_FCC_FCRX_MASK);
 
        /* Sync default options with HW but leave receiver and
@@ -1660,6 +1704,7 @@ static int axienet_probe(struct platform_device *pdev)
        }
        lp->rx_irq = irq_of_parse_and_map(np, 1);
        lp->tx_irq = irq_of_parse_and_map(np, 0);
+       lp->eth_irq = irq_of_parse_and_map(np, 2);
        of_node_put(np);
        if ((lp->rx_irq <= 0) || (lp->tx_irq <= 0)) {
                dev_err(&pdev->dev, "could not determine irqs\n");
@@ -1667,6 +1712,10 @@ static int axienet_probe(struct platform_device *pdev)
                goto free_netdev;
        }
 
+       /* Check for Ethernet core IRQ (optional) */
+       if (lp->eth_irq <= 0)
+               dev_info(&pdev->dev, "Ethernet core IRQ not defined\n");
+
        /* Retrieve the MAC address */
        mac_addr = of_get_mac_address(pdev->dev.of_node);
        if (IS_ERR(mac_addr)) {