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