From 50588ef192d7141040a4f42902a71312b2a5ee75 Mon Sep 17 00:00:00 2001
From: Felix Fietkau <nbd@openwrt.org>
Date: Sun, 18 Jan 2015 20:16:50 +0000
Subject: [PATCH] ralink: improve tx_timeout function

* use default timeout value
* print more debug ring info
* move timeout reset function to workqueue

Signed-off-by: michael lee <igvtee@gmail.com>

SVN-Revision: 44043
---
 .../net/ethernet/ralink/ralink_soc_eth.c      | 85 ++++++++++++++++---
 .../net/ethernet/ralink/ralink_soc_eth.h      | 11 ++-
 .../drivers/net/ethernet/ralink/soc_mt7620.c  |  4 +
 .../drivers/net/ethernet/ralink/soc_rt305x.c  |  2 +
 4 files changed, 87 insertions(+), 15 deletions(-)

diff --git a/target/linux/ramips/files/drivers/net/ethernet/ralink/ralink_soc_eth.c b/target/linux/ramips/files/drivers/net/ethernet/ralink/ralink_soc_eth.c
index ca78d71597..4252ff3ad2 100644
--- a/target/linux/ramips/files/drivers/net/ethernet/ralink/ralink_soc_eth.c
+++ b/target/linux/ramips/files/drivers/net/ethernet/ralink/ralink_soc_eth.c
@@ -40,7 +40,6 @@
 #include "mdio.h"
 #include "ralink_ethtool.h"
 
-#define TX_TIMEOUT		(2 * HZ)
 #define	MAX_RX_LENGTH		1536
 #define FE_RX_OFFSET		(NET_SKB_PAD + NET_IP_ALIGN)
 #define FE_RX_HLEN		(FE_RX_OFFSET + VLAN_ETH_HLEN + VLAN_HLEN + \
@@ -72,9 +71,11 @@ static const u32 fe_reg_table_default[FE_REG_COUNT] = {
 	[FE_REG_TX_BASE_PTR0] = FE_TX_BASE_PTR0,
 	[FE_REG_TX_MAX_CNT0] = FE_TX_MAX_CNT0,
 	[FE_REG_TX_CTX_IDX0] = FE_TX_CTX_IDX0,
+	[FE_REG_TX_DTX_IDX0] = FE_TX_DTX_IDX0,
 	[FE_REG_RX_BASE_PTR0] = FE_RX_BASE_PTR0,
 	[FE_REG_RX_MAX_CNT0] = FE_RX_MAX_CNT0,
 	[FE_REG_RX_CALC_IDX0] = FE_RX_CALC_IDX0,
+	[FE_REG_RX_DRX_IDX0] = FE_RX_DRX_IDX0,
 	[FE_REG_FE_INT_ENABLE] = FE_FE_INT_ENABLE,
 	[FE_REG_FE_INT_STATUS] = FE_FE_INT_STATUS,
 	[FE_REG_FE_DMA_VID_BASE] = FE_DMA_VID0,
@@ -84,6 +85,11 @@ static const u32 fe_reg_table_default[FE_REG_COUNT] = {
 
 static const u32 *fe_reg_table = fe_reg_table_default;
 
+struct fe_work_t {
+	int bitnr;
+	void (*action)(struct fe_priv *);
+};
+
 static void __iomem *fe_base = 0;
 
 void fe_w32(u32 val, unsigned reg)
@@ -683,13 +689,11 @@ static int fe_start_xmit(struct sk_buff *skb, struct net_device *dev)
 		return NETDEV_TX_OK;
 	}
 
-	spin_lock(&priv->page_lock);
 	tx_num = 1 + (skb_shinfo(skb)->nr_frags >> 1);
 	tx = fe_reg_r32(FE_REG_TX_CTX_IDX0);
 	if (unlikely(fe_empty_txd(priv, tx) <= tx_num))
 	{
 		netif_stop_queue(dev);
-		spin_unlock(&priv->page_lock);
 		netif_err(priv, tx_queued,dev,
 				"Tx Ring full when queue awake!\n");
 		return NETDEV_TX_BUSY;
@@ -704,8 +708,6 @@ static int fe_start_xmit(struct sk_buff *skb, struct net_device *dev)
 		stats->tx_bytes += len;
 	}
 
-	spin_unlock(&priv->page_lock);
-
 	return NETDEV_TX_OK;
 }
 
@@ -779,8 +781,8 @@ static int fe_poll_rx(struct napi_struct *napi, int budget,
 		dma_unmap_single(&netdev->dev, trxd.rxd1,
 				priv->rx_buf_size, DMA_FROM_DEVICE);
 		pktlen = RX_DMA_PLEN0(trxd.rxd2);
-		skb_put(skb, pktlen);
 		skb->dev = netdev;
+		skb_put(skb, pktlen);
 		if (trxd.rxd4 & checksum_bit) {
 			skb->ip_summed = CHECKSUM_UNNECESSARY;
 		} else {
@@ -902,7 +904,7 @@ poll_again:
 	if (unlikely(netif_msg_intr(priv))) {
 		mask = fe_reg_r32(FE_REG_FE_INT_ENABLE);
 		netdev_info(priv->netdev,
-				"done tx %d, rx %d, intr 0x%x/0x%x\n",
+				"done tx %d, rx %d, intr 0x%08x/0x%x\n",
 				tx_done, rx_done, status, mask);
 	}
 
@@ -924,13 +926,27 @@ static void fe_tx_timeout(struct net_device *dev)
 
 	priv->netdev->stats.tx_errors++;
 	netif_err(priv, tx_err, dev,
-			"transmit timed out, waking up the queue\n");
-	netif_info(priv, drv, dev, ": dma_cfg:%08x, free_idx:%d, " \
-			"dma_ctx_idx=%u, dma_crx_idx=%u\n",
-			fe_reg_r32(FE_REG_PDMA_GLO_CFG), priv->tx_free_idx,
+			"transmit timed out\n");
+	netif_info(priv, drv, dev, "dma_cfg:%08x\n",
+			fe_reg_r32(FE_REG_PDMA_GLO_CFG));
+	netif_info(priv, drv, dev, "tx_ring=%d, " \
+			"base=%08x, max=%u, ctx=%u, dtx=%u, fdx=%d\n", 0,
+			fe_reg_r32(FE_REG_TX_BASE_PTR0),
+			fe_reg_r32(FE_REG_TX_MAX_CNT0),
 			fe_reg_r32(FE_REG_TX_CTX_IDX0),
-			fe_reg_r32(FE_REG_RX_CALC_IDX0));
-	netif_wake_queue(dev);
+			fe_reg_r32(FE_REG_TX_DTX_IDX0),
+			priv->tx_free_idx
+		  );
+	netif_info(priv, drv, dev, "rx_ring=%d, " \
+			"base=%08x, max=%u, calc=%u, drx=%u\n", 0,
+			fe_reg_r32(FE_REG_RX_BASE_PTR0),
+			fe_reg_r32(FE_REG_RX_MAX_CNT0),
+			fe_reg_r32(FE_REG_RX_CALC_IDX0),
+			fe_reg_r32(FE_REG_RX_DRX_IDX0)
+		  );
+
+	if (!test_and_set_bit(FE_FLAG_RESET_PENDING, priv->pending_flags))
+		schedule_work(&priv->pending_work);
 }
 
 static irqreturn_t fe_handle_irq(int irq, void *dev)
@@ -1287,6 +1303,45 @@ static const struct net_device_ops fe_netdev_ops = {
 #endif
 };
 
+static void fe_reset_pending(struct fe_priv *priv)
+{
+	struct net_device *dev = priv->netdev;
+	int err;
+
+	rtnl_lock();
+	fe_stop(dev);
+
+	err = fe_open(dev);
+	if (err)
+		goto error;
+	rtnl_unlock();
+
+	return;
+error:
+	netif_alert(priv, ifup, dev,
+			"Driver up/down cycle failed, closing device.\n");
+	dev_close(dev);
+	rtnl_unlock();
+}
+
+static const struct fe_work_t fe_work[] = {
+	{FE_FLAG_RESET_PENDING, fe_reset_pending},
+};
+
+static void fe_pending_work(struct work_struct *work)
+{
+	struct fe_priv *priv = container_of(work, struct fe_priv, pending_work);
+	int i;
+	bool pending;
+
+	for (i = 0; i < ARRAY_SIZE(fe_work); i++) {
+		pending = test_and_clear_bit(fe_work[i].bitnr,
+				priv->pending_flags);
+		if (pending)
+			fe_work[i].action(priv);
+	}
+}
+
 static int fe_probe(struct platform_device *pdev)
 {
 	struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -1323,7 +1378,6 @@ static int fe_probe(struct platform_device *pdev)
 	SET_NETDEV_DEV(netdev, &pdev->dev);
 	netdev->netdev_ops = &fe_netdev_ops;
 	netdev->base_addr = (unsigned long) fe_base;
-	netdev->watchdog_timeo = TX_TIMEOUT;
 
 	netdev->irq = platform_get_irq(pdev, 0);
 	if (netdev->irq < 0) {
@@ -1370,6 +1424,7 @@ static int fe_probe(struct platform_device *pdev)
 		err = -EINVAL;
 		goto err_free_dev;
 	}
+	INIT_WORK(&priv->pending_work, fe_pending_work);
 
 	netif_napi_add(netdev, &priv->rx_napi, fe_poll, 32);
 	fe_set_ethtool_ops(netdev);
@@ -1404,6 +1459,8 @@ static int fe_remove(struct platform_device *pdev)
 	if (priv->hw_stats)
 		kfree(priv->hw_stats);
 
+	cancel_work_sync(&priv->pending_work);
+
 	unregister_netdev(dev);
 	free_netdev(dev);
 	platform_set_drvdata(pdev, NULL);
diff --git a/target/linux/ramips/files/drivers/net/ethernet/ralink/ralink_soc_eth.h b/target/linux/ramips/files/drivers/net/ethernet/ralink/ralink_soc_eth.h
index e41d12517c..007ad722d6 100644
--- a/target/linux/ramips/files/drivers/net/ethernet/ralink/ralink_soc_eth.h
+++ b/target/linux/ramips/files/drivers/net/ethernet/ralink/ralink_soc_eth.h
@@ -33,9 +33,11 @@ enum fe_reg {
 	FE_REG_TX_BASE_PTR0,
 	FE_REG_TX_MAX_CNT0,
 	FE_REG_TX_CTX_IDX0,
+	FE_REG_TX_DTX_IDX0,
 	FE_REG_RX_BASE_PTR0,
 	FE_REG_RX_MAX_CNT0,
 	FE_REG_RX_CALC_IDX0,
+	FE_REG_RX_DRX_IDX0,
 	FE_REG_FE_INT_ENABLE,
 	FE_REG_FE_INT_STATUS,
 	FE_REG_FE_DMA_VID_BASE,
@@ -44,7 +46,12 @@ enum fe_reg {
 	FE_REG_COUNT
 };
 
-#define FE_DRV_VERSION		"0.1.0"
+enum fe_work_flag {
+        FE_FLAG_RESET_PENDING,
+        FE_FLAG_MAX
+};
+
+#define FE_DRV_VERSION		"0.1.1"
 
 /* power of 2 to let NEXT_TX_DESP_IDX work */
 #ifdef CONFIG_SOC_MT7621
@@ -451,6 +458,8 @@ struct fe_priv
 
 	struct fe_hw_stats		*hw_stats;
 	unsigned long			vlan_map;
+	struct work_struct		pending_work;
+	DECLARE_BITMAP(pending_flags, FE_FLAG_MAX);
 };
 
 extern const struct of_device_id of_fe_match[];
diff --git a/target/linux/ramips/files/drivers/net/ethernet/ralink/soc_mt7620.c b/target/linux/ramips/files/drivers/net/ethernet/ralink/soc_mt7620.c
index 50e39af248..0c1384b51c 100644
--- a/target/linux/ramips/files/drivers/net/ethernet/ralink/soc_mt7620.c
+++ b/target/linux/ramips/files/drivers/net/ethernet/ralink/soc_mt7620.c
@@ -72,9 +72,11 @@ static const u32 mt7620_reg_table[FE_REG_COUNT] = {
 	[FE_REG_TX_BASE_PTR0] = RT5350_TX_BASE_PTR0,
 	[FE_REG_TX_MAX_CNT0] = RT5350_TX_MAX_CNT0,
 	[FE_REG_TX_CTX_IDX0] = RT5350_TX_CTX_IDX0,
+	[FE_REG_TX_DTX_IDX0] = RT5350_TX_DTX_IDX0,
 	[FE_REG_RX_BASE_PTR0] = RT5350_RX_BASE_PTR0,
 	[FE_REG_RX_MAX_CNT0] = RT5350_RX_MAX_CNT0,
 	[FE_REG_RX_CALC_IDX0] = RT5350_RX_CALC_IDX0,
+	[FE_REG_RX_DRX_IDX0] = RT5350_RX_DRX_IDX0,
 	[FE_REG_FE_INT_ENABLE] = RT5350_FE_INT_ENABLE,
 	[FE_REG_FE_INT_STATUS] = RT5350_FE_INT_STATUS,
 	[FE_REG_FE_DMA_VID_BASE] = MT7620_DMA_VID,
@@ -89,9 +91,11 @@ static const u32 mt7621_reg_table[FE_REG_COUNT] = {
 	[FE_REG_TX_BASE_PTR0] = RT5350_TX_BASE_PTR0,
 	[FE_REG_TX_MAX_CNT0] = RT5350_TX_MAX_CNT0,
 	[FE_REG_TX_CTX_IDX0] = RT5350_TX_CTX_IDX0,
+	[FE_REG_TX_DTX_IDX0] = RT5350_TX_DTX_IDX0,
 	[FE_REG_RX_BASE_PTR0] = RT5350_RX_BASE_PTR0,
 	[FE_REG_RX_MAX_CNT0] = RT5350_RX_MAX_CNT0,
 	[FE_REG_RX_CALC_IDX0] = RT5350_RX_CALC_IDX0,
+	[FE_REG_RX_DRX_IDX0] = RT5350_RX_DRX_IDX0,
 	[FE_REG_FE_INT_ENABLE] = RT5350_FE_INT_ENABLE,
 	[FE_REG_FE_INT_STATUS] = RT5350_FE_INT_STATUS,
 	[FE_REG_FE_DMA_VID_BASE] = MT7621_DMA_VID,
diff --git a/target/linux/ramips/files/drivers/net/ethernet/ralink/soc_rt305x.c b/target/linux/ramips/files/drivers/net/ethernet/ralink/soc_rt305x.c
index 2650e30ca4..348005b345 100644
--- a/target/linux/ramips/files/drivers/net/ethernet/ralink/soc_rt305x.c
+++ b/target/linux/ramips/files/drivers/net/ethernet/ralink/soc_rt305x.c
@@ -33,9 +33,11 @@ static const u32 rt5350_reg_table[FE_REG_COUNT] = {
 	[FE_REG_TX_BASE_PTR0] = RT5350_TX_BASE_PTR0,
 	[FE_REG_TX_MAX_CNT0] = RT5350_TX_MAX_CNT0,
 	[FE_REG_TX_CTX_IDX0] = RT5350_TX_CTX_IDX0,
+	[FE_REG_TX_DTX_IDX0] = RT5350_TX_DTX_IDX0,
 	[FE_REG_RX_BASE_PTR0] = RT5350_RX_BASE_PTR0,
 	[FE_REG_RX_MAX_CNT0] = RT5350_RX_MAX_CNT0,
 	[FE_REG_RX_CALC_IDX0] = RT5350_RX_CALC_IDX0,
+	[FE_REG_RX_DRX_IDX0] = RT5350_RX_DRX_IDX0,
 	[FE_REG_FE_INT_ENABLE] = RT5350_FE_INT_ENABLE,
 	[FE_REG_FE_INT_STATUS] = RT5350_FE_INT_STATUS,
 	[FE_REG_FE_RST_GL] = 0,
-- 
2.30.2