blob: 288d6bb0464c3985a263db94f59df39fb88d960f [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);
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
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700128 if (!core->freq_driver || !core->set_frequency) {
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700129 /* Core may have unregistered or hotplugged */
130 return -ENODEV;
131 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700132 spin_lock_irqsave(&core->cpu_lock, flags);
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700133repeat:
134
135 requested_freq = core->pending_freq;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700136 time_start = core->time_start;
137 core->time_start = 0;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700138 /**
139 * Cancel the timers, we dont want the timer firing as we are
140 * changing the clock rate. Dont let idle_exit and others setup
141 * timers as well.
142 */
143 hrtimer_cancel(&core->timer);
144 core->timer_disabled = 1;
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700145 if (requested_freq == core->actual_freq)
146 goto out;
147
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700148 spin_unlock_irqrestore(&core->cpu_lock, flags);
149
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700150
151 /**
152 * Call the frequency sink driver to change the frequency
153 * We will need to get back the actual frequency in KHz and
154 * the record the time taken to change it.
155 */
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700156 ret = core->set_frequency(core->freq_driver, requested_freq);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700157 if (ret <= 0) {
158 __err("Core %s failed to set freq %u\n",
159 core->core_name, requested_freq);
Eugene Seah76af9832012-03-28 18:43:53 -0600160 /* continue to call TZ to get updated slack timer */
161 } else {
162 prev_freq = core->actual_freq;
163 core->actual_freq = ret;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700164 }
165
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700166 time_end = ktime_to_ns(ktime_get());
167 if (msm_dcvs_debug & MSM_DCVS_DEBUG_FREQ_CHANGE)
168 __info("Core %s Time end %llu Time start: %llu\n",
169 core->core_name, time_end, time_start);
170 time_end -= time_start;
171 do_div(time_end, NSEC_PER_USEC);
172 core->freq_change_us = (uint32_t)time_end;
173
174 /**
175 * Disable low power modes if the actual frequency is >
176 * disable_pc_threshold.
177 */
178 if (core->actual_freq >
179 core->algo_param.disable_pc_threshold) {
180 core->idle_driver->enable(core->idle_driver,
181 MSM_DCVS_DISABLE_HIGH_LATENCY_MODES);
182 if (msm_dcvs_debug & MSM_DCVS_DEBUG_IDLE_PULSE)
183 __info("Disabling LPM for %s\n", core->core_name);
184 } else if (core->actual_freq <=
185 core->algo_param.disable_pc_threshold) {
186 core->idle_driver->enable(core->idle_driver,
187 MSM_DCVS_ENABLE_HIGH_LATENCY_MODES);
188 if (msm_dcvs_debug & MSM_DCVS_DEBUG_IDLE_PULSE)
189 __info("Enabling LPM for %s\n", core->core_name);
190 }
191
192 /**
193 * Update algorithm with new freq and time taken to change
194 * to this frequency and that will get us the new slack
195 * timer
196 */
197 ret = msm_dcvs_scm_event(core->handle, MSM_DCVS_SCM_CLOCK_FREQ_UPDATE,
198 core->actual_freq, (uint32_t)time_end, &slack_us, &ret1);
199 if (!ret) {
200 /* Reset the slack timer */
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700201 if (slack_us) {
202 core->timer_disabled = 0;
203 ret = hrtimer_start(&core->timer,
204 ktime_set(0, slack_us * 1000),
205 HRTIMER_MODE_REL_PINNED);
206 if (ret)
207 __err("Failed to register timer for core %s\n",
208 core->core_name);
209 }
210 } else {
211 __err("Error sending core (%s) freq change (%u)\n",
212 core->core_name, core->actual_freq);
213 }
214
215 if (msm_dcvs_debug & MSM_DCVS_DEBUG_FREQ_CHANGE)
216 __info("Freq %u requested for core %s (actual %u prev %u) "
217 "change time %u us slack time %u us\n",
218 requested_freq, core->core_name,
219 core->actual_freq, prev_freq,
Eugene Seah76af9832012-03-28 18:43:53 -0600220 core->freq_change_us, slack_us);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700221
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700222 spin_lock_irqsave(&core->cpu_lock, flags);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700223 /**
224 * By the time we are done with freq changes, we could be asked to
225 * change again. Check before exiting.
226 */
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700227 if (core->pending_freq)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700228 goto repeat;
229
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700230
231out: /* should always be jumped to with the spin_lock held */
232 core->pending_freq = 0;
233 spin_unlock_irqrestore(&core->cpu_lock, flags);
234
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700235 return ret;
236}
237
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700238static int __msm_dcvs_report_temp(struct dcvs_core *core)
239{
240 struct msm_dcvs_core_info *info = core->info;
241 struct tsens_device tsens_dev;
242 int ret;
243 unsigned long temp = 0;
244
245 tsens_dev.sensor_num = core->sensor;
246 ret = tsens_get_temp(&tsens_dev, &temp);
247 if (!ret) {
248 tsens_dev.sensor_num = 0;
249 ret = tsens_get_temp(&tsens_dev, &temp);
250 if (!ret)
251 return -ENODEV;
252 }
253
254 ret = msm_dcvs_scm_set_power_params(core->handle, &info->power_param,
255 &info->freq_tbl[0], &core->coeffs);
256 return ret;
257}
258
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700259static int msm_dcvs_do_freq(void *data)
260{
261 struct dcvs_core *core = (struct dcvs_core *)data;
262 static struct sched_param param = {.sched_priority = MAX_RT_PRIO - 1};
263
264 sched_setscheduler(current, SCHED_FIFO, &param);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700265
266 while (!kthread_should_stop()) {
Abhijeet Dharmapurikarbbb52fe2012-08-31 20:31:16 -0700267 wait_event(core->wait_q, !(core->pending_freq == 0 ||
268 core->pending_freq == -1) ||
269 kthread_should_stop());
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700270
271 if (kthread_should_stop())
272 break;
273
Abhijeet Dharmapurikarbbb52fe2012-08-31 20:31:16 -0700274 mutex_lock(&core->lock);
275 __msm_dcvs_change_freq(core);
276 __msm_dcvs_report_temp(core);
277 mutex_unlock(&core->lock);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700278 }
279
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700280 return 0;
281}
282
283static int msm_dcvs_update_freq(struct dcvs_core *core,
Eugene Seah76af9832012-03-28 18:43:53 -0600284 enum msm_dcvs_scm_event event, uint32_t param0,
285 uint32_t *ret1, int *freq_changed)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700286{
287 int ret = 0;
288 unsigned long flags = 0;
289 uint32_t new_freq = 0;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700290
291 spin_lock_irqsave(&core->cpu_lock, flags);
292 ret = msm_dcvs_scm_event(core->handle, event, param0,
Eugene Seah76af9832012-03-28 18:43:53 -0600293 core->actual_freq, &new_freq, ret1);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700294 if (ret) {
Jeff Ohlstein4aa4a2b2012-06-28 19:03:49 -0700295 if (ret == -13)
296 ret = 0;
297 else
298 __err("Error (%d) sending SCM event %d for core %s\n",
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700299 ret, event, core->core_name);
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700300 goto out;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700301 }
302
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700303 if (core->actual_freq != new_freq && core->pending_freq != new_freq) {
304 core->pending_freq = new_freq;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700305 core->time_start = ktime_to_ns(ktime_get());
306
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700307 if (core->task)
Abhijeet Dharmapurikarbbb52fe2012-08-31 20:31:16 -0700308 wake_up(&core->wait_q);
Eugene Seah76af9832012-03-28 18:43:53 -0600309 } else {
310 if (freq_changed)
311 *freq_changed = 0;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700312 }
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700313out:
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700314 spin_unlock_irqrestore(&core->cpu_lock, flags);
315
316 return ret;
317}
318
319static enum hrtimer_restart msm_dcvs_core_slack_timer(struct hrtimer *timer)
320{
321 int ret = 0;
322 struct dcvs_core *core = container_of(timer, struct dcvs_core, timer);
Eugene Seah76af9832012-03-28 18:43:53 -0600323 uint32_t ret1;
324 uint32_t ret2;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700325
326 if (msm_dcvs_debug & MSM_DCVS_DEBUG_FREQ_CHANGE)
327 __info("Slack timer fired for core %s\n", core->core_name);
328
329 /**
330 * Timer expired, notify TZ
331 * Dont care about the third arg.
332 */
Eugene Seah76af9832012-03-28 18:43:53 -0600333 ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_QOS_TIMER_EXPIRED, 0,
334 &ret1, &ret2);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700335 if (ret)
336 __err("Timer expired for core %s but failed to notify.\n",
337 core->core_name);
338
339 return HRTIMER_NORESTART;
340}
341
342/* Helper functions and macros for sysfs nodes for a core */
343#define CORE_FROM_ATTRIBS(attr, name) \
344 container_of(container_of(attr, struct core_attribs, name), \
345 struct dcvs_core, attrib);
346
347#define DCVS_PARAM_SHOW(_name, v) \
348static ssize_t msm_dcvs_attr_##_name##_show(struct kobject *kobj, \
349 struct kobj_attribute *attr, char *buf) \
350{ \
351 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
352 return snprintf(buf, PAGE_SIZE, "%d\n", v); \
353}
354
355#define DCVS_ALGO_PARAM(_name) \
356static ssize_t msm_dcvs_attr_##_name##_show(struct kobject *kobj,\
357 struct kobj_attribute *attr, char *buf) \
358{ \
359 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
360 return snprintf(buf, PAGE_SIZE, "%d\n", core->algo_param._name); \
361} \
362static ssize_t msm_dcvs_attr_##_name##_store(struct kobject *kobj, \
363 struct kobj_attribute *attr, const char *buf, size_t count) \
364{ \
365 int ret = 0; \
366 uint32_t val = 0; \
367 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
368 mutex_lock(&core->lock); \
369 ret = kstrtouint(buf, 10, &val); \
370 if (ret) { \
371 __err("Invalid input %s for %s\n", buf, __stringify(_name));\
372 } else { \
373 uint32_t old_val = core->algo_param._name; \
374 core->algo_param._name = val; \
375 ret = msm_dcvs_scm_set_algo_params(core->handle, \
376 &core->algo_param); \
377 if (ret) { \
378 core->algo_param._name = old_val; \
379 __err("Error(%d) in setting %d for algo param %s\n",\
380 ret, val, __stringify(_name)); \
381 } \
382 } \
383 mutex_unlock(&core->lock); \
384 return count; \
385}
386
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700387#define DCVS_ENERGY_PARAM(_name) \
388static ssize_t msm_dcvs_attr_##_name##_show(struct kobject *kobj,\
389 struct kobj_attribute *attr, char *buf) \
390{ \
391 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
392 return snprintf(buf, PAGE_SIZE, "%d\n", core->coeffs._name); \
393} \
394static ssize_t msm_dcvs_attr_##_name##_store(struct kobject *kobj, \
395 struct kobj_attribute *attr, const char *buf, size_t count) \
396{ \
397 int ret = 0; \
398 int32_t val = 0; \
399 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
400 mutex_lock(&core->lock); \
401 ret = kstrtoint(buf, 10, &val); \
402 if (ret) { \
403 __err("Invalid input %s for %s\n", buf, __stringify(_name));\
404 } else { \
405 int32_t old_val = core->coeffs._name; \
406 core->coeffs._name = val; \
407 ret = msm_dcvs_scm_set_power_params(core->handle, \
408 &core->info->power_param, &core->info->freq_tbl[0], \
409 &core->coeffs); \
410 if (ret) { \
411 core->coeffs._name = old_val; \
412 __err("Error(%d) in setting %d for coeffs param %s\n",\
413 ret, val, __stringify(_name)); \
414 } \
415 } \
416 mutex_unlock(&core->lock); \
417 return count; \
418}
419
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700420#define DCVS_RO_ATTRIB(i, _name) \
421 core->attrib._name.attr.name = __stringify(_name); \
422 core->attrib._name.attr.mode = S_IRUGO; \
423 core->attrib._name.show = msm_dcvs_attr_##_name##_show; \
424 core->attrib._name.store = NULL; \
425 core->attrib.attrib_group.attrs[i] = &core->attrib._name.attr;
426
427#define DCVS_RW_ATTRIB(i, _name) \
428 core->attrib._name.attr.name = __stringify(_name); \
429 core->attrib._name.attr.mode = S_IRUGO | S_IWUSR; \
430 core->attrib._name.show = msm_dcvs_attr_##_name##_show; \
431 core->attrib._name.store = msm_dcvs_attr_##_name##_store; \
432 core->attrib.attrib_group.attrs[i] = &core->attrib._name.attr;
433
434/**
435 * Function declarations for different attributes.
436 * Gets used when setting the attribute show and store parameters.
437 */
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700438DCVS_PARAM_SHOW(core_id, core->handle)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700439DCVS_PARAM_SHOW(idle_enabled, (core->idle_driver != NULL))
440DCVS_PARAM_SHOW(freq_change_enabled, (core->freq_driver != NULL))
441DCVS_PARAM_SHOW(actual_freq, (core->actual_freq))
442DCVS_PARAM_SHOW(freq_change_us, (core->freq_change_us))
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700443
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700444DCVS_ALGO_PARAM(disable_pc_threshold)
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700445DCVS_ALGO_PARAM(em_win_size_min_us)
446DCVS_ALGO_PARAM(em_win_size_max_us)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700447DCVS_ALGO_PARAM(em_max_util_pct)
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700448DCVS_ALGO_PARAM(group_id)
449DCVS_ALGO_PARAM(max_freq_chg_time_us)
450DCVS_ALGO_PARAM(slack_mode_dynamic)
451DCVS_ALGO_PARAM(slack_time_min_us)
452DCVS_ALGO_PARAM(slack_time_max_us)
453DCVS_ALGO_PARAM(slack_weight_thresh_pct)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700454DCVS_ALGO_PARAM(ss_iobusy_conv)
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700455DCVS_ALGO_PARAM(ss_win_size_min_us)
456DCVS_ALGO_PARAM(ss_win_size_max_us)
457DCVS_ALGO_PARAM(ss_util_pct)
458
459DCVS_ENERGY_PARAM(active_coeff_a)
460DCVS_ENERGY_PARAM(active_coeff_b)
461DCVS_ENERGY_PARAM(active_coeff_c)
462DCVS_ENERGY_PARAM(leakage_coeff_a)
463DCVS_ENERGY_PARAM(leakage_coeff_b)
464DCVS_ENERGY_PARAM(leakage_coeff_c)
465DCVS_ENERGY_PARAM(leakage_coeff_d)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700466
467static int msm_dcvs_setup_core_sysfs(struct dcvs_core *core)
468{
469 int ret = 0;
470 struct kobject *core_kobj = NULL;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700471 const int attr_count = 27;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700472
473 BUG_ON(!cores_kobj);
474
475 core->attrib.attrib_group.attrs =
476 kzalloc(attr_count * sizeof(struct attribute *), GFP_KERNEL);
477
478 if (!core->attrib.attrib_group.attrs) {
479 ret = -ENOMEM;
480 goto done;
481 }
482
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700483
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700484 DCVS_RO_ATTRIB(0, core_id);
485 DCVS_RO_ATTRIB(1, idle_enabled);
486 DCVS_RO_ATTRIB(2, freq_change_enabled);
487 DCVS_RO_ATTRIB(3, actual_freq);
488 DCVS_RO_ATTRIB(4, freq_change_us);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700489
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700490 DCVS_RW_ATTRIB(5, disable_pc_threshold);
491 DCVS_RW_ATTRIB(6, em_win_size_min_us);
492 DCVS_RW_ATTRIB(7, em_win_size_max_us);
493 DCVS_RW_ATTRIB(8, em_max_util_pct);
494 DCVS_RW_ATTRIB(9, group_id);
495 DCVS_RW_ATTRIB(10, max_freq_chg_time_us);
496 DCVS_RW_ATTRIB(11, slack_mode_dynamic);
497 DCVS_RW_ATTRIB(12, slack_time_min_us);
498 DCVS_RW_ATTRIB(13, slack_time_max_us);
499 DCVS_RW_ATTRIB(14, slack_weight_thresh_pct);
500 DCVS_RW_ATTRIB(15, ss_iobusy_conv);
501 DCVS_RW_ATTRIB(16, ss_win_size_min_us);
502 DCVS_RW_ATTRIB(17, ss_win_size_max_us);
503 DCVS_RW_ATTRIB(18, ss_util_pct);
504
505 DCVS_RW_ATTRIB(19, active_coeff_a);
506 DCVS_RW_ATTRIB(20, active_coeff_b);
507 DCVS_RW_ATTRIB(21, active_coeff_c);
508 DCVS_RW_ATTRIB(22, leakage_coeff_a);
509 DCVS_RW_ATTRIB(23, leakage_coeff_b);
510 DCVS_RW_ATTRIB(24, leakage_coeff_c);
511 DCVS_RW_ATTRIB(25, leakage_coeff_d);
512
513 core->attrib.attrib_group.attrs[26] = NULL;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700514
515 core_kobj = kobject_create_and_add(core->core_name, cores_kobj);
516 if (!core_kobj) {
517 ret = -ENOMEM;
518 goto done;
519 }
520
521 ret = sysfs_create_group(core_kobj, &core->attrib.attrib_group);
522 if (ret)
523 __err("Cannot create core %s attr group\n", core->core_name);
524 else if (msm_dcvs_debug & MSM_DCVS_DEBUG_NOTIFIER)
525 __info("Setting up attributes for core %s\n", core->core_name);
526
527done:
528 if (ret) {
529 kfree(core->attrib.attrib_group.attrs);
530 kobject_del(core_kobj);
531 }
532
533 return ret;
534}
535
536/* Return the core if found or add to list if @add_to_list is true */
537static struct dcvs_core *msm_dcvs_get_core(const char *name, int add_to_list)
538{
539 struct dcvs_core *core = NULL;
540 int i;
541 int empty = -1;
542
543 if (!name[0] ||
544 (strnlen(name, CORE_NAME_MAX - 1) == CORE_NAME_MAX - 1))
545 return core;
546
547 mutex_lock(&core_list_lock);
548 for (i = 0; i < CORES_MAX; i++) {
549 core = &core_list[i];
550 if ((empty < 0) && !core->core_name[0]) {
551 empty = i;
552 continue;
553 }
554 if (!strncmp(name, core->core_name, CORE_NAME_MAX))
555 break;
556 }
557
558 /* Check for core_list full */
559 if ((i == CORES_MAX) && (empty < 0)) {
560 mutex_unlock(&core_list_lock);
561 return NULL;
562 }
563
564 if (i == CORES_MAX && add_to_list) {
565 core = &core_list[empty];
566 strlcpy(core->core_name, name, CORE_NAME_MAX);
567 mutex_init(&core->lock);
568 spin_lock_init(&core->cpu_lock);
569 core->handle = empty + CORE_HANDLE_OFFSET;
570 hrtimer_init(&core->timer,
571 CLOCK_MONOTONIC, HRTIMER_MODE_REL_PINNED);
572 core->timer.function = msm_dcvs_core_slack_timer;
573 }
574 mutex_unlock(&core_list_lock);
575
576 return core;
577}
578
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700579int msm_dcvs_register_core(const char *core_name,
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700580 struct msm_dcvs_core_info *info,
581 int (*set_frequency)(struct msm_dcvs_freq *self, unsigned int freq),
582 unsigned int (*get_frequency)(struct msm_dcvs_freq *self),
583 int sensor)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700584{
585 int ret = -EINVAL;
586 struct dcvs_core *core = NULL;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700587 uint32_t ret1;
588 uint32_t ret2;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700589
590 if (!core_name || !core_name[0])
591 return ret;
592
593 core = msm_dcvs_get_core(core_name, true);
594 if (!core)
595 return ret;
596
597 mutex_lock(&core->lock);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700598
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700599 core->set_frequency = set_frequency;
600 core->get_frequency = get_frequency;
601
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700602 core->info = info;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700603 memcpy(&core->algo_param, &info->algo_param,
604 sizeof(struct msm_dcvs_algo_param));
605
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700606 memcpy(&core->coeffs, &info->energy_coeffs,
607 sizeof(struct msm_dcvs_energy_curve_coeffs));
608
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700609 pr_debug("registering core with sensor %d\n", sensor);
610 core->sensor = sensor;
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700611
612 ret = msm_dcvs_scm_register_core(core->handle, &info->core_param);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700613 if (ret)
614 goto bail;
615
616 ret = msm_dcvs_scm_set_algo_params(core->handle, &info->algo_param);
617 if (ret)
618 goto bail;
619
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700620 ret = msm_dcvs_scm_set_power_params(core->handle, &info->power_param,
621 &info->freq_tbl[0], &core->coeffs);
622 if (ret)
623 goto bail;
624
625 ret = msm_dcvs_scm_event(core->handle, MSM_DCVS_SCM_CORE_ONLINE,
626 core->actual_freq, 0, &ret1, &ret2);
627 if (ret)
628 goto bail;
629
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700630 ret = msm_dcvs_setup_core_sysfs(core);
631 if (ret) {
632 __err("Unable to setup core %s sysfs\n", core->core_name);
633 core_handles[core->handle - CORE_HANDLE_OFFSET] = NULL;
634 goto bail;
635 }
Abhijeet Dharmapurikarbbb52fe2012-08-31 20:31:16 -0700636 init_waitqueue_head(&core->wait_q);
637 core->task = kthread_run(msm_dcvs_do_freq, (void *)core,
Abhijeet Dharmapurikar74f10832012-08-26 22:40:28 -0700638 "msm_dcvs/%d", core->handle);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700639bail:
640 mutex_unlock(&core->lock);
641 return ret;
642}
643EXPORT_SYMBOL(msm_dcvs_register_core);
644
Abhijeet Dharmapurikarc43f0db2012-08-31 20:42:53 -0700645void msm_dcvs_update_limits(struct msm_dcvs_freq *drv)
646{
647 struct dcvs_core *core;
648
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700649 if (!drv || !drv->core_name)
Abhijeet Dharmapurikarc43f0db2012-08-31 20:42:53 -0700650 return;
651
652 core = msm_dcvs_get_core(drv->core_name, false);
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700653 core->actual_freq = core->get_frequency(drv);
Abhijeet Dharmapurikarc43f0db2012-08-31 20:42:53 -0700654}
655
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700656int msm_dcvs_freq_sink_start(struct msm_dcvs_freq *drv)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700657{
658 int ret = -EINVAL;
659 struct dcvs_core *core = NULL;
Eugene Seah76af9832012-03-28 18:43:53 -0600660 uint32_t ret1;
661 uint32_t ret2;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700662
663 if (!drv || !drv->core_name)
664 return ret;
665
666 core = msm_dcvs_get_core(drv->core_name, true);
667 if (!core)
668 return ret;
669
670 mutex_lock(&core->lock);
671 if (core->freq_driver && (msm_dcvs_debug & MSM_DCVS_DEBUG_NOTIFIER))
672 __info("Frequency notifier for %s being replaced\n",
673 core->core_name);
674 core->freq_driver = drv;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700675 if (IS_ERR(core->task)) {
676 mutex_unlock(&core->lock);
677 return -EFAULT;
678 }
679
680 if (msm_dcvs_debug & MSM_DCVS_DEBUG_IDLE_PULSE)
681 __info("Enabling idle pulse for %s\n", core->core_name);
682
683 if (core->idle_driver) {
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700684 core->actual_freq = core->get_frequency(drv);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700685 /* Notify TZ to start receiving idle info for the core */
Abhijeet Dharmapurikar7b933c52012-08-23 15:51:58 -0700686 ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_DCVS_ENABLE, 1,
Eugene Seah76af9832012-03-28 18:43:53 -0600687 &ret1, &ret2);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700688 core->idle_driver->enable(core->idle_driver,
689 MSM_DCVS_ENABLE_IDLE_PULSE);
690 }
691
692 mutex_unlock(&core->lock);
693
694 return core->handle;
695}
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700696EXPORT_SYMBOL(msm_dcvs_freq_sink_start);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700697
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700698int msm_dcvs_freq_sink_stop(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
708 core = msm_dcvs_get_core(drv->core_name, false);
709 if (!core)
710 return ret;
711
712 mutex_lock(&core->lock);
713 if (msm_dcvs_debug & MSM_DCVS_DEBUG_IDLE_PULSE)
714 __info("Disabling idle pulse for %s\n", core->core_name);
715 if (core->idle_driver) {
716 core->idle_driver->enable(core->idle_driver,
717 MSM_DCVS_DISABLE_IDLE_PULSE);
718 /* Notify TZ to stop receiving idle info for the core */
Abhijeet Dharmapurikar7b933c52012-08-23 15:51:58 -0700719 ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_DCVS_ENABLE, 0,
Eugene Seah76af9832012-03-28 18:43:53 -0600720 &ret1, &ret2);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700721 hrtimer_cancel(&core->timer);
722 core->idle_driver->enable(core->idle_driver,
723 MSM_DCVS_ENABLE_HIGH_LATENCY_MODES);
724 if (msm_dcvs_debug & MSM_DCVS_DEBUG_IDLE_PULSE)
725 __info("Enabling LPM for %s\n", core->core_name);
726 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700727 core->freq_driver = NULL;
728 mutex_unlock(&core->lock);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700729
730 return 0;
731}
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700732EXPORT_SYMBOL(msm_dcvs_freq_sink_stop);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700733
734int msm_dcvs_idle_source_register(struct msm_dcvs_idle *drv)
735{
736 int ret = -EINVAL;
737 struct dcvs_core *core = NULL;
738
739 if (!drv || !drv->core_name)
740 return ret;
741
742 core = msm_dcvs_get_core(drv->core_name, true);
743 if (!core)
744 return ret;
745
746 mutex_lock(&core->lock);
747 if (core->idle_driver && (msm_dcvs_debug & MSM_DCVS_DEBUG_NOTIFIER))
748 __info("Idle notifier for %s being replaced\n",
749 core->core_name);
750 core->idle_driver = drv;
751 mutex_unlock(&core->lock);
752
753 return core->handle;
754}
755EXPORT_SYMBOL(msm_dcvs_idle_source_register);
756
757int msm_dcvs_idle_source_unregister(struct msm_dcvs_idle *drv)
758{
759 int ret = -EINVAL;
760 struct dcvs_core *core = NULL;
761
762 if (!drv || !drv->core_name)
763 return ret;
764
765 core = msm_dcvs_get_core(drv->core_name, false);
766 if (!core)
767 return ret;
768
769 mutex_lock(&core->lock);
770 core->idle_driver = NULL;
771 mutex_unlock(&core->lock);
772
773 return 0;
774}
775EXPORT_SYMBOL(msm_dcvs_idle_source_unregister);
776
777int msm_dcvs_idle(int handle, enum msm_core_idle_state state, uint32_t iowaited)
778{
779 int ret = 0;
780 struct dcvs_core *core = NULL;
Eugene Seah76af9832012-03-28 18:43:53 -0600781 uint32_t timer_interval_us = 0;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700782 uint32_t r0, r1;
Eugene Seah76af9832012-03-28 18:43:53 -0600783 uint32_t freq_changed = 0;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700784
785 if (handle >= CORE_HANDLE_OFFSET &&
786 (handle - CORE_HANDLE_OFFSET) < CORES_MAX)
787 core = &core_list[handle - CORE_HANDLE_OFFSET];
788
789 BUG_ON(!core);
790
791 if (msm_dcvs_debug & MSM_DCVS_DEBUG_IDLE_PULSE)
792 __info("Core %s idle state %d\n", core->core_name, state);
793
794 switch (state) {
795 case MSM_DCVS_IDLE_ENTER:
796 hrtimer_cancel(&core->timer);
797 ret = msm_dcvs_scm_event(core->handle,
798 MSM_DCVS_SCM_IDLE_ENTER, 0, 0, &r0, &r1);
799 if (ret)
800 __err("Error (%d) sending idle enter for %s\n",
801 ret, core->core_name);
802 break;
803
804 case MSM_DCVS_IDLE_EXIT:
805 hrtimer_cancel(&core->timer);
806 ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_IDLE_EXIT,
Eugene Seah76af9832012-03-28 18:43:53 -0600807 iowaited, &timer_interval_us, &freq_changed);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700808 if (ret)
809 __err("Error (%d) sending idle exit for %s\n",
810 ret, core->core_name);
Eugene Seah76af9832012-03-28 18:43:53 -0600811 /* only start slack timer if change_freq won't */
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700812 if (freq_changed)
Eugene Seah76af9832012-03-28 18:43:53 -0600813 break;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700814 if (timer_interval_us && !core->timer_disabled) {
815 ret = hrtimer_start(&core->timer,
816 ktime_set(0, timer_interval_us * 1000),
817 HRTIMER_MODE_REL_PINNED);
Eugene Seah76af9832012-03-28 18:43:53 -0600818
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700819 if (ret)
820 __err("Failed to register timer for core %s\n",
Eugene Seah76af9832012-03-28 18:43:53 -0600821 core->core_name);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700822 }
823 break;
824 }
825
826 return ret;
827}
828EXPORT_SYMBOL(msm_dcvs_idle);
829
830static int __init msm_dcvs_late_init(void)
831{
832 struct kobject *module_kobj = NULL;
833 int ret = 0;
834
835 module_kobj = kset_find_obj(module_kset, KBUILD_MODNAME);
836 if (!module_kobj) {
837 pr_err("%s: cannot find kobject for module %s\n",
838 __func__, KBUILD_MODNAME);
839 ret = -ENOENT;
840 goto err;
841 }
842
843 cores_kobj = kobject_create_and_add("cores", module_kobj);
844 if (!cores_kobj) {
845 __err("Cannot create %s kobject\n", "cores");
846 ret = -ENOMEM;
847 goto err;
848 }
849
850 debugfs_base = debugfs_create_dir("msm_dcvs", NULL);
851 if (!debugfs_base) {
852 __err("Cannot create debugfs base %s\n", "msm_dcvs");
853 ret = -ENOENT;
854 goto err;
855 }
856
857 if (!debugfs_create_u32("debug_mask", S_IRUGO | S_IWUSR,
858 debugfs_base, &msm_dcvs_debug)) {
859 __err("Cannot create debugfs entry %s\n", "debug_mask");
860 ret = -ENOMEM;
861 goto err;
862 }
863
864err:
865 if (ret) {
866 kobject_del(cores_kobj);
867 cores_kobj = NULL;
868 debugfs_remove(debugfs_base);
869 }
870
871 return ret;
872}
873late_initcall(msm_dcvs_late_init);
874
875static int __init msm_dcvs_early_init(void)
876{
877 int ret = 0;
878
879 if (!msm_dcvs_enabled) {
880 __info("Not enabled (%d)\n", msm_dcvs_enabled);
881 return 0;
882 }
883
884 ret = msm_dcvs_scm_init(10 * 1024);
885 if (ret)
886 __err("Unable to initialize DCVS err=%d\n", ret);
887
888 return ret;
889}
890postcore_initcall(msm_dcvs_early_init);