blob: 17adb7cb38112320e9989da303edbd2ed93ee0bc [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>
17#include <linux/mutex.h>
18#include <linux/kthread.h>
19#include <linux/kobject.h>
20#include <linux/ktime.h>
21#include <linux/hrtimer.h>
22#include <linux/slab.h>
23#include <linux/spinlock.h>
24#include <linux/stringify.h>
25#include <linux/debugfs.h>
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -070026#include <linux/msm_tsens.h>
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070027#include <asm/atomic.h>
28#include <asm/page.h>
29#include <mach/msm_dcvs.h>
30
31#define CORE_HANDLE_OFFSET (0xA0)
32#define __err(f, ...) pr_err("MSM_DCVS: %s: " f, __func__, __VA_ARGS__)
33#define __info(f, ...) pr_info("MSM_DCVS: %s: " f, __func__, __VA_ARGS__)
34#define MAX_PENDING (5)
35
36enum {
37 MSM_DCVS_DEBUG_NOTIFIER = BIT(0),
38 MSM_DCVS_DEBUG_IDLE_PULSE = BIT(1),
39 MSM_DCVS_DEBUG_FREQ_CHANGE = BIT(2),
40};
41
42struct core_attribs {
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -070043 struct kobj_attribute core_id;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070044 struct kobj_attribute idle_enabled;
45 struct kobj_attribute freq_change_enabled;
46 struct kobj_attribute actual_freq;
47 struct kobj_attribute freq_change_us;
48
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070049 struct kobj_attribute disable_pc_threshold;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -070050 struct kobj_attribute em_win_size_min_us;
51 struct kobj_attribute em_win_size_max_us;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070052 struct kobj_attribute em_max_util_pct;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -070053 struct kobj_attribute group_id;
54 struct kobj_attribute max_freq_chg_time_us;
55 struct kobj_attribute slack_mode_dynamic;
56 struct kobj_attribute slack_time_min_us;
57 struct kobj_attribute slack_time_max_us;
58 struct kobj_attribute slack_weight_thresh_pct;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070059 struct kobj_attribute ss_iobusy_conv;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -070060 struct kobj_attribute ss_win_size_min_us;
61 struct kobj_attribute ss_win_size_max_us;
62 struct kobj_attribute ss_util_pct;
63
64 struct kobj_attribute active_coeff_a;
65 struct kobj_attribute active_coeff_b;
66 struct kobj_attribute active_coeff_c;
67 struct kobj_attribute leakage_coeff_a;
68 struct kobj_attribute leakage_coeff_b;
69 struct kobj_attribute leakage_coeff_c;
70 struct kobj_attribute leakage_coeff_d;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070071
72 struct attribute_group attrib_group;
73};
74
75struct dcvs_core {
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -070076 enum msm_dcvs_core_type type;
77 /* this is the number in each type for example cpu 0,1,2 and gpu 0,1 */
78 int type_core_num;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070079 char core_name[CORE_NAME_MAX];
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070080 uint32_t actual_freq;
81 uint32_t freq_change_us;
82
83 uint32_t max_time_us; /* core param */
84
85 struct msm_dcvs_algo_param algo_param;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -070086 struct msm_dcvs_energy_curve_coeffs coeffs;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070087
88 /* private */
89 int64_t time_start;
90 struct mutex lock;
91 spinlock_t cpu_lock;
92 struct task_struct *task;
93 struct core_attribs attrib;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -070094 uint32_t dcvs_core_id;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070095 struct hrtimer timer;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070096 int32_t timer_disabled;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -070097 struct msm_dcvs_core_info *info;
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -070098 int sensor;
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -070099 int pending_freq;
Abhijeet Dharmapurikarbbb52fe2012-08-31 20:31:16 -0700100 wait_queue_head_t wait_q;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700101
102 int (*set_frequency)(int type_core_num, unsigned int freq);
103 unsigned int (*get_frequency)(int type_core_num);
104 int (*idle_enable)(int type_core_num,
Abhijeet Dharmapurikar6e9b34f2012-09-10 16:03:39 -0700105 enum msm_core_control_event event);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700106};
107
108static int msm_dcvs_debug;
109static int msm_dcvs_enabled = 1;
110module_param_named(enable, msm_dcvs_enabled, int, S_IRUGO | S_IWUSR | S_IWGRP);
111
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700112static struct dentry *debugfs_base;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700113
114static struct dcvs_core core_list[CORES_MAX];
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700115
116static struct kobject *cores_kobj;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700117
118/* Change core frequency, called with core mutex locked */
119static int __msm_dcvs_change_freq(struct dcvs_core *core)
120{
121 int ret = 0;
122 unsigned long flags = 0;
123 unsigned int requested_freq = 0;
124 unsigned int prev_freq = 0;
125 int64_t time_start = 0;
126 int64_t time_end = 0;
127 uint32_t slack_us = 0;
128 uint32_t ret1 = 0;
129
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700130 if (!core->set_frequency) {
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700131 /* Core may have unregistered or hotplugged */
132 return -ENODEV;
133 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700134 spin_lock_irqsave(&core->cpu_lock, flags);
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700135repeat:
136
137 requested_freq = core->pending_freq;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700138 time_start = core->time_start;
139 core->time_start = 0;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700140 /**
141 * Cancel the timers, we dont want the timer firing as we are
142 * changing the clock rate. Dont let idle_exit and others setup
143 * timers as well.
144 */
145 hrtimer_cancel(&core->timer);
146 core->timer_disabled = 1;
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700147 if (requested_freq == core->actual_freq)
148 goto out;
149
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700150 spin_unlock_irqrestore(&core->cpu_lock, flags);
151
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700152
153 /**
154 * Call the frequency sink driver to change the frequency
155 * We will need to get back the actual frequency in KHz and
156 * the record the time taken to change it.
157 */
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700158 ret = core->set_frequency(core->type_core_num, requested_freq);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700159 if (ret <= 0) {
160 __err("Core %s failed to set freq %u\n",
161 core->core_name, requested_freq);
Eugene Seah76af9832012-03-28 18:43:53 -0600162 /* continue to call TZ to get updated slack timer */
163 } else {
164 prev_freq = core->actual_freq;
165 core->actual_freq = ret;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700166 }
167
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700168 time_end = ktime_to_ns(ktime_get());
169 if (msm_dcvs_debug & MSM_DCVS_DEBUG_FREQ_CHANGE)
170 __info("Core %s Time end %llu Time start: %llu\n",
171 core->core_name, time_end, time_start);
172 time_end -= time_start;
173 do_div(time_end, NSEC_PER_USEC);
174 core->freq_change_us = (uint32_t)time_end;
175
176 /**
177 * Disable low power modes if the actual frequency is >
178 * disable_pc_threshold.
179 */
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700180 if (core->actual_freq > core->algo_param.disable_pc_threshold) {
181 core->idle_enable(core->type_core_num,
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700182 MSM_DCVS_DISABLE_HIGH_LATENCY_MODES);
183 if (msm_dcvs_debug & MSM_DCVS_DEBUG_IDLE_PULSE)
184 __info("Disabling LPM for %s\n", core->core_name);
185 } else if (core->actual_freq <=
186 core->algo_param.disable_pc_threshold) {
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700187 core->idle_enable(core->type_core_num,
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700188 MSM_DCVS_ENABLE_HIGH_LATENCY_MODES);
189 if (msm_dcvs_debug & MSM_DCVS_DEBUG_IDLE_PULSE)
190 __info("Enabling LPM for %s\n", core->core_name);
191 }
192
193 /**
194 * Update algorithm with new freq and time taken to change
195 * to this frequency and that will get us the new slack
196 * timer
197 */
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700198 ret = msm_dcvs_scm_event(core->dcvs_core_id,
199 MSM_DCVS_SCM_CLOCK_FREQ_UPDATE,
200 core->actual_freq, (uint32_t)time_end,
201 &slack_us, &ret1);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700202 if (!ret) {
203 /* Reset the slack timer */
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700204 if (slack_us) {
205 core->timer_disabled = 0;
206 ret = hrtimer_start(&core->timer,
207 ktime_set(0, slack_us * 1000),
208 HRTIMER_MODE_REL_PINNED);
209 if (ret)
210 __err("Failed to register timer for core %s\n",
211 core->core_name);
212 }
213 } else {
214 __err("Error sending core (%s) freq change (%u)\n",
215 core->core_name, core->actual_freq);
216 }
217
218 if (msm_dcvs_debug & MSM_DCVS_DEBUG_FREQ_CHANGE)
219 __info("Freq %u requested for core %s (actual %u prev %u) "
220 "change time %u us slack time %u us\n",
221 requested_freq, core->core_name,
222 core->actual_freq, prev_freq,
Eugene Seah76af9832012-03-28 18:43:53 -0600223 core->freq_change_us, slack_us);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700224
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700225 spin_lock_irqsave(&core->cpu_lock, flags);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700226 /**
227 * By the time we are done with freq changes, we could be asked to
228 * change again. Check before exiting.
229 */
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700230 if (core->pending_freq)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700231 goto repeat;
232
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700233
234out: /* should always be jumped to with the spin_lock held */
235 core->pending_freq = 0;
236 spin_unlock_irqrestore(&core->cpu_lock, flags);
237
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700238 return ret;
239}
240
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700241static int __msm_dcvs_report_temp(struct dcvs_core *core)
242{
243 struct msm_dcvs_core_info *info = core->info;
244 struct tsens_device tsens_dev;
245 int ret;
246 unsigned long temp = 0;
247
248 tsens_dev.sensor_num = core->sensor;
249 ret = tsens_get_temp(&tsens_dev, &temp);
250 if (!ret) {
251 tsens_dev.sensor_num = 0;
252 ret = tsens_get_temp(&tsens_dev, &temp);
253 if (!ret)
254 return -ENODEV;
255 }
256
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700257 ret = msm_dcvs_scm_set_power_params(core->dcvs_core_id,
258 &info->power_param,
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700259 &info->freq_tbl[0], &core->coeffs);
260 return ret;
261}
262
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700263static int msm_dcvs_do_freq(void *data)
264{
265 struct dcvs_core *core = (struct dcvs_core *)data;
266 static struct sched_param param = {.sched_priority = MAX_RT_PRIO - 1};
267
268 sched_setscheduler(current, SCHED_FIFO, &param);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700269
270 while (!kthread_should_stop()) {
Abhijeet Dharmapurikarbbb52fe2012-08-31 20:31:16 -0700271 wait_event(core->wait_q, !(core->pending_freq == 0 ||
272 core->pending_freq == -1) ||
273 kthread_should_stop());
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700274
275 if (kthread_should_stop())
276 break;
277
Abhijeet Dharmapurikarbbb52fe2012-08-31 20:31:16 -0700278 mutex_lock(&core->lock);
279 __msm_dcvs_change_freq(core);
280 __msm_dcvs_report_temp(core);
281 mutex_unlock(&core->lock);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700282 }
283
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700284 return 0;
285}
286
287static int msm_dcvs_update_freq(struct dcvs_core *core,
Eugene Seah76af9832012-03-28 18:43:53 -0600288 enum msm_dcvs_scm_event event, uint32_t param0,
289 uint32_t *ret1, int *freq_changed)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700290{
291 int ret = 0;
292 unsigned long flags = 0;
293 uint32_t new_freq = 0;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700294
295 spin_lock_irqsave(&core->cpu_lock, flags);
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700296 ret = msm_dcvs_scm_event(core->dcvs_core_id, event, param0,
Eugene Seah76af9832012-03-28 18:43:53 -0600297 core->actual_freq, &new_freq, ret1);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700298 if (ret) {
Jeff Ohlstein4aa4a2b2012-06-28 19:03:49 -0700299 if (ret == -13)
300 ret = 0;
301 else
302 __err("Error (%d) sending SCM event %d for core %s\n",
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700303 ret, event, core->core_name);
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700304 goto out;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700305 }
306
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700307 if (core->actual_freq != new_freq && core->pending_freq != new_freq) {
308 core->pending_freq = new_freq;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700309 core->time_start = ktime_to_ns(ktime_get());
310
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700311 if (core->task)
Abhijeet Dharmapurikarbbb52fe2012-08-31 20:31:16 -0700312 wake_up(&core->wait_q);
Eugene Seah76af9832012-03-28 18:43:53 -0600313 } else {
314 if (freq_changed)
315 *freq_changed = 0;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700316 }
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700317out:
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700318 spin_unlock_irqrestore(&core->cpu_lock, flags);
319
320 return ret;
321}
322
323static enum hrtimer_restart msm_dcvs_core_slack_timer(struct hrtimer *timer)
324{
325 int ret = 0;
326 struct dcvs_core *core = container_of(timer, struct dcvs_core, timer);
Eugene Seah76af9832012-03-28 18:43:53 -0600327 uint32_t ret1;
328 uint32_t ret2;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700329
330 if (msm_dcvs_debug & MSM_DCVS_DEBUG_FREQ_CHANGE)
331 __info("Slack timer fired for core %s\n", core->core_name);
332
333 /**
334 * Timer expired, notify TZ
335 * Dont care about the third arg.
336 */
Eugene Seah76af9832012-03-28 18:43:53 -0600337 ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_QOS_TIMER_EXPIRED, 0,
338 &ret1, &ret2);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700339 if (ret)
340 __err("Timer expired for core %s but failed to notify.\n",
341 core->core_name);
342
343 return HRTIMER_NORESTART;
344}
345
346/* Helper functions and macros for sysfs nodes for a core */
347#define CORE_FROM_ATTRIBS(attr, name) \
348 container_of(container_of(attr, struct core_attribs, name), \
349 struct dcvs_core, attrib);
350
351#define DCVS_PARAM_SHOW(_name, v) \
352static ssize_t msm_dcvs_attr_##_name##_show(struct kobject *kobj, \
353 struct kobj_attribute *attr, char *buf) \
354{ \
355 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
356 return snprintf(buf, PAGE_SIZE, "%d\n", v); \
357}
358
359#define DCVS_ALGO_PARAM(_name) \
360static ssize_t msm_dcvs_attr_##_name##_show(struct kobject *kobj,\
361 struct kobj_attribute *attr, char *buf) \
362{ \
363 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
364 return snprintf(buf, PAGE_SIZE, "%d\n", core->algo_param._name); \
365} \
366static ssize_t msm_dcvs_attr_##_name##_store(struct kobject *kobj, \
367 struct kobj_attribute *attr, const char *buf, size_t count) \
368{ \
369 int ret = 0; \
370 uint32_t val = 0; \
371 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
372 mutex_lock(&core->lock); \
373 ret = kstrtouint(buf, 10, &val); \
374 if (ret) { \
375 __err("Invalid input %s for %s\n", buf, __stringify(_name));\
376 } else { \
377 uint32_t old_val = core->algo_param._name; \
378 core->algo_param._name = val; \
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700379 ret = msm_dcvs_scm_set_algo_params(core->dcvs_core_id, \
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700380 &core->algo_param); \
381 if (ret) { \
382 core->algo_param._name = old_val; \
383 __err("Error(%d) in setting %d for algo param %s\n",\
384 ret, val, __stringify(_name)); \
385 } \
386 } \
387 mutex_unlock(&core->lock); \
388 return count; \
389}
390
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700391#define DCVS_ENERGY_PARAM(_name) \
392static ssize_t msm_dcvs_attr_##_name##_show(struct kobject *kobj,\
393 struct kobj_attribute *attr, char *buf) \
394{ \
395 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
396 return snprintf(buf, PAGE_SIZE, "%d\n", core->coeffs._name); \
397} \
398static ssize_t msm_dcvs_attr_##_name##_store(struct kobject *kobj, \
399 struct kobj_attribute *attr, const char *buf, size_t count) \
400{ \
401 int ret = 0; \
402 int32_t val = 0; \
403 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
404 mutex_lock(&core->lock); \
405 ret = kstrtoint(buf, 10, &val); \
406 if (ret) { \
407 __err("Invalid input %s for %s\n", buf, __stringify(_name));\
408 } else { \
409 int32_t old_val = core->coeffs._name; \
410 core->coeffs._name = val; \
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700411 ret = msm_dcvs_scm_set_power_params(core->dcvs_core_id, \
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700412 &core->info->power_param, &core->info->freq_tbl[0], \
413 &core->coeffs); \
414 if (ret) { \
415 core->coeffs._name = old_val; \
416 __err("Error(%d) in setting %d for coeffs param %s\n",\
417 ret, val, __stringify(_name)); \
418 } \
419 } \
420 mutex_unlock(&core->lock); \
421 return count; \
422}
423
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700424#define DCVS_RO_ATTRIB(i, _name) \
425 core->attrib._name.attr.name = __stringify(_name); \
426 core->attrib._name.attr.mode = S_IRUGO; \
427 core->attrib._name.show = msm_dcvs_attr_##_name##_show; \
428 core->attrib._name.store = NULL; \
429 core->attrib.attrib_group.attrs[i] = &core->attrib._name.attr;
430
431#define DCVS_RW_ATTRIB(i, _name) \
432 core->attrib._name.attr.name = __stringify(_name); \
433 core->attrib._name.attr.mode = S_IRUGO | S_IWUSR; \
434 core->attrib._name.show = msm_dcvs_attr_##_name##_show; \
435 core->attrib._name.store = msm_dcvs_attr_##_name##_store; \
436 core->attrib.attrib_group.attrs[i] = &core->attrib._name.attr;
437
438/**
439 * Function declarations for different attributes.
440 * Gets used when setting the attribute show and store parameters.
441 */
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700442DCVS_PARAM_SHOW(core_id, core->dcvs_core_id)
443DCVS_PARAM_SHOW(idle_enabled, (core->idle_enable != NULL))
444DCVS_PARAM_SHOW(freq_change_enabled, (core->set_frequency != NULL))
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700445DCVS_PARAM_SHOW(actual_freq, (core->actual_freq))
446DCVS_PARAM_SHOW(freq_change_us, (core->freq_change_us))
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700447
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700448DCVS_ALGO_PARAM(disable_pc_threshold)
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700449DCVS_ALGO_PARAM(em_win_size_min_us)
450DCVS_ALGO_PARAM(em_win_size_max_us)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700451DCVS_ALGO_PARAM(em_max_util_pct)
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700452DCVS_ALGO_PARAM(group_id)
453DCVS_ALGO_PARAM(max_freq_chg_time_us)
454DCVS_ALGO_PARAM(slack_mode_dynamic)
455DCVS_ALGO_PARAM(slack_time_min_us)
456DCVS_ALGO_PARAM(slack_time_max_us)
457DCVS_ALGO_PARAM(slack_weight_thresh_pct)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700458DCVS_ALGO_PARAM(ss_iobusy_conv)
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700459DCVS_ALGO_PARAM(ss_win_size_min_us)
460DCVS_ALGO_PARAM(ss_win_size_max_us)
461DCVS_ALGO_PARAM(ss_util_pct)
462
463DCVS_ENERGY_PARAM(active_coeff_a)
464DCVS_ENERGY_PARAM(active_coeff_b)
465DCVS_ENERGY_PARAM(active_coeff_c)
466DCVS_ENERGY_PARAM(leakage_coeff_a)
467DCVS_ENERGY_PARAM(leakage_coeff_b)
468DCVS_ENERGY_PARAM(leakage_coeff_c)
469DCVS_ENERGY_PARAM(leakage_coeff_d)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700470
471static int msm_dcvs_setup_core_sysfs(struct dcvs_core *core)
472{
473 int ret = 0;
474 struct kobject *core_kobj = NULL;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700475 const int attr_count = 27;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700476
477 BUG_ON(!cores_kobj);
478
479 core->attrib.attrib_group.attrs =
480 kzalloc(attr_count * sizeof(struct attribute *), GFP_KERNEL);
481
482 if (!core->attrib.attrib_group.attrs) {
483 ret = -ENOMEM;
484 goto done;
485 }
486
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700487
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700488 DCVS_RO_ATTRIB(0, core_id);
489 DCVS_RO_ATTRIB(1, idle_enabled);
490 DCVS_RO_ATTRIB(2, freq_change_enabled);
491 DCVS_RO_ATTRIB(3, actual_freq);
492 DCVS_RO_ATTRIB(4, freq_change_us);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700493
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700494 DCVS_RW_ATTRIB(5, disable_pc_threshold);
495 DCVS_RW_ATTRIB(6, em_win_size_min_us);
496 DCVS_RW_ATTRIB(7, em_win_size_max_us);
497 DCVS_RW_ATTRIB(8, em_max_util_pct);
498 DCVS_RW_ATTRIB(9, group_id);
499 DCVS_RW_ATTRIB(10, max_freq_chg_time_us);
500 DCVS_RW_ATTRIB(11, slack_mode_dynamic);
501 DCVS_RW_ATTRIB(12, slack_time_min_us);
502 DCVS_RW_ATTRIB(13, slack_time_max_us);
503 DCVS_RW_ATTRIB(14, slack_weight_thresh_pct);
504 DCVS_RW_ATTRIB(15, ss_iobusy_conv);
505 DCVS_RW_ATTRIB(16, ss_win_size_min_us);
506 DCVS_RW_ATTRIB(17, ss_win_size_max_us);
507 DCVS_RW_ATTRIB(18, ss_util_pct);
508
509 DCVS_RW_ATTRIB(19, active_coeff_a);
510 DCVS_RW_ATTRIB(20, active_coeff_b);
511 DCVS_RW_ATTRIB(21, active_coeff_c);
512 DCVS_RW_ATTRIB(22, leakage_coeff_a);
513 DCVS_RW_ATTRIB(23, leakage_coeff_b);
514 DCVS_RW_ATTRIB(24, leakage_coeff_c);
515 DCVS_RW_ATTRIB(25, leakage_coeff_d);
516
517 core->attrib.attrib_group.attrs[26] = NULL;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700518
519 core_kobj = kobject_create_and_add(core->core_name, cores_kobj);
520 if (!core_kobj) {
521 ret = -ENOMEM;
522 goto done;
523 }
524
525 ret = sysfs_create_group(core_kobj, &core->attrib.attrib_group);
526 if (ret)
527 __err("Cannot create core %s attr group\n", core->core_name);
528 else if (msm_dcvs_debug & MSM_DCVS_DEBUG_NOTIFIER)
529 __info("Setting up attributes for core %s\n", core->core_name);
530
531done:
532 if (ret) {
533 kfree(core->attrib.attrib_group.attrs);
534 kobject_del(core_kobj);
535 }
536
537 return ret;
538}
539
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700540/* Return the core and initialize non platform data specific numbers in it */
541static struct dcvs_core *msm_dcvs_add_core(enum msm_dcvs_core_type type,
542 int num)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700543{
544 struct dcvs_core *core = NULL;
545 int i;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700546 char name[CORE_NAME_MAX];
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700547
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700548 switch (type) {
549 case MSM_DCVS_CORE_TYPE_CPU:
550 i = CPU_OFFSET + num;
551 BUG_ON(i >= GPU_OFFSET);
552 snprintf(name, CORE_NAME_MAX, "cpu%d", num);
553 break;
554 case MSM_DCVS_CORE_TYPE_GPU:
555 i = GPU_OFFSET + num;
556 BUG_ON(i >= CORES_MAX);
557 snprintf(name, CORE_NAME_MAX, "gpu%d", num);
558 break;
559 default:
560 return NULL;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700561 }
562
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700563 core = &core_list[i];
564 core->dcvs_core_id = i;
Abhijeet Dharmapurikar2ebc0fe2012-09-12 14:05:13 -0700565 strlcpy(core->core_name, name, CORE_NAME_MAX);
566 mutex_init(&core->lock);
567 spin_lock_init(&core->cpu_lock);
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700568 hrtimer_init(&core->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_PINNED);
Abhijeet Dharmapurikar2ebc0fe2012-09-12 14:05:13 -0700569 core->timer.function = msm_dcvs_core_slack_timer;
Abhijeet Dharmapurikar2ebc0fe2012-09-12 14:05:13 -0700570 return core;
571}
572
573/* Return the core if found or add to list if @add_to_list is true */
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700574static struct dcvs_core *msm_dcvs_get_core(int offset)
Abhijeet Dharmapurikar2ebc0fe2012-09-12 14:05:13 -0700575{
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700576 /* if the handle is still not set bug */
577 BUG_ON(core_list[offset].dcvs_core_id == -1);
578 return &core_list[offset];
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700579}
580
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700581
582int msm_dcvs_register_core(
583 enum msm_dcvs_core_type type,
584 int type_core_num,
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700585 struct msm_dcvs_core_info *info,
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700586 int (*set_frequency)(int type_core_num, unsigned int freq),
587 unsigned int (*get_frequency)(int type_core_num),
588 int (*idle_enable)(int type_core_num,
Abhijeet Dharmapurikar6e9b34f2012-09-10 16:03:39 -0700589 enum msm_core_control_event event),
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700590 int sensor)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700591{
592 int ret = -EINVAL;
593 struct dcvs_core *core = NULL;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700594 uint32_t ret1;
595 uint32_t ret2;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700596
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700597 core = msm_dcvs_add_core(type, type_core_num);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700598 if (!core)
599 return ret;
600
601 mutex_lock(&core->lock);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700602
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700603 core->type = type;
604 core->type_core_num = type_core_num;
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700605 core->set_frequency = set_frequency;
606 core->get_frequency = get_frequency;
Abhijeet Dharmapurikar6e9b34f2012-09-10 16:03:39 -0700607 core->idle_enable = idle_enable;
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700608
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700609 core->info = info;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700610 memcpy(&core->algo_param, &info->algo_param,
611 sizeof(struct msm_dcvs_algo_param));
612
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700613 memcpy(&core->coeffs, &info->energy_coeffs,
614 sizeof(struct msm_dcvs_energy_curve_coeffs));
615
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700616 /*
617 * The tz expects cpu0 to represent bit 0 in the mask, however the
618 * dcvs_core_id needs to start from 1, dcvs_core_id = 0 is used to
619 * indicate that this request is not associated with any core.
620 * mpdecision
621 */
622 info->core_param.core_bitmask_id
623 = 1 << (core->dcvs_core_id - CPU_OFFSET);
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700624 core->sensor = sensor;
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700625
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700626 ret = msm_dcvs_scm_register_core(core->dcvs_core_id, &info->core_param);
627 if (ret) {
628 __err("%s: scm register core fail handle = %d ret = %d\n",
629 __func__, core->dcvs_core_id, ret);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700630 goto bail;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700631 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700632
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700633 ret = msm_dcvs_scm_set_algo_params(core->dcvs_core_id,
634 &info->algo_param);
635 if (ret) {
636 __err("%s: scm algo params failed ret = %d\n", __func__, ret);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700637 goto bail;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700638 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700639
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700640 ret = msm_dcvs_scm_set_power_params(core->dcvs_core_id,
641 &info->power_param,
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700642 &info->freq_tbl[0], &core->coeffs);
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700643 if (ret) {
644 __err("%s: scm power params failed ret = %d\n", __func__, ret);
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700645 goto bail;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700646 }
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700647
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700648 ret = msm_dcvs_scm_event(core->dcvs_core_id, MSM_DCVS_SCM_CORE_ONLINE,
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700649 core->actual_freq, 0, &ret1, &ret2);
650 if (ret)
651 goto bail;
652
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700653 ret = msm_dcvs_setup_core_sysfs(core);
654 if (ret) {
655 __err("Unable to setup core %s sysfs\n", core->core_name);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700656 goto bail;
657 }
Abhijeet Dharmapurikarbbb52fe2012-08-31 20:31:16 -0700658 init_waitqueue_head(&core->wait_q);
659 core->task = kthread_run(msm_dcvs_do_freq, (void *)core,
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700660 "msm_dcvs/%d", core->dcvs_core_id);
661 ret = core->dcvs_core_id;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700662 mutex_unlock(&core->lock);
663 return ret;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700664bail:
665 mutex_unlock(&core->lock);
666 core->dcvs_core_id = -1;
667 return -EINVAL;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700668}
669EXPORT_SYMBOL(msm_dcvs_register_core);
670
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700671void msm_dcvs_update_limits(int dcvs_core_id)
Abhijeet Dharmapurikarc43f0db2012-08-31 20:42:53 -0700672{
673 struct dcvs_core *core;
674
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700675 if (dcvs_core_id < CPU_OFFSET || dcvs_core_id > CORES_MAX) {
676 __err("%s invalid dcvs_core_id = %d returning -EINVAL\n",
677 __func__, dcvs_core_id);
Abhijeet Dharmapurikarc43f0db2012-08-31 20:42:53 -0700678 return;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700679 }
Abhijeet Dharmapurikarc43f0db2012-08-31 20:42:53 -0700680
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700681 core = msm_dcvs_get_core(dcvs_core_id);
682 core->actual_freq = core->get_frequency(core->type_core_num);
Abhijeet Dharmapurikarc43f0db2012-08-31 20:42:53 -0700683}
684
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700685int msm_dcvs_freq_sink_start(int dcvs_core_id)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700686{
687 int ret = -EINVAL;
688 struct dcvs_core *core = NULL;
Eugene Seah76af9832012-03-28 18:43:53 -0600689 uint32_t ret1;
690 uint32_t ret2;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700691
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700692 if (dcvs_core_id < CPU_OFFSET || dcvs_core_id > CORES_MAX) {
693 __err("%s invalid dcvs_core_id = %d returning -EINVAL\n",
694 __func__, dcvs_core_id);
695 return -EINVAL;
696 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700697
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700698 core = msm_dcvs_get_core(dcvs_core_id);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700699 if (!core)
700 return ret;
701
702 mutex_lock(&core->lock);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700703 if (IS_ERR(core->task)) {
704 mutex_unlock(&core->lock);
705 return -EFAULT;
706 }
707
708 if (msm_dcvs_debug & MSM_DCVS_DEBUG_IDLE_PULSE)
709 __info("Enabling idle pulse for %s\n", core->core_name);
710
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700711 core->actual_freq = core->get_frequency(core->type_core_num);
712 /* Notify TZ to start receiving idle info for the core */
713 ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_DCVS_ENABLE, 1,
Eugene Seah76af9832012-03-28 18:43:53 -0600714 &ret1, &ret2);
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700715 core->idle_enable(core->type_core_num, MSM_DCVS_ENABLE_IDLE_PULSE);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700716
717 mutex_unlock(&core->lock);
718
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700719 return core->dcvs_core_id;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700720}
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700721EXPORT_SYMBOL(msm_dcvs_freq_sink_start);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700722
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700723int msm_dcvs_freq_sink_stop(int dcvs_core_id)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700724{
725 int ret = -EINVAL;
726 struct dcvs_core *core = NULL;
Eugene Seah76af9832012-03-28 18:43:53 -0600727 uint32_t ret1;
728 uint32_t ret2;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700729
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700730 if (dcvs_core_id < 0 || dcvs_core_id > CORES_MAX) {
731 pr_err("%s invalid dcvs_core_id = %d returning -EINVAL\n",
732 __func__, dcvs_core_id);
733 return -EINVAL;
734 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700735
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700736 core = msm_dcvs_get_core(dcvs_core_id);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700737 if (!core)
738 return ret;
739
740 mutex_lock(&core->lock);
741 if (msm_dcvs_debug & MSM_DCVS_DEBUG_IDLE_PULSE)
742 __info("Disabling idle pulse for %s\n", core->core_name);
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700743
744 core->idle_enable(core->type_core_num, MSM_DCVS_DISABLE_IDLE_PULSE);
745 /* Notify TZ to stop receiving idle info for the core */
746 ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_DCVS_ENABLE, 0,
747 &ret1, &ret2);
748 hrtimer_cancel(&core->timer);
749 core->idle_enable(core->type_core_num,
750 MSM_DCVS_ENABLE_HIGH_LATENCY_MODES);
751 if (msm_dcvs_debug & MSM_DCVS_DEBUG_IDLE_PULSE)
752 __info("Enabling LPM for %s\n", core->core_name);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700753 mutex_unlock(&core->lock);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700754
755 return 0;
756}
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700757EXPORT_SYMBOL(msm_dcvs_freq_sink_stop);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700758
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700759int msm_dcvs_idle(int dcvs_core_id, enum msm_core_idle_state state,
760 uint32_t iowaited)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700761{
762 int ret = 0;
763 struct dcvs_core *core = NULL;
Eugene Seah76af9832012-03-28 18:43:53 -0600764 uint32_t timer_interval_us = 0;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700765 uint32_t r0, r1;
Eugene Seah76af9832012-03-28 18:43:53 -0600766 uint32_t freq_changed = 0;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700767
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700768 if (dcvs_core_id < CPU_OFFSET || dcvs_core_id > CORES_MAX) {
769 pr_err("invalid dcvs_core_id = %d ret -EINVAL\n", dcvs_core_id);
770 return -EINVAL;
771 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700772
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700773 core = msm_dcvs_get_core(dcvs_core_id);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700774
775 if (msm_dcvs_debug & MSM_DCVS_DEBUG_IDLE_PULSE)
776 __info("Core %s idle state %d\n", core->core_name, state);
777
778 switch (state) {
779 case MSM_DCVS_IDLE_ENTER:
780 hrtimer_cancel(&core->timer);
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700781 ret = msm_dcvs_scm_event(core->dcvs_core_id,
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700782 MSM_DCVS_SCM_IDLE_ENTER, 0, 0, &r0, &r1);
783 if (ret)
784 __err("Error (%d) sending idle enter for %s\n",
785 ret, core->core_name);
786 break;
787
788 case MSM_DCVS_IDLE_EXIT:
789 hrtimer_cancel(&core->timer);
790 ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_IDLE_EXIT,
Eugene Seah76af9832012-03-28 18:43:53 -0600791 iowaited, &timer_interval_us, &freq_changed);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700792 if (ret)
793 __err("Error (%d) sending idle exit for %s\n",
794 ret, core->core_name);
Eugene Seah76af9832012-03-28 18:43:53 -0600795 /* only start slack timer if change_freq won't */
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700796 if (freq_changed)
Eugene Seah76af9832012-03-28 18:43:53 -0600797 break;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700798 if (timer_interval_us && !core->timer_disabled) {
799 ret = hrtimer_start(&core->timer,
800 ktime_set(0, timer_interval_us * 1000),
801 HRTIMER_MODE_REL_PINNED);
Eugene Seah76af9832012-03-28 18:43:53 -0600802
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700803 if (ret)
804 __err("Failed to register timer for core %s\n",
Eugene Seah76af9832012-03-28 18:43:53 -0600805 core->core_name);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700806 }
807 break;
808 }
809
810 return ret;
811}
812EXPORT_SYMBOL(msm_dcvs_idle);
813
814static int __init msm_dcvs_late_init(void)
815{
816 struct kobject *module_kobj = NULL;
817 int ret = 0;
818
819 module_kobj = kset_find_obj(module_kset, KBUILD_MODNAME);
820 if (!module_kobj) {
821 pr_err("%s: cannot find kobject for module %s\n",
822 __func__, KBUILD_MODNAME);
823 ret = -ENOENT;
824 goto err;
825 }
826
827 cores_kobj = kobject_create_and_add("cores", module_kobj);
828 if (!cores_kobj) {
829 __err("Cannot create %s kobject\n", "cores");
830 ret = -ENOMEM;
831 goto err;
832 }
833
834 debugfs_base = debugfs_create_dir("msm_dcvs", NULL);
835 if (!debugfs_base) {
836 __err("Cannot create debugfs base %s\n", "msm_dcvs");
837 ret = -ENOENT;
838 goto err;
839 }
840
841 if (!debugfs_create_u32("debug_mask", S_IRUGO | S_IWUSR,
842 debugfs_base, &msm_dcvs_debug)) {
843 __err("Cannot create debugfs entry %s\n", "debug_mask");
844 ret = -ENOMEM;
845 goto err;
846 }
847
848err:
849 if (ret) {
850 kobject_del(cores_kobj);
851 cores_kobj = NULL;
852 debugfs_remove(debugfs_base);
853 }
854
855 return ret;
856}
857late_initcall(msm_dcvs_late_init);
858
859static int __init msm_dcvs_early_init(void)
860{
861 int ret = 0;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700862 int i;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700863
864 if (!msm_dcvs_enabled) {
865 __info("Not enabled (%d)\n", msm_dcvs_enabled);
866 return 0;
867 }
868
869 ret = msm_dcvs_scm_init(10 * 1024);
870 if (ret)
871 __err("Unable to initialize DCVS err=%d\n", ret);
872
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700873 for (i = 0; i < CORES_MAX; i++)
874 core_list[i].dcvs_core_id = -1;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700875 return ret;
876}
877postcore_initcall(msm_dcvs_early_init);