diff options
Diffstat (limited to 'drivers/of/dynamic.c')
-rw-r--r-- | drivers/of/dynamic.c | 135 |
1 files changed, 115 insertions, 20 deletions
diff --git a/drivers/of/dynamic.c b/drivers/of/dynamic.c index 4bc96af4d8e2..747d87619faf 100644 --- a/drivers/of/dynamic.c +++ b/drivers/of/dynamic.c | |||
@@ -491,11 +491,12 @@ static void __of_changeset_entry_invert(struct of_changeset_entry *ce, | |||
491 | } | 491 | } |
492 | } | 492 | } |
493 | 493 | ||
494 | static void __of_changeset_entry_notify(struct of_changeset_entry *ce, bool revert) | 494 | static int __of_changeset_entry_notify(struct of_changeset_entry *ce, |
495 | bool revert) | ||
495 | { | 496 | { |
496 | struct of_reconfig_data rd; | 497 | struct of_reconfig_data rd; |
497 | struct of_changeset_entry ce_inverted; | 498 | struct of_changeset_entry ce_inverted; |
498 | int ret; | 499 | int ret = 0; |
499 | 500 | ||
500 | if (revert) { | 501 | if (revert) { |
501 | __of_changeset_entry_invert(ce, &ce_inverted); | 502 | __of_changeset_entry_invert(ce, &ce_inverted); |
@@ -517,11 +518,12 @@ static void __of_changeset_entry_notify(struct of_changeset_entry *ce, bool reve | |||
517 | default: | 518 | default: |
518 | pr_err("invalid devicetree changeset action: %i\n", | 519 | pr_err("invalid devicetree changeset action: %i\n", |
519 | (int)ce->action); | 520 | (int)ce->action); |
520 | return; | 521 | ret = -EINVAL; |
521 | } | 522 | } |
522 | 523 | ||
523 | if (ret) | 524 | if (ret) |
524 | pr_err("changeset notifier error @%pOF\n", ce->np); | 525 | pr_err("changeset notifier error @%pOF\n", ce->np); |
526 | return ret; | ||
525 | } | 527 | } |
526 | 528 | ||
527 | static int __of_changeset_entry_apply(struct of_changeset_entry *ce) | 529 | static int __of_changeset_entry_apply(struct of_changeset_entry *ce) |
@@ -655,32 +657,82 @@ void of_changeset_destroy(struct of_changeset *ocs) | |||
655 | } | 657 | } |
656 | EXPORT_SYMBOL_GPL(of_changeset_destroy); | 658 | EXPORT_SYMBOL_GPL(of_changeset_destroy); |
657 | 659 | ||
658 | int __of_changeset_apply(struct of_changeset *ocs) | 660 | /* |
661 | * Apply the changeset entries in @ocs. | ||
662 | * If apply fails, an attempt is made to revert the entries that were | ||
663 | * successfully applied. | ||
664 | * | ||
665 | * If multiple revert errors occur then only the final revert error is reported. | ||
666 | * | ||
667 | * Returns 0 on success, a negative error value in case of an error. | ||
668 | * If a revert error occurs, it is returned in *ret_revert. | ||
669 | */ | ||
670 | int __of_changeset_apply_entries(struct of_changeset *ocs, int *ret_revert) | ||
659 | { | 671 | { |
660 | struct of_changeset_entry *ce; | 672 | struct of_changeset_entry *ce; |
661 | int ret; | 673 | int ret, ret_tmp; |
662 | 674 | ||
663 | /* perform the rest of the work */ | ||
664 | pr_debug("changeset: applying...\n"); | 675 | pr_debug("changeset: applying...\n"); |
665 | list_for_each_entry(ce, &ocs->entries, node) { | 676 | list_for_each_entry(ce, &ocs->entries, node) { |
666 | ret = __of_changeset_entry_apply(ce); | 677 | ret = __of_changeset_entry_apply(ce); |
667 | if (ret) { | 678 | if (ret) { |
668 | pr_err("Error applying changeset (%d)\n", ret); | 679 | pr_err("Error applying changeset (%d)\n", ret); |
669 | list_for_each_entry_continue_reverse(ce, &ocs->entries, node) | 680 | list_for_each_entry_continue_reverse(ce, &ocs->entries, |
670 | __of_changeset_entry_revert(ce); | 681 | node) { |
682 | ret_tmp = __of_changeset_entry_revert(ce); | ||
683 | if (ret_tmp) | ||
684 | *ret_revert = ret_tmp; | ||
685 | } | ||
671 | return ret; | 686 | return ret; |
672 | } | 687 | } |
673 | } | 688 | } |
674 | pr_debug("changeset: applied, emitting notifiers.\n"); | 689 | |
690 | return 0; | ||
691 | } | ||
692 | |||
693 | /* | ||
694 | * Returns 0 on success, a negative error value in case of an error. | ||
695 | * | ||
696 | * If multiple changset entry notification errors occur then only the | ||
697 | * final notification error is reported. | ||
698 | */ | ||
699 | int __of_changeset_apply_notify(struct of_changeset *ocs) | ||
700 | { | ||
701 | struct of_changeset_entry *ce; | ||
702 | int ret = 0, ret_tmp; | ||
703 | |||
704 | pr_debug("changeset: emitting notifiers.\n"); | ||
675 | 705 | ||
676 | /* drop the global lock while emitting notifiers */ | 706 | /* drop the global lock while emitting notifiers */ |
677 | mutex_unlock(&of_mutex); | 707 | mutex_unlock(&of_mutex); |
678 | list_for_each_entry(ce, &ocs->entries, node) | 708 | list_for_each_entry(ce, &ocs->entries, node) { |
679 | __of_changeset_entry_notify(ce, 0); | 709 | ret_tmp = __of_changeset_entry_notify(ce, 0); |
710 | if (ret_tmp) | ||
711 | ret = ret_tmp; | ||
712 | } | ||
680 | mutex_lock(&of_mutex); | 713 | mutex_lock(&of_mutex); |
681 | pr_debug("changeset: notifiers sent.\n"); | 714 | pr_debug("changeset: notifiers sent.\n"); |
682 | 715 | ||
683 | return 0; | 716 | return ret; |
717 | } | ||
718 | |||
719 | /* | ||
720 | * Returns 0 on success, a negative error value in case of an error. | ||
721 | * | ||
722 | * If a changeset entry apply fails, an attempt is made to revert any | ||
723 | * previous entries in the changeset. If any of the reverts fails, | ||
724 | * that failure is not reported. Thus the state of the device tree | ||
725 | * is unknown if an apply error occurs. | ||
726 | */ | ||
727 | static int __of_changeset_apply(struct of_changeset *ocs) | ||
728 | { | ||
729 | int ret, ret_revert = 0; | ||
730 | |||
731 | ret = __of_changeset_apply_entries(ocs, &ret_revert); | ||
732 | if (!ret) | ||
733 | ret = __of_changeset_apply_notify(ocs); | ||
734 | |||
735 | return ret; | ||
684 | } | 736 | } |
685 | 737 | ||
686 | /** | 738 | /** |
@@ -707,31 +759,74 @@ int of_changeset_apply(struct of_changeset *ocs) | |||
707 | } | 759 | } |
708 | EXPORT_SYMBOL_GPL(of_changeset_apply); | 760 | EXPORT_SYMBOL_GPL(of_changeset_apply); |
709 | 761 | ||
710 | int __of_changeset_revert(struct of_changeset *ocs) | 762 | /* |
763 | * Revert the changeset entries in @ocs. | ||
764 | * If revert fails, an attempt is made to re-apply the entries that were | ||
765 | * successfully removed. | ||
766 | * | ||
767 | * If multiple re-apply errors occur then only the final apply error is | ||
768 | * reported. | ||
769 | * | ||
770 | * Returns 0 on success, a negative error value in case of an error. | ||
771 | * If an apply error occurs, it is returned in *ret_apply. | ||
772 | */ | ||
773 | int __of_changeset_revert_entries(struct of_changeset *ocs, int *ret_apply) | ||
711 | { | 774 | { |
712 | struct of_changeset_entry *ce; | 775 | struct of_changeset_entry *ce; |
713 | int ret; | 776 | int ret, ret_tmp; |
714 | 777 | ||
715 | pr_debug("changeset: reverting...\n"); | 778 | pr_debug("changeset: reverting...\n"); |
716 | list_for_each_entry_reverse(ce, &ocs->entries, node) { | 779 | list_for_each_entry_reverse(ce, &ocs->entries, node) { |
717 | ret = __of_changeset_entry_revert(ce); | 780 | ret = __of_changeset_entry_revert(ce); |
718 | if (ret) { | 781 | if (ret) { |
719 | pr_err("Error reverting changeset (%d)\n", ret); | 782 | pr_err("Error reverting changeset (%d)\n", ret); |
720 | list_for_each_entry_continue(ce, &ocs->entries, node) | 783 | list_for_each_entry_continue(ce, &ocs->entries, node) { |
721 | __of_changeset_entry_apply(ce); | 784 | ret_tmp = __of_changeset_entry_apply(ce); |
785 | if (ret_tmp) | ||
786 | *ret_apply = ret_tmp; | ||
787 | } | ||
722 | return ret; | 788 | return ret; |
723 | } | 789 | } |
724 | } | 790 | } |
725 | pr_debug("changeset: reverted, emitting notifiers.\n"); | 791 | |
792 | return 0; | ||
793 | } | ||
794 | |||
795 | /* | ||
796 | * If multiple changset entry notification errors occur then only the | ||
797 | * final notification error is reported. | ||
798 | */ | ||
799 | int __of_changeset_revert_notify(struct of_changeset *ocs) | ||
800 | { | ||
801 | struct of_changeset_entry *ce; | ||
802 | int ret = 0, ret_tmp; | ||
803 | |||
804 | pr_debug("changeset: emitting notifiers.\n"); | ||
726 | 805 | ||
727 | /* drop the global lock while emitting notifiers */ | 806 | /* drop the global lock while emitting notifiers */ |
728 | mutex_unlock(&of_mutex); | 807 | mutex_unlock(&of_mutex); |
729 | list_for_each_entry_reverse(ce, &ocs->entries, node) | 808 | list_for_each_entry_reverse(ce, &ocs->entries, node) { |
730 | __of_changeset_entry_notify(ce, 1); | 809 | ret_tmp = __of_changeset_entry_notify(ce, 1); |
810 | if (ret_tmp) | ||
811 | ret = ret_tmp; | ||
812 | } | ||
731 | mutex_lock(&of_mutex); | 813 | mutex_lock(&of_mutex); |
732 | pr_debug("changeset: notifiers sent.\n"); | 814 | pr_debug("changeset: notifiers sent.\n"); |
733 | 815 | ||
734 | return 0; | 816 | return ret; |
817 | } | ||
818 | |||
819 | static int __of_changeset_revert(struct of_changeset *ocs) | ||
820 | { | ||
821 | int ret, ret_reply; | ||
822 | |||
823 | ret_reply = 0; | ||
824 | ret = __of_changeset_revert_entries(ocs, &ret_reply); | ||
825 | |||
826 | if (!ret) | ||
827 | ret = __of_changeset_revert_notify(ocs); | ||
828 | |||
829 | return ret; | ||
735 | } | 830 | } |
736 | 831 | ||
737 | /** | 832 | /** |