blob: 0505d44b82ba4f328d0fc295ad25d6b2e12d4662 [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];
77 uint32_t new_freq[MAX_PENDING];
78 uint32_t actual_freq;
79 uint32_t freq_change_us;
80
81 uint32_t max_time_us; /* core param */
82
83 struct msm_dcvs_algo_param algo_param;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -070084 struct msm_dcvs_energy_curve_coeffs coeffs;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070085 struct msm_dcvs_idle *idle_driver;
86 struct msm_dcvs_freq *freq_driver;
87
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;
94 uint32_t handle;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070095 uint32_t freq_pending;
96 struct hrtimer timer;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070097 int32_t timer_disabled;
Eugene Seah76af9832012-03-28 18:43:53 -060098 /* track if kthread for change_freq is active */
99 int32_t change_freq_activated;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700100 struct msm_dcvs_core_info *info;
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700101 int sensor;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700102};
103
104static int msm_dcvs_debug;
105static int msm_dcvs_enabled = 1;
106module_param_named(enable, msm_dcvs_enabled, int, S_IRUGO | S_IWUSR | S_IWGRP);
107
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700108static struct dentry *debugfs_base;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700109
110static struct dcvs_core core_list[CORES_MAX];
111static DEFINE_MUTEX(core_list_lock);
112
113static struct kobject *cores_kobj;
114static struct dcvs_core *core_handles[CORES_MAX];
115
116/* Change core frequency, called with core mutex locked */
117static int __msm_dcvs_change_freq(struct dcvs_core *core)
118{
119 int ret = 0;
120 unsigned long flags = 0;
121 unsigned int requested_freq = 0;
122 unsigned int prev_freq = 0;
123 int64_t time_start = 0;
124 int64_t time_end = 0;
125 uint32_t slack_us = 0;
126 uint32_t ret1 = 0;
127
128 if (!core->freq_driver || !core->freq_driver->set_frequency) {
129 /* Core may have unregistered or hotplugged */
130 return -ENODEV;
131 }
132repeat:
133 spin_lock_irqsave(&core->cpu_lock, flags);
134 if (unlikely(!core->freq_pending)) {
135 spin_unlock_irqrestore(&core->cpu_lock, flags);
136 return ret;
137 }
138 requested_freq = core->new_freq[core->freq_pending - 1];
139 if (unlikely(core->freq_pending > 1) &&
140 (msm_dcvs_debug & MSM_DCVS_DEBUG_FREQ_CHANGE)) {
141 int i;
142 for (i = 0; i < core->freq_pending - 1; i++) {
143 __info("Core %s missing freq %u\n",
144 core->core_name, core->new_freq[i]);
145 }
146 }
147 time_start = core->time_start;
148 core->time_start = 0;
149 core->freq_pending = 0;
150 /**
151 * Cancel the timers, we dont want the timer firing as we are
152 * changing the clock rate. Dont let idle_exit and others setup
153 * timers as well.
154 */
155 hrtimer_cancel(&core->timer);
156 core->timer_disabled = 1;
157 spin_unlock_irqrestore(&core->cpu_lock, flags);
158
159 if (requested_freq == core->actual_freq)
160 return ret;
161
162 /**
163 * Call the frequency sink driver to change the frequency
164 * We will need to get back the actual frequency in KHz and
165 * the record the time taken to change it.
166 */
167 ret = core->freq_driver->set_frequency(core->freq_driver,
168 requested_freq);
169 if (ret <= 0) {
170 __err("Core %s failed to set freq %u\n",
171 core->core_name, requested_freq);
Eugene Seah76af9832012-03-28 18:43:53 -0600172 /* continue to call TZ to get updated slack timer */
173 } else {
174 prev_freq = core->actual_freq;
175 core->actual_freq = ret;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700176 }
177
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700178 time_end = ktime_to_ns(ktime_get());
179 if (msm_dcvs_debug & MSM_DCVS_DEBUG_FREQ_CHANGE)
180 __info("Core %s Time end %llu Time start: %llu\n",
181 core->core_name, time_end, time_start);
182 time_end -= time_start;
183 do_div(time_end, NSEC_PER_USEC);
184 core->freq_change_us = (uint32_t)time_end;
185
186 /**
187 * Disable low power modes if the actual frequency is >
188 * disable_pc_threshold.
189 */
190 if (core->actual_freq >
191 core->algo_param.disable_pc_threshold) {
192 core->idle_driver->enable(core->idle_driver,
193 MSM_DCVS_DISABLE_HIGH_LATENCY_MODES);
194 if (msm_dcvs_debug & MSM_DCVS_DEBUG_IDLE_PULSE)
195 __info("Disabling LPM for %s\n", core->core_name);
196 } else if (core->actual_freq <=
197 core->algo_param.disable_pc_threshold) {
198 core->idle_driver->enable(core->idle_driver,
199 MSM_DCVS_ENABLE_HIGH_LATENCY_MODES);
200 if (msm_dcvs_debug & MSM_DCVS_DEBUG_IDLE_PULSE)
201 __info("Enabling LPM for %s\n", core->core_name);
202 }
203
204 /**
205 * Update algorithm with new freq and time taken to change
206 * to this frequency and that will get us the new slack
207 * timer
208 */
209 ret = msm_dcvs_scm_event(core->handle, MSM_DCVS_SCM_CLOCK_FREQ_UPDATE,
210 core->actual_freq, (uint32_t)time_end, &slack_us, &ret1);
211 if (!ret) {
212 /* Reset the slack timer */
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700213 if (slack_us) {
214 core->timer_disabled = 0;
215 ret = hrtimer_start(&core->timer,
216 ktime_set(0, slack_us * 1000),
217 HRTIMER_MODE_REL_PINNED);
218 if (ret)
219 __err("Failed to register timer for core %s\n",
220 core->core_name);
221 }
222 } else {
223 __err("Error sending core (%s) freq change (%u)\n",
224 core->core_name, core->actual_freq);
225 }
226
227 if (msm_dcvs_debug & MSM_DCVS_DEBUG_FREQ_CHANGE)
228 __info("Freq %u requested for core %s (actual %u prev %u) "
229 "change time %u us slack time %u us\n",
230 requested_freq, core->core_name,
231 core->actual_freq, prev_freq,
Eugene Seah76af9832012-03-28 18:43:53 -0600232 core->freq_change_us, slack_us);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700233
234 /**
235 * By the time we are done with freq changes, we could be asked to
236 * change again. Check before exiting.
237 */
238 if (core->freq_pending)
239 goto repeat;
240
Eugene Seah76af9832012-03-28 18:43:53 -0600241 core->change_freq_activated = 0;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700242 return ret;
243}
244
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700245static int __msm_dcvs_report_temp(struct dcvs_core *core)
246{
247 struct msm_dcvs_core_info *info = core->info;
248 struct tsens_device tsens_dev;
249 int ret;
250 unsigned long temp = 0;
251
252 tsens_dev.sensor_num = core->sensor;
253 ret = tsens_get_temp(&tsens_dev, &temp);
254 if (!ret) {
255 tsens_dev.sensor_num = 0;
256 ret = tsens_get_temp(&tsens_dev, &temp);
257 if (!ret)
258 return -ENODEV;
259 }
260
261 ret = msm_dcvs_scm_set_power_params(core->handle, &info->power_param,
262 &info->freq_tbl[0], &core->coeffs);
263 return ret;
264}
265
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700266static int msm_dcvs_do_freq(void *data)
267{
268 struct dcvs_core *core = (struct dcvs_core *)data;
269 static struct sched_param param = {.sched_priority = MAX_RT_PRIO - 1};
270
271 sched_setscheduler(current, SCHED_FIFO, &param);
272 set_current_state(TASK_UNINTERRUPTIBLE);
273
274 while (!kthread_should_stop()) {
275 mutex_lock(&core->lock);
276 __msm_dcvs_change_freq(core);
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700277 __msm_dcvs_report_temp(core);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700278 mutex_unlock(&core->lock);
279
280 schedule();
281
282 if (kthread_should_stop())
283 break;
284
285 set_current_state(TASK_UNINTERRUPTIBLE);
286 }
287
288 __set_current_state(TASK_RUNNING);
289
290 return 0;
291}
292
293static int msm_dcvs_update_freq(struct dcvs_core *core,
Eugene Seah76af9832012-03-28 18:43:53 -0600294 enum msm_dcvs_scm_event event, uint32_t param0,
295 uint32_t *ret1, int *freq_changed)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700296{
297 int ret = 0;
298 unsigned long flags = 0;
299 uint32_t new_freq = 0;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700300
301 spin_lock_irqsave(&core->cpu_lock, flags);
302 ret = msm_dcvs_scm_event(core->handle, event, param0,
Eugene Seah76af9832012-03-28 18:43:53 -0600303 core->actual_freq, &new_freq, ret1);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700304 if (ret) {
305 __err("Error (%d) sending SCM event %d for core %s\n",
306 ret, event, core->core_name);
307 goto freq_done;
308 }
309
310 if ((core->actual_freq != new_freq) &&
311 (core->new_freq[core->freq_pending] != new_freq)) {
312 if (core->freq_pending >= MAX_PENDING - 1)
313 core->freq_pending = MAX_PENDING - 1;
314 core->new_freq[core->freq_pending++] = new_freq;
315 core->time_start = ktime_to_ns(ktime_get());
316
317 /* Schedule the frequency change */
318 if (!core->task)
319 __err("Uninitialized task for core %s\n",
320 core->core_name);
Eugene Seah76af9832012-03-28 18:43:53 -0600321 else {
322 if (freq_changed)
323 *freq_changed = 1;
324 core->change_freq_activated = 1;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700325 wake_up_process(core->task);
Eugene Seah76af9832012-03-28 18:43:53 -0600326 }
327 } else {
328 if (freq_changed)
329 *freq_changed = 0;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700330 }
331freq_done:
332 spin_unlock_irqrestore(&core->cpu_lock, flags);
333
334 return ret;
335}
336
337static enum hrtimer_restart msm_dcvs_core_slack_timer(struct hrtimer *timer)
338{
339 int ret = 0;
340 struct dcvs_core *core = container_of(timer, struct dcvs_core, timer);
Eugene Seah76af9832012-03-28 18:43:53 -0600341 uint32_t ret1;
342 uint32_t ret2;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700343
344 if (msm_dcvs_debug & MSM_DCVS_DEBUG_FREQ_CHANGE)
345 __info("Slack timer fired for core %s\n", core->core_name);
346
347 /**
348 * Timer expired, notify TZ
349 * Dont care about the third arg.
350 */
Eugene Seah76af9832012-03-28 18:43:53 -0600351 ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_QOS_TIMER_EXPIRED, 0,
352 &ret1, &ret2);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700353 if (ret)
354 __err("Timer expired for core %s but failed to notify.\n",
355 core->core_name);
356
357 return HRTIMER_NORESTART;
358}
359
360/* Helper functions and macros for sysfs nodes for a core */
361#define CORE_FROM_ATTRIBS(attr, name) \
362 container_of(container_of(attr, struct core_attribs, name), \
363 struct dcvs_core, attrib);
364
365#define DCVS_PARAM_SHOW(_name, v) \
366static ssize_t msm_dcvs_attr_##_name##_show(struct kobject *kobj, \
367 struct kobj_attribute *attr, char *buf) \
368{ \
369 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
370 return snprintf(buf, PAGE_SIZE, "%d\n", v); \
371}
372
373#define DCVS_ALGO_PARAM(_name) \
374static ssize_t msm_dcvs_attr_##_name##_show(struct kobject *kobj,\
375 struct kobj_attribute *attr, char *buf) \
376{ \
377 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
378 return snprintf(buf, PAGE_SIZE, "%d\n", core->algo_param._name); \
379} \
380static ssize_t msm_dcvs_attr_##_name##_store(struct kobject *kobj, \
381 struct kobj_attribute *attr, const char *buf, size_t count) \
382{ \
383 int ret = 0; \
384 uint32_t val = 0; \
385 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
386 mutex_lock(&core->lock); \
387 ret = kstrtouint(buf, 10, &val); \
388 if (ret) { \
389 __err("Invalid input %s for %s\n", buf, __stringify(_name));\
390 } else { \
391 uint32_t old_val = core->algo_param._name; \
392 core->algo_param._name = val; \
393 ret = msm_dcvs_scm_set_algo_params(core->handle, \
394 &core->algo_param); \
395 if (ret) { \
396 core->algo_param._name = old_val; \
397 __err("Error(%d) in setting %d for algo param %s\n",\
398 ret, val, __stringify(_name)); \
399 } \
400 } \
401 mutex_unlock(&core->lock); \
402 return count; \
403}
404
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700405#define DCVS_ENERGY_PARAM(_name) \
406static ssize_t msm_dcvs_attr_##_name##_show(struct kobject *kobj,\
407 struct kobj_attribute *attr, char *buf) \
408{ \
409 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
410 return snprintf(buf, PAGE_SIZE, "%d\n", core->coeffs._name); \
411} \
412static ssize_t msm_dcvs_attr_##_name##_store(struct kobject *kobj, \
413 struct kobj_attribute *attr, const char *buf, size_t count) \
414{ \
415 int ret = 0; \
416 int32_t val = 0; \
417 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
418 mutex_lock(&core->lock); \
419 ret = kstrtoint(buf, 10, &val); \
420 if (ret) { \
421 __err("Invalid input %s for %s\n", buf, __stringify(_name));\
422 } else { \
423 int32_t old_val = core->coeffs._name; \
424 core->coeffs._name = val; \
425 ret = msm_dcvs_scm_set_power_params(core->handle, \
426 &core->info->power_param, &core->info->freq_tbl[0], \
427 &core->coeffs); \
428 if (ret) { \
429 core->coeffs._name = old_val; \
430 __err("Error(%d) in setting %d for coeffs param %s\n",\
431 ret, val, __stringify(_name)); \
432 } \
433 } \
434 mutex_unlock(&core->lock); \
435 return count; \
436}
437
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700438#define DCVS_RO_ATTRIB(i, _name) \
439 core->attrib._name.attr.name = __stringify(_name); \
440 core->attrib._name.attr.mode = S_IRUGO; \
441 core->attrib._name.show = msm_dcvs_attr_##_name##_show; \
442 core->attrib._name.store = NULL; \
443 core->attrib.attrib_group.attrs[i] = &core->attrib._name.attr;
444
445#define DCVS_RW_ATTRIB(i, _name) \
446 core->attrib._name.attr.name = __stringify(_name); \
447 core->attrib._name.attr.mode = S_IRUGO | S_IWUSR; \
448 core->attrib._name.show = msm_dcvs_attr_##_name##_show; \
449 core->attrib._name.store = msm_dcvs_attr_##_name##_store; \
450 core->attrib.attrib_group.attrs[i] = &core->attrib._name.attr;
451
452/**
453 * Function declarations for different attributes.
454 * Gets used when setting the attribute show and store parameters.
455 */
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700456DCVS_PARAM_SHOW(core_id, core->handle)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700457DCVS_PARAM_SHOW(idle_enabled, (core->idle_driver != NULL))
458DCVS_PARAM_SHOW(freq_change_enabled, (core->freq_driver != NULL))
459DCVS_PARAM_SHOW(actual_freq, (core->actual_freq))
460DCVS_PARAM_SHOW(freq_change_us, (core->freq_change_us))
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700461
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700462DCVS_ALGO_PARAM(disable_pc_threshold)
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700463DCVS_ALGO_PARAM(em_win_size_min_us)
464DCVS_ALGO_PARAM(em_win_size_max_us)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700465DCVS_ALGO_PARAM(em_max_util_pct)
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700466DCVS_ALGO_PARAM(group_id)
467DCVS_ALGO_PARAM(max_freq_chg_time_us)
468DCVS_ALGO_PARAM(slack_mode_dynamic)
469DCVS_ALGO_PARAM(slack_time_min_us)
470DCVS_ALGO_PARAM(slack_time_max_us)
471DCVS_ALGO_PARAM(slack_weight_thresh_pct)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700472DCVS_ALGO_PARAM(ss_iobusy_conv)
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700473DCVS_ALGO_PARAM(ss_win_size_min_us)
474DCVS_ALGO_PARAM(ss_win_size_max_us)
475DCVS_ALGO_PARAM(ss_util_pct)
476
477DCVS_ENERGY_PARAM(active_coeff_a)
478DCVS_ENERGY_PARAM(active_coeff_b)
479DCVS_ENERGY_PARAM(active_coeff_c)
480DCVS_ENERGY_PARAM(leakage_coeff_a)
481DCVS_ENERGY_PARAM(leakage_coeff_b)
482DCVS_ENERGY_PARAM(leakage_coeff_c)
483DCVS_ENERGY_PARAM(leakage_coeff_d)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700484
485static int msm_dcvs_setup_core_sysfs(struct dcvs_core *core)
486{
487 int ret = 0;
488 struct kobject *core_kobj = NULL;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700489 const int attr_count = 27;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700490
491 BUG_ON(!cores_kobj);
492
493 core->attrib.attrib_group.attrs =
494 kzalloc(attr_count * sizeof(struct attribute *), GFP_KERNEL);
495
496 if (!core->attrib.attrib_group.attrs) {
497 ret = -ENOMEM;
498 goto done;
499 }
500
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700501
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700502 DCVS_RO_ATTRIB(0, core_id);
503 DCVS_RO_ATTRIB(1, idle_enabled);
504 DCVS_RO_ATTRIB(2, freq_change_enabled);
505 DCVS_RO_ATTRIB(3, actual_freq);
506 DCVS_RO_ATTRIB(4, freq_change_us);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700507
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700508 DCVS_RW_ATTRIB(5, disable_pc_threshold);
509 DCVS_RW_ATTRIB(6, em_win_size_min_us);
510 DCVS_RW_ATTRIB(7, em_win_size_max_us);
511 DCVS_RW_ATTRIB(8, em_max_util_pct);
512 DCVS_RW_ATTRIB(9, group_id);
513 DCVS_RW_ATTRIB(10, max_freq_chg_time_us);
514 DCVS_RW_ATTRIB(11, slack_mode_dynamic);
515 DCVS_RW_ATTRIB(12, slack_time_min_us);
516 DCVS_RW_ATTRIB(13, slack_time_max_us);
517 DCVS_RW_ATTRIB(14, slack_weight_thresh_pct);
518 DCVS_RW_ATTRIB(15, ss_iobusy_conv);
519 DCVS_RW_ATTRIB(16, ss_win_size_min_us);
520 DCVS_RW_ATTRIB(17, ss_win_size_max_us);
521 DCVS_RW_ATTRIB(18, ss_util_pct);
522
523 DCVS_RW_ATTRIB(19, active_coeff_a);
524 DCVS_RW_ATTRIB(20, active_coeff_b);
525 DCVS_RW_ATTRIB(21, active_coeff_c);
526 DCVS_RW_ATTRIB(22, leakage_coeff_a);
527 DCVS_RW_ATTRIB(23, leakage_coeff_b);
528 DCVS_RW_ATTRIB(24, leakage_coeff_c);
529 DCVS_RW_ATTRIB(25, leakage_coeff_d);
530
531 core->attrib.attrib_group.attrs[26] = NULL;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700532
533 core_kobj = kobject_create_and_add(core->core_name, cores_kobj);
534 if (!core_kobj) {
535 ret = -ENOMEM;
536 goto done;
537 }
538
539 ret = sysfs_create_group(core_kobj, &core->attrib.attrib_group);
540 if (ret)
541 __err("Cannot create core %s attr group\n", core->core_name);
542 else if (msm_dcvs_debug & MSM_DCVS_DEBUG_NOTIFIER)
543 __info("Setting up attributes for core %s\n", core->core_name);
544
545done:
546 if (ret) {
547 kfree(core->attrib.attrib_group.attrs);
548 kobject_del(core_kobj);
549 }
550
551 return ret;
552}
553
554/* Return the core if found or add to list if @add_to_list is true */
555static struct dcvs_core *msm_dcvs_get_core(const char *name, int add_to_list)
556{
557 struct dcvs_core *core = NULL;
558 int i;
559 int empty = -1;
560
561 if (!name[0] ||
562 (strnlen(name, CORE_NAME_MAX - 1) == CORE_NAME_MAX - 1))
563 return core;
564
565 mutex_lock(&core_list_lock);
566 for (i = 0; i < CORES_MAX; i++) {
567 core = &core_list[i];
568 if ((empty < 0) && !core->core_name[0]) {
569 empty = i;
570 continue;
571 }
572 if (!strncmp(name, core->core_name, CORE_NAME_MAX))
573 break;
574 }
575
576 /* Check for core_list full */
577 if ((i == CORES_MAX) && (empty < 0)) {
578 mutex_unlock(&core_list_lock);
579 return NULL;
580 }
581
582 if (i == CORES_MAX && add_to_list) {
583 core = &core_list[empty];
584 strlcpy(core->core_name, name, CORE_NAME_MAX);
585 mutex_init(&core->lock);
586 spin_lock_init(&core->cpu_lock);
587 core->handle = empty + CORE_HANDLE_OFFSET;
588 hrtimer_init(&core->timer,
589 CLOCK_MONOTONIC, HRTIMER_MODE_REL_PINNED);
590 core->timer.function = msm_dcvs_core_slack_timer;
591 }
592 mutex_unlock(&core_list_lock);
593
594 return core;
595}
596
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700597int msm_dcvs_register_core(const char *core_name,
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700598 struct msm_dcvs_core_info *info, int sensor)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700599{
600 int ret = -EINVAL;
601 struct dcvs_core *core = NULL;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700602 uint32_t ret1;
603 uint32_t ret2;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700604
605 if (!core_name || !core_name[0])
606 return ret;
607
608 core = msm_dcvs_get_core(core_name, true);
609 if (!core)
610 return ret;
611
612 mutex_lock(&core->lock);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700613
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700614 core->info = info;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700615 memcpy(&core->algo_param, &info->algo_param,
616 sizeof(struct msm_dcvs_algo_param));
617
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700618 memcpy(&core->coeffs, &info->energy_coeffs,
619 sizeof(struct msm_dcvs_energy_curve_coeffs));
620
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700621 pr_debug("registering core with sensor %d\n", sensor);
622 core->sensor = sensor;
623 ret = msm_dcvs_scm_register_core(core->handle,
624 &info->core_param);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700625 if (ret)
626 goto bail;
627
628 ret = msm_dcvs_scm_set_algo_params(core->handle, &info->algo_param);
629 if (ret)
630 goto bail;
631
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700632 ret = msm_dcvs_scm_set_power_params(core->handle, &info->power_param,
633 &info->freq_tbl[0], &core->coeffs);
634 if (ret)
635 goto bail;
636
637 ret = msm_dcvs_scm_event(core->handle, MSM_DCVS_SCM_CORE_ONLINE,
638 core->actual_freq, 0, &ret1, &ret2);
639 if (ret)
640 goto bail;
641
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700642 ret = msm_dcvs_setup_core_sysfs(core);
643 if (ret) {
644 __err("Unable to setup core %s sysfs\n", core->core_name);
645 core_handles[core->handle - CORE_HANDLE_OFFSET] = NULL;
646 goto bail;
647 }
648
649bail:
650 mutex_unlock(&core->lock);
651 return ret;
652}
653EXPORT_SYMBOL(msm_dcvs_register_core);
654
655int msm_dcvs_freq_sink_register(struct msm_dcvs_freq *drv)
656{
657 int ret = -EINVAL;
658 struct dcvs_core *core = NULL;
Eugene Seah76af9832012-03-28 18:43:53 -0600659 uint32_t ret1;
660 uint32_t ret2;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700661
662 if (!drv || !drv->core_name)
663 return ret;
664
665 core = msm_dcvs_get_core(drv->core_name, true);
666 if (!core)
667 return ret;
668
669 mutex_lock(&core->lock);
670 if (core->freq_driver && (msm_dcvs_debug & MSM_DCVS_DEBUG_NOTIFIER))
671 __info("Frequency notifier for %s being replaced\n",
672 core->core_name);
673 core->freq_driver = drv;
674 core->task = kthread_create(msm_dcvs_do_freq, (void *)core,
675 "msm_dcvs/%d", core->handle);
676 if (IS_ERR(core->task)) {
677 mutex_unlock(&core->lock);
678 return -EFAULT;
679 }
680
681 if (msm_dcvs_debug & MSM_DCVS_DEBUG_IDLE_PULSE)
682 __info("Enabling idle pulse for %s\n", core->core_name);
683
684 if (core->idle_driver) {
685 core->actual_freq = core->freq_driver->get_frequency(drv);
686 /* Notify TZ to start receiving idle info for the core */
Abhijeet Dharmapurikar7b933c52012-08-23 15:51:58 -0700687 ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_DCVS_ENABLE, 1,
Eugene Seah76af9832012-03-28 18:43:53 -0600688 &ret1, &ret2);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700689 core->idle_driver->enable(core->idle_driver,
690 MSM_DCVS_ENABLE_IDLE_PULSE);
691 }
692
693 mutex_unlock(&core->lock);
694
695 return core->handle;
696}
697EXPORT_SYMBOL(msm_dcvs_freq_sink_register);
698
699int msm_dcvs_freq_sink_unregister(struct msm_dcvs_freq *drv)
700{
701 int ret = -EINVAL;
702 struct dcvs_core *core = NULL;
Eugene Seah76af9832012-03-28 18:43:53 -0600703 uint32_t ret1;
704 uint32_t ret2;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700705
706 if (!drv || !drv->core_name)
707 return ret;
708
709 core = msm_dcvs_get_core(drv->core_name, false);
710 if (!core)
711 return ret;
712
713 mutex_lock(&core->lock);
714 if (msm_dcvs_debug & MSM_DCVS_DEBUG_IDLE_PULSE)
715 __info("Disabling idle pulse for %s\n", core->core_name);
716 if (core->idle_driver) {
717 core->idle_driver->enable(core->idle_driver,
718 MSM_DCVS_DISABLE_IDLE_PULSE);
719 /* Notify TZ to stop receiving idle info for the core */
Abhijeet Dharmapurikar7b933c52012-08-23 15:51:58 -0700720 ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_DCVS_ENABLE, 0,
Eugene Seah76af9832012-03-28 18:43:53 -0600721 &ret1, &ret2);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700722 hrtimer_cancel(&core->timer);
723 core->idle_driver->enable(core->idle_driver,
724 MSM_DCVS_ENABLE_HIGH_LATENCY_MODES);
725 if (msm_dcvs_debug & MSM_DCVS_DEBUG_IDLE_PULSE)
726 __info("Enabling LPM for %s\n", core->core_name);
727 }
728 core->freq_pending = 0;
729 core->freq_driver = NULL;
730 mutex_unlock(&core->lock);
731 kthread_stop(core->task);
732
733 return 0;
734}
735EXPORT_SYMBOL(msm_dcvs_freq_sink_unregister);
736
737int msm_dcvs_idle_source_register(struct msm_dcvs_idle *drv)
738{
739 int ret = -EINVAL;
740 struct dcvs_core *core = NULL;
741
742 if (!drv || !drv->core_name)
743 return ret;
744
745 core = msm_dcvs_get_core(drv->core_name, true);
746 if (!core)
747 return ret;
748
749 mutex_lock(&core->lock);
750 if (core->idle_driver && (msm_dcvs_debug & MSM_DCVS_DEBUG_NOTIFIER))
751 __info("Idle notifier for %s being replaced\n",
752 core->core_name);
753 core->idle_driver = drv;
754 mutex_unlock(&core->lock);
755
756 return core->handle;
757}
758EXPORT_SYMBOL(msm_dcvs_idle_source_register);
759
760int msm_dcvs_idle_source_unregister(struct msm_dcvs_idle *drv)
761{
762 int ret = -EINVAL;
763 struct dcvs_core *core = NULL;
764
765 if (!drv || !drv->core_name)
766 return ret;
767
768 core = msm_dcvs_get_core(drv->core_name, false);
769 if (!core)
770 return ret;
771
772 mutex_lock(&core->lock);
773 core->idle_driver = NULL;
774 mutex_unlock(&core->lock);
775
776 return 0;
777}
778EXPORT_SYMBOL(msm_dcvs_idle_source_unregister);
779
780int msm_dcvs_idle(int handle, enum msm_core_idle_state state, uint32_t iowaited)
781{
782 int ret = 0;
783 struct dcvs_core *core = NULL;
Eugene Seah76af9832012-03-28 18:43:53 -0600784 uint32_t timer_interval_us = 0;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700785 uint32_t r0, r1;
Eugene Seah76af9832012-03-28 18:43:53 -0600786 uint32_t freq_changed = 0;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700787
788 if (handle >= CORE_HANDLE_OFFSET &&
789 (handle - CORE_HANDLE_OFFSET) < CORES_MAX)
790 core = &core_list[handle - CORE_HANDLE_OFFSET];
791
792 BUG_ON(!core);
793
794 if (msm_dcvs_debug & MSM_DCVS_DEBUG_IDLE_PULSE)
795 __info("Core %s idle state %d\n", core->core_name, state);
796
797 switch (state) {
798 case MSM_DCVS_IDLE_ENTER:
799 hrtimer_cancel(&core->timer);
800 ret = msm_dcvs_scm_event(core->handle,
801 MSM_DCVS_SCM_IDLE_ENTER, 0, 0, &r0, &r1);
802 if (ret)
803 __err("Error (%d) sending idle enter for %s\n",
804 ret, core->core_name);
805 break;
806
807 case MSM_DCVS_IDLE_EXIT:
808 hrtimer_cancel(&core->timer);
809 ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_IDLE_EXIT,
Eugene Seah76af9832012-03-28 18:43:53 -0600810 iowaited, &timer_interval_us, &freq_changed);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700811 if (ret)
812 __err("Error (%d) sending idle exit for %s\n",
813 ret, core->core_name);
Eugene Seah76af9832012-03-28 18:43:53 -0600814 /* only start slack timer if change_freq won't */
815 if (freq_changed || core->change_freq_activated)
816 break;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700817 if (timer_interval_us && !core->timer_disabled) {
818 ret = hrtimer_start(&core->timer,
819 ktime_set(0, timer_interval_us * 1000),
820 HRTIMER_MODE_REL_PINNED);
Eugene Seah76af9832012-03-28 18:43:53 -0600821
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700822 if (ret)
823 __err("Failed to register timer for core %s\n",
Eugene Seah76af9832012-03-28 18:43:53 -0600824 core->core_name);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700825 }
826 break;
827 }
828
829 return ret;
830}
831EXPORT_SYMBOL(msm_dcvs_idle);
832
833static int __init msm_dcvs_late_init(void)
834{
835 struct kobject *module_kobj = NULL;
836 int ret = 0;
837
838 module_kobj = kset_find_obj(module_kset, KBUILD_MODNAME);
839 if (!module_kobj) {
840 pr_err("%s: cannot find kobject for module %s\n",
841 __func__, KBUILD_MODNAME);
842 ret = -ENOENT;
843 goto err;
844 }
845
846 cores_kobj = kobject_create_and_add("cores", module_kobj);
847 if (!cores_kobj) {
848 __err("Cannot create %s kobject\n", "cores");
849 ret = -ENOMEM;
850 goto err;
851 }
852
853 debugfs_base = debugfs_create_dir("msm_dcvs", NULL);
854 if (!debugfs_base) {
855 __err("Cannot create debugfs base %s\n", "msm_dcvs");
856 ret = -ENOENT;
857 goto err;
858 }
859
860 if (!debugfs_create_u32("debug_mask", S_IRUGO | S_IWUSR,
861 debugfs_base, &msm_dcvs_debug)) {
862 __err("Cannot create debugfs entry %s\n", "debug_mask");
863 ret = -ENOMEM;
864 goto err;
865 }
866
867err:
868 if (ret) {
869 kobject_del(cores_kobj);
870 cores_kobj = NULL;
871 debugfs_remove(debugfs_base);
872 }
873
874 return ret;
875}
876late_initcall(msm_dcvs_late_init);
877
878static int __init msm_dcvs_early_init(void)
879{
880 int ret = 0;
881
882 if (!msm_dcvs_enabled) {
883 __info("Not enabled (%d)\n", msm_dcvs_enabled);
884 return 0;
885 }
886
887 ret = msm_dcvs_scm_init(10 * 1024);
888 if (ret)
889 __err("Unable to initialize DCVS err=%d\n", ret);
890
891 return ret;
892}
893postcore_initcall(msm_dcvs_early_init);