in generators/pps_gen_parport.c [57:139]
static enum hrtimer_restart hrtimer_event(struct hrtimer *timer)
{
struct timespec64 expire_time, ts1, ts2, ts3, dts;
struct pps_generator_pp *dev;
struct parport *port;
long lim, delta;
unsigned long flags;
/* We have to disable interrupts here. The idea is to prevent
* other interrupts on the same processor to introduce random
* lags while polling the clock. ktime_get_real_ts64() takes <1us on
* most machines while other interrupt handlers can take much
* more potentially.
*
* NB: approx time with blocked interrupts =
* send_delay + 3 * SAFETY_INTERVAL
*/
local_irq_save(flags);
/* first of all we get the time stamp... */
ktime_get_real_ts64(&ts1);
expire_time = ktime_to_timespec64(hrtimer_get_softexpires(timer));
dev = container_of(timer, struct pps_generator_pp, timer);
lim = NSEC_PER_SEC - send_delay - dev->port_write_time;
/* check if we are late */
if (expire_time.tv_sec != ts1.tv_sec || ts1.tv_nsec > lim) {
local_irq_restore(flags);
pr_err("we are late this time %lld.%09ld\n",
(s64)ts1.tv_sec, ts1.tv_nsec);
goto done;
}
/* busy loop until the time is right for an assert edge */
do {
ktime_get_real_ts64(&ts2);
} while (expire_time.tv_sec == ts2.tv_sec && ts2.tv_nsec < lim);
/* set the signal */
port = dev->pardev->port;
port->ops->write_control(port, SIGNAL);
/* busy loop until the time is right for a clear edge */
lim = NSEC_PER_SEC - dev->port_write_time;
do {
ktime_get_real_ts64(&ts2);
} while (expire_time.tv_sec == ts2.tv_sec && ts2.tv_nsec < lim);
/* unset the signal */
port->ops->write_control(port, NO_SIGNAL);
ktime_get_real_ts64(&ts3);
local_irq_restore(flags);
/* update calibrated port write time */
dts = timespec64_sub(ts3, ts2);
dev->port_write_time =
(dev->port_write_time + timespec64_to_ns(&dts)) >> 1;
done:
/* update calibrated hrtimer error */
dts = timespec64_sub(ts1, expire_time);
delta = timespec64_to_ns(&dts);
/* If the new error value is bigger then the old, use the new
* value, if not then slowly move towards the new value. This
* way it should be safe in bad conditions and efficient in
* good conditions.
*/
if (delta >= hrtimer_error)
hrtimer_error = delta;
else
hrtimer_error = (3 * hrtimer_error + delta) >> 2;
/* update the hrtimer expire time */
hrtimer_set_expires(timer,
ktime_set(expire_time.tv_sec + 1,
NSEC_PER_SEC - (send_delay +
dev->port_write_time + SAFETY_INTERVAL +
2 * hrtimer_error)));
return HRTIMER_RESTART;
}