xref: /OK3568_Linux_fs/kernel/drivers/gpu/arm/mali400/mali/common/mali_pm.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1 /*
2  * Copyright (C) 2011-2017 ARM Limited. All rights reserved.
3  *
4  * This program is free software and is provided to you under the terms of the GNU General Public License version 2
5  * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence.
6  *
7  * A copy of the licence is included with the program, and can also be obtained from Free Software
8  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
9  */
10 
11 #include "mali_pm.h"
12 #include "mali_kernel_common.h"
13 #include "mali_osk.h"
14 #include "mali_osk_mali.h"
15 #include "mali_scheduler.h"
16 #include "mali_group.h"
17 #include "mali_pm_domain.h"
18 #include "mali_pmu.h"
19 
20 #include "mali_executor.h"
21 #include "mali_control_timer.h"
22 
23 #if defined(DEBUG)
24 u32 num_pm_runtime_resume = 0;
25 u32 num_pm_updates = 0;
26 u32 num_pm_updates_up = 0;
27 u32 num_pm_updates_down = 0;
28 #endif
29 
30 #define MALI_PM_DOMAIN_DUMMY_MASK (1 << MALI_DOMAIN_INDEX_DUMMY)
31 
32 /* lock protecting power state (including pm_domains) */
33 static _mali_osk_spinlock_irq_t *pm_lock_state = NULL;
34 
35 /* the wanted domain mask (protected by pm_lock_state) */
36 static u32 pd_mask_wanted = 0;
37 
38 /* used to deferring the actual power changes */
39 static _mali_osk_wq_work_t *pm_work = NULL;
40 
41 /* lock protecting power change execution */
42 static _mali_osk_mutex_t *pm_lock_exec = NULL;
43 
44 /* PMU domains which are actually powered on (protected by pm_lock_exec) */
45 static u32 pmu_mask_current = 0;
46 
47 /*
48  * domains which marked as powered on (protected by pm_lock_exec)
49  * This can be different from pmu_mask_current right after GPU power on
50  * if the PMU domains default to powered up.
51  */
52 static u32 pd_mask_current = 0;
53 
54 static u16 domain_config[MALI_MAX_NUMBER_OF_DOMAINS] = {
55 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
56 	1 << MALI_DOMAIN_INDEX_DUMMY
57 };
58 
59 /* The relative core power cost */
60 #define MALI_GP_COST 3
61 #define MALI_PP_COST 6
62 #define MALI_L2_COST 1
63 
64 /*
65  *We have MALI_MAX_NUMBER_OF_PP_PHYSICAL_CORES + 1 rows in this matrix
66  *because we mush store the mask of different pp cores: 0, 1, 2, 3, 4, 5, 6, 7, 8.
67  */
68 static int mali_pm_domain_power_cost_result[MALI_MAX_NUMBER_OF_PHYSICAL_PP_GROUPS + 1][MALI_MAX_NUMBER_OF_DOMAINS];
69 /*
70  * Keep track of runtime PM state, so that we know
71  * how to resume during OS resume.
72  */
73 #ifdef CONFIG_PM_RUNTIME
74 static mali_bool mali_pm_runtime_active = MALI_FALSE;
75 #else
76 /* when kernel don't enable PM_RUNTIME, set the flag always true,
77  * for GPU will not power off by runtime */
78 static mali_bool mali_pm_runtime_active = MALI_TRUE;
79 #endif
80 
81 static void mali_pm_state_lock(void);
82 static void mali_pm_state_unlock(void);
83 static _mali_osk_errcode_t mali_pm_create_pm_domains(void);
84 static void mali_pm_set_pmu_domain_config(void);
85 static u32 mali_pm_get_registered_cores_mask(void);
86 static void mali_pm_update_sync_internal(void);
87 static mali_bool mali_pm_common_suspend(void);
88 static void mali_pm_update_work(void *data);
89 #if defined(DEBUG)
90 const char *mali_pm_mask_to_string(u32 mask);
91 const char *mali_pm_group_stats_to_string(void);
92 #endif
93 
mali_pm_initialize(void)94 _mali_osk_errcode_t mali_pm_initialize(void)
95 {
96 	_mali_osk_errcode_t err;
97 	struct mali_pmu_core *pmu;
98 
99 	pm_lock_state = _mali_osk_spinlock_irq_init(_MALI_OSK_LOCKFLAG_ORDERED,
100 			_MALI_OSK_LOCK_ORDER_PM_STATE);
101 	if (NULL == pm_lock_state) {
102 		mali_pm_terminate();
103 		return _MALI_OSK_ERR_FAULT;
104 	}
105 
106 	pm_lock_exec = _mali_osk_mutex_init(_MALI_OSK_LOCKFLAG_ORDERED,
107 					    _MALI_OSK_LOCK_ORDER_PM_STATE);
108 	if (NULL == pm_lock_exec) {
109 		mali_pm_terminate();
110 		return _MALI_OSK_ERR_FAULT;
111 	}
112 
113 	pm_work = _mali_osk_wq_create_work(mali_pm_update_work, NULL);
114 	if (NULL == pm_work) {
115 		mali_pm_terminate();
116 		return _MALI_OSK_ERR_FAULT;
117 	}
118 
119 	pmu = mali_pmu_get_global_pmu_core();
120 	if (NULL != pmu) {
121 		/*
122 		 * We have a Mali PMU, set the correct domain
123 		 * configuration (default or custom)
124 		 */
125 
126 		u32 registered_cores_mask;
127 
128 		mali_pm_set_pmu_domain_config();
129 
130 		registered_cores_mask = mali_pm_get_registered_cores_mask();
131 		mali_pmu_set_registered_cores_mask(pmu, registered_cores_mask);
132 
133 		MALI_DEBUG_ASSERT(0 == pd_mask_wanted);
134 	}
135 
136 	/* Create all power domains needed (at least one dummy domain) */
137 	err = mali_pm_create_pm_domains();
138 	if (_MALI_OSK_ERR_OK != err) {
139 		mali_pm_terminate();
140 		return err;
141 	}
142 
143 	return _MALI_OSK_ERR_OK;
144 }
145 
mali_pm_terminate(void)146 void mali_pm_terminate(void)
147 {
148 	if (NULL != pm_work) {
149 		_mali_osk_wq_delete_work(pm_work);
150 		pm_work = NULL;
151 	}
152 
153 	mali_pm_domain_terminate();
154 
155 	if (NULL != pm_lock_exec) {
156 		_mali_osk_mutex_term(pm_lock_exec);
157 		pm_lock_exec = NULL;
158 	}
159 
160 	if (NULL != pm_lock_state) {
161 		_mali_osk_spinlock_irq_term(pm_lock_state);
162 		pm_lock_state = NULL;
163 	}
164 }
165 
mali_pm_register_l2_cache(u32 domain_index,struct mali_l2_cache_core * l2_cache)166 struct mali_pm_domain *mali_pm_register_l2_cache(u32 domain_index,
167 		struct mali_l2_cache_core *l2_cache)
168 {
169 	struct mali_pm_domain *domain;
170 
171 	domain = mali_pm_domain_get_from_mask(domain_config[domain_index]);
172 	if (NULL == domain) {
173 		MALI_DEBUG_ASSERT(0 == domain_config[domain_index]);
174 		domain = mali_pm_domain_get_from_index(
175 				 MALI_DOMAIN_INDEX_DUMMY);
176 		domain_config[domain_index] = MALI_PM_DOMAIN_DUMMY_MASK;
177 	} else {
178 		MALI_DEBUG_ASSERT(0 != domain_config[domain_index]);
179 	}
180 
181 	MALI_DEBUG_ASSERT(NULL != domain);
182 
183 	mali_pm_domain_add_l2_cache(domain, l2_cache);
184 
185 	return domain; /* return the actual domain this was registered in */
186 }
187 
mali_pm_register_group(u32 domain_index,struct mali_group * group)188 struct mali_pm_domain *mali_pm_register_group(u32 domain_index,
189 		struct mali_group *group)
190 {
191 	struct mali_pm_domain *domain;
192 
193 	domain = mali_pm_domain_get_from_mask(domain_config[domain_index]);
194 	if (NULL == domain) {
195 		MALI_DEBUG_ASSERT(0 == domain_config[domain_index]);
196 		domain = mali_pm_domain_get_from_index(
197 				 MALI_DOMAIN_INDEX_DUMMY);
198 		domain_config[domain_index] = MALI_PM_DOMAIN_DUMMY_MASK;
199 	} else {
200 		MALI_DEBUG_ASSERT(0 != domain_config[domain_index]);
201 	}
202 
203 	MALI_DEBUG_ASSERT(NULL != domain);
204 
205 	mali_pm_domain_add_group(domain, group);
206 
207 	return domain; /* return the actual domain this was registered in */
208 }
209 
mali_pm_get_domain_refs(struct mali_pm_domain ** domains,struct mali_group ** groups,u32 num_domains)210 mali_bool mali_pm_get_domain_refs(struct mali_pm_domain **domains,
211 				  struct mali_group **groups,
212 				  u32 num_domains)
213 {
214 	mali_bool ret = MALI_TRUE; /* Assume all is powered on instantly */
215 	u32 i;
216 
217 	mali_pm_state_lock();
218 
219 	for (i = 0; i < num_domains; i++) {
220 		MALI_DEBUG_ASSERT_POINTER(domains[i]);
221 		pd_mask_wanted |= mali_pm_domain_ref_get(domains[i]);
222 		if (MALI_FALSE == mali_pm_domain_power_is_on(domains[i])) {
223 			/*
224 			 * Tell caller that the corresponding group
225 			 * was not already powered on.
226 			 */
227 			ret = MALI_FALSE;
228 		} else {
229 			/*
230 			 * There is a time gap between we power on the domain and
231 			 * set the power state of the corresponding groups to be on.
232 			 */
233 			if (NULL != groups[i] &&
234 			    MALI_FALSE == mali_group_power_is_on(groups[i])) {
235 				ret = MALI_FALSE;
236 			}
237 		}
238 	}
239 
240 	MALI_DEBUG_PRINT(3, ("PM: wanted domain mask = 0x%08X (get refs)\n", pd_mask_wanted));
241 
242 	mali_pm_state_unlock();
243 
244 	return ret;
245 }
246 
mali_pm_put_domain_refs(struct mali_pm_domain ** domains,u32 num_domains)247 mali_bool mali_pm_put_domain_refs(struct mali_pm_domain **domains,
248 				  u32 num_domains)
249 {
250 	u32 mask = 0;
251 	mali_bool ret;
252 	u32 i;
253 
254 	mali_pm_state_lock();
255 
256 	for (i = 0; i < num_domains; i++) {
257 		MALI_DEBUG_ASSERT_POINTER(domains[i]);
258 		mask |= mali_pm_domain_ref_put(domains[i]);
259 	}
260 
261 	if (0 == mask) {
262 		/* return false, all domains should still stay on */
263 		ret = MALI_FALSE;
264 	} else {
265 		/* Assert that we are dealing with a change */
266 		MALI_DEBUG_ASSERT((pd_mask_wanted & mask) == mask);
267 
268 		/* Update our desired domain mask */
269 		pd_mask_wanted &= ~mask;
270 
271 		/* return true; one or more domains can now be powered down */
272 		ret = MALI_TRUE;
273 	}
274 
275 	MALI_DEBUG_PRINT(3, ("PM: wanted domain mask = 0x%08X (put refs)\n", pd_mask_wanted));
276 
277 	mali_pm_state_unlock();
278 
279 	return ret;
280 }
281 
mali_pm_init_begin(void)282 void mali_pm_init_begin(void)
283 {
284 	struct mali_pmu_core *pmu = mali_pmu_get_global_pmu_core();
285 
286 	_mali_osk_pm_dev_ref_get_sync();
287 
288 	/* Ensure all PMU domains are on */
289 	if (NULL != pmu) {
290 		mali_pmu_power_up_all(pmu);
291 	}
292 }
293 
mali_pm_init_end(void)294 void mali_pm_init_end(void)
295 {
296 	struct mali_pmu_core *pmu = mali_pmu_get_global_pmu_core();
297 
298 	/* Ensure all PMU domains are off */
299 	if (NULL != pmu) {
300 		mali_pmu_power_down_all(pmu);
301 	}
302 
303 	_mali_osk_pm_dev_ref_put();
304 }
305 
mali_pm_update_sync(void)306 void mali_pm_update_sync(void)
307 {
308 	mali_pm_exec_lock();
309 
310 	if (MALI_TRUE == mali_pm_runtime_active) {
311 		/*
312 		 * Only update if GPU is powered on.
313 		 * Deactivation of the last group will result in both a
314 		 * deferred runtime PM suspend operation and
315 		 * deferred execution of this function.
316 		 * mali_pm_runtime_active will be false if runtime PM
317 		 * executed first and thus the GPU is now fully powered off.
318 		 */
319 		mali_pm_update_sync_internal();
320 	}
321 
322 	mali_pm_exec_unlock();
323 }
324 
mali_pm_update_async(void)325 void mali_pm_update_async(void)
326 {
327 	_mali_osk_wq_schedule_work(pm_work);
328 }
329 
mali_pm_os_suspend(mali_bool os_suspend)330 void mali_pm_os_suspend(mali_bool os_suspend)
331 {
332 	int ret;
333 
334 	MALI_DEBUG_PRINT(3, ("Mali PM: OS suspend\n"));
335 
336 	/* Suspend execution of all jobs, and go to inactive state */
337 	mali_executor_suspend();
338 
339 	if (os_suspend) {
340 		mali_control_timer_suspend(MALI_TRUE);
341 	}
342 
343 	mali_pm_exec_lock();
344 
345 	ret = mali_pm_common_suspend();
346 
347 	MALI_DEBUG_ASSERT(MALI_TRUE == ret);
348 	MALI_IGNORE(ret);
349 
350 	mali_pm_exec_unlock();
351 }
352 
mali_pm_os_resume(void)353 void mali_pm_os_resume(void)
354 {
355 	struct mali_pmu_core *pmu = mali_pmu_get_global_pmu_core();
356 
357 	MALI_DEBUG_PRINT(3, ("Mali PM: OS resume\n"));
358 
359 	mali_pm_exec_lock();
360 
361 #if defined(DEBUG)
362 	mali_pm_state_lock();
363 
364 	/* Assert that things are as we left them in os_suspend(). */
365 	MALI_DEBUG_ASSERT(0 == pd_mask_wanted);
366 	MALI_DEBUG_ASSERT(0 == pd_mask_current);
367 	MALI_DEBUG_ASSERT(0 == pmu_mask_current);
368 
369 	MALI_DEBUG_ASSERT(MALI_TRUE == mali_pm_domain_all_unused());
370 
371 	mali_pm_state_unlock();
372 #endif
373 
374 	if (MALI_TRUE == mali_pm_runtime_active) {
375 		/* Runtime PM was active, so reset PMU */
376 		if (NULL != pmu) {
377 			mali_pmu_reset(pmu);
378 			pmu_mask_current = mali_pmu_get_mask(pmu);
379 
380 			MALI_DEBUG_PRINT(3, ("Mali PM: OS resume 0x%x \n", pmu_mask_current));
381 		}
382 
383 		mali_pm_update_sync_internal();
384 	}
385 
386 	mali_pm_exec_unlock();
387 
388 	/* Start executing jobs again */
389 	mali_executor_resume();
390 }
391 
mali_pm_runtime_suspend(void)392 mali_bool mali_pm_runtime_suspend(void)
393 {
394 	mali_bool ret;
395 
396 	MALI_DEBUG_PRINT(3, ("Mali PM: Runtime suspend\n"));
397 
398 	mali_pm_exec_lock();
399 
400 	/*
401 	 * Put SW state directly into "off" state, and do not bother to power
402 	 * down each power domain, because entire GPU will be powered off
403 	 * when we return.
404 	 * For runtime PM suspend, in contrast to OS suspend, there is a race
405 	 * between this function and the mali_pm_update_sync_internal(), which
406 	 * is fine...
407 	 */
408 	ret = mali_pm_common_suspend();
409 	if (MALI_TRUE == ret) {
410 		mali_pm_runtime_active = MALI_FALSE;
411 	} else {
412 		/*
413 		 * Process the "power up" instead,
414 		 * which could have been "lost"
415 		 */
416 		mali_pm_update_sync_internal();
417 	}
418 
419 	mali_pm_exec_unlock();
420 
421 	return ret;
422 }
423 
mali_pm_runtime_resume(void)424 void mali_pm_runtime_resume(void)
425 {
426 	struct mali_pmu_core *pmu = mali_pmu_get_global_pmu_core();
427 
428 	mali_pm_exec_lock();
429 
430 	mali_pm_runtime_active = MALI_TRUE;
431 
432 #if defined(DEBUG)
433 	++num_pm_runtime_resume;
434 
435 	mali_pm_state_lock();
436 
437 	/*
438 	 * Assert that things are as we left them in runtime_suspend(),
439 	 * except for pd_mask_wanted which normally will be the reason we
440 	 * got here (job queued => domains wanted)
441 	 */
442 	MALI_DEBUG_ASSERT(0 == pd_mask_current);
443 	MALI_DEBUG_ASSERT(0 == pmu_mask_current);
444 
445 	mali_pm_state_unlock();
446 #endif
447 
448 	if (NULL != pmu) {
449 		mali_pmu_reset(pmu);
450 		pmu_mask_current = mali_pmu_get_mask(pmu);
451 		MALI_DEBUG_PRINT(3, ("Mali PM: Runtime resume 0x%x \n", pmu_mask_current));
452 	}
453 
454 	/*
455 	 * Normally we are resumed because a job has just been queued.
456 	 * pd_mask_wanted should thus be != 0.
457 	 * It is however possible for others to take a Mali Runtime PM ref
458 	 * without having a job queued.
459 	 * We should however always call mali_pm_update_sync_internal(),
460 	 * because this will take care of any potential mismatch between
461 	 * pmu_mask_current and pd_mask_current.
462 	 */
463 	mali_pm_update_sync_internal();
464 
465 	mali_pm_exec_unlock();
466 }
467 
468 #if MALI_STATE_TRACKING
mali_pm_dump_state_domain(struct mali_pm_domain * domain,char * buf,u32 size)469 u32 mali_pm_dump_state_domain(struct mali_pm_domain *domain,
470 			      char *buf, u32 size)
471 {
472 	int n = 0;
473 
474 	n += _mali_osk_snprintf(buf + n, size - n,
475 				"\tPower domain: id %u\n",
476 				mali_pm_domain_get_id(domain));
477 
478 	n += _mali_osk_snprintf(buf + n, size - n,
479 				"\t\tMask: 0x%04x\n",
480 				mali_pm_domain_get_mask(domain));
481 
482 	n += _mali_osk_snprintf(buf + n, size - n,
483 				"\t\tUse count: %u\n",
484 				mali_pm_domain_get_use_count(domain));
485 
486 	n += _mali_osk_snprintf(buf + n, size - n,
487 				"\t\tCurrent power state: %s\n",
488 				(mali_pm_domain_get_mask(domain) & pd_mask_current) ?
489 				"On" : "Off");
490 
491 	n += _mali_osk_snprintf(buf + n, size - n,
492 				"\t\tWanted power state: %s\n",
493 				(mali_pm_domain_get_mask(domain) & pd_mask_wanted) ?
494 				"On" : "Off");
495 
496 	return n;
497 }
498 #endif
499 
mali_pm_state_lock(void)500 static void mali_pm_state_lock(void)
501 {
502 	_mali_osk_spinlock_irq_lock(pm_lock_state);
503 }
504 
mali_pm_state_unlock(void)505 static void mali_pm_state_unlock(void)
506 {
507 	_mali_osk_spinlock_irq_unlock(pm_lock_state);
508 }
509 
mali_pm_exec_lock(void)510 void mali_pm_exec_lock(void)
511 {
512 	_mali_osk_mutex_wait(pm_lock_exec);
513 }
514 
mali_pm_exec_unlock(void)515 void mali_pm_exec_unlock(void)
516 {
517 	_mali_osk_mutex_signal(pm_lock_exec);
518 }
519 
mali_pm_domain_power_up(u32 power_up_mask,struct mali_group * groups_up[MALI_MAX_NUMBER_OF_GROUPS],u32 * num_groups_up,struct mali_l2_cache_core * l2_up[MALI_MAX_NUMBER_OF_L2_CACHE_CORES],u32 * num_l2_up)520 static void mali_pm_domain_power_up(u32 power_up_mask,
521 				    struct mali_group *groups_up[MALI_MAX_NUMBER_OF_GROUPS],
522 				    u32 *num_groups_up,
523 				    struct mali_l2_cache_core *l2_up[MALI_MAX_NUMBER_OF_L2_CACHE_CORES],
524 				    u32 *num_l2_up)
525 {
526 	u32 domain_bit;
527 	u32 notify_mask = power_up_mask;
528 
529 	MALI_DEBUG_ASSERT(0 != power_up_mask);
530 	MALI_DEBUG_ASSERT_POINTER(groups_up);
531 	MALI_DEBUG_ASSERT_POINTER(num_groups_up);
532 	MALI_DEBUG_ASSERT(0 == *num_groups_up);
533 	MALI_DEBUG_ASSERT_POINTER(l2_up);
534 	MALI_DEBUG_ASSERT_POINTER(num_l2_up);
535 	MALI_DEBUG_ASSERT(0 == *num_l2_up);
536 
537 	MALI_DEBUG_ASSERT_LOCK_HELD(pm_lock_exec);
538 	MALI_DEBUG_ASSERT_LOCK_HELD(pm_lock_state);
539 
540 	MALI_DEBUG_PRINT(5,
541 			 ("PM update:      Powering up domains: . [%s]\n",
542 			  mali_pm_mask_to_string(power_up_mask)));
543 
544 	pd_mask_current |= power_up_mask;
545 
546 	domain_bit = _mali_osk_fls(notify_mask);
547 	while (0 != domain_bit) {
548 		u32 domain_id = domain_bit - 1;
549 		struct mali_pm_domain *domain =
550 			mali_pm_domain_get_from_index(
551 				domain_id);
552 		struct mali_l2_cache_core *l2_cache;
553 		struct mali_l2_cache_core *l2_cache_tmp;
554 		struct mali_group *group;
555 		struct mali_group *group_tmp;
556 
557 		/* Mark domain as powered up */
558 		mali_pm_domain_set_power_on(domain, MALI_TRUE);
559 
560 		/*
561 		 * Make a note of the L2 and/or group(s) to notify
562 		 * (need to release the PM state lock before doing so)
563 		 */
564 
565 		_MALI_OSK_LIST_FOREACHENTRY(l2_cache,
566 					    l2_cache_tmp,
567 					    mali_pm_domain_get_l2_cache_list(
568 						    domain),
569 					    struct mali_l2_cache_core,
570 					    pm_domain_list) {
571 			MALI_DEBUG_ASSERT(*num_l2_up <
572 					  MALI_MAX_NUMBER_OF_L2_CACHE_CORES);
573 			l2_up[*num_l2_up] = l2_cache;
574 			(*num_l2_up)++;
575 		}
576 
577 		_MALI_OSK_LIST_FOREACHENTRY(group,
578 					    group_tmp,
579 					    mali_pm_domain_get_group_list(domain),
580 					    struct mali_group,
581 					    pm_domain_list) {
582 			MALI_DEBUG_ASSERT(*num_groups_up <
583 					  MALI_MAX_NUMBER_OF_GROUPS);
584 			groups_up[*num_groups_up] = group;
585 
586 			(*num_groups_up)++;
587 		}
588 
589 		/* Remove current bit and find next */
590 		notify_mask &= ~(1 << (domain_id));
591 		domain_bit = _mali_osk_fls(notify_mask);
592 	}
593 }
mali_pm_domain_power_down(u32 power_down_mask,struct mali_group * groups_down[MALI_MAX_NUMBER_OF_GROUPS],u32 * num_groups_down,struct mali_l2_cache_core * l2_down[MALI_MAX_NUMBER_OF_L2_CACHE_CORES],u32 * num_l2_down)594 static void mali_pm_domain_power_down(u32 power_down_mask,
595 				      struct mali_group *groups_down[MALI_MAX_NUMBER_OF_GROUPS],
596 				      u32 *num_groups_down,
597 				      struct mali_l2_cache_core *l2_down[MALI_MAX_NUMBER_OF_L2_CACHE_CORES],
598 				      u32 *num_l2_down)
599 {
600 	u32 domain_bit;
601 	u32 notify_mask = power_down_mask;
602 
603 	MALI_DEBUG_ASSERT(0 != power_down_mask);
604 	MALI_DEBUG_ASSERT_POINTER(groups_down);
605 	MALI_DEBUG_ASSERT_POINTER(num_groups_down);
606 	MALI_DEBUG_ASSERT(0 == *num_groups_down);
607 	MALI_DEBUG_ASSERT_POINTER(l2_down);
608 	MALI_DEBUG_ASSERT_POINTER(num_l2_down);
609 	MALI_DEBUG_ASSERT(0 == *num_l2_down);
610 
611 	MALI_DEBUG_ASSERT_LOCK_HELD(pm_lock_exec);
612 	MALI_DEBUG_ASSERT_LOCK_HELD(pm_lock_state);
613 
614 	MALI_DEBUG_PRINT(5,
615 			 ("PM update:      Powering down domains: [%s]\n",
616 			  mali_pm_mask_to_string(power_down_mask)));
617 
618 	pd_mask_current &= ~power_down_mask;
619 
620 	domain_bit = _mali_osk_fls(notify_mask);
621 	while (0 != domain_bit) {
622 		u32 domain_id = domain_bit - 1;
623 		struct mali_pm_domain *domain =
624 			mali_pm_domain_get_from_index(domain_id);
625 		struct mali_l2_cache_core *l2_cache;
626 		struct mali_l2_cache_core *l2_cache_tmp;
627 		struct mali_group *group;
628 		struct mali_group *group_tmp;
629 
630 		/* Mark domain as powered down */
631 		mali_pm_domain_set_power_on(domain, MALI_FALSE);
632 
633 		/*
634 		 * Make a note of the L2s and/or groups to notify
635 		 * (need to release the PM state lock before doing so)
636 		 */
637 
638 		_MALI_OSK_LIST_FOREACHENTRY(l2_cache,
639 					    l2_cache_tmp,
640 					    mali_pm_domain_get_l2_cache_list(domain),
641 					    struct mali_l2_cache_core,
642 					    pm_domain_list) {
643 			MALI_DEBUG_ASSERT(*num_l2_down <
644 					  MALI_MAX_NUMBER_OF_L2_CACHE_CORES);
645 			l2_down[*num_l2_down] = l2_cache;
646 			(*num_l2_down)++;
647 		}
648 
649 		_MALI_OSK_LIST_FOREACHENTRY(group,
650 					    group_tmp,
651 					    mali_pm_domain_get_group_list(domain),
652 					    struct mali_group,
653 					    pm_domain_list) {
654 			MALI_DEBUG_ASSERT(*num_groups_down <
655 					  MALI_MAX_NUMBER_OF_GROUPS);
656 			groups_down[*num_groups_down] = group;
657 			(*num_groups_down)++;
658 		}
659 
660 		/* Remove current bit and find next */
661 		notify_mask &= ~(1 << (domain_id));
662 		domain_bit = _mali_osk_fls(notify_mask);
663 	}
664 }
665 
666 /*
667  * Execute pending power domain changes
668  * pm_lock_exec lock must be taken by caller.
669  */
mali_pm_update_sync_internal(void)670 static void mali_pm_update_sync_internal(void)
671 {
672 	/*
673 	 * This should only be called in non-atomic context
674 	 * (normally as deferred work)
675 	 *
676 	 * Look at the pending power domain changes, and execute these.
677 	 * Make sure group and schedulers are notified about changes.
678 	 */
679 
680 	struct mali_pmu_core *pmu = mali_pmu_get_global_pmu_core();
681 
682 	u32 power_down_mask;
683 	u32 power_up_mask;
684 
685 	MALI_DEBUG_ASSERT_LOCK_HELD(pm_lock_exec);
686 
687 #if defined(DEBUG)
688 	++num_pm_updates;
689 #endif
690 
691 	/* Hold PM state lock while we look at (and obey) the wanted state */
692 	mali_pm_state_lock();
693 
694 	MALI_DEBUG_PRINT(5, ("PM update pre:  Wanted domain mask: .. [%s]\n",
695 			     mali_pm_mask_to_string(pd_mask_wanted)));
696 	MALI_DEBUG_PRINT(5, ("PM update pre:  Current domain mask: . [%s]\n",
697 			     mali_pm_mask_to_string(pd_mask_current)));
698 	MALI_DEBUG_PRINT(5, ("PM update pre:  Current PMU mask: .... [%s]\n",
699 			     mali_pm_mask_to_string(pmu_mask_current)));
700 	MALI_DEBUG_PRINT(5, ("PM update pre:  Group power stats: ... <%s>\n",
701 			     mali_pm_group_stats_to_string()));
702 
703 	/* Figure out which cores we need to power on */
704 	power_up_mask = pd_mask_wanted &
705 			(pd_mask_wanted ^ pd_mask_current);
706 
707 	if (0 != power_up_mask) {
708 		u32 power_up_mask_pmu;
709 		struct mali_group *groups_up[MALI_MAX_NUMBER_OF_GROUPS];
710 		u32 num_groups_up = 0;
711 		struct mali_l2_cache_core *
712 			l2_up[MALI_MAX_NUMBER_OF_L2_CACHE_CORES];
713 		u32 num_l2_up = 0;
714 		u32 i;
715 
716 #if defined(DEBUG)
717 		++num_pm_updates_up;
718 #endif
719 
720 		/*
721 		 * Make sure dummy/global domain is always included when
722 		 * powering up, since this is controlled by runtime PM,
723 		 * and device power is on at this stage.
724 		 */
725 		power_up_mask |= MALI_PM_DOMAIN_DUMMY_MASK;
726 
727 		/* Power up only real PMU domains */
728 		power_up_mask_pmu = power_up_mask & ~MALI_PM_DOMAIN_DUMMY_MASK;
729 
730 		/* But not those that happen to be powered on already */
731 		power_up_mask_pmu &= (power_up_mask ^ pmu_mask_current) &
732 				     power_up_mask;
733 
734 		if (0 != power_up_mask_pmu) {
735 			MALI_DEBUG_ASSERT(NULL != pmu);
736 			pmu_mask_current |= power_up_mask_pmu;
737 			mali_pmu_power_up(pmu, power_up_mask_pmu);
738 		}
739 
740 		/*
741 		 * Put the domains themselves in power up state.
742 		 * We get the groups and L2s to notify in return.
743 		 */
744 		mali_pm_domain_power_up(power_up_mask,
745 					groups_up, &num_groups_up,
746 					l2_up, &num_l2_up);
747 
748 		/* Need to unlock PM state lock before notifying L2 + groups */
749 		mali_pm_state_unlock();
750 
751 		/* Notify each L2 cache that we have be powered up */
752 		for (i = 0; i < num_l2_up; i++) {
753 			mali_l2_cache_power_up(l2_up[i]);
754 		}
755 
756 		/*
757 		 * Tell execution module about all the groups we have
758 		 * powered up. Groups will be notified as a result of this.
759 		 */
760 		mali_executor_group_power_up(groups_up, num_groups_up);
761 
762 		/* Lock state again before checking for power down */
763 		mali_pm_state_lock();
764 	}
765 
766 	/* Figure out which cores we need to power off */
767 	power_down_mask = pd_mask_current &
768 			  (pd_mask_wanted ^ pd_mask_current);
769 
770 	/*
771 	 * Never power down the dummy/global domain here. This is to be done
772 	 * from a suspend request (since this domain is only physicall powered
773 	 * down at that point)
774 	 */
775 	power_down_mask &= ~MALI_PM_DOMAIN_DUMMY_MASK;
776 
777 	if (0 != power_down_mask) {
778 		u32 power_down_mask_pmu;
779 		struct mali_group *groups_down[MALI_MAX_NUMBER_OF_GROUPS];
780 		u32 num_groups_down = 0;
781 		struct mali_l2_cache_core *
782 			l2_down[MALI_MAX_NUMBER_OF_L2_CACHE_CORES];
783 		u32 num_l2_down = 0;
784 		u32 i;
785 
786 #if defined(DEBUG)
787 		++num_pm_updates_down;
788 #endif
789 
790 		/*
791 		 * Put the domains themselves in power down state.
792 		 * We get the groups and L2s to notify in return.
793 		 */
794 		mali_pm_domain_power_down(power_down_mask,
795 					  groups_down, &num_groups_down,
796 					  l2_down, &num_l2_down);
797 
798 		/* Need to unlock PM state lock before notifying L2 + groups */
799 		mali_pm_state_unlock();
800 
801 		/*
802 		 * Tell execution module about all the groups we will be
803 		 * powering down. Groups will be notified as a result of this.
804 		 */
805 		if (0 < num_groups_down) {
806 			mali_executor_group_power_down(groups_down, num_groups_down);
807 		}
808 
809 		/* Notify each L2 cache that we will be powering down */
810 		for (i = 0; i < num_l2_down; i++) {
811 			mali_l2_cache_power_down(l2_down[i]);
812 		}
813 
814 		/*
815 		 * Power down only PMU domains which should not stay on
816 		 * Some domains might for instance currently be incorrectly
817 		 * powered up if default domain power state is all on.
818 		 */
819 		power_down_mask_pmu = pmu_mask_current & (~pd_mask_current);
820 
821 		if (0 != power_down_mask_pmu) {
822 			MALI_DEBUG_ASSERT(NULL != pmu);
823 			pmu_mask_current &= ~power_down_mask_pmu;
824 			mali_pmu_power_down(pmu, power_down_mask_pmu);
825 
826 		}
827 	} else {
828 		/*
829 		 * Power down only PMU domains which should not stay on
830 		 * Some domains might for instance currently be incorrectly
831 		 * powered up if default domain power state is all on.
832 		 */
833 		u32 power_down_mask_pmu;
834 
835 		/* No need for state lock since we'll only update PMU */
836 		mali_pm_state_unlock();
837 
838 		power_down_mask_pmu = pmu_mask_current & (~pd_mask_current);
839 
840 		if (0 != power_down_mask_pmu) {
841 			MALI_DEBUG_ASSERT(NULL != pmu);
842 			pmu_mask_current &= ~power_down_mask_pmu;
843 			mali_pmu_power_down(pmu, power_down_mask_pmu);
844 		}
845 	}
846 
847 	MALI_DEBUG_PRINT(5, ("PM update post: Current domain mask: . [%s]\n",
848 			     mali_pm_mask_to_string(pd_mask_current)));
849 	MALI_DEBUG_PRINT(5, ("PM update post: Current PMU mask: .... [%s]\n",
850 			     mali_pm_mask_to_string(pmu_mask_current)));
851 	MALI_DEBUG_PRINT(5, ("PM update post: Group power stats: ... <%s>\n",
852 			     mali_pm_group_stats_to_string()));
853 }
854 
mali_pm_common_suspend(void)855 static mali_bool mali_pm_common_suspend(void)
856 {
857 	mali_pm_state_lock();
858 
859 	if (0 != pd_mask_wanted) {
860 		MALI_DEBUG_PRINT(5, ("PM: Aborting suspend operation\n\n\n"));
861 		mali_pm_state_unlock();
862 		return MALI_FALSE;
863 	}
864 
865 	MALI_DEBUG_PRINT(5, ("PM suspend pre: Wanted domain mask: .. [%s]\n",
866 			     mali_pm_mask_to_string(pd_mask_wanted)));
867 	MALI_DEBUG_PRINT(5, ("PM suspend pre: Current domain mask: . [%s]\n",
868 			     mali_pm_mask_to_string(pd_mask_current)));
869 	MALI_DEBUG_PRINT(5, ("PM suspend pre: Current PMU mask: .... [%s]\n",
870 			     mali_pm_mask_to_string(pmu_mask_current)));
871 	MALI_DEBUG_PRINT(5, ("PM suspend pre: Group power stats: ... <%s>\n",
872 			     mali_pm_group_stats_to_string()));
873 
874 	if (0 != pd_mask_current) {
875 		/*
876 		 * We have still some domains powered on.
877 		 * It is for instance very normal that at least the
878 		 * dummy/global domain is marked as powered on at this point.
879 		 * (because it is physically powered on until this function
880 		 * returns)
881 		 */
882 
883 		struct mali_group *groups_down[MALI_MAX_NUMBER_OF_GROUPS];
884 		u32 num_groups_down = 0;
885 		struct mali_l2_cache_core *
886 			l2_down[MALI_MAX_NUMBER_OF_L2_CACHE_CORES];
887 		u32 num_l2_down = 0;
888 		u32 i;
889 
890 		/*
891 		 * Put the domains themselves in power down state.
892 		 * We get the groups and L2s to notify in return.
893 		 */
894 		mali_pm_domain_power_down(pd_mask_current,
895 					  groups_down,
896 					  &num_groups_down,
897 					  l2_down,
898 					  &num_l2_down);
899 
900 		MALI_DEBUG_ASSERT(0 == pd_mask_current);
901 		MALI_DEBUG_ASSERT(MALI_TRUE == mali_pm_domain_all_unused());
902 
903 		/* Need to unlock PM state lock before notifying L2 + groups */
904 		mali_pm_state_unlock();
905 
906 		/*
907 		 * Tell execution module about all the groups we will be
908 		 * powering down. Groups will be notified as a result of this.
909 		 */
910 		if (0 < num_groups_down) {
911 			mali_executor_group_power_down(groups_down, num_groups_down);
912 		}
913 
914 		/* Notify each L2 cache that we will be powering down */
915 		for (i = 0; i < num_l2_down; i++) {
916 			mali_l2_cache_power_down(l2_down[i]);
917 		}
918 
919 		pmu_mask_current = 0;
920 	} else {
921 		MALI_DEBUG_ASSERT(0 == pmu_mask_current);
922 
923 		MALI_DEBUG_ASSERT(MALI_TRUE == mali_pm_domain_all_unused());
924 
925 		mali_pm_state_unlock();
926 	}
927 
928 	MALI_DEBUG_PRINT(5, ("PM suspend post: Current domain mask:  [%s]\n",
929 			     mali_pm_mask_to_string(pd_mask_current)));
930 	MALI_DEBUG_PRINT(5, ("PM suspend post: Current PMU mask: ... [%s]\n",
931 			     mali_pm_mask_to_string(pmu_mask_current)));
932 	MALI_DEBUG_PRINT(5, ("PM suspend post: Group power stats: .. <%s>\n",
933 			     mali_pm_group_stats_to_string()));
934 
935 	return MALI_TRUE;
936 }
937 
mali_pm_update_work(void * data)938 static void mali_pm_update_work(void *data)
939 {
940 	MALI_IGNORE(data);
941 	mali_pm_update_sync();
942 }
943 
mali_pm_create_pm_domains(void)944 static _mali_osk_errcode_t mali_pm_create_pm_domains(void)
945 {
946 	int i;
947 
948 	/* Create all domains (including dummy domain) */
949 	for (i = 0; i < MALI_MAX_NUMBER_OF_DOMAINS; i++) {
950 		if (0x0 == domain_config[i]) continue;
951 
952 		if (NULL == mali_pm_domain_create(domain_config[i])) {
953 			return _MALI_OSK_ERR_NOMEM;
954 		}
955 	}
956 
957 	return _MALI_OSK_ERR_OK;
958 }
959 
mali_pm_set_default_pm_domain_config(void)960 static void mali_pm_set_default_pm_domain_config(void)
961 {
962 	MALI_DEBUG_ASSERT(0 != _mali_osk_resource_base_address());
963 
964 	/* GP core */
965 	if (_MALI_OSK_ERR_OK == _mali_osk_resource_find(
966 		    MALI_OFFSET_GP, NULL)) {
967 		domain_config[MALI_DOMAIN_INDEX_GP] = 0x01;
968 	}
969 
970 	/* PP0 - PP3 core */
971 	if (_MALI_OSK_ERR_OK == _mali_osk_resource_find(
972 		    MALI_OFFSET_PP0, NULL)) {
973 		if (mali_is_mali400()) {
974 			domain_config[MALI_DOMAIN_INDEX_PP0] = 0x01 << 2;
975 		} else if (mali_is_mali450()) {
976 			domain_config[MALI_DOMAIN_INDEX_PP0] = 0x01 << 1;
977 		} else if (mali_is_mali470()) {
978 			domain_config[MALI_DOMAIN_INDEX_PP0] = 0x01 << 0;
979 		}
980 	}
981 
982 	if (_MALI_OSK_ERR_OK == _mali_osk_resource_find(
983 		    MALI_OFFSET_PP1, NULL)) {
984 		if (mali_is_mali400()) {
985 			domain_config[MALI_DOMAIN_INDEX_PP1] = 0x01 << 3;
986 		} else if (mali_is_mali450()) {
987 			domain_config[MALI_DOMAIN_INDEX_PP1] = 0x01 << 2;
988 		} else if (mali_is_mali470()) {
989 			domain_config[MALI_DOMAIN_INDEX_PP1] = 0x01 << 1;
990 		}
991 	}
992 
993 	if (_MALI_OSK_ERR_OK == _mali_osk_resource_find(
994 		    MALI_OFFSET_PP2, NULL)) {
995 		if (mali_is_mali400()) {
996 			domain_config[MALI_DOMAIN_INDEX_PP2] = 0x01 << 4;
997 		} else if (mali_is_mali450()) {
998 			domain_config[MALI_DOMAIN_INDEX_PP2] = 0x01 << 2;
999 		} else if (mali_is_mali470()) {
1000 			domain_config[MALI_DOMAIN_INDEX_PP2] = 0x01 << 1;
1001 		}
1002 	}
1003 
1004 	if (_MALI_OSK_ERR_OK == _mali_osk_resource_find(
1005 		    MALI_OFFSET_PP3, NULL)) {
1006 		if (mali_is_mali400()) {
1007 			domain_config[MALI_DOMAIN_INDEX_PP3] = 0x01 << 5;
1008 		} else if (mali_is_mali450()) {
1009 			domain_config[MALI_DOMAIN_INDEX_PP3] = 0x01 << 2;
1010 		} else if (mali_is_mali470()) {
1011 			domain_config[MALI_DOMAIN_INDEX_PP3] = 0x01 << 1;
1012 		}
1013 	}
1014 
1015 	/* PP4 - PP7 */
1016 	if (_MALI_OSK_ERR_OK == _mali_osk_resource_find(
1017 		    MALI_OFFSET_PP4, NULL)) {
1018 		domain_config[MALI_DOMAIN_INDEX_PP4] = 0x01 << 3;
1019 	}
1020 
1021 	if (_MALI_OSK_ERR_OK == _mali_osk_resource_find(
1022 		    MALI_OFFSET_PP5, NULL)) {
1023 		domain_config[MALI_DOMAIN_INDEX_PP5] = 0x01 << 3;
1024 	}
1025 
1026 	if (_MALI_OSK_ERR_OK == _mali_osk_resource_find(
1027 		    MALI_OFFSET_PP6, NULL)) {
1028 		domain_config[MALI_DOMAIN_INDEX_PP6] = 0x01 << 3;
1029 	}
1030 
1031 	if (_MALI_OSK_ERR_OK == _mali_osk_resource_find(
1032 		    MALI_OFFSET_PP7, NULL)) {
1033 		domain_config[MALI_DOMAIN_INDEX_PP7] = 0x01 << 3;
1034 	}
1035 
1036 	/* L2gp/L2PP0/L2PP4 */
1037 	if (mali_is_mali400()) {
1038 		if (_MALI_OSK_ERR_OK == _mali_osk_resource_find(
1039 			    MALI400_OFFSET_L2_CACHE0, NULL)) {
1040 			domain_config[MALI_DOMAIN_INDEX_L20] = 0x01 << 1;
1041 		}
1042 	} else if (mali_is_mali450()) {
1043 		if (_MALI_OSK_ERR_OK == _mali_osk_resource_find(
1044 			    MALI450_OFFSET_L2_CACHE0, NULL)) {
1045 			domain_config[MALI_DOMAIN_INDEX_L20] = 0x01 << 0;
1046 		}
1047 
1048 		if (_MALI_OSK_ERR_OK == _mali_osk_resource_find(
1049 			    MALI450_OFFSET_L2_CACHE1, NULL)) {
1050 			domain_config[MALI_DOMAIN_INDEX_L21] = 0x01 << 1;
1051 		}
1052 
1053 		if (_MALI_OSK_ERR_OK == _mali_osk_resource_find(
1054 			    MALI450_OFFSET_L2_CACHE2, NULL)) {
1055 			domain_config[MALI_DOMAIN_INDEX_L22] = 0x01 << 3;
1056 		}
1057 	} else if (mali_is_mali470()) {
1058 		if (_MALI_OSK_ERR_OK == _mali_osk_resource_find(
1059 			    MALI470_OFFSET_L2_CACHE1, NULL)) {
1060 			domain_config[MALI_DOMAIN_INDEX_L21] = 0x01 << 0;
1061 		}
1062 	}
1063 }
1064 
mali_pm_get_registered_cores_mask(void)1065 static u32 mali_pm_get_registered_cores_mask(void)
1066 {
1067 	int i = 0;
1068 	u32 mask = 0;
1069 
1070 	for (i = 0; i < MALI_DOMAIN_INDEX_DUMMY; i++) {
1071 		mask |= domain_config[i];
1072 	}
1073 
1074 	return mask;
1075 }
1076 
mali_pm_set_pmu_domain_config(void)1077 static void mali_pm_set_pmu_domain_config(void)
1078 {
1079 	int i = 0;
1080 
1081 	_mali_osk_device_data_pmu_config_get(domain_config, MALI_MAX_NUMBER_OF_DOMAINS - 1);
1082 
1083 	for (i = 0; i < MALI_MAX_NUMBER_OF_DOMAINS - 1; i++) {
1084 		if (0 != domain_config[i]) {
1085 			MALI_DEBUG_PRINT(2, ("Using customer pmu config:\n"));
1086 			break;
1087 		}
1088 	}
1089 
1090 	if (MALI_MAX_NUMBER_OF_DOMAINS - 1 == i) {
1091 		MALI_DEBUG_PRINT(2, ("Using hw detect pmu config:\n"));
1092 		mali_pm_set_default_pm_domain_config();
1093 	}
1094 
1095 	for (i = 0; i < MALI_MAX_NUMBER_OF_DOMAINS - 1; i++) {
1096 		if (domain_config[i]) {
1097 			MALI_DEBUG_PRINT(2, ("domain_config[%d] = 0x%x \n", i, domain_config[i]));
1098 		}
1099 	}
1100 	/* Can't override dummy domain mask */
1101 	domain_config[MALI_DOMAIN_INDEX_DUMMY] =
1102 		1 << MALI_DOMAIN_INDEX_DUMMY;
1103 }
1104 
1105 #if defined(DEBUG)
mali_pm_mask_to_string(u32 mask)1106 const char *mali_pm_mask_to_string(u32 mask)
1107 {
1108 	static char bit_str[MALI_MAX_NUMBER_OF_DOMAINS + 1];
1109 	int bit;
1110 	int str_pos = 0;
1111 
1112 	/* Must be protected by lock since we use shared string buffer */
1113 	if (NULL != pm_lock_exec) {
1114 		MALI_DEBUG_ASSERT_LOCK_HELD(pm_lock_exec);
1115 	}
1116 
1117 	for (bit = MALI_MAX_NUMBER_OF_DOMAINS - 1; bit >= 0; bit--) {
1118 		if (mask & (1 << bit)) {
1119 			bit_str[str_pos] = 'X';
1120 		} else {
1121 			bit_str[str_pos] = '-';
1122 		}
1123 		str_pos++;
1124 	}
1125 
1126 	bit_str[MALI_MAX_NUMBER_OF_DOMAINS] = '\0';
1127 
1128 	return bit_str;
1129 }
1130 
mali_pm_group_stats_to_string(void)1131 const char *mali_pm_group_stats_to_string(void)
1132 {
1133 	static char bit_str[MALI_MAX_NUMBER_OF_GROUPS + 1];
1134 	u32 num_groups = mali_group_get_glob_num_groups();
1135 	u32 i;
1136 
1137 	/* Must be protected by lock since we use shared string buffer */
1138 	if (NULL != pm_lock_exec) {
1139 		MALI_DEBUG_ASSERT_LOCK_HELD(pm_lock_exec);
1140 	}
1141 
1142 	for (i = 0; i < num_groups && i < MALI_MAX_NUMBER_OF_GROUPS; i++) {
1143 		struct mali_group *group;
1144 
1145 		group = mali_group_get_glob_group(i);
1146 
1147 		if (MALI_TRUE == mali_group_power_is_on(group)) {
1148 			bit_str[i] = 'X';
1149 		} else {
1150 			bit_str[i] = '-';
1151 		}
1152 	}
1153 
1154 	bit_str[i] = '\0';
1155 
1156 	return bit_str;
1157 }
1158 #endif
1159 
1160 /*
1161  * num_pp is the number of PP cores which will be powered on given this mask
1162  * cost is the total power cost of cores which will be powered on given this mask
1163  */
mali_pm_stat_from_mask(u32 mask,u32 * num_pp,u32 * cost)1164 static void mali_pm_stat_from_mask(u32 mask, u32 *num_pp, u32 *cost)
1165 {
1166 	u32 i;
1167 
1168 	/* loop through all cores */
1169 	for (i = 0; i < MALI_MAX_NUMBER_OF_DOMAINS; i++) {
1170 		if (!(domain_config[i] & mask)) {
1171 			continue;
1172 		}
1173 
1174 		switch (i) {
1175 		case MALI_DOMAIN_INDEX_GP:
1176 			*cost += MALI_GP_COST;
1177 
1178 			break;
1179 		case MALI_DOMAIN_INDEX_PP0: /* Fall through */
1180 		case MALI_DOMAIN_INDEX_PP1: /* Fall through */
1181 		case MALI_DOMAIN_INDEX_PP2: /* Fall through */
1182 		case MALI_DOMAIN_INDEX_PP3:
1183 			if (mali_is_mali400()) {
1184 				if ((domain_config[MALI_DOMAIN_INDEX_L20] & mask)
1185 				    || (domain_config[MALI_DOMAIN_INDEX_DUMMY]
1186 					== domain_config[MALI_DOMAIN_INDEX_L20])) {
1187 					*num_pp += 1;
1188 				}
1189 			} else {
1190 				if ((domain_config[MALI_DOMAIN_INDEX_L21] & mask)
1191 				    || (domain_config[MALI_DOMAIN_INDEX_DUMMY]
1192 					== domain_config[MALI_DOMAIN_INDEX_L21])) {
1193 					*num_pp += 1;
1194 				}
1195 			}
1196 
1197 			*cost += MALI_PP_COST;
1198 			break;
1199 		case MALI_DOMAIN_INDEX_PP4: /* Fall through */
1200 		case MALI_DOMAIN_INDEX_PP5: /* Fall through */
1201 		case MALI_DOMAIN_INDEX_PP6: /* Fall through */
1202 		case MALI_DOMAIN_INDEX_PP7:
1203 			MALI_DEBUG_ASSERT(mali_is_mali450());
1204 
1205 			if ((domain_config[MALI_DOMAIN_INDEX_L22] & mask)
1206 			    || (domain_config[MALI_DOMAIN_INDEX_DUMMY]
1207 				== domain_config[MALI_DOMAIN_INDEX_L22])) {
1208 				*num_pp += 1;
1209 			}
1210 
1211 			*cost += MALI_PP_COST;
1212 			break;
1213 		case MALI_DOMAIN_INDEX_L20: /* Fall through */
1214 		case MALI_DOMAIN_INDEX_L21: /* Fall through */
1215 		case MALI_DOMAIN_INDEX_L22:
1216 			*cost += MALI_L2_COST;
1217 
1218 			break;
1219 		}
1220 	}
1221 }
1222 
mali_pm_power_cost_setup(void)1223 void mali_pm_power_cost_setup(void)
1224 {
1225 	/*
1226 	 * Two parallel arrays which store the best domain mask and its cost
1227 	 * The index is the number of PP cores, E.g. Index 0 is for 1 PP option,
1228 	 * might have mask 0x2 and with cost of 1, lower cost is better
1229 	 */
1230 	u32 best_mask[MALI_MAX_NUMBER_OF_PHYSICAL_PP_GROUPS] = { 0 };
1231 	u32 best_cost[MALI_MAX_NUMBER_OF_PHYSICAL_PP_GROUPS] = { 0 };
1232 	/* Array cores_in_domain is used to store the total pp cores in each pm domain. */
1233 	u32 cores_in_domain[MALI_MAX_NUMBER_OF_DOMAINS] = { 0 };
1234 	/* Domain_count is used to represent the max domain we have.*/
1235 	u32 max_domain_mask = 0;
1236 	u32 max_domain_id = 0;
1237 	u32 always_on_pp_cores = 0;
1238 
1239 	u32 num_pp, cost, mask;
1240 	u32 i, j , k;
1241 
1242 	/* Initialize statistics */
1243 	for (i = 0; i < MALI_MAX_NUMBER_OF_PHYSICAL_PP_GROUPS; i++) {
1244 		best_mask[i] = 0;
1245 		best_cost[i] = 0xFFFFFFFF; /* lower cost is better */
1246 	}
1247 
1248 	for (i = 0; i < MALI_MAX_NUMBER_OF_PHYSICAL_PP_GROUPS + 1; i++) {
1249 		for (j = 0; j < MALI_MAX_NUMBER_OF_DOMAINS; j++) {
1250 			mali_pm_domain_power_cost_result[i][j] = 0;
1251 		}
1252 	}
1253 
1254 	/* Caculate number of pp cores of a given domain config. */
1255 	for (i = MALI_DOMAIN_INDEX_PP0; i <= MALI_DOMAIN_INDEX_PP7; i++) {
1256 		if (0 < domain_config[i]) {
1257 			/* Get the max domain mask value used to caculate power cost
1258 			 * and we don't count in always on pp cores. */
1259 			if (MALI_PM_DOMAIN_DUMMY_MASK != domain_config[i]
1260 			    && max_domain_mask < domain_config[i]) {
1261 				max_domain_mask = domain_config[i];
1262 			}
1263 
1264 			if (MALI_PM_DOMAIN_DUMMY_MASK == domain_config[i]) {
1265 				always_on_pp_cores++;
1266 			}
1267 		}
1268 	}
1269 	max_domain_id = _mali_osk_fls(max_domain_mask);
1270 
1271 	/*
1272 	 * Try all combinations of power domains and check how many PP cores
1273 	 * they have and their power cost.
1274 	 */
1275 	for (mask = 0; mask < (1 << max_domain_id); mask++) {
1276 		num_pp = 0;
1277 		cost = 0;
1278 
1279 		mali_pm_stat_from_mask(mask, &num_pp, &cost);
1280 
1281 		/* This mask is usable for all MP1 up to num_pp PP cores, check statistics for all */
1282 		for (i = 0; i < num_pp; i++) {
1283 			if (best_cost[i] >= cost) {
1284 				best_cost[i] = cost;
1285 				best_mask[i] = mask;
1286 			}
1287 		}
1288 	}
1289 
1290 	/*
1291 	 * If we want to enable x pp cores, if x is less than number of always_on pp cores,
1292 	 * all of pp cores we will enable must be always_on pp cores.
1293 	 */
1294 	for (i = 0; i < mali_executor_get_num_cores_total(); i++) {
1295 		if (i < always_on_pp_cores) {
1296 			mali_pm_domain_power_cost_result[i + 1][MALI_MAX_NUMBER_OF_DOMAINS - 1]
1297 				= i + 1;
1298 		} else {
1299 			mali_pm_domain_power_cost_result[i + 1][MALI_MAX_NUMBER_OF_DOMAINS - 1]
1300 				= always_on_pp_cores;
1301 		}
1302 	}
1303 
1304 	/* In this loop, variable i represent for the number of non-always on pp cores we want to enabled. */
1305 	for (i = 0; i < (mali_executor_get_num_cores_total() - always_on_pp_cores); i++) {
1306 		if (best_mask[i] == 0) {
1307 			/* This MP variant is not available */
1308 			continue;
1309 		}
1310 
1311 		for (j = 0; j < MALI_MAX_NUMBER_OF_DOMAINS; j++) {
1312 			cores_in_domain[j] = 0;
1313 		}
1314 
1315 		for (j = MALI_DOMAIN_INDEX_PP0; j <= MALI_DOMAIN_INDEX_PP7; j++) {
1316 			if (0 < domain_config[j]
1317 			    && (MALI_PM_DOMAIN_DUMMY_MASK != domain_config[i])) {
1318 				cores_in_domain[_mali_osk_fls(domain_config[j]) - 1]++;
1319 			}
1320 		}
1321 
1322 		/* In this loop, j represent for the number we have already enabled.*/
1323 		for (j = 0; j <= i;) {
1324 			/* j used to visit all of domain to get the number of pp cores remained in it. */
1325 			for (k = 0; k < max_domain_id; k++) {
1326 				/* If domain k in best_mask[i] is enabled and this domain has extra pp cores,
1327 				 * we know we must pick at least one pp core from this domain.
1328 				 * And then we move to next enabled pm domain. */
1329 				if ((best_mask[i] & (0x1 << k)) && (0 < cores_in_domain[k])) {
1330 					cores_in_domain[k]--;
1331 					mali_pm_domain_power_cost_result[always_on_pp_cores + i + 1][k]++;
1332 					j++;
1333 					if (j > i) {
1334 						break;
1335 					}
1336 				}
1337 			}
1338 		}
1339 	}
1340 }
1341 
1342 /*
1343  * When we are doing core scaling,
1344  * this function is called to return the best mask to
1345  * achieve the best pp group power cost.
1346  */
mali_pm_get_best_power_cost_mask(int num_requested,int * dst)1347 void mali_pm_get_best_power_cost_mask(int num_requested, int *dst)
1348 {
1349 	MALI_DEBUG_ASSERT((mali_executor_get_num_cores_total() >= num_requested) && (0 <= num_requested));
1350 
1351 	_mali_osk_memcpy(dst, mali_pm_domain_power_cost_result[num_requested], MALI_MAX_NUMBER_OF_DOMAINS * sizeof(int));
1352 }
1353 
mali_pm_get_current_mask(void)1354 u32 mali_pm_get_current_mask(void)
1355 {
1356 	return pd_mask_current;
1357 }
1358 
mali_pm_get_wanted_mask(void)1359 u32 mali_pm_get_wanted_mask(void)
1360 {
1361 	return pd_mask_wanted;
1362 }
1363