xref: /openbmc/linux/drivers/pps/kapi.c (revision 083e5866)
1eae9d2baSRodolfo Giometti /*
2eae9d2baSRodolfo Giometti  * kernel API
3eae9d2baSRodolfo Giometti  *
4eae9d2baSRodolfo Giometti  *
5eae9d2baSRodolfo Giometti  * Copyright (C) 2005-2009   Rodolfo Giometti <giometti@linux.it>
6eae9d2baSRodolfo Giometti  *
7eae9d2baSRodolfo Giometti  *   This program is free software; you can redistribute it and/or modify
8eae9d2baSRodolfo Giometti  *   it under the terms of the GNU General Public License as published by
9eae9d2baSRodolfo Giometti  *   the Free Software Foundation; either version 2 of the License, or
10eae9d2baSRodolfo Giometti  *   (at your option) any later version.
11eae9d2baSRodolfo Giometti  *
12eae9d2baSRodolfo Giometti  *   This program is distributed in the hope that it will be useful,
13eae9d2baSRodolfo Giometti  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
14eae9d2baSRodolfo Giometti  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15eae9d2baSRodolfo Giometti  *   GNU General Public License for more details.
16eae9d2baSRodolfo Giometti  *
17eae9d2baSRodolfo Giometti  *   You should have received a copy of the GNU General Public License
18eae9d2baSRodolfo Giometti  *   along with this program; if not, write to the Free Software
19eae9d2baSRodolfo Giometti  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20eae9d2baSRodolfo Giometti  */
21eae9d2baSRodolfo Giometti 
227f7cce74SAlexander Gordeev #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
23eae9d2baSRodolfo Giometti 
24eae9d2baSRodolfo Giometti #include <linux/kernel.h>
25eae9d2baSRodolfo Giometti #include <linux/module.h>
26eae9d2baSRodolfo Giometti #include <linux/init.h>
27eae9d2baSRodolfo Giometti #include <linux/sched.h>
28eae9d2baSRodolfo Giometti #include <linux/time.h>
29eae9d2baSRodolfo Giometti #include <linux/spinlock.h>
30eae9d2baSRodolfo Giometti #include <linux/fs.h>
31eae9d2baSRodolfo Giometti #include <linux/pps_kernel.h>
325a0e3ad6STejun Heo #include <linux/slab.h>
33eae9d2baSRodolfo Giometti 
34eae9d2baSRodolfo Giometti /*
35eae9d2baSRodolfo Giometti  * Local functions
36eae9d2baSRodolfo Giometti  */
37eae9d2baSRodolfo Giometti 
38eae9d2baSRodolfo Giometti static void pps_add_offset(struct pps_ktime *ts, struct pps_ktime *offset)
39eae9d2baSRodolfo Giometti {
40eae9d2baSRodolfo Giometti 	ts->nsec += offset->nsec;
41eae9d2baSRodolfo Giometti 	while (ts->nsec >= NSEC_PER_SEC) {
42eae9d2baSRodolfo Giometti 		ts->nsec -= NSEC_PER_SEC;
43eae9d2baSRodolfo Giometti 		ts->sec++;
44eae9d2baSRodolfo Giometti 	}
45eae9d2baSRodolfo Giometti 	while (ts->nsec < 0) {
46eae9d2baSRodolfo Giometti 		ts->nsec += NSEC_PER_SEC;
47eae9d2baSRodolfo Giometti 		ts->sec--;
48eae9d2baSRodolfo Giometti 	}
49eae9d2baSRodolfo Giometti 	ts->sec += offset->sec;
50eae9d2baSRodolfo Giometti }
51eae9d2baSRodolfo Giometti 
52eae9d2baSRodolfo Giometti /*
53eae9d2baSRodolfo Giometti  * Exported functions
54eae9d2baSRodolfo Giometti  */
55eae9d2baSRodolfo Giometti 
56eae9d2baSRodolfo Giometti /* pps_register_source - add a PPS source in the system
57eae9d2baSRodolfo Giometti  * @info: the PPS info struct
58eae9d2baSRodolfo Giometti  * @default_params: the default PPS parameters of the new source
59eae9d2baSRodolfo Giometti  *
60eae9d2baSRodolfo Giometti  * This function is used to add a new PPS source in the system. The new
61eae9d2baSRodolfo Giometti  * source is described by info's fields and it will have, as default PPS
62eae9d2baSRodolfo Giometti  * parameters, the ones specified into default_params.
63eae9d2baSRodolfo Giometti  *
645e196d34SAlexander Gordeev  * The function returns, in case of success, the PPS device. Otherwise NULL.
65eae9d2baSRodolfo Giometti  */
66eae9d2baSRodolfo Giometti 
675e196d34SAlexander Gordeev struct pps_device *pps_register_source(struct pps_source_info *info,
685e196d34SAlexander Gordeev 		int default_params)
69eae9d2baSRodolfo Giometti {
70eae9d2baSRodolfo Giometti 	struct pps_device *pps;
71eae9d2baSRodolfo Giometti 	int err;
72eae9d2baSRodolfo Giometti 
73eae9d2baSRodolfo Giometti 	/* Sanity checks */
74eae9d2baSRodolfo Giometti 	if ((info->mode & default_params) != default_params) {
757f7cce74SAlexander Gordeev 		pr_err("%s: unsupported default parameters\n",
76eae9d2baSRodolfo Giometti 					info->name);
77eae9d2baSRodolfo Giometti 		err = -EINVAL;
78eae9d2baSRodolfo Giometti 		goto pps_register_source_exit;
79eae9d2baSRodolfo Giometti 	}
80eae9d2baSRodolfo Giometti 	if ((info->mode & (PPS_ECHOASSERT | PPS_ECHOCLEAR)) != 0 &&
81eae9d2baSRodolfo Giometti 			info->echo == NULL) {
827f7cce74SAlexander Gordeev 		pr_err("%s: echo function is not defined\n",
83eae9d2baSRodolfo Giometti 					info->name);
84eae9d2baSRodolfo Giometti 		err = -EINVAL;
85eae9d2baSRodolfo Giometti 		goto pps_register_source_exit;
86eae9d2baSRodolfo Giometti 	}
87eae9d2baSRodolfo Giometti 	if ((info->mode & (PPS_TSFMT_TSPEC | PPS_TSFMT_NTPFP)) == 0) {
887f7cce74SAlexander Gordeev 		pr_err("%s: unspecified time format\n",
89eae9d2baSRodolfo Giometti 					info->name);
90eae9d2baSRodolfo Giometti 		err = -EINVAL;
91eae9d2baSRodolfo Giometti 		goto pps_register_source_exit;
92eae9d2baSRodolfo Giometti 	}
93eae9d2baSRodolfo Giometti 
94eae9d2baSRodolfo Giometti 	/* Allocate memory for the new PPS source struct */
95eae9d2baSRodolfo Giometti 	pps = kzalloc(sizeof(struct pps_device), GFP_KERNEL);
96eae9d2baSRodolfo Giometti 	if (pps == NULL) {
97eae9d2baSRodolfo Giometti 		err = -ENOMEM;
98eae9d2baSRodolfo Giometti 		goto pps_register_source_exit;
99eae9d2baSRodolfo Giometti 	}
100eae9d2baSRodolfo Giometti 
101eae9d2baSRodolfo Giometti 	/* These initializations must be done before calling idr_get_new()
102eae9d2baSRodolfo Giometti 	 * in order to avoid reces into pps_event().
103eae9d2baSRodolfo Giometti 	 */
104eae9d2baSRodolfo Giometti 	pps->params.api_version = PPS_API_VERS;
105eae9d2baSRodolfo Giometti 	pps->params.mode = default_params;
106eae9d2baSRodolfo Giometti 	pps->info = *info;
107eae9d2baSRodolfo Giometti 
108eae9d2baSRodolfo Giometti 	init_waitqueue_head(&pps->queue);
109eae9d2baSRodolfo Giometti 	spin_lock_init(&pps->lock);
110eae9d2baSRodolfo Giometti 
111eae9d2baSRodolfo Giometti 	/* Create the char device */
112eae9d2baSRodolfo Giometti 	err = pps_register_cdev(pps);
113eae9d2baSRodolfo Giometti 	if (err < 0) {
1147f7cce74SAlexander Gordeev 		pr_err("%s: unable to create char device\n",
115eae9d2baSRodolfo Giometti 					info->name);
116083e5866SAlexander Gordeev 		goto kfree_pps;
117eae9d2baSRodolfo Giometti 	}
118eae9d2baSRodolfo Giometti 
1197f7cce74SAlexander Gordeev 	dev_info(pps->dev, "new PPS source %s\n", info->name);
120eae9d2baSRodolfo Giometti 
1215e196d34SAlexander Gordeev 	return pps;
122eae9d2baSRodolfo Giometti 
123eae9d2baSRodolfo Giometti kfree_pps:
124eae9d2baSRodolfo Giometti 	kfree(pps);
125eae9d2baSRodolfo Giometti 
126eae9d2baSRodolfo Giometti pps_register_source_exit:
1277f7cce74SAlexander Gordeev 	pr_err("%s: unable to register source\n", info->name);
128eae9d2baSRodolfo Giometti 
1295e196d34SAlexander Gordeev 	return NULL;
130eae9d2baSRodolfo Giometti }
131eae9d2baSRodolfo Giometti EXPORT_SYMBOL(pps_register_source);
132eae9d2baSRodolfo Giometti 
133eae9d2baSRodolfo Giometti /* pps_unregister_source - remove a PPS source from the system
1345e196d34SAlexander Gordeev  * @pps: the PPS source
135eae9d2baSRodolfo Giometti  *
136eae9d2baSRodolfo Giometti  * This function is used to remove a previously registered PPS source from
137eae9d2baSRodolfo Giometti  * the system.
138eae9d2baSRodolfo Giometti  */
139eae9d2baSRodolfo Giometti 
1405e196d34SAlexander Gordeev void pps_unregister_source(struct pps_device *pps)
141eae9d2baSRodolfo Giometti {
142eae9d2baSRodolfo Giometti 	pps_unregister_cdev(pps);
1435e196d34SAlexander Gordeev 
144083e5866SAlexander Gordeev 	/* don't have to kfree(pps) here because it will be done on
145083e5866SAlexander Gordeev 	 * device destruction */
146eae9d2baSRodolfo Giometti }
147eae9d2baSRodolfo Giometti EXPORT_SYMBOL(pps_unregister_source);
148eae9d2baSRodolfo Giometti 
149eae9d2baSRodolfo Giometti /* pps_event - register a PPS event into the system
1505e196d34SAlexander Gordeev  * @pps: the PPS device
151eae9d2baSRodolfo Giometti  * @ts: the event timestamp
152eae9d2baSRodolfo Giometti  * @event: the event type
153eae9d2baSRodolfo Giometti  * @data: userdef pointer
154eae9d2baSRodolfo Giometti  *
155eae9d2baSRodolfo Giometti  * This function is used by each PPS client in order to register a new
156eae9d2baSRodolfo Giometti  * PPS event into the system (it's usually called inside an IRQ handler).
157eae9d2baSRodolfo Giometti  *
1585e196d34SAlexander Gordeev  * If an echo function is associated with the PPS device it will be called
159eae9d2baSRodolfo Giometti  * as:
1605e196d34SAlexander Gordeev  *	pps->info.echo(pps, event, data);
161eae9d2baSRodolfo Giometti  */
1625e196d34SAlexander Gordeev void pps_event(struct pps_device *pps, struct pps_event_time *ts, int event,
1635e196d34SAlexander Gordeev 		void *data)
164eae9d2baSRodolfo Giometti {
165eae9d2baSRodolfo Giometti 	unsigned long flags;
166276b282eSRodolfo Giometti 	int captured = 0;
1676f4229b5SAlexander Gordeev 	struct pps_ktime ts_real;
168eae9d2baSRodolfo Giometti 
169eae9d2baSRodolfo Giometti 	if ((event & (PPS_CAPTUREASSERT | PPS_CAPTURECLEAR)) == 0) {
1705e196d34SAlexander Gordeev 		dev_err(pps->dev, "unknown event (%x)\n", event);
171eae9d2baSRodolfo Giometti 		return;
172eae9d2baSRodolfo Giometti 	}
173eae9d2baSRodolfo Giometti 
1745e196d34SAlexander Gordeev 	dev_dbg(pps->dev, "PPS event at %ld.%09ld\n",
1755e196d34SAlexander Gordeev 			ts->ts_real.tv_sec, ts->ts_real.tv_nsec);
1766f4229b5SAlexander Gordeev 
1776f4229b5SAlexander Gordeev 	timespec_to_pps_ktime(&ts_real, ts->ts_real);
178eae9d2baSRodolfo Giometti 
179eae9d2baSRodolfo Giometti 	spin_lock_irqsave(&pps->lock, flags);
180eae9d2baSRodolfo Giometti 
181eae9d2baSRodolfo Giometti 	/* Must call the echo function? */
182eae9d2baSRodolfo Giometti 	if ((pps->params.mode & (PPS_ECHOASSERT | PPS_ECHOCLEAR)))
1835e196d34SAlexander Gordeev 		pps->info.echo(pps, event, data);
184eae9d2baSRodolfo Giometti 
185eae9d2baSRodolfo Giometti 	/* Check the event */
186eae9d2baSRodolfo Giometti 	pps->current_mode = pps->params.mode;
187276b282eSRodolfo Giometti 	if ((event & PPS_CAPTUREASSERT) &
188276b282eSRodolfo Giometti 			(pps->params.mode & PPS_CAPTUREASSERT)) {
189eae9d2baSRodolfo Giometti 		/* We have to add an offset? */
190eae9d2baSRodolfo Giometti 		if (pps->params.mode & PPS_OFFSETASSERT)
1916f4229b5SAlexander Gordeev 			pps_add_offset(&ts_real,
1926f4229b5SAlexander Gordeev 					&pps->params.assert_off_tu);
193eae9d2baSRodolfo Giometti 
194eae9d2baSRodolfo Giometti 		/* Save the time stamp */
1956f4229b5SAlexander Gordeev 		pps->assert_tu = ts_real;
196eae9d2baSRodolfo Giometti 		pps->assert_sequence++;
1975e196d34SAlexander Gordeev 		dev_dbg(pps->dev, "capture assert seq #%u\n",
1985e196d34SAlexander Gordeev 			pps->assert_sequence);
199276b282eSRodolfo Giometti 
200276b282eSRodolfo Giometti 		captured = ~0;
201eae9d2baSRodolfo Giometti 	}
202276b282eSRodolfo Giometti 	if ((event & PPS_CAPTURECLEAR) &
203276b282eSRodolfo Giometti 			(pps->params.mode & PPS_CAPTURECLEAR)) {
204eae9d2baSRodolfo Giometti 		/* We have to add an offset? */
205eae9d2baSRodolfo Giometti 		if (pps->params.mode & PPS_OFFSETCLEAR)
2066f4229b5SAlexander Gordeev 			pps_add_offset(&ts_real,
2076f4229b5SAlexander Gordeev 					&pps->params.clear_off_tu);
208eae9d2baSRodolfo Giometti 
209eae9d2baSRodolfo Giometti 		/* Save the time stamp */
2106f4229b5SAlexander Gordeev 		pps->clear_tu = ts_real;
211eae9d2baSRodolfo Giometti 		pps->clear_sequence++;
2125e196d34SAlexander Gordeev 		dev_dbg(pps->dev, "capture clear seq #%u\n",
2135e196d34SAlexander Gordeev 			pps->clear_sequence);
214276b282eSRodolfo Giometti 
215276b282eSRodolfo Giometti 		captured = ~0;
216eae9d2baSRodolfo Giometti 	}
217eae9d2baSRodolfo Giometti 
2187a21a3ccSAlexander Gordeev 	/* Wake up if captured something */
219276b282eSRodolfo Giometti 	if (captured) {
2203003d55bSAlexander Gordeev 		pps->last_ev++;
2213003d55bSAlexander Gordeev 		wake_up_interruptible_all(&pps->queue);
222eae9d2baSRodolfo Giometti 
223eae9d2baSRodolfo Giometti 		kill_fasync(&pps->async_queue, SIGIO, POLL_IN);
224276b282eSRodolfo Giometti 	}
225eae9d2baSRodolfo Giometti 
226eae9d2baSRodolfo Giometti 	spin_unlock_irqrestore(&pps->lock, flags);
227eae9d2baSRodolfo Giometti }
228eae9d2baSRodolfo Giometti EXPORT_SYMBOL(pps_event);
229