Commit 522856ce authored by Robert Hancock's avatar Robert Hancock Committed by David S. Miller

net: axienet: Add optional support for Ethernet core interrupt

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: default avatarRobert Hancock <hancock@sedsystems.ca>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 9cbc1b68
...@@ -18,7 +18,8 @@ Required properties: ...@@ -18,7 +18,8 @@ Required properties:
- compatible : Must be one of "xlnx,axi-ethernet-1.00.a", - compatible : Must be one of "xlnx,axi-ethernet-1.00.a",
"xlnx,axi-ethernet-1.01.a", "xlnx,axi-ethernet-2.01.a" "xlnx,axi-ethernet-1.01.a", "xlnx,axi-ethernet-2.01.a"
- reg : Address and length of the IO space. - 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. - phy-handle : Should point to the external phy device.
See ethernet.txt file in the same directory. See ethernet.txt file in the same directory.
- xlnx,rxmem : Set to allocated memory buffer for Rx/Tx in the hardware - xlnx,rxmem : Set to allocated memory buffer for Rx/Tx in the hardware
......
...@@ -435,6 +435,7 @@ struct axienet_local { ...@@ -435,6 +435,7 @@ struct axienet_local {
int tx_irq; int tx_irq;
int rx_irq; int rx_irq;
int eth_irq;
phy_interface_t phy_mode; phy_interface_t phy_mode;
u32 options; /* Current options word */ u32 options; /* Current options word */
......
...@@ -502,6 +502,8 @@ static void axienet_device_reset(struct net_device *ndev) ...@@ -502,6 +502,8 @@ static void axienet_device_reset(struct net_device *ndev)
axienet_status = axienet_ior(lp, XAE_IP_OFFSET); axienet_status = axienet_ior(lp, XAE_IP_OFFSET);
if (axienet_status & XAE_INT_RXRJECT_MASK) if (axienet_status & XAE_INT_RXRJECT_MASK)
axienet_iow(lp, XAE_IS_OFFSET, 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); axienet_iow(lp, XAE_FCC_OFFSET, XAE_FCC_FCRX_MASK);
...@@ -902,6 +904,35 @@ static irqreturn_t axienet_rx_irq(int irq, void *_ndev) ...@@ -902,6 +904,35 @@ static irqreturn_t axienet_rx_irq(int irq, void *_ndev)
return IRQ_HANDLED; 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); static void axienet_dma_err_handler(unsigned long data);
/** /**
...@@ -962,9 +993,18 @@ static int axienet_open(struct net_device *ndev) ...@@ -962,9 +993,18 @@ static int axienet_open(struct net_device *ndev)
ndev->name, ndev); ndev->name, ndev);
if (ret) if (ret)
goto err_rx_irq; 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; return 0;
err_eth_irq:
free_irq(lp->rx_irq, ndev);
err_rx_irq: err_rx_irq:
free_irq(lp->tx_irq, ndev); free_irq(lp->tx_irq, ndev);
err_tx_irq: err_tx_irq:
...@@ -1028,6 +1068,8 @@ static int axienet_stop(struct net_device *ndev) ...@@ -1028,6 +1068,8 @@ static int axienet_stop(struct net_device *ndev)
tasklet_kill(&lp->dma_err_tasklet); 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->tx_irq, ndev);
free_irq(lp->rx_irq, ndev); free_irq(lp->rx_irq, ndev);
...@@ -1491,6 +1533,8 @@ static void axienet_dma_err_handler(unsigned long data) ...@@ -1491,6 +1533,8 @@ static void axienet_dma_err_handler(unsigned long data)
axienet_status = axienet_ior(lp, XAE_IP_OFFSET); axienet_status = axienet_ior(lp, XAE_IP_OFFSET);
if (axienet_status & XAE_INT_RXRJECT_MASK) if (axienet_status & XAE_INT_RXRJECT_MASK)
axienet_iow(lp, XAE_IS_OFFSET, 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); axienet_iow(lp, XAE_FCC_OFFSET, XAE_FCC_FCRX_MASK);
/* Sync default options with HW but leave receiver and /* Sync default options with HW but leave receiver and
...@@ -1660,6 +1704,7 @@ static int axienet_probe(struct platform_device *pdev) ...@@ -1660,6 +1704,7 @@ static int axienet_probe(struct platform_device *pdev)
} }
lp->rx_irq = irq_of_parse_and_map(np, 1); lp->rx_irq = irq_of_parse_and_map(np, 1);
lp->tx_irq = irq_of_parse_and_map(np, 0); lp->tx_irq = irq_of_parse_and_map(np, 0);
lp->eth_irq = irq_of_parse_and_map(np, 2);
of_node_put(np); of_node_put(np);
if ((lp->rx_irq <= 0) || (lp->tx_irq <= 0)) { if ((lp->rx_irq <= 0) || (lp->tx_irq <= 0)) {
dev_err(&pdev->dev, "could not determine irqs\n"); dev_err(&pdev->dev, "could not determine irqs\n");
...@@ -1667,6 +1712,10 @@ static int axienet_probe(struct platform_device *pdev) ...@@ -1667,6 +1712,10 @@ static int axienet_probe(struct platform_device *pdev)
goto free_netdev; 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 */ /* Retrieve the MAC address */
mac_addr = of_get_mac_address(pdev->dev.of_node); mac_addr = of_get_mac_address(pdev->dev.of_node);
if (IS_ERR(mac_addr)) { if (IS_ERR(mac_addr)) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment