--- /dev/null
+// SPDX-License-Identifier: GPL-2.0
+/* Microchip KSZ PTP Implementation
+ *
+ * Copyright (C) 2020 ARRI Lighting
+ * Copyright (C) 2022 Microchip Technology Inc.
+ */
+
+#include <linux/kernel.h>
+#include <linux/ptp_classify.h>
+#include <linux/ptp_clock_kernel.h>
+
+#include "ksz_common.h"
+#include "ksz_ptp.h"
+#include "ksz_ptp_reg.h"
+
+#define ptp_caps_to_data(d) container_of((d), struct ksz_ptp_data, caps)
+#define ptp_data_to_ksz_dev(d) container_of((d), struct ksz_device, ptp_data)
+
+/* Sub-nanoseconds-adj,max * sub-nanoseconds / 40ns * 1ns
+ * = (2^30-1) * (2 ^ 32) / 40 ns * 1 ns = 6249999
+ */
+#define KSZ_MAX_DRIFT_CORR 6249999
+
+#define KSZ_PTP_INC_NS 40ULL  /* HW clock is incremented every 40 ns (by 40) */
+#define KSZ_PTP_SUBNS_BITS 32
+
+static int _ksz_ptp_gettime(struct ksz_device *dev, struct timespec64 *ts)
+{
+       u32 nanoseconds;
+       u32 seconds;
+       u8 phase;
+       int ret;
+
+       /* Copy current PTP clock into shadow registers and read */
+       ret = ksz_rmw16(dev, REG_PTP_CLK_CTRL, PTP_READ_TIME, PTP_READ_TIME);
+       if (ret)
+               return ret;
+
+       ret = ksz_read8(dev, REG_PTP_RTC_SUB_NANOSEC__2, &phase);
+       if (ret)
+               return ret;
+
+       ret = ksz_read32(dev, REG_PTP_RTC_NANOSEC, &nanoseconds);
+       if (ret)
+               return ret;
+
+       ret = ksz_read32(dev, REG_PTP_RTC_SEC, &seconds);
+       if (ret)
+               return ret;
+
+       ts->tv_sec = seconds;
+       ts->tv_nsec = nanoseconds + phase * 8;
+
+       return 0;
+}
+
+static int ksz_ptp_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts)
+{
+       struct ksz_ptp_data *ptp_data = ptp_caps_to_data(ptp);
+       struct ksz_device *dev = ptp_data_to_ksz_dev(ptp_data);
+       int ret;
+
+       mutex_lock(&ptp_data->lock);
+       ret = _ksz_ptp_gettime(dev, ts);
+       mutex_unlock(&ptp_data->lock);
+
+       return ret;
+}
+
+static int ksz_ptp_settime(struct ptp_clock_info *ptp,
+                          const struct timespec64 *ts)
+{
+       struct ksz_ptp_data *ptp_data = ptp_caps_to_data(ptp);
+       struct ksz_device *dev = ptp_data_to_ksz_dev(ptp_data);
+       int ret;
+
+       mutex_lock(&ptp_data->lock);
+
+       /* Write to shadow registers and Load PTP clock */
+       ret = ksz_write16(dev, REG_PTP_RTC_SUB_NANOSEC__2, PTP_RTC_0NS);
+       if (ret)
+               goto unlock;
+
+       ret = ksz_write32(dev, REG_PTP_RTC_NANOSEC, ts->tv_nsec);
+       if (ret)
+               goto unlock;
+
+       ret = ksz_write32(dev, REG_PTP_RTC_SEC, ts->tv_sec);
+       if (ret)
+               goto unlock;
+
+       ret = ksz_rmw16(dev, REG_PTP_CLK_CTRL, PTP_LOAD_TIME, PTP_LOAD_TIME);
+
+unlock:
+       mutex_unlock(&ptp_data->lock);
+
+       return ret;
+}
+
+static int ksz_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
+{
+       struct ksz_ptp_data *ptp_data = ptp_caps_to_data(ptp);
+       struct ksz_device *dev = ptp_data_to_ksz_dev(ptp_data);
+       u64 base, adj;
+       bool negative;
+       u32 data32;
+       int ret;
+
+       mutex_lock(&ptp_data->lock);
+
+       if (scaled_ppm) {
+               base = KSZ_PTP_INC_NS << KSZ_PTP_SUBNS_BITS;
+               negative = diff_by_scaled_ppm(base, scaled_ppm, &adj);
+
+               data32 = (u32)adj;
+               data32 &= PTP_SUBNANOSEC_M;
+               if (!negative)
+                       data32 |= PTP_RATE_DIR;
+
+               ret = ksz_write32(dev, REG_PTP_SUBNANOSEC_RATE, data32);
+               if (ret)
+                       goto unlock;
+
+               ret = ksz_rmw16(dev, REG_PTP_CLK_CTRL, PTP_CLK_ADJ_ENABLE,
+                               PTP_CLK_ADJ_ENABLE);
+               if (ret)
+                       goto unlock;
+       } else {
+               ret = ksz_rmw16(dev, REG_PTP_CLK_CTRL, PTP_CLK_ADJ_ENABLE, 0);
+               if (ret)
+                       goto unlock;
+       }
+
+unlock:
+       mutex_unlock(&ptp_data->lock);
+       return ret;
+}
+
+static int ksz_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
+{
+       struct ksz_ptp_data *ptp_data = ptp_caps_to_data(ptp);
+       struct ksz_device *dev = ptp_data_to_ksz_dev(ptp_data);
+       s32 sec, nsec;
+       u16 data16;
+       int ret;
+
+       mutex_lock(&ptp_data->lock);
+
+       /* do not use ns_to_timespec64(),
+        * both sec and nsec are subtracted by hw
+        */
+       sec = div_s64_rem(delta, NSEC_PER_SEC, &nsec);
+
+       ret = ksz_write32(dev, REG_PTP_RTC_NANOSEC, abs(nsec));
+       if (ret)
+               goto unlock;
+
+       ret = ksz_write32(dev, REG_PTP_RTC_SEC, abs(sec));
+       if (ret)
+               goto unlock;
+
+       ret = ksz_read16(dev, REG_PTP_CLK_CTRL, &data16);
+       if (ret)
+               goto unlock;
+
+       data16 |= PTP_STEP_ADJ;
+
+       /* PTP_STEP_DIR -- 0: subtract, 1: add */
+       if (delta < 0)
+               data16 &= ~PTP_STEP_DIR;
+       else
+               data16 |= PTP_STEP_DIR;
+
+       ret = ksz_write16(dev, REG_PTP_CLK_CTRL, data16);
+
+unlock:
+       mutex_unlock(&ptp_data->lock);
+       return ret;
+}
+
+static int ksz_ptp_start_clock(struct ksz_device *dev)
+{
+       return ksz_rmw16(dev, REG_PTP_CLK_CTRL, PTP_CLK_ENABLE, PTP_CLK_ENABLE);
+}
+
+int ksz_ptp_clock_register(struct dsa_switch *ds)
+{
+       struct ksz_device *dev = ds->priv;
+       struct ksz_ptp_data *ptp_data;
+       int ret;
+
+       ptp_data = &dev->ptp_data;
+       mutex_init(&ptp_data->lock);
+
+       ptp_data->caps.owner            = THIS_MODULE;
+       snprintf(ptp_data->caps.name, 16, "Microchip Clock");
+       ptp_data->caps.max_adj          = KSZ_MAX_DRIFT_CORR;
+       ptp_data->caps.gettime64        = ksz_ptp_gettime;
+       ptp_data->caps.settime64        = ksz_ptp_settime;
+       ptp_data->caps.adjfine          = ksz_ptp_adjfine;
+       ptp_data->caps.adjtime          = ksz_ptp_adjtime;
+
+       ret = ksz_ptp_start_clock(dev);
+       if (ret)
+               return ret;
+
+       /* Currently only P2P mode is supported. When 802_1AS bit is set, it
+        * forwards all PTP packets to host port and none to other ports.
+        */
+       ret = ksz_rmw16(dev, REG_PTP_MSG_CONF1, PTP_TC_P2P | PTP_802_1AS,
+                       PTP_TC_P2P | PTP_802_1AS);
+       if (ret)
+               return ret;
+
+       ptp_data->clock = ptp_clock_register(&ptp_data->caps, dev->dev);
+       if (IS_ERR_OR_NULL(ptp_data->clock))
+               return PTR_ERR(ptp_data->clock);
+
+       return 0;
+}
+
+void ksz_ptp_clock_unregister(struct dsa_switch *ds)
+{
+       struct ksz_device *dev = ds->priv;
+       struct ksz_ptp_data *ptp_data;
+
+       ptp_data = &dev->ptp_data;
+
+       if (ptp_data->clock)
+               ptp_clock_unregister(ptp_data->clock);
+}
+
+MODULE_AUTHOR("Christian Eggers <ceggers@arri.de>");
+MODULE_AUTHOR("Arun Ramadoss <arun.ramadoss@microchip.com>");
+MODULE_DESCRIPTION("PTP support for KSZ switch");
+MODULE_LICENSE("GPL");
 
--- /dev/null
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Microchip KSZ PTP register definitions
+ * Copyright (C) 2022 Microchip Technology Inc.
+ */
+
+#ifndef __KSZ_PTP_REGS_H
+#define __KSZ_PTP_REGS_H
+
+/* 5 - PTP Clock */
+#define REG_PTP_CLK_CTRL               0x0500
+
+#define PTP_STEP_ADJ                   BIT(6)
+#define PTP_STEP_DIR                   BIT(5)
+#define PTP_READ_TIME                  BIT(4)
+#define PTP_LOAD_TIME                  BIT(3)
+#define PTP_CLK_ADJ_ENABLE             BIT(2)
+#define PTP_CLK_ENABLE                 BIT(1)
+#define PTP_CLK_RESET                  BIT(0)
+
+#define REG_PTP_RTC_SUB_NANOSEC__2     0x0502
+
+#define PTP_RTC_SUB_NANOSEC_M          0x0007
+#define PTP_RTC_0NS                    0x00
+
+#define REG_PTP_RTC_NANOSEC            0x0504
+
+#define REG_PTP_RTC_SEC                        0x0508
+
+#define REG_PTP_SUBNANOSEC_RATE                0x050C
+
+#define PTP_SUBNANOSEC_M               0x3FFFFFFF
+#define PTP_RATE_DIR                   BIT(31)
+#define PTP_TMP_RATE_ENABLE            BIT(30)
+
+#define REG_PTP_SUBNANOSEC_RATE_L      0x050E
+
+#define REG_PTP_RATE_DURATION          0x0510
+#define REG_PTP_RATE_DURATION_H                0x0510
+#define REG_PTP_RATE_DURATION_L                0x0512
+
+#define REG_PTP_MSG_CONF1              0x0514
+
+#define PTP_802_1AS                    BIT(7)
+#define PTP_ENABLE                     BIT(6)
+#define PTP_ETH_ENABLE                 BIT(5)
+#define PTP_IPV4_UDP_ENABLE            BIT(4)
+#define PTP_IPV6_UDP_ENABLE            BIT(3)
+#define PTP_TC_P2P                     BIT(2)
+#define PTP_MASTER                     BIT(1)
+#define PTP_1STEP                      BIT(0)
+
+#endif