.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
---|
1 | 2 | /* |
---|
2 | 3 | * Reset Controller framework |
---|
3 | 4 | * |
---|
4 | 5 | * Copyright 2013 Philipp Zabel, Pengutronix |
---|
5 | | - * |
---|
6 | | - * This program is free software; you can redistribute it and/or modify |
---|
7 | | - * it under the terms of the GNU General Public License as published by |
---|
8 | | - * the Free Software Foundation; either version 2 of the License, or |
---|
9 | | - * (at your option) any later version. |
---|
10 | 6 | */ |
---|
11 | 7 | #include <linux/atomic.h> |
---|
12 | 8 | #include <linux/device.h> |
---|
.. | .. |
---|
34 | 30 | * @id: ID of the reset controller in the reset |
---|
35 | 31 | * controller device |
---|
36 | 32 | * @refcnt: Number of gets of this reset_control |
---|
| 33 | + * @acquired: Only one reset_control may be acquired for a given rcdev and id. |
---|
37 | 34 | * @shared: Is this a shared (1), or an exclusive (0) reset_control? |
---|
38 | | - * @deassert_cnt: Number of times this reset line has been deasserted |
---|
| 35 | + * @array: Is this an array of reset controls (1)? |
---|
| 36 | + * @deassert_count: Number of times this reset line has been deasserted |
---|
39 | 37 | * @triggered_count: Number of times this reset line has been reset. Currently |
---|
40 | 38 | * only used for shared resets, which means that the value |
---|
41 | 39 | * will be either 0 or 1. |
---|
.. | .. |
---|
45 | 43 | struct list_head list; |
---|
46 | 44 | unsigned int id; |
---|
47 | 45 | struct kref refcnt; |
---|
| 46 | + bool acquired; |
---|
48 | 47 | bool shared; |
---|
49 | 48 | bool array; |
---|
50 | 49 | atomic_t deassert_count; |
---|
.. | .. |
---|
63 | 62 | struct reset_control *rstc[]; |
---|
64 | 63 | }; |
---|
65 | 64 | |
---|
| 65 | +static const char *rcdev_name(struct reset_controller_dev *rcdev) |
---|
| 66 | +{ |
---|
| 67 | + if (rcdev->dev) |
---|
| 68 | + return dev_name(rcdev->dev); |
---|
| 69 | + |
---|
| 70 | + if (rcdev->of_node) |
---|
| 71 | + return rcdev->of_node->full_name; |
---|
| 72 | + |
---|
| 73 | + return NULL; |
---|
| 74 | +} |
---|
| 75 | + |
---|
66 | 76 | /** |
---|
67 | 77 | * of_reset_simple_xlate - translate reset_spec to the reset line number |
---|
68 | 78 | * @rcdev: a pointer to the reset controller device |
---|
69 | 79 | * @reset_spec: reset line specifier as found in the device tree |
---|
70 | | - * @flags: a flags pointer to fill in (optional) |
---|
71 | 80 | * |
---|
72 | | - * This simple translation function should be used for reset controllers |
---|
73 | | - * with 1:1 mapping, where reset lines can be indexed by number without gaps. |
---|
| 81 | + * This static translation function is used by default if of_xlate in |
---|
| 82 | + * :c:type:`reset_controller_dev` is not set. It is useful for all reset |
---|
| 83 | + * controllers with 1:1 mapping, where reset lines can be indexed by number |
---|
| 84 | + * without gaps. |
---|
74 | 85 | */ |
---|
75 | 86 | static int of_reset_simple_xlate(struct reset_controller_dev *rcdev, |
---|
76 | 87 | const struct of_phandle_args *reset_spec) |
---|
.. | .. |
---|
140 | 151 | return -ENOMEM; |
---|
141 | 152 | |
---|
142 | 153 | ret = reset_controller_register(rcdev); |
---|
143 | | - if (!ret) { |
---|
144 | | - *rcdevp = rcdev; |
---|
145 | | - devres_add(dev, rcdevp); |
---|
146 | | - } else { |
---|
| 154 | + if (ret) { |
---|
147 | 155 | devres_free(rcdevp); |
---|
| 156 | + return ret; |
---|
148 | 157 | } |
---|
| 158 | + |
---|
| 159 | + *rcdevp = rcdev; |
---|
| 160 | + devres_add(dev, rcdevp); |
---|
149 | 161 | |
---|
150 | 162 | return ret; |
---|
151 | 163 | } |
---|
.. | .. |
---|
232 | 244 | return ret; |
---|
233 | 245 | } |
---|
234 | 246 | |
---|
| 247 | +static int reset_control_array_acquire(struct reset_control_array *resets) |
---|
| 248 | +{ |
---|
| 249 | + unsigned int i; |
---|
| 250 | + int err; |
---|
| 251 | + |
---|
| 252 | + for (i = 0; i < resets->num_rstcs; i++) { |
---|
| 253 | + err = reset_control_acquire(resets->rstc[i]); |
---|
| 254 | + if (err < 0) |
---|
| 255 | + goto release; |
---|
| 256 | + } |
---|
| 257 | + |
---|
| 258 | + return 0; |
---|
| 259 | + |
---|
| 260 | +release: |
---|
| 261 | + while (i--) |
---|
| 262 | + reset_control_release(resets->rstc[i]); |
---|
| 263 | + |
---|
| 264 | + return err; |
---|
| 265 | +} |
---|
| 266 | + |
---|
| 267 | +static void reset_control_array_release(struct reset_control_array *resets) |
---|
| 268 | +{ |
---|
| 269 | + unsigned int i; |
---|
| 270 | + |
---|
| 271 | + for (i = 0; i < resets->num_rstcs; i++) |
---|
| 272 | + reset_control_release(resets->rstc[i]); |
---|
| 273 | +} |
---|
| 274 | + |
---|
235 | 275 | static inline bool reset_control_is_array(struct reset_control *rstc) |
---|
236 | 276 | { |
---|
237 | 277 | return rstc->array; |
---|
.. | .. |
---|
272 | 312 | |
---|
273 | 313 | if (atomic_inc_return(&rstc->triggered_count) != 1) |
---|
274 | 314 | return 0; |
---|
| 315 | + } else { |
---|
| 316 | + if (!rstc->acquired) |
---|
| 317 | + return -EPERM; |
---|
275 | 318 | } |
---|
276 | 319 | |
---|
277 | 320 | ret = rstc->rcdev->ops->reset(rstc->rcdev, rstc->id); |
---|
.. | .. |
---|
294 | 337 | * internal state to be reset, but must be prepared for this to happen. |
---|
295 | 338 | * Consumers must not use reset_control_reset on shared reset lines when |
---|
296 | 339 | * reset_control_(de)assert has been used. |
---|
297 | | - * return 0. |
---|
298 | 340 | * |
---|
299 | 341 | * If rstc is NULL it is an optional reset and the function will just |
---|
300 | 342 | * return 0. |
---|
.. | .. |
---|
334 | 376 | */ |
---|
335 | 377 | if (!rstc->rcdev->ops->assert) |
---|
336 | 378 | return -ENOTSUPP; |
---|
| 379 | + |
---|
| 380 | + if (!rstc->acquired) { |
---|
| 381 | + WARN(1, "reset %s (ID: %u) is not acquired\n", |
---|
| 382 | + rcdev_name(rstc->rcdev), rstc->id); |
---|
| 383 | + return -EPERM; |
---|
| 384 | + } |
---|
337 | 385 | } |
---|
338 | 386 | |
---|
339 | 387 | return rstc->rcdev->ops->assert(rstc->rcdev, rstc->id); |
---|
.. | .. |
---|
347 | 395 | * After calling this function, the reset is guaranteed to be deasserted. |
---|
348 | 396 | * Consumers must not use reset_control_reset on shared reset lines when |
---|
349 | 397 | * reset_control_(de)assert has been used. |
---|
350 | | - * return 0. |
---|
351 | 398 | * |
---|
352 | 399 | * If rstc is NULL it is an optional reset and the function will just |
---|
353 | 400 | * return 0. |
---|
.. | .. |
---|
369 | 416 | |
---|
370 | 417 | if (atomic_inc_return(&rstc->deassert_count) != 1) |
---|
371 | 418 | return 0; |
---|
| 419 | + } else { |
---|
| 420 | + if (!rstc->acquired) { |
---|
| 421 | + WARN(1, "reset %s (ID: %u) is not acquired\n", |
---|
| 422 | + rcdev_name(rstc->rcdev), rstc->id); |
---|
| 423 | + return -EPERM; |
---|
| 424 | + } |
---|
372 | 425 | } |
---|
373 | 426 | |
---|
374 | 427 | /* |
---|
.. | .. |
---|
406 | 459 | } |
---|
407 | 460 | EXPORT_SYMBOL_GPL(reset_control_status); |
---|
408 | 461 | |
---|
| 462 | +/** |
---|
| 463 | + * reset_control_acquire() - acquires a reset control for exclusive use |
---|
| 464 | + * @rstc: reset control |
---|
| 465 | + * |
---|
| 466 | + * This is used to explicitly acquire a reset control for exclusive use. Note |
---|
| 467 | + * that exclusive resets are requested as acquired by default. In order for a |
---|
| 468 | + * second consumer to be able to control the reset, the first consumer has to |
---|
| 469 | + * release it first. Typically the easiest way to achieve this is to call the |
---|
| 470 | + * reset_control_get_exclusive_released() to obtain an instance of the reset |
---|
| 471 | + * control. Such reset controls are not acquired by default. |
---|
| 472 | + * |
---|
| 473 | + * Consumers implementing shared access to an exclusive reset need to follow |
---|
| 474 | + * a specific protocol in order to work together. Before consumers can change |
---|
| 475 | + * a reset they must acquire exclusive access using reset_control_acquire(). |
---|
| 476 | + * After they are done operating the reset, they must release exclusive access |
---|
| 477 | + * with a call to reset_control_release(). Consumers are not granted exclusive |
---|
| 478 | + * access to the reset as long as another consumer hasn't released a reset. |
---|
| 479 | + * |
---|
| 480 | + * See also: reset_control_release() |
---|
| 481 | + */ |
---|
| 482 | +int reset_control_acquire(struct reset_control *rstc) |
---|
| 483 | +{ |
---|
| 484 | + struct reset_control *rc; |
---|
| 485 | + |
---|
| 486 | + if (!rstc) |
---|
| 487 | + return 0; |
---|
| 488 | + |
---|
| 489 | + if (WARN_ON(IS_ERR(rstc))) |
---|
| 490 | + return -EINVAL; |
---|
| 491 | + |
---|
| 492 | + if (reset_control_is_array(rstc)) |
---|
| 493 | + return reset_control_array_acquire(rstc_to_array(rstc)); |
---|
| 494 | + |
---|
| 495 | + mutex_lock(&reset_list_mutex); |
---|
| 496 | + |
---|
| 497 | + if (rstc->acquired) { |
---|
| 498 | + mutex_unlock(&reset_list_mutex); |
---|
| 499 | + return 0; |
---|
| 500 | + } |
---|
| 501 | + |
---|
| 502 | + list_for_each_entry(rc, &rstc->rcdev->reset_control_head, list) { |
---|
| 503 | + if (rstc != rc && rstc->id == rc->id) { |
---|
| 504 | + if (rc->acquired) { |
---|
| 505 | + mutex_unlock(&reset_list_mutex); |
---|
| 506 | + return -EBUSY; |
---|
| 507 | + } |
---|
| 508 | + } |
---|
| 509 | + } |
---|
| 510 | + |
---|
| 511 | + rstc->acquired = true; |
---|
| 512 | + |
---|
| 513 | + mutex_unlock(&reset_list_mutex); |
---|
| 514 | + return 0; |
---|
| 515 | +} |
---|
| 516 | +EXPORT_SYMBOL_GPL(reset_control_acquire); |
---|
| 517 | + |
---|
| 518 | +/** |
---|
| 519 | + * reset_control_release() - releases exclusive access to a reset control |
---|
| 520 | + * @rstc: reset control |
---|
| 521 | + * |
---|
| 522 | + * Releases exclusive access right to a reset control previously obtained by a |
---|
| 523 | + * call to reset_control_acquire(). Until a consumer calls this function, no |
---|
| 524 | + * other consumers will be granted exclusive access. |
---|
| 525 | + * |
---|
| 526 | + * See also: reset_control_acquire() |
---|
| 527 | + */ |
---|
| 528 | +void reset_control_release(struct reset_control *rstc) |
---|
| 529 | +{ |
---|
| 530 | + if (!rstc || WARN_ON(IS_ERR(rstc))) |
---|
| 531 | + return; |
---|
| 532 | + |
---|
| 533 | + if (reset_control_is_array(rstc)) |
---|
| 534 | + reset_control_array_release(rstc_to_array(rstc)); |
---|
| 535 | + else |
---|
| 536 | + rstc->acquired = false; |
---|
| 537 | +} |
---|
| 538 | +EXPORT_SYMBOL_GPL(reset_control_release); |
---|
| 539 | + |
---|
409 | 540 | static struct reset_control *__reset_control_get_internal( |
---|
410 | 541 | struct reset_controller_dev *rcdev, |
---|
411 | | - unsigned int index, bool shared) |
---|
| 542 | + unsigned int index, bool shared, bool acquired) |
---|
412 | 543 | { |
---|
413 | 544 | struct reset_control *rstc; |
---|
414 | 545 | |
---|
.. | .. |
---|
416 | 547 | |
---|
417 | 548 | list_for_each_entry(rstc, &rcdev->reset_control_head, list) { |
---|
418 | 549 | if (rstc->id == index) { |
---|
| 550 | + /* |
---|
| 551 | + * Allow creating a secondary exclusive reset_control |
---|
| 552 | + * that is initially not acquired for an already |
---|
| 553 | + * controlled reset line. |
---|
| 554 | + */ |
---|
| 555 | + if (!rstc->shared && !shared && !acquired) |
---|
| 556 | + break; |
---|
| 557 | + |
---|
419 | 558 | if (WARN_ON(!rstc->shared || !shared)) |
---|
420 | 559 | return ERR_PTR(-EBUSY); |
---|
421 | 560 | |
---|
.. | .. |
---|
437 | 576 | list_add(&rstc->list, &rcdev->reset_control_head); |
---|
438 | 577 | rstc->id = index; |
---|
439 | 578 | kref_init(&rstc->refcnt); |
---|
| 579 | + rstc->acquired = acquired; |
---|
440 | 580 | rstc->shared = shared; |
---|
441 | 581 | |
---|
442 | 582 | return rstc; |
---|
.. | .. |
---|
464 | 604 | |
---|
465 | 605 | struct reset_control *__of_reset_control_get(struct device_node *node, |
---|
466 | 606 | const char *id, int index, bool shared, |
---|
467 | | - bool optional) |
---|
| 607 | + bool optional, bool acquired) |
---|
468 | 608 | { |
---|
469 | 609 | struct reset_control *rstc; |
---|
470 | 610 | struct reset_controller_dev *r, *rcdev; |
---|
.. | .. |
---|
517 | 657 | } |
---|
518 | 658 | |
---|
519 | 659 | /* reset_list_mutex also protects the rcdev's reset_control list */ |
---|
520 | | - rstc = __reset_control_get_internal(rcdev, rstc_id, shared); |
---|
| 660 | + rstc = __reset_control_get_internal(rcdev, rstc_id, shared, acquired); |
---|
521 | 661 | |
---|
522 | 662 | out: |
---|
523 | 663 | mutex_unlock(&reset_list_mutex); |
---|
.. | .. |
---|
547 | 687 | |
---|
548 | 688 | static struct reset_control * |
---|
549 | 689 | __reset_control_get_from_lookup(struct device *dev, const char *con_id, |
---|
550 | | - bool shared, bool optional) |
---|
| 690 | + bool shared, bool optional, bool acquired) |
---|
551 | 691 | { |
---|
552 | 692 | const struct reset_control_lookup *lookup; |
---|
553 | 693 | struct reset_controller_dev *rcdev; |
---|
554 | 694 | const char *dev_id = dev_name(dev); |
---|
555 | 695 | struct reset_control *rstc = NULL; |
---|
556 | | - |
---|
557 | | - if (!dev) |
---|
558 | | - return ERR_PTR(-EINVAL); |
---|
559 | 696 | |
---|
560 | 697 | mutex_lock(&reset_lookup_mutex); |
---|
561 | 698 | |
---|
.. | .. |
---|
577 | 714 | |
---|
578 | 715 | rstc = __reset_control_get_internal(rcdev, |
---|
579 | 716 | lookup->index, |
---|
580 | | - shared); |
---|
| 717 | + shared, acquired); |
---|
581 | 718 | mutex_unlock(&reset_list_mutex); |
---|
582 | 719 | break; |
---|
583 | 720 | } |
---|
.. | .. |
---|
592 | 729 | } |
---|
593 | 730 | |
---|
594 | 731 | struct reset_control *__reset_control_get(struct device *dev, const char *id, |
---|
595 | | - int index, bool shared, bool optional) |
---|
| 732 | + int index, bool shared, bool optional, |
---|
| 733 | + bool acquired) |
---|
596 | 734 | { |
---|
| 735 | + if (WARN_ON(shared && acquired)) |
---|
| 736 | + return ERR_PTR(-EINVAL); |
---|
| 737 | + |
---|
597 | 738 | if (dev->of_node) |
---|
598 | 739 | return __of_reset_control_get(dev->of_node, id, index, shared, |
---|
599 | | - optional); |
---|
| 740 | + optional, acquired); |
---|
600 | 741 | |
---|
601 | | - return __reset_control_get_from_lookup(dev, id, shared, optional); |
---|
| 742 | + return __reset_control_get_from_lookup(dev, id, shared, optional, |
---|
| 743 | + acquired); |
---|
602 | 744 | } |
---|
603 | 745 | EXPORT_SYMBOL_GPL(__reset_control_get); |
---|
604 | 746 | |
---|
.. | .. |
---|
640 | 782 | |
---|
641 | 783 | struct reset_control *__devm_reset_control_get(struct device *dev, |
---|
642 | 784 | const char *id, int index, bool shared, |
---|
643 | | - bool optional) |
---|
| 785 | + bool optional, bool acquired) |
---|
644 | 786 | { |
---|
645 | 787 | struct reset_control **ptr, *rstc; |
---|
646 | 788 | |
---|
.. | .. |
---|
649 | 791 | if (!ptr) |
---|
650 | 792 | return ERR_PTR(-ENOMEM); |
---|
651 | 793 | |
---|
652 | | - rstc = __reset_control_get(dev, id, index, shared, optional); |
---|
653 | | - if (!IS_ERR(rstc)) { |
---|
654 | | - *ptr = rstc; |
---|
655 | | - devres_add(dev, ptr); |
---|
656 | | - } else { |
---|
| 794 | + rstc = __reset_control_get(dev, id, index, shared, optional, acquired); |
---|
| 795 | + if (IS_ERR_OR_NULL(rstc)) { |
---|
657 | 796 | devres_free(ptr); |
---|
| 797 | + return rstc; |
---|
658 | 798 | } |
---|
| 799 | + |
---|
| 800 | + *ptr = rstc; |
---|
| 801 | + devres_add(dev, ptr); |
---|
659 | 802 | |
---|
660 | 803 | return rstc; |
---|
661 | 804 | } |
---|
.. | .. |
---|
676 | 819 | struct reset_control *rstc; |
---|
677 | 820 | int ret; |
---|
678 | 821 | |
---|
679 | | - rstc = __reset_control_get(dev, NULL, 0, 0, optional); |
---|
| 822 | + rstc = __reset_control_get(dev, NULL, 0, 0, optional, true); |
---|
680 | 823 | if (IS_ERR(rstc)) |
---|
681 | 824 | return PTR_ERR(rstc); |
---|
682 | 825 | |
---|
.. | .. |
---|
688 | 831 | } |
---|
689 | 832 | EXPORT_SYMBOL_GPL(__device_reset); |
---|
690 | 833 | |
---|
691 | | -/** |
---|
| 834 | +/* |
---|
692 | 835 | * APIs to manage an array of reset controls. |
---|
693 | 836 | */ |
---|
| 837 | + |
---|
694 | 838 | /** |
---|
695 | 839 | * of_reset_control_get_count - Count number of resets available with a device |
---|
696 | 840 | * |
---|
.. | .. |
---|
720 | 864 | * @np: device node for the device that requests the reset controls array |
---|
721 | 865 | * @shared: whether reset controls are shared or not |
---|
722 | 866 | * @optional: whether it is optional to get the reset controls |
---|
| 867 | + * @acquired: only one reset control may be acquired for a given controller |
---|
| 868 | + * and ID |
---|
723 | 869 | * |
---|
724 | | - * Returns pointer to allocated reset_control_array on success or |
---|
725 | | - * error on failure |
---|
| 870 | + * Returns pointer to allocated reset_control on success or error on failure |
---|
726 | 871 | */ |
---|
727 | 872 | struct reset_control * |
---|
728 | | -of_reset_control_array_get(struct device_node *np, bool shared, bool optional) |
---|
| 873 | +of_reset_control_array_get(struct device_node *np, bool shared, bool optional, |
---|
| 874 | + bool acquired) |
---|
729 | 875 | { |
---|
730 | 876 | struct reset_control_array *resets; |
---|
731 | 877 | struct reset_control *rstc; |
---|
.. | .. |
---|
740 | 886 | return ERR_PTR(-ENOMEM); |
---|
741 | 887 | |
---|
742 | 888 | for (i = 0; i < num; i++) { |
---|
743 | | - rstc = __of_reset_control_get(np, NULL, i, shared, optional); |
---|
| 889 | + rstc = __of_reset_control_get(np, NULL, i, shared, optional, |
---|
| 890 | + acquired); |
---|
744 | 891 | if (IS_ERR(rstc)) |
---|
745 | 892 | goto err_rst; |
---|
746 | 893 | resets->rstc[i] = rstc; |
---|
.. | .. |
---|
773 | 920 | * that just have to be asserted or deasserted, without any |
---|
774 | 921 | * requirements on the order. |
---|
775 | 922 | * |
---|
776 | | - * Returns pointer to allocated reset_control_array on success or |
---|
777 | | - * error on failure |
---|
| 923 | + * Returns pointer to allocated reset_control on success or error on failure |
---|
778 | 924 | */ |
---|
779 | 925 | struct reset_control * |
---|
780 | 926 | devm_reset_control_array_get(struct device *dev, bool shared, bool optional) |
---|
781 | 927 | { |
---|
782 | | - struct reset_control **devres; |
---|
783 | | - struct reset_control *rstc; |
---|
| 928 | + struct reset_control **ptr, *rstc; |
---|
784 | 929 | |
---|
785 | | - devres = devres_alloc(devm_reset_control_release, sizeof(*devres), |
---|
786 | | - GFP_KERNEL); |
---|
787 | | - if (!devres) |
---|
| 930 | + ptr = devres_alloc(devm_reset_control_release, sizeof(*ptr), |
---|
| 931 | + GFP_KERNEL); |
---|
| 932 | + if (!ptr) |
---|
788 | 933 | return ERR_PTR(-ENOMEM); |
---|
789 | 934 | |
---|
790 | | - rstc = of_reset_control_array_get(dev->of_node, shared, optional); |
---|
791 | | - if (IS_ERR(rstc)) { |
---|
792 | | - devres_free(devres); |
---|
| 935 | + rstc = of_reset_control_array_get(dev->of_node, shared, optional, true); |
---|
| 936 | + if (IS_ERR_OR_NULL(rstc)) { |
---|
| 937 | + devres_free(ptr); |
---|
793 | 938 | return rstc; |
---|
794 | 939 | } |
---|
795 | 940 | |
---|
796 | | - *devres = rstc; |
---|
797 | | - devres_add(dev, devres); |
---|
| 941 | + *ptr = rstc; |
---|
| 942 | + devres_add(dev, ptr); |
---|
798 | 943 | |
---|
799 | 944 | return rstc; |
---|
800 | 945 | } |
---|
801 | 946 | EXPORT_SYMBOL_GPL(devm_reset_control_array_get); |
---|
| 947 | + |
---|
| 948 | +static int reset_control_get_count_from_lookup(struct device *dev) |
---|
| 949 | +{ |
---|
| 950 | + const struct reset_control_lookup *lookup; |
---|
| 951 | + const char *dev_id; |
---|
| 952 | + int count = 0; |
---|
| 953 | + |
---|
| 954 | + if (!dev) |
---|
| 955 | + return -EINVAL; |
---|
| 956 | + |
---|
| 957 | + dev_id = dev_name(dev); |
---|
| 958 | + mutex_lock(&reset_lookup_mutex); |
---|
| 959 | + |
---|
| 960 | + list_for_each_entry(lookup, &reset_lookup_list, list) { |
---|
| 961 | + if (!strcmp(lookup->dev_id, dev_id)) |
---|
| 962 | + count++; |
---|
| 963 | + } |
---|
| 964 | + |
---|
| 965 | + mutex_unlock(&reset_lookup_mutex); |
---|
| 966 | + |
---|
| 967 | + if (count == 0) |
---|
| 968 | + count = -ENOENT; |
---|
| 969 | + |
---|
| 970 | + return count; |
---|
| 971 | +} |
---|
| 972 | + |
---|
| 973 | +/** |
---|
| 974 | + * reset_control_get_count - Count number of resets available with a device |
---|
| 975 | + * |
---|
| 976 | + * @dev: device for which to return the number of resets |
---|
| 977 | + * |
---|
| 978 | + * Returns positive reset count on success, or error number on failure and |
---|
| 979 | + * on count being zero. |
---|
| 980 | + */ |
---|
| 981 | +int reset_control_get_count(struct device *dev) |
---|
| 982 | +{ |
---|
| 983 | + if (dev->of_node) |
---|
| 984 | + return of_reset_control_get_count(dev->of_node); |
---|
| 985 | + |
---|
| 986 | + return reset_control_get_count_from_lookup(dev); |
---|
| 987 | +} |
---|
| 988 | +EXPORT_SYMBOL_GPL(reset_control_get_count); |
---|