diff --git a/Kconfig b/Kconfig index 962a5255844..921cfc33e2f 100644 --- a/Kconfig +++ b/Kconfig @@ -2366,6 +2366,41 @@ config DEBUG_PCI_INFO endif # DEBUG_PCI +config DEBUG_PTP + bool "PTP Debug Features" + default n + depends on PTP_CLOCK + ---help--- + Enable PTP driver debug features. + + Support for this debug option is architecture-specific and may not + be available for some MCUs. + +if DEBUG_PTP + +config DEBUG_PTP_ERROR + bool "PTP Error Output" + default n + depends on DEBUG_ERROR + ---help--- + Enable PTP driver error output to SYSLOG. + +config DEBUG_PTP_WARN + bool "PTP Warnings Output" + default n + depends on DEBUG_WARN + ---help--- + Enable PTP driver warning output to SYSLOG. + +config DEBUG_PTP_INFO + bool "PTP Informational Output" + default n + depends on DEBUG_INFO + ---help--- + Enable PTP driver informational output to SYSLOG. + +endif # DEBUG_PTP + config DEBUG_RPMSG bool "RPMSG Debug Features" default n diff --git a/drivers/timers/CMakeLists.txt b/drivers/timers/CMakeLists.txt index 60fdda858e9..0b56569c697 100644 --- a/drivers/timers/CMakeLists.txt +++ b/drivers/timers/CMakeLists.txt @@ -90,4 +90,8 @@ if(CONFIG_GOLDFISH_TIMER) list(APPEND SRCS goldfish_timer.c) endif() +if(CONFIG_PTP_CLOCK) + list(APPEND SRCS ptp_clock.c) +endif() + target_sources(drivers PRIVATE ${SRCS}) diff --git a/drivers/timers/Kconfig b/drivers/timers/Kconfig index 9dcf3c11387..5317fb0a464 100644 --- a/drivers/timers/Kconfig +++ b/drivers/timers/Kconfig @@ -549,4 +549,20 @@ config GOLDFISH_TIMER emulator. It is used to provide a virtual timer to the guest OS. See: https://android.googlesource.com/platform/external/qemu/+/master/docs/GOLDFISH-VIRTUAL-HARDWARE.TXT +config PTP_CLOCK + bool "PTP clock driver" + default n + ---help--- + The IEEE 1588 standard defines a method to precisely + synchronize distributed clocks over Ethernet networks. The + standard defines a Precision Time Protocol (PTP), which can + be used to achieve synchronization within a few dozen + microseconds. In addition, with the help of special hardware + time stamping units, it can be possible to achieve + synchronization to within a few hundred nanoseconds. + + This driver adds support for PTP clocks as character + devices. If you want to use a PTP clock, then you should + also enable at least one clock driver as well. + endmenu # Timer Driver Support diff --git a/drivers/timers/Make.defs b/drivers/timers/Make.defs index ddf2620ffac..c8b6d21cdf4 100644 --- a/drivers/timers/Make.defs +++ b/drivers/timers/Make.defs @@ -124,6 +124,10 @@ ifeq ($(CONFIG_GOLDFISH_TIMER),y) TMRVPATH = :timers endif +ifeq ($(CONFIG_PTP_CLOCK),y) + CSRCS += ptp_clock.c +endif + # Include timer build support (if any were selected) DEPPATH += $(TMRDEPPATH) diff --git a/drivers/timers/ptp_clock.c b/drivers/timers/ptp_clock.c new file mode 100644 index 00000000000..8ad6b3ca42d --- /dev/null +++ b/drivers/timers/ptp_clock.c @@ -0,0 +1,476 @@ +/**************************************************************************** + * drivers/timers/ptp_clock.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include +#include + +#include +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* This structure describes the state of the upper half driver */ + +struct ptp_upperhalf_s +{ + FAR struct ptp_lowerhalf_s *lower; /* The handle of lower half driver */ + mutex_t lock; /* Manages exclusive access to file operations */ + long max_adj; /* The maximum frequency adjustment */ + long adj_freq; /* remembers the frequency adjustment */ +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static ssize_t ptp_clock_read(FAR struct file *filep, FAR char *buffer, + size_t buflen); +static int ptp_clock_ioctl(FAR struct file *filep, int cmd, + unsigned long arg); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct file_operations g_ptp_clock_file_ops = +{ + NULL, /* open */ + NULL, /* close */ + ptp_clock_read, /* read */ + NULL, /* write */ + NULL, /* seek */ + ptp_clock_ioctl, /* ioctl */ +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static ssize_t ptp_clock_read(FAR struct file *filep, FAR char *buffer, + size_t buflen) +{ + return 0; +} + +static inline long ptp_clock_scaled_ppm_to_ppb(long ppm) +{ + /* The 'freq' field in the 'struct timex' is in parts per + * million, but with a 16 bit binary fractional field. + * + * We want to calculate + * + * ppb = scaled_ppm * 1000 / 2^16 + * + * which simplifies to + * + * ppb = scaled_ppm * 125 / 2^13 + */ + + int64_t ppb = 1 + ppm; + + ppb *= 125; + ppb >>= 13; + return (long)ppb; +} + +static int ptp_clock_adjtime(FAR struct ptp_lowerhalf_s *lower, + FAR struct timex *tx) +{ + FAR struct ptp_upperhalf_s *upper = lower->upper; + int ret = -ENOTSUP; + + if (tx->modes & ADJ_SETOFFSET) + { + struct timespec ts; + int64_t delta; + + ts.tv_sec = tx->time.tv_sec; + ts.tv_nsec = tx->time.tv_usec; + if (!(tx->modes & ADJ_NANO)) + { + ts.tv_nsec *= 1000; + } + + if ((unsigned long)ts.tv_nsec >= NSEC_PER_SEC) + { + return -EINVAL; + } + + delta = ts.tv_sec * NSEC_PER_SEC + ts.tv_nsec; + ret = lower->ops->adjtime(lower, delta); + } + else if ((tx->modes & ADJ_FREQUENCY) && lower->ops->adjfine != NULL) + { + long ppb = ptp_clock_scaled_ppm_to_ppb(tx->freq); + + if (ppb > upper->max_adj || ppb < -upper->max_adj) + { + return -ERANGE; + } + + ret = lower->ops->adjfine(lower, tx->freq); + upper->adj_freq = tx->freq; + } + else if ((tx->modes & ADJ_OFFSET) && + lower->ops->adjphase != NULL) + { + int32_t offset = tx->offset; + + if (!(tx->modes & ADJ_NANO)) + { + offset *= NSEC_PER_USEC; + } + + ret = lower->ops->adjphase(lower, offset); + } + else if (tx->modes == 0) + { + tx->freq = upper->adj_freq; + ret = 0; + } + + return ret; +} + +static int ptp_clock_ioctl(FAR struct file *filep, int cmd, + unsigned long arg) +{ + FAR struct ptp_upperhalf_s *upper = filep->f_inode->i_private; + FAR struct ptp_lowerhalf_s *lower = upper->lower; + int ret = -ENOTTY; + int i; + + nxmutex_lock(&upper->lock); + switch (cmd) + { + case PTP_CLOCK_SETTIME: + { + ret = -ENOTSUP; + if (lower->ops->settime) + { + ret = lower->ops->settime(lower, + (FAR const struct timespec *)(uintptr_t)arg); + } + } + break; + + case PTP_CLOCK_GETTIME: + { + ret = -ENOTSUP; + if (lower->ops->gettime) + { + ret = lower->ops->gettime(lower, + (FAR struct timespec *)(uintptr_t)arg, NULL); + } + } + break; + + case PTP_CLOCK_GETRES: + { + ret = -ENOTSUP; + if (lower->ops->getres) + { + ret = lower->ops->getres(lower, + (FAR struct timespec *)(uintptr_t)arg); + } + } + break; + + case PTP_CLOCK_ADJTIME: + { + ret = ptp_clock_adjtime(lower, (FAR struct timex *)(uintptr_t)arg); + } + break; + + case PTP_CLOCK_GETCAPS: + case PTP_CLOCK_GETCAPS2: + { + FAR struct ptp_clock_caps *caps = (FAR struct ptp_clock_caps *) + (uintptr_t)arg; + + memset(caps, 0, sizeof(*caps)); + caps->max_adj = upper->max_adj; + caps->cross_timestamping = lower->ops->getcrosststamp != NULL; + caps->adjust_phase = lower->ops->adjphase != NULL; + ret = OK; + } + break; + + case PTP_SYS_OFFSET_PRECISE: + case PTP_SYS_OFFSET_PRECISE2: + { + FAR struct ptp_sys_offset_precise *preoff = + (FAR struct ptp_sys_offset_precise *)(uintptr_t)arg; + struct system_device_crosststamp xtstamp; + + if (!lower->ops->getcrosststamp) + { + ret = -ENOTSUP; + break; + } + + ret = lower->ops->getcrosststamp(lower, &xtstamp); + if (ret != 0) + { + break; + } + + memset(preoff, 0, sizeof(*preoff)); + preoff->device.sec = xtstamp.device.tv_sec; + preoff->device.nsec = xtstamp.device.tv_nsec; + preoff->sys_realtime.sec = xtstamp.realtime.tv_sec; + preoff->sys_realtime.nsec = xtstamp.realtime.tv_nsec; + preoff->sys_monoraw.sec = xtstamp.monoraw.tv_sec; + preoff->sys_monoraw.nsec = xtstamp.monoraw.tv_nsec; + } + break; + + case PTP_SYS_OFFSET_EXTENDED: + case PTP_SYS_OFFSET_EXTENDED2: + { + FAR struct ptp_sys_offset_extended *extoff = + (FAR struct ptp_sys_offset_extended *)(uintptr_t)arg; + struct ptp_system_timestamp sts; + struct timespec ts; + + if (!lower->ops->gettime) + { + ret = -ENOTSUP; + break; + } + + if (extoff->n_samples > PTP_MAX_SAMPLES || + extoff->rsv[0] || extoff->rsv[1] || extoff->rsv[2]) + { + ret = -EINVAL; + break; + } + + for (i = 0; i < extoff->n_samples; i++) + { + ret = lower->ops->gettime(lower, &ts, &sts); + if (ret < 0) + { + break; + } + + extoff->ts[i][0].sec = sts.pre_ts.tv_sec; + extoff->ts[i][0].nsec = sts.pre_ts.tv_nsec; + extoff->ts[i][1].sec = ts.tv_sec; + extoff->ts[i][1].nsec = ts.tv_nsec; + extoff->ts[i][2].sec = sts.post_ts.tv_sec; + extoff->ts[i][2].nsec = sts.post_ts.tv_nsec; + } + } + break; + + case PTP_SYS_OFFSET: + case PTP_SYS_OFFSET2: + { + FAR struct ptp_sys_offset *sysoff = + (FAR struct ptp_sys_offset *)(uintptr_t)arg; + FAR struct ptp_clock_time *pct; + struct timespec ts; + + if (lower->ops->gettime == NULL) + { + ret = -ENOTSUP; + break; + } + + if (sysoff->n_samples > PTP_MAX_SAMPLES) + { + ret = -EINVAL; + break; + } + + pct = &sysoff->ts[0]; + for (i = 0; i < sysoff->n_samples; i++) + { + nxclock_gettime(CLOCK_REALTIME, &ts); + pct->sec = ts.tv_sec; + pct->nsec = ts.tv_nsec; + pct++; + ret = lower->ops->gettime(lower, &ts, NULL); + if (ret < 0) + { + break; + } + + pct->sec = ts.tv_sec; + pct->nsec = ts.tv_nsec; + pct++; + } + + nxclock_gettime(CLOCK_REALTIME, &ts); + pct->sec = ts.tv_sec; + pct->nsec = ts.tv_nsec; + } + break; + + default: + break; + } + + nxmutex_unlock(&upper->lock); + return ret; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: ptp_clockid_to_filep + * + * Description: + * Convert clockid to struct filep. + * + ****************************************************************************/ + +int ptp_clockid_to_filep(clockid_t clock_id, FAR struct file **filep) +{ + FAR const struct file_operations *ops; + int ret; + + if ((clock_id & CLOCK_MASK) != CLOCK_FD) + { + return -EINVAL; + } + + ret = clock_id >> CLOCK_SHIFT; + if (ret >= 0) + { + ret = fs_getfilep(ret, filep); + } + + if (ret < 0) + { + return ret; + } + + ops = (*filep)->f_inode->u.i_ops; + if (ops != &g_ptp_clock_file_ops) + { + fs_putfilep(*filep); + return -EINVAL; + } + + return 0; +} + +/**************************************************************************** + * Name: ptp_clock_register + * + * Description: + * This function binds an instance of a "lower half" ptp driver with the + * "upper half" ptp device and registers that device so that can be used + * by application code. + * + * Input Parameters: + * lower - A pointer to an instance of lower half ptp driver. This + * instance is bound to the ptp driver and must persists as long + * as the driver persists. + * mxa_adj - The maximum frequency adjustment in parts per billion. + * devno - The user specifies number of device. ex: /dev/ptpX. + * + * Returned Value: + * OK if the driver was successfully register; A negated errno value is + * returned on any failure. + * + ****************************************************************************/ + +int ptp_clock_register(FAR struct ptp_lowerhalf_s *lower, int32_t max_adj, + int devno) +{ + FAR struct ptp_upperhalf_s *upper; + char path[16]; + int ret; + + DEBUGASSERT(lower != NULL); + + /* Allocate the upper-half data structure */ + + upper = kmm_zalloc(sizeof(struct ptp_upperhalf_s)); + if (!upper) + { + ptperr("ERROR: Allocation failed\n"); + return -ENOMEM; + } + + upper->lower = lower; + upper->max_adj = max_adj; + lower->upper = upper; + nxmutex_init(&upper->lock); + + snprintf(path, sizeof(path), "/dev/ptp%d", devno); + ptpinfo("Registering %s\n", path); + ret = register_driver(path, &g_ptp_clock_file_ops, 0666, upper); + if (ret < 0) + { + nxmutex_destroy(&upper->lock); + kmm_free(upper); + } + + return ret; +} + +/**************************************************************************** + * Name: ptp_clock_unregister + * + * Description: + * This function unregister character node and release all resource about + * upper half driver. + * + * Input Parameters: + * lower - A pointer to an instance of lower half ptp driver. This + * instance is bound to the ptp driver and must persists as long + * as the driver persists. + * devno - The user specifies which device of this type, from 0. + ****************************************************************************/ + +void ptp_clock_unregister(FAR struct ptp_lowerhalf_s *lower, int devno) +{ + FAR struct ptp_upperhalf_s *upper = lower->upper; + + if (upper != NULL) + { + char path[16]; + + snprintf(path, sizeof(path), "/dev/ptp%d", devno); + unregister_driver(path); + nxmutex_destroy(&upper->lock); + kmm_free(upper); + } +} diff --git a/include/debug.h b/include/debug.h index cc406d0a987..7c96eaf5994 100644 --- a/include/debug.h +++ b/include/debug.h @@ -1031,6 +1031,24 @@ # define csinfo _none #endif +#ifdef CONFIG_DEBUG_PTP_ERROR +# define ptperr _err +#else +# define ptperr _none +#endif + +#ifdef CONFIG_DEBUG_PTP_WARN +# define ptpwarn _warn +#else +# define ptpwarn _none +#endif + +#ifdef CONFIG_DEBUG_PTP_INFO +# define ptpinfo _info +#else +# define ptpinfo _none +#endif + /* Buffer dumping macros do not depend on varargs */ #ifdef CONFIG_DEBUG_ERROR diff --git a/include/nuttx/clock.h b/include/nuttx/clock.h index 140f3c39f08..60d2941e071 100644 --- a/include/nuttx/clock.h +++ b/include/nuttx/clock.h @@ -91,11 +91,15 @@ * CLOCK_PROCESS_CPUTIME_ID - 2 * CLOCK_THREAD_CPUTIME_ID - 3 * CLOCK_BOOTTIME - 4 - * bit 3~32: the pid or tid value + * CLOCK_FD - 5 + * + * if the clockid value exceeds CLOCK_MASK, it indicates a dynamic clockid. + * bit 3~32: the fd, pid or tid value * * The CLOCK_MASK are using to extract the clock_type from the clockid_t */ +#define CLOCK_FD 5 #define CLOCK_MASK 7 #define CLOCK_SHIFT 3 diff --git a/include/nuttx/fs/ioctl.h b/include/nuttx/fs/ioctl.h index 8ca85dc779b..a231b827dd0 100644 --- a/include/nuttx/fs/ioctl.h +++ b/include/nuttx/fs/ioctl.h @@ -112,6 +112,7 @@ #define _I2SOCBASE (0x4400) /* I2S driver ioctl commands */ #define _1WIREBASE (0x4500) /* 1WIRE ioctl commands */ #define _EEPIOCBASE (0x4600) /* EEPROM driver ioctl commands */ +#define _PTPBASE (0x4700) /* PTP ioctl commands */ #define _WLIOCBASE (0x8b00) /* Wireless modules ioctl network commands */ /* boardctl() commands share the same number space */ @@ -799,6 +800,13 @@ #define _EEPIOCVALID(c) (_IOC_TYPE(c)==_EEPIOCBASE) #define _EEPIOC(nr) _IOC(_EEPIOCBASE,nr) +/* PTP driver ioctl definitions *********************************************/ + +/* see nuttx/include/ptp_clock.h */ + +#define _PTPIOCVALID(c) (_IOC_TYPE(c)==_PTPBASE) +#define _PTPIOC(nr) _IOC(_PTPBASE,nr) + /**************************************************************************** * Public Type Definitions ****************************************************************************/ diff --git a/include/nuttx/timers/ptp_clock.h b/include/nuttx/timers/ptp_clock.h new file mode 100644 index 00000000000..3733abc6ec4 --- /dev/null +++ b/include/nuttx/timers/ptp_clock.h @@ -0,0 +1,355 @@ +/**************************************************************************** + * include/nuttx/timers/ptp_clock.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __INCLUDE_NUTTX_TIMERS_PTP_CLOCK_H +#define __INCLUDE_NUTTX_TIMERS_PTP_CLOCK_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define PTP_CLOCK_SETTIME _PTPIOC(0x1) +#define PTP_CLOCK_GETTIME _PTPIOC(0x2) +#define PTP_CLOCK_GETRES _PTPIOC(0x3) +#define PTP_CLOCK_ADJTIME _PTPIOC(0x4) + +#define PTP_CLOCK_GETCAPS _PTPIOC(0x5) +#define PTP_SYS_OFFSET _PTPIOC(0x6) +#define PTP_SYS_OFFSET_PRECISE _PTPIOC(0x7) +#define PTP_SYS_OFFSET_EXTENDED _PTPIOC(0x8) + +#define PTP_CLOCK_GETCAPS2 _PTPIOC(0x9) +#define PTP_SYS_OFFSET2 _PTPIOC(0xa) +#define PTP_SYS_OFFSET_PRECISE2 _PTPIOC(0xb) +#define PTP_SYS_OFFSET_EXTENDED2 _PTPIOC(0xc) + +/* Maximum allowed offset measurement samples. */ + +#define PTP_MAX_SAMPLES 25 + +/**************************************************************************** + * Inline Functions + ****************************************************************************/ + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* struct system_device_crosststamp - system/device cross-timestamp + * (synchronized capture) + */ + +struct system_device_crosststamp +{ + struct timespec device; /* Device time */ + struct timespec realtime; /* Realtime simultaneous with device time */ + struct timespec monoraw; /* Monotonic raw simultaneous with device time */ +}; + +/* struct ptp_clock_time - represents a time value + * + * The sign of the seconds field applies to the whole value. The + * nanoseconds field is always unsigned. The reserved field is + * included for sub-nanosecond resolution, should the demand for + * this ever appear. + */ + +struct ptp_clock_time +{ + int64_t sec; + uint32_t nsec; + uint32_t reserved; +}; + +struct ptp_clock_caps +{ + int max_adj; /* Maximum frequency adjustment in parts per billion. */ + int cross_timestamping; /* Whether the clock supports precise system-device cross timestamps */ + int adjust_phase; /* Whether the clock supports phase adjustment */ +}; + +struct ptp_sys_offset +{ + unsigned int n_samples; /* Desired number of measurements. */ + unsigned int rsv[3]; /* Reserved for future use. */ + + /* Array of interleaved system/phc time stamps. The kernel + * will provide 2*n_samples + 1 time stamps, with the last + * one as a system time stamp. + */ + + struct ptp_clock_time ts[2 * PTP_MAX_SAMPLES + 1]; +}; + +struct ptp_sys_offset_extended +{ + unsigned int n_samples; /* Desired number of measurements. */ + unsigned int rsv[3]; /* Reserved for future use. */ + + /* Array of [system, phc, system] time stamps. The kernel will provide + * 3*n_samples time stamps. + */ + + struct ptp_clock_time ts[PTP_MAX_SAMPLES][3]; +}; + +struct ptp_sys_offset_precise +{ + struct ptp_clock_time device; + struct ptp_clock_time sys_realtime; + struct ptp_clock_time sys_monoraw; + unsigned int rsv[4]; +}; + +/* struct ptp_system_timestamp - system time corresponding to a + * PHC timestamp. + */ + +struct ptp_system_timestamp +{ + struct timespec pre_ts; + struct timespec post_ts; +}; + +struct ptp_lowerhalf_s; +struct ptp_ops_s +{ + /************************************************************************** + * Name: adjfine + * + * Description: + * Adjusts the frequency of the hardware clock. + * + * Input Parameters: + * lower - The instance of lower half ptp driver + * scaled_ppm - Desired frequency offset from nominal frequency in parts + * per million, but with a 16 bit binary fractional field. + * + * Returned Value: + * Zero (OK) or positive on success; a negated errno value on failure. + * + **************************************************************************/ + + CODE int (*adjfine)(FAR struct ptp_lowerhalf_s *lower, long scaled_ppm); + + /************************************************************************** + * Name: adjphase + * + * Description: + * Adjusts the phase offset of the hardware clock. + * + * Input Parameters: + * lower - The instance of lower half ptp driver. + * phase - Desired change in nanoseconds. + * + * Returned Value: + * Zero (OK) or positive on success; a negated errno value on failure. + * + **************************************************************************/ + + CODE int (*adjphase)(FAR struct ptp_lowerhalf_s *lower, int32_t phase); + + /************************************************************************** + * Name: adjtime + * + * Description: + * Shifts the time of the hardware clock. + * + * Input Parameters: + * lower - The instance of lower half ptp driver. + * delta - Desired change in nanoseconds. + * + * Returned Value: + * Zero (OK) or positive on success; a negated errno value on failure. + * + **************************************************************************/ + + CODE int (*adjtime)(FAR struct ptp_lowerhalf_s *lower, int64_t delta); + + /************************************************************************** + * Name: gettime + * + * Description: + * Reads the current time from the hardware clock and optionally also + * also the system clock. The first reading is made right before reading + * the lowest bits of the PHC timestamp and the second reading + * immediately follows that. + * + * Input Parameters: + * lower - The instance of lower half ptp driver. + * ts - Holds the PHC timestamp. + * sts - If not NULL, it holds a pair of timestamps from the + * system clock. + * + * Returned Value: + * Zero (OK) or positive on success; a negated errno value on failure. + * + **************************************************************************/ + + CODE int (*gettime)(FAR struct ptp_lowerhalf_s *lower, + FAR struct timespec *ts, + FAR struct ptp_system_timestamp *sts); + + /************************************************************************** + * Name: getcrosststamp + * + * Description: + * Reads the current time from the hardware clock and system clock + * simultaneously. + * + * Input Parameters: + * lower - The instance of lower half ptp driver. + * cts - Contains timestamp (device,system) pair, where system time is + * realtime and monotonic. + * + * Returned Value: + * Zero (OK) or positive on success; a negated errno value on failure. + * + **************************************************************************/ + + CODE int (*getcrosststamp)(FAR struct ptp_lowerhalf_s *lower, + FAR struct system_device_crosststamp *cts); + + /************************************************************************** + * Name: settime + * + * Description: + * Set the current time on the hardware clock. + * + * Input Parameters: + * lower - The instance of lower half ptp driver + * ts - Time value to set. + * + * Returned Value: + * Zero (OK) or positive on success; a negated errno value on failure. + * + **************************************************************************/ + + CODE int (*settime)(FAR struct ptp_lowerhalf_s *lower, + FAR const struct timespec *ts); + + /************************************************************************** + * Name: getres + * + * Description: + * Finds the resolution (precision) of the specified clock, and, if res + * is non-NULL, stores it in the struct timespec pointed to by res. + * + * Input Parameters: + * pc - The instance of lower half ptp driver. + * res - Holds the resolution. + * + * Returned Value: + * Zero (OK) or positive on success; a negated errno value on failure. + * + **************************************************************************/ + + CODE int (*getres)(FAR struct ptp_lowerhalf_s *lower, + FAR struct timespec *res); +}; + +/* The ptp lower half driver interface, describes a PTP hardware + * clock driver. + */ + +struct ptp_lowerhalf_s +{ + FAR const struct ptp_ops_s *ops; /* Lower half driver operations. */ + FAR void *upper; /* The upper handle */ +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Name: ptp_clockid_to_filep + * + * Description: + * Convert clockid to struct filep. + * + ****************************************************************************/ + +int ptp_clockid_to_filep(clockid_t clock_id, FAR struct file **filep); + +/**************************************************************************** + * Name: ptp_clock_register + * + * Description: + * This function binds an instance of a "lower half" ptp driver with the + * "upper half" ptp device and registers that device so that can be used + * by application code. + * + * Input Parameters: + * lower - A pointer to an instance of lower half ptp driver. This + * instance is bound to the ptp driver and must persists as long + * as the driver persists. + * mxa_adj - The maximum frequency adjustment in parts per billion. + * devno - The user specifies number of device. ex: /dev/ptpX. + * + * Returned Value: + * OK if the driver was successfully register; A negated errno value is + * returned on any failure. + * + ****************************************************************************/ + +int ptp_clock_register(FAR struct ptp_lowerhalf_s *lower, int32_t max_adj, + int devno); + +/**************************************************************************** + * Name: ptp_clock_unregister + * + * Description: + * This function unregisters character node and releases all resource from + * upper half driver. This API corresponds to the ptp_register. + * + * Input Parameters: + * dev - A pointer to an instance of lower half ptp driver. This + * instance is bound to the ptp driver and must persists as long + * as the driver persists. + * devno - The user specifies which device of this type, from 0. + ****************************************************************************/ + +void ptp_clock_unregister(FAR struct ptp_lowerhalf_s *dev, int devno); + +#undef EXTERN +#if defined(__cplusplus) +} +#endif +#endif /* __INCLUDE_NUTTX_TIMERS_PTP_CLOCK_H */ diff --git a/include/sys/timex.h b/include/sys/timex.h new file mode 100644 index 00000000000..19418da7f94 --- /dev/null +++ b/include/sys/timex.h @@ -0,0 +1,98 @@ +/**************************************************************************** + * include/sys/timex.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __INCLUDE_SYS_TIMEX_H +#define __INCLUDE_SYS_TIMEX_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Mode codes (timex.mode) */ + +#define ADJ_OFFSET 0x0001 /* time offset */ +#define ADJ_FREQUENCY 0x0002 /* frequency offset */ +#define ADJ_MAXERROR 0x0004 /* maximum time error */ +#define ADJ_ESTERROR 0x0008 /* estimated time error */ +#define ADJ_STATUS 0x0010 /* clock status */ +#define ADJ_TIMECONST 0x0020 /* pll time constant */ +#define ADJ_TAI 0x0080 /* set TAI offset */ +#define ADJ_SETOFFSET 0x0100 /* add 'time' to current time */ +#define ADJ_MICRO 0x1000 /* select microsecond resolution */ +#define ADJ_NANO 0x2000 /* select nanosecond resolution */ +#define ADJ_TICK 0x4000 /* tick value */ +#define ADJ_OFFSET_SINGLESHOT 0x8001 /* old-fashioned adjtime */ +#define ADJ_OFFSET_SS_READ 0xa001 /* read-only adjtime */ + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +struct timex +{ + unsigned int modes; /* mode selector */ + long offset; /* time offset (usec) */ + long freq; /* frequency offset (scaled ppm) */ + long maxerror; /* maximum error (usec) */ + long esterror; /* estimated error (usec) */ + int status; /* clock command/status */ + long constant; /* pll time constant */ + long precision; /* clock precision (usec) (read only) */ + long tolerance; /* clock frequency tolerance (ppm) (read only) */ + struct timeval time; /* (read only, except for ADJ_SETOFFSET) */ + long tick; /* (modified) usecs between clock ticks */ + long ppsfreq; /* pps frequency (scaled ppm) (ro) */ + long jitter; /* pps jitter (us) (ro) */ + int shift; /* interval duration (s) (shift) (ro) */ + long stabil; /* pps stability (scaled ppm) (ro) */ + long jitcnt; /* jitter limit exceeded (ro) */ + long calcnt; /* calibration intervals (ro) */ + long errcnt; /* calibration errors (ro) */ + long stbcnt; /* stability limit exceeded (ro) */ + int tai; /* TAI offset (ro) */ +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +#undef EXTERN +#if defined(__cplusplus) +} +#endif + +#endif /* __INCLUDE_SYS_TIMEX_H */