From d2aeca6b6dbb96121b823200b15b6b2beae959f0 Mon Sep 17 00:00:00 2001 From: Felix Fietkau <nbd@openwrt.org> Date: Thu, 4 Aug 2011 17:36:27 +0000 Subject: [PATCH] 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 --- .../ar71xx/files/drivers/net/ag71xx/ag71xx.h | 3 +++ .../files/drivers/net/ag71xx/ag71xx_main.c | 19 ++++++++++++++++--- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/target/linux/ar71xx/files/drivers/net/ag71xx/ag71xx.h b/target/linux/ar71xx/files/drivers/net/ag71xx/ag71xx.h index 1025bb4c49..9dcbf4a5b2 100644 --- a/target/linux/ar71xx/files/drivers/net/ag71xx/ag71xx.h +++ b/target/linux/ar71xx/files/drivers/net/ag71xx/ag71xx.h @@ -148,6 +148,9 @@ struct ag71xx { struct napi_struct napi; u32 msg_enable; + struct ag71xx_desc *stop_desc; + dma_addr_t stop_desc_dma; + struct ag71xx_ring rx_ring; struct ag71xx_ring tx_ring; diff --git a/target/linux/ar71xx/files/drivers/net/ag71xx/ag71xx_main.c b/target/linux/ar71xx/files/drivers/net/ag71xx/ag71xx_main.c index 1f9cdf3b7d..9bfaa82137 100644 --- a/target/linux/ar71xx/files/drivers/net/ag71xx/ag71xx_main.c +++ b/target/linux/ar71xx/files/drivers/net/ag71xx/ag71xx_main.c @@ -374,8 +374,8 @@ static void ag71xx_dma_reset(struct ag71xx *ag) mdelay(1); /* clear descriptor addresses */ - ag71xx_wr(ag, AG71XX_REG_TX_DESC, 0); - ag71xx_wr(ag, AG71XX_REG_RX_DESC, 0); + ag71xx_wr(ag, AG71XX_REG_TX_DESC, ag->stop_desc_dma); + ag71xx_wr(ag, AG71XX_REG_RX_DESC, ag->stop_desc_dma); /* clear pending RX/TX interrupts */ 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->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); 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); if (err) { 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", @@ -1120,6 +1130,9 @@ err_phy_disconnect: ag71xx_phy_disconnect(ag); err_unregister_netdev: unregister_netdev(dev); +err_free_desc: + dma_free_coherent(NULL, sizeof(struct ag71xx_desc), ag->stop_desc, + ag->stop_desc_dma); err_free_irq: free_irq(dev->irq, dev); err_unmap_mii_ctrl: