Quelle virtio_rtc_ptp.c
Sprache: C
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Expose virtio_rtc clocks as PTP clocks .
*
* Copyright ( C ) 2022 - 2023 OpenSynergy GmbH
* Copyright ( c ) 2024 Qualcomm Innovation Center , Inc . All rights reserved .
*
* Derived from ptp_kvm_common . c , virtual PTP 1588 clock for use with KVM
* guests .
*
* Copyright ( C ) 2017 Red Hat Inc .
*/
#include <linux/device.h>
#include <linux/err.h>
#include <linux/ptp_clock_kernel.h>
#include <uapi/linux/virtio_rtc.h>
#include "virtio_rtc_internal.h"
/**
* struct viortc_ptp_clock - PTP clock abstraction
* @ ptp_clock : PTP clock handle for unregistering
* @ viortc : virtio_rtc device data
* @ ptp_info : PTP clock description
* @ vio_clk_id : virtio_rtc clock id
* @ have_cross : device supports crosststamp with available HW counter
*/
struct viortc_ptp_clock {
struct ptp_clock *ptp_clock;
struct viortc_dev *viortc;
struct ptp_clock_info ptp_info;
u16 vio_clk_id;
bool have_cross;
};
/**
* struct viortc_ptp_cross_ctx - context for get_device_system_crosststamp ( )
* @ device_time : device clock reading
* @ system_counterval : HW counter value at device_time
*
* Provides the already obtained crosststamp to get_device_system_crosststamp ( ) .
*/
struct viortc_ptp_cross_ctx {
ktime_t device_time;
struct system_counterval_t system_counterval;
};
/* Weak function in case get_device_system_crosststamp() is not supported */
int __weak viortc_hw_xtstamp_params(u8 *hw_counter, enum clocksource_ids *cs_id)
{
return -EOPNOTSUPP;
}
/**
* viortc_ptp_get_time_fn ( ) - callback for get_device_system_crosststamp ( )
* @ device_time : device clock reading
* @ system_counterval : HW counter value at device_time
* @ ctx : context with already obtained crosststamp
*
* Return : zero ( success ) .
*/
static int viortc_ptp_get_time_fn(ktime_t *device_time,
struct system_counterval_t *system_counterval,
void *ctx)
{
struct viortc_ptp_cross_ctx *vio_ctx = ctx;
*device_time = vio_ctx->device_time;
*system_counterval = vio_ctx->system_counterval;
return 0 ;
}
/**
* viortc_ptp_do_xtstamp ( ) - get crosststamp from device
* @ vio_ptp : virtio_rtc PTP clock
* @ hw_counter : virtio_rtc HW counter type
* @ cs_id : clocksource id corresponding to hw_counter
* @ ctx : context for get_device_system_crosststamp ( )
*
* Reads HW - specific crosststamp from device .
*
* Context : Process context .
* Return : Zero on success , negative error code otherwise .
*/
static int viortc_ptp_do_xtstamp(struct viortc_ptp_clock *vio_ptp,
u8 hw_counter, enum clocksource_ids cs_id,
struct viortc_ptp_cross_ctx *ctx)
{
u64 max_ns, ns;
int ret;
ctx->system_counterval.cs_id = cs_id;
ret = viortc_read_cross(vio_ptp->viortc, vio_ptp->vio_clk_id,
hw_counter, &ns,
&ctx->system_counterval.cycles);
if (ret)
return ret;
max_ns = (u64)ktime_to_ns(KTIME_MAX);
if (ns > max_ns)
return -EINVAL;
ctx->device_time = ns_to_ktime(ns);
return 0 ;
}
/*
* PTP clock operations
*/
/**
* viortc_ptp_getcrosststamp ( ) - PTP clock getcrosststamp op
* @ ptp : PTP clock info
* @ xtstamp : crosststamp
*
* Context : Process context .
* Return : Zero on success , negative error code otherwise .
*/
static int viortc_ptp_getcrosststamp(struct ptp_clock_info *ptp,
struct system_device_crosststamp *xtstamp)
{
struct viortc_ptp_clock *vio_ptp =
container_of(ptp, struct viortc_ptp_clock, ptp_info);
struct system_time_snapshot history_begin;
struct viortc_ptp_cross_ctx ctx;
enum clocksource_ids cs_id;
u8 hw_counter;
int ret;
if (!vio_ptp->have_cross)
return -EOPNOTSUPP;
ret = viortc_hw_xtstamp_params(&hw_counter, &cs_id);
if (ret)
return ret;
ktime_get_snapshot(&history_begin);
if (history_begin.cs_id != cs_id)
return -EOPNOTSUPP;
/*
* Getting the timestamp can take many milliseconds with a slow Virtio
* device . This is too long for viortc_ptp_get_time_fn ( ) passed to
* get_device_system_crosststamp ( ) , which has to usually return before
* the timekeeper seqcount increases ( every tick or so ) .
*
* So , get the actual cross - timestamp first .
*/
ret = viortc_ptp_do_xtstamp(vio_ptp, hw_counter, cs_id, &ctx);
if (ret)
return ret;
ret = get_device_system_crosststamp(viortc_ptp_get_time_fn, &ctx,
&history_begin, xtstamp);
if (ret)
pr_debug("%s: get_device_system_crosststamp() returned %d\n" ,
__func__, ret);
return ret;
}
/* viortc_ptp_adjfine() - unsupported PTP clock adjfine op */
static int viortc_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
{
return -EOPNOTSUPP;
}
/* viortc_ptp_adjtime() - unsupported PTP clock adjtime op */
static int viortc_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
{
return -EOPNOTSUPP;
}
/* viortc_ptp_settime64() - unsupported PTP clock settime64 op */
static int viortc_ptp_settime64(struct ptp_clock_info *ptp,
const struct timespec64 *ts)
{
return -EOPNOTSUPP;
}
/*
* viortc_ptp_gettimex64 ( ) - PTP clock gettimex64 op
*
* Context : Process context .
*/
static int viortc_ptp_gettimex64(struct ptp_clock_info *ptp,
struct timespec64 *ts,
struct ptp_system_timestamp *sts)
{
struct viortc_ptp_clock *vio_ptp =
container_of(ptp, struct viortc_ptp_clock, ptp_info);
int ret;
u64 ns;
ptp_read_system_prets(sts);
ret = viortc_read(vio_ptp->viortc, vio_ptp->vio_clk_id, &ns);
ptp_read_system_postts(sts);
if (ret)
return ret;
if (ns > (u64)S64_MAX)
return -EINVAL;
*ts = ns_to_timespec64((s64)ns);
return 0 ;
}
/* viortc_ptp_enable() - unsupported PTP clock enable op */
static int viortc_ptp_enable(struct ptp_clock_info *ptp,
struct ptp_clock_request *rq, int on)
{
return -EOPNOTSUPP;
}
/*
* viortc_ptp_info_template - ptp_clock_info template
*
* The . name member will be set for individual virtio_rtc PTP clocks .
*
* The . getcrosststamp member will be cleared for PTP clocks not supporting
* crosststamp .
*/
static const struct ptp_clock_info viortc_ptp_info_template = {
.owner = THIS_MODULE,
/* .name is set according to clock type */
.adjfine = viortc_ptp_adjfine,
.adjtime = viortc_ptp_adjtime,
.gettimex64 = viortc_ptp_gettimex64,
.settime64 = viortc_ptp_settime64,
.enable = viortc_ptp_enable,
.getcrosststamp = viortc_ptp_getcrosststamp,
};
/**
* viortc_ptp_unregister ( ) - PTP clock unregistering wrapper
* @ vio_ptp : virtio_rtc PTP clock
* @ parent_dev : parent device of PTP clock
*
* Return : Zero on success , negative error code otherwise .
*/
int viortc_ptp_unregister(struct viortc_ptp_clock *vio_ptp,
struct device *parent_dev)
{
int ret = ptp_clock_unregister(vio_ptp->ptp_clock);
if (!ret)
devm_kfree(parent_dev, vio_ptp);
return ret;
}
/**
* viortc_ptp_get_cross_cap ( ) - get xtstamp support info from device
* @ viortc : virtio_rtc device data
* @ vio_ptp : virtio_rtc PTP clock abstraction
*
* Context : Process context .
* Return : Zero on success , negative error code otherwise .
*/
static int viortc_ptp_get_cross_cap(struct viortc_dev *viortc,
struct viortc_ptp_clock *vio_ptp)
{
enum clocksource_ids cs_id;
bool xtstamp_supported;
u8 hw_counter;
int ret;
ret = viortc_hw_xtstamp_params(&hw_counter, &cs_id);
if (ret) {
vio_ptp->have_cross = false ;
return 0 ;
}
ret = viortc_cross_cap(viortc, vio_ptp->vio_clk_id, hw_counter,
&xtstamp_supported);
if (ret)
return ret;
vio_ptp->have_cross = xtstamp_supported;
return 0 ;
}
/**
* viortc_ptp_register ( ) - prepare and register PTP clock
* @ viortc : virtio_rtc device data
* @ parent_dev : parent device for PTP clock
* @ vio_clk_id : id of virtio_rtc clock which backs PTP clock
* @ ptp_clock_name : PTP clock name
*
* Context : Process context .
* Return : Pointer on success , ERR_PTR ( ) otherwise ; NULL if PTP clock support
* not available .
*/
struct viortc_ptp_clock *viortc_ptp_register(struct viortc_dev *viortc,
struct device *parent_dev,
u16 vio_clk_id,
const char *ptp_clock_name)
{
struct viortc_ptp_clock *vio_ptp;
struct ptp_clock *ptp_clock;
ssize_t len;
int ret;
vio_ptp = devm_kzalloc(parent_dev, sizeof (*vio_ptp), GFP_KERNEL);
if (!vio_ptp)
return ERR_PTR(-ENOMEM);
vio_ptp->viortc = viortc;
vio_ptp->vio_clk_id = vio_clk_id;
vio_ptp->ptp_info = viortc_ptp_info_template;
len = strscpy(vio_ptp->ptp_info.name, ptp_clock_name,
sizeof (vio_ptp->ptp_info.name));
if (len < 0 ) {
ret = len;
goto err_free_dev;
}
ret = viortc_ptp_get_cross_cap(viortc, vio_ptp);
if (ret)
goto err_free_dev;
if (!vio_ptp->have_cross)
vio_ptp->ptp_info.getcrosststamp = NULL;
ptp_clock = ptp_clock_register(&vio_ptp->ptp_info, parent_dev);
if (IS_ERR(ptp_clock))
goto err_on_register;
vio_ptp->ptp_clock = ptp_clock;
return vio_ptp;
err_on_register:
ret = PTR_ERR(ptp_clock);
err_free_dev:
devm_kfree(parent_dev, vio_ptp);
return ERR_PTR(ret);
}
Messung V0.5 in Prozent C=95 H=96 G=95
¤ Dauer der Verarbeitung: 0.21 Sekunden
(vorverarbeitet am 2026-09-29)
¤
*© Formatika GbR, Deutschland
2026-10-10
Neuigkeiten
Aktuelles
Motto des Tages
Open Source Software
Jenseits des Üblichen ....
Besucherstatistik
Statistik
#Sources=1126864
#Domains=1897691