Skip to content

Commit 91c0d98

Browse files
Nimrod Andydavem330
authored andcommitted
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: Fugang Duan <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 08f1a1b commit 91c0d98

File tree

3 files changed

+41
-15
lines changed

3 files changed

+41
-15
lines changed

drivers/net/ethernet/freescale/fec.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,9 @@ struct fec_enet_private {
275275
struct clk *clk_enet_out;
276276
struct clk *clk_ptp;
277277

278+
bool ptp_clk_on;
279+
struct mutex ptp_clk_mutex;
280+
278281
/* The saved address of a sent-in-place packet/buffer, for skfree(). */
279282
unsigned char *tx_bounce[TX_RING_SIZE];
280283
struct sk_buff *tx_skbuff[TX_RING_SIZE];
@@ -335,7 +338,7 @@ struct fec_enet_private {
335338
u32 cycle_speed;
336339
int hwts_rx_en;
337340
int hwts_tx_en;
338-
struct timer_list time_keep;
341+
struct delayed_work time_keep;
339342
struct regulator *reg_phy;
340343
};
341344

drivers/net/ethernet/freescale/fec_main.c

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1611,17 +1611,27 @@ static int fec_enet_clk_enable(struct net_device *ndev, bool enable)
16111611
goto failed_clk_enet_out;
16121612
}
16131613
if (fep->clk_ptp) {
1614+
mutex_lock(&fep->ptp_clk_mutex);
16141615
ret = clk_prepare_enable(fep->clk_ptp);
1615-
if (ret)
1616+
if (ret) {
1617+
mutex_unlock(&fep->ptp_clk_mutex);
16161618
goto failed_clk_ptp;
1619+
} else {
1620+
fep->ptp_clk_on = true;
1621+
}
1622+
mutex_unlock(&fep->ptp_clk_mutex);
16171623
}
16181624
} else {
16191625
clk_disable_unprepare(fep->clk_ahb);
16201626
clk_disable_unprepare(fep->clk_ipg);
16211627
if (fep->clk_enet_out)
16221628
clk_disable_unprepare(fep->clk_enet_out);
1623-
if (fep->clk_ptp)
1629+
if (fep->clk_ptp) {
1630+
mutex_lock(&fep->ptp_clk_mutex);
16241631
clk_disable_unprepare(fep->clk_ptp);
1632+
fep->ptp_clk_on = false;
1633+
mutex_unlock(&fep->ptp_clk_mutex);
1634+
}
16251635
}
16261636

16271637
return 0;
@@ -2625,6 +2635,8 @@ fec_probe(struct platform_device *pdev)
26252635
if (IS_ERR(fep->clk_enet_out))
26262636
fep->clk_enet_out = NULL;
26272637

2638+
fep->ptp_clk_on = false;
2639+
mutex_init(&fep->ptp_clk_mutex);
26282640
fep->clk_ptp = devm_clk_get(&pdev->dev, "ptp");
26292641
fep->bufdesc_ex =
26302642
pdev->id_entry->driver_data & FEC_QUIRK_HAS_BUFDESC_EX;
@@ -2715,10 +2727,10 @@ fec_drv_remove(struct platform_device *pdev)
27152727
struct net_device *ndev = platform_get_drvdata(pdev);
27162728
struct fec_enet_private *fep = netdev_priv(ndev);
27172729

2730+
cancel_delayed_work_sync(&fep->time_keep);
27182731
cancel_work_sync(&fep->tx_timeout_work);
27192732
unregister_netdev(ndev);
27202733
fec_enet_mii_remove(fep);
2721-
del_timer_sync(&fep->time_keep);
27222734
if (fep->reg_phy)
27232735
regulator_disable(fep->reg_phy);
27242736
if (fep->ptp_clock)

drivers/net/ethernet/freescale/fec_ptp.c

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -245,12 +245,20 @@ static int fec_ptp_settime(struct ptp_clock_info *ptp,
245245
u64 ns;
246246
unsigned long flags;
247247

248+
mutex_lock(&fep->ptp_clk_mutex);
249+
/* Check the ptp clock */
250+
if (!fep->ptp_clk_on) {
251+
mutex_unlock(&fep->ptp_clk_mutex);
252+
return -EINVAL;
253+
}
254+
248255
ns = ts->tv_sec * 1000000000ULL;
249256
ns += ts->tv_nsec;
250257

251258
spin_lock_irqsave(&fep->tmreg_lock, flags);
252259
timecounter_init(&fep->tc, &fep->cc, ns);
253260
spin_unlock_irqrestore(&fep->tmreg_lock, flags);
261+
mutex_unlock(&fep->ptp_clk_mutex);
254262
return 0;
255263
}
256264

@@ -338,17 +346,22 @@ int fec_ptp_get(struct net_device *ndev, struct ifreq *ifr)
338346
* fec_time_keep - call timecounter_read every second to avoid timer overrun
339347
* because ENET just support 32bit counter, will timeout in 4s
340348
*/
341-
static void fec_time_keep(unsigned long _data)
349+
static void fec_time_keep(struct work_struct *work)
342350
{
343-
struct fec_enet_private *fep = (struct fec_enet_private *)_data;
351+
struct delayed_work *dwork = to_delayed_work(work);
352+
struct fec_enet_private *fep = container_of(dwork, struct fec_enet_private, time_keep);
344353
u64 ns;
345354
unsigned long flags;
346355

347-
spin_lock_irqsave(&fep->tmreg_lock, flags);
348-
ns = timecounter_read(&fep->tc);
349-
spin_unlock_irqrestore(&fep->tmreg_lock, flags);
356+
mutex_lock(&fep->ptp_clk_mutex);
357+
if (fep->ptp_clk_on) {
358+
spin_lock_irqsave(&fep->tmreg_lock, flags);
359+
ns = timecounter_read(&fep->tc);
360+
spin_unlock_irqrestore(&fep->tmreg_lock, flags);
361+
}
362+
mutex_unlock(&fep->ptp_clk_mutex);
350363

351-
mod_timer(&fep->time_keep, jiffies + HZ);
364+
schedule_delayed_work(&fep->time_keep, HZ);
352365
}
353366

354367
/**
@@ -386,15 +399,13 @@ void fec_ptp_init(struct platform_device *pdev)
386399

387400
fec_ptp_start_cyclecounter(ndev);
388401

389-
init_timer(&fep->time_keep);
390-
fep->time_keep.data = (unsigned long)fep;
391-
fep->time_keep.function = fec_time_keep;
392-
fep->time_keep.expires = jiffies + HZ;
393-
add_timer(&fep->time_keep);
402+
INIT_DELAYED_WORK(&fep->time_keep, fec_time_keep);
394403

395404
fep->ptp_clock = ptp_clock_register(&fep->ptp_caps, &pdev->dev);
396405
if (IS_ERR(fep->ptp_clock)) {
397406
fep->ptp_clock = NULL;
398407
pr_err("ptp_clock_register failed\n");
399408
}
409+
410+
schedule_delayed_work(&fep->time_keep, HZ);
400411
}

0 commit comments

Comments
 (0)