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