blob: 2dca4a7aeb11533ddf2c19f12fecff1a23c98c98 [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 {
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070036 struct kobj_attribute freq_change_us;
37
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070038 struct kobj_attribute disable_pc_threshold;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -070039 struct kobj_attribute em_win_size_min_us;
40 struct kobj_attribute em_win_size_max_us;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070041 struct kobj_attribute em_max_util_pct;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -070042 struct kobj_attribute group_id;
43 struct kobj_attribute max_freq_chg_time_us;
44 struct kobj_attribute slack_mode_dynamic;
45 struct kobj_attribute slack_time_min_us;
46 struct kobj_attribute slack_time_max_us;
47 struct kobj_attribute slack_weight_thresh_pct;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070048 struct kobj_attribute ss_iobusy_conv;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -070049 struct kobj_attribute ss_win_size_min_us;
50 struct kobj_attribute ss_win_size_max_us;
51 struct kobj_attribute ss_util_pct;
52
53 struct kobj_attribute active_coeff_a;
54 struct kobj_attribute active_coeff_b;
55 struct kobj_attribute active_coeff_c;
56 struct kobj_attribute leakage_coeff_a;
57 struct kobj_attribute leakage_coeff_b;
58 struct kobj_attribute leakage_coeff_c;
59 struct kobj_attribute leakage_coeff_d;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070060
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -070061 struct kobj_attribute thermal_poll_ms;
62
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070063 struct attribute_group attrib_group;
64};
65
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -070066enum pending_freq_state {
67 /*
68 * used by the thread to check if pending_freq was updated while it was
69 * setting previous frequency - this is written to and used by the
70 * freq updating thread
71 */
72 NO_OUTSTANDING_FREQ_CHANGE = 0,
73
74 /*
75 * This request is set to indicate that the governor is stopped and no
76 * more frequency change requests are accepted untill it starts again.
77 * This is checked/used by the threads that want to change the freq
78 */
79 STOP_FREQ_CHANGE = -1,
80
81 /*
82 * Any other +ve value means that a freq change was requested and the
83 * thread has not gotten around to update it
84 *
85 * Any other -ve value means that this is the last freq change i.e. a
86 * freq change was requested but the thread has not run yet and
87 * meanwhile the governor was stopped.
88 */
89};
90
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070091struct dcvs_core {
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -070092 spinlock_t idle_state_change_lock;
93 /* 0 when not idle (busy) 1 when idle and -1 when governor starts and
94 * we dont know whether the next call is going to be idle enter or exit
95 */
96 int idle_entered;
97
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -070098 enum msm_dcvs_core_type type;
99 /* this is the number in each type for example cpu 0,1,2 and gpu 0,1 */
100 int type_core_num;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700101 char core_name[CORE_NAME_MAX];
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700102 uint32_t actual_freq;
103 uint32_t freq_change_us;
104
105 uint32_t max_time_us; /* core param */
106
107 struct msm_dcvs_algo_param algo_param;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700108 struct msm_dcvs_energy_curve_coeffs coeffs;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700109
110 /* private */
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700111 ktime_t time_start;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700112 struct task_struct *task;
113 struct core_attribs attrib;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700114 uint32_t dcvs_core_id;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700115 struct msm_dcvs_core_info *info;
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700116 int sensor;
Abhijeet Dharmapurikarbbb52fe2012-08-31 20:31:16 -0700117 wait_queue_head_t wait_q;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700118
119 int (*set_frequency)(int type_core_num, unsigned int freq);
120 unsigned int (*get_frequency)(int type_core_num);
121 int (*idle_enable)(int type_core_num,
Abhijeet Dharmapurikar6e9b34f2012-09-10 16:03:39 -0700122 enum msm_core_control_event event);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700123
124 spinlock_t pending_freq_lock;
125 int pending_freq;
126
127 struct hrtimer slack_timer;
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700128 struct delayed_work temperature_work;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700129};
130
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700131static int msm_dcvs_enabled = 1;
132module_param_named(enable, msm_dcvs_enabled, int, S_IRUGO | S_IWUSR | S_IWGRP);
133
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700134static struct dentry *debugfs_base;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700135
136static struct dcvs_core core_list[CORES_MAX];
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700137
138static struct kobject *cores_kobj;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700139
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700140static void force_stop_slack_timer(struct dcvs_core *core)
141{
142 unsigned long flags;
143
144 spin_lock_irqsave(&core->idle_state_change_lock, flags);
145 hrtimer_cancel(&core->slack_timer);
146 spin_unlock_irqrestore(&core->idle_state_change_lock, flags);
147}
148
Abhijeet Dharmapurikar8d843b32012-09-13 18:33:20 -0700149static void force_start_slack_timer(struct dcvs_core *core, int slack_us)
150{
151 unsigned long flags;
152 int ret;
153
154 spin_lock_irqsave(&core->idle_state_change_lock, flags);
155
156 /*
157 * only start the timer if governor is not stopped
158 */
159 if (slack_us != 0) {
160 ret = hrtimer_start(&core->slack_timer,
161 ktime_set(0, slack_us * 1000),
162 HRTIMER_MODE_REL_PINNED);
163 if (ret) {
164 pr_err("%s Failed to start timer ret = %d\n",
165 core->core_name, ret);
166 }
167 }
168
169 spin_unlock_irqrestore(&core->idle_state_change_lock, flags);
170}
171
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700172static void stop_slack_timer(struct dcvs_core *core)
173{
174 unsigned long flags;
175
176 spin_lock_irqsave(&core->idle_state_change_lock, flags);
177 /* err only for cpu type's GPU's can do idle exit consecutively */
178 if (core->idle_entered == 1 && !(core->dcvs_core_id >= GPU_OFFSET))
179 __err("%s trying to reenter idle", core->core_name);
180 core->idle_entered = 1;
181 hrtimer_cancel(&core->slack_timer);
182 core->idle_entered = 1;
183 spin_unlock_irqrestore(&core->idle_state_change_lock, flags);
184}
185
186static void start_slack_timer(struct dcvs_core *core, int slack_us)
187{
188 unsigned long flags1, flags2;
189 int ret;
190
191 spin_lock_irqsave(&core->idle_state_change_lock, flags2);
192
193 spin_lock_irqsave(&core->pending_freq_lock, flags1);
194
195 /* err only for cpu type's GPU's can do idle enter consecutively */
196 if (core->idle_entered == 0 && !(core->dcvs_core_id >= GPU_OFFSET))
197 __err("%s trying to reexit idle", core->core_name);
198 core->idle_entered = 0;
199 /*
200 * only start the timer if governor is not stopped
201 */
202 if (slack_us != 0
203 && !(core->pending_freq < NO_OUTSTANDING_FREQ_CHANGE)) {
204 ret = hrtimer_start(&core->slack_timer,
205 ktime_set(0, slack_us * 1000),
206 HRTIMER_MODE_REL_PINNED);
207 if (ret) {
208 pr_err("%s Failed to start timer ret = %d\n",
209 core->core_name, ret);
210 }
211 }
212 spin_unlock_irqrestore(&core->pending_freq_lock, flags1);
213
214 spin_unlock_irqrestore(&core->idle_state_change_lock, flags2);
215}
216
217static void restart_slack_timer(struct dcvs_core *core, int slack_us)
218{
219 unsigned long flags1, flags2;
220 int ret;
221
222 spin_lock_irqsave(&core->idle_state_change_lock, flags2);
223
224 hrtimer_cancel(&core->slack_timer);
225
226 spin_lock_irqsave(&core->pending_freq_lock, flags1);
227
228 /*
229 * only start the timer if idle is not entered
230 * and governor is not stopped
231 */
232 if (slack_us != 0 && (core->idle_entered != 1)
233 && !(core->pending_freq < NO_OUTSTANDING_FREQ_CHANGE)) {
234 ret = hrtimer_start(&core->slack_timer,
235 ktime_set(0, slack_us * 1000),
236 HRTIMER_MODE_REL_PINNED);
237 if (ret) {
238 pr_err("%s Failed to start timer ret = %d\n",
239 core->core_name, ret);
240 }
241 }
242 spin_unlock_irqrestore(&core->pending_freq_lock, flags1);
243 spin_unlock_irqrestore(&core->idle_state_change_lock, flags2);
244}
245
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700246static int __msm_dcvs_change_freq(struct dcvs_core *core)
247{
248 int ret = 0;
249 unsigned long flags = 0;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700250 int requested_freq = 0;
251 ktime_t time_start;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700252 uint32_t slack_us = 0;
253 uint32_t ret1 = 0;
254
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700255 spin_lock_irqsave(&core->pending_freq_lock, flags);
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700256repeat:
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700257 BUG_ON(!core->pending_freq);
258 if (core->pending_freq == STOP_FREQ_CHANGE)
259 BUG();
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700260
261 requested_freq = core->pending_freq;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700262 time_start = core->time_start;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700263 core->time_start = ns_to_ktime(0);
264
265 if (requested_freq < 0) {
266 requested_freq = -1 * requested_freq;
267 core->pending_freq = STOP_FREQ_CHANGE;
268 } else {
269 core->pending_freq = NO_OUTSTANDING_FREQ_CHANGE;
270 }
271
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700272 if (requested_freq == core->actual_freq)
273 goto out;
274
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700275 spin_unlock_irqrestore(&core->pending_freq_lock, flags);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700276
277 /**
278 * Call the frequency sink driver to change the frequency
279 * We will need to get back the actual frequency in KHz and
280 * the record the time taken to change it.
281 */
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700282 ret = core->set_frequency(core->type_core_num, requested_freq);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700283 if (ret <= 0)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700284 __err("Core %s failed to set freq %u\n",
285 core->core_name, requested_freq);
Eugene Seah76af9832012-03-28 18:43:53 -0600286 /* continue to call TZ to get updated slack timer */
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700287 else
Eugene Seah76af9832012-03-28 18:43:53 -0600288 core->actual_freq = ret;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700289
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700290 core->freq_change_us = (uint32_t)ktime_to_us(
291 ktime_sub(ktime_get(), time_start));
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700292
293 /**
294 * Disable low power modes if the actual frequency is >
295 * disable_pc_threshold.
296 */
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700297 if (core->actual_freq > core->algo_param.disable_pc_threshold) {
298 core->idle_enable(core->type_core_num,
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700299 MSM_DCVS_DISABLE_HIGH_LATENCY_MODES);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700300 } else if (core->actual_freq <= core->algo_param.disable_pc_threshold) {
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700301 core->idle_enable(core->type_core_num,
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700302 MSM_DCVS_ENABLE_HIGH_LATENCY_MODES);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700303 }
304
305 /**
306 * Update algorithm with new freq and time taken to change
307 * to this frequency and that will get us the new slack
308 * timer
309 */
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700310 ret = msm_dcvs_scm_event(core->dcvs_core_id,
311 MSM_DCVS_SCM_CLOCK_FREQ_UPDATE,
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700312 core->actual_freq, core->freq_change_us,
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700313 &slack_us, &ret1);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700314 if (ret) {
315 __err("Error sending core (%s) dcvs_core_id = %d freq change (%u) reqfreq = %d slack_us=%d ret = %d\n",
316 core->core_name, core->dcvs_core_id,
317 core->actual_freq, requested_freq,
318 slack_us, ret);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700319 }
320
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700321 /* TODO confirm that we get a valid freq from SM even when the above
322 * FREQ_UPDATE fails
323 */
324 restart_slack_timer(core, slack_us);
325 spin_lock_irqsave(&core->pending_freq_lock, flags);
326
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700327 /**
328 * By the time we are done with freq changes, we could be asked to
329 * change again. Check before exiting.
330 */
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700331 if (core->pending_freq != NO_OUTSTANDING_FREQ_CHANGE
332 && core->pending_freq != STOP_FREQ_CHANGE) {
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700333 goto repeat;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700334 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700335
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700336out: /* should always be jumped to with the spin_lock held */
337 spin_unlock_irqrestore(&core->pending_freq_lock, flags);
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700338
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700339 return ret;
340}
341
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700342static void msm_dcvs_report_temp_work(struct work_struct *work)
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700343{
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700344 struct dcvs_core *core = container_of(work,
345 struct dcvs_core,
346 temperature_work.work);
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700347 struct msm_dcvs_core_info *info = core->info;
348 struct tsens_device tsens_dev;
349 int ret;
350 unsigned long temp = 0;
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700351 int interval_ms;
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700352
353 tsens_dev.sensor_num = core->sensor;
354 ret = tsens_get_temp(&tsens_dev, &temp);
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700355 if (!temp) {
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700356 tsens_dev.sensor_num = 0;
357 ret = tsens_get_temp(&tsens_dev, &temp);
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700358 if (!temp)
359 goto out;
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700360 }
361
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700362 if (temp == info->power_param.current_temp)
363 goto out;
364 info->power_param.current_temp = temp;
365
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700366 ret = msm_dcvs_scm_set_power_params(core->dcvs_core_id,
367 &info->power_param,
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700368 &info->freq_tbl[0], &core->coeffs);
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700369out:
370 if (info->thermal_poll_ms == 0)
371 interval_ms = 60000;
372 else if (info->thermal_poll_ms < 1000)
373 interval_ms = 1000;
374 else
375 interval_ms = info->thermal_poll_ms;
376
377 schedule_delayed_work(&core->temperature_work,
378 msecs_to_jiffies(interval_ms));
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700379}
380
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700381static int msm_dcvs_do_freq(void *data)
382{
383 struct dcvs_core *core = (struct dcvs_core *)data;
384 static struct sched_param param = {.sched_priority = MAX_RT_PRIO - 1};
385
386 sched_setscheduler(current, SCHED_FIFO, &param);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700387
388 while (!kthread_should_stop()) {
Abhijeet Dharmapurikarbbb52fe2012-08-31 20:31:16 -0700389 wait_event(core->wait_q, !(core->pending_freq == 0 ||
390 core->pending_freq == -1) ||
391 kthread_should_stop());
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700392
393 if (kthread_should_stop())
394 break;
395
Abhijeet Dharmapurikarbbb52fe2012-08-31 20:31:16 -0700396 __msm_dcvs_change_freq(core);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700397 }
398
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700399 return 0;
400}
401
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700402/* freq_pending_lock should be held */
403static void request_freq_change(struct dcvs_core *core, int new_freq)
404{
405 if (new_freq == NO_OUTSTANDING_FREQ_CHANGE) {
406 if (core->pending_freq != STOP_FREQ_CHANGE) {
407 __err("%s gov started with earlier pending freq %d\n",
408 core->core_name, core->pending_freq);
409 }
410 core->pending_freq = NO_OUTSTANDING_FREQ_CHANGE;
411 return;
412 }
413
414 if (new_freq == STOP_FREQ_CHANGE) {
415 if (core->pending_freq == NO_OUTSTANDING_FREQ_CHANGE)
416 core->pending_freq = STOP_FREQ_CHANGE;
417 else if (core->pending_freq > 0)
418 core->pending_freq = -1 * core->pending_freq;
419 return;
420 }
421
422 if (core->pending_freq < 0) {
423 /* a value less than 0 means that the governor has stopped
424 * and no more freq changes should be requested
425 */
426 return;
427 }
428
429 if (core->actual_freq != new_freq && core->pending_freq != new_freq) {
430 core->pending_freq = new_freq;
431 core->time_start = ktime_get();
432 wake_up(&core->wait_q);
433 }
434}
435
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700436static int msm_dcvs_update_freq(struct dcvs_core *core,
Eugene Seah76af9832012-03-28 18:43:53 -0600437 enum msm_dcvs_scm_event event, uint32_t param0,
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700438 uint32_t *ret1)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700439{
440 int ret = 0;
441 unsigned long flags = 0;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700442 uint32_t new_freq = -EINVAL;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700443
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700444 spin_lock_irqsave(&core->pending_freq_lock, flags);
445
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700446 ret = msm_dcvs_scm_event(core->dcvs_core_id, event, param0,
Eugene Seah76af9832012-03-28 18:43:53 -0600447 core->actual_freq, &new_freq, ret1);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700448 if (ret) {
Jeff Ohlstein4aa4a2b2012-06-28 19:03:49 -0700449 if (ret == -13)
450 ret = 0;
451 else
452 __err("Error (%d) sending SCM event %d for core %s\n",
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700453 ret, event, core->core_name);
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700454 goto out;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700455 }
456
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700457 if (new_freq == 0) {
458 /*
459 * sometimes TZ gives us a 0 freq back,
460 * do not queue up a request
461 */
462 goto out;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700463 }
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700464
465 request_freq_change(core, new_freq);
466
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700467out:
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700468 spin_unlock_irqrestore(&core->pending_freq_lock, flags);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700469
470 return ret;
471}
472
473static enum hrtimer_restart msm_dcvs_core_slack_timer(struct hrtimer *timer)
474{
475 int ret = 0;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700476 struct dcvs_core *core = container_of(timer,
477 struct dcvs_core, slack_timer);
Eugene Seah76af9832012-03-28 18:43:53 -0600478 uint32_t ret1;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700479
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700480 /**
481 * Timer expired, notify TZ
482 * Dont care about the third arg.
483 */
Eugene Seah76af9832012-03-28 18:43:53 -0600484 ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_QOS_TIMER_EXPIRED, 0,
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700485 &ret1);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700486 if (ret)
487 __err("Timer expired for core %s but failed to notify.\n",
488 core->core_name);
489
490 return HRTIMER_NORESTART;
491}
492
493/* Helper functions and macros for sysfs nodes for a core */
494#define CORE_FROM_ATTRIBS(attr, name) \
495 container_of(container_of(attr, struct core_attribs, name), \
496 struct dcvs_core, attrib);
497
498#define DCVS_PARAM_SHOW(_name, v) \
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", v); \
504}
505
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700506#define DCVS_PARAM_STORE(_name) \
507static ssize_t msm_dcvs_attr_##_name##_show(struct kobject *kobj,\
508 struct kobj_attribute *attr, char *buf) \
509{ \
510 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
511 return snprintf(buf, PAGE_SIZE, "%d\n", core->info->_name); \
512} \
513static ssize_t msm_dcvs_attr_##_name##_store(struct kobject *kobj, \
514 struct kobj_attribute *attr, const char *buf, size_t count) \
515{ \
516 int ret = 0; \
517 uint32_t val = 0; \
518 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
519 ret = kstrtouint(buf, 10, &val); \
520 if (ret) { \
521 __err("Invalid input %s for %s\n", buf, __stringify(_name));\
522 } else { \
523 core->info->_name = val; \
524 } \
525 return count; \
526}
527
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700528#define DCVS_ALGO_PARAM(_name) \
529static ssize_t msm_dcvs_attr_##_name##_show(struct kobject *kobj,\
530 struct kobj_attribute *attr, char *buf) \
531{ \
532 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
533 return snprintf(buf, PAGE_SIZE, "%d\n", core->algo_param._name); \
534} \
535static ssize_t msm_dcvs_attr_##_name##_store(struct kobject *kobj, \
536 struct kobj_attribute *attr, const char *buf, size_t count) \
537{ \
538 int ret = 0; \
539 uint32_t val = 0; \
540 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700541 ret = kstrtouint(buf, 10, &val); \
542 if (ret) { \
543 __err("Invalid input %s for %s\n", buf, __stringify(_name));\
544 } else { \
545 uint32_t old_val = core->algo_param._name; \
546 core->algo_param._name = val; \
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700547 ret = msm_dcvs_scm_set_algo_params(core->dcvs_core_id, \
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700548 &core->algo_param); \
549 if (ret) { \
550 core->algo_param._name = old_val; \
551 __err("Error(%d) in setting %d for algo param %s\n",\
552 ret, val, __stringify(_name)); \
553 } \
554 } \
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700555 return count; \
556}
557
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700558#define DCVS_ENERGY_PARAM(_name) \
559static ssize_t msm_dcvs_attr_##_name##_show(struct kobject *kobj,\
560 struct kobj_attribute *attr, char *buf) \
561{ \
562 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
563 return snprintf(buf, PAGE_SIZE, "%d\n", core->coeffs._name); \
564} \
565static ssize_t msm_dcvs_attr_##_name##_store(struct kobject *kobj, \
566 struct kobj_attribute *attr, const char *buf, size_t count) \
567{ \
568 int ret = 0; \
569 int32_t val = 0; \
570 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700571 ret = kstrtoint(buf, 10, &val); \
572 if (ret) { \
573 __err("Invalid input %s for %s\n", buf, __stringify(_name));\
574 } else { \
575 int32_t old_val = core->coeffs._name; \
576 core->coeffs._name = val; \
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700577 ret = msm_dcvs_scm_set_power_params(core->dcvs_core_id, \
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700578 &core->info->power_param, &core->info->freq_tbl[0], \
579 &core->coeffs); \
580 if (ret) { \
581 core->coeffs._name = old_val; \
582 __err("Error(%d) in setting %d for coeffs param %s\n",\
583 ret, val, __stringify(_name)); \
584 } \
585 } \
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700586 return count; \
587}
588
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700589#define DCVS_RO_ATTRIB(i, _name) \
590 core->attrib._name.attr.name = __stringify(_name); \
591 core->attrib._name.attr.mode = S_IRUGO; \
592 core->attrib._name.show = msm_dcvs_attr_##_name##_show; \
593 core->attrib._name.store = NULL; \
594 core->attrib.attrib_group.attrs[i] = &core->attrib._name.attr;
595
596#define DCVS_RW_ATTRIB(i, _name) \
597 core->attrib._name.attr.name = __stringify(_name); \
598 core->attrib._name.attr.mode = S_IRUGO | S_IWUSR; \
599 core->attrib._name.show = msm_dcvs_attr_##_name##_show; \
600 core->attrib._name.store = msm_dcvs_attr_##_name##_store; \
601 core->attrib.attrib_group.attrs[i] = &core->attrib._name.attr;
602
603/**
604 * Function declarations for different attributes.
605 * Gets used when setting the attribute show and store parameters.
606 */
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700607DCVS_PARAM_SHOW(freq_change_us, (core->freq_change_us))
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700608
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700609DCVS_ALGO_PARAM(disable_pc_threshold)
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700610DCVS_ALGO_PARAM(em_win_size_min_us)
611DCVS_ALGO_PARAM(em_win_size_max_us)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700612DCVS_ALGO_PARAM(em_max_util_pct)
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700613DCVS_ALGO_PARAM(group_id)
614DCVS_ALGO_PARAM(max_freq_chg_time_us)
615DCVS_ALGO_PARAM(slack_mode_dynamic)
616DCVS_ALGO_PARAM(slack_time_min_us)
617DCVS_ALGO_PARAM(slack_time_max_us)
618DCVS_ALGO_PARAM(slack_weight_thresh_pct)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700619DCVS_ALGO_PARAM(ss_iobusy_conv)
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700620DCVS_ALGO_PARAM(ss_win_size_min_us)
621DCVS_ALGO_PARAM(ss_win_size_max_us)
622DCVS_ALGO_PARAM(ss_util_pct)
623
624DCVS_ENERGY_PARAM(active_coeff_a)
625DCVS_ENERGY_PARAM(active_coeff_b)
626DCVS_ENERGY_PARAM(active_coeff_c)
627DCVS_ENERGY_PARAM(leakage_coeff_a)
628DCVS_ENERGY_PARAM(leakage_coeff_b)
629DCVS_ENERGY_PARAM(leakage_coeff_c)
630DCVS_ENERGY_PARAM(leakage_coeff_d)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700631
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700632DCVS_PARAM_STORE(thermal_poll_ms)
633
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700634static int msm_dcvs_setup_core_sysfs(struct dcvs_core *core)
635{
636 int ret = 0;
637 struct kobject *core_kobj = NULL;
Abhijeet Dharmapurikar35876382012-09-13 16:09:51 -0700638 const int attr_count = 24;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700639
640 BUG_ON(!cores_kobj);
641
642 core->attrib.attrib_group.attrs =
643 kzalloc(attr_count * sizeof(struct attribute *), GFP_KERNEL);
644
645 if (!core->attrib.attrib_group.attrs) {
646 ret = -ENOMEM;
647 goto done;
648 }
649
Abhijeet Dharmapurikar35876382012-09-13 16:09:51 -0700650 DCVS_RO_ATTRIB(0, freq_change_us);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700651
Abhijeet Dharmapurikar35876382012-09-13 16:09:51 -0700652 DCVS_RW_ATTRIB(1, disable_pc_threshold);
653 DCVS_RW_ATTRIB(2, em_win_size_min_us);
654 DCVS_RW_ATTRIB(3, em_win_size_max_us);
655 DCVS_RW_ATTRIB(4, em_max_util_pct);
656 DCVS_RW_ATTRIB(5, group_id);
657 DCVS_RW_ATTRIB(6, max_freq_chg_time_us);
658 DCVS_RW_ATTRIB(7, slack_mode_dynamic);
659 DCVS_RW_ATTRIB(8, slack_weight_thresh_pct);
660 DCVS_RW_ATTRIB(9, slack_time_min_us);
661 DCVS_RW_ATTRIB(10, slack_time_max_us);
662 DCVS_RW_ATTRIB(11, ss_iobusy_conv);
663 DCVS_RW_ATTRIB(12, ss_win_size_min_us);
664 DCVS_RW_ATTRIB(13, ss_win_size_max_us);
665 DCVS_RW_ATTRIB(14, ss_util_pct);
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700666
Abhijeet Dharmapurikar35876382012-09-13 16:09:51 -0700667 DCVS_RW_ATTRIB(15, active_coeff_a);
668 DCVS_RW_ATTRIB(16, active_coeff_b);
669 DCVS_RW_ATTRIB(17, active_coeff_c);
670 DCVS_RW_ATTRIB(18, leakage_coeff_a);
671 DCVS_RW_ATTRIB(19, leakage_coeff_b);
672 DCVS_RW_ATTRIB(20, leakage_coeff_c);
673 DCVS_RW_ATTRIB(21, leakage_coeff_d);
674 DCVS_RW_ATTRIB(22, thermal_poll_ms);
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700675
Abhijeet Dharmapurikar35876382012-09-13 16:09:51 -0700676 core->attrib.attrib_group.attrs[23] = NULL;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700677
678 core_kobj = kobject_create_and_add(core->core_name, cores_kobj);
679 if (!core_kobj) {
680 ret = -ENOMEM;
681 goto done;
682 }
683
684 ret = sysfs_create_group(core_kobj, &core->attrib.attrib_group);
685 if (ret)
686 __err("Cannot create core %s attr group\n", core->core_name);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700687
688done:
689 if (ret) {
690 kfree(core->attrib.attrib_group.attrs);
691 kobject_del(core_kobj);
692 }
693
694 return ret;
695}
696
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700697/* Return the core and initialize non platform data specific numbers in it */
698static struct dcvs_core *msm_dcvs_add_core(enum msm_dcvs_core_type type,
699 int num)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700700{
701 struct dcvs_core *core = NULL;
702 int i;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700703 char name[CORE_NAME_MAX];
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700704
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700705 switch (type) {
706 case MSM_DCVS_CORE_TYPE_CPU:
707 i = CPU_OFFSET + num;
708 BUG_ON(i >= GPU_OFFSET);
709 snprintf(name, CORE_NAME_MAX, "cpu%d", num);
710 break;
711 case MSM_DCVS_CORE_TYPE_GPU:
712 i = GPU_OFFSET + num;
713 BUG_ON(i >= CORES_MAX);
714 snprintf(name, CORE_NAME_MAX, "gpu%d", num);
715 break;
716 default:
717 return NULL;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700718 }
719
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700720 core = &core_list[i];
721 core->dcvs_core_id = i;
Abhijeet Dharmapurikar2ebc0fe2012-09-12 14:05:13 -0700722 strlcpy(core->core_name, name, CORE_NAME_MAX);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700723 spin_lock_init(&core->pending_freq_lock);
724 spin_lock_init(&core->idle_state_change_lock);
725 hrtimer_init(&core->slack_timer,
726 CLOCK_MONOTONIC, HRTIMER_MODE_REL_PINNED);
727 core->slack_timer.function = msm_dcvs_core_slack_timer;
Abhijeet Dharmapurikar2ebc0fe2012-09-12 14:05:13 -0700728 return core;
729}
730
731/* Return the core if found or add to list if @add_to_list is true */
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700732static struct dcvs_core *msm_dcvs_get_core(int offset)
Abhijeet Dharmapurikar2ebc0fe2012-09-12 14:05:13 -0700733{
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700734 /* if the handle is still not set bug */
735 BUG_ON(core_list[offset].dcvs_core_id == -1);
736 return &core_list[offset];
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700737}
738
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700739
740int msm_dcvs_register_core(
741 enum msm_dcvs_core_type type,
742 int type_core_num,
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700743 struct msm_dcvs_core_info *info,
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700744 int (*set_frequency)(int type_core_num, unsigned int freq),
745 unsigned int (*get_frequency)(int type_core_num),
746 int (*idle_enable)(int type_core_num,
Abhijeet Dharmapurikar6e9b34f2012-09-10 16:03:39 -0700747 enum msm_core_control_event event),
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700748 int sensor)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700749{
750 int ret = -EINVAL;
751 struct dcvs_core *core = NULL;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700752 uint32_t ret1;
753 uint32_t ret2;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700754
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700755 core = msm_dcvs_add_core(type, type_core_num);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700756 if (!core)
757 return ret;
758
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700759 core->type = type;
760 core->type_core_num = type_core_num;
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700761 core->set_frequency = set_frequency;
762 core->get_frequency = get_frequency;
Abhijeet Dharmapurikar6e9b34f2012-09-10 16:03:39 -0700763 core->idle_enable = idle_enable;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700764 core->pending_freq = STOP_FREQ_CHANGE;
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700765
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700766 core->info = info;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700767 memcpy(&core->algo_param, &info->algo_param,
768 sizeof(struct msm_dcvs_algo_param));
769
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700770 memcpy(&core->coeffs, &info->energy_coeffs,
771 sizeof(struct msm_dcvs_energy_curve_coeffs));
772
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700773 /*
774 * The tz expects cpu0 to represent bit 0 in the mask, however the
775 * dcvs_core_id needs to start from 1, dcvs_core_id = 0 is used to
776 * indicate that this request is not associated with any core.
777 * mpdecision
778 */
779 info->core_param.core_bitmask_id
780 = 1 << (core->dcvs_core_id - CPU_OFFSET);
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700781 core->sensor = sensor;
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700782
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700783 ret = msm_dcvs_scm_register_core(core->dcvs_core_id, &info->core_param);
784 if (ret) {
785 __err("%s: scm register core fail handle = %d ret = %d\n",
786 __func__, core->dcvs_core_id, ret);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700787 goto bail;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700788 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700789
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700790 ret = msm_dcvs_scm_set_algo_params(core->dcvs_core_id,
791 &info->algo_param);
792 if (ret) {
793 __err("%s: scm algo params failed ret = %d\n", __func__, ret);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700794 goto bail;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700795 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700796
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700797 ret = msm_dcvs_scm_set_power_params(core->dcvs_core_id,
798 &info->power_param,
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700799 &info->freq_tbl[0], &core->coeffs);
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700800 if (ret) {
801 __err("%s: scm power params failed ret = %d\n", __func__, ret);
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700802 goto bail;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700803 }
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700804
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700805 ret = msm_dcvs_scm_event(core->dcvs_core_id, MSM_DCVS_SCM_CORE_ONLINE,
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700806 core->actual_freq, 0, &ret1, &ret2);
807 if (ret)
808 goto bail;
809
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700810 ret = msm_dcvs_setup_core_sysfs(core);
811 if (ret) {
812 __err("Unable to setup core %s sysfs\n", core->core_name);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700813 goto bail;
814 }
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700815 core->idle_entered = -1;
Abhijeet Dharmapurikarbbb52fe2012-08-31 20:31:16 -0700816 init_waitqueue_head(&core->wait_q);
817 core->task = kthread_run(msm_dcvs_do_freq, (void *)core,
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700818 "msm_dcvs/%d", core->dcvs_core_id);
819 ret = core->dcvs_core_id;
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700820
821 INIT_DELAYED_WORK(&core->temperature_work, msm_dcvs_report_temp_work);
822 schedule_delayed_work(&core->temperature_work,
823 msecs_to_jiffies(info->thermal_poll_ms));
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700824 return ret;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700825bail:
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700826 core->dcvs_core_id = -1;
827 return -EINVAL;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700828}
829EXPORT_SYMBOL(msm_dcvs_register_core);
830
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700831void msm_dcvs_update_limits(int dcvs_core_id)
Abhijeet Dharmapurikarc43f0db2012-08-31 20:42:53 -0700832{
833 struct dcvs_core *core;
834
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700835 if (dcvs_core_id < CPU_OFFSET || dcvs_core_id > CORES_MAX) {
836 __err("%s invalid dcvs_core_id = %d returning -EINVAL\n",
837 __func__, dcvs_core_id);
Abhijeet Dharmapurikarc43f0db2012-08-31 20:42:53 -0700838 return;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700839 }
Abhijeet Dharmapurikarc43f0db2012-08-31 20:42:53 -0700840
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700841 core = msm_dcvs_get_core(dcvs_core_id);
842 core->actual_freq = core->get_frequency(core->type_core_num);
Abhijeet Dharmapurikarc43f0db2012-08-31 20:42:53 -0700843}
844
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700845int msm_dcvs_freq_sink_start(int dcvs_core_id)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700846{
847 int ret = -EINVAL;
848 struct dcvs_core *core = NULL;
Eugene Seah76af9832012-03-28 18:43:53 -0600849 uint32_t ret1;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700850 unsigned long flags;
Abhijeet Dharmapurikar8d843b32012-09-13 18:33:20 -0700851 int new_freq;
852 int timer_interval_us;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700853
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700854 if (dcvs_core_id < CPU_OFFSET || dcvs_core_id > CORES_MAX) {
855 __err("%s invalid dcvs_core_id = %d returning -EINVAL\n",
856 __func__, dcvs_core_id);
857 return -EINVAL;
858 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700859
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700860 core = msm_dcvs_get_core(dcvs_core_id);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700861 if (!core)
862 return ret;
863
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700864 core->actual_freq = core->get_frequency(core->type_core_num);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700865
866 spin_lock_irqsave(&core->pending_freq_lock, flags);
867 /* mark that we are ready to accept new frequencies */
868 request_freq_change(core, NO_OUTSTANDING_FREQ_CHANGE);
869 spin_unlock_irqrestore(&core->pending_freq_lock, flags);
870
871 spin_lock_irqsave(&core->idle_state_change_lock, flags);
872 core->idle_entered = -1;
873 spin_unlock_irqrestore(&core->idle_state_change_lock, flags);
874
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700875 /* Notify TZ to start receiving idle info for the core */
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700876 ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_DCVS_ENABLE, 1, &ret1);
877
Abhijeet Dharmapurikar8d843b32012-09-13 18:33:20 -0700878 ret = msm_dcvs_scm_event(
879 core->dcvs_core_id, MSM_DCVS_SCM_CORE_ONLINE, core->actual_freq,
880 0, &new_freq, &timer_interval_us);
881 if (ret)
882 __err("Error (%d) DCVS sending online for %s\n",
883 ret, core->core_name);
884
885 if (new_freq != 0) {
886 spin_lock_irqsave(&core->pending_freq_lock, flags);
887 request_freq_change(core, new_freq);
888 spin_unlock_irqrestore(&core->pending_freq_lock, flags);
889 }
890 force_start_slack_timer(core, timer_interval_us);
891
892
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700893 core->idle_enable(core->type_core_num, MSM_DCVS_ENABLE_IDLE_PULSE);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700894 return 0;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700895}
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700896EXPORT_SYMBOL(msm_dcvs_freq_sink_start);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700897
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700898int msm_dcvs_freq_sink_stop(int dcvs_core_id)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700899{
900 int ret = -EINVAL;
901 struct dcvs_core *core = NULL;
Eugene Seah76af9832012-03-28 18:43:53 -0600902 uint32_t ret1;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700903 uint32_t freq;
904 unsigned long flags;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700905
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700906 if (dcvs_core_id < 0 || dcvs_core_id > CORES_MAX) {
907 pr_err("%s invalid dcvs_core_id = %d returning -EINVAL\n",
908 __func__, dcvs_core_id);
909 return -EINVAL;
910 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700911
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700912 core = msm_dcvs_get_core(dcvs_core_id);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700913 if (!core) {
914 __err("couldn't find core for coreid = %d\n", dcvs_core_id);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700915 return ret;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700916 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700917
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700918 core->idle_enable(core->type_core_num, MSM_DCVS_DISABLE_IDLE_PULSE);
919 /* Notify TZ to stop receiving idle info for the core */
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700920 ret = msm_dcvs_scm_event(core->dcvs_core_id, MSM_DCVS_SCM_DCVS_ENABLE,
921 0, core->actual_freq, &freq, &ret1);
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700922 core->idle_enable(core->type_core_num,
923 MSM_DCVS_ENABLE_HIGH_LATENCY_MODES);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700924 spin_lock_irqsave(&core->pending_freq_lock, flags);
925 /* flush out all the pending freq changes */
926 request_freq_change(core, STOP_FREQ_CHANGE);
927 spin_unlock_irqrestore(&core->pending_freq_lock, flags);
928 force_stop_slack_timer(core);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700929
930 return 0;
931}
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700932EXPORT_SYMBOL(msm_dcvs_freq_sink_stop);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700933
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700934int msm_dcvs_idle(int dcvs_core_id, enum msm_core_idle_state state,
935 uint32_t iowaited)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700936{
937 int ret = 0;
938 struct dcvs_core *core = NULL;
Eugene Seah76af9832012-03-28 18:43:53 -0600939 uint32_t timer_interval_us = 0;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700940 uint32_t r0, r1;
941
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700942 if (dcvs_core_id < CPU_OFFSET || dcvs_core_id > CORES_MAX) {
943 pr_err("invalid dcvs_core_id = %d ret -EINVAL\n", dcvs_core_id);
944 return -EINVAL;
945 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700946
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700947 core = msm_dcvs_get_core(dcvs_core_id);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700948
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700949 switch (state) {
950 case MSM_DCVS_IDLE_ENTER:
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700951 stop_slack_timer(core);
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700952 ret = msm_dcvs_scm_event(core->dcvs_core_id,
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700953 MSM_DCVS_SCM_IDLE_ENTER, 0, 0, &r0, &r1);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700954 if (ret < 0 && ret != -13)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700955 __err("Error (%d) sending idle enter for %s\n",
956 ret, core->core_name);
957 break;
958
959 case MSM_DCVS_IDLE_EXIT:
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700960 ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_IDLE_EXIT,
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700961 iowaited, &timer_interval_us);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700962 if (ret)
963 __err("Error (%d) sending idle exit for %s\n",
964 ret, core->core_name);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700965 start_slack_timer(core, timer_interval_us);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700966 break;
967 }
968
969 return ret;
970}
971EXPORT_SYMBOL(msm_dcvs_idle);
972
973static int __init msm_dcvs_late_init(void)
974{
975 struct kobject *module_kobj = NULL;
976 int ret = 0;
977
978 module_kobj = kset_find_obj(module_kset, KBUILD_MODNAME);
979 if (!module_kobj) {
980 pr_err("%s: cannot find kobject for module %s\n",
981 __func__, KBUILD_MODNAME);
982 ret = -ENOENT;
983 goto err;
984 }
985
986 cores_kobj = kobject_create_and_add("cores", module_kobj);
987 if (!cores_kobj) {
988 __err("Cannot create %s kobject\n", "cores");
989 ret = -ENOMEM;
990 goto err;
991 }
992
993 debugfs_base = debugfs_create_dir("msm_dcvs", NULL);
994 if (!debugfs_base) {
995 __err("Cannot create debugfs base %s\n", "msm_dcvs");
996 ret = -ENOENT;
997 goto err;
998 }
999
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001000err:
1001 if (ret) {
1002 kobject_del(cores_kobj);
1003 cores_kobj = NULL;
1004 debugfs_remove(debugfs_base);
1005 }
1006
1007 return ret;
1008}
1009late_initcall(msm_dcvs_late_init);
1010
1011static int __init msm_dcvs_early_init(void)
1012{
1013 int ret = 0;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001014 int i;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001015
1016 if (!msm_dcvs_enabled) {
1017 __info("Not enabled (%d)\n", msm_dcvs_enabled);
1018 return 0;
1019 }
1020
Abhijeet Dharmapurikar3edb5de2012-09-13 11:02:03 -07001021
1022 /* Only need about 32kBytes for normal operation */
1023 ret = msm_dcvs_scm_init(SZ_32K);
1024 if (ret) {
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001025 __err("Unable to initialize DCVS err=%d\n", ret);
Abhijeet Dharmapurikar3edb5de2012-09-13 11:02:03 -07001026 goto done;
1027 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001028
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001029 for (i = 0; i < CORES_MAX; i++)
1030 core_list[i].dcvs_core_id = -1;
Abhijeet Dharmapurikar3edb5de2012-09-13 11:02:03 -07001031done:
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001032 return ret;
1033}
1034postcore_initcall(msm_dcvs_early_init);