blob: 0c158de57ac85e5f957fd83c458625a7d20c89d0 [file] [log] [blame]
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
2 *
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>
26#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
35enum {
36 MSM_DCVS_DEBUG_NOTIFIER = BIT(0),
37 MSM_DCVS_DEBUG_IDLE_PULSE = BIT(1),
38 MSM_DCVS_DEBUG_FREQ_CHANGE = BIT(2),
39};
40
41struct core_attribs {
42 struct kobj_attribute idle_enabled;
43 struct kobj_attribute freq_change_enabled;
44 struct kobj_attribute actual_freq;
45 struct kobj_attribute freq_change_us;
46
47 struct kobj_attribute max_time_us;
48
49 struct kobj_attribute slack_time_us;
50 struct kobj_attribute scale_slack_time;
51 struct kobj_attribute scale_slack_time_pct;
52 struct kobj_attribute disable_pc_threshold;
53 struct kobj_attribute em_window_size;
54 struct kobj_attribute em_max_util_pct;
55 struct kobj_attribute ss_window_size;
56 struct kobj_attribute ss_util_pct;
57 struct kobj_attribute ss_iobusy_conv;
58
59 struct attribute_group attrib_group;
60};
61
62struct dcvs_core {
63 char core_name[CORE_NAME_MAX];
64 uint32_t new_freq[MAX_PENDING];
65 uint32_t actual_freq;
66 uint32_t freq_change_us;
67
68 uint32_t max_time_us; /* core param */
69
70 struct msm_dcvs_algo_param algo_param;
71 struct msm_dcvs_idle *idle_driver;
72 struct msm_dcvs_freq *freq_driver;
73
74 /* private */
75 int64_t time_start;
76 struct mutex lock;
77 spinlock_t cpu_lock;
78 struct task_struct *task;
79 struct core_attribs attrib;
80 uint32_t handle;
81 uint32_t group_id;
82 uint32_t freq_pending;
83 struct hrtimer timer;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070084 int32_t timer_disabled;
Eugene Seah76af9832012-03-28 18:43:53 -060085 /* track if kthread for change_freq is active */
86 int32_t change_freq_activated;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070087};
88
89static int msm_dcvs_debug;
90static int msm_dcvs_enabled = 1;
91module_param_named(enable, msm_dcvs_enabled, int, S_IRUGO | S_IWUSR | S_IWGRP);
92
93static struct dentry *debugfs_base;
94
95static struct dcvs_core core_list[CORES_MAX];
96static DEFINE_MUTEX(core_list_lock);
97
98static struct kobject *cores_kobj;
99static struct dcvs_core *core_handles[CORES_MAX];
100
101/* Change core frequency, called with core mutex locked */
102static int __msm_dcvs_change_freq(struct dcvs_core *core)
103{
104 int ret = 0;
105 unsigned long flags = 0;
106 unsigned int requested_freq = 0;
107 unsigned int prev_freq = 0;
108 int64_t time_start = 0;
109 int64_t time_end = 0;
110 uint32_t slack_us = 0;
111 uint32_t ret1 = 0;
112
113 if (!core->freq_driver || !core->freq_driver->set_frequency) {
114 /* Core may have unregistered or hotplugged */
115 return -ENODEV;
116 }
117repeat:
118 spin_lock_irqsave(&core->cpu_lock, flags);
119 if (unlikely(!core->freq_pending)) {
120 spin_unlock_irqrestore(&core->cpu_lock, flags);
121 return ret;
122 }
123 requested_freq = core->new_freq[core->freq_pending - 1];
124 if (unlikely(core->freq_pending > 1) &&
125 (msm_dcvs_debug & MSM_DCVS_DEBUG_FREQ_CHANGE)) {
126 int i;
127 for (i = 0; i < core->freq_pending - 1; i++) {
128 __info("Core %s missing freq %u\n",
129 core->core_name, core->new_freq[i]);
130 }
131 }
132 time_start = core->time_start;
133 core->time_start = 0;
134 core->freq_pending = 0;
135 /**
136 * Cancel the timers, we dont want the timer firing as we are
137 * changing the clock rate. Dont let idle_exit and others setup
138 * timers as well.
139 */
140 hrtimer_cancel(&core->timer);
141 core->timer_disabled = 1;
142 spin_unlock_irqrestore(&core->cpu_lock, flags);
143
144 if (requested_freq == core->actual_freq)
145 return ret;
146
147 /**
148 * Call the frequency sink driver to change the frequency
149 * We will need to get back the actual frequency in KHz and
150 * the record the time taken to change it.
151 */
152 ret = core->freq_driver->set_frequency(core->freq_driver,
153 requested_freq);
154 if (ret <= 0) {
155 __err("Core %s failed to set freq %u\n",
156 core->core_name, requested_freq);
Eugene Seah76af9832012-03-28 18:43:53 -0600157 /* continue to call TZ to get updated slack timer */
158 } else {
159 prev_freq = core->actual_freq;
160 core->actual_freq = ret;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700161 }
162
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700163 time_end = ktime_to_ns(ktime_get());
164 if (msm_dcvs_debug & MSM_DCVS_DEBUG_FREQ_CHANGE)
165 __info("Core %s Time end %llu Time start: %llu\n",
166 core->core_name, time_end, time_start);
167 time_end -= time_start;
168 do_div(time_end, NSEC_PER_USEC);
169 core->freq_change_us = (uint32_t)time_end;
170
171 /**
172 * Disable low power modes if the actual frequency is >
173 * disable_pc_threshold.
174 */
175 if (core->actual_freq >
176 core->algo_param.disable_pc_threshold) {
177 core->idle_driver->enable(core->idle_driver,
178 MSM_DCVS_DISABLE_HIGH_LATENCY_MODES);
179 if (msm_dcvs_debug & MSM_DCVS_DEBUG_IDLE_PULSE)
180 __info("Disabling LPM for %s\n", core->core_name);
181 } else if (core->actual_freq <=
182 core->algo_param.disable_pc_threshold) {
183 core->idle_driver->enable(core->idle_driver,
184 MSM_DCVS_ENABLE_HIGH_LATENCY_MODES);
185 if (msm_dcvs_debug & MSM_DCVS_DEBUG_IDLE_PULSE)
186 __info("Enabling LPM for %s\n", core->core_name);
187 }
188
189 /**
190 * Update algorithm with new freq and time taken to change
191 * to this frequency and that will get us the new slack
192 * timer
193 */
194 ret = msm_dcvs_scm_event(core->handle, MSM_DCVS_SCM_CLOCK_FREQ_UPDATE,
195 core->actual_freq, (uint32_t)time_end, &slack_us, &ret1);
196 if (!ret) {
197 /* Reset the slack timer */
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700198 if (slack_us) {
199 core->timer_disabled = 0;
200 ret = hrtimer_start(&core->timer,
201 ktime_set(0, slack_us * 1000),
202 HRTIMER_MODE_REL_PINNED);
203 if (ret)
204 __err("Failed to register timer for core %s\n",
205 core->core_name);
206 }
207 } else {
208 __err("Error sending core (%s) freq change (%u)\n",
209 core->core_name, core->actual_freq);
210 }
211
212 if (msm_dcvs_debug & MSM_DCVS_DEBUG_FREQ_CHANGE)
213 __info("Freq %u requested for core %s (actual %u prev %u) "
214 "change time %u us slack time %u us\n",
215 requested_freq, core->core_name,
216 core->actual_freq, prev_freq,
Eugene Seah76af9832012-03-28 18:43:53 -0600217 core->freq_change_us, slack_us);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700218
219 /**
220 * By the time we are done with freq changes, we could be asked to
221 * change again. Check before exiting.
222 */
223 if (core->freq_pending)
224 goto repeat;
225
Eugene Seah76af9832012-03-28 18:43:53 -0600226 core->change_freq_activated = 0;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700227 return ret;
228}
229
230static int msm_dcvs_do_freq(void *data)
231{
232 struct dcvs_core *core = (struct dcvs_core *)data;
233 static struct sched_param param = {.sched_priority = MAX_RT_PRIO - 1};
234
235 sched_setscheduler(current, SCHED_FIFO, &param);
236 set_current_state(TASK_UNINTERRUPTIBLE);
237
238 while (!kthread_should_stop()) {
239 mutex_lock(&core->lock);
240 __msm_dcvs_change_freq(core);
241 mutex_unlock(&core->lock);
242
243 schedule();
244
245 if (kthread_should_stop())
246 break;
247
248 set_current_state(TASK_UNINTERRUPTIBLE);
249 }
250
251 __set_current_state(TASK_RUNNING);
252
253 return 0;
254}
255
256static int msm_dcvs_update_freq(struct dcvs_core *core,
Eugene Seah76af9832012-03-28 18:43:53 -0600257 enum msm_dcvs_scm_event event, uint32_t param0,
258 uint32_t *ret1, int *freq_changed)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700259{
260 int ret = 0;
261 unsigned long flags = 0;
262 uint32_t new_freq = 0;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700263
264 spin_lock_irqsave(&core->cpu_lock, flags);
265 ret = msm_dcvs_scm_event(core->handle, event, param0,
Eugene Seah76af9832012-03-28 18:43:53 -0600266 core->actual_freq, &new_freq, ret1);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700267 if (ret) {
268 __err("Error (%d) sending SCM event %d for core %s\n",
269 ret, event, core->core_name);
270 goto freq_done;
271 }
272
273 if ((core->actual_freq != new_freq) &&
274 (core->new_freq[core->freq_pending] != new_freq)) {
275 if (core->freq_pending >= MAX_PENDING - 1)
276 core->freq_pending = MAX_PENDING - 1;
277 core->new_freq[core->freq_pending++] = new_freq;
278 core->time_start = ktime_to_ns(ktime_get());
279
280 /* Schedule the frequency change */
281 if (!core->task)
282 __err("Uninitialized task for core %s\n",
283 core->core_name);
Eugene Seah76af9832012-03-28 18:43:53 -0600284 else {
285 if (freq_changed)
286 *freq_changed = 1;
287 core->change_freq_activated = 1;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700288 wake_up_process(core->task);
Eugene Seah76af9832012-03-28 18:43:53 -0600289 }
290 } else {
291 if (freq_changed)
292 *freq_changed = 0;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700293 }
294freq_done:
295 spin_unlock_irqrestore(&core->cpu_lock, flags);
296
297 return ret;
298}
299
300static enum hrtimer_restart msm_dcvs_core_slack_timer(struct hrtimer *timer)
301{
302 int ret = 0;
303 struct dcvs_core *core = container_of(timer, struct dcvs_core, timer);
Eugene Seah76af9832012-03-28 18:43:53 -0600304 uint32_t ret1;
305 uint32_t ret2;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700306
307 if (msm_dcvs_debug & MSM_DCVS_DEBUG_FREQ_CHANGE)
308 __info("Slack timer fired for core %s\n", core->core_name);
309
310 /**
311 * Timer expired, notify TZ
312 * Dont care about the third arg.
313 */
Eugene Seah76af9832012-03-28 18:43:53 -0600314 ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_QOS_TIMER_EXPIRED, 0,
315 &ret1, &ret2);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700316 if (ret)
317 __err("Timer expired for core %s but failed to notify.\n",
318 core->core_name);
319
320 return HRTIMER_NORESTART;
321}
322
323/* Helper functions and macros for sysfs nodes for a core */
324#define CORE_FROM_ATTRIBS(attr, name) \
325 container_of(container_of(attr, struct core_attribs, name), \
326 struct dcvs_core, attrib);
327
328#define DCVS_PARAM_SHOW(_name, v) \
329static ssize_t msm_dcvs_attr_##_name##_show(struct kobject *kobj, \
330 struct kobj_attribute *attr, char *buf) \
331{ \
332 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
333 return snprintf(buf, PAGE_SIZE, "%d\n", v); \
334}
335
336#define DCVS_ALGO_PARAM(_name) \
337static ssize_t msm_dcvs_attr_##_name##_show(struct kobject *kobj,\
338 struct kobj_attribute *attr, char *buf) \
339{ \
340 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
341 return snprintf(buf, PAGE_SIZE, "%d\n", core->algo_param._name); \
342} \
343static ssize_t msm_dcvs_attr_##_name##_store(struct kobject *kobj, \
344 struct kobj_attribute *attr, const char *buf, size_t count) \
345{ \
346 int ret = 0; \
347 uint32_t val = 0; \
348 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
349 mutex_lock(&core->lock); \
350 ret = kstrtouint(buf, 10, &val); \
351 if (ret) { \
352 __err("Invalid input %s for %s\n", buf, __stringify(_name));\
353 } else { \
354 uint32_t old_val = core->algo_param._name; \
355 core->algo_param._name = val; \
356 ret = msm_dcvs_scm_set_algo_params(core->handle, \
357 &core->algo_param); \
358 if (ret) { \
359 core->algo_param._name = old_val; \
360 __err("Error(%d) in setting %d for algo param %s\n",\
361 ret, val, __stringify(_name)); \
362 } \
363 } \
364 mutex_unlock(&core->lock); \
365 return count; \
366}
367
368#define DCVS_RO_ATTRIB(i, _name) \
369 core->attrib._name.attr.name = __stringify(_name); \
370 core->attrib._name.attr.mode = S_IRUGO; \
371 core->attrib._name.show = msm_dcvs_attr_##_name##_show; \
372 core->attrib._name.store = NULL; \
373 core->attrib.attrib_group.attrs[i] = &core->attrib._name.attr;
374
375#define DCVS_RW_ATTRIB(i, _name) \
376 core->attrib._name.attr.name = __stringify(_name); \
377 core->attrib._name.attr.mode = S_IRUGO | S_IWUSR; \
378 core->attrib._name.show = msm_dcvs_attr_##_name##_show; \
379 core->attrib._name.store = msm_dcvs_attr_##_name##_store; \
380 core->attrib.attrib_group.attrs[i] = &core->attrib._name.attr;
381
382/**
383 * Function declarations for different attributes.
384 * Gets used when setting the attribute show and store parameters.
385 */
386DCVS_PARAM_SHOW(idle_enabled, (core->idle_driver != NULL))
387DCVS_PARAM_SHOW(freq_change_enabled, (core->freq_driver != NULL))
388DCVS_PARAM_SHOW(actual_freq, (core->actual_freq))
389DCVS_PARAM_SHOW(freq_change_us, (core->freq_change_us))
390DCVS_PARAM_SHOW(max_time_us, (core->max_time_us))
391
392DCVS_ALGO_PARAM(slack_time_us)
393DCVS_ALGO_PARAM(scale_slack_time)
394DCVS_ALGO_PARAM(scale_slack_time_pct)
395DCVS_ALGO_PARAM(disable_pc_threshold)
396DCVS_ALGO_PARAM(em_window_size)
397DCVS_ALGO_PARAM(em_max_util_pct)
398DCVS_ALGO_PARAM(ss_window_size)
399DCVS_ALGO_PARAM(ss_util_pct)
400DCVS_ALGO_PARAM(ss_iobusy_conv)
401
402static int msm_dcvs_setup_core_sysfs(struct dcvs_core *core)
403{
404 int ret = 0;
405 struct kobject *core_kobj = NULL;
406 const int attr_count = 15;
407
408 BUG_ON(!cores_kobj);
409
410 core->attrib.attrib_group.attrs =
411 kzalloc(attr_count * sizeof(struct attribute *), GFP_KERNEL);
412
413 if (!core->attrib.attrib_group.attrs) {
414 ret = -ENOMEM;
415 goto done;
416 }
417
418 DCVS_RO_ATTRIB(0, idle_enabled);
419 DCVS_RO_ATTRIB(1, freq_change_enabled);
420 DCVS_RO_ATTRIB(2, actual_freq);
421 DCVS_RO_ATTRIB(3, freq_change_us);
422 DCVS_RO_ATTRIB(4, max_time_us);
423
424 DCVS_RW_ATTRIB(5, slack_time_us);
425 DCVS_RW_ATTRIB(6, scale_slack_time);
426 DCVS_RW_ATTRIB(7, scale_slack_time_pct);
427 DCVS_RW_ATTRIB(8, disable_pc_threshold);
428 DCVS_RW_ATTRIB(9, em_window_size);
429 DCVS_RW_ATTRIB(10, em_max_util_pct);
430 DCVS_RW_ATTRIB(11, ss_window_size);
431 DCVS_RW_ATTRIB(12, ss_util_pct);
432 DCVS_RW_ATTRIB(13, ss_iobusy_conv);
433
434 core->attrib.attrib_group.attrs[14] = NULL;
435
436 core_kobj = kobject_create_and_add(core->core_name, cores_kobj);
437 if (!core_kobj) {
438 ret = -ENOMEM;
439 goto done;
440 }
441
442 ret = sysfs_create_group(core_kobj, &core->attrib.attrib_group);
443 if (ret)
444 __err("Cannot create core %s attr group\n", core->core_name);
445 else if (msm_dcvs_debug & MSM_DCVS_DEBUG_NOTIFIER)
446 __info("Setting up attributes for core %s\n", core->core_name);
447
448done:
449 if (ret) {
450 kfree(core->attrib.attrib_group.attrs);
451 kobject_del(core_kobj);
452 }
453
454 return ret;
455}
456
457/* Return the core if found or add to list if @add_to_list is true */
458static struct dcvs_core *msm_dcvs_get_core(const char *name, int add_to_list)
459{
460 struct dcvs_core *core = NULL;
461 int i;
462 int empty = -1;
463
464 if (!name[0] ||
465 (strnlen(name, CORE_NAME_MAX - 1) == CORE_NAME_MAX - 1))
466 return core;
467
468 mutex_lock(&core_list_lock);
469 for (i = 0; i < CORES_MAX; i++) {
470 core = &core_list[i];
471 if ((empty < 0) && !core->core_name[0]) {
472 empty = i;
473 continue;
474 }
475 if (!strncmp(name, core->core_name, CORE_NAME_MAX))
476 break;
477 }
478
479 /* Check for core_list full */
480 if ((i == CORES_MAX) && (empty < 0)) {
481 mutex_unlock(&core_list_lock);
482 return NULL;
483 }
484
485 if (i == CORES_MAX && add_to_list) {
486 core = &core_list[empty];
487 strlcpy(core->core_name, name, CORE_NAME_MAX);
488 mutex_init(&core->lock);
489 spin_lock_init(&core->cpu_lock);
490 core->handle = empty + CORE_HANDLE_OFFSET;
491 hrtimer_init(&core->timer,
492 CLOCK_MONOTONIC, HRTIMER_MODE_REL_PINNED);
493 core->timer.function = msm_dcvs_core_slack_timer;
494 }
495 mutex_unlock(&core_list_lock);
496
497 return core;
498}
499
500int msm_dcvs_register_core(const char *core_name, uint32_t group_id,
501 struct msm_dcvs_core_info *info)
502{
503 int ret = -EINVAL;
504 struct dcvs_core *core = NULL;
505
506 if (!core_name || !core_name[0])
507 return ret;
508
509 core = msm_dcvs_get_core(core_name, true);
510 if (!core)
511 return ret;
512
513 mutex_lock(&core->lock);
514 if (group_id) {
515 /**
516 * Create a group for cores, if this core is part of a group
517 * if the group_id is 0, the core is not part of a group.
518 * If the group_id already exits, it will through an error
519 * which we will ignore.
520 */
521 ret = msm_dcvs_scm_create_group(group_id);
522 if (ret == -ENOMEM)
523 goto bail;
524 }
525 core->group_id = group_id;
526
527 core->max_time_us = info->core_param.max_time_us;
528 memcpy(&core->algo_param, &info->algo_param,
529 sizeof(struct msm_dcvs_algo_param));
530
531 ret = msm_dcvs_scm_register_core(core->handle, group_id,
532 &info->core_param, info->freq_tbl);
533 if (ret)
534 goto bail;
535
536 ret = msm_dcvs_scm_set_algo_params(core->handle, &info->algo_param);
537 if (ret)
538 goto bail;
539
540 ret = msm_dcvs_setup_core_sysfs(core);
541 if (ret) {
542 __err("Unable to setup core %s sysfs\n", core->core_name);
543 core_handles[core->handle - CORE_HANDLE_OFFSET] = NULL;
544 goto bail;
545 }
546
547bail:
548 mutex_unlock(&core->lock);
549 return ret;
550}
551EXPORT_SYMBOL(msm_dcvs_register_core);
552
553int msm_dcvs_freq_sink_register(struct msm_dcvs_freq *drv)
554{
555 int ret = -EINVAL;
556 struct dcvs_core *core = NULL;
Eugene Seah76af9832012-03-28 18:43:53 -0600557 uint32_t ret1;
558 uint32_t ret2;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700559
560 if (!drv || !drv->core_name)
561 return ret;
562
563 core = msm_dcvs_get_core(drv->core_name, true);
564 if (!core)
565 return ret;
566
567 mutex_lock(&core->lock);
568 if (core->freq_driver && (msm_dcvs_debug & MSM_DCVS_DEBUG_NOTIFIER))
569 __info("Frequency notifier for %s being replaced\n",
570 core->core_name);
571 core->freq_driver = drv;
572 core->task = kthread_create(msm_dcvs_do_freq, (void *)core,
573 "msm_dcvs/%d", core->handle);
574 if (IS_ERR(core->task)) {
575 mutex_unlock(&core->lock);
576 return -EFAULT;
577 }
578
579 if (msm_dcvs_debug & MSM_DCVS_DEBUG_IDLE_PULSE)
580 __info("Enabling idle pulse for %s\n", core->core_name);
581
582 if (core->idle_driver) {
583 core->actual_freq = core->freq_driver->get_frequency(drv);
584 /* Notify TZ to start receiving idle info for the core */
Eugene Seah76af9832012-03-28 18:43:53 -0600585 ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_ENABLE_CORE, 1,
586 &ret1, &ret2);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700587 core->idle_driver->enable(core->idle_driver,
588 MSM_DCVS_ENABLE_IDLE_PULSE);
589 }
590
591 mutex_unlock(&core->lock);
592
593 return core->handle;
594}
595EXPORT_SYMBOL(msm_dcvs_freq_sink_register);
596
597int msm_dcvs_freq_sink_unregister(struct msm_dcvs_freq *drv)
598{
599 int ret = -EINVAL;
600 struct dcvs_core *core = NULL;
Eugene Seah76af9832012-03-28 18:43:53 -0600601 uint32_t ret1;
602 uint32_t ret2;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700603
604 if (!drv || !drv->core_name)
605 return ret;
606
607 core = msm_dcvs_get_core(drv->core_name, false);
608 if (!core)
609 return ret;
610
611 mutex_lock(&core->lock);
612 if (msm_dcvs_debug & MSM_DCVS_DEBUG_IDLE_PULSE)
613 __info("Disabling idle pulse for %s\n", core->core_name);
614 if (core->idle_driver) {
615 core->idle_driver->enable(core->idle_driver,
616 MSM_DCVS_DISABLE_IDLE_PULSE);
617 /* Notify TZ to stop receiving idle info for the core */
Eugene Seah76af9832012-03-28 18:43:53 -0600618 ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_ENABLE_CORE, 0,
619 &ret1, &ret2);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700620 hrtimer_cancel(&core->timer);
621 core->idle_driver->enable(core->idle_driver,
622 MSM_DCVS_ENABLE_HIGH_LATENCY_MODES);
623 if (msm_dcvs_debug & MSM_DCVS_DEBUG_IDLE_PULSE)
624 __info("Enabling LPM for %s\n", core->core_name);
625 }
626 core->freq_pending = 0;
627 core->freq_driver = NULL;
628 mutex_unlock(&core->lock);
629 kthread_stop(core->task);
630
631 return 0;
632}
633EXPORT_SYMBOL(msm_dcvs_freq_sink_unregister);
634
635int msm_dcvs_idle_source_register(struct msm_dcvs_idle *drv)
636{
637 int ret = -EINVAL;
638 struct dcvs_core *core = NULL;
639
640 if (!drv || !drv->core_name)
641 return ret;
642
643 core = msm_dcvs_get_core(drv->core_name, true);
644 if (!core)
645 return ret;
646
647 mutex_lock(&core->lock);
648 if (core->idle_driver && (msm_dcvs_debug & MSM_DCVS_DEBUG_NOTIFIER))
649 __info("Idle notifier for %s being replaced\n",
650 core->core_name);
651 core->idle_driver = drv;
652 mutex_unlock(&core->lock);
653
654 return core->handle;
655}
656EXPORT_SYMBOL(msm_dcvs_idle_source_register);
657
658int msm_dcvs_idle_source_unregister(struct msm_dcvs_idle *drv)
659{
660 int ret = -EINVAL;
661 struct dcvs_core *core = NULL;
662
663 if (!drv || !drv->core_name)
664 return ret;
665
666 core = msm_dcvs_get_core(drv->core_name, false);
667 if (!core)
668 return ret;
669
670 mutex_lock(&core->lock);
671 core->idle_driver = NULL;
672 mutex_unlock(&core->lock);
673
674 return 0;
675}
676EXPORT_SYMBOL(msm_dcvs_idle_source_unregister);
677
678int msm_dcvs_idle(int handle, enum msm_core_idle_state state, uint32_t iowaited)
679{
680 int ret = 0;
681 struct dcvs_core *core = NULL;
Eugene Seah76af9832012-03-28 18:43:53 -0600682 uint32_t timer_interval_us = 0;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700683 uint32_t r0, r1;
Eugene Seah76af9832012-03-28 18:43:53 -0600684 uint32_t freq_changed = 0;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700685
686 if (handle >= CORE_HANDLE_OFFSET &&
687 (handle - CORE_HANDLE_OFFSET) < CORES_MAX)
688 core = &core_list[handle - CORE_HANDLE_OFFSET];
689
690 BUG_ON(!core);
691
692 if (msm_dcvs_debug & MSM_DCVS_DEBUG_IDLE_PULSE)
693 __info("Core %s idle state %d\n", core->core_name, state);
694
695 switch (state) {
696 case MSM_DCVS_IDLE_ENTER:
697 hrtimer_cancel(&core->timer);
698 ret = msm_dcvs_scm_event(core->handle,
699 MSM_DCVS_SCM_IDLE_ENTER, 0, 0, &r0, &r1);
700 if (ret)
701 __err("Error (%d) sending idle enter for %s\n",
702 ret, core->core_name);
703 break;
704
705 case MSM_DCVS_IDLE_EXIT:
706 hrtimer_cancel(&core->timer);
707 ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_IDLE_EXIT,
Eugene Seah76af9832012-03-28 18:43:53 -0600708 iowaited, &timer_interval_us, &freq_changed);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700709 if (ret)
710 __err("Error (%d) sending idle exit for %s\n",
711 ret, core->core_name);
Eugene Seah76af9832012-03-28 18:43:53 -0600712 /* only start slack timer if change_freq won't */
713 if (freq_changed || core->change_freq_activated)
714 break;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700715 if (timer_interval_us && !core->timer_disabled) {
716 ret = hrtimer_start(&core->timer,
717 ktime_set(0, timer_interval_us * 1000),
718 HRTIMER_MODE_REL_PINNED);
Eugene Seah76af9832012-03-28 18:43:53 -0600719
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700720 if (ret)
721 __err("Failed to register timer for core %s\n",
Eugene Seah76af9832012-03-28 18:43:53 -0600722 core->core_name);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700723 }
724 break;
725 }
726
727 return ret;
728}
729EXPORT_SYMBOL(msm_dcvs_idle);
730
731static int __init msm_dcvs_late_init(void)
732{
733 struct kobject *module_kobj = NULL;
734 int ret = 0;
735
736 module_kobj = kset_find_obj(module_kset, KBUILD_MODNAME);
737 if (!module_kobj) {
738 pr_err("%s: cannot find kobject for module %s\n",
739 __func__, KBUILD_MODNAME);
740 ret = -ENOENT;
741 goto err;
742 }
743
744 cores_kobj = kobject_create_and_add("cores", module_kobj);
745 if (!cores_kobj) {
746 __err("Cannot create %s kobject\n", "cores");
747 ret = -ENOMEM;
748 goto err;
749 }
750
751 debugfs_base = debugfs_create_dir("msm_dcvs", NULL);
752 if (!debugfs_base) {
753 __err("Cannot create debugfs base %s\n", "msm_dcvs");
754 ret = -ENOENT;
755 goto err;
756 }
757
758 if (!debugfs_create_u32("debug_mask", S_IRUGO | S_IWUSR,
759 debugfs_base, &msm_dcvs_debug)) {
760 __err("Cannot create debugfs entry %s\n", "debug_mask");
761 ret = -ENOMEM;
762 goto err;
763 }
764
765err:
766 if (ret) {
767 kobject_del(cores_kobj);
768 cores_kobj = NULL;
769 debugfs_remove(debugfs_base);
770 }
771
772 return ret;
773}
774late_initcall(msm_dcvs_late_init);
775
776static int __init msm_dcvs_early_init(void)
777{
778 int ret = 0;
779
780 if (!msm_dcvs_enabled) {
781 __info("Not enabled (%d)\n", msm_dcvs_enabled);
782 return 0;
783 }
784
785 ret = msm_dcvs_scm_init(10 * 1024);
786 if (ret)
787 __err("Unable to initialize DCVS err=%d\n", ret);
788
789 return ret;
790}
791postcore_initcall(msm_dcvs_early_init);