Commit 84b9ccc0 authored by Robert Hancock's avatar Robert Hancock Committed by David S. Miller

net: axienet: Clean up DMA start/stop and error handling

Simplify the DMA error handling process, and remove some duplicated code
between the DMA error handling and the stop function.
Signed-off-by: default avatarRobert Hancock <robert.hancock@calian.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 17882fd4
...@@ -226,6 +226,44 @@ static void axienet_dma_bd_release(struct net_device *ndev) ...@@ -226,6 +226,44 @@ static void axienet_dma_bd_release(struct net_device *ndev)
lp->rx_bd_p); lp->rx_bd_p);
} }
/**
* 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 * axienet_dma_bd_init - Setup buffer descriptor rings for Axi DMA
* @ndev: Pointer to the net_device structure * @ndev: Pointer to the net_device structure
...@@ -238,7 +276,6 @@ static void axienet_dma_bd_release(struct net_device *ndev) ...@@ -238,7 +276,6 @@ static void axienet_dma_bd_release(struct net_device *ndev)
*/ */
static int axienet_dma_bd_init(struct net_device *ndev) static int axienet_dma_bd_init(struct net_device *ndev)
{ {
u32 cr;
int i; int i;
struct sk_buff *skb; struct sk_buff *skb;
struct axienet_local *lp = netdev_priv(ndev); struct axienet_local *lp = netdev_priv(ndev);
...@@ -296,50 +333,7 @@ static int axienet_dma_bd_init(struct net_device *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; lp->rx_bd_v[i].cntrl = lp->max_frm_size;
} }
/* Start updating the Rx channel control register */ axienet_dma_start(lp);
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);
return 0; return 0;
out: out:
...@@ -530,6 +524,44 @@ static int __axienet_device_reset(struct axienet_local *lp) ...@@ -530,6 +524,44 @@ static int __axienet_device_reset(struct axienet_local *lp)
return 0; return 0;
} }
/**
* 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. * axienet_device_reset - Reset and initialize the Axi Ethernet hardware.
* @ndev: Pointer to the net_device structure * @ndev: Pointer to the net_device structure
...@@ -949,41 +981,27 @@ static void axienet_recv(struct net_device *ndev) ...@@ -949,41 +981,27 @@ static void axienet_recv(struct net_device *ndev)
*/ */
static irqreturn_t axienet_tx_irq(int irq, void *_ndev) static irqreturn_t axienet_tx_irq(int irq, void *_ndev)
{ {
u32 cr;
unsigned int status; unsigned int status;
struct net_device *ndev = _ndev; struct net_device *ndev = _ndev;
struct axienet_local *lp = netdev_priv(ndev); struct axienet_local *lp = netdev_priv(ndev);
status = axienet_dma_in32(lp, XAXIDMA_TX_SR_OFFSET); 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)) if (!(status & XAXIDMA_IRQ_ALL_MASK))
return IRQ_NONE; 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); axienet_dma_out32(lp, XAXIDMA_TX_SR_OFFSET, status);
/* 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);
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); 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; return IRQ_HANDLED;
} }
...@@ -999,41 +1017,27 @@ static irqreturn_t axienet_tx_irq(int irq, void *_ndev) ...@@ -999,41 +1017,27 @@ static irqreturn_t axienet_tx_irq(int irq, void *_ndev)
*/ */
static irqreturn_t axienet_rx_irq(int irq, void *_ndev) static irqreturn_t axienet_rx_irq(int irq, void *_ndev)
{ {
u32 cr;
unsigned int status; unsigned int status;
struct net_device *ndev = _ndev; struct net_device *ndev = _ndev;
struct axienet_local *lp = netdev_priv(ndev); struct axienet_local *lp = netdev_priv(ndev);
status = axienet_dma_in32(lp, XAXIDMA_RX_SR_OFFSET); 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)) if (!(status & XAXIDMA_IRQ_ALL_MASK))
return IRQ_NONE; 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); axienet_dma_out32(lp, XAXIDMA_RX_SR_OFFSET, status);
/* 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);
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); schedule_work(&lp->dma_err_task);
axienet_dma_out32(lp, XAXIDMA_RX_SR_OFFSET, status); } else {
axienet_recv(lp->ndev);
} }
out:
return IRQ_HANDLED; return IRQ_HANDLED;
} }
...@@ -1151,8 +1155,6 @@ static int axienet_open(struct net_device *ndev) ...@@ -1151,8 +1155,6 @@ static int axienet_open(struct net_device *ndev)
*/ */
static int axienet_stop(struct net_device *ndev) static int axienet_stop(struct net_device *ndev)
{ {
u32 cr, sr;
int count;
struct axienet_local *lp = netdev_priv(ndev); struct axienet_local *lp = netdev_priv(ndev);
dev_dbg(&ndev->dev, "axienet_close()\n"); dev_dbg(&ndev->dev, "axienet_close()\n");
...@@ -1163,34 +1165,10 @@ static int axienet_stop(struct net_device *ndev) ...@@ -1163,34 +1165,10 @@ static int axienet_stop(struct net_device *ndev)
axienet_setoptions(ndev, lp->options & axienet_setoptions(ndev, lp->options &
~(XAE_OPTION_TXEN | XAE_OPTION_RXEN)); ~(XAE_OPTION_TXEN | XAE_OPTION_RXEN));
cr = axienet_dma_in32(lp, XAXIDMA_RX_CR_OFFSET); axienet_dma_stop(lp);
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_iow(lp, XAE_IE_OFFSET, 0); 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); cancel_work_sync(&lp->dma_err_task);
if (lp->eth_irq > 0) if (lp->eth_irq > 0)
...@@ -1690,22 +1668,17 @@ static const struct phylink_mac_ops axienet_phylink_ops = { ...@@ -1690,22 +1668,17 @@ static const struct phylink_mac_ops axienet_phylink_ops = {
*/ */
static void axienet_dma_err_handler(struct work_struct *work) static void axienet_dma_err_handler(struct work_struct *work)
{ {
u32 i;
u32 axienet_status; u32 axienet_status;
u32 cr, i; struct axidma_bd *cur_p;
struct axienet_local *lp = container_of(work, struct axienet_local, struct axienet_local *lp = container_of(work, struct axienet_local,
dma_err_task); dma_err_task);
struct net_device *ndev = lp->ndev; struct net_device *ndev = lp->ndev;
struct axidma_bd *cur_p;
axienet_setoptions(ndev, lp->options & axienet_setoptions(ndev, lp->options &
~(XAE_OPTION_TXEN | XAE_OPTION_RXEN)); ~(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. axienet_dma_stop(lp);
* Hold MDIO bus lock to avoid MDIO accesses during the reset.
*/
axienet_lock_mii(lp);
__axienet_device_reset(lp);
axienet_unlock_mii(lp);
for (i = 0; i < lp->tx_bd_num; i++) { for (i = 0; i < lp->tx_bd_num; i++) {
cur_p = &lp->tx_bd_v[i]; cur_p = &lp->tx_bd_v[i];
...@@ -1745,50 +1718,7 @@ static void axienet_dma_err_handler(struct work_struct *work) ...@@ -1745,50 +1718,7 @@ static void axienet_dma_err_handler(struct work_struct *work)
lp->tx_bd_tail = 0; lp->tx_bd_tail = 0;
lp->rx_bd_ci = 0; lp->rx_bd_ci = 0;
/* Start updating the Rx channel control register */ axienet_dma_start(lp);
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_status = axienet_ior(lp, XAE_RCW1_OFFSET); axienet_status = axienet_ior(lp, XAE_RCW1_OFFSET);
axienet_status &= ~XAE_RCW1_RX_MASK; axienet_status &= ~XAE_RCW1_RX_MASK;
......
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