Commit 91c0d987 authored by Nimrod Andy's avatar Nimrod Andy Committed by David S. Miller

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 that ptp uses a periodic 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 clock status before ptp timer count access.
Signed-off-by: default avatarFugang Duan <B38611@freescale.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 08f1a1b9
...@@ -275,6 +275,9 @@ struct fec_enet_private { ...@@ -275,6 +275,9 @@ struct fec_enet_private {
struct clk *clk_enet_out; struct clk *clk_enet_out;
struct clk *clk_ptp; struct clk *clk_ptp;
bool ptp_clk_on;
struct mutex ptp_clk_mutex;
/* The saved address of a sent-in-place packet/buffer, for skfree(). */ /* The saved address of a sent-in-place packet/buffer, for skfree(). */
unsigned char *tx_bounce[TX_RING_SIZE]; unsigned char *tx_bounce[TX_RING_SIZE];
struct sk_buff *tx_skbuff[TX_RING_SIZE]; struct sk_buff *tx_skbuff[TX_RING_SIZE];
...@@ -335,7 +338,7 @@ struct fec_enet_private { ...@@ -335,7 +338,7 @@ struct fec_enet_private {
u32 cycle_speed; u32 cycle_speed;
int hwts_rx_en; int hwts_rx_en;
int hwts_tx_en; int hwts_tx_en;
struct timer_list time_keep; struct delayed_work time_keep;
struct regulator *reg_phy; struct regulator *reg_phy;
}; };
......
...@@ -1611,17 +1611,27 @@ static int fec_enet_clk_enable(struct net_device *ndev, bool enable) ...@@ -1611,17 +1611,27 @@ static int fec_enet_clk_enable(struct net_device *ndev, bool enable)
goto failed_clk_enet_out; goto failed_clk_enet_out;
} }
if (fep->clk_ptp) { if (fep->clk_ptp) {
mutex_lock(&fep->ptp_clk_mutex);
ret = clk_prepare_enable(fep->clk_ptp); ret = clk_prepare_enable(fep->clk_ptp);
if (ret) if (ret) {
mutex_unlock(&fep->ptp_clk_mutex);
goto failed_clk_ptp; goto failed_clk_ptp;
} else {
fep->ptp_clk_on = true;
}
mutex_unlock(&fep->ptp_clk_mutex);
} }
} else { } else {
clk_disable_unprepare(fep->clk_ahb); clk_disable_unprepare(fep->clk_ahb);
clk_disable_unprepare(fep->clk_ipg); clk_disable_unprepare(fep->clk_ipg);
if (fep->clk_enet_out) if (fep->clk_enet_out)
clk_disable_unprepare(fep->clk_enet_out); clk_disable_unprepare(fep->clk_enet_out);
if (fep->clk_ptp) if (fep->clk_ptp) {
mutex_lock(&fep->ptp_clk_mutex);
clk_disable_unprepare(fep->clk_ptp); clk_disable_unprepare(fep->clk_ptp);
fep->ptp_clk_on = false;
mutex_unlock(&fep->ptp_clk_mutex);
}
} }
return 0; return 0;
...@@ -2625,6 +2635,8 @@ fec_probe(struct platform_device *pdev) ...@@ -2625,6 +2635,8 @@ fec_probe(struct platform_device *pdev)
if (IS_ERR(fep->clk_enet_out)) if (IS_ERR(fep->clk_enet_out))
fep->clk_enet_out = NULL; fep->clk_enet_out = NULL;
fep->ptp_clk_on = false;
mutex_init(&fep->ptp_clk_mutex);
fep->clk_ptp = devm_clk_get(&pdev->dev, "ptp"); fep->clk_ptp = devm_clk_get(&pdev->dev, "ptp");
fep->bufdesc_ex = fep->bufdesc_ex =
pdev->id_entry->driver_data & FEC_QUIRK_HAS_BUFDESC_EX; pdev->id_entry->driver_data & FEC_QUIRK_HAS_BUFDESC_EX;
...@@ -2715,10 +2727,10 @@ fec_drv_remove(struct platform_device *pdev) ...@@ -2715,10 +2727,10 @@ fec_drv_remove(struct platform_device *pdev)
struct net_device *ndev = platform_get_drvdata(pdev); struct net_device *ndev = platform_get_drvdata(pdev);
struct fec_enet_private *fep = netdev_priv(ndev); struct fec_enet_private *fep = netdev_priv(ndev);
cancel_delayed_work_sync(&fep->time_keep);
cancel_work_sync(&fep->tx_timeout_work); cancel_work_sync(&fep->tx_timeout_work);
unregister_netdev(ndev); unregister_netdev(ndev);
fec_enet_mii_remove(fep); fec_enet_mii_remove(fep);
del_timer_sync(&fep->time_keep);
if (fep->reg_phy) if (fep->reg_phy)
regulator_disable(fep->reg_phy); regulator_disable(fep->reg_phy);
if (fep->ptp_clock) if (fep->ptp_clock)
......
...@@ -245,12 +245,20 @@ static int fec_ptp_settime(struct ptp_clock_info *ptp, ...@@ -245,12 +245,20 @@ static int fec_ptp_settime(struct ptp_clock_info *ptp,
u64 ns; u64 ns;
unsigned long flags; unsigned long flags;
mutex_lock(&fep->ptp_clk_mutex);
/* Check the ptp clock */
if (!fep->ptp_clk_on) {
mutex_unlock(&fep->ptp_clk_mutex);
return -EINVAL;
}
ns = ts->tv_sec * 1000000000ULL; ns = ts->tv_sec * 1000000000ULL;
ns += ts->tv_nsec; ns += ts->tv_nsec;
spin_lock_irqsave(&fep->tmreg_lock, flags); spin_lock_irqsave(&fep->tmreg_lock, flags);
timecounter_init(&fep->tc, &fep->cc, ns); timecounter_init(&fep->tc, &fep->cc, ns);
spin_unlock_irqrestore(&fep->tmreg_lock, flags); spin_unlock_irqrestore(&fep->tmreg_lock, flags);
mutex_unlock(&fep->ptp_clk_mutex);
return 0; return 0;
} }
...@@ -338,17 +346,22 @@ int fec_ptp_get(struct net_device *ndev, struct ifreq *ifr) ...@@ -338,17 +346,22 @@ int fec_ptp_get(struct net_device *ndev, struct ifreq *ifr)
* fec_time_keep - call timecounter_read every second to avoid timer overrun * fec_time_keep - call timecounter_read every second to avoid timer overrun
* because ENET just support 32bit counter, will timeout in 4s * 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; u64 ns;
unsigned long flags; unsigned long flags;
spin_lock_irqsave(&fep->tmreg_lock, flags); mutex_lock(&fep->ptp_clk_mutex);
ns = timecounter_read(&fep->tc); if (fep->ptp_clk_on) {
spin_unlock_irqrestore(&fep->tmreg_lock, flags); spin_lock_irqsave(&fep->tmreg_lock, flags);
ns = timecounter_read(&fep->tc);
spin_unlock_irqrestore(&fep->tmreg_lock, flags);
}
mutex_unlock(&fep->ptp_clk_mutex);
mod_timer(&fep->time_keep, jiffies + HZ); schedule_delayed_work(&fep->time_keep, HZ);
} }
/** /**
...@@ -386,15 +399,13 @@ void fec_ptp_init(struct platform_device *pdev) ...@@ -386,15 +399,13 @@ void fec_ptp_init(struct platform_device *pdev)
fec_ptp_start_cyclecounter(ndev); fec_ptp_start_cyclecounter(ndev);
init_timer(&fep->time_keep); INIT_DELAYED_WORK(&fep->time_keep, fec_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);
fep->ptp_clock = ptp_clock_register(&fep->ptp_caps, &pdev->dev); fep->ptp_clock = ptp_clock_register(&fep->ptp_caps, &pdev->dev);
if (IS_ERR(fep->ptp_clock)) { if (IS_ERR(fep->ptp_clock)) {
fep->ptp_clock = NULL; fep->ptp_clock = NULL;
pr_err("ptp_clock_register failed\n"); pr_err("ptp_clock_register failed\n");
} }
schedule_delayed_work(&fep->time_keep, HZ);
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment