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