blob: 98e3722d2fd6221529b6d32c27ab99ec5bf6fc6b [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
149static void stop_slack_timer(struct dcvs_core *core)
150{
151 unsigned long flags;
152
153 spin_lock_irqsave(&core->idle_state_change_lock, flags);
154 /* err only for cpu type's GPU's can do idle exit consecutively */
155 if (core->idle_entered == 1 && !(core->dcvs_core_id >= GPU_OFFSET))
156 __err("%s trying to reenter idle", core->core_name);
157 core->idle_entered = 1;
158 hrtimer_cancel(&core->slack_timer);
159 core->idle_entered = 1;
160 spin_unlock_irqrestore(&core->idle_state_change_lock, flags);
161}
162
163static void start_slack_timer(struct dcvs_core *core, int slack_us)
164{
165 unsigned long flags1, flags2;
166 int ret;
167
168 spin_lock_irqsave(&core->idle_state_change_lock, flags2);
169
170 spin_lock_irqsave(&core->pending_freq_lock, flags1);
171
172 /* err only for cpu type's GPU's can do idle enter consecutively */
173 if (core->idle_entered == 0 && !(core->dcvs_core_id >= GPU_OFFSET))
174 __err("%s trying to reexit idle", core->core_name);
175 core->idle_entered = 0;
176 /*
177 * only start the timer if governor is not stopped
178 */
179 if (slack_us != 0
180 && !(core->pending_freq < NO_OUTSTANDING_FREQ_CHANGE)) {
181 ret = hrtimer_start(&core->slack_timer,
182 ktime_set(0, slack_us * 1000),
183 HRTIMER_MODE_REL_PINNED);
184 if (ret) {
185 pr_err("%s Failed to start timer ret = %d\n",
186 core->core_name, ret);
187 }
188 }
189 spin_unlock_irqrestore(&core->pending_freq_lock, flags1);
190
191 spin_unlock_irqrestore(&core->idle_state_change_lock, flags2);
192}
193
194static void restart_slack_timer(struct dcvs_core *core, int slack_us)
195{
196 unsigned long flags1, flags2;
197 int ret;
198
199 spin_lock_irqsave(&core->idle_state_change_lock, flags2);
200
201 hrtimer_cancel(&core->slack_timer);
202
203 spin_lock_irqsave(&core->pending_freq_lock, flags1);
204
205 /*
206 * only start the timer if idle is not entered
207 * and governor is not stopped
208 */
209 if (slack_us != 0 && (core->idle_entered != 1)
210 && !(core->pending_freq < NO_OUTSTANDING_FREQ_CHANGE)) {
211 ret = hrtimer_start(&core->slack_timer,
212 ktime_set(0, slack_us * 1000),
213 HRTIMER_MODE_REL_PINNED);
214 if (ret) {
215 pr_err("%s Failed to start timer ret = %d\n",
216 core->core_name, ret);
217 }
218 }
219 spin_unlock_irqrestore(&core->pending_freq_lock, flags1);
220 spin_unlock_irqrestore(&core->idle_state_change_lock, flags2);
221}
222
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700223static int __msm_dcvs_change_freq(struct dcvs_core *core)
224{
225 int ret = 0;
226 unsigned long flags = 0;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700227 int requested_freq = 0;
228 ktime_t time_start;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700229 uint32_t slack_us = 0;
230 uint32_t ret1 = 0;
231
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700232 spin_lock_irqsave(&core->pending_freq_lock, flags);
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700233repeat:
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700234 BUG_ON(!core->pending_freq);
235 if (core->pending_freq == STOP_FREQ_CHANGE)
236 BUG();
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700237
238 requested_freq = core->pending_freq;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700239 time_start = core->time_start;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700240 core->time_start = ns_to_ktime(0);
241
242 if (requested_freq < 0) {
243 requested_freq = -1 * requested_freq;
244 core->pending_freq = STOP_FREQ_CHANGE;
245 } else {
246 core->pending_freq = NO_OUTSTANDING_FREQ_CHANGE;
247 }
248
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700249 if (requested_freq == core->actual_freq)
250 goto out;
251
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700252 spin_unlock_irqrestore(&core->pending_freq_lock, flags);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700253
254 /**
255 * Call the frequency sink driver to change the frequency
256 * We will need to get back the actual frequency in KHz and
257 * the record the time taken to change it.
258 */
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700259 ret = core->set_frequency(core->type_core_num, requested_freq);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700260 if (ret <= 0)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700261 __err("Core %s failed to set freq %u\n",
262 core->core_name, requested_freq);
Eugene Seah76af9832012-03-28 18:43:53 -0600263 /* continue to call TZ to get updated slack timer */
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700264 else
Eugene Seah76af9832012-03-28 18:43:53 -0600265 core->actual_freq = ret;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700266
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700267 core->freq_change_us = (uint32_t)ktime_to_us(
268 ktime_sub(ktime_get(), time_start));
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700269
270 /**
271 * Disable low power modes if the actual frequency is >
272 * disable_pc_threshold.
273 */
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700274 if (core->actual_freq > core->algo_param.disable_pc_threshold) {
275 core->idle_enable(core->type_core_num,
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700276 MSM_DCVS_DISABLE_HIGH_LATENCY_MODES);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700277 } else if (core->actual_freq <= core->algo_param.disable_pc_threshold) {
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700278 core->idle_enable(core->type_core_num,
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700279 MSM_DCVS_ENABLE_HIGH_LATENCY_MODES);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700280 }
281
282 /**
283 * Update algorithm with new freq and time taken to change
284 * to this frequency and that will get us the new slack
285 * timer
286 */
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700287 ret = msm_dcvs_scm_event(core->dcvs_core_id,
288 MSM_DCVS_SCM_CLOCK_FREQ_UPDATE,
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700289 core->actual_freq, core->freq_change_us,
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700290 &slack_us, &ret1);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700291 if (ret) {
292 __err("Error sending core (%s) dcvs_core_id = %d freq change (%u) reqfreq = %d slack_us=%d ret = %d\n",
293 core->core_name, core->dcvs_core_id,
294 core->actual_freq, requested_freq,
295 slack_us, ret);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700296 }
297
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700298 /* TODO confirm that we get a valid freq from SM even when the above
299 * FREQ_UPDATE fails
300 */
301 restart_slack_timer(core, slack_us);
302 spin_lock_irqsave(&core->pending_freq_lock, flags);
303
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700304 /**
305 * By the time we are done with freq changes, we could be asked to
306 * change again. Check before exiting.
307 */
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700308 if (core->pending_freq != NO_OUTSTANDING_FREQ_CHANGE
309 && core->pending_freq != STOP_FREQ_CHANGE) {
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700310 goto repeat;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700311 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700312
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700313out: /* should always be jumped to with the spin_lock held */
314 spin_unlock_irqrestore(&core->pending_freq_lock, flags);
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700315
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700316 return ret;
317}
318
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700319static void msm_dcvs_report_temp_work(struct work_struct *work)
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700320{
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700321 struct dcvs_core *core = container_of(work,
322 struct dcvs_core,
323 temperature_work.work);
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700324 struct msm_dcvs_core_info *info = core->info;
325 struct tsens_device tsens_dev;
326 int ret;
327 unsigned long temp = 0;
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700328 int interval_ms;
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700329
330 tsens_dev.sensor_num = core->sensor;
331 ret = tsens_get_temp(&tsens_dev, &temp);
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700332 if (!temp) {
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700333 tsens_dev.sensor_num = 0;
334 ret = tsens_get_temp(&tsens_dev, &temp);
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700335 if (!temp)
336 goto out;
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700337 }
338
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700339 if (temp == info->power_param.current_temp)
340 goto out;
341 info->power_param.current_temp = temp;
342
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700343 ret = msm_dcvs_scm_set_power_params(core->dcvs_core_id,
344 &info->power_param,
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700345 &info->freq_tbl[0], &core->coeffs);
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700346out:
347 if (info->thermal_poll_ms == 0)
348 interval_ms = 60000;
349 else if (info->thermal_poll_ms < 1000)
350 interval_ms = 1000;
351 else
352 interval_ms = info->thermal_poll_ms;
353
354 schedule_delayed_work(&core->temperature_work,
355 msecs_to_jiffies(interval_ms));
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700356}
357
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700358static int msm_dcvs_do_freq(void *data)
359{
360 struct dcvs_core *core = (struct dcvs_core *)data;
361 static struct sched_param param = {.sched_priority = MAX_RT_PRIO - 1};
362
363 sched_setscheduler(current, SCHED_FIFO, &param);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700364
365 while (!kthread_should_stop()) {
Abhijeet Dharmapurikarbbb52fe2012-08-31 20:31:16 -0700366 wait_event(core->wait_q, !(core->pending_freq == 0 ||
367 core->pending_freq == -1) ||
368 kthread_should_stop());
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700369
370 if (kthread_should_stop())
371 break;
372
Abhijeet Dharmapurikarbbb52fe2012-08-31 20:31:16 -0700373 __msm_dcvs_change_freq(core);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700374 }
375
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700376 return 0;
377}
378
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700379/* freq_pending_lock should be held */
380static void request_freq_change(struct dcvs_core *core, int new_freq)
381{
382 if (new_freq == NO_OUTSTANDING_FREQ_CHANGE) {
383 if (core->pending_freq != STOP_FREQ_CHANGE) {
384 __err("%s gov started with earlier pending freq %d\n",
385 core->core_name, core->pending_freq);
386 }
387 core->pending_freq = NO_OUTSTANDING_FREQ_CHANGE;
388 return;
389 }
390
391 if (new_freq == STOP_FREQ_CHANGE) {
392 if (core->pending_freq == NO_OUTSTANDING_FREQ_CHANGE)
393 core->pending_freq = STOP_FREQ_CHANGE;
394 else if (core->pending_freq > 0)
395 core->pending_freq = -1 * core->pending_freq;
396 return;
397 }
398
399 if (core->pending_freq < 0) {
400 /* a value less than 0 means that the governor has stopped
401 * and no more freq changes should be requested
402 */
403 return;
404 }
405
406 if (core->actual_freq != new_freq && core->pending_freq != new_freq) {
407 core->pending_freq = new_freq;
408 core->time_start = ktime_get();
409 wake_up(&core->wait_q);
410 }
411}
412
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700413static int msm_dcvs_update_freq(struct dcvs_core *core,
Eugene Seah76af9832012-03-28 18:43:53 -0600414 enum msm_dcvs_scm_event event, uint32_t param0,
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700415 uint32_t *ret1)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700416{
417 int ret = 0;
418 unsigned long flags = 0;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700419 uint32_t new_freq = -EINVAL;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700420
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700421 spin_lock_irqsave(&core->pending_freq_lock, flags);
422
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700423 ret = msm_dcvs_scm_event(core->dcvs_core_id, event, param0,
Eugene Seah76af9832012-03-28 18:43:53 -0600424 core->actual_freq, &new_freq, ret1);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700425 if (ret) {
Jeff Ohlstein4aa4a2b2012-06-28 19:03:49 -0700426 if (ret == -13)
427 ret = 0;
428 else
429 __err("Error (%d) sending SCM event %d for core %s\n",
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700430 ret, event, core->core_name);
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700431 goto out;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700432 }
433
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700434 if (new_freq == 0) {
435 /*
436 * sometimes TZ gives us a 0 freq back,
437 * do not queue up a request
438 */
439 goto out;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700440 }
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700441
442 request_freq_change(core, new_freq);
443
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700444out:
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700445 spin_unlock_irqrestore(&core->pending_freq_lock, flags);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700446
447 return ret;
448}
449
450static enum hrtimer_restart msm_dcvs_core_slack_timer(struct hrtimer *timer)
451{
452 int ret = 0;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700453 struct dcvs_core *core = container_of(timer,
454 struct dcvs_core, slack_timer);
Eugene Seah76af9832012-03-28 18:43:53 -0600455 uint32_t ret1;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700456
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700457 /**
458 * Timer expired, notify TZ
459 * Dont care about the third arg.
460 */
Eugene Seah76af9832012-03-28 18:43:53 -0600461 ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_QOS_TIMER_EXPIRED, 0,
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700462 &ret1);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700463 if (ret)
464 __err("Timer expired for core %s but failed to notify.\n",
465 core->core_name);
466
467 return HRTIMER_NORESTART;
468}
469
470/* Helper functions and macros for sysfs nodes for a core */
471#define CORE_FROM_ATTRIBS(attr, name) \
472 container_of(container_of(attr, struct core_attribs, name), \
473 struct dcvs_core, attrib);
474
475#define DCVS_PARAM_SHOW(_name, v) \
476static ssize_t msm_dcvs_attr_##_name##_show(struct kobject *kobj, \
477 struct kobj_attribute *attr, char *buf) \
478{ \
479 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
480 return snprintf(buf, PAGE_SIZE, "%d\n", v); \
481}
482
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700483#define DCVS_PARAM_STORE(_name) \
484static ssize_t msm_dcvs_attr_##_name##_show(struct kobject *kobj,\
485 struct kobj_attribute *attr, char *buf) \
486{ \
487 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
488 return snprintf(buf, PAGE_SIZE, "%d\n", core->info->_name); \
489} \
490static ssize_t msm_dcvs_attr_##_name##_store(struct kobject *kobj, \
491 struct kobj_attribute *attr, const char *buf, size_t count) \
492{ \
493 int ret = 0; \
494 uint32_t val = 0; \
495 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
496 ret = kstrtouint(buf, 10, &val); \
497 if (ret) { \
498 __err("Invalid input %s for %s\n", buf, __stringify(_name));\
499 } else { \
500 core->info->_name = val; \
501 } \
502 return count; \
503}
504
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700505#define DCVS_ALGO_PARAM(_name) \
506static ssize_t msm_dcvs_attr_##_name##_show(struct kobject *kobj,\
507 struct kobj_attribute *attr, char *buf) \
508{ \
509 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
510 return snprintf(buf, PAGE_SIZE, "%d\n", core->algo_param._name); \
511} \
512static ssize_t msm_dcvs_attr_##_name##_store(struct kobject *kobj, \
513 struct kobj_attribute *attr, const char *buf, size_t count) \
514{ \
515 int ret = 0; \
516 uint32_t val = 0; \
517 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700518 ret = kstrtouint(buf, 10, &val); \
519 if (ret) { \
520 __err("Invalid input %s for %s\n", buf, __stringify(_name));\
521 } else { \
522 uint32_t old_val = core->algo_param._name; \
523 core->algo_param._name = val; \
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700524 ret = msm_dcvs_scm_set_algo_params(core->dcvs_core_id, \
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700525 &core->algo_param); \
526 if (ret) { \
527 core->algo_param._name = old_val; \
528 __err("Error(%d) in setting %d for algo param %s\n",\
529 ret, val, __stringify(_name)); \
530 } \
531 } \
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700532 return count; \
533}
534
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700535#define DCVS_ENERGY_PARAM(_name) \
536static ssize_t msm_dcvs_attr_##_name##_show(struct kobject *kobj,\
537 struct kobj_attribute *attr, char *buf) \
538{ \
539 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
540 return snprintf(buf, PAGE_SIZE, "%d\n", core->coeffs._name); \
541} \
542static ssize_t msm_dcvs_attr_##_name##_store(struct kobject *kobj, \
543 struct kobj_attribute *attr, const char *buf, size_t count) \
544{ \
545 int ret = 0; \
546 int32_t val = 0; \
547 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700548 ret = kstrtoint(buf, 10, &val); \
549 if (ret) { \
550 __err("Invalid input %s for %s\n", buf, __stringify(_name));\
551 } else { \
552 int32_t old_val = core->coeffs._name; \
553 core->coeffs._name = val; \
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700554 ret = msm_dcvs_scm_set_power_params(core->dcvs_core_id, \
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700555 &core->info->power_param, &core->info->freq_tbl[0], \
556 &core->coeffs); \
557 if (ret) { \
558 core->coeffs._name = old_val; \
559 __err("Error(%d) in setting %d for coeffs param %s\n",\
560 ret, val, __stringify(_name)); \
561 } \
562 } \
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700563 return count; \
564}
565
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700566#define DCVS_RO_ATTRIB(i, _name) \
567 core->attrib._name.attr.name = __stringify(_name); \
568 core->attrib._name.attr.mode = S_IRUGO; \
569 core->attrib._name.show = msm_dcvs_attr_##_name##_show; \
570 core->attrib._name.store = NULL; \
571 core->attrib.attrib_group.attrs[i] = &core->attrib._name.attr;
572
573#define DCVS_RW_ATTRIB(i, _name) \
574 core->attrib._name.attr.name = __stringify(_name); \
575 core->attrib._name.attr.mode = S_IRUGO | S_IWUSR; \
576 core->attrib._name.show = msm_dcvs_attr_##_name##_show; \
577 core->attrib._name.store = msm_dcvs_attr_##_name##_store; \
578 core->attrib.attrib_group.attrs[i] = &core->attrib._name.attr;
579
580/**
581 * Function declarations for different attributes.
582 * Gets used when setting the attribute show and store parameters.
583 */
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700584DCVS_PARAM_SHOW(freq_change_us, (core->freq_change_us))
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700585
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700586DCVS_ALGO_PARAM(disable_pc_threshold)
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700587DCVS_ALGO_PARAM(em_win_size_min_us)
588DCVS_ALGO_PARAM(em_win_size_max_us)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700589DCVS_ALGO_PARAM(em_max_util_pct)
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700590DCVS_ALGO_PARAM(group_id)
591DCVS_ALGO_PARAM(max_freq_chg_time_us)
592DCVS_ALGO_PARAM(slack_mode_dynamic)
593DCVS_ALGO_PARAM(slack_time_min_us)
594DCVS_ALGO_PARAM(slack_time_max_us)
595DCVS_ALGO_PARAM(slack_weight_thresh_pct)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700596DCVS_ALGO_PARAM(ss_iobusy_conv)
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700597DCVS_ALGO_PARAM(ss_win_size_min_us)
598DCVS_ALGO_PARAM(ss_win_size_max_us)
599DCVS_ALGO_PARAM(ss_util_pct)
600
601DCVS_ENERGY_PARAM(active_coeff_a)
602DCVS_ENERGY_PARAM(active_coeff_b)
603DCVS_ENERGY_PARAM(active_coeff_c)
604DCVS_ENERGY_PARAM(leakage_coeff_a)
605DCVS_ENERGY_PARAM(leakage_coeff_b)
606DCVS_ENERGY_PARAM(leakage_coeff_c)
607DCVS_ENERGY_PARAM(leakage_coeff_d)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700608
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700609DCVS_PARAM_STORE(thermal_poll_ms)
610
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700611static int msm_dcvs_setup_core_sysfs(struct dcvs_core *core)
612{
613 int ret = 0;
614 struct kobject *core_kobj = NULL;
Abhijeet Dharmapurikar35876382012-09-13 16:09:51 -0700615 const int attr_count = 24;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700616
617 BUG_ON(!cores_kobj);
618
619 core->attrib.attrib_group.attrs =
620 kzalloc(attr_count * sizeof(struct attribute *), GFP_KERNEL);
621
622 if (!core->attrib.attrib_group.attrs) {
623 ret = -ENOMEM;
624 goto done;
625 }
626
Abhijeet Dharmapurikar35876382012-09-13 16:09:51 -0700627 DCVS_RO_ATTRIB(0, freq_change_us);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700628
Abhijeet Dharmapurikar35876382012-09-13 16:09:51 -0700629 DCVS_RW_ATTRIB(1, disable_pc_threshold);
630 DCVS_RW_ATTRIB(2, em_win_size_min_us);
631 DCVS_RW_ATTRIB(3, em_win_size_max_us);
632 DCVS_RW_ATTRIB(4, em_max_util_pct);
633 DCVS_RW_ATTRIB(5, group_id);
634 DCVS_RW_ATTRIB(6, max_freq_chg_time_us);
635 DCVS_RW_ATTRIB(7, slack_mode_dynamic);
636 DCVS_RW_ATTRIB(8, slack_weight_thresh_pct);
637 DCVS_RW_ATTRIB(9, slack_time_min_us);
638 DCVS_RW_ATTRIB(10, slack_time_max_us);
639 DCVS_RW_ATTRIB(11, ss_iobusy_conv);
640 DCVS_RW_ATTRIB(12, ss_win_size_min_us);
641 DCVS_RW_ATTRIB(13, ss_win_size_max_us);
642 DCVS_RW_ATTRIB(14, ss_util_pct);
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700643
Abhijeet Dharmapurikar35876382012-09-13 16:09:51 -0700644 DCVS_RW_ATTRIB(15, active_coeff_a);
645 DCVS_RW_ATTRIB(16, active_coeff_b);
646 DCVS_RW_ATTRIB(17, active_coeff_c);
647 DCVS_RW_ATTRIB(18, leakage_coeff_a);
648 DCVS_RW_ATTRIB(19, leakage_coeff_b);
649 DCVS_RW_ATTRIB(20, leakage_coeff_c);
650 DCVS_RW_ATTRIB(21, leakage_coeff_d);
651 DCVS_RW_ATTRIB(22, thermal_poll_ms);
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700652
Abhijeet Dharmapurikar35876382012-09-13 16:09:51 -0700653 core->attrib.attrib_group.attrs[23] = NULL;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700654
655 core_kobj = kobject_create_and_add(core->core_name, cores_kobj);
656 if (!core_kobj) {
657 ret = -ENOMEM;
658 goto done;
659 }
660
661 ret = sysfs_create_group(core_kobj, &core->attrib.attrib_group);
662 if (ret)
663 __err("Cannot create core %s attr group\n", core->core_name);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700664
665done:
666 if (ret) {
667 kfree(core->attrib.attrib_group.attrs);
668 kobject_del(core_kobj);
669 }
670
671 return ret;
672}
673
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700674/* Return the core and initialize non platform data specific numbers in it */
675static struct dcvs_core *msm_dcvs_add_core(enum msm_dcvs_core_type type,
676 int num)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700677{
678 struct dcvs_core *core = NULL;
679 int i;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700680 char name[CORE_NAME_MAX];
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700681
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700682 switch (type) {
683 case MSM_DCVS_CORE_TYPE_CPU:
684 i = CPU_OFFSET + num;
685 BUG_ON(i >= GPU_OFFSET);
686 snprintf(name, CORE_NAME_MAX, "cpu%d", num);
687 break;
688 case MSM_DCVS_CORE_TYPE_GPU:
689 i = GPU_OFFSET + num;
690 BUG_ON(i >= CORES_MAX);
691 snprintf(name, CORE_NAME_MAX, "gpu%d", num);
692 break;
693 default:
694 return NULL;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700695 }
696
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700697 core = &core_list[i];
698 core->dcvs_core_id = i;
Abhijeet Dharmapurikar2ebc0fe2012-09-12 14:05:13 -0700699 strlcpy(core->core_name, name, CORE_NAME_MAX);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700700 spin_lock_init(&core->pending_freq_lock);
701 spin_lock_init(&core->idle_state_change_lock);
702 hrtimer_init(&core->slack_timer,
703 CLOCK_MONOTONIC, HRTIMER_MODE_REL_PINNED);
704 core->slack_timer.function = msm_dcvs_core_slack_timer;
Abhijeet Dharmapurikar2ebc0fe2012-09-12 14:05:13 -0700705 return core;
706}
707
708/* Return the core if found or add to list if @add_to_list is true */
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700709static struct dcvs_core *msm_dcvs_get_core(int offset)
Abhijeet Dharmapurikar2ebc0fe2012-09-12 14:05:13 -0700710{
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700711 /* if the handle is still not set bug */
712 BUG_ON(core_list[offset].dcvs_core_id == -1);
713 return &core_list[offset];
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700714}
715
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700716
717int msm_dcvs_register_core(
718 enum msm_dcvs_core_type type,
719 int type_core_num,
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700720 struct msm_dcvs_core_info *info,
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700721 int (*set_frequency)(int type_core_num, unsigned int freq),
722 unsigned int (*get_frequency)(int type_core_num),
723 int (*idle_enable)(int type_core_num,
Abhijeet Dharmapurikar6e9b34f2012-09-10 16:03:39 -0700724 enum msm_core_control_event event),
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700725 int sensor)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700726{
727 int ret = -EINVAL;
728 struct dcvs_core *core = NULL;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700729 uint32_t ret1;
730 uint32_t ret2;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700731
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700732 core = msm_dcvs_add_core(type, type_core_num);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700733 if (!core)
734 return ret;
735
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700736 core->type = type;
737 core->type_core_num = type_core_num;
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700738 core->set_frequency = set_frequency;
739 core->get_frequency = get_frequency;
Abhijeet Dharmapurikar6e9b34f2012-09-10 16:03:39 -0700740 core->idle_enable = idle_enable;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700741 core->pending_freq = STOP_FREQ_CHANGE;
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700742
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700743 core->info = info;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700744 memcpy(&core->algo_param, &info->algo_param,
745 sizeof(struct msm_dcvs_algo_param));
746
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700747 memcpy(&core->coeffs, &info->energy_coeffs,
748 sizeof(struct msm_dcvs_energy_curve_coeffs));
749
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700750 /*
751 * The tz expects cpu0 to represent bit 0 in the mask, however the
752 * dcvs_core_id needs to start from 1, dcvs_core_id = 0 is used to
753 * indicate that this request is not associated with any core.
754 * mpdecision
755 */
756 info->core_param.core_bitmask_id
757 = 1 << (core->dcvs_core_id - CPU_OFFSET);
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700758 core->sensor = sensor;
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700759
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700760 ret = msm_dcvs_scm_register_core(core->dcvs_core_id, &info->core_param);
761 if (ret) {
762 __err("%s: scm register core fail handle = %d ret = %d\n",
763 __func__, core->dcvs_core_id, ret);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700764 goto bail;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700765 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700766
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700767 ret = msm_dcvs_scm_set_algo_params(core->dcvs_core_id,
768 &info->algo_param);
769 if (ret) {
770 __err("%s: scm algo params failed ret = %d\n", __func__, ret);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700771 goto bail;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700772 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700773
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700774 ret = msm_dcvs_scm_set_power_params(core->dcvs_core_id,
775 &info->power_param,
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700776 &info->freq_tbl[0], &core->coeffs);
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700777 if (ret) {
778 __err("%s: scm power params failed ret = %d\n", __func__, ret);
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700779 goto bail;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700780 }
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700781
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700782 ret = msm_dcvs_scm_event(core->dcvs_core_id, MSM_DCVS_SCM_CORE_ONLINE,
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700783 core->actual_freq, 0, &ret1, &ret2);
784 if (ret)
785 goto bail;
786
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700787 ret = msm_dcvs_setup_core_sysfs(core);
788 if (ret) {
789 __err("Unable to setup core %s sysfs\n", core->core_name);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700790 goto bail;
791 }
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700792 core->idle_entered = -1;
Abhijeet Dharmapurikarbbb52fe2012-08-31 20:31:16 -0700793 init_waitqueue_head(&core->wait_q);
794 core->task = kthread_run(msm_dcvs_do_freq, (void *)core,
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700795 "msm_dcvs/%d", core->dcvs_core_id);
796 ret = core->dcvs_core_id;
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700797
798 INIT_DELAYED_WORK(&core->temperature_work, msm_dcvs_report_temp_work);
799 schedule_delayed_work(&core->temperature_work,
800 msecs_to_jiffies(info->thermal_poll_ms));
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700801 return ret;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700802bail:
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700803 core->dcvs_core_id = -1;
804 return -EINVAL;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700805}
806EXPORT_SYMBOL(msm_dcvs_register_core);
807
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700808void msm_dcvs_update_limits(int dcvs_core_id)
Abhijeet Dharmapurikarc43f0db2012-08-31 20:42:53 -0700809{
810 struct dcvs_core *core;
811
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700812 if (dcvs_core_id < CPU_OFFSET || dcvs_core_id > CORES_MAX) {
813 __err("%s invalid dcvs_core_id = %d returning -EINVAL\n",
814 __func__, dcvs_core_id);
Abhijeet Dharmapurikarc43f0db2012-08-31 20:42:53 -0700815 return;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700816 }
Abhijeet Dharmapurikarc43f0db2012-08-31 20:42:53 -0700817
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700818 core = msm_dcvs_get_core(dcvs_core_id);
819 core->actual_freq = core->get_frequency(core->type_core_num);
Abhijeet Dharmapurikarc43f0db2012-08-31 20:42:53 -0700820}
821
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700822int msm_dcvs_freq_sink_start(int dcvs_core_id)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700823{
824 int ret = -EINVAL;
825 struct dcvs_core *core = NULL;
Eugene Seah76af9832012-03-28 18:43:53 -0600826 uint32_t ret1;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700827 unsigned long flags;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700828
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700829 if (dcvs_core_id < CPU_OFFSET || dcvs_core_id > CORES_MAX) {
830 __err("%s invalid dcvs_core_id = %d returning -EINVAL\n",
831 __func__, dcvs_core_id);
832 return -EINVAL;
833 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700834
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700835 core = msm_dcvs_get_core(dcvs_core_id);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700836 if (!core)
837 return ret;
838
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700839 core->actual_freq = core->get_frequency(core->type_core_num);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700840
841 spin_lock_irqsave(&core->pending_freq_lock, flags);
842 /* mark that we are ready to accept new frequencies */
843 request_freq_change(core, NO_OUTSTANDING_FREQ_CHANGE);
844 spin_unlock_irqrestore(&core->pending_freq_lock, flags);
845
846 spin_lock_irqsave(&core->idle_state_change_lock, flags);
847 core->idle_entered = -1;
848 spin_unlock_irqrestore(&core->idle_state_change_lock, flags);
849
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700850 /* Notify TZ to start receiving idle info for the core */
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700851 ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_DCVS_ENABLE, 1, &ret1);
852
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700853 core->idle_enable(core->type_core_num, MSM_DCVS_ENABLE_IDLE_PULSE);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700854 return 0;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700855}
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700856EXPORT_SYMBOL(msm_dcvs_freq_sink_start);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700857
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700858int msm_dcvs_freq_sink_stop(int dcvs_core_id)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700859{
860 int ret = -EINVAL;
861 struct dcvs_core *core = NULL;
Eugene Seah76af9832012-03-28 18:43:53 -0600862 uint32_t ret1;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700863 uint32_t freq;
864 unsigned long flags;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700865
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700866 if (dcvs_core_id < 0 || dcvs_core_id > CORES_MAX) {
867 pr_err("%s invalid dcvs_core_id = %d returning -EINVAL\n",
868 __func__, dcvs_core_id);
869 return -EINVAL;
870 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700871
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700872 core = msm_dcvs_get_core(dcvs_core_id);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700873 if (!core) {
874 __err("couldn't find core for coreid = %d\n", dcvs_core_id);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700875 return ret;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700876 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700877
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700878 core->idle_enable(core->type_core_num, MSM_DCVS_DISABLE_IDLE_PULSE);
879 /* Notify TZ to stop receiving idle info for the core */
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700880 ret = msm_dcvs_scm_event(core->dcvs_core_id, MSM_DCVS_SCM_DCVS_ENABLE,
881 0, core->actual_freq, &freq, &ret1);
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700882 core->idle_enable(core->type_core_num,
883 MSM_DCVS_ENABLE_HIGH_LATENCY_MODES);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700884 spin_lock_irqsave(&core->pending_freq_lock, flags);
885 /* flush out all the pending freq changes */
886 request_freq_change(core, STOP_FREQ_CHANGE);
887 spin_unlock_irqrestore(&core->pending_freq_lock, flags);
888 force_stop_slack_timer(core);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700889
890 return 0;
891}
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700892EXPORT_SYMBOL(msm_dcvs_freq_sink_stop);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700893
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700894int msm_dcvs_idle(int dcvs_core_id, enum msm_core_idle_state state,
895 uint32_t iowaited)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700896{
897 int ret = 0;
898 struct dcvs_core *core = NULL;
Eugene Seah76af9832012-03-28 18:43:53 -0600899 uint32_t timer_interval_us = 0;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700900 uint32_t r0, r1;
901
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700902 if (dcvs_core_id < CPU_OFFSET || dcvs_core_id > CORES_MAX) {
903 pr_err("invalid dcvs_core_id = %d ret -EINVAL\n", dcvs_core_id);
904 return -EINVAL;
905 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700906
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700907 core = msm_dcvs_get_core(dcvs_core_id);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700908
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700909 switch (state) {
910 case MSM_DCVS_IDLE_ENTER:
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700911 stop_slack_timer(core);
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700912 ret = msm_dcvs_scm_event(core->dcvs_core_id,
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700913 MSM_DCVS_SCM_IDLE_ENTER, 0, 0, &r0, &r1);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700914 if (ret < 0 && ret != -13)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700915 __err("Error (%d) sending idle enter for %s\n",
916 ret, core->core_name);
917 break;
918
919 case MSM_DCVS_IDLE_EXIT:
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700920 ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_IDLE_EXIT,
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700921 iowaited, &timer_interval_us);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700922 if (ret)
923 __err("Error (%d) sending idle exit for %s\n",
924 ret, core->core_name);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700925 start_slack_timer(core, timer_interval_us);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700926 break;
927 }
928
929 return ret;
930}
931EXPORT_SYMBOL(msm_dcvs_idle);
932
933static int __init msm_dcvs_late_init(void)
934{
935 struct kobject *module_kobj = NULL;
936 int ret = 0;
937
938 module_kobj = kset_find_obj(module_kset, KBUILD_MODNAME);
939 if (!module_kobj) {
940 pr_err("%s: cannot find kobject for module %s\n",
941 __func__, KBUILD_MODNAME);
942 ret = -ENOENT;
943 goto err;
944 }
945
946 cores_kobj = kobject_create_and_add("cores", module_kobj);
947 if (!cores_kobj) {
948 __err("Cannot create %s kobject\n", "cores");
949 ret = -ENOMEM;
950 goto err;
951 }
952
953 debugfs_base = debugfs_create_dir("msm_dcvs", NULL);
954 if (!debugfs_base) {
955 __err("Cannot create debugfs base %s\n", "msm_dcvs");
956 ret = -ENOENT;
957 goto err;
958 }
959
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700960err:
961 if (ret) {
962 kobject_del(cores_kobj);
963 cores_kobj = NULL;
964 debugfs_remove(debugfs_base);
965 }
966
967 return ret;
968}
969late_initcall(msm_dcvs_late_init);
970
971static int __init msm_dcvs_early_init(void)
972{
973 int ret = 0;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700974 int i;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700975
976 if (!msm_dcvs_enabled) {
977 __info("Not enabled (%d)\n", msm_dcvs_enabled);
978 return 0;
979 }
980
Abhijeet Dharmapurikar3edb5de2012-09-13 11:02:03 -0700981
982 /* Only need about 32kBytes for normal operation */
983 ret = msm_dcvs_scm_init(SZ_32K);
984 if (ret) {
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700985 __err("Unable to initialize DCVS err=%d\n", ret);
Abhijeet Dharmapurikar3edb5de2012-09-13 11:02:03 -0700986 goto done;
987 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700988
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700989 for (i = 0; i < CORES_MAX; i++)
990 core_list[i].dcvs_core_id = -1;
Abhijeet Dharmapurikar3edb5de2012-09-13 11:02:03 -0700991done:
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700992 return ret;
993}
994postcore_initcall(msm_dcvs_early_init);