2021-10-21 14:55:48 -07:00
|
|
|
// SPDX-License-Identifier: BSD-2-Clause-Views
|
2019-07-17 18:13:51 -07:00
|
|
|
/*
|
2023-06-26 11:44:57 -07:00
|
|
|
* Copyright (c) 2019-2023 The Regents of the University of California
|
2021-10-21 14:55:48 -07:00
|
|
|
*/
|
2019-07-17 18:13:51 -07:00
|
|
|
|
|
|
|
#include "mqnic.h"
|
|
|
|
#include <linux/version.h>
|
|
|
|
|
2021-10-08 18:31:53 -07:00
|
|
|
ktime_t mqnic_read_cpl_ts(struct mqnic_dev *mdev, struct mqnic_ring *ring,
|
2021-10-21 13:54:00 -07:00
|
|
|
const struct mqnic_cpl *cpl)
|
2019-07-17 18:13:51 -07:00
|
|
|
{
|
2021-10-08 18:31:53 -07:00
|
|
|
u64 ts_s = le16_to_cpu(cpl->ts_s);
|
|
|
|
u32 ts_ns = le32_to_cpu(cpl->ts_ns);
|
2019-07-17 18:13:51 -07:00
|
|
|
|
2021-10-08 18:31:53 -07:00
|
|
|
if (unlikely(!ring->ts_valid || (ring->ts_s ^ ts_s) & 0xff00)) {
|
|
|
|
// seconds MSBs do not match, update cached timestamp
|
2021-12-29 22:31:46 -08:00
|
|
|
if (mdev->phc_rb) {
|
2023-11-07 21:57:07 -08:00
|
|
|
ring->ts_s = ioread32(mdev->phc_rb->regs + MQNIC_RB_PHC_REG_CUR_TOD_SEC_L);
|
|
|
|
ring->ts_s |= (u64) ioread32(mdev->phc_rb->regs + MQNIC_RB_PHC_REG_CUR_TOD_SEC_H) << 32;
|
2021-12-29 22:31:46 -08:00
|
|
|
ring->ts_valid = 1;
|
|
|
|
}
|
2021-10-08 18:31:53 -07:00
|
|
|
}
|
2019-07-17 18:13:51 -07:00
|
|
|
|
2021-10-08 18:31:53 -07:00
|
|
|
ts_s |= ring->ts_s & 0xffffffffffffff00;
|
2019-07-17 18:13:51 -07:00
|
|
|
|
2021-10-08 18:31:53 -07:00
|
|
|
return ktime_set(ts_s, ts_ns);
|
2019-07-17 18:13:51 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static int mqnic_phc_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
|
|
|
|
{
|
2021-10-08 18:31:53 -07:00
|
|
|
struct mqnic_dev *mdev = container_of(ptp, struct mqnic_dev, ptp_clock_info);
|
2019-07-17 18:13:51 -07:00
|
|
|
|
2021-10-08 18:31:53 -07:00
|
|
|
bool neg = false;
|
|
|
|
u64 nom_per_fns, adj;
|
2019-07-17 18:13:51 -07:00
|
|
|
|
2023-07-07 01:10:49 -07:00
|
|
|
dev_dbg(mdev->dev, "%s: scaled_ppm: %ld", __func__, scaled_ppm);
|
2019-07-17 18:13:51 -07:00
|
|
|
|
2021-10-08 18:31:53 -07:00
|
|
|
if (scaled_ppm < 0) {
|
|
|
|
neg = true;
|
|
|
|
scaled_ppm = -scaled_ppm;
|
|
|
|
}
|
2019-07-17 18:13:51 -07:00
|
|
|
|
2021-12-29 22:31:46 -08:00
|
|
|
nom_per_fns = ioread32(mdev->phc_rb->regs + MQNIC_RB_PHC_REG_NOM_PERIOD_FNS);
|
2022-04-11 10:55:07 -07:00
|
|
|
nom_per_fns |= (u64) ioread32(mdev->phc_rb->regs + MQNIC_RB_PHC_REG_NOM_PERIOD_NS) << 32;
|
2019-07-17 18:13:51 -07:00
|
|
|
|
2021-10-08 18:31:53 -07:00
|
|
|
if (nom_per_fns == 0)
|
|
|
|
nom_per_fns = 0x4ULL << 32;
|
2019-07-17 18:13:51 -07:00
|
|
|
|
2021-10-08 18:31:53 -07:00
|
|
|
adj = div_u64(((nom_per_fns >> 16) * scaled_ppm) + 500000, 1000000);
|
2019-07-17 18:13:51 -07:00
|
|
|
|
2021-10-08 18:31:53 -07:00
|
|
|
if (neg)
|
|
|
|
adj = nom_per_fns - adj;
|
|
|
|
else
|
|
|
|
adj = nom_per_fns + adj;
|
2019-07-17 18:13:51 -07:00
|
|
|
|
2021-12-29 22:31:46 -08:00
|
|
|
iowrite32(adj & 0xffffffff, mdev->phc_rb->regs + MQNIC_RB_PHC_REG_PERIOD_FNS);
|
|
|
|
iowrite32(adj >> 32, mdev->phc_rb->regs + MQNIC_RB_PHC_REG_PERIOD_NS);
|
2019-07-17 18:13:51 -07:00
|
|
|
|
2023-07-07 01:10:49 -07:00
|
|
|
dev_dbg(mdev->dev, "%s adj: 0x%llx", __func__, adj);
|
2019-07-17 18:13:51 -07:00
|
|
|
|
2021-10-08 18:31:53 -07:00
|
|
|
return 0;
|
2019-07-17 18:13:51 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static int mqnic_phc_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts)
|
|
|
|
{
|
2021-10-08 18:31:53 -07:00
|
|
|
struct mqnic_dev *mdev = container_of(ptp, struct mqnic_dev, ptp_clock_info);
|
2019-07-17 18:13:51 -07:00
|
|
|
|
2023-11-07 21:57:07 -08:00
|
|
|
ioread32(mdev->phc_rb->regs + MQNIC_RB_PHC_REG_SNAP_FNS);
|
|
|
|
ts->tv_nsec = ioread32(mdev->phc_rb->regs + MQNIC_RB_PHC_REG_SNAP_TOD_NS);
|
|
|
|
ts->tv_sec = ioread32(mdev->phc_rb->regs + MQNIC_RB_PHC_REG_SNAP_TOD_SEC_L);
|
|
|
|
ts->tv_sec |= (u64) ioread32(mdev->phc_rb->regs + MQNIC_RB_PHC_REG_SNAP_TOD_SEC_H) << 32;
|
2019-07-17 18:13:51 -07:00
|
|
|
|
2021-10-08 18:31:53 -07:00
|
|
|
return 0;
|
2019-07-17 18:13:51 -07:00
|
|
|
}
|
|
|
|
|
2021-10-08 18:31:53 -07:00
|
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0)
|
|
|
|
static int mqnic_phc_gettimex(struct ptp_clock_info *ptp, struct timespec64 *ts,
|
2021-10-21 13:54:00 -07:00
|
|
|
struct ptp_system_timestamp *sts)
|
2019-07-17 18:13:51 -07:00
|
|
|
{
|
2021-10-08 18:31:53 -07:00
|
|
|
struct mqnic_dev *mdev = container_of(ptp, struct mqnic_dev, ptp_clock_info);
|
2019-07-17 18:13:51 -07:00
|
|
|
|
2021-10-08 18:31:53 -07:00
|
|
|
ptp_read_system_prets(sts);
|
2023-11-07 21:57:07 -08:00
|
|
|
ioread32(mdev->phc_rb->regs + MQNIC_RB_PHC_REG_SNAP_FNS);
|
2021-10-08 18:31:53 -07:00
|
|
|
ptp_read_system_postts(sts);
|
2023-11-07 21:57:07 -08:00
|
|
|
ts->tv_nsec = ioread32(mdev->phc_rb->regs + MQNIC_RB_PHC_REG_SNAP_TOD_NS);
|
|
|
|
ts->tv_sec = ioread32(mdev->phc_rb->regs + MQNIC_RB_PHC_REG_SNAP_TOD_SEC_L);
|
|
|
|
ts->tv_sec |= (u64) ioread32(mdev->phc_rb->regs + MQNIC_RB_PHC_REG_SNAP_TOD_SEC_H) << 32;
|
2019-07-17 18:13:51 -07:00
|
|
|
|
2021-10-08 18:31:53 -07:00
|
|
|
return 0;
|
2019-07-17 18:13:51 -07:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static int mqnic_phc_settime(struct ptp_clock_info *ptp, const struct timespec64 *ts)
|
|
|
|
{
|
2021-10-08 18:31:53 -07:00
|
|
|
struct mqnic_dev *mdev = container_of(ptp, struct mqnic_dev, ptp_clock_info);
|
2019-07-17 18:13:51 -07:00
|
|
|
|
2023-11-07 21:57:07 -08:00
|
|
|
iowrite32(ts->tv_nsec, mdev->phc_rb->regs + MQNIC_RB_PHC_REG_SET_TOD_NS);
|
|
|
|
iowrite32(ts->tv_sec & 0xffffffff, mdev->phc_rb->regs + MQNIC_RB_PHC_REG_SET_TOD_SEC_L);
|
|
|
|
iowrite32(ts->tv_sec >> 32, mdev->phc_rb->regs + MQNIC_RB_PHC_REG_SET_TOD_SEC_H);
|
2019-07-17 18:13:51 -07:00
|
|
|
|
2021-10-08 18:31:53 -07:00
|
|
|
return 0;
|
2019-07-17 18:13:51 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static int mqnic_phc_adjtime(struct ptp_clock_info *ptp, s64 delta)
|
|
|
|
{
|
2021-10-08 18:31:53 -07:00
|
|
|
struct mqnic_dev *mdev = container_of(ptp, struct mqnic_dev, ptp_clock_info);
|
|
|
|
struct timespec64 ts;
|
|
|
|
|
2023-07-07 01:10:49 -07:00
|
|
|
dev_dbg(mdev->dev, "%s: delta: %lld", __func__, delta);
|
2021-10-08 18:31:53 -07:00
|
|
|
|
2023-11-07 21:57:07 -08:00
|
|
|
if (delta > 536000000 || delta < -536000000) {
|
2021-10-08 18:31:53 -07:00
|
|
|
mqnic_phc_gettime(ptp, &ts);
|
|
|
|
ts = timespec64_add(ts, ns_to_timespec64(delta));
|
|
|
|
mqnic_phc_settime(ptp, &ts);
|
|
|
|
} else {
|
2023-11-07 21:57:07 -08:00
|
|
|
iowrite32(delta & 0xffffffff, mdev->phc_rb->regs + MQNIC_RB_PHC_REG_OFFSET_TOD_NS);
|
2021-10-08 18:31:53 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2019-07-17 18:13:51 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static int mqnic_phc_perout(struct ptp_clock_info *ptp, int on, struct ptp_perout_request *perout)
|
|
|
|
{
|
2021-10-08 18:31:53 -07:00
|
|
|
struct mqnic_dev *mdev = container_of(ptp, struct mqnic_dev, ptp_clock_info);
|
2022-04-24 23:01:15 -07:00
|
|
|
struct mqnic_reg_block *rb;
|
2019-07-17 18:13:51 -07:00
|
|
|
|
2021-10-08 18:31:53 -07:00
|
|
|
u64 start_sec, period_sec, width_sec;
|
|
|
|
u32 start_nsec, period_nsec, width_nsec;
|
2019-07-17 18:13:51 -07:00
|
|
|
|
2022-04-24 23:01:15 -07:00
|
|
|
rb = mqnic_find_reg_block(mdev->rb_list, MQNIC_RB_PHC_PEROUT_TYPE,
|
2021-12-29 22:31:46 -08:00
|
|
|
MQNIC_RB_PHC_PEROUT_VER, perout->index);
|
2019-07-17 18:13:51 -07:00
|
|
|
|
2021-12-29 22:31:46 -08:00
|
|
|
if (!rb)
|
|
|
|
return -EINVAL;
|
2019-07-17 18:13:51 -07:00
|
|
|
|
2021-10-08 18:31:53 -07:00
|
|
|
if (!on) {
|
2021-12-29 22:31:46 -08:00
|
|
|
iowrite32(0, rb->regs + MQNIC_RB_PHC_PEROUT_REG_CTRL);
|
2019-07-17 18:13:51 -07:00
|
|
|
|
2021-10-08 18:31:53 -07:00
|
|
|
return 0;
|
|
|
|
}
|
2019-07-17 18:13:51 -07:00
|
|
|
|
2021-10-08 18:31:53 -07:00
|
|
|
start_nsec = perout->start.nsec;
|
|
|
|
start_sec = start_nsec / NSEC_PER_SEC;
|
|
|
|
start_nsec -= start_sec * NSEC_PER_SEC;
|
|
|
|
start_sec += perout->start.sec;
|
2019-07-17 18:13:51 -07:00
|
|
|
|
2021-10-08 18:31:53 -07:00
|
|
|
period_nsec = perout->period.nsec;
|
|
|
|
period_sec = period_nsec / NSEC_PER_SEC;
|
|
|
|
period_nsec -= period_sec * NSEC_PER_SEC;
|
|
|
|
period_sec += perout->period.sec;
|
2019-07-17 18:13:51 -07:00
|
|
|
|
2021-10-08 18:31:53 -07:00
|
|
|
// set width to half of period
|
|
|
|
width_sec = period_sec >> 1;
|
|
|
|
width_nsec = (period_nsec + (period_sec & 1 ? NSEC_PER_SEC : 0)) >> 1;
|
2019-07-17 18:13:51 -07:00
|
|
|
|
2021-10-21 14:44:05 -07:00
|
|
|
dev_info(mdev->dev, "%s: start: %lld.%09d", __func__, start_sec, start_nsec);
|
|
|
|
dev_info(mdev->dev, "%s: period: %lld.%09d", __func__, period_sec, period_nsec);
|
|
|
|
dev_info(mdev->dev, "%s: width: %lld.%09d", __func__, width_sec, width_nsec);
|
2019-07-17 18:13:51 -07:00
|
|
|
|
2021-12-29 22:31:46 -08:00
|
|
|
iowrite32(0, rb->regs + MQNIC_RB_PHC_PEROUT_REG_START_FNS);
|
|
|
|
iowrite32(start_nsec, rb->regs + MQNIC_RB_PHC_PEROUT_REG_START_NS);
|
|
|
|
iowrite32(start_sec & 0xffffffff, rb->regs + MQNIC_RB_PHC_PEROUT_REG_START_SEC_L);
|
|
|
|
iowrite32(start_sec >> 32, rb->regs + MQNIC_RB_PHC_PEROUT_REG_START_SEC_H);
|
2019-07-17 18:13:51 -07:00
|
|
|
|
2021-12-29 22:31:46 -08:00
|
|
|
iowrite32(0, rb->regs + MQNIC_RB_PHC_PEROUT_REG_PERIOD_FNS);
|
|
|
|
iowrite32(period_nsec, rb->regs + MQNIC_RB_PHC_PEROUT_REG_PERIOD_NS);
|
|
|
|
iowrite32(period_sec & 0xffffffff, rb->regs + MQNIC_RB_PHC_PEROUT_REG_PERIOD_SEC_L);
|
|
|
|
iowrite32(period_sec >> 32, rb->regs + MQNIC_RB_PHC_PEROUT_REG_PERIOD_SEC_H);
|
2019-07-17 18:13:51 -07:00
|
|
|
|
2021-12-29 22:31:46 -08:00
|
|
|
iowrite32(0, rb->regs + MQNIC_RB_PHC_PEROUT_REG_WIDTH_FNS);
|
|
|
|
iowrite32(width_nsec, rb->regs + MQNIC_RB_PHC_PEROUT_REG_WIDTH_NS);
|
|
|
|
iowrite32(width_sec & 0xffffffff, rb->regs + MQNIC_RB_PHC_PEROUT_REG_WIDTH_SEC_L);
|
|
|
|
iowrite32(width_sec >> 32, rb->regs + MQNIC_RB_PHC_PEROUT_REG_WIDTH_SEC_H);
|
2019-07-17 18:13:51 -07:00
|
|
|
|
2021-12-29 22:31:46 -08:00
|
|
|
iowrite32(1, rb->regs + MQNIC_RB_PHC_PEROUT_REG_CTRL);
|
2019-07-17 18:13:51 -07:00
|
|
|
|
2021-10-08 18:31:53 -07:00
|
|
|
return 0;
|
2019-07-17 18:13:51 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static int mqnic_phc_enable(struct ptp_clock_info *ptp, struct ptp_clock_request *request, int on)
|
|
|
|
{
|
2021-10-08 18:31:53 -07:00
|
|
|
if (!request)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
switch (request->type) {
|
|
|
|
case PTP_CLK_REQ_EXTTS:
|
|
|
|
return -EINVAL;
|
|
|
|
case PTP_CLK_REQ_PEROUT:
|
|
|
|
return mqnic_phc_perout(ptp, on, &request->perout);
|
|
|
|
case PTP_CLK_REQ_PPS:
|
|
|
|
return -EINVAL;
|
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2019-07-17 18:13:51 -07:00
|
|
|
}
|
|
|
|
|
2021-01-13 20:09:09 -08:00
|
|
|
static void mqnic_phc_set_from_system_clock(struct ptp_clock_info *ptp)
|
2019-07-17 18:13:51 -07:00
|
|
|
{
|
2021-10-08 18:31:53 -07:00
|
|
|
struct timespec64 ts;
|
2019-07-17 18:13:51 -07:00
|
|
|
|
2021-10-08 18:31:53 -07:00
|
|
|
#ifdef ktime_get_clocktai_ts64
|
|
|
|
ktime_get_clocktai_ts64(&ts);
|
2019-07-17 18:13:51 -07:00
|
|
|
#else
|
2021-10-08 18:31:53 -07:00
|
|
|
ts = ktime_to_timespec64(ktime_get_clocktai());
|
2019-07-17 18:13:51 -07:00
|
|
|
#endif
|
|
|
|
|
2021-10-08 18:31:53 -07:00
|
|
|
mqnic_phc_settime(ptp, &ts);
|
2019-07-17 18:13:51 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void mqnic_register_phc(struct mqnic_dev *mdev)
|
|
|
|
{
|
2021-12-29 22:31:46 -08:00
|
|
|
int perout_ch_count = 0;
|
2022-04-24 23:01:15 -07:00
|
|
|
struct mqnic_reg_block *rb;
|
2021-12-29 22:31:46 -08:00
|
|
|
|
|
|
|
if (!mdev->phc_rb) {
|
2022-04-16 23:23:20 -07:00
|
|
|
dev_warn(mdev->dev, "PTP clock not present");
|
2021-12-29 22:31:46 -08:00
|
|
|
return;
|
|
|
|
}
|
2021-10-08 18:31:53 -07:00
|
|
|
|
|
|
|
if (mdev->ptp_clock) {
|
|
|
|
dev_warn(mdev->dev, "PTP clock already registered");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-04-16 23:23:20 -07:00
|
|
|
// count PTP period output channels
|
2022-04-24 23:01:15 -07:00
|
|
|
while ((rb = mqnic_find_reg_block(mdev->rb_list, MQNIC_RB_PHC_PEROUT_TYPE,
|
2021-12-29 22:31:46 -08:00
|
|
|
MQNIC_RB_PHC_PEROUT_VER, perout_ch_count))) {
|
|
|
|
perout_ch_count++;
|
|
|
|
}
|
2021-10-08 18:31:53 -07:00
|
|
|
|
|
|
|
mdev->ptp_clock_info.owner = THIS_MODULE;
|
2022-02-02 18:41:19 -08:00
|
|
|
snprintf(mdev->ptp_clock_info.name, sizeof(mdev->ptp_clock_info.name),
|
|
|
|
"%s_ptp", mdev->name);
|
2021-10-08 18:31:53 -07:00
|
|
|
mdev->ptp_clock_info.max_adj = 100000000;
|
|
|
|
mdev->ptp_clock_info.n_alarm = 0;
|
|
|
|
mdev->ptp_clock_info.n_ext_ts = 0;
|
2021-12-29 22:31:46 -08:00
|
|
|
mdev->ptp_clock_info.n_per_out = perout_ch_count;
|
2021-10-08 18:31:53 -07:00
|
|
|
mdev->ptp_clock_info.n_pins = 0;
|
|
|
|
mdev->ptp_clock_info.pps = 0;
|
|
|
|
mdev->ptp_clock_info.adjfine = mqnic_phc_adjfine;
|
|
|
|
mdev->ptp_clock_info.adjtime = mqnic_phc_adjtime;
|
|
|
|
mdev->ptp_clock_info.gettime64 = mqnic_phc_gettime;
|
|
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0)
|
|
|
|
mdev->ptp_clock_info.gettimex64 = mqnic_phc_gettimex;
|
2019-07-17 18:13:51 -07:00
|
|
|
#endif
|
2021-10-08 18:31:53 -07:00
|
|
|
mdev->ptp_clock_info.settime64 = mqnic_phc_settime;
|
|
|
|
mdev->ptp_clock_info.enable = mqnic_phc_enable;
|
|
|
|
mdev->ptp_clock = ptp_clock_register(&mdev->ptp_clock_info, mdev->dev);
|
|
|
|
|
|
|
|
if (IS_ERR(mdev->ptp_clock)) {
|
2022-04-16 23:23:20 -07:00
|
|
|
dev_err(mdev->dev, "%s: failed to register PHC (%ld)", __func__, PTR_ERR(mdev->ptp_clock));
|
2021-10-08 18:31:53 -07:00
|
|
|
mdev->ptp_clock = NULL;
|
2022-04-16 23:23:20 -07:00
|
|
|
return;
|
2021-10-08 18:31:53 -07:00
|
|
|
}
|
2022-04-16 23:23:20 -07:00
|
|
|
|
|
|
|
dev_info(mdev->dev, "registered PHC (index %d)", ptp_clock_index(mdev->ptp_clock));
|
|
|
|
|
|
|
|
mqnic_phc_set_from_system_clock(&mdev->ptp_clock_info);
|
2019-07-17 18:13:51 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void mqnic_unregister_phc(struct mqnic_dev *mdev)
|
|
|
|
{
|
2021-10-08 18:31:53 -07:00
|
|
|
if (mdev->ptp_clock) {
|
|
|
|
ptp_clock_unregister(mdev->ptp_clock);
|
|
|
|
mdev->ptp_clock = NULL;
|
|
|
|
dev_info(mdev->dev, "unregistered PHC");
|
|
|
|
}
|
2019-07-17 18:13:51 -07:00
|
|
|
}
|