Merge tag 'x86-asm-2024-03-11' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
[linux-2.6-block.git] / drivers / dpll / dpll_netlink.c
CommitLineData
9d71b54b
VF
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Generic netlink for DPLL management framework
4 *
5 * Copyright (c) 2023 Meta Platforms, Inc. and affiliates
6 * Copyright (c) 2023 Intel and affiliates
7 *
8 */
9#include <linux/module.h>
10#include <linux/kernel.h>
289e9225 11#include <linux/netdevice.h>
9d71b54b
VF
12#include <net/genetlink.h>
13#include "dpll_core.h"
14#include "dpll_netlink.h"
15#include "dpll_nl.h"
16#include <uapi/linux/dpll.h>
17
18#define ASSERT_NOT_NULL(ptr) (WARN_ON(!ptr))
19
20#define xa_for_each_marked_start(xa, index, entry, filter, start) \
21 for (index = start, entry = xa_find(xa, &index, ULONG_MAX, filter); \
22 entry; entry = xa_find_after(xa, &index, ULONG_MAX, filter))
23
24struct dpll_dump_ctx {
25 unsigned long idx;
26};
27
28static struct dpll_dump_ctx *dpll_dump_context(struct netlink_callback *cb)
29{
30 return (struct dpll_dump_ctx *)cb->ctx;
31}
32
33static int
34dpll_msg_add_dev_handle(struct sk_buff *msg, struct dpll_device *dpll)
35{
36 if (nla_put_u32(msg, DPLL_A_ID, dpll->id))
37 return -EMSGSIZE;
38
39 return 0;
40}
41
42static int
43dpll_msg_add_dev_parent_handle(struct sk_buff *msg, u32 id)
44{
45 if (nla_put_u32(msg, DPLL_A_PIN_PARENT_ID, id))
46 return -EMSGSIZE;
47
48 return 0;
49}
50
51/**
52 * dpll_msg_add_pin_handle - attach pin handle attribute to a given message
53 * @msg: pointer to sk_buff message to attach a pin handle
54 * @pin: pin pointer
55 *
56 * Return:
57 * * 0 - success
58 * * -EMSGSIZE - no space in message to attach pin handle
59 */
289e9225 60static int dpll_msg_add_pin_handle(struct sk_buff *msg, struct dpll_pin *pin)
9d71b54b
VF
61{
62 if (!pin)
63 return 0;
64 if (nla_put_u32(msg, DPLL_A_PIN_ID, pin->id))
65 return -EMSGSIZE;
66 return 0;
67}
289e9225
JK
68
69static struct dpll_pin *dpll_netdev_pin(const struct net_device *dev)
70{
71 return rcu_dereference_rtnl(dev->dpll_pin);
72}
73
74/**
75 * dpll_netdev_pin_handle_size - get size of pin handle attribute of a netdev
76 * @dev: netdev from which to get the pin
77 *
78 * Return: byte size of pin handle attribute, or 0 if @dev has no pin.
79 */
80size_t dpll_netdev_pin_handle_size(const struct net_device *dev)
81{
82 return dpll_netdev_pin(dev) ? nla_total_size(4) : 0; /* DPLL_A_PIN_ID */
83}
84
85int dpll_netdev_add_pin_handle(struct sk_buff *msg,
86 const struct net_device *dev)
87{
88 return dpll_msg_add_pin_handle(msg, dpll_netdev_pin(dev));
89}
9d71b54b
VF
90
91static int
92dpll_msg_add_mode(struct sk_buff *msg, struct dpll_device *dpll,
93 struct netlink_ext_ack *extack)
94{
95 const struct dpll_device_ops *ops = dpll_device_ops(dpll);
96 enum dpll_mode mode;
97 int ret;
98
99 ret = ops->mode_get(dpll, dpll_priv(dpll), &mode, extack);
100 if (ret)
101 return ret;
102 if (nla_put_u32(msg, DPLL_A_MODE, mode))
103 return -EMSGSIZE;
104
105 return 0;
106}
107
108static int
109dpll_msg_add_mode_supported(struct sk_buff *msg, struct dpll_device *dpll,
110 struct netlink_ext_ack *extack)
111{
112 const struct dpll_device_ops *ops = dpll_device_ops(dpll);
113 enum dpll_mode mode;
4f7aa122 114 int ret;
9d71b54b 115
4f7aa122
JP
116 /* No mode change is supported now, so the only supported mode is the
117 * one obtained by mode_get().
118 */
119
120 ret = ops->mode_get(dpll, dpll_priv(dpll), &mode, extack);
121 if (ret)
122 return ret;
123 if (nla_put_u32(msg, DPLL_A_MODE_SUPPORTED, mode))
124 return -EMSGSIZE;
9d71b54b
VF
125
126 return 0;
127}
128
129static int
130dpll_msg_add_lock_status(struct sk_buff *msg, struct dpll_device *dpll,
131 struct netlink_ext_ack *extack)
132{
133 const struct dpll_device_ops *ops = dpll_device_ops(dpll);
134 enum dpll_lock_status status;
135 int ret;
136
137 ret = ops->lock_status_get(dpll, dpll_priv(dpll), &status, extack);
138 if (ret)
139 return ret;
140 if (nla_put_u32(msg, DPLL_A_LOCK_STATUS, status))
141 return -EMSGSIZE;
142
143 return 0;
144}
145
146static int
147dpll_msg_add_temp(struct sk_buff *msg, struct dpll_device *dpll,
148 struct netlink_ext_ack *extack)
149{
150 const struct dpll_device_ops *ops = dpll_device_ops(dpll);
151 s32 temp;
152 int ret;
153
154 if (!ops->temp_get)
155 return 0;
156 ret = ops->temp_get(dpll, dpll_priv(dpll), &temp, extack);
157 if (ret)
158 return ret;
159 if (nla_put_s32(msg, DPLL_A_TEMP, temp))
160 return -EMSGSIZE;
161
162 return 0;
163}
164
165static int
166dpll_msg_add_pin_prio(struct sk_buff *msg, struct dpll_pin *pin,
167 struct dpll_pin_ref *ref,
168 struct netlink_ext_ack *extack)
169{
170 const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
171 struct dpll_device *dpll = ref->dpll;
172 u32 prio;
173 int ret;
174
175 if (!ops->prio_get)
176 return 0;
177 ret = ops->prio_get(pin, dpll_pin_on_dpll_priv(dpll, pin), dpll,
178 dpll_priv(dpll), &prio, extack);
179 if (ret)
180 return ret;
181 if (nla_put_u32(msg, DPLL_A_PIN_PRIO, prio))
182 return -EMSGSIZE;
183
184 return 0;
185}
186
187static int
188dpll_msg_add_pin_on_dpll_state(struct sk_buff *msg, struct dpll_pin *pin,
189 struct dpll_pin_ref *ref,
190 struct netlink_ext_ack *extack)
191{
192 const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
193 struct dpll_device *dpll = ref->dpll;
194 enum dpll_pin_state state;
195 int ret;
196
197 if (!ops->state_on_dpll_get)
198 return 0;
199 ret = ops->state_on_dpll_get(pin, dpll_pin_on_dpll_priv(dpll, pin),
200 dpll, dpll_priv(dpll), &state, extack);
201 if (ret)
202 return ret;
203 if (nla_put_u32(msg, DPLL_A_PIN_STATE, state))
204 return -EMSGSIZE;
205
206 return 0;
207}
208
209static int
210dpll_msg_add_pin_direction(struct sk_buff *msg, struct dpll_pin *pin,
211 struct dpll_pin_ref *ref,
212 struct netlink_ext_ack *extack)
213{
214 const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
215 struct dpll_device *dpll = ref->dpll;
216 enum dpll_pin_direction direction;
217 int ret;
218
219 ret = ops->direction_get(pin, dpll_pin_on_dpll_priv(dpll, pin), dpll,
220 dpll_priv(dpll), &direction, extack);
221 if (ret)
222 return ret;
223 if (nla_put_u32(msg, DPLL_A_PIN_DIRECTION, direction))
224 return -EMSGSIZE;
225
226 return 0;
227}
228
d7fbc0b7
AK
229static int
230dpll_msg_add_pin_phase_adjust(struct sk_buff *msg, struct dpll_pin *pin,
231 struct dpll_pin_ref *ref,
232 struct netlink_ext_ack *extack)
233{
234 const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
235 struct dpll_device *dpll = ref->dpll;
236 s32 phase_adjust;
237 int ret;
238
239 if (!ops->phase_adjust_get)
240 return 0;
241 ret = ops->phase_adjust_get(pin, dpll_pin_on_dpll_priv(dpll, pin),
242 dpll, dpll_priv(dpll),
243 &phase_adjust, extack);
244 if (ret)
245 return ret;
246 if (nla_put_s32(msg, DPLL_A_PIN_PHASE_ADJUST, phase_adjust))
247 return -EMSGSIZE;
248
249 return 0;
250}
251
252static int
253dpll_msg_add_phase_offset(struct sk_buff *msg, struct dpll_pin *pin,
254 struct dpll_pin_ref *ref,
255 struct netlink_ext_ack *extack)
256{
257 const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
258 struct dpll_device *dpll = ref->dpll;
259 s64 phase_offset;
260 int ret;
261
262 if (!ops->phase_offset_get)
263 return 0;
264 ret = ops->phase_offset_get(pin, dpll_pin_on_dpll_priv(dpll, pin),
265 dpll, dpll_priv(dpll), &phase_offset,
266 extack);
267 if (ret)
268 return ret;
269 if (nla_put_64bit(msg, DPLL_A_PIN_PHASE_OFFSET, sizeof(phase_offset),
270 &phase_offset, DPLL_A_PIN_PAD))
271 return -EMSGSIZE;
272
273 return 0;
274}
275
8a6286c1
JP
276static int dpll_msg_add_ffo(struct sk_buff *msg, struct dpll_pin *pin,
277 struct dpll_pin_ref *ref,
278 struct netlink_ext_ack *extack)
279{
280 const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
281 struct dpll_device *dpll = ref->dpll;
282 s64 ffo;
283 int ret;
284
285 if (!ops->ffo_get)
286 return 0;
287 ret = ops->ffo_get(pin, dpll_pin_on_dpll_priv(dpll, pin),
288 dpll, dpll_priv(dpll), &ffo, extack);
289 if (ret) {
290 if (ret == -ENODATA)
291 return 0;
292 return ret;
293 }
294 return nla_put_sint(msg, DPLL_A_PIN_FRACTIONAL_FREQUENCY_OFFSET, ffo);
295}
296
9d71b54b
VF
297static int
298dpll_msg_add_pin_freq(struct sk_buff *msg, struct dpll_pin *pin,
299 struct dpll_pin_ref *ref, struct netlink_ext_ack *extack)
300{
301 const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
302 struct dpll_device *dpll = ref->dpll;
303 struct nlattr *nest;
304 int fs, ret;
305 u64 freq;
306
307 if (!ops->frequency_get)
308 return 0;
309 ret = ops->frequency_get(pin, dpll_pin_on_dpll_priv(dpll, pin), dpll,
310 dpll_priv(dpll), &freq, extack);
311 if (ret)
312 return ret;
313 if (nla_put_64bit(msg, DPLL_A_PIN_FREQUENCY, sizeof(freq), &freq,
314 DPLL_A_PIN_PAD))
315 return -EMSGSIZE;
830ead5f 316 for (fs = 0; fs < pin->prop.freq_supported_num; fs++) {
9d71b54b
VF
317 nest = nla_nest_start(msg, DPLL_A_PIN_FREQUENCY_SUPPORTED);
318 if (!nest)
319 return -EMSGSIZE;
830ead5f 320 freq = pin->prop.freq_supported[fs].min;
9d71b54b
VF
321 if (nla_put_64bit(msg, DPLL_A_PIN_FREQUENCY_MIN, sizeof(freq),
322 &freq, DPLL_A_PIN_PAD)) {
323 nla_nest_cancel(msg, nest);
324 return -EMSGSIZE;
325 }
830ead5f 326 freq = pin->prop.freq_supported[fs].max;
9d71b54b
VF
327 if (nla_put_64bit(msg, DPLL_A_PIN_FREQUENCY_MAX, sizeof(freq),
328 &freq, DPLL_A_PIN_PAD)) {
329 nla_nest_cancel(msg, nest);
330 return -EMSGSIZE;
331 }
332 nla_nest_end(msg, nest);
333 }
334
335 return 0;
336}
337
338static bool dpll_pin_is_freq_supported(struct dpll_pin *pin, u32 freq)
339{
340 int fs;
341
830ead5f
AK
342 for (fs = 0; fs < pin->prop.freq_supported_num; fs++)
343 if (freq >= pin->prop.freq_supported[fs].min &&
344 freq <= pin->prop.freq_supported[fs].max)
9d71b54b
VF
345 return true;
346 return false;
347}
348
349static int
350dpll_msg_add_pin_parents(struct sk_buff *msg, struct dpll_pin *pin,
351 struct dpll_pin_ref *dpll_ref,
352 struct netlink_ext_ack *extack)
353{
354 enum dpll_pin_state state;
355 struct dpll_pin_ref *ref;
356 struct dpll_pin *ppin;
357 struct nlattr *nest;
358 unsigned long index;
359 int ret;
360
361 xa_for_each(&pin->parent_refs, index, ref) {
362 const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
363 void *parent_priv;
364
365 ppin = ref->pin;
366 parent_priv = dpll_pin_on_dpll_priv(dpll_ref->dpll, ppin);
367 ret = ops->state_on_pin_get(pin,
368 dpll_pin_on_pin_priv(ppin, pin),
369 ppin, parent_priv, &state, extack);
370 if (ret)
371 return ret;
372 nest = nla_nest_start(msg, DPLL_A_PIN_PARENT_PIN);
373 if (!nest)
374 return -EMSGSIZE;
375 ret = dpll_msg_add_dev_parent_handle(msg, ppin->id);
376 if (ret)
377 goto nest_cancel;
378 if (nla_put_u32(msg, DPLL_A_PIN_STATE, state)) {
379 ret = -EMSGSIZE;
380 goto nest_cancel;
381 }
382 nla_nest_end(msg, nest);
383 }
384
385 return 0;
386
387nest_cancel:
388 nla_nest_cancel(msg, nest);
389 return ret;
390}
391
392static int
393dpll_msg_add_pin_dplls(struct sk_buff *msg, struct dpll_pin *pin,
394 struct netlink_ext_ack *extack)
395{
396 struct dpll_pin_ref *ref;
397 struct nlattr *attr;
398 unsigned long index;
399 int ret;
400
401 xa_for_each(&pin->dpll_refs, index, ref) {
402 attr = nla_nest_start(msg, DPLL_A_PIN_PARENT_DEVICE);
403 if (!attr)
404 return -EMSGSIZE;
405 ret = dpll_msg_add_dev_parent_handle(msg, ref->dpll->id);
406 if (ret)
407 goto nest_cancel;
408 ret = dpll_msg_add_pin_on_dpll_state(msg, pin, ref, extack);
409 if (ret)
410 goto nest_cancel;
411 ret = dpll_msg_add_pin_prio(msg, pin, ref, extack);
412 if (ret)
413 goto nest_cancel;
414 ret = dpll_msg_add_pin_direction(msg, pin, ref, extack);
d7fbc0b7
AK
415 if (ret)
416 goto nest_cancel;
417 ret = dpll_msg_add_phase_offset(msg, pin, ref, extack);
9d71b54b
VF
418 if (ret)
419 goto nest_cancel;
420 nla_nest_end(msg, attr);
421 }
422
423 return 0;
424
425nest_cancel:
426 nla_nest_end(msg, attr);
427 return ret;
428}
429
430static int
431dpll_cmd_pin_get_one(struct sk_buff *msg, struct dpll_pin *pin,
432 struct netlink_ext_ack *extack)
433{
830ead5f 434 const struct dpll_pin_properties *prop = &pin->prop;
9d71b54b
VF
435 struct dpll_pin_ref *ref;
436 int ret;
437
438 ref = dpll_xa_ref_dpll_first(&pin->dpll_refs);
439 ASSERT_NOT_NULL(ref);
440
441 ret = dpll_msg_add_pin_handle(msg, pin);
442 if (ret)
443 return ret;
444 if (nla_put_string(msg, DPLL_A_PIN_MODULE_NAME,
445 module_name(pin->module)))
446 return -EMSGSIZE;
447 if (nla_put_64bit(msg, DPLL_A_PIN_CLOCK_ID, sizeof(pin->clock_id),
448 &pin->clock_id, DPLL_A_PIN_PAD))
449 return -EMSGSIZE;
450 if (prop->board_label &&
451 nla_put_string(msg, DPLL_A_PIN_BOARD_LABEL, prop->board_label))
452 return -EMSGSIZE;
453 if (prop->panel_label &&
454 nla_put_string(msg, DPLL_A_PIN_PANEL_LABEL, prop->panel_label))
455 return -EMSGSIZE;
456 if (prop->package_label &&
457 nla_put_string(msg, DPLL_A_PIN_PACKAGE_LABEL,
458 prop->package_label))
459 return -EMSGSIZE;
460 if (nla_put_u32(msg, DPLL_A_PIN_TYPE, prop->type))
461 return -EMSGSIZE;
462 if (nla_put_u32(msg, DPLL_A_PIN_CAPABILITIES, prop->capabilities))
463 return -EMSGSIZE;
464 ret = dpll_msg_add_pin_freq(msg, pin, ref, extack);
d7fbc0b7
AK
465 if (ret)
466 return ret;
467 if (nla_put_s32(msg, DPLL_A_PIN_PHASE_ADJUST_MIN,
468 prop->phase_range.min))
469 return -EMSGSIZE;
470 if (nla_put_s32(msg, DPLL_A_PIN_PHASE_ADJUST_MAX,
471 prop->phase_range.max))
472 return -EMSGSIZE;
473 ret = dpll_msg_add_pin_phase_adjust(msg, pin, ref, extack);
8a6286c1
JP
474 if (ret)
475 return ret;
476 ret = dpll_msg_add_ffo(msg, pin, ref, extack);
9d71b54b
VF
477 if (ret)
478 return ret;
479 if (xa_empty(&pin->parent_refs))
480 ret = dpll_msg_add_pin_dplls(msg, pin, extack);
481 else
482 ret = dpll_msg_add_pin_parents(msg, pin, ref, extack);
483
484 return ret;
485}
486
487static int
488dpll_device_get_one(struct dpll_device *dpll, struct sk_buff *msg,
489 struct netlink_ext_ack *extack)
490{
491 int ret;
492
493 ret = dpll_msg_add_dev_handle(msg, dpll);
494 if (ret)
495 return ret;
496 if (nla_put_string(msg, DPLL_A_MODULE_NAME, module_name(dpll->module)))
497 return -EMSGSIZE;
498 if (nla_put_64bit(msg, DPLL_A_CLOCK_ID, sizeof(dpll->clock_id),
499 &dpll->clock_id, DPLL_A_PAD))
500 return -EMSGSIZE;
501 ret = dpll_msg_add_temp(msg, dpll, extack);
502 if (ret)
503 return ret;
504 ret = dpll_msg_add_lock_status(msg, dpll, extack);
505 if (ret)
506 return ret;
507 ret = dpll_msg_add_mode(msg, dpll, extack);
508 if (ret)
509 return ret;
510 ret = dpll_msg_add_mode_supported(msg, dpll, extack);
511 if (ret)
512 return ret;
513 if (nla_put_u32(msg, DPLL_A_TYPE, dpll->type))
514 return -EMSGSIZE;
515
d7fbc0b7 516 return 0;
9d71b54b
VF
517}
518
519static int
520dpll_device_event_send(enum dpll_cmd event, struct dpll_device *dpll)
521{
522 struct sk_buff *msg;
523 int ret = -ENOMEM;
524 void *hdr;
525
526 if (WARN_ON(!xa_get_mark(&dpll_device_xa, dpll->id, DPLL_REGISTERED)))
527 return -ENODEV;
528 msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
529 if (!msg)
530 return -ENOMEM;
531 hdr = genlmsg_put(msg, 0, 0, &dpll_nl_family, 0, event);
532 if (!hdr)
533 goto err_free_msg;
534 ret = dpll_device_get_one(dpll, msg, NULL);
535 if (ret)
536 goto err_cancel_msg;
537 genlmsg_end(msg, hdr);
538 genlmsg_multicast(&dpll_nl_family, msg, 0, 0, GFP_KERNEL);
539
540 return 0;
541
542err_cancel_msg:
543 genlmsg_cancel(msg, hdr);
544err_free_msg:
545 nlmsg_free(msg);
546
547 return ret;
548}
549
550int dpll_device_create_ntf(struct dpll_device *dpll)
551{
552 return dpll_device_event_send(DPLL_CMD_DEVICE_CREATE_NTF, dpll);
553}
554
555int dpll_device_delete_ntf(struct dpll_device *dpll)
556{
557 return dpll_device_event_send(DPLL_CMD_DEVICE_DELETE_NTF, dpll);
558}
559
560static int
561__dpll_device_change_ntf(struct dpll_device *dpll)
562{
563 return dpll_device_event_send(DPLL_CMD_DEVICE_CHANGE_NTF, dpll);
564}
565
db2ec3c9
AK
566static bool dpll_pin_available(struct dpll_pin *pin)
567{
568 struct dpll_pin_ref *par_ref;
569 unsigned long i;
570
571 if (!xa_get_mark(&dpll_pin_xa, pin->id, DPLL_REGISTERED))
572 return false;
573 xa_for_each(&pin->parent_refs, i, par_ref)
574 if (xa_get_mark(&dpll_pin_xa, par_ref->pin->id,
575 DPLL_REGISTERED))
576 return true;
577 xa_for_each(&pin->dpll_refs, i, par_ref)
578 if (xa_get_mark(&dpll_device_xa, par_ref->dpll->id,
579 DPLL_REGISTERED))
580 return true;
581 return false;
582}
583
9d71b54b
VF
584/**
585 * dpll_device_change_ntf - notify that the dpll device has been changed
586 * @dpll: registered dpll pointer
587 *
588 * Context: acquires and holds a dpll_lock.
589 * Return: 0 if succeeds, error code otherwise.
590 */
591int dpll_device_change_ntf(struct dpll_device *dpll)
592{
593 int ret;
594
595 mutex_lock(&dpll_lock);
596 ret = __dpll_device_change_ntf(dpll);
597 mutex_unlock(&dpll_lock);
598
599 return ret;
600}
601EXPORT_SYMBOL_GPL(dpll_device_change_ntf);
602
603static int
604dpll_pin_event_send(enum dpll_cmd event, struct dpll_pin *pin)
605{
606 struct sk_buff *msg;
607 int ret = -ENOMEM;
608 void *hdr;
609
db2ec3c9 610 if (!dpll_pin_available(pin))
9d71b54b
VF
611 return -ENODEV;
612
613 msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
614 if (!msg)
615 return -ENOMEM;
616
617 hdr = genlmsg_put(msg, 0, 0, &dpll_nl_family, 0, event);
618 if (!hdr)
619 goto err_free_msg;
620 ret = dpll_cmd_pin_get_one(msg, pin, NULL);
621 if (ret)
622 goto err_cancel_msg;
623 genlmsg_end(msg, hdr);
624 genlmsg_multicast(&dpll_nl_family, msg, 0, 0, GFP_KERNEL);
625
626 return 0;
627
628err_cancel_msg:
629 genlmsg_cancel(msg, hdr);
630err_free_msg:
631 nlmsg_free(msg);
632
633 return ret;
634}
635
636int dpll_pin_create_ntf(struct dpll_pin *pin)
637{
638 return dpll_pin_event_send(DPLL_CMD_PIN_CREATE_NTF, pin);
639}
640
641int dpll_pin_delete_ntf(struct dpll_pin *pin)
642{
643 return dpll_pin_event_send(DPLL_CMD_PIN_DELETE_NTF, pin);
644}
645
646static int __dpll_pin_change_ntf(struct dpll_pin *pin)
647{
648 return dpll_pin_event_send(DPLL_CMD_PIN_CHANGE_NTF, pin);
649}
650
651/**
652 * dpll_pin_change_ntf - notify that the pin has been changed
653 * @pin: registered pin pointer
654 *
655 * Context: acquires and holds a dpll_lock.
656 * Return: 0 if succeeds, error code otherwise.
657 */
658int dpll_pin_change_ntf(struct dpll_pin *pin)
659{
660 int ret;
661
662 mutex_lock(&dpll_lock);
663 ret = __dpll_pin_change_ntf(pin);
664 mutex_unlock(&dpll_lock);
665
666 return ret;
667}
668EXPORT_SYMBOL_GPL(dpll_pin_change_ntf);
669
670static int
671dpll_pin_freq_set(struct dpll_pin *pin, struct nlattr *a,
672 struct netlink_ext_ack *extack)
673{
20f66772
AK
674 u64 freq = nla_get_u64(a), old_freq;
675 struct dpll_pin_ref *ref, *failed;
676 const struct dpll_pin_ops *ops;
677 struct dpll_device *dpll;
9d71b54b
VF
678 unsigned long i;
679 int ret;
680
681 if (!dpll_pin_is_freq_supported(pin, freq)) {
682 NL_SET_ERR_MSG_ATTR(extack, a, "frequency is not supported by the device");
683 return -EINVAL;
684 }
685
686 xa_for_each(&pin->dpll_refs, i, ref) {
20f66772
AK
687 ops = dpll_pin_ops(ref);
688 if (!ops->frequency_set || !ops->frequency_get) {
689 NL_SET_ERR_MSG(extack, "frequency set not supported by the device");
9d71b54b 690 return -EOPNOTSUPP;
20f66772
AK
691 }
692 }
693 ref = dpll_xa_ref_dpll_first(&pin->dpll_refs);
694 ops = dpll_pin_ops(ref);
695 dpll = ref->dpll;
696 ret = ops->frequency_get(pin, dpll_pin_on_dpll_priv(dpll, pin), dpll,
697 dpll_priv(dpll), &old_freq, extack);
698 if (ret) {
699 NL_SET_ERR_MSG(extack, "unable to get old frequency value");
700 return ret;
701 }
702 if (freq == old_freq)
703 return 0;
704
705 xa_for_each(&pin->dpll_refs, i, ref) {
706 ops = dpll_pin_ops(ref);
707 dpll = ref->dpll;
9d71b54b
VF
708 ret = ops->frequency_set(pin, dpll_pin_on_dpll_priv(dpll, pin),
709 dpll, dpll_priv(dpll), freq, extack);
20f66772
AK
710 if (ret) {
711 failed = ref;
712 NL_SET_ERR_MSG_FMT(extack, "frequency set failed for dpll_id:%u",
713 dpll->id);
714 goto rollback;
715 }
9d71b54b
VF
716 }
717 __dpll_pin_change_ntf(pin);
718
719 return 0;
20f66772
AK
720
721rollback:
722 xa_for_each(&pin->dpll_refs, i, ref) {
723 if (ref == failed)
724 break;
725 ops = dpll_pin_ops(ref);
726 dpll = ref->dpll;
727 if (ops->frequency_set(pin, dpll_pin_on_dpll_priv(dpll, pin),
728 dpll, dpll_priv(dpll), old_freq, extack))
729 NL_SET_ERR_MSG(extack, "set frequency rollback failed");
730 }
731 return ret;
9d71b54b
VF
732}
733
734static int
735dpll_pin_on_pin_state_set(struct dpll_pin *pin, u32 parent_idx,
736 enum dpll_pin_state state,
737 struct netlink_ext_ack *extack)
738{
739 struct dpll_pin_ref *parent_ref;
740 const struct dpll_pin_ops *ops;
741 struct dpll_pin_ref *dpll_ref;
742 void *pin_priv, *parent_priv;
743 struct dpll_pin *parent;
744 unsigned long i;
745 int ret;
746
747 if (!(DPLL_PIN_CAPABILITIES_STATE_CAN_CHANGE &
830ead5f 748 pin->prop.capabilities)) {
9d71b54b
VF
749 NL_SET_ERR_MSG(extack, "state changing is not allowed");
750 return -EOPNOTSUPP;
751 }
752 parent = xa_load(&dpll_pin_xa, parent_idx);
753 if (!parent)
754 return -EINVAL;
755 parent_ref = xa_load(&pin->parent_refs, parent->pin_idx);
756 if (!parent_ref)
757 return -EINVAL;
758 xa_for_each(&parent->dpll_refs, i, dpll_ref) {
759 ops = dpll_pin_ops(parent_ref);
760 if (!ops->state_on_pin_set)
761 return -EOPNOTSUPP;
762 pin_priv = dpll_pin_on_pin_priv(parent, pin);
763 parent_priv = dpll_pin_on_dpll_priv(dpll_ref->dpll, parent);
764 ret = ops->state_on_pin_set(pin, pin_priv, parent, parent_priv,
765 state, extack);
766 if (ret)
767 return ret;
768 }
769 __dpll_pin_change_ntf(pin);
770
771 return 0;
772}
773
774static int
775dpll_pin_state_set(struct dpll_device *dpll, struct dpll_pin *pin,
776 enum dpll_pin_state state,
777 struct netlink_ext_ack *extack)
778{
779 const struct dpll_pin_ops *ops;
780 struct dpll_pin_ref *ref;
781 int ret;
782
783 if (!(DPLL_PIN_CAPABILITIES_STATE_CAN_CHANGE &
830ead5f 784 pin->prop.capabilities)) {
9d71b54b
VF
785 NL_SET_ERR_MSG(extack, "state changing is not allowed");
786 return -EOPNOTSUPP;
787 }
788 ref = xa_load(&pin->dpll_refs, dpll->id);
789 ASSERT_NOT_NULL(ref);
790 ops = dpll_pin_ops(ref);
791 if (!ops->state_on_dpll_set)
792 return -EOPNOTSUPP;
793 ret = ops->state_on_dpll_set(pin, dpll_pin_on_dpll_priv(dpll, pin),
794 dpll, dpll_priv(dpll), state, extack);
795 if (ret)
796 return ret;
797 __dpll_pin_change_ntf(pin);
798
799 return 0;
800}
801
802static int
803dpll_pin_prio_set(struct dpll_device *dpll, struct dpll_pin *pin,
804 u32 prio, struct netlink_ext_ack *extack)
805{
806 const struct dpll_pin_ops *ops;
807 struct dpll_pin_ref *ref;
808 int ret;
809
810 if (!(DPLL_PIN_CAPABILITIES_PRIORITY_CAN_CHANGE &
830ead5f 811 pin->prop.capabilities)) {
9d71b54b
VF
812 NL_SET_ERR_MSG(extack, "prio changing is not allowed");
813 return -EOPNOTSUPP;
814 }
815 ref = xa_load(&pin->dpll_refs, dpll->id);
816 ASSERT_NOT_NULL(ref);
817 ops = dpll_pin_ops(ref);
818 if (!ops->prio_set)
819 return -EOPNOTSUPP;
820 ret = ops->prio_set(pin, dpll_pin_on_dpll_priv(dpll, pin), dpll,
821 dpll_priv(dpll), prio, extack);
822 if (ret)
823 return ret;
824 __dpll_pin_change_ntf(pin);
825
826 return 0;
827}
828
829static int
830dpll_pin_direction_set(struct dpll_pin *pin, struct dpll_device *dpll,
831 enum dpll_pin_direction direction,
832 struct netlink_ext_ack *extack)
833{
834 const struct dpll_pin_ops *ops;
835 struct dpll_pin_ref *ref;
836 int ret;
837
838 if (!(DPLL_PIN_CAPABILITIES_DIRECTION_CAN_CHANGE &
830ead5f 839 pin->prop.capabilities)) {
9d71b54b
VF
840 NL_SET_ERR_MSG(extack, "direction changing is not allowed");
841 return -EOPNOTSUPP;
842 }
843 ref = xa_load(&pin->dpll_refs, dpll->id);
844 ASSERT_NOT_NULL(ref);
845 ops = dpll_pin_ops(ref);
846 if (!ops->direction_set)
847 return -EOPNOTSUPP;
848 ret = ops->direction_set(pin, dpll_pin_on_dpll_priv(dpll, pin),
849 dpll, dpll_priv(dpll), direction, extack);
850 if (ret)
851 return ret;
852 __dpll_pin_change_ntf(pin);
853
854 return 0;
855}
856
d7fbc0b7
AK
857static int
858dpll_pin_phase_adj_set(struct dpll_pin *pin, struct nlattr *phase_adj_attr,
859 struct netlink_ext_ack *extack)
860{
861 struct dpll_pin_ref *ref, *failed;
862 const struct dpll_pin_ops *ops;
863 s32 phase_adj, old_phase_adj;
864 struct dpll_device *dpll;
865 unsigned long i;
866 int ret;
867
868 phase_adj = nla_get_s32(phase_adj_attr);
830ead5f
AK
869 if (phase_adj > pin->prop.phase_range.max ||
870 phase_adj < pin->prop.phase_range.min) {
d7fbc0b7
AK
871 NL_SET_ERR_MSG_ATTR(extack, phase_adj_attr,
872 "phase adjust value not supported");
873 return -EINVAL;
874 }
875
876 xa_for_each(&pin->dpll_refs, i, ref) {
877 ops = dpll_pin_ops(ref);
878 if (!ops->phase_adjust_set || !ops->phase_adjust_get) {
879 NL_SET_ERR_MSG(extack, "phase adjust not supported");
880 return -EOPNOTSUPP;
881 }
882 }
883 ref = dpll_xa_ref_dpll_first(&pin->dpll_refs);
884 ops = dpll_pin_ops(ref);
885 dpll = ref->dpll;
886 ret = ops->phase_adjust_get(pin, dpll_pin_on_dpll_priv(dpll, pin),
887 dpll, dpll_priv(dpll), &old_phase_adj,
888 extack);
889 if (ret) {
890 NL_SET_ERR_MSG(extack, "unable to get old phase adjust value");
891 return ret;
892 }
893 if (phase_adj == old_phase_adj)
894 return 0;
895
896 xa_for_each(&pin->dpll_refs, i, ref) {
897 ops = dpll_pin_ops(ref);
898 dpll = ref->dpll;
899 ret = ops->phase_adjust_set(pin,
900 dpll_pin_on_dpll_priv(dpll, pin),
901 dpll, dpll_priv(dpll), phase_adj,
902 extack);
903 if (ret) {
904 failed = ref;
905 NL_SET_ERR_MSG_FMT(extack,
906 "phase adjust set failed for dpll_id:%u",
907 dpll->id);
908 goto rollback;
909 }
910 }
911 __dpll_pin_change_ntf(pin);
912
913 return 0;
914
915rollback:
916 xa_for_each(&pin->dpll_refs, i, ref) {
917 if (ref == failed)
918 break;
919 ops = dpll_pin_ops(ref);
920 dpll = ref->dpll;
921 if (ops->phase_adjust_set(pin, dpll_pin_on_dpll_priv(dpll, pin),
922 dpll, dpll_priv(dpll), old_phase_adj,
923 extack))
924 NL_SET_ERR_MSG(extack, "set phase adjust rollback failed");
925 }
926 return ret;
927}
928
9d71b54b
VF
929static int
930dpll_pin_parent_device_set(struct dpll_pin *pin, struct nlattr *parent_nest,
931 struct netlink_ext_ack *extack)
932{
933 struct nlattr *tb[DPLL_A_PIN_MAX + 1];
934 enum dpll_pin_direction direction;
935 enum dpll_pin_state state;
936 struct dpll_pin_ref *ref;
937 struct dpll_device *dpll;
938 u32 pdpll_idx, prio;
939 int ret;
940
941 nla_parse_nested(tb, DPLL_A_PIN_MAX, parent_nest,
942 dpll_pin_parent_device_nl_policy, extack);
943 if (!tb[DPLL_A_PIN_PARENT_ID]) {
944 NL_SET_ERR_MSG(extack, "device parent id expected");
945 return -EINVAL;
946 }
947 pdpll_idx = nla_get_u32(tb[DPLL_A_PIN_PARENT_ID]);
948 dpll = xa_load(&dpll_device_xa, pdpll_idx);
949 if (!dpll) {
950 NL_SET_ERR_MSG(extack, "parent device not found");
951 return -EINVAL;
952 }
953 ref = xa_load(&pin->dpll_refs, dpll->id);
954 if (!ref) {
955 NL_SET_ERR_MSG(extack, "pin not connected to given parent device");
956 return -EINVAL;
957 }
958 if (tb[DPLL_A_PIN_STATE]) {
959 state = nla_get_u32(tb[DPLL_A_PIN_STATE]);
960 ret = dpll_pin_state_set(dpll, pin, state, extack);
961 if (ret)
962 return ret;
963 }
964 if (tb[DPLL_A_PIN_PRIO]) {
965 prio = nla_get_u32(tb[DPLL_A_PIN_PRIO]);
966 ret = dpll_pin_prio_set(dpll, pin, prio, extack);
967 if (ret)
968 return ret;
969 }
970 if (tb[DPLL_A_PIN_DIRECTION]) {
971 direction = nla_get_u32(tb[DPLL_A_PIN_DIRECTION]);
972 ret = dpll_pin_direction_set(pin, dpll, direction, extack);
973 if (ret)
974 return ret;
975 }
976 return 0;
977}
978
979static int
980dpll_pin_parent_pin_set(struct dpll_pin *pin, struct nlattr *parent_nest,
981 struct netlink_ext_ack *extack)
982{
983 struct nlattr *tb[DPLL_A_PIN_MAX + 1];
9d71b54b
VF
984 u32 ppin_idx;
985 int ret;
986
987 nla_parse_nested(tb, DPLL_A_PIN_MAX, parent_nest,
988 dpll_pin_parent_pin_nl_policy, extack);
989 if (!tb[DPLL_A_PIN_PARENT_ID]) {
990 NL_SET_ERR_MSG(extack, "device parent id expected");
991 return -EINVAL;
992 }
993 ppin_idx = nla_get_u32(tb[DPLL_A_PIN_PARENT_ID]);
65c95f78
JP
994
995 if (tb[DPLL_A_PIN_STATE]) {
996 enum dpll_pin_state state = nla_get_u32(tb[DPLL_A_PIN_STATE]);
997
998 ret = dpll_pin_on_pin_state_set(pin, ppin_idx, state, extack);
999 if (ret)
1000 return ret;
1001 }
9d71b54b
VF
1002
1003 return 0;
1004}
1005
1006static int
1007dpll_pin_set_from_nlattr(struct dpll_pin *pin, struct genl_info *info)
1008{
1009 struct nlattr *a;
1010 int rem, ret;
1011
1012 nla_for_each_attr(a, genlmsg_data(info->genlhdr),
1013 genlmsg_len(info->genlhdr), rem) {
1014 switch (nla_type(a)) {
1015 case DPLL_A_PIN_FREQUENCY:
1016 ret = dpll_pin_freq_set(pin, a, info->extack);
1017 if (ret)
1018 return ret;
1019 break;
d7fbc0b7
AK
1020 case DPLL_A_PIN_PHASE_ADJUST:
1021 ret = dpll_pin_phase_adj_set(pin, a, info->extack);
1022 if (ret)
1023 return ret;
1024 break;
9d71b54b
VF
1025 case DPLL_A_PIN_PARENT_DEVICE:
1026 ret = dpll_pin_parent_device_set(pin, a, info->extack);
1027 if (ret)
1028 return ret;
1029 break;
1030 case DPLL_A_PIN_PARENT_PIN:
1031 ret = dpll_pin_parent_pin_set(pin, a, info->extack);
1032 if (ret)
1033 return ret;
1034 break;
1035 }
1036 }
1037
1038 return 0;
1039}
1040
1041static struct dpll_pin *
1042dpll_pin_find(u64 clock_id, struct nlattr *mod_name_attr,
1043 enum dpll_pin_type type, struct nlattr *board_label,
1044 struct nlattr *panel_label, struct nlattr *package_label,
1045 struct netlink_ext_ack *extack)
1046{
1047 bool board_match, panel_match, package_match;
1048 struct dpll_pin *pin_match = NULL, *pin;
1049 const struct dpll_pin_properties *prop;
1050 bool cid_match, mod_match, type_match;
1051 unsigned long i;
1052
1053 xa_for_each_marked(&dpll_pin_xa, i, pin, DPLL_REGISTERED) {
830ead5f 1054 prop = &pin->prop;
9d71b54b
VF
1055 cid_match = clock_id ? pin->clock_id == clock_id : true;
1056 mod_match = mod_name_attr && module_name(pin->module) ?
1057 !nla_strcmp(mod_name_attr,
1058 module_name(pin->module)) : true;
1059 type_match = type ? prop->type == type : true;
1060 board_match = board_label ? (prop->board_label ?
1061 !nla_strcmp(board_label, prop->board_label) : false) :
1062 true;
1063 panel_match = panel_label ? (prop->panel_label ?
1064 !nla_strcmp(panel_label, prop->panel_label) : false) :
1065 true;
1066 package_match = package_label ? (prop->package_label ?
1067 !nla_strcmp(package_label, prop->package_label) :
1068 false) : true;
1069 if (cid_match && mod_match && type_match && board_match &&
1070 panel_match && package_match) {
1071 if (pin_match) {
1072 NL_SET_ERR_MSG(extack, "multiple matches");
1073 return ERR_PTR(-EINVAL);
1074 }
1075 pin_match = pin;
f20161cf 1076 }
9d71b54b
VF
1077 }
1078 if (!pin_match) {
1079 NL_SET_ERR_MSG(extack, "not found");
1080 return ERR_PTR(-ENODEV);
1081 }
1082 return pin_match;
1083}
1084
1085static struct dpll_pin *dpll_pin_find_from_nlattr(struct genl_info *info)
1086{
1087 struct nlattr *attr, *mod_name_attr = NULL, *board_label_attr = NULL,
1088 *panel_label_attr = NULL, *package_label_attr = NULL;
1089 enum dpll_pin_type type = 0;
1090 u64 clock_id = 0;
1091 int rem = 0;
1092
1093 nla_for_each_attr(attr, genlmsg_data(info->genlhdr),
1094 genlmsg_len(info->genlhdr), rem) {
1095 switch (nla_type(attr)) {
1096 case DPLL_A_PIN_CLOCK_ID:
1097 if (clock_id)
1098 goto duplicated_attr;
1099 clock_id = nla_get_u64(attr);
1100 break;
1101 case DPLL_A_PIN_MODULE_NAME:
1102 if (mod_name_attr)
1103 goto duplicated_attr;
1104 mod_name_attr = attr;
1105 break;
1106 case DPLL_A_PIN_TYPE:
1107 if (type)
1108 goto duplicated_attr;
1109 type = nla_get_u32(attr);
1110 break;
1111 case DPLL_A_PIN_BOARD_LABEL:
1112 if (board_label_attr)
1113 goto duplicated_attr;
1114 board_label_attr = attr;
1115 break;
1116 case DPLL_A_PIN_PANEL_LABEL:
1117 if (panel_label_attr)
1118 goto duplicated_attr;
1119 panel_label_attr = attr;
1120 break;
1121 case DPLL_A_PIN_PACKAGE_LABEL:
1122 if (package_label_attr)
1123 goto duplicated_attr;
1124 package_label_attr = attr;
1125 break;
1126 default:
1127 break;
1128 }
1129 }
1130 if (!(clock_id || mod_name_attr || board_label_attr ||
1131 panel_label_attr || package_label_attr)) {
1132 NL_SET_ERR_MSG(info->extack, "missing attributes");
1133 return ERR_PTR(-EINVAL);
1134 }
1135 return dpll_pin_find(clock_id, mod_name_attr, type, board_label_attr,
1136 panel_label_attr, package_label_attr,
1137 info->extack);
1138duplicated_attr:
1139 NL_SET_ERR_MSG(info->extack, "duplicated attribute");
1140 return ERR_PTR(-EINVAL);
1141}
1142
1143int dpll_nl_pin_id_get_doit(struct sk_buff *skb, struct genl_info *info)
1144{
1145 struct dpll_pin *pin;
1146 struct sk_buff *msg;
1147 struct nlattr *hdr;
1148 int ret;
1149
1150 msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
1151 if (!msg)
1152 return -ENOMEM;
1153 hdr = genlmsg_put_reply(msg, info, &dpll_nl_family, 0,
1154 DPLL_CMD_PIN_ID_GET);
b6fe6f03
HG
1155 if (!hdr) {
1156 nlmsg_free(msg);
9d71b54b 1157 return -EMSGSIZE;
b6fe6f03 1158 }
9d71b54b
VF
1159 pin = dpll_pin_find_from_nlattr(info);
1160 if (!IS_ERR(pin)) {
db2ec3c9
AK
1161 if (!dpll_pin_available(pin)) {
1162 nlmsg_free(msg);
1163 return -ENODEV;
1164 }
9d71b54b
VF
1165 ret = dpll_msg_add_pin_handle(msg, pin);
1166 if (ret) {
1167 nlmsg_free(msg);
1168 return ret;
1169 }
1170 }
1171 genlmsg_end(msg, hdr);
1172
1173 return genlmsg_reply(msg, info);
1174}
1175
1176int dpll_nl_pin_get_doit(struct sk_buff *skb, struct genl_info *info)
1177{
1178 struct dpll_pin *pin = info->user_ptr[0];
1179 struct sk_buff *msg;
1180 struct nlattr *hdr;
1181 int ret;
1182
1183 if (!pin)
1184 return -ENODEV;
1185 msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
1186 if (!msg)
1187 return -ENOMEM;
1188 hdr = genlmsg_put_reply(msg, info, &dpll_nl_family, 0,
1189 DPLL_CMD_PIN_GET);
b6fe6f03
HG
1190 if (!hdr) {
1191 nlmsg_free(msg);
9d71b54b 1192 return -EMSGSIZE;
b6fe6f03 1193 }
9d71b54b
VF
1194 ret = dpll_cmd_pin_get_one(msg, pin, info->extack);
1195 if (ret) {
1196 nlmsg_free(msg);
1197 return ret;
1198 }
1199 genlmsg_end(msg, hdr);
1200
1201 return genlmsg_reply(msg, info);
1202}
1203
1204int dpll_nl_pin_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
1205{
1206 struct dpll_dump_ctx *ctx = dpll_dump_context(cb);
1207 struct dpll_pin *pin;
1208 struct nlattr *hdr;
1209 unsigned long i;
1210 int ret = 0;
1211
53c0441d 1212 mutex_lock(&dpll_lock);
9d71b54b
VF
1213 xa_for_each_marked_start(&dpll_pin_xa, i, pin, DPLL_REGISTERED,
1214 ctx->idx) {
db2ec3c9
AK
1215 if (!dpll_pin_available(pin))
1216 continue;
9d71b54b
VF
1217 hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid,
1218 cb->nlh->nlmsg_seq,
1219 &dpll_nl_family, NLM_F_MULTI,
1220 DPLL_CMD_PIN_GET);
1221 if (!hdr) {
1222 ret = -EMSGSIZE;
1223 break;
1224 }
1225 ret = dpll_cmd_pin_get_one(skb, pin, cb->extack);
1226 if (ret) {
1227 genlmsg_cancel(skb, hdr);
1228 break;
1229 }
1230 genlmsg_end(skb, hdr);
1231 }
53c0441d
JP
1232 mutex_unlock(&dpll_lock);
1233
9d71b54b
VF
1234 if (ret == -EMSGSIZE) {
1235 ctx->idx = i;
1236 return skb->len;
1237 }
1238 return ret;
1239}
1240
1241int dpll_nl_pin_set_doit(struct sk_buff *skb, struct genl_info *info)
1242{
1243 struct dpll_pin *pin = info->user_ptr[0];
1244
1245 return dpll_pin_set_from_nlattr(pin, info);
1246}
1247
1248static struct dpll_device *
1249dpll_device_find(u64 clock_id, struct nlattr *mod_name_attr,
1250 enum dpll_type type, struct netlink_ext_ack *extack)
1251{
1252 struct dpll_device *dpll_match = NULL, *dpll;
1253 bool cid_match, mod_match, type_match;
1254 unsigned long i;
1255
1256 xa_for_each_marked(&dpll_device_xa, i, dpll, DPLL_REGISTERED) {
1257 cid_match = clock_id ? dpll->clock_id == clock_id : true;
1258 mod_match = mod_name_attr ? (module_name(dpll->module) ?
1259 !nla_strcmp(mod_name_attr,
1260 module_name(dpll->module)) : false) : true;
1261 type_match = type ? dpll->type == type : true;
1262 if (cid_match && mod_match && type_match) {
1263 if (dpll_match) {
1264 NL_SET_ERR_MSG(extack, "multiple matches");
1265 return ERR_PTR(-EINVAL);
1266 }
1267 dpll_match = dpll;
1268 }
1269 }
1270 if (!dpll_match) {
1271 NL_SET_ERR_MSG(extack, "not found");
1272 return ERR_PTR(-ENODEV);
1273 }
1274
1275 return dpll_match;
1276}
1277
1278static struct dpll_device *
1279dpll_device_find_from_nlattr(struct genl_info *info)
1280{
1281 struct nlattr *attr, *mod_name_attr = NULL;
1282 enum dpll_type type = 0;
1283 u64 clock_id = 0;
1284 int rem = 0;
1285
1286 nla_for_each_attr(attr, genlmsg_data(info->genlhdr),
1287 genlmsg_len(info->genlhdr), rem) {
1288 switch (nla_type(attr)) {
1289 case DPLL_A_CLOCK_ID:
1290 if (clock_id)
1291 goto duplicated_attr;
1292 clock_id = nla_get_u64(attr);
1293 break;
1294 case DPLL_A_MODULE_NAME:
1295 if (mod_name_attr)
1296 goto duplicated_attr;
1297 mod_name_attr = attr;
1298 break;
1299 case DPLL_A_TYPE:
1300 if (type)
1301 goto duplicated_attr;
1302 type = nla_get_u32(attr);
1303 break;
1304 default:
1305 break;
1306 }
1307 }
1308 if (!clock_id && !mod_name_attr && !type) {
1309 NL_SET_ERR_MSG(info->extack, "missing attributes");
1310 return ERR_PTR(-EINVAL);
1311 }
1312 return dpll_device_find(clock_id, mod_name_attr, type, info->extack);
1313duplicated_attr:
1314 NL_SET_ERR_MSG(info->extack, "duplicated attribute");
1315 return ERR_PTR(-EINVAL);
1316}
1317
1318int dpll_nl_device_id_get_doit(struct sk_buff *skb, struct genl_info *info)
1319{
1320 struct dpll_device *dpll;
1321 struct sk_buff *msg;
1322 struct nlattr *hdr;
1323 int ret;
1324
1325 msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
1326 if (!msg)
1327 return -ENOMEM;
1328 hdr = genlmsg_put_reply(msg, info, &dpll_nl_family, 0,
1329 DPLL_CMD_DEVICE_ID_GET);
b6fe6f03
HG
1330 if (!hdr) {
1331 nlmsg_free(msg);
9d71b54b 1332 return -EMSGSIZE;
b6fe6f03 1333 }
9d71b54b
VF
1334
1335 dpll = dpll_device_find_from_nlattr(info);
1336 if (!IS_ERR(dpll)) {
1337 ret = dpll_msg_add_dev_handle(msg, dpll);
1338 if (ret) {
1339 nlmsg_free(msg);
1340 return ret;
1341 }
1342 }
1343 genlmsg_end(msg, hdr);
1344
1345 return genlmsg_reply(msg, info);
1346}
1347
1348int dpll_nl_device_get_doit(struct sk_buff *skb, struct genl_info *info)
1349{
1350 struct dpll_device *dpll = info->user_ptr[0];
1351 struct sk_buff *msg;
1352 struct nlattr *hdr;
1353 int ret;
1354
1355 msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
1356 if (!msg)
1357 return -ENOMEM;
1358 hdr = genlmsg_put_reply(msg, info, &dpll_nl_family, 0,
1359 DPLL_CMD_DEVICE_GET);
b6fe6f03
HG
1360 if (!hdr) {
1361 nlmsg_free(msg);
9d71b54b 1362 return -EMSGSIZE;
b6fe6f03 1363 }
9d71b54b
VF
1364
1365 ret = dpll_device_get_one(dpll, msg, info->extack);
1366 if (ret) {
1367 nlmsg_free(msg);
1368 return ret;
1369 }
1370 genlmsg_end(msg, hdr);
1371
1372 return genlmsg_reply(msg, info);
1373}
1374
1375int dpll_nl_device_set_doit(struct sk_buff *skb, struct genl_info *info)
1376{
1377 /* placeholder for set command */
1378 return 0;
1379}
1380
1381int dpll_nl_device_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
1382{
1383 struct dpll_dump_ctx *ctx = dpll_dump_context(cb);
1384 struct dpll_device *dpll;
1385 struct nlattr *hdr;
1386 unsigned long i;
1387 int ret = 0;
1388
53c0441d 1389 mutex_lock(&dpll_lock);
9d71b54b
VF
1390 xa_for_each_marked_start(&dpll_device_xa, i, dpll, DPLL_REGISTERED,
1391 ctx->idx) {
1392 hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid,
1393 cb->nlh->nlmsg_seq, &dpll_nl_family,
1394 NLM_F_MULTI, DPLL_CMD_DEVICE_GET);
1395 if (!hdr) {
1396 ret = -EMSGSIZE;
1397 break;
1398 }
1399 ret = dpll_device_get_one(dpll, skb, cb->extack);
1400 if (ret) {
1401 genlmsg_cancel(skb, hdr);
1402 break;
1403 }
1404 genlmsg_end(skb, hdr);
1405 }
53c0441d
JP
1406 mutex_unlock(&dpll_lock);
1407
9d71b54b
VF
1408 if (ret == -EMSGSIZE) {
1409 ctx->idx = i;
1410 return skb->len;
1411 }
1412 return ret;
1413}
1414
1415int dpll_pre_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
1416 struct genl_info *info)
1417{
1418 u32 id;
1419
1420 if (GENL_REQ_ATTR_CHECK(info, DPLL_A_ID))
1421 return -EINVAL;
1422
1423 mutex_lock(&dpll_lock);
1424 id = nla_get_u32(info->attrs[DPLL_A_ID]);
1425 info->user_ptr[0] = dpll_device_get_by_id(id);
1426 if (!info->user_ptr[0]) {
1427 NL_SET_ERR_MSG(info->extack, "device not found");
1428 goto unlock;
1429 }
1430 return 0;
1431unlock:
1432 mutex_unlock(&dpll_lock);
1433 return -ENODEV;
1434}
1435
1436void dpll_post_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
1437 struct genl_info *info)
1438{
1439 mutex_unlock(&dpll_lock);
1440}
1441
1442int
1443dpll_lock_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
1444 struct genl_info *info)
1445{
1446 mutex_lock(&dpll_lock);
1447
1448 return 0;
1449}
1450
1451void
1452dpll_unlock_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
1453 struct genl_info *info)
1454{
1455 mutex_unlock(&dpll_lock);
1456}
1457
9d71b54b
VF
1458int dpll_pin_pre_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
1459 struct genl_info *info)
1460{
1461 int ret;
1462
1463 mutex_lock(&dpll_lock);
1464 if (GENL_REQ_ATTR_CHECK(info, DPLL_A_PIN_ID)) {
1465 ret = -EINVAL;
1466 goto unlock_dev;
1467 }
1468 info->user_ptr[0] = xa_load(&dpll_pin_xa,
1469 nla_get_u32(info->attrs[DPLL_A_PIN_ID]));
db2ec3c9
AK
1470 if (!info->user_ptr[0] ||
1471 !dpll_pin_available(info->user_ptr[0])) {
9d71b54b
VF
1472 NL_SET_ERR_MSG(info->extack, "pin not found");
1473 ret = -ENODEV;
1474 goto unlock_dev;
1475 }
1476
1477 return 0;
1478
1479unlock_dev:
1480 mutex_unlock(&dpll_lock);
1481 return ret;
1482}
1483
1484void dpll_pin_post_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
1485 struct genl_info *info)
1486{
1487 mutex_unlock(&dpll_lock);
1488}