2 * PPS kernel consumer API
4 * Copyright (C) 2009-2010 Alexander Gordeev <lasaine@lvk.cs.msu.su>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
23 #include <linux/kernel.h>
24 #include <linux/module.h>
25 #include <linux/device.h>
26 #include <linux/init.h>
27 #include <linux/spinlock.h>
28 #include <linux/pps_kernel.h>
36 /* state variables to bind kernel consumer */
37 static DEFINE_SPINLOCK(pps_kc_hardpps_lock
);
38 /* PPS API (RFC 2783): current source and mode for kernel consumer */
39 static struct pps_device
*pps_kc_hardpps_dev
; /* unique pointer to device */
40 static int pps_kc_hardpps_mode
; /* mode bits for kernel consumer */
42 /* pps_kc_bind - control PPS kernel consumer binding
43 * @pps: the PPS source
44 * @bind_args: kernel consumer bind parameters
46 * This function is used to bind or unbind PPS kernel consumer according to
47 * supplied parameters. Should not be called in interrupt context.
49 int pps_kc_bind(struct pps_device
*pps
, struct pps_bind_args
*bind_args
)
51 /* Check if another consumer is already bound */
52 spin_lock_irq(&pps_kc_hardpps_lock
);
54 if (bind_args
->edge
== 0)
55 if (pps_kc_hardpps_dev
== pps
) {
56 pps_kc_hardpps_mode
= 0;
57 pps_kc_hardpps_dev
= NULL
;
58 spin_unlock_irq(&pps_kc_hardpps_lock
);
59 dev_info(pps
->dev
, "unbound kernel"
62 spin_unlock_irq(&pps_kc_hardpps_lock
);
63 dev_err(pps
->dev
, "selected kernel consumer"
68 if (pps_kc_hardpps_dev
== NULL
||
69 pps_kc_hardpps_dev
== pps
) {
70 pps_kc_hardpps_mode
= bind_args
->edge
;
71 pps_kc_hardpps_dev
= pps
;
72 spin_unlock_irq(&pps_kc_hardpps_lock
);
73 dev_info(pps
->dev
, "bound kernel consumer: "
74 "edge=0x%x\n", bind_args
->edge
);
76 spin_unlock_irq(&pps_kc_hardpps_lock
);
77 dev_err(pps
->dev
, "another kernel consumer"
78 " is already bound\n");
85 /* pps_kc_remove - unbind kernel consumer on PPS source removal
86 * @pps: the PPS source
88 * This function is used to disable kernel consumer on PPS source removal
89 * if this source was bound to PPS kernel consumer. Can be called on any
90 * source safely. Should not be called in interrupt context.
92 void pps_kc_remove(struct pps_device
*pps
)
94 spin_lock_irq(&pps_kc_hardpps_lock
);
95 if (pps
== pps_kc_hardpps_dev
) {
96 pps_kc_hardpps_mode
= 0;
97 pps_kc_hardpps_dev
= NULL
;
98 spin_unlock_irq(&pps_kc_hardpps_lock
);
99 dev_info(pps
->dev
, "unbound kernel consumer"
100 " on device removal\n");
102 spin_unlock_irq(&pps_kc_hardpps_lock
);
105 /* pps_kc_event - call hardpps() on PPS event
106 * @pps: the PPS source
107 * @ts: PPS event timestamp
108 * @event: PPS event edge
110 * This function calls hardpps() when an event from bound PPS source occurs.
112 void pps_kc_event(struct pps_device
*pps
, struct pps_event_time
*ts
,
117 /* Pass some events to kernel consumer if activated */
118 spin_lock_irqsave(&pps_kc_hardpps_lock
, flags
);
119 if (pps
== pps_kc_hardpps_dev
&& event
& pps_kc_hardpps_mode
)
120 hardpps(&ts
->ts_real
, &ts
->ts_raw
);
121 spin_unlock_irqrestore(&pps_kc_hardpps_lock
, flags
);