blob: 86f4eaff00301ffc718d1eee423815ec5dadd372 [file] [log] [blame]
Abhijeet Dharmapurikar7b933c52012-08-23 15:51:58 -07001/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07002 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 */
13
14#include <linux/kernel.h>
15#include <linux/init.h>
16#include <linux/module.h>
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070017#include <linux/kthread.h>
18#include <linux/kobject.h>
19#include <linux/ktime.h>
20#include <linux/hrtimer.h>
21#include <linux/slab.h>
22#include <linux/spinlock.h>
23#include <linux/stringify.h>
24#include <linux/debugfs.h>
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -070025#include <linux/msm_tsens.h>
Steve Mucklea9aac292012-11-02 15:41:00 -070026#include <linux/platform_device.h>
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070027#include <asm/atomic.h>
28#include <asm/page.h>
29#include <mach/msm_dcvs.h>
Abhijeet Dharmapurikar07cf2ff2012-09-13 19:05:13 -070030#include <trace/events/mpdcvs_trace.h>
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070031
32#define CORE_HANDLE_OFFSET (0xA0)
33#define __err(f, ...) pr_err("MSM_DCVS: %s: " f, __func__, __VA_ARGS__)
34#define __info(f, ...) pr_info("MSM_DCVS: %s: " f, __func__, __VA_ARGS__)
35#define MAX_PENDING (5)
36
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070037struct core_attribs {
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070038 struct kobj_attribute freq_change_us;
39
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070040 struct kobj_attribute disable_pc_threshold;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -070041 struct kobj_attribute em_win_size_min_us;
42 struct kobj_attribute em_win_size_max_us;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070043 struct kobj_attribute em_max_util_pct;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -070044 struct kobj_attribute group_id;
45 struct kobj_attribute max_freq_chg_time_us;
46 struct kobj_attribute slack_mode_dynamic;
47 struct kobj_attribute slack_time_min_us;
48 struct kobj_attribute slack_time_max_us;
49 struct kobj_attribute slack_weight_thresh_pct;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070050 struct kobj_attribute ss_iobusy_conv;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -070051 struct kobj_attribute ss_win_size_min_us;
52 struct kobj_attribute ss_win_size_max_us;
53 struct kobj_attribute ss_util_pct;
54
55 struct kobj_attribute active_coeff_a;
56 struct kobj_attribute active_coeff_b;
57 struct kobj_attribute active_coeff_c;
58 struct kobj_attribute leakage_coeff_a;
59 struct kobj_attribute leakage_coeff_b;
60 struct kobj_attribute leakage_coeff_c;
61 struct kobj_attribute leakage_coeff_d;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070062
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -070063 struct kobj_attribute thermal_poll_ms;
64
Steve Muckle118f47b2012-10-17 16:09:37 -070065 struct kobj_attribute freq_tbl;
66
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070067 struct attribute_group attrib_group;
68};
69
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -070070enum pending_freq_state {
71 /*
72 * used by the thread to check if pending_freq was updated while it was
73 * setting previous frequency - this is written to and used by the
74 * freq updating thread
75 */
76 NO_OUTSTANDING_FREQ_CHANGE = 0,
77
78 /*
79 * This request is set to indicate that the governor is stopped and no
80 * more frequency change requests are accepted untill it starts again.
81 * This is checked/used by the threads that want to change the freq
82 */
83 STOP_FREQ_CHANGE = -1,
84
85 /*
86 * Any other +ve value means that a freq change was requested and the
87 * thread has not gotten around to update it
88 *
89 * Any other -ve value means that this is the last freq change i.e. a
90 * freq change was requested but the thread has not run yet and
91 * meanwhile the governor was stopped.
92 */
93};
94
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070095struct dcvs_core {
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -070096 spinlock_t idle_state_change_lock;
97 /* 0 when not idle (busy) 1 when idle and -1 when governor starts and
98 * we dont know whether the next call is going to be idle enter or exit
99 */
100 int idle_entered;
101
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700102 enum msm_dcvs_core_type type;
103 /* this is the number in each type for example cpu 0,1,2 and gpu 0,1 */
104 int type_core_num;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700105 char core_name[CORE_NAME_MAX];
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700106 uint32_t actual_freq;
107 uint32_t freq_change_us;
108
109 uint32_t max_time_us; /* core param */
110
111 struct msm_dcvs_algo_param algo_param;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700112 struct msm_dcvs_energy_curve_coeffs coeffs;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700113
114 /* private */
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700115 ktime_t time_start;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700116 struct task_struct *task;
117 struct core_attribs attrib;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700118 uint32_t dcvs_core_id;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700119 struct msm_dcvs_core_info *info;
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700120 int sensor;
Abhijeet Dharmapurikarbbb52fe2012-08-31 20:31:16 -0700121 wait_queue_head_t wait_q;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700122
123 int (*set_frequency)(int type_core_num, unsigned int freq);
124 unsigned int (*get_frequency)(int type_core_num);
125 int (*idle_enable)(int type_core_num,
Abhijeet Dharmapurikar6e9b34f2012-09-10 16:03:39 -0700126 enum msm_core_control_event event);
Steve Muckle93bb4252012-11-12 14:20:39 -0800127 int (*set_floor_frequency)(int type_core_num, unsigned int freq);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700128
129 spinlock_t pending_freq_lock;
130 int pending_freq;
131
132 struct hrtimer slack_timer;
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700133 struct delayed_work temperature_work;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700134};
135
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700136static int msm_dcvs_enabled = 1;
137module_param_named(enable, msm_dcvs_enabled, int, S_IRUGO | S_IWUSR | S_IWGRP);
138
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700139static struct dentry *debugfs_base;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700140
141static struct dcvs_core core_list[CORES_MAX];
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700142
143static struct kobject *cores_kobj;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700144
Steve Mucklea9aac292012-11-02 15:41:00 -0700145#define DCVS_MAX_NUM_FREQS 15
146static struct msm_dcvs_freq_entry cpu_freq_tbl[DCVS_MAX_NUM_FREQS];
147static unsigned num_cpu_freqs;
148static struct msm_dcvs_platform_data *dcvs_pdata;
149
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700150static void force_stop_slack_timer(struct dcvs_core *core)
151{
152 unsigned long flags;
153
154 spin_lock_irqsave(&core->idle_state_change_lock, flags);
155 hrtimer_cancel(&core->slack_timer);
156 spin_unlock_irqrestore(&core->idle_state_change_lock, flags);
157}
158
Abhijeet Dharmapurikar8d843b32012-09-13 18:33:20 -0700159static void force_start_slack_timer(struct dcvs_core *core, int slack_us)
160{
161 unsigned long flags;
162 int ret;
163
164 spin_lock_irqsave(&core->idle_state_change_lock, flags);
165
166 /*
167 * only start the timer if governor is not stopped
168 */
169 if (slack_us != 0) {
170 ret = hrtimer_start(&core->slack_timer,
171 ktime_set(0, slack_us * 1000),
172 HRTIMER_MODE_REL_PINNED);
173 if (ret) {
174 pr_err("%s Failed to start timer ret = %d\n",
175 core->core_name, ret);
176 }
177 }
178
179 spin_unlock_irqrestore(&core->idle_state_change_lock, flags);
180}
181
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700182static void stop_slack_timer(struct dcvs_core *core)
183{
184 unsigned long flags;
185
186 spin_lock_irqsave(&core->idle_state_change_lock, flags);
187 /* err only for cpu type's GPU's can do idle exit consecutively */
188 if (core->idle_entered == 1 && !(core->dcvs_core_id >= GPU_OFFSET))
189 __err("%s trying to reenter idle", core->core_name);
190 core->idle_entered = 1;
191 hrtimer_cancel(&core->slack_timer);
192 core->idle_entered = 1;
193 spin_unlock_irqrestore(&core->idle_state_change_lock, flags);
194}
195
196static void start_slack_timer(struct dcvs_core *core, int slack_us)
197{
198 unsigned long flags1, flags2;
199 int ret;
200
201 spin_lock_irqsave(&core->idle_state_change_lock, flags2);
202
203 spin_lock_irqsave(&core->pending_freq_lock, flags1);
204
205 /* err only for cpu type's GPU's can do idle enter consecutively */
206 if (core->idle_entered == 0 && !(core->dcvs_core_id >= GPU_OFFSET))
207 __err("%s trying to reexit idle", core->core_name);
208 core->idle_entered = 0;
209 /*
210 * only start the timer if governor is not stopped
211 */
212 if (slack_us != 0
213 && !(core->pending_freq < NO_OUTSTANDING_FREQ_CHANGE)) {
214 ret = hrtimer_start(&core->slack_timer,
215 ktime_set(0, slack_us * 1000),
216 HRTIMER_MODE_REL_PINNED);
217 if (ret) {
218 pr_err("%s Failed to start timer ret = %d\n",
219 core->core_name, ret);
220 }
221 }
222 spin_unlock_irqrestore(&core->pending_freq_lock, flags1);
223
224 spin_unlock_irqrestore(&core->idle_state_change_lock, flags2);
225}
226
227static void restart_slack_timer(struct dcvs_core *core, int slack_us)
228{
229 unsigned long flags1, flags2;
230 int ret;
231
232 spin_lock_irqsave(&core->idle_state_change_lock, flags2);
233
234 hrtimer_cancel(&core->slack_timer);
235
236 spin_lock_irqsave(&core->pending_freq_lock, flags1);
237
238 /*
239 * only start the timer if idle is not entered
240 * and governor is not stopped
241 */
242 if (slack_us != 0 && (core->idle_entered != 1)
243 && !(core->pending_freq < NO_OUTSTANDING_FREQ_CHANGE)) {
244 ret = hrtimer_start(&core->slack_timer,
245 ktime_set(0, slack_us * 1000),
246 HRTIMER_MODE_REL_PINNED);
247 if (ret) {
248 pr_err("%s Failed to start timer ret = %d\n",
249 core->core_name, ret);
250 }
251 }
252 spin_unlock_irqrestore(&core->pending_freq_lock, flags1);
253 spin_unlock_irqrestore(&core->idle_state_change_lock, flags2);
254}
255
Steve Muckle93bb4252012-11-12 14:20:39 -0800256static void apply_gpu_floor(int cpu_freq)
257{
258 int i;
259 int gpu_floor_freq = 0;
260 struct dcvs_core *gpu;
261
262 if (!dcvs_pdata)
263 return;
264
265 for (i = 0; i < dcvs_pdata->num_sync_rules; i++)
266 if (cpu_freq > dcvs_pdata->sync_rules[i].cpu_khz) {
267 gpu_floor_freq =
268 dcvs_pdata->sync_rules[i].gpu_floor_khz;
269 break;
270 }
271
272 if (!gpu_floor_freq)
273 return;
274
275 for (i = GPU_OFFSET; i < CORES_MAX; i++) {
276 gpu = &core_list[i];
277 if (gpu->dcvs_core_id == -1)
278 continue;
279 if (gpu->set_floor_frequency)
280 gpu->set_floor_frequency(gpu->type_core_num,
281 gpu_floor_freq);
282 }
283}
284
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700285static int __msm_dcvs_change_freq(struct dcvs_core *core)
286{
287 int ret = 0;
288 unsigned long flags = 0;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700289 int requested_freq = 0;
290 ktime_t time_start;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700291 uint32_t slack_us = 0;
292 uint32_t ret1 = 0;
293
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700294 spin_lock_irqsave(&core->pending_freq_lock, flags);
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700295repeat:
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700296 BUG_ON(!core->pending_freq);
297 if (core->pending_freq == STOP_FREQ_CHANGE)
298 BUG();
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700299
300 requested_freq = core->pending_freq;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700301 time_start = core->time_start;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700302 core->time_start = ns_to_ktime(0);
303
304 if (requested_freq < 0) {
305 requested_freq = -1 * requested_freq;
306 core->pending_freq = STOP_FREQ_CHANGE;
307 } else {
308 core->pending_freq = NO_OUTSTANDING_FREQ_CHANGE;
309 }
310
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700311 if (requested_freq == core->actual_freq)
312 goto out;
313
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700314 spin_unlock_irqrestore(&core->pending_freq_lock, flags);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700315
Steve Muckle93bb4252012-11-12 14:20:39 -0800316 if (core->type == MSM_DCVS_CORE_TYPE_CPU &&
317 core->type_core_num == 0)
318 apply_gpu_floor(requested_freq);
319
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700320 /**
321 * Call the frequency sink driver to change the frequency
322 * We will need to get back the actual frequency in KHz and
323 * the record the time taken to change it.
324 */
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700325 ret = core->set_frequency(core->type_core_num, requested_freq);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700326 if (ret <= 0)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700327 __err("Core %s failed to set freq %u\n",
328 core->core_name, requested_freq);
Eugene Seah76af9832012-03-28 18:43:53 -0600329 /* continue to call TZ to get updated slack timer */
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700330 else
Eugene Seah76af9832012-03-28 18:43:53 -0600331 core->actual_freq = ret;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700332
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700333 core->freq_change_us = (uint32_t)ktime_to_us(
334 ktime_sub(ktime_get(), time_start));
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700335
336 /**
337 * Disable low power modes if the actual frequency is >
338 * disable_pc_threshold.
339 */
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700340 if (core->actual_freq > core->algo_param.disable_pc_threshold) {
341 core->idle_enable(core->type_core_num,
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700342 MSM_DCVS_DISABLE_HIGH_LATENCY_MODES);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700343 } else if (core->actual_freq <= core->algo_param.disable_pc_threshold) {
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700344 core->idle_enable(core->type_core_num,
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700345 MSM_DCVS_ENABLE_HIGH_LATENCY_MODES);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700346 }
347
348 /**
349 * Update algorithm with new freq and time taken to change
350 * to this frequency and that will get us the new slack
351 * timer
352 */
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700353 ret = msm_dcvs_scm_event(core->dcvs_core_id,
354 MSM_DCVS_SCM_CLOCK_FREQ_UPDATE,
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700355 core->actual_freq, core->freq_change_us,
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700356 &slack_us, &ret1);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700357 if (ret) {
358 __err("Error sending core (%s) dcvs_core_id = %d freq change (%u) reqfreq = %d slack_us=%d ret = %d\n",
359 core->core_name, core->dcvs_core_id,
360 core->actual_freq, requested_freq,
361 slack_us, ret);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700362 }
363
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700364 /* TODO confirm that we get a valid freq from SM even when the above
365 * FREQ_UPDATE fails
366 */
367 restart_slack_timer(core, slack_us);
368 spin_lock_irqsave(&core->pending_freq_lock, flags);
369
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700370 /**
371 * By the time we are done with freq changes, we could be asked to
372 * change again. Check before exiting.
373 */
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700374 if (core->pending_freq != NO_OUTSTANDING_FREQ_CHANGE
375 && core->pending_freq != STOP_FREQ_CHANGE) {
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700376 goto repeat;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700377 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700378
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700379out: /* should always be jumped to with the spin_lock held */
380 spin_unlock_irqrestore(&core->pending_freq_lock, flags);
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700381
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700382 return ret;
383}
384
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700385static void msm_dcvs_report_temp_work(struct work_struct *work)
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700386{
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700387 struct dcvs_core *core = container_of(work,
388 struct dcvs_core,
389 temperature_work.work);
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700390 struct msm_dcvs_core_info *info = core->info;
391 struct tsens_device tsens_dev;
392 int ret;
393 unsigned long temp = 0;
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700394 int interval_ms;
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700395
396 tsens_dev.sensor_num = core->sensor;
397 ret = tsens_get_temp(&tsens_dev, &temp);
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700398 if (!temp) {
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700399 tsens_dev.sensor_num = 0;
400 ret = tsens_get_temp(&tsens_dev, &temp);
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700401 if (!temp)
402 goto out;
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700403 }
404
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700405 if (temp == info->power_param.current_temp)
406 goto out;
407 info->power_param.current_temp = temp;
408
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700409 ret = msm_dcvs_scm_set_power_params(core->dcvs_core_id,
410 &info->power_param,
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700411 &info->freq_tbl[0], &core->coeffs);
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700412out:
413 if (info->thermal_poll_ms == 0)
414 interval_ms = 60000;
415 else if (info->thermal_poll_ms < 1000)
416 interval_ms = 1000;
417 else
418 interval_ms = info->thermal_poll_ms;
419
420 schedule_delayed_work(&core->temperature_work,
421 msecs_to_jiffies(interval_ms));
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700422}
423
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700424static int msm_dcvs_do_freq(void *data)
425{
426 struct dcvs_core *core = (struct dcvs_core *)data;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700427
428 while (!kthread_should_stop()) {
Abhijeet Dharmapurikarbbb52fe2012-08-31 20:31:16 -0700429 wait_event(core->wait_q, !(core->pending_freq == 0 ||
430 core->pending_freq == -1) ||
431 kthread_should_stop());
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700432
433 if (kthread_should_stop())
434 break;
435
Abhijeet Dharmapurikarbbb52fe2012-08-31 20:31:16 -0700436 __msm_dcvs_change_freq(core);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700437 }
438
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700439 return 0;
440}
441
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700442/* freq_pending_lock should be held */
443static void request_freq_change(struct dcvs_core *core, int new_freq)
444{
445 if (new_freq == NO_OUTSTANDING_FREQ_CHANGE) {
446 if (core->pending_freq != STOP_FREQ_CHANGE) {
447 __err("%s gov started with earlier pending freq %d\n",
448 core->core_name, core->pending_freq);
449 }
450 core->pending_freq = NO_OUTSTANDING_FREQ_CHANGE;
451 return;
452 }
453
454 if (new_freq == STOP_FREQ_CHANGE) {
455 if (core->pending_freq == NO_OUTSTANDING_FREQ_CHANGE)
456 core->pending_freq = STOP_FREQ_CHANGE;
457 else if (core->pending_freq > 0)
458 core->pending_freq = -1 * core->pending_freq;
459 return;
460 }
461
462 if (core->pending_freq < 0) {
463 /* a value less than 0 means that the governor has stopped
464 * and no more freq changes should be requested
465 */
466 return;
467 }
468
469 if (core->actual_freq != new_freq && core->pending_freq != new_freq) {
470 core->pending_freq = new_freq;
471 core->time_start = ktime_get();
472 wake_up(&core->wait_q);
473 }
474}
475
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700476static int msm_dcvs_update_freq(struct dcvs_core *core,
Eugene Seah76af9832012-03-28 18:43:53 -0600477 enum msm_dcvs_scm_event event, uint32_t param0,
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700478 uint32_t *ret1)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700479{
480 int ret = 0;
481 unsigned long flags = 0;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700482 uint32_t new_freq = -EINVAL;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700483
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700484 spin_lock_irqsave(&core->pending_freq_lock, flags);
485
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700486 ret = msm_dcvs_scm_event(core->dcvs_core_id, event, param0,
Eugene Seah76af9832012-03-28 18:43:53 -0600487 core->actual_freq, &new_freq, ret1);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700488 if (ret) {
Jeff Ohlstein4aa4a2b2012-06-28 19:03:49 -0700489 if (ret == -13)
490 ret = 0;
491 else
492 __err("Error (%d) sending SCM event %d for core %s\n",
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700493 ret, event, core->core_name);
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700494 goto out;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700495 }
496
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700497 if (new_freq == 0) {
498 /*
499 * sometimes TZ gives us a 0 freq back,
500 * do not queue up a request
501 */
502 goto out;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700503 }
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700504
505 request_freq_change(core, new_freq);
506
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700507out:
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700508 spin_unlock_irqrestore(&core->pending_freq_lock, flags);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700509
510 return ret;
511}
512
513static enum hrtimer_restart msm_dcvs_core_slack_timer(struct hrtimer *timer)
514{
515 int ret = 0;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700516 struct dcvs_core *core = container_of(timer,
517 struct dcvs_core, slack_timer);
Eugene Seah76af9832012-03-28 18:43:53 -0600518 uint32_t ret1;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700519
Abhijeet Dharmapurikar07cf2ff2012-09-13 19:05:13 -0700520 trace_printk("dcvs: Slack timer fired for core=%s\n", core->core_name);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700521 /**
522 * Timer expired, notify TZ
523 * Dont care about the third arg.
524 */
Eugene Seah76af9832012-03-28 18:43:53 -0600525 ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_QOS_TIMER_EXPIRED, 0,
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700526 &ret1);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700527 if (ret)
528 __err("Timer expired for core %s but failed to notify.\n",
529 core->core_name);
530
531 return HRTIMER_NORESTART;
532}
533
534/* Helper functions and macros for sysfs nodes for a core */
535#define CORE_FROM_ATTRIBS(attr, name) \
536 container_of(container_of(attr, struct core_attribs, name), \
537 struct dcvs_core, attrib);
538
539#define DCVS_PARAM_SHOW(_name, v) \
540static ssize_t msm_dcvs_attr_##_name##_show(struct kobject *kobj, \
541 struct kobj_attribute *attr, char *buf) \
542{ \
543 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
544 return snprintf(buf, PAGE_SIZE, "%d\n", v); \
545}
546
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700547#define DCVS_PARAM_STORE(_name) \
548static ssize_t msm_dcvs_attr_##_name##_show(struct kobject *kobj,\
549 struct kobj_attribute *attr, char *buf) \
550{ \
551 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
552 return snprintf(buf, PAGE_SIZE, "%d\n", core->info->_name); \
553} \
554static ssize_t msm_dcvs_attr_##_name##_store(struct kobject *kobj, \
555 struct kobj_attribute *attr, const char *buf, size_t count) \
556{ \
557 int ret = 0; \
558 uint32_t val = 0; \
559 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
560 ret = kstrtouint(buf, 10, &val); \
561 if (ret) { \
562 __err("Invalid input %s for %s\n", buf, __stringify(_name));\
563 } else { \
564 core->info->_name = val; \
565 } \
566 return count; \
567}
568
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700569#define DCVS_ALGO_PARAM(_name) \
570static ssize_t msm_dcvs_attr_##_name##_show(struct kobject *kobj,\
571 struct kobj_attribute *attr, char *buf) \
572{ \
573 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
574 return snprintf(buf, PAGE_SIZE, "%d\n", core->algo_param._name); \
575} \
576static ssize_t msm_dcvs_attr_##_name##_store(struct kobject *kobj, \
577 struct kobj_attribute *attr, const char *buf, size_t count) \
578{ \
579 int ret = 0; \
580 uint32_t val = 0; \
581 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700582 ret = kstrtouint(buf, 10, &val); \
583 if (ret) { \
584 __err("Invalid input %s for %s\n", buf, __stringify(_name));\
585 } else { \
586 uint32_t old_val = core->algo_param._name; \
587 core->algo_param._name = val; \
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700588 ret = msm_dcvs_scm_set_algo_params(core->dcvs_core_id, \
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700589 &core->algo_param); \
590 if (ret) { \
591 core->algo_param._name = old_val; \
592 __err("Error(%d) in setting %d for algo param %s\n",\
593 ret, val, __stringify(_name)); \
594 } \
595 } \
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700596 return count; \
597}
598
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700599#define DCVS_ENERGY_PARAM(_name) \
600static ssize_t msm_dcvs_attr_##_name##_show(struct kobject *kobj,\
601 struct kobj_attribute *attr, char *buf) \
602{ \
603 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
604 return snprintf(buf, PAGE_SIZE, "%d\n", core->coeffs._name); \
605} \
606static ssize_t msm_dcvs_attr_##_name##_store(struct kobject *kobj, \
607 struct kobj_attribute *attr, const char *buf, size_t count) \
608{ \
609 int ret = 0; \
610 int32_t val = 0; \
611 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700612 ret = kstrtoint(buf, 10, &val); \
613 if (ret) { \
614 __err("Invalid input %s for %s\n", buf, __stringify(_name));\
615 } else { \
616 int32_t old_val = core->coeffs._name; \
617 core->coeffs._name = val; \
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700618 ret = msm_dcvs_scm_set_power_params(core->dcvs_core_id, \
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700619 &core->info->power_param, &core->info->freq_tbl[0], \
620 &core->coeffs); \
621 if (ret) { \
622 core->coeffs._name = old_val; \
623 __err("Error(%d) in setting %d for coeffs param %s\n",\
624 ret, val, __stringify(_name)); \
625 } \
626 } \
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700627 return count; \
628}
629
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700630#define DCVS_RO_ATTRIB(i, _name) \
631 core->attrib._name.attr.name = __stringify(_name); \
632 core->attrib._name.attr.mode = S_IRUGO; \
633 core->attrib._name.show = msm_dcvs_attr_##_name##_show; \
634 core->attrib._name.store = NULL; \
635 core->attrib.attrib_group.attrs[i] = &core->attrib._name.attr;
636
637#define DCVS_RW_ATTRIB(i, _name) \
638 core->attrib._name.attr.name = __stringify(_name); \
639 core->attrib._name.attr.mode = S_IRUGO | S_IWUSR; \
640 core->attrib._name.show = msm_dcvs_attr_##_name##_show; \
641 core->attrib._name.store = msm_dcvs_attr_##_name##_store; \
642 core->attrib.attrib_group.attrs[i] = &core->attrib._name.attr;
643
644/**
645 * Function declarations for different attributes.
646 * Gets used when setting the attribute show and store parameters.
647 */
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700648DCVS_PARAM_SHOW(freq_change_us, (core->freq_change_us))
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700649
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700650DCVS_ALGO_PARAM(disable_pc_threshold)
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700651DCVS_ALGO_PARAM(em_win_size_min_us)
652DCVS_ALGO_PARAM(em_win_size_max_us)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700653DCVS_ALGO_PARAM(em_max_util_pct)
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700654DCVS_ALGO_PARAM(group_id)
655DCVS_ALGO_PARAM(max_freq_chg_time_us)
656DCVS_ALGO_PARAM(slack_mode_dynamic)
657DCVS_ALGO_PARAM(slack_time_min_us)
658DCVS_ALGO_PARAM(slack_time_max_us)
659DCVS_ALGO_PARAM(slack_weight_thresh_pct)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700660DCVS_ALGO_PARAM(ss_iobusy_conv)
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700661DCVS_ALGO_PARAM(ss_win_size_min_us)
662DCVS_ALGO_PARAM(ss_win_size_max_us)
663DCVS_ALGO_PARAM(ss_util_pct)
664
665DCVS_ENERGY_PARAM(active_coeff_a)
666DCVS_ENERGY_PARAM(active_coeff_b)
667DCVS_ENERGY_PARAM(active_coeff_c)
668DCVS_ENERGY_PARAM(leakage_coeff_a)
669DCVS_ENERGY_PARAM(leakage_coeff_b)
670DCVS_ENERGY_PARAM(leakage_coeff_c)
671DCVS_ENERGY_PARAM(leakage_coeff_d)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700672
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700673DCVS_PARAM_STORE(thermal_poll_ms)
674
Steve Muckle118f47b2012-10-17 16:09:37 -0700675static ssize_t msm_dcvs_attr_freq_tbl_show(struct kobject *kobj,
676 struct kobj_attribute *attr,
677 char *buf)
678{
679 struct msm_dcvs_freq_entry *freq_tbl;
680 char *buf_idx = buf;
681 int i, len;
682 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, freq_tbl);
683
684 freq_tbl = core->info->freq_tbl;
685 *buf_idx = '\0';
686
687 /* limit the number of frequencies we will print into
688 * the PAGE_SIZE sysfs show buffer. */
689 if (core->info->power_param.num_freq > 64)
690 return 0;
691
692 for (i = 0; i < core->info->power_param.num_freq; i++) {
693 if (freq_tbl[i].is_trans_level) {
694 len = snprintf(buf_idx, 10, "%7d ", freq_tbl[i].freq);
695 /* buf_idx always points at terminating null */
696 buf_idx += len;
697 }
698 }
699 /* overwrite final trailing space with newline */
700 if (buf_idx > buf)
701 *(buf_idx - 1) = '\n';
702
703 return buf_idx - buf;
704}
705
706static ssize_t msm_dcvs_attr_freq_tbl_store(struct kobject *kobj,
707 struct kobj_attribute *attr,
708 const char *buf,
709 size_t count)
710{
711 struct msm_dcvs_freq_entry *freq_tbl;
712 uint32_t freq;
713 int i, ret;
714 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, freq_tbl);
715
716 freq_tbl = core->info->freq_tbl;
717
718 ret = kstrtouint(buf, 10, &freq);
719 if (ret) {
720 __err("Invalid input %s for freq_tbl\n", buf);
721 return count;
722 }
723
724 for (i = 0; i < core->info->power_param.num_freq; i++)
725 if (freq_tbl[i].freq == freq) {
726 freq_tbl[i].is_trans_level ^= 1;
727 break;
728 }
729
730 if (i >= core->info->power_param.num_freq) {
731 __err("Invalid frequency for freq_tbl: %d\n", freq);
732 return count;
733 }
734
735 ret = msm_dcvs_scm_set_power_params(core->dcvs_core_id,
736 &core->info->power_param,
737 &core->info->freq_tbl[0],
738 &core->coeffs);
739 if (ret) {
740 freq_tbl[i].is_trans_level ^= 1;
741 __err("Error %d in toggling freq %d (orig enable val %d)\n",
742 ret, freq_tbl[i].freq, freq_tbl[i].is_trans_level);
743 }
744 return count;
745}
746
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700747static int msm_dcvs_setup_core_sysfs(struct dcvs_core *core)
748{
749 int ret = 0;
750 struct kobject *core_kobj = NULL;
Steve Muckle118f47b2012-10-17 16:09:37 -0700751 const int attr_count = 25;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700752
753 BUG_ON(!cores_kobj);
754
755 core->attrib.attrib_group.attrs =
756 kzalloc(attr_count * sizeof(struct attribute *), GFP_KERNEL);
757
758 if (!core->attrib.attrib_group.attrs) {
759 ret = -ENOMEM;
760 goto done;
761 }
762
Abhijeet Dharmapurikar35876382012-09-13 16:09:51 -0700763 DCVS_RO_ATTRIB(0, freq_change_us);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700764
Abhijeet Dharmapurikar35876382012-09-13 16:09:51 -0700765 DCVS_RW_ATTRIB(1, disable_pc_threshold);
766 DCVS_RW_ATTRIB(2, em_win_size_min_us);
767 DCVS_RW_ATTRIB(3, em_win_size_max_us);
768 DCVS_RW_ATTRIB(4, em_max_util_pct);
769 DCVS_RW_ATTRIB(5, group_id);
770 DCVS_RW_ATTRIB(6, max_freq_chg_time_us);
771 DCVS_RW_ATTRIB(7, slack_mode_dynamic);
772 DCVS_RW_ATTRIB(8, slack_weight_thresh_pct);
773 DCVS_RW_ATTRIB(9, slack_time_min_us);
774 DCVS_RW_ATTRIB(10, slack_time_max_us);
775 DCVS_RW_ATTRIB(11, ss_iobusy_conv);
776 DCVS_RW_ATTRIB(12, ss_win_size_min_us);
777 DCVS_RW_ATTRIB(13, ss_win_size_max_us);
778 DCVS_RW_ATTRIB(14, ss_util_pct);
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700779
Abhijeet Dharmapurikar35876382012-09-13 16:09:51 -0700780 DCVS_RW_ATTRIB(15, active_coeff_a);
781 DCVS_RW_ATTRIB(16, active_coeff_b);
782 DCVS_RW_ATTRIB(17, active_coeff_c);
783 DCVS_RW_ATTRIB(18, leakage_coeff_a);
784 DCVS_RW_ATTRIB(19, leakage_coeff_b);
785 DCVS_RW_ATTRIB(20, leakage_coeff_c);
786 DCVS_RW_ATTRIB(21, leakage_coeff_d);
787 DCVS_RW_ATTRIB(22, thermal_poll_ms);
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700788
Steve Muckle118f47b2012-10-17 16:09:37 -0700789 DCVS_RW_ATTRIB(23, freq_tbl);
790
791 core->attrib.attrib_group.attrs[24] = NULL;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700792
793 core_kobj = kobject_create_and_add(core->core_name, cores_kobj);
794 if (!core_kobj) {
795 ret = -ENOMEM;
796 goto done;
797 }
798
799 ret = sysfs_create_group(core_kobj, &core->attrib.attrib_group);
800 if (ret)
801 __err("Cannot create core %s attr group\n", core->core_name);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700802
803done:
804 if (ret) {
805 kfree(core->attrib.attrib_group.attrs);
806 kobject_del(core_kobj);
807 }
808
809 return ret;
810}
811
Steve Muckle5e5a60d2012-10-09 13:25:22 -0700812static int get_core_offset(enum msm_dcvs_core_type type, int num)
813{
814 int offset = -EINVAL;
815
816 switch (type) {
817 case MSM_DCVS_CORE_TYPE_CPU:
818 offset = CPU_OFFSET + num;
819 BUG_ON(offset >= GPU_OFFSET);
820 break;
821 case MSM_DCVS_CORE_TYPE_GPU:
822 offset = GPU_OFFSET + num;
823 BUG_ON(offset >= CORES_MAX);
824 break;
825 default:
826 BUG();
827 }
828
829 return offset;
830}
831
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700832/* Return the core and initialize non platform data specific numbers in it */
833static struct dcvs_core *msm_dcvs_add_core(enum msm_dcvs_core_type type,
834 int num)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700835{
836 struct dcvs_core *core = NULL;
837 int i;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700838 char name[CORE_NAME_MAX];
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700839
Steve Muckle5e5a60d2012-10-09 13:25:22 -0700840 i = get_core_offset(type, num);
841 if (i < 0)
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700842 return NULL;
Steve Muckle5e5a60d2012-10-09 13:25:22 -0700843
844 if (type == MSM_DCVS_CORE_TYPE_CPU)
845 snprintf(name, CORE_NAME_MAX, "cpu%d", num);
846 else
847 snprintf(name, CORE_NAME_MAX, "gpu%d", num);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700848
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700849 core = &core_list[i];
850 core->dcvs_core_id = i;
Abhijeet Dharmapurikar2ebc0fe2012-09-12 14:05:13 -0700851 strlcpy(core->core_name, name, CORE_NAME_MAX);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700852 spin_lock_init(&core->pending_freq_lock);
853 spin_lock_init(&core->idle_state_change_lock);
854 hrtimer_init(&core->slack_timer,
855 CLOCK_MONOTONIC, HRTIMER_MODE_REL_PINNED);
856 core->slack_timer.function = msm_dcvs_core_slack_timer;
Abhijeet Dharmapurikar2ebc0fe2012-09-12 14:05:13 -0700857 return core;
858}
859
860/* Return the core if found or add to list if @add_to_list is true */
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700861static struct dcvs_core *msm_dcvs_get_core(int offset)
Abhijeet Dharmapurikar2ebc0fe2012-09-12 14:05:13 -0700862{
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700863 /* if the handle is still not set bug */
864 BUG_ON(core_list[offset].dcvs_core_id == -1);
865 return &core_list[offset];
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700866}
867
Steve Mucklea9aac292012-11-02 15:41:00 -0700868void msm_dcvs_register_cpu_freq(uint32_t freq, uint32_t voltage)
869{
870 BUG_ON(freq == 0 || voltage == 0 ||
871 num_cpu_freqs == DCVS_MAX_NUM_FREQS);
872
873 cpu_freq_tbl[num_cpu_freqs].freq = freq;
874 cpu_freq_tbl[num_cpu_freqs].voltage = voltage;
875
876 num_cpu_freqs++;
877}
878
879static void update_cpu_dcvs_params(struct msm_dcvs_core_info *info)
880{
881 int i;
882
883 BUG_ON(num_cpu_freqs == 0);
884
885 info->freq_tbl = cpu_freq_tbl;
886 info->power_param.num_freq = num_cpu_freqs;
887
888 if (!dcvs_pdata || dcvs_pdata->num_sync_rules == 0)
889 return;
890
891 /* the first sync rule shows what the turbo frequencies are -
892 * these frequencies need energy offsets set */
893 for (i = 0; i < DCVS_MAX_NUM_FREQS && cpu_freq_tbl[i].freq != 0; i++)
894 if (cpu_freq_tbl[i].freq > dcvs_pdata->sync_rules[0].cpu_khz) {
895 cpu_freq_tbl[i].active_energy_offset = 100;
896 cpu_freq_tbl[i].leakage_energy_offset = 100;
897 }
898}
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700899
900int msm_dcvs_register_core(
901 enum msm_dcvs_core_type type,
902 int type_core_num,
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700903 struct msm_dcvs_core_info *info,
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700904 int (*set_frequency)(int type_core_num, unsigned int freq),
905 unsigned int (*get_frequency)(int type_core_num),
906 int (*idle_enable)(int type_core_num,
Abhijeet Dharmapurikar6e9b34f2012-09-10 16:03:39 -0700907 enum msm_core_control_event event),
Steve Muckle93bb4252012-11-12 14:20:39 -0800908 int (*set_floor_frequency)(int type_core_num, unsigned int freq),
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700909 int sensor)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700910{
911 int ret = -EINVAL;
Steve Muckle5e5a60d2012-10-09 13:25:22 -0700912 int offset;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700913 struct dcvs_core *core = NULL;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700914 uint32_t ret1;
915 uint32_t ret2;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700916
Steve Muckle5e5a60d2012-10-09 13:25:22 -0700917 offset = get_core_offset(type, type_core_num);
918 if (offset < 0)
919 return ret;
920 if (core_list[offset].dcvs_core_id != -1)
921 return core_list[offset].dcvs_core_id;
922
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700923 core = msm_dcvs_add_core(type, type_core_num);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700924 if (!core)
925 return ret;
926
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700927 core->type = type;
928 core->type_core_num = type_core_num;
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700929 core->set_frequency = set_frequency;
930 core->get_frequency = get_frequency;
Abhijeet Dharmapurikar6e9b34f2012-09-10 16:03:39 -0700931 core->idle_enable = idle_enable;
Steve Muckle93bb4252012-11-12 14:20:39 -0800932 core->set_floor_frequency = set_floor_frequency;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700933 core->pending_freq = STOP_FREQ_CHANGE;
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700934
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700935 core->info = info;
Steve Mucklea9aac292012-11-02 15:41:00 -0700936 if (type == MSM_DCVS_CORE_TYPE_CPU)
937 update_cpu_dcvs_params(info);
938
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700939 memcpy(&core->algo_param, &info->algo_param,
940 sizeof(struct msm_dcvs_algo_param));
941
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700942 memcpy(&core->coeffs, &info->energy_coeffs,
943 sizeof(struct msm_dcvs_energy_curve_coeffs));
944
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700945 /*
946 * The tz expects cpu0 to represent bit 0 in the mask, however the
947 * dcvs_core_id needs to start from 1, dcvs_core_id = 0 is used to
948 * indicate that this request is not associated with any core.
949 * mpdecision
950 */
951 info->core_param.core_bitmask_id
952 = 1 << (core->dcvs_core_id - CPU_OFFSET);
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700953 core->sensor = sensor;
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700954
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700955 ret = msm_dcvs_scm_register_core(core->dcvs_core_id, &info->core_param);
956 if (ret) {
957 __err("%s: scm register core fail handle = %d ret = %d\n",
958 __func__, core->dcvs_core_id, ret);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700959 goto bail;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700960 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700961
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700962 ret = msm_dcvs_scm_set_algo_params(core->dcvs_core_id,
963 &info->algo_param);
964 if (ret) {
965 __err("%s: scm algo params failed ret = %d\n", __func__, ret);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700966 goto bail;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700967 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700968
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700969 ret = msm_dcvs_scm_set_power_params(core->dcvs_core_id,
970 &info->power_param,
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700971 &info->freq_tbl[0], &core->coeffs);
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700972 if (ret) {
973 __err("%s: scm power params failed ret = %d\n", __func__, ret);
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700974 goto bail;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700975 }
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700976
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700977 ret = msm_dcvs_scm_event(core->dcvs_core_id, MSM_DCVS_SCM_CORE_ONLINE,
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700978 core->actual_freq, 0, &ret1, &ret2);
979 if (ret)
980 goto bail;
981
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700982 ret = msm_dcvs_setup_core_sysfs(core);
983 if (ret) {
984 __err("Unable to setup core %s sysfs\n", core->core_name);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700985 goto bail;
986 }
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700987 core->idle_entered = -1;
Abhijeet Dharmapurikarbbb52fe2012-08-31 20:31:16 -0700988 init_waitqueue_head(&core->wait_q);
989 core->task = kthread_run(msm_dcvs_do_freq, (void *)core,
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700990 "msm_dcvs/%d", core->dcvs_core_id);
991 ret = core->dcvs_core_id;
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700992
993 INIT_DELAYED_WORK(&core->temperature_work, msm_dcvs_report_temp_work);
994 schedule_delayed_work(&core->temperature_work,
995 msecs_to_jiffies(info->thermal_poll_ms));
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700996 return ret;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700997bail:
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700998 core->dcvs_core_id = -1;
999 return -EINVAL;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001000}
1001EXPORT_SYMBOL(msm_dcvs_register_core);
1002
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001003void msm_dcvs_update_limits(int dcvs_core_id)
Abhijeet Dharmapurikarc43f0db2012-08-31 20:42:53 -07001004{
1005 struct dcvs_core *core;
1006
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001007 if (dcvs_core_id < CPU_OFFSET || dcvs_core_id > CORES_MAX) {
1008 __err("%s invalid dcvs_core_id = %d returning -EINVAL\n",
1009 __func__, dcvs_core_id);
Abhijeet Dharmapurikarc43f0db2012-08-31 20:42:53 -07001010 return;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001011 }
Abhijeet Dharmapurikarc43f0db2012-08-31 20:42:53 -07001012
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001013 core = msm_dcvs_get_core(dcvs_core_id);
1014 core->actual_freq = core->get_frequency(core->type_core_num);
Abhijeet Dharmapurikarc43f0db2012-08-31 20:42:53 -07001015}
1016
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001017int msm_dcvs_freq_sink_start(int dcvs_core_id)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001018{
1019 int ret = -EINVAL;
1020 struct dcvs_core *core = NULL;
Eugene Seah76af9832012-03-28 18:43:53 -06001021 uint32_t ret1;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001022 unsigned long flags;
Abhijeet Dharmapurikar8d843b32012-09-13 18:33:20 -07001023 int new_freq;
1024 int timer_interval_us;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001025
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001026 if (dcvs_core_id < CPU_OFFSET || dcvs_core_id > CORES_MAX) {
1027 __err("%s invalid dcvs_core_id = %d returning -EINVAL\n",
1028 __func__, dcvs_core_id);
1029 return -EINVAL;
1030 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001031
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001032 core = msm_dcvs_get_core(dcvs_core_id);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001033 if (!core)
1034 return ret;
1035
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001036 core->actual_freq = core->get_frequency(core->type_core_num);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001037
1038 spin_lock_irqsave(&core->pending_freq_lock, flags);
1039 /* mark that we are ready to accept new frequencies */
1040 request_freq_change(core, NO_OUTSTANDING_FREQ_CHANGE);
1041 spin_unlock_irqrestore(&core->pending_freq_lock, flags);
1042
1043 spin_lock_irqsave(&core->idle_state_change_lock, flags);
1044 core->idle_entered = -1;
1045 spin_unlock_irqrestore(&core->idle_state_change_lock, flags);
1046
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001047 /* Notify TZ to start receiving idle info for the core */
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001048 ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_DCVS_ENABLE, 1, &ret1);
1049
Abhijeet Dharmapurikar8d843b32012-09-13 18:33:20 -07001050 ret = msm_dcvs_scm_event(
1051 core->dcvs_core_id, MSM_DCVS_SCM_CORE_ONLINE, core->actual_freq,
1052 0, &new_freq, &timer_interval_us);
1053 if (ret)
1054 __err("Error (%d) DCVS sending online for %s\n",
1055 ret, core->core_name);
1056
1057 if (new_freq != 0) {
1058 spin_lock_irqsave(&core->pending_freq_lock, flags);
1059 request_freq_change(core, new_freq);
1060 spin_unlock_irqrestore(&core->pending_freq_lock, flags);
1061 }
1062 force_start_slack_timer(core, timer_interval_us);
1063
1064
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001065 core->idle_enable(core->type_core_num, MSM_DCVS_ENABLE_IDLE_PULSE);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001066 return 0;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001067}
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -07001068EXPORT_SYMBOL(msm_dcvs_freq_sink_start);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001069
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001070int msm_dcvs_freq_sink_stop(int dcvs_core_id)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001071{
1072 int ret = -EINVAL;
1073 struct dcvs_core *core = NULL;
Eugene Seah76af9832012-03-28 18:43:53 -06001074 uint32_t ret1;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001075 uint32_t freq;
1076 unsigned long flags;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001077
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001078 if (dcvs_core_id < 0 || dcvs_core_id > CORES_MAX) {
1079 pr_err("%s invalid dcvs_core_id = %d returning -EINVAL\n",
1080 __func__, dcvs_core_id);
1081 return -EINVAL;
1082 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001083
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001084 core = msm_dcvs_get_core(dcvs_core_id);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001085 if (!core) {
1086 __err("couldn't find core for coreid = %d\n", dcvs_core_id);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001087 return ret;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001088 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001089
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001090 core->idle_enable(core->type_core_num, MSM_DCVS_DISABLE_IDLE_PULSE);
1091 /* Notify TZ to stop receiving idle info for the core */
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001092 ret = msm_dcvs_scm_event(core->dcvs_core_id, MSM_DCVS_SCM_DCVS_ENABLE,
1093 0, core->actual_freq, &freq, &ret1);
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001094 core->idle_enable(core->type_core_num,
1095 MSM_DCVS_ENABLE_HIGH_LATENCY_MODES);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001096 spin_lock_irqsave(&core->pending_freq_lock, flags);
1097 /* flush out all the pending freq changes */
1098 request_freq_change(core, STOP_FREQ_CHANGE);
1099 spin_unlock_irqrestore(&core->pending_freq_lock, flags);
1100 force_stop_slack_timer(core);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001101
1102 return 0;
1103}
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -07001104EXPORT_SYMBOL(msm_dcvs_freq_sink_stop);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001105
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001106int msm_dcvs_idle(int dcvs_core_id, enum msm_core_idle_state state,
1107 uint32_t iowaited)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001108{
1109 int ret = 0;
1110 struct dcvs_core *core = NULL;
Eugene Seah76af9832012-03-28 18:43:53 -06001111 uint32_t timer_interval_us = 0;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001112 uint32_t r0, r1;
1113
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001114 if (dcvs_core_id < CPU_OFFSET || dcvs_core_id > CORES_MAX) {
1115 pr_err("invalid dcvs_core_id = %d ret -EINVAL\n", dcvs_core_id);
1116 return -EINVAL;
1117 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001118
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001119 core = msm_dcvs_get_core(dcvs_core_id);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001120
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001121 switch (state) {
1122 case MSM_DCVS_IDLE_ENTER:
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001123 stop_slack_timer(core);
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001124 ret = msm_dcvs_scm_event(core->dcvs_core_id,
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001125 MSM_DCVS_SCM_IDLE_ENTER, 0, 0, &r0, &r1);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001126 if (ret < 0 && ret != -13)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001127 __err("Error (%d) sending idle enter for %s\n",
1128 ret, core->core_name);
Abhijeet Dharmapurikar07cf2ff2012-09-13 19:05:13 -07001129 trace_msm_dcvs_idle("idle_enter_exit", core->core_name, 1);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001130 break;
1131
1132 case MSM_DCVS_IDLE_EXIT:
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001133 ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_IDLE_EXIT,
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001134 iowaited, &timer_interval_us);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001135 if (ret)
1136 __err("Error (%d) sending idle exit for %s\n",
1137 ret, core->core_name);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001138 start_slack_timer(core, timer_interval_us);
Abhijeet Dharmapurikar07cf2ff2012-09-13 19:05:13 -07001139 trace_msm_dcvs_idle("idle_enter_exit", core->core_name, 0);
1140 trace_msm_dcvs_iowait("iowait", core->core_name, iowaited);
1141 trace_msm_dcvs_slack_time("slack_timer_dcvs", core->core_name,
1142 timer_interval_us);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001143 break;
1144 }
1145
1146 return ret;
1147}
1148EXPORT_SYMBOL(msm_dcvs_idle);
1149
1150static int __init msm_dcvs_late_init(void)
1151{
1152 struct kobject *module_kobj = NULL;
1153 int ret = 0;
1154
1155 module_kobj = kset_find_obj(module_kset, KBUILD_MODNAME);
1156 if (!module_kobj) {
1157 pr_err("%s: cannot find kobject for module %s\n",
1158 __func__, KBUILD_MODNAME);
1159 ret = -ENOENT;
1160 goto err;
1161 }
1162
1163 cores_kobj = kobject_create_and_add("cores", module_kobj);
1164 if (!cores_kobj) {
1165 __err("Cannot create %s kobject\n", "cores");
1166 ret = -ENOMEM;
1167 goto err;
1168 }
1169
1170 debugfs_base = debugfs_create_dir("msm_dcvs", NULL);
1171 if (!debugfs_base) {
1172 __err("Cannot create debugfs base %s\n", "msm_dcvs");
1173 ret = -ENOENT;
1174 goto err;
1175 }
1176
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001177err:
1178 if (ret) {
1179 kobject_del(cores_kobj);
1180 cores_kobj = NULL;
1181 debugfs_remove(debugfs_base);
1182 }
1183
1184 return ret;
1185}
1186late_initcall(msm_dcvs_late_init);
1187
Steve Mucklea9aac292012-11-02 15:41:00 -07001188static int __devinit dcvs_probe(struct platform_device *pdev)
1189{
1190 if (pdev->dev.platform_data)
1191 dcvs_pdata = pdev->dev.platform_data;
1192
1193 return 0;
1194}
1195
1196static struct platform_driver dcvs_driver = {
1197 .probe = dcvs_probe,
1198 .driver = {
1199 .name = "dcvs",
1200 .owner = THIS_MODULE,
1201 },
1202};
1203
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001204static int __init msm_dcvs_early_init(void)
1205{
1206 int ret = 0;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001207 int i;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001208
Steve Mucklea9aac292012-11-02 15:41:00 -07001209 platform_driver_register(&dcvs_driver);
1210
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001211 if (!msm_dcvs_enabled) {
1212 __info("Not enabled (%d)\n", msm_dcvs_enabled);
1213 return 0;
1214 }
1215
Abhijeet Dharmapurikar3edb5de2012-09-13 11:02:03 -07001216
1217 /* Only need about 32kBytes for normal operation */
1218 ret = msm_dcvs_scm_init(SZ_32K);
1219 if (ret) {
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001220 __err("Unable to initialize DCVS err=%d\n", ret);
Abhijeet Dharmapurikar3edb5de2012-09-13 11:02:03 -07001221 goto done;
1222 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001223
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001224 for (i = 0; i < CORES_MAX; i++)
1225 core_list[i].dcvs_core_id = -1;
Abhijeet Dharmapurikar3edb5de2012-09-13 11:02:03 -07001226done:
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001227 return ret;
1228}
1229postcore_initcall(msm_dcvs_early_init);