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