1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * pps_parport.c -- kernel parallel port PPS client 4 * 5 * Copyright (C) 2009 Alexander Gordeev <lasaine@lvk.cs.msu.su> 6 */ 7 8 9 /* 10 * TODO: 11 * implement echo over SEL pin 12 */ 13 14 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 15 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 100 28 #define CLEAR_WAIT_MAX_ERRORS 5 29 30 static unsigned int clear_wait = 100; 31 MODULE_PARM_DESC(clear_wait, 32 "Maximum number of port reads when polling for signal clear," 33 " zero turns clear edge capture off entirely"); 34 module_param(clear_wait, uint, 0); 35 36 static DEFINE_IDA(pps_client_index); 37 38 /* internal per port structure */ 39 struct 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 47 static 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 */ 53 static 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 prevent 71 * other interrupts on the same processor to introduce random 72 * lags while polling the port. Reading from IO port is known 73 * to take approximately 1us while other interrupt handlers can 74 * take much more potentially. 75 * 76 * Interrupts won't be disabled for a long time because the 77 * number of polls is limited by clear_wait parameter which is 78 * 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 107 out_assert: 108 /* fire assert event */ 109 pps_event(dev->pps, &ts_assert, 110 PPS_CAPTUREASSERT, NULL); 111 return; 112 113 out_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 123 static 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 = NULL 137 }; 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_simple_get(&pps_client_index, 0, 0, GFP_KERNEL); 152 memset(&pps_client_cb, 0, sizeof(pps_client_cb)); 153 pps_client_cb.private = device; 154 pps_client_cb.irq_func = parport_irq; 155 pps_client_cb.flags = PARPORT_FLAG_EXCL; 156 device->pardev = parport_register_dev_model(port, 157 KBUILD_MODNAME, 158 &pps_client_cb, 159 index); 160 if (!device->pardev) { 161 pr_err("couldn't register with %s\n", port->name); 162 goto err_free; 163 } 164 165 if (parport_claim_or_block(device->pardev) < 0) { 166 pr_err("couldn't claim %s\n", port->name); 167 goto err_unregister_dev; 168 } 169 170 device->pps = pps_register_source(&info, 171 PPS_CAPTUREBOTH | PPS_OFFSETASSERT | PPS_OFFSETCLEAR); 172 if (IS_ERR(device->pps)) { 173 pr_err("couldn't register PPS source\n"); 174 goto err_release_dev; 175 } 176 177 device->cw = clear_wait; 178 179 port->ops->enable_irq(port); 180 device->index = index; 181 182 pr_info("attached to %s\n", port->name); 183 184 return; 185 186 err_release_dev: 187 parport_release(device->pardev); 188 err_unregister_dev: 189 parport_unregister_device(device->pardev); 190 err_free: 191 ida_simple_remove(&pps_client_index, index); 192 kfree(device); 193 } 194 195 static void parport_detach(struct parport *port) 196 { 197 struct pardevice *pardev = port->cad; 198 struct pps_client_pp *device; 199 200 /* FIXME: oooh, this is ugly! */ 201 if (!pardev || strcmp(pardev->name, KBUILD_MODNAME)) 202 /* not our port */ 203 return; 204 205 device = pardev->private; 206 207 port->ops->disable_irq(port); 208 pps_unregister_source(device->pps); 209 parport_release(pardev); 210 parport_unregister_device(pardev); 211 ida_simple_remove(&pps_client_index, device->index); 212 kfree(device); 213 } 214 215 static struct parport_driver pps_parport_driver = { 216 .name = KBUILD_MODNAME, 217 .match_port = parport_attach, 218 .detach = parport_detach, 219 .devmodel = true, 220 }; 221 module_parport_driver(pps_parport_driver); 222 223 MODULE_AUTHOR("Alexander Gordeev <lasaine@lvk.cs.msu.su>"); 224 MODULE_DESCRIPTION("parallel port PPS client"); 225 MODULE_LICENSE("GPL"); 226