Commit 220a60a425146b0e37998cc0b3082f0541aad866
1 parent
ef2c7d7b59
Exists in
smarc-l5.0.0_1.0.0-ga
and in
5 other branches
pps/ptp: Allow PHC devices to adjust PPS events for known delay
Initial version by Stuart Hodgson <smhodgson@solarflare.com> Some PHC device drivers may deliver PPS events with a significant and variable delay, but still be able to measure precisely what that delay is. Add a pps_sub_ts() function for subtracting a delay from the timestamp(s) in a PPS event, and a PTP event type (PTP_CLOCK_PPSUSR) for which the caller provides a complete PPS event. Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
Showing 3 changed files with 22 additions and 2 deletions Inline Diff
drivers/ptp/ptp_clock.c
1 | /* | 1 | /* |
2 | * PTP 1588 clock support | 2 | * PTP 1588 clock support |
3 | * | 3 | * |
4 | * Copyright (C) 2010 OMICRON electronics GmbH | 4 | * Copyright (C) 2010 OMICRON electronics GmbH |
5 | * | 5 | * |
6 | * This program is free software; you can redistribute it and/or modify | 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 | 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 | 8 | * the Free Software Foundation; either version 2 of the License, or |
9 | * (at your option) any later version. | 9 | * (at your option) any later version. |
10 | * | 10 | * |
11 | * This program is distributed in the hope that it will be useful, | 11 | * This program is distributed in the hope that it will be useful, |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | * GNU General Public License for more details. | 14 | * GNU General Public License for more details. |
15 | * | 15 | * |
16 | * You should have received a copy of the GNU General Public License | 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 | 17 | * along with this program; if not, write to the Free Software |
18 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | 18 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
19 | */ | 19 | */ |
20 | #include <linux/bitops.h> | 20 | #include <linux/bitops.h> |
21 | #include <linux/device.h> | 21 | #include <linux/device.h> |
22 | #include <linux/err.h> | 22 | #include <linux/err.h> |
23 | #include <linux/init.h> | 23 | #include <linux/init.h> |
24 | #include <linux/kernel.h> | 24 | #include <linux/kernel.h> |
25 | #include <linux/module.h> | 25 | #include <linux/module.h> |
26 | #include <linux/posix-clock.h> | 26 | #include <linux/posix-clock.h> |
27 | #include <linux/pps_kernel.h> | 27 | #include <linux/pps_kernel.h> |
28 | #include <linux/slab.h> | 28 | #include <linux/slab.h> |
29 | #include <linux/syscalls.h> | 29 | #include <linux/syscalls.h> |
30 | #include <linux/uaccess.h> | 30 | #include <linux/uaccess.h> |
31 | 31 | ||
32 | #include "ptp_private.h" | 32 | #include "ptp_private.h" |
33 | 33 | ||
34 | #define PTP_MAX_ALARMS 4 | 34 | #define PTP_MAX_ALARMS 4 |
35 | #define PTP_MAX_CLOCKS 8 | 35 | #define PTP_MAX_CLOCKS 8 |
36 | #define PTP_PPS_DEFAULTS (PPS_CAPTUREASSERT | PPS_OFFSETASSERT) | 36 | #define PTP_PPS_DEFAULTS (PPS_CAPTUREASSERT | PPS_OFFSETASSERT) |
37 | #define PTP_PPS_EVENT PPS_CAPTUREASSERT | 37 | #define PTP_PPS_EVENT PPS_CAPTUREASSERT |
38 | #define PTP_PPS_MODE (PTP_PPS_DEFAULTS | PPS_CANWAIT | PPS_TSFMT_TSPEC) | 38 | #define PTP_PPS_MODE (PTP_PPS_DEFAULTS | PPS_CANWAIT | PPS_TSFMT_TSPEC) |
39 | 39 | ||
40 | /* private globals */ | 40 | /* private globals */ |
41 | 41 | ||
42 | static dev_t ptp_devt; | 42 | static dev_t ptp_devt; |
43 | static struct class *ptp_class; | 43 | static struct class *ptp_class; |
44 | 44 | ||
45 | static DECLARE_BITMAP(ptp_clocks_map, PTP_MAX_CLOCKS); | 45 | static DECLARE_BITMAP(ptp_clocks_map, PTP_MAX_CLOCKS); |
46 | static DEFINE_MUTEX(ptp_clocks_mutex); /* protects 'ptp_clocks_map' */ | 46 | static DEFINE_MUTEX(ptp_clocks_mutex); /* protects 'ptp_clocks_map' */ |
47 | 47 | ||
48 | /* time stamp event queue operations */ | 48 | /* time stamp event queue operations */ |
49 | 49 | ||
50 | static inline int queue_free(struct timestamp_event_queue *q) | 50 | static inline int queue_free(struct timestamp_event_queue *q) |
51 | { | 51 | { |
52 | return PTP_MAX_TIMESTAMPS - queue_cnt(q) - 1; | 52 | return PTP_MAX_TIMESTAMPS - queue_cnt(q) - 1; |
53 | } | 53 | } |
54 | 54 | ||
55 | static void enqueue_external_timestamp(struct timestamp_event_queue *queue, | 55 | static void enqueue_external_timestamp(struct timestamp_event_queue *queue, |
56 | struct ptp_clock_event *src) | 56 | struct ptp_clock_event *src) |
57 | { | 57 | { |
58 | struct ptp_extts_event *dst; | 58 | struct ptp_extts_event *dst; |
59 | unsigned long flags; | 59 | unsigned long flags; |
60 | s64 seconds; | 60 | s64 seconds; |
61 | u32 remainder; | 61 | u32 remainder; |
62 | 62 | ||
63 | seconds = div_u64_rem(src->timestamp, 1000000000, &remainder); | 63 | seconds = div_u64_rem(src->timestamp, 1000000000, &remainder); |
64 | 64 | ||
65 | spin_lock_irqsave(&queue->lock, flags); | 65 | spin_lock_irqsave(&queue->lock, flags); |
66 | 66 | ||
67 | dst = &queue->buf[queue->tail]; | 67 | dst = &queue->buf[queue->tail]; |
68 | dst->index = src->index; | 68 | dst->index = src->index; |
69 | dst->t.sec = seconds; | 69 | dst->t.sec = seconds; |
70 | dst->t.nsec = remainder; | 70 | dst->t.nsec = remainder; |
71 | 71 | ||
72 | if (!queue_free(queue)) | 72 | if (!queue_free(queue)) |
73 | queue->head = (queue->head + 1) % PTP_MAX_TIMESTAMPS; | 73 | queue->head = (queue->head + 1) % PTP_MAX_TIMESTAMPS; |
74 | 74 | ||
75 | queue->tail = (queue->tail + 1) % PTP_MAX_TIMESTAMPS; | 75 | queue->tail = (queue->tail + 1) % PTP_MAX_TIMESTAMPS; |
76 | 76 | ||
77 | spin_unlock_irqrestore(&queue->lock, flags); | 77 | spin_unlock_irqrestore(&queue->lock, flags); |
78 | } | 78 | } |
79 | 79 | ||
80 | static s32 scaled_ppm_to_ppb(long ppm) | 80 | static s32 scaled_ppm_to_ppb(long ppm) |
81 | { | 81 | { |
82 | /* | 82 | /* |
83 | * The 'freq' field in the 'struct timex' is in parts per | 83 | * The 'freq' field in the 'struct timex' is in parts per |
84 | * million, but with a 16 bit binary fractional field. | 84 | * million, but with a 16 bit binary fractional field. |
85 | * | 85 | * |
86 | * We want to calculate | 86 | * We want to calculate |
87 | * | 87 | * |
88 | * ppb = scaled_ppm * 1000 / 2^16 | 88 | * ppb = scaled_ppm * 1000 / 2^16 |
89 | * | 89 | * |
90 | * which simplifies to | 90 | * which simplifies to |
91 | * | 91 | * |
92 | * ppb = scaled_ppm * 125 / 2^13 | 92 | * ppb = scaled_ppm * 125 / 2^13 |
93 | */ | 93 | */ |
94 | s64 ppb = 1 + ppm; | 94 | s64 ppb = 1 + ppm; |
95 | ppb *= 125; | 95 | ppb *= 125; |
96 | ppb >>= 13; | 96 | ppb >>= 13; |
97 | return (s32) ppb; | 97 | return (s32) ppb; |
98 | } | 98 | } |
99 | 99 | ||
100 | /* posix clock implementation */ | 100 | /* posix clock implementation */ |
101 | 101 | ||
102 | static int ptp_clock_getres(struct posix_clock *pc, struct timespec *tp) | 102 | static int ptp_clock_getres(struct posix_clock *pc, struct timespec *tp) |
103 | { | 103 | { |
104 | tp->tv_sec = 0; | 104 | tp->tv_sec = 0; |
105 | tp->tv_nsec = 1; | 105 | tp->tv_nsec = 1; |
106 | return 0; | 106 | return 0; |
107 | } | 107 | } |
108 | 108 | ||
109 | static int ptp_clock_settime(struct posix_clock *pc, const struct timespec *tp) | 109 | static int ptp_clock_settime(struct posix_clock *pc, const struct timespec *tp) |
110 | { | 110 | { |
111 | struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock); | 111 | struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock); |
112 | return ptp->info->settime(ptp->info, tp); | 112 | return ptp->info->settime(ptp->info, tp); |
113 | } | 113 | } |
114 | 114 | ||
115 | static int ptp_clock_gettime(struct posix_clock *pc, struct timespec *tp) | 115 | static int ptp_clock_gettime(struct posix_clock *pc, struct timespec *tp) |
116 | { | 116 | { |
117 | struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock); | 117 | struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock); |
118 | return ptp->info->gettime(ptp->info, tp); | 118 | return ptp->info->gettime(ptp->info, tp); |
119 | } | 119 | } |
120 | 120 | ||
121 | static int ptp_clock_adjtime(struct posix_clock *pc, struct timex *tx) | 121 | static int ptp_clock_adjtime(struct posix_clock *pc, struct timex *tx) |
122 | { | 122 | { |
123 | struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock); | 123 | struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock); |
124 | struct ptp_clock_info *ops; | 124 | struct ptp_clock_info *ops; |
125 | int err = -EOPNOTSUPP; | 125 | int err = -EOPNOTSUPP; |
126 | 126 | ||
127 | ops = ptp->info; | 127 | ops = ptp->info; |
128 | 128 | ||
129 | if (tx->modes & ADJ_SETOFFSET) { | 129 | if (tx->modes & ADJ_SETOFFSET) { |
130 | struct timespec ts; | 130 | struct timespec ts; |
131 | ktime_t kt; | 131 | ktime_t kt; |
132 | s64 delta; | 132 | s64 delta; |
133 | 133 | ||
134 | ts.tv_sec = tx->time.tv_sec; | 134 | ts.tv_sec = tx->time.tv_sec; |
135 | ts.tv_nsec = tx->time.tv_usec; | 135 | ts.tv_nsec = tx->time.tv_usec; |
136 | 136 | ||
137 | if (!(tx->modes & ADJ_NANO)) | 137 | if (!(tx->modes & ADJ_NANO)) |
138 | ts.tv_nsec *= 1000; | 138 | ts.tv_nsec *= 1000; |
139 | 139 | ||
140 | if ((unsigned long) ts.tv_nsec >= NSEC_PER_SEC) | 140 | if ((unsigned long) ts.tv_nsec >= NSEC_PER_SEC) |
141 | return -EINVAL; | 141 | return -EINVAL; |
142 | 142 | ||
143 | kt = timespec_to_ktime(ts); | 143 | kt = timespec_to_ktime(ts); |
144 | delta = ktime_to_ns(kt); | 144 | delta = ktime_to_ns(kt); |
145 | err = ops->adjtime(ops, delta); | 145 | err = ops->adjtime(ops, delta); |
146 | 146 | ||
147 | } else if (tx->modes & ADJ_FREQUENCY) { | 147 | } else if (tx->modes & ADJ_FREQUENCY) { |
148 | 148 | ||
149 | err = ops->adjfreq(ops, scaled_ppm_to_ppb(tx->freq)); | 149 | err = ops->adjfreq(ops, scaled_ppm_to_ppb(tx->freq)); |
150 | } | 150 | } |
151 | 151 | ||
152 | return err; | 152 | return err; |
153 | } | 153 | } |
154 | 154 | ||
155 | static struct posix_clock_operations ptp_clock_ops = { | 155 | static struct posix_clock_operations ptp_clock_ops = { |
156 | .owner = THIS_MODULE, | 156 | .owner = THIS_MODULE, |
157 | .clock_adjtime = ptp_clock_adjtime, | 157 | .clock_adjtime = ptp_clock_adjtime, |
158 | .clock_gettime = ptp_clock_gettime, | 158 | .clock_gettime = ptp_clock_gettime, |
159 | .clock_getres = ptp_clock_getres, | 159 | .clock_getres = ptp_clock_getres, |
160 | .clock_settime = ptp_clock_settime, | 160 | .clock_settime = ptp_clock_settime, |
161 | .ioctl = ptp_ioctl, | 161 | .ioctl = ptp_ioctl, |
162 | .open = ptp_open, | 162 | .open = ptp_open, |
163 | .poll = ptp_poll, | 163 | .poll = ptp_poll, |
164 | .read = ptp_read, | 164 | .read = ptp_read, |
165 | }; | 165 | }; |
166 | 166 | ||
167 | static void delete_ptp_clock(struct posix_clock *pc) | 167 | static void delete_ptp_clock(struct posix_clock *pc) |
168 | { | 168 | { |
169 | struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock); | 169 | struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock); |
170 | 170 | ||
171 | mutex_destroy(&ptp->tsevq_mux); | 171 | mutex_destroy(&ptp->tsevq_mux); |
172 | 172 | ||
173 | /* Remove the clock from the bit map. */ | 173 | /* Remove the clock from the bit map. */ |
174 | mutex_lock(&ptp_clocks_mutex); | 174 | mutex_lock(&ptp_clocks_mutex); |
175 | clear_bit(ptp->index, ptp_clocks_map); | 175 | clear_bit(ptp->index, ptp_clocks_map); |
176 | mutex_unlock(&ptp_clocks_mutex); | 176 | mutex_unlock(&ptp_clocks_mutex); |
177 | 177 | ||
178 | kfree(ptp); | 178 | kfree(ptp); |
179 | } | 179 | } |
180 | 180 | ||
181 | /* public interface */ | 181 | /* public interface */ |
182 | 182 | ||
183 | struct ptp_clock *ptp_clock_register(struct ptp_clock_info *info) | 183 | struct ptp_clock *ptp_clock_register(struct ptp_clock_info *info) |
184 | { | 184 | { |
185 | struct ptp_clock *ptp; | 185 | struct ptp_clock *ptp; |
186 | int err = 0, index, major = MAJOR(ptp_devt); | 186 | int err = 0, index, major = MAJOR(ptp_devt); |
187 | 187 | ||
188 | if (info->n_alarm > PTP_MAX_ALARMS) | 188 | if (info->n_alarm > PTP_MAX_ALARMS) |
189 | return ERR_PTR(-EINVAL); | 189 | return ERR_PTR(-EINVAL); |
190 | 190 | ||
191 | /* Find a free clock slot and reserve it. */ | 191 | /* Find a free clock slot and reserve it. */ |
192 | err = -EBUSY; | 192 | err = -EBUSY; |
193 | mutex_lock(&ptp_clocks_mutex); | 193 | mutex_lock(&ptp_clocks_mutex); |
194 | index = find_first_zero_bit(ptp_clocks_map, PTP_MAX_CLOCKS); | 194 | index = find_first_zero_bit(ptp_clocks_map, PTP_MAX_CLOCKS); |
195 | if (index < PTP_MAX_CLOCKS) | 195 | if (index < PTP_MAX_CLOCKS) |
196 | set_bit(index, ptp_clocks_map); | 196 | set_bit(index, ptp_clocks_map); |
197 | else | 197 | else |
198 | goto no_slot; | 198 | goto no_slot; |
199 | 199 | ||
200 | /* Initialize a clock structure. */ | 200 | /* Initialize a clock structure. */ |
201 | err = -ENOMEM; | 201 | err = -ENOMEM; |
202 | ptp = kzalloc(sizeof(struct ptp_clock), GFP_KERNEL); | 202 | ptp = kzalloc(sizeof(struct ptp_clock), GFP_KERNEL); |
203 | if (ptp == NULL) | 203 | if (ptp == NULL) |
204 | goto no_memory; | 204 | goto no_memory; |
205 | 205 | ||
206 | ptp->clock.ops = ptp_clock_ops; | 206 | ptp->clock.ops = ptp_clock_ops; |
207 | ptp->clock.release = delete_ptp_clock; | 207 | ptp->clock.release = delete_ptp_clock; |
208 | ptp->info = info; | 208 | ptp->info = info; |
209 | ptp->devid = MKDEV(major, index); | 209 | ptp->devid = MKDEV(major, index); |
210 | ptp->index = index; | 210 | ptp->index = index; |
211 | spin_lock_init(&ptp->tsevq.lock); | 211 | spin_lock_init(&ptp->tsevq.lock); |
212 | mutex_init(&ptp->tsevq_mux); | 212 | mutex_init(&ptp->tsevq_mux); |
213 | init_waitqueue_head(&ptp->tsev_wq); | 213 | init_waitqueue_head(&ptp->tsev_wq); |
214 | 214 | ||
215 | /* Create a new device in our class. */ | 215 | /* Create a new device in our class. */ |
216 | ptp->dev = device_create(ptp_class, NULL, ptp->devid, ptp, | 216 | ptp->dev = device_create(ptp_class, NULL, ptp->devid, ptp, |
217 | "ptp%d", ptp->index); | 217 | "ptp%d", ptp->index); |
218 | if (IS_ERR(ptp->dev)) | 218 | if (IS_ERR(ptp->dev)) |
219 | goto no_device; | 219 | goto no_device; |
220 | 220 | ||
221 | dev_set_drvdata(ptp->dev, ptp); | 221 | dev_set_drvdata(ptp->dev, ptp); |
222 | 222 | ||
223 | err = ptp_populate_sysfs(ptp); | 223 | err = ptp_populate_sysfs(ptp); |
224 | if (err) | 224 | if (err) |
225 | goto no_sysfs; | 225 | goto no_sysfs; |
226 | 226 | ||
227 | /* Register a new PPS source. */ | 227 | /* Register a new PPS source. */ |
228 | if (info->pps) { | 228 | if (info->pps) { |
229 | struct pps_source_info pps; | 229 | struct pps_source_info pps; |
230 | memset(&pps, 0, sizeof(pps)); | 230 | memset(&pps, 0, sizeof(pps)); |
231 | snprintf(pps.name, PPS_MAX_NAME_LEN, "ptp%d", index); | 231 | snprintf(pps.name, PPS_MAX_NAME_LEN, "ptp%d", index); |
232 | pps.mode = PTP_PPS_MODE; | 232 | pps.mode = PTP_PPS_MODE; |
233 | pps.owner = info->owner; | 233 | pps.owner = info->owner; |
234 | ptp->pps_source = pps_register_source(&pps, PTP_PPS_DEFAULTS); | 234 | ptp->pps_source = pps_register_source(&pps, PTP_PPS_DEFAULTS); |
235 | if (!ptp->pps_source) { | 235 | if (!ptp->pps_source) { |
236 | pr_err("failed to register pps source\n"); | 236 | pr_err("failed to register pps source\n"); |
237 | goto no_pps; | 237 | goto no_pps; |
238 | } | 238 | } |
239 | } | 239 | } |
240 | 240 | ||
241 | /* Create a posix clock. */ | 241 | /* Create a posix clock. */ |
242 | err = posix_clock_register(&ptp->clock, ptp->devid); | 242 | err = posix_clock_register(&ptp->clock, ptp->devid); |
243 | if (err) { | 243 | if (err) { |
244 | pr_err("failed to create posix clock\n"); | 244 | pr_err("failed to create posix clock\n"); |
245 | goto no_clock; | 245 | goto no_clock; |
246 | } | 246 | } |
247 | 247 | ||
248 | mutex_unlock(&ptp_clocks_mutex); | 248 | mutex_unlock(&ptp_clocks_mutex); |
249 | return ptp; | 249 | return ptp; |
250 | 250 | ||
251 | no_clock: | 251 | no_clock: |
252 | if (ptp->pps_source) | 252 | if (ptp->pps_source) |
253 | pps_unregister_source(ptp->pps_source); | 253 | pps_unregister_source(ptp->pps_source); |
254 | no_pps: | 254 | no_pps: |
255 | ptp_cleanup_sysfs(ptp); | 255 | ptp_cleanup_sysfs(ptp); |
256 | no_sysfs: | 256 | no_sysfs: |
257 | device_destroy(ptp_class, ptp->devid); | 257 | device_destroy(ptp_class, ptp->devid); |
258 | no_device: | 258 | no_device: |
259 | mutex_destroy(&ptp->tsevq_mux); | 259 | mutex_destroy(&ptp->tsevq_mux); |
260 | kfree(ptp); | 260 | kfree(ptp); |
261 | no_memory: | 261 | no_memory: |
262 | clear_bit(index, ptp_clocks_map); | 262 | clear_bit(index, ptp_clocks_map); |
263 | no_slot: | 263 | no_slot: |
264 | mutex_unlock(&ptp_clocks_mutex); | 264 | mutex_unlock(&ptp_clocks_mutex); |
265 | return ERR_PTR(err); | 265 | return ERR_PTR(err); |
266 | } | 266 | } |
267 | EXPORT_SYMBOL(ptp_clock_register); | 267 | EXPORT_SYMBOL(ptp_clock_register); |
268 | 268 | ||
269 | int ptp_clock_unregister(struct ptp_clock *ptp) | 269 | int ptp_clock_unregister(struct ptp_clock *ptp) |
270 | { | 270 | { |
271 | ptp->defunct = 1; | 271 | ptp->defunct = 1; |
272 | wake_up_interruptible(&ptp->tsev_wq); | 272 | wake_up_interruptible(&ptp->tsev_wq); |
273 | 273 | ||
274 | /* Release the clock's resources. */ | 274 | /* Release the clock's resources. */ |
275 | if (ptp->pps_source) | 275 | if (ptp->pps_source) |
276 | pps_unregister_source(ptp->pps_source); | 276 | pps_unregister_source(ptp->pps_source); |
277 | ptp_cleanup_sysfs(ptp); | 277 | ptp_cleanup_sysfs(ptp); |
278 | device_destroy(ptp_class, ptp->devid); | 278 | device_destroy(ptp_class, ptp->devid); |
279 | 279 | ||
280 | posix_clock_unregister(&ptp->clock); | 280 | posix_clock_unregister(&ptp->clock); |
281 | return 0; | 281 | return 0; |
282 | } | 282 | } |
283 | EXPORT_SYMBOL(ptp_clock_unregister); | 283 | EXPORT_SYMBOL(ptp_clock_unregister); |
284 | 284 | ||
285 | void ptp_clock_event(struct ptp_clock *ptp, struct ptp_clock_event *event) | 285 | void ptp_clock_event(struct ptp_clock *ptp, struct ptp_clock_event *event) |
286 | { | 286 | { |
287 | struct pps_event_time evt; | 287 | struct pps_event_time evt; |
288 | 288 | ||
289 | switch (event->type) { | 289 | switch (event->type) { |
290 | 290 | ||
291 | case PTP_CLOCK_ALARM: | 291 | case PTP_CLOCK_ALARM: |
292 | break; | 292 | break; |
293 | 293 | ||
294 | case PTP_CLOCK_EXTTS: | 294 | case PTP_CLOCK_EXTTS: |
295 | enqueue_external_timestamp(&ptp->tsevq, event); | 295 | enqueue_external_timestamp(&ptp->tsevq, event); |
296 | wake_up_interruptible(&ptp->tsev_wq); | 296 | wake_up_interruptible(&ptp->tsev_wq); |
297 | break; | 297 | break; |
298 | 298 | ||
299 | case PTP_CLOCK_PPS: | 299 | case PTP_CLOCK_PPS: |
300 | pps_get_ts(&evt); | 300 | pps_get_ts(&evt); |
301 | pps_event(ptp->pps_source, &evt, PTP_PPS_EVENT, NULL); | 301 | pps_event(ptp->pps_source, &evt, PTP_PPS_EVENT, NULL); |
302 | break; | 302 | break; |
303 | |||
304 | case PTP_CLOCK_PPSUSR: | ||
305 | pps_event(ptp->pps_source, &event->pps_times, | ||
306 | PTP_PPS_EVENT, NULL); | ||
307 | break; | ||
303 | } | 308 | } |
304 | } | 309 | } |
305 | EXPORT_SYMBOL(ptp_clock_event); | 310 | EXPORT_SYMBOL(ptp_clock_event); |
306 | 311 | ||
307 | int ptp_clock_index(struct ptp_clock *ptp) | 312 | int ptp_clock_index(struct ptp_clock *ptp) |
308 | { | 313 | { |
309 | return ptp->index; | 314 | return ptp->index; |
310 | } | 315 | } |
311 | EXPORT_SYMBOL(ptp_clock_index); | 316 | EXPORT_SYMBOL(ptp_clock_index); |
312 | 317 | ||
313 | /* module operations */ | 318 | /* module operations */ |
314 | 319 | ||
315 | static void __exit ptp_exit(void) | 320 | static void __exit ptp_exit(void) |
316 | { | 321 | { |
317 | class_destroy(ptp_class); | 322 | class_destroy(ptp_class); |
318 | unregister_chrdev_region(ptp_devt, PTP_MAX_CLOCKS); | 323 | unregister_chrdev_region(ptp_devt, PTP_MAX_CLOCKS); |
319 | } | 324 | } |
320 | 325 | ||
321 | static int __init ptp_init(void) | 326 | static int __init ptp_init(void) |
322 | { | 327 | { |
323 | int err; | 328 | int err; |
324 | 329 | ||
325 | ptp_class = class_create(THIS_MODULE, "ptp"); | 330 | ptp_class = class_create(THIS_MODULE, "ptp"); |
326 | if (IS_ERR(ptp_class)) { | 331 | if (IS_ERR(ptp_class)) { |
327 | pr_err("ptp: failed to allocate class\n"); | 332 | pr_err("ptp: failed to allocate class\n"); |
328 | return PTR_ERR(ptp_class); | 333 | return PTR_ERR(ptp_class); |
329 | } | 334 | } |
330 | 335 | ||
331 | err = alloc_chrdev_region(&ptp_devt, 0, PTP_MAX_CLOCKS, "ptp"); | 336 | err = alloc_chrdev_region(&ptp_devt, 0, PTP_MAX_CLOCKS, "ptp"); |
332 | if (err < 0) { | 337 | if (err < 0) { |
333 | pr_err("ptp: failed to allocate device region\n"); | 338 | pr_err("ptp: failed to allocate device region\n"); |
334 | goto no_region; | 339 | goto no_region; |
335 | } | 340 | } |
336 | 341 | ||
337 | ptp_class->dev_attrs = ptp_dev_attrs; | 342 | ptp_class->dev_attrs = ptp_dev_attrs; |
338 | pr_info("PTP clock support registered\n"); | 343 | pr_info("PTP clock support registered\n"); |
339 | return 0; | 344 | return 0; |
340 | 345 | ||
341 | no_region: | 346 | no_region: |
342 | class_destroy(ptp_class); | 347 | class_destroy(ptp_class); |
343 | return err; | 348 | return err; |
344 | } | 349 | } |
345 | 350 | ||
346 | subsys_initcall(ptp_init); | 351 | subsys_initcall(ptp_init); |
347 | module_exit(ptp_exit); | 352 | module_exit(ptp_exit); |
348 | 353 | ||
349 | MODULE_AUTHOR("Richard Cochran <richardcochran@gmail.com>"); | 354 | MODULE_AUTHOR("Richard Cochran <richardcochran@gmail.com>"); |
350 | MODULE_DESCRIPTION("PTP clocks support"); | 355 | MODULE_DESCRIPTION("PTP clocks support"); |
351 | MODULE_LICENSE("GPL"); | 356 | MODULE_LICENSE("GPL"); |
352 | 357 |
include/linux/pps_kernel.h
1 | /* | 1 | /* |
2 | * PPS API kernel header | 2 | * PPS API kernel header |
3 | * | 3 | * |
4 | * Copyright (C) 2009 Rodolfo Giometti <giometti@linux.it> | 4 | * Copyright (C) 2009 Rodolfo Giometti <giometti@linux.it> |
5 | * | 5 | * |
6 | * This program is free software; you can redistribute it and/or modify | 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 | 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 | 8 | * the Free Software Foundation; either version 2 of the License, or |
9 | * (at your option) any later version. | 9 | * (at your option) any later version. |
10 | * | 10 | * |
11 | * This program is distributed in the hope that it will be useful, | 11 | * This program is distributed in the hope that it will be useful, |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | * GNU General Public License for more details. | 14 | * GNU General Public License for more details. |
15 | * | 15 | * |
16 | * You should have received a copy of the GNU General Public License | 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 | 17 | * along with this program; if not, write to the Free Software |
18 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | 18 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
19 | */ | 19 | */ |
20 | 20 | ||
21 | #ifndef LINUX_PPS_KERNEL_H | 21 | #ifndef LINUX_PPS_KERNEL_H |
22 | #define LINUX_PPS_KERNEL_H | 22 | #define LINUX_PPS_KERNEL_H |
23 | 23 | ||
24 | #include <linux/pps.h> | 24 | #include <linux/pps.h> |
25 | 25 | ||
26 | #include <linux/cdev.h> | 26 | #include <linux/cdev.h> |
27 | #include <linux/device.h> | 27 | #include <linux/device.h> |
28 | #include <linux/time.h> | 28 | #include <linux/time.h> |
29 | 29 | ||
30 | /* | 30 | /* |
31 | * Global defines | 31 | * Global defines |
32 | */ | 32 | */ |
33 | 33 | ||
34 | struct pps_device; | 34 | struct pps_device; |
35 | 35 | ||
36 | /* The specific PPS source info */ | 36 | /* The specific PPS source info */ |
37 | struct pps_source_info { | 37 | struct pps_source_info { |
38 | char name[PPS_MAX_NAME_LEN]; /* simbolic name */ | 38 | char name[PPS_MAX_NAME_LEN]; /* simbolic name */ |
39 | char path[PPS_MAX_NAME_LEN]; /* path of connected device */ | 39 | char path[PPS_MAX_NAME_LEN]; /* path of connected device */ |
40 | int mode; /* PPS's allowed mode */ | 40 | int mode; /* PPS's allowed mode */ |
41 | 41 | ||
42 | void (*echo)(struct pps_device *pps, | 42 | void (*echo)(struct pps_device *pps, |
43 | int event, void *data); /* PPS echo function */ | 43 | int event, void *data); /* PPS echo function */ |
44 | 44 | ||
45 | struct module *owner; | 45 | struct module *owner; |
46 | struct device *dev; | 46 | struct device *dev; |
47 | }; | 47 | }; |
48 | 48 | ||
49 | struct pps_event_time { | 49 | struct pps_event_time { |
50 | #ifdef CONFIG_NTP_PPS | 50 | #ifdef CONFIG_NTP_PPS |
51 | struct timespec ts_raw; | 51 | struct timespec ts_raw; |
52 | #endif /* CONFIG_NTP_PPS */ | 52 | #endif /* CONFIG_NTP_PPS */ |
53 | struct timespec ts_real; | 53 | struct timespec ts_real; |
54 | }; | 54 | }; |
55 | 55 | ||
56 | /* The main struct */ | 56 | /* The main struct */ |
57 | struct pps_device { | 57 | struct pps_device { |
58 | struct pps_source_info info; /* PSS source info */ | 58 | struct pps_source_info info; /* PSS source info */ |
59 | 59 | ||
60 | struct pps_kparams params; /* PPS's current params */ | 60 | struct pps_kparams params; /* PPS's current params */ |
61 | 61 | ||
62 | __u32 assert_sequence; /* PPS' assert event seq # */ | 62 | __u32 assert_sequence; /* PPS' assert event seq # */ |
63 | __u32 clear_sequence; /* PPS' clear event seq # */ | 63 | __u32 clear_sequence; /* PPS' clear event seq # */ |
64 | struct pps_ktime assert_tu; | 64 | struct pps_ktime assert_tu; |
65 | struct pps_ktime clear_tu; | 65 | struct pps_ktime clear_tu; |
66 | int current_mode; /* PPS mode at event time */ | 66 | int current_mode; /* PPS mode at event time */ |
67 | 67 | ||
68 | unsigned int last_ev; /* last PPS event id */ | 68 | unsigned int last_ev; /* last PPS event id */ |
69 | wait_queue_head_t queue; /* PPS event queue */ | 69 | wait_queue_head_t queue; /* PPS event queue */ |
70 | 70 | ||
71 | unsigned int id; /* PPS source unique ID */ | 71 | unsigned int id; /* PPS source unique ID */ |
72 | struct cdev cdev; | 72 | struct cdev cdev; |
73 | struct device *dev; | 73 | struct device *dev; |
74 | struct fasync_struct *async_queue; /* fasync method */ | 74 | struct fasync_struct *async_queue; /* fasync method */ |
75 | spinlock_t lock; | 75 | spinlock_t lock; |
76 | }; | 76 | }; |
77 | 77 | ||
78 | /* | 78 | /* |
79 | * Global variables | 79 | * Global variables |
80 | */ | 80 | */ |
81 | 81 | ||
82 | extern struct device_attribute pps_attrs[]; | 82 | extern struct device_attribute pps_attrs[]; |
83 | 83 | ||
84 | /* | 84 | /* |
85 | * Exported functions | 85 | * Exported functions |
86 | */ | 86 | */ |
87 | 87 | ||
88 | extern struct pps_device *pps_register_source( | 88 | extern struct pps_device *pps_register_source( |
89 | struct pps_source_info *info, int default_params); | 89 | struct pps_source_info *info, int default_params); |
90 | extern void pps_unregister_source(struct pps_device *pps); | 90 | extern void pps_unregister_source(struct pps_device *pps); |
91 | extern int pps_register_cdev(struct pps_device *pps); | 91 | extern int pps_register_cdev(struct pps_device *pps); |
92 | extern void pps_unregister_cdev(struct pps_device *pps); | 92 | extern void pps_unregister_cdev(struct pps_device *pps); |
93 | extern void pps_event(struct pps_device *pps, | 93 | extern void pps_event(struct pps_device *pps, |
94 | struct pps_event_time *ts, int event, void *data); | 94 | struct pps_event_time *ts, int event, void *data); |
95 | 95 | ||
96 | static inline void timespec_to_pps_ktime(struct pps_ktime *kt, | 96 | static inline void timespec_to_pps_ktime(struct pps_ktime *kt, |
97 | struct timespec ts) | 97 | struct timespec ts) |
98 | { | 98 | { |
99 | kt->sec = ts.tv_sec; | 99 | kt->sec = ts.tv_sec; |
100 | kt->nsec = ts.tv_nsec; | 100 | kt->nsec = ts.tv_nsec; |
101 | } | 101 | } |
102 | 102 | ||
103 | #ifdef CONFIG_NTP_PPS | 103 | #ifdef CONFIG_NTP_PPS |
104 | 104 | ||
105 | static inline void pps_get_ts(struct pps_event_time *ts) | 105 | static inline void pps_get_ts(struct pps_event_time *ts) |
106 | { | 106 | { |
107 | getnstime_raw_and_real(&ts->ts_raw, &ts->ts_real); | 107 | getnstime_raw_and_real(&ts->ts_raw, &ts->ts_real); |
108 | } | 108 | } |
109 | 109 | ||
110 | #else /* CONFIG_NTP_PPS */ | 110 | #else /* CONFIG_NTP_PPS */ |
111 | 111 | ||
112 | static inline void pps_get_ts(struct pps_event_time *ts) | 112 | static inline void pps_get_ts(struct pps_event_time *ts) |
113 | { | 113 | { |
114 | getnstimeofday(&ts->ts_real); | 114 | getnstimeofday(&ts->ts_real); |
115 | } | 115 | } |
116 | 116 | ||
117 | #endif /* CONFIG_NTP_PPS */ | 117 | #endif /* CONFIG_NTP_PPS */ |
118 | 118 | ||
119 | /* Subtract known time delay from PPS event time(s) */ | ||
120 | static inline void pps_sub_ts(struct pps_event_time *ts, struct timespec delta) | ||
121 | { | ||
122 | ts->ts_real = timespec_sub(ts->ts_real, delta); | ||
123 | #ifdef CONFIG_NTP_PPS | ||
124 | ts->ts_raw = timespec_sub(ts->ts_raw, delta); | ||
125 | #endif | ||
126 | } | ||
127 | |||
119 | #endif /* LINUX_PPS_KERNEL_H */ | 128 | #endif /* LINUX_PPS_KERNEL_H */ |
120 | 129 | ||
121 | 130 |
include/linux/ptp_clock_kernel.h
1 | /* | 1 | /* |
2 | * PTP 1588 clock support | 2 | * PTP 1588 clock support |
3 | * | 3 | * |
4 | * Copyright (C) 2010 OMICRON electronics GmbH | 4 | * Copyright (C) 2010 OMICRON electronics GmbH |
5 | * | 5 | * |
6 | * This program is free software; you can redistribute it and/or modify | 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 | 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 | 8 | * the Free Software Foundation; either version 2 of the License, or |
9 | * (at your option) any later version. | 9 | * (at your option) any later version. |
10 | * | 10 | * |
11 | * This program is distributed in the hope that it will be useful, | 11 | * This program is distributed in the hope that it will be useful, |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | * GNU General Public License for more details. | 14 | * GNU General Public License for more details. |
15 | * | 15 | * |
16 | * You should have received a copy of the GNU General Public License | 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 | 17 | * along with this program; if not, write to the Free Software |
18 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | 18 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
19 | */ | 19 | */ |
20 | 20 | ||
21 | #ifndef _PTP_CLOCK_KERNEL_H_ | 21 | #ifndef _PTP_CLOCK_KERNEL_H_ |
22 | #define _PTP_CLOCK_KERNEL_H_ | 22 | #define _PTP_CLOCK_KERNEL_H_ |
23 | 23 | ||
24 | #include <linux/pps_kernel.h> | ||
24 | #include <linux/ptp_clock.h> | 25 | #include <linux/ptp_clock.h> |
25 | 26 | ||
26 | 27 | ||
27 | struct ptp_clock_request { | 28 | struct ptp_clock_request { |
28 | enum { | 29 | enum { |
29 | PTP_CLK_REQ_EXTTS, | 30 | PTP_CLK_REQ_EXTTS, |
30 | PTP_CLK_REQ_PEROUT, | 31 | PTP_CLK_REQ_PEROUT, |
31 | PTP_CLK_REQ_PPS, | 32 | PTP_CLK_REQ_PPS, |
32 | } type; | 33 | } type; |
33 | union { | 34 | union { |
34 | struct ptp_extts_request extts; | 35 | struct ptp_extts_request extts; |
35 | struct ptp_perout_request perout; | 36 | struct ptp_perout_request perout; |
36 | }; | 37 | }; |
37 | }; | 38 | }; |
38 | 39 | ||
39 | /** | 40 | /** |
40 | * struct ptp_clock_info - decribes a PTP hardware clock | 41 | * struct ptp_clock_info - decribes a PTP hardware clock |
41 | * | 42 | * |
42 | * @owner: The clock driver should set to THIS_MODULE. | 43 | * @owner: The clock driver should set to THIS_MODULE. |
43 | * @name: A short name to identify the clock. | 44 | * @name: A short name to identify the clock. |
44 | * @max_adj: The maximum possible frequency adjustment, in parts per billon. | 45 | * @max_adj: The maximum possible frequency adjustment, in parts per billon. |
45 | * @n_alarm: The number of programmable alarms. | 46 | * @n_alarm: The number of programmable alarms. |
46 | * @n_ext_ts: The number of external time stamp channels. | 47 | * @n_ext_ts: The number of external time stamp channels. |
47 | * @n_per_out: The number of programmable periodic signals. | 48 | * @n_per_out: The number of programmable periodic signals. |
48 | * @pps: Indicates whether the clock supports a PPS callback. | 49 | * @pps: Indicates whether the clock supports a PPS callback. |
49 | * | 50 | * |
50 | * clock operations | 51 | * clock operations |
51 | * | 52 | * |
52 | * @adjfreq: Adjusts the frequency of the hardware clock. | 53 | * @adjfreq: Adjusts the frequency of the hardware clock. |
53 | * parameter delta: Desired period change in parts per billion. | 54 | * parameter delta: Desired period change in parts per billion. |
54 | * | 55 | * |
55 | * @adjtime: Shifts the time of the hardware clock. | 56 | * @adjtime: Shifts the time of the hardware clock. |
56 | * parameter delta: Desired change in nanoseconds. | 57 | * parameter delta: Desired change in nanoseconds. |
57 | * | 58 | * |
58 | * @gettime: Reads the current time from the hardware clock. | 59 | * @gettime: Reads the current time from the hardware clock. |
59 | * parameter ts: Holds the result. | 60 | * parameter ts: Holds the result. |
60 | * | 61 | * |
61 | * @settime: Set the current time on the hardware clock. | 62 | * @settime: Set the current time on the hardware clock. |
62 | * parameter ts: Time value to set. | 63 | * parameter ts: Time value to set. |
63 | * | 64 | * |
64 | * @enable: Request driver to enable or disable an ancillary feature. | 65 | * @enable: Request driver to enable or disable an ancillary feature. |
65 | * parameter request: Desired resource to enable or disable. | 66 | * parameter request: Desired resource to enable or disable. |
66 | * parameter on: Caller passes one to enable or zero to disable. | 67 | * parameter on: Caller passes one to enable or zero to disable. |
67 | * | 68 | * |
68 | * Drivers should embed their ptp_clock_info within a private | 69 | * Drivers should embed their ptp_clock_info within a private |
69 | * structure, obtaining a reference to it using container_of(). | 70 | * structure, obtaining a reference to it using container_of(). |
70 | * | 71 | * |
71 | * The callbacks must all return zero on success, non-zero otherwise. | 72 | * The callbacks must all return zero on success, non-zero otherwise. |
72 | */ | 73 | */ |
73 | 74 | ||
74 | struct ptp_clock_info { | 75 | struct ptp_clock_info { |
75 | struct module *owner; | 76 | struct module *owner; |
76 | char name[16]; | 77 | char name[16]; |
77 | s32 max_adj; | 78 | s32 max_adj; |
78 | int n_alarm; | 79 | int n_alarm; |
79 | int n_ext_ts; | 80 | int n_ext_ts; |
80 | int n_per_out; | 81 | int n_per_out; |
81 | int pps; | 82 | int pps; |
82 | int (*adjfreq)(struct ptp_clock_info *ptp, s32 delta); | 83 | int (*adjfreq)(struct ptp_clock_info *ptp, s32 delta); |
83 | int (*adjtime)(struct ptp_clock_info *ptp, s64 delta); | 84 | int (*adjtime)(struct ptp_clock_info *ptp, s64 delta); |
84 | int (*gettime)(struct ptp_clock_info *ptp, struct timespec *ts); | 85 | int (*gettime)(struct ptp_clock_info *ptp, struct timespec *ts); |
85 | int (*settime)(struct ptp_clock_info *ptp, const struct timespec *ts); | 86 | int (*settime)(struct ptp_clock_info *ptp, const struct timespec *ts); |
86 | int (*enable)(struct ptp_clock_info *ptp, | 87 | int (*enable)(struct ptp_clock_info *ptp, |
87 | struct ptp_clock_request *request, int on); | 88 | struct ptp_clock_request *request, int on); |
88 | }; | 89 | }; |
89 | 90 | ||
90 | struct ptp_clock; | 91 | struct ptp_clock; |
91 | 92 | ||
92 | /** | 93 | /** |
93 | * ptp_clock_register() - register a PTP hardware clock driver | 94 | * ptp_clock_register() - register a PTP hardware clock driver |
94 | * | 95 | * |
95 | * @info: Structure describing the new clock. | 96 | * @info: Structure describing the new clock. |
96 | */ | 97 | */ |
97 | 98 | ||
98 | extern struct ptp_clock *ptp_clock_register(struct ptp_clock_info *info); | 99 | extern struct ptp_clock *ptp_clock_register(struct ptp_clock_info *info); |
99 | 100 | ||
100 | /** | 101 | /** |
101 | * ptp_clock_unregister() - unregister a PTP hardware clock driver | 102 | * ptp_clock_unregister() - unregister a PTP hardware clock driver |
102 | * | 103 | * |
103 | * @ptp: The clock to remove from service. | 104 | * @ptp: The clock to remove from service. |
104 | */ | 105 | */ |
105 | 106 | ||
106 | extern int ptp_clock_unregister(struct ptp_clock *ptp); | 107 | extern int ptp_clock_unregister(struct ptp_clock *ptp); |
107 | 108 | ||
108 | 109 | ||
109 | enum ptp_clock_events { | 110 | enum ptp_clock_events { |
110 | PTP_CLOCK_ALARM, | 111 | PTP_CLOCK_ALARM, |
111 | PTP_CLOCK_EXTTS, | 112 | PTP_CLOCK_EXTTS, |
112 | PTP_CLOCK_PPS, | 113 | PTP_CLOCK_PPS, |
114 | PTP_CLOCK_PPSUSR, | ||
113 | }; | 115 | }; |
114 | 116 | ||
115 | /** | 117 | /** |
116 | * struct ptp_clock_event - decribes a PTP hardware clock event | 118 | * struct ptp_clock_event - decribes a PTP hardware clock event |
117 | * | 119 | * |
118 | * @type: One of the ptp_clock_events enumeration values. | 120 | * @type: One of the ptp_clock_events enumeration values. |
119 | * @index: Identifies the source of the event. | 121 | * @index: Identifies the source of the event. |
120 | * @timestamp: When the event occured. | 122 | * @timestamp: When the event occurred (%PTP_CLOCK_EXTTS only). |
123 | * @pps_times: When the event occurred (%PTP_CLOCK_PPSUSR only). | ||
121 | */ | 124 | */ |
122 | 125 | ||
123 | struct ptp_clock_event { | 126 | struct ptp_clock_event { |
124 | int type; | 127 | int type; |
125 | int index; | 128 | int index; |
126 | u64 timestamp; | 129 | union { |
130 | u64 timestamp; | ||
131 | struct pps_event_time pps_times; | ||
132 | }; | ||
127 | }; | 133 | }; |
128 | 134 | ||
129 | /** | 135 | /** |
130 | * ptp_clock_event() - notify the PTP layer about an event | 136 | * ptp_clock_event() - notify the PTP layer about an event |
131 | * | 137 | * |
132 | * @ptp: The clock obtained from ptp_clock_register(). | 138 | * @ptp: The clock obtained from ptp_clock_register(). |
133 | * @event: Message structure describing the event. | 139 | * @event: Message structure describing the event. |
134 | */ | 140 | */ |
135 | 141 | ||
136 | extern void ptp_clock_event(struct ptp_clock *ptp, | 142 | extern void ptp_clock_event(struct ptp_clock *ptp, |
137 | struct ptp_clock_event *event); | 143 | struct ptp_clock_event *event); |
138 | 144 | ||
139 | /** | 145 | /** |
140 | * ptp_clock_index() - obtain the device index of a PTP clock | 146 | * ptp_clock_index() - obtain the device index of a PTP clock |
141 | * | 147 | * |
142 | * @ptp: The clock obtained from ptp_clock_register(). | 148 | * @ptp: The clock obtained from ptp_clock_register(). |
143 | */ | 149 | */ |
144 | 150 | ||
145 | extern int ptp_clock_index(struct ptp_clock *ptp); | 151 | extern int ptp_clock_index(struct ptp_clock *ptp); |
146 | 152 | ||
147 | #endif | 153 | #endif |
148 | 154 |