phy: mscc: Fix timestamping for vsc8584
authorHoratiu Vultur <horatiu.vultur@microchip.com>
Mon, 18 Aug 2025 08:10:29 +0000 (10:10 +0200)
committerJakub Kicinski <kuba@kernel.org>
Wed, 20 Aug 2025 00:52:02 +0000 (17:52 -0700)
There was a problem when we received frames and the frames were
timestamped. The driver is configured to store the nanosecond part of
the timestmap in the ptp reserved bits and it would take the second part
by reading the LTC. The problem is that when reading the LTC we are in
atomic context and to read the second part will go over mdio bus which
might sleep, so we get an error.
The fix consists in actually put all the frames in a queue and start the
aux work and in that work to read the LTC and then calculate the full
received time.

Fixes: 7d272e63e0979d ("net: phy: mscc: timestamping and PHC support")
Signed-off-by: Horatiu Vultur <horatiu.vultur@microchip.com>
Reviewed-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
Reviewed-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Link: https://patch.msgid.link/20250818081029.1300780-1-horatiu.vultur@microchip.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/net/phy/mscc/mscc.h
drivers/net/phy/mscc/mscc_main.c
drivers/net/phy/mscc/mscc_ptp.c

index 6a3d8a754eb8de6dfc1740208744f625fe328384..58c6d47fbe046dbfc6cd1e361c7ac1da34852f9b 100644 (file)
@@ -362,6 +362,13 @@ struct vsc85xx_hw_stat {
        u16 mask;
 };
 
+struct vsc8531_skb_cb {
+       u32 ns;
+};
+
+#define VSC8531_SKB_CB(skb) \
+       ((struct vsc8531_skb_cb *)((skb)->cb))
+
 struct vsc8531_private {
        int rate_magic;
        u16 supp_led_modes;
@@ -410,6 +417,11 @@ struct vsc8531_private {
         */
        struct mutex ts_lock;
        struct mutex phc_lock;
+
+       /* list of skbs that were received and need timestamp information but it
+        * didn't received it yet
+        */
+       struct sk_buff_head rx_skbs_list;
 };
 
 /* Shared structure between the PHYs of the same package.
index 7ed6522fb0ef7474762f2a9d417362a6974646c9..f1c9ce351ab42c5e4095c15a5f12bfed3cf14fb2 100644 (file)
@@ -2335,6 +2335,13 @@ static int vsc85xx_probe(struct phy_device *phydev)
        return vsc85xx_dt_led_modes_get(phydev, default_mode);
 }
 
+static void vsc85xx_remove(struct phy_device *phydev)
+{
+       struct vsc8531_private *priv = phydev->priv;
+
+       skb_queue_purge(&priv->rx_skbs_list);
+}
+
 /* Microsemi VSC85xx PHYs */
 static struct phy_driver vsc85xx_driver[] = {
 {
@@ -2589,6 +2596,7 @@ static struct phy_driver vsc85xx_driver[] = {
        .config_intr    = &vsc85xx_config_intr,
        .suspend        = &genphy_suspend,
        .resume         = &genphy_resume,
+       .remove         = &vsc85xx_remove,
        .probe          = &vsc8574_probe,
        .set_wol        = &vsc85xx_wol_set,
        .get_wol        = &vsc85xx_wol_get,
@@ -2614,6 +2622,7 @@ static struct phy_driver vsc85xx_driver[] = {
        .config_intr    = &vsc85xx_config_intr,
        .suspend        = &genphy_suspend,
        .resume         = &genphy_resume,
+       .remove         = &vsc85xx_remove,
        .probe          = &vsc8574_probe,
        .set_wol        = &vsc85xx_wol_set,
        .get_wol        = &vsc85xx_wol_get,
@@ -2639,6 +2648,7 @@ static struct phy_driver vsc85xx_driver[] = {
        .config_intr    = &vsc85xx_config_intr,
        .suspend        = &genphy_suspend,
        .resume         = &genphy_resume,
+       .remove         = &vsc85xx_remove,
        .probe          = &vsc8584_probe,
        .get_tunable    = &vsc85xx_get_tunable,
        .set_tunable    = &vsc85xx_set_tunable,
@@ -2662,6 +2672,7 @@ static struct phy_driver vsc85xx_driver[] = {
        .config_intr    = &vsc85xx_config_intr,
        .suspend        = &genphy_suspend,
        .resume         = &genphy_resume,
+       .remove         = &vsc85xx_remove,
        .probe          = &vsc8584_probe,
        .get_tunable    = &vsc85xx_get_tunable,
        .set_tunable    = &vsc85xx_set_tunable,
@@ -2685,6 +2696,7 @@ static struct phy_driver vsc85xx_driver[] = {
        .config_intr    = &vsc85xx_config_intr,
        .suspend        = &genphy_suspend,
        .resume         = &genphy_resume,
+       .remove         = &vsc85xx_remove,
        .probe          = &vsc8584_probe,
        .get_tunable    = &vsc85xx_get_tunable,
        .set_tunable    = &vsc85xx_set_tunable,
index 275706de5847cd8f8e8bde7425a5e66978a178e0..de6c7312e8f290f851b4e548efc876e7818bc790 100644 (file)
@@ -1194,9 +1194,7 @@ static bool vsc85xx_rxtstamp(struct mii_timestamper *mii_ts,
 {
        struct vsc8531_private *vsc8531 =
                container_of(mii_ts, struct vsc8531_private, mii_ts);
-       struct skb_shared_hwtstamps *shhwtstamps = NULL;
        struct vsc85xx_ptphdr *ptphdr;
-       struct timespec64 ts;
        unsigned long ns;
 
        if (!vsc8531->ptp->configured)
@@ -1206,27 +1204,52 @@ static bool vsc85xx_rxtstamp(struct mii_timestamper *mii_ts,
            type == PTP_CLASS_NONE)
                return false;
 
-       vsc85xx_gettime(&vsc8531->ptp->caps, &ts);
-
        ptphdr = get_ptp_header_rx(skb, vsc8531->ptp->rx_filter);
        if (!ptphdr)
                return false;
 
-       shhwtstamps = skb_hwtstamps(skb);
-       memset(shhwtstamps, 0, sizeof(struct skb_shared_hwtstamps));
-
        ns = ntohl(ptphdr->rsrvd2);
 
-       /* nsec is in reserved field */
-       if (ts.tv_nsec < ns)
-               ts.tv_sec--;
+       VSC8531_SKB_CB(skb)->ns = ns;
+       skb_queue_tail(&vsc8531->rx_skbs_list, skb);
 
-       shhwtstamps->hwtstamp = ktime_set(ts.tv_sec, ns);
-       netif_rx(skb);
+       ptp_schedule_worker(vsc8531->ptp->ptp_clock, 0);
 
        return true;
 }
 
+static long vsc85xx_do_aux_work(struct ptp_clock_info *info)
+{
+       struct vsc85xx_ptp *ptp = container_of(info, struct vsc85xx_ptp, caps);
+       struct skb_shared_hwtstamps *shhwtstamps = NULL;
+       struct phy_device *phydev = ptp->phydev;
+       struct vsc8531_private *priv = phydev->priv;
+       struct sk_buff_head received;
+       struct sk_buff *rx_skb;
+       struct timespec64 ts;
+       unsigned long flags;
+
+       __skb_queue_head_init(&received);
+       spin_lock_irqsave(&priv->rx_skbs_list.lock, flags);
+       skb_queue_splice_tail_init(&priv->rx_skbs_list, &received);
+       spin_unlock_irqrestore(&priv->rx_skbs_list.lock, flags);
+
+       vsc85xx_gettime(info, &ts);
+       while ((rx_skb = __skb_dequeue(&received)) != NULL) {
+               shhwtstamps = skb_hwtstamps(rx_skb);
+               memset(shhwtstamps, 0, sizeof(struct skb_shared_hwtstamps));
+
+               if (ts.tv_nsec < VSC8531_SKB_CB(rx_skb)->ns)
+                       ts.tv_sec--;
+
+               shhwtstamps->hwtstamp = ktime_set(ts.tv_sec,
+                                                 VSC8531_SKB_CB(rx_skb)->ns);
+               netif_rx(rx_skb);
+       }
+
+       return -1;
+}
+
 static const struct ptp_clock_info vsc85xx_clk_caps = {
        .owner          = THIS_MODULE,
        .name           = "VSC85xx timer",
@@ -1240,6 +1263,7 @@ static const struct ptp_clock_info vsc85xx_clk_caps = {
        .adjfine        = &vsc85xx_adjfine,
        .gettime64      = &vsc85xx_gettime,
        .settime64      = &vsc85xx_settime,
+       .do_aux_work    = &vsc85xx_do_aux_work,
 };
 
 static struct vsc8531_private *vsc8584_base_priv(struct phy_device *phydev)
@@ -1567,6 +1591,7 @@ int vsc8584_ptp_probe(struct phy_device *phydev)
 
        mutex_init(&vsc8531->phc_lock);
        mutex_init(&vsc8531->ts_lock);
+       skb_queue_head_init(&vsc8531->rx_skbs_list);
 
        /* Retrieve the shared load/save GPIO. Request it as non exclusive as
         * the same GPIO can be requested by all the PHYs of the same package.