ag71xx: fix memory corruption issues on ar7240 on ethernet start/stop
When the DMA engine state gets corrupted due to a hardware issues, it often won't stop rx until a full reset is issued. In that case the hardware must keep a valid descriptor, otherwise it will write to random places in system RAM, triggering random crashes. To fix this, keep a dummy descriptor without a buffer that keeps the DMA engine in a sane state until the reset is done SVN-Revision: 27895
This commit is contained in:
parent
500c3a1475
commit
d2aeca6b6d
2 changed files with 19 additions and 3 deletions
|
@ -148,6 +148,9 @@ struct ag71xx {
|
||||||
struct napi_struct napi;
|
struct napi_struct napi;
|
||||||
u32 msg_enable;
|
u32 msg_enable;
|
||||||
|
|
||||||
|
struct ag71xx_desc *stop_desc;
|
||||||
|
dma_addr_t stop_desc_dma;
|
||||||
|
|
||||||
struct ag71xx_ring rx_ring;
|
struct ag71xx_ring rx_ring;
|
||||||
struct ag71xx_ring tx_ring;
|
struct ag71xx_ring tx_ring;
|
||||||
|
|
||||||
|
|
|
@ -374,8 +374,8 @@ static void ag71xx_dma_reset(struct ag71xx *ag)
|
||||||
mdelay(1);
|
mdelay(1);
|
||||||
|
|
||||||
/* clear descriptor addresses */
|
/* clear descriptor addresses */
|
||||||
ag71xx_wr(ag, AG71XX_REG_TX_DESC, 0);
|
ag71xx_wr(ag, AG71XX_REG_TX_DESC, ag->stop_desc_dma);
|
||||||
ag71xx_wr(ag, AG71XX_REG_RX_DESC, 0);
|
ag71xx_wr(ag, AG71XX_REG_RX_DESC, ag->stop_desc_dma);
|
||||||
|
|
||||||
/* clear pending RX/TX interrupts */
|
/* clear pending RX/TX interrupts */
|
||||||
for (i = 0; i < 256; i++) {
|
for (i = 0; i < 256; i++) {
|
||||||
|
@ -1085,6 +1085,16 @@ static int __devinit ag71xx_probe(struct platform_device *pdev)
|
||||||
ag->tx_ring.size = AG71XX_TX_RING_SIZE_DEFAULT;
|
ag->tx_ring.size = AG71XX_TX_RING_SIZE_DEFAULT;
|
||||||
ag->rx_ring.size = AG71XX_RX_RING_SIZE_DEFAULT;
|
ag->rx_ring.size = AG71XX_RX_RING_SIZE_DEFAULT;
|
||||||
|
|
||||||
|
ag->stop_desc = dma_alloc_coherent(NULL,
|
||||||
|
sizeof(struct ag71xx_desc), &ag->stop_desc_dma, GFP_KERNEL);
|
||||||
|
|
||||||
|
if (!ag->stop_desc)
|
||||||
|
goto err_free_irq;
|
||||||
|
|
||||||
|
ag->stop_desc->data = 0;
|
||||||
|
ag->stop_desc->ctrl = 0;
|
||||||
|
ag->stop_desc->next = (u32) ag->stop_desc_dma;
|
||||||
|
|
||||||
memcpy(dev->dev_addr, pdata->mac_addr, ETH_ALEN);
|
memcpy(dev->dev_addr, pdata->mac_addr, ETH_ALEN);
|
||||||
|
|
||||||
netif_napi_add(dev, &ag->napi, ag71xx_poll, AG71XX_NAPI_WEIGHT);
|
netif_napi_add(dev, &ag->napi, ag71xx_poll, AG71XX_NAPI_WEIGHT);
|
||||||
|
@ -1092,7 +1102,7 @@ static int __devinit ag71xx_probe(struct platform_device *pdev)
|
||||||
err = register_netdev(dev);
|
err = register_netdev(dev);
|
||||||
if (err) {
|
if (err) {
|
||||||
dev_err(&pdev->dev, "unable to register net device\n");
|
dev_err(&pdev->dev, "unable to register net device\n");
|
||||||
goto err_free_irq;
|
goto err_free_desc;
|
||||||
}
|
}
|
||||||
|
|
||||||
printk(KERN_INFO "%s: Atheros AG71xx at 0x%08lx, irq %d\n",
|
printk(KERN_INFO "%s: Atheros AG71xx at 0x%08lx, irq %d\n",
|
||||||
|
@ -1120,6 +1130,9 @@ err_phy_disconnect:
|
||||||
ag71xx_phy_disconnect(ag);
|
ag71xx_phy_disconnect(ag);
|
||||||
err_unregister_netdev:
|
err_unregister_netdev:
|
||||||
unregister_netdev(dev);
|
unregister_netdev(dev);
|
||||||
|
err_free_desc:
|
||||||
|
dma_free_coherent(NULL, sizeof(struct ag71xx_desc), ag->stop_desc,
|
||||||
|
ag->stop_desc_dma);
|
||||||
err_free_irq:
|
err_free_irq:
|
||||||
free_irq(dev->irq, dev);
|
free_irq(dev->irq, dev);
|
||||||
err_unmap_mii_ctrl:
|
err_unmap_mii_ctrl:
|
||||||
|
|
Loading…
Reference in a new issue