File Index
Files and directories
Sign in
FILE

rcar_gen4_ptp.c

File preview with syntax highlighting.

linux-6.19.14/drivers/net/ethernet/renesas/rcar_gen4_ptp.c C
  1// SPDX-License-Identifier: GPL-2.0
  2/* Renesas R-Car Gen4 gPTP device driver
  3 *
  4 * Copyright (C) 2022 Renesas Electronics Corporation
  5 */
  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
 16#define PTPTMEC_REG		0x0010
 17#define PTPTMDC_REG		0x0014
 18#define PTPTIVC0_REG		0x0020
 19#define PTPTOVC00_REG		0x0030
 20#define PTPTOVC10_REG		0x0034
 21#define PTPTOVC20_REG		0x0038
 22#define PTPGPTPTM00_REG		0x0050
 23#define PTPGPTPTM10_REG		0x0054
 24#define PTPGPTPTM20_REG		0x0058
 25
 26#define ptp_to_priv(ptp)	container_of(ptp, struct rcar_gen4_ptp_private, info)
 27
 28static int rcar_gen4_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
 29{
 30	struct rcar_gen4_ptp_private *ptp_priv = ptp_to_priv(ptp);
 31	s64 addend = ptp_priv->default_addend;
 32	bool neg_adj = scaled_ppm < 0;
 33	s64 diff;
 34
 35	if (neg_adj)
 36		scaled_ppm = -scaled_ppm;
 37	diff = div_s64(addend * scaled_ppm_to_ppb(scaled_ppm), NSEC_PER_SEC);
 38	addend = neg_adj ? addend - diff : addend + diff;
 39
 40	iowrite32(addend, ptp_priv->addr + PTPTIVC0_REG);
 41
 42	return 0;
 43}
 44
 45static void _rcar_gen4_ptp_gettime(struct ptp_clock_info *ptp,
 46				   struct timespec64 *ts)
 47{
 48	struct rcar_gen4_ptp_private *ptp_priv = ptp_to_priv(ptp);
 49
 50	lockdep_assert_held(&ptp_priv->lock);
 51
 52	ts->tv_nsec = ioread32(ptp_priv->addr + PTPGPTPTM00_REG);
 53	ts->tv_sec = ioread32(ptp_priv->addr + PTPGPTPTM10_REG) |
 54		     ((s64)ioread32(ptp_priv->addr + PTPGPTPTM20_REG) << 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 + PTPTMDC_REG);
 77	iowrite32(0, ptp_priv->addr + PTPTOVC20_REG);
 78	iowrite32(0, ptp_priv->addr + PTPTOVC10_REG);
 79	iowrite32(0, ptp_priv->addr + PTPTOVC00_REG);
 80	iowrite32(1, ptp_priv->addr + PTPTMEC_REG);
 81	iowrite32(ts->tv_sec >> 32, ptp_priv->addr + PTPTOVC20_REG);
 82	iowrite32(ts->tv_sec, ptp_priv->addr + PTPTOVC10_REG);
 83	iowrite32(ts->tv_nsec, ptp_priv->addr + PTPTOVC00_REG);
 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 s64 rcar_gen4_ptp_rate_to_increment(u32 rate)
134{
135	/* Timer increment in ns.
136	 * bit[31:27] - integer
137	 * bit[26:0]  - decimal
138	 * increment[ns] = perid[ns] * 2^27 => (1ns * 2^27) / rate[hz]
139	 */
140	return div_s64(1000000000LL << 27, rate);
141}
142
143int rcar_gen4_ptp_register(struct rcar_gen4_ptp_private *ptp_priv, u32 rate)
144{
145	if (ptp_priv->initialized)
146		return 0;
147
148	spin_lock_init(&ptp_priv->lock);
149
150	ptp_priv->default_addend = rcar_gen4_ptp_rate_to_increment(rate);
151	iowrite32(ptp_priv->default_addend, ptp_priv->addr + PTPTIVC0_REG);
152	ptp_priv->clock = ptp_clock_register(&ptp_priv->info, NULL);
153	if (IS_ERR(ptp_priv->clock))
154		return PTR_ERR(ptp_priv->clock);
155
156	iowrite32(0x01, ptp_priv->addr + PTPTMEC_REG);
157	ptp_priv->initialized = true;
158
159	return 0;
160}
161EXPORT_SYMBOL_GPL(rcar_gen4_ptp_register);
162
163int rcar_gen4_ptp_unregister(struct rcar_gen4_ptp_private *ptp_priv)
164{
165	iowrite32(1, ptp_priv->addr + PTPTMDC_REG);
166
167	return ptp_clock_unregister(ptp_priv->clock);
168}
169EXPORT_SYMBOL_GPL(rcar_gen4_ptp_unregister);
170
171struct rcar_gen4_ptp_private *rcar_gen4_ptp_alloc(struct platform_device *pdev)
172{
173	struct rcar_gen4_ptp_private *ptp;
174
175	ptp = devm_kzalloc(&pdev->dev, sizeof(*ptp), GFP_KERNEL);
176	if (!ptp)
177		return NULL;
178
179	ptp->info = rcar_gen4_ptp_info;
180
181	return ptp;
182}
183EXPORT_SYMBOL_GPL(rcar_gen4_ptp_alloc);
184
185MODULE_AUTHOR("Yoshihiro Shimoda");
186MODULE_DESCRIPTION("Renesas R-Car Gen4 gPTP driver");
187MODULE_LICENSE("GPL");
~/ Read-only file preview.