blob: aadf08f92a24f52b5ae31060d45ce3befe2b13e0 [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>
29
30#define CORE_HANDLE_OFFSET (0xA0)
31#define __err(f, ...) pr_err("MSM_DCVS: %s: " f, __func__, __VA_ARGS__)
32#define __info(f, ...) pr_info("MSM_DCVS: %s: " f, __func__, __VA_ARGS__)
33#define MAX_PENDING (5)
34
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070035struct core_attribs {
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -070036 struct kobj_attribute core_id;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070037 struct kobj_attribute idle_enabled;
38 struct kobj_attribute freq_change_enabled;
39 struct kobj_attribute actual_freq;
40 struct kobj_attribute freq_change_us;
41
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070042 struct kobj_attribute disable_pc_threshold;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -070043 struct kobj_attribute em_win_size_min_us;
44 struct kobj_attribute em_win_size_max_us;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070045 struct kobj_attribute em_max_util_pct;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -070046 struct kobj_attribute group_id;
47 struct kobj_attribute max_freq_chg_time_us;
48 struct kobj_attribute slack_mode_dynamic;
49 struct kobj_attribute slack_time_min_us;
50 struct kobj_attribute slack_time_max_us;
51 struct kobj_attribute slack_weight_thresh_pct;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070052 struct kobj_attribute ss_iobusy_conv;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -070053 struct kobj_attribute ss_win_size_min_us;
54 struct kobj_attribute ss_win_size_max_us;
55 struct kobj_attribute ss_util_pct;
56
57 struct kobj_attribute active_coeff_a;
58 struct kobj_attribute active_coeff_b;
59 struct kobj_attribute active_coeff_c;
60 struct kobj_attribute leakage_coeff_a;
61 struct kobj_attribute leakage_coeff_b;
62 struct kobj_attribute leakage_coeff_c;
63 struct kobj_attribute leakage_coeff_d;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070064
65 struct attribute_group attrib_group;
66};
67
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -070068enum pending_freq_state {
69 /*
70 * used by the thread to check if pending_freq was updated while it was
71 * setting previous frequency - this is written to and used by the
72 * freq updating thread
73 */
74 NO_OUTSTANDING_FREQ_CHANGE = 0,
75
76 /*
77 * This request is set to indicate that the governor is stopped and no
78 * more frequency change requests are accepted untill it starts again.
79 * This is checked/used by the threads that want to change the freq
80 */
81 STOP_FREQ_CHANGE = -1,
82
83 /*
84 * Any other +ve value means that a freq change was requested and the
85 * thread has not gotten around to update it
86 *
87 * Any other -ve value means that this is the last freq change i.e. a
88 * freq change was requested but the thread has not run yet and
89 * meanwhile the governor was stopped.
90 */
91};
92
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070093struct dcvs_core {
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -070094 spinlock_t idle_state_change_lock;
95 /* 0 when not idle (busy) 1 when idle and -1 when governor starts and
96 * we dont know whether the next call is going to be idle enter or exit
97 */
98 int idle_entered;
99
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700100 enum msm_dcvs_core_type type;
101 /* this is the number in each type for example cpu 0,1,2 and gpu 0,1 */
102 int type_core_num;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700103 char core_name[CORE_NAME_MAX];
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700104 uint32_t actual_freq;
105 uint32_t freq_change_us;
106
107 uint32_t max_time_us; /* core param */
108
109 struct msm_dcvs_algo_param algo_param;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700110 struct msm_dcvs_energy_curve_coeffs coeffs;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700111
112 /* private */
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700113 ktime_t time_start;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700114 struct task_struct *task;
115 struct core_attribs attrib;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700116 uint32_t dcvs_core_id;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700117 struct msm_dcvs_core_info *info;
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700118 int sensor;
Abhijeet Dharmapurikarbbb52fe2012-08-31 20:31:16 -0700119 wait_queue_head_t wait_q;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700120
121 int (*set_frequency)(int type_core_num, unsigned int freq);
122 unsigned int (*get_frequency)(int type_core_num);
123 int (*idle_enable)(int type_core_num,
Abhijeet Dharmapurikar6e9b34f2012-09-10 16:03:39 -0700124 enum msm_core_control_event event);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700125
126 spinlock_t pending_freq_lock;
127 int pending_freq;
128
129 struct hrtimer slack_timer;
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
150static void stop_slack_timer(struct dcvs_core *core)
151{
152 unsigned long flags;
153
154 spin_lock_irqsave(&core->idle_state_change_lock, flags);
155 /* err only for cpu type's GPU's can do idle exit consecutively */
156 if (core->idle_entered == 1 && !(core->dcvs_core_id >= GPU_OFFSET))
157 __err("%s trying to reenter idle", core->core_name);
158 core->idle_entered = 1;
159 hrtimer_cancel(&core->slack_timer);
160 core->idle_entered = 1;
161 spin_unlock_irqrestore(&core->idle_state_change_lock, flags);
162}
163
164static void start_slack_timer(struct dcvs_core *core, int slack_us)
165{
166 unsigned long flags1, flags2;
167 int ret;
168
169 spin_lock_irqsave(&core->idle_state_change_lock, flags2);
170
171 spin_lock_irqsave(&core->pending_freq_lock, flags1);
172
173 /* err only for cpu type's GPU's can do idle enter consecutively */
174 if (core->idle_entered == 0 && !(core->dcvs_core_id >= GPU_OFFSET))
175 __err("%s trying to reexit idle", core->core_name);
176 core->idle_entered = 0;
177 /*
178 * only start the timer if governor is not stopped
179 */
180 if (slack_us != 0
181 && !(core->pending_freq < NO_OUTSTANDING_FREQ_CHANGE)) {
182 ret = hrtimer_start(&core->slack_timer,
183 ktime_set(0, slack_us * 1000),
184 HRTIMER_MODE_REL_PINNED);
185 if (ret) {
186 pr_err("%s Failed to start timer ret = %d\n",
187 core->core_name, ret);
188 }
189 }
190 spin_unlock_irqrestore(&core->pending_freq_lock, flags1);
191
192 spin_unlock_irqrestore(&core->idle_state_change_lock, flags2);
193}
194
195static void restart_slack_timer(struct dcvs_core *core, int slack_us)
196{
197 unsigned long flags1, flags2;
198 int ret;
199
200 spin_lock_irqsave(&core->idle_state_change_lock, flags2);
201
202 hrtimer_cancel(&core->slack_timer);
203
204 spin_lock_irqsave(&core->pending_freq_lock, flags1);
205
206 /*
207 * only start the timer if idle is not entered
208 * and governor is not stopped
209 */
210 if (slack_us != 0 && (core->idle_entered != 1)
211 && !(core->pending_freq < NO_OUTSTANDING_FREQ_CHANGE)) {
212 ret = hrtimer_start(&core->slack_timer,
213 ktime_set(0, slack_us * 1000),
214 HRTIMER_MODE_REL_PINNED);
215 if (ret) {
216 pr_err("%s Failed to start timer ret = %d\n",
217 core->core_name, ret);
218 }
219 }
220 spin_unlock_irqrestore(&core->pending_freq_lock, flags1);
221 spin_unlock_irqrestore(&core->idle_state_change_lock, flags2);
222}
223
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700224static int __msm_dcvs_change_freq(struct dcvs_core *core)
225{
226 int ret = 0;
227 unsigned long flags = 0;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700228 int requested_freq = 0;
229 ktime_t time_start;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700230 uint32_t slack_us = 0;
231 uint32_t ret1 = 0;
232
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700233 spin_lock_irqsave(&core->pending_freq_lock, flags);
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700234repeat:
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700235 BUG_ON(!core->pending_freq);
236 if (core->pending_freq == STOP_FREQ_CHANGE)
237 BUG();
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700238
239 requested_freq = core->pending_freq;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700240 time_start = core->time_start;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700241 core->time_start = ns_to_ktime(0);
242
243 if (requested_freq < 0) {
244 requested_freq = -1 * requested_freq;
245 core->pending_freq = STOP_FREQ_CHANGE;
246 } else {
247 core->pending_freq = NO_OUTSTANDING_FREQ_CHANGE;
248 }
249
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700250 if (requested_freq == core->actual_freq)
251 goto out;
252
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700253 spin_unlock_irqrestore(&core->pending_freq_lock, flags);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700254
255 /**
256 * Call the frequency sink driver to change the frequency
257 * We will need to get back the actual frequency in KHz and
258 * the record the time taken to change it.
259 */
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700260 ret = core->set_frequency(core->type_core_num, requested_freq);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700261 if (ret <= 0)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700262 __err("Core %s failed to set freq %u\n",
263 core->core_name, requested_freq);
Eugene Seah76af9832012-03-28 18:43:53 -0600264 /* continue to call TZ to get updated slack timer */
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700265 else
Eugene Seah76af9832012-03-28 18:43:53 -0600266 core->actual_freq = ret;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700267
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700268 core->freq_change_us = (uint32_t)ktime_to_us(
269 ktime_sub(ktime_get(), time_start));
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700270
271 /**
272 * Disable low power modes if the actual frequency is >
273 * disable_pc_threshold.
274 */
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700275 if (core->actual_freq > core->algo_param.disable_pc_threshold) {
276 core->idle_enable(core->type_core_num,
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700277 MSM_DCVS_DISABLE_HIGH_LATENCY_MODES);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700278 } else if (core->actual_freq <= core->algo_param.disable_pc_threshold) {
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700279 core->idle_enable(core->type_core_num,
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700280 MSM_DCVS_ENABLE_HIGH_LATENCY_MODES);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700281 }
282
283 /**
284 * Update algorithm with new freq and time taken to change
285 * to this frequency and that will get us the new slack
286 * timer
287 */
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700288 ret = msm_dcvs_scm_event(core->dcvs_core_id,
289 MSM_DCVS_SCM_CLOCK_FREQ_UPDATE,
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700290 core->actual_freq, core->freq_change_us,
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700291 &slack_us, &ret1);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700292 if (ret) {
293 __err("Error sending core (%s) dcvs_core_id = %d freq change (%u) reqfreq = %d slack_us=%d ret = %d\n",
294 core->core_name, core->dcvs_core_id,
295 core->actual_freq, requested_freq,
296 slack_us, ret);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700297 }
298
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700299 /* TODO confirm that we get a valid freq from SM even when the above
300 * FREQ_UPDATE fails
301 */
302 restart_slack_timer(core, slack_us);
303 spin_lock_irqsave(&core->pending_freq_lock, flags);
304
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700305 /**
306 * By the time we are done with freq changes, we could be asked to
307 * change again. Check before exiting.
308 */
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700309 if (core->pending_freq != NO_OUTSTANDING_FREQ_CHANGE
310 && core->pending_freq != STOP_FREQ_CHANGE) {
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700311 goto repeat;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700312 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700313
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700314out: /* should always be jumped to with the spin_lock held */
315 spin_unlock_irqrestore(&core->pending_freq_lock, flags);
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700316
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700317 return ret;
318}
319
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700320static int __msm_dcvs_report_temp(struct dcvs_core *core)
321{
322 struct msm_dcvs_core_info *info = core->info;
323 struct tsens_device tsens_dev;
324 int ret;
325 unsigned long temp = 0;
326
327 tsens_dev.sensor_num = core->sensor;
328 ret = tsens_get_temp(&tsens_dev, &temp);
329 if (!ret) {
330 tsens_dev.sensor_num = 0;
331 ret = tsens_get_temp(&tsens_dev, &temp);
332 if (!ret)
333 return -ENODEV;
334 }
335
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700336 ret = msm_dcvs_scm_set_power_params(core->dcvs_core_id,
337 &info->power_param,
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700338 &info->freq_tbl[0], &core->coeffs);
339 return ret;
340}
341
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700342static int msm_dcvs_do_freq(void *data)
343{
344 struct dcvs_core *core = (struct dcvs_core *)data;
345 static struct sched_param param = {.sched_priority = MAX_RT_PRIO - 1};
346
347 sched_setscheduler(current, SCHED_FIFO, &param);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700348
349 while (!kthread_should_stop()) {
Abhijeet Dharmapurikarbbb52fe2012-08-31 20:31:16 -0700350 wait_event(core->wait_q, !(core->pending_freq == 0 ||
351 core->pending_freq == -1) ||
352 kthread_should_stop());
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700353
354 if (kthread_should_stop())
355 break;
356
Abhijeet Dharmapurikarbbb52fe2012-08-31 20:31:16 -0700357 __msm_dcvs_change_freq(core);
358 __msm_dcvs_report_temp(core);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700359 }
360
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700361 return 0;
362}
363
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700364/* freq_pending_lock should be held */
365static void request_freq_change(struct dcvs_core *core, int new_freq)
366{
367 if (new_freq == NO_OUTSTANDING_FREQ_CHANGE) {
368 if (core->pending_freq != STOP_FREQ_CHANGE) {
369 __err("%s gov started with earlier pending freq %d\n",
370 core->core_name, core->pending_freq);
371 }
372 core->pending_freq = NO_OUTSTANDING_FREQ_CHANGE;
373 return;
374 }
375
376 if (new_freq == STOP_FREQ_CHANGE) {
377 if (core->pending_freq == NO_OUTSTANDING_FREQ_CHANGE)
378 core->pending_freq = STOP_FREQ_CHANGE;
379 else if (core->pending_freq > 0)
380 core->pending_freq = -1 * core->pending_freq;
381 return;
382 }
383
384 if (core->pending_freq < 0) {
385 /* a value less than 0 means that the governor has stopped
386 * and no more freq changes should be requested
387 */
388 return;
389 }
390
391 if (core->actual_freq != new_freq && core->pending_freq != new_freq) {
392 core->pending_freq = new_freq;
393 core->time_start = ktime_get();
394 wake_up(&core->wait_q);
395 }
396}
397
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700398static int msm_dcvs_update_freq(struct dcvs_core *core,
Eugene Seah76af9832012-03-28 18:43:53 -0600399 enum msm_dcvs_scm_event event, uint32_t param0,
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700400 uint32_t *ret1)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700401{
402 int ret = 0;
403 unsigned long flags = 0;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700404 uint32_t new_freq = -EINVAL;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700405
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700406 spin_lock_irqsave(&core->pending_freq_lock, flags);
407
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700408 ret = msm_dcvs_scm_event(core->dcvs_core_id, event, param0,
Eugene Seah76af9832012-03-28 18:43:53 -0600409 core->actual_freq, &new_freq, ret1);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700410 if (ret) {
Jeff Ohlstein4aa4a2b2012-06-28 19:03:49 -0700411 if (ret == -13)
412 ret = 0;
413 else
414 __err("Error (%d) sending SCM event %d for core %s\n",
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700415 ret, event, core->core_name);
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700416 goto out;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700417 }
418
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700419 if (new_freq == 0) {
420 /*
421 * sometimes TZ gives us a 0 freq back,
422 * do not queue up a request
423 */
424 goto out;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700425 }
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700426
427 request_freq_change(core, new_freq);
428
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700429out:
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700430 spin_unlock_irqrestore(&core->pending_freq_lock, flags);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700431
432 return ret;
433}
434
435static enum hrtimer_restart msm_dcvs_core_slack_timer(struct hrtimer *timer)
436{
437 int ret = 0;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700438 struct dcvs_core *core = container_of(timer,
439 struct dcvs_core, slack_timer);
Eugene Seah76af9832012-03-28 18:43:53 -0600440 uint32_t ret1;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700441
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700442 /**
443 * Timer expired, notify TZ
444 * Dont care about the third arg.
445 */
Eugene Seah76af9832012-03-28 18:43:53 -0600446 ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_QOS_TIMER_EXPIRED, 0,
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700447 &ret1);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700448 if (ret)
449 __err("Timer expired for core %s but failed to notify.\n",
450 core->core_name);
451
452 return HRTIMER_NORESTART;
453}
454
455/* Helper functions and macros for sysfs nodes for a core */
456#define CORE_FROM_ATTRIBS(attr, name) \
457 container_of(container_of(attr, struct core_attribs, name), \
458 struct dcvs_core, attrib);
459
460#define DCVS_PARAM_SHOW(_name, v) \
461static ssize_t msm_dcvs_attr_##_name##_show(struct kobject *kobj, \
462 struct kobj_attribute *attr, char *buf) \
463{ \
464 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
465 return snprintf(buf, PAGE_SIZE, "%d\n", v); \
466}
467
468#define DCVS_ALGO_PARAM(_name) \
469static ssize_t msm_dcvs_attr_##_name##_show(struct kobject *kobj,\
470 struct kobj_attribute *attr, char *buf) \
471{ \
472 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
473 return snprintf(buf, PAGE_SIZE, "%d\n", core->algo_param._name); \
474} \
475static ssize_t msm_dcvs_attr_##_name##_store(struct kobject *kobj, \
476 struct kobj_attribute *attr, const char *buf, size_t count) \
477{ \
478 int ret = 0; \
479 uint32_t val = 0; \
480 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700481 ret = kstrtouint(buf, 10, &val); \
482 if (ret) { \
483 __err("Invalid input %s for %s\n", buf, __stringify(_name));\
484 } else { \
485 uint32_t old_val = core->algo_param._name; \
486 core->algo_param._name = val; \
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700487 ret = msm_dcvs_scm_set_algo_params(core->dcvs_core_id, \
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700488 &core->algo_param); \
489 if (ret) { \
490 core->algo_param._name = old_val; \
491 __err("Error(%d) in setting %d for algo param %s\n",\
492 ret, val, __stringify(_name)); \
493 } \
494 } \
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700495 return count; \
496}
497
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700498#define DCVS_ENERGY_PARAM(_name) \
499static ssize_t msm_dcvs_attr_##_name##_show(struct kobject *kobj,\
500 struct kobj_attribute *attr, char *buf) \
501{ \
502 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
503 return snprintf(buf, PAGE_SIZE, "%d\n", core->coeffs._name); \
504} \
505static ssize_t msm_dcvs_attr_##_name##_store(struct kobject *kobj, \
506 struct kobj_attribute *attr, const char *buf, size_t count) \
507{ \
508 int ret = 0; \
509 int32_t val = 0; \
510 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700511 ret = kstrtoint(buf, 10, &val); \
512 if (ret) { \
513 __err("Invalid input %s for %s\n", buf, __stringify(_name));\
514 } else { \
515 int32_t old_val = core->coeffs._name; \
516 core->coeffs._name = val; \
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700517 ret = msm_dcvs_scm_set_power_params(core->dcvs_core_id, \
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700518 &core->info->power_param, &core->info->freq_tbl[0], \
519 &core->coeffs); \
520 if (ret) { \
521 core->coeffs._name = old_val; \
522 __err("Error(%d) in setting %d for coeffs param %s\n",\
523 ret, val, __stringify(_name)); \
524 } \
525 } \
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700526 return count; \
527}
528
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700529#define DCVS_RO_ATTRIB(i, _name) \
530 core->attrib._name.attr.name = __stringify(_name); \
531 core->attrib._name.attr.mode = S_IRUGO; \
532 core->attrib._name.show = msm_dcvs_attr_##_name##_show; \
533 core->attrib._name.store = NULL; \
534 core->attrib.attrib_group.attrs[i] = &core->attrib._name.attr;
535
536#define DCVS_RW_ATTRIB(i, _name) \
537 core->attrib._name.attr.name = __stringify(_name); \
538 core->attrib._name.attr.mode = S_IRUGO | S_IWUSR; \
539 core->attrib._name.show = msm_dcvs_attr_##_name##_show; \
540 core->attrib._name.store = msm_dcvs_attr_##_name##_store; \
541 core->attrib.attrib_group.attrs[i] = &core->attrib._name.attr;
542
543/**
544 * Function declarations for different attributes.
545 * Gets used when setting the attribute show and store parameters.
546 */
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700547DCVS_PARAM_SHOW(core_id, core->dcvs_core_id)
548DCVS_PARAM_SHOW(idle_enabled, (core->idle_enable != NULL))
549DCVS_PARAM_SHOW(freq_change_enabled, (core->set_frequency != NULL))
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700550DCVS_PARAM_SHOW(actual_freq, (core->actual_freq))
551DCVS_PARAM_SHOW(freq_change_us, (core->freq_change_us))
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700552
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700553DCVS_ALGO_PARAM(disable_pc_threshold)
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700554DCVS_ALGO_PARAM(em_win_size_min_us)
555DCVS_ALGO_PARAM(em_win_size_max_us)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700556DCVS_ALGO_PARAM(em_max_util_pct)
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700557DCVS_ALGO_PARAM(group_id)
558DCVS_ALGO_PARAM(max_freq_chg_time_us)
559DCVS_ALGO_PARAM(slack_mode_dynamic)
560DCVS_ALGO_PARAM(slack_time_min_us)
561DCVS_ALGO_PARAM(slack_time_max_us)
562DCVS_ALGO_PARAM(slack_weight_thresh_pct)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700563DCVS_ALGO_PARAM(ss_iobusy_conv)
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700564DCVS_ALGO_PARAM(ss_win_size_min_us)
565DCVS_ALGO_PARAM(ss_win_size_max_us)
566DCVS_ALGO_PARAM(ss_util_pct)
567
568DCVS_ENERGY_PARAM(active_coeff_a)
569DCVS_ENERGY_PARAM(active_coeff_b)
570DCVS_ENERGY_PARAM(active_coeff_c)
571DCVS_ENERGY_PARAM(leakage_coeff_a)
572DCVS_ENERGY_PARAM(leakage_coeff_b)
573DCVS_ENERGY_PARAM(leakage_coeff_c)
574DCVS_ENERGY_PARAM(leakage_coeff_d)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700575
576static int msm_dcvs_setup_core_sysfs(struct dcvs_core *core)
577{
578 int ret = 0;
579 struct kobject *core_kobj = NULL;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700580 const int attr_count = 27;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700581
582 BUG_ON(!cores_kobj);
583
584 core->attrib.attrib_group.attrs =
585 kzalloc(attr_count * sizeof(struct attribute *), GFP_KERNEL);
586
587 if (!core->attrib.attrib_group.attrs) {
588 ret = -ENOMEM;
589 goto done;
590 }
591
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700592 DCVS_RO_ATTRIB(0, core_id);
593 DCVS_RO_ATTRIB(1, idle_enabled);
594 DCVS_RO_ATTRIB(2, freq_change_enabled);
595 DCVS_RO_ATTRIB(3, actual_freq);
596 DCVS_RO_ATTRIB(4, freq_change_us);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700597
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700598 DCVS_RW_ATTRIB(5, disable_pc_threshold);
599 DCVS_RW_ATTRIB(6, em_win_size_min_us);
600 DCVS_RW_ATTRIB(7, em_win_size_max_us);
601 DCVS_RW_ATTRIB(8, em_max_util_pct);
602 DCVS_RW_ATTRIB(9, group_id);
603 DCVS_RW_ATTRIB(10, max_freq_chg_time_us);
604 DCVS_RW_ATTRIB(11, slack_mode_dynamic);
605 DCVS_RW_ATTRIB(12, slack_time_min_us);
606 DCVS_RW_ATTRIB(13, slack_time_max_us);
607 DCVS_RW_ATTRIB(14, slack_weight_thresh_pct);
608 DCVS_RW_ATTRIB(15, ss_iobusy_conv);
609 DCVS_RW_ATTRIB(16, ss_win_size_min_us);
610 DCVS_RW_ATTRIB(17, ss_win_size_max_us);
611 DCVS_RW_ATTRIB(18, ss_util_pct);
612
613 DCVS_RW_ATTRIB(19, active_coeff_a);
614 DCVS_RW_ATTRIB(20, active_coeff_b);
615 DCVS_RW_ATTRIB(21, active_coeff_c);
616 DCVS_RW_ATTRIB(22, leakage_coeff_a);
617 DCVS_RW_ATTRIB(23, leakage_coeff_b);
618 DCVS_RW_ATTRIB(24, leakage_coeff_c);
619 DCVS_RW_ATTRIB(25, leakage_coeff_d);
620
621 core->attrib.attrib_group.attrs[26] = NULL;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700622
623 core_kobj = kobject_create_and_add(core->core_name, cores_kobj);
624 if (!core_kobj) {
625 ret = -ENOMEM;
626 goto done;
627 }
628
629 ret = sysfs_create_group(core_kobj, &core->attrib.attrib_group);
630 if (ret)
631 __err("Cannot create core %s attr group\n", core->core_name);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700632
633done:
634 if (ret) {
635 kfree(core->attrib.attrib_group.attrs);
636 kobject_del(core_kobj);
637 }
638
639 return ret;
640}
641
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700642/* Return the core and initialize non platform data specific numbers in it */
643static struct dcvs_core *msm_dcvs_add_core(enum msm_dcvs_core_type type,
644 int num)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700645{
646 struct dcvs_core *core = NULL;
647 int i;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700648 char name[CORE_NAME_MAX];
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700649
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700650 switch (type) {
651 case MSM_DCVS_CORE_TYPE_CPU:
652 i = CPU_OFFSET + num;
653 BUG_ON(i >= GPU_OFFSET);
654 snprintf(name, CORE_NAME_MAX, "cpu%d", num);
655 break;
656 case MSM_DCVS_CORE_TYPE_GPU:
657 i = GPU_OFFSET + num;
658 BUG_ON(i >= CORES_MAX);
659 snprintf(name, CORE_NAME_MAX, "gpu%d", num);
660 break;
661 default:
662 return NULL;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700663 }
664
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700665 core = &core_list[i];
666 core->dcvs_core_id = i;
Abhijeet Dharmapurikar2ebc0fe2012-09-12 14:05:13 -0700667 strlcpy(core->core_name, name, CORE_NAME_MAX);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700668 spin_lock_init(&core->pending_freq_lock);
669 spin_lock_init(&core->idle_state_change_lock);
670 hrtimer_init(&core->slack_timer,
671 CLOCK_MONOTONIC, HRTIMER_MODE_REL_PINNED);
672 core->slack_timer.function = msm_dcvs_core_slack_timer;
Abhijeet Dharmapurikar2ebc0fe2012-09-12 14:05:13 -0700673 return core;
674}
675
676/* Return the core if found or add to list if @add_to_list is true */
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700677static struct dcvs_core *msm_dcvs_get_core(int offset)
Abhijeet Dharmapurikar2ebc0fe2012-09-12 14:05:13 -0700678{
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700679 /* if the handle is still not set bug */
680 BUG_ON(core_list[offset].dcvs_core_id == -1);
681 return &core_list[offset];
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700682}
683
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700684
685int msm_dcvs_register_core(
686 enum msm_dcvs_core_type type,
687 int type_core_num,
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700688 struct msm_dcvs_core_info *info,
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700689 int (*set_frequency)(int type_core_num, unsigned int freq),
690 unsigned int (*get_frequency)(int type_core_num),
691 int (*idle_enable)(int type_core_num,
Abhijeet Dharmapurikar6e9b34f2012-09-10 16:03:39 -0700692 enum msm_core_control_event event),
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700693 int sensor)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700694{
695 int ret = -EINVAL;
696 struct dcvs_core *core = NULL;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700697 uint32_t ret1;
698 uint32_t ret2;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700699
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700700 core = msm_dcvs_add_core(type, type_core_num);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700701 if (!core)
702 return ret;
703
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700704 core->type = type;
705 core->type_core_num = type_core_num;
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700706 core->set_frequency = set_frequency;
707 core->get_frequency = get_frequency;
Abhijeet Dharmapurikar6e9b34f2012-09-10 16:03:39 -0700708 core->idle_enable = idle_enable;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700709 core->pending_freq = STOP_FREQ_CHANGE;
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700710
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700711 core->info = info;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700712 memcpy(&core->algo_param, &info->algo_param,
713 sizeof(struct msm_dcvs_algo_param));
714
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700715 memcpy(&core->coeffs, &info->energy_coeffs,
716 sizeof(struct msm_dcvs_energy_curve_coeffs));
717
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700718 /*
719 * The tz expects cpu0 to represent bit 0 in the mask, however the
720 * dcvs_core_id needs to start from 1, dcvs_core_id = 0 is used to
721 * indicate that this request is not associated with any core.
722 * mpdecision
723 */
724 info->core_param.core_bitmask_id
725 = 1 << (core->dcvs_core_id - CPU_OFFSET);
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700726 core->sensor = sensor;
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700727
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700728 ret = msm_dcvs_scm_register_core(core->dcvs_core_id, &info->core_param);
729 if (ret) {
730 __err("%s: scm register core fail handle = %d ret = %d\n",
731 __func__, core->dcvs_core_id, ret);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700732 goto bail;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700733 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700734
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700735 ret = msm_dcvs_scm_set_algo_params(core->dcvs_core_id,
736 &info->algo_param);
737 if (ret) {
738 __err("%s: scm algo params failed ret = %d\n", __func__, ret);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700739 goto bail;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700740 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700741
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700742 ret = msm_dcvs_scm_set_power_params(core->dcvs_core_id,
743 &info->power_param,
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700744 &info->freq_tbl[0], &core->coeffs);
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700745 if (ret) {
746 __err("%s: scm power params failed ret = %d\n", __func__, ret);
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700747 goto bail;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700748 }
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700749
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700750 ret = msm_dcvs_scm_event(core->dcvs_core_id, MSM_DCVS_SCM_CORE_ONLINE,
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700751 core->actual_freq, 0, &ret1, &ret2);
752 if (ret)
753 goto bail;
754
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700755 ret = msm_dcvs_setup_core_sysfs(core);
756 if (ret) {
757 __err("Unable to setup core %s sysfs\n", core->core_name);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700758 goto bail;
759 }
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700760 core->idle_entered = -1;
Abhijeet Dharmapurikarbbb52fe2012-08-31 20:31:16 -0700761 init_waitqueue_head(&core->wait_q);
762 core->task = kthread_run(msm_dcvs_do_freq, (void *)core,
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700763 "msm_dcvs/%d", core->dcvs_core_id);
764 ret = core->dcvs_core_id;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700765 return ret;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700766bail:
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700767 core->dcvs_core_id = -1;
768 return -EINVAL;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700769}
770EXPORT_SYMBOL(msm_dcvs_register_core);
771
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700772void msm_dcvs_update_limits(int dcvs_core_id)
Abhijeet Dharmapurikarc43f0db2012-08-31 20:42:53 -0700773{
774 struct dcvs_core *core;
775
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700776 if (dcvs_core_id < CPU_OFFSET || dcvs_core_id > CORES_MAX) {
777 __err("%s invalid dcvs_core_id = %d returning -EINVAL\n",
778 __func__, dcvs_core_id);
Abhijeet Dharmapurikarc43f0db2012-08-31 20:42:53 -0700779 return;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700780 }
Abhijeet Dharmapurikarc43f0db2012-08-31 20:42:53 -0700781
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700782 core = msm_dcvs_get_core(dcvs_core_id);
783 core->actual_freq = core->get_frequency(core->type_core_num);
Abhijeet Dharmapurikarc43f0db2012-08-31 20:42:53 -0700784}
785
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700786int msm_dcvs_freq_sink_start(int dcvs_core_id)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700787{
788 int ret = -EINVAL;
789 struct dcvs_core *core = NULL;
Eugene Seah76af9832012-03-28 18:43:53 -0600790 uint32_t ret1;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700791 unsigned long flags;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700792
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700793 if (dcvs_core_id < CPU_OFFSET || dcvs_core_id > CORES_MAX) {
794 __err("%s invalid dcvs_core_id = %d returning -EINVAL\n",
795 __func__, dcvs_core_id);
796 return -EINVAL;
797 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700798
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700799 core = msm_dcvs_get_core(dcvs_core_id);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700800 if (!core)
801 return ret;
802
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700803 core->actual_freq = core->get_frequency(core->type_core_num);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700804
805 spin_lock_irqsave(&core->pending_freq_lock, flags);
806 /* mark that we are ready to accept new frequencies */
807 request_freq_change(core, NO_OUTSTANDING_FREQ_CHANGE);
808 spin_unlock_irqrestore(&core->pending_freq_lock, flags);
809
810 spin_lock_irqsave(&core->idle_state_change_lock, flags);
811 core->idle_entered = -1;
812 spin_unlock_irqrestore(&core->idle_state_change_lock, flags);
813
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700814 /* Notify TZ to start receiving idle info for the core */
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700815 ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_DCVS_ENABLE, 1, &ret1);
816
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700817 core->idle_enable(core->type_core_num, MSM_DCVS_ENABLE_IDLE_PULSE);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700818 return 0;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700819}
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700820EXPORT_SYMBOL(msm_dcvs_freq_sink_start);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700821
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700822int msm_dcvs_freq_sink_stop(int dcvs_core_id)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700823{
824 int ret = -EINVAL;
825 struct dcvs_core *core = NULL;
Eugene Seah76af9832012-03-28 18:43:53 -0600826 uint32_t ret1;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700827 uint32_t freq;
828 unsigned long flags;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700829
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700830 if (dcvs_core_id < 0 || dcvs_core_id > CORES_MAX) {
831 pr_err("%s invalid dcvs_core_id = %d returning -EINVAL\n",
832 __func__, dcvs_core_id);
833 return -EINVAL;
834 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700835
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700836 core = msm_dcvs_get_core(dcvs_core_id);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700837 if (!core) {
838 __err("couldn't find core for coreid = %d\n", dcvs_core_id);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700839 return ret;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700840 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700841
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700842 core->idle_enable(core->type_core_num, MSM_DCVS_DISABLE_IDLE_PULSE);
843 /* Notify TZ to stop receiving idle info for the core */
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700844 ret = msm_dcvs_scm_event(core->dcvs_core_id, MSM_DCVS_SCM_DCVS_ENABLE,
845 0, core->actual_freq, &freq, &ret1);
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700846 core->idle_enable(core->type_core_num,
847 MSM_DCVS_ENABLE_HIGH_LATENCY_MODES);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700848 spin_lock_irqsave(&core->pending_freq_lock, flags);
849 /* flush out all the pending freq changes */
850 request_freq_change(core, STOP_FREQ_CHANGE);
851 spin_unlock_irqrestore(&core->pending_freq_lock, flags);
852 force_stop_slack_timer(core);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700853
854 return 0;
855}
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700856EXPORT_SYMBOL(msm_dcvs_freq_sink_stop);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700857
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700858int msm_dcvs_idle(int dcvs_core_id, enum msm_core_idle_state state,
859 uint32_t iowaited)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700860{
861 int ret = 0;
862 struct dcvs_core *core = NULL;
Eugene Seah76af9832012-03-28 18:43:53 -0600863 uint32_t timer_interval_us = 0;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700864 uint32_t r0, r1;
865
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700866 if (dcvs_core_id < CPU_OFFSET || dcvs_core_id > CORES_MAX) {
867 pr_err("invalid dcvs_core_id = %d ret -EINVAL\n", dcvs_core_id);
868 return -EINVAL;
869 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700870
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700871 core = msm_dcvs_get_core(dcvs_core_id);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700872
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700873 switch (state) {
874 case MSM_DCVS_IDLE_ENTER:
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700875 stop_slack_timer(core);
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700876 ret = msm_dcvs_scm_event(core->dcvs_core_id,
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700877 MSM_DCVS_SCM_IDLE_ENTER, 0, 0, &r0, &r1);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700878 if (ret < 0 && ret != -13)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700879 __err("Error (%d) sending idle enter for %s\n",
880 ret, core->core_name);
881 break;
882
883 case MSM_DCVS_IDLE_EXIT:
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700884 ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_IDLE_EXIT,
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700885 iowaited, &timer_interval_us);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700886 if (ret)
887 __err("Error (%d) sending idle exit for %s\n",
888 ret, core->core_name);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700889 start_slack_timer(core, timer_interval_us);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700890 break;
891 }
892
893 return ret;
894}
895EXPORT_SYMBOL(msm_dcvs_idle);
896
897static int __init msm_dcvs_late_init(void)
898{
899 struct kobject *module_kobj = NULL;
900 int ret = 0;
901
902 module_kobj = kset_find_obj(module_kset, KBUILD_MODNAME);
903 if (!module_kobj) {
904 pr_err("%s: cannot find kobject for module %s\n",
905 __func__, KBUILD_MODNAME);
906 ret = -ENOENT;
907 goto err;
908 }
909
910 cores_kobj = kobject_create_and_add("cores", module_kobj);
911 if (!cores_kobj) {
912 __err("Cannot create %s kobject\n", "cores");
913 ret = -ENOMEM;
914 goto err;
915 }
916
917 debugfs_base = debugfs_create_dir("msm_dcvs", NULL);
918 if (!debugfs_base) {
919 __err("Cannot create debugfs base %s\n", "msm_dcvs");
920 ret = -ENOENT;
921 goto err;
922 }
923
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700924err:
925 if (ret) {
926 kobject_del(cores_kobj);
927 cores_kobj = NULL;
928 debugfs_remove(debugfs_base);
929 }
930
931 return ret;
932}
933late_initcall(msm_dcvs_late_init);
934
935static int __init msm_dcvs_early_init(void)
936{
937 int ret = 0;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700938 int i;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700939
940 if (!msm_dcvs_enabled) {
941 __info("Not enabled (%d)\n", msm_dcvs_enabled);
942 return 0;
943 }
944
Abhijeet Dharmapurikar3edb5de2012-09-13 11:02:03 -0700945
946 /* Only need about 32kBytes for normal operation */
947 ret = msm_dcvs_scm_init(SZ_32K);
948 if (ret) {
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700949 __err("Unable to initialize DCVS err=%d\n", ret);
Abhijeet Dharmapurikar3edb5de2012-09-13 11:02:03 -0700950 goto done;
951 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700952
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700953 for (i = 0; i < CORES_MAX; i++)
954 core_list[i].dcvs_core_id = -1;
Abhijeet Dharmapurikar3edb5de2012-09-13 11:02:03 -0700955done:
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700956 return ret;
957}
958postcore_initcall(msm_dcvs_early_init);