[PATCH v1] timer:clock:ptp: add support the dynamic posix clock alarm set for ptp

Po Liu
2019-05-05T05:02:17Z

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.Liu@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(&regs->alarm_regs->tmr_alarm2_l, lo);
+ ptp_qoriq->write(&regs->alarm_regs->tmr_alarm2_h, hi);
+
+ spin_lock(&ptp_qoriq->lock);
+ mask = ptp_qoriq->read(&regs->ctrl_regs->tmr_temask);
+ mask |= ALM2EN;
+ ptp_qoriq->write(&regs->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