blob: 25b70e7a354f8d0877f4e9ae8bfa6b4bece4b137 [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
Steve Mucklec1785c32012-11-13 14:27:43 -080037#define CORE_FLAG_TEMP_UPDATE 0x1
38
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070039struct core_attribs {
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070040 struct kobj_attribute freq_change_us;
41
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070042 struct kobj_attribute disable_pc_threshold;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -070043 struct kobj_attribute em_win_size_min_us;
44 struct kobj_attribute em_win_size_max_us;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070045 struct kobj_attribute em_max_util_pct;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -070046 struct kobj_attribute group_id;
47 struct kobj_attribute max_freq_chg_time_us;
48 struct kobj_attribute slack_mode_dynamic;
49 struct kobj_attribute slack_time_min_us;
50 struct kobj_attribute slack_time_max_us;
51 struct kobj_attribute slack_weight_thresh_pct;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070052 struct kobj_attribute ss_iobusy_conv;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -070053 struct kobj_attribute ss_win_size_min_us;
54 struct kobj_attribute ss_win_size_max_us;
55 struct kobj_attribute ss_util_pct;
56
57 struct kobj_attribute active_coeff_a;
58 struct kobj_attribute active_coeff_b;
59 struct kobj_attribute active_coeff_c;
60 struct kobj_attribute leakage_coeff_a;
61 struct kobj_attribute leakage_coeff_b;
62 struct kobj_attribute leakage_coeff_c;
63 struct kobj_attribute leakage_coeff_d;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070064
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -070065 struct kobj_attribute thermal_poll_ms;
66
Steve Muckle118f47b2012-10-17 16:09:37 -070067 struct kobj_attribute freq_tbl;
68
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070069 struct attribute_group attrib_group;
70};
71
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -070072enum pending_freq_state {
73 /*
74 * used by the thread to check if pending_freq was updated while it was
75 * setting previous frequency - this is written to and used by the
76 * freq updating thread
77 */
78 NO_OUTSTANDING_FREQ_CHANGE = 0,
79
80 /*
81 * This request is set to indicate that the governor is stopped and no
82 * more frequency change requests are accepted untill it starts again.
83 * This is checked/used by the threads that want to change the freq
84 */
85 STOP_FREQ_CHANGE = -1,
86
87 /*
88 * Any other +ve value means that a freq change was requested and the
89 * thread has not gotten around to update it
90 *
91 * Any other -ve value means that this is the last freq change i.e. a
92 * freq change was requested but the thread has not run yet and
93 * meanwhile the governor was stopped.
94 */
95};
96
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070097struct dcvs_core {
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -070098 spinlock_t idle_state_change_lock;
99 /* 0 when not idle (busy) 1 when idle and -1 when governor starts and
100 * we dont know whether the next call is going to be idle enter or exit
101 */
102 int idle_entered;
103
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700104 enum msm_dcvs_core_type type;
105 /* this is the number in each type for example cpu 0,1,2 and gpu 0,1 */
106 int type_core_num;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700107 char core_name[CORE_NAME_MAX];
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700108 uint32_t actual_freq;
109 uint32_t freq_change_us;
110
111 uint32_t max_time_us; /* core param */
112
113 struct msm_dcvs_algo_param algo_param;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700114 struct msm_dcvs_energy_curve_coeffs coeffs;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700115
116 /* private */
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700117 ktime_t time_start;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700118 struct task_struct *task;
119 struct core_attribs attrib;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700120 uint32_t dcvs_core_id;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700121 struct msm_dcvs_core_info *info;
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700122 int sensor;
Abhijeet Dharmapurikarbbb52fe2012-08-31 20:31:16 -0700123 wait_queue_head_t wait_q;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700124
125 int (*set_frequency)(int type_core_num, unsigned int freq);
126 unsigned int (*get_frequency)(int type_core_num);
127 int (*idle_enable)(int type_core_num,
Abhijeet Dharmapurikar6e9b34f2012-09-10 16:03:39 -0700128 enum msm_core_control_event event);
Steve Muckle93bb4252012-11-12 14:20:39 -0800129 int (*set_floor_frequency)(int type_core_num, unsigned int freq);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700130
131 spinlock_t pending_freq_lock;
132 int pending_freq;
133
134 struct hrtimer slack_timer;
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700135 struct delayed_work temperature_work;
Steve Mucklec1785c32012-11-13 14:27:43 -0800136 int flags;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700137};
138
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700139static int msm_dcvs_enabled = 1;
140module_param_named(enable, msm_dcvs_enabled, int, S_IRUGO | S_IWUSR | S_IWGRP);
141
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700142static struct dentry *debugfs_base;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700143
144static struct dcvs_core core_list[CORES_MAX];
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700145
146static struct kobject *cores_kobj;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700147
Steve Mucklea9aac292012-11-02 15:41:00 -0700148#define DCVS_MAX_NUM_FREQS 15
149static struct msm_dcvs_freq_entry cpu_freq_tbl[DCVS_MAX_NUM_FREQS];
150static unsigned num_cpu_freqs;
151static struct msm_dcvs_platform_data *dcvs_pdata;
152
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700153static void force_stop_slack_timer(struct dcvs_core *core)
154{
155 unsigned long flags;
156
157 spin_lock_irqsave(&core->idle_state_change_lock, flags);
158 hrtimer_cancel(&core->slack_timer);
159 spin_unlock_irqrestore(&core->idle_state_change_lock, flags);
160}
161
Abhijeet Dharmapurikar8d843b32012-09-13 18:33:20 -0700162static void force_start_slack_timer(struct dcvs_core *core, int slack_us)
163{
164 unsigned long flags;
165 int ret;
166
167 spin_lock_irqsave(&core->idle_state_change_lock, flags);
168
169 /*
170 * only start the timer if governor is not stopped
171 */
172 if (slack_us != 0) {
173 ret = hrtimer_start(&core->slack_timer,
174 ktime_set(0, slack_us * 1000),
175 HRTIMER_MODE_REL_PINNED);
176 if (ret) {
177 pr_err("%s Failed to start timer ret = %d\n",
178 core->core_name, ret);
179 }
180 }
181
182 spin_unlock_irqrestore(&core->idle_state_change_lock, flags);
183}
184
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700185static void stop_slack_timer(struct dcvs_core *core)
186{
187 unsigned long flags;
188
189 spin_lock_irqsave(&core->idle_state_change_lock, flags);
190 /* err only for cpu type's GPU's can do idle exit consecutively */
191 if (core->idle_entered == 1 && !(core->dcvs_core_id >= GPU_OFFSET))
192 __err("%s trying to reenter idle", core->core_name);
193 core->idle_entered = 1;
194 hrtimer_cancel(&core->slack_timer);
195 core->idle_entered = 1;
196 spin_unlock_irqrestore(&core->idle_state_change_lock, flags);
197}
198
199static void start_slack_timer(struct dcvs_core *core, int slack_us)
200{
201 unsigned long flags1, flags2;
202 int ret;
203
204 spin_lock_irqsave(&core->idle_state_change_lock, flags2);
205
206 spin_lock_irqsave(&core->pending_freq_lock, flags1);
207
208 /* err only for cpu type's GPU's can do idle enter consecutively */
209 if (core->idle_entered == 0 && !(core->dcvs_core_id >= GPU_OFFSET))
210 __err("%s trying to reexit idle", core->core_name);
211 core->idle_entered = 0;
212 /*
213 * only start the timer if governor is not stopped
214 */
215 if (slack_us != 0
216 && !(core->pending_freq < NO_OUTSTANDING_FREQ_CHANGE)) {
217 ret = hrtimer_start(&core->slack_timer,
218 ktime_set(0, slack_us * 1000),
219 HRTIMER_MODE_REL_PINNED);
220 if (ret) {
221 pr_err("%s Failed to start timer ret = %d\n",
222 core->core_name, ret);
223 }
224 }
225 spin_unlock_irqrestore(&core->pending_freq_lock, flags1);
226
227 spin_unlock_irqrestore(&core->idle_state_change_lock, flags2);
228}
229
230static void restart_slack_timer(struct dcvs_core *core, int slack_us)
231{
232 unsigned long flags1, flags2;
233 int ret;
234
235 spin_lock_irqsave(&core->idle_state_change_lock, flags2);
236
237 hrtimer_cancel(&core->slack_timer);
238
239 spin_lock_irqsave(&core->pending_freq_lock, flags1);
240
241 /*
242 * only start the timer if idle is not entered
243 * and governor is not stopped
244 */
245 if (slack_us != 0 && (core->idle_entered != 1)
246 && !(core->pending_freq < NO_OUTSTANDING_FREQ_CHANGE)) {
247 ret = hrtimer_start(&core->slack_timer,
248 ktime_set(0, slack_us * 1000),
249 HRTIMER_MODE_REL_PINNED);
250 if (ret) {
251 pr_err("%s Failed to start timer ret = %d\n",
252 core->core_name, ret);
253 }
254 }
255 spin_unlock_irqrestore(&core->pending_freq_lock, flags1);
256 spin_unlock_irqrestore(&core->idle_state_change_lock, flags2);
257}
258
Steve Muckle93bb4252012-11-12 14:20:39 -0800259static void apply_gpu_floor(int cpu_freq)
260{
261 int i;
262 int gpu_floor_freq = 0;
263 struct dcvs_core *gpu;
264
265 if (!dcvs_pdata)
266 return;
267
268 for (i = 0; i < dcvs_pdata->num_sync_rules; i++)
269 if (cpu_freq > dcvs_pdata->sync_rules[i].cpu_khz) {
270 gpu_floor_freq =
271 dcvs_pdata->sync_rules[i].gpu_floor_khz;
272 break;
273 }
274
275 if (!gpu_floor_freq)
276 return;
277
278 for (i = GPU_OFFSET; i < CORES_MAX; i++) {
279 gpu = &core_list[i];
280 if (gpu->dcvs_core_id == -1)
281 continue;
282 if (gpu->set_floor_frequency)
283 gpu->set_floor_frequency(gpu->type_core_num,
284 gpu_floor_freq);
285 }
286}
287
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700288static int __msm_dcvs_change_freq(struct dcvs_core *core)
289{
290 int ret = 0;
291 unsigned long flags = 0;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700292 int requested_freq = 0;
293 ktime_t time_start;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700294 uint32_t slack_us = 0;
295 uint32_t ret1 = 0;
296
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700297 spin_lock_irqsave(&core->pending_freq_lock, flags);
Steve Muckle80e0b7b2012-11-19 15:46:39 -0800298 if (core->pending_freq == STOP_FREQ_CHANGE)
299 goto out;
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700300repeat:
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700301 BUG_ON(!core->pending_freq);
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700302
303 requested_freq = core->pending_freq;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700304 time_start = core->time_start;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700305 core->time_start = ns_to_ktime(0);
306
Steve Muckle80e0b7b2012-11-19 15:46:39 -0800307 core->pending_freq = NO_OUTSTANDING_FREQ_CHANGE;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700308
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700309 if (requested_freq == core->actual_freq)
310 goto out;
311
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700312 spin_unlock_irqrestore(&core->pending_freq_lock, flags);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700313
Steve Muckle93bb4252012-11-12 14:20:39 -0800314 if (core->type == MSM_DCVS_CORE_TYPE_CPU &&
315 core->type_core_num == 0)
316 apply_gpu_floor(requested_freq);
317
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700318 /**
319 * Call the frequency sink driver to change the frequency
320 * We will need to get back the actual frequency in KHz and
321 * the record the time taken to change it.
322 */
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700323 ret = core->set_frequency(core->type_core_num, requested_freq);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700324 if (ret <= 0)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700325 __err("Core %s failed to set freq %u\n",
326 core->core_name, requested_freq);
Eugene Seah76af9832012-03-28 18:43:53 -0600327 /* continue to call TZ to get updated slack timer */
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700328 else
Eugene Seah76af9832012-03-28 18:43:53 -0600329 core->actual_freq = ret;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700330
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700331 core->freq_change_us = (uint32_t)ktime_to_us(
332 ktime_sub(ktime_get(), time_start));
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700333
334 /**
335 * Disable low power modes if the actual frequency is >
336 * disable_pc_threshold.
337 */
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700338 if (core->actual_freq > core->algo_param.disable_pc_threshold) {
339 core->idle_enable(core->type_core_num,
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700340 MSM_DCVS_DISABLE_HIGH_LATENCY_MODES);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700341 } else if (core->actual_freq <= core->algo_param.disable_pc_threshold) {
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700342 core->idle_enable(core->type_core_num,
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700343 MSM_DCVS_ENABLE_HIGH_LATENCY_MODES);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700344 }
345
346 /**
347 * Update algorithm with new freq and time taken to change
348 * to this frequency and that will get us the new slack
349 * timer
350 */
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700351 ret = msm_dcvs_scm_event(core->dcvs_core_id,
352 MSM_DCVS_SCM_CLOCK_FREQ_UPDATE,
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700353 core->actual_freq, core->freq_change_us,
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700354 &slack_us, &ret1);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700355 if (ret) {
356 __err("Error sending core (%s) dcvs_core_id = %d freq change (%u) reqfreq = %d slack_us=%d ret = %d\n",
357 core->core_name, core->dcvs_core_id,
358 core->actual_freq, requested_freq,
359 slack_us, ret);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700360 }
361
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700362 /* TODO confirm that we get a valid freq from SM even when the above
363 * FREQ_UPDATE fails
364 */
365 restart_slack_timer(core, slack_us);
366 spin_lock_irqsave(&core->pending_freq_lock, flags);
367
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700368 /**
369 * By the time we are done with freq changes, we could be asked to
370 * change again. Check before exiting.
371 */
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700372 if (core->pending_freq != NO_OUTSTANDING_FREQ_CHANGE
373 && core->pending_freq != STOP_FREQ_CHANGE) {
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700374 goto repeat;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700375 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700376
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700377out: /* should always be jumped to with the spin_lock held */
378 spin_unlock_irqrestore(&core->pending_freq_lock, flags);
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700379
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700380 return ret;
381}
382
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700383static void msm_dcvs_report_temp_work(struct work_struct *work)
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700384{
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700385 struct dcvs_core *core = container_of(work,
386 struct dcvs_core,
387 temperature_work.work);
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700388 struct msm_dcvs_core_info *info = core->info;
389 struct tsens_device tsens_dev;
390 int ret;
391 unsigned long temp = 0;
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700392 int interval_ms;
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700393
Steve Mucklec1785c32012-11-13 14:27:43 -0800394 if (!(core->flags & CORE_FLAG_TEMP_UPDATE))
395 return;
396
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700397 tsens_dev.sensor_num = core->sensor;
398 ret = tsens_get_temp(&tsens_dev, &temp);
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700399 if (!temp) {
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700400 tsens_dev.sensor_num = 0;
401 ret = tsens_get_temp(&tsens_dev, &temp);
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700402 if (!temp)
403 goto out;
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700404 }
405
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700406 if (temp == info->power_param.current_temp)
407 goto out;
408 info->power_param.current_temp = temp;
409
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700410 ret = msm_dcvs_scm_set_power_params(core->dcvs_core_id,
411 &info->power_param,
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700412 &info->freq_tbl[0], &core->coeffs);
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700413out:
414 if (info->thermal_poll_ms == 0)
415 interval_ms = 60000;
416 else if (info->thermal_poll_ms < 1000)
417 interval_ms = 1000;
418 else
419 interval_ms = info->thermal_poll_ms;
420
421 schedule_delayed_work(&core->temperature_work,
422 msecs_to_jiffies(interval_ms));
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700423}
424
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700425static int msm_dcvs_do_freq(void *data)
426{
427 struct dcvs_core *core = (struct dcvs_core *)data;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700428
429 while (!kthread_should_stop()) {
Abhijeet Dharmapurikarbbb52fe2012-08-31 20:31:16 -0700430 wait_event(core->wait_q, !(core->pending_freq == 0 ||
431 core->pending_freq == -1) ||
432 kthread_should_stop());
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700433
434 if (kthread_should_stop())
435 break;
436
Abhijeet Dharmapurikarbbb52fe2012-08-31 20:31:16 -0700437 __msm_dcvs_change_freq(core);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700438 }
439
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700440 return 0;
441}
442
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700443/* freq_pending_lock should be held */
444static void request_freq_change(struct dcvs_core *core, int new_freq)
445{
446 if (new_freq == NO_OUTSTANDING_FREQ_CHANGE) {
447 if (core->pending_freq != STOP_FREQ_CHANGE) {
448 __err("%s gov started with earlier pending freq %d\n",
449 core->core_name, core->pending_freq);
450 }
451 core->pending_freq = NO_OUTSTANDING_FREQ_CHANGE;
452 return;
453 }
454
455 if (new_freq == STOP_FREQ_CHANGE) {
Steve Muckle80e0b7b2012-11-19 15:46:39 -0800456 core->pending_freq = STOP_FREQ_CHANGE;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700457 return;
458 }
459
460 if (core->pending_freq < 0) {
461 /* a value less than 0 means that the governor has stopped
462 * and no more freq changes should be requested
463 */
464 return;
465 }
466
467 if (core->actual_freq != new_freq && core->pending_freq != new_freq) {
468 core->pending_freq = new_freq;
469 core->time_start = ktime_get();
470 wake_up(&core->wait_q);
471 }
472}
473
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700474static int msm_dcvs_update_freq(struct dcvs_core *core,
Eugene Seah76af9832012-03-28 18:43:53 -0600475 enum msm_dcvs_scm_event event, uint32_t param0,
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700476 uint32_t *ret1)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700477{
478 int ret = 0;
479 unsigned long flags = 0;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700480 uint32_t new_freq = -EINVAL;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700481
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700482 spin_lock_irqsave(&core->pending_freq_lock, flags);
483
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700484 ret = msm_dcvs_scm_event(core->dcvs_core_id, event, param0,
Eugene Seah76af9832012-03-28 18:43:53 -0600485 core->actual_freq, &new_freq, ret1);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700486 if (ret) {
Jeff Ohlstein4aa4a2b2012-06-28 19:03:49 -0700487 if (ret == -13)
488 ret = 0;
489 else
490 __err("Error (%d) sending SCM event %d for core %s\n",
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700491 ret, event, core->core_name);
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700492 goto out;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700493 }
494
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700495 if (new_freq == 0) {
496 /*
497 * sometimes TZ gives us a 0 freq back,
498 * do not queue up a request
499 */
500 goto out;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700501 }
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700502
503 request_freq_change(core, new_freq);
504
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700505out:
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700506 spin_unlock_irqrestore(&core->pending_freq_lock, flags);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700507
508 return ret;
509}
510
511static enum hrtimer_restart msm_dcvs_core_slack_timer(struct hrtimer *timer)
512{
513 int ret = 0;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700514 struct dcvs_core *core = container_of(timer,
515 struct dcvs_core, slack_timer);
Eugene Seah76af9832012-03-28 18:43:53 -0600516 uint32_t ret1;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700517
Abhijeet Dharmapurikar07cf2ff2012-09-13 19:05:13 -0700518 trace_printk("dcvs: Slack timer fired for core=%s\n", core->core_name);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700519 /**
520 * Timer expired, notify TZ
521 * Dont care about the third arg.
522 */
Eugene Seah76af9832012-03-28 18:43:53 -0600523 ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_QOS_TIMER_EXPIRED, 0,
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700524 &ret1);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700525 if (ret)
526 __err("Timer expired for core %s but failed to notify.\n",
527 core->core_name);
528
529 return HRTIMER_NORESTART;
530}
531
532/* Helper functions and macros for sysfs nodes for a core */
533#define CORE_FROM_ATTRIBS(attr, name) \
534 container_of(container_of(attr, struct core_attribs, name), \
535 struct dcvs_core, attrib);
536
537#define DCVS_PARAM_SHOW(_name, v) \
538static ssize_t msm_dcvs_attr_##_name##_show(struct kobject *kobj, \
539 struct kobj_attribute *attr, char *buf) \
540{ \
541 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
542 return snprintf(buf, PAGE_SIZE, "%d\n", v); \
543}
544
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700545#define DCVS_PARAM_STORE(_name) \
546static ssize_t msm_dcvs_attr_##_name##_show(struct kobject *kobj,\
547 struct kobj_attribute *attr, char *buf) \
548{ \
549 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
550 return snprintf(buf, PAGE_SIZE, "%d\n", core->info->_name); \
551} \
552static ssize_t msm_dcvs_attr_##_name##_store(struct kobject *kobj, \
553 struct kobj_attribute *attr, const char *buf, size_t count) \
554{ \
555 int ret = 0; \
556 uint32_t val = 0; \
557 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
558 ret = kstrtouint(buf, 10, &val); \
559 if (ret) { \
560 __err("Invalid input %s for %s\n", buf, __stringify(_name));\
561 } else { \
562 core->info->_name = val; \
563 } \
564 return count; \
565}
566
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700567#define DCVS_ALGO_PARAM(_name) \
568static ssize_t msm_dcvs_attr_##_name##_show(struct kobject *kobj,\
569 struct kobj_attribute *attr, char *buf) \
570{ \
571 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
572 return snprintf(buf, PAGE_SIZE, "%d\n", core->algo_param._name); \
573} \
574static ssize_t msm_dcvs_attr_##_name##_store(struct kobject *kobj, \
575 struct kobj_attribute *attr, const char *buf, size_t count) \
576{ \
577 int ret = 0; \
578 uint32_t val = 0; \
579 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700580 ret = kstrtouint(buf, 10, &val); \
581 if (ret) { \
582 __err("Invalid input %s for %s\n", buf, __stringify(_name));\
583 } else { \
584 uint32_t old_val = core->algo_param._name; \
585 core->algo_param._name = val; \
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700586 ret = msm_dcvs_scm_set_algo_params(core->dcvs_core_id, \
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700587 &core->algo_param); \
588 if (ret) { \
589 core->algo_param._name = old_val; \
590 __err("Error(%d) in setting %d for algo param %s\n",\
591 ret, val, __stringify(_name)); \
592 } \
593 } \
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700594 return count; \
595}
596
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700597#define DCVS_ENERGY_PARAM(_name) \
598static ssize_t msm_dcvs_attr_##_name##_show(struct kobject *kobj,\
599 struct kobj_attribute *attr, char *buf) \
600{ \
601 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
602 return snprintf(buf, PAGE_SIZE, "%d\n", core->coeffs._name); \
603} \
604static ssize_t msm_dcvs_attr_##_name##_store(struct kobject *kobj, \
605 struct kobj_attribute *attr, const char *buf, size_t count) \
606{ \
607 int ret = 0; \
608 int32_t val = 0; \
609 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700610 ret = kstrtoint(buf, 10, &val); \
611 if (ret) { \
612 __err("Invalid input %s for %s\n", buf, __stringify(_name));\
613 } else { \
614 int32_t old_val = core->coeffs._name; \
615 core->coeffs._name = val; \
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700616 ret = msm_dcvs_scm_set_power_params(core->dcvs_core_id, \
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700617 &core->info->power_param, &core->info->freq_tbl[0], \
618 &core->coeffs); \
619 if (ret) { \
620 core->coeffs._name = old_val; \
621 __err("Error(%d) in setting %d for coeffs param %s\n",\
622 ret, val, __stringify(_name)); \
623 } \
624 } \
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700625 return count; \
626}
627
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700628#define DCVS_RO_ATTRIB(i, _name) \
629 core->attrib._name.attr.name = __stringify(_name); \
630 core->attrib._name.attr.mode = S_IRUGO; \
631 core->attrib._name.show = msm_dcvs_attr_##_name##_show; \
632 core->attrib._name.store = NULL; \
633 core->attrib.attrib_group.attrs[i] = &core->attrib._name.attr;
634
635#define DCVS_RW_ATTRIB(i, _name) \
636 core->attrib._name.attr.name = __stringify(_name); \
637 core->attrib._name.attr.mode = S_IRUGO | S_IWUSR; \
638 core->attrib._name.show = msm_dcvs_attr_##_name##_show; \
639 core->attrib._name.store = msm_dcvs_attr_##_name##_store; \
640 core->attrib.attrib_group.attrs[i] = &core->attrib._name.attr;
641
642/**
643 * Function declarations for different attributes.
644 * Gets used when setting the attribute show and store parameters.
645 */
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700646DCVS_PARAM_SHOW(freq_change_us, (core->freq_change_us))
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700647
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700648DCVS_ALGO_PARAM(disable_pc_threshold)
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700649DCVS_ALGO_PARAM(em_win_size_min_us)
650DCVS_ALGO_PARAM(em_win_size_max_us)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700651DCVS_ALGO_PARAM(em_max_util_pct)
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700652DCVS_ALGO_PARAM(group_id)
653DCVS_ALGO_PARAM(max_freq_chg_time_us)
654DCVS_ALGO_PARAM(slack_mode_dynamic)
655DCVS_ALGO_PARAM(slack_time_min_us)
656DCVS_ALGO_PARAM(slack_time_max_us)
657DCVS_ALGO_PARAM(slack_weight_thresh_pct)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700658DCVS_ALGO_PARAM(ss_iobusy_conv)
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700659DCVS_ALGO_PARAM(ss_win_size_min_us)
660DCVS_ALGO_PARAM(ss_win_size_max_us)
661DCVS_ALGO_PARAM(ss_util_pct)
662
663DCVS_ENERGY_PARAM(active_coeff_a)
664DCVS_ENERGY_PARAM(active_coeff_b)
665DCVS_ENERGY_PARAM(active_coeff_c)
666DCVS_ENERGY_PARAM(leakage_coeff_a)
667DCVS_ENERGY_PARAM(leakage_coeff_b)
668DCVS_ENERGY_PARAM(leakage_coeff_c)
669DCVS_ENERGY_PARAM(leakage_coeff_d)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700670
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700671DCVS_PARAM_STORE(thermal_poll_ms)
672
Steve Muckle118f47b2012-10-17 16:09:37 -0700673static ssize_t msm_dcvs_attr_freq_tbl_show(struct kobject *kobj,
674 struct kobj_attribute *attr,
675 char *buf)
676{
677 struct msm_dcvs_freq_entry *freq_tbl;
678 char *buf_idx = buf;
679 int i, len;
680 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, freq_tbl);
681
682 freq_tbl = core->info->freq_tbl;
683 *buf_idx = '\0';
684
685 /* limit the number of frequencies we will print into
686 * the PAGE_SIZE sysfs show buffer. */
687 if (core->info->power_param.num_freq > 64)
688 return 0;
689
690 for (i = 0; i < core->info->power_param.num_freq; i++) {
691 if (freq_tbl[i].is_trans_level) {
692 len = snprintf(buf_idx, 10, "%7d ", freq_tbl[i].freq);
693 /* buf_idx always points at terminating null */
694 buf_idx += len;
695 }
696 }
697 /* overwrite final trailing space with newline */
698 if (buf_idx > buf)
699 *(buf_idx - 1) = '\n';
700
701 return buf_idx - buf;
702}
703
704static ssize_t msm_dcvs_attr_freq_tbl_store(struct kobject *kobj,
705 struct kobj_attribute *attr,
706 const char *buf,
707 size_t count)
708{
709 struct msm_dcvs_freq_entry *freq_tbl;
710 uint32_t freq;
711 int i, ret;
712 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, freq_tbl);
713
714 freq_tbl = core->info->freq_tbl;
715
716 ret = kstrtouint(buf, 10, &freq);
717 if (ret) {
718 __err("Invalid input %s for freq_tbl\n", buf);
719 return count;
720 }
721
722 for (i = 0; i < core->info->power_param.num_freq; i++)
723 if (freq_tbl[i].freq == freq) {
724 freq_tbl[i].is_trans_level ^= 1;
725 break;
726 }
727
728 if (i >= core->info->power_param.num_freq) {
729 __err("Invalid frequency for freq_tbl: %d\n", freq);
730 return count;
731 }
732
733 ret = msm_dcvs_scm_set_power_params(core->dcvs_core_id,
734 &core->info->power_param,
735 &core->info->freq_tbl[0],
736 &core->coeffs);
737 if (ret) {
738 freq_tbl[i].is_trans_level ^= 1;
739 __err("Error %d in toggling freq %d (orig enable val %d)\n",
740 ret, freq_tbl[i].freq, freq_tbl[i].is_trans_level);
741 }
742 return count;
743}
744
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700745static int msm_dcvs_setup_core_sysfs(struct dcvs_core *core)
746{
747 int ret = 0;
748 struct kobject *core_kobj = NULL;
Steve Muckle118f47b2012-10-17 16:09:37 -0700749 const int attr_count = 25;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700750
751 BUG_ON(!cores_kobj);
752
753 core->attrib.attrib_group.attrs =
754 kzalloc(attr_count * sizeof(struct attribute *), GFP_KERNEL);
755
756 if (!core->attrib.attrib_group.attrs) {
757 ret = -ENOMEM;
758 goto done;
759 }
760
Abhijeet Dharmapurikar35876382012-09-13 16:09:51 -0700761 DCVS_RO_ATTRIB(0, freq_change_us);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700762
Abhijeet Dharmapurikar35876382012-09-13 16:09:51 -0700763 DCVS_RW_ATTRIB(1, disable_pc_threshold);
764 DCVS_RW_ATTRIB(2, em_win_size_min_us);
765 DCVS_RW_ATTRIB(3, em_win_size_max_us);
766 DCVS_RW_ATTRIB(4, em_max_util_pct);
767 DCVS_RW_ATTRIB(5, group_id);
768 DCVS_RW_ATTRIB(6, max_freq_chg_time_us);
769 DCVS_RW_ATTRIB(7, slack_mode_dynamic);
770 DCVS_RW_ATTRIB(8, slack_weight_thresh_pct);
771 DCVS_RW_ATTRIB(9, slack_time_min_us);
772 DCVS_RW_ATTRIB(10, slack_time_max_us);
773 DCVS_RW_ATTRIB(11, ss_iobusy_conv);
774 DCVS_RW_ATTRIB(12, ss_win_size_min_us);
775 DCVS_RW_ATTRIB(13, ss_win_size_max_us);
776 DCVS_RW_ATTRIB(14, ss_util_pct);
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700777
Abhijeet Dharmapurikar35876382012-09-13 16:09:51 -0700778 DCVS_RW_ATTRIB(15, active_coeff_a);
779 DCVS_RW_ATTRIB(16, active_coeff_b);
780 DCVS_RW_ATTRIB(17, active_coeff_c);
781 DCVS_RW_ATTRIB(18, leakage_coeff_a);
782 DCVS_RW_ATTRIB(19, leakage_coeff_b);
783 DCVS_RW_ATTRIB(20, leakage_coeff_c);
784 DCVS_RW_ATTRIB(21, leakage_coeff_d);
785 DCVS_RW_ATTRIB(22, thermal_poll_ms);
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700786
Steve Muckle118f47b2012-10-17 16:09:37 -0700787 DCVS_RW_ATTRIB(23, freq_tbl);
788
789 core->attrib.attrib_group.attrs[24] = NULL;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700790
791 core_kobj = kobject_create_and_add(core->core_name, cores_kobj);
792 if (!core_kobj) {
793 ret = -ENOMEM;
794 goto done;
795 }
796
797 ret = sysfs_create_group(core_kobj, &core->attrib.attrib_group);
798 if (ret)
799 __err("Cannot create core %s attr group\n", core->core_name);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700800
801done:
802 if (ret) {
803 kfree(core->attrib.attrib_group.attrs);
804 kobject_del(core_kobj);
805 }
806
807 return ret;
808}
809
Steve Muckle5e5a60d2012-10-09 13:25:22 -0700810static int get_core_offset(enum msm_dcvs_core_type type, int num)
811{
812 int offset = -EINVAL;
813
814 switch (type) {
815 case MSM_DCVS_CORE_TYPE_CPU:
816 offset = CPU_OFFSET + num;
817 BUG_ON(offset >= GPU_OFFSET);
818 break;
819 case MSM_DCVS_CORE_TYPE_GPU:
820 offset = GPU_OFFSET + num;
821 BUG_ON(offset >= CORES_MAX);
822 break;
823 default:
824 BUG();
825 }
826
827 return offset;
828}
829
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700830/* Return the core and initialize non platform data specific numbers in it */
831static struct dcvs_core *msm_dcvs_add_core(enum msm_dcvs_core_type type,
832 int num)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700833{
834 struct dcvs_core *core = NULL;
835 int i;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700836 char name[CORE_NAME_MAX];
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700837
Steve Muckle5e5a60d2012-10-09 13:25:22 -0700838 i = get_core_offset(type, num);
839 if (i < 0)
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700840 return NULL;
Steve Muckle5e5a60d2012-10-09 13:25:22 -0700841
842 if (type == MSM_DCVS_CORE_TYPE_CPU)
843 snprintf(name, CORE_NAME_MAX, "cpu%d", num);
844 else
845 snprintf(name, CORE_NAME_MAX, "gpu%d", num);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700846
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700847 core = &core_list[i];
848 core->dcvs_core_id = i;
Abhijeet Dharmapurikar2ebc0fe2012-09-12 14:05:13 -0700849 strlcpy(core->core_name, name, CORE_NAME_MAX);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700850 spin_lock_init(&core->pending_freq_lock);
851 spin_lock_init(&core->idle_state_change_lock);
852 hrtimer_init(&core->slack_timer,
853 CLOCK_MONOTONIC, HRTIMER_MODE_REL_PINNED);
854 core->slack_timer.function = msm_dcvs_core_slack_timer;
Abhijeet Dharmapurikar2ebc0fe2012-09-12 14:05:13 -0700855 return core;
856}
857
858/* Return the core if found or add to list if @add_to_list is true */
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700859static struct dcvs_core *msm_dcvs_get_core(int offset)
Abhijeet Dharmapurikar2ebc0fe2012-09-12 14:05:13 -0700860{
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700861 /* if the handle is still not set bug */
862 BUG_ON(core_list[offset].dcvs_core_id == -1);
863 return &core_list[offset];
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700864}
865
Steve Mucklea9aac292012-11-02 15:41:00 -0700866void msm_dcvs_register_cpu_freq(uint32_t freq, uint32_t voltage)
867{
868 BUG_ON(freq == 0 || voltage == 0 ||
869 num_cpu_freqs == DCVS_MAX_NUM_FREQS);
870
871 cpu_freq_tbl[num_cpu_freqs].freq = freq;
872 cpu_freq_tbl[num_cpu_freqs].voltage = voltage;
873
874 num_cpu_freqs++;
875}
876
877static void update_cpu_dcvs_params(struct msm_dcvs_core_info *info)
878{
879 int i;
880
881 BUG_ON(num_cpu_freqs == 0);
882
883 info->freq_tbl = cpu_freq_tbl;
884 info->power_param.num_freq = num_cpu_freqs;
885
886 if (!dcvs_pdata || dcvs_pdata->num_sync_rules == 0)
887 return;
888
889 /* the first sync rule shows what the turbo frequencies are -
890 * these frequencies need energy offsets set */
891 for (i = 0; i < DCVS_MAX_NUM_FREQS && cpu_freq_tbl[i].freq != 0; i++)
892 if (cpu_freq_tbl[i].freq > dcvs_pdata->sync_rules[0].cpu_khz) {
893 cpu_freq_tbl[i].active_energy_offset = 100;
894 cpu_freq_tbl[i].leakage_energy_offset = 100;
895 }
896}
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700897
898int msm_dcvs_register_core(
899 enum msm_dcvs_core_type type,
900 int type_core_num,
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700901 struct msm_dcvs_core_info *info,
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700902 int (*set_frequency)(int type_core_num, unsigned int freq),
903 unsigned int (*get_frequency)(int type_core_num),
904 int (*idle_enable)(int type_core_num,
Abhijeet Dharmapurikar6e9b34f2012-09-10 16:03:39 -0700905 enum msm_core_control_event event),
Steve Muckle93bb4252012-11-12 14:20:39 -0800906 int (*set_floor_frequency)(int type_core_num, unsigned int freq),
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700907 int sensor)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700908{
909 int ret = -EINVAL;
Steve Muckle5e5a60d2012-10-09 13:25:22 -0700910 int offset;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700911 struct dcvs_core *core = NULL;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700912 uint32_t ret1;
913 uint32_t ret2;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700914
Steve Muckle5e5a60d2012-10-09 13:25:22 -0700915 offset = get_core_offset(type, type_core_num);
916 if (offset < 0)
917 return ret;
918 if (core_list[offset].dcvs_core_id != -1)
919 return core_list[offset].dcvs_core_id;
920
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700921 core = msm_dcvs_add_core(type, type_core_num);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700922 if (!core)
923 return ret;
924
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700925 core->type = type;
926 core->type_core_num = type_core_num;
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700927 core->set_frequency = set_frequency;
928 core->get_frequency = get_frequency;
Abhijeet Dharmapurikar6e9b34f2012-09-10 16:03:39 -0700929 core->idle_enable = idle_enable;
Steve Muckle93bb4252012-11-12 14:20:39 -0800930 core->set_floor_frequency = set_floor_frequency;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700931 core->pending_freq = STOP_FREQ_CHANGE;
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700932
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700933 core->info = info;
Steve Mucklea9aac292012-11-02 15:41:00 -0700934 if (type == MSM_DCVS_CORE_TYPE_CPU)
935 update_cpu_dcvs_params(info);
936
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700937 memcpy(&core->algo_param, &info->algo_param,
938 sizeof(struct msm_dcvs_algo_param));
939
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700940 memcpy(&core->coeffs, &info->energy_coeffs,
941 sizeof(struct msm_dcvs_energy_curve_coeffs));
942
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700943 /*
944 * The tz expects cpu0 to represent bit 0 in the mask, however the
945 * dcvs_core_id needs to start from 1, dcvs_core_id = 0 is used to
946 * indicate that this request is not associated with any core.
947 * mpdecision
948 */
949 info->core_param.core_bitmask_id
950 = 1 << (core->dcvs_core_id - CPU_OFFSET);
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700951 core->sensor = sensor;
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700952
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700953 ret = msm_dcvs_scm_register_core(core->dcvs_core_id, &info->core_param);
954 if (ret) {
955 __err("%s: scm register core fail handle = %d ret = %d\n",
956 __func__, core->dcvs_core_id, ret);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700957 goto bail;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700958 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700959
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700960 ret = msm_dcvs_scm_set_algo_params(core->dcvs_core_id,
961 &info->algo_param);
962 if (ret) {
963 __err("%s: scm algo params failed ret = %d\n", __func__, ret);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700964 goto bail;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700965 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700966
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700967 ret = msm_dcvs_scm_set_power_params(core->dcvs_core_id,
968 &info->power_param,
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700969 &info->freq_tbl[0], &core->coeffs);
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700970 if (ret) {
971 __err("%s: scm power params failed ret = %d\n", __func__, ret);
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700972 goto bail;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700973 }
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700974
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700975 ret = msm_dcvs_scm_event(core->dcvs_core_id, MSM_DCVS_SCM_CORE_ONLINE,
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700976 core->actual_freq, 0, &ret1, &ret2);
977 if (ret)
978 goto bail;
979
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700980 ret = msm_dcvs_setup_core_sysfs(core);
981 if (ret) {
982 __err("Unable to setup core %s sysfs\n", core->core_name);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700983 goto bail;
984 }
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700985 core->idle_entered = -1;
Abhijeet Dharmapurikarbbb52fe2012-08-31 20:31:16 -0700986 init_waitqueue_head(&core->wait_q);
987 core->task = kthread_run(msm_dcvs_do_freq, (void *)core,
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700988 "msm_dcvs/%d", core->dcvs_core_id);
989 ret = core->dcvs_core_id;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700990 return ret;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700991bail:
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700992 core->dcvs_core_id = -1;
993 return -EINVAL;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700994}
995EXPORT_SYMBOL(msm_dcvs_register_core);
996
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700997void msm_dcvs_update_limits(int dcvs_core_id)
Abhijeet Dharmapurikarc43f0db2012-08-31 20:42:53 -0700998{
999 struct dcvs_core *core;
1000
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001001 if (dcvs_core_id < CPU_OFFSET || dcvs_core_id > CORES_MAX) {
1002 __err("%s invalid dcvs_core_id = %d returning -EINVAL\n",
1003 __func__, dcvs_core_id);
Abhijeet Dharmapurikarc43f0db2012-08-31 20:42:53 -07001004 return;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001005 }
Abhijeet Dharmapurikarc43f0db2012-08-31 20:42:53 -07001006
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001007 core = msm_dcvs_get_core(dcvs_core_id);
1008 core->actual_freq = core->get_frequency(core->type_core_num);
Abhijeet Dharmapurikarc43f0db2012-08-31 20:42:53 -07001009}
1010
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001011int msm_dcvs_freq_sink_start(int dcvs_core_id)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001012{
1013 int ret = -EINVAL;
1014 struct dcvs_core *core = NULL;
Eugene Seah76af9832012-03-28 18:43:53 -06001015 uint32_t ret1;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001016 unsigned long flags;
Abhijeet Dharmapurikar8d843b32012-09-13 18:33:20 -07001017 int new_freq;
1018 int timer_interval_us;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001019
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001020 if (dcvs_core_id < CPU_OFFSET || dcvs_core_id > CORES_MAX) {
1021 __err("%s invalid dcvs_core_id = %d returning -EINVAL\n",
1022 __func__, dcvs_core_id);
1023 return -EINVAL;
1024 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001025
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001026 core = msm_dcvs_get_core(dcvs_core_id);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001027 if (!core)
1028 return ret;
1029
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001030 core->actual_freq = core->get_frequency(core->type_core_num);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001031
1032 spin_lock_irqsave(&core->pending_freq_lock, flags);
1033 /* mark that we are ready to accept new frequencies */
1034 request_freq_change(core, NO_OUTSTANDING_FREQ_CHANGE);
1035 spin_unlock_irqrestore(&core->pending_freq_lock, flags);
1036
1037 spin_lock_irqsave(&core->idle_state_change_lock, flags);
1038 core->idle_entered = -1;
1039 spin_unlock_irqrestore(&core->idle_state_change_lock, flags);
1040
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001041 /* Notify TZ to start receiving idle info for the core */
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001042 ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_DCVS_ENABLE, 1, &ret1);
1043
Abhijeet Dharmapurikar8d843b32012-09-13 18:33:20 -07001044 ret = msm_dcvs_scm_event(
1045 core->dcvs_core_id, MSM_DCVS_SCM_CORE_ONLINE, core->actual_freq,
1046 0, &new_freq, &timer_interval_us);
1047 if (ret)
1048 __err("Error (%d) DCVS sending online for %s\n",
1049 ret, core->core_name);
1050
1051 if (new_freq != 0) {
1052 spin_lock_irqsave(&core->pending_freq_lock, flags);
1053 request_freq_change(core, new_freq);
1054 spin_unlock_irqrestore(&core->pending_freq_lock, flags);
1055 }
1056 force_start_slack_timer(core, timer_interval_us);
1057
Steve Mucklec1785c32012-11-13 14:27:43 -08001058 core->flags |= CORE_FLAG_TEMP_UPDATE;
1059 INIT_DELAYED_WORK(&core->temperature_work, msm_dcvs_report_temp_work);
1060 schedule_delayed_work(&core->temperature_work,
1061 msecs_to_jiffies(core->info->thermal_poll_ms));
Abhijeet Dharmapurikar8d843b32012-09-13 18:33:20 -07001062
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001063 core->idle_enable(core->type_core_num, MSM_DCVS_ENABLE_IDLE_PULSE);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001064 return 0;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001065}
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -07001066EXPORT_SYMBOL(msm_dcvs_freq_sink_start);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001067
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001068int msm_dcvs_freq_sink_stop(int dcvs_core_id)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001069{
1070 int ret = -EINVAL;
1071 struct dcvs_core *core = NULL;
Eugene Seah76af9832012-03-28 18:43:53 -06001072 uint32_t ret1;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001073 uint32_t freq;
1074 unsigned long flags;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001075
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001076 if (dcvs_core_id < 0 || dcvs_core_id > CORES_MAX) {
1077 pr_err("%s invalid dcvs_core_id = %d returning -EINVAL\n",
1078 __func__, dcvs_core_id);
1079 return -EINVAL;
1080 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001081
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001082 core = msm_dcvs_get_core(dcvs_core_id);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001083 if (!core) {
1084 __err("couldn't find core for coreid = %d\n", dcvs_core_id);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001085 return ret;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001086 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001087
Steve Mucklec1785c32012-11-13 14:27:43 -08001088 core->flags &= ~CORE_FLAG_TEMP_UPDATE;
1089 cancel_delayed_work(&core->temperature_work);
1090
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001091 core->idle_enable(core->type_core_num, MSM_DCVS_DISABLE_IDLE_PULSE);
1092 /* Notify TZ to stop receiving idle info for the core */
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001093 ret = msm_dcvs_scm_event(core->dcvs_core_id, MSM_DCVS_SCM_DCVS_ENABLE,
1094 0, core->actual_freq, &freq, &ret1);
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001095 core->idle_enable(core->type_core_num,
1096 MSM_DCVS_ENABLE_HIGH_LATENCY_MODES);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001097 spin_lock_irqsave(&core->pending_freq_lock, flags);
1098 /* flush out all the pending freq changes */
1099 request_freq_change(core, STOP_FREQ_CHANGE);
1100 spin_unlock_irqrestore(&core->pending_freq_lock, flags);
1101 force_stop_slack_timer(core);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001102
1103 return 0;
1104}
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -07001105EXPORT_SYMBOL(msm_dcvs_freq_sink_stop);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001106
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001107int msm_dcvs_idle(int dcvs_core_id, enum msm_core_idle_state state,
1108 uint32_t iowaited)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001109{
1110 int ret = 0;
1111 struct dcvs_core *core = NULL;
Eugene Seah76af9832012-03-28 18:43:53 -06001112 uint32_t timer_interval_us = 0;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001113 uint32_t r0, r1;
1114
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001115 if (dcvs_core_id < CPU_OFFSET || dcvs_core_id > CORES_MAX) {
1116 pr_err("invalid dcvs_core_id = %d ret -EINVAL\n", dcvs_core_id);
1117 return -EINVAL;
1118 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001119
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001120 core = msm_dcvs_get_core(dcvs_core_id);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001121
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001122 switch (state) {
1123 case MSM_DCVS_IDLE_ENTER:
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001124 stop_slack_timer(core);
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001125 ret = msm_dcvs_scm_event(core->dcvs_core_id,
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001126 MSM_DCVS_SCM_IDLE_ENTER, 0, 0, &r0, &r1);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001127 if (ret < 0 && ret != -13)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001128 __err("Error (%d) sending idle enter for %s\n",
1129 ret, core->core_name);
Abhijeet Dharmapurikar07cf2ff2012-09-13 19:05:13 -07001130 trace_msm_dcvs_idle("idle_enter_exit", core->core_name, 1);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001131 break;
1132
1133 case MSM_DCVS_IDLE_EXIT:
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001134 ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_IDLE_EXIT,
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001135 iowaited, &timer_interval_us);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001136 if (ret)
1137 __err("Error (%d) sending idle exit for %s\n",
1138 ret, core->core_name);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001139 start_slack_timer(core, timer_interval_us);
Abhijeet Dharmapurikar07cf2ff2012-09-13 19:05:13 -07001140 trace_msm_dcvs_idle("idle_enter_exit", core->core_name, 0);
1141 trace_msm_dcvs_iowait("iowait", core->core_name, iowaited);
1142 trace_msm_dcvs_slack_time("slack_timer_dcvs", core->core_name,
1143 timer_interval_us);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001144 break;
1145 }
1146
1147 return ret;
1148}
1149EXPORT_SYMBOL(msm_dcvs_idle);
1150
1151static int __init msm_dcvs_late_init(void)
1152{
1153 struct kobject *module_kobj = NULL;
1154 int ret = 0;
1155
1156 module_kobj = kset_find_obj(module_kset, KBUILD_MODNAME);
1157 if (!module_kobj) {
1158 pr_err("%s: cannot find kobject for module %s\n",
1159 __func__, KBUILD_MODNAME);
1160 ret = -ENOENT;
1161 goto err;
1162 }
1163
1164 cores_kobj = kobject_create_and_add("cores", module_kobj);
1165 if (!cores_kobj) {
1166 __err("Cannot create %s kobject\n", "cores");
1167 ret = -ENOMEM;
1168 goto err;
1169 }
1170
1171 debugfs_base = debugfs_create_dir("msm_dcvs", NULL);
1172 if (!debugfs_base) {
1173 __err("Cannot create debugfs base %s\n", "msm_dcvs");
1174 ret = -ENOENT;
1175 goto err;
1176 }
1177
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001178err:
1179 if (ret) {
1180 kobject_del(cores_kobj);
1181 cores_kobj = NULL;
1182 debugfs_remove(debugfs_base);
1183 }
1184
1185 return ret;
1186}
1187late_initcall(msm_dcvs_late_init);
1188
Steve Mucklea9aac292012-11-02 15:41:00 -07001189static int __devinit dcvs_probe(struct platform_device *pdev)
1190{
1191 if (pdev->dev.platform_data)
1192 dcvs_pdata = pdev->dev.platform_data;
1193
1194 return 0;
1195}
1196
1197static struct platform_driver dcvs_driver = {
1198 .probe = dcvs_probe,
1199 .driver = {
1200 .name = "dcvs",
1201 .owner = THIS_MODULE,
1202 },
1203};
1204
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001205static int __init msm_dcvs_early_init(void)
1206{
1207 int ret = 0;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001208 int i;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001209
Steve Mucklea9aac292012-11-02 15:41:00 -07001210 platform_driver_register(&dcvs_driver);
1211
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001212 if (!msm_dcvs_enabled) {
1213 __info("Not enabled (%d)\n", msm_dcvs_enabled);
1214 return 0;
1215 }
1216
Abhijeet Dharmapurikar3edb5de2012-09-13 11:02:03 -07001217
1218 /* Only need about 32kBytes for normal operation */
1219 ret = msm_dcvs_scm_init(SZ_32K);
1220 if (ret) {
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001221 __err("Unable to initialize DCVS err=%d\n", ret);
Abhijeet Dharmapurikar3edb5de2012-09-13 11:02:03 -07001222 goto done;
1223 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001224
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001225 for (i = 0; i < CORES_MAX; i++)
1226 core_list[i].dcvs_core_id = -1;
Abhijeet Dharmapurikar3edb5de2012-09-13 11:02:03 -07001227done:
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001228 return ret;
1229}
1230postcore_initcall(msm_dcvs_early_init);