2 * Mali400 platform glue for Samsung Exynos 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(struct mali_gpu_utilization_data *data)
197 if (data->utilization_gpu > 255)
198 data->utilization_gpu = 255;
200 mali->load = data->utilization_gpu;
202 queue_work(mali->dvfs_workqueue, &mali->dvfs_work);
209 void mali_platform_power_mode_change(int 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;
236 bool mali_is_on(void)
238 if (mali->power_mode == MALI_POWER_MODE_ON)
245 * Platform-specific initialization/cleanup
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,
256 int mali_platform_device_init(struct platform_device *pdev)
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;
268 MALI_DEBUG_PRINT(4, ("mali_platform_device_register() called\n"));
270 pdev->dev.platform_data = &mali_exynos_gpu_data;
272 np = pdev->dev.of_node;
276 match = of_match_node(mali_of_matches, np);
280 variant = match->data;
282 old_res = pdev->resource;
283 new_res = kzalloc(sizeof(*new_res) * pdev->num_resources, GFP_KERNEL);
284 if (WARN_ON(!new_res))
287 /* Rearrange next resources */
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));
299 kfree(pdev->resource);
300 pdev->resource = new_res;
302 mali = devm_kzalloc(&pdev->dev, sizeof(*mali), GFP_KERNEL);
306 mali->dev = &pdev->dev;
307 mali->steps = variant->steps;
308 mali->nr_steps = variant->nr_steps;
309 mali->has_smmuclk = variant->has_smmuclk;
311 mali->pll = devm_clk_get(mali->dev, "pll");
312 if (WARN_ON(IS_ERR(mali->pll)))
313 return PTR_ERR(mali->pll);
315 mali->mux1 = devm_clk_get(mali->dev, "mux1");
316 if (WARN_ON(IS_ERR(mali->mux1)))
317 return PTR_ERR(mali->mux1);
319 mali->mux2 = devm_clk_get(mali->dev, "mux2");
320 if (WARN_ON(IS_ERR(mali->mux2)))
321 return PTR_ERR(mali->mux2);
323 mali->sclk = devm_clk_get(mali->dev, "sclk");
324 if (WARN_ON(IS_ERR(mali->sclk)))
325 return PTR_ERR(mali->sclk);
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);
333 mali->g3d = devm_clk_get(mali->dev, "g3d");
334 if (WARN_ON(IS_ERR(mali->g3d)))
335 return PTR_ERR(mali->g3d);
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);
341 mali->dvfs_workqueue = create_singlethread_workqueue("mali_dvfs");
342 if (WARN_ON(!mali->dvfs_workqueue))
345 mali->power_mode = MALI_POWER_MODE_LIGHT_SLEEP;
347 INIT_WORK(&mali->dvfs_work, exynos_dvfs_work);
349 ret = regulator_enable(mali->vdd_g3d);
351 destroy_workqueue(mali->dvfs_workqueue);
352 MALI_ERROR(_MALI_OSK_ERR_FAULT);
355 clk_set_parent(mali->mux1, mali->pll);
356 clk_set_parent(mali->mux2, mali->mux1);
357 mali_exynos_set_dvfs_step(mali, 0);
359 pm_runtime_set_autosuspend_delay(&pdev->dev, 300);
360 pm_runtime_use_autosuspend(&pdev->dev);
362 pm_runtime_enable(&pdev->dev);
367 int mali_platform_device_deinit(struct platform_device *pdev)
369 pm_runtime_disable(&pdev->dev);
371 regulator_disable(mali->vdd_g3d);
373 _mali_osk_profiling_add_gpufreq_event(0, 0);