162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* Renesas R-Car Gen4 gPTP device driver 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 2022 Renesas Electronics Corporation 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/err.h> 862306a36Sopenharmony_ci#include <linux/etherdevice.h> 962306a36Sopenharmony_ci#include <linux/kernel.h> 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/platform_device.h> 1262306a36Sopenharmony_ci#include <linux/slab.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include "rcar_gen4_ptp.h" 1562306a36Sopenharmony_ci#define ptp_to_priv(ptp) container_of(ptp, struct rcar_gen4_ptp_private, info) 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_cistatic const struct rcar_gen4_ptp_reg_offset s4_offs = { 1862306a36Sopenharmony_ci .enable = PTPTMEC, 1962306a36Sopenharmony_ci .disable = PTPTMDC, 2062306a36Sopenharmony_ci .increment = PTPTIVC0, 2162306a36Sopenharmony_ci .config_t0 = PTPTOVC00, 2262306a36Sopenharmony_ci .config_t1 = PTPTOVC10, 2362306a36Sopenharmony_ci .config_t2 = PTPTOVC20, 2462306a36Sopenharmony_ci .monitor_t0 = PTPGPTPTM00, 2562306a36Sopenharmony_ci .monitor_t1 = PTPGPTPTM10, 2662306a36Sopenharmony_ci .monitor_t2 = PTPGPTPTM20, 2762306a36Sopenharmony_ci}; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cistatic int rcar_gen4_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) 3062306a36Sopenharmony_ci{ 3162306a36Sopenharmony_ci struct rcar_gen4_ptp_private *ptp_priv = ptp_to_priv(ptp); 3262306a36Sopenharmony_ci bool neg_adj = scaled_ppm < 0 ? true : false; 3362306a36Sopenharmony_ci s64 addend = ptp_priv->default_addend; 3462306a36Sopenharmony_ci s64 diff; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci if (neg_adj) 3762306a36Sopenharmony_ci scaled_ppm = -scaled_ppm; 3862306a36Sopenharmony_ci diff = div_s64(addend * scaled_ppm_to_ppb(scaled_ppm), NSEC_PER_SEC); 3962306a36Sopenharmony_ci addend = neg_adj ? addend - diff : addend + diff; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci iowrite32(addend, ptp_priv->addr + ptp_priv->offs->increment); 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci return 0; 4462306a36Sopenharmony_ci} 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci/* Caller must hold the lock */ 4762306a36Sopenharmony_cistatic void _rcar_gen4_ptp_gettime(struct ptp_clock_info *ptp, 4862306a36Sopenharmony_ci struct timespec64 *ts) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci struct rcar_gen4_ptp_private *ptp_priv = ptp_to_priv(ptp); 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci ts->tv_nsec = ioread32(ptp_priv->addr + ptp_priv->offs->monitor_t0); 5362306a36Sopenharmony_ci ts->tv_sec = ioread32(ptp_priv->addr + ptp_priv->offs->monitor_t1) | 5462306a36Sopenharmony_ci ((s64)ioread32(ptp_priv->addr + ptp_priv->offs->monitor_t2) << 32); 5562306a36Sopenharmony_ci} 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic int rcar_gen4_ptp_gettime(struct ptp_clock_info *ptp, 5862306a36Sopenharmony_ci struct timespec64 *ts) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci struct rcar_gen4_ptp_private *ptp_priv = ptp_to_priv(ptp); 6162306a36Sopenharmony_ci unsigned long flags; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci spin_lock_irqsave(&ptp_priv->lock, flags); 6462306a36Sopenharmony_ci _rcar_gen4_ptp_gettime(ptp, ts); 6562306a36Sopenharmony_ci spin_unlock_irqrestore(&ptp_priv->lock, flags); 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci return 0; 6862306a36Sopenharmony_ci} 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci/* Caller must hold the lock */ 7162306a36Sopenharmony_cistatic void _rcar_gen4_ptp_settime(struct ptp_clock_info *ptp, 7262306a36Sopenharmony_ci const struct timespec64 *ts) 7362306a36Sopenharmony_ci{ 7462306a36Sopenharmony_ci struct rcar_gen4_ptp_private *ptp_priv = ptp_to_priv(ptp); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci iowrite32(1, ptp_priv->addr + ptp_priv->offs->disable); 7762306a36Sopenharmony_ci iowrite32(0, ptp_priv->addr + ptp_priv->offs->config_t2); 7862306a36Sopenharmony_ci iowrite32(0, ptp_priv->addr + ptp_priv->offs->config_t1); 7962306a36Sopenharmony_ci iowrite32(0, ptp_priv->addr + ptp_priv->offs->config_t0); 8062306a36Sopenharmony_ci iowrite32(1, ptp_priv->addr + ptp_priv->offs->enable); 8162306a36Sopenharmony_ci iowrite32(ts->tv_sec >> 32, ptp_priv->addr + ptp_priv->offs->config_t2); 8262306a36Sopenharmony_ci iowrite32(ts->tv_sec, ptp_priv->addr + ptp_priv->offs->config_t1); 8362306a36Sopenharmony_ci iowrite32(ts->tv_nsec, ptp_priv->addr + ptp_priv->offs->config_t0); 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistatic int rcar_gen4_ptp_settime(struct ptp_clock_info *ptp, 8762306a36Sopenharmony_ci const struct timespec64 *ts) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci struct rcar_gen4_ptp_private *ptp_priv = ptp_to_priv(ptp); 9062306a36Sopenharmony_ci unsigned long flags; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci spin_lock_irqsave(&ptp_priv->lock, flags); 9362306a36Sopenharmony_ci _rcar_gen4_ptp_settime(ptp, ts); 9462306a36Sopenharmony_ci spin_unlock_irqrestore(&ptp_priv->lock, flags); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci return 0; 9762306a36Sopenharmony_ci} 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistatic int rcar_gen4_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci struct rcar_gen4_ptp_private *ptp_priv = ptp_to_priv(ptp); 10262306a36Sopenharmony_ci struct timespec64 ts; 10362306a36Sopenharmony_ci unsigned long flags; 10462306a36Sopenharmony_ci s64 now; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci spin_lock_irqsave(&ptp_priv->lock, flags); 10762306a36Sopenharmony_ci _rcar_gen4_ptp_gettime(ptp, &ts); 10862306a36Sopenharmony_ci now = ktime_to_ns(timespec64_to_ktime(ts)); 10962306a36Sopenharmony_ci ts = ns_to_timespec64(now + delta); 11062306a36Sopenharmony_ci _rcar_gen4_ptp_settime(ptp, &ts); 11162306a36Sopenharmony_ci spin_unlock_irqrestore(&ptp_priv->lock, flags); 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci return 0; 11462306a36Sopenharmony_ci} 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistatic int rcar_gen4_ptp_enable(struct ptp_clock_info *ptp, 11762306a36Sopenharmony_ci struct ptp_clock_request *rq, int on) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci return -EOPNOTSUPP; 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic struct ptp_clock_info rcar_gen4_ptp_info = { 12362306a36Sopenharmony_ci .owner = THIS_MODULE, 12462306a36Sopenharmony_ci .name = "rcar_gen4_ptp", 12562306a36Sopenharmony_ci .max_adj = 50000000, 12662306a36Sopenharmony_ci .adjfine = rcar_gen4_ptp_adjfine, 12762306a36Sopenharmony_ci .adjtime = rcar_gen4_ptp_adjtime, 12862306a36Sopenharmony_ci .gettime64 = rcar_gen4_ptp_gettime, 12962306a36Sopenharmony_ci .settime64 = rcar_gen4_ptp_settime, 13062306a36Sopenharmony_ci .enable = rcar_gen4_ptp_enable, 13162306a36Sopenharmony_ci}; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cistatic void rcar_gen4_ptp_set_offs(struct rcar_gen4_ptp_private *ptp_priv, 13462306a36Sopenharmony_ci enum rcar_gen4_ptp_reg_layout layout) 13562306a36Sopenharmony_ci{ 13662306a36Sopenharmony_ci WARN_ON(layout != RCAR_GEN4_PTP_REG_LAYOUT_S4); 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci ptp_priv->offs = &s4_offs; 13962306a36Sopenharmony_ci} 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ciint rcar_gen4_ptp_register(struct rcar_gen4_ptp_private *ptp_priv, 14262306a36Sopenharmony_ci enum rcar_gen4_ptp_reg_layout layout, u32 clock) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci if (ptp_priv->initialized) 14562306a36Sopenharmony_ci return 0; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci spin_lock_init(&ptp_priv->lock); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci rcar_gen4_ptp_set_offs(ptp_priv, layout); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci ptp_priv->default_addend = clock; 15262306a36Sopenharmony_ci iowrite32(ptp_priv->default_addend, ptp_priv->addr + ptp_priv->offs->increment); 15362306a36Sopenharmony_ci ptp_priv->clock = ptp_clock_register(&ptp_priv->info, NULL); 15462306a36Sopenharmony_ci if (IS_ERR(ptp_priv->clock)) 15562306a36Sopenharmony_ci return PTR_ERR(ptp_priv->clock); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci iowrite32(0x01, ptp_priv->addr + ptp_priv->offs->enable); 15862306a36Sopenharmony_ci ptp_priv->initialized = true; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci return 0; 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ciint rcar_gen4_ptp_unregister(struct rcar_gen4_ptp_private *ptp_priv) 16462306a36Sopenharmony_ci{ 16562306a36Sopenharmony_ci iowrite32(1, ptp_priv->addr + ptp_priv->offs->disable); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci return ptp_clock_unregister(ptp_priv->clock); 16862306a36Sopenharmony_ci} 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_cistruct rcar_gen4_ptp_private *rcar_gen4_ptp_alloc(struct platform_device *pdev) 17162306a36Sopenharmony_ci{ 17262306a36Sopenharmony_ci struct rcar_gen4_ptp_private *ptp; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci ptp = devm_kzalloc(&pdev->dev, sizeof(*ptp), GFP_KERNEL); 17562306a36Sopenharmony_ci if (!ptp) 17662306a36Sopenharmony_ci return NULL; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci ptp->info = rcar_gen4_ptp_info; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci return ptp; 18162306a36Sopenharmony_ci} 182