blob: 2386a0585be4beefbd516a07d6c1f2a45e5f90a0 [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
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -070065 struct kobj_attribute thermal_poll_ms;
66
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070067 struct attribute_group attrib_group;
68};
69
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -070070enum pending_freq_state {
71 /*
72 * used by the thread to check if pending_freq was updated while it was
73 * setting previous frequency - this is written to and used by the
74 * freq updating thread
75 */
76 NO_OUTSTANDING_FREQ_CHANGE = 0,
77
78 /*
79 * This request is set to indicate that the governor is stopped and no
80 * more frequency change requests are accepted untill it starts again.
81 * This is checked/used by the threads that want to change the freq
82 */
83 STOP_FREQ_CHANGE = -1,
84
85 /*
86 * Any other +ve value means that a freq change was requested and the
87 * thread has not gotten around to update it
88 *
89 * Any other -ve value means that this is the last freq change i.e. a
90 * freq change was requested but the thread has not run yet and
91 * meanwhile the governor was stopped.
92 */
93};
94
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070095struct dcvs_core {
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -070096 spinlock_t idle_state_change_lock;
97 /* 0 when not idle (busy) 1 when idle and -1 when governor starts and
98 * we dont know whether the next call is going to be idle enter or exit
99 */
100 int idle_entered;
101
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700102 enum msm_dcvs_core_type type;
103 /* this is the number in each type for example cpu 0,1,2 and gpu 0,1 */
104 int type_core_num;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700105 char core_name[CORE_NAME_MAX];
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700106 uint32_t actual_freq;
107 uint32_t freq_change_us;
108
109 uint32_t max_time_us; /* core param */
110
111 struct msm_dcvs_algo_param algo_param;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700112 struct msm_dcvs_energy_curve_coeffs coeffs;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700113
114 /* private */
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700115 ktime_t time_start;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700116 struct task_struct *task;
117 struct core_attribs attrib;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700118 uint32_t dcvs_core_id;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700119 struct msm_dcvs_core_info *info;
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700120 int sensor;
Abhijeet Dharmapurikarbbb52fe2012-08-31 20:31:16 -0700121 wait_queue_head_t wait_q;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700122
123 int (*set_frequency)(int type_core_num, unsigned int freq);
124 unsigned int (*get_frequency)(int type_core_num);
125 int (*idle_enable)(int type_core_num,
Abhijeet Dharmapurikar6e9b34f2012-09-10 16:03:39 -0700126 enum msm_core_control_event event);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700127
128 spinlock_t pending_freq_lock;
129 int pending_freq;
130
131 struct hrtimer slack_timer;
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700132 struct delayed_work temperature_work;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700133};
134
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700135static int msm_dcvs_enabled = 1;
136module_param_named(enable, msm_dcvs_enabled, int, S_IRUGO | S_IWUSR | S_IWGRP);
137
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700138static struct dentry *debugfs_base;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700139
140static struct dcvs_core core_list[CORES_MAX];
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700141
142static struct kobject *cores_kobj;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700143
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700144static void force_stop_slack_timer(struct dcvs_core *core)
145{
146 unsigned long flags;
147
148 spin_lock_irqsave(&core->idle_state_change_lock, flags);
149 hrtimer_cancel(&core->slack_timer);
150 spin_unlock_irqrestore(&core->idle_state_change_lock, flags);
151}
152
153static void stop_slack_timer(struct dcvs_core *core)
154{
155 unsigned long flags;
156
157 spin_lock_irqsave(&core->idle_state_change_lock, flags);
158 /* err only for cpu type's GPU's can do idle exit consecutively */
159 if (core->idle_entered == 1 && !(core->dcvs_core_id >= GPU_OFFSET))
160 __err("%s trying to reenter idle", core->core_name);
161 core->idle_entered = 1;
162 hrtimer_cancel(&core->slack_timer);
163 core->idle_entered = 1;
164 spin_unlock_irqrestore(&core->idle_state_change_lock, flags);
165}
166
167static void start_slack_timer(struct dcvs_core *core, int slack_us)
168{
169 unsigned long flags1, flags2;
170 int ret;
171
172 spin_lock_irqsave(&core->idle_state_change_lock, flags2);
173
174 spin_lock_irqsave(&core->pending_freq_lock, flags1);
175
176 /* err only for cpu type's GPU's can do idle enter consecutively */
177 if (core->idle_entered == 0 && !(core->dcvs_core_id >= GPU_OFFSET))
178 __err("%s trying to reexit idle", core->core_name);
179 core->idle_entered = 0;
180 /*
181 * only start the timer if governor is not stopped
182 */
183 if (slack_us != 0
184 && !(core->pending_freq < NO_OUTSTANDING_FREQ_CHANGE)) {
185 ret = hrtimer_start(&core->slack_timer,
186 ktime_set(0, slack_us * 1000),
187 HRTIMER_MODE_REL_PINNED);
188 if (ret) {
189 pr_err("%s Failed to start timer ret = %d\n",
190 core->core_name, ret);
191 }
192 }
193 spin_unlock_irqrestore(&core->pending_freq_lock, flags1);
194
195 spin_unlock_irqrestore(&core->idle_state_change_lock, flags2);
196}
197
198static void restart_slack_timer(struct dcvs_core *core, int slack_us)
199{
200 unsigned long flags1, flags2;
201 int ret;
202
203 spin_lock_irqsave(&core->idle_state_change_lock, flags2);
204
205 hrtimer_cancel(&core->slack_timer);
206
207 spin_lock_irqsave(&core->pending_freq_lock, flags1);
208
209 /*
210 * only start the timer if idle is not entered
211 * and governor is not stopped
212 */
213 if (slack_us != 0 && (core->idle_entered != 1)
214 && !(core->pending_freq < NO_OUTSTANDING_FREQ_CHANGE)) {
215 ret = hrtimer_start(&core->slack_timer,
216 ktime_set(0, slack_us * 1000),
217 HRTIMER_MODE_REL_PINNED);
218 if (ret) {
219 pr_err("%s Failed to start timer ret = %d\n",
220 core->core_name, ret);
221 }
222 }
223 spin_unlock_irqrestore(&core->pending_freq_lock, flags1);
224 spin_unlock_irqrestore(&core->idle_state_change_lock, flags2);
225}
226
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700227static int __msm_dcvs_change_freq(struct dcvs_core *core)
228{
229 int ret = 0;
230 unsigned long flags = 0;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700231 int requested_freq = 0;
232 ktime_t time_start;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700233 uint32_t slack_us = 0;
234 uint32_t ret1 = 0;
235
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700236 spin_lock_irqsave(&core->pending_freq_lock, flags);
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700237repeat:
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700238 BUG_ON(!core->pending_freq);
239 if (core->pending_freq == STOP_FREQ_CHANGE)
240 BUG();
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700241
242 requested_freq = core->pending_freq;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700243 time_start = core->time_start;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700244 core->time_start = ns_to_ktime(0);
245
246 if (requested_freq < 0) {
247 requested_freq = -1 * requested_freq;
248 core->pending_freq = STOP_FREQ_CHANGE;
249 } else {
250 core->pending_freq = NO_OUTSTANDING_FREQ_CHANGE;
251 }
252
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700253 if (requested_freq == core->actual_freq)
254 goto out;
255
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700256 spin_unlock_irqrestore(&core->pending_freq_lock, flags);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700257
258 /**
259 * Call the frequency sink driver to change the frequency
260 * We will need to get back the actual frequency in KHz and
261 * the record the time taken to change it.
262 */
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700263 ret = core->set_frequency(core->type_core_num, requested_freq);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700264 if (ret <= 0)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700265 __err("Core %s failed to set freq %u\n",
266 core->core_name, requested_freq);
Eugene Seah76af9832012-03-28 18:43:53 -0600267 /* continue to call TZ to get updated slack timer */
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700268 else
Eugene Seah76af9832012-03-28 18:43:53 -0600269 core->actual_freq = ret;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700270
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700271 core->freq_change_us = (uint32_t)ktime_to_us(
272 ktime_sub(ktime_get(), time_start));
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700273
274 /**
275 * Disable low power modes if the actual frequency is >
276 * disable_pc_threshold.
277 */
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700278 if (core->actual_freq > core->algo_param.disable_pc_threshold) {
279 core->idle_enable(core->type_core_num,
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700280 MSM_DCVS_DISABLE_HIGH_LATENCY_MODES);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700281 } else if (core->actual_freq <= core->algo_param.disable_pc_threshold) {
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700282 core->idle_enable(core->type_core_num,
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700283 MSM_DCVS_ENABLE_HIGH_LATENCY_MODES);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700284 }
285
286 /**
287 * Update algorithm with new freq and time taken to change
288 * to this frequency and that will get us the new slack
289 * timer
290 */
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700291 ret = msm_dcvs_scm_event(core->dcvs_core_id,
292 MSM_DCVS_SCM_CLOCK_FREQ_UPDATE,
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700293 core->actual_freq, core->freq_change_us,
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700294 &slack_us, &ret1);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700295 if (ret) {
296 __err("Error sending core (%s) dcvs_core_id = %d freq change (%u) reqfreq = %d slack_us=%d ret = %d\n",
297 core->core_name, core->dcvs_core_id,
298 core->actual_freq, requested_freq,
299 slack_us, ret);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700300 }
301
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700302 /* TODO confirm that we get a valid freq from SM even when the above
303 * FREQ_UPDATE fails
304 */
305 restart_slack_timer(core, slack_us);
306 spin_lock_irqsave(&core->pending_freq_lock, flags);
307
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700308 /**
309 * By the time we are done with freq changes, we could be asked to
310 * change again. Check before exiting.
311 */
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700312 if (core->pending_freq != NO_OUTSTANDING_FREQ_CHANGE
313 && core->pending_freq != STOP_FREQ_CHANGE) {
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700314 goto repeat;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700315 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700316
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700317out: /* should always be jumped to with the spin_lock held */
318 spin_unlock_irqrestore(&core->pending_freq_lock, flags);
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700319
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700320 return ret;
321}
322
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700323static void msm_dcvs_report_temp_work(struct work_struct *work)
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700324{
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700325 struct dcvs_core *core = container_of(work,
326 struct dcvs_core,
327 temperature_work.work);
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700328 struct msm_dcvs_core_info *info = core->info;
329 struct tsens_device tsens_dev;
330 int ret;
331 unsigned long temp = 0;
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700332 int interval_ms;
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700333
334 tsens_dev.sensor_num = core->sensor;
335 ret = tsens_get_temp(&tsens_dev, &temp);
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700336 if (!temp) {
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700337 tsens_dev.sensor_num = 0;
338 ret = tsens_get_temp(&tsens_dev, &temp);
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700339 if (!temp)
340 goto out;
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700341 }
342
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700343 if (temp == info->power_param.current_temp)
344 goto out;
345 info->power_param.current_temp = temp;
346
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700347 ret = msm_dcvs_scm_set_power_params(core->dcvs_core_id,
348 &info->power_param,
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700349 &info->freq_tbl[0], &core->coeffs);
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700350out:
351 if (info->thermal_poll_ms == 0)
352 interval_ms = 60000;
353 else if (info->thermal_poll_ms < 1000)
354 interval_ms = 1000;
355 else
356 interval_ms = info->thermal_poll_ms;
357
358 schedule_delayed_work(&core->temperature_work,
359 msecs_to_jiffies(interval_ms));
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700360}
361
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700362static int msm_dcvs_do_freq(void *data)
363{
364 struct dcvs_core *core = (struct dcvs_core *)data;
365 static struct sched_param param = {.sched_priority = MAX_RT_PRIO - 1};
366
367 sched_setscheduler(current, SCHED_FIFO, &param);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700368
369 while (!kthread_should_stop()) {
Abhijeet Dharmapurikarbbb52fe2012-08-31 20:31:16 -0700370 wait_event(core->wait_q, !(core->pending_freq == 0 ||
371 core->pending_freq == -1) ||
372 kthread_should_stop());
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700373
374 if (kthread_should_stop())
375 break;
376
Abhijeet Dharmapurikarbbb52fe2012-08-31 20:31:16 -0700377 __msm_dcvs_change_freq(core);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700378 }
379
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700380 return 0;
381}
382
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700383/* freq_pending_lock should be held */
384static void request_freq_change(struct dcvs_core *core, int new_freq)
385{
386 if (new_freq == NO_OUTSTANDING_FREQ_CHANGE) {
387 if (core->pending_freq != STOP_FREQ_CHANGE) {
388 __err("%s gov started with earlier pending freq %d\n",
389 core->core_name, core->pending_freq);
390 }
391 core->pending_freq = NO_OUTSTANDING_FREQ_CHANGE;
392 return;
393 }
394
395 if (new_freq == STOP_FREQ_CHANGE) {
396 if (core->pending_freq == NO_OUTSTANDING_FREQ_CHANGE)
397 core->pending_freq = STOP_FREQ_CHANGE;
398 else if (core->pending_freq > 0)
399 core->pending_freq = -1 * core->pending_freq;
400 return;
401 }
402
403 if (core->pending_freq < 0) {
404 /* a value less than 0 means that the governor has stopped
405 * and no more freq changes should be requested
406 */
407 return;
408 }
409
410 if (core->actual_freq != new_freq && core->pending_freq != new_freq) {
411 core->pending_freq = new_freq;
412 core->time_start = ktime_get();
413 wake_up(&core->wait_q);
414 }
415}
416
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700417static int msm_dcvs_update_freq(struct dcvs_core *core,
Eugene Seah76af9832012-03-28 18:43:53 -0600418 enum msm_dcvs_scm_event event, uint32_t param0,
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700419 uint32_t *ret1)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700420{
421 int ret = 0;
422 unsigned long flags = 0;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700423 uint32_t new_freq = -EINVAL;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700424
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700425 spin_lock_irqsave(&core->pending_freq_lock, flags);
426
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700427 ret = msm_dcvs_scm_event(core->dcvs_core_id, event, param0,
Eugene Seah76af9832012-03-28 18:43:53 -0600428 core->actual_freq, &new_freq, ret1);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700429 if (ret) {
Jeff Ohlstein4aa4a2b2012-06-28 19:03:49 -0700430 if (ret == -13)
431 ret = 0;
432 else
433 __err("Error (%d) sending SCM event %d for core %s\n",
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700434 ret, event, core->core_name);
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700435 goto out;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700436 }
437
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700438 if (new_freq == 0) {
439 /*
440 * sometimes TZ gives us a 0 freq back,
441 * do not queue up a request
442 */
443 goto out;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700444 }
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700445
446 request_freq_change(core, new_freq);
447
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700448out:
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700449 spin_unlock_irqrestore(&core->pending_freq_lock, flags);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700450
451 return ret;
452}
453
454static enum hrtimer_restart msm_dcvs_core_slack_timer(struct hrtimer *timer)
455{
456 int ret = 0;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700457 struct dcvs_core *core = container_of(timer,
458 struct dcvs_core, slack_timer);
Eugene Seah76af9832012-03-28 18:43:53 -0600459 uint32_t ret1;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700460
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700461 /**
462 * Timer expired, notify TZ
463 * Dont care about the third arg.
464 */
Eugene Seah76af9832012-03-28 18:43:53 -0600465 ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_QOS_TIMER_EXPIRED, 0,
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700466 &ret1);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700467 if (ret)
468 __err("Timer expired for core %s but failed to notify.\n",
469 core->core_name);
470
471 return HRTIMER_NORESTART;
472}
473
474/* Helper functions and macros for sysfs nodes for a core */
475#define CORE_FROM_ATTRIBS(attr, name) \
476 container_of(container_of(attr, struct core_attribs, name), \
477 struct dcvs_core, attrib);
478
479#define DCVS_PARAM_SHOW(_name, v) \
480static ssize_t msm_dcvs_attr_##_name##_show(struct kobject *kobj, \
481 struct kobj_attribute *attr, char *buf) \
482{ \
483 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
484 return snprintf(buf, PAGE_SIZE, "%d\n", v); \
485}
486
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700487#define DCVS_PARAM_STORE(_name) \
488static ssize_t msm_dcvs_attr_##_name##_show(struct kobject *kobj,\
489 struct kobj_attribute *attr, char *buf) \
490{ \
491 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
492 return snprintf(buf, PAGE_SIZE, "%d\n", core->info->_name); \
493} \
494static ssize_t msm_dcvs_attr_##_name##_store(struct kobject *kobj, \
495 struct kobj_attribute *attr, const char *buf, size_t count) \
496{ \
497 int ret = 0; \
498 uint32_t val = 0; \
499 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
500 ret = kstrtouint(buf, 10, &val); \
501 if (ret) { \
502 __err("Invalid input %s for %s\n", buf, __stringify(_name));\
503 } else { \
504 core->info->_name = val; \
505 } \
506 return count; \
507}
508
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700509#define DCVS_ALGO_PARAM(_name) \
510static ssize_t msm_dcvs_attr_##_name##_show(struct kobject *kobj,\
511 struct kobj_attribute *attr, char *buf) \
512{ \
513 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
514 return snprintf(buf, PAGE_SIZE, "%d\n", core->algo_param._name); \
515} \
516static ssize_t msm_dcvs_attr_##_name##_store(struct kobject *kobj, \
517 struct kobj_attribute *attr, const char *buf, size_t count) \
518{ \
519 int ret = 0; \
520 uint32_t val = 0; \
521 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700522 ret = kstrtouint(buf, 10, &val); \
523 if (ret) { \
524 __err("Invalid input %s for %s\n", buf, __stringify(_name));\
525 } else { \
526 uint32_t old_val = core->algo_param._name; \
527 core->algo_param._name = val; \
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700528 ret = msm_dcvs_scm_set_algo_params(core->dcvs_core_id, \
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700529 &core->algo_param); \
530 if (ret) { \
531 core->algo_param._name = old_val; \
532 __err("Error(%d) in setting %d for algo param %s\n",\
533 ret, val, __stringify(_name)); \
534 } \
535 } \
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700536 return count; \
537}
538
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700539#define DCVS_ENERGY_PARAM(_name) \
540static ssize_t msm_dcvs_attr_##_name##_show(struct kobject *kobj,\
541 struct kobj_attribute *attr, char *buf) \
542{ \
543 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
544 return snprintf(buf, PAGE_SIZE, "%d\n", core->coeffs._name); \
545} \
546static ssize_t msm_dcvs_attr_##_name##_store(struct kobject *kobj, \
547 struct kobj_attribute *attr, const char *buf, size_t count) \
548{ \
549 int ret = 0; \
550 int32_t val = 0; \
551 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700552 ret = kstrtoint(buf, 10, &val); \
553 if (ret) { \
554 __err("Invalid input %s for %s\n", buf, __stringify(_name));\
555 } else { \
556 int32_t old_val = core->coeffs._name; \
557 core->coeffs._name = val; \
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700558 ret = msm_dcvs_scm_set_power_params(core->dcvs_core_id, \
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700559 &core->info->power_param, &core->info->freq_tbl[0], \
560 &core->coeffs); \
561 if (ret) { \
562 core->coeffs._name = old_val; \
563 __err("Error(%d) in setting %d for coeffs param %s\n",\
564 ret, val, __stringify(_name)); \
565 } \
566 } \
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700567 return count; \
568}
569
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700570#define DCVS_RO_ATTRIB(i, _name) \
571 core->attrib._name.attr.name = __stringify(_name); \
572 core->attrib._name.attr.mode = S_IRUGO; \
573 core->attrib._name.show = msm_dcvs_attr_##_name##_show; \
574 core->attrib._name.store = NULL; \
575 core->attrib.attrib_group.attrs[i] = &core->attrib._name.attr;
576
577#define DCVS_RW_ATTRIB(i, _name) \
578 core->attrib._name.attr.name = __stringify(_name); \
579 core->attrib._name.attr.mode = S_IRUGO | S_IWUSR; \
580 core->attrib._name.show = msm_dcvs_attr_##_name##_show; \
581 core->attrib._name.store = msm_dcvs_attr_##_name##_store; \
582 core->attrib.attrib_group.attrs[i] = &core->attrib._name.attr;
583
584/**
585 * Function declarations for different attributes.
586 * Gets used when setting the attribute show and store parameters.
587 */
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700588DCVS_PARAM_SHOW(core_id, core->dcvs_core_id)
589DCVS_PARAM_SHOW(idle_enabled, (core->idle_enable != NULL))
590DCVS_PARAM_SHOW(freq_change_enabled, (core->set_frequency != NULL))
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700591DCVS_PARAM_SHOW(actual_freq, (core->actual_freq))
592DCVS_PARAM_SHOW(freq_change_us, (core->freq_change_us))
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700593
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700594DCVS_ALGO_PARAM(disable_pc_threshold)
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700595DCVS_ALGO_PARAM(em_win_size_min_us)
596DCVS_ALGO_PARAM(em_win_size_max_us)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700597DCVS_ALGO_PARAM(em_max_util_pct)
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700598DCVS_ALGO_PARAM(group_id)
599DCVS_ALGO_PARAM(max_freq_chg_time_us)
600DCVS_ALGO_PARAM(slack_mode_dynamic)
601DCVS_ALGO_PARAM(slack_time_min_us)
602DCVS_ALGO_PARAM(slack_time_max_us)
603DCVS_ALGO_PARAM(slack_weight_thresh_pct)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700604DCVS_ALGO_PARAM(ss_iobusy_conv)
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700605DCVS_ALGO_PARAM(ss_win_size_min_us)
606DCVS_ALGO_PARAM(ss_win_size_max_us)
607DCVS_ALGO_PARAM(ss_util_pct)
608
609DCVS_ENERGY_PARAM(active_coeff_a)
610DCVS_ENERGY_PARAM(active_coeff_b)
611DCVS_ENERGY_PARAM(active_coeff_c)
612DCVS_ENERGY_PARAM(leakage_coeff_a)
613DCVS_ENERGY_PARAM(leakage_coeff_b)
614DCVS_ENERGY_PARAM(leakage_coeff_c)
615DCVS_ENERGY_PARAM(leakage_coeff_d)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700616
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700617DCVS_PARAM_STORE(thermal_poll_ms)
618
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700619static int msm_dcvs_setup_core_sysfs(struct dcvs_core *core)
620{
621 int ret = 0;
622 struct kobject *core_kobj = NULL;
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700623 const int attr_count = 28;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700624
625 BUG_ON(!cores_kobj);
626
627 core->attrib.attrib_group.attrs =
628 kzalloc(attr_count * sizeof(struct attribute *), GFP_KERNEL);
629
630 if (!core->attrib.attrib_group.attrs) {
631 ret = -ENOMEM;
632 goto done;
633 }
634
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700635 DCVS_RO_ATTRIB(0, core_id);
636 DCVS_RO_ATTRIB(1, idle_enabled);
637 DCVS_RO_ATTRIB(2, freq_change_enabled);
638 DCVS_RO_ATTRIB(3, actual_freq);
639 DCVS_RO_ATTRIB(4, freq_change_us);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700640
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700641 DCVS_RW_ATTRIB(5, disable_pc_threshold);
642 DCVS_RW_ATTRIB(6, em_win_size_min_us);
643 DCVS_RW_ATTRIB(7, em_win_size_max_us);
644 DCVS_RW_ATTRIB(8, em_max_util_pct);
645 DCVS_RW_ATTRIB(9, group_id);
646 DCVS_RW_ATTRIB(10, max_freq_chg_time_us);
647 DCVS_RW_ATTRIB(11, slack_mode_dynamic);
648 DCVS_RW_ATTRIB(12, slack_time_min_us);
649 DCVS_RW_ATTRIB(13, slack_time_max_us);
650 DCVS_RW_ATTRIB(14, slack_weight_thresh_pct);
651 DCVS_RW_ATTRIB(15, ss_iobusy_conv);
652 DCVS_RW_ATTRIB(16, ss_win_size_min_us);
653 DCVS_RW_ATTRIB(17, ss_win_size_max_us);
654 DCVS_RW_ATTRIB(18, ss_util_pct);
655
656 DCVS_RW_ATTRIB(19, active_coeff_a);
657 DCVS_RW_ATTRIB(20, active_coeff_b);
658 DCVS_RW_ATTRIB(21, active_coeff_c);
659 DCVS_RW_ATTRIB(22, leakage_coeff_a);
660 DCVS_RW_ATTRIB(23, leakage_coeff_b);
661 DCVS_RW_ATTRIB(24, leakage_coeff_c);
662 DCVS_RW_ATTRIB(25, leakage_coeff_d);
663
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700664 DCVS_RW_ATTRIB(26, thermal_poll_ms);
665 core->attrib.attrib_group.attrs[27] = NULL;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700666
667 core_kobj = kobject_create_and_add(core->core_name, cores_kobj);
668 if (!core_kobj) {
669 ret = -ENOMEM;
670 goto done;
671 }
672
673 ret = sysfs_create_group(core_kobj, &core->attrib.attrib_group);
674 if (ret)
675 __err("Cannot create core %s attr group\n", core->core_name);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700676
677done:
678 if (ret) {
679 kfree(core->attrib.attrib_group.attrs);
680 kobject_del(core_kobj);
681 }
682
683 return ret;
684}
685
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700686/* Return the core and initialize non platform data specific numbers in it */
687static struct dcvs_core *msm_dcvs_add_core(enum msm_dcvs_core_type type,
688 int num)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700689{
690 struct dcvs_core *core = NULL;
691 int i;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700692 char name[CORE_NAME_MAX];
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700693
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700694 switch (type) {
695 case MSM_DCVS_CORE_TYPE_CPU:
696 i = CPU_OFFSET + num;
697 BUG_ON(i >= GPU_OFFSET);
698 snprintf(name, CORE_NAME_MAX, "cpu%d", num);
699 break;
700 case MSM_DCVS_CORE_TYPE_GPU:
701 i = GPU_OFFSET + num;
702 BUG_ON(i >= CORES_MAX);
703 snprintf(name, CORE_NAME_MAX, "gpu%d", num);
704 break;
705 default:
706 return NULL;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700707 }
708
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700709 core = &core_list[i];
710 core->dcvs_core_id = i;
Abhijeet Dharmapurikar2ebc0fe2012-09-12 14:05:13 -0700711 strlcpy(core->core_name, name, CORE_NAME_MAX);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700712 spin_lock_init(&core->pending_freq_lock);
713 spin_lock_init(&core->idle_state_change_lock);
714 hrtimer_init(&core->slack_timer,
715 CLOCK_MONOTONIC, HRTIMER_MODE_REL_PINNED);
716 core->slack_timer.function = msm_dcvs_core_slack_timer;
Abhijeet Dharmapurikar2ebc0fe2012-09-12 14:05:13 -0700717 return core;
718}
719
720/* Return the core if found or add to list if @add_to_list is true */
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700721static struct dcvs_core *msm_dcvs_get_core(int offset)
Abhijeet Dharmapurikar2ebc0fe2012-09-12 14:05:13 -0700722{
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700723 /* if the handle is still not set bug */
724 BUG_ON(core_list[offset].dcvs_core_id == -1);
725 return &core_list[offset];
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700726}
727
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700728
729int msm_dcvs_register_core(
730 enum msm_dcvs_core_type type,
731 int type_core_num,
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700732 struct msm_dcvs_core_info *info,
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700733 int (*set_frequency)(int type_core_num, unsigned int freq),
734 unsigned int (*get_frequency)(int type_core_num),
735 int (*idle_enable)(int type_core_num,
Abhijeet Dharmapurikar6e9b34f2012-09-10 16:03:39 -0700736 enum msm_core_control_event event),
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700737 int sensor)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700738{
739 int ret = -EINVAL;
740 struct dcvs_core *core = NULL;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700741 uint32_t ret1;
742 uint32_t ret2;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700743
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700744 core = msm_dcvs_add_core(type, type_core_num);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700745 if (!core)
746 return ret;
747
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700748 core->type = type;
749 core->type_core_num = type_core_num;
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700750 core->set_frequency = set_frequency;
751 core->get_frequency = get_frequency;
Abhijeet Dharmapurikar6e9b34f2012-09-10 16:03:39 -0700752 core->idle_enable = idle_enable;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700753 core->pending_freq = STOP_FREQ_CHANGE;
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700754
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700755 core->info = info;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700756 memcpy(&core->algo_param, &info->algo_param,
757 sizeof(struct msm_dcvs_algo_param));
758
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700759 memcpy(&core->coeffs, &info->energy_coeffs,
760 sizeof(struct msm_dcvs_energy_curve_coeffs));
761
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700762 /*
763 * The tz expects cpu0 to represent bit 0 in the mask, however the
764 * dcvs_core_id needs to start from 1, dcvs_core_id = 0 is used to
765 * indicate that this request is not associated with any core.
766 * mpdecision
767 */
768 info->core_param.core_bitmask_id
769 = 1 << (core->dcvs_core_id - CPU_OFFSET);
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700770 core->sensor = sensor;
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700771
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700772 ret = msm_dcvs_scm_register_core(core->dcvs_core_id, &info->core_param);
773 if (ret) {
774 __err("%s: scm register core fail handle = %d ret = %d\n",
775 __func__, core->dcvs_core_id, ret);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700776 goto bail;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700777 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700778
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700779 ret = msm_dcvs_scm_set_algo_params(core->dcvs_core_id,
780 &info->algo_param);
781 if (ret) {
782 __err("%s: scm algo params failed ret = %d\n", __func__, ret);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700783 goto bail;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700784 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700785
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700786 ret = msm_dcvs_scm_set_power_params(core->dcvs_core_id,
787 &info->power_param,
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700788 &info->freq_tbl[0], &core->coeffs);
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700789 if (ret) {
790 __err("%s: scm power params failed ret = %d\n", __func__, ret);
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700791 goto bail;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700792 }
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700793
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700794 ret = msm_dcvs_scm_event(core->dcvs_core_id, MSM_DCVS_SCM_CORE_ONLINE,
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700795 core->actual_freq, 0, &ret1, &ret2);
796 if (ret)
797 goto bail;
798
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700799 ret = msm_dcvs_setup_core_sysfs(core);
800 if (ret) {
801 __err("Unable to setup core %s sysfs\n", core->core_name);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700802 goto bail;
803 }
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700804 core->idle_entered = -1;
Abhijeet Dharmapurikarbbb52fe2012-08-31 20:31:16 -0700805 init_waitqueue_head(&core->wait_q);
806 core->task = kthread_run(msm_dcvs_do_freq, (void *)core,
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700807 "msm_dcvs/%d", core->dcvs_core_id);
808 ret = core->dcvs_core_id;
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700809
810 INIT_DELAYED_WORK(&core->temperature_work, msm_dcvs_report_temp_work);
811 schedule_delayed_work(&core->temperature_work,
812 msecs_to_jiffies(info->thermal_poll_ms));
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700813 return ret;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700814bail:
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700815 core->dcvs_core_id = -1;
816 return -EINVAL;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700817}
818EXPORT_SYMBOL(msm_dcvs_register_core);
819
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700820void msm_dcvs_update_limits(int dcvs_core_id)
Abhijeet Dharmapurikarc43f0db2012-08-31 20:42:53 -0700821{
822 struct dcvs_core *core;
823
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700824 if (dcvs_core_id < CPU_OFFSET || dcvs_core_id > CORES_MAX) {
825 __err("%s invalid dcvs_core_id = %d returning -EINVAL\n",
826 __func__, dcvs_core_id);
Abhijeet Dharmapurikarc43f0db2012-08-31 20:42:53 -0700827 return;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700828 }
Abhijeet Dharmapurikarc43f0db2012-08-31 20:42:53 -0700829
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700830 core = msm_dcvs_get_core(dcvs_core_id);
831 core->actual_freq = core->get_frequency(core->type_core_num);
Abhijeet Dharmapurikarc43f0db2012-08-31 20:42:53 -0700832}
833
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700834int msm_dcvs_freq_sink_start(int dcvs_core_id)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700835{
836 int ret = -EINVAL;
837 struct dcvs_core *core = NULL;
Eugene Seah76af9832012-03-28 18:43:53 -0600838 uint32_t ret1;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700839 unsigned long flags;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700840
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700841 if (dcvs_core_id < CPU_OFFSET || dcvs_core_id > CORES_MAX) {
842 __err("%s invalid dcvs_core_id = %d returning -EINVAL\n",
843 __func__, dcvs_core_id);
844 return -EINVAL;
845 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700846
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700847 core = msm_dcvs_get_core(dcvs_core_id);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700848 if (!core)
849 return ret;
850
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700851 core->actual_freq = core->get_frequency(core->type_core_num);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700852
853 spin_lock_irqsave(&core->pending_freq_lock, flags);
854 /* mark that we are ready to accept new frequencies */
855 request_freq_change(core, NO_OUTSTANDING_FREQ_CHANGE);
856 spin_unlock_irqrestore(&core->pending_freq_lock, flags);
857
858 spin_lock_irqsave(&core->idle_state_change_lock, flags);
859 core->idle_entered = -1;
860 spin_unlock_irqrestore(&core->idle_state_change_lock, flags);
861
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700862 /* Notify TZ to start receiving idle info for the core */
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700863 ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_DCVS_ENABLE, 1, &ret1);
864
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700865 core->idle_enable(core->type_core_num, MSM_DCVS_ENABLE_IDLE_PULSE);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700866 return 0;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700867}
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700868EXPORT_SYMBOL(msm_dcvs_freq_sink_start);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700869
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700870int msm_dcvs_freq_sink_stop(int dcvs_core_id)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700871{
872 int ret = -EINVAL;
873 struct dcvs_core *core = NULL;
Eugene Seah76af9832012-03-28 18:43:53 -0600874 uint32_t ret1;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700875 uint32_t freq;
876 unsigned long flags;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700877
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700878 if (dcvs_core_id < 0 || dcvs_core_id > CORES_MAX) {
879 pr_err("%s invalid dcvs_core_id = %d returning -EINVAL\n",
880 __func__, dcvs_core_id);
881 return -EINVAL;
882 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700883
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700884 core = msm_dcvs_get_core(dcvs_core_id);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700885 if (!core) {
886 __err("couldn't find core for coreid = %d\n", dcvs_core_id);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700887 return ret;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700888 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700889
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700890 core->idle_enable(core->type_core_num, MSM_DCVS_DISABLE_IDLE_PULSE);
891 /* Notify TZ to stop receiving idle info for the core */
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700892 ret = msm_dcvs_scm_event(core->dcvs_core_id, MSM_DCVS_SCM_DCVS_ENABLE,
893 0, core->actual_freq, &freq, &ret1);
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700894 core->idle_enable(core->type_core_num,
895 MSM_DCVS_ENABLE_HIGH_LATENCY_MODES);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700896 spin_lock_irqsave(&core->pending_freq_lock, flags);
897 /* flush out all the pending freq changes */
898 request_freq_change(core, STOP_FREQ_CHANGE);
899 spin_unlock_irqrestore(&core->pending_freq_lock, flags);
900 force_stop_slack_timer(core);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700901
902 return 0;
903}
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700904EXPORT_SYMBOL(msm_dcvs_freq_sink_stop);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700905
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700906int msm_dcvs_idle(int dcvs_core_id, enum msm_core_idle_state state,
907 uint32_t iowaited)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700908{
909 int ret = 0;
910 struct dcvs_core *core = NULL;
Eugene Seah76af9832012-03-28 18:43:53 -0600911 uint32_t timer_interval_us = 0;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700912 uint32_t r0, r1;
913
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700914 if (dcvs_core_id < CPU_OFFSET || dcvs_core_id > CORES_MAX) {
915 pr_err("invalid dcvs_core_id = %d ret -EINVAL\n", dcvs_core_id);
916 return -EINVAL;
917 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700918
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700919 core = msm_dcvs_get_core(dcvs_core_id);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700920
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700921 switch (state) {
922 case MSM_DCVS_IDLE_ENTER:
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700923 stop_slack_timer(core);
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700924 ret = msm_dcvs_scm_event(core->dcvs_core_id,
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700925 MSM_DCVS_SCM_IDLE_ENTER, 0, 0, &r0, &r1);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700926 if (ret < 0 && ret != -13)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700927 __err("Error (%d) sending idle enter for %s\n",
928 ret, core->core_name);
929 break;
930
931 case MSM_DCVS_IDLE_EXIT:
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700932 ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_IDLE_EXIT,
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700933 iowaited, &timer_interval_us);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700934 if (ret)
935 __err("Error (%d) sending idle exit for %s\n",
936 ret, core->core_name);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700937 start_slack_timer(core, timer_interval_us);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700938 break;
939 }
940
941 return ret;
942}
943EXPORT_SYMBOL(msm_dcvs_idle);
944
945static int __init msm_dcvs_late_init(void)
946{
947 struct kobject *module_kobj = NULL;
948 int ret = 0;
949
950 module_kobj = kset_find_obj(module_kset, KBUILD_MODNAME);
951 if (!module_kobj) {
952 pr_err("%s: cannot find kobject for module %s\n",
953 __func__, KBUILD_MODNAME);
954 ret = -ENOENT;
955 goto err;
956 }
957
958 cores_kobj = kobject_create_and_add("cores", module_kobj);
959 if (!cores_kobj) {
960 __err("Cannot create %s kobject\n", "cores");
961 ret = -ENOMEM;
962 goto err;
963 }
964
965 debugfs_base = debugfs_create_dir("msm_dcvs", NULL);
966 if (!debugfs_base) {
967 __err("Cannot create debugfs base %s\n", "msm_dcvs");
968 ret = -ENOENT;
969 goto err;
970 }
971
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700972err:
973 if (ret) {
974 kobject_del(cores_kobj);
975 cores_kobj = NULL;
976 debugfs_remove(debugfs_base);
977 }
978
979 return ret;
980}
981late_initcall(msm_dcvs_late_init);
982
983static int __init msm_dcvs_early_init(void)
984{
985 int ret = 0;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700986 int i;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700987
988 if (!msm_dcvs_enabled) {
989 __info("Not enabled (%d)\n", msm_dcvs_enabled);
990 return 0;
991 }
992
Abhijeet Dharmapurikar3edb5de2012-09-13 11:02:03 -0700993
994 /* Only need about 32kBytes for normal operation */
995 ret = msm_dcvs_scm_init(SZ_32K);
996 if (ret) {
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700997 __err("Unable to initialize DCVS err=%d\n", ret);
Abhijeet Dharmapurikar3edb5de2012-09-13 11:02:03 -0700998 goto done;
999 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001000
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001001 for (i = 0; i < CORES_MAX; i++)
1002 core_list[i].dcvs_core_id = -1;
Abhijeet Dharmapurikar3edb5de2012-09-13 11:02:03 -07001003done:
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001004 return ret;
1005}
1006postcore_initcall(msm_dcvs_early_init);