1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * dpll_core.c - DPLL subsystem kernel-space interface implementation. |
4 | * |
5 | * Copyright (c) 2023 Meta Platforms, Inc. and affiliates |
6 | * Copyright (c) 2023 Intel Corporation. |
7 | */ |
8 | |
9 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
10 | |
11 | #include <linux/device.h> |
12 | #include <linux/err.h> |
13 | #include <linux/slab.h> |
14 | #include <linux/string.h> |
15 | |
16 | #include "dpll_core.h" |
17 | #include "dpll_netlink.h" |
18 | |
19 | /* Mutex lock to protect DPLL subsystem devices and pins */ |
20 | DEFINE_MUTEX(dpll_lock); |
21 | |
22 | DEFINE_XARRAY_FLAGS(dpll_device_xa, XA_FLAGS_ALLOC); |
23 | DEFINE_XARRAY_FLAGS(dpll_pin_xa, XA_FLAGS_ALLOC); |
24 | |
25 | static u32 dpll_device_xa_id; |
26 | static u32 dpll_pin_xa_id; |
27 | |
28 | #define ASSERT_DPLL_REGISTERED(d) \ |
29 | WARN_ON_ONCE(!xa_get_mark(&dpll_device_xa, (d)->id, DPLL_REGISTERED)) |
30 | #define ASSERT_DPLL_NOT_REGISTERED(d) \ |
31 | WARN_ON_ONCE(xa_get_mark(&dpll_device_xa, (d)->id, DPLL_REGISTERED)) |
32 | #define ASSERT_DPLL_PIN_REGISTERED(p) \ |
33 | WARN_ON_ONCE(!xa_get_mark(&dpll_pin_xa, (p)->id, DPLL_REGISTERED)) |
34 | |
35 | struct dpll_device_registration { |
36 | struct list_head list; |
37 | const struct dpll_device_ops *ops; |
38 | void *priv; |
39 | }; |
40 | |
41 | struct dpll_pin_registration { |
42 | struct list_head list; |
43 | const struct dpll_pin_ops *ops; |
44 | void *priv; |
45 | }; |
46 | |
47 | struct dpll_device *dpll_device_get_by_id(int id) |
48 | { |
49 | if (xa_get_mark(&dpll_device_xa, index: id, DPLL_REGISTERED)) |
50 | return xa_load(&dpll_device_xa, index: id); |
51 | |
52 | return NULL; |
53 | } |
54 | |
55 | static struct dpll_pin_registration * |
56 | dpll_pin_registration_find(struct dpll_pin_ref *ref, |
57 | const struct dpll_pin_ops *ops, void *priv) |
58 | { |
59 | struct dpll_pin_registration *reg; |
60 | |
61 | list_for_each_entry(reg, &ref->registration_list, list) { |
62 | if (reg->ops == ops && reg->priv == priv) |
63 | return reg; |
64 | } |
65 | return NULL; |
66 | } |
67 | |
68 | static int |
69 | dpll_xa_ref_pin_add(struct xarray *xa_pins, struct dpll_pin *pin, |
70 | const struct dpll_pin_ops *ops, void *priv) |
71 | { |
72 | struct dpll_pin_registration *reg; |
73 | struct dpll_pin_ref *ref; |
74 | bool ref_exists = false; |
75 | unsigned long i; |
76 | int ret; |
77 | |
78 | xa_for_each(xa_pins, i, ref) { |
79 | if (ref->pin != pin) |
80 | continue; |
81 | reg = dpll_pin_registration_find(ref, ops, priv); |
82 | if (reg) { |
83 | refcount_inc(r: &ref->refcount); |
84 | return 0; |
85 | } |
86 | ref_exists = true; |
87 | break; |
88 | } |
89 | |
90 | if (!ref_exists) { |
91 | ref = kzalloc(size: sizeof(*ref), GFP_KERNEL); |
92 | if (!ref) |
93 | return -ENOMEM; |
94 | ref->pin = pin; |
95 | INIT_LIST_HEAD(list: &ref->registration_list); |
96 | ret = xa_insert(xa: xa_pins, index: pin->pin_idx, entry: ref, GFP_KERNEL); |
97 | if (ret) { |
98 | kfree(objp: ref); |
99 | return ret; |
100 | } |
101 | refcount_set(r: &ref->refcount, n: 1); |
102 | } |
103 | |
104 | reg = kzalloc(size: sizeof(*reg), GFP_KERNEL); |
105 | if (!reg) { |
106 | if (!ref_exists) { |
107 | xa_erase(xa_pins, index: pin->pin_idx); |
108 | kfree(objp: ref); |
109 | } |
110 | return -ENOMEM; |
111 | } |
112 | reg->ops = ops; |
113 | reg->priv = priv; |
114 | if (ref_exists) |
115 | refcount_inc(r: &ref->refcount); |
116 | list_add_tail(new: ®->list, head: &ref->registration_list); |
117 | |
118 | return 0; |
119 | } |
120 | |
121 | static int dpll_xa_ref_pin_del(struct xarray *xa_pins, struct dpll_pin *pin, |
122 | const struct dpll_pin_ops *ops, void *priv) |
123 | { |
124 | struct dpll_pin_registration *reg; |
125 | struct dpll_pin_ref *ref; |
126 | unsigned long i; |
127 | |
128 | xa_for_each(xa_pins, i, ref) { |
129 | if (ref->pin != pin) |
130 | continue; |
131 | reg = dpll_pin_registration_find(ref, ops, priv); |
132 | if (WARN_ON(!reg)) |
133 | return -EINVAL; |
134 | list_del(entry: ®->list); |
135 | kfree(objp: reg); |
136 | if (refcount_dec_and_test(r: &ref->refcount)) { |
137 | xa_erase(xa_pins, index: i); |
138 | WARN_ON(!list_empty(&ref->registration_list)); |
139 | kfree(objp: ref); |
140 | } |
141 | return 0; |
142 | } |
143 | |
144 | return -EINVAL; |
145 | } |
146 | |
147 | static int |
148 | dpll_xa_ref_dpll_add(struct xarray *xa_dplls, struct dpll_device *dpll, |
149 | const struct dpll_pin_ops *ops, void *priv) |
150 | { |
151 | struct dpll_pin_registration *reg; |
152 | struct dpll_pin_ref *ref; |
153 | bool ref_exists = false; |
154 | unsigned long i; |
155 | int ret; |
156 | |
157 | xa_for_each(xa_dplls, i, ref) { |
158 | if (ref->dpll != dpll) |
159 | continue; |
160 | reg = dpll_pin_registration_find(ref, ops, priv); |
161 | if (reg) { |
162 | refcount_inc(r: &ref->refcount); |
163 | return 0; |
164 | } |
165 | ref_exists = true; |
166 | break; |
167 | } |
168 | |
169 | if (!ref_exists) { |
170 | ref = kzalloc(size: sizeof(*ref), GFP_KERNEL); |
171 | if (!ref) |
172 | return -ENOMEM; |
173 | ref->dpll = dpll; |
174 | INIT_LIST_HEAD(list: &ref->registration_list); |
175 | ret = xa_insert(xa: xa_dplls, index: dpll->id, entry: ref, GFP_KERNEL); |
176 | if (ret) { |
177 | kfree(objp: ref); |
178 | return ret; |
179 | } |
180 | refcount_set(r: &ref->refcount, n: 1); |
181 | } |
182 | |
183 | reg = kzalloc(size: sizeof(*reg), GFP_KERNEL); |
184 | if (!reg) { |
185 | if (!ref_exists) { |
186 | xa_erase(xa_dplls, index: dpll->id); |
187 | kfree(objp: ref); |
188 | } |
189 | return -ENOMEM; |
190 | } |
191 | reg->ops = ops; |
192 | reg->priv = priv; |
193 | if (ref_exists) |
194 | refcount_inc(r: &ref->refcount); |
195 | list_add_tail(new: ®->list, head: &ref->registration_list); |
196 | |
197 | return 0; |
198 | } |
199 | |
200 | static void |
201 | dpll_xa_ref_dpll_del(struct xarray *xa_dplls, struct dpll_device *dpll, |
202 | const struct dpll_pin_ops *ops, void *priv) |
203 | { |
204 | struct dpll_pin_registration *reg; |
205 | struct dpll_pin_ref *ref; |
206 | unsigned long i; |
207 | |
208 | xa_for_each(xa_dplls, i, ref) { |
209 | if (ref->dpll != dpll) |
210 | continue; |
211 | reg = dpll_pin_registration_find(ref, ops, priv); |
212 | if (WARN_ON(!reg)) |
213 | return; |
214 | list_del(entry: ®->list); |
215 | kfree(objp: reg); |
216 | if (refcount_dec_and_test(r: &ref->refcount)) { |
217 | xa_erase(xa_dplls, index: i); |
218 | WARN_ON(!list_empty(&ref->registration_list)); |
219 | kfree(objp: ref); |
220 | } |
221 | return; |
222 | } |
223 | } |
224 | |
225 | struct dpll_pin_ref *dpll_xa_ref_dpll_first(struct xarray *xa_refs) |
226 | { |
227 | struct dpll_pin_ref *ref; |
228 | unsigned long i = 0; |
229 | |
230 | ref = xa_find(xa: xa_refs, index: &i, ULONG_MAX, XA_PRESENT); |
231 | WARN_ON(!ref); |
232 | return ref; |
233 | } |
234 | |
235 | static struct dpll_device * |
236 | dpll_device_alloc(const u64 clock_id, u32 device_idx, struct module *module) |
237 | { |
238 | struct dpll_device *dpll; |
239 | int ret; |
240 | |
241 | dpll = kzalloc(size: sizeof(*dpll), GFP_KERNEL); |
242 | if (!dpll) |
243 | return ERR_PTR(error: -ENOMEM); |
244 | refcount_set(r: &dpll->refcount, n: 1); |
245 | INIT_LIST_HEAD(list: &dpll->registration_list); |
246 | dpll->device_idx = device_idx; |
247 | dpll->clock_id = clock_id; |
248 | dpll->module = module; |
249 | ret = xa_alloc_cyclic(xa: &dpll_device_xa, id: &dpll->id, entry: dpll, xa_limit_32b, |
250 | next: &dpll_device_xa_id, GFP_KERNEL); |
251 | if (ret < 0) { |
252 | kfree(objp: dpll); |
253 | return ERR_PTR(error: ret); |
254 | } |
255 | xa_init_flags(xa: &dpll->pin_refs, XA_FLAGS_ALLOC); |
256 | |
257 | return dpll; |
258 | } |
259 | |
260 | /** |
261 | * dpll_device_get - find existing or create new dpll device |
262 | * @clock_id: clock_id of creator |
263 | * @device_idx: idx given by device driver |
264 | * @module: reference to registering module |
265 | * |
266 | * Get existing object of a dpll device, unique for given arguments. |
267 | * Create new if doesn't exist yet. |
268 | * |
269 | * Context: Acquires a lock (dpll_lock) |
270 | * Return: |
271 | * * valid dpll_device struct pointer if succeeded |
272 | * * ERR_PTR(X) - error |
273 | */ |
274 | struct dpll_device * |
275 | dpll_device_get(u64 clock_id, u32 device_idx, struct module *module) |
276 | { |
277 | struct dpll_device *dpll, *ret = NULL; |
278 | unsigned long index; |
279 | |
280 | mutex_lock(&dpll_lock); |
281 | xa_for_each(&dpll_device_xa, index, dpll) { |
282 | if (dpll->clock_id == clock_id && |
283 | dpll->device_idx == device_idx && |
284 | dpll->module == module) { |
285 | ret = dpll; |
286 | refcount_inc(r: &ret->refcount); |
287 | break; |
288 | } |
289 | } |
290 | if (!ret) |
291 | ret = dpll_device_alloc(clock_id, device_idx, module); |
292 | mutex_unlock(lock: &dpll_lock); |
293 | |
294 | return ret; |
295 | } |
296 | EXPORT_SYMBOL_GPL(dpll_device_get); |
297 | |
298 | /** |
299 | * dpll_device_put - decrease the refcount and free memory if possible |
300 | * @dpll: dpll_device struct pointer |
301 | * |
302 | * Context: Acquires a lock (dpll_lock) |
303 | * Drop reference for a dpll device, if all references are gone, delete |
304 | * dpll device object. |
305 | */ |
306 | void dpll_device_put(struct dpll_device *dpll) |
307 | { |
308 | mutex_lock(&dpll_lock); |
309 | if (refcount_dec_and_test(r: &dpll->refcount)) { |
310 | ASSERT_DPLL_NOT_REGISTERED(dpll); |
311 | WARN_ON_ONCE(!xa_empty(&dpll->pin_refs)); |
312 | xa_destroy(&dpll->pin_refs); |
313 | xa_erase(&dpll_device_xa, index: dpll->id); |
314 | WARN_ON(!list_empty(&dpll->registration_list)); |
315 | kfree(objp: dpll); |
316 | } |
317 | mutex_unlock(lock: &dpll_lock); |
318 | } |
319 | EXPORT_SYMBOL_GPL(dpll_device_put); |
320 | |
321 | static struct dpll_device_registration * |
322 | dpll_device_registration_find(struct dpll_device *dpll, |
323 | const struct dpll_device_ops *ops, void *priv) |
324 | { |
325 | struct dpll_device_registration *reg; |
326 | |
327 | list_for_each_entry(reg, &dpll->registration_list, list) { |
328 | if (reg->ops == ops && reg->priv == priv) |
329 | return reg; |
330 | } |
331 | return NULL; |
332 | } |
333 | |
334 | /** |
335 | * dpll_device_register - register the dpll device in the subsystem |
336 | * @dpll: pointer to a dpll |
337 | * @type: type of a dpll |
338 | * @ops: ops for a dpll device |
339 | * @priv: pointer to private information of owner |
340 | * |
341 | * Make dpll device available for user space. |
342 | * |
343 | * Context: Acquires a lock (dpll_lock) |
344 | * Return: |
345 | * * 0 on success |
346 | * * negative - error value |
347 | */ |
348 | int dpll_device_register(struct dpll_device *dpll, enum dpll_type type, |
349 | const struct dpll_device_ops *ops, void *priv) |
350 | { |
351 | struct dpll_device_registration *reg; |
352 | bool first_registration = false; |
353 | |
354 | if (WARN_ON(!ops)) |
355 | return -EINVAL; |
356 | if (WARN_ON(!ops->mode_get)) |
357 | return -EINVAL; |
358 | if (WARN_ON(!ops->lock_status_get)) |
359 | return -EINVAL; |
360 | if (WARN_ON(type < DPLL_TYPE_PPS || type > DPLL_TYPE_MAX)) |
361 | return -EINVAL; |
362 | |
363 | mutex_lock(&dpll_lock); |
364 | reg = dpll_device_registration_find(dpll, ops, priv); |
365 | if (reg) { |
366 | mutex_unlock(lock: &dpll_lock); |
367 | return -EEXIST; |
368 | } |
369 | |
370 | reg = kzalloc(size: sizeof(*reg), GFP_KERNEL); |
371 | if (!reg) { |
372 | mutex_unlock(lock: &dpll_lock); |
373 | return -ENOMEM; |
374 | } |
375 | reg->ops = ops; |
376 | reg->priv = priv; |
377 | dpll->type = type; |
378 | first_registration = list_empty(head: &dpll->registration_list); |
379 | list_add_tail(new: ®->list, head: &dpll->registration_list); |
380 | if (!first_registration) { |
381 | mutex_unlock(lock: &dpll_lock); |
382 | return 0; |
383 | } |
384 | |
385 | xa_set_mark(&dpll_device_xa, index: dpll->id, DPLL_REGISTERED); |
386 | dpll_device_create_ntf(dpll); |
387 | mutex_unlock(lock: &dpll_lock); |
388 | |
389 | return 0; |
390 | } |
391 | EXPORT_SYMBOL_GPL(dpll_device_register); |
392 | |
393 | /** |
394 | * dpll_device_unregister - unregister dpll device |
395 | * @dpll: registered dpll pointer |
396 | * @ops: ops for a dpll device |
397 | * @priv: pointer to private information of owner |
398 | * |
399 | * Unregister device, make it unavailable for userspace. |
400 | * Note: It does not free the memory |
401 | * Context: Acquires a lock (dpll_lock) |
402 | */ |
403 | void dpll_device_unregister(struct dpll_device *dpll, |
404 | const struct dpll_device_ops *ops, void *priv) |
405 | { |
406 | struct dpll_device_registration *reg; |
407 | |
408 | mutex_lock(&dpll_lock); |
409 | ASSERT_DPLL_REGISTERED(dpll); |
410 | dpll_device_delete_ntf(dpll); |
411 | reg = dpll_device_registration_find(dpll, ops, priv); |
412 | if (WARN_ON(!reg)) { |
413 | mutex_unlock(lock: &dpll_lock); |
414 | return; |
415 | } |
416 | list_del(entry: ®->list); |
417 | kfree(objp: reg); |
418 | |
419 | if (!list_empty(head: &dpll->registration_list)) { |
420 | mutex_unlock(lock: &dpll_lock); |
421 | return; |
422 | } |
423 | xa_clear_mark(&dpll_device_xa, index: dpll->id, DPLL_REGISTERED); |
424 | mutex_unlock(lock: &dpll_lock); |
425 | } |
426 | EXPORT_SYMBOL_GPL(dpll_device_unregister); |
427 | |
428 | static void dpll_pin_prop_free(struct dpll_pin_properties *prop) |
429 | { |
430 | kfree(objp: prop->package_label); |
431 | kfree(objp: prop->panel_label); |
432 | kfree(objp: prop->board_label); |
433 | kfree(objp: prop->freq_supported); |
434 | } |
435 | |
436 | static int dpll_pin_prop_dup(const struct dpll_pin_properties *src, |
437 | struct dpll_pin_properties *dst) |
438 | { |
439 | memcpy(dst, src, sizeof(*dst)); |
440 | if (src->freq_supported && src->freq_supported_num) { |
441 | size_t freq_size = src->freq_supported_num * |
442 | sizeof(*src->freq_supported); |
443 | dst->freq_supported = kmemdup(p: src->freq_supported, |
444 | size: freq_size, GFP_KERNEL); |
445 | if (!src->freq_supported) |
446 | return -ENOMEM; |
447 | } |
448 | if (src->board_label) { |
449 | dst->board_label = kstrdup(s: src->board_label, GFP_KERNEL); |
450 | if (!dst->board_label) |
451 | goto err_board_label; |
452 | } |
453 | if (src->panel_label) { |
454 | dst->panel_label = kstrdup(s: src->panel_label, GFP_KERNEL); |
455 | if (!dst->panel_label) |
456 | goto err_panel_label; |
457 | } |
458 | if (src->package_label) { |
459 | dst->package_label = kstrdup(s: src->package_label, GFP_KERNEL); |
460 | if (!dst->package_label) |
461 | goto err_package_label; |
462 | } |
463 | |
464 | return 0; |
465 | |
466 | err_package_label: |
467 | kfree(objp: dst->panel_label); |
468 | err_panel_label: |
469 | kfree(objp: dst->board_label); |
470 | err_board_label: |
471 | kfree(objp: dst->freq_supported); |
472 | return -ENOMEM; |
473 | } |
474 | |
475 | static struct dpll_pin * |
476 | dpll_pin_alloc(u64 clock_id, u32 pin_idx, struct module *module, |
477 | const struct dpll_pin_properties *prop) |
478 | { |
479 | struct dpll_pin *pin; |
480 | int ret; |
481 | |
482 | pin = kzalloc(size: sizeof(*pin), GFP_KERNEL); |
483 | if (!pin) |
484 | return ERR_PTR(error: -ENOMEM); |
485 | pin->pin_idx = pin_idx; |
486 | pin->clock_id = clock_id; |
487 | pin->module = module; |
488 | if (WARN_ON(prop->type < DPLL_PIN_TYPE_MUX || |
489 | prop->type > DPLL_PIN_TYPE_MAX)) { |
490 | ret = -EINVAL; |
491 | goto err_pin_prop; |
492 | } |
493 | ret = dpll_pin_prop_dup(src: prop, dst: &pin->prop); |
494 | if (ret) |
495 | goto err_pin_prop; |
496 | refcount_set(r: &pin->refcount, n: 1); |
497 | xa_init_flags(xa: &pin->dpll_refs, XA_FLAGS_ALLOC); |
498 | xa_init_flags(xa: &pin->parent_refs, XA_FLAGS_ALLOC); |
499 | ret = xa_alloc_cyclic(xa: &dpll_pin_xa, id: &pin->id, entry: pin, xa_limit_32b, |
500 | next: &dpll_pin_xa_id, GFP_KERNEL); |
501 | if (ret) |
502 | goto err_xa_alloc; |
503 | return pin; |
504 | err_xa_alloc: |
505 | xa_destroy(&pin->dpll_refs); |
506 | xa_destroy(&pin->parent_refs); |
507 | dpll_pin_prop_free(prop: &pin->prop); |
508 | err_pin_prop: |
509 | kfree(objp: pin); |
510 | return ERR_PTR(error: ret); |
511 | } |
512 | |
513 | static void dpll_netdev_pin_assign(struct net_device *dev, struct dpll_pin *dpll_pin) |
514 | { |
515 | rtnl_lock(); |
516 | rcu_assign_pointer(dev->dpll_pin, dpll_pin); |
517 | rtnl_unlock(); |
518 | } |
519 | |
520 | void dpll_netdev_pin_set(struct net_device *dev, struct dpll_pin *dpll_pin) |
521 | { |
522 | WARN_ON(!dpll_pin); |
523 | dpll_netdev_pin_assign(dev, dpll_pin); |
524 | } |
525 | EXPORT_SYMBOL(dpll_netdev_pin_set); |
526 | |
527 | void dpll_netdev_pin_clear(struct net_device *dev) |
528 | { |
529 | dpll_netdev_pin_assign(dev, NULL); |
530 | } |
531 | EXPORT_SYMBOL(dpll_netdev_pin_clear); |
532 | |
533 | /** |
534 | * dpll_pin_get - find existing or create new dpll pin |
535 | * @clock_id: clock_id of creator |
536 | * @pin_idx: idx given by dev driver |
537 | * @module: reference to registering module |
538 | * @prop: dpll pin properties |
539 | * |
540 | * Get existing object of a pin (unique for given arguments) or create new |
541 | * if doesn't exist yet. |
542 | * |
543 | * Context: Acquires a lock (dpll_lock) |
544 | * Return: |
545 | * * valid allocated dpll_pin struct pointer if succeeded |
546 | * * ERR_PTR(X) - error |
547 | */ |
548 | struct dpll_pin * |
549 | dpll_pin_get(u64 clock_id, u32 pin_idx, struct module *module, |
550 | const struct dpll_pin_properties *prop) |
551 | { |
552 | struct dpll_pin *pos, *ret = NULL; |
553 | unsigned long i; |
554 | |
555 | mutex_lock(&dpll_lock); |
556 | xa_for_each(&dpll_pin_xa, i, pos) { |
557 | if (pos->clock_id == clock_id && |
558 | pos->pin_idx == pin_idx && |
559 | pos->module == module) { |
560 | ret = pos; |
561 | refcount_inc(r: &ret->refcount); |
562 | break; |
563 | } |
564 | } |
565 | if (!ret) |
566 | ret = dpll_pin_alloc(clock_id, pin_idx, module, prop); |
567 | mutex_unlock(lock: &dpll_lock); |
568 | |
569 | return ret; |
570 | } |
571 | EXPORT_SYMBOL_GPL(dpll_pin_get); |
572 | |
573 | /** |
574 | * dpll_pin_put - decrease the refcount and free memory if possible |
575 | * @pin: pointer to a pin to be put |
576 | * |
577 | * Drop reference for a pin, if all references are gone, delete pin object. |
578 | * |
579 | * Context: Acquires a lock (dpll_lock) |
580 | */ |
581 | void dpll_pin_put(struct dpll_pin *pin) |
582 | { |
583 | mutex_lock(&dpll_lock); |
584 | if (refcount_dec_and_test(r: &pin->refcount)) { |
585 | xa_erase(&dpll_pin_xa, index: pin->id); |
586 | xa_destroy(&pin->dpll_refs); |
587 | xa_destroy(&pin->parent_refs); |
588 | dpll_pin_prop_free(prop: &pin->prop); |
589 | kfree_rcu(pin, rcu); |
590 | } |
591 | mutex_unlock(lock: &dpll_lock); |
592 | } |
593 | EXPORT_SYMBOL_GPL(dpll_pin_put); |
594 | |
595 | static int |
596 | __dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin, |
597 | const struct dpll_pin_ops *ops, void *priv) |
598 | { |
599 | int ret; |
600 | |
601 | ret = dpll_xa_ref_pin_add(xa_pins: &dpll->pin_refs, pin, ops, priv); |
602 | if (ret) |
603 | return ret; |
604 | ret = dpll_xa_ref_dpll_add(xa_dplls: &pin->dpll_refs, dpll, ops, priv); |
605 | if (ret) |
606 | goto ref_pin_del; |
607 | xa_set_mark(&dpll_pin_xa, index: pin->id, DPLL_REGISTERED); |
608 | dpll_pin_create_ntf(pin); |
609 | |
610 | return ret; |
611 | |
612 | ref_pin_del: |
613 | dpll_xa_ref_pin_del(xa_pins: &dpll->pin_refs, pin, ops, priv); |
614 | return ret; |
615 | } |
616 | |
617 | /** |
618 | * dpll_pin_register - register the dpll pin in the subsystem |
619 | * @dpll: pointer to a dpll |
620 | * @pin: pointer to a dpll pin |
621 | * @ops: ops for a dpll pin ops |
622 | * @priv: pointer to private information of owner |
623 | * |
624 | * Context: Acquires a lock (dpll_lock) |
625 | * Return: |
626 | * * 0 on success |
627 | * * negative - error value |
628 | */ |
629 | int |
630 | dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin, |
631 | const struct dpll_pin_ops *ops, void *priv) |
632 | { |
633 | int ret; |
634 | |
635 | if (WARN_ON(!ops) || |
636 | WARN_ON(!ops->state_on_dpll_get) || |
637 | WARN_ON(!ops->direction_get)) |
638 | return -EINVAL; |
639 | |
640 | mutex_lock(&dpll_lock); |
641 | if (WARN_ON(!(dpll->module == pin->module && |
642 | dpll->clock_id == pin->clock_id))) |
643 | ret = -EINVAL; |
644 | else |
645 | ret = __dpll_pin_register(dpll, pin, ops, priv); |
646 | mutex_unlock(lock: &dpll_lock); |
647 | |
648 | return ret; |
649 | } |
650 | EXPORT_SYMBOL_GPL(dpll_pin_register); |
651 | |
652 | static void |
653 | __dpll_pin_unregister(struct dpll_device *dpll, struct dpll_pin *pin, |
654 | const struct dpll_pin_ops *ops, void *priv) |
655 | { |
656 | ASSERT_DPLL_PIN_REGISTERED(pin); |
657 | dpll_xa_ref_pin_del(xa_pins: &dpll->pin_refs, pin, ops, priv); |
658 | dpll_xa_ref_dpll_del(xa_dplls: &pin->dpll_refs, dpll, ops, priv); |
659 | if (xa_empty(xa: &pin->dpll_refs)) |
660 | xa_clear_mark(&dpll_pin_xa, index: pin->id, DPLL_REGISTERED); |
661 | } |
662 | |
663 | /** |
664 | * dpll_pin_unregister - unregister dpll pin from dpll device |
665 | * @dpll: registered dpll pointer |
666 | * @pin: pointer to a pin |
667 | * @ops: ops for a dpll pin |
668 | * @priv: pointer to private information of owner |
669 | * |
670 | * Note: It does not free the memory |
671 | * Context: Acquires a lock (dpll_lock) |
672 | */ |
673 | void dpll_pin_unregister(struct dpll_device *dpll, struct dpll_pin *pin, |
674 | const struct dpll_pin_ops *ops, void *priv) |
675 | { |
676 | if (WARN_ON(xa_empty(&dpll->pin_refs))) |
677 | return; |
678 | if (WARN_ON(!xa_empty(&pin->parent_refs))) |
679 | return; |
680 | |
681 | mutex_lock(&dpll_lock); |
682 | dpll_pin_delete_ntf(pin); |
683 | __dpll_pin_unregister(dpll, pin, ops, priv); |
684 | mutex_unlock(lock: &dpll_lock); |
685 | } |
686 | EXPORT_SYMBOL_GPL(dpll_pin_unregister); |
687 | |
688 | /** |
689 | * dpll_pin_on_pin_register - register a pin with a parent pin |
690 | * @parent: pointer to a parent pin |
691 | * @pin: pointer to a pin |
692 | * @ops: ops for a dpll pin |
693 | * @priv: pointer to private information of owner |
694 | * |
695 | * Register a pin with a parent pin, create references between them and |
696 | * between newly registered pin and dplls connected with a parent pin. |
697 | * |
698 | * Context: Acquires a lock (dpll_lock) |
699 | * Return: |
700 | * * 0 on success |
701 | * * negative - error value |
702 | */ |
703 | int dpll_pin_on_pin_register(struct dpll_pin *parent, struct dpll_pin *pin, |
704 | const struct dpll_pin_ops *ops, void *priv) |
705 | { |
706 | struct dpll_pin_ref *ref; |
707 | unsigned long i, stop; |
708 | int ret; |
709 | |
710 | if (WARN_ON(parent->prop.type != DPLL_PIN_TYPE_MUX)) |
711 | return -EINVAL; |
712 | |
713 | if (WARN_ON(!ops) || |
714 | WARN_ON(!ops->state_on_pin_get) || |
715 | WARN_ON(!ops->direction_get)) |
716 | return -EINVAL; |
717 | |
718 | mutex_lock(&dpll_lock); |
719 | ret = dpll_xa_ref_pin_add(xa_pins: &pin->parent_refs, pin: parent, ops, priv); |
720 | if (ret) |
721 | goto unlock; |
722 | refcount_inc(r: &pin->refcount); |
723 | xa_for_each(&parent->dpll_refs, i, ref) { |
724 | ret = __dpll_pin_register(dpll: ref->dpll, pin, ops, priv); |
725 | if (ret) { |
726 | stop = i; |
727 | goto dpll_unregister; |
728 | } |
729 | dpll_pin_create_ntf(pin); |
730 | } |
731 | mutex_unlock(lock: &dpll_lock); |
732 | |
733 | return ret; |
734 | |
735 | dpll_unregister: |
736 | xa_for_each(&parent->dpll_refs, i, ref) |
737 | if (i < stop) { |
738 | __dpll_pin_unregister(dpll: ref->dpll, pin, ops, priv); |
739 | dpll_pin_delete_ntf(pin); |
740 | } |
741 | refcount_dec(r: &pin->refcount); |
742 | dpll_xa_ref_pin_del(xa_pins: &pin->parent_refs, pin: parent, ops, priv); |
743 | unlock: |
744 | mutex_unlock(lock: &dpll_lock); |
745 | return ret; |
746 | } |
747 | EXPORT_SYMBOL_GPL(dpll_pin_on_pin_register); |
748 | |
749 | /** |
750 | * dpll_pin_on_pin_unregister - unregister dpll pin from a parent pin |
751 | * @parent: pointer to a parent pin |
752 | * @pin: pointer to a pin |
753 | * @ops: ops for a dpll pin |
754 | * @priv: pointer to private information of owner |
755 | * |
756 | * Context: Acquires a lock (dpll_lock) |
757 | * Note: It does not free the memory |
758 | */ |
759 | void dpll_pin_on_pin_unregister(struct dpll_pin *parent, struct dpll_pin *pin, |
760 | const struct dpll_pin_ops *ops, void *priv) |
761 | { |
762 | struct dpll_pin_ref *ref; |
763 | unsigned long i; |
764 | |
765 | mutex_lock(&dpll_lock); |
766 | dpll_pin_delete_ntf(pin); |
767 | dpll_xa_ref_pin_del(xa_pins: &pin->parent_refs, pin: parent, ops, priv); |
768 | refcount_dec(r: &pin->refcount); |
769 | xa_for_each(&pin->dpll_refs, i, ref) |
770 | __dpll_pin_unregister(dpll: ref->dpll, pin, ops, priv); |
771 | mutex_unlock(lock: &dpll_lock); |
772 | } |
773 | EXPORT_SYMBOL_GPL(dpll_pin_on_pin_unregister); |
774 | |
775 | static struct dpll_device_registration * |
776 | dpll_device_registration_first(struct dpll_device *dpll) |
777 | { |
778 | struct dpll_device_registration *reg; |
779 | |
780 | reg = list_first_entry_or_null((struct list_head *)&dpll->registration_list, |
781 | struct dpll_device_registration, list); |
782 | WARN_ON(!reg); |
783 | return reg; |
784 | } |
785 | |
786 | void *dpll_priv(struct dpll_device *dpll) |
787 | { |
788 | struct dpll_device_registration *reg; |
789 | |
790 | reg = dpll_device_registration_first(dpll); |
791 | return reg->priv; |
792 | } |
793 | |
794 | const struct dpll_device_ops *dpll_device_ops(struct dpll_device *dpll) |
795 | { |
796 | struct dpll_device_registration *reg; |
797 | |
798 | reg = dpll_device_registration_first(dpll); |
799 | return reg->ops; |
800 | } |
801 | |
802 | static struct dpll_pin_registration * |
803 | dpll_pin_registration_first(struct dpll_pin_ref *ref) |
804 | { |
805 | struct dpll_pin_registration *reg; |
806 | |
807 | reg = list_first_entry_or_null(&ref->registration_list, |
808 | struct dpll_pin_registration, list); |
809 | WARN_ON(!reg); |
810 | return reg; |
811 | } |
812 | |
813 | void *dpll_pin_on_dpll_priv(struct dpll_device *dpll, |
814 | struct dpll_pin *pin) |
815 | { |
816 | struct dpll_pin_registration *reg; |
817 | struct dpll_pin_ref *ref; |
818 | |
819 | ref = xa_load(&dpll->pin_refs, index: pin->pin_idx); |
820 | if (!ref) |
821 | return NULL; |
822 | reg = dpll_pin_registration_first(ref); |
823 | return reg->priv; |
824 | } |
825 | |
826 | void *dpll_pin_on_pin_priv(struct dpll_pin *parent, |
827 | struct dpll_pin *pin) |
828 | { |
829 | struct dpll_pin_registration *reg; |
830 | struct dpll_pin_ref *ref; |
831 | |
832 | ref = xa_load(&pin->parent_refs, index: parent->pin_idx); |
833 | if (!ref) |
834 | return NULL; |
835 | reg = dpll_pin_registration_first(ref); |
836 | return reg->priv; |
837 | } |
838 | |
839 | const struct dpll_pin_ops *dpll_pin_ops(struct dpll_pin_ref *ref) |
840 | { |
841 | struct dpll_pin_registration *reg; |
842 | |
843 | reg = dpll_pin_registration_first(ref); |
844 | return reg->ops; |
845 | } |
846 | |
847 | static int __init dpll_init(void) |
848 | { |
849 | int ret; |
850 | |
851 | ret = genl_register_family(family: &dpll_nl_family); |
852 | if (ret) |
853 | goto error; |
854 | |
855 | return 0; |
856 | |
857 | error: |
858 | mutex_destroy(lock: &dpll_lock); |
859 | return ret; |
860 | } |
861 | |
862 | static void __exit dpll_exit(void) |
863 | { |
864 | genl_unregister_family(family: &dpll_nl_family); |
865 | mutex_destroy(lock: &dpll_lock); |
866 | } |
867 | |
868 | subsys_initcall(dpll_init); |
869 | module_exit(dpll_exit); |
870 | |