diff options
Diffstat (limited to 'drivers/net/ethernet/freescale/fec_main.c')
| -rw-r--r-- | drivers/net/ethernet/freescale/fec_main.c | 149 | 
1 files changed, 114 insertions, 35 deletions
diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index 1f89c59b4353..dd4ca39d5d8f 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -24,6 +24,7 @@  #include <linux/module.h>  #include <linux/kernel.h>  #include <linux/string.h> +#include <linux/pm_runtime.h>  #include <linux/ptrace.h>  #include <linux/errno.h>  #include <linux/ioport.h> @@ -77,6 +78,7 @@ static void fec_enet_itr_coal_init(struct net_device *ndev);  #define FEC_ENET_RAEM_V	0x8  #define FEC_ENET_RAFL_V	0x8  #define FEC_ENET_OPD_V	0xFFF0 +#define FEC_MDIO_PM_TIMEOUT  100 /* ms */  static struct platform_device_id fec_devtype[] = {  	{ @@ -362,7 +364,7 @@ fec_enet_clear_csum(struct sk_buff *skb, struct net_device *ndev)  	return 0;  } -static int +static struct bufdesc *  fec_enet_txq_submit_frag_skb(struct fec_enet_priv_tx_q *txq,  			     struct sk_buff *skb,  			     struct net_device *ndev) @@ -437,10 +439,7 @@ fec_enet_txq_submit_frag_skb(struct fec_enet_priv_tx_q *txq,  		bdp->cbd_sc = status;  	} -	txq->cur_tx = bdp; - -	return 0; - +	return bdp;  dma_mapping_error:  	bdp = txq->cur_tx;  	for (i = 0; i < frag; i++) { @@ -448,7 +447,7 @@ dma_mapping_error:  		dma_unmap_single(&fep->pdev->dev, bdp->cbd_bufaddr,  				bdp->cbd_datlen, DMA_TO_DEVICE);  	} -	return NETDEV_TX_OK; +	return ERR_PTR(-ENOMEM);  }  static int fec_enet_txq_submit_skb(struct fec_enet_priv_tx_q *txq, @@ -465,7 +464,6 @@ static int fec_enet_txq_submit_skb(struct fec_enet_priv_tx_q *txq,  	unsigned int estatus = 0;  	unsigned int index;  	int entries_free; -	int ret;  	entries_free = fec_enet_get_free_txdesc_num(fep, txq);  	if (entries_free < MAX_SKB_FRAGS + 1) { @@ -483,6 +481,7 @@ static int fec_enet_txq_submit_skb(struct fec_enet_priv_tx_q *txq,  	/* Fill in a Tx ring entry */  	bdp = txq->cur_tx; +	last_bdp = bdp;  	status = bdp->cbd_sc;  	status &= ~BD_ENET_TX_STATS; @@ -511,9 +510,9 @@ static int fec_enet_txq_submit_skb(struct fec_enet_priv_tx_q *txq,  	}  	if (nr_frags) { -		ret = fec_enet_txq_submit_frag_skb(txq, skb, ndev); -		if (ret) -			return ret; +		last_bdp = fec_enet_txq_submit_frag_skb(txq, skb, ndev); +		if (IS_ERR(last_bdp)) +			return NETDEV_TX_OK;  	} else {  		status |= (BD_ENET_TX_INTR | BD_ENET_TX_LAST);  		if (fep->bufdesc_ex) { @@ -542,7 +541,6 @@ static int fec_enet_txq_submit_skb(struct fec_enet_priv_tx_q *txq,  		ebdp->cbd_esc = estatus;  	} -	last_bdp = txq->cur_tx;  	index = fec_enet_get_bd_index(txq->tx_bd_base, last_bdp, fep);  	/* Save skb pointer */  	txq->tx_skbuff[index] = skb; @@ -561,6 +559,10 @@ static int fec_enet_txq_submit_skb(struct fec_enet_priv_tx_q *txq,  	skb_tx_timestamp(skb); +	/* Make sure the update to bdp and tx_skbuff are performed before +	 * cur_tx. +	 */ +	wmb();  	txq->cur_tx = bdp;  	/* Trigger transmission start */ @@ -1216,10 +1218,11 @@ fec_enet_tx_queue(struct net_device *ndev, u16 queue_id)  	/* get next bdp of dirty_tx */  	bdp = fec_enet_get_nextdesc(bdp, fep, queue_id); -	while (((status = bdp->cbd_sc) & BD_ENET_TX_READY) == 0) { - -		/* current queue is empty */ -		if (bdp == txq->cur_tx) +	while (bdp != READ_ONCE(txq->cur_tx)) { +		/* Order the load of cur_tx and cbd_sc */ +		rmb(); +		status = READ_ONCE(bdp->cbd_sc); +		if (status & BD_ENET_TX_READY)  			break;  		index = fec_enet_get_bd_index(txq->tx_bd_base, bdp, fep); @@ -1273,6 +1276,10 @@ fec_enet_tx_queue(struct net_device *ndev, u16 queue_id)  		/* Free the sk buffer associated with this last transmit */  		dev_kfree_skb_any(skb); +		/* Make sure the update to bdp and tx_skbuff are performed +		 * before dirty_tx +		 */ +		wmb();  		txq->dirty_tx = bdp;  		/* Update pointer to next buffer descriptor to be transmitted */ @@ -1400,6 +1407,7 @@ fec_enet_rx_queue(struct net_device *ndev, int budget, u16 queue_id)  		if ((status & BD_ENET_RX_LAST) == 0)  			netdev_err(ndev, "rcv is not +last\n"); +		writel(FEC_ENET_RXF, fep->hwp + FEC_IEVENT);  		/* Check for errors. */  		if (status & (BD_ENET_RX_LG | BD_ENET_RX_SH | BD_ENET_RX_NO | @@ -1767,10 +1775,16 @@ static void fec_enet_adjust_link(struct net_device *ndev)  static int fec_enet_mdio_read(struct mii_bus *bus, int mii_id, int regnum)  {  	struct fec_enet_private *fep = bus->priv; +	struct device *dev = &fep->pdev->dev;  	unsigned long time_left; +	int ret = 0; + +	ret = pm_runtime_get_sync(dev); +	if (ret < 0) +		return ret;  	fep->mii_timeout = 0; -	init_completion(&fep->mdio_done); +	reinit_completion(&fep->mdio_done);  	/* start a read op */  	writel(FEC_MMFR_ST | FEC_MMFR_OP_READ | @@ -1783,21 +1797,35 @@ static int fec_enet_mdio_read(struct mii_bus *bus, int mii_id, int regnum)  	if (time_left == 0) {  		fep->mii_timeout = 1;  		netdev_err(fep->netdev, "MDIO read timeout\n"); -		return -ETIMEDOUT; +		ret = -ETIMEDOUT; +		goto out;  	} -	/* return value */ -	return FEC_MMFR_DATA(readl(fep->hwp + FEC_MII_DATA)); +	ret = FEC_MMFR_DATA(readl(fep->hwp + FEC_MII_DATA)); + +out: +	pm_runtime_mark_last_busy(dev); +	pm_runtime_put_autosuspend(dev); + +	return ret;  }  static int fec_enet_mdio_write(struct mii_bus *bus, int mii_id, int regnum,  			   u16 value)  {  	struct fec_enet_private *fep = bus->priv; +	struct device *dev = &fep->pdev->dev;  	unsigned long time_left; +	int ret; + +	ret = pm_runtime_get_sync(dev); +	if (ret < 0) +		return ret; +	else +		ret = 0;  	fep->mii_timeout = 0; -	init_completion(&fep->mdio_done); +	reinit_completion(&fep->mdio_done);  	/* start a write op */  	writel(FEC_MMFR_ST | FEC_MMFR_OP_WRITE | @@ -1811,10 +1839,13 @@ static int fec_enet_mdio_write(struct mii_bus *bus, int mii_id, int regnum,  	if (time_left == 0) {  		fep->mii_timeout = 1;  		netdev_err(fep->netdev, "MDIO write timeout\n"); -		return -ETIMEDOUT; +		ret  = -ETIMEDOUT;  	} -	return 0; +	pm_runtime_mark_last_busy(dev); +	pm_runtime_put_autosuspend(dev); + +	return ret;  }  static int fec_enet_clk_enable(struct net_device *ndev, bool enable) @@ -1826,9 +1857,6 @@ static int fec_enet_clk_enable(struct net_device *ndev, bool enable)  		ret = clk_prepare_enable(fep->clk_ahb);  		if (ret)  			return ret; -		ret = clk_prepare_enable(fep->clk_ipg); -		if (ret) -			goto failed_clk_ipg;  		if (fep->clk_enet_out) {  			ret = clk_prepare_enable(fep->clk_enet_out);  			if (ret) @@ -1852,7 +1880,6 @@ static int fec_enet_clk_enable(struct net_device *ndev, bool enable)  		}  	} else {  		clk_disable_unprepare(fep->clk_ahb); -		clk_disable_unprepare(fep->clk_ipg);  		if (fep->clk_enet_out)  			clk_disable_unprepare(fep->clk_enet_out);  		if (fep->clk_ptp) { @@ -1874,8 +1901,6 @@ failed_clk_ptp:  	if (fep->clk_enet_out)  		clk_disable_unprepare(fep->clk_enet_out);  failed_clk_enet_out: -		clk_disable_unprepare(fep->clk_ipg); -failed_clk_ipg:  		clk_disable_unprepare(fep->clk_ahb);  	return ret; @@ -2847,10 +2872,14 @@ fec_enet_open(struct net_device *ndev)  	struct fec_enet_private *fep = netdev_priv(ndev);  	int ret; +	ret = pm_runtime_get_sync(&fep->pdev->dev); +	if (ret < 0) +		return ret; +  	pinctrl_pm_select_default_state(&fep->pdev->dev);  	ret = fec_enet_clk_enable(ndev, true);  	if (ret) -		return ret; +		goto clk_enable;  	/* I should reset the ring buffers here, but I don't yet know  	 * a simple way to do that. @@ -2881,6 +2910,9 @@ err_enet_mii_probe:  	fec_enet_free_buffers(ndev);  err_enet_alloc:  	fec_enet_clk_enable(ndev, false); +clk_enable: +	pm_runtime_mark_last_busy(&fep->pdev->dev); +	pm_runtime_put_autosuspend(&fep->pdev->dev);  	pinctrl_pm_select_sleep_state(&fep->pdev->dev);  	return ret;  } @@ -2903,6 +2935,9 @@ fec_enet_close(struct net_device *ndev)  	fec_enet_clk_enable(ndev, false);  	pinctrl_pm_select_sleep_state(&fep->pdev->dev); +	pm_runtime_mark_last_busy(&fep->pdev->dev); +	pm_runtime_put_autosuspend(&fep->pdev->dev); +  	fec_enet_free_buffers(ndev);  	return 0; @@ -2996,6 +3031,14 @@ fec_set_mac_address(struct net_device *ndev, void *p)  		memcpy(ndev->dev_addr, addr->sa_data, ndev->addr_len);  	} +	/* Add netif status check here to avoid system hang in below case: +	 * ifconfig ethx down; ifconfig ethx hw ether xx:xx:xx:xx:xx:xx; +	 * After ethx down, fec all clocks are gated off and then register +	 * access causes system hang. +	 */ +	if (!netif_running(ndev)) +		return 0; +  	writel(ndev->dev_addr[3] | (ndev->dev_addr[2] << 8) |  		(ndev->dev_addr[1] << 16) | (ndev->dev_addr[0] << 24),  		fep->hwp + FEC_ADDR_LOW); @@ -3115,8 +3158,8 @@ static int fec_enet_init(struct net_device *ndev)  			fep->bufdesc_size;  	/* Allocate memory for buffer descriptors. */ -	cbd_base = dma_alloc_coherent(NULL, bd_size, &bd_dma, -				      GFP_KERNEL); +	cbd_base = dmam_alloc_coherent(&fep->pdev->dev, bd_size, &bd_dma, +				       GFP_KERNEL);  	if (!cbd_base) {  		return -ENOMEM;  	} @@ -3388,6 +3431,10 @@ fec_probe(struct platform_device *pdev)  	if (ret)  		goto failed_clk; +	ret = clk_prepare_enable(fep->clk_ipg); +	if (ret) +		goto failed_clk_ipg; +  	fep->reg_phy = devm_regulator_get(&pdev->dev, "phy");  	if (!IS_ERR(fep->reg_phy)) {  		ret = regulator_enable(fep->reg_phy); @@ -3400,6 +3447,12 @@ fec_probe(struct platform_device *pdev)  		fep->reg_phy = NULL;  	} +	pm_runtime_set_autosuspend_delay(&pdev->dev, FEC_MDIO_PM_TIMEOUT); +	pm_runtime_use_autosuspend(&pdev->dev); +	pm_runtime_get_noresume(&pdev->dev); +	pm_runtime_set_active(&pdev->dev); +	pm_runtime_enable(&pdev->dev); +  	fec_reset_phy(pdev);  	if (fep->bufdesc_ex) @@ -3447,6 +3500,10 @@ fec_probe(struct platform_device *pdev)  	fep->rx_copybreak = COPYBREAK_DEFAULT;  	INIT_WORK(&fep->tx_timeout_work, fec_enet_timeout_work); + +	pm_runtime_mark_last_busy(&pdev->dev); +	pm_runtime_put_autosuspend(&pdev->dev); +  	return 0;  failed_register: @@ -3454,9 +3511,12 @@ failed_register:  failed_mii_init:  failed_irq:  failed_init: +	fec_ptp_stop(pdev);  	if (fep->reg_phy)  		regulator_disable(fep->reg_phy);  failed_regulator: +	clk_disable_unprepare(fep->clk_ipg); +failed_clk_ipg:  	fec_enet_clk_enable(ndev, false);  failed_clk:  failed_phy: @@ -3473,14 +3533,12 @@ fec_drv_remove(struct platform_device *pdev)  	struct net_device *ndev = platform_get_drvdata(pdev);  	struct fec_enet_private *fep = netdev_priv(ndev); -	cancel_delayed_work_sync(&fep->time_keep);  	cancel_work_sync(&fep->tx_timeout_work); +	fec_ptp_stop(pdev);  	unregister_netdev(ndev);  	fec_enet_mii_remove(fep);  	if (fep->reg_phy)  		regulator_disable(fep->reg_phy); -	if (fep->ptp_clock) -		ptp_clock_unregister(fep->ptp_clock);  	of_node_put(fep->phy_node);  	free_netdev(ndev); @@ -3568,7 +3626,28 @@ failed_clk:  	return ret;  } -static SIMPLE_DEV_PM_OPS(fec_pm_ops, fec_suspend, fec_resume); +static int __maybe_unused fec_runtime_suspend(struct device *dev) +{ +	struct net_device *ndev = dev_get_drvdata(dev); +	struct fec_enet_private *fep = netdev_priv(ndev); + +	clk_disable_unprepare(fep->clk_ipg); + +	return 0; +} + +static int __maybe_unused fec_runtime_resume(struct device *dev) +{ +	struct net_device *ndev = dev_get_drvdata(dev); +	struct fec_enet_private *fep = netdev_priv(ndev); + +	return clk_prepare_enable(fep->clk_ipg); +} + +static const struct dev_pm_ops fec_pm_ops = { +	SET_SYSTEM_SLEEP_PM_OPS(fec_suspend, fec_resume) +	SET_RUNTIME_PM_OPS(fec_runtime_suspend, fec_runtime_resume, NULL) +};  static struct platform_driver fec_driver = {  	.driver	= {  |