| From e2342f4b379e8b069528f3b9d1f1d6264886ff5e Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Tue, 25 Nov 2025 16:52:08 +0800 |
| Subject: net: fec: do not update PEROUT if it is enabled |
| |
| From: Wei Fang <wei.fang@nxp.com> |
| |
| [ Upstream commit e97faa0c20ea8840f45569ba434e30538fff8fc9 ] |
| |
| If the previously set PEROUT is already active, updating it will cause |
| the new PEROUT to start immediately instead of at the specified time. |
| This is because fep->reload_period is updated whithout check whether |
| the PEROUT is enabled, and the old PEROUT is not disabled. Therefore, |
| the pulse period will be updated immediately in the pulse interrupt |
| handler fec_pps_interrupt(). |
| |
| Currently, the driver does not support directly updating PEROUT and it |
| will make the logic be more complicated. To fix the current issue, add |
| a check before enabling the PEROUT, the driver will return an error if |
| PEROUT is enabled. If users wants to update a new PEROUT, they should |
| disable the old PEROUT first. |
| |
| Fixes: 350749b909bf ("net: fec: Add support for periodic output signal of PPS") |
| Signed-off-by: Wei Fang <wei.fang@nxp.com> |
| Link: https://patch.msgid.link/20251125085210.1094306-3-wei.fang@nxp.com |
| Signed-off-by: Paolo Abeni <pabeni@redhat.com> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| drivers/net/ethernet/freescale/fec.h | 1 + |
| drivers/net/ethernet/freescale/fec_ptp.c | 43 ++++++++++++++++++------ |
| 2 files changed, 34 insertions(+), 10 deletions(-) |
| |
| diff --git a/drivers/net/ethernet/freescale/fec.h b/drivers/net/ethernet/freescale/fec.h |
| index 1cca0425d4939..03fa6d6e6f0a8 100644 |
| --- a/drivers/net/ethernet/freescale/fec.h |
| +++ b/drivers/net/ethernet/freescale/fec.h |
| @@ -683,6 +683,7 @@ struct fec_enet_private { |
| unsigned int reload_period; |
| int pps_enable; |
| unsigned int next_counter; |
| + bool perout_enable; |
| struct hrtimer perout_timer; |
| u64 perout_stime; |
| |
| diff --git a/drivers/net/ethernet/freescale/fec_ptp.c b/drivers/net/ethernet/freescale/fec_ptp.c |
| index cb3f05da3eee6..a3853fccdc7b6 100644 |
| --- a/drivers/net/ethernet/freescale/fec_ptp.c |
| +++ b/drivers/net/ethernet/freescale/fec_ptp.c |
| @@ -244,6 +244,7 @@ static int fec_ptp_pps_perout(struct fec_enet_private *fep) |
| * the FEC_TCCR register in time and missed the start time. |
| */ |
| if (fep->perout_stime < curr_time + 100 * NSEC_PER_MSEC) { |
| + fep->perout_enable = false; |
| dev_err(&fep->pdev->dev, "Current time is too close to the start time!\n"); |
| spin_unlock_irqrestore(&fep->tmreg_lock, flags); |
| return -1; |
| @@ -501,6 +502,7 @@ static int fec_ptp_pps_disable(struct fec_enet_private *fep, uint channel) |
| hrtimer_cancel(&fep->perout_timer); |
| |
| spin_lock_irqsave(&fep->tmreg_lock, flags); |
| + fep->perout_enable = false; |
| writel(0, fep->hwp + FEC_TCSR(channel)); |
| spin_unlock_irqrestore(&fep->tmreg_lock, flags); |
| |
| @@ -532,6 +534,8 @@ static int fec_ptp_enable(struct ptp_clock_info *ptp, |
| |
| return ret; |
| } else if (rq->type == PTP_CLK_REQ_PEROUT) { |
| + u32 reload_period; |
| + |
| /* Reject requests with unsupported flags */ |
| if (rq->perout.flags) |
| return -EOPNOTSUPP; |
| @@ -551,12 +555,14 @@ static int fec_ptp_enable(struct ptp_clock_info *ptp, |
| return -EOPNOTSUPP; |
| } |
| |
| - fep->reload_period = div_u64(period_ns, 2); |
| - if (on && fep->reload_period) { |
| + reload_period = div_u64(period_ns, 2); |
| + if (on && reload_period) { |
| + u64 perout_stime; |
| + |
| /* Convert 1588 timestamp to ns*/ |
| start_time.tv_sec = rq->perout.start.sec; |
| start_time.tv_nsec = rq->perout.start.nsec; |
| - fep->perout_stime = timespec64_to_ns(&start_time); |
| + perout_stime = timespec64_to_ns(&start_time); |
| |
| mutex_lock(&fep->ptp_clk_mutex); |
| if (!fep->ptp_clk_on) { |
| @@ -565,18 +571,35 @@ static int fec_ptp_enable(struct ptp_clock_info *ptp, |
| return -EOPNOTSUPP; |
| } |
| spin_lock_irqsave(&fep->tmreg_lock, flags); |
| + |
| + if (fep->perout_enable) { |
| + dev_err(&fep->pdev->dev, |
| + "PEROUT has been enabled\n"); |
| + ret = -EBUSY; |
| + goto unlock; |
| + } |
| + |
| /* Read current timestamp */ |
| curr_time = timecounter_read(&fep->tc); |
| - spin_unlock_irqrestore(&fep->tmreg_lock, flags); |
| - mutex_unlock(&fep->ptp_clk_mutex); |
| + if (perout_stime <= curr_time) { |
| + dev_err(&fep->pdev->dev, |
| + "Start time must be greater than current time\n"); |
| + ret = -EINVAL; |
| + goto unlock; |
| + } |
| |
| /* Calculate time difference */ |
| - delta = fep->perout_stime - curr_time; |
| + delta = perout_stime - curr_time; |
| + fep->reload_period = reload_period; |
| + fep->perout_stime = perout_stime; |
| + fep->perout_enable = true; |
| |
| - if (fep->perout_stime <= curr_time) { |
| - dev_err(&fep->pdev->dev, "Start time must larger than current time!\n"); |
| - return -EINVAL; |
| - } |
| +unlock: |
| + spin_unlock_irqrestore(&fep->tmreg_lock, flags); |
| + mutex_unlock(&fep->ptp_clk_mutex); |
| + |
| + if (ret) |
| + return ret; |
| |
| /* Because the timer counter of FEC only has 31-bits, correspondingly, |
| * the time comparison register FEC_TCCR also only low 31 bits can be |
| -- |
| 2.51.0 |
| |