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: