tizen 2.4 release
[kernel/linux-3.0.git] / drivers / gpu / arm / mali400 / mali / platform / exynos4 / exynos4.c
1 /*
2  * Mali400 platform glue for Samsung Exynos 4 SoCs
3  *
4  * Copyright 2013 by Samsung Electronics Co., Ltd.
5  * Author: Tomasz Figa <t.figa@samsung.com>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License version 2 as
9  * published by the Free Software FoundatIon.
10  */
11
12 #include <linux/clk.h>
13 #include <linux/of.h>
14 #include <linux/mali/mali_utgard.h>
15 #include <linux/platform_device.h>
16 #include <linux/pm.h>
17 #include <linux/pm_runtime.h>
18 #include <linux/regulator/consumer.h>
19 #include <linux/slab.h>
20 #include <linux/workqueue.h>
21
22 #include "mali_kernel_common.h"
23 #include "mali_osk.h"
24
25 #ifdef CONFIG_MALI400_PROFILING
26 #include "mali_osk_profiling.h"
27 #endif
28
29 #include "exynos4.h"
30
31 struct mali_exynos_variant {
32         const struct mali_exynos_dvfs_step *steps;
33         unsigned int nr_steps;
34         unsigned int has_smmuclk;
35 };
36
37 struct mali_exynos_dvfs_step {
38         unsigned int rate;
39         unsigned int voltage;
40         unsigned int downthreshold;
41         unsigned int upthreshold;
42 };
43
44 struct mali_exynos_drvdata {
45         struct device *dev;
46
47         const struct mali_exynos_dvfs_step *steps;
48         unsigned int nr_steps;
49         unsigned int has_smmuclk;
50
51         struct clk *pll;
52         struct clk *mux1;
53         struct clk *mux2;
54         struct clk *sclk;
55         struct clk *smmu;
56         struct clk *g3d;
57
58         struct regulator *vdd_g3d;
59
60         mali_power_mode power_mode;
61         unsigned int dvfs_step;
62         unsigned int load;
63
64         struct workqueue_struct *dvfs_workqueue;
65         struct work_struct dvfs_work;
66 };
67
68 extern struct platform_device *mali_platform_device;
69
70 static struct mali_exynos_drvdata *mali;
71
72 /*
73  * DVFS tables
74  */
75
76 #define MALI_DVFS_STEP(freq, voltage, down, up) \
77         {freq, voltage, (256 * down) / 100, (256 * up) / 100}
78
79 static const struct mali_exynos_dvfs_step mali_exynos_dvfs_step_3250[] = {
80         MALI_DVFS_STEP(134,       0,  0, 100)
81 };
82
83 static const struct mali_exynos_dvfs_step mali_exynos_dvfs_step_4210[] = {
84         MALI_DVFS_STEP(160,  950000,  0,  90),
85         MALI_DVFS_STEP(266, 1050000, 85, 100)
86 };
87
88 static const struct mali_exynos_dvfs_step mali_exynos_dvfs_step_4x12[] = {
89         MALI_DVFS_STEP(160,  875000,  0,  70),
90         MALI_DVFS_STEP(266,  900000, 62,  90),
91         MALI_DVFS_STEP(350,  950000, 85,  90),
92         MALI_DVFS_STEP(440, 1025000, 85, 100)
93 };
94
95 static const struct mali_exynos_dvfs_step mali_exynos_dvfs_step_4x12_prime[] = {
96         MALI_DVFS_STEP(160,  875000,  0,  70),
97         MALI_DVFS_STEP(266,  900000, 62,  90),
98         MALI_DVFS_STEP(350,  950000, 85,  90),
99         MALI_DVFS_STEP(440, 1025000, 85,  90),
100         MALI_DVFS_STEP(533, 1075000, 95, 100)
101 };
102
103 /*
104  * Variants
105  */
106
107 static const struct mali_exynos_variant mali_variant_3250= {
108         .steps = mali_exynos_dvfs_step_3250,
109         .nr_steps = ARRAY_SIZE(mali_exynos_dvfs_step_3250),
110         .has_smmuclk = true,
111 };
112
113 static const struct mali_exynos_variant mali_variant_4210 = {
114         .steps = mali_exynos_dvfs_step_4210,
115         .nr_steps = ARRAY_SIZE(mali_exynos_dvfs_step_4210),
116 };
117
118 static const struct mali_exynos_variant mali_variant_4x12 = {
119         .steps = mali_exynos_dvfs_step_4x12,
120         .nr_steps = ARRAY_SIZE(mali_exynos_dvfs_step_4x12),
121 };
122
123 static const struct mali_exynos_variant mali_variant_4x12_prime = {
124         .steps = mali_exynos_dvfs_step_4x12_prime,
125         .nr_steps = ARRAY_SIZE(mali_exynos_dvfs_step_4x12_prime),
126 };
127
128 const struct of_device_id mali_of_matches[] = {
129         { .compatible = "samsung,exynos3250-g3d",
130                                         .data = &mali_variant_3250, },
131         { .compatible = "samsung,exynos4210-g3d",
132                                         .data = &mali_variant_4210, },
133         { .compatible = "samsung,exynos4x12-g3d",
134                                         .data = &mali_variant_4x12, },
135         { .compatible = "samsung,exynos4x12-prime-g3d",
136                                         .data = &mali_variant_4x12_prime, },
137         { /* Sentinel */ }
138 };
139
140 #ifdef CONFIG_MALI400_PROFILING
141 static inline void _mali_osk_profiling_add_gpufreq_event(int rate, int vol)
142 {
143         _mali_osk_profiling_add_event(MALI_PROFILING_EVENT_TYPE_SINGLE |
144                  MALI_PROFILING_EVENT_CHANNEL_GPU |
145                  MALI_PROFILING_EVENT_REASON_SINGLE_GPU_FREQ_VOLT_CHANGE,
146                  rate, vol, 0, 0, 0);
147 }
148 #else
149 static inline void _mali_osk_profiling_add_gpufreq_event(int rate, int vol)
150 {
151 }
152 #endif
153
154 /*
155  * DVFS control
156  */
157
158 static void mali_exynos_set_dvfs_step(struct mali_exynos_drvdata *mali,
159                                                         unsigned int step)
160 {
161         const struct mali_exynos_dvfs_step *next = &mali->steps[step];
162
163         if (step <= mali->dvfs_step)
164                 clk_set_rate(mali->sclk, next->rate * 1000000);
165
166         regulator_set_voltage(mali->vdd_g3d,
167                                         next->voltage, next->voltage);
168
169         if (step > mali->dvfs_step)
170                 clk_set_rate(mali->sclk, next->rate * 1000000);
171
172         _mali_osk_profiling_add_gpufreq_event(next->rate * 1000000,
173                  regulator_get_voltage(mali->vdd_g3d) / 1000);
174         mali->dvfs_step = step;
175 }
176
177 static void exynos_dvfs_work(struct work_struct *work)
178 {
179         struct mali_exynos_drvdata *mali = container_of(work,
180                                         struct mali_exynos_drvdata, dvfs_work);
181         unsigned int step = mali->dvfs_step;
182         const struct mali_exynos_dvfs_step *cur = &mali->steps[step];
183
184         if (mali->load > cur->upthreshold)
185                 ++step;
186         else if (mali->load < cur->downthreshold)
187                 --step;
188
189         BUG_ON(step >= mali->nr_steps);
190
191         if (step != mali->dvfs_step)
192                 mali_exynos_set_dvfs_step(mali, step);
193 }
194
195 static void exynos_update_dvfs(unsigned int load)
196 {
197         if (load > 255)
198                 load = 255;
199
200         mali->load = load;
201
202         queue_work(mali->dvfs_workqueue, &mali->dvfs_work);
203 }
204
205 /*
206  * Power management
207  */
208
209 _mali_osk_errcode_t mali_platform_power_mode_change(mali_power_mode power_mode)
210 {
211         if (WARN_ON(mali->power_mode == power_mode))
212                 MALI_SUCCESS;
213
214         switch (power_mode) {
215         case MALI_POWER_MODE_ON:
216                 mali_exynos_set_dvfs_step(mali, 0);
217                 clk_prepare_enable(mali->g3d);
218                 clk_prepare_enable(mali->sclk);
219                 if (mali->has_smmuclk)
220                         clk_prepare_enable(mali->smmu);
221                 break;
222
223         case MALI_POWER_MODE_LIGHT_SLEEP:
224         case MALI_POWER_MODE_DEEP_SLEEP:
225                 if (mali->has_smmuclk)
226                         clk_disable_unprepare(mali->smmu);
227                 clk_disable_unprepare(mali->sclk);
228                 clk_disable_unprepare(mali->g3d);
229                 _mali_osk_profiling_add_gpufreq_event(0, 0);
230                 break;
231         }
232
233         mali->power_mode = power_mode;
234
235         MALI_SUCCESS;
236 }
237
238 /*
239  * Platform-specific initialization/cleanup
240  */
241
242 static struct mali_gpu_device_data mali_exynos_gpu_data = {
243         .shared_mem_size = SZ_256M,
244         .fb_start = 0x40000000,
245         .fb_size = 0xb1000000,
246         .utilization_interval = 100, /* 100ms in Tizen */
247         .utilization_handler = exynos_update_dvfs,
248 };
249
250 _mali_osk_errcode_t mali_platform_init(void)
251 {
252         struct platform_device *pdev = mali_platform_device;
253         const struct mali_exynos_variant *variant;
254         const struct of_device_id *match;
255         struct resource *old_res, *new_res;
256         unsigned int i, irq_res, mem_res;
257         struct device_node *np;
258
259         if (WARN_ON(!pdev))
260                 return -ENODEV;
261
262         MALI_DEBUG_PRINT(4, ("mali_platform_device_register() called\n"));
263
264         pdev->dev.platform_data = &mali_exynos_gpu_data;
265
266         np = pdev->dev.of_node;
267         if (WARN_ON(!np))
268                 return -ENODEV;
269
270         match = of_match_node(mali_of_matches, np);
271         if (WARN_ON(!match))
272                 return -ENODEV;
273
274         variant = match->data;
275
276         old_res = pdev->resource;
277         new_res = kzalloc(sizeof(*new_res) * pdev->num_resources, GFP_KERNEL);
278         if (WARN_ON(!new_res))
279                 return -ENOMEM;
280
281         /* Copy first resource */
282         memcpy(new_res, old_res++, sizeof(*new_res));
283
284         /* Rearrange next resources */
285         irq_res = 0;
286         mem_res = 0;
287         for (i = 1; i < pdev->num_resources; ++i, ++old_res) {
288                 if (resource_type(old_res) == IORESOURCE_MEM)
289                         memcpy(&new_res[1 + 2 * mem_res++],
290                                                 old_res, sizeof(*old_res));
291                 else if (resource_type(old_res) == IORESOURCE_IRQ)
292                         memcpy(&new_res[2 + 2 * irq_res++],
293                                                 old_res, sizeof(*old_res));
294         }
295
296         kfree(pdev->resource);
297         pdev->resource = new_res;
298
299         mali = devm_kzalloc(&pdev->dev, sizeof(*mali), GFP_KERNEL);
300         if (WARN_ON(!mali))
301                 return -ENOMEM;
302
303         mali->dev = &pdev->dev;
304         mali->steps = variant->steps;
305         mali->nr_steps = variant->nr_steps;
306         mali->has_smmuclk = variant->has_smmuclk;
307
308         mali->pll = devm_clk_get(mali->dev, "pll");
309         if (WARN_ON(IS_ERR(mali->pll)))
310                 return PTR_ERR(mali->pll);
311
312         mali->mux1 = devm_clk_get(mali->dev, "mux1");
313         if (WARN_ON(IS_ERR(mali->mux1)))
314                 return PTR_ERR(mali->mux1);
315
316         mali->mux2 = devm_clk_get(mali->dev, "mux2");
317         if (WARN_ON(IS_ERR(mali->mux2)))
318                 return PTR_ERR(mali->mux2);
319
320         mali->sclk = devm_clk_get(mali->dev, "sclk");
321         if (WARN_ON(IS_ERR(mali->sclk)))
322                 return PTR_ERR(mali->sclk);
323
324         if (mali->has_smmuclk) {
325                 mali->smmu = devm_clk_get(mali->dev, "smmu");
326                 if (WARN_ON(IS_ERR(mali->smmu)))
327                         return PTR_ERR(mali->smmu);
328         }
329
330         mali->g3d = devm_clk_get(mali->dev, "g3d");
331         if (WARN_ON(IS_ERR(mali->g3d)))
332                 return PTR_ERR(mali->g3d);
333
334         mali->vdd_g3d = devm_regulator_get(mali->dev, "vdd_g3d");
335         if (WARN_ON(IS_ERR(mali->vdd_g3d)))
336                 return PTR_ERR(mali->vdd_g3d);
337
338         mali->dvfs_workqueue = create_singlethread_workqueue("mali_dvfs");
339         if (WARN_ON(!mali->dvfs_workqueue))
340                 return -EFAULT;
341
342         mali->power_mode = MALI_POWER_MODE_LIGHT_SLEEP;
343
344         INIT_WORK(&mali->dvfs_work, exynos_dvfs_work);
345
346         regulator_enable(mali->vdd_g3d);
347
348         clk_set_parent(mali->mux1, mali->pll);
349         clk_set_parent(mali->mux2, mali->mux1);
350         mali_exynos_set_dvfs_step(mali, 0);
351
352         pm_runtime_set_autosuspend_delay(&pdev->dev, 300);
353         pm_runtime_use_autosuspend(&pdev->dev);
354
355         pm_runtime_enable(&pdev->dev);
356
357         MALI_SUCCESS;
358 }
359
360 _mali_osk_errcode_t mali_platform_deinit(void)
361 {
362         struct platform_device *pdev = mali_platform_device;
363
364         pm_runtime_disable(&pdev->dev);
365
366         regulator_disable(mali->vdd_g3d);
367
368         _mali_osk_profiling_add_gpufreq_event(0, 0);
369
370         MALI_SUCCESS;
371 }