Current kernel code do not support the dynamic posix clock alarm set. This code would support it by the posix timer structure.
319 const struct k_clock clock_posix_dynamic = { 320 .clock_getres = pc_clock_getres, 321 .clock_set = pc_clock_settime, 322 .clock_get = pc_clock_gettime, 323 .clock_adj = pc_clock_adjtime, 324 + .timer_create = pc_timer_create, 325 + .timer_del = pc_timer_delete, 326 + .timer_set = pc_timer_set, 327 + .timer_arm = pc_timer_arm, } This won't change the user space system call code. Normally the user space set alarm by timer_create() and timer_settime(). Reference code are tools/testing/selftests/ptp/testptp.c. Some case requiring providing the alarm set for user space by ptp clock. Signed-off-by: Po Liu <po....@nxp.com> --- drivers/net/ethernet/freescale/enetc/enetc_ptp.c | 1 + drivers/ptp/ptp_clock.c | 39 ++++++++++++++- drivers/ptp/ptp_qoriq.c | 44 +++++++++++++++++ include/linux/fsl/ptp_qoriq.h | 3 ++ include/linux/posix-clock.h | 3 +- include/linux/ptp_clock_kernel.h | 5 +- kernel/time/posix-clock.c | 60 ++++++++++++++++++++++++ 7 files changed, 152 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/freescale/enetc/enetc_ptp.c b/drivers/net/ethernet/freescale/enetc/enetc_ptp.c index 8c1497e..35e2f2a 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc_ptp.c +++ b/drivers/net/ethernet/freescale/enetc/enetc_ptp.c @@ -21,6 +21,7 @@ .gettime64 = ptp_qoriq_gettime, .settime64 = ptp_qoriq_settime, .enable = ptp_qoriq_enable, + .alarm = ptp_qoriq_alarm, }; static int enetc_ptp_probe(struct pci_dev *pdev, diff --git a/drivers/ptp/ptp_clock.c b/drivers/ptp/ptp_clock.c index 79bd102..72d06a8 100644 --- a/drivers/ptp/ptp_clock.c +++ b/drivers/ptp/ptp_clock.c @@ -23,7 +23,9 @@ #include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> +#include <linux/sched/task.h> #include <linux/posix-clock.h> +#include <linux/posix-timers.h> #include <linux/pps_kernel.h> #include <linux/slab.h> #include <linux/syscalls.h> @@ -166,12 +168,31 @@ static int ptp_clock_adjtime(struct posix_clock *pc, struct __kernel_timex *tx) return err; } +static int ptp_clock_alarm(struct posix_clock *pc, ktime_t expires, + bool absolute, struct k_itimer *timr) +{ + struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock); + struct ptp_clock_info *ops; + + ops = ptp->info; + if (!ops) + return -EINVAL; + + if (!ops->alarm) + return -EINVAL; + + ops->alarm(ops, expires, absolute, timr); + + return 0; +} + static struct posix_clock_operations ptp_clock_ops = { .owner = THIS_MODULE, .clock_adjtime = ptp_clock_adjtime, .clock_gettime = ptp_clock_gettime, .clock_getres = ptp_clock_getres, .clock_settime = ptp_clock_settime, + .clock_alarm = ptp_clock_alarm, .ioctl = ptp_ioctl, .open = ptp_open, .poll = ptp_poll, @@ -324,6 +345,20 @@ int ptp_clock_unregister(struct ptp_clock *ptp) } EXPORT_SYMBOL(ptp_clock_unregister); +int alarm_timer_event(struct k_itimer *timr, int si_private) +{ + int ret = -1; + + timr->sigq->info.si_sys_private = si_private; + + rcu_read_lock(); + ret = send_sigqueue(timr->sigq, timr->it_pid, PIDTYPE_PID); + rcu_read_unlock(); + + /* If we failed to send the signal the timer stops. */ + return ret > 0; +} + void ptp_clock_event(struct ptp_clock *ptp, struct ptp_clock_event *event) { struct pps_event_time evt; @@ -331,8 +366,10 @@ void ptp_clock_event(struct ptp_clock *ptp, struct ptp_clock_event *event) switch (event->type) { case PTP_CLOCK_ALARM: + if (!event->timr) + break; + alarm_timer_event(event->timr, 0); break; - case PTP_CLOCK_EXTTS: enqueue_external_timestamp(&ptp->tsevq, event); wake_up_interruptible(&ptp->tsev_wq); diff --git a/drivers/ptp/ptp_qoriq.c b/drivers/ptp/ptp_qoriq.c index 5377536..ce14d44 100644 --- a/drivers/ptp/ptp_qoriq.c +++ b/drivers/ptp/ptp_qoriq.c @@ -163,10 +163,15 @@ irqreturn_t ptp_qoriq_isr(int irq, void *priv) if (irqs & ALM2) { ack |= ALM2; + if (!ptp_qoriq->timr) { + ptp_qoriq->alarm_value = 0; + ptp_qoriq->alarm_interval = 0; + } if (ptp_qoriq->alarm_value) { event.type = PTP_CLOCK_ALARM; event.index = 0; event.timestamp = ptp_qoriq->alarm_value; + event.timr = ptp_qoriq->timr; ptp_clock_event(ptp_qoriq->clock, &event); } if (ptp_qoriq->alarm_interval) { @@ -341,6 +346,44 @@ int ptp_qoriq_enable(struct ptp_clock_info *ptp, } EXPORT_SYMBOL_GPL(ptp_qoriq_enable); +int ptp_qoriq_alarm(struct ptp_clock_info *ptp, ktime_t expires, + bool absolute, struct k_itimer *timr) +{ + u64 ns, now; + u32 lo, hi, mask; + + struct ptp_qoriq *ptp_qoriq = container_of(ptp, struct ptp_qoriq, caps); + struct ptp_qoriq_registers *regs = &ptp_qoriq->regs; + + if (!timr) + return -EINVAL; + + now = tmr_cnt_read(ptp_qoriq); + if (!absolute) + ns = now + ktime_to_ns(expires); + else if (ktime_to_ns(expires) < now) + ns = now; + else + ns = ktime_to_ns(expires); + + hi = ns >> 32; + lo = ns & 0xffffffff; + ptp_qoriq->write(®s->alarm_regs->tmr_alarm2_l, lo); + ptp_qoriq->write(®s->alarm_regs->tmr_alarm2_h, hi); + + spin_lock(&ptp_qoriq->lock); + mask = ptp_qoriq->read(®s->ctrl_regs->tmr_temask); + mask |= ALM2EN; + ptp_qoriq->write(®s->ctrl_regs->tmr_temask, mask); + spin_unlock(&ptp_qoriq->lock); + + ptp_qoriq->alarm_value = ns; + ptp_qoriq->alarm_interval = ktime_to_ns(timr->it_interval); + + ptp_qoriq->timr = timr; + return 0; +} + static const struct ptp_clock_info ptp_qoriq_caps = { .owner = THIS_MODULE, .name = "qoriq ptp clock", @@ -355,6 +398,7 @@ int ptp_qoriq_enable(struct ptp_clock_info *ptp, .gettime64 = ptp_qoriq_gettime, .settime64 = ptp_qoriq_settime, .enable = ptp_qoriq_enable, + .alarm = ptp_qoriq_alarm, }; /** diff --git a/include/linux/fsl/ptp_qoriq.h b/include/linux/fsl/ptp_qoriq.h index 992bf9f..2928df4 100644 --- a/include/linux/fsl/ptp_qoriq.h +++ b/include/linux/fsl/ptp_qoriq.h @@ -143,6 +143,7 @@ struct ptp_qoriq { spinlock_t lock; /* protects regs */ struct ptp_clock *clock; struct ptp_clock_info caps; + struct k_itimer *timr; struct resource *rsrc; struct dentry *debugfs_root; struct device *dev; @@ -190,6 +191,8 @@ int ptp_qoriq_init(struct ptp_qoriq *ptp_qoriq, void __iomem *base, int ptp_qoriq_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts); int ptp_qoriq_settime(struct ptp_clock_info *ptp, const struct timespec64 *ts); +int ptp_qoriq_alarm(struct ptp_clock_info *ptp, ktime_t expires, + bool absolute, struct k_itimer *timr); int ptp_qoriq_enable(struct ptp_clock_info *ptp, struct ptp_clock_request *rq, int on); #ifdef CONFIG_DEBUG_FS diff --git a/include/linux/posix-clock.h b/include/linux/posix-clock.h index 18674d7..80cc214 100644 --- a/include/linux/posix-clock.h +++ b/include/linux/posix-clock.h @@ -59,7 +59,8 @@ struct posix_clock_operations { int (*clock_settime)(struct posix_clock *pc, const struct timespec64 *ts); - + int (*clock_alarm)(struct posix_clock *pc, ktime_t expires, + bool absolute, struct k_itimer *timr); /* * Optional character device methods: */ diff --git a/include/linux/ptp_clock_kernel.h b/include/linux/ptp_clock_kernel.h index 7121bbe..b51f64b 100644 --- a/include/linux/ptp_clock_kernel.h +++ b/include/linux/ptp_clock_kernel.h @@ -24,7 +24,7 @@ #include <linux/device.h> #include <linux/pps_kernel.h> #include <linux/ptp_clock.h> - +#include <linux/posix-timers.h> struct ptp_clock_request { enum { @@ -148,6 +148,8 @@ struct ptp_clock_info { int (*getcrosststamp)(struct ptp_clock_info *ptp, struct system_device_crosststamp *cts); int (*settime64)(struct ptp_clock_info *p, const struct timespec64 *ts); + int (*alarm)(struct ptp_clock_info *p, ktime_t expires, + bool absolute, struct k_itimer *timr); int (*enable)(struct ptp_clock_info *ptp, struct ptp_clock_request *request, int on); int (*verify)(struct ptp_clock_info *ptp, unsigned int pin, @@ -180,6 +182,7 @@ struct ptp_clock_event { u64 timestamp; struct pps_event_time pps_times; }; + struct k_itimer *timr; }; #if IS_REACHABLE(CONFIG_PTP_1588_CLOCK) diff --git a/kernel/time/posix-clock.c b/kernel/time/posix-clock.c index ec960bb..ac25d17 100644 --- a/kernel/time/posix-clock.c +++ b/kernel/time/posix-clock.c @@ -8,6 +8,7 @@ #include <linux/export.h> #include <linux/file.h> #include <linux/posix-clock.h> +#include <linux/posix-timers.h> #include <linux/slab.h> #include <linux/syscalls.h> #include <linux/uaccess.h> @@ -314,9 +315,68 @@ static int pc_clock_settime(clockid_t id, const struct timespec64 *ts) return err; } +static void pc_timer_arm(struct k_itimer *timr, ktime_t expires, + bool absolute, bool sigev_none) +{ + struct posix_clock_desc cd; + int err; + + err = get_clock_desc(timr->it_clock, &cd); + if (err) + return; + + cd.clk->ops.clock_alarm(cd.clk, expires, absolute, timr); +} + +static int pc_timer_set(struct k_itimer *timr, int flags, + struct itimerspec64 *new_setting, + struct itimerspec64 *old_setting) +{ + const struct k_clock *kc = timr->kclock; + bool sigev_none; + ktime_t expires; + + if (old_setting) + pr_err("old_setting not support!\n"); + + /* Prevent rearming by clearing the interval */ + timr->it_interval = 0; + + timr->it_active = 0; + timr->it_requeue_pending = (timr->it_requeue_pending + 2) & + ~REQUEUE_PENDING; + timr->it_overrun_last = 0; + + /* Switch off the timer when it_value is zero */ + if (!new_setting->it_value.tv_sec && !new_setting->it_value.tv_nsec) + return 0; + + timr->it_interval = timespec64_to_ktime(new_setting->it_interval); + expires = timespec64_to_ktime(new_setting->it_value); + sigev_none = timr->it_sigev_notify == SIGEV_NONE; + + kc->timer_arm(timr, expires, flags & TIMER_ABSTIME, sigev_none); + timr->it_active = !sigev_none; + return 0; +} + +static int pc_timer_create(struct k_itimer *new_timer) +{ + return 0; +} + +static int pc_timer_delete(struct k_itimer *new_timer) +{ + return 0; +} + const struct k_clock clock_posix_dynamic = { .clock_getres = pc_clock_getres, .clock_set = pc_clock_settime, .clock_get = pc_clock_gettime, .clock_adj = pc_clock_adjtime, + .timer_create = pc_timer_create, + .timer_del = pc_timer_delete, + .timer_set = pc_timer_set, + .timer_arm = pc_timer_arm, }; -- 1.8.3.1