| .. | .. |
|---|
| 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); |
|---|