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