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: &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: &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(pps: dev->pps, ts: &ts_assert, |
110 | PPS_CAPTUREASSERT, NULL); |
111 | return; |
112 | |
113 | out_both: |
114 | /* fire assert event */ |
115 | pps_event(pps: dev->pps, ts: &ts_assert, |
116 | PPS_CAPTUREASSERT, NULL); |
117 | /* fire clear event */ |
118 | pps_event(pps: dev->pps, ts: &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(size: 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 | par_dev_cb: &pps_client_cb, |
159 | cnt: 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(dev: 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: &info, |
171 | PPS_CAPTUREBOTH | PPS_OFFSETASSERT | PPS_OFFSETCLEAR); |
172 | if (IS_ERR(ptr: 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(dev: device->pardev); |
188 | err_unregister_dev: |
189 | parport_unregister_device(dev: device->pardev); |
190 | err_free: |
191 | ida_simple_remove(&pps_client_index, index); |
192 | kfree(objp: 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(pps: device->pps); |
209 | parport_release(dev: pardev); |
210 | parport_unregister_device(dev: pardev); |
211 | ida_simple_remove(&pps_client_index, device->index); |
212 | kfree(objp: 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 | |