Commit | Line | Data |
---|---|---|
6fa3eb70 S |
1 | /* |
2 | * This confidential and proprietary software may be used only as | |
3 | * authorised by a licensing agreement from ARM Limited | |
4 | * (C) COPYRIGHT 2009-2013 ARM Limited | |
5 | * ALL RIGHTS RESERVED | |
6 | * The entire notice above must be reproduced on all authorised | |
7 | * copies and copies may only be made to the extent permitted | |
8 | * by a licensing agreement from ARM Limited. | |
9 | */ | |
10 | ||
11 | /** | |
12 | * @file mali_pmu.c | |
13 | * Mali driver functions for Mali 400 PMU hardware | |
14 | */ | |
15 | #include "mali_hw_core.h" | |
16 | #include "mali_pmu.h" | |
17 | #include "mali_pp.h" | |
18 | #include "mali_kernel_common.h" | |
19 | #include "mali_osk.h" | |
20 | #include "mali_pm.h" | |
21 | #include "mali_osk_mali.h" | |
22 | ||
23 | u16 mali_pmu_global_domain_config[MALI_MAX_NUMBER_OF_DOMAINS]= {0}; | |
24 | ||
25 | static u32 mali_pmu_detect_mask(void); | |
26 | ||
27 | /** @brief MALI inbuilt PMU hardware info and PMU hardware has knowledge of cores power mask | |
28 | */ | |
29 | struct mali_pmu_core { | |
30 | struct mali_hw_core hw_core; | |
31 | _mali_osk_spinlock_t *lock; | |
32 | u32 registered_cores_mask; | |
33 | u32 active_cores_mask; | |
34 | u32 switch_delay; | |
35 | }; | |
36 | ||
37 | static struct mali_pmu_core *mali_global_pmu_core = NULL; | |
38 | ||
39 | /** @brief Register layout for hardware PMU | |
40 | */ | |
41 | typedef enum { | |
42 | PMU_REG_ADDR_MGMT_POWER_UP = 0x00, /*< Power up register */ | |
43 | PMU_REG_ADDR_MGMT_POWER_DOWN = 0x04, /*< Power down register */ | |
44 | PMU_REG_ADDR_MGMT_STATUS = 0x08, /*< Core sleep status register */ | |
45 | PMU_REG_ADDR_MGMT_INT_MASK = 0x0C, /*< Interrupt mask register */ | |
46 | PMU_REG_ADDR_MGMT_INT_RAWSTAT = 0x10, /*< Interrupt raw status register */ | |
47 | PMU_REG_ADDR_MGMT_INT_CLEAR = 0x18, /*< Interrupt clear register */ | |
48 | PMU_REG_ADDR_MGMT_SW_DELAY = 0x1C, /*< Switch delay register */ | |
49 | PMU_REGISTER_ADDRESS_SPACE_SIZE = 0x28, /*< Size of register space */ | |
50 | } pmu_reg_addr_mgmt_addr; | |
51 | ||
52 | #define PMU_REG_VAL_IRQ 1 | |
53 | ||
54 | struct mali_pmu_core *mali_pmu_create(_mali_osk_resource_t *resource) | |
55 | { | |
56 | struct mali_pmu_core* pmu; | |
57 | ||
58 | MALI_DEBUG_ASSERT(NULL == mali_global_pmu_core); | |
59 | MALI_DEBUG_PRINT(2, ("Mali PMU: Creating Mali PMU core\n")); | |
60 | ||
61 | pmu = (struct mali_pmu_core *)_mali_osk_malloc(sizeof(struct mali_pmu_core)); | |
62 | if (NULL != pmu) { | |
63 | pmu->lock = _mali_osk_spinlock_init(_MALI_OSK_LOCKFLAG_ORDERED, _MALI_OSK_LOCK_ORDER_PMU); | |
64 | if (NULL != pmu->lock) { | |
65 | pmu->registered_cores_mask = mali_pmu_detect_mask(); | |
66 | pmu->active_cores_mask = pmu->registered_cores_mask; | |
67 | ||
68 | if (_MALI_OSK_ERR_OK == mali_hw_core_create(&pmu->hw_core, resource, PMU_REGISTER_ADDRESS_SPACE_SIZE)) { | |
69 | _mali_osk_errcode_t err; | |
70 | struct _mali_osk_device_data data = { 0, }; | |
71 | ||
72 | err = _mali_osk_device_data_get(&data); | |
73 | if (_MALI_OSK_ERR_OK == err) { | |
74 | pmu->switch_delay = data.pmu_switch_delay; | |
75 | mali_global_pmu_core = pmu; | |
76 | return pmu; | |
77 | } | |
78 | mali_hw_core_delete(&pmu->hw_core); | |
79 | } | |
80 | _mali_osk_spinlock_term(pmu->lock); | |
81 | } | |
82 | _mali_osk_free(pmu); | |
83 | } | |
84 | ||
85 | return NULL; | |
86 | } | |
87 | ||
88 | void mali_pmu_delete(struct mali_pmu_core *pmu) | |
89 | { | |
90 | MALI_DEBUG_ASSERT_POINTER(pmu); | |
91 | MALI_DEBUG_ASSERT(pmu == mali_global_pmu_core); | |
92 | MALI_DEBUG_PRINT(2, ("Mali PMU: Deleting Mali PMU core\n")); | |
93 | ||
94 | _mali_osk_spinlock_term(pmu->lock); | |
95 | mali_hw_core_delete(&pmu->hw_core); | |
96 | _mali_osk_free(pmu); | |
97 | mali_global_pmu_core = NULL; | |
98 | } | |
99 | ||
100 | static void mali_pmu_lock(struct mali_pmu_core *pmu) | |
101 | { | |
102 | _mali_osk_spinlock_lock(pmu->lock); | |
103 | } | |
104 | static void mali_pmu_unlock(struct mali_pmu_core *pmu) | |
105 | { | |
106 | _mali_osk_spinlock_unlock(pmu->lock); | |
107 | } | |
108 | ||
109 | static _mali_osk_errcode_t mali_pmu_wait_for_command_finish(struct mali_pmu_core *pmu) | |
110 | { | |
111 | u32 rawstat; | |
112 | u32 timeout = MALI_REG_POLL_COUNT_SLOW; | |
113 | ||
114 | MALI_DEBUG_ASSERT(pmu); | |
115 | ||
116 | /* Wait for the command to complete */ | |
117 | do { | |
118 | rawstat = mali_hw_core_register_read(&pmu->hw_core, PMU_REG_ADDR_MGMT_INT_RAWSTAT); | |
119 | --timeout; | |
120 | } while (0 == (rawstat & PMU_REG_VAL_IRQ) && 0 < timeout); | |
121 | ||
122 | MALI_DEBUG_ASSERT(0 < timeout); | |
123 | if (0 == timeout) { | |
124 | return _MALI_OSK_ERR_TIMEOUT; | |
125 | } | |
126 | ||
127 | mali_hw_core_register_write(&pmu->hw_core, PMU_REG_ADDR_MGMT_INT_CLEAR, PMU_REG_VAL_IRQ); | |
128 | ||
129 | return _MALI_OSK_ERR_OK; | |
130 | } | |
131 | ||
132 | static _mali_osk_errcode_t mali_pmu_power_up_internal(struct mali_pmu_core *pmu, const u32 mask) | |
133 | { | |
134 | u32 stat; | |
135 | _mali_osk_errcode_t err; | |
136 | #if !defined(CONFIG_MALI_PMU_PARALLEL_POWER_UP) | |
137 | u32 current_domain; | |
138 | #endif | |
139 | ||
140 | MALI_DEBUG_ASSERT_POINTER(pmu); | |
141 | MALI_DEBUG_ASSERT(0 == (mali_hw_core_register_read(&pmu->hw_core, PMU_REG_ADDR_MGMT_INT_RAWSTAT) | |
142 | & PMU_REG_VAL_IRQ)); | |
143 | ||
144 | stat = mali_hw_core_register_read(&pmu->hw_core, PMU_REG_ADDR_MGMT_STATUS); | |
145 | stat &= pmu->registered_cores_mask; | |
146 | if (0 == mask || 0 == (stat & mask)) return _MALI_OSK_ERR_OK; | |
147 | ||
148 | #if defined(CONFIG_MALI_PMU_PARALLEL_POWER_UP) | |
149 | mali_hw_core_register_write(&pmu->hw_core, PMU_REG_ADDR_MGMT_POWER_UP, mask); | |
150 | ||
151 | err = mali_pmu_wait_for_command_finish(pmu); | |
152 | if (_MALI_OSK_ERR_OK != err) { | |
153 | return err; | |
154 | } | |
155 | #else | |
156 | for (current_domain = 1; current_domain <= pmu->registered_cores_mask; current_domain <<= 1) { | |
157 | if (current_domain & mask & stat) { | |
158 | mali_hw_core_register_write(&pmu->hw_core, PMU_REG_ADDR_MGMT_POWER_UP, current_domain); | |
159 | ||
160 | err = mali_pmu_wait_for_command_finish(pmu); | |
161 | if (_MALI_OSK_ERR_OK != err) { | |
162 | return err; | |
163 | } | |
164 | } | |
165 | } | |
166 | #endif | |
167 | ||
168 | #if defined(DEBUG) | |
169 | /* Get power status of cores */ | |
170 | stat = mali_hw_core_register_read(&pmu->hw_core, PMU_REG_ADDR_MGMT_STATUS); | |
171 | stat &= pmu->registered_cores_mask; | |
172 | ||
173 | MALI_DEBUG_ASSERT(0 == (stat & mask)); | |
174 | MALI_DEBUG_ASSERT(0 == (stat & pmu->active_cores_mask)); | |
175 | #endif /* defined(DEBUG) */ | |
176 | ||
177 | return _MALI_OSK_ERR_OK; | |
178 | } | |
179 | ||
180 | static _mali_osk_errcode_t mali_pmu_power_down_internal(struct mali_pmu_core *pmu, const u32 mask) | |
181 | { | |
182 | u32 stat; | |
183 | _mali_osk_errcode_t err; | |
184 | ||
185 | MALI_DEBUG_ASSERT_POINTER(pmu); | |
186 | MALI_DEBUG_ASSERT(0 == (mali_hw_core_register_read(&pmu->hw_core, PMU_REG_ADDR_MGMT_INT_RAWSTAT) | |
187 | & PMU_REG_VAL_IRQ)); | |
188 | ||
189 | stat = mali_hw_core_register_read(&pmu->hw_core, PMU_REG_ADDR_MGMT_STATUS); | |
190 | stat &= pmu->registered_cores_mask; | |
191 | ||
192 | if (0 == mask || 0 == ((~stat) & mask)) return _MALI_OSK_ERR_OK; | |
193 | ||
194 | mali_hw_core_register_write(&pmu->hw_core, PMU_REG_ADDR_MGMT_POWER_DOWN, mask); | |
195 | ||
196 | /* Do not wait for interrupt on Mali-300/400 if all domains are powered off | |
197 | * by our power down command, because the HW will simply not generate an | |
198 | * interrupt in this case.*/ | |
199 | if (mali_is_mali450() || pmu->registered_cores_mask != (mask | stat)) { | |
200 | err = mali_pmu_wait_for_command_finish(pmu); | |
201 | if (_MALI_OSK_ERR_OK != err) { | |
202 | return err; | |
203 | } | |
204 | } else { | |
205 | mali_hw_core_register_write(&pmu->hw_core, PMU_REG_ADDR_MGMT_INT_CLEAR, PMU_REG_VAL_IRQ); | |
206 | } | |
207 | #if defined(DEBUG) | |
208 | /* Get power status of cores */ | |
209 | stat = mali_hw_core_register_read(&pmu->hw_core, PMU_REG_ADDR_MGMT_STATUS); | |
210 | stat &= pmu->registered_cores_mask; | |
211 | ||
212 | MALI_DEBUG_ASSERT(mask == (stat & mask)); | |
213 | #endif | |
214 | ||
215 | return _MALI_OSK_ERR_OK; | |
216 | } | |
217 | ||
218 | _mali_osk_errcode_t mali_pmu_reset(struct mali_pmu_core *pmu) | |
219 | { | |
220 | _mali_osk_errcode_t err; | |
221 | u32 cores_off_mask, cores_on_mask, stat; | |
222 | ||
223 | mali_pmu_lock(pmu); | |
224 | ||
225 | /* Setup the desired defaults */ | |
226 | mali_hw_core_register_write_relaxed(&pmu->hw_core, PMU_REG_ADDR_MGMT_INT_MASK, 0); | |
227 | mali_hw_core_register_write_relaxed(&pmu->hw_core, PMU_REG_ADDR_MGMT_SW_DELAY, pmu->switch_delay); | |
228 | ||
229 | /* Get power status of cores */ | |
230 | stat = mali_hw_core_register_read(&pmu->hw_core, PMU_REG_ADDR_MGMT_STATUS); | |
231 | ||
232 | cores_off_mask = pmu->registered_cores_mask & ~(stat | pmu->active_cores_mask); | |
233 | cores_on_mask = pmu->registered_cores_mask & (stat & pmu->active_cores_mask); | |
234 | ||
235 | if (0 != cores_off_mask) { | |
236 | err = mali_pmu_power_down_internal(pmu, cores_off_mask); | |
237 | if (_MALI_OSK_ERR_OK != err) return err; | |
238 | } | |
239 | ||
240 | if (0 != cores_on_mask) { | |
241 | err = mali_pmu_power_up_internal(pmu, cores_on_mask); | |
242 | if (_MALI_OSK_ERR_OK != err) return err; | |
243 | } | |
244 | ||
245 | #if defined(DEBUG) | |
246 | { | |
247 | stat = mali_hw_core_register_read(&pmu->hw_core, PMU_REG_ADDR_MGMT_STATUS); | |
248 | stat &= pmu->registered_cores_mask; | |
249 | ||
250 | MALI_DEBUG_ASSERT(stat == (pmu->registered_cores_mask & ~pmu->active_cores_mask)); | |
251 | } | |
252 | #endif /* defined(DEBUG) */ | |
253 | ||
254 | mali_pmu_unlock(pmu); | |
255 | ||
256 | return _MALI_OSK_ERR_OK; | |
257 | } | |
258 | ||
259 | _mali_osk_errcode_t mali_pmu_power_down(struct mali_pmu_core *pmu, u32 mask) | |
260 | { | |
261 | _mali_osk_errcode_t err; | |
262 | ||
263 | MALI_DEBUG_ASSERT_POINTER(pmu); | |
264 | MALI_DEBUG_ASSERT(pmu->registered_cores_mask != 0 ); | |
265 | ||
266 | /* Make sure we have a valid power domain mask */ | |
267 | if (mask > pmu->registered_cores_mask) { | |
268 | return _MALI_OSK_ERR_INVALID_ARGS; | |
269 | } | |
270 | ||
271 | mali_pmu_lock(pmu); | |
272 | ||
273 | MALI_DEBUG_PRINT(4, ("Mali PMU: Power down (0x%08X)\n", mask)); | |
274 | ||
275 | pmu->active_cores_mask &= ~mask; | |
276 | ||
277 | _mali_osk_pm_dev_ref_add_no_power_on(); | |
278 | if (!mali_pm_is_power_on()) { | |
279 | /* Don't touch hardware if all of Mali is powered off. */ | |
280 | _mali_osk_pm_dev_ref_dec_no_power_on(); | |
281 | mali_pmu_unlock(pmu); | |
282 | ||
283 | MALI_DEBUG_PRINT(4, ("Mali PMU: Skipping power down (0x%08X) since Mali is off\n", mask)); | |
284 | ||
285 | return _MALI_OSK_ERR_BUSY; | |
286 | } | |
287 | ||
288 | err = mali_pmu_power_down_internal(pmu, mask); | |
289 | ||
290 | _mali_osk_pm_dev_ref_dec_no_power_on(); | |
291 | mali_pmu_unlock(pmu); | |
292 | ||
293 | return err; | |
294 | } | |
295 | ||
296 | _mali_osk_errcode_t mali_pmu_power_up(struct mali_pmu_core *pmu, u32 mask) | |
297 | { | |
298 | _mali_osk_errcode_t err; | |
299 | ||
300 | MALI_DEBUG_ASSERT_POINTER(pmu); | |
301 | MALI_DEBUG_ASSERT(pmu->registered_cores_mask != 0 ); | |
302 | ||
303 | /* Make sure we have a valid power domain mask */ | |
304 | if (mask & ~pmu->registered_cores_mask) { | |
305 | return _MALI_OSK_ERR_INVALID_ARGS; | |
306 | } | |
307 | ||
308 | mali_pmu_lock(pmu); | |
309 | ||
310 | MALI_DEBUG_PRINT(4, ("Mali PMU: Power up (0x%08X)\n", mask)); | |
311 | ||
312 | pmu->active_cores_mask |= mask; | |
313 | ||
314 | _mali_osk_pm_dev_ref_add_no_power_on(); | |
315 | if (!mali_pm_is_power_on()) { | |
316 | /* Don't touch hardware if all of Mali is powered off. */ | |
317 | _mali_osk_pm_dev_ref_dec_no_power_on(); | |
318 | mali_pmu_unlock(pmu); | |
319 | ||
320 | MALI_DEBUG_PRINT(4, ("Mali PMU: Skipping power up (0x%08X) since Mali is off\n", mask)); | |
321 | ||
322 | return _MALI_OSK_ERR_BUSY; | |
323 | } | |
324 | ||
325 | err = mali_pmu_power_up_internal(pmu, mask); | |
326 | ||
327 | _mali_osk_pm_dev_ref_dec_no_power_on(); | |
328 | mali_pmu_unlock(pmu); | |
329 | ||
330 | return err; | |
331 | } | |
332 | ||
333 | _mali_osk_errcode_t mali_pmu_power_down_all(struct mali_pmu_core *pmu) | |
334 | { | |
335 | _mali_osk_errcode_t err; | |
336 | ||
337 | MALI_DEBUG_ASSERT_POINTER(pmu); | |
338 | MALI_DEBUG_ASSERT(pmu->registered_cores_mask != 0); | |
339 | ||
340 | mali_pmu_lock(pmu); | |
341 | ||
342 | /* Setup the desired defaults in case we were called before mali_pmu_reset() */ | |
343 | mali_hw_core_register_write_relaxed(&pmu->hw_core, PMU_REG_ADDR_MGMT_INT_MASK, 0); | |
344 | mali_hw_core_register_write_relaxed(&pmu->hw_core, PMU_REG_ADDR_MGMT_SW_DELAY, pmu->switch_delay); | |
345 | ||
346 | err = mali_pmu_power_down_internal(pmu, pmu->registered_cores_mask); | |
347 | ||
348 | mali_pmu_unlock(pmu); | |
349 | ||
350 | return err; | |
351 | } | |
352 | ||
353 | _mali_osk_errcode_t mali_pmu_power_up_all(struct mali_pmu_core *pmu) | |
354 | { | |
355 | _mali_osk_errcode_t err; | |
356 | ||
357 | MALI_DEBUG_ASSERT_POINTER(pmu); | |
358 | MALI_DEBUG_ASSERT(pmu->registered_cores_mask != 0); | |
359 | ||
360 | mali_pmu_lock(pmu); | |
361 | ||
362 | /* Setup the desired defaults in case we were called before mali_pmu_reset() */ | |
363 | mali_hw_core_register_write_relaxed(&pmu->hw_core, PMU_REG_ADDR_MGMT_INT_MASK, 0); | |
364 | mali_hw_core_register_write_relaxed(&pmu->hw_core, PMU_REG_ADDR_MGMT_SW_DELAY, pmu->switch_delay); | |
365 | ||
366 | err = mali_pmu_power_up_internal(pmu, pmu->active_cores_mask); | |
367 | ||
368 | mali_pmu_unlock(pmu); | |
369 | return err; | |
370 | } | |
371 | ||
372 | struct mali_pmu_core *mali_pmu_get_global_pmu_core(void) | |
373 | { | |
374 | return mali_global_pmu_core; | |
375 | } | |
376 | ||
377 | static u32 mali_pmu_detect_mask(void) | |
378 | { | |
379 | int dynamic_config_pp = 0; | |
380 | int dynamic_config_l2 = 0; | |
381 | int i = 0; | |
382 | u32 mask = 0; | |
383 | ||
384 | /* Check if PM domain compatible with actually pp core and l2 cache and collection info about domain */ | |
385 | mask = mali_pmu_get_domain_mask(MALI_GP_DOMAIN_INDEX); | |
386 | ||
387 | for (i = MALI_PP0_DOMAIN_INDEX; i <= MALI_PP7_DOMAIN_INDEX; i++) { | |
388 | mask |= mali_pmu_get_domain_mask(i); | |
389 | ||
390 | if (0x0 != mali_pmu_get_domain_mask(i)) { | |
391 | dynamic_config_pp++; | |
392 | } | |
393 | } | |
394 | ||
395 | for (i = MALI_L20_DOMAIN_INDEX; i <= MALI_L22_DOMAIN_INDEX; i++) { | |
396 | mask |= mali_pmu_get_domain_mask(i); | |
397 | ||
398 | if (0x0 != mali_pmu_get_domain_mask(i)) { | |
399 | dynamic_config_l2++; | |
400 | } | |
401 | } | |
402 | ||
403 | MALI_DEBUG_PRINT(2, ("Mali PMU: mask 0x%x, pp_core %d, l2_core %d \n", mask, dynamic_config_pp, dynamic_config_l2)); | |
404 | ||
405 | return mask; | |
406 | } |