Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * pps_gen_parport.c -- kernel parallel port PPS signal generator
0004  *
0005  * Copyright (C) 2009   Alexander Gordeev <lasaine@lvk.cs.msu.su>
0006  */
0007 
0008 
0009 /*
0010  * TODO:
0011  * fix issues when realtime clock is adjusted in a leap
0012  */
0013 
0014 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0015 
0016 #include <linux/kernel.h>
0017 #include <linux/module.h>
0018 #include <linux/init.h>
0019 #include <linux/time.h>
0020 #include <linux/hrtimer.h>
0021 #include <linux/parport.h>
0022 
0023 #define SIGNAL      0
0024 #define NO_SIGNAL   PARPORT_CONTROL_STROBE
0025 
0026 /* module parameters */
0027 
0028 #define SEND_DELAY_MAX      100000
0029 
0030 static unsigned int send_delay = 30000;
0031 MODULE_PARM_DESC(delay,
0032     "Delay between setting and dropping the signal (ns)");
0033 module_param_named(delay, send_delay, uint, 0);
0034 
0035 
0036 #define SAFETY_INTERVAL 3000    /* set the hrtimer earlier for safety (ns) */
0037 
0038 /* internal per port structure */
0039 struct pps_generator_pp {
0040     struct pardevice *pardev;   /* parport device */
0041     struct hrtimer timer;
0042     long port_write_time;       /* calibrated port write time (ns) */
0043 };
0044 
0045 static struct pps_generator_pp device = {
0046     .pardev = NULL,
0047 };
0048 
0049 static int attached;
0050 
0051 /* calibrated time between a hrtimer event and the reaction */
0052 static long hrtimer_error = SAFETY_INTERVAL;
0053 
0054 /* the kernel hrtimer event */
0055 static enum hrtimer_restart hrtimer_event(struct hrtimer *timer)
0056 {
0057     struct timespec64 expire_time, ts1, ts2, ts3, dts;
0058     struct pps_generator_pp *dev;
0059     struct parport *port;
0060     long lim, delta;
0061     unsigned long flags;
0062 
0063     /* We have to disable interrupts here. The idea is to prevent
0064      * other interrupts on the same processor to introduce random
0065      * lags while polling the clock. ktime_get_real_ts64() takes <1us on
0066      * most machines while other interrupt handlers can take much
0067      * more potentially.
0068      *
0069      * NB: approx time with blocked interrupts =
0070      * send_delay + 3 * SAFETY_INTERVAL
0071      */
0072     local_irq_save(flags);
0073 
0074     /* first of all we get the time stamp... */
0075     ktime_get_real_ts64(&ts1);
0076     expire_time = ktime_to_timespec64(hrtimer_get_softexpires(timer));
0077     dev = container_of(timer, struct pps_generator_pp, timer);
0078     lim = NSEC_PER_SEC - send_delay - dev->port_write_time;
0079 
0080     /* check if we are late */
0081     if (expire_time.tv_sec != ts1.tv_sec || ts1.tv_nsec > lim) {
0082         local_irq_restore(flags);
0083         pr_err("we are late this time %lld.%09ld\n",
0084                 (s64)ts1.tv_sec, ts1.tv_nsec);
0085         goto done;
0086     }
0087 
0088     /* busy loop until the time is right for an assert edge */
0089     do {
0090         ktime_get_real_ts64(&ts2);
0091     } while (expire_time.tv_sec == ts2.tv_sec && ts2.tv_nsec < lim);
0092 
0093     /* set the signal */
0094     port = dev->pardev->port;
0095     port->ops->write_control(port, SIGNAL);
0096 
0097     /* busy loop until the time is right for a clear edge */
0098     lim = NSEC_PER_SEC - dev->port_write_time;
0099     do {
0100         ktime_get_real_ts64(&ts2);
0101     } while (expire_time.tv_sec == ts2.tv_sec && ts2.tv_nsec < lim);
0102 
0103     /* unset the signal */
0104     port->ops->write_control(port, NO_SIGNAL);
0105 
0106     ktime_get_real_ts64(&ts3);
0107 
0108     local_irq_restore(flags);
0109 
0110     /* update calibrated port write time */
0111     dts = timespec64_sub(ts3, ts2);
0112     dev->port_write_time =
0113         (dev->port_write_time + timespec64_to_ns(&dts)) >> 1;
0114 
0115 done:
0116     /* update calibrated hrtimer error */
0117     dts = timespec64_sub(ts1, expire_time);
0118     delta = timespec64_to_ns(&dts);
0119     /* If the new error value is bigger then the old, use the new
0120      * value, if not then slowly move towards the new value. This
0121      * way it should be safe in bad conditions and efficient in
0122      * good conditions.
0123      */
0124     if (delta >= hrtimer_error)
0125         hrtimer_error = delta;
0126     else
0127         hrtimer_error = (3 * hrtimer_error + delta) >> 2;
0128 
0129     /* update the hrtimer expire time */
0130     hrtimer_set_expires(timer,
0131             ktime_set(expire_time.tv_sec + 1,
0132                 NSEC_PER_SEC - (send_delay +
0133                 dev->port_write_time + SAFETY_INTERVAL +
0134                 2 * hrtimer_error)));
0135 
0136     return HRTIMER_RESTART;
0137 }
0138 
0139 /* calibrate port write time */
0140 #define PORT_NTESTS_SHIFT   5
0141 static void calibrate_port(struct pps_generator_pp *dev)
0142 {
0143     struct parport *port = dev->pardev->port;
0144     int i;
0145     long acc = 0;
0146 
0147     for (i = 0; i < (1 << PORT_NTESTS_SHIFT); i++) {
0148         struct timespec64 a, b;
0149         unsigned long irq_flags;
0150 
0151         local_irq_save(irq_flags);
0152         ktime_get_real_ts64(&a);
0153         port->ops->write_control(port, NO_SIGNAL);
0154         ktime_get_real_ts64(&b);
0155         local_irq_restore(irq_flags);
0156 
0157         b = timespec64_sub(b, a);
0158         acc += timespec64_to_ns(&b);
0159     }
0160 
0161     dev->port_write_time = acc >> PORT_NTESTS_SHIFT;
0162     pr_info("port write takes %ldns\n", dev->port_write_time);
0163 }
0164 
0165 static inline ktime_t next_intr_time(struct pps_generator_pp *dev)
0166 {
0167     struct timespec64 ts;
0168 
0169     ktime_get_real_ts64(&ts);
0170 
0171     return ktime_set(ts.tv_sec +
0172             ((ts.tv_nsec > 990 * NSEC_PER_MSEC) ? 1 : 0),
0173             NSEC_PER_SEC - (send_delay +
0174             dev->port_write_time + 3 * SAFETY_INTERVAL));
0175 }
0176 
0177 static void parport_attach(struct parport *port)
0178 {
0179     struct pardev_cb pps_cb;
0180 
0181     if (send_delay > SEND_DELAY_MAX) {
0182         pr_err("delay value should be not greater then %d\n", SEND_DELAY_MAX);
0183         return;
0184     }
0185 
0186     if (attached) {
0187         /* we already have a port */
0188         return;
0189     }
0190 
0191     memset(&pps_cb, 0, sizeof(pps_cb));
0192     pps_cb.private = &device;
0193     pps_cb.flags = PARPORT_FLAG_EXCL;
0194     device.pardev = parport_register_dev_model(port, KBUILD_MODNAME,
0195                            &pps_cb, 0);
0196     if (!device.pardev) {
0197         pr_err("couldn't register with %s\n", port->name);
0198         return;
0199     }
0200 
0201     if (parport_claim_or_block(device.pardev) < 0) {
0202         pr_err("couldn't claim %s\n", port->name);
0203         goto err_unregister_dev;
0204     }
0205 
0206     pr_info("attached to %s\n", port->name);
0207     attached = 1;
0208 
0209     calibrate_port(&device);
0210 
0211     hrtimer_init(&device.timer, CLOCK_REALTIME, HRTIMER_MODE_ABS);
0212     device.timer.function = hrtimer_event;
0213     hrtimer_start(&device.timer, next_intr_time(&device), HRTIMER_MODE_ABS);
0214 
0215     return;
0216 
0217 err_unregister_dev:
0218     parport_unregister_device(device.pardev);
0219 }
0220 
0221 static void parport_detach(struct parport *port)
0222 {
0223     if (port->cad != device.pardev)
0224         return; /* not our port */
0225 
0226     hrtimer_cancel(&device.timer);
0227     parport_release(device.pardev);
0228     parport_unregister_device(device.pardev);
0229 }
0230 
0231 static struct parport_driver pps_gen_parport_driver = {
0232     .name = KBUILD_MODNAME,
0233     .match_port = parport_attach,
0234     .detach = parport_detach,
0235     .devmodel = true,
0236 };
0237 module_parport_driver(pps_gen_parport_driver);
0238 
0239 MODULE_AUTHOR("Alexander Gordeev <lasaine@lvk.cs.msu.su>");
0240 MODULE_DESCRIPTION("parallel port PPS signal generator");
0241 MODULE_LICENSE("GPL");