229 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * pps_parport.c -- kernel parallel port PPS client4 *5 * Copyright (C) 2009 Alexander Gordeev <lasaine@lvk.cs.msu.su>6 */7 8 9/*10 * TODO:11 * implement echo over SEL pin12 */13 14#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt15 16#include <linux/kernel.h>17#include <linux/module.h>18#include <linux/init.h>19#include <linux/irqnr.h>20#include <linux/time.h>21#include <linux/slab.h>22#include <linux/parport.h>23#include <linux/pps_kernel.h>24 25/* module parameters */26 27#define CLEAR_WAIT_MAX 10028#define CLEAR_WAIT_MAX_ERRORS 529 30static unsigned int clear_wait = 100;31MODULE_PARM_DESC(clear_wait,32 "Maximum number of port reads when polling for signal clear,"33 " zero turns clear edge capture off entirely");34module_param(clear_wait, uint, 0);35 36static DEFINE_IDA(pps_client_index);37 38/* internal per port structure */39struct pps_client_pp {40 struct pardevice *pardev; /* parport device */41 struct pps_device *pps; /* PPS device */42 unsigned int cw; /* port clear timeout */43 unsigned int cw_err; /* number of timeouts */44 int index; /* device number */45};46 47static inline int signal_is_set(struct parport *port)48{49 return (port->ops->read_status(port) & PARPORT_STATUS_ACK) != 0;50}51 52/* parport interrupt handler */53static void parport_irq(void *handle)54{55 struct pps_event_time ts_assert, ts_clear;56 struct pps_client_pp *dev = handle;57 struct parport *port = dev->pardev->port;58 unsigned int i;59 unsigned long flags;60 61 /* first of all we get the time stamp... */62 pps_get_ts(&ts_assert);63 64 if (dev->cw == 0)65 /* clear edge capture disabled */66 goto out_assert;67 68 /* try capture the clear edge */69 70 /* We have to disable interrupts here. The idea is to prevent71 * other interrupts on the same processor to introduce random72 * lags while polling the port. Reading from IO port is known73 * to take approximately 1us while other interrupt handlers can74 * take much more potentially.75 *76 * Interrupts won't be disabled for a long time because the77 * number of polls is limited by clear_wait parameter which is78 * kept rather low. So it should never be an issue.79 */80 local_irq_save(flags);81 /* check the signal (no signal means the pulse is lost this time) */82 if (!signal_is_set(port)) {83 local_irq_restore(flags);84 dev_err(dev->pps->dev, "lost the signal\n");85 goto out_assert;86 }87 88 /* poll the port until the signal is unset */89 for (i = dev->cw; i; i--)90 if (!signal_is_set(port)) {91 pps_get_ts(&ts_clear);92 local_irq_restore(flags);93 dev->cw_err = 0;94 goto out_both;95 }96 local_irq_restore(flags);97 98 /* timeout */99 dev->cw_err++;100 if (dev->cw_err >= CLEAR_WAIT_MAX_ERRORS) {101 dev_err(dev->pps->dev, "disabled clear edge capture after %d"102 " timeouts\n", dev->cw_err);103 dev->cw = 0;104 dev->cw_err = 0;105 }106 107out_assert:108 /* fire assert event */109 pps_event(dev->pps, &ts_assert,110 PPS_CAPTUREASSERT, NULL);111 return;112 113out_both:114 /* fire assert event */115 pps_event(dev->pps, &ts_assert,116 PPS_CAPTUREASSERT, NULL);117 /* fire clear event */118 pps_event(dev->pps, &ts_clear,119 PPS_CAPTURECLEAR, NULL);120 return;121}122 123static void parport_attach(struct parport *port)124{125 struct pardev_cb pps_client_cb;126 int index;127 struct pps_client_pp *device;128 struct pps_source_info info = {129 .name = KBUILD_MODNAME,130 .path = "",131 .mode = PPS_CAPTUREBOTH | \132 PPS_OFFSETASSERT | PPS_OFFSETCLEAR | \133 PPS_ECHOASSERT | PPS_ECHOCLEAR | \134 PPS_CANWAIT | PPS_TSFMT_TSPEC,135 .owner = THIS_MODULE,136 .dev = NULL137 };138 139 if (clear_wait > CLEAR_WAIT_MAX) {140 pr_err("clear_wait value should be not greater then %d\n",141 CLEAR_WAIT_MAX);142 return;143 }144 145 device = kzalloc(sizeof(struct pps_client_pp), GFP_KERNEL);146 if (!device) {147 pr_err("memory allocation failed, not attaching\n");148 return;149 }150 151 index = ida_alloc(&pps_client_index, GFP_KERNEL);152 if (index < 0)153 goto err_free_device;154 155 memset(&pps_client_cb, 0, sizeof(pps_client_cb));156 pps_client_cb.private = device;157 pps_client_cb.irq_func = parport_irq;158 pps_client_cb.flags = PARPORT_FLAG_EXCL;159 device->pardev = parport_register_dev_model(port,160 KBUILD_MODNAME,161 &pps_client_cb,162 index);163 if (!device->pardev) {164 pr_err("couldn't register with %s\n", port->name);165 goto err_free_ida;166 }167 168 if (parport_claim_or_block(device->pardev) < 0) {169 pr_err("couldn't claim %s\n", port->name);170 goto err_unregister_dev;171 }172 173 device->pps = pps_register_source(&info,174 PPS_CAPTUREBOTH | PPS_OFFSETASSERT | PPS_OFFSETCLEAR);175 if (IS_ERR(device->pps)) {176 pr_err("couldn't register PPS source\n");177 goto err_release_dev;178 }179 180 device->cw = clear_wait;181 182 port->ops->enable_irq(port);183 device->index = index;184 185 pr_info("attached to %s\n", port->name);186 187 return;188 189err_release_dev:190 parport_release(device->pardev);191err_unregister_dev:192 parport_unregister_device(device->pardev);193err_free_ida:194 ida_free(&pps_client_index, index);195err_free_device:196 kfree(device);197}198 199static void parport_detach(struct parport *port)200{201 struct pardevice *pardev = port->cad;202 struct pps_client_pp *device;203 204 /* FIXME: oooh, this is ugly! */205 if (!pardev || strcmp(pardev->name, KBUILD_MODNAME))206 /* not our port */207 return;208 209 device = pardev->private;210 211 port->ops->disable_irq(port);212 pps_unregister_source(device->pps);213 parport_release(pardev);214 parport_unregister_device(pardev);215 ida_free(&pps_client_index, device->index);216 kfree(device);217}218 219static struct parport_driver pps_parport_driver = {220 .name = KBUILD_MODNAME,221 .match_port = parport_attach,222 .detach = parport_detach,223};224module_parport_driver(pps_parport_driver);225 226MODULE_AUTHOR("Alexander Gordeev <lasaine@lvk.cs.msu.su>");227MODULE_DESCRIPTION("parallel port PPS client");228MODULE_LICENSE("GPL");229