]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
net: fec: do not update PEROUT if it is enabled
authorWei Fang <wei.fang@nxp.com>
Tue, 25 Nov 2025 08:52:08 +0000 (16:52 +0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 6 Dec 2025 21:24:56 +0000 (06:24 +0900)
[ 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
drivers/net/ethernet/freescale/fec_ptp.c

index 1cca0425d49397bbdb97f2c058bd759f9e602f17..03fa6d6e6f0a865dc5c0eb54cd11fd578ad0524d 100644 (file)
@@ -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;
 
index cb3f05da3eee696e7f4ae5a607c80d272e535dad..a3853fccdc7b62b02a5ba0fb9bbb4440ed9bf630 100644 (file)
@@ -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