2 * Mali400 platform glue for Samsung Exynos 4 SoCs
4 * Copyright 2013 by Samsung Electronics Co., Ltd.
5 * Author: Tomasz Figa <t.figa@samsung.com>
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.
12 #include <linux/clk.h>
14 #include <linux/mali/mali_utgard.h>
15 #include <linux/platform_device.h>
17 #include <linux/pm_runtime.h>
18 #include <linux/regulator/consumer.h>
19 #include <linux/slab.h>
20 #include <linux/workqueue.h>
22 #include "mali_kernel_common.h"
25 #ifdef CONFIG_MALI400_PROFILING
26 #include "mali_osk_profiling.h"
31 struct mali_exynos_variant {
32 const struct mali_exynos_dvfs_step *steps;
33 unsigned int nr_steps;
34 unsigned int has_smmuclk;
37 struct mali_exynos_dvfs_step {
40 unsigned int downthreshold;
41 unsigned int upthreshold;
44 struct mali_exynos_drvdata {
47 const struct mali_exynos_dvfs_step *steps;
48 unsigned int nr_steps;
49 unsigned int has_smmuclk;
58 struct regulator *vdd_g3d;
60 mali_power_mode power_mode;
61 unsigned int dvfs_step;
64 struct workqueue_struct *dvfs_workqueue;
65 struct work_struct dvfs_work;
68 extern struct platform_device *mali_platform_device;
70 static struct mali_exynos_drvdata *mali;
76 #define MALI_DVFS_STEP(freq, voltage, down, up) \
77 {freq, voltage, (256 * down) / 100, (256 * up) / 100}
79 static const struct mali_exynos_dvfs_step mali_exynos_dvfs_step_3250[] = {
80 MALI_DVFS_STEP(134, 0, 0, 100)
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)
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)
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)
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),
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),
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),
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),
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, },
140 #ifdef CONFIG_MALI400_PROFILING
141 static inline void _mali_osk_profiling_add_gpufreq_event(int rate, int vol)
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,
149 static inline void _mali_osk_profiling_add_gpufreq_event(int rate, int vol)
158 static void mali_exynos_set_dvfs_step(struct mali_exynos_drvdata *mali,
161 const struct mali_exynos_dvfs_step *next = &mali->steps[step];
163 if (step <= mali->dvfs_step)
164 clk_set_rate(mali->sclk, next->rate * 1000000);
166 regulator_set_voltage(mali->vdd_g3d,
167 next->voltage, next->voltage);
169 if (step > mali->dvfs_step)
170 clk_set_rate(mali->sclk, next->rate * 1000000);
172 _mali_osk_profiling_add_gpufreq_event(next->rate * 1000000,
173 regulator_get_voltage(mali->vdd_g3d) / 1000);
174 mali->dvfs_step = step;
177 static void exynos_dvfs_work(struct work_struct *work)
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];
184 if (mali->load > cur->upthreshold)
186 else if (mali->load < cur->downthreshold)
189 BUG_ON(step >= mali->nr_steps);
191 if (step != mali->dvfs_step)
192 mali_exynos_set_dvfs_step(mali, step);
195 static void exynos_update_dvfs(unsigned int load)
202 queue_work(mali->dvfs_workqueue, &mali->dvfs_work);
209 _mali_osk_errcode_t mali_platform_power_mode_change(mali_power_mode power_mode)
211 if (WARN_ON(mali->power_mode == power_mode))
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);
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);
233 mali->power_mode = power_mode;
239 * Platform-specific initialization/cleanup
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,
250 _mali_osk_errcode_t mali_platform_init(void)
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;
262 MALI_DEBUG_PRINT(4, ("mali_platform_device_register() called\n"));
264 pdev->dev.platform_data = &mali_exynos_gpu_data;
266 np = pdev->dev.of_node;
270 match = of_match_node(mali_of_matches, np);
274 variant = match->data;
276 old_res = pdev->resource;
277 new_res = kzalloc(sizeof(*new_res) * pdev->num_resources, GFP_KERNEL);
278 if (WARN_ON(!new_res))
281 /* Copy first resource */
282 memcpy(new_res, old_res++, sizeof(*new_res));
284 /* Rearrange next resources */
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));
296 kfree(pdev->resource);
297 pdev->resource = new_res;
299 mali = devm_kzalloc(&pdev->dev, sizeof(*mali), GFP_KERNEL);
303 mali->dev = &pdev->dev;
304 mali->steps = variant->steps;
305 mali->nr_steps = variant->nr_steps;
306 mali->has_smmuclk = variant->has_smmuclk;
308 mali->pll = devm_clk_get(mali->dev, "pll");
309 if (WARN_ON(IS_ERR(mali->pll)))
310 return PTR_ERR(mali->pll);
312 mali->mux1 = devm_clk_get(mali->dev, "mux1");
313 if (WARN_ON(IS_ERR(mali->mux1)))
314 return PTR_ERR(mali->mux1);
316 mali->mux2 = devm_clk_get(mali->dev, "mux2");
317 if (WARN_ON(IS_ERR(mali->mux2)))
318 return PTR_ERR(mali->mux2);
320 mali->sclk = devm_clk_get(mali->dev, "sclk");
321 if (WARN_ON(IS_ERR(mali->sclk)))
322 return PTR_ERR(mali->sclk);
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);
330 mali->g3d = devm_clk_get(mali->dev, "g3d");
331 if (WARN_ON(IS_ERR(mali->g3d)))
332 return PTR_ERR(mali->g3d);
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);
338 mali->dvfs_workqueue = create_singlethread_workqueue("mali_dvfs");
339 if (WARN_ON(!mali->dvfs_workqueue))
342 mali->power_mode = MALI_POWER_MODE_LIGHT_SLEEP;
344 INIT_WORK(&mali->dvfs_work, exynos_dvfs_work);
346 regulator_enable(mali->vdd_g3d);
348 clk_set_parent(mali->mux1, mali->pll);
349 clk_set_parent(mali->mux2, mali->mux1);
350 mali_exynos_set_dvfs_step(mali, 0);
352 pm_runtime_set_autosuspend_delay(&pdev->dev, 300);
353 pm_runtime_use_autosuspend(&pdev->dev);
355 pm_runtime_enable(&pdev->dev);
360 _mali_osk_errcode_t mali_platform_deinit(void)
362 struct platform_device *pdev = mali_platform_device;
364 pm_runtime_disable(&pdev->dev);
366 regulator_disable(mali->vdd_g3d);
368 _mali_osk_profiling_add_gpufreq_event(0, 0);