blob: 310197ed9a7c5923fa440bbf77b84c3beeadcc0f [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>
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070026#include <asm/atomic.h>
27#include <asm/page.h>
28#include <mach/msm_dcvs.h>
Abhijeet Dharmapurikar07cf2ff2012-09-13 19:05:13 -070029#include <trace/events/mpdcvs_trace.h>
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070030
31#define CORE_HANDLE_OFFSET (0xA0)
32#define __err(f, ...) pr_err("MSM_DCVS: %s: " f, __func__, __VA_ARGS__)
33#define __info(f, ...) pr_info("MSM_DCVS: %s: " f, __func__, __VA_ARGS__)
34#define MAX_PENDING (5)
35
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070036struct core_attribs {
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070037 struct kobj_attribute freq_change_us;
38
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070039 struct kobj_attribute disable_pc_threshold;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -070040 struct kobj_attribute em_win_size_min_us;
41 struct kobj_attribute em_win_size_max_us;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070042 struct kobj_attribute em_max_util_pct;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -070043 struct kobj_attribute group_id;
44 struct kobj_attribute max_freq_chg_time_us;
45 struct kobj_attribute slack_mode_dynamic;
46 struct kobj_attribute slack_time_min_us;
47 struct kobj_attribute slack_time_max_us;
48 struct kobj_attribute slack_weight_thresh_pct;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070049 struct kobj_attribute ss_iobusy_conv;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -070050 struct kobj_attribute ss_win_size_min_us;
51 struct kobj_attribute ss_win_size_max_us;
52 struct kobj_attribute ss_util_pct;
53
54 struct kobj_attribute active_coeff_a;
55 struct kobj_attribute active_coeff_b;
56 struct kobj_attribute active_coeff_c;
57 struct kobj_attribute leakage_coeff_a;
58 struct kobj_attribute leakage_coeff_b;
59 struct kobj_attribute leakage_coeff_c;
60 struct kobj_attribute leakage_coeff_d;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070061
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -070062 struct kobj_attribute thermal_poll_ms;
63
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070064 struct attribute_group attrib_group;
65};
66
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -070067enum pending_freq_state {
68 /*
69 * used by the thread to check if pending_freq was updated while it was
70 * setting previous frequency - this is written to and used by the
71 * freq updating thread
72 */
73 NO_OUTSTANDING_FREQ_CHANGE = 0,
74
75 /*
76 * This request is set to indicate that the governor is stopped and no
77 * more frequency change requests are accepted untill it starts again.
78 * This is checked/used by the threads that want to change the freq
79 */
80 STOP_FREQ_CHANGE = -1,
81
82 /*
83 * Any other +ve value means that a freq change was requested and the
84 * thread has not gotten around to update it
85 *
86 * Any other -ve value means that this is the last freq change i.e. a
87 * freq change was requested but the thread has not run yet and
88 * meanwhile the governor was stopped.
89 */
90};
91
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070092struct dcvs_core {
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -070093 spinlock_t idle_state_change_lock;
94 /* 0 when not idle (busy) 1 when idle and -1 when governor starts and
95 * we dont know whether the next call is going to be idle enter or exit
96 */
97 int idle_entered;
98
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -070099 enum msm_dcvs_core_type type;
100 /* this is the number in each type for example cpu 0,1,2 and gpu 0,1 */
101 int type_core_num;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700102 char core_name[CORE_NAME_MAX];
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700103 uint32_t actual_freq;
104 uint32_t freq_change_us;
105
106 uint32_t max_time_us; /* core param */
107
108 struct msm_dcvs_algo_param algo_param;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700109 struct msm_dcvs_energy_curve_coeffs coeffs;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700110
111 /* private */
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700112 ktime_t time_start;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700113 struct task_struct *task;
114 struct core_attribs attrib;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700115 uint32_t dcvs_core_id;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700116 struct msm_dcvs_core_info *info;
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700117 int sensor;
Abhijeet Dharmapurikarbbb52fe2012-08-31 20:31:16 -0700118 wait_queue_head_t wait_q;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700119
120 int (*set_frequency)(int type_core_num, unsigned int freq);
121 unsigned int (*get_frequency)(int type_core_num);
122 int (*idle_enable)(int type_core_num,
Abhijeet Dharmapurikar6e9b34f2012-09-10 16:03:39 -0700123 enum msm_core_control_event event);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700124
125 spinlock_t pending_freq_lock;
126 int pending_freq;
127
128 struct hrtimer slack_timer;
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700129 struct delayed_work temperature_work;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700130};
131
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700132static int msm_dcvs_enabled = 1;
133module_param_named(enable, msm_dcvs_enabled, int, S_IRUGO | S_IWUSR | S_IWGRP);
134
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700135static struct dentry *debugfs_base;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700136
137static struct dcvs_core core_list[CORES_MAX];
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700138
139static struct kobject *cores_kobj;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700140
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700141static void force_stop_slack_timer(struct dcvs_core *core)
142{
143 unsigned long flags;
144
145 spin_lock_irqsave(&core->idle_state_change_lock, flags);
146 hrtimer_cancel(&core->slack_timer);
147 spin_unlock_irqrestore(&core->idle_state_change_lock, flags);
148}
149
Abhijeet Dharmapurikar8d843b32012-09-13 18:33:20 -0700150static void force_start_slack_timer(struct dcvs_core *core, int slack_us)
151{
152 unsigned long flags;
153 int ret;
154
155 spin_lock_irqsave(&core->idle_state_change_lock, flags);
156
157 /*
158 * only start the timer if governor is not stopped
159 */
160 if (slack_us != 0) {
161 ret = hrtimer_start(&core->slack_timer,
162 ktime_set(0, slack_us * 1000),
163 HRTIMER_MODE_REL_PINNED);
164 if (ret) {
165 pr_err("%s Failed to start timer ret = %d\n",
166 core->core_name, ret);
167 }
168 }
169
170 spin_unlock_irqrestore(&core->idle_state_change_lock, flags);
171}
172
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700173static void stop_slack_timer(struct dcvs_core *core)
174{
175 unsigned long flags;
176
177 spin_lock_irqsave(&core->idle_state_change_lock, flags);
178 /* err only for cpu type's GPU's can do idle exit consecutively */
179 if (core->idle_entered == 1 && !(core->dcvs_core_id >= GPU_OFFSET))
180 __err("%s trying to reenter idle", core->core_name);
181 core->idle_entered = 1;
182 hrtimer_cancel(&core->slack_timer);
183 core->idle_entered = 1;
184 spin_unlock_irqrestore(&core->idle_state_change_lock, flags);
185}
186
187static void start_slack_timer(struct dcvs_core *core, int slack_us)
188{
189 unsigned long flags1, flags2;
190 int ret;
191
192 spin_lock_irqsave(&core->idle_state_change_lock, flags2);
193
194 spin_lock_irqsave(&core->pending_freq_lock, flags1);
195
196 /* err only for cpu type's GPU's can do idle enter consecutively */
197 if (core->idle_entered == 0 && !(core->dcvs_core_id >= GPU_OFFSET))
198 __err("%s trying to reexit idle", core->core_name);
199 core->idle_entered = 0;
200 /*
201 * only start the timer if governor is not stopped
202 */
203 if (slack_us != 0
204 && !(core->pending_freq < NO_OUTSTANDING_FREQ_CHANGE)) {
205 ret = hrtimer_start(&core->slack_timer,
206 ktime_set(0, slack_us * 1000),
207 HRTIMER_MODE_REL_PINNED);
208 if (ret) {
209 pr_err("%s Failed to start timer ret = %d\n",
210 core->core_name, ret);
211 }
212 }
213 spin_unlock_irqrestore(&core->pending_freq_lock, flags1);
214
215 spin_unlock_irqrestore(&core->idle_state_change_lock, flags2);
216}
217
218static void restart_slack_timer(struct dcvs_core *core, int slack_us)
219{
220 unsigned long flags1, flags2;
221 int ret;
222
223 spin_lock_irqsave(&core->idle_state_change_lock, flags2);
224
225 hrtimer_cancel(&core->slack_timer);
226
227 spin_lock_irqsave(&core->pending_freq_lock, flags1);
228
229 /*
230 * only start the timer if idle is not entered
231 * and governor is not stopped
232 */
233 if (slack_us != 0 && (core->idle_entered != 1)
234 && !(core->pending_freq < NO_OUTSTANDING_FREQ_CHANGE)) {
235 ret = hrtimer_start(&core->slack_timer,
236 ktime_set(0, slack_us * 1000),
237 HRTIMER_MODE_REL_PINNED);
238 if (ret) {
239 pr_err("%s Failed to start timer ret = %d\n",
240 core->core_name, ret);
241 }
242 }
243 spin_unlock_irqrestore(&core->pending_freq_lock, flags1);
244 spin_unlock_irqrestore(&core->idle_state_change_lock, flags2);
245}
246
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700247static int __msm_dcvs_change_freq(struct dcvs_core *core)
248{
249 int ret = 0;
250 unsigned long flags = 0;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700251 int requested_freq = 0;
252 ktime_t time_start;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700253 uint32_t slack_us = 0;
254 uint32_t ret1 = 0;
255
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700256 spin_lock_irqsave(&core->pending_freq_lock, flags);
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700257repeat:
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700258 BUG_ON(!core->pending_freq);
259 if (core->pending_freq == STOP_FREQ_CHANGE)
260 BUG();
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700261
262 requested_freq = core->pending_freq;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700263 time_start = core->time_start;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700264 core->time_start = ns_to_ktime(0);
265
266 if (requested_freq < 0) {
267 requested_freq = -1 * requested_freq;
268 core->pending_freq = STOP_FREQ_CHANGE;
269 } else {
270 core->pending_freq = NO_OUTSTANDING_FREQ_CHANGE;
271 }
272
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700273 if (requested_freq == core->actual_freq)
274 goto out;
275
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700276 spin_unlock_irqrestore(&core->pending_freq_lock, flags);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700277
278 /**
279 * Call the frequency sink driver to change the frequency
280 * We will need to get back the actual frequency in KHz and
281 * the record the time taken to change it.
282 */
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700283 ret = core->set_frequency(core->type_core_num, requested_freq);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700284 if (ret <= 0)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700285 __err("Core %s failed to set freq %u\n",
286 core->core_name, requested_freq);
Eugene Seah76af9832012-03-28 18:43:53 -0600287 /* continue to call TZ to get updated slack timer */
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700288 else
Eugene Seah76af9832012-03-28 18:43:53 -0600289 core->actual_freq = ret;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700290
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700291 core->freq_change_us = (uint32_t)ktime_to_us(
292 ktime_sub(ktime_get(), time_start));
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700293
294 /**
295 * Disable low power modes if the actual frequency is >
296 * disable_pc_threshold.
297 */
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700298 if (core->actual_freq > core->algo_param.disable_pc_threshold) {
299 core->idle_enable(core->type_core_num,
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700300 MSM_DCVS_DISABLE_HIGH_LATENCY_MODES);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700301 } else if (core->actual_freq <= core->algo_param.disable_pc_threshold) {
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700302 core->idle_enable(core->type_core_num,
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700303 MSM_DCVS_ENABLE_HIGH_LATENCY_MODES);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700304 }
305
306 /**
307 * Update algorithm with new freq and time taken to change
308 * to this frequency and that will get us the new slack
309 * timer
310 */
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700311 ret = msm_dcvs_scm_event(core->dcvs_core_id,
312 MSM_DCVS_SCM_CLOCK_FREQ_UPDATE,
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700313 core->actual_freq, core->freq_change_us,
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700314 &slack_us, &ret1);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700315 if (ret) {
316 __err("Error sending core (%s) dcvs_core_id = %d freq change (%u) reqfreq = %d slack_us=%d ret = %d\n",
317 core->core_name, core->dcvs_core_id,
318 core->actual_freq, requested_freq,
319 slack_us, ret);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700320 }
321
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700322 /* TODO confirm that we get a valid freq from SM even when the above
323 * FREQ_UPDATE fails
324 */
325 restart_slack_timer(core, slack_us);
326 spin_lock_irqsave(&core->pending_freq_lock, flags);
327
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700328 /**
329 * By the time we are done with freq changes, we could be asked to
330 * change again. Check before exiting.
331 */
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700332 if (core->pending_freq != NO_OUTSTANDING_FREQ_CHANGE
333 && core->pending_freq != STOP_FREQ_CHANGE) {
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700334 goto repeat;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700335 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700336
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700337out: /* should always be jumped to with the spin_lock held */
338 spin_unlock_irqrestore(&core->pending_freq_lock, flags);
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700339
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700340 return ret;
341}
342
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700343static void msm_dcvs_report_temp_work(struct work_struct *work)
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700344{
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700345 struct dcvs_core *core = container_of(work,
346 struct dcvs_core,
347 temperature_work.work);
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700348 struct msm_dcvs_core_info *info = core->info;
349 struct tsens_device tsens_dev;
350 int ret;
351 unsigned long temp = 0;
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700352 int interval_ms;
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700353
354 tsens_dev.sensor_num = core->sensor;
355 ret = tsens_get_temp(&tsens_dev, &temp);
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700356 if (!temp) {
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700357 tsens_dev.sensor_num = 0;
358 ret = tsens_get_temp(&tsens_dev, &temp);
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700359 if (!temp)
360 goto out;
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700361 }
362
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700363 if (temp == info->power_param.current_temp)
364 goto out;
365 info->power_param.current_temp = temp;
366
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700367 ret = msm_dcvs_scm_set_power_params(core->dcvs_core_id,
368 &info->power_param,
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700369 &info->freq_tbl[0], &core->coeffs);
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700370out:
371 if (info->thermal_poll_ms == 0)
372 interval_ms = 60000;
373 else if (info->thermal_poll_ms < 1000)
374 interval_ms = 1000;
375 else
376 interval_ms = info->thermal_poll_ms;
377
378 schedule_delayed_work(&core->temperature_work,
379 msecs_to_jiffies(interval_ms));
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700380}
381
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700382static int msm_dcvs_do_freq(void *data)
383{
384 struct dcvs_core *core = (struct dcvs_core *)data;
385 static struct sched_param param = {.sched_priority = MAX_RT_PRIO - 1};
386
387 sched_setscheduler(current, SCHED_FIFO, &param);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700388
389 while (!kthread_should_stop()) {
Abhijeet Dharmapurikarbbb52fe2012-08-31 20:31:16 -0700390 wait_event(core->wait_q, !(core->pending_freq == 0 ||
391 core->pending_freq == -1) ||
392 kthread_should_stop());
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700393
394 if (kthread_should_stop())
395 break;
396
Abhijeet Dharmapurikarbbb52fe2012-08-31 20:31:16 -0700397 __msm_dcvs_change_freq(core);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700398 }
399
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700400 return 0;
401}
402
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700403/* freq_pending_lock should be held */
404static void request_freq_change(struct dcvs_core *core, int new_freq)
405{
406 if (new_freq == NO_OUTSTANDING_FREQ_CHANGE) {
407 if (core->pending_freq != STOP_FREQ_CHANGE) {
408 __err("%s gov started with earlier pending freq %d\n",
409 core->core_name, core->pending_freq);
410 }
411 core->pending_freq = NO_OUTSTANDING_FREQ_CHANGE;
412 return;
413 }
414
415 if (new_freq == STOP_FREQ_CHANGE) {
416 if (core->pending_freq == NO_OUTSTANDING_FREQ_CHANGE)
417 core->pending_freq = STOP_FREQ_CHANGE;
418 else if (core->pending_freq > 0)
419 core->pending_freq = -1 * core->pending_freq;
420 return;
421 }
422
423 if (core->pending_freq < 0) {
424 /* a value less than 0 means that the governor has stopped
425 * and no more freq changes should be requested
426 */
427 return;
428 }
429
430 if (core->actual_freq != new_freq && core->pending_freq != new_freq) {
431 core->pending_freq = new_freq;
432 core->time_start = ktime_get();
433 wake_up(&core->wait_q);
434 }
435}
436
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700437static int msm_dcvs_update_freq(struct dcvs_core *core,
Eugene Seah76af9832012-03-28 18:43:53 -0600438 enum msm_dcvs_scm_event event, uint32_t param0,
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700439 uint32_t *ret1)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700440{
441 int ret = 0;
442 unsigned long flags = 0;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700443 uint32_t new_freq = -EINVAL;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700444
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700445 spin_lock_irqsave(&core->pending_freq_lock, flags);
446
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700447 ret = msm_dcvs_scm_event(core->dcvs_core_id, event, param0,
Eugene Seah76af9832012-03-28 18:43:53 -0600448 core->actual_freq, &new_freq, ret1);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700449 if (ret) {
Jeff Ohlstein4aa4a2b2012-06-28 19:03:49 -0700450 if (ret == -13)
451 ret = 0;
452 else
453 __err("Error (%d) sending SCM event %d for core %s\n",
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700454 ret, event, core->core_name);
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700455 goto out;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700456 }
457
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700458 if (new_freq == 0) {
459 /*
460 * sometimes TZ gives us a 0 freq back,
461 * do not queue up a request
462 */
463 goto out;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700464 }
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700465
466 request_freq_change(core, new_freq);
467
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700468out:
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700469 spin_unlock_irqrestore(&core->pending_freq_lock, flags);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700470
471 return ret;
472}
473
474static enum hrtimer_restart msm_dcvs_core_slack_timer(struct hrtimer *timer)
475{
476 int ret = 0;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700477 struct dcvs_core *core = container_of(timer,
478 struct dcvs_core, slack_timer);
Eugene Seah76af9832012-03-28 18:43:53 -0600479 uint32_t ret1;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700480
Abhijeet Dharmapurikar07cf2ff2012-09-13 19:05:13 -0700481 trace_printk("dcvs: Slack timer fired for core=%s\n", core->core_name);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700482 /**
483 * Timer expired, notify TZ
484 * Dont care about the third arg.
485 */
Eugene Seah76af9832012-03-28 18:43:53 -0600486 ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_QOS_TIMER_EXPIRED, 0,
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700487 &ret1);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700488 if (ret)
489 __err("Timer expired for core %s but failed to notify.\n",
490 core->core_name);
491
492 return HRTIMER_NORESTART;
493}
494
495/* Helper functions and macros for sysfs nodes for a core */
496#define CORE_FROM_ATTRIBS(attr, name) \
497 container_of(container_of(attr, struct core_attribs, name), \
498 struct dcvs_core, attrib);
499
500#define DCVS_PARAM_SHOW(_name, v) \
501static ssize_t msm_dcvs_attr_##_name##_show(struct kobject *kobj, \
502 struct kobj_attribute *attr, char *buf) \
503{ \
504 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
505 return snprintf(buf, PAGE_SIZE, "%d\n", v); \
506}
507
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700508#define DCVS_PARAM_STORE(_name) \
509static ssize_t msm_dcvs_attr_##_name##_show(struct kobject *kobj,\
510 struct kobj_attribute *attr, char *buf) \
511{ \
512 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
513 return snprintf(buf, PAGE_SIZE, "%d\n", core->info->_name); \
514} \
515static ssize_t msm_dcvs_attr_##_name##_store(struct kobject *kobj, \
516 struct kobj_attribute *attr, const char *buf, size_t count) \
517{ \
518 int ret = 0; \
519 uint32_t val = 0; \
520 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
521 ret = kstrtouint(buf, 10, &val); \
522 if (ret) { \
523 __err("Invalid input %s for %s\n", buf, __stringify(_name));\
524 } else { \
525 core->info->_name = val; \
526 } \
527 return count; \
528}
529
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700530#define DCVS_ALGO_PARAM(_name) \
531static ssize_t msm_dcvs_attr_##_name##_show(struct kobject *kobj,\
532 struct kobj_attribute *attr, char *buf) \
533{ \
534 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
535 return snprintf(buf, PAGE_SIZE, "%d\n", core->algo_param._name); \
536} \
537static ssize_t msm_dcvs_attr_##_name##_store(struct kobject *kobj, \
538 struct kobj_attribute *attr, const char *buf, size_t count) \
539{ \
540 int ret = 0; \
541 uint32_t val = 0; \
542 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700543 ret = kstrtouint(buf, 10, &val); \
544 if (ret) { \
545 __err("Invalid input %s for %s\n", buf, __stringify(_name));\
546 } else { \
547 uint32_t old_val = core->algo_param._name; \
548 core->algo_param._name = val; \
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700549 ret = msm_dcvs_scm_set_algo_params(core->dcvs_core_id, \
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700550 &core->algo_param); \
551 if (ret) { \
552 core->algo_param._name = old_val; \
553 __err("Error(%d) in setting %d for algo param %s\n",\
554 ret, val, __stringify(_name)); \
555 } \
556 } \
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700557 return count; \
558}
559
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700560#define DCVS_ENERGY_PARAM(_name) \
561static ssize_t msm_dcvs_attr_##_name##_show(struct kobject *kobj,\
562 struct kobj_attribute *attr, char *buf) \
563{ \
564 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
565 return snprintf(buf, PAGE_SIZE, "%d\n", core->coeffs._name); \
566} \
567static ssize_t msm_dcvs_attr_##_name##_store(struct kobject *kobj, \
568 struct kobj_attribute *attr, const char *buf, size_t count) \
569{ \
570 int ret = 0; \
571 int32_t val = 0; \
572 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700573 ret = kstrtoint(buf, 10, &val); \
574 if (ret) { \
575 __err("Invalid input %s for %s\n", buf, __stringify(_name));\
576 } else { \
577 int32_t old_val = core->coeffs._name; \
578 core->coeffs._name = val; \
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700579 ret = msm_dcvs_scm_set_power_params(core->dcvs_core_id, \
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700580 &core->info->power_param, &core->info->freq_tbl[0], \
581 &core->coeffs); \
582 if (ret) { \
583 core->coeffs._name = old_val; \
584 __err("Error(%d) in setting %d for coeffs param %s\n",\
585 ret, val, __stringify(_name)); \
586 } \
587 } \
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700588 return count; \
589}
590
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700591#define DCVS_RO_ATTRIB(i, _name) \
592 core->attrib._name.attr.name = __stringify(_name); \
593 core->attrib._name.attr.mode = S_IRUGO; \
594 core->attrib._name.show = msm_dcvs_attr_##_name##_show; \
595 core->attrib._name.store = NULL; \
596 core->attrib.attrib_group.attrs[i] = &core->attrib._name.attr;
597
598#define DCVS_RW_ATTRIB(i, _name) \
599 core->attrib._name.attr.name = __stringify(_name); \
600 core->attrib._name.attr.mode = S_IRUGO | S_IWUSR; \
601 core->attrib._name.show = msm_dcvs_attr_##_name##_show; \
602 core->attrib._name.store = msm_dcvs_attr_##_name##_store; \
603 core->attrib.attrib_group.attrs[i] = &core->attrib._name.attr;
604
605/**
606 * Function declarations for different attributes.
607 * Gets used when setting the attribute show and store parameters.
608 */
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700609DCVS_PARAM_SHOW(freq_change_us, (core->freq_change_us))
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700610
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700611DCVS_ALGO_PARAM(disable_pc_threshold)
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700612DCVS_ALGO_PARAM(em_win_size_min_us)
613DCVS_ALGO_PARAM(em_win_size_max_us)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700614DCVS_ALGO_PARAM(em_max_util_pct)
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700615DCVS_ALGO_PARAM(group_id)
616DCVS_ALGO_PARAM(max_freq_chg_time_us)
617DCVS_ALGO_PARAM(slack_mode_dynamic)
618DCVS_ALGO_PARAM(slack_time_min_us)
619DCVS_ALGO_PARAM(slack_time_max_us)
620DCVS_ALGO_PARAM(slack_weight_thresh_pct)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700621DCVS_ALGO_PARAM(ss_iobusy_conv)
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700622DCVS_ALGO_PARAM(ss_win_size_min_us)
623DCVS_ALGO_PARAM(ss_win_size_max_us)
624DCVS_ALGO_PARAM(ss_util_pct)
625
626DCVS_ENERGY_PARAM(active_coeff_a)
627DCVS_ENERGY_PARAM(active_coeff_b)
628DCVS_ENERGY_PARAM(active_coeff_c)
629DCVS_ENERGY_PARAM(leakage_coeff_a)
630DCVS_ENERGY_PARAM(leakage_coeff_b)
631DCVS_ENERGY_PARAM(leakage_coeff_c)
632DCVS_ENERGY_PARAM(leakage_coeff_d)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700633
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700634DCVS_PARAM_STORE(thermal_poll_ms)
635
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700636static int msm_dcvs_setup_core_sysfs(struct dcvs_core *core)
637{
638 int ret = 0;
639 struct kobject *core_kobj = NULL;
Abhijeet Dharmapurikar35876382012-09-13 16:09:51 -0700640 const int attr_count = 24;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700641
642 BUG_ON(!cores_kobj);
643
644 core->attrib.attrib_group.attrs =
645 kzalloc(attr_count * sizeof(struct attribute *), GFP_KERNEL);
646
647 if (!core->attrib.attrib_group.attrs) {
648 ret = -ENOMEM;
649 goto done;
650 }
651
Abhijeet Dharmapurikar35876382012-09-13 16:09:51 -0700652 DCVS_RO_ATTRIB(0, freq_change_us);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700653
Abhijeet Dharmapurikar35876382012-09-13 16:09:51 -0700654 DCVS_RW_ATTRIB(1, disable_pc_threshold);
655 DCVS_RW_ATTRIB(2, em_win_size_min_us);
656 DCVS_RW_ATTRIB(3, em_win_size_max_us);
657 DCVS_RW_ATTRIB(4, em_max_util_pct);
658 DCVS_RW_ATTRIB(5, group_id);
659 DCVS_RW_ATTRIB(6, max_freq_chg_time_us);
660 DCVS_RW_ATTRIB(7, slack_mode_dynamic);
661 DCVS_RW_ATTRIB(8, slack_weight_thresh_pct);
662 DCVS_RW_ATTRIB(9, slack_time_min_us);
663 DCVS_RW_ATTRIB(10, slack_time_max_us);
664 DCVS_RW_ATTRIB(11, ss_iobusy_conv);
665 DCVS_RW_ATTRIB(12, ss_win_size_min_us);
666 DCVS_RW_ATTRIB(13, ss_win_size_max_us);
667 DCVS_RW_ATTRIB(14, ss_util_pct);
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700668
Abhijeet Dharmapurikar35876382012-09-13 16:09:51 -0700669 DCVS_RW_ATTRIB(15, active_coeff_a);
670 DCVS_RW_ATTRIB(16, active_coeff_b);
671 DCVS_RW_ATTRIB(17, active_coeff_c);
672 DCVS_RW_ATTRIB(18, leakage_coeff_a);
673 DCVS_RW_ATTRIB(19, leakage_coeff_b);
674 DCVS_RW_ATTRIB(20, leakage_coeff_c);
675 DCVS_RW_ATTRIB(21, leakage_coeff_d);
676 DCVS_RW_ATTRIB(22, thermal_poll_ms);
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700677
Abhijeet Dharmapurikar35876382012-09-13 16:09:51 -0700678 core->attrib.attrib_group.attrs[23] = NULL;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700679
680 core_kobj = kobject_create_and_add(core->core_name, cores_kobj);
681 if (!core_kobj) {
682 ret = -ENOMEM;
683 goto done;
684 }
685
686 ret = sysfs_create_group(core_kobj, &core->attrib.attrib_group);
687 if (ret)
688 __err("Cannot create core %s attr group\n", core->core_name);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700689
690done:
691 if (ret) {
692 kfree(core->attrib.attrib_group.attrs);
693 kobject_del(core_kobj);
694 }
695
696 return ret;
697}
698
Steve Muckle5e5a60d2012-10-09 13:25:22 -0700699static int get_core_offset(enum msm_dcvs_core_type type, int num)
700{
701 int offset = -EINVAL;
702
703 switch (type) {
704 case MSM_DCVS_CORE_TYPE_CPU:
705 offset = CPU_OFFSET + num;
706 BUG_ON(offset >= GPU_OFFSET);
707 break;
708 case MSM_DCVS_CORE_TYPE_GPU:
709 offset = GPU_OFFSET + num;
710 BUG_ON(offset >= CORES_MAX);
711 break;
712 default:
713 BUG();
714 }
715
716 return offset;
717}
718
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700719/* Return the core and initialize non platform data specific numbers in it */
720static struct dcvs_core *msm_dcvs_add_core(enum msm_dcvs_core_type type,
721 int num)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700722{
723 struct dcvs_core *core = NULL;
724 int i;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700725 char name[CORE_NAME_MAX];
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700726
Steve Muckle5e5a60d2012-10-09 13:25:22 -0700727 i = get_core_offset(type, num);
728 if (i < 0)
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700729 return NULL;
Steve Muckle5e5a60d2012-10-09 13:25:22 -0700730
731 if (type == MSM_DCVS_CORE_TYPE_CPU)
732 snprintf(name, CORE_NAME_MAX, "cpu%d", num);
733 else
734 snprintf(name, CORE_NAME_MAX, "gpu%d", num);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700735
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700736 core = &core_list[i];
737 core->dcvs_core_id = i;
Abhijeet Dharmapurikar2ebc0fe2012-09-12 14:05:13 -0700738 strlcpy(core->core_name, name, CORE_NAME_MAX);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700739 spin_lock_init(&core->pending_freq_lock);
740 spin_lock_init(&core->idle_state_change_lock);
741 hrtimer_init(&core->slack_timer,
742 CLOCK_MONOTONIC, HRTIMER_MODE_REL_PINNED);
743 core->slack_timer.function = msm_dcvs_core_slack_timer;
Abhijeet Dharmapurikar2ebc0fe2012-09-12 14:05:13 -0700744 return core;
745}
746
747/* Return the core if found or add to list if @add_to_list is true */
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700748static struct dcvs_core *msm_dcvs_get_core(int offset)
Abhijeet Dharmapurikar2ebc0fe2012-09-12 14:05:13 -0700749{
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700750 /* if the handle is still not set bug */
751 BUG_ON(core_list[offset].dcvs_core_id == -1);
752 return &core_list[offset];
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700753}
754
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700755
756int msm_dcvs_register_core(
757 enum msm_dcvs_core_type type,
758 int type_core_num,
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700759 struct msm_dcvs_core_info *info,
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700760 int (*set_frequency)(int type_core_num, unsigned int freq),
761 unsigned int (*get_frequency)(int type_core_num),
762 int (*idle_enable)(int type_core_num,
Abhijeet Dharmapurikar6e9b34f2012-09-10 16:03:39 -0700763 enum msm_core_control_event event),
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700764 int sensor)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700765{
766 int ret = -EINVAL;
Steve Muckle5e5a60d2012-10-09 13:25:22 -0700767 int offset;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700768 struct dcvs_core *core = NULL;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700769 uint32_t ret1;
770 uint32_t ret2;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700771
Steve Muckle5e5a60d2012-10-09 13:25:22 -0700772 offset = get_core_offset(type, type_core_num);
773 if (offset < 0)
774 return ret;
775 if (core_list[offset].dcvs_core_id != -1)
776 return core_list[offset].dcvs_core_id;
777
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700778 core = msm_dcvs_add_core(type, type_core_num);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700779 if (!core)
780 return ret;
781
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700782 core->type = type;
783 core->type_core_num = type_core_num;
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700784 core->set_frequency = set_frequency;
785 core->get_frequency = get_frequency;
Abhijeet Dharmapurikar6e9b34f2012-09-10 16:03:39 -0700786 core->idle_enable = idle_enable;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700787 core->pending_freq = STOP_FREQ_CHANGE;
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700788
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700789 core->info = info;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700790 memcpy(&core->algo_param, &info->algo_param,
791 sizeof(struct msm_dcvs_algo_param));
792
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700793 memcpy(&core->coeffs, &info->energy_coeffs,
794 sizeof(struct msm_dcvs_energy_curve_coeffs));
795
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700796 /*
797 * The tz expects cpu0 to represent bit 0 in the mask, however the
798 * dcvs_core_id needs to start from 1, dcvs_core_id = 0 is used to
799 * indicate that this request is not associated with any core.
800 * mpdecision
801 */
802 info->core_param.core_bitmask_id
803 = 1 << (core->dcvs_core_id - CPU_OFFSET);
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700804 core->sensor = sensor;
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700805
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700806 ret = msm_dcvs_scm_register_core(core->dcvs_core_id, &info->core_param);
807 if (ret) {
808 __err("%s: scm register core fail handle = %d ret = %d\n",
809 __func__, core->dcvs_core_id, ret);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700810 goto bail;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700811 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700812
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700813 ret = msm_dcvs_scm_set_algo_params(core->dcvs_core_id,
814 &info->algo_param);
815 if (ret) {
816 __err("%s: scm algo params failed ret = %d\n", __func__, ret);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700817 goto bail;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700818 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700819
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700820 ret = msm_dcvs_scm_set_power_params(core->dcvs_core_id,
821 &info->power_param,
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700822 &info->freq_tbl[0], &core->coeffs);
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700823 if (ret) {
824 __err("%s: scm power params failed ret = %d\n", __func__, ret);
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700825 goto bail;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700826 }
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700827
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700828 ret = msm_dcvs_scm_event(core->dcvs_core_id, MSM_DCVS_SCM_CORE_ONLINE,
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700829 core->actual_freq, 0, &ret1, &ret2);
830 if (ret)
831 goto bail;
832
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700833 ret = msm_dcvs_setup_core_sysfs(core);
834 if (ret) {
835 __err("Unable to setup core %s sysfs\n", core->core_name);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700836 goto bail;
837 }
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700838 core->idle_entered = -1;
Abhijeet Dharmapurikarbbb52fe2012-08-31 20:31:16 -0700839 init_waitqueue_head(&core->wait_q);
840 core->task = kthread_run(msm_dcvs_do_freq, (void *)core,
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700841 "msm_dcvs/%d", core->dcvs_core_id);
842 ret = core->dcvs_core_id;
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700843
844 INIT_DELAYED_WORK(&core->temperature_work, msm_dcvs_report_temp_work);
845 schedule_delayed_work(&core->temperature_work,
846 msecs_to_jiffies(info->thermal_poll_ms));
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700847 return ret;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700848bail:
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700849 core->dcvs_core_id = -1;
850 return -EINVAL;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700851}
852EXPORT_SYMBOL(msm_dcvs_register_core);
853
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700854void msm_dcvs_update_limits(int dcvs_core_id)
Abhijeet Dharmapurikarc43f0db2012-08-31 20:42:53 -0700855{
856 struct dcvs_core *core;
857
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700858 if (dcvs_core_id < CPU_OFFSET || dcvs_core_id > CORES_MAX) {
859 __err("%s invalid dcvs_core_id = %d returning -EINVAL\n",
860 __func__, dcvs_core_id);
Abhijeet Dharmapurikarc43f0db2012-08-31 20:42:53 -0700861 return;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700862 }
Abhijeet Dharmapurikarc43f0db2012-08-31 20:42:53 -0700863
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700864 core = msm_dcvs_get_core(dcvs_core_id);
865 core->actual_freq = core->get_frequency(core->type_core_num);
Abhijeet Dharmapurikarc43f0db2012-08-31 20:42:53 -0700866}
867
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700868int msm_dcvs_freq_sink_start(int dcvs_core_id)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700869{
870 int ret = -EINVAL;
871 struct dcvs_core *core = NULL;
Eugene Seah76af9832012-03-28 18:43:53 -0600872 uint32_t ret1;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700873 unsigned long flags;
Abhijeet Dharmapurikar8d843b32012-09-13 18:33:20 -0700874 int new_freq;
875 int timer_interval_us;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700876
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700877 if (dcvs_core_id < CPU_OFFSET || dcvs_core_id > CORES_MAX) {
878 __err("%s invalid dcvs_core_id = %d returning -EINVAL\n",
879 __func__, dcvs_core_id);
880 return -EINVAL;
881 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700882
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700883 core = msm_dcvs_get_core(dcvs_core_id);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700884 if (!core)
885 return ret;
886
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700887 core->actual_freq = core->get_frequency(core->type_core_num);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700888
889 spin_lock_irqsave(&core->pending_freq_lock, flags);
890 /* mark that we are ready to accept new frequencies */
891 request_freq_change(core, NO_OUTSTANDING_FREQ_CHANGE);
892 spin_unlock_irqrestore(&core->pending_freq_lock, flags);
893
894 spin_lock_irqsave(&core->idle_state_change_lock, flags);
895 core->idle_entered = -1;
896 spin_unlock_irqrestore(&core->idle_state_change_lock, flags);
897
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700898 /* Notify TZ to start receiving idle info for the core */
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700899 ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_DCVS_ENABLE, 1, &ret1);
900
Abhijeet Dharmapurikar8d843b32012-09-13 18:33:20 -0700901 ret = msm_dcvs_scm_event(
902 core->dcvs_core_id, MSM_DCVS_SCM_CORE_ONLINE, core->actual_freq,
903 0, &new_freq, &timer_interval_us);
904 if (ret)
905 __err("Error (%d) DCVS sending online for %s\n",
906 ret, core->core_name);
907
908 if (new_freq != 0) {
909 spin_lock_irqsave(&core->pending_freq_lock, flags);
910 request_freq_change(core, new_freq);
911 spin_unlock_irqrestore(&core->pending_freq_lock, flags);
912 }
913 force_start_slack_timer(core, timer_interval_us);
914
915
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700916 core->idle_enable(core->type_core_num, MSM_DCVS_ENABLE_IDLE_PULSE);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700917 return 0;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700918}
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700919EXPORT_SYMBOL(msm_dcvs_freq_sink_start);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700920
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700921int msm_dcvs_freq_sink_stop(int dcvs_core_id)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700922{
923 int ret = -EINVAL;
924 struct dcvs_core *core = NULL;
Eugene Seah76af9832012-03-28 18:43:53 -0600925 uint32_t ret1;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700926 uint32_t freq;
927 unsigned long flags;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700928
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700929 if (dcvs_core_id < 0 || dcvs_core_id > CORES_MAX) {
930 pr_err("%s invalid dcvs_core_id = %d returning -EINVAL\n",
931 __func__, dcvs_core_id);
932 return -EINVAL;
933 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700934
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700935 core = msm_dcvs_get_core(dcvs_core_id);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700936 if (!core) {
937 __err("couldn't find core for coreid = %d\n", dcvs_core_id);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700938 return ret;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700939 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700940
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700941 core->idle_enable(core->type_core_num, MSM_DCVS_DISABLE_IDLE_PULSE);
942 /* Notify TZ to stop receiving idle info for the core */
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700943 ret = msm_dcvs_scm_event(core->dcvs_core_id, MSM_DCVS_SCM_DCVS_ENABLE,
944 0, core->actual_freq, &freq, &ret1);
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700945 core->idle_enable(core->type_core_num,
946 MSM_DCVS_ENABLE_HIGH_LATENCY_MODES);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700947 spin_lock_irqsave(&core->pending_freq_lock, flags);
948 /* flush out all the pending freq changes */
949 request_freq_change(core, STOP_FREQ_CHANGE);
950 spin_unlock_irqrestore(&core->pending_freq_lock, flags);
951 force_stop_slack_timer(core);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700952
953 return 0;
954}
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700955EXPORT_SYMBOL(msm_dcvs_freq_sink_stop);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700956
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700957int msm_dcvs_idle(int dcvs_core_id, enum msm_core_idle_state state,
958 uint32_t iowaited)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700959{
960 int ret = 0;
961 struct dcvs_core *core = NULL;
Eugene Seah76af9832012-03-28 18:43:53 -0600962 uint32_t timer_interval_us = 0;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700963 uint32_t r0, r1;
964
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700965 if (dcvs_core_id < CPU_OFFSET || dcvs_core_id > CORES_MAX) {
966 pr_err("invalid dcvs_core_id = %d ret -EINVAL\n", dcvs_core_id);
967 return -EINVAL;
968 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700969
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700970 core = msm_dcvs_get_core(dcvs_core_id);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700971
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700972 switch (state) {
973 case MSM_DCVS_IDLE_ENTER:
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700974 stop_slack_timer(core);
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700975 ret = msm_dcvs_scm_event(core->dcvs_core_id,
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700976 MSM_DCVS_SCM_IDLE_ENTER, 0, 0, &r0, &r1);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700977 if (ret < 0 && ret != -13)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700978 __err("Error (%d) sending idle enter for %s\n",
979 ret, core->core_name);
Abhijeet Dharmapurikar07cf2ff2012-09-13 19:05:13 -0700980 trace_msm_dcvs_idle("idle_enter_exit", core->core_name, 1);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700981 break;
982
983 case MSM_DCVS_IDLE_EXIT:
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700984 ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_IDLE_EXIT,
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700985 iowaited, &timer_interval_us);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700986 if (ret)
987 __err("Error (%d) sending idle exit for %s\n",
988 ret, core->core_name);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700989 start_slack_timer(core, timer_interval_us);
Abhijeet Dharmapurikar07cf2ff2012-09-13 19:05:13 -0700990 trace_msm_dcvs_idle("idle_enter_exit", core->core_name, 0);
991 trace_msm_dcvs_iowait("iowait", core->core_name, iowaited);
992 trace_msm_dcvs_slack_time("slack_timer_dcvs", core->core_name,
993 timer_interval_us);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700994 break;
995 }
996
997 return ret;
998}
999EXPORT_SYMBOL(msm_dcvs_idle);
1000
1001static int __init msm_dcvs_late_init(void)
1002{
1003 struct kobject *module_kobj = NULL;
1004 int ret = 0;
1005
1006 module_kobj = kset_find_obj(module_kset, KBUILD_MODNAME);
1007 if (!module_kobj) {
1008 pr_err("%s: cannot find kobject for module %s\n",
1009 __func__, KBUILD_MODNAME);
1010 ret = -ENOENT;
1011 goto err;
1012 }
1013
1014 cores_kobj = kobject_create_and_add("cores", module_kobj);
1015 if (!cores_kobj) {
1016 __err("Cannot create %s kobject\n", "cores");
1017 ret = -ENOMEM;
1018 goto err;
1019 }
1020
1021 debugfs_base = debugfs_create_dir("msm_dcvs", NULL);
1022 if (!debugfs_base) {
1023 __err("Cannot create debugfs base %s\n", "msm_dcvs");
1024 ret = -ENOENT;
1025 goto err;
1026 }
1027
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001028err:
1029 if (ret) {
1030 kobject_del(cores_kobj);
1031 cores_kobj = NULL;
1032 debugfs_remove(debugfs_base);
1033 }
1034
1035 return ret;
1036}
1037late_initcall(msm_dcvs_late_init);
1038
1039static int __init msm_dcvs_early_init(void)
1040{
1041 int ret = 0;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001042 int i;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001043
1044 if (!msm_dcvs_enabled) {
1045 __info("Not enabled (%d)\n", msm_dcvs_enabled);
1046 return 0;
1047 }
1048
Abhijeet Dharmapurikar3edb5de2012-09-13 11:02:03 -07001049
1050 /* Only need about 32kBytes for normal operation */
1051 ret = msm_dcvs_scm_init(SZ_32K);
1052 if (ret) {
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001053 __err("Unable to initialize DCVS err=%d\n", ret);
Abhijeet Dharmapurikar3edb5de2012-09-13 11:02:03 -07001054 goto done;
1055 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001056
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001057 for (i = 0; i < CORES_MAX; i++)
1058 core_list[i].dcvs_core_id = -1;
Abhijeet Dharmapurikar3edb5de2012-09-13 11:02:03 -07001059done:
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001060 return ret;
1061}
1062postcore_initcall(msm_dcvs_early_init);