-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
net: ethernet: renesas: rswitch: Add R-Car Gen4 gPTP support
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
Showing
5 changed files
with
398 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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__ */ |
Oops, something went wrong.