182 lines
4.9 KiB
C
182 lines
4.9 KiB
C
|
// SPDX-License-Identifier: GPL-2.0
|
||
|
/* Renesas R-Car Gen4 gPTP device driver
|
||
|
*
|
||
|
* Copyright (C) 2022 Renesas Electronics Corporation
|
||
|
*/
|
||
|
|
||
|
#include <linux/err.h>
|
||
|
#include <linux/etherdevice.h>
|
||
|
#include <linux/kernel.h>
|
||
|
#include <linux/module.h>
|
||
|
#include <linux/platform_device.h>
|
||
|
#include <linux/slab.h>
|
||
|
|
||
|
#include "rcar_gen4_ptp.h"
|
||
|
#define ptp_to_priv(ptp) container_of(ptp, struct rcar_gen4_ptp_private, info)
|
||
|
|
||
|
static const struct rcar_gen4_ptp_reg_offset s4_offs = {
|
||
|
.enable = PTPTMEC,
|
||
|
.disable = PTPTMDC,
|
||
|
.increment = PTPTIVC0,
|
||
|
.config_t0 = PTPTOVC00,
|
||
|
.config_t1 = PTPTOVC10,
|
||
|
.config_t2 = PTPTOVC20,
|
||
|
.monitor_t0 = PTPGPTPTM00,
|
||
|
.monitor_t1 = PTPGPTPTM10,
|
||
|
.monitor_t2 = PTPGPTPTM20,
|
||
|
};
|
||
|
|
||
|
static int rcar_gen4_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
|
||
|
{
|
||
|
struct rcar_gen4_ptp_private *ptp_priv = ptp_to_priv(ptp);
|
||
|
bool neg_adj = scaled_ppm < 0 ? true : false;
|
||
|
s64 addend = ptp_priv->default_addend;
|
||
|
s64 diff;
|
||
|
|
||
|
if (neg_adj)
|
||
|
scaled_ppm = -scaled_ppm;
|
||
|
diff = div_s64(addend * scaled_ppm_to_ppb(scaled_ppm), NSEC_PER_SEC);
|
||
|
addend = neg_adj ? addend - diff : addend + diff;
|
||
|
|
||
|
iowrite32(addend, ptp_priv->addr + ptp_priv->offs->increment);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/* Caller must hold the lock */
|
||
|
static void _rcar_gen4_ptp_gettime(struct ptp_clock_info *ptp,
|
||
|
struct timespec64 *ts)
|
||
|
{
|
||
|
struct rcar_gen4_ptp_private *ptp_priv = ptp_to_priv(ptp);
|
||
|
|
||
|
ts->tv_nsec = ioread32(ptp_priv->addr + ptp_priv->offs->monitor_t0);
|
||
|
ts->tv_sec = ioread32(ptp_priv->addr + ptp_priv->offs->monitor_t1) |
|
||
|
((s64)ioread32(ptp_priv->addr + ptp_priv->offs->monitor_t2) << 32);
|
||
|
}
|
||
|
|
||
|
static int rcar_gen4_ptp_gettime(struct ptp_clock_info *ptp,
|
||
|
struct timespec64 *ts)
|
||
|
{
|
||
|
struct rcar_gen4_ptp_private *ptp_priv = ptp_to_priv(ptp);
|
||
|
unsigned long flags;
|
||
|
|
||
|
spin_lock_irqsave(&ptp_priv->lock, flags);
|
||
|
_rcar_gen4_ptp_gettime(ptp, ts);
|
||
|
spin_unlock_irqrestore(&ptp_priv->lock, flags);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/* Caller must hold the lock */
|
||
|
static void _rcar_gen4_ptp_settime(struct ptp_clock_info *ptp,
|
||
|
const struct timespec64 *ts)
|
||
|
{
|
||
|
struct rcar_gen4_ptp_private *ptp_priv = ptp_to_priv(ptp);
|
||
|
|
||
|
iowrite32(1, ptp_priv->addr + ptp_priv->offs->disable);
|
||
|
iowrite32(0, ptp_priv->addr + ptp_priv->offs->config_t2);
|
||
|
iowrite32(0, ptp_priv->addr + ptp_priv->offs->config_t1);
|
||
|
iowrite32(0, ptp_priv->addr + ptp_priv->offs->config_t0);
|
||
|
iowrite32(1, ptp_priv->addr + ptp_priv->offs->enable);
|
||
|
iowrite32(ts->tv_sec >> 32, ptp_priv->addr + ptp_priv->offs->config_t2);
|
||
|
iowrite32(ts->tv_sec, ptp_priv->addr + ptp_priv->offs->config_t1);
|
||
|
iowrite32(ts->tv_nsec, ptp_priv->addr + ptp_priv->offs->config_t0);
|
||
|
}
|
||
|
|
||
|
static int rcar_gen4_ptp_settime(struct ptp_clock_info *ptp,
|
||
|
const struct timespec64 *ts)
|
||
|
{
|
||
|
struct rcar_gen4_ptp_private *ptp_priv = ptp_to_priv(ptp);
|
||
|
unsigned long flags;
|
||
|
|
||
|
spin_lock_irqsave(&ptp_priv->lock, flags);
|
||
|
_rcar_gen4_ptp_settime(ptp, ts);
|
||
|
spin_unlock_irqrestore(&ptp_priv->lock, flags);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int rcar_gen4_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
|
||
|
{
|
||
|
struct rcar_gen4_ptp_private *ptp_priv = ptp_to_priv(ptp);
|
||
|
struct timespec64 ts;
|
||
|
unsigned long flags;
|
||
|
s64 now;
|
||
|
|
||
|
spin_lock_irqsave(&ptp_priv->lock, flags);
|
||
|
_rcar_gen4_ptp_gettime(ptp, &ts);
|
||
|
now = ktime_to_ns(timespec64_to_ktime(ts));
|
||
|
ts = ns_to_timespec64(now + delta);
|
||
|
_rcar_gen4_ptp_settime(ptp, &ts);
|
||
|
spin_unlock_irqrestore(&ptp_priv->lock, flags);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int rcar_gen4_ptp_enable(struct ptp_clock_info *ptp,
|
||
|
struct ptp_clock_request *rq, int on)
|
||
|
{
|
||
|
return -EOPNOTSUPP;
|
||
|
}
|
||
|
|
||
|
static struct ptp_clock_info rcar_gen4_ptp_info = {
|
||
|
.owner = THIS_MODULE,
|
||
|
.name = "rcar_gen4_ptp",
|
||
|
.max_adj = 50000000,
|
||
|
.adjfine = rcar_gen4_ptp_adjfine,
|
||
|
.adjtime = rcar_gen4_ptp_adjtime,
|
||
|
.gettime64 = rcar_gen4_ptp_gettime,
|
||
|
.settime64 = rcar_gen4_ptp_settime,
|
||
|
.enable = rcar_gen4_ptp_enable,
|
||
|
};
|
||
|
|
||
|
static void rcar_gen4_ptp_set_offs(struct rcar_gen4_ptp_private *ptp_priv,
|
||
|
enum rcar_gen4_ptp_reg_layout layout)
|
||
|
{
|
||
|
WARN_ON(layout != RCAR_GEN4_PTP_REG_LAYOUT_S4);
|
||
|
|
||
|
ptp_priv->offs = &s4_offs;
|
||
|
}
|
||
|
|
||
|
int rcar_gen4_ptp_register(struct rcar_gen4_ptp_private *ptp_priv,
|
||
|
enum rcar_gen4_ptp_reg_layout layout, u32 clock)
|
||
|
{
|
||
|
if (ptp_priv->initialized)
|
||
|
return 0;
|
||
|
|
||
|
spin_lock_init(&ptp_priv->lock);
|
||
|
|
||
|
rcar_gen4_ptp_set_offs(ptp_priv, layout);
|
||
|
|
||
|
ptp_priv->default_addend = clock;
|
||
|
iowrite32(ptp_priv->default_addend, ptp_priv->addr + ptp_priv->offs->increment);
|
||
|
ptp_priv->clock = ptp_clock_register(&ptp_priv->info, NULL);
|
||
|
if (IS_ERR(ptp_priv->clock))
|
||
|
return PTR_ERR(ptp_priv->clock);
|
||
|
|
||
|
iowrite32(0x01, ptp_priv->addr + ptp_priv->offs->enable);
|
||
|
ptp_priv->initialized = true;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int rcar_gen4_ptp_unregister(struct rcar_gen4_ptp_private *ptp_priv)
|
||
|
{
|
||
|
iowrite32(1, ptp_priv->addr + ptp_priv->offs->disable);
|
||
|
|
||
|
return ptp_clock_unregister(ptp_priv->clock);
|
||
|
}
|
||
|
|
||
|
struct rcar_gen4_ptp_private *rcar_gen4_ptp_alloc(struct platform_device *pdev)
|
||
|
{
|
||
|
struct rcar_gen4_ptp_private *ptp;
|
||
|
|
||
|
ptp = devm_kzalloc(&pdev->dev, sizeof(*ptp), GFP_KERNEL);
|
||
|
if (!ptp)
|
||
|
return NULL;
|
||
|
|
||
|
ptp->info = rcar_gen4_ptp_info;
|
||
|
|
||
|
return ptp;
|
||
|
}
|