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