1 /* drivers/gpu/mali400/mali/platform/exynos3250/exynos3_pmm.c
3 * Copyright 2011 by S.LSI. Samsung Electronics Inc.
4 * San#24, Nongseo-Dong, Giheung-Gu, Yongin, Korea
6 * Samsung SoC Mali400 DVFS driver
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.
15 * Platform specific Mali driver functions for the exynos 4XXX based platforms
17 #include "mali_kernel_common.h"
19 #include "exynos3_pmm.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>
28 #ifdef CONFIG_MALI400_PROFILING
29 #include "mali_osk_profiling.h"
32 #define MPLLCLK_NAME "sclk_mpll_pre_div"
33 #define MOUT0_NAME "mout_g3d0"
34 #define GPUCLK_NAME "sclk_g3d"
36 struct mali_exynos_dvfs_step {
38 unsigned int downthreshold;
39 unsigned int upthreshold;
42 struct mali_exynos_drvdata {
45 const struct mali_exynos_dvfs_step *steps;
46 unsigned int nr_steps;
52 mali_power_mode power_mode;
53 unsigned int dvfs_step;
56 struct work_struct dvfs_work;
57 struct workqueue_struct *dvfs_workqueue;
60 static struct mali_exynos_drvdata *mali;
62 #define MALI_DVFS_STEP(freq, down, up) \
63 {freq * 1000000, (256 * down) / 100, (256 * up) / 100}
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)
72 unsigned int mali_dvfs_utilization;
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");
78 #ifdef CONFIG_MALI400_PROFILING
79 static inline void _mali_osk_profiling_add_gpufreq_event(int rate)
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,
87 static inline void _mali_osk_profiling_add_gpufreq_event(int rate)
92 static void mali_exynos_set_dvfs_step(struct mali_exynos_drvdata *mali,
95 const struct mali_exynos_dvfs_step *next = &mali->steps[step];
97 if (mali->dvfs_step == step)
100 clk_set_rate(mali->sclk, next->rate);
102 mali_gpu_clk = (int)(clk_get_rate(mali->sclk) / 1000000);
104 _mali_osk_profiling_add_gpufreq_event(mali_gpu_clk);
106 mali->dvfs_step = step;
109 static void mali_exynos_dvfs_work(struct work_struct *work)
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];
116 if (mali->load > cur->upthreshold)
118 else if (mali->load < cur->downthreshold)
121 BUG_ON(step >= mali->nr_steps);
123 if (step != mali->dvfs_step)
124 mali_exynos_set_dvfs_step(mali, step);
127 void mali_exynos_update_dvfs(struct mali_gpu_utilization_data *data)
129 mali->load = data->utilization_gpu;
130 mali_dvfs_utilization = data->utilization_gpu;
132 queue_work(mali->dvfs_workqueue, &mali->dvfs_work);
135 _mali_osk_errcode_t mali_platform_power_mode_change(struct device *dev,
136 mali_power_mode power_mode)
138 if (mali->power_mode == power_mode)
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;
147 switch (power_mode) {
148 case MALI_POWER_MODE_ON:
149 mali_exynos_set_dvfs_step(mali, 1);
150 clk_enable(mali->sclk);
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);
160 mali->power_mode = power_mode;
165 static mali_bool mali_clk_get(struct mali_exynos_drvdata *mali)
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"));
173 mali->parent = clk_get(NULL, MOUT0_NAME);
174 if (IS_ERR(mali->parent)) {
175 MALI_PRINT_ERROR(("failed to get parent_clock"));
179 mali->sclk = clk_get(NULL, GPUCLK_NAME);
180 if (IS_ERR(mali->sclk)) {
181 MALI_PRINT_ERROR(("failed to get sclk_clock"));
188 static void mali_clk_put(struct mali_exynos_drvdata *mali,
189 mali_bool binc_mali_clock)
192 clk_put(mali->parent);
201 if (binc_mali_clock && mali->sclk) {
207 _mali_osk_errcode_t mali_platform_init(struct device *dev)
209 mali = kzalloc(sizeof(*mali), GFP_KERNEL);
211 MALI_ERROR(_MALI_OSK_ERR_NOMEM);
213 mali->steps = mali_exynos_dvfs_step_tbl;
214 mali->nr_steps = ARRAY_SIZE(mali_exynos_dvfs_step_tbl);
216 if (!mali_clk_get(mali)) {
217 MALI_PRINT_ERROR(("Failed to get Mali clocks"));
221 clk_set_parent(mali->parent, mali->mpll);
222 clk_set_parent(mali->sclk, mali->parent);
224 mali->dvfs_workqueue = create_singlethread_workqueue("mali_dvfs");
225 if (WARN_ON(!mali->dvfs_workqueue)) {
226 MALI_PRINT_ERROR(("failed to create workqueue"));
230 mali->power_mode = MALI_POWER_MODE_DEEP_SLEEP;
232 INIT_WORK(&mali->dvfs_work, mali_exynos_dvfs_work);
234 mali_exynos_set_dvfs_step(mali, 1);
236 mali_clk_put(mali, MALI_FALSE);
241 mali_clk_put(mali, MALI_TRUE);
243 MALI_ERROR(_MALI_OSK_ERR_FAULT);
246 _mali_osk_errcode_t mali_platform_deinit(struct device *dev)
248 mali_clk_put(mali, MALI_TRUE);