1 /*
2 * Arm SCP/MCP Software
3 * Copyright (c) 2022, Arm Limited and Contributors. All rights reserved.
4 *
5 * SPDX-License-Identifier: BSD-3-Clause
6 *
7 * Description:
8 * Power Allocation.
9 * This unit performs power allocation among actors based on power budget
10 * from thermal and the power requested.
11 */
12
13 #include "thermal_mgmt.h"
14
15 /*
16 * Helper functions.
17 */
18
is_power_request_satisfied(struct mod_thermal_mgmt_actor_ctx * actor_ctx)19 static inline bool is_power_request_satisfied(
20 struct mod_thermal_mgmt_actor_ctx *actor_ctx)
21 {
22 return (actor_ctx->granted_power >= actor_ctx->demand_power);
23 }
24
get_actor_power(struct mod_thermal_mgmt_dev_ctx * dev_ctx,struct mod_thermal_mgmt_actor_ctx * actor_ctx,uint32_t req_level)25 static void get_actor_power(
26 struct mod_thermal_mgmt_dev_ctx *dev_ctx,
27 struct mod_thermal_mgmt_actor_ctx *actor_ctx,
28 uint32_t req_level)
29 {
30 struct mod_thermal_mgmt_driver_api *driver;
31 fwk_id_t driver_id;
32
33 driver = dev_ctx->driver_api;
34 driver_id = actor_ctx->config->driver_id;
35
36 actor_ctx->demand_power = driver->level_to_power(driver_id, req_level);
37 }
38
get_actor_level(struct mod_thermal_mgmt_dev_ctx * dev_ctx,struct mod_thermal_mgmt_actor_ctx * actor_ctx,uint32_t * level)39 static void get_actor_level(
40 struct mod_thermal_mgmt_dev_ctx *dev_ctx,
41 struct mod_thermal_mgmt_actor_ctx *actor_ctx,
42 uint32_t *level)
43 {
44 struct mod_thermal_mgmt_driver_api *driver;
45 fwk_id_t driver_id;
46
47 driver = dev_ctx->driver_api;
48 driver_id = actor_ctx->config->driver_id;
49
50 *level = driver->power_to_level(driver_id, actor_ctx->granted_power);
51 }
52
53 /*
54 * Perform the first round of power distribution.
55 * An actor gets at most the power requested. Some actors may get less than
56 * their demand power.
57 */
allocate_power(struct mod_thermal_mgmt_dev_ctx * dev_ctx,struct mod_thermal_mgmt_actor_ctx * actor_ctx)58 static void allocate_power(
59 struct mod_thermal_mgmt_dev_ctx *dev_ctx,
60 struct mod_thermal_mgmt_actor_ctx *actor_ctx)
61 {
62 actor_ctx->granted_power =
63 ((actor_ctx->config->weight * actor_ctx->demand_power) *
64 dev_ctx->allocatable_power) /
65 dev_ctx->tot_weighted_demand_power;
66
67 if (actor_ctx->granted_power > actor_ctx->demand_power) {
68 actor_ctx->spare_power =
69 actor_ctx->granted_power - actor_ctx->demand_power;
70 actor_ctx->power_deficit = 0;
71
72 actor_ctx->granted_power = actor_ctx->demand_power;
73 } else {
74 actor_ctx->spare_power = 0;
75 actor_ctx->power_deficit =
76 actor_ctx->demand_power - actor_ctx->granted_power;
77 }
78
79 dev_ctx->tot_spare_power += actor_ctx->spare_power;
80 dev_ctx->tot_power_deficit += actor_ctx->power_deficit;
81 }
82
83 /*
84 * Perform the second round of power distribution.
85 * If an actor has received less than its demand power and there's a reserve of
86 * power not allocated, it may get a fraction of that.
87 */
re_allocate_power(struct mod_thermal_mgmt_dev_ctx * dev_ctx,struct mod_thermal_mgmt_actor_ctx * actor_ctx)88 static void re_allocate_power(
89 struct mod_thermal_mgmt_dev_ctx *dev_ctx,
90 struct mod_thermal_mgmt_actor_ctx *actor_ctx)
91 {
92 if (dev_ctx->tot_spare_power == 0) {
93 return;
94 }
95
96 if (actor_ctx->power_deficit > 0) {
97 /*
98 * The actor has been given less than requested, and it may still take
99 * some power
100 */
101 actor_ctx->granted_power +=
102 (actor_ctx->power_deficit * dev_ctx->tot_spare_power) /
103 dev_ctx->tot_power_deficit;
104
105 if (actor_ctx->granted_power > actor_ctx->demand_power) {
106 dev_ctx->carry_over_power +=
107 actor_ctx->granted_power - actor_ctx->demand_power;
108
109 actor_ctx->granted_power = actor_ctx->demand_power;
110 } else {
111 /*
112 * The actor has received the power it requested. The amount of
113 * power left can be used in the next fast-loop.
114 */
115 dev_ctx->carry_over_power += actor_ctx->spare_power;
116 }
117 }
118 }
119
get_dvfs_domain_idx(struct mod_thermal_mgmt_actor_ctx * actor_ctx)120 static unsigned int get_dvfs_domain_idx(
121 struct mod_thermal_mgmt_actor_ctx *actor_ctx)
122 {
123 return fwk_id_get_element_idx(actor_ctx->config->dvfs_domain_id);
124 }
125
distribute_power(fwk_id_t id,uint32_t * perf_request,uint32_t * perf_limit)126 void distribute_power(fwk_id_t id, uint32_t *perf_request, uint32_t *perf_limit)
127 {
128 struct mod_thermal_mgmt_dev_ctx *dev_ctx;
129 struct mod_thermal_mgmt_actor_ctx *actor_ctx;
130 int status;
131 unsigned int actor_idx, dom;
132 uint32_t new_perf_limit, dev_perf_request;
133 uint32_t actors_count;
134 uint32_t idle_power, prev_used_power;
135 uint16_t activity;
136
137 dev_ctx = get_dev_ctx(id);
138
139 actors_count = dev_ctx->config->thermal_actors_count;
140 idle_power = 0;
141 dev_ctx->tot_weighted_demand_power = 0;
142 dev_ctx->tot_spare_power = 0;
143 dev_ctx->tot_power_deficit = 0;
144
145 /*
146 * STEP 0:
147 * Initialise the actors' demand power.
148 */
149 for (actor_idx = 0; actor_idx < actors_count; actor_idx++) {
150 actor_ctx = get_actor_ctx(dev_ctx, actor_idx);
151 dom = get_dvfs_domain_idx(actor_ctx);
152
153 /* Here we take into account limits already placed by other plugins */
154 if (perf_request[dom] > perf_limit[dom]) {
155 dev_perf_request = perf_limit[dom];
156 } else {
157 dev_perf_request = perf_request[dom];
158 }
159
160 get_actor_power(dev_ctx, actor_ctx, dev_perf_request);
161 if (actor_ctx->activity_api != NULL) {
162 status = actor_ctx->activity_api->get_activity_factor(
163 actor_ctx->config->activity_factor->driver_id, &activity);
164 if (status != FWK_SUCCESS) {
165 FWK_LOG_INFO(
166 "[THERMAL] Failed to get activity factor (%u,%u)",
167 fwk_id_get_element_idx(id),
168 actor_idx);
169 continue;
170 }
171
172 /* Calculate used power and accumulate idle power */
173 prev_used_power = (actor_ctx->granted_power * activity) / 1024;
174 idle_power += actor_ctx->granted_power - prev_used_power;
175 }
176
177 dev_ctx->tot_weighted_demand_power +=
178 actor_ctx->config->weight * actor_ctx->demand_power;
179 }
180
181 dev_ctx->allocatable_power =
182 dev_ctx->thermal_allocatable_power + idle_power;
183
184 /*
185 * STEP 1:
186 * The power available is allocated in proportion to the actors' weight and
187 * their power demand.
188 */
189 for (actor_idx = 0; actor_idx < actors_count; actor_idx++) {
190 actor_ctx = get_actor_ctx(dev_ctx, actor_idx);
191
192 allocate_power(dev_ctx, actor_ctx);
193 }
194
195 /*
196 * STEP 2:
197 * A further allocation based on actors' power deficit, if any spare power
198 * left.
199 * Finally, get the corresponding performance level and place a new limit.
200 */
201 dev_ctx->tot_spare_power += dev_ctx->carry_over_power;
202 dev_ctx->carry_over_power = 0;
203
204 for (actor_idx = 0; actor_idx < actors_count; actor_idx++) {
205 actor_ctx = get_actor_ctx(dev_ctx, actor_idx);
206 dom = get_dvfs_domain_idx(actor_ctx);
207
208 re_allocate_power(dev_ctx, actor_ctx);
209
210 get_actor_level(dev_ctx, actor_ctx, &new_perf_limit);
211
212 /*
213 * If we have been granted the power we requested (which is at most the
214 * limit already placed by other plugins), then there's no need to limit
215 * further.
216 */
217 if (!is_power_request_satisfied(actor_ctx) &&
218 (new_perf_limit < perf_limit[dom])) {
219 perf_limit[dom] = new_perf_limit;
220 }
221 }
222 }
223