[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <1407905705-27984-2-git-send-email-b38611@freescale.com>
Date: Wed, 13 Aug 2014 12:55:05 +0800
From: Fugang Duan <b38611@...escale.com>
To: <shawn.guo@...aro.org>, <davem@...emloft.net>,
<richardcochran@...il.com>
CC: <netdev@...r.kernel.org>, <b38611@...escale.com>
Subject: [PATCH v2 1/1] net: fec: ptp: avoid register access when ipg clock is disabled
The current kernel hang on i.MX6SX with rootfs mount from MMC.
The root cause is ptp rise up period timer to access enet register
even if ipg clock is disabled.
FEC ptp driver start one period timer to read 1588 counter register in the
ptp init function that is called after FEC driver is probed.
To save power, after FEC probe finish, FEC driver disable all clocks including
ipg clock that is needed for register access.
i.MX5x, i.MX6q/dl/sl FEC register access don't cause system hang when ipg clock
is disabled, just return zero value. But for i.MX6sx SOC, it cause system hang.
To avoid the issue, we need to check ptp stack running status. If ptp4l run up,
and then schdule one delayed work to read ptp counter register. If ptp4l stack
is down, cancel the period delayed work. For ethx interface open/close, fec
suspend/resume situations, schdule/cancel the delayed work.
Signed-off-by: Fugang Duan <B38611@...escale.com>
---
drivers/net/ethernet/freescale/fec.h | 2 +-
drivers/net/ethernet/freescale/fec_main.c | 12 +++++++++++-
drivers/net/ethernet/freescale/fec_ptp.c | 15 +++++++--------
3 files changed, 19 insertions(+), 10 deletions(-)
diff --git a/drivers/net/ethernet/freescale/fec.h b/drivers/net/ethernet/freescale/fec.h
index bd53caf..03fad35 100644
--- a/drivers/net/ethernet/freescale/fec.h
+++ b/drivers/net/ethernet/freescale/fec.h
@@ -334,7 +334,7 @@ struct fec_enet_private {
u32 cycle_speed;
int hwts_rx_en;
int hwts_tx_en;
- struct timer_list time_keep;
+ struct delayed_work time_keep;
struct regulator *reg_phy;
};
diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c
index 66fe1f6..8befa2c 100644
--- a/drivers/net/ethernet/freescale/fec_main.c
+++ b/drivers/net/ethernet/freescale/fec_main.c
@@ -2198,6 +2198,8 @@ fec_enet_open(struct net_device *ndev)
napi_enable(&fep->napi);
phy_start(fep->phy_dev);
netif_start_queue(ndev);
+ if (fep->hwts_tx_en)
+ schedule_delayed_work(&fep->time_keep, HZ);
return 0;
}
@@ -2206,6 +2208,9 @@ fec_enet_close(struct net_device *ndev)
{
struct fec_enet_private *fep = netdev_priv(ndev);
+ if (fep->hwts_tx_en)
+ cancel_delayed_work_sync(&fep->time_keep);
+
phy_stop(fep->phy_dev);
if (netif_device_present(ndev)) {
@@ -2682,10 +2687,11 @@ fec_drv_remove(struct platform_device *pdev)
struct net_device *ndev = platform_get_drvdata(pdev);
struct fec_enet_private *fep = netdev_priv(ndev);
+ if (fep->hwts_tx_en)
+ cancel_delayed_work_sync(&fep->time_keep);
cancel_work_sync(&fep->tx_timeout_work);
unregister_netdev(ndev);
fec_enet_mii_remove(fep);
- del_timer_sync(&fep->time_keep);
if (fep->reg_phy)
regulator_disable(fep->reg_phy);
if (fep->ptp_clock)
@@ -2703,6 +2709,8 @@ static int __maybe_unused fec_suspend(struct device *dev)
rtnl_lock();
if (netif_running(ndev)) {
+ if (fep->hwts_tx_en)
+ cancel_delayed_work_sync(&fep->time_keep);
phy_stop(fep->phy_dev);
napi_disable(&fep->napi);
netif_tx_lock_bh(ndev);
@@ -2746,6 +2754,8 @@ static int __maybe_unused fec_resume(struct device *dev)
netif_tx_unlock_bh(ndev);
napi_enable(&fep->napi);
phy_start(fep->phy_dev);
+ if (fep->hwts_tx_en)
+ schedule_delayed_work(&fep->time_keep, HZ);
}
rtnl_unlock();
diff --git a/drivers/net/ethernet/freescale/fec_ptp.c b/drivers/net/ethernet/freescale/fec_ptp.c
index 82386b2..281f4f8 100644
--- a/drivers/net/ethernet/freescale/fec_ptp.c
+++ b/drivers/net/ethernet/freescale/fec_ptp.c
@@ -289,9 +289,11 @@ int fec_ptp_set(struct net_device *ndev, struct ifreq *ifr)
switch (config.tx_type) {
case HWTSTAMP_TX_OFF:
fep->hwts_tx_en = 0;
+ cancel_delayed_work_sync(&fep->time_keep);
break;
case HWTSTAMP_TX_ON:
fep->hwts_tx_en = 1;
+ schedule_delayed_work(&fep->time_keep, HZ);
break;
default:
return -ERANGE;
@@ -338,9 +340,10 @@ int fec_ptp_get(struct net_device *ndev, struct ifreq *ifr)
* fec_time_keep - call timecounter_read every second to avoid timer overrun
* because ENET just support 32bit counter, will timeout in 4s
*/
-static void fec_time_keep(unsigned long _data)
+static void fec_time_keep(struct work_struct *work)
{
- struct fec_enet_private *fep = (struct fec_enet_private *)_data;
+ struct delayed_work *dwork = to_delayed_work(work);
+ struct fec_enet_private *fep = container_of(dwork, struct fec_enet_private, time_keep);
u64 ns;
unsigned long flags;
@@ -348,7 +351,7 @@ static void fec_time_keep(unsigned long _data)
ns = timecounter_read(&fep->tc);
spin_unlock_irqrestore(&fep->tmreg_lock, flags);
- mod_timer(&fep->time_keep, jiffies + HZ);
+ schedule_delayed_work(&fep->time_keep, HZ);
}
/**
@@ -386,11 +389,7 @@ void fec_ptp_init(struct platform_device *pdev)
fec_ptp_start_cyclecounter(ndev);
- init_timer(&fep->time_keep);
- fep->time_keep.data = (unsigned long)fep;
- fep->time_keep.function = fec_time_keep;
- fep->time_keep.expires = jiffies + HZ;
- add_timer(&fep->time_keep);
+ INIT_DELAYED_WORK(&fep->time_keep, fec_time_keep);
fep->ptp_clock = ptp_clock_register(&fep->ptp_caps, &pdev->dev);
if (IS_ERR(fep->ptp_clock)) {
--
1.7.8
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Powered by blists - more mailing lists