1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Copyright (c) 2021-2022 NVIDIA Corporation |
4 | * |
5 | * Author: Dipen Patel <dipenp@nvidia.com> |
6 | */ |
7 | |
8 | #include <linux/kernel.h> |
9 | #include <linux/module.h> |
10 | #include <linux/err.h> |
11 | #include <linux/slab.h> |
12 | #include <linux/of.h> |
13 | #include <linux/mutex.h> |
14 | #include <linux/uaccess.h> |
15 | #include <linux/hte.h> |
16 | #include <linux/delay.h> |
17 | #include <linux/debugfs.h> |
18 | #include <linux/device.h> |
19 | |
20 | /* Global list of the HTE devices */ |
21 | static DEFINE_SPINLOCK(hte_lock); |
22 | static LIST_HEAD(hte_devices); |
23 | |
24 | enum { |
25 | HTE_TS_REGISTERED, |
26 | HTE_TS_REQ, |
27 | HTE_TS_DISABLE, |
28 | HTE_TS_QUEUE_WK, |
29 | }; |
30 | |
31 | /** |
32 | * struct hte_ts_info - Information related to requested timestamp. |
33 | * |
34 | * @xlated_id: Timestamp ID as understood between HTE subsys and HTE provider, |
35 | * See xlate callback API. |
36 | * @flags: Flags holding state information. |
37 | * @hte_cb_flags: Callback related flags. |
38 | * @seq: Timestamp sequence counter. |
39 | * @line_name: HTE allocated line name. |
40 | * @free_attr_name: If set, free the attr name. |
41 | * @cb: A nonsleeping callback function provided by clients. |
42 | * @tcb: A secondary sleeping callback function provided by clients. |
43 | * @dropped_ts: Dropped timestamps. |
44 | * @slock: Spin lock to synchronize between disable/enable, |
45 | * request/release APIs. |
46 | * @cb_work: callback workqueue, used when tcb is specified. |
47 | * @req_mlock: Lock during timestamp request/release APIs. |
48 | * @ts_dbg_root: Root for the debug fs. |
49 | * @gdev: HTE abstract device that this timestamp information belongs to. |
50 | * @cl_data: Client specific data. |
51 | */ |
52 | struct hte_ts_info { |
53 | u32 xlated_id; |
54 | unsigned long flags; |
55 | unsigned long hte_cb_flags; |
56 | u64 seq; |
57 | char *line_name; |
58 | bool free_attr_name; |
59 | hte_ts_cb_t cb; |
60 | hte_ts_sec_cb_t tcb; |
61 | atomic_t dropped_ts; |
62 | spinlock_t slock; |
63 | struct work_struct cb_work; |
64 | struct mutex req_mlock; |
65 | struct dentry *ts_dbg_root; |
66 | struct hte_device *gdev; |
67 | void *cl_data; |
68 | }; |
69 | |
70 | /** |
71 | * struct hte_device - HTE abstract device |
72 | * @nlines: Number of entities this device supports. |
73 | * @ts_req: Total number of entities requested. |
74 | * @sdev: Device used at various debug prints. |
75 | * @dbg_root: Root directory for debug fs. |
76 | * @list: List node to store hte_device for each provider. |
77 | * @chip: HTE chip providing this HTE device. |
78 | * @owner: helps prevent removal of modules when in use. |
79 | * @ei: Timestamp information. |
80 | */ |
81 | struct hte_device { |
82 | u32 nlines; |
83 | atomic_t ts_req; |
84 | struct device *sdev; |
85 | struct dentry *dbg_root; |
86 | struct list_head list; |
87 | struct hte_chip *chip; |
88 | struct module *owner; |
89 | struct hte_ts_info ei[] __counted_by(nlines); |
90 | }; |
91 | |
92 | #ifdef CONFIG_DEBUG_FS |
93 | |
94 | static struct dentry *hte_root; |
95 | |
96 | static int __init hte_subsys_dbgfs_init(void) |
97 | { |
98 | /* creates /sys/kernel/debug/hte/ */ |
99 | hte_root = debugfs_create_dir(name: "hte" , NULL); |
100 | |
101 | return 0; |
102 | } |
103 | subsys_initcall(hte_subsys_dbgfs_init); |
104 | |
105 | static void hte_chip_dbgfs_init(struct hte_device *gdev) |
106 | { |
107 | const struct hte_chip *chip = gdev->chip; |
108 | const char *name = chip->name ? chip->name : dev_name(dev: chip->dev); |
109 | |
110 | gdev->dbg_root = debugfs_create_dir(name, parent: hte_root); |
111 | |
112 | debugfs_create_atomic_t(name: "ts_requested" , mode: 0444, parent: gdev->dbg_root, |
113 | value: &gdev->ts_req); |
114 | debugfs_create_u32(name: "total_ts" , mode: 0444, parent: gdev->dbg_root, |
115 | value: &gdev->nlines); |
116 | } |
117 | |
118 | static void hte_ts_dbgfs_init(const char *name, struct hte_ts_info *ei) |
119 | { |
120 | if (!ei->gdev->dbg_root || !name) |
121 | return; |
122 | |
123 | ei->ts_dbg_root = debugfs_create_dir(name, parent: ei->gdev->dbg_root); |
124 | |
125 | debugfs_create_atomic_t(name: "dropped_timestamps" , mode: 0444, parent: ei->ts_dbg_root, |
126 | value: &ei->dropped_ts); |
127 | } |
128 | |
129 | #else |
130 | |
131 | static void hte_chip_dbgfs_init(struct hte_device *gdev) |
132 | { |
133 | } |
134 | |
135 | static void hte_ts_dbgfs_init(const char *name, struct hte_ts_info *ei) |
136 | { |
137 | } |
138 | |
139 | #endif |
140 | |
141 | /** |
142 | * hte_ts_put() - Release and disable timestamp for the given desc. |
143 | * |
144 | * @desc: timestamp descriptor. |
145 | * |
146 | * Context: debugfs_remove_recursive() function call may use sleeping locks, |
147 | * not suitable from atomic context. |
148 | * Returns: 0 on success or a negative error code on failure. |
149 | */ |
150 | int hte_ts_put(struct hte_ts_desc *desc) |
151 | { |
152 | int ret = 0; |
153 | unsigned long flag; |
154 | struct hte_device *gdev; |
155 | struct hte_ts_info *ei; |
156 | |
157 | if (!desc) |
158 | return -EINVAL; |
159 | |
160 | ei = desc->hte_data; |
161 | |
162 | if (!ei || !ei->gdev) |
163 | return -EINVAL; |
164 | |
165 | gdev = ei->gdev; |
166 | |
167 | mutex_lock(&ei->req_mlock); |
168 | |
169 | if (unlikely(!test_bit(HTE_TS_REQ, &ei->flags) && |
170 | !test_bit(HTE_TS_REGISTERED, &ei->flags))) { |
171 | dev_info(gdev->sdev, "id:%d is not requested\n" , |
172 | desc->attr.line_id); |
173 | ret = -EINVAL; |
174 | goto unlock; |
175 | } |
176 | |
177 | if (unlikely(!test_bit(HTE_TS_REQ, &ei->flags) && |
178 | test_bit(HTE_TS_REGISTERED, &ei->flags))) { |
179 | dev_info(gdev->sdev, "id:%d is registered but not requested\n" , |
180 | desc->attr.line_id); |
181 | ret = -EINVAL; |
182 | goto unlock; |
183 | } |
184 | |
185 | if (test_bit(HTE_TS_REQ, &ei->flags) && |
186 | !test_bit(HTE_TS_REGISTERED, &ei->flags)) { |
187 | clear_bit(nr: HTE_TS_REQ, addr: &ei->flags); |
188 | desc->hte_data = NULL; |
189 | ret = 0; |
190 | goto mod_put; |
191 | } |
192 | |
193 | ret = gdev->chip->ops->release(gdev->chip, desc, ei->xlated_id); |
194 | if (ret) { |
195 | dev_err(gdev->sdev, "id: %d free failed\n" , |
196 | desc->attr.line_id); |
197 | goto unlock; |
198 | } |
199 | |
200 | kfree(objp: ei->line_name); |
201 | if (ei->free_attr_name) |
202 | kfree_const(x: desc->attr.name); |
203 | |
204 | debugfs_remove_recursive(dentry: ei->ts_dbg_root); |
205 | |
206 | spin_lock_irqsave(&ei->slock, flag); |
207 | |
208 | if (test_bit(HTE_TS_QUEUE_WK, &ei->flags)) { |
209 | spin_unlock_irqrestore(lock: &ei->slock, flags: flag); |
210 | flush_work(work: &ei->cb_work); |
211 | spin_lock_irqsave(&ei->slock, flag); |
212 | } |
213 | |
214 | atomic_dec(v: &gdev->ts_req); |
215 | atomic_set(v: &ei->dropped_ts, i: 0); |
216 | |
217 | ei->seq = 1; |
218 | ei->flags = 0; |
219 | desc->hte_data = NULL; |
220 | |
221 | spin_unlock_irqrestore(lock: &ei->slock, flags: flag); |
222 | |
223 | ei->cb = NULL; |
224 | ei->tcb = NULL; |
225 | ei->cl_data = NULL; |
226 | |
227 | mod_put: |
228 | module_put(module: gdev->owner); |
229 | unlock: |
230 | mutex_unlock(lock: &ei->req_mlock); |
231 | dev_dbg(gdev->sdev, "release id: %d\n" , desc->attr.line_id); |
232 | |
233 | return ret; |
234 | } |
235 | EXPORT_SYMBOL_GPL(hte_ts_put); |
236 | |
237 | static int hte_ts_dis_en_common(struct hte_ts_desc *desc, bool en) |
238 | { |
239 | u32 ts_id; |
240 | struct hte_device *gdev; |
241 | struct hte_ts_info *ei; |
242 | int ret; |
243 | unsigned long flag; |
244 | |
245 | if (!desc) |
246 | return -EINVAL; |
247 | |
248 | ei = desc->hte_data; |
249 | |
250 | if (!ei || !ei->gdev) |
251 | return -EINVAL; |
252 | |
253 | gdev = ei->gdev; |
254 | ts_id = desc->attr.line_id; |
255 | |
256 | mutex_lock(&ei->req_mlock); |
257 | |
258 | if (!test_bit(HTE_TS_REGISTERED, &ei->flags)) { |
259 | dev_dbg(gdev->sdev, "id:%d is not registered" , ts_id); |
260 | ret = -EUSERS; |
261 | goto out; |
262 | } |
263 | |
264 | spin_lock_irqsave(&ei->slock, flag); |
265 | |
266 | if (en) { |
267 | if (!test_bit(HTE_TS_DISABLE, &ei->flags)) { |
268 | ret = 0; |
269 | goto out_unlock; |
270 | } |
271 | |
272 | spin_unlock_irqrestore(lock: &ei->slock, flags: flag); |
273 | ret = gdev->chip->ops->enable(gdev->chip, ei->xlated_id); |
274 | if (ret) { |
275 | dev_warn(gdev->sdev, "id: %d enable failed\n" , |
276 | ts_id); |
277 | goto out; |
278 | } |
279 | |
280 | spin_lock_irqsave(&ei->slock, flag); |
281 | clear_bit(nr: HTE_TS_DISABLE, addr: &ei->flags); |
282 | } else { |
283 | if (test_bit(HTE_TS_DISABLE, &ei->flags)) { |
284 | ret = 0; |
285 | goto out_unlock; |
286 | } |
287 | |
288 | spin_unlock_irqrestore(lock: &ei->slock, flags: flag); |
289 | ret = gdev->chip->ops->disable(gdev->chip, ei->xlated_id); |
290 | if (ret) { |
291 | dev_warn(gdev->sdev, "id: %d disable failed\n" , |
292 | ts_id); |
293 | goto out; |
294 | } |
295 | |
296 | spin_lock_irqsave(&ei->slock, flag); |
297 | set_bit(nr: HTE_TS_DISABLE, addr: &ei->flags); |
298 | } |
299 | |
300 | out_unlock: |
301 | spin_unlock_irqrestore(lock: &ei->slock, flags: flag); |
302 | out: |
303 | mutex_unlock(lock: &ei->req_mlock); |
304 | return ret; |
305 | } |
306 | |
307 | /** |
308 | * hte_disable_ts() - Disable timestamp on given descriptor. |
309 | * |
310 | * The API does not release any resources associated with desc. |
311 | * |
312 | * @desc: ts descriptor, this is the same as returned by the request API. |
313 | * |
314 | * Context: Holds mutex lock, not suitable from atomic context. |
315 | * Returns: 0 on success or a negative error code on failure. |
316 | */ |
317 | int hte_disable_ts(struct hte_ts_desc *desc) |
318 | { |
319 | return hte_ts_dis_en_common(desc, en: false); |
320 | } |
321 | EXPORT_SYMBOL_GPL(hte_disable_ts); |
322 | |
323 | /** |
324 | * hte_enable_ts() - Enable timestamp on given descriptor. |
325 | * |
326 | * @desc: ts descriptor, this is the same as returned by the request API. |
327 | * |
328 | * Context: Holds mutex lock, not suitable from atomic context. |
329 | * Returns: 0 on success or a negative error code on failure. |
330 | */ |
331 | int hte_enable_ts(struct hte_ts_desc *desc) |
332 | { |
333 | return hte_ts_dis_en_common(desc, en: true); |
334 | } |
335 | EXPORT_SYMBOL_GPL(hte_enable_ts); |
336 | |
337 | static void hte_do_cb_work(struct work_struct *w) |
338 | { |
339 | unsigned long flag; |
340 | struct hte_ts_info *ei = container_of(w, struct hte_ts_info, cb_work); |
341 | |
342 | if (unlikely(!ei->tcb)) |
343 | return; |
344 | |
345 | ei->tcb(ei->cl_data); |
346 | |
347 | spin_lock_irqsave(&ei->slock, flag); |
348 | clear_bit(nr: HTE_TS_QUEUE_WK, addr: &ei->flags); |
349 | spin_unlock_irqrestore(lock: &ei->slock, flags: flag); |
350 | } |
351 | |
352 | static int __hte_req_ts(struct hte_ts_desc *desc, hte_ts_cb_t cb, |
353 | hte_ts_sec_cb_t tcb, void *data) |
354 | { |
355 | int ret; |
356 | struct hte_device *gdev; |
357 | struct hte_ts_info *ei = desc->hte_data; |
358 | |
359 | gdev = ei->gdev; |
360 | /* |
361 | * There is a chance that multiple consumers requesting same entity, |
362 | * lock here. |
363 | */ |
364 | mutex_lock(&ei->req_mlock); |
365 | |
366 | if (test_bit(HTE_TS_REGISTERED, &ei->flags) || |
367 | !test_bit(HTE_TS_REQ, &ei->flags)) { |
368 | dev_dbg(gdev->chip->dev, "id:%u req failed\n" , |
369 | desc->attr.line_id); |
370 | ret = -EUSERS; |
371 | goto unlock; |
372 | } |
373 | |
374 | ei->cb = cb; |
375 | ei->tcb = tcb; |
376 | if (tcb) |
377 | INIT_WORK(&ei->cb_work, hte_do_cb_work); |
378 | |
379 | ret = gdev->chip->ops->request(gdev->chip, desc, ei->xlated_id); |
380 | if (ret < 0) { |
381 | dev_err(gdev->chip->dev, "ts request failed\n" ); |
382 | goto unlock; |
383 | } |
384 | |
385 | ei->cl_data = data; |
386 | ei->seq = 1; |
387 | |
388 | atomic_inc(v: &gdev->ts_req); |
389 | |
390 | if (desc->attr.name) |
391 | ei->line_name = NULL; |
392 | else |
393 | ei->line_name = kasprintf(GFP_KERNEL, fmt: "ts_%u" , desc->attr.line_id); |
394 | |
395 | hte_ts_dbgfs_init(name: desc->attr.name == NULL ? |
396 | ei->line_name : desc->attr.name, ei); |
397 | set_bit(nr: HTE_TS_REGISTERED, addr: &ei->flags); |
398 | |
399 | dev_dbg(gdev->chip->dev, "id: %u, xlated id:%u" , |
400 | desc->attr.line_id, ei->xlated_id); |
401 | |
402 | ret = 0; |
403 | |
404 | unlock: |
405 | mutex_unlock(lock: &ei->req_mlock); |
406 | |
407 | return ret; |
408 | } |
409 | |
410 | static int hte_bind_ts_info_locked(struct hte_ts_info *ei, |
411 | struct hte_ts_desc *desc, u32 x_id) |
412 | { |
413 | int ret = 0; |
414 | |
415 | mutex_lock(&ei->req_mlock); |
416 | |
417 | if (test_bit(HTE_TS_REQ, &ei->flags)) { |
418 | dev_dbg(ei->gdev->chip->dev, "id:%u is already requested\n" , |
419 | desc->attr.line_id); |
420 | ret = -EUSERS; |
421 | goto out; |
422 | } |
423 | |
424 | set_bit(nr: HTE_TS_REQ, addr: &ei->flags); |
425 | desc->hte_data = ei; |
426 | ei->xlated_id = x_id; |
427 | |
428 | out: |
429 | mutex_unlock(lock: &ei->req_mlock); |
430 | |
431 | return ret; |
432 | } |
433 | |
434 | static struct hte_device *of_node_to_htedevice(struct device_node *np) |
435 | { |
436 | struct hte_device *gdev; |
437 | |
438 | spin_lock(lock: &hte_lock); |
439 | |
440 | list_for_each_entry(gdev, &hte_devices, list) |
441 | if (gdev->chip && gdev->chip->dev && |
442 | device_match_of_node(dev: gdev->chip->dev, np)) { |
443 | spin_unlock(lock: &hte_lock); |
444 | return gdev; |
445 | } |
446 | |
447 | spin_unlock(lock: &hte_lock); |
448 | |
449 | return ERR_PTR(error: -ENODEV); |
450 | } |
451 | |
452 | static struct hte_device *hte_find_dev_from_linedata(struct hte_ts_desc *desc) |
453 | { |
454 | struct hte_device *gdev; |
455 | |
456 | spin_lock(lock: &hte_lock); |
457 | |
458 | list_for_each_entry(gdev, &hte_devices, list) |
459 | if (gdev->chip && gdev->chip->match_from_linedata) { |
460 | if (!gdev->chip->match_from_linedata(gdev->chip, desc)) |
461 | continue; |
462 | spin_unlock(lock: &hte_lock); |
463 | return gdev; |
464 | } |
465 | |
466 | spin_unlock(lock: &hte_lock); |
467 | |
468 | return ERR_PTR(error: -ENODEV); |
469 | } |
470 | |
471 | /** |
472 | * of_hte_req_count - Return the number of entities to timestamp. |
473 | * |
474 | * The function returns the total count of the requested entities to timestamp |
475 | * by parsing device tree. |
476 | * |
477 | * @dev: The HTE consumer. |
478 | * |
479 | * Returns: Positive number on success, -ENOENT if no entries, |
480 | * -EINVAL for other errors. |
481 | */ |
482 | int of_hte_req_count(struct device *dev) |
483 | { |
484 | int count; |
485 | |
486 | if (!dev || !dev->of_node) |
487 | return -EINVAL; |
488 | |
489 | count = of_count_phandle_with_args(np: dev->of_node, list_name: "timestamps" , |
490 | cells_name: "#timestamp-cells" ); |
491 | |
492 | return count ? count : -ENOENT; |
493 | } |
494 | EXPORT_SYMBOL_GPL(of_hte_req_count); |
495 | |
496 | static inline struct hte_device *hte_get_dev(struct hte_ts_desc *desc) |
497 | { |
498 | return hte_find_dev_from_linedata(desc); |
499 | } |
500 | |
501 | static struct hte_device *hte_of_get_dev(struct device *dev, |
502 | struct hte_ts_desc *desc, |
503 | int index, |
504 | struct of_phandle_args *args, |
505 | bool *free_name) |
506 | { |
507 | int ret; |
508 | struct device_node *np; |
509 | char *temp; |
510 | |
511 | if (!dev->of_node) |
512 | return ERR_PTR(error: -EINVAL); |
513 | |
514 | np = dev->of_node; |
515 | |
516 | if (!of_property_present(np, propname: "timestamp-names" )) { |
517 | /* Let hte core construct it during request time */ |
518 | desc->attr.name = NULL; |
519 | } else { |
520 | ret = of_property_read_string_index(np, propname: "timestamp-names" , |
521 | index, output: &desc->attr.name); |
522 | if (ret) { |
523 | pr_err("can't parse \"timestamp-names\" property\n" ); |
524 | return ERR_PTR(error: ret); |
525 | } |
526 | *free_name = false; |
527 | if (desc->attr.name) { |
528 | temp = skip_spaces(desc->attr.name); |
529 | if (!*temp) |
530 | desc->attr.name = NULL; |
531 | } |
532 | } |
533 | |
534 | ret = of_parse_phandle_with_args(np, list_name: "timestamps" , cells_name: "#timestamp-cells" , |
535 | index, out_args: args); |
536 | if (ret) { |
537 | pr_err("%s(): can't parse \"timestamps\" property\n" , |
538 | __func__); |
539 | return ERR_PTR(error: ret); |
540 | } |
541 | |
542 | of_node_put(node: args->np); |
543 | |
544 | return of_node_to_htedevice(np: args->np); |
545 | } |
546 | |
547 | /** |
548 | * hte_ts_get() - The function to initialize and obtain HTE desc. |
549 | * |
550 | * The function initializes the consumer provided HTE descriptor. If consumer |
551 | * has device tree node, index is used to parse the line id and other details. |
552 | * The function needs to be called before using any request APIs. |
553 | * |
554 | * @dev: HTE consumer/client device, used in case of parsing device tree node. |
555 | * @desc: Pre-allocated timestamp descriptor. |
556 | * @index: The index will be used as an index to parse line_id from the |
557 | * device tree node if node is present. |
558 | * |
559 | * Context: Holds mutex lock. |
560 | * Returns: Returns 0 on success or negative error code on failure. |
561 | */ |
562 | int hte_ts_get(struct device *dev, struct hte_ts_desc *desc, int index) |
563 | { |
564 | struct hte_device *gdev; |
565 | struct hte_ts_info *ei; |
566 | const struct fwnode_handle *fwnode; |
567 | struct of_phandle_args args; |
568 | u32 xlated_id; |
569 | int ret; |
570 | bool free_name = false; |
571 | |
572 | if (!desc) |
573 | return -EINVAL; |
574 | |
575 | fwnode = dev ? dev_fwnode(dev) : NULL; |
576 | |
577 | if (is_of_node(fwnode)) |
578 | gdev = hte_of_get_dev(dev, desc, index, args: &args, free_name: &free_name); |
579 | else |
580 | gdev = hte_get_dev(desc); |
581 | |
582 | if (IS_ERR(ptr: gdev)) { |
583 | pr_err("%s() no hte dev found\n" , __func__); |
584 | return PTR_ERR(ptr: gdev); |
585 | } |
586 | |
587 | if (!try_module_get(module: gdev->owner)) |
588 | return -ENODEV; |
589 | |
590 | if (!gdev->chip) { |
591 | pr_err("%s(): requested id does not have provider\n" , |
592 | __func__); |
593 | ret = -ENODEV; |
594 | goto put; |
595 | } |
596 | |
597 | if (is_of_node(fwnode)) { |
598 | if (!gdev->chip->xlate_of) |
599 | ret = -EINVAL; |
600 | else |
601 | ret = gdev->chip->xlate_of(gdev->chip, &args, |
602 | desc, &xlated_id); |
603 | } else { |
604 | if (!gdev->chip->xlate_plat) |
605 | ret = -EINVAL; |
606 | else |
607 | ret = gdev->chip->xlate_plat(gdev->chip, desc, |
608 | &xlated_id); |
609 | } |
610 | |
611 | if (ret < 0) |
612 | goto put; |
613 | |
614 | ei = &gdev->ei[xlated_id]; |
615 | |
616 | ret = hte_bind_ts_info_locked(ei, desc, x_id: xlated_id); |
617 | if (ret) |
618 | goto put; |
619 | |
620 | ei->free_attr_name = free_name; |
621 | |
622 | return 0; |
623 | |
624 | put: |
625 | module_put(module: gdev->owner); |
626 | return ret; |
627 | } |
628 | EXPORT_SYMBOL_GPL(hte_ts_get); |
629 | |
630 | static void __devm_hte_release_ts(void *res) |
631 | { |
632 | hte_ts_put(res); |
633 | } |
634 | |
635 | /** |
636 | * hte_request_ts_ns() - The API to request and enable hardware timestamp in |
637 | * nanoseconds. |
638 | * |
639 | * The entity is provider specific for example, GPIO lines, signals, buses |
640 | * etc...The API allocates necessary resources and enables the timestamp. |
641 | * |
642 | * @desc: Pre-allocated and initialized timestamp descriptor. |
643 | * @cb: Callback to push the timestamp data to consumer. |
644 | * @tcb: Optional callback. If its provided, subsystem initializes |
645 | * workqueue. It is called when cb returns HTE_RUN_SECOND_CB. |
646 | * @data: Client data, used during cb and tcb callbacks. |
647 | * |
648 | * Context: Holds mutex lock. |
649 | * Returns: Returns 0 on success or negative error code on failure. |
650 | */ |
651 | int hte_request_ts_ns(struct hte_ts_desc *desc, hte_ts_cb_t cb, |
652 | hte_ts_sec_cb_t tcb, void *data) |
653 | { |
654 | int ret; |
655 | struct hte_ts_info *ei; |
656 | |
657 | if (!desc || !desc->hte_data || !cb) |
658 | return -EINVAL; |
659 | |
660 | ei = desc->hte_data; |
661 | if (!ei || !ei->gdev) |
662 | return -EINVAL; |
663 | |
664 | ret = __hte_req_ts(desc, cb, tcb, data); |
665 | if (ret < 0) { |
666 | dev_err(ei->gdev->chip->dev, |
667 | "failed to request id: %d\n" , desc->attr.line_id); |
668 | return ret; |
669 | } |
670 | |
671 | return 0; |
672 | } |
673 | EXPORT_SYMBOL_GPL(hte_request_ts_ns); |
674 | |
675 | /** |
676 | * devm_hte_request_ts_ns() - Resource managed API to request and enable |
677 | * hardware timestamp in nanoseconds. |
678 | * |
679 | * The entity is provider specific for example, GPIO lines, signals, buses |
680 | * etc...The API allocates necessary resources and enables the timestamp. It |
681 | * deallocates and disables automatically when the consumer exits. |
682 | * |
683 | * @dev: HTE consumer/client device. |
684 | * @desc: Pre-allocated and initialized timestamp descriptor. |
685 | * @cb: Callback to push the timestamp data to consumer. |
686 | * @tcb: Optional callback. If its provided, subsystem initializes |
687 | * workqueue. It is called when cb returns HTE_RUN_SECOND_CB. |
688 | * @data: Client data, used during cb and tcb callbacks. |
689 | * |
690 | * Context: Holds mutex lock. |
691 | * Returns: Returns 0 on success or negative error code on failure. |
692 | */ |
693 | int devm_hte_request_ts_ns(struct device *dev, struct hte_ts_desc *desc, |
694 | hte_ts_cb_t cb, hte_ts_sec_cb_t tcb, |
695 | void *data) |
696 | { |
697 | int err; |
698 | |
699 | if (!dev) |
700 | return -EINVAL; |
701 | |
702 | err = hte_request_ts_ns(desc, cb, tcb, data); |
703 | if (err) |
704 | return err; |
705 | |
706 | err = devm_add_action_or_reset(dev, __devm_hte_release_ts, desc); |
707 | if (err) |
708 | return err; |
709 | |
710 | return 0; |
711 | } |
712 | EXPORT_SYMBOL_GPL(devm_hte_request_ts_ns); |
713 | |
714 | /** |
715 | * hte_init_line_attr() - Initialize line attributes. |
716 | * |
717 | * Zeroes out line attributes and initializes with provided arguments. |
718 | * The function needs to be called before calling any consumer facing |
719 | * functions. |
720 | * |
721 | * @desc: Pre-allocated timestamp descriptor. |
722 | * @line_id: line id. |
723 | * @edge_flags: edge flags related to line_id. |
724 | * @name: name of the line. |
725 | * @data: line data related to line_id. |
726 | * |
727 | * Context: Any. |
728 | * Returns: 0 on success or negative error code for the failure. |
729 | */ |
730 | int hte_init_line_attr(struct hte_ts_desc *desc, u32 line_id, |
731 | unsigned long edge_flags, const char *name, void *data) |
732 | { |
733 | if (!desc) |
734 | return -EINVAL; |
735 | |
736 | memset(&desc->attr, 0, sizeof(desc->attr)); |
737 | |
738 | desc->attr.edge_flags = edge_flags; |
739 | desc->attr.line_id = line_id; |
740 | desc->attr.line_data = data; |
741 | if (name) { |
742 | name = kstrdup_const(s: name, GFP_KERNEL); |
743 | if (!name) |
744 | return -ENOMEM; |
745 | } |
746 | |
747 | desc->attr.name = name; |
748 | |
749 | return 0; |
750 | } |
751 | EXPORT_SYMBOL_GPL(hte_init_line_attr); |
752 | |
753 | /** |
754 | * hte_get_clk_src_info() - Get the clock source information for a ts |
755 | * descriptor. |
756 | * |
757 | * @desc: ts descriptor, same as returned from request API. |
758 | * @ci: The API fills this structure with the clock information data. |
759 | * |
760 | * Context: Any context. |
761 | * Returns: 0 on success else negative error code on failure. |
762 | */ |
763 | int hte_get_clk_src_info(const struct hte_ts_desc *desc, |
764 | struct hte_clk_info *ci) |
765 | { |
766 | struct hte_chip *chip; |
767 | struct hte_ts_info *ei; |
768 | |
769 | if (!desc || !desc->hte_data || !ci) { |
770 | pr_debug("%s:%d\n" , __func__, __LINE__); |
771 | return -EINVAL; |
772 | } |
773 | |
774 | ei = desc->hte_data; |
775 | if (!ei->gdev || !ei->gdev->chip) |
776 | return -EINVAL; |
777 | |
778 | chip = ei->gdev->chip; |
779 | if (!chip->ops->get_clk_src_info) |
780 | return -EOPNOTSUPP; |
781 | |
782 | return chip->ops->get_clk_src_info(chip, ci); |
783 | } |
784 | EXPORT_SYMBOL_GPL(hte_get_clk_src_info); |
785 | |
786 | /** |
787 | * hte_push_ts_ns() - Push timestamp data in nanoseconds. |
788 | * |
789 | * It is used by the provider to push timestamp data. |
790 | * |
791 | * @chip: The HTE chip, used during the registration. |
792 | * @xlated_id: entity id understood by both subsystem and provider, this is |
793 | * obtained from xlate callback during request API. |
794 | * @data: timestamp data. |
795 | * |
796 | * Returns: 0 on success or a negative error code on failure. |
797 | */ |
798 | int hte_push_ts_ns(const struct hte_chip *chip, u32 xlated_id, |
799 | struct hte_ts_data *data) |
800 | { |
801 | enum hte_return ret; |
802 | int st = 0; |
803 | struct hte_ts_info *ei; |
804 | unsigned long flag; |
805 | |
806 | if (!chip || !data || !chip->gdev) |
807 | return -EINVAL; |
808 | |
809 | if (xlated_id >= chip->nlines) |
810 | return -EINVAL; |
811 | |
812 | ei = &chip->gdev->ei[xlated_id]; |
813 | |
814 | spin_lock_irqsave(&ei->slock, flag); |
815 | |
816 | /* timestamp sequence counter */ |
817 | data->seq = ei->seq++; |
818 | |
819 | if (!test_bit(HTE_TS_REGISTERED, &ei->flags) || |
820 | test_bit(HTE_TS_DISABLE, &ei->flags)) { |
821 | dev_dbg(chip->dev, "Unknown timestamp push\n" ); |
822 | atomic_inc(v: &ei->dropped_ts); |
823 | st = -EINVAL; |
824 | goto unlock; |
825 | } |
826 | |
827 | ret = ei->cb(data, ei->cl_data); |
828 | if (ret == HTE_RUN_SECOND_CB && ei->tcb) { |
829 | queue_work(wq: system_unbound_wq, work: &ei->cb_work); |
830 | set_bit(nr: HTE_TS_QUEUE_WK, addr: &ei->flags); |
831 | } |
832 | |
833 | unlock: |
834 | spin_unlock_irqrestore(lock: &ei->slock, flags: flag); |
835 | |
836 | return st; |
837 | } |
838 | EXPORT_SYMBOL_GPL(hte_push_ts_ns); |
839 | |
840 | static int hte_register_chip(struct hte_chip *chip) |
841 | { |
842 | struct hte_device *gdev; |
843 | u32 i; |
844 | |
845 | if (!chip || !chip->dev || !chip->dev->of_node) |
846 | return -EINVAL; |
847 | |
848 | if (!chip->ops || !chip->ops->request || !chip->ops->release) { |
849 | dev_err(chip->dev, "Driver needs to provide ops\n" ); |
850 | return -EINVAL; |
851 | } |
852 | |
853 | gdev = kzalloc(struct_size(gdev, ei, chip->nlines), GFP_KERNEL); |
854 | if (!gdev) |
855 | return -ENOMEM; |
856 | |
857 | gdev->chip = chip; |
858 | chip->gdev = gdev; |
859 | gdev->nlines = chip->nlines; |
860 | gdev->sdev = chip->dev; |
861 | |
862 | for (i = 0; i < chip->nlines; i++) { |
863 | gdev->ei[i].gdev = gdev; |
864 | mutex_init(&gdev->ei[i].req_mlock); |
865 | spin_lock_init(&gdev->ei[i].slock); |
866 | } |
867 | |
868 | if (chip->dev->driver) |
869 | gdev->owner = chip->dev->driver->owner; |
870 | else |
871 | gdev->owner = THIS_MODULE; |
872 | |
873 | of_node_get(node: chip->dev->of_node); |
874 | |
875 | INIT_LIST_HEAD(list: &gdev->list); |
876 | |
877 | spin_lock(lock: &hte_lock); |
878 | list_add_tail(new: &gdev->list, head: &hte_devices); |
879 | spin_unlock(lock: &hte_lock); |
880 | |
881 | hte_chip_dbgfs_init(gdev); |
882 | |
883 | dev_dbg(chip->dev, "Added hte chip\n" ); |
884 | |
885 | return 0; |
886 | } |
887 | |
888 | static int hte_unregister_chip(struct hte_chip *chip) |
889 | { |
890 | struct hte_device *gdev; |
891 | |
892 | if (!chip) |
893 | return -EINVAL; |
894 | |
895 | gdev = chip->gdev; |
896 | |
897 | spin_lock(lock: &hte_lock); |
898 | list_del(entry: &gdev->list); |
899 | spin_unlock(lock: &hte_lock); |
900 | |
901 | gdev->chip = NULL; |
902 | |
903 | of_node_put(node: chip->dev->of_node); |
904 | debugfs_remove_recursive(dentry: gdev->dbg_root); |
905 | kfree(objp: gdev); |
906 | |
907 | dev_dbg(chip->dev, "Removed hte chip\n" ); |
908 | |
909 | return 0; |
910 | } |
911 | |
912 | static void _hte_devm_unregister_chip(void *chip) |
913 | { |
914 | hte_unregister_chip(chip); |
915 | } |
916 | |
917 | /** |
918 | * devm_hte_register_chip() - Resource managed API to register HTE chip. |
919 | * |
920 | * It is used by the provider to register itself with the HTE subsystem. |
921 | * The unregistration is done automatically when the provider exits. |
922 | * |
923 | * @chip: the HTE chip to add to subsystem. |
924 | * |
925 | * Returns: 0 on success or a negative error code on failure. |
926 | */ |
927 | int devm_hte_register_chip(struct hte_chip *chip) |
928 | { |
929 | int err; |
930 | |
931 | err = hte_register_chip(chip); |
932 | if (err) |
933 | return err; |
934 | |
935 | err = devm_add_action_or_reset(chip->dev, _hte_devm_unregister_chip, |
936 | chip); |
937 | if (err) |
938 | return err; |
939 | |
940 | return 0; |
941 | } |
942 | EXPORT_SYMBOL_GPL(devm_hte_register_chip); |
943 | |