blob: 4ef120c5e852d7be5c243a5b249b7a16fc879854 [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;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070099};
100
101static int msm_dcvs_debug;
102static int msm_dcvs_enabled = 1;
103module_param_named(enable, msm_dcvs_enabled, int, S_IRUGO | S_IWUSR | S_IWGRP);
104
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700105static struct dentry *debugfs_base;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700106
107static struct dcvs_core core_list[CORES_MAX];
108static DEFINE_MUTEX(core_list_lock);
109
110static struct kobject *cores_kobj;
111static struct dcvs_core *core_handles[CORES_MAX];
112
113/* Change core frequency, called with core mutex locked */
114static int __msm_dcvs_change_freq(struct dcvs_core *core)
115{
116 int ret = 0;
117 unsigned long flags = 0;
118 unsigned int requested_freq = 0;
119 unsigned int prev_freq = 0;
120 int64_t time_start = 0;
121 int64_t time_end = 0;
122 uint32_t slack_us = 0;
123 uint32_t ret1 = 0;
124
125 if (!core->freq_driver || !core->freq_driver->set_frequency) {
126 /* Core may have unregistered or hotplugged */
127 return -ENODEV;
128 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700129 spin_lock_irqsave(&core->cpu_lock, flags);
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700130repeat:
131
132 requested_freq = core->pending_freq;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700133 time_start = core->time_start;
134 core->time_start = 0;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700135 /**
136 * Cancel the timers, we dont want the timer firing as we are
137 * changing the clock rate. Dont let idle_exit and others setup
138 * timers as well.
139 */
140 hrtimer_cancel(&core->timer);
141 core->timer_disabled = 1;
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700142 if (requested_freq == core->actual_freq)
143 goto out;
144
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700145 spin_unlock_irqrestore(&core->cpu_lock, flags);
146
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700147
148 /**
149 * Call the frequency sink driver to change the frequency
150 * We will need to get back the actual frequency in KHz and
151 * the record the time taken to change it.
152 */
153 ret = core->freq_driver->set_frequency(core->freq_driver,
154 requested_freq);
155 if (ret <= 0) {
156 __err("Core %s failed to set freq %u\n",
157 core->core_name, requested_freq);
Eugene Seah76af9832012-03-28 18:43:53 -0600158 /* continue to call TZ to get updated slack timer */
159 } else {
160 prev_freq = core->actual_freq;
161 core->actual_freq = ret;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700162 }
163
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700164 time_end = ktime_to_ns(ktime_get());
165 if (msm_dcvs_debug & MSM_DCVS_DEBUG_FREQ_CHANGE)
166 __info("Core %s Time end %llu Time start: %llu\n",
167 core->core_name, time_end, time_start);
168 time_end -= time_start;
169 do_div(time_end, NSEC_PER_USEC);
170 core->freq_change_us = (uint32_t)time_end;
171
172 /**
173 * Disable low power modes if the actual frequency is >
174 * disable_pc_threshold.
175 */
176 if (core->actual_freq >
177 core->algo_param.disable_pc_threshold) {
178 core->idle_driver->enable(core->idle_driver,
179 MSM_DCVS_DISABLE_HIGH_LATENCY_MODES);
180 if (msm_dcvs_debug & MSM_DCVS_DEBUG_IDLE_PULSE)
181 __info("Disabling LPM for %s\n", core->core_name);
182 } else if (core->actual_freq <=
183 core->algo_param.disable_pc_threshold) {
184 core->idle_driver->enable(core->idle_driver,
185 MSM_DCVS_ENABLE_HIGH_LATENCY_MODES);
186 if (msm_dcvs_debug & MSM_DCVS_DEBUG_IDLE_PULSE)
187 __info("Enabling LPM for %s\n", core->core_name);
188 }
189
190 /**
191 * Update algorithm with new freq and time taken to change
192 * to this frequency and that will get us the new slack
193 * timer
194 */
195 ret = msm_dcvs_scm_event(core->handle, MSM_DCVS_SCM_CLOCK_FREQ_UPDATE,
196 core->actual_freq, (uint32_t)time_end, &slack_us, &ret1);
197 if (!ret) {
198 /* Reset the slack timer */
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700199 if (slack_us) {
200 core->timer_disabled = 0;
201 ret = hrtimer_start(&core->timer,
202 ktime_set(0, slack_us * 1000),
203 HRTIMER_MODE_REL_PINNED);
204 if (ret)
205 __err("Failed to register timer for core %s\n",
206 core->core_name);
207 }
208 } else {
209 __err("Error sending core (%s) freq change (%u)\n",
210 core->core_name, core->actual_freq);
211 }
212
213 if (msm_dcvs_debug & MSM_DCVS_DEBUG_FREQ_CHANGE)
214 __info("Freq %u requested for core %s (actual %u prev %u) "
215 "change time %u us slack time %u us\n",
216 requested_freq, core->core_name,
217 core->actual_freq, prev_freq,
Eugene Seah76af9832012-03-28 18:43:53 -0600218 core->freq_change_us, slack_us);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700219
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700220 spin_lock_irqsave(&core->cpu_lock, flags);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700221 /**
222 * By the time we are done with freq changes, we could be asked to
223 * change again. Check before exiting.
224 */
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700225 if (core->pending_freq)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700226 goto repeat;
227
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700228
229out: /* should always be jumped to with the spin_lock held */
230 core->pending_freq = 0;
231 spin_unlock_irqrestore(&core->cpu_lock, flags);
232
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700233 return ret;
234}
235
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700236static int __msm_dcvs_report_temp(struct dcvs_core *core)
237{
238 struct msm_dcvs_core_info *info = core->info;
239 struct tsens_device tsens_dev;
240 int ret;
241 unsigned long temp = 0;
242
243 tsens_dev.sensor_num = core->sensor;
244 ret = tsens_get_temp(&tsens_dev, &temp);
245 if (!ret) {
246 tsens_dev.sensor_num = 0;
247 ret = tsens_get_temp(&tsens_dev, &temp);
248 if (!ret)
249 return -ENODEV;
250 }
251
252 ret = msm_dcvs_scm_set_power_params(core->handle, &info->power_param,
253 &info->freq_tbl[0], &core->coeffs);
254 return ret;
255}
256
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700257static int msm_dcvs_do_freq(void *data)
258{
259 struct dcvs_core *core = (struct dcvs_core *)data;
260 static struct sched_param param = {.sched_priority = MAX_RT_PRIO - 1};
261
262 sched_setscheduler(current, SCHED_FIFO, &param);
263 set_current_state(TASK_UNINTERRUPTIBLE);
264
265 while (!kthread_should_stop()) {
266 mutex_lock(&core->lock);
267 __msm_dcvs_change_freq(core);
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700268 __msm_dcvs_report_temp(core);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700269 mutex_unlock(&core->lock);
270
271 schedule();
272
273 if (kthread_should_stop())
274 break;
275
276 set_current_state(TASK_UNINTERRUPTIBLE);
277 }
278
279 __set_current_state(TASK_RUNNING);
280
281 return 0;
282}
283
284static int msm_dcvs_update_freq(struct dcvs_core *core,
Eugene Seah76af9832012-03-28 18:43:53 -0600285 enum msm_dcvs_scm_event event, uint32_t param0,
286 uint32_t *ret1, int *freq_changed)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700287{
288 int ret = 0;
289 unsigned long flags = 0;
290 uint32_t new_freq = 0;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700291
292 spin_lock_irqsave(&core->cpu_lock, flags);
293 ret = msm_dcvs_scm_event(core->handle, event, param0,
Eugene Seah76af9832012-03-28 18:43:53 -0600294 core->actual_freq, &new_freq, ret1);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700295 if (ret) {
Jeff Ohlstein4aa4a2b2012-06-28 19:03:49 -0700296 if (ret == -13)
297 ret = 0;
298 else
299 __err("Error (%d) sending SCM event %d for core %s\n",
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700300 ret, event, core->core_name);
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700301 goto out;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700302 }
303
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700304 if (core->actual_freq != new_freq && core->pending_freq != new_freq) {
305 core->pending_freq = new_freq;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700306 core->time_start = ktime_to_ns(ktime_get());
307
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700308 if (core->task)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700309 wake_up_process(core->task);
Eugene Seah76af9832012-03-28 18:43:53 -0600310 } else {
311 if (freq_changed)
312 *freq_changed = 0;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700313 }
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700314out:
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700315 spin_unlock_irqrestore(&core->cpu_lock, flags);
316
317 return ret;
318}
319
320static enum hrtimer_restart msm_dcvs_core_slack_timer(struct hrtimer *timer)
321{
322 int ret = 0;
323 struct dcvs_core *core = container_of(timer, struct dcvs_core, timer);
Eugene Seah76af9832012-03-28 18:43:53 -0600324 uint32_t ret1;
325 uint32_t ret2;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700326
327 if (msm_dcvs_debug & MSM_DCVS_DEBUG_FREQ_CHANGE)
328 __info("Slack timer fired for core %s\n", core->core_name);
329
330 /**
331 * Timer expired, notify TZ
332 * Dont care about the third arg.
333 */
Eugene Seah76af9832012-03-28 18:43:53 -0600334 ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_QOS_TIMER_EXPIRED, 0,
335 &ret1, &ret2);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700336 if (ret)
337 __err("Timer expired for core %s but failed to notify.\n",
338 core->core_name);
339
340 return HRTIMER_NORESTART;
341}
342
343/* Helper functions and macros for sysfs nodes for a core */
344#define CORE_FROM_ATTRIBS(attr, name) \
345 container_of(container_of(attr, struct core_attribs, name), \
346 struct dcvs_core, attrib);
347
348#define DCVS_PARAM_SHOW(_name, v) \
349static ssize_t msm_dcvs_attr_##_name##_show(struct kobject *kobj, \
350 struct kobj_attribute *attr, char *buf) \
351{ \
352 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
353 return snprintf(buf, PAGE_SIZE, "%d\n", v); \
354}
355
356#define DCVS_ALGO_PARAM(_name) \
357static ssize_t msm_dcvs_attr_##_name##_show(struct kobject *kobj,\
358 struct kobj_attribute *attr, char *buf) \
359{ \
360 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
361 return snprintf(buf, PAGE_SIZE, "%d\n", core->algo_param._name); \
362} \
363static ssize_t msm_dcvs_attr_##_name##_store(struct kobject *kobj, \
364 struct kobj_attribute *attr, const char *buf, size_t count) \
365{ \
366 int ret = 0; \
367 uint32_t val = 0; \
368 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
369 mutex_lock(&core->lock); \
370 ret = kstrtouint(buf, 10, &val); \
371 if (ret) { \
372 __err("Invalid input %s for %s\n", buf, __stringify(_name));\
373 } else { \
374 uint32_t old_val = core->algo_param._name; \
375 core->algo_param._name = val; \
376 ret = msm_dcvs_scm_set_algo_params(core->handle, \
377 &core->algo_param); \
378 if (ret) { \
379 core->algo_param._name = old_val; \
380 __err("Error(%d) in setting %d for algo param %s\n",\
381 ret, val, __stringify(_name)); \
382 } \
383 } \
384 mutex_unlock(&core->lock); \
385 return count; \
386}
387
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700388#define DCVS_ENERGY_PARAM(_name) \
389static ssize_t msm_dcvs_attr_##_name##_show(struct kobject *kobj,\
390 struct kobj_attribute *attr, char *buf) \
391{ \
392 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
393 return snprintf(buf, PAGE_SIZE, "%d\n", core->coeffs._name); \
394} \
395static ssize_t msm_dcvs_attr_##_name##_store(struct kobject *kobj, \
396 struct kobj_attribute *attr, const char *buf, size_t count) \
397{ \
398 int ret = 0; \
399 int32_t val = 0; \
400 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
401 mutex_lock(&core->lock); \
402 ret = kstrtoint(buf, 10, &val); \
403 if (ret) { \
404 __err("Invalid input %s for %s\n", buf, __stringify(_name));\
405 } else { \
406 int32_t old_val = core->coeffs._name; \
407 core->coeffs._name = val; \
408 ret = msm_dcvs_scm_set_power_params(core->handle, \
409 &core->info->power_param, &core->info->freq_tbl[0], \
410 &core->coeffs); \
411 if (ret) { \
412 core->coeffs._name = old_val; \
413 __err("Error(%d) in setting %d for coeffs param %s\n",\
414 ret, val, __stringify(_name)); \
415 } \
416 } \
417 mutex_unlock(&core->lock); \
418 return count; \
419}
420
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700421#define DCVS_RO_ATTRIB(i, _name) \
422 core->attrib._name.attr.name = __stringify(_name); \
423 core->attrib._name.attr.mode = S_IRUGO; \
424 core->attrib._name.show = msm_dcvs_attr_##_name##_show; \
425 core->attrib._name.store = NULL; \
426 core->attrib.attrib_group.attrs[i] = &core->attrib._name.attr;
427
428#define DCVS_RW_ATTRIB(i, _name) \
429 core->attrib._name.attr.name = __stringify(_name); \
430 core->attrib._name.attr.mode = S_IRUGO | S_IWUSR; \
431 core->attrib._name.show = msm_dcvs_attr_##_name##_show; \
432 core->attrib._name.store = msm_dcvs_attr_##_name##_store; \
433 core->attrib.attrib_group.attrs[i] = &core->attrib._name.attr;
434
435/**
436 * Function declarations for different attributes.
437 * Gets used when setting the attribute show and store parameters.
438 */
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700439DCVS_PARAM_SHOW(core_id, core->handle)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700440DCVS_PARAM_SHOW(idle_enabled, (core->idle_driver != NULL))
441DCVS_PARAM_SHOW(freq_change_enabled, (core->freq_driver != NULL))
442DCVS_PARAM_SHOW(actual_freq, (core->actual_freq))
443DCVS_PARAM_SHOW(freq_change_us, (core->freq_change_us))
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700444
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700445DCVS_ALGO_PARAM(disable_pc_threshold)
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700446DCVS_ALGO_PARAM(em_win_size_min_us)
447DCVS_ALGO_PARAM(em_win_size_max_us)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700448DCVS_ALGO_PARAM(em_max_util_pct)
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700449DCVS_ALGO_PARAM(group_id)
450DCVS_ALGO_PARAM(max_freq_chg_time_us)
451DCVS_ALGO_PARAM(slack_mode_dynamic)
452DCVS_ALGO_PARAM(slack_time_min_us)
453DCVS_ALGO_PARAM(slack_time_max_us)
454DCVS_ALGO_PARAM(slack_weight_thresh_pct)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700455DCVS_ALGO_PARAM(ss_iobusy_conv)
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700456DCVS_ALGO_PARAM(ss_win_size_min_us)
457DCVS_ALGO_PARAM(ss_win_size_max_us)
458DCVS_ALGO_PARAM(ss_util_pct)
459
460DCVS_ENERGY_PARAM(active_coeff_a)
461DCVS_ENERGY_PARAM(active_coeff_b)
462DCVS_ENERGY_PARAM(active_coeff_c)
463DCVS_ENERGY_PARAM(leakage_coeff_a)
464DCVS_ENERGY_PARAM(leakage_coeff_b)
465DCVS_ENERGY_PARAM(leakage_coeff_c)
466DCVS_ENERGY_PARAM(leakage_coeff_d)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700467
468static int msm_dcvs_setup_core_sysfs(struct dcvs_core *core)
469{
470 int ret = 0;
471 struct kobject *core_kobj = NULL;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700472 const int attr_count = 27;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700473
474 BUG_ON(!cores_kobj);
475
476 core->attrib.attrib_group.attrs =
477 kzalloc(attr_count * sizeof(struct attribute *), GFP_KERNEL);
478
479 if (!core->attrib.attrib_group.attrs) {
480 ret = -ENOMEM;
481 goto done;
482 }
483
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700484
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700485 DCVS_RO_ATTRIB(0, core_id);
486 DCVS_RO_ATTRIB(1, idle_enabled);
487 DCVS_RO_ATTRIB(2, freq_change_enabled);
488 DCVS_RO_ATTRIB(3, actual_freq);
489 DCVS_RO_ATTRIB(4, freq_change_us);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700490
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700491 DCVS_RW_ATTRIB(5, disable_pc_threshold);
492 DCVS_RW_ATTRIB(6, em_win_size_min_us);
493 DCVS_RW_ATTRIB(7, em_win_size_max_us);
494 DCVS_RW_ATTRIB(8, em_max_util_pct);
495 DCVS_RW_ATTRIB(9, group_id);
496 DCVS_RW_ATTRIB(10, max_freq_chg_time_us);
497 DCVS_RW_ATTRIB(11, slack_mode_dynamic);
498 DCVS_RW_ATTRIB(12, slack_time_min_us);
499 DCVS_RW_ATTRIB(13, slack_time_max_us);
500 DCVS_RW_ATTRIB(14, slack_weight_thresh_pct);
501 DCVS_RW_ATTRIB(15, ss_iobusy_conv);
502 DCVS_RW_ATTRIB(16, ss_win_size_min_us);
503 DCVS_RW_ATTRIB(17, ss_win_size_max_us);
504 DCVS_RW_ATTRIB(18, ss_util_pct);
505
506 DCVS_RW_ATTRIB(19, active_coeff_a);
507 DCVS_RW_ATTRIB(20, active_coeff_b);
508 DCVS_RW_ATTRIB(21, active_coeff_c);
509 DCVS_RW_ATTRIB(22, leakage_coeff_a);
510 DCVS_RW_ATTRIB(23, leakage_coeff_b);
511 DCVS_RW_ATTRIB(24, leakage_coeff_c);
512 DCVS_RW_ATTRIB(25, leakage_coeff_d);
513
514 core->attrib.attrib_group.attrs[26] = NULL;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700515
516 core_kobj = kobject_create_and_add(core->core_name, cores_kobj);
517 if (!core_kobj) {
518 ret = -ENOMEM;
519 goto done;
520 }
521
522 ret = sysfs_create_group(core_kobj, &core->attrib.attrib_group);
523 if (ret)
524 __err("Cannot create core %s attr group\n", core->core_name);
525 else if (msm_dcvs_debug & MSM_DCVS_DEBUG_NOTIFIER)
526 __info("Setting up attributes for core %s\n", core->core_name);
527
528done:
529 if (ret) {
530 kfree(core->attrib.attrib_group.attrs);
531 kobject_del(core_kobj);
532 }
533
534 return ret;
535}
536
537/* Return the core if found or add to list if @add_to_list is true */
538static struct dcvs_core *msm_dcvs_get_core(const char *name, int add_to_list)
539{
540 struct dcvs_core *core = NULL;
541 int i;
542 int empty = -1;
543
544 if (!name[0] ||
545 (strnlen(name, CORE_NAME_MAX - 1) == CORE_NAME_MAX - 1))
546 return core;
547
548 mutex_lock(&core_list_lock);
549 for (i = 0; i < CORES_MAX; i++) {
550 core = &core_list[i];
551 if ((empty < 0) && !core->core_name[0]) {
552 empty = i;
553 continue;
554 }
555 if (!strncmp(name, core->core_name, CORE_NAME_MAX))
556 break;
557 }
558
559 /* Check for core_list full */
560 if ((i == CORES_MAX) && (empty < 0)) {
561 mutex_unlock(&core_list_lock);
562 return NULL;
563 }
564
565 if (i == CORES_MAX && add_to_list) {
566 core = &core_list[empty];
567 strlcpy(core->core_name, name, CORE_NAME_MAX);
568 mutex_init(&core->lock);
569 spin_lock_init(&core->cpu_lock);
570 core->handle = empty + CORE_HANDLE_OFFSET;
571 hrtimer_init(&core->timer,
572 CLOCK_MONOTONIC, HRTIMER_MODE_REL_PINNED);
573 core->timer.function = msm_dcvs_core_slack_timer;
574 }
575 mutex_unlock(&core_list_lock);
576
577 return core;
578}
579
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700580int msm_dcvs_register_core(const char *core_name,
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700581 struct msm_dcvs_core_info *info, int sensor)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700582{
583 int ret = -EINVAL;
584 struct dcvs_core *core = NULL;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700585 uint32_t ret1;
586 uint32_t ret2;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700587
588 if (!core_name || !core_name[0])
589 return ret;
590
591 core = msm_dcvs_get_core(core_name, true);
592 if (!core)
593 return ret;
594
595 mutex_lock(&core->lock);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700596
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700597 core->info = info;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700598 memcpy(&core->algo_param, &info->algo_param,
599 sizeof(struct msm_dcvs_algo_param));
600
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700601 memcpy(&core->coeffs, &info->energy_coeffs,
602 sizeof(struct msm_dcvs_energy_curve_coeffs));
603
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700604 pr_debug("registering core with sensor %d\n", sensor);
605 core->sensor = sensor;
606 ret = msm_dcvs_scm_register_core(core->handle,
607 &info->core_param);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700608 if (ret)
609 goto bail;
610
611 ret = msm_dcvs_scm_set_algo_params(core->handle, &info->algo_param);
612 if (ret)
613 goto bail;
614
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700615 ret = msm_dcvs_scm_set_power_params(core->handle, &info->power_param,
616 &info->freq_tbl[0], &core->coeffs);
617 if (ret)
618 goto bail;
619
620 ret = msm_dcvs_scm_event(core->handle, MSM_DCVS_SCM_CORE_ONLINE,
621 core->actual_freq, 0, &ret1, &ret2);
622 if (ret)
623 goto bail;
624
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700625 ret = msm_dcvs_setup_core_sysfs(core);
626 if (ret) {
627 __err("Unable to setup core %s sysfs\n", core->core_name);
628 core_handles[core->handle - CORE_HANDLE_OFFSET] = NULL;
629 goto bail;
630 }
631
Abhijeet Dharmapurikar74f10832012-08-26 22:40:28 -0700632 core->task = kthread_create(msm_dcvs_do_freq, (void *)core,
633 "msm_dcvs/%d", core->handle);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700634bail:
635 mutex_unlock(&core->lock);
636 return ret;
637}
638EXPORT_SYMBOL(msm_dcvs_register_core);
639
640int msm_dcvs_freq_sink_register(struct msm_dcvs_freq *drv)
641{
642 int ret = -EINVAL;
643 struct dcvs_core *core = NULL;
Eugene Seah76af9832012-03-28 18:43:53 -0600644 uint32_t ret1;
645 uint32_t ret2;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700646
647 if (!drv || !drv->core_name)
648 return ret;
649
650 core = msm_dcvs_get_core(drv->core_name, true);
651 if (!core)
652 return ret;
653
654 mutex_lock(&core->lock);
655 if (core->freq_driver && (msm_dcvs_debug & MSM_DCVS_DEBUG_NOTIFIER))
656 __info("Frequency notifier for %s being replaced\n",
657 core->core_name);
658 core->freq_driver = drv;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700659 if (IS_ERR(core->task)) {
660 mutex_unlock(&core->lock);
661 return -EFAULT;
662 }
663
664 if (msm_dcvs_debug & MSM_DCVS_DEBUG_IDLE_PULSE)
665 __info("Enabling idle pulse for %s\n", core->core_name);
666
667 if (core->idle_driver) {
668 core->actual_freq = core->freq_driver->get_frequency(drv);
669 /* Notify TZ to start receiving idle info for the core */
Abhijeet Dharmapurikar7b933c52012-08-23 15:51:58 -0700670 ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_DCVS_ENABLE, 1,
Eugene Seah76af9832012-03-28 18:43:53 -0600671 &ret1, &ret2);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700672 core->idle_driver->enable(core->idle_driver,
673 MSM_DCVS_ENABLE_IDLE_PULSE);
674 }
675
676 mutex_unlock(&core->lock);
677
678 return core->handle;
679}
680EXPORT_SYMBOL(msm_dcvs_freq_sink_register);
681
682int msm_dcvs_freq_sink_unregister(struct msm_dcvs_freq *drv)
683{
684 int ret = -EINVAL;
685 struct dcvs_core *core = NULL;
Eugene Seah76af9832012-03-28 18:43:53 -0600686 uint32_t ret1;
687 uint32_t ret2;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700688
689 if (!drv || !drv->core_name)
690 return ret;
691
692 core = msm_dcvs_get_core(drv->core_name, false);
693 if (!core)
694 return ret;
695
696 mutex_lock(&core->lock);
697 if (msm_dcvs_debug & MSM_DCVS_DEBUG_IDLE_PULSE)
698 __info("Disabling idle pulse for %s\n", core->core_name);
699 if (core->idle_driver) {
700 core->idle_driver->enable(core->idle_driver,
701 MSM_DCVS_DISABLE_IDLE_PULSE);
702 /* Notify TZ to stop receiving idle info for the core */
Abhijeet Dharmapurikar7b933c52012-08-23 15:51:58 -0700703 ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_DCVS_ENABLE, 0,
Eugene Seah76af9832012-03-28 18:43:53 -0600704 &ret1, &ret2);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700705 hrtimer_cancel(&core->timer);
706 core->idle_driver->enable(core->idle_driver,
707 MSM_DCVS_ENABLE_HIGH_LATENCY_MODES);
708 if (msm_dcvs_debug & MSM_DCVS_DEBUG_IDLE_PULSE)
709 __info("Enabling LPM for %s\n", core->core_name);
710 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700711 core->freq_driver = NULL;
712 mutex_unlock(&core->lock);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700713
714 return 0;
715}
716EXPORT_SYMBOL(msm_dcvs_freq_sink_unregister);
717
718int msm_dcvs_idle_source_register(struct msm_dcvs_idle *drv)
719{
720 int ret = -EINVAL;
721 struct dcvs_core *core = NULL;
722
723 if (!drv || !drv->core_name)
724 return ret;
725
726 core = msm_dcvs_get_core(drv->core_name, true);
727 if (!core)
728 return ret;
729
730 mutex_lock(&core->lock);
731 if (core->idle_driver && (msm_dcvs_debug & MSM_DCVS_DEBUG_NOTIFIER))
732 __info("Idle notifier for %s being replaced\n",
733 core->core_name);
734 core->idle_driver = drv;
735 mutex_unlock(&core->lock);
736
737 return core->handle;
738}
739EXPORT_SYMBOL(msm_dcvs_idle_source_register);
740
741int msm_dcvs_idle_source_unregister(struct msm_dcvs_idle *drv)
742{
743 int ret = -EINVAL;
744 struct dcvs_core *core = NULL;
745
746 if (!drv || !drv->core_name)
747 return ret;
748
749 core = msm_dcvs_get_core(drv->core_name, false);
750 if (!core)
751 return ret;
752
753 mutex_lock(&core->lock);
754 core->idle_driver = NULL;
755 mutex_unlock(&core->lock);
756
757 return 0;
758}
759EXPORT_SYMBOL(msm_dcvs_idle_source_unregister);
760
761int msm_dcvs_idle(int handle, enum msm_core_idle_state state, uint32_t iowaited)
762{
763 int ret = 0;
764 struct dcvs_core *core = NULL;
Eugene Seah76af9832012-03-28 18:43:53 -0600765 uint32_t timer_interval_us = 0;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700766 uint32_t r0, r1;
Eugene Seah76af9832012-03-28 18:43:53 -0600767 uint32_t freq_changed = 0;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700768
769 if (handle >= CORE_HANDLE_OFFSET &&
770 (handle - CORE_HANDLE_OFFSET) < CORES_MAX)
771 core = &core_list[handle - CORE_HANDLE_OFFSET];
772
773 BUG_ON(!core);
774
775 if (msm_dcvs_debug & MSM_DCVS_DEBUG_IDLE_PULSE)
776 __info("Core %s idle state %d\n", core->core_name, state);
777
778 switch (state) {
779 case MSM_DCVS_IDLE_ENTER:
780 hrtimer_cancel(&core->timer);
781 ret = msm_dcvs_scm_event(core->handle,
782 MSM_DCVS_SCM_IDLE_ENTER, 0, 0, &r0, &r1);
783 if (ret)
784 __err("Error (%d) sending idle enter for %s\n",
785 ret, core->core_name);
786 break;
787
788 case MSM_DCVS_IDLE_EXIT:
789 hrtimer_cancel(&core->timer);
790 ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_IDLE_EXIT,
Eugene Seah76af9832012-03-28 18:43:53 -0600791 iowaited, &timer_interval_us, &freq_changed);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700792 if (ret)
793 __err("Error (%d) sending idle exit for %s\n",
794 ret, core->core_name);
Eugene Seah76af9832012-03-28 18:43:53 -0600795 /* only start slack timer if change_freq won't */
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700796 if (freq_changed)
Eugene Seah76af9832012-03-28 18:43:53 -0600797 break;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700798 if (timer_interval_us && !core->timer_disabled) {
799 ret = hrtimer_start(&core->timer,
800 ktime_set(0, timer_interval_us * 1000),
801 HRTIMER_MODE_REL_PINNED);
Eugene Seah76af9832012-03-28 18:43:53 -0600802
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700803 if (ret)
804 __err("Failed to register timer for core %s\n",
Eugene Seah76af9832012-03-28 18:43:53 -0600805 core->core_name);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700806 }
807 break;
808 }
809
810 return ret;
811}
812EXPORT_SYMBOL(msm_dcvs_idle);
813
814static int __init msm_dcvs_late_init(void)
815{
816 struct kobject *module_kobj = NULL;
817 int ret = 0;
818
819 module_kobj = kset_find_obj(module_kset, KBUILD_MODNAME);
820 if (!module_kobj) {
821 pr_err("%s: cannot find kobject for module %s\n",
822 __func__, KBUILD_MODNAME);
823 ret = -ENOENT;
824 goto err;
825 }
826
827 cores_kobj = kobject_create_and_add("cores", module_kobj);
828 if (!cores_kobj) {
829 __err("Cannot create %s kobject\n", "cores");
830 ret = -ENOMEM;
831 goto err;
832 }
833
834 debugfs_base = debugfs_create_dir("msm_dcvs", NULL);
835 if (!debugfs_base) {
836 __err("Cannot create debugfs base %s\n", "msm_dcvs");
837 ret = -ENOENT;
838 goto err;
839 }
840
841 if (!debugfs_create_u32("debug_mask", S_IRUGO | S_IWUSR,
842 debugfs_base, &msm_dcvs_debug)) {
843 __err("Cannot create debugfs entry %s\n", "debug_mask");
844 ret = -ENOMEM;
845 goto err;
846 }
847
848err:
849 if (ret) {
850 kobject_del(cores_kobj);
851 cores_kobj = NULL;
852 debugfs_remove(debugfs_base);
853 }
854
855 return ret;
856}
857late_initcall(msm_dcvs_late_init);
858
859static int __init msm_dcvs_early_init(void)
860{
861 int ret = 0;
862
863 if (!msm_dcvs_enabled) {
864 __info("Not enabled (%d)\n", msm_dcvs_enabled);
865 return 0;
866 }
867
868 ret = msm_dcvs_scm_init(10 * 1024);
869 if (ret)
870 __err("Unable to initialize DCVS err=%d\n", ret);
871
872 return ret;
873}
874postcore_initcall(msm_dcvs_early_init);