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_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 189 err_release_dev: 190 parport_release(device->pardev); 191 err_unregister_dev: 192 parport_unregister_device(device->pardev); 193 err_free_ida: 194 ida_free(&pps_client_index, index); 195 err_free_device: 196 kfree(device); 197 } 198 199 static 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 219 static struct parport_driver pps_parport_driver = { 220 .name = KBUILD_MODNAME, 221 .match_port = parport_attach, 222 .detach = parport_detach, 223 .devmodel = true, 224 }; 225 module_parport_driver(pps_parport_driver); 226 227 MODULE_AUTHOR("Alexander Gordeev <lasaine@lvk.cs.msu.su>"); 228 MODULE_DESCRIPTION("parallel port PPS client"); 229 MODULE_LICENSE("GPL"); 230