Skip to content

Commit

Permalink
net: ethernet: renesas: rswitch: Add R-Car Gen4 gPTP support
Browse files Browse the repository at this point in the history
Add R-Car Gen4 gPTP support into the rswitch driver.

Signed-off-by: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Yoshihiro Shimoda authored and David S. Miller committed Nov 2, 2022
1 parent 3590918 commit 6c6fa1a
Show file tree
Hide file tree
Showing 5 changed files with 398 additions and 2 deletions.
2 changes: 1 addition & 1 deletion drivers/net/ethernet/renesas/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@ ravb-objs := ravb_main.o ravb_ptp.o

obj-$(CONFIG_RAVB) += ravb.o

rswitch_drv-objs := rswitch.o
rswitch_drv-objs := rswitch.o rcar_gen4_ptp.o

obj-$(CONFIG_RENESAS_ETHER_SWITCH) += rswitch_drv.o
181 changes: 181 additions & 0 deletions drivers/net/ethernet/renesas/rcar_gen4_ptp.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
// 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;
}
72 changes: 72 additions & 0 deletions drivers/net/ethernet/renesas/rcar_gen4_ptp.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Renesas R-Car Gen4 gPTP device driver
*
* Copyright (C) 2022 Renesas Electronics Corporation
*/

#ifndef __RCAR_GEN4_PTP_H__
#define __RCAR_GEN4_PTP_H__

#include <linux/ptp_clock_kernel.h>

#define PTPTIVC_INIT 0x19000000 /* 320MHz */
#define RCAR_GEN4_PTP_CLOCK_S4 PTPTIVC_INIT
#define RCAR_GEN4_GPTP_OFFSET_S4 0x00018000

/* for rcar_gen4_ptp_init */
enum rcar_gen4_ptp_reg_layout {
RCAR_GEN4_PTP_REG_LAYOUT_S4
};

/* driver's definitions */
#define RCAR_GEN4_RXTSTAMP_ENABLED BIT(0)
#define RCAR_GEN4_RXTSTAMP_TYPE_V2_L2_EVENT BIT(1)
#define RCAR_GEN4_RXTSTAMP_TYPE_ALL (RCAR_GEN4_RXTSTAMP_TYPE_V2_L2_EVENT | BIT(2))
#define RCAR_GEN4_RXTSTAMP_TYPE RCAR_GEN4_RXTSTAMP_TYPE_ALL

#define RCAR_GEN4_TXTSTAMP_ENABLED BIT(0)

#define PTPRO 0

enum rcar_gen4_ptp_reg_s4 {
PTPTMEC = PTPRO + 0x0010,
PTPTMDC = PTPRO + 0x0014,
PTPTIVC0 = PTPRO + 0x0020,
PTPTOVC00 = PTPRO + 0x0030,
PTPTOVC10 = PTPRO + 0x0034,
PTPTOVC20 = PTPRO + 0x0038,
PTPGPTPTM00 = PTPRO + 0x0050,
PTPGPTPTM10 = PTPRO + 0x0054,
PTPGPTPTM20 = PTPRO + 0x0058,
};

struct rcar_gen4_ptp_reg_offset {
u16 enable;
u16 disable;
u16 increment;
u16 config_t0;
u16 config_t1;
u16 config_t2;
u16 monitor_t0;
u16 monitor_t1;
u16 monitor_t2;
};

struct rcar_gen4_ptp_private {
void __iomem *addr;
struct ptp_clock *clock;
struct ptp_clock_info info;
const struct rcar_gen4_ptp_reg_offset *offs;
spinlock_t lock; /* For multiple registers access */
u32 tstamp_tx_ctrl;
u32 tstamp_rx_ctrl;
s64 default_addend;
bool initialized;
};

int rcar_gen4_ptp_register(struct rcar_gen4_ptp_private *ptp_priv,
enum rcar_gen4_ptp_reg_layout layout, u32 clock);
int rcar_gen4_ptp_unregister(struct rcar_gen4_ptp_private *ptp_priv);
struct rcar_gen4_ptp_private *rcar_gen4_ptp_alloc(struct platform_device *pdev);

#endif /* #ifndef __RCAR_GEN4_PTP_H__ */
Loading

0 comments on commit 6c6fa1a

Please sign in to comment.