tizen 2.4 release
[kernel/linux-3.0.git] / drivers / gpu / arm / mali400 / r4p0_rel0 / platform / exynos3250 / exynos3_pmm.c
1 /* drivers/gpu/mali400/mali/platform/exynos3250/exynos3_pmm.c
2  *
3  * Copyright 2011 by S.LSI. Samsung Electronics Inc.
4  * San#24, Nongseo-Dong, Giheung-Gu, Yongin, Korea
5  *
6  * Samsung SoC Mali400 DVFS driver
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License version 2 as
10  * published by the Free Software FoundatIon.
11  */
12
13 /**
14  * @file exynos3_pmm.c
15  * Platform specific Mali driver functions for the exynos 4XXX based platforms
16  */
17 #include "mali_kernel_common.h"
18 #include "mali_osk.h"
19 #include "exynos3_pmm.h"
20 #include <linux/io.h>
21 #include <linux/clk.h>
22 #include <linux/err.h>
23 #include <linux/module.h>
24 #include <linux/platform_device.h>
25 #include <linux/slab.h>
26 #include <mach/regs-pmu.h>
27
28 #ifdef CONFIG_MALI400_PROFILING
29 #include "mali_osk_profiling.h"
30 #endif
31
32 #define MPLLCLK_NAME    "sclk_mpll_pre_div"
33 #define MOUT0_NAME      "mout_g3d0"
34 #define GPUCLK_NAME      "sclk_g3d"
35
36 struct mali_exynos_dvfs_step {
37         unsigned int    rate;
38         unsigned int    downthreshold;
39         unsigned int    upthreshold;
40 };
41
42 struct mali_exynos_drvdata {
43         struct device                           *dev;
44
45         const struct mali_exynos_dvfs_step      *steps;
46         unsigned int                            nr_steps;
47
48         struct clk                              *mpll;
49         struct clk                              *parent;
50         struct clk                              *sclk;
51
52         mali_power_mode                         power_mode;
53         unsigned int                            dvfs_step;
54         unsigned int                            load;
55
56         struct work_struct                      dvfs_work;
57         struct workqueue_struct                 *dvfs_workqueue;
58 };
59
60 static struct mali_exynos_drvdata *mali;
61
62 #define MALI_DVFS_STEP(freq, down, up) \
63         {freq * 1000000, (256 * down) / 100, (256 * up) / 100}
64
65 static const struct mali_exynos_dvfs_step mali_exynos_dvfs_step_tbl[] = {
66         MALI_DVFS_STEP(134, 0, 100),
67         MALI_DVFS_STEP(134, 0, 100)
68 };
69
70 /* PegaW1 */
71 int mali_gpu_clk;
72 unsigned int mali_dvfs_utilization;
73
74 /* export GPU frequency as a read-only parameter so that it can be read in /sys */
75 module_param(mali_gpu_clk, int, S_IRUSR | S_IRGRP | S_IROTH);
76 MODULE_PARM_DESC(mali_gpu_clk, "Mali Current Clock");
77
78 #ifdef CONFIG_MALI400_PROFILING
79 static inline void _mali_osk_profiling_add_gpufreq_event(int rate)
80 {
81         _mali_osk_profiling_add_event(MALI_PROFILING_EVENT_TYPE_SINGLE |
82                  MALI_PROFILING_EVENT_CHANNEL_GPU |
83                  MALI_PROFILING_EVENT_REASON_SINGLE_GPU_FREQ_VOLT_CHANGE,
84                  rate, 0, 0, 0, 0);
85 }
86 #else
87 static inline void _mali_osk_profiling_add_gpufreq_event(int rate)
88 {
89 }
90 #endif
91
92 static void mali_exynos_set_dvfs_step(struct mali_exynos_drvdata *mali,
93                                                         unsigned int step)
94 {
95         const struct mali_exynos_dvfs_step *next = &mali->steps[step];
96
97         if (mali->dvfs_step == step)
98                 return;
99
100         clk_set_rate(mali->sclk, next->rate);
101
102         mali_gpu_clk = (int)(clk_get_rate(mali->sclk) / 1000000);
103
104         _mali_osk_profiling_add_gpufreq_event(mali_gpu_clk);
105
106         mali->dvfs_step = step;
107 }
108
109 static void mali_exynos_dvfs_work(struct work_struct *work)
110 {
111         struct mali_exynos_drvdata *mali = container_of(work,
112                                         struct mali_exynos_drvdata, dvfs_work);
113         unsigned int step = mali->dvfs_step;
114         const struct mali_exynos_dvfs_step *cur = &mali->steps[step];
115
116         if (mali->load > cur->upthreshold)
117                 ++step;
118         else if (mali->load < cur->downthreshold)
119                 --step;
120
121         BUG_ON(step >= mali->nr_steps);
122
123         if (step != mali->dvfs_step)
124                 mali_exynos_set_dvfs_step(mali, step);
125 }
126
127 void mali_exynos_update_dvfs(struct mali_gpu_utilization_data *data)
128 {
129         mali->load = data->utilization_gpu;
130         mali_dvfs_utilization = data->utilization_gpu;
131
132         queue_work(mali->dvfs_workqueue, &mali->dvfs_work);
133 }
134
135 _mali_osk_errcode_t mali_platform_power_mode_change(struct device *dev,
136                                                 mali_power_mode power_mode)
137 {
138         if (mali->power_mode == power_mode)
139                 MALI_SUCCESS;
140         /* to avoid multiple clk_disable() call */
141         else if ((mali->power_mode > MALI_POWER_MODE_ON) &&
142                                         (power_mode > MALI_POWER_MODE_ON)) {
143                 mali->power_mode = power_mode;
144                 MALI_SUCCESS;
145         }
146
147         switch (power_mode) {
148         case MALI_POWER_MODE_ON:
149                 mali_exynos_set_dvfs_step(mali, 1);
150                 clk_enable(mali->sclk);
151                 break;
152         case MALI_POWER_MODE_DEEP_SLEEP:
153         case MALI_POWER_MODE_LIGHT_SLEEP:
154                 clk_disable(mali->sclk);
155                 mali_exynos_set_dvfs_step(mali, 0);
156                 mali_gpu_clk = 0;
157                 break;
158         }
159
160         mali->power_mode = power_mode;
161
162         MALI_SUCCESS;
163 }
164
165 static mali_bool mali_clk_get(struct mali_exynos_drvdata *mali)
166 {
167         mali->mpll = clk_get(NULL, MPLLCLK_NAME);
168         if (IS_ERR(mali->mpll)) {
169                 MALI_PRINT_ERROR(("failed to get sclk_mpll_pre_div clock"));
170                 return MALI_FALSE;
171         }
172
173         mali->parent = clk_get(NULL, MOUT0_NAME);
174         if (IS_ERR(mali->parent)) {
175                 MALI_PRINT_ERROR(("failed to get parent_clock"));
176                 return MALI_FALSE;
177         }
178
179         mali->sclk = clk_get(NULL, GPUCLK_NAME);
180         if (IS_ERR(mali->sclk)) {
181                 MALI_PRINT_ERROR(("failed to get sclk_clock"));
182                 return MALI_FALSE;
183         }
184
185         return MALI_TRUE;
186 }
187
188 static void mali_clk_put(struct mali_exynos_drvdata *mali,
189                                                 mali_bool binc_mali_clock)
190 {
191         if (mali->parent) {
192                 clk_put(mali->parent);
193                 mali->parent = NULL;
194         }
195
196         if (mali->mpll) {
197                 clk_put(mali->mpll);
198                 mali->mpll = NULL;
199         }
200
201         if (binc_mali_clock && mali->sclk) {
202                 clk_put(mali->sclk);
203                 mali->sclk = NULL;
204         }
205 }
206
207 _mali_osk_errcode_t mali_platform_init(struct device *dev)
208 {
209         mali = kzalloc(sizeof(*mali), GFP_KERNEL);
210         if (WARN_ON(!mali))
211                 MALI_ERROR(_MALI_OSK_ERR_NOMEM);
212
213         mali->steps = mali_exynos_dvfs_step_tbl;
214         mali->nr_steps = ARRAY_SIZE(mali_exynos_dvfs_step_tbl);
215
216         if (!mali_clk_get(mali)) {
217                 MALI_PRINT_ERROR(("Failed to get Mali clocks"));
218                 goto err_clk_put;
219         }
220
221         clk_set_parent(mali->parent, mali->mpll);
222         clk_set_parent(mali->sclk, mali->parent);
223
224         mali->dvfs_workqueue = create_singlethread_workqueue("mali_dvfs");
225         if (WARN_ON(!mali->dvfs_workqueue)) {
226                 MALI_PRINT_ERROR(("failed to create workqueue"));
227                 goto err_clk_put;
228         }
229
230         mali->power_mode = MALI_POWER_MODE_DEEP_SLEEP;
231
232         INIT_WORK(&mali->dvfs_work, mali_exynos_dvfs_work);
233
234         mali_exynos_set_dvfs_step(mali, 1);
235
236         mali_clk_put(mali, MALI_FALSE);
237
238         MALI_SUCCESS;
239
240 err_clk_put:
241         mali_clk_put(mali, MALI_TRUE);
242         kfree(mali);
243         MALI_ERROR(_MALI_OSK_ERR_FAULT);
244 }
245
246 _mali_osk_errcode_t mali_platform_deinit(struct device *dev)
247 {
248         mali_clk_put(mali, MALI_TRUE);
249
250         kfree(mali);
251
252         MALI_SUCCESS;
253 }