// 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 #include #include /* 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(const 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");