diff options
| -rw-r--r-- | drivers/opp/of.c | 99 | ||||
| -rw-r--r-- | include/linux/pm_opp.h | 6 |
2 files changed, 105 insertions, 0 deletions
diff --git a/drivers/opp/of.c b/drivers/opp/of.c index 06f0f632ec47..cd58959e5158 100644 --- a/drivers/opp/of.c +++ b/drivers/opp/of.c | |||
| @@ -20,6 +20,7 @@ | |||
| 20 | #include <linux/pm_domain.h> | 20 | #include <linux/pm_domain.h> |
| 21 | #include <linux/slab.h> | 21 | #include <linux/slab.h> |
| 22 | #include <linux/export.h> | 22 | #include <linux/export.h> |
| 23 | #include <linux/energy_model.h> | ||
| 23 | 24 | ||
| 24 | #include "opp.h" | 25 | #include "opp.h" |
| 25 | 26 | ||
| @@ -1047,3 +1048,101 @@ struct device_node *dev_pm_opp_get_of_node(struct dev_pm_opp *opp) | |||
| 1047 | return of_node_get(opp->np); | 1048 | return of_node_get(opp->np); |
| 1048 | } | 1049 | } |
| 1049 | EXPORT_SYMBOL_GPL(dev_pm_opp_get_of_node); | 1050 | EXPORT_SYMBOL_GPL(dev_pm_opp_get_of_node); |
| 1051 | |||
| 1052 | /* | ||
| 1053 | * Callback function provided to the Energy Model framework upon registration. | ||
| 1054 | * This computes the power estimated by @CPU at @kHz if it is the frequency | ||
| 1055 | * of an existing OPP, or at the frequency of the first OPP above @kHz otherwise | ||
| 1056 | * (see dev_pm_opp_find_freq_ceil()). This function updates @kHz to the ceiled | ||
| 1057 | * frequency and @mW to the associated power. The power is estimated as | ||
| 1058 | * P = C * V^2 * f with C being the CPU's capacitance and V and f respectively | ||
| 1059 | * the voltage and frequency of the OPP. | ||
| 1060 | * | ||
| 1061 | * Returns -ENODEV if the CPU device cannot be found, -EINVAL if the power | ||
| 1062 | * calculation failed because of missing parameters, 0 otherwise. | ||
| 1063 | */ | ||
| 1064 | static int __maybe_unused _get_cpu_power(unsigned long *mW, unsigned long *kHz, | ||
| 1065 | int cpu) | ||
| 1066 | { | ||
| 1067 | struct device *cpu_dev; | ||
| 1068 | struct dev_pm_opp *opp; | ||
| 1069 | struct device_node *np; | ||
| 1070 | unsigned long mV, Hz; | ||
| 1071 | u32 cap; | ||
| 1072 | u64 tmp; | ||
| 1073 | int ret; | ||
| 1074 | |||
| 1075 | cpu_dev = get_cpu_device(cpu); | ||
| 1076 | if (!cpu_dev) | ||
| 1077 | return -ENODEV; | ||
| 1078 | |||
| 1079 | np = of_node_get(cpu_dev->of_node); | ||
| 1080 | if (!np) | ||
| 1081 | return -EINVAL; | ||
| 1082 | |||
| 1083 | ret = of_property_read_u32(np, "dynamic-power-coefficient", &cap); | ||
| 1084 | of_node_put(np); | ||
| 1085 | if (ret) | ||
| 1086 | return -EINVAL; | ||
| 1087 | |||
| 1088 | Hz = *kHz * 1000; | ||
| 1089 | opp = dev_pm_opp_find_freq_ceil(cpu_dev, &Hz); | ||
| 1090 | if (IS_ERR(opp)) | ||
| 1091 | return -EINVAL; | ||
| 1092 | |||
| 1093 | mV = dev_pm_opp_get_voltage(opp) / 1000; | ||
| 1094 | dev_pm_opp_put(opp); | ||
| 1095 | if (!mV) | ||
| 1096 | return -EINVAL; | ||
| 1097 | |||
| 1098 | tmp = (u64)cap * mV * mV * (Hz / 1000000); | ||
| 1099 | do_div(tmp, 1000000000); | ||
| 1100 | |||
| 1101 | *mW = (unsigned long)tmp; | ||
| 1102 | *kHz = Hz / 1000; | ||
| 1103 | |||
| 1104 | return 0; | ||
| 1105 | } | ||
| 1106 | |||
| 1107 | /** | ||
| 1108 | * dev_pm_opp_of_register_em() - Attempt to register an Energy Model | ||
| 1109 | * @cpus : CPUs for which an Energy Model has to be registered | ||
| 1110 | * | ||
| 1111 | * This checks whether the "dynamic-power-coefficient" devicetree property has | ||
| 1112 | * been specified, and tries to register an Energy Model with it if it has. | ||
| 1113 | */ | ||
| 1114 | void dev_pm_opp_of_register_em(struct cpumask *cpus) | ||
| 1115 | { | ||
| 1116 | struct em_data_callback em_cb = EM_DATA_CB(_get_cpu_power); | ||
| 1117 | int ret, nr_opp, cpu = cpumask_first(cpus); | ||
| 1118 | struct device *cpu_dev; | ||
| 1119 | struct device_node *np; | ||
| 1120 | u32 cap; | ||
| 1121 | |||
| 1122 | cpu_dev = get_cpu_device(cpu); | ||
| 1123 | if (!cpu_dev) | ||
| 1124 | return; | ||
| 1125 | |||
| 1126 | nr_opp = dev_pm_opp_get_opp_count(cpu_dev); | ||
| 1127 | if (nr_opp <= 0) | ||
| 1128 | return; | ||
| 1129 | |||
| 1130 | np = of_node_get(cpu_dev->of_node); | ||
| 1131 | if (!np) | ||
| 1132 | return; | ||
| 1133 | |||
| 1134 | /* | ||
| 1135 | * Register an EM only if the 'dynamic-power-coefficient' property is | ||
| 1136 | * set in devicetree. It is assumed the voltage values are known if that | ||
| 1137 | * property is set since it is useless otherwise. If voltages are not | ||
| 1138 | * known, just let the EM registration fail with an error to alert the | ||
| 1139 | * user about the inconsistent configuration. | ||
| 1140 | */ | ||
| 1141 | ret = of_property_read_u32(np, "dynamic-power-coefficient", &cap); | ||
| 1142 | of_node_put(np); | ||
| 1143 | if (ret || !cap) | ||
| 1144 | return; | ||
| 1145 | |||
| 1146 | em_register_perf_domain(cpus, nr_opp, &em_cb); | ||
| 1147 | } | ||
| 1148 | EXPORT_SYMBOL_GPL(dev_pm_opp_of_register_em); | ||
diff --git a/include/linux/pm_opp.h b/include/linux/pm_opp.h index 0a2a88e5a383..1470c57933cf 100644 --- a/include/linux/pm_opp.h +++ b/include/linux/pm_opp.h | |||
| @@ -322,6 +322,7 @@ int dev_pm_opp_of_get_sharing_cpus(struct device *cpu_dev, struct cpumask *cpuma | |||
| 322 | struct device_node *dev_pm_opp_of_get_opp_desc_node(struct device *dev); | 322 | struct device_node *dev_pm_opp_of_get_opp_desc_node(struct device *dev); |
| 323 | struct device_node *dev_pm_opp_get_of_node(struct dev_pm_opp *opp); | 323 | struct device_node *dev_pm_opp_get_of_node(struct dev_pm_opp *opp); |
| 324 | int of_get_required_opp_performance_state(struct device_node *np, int index); | 324 | int of_get_required_opp_performance_state(struct device_node *np, int index); |
| 325 | void dev_pm_opp_of_register_em(struct cpumask *cpus); | ||
| 325 | #else | 326 | #else |
| 326 | static inline int dev_pm_opp_of_add_table(struct device *dev) | 327 | static inline int dev_pm_opp_of_add_table(struct device *dev) |
| 327 | { | 328 | { |
| @@ -360,6 +361,11 @@ static inline struct device_node *dev_pm_opp_get_of_node(struct dev_pm_opp *opp) | |||
| 360 | { | 361 | { |
| 361 | return NULL; | 362 | return NULL; |
| 362 | } | 363 | } |
| 364 | |||
| 365 | static inline void dev_pm_opp_of_register_em(struct cpumask *cpus) | ||
| 366 | { | ||
| 367 | } | ||
| 368 | |||
| 363 | static inline int of_get_required_opp_performance_state(struct device_node *np, int index) | 369 | static inline int of_get_required_opp_performance_state(struct device_node *np, int index) |
| 364 | { | 370 | { |
| 365 | return -ENOTSUPP; | 371 | return -ENOTSUPP; |
