diff options
author | Viresh Kumar <viresh.kumar@linaro.org> | 2015-12-08 21:31:46 -0500 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2015-12-10 17:08:51 -0500 |
commit | 7de36b0aa51a5a59e28fb2da768fa3ab07de0674 (patch) | |
tree | a46061b13b61ebf6c8c31ddb92f95590828dedae /drivers | |
parent | dc4e7b1fa20a840d2317fcfdaa1064fc09d2afcb (diff) |
PM / OPP: Parse 'opp-supported-hw' binding
OPP bindings allow a platform to enable OPPs based on the version of the
hardware they are used for.
Add support to the OPP-core to parse these bindings, by introducing
dev_pm_opp_{set|put}_supported_hw() APIs.
Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
Tested-by: Lee Jones <lee.jones@linaro.org>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/base/power/opp/core.c | 148 | ||||
-rw-r--r-- | drivers/base/power/opp/opp.h | 5 |
2 files changed, 153 insertions, 0 deletions
diff --git a/drivers/base/power/opp/core.c b/drivers/base/power/opp/core.c index 6aa172be6e8e..55cf1a99b532 100644 --- a/drivers/base/power/opp/core.c +++ b/drivers/base/power/opp/core.c | |||
@@ -559,6 +559,9 @@ static void _remove_device_opp(struct device_opp *dev_opp) | |||
559 | if (!list_empty(&dev_opp->opp_list)) | 559 | if (!list_empty(&dev_opp->opp_list)) |
560 | return; | 560 | return; |
561 | 561 | ||
562 | if (dev_opp->supported_hw) | ||
563 | return; | ||
564 | |||
562 | list_dev = list_first_entry(&dev_opp->dev_list, struct device_list_opp, | 565 | list_dev = list_first_entry(&dev_opp->dev_list, struct device_list_opp, |
563 | node); | 566 | node); |
564 | 567 | ||
@@ -834,6 +837,145 @@ static int opp_parse_supplies(struct dev_pm_opp *opp, struct device *dev) | |||
834 | } | 837 | } |
835 | 838 | ||
836 | /** | 839 | /** |
840 | * dev_pm_opp_set_supported_hw() - Set supported platforms | ||
841 | * @dev: Device for which supported-hw has to be set. | ||
842 | * @versions: Array of hierarchy of versions to match. | ||
843 | * @count: Number of elements in the array. | ||
844 | * | ||
845 | * This is required only for the V2 bindings, and it enables a platform to | ||
846 | * specify the hierarchy of versions it supports. OPP layer will then enable | ||
847 | * OPPs, which are available for those versions, based on its 'opp-supported-hw' | ||
848 | * property. | ||
849 | * | ||
850 | * Locking: The internal device_opp and opp structures are RCU protected. | ||
851 | * Hence this function internally uses RCU updater strategy with mutex locks | ||
852 | * to keep the integrity of the internal data structures. Callers should ensure | ||
853 | * that this function is *NOT* called under RCU protection or in contexts where | ||
854 | * mutex cannot be locked. | ||
855 | */ | ||
856 | int dev_pm_opp_set_supported_hw(struct device *dev, const u32 *versions, | ||
857 | unsigned int count) | ||
858 | { | ||
859 | struct device_opp *dev_opp; | ||
860 | int ret = 0; | ||
861 | |||
862 | /* Hold our list modification lock here */ | ||
863 | mutex_lock(&dev_opp_list_lock); | ||
864 | |||
865 | dev_opp = _add_device_opp(dev); | ||
866 | if (!dev_opp) { | ||
867 | ret = -ENOMEM; | ||
868 | goto unlock; | ||
869 | } | ||
870 | |||
871 | /* Make sure there are no concurrent readers while updating dev_opp */ | ||
872 | WARN_ON(!list_empty(&dev_opp->opp_list)); | ||
873 | |||
874 | /* Do we already have a version hierarchy associated with dev_opp? */ | ||
875 | if (dev_opp->supported_hw) { | ||
876 | dev_err(dev, "%s: Already have supported hardware list\n", | ||
877 | __func__); | ||
878 | ret = -EBUSY; | ||
879 | goto err; | ||
880 | } | ||
881 | |||
882 | dev_opp->supported_hw = kmemdup(versions, count * sizeof(*versions), | ||
883 | GFP_KERNEL); | ||
884 | if (!dev_opp->supported_hw) { | ||
885 | ret = -ENOMEM; | ||
886 | goto err; | ||
887 | } | ||
888 | |||
889 | dev_opp->supported_hw_count = count; | ||
890 | mutex_unlock(&dev_opp_list_lock); | ||
891 | return 0; | ||
892 | |||
893 | err: | ||
894 | _remove_device_opp(dev_opp); | ||
895 | unlock: | ||
896 | mutex_unlock(&dev_opp_list_lock); | ||
897 | |||
898 | return ret; | ||
899 | } | ||
900 | EXPORT_SYMBOL_GPL(dev_pm_opp_set_supported_hw); | ||
901 | |||
902 | /** | ||
903 | * dev_pm_opp_put_supported_hw() - Releases resources blocked for supported hw | ||
904 | * @dev: Device for which supported-hw has to be set. | ||
905 | * | ||
906 | * This is required only for the V2 bindings, and is called for a matching | ||
907 | * dev_pm_opp_set_supported_hw(). Until this is called, the device_opp structure | ||
908 | * will not be freed. | ||
909 | * | ||
910 | * Locking: The internal device_opp and opp structures are RCU protected. | ||
911 | * Hence this function internally uses RCU updater strategy with mutex locks | ||
912 | * to keep the integrity of the internal data structures. Callers should ensure | ||
913 | * that this function is *NOT* called under RCU protection or in contexts where | ||
914 | * mutex cannot be locked. | ||
915 | */ | ||
916 | void dev_pm_opp_put_supported_hw(struct device *dev) | ||
917 | { | ||
918 | struct device_opp *dev_opp; | ||
919 | |||
920 | /* Hold our list modification lock here */ | ||
921 | mutex_lock(&dev_opp_list_lock); | ||
922 | |||
923 | /* Check for existing list for 'dev' first */ | ||
924 | dev_opp = _find_device_opp(dev); | ||
925 | if (IS_ERR(dev_opp)) { | ||
926 | dev_err(dev, "Failed to find dev_opp: %ld\n", PTR_ERR(dev_opp)); | ||
927 | goto unlock; | ||
928 | } | ||
929 | |||
930 | /* Make sure there are no concurrent readers while updating dev_opp */ | ||
931 | WARN_ON(!list_empty(&dev_opp->opp_list)); | ||
932 | |||
933 | if (!dev_opp->supported_hw) { | ||
934 | dev_err(dev, "%s: Doesn't have supported hardware list\n", | ||
935 | __func__); | ||
936 | goto unlock; | ||
937 | } | ||
938 | |||
939 | kfree(dev_opp->supported_hw); | ||
940 | dev_opp->supported_hw = NULL; | ||
941 | dev_opp->supported_hw_count = 0; | ||
942 | |||
943 | /* Try freeing device_opp if this was the last blocking resource */ | ||
944 | _remove_device_opp(dev_opp); | ||
945 | |||
946 | unlock: | ||
947 | mutex_unlock(&dev_opp_list_lock); | ||
948 | } | ||
949 | EXPORT_SYMBOL_GPL(dev_pm_opp_put_supported_hw); | ||
950 | |||
951 | static bool _opp_is_supported(struct device *dev, struct device_opp *dev_opp, | ||
952 | struct device_node *np) | ||
953 | { | ||
954 | unsigned int count = dev_opp->supported_hw_count; | ||
955 | u32 version; | ||
956 | int ret; | ||
957 | |||
958 | if (!dev_opp->supported_hw) | ||
959 | return true; | ||
960 | |||
961 | while (count--) { | ||
962 | ret = of_property_read_u32_index(np, "opp-supported-hw", count, | ||
963 | &version); | ||
964 | if (ret) { | ||
965 | dev_warn(dev, "%s: failed to read opp-supported-hw property at index %d: %d\n", | ||
966 | __func__, count, ret); | ||
967 | return false; | ||
968 | } | ||
969 | |||
970 | /* Both of these are bitwise masks of the versions */ | ||
971 | if (!(version & dev_opp->supported_hw[count])) | ||
972 | return false; | ||
973 | } | ||
974 | |||
975 | return true; | ||
976 | } | ||
977 | |||
978 | /** | ||
837 | * _opp_add_static_v2() - Allocate static OPPs (As per 'v2' DT bindings) | 979 | * _opp_add_static_v2() - Allocate static OPPs (As per 'v2' DT bindings) |
838 | * @dev: device for which we do this operation | 980 | * @dev: device for which we do this operation |
839 | * @np: device node | 981 | * @np: device node |
@@ -879,6 +1021,12 @@ static int _opp_add_static_v2(struct device *dev, struct device_node *np) | |||
879 | goto free_opp; | 1021 | goto free_opp; |
880 | } | 1022 | } |
881 | 1023 | ||
1024 | /* Check if the OPP supports hardware's hierarchy of versions or not */ | ||
1025 | if (!_opp_is_supported(dev, dev_opp, np)) { | ||
1026 | dev_dbg(dev, "OPP not supported by hardware: %llu\n", rate); | ||
1027 | goto free_opp; | ||
1028 | } | ||
1029 | |||
882 | /* | 1030 | /* |
883 | * Rate is defined as an unsigned long in clk API, and so casting | 1031 | * Rate is defined as an unsigned long in clk API, and so casting |
884 | * explicitly to its type. Must be fixed once rate is 64 bit | 1032 | * explicitly to its type. Must be fixed once rate is 64 bit |
diff --git a/drivers/base/power/opp/opp.h b/drivers/base/power/opp/opp.h index b8880c7f8be1..70f4564a6ab9 100644 --- a/drivers/base/power/opp/opp.h +++ b/drivers/base/power/opp/opp.h | |||
@@ -129,6 +129,8 @@ struct device_list_opp { | |||
129 | * @clock_latency_ns_max: Max clock latency in nanoseconds. | 129 | * @clock_latency_ns_max: Max clock latency in nanoseconds. |
130 | * @shared_opp: OPP is shared between multiple devices. | 130 | * @shared_opp: OPP is shared between multiple devices. |
131 | * @suspend_opp: Pointer to OPP to be used during device suspend. | 131 | * @suspend_opp: Pointer to OPP to be used during device suspend. |
132 | * @supported_hw: Array of version number to support. | ||
133 | * @supported_hw_count: Number of elements in supported_hw array. | ||
132 | * @dentry: debugfs dentry pointer of the real device directory (not links). | 134 | * @dentry: debugfs dentry pointer of the real device directory (not links). |
133 | * @dentry_name: Name of the real dentry. | 135 | * @dentry_name: Name of the real dentry. |
134 | * | 136 | * |
@@ -153,6 +155,9 @@ struct device_opp { | |||
153 | bool shared_opp; | 155 | bool shared_opp; |
154 | struct dev_pm_opp *suspend_opp; | 156 | struct dev_pm_opp *suspend_opp; |
155 | 157 | ||
158 | unsigned int *supported_hw; | ||
159 | unsigned int supported_hw_count; | ||
160 | |||
156 | #ifdef CONFIG_DEBUG_FS | 161 | #ifdef CONFIG_DEBUG_FS |
157 | struct dentry *dentry; | 162 | struct dentry *dentry; |
158 | char dentry_name[NAME_MAX]; | 163 | char dentry_name[NAME_MAX]; |