mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-08-05 16:54:27 +00:00

In both the read callback for struct cyclecounter, and in struct timecounter, struct cyclecounter is declared as a const pointer. Unfortunatly, a number of users of this pointer treat it as a non-const pointer as it is burried in a larger structure that is heavily modified by the callback function when accessed. This lie had been hidden by the fact that container_of() "casts away" a const attribute of a pointer without any compiler warning happening at all. Fix this all up by removing the const attribute in the needed places so that everyone can see that the structure really isn't const, but can, and is, modified by the users of it. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Link: https://lore.kernel.org/all/2025070124-backyard-hurt-783a@gregkh
175 lines
4 KiB
C
175 lines
4 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* Copyright 2023 NXP
|
|
*
|
|
* Mock-up PTP Hardware Clock driver for virtual network devices
|
|
*
|
|
* Create a PTP clock which offers PTP time manipulation operations
|
|
* using a timecounter/cyclecounter on top of CLOCK_MONOTONIC_RAW.
|
|
*/
|
|
|
|
#include <linux/ptp_clock_kernel.h>
|
|
#include <linux/ptp_mock.h>
|
|
#include <linux/timecounter.h>
|
|
|
|
/* Clamp scaled_ppm between -2,097,152,000 and 2,097,152,000,
|
|
* and thus "adj" between -68,719,476 and 68,719,476
|
|
*/
|
|
#define MOCK_PHC_MAX_ADJ_PPB 32000000
|
|
/* Timestamps from ktime_get_raw() have 1 ns resolution, so the scale factor
|
|
* (MULT >> SHIFT) needs to be 1. Pick SHIFT as 31 bits, which translates
|
|
* MULT(freq 0) into 0x80000000.
|
|
*/
|
|
#define MOCK_PHC_CC_SHIFT 31
|
|
#define MOCK_PHC_CC_MULT (1 << MOCK_PHC_CC_SHIFT)
|
|
#define MOCK_PHC_FADJ_SHIFT 9
|
|
#define MOCK_PHC_FADJ_DENOMINATOR 15625ULL
|
|
|
|
/* The largest cycle_delta that timecounter_read_delta() can handle without a
|
|
* 64-bit overflow during the multiplication with cc->mult, given the max "adj"
|
|
* we permit, is ~8.3 seconds. Make sure readouts are more frequent than that.
|
|
*/
|
|
#define MOCK_PHC_REFRESH_INTERVAL (HZ * 5)
|
|
|
|
#define info_to_phc(d) container_of((d), struct mock_phc, info)
|
|
|
|
struct mock_phc {
|
|
struct ptp_clock_info info;
|
|
struct ptp_clock *clock;
|
|
struct timecounter tc;
|
|
struct cyclecounter cc;
|
|
spinlock_t lock;
|
|
};
|
|
|
|
static u64 mock_phc_cc_read(struct cyclecounter *cc)
|
|
{
|
|
return ktime_get_raw_ns();
|
|
}
|
|
|
|
static int mock_phc_adjfine(struct ptp_clock_info *info, long scaled_ppm)
|
|
{
|
|
struct mock_phc *phc = info_to_phc(info);
|
|
s64 adj;
|
|
|
|
adj = (s64)scaled_ppm << MOCK_PHC_FADJ_SHIFT;
|
|
adj = div_s64(adj, MOCK_PHC_FADJ_DENOMINATOR);
|
|
|
|
spin_lock(&phc->lock);
|
|
timecounter_read(&phc->tc);
|
|
phc->cc.mult = MOCK_PHC_CC_MULT + adj;
|
|
spin_unlock(&phc->lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mock_phc_adjtime(struct ptp_clock_info *info, s64 delta)
|
|
{
|
|
struct mock_phc *phc = info_to_phc(info);
|
|
|
|
spin_lock(&phc->lock);
|
|
timecounter_adjtime(&phc->tc, delta);
|
|
spin_unlock(&phc->lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mock_phc_settime64(struct ptp_clock_info *info,
|
|
const struct timespec64 *ts)
|
|
{
|
|
struct mock_phc *phc = info_to_phc(info);
|
|
u64 ns = timespec64_to_ns(ts);
|
|
|
|
spin_lock(&phc->lock);
|
|
timecounter_init(&phc->tc, &phc->cc, ns);
|
|
spin_unlock(&phc->lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mock_phc_gettime64(struct ptp_clock_info *info, struct timespec64 *ts)
|
|
{
|
|
struct mock_phc *phc = info_to_phc(info);
|
|
u64 ns;
|
|
|
|
spin_lock(&phc->lock);
|
|
ns = timecounter_read(&phc->tc);
|
|
spin_unlock(&phc->lock);
|
|
|
|
*ts = ns_to_timespec64(ns);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static long mock_phc_refresh(struct ptp_clock_info *info)
|
|
{
|
|
struct timespec64 ts;
|
|
|
|
mock_phc_gettime64(info, &ts);
|
|
|
|
return MOCK_PHC_REFRESH_INTERVAL;
|
|
}
|
|
|
|
int mock_phc_index(struct mock_phc *phc)
|
|
{
|
|
return ptp_clock_index(phc->clock);
|
|
}
|
|
EXPORT_SYMBOL_GPL(mock_phc_index);
|
|
|
|
struct mock_phc *mock_phc_create(struct device *dev)
|
|
{
|
|
struct mock_phc *phc;
|
|
int err;
|
|
|
|
phc = kzalloc(sizeof(*phc), GFP_KERNEL);
|
|
if (!phc) {
|
|
err = -ENOMEM;
|
|
goto out;
|
|
}
|
|
|
|
phc->info = (struct ptp_clock_info) {
|
|
.owner = THIS_MODULE,
|
|
.name = "Mock-up PTP clock",
|
|
.max_adj = MOCK_PHC_MAX_ADJ_PPB,
|
|
.adjfine = mock_phc_adjfine,
|
|
.adjtime = mock_phc_adjtime,
|
|
.gettime64 = mock_phc_gettime64,
|
|
.settime64 = mock_phc_settime64,
|
|
.do_aux_work = mock_phc_refresh,
|
|
};
|
|
|
|
phc->cc = (struct cyclecounter) {
|
|
.read = mock_phc_cc_read,
|
|
.mask = CYCLECOUNTER_MASK(64),
|
|
.mult = MOCK_PHC_CC_MULT,
|
|
.shift = MOCK_PHC_CC_SHIFT,
|
|
};
|
|
|
|
spin_lock_init(&phc->lock);
|
|
timecounter_init(&phc->tc, &phc->cc, 0);
|
|
|
|
phc->clock = ptp_clock_register(&phc->info, dev);
|
|
if (IS_ERR(phc->clock)) {
|
|
err = PTR_ERR(phc->clock);
|
|
goto out_free_phc;
|
|
}
|
|
|
|
ptp_schedule_worker(phc->clock, MOCK_PHC_REFRESH_INTERVAL);
|
|
|
|
return phc;
|
|
|
|
out_free_phc:
|
|
kfree(phc);
|
|
out:
|
|
return ERR_PTR(err);
|
|
}
|
|
EXPORT_SYMBOL_GPL(mock_phc_create);
|
|
|
|
void mock_phc_destroy(struct mock_phc *phc)
|
|
{
|
|
ptp_clock_unregister(phc->clock);
|
|
kfree(phc);
|
|
}
|
|
EXPORT_SYMBOL_GPL(mock_phc_destroy);
|
|
|
|
MODULE_DESCRIPTION("Mock-up PTP Hardware Clock driver");
|
|
MODULE_LICENSE("GPL");
|