tizen 2.4 release
[kernel/linux-3.0.git] / drivers / gpu / arm / mali400 / r4p0_rel0 / common / mali_pmu.c
1 /*
2  * Copyright (C) 2010-2012 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 /**
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 }