xref: /openbmc/linux/drivers/pps/pps.c (revision eae9d2ba0cfc27a2ad9765f23efb98fb80d80234)
1*eae9d2baSRodolfo Giometti /*
2*eae9d2baSRodolfo Giometti  * PPS core file
3*eae9d2baSRodolfo Giometti  *
4*eae9d2baSRodolfo Giometti  *
5*eae9d2baSRodolfo Giometti  * Copyright (C) 2005-2009   Rodolfo Giometti <giometti@linux.it>
6*eae9d2baSRodolfo Giometti  *
7*eae9d2baSRodolfo Giometti  *   This program is free software; you can redistribute it and/or modify
8*eae9d2baSRodolfo Giometti  *   it under the terms of the GNU General Public License as published by
9*eae9d2baSRodolfo Giometti  *   the Free Software Foundation; either version 2 of the License, or
10*eae9d2baSRodolfo Giometti  *   (at your option) any later version.
11*eae9d2baSRodolfo Giometti  *
12*eae9d2baSRodolfo Giometti  *   This program is distributed in the hope that it will be useful,
13*eae9d2baSRodolfo Giometti  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
14*eae9d2baSRodolfo Giometti  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15*eae9d2baSRodolfo Giometti  *   GNU General Public License for more details.
16*eae9d2baSRodolfo Giometti  *
17*eae9d2baSRodolfo Giometti  *   You should have received a copy of the GNU General Public License
18*eae9d2baSRodolfo Giometti  *   along with this program; if not, write to the Free Software
19*eae9d2baSRodolfo Giometti  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20*eae9d2baSRodolfo Giometti  */
21*eae9d2baSRodolfo Giometti 
22*eae9d2baSRodolfo Giometti 
23*eae9d2baSRodolfo Giometti #include <linux/kernel.h>
24*eae9d2baSRodolfo Giometti #include <linux/module.h>
25*eae9d2baSRodolfo Giometti #include <linux/init.h>
26*eae9d2baSRodolfo Giometti #include <linux/sched.h>
27*eae9d2baSRodolfo Giometti #include <linux/uaccess.h>
28*eae9d2baSRodolfo Giometti #include <linux/idr.h>
29*eae9d2baSRodolfo Giometti #include <linux/cdev.h>
30*eae9d2baSRodolfo Giometti #include <linux/poll.h>
31*eae9d2baSRodolfo Giometti #include <linux/pps_kernel.h>
32*eae9d2baSRodolfo Giometti 
33*eae9d2baSRodolfo Giometti /*
34*eae9d2baSRodolfo Giometti  * Local variables
35*eae9d2baSRodolfo Giometti  */
36*eae9d2baSRodolfo Giometti 
37*eae9d2baSRodolfo Giometti static dev_t pps_devt;
38*eae9d2baSRodolfo Giometti static struct class *pps_class;
39*eae9d2baSRodolfo Giometti 
40*eae9d2baSRodolfo Giometti /*
41*eae9d2baSRodolfo Giometti  * Char device methods
42*eae9d2baSRodolfo Giometti  */
43*eae9d2baSRodolfo Giometti 
44*eae9d2baSRodolfo Giometti static unsigned int pps_cdev_poll(struct file *file, poll_table *wait)
45*eae9d2baSRodolfo Giometti {
46*eae9d2baSRodolfo Giometti 	struct pps_device *pps = file->private_data;
47*eae9d2baSRodolfo Giometti 
48*eae9d2baSRodolfo Giometti 	poll_wait(file, &pps->queue, wait);
49*eae9d2baSRodolfo Giometti 
50*eae9d2baSRodolfo Giometti 	return POLLIN | POLLRDNORM;
51*eae9d2baSRodolfo Giometti }
52*eae9d2baSRodolfo Giometti 
53*eae9d2baSRodolfo Giometti static int pps_cdev_fasync(int fd, struct file *file, int on)
54*eae9d2baSRodolfo Giometti {
55*eae9d2baSRodolfo Giometti 	struct pps_device *pps = file->private_data;
56*eae9d2baSRodolfo Giometti 	return fasync_helper(fd, file, on, &pps->async_queue);
57*eae9d2baSRodolfo Giometti }
58*eae9d2baSRodolfo Giometti 
59*eae9d2baSRodolfo Giometti static long pps_cdev_ioctl(struct file *file,
60*eae9d2baSRodolfo Giometti 		unsigned int cmd, unsigned long arg)
61*eae9d2baSRodolfo Giometti {
62*eae9d2baSRodolfo Giometti 	struct pps_device *pps = file->private_data;
63*eae9d2baSRodolfo Giometti 	struct pps_kparams params;
64*eae9d2baSRodolfo Giometti 	struct pps_fdata fdata;
65*eae9d2baSRodolfo Giometti 	unsigned long ticks;
66*eae9d2baSRodolfo Giometti 	void __user *uarg = (void __user *) arg;
67*eae9d2baSRodolfo Giometti 	int __user *iuarg = (int __user *) arg;
68*eae9d2baSRodolfo Giometti 	int err;
69*eae9d2baSRodolfo Giometti 
70*eae9d2baSRodolfo Giometti 	switch (cmd) {
71*eae9d2baSRodolfo Giometti 	case PPS_GETPARAMS:
72*eae9d2baSRodolfo Giometti 		pr_debug("PPS_GETPARAMS: source %d\n", pps->id);
73*eae9d2baSRodolfo Giometti 
74*eae9d2baSRodolfo Giometti 		/* Return current parameters */
75*eae9d2baSRodolfo Giometti 		err = copy_to_user(uarg, &pps->params,
76*eae9d2baSRodolfo Giometti 						sizeof(struct pps_kparams));
77*eae9d2baSRodolfo Giometti 		if (err)
78*eae9d2baSRodolfo Giometti 			return -EFAULT;
79*eae9d2baSRodolfo Giometti 
80*eae9d2baSRodolfo Giometti 		break;
81*eae9d2baSRodolfo Giometti 
82*eae9d2baSRodolfo Giometti 	case PPS_SETPARAMS:
83*eae9d2baSRodolfo Giometti 		pr_debug("PPS_SETPARAMS: source %d\n", pps->id);
84*eae9d2baSRodolfo Giometti 
85*eae9d2baSRodolfo Giometti 		/* Check the capabilities */
86*eae9d2baSRodolfo Giometti 		if (!capable(CAP_SYS_TIME))
87*eae9d2baSRodolfo Giometti 			return -EPERM;
88*eae9d2baSRodolfo Giometti 
89*eae9d2baSRodolfo Giometti 		err = copy_from_user(&params, uarg, sizeof(struct pps_kparams));
90*eae9d2baSRodolfo Giometti 		if (err)
91*eae9d2baSRodolfo Giometti 			return -EFAULT;
92*eae9d2baSRodolfo Giometti 		if (!(params.mode & (PPS_CAPTUREASSERT | PPS_CAPTURECLEAR))) {
93*eae9d2baSRodolfo Giometti 			pr_debug("capture mode unspecified (%x)\n",
94*eae9d2baSRodolfo Giometti 								params.mode);
95*eae9d2baSRodolfo Giometti 			return -EINVAL;
96*eae9d2baSRodolfo Giometti 		}
97*eae9d2baSRodolfo Giometti 
98*eae9d2baSRodolfo Giometti 		/* Check for supported capabilities */
99*eae9d2baSRodolfo Giometti 		if ((params.mode & ~pps->info.mode) != 0) {
100*eae9d2baSRodolfo Giometti 			pr_debug("unsupported capabilities (%x)\n",
101*eae9d2baSRodolfo Giometti 								params.mode);
102*eae9d2baSRodolfo Giometti 			return -EINVAL;
103*eae9d2baSRodolfo Giometti 		}
104*eae9d2baSRodolfo Giometti 
105*eae9d2baSRodolfo Giometti 		spin_lock_irq(&pps->lock);
106*eae9d2baSRodolfo Giometti 
107*eae9d2baSRodolfo Giometti 		/* Save the new parameters */
108*eae9d2baSRodolfo Giometti 		pps->params = params;
109*eae9d2baSRodolfo Giometti 
110*eae9d2baSRodolfo Giometti 		/* Restore the read only parameters */
111*eae9d2baSRodolfo Giometti 		if ((params.mode & (PPS_TSFMT_TSPEC | PPS_TSFMT_NTPFP)) == 0) {
112*eae9d2baSRodolfo Giometti 			/* section 3.3 of RFC 2783 interpreted */
113*eae9d2baSRodolfo Giometti 			pr_debug("time format unspecified (%x)\n",
114*eae9d2baSRodolfo Giometti 								params.mode);
115*eae9d2baSRodolfo Giometti 			pps->params.mode |= PPS_TSFMT_TSPEC;
116*eae9d2baSRodolfo Giometti 		}
117*eae9d2baSRodolfo Giometti 		if (pps->info.mode & PPS_CANWAIT)
118*eae9d2baSRodolfo Giometti 			pps->params.mode |= PPS_CANWAIT;
119*eae9d2baSRodolfo Giometti 		pps->params.api_version = PPS_API_VERS;
120*eae9d2baSRodolfo Giometti 
121*eae9d2baSRodolfo Giometti 		spin_unlock_irq(&pps->lock);
122*eae9d2baSRodolfo Giometti 
123*eae9d2baSRodolfo Giometti 		break;
124*eae9d2baSRodolfo Giometti 
125*eae9d2baSRodolfo Giometti 	case PPS_GETCAP:
126*eae9d2baSRodolfo Giometti 		pr_debug("PPS_GETCAP: source %d\n", pps->id);
127*eae9d2baSRodolfo Giometti 
128*eae9d2baSRodolfo Giometti 		err = put_user(pps->info.mode, iuarg);
129*eae9d2baSRodolfo Giometti 		if (err)
130*eae9d2baSRodolfo Giometti 			return -EFAULT;
131*eae9d2baSRodolfo Giometti 
132*eae9d2baSRodolfo Giometti 		break;
133*eae9d2baSRodolfo Giometti 
134*eae9d2baSRodolfo Giometti 	case PPS_FETCH:
135*eae9d2baSRodolfo Giometti 		pr_debug("PPS_FETCH: source %d\n", pps->id);
136*eae9d2baSRodolfo Giometti 
137*eae9d2baSRodolfo Giometti 		err = copy_from_user(&fdata, uarg, sizeof(struct pps_fdata));
138*eae9d2baSRodolfo Giometti 		if (err)
139*eae9d2baSRodolfo Giometti 			return -EFAULT;
140*eae9d2baSRodolfo Giometti 
141*eae9d2baSRodolfo Giometti 		pps->go = 0;
142*eae9d2baSRodolfo Giometti 
143*eae9d2baSRodolfo Giometti 		/* Manage the timeout */
144*eae9d2baSRodolfo Giometti 		if (fdata.timeout.flags & PPS_TIME_INVALID)
145*eae9d2baSRodolfo Giometti 			err = wait_event_interruptible(pps->queue, pps->go);
146*eae9d2baSRodolfo Giometti 		else {
147*eae9d2baSRodolfo Giometti 			pr_debug("timeout %lld.%09d\n",
148*eae9d2baSRodolfo Giometti 					(long long) fdata.timeout.sec,
149*eae9d2baSRodolfo Giometti 					fdata.timeout.nsec);
150*eae9d2baSRodolfo Giometti 			ticks = fdata.timeout.sec * HZ;
151*eae9d2baSRodolfo Giometti 			ticks += fdata.timeout.nsec / (NSEC_PER_SEC / HZ);
152*eae9d2baSRodolfo Giometti 
153*eae9d2baSRodolfo Giometti 			if (ticks != 0) {
154*eae9d2baSRodolfo Giometti 				err = wait_event_interruptible_timeout(
155*eae9d2baSRodolfo Giometti 						pps->queue, pps->go, ticks);
156*eae9d2baSRodolfo Giometti 				if (err == 0)
157*eae9d2baSRodolfo Giometti 					return -ETIMEDOUT;
158*eae9d2baSRodolfo Giometti 			}
159*eae9d2baSRodolfo Giometti 		}
160*eae9d2baSRodolfo Giometti 
161*eae9d2baSRodolfo Giometti 		/* Check for pending signals */
162*eae9d2baSRodolfo Giometti 		if (err == -ERESTARTSYS) {
163*eae9d2baSRodolfo Giometti 			pr_debug("pending signal caught\n");
164*eae9d2baSRodolfo Giometti 			return -EINTR;
165*eae9d2baSRodolfo Giometti 		}
166*eae9d2baSRodolfo Giometti 
167*eae9d2baSRodolfo Giometti 		/* Return the fetched timestamp */
168*eae9d2baSRodolfo Giometti 		spin_lock_irq(&pps->lock);
169*eae9d2baSRodolfo Giometti 
170*eae9d2baSRodolfo Giometti 		fdata.info.assert_sequence = pps->assert_sequence;
171*eae9d2baSRodolfo Giometti 		fdata.info.clear_sequence = pps->clear_sequence;
172*eae9d2baSRodolfo Giometti 		fdata.info.assert_tu = pps->assert_tu;
173*eae9d2baSRodolfo Giometti 		fdata.info.clear_tu = pps->clear_tu;
174*eae9d2baSRodolfo Giometti 		fdata.info.current_mode = pps->current_mode;
175*eae9d2baSRodolfo Giometti 
176*eae9d2baSRodolfo Giometti 		spin_unlock_irq(&pps->lock);
177*eae9d2baSRodolfo Giometti 
178*eae9d2baSRodolfo Giometti 		err = copy_to_user(uarg, &fdata, sizeof(struct pps_fdata));
179*eae9d2baSRodolfo Giometti 		if (err)
180*eae9d2baSRodolfo Giometti 			return -EFAULT;
181*eae9d2baSRodolfo Giometti 
182*eae9d2baSRodolfo Giometti 		break;
183*eae9d2baSRodolfo Giometti 
184*eae9d2baSRodolfo Giometti 	default:
185*eae9d2baSRodolfo Giometti 		return -ENOTTY;
186*eae9d2baSRodolfo Giometti 		break;
187*eae9d2baSRodolfo Giometti 	}
188*eae9d2baSRodolfo Giometti 
189*eae9d2baSRodolfo Giometti 	return 0;
190*eae9d2baSRodolfo Giometti }
191*eae9d2baSRodolfo Giometti 
192*eae9d2baSRodolfo Giometti static int pps_cdev_open(struct inode *inode, struct file *file)
193*eae9d2baSRodolfo Giometti {
194*eae9d2baSRodolfo Giometti 	struct pps_device *pps = container_of(inode->i_cdev,
195*eae9d2baSRodolfo Giometti 						struct pps_device, cdev);
196*eae9d2baSRodolfo Giometti 	int found;
197*eae9d2baSRodolfo Giometti 
198*eae9d2baSRodolfo Giometti 	found = pps_get_source(pps->id) != 0;
199*eae9d2baSRodolfo Giometti 	if (!found)
200*eae9d2baSRodolfo Giometti 		return -ENODEV;
201*eae9d2baSRodolfo Giometti 
202*eae9d2baSRodolfo Giometti 	file->private_data = pps;
203*eae9d2baSRodolfo Giometti 
204*eae9d2baSRodolfo Giometti 	return 0;
205*eae9d2baSRodolfo Giometti }
206*eae9d2baSRodolfo Giometti 
207*eae9d2baSRodolfo Giometti static int pps_cdev_release(struct inode *inode, struct file *file)
208*eae9d2baSRodolfo Giometti {
209*eae9d2baSRodolfo Giometti 	struct pps_device *pps = file->private_data;
210*eae9d2baSRodolfo Giometti 
211*eae9d2baSRodolfo Giometti 	/* Free the PPS source and wake up (possible) deregistration */
212*eae9d2baSRodolfo Giometti 	pps_put_source(pps);
213*eae9d2baSRodolfo Giometti 
214*eae9d2baSRodolfo Giometti 	return 0;
215*eae9d2baSRodolfo Giometti }
216*eae9d2baSRodolfo Giometti 
217*eae9d2baSRodolfo Giometti /*
218*eae9d2baSRodolfo Giometti  * Char device stuff
219*eae9d2baSRodolfo Giometti  */
220*eae9d2baSRodolfo Giometti 
221*eae9d2baSRodolfo Giometti static const struct file_operations pps_cdev_fops = {
222*eae9d2baSRodolfo Giometti 	.owner		= THIS_MODULE,
223*eae9d2baSRodolfo Giometti 	.llseek		= no_llseek,
224*eae9d2baSRodolfo Giometti 	.poll		= pps_cdev_poll,
225*eae9d2baSRodolfo Giometti 	.fasync		= pps_cdev_fasync,
226*eae9d2baSRodolfo Giometti 	.unlocked_ioctl	= pps_cdev_ioctl,
227*eae9d2baSRodolfo Giometti 	.open		= pps_cdev_open,
228*eae9d2baSRodolfo Giometti 	.release	= pps_cdev_release,
229*eae9d2baSRodolfo Giometti };
230*eae9d2baSRodolfo Giometti 
231*eae9d2baSRodolfo Giometti int pps_register_cdev(struct pps_device *pps)
232*eae9d2baSRodolfo Giometti {
233*eae9d2baSRodolfo Giometti 	int err;
234*eae9d2baSRodolfo Giometti 
235*eae9d2baSRodolfo Giometti 	pps->devno = MKDEV(MAJOR(pps_devt), pps->id);
236*eae9d2baSRodolfo Giometti 	cdev_init(&pps->cdev, &pps_cdev_fops);
237*eae9d2baSRodolfo Giometti 	pps->cdev.owner = pps->info.owner;
238*eae9d2baSRodolfo Giometti 
239*eae9d2baSRodolfo Giometti 	err = cdev_add(&pps->cdev, pps->devno, 1);
240*eae9d2baSRodolfo Giometti 	if (err) {
241*eae9d2baSRodolfo Giometti 		printk(KERN_ERR "pps: %s: failed to add char device %d:%d\n",
242*eae9d2baSRodolfo Giometti 				pps->info.name, MAJOR(pps_devt), pps->id);
243*eae9d2baSRodolfo Giometti 		return err;
244*eae9d2baSRodolfo Giometti 	}
245*eae9d2baSRodolfo Giometti 	pps->dev = device_create(pps_class, pps->info.dev, pps->devno, NULL,
246*eae9d2baSRodolfo Giometti 							"pps%d", pps->id);
247*eae9d2baSRodolfo Giometti 	if (err)
248*eae9d2baSRodolfo Giometti 		goto del_cdev;
249*eae9d2baSRodolfo Giometti 	dev_set_drvdata(pps->dev, pps);
250*eae9d2baSRodolfo Giometti 
251*eae9d2baSRodolfo Giometti 	pr_debug("source %s got cdev (%d:%d)\n", pps->info.name,
252*eae9d2baSRodolfo Giometti 			MAJOR(pps_devt), pps->id);
253*eae9d2baSRodolfo Giometti 
254*eae9d2baSRodolfo Giometti 	return 0;
255*eae9d2baSRodolfo Giometti 
256*eae9d2baSRodolfo Giometti del_cdev:
257*eae9d2baSRodolfo Giometti 	cdev_del(&pps->cdev);
258*eae9d2baSRodolfo Giometti 
259*eae9d2baSRodolfo Giometti 	return err;
260*eae9d2baSRodolfo Giometti }
261*eae9d2baSRodolfo Giometti 
262*eae9d2baSRodolfo Giometti void pps_unregister_cdev(struct pps_device *pps)
263*eae9d2baSRodolfo Giometti {
264*eae9d2baSRodolfo Giometti 	device_destroy(pps_class, pps->devno);
265*eae9d2baSRodolfo Giometti 	cdev_del(&pps->cdev);
266*eae9d2baSRodolfo Giometti }
267*eae9d2baSRodolfo Giometti 
268*eae9d2baSRodolfo Giometti /*
269*eae9d2baSRodolfo Giometti  * Module stuff
270*eae9d2baSRodolfo Giometti  */
271*eae9d2baSRodolfo Giometti 
272*eae9d2baSRodolfo Giometti static void __exit pps_exit(void)
273*eae9d2baSRodolfo Giometti {
274*eae9d2baSRodolfo Giometti 	class_destroy(pps_class);
275*eae9d2baSRodolfo Giometti 	unregister_chrdev_region(pps_devt, PPS_MAX_SOURCES);
276*eae9d2baSRodolfo Giometti }
277*eae9d2baSRodolfo Giometti 
278*eae9d2baSRodolfo Giometti static int __init pps_init(void)
279*eae9d2baSRodolfo Giometti {
280*eae9d2baSRodolfo Giometti 	int err;
281*eae9d2baSRodolfo Giometti 
282*eae9d2baSRodolfo Giometti 	pps_class = class_create(THIS_MODULE, "pps");
283*eae9d2baSRodolfo Giometti 	if (!pps_class) {
284*eae9d2baSRodolfo Giometti 		printk(KERN_ERR "pps: failed to allocate class\n");
285*eae9d2baSRodolfo Giometti 		return -ENOMEM;
286*eae9d2baSRodolfo Giometti 	}
287*eae9d2baSRodolfo Giometti 	pps_class->dev_attrs = pps_attrs;
288*eae9d2baSRodolfo Giometti 
289*eae9d2baSRodolfo Giometti 	err = alloc_chrdev_region(&pps_devt, 0, PPS_MAX_SOURCES, "pps");
290*eae9d2baSRodolfo Giometti 	if (err < 0) {
291*eae9d2baSRodolfo Giometti 		printk(KERN_ERR "pps: failed to allocate char device region\n");
292*eae9d2baSRodolfo Giometti 		goto remove_class;
293*eae9d2baSRodolfo Giometti 	}
294*eae9d2baSRodolfo Giometti 
295*eae9d2baSRodolfo Giometti 	pr_info("LinuxPPS API ver. %d registered\n", PPS_API_VERS);
296*eae9d2baSRodolfo Giometti 	pr_info("Software ver. %s - Copyright 2005-2007 Rodolfo Giometti "
297*eae9d2baSRodolfo Giometti 		"<giometti@linux.it>\n", PPS_VERSION);
298*eae9d2baSRodolfo Giometti 
299*eae9d2baSRodolfo Giometti 	return 0;
300*eae9d2baSRodolfo Giometti 
301*eae9d2baSRodolfo Giometti remove_class:
302*eae9d2baSRodolfo Giometti 	class_destroy(pps_class);
303*eae9d2baSRodolfo Giometti 
304*eae9d2baSRodolfo Giometti 	return err;
305*eae9d2baSRodolfo Giometti }
306*eae9d2baSRodolfo Giometti 
307*eae9d2baSRodolfo Giometti subsys_initcall(pps_init);
308*eae9d2baSRodolfo Giometti module_exit(pps_exit);
309*eae9d2baSRodolfo Giometti 
310*eae9d2baSRodolfo Giometti MODULE_AUTHOR("Rodolfo Giometti <giometti@linux.it>");
311*eae9d2baSRodolfo Giometti MODULE_DESCRIPTION("LinuxPPS support (RFC 2783) - ver. " PPS_VERSION);
312*eae9d2baSRodolfo Giometti MODULE_LICENSE("GPL");
313