206 lines · c
1// SPDX-License-Identifier: GPL-2.02/* Renesas R-Car Gen4 gPTP device driver3 *4 * Copyright (C) 2022 Renesas Electronics Corporation5 */6 7#include <linux/err.h>8#include <linux/etherdevice.h>9#include <linux/kernel.h>10#include <linux/module.h>11#include <linux/platform_device.h>12#include <linux/slab.h>13 14#include "rcar_gen4_ptp.h"15#define ptp_to_priv(ptp) container_of(ptp, struct rcar_gen4_ptp_private, info)16 17static const struct rcar_gen4_ptp_reg_offset gen4_offs = {18 .enable = PTPTMEC,19 .disable = PTPTMDC,20 .increment = PTPTIVC0,21 .config_t0 = PTPTOVC00,22 .config_t1 = PTPTOVC10,23 .config_t2 = PTPTOVC20,24 .monitor_t0 = PTPGPTPTM00,25 .monitor_t1 = PTPGPTPTM10,26 .monitor_t2 = PTPGPTPTM20,27};28 29static int rcar_gen4_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)30{31 struct rcar_gen4_ptp_private *ptp_priv = ptp_to_priv(ptp);32 bool neg_adj = scaled_ppm < 0 ? true : false;33 s64 addend = ptp_priv->default_addend;34 s64 diff;35 36 if (neg_adj)37 scaled_ppm = -scaled_ppm;38 diff = div_s64(addend * scaled_ppm_to_ppb(scaled_ppm), NSEC_PER_SEC);39 addend = neg_adj ? addend - diff : addend + diff;40 41 iowrite32(addend, ptp_priv->addr + ptp_priv->offs->increment);42 43 return 0;44}45 46/* Caller must hold the lock */47static void _rcar_gen4_ptp_gettime(struct ptp_clock_info *ptp,48 struct timespec64 *ts)49{50 struct rcar_gen4_ptp_private *ptp_priv = ptp_to_priv(ptp);51 52 ts->tv_nsec = ioread32(ptp_priv->addr + ptp_priv->offs->monitor_t0);53 ts->tv_sec = ioread32(ptp_priv->addr + ptp_priv->offs->monitor_t1) |54 ((s64)ioread32(ptp_priv->addr + ptp_priv->offs->monitor_t2) << 32);55}56 57static int rcar_gen4_ptp_gettime(struct ptp_clock_info *ptp,58 struct timespec64 *ts)59{60 struct rcar_gen4_ptp_private *ptp_priv = ptp_to_priv(ptp);61 unsigned long flags;62 63 spin_lock_irqsave(&ptp_priv->lock, flags);64 _rcar_gen4_ptp_gettime(ptp, ts);65 spin_unlock_irqrestore(&ptp_priv->lock, flags);66 67 return 0;68}69 70/* Caller must hold the lock */71static void _rcar_gen4_ptp_settime(struct ptp_clock_info *ptp,72 const struct timespec64 *ts)73{74 struct rcar_gen4_ptp_private *ptp_priv = ptp_to_priv(ptp);75 76 iowrite32(1, ptp_priv->addr + ptp_priv->offs->disable);77 iowrite32(0, ptp_priv->addr + ptp_priv->offs->config_t2);78 iowrite32(0, ptp_priv->addr + ptp_priv->offs->config_t1);79 iowrite32(0, ptp_priv->addr + ptp_priv->offs->config_t0);80 iowrite32(1, ptp_priv->addr + ptp_priv->offs->enable);81 iowrite32(ts->tv_sec >> 32, ptp_priv->addr + ptp_priv->offs->config_t2);82 iowrite32(ts->tv_sec, ptp_priv->addr + ptp_priv->offs->config_t1);83 iowrite32(ts->tv_nsec, ptp_priv->addr + ptp_priv->offs->config_t0);84}85 86static int rcar_gen4_ptp_settime(struct ptp_clock_info *ptp,87 const struct timespec64 *ts)88{89 struct rcar_gen4_ptp_private *ptp_priv = ptp_to_priv(ptp);90 unsigned long flags;91 92 spin_lock_irqsave(&ptp_priv->lock, flags);93 _rcar_gen4_ptp_settime(ptp, ts);94 spin_unlock_irqrestore(&ptp_priv->lock, flags);95 96 return 0;97}98 99static int rcar_gen4_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)100{101 struct rcar_gen4_ptp_private *ptp_priv = ptp_to_priv(ptp);102 struct timespec64 ts;103 unsigned long flags;104 s64 now;105 106 spin_lock_irqsave(&ptp_priv->lock, flags);107 _rcar_gen4_ptp_gettime(ptp, &ts);108 now = ktime_to_ns(timespec64_to_ktime(ts));109 ts = ns_to_timespec64(now + delta);110 _rcar_gen4_ptp_settime(ptp, &ts);111 spin_unlock_irqrestore(&ptp_priv->lock, flags);112 113 return 0;114}115 116static int rcar_gen4_ptp_enable(struct ptp_clock_info *ptp,117 struct ptp_clock_request *rq, int on)118{119 return -EOPNOTSUPP;120}121 122static struct ptp_clock_info rcar_gen4_ptp_info = {123 .owner = THIS_MODULE,124 .name = "rcar_gen4_ptp",125 .max_adj = 50000000,126 .adjfine = rcar_gen4_ptp_adjfine,127 .adjtime = rcar_gen4_ptp_adjtime,128 .gettime64 = rcar_gen4_ptp_gettime,129 .settime64 = rcar_gen4_ptp_settime,130 .enable = rcar_gen4_ptp_enable,131};132 133static int rcar_gen4_ptp_set_offs(struct rcar_gen4_ptp_private *ptp_priv,134 enum rcar_gen4_ptp_reg_layout layout)135{136 if (layout != RCAR_GEN4_PTP_REG_LAYOUT)137 return -EINVAL;138 139 ptp_priv->offs = &gen4_offs;140 141 return 0;142}143 144static s64 rcar_gen4_ptp_rate_to_increment(u32 rate)145{146 /* Timer increment in ns.147 * bit[31:27] - integer148 * bit[26:0] - decimal149 * increment[ns] = perid[ns] * 2^27 => (1ns * 2^27) / rate[hz]150 */151 return div_s64(1000000000LL << 27, rate);152}153 154int rcar_gen4_ptp_register(struct rcar_gen4_ptp_private *ptp_priv,155 enum rcar_gen4_ptp_reg_layout layout, u32 rate)156{157 int ret;158 159 if (ptp_priv->initialized)160 return 0;161 162 spin_lock_init(&ptp_priv->lock);163 164 ret = rcar_gen4_ptp_set_offs(ptp_priv, layout);165 if (ret)166 return ret;167 168 ptp_priv->default_addend = rcar_gen4_ptp_rate_to_increment(rate);169 iowrite32(ptp_priv->default_addend, ptp_priv->addr + ptp_priv->offs->increment);170 ptp_priv->clock = ptp_clock_register(&ptp_priv->info, NULL);171 if (IS_ERR(ptp_priv->clock))172 return PTR_ERR(ptp_priv->clock);173 174 iowrite32(0x01, ptp_priv->addr + ptp_priv->offs->enable);175 ptp_priv->initialized = true;176 177 return 0;178}179EXPORT_SYMBOL_GPL(rcar_gen4_ptp_register);180 181int rcar_gen4_ptp_unregister(struct rcar_gen4_ptp_private *ptp_priv)182{183 iowrite32(1, ptp_priv->addr + ptp_priv->offs->disable);184 185 return ptp_clock_unregister(ptp_priv->clock);186}187EXPORT_SYMBOL_GPL(rcar_gen4_ptp_unregister);188 189struct rcar_gen4_ptp_private *rcar_gen4_ptp_alloc(struct platform_device *pdev)190{191 struct rcar_gen4_ptp_private *ptp;192 193 ptp = devm_kzalloc(&pdev->dev, sizeof(*ptp), GFP_KERNEL);194 if (!ptp)195 return NULL;196 197 ptp->info = rcar_gen4_ptp_info;198 199 return ptp;200}201EXPORT_SYMBOL_GPL(rcar_gen4_ptp_alloc);202 203MODULE_AUTHOR("Yoshihiro Shimoda");204MODULE_DESCRIPTION("Renesas R-Car Gen4 gPTP driver");205MODULE_LICENSE("GPL");206