blob: bd60824ec1de8c1f8d73900025afa68cedfd07f2 [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 {
76 char core_name[CORE_NAME_MAX];
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070077 uint32_t actual_freq;
78 uint32_t freq_change_us;
79
80 uint32_t max_time_us; /* core param */
81
82 struct msm_dcvs_algo_param algo_param;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -070083 struct msm_dcvs_energy_curve_coeffs coeffs;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070084 struct msm_dcvs_idle *idle_driver;
85 struct msm_dcvs_freq *freq_driver;
86
87 /* private */
88 int64_t time_start;
89 struct mutex lock;
90 spinlock_t cpu_lock;
91 struct task_struct *task;
92 struct core_attribs attrib;
93 uint32_t handle;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070094 struct hrtimer timer;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070095 int32_t timer_disabled;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -070096 struct msm_dcvs_core_info *info;
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -070097 int sensor;
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -070098 int pending_freq;
Abhijeet Dharmapurikarbbb52fe2012-08-31 20:31:16 -070099 wait_queue_head_t wait_q;
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700100 int (*set_frequency)(struct msm_dcvs_freq *self, unsigned int freq);
101 unsigned int (*get_frequency)(struct msm_dcvs_freq *self);
Abhijeet Dharmapurikar6e9b34f2012-09-10 16:03:39 -0700102 int (*idle_enable)(struct msm_dcvs_idle *self,
103 enum msm_core_control_event event);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700104};
105
106static int msm_dcvs_debug;
107static int msm_dcvs_enabled = 1;
108module_param_named(enable, msm_dcvs_enabled, int, S_IRUGO | S_IWUSR | S_IWGRP);
109
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700110static struct dentry *debugfs_base;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700111
112static struct dcvs_core core_list[CORES_MAX];
113static DEFINE_MUTEX(core_list_lock);
114
115static struct kobject *cores_kobj;
116static struct dcvs_core *core_handles[CORES_MAX];
117
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 Dharmapurikar50bcc832012-08-31 22:10:41 -0700130 if (!core->freq_driver || !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 Dharmapurikar50bcc832012-08-31 22:10:41 -0700158 ret = core->set_frequency(core->freq_driver, 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 */
180 if (core->actual_freq >
181 core->algo_param.disable_pc_threshold) {
Abhijeet Dharmapurikar6e9b34f2012-09-10 16:03:39 -0700182 core->idle_enable(core->idle_driver,
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700183 MSM_DCVS_DISABLE_HIGH_LATENCY_MODES);
184 if (msm_dcvs_debug & MSM_DCVS_DEBUG_IDLE_PULSE)
185 __info("Disabling LPM for %s\n", core->core_name);
186 } else if (core->actual_freq <=
187 core->algo_param.disable_pc_threshold) {
Abhijeet Dharmapurikar6e9b34f2012-09-10 16:03:39 -0700188 core->idle_enable(core->idle_driver,
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700189 MSM_DCVS_ENABLE_HIGH_LATENCY_MODES);
190 if (msm_dcvs_debug & MSM_DCVS_DEBUG_IDLE_PULSE)
191 __info("Enabling LPM for %s\n", core->core_name);
192 }
193
194 /**
195 * Update algorithm with new freq and time taken to change
196 * to this frequency and that will get us the new slack
197 * timer
198 */
199 ret = msm_dcvs_scm_event(core->handle, MSM_DCVS_SCM_CLOCK_FREQ_UPDATE,
200 core->actual_freq, (uint32_t)time_end, &slack_us, &ret1);
201 if (!ret) {
202 /* Reset the slack timer */
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700203 if (slack_us) {
204 core->timer_disabled = 0;
205 ret = hrtimer_start(&core->timer,
206 ktime_set(0, slack_us * 1000),
207 HRTIMER_MODE_REL_PINNED);
208 if (ret)
209 __err("Failed to register timer for core %s\n",
210 core->core_name);
211 }
212 } else {
213 __err("Error sending core (%s) freq change (%u)\n",
214 core->core_name, core->actual_freq);
215 }
216
217 if (msm_dcvs_debug & MSM_DCVS_DEBUG_FREQ_CHANGE)
218 __info("Freq %u requested for core %s (actual %u prev %u) "
219 "change time %u us slack time %u us\n",
220 requested_freq, core->core_name,
221 core->actual_freq, prev_freq,
Eugene Seah76af9832012-03-28 18:43:53 -0600222 core->freq_change_us, slack_us);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700223
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700224 spin_lock_irqsave(&core->cpu_lock, flags);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700225 /**
226 * By the time we are done with freq changes, we could be asked to
227 * change again. Check before exiting.
228 */
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700229 if (core->pending_freq)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700230 goto repeat;
231
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700232
233out: /* should always be jumped to with the spin_lock held */
234 core->pending_freq = 0;
235 spin_unlock_irqrestore(&core->cpu_lock, flags);
236
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700237 return ret;
238}
239
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700240static int __msm_dcvs_report_temp(struct dcvs_core *core)
241{
242 struct msm_dcvs_core_info *info = core->info;
243 struct tsens_device tsens_dev;
244 int ret;
245 unsigned long temp = 0;
246
247 tsens_dev.sensor_num = core->sensor;
248 ret = tsens_get_temp(&tsens_dev, &temp);
249 if (!ret) {
250 tsens_dev.sensor_num = 0;
251 ret = tsens_get_temp(&tsens_dev, &temp);
252 if (!ret)
253 return -ENODEV;
254 }
255
256 ret = msm_dcvs_scm_set_power_params(core->handle, &info->power_param,
257 &info->freq_tbl[0], &core->coeffs);
258 return ret;
259}
260
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700261static int msm_dcvs_do_freq(void *data)
262{
263 struct dcvs_core *core = (struct dcvs_core *)data;
264 static struct sched_param param = {.sched_priority = MAX_RT_PRIO - 1};
265
266 sched_setscheduler(current, SCHED_FIFO, &param);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700267
268 while (!kthread_should_stop()) {
Abhijeet Dharmapurikarbbb52fe2012-08-31 20:31:16 -0700269 wait_event(core->wait_q, !(core->pending_freq == 0 ||
270 core->pending_freq == -1) ||
271 kthread_should_stop());
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700272
273 if (kthread_should_stop())
274 break;
275
Abhijeet Dharmapurikarbbb52fe2012-08-31 20:31:16 -0700276 mutex_lock(&core->lock);
277 __msm_dcvs_change_freq(core);
278 __msm_dcvs_report_temp(core);
279 mutex_unlock(&core->lock);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700280 }
281
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700282 return 0;
283}
284
285static int msm_dcvs_update_freq(struct dcvs_core *core,
Eugene Seah76af9832012-03-28 18:43:53 -0600286 enum msm_dcvs_scm_event event, uint32_t param0,
287 uint32_t *ret1, int *freq_changed)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700288{
289 int ret = 0;
290 unsigned long flags = 0;
291 uint32_t new_freq = 0;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700292
293 spin_lock_irqsave(&core->cpu_lock, flags);
294 ret = msm_dcvs_scm_event(core->handle, event, param0,
Eugene Seah76af9832012-03-28 18:43:53 -0600295 core->actual_freq, &new_freq, ret1);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700296 if (ret) {
Jeff Ohlstein4aa4a2b2012-06-28 19:03:49 -0700297 if (ret == -13)
298 ret = 0;
299 else
300 __err("Error (%d) sending SCM event %d for core %s\n",
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700301 ret, event, core->core_name);
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700302 goto out;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700303 }
304
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700305 if (core->actual_freq != new_freq && core->pending_freq != new_freq) {
306 core->pending_freq = new_freq;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700307 core->time_start = ktime_to_ns(ktime_get());
308
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700309 if (core->task)
Abhijeet Dharmapurikarbbb52fe2012-08-31 20:31:16 -0700310 wake_up(&core->wait_q);
Eugene Seah76af9832012-03-28 18:43:53 -0600311 } else {
312 if (freq_changed)
313 *freq_changed = 0;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700314 }
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700315out:
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700316 spin_unlock_irqrestore(&core->cpu_lock, flags);
317
318 return ret;
319}
320
321static enum hrtimer_restart msm_dcvs_core_slack_timer(struct hrtimer *timer)
322{
323 int ret = 0;
324 struct dcvs_core *core = container_of(timer, struct dcvs_core, timer);
Eugene Seah76af9832012-03-28 18:43:53 -0600325 uint32_t ret1;
326 uint32_t ret2;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700327
328 if (msm_dcvs_debug & MSM_DCVS_DEBUG_FREQ_CHANGE)
329 __info("Slack timer fired for core %s\n", core->core_name);
330
331 /**
332 * Timer expired, notify TZ
333 * Dont care about the third arg.
334 */
Eugene Seah76af9832012-03-28 18:43:53 -0600335 ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_QOS_TIMER_EXPIRED, 0,
336 &ret1, &ret2);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700337 if (ret)
338 __err("Timer expired for core %s but failed to notify.\n",
339 core->core_name);
340
341 return HRTIMER_NORESTART;
342}
343
344/* Helper functions and macros for sysfs nodes for a core */
345#define CORE_FROM_ATTRIBS(attr, name) \
346 container_of(container_of(attr, struct core_attribs, name), \
347 struct dcvs_core, attrib);
348
349#define DCVS_PARAM_SHOW(_name, v) \
350static ssize_t msm_dcvs_attr_##_name##_show(struct kobject *kobj, \
351 struct kobj_attribute *attr, char *buf) \
352{ \
353 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
354 return snprintf(buf, PAGE_SIZE, "%d\n", v); \
355}
356
357#define DCVS_ALGO_PARAM(_name) \
358static ssize_t msm_dcvs_attr_##_name##_show(struct kobject *kobj,\
359 struct kobj_attribute *attr, char *buf) \
360{ \
361 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
362 return snprintf(buf, PAGE_SIZE, "%d\n", core->algo_param._name); \
363} \
364static ssize_t msm_dcvs_attr_##_name##_store(struct kobject *kobj, \
365 struct kobj_attribute *attr, const char *buf, size_t count) \
366{ \
367 int ret = 0; \
368 uint32_t val = 0; \
369 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
370 mutex_lock(&core->lock); \
371 ret = kstrtouint(buf, 10, &val); \
372 if (ret) { \
373 __err("Invalid input %s for %s\n", buf, __stringify(_name));\
374 } else { \
375 uint32_t old_val = core->algo_param._name; \
376 core->algo_param._name = val; \
377 ret = msm_dcvs_scm_set_algo_params(core->handle, \
378 &core->algo_param); \
379 if (ret) { \
380 core->algo_param._name = old_val; \
381 __err("Error(%d) in setting %d for algo param %s\n",\
382 ret, val, __stringify(_name)); \
383 } \
384 } \
385 mutex_unlock(&core->lock); \
386 return count; \
387}
388
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700389#define DCVS_ENERGY_PARAM(_name) \
390static ssize_t msm_dcvs_attr_##_name##_show(struct kobject *kobj,\
391 struct kobj_attribute *attr, char *buf) \
392{ \
393 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
394 return snprintf(buf, PAGE_SIZE, "%d\n", core->coeffs._name); \
395} \
396static ssize_t msm_dcvs_attr_##_name##_store(struct kobject *kobj, \
397 struct kobj_attribute *attr, const char *buf, size_t count) \
398{ \
399 int ret = 0; \
400 int32_t val = 0; \
401 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
402 mutex_lock(&core->lock); \
403 ret = kstrtoint(buf, 10, &val); \
404 if (ret) { \
405 __err("Invalid input %s for %s\n", buf, __stringify(_name));\
406 } else { \
407 int32_t old_val = core->coeffs._name; \
408 core->coeffs._name = val; \
409 ret = msm_dcvs_scm_set_power_params(core->handle, \
410 &core->info->power_param, &core->info->freq_tbl[0], \
411 &core->coeffs); \
412 if (ret) { \
413 core->coeffs._name = old_val; \
414 __err("Error(%d) in setting %d for coeffs param %s\n",\
415 ret, val, __stringify(_name)); \
416 } \
417 } \
418 mutex_unlock(&core->lock); \
419 return count; \
420}
421
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700422#define DCVS_RO_ATTRIB(i, _name) \
423 core->attrib._name.attr.name = __stringify(_name); \
424 core->attrib._name.attr.mode = S_IRUGO; \
425 core->attrib._name.show = msm_dcvs_attr_##_name##_show; \
426 core->attrib._name.store = NULL; \
427 core->attrib.attrib_group.attrs[i] = &core->attrib._name.attr;
428
429#define DCVS_RW_ATTRIB(i, _name) \
430 core->attrib._name.attr.name = __stringify(_name); \
431 core->attrib._name.attr.mode = S_IRUGO | S_IWUSR; \
432 core->attrib._name.show = msm_dcvs_attr_##_name##_show; \
433 core->attrib._name.store = msm_dcvs_attr_##_name##_store; \
434 core->attrib.attrib_group.attrs[i] = &core->attrib._name.attr;
435
436/**
437 * Function declarations for different attributes.
438 * Gets used when setting the attribute show and store parameters.
439 */
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700440DCVS_PARAM_SHOW(core_id, core->handle)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700441DCVS_PARAM_SHOW(idle_enabled, (core->idle_driver != NULL))
442DCVS_PARAM_SHOW(freq_change_enabled, (core->freq_driver != NULL))
443DCVS_PARAM_SHOW(actual_freq, (core->actual_freq))
444DCVS_PARAM_SHOW(freq_change_us, (core->freq_change_us))
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700445
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700446DCVS_ALGO_PARAM(disable_pc_threshold)
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700447DCVS_ALGO_PARAM(em_win_size_min_us)
448DCVS_ALGO_PARAM(em_win_size_max_us)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700449DCVS_ALGO_PARAM(em_max_util_pct)
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700450DCVS_ALGO_PARAM(group_id)
451DCVS_ALGO_PARAM(max_freq_chg_time_us)
452DCVS_ALGO_PARAM(slack_mode_dynamic)
453DCVS_ALGO_PARAM(slack_time_min_us)
454DCVS_ALGO_PARAM(slack_time_max_us)
455DCVS_ALGO_PARAM(slack_weight_thresh_pct)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700456DCVS_ALGO_PARAM(ss_iobusy_conv)
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700457DCVS_ALGO_PARAM(ss_win_size_min_us)
458DCVS_ALGO_PARAM(ss_win_size_max_us)
459DCVS_ALGO_PARAM(ss_util_pct)
460
461DCVS_ENERGY_PARAM(active_coeff_a)
462DCVS_ENERGY_PARAM(active_coeff_b)
463DCVS_ENERGY_PARAM(active_coeff_c)
464DCVS_ENERGY_PARAM(leakage_coeff_a)
465DCVS_ENERGY_PARAM(leakage_coeff_b)
466DCVS_ENERGY_PARAM(leakage_coeff_c)
467DCVS_ENERGY_PARAM(leakage_coeff_d)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700468
469static int msm_dcvs_setup_core_sysfs(struct dcvs_core *core)
470{
471 int ret = 0;
472 struct kobject *core_kobj = NULL;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700473 const int attr_count = 27;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700474
475 BUG_ON(!cores_kobj);
476
477 core->attrib.attrib_group.attrs =
478 kzalloc(attr_count * sizeof(struct attribute *), GFP_KERNEL);
479
480 if (!core->attrib.attrib_group.attrs) {
481 ret = -ENOMEM;
482 goto done;
483 }
484
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700485
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700486 DCVS_RO_ATTRIB(0, core_id);
487 DCVS_RO_ATTRIB(1, idle_enabled);
488 DCVS_RO_ATTRIB(2, freq_change_enabled);
489 DCVS_RO_ATTRIB(3, actual_freq);
490 DCVS_RO_ATTRIB(4, freq_change_us);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700491
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700492 DCVS_RW_ATTRIB(5, disable_pc_threshold);
493 DCVS_RW_ATTRIB(6, em_win_size_min_us);
494 DCVS_RW_ATTRIB(7, em_win_size_max_us);
495 DCVS_RW_ATTRIB(8, em_max_util_pct);
496 DCVS_RW_ATTRIB(9, group_id);
497 DCVS_RW_ATTRIB(10, max_freq_chg_time_us);
498 DCVS_RW_ATTRIB(11, slack_mode_dynamic);
499 DCVS_RW_ATTRIB(12, slack_time_min_us);
500 DCVS_RW_ATTRIB(13, slack_time_max_us);
501 DCVS_RW_ATTRIB(14, slack_weight_thresh_pct);
502 DCVS_RW_ATTRIB(15, ss_iobusy_conv);
503 DCVS_RW_ATTRIB(16, ss_win_size_min_us);
504 DCVS_RW_ATTRIB(17, ss_win_size_max_us);
505 DCVS_RW_ATTRIB(18, ss_util_pct);
506
507 DCVS_RW_ATTRIB(19, active_coeff_a);
508 DCVS_RW_ATTRIB(20, active_coeff_b);
509 DCVS_RW_ATTRIB(21, active_coeff_c);
510 DCVS_RW_ATTRIB(22, leakage_coeff_a);
511 DCVS_RW_ATTRIB(23, leakage_coeff_b);
512 DCVS_RW_ATTRIB(24, leakage_coeff_c);
513 DCVS_RW_ATTRIB(25, leakage_coeff_d);
514
515 core->attrib.attrib_group.attrs[26] = NULL;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700516
517 core_kobj = kobject_create_and_add(core->core_name, cores_kobj);
518 if (!core_kobj) {
519 ret = -ENOMEM;
520 goto done;
521 }
522
523 ret = sysfs_create_group(core_kobj, &core->attrib.attrib_group);
524 if (ret)
525 __err("Cannot create core %s attr group\n", core->core_name);
526 else if (msm_dcvs_debug & MSM_DCVS_DEBUG_NOTIFIER)
527 __info("Setting up attributes for core %s\n", core->core_name);
528
529done:
530 if (ret) {
531 kfree(core->attrib.attrib_group.attrs);
532 kobject_del(core_kobj);
533 }
534
535 return ret;
536}
537
538/* Return the core if found or add to list if @add_to_list is true */
Abhijeet Dharmapurikar2ebc0fe2012-09-12 14:05:13 -0700539static struct dcvs_core *msm_dcvs_add_core(const char *name, int *pos)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700540{
541 struct dcvs_core *core = NULL;
542 int i;
543 int empty = -1;
544
545 if (!name[0] ||
546 (strnlen(name, CORE_NAME_MAX - 1) == CORE_NAME_MAX - 1))
547 return core;
548
549 mutex_lock(&core_list_lock);
550 for (i = 0; i < CORES_MAX; i++) {
551 core = &core_list[i];
552 if ((empty < 0) && !core->core_name[0]) {
553 empty = i;
554 continue;
555 }
Abhijeet Dharmapurikar2ebc0fe2012-09-12 14:05:13 -0700556 if (!strncmp(name, core->core_name, CORE_NAME_MAX)) {
557 if (pos != NULL)
558 *pos = i;
559 /*
560 * found a core with the same name, return NULL and
561 * set pos
562 */
563 core = NULL;
564 goto out;
565 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700566 }
567
568 /* Check for core_list full */
Abhijeet Dharmapurikar2ebc0fe2012-09-12 14:05:13 -0700569 if (empty < 0) {
570 *pos = 0;
571 core = NULL;
572 goto out;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700573 }
574
Abhijeet Dharmapurikar2ebc0fe2012-09-12 14:05:13 -0700575 core = &core_list[empty];
576 strlcpy(core->core_name, name, CORE_NAME_MAX);
577 mutex_init(&core->lock);
578 spin_lock_init(&core->cpu_lock);
579 core->handle = empty + CORE_HANDLE_OFFSET;
580 hrtimer_init(&core->timer,
581 CLOCK_MONOTONIC, HRTIMER_MODE_REL_PINNED);
582 core->timer.function = msm_dcvs_core_slack_timer;
583 if (pos != NULL)
584 *pos = empty;
585
586out:
587 mutex_unlock(&core_list_lock);
588 return core;
589}
590
591/* Return the core if found or add to list if @add_to_list is true */
592static struct dcvs_core *msm_dcvs_get_core(const char *name, int *pos)
593{
594 struct dcvs_core *core = NULL;
595 int i;
596
597 if (!name[0] ||
598 (strnlen(name, CORE_NAME_MAX - 1) == CORE_NAME_MAX - 1))
599 return core;
600
601 mutex_lock(&core_list_lock);
602 for (i = 0; i < CORES_MAX; i++) {
603 core = &core_list[i];
604 if (!strncmp(name, core->core_name, CORE_NAME_MAX)) {
605 if (pos != NULL)
606 *pos = i;
607 break;
608 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700609 }
Abhijeet Dharmapurikar2ebc0fe2012-09-12 14:05:13 -0700610
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700611 mutex_unlock(&core_list_lock);
612
613 return core;
614}
615
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700616int msm_dcvs_register_core(const char *core_name,
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700617 struct msm_dcvs_core_info *info,
618 int (*set_frequency)(struct msm_dcvs_freq *self, unsigned int freq),
619 unsigned int (*get_frequency)(struct msm_dcvs_freq *self),
Abhijeet Dharmapurikar6e9b34f2012-09-10 16:03:39 -0700620 int (*idle_enable)(struct msm_dcvs_idle *self,
621 enum msm_core_control_event event),
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700622 int sensor)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700623{
624 int ret = -EINVAL;
Abhijeet Dharmapurikar2ebc0fe2012-09-12 14:05:13 -0700625 int pos = 0;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700626 struct dcvs_core *core = NULL;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700627 uint32_t ret1;
628 uint32_t ret2;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700629
630 if (!core_name || !core_name[0])
631 return ret;
632
Abhijeet Dharmapurikar2ebc0fe2012-09-12 14:05:13 -0700633 core = msm_dcvs_add_core(core_name, &pos);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700634 if (!core)
635 return ret;
636
637 mutex_lock(&core->lock);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700638
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700639 core->set_frequency = set_frequency;
640 core->get_frequency = get_frequency;
Abhijeet Dharmapurikar6e9b34f2012-09-10 16:03:39 -0700641 core->idle_enable = idle_enable;
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700642
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700643 core->info = info;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700644 memcpy(&core->algo_param, &info->algo_param,
645 sizeof(struct msm_dcvs_algo_param));
646
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700647 memcpy(&core->coeffs, &info->energy_coeffs,
648 sizeof(struct msm_dcvs_energy_curve_coeffs));
649
Abhijeet Dharmapurikar2ebc0fe2012-09-12 14:05:13 -0700650 info->core_param.core_bitmask_id = 1 << pos;
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700651 pr_debug("registering core with sensor %d\n", sensor);
652 core->sensor = sensor;
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700653
654 ret = msm_dcvs_scm_register_core(core->handle, &info->core_param);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700655 if (ret)
656 goto bail;
657
658 ret = msm_dcvs_scm_set_algo_params(core->handle, &info->algo_param);
659 if (ret)
660 goto bail;
661
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700662 ret = msm_dcvs_scm_set_power_params(core->handle, &info->power_param,
663 &info->freq_tbl[0], &core->coeffs);
664 if (ret)
665 goto bail;
666
667 ret = msm_dcvs_scm_event(core->handle, MSM_DCVS_SCM_CORE_ONLINE,
668 core->actual_freq, 0, &ret1, &ret2);
669 if (ret)
670 goto bail;
671
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700672 ret = msm_dcvs_setup_core_sysfs(core);
673 if (ret) {
674 __err("Unable to setup core %s sysfs\n", core->core_name);
675 core_handles[core->handle - CORE_HANDLE_OFFSET] = NULL;
676 goto bail;
677 }
Abhijeet Dharmapurikarbbb52fe2012-08-31 20:31:16 -0700678 init_waitqueue_head(&core->wait_q);
679 core->task = kthread_run(msm_dcvs_do_freq, (void *)core,
Abhijeet Dharmapurikar74f10832012-08-26 22:40:28 -0700680 "msm_dcvs/%d", core->handle);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700681bail:
682 mutex_unlock(&core->lock);
683 return ret;
684}
685EXPORT_SYMBOL(msm_dcvs_register_core);
686
Abhijeet Dharmapurikarc43f0db2012-08-31 20:42:53 -0700687void msm_dcvs_update_limits(struct msm_dcvs_freq *drv)
688{
689 struct dcvs_core *core;
690
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700691 if (!drv || !drv->core_name)
Abhijeet Dharmapurikarc43f0db2012-08-31 20:42:53 -0700692 return;
693
Abhijeet Dharmapurikar2ebc0fe2012-09-12 14:05:13 -0700694 core = msm_dcvs_get_core(drv->core_name, NULL);
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700695 core->actual_freq = core->get_frequency(drv);
Abhijeet Dharmapurikarc43f0db2012-08-31 20:42:53 -0700696}
697
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700698int msm_dcvs_freq_sink_start(struct msm_dcvs_freq *drv)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700699{
700 int ret = -EINVAL;
701 struct dcvs_core *core = NULL;
Eugene Seah76af9832012-03-28 18:43:53 -0600702 uint32_t ret1;
703 uint32_t ret2;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700704
705 if (!drv || !drv->core_name)
706 return ret;
707
Abhijeet Dharmapurikar2ebc0fe2012-09-12 14:05:13 -0700708 core = msm_dcvs_get_core(drv->core_name, NULL);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700709 if (!core)
710 return ret;
711
712 mutex_lock(&core->lock);
713 if (core->freq_driver && (msm_dcvs_debug & MSM_DCVS_DEBUG_NOTIFIER))
714 __info("Frequency notifier for %s being replaced\n",
715 core->core_name);
716 core->freq_driver = drv;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700717 if (IS_ERR(core->task)) {
718 mutex_unlock(&core->lock);
719 return -EFAULT;
720 }
721
722 if (msm_dcvs_debug & MSM_DCVS_DEBUG_IDLE_PULSE)
723 __info("Enabling idle pulse for %s\n", core->core_name);
724
725 if (core->idle_driver) {
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700726 core->actual_freq = core->get_frequency(drv);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700727 /* Notify TZ to start receiving idle info for the core */
Abhijeet Dharmapurikar7b933c52012-08-23 15:51:58 -0700728 ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_DCVS_ENABLE, 1,
Eugene Seah76af9832012-03-28 18:43:53 -0600729 &ret1, &ret2);
Abhijeet Dharmapurikar6e9b34f2012-09-10 16:03:39 -0700730 core->idle_enable(core->idle_driver,
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700731 MSM_DCVS_ENABLE_IDLE_PULSE);
732 }
733
734 mutex_unlock(&core->lock);
735
736 return core->handle;
737}
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700738EXPORT_SYMBOL(msm_dcvs_freq_sink_start);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700739
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700740int msm_dcvs_freq_sink_stop(struct msm_dcvs_freq *drv)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700741{
742 int ret = -EINVAL;
743 struct dcvs_core *core = NULL;
Eugene Seah76af9832012-03-28 18:43:53 -0600744 uint32_t ret1;
745 uint32_t ret2;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700746
747 if (!drv || !drv->core_name)
748 return ret;
749
Abhijeet Dharmapurikar2ebc0fe2012-09-12 14:05:13 -0700750 core = msm_dcvs_get_core(drv->core_name, NULL);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700751 if (!core)
752 return ret;
753
754 mutex_lock(&core->lock);
755 if (msm_dcvs_debug & MSM_DCVS_DEBUG_IDLE_PULSE)
756 __info("Disabling idle pulse for %s\n", core->core_name);
757 if (core->idle_driver) {
Abhijeet Dharmapurikar6e9b34f2012-09-10 16:03:39 -0700758 core->idle_enable(core->idle_driver,
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700759 MSM_DCVS_DISABLE_IDLE_PULSE);
760 /* Notify TZ to stop receiving idle info for the core */
Abhijeet Dharmapurikar7b933c52012-08-23 15:51:58 -0700761 ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_DCVS_ENABLE, 0,
Eugene Seah76af9832012-03-28 18:43:53 -0600762 &ret1, &ret2);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700763 hrtimer_cancel(&core->timer);
Abhijeet Dharmapurikar6e9b34f2012-09-10 16:03:39 -0700764 core->idle_enable(core->idle_driver,
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700765 MSM_DCVS_ENABLE_HIGH_LATENCY_MODES);
766 if (msm_dcvs_debug & MSM_DCVS_DEBUG_IDLE_PULSE)
767 __info("Enabling LPM for %s\n", core->core_name);
768 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700769 core->freq_driver = NULL;
770 mutex_unlock(&core->lock);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700771
772 return 0;
773}
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700774EXPORT_SYMBOL(msm_dcvs_freq_sink_stop);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700775
776int msm_dcvs_idle_source_register(struct msm_dcvs_idle *drv)
777{
778 int ret = -EINVAL;
779 struct dcvs_core *core = NULL;
780
781 if (!drv || !drv->core_name)
782 return ret;
783
Abhijeet Dharmapurikar2ebc0fe2012-09-12 14:05:13 -0700784 core = msm_dcvs_get_core(drv->core_name, NULL);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700785 if (!core)
786 return ret;
787
788 mutex_lock(&core->lock);
789 if (core->idle_driver && (msm_dcvs_debug & MSM_DCVS_DEBUG_NOTIFIER))
790 __info("Idle notifier for %s being replaced\n",
791 core->core_name);
792 core->idle_driver = drv;
793 mutex_unlock(&core->lock);
794
795 return core->handle;
796}
797EXPORT_SYMBOL(msm_dcvs_idle_source_register);
798
799int msm_dcvs_idle_source_unregister(struct msm_dcvs_idle *drv)
800{
801 int ret = -EINVAL;
802 struct dcvs_core *core = NULL;
803
804 if (!drv || !drv->core_name)
805 return ret;
806
Abhijeet Dharmapurikar2ebc0fe2012-09-12 14:05:13 -0700807 core = msm_dcvs_get_core(drv->core_name, NULL);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700808 if (!core)
809 return ret;
810
811 mutex_lock(&core->lock);
812 core->idle_driver = NULL;
813 mutex_unlock(&core->lock);
814
815 return 0;
816}
817EXPORT_SYMBOL(msm_dcvs_idle_source_unregister);
818
819int msm_dcvs_idle(int handle, enum msm_core_idle_state state, uint32_t iowaited)
820{
821 int ret = 0;
822 struct dcvs_core *core = NULL;
Eugene Seah76af9832012-03-28 18:43:53 -0600823 uint32_t timer_interval_us = 0;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700824 uint32_t r0, r1;
Eugene Seah76af9832012-03-28 18:43:53 -0600825 uint32_t freq_changed = 0;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700826
827 if (handle >= CORE_HANDLE_OFFSET &&
828 (handle - CORE_HANDLE_OFFSET) < CORES_MAX)
829 core = &core_list[handle - CORE_HANDLE_OFFSET];
830
831 BUG_ON(!core);
832
833 if (msm_dcvs_debug & MSM_DCVS_DEBUG_IDLE_PULSE)
834 __info("Core %s idle state %d\n", core->core_name, state);
835
836 switch (state) {
837 case MSM_DCVS_IDLE_ENTER:
838 hrtimer_cancel(&core->timer);
839 ret = msm_dcvs_scm_event(core->handle,
840 MSM_DCVS_SCM_IDLE_ENTER, 0, 0, &r0, &r1);
841 if (ret)
842 __err("Error (%d) sending idle enter for %s\n",
843 ret, core->core_name);
844 break;
845
846 case MSM_DCVS_IDLE_EXIT:
847 hrtimer_cancel(&core->timer);
848 ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_IDLE_EXIT,
Eugene Seah76af9832012-03-28 18:43:53 -0600849 iowaited, &timer_interval_us, &freq_changed);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700850 if (ret)
851 __err("Error (%d) sending idle exit for %s\n",
852 ret, core->core_name);
Eugene Seah76af9832012-03-28 18:43:53 -0600853 /* only start slack timer if change_freq won't */
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700854 if (freq_changed)
Eugene Seah76af9832012-03-28 18:43:53 -0600855 break;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700856 if (timer_interval_us && !core->timer_disabled) {
857 ret = hrtimer_start(&core->timer,
858 ktime_set(0, timer_interval_us * 1000),
859 HRTIMER_MODE_REL_PINNED);
Eugene Seah76af9832012-03-28 18:43:53 -0600860
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700861 if (ret)
862 __err("Failed to register timer for core %s\n",
Eugene Seah76af9832012-03-28 18:43:53 -0600863 core->core_name);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700864 }
865 break;
866 }
867
868 return ret;
869}
870EXPORT_SYMBOL(msm_dcvs_idle);
871
872static int __init msm_dcvs_late_init(void)
873{
874 struct kobject *module_kobj = NULL;
875 int ret = 0;
876
877 module_kobj = kset_find_obj(module_kset, KBUILD_MODNAME);
878 if (!module_kobj) {
879 pr_err("%s: cannot find kobject for module %s\n",
880 __func__, KBUILD_MODNAME);
881 ret = -ENOENT;
882 goto err;
883 }
884
885 cores_kobj = kobject_create_and_add("cores", module_kobj);
886 if (!cores_kobj) {
887 __err("Cannot create %s kobject\n", "cores");
888 ret = -ENOMEM;
889 goto err;
890 }
891
892 debugfs_base = debugfs_create_dir("msm_dcvs", NULL);
893 if (!debugfs_base) {
894 __err("Cannot create debugfs base %s\n", "msm_dcvs");
895 ret = -ENOENT;
896 goto err;
897 }
898
899 if (!debugfs_create_u32("debug_mask", S_IRUGO | S_IWUSR,
900 debugfs_base, &msm_dcvs_debug)) {
901 __err("Cannot create debugfs entry %s\n", "debug_mask");
902 ret = -ENOMEM;
903 goto err;
904 }
905
906err:
907 if (ret) {
908 kobject_del(cores_kobj);
909 cores_kobj = NULL;
910 debugfs_remove(debugfs_base);
911 }
912
913 return ret;
914}
915late_initcall(msm_dcvs_late_init);
916
917static int __init msm_dcvs_early_init(void)
918{
919 int ret = 0;
920
921 if (!msm_dcvs_enabled) {
922 __info("Not enabled (%d)\n", msm_dcvs_enabled);
923 return 0;
924 }
925
926 ret = msm_dcvs_scm_init(10 * 1024);
927 if (ret)
928 __err("Unable to initialize DCVS err=%d\n", ret);
929
930 return ret;
931}
932postcore_initcall(msm_dcvs_early_init);