blob: 1a919fcf577599db1a0b5f78c0fe977a8b82369f [file] [log] [blame]
Steve Muckle2f8214d2013-05-08 10:30:54 -07001/* Copyright (c) 2012-2013, 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;
Steve Muckle8d0782e2012-12-06 14:31:00 -080052 struct kobj_attribute ss_no_corr_below_freq;
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;
Steve Muckle74da4a02012-11-28 16:29:46 -080068 struct kobj_attribute offset_tbl;
Steve Muckle118f47b2012-10-17 16:09:37 -070069
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070070 struct attribute_group attrib_group;
71};
72
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -070073enum pending_freq_state {
74 /*
75 * used by the thread to check if pending_freq was updated while it was
76 * setting previous frequency - this is written to and used by the
77 * freq updating thread
78 */
79 NO_OUTSTANDING_FREQ_CHANGE = 0,
80
81 /*
82 * This request is set to indicate that the governor is stopped and no
83 * more frequency change requests are accepted untill it starts again.
84 * This is checked/used by the threads that want to change the freq
85 */
86 STOP_FREQ_CHANGE = -1,
87
88 /*
89 * Any other +ve value means that a freq change was requested and the
90 * thread has not gotten around to update it
91 *
92 * Any other -ve value means that this is the last freq change i.e. a
93 * freq change was requested but the thread has not run yet and
94 * meanwhile the governor was stopped.
95 */
96};
97
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070098struct dcvs_core {
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -070099 spinlock_t idle_state_change_lock;
100 /* 0 when not idle (busy) 1 when idle and -1 when governor starts and
101 * we dont know whether the next call is going to be idle enter or exit
102 */
103 int idle_entered;
104
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700105 enum msm_dcvs_core_type type;
106 /* this is the number in each type for example cpu 0,1,2 and gpu 0,1 */
107 int type_core_num;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700108 char core_name[CORE_NAME_MAX];
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700109 uint32_t actual_freq;
110 uint32_t freq_change_us;
111
112 uint32_t max_time_us; /* core param */
113
114 struct msm_dcvs_algo_param algo_param;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700115 struct msm_dcvs_energy_curve_coeffs coeffs;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700116
117 /* private */
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700118 ktime_t time_start;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700119 struct task_struct *task;
120 struct core_attribs attrib;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700121 uint32_t dcvs_core_id;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700122 struct msm_dcvs_core_info *info;
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700123 int sensor;
Abhijeet Dharmapurikarbbb52fe2012-08-31 20:31:16 -0700124 wait_queue_head_t wait_q;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700125
126 int (*set_frequency)(int type_core_num, unsigned int freq);
127 unsigned int (*get_frequency)(int type_core_num);
128 int (*idle_enable)(int type_core_num,
Abhijeet Dharmapurikar6e9b34f2012-09-10 16:03:39 -0700129 enum msm_core_control_event event);
Steve Muckle93bb4252012-11-12 14:20:39 -0800130 int (*set_floor_frequency)(int type_core_num, unsigned int freq);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700131
132 spinlock_t pending_freq_lock;
133 int pending_freq;
134
135 struct hrtimer slack_timer;
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700136 struct delayed_work temperature_work;
Steve Mucklec1785c32012-11-13 14:27:43 -0800137 int flags;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700138};
139
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700140static int msm_dcvs_enabled = 1;
141module_param_named(enable, msm_dcvs_enabled, int, S_IRUGO | S_IWUSR | S_IWGRP);
142
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700143static struct dentry *debugfs_base;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700144
145static struct dcvs_core core_list[CORES_MAX];
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700146
147static struct kobject *cores_kobj;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700148
Steve Mucklea9aac292012-11-02 15:41:00 -0700149#define DCVS_MAX_NUM_FREQS 15
150static struct msm_dcvs_freq_entry cpu_freq_tbl[DCVS_MAX_NUM_FREQS];
151static unsigned num_cpu_freqs;
152static struct msm_dcvs_platform_data *dcvs_pdata;
153
Steve Muckle520b3f22012-12-06 14:34:38 -0800154static DEFINE_MUTEX(param_update_mutex);
Steve Muckle43a980c2012-11-19 16:38:55 -0800155static DEFINE_MUTEX(gpu_floor_mutex);
156
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700157static void force_stop_slack_timer(struct dcvs_core *core)
158{
159 unsigned long flags;
160
161 spin_lock_irqsave(&core->idle_state_change_lock, flags);
162 hrtimer_cancel(&core->slack_timer);
163 spin_unlock_irqrestore(&core->idle_state_change_lock, flags);
164}
165
Abhijeet Dharmapurikar8d843b32012-09-13 18:33:20 -0700166static void force_start_slack_timer(struct dcvs_core *core, int slack_us)
167{
168 unsigned long flags;
169 int ret;
170
171 spin_lock_irqsave(&core->idle_state_change_lock, flags);
172
173 /*
174 * only start the timer if governor is not stopped
175 */
176 if (slack_us != 0) {
177 ret = hrtimer_start(&core->slack_timer,
178 ktime_set(0, slack_us * 1000),
179 HRTIMER_MODE_REL_PINNED);
180 if (ret) {
181 pr_err("%s Failed to start timer ret = %d\n",
182 core->core_name, ret);
183 }
184 }
185
186 spin_unlock_irqrestore(&core->idle_state_change_lock, flags);
187}
188
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700189static void stop_slack_timer(struct dcvs_core *core)
190{
191 unsigned long flags;
192
193 spin_lock_irqsave(&core->idle_state_change_lock, flags);
194 /* err only for cpu type's GPU's can do idle exit consecutively */
195 if (core->idle_entered == 1 && !(core->dcvs_core_id >= GPU_OFFSET))
196 __err("%s trying to reenter idle", core->core_name);
197 core->idle_entered = 1;
198 hrtimer_cancel(&core->slack_timer);
199 core->idle_entered = 1;
200 spin_unlock_irqrestore(&core->idle_state_change_lock, flags);
201}
202
203static void start_slack_timer(struct dcvs_core *core, int slack_us)
204{
205 unsigned long flags1, flags2;
206 int ret;
207
208 spin_lock_irqsave(&core->idle_state_change_lock, flags2);
209
210 spin_lock_irqsave(&core->pending_freq_lock, flags1);
211
212 /* err only for cpu type's GPU's can do idle enter consecutively */
213 if (core->idle_entered == 0 && !(core->dcvs_core_id >= GPU_OFFSET))
214 __err("%s trying to reexit idle", core->core_name);
215 core->idle_entered = 0;
216 /*
217 * only start the timer if governor is not stopped
218 */
219 if (slack_us != 0
220 && !(core->pending_freq < NO_OUTSTANDING_FREQ_CHANGE)) {
221 ret = hrtimer_start(&core->slack_timer,
222 ktime_set(0, slack_us * 1000),
223 HRTIMER_MODE_REL_PINNED);
224 if (ret) {
225 pr_err("%s Failed to start timer ret = %d\n",
226 core->core_name, ret);
227 }
228 }
229 spin_unlock_irqrestore(&core->pending_freq_lock, flags1);
230
231 spin_unlock_irqrestore(&core->idle_state_change_lock, flags2);
232}
233
234static void restart_slack_timer(struct dcvs_core *core, int slack_us)
235{
236 unsigned long flags1, flags2;
237 int ret;
238
239 spin_lock_irqsave(&core->idle_state_change_lock, flags2);
240
241 hrtimer_cancel(&core->slack_timer);
242
243 spin_lock_irqsave(&core->pending_freq_lock, flags1);
244
245 /*
246 * only start the timer if idle is not entered
247 * and governor is not stopped
248 */
249 if (slack_us != 0 && (core->idle_entered != 1)
250 && !(core->pending_freq < NO_OUTSTANDING_FREQ_CHANGE)) {
251 ret = hrtimer_start(&core->slack_timer,
252 ktime_set(0, slack_us * 1000),
253 HRTIMER_MODE_REL_PINNED);
254 if (ret) {
255 pr_err("%s Failed to start timer ret = %d\n",
256 core->core_name, ret);
257 }
258 }
259 spin_unlock_irqrestore(&core->pending_freq_lock, flags1);
260 spin_unlock_irqrestore(&core->idle_state_change_lock, flags2);
261}
262
Steve Muckle28ddcdd2012-11-21 10:12:39 -0800263void msm_dcvs_apply_gpu_floor(unsigned long cpu_freq)
Steve Muckle93bb4252012-11-12 14:20:39 -0800264{
Steve Muckle28ddcdd2012-11-21 10:12:39 -0800265 static unsigned long curr_cpu0_freq;
266 unsigned long gpu_floor_freq = 0;
Steve Muckle93bb4252012-11-12 14:20:39 -0800267 struct dcvs_core *gpu;
Steve Muckle28ddcdd2012-11-21 10:12:39 -0800268 int i;
Steve Muckle93bb4252012-11-12 14:20:39 -0800269
270 if (!dcvs_pdata)
271 return;
272
Steve Muckle28ddcdd2012-11-21 10:12:39 -0800273 mutex_lock(&gpu_floor_mutex);
274
275 if (cpu_freq)
276 curr_cpu0_freq = cpu_freq;
277
Steve Muckle93bb4252012-11-12 14:20:39 -0800278 for (i = 0; i < dcvs_pdata->num_sync_rules; i++)
Steve Muckle28ddcdd2012-11-21 10:12:39 -0800279 if (curr_cpu0_freq > dcvs_pdata->sync_rules[i].cpu_khz) {
Steve Muckle93bb4252012-11-12 14:20:39 -0800280 gpu_floor_freq =
281 dcvs_pdata->sync_rules[i].gpu_floor_khz;
282 break;
283 }
284
Steve Muckle28ddcdd2012-11-21 10:12:39 -0800285 if (num_online_cpus() > 1)
286 gpu_floor_freq = max(gpu_floor_freq,
287 dcvs_pdata->gpu_max_nom_khz);
288
289 if (!gpu_floor_freq) {
290 mutex_unlock(&gpu_floor_mutex);
Steve Muckle93bb4252012-11-12 14:20:39 -0800291 return;
Steve Muckle28ddcdd2012-11-21 10:12:39 -0800292 }
Steve Muckle93bb4252012-11-12 14:20:39 -0800293
294 for (i = GPU_OFFSET; i < CORES_MAX; i++) {
295 gpu = &core_list[i];
296 if (gpu->dcvs_core_id == -1)
297 continue;
Steve Muckle28ddcdd2012-11-21 10:12:39 -0800298
Steve Muckle43a980c2012-11-19 16:38:55 -0800299 if (gpu->pending_freq != STOP_FREQ_CHANGE &&
Steve Mucklea46930c2012-11-28 17:00:29 -0800300 gpu->set_floor_frequency) {
Steve Muckle93bb4252012-11-12 14:20:39 -0800301 gpu->set_floor_frequency(gpu->type_core_num,
302 gpu_floor_freq);
Steve Mucklea46930c2012-11-28 17:00:29 -0800303 /* TZ will know about a freq change (if any)
304 * at next idle exit. */
305 gpu->actual_freq =
306 gpu->get_frequency(gpu->type_core_num);
307 }
Steve Muckle93bb4252012-11-12 14:20:39 -0800308 }
Steve Muckle28ddcdd2012-11-21 10:12:39 -0800309
310 mutex_unlock(&gpu_floor_mutex);
Steve Muckle93bb4252012-11-12 14:20:39 -0800311}
312
Steve Muckle520b3f22012-12-06 14:34:38 -0800313static void check_power_collapse_modes(struct dcvs_core *core)
314{
315 struct msm_dcvs_algo_param *params;
316
317 params = &core_list[CPU_OFFSET + num_online_cpus() - 1].algo_param;
318
319 if (core->actual_freq >= params->disable_pc_threshold)
320 core->idle_enable(core->type_core_num,
321 MSM_DCVS_DISABLE_HIGH_LATENCY_MODES);
322 else
323 core->idle_enable(core->type_core_num,
324 MSM_DCVS_ENABLE_HIGH_LATENCY_MODES);
325}
326
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700327static int __msm_dcvs_change_freq(struct dcvs_core *core)
328{
329 int ret = 0;
330 unsigned long flags = 0;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700331 int requested_freq = 0;
332 ktime_t time_start;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700333 uint32_t slack_us = 0;
334 uint32_t ret1 = 0;
335
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700336 spin_lock_irqsave(&core->pending_freq_lock, flags);
Steve Muckle80e0b7b2012-11-19 15:46:39 -0800337 if (core->pending_freq == STOP_FREQ_CHANGE)
338 goto out;
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700339repeat:
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700340 BUG_ON(!core->pending_freq);
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700341
342 requested_freq = core->pending_freq;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700343 time_start = core->time_start;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700344 core->time_start = ns_to_ktime(0);
345
Steve Muckle80e0b7b2012-11-19 15:46:39 -0800346 core->pending_freq = NO_OUTSTANDING_FREQ_CHANGE;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700347
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700348 if (requested_freq == core->actual_freq)
349 goto out;
350
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700351 spin_unlock_irqrestore(&core->pending_freq_lock, flags);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700352
Steve Muckle93bb4252012-11-12 14:20:39 -0800353 if (core->type == MSM_DCVS_CORE_TYPE_CPU &&
354 core->type_core_num == 0)
Steve Muckle28ddcdd2012-11-21 10:12:39 -0800355 msm_dcvs_apply_gpu_floor(requested_freq);
Steve Muckle93bb4252012-11-12 14:20:39 -0800356
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700357 /**
358 * Call the frequency sink driver to change the frequency
359 * We will need to get back the actual frequency in KHz and
360 * the record the time taken to change it.
361 */
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700362 ret = core->set_frequency(core->type_core_num, requested_freq);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700363 if (ret <= 0)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700364 __err("Core %s failed to set freq %u\n",
365 core->core_name, requested_freq);
Eugene Seah76af9832012-03-28 18:43:53 -0600366 /* continue to call TZ to get updated slack timer */
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700367 else
Eugene Seah76af9832012-03-28 18:43:53 -0600368 core->actual_freq = ret;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700369
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700370 core->freq_change_us = (uint32_t)ktime_to_us(
371 ktime_sub(ktime_get(), time_start));
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700372
Steve Muckle98e5fa52012-12-06 14:41:35 -0800373 if (core->type == MSM_DCVS_CORE_TYPE_CPU &&
374 core->type_core_num == 0) {
375 mutex_lock(&param_update_mutex);
376 check_power_collapse_modes(core);
377 mutex_unlock(&param_update_mutex);
378 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700379
380 /**
381 * Update algorithm with new freq and time taken to change
382 * to this frequency and that will get us the new slack
383 * timer
384 */
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700385 ret = msm_dcvs_scm_event(core->dcvs_core_id,
386 MSM_DCVS_SCM_CLOCK_FREQ_UPDATE,
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700387 core->actual_freq, core->freq_change_us,
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700388 &slack_us, &ret1);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700389 if (ret) {
390 __err("Error sending core (%s) dcvs_core_id = %d freq change (%u) reqfreq = %d slack_us=%d ret = %d\n",
391 core->core_name, core->dcvs_core_id,
392 core->actual_freq, requested_freq,
393 slack_us, ret);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700394 }
395
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700396 /* TODO confirm that we get a valid freq from SM even when the above
397 * FREQ_UPDATE fails
398 */
399 restart_slack_timer(core, slack_us);
400 spin_lock_irqsave(&core->pending_freq_lock, flags);
401
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700402 /**
403 * By the time we are done with freq changes, we could be asked to
404 * change again. Check before exiting.
405 */
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700406 if (core->pending_freq != NO_OUTSTANDING_FREQ_CHANGE
407 && core->pending_freq != STOP_FREQ_CHANGE) {
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700408 goto repeat;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700409 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700410
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700411out: /* should always be jumped to with the spin_lock held */
412 spin_unlock_irqrestore(&core->pending_freq_lock, flags);
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700413
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700414 return ret;
415}
416
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700417static void msm_dcvs_report_temp_work(struct work_struct *work)
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700418{
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700419 struct dcvs_core *core = container_of(work,
420 struct dcvs_core,
421 temperature_work.work);
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700422 struct msm_dcvs_core_info *info = core->info;
423 struct tsens_device tsens_dev;
424 int ret;
425 unsigned long temp = 0;
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700426 int interval_ms;
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700427
Steve Mucklec1785c32012-11-13 14:27:43 -0800428 if (!(core->flags & CORE_FLAG_TEMP_UPDATE))
429 return;
430
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700431 tsens_dev.sensor_num = core->sensor;
432 ret = tsens_get_temp(&tsens_dev, &temp);
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700433 if (!temp) {
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700434 tsens_dev.sensor_num = 0;
435 ret = tsens_get_temp(&tsens_dev, &temp);
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700436 if (!temp)
437 goto out;
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700438 }
439
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700440 if (temp == info->power_param.current_temp)
441 goto out;
442 info->power_param.current_temp = temp;
443
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700444 ret = msm_dcvs_scm_set_power_params(core->dcvs_core_id,
445 &info->power_param,
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700446 &info->freq_tbl[0], &core->coeffs);
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700447out:
448 if (info->thermal_poll_ms == 0)
449 interval_ms = 60000;
450 else if (info->thermal_poll_ms < 1000)
451 interval_ms = 1000;
452 else
453 interval_ms = info->thermal_poll_ms;
454
455 schedule_delayed_work(&core->temperature_work,
456 msecs_to_jiffies(interval_ms));
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700457}
458
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700459static int msm_dcvs_do_freq(void *data)
460{
461 struct dcvs_core *core = (struct dcvs_core *)data;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700462
463 while (!kthread_should_stop()) {
Abhijeet Dharmapurikarbbb52fe2012-08-31 20:31:16 -0700464 wait_event(core->wait_q, !(core->pending_freq == 0 ||
465 core->pending_freq == -1) ||
466 kthread_should_stop());
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700467
468 if (kthread_should_stop())
469 break;
470
Abhijeet Dharmapurikarbbb52fe2012-08-31 20:31:16 -0700471 __msm_dcvs_change_freq(core);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700472 }
473
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700474 return 0;
475}
476
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700477/* freq_pending_lock should be held */
478static void request_freq_change(struct dcvs_core *core, int new_freq)
479{
480 if (new_freq == NO_OUTSTANDING_FREQ_CHANGE) {
481 if (core->pending_freq != STOP_FREQ_CHANGE) {
482 __err("%s gov started with earlier pending freq %d\n",
483 core->core_name, core->pending_freq);
484 }
485 core->pending_freq = NO_OUTSTANDING_FREQ_CHANGE;
486 return;
487 }
488
489 if (new_freq == STOP_FREQ_CHANGE) {
Steve Muckle80e0b7b2012-11-19 15:46:39 -0800490 core->pending_freq = STOP_FREQ_CHANGE;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700491 return;
492 }
493
494 if (core->pending_freq < 0) {
495 /* a value less than 0 means that the governor has stopped
496 * and no more freq changes should be requested
497 */
498 return;
499 }
500
501 if (core->actual_freq != new_freq && core->pending_freq != new_freq) {
502 core->pending_freq = new_freq;
503 core->time_start = ktime_get();
504 wake_up(&core->wait_q);
505 }
506}
507
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700508static int msm_dcvs_update_freq(struct dcvs_core *core,
Eugene Seah76af9832012-03-28 18:43:53 -0600509 enum msm_dcvs_scm_event event, uint32_t param0,
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700510 uint32_t *ret1)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700511{
512 int ret = 0;
513 unsigned long flags = 0;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700514 uint32_t new_freq = -EINVAL;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700515
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700516 spin_lock_irqsave(&core->pending_freq_lock, flags);
517
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700518 ret = msm_dcvs_scm_event(core->dcvs_core_id, event, param0,
Eugene Seah76af9832012-03-28 18:43:53 -0600519 core->actual_freq, &new_freq, ret1);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700520 if (ret) {
Jeff Ohlstein4aa4a2b2012-06-28 19:03:49 -0700521 if (ret == -13)
522 ret = 0;
523 else
524 __err("Error (%d) sending SCM event %d for core %s\n",
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700525 ret, event, core->core_name);
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700526 goto out;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700527 }
528
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700529 if (new_freq == 0) {
530 /*
531 * sometimes TZ gives us a 0 freq back,
532 * do not queue up a request
533 */
534 goto out;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700535 }
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700536
537 request_freq_change(core, new_freq);
538
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700539out:
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700540 spin_unlock_irqrestore(&core->pending_freq_lock, flags);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700541
542 return ret;
543}
544
545static enum hrtimer_restart msm_dcvs_core_slack_timer(struct hrtimer *timer)
546{
547 int ret = 0;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700548 struct dcvs_core *core = container_of(timer,
549 struct dcvs_core, slack_timer);
Eugene Seah76af9832012-03-28 18:43:53 -0600550 uint32_t ret1;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700551
Abhijeet Dharmapurikar07cf2ff2012-09-13 19:05:13 -0700552 trace_printk("dcvs: Slack timer fired for core=%s\n", core->core_name);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700553 /**
554 * Timer expired, notify TZ
555 * Dont care about the third arg.
556 */
Eugene Seah76af9832012-03-28 18:43:53 -0600557 ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_QOS_TIMER_EXPIRED, 0,
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700558 &ret1);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700559 if (ret)
560 __err("Timer expired for core %s but failed to notify.\n",
561 core->core_name);
562
563 return HRTIMER_NORESTART;
564}
565
Steve Muckle388cc2e2012-11-21 15:47:15 -0800566int msm_dcvs_update_algo_params(void)
567{
568 static struct msm_dcvs_algo_param curr_params;
Steve Muckle388cc2e2012-11-21 15:47:15 -0800569 struct msm_dcvs_algo_param *new_params;
570 int cpu, ret = 0;
571
572 mutex_lock(&param_update_mutex);
573 new_params = &core_list[CPU_OFFSET + num_online_cpus() - 1].algo_param;
574
575 if (memcmp(&curr_params, new_params,
576 sizeof(struct msm_dcvs_algo_param))) {
577 for_each_possible_cpu(cpu) {
Steve Muckle520b3f22012-12-06 14:34:38 -0800578 struct dcvs_core *core = &core_list[CPU_OFFSET + cpu];
Steve Muckle388cc2e2012-11-21 15:47:15 -0800579 ret = msm_dcvs_scm_set_algo_params(CPU_OFFSET + cpu,
580 new_params);
581 if (ret) {
582 pr_err("scm set algo params failed on cpu %d, ret %d\n",
583 cpu, ret);
584 mutex_unlock(&param_update_mutex);
585 return ret;
586 }
Steve Muckle98e5fa52012-12-06 14:41:35 -0800587 if (cpu == 0)
588 check_power_collapse_modes(core);
Steve Muckle388cc2e2012-11-21 15:47:15 -0800589 }
590 memcpy(&curr_params, new_params,
591 sizeof(struct msm_dcvs_algo_param));
592 }
593
594 mutex_unlock(&param_update_mutex);
595 return ret;
596}
597
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700598/* Helper functions and macros for sysfs nodes for a core */
599#define CORE_FROM_ATTRIBS(attr, name) \
600 container_of(container_of(attr, struct core_attribs, name), \
601 struct dcvs_core, attrib);
602
603#define DCVS_PARAM_SHOW(_name, v) \
604static ssize_t msm_dcvs_attr_##_name##_show(struct kobject *kobj, \
605 struct kobj_attribute *attr, char *buf) \
606{ \
607 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
608 return snprintf(buf, PAGE_SIZE, "%d\n", v); \
609}
610
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700611#define DCVS_PARAM_STORE(_name) \
612static ssize_t msm_dcvs_attr_##_name##_show(struct kobject *kobj,\
613 struct kobj_attribute *attr, char *buf) \
614{ \
615 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
616 return snprintf(buf, PAGE_SIZE, "%d\n", core->info->_name); \
617} \
618static ssize_t msm_dcvs_attr_##_name##_store(struct kobject *kobj, \
619 struct kobj_attribute *attr, const char *buf, size_t count) \
620{ \
621 int ret = 0; \
622 uint32_t val = 0; \
623 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
624 ret = kstrtouint(buf, 10, &val); \
625 if (ret) { \
626 __err("Invalid input %s for %s\n", buf, __stringify(_name));\
627 } else { \
628 core->info->_name = val; \
629 } \
630 return count; \
631}
632
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700633#define DCVS_ALGO_PARAM(_name) \
634static ssize_t msm_dcvs_attr_##_name##_show(struct kobject *kobj,\
635 struct kobj_attribute *attr, char *buf) \
636{ \
637 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
638 return snprintf(buf, PAGE_SIZE, "%d\n", core->algo_param._name); \
639} \
640static ssize_t msm_dcvs_attr_##_name##_store(struct kobject *kobj, \
641 struct kobj_attribute *attr, const char *buf, size_t count) \
642{ \
643 int ret = 0; \
644 uint32_t val = 0; \
645 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700646 ret = kstrtouint(buf, 10, &val); \
647 if (ret) { \
648 __err("Invalid input %s for %s\n", buf, __stringify(_name));\
649 } else { \
650 uint32_t old_val = core->algo_param._name; \
651 core->algo_param._name = val; \
Steve Muckle388cc2e2012-11-21 15:47:15 -0800652 ret = msm_dcvs_update_algo_params(); \
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700653 if (ret) { \
654 core->algo_param._name = old_val; \
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700655 } \
656 } \
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700657 return count; \
658}
659
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700660#define DCVS_ENERGY_PARAM(_name) \
661static ssize_t msm_dcvs_attr_##_name##_show(struct kobject *kobj,\
662 struct kobj_attribute *attr, char *buf) \
663{ \
664 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
665 return snprintf(buf, PAGE_SIZE, "%d\n", core->coeffs._name); \
666} \
667static ssize_t msm_dcvs_attr_##_name##_store(struct kobject *kobj, \
668 struct kobj_attribute *attr, const char *buf, size_t count) \
669{ \
670 int ret = 0; \
671 int32_t val = 0; \
672 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700673 ret = kstrtoint(buf, 10, &val); \
674 if (ret) { \
675 __err("Invalid input %s for %s\n", buf, __stringify(_name));\
676 } else { \
677 int32_t old_val = core->coeffs._name; \
678 core->coeffs._name = val; \
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700679 ret = msm_dcvs_scm_set_power_params(core->dcvs_core_id, \
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700680 &core->info->power_param, &core->info->freq_tbl[0], \
681 &core->coeffs); \
682 if (ret) { \
683 core->coeffs._name = old_val; \
684 __err("Error(%d) in setting %d for coeffs param %s\n",\
685 ret, val, __stringify(_name)); \
686 } \
687 } \
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700688 return count; \
689}
690
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700691#define DCVS_RO_ATTRIB(i, _name) \
692 core->attrib._name.attr.name = __stringify(_name); \
693 core->attrib._name.attr.mode = S_IRUGO; \
694 core->attrib._name.show = msm_dcvs_attr_##_name##_show; \
695 core->attrib._name.store = NULL; \
696 core->attrib.attrib_group.attrs[i] = &core->attrib._name.attr;
697
698#define DCVS_RW_ATTRIB(i, _name) \
699 core->attrib._name.attr.name = __stringify(_name); \
700 core->attrib._name.attr.mode = S_IRUGO | S_IWUSR; \
701 core->attrib._name.show = msm_dcvs_attr_##_name##_show; \
702 core->attrib._name.store = msm_dcvs_attr_##_name##_store; \
703 core->attrib.attrib_group.attrs[i] = &core->attrib._name.attr;
704
705/**
706 * Function declarations for different attributes.
707 * Gets used when setting the attribute show and store parameters.
708 */
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700709DCVS_PARAM_SHOW(freq_change_us, (core->freq_change_us))
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700710
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700711DCVS_ALGO_PARAM(disable_pc_threshold)
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700712DCVS_ALGO_PARAM(em_win_size_min_us)
713DCVS_ALGO_PARAM(em_win_size_max_us)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700714DCVS_ALGO_PARAM(em_max_util_pct)
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700715DCVS_ALGO_PARAM(group_id)
716DCVS_ALGO_PARAM(max_freq_chg_time_us)
717DCVS_ALGO_PARAM(slack_mode_dynamic)
718DCVS_ALGO_PARAM(slack_time_min_us)
719DCVS_ALGO_PARAM(slack_time_max_us)
720DCVS_ALGO_PARAM(slack_weight_thresh_pct)
Steve Muckle8d0782e2012-12-06 14:31:00 -0800721DCVS_ALGO_PARAM(ss_no_corr_below_freq)
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700722DCVS_ALGO_PARAM(ss_win_size_min_us)
723DCVS_ALGO_PARAM(ss_win_size_max_us)
724DCVS_ALGO_PARAM(ss_util_pct)
725
726DCVS_ENERGY_PARAM(active_coeff_a)
727DCVS_ENERGY_PARAM(active_coeff_b)
728DCVS_ENERGY_PARAM(active_coeff_c)
729DCVS_ENERGY_PARAM(leakage_coeff_a)
730DCVS_ENERGY_PARAM(leakage_coeff_b)
731DCVS_ENERGY_PARAM(leakage_coeff_c)
732DCVS_ENERGY_PARAM(leakage_coeff_d)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700733
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700734DCVS_PARAM_STORE(thermal_poll_ms)
735
Steve Muckle74da4a02012-11-28 16:29:46 -0800736static ssize_t msm_dcvs_attr_offset_tbl_show(struct kobject *kobj,
737 struct kobj_attribute *attr,
738 char *buf)
739{
740 struct msm_dcvs_freq_entry *freq_tbl;
741 char *buf_idx = buf;
742 int i, len;
743 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, offset_tbl);
744
745 freq_tbl = core->info->freq_tbl;
746 *buf_idx = '\0';
747
748 /* limit the number of frequencies we will print into
749 * the PAGE_SIZE sysfs show buffer. */
750 if (core->info->power_param.num_freq > 64)
751 return 0;
752
753 for (i = 0; i < core->info->power_param.num_freq; i++) {
754 len = snprintf(buf_idx, 30, "%7d %7d %7d\n",
755 freq_tbl[i].freq,
756 freq_tbl[i].active_energy_offset,
757 freq_tbl[i].leakage_energy_offset);
758 /* buf_idx always points at terminating null */
759 buf_idx += len;
760 }
761 return buf_idx - buf;
762}
763
764static ssize_t msm_dcvs_attr_offset_tbl_store(struct kobject *kobj,
765 struct kobj_attribute *attr,
766 const char *buf,
767 size_t count)
768{
769 struct msm_dcvs_freq_entry *freq_tbl;
770 uint32_t freq, active_energy_offset, leakage_energy_offset;
771 int i, ret;
772 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, offset_tbl);
773
774 freq_tbl = core->info->freq_tbl;
775
776 ret = sscanf(buf, "%u %u %u",
777 &freq, &active_energy_offset, &leakage_energy_offset);
778 if (ret != 3) {
779 __err("Invalid input %s for offset_tbl\n", buf);
780 return count;
781 }
782
783 for (i = 0; i < core->info->power_param.num_freq; i++)
784 if (freq_tbl[i].freq == freq) {
785 freq_tbl[i].active_energy_offset =
786 active_energy_offset;
787 freq_tbl[i].leakage_energy_offset =
788 leakage_energy_offset;
789 break;
790 }
791
792 if (i >= core->info->power_param.num_freq) {
793 __err("Invalid frequency for offset_tbl: %d\n", freq);
794 return count;
795 }
796
797 ret = msm_dcvs_scm_set_power_params(core->dcvs_core_id,
798 &core->info->power_param,
799 &core->info->freq_tbl[0],
800 &core->coeffs);
801 if (ret)
802 __err("Error %d in updating active/leakage energy\n", ret);
803
804 return count;
805}
806
Steve Muckle118f47b2012-10-17 16:09:37 -0700807static ssize_t msm_dcvs_attr_freq_tbl_show(struct kobject *kobj,
808 struct kobj_attribute *attr,
809 char *buf)
810{
811 struct msm_dcvs_freq_entry *freq_tbl;
812 char *buf_idx = buf;
813 int i, len;
814 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, freq_tbl);
815
816 freq_tbl = core->info->freq_tbl;
817 *buf_idx = '\0';
818
819 /* limit the number of frequencies we will print into
820 * the PAGE_SIZE sysfs show buffer. */
821 if (core->info->power_param.num_freq > 64)
822 return 0;
823
824 for (i = 0; i < core->info->power_param.num_freq; i++) {
825 if (freq_tbl[i].is_trans_level) {
826 len = snprintf(buf_idx, 10, "%7d ", freq_tbl[i].freq);
827 /* buf_idx always points at terminating null */
828 buf_idx += len;
829 }
830 }
831 /* overwrite final trailing space with newline */
832 if (buf_idx > buf)
833 *(buf_idx - 1) = '\n';
834
835 return buf_idx - buf;
836}
837
838static ssize_t msm_dcvs_attr_freq_tbl_store(struct kobject *kobj,
839 struct kobj_attribute *attr,
840 const char *buf,
841 size_t count)
842{
843 struct msm_dcvs_freq_entry *freq_tbl;
844 uint32_t freq;
845 int i, ret;
846 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, freq_tbl);
847
848 freq_tbl = core->info->freq_tbl;
849
850 ret = kstrtouint(buf, 10, &freq);
851 if (ret) {
852 __err("Invalid input %s for freq_tbl\n", buf);
853 return count;
854 }
855
856 for (i = 0; i < core->info->power_param.num_freq; i++)
857 if (freq_tbl[i].freq == freq) {
858 freq_tbl[i].is_trans_level ^= 1;
859 break;
860 }
861
862 if (i >= core->info->power_param.num_freq) {
863 __err("Invalid frequency for freq_tbl: %d\n", freq);
864 return count;
865 }
866
867 ret = msm_dcvs_scm_set_power_params(core->dcvs_core_id,
868 &core->info->power_param,
869 &core->info->freq_tbl[0],
870 &core->coeffs);
871 if (ret) {
872 freq_tbl[i].is_trans_level ^= 1;
873 __err("Error %d in toggling freq %d (orig enable val %d)\n",
874 ret, freq_tbl[i].freq, freq_tbl[i].is_trans_level);
875 }
876 return count;
877}
878
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700879static int msm_dcvs_setup_core_sysfs(struct dcvs_core *core)
880{
881 int ret = 0;
882 struct kobject *core_kobj = NULL;
Steve Muckle74da4a02012-11-28 16:29:46 -0800883 const int attr_count = 26;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700884
885 BUG_ON(!cores_kobj);
886
887 core->attrib.attrib_group.attrs =
888 kzalloc(attr_count * sizeof(struct attribute *), GFP_KERNEL);
889
890 if (!core->attrib.attrib_group.attrs) {
891 ret = -ENOMEM;
892 goto done;
893 }
894
Abhijeet Dharmapurikar35876382012-09-13 16:09:51 -0700895 DCVS_RO_ATTRIB(0, freq_change_us);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700896
Abhijeet Dharmapurikar35876382012-09-13 16:09:51 -0700897 DCVS_RW_ATTRIB(1, disable_pc_threshold);
898 DCVS_RW_ATTRIB(2, em_win_size_min_us);
899 DCVS_RW_ATTRIB(3, em_win_size_max_us);
900 DCVS_RW_ATTRIB(4, em_max_util_pct);
901 DCVS_RW_ATTRIB(5, group_id);
902 DCVS_RW_ATTRIB(6, max_freq_chg_time_us);
903 DCVS_RW_ATTRIB(7, slack_mode_dynamic);
904 DCVS_RW_ATTRIB(8, slack_weight_thresh_pct);
905 DCVS_RW_ATTRIB(9, slack_time_min_us);
906 DCVS_RW_ATTRIB(10, slack_time_max_us);
Steve Muckle8d0782e2012-12-06 14:31:00 -0800907 DCVS_RW_ATTRIB(11, ss_no_corr_below_freq);
Abhijeet Dharmapurikar35876382012-09-13 16:09:51 -0700908 DCVS_RW_ATTRIB(12, ss_win_size_min_us);
909 DCVS_RW_ATTRIB(13, ss_win_size_max_us);
910 DCVS_RW_ATTRIB(14, ss_util_pct);
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700911
Abhijeet Dharmapurikar35876382012-09-13 16:09:51 -0700912 DCVS_RW_ATTRIB(15, active_coeff_a);
913 DCVS_RW_ATTRIB(16, active_coeff_b);
914 DCVS_RW_ATTRIB(17, active_coeff_c);
915 DCVS_RW_ATTRIB(18, leakage_coeff_a);
916 DCVS_RW_ATTRIB(19, leakage_coeff_b);
917 DCVS_RW_ATTRIB(20, leakage_coeff_c);
918 DCVS_RW_ATTRIB(21, leakage_coeff_d);
919 DCVS_RW_ATTRIB(22, thermal_poll_ms);
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700920
Steve Muckle118f47b2012-10-17 16:09:37 -0700921 DCVS_RW_ATTRIB(23, freq_tbl);
Steve Muckle74da4a02012-11-28 16:29:46 -0800922 DCVS_RW_ATTRIB(24, offset_tbl);
Steve Muckle118f47b2012-10-17 16:09:37 -0700923
Steve Muckle74da4a02012-11-28 16:29:46 -0800924 core->attrib.attrib_group.attrs[25] = NULL;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700925
926 core_kobj = kobject_create_and_add(core->core_name, cores_kobj);
927 if (!core_kobj) {
928 ret = -ENOMEM;
929 goto done;
930 }
931
932 ret = sysfs_create_group(core_kobj, &core->attrib.attrib_group);
933 if (ret)
934 __err("Cannot create core %s attr group\n", core->core_name);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700935
936done:
937 if (ret) {
938 kfree(core->attrib.attrib_group.attrs);
939 kobject_del(core_kobj);
940 }
941
942 return ret;
943}
944
Steve Muckle5e5a60d2012-10-09 13:25:22 -0700945static int get_core_offset(enum msm_dcvs_core_type type, int num)
946{
947 int offset = -EINVAL;
948
949 switch (type) {
950 case MSM_DCVS_CORE_TYPE_CPU:
951 offset = CPU_OFFSET + num;
952 BUG_ON(offset >= GPU_OFFSET);
953 break;
954 case MSM_DCVS_CORE_TYPE_GPU:
955 offset = GPU_OFFSET + num;
956 BUG_ON(offset >= CORES_MAX);
957 break;
958 default:
959 BUG();
960 }
961
962 return offset;
963}
964
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700965/* Return the core and initialize non platform data specific numbers in it */
966static struct dcvs_core *msm_dcvs_add_core(enum msm_dcvs_core_type type,
967 int num)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700968{
969 struct dcvs_core *core = NULL;
970 int i;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700971 char name[CORE_NAME_MAX];
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700972
Steve Muckle5e5a60d2012-10-09 13:25:22 -0700973 i = get_core_offset(type, num);
974 if (i < 0)
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700975 return NULL;
Steve Muckle5e5a60d2012-10-09 13:25:22 -0700976
977 if (type == MSM_DCVS_CORE_TYPE_CPU)
978 snprintf(name, CORE_NAME_MAX, "cpu%d", num);
979 else
980 snprintf(name, CORE_NAME_MAX, "gpu%d", num);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700981
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700982 core = &core_list[i];
983 core->dcvs_core_id = i;
Abhijeet Dharmapurikar2ebc0fe2012-09-12 14:05:13 -0700984 strlcpy(core->core_name, name, CORE_NAME_MAX);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700985 spin_lock_init(&core->pending_freq_lock);
986 spin_lock_init(&core->idle_state_change_lock);
987 hrtimer_init(&core->slack_timer,
988 CLOCK_MONOTONIC, HRTIMER_MODE_REL_PINNED);
989 core->slack_timer.function = msm_dcvs_core_slack_timer;
Abhijeet Dharmapurikar2ebc0fe2012-09-12 14:05:13 -0700990 return core;
991}
992
993/* Return the core if found or add to list if @add_to_list is true */
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700994static struct dcvs_core *msm_dcvs_get_core(int offset)
Abhijeet Dharmapurikar2ebc0fe2012-09-12 14:05:13 -0700995{
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700996 /* if the handle is still not set bug */
997 BUG_ON(core_list[offset].dcvs_core_id == -1);
998 return &core_list[offset];
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700999}
1000
Steve Mucklea9aac292012-11-02 15:41:00 -07001001void msm_dcvs_register_cpu_freq(uint32_t freq, uint32_t voltage)
1002{
1003 BUG_ON(freq == 0 || voltage == 0 ||
1004 num_cpu_freqs == DCVS_MAX_NUM_FREQS);
1005
1006 cpu_freq_tbl[num_cpu_freqs].freq = freq;
1007 cpu_freq_tbl[num_cpu_freqs].voltage = voltage;
1008
1009 num_cpu_freqs++;
1010}
1011
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001012int msm_dcvs_register_core(
1013 enum msm_dcvs_core_type type,
1014 int type_core_num,
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -07001015 struct msm_dcvs_core_info *info,
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001016 int (*set_frequency)(int type_core_num, unsigned int freq),
1017 unsigned int (*get_frequency)(int type_core_num),
1018 int (*idle_enable)(int type_core_num,
Abhijeet Dharmapurikar6e9b34f2012-09-10 16:03:39 -07001019 enum msm_core_control_event event),
Steve Muckle93bb4252012-11-12 14:20:39 -08001020 int (*set_floor_frequency)(int type_core_num, unsigned int freq),
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -07001021 int sensor)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001022{
1023 int ret = -EINVAL;
Steve Muckle5e5a60d2012-10-09 13:25:22 -07001024 int offset;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001025 struct dcvs_core *core = NULL;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -07001026 uint32_t ret1;
1027 uint32_t ret2;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001028
Steve Muckle2f8214d2013-05-08 10:30:54 -07001029 if (!msm_dcvs_enabled)
1030 return ret;
1031
Steve Muckle5e5a60d2012-10-09 13:25:22 -07001032 offset = get_core_offset(type, type_core_num);
1033 if (offset < 0)
1034 return ret;
1035 if (core_list[offset].dcvs_core_id != -1)
1036 return core_list[offset].dcvs_core_id;
1037
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001038 core = msm_dcvs_add_core(type, type_core_num);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001039 if (!core)
1040 return ret;
1041
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001042 core->type = type;
1043 core->type_core_num = type_core_num;
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -07001044 core->set_frequency = set_frequency;
1045 core->get_frequency = get_frequency;
Abhijeet Dharmapurikar6e9b34f2012-09-10 16:03:39 -07001046 core->idle_enable = idle_enable;
Steve Muckle93bb4252012-11-12 14:20:39 -08001047 core->set_floor_frequency = set_floor_frequency;
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -07001048
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -07001049 core->info = info;
Steve Muckle74da4a02012-11-28 16:29:46 -08001050 if (type == MSM_DCVS_CORE_TYPE_CPU) {
1051 BUG_ON(num_cpu_freqs == 0);
1052 info->freq_tbl = cpu_freq_tbl;
1053 info->power_param.num_freq = num_cpu_freqs;
1054 }
Steve Mucklea9aac292012-11-02 15:41:00 -07001055
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001056 memcpy(&core->algo_param, &info->algo_param,
1057 sizeof(struct msm_dcvs_algo_param));
1058
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -07001059 memcpy(&core->coeffs, &info->energy_coeffs,
1060 sizeof(struct msm_dcvs_energy_curve_coeffs));
1061
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001062 /*
1063 * The tz expects cpu0 to represent bit 0 in the mask, however the
1064 * dcvs_core_id needs to start from 1, dcvs_core_id = 0 is used to
1065 * indicate that this request is not associated with any core.
1066 * mpdecision
1067 */
1068 info->core_param.core_bitmask_id
1069 = 1 << (core->dcvs_core_id - CPU_OFFSET);
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -07001070 core->sensor = sensor;
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -07001071
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001072 ret = msm_dcvs_scm_register_core(core->dcvs_core_id, &info->core_param);
1073 if (ret) {
1074 __err("%s: scm register core fail handle = %d ret = %d\n",
1075 __func__, core->dcvs_core_id, ret);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001076 goto bail;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001077 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001078
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001079 ret = msm_dcvs_scm_set_algo_params(core->dcvs_core_id,
1080 &info->algo_param);
1081 if (ret) {
1082 __err("%s: scm algo params failed ret = %d\n", __func__, ret);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001083 goto bail;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001084 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001085
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001086 ret = msm_dcvs_scm_set_power_params(core->dcvs_core_id,
1087 &info->power_param,
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -07001088 &info->freq_tbl[0], &core->coeffs);
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001089 if (ret) {
1090 __err("%s: scm power params failed ret = %d\n", __func__, ret);
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -07001091 goto bail;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001092 }
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -07001093
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001094 ret = msm_dcvs_scm_event(core->dcvs_core_id, MSM_DCVS_SCM_CORE_ONLINE,
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -07001095 core->actual_freq, 0, &ret1, &ret2);
1096 if (ret)
1097 goto bail;
1098
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001099 ret = msm_dcvs_setup_core_sysfs(core);
1100 if (ret) {
1101 __err("Unable to setup core %s sysfs\n", core->core_name);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001102 goto bail;
1103 }
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001104 core->idle_entered = -1;
Abhijeet Dharmapurikarbbb52fe2012-08-31 20:31:16 -07001105 init_waitqueue_head(&core->wait_q);
1106 core->task = kthread_run(msm_dcvs_do_freq, (void *)core,
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001107 "msm_dcvs/%d", core->dcvs_core_id);
1108 ret = core->dcvs_core_id;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001109 return ret;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001110bail:
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001111 core->dcvs_core_id = -1;
1112 return -EINVAL;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001113}
1114EXPORT_SYMBOL(msm_dcvs_register_core);
1115
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001116void msm_dcvs_update_limits(int dcvs_core_id)
Abhijeet Dharmapurikarc43f0db2012-08-31 20:42:53 -07001117{
1118 struct dcvs_core *core;
1119
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001120 if (dcvs_core_id < CPU_OFFSET || dcvs_core_id > CORES_MAX) {
1121 __err("%s invalid dcvs_core_id = %d returning -EINVAL\n",
1122 __func__, dcvs_core_id);
Abhijeet Dharmapurikarc43f0db2012-08-31 20:42:53 -07001123 return;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001124 }
Abhijeet Dharmapurikarc43f0db2012-08-31 20:42:53 -07001125
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001126 core = msm_dcvs_get_core(dcvs_core_id);
1127 core->actual_freq = core->get_frequency(core->type_core_num);
Abhijeet Dharmapurikarc43f0db2012-08-31 20:42:53 -07001128}
1129
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001130int msm_dcvs_freq_sink_start(int dcvs_core_id)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001131{
1132 int ret = -EINVAL;
1133 struct dcvs_core *core = NULL;
Eugene Seah76af9832012-03-28 18:43:53 -06001134 uint32_t ret1;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001135 unsigned long flags;
Abhijeet Dharmapurikar8d843b32012-09-13 18:33:20 -07001136 int new_freq;
1137 int timer_interval_us;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001138
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001139 if (dcvs_core_id < CPU_OFFSET || dcvs_core_id > CORES_MAX) {
1140 __err("%s invalid dcvs_core_id = %d returning -EINVAL\n",
1141 __func__, dcvs_core_id);
1142 return -EINVAL;
1143 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001144
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001145 core = msm_dcvs_get_core(dcvs_core_id);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001146 if (!core)
1147 return ret;
1148
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001149 core->actual_freq = core->get_frequency(core->type_core_num);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001150
1151 spin_lock_irqsave(&core->pending_freq_lock, flags);
1152 /* mark that we are ready to accept new frequencies */
1153 request_freq_change(core, NO_OUTSTANDING_FREQ_CHANGE);
1154 spin_unlock_irqrestore(&core->pending_freq_lock, flags);
1155
1156 spin_lock_irqsave(&core->idle_state_change_lock, flags);
1157 core->idle_entered = -1;
1158 spin_unlock_irqrestore(&core->idle_state_change_lock, flags);
1159
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001160 /* Notify TZ to start receiving idle info for the core */
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001161 ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_DCVS_ENABLE, 1, &ret1);
1162
Abhijeet Dharmapurikar8d843b32012-09-13 18:33:20 -07001163 ret = msm_dcvs_scm_event(
1164 core->dcvs_core_id, MSM_DCVS_SCM_CORE_ONLINE, core->actual_freq,
1165 0, &new_freq, &timer_interval_us);
1166 if (ret)
1167 __err("Error (%d) DCVS sending online for %s\n",
1168 ret, core->core_name);
1169
1170 if (new_freq != 0) {
1171 spin_lock_irqsave(&core->pending_freq_lock, flags);
1172 request_freq_change(core, new_freq);
1173 spin_unlock_irqrestore(&core->pending_freq_lock, flags);
1174 }
1175 force_start_slack_timer(core, timer_interval_us);
1176
Steve Mucklec1785c32012-11-13 14:27:43 -08001177 core->flags |= CORE_FLAG_TEMP_UPDATE;
1178 INIT_DELAYED_WORK(&core->temperature_work, msm_dcvs_report_temp_work);
1179 schedule_delayed_work(&core->temperature_work,
1180 msecs_to_jiffies(core->info->thermal_poll_ms));
Abhijeet Dharmapurikar8d843b32012-09-13 18:33:20 -07001181
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001182 core->idle_enable(core->type_core_num, MSM_DCVS_ENABLE_IDLE_PULSE);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001183 return 0;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001184}
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -07001185EXPORT_SYMBOL(msm_dcvs_freq_sink_start);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001186
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001187int msm_dcvs_freq_sink_stop(int dcvs_core_id)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001188{
1189 int ret = -EINVAL;
1190 struct dcvs_core *core = NULL;
Eugene Seah76af9832012-03-28 18:43:53 -06001191 uint32_t ret1;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001192 uint32_t freq;
1193 unsigned long flags;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001194
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001195 if (dcvs_core_id < 0 || dcvs_core_id > CORES_MAX) {
1196 pr_err("%s invalid dcvs_core_id = %d returning -EINVAL\n",
1197 __func__, dcvs_core_id);
1198 return -EINVAL;
1199 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001200
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001201 core = msm_dcvs_get_core(dcvs_core_id);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001202 if (!core) {
1203 __err("couldn't find core for coreid = %d\n", dcvs_core_id);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001204 return ret;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001205 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001206
Steve Mucklec1785c32012-11-13 14:27:43 -08001207 core->flags &= ~CORE_FLAG_TEMP_UPDATE;
1208 cancel_delayed_work(&core->temperature_work);
1209
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001210 core->idle_enable(core->type_core_num, MSM_DCVS_DISABLE_IDLE_PULSE);
1211 /* Notify TZ to stop receiving idle info for the core */
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001212 ret = msm_dcvs_scm_event(core->dcvs_core_id, MSM_DCVS_SCM_DCVS_ENABLE,
1213 0, core->actual_freq, &freq, &ret1);
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001214 core->idle_enable(core->type_core_num,
1215 MSM_DCVS_ENABLE_HIGH_LATENCY_MODES);
Steve Muckle43a980c2012-11-19 16:38:55 -08001216
1217 if (core->type == MSM_DCVS_CORE_TYPE_GPU)
1218 mutex_lock(&gpu_floor_mutex);
1219
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001220 spin_lock_irqsave(&core->pending_freq_lock, flags);
1221 /* flush out all the pending freq changes */
1222 request_freq_change(core, STOP_FREQ_CHANGE);
1223 spin_unlock_irqrestore(&core->pending_freq_lock, flags);
Steve Muckle43a980c2012-11-19 16:38:55 -08001224
1225 if (core->type == MSM_DCVS_CORE_TYPE_GPU)
1226 mutex_unlock(&gpu_floor_mutex);
1227
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001228 force_stop_slack_timer(core);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001229
1230 return 0;
1231}
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -07001232EXPORT_SYMBOL(msm_dcvs_freq_sink_stop);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001233
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001234int msm_dcvs_idle(int dcvs_core_id, enum msm_core_idle_state state,
1235 uint32_t iowaited)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001236{
1237 int ret = 0;
1238 struct dcvs_core *core = NULL;
Eugene Seah76af9832012-03-28 18:43:53 -06001239 uint32_t timer_interval_us = 0;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001240 uint32_t r0, r1;
1241
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001242 if (dcvs_core_id < CPU_OFFSET || dcvs_core_id > CORES_MAX) {
1243 pr_err("invalid dcvs_core_id = %d ret -EINVAL\n", dcvs_core_id);
1244 return -EINVAL;
1245 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001246
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001247 core = msm_dcvs_get_core(dcvs_core_id);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001248
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001249 switch (state) {
1250 case MSM_DCVS_IDLE_ENTER:
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001251 stop_slack_timer(core);
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001252 ret = msm_dcvs_scm_event(core->dcvs_core_id,
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001253 MSM_DCVS_SCM_IDLE_ENTER, 0, 0, &r0, &r1);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001254 if (ret < 0 && ret != -13)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001255 __err("Error (%d) sending idle enter for %s\n",
1256 ret, core->core_name);
Abhijeet Dharmapurikar07cf2ff2012-09-13 19:05:13 -07001257 trace_msm_dcvs_idle("idle_enter_exit", core->core_name, 1);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001258 break;
1259
1260 case MSM_DCVS_IDLE_EXIT:
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001261 ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_IDLE_EXIT,
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001262 iowaited, &timer_interval_us);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001263 if (ret)
1264 __err("Error (%d) sending idle exit for %s\n",
1265 ret, core->core_name);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001266 start_slack_timer(core, timer_interval_us);
Abhijeet Dharmapurikar07cf2ff2012-09-13 19:05:13 -07001267 trace_msm_dcvs_idle("idle_enter_exit", core->core_name, 0);
1268 trace_msm_dcvs_iowait("iowait", core->core_name, iowaited);
1269 trace_msm_dcvs_slack_time("slack_timer_dcvs", core->core_name,
1270 timer_interval_us);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001271 break;
1272 }
1273
1274 return ret;
1275}
1276EXPORT_SYMBOL(msm_dcvs_idle);
1277
1278static int __init msm_dcvs_late_init(void)
1279{
1280 struct kobject *module_kobj = NULL;
1281 int ret = 0;
1282
Steve Muckle2f8214d2013-05-08 10:30:54 -07001283 if (!msm_dcvs_enabled)
1284 return ret;
1285
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001286 module_kobj = kset_find_obj(module_kset, KBUILD_MODNAME);
1287 if (!module_kobj) {
1288 pr_err("%s: cannot find kobject for module %s\n",
1289 __func__, KBUILD_MODNAME);
1290 ret = -ENOENT;
1291 goto err;
1292 }
1293
1294 cores_kobj = kobject_create_and_add("cores", module_kobj);
1295 if (!cores_kobj) {
1296 __err("Cannot create %s kobject\n", "cores");
1297 ret = -ENOMEM;
1298 goto err;
1299 }
1300
1301 debugfs_base = debugfs_create_dir("msm_dcvs", NULL);
1302 if (!debugfs_base) {
1303 __err("Cannot create debugfs base %s\n", "msm_dcvs");
1304 ret = -ENOENT;
1305 goto err;
1306 }
1307
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001308err:
1309 if (ret) {
1310 kobject_del(cores_kobj);
1311 cores_kobj = NULL;
1312 debugfs_remove(debugfs_base);
1313 }
1314
1315 return ret;
1316}
1317late_initcall(msm_dcvs_late_init);
1318
Steve Mucklea9aac292012-11-02 15:41:00 -07001319static int __devinit dcvs_probe(struct platform_device *pdev)
1320{
1321 if (pdev->dev.platform_data)
1322 dcvs_pdata = pdev->dev.platform_data;
1323
1324 return 0;
1325}
1326
1327static struct platform_driver dcvs_driver = {
1328 .probe = dcvs_probe,
1329 .driver = {
1330 .name = "dcvs",
1331 .owner = THIS_MODULE,
1332 },
1333};
1334
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001335static int __init msm_dcvs_early_init(void)
1336{
1337 int ret = 0;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001338 int i;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001339
Steve Mucklea9aac292012-11-02 15:41:00 -07001340 platform_driver_register(&dcvs_driver);
1341
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001342 if (!msm_dcvs_enabled) {
1343 __info("Not enabled (%d)\n", msm_dcvs_enabled);
1344 return 0;
1345 }
1346
Abhijeet Dharmapurikar3edb5de2012-09-13 11:02:03 -07001347
1348 /* Only need about 32kBytes for normal operation */
1349 ret = msm_dcvs_scm_init(SZ_32K);
1350 if (ret) {
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001351 __err("Unable to initialize DCVS err=%d\n", ret);
Steve Muckle2f8214d2013-05-08 10:30:54 -07001352 msm_dcvs_enabled = 0;
Abhijeet Dharmapurikar3edb5de2012-09-13 11:02:03 -07001353 goto done;
1354 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001355
Steve Muckle43a980c2012-11-19 16:38:55 -08001356 for (i = 0; i < CORES_MAX; i++) {
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001357 core_list[i].dcvs_core_id = -1;
Steve Muckle43a980c2012-11-19 16:38:55 -08001358 core_list[i].pending_freq = STOP_FREQ_CHANGE;
1359 }
Abhijeet Dharmapurikar3edb5de2012-09-13 11:02:03 -07001360done:
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001361 return ret;
1362}
1363postcore_initcall(msm_dcvs_early_init);