summaryrefslogtreecommitdiffstats
path: root/drivers/net/ethernet/renesas/rcar_gen4_ptp.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/ethernet/renesas/rcar_gen4_ptp.c')
-rw-r--r--drivers/net/ethernet/renesas/rcar_gen4_ptp.c181
1 files changed, 181 insertions, 0 deletions
diff --git a/drivers/net/ethernet/renesas/rcar_gen4_ptp.c b/drivers/net/ethernet/renesas/rcar_gen4_ptp.c
new file mode 100644
index 0000000000..c007e33c47
--- /dev/null
+++ b/drivers/net/ethernet/renesas/rcar_gen4_ptp.c
@@ -0,0 +1,181 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Renesas R-Car Gen4 gPTP device driver
+ *
+ * Copyright (C) 2022 Renesas Electronics Corporation
+ */
+
+#include <linux/err.h>
+#include <linux/etherdevice.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include "rcar_gen4_ptp.h"
+#define ptp_to_priv(ptp) container_of(ptp, struct rcar_gen4_ptp_private, info)
+
+static const struct rcar_gen4_ptp_reg_offset s4_offs = {
+ .enable = PTPTMEC,
+ .disable = PTPTMDC,
+ .increment = PTPTIVC0,
+ .config_t0 = PTPTOVC00,
+ .config_t1 = PTPTOVC10,
+ .config_t2 = PTPTOVC20,
+ .monitor_t0 = PTPGPTPTM00,
+ .monitor_t1 = PTPGPTPTM10,
+ .monitor_t2 = PTPGPTPTM20,
+};
+
+static int rcar_gen4_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
+{
+ struct rcar_gen4_ptp_private *ptp_priv = ptp_to_priv(ptp);
+ bool neg_adj = scaled_ppm < 0 ? true : false;
+ s64 addend = ptp_priv->default_addend;
+ s64 diff;
+
+ if (neg_adj)
+ scaled_ppm = -scaled_ppm;
+ diff = div_s64(addend * scaled_ppm_to_ppb(scaled_ppm), NSEC_PER_SEC);
+ addend = neg_adj ? addend - diff : addend + diff;
+
+ iowrite32(addend, ptp_priv->addr + ptp_priv->offs->increment);
+
+ return 0;
+}
+
+/* Caller must hold the lock */
+static void _rcar_gen4_ptp_gettime(struct ptp_clock_info *ptp,
+ struct timespec64 *ts)
+{
+ struct rcar_gen4_ptp_private *ptp_priv = ptp_to_priv(ptp);
+
+ ts->tv_nsec = ioread32(ptp_priv->addr + ptp_priv->offs->monitor_t0);
+ ts->tv_sec = ioread32(ptp_priv->addr + ptp_priv->offs->monitor_t1) |
+ ((s64)ioread32(ptp_priv->addr + ptp_priv->offs->monitor_t2) << 32);
+}
+
+static int rcar_gen4_ptp_gettime(struct ptp_clock_info *ptp,
+ struct timespec64 *ts)
+{
+ struct rcar_gen4_ptp_private *ptp_priv = ptp_to_priv(ptp);
+ unsigned long flags;
+
+ spin_lock_irqsave(&ptp_priv->lock, flags);
+ _rcar_gen4_ptp_gettime(ptp, ts);
+ spin_unlock_irqrestore(&ptp_priv->lock, flags);
+
+ return 0;
+}
+
+/* Caller must hold the lock */
+static void _rcar_gen4_ptp_settime(struct ptp_clock_info *ptp,
+ const struct timespec64 *ts)
+{
+ struct rcar_gen4_ptp_private *ptp_priv = ptp_to_priv(ptp);
+
+ iowrite32(1, ptp_priv->addr + ptp_priv->offs->disable);
+ iowrite32(0, ptp_priv->addr + ptp_priv->offs->config_t2);
+ iowrite32(0, ptp_priv->addr + ptp_priv->offs->config_t1);
+ iowrite32(0, ptp_priv->addr + ptp_priv->offs->config_t0);
+ iowrite32(1, ptp_priv->addr + ptp_priv->offs->enable);
+ iowrite32(ts->tv_sec >> 32, ptp_priv->addr + ptp_priv->offs->config_t2);
+ iowrite32(ts->tv_sec, ptp_priv->addr + ptp_priv->offs->config_t1);
+ iowrite32(ts->tv_nsec, ptp_priv->addr + ptp_priv->offs->config_t0);
+}
+
+static int rcar_gen4_ptp_settime(struct ptp_clock_info *ptp,
+ const struct timespec64 *ts)
+{
+ struct rcar_gen4_ptp_private *ptp_priv = ptp_to_priv(ptp);
+ unsigned long flags;
+
+ spin_lock_irqsave(&ptp_priv->lock, flags);
+ _rcar_gen4_ptp_settime(ptp, ts);
+ spin_unlock_irqrestore(&ptp_priv->lock, flags);
+
+ return 0;
+}
+
+static int rcar_gen4_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
+{
+ struct rcar_gen4_ptp_private *ptp_priv = ptp_to_priv(ptp);
+ struct timespec64 ts;
+ unsigned long flags;
+ s64 now;
+
+ spin_lock_irqsave(&ptp_priv->lock, flags);
+ _rcar_gen4_ptp_gettime(ptp, &ts);
+ now = ktime_to_ns(timespec64_to_ktime(ts));
+ ts = ns_to_timespec64(now + delta);
+ _rcar_gen4_ptp_settime(ptp, &ts);
+ spin_unlock_irqrestore(&ptp_priv->lock, flags);
+
+ return 0;
+}
+
+static int rcar_gen4_ptp_enable(struct ptp_clock_info *ptp,
+ struct ptp_clock_request *rq, int on)
+{
+ return -EOPNOTSUPP;
+}
+
+static struct ptp_clock_info rcar_gen4_ptp_info = {
+ .owner = THIS_MODULE,
+ .name = "rcar_gen4_ptp",
+ .max_adj = 50000000,
+ .adjfine = rcar_gen4_ptp_adjfine,
+ .adjtime = rcar_gen4_ptp_adjtime,
+ .gettime64 = rcar_gen4_ptp_gettime,
+ .settime64 = rcar_gen4_ptp_settime,
+ .enable = rcar_gen4_ptp_enable,
+};
+
+static void rcar_gen4_ptp_set_offs(struct rcar_gen4_ptp_private *ptp_priv,
+ enum rcar_gen4_ptp_reg_layout layout)
+{
+ WARN_ON(layout != RCAR_GEN4_PTP_REG_LAYOUT_S4);
+
+ ptp_priv->offs = &s4_offs;
+}
+
+int rcar_gen4_ptp_register(struct rcar_gen4_ptp_private *ptp_priv,
+ enum rcar_gen4_ptp_reg_layout layout, u32 clock)
+{
+ if (ptp_priv->initialized)
+ return 0;
+
+ spin_lock_init(&ptp_priv->lock);
+
+ rcar_gen4_ptp_set_offs(ptp_priv, layout);
+
+ ptp_priv->default_addend = clock;
+ iowrite32(ptp_priv->default_addend, ptp_priv->addr + ptp_priv->offs->increment);
+ ptp_priv->clock = ptp_clock_register(&ptp_priv->info, NULL);
+ if (IS_ERR(ptp_priv->clock))
+ return PTR_ERR(ptp_priv->clock);
+
+ iowrite32(0x01, ptp_priv->addr + ptp_priv->offs->enable);
+ ptp_priv->initialized = true;
+
+ return 0;
+}
+
+int rcar_gen4_ptp_unregister(struct rcar_gen4_ptp_private *ptp_priv)
+{
+ iowrite32(1, ptp_priv->addr + ptp_priv->offs->disable);
+
+ return ptp_clock_unregister(ptp_priv->clock);
+}
+
+struct rcar_gen4_ptp_private *rcar_gen4_ptp_alloc(struct platform_device *pdev)
+{
+ struct rcar_gen4_ptp_private *ptp;
+
+ ptp = devm_kzalloc(&pdev->dev, sizeof(*ptp), GFP_KERNEL);
+ if (!ptp)
+ return NULL;
+
+ ptp->info = rcar_gen4_ptp_info;
+
+ return ptp;
+}