ARM: tizen_odroid_defconfig: Change the default cpufreq governor
[platform/kernel/linux-exynos.git] / drivers / gpu / arm / mali400 / r5p2_rel0 / platform / exynos / exynos.c
1 /*
2  * Mali400 platform glue for Samsung Exynos 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 "exynos.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(struct mali_gpu_utilization_data *data)
196 {
197         if (data->utilization_gpu > 255)
198                 data->utilization_gpu = 255;
199
200         mali->load = data->utilization_gpu;
201
202         queue_work(mali->dvfs_workqueue, &mali->dvfs_work);
203 }
204
205 /*
206  * Power management
207  */
208
209 void mali_platform_power_mode_change(int power_mode)
210 {
211         if (WARN_ON(mali->power_mode == power_mode))
212                 return;
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
236 bool mali_is_on(void)
237 {
238         if (mali->power_mode == MALI_POWER_MODE_ON)
239                 return true;
240         else
241                 return false;
242 }
243
244 /*
245  * Platform-specific initialization/cleanup
246  */
247
248 static struct mali_gpu_device_data mali_exynos_gpu_data = {
249         .shared_mem_size = SZ_256M,
250         .fb_start = 0x40000000,
251         .fb_size = 0xb1000000,
252         .control_interval = 100, /* 100ms in Tizen */
253         .utilization_callback = exynos_update_dvfs,
254 };
255
256 int mali_platform_device_init(struct platform_device *pdev)
257 {
258         const struct mali_exynos_variant *variant;
259         const struct of_device_id *match;
260         struct resource *old_res, *new_res;
261         unsigned int i, irq_res, mem_res;
262         struct device_node *np;
263         int ret;
264
265         if (WARN_ON(!pdev))
266                 return -ENODEV;
267
268         MALI_DEBUG_PRINT(4, ("mali_platform_device_register() called\n"));
269
270         pdev->dev.platform_data = &mali_exynos_gpu_data;
271
272         np = pdev->dev.of_node;
273         if (WARN_ON(!np))
274                 return -ENODEV;
275
276         match = of_match_node(mali_of_matches, np);
277         if (WARN_ON(!match))
278                 return -ENODEV;
279
280         variant = match->data;
281
282         old_res = pdev->resource;
283         new_res = kzalloc(sizeof(*new_res) * pdev->num_resources, GFP_KERNEL);
284         if (WARN_ON(!new_res))
285                 return -ENOMEM;
286
287         /* Rearrange next resources */
288         irq_res = 0;
289         mem_res = 0;
290         for (i = 0; i < pdev->num_resources; ++i, ++old_res) {
291                 if (resource_type(old_res) == IORESOURCE_MEM)
292                         memcpy(&new_res[2 * mem_res++],
293                                                 old_res, sizeof(*old_res));
294                 else if (resource_type(old_res) == IORESOURCE_IRQ)
295                         memcpy(&new_res[1 + 2 * irq_res++],
296                                                 old_res, sizeof(*old_res));
297         }
298
299         kfree(pdev->resource);
300         pdev->resource = new_res;
301
302         mali = devm_kzalloc(&pdev->dev, sizeof(*mali), GFP_KERNEL);
303         if (WARN_ON(!mali))
304                 return -ENOMEM;
305
306         mali->dev = &pdev->dev;
307         mali->steps = variant->steps;
308         mali->nr_steps = variant->nr_steps;
309         mali->has_smmuclk = variant->has_smmuclk;
310
311         mali->pll = devm_clk_get(mali->dev, "pll");
312         if (WARN_ON(IS_ERR(mali->pll)))
313                 return PTR_ERR(mali->pll);
314
315         mali->mux1 = devm_clk_get(mali->dev, "mux1");
316         if (WARN_ON(IS_ERR(mali->mux1)))
317                 return PTR_ERR(mali->mux1);
318
319         mali->mux2 = devm_clk_get(mali->dev, "mux2");
320         if (WARN_ON(IS_ERR(mali->mux2)))
321                 return PTR_ERR(mali->mux2);
322
323         mali->sclk = devm_clk_get(mali->dev, "sclk");
324         if (WARN_ON(IS_ERR(mali->sclk)))
325                 return PTR_ERR(mali->sclk);
326
327         if (mali->has_smmuclk) {
328                 mali->smmu = devm_clk_get(mali->dev, "smmu");
329                 if (WARN_ON(IS_ERR(mali->smmu)))
330                         return PTR_ERR(mali->smmu);
331         }
332
333         mali->g3d = devm_clk_get(mali->dev, "g3d");
334         if (WARN_ON(IS_ERR(mali->g3d)))
335                 return PTR_ERR(mali->g3d);
336
337         mali->vdd_g3d = devm_regulator_get(mali->dev, "vdd_g3d");
338         if (WARN_ON(IS_ERR(mali->vdd_g3d)))
339                 return PTR_ERR(mali->vdd_g3d);
340
341         mali->dvfs_workqueue = create_singlethread_workqueue("mali_dvfs");
342         if (WARN_ON(!mali->dvfs_workqueue))
343                 return -EFAULT;
344
345         mali->power_mode = MALI_POWER_MODE_LIGHT_SLEEP;
346
347         INIT_WORK(&mali->dvfs_work, exynos_dvfs_work);
348
349         ret = regulator_enable(mali->vdd_g3d);
350         if (WARN_ON(ret)) {
351                 destroy_workqueue(mali->dvfs_workqueue);
352                 MALI_ERROR(_MALI_OSK_ERR_FAULT);
353         }
354
355         clk_set_parent(mali->mux1, mali->pll);
356         clk_set_parent(mali->mux2, mali->mux1);
357         mali_exynos_set_dvfs_step(mali, 0);
358
359         pm_runtime_set_autosuspend_delay(&pdev->dev, 300);
360         pm_runtime_use_autosuspend(&pdev->dev);
361
362         pm_runtime_enable(&pdev->dev);
363
364         MALI_SUCCESS;
365 }
366
367 int mali_platform_device_deinit(struct platform_device *pdev)
368 {
369         pm_runtime_disable(&pdev->dev);
370
371         regulator_disable(mali->vdd_g3d);
372
373         _mali_osk_profiling_add_gpufreq_event(0, 0);
374
375         MALI_SUCCESS;
376 }