ftgmac100: Add a reset task and use it for link changes
Link speed changes require a full HW reset. This isn't done properly at the moment. It will involve delays and thus isn't suitable to do from the link poll callback. So let's create a reset_task that we can queue up when the link changes. It will be useful for various cases of error handling as well. Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org> Reviewed-by: Andrew Lunn <andrew@lunn.ch> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
da40d9d4b5
commit
855944ce1c
@ -76,6 +76,7 @@ struct ftgmac100 {
|
|||||||
struct device *dev;
|
struct device *dev;
|
||||||
struct ncsi_dev *ndev;
|
struct ncsi_dev *ndev;
|
||||||
struct napi_struct napi;
|
struct napi_struct napi;
|
||||||
|
struct work_struct reset_task;
|
||||||
struct mii_bus *mii_bus;
|
struct mii_bus *mii_bus;
|
||||||
|
|
||||||
/* Link management */
|
/* Link management */
|
||||||
@ -874,7 +875,6 @@ static void ftgmac100_adjust_link(struct net_device *netdev)
|
|||||||
struct ftgmac100 *priv = netdev_priv(netdev);
|
struct ftgmac100 *priv = netdev_priv(netdev);
|
||||||
struct phy_device *phydev = netdev->phydev;
|
struct phy_device *phydev = netdev->phydev;
|
||||||
int new_speed;
|
int new_speed;
|
||||||
int ier;
|
|
||||||
|
|
||||||
/* We store "no link" as speed 0 */
|
/* We store "no link" as speed 0 */
|
||||||
if (!phydev->link)
|
if (!phydev->link)
|
||||||
@ -899,20 +899,11 @@ static void ftgmac100_adjust_link(struct net_device *netdev)
|
|||||||
if (!new_speed)
|
if (!new_speed)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ier = ioread32(priv->base + FTGMAC100_OFFSET_IER);
|
/* Disable all interrupts */
|
||||||
|
|
||||||
/* disable all interrupts */
|
|
||||||
iowrite32(0, priv->base + FTGMAC100_OFFSET_IER);
|
iowrite32(0, priv->base + FTGMAC100_OFFSET_IER);
|
||||||
|
|
||||||
netif_stop_queue(netdev);
|
/* Reset the adapter asynchronously */
|
||||||
ftgmac100_stop_hw(priv);
|
schedule_work(&priv->reset_task);
|
||||||
|
|
||||||
netif_start_queue(netdev);
|
|
||||||
ftgmac100_init_hw(priv);
|
|
||||||
ftgmac100_start_hw(priv);
|
|
||||||
|
|
||||||
/* re-enable interrupts */
|
|
||||||
iowrite32(ier, priv->base + FTGMAC100_OFFSET_IER);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ftgmac100_mii_probe(struct ftgmac100 *priv)
|
static int ftgmac100_mii_probe(struct ftgmac100 *priv)
|
||||||
@ -1137,6 +1128,61 @@ static int ftgmac100_init_all(struct ftgmac100 *priv, bool ignore_alloc_err)
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void ftgmac100_reset_task(struct work_struct *work)
|
||||||
|
{
|
||||||
|
struct ftgmac100 *priv = container_of(work, struct ftgmac100,
|
||||||
|
reset_task);
|
||||||
|
struct net_device *netdev = priv->netdev;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
netdev_dbg(netdev, "Resetting NIC...\n");
|
||||||
|
|
||||||
|
/* Lock the world */
|
||||||
|
rtnl_lock();
|
||||||
|
if (netdev->phydev)
|
||||||
|
mutex_lock(&netdev->phydev->lock);
|
||||||
|
if (priv->mii_bus)
|
||||||
|
mutex_lock(&priv->mii_bus->mdio_lock);
|
||||||
|
|
||||||
|
|
||||||
|
/* Check if the interface is still up */
|
||||||
|
if (!netif_running(netdev))
|
||||||
|
goto bail;
|
||||||
|
|
||||||
|
/* Stop the network stack */
|
||||||
|
netif_trans_update(netdev);
|
||||||
|
napi_disable(&priv->napi);
|
||||||
|
netif_tx_disable(netdev);
|
||||||
|
|
||||||
|
/* Stop and reset the MAC */
|
||||||
|
ftgmac100_stop_hw(priv);
|
||||||
|
err = ftgmac100_reset_hw(priv);
|
||||||
|
if (err) {
|
||||||
|
/* Not much we can do ... it might come back... */
|
||||||
|
netdev_err(netdev, "attempting to continue...\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Free all rx and tx buffers */
|
||||||
|
ftgmac100_free_buffers(priv);
|
||||||
|
|
||||||
|
/* The ring pointers have been reset in HW, reflect this here */
|
||||||
|
priv->rx_pointer = 0;
|
||||||
|
priv->tx_clean_pointer = 0;
|
||||||
|
priv->tx_pointer = 0;
|
||||||
|
priv->tx_pending = 0;
|
||||||
|
|
||||||
|
/* Setup everything again and restart chip */
|
||||||
|
ftgmac100_init_all(priv, true);
|
||||||
|
|
||||||
|
netdev_dbg(netdev, "Reset done !\n");
|
||||||
|
bail:
|
||||||
|
if (priv->mii_bus)
|
||||||
|
mutex_unlock(&priv->mii_bus->mdio_lock);
|
||||||
|
if (netdev->phydev)
|
||||||
|
mutex_unlock(&netdev->phydev->lock);
|
||||||
|
rtnl_unlock();
|
||||||
|
}
|
||||||
|
|
||||||
static int ftgmac100_open(struct net_device *netdev)
|
static int ftgmac100_open(struct net_device *netdev)
|
||||||
{
|
{
|
||||||
struct ftgmac100 *priv = netdev_priv(netdev);
|
struct ftgmac100 *priv = netdev_priv(netdev);
|
||||||
@ -1222,6 +1268,14 @@ static int ftgmac100_stop(struct net_device *netdev)
|
|||||||
{
|
{
|
||||||
struct ftgmac100 *priv = netdev_priv(netdev);
|
struct ftgmac100 *priv = netdev_priv(netdev);
|
||||||
|
|
||||||
|
/* Note about the reset task: We are called with the rtnl lock
|
||||||
|
* held, so we are synchronized against the core of the reset
|
||||||
|
* task. We must not try to synchronously cancel it otherwise
|
||||||
|
* we can deadlock. But since it will test for netif_running()
|
||||||
|
* which has already been cleared by the net core, we don't
|
||||||
|
* anything special to do.
|
||||||
|
*/
|
||||||
|
|
||||||
/* disable all interrupts */
|
/* disable all interrupts */
|
||||||
iowrite32(0, priv->base + FTGMAC100_OFFSET_IER);
|
iowrite32(0, priv->base + FTGMAC100_OFFSET_IER);
|
||||||
|
|
||||||
@ -1397,6 +1451,7 @@ static int ftgmac100_probe(struct platform_device *pdev)
|
|||||||
priv = netdev_priv(netdev);
|
priv = netdev_priv(netdev);
|
||||||
priv->netdev = netdev;
|
priv->netdev = netdev;
|
||||||
priv->dev = &pdev->dev;
|
priv->dev = &pdev->dev;
|
||||||
|
INIT_WORK(&priv->reset_task, ftgmac100_reset_task);
|
||||||
|
|
||||||
spin_lock_init(&priv->tx_lock);
|
spin_lock_init(&priv->tx_lock);
|
||||||
|
|
||||||
@ -1500,6 +1555,12 @@ static int ftgmac100_remove(struct platform_device *pdev)
|
|||||||
priv = netdev_priv(netdev);
|
priv = netdev_priv(netdev);
|
||||||
|
|
||||||
unregister_netdev(netdev);
|
unregister_netdev(netdev);
|
||||||
|
|
||||||
|
/* There's a small chance the reset task will have been re-queued,
|
||||||
|
* during stop, make sure it's gone before we free the structure.
|
||||||
|
*/
|
||||||
|
cancel_work_sync(&priv->reset_task);
|
||||||
|
|
||||||
ftgmac100_destroy_mdio(netdev);
|
ftgmac100_destroy_mdio(netdev);
|
||||||
|
|
||||||
iounmap(priv->base);
|
iounmap(priv->base);
|
||||||
|
Loading…
Reference in New Issue
Block a user