Signed-off-by: Kieran Tyrrell <kie...@sienda.com>
---
diff --git a/drivers/net/ethernet/intel/igb/igb.h 
b/drivers/net/ethernet/intel/igb/igb.h
index 5387b3a..671c579 100644
--- a/drivers/net/ethernet/intel/igb/igb.h
+++ b/drivers/net/ethernet/intel/igb/igb.h
@@ -352,6 +352,7 @@ struct hwmon_buff {
 
 #define IGB_N_EXTTS    2
 #define IGB_N_PEROUT   2
+#define IGB_N_ALARM    2
 #define IGB_N_SDP      4
 #define IGB_RETA_SIZE  128
 
@@ -456,6 +457,7 @@ struct igb_adapter {
        struct {
                struct timespec64 start;
                struct timespec64 period;
+               bool enabled;
        } perout[IGB_N_PEROUT];
 
        char fw_version[32];
@@ -473,6 +475,8 @@ struct igb_adapter {
        int copper_tries;
        struct e1000_info ei;
        u16 eee_advert;
+
+       int timer_channel;
 };
 
 /* flags controlling PTP/1588 function */
@@ -558,6 +562,10 @@ void igb_ptp_rx_pktstamp(struct igb_q_vector *q_vector, 
unsigned char *va,
 int igb_ptp_set_ts_config(struct net_device *netdev, struct ifreq *ifr);
 int igb_ptp_get_ts_config(struct net_device *netdev, struct ifreq *ifr);
 void igb_set_flag_queue_pairs(struct igb_adapter *, const u32);
+void igb_tt_timer_enable(struct igb_adapter *adapter, bool enable,
+                        struct timespec64 *ts);
+void igb_ptp_read_i210(struct igb_adapter *adapter,
+                      struct timespec64 *ts);
 #ifdef CONFIG_IGB_HWMON
 void igb_sysfs_exit(struct igb_adapter *adapter);
 int igb_sysfs_init(struct igb_adapter *adapter);
diff --git a/drivers/net/ethernet/intel/igb/igb_main.c 
b/drivers/net/ethernet/intel/igb/igb_main.c
index 942a89f..aae53e6 100644
--- a/drivers/net/ethernet/intel/igb/igb_main.c
+++ b/drivers/net/ethernet/intel/igb/igb_main.c
@@ -5638,32 +5638,69 @@ static void igb_tsync_interrupt(struct igb_adapter 
*adapter)
        }
 
        if (tsicr & TSINTR_TT0) {
-               spin_lock(&adapter->tmreg_lock);
-               ts = timespec64_add(adapter->perout[0].start,
-                                   adapter->perout[0].period);
-               /* u32 conversion of tv_sec is safe until y2106 */
-               wr32(E1000_TRGTTIML0, ts.tv_nsec);
-               wr32(E1000_TRGTTIMH0, (u32)ts.tv_sec);
-               tsauxc = rd32(E1000_TSAUXC);
-               tsauxc |= TSAUXC_EN_TT0;
-               wr32(E1000_TSAUXC, tsauxc);
-               adapter->perout[0].start = ts;
-               spin_unlock(&adapter->tmreg_lock);
-               ack |= TSINTR_TT0;
+               if (adapter->timer_channel == 0) {
+                       /* this is a timer interrupt */
+                       spin_lock(&adapter->tmreg_lock);
+                       igb_tt_timer_enable(adapter, false, NULL);
+                       wr32(E1000_TSICR, TSINTR_TT0);
+                       igb_ptp_read_i210(adapter, &event.alarm_time);
+                       spin_unlock(&adapter->tmreg_lock);
+                       event.type = PTP_CLOCK_ALARM;
+                       event.index = 0;
+                       /* ptp_clock_event will return the next time to set */
+                       ptp_clock_event(adapter->ptp_clock, &event);
+                       spin_lock(&adapter->tmreg_lock);
+                       if (timespec64_to_ns(&event.alarm_time) != 0)
+                               igb_tt_timer_enable(adapter, true, 
&event.alarm_time);
+
+                       spin_unlock(&adapter->tmreg_lock);
+               } else {
+                       /* this is a periodic output interrupt */
+                       spin_lock(&adapter->tmreg_lock);
+                       ts = timespec64_add(adapter->perout[0].start,
+                                           adapter->perout[0].period);
+                       /* u32 conversion of tv_sec is safe until y2106 */
+                       wr32(E1000_TRGTTIML0, ts.tv_nsec);
+                       wr32(E1000_TRGTTIMH0, (u32)ts.tv_sec);
+                       tsauxc = rd32(E1000_TSAUXC);
+                       tsauxc |= TSAUXC_EN_TT0;
+                       wr32(E1000_TSAUXC, tsauxc);
+                       adapter->perout[0].start = ts;
+                       spin_unlock(&adapter->tmreg_lock);
+                       ack |= TSINTR_TT0;
+               }
+
        }
 
        if (tsicr & TSINTR_TT1) {
-               spin_lock(&adapter->tmreg_lock);
-               ts = timespec64_add(adapter->perout[1].start,
-                                   adapter->perout[1].period);
-               wr32(E1000_TRGTTIML1, ts.tv_nsec);
-               wr32(E1000_TRGTTIMH1, (u32)ts.tv_sec);
-               tsauxc = rd32(E1000_TSAUXC);
-               tsauxc |= TSAUXC_EN_TT1;
-               wr32(E1000_TSAUXC, tsauxc);
-               adapter->perout[1].start = ts;
-               spin_unlock(&adapter->tmreg_lock);
-               ack |= TSINTR_TT1;
+               if (adapter->timer_channel == 1) {
+                       /* this is a timer interrupt */
+                       spin_lock(&adapter->tmreg_lock);
+                       igb_tt_timer_enable(adapter, false, NULL);
+                       wr32(E1000_TSICR, TSINTR_TT1);
+                       igb_ptp_read_i210(adapter, &event.alarm_time);
+                       spin_unlock(&adapter->tmreg_lock);
+                       event.type = PTP_CLOCK_ALARM;
+                       /* ptp_clock_event will return the next time to set */
+                       ptp_clock_event(adapter->ptp_clock, &event);
+                       spin_lock(&adapter->tmreg_lock);
+                       if (timespec64_to_ns(&event.alarm_time) != 0)
+                               igb_tt_timer_enable(adapter, true, 
&event.alarm_time);
+                       spin_unlock(&adapter->tmreg_lock);
+               } else {
+                       /* this is a periodic output interrupt */
+                       spin_lock(&adapter->tmreg_lock);
+                       ts = timespec64_add(adapter->perout[1].start,
+                                           adapter->perout[1].period);
+                       wr32(E1000_TRGTTIML1, ts.tv_nsec);
+                       wr32(E1000_TRGTTIMH1, (u32)ts.tv_sec);
+                       tsauxc = rd32(E1000_TSAUXC);
+                       tsauxc |= TSAUXC_EN_TT1;
+                       wr32(E1000_TSAUXC, tsauxc);
+                       adapter->perout[1].start = ts;
+                       spin_unlock(&adapter->tmreg_lock);
+                       ack |= TSINTR_TT1;
+               }
        }
 
        if (tsicr & TSINTR_AUTT0) {
@@ -5687,7 +5724,8 @@ static void igb_tsync_interrupt(struct igb_adapter 
*adapter)
        }
 
        /* acknowledge the interrupts */
-       wr32(E1000_TSICR, ack);
+       if (ack)
+               wr32(E1000_TSICR, ack);
 }
 
 static irqreturn_t igb_msix_other(int irq, void *data)
diff --git a/drivers/net/ethernet/intel/igb/igb_ptp.c 
b/drivers/net/ethernet/intel/igb/igb_ptp.c
index 336c103..fffdb46 100644
--- a/drivers/net/ethernet/intel/igb/igb_ptp.c
+++ b/drivers/net/ethernet/intel/igb/igb_ptp.c
@@ -19,6 +19,7 @@
 #include <linux/device.h>
 #include <linux/pci.h>
 #include <linux/ptp_classify.h>
+#include <linux/posix-timers.h>
 
 #include "igb.h"
 
@@ -116,8 +117,8 @@ static cycle_t igb_ptp_read_82580(const struct cyclecounter 
*cc)
 }
 
 /* SYSTIM read access for I210/I211 */
-static void igb_ptp_read_i210(struct igb_adapter *adapter,
-                             struct timespec64 *ts)
+void igb_ptp_read_i210(struct igb_adapter *adapter,
+                      struct timespec64 *ts)
 {
        struct e1000_hw *hw = &adapter->hw;
        u32 sec, nsec;
@@ -515,6 +516,9 @@ static int igb_ptp_feature_enable_i210(struct 
ptp_clock_info *ptp,
                return 0;
 
        case PTP_CLK_REQ_PEROUT:
+               /* cannot use perout and timer at the same time */
+               if (igb->timer_channel == rq->perout.index)
+                       return -EBUSY;
                if (on) {
                        pin = ptp_find_pin(igb->ptp_clock, PTP_PF_PEROUT,
                                           rq->perout.index);
@@ -578,7 +582,9 @@ static int igb_ptp_feature_enable_i210(struct 
ptp_clock_info *ptp,
                                wr32(freqout, ns);
                        tsauxc |= tsauxc_mask;
                        tsim |= tsim_mask;
-               }
+                       igb->perout[i].enabled = true;
+               } else
+                       igb->perout[rq->perout.index].enabled = false;
                wr32(E1000_TSAUXC, tsauxc);
                wr32(E1000_TSIM, tsim);
                spin_unlock_irqrestore(&igb->tmreg_lock, flags);
@@ -594,6 +600,25 @@ static int igb_ptp_feature_enable_i210(struct 
ptp_clock_info *ptp,
                wr32(E1000_TSIM, tsim);
                spin_unlock_irqrestore(&igb->tmreg_lock, flags);
                return 0;
+
+       case PTP_CLK_REQ_ALARM:
+               /* cannot use perout and timer at the same time */
+               if (igb->perout[rq->alarm.index].enabled)
+                       return -EBUSY;
+               if (on && (igb->timer_channel != -1))
+                       return -EINVAL;
+               if (!on && (igb->timer_channel != rq->alarm.index))
+                       return -EINVAL;
+               if (on)
+                       igb->timer_channel = rq->alarm.index;
+               else {
+                       /* disable the timer */
+                       spin_lock_irqsave(&igb->tmreg_lock, flags);
+                       igb_tt_timer_enable(igb, false, NULL);
+                       igb->timer_channel = -1;
+                       spin_unlock_irqrestore(&igb->tmreg_lock, flags);
+               }
+               return 0;
        }
 
        return -EOPNOTSUPP;
@@ -1063,6 +1088,71 @@ int igb_ptp_set_ts_config(struct net_device *netdev, 
struct ifreq *ifr)
                -EFAULT : 0;
 }
 
+/* tmreg_lock should be held for this call */
+void igb_tt_timer_enable(struct igb_adapter *adapter, bool enable,
+                        struct timespec64 *ts)
+{
+       u32 tsauxc, tsim, ttimlreg, ttimhreg;
+       struct e1000_hw *hw = &adapter->hw;
+       int channel = adapter->timer_channel;
+
+       tsauxc = rd32(E1000_TSAUXC);
+       tsim = rd32(E1000_TSIM);
+
+       if (enable) {
+               if (channel == 1) {
+                       tsauxc |= TSAUXC_EN_TT1;
+                       tsim |= TSINTR_TT1;
+                       ttimlreg = E1000_TRGTTIML1;
+                       ttimhreg = E1000_TRGTTIMH1;
+               }
+               else {
+                       tsauxc |= TSAUXC_EN_TT0;
+                       tsim |= TSINTR_TT0;
+                       ttimlreg = E1000_TRGTTIML0;
+                       ttimhreg = E1000_TRGTTIMH0;
+               }
+               /* set trigger time and then enable timer */
+               wr32(ttimlreg, ts->tv_nsec);
+               wr32(ttimhreg, (u32)ts->tv_sec);
+               wr32(E1000_TSAUXC, tsauxc);
+               wr32(E1000_TSIM, tsim);
+       } else {
+               if (channel == 1) {
+                       tsauxc &= ~TSAUXC_EN_TT1;
+                       tsim &= ~TSINTR_TT1;
+               }
+               else {
+                       tsauxc &= ~TSAUXC_EN_TT0;
+                       tsim &= ~TSINTR_TT0;
+               }
+               wr32(E1000_TSAUXC, tsauxc);
+               wr32(E1000_TSIM, tsim);
+       }
+}
+
+static int igb_ptp_timer_settime_i210(struct ptp_clock_info *ptp,
+                                     struct timespec64 *ts)
+{
+       struct igb_adapter *igb =
+                               container_of(ptp, struct igb_adapter, ptp_caps);
+       unsigned long irqsaveflags;
+
+       if (igb->timer_channel == -1)
+               return -EBUSY;
+
+       spin_lock_irqsave(&igb->tmreg_lock, irqsaveflags);
+
+       if (timespec64_to_ns(ts) == 0)
+               igb_tt_timer_enable(igb, false, NULL);
+       else
+               igb_tt_timer_enable(igb, true, ts);
+
+       spin_unlock_irqrestore(&igb->tmreg_lock, irqsaveflags);
+
+       return 0;
+}
+
 /**
  * igb_ptp_init - Initialize PTP functionality
  * @adapter: Board private structure
@@ -1125,6 +1215,7 @@ void igb_ptp_init(struct igb_adapter *adapter)
                snprintf(adapter->ptp_caps.name, 16, "%pm", netdev->dev_addr);
                adapter->ptp_caps.owner = THIS_MODULE;
                adapter->ptp_caps.max_adj = 62499999;
+               adapter->ptp_caps.n_alarm = IGB_N_ALARM;
                adapter->ptp_caps.n_ext_ts = IGB_N_EXTTS;
                adapter->ptp_caps.n_per_out = IGB_N_PEROUT;
                adapter->ptp_caps.n_pins = IGB_N_SDP;
@@ -1136,6 +1227,7 @@ void igb_ptp_init(struct igb_adapter *adapter)
                adapter->ptp_caps.settime64 = igb_ptp_settime_i210;
                adapter->ptp_caps.enable = igb_ptp_feature_enable_i210;
                adapter->ptp_caps.verify = igb_ptp_verify_pin;
+               adapter->ptp_caps.timersettime = igb_ptp_timer_settime_i210;
                break;
        default:
                adapter->ptp_clock = NULL;
@@ -1151,6 +1243,7 @@ void igb_ptp_init(struct igb_adapter *adapter)
 
        adapter->tstamp_config.rx_filter = HWTSTAMP_FILTER_NONE;
        adapter->tstamp_config.tx_type = HWTSTAMP_TX_OFF;
+       adapter->timer_channel = -1;
 
        igb_ptp_reset(adapter);
 
diff --git a/drivers/ptp/ptp_chardev.c b/drivers/ptp/ptp_chardev.c
index d637c93..b351c44 100644
--- a/drivers/ptp/ptp_chardev.c
+++ b/drivers/ptp/ptp_chardev.c
@@ -176,6 +176,21 @@ long ptp_ioctl(struct posix_clock *pc, unsigned int cmd, 
unsigned long arg)
                err = ops->enable(ops, &req, enable);
                break;
 
+       case PTP_ALARM_REQUEST:
+               if (copy_from_user(&req.alarm, (void __user *)arg,
+                                  sizeof(req.alarm))) {
+                       err = -EFAULT;
+                       break;
+               }
+               if (req.alarm.index >= ops->n_alarm) {
+                       err = -EINVAL;
+                       break;
+               }
+               req.type = PTP_CLK_REQ_ALARM;
+               enable = req.alarm.flags & PTP_ENABLE_FEATURE ? 1 : 0;
+               err = ops->enable(ops, &req, enable);
+               break;
+
        case PTP_ENABLE_PPS:
                if (!capable(CAP_SYS_TIME))
                        return -EPERM;
diff --git a/drivers/ptp/ptp_clock.c b/drivers/ptp/ptp_clock.c
index 2e481b9..28573dc 100644
--- a/drivers/ptp/ptp_clock.c
+++ b/drivers/ptp/ptp_clock.c
@@ -36,6 +36,8 @@
 #define PTP_PPS_EVENT PPS_CAPTUREASSERT
 #define PTP_PPS_MODE (PTP_PPS_DEFAULTS | PPS_CANWAIT | PPS_TSFMT_TSPEC)
 
+#define PTP_TIMER_MINIMUM_INTERVAL_NS 100000
+
 /* private globals */
 
 static dev_t ptp_devt;
@@ -43,6 +45,56 @@ static struct class *ptp_class;
 
 static DEFINE_IDA(ptp_clocks_map);
 
+static void alarm_event(struct ptp_clock *ptp, struct ptp_clock_event *event)
+{
+       struct timerqueue_node *next;
+       struct k_itimer *kit;
+       s64 ns_now;
+       bool signal_failed_to_send;
+       unsigned long tq_lock_flags;
+
+       ns_now = timespec64_to_ns(&event->alarm_time);
+
+       spin_lock_irqsave(&ptp->tq_lock, tq_lock_flags);
+
+       next = timerqueue_getnext(&ptp->timerqueue);
+
+       while (next) {
+               if (next->expires.tv64 > ns_now)
+                       break;
+
+               kit = container_of(next, struct k_itimer, it.real.timer.node);
+
+               signal_failed_to_send = posix_timer_event(kit, 0);
+
+               /* update the last one that has fired */
+               timerqueue_del(&ptp->timerqueue, &kit->it.real.timer.node);
+               if ((ktime_to_ns(kit->it.real.interval) != 0)
+                               && !signal_failed_to_send) {
+                       /* this is a periodic timer, set the next fire time */
+                       kit->it.real.timer.node.expires =
+                                       ktime_add(
+                                               kit->it.real.timer.node.expires,
+                                                 kit->it.real.interval);
+                       timerqueue_add(&ptp->timerqueue,
+                                      &kit->it.real.timer.node);
+               }
+
+               next = timerqueue_getnext(&ptp->timerqueue);
+       }
+
+       /* now set the event time to the next timer fire time */
+       next = timerqueue_getnext(&ptp->timerqueue);
+       if (next) {
+               event->alarm_time = ktime_to_timespec64(next->expires);
+       } else {
+               event->alarm_time.tv_sec = 0;
+               event->alarm_time.tv_nsec = 0;
+       }
+
+       spin_unlock_irqrestore(&ptp->tq_lock, tq_lock_flags);
+}
+
 /* time stamp event queue operations */
 
 static inline int queue_free(struct timestamp_event_queue *q)
@@ -163,12 +215,113 @@ static int ptp_clock_adjtime(struct posix_clock *pc, 
struct timex *tx)
        return err;
 }
 
+static int ptp_timer_create(struct posix_clock *pc, struct k_itimer *kit)
+{
+       struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock);
+       unsigned long tq_lock_flags;
+
+       spin_lock_irqsave(&ptp->tq_lock, tq_lock_flags);
+       timerqueue_init(&kit->it.real.timer.node);
+       spin_unlock_irqrestore(&ptp->tq_lock, tq_lock_flags);
+
+       return 0;
+}
+
+static int ptp_timer_delete(struct posix_clock *pc, struct k_itimer *kit)
+{
+       struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock);
+       unsigned long tq_lock_flags;
+
+       spin_lock_irqsave(&ptp->tq_lock, tq_lock_flags);
+       if (!RB_EMPTY_NODE(&kit->it.real.timer.node.node))
+               timerqueue_del(&ptp->timerqueue, &kit->it.real.timer.node);
+       spin_unlock_irqrestore(&ptp->tq_lock, tq_lock_flags);
+
+       return 0;
+}
+
+static void ptp_timer_gettime(struct posix_clock *pc,
+                             struct k_itimer *kit,
+                             struct itimerspec *tsp)
+{
+       struct timespec time_now;
+
+       if (ptp_clock_gettime(pc, &time_now) != 0)
+               return;
+
+       tsp->it_interval = ktime_to_timespec(kit->it.real.interval);
+       tsp->it_value = timespec_sub(ktime_to_timespec(
+                       kit->it.real.timer.node.expires), time_now);
+}
+
+
+static int ptp_timer_settime(struct posix_clock *pc,
+                            struct k_itimer *kit, int flags,
+                            struct itimerspec *tsp, struct itimerspec *old)
+{
+       struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock);
+       int err;
+       unsigned long tq_lock_flags;
+       struct timespec time_now;
+       ktime_t fire_time;
+       struct timerqueue_node *next;
+       struct timespec64 ts;
+
+       if (ptp->info->timersettime == 0)
+               return -EOPNOTSUPP;
+
+       if (old)
+               ptp_timer_gettime(pc, kit, old);
+
+       fire_time = timespec_to_ktime(tsp->it_value);
+
+       if ((fire_time.tv64 != 0) && !(flags & TIMER_ABSTIME)) {
+               err = ptp_clock_gettime(pc, &time_now);
+               if (err)
+                       return err;
+               /* convert relative to absolute time */
+               fire_time = ktime_add(fire_time, timespec_to_ktime(time_now));
+       }
+
+       kit->it.real.interval = timespec_to_ktime(tsp->it_interval);
+
+       if ((ktime_to_ns(kit->it.real.interval) != 0)
+               && (ktime_to_ns(kit->it.real.interval) < 
PTP_TIMER_MINIMUM_INTERVAL_NS))
+               kit->it.real.interval = 
ns_to_ktime(PTP_TIMER_MINIMUM_INTERVAL_NS);
+
+       spin_lock_irqsave(&ptp->tq_lock, tq_lock_flags);
+
+       kit->it.real.timer.node.expires = fire_time;
+
+       if (!RB_EMPTY_NODE(&kit->it.real.timer.node.node))
+               timerqueue_del(&ptp->timerqueue, &kit->it.real.timer.node);
+
+       if (fire_time.tv64 != 0)
+               timerqueue_add(&ptp->timerqueue, &kit->it.real.timer.node);
+
+       next = timerqueue_getnext(&ptp->timerqueue);
+
+       spin_unlock_irqrestore(&ptp->tq_lock, tq_lock_flags);
+
+       if (next)
+               ts = ktime_to_timespec64(next->expires);
+       else {
+               ts.tv_sec = 0;
+               ts.tv_nsec = 0;
+       }
+       return ptp->info->timersettime(ptp->info, &ts);
+}
+
 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,
+       .timer_create   = ptp_timer_create,
+       .timer_delete   = ptp_timer_delete,
+       .timer_gettime  = ptp_timer_gettime,
+       .timer_settime  = ptp_timer_settime,
        .ioctl          = ptp_ioctl,
        .open           = ptp_open,
        .poll           = ptp_poll,
@@ -217,6 +370,8 @@ struct ptp_clock *ptp_clock_register(struct ptp_clock_info 
*info,
        mutex_init(&ptp->tsevq_mux);
        mutex_init(&ptp->pincfg_mux);
        init_waitqueue_head(&ptp->tsev_wq);
+       spin_lock_init(&ptp->tq_lock);
+       timerqueue_init_head(&ptp->timerqueue);
 
        /* Create a new device in our class. */
        ptp->dev = device_create(ptp_class, parent, ptp->devid, ptp,
@@ -293,6 +448,7 @@ void ptp_clock_event(struct ptp_clock *ptp, struct 
ptp_clock_event *event)
        switch (event->type) {
 
        case PTP_CLOCK_ALARM:
+               alarm_event(ptp, event);
                break;
 
        case PTP_CLOCK_EXTTS:
diff --git a/drivers/ptp/ptp_private.h b/drivers/ptp/ptp_private.h
index 9c5d414..d491299 100644
--- a/drivers/ptp/ptp_private.h
+++ b/drivers/ptp/ptp_private.h
@@ -54,6 +54,10 @@ struct ptp_clock {
        struct device_attribute *pin_dev_attr;
        struct attribute **pin_attr;
        struct attribute_group pin_attr_group;
+
+       struct timerqueue_head timerqueue;
+       spinlock_t tq_lock;
+       struct work_struct alarm_work;
 };
 
 /*
diff --git a/include/linux/ptp_clock_kernel.h b/include/linux/ptp_clock_kernel.h
index 6b15e16..155acc2 100644
--- a/include/linux/ptp_clock_kernel.h
+++ b/include/linux/ptp_clock_kernel.h
@@ -31,10 +31,12 @@ struct ptp_clock_request {
                PTP_CLK_REQ_EXTTS,
                PTP_CLK_REQ_PEROUT,
                PTP_CLK_REQ_PPS,
+               PTP_CLK_REQ_ALARM
        } type;
        union {
                struct ptp_extts_request extts;
                struct ptp_perout_request perout;
+               struct ptp_alarm_request alarm;
        };
 };
 
@@ -92,6 +94,9 @@ struct system_device_crosststamp;
  *            parameter func: the desired function to use.
  *            parameter chan: the function channel index to use.
  *
+ * @timersettime:  Set the alarm time for the hardware timer.
+ *                 parameter ts: Time value to set.
+ *
  * Drivers should embed their ptp_clock_info within a private
  * structure, obtaining a reference to it using container_of().
  *
@@ -118,6 +123,7 @@ struct ptp_clock_info {
                      struct ptp_clock_request *request, int on);
        int (*verify)(struct ptp_clock_info *ptp, unsigned int pin,
                      enum ptp_pin_function func, unsigned int chan);
+       int (*timersettime)(struct ptp_clock_info *ptp, struct timespec64 *ts);
 };
 
 struct ptp_clock;
@@ -149,12 +155,14 @@ enum ptp_clock_events {
 };
 
 /**
- * struct ptp_clock_event - decribes a PTP hardware clock event
+ * struct ptp_clock_event - describes a PTP hardware clock event
  *
  * @type:  One of the ptp_clock_events enumeration values.
  * @index: Identifies the source of the event.
- * @timestamp: When the event occurred (%PTP_CLOCK_EXTTS only).
- * @pps_times: When the event occurred (%PTP_CLOCK_PPSUSR only).
+ * @timestamp:         When the event occurred (%PTP_CLOCK_EXTTS only).
+ * @pps_times:         When the event occurred (%PTP_CLOCK_PPSUSR only).
+ * @alarm_time:        When the event occurred, and on return the time for
+ *             the next event (%PTP_CLOCK_ALARM only).
  */
 
 struct ptp_clock_event {
@@ -163,6 +171,7 @@ struct ptp_clock_event {
        union {
                u64 timestamp;
                struct pps_event_time pps_times;
+               struct timespec64 alarm_time;
        };
 };
 
diff --git a/include/uapi/linux/ptp_clock.h b/include/uapi/linux/ptp_clock.h
index ac6dded..066424b 100644
--- a/include/uapi/linux/ptp_clock.h
+++ b/include/uapi/linux/ptp_clock.h
@@ -70,6 +70,12 @@ struct ptp_perout_request {
        unsigned int rsv[4];          /* Reserved for future use. */
 };
 
+struct ptp_alarm_request {
+       unsigned int index;  /* Which channel to configure. */
+       unsigned int flags;  /* Bit field for PTP_xxx flags. */
+       unsigned int rsv[2]; /* Reserved for future use. */
+};
+
 #define PTP_MAX_SAMPLES 25 /* Maximum allowed offset measurement samples. */
 
 struct ptp_sys_offset {
@@ -135,6 +141,7 @@ struct ptp_pin_desc {
 #define PTP_PIN_SETFUNC    _IOW(PTP_CLK_MAGIC, 7, struct ptp_pin_desc)
 #define PTP_SYS_OFFSET_PRECISE \
        _IOWR(PTP_CLK_MAGIC, 8, struct ptp_sys_offset_precise)
+#define PTP_ALARM_REQUEST  _IOW(PTP_CLK_MAGIC, 9, struct ptp_alarm_request)
 
 struct ptp_extts_event {
        struct ptp_clock_time t; /* Time event occured. */


------------------------------------------------------------------------------
_______________________________________________
Linuxptp-devel mailing list
Linuxptp-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/linuxptp-devel

Reply via email to