aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJose Abreu <Jose.Abreu@synopsys.com>2018-03-29 10:40:18 +0100
committerVinod Koul <vkoul@kernel.org>2019-02-26 00:06:36 +0530
commit6b1e0ced1d0f42a8ca6882763cfc2244e9840e73 (patch)
treeb98067345606bae4c0a5dd304105be6dc036a571
parent893bcfee5a3be9f8954ba05fb545c60b5ccc146a (diff)
UPSTREAM: net: stmmac: Rework and fix TX Timeout code
Currently TX Timeout handler does not behaves as expected and leads to an unrecoverable state. Rework current implementation of TX Timeout handling to actually perform a complete reset of the driver state and IP. We use deferred work to init a task which will be responsible for resetting the system. Signed-off-by: Jose Abreu <joabreu@synopsys.com> Cc: David S. Miller <davem@davemloft.net> Cc: Joao Pinto <jpinto@synopsys.com> Cc: Giuseppe Cavallaro <peppe.cavallaro@st.com> Cc: Alexandre Torgue <alexandre.torgue@st.com> Cc: Andrew Lunn <andrew@lunn.ch> Signed-off-by: David S. Miller <davem@davemloft.net> (cherry picked from commit 34877a15f787b594649ed375943ecc65f4342e30 https://github.com/torvalds/linux.git) Signed-off-by: Vinod Koul <vkoul@kernel.org>
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac.h11
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_main.c67
2 files changed, 73 insertions, 5 deletions
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h
index 75161e1b7e55..1485d8fcfaa9 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h
@@ -145,6 +145,17 @@ struct stmmac_priv {
struct dentry *dbgfs_rings_status;
struct dentry *dbgfs_dma_cap;
#endif
+
+ unsigned long state;
+ struct workqueue_struct *wq;
+ struct work_struct service_task;
+};
+
+enum stmmac_state {
+ STMMAC_DOWN,
+ STMMAC_RESET_REQUESTED,
+ STMMAC_RESETING,
+ STMMAC_SERVICE_SCHED,
};
int stmmac_mdio_unregister(struct net_device *ndev);
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index 504667a0b9f3..a645a356aa7b 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -196,6 +196,20 @@ static void stmmac_start_all_queues(struct stmmac_priv *priv)
netif_tx_start_queue(netdev_get_tx_queue(priv->dev, queue));
}
+static void stmmac_service_event_schedule(struct stmmac_priv *priv)
+{
+ if (!test_bit(STMMAC_DOWN, &priv->state) &&
+ !test_and_set_bit(STMMAC_SERVICE_SCHED, &priv->state))
+ queue_work(priv->wq, &priv->service_task);
+}
+
+static void stmmac_global_err(struct stmmac_priv *priv)
+{
+ netif_carrier_off(priv->dev);
+ set_bit(STMMAC_RESET_REQUESTED, &priv->state);
+ stmmac_service_event_schedule(priv);
+}
+
/**
* stmmac_clk_csr_set - dynamically set the MDC clock
* @priv: driver private structure
@@ -3603,12 +3617,8 @@ static int stmmac_poll(struct napi_struct *napi, int budget)
static void stmmac_tx_timeout(struct net_device *dev)
{
struct stmmac_priv *priv = netdev_priv(dev);
- u32 tx_count = priv->plat->tx_queues_to_use;
- u32 chan;
- /* Clear Tx resources and restart transmitting again */
- for (chan = 0; chan < tx_count; chan++)
- stmmac_tx_err(priv, chan);
+ stmmac_global_err(priv);
}
/**
@@ -3732,6 +3742,10 @@ static irqreturn_t stmmac_interrupt(int irq, void *dev_id)
return IRQ_NONE;
}
+ /* Check if adapter is up */
+ if (test_bit(STMMAC_DOWN, &priv->state))
+ return IRQ_HANDLED;
+
/* To handle GMAC own interrupts */
if ((priv->plat->has_gmac) || (priv->plat->has_gmac4)) {
int status = priv->hw->mac->host_irq_status(priv->hw,
@@ -4053,6 +4067,37 @@ static const struct net_device_ops stmmac_netdev_ops = {
.ndo_set_mac_address = eth_mac_addr,
};
+static void stmmac_reset_subtask(struct stmmac_priv *priv)
+{
+ if (!test_and_clear_bit(STMMAC_RESET_REQUESTED, &priv->state))
+ return;
+ if (test_bit(STMMAC_DOWN, &priv->state))
+ return;
+
+ netdev_err(priv->dev, "Reset adapter.\n");
+
+ rtnl_lock();
+ netif_trans_update(priv->dev);
+ while (test_and_set_bit(STMMAC_RESETING, &priv->state))
+ usleep_range(1000, 2000);
+
+ set_bit(STMMAC_DOWN, &priv->state);
+ dev_close(priv->dev);
+ dev_open(priv->dev);
+ clear_bit(STMMAC_DOWN, &priv->state);
+ clear_bit(STMMAC_RESETING, &priv->state);
+ rtnl_unlock();
+}
+
+static void stmmac_service_task(struct work_struct *work)
+{
+ struct stmmac_priv *priv = container_of(work, struct stmmac_priv,
+ service_task);
+
+ stmmac_reset_subtask(priv);
+ clear_bit(STMMAC_SERVICE_SCHED, &priv->state);
+}
+
/**
* stmmac_hw_init - Init the MAC device
* @priv: driver private structure
@@ -4214,6 +4259,15 @@ int stmmac_dvr_probe(struct device *device,
/* Verify driver arguments */
stmmac_verify_args();
+ /* Allocate workqueue */
+ priv->wq = create_singlethread_workqueue("stmmac_wq");
+ if (!priv->wq) {
+ dev_err(priv->device, "failed to create workqueue\n");
+ goto error_wq;
+ }
+
+ INIT_WORK(&priv->service_task, stmmac_service_task);
+
/* Override with kernel parameters if supplied XXX CRS XXX
* this needs to have multiple instances
*/
@@ -4344,6 +4398,8 @@ error_mdio_register:
netif_napi_del(&rx_q->napi);
}
error_hw_init:
+ destroy_workqueue(priv->wq);
+error_wq:
free_netdev(ndev);
return ret;
@@ -4376,6 +4432,7 @@ int stmmac_dvr_remove(struct device *dev)
priv->hw->pcs != STMMAC_PCS_TBI &&
priv->hw->pcs != STMMAC_PCS_RTBI)
stmmac_mdio_unregister(ndev);
+ destroy_workqueue(priv->wq);
free_netdev(ndev);
return 0;