162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright 2023 NXP 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Mock-up PTP Hardware Clock driver for virtual network devices 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Create a PTP clock which offers PTP time manipulation operations 862306a36Sopenharmony_ci * using a timecounter/cyclecounter on top of CLOCK_MONOTONIC_RAW. 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/ptp_clock_kernel.h> 1262306a36Sopenharmony_ci#include <linux/ptp_mock.h> 1362306a36Sopenharmony_ci#include <linux/timecounter.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci/* Clamp scaled_ppm between -2,097,152,000 and 2,097,152,000, 1662306a36Sopenharmony_ci * and thus "adj" between -68,719,476 and 68,719,476 1762306a36Sopenharmony_ci */ 1862306a36Sopenharmony_ci#define MOCK_PHC_MAX_ADJ_PPB 32000000 1962306a36Sopenharmony_ci/* Timestamps from ktime_get_raw() have 1 ns resolution, so the scale factor 2062306a36Sopenharmony_ci * (MULT >> SHIFT) needs to be 1. Pick SHIFT as 31 bits, which translates 2162306a36Sopenharmony_ci * MULT(freq 0) into 0x80000000. 2262306a36Sopenharmony_ci */ 2362306a36Sopenharmony_ci#define MOCK_PHC_CC_SHIFT 31 2462306a36Sopenharmony_ci#define MOCK_PHC_CC_MULT (1 << MOCK_PHC_CC_SHIFT) 2562306a36Sopenharmony_ci#define MOCK_PHC_FADJ_SHIFT 9 2662306a36Sopenharmony_ci#define MOCK_PHC_FADJ_DENOMINATOR 15625ULL 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci/* The largest cycle_delta that timecounter_read_delta() can handle without a 2962306a36Sopenharmony_ci * 64-bit overflow during the multiplication with cc->mult, given the max "adj" 3062306a36Sopenharmony_ci * we permit, is ~8.3 seconds. Make sure readouts are more frequent than that. 3162306a36Sopenharmony_ci */ 3262306a36Sopenharmony_ci#define MOCK_PHC_REFRESH_INTERVAL (HZ * 5) 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#define info_to_phc(d) container_of((d), struct mock_phc, info) 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistruct mock_phc { 3762306a36Sopenharmony_ci struct ptp_clock_info info; 3862306a36Sopenharmony_ci struct ptp_clock *clock; 3962306a36Sopenharmony_ci struct timecounter tc; 4062306a36Sopenharmony_ci struct cyclecounter cc; 4162306a36Sopenharmony_ci spinlock_t lock; 4262306a36Sopenharmony_ci}; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistatic u64 mock_phc_cc_read(const struct cyclecounter *cc) 4562306a36Sopenharmony_ci{ 4662306a36Sopenharmony_ci return ktime_get_raw_ns(); 4762306a36Sopenharmony_ci} 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic int mock_phc_adjfine(struct ptp_clock_info *info, long scaled_ppm) 5062306a36Sopenharmony_ci{ 5162306a36Sopenharmony_ci struct mock_phc *phc = info_to_phc(info); 5262306a36Sopenharmony_ci s64 adj; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci adj = (s64)scaled_ppm << MOCK_PHC_FADJ_SHIFT; 5562306a36Sopenharmony_ci adj = div_s64(adj, MOCK_PHC_FADJ_DENOMINATOR); 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci spin_lock(&phc->lock); 5862306a36Sopenharmony_ci timecounter_read(&phc->tc); 5962306a36Sopenharmony_ci phc->cc.mult = MOCK_PHC_CC_MULT + adj; 6062306a36Sopenharmony_ci spin_unlock(&phc->lock); 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci return 0; 6362306a36Sopenharmony_ci} 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_cistatic int mock_phc_adjtime(struct ptp_clock_info *info, s64 delta) 6662306a36Sopenharmony_ci{ 6762306a36Sopenharmony_ci struct mock_phc *phc = info_to_phc(info); 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci spin_lock(&phc->lock); 7062306a36Sopenharmony_ci timecounter_adjtime(&phc->tc, delta); 7162306a36Sopenharmony_ci spin_unlock(&phc->lock); 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci return 0; 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistatic int mock_phc_settime64(struct ptp_clock_info *info, 7762306a36Sopenharmony_ci const struct timespec64 *ts) 7862306a36Sopenharmony_ci{ 7962306a36Sopenharmony_ci struct mock_phc *phc = info_to_phc(info); 8062306a36Sopenharmony_ci u64 ns = timespec64_to_ns(ts); 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci spin_lock(&phc->lock); 8362306a36Sopenharmony_ci timecounter_init(&phc->tc, &phc->cc, ns); 8462306a36Sopenharmony_ci spin_unlock(&phc->lock); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci return 0; 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic int mock_phc_gettime64(struct ptp_clock_info *info, struct timespec64 *ts) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci struct mock_phc *phc = info_to_phc(info); 9262306a36Sopenharmony_ci u64 ns; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci spin_lock(&phc->lock); 9562306a36Sopenharmony_ci ns = timecounter_read(&phc->tc); 9662306a36Sopenharmony_ci spin_unlock(&phc->lock); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci *ts = ns_to_timespec64(ns); 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci return 0; 10162306a36Sopenharmony_ci} 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_cistatic long mock_phc_refresh(struct ptp_clock_info *info) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci struct timespec64 ts; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci mock_phc_gettime64(info, &ts); 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci return MOCK_PHC_REFRESH_INTERVAL; 11062306a36Sopenharmony_ci} 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ciint mock_phc_index(struct mock_phc *phc) 11362306a36Sopenharmony_ci{ 11462306a36Sopenharmony_ci return ptp_clock_index(phc->clock); 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mock_phc_index); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_cistruct mock_phc *mock_phc_create(struct device *dev) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci struct mock_phc *phc; 12162306a36Sopenharmony_ci int err; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci phc = kzalloc(sizeof(*phc), GFP_KERNEL); 12462306a36Sopenharmony_ci if (!phc) { 12562306a36Sopenharmony_ci err = -ENOMEM; 12662306a36Sopenharmony_ci goto out; 12762306a36Sopenharmony_ci } 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci phc->info = (struct ptp_clock_info) { 13062306a36Sopenharmony_ci .owner = THIS_MODULE, 13162306a36Sopenharmony_ci .name = "Mock-up PTP clock", 13262306a36Sopenharmony_ci .max_adj = MOCK_PHC_MAX_ADJ_PPB, 13362306a36Sopenharmony_ci .adjfine = mock_phc_adjfine, 13462306a36Sopenharmony_ci .adjtime = mock_phc_adjtime, 13562306a36Sopenharmony_ci .gettime64 = mock_phc_gettime64, 13662306a36Sopenharmony_ci .settime64 = mock_phc_settime64, 13762306a36Sopenharmony_ci .do_aux_work = mock_phc_refresh, 13862306a36Sopenharmony_ci }; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci phc->cc = (struct cyclecounter) { 14162306a36Sopenharmony_ci .read = mock_phc_cc_read, 14262306a36Sopenharmony_ci .mask = CYCLECOUNTER_MASK(64), 14362306a36Sopenharmony_ci .mult = MOCK_PHC_CC_MULT, 14462306a36Sopenharmony_ci .shift = MOCK_PHC_CC_SHIFT, 14562306a36Sopenharmony_ci }; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci spin_lock_init(&phc->lock); 14862306a36Sopenharmony_ci timecounter_init(&phc->tc, &phc->cc, 0); 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci phc->clock = ptp_clock_register(&phc->info, dev); 15162306a36Sopenharmony_ci if (IS_ERR(phc->clock)) { 15262306a36Sopenharmony_ci err = PTR_ERR(phc->clock); 15362306a36Sopenharmony_ci goto out_free_phc; 15462306a36Sopenharmony_ci } 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci ptp_schedule_worker(phc->clock, MOCK_PHC_REFRESH_INTERVAL); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci return phc; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ciout_free_phc: 16162306a36Sopenharmony_ci kfree(phc); 16262306a36Sopenharmony_ciout: 16362306a36Sopenharmony_ci return ERR_PTR(err); 16462306a36Sopenharmony_ci} 16562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mock_phc_create); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_civoid mock_phc_destroy(struct mock_phc *phc) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci ptp_clock_unregister(phc->clock); 17062306a36Sopenharmony_ci kfree(phc); 17162306a36Sopenharmony_ci} 17262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mock_phc_destroy); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ciMODULE_DESCRIPTION("Mock-up PTP Hardware Clock driver"); 17562306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 176