brintos

brintos / linux-shallow public Read only

0
0
Text · 5.5 KiB · 72e7fcc Raw
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