diff options
author | Felix Fietkau <nbd@openwrt.org> | 2011-08-04 17:36:27 +0000 |
---|---|---|
committer | Felix Fietkau <nbd@openwrt.org> | 2011-08-04 17:36:27 +0000 |
commit | d2aeca6b6dbb96121b823200b15b6b2beae959f0 (patch) | |
tree | 3ae86d895056e617a12082772febdde984b011a1 | |
parent | 500c3a147574fae2b953b00514b380b5adc9b097 (diff) | |
download | mtk-20170518-d2aeca6b6dbb96121b823200b15b6b2beae959f0.zip mtk-20170518-d2aeca6b6dbb96121b823200b15b6b2beae959f0.tar.gz mtk-20170518-d2aeca6b6dbb96121b823200b15b6b2beae959f0.tar.bz2 |
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
-rw-r--r-- | target/linux/ar71xx/files/drivers/net/ag71xx/ag71xx.h | 3 | ||||
-rw-r--r-- | target/linux/ar71xx/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 1025bb4..9dcbf4a 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 1f9cdf3..9bfaa82 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: |