blob: f761bf994b2f676c6f22535a150848e6bc9e7363 [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;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700100};
101
102static int msm_dcvs_debug;
103static int msm_dcvs_enabled = 1;
104module_param_named(enable, msm_dcvs_enabled, int, S_IRUGO | S_IWUSR | S_IWGRP);
105
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700106static struct dentry *debugfs_base;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700107
108static struct dcvs_core core_list[CORES_MAX];
109static DEFINE_MUTEX(core_list_lock);
110
111static struct kobject *cores_kobj;
112static struct dcvs_core *core_handles[CORES_MAX];
113
114/* Change core frequency, called with core mutex locked */
115static int __msm_dcvs_change_freq(struct dcvs_core *core)
116{
117 int ret = 0;
118 unsigned long flags = 0;
119 unsigned int requested_freq = 0;
120 unsigned int prev_freq = 0;
121 int64_t time_start = 0;
122 int64_t time_end = 0;
123 uint32_t slack_us = 0;
124 uint32_t ret1 = 0;
125
126 if (!core->freq_driver || !core->freq_driver->set_frequency) {
127 /* Core may have unregistered or hotplugged */
128 return -ENODEV;
129 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700130 spin_lock_irqsave(&core->cpu_lock, flags);
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700131repeat:
132
133 requested_freq = core->pending_freq;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700134 time_start = core->time_start;
135 core->time_start = 0;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700136 /**
137 * Cancel the timers, we dont want the timer firing as we are
138 * changing the clock rate. Dont let idle_exit and others setup
139 * timers as well.
140 */
141 hrtimer_cancel(&core->timer);
142 core->timer_disabled = 1;
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700143 if (requested_freq == core->actual_freq)
144 goto out;
145
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700146 spin_unlock_irqrestore(&core->cpu_lock, flags);
147
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700148
149 /**
150 * Call the frequency sink driver to change the frequency
151 * We will need to get back the actual frequency in KHz and
152 * the record the time taken to change it.
153 */
154 ret = core->freq_driver->set_frequency(core->freq_driver,
155 requested_freq);
156 if (ret <= 0) {
157 __err("Core %s failed to set freq %u\n",
158 core->core_name, requested_freq);
Eugene Seah76af9832012-03-28 18:43:53 -0600159 /* continue to call TZ to get updated slack timer */
160 } else {
161 prev_freq = core->actual_freq;
162 core->actual_freq = ret;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700163 }
164
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700165 time_end = ktime_to_ns(ktime_get());
166 if (msm_dcvs_debug & MSM_DCVS_DEBUG_FREQ_CHANGE)
167 __info("Core %s Time end %llu Time start: %llu\n",
168 core->core_name, time_end, time_start);
169 time_end -= time_start;
170 do_div(time_end, NSEC_PER_USEC);
171 core->freq_change_us = (uint32_t)time_end;
172
173 /**
174 * Disable low power modes if the actual frequency is >
175 * disable_pc_threshold.
176 */
177 if (core->actual_freq >
178 core->algo_param.disable_pc_threshold) {
179 core->idle_driver->enable(core->idle_driver,
180 MSM_DCVS_DISABLE_HIGH_LATENCY_MODES);
181 if (msm_dcvs_debug & MSM_DCVS_DEBUG_IDLE_PULSE)
182 __info("Disabling LPM for %s\n", core->core_name);
183 } else if (core->actual_freq <=
184 core->algo_param.disable_pc_threshold) {
185 core->idle_driver->enable(core->idle_driver,
186 MSM_DCVS_ENABLE_HIGH_LATENCY_MODES);
187 if (msm_dcvs_debug & MSM_DCVS_DEBUG_IDLE_PULSE)
188 __info("Enabling LPM for %s\n", core->core_name);
189 }
190
191 /**
192 * Update algorithm with new freq and time taken to change
193 * to this frequency and that will get us the new slack
194 * timer
195 */
196 ret = msm_dcvs_scm_event(core->handle, MSM_DCVS_SCM_CLOCK_FREQ_UPDATE,
197 core->actual_freq, (uint32_t)time_end, &slack_us, &ret1);
198 if (!ret) {
199 /* Reset the slack timer */
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700200 if (slack_us) {
201 core->timer_disabled = 0;
202 ret = hrtimer_start(&core->timer,
203 ktime_set(0, slack_us * 1000),
204 HRTIMER_MODE_REL_PINNED);
205 if (ret)
206 __err("Failed to register timer for core %s\n",
207 core->core_name);
208 }
209 } else {
210 __err("Error sending core (%s) freq change (%u)\n",
211 core->core_name, core->actual_freq);
212 }
213
214 if (msm_dcvs_debug & MSM_DCVS_DEBUG_FREQ_CHANGE)
215 __info("Freq %u requested for core %s (actual %u prev %u) "
216 "change time %u us slack time %u us\n",
217 requested_freq, core->core_name,
218 core->actual_freq, prev_freq,
Eugene Seah76af9832012-03-28 18:43:53 -0600219 core->freq_change_us, slack_us);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700220
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700221 spin_lock_irqsave(&core->cpu_lock, flags);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700222 /**
223 * By the time we are done with freq changes, we could be asked to
224 * change again. Check before exiting.
225 */
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700226 if (core->pending_freq)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700227 goto repeat;
228
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700229
230out: /* should always be jumped to with the spin_lock held */
231 core->pending_freq = 0;
232 spin_unlock_irqrestore(&core->cpu_lock, flags);
233
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700234 return ret;
235}
236
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700237static int __msm_dcvs_report_temp(struct dcvs_core *core)
238{
239 struct msm_dcvs_core_info *info = core->info;
240 struct tsens_device tsens_dev;
241 int ret;
242 unsigned long temp = 0;
243
244 tsens_dev.sensor_num = core->sensor;
245 ret = tsens_get_temp(&tsens_dev, &temp);
246 if (!ret) {
247 tsens_dev.sensor_num = 0;
248 ret = tsens_get_temp(&tsens_dev, &temp);
249 if (!ret)
250 return -ENODEV;
251 }
252
253 ret = msm_dcvs_scm_set_power_params(core->handle, &info->power_param,
254 &info->freq_tbl[0], &core->coeffs);
255 return ret;
256}
257
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700258static int msm_dcvs_do_freq(void *data)
259{
260 struct dcvs_core *core = (struct dcvs_core *)data;
261 static struct sched_param param = {.sched_priority = MAX_RT_PRIO - 1};
262
263 sched_setscheduler(current, SCHED_FIFO, &param);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700264
265 while (!kthread_should_stop()) {
Abhijeet Dharmapurikarbbb52fe2012-08-31 20:31:16 -0700266 wait_event(core->wait_q, !(core->pending_freq == 0 ||
267 core->pending_freq == -1) ||
268 kthread_should_stop());
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700269
270 if (kthread_should_stop())
271 break;
272
Abhijeet Dharmapurikarbbb52fe2012-08-31 20:31:16 -0700273 mutex_lock(&core->lock);
274 __msm_dcvs_change_freq(core);
275 __msm_dcvs_report_temp(core);
276 mutex_unlock(&core->lock);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700277 }
278
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700279 return 0;
280}
281
282static int msm_dcvs_update_freq(struct dcvs_core *core,
Eugene Seah76af9832012-03-28 18:43:53 -0600283 enum msm_dcvs_scm_event event, uint32_t param0,
284 uint32_t *ret1, int *freq_changed)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700285{
286 int ret = 0;
287 unsigned long flags = 0;
288 uint32_t new_freq = 0;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700289
290 spin_lock_irqsave(&core->cpu_lock, flags);
291 ret = msm_dcvs_scm_event(core->handle, event, param0,
Eugene Seah76af9832012-03-28 18:43:53 -0600292 core->actual_freq, &new_freq, ret1);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700293 if (ret) {
Jeff Ohlstein4aa4a2b2012-06-28 19:03:49 -0700294 if (ret == -13)
295 ret = 0;
296 else
297 __err("Error (%d) sending SCM event %d for core %s\n",
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700298 ret, event, core->core_name);
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700299 goto out;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700300 }
301
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700302 if (core->actual_freq != new_freq && core->pending_freq != new_freq) {
303 core->pending_freq = new_freq;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700304 core->time_start = ktime_to_ns(ktime_get());
305
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700306 if (core->task)
Abhijeet Dharmapurikarbbb52fe2012-08-31 20:31:16 -0700307 wake_up(&core->wait_q);
Eugene Seah76af9832012-03-28 18:43:53 -0600308 } else {
309 if (freq_changed)
310 *freq_changed = 0;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700311 }
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700312out:
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700313 spin_unlock_irqrestore(&core->cpu_lock, flags);
314
315 return ret;
316}
317
318static enum hrtimer_restart msm_dcvs_core_slack_timer(struct hrtimer *timer)
319{
320 int ret = 0;
321 struct dcvs_core *core = container_of(timer, struct dcvs_core, timer);
Eugene Seah76af9832012-03-28 18:43:53 -0600322 uint32_t ret1;
323 uint32_t ret2;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700324
325 if (msm_dcvs_debug & MSM_DCVS_DEBUG_FREQ_CHANGE)
326 __info("Slack timer fired for core %s\n", core->core_name);
327
328 /**
329 * Timer expired, notify TZ
330 * Dont care about the third arg.
331 */
Eugene Seah76af9832012-03-28 18:43:53 -0600332 ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_QOS_TIMER_EXPIRED, 0,
333 &ret1, &ret2);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700334 if (ret)
335 __err("Timer expired for core %s but failed to notify.\n",
336 core->core_name);
337
338 return HRTIMER_NORESTART;
339}
340
341/* Helper functions and macros for sysfs nodes for a core */
342#define CORE_FROM_ATTRIBS(attr, name) \
343 container_of(container_of(attr, struct core_attribs, name), \
344 struct dcvs_core, attrib);
345
346#define DCVS_PARAM_SHOW(_name, v) \
347static ssize_t msm_dcvs_attr_##_name##_show(struct kobject *kobj, \
348 struct kobj_attribute *attr, char *buf) \
349{ \
350 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
351 return snprintf(buf, PAGE_SIZE, "%d\n", v); \
352}
353
354#define DCVS_ALGO_PARAM(_name) \
355static ssize_t msm_dcvs_attr_##_name##_show(struct kobject *kobj,\
356 struct kobj_attribute *attr, char *buf) \
357{ \
358 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
359 return snprintf(buf, PAGE_SIZE, "%d\n", core->algo_param._name); \
360} \
361static ssize_t msm_dcvs_attr_##_name##_store(struct kobject *kobj, \
362 struct kobj_attribute *attr, const char *buf, size_t count) \
363{ \
364 int ret = 0; \
365 uint32_t val = 0; \
366 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
367 mutex_lock(&core->lock); \
368 ret = kstrtouint(buf, 10, &val); \
369 if (ret) { \
370 __err("Invalid input %s for %s\n", buf, __stringify(_name));\
371 } else { \
372 uint32_t old_val = core->algo_param._name; \
373 core->algo_param._name = val; \
374 ret = msm_dcvs_scm_set_algo_params(core->handle, \
375 &core->algo_param); \
376 if (ret) { \
377 core->algo_param._name = old_val; \
378 __err("Error(%d) in setting %d for algo param %s\n",\
379 ret, val, __stringify(_name)); \
380 } \
381 } \
382 mutex_unlock(&core->lock); \
383 return count; \
384}
385
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700386#define DCVS_ENERGY_PARAM(_name) \
387static ssize_t msm_dcvs_attr_##_name##_show(struct kobject *kobj,\
388 struct kobj_attribute *attr, char *buf) \
389{ \
390 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
391 return snprintf(buf, PAGE_SIZE, "%d\n", core->coeffs._name); \
392} \
393static ssize_t msm_dcvs_attr_##_name##_store(struct kobject *kobj, \
394 struct kobj_attribute *attr, const char *buf, size_t count) \
395{ \
396 int ret = 0; \
397 int32_t val = 0; \
398 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
399 mutex_lock(&core->lock); \
400 ret = kstrtoint(buf, 10, &val); \
401 if (ret) { \
402 __err("Invalid input %s for %s\n", buf, __stringify(_name));\
403 } else { \
404 int32_t old_val = core->coeffs._name; \
405 core->coeffs._name = val; \
406 ret = msm_dcvs_scm_set_power_params(core->handle, \
407 &core->info->power_param, &core->info->freq_tbl[0], \
408 &core->coeffs); \
409 if (ret) { \
410 core->coeffs._name = old_val; \
411 __err("Error(%d) in setting %d for coeffs param %s\n",\
412 ret, val, __stringify(_name)); \
413 } \
414 } \
415 mutex_unlock(&core->lock); \
416 return count; \
417}
418
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700419#define DCVS_RO_ATTRIB(i, _name) \
420 core->attrib._name.attr.name = __stringify(_name); \
421 core->attrib._name.attr.mode = S_IRUGO; \
422 core->attrib._name.show = msm_dcvs_attr_##_name##_show; \
423 core->attrib._name.store = NULL; \
424 core->attrib.attrib_group.attrs[i] = &core->attrib._name.attr;
425
426#define DCVS_RW_ATTRIB(i, _name) \
427 core->attrib._name.attr.name = __stringify(_name); \
428 core->attrib._name.attr.mode = S_IRUGO | S_IWUSR; \
429 core->attrib._name.show = msm_dcvs_attr_##_name##_show; \
430 core->attrib._name.store = msm_dcvs_attr_##_name##_store; \
431 core->attrib.attrib_group.attrs[i] = &core->attrib._name.attr;
432
433/**
434 * Function declarations for different attributes.
435 * Gets used when setting the attribute show and store parameters.
436 */
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700437DCVS_PARAM_SHOW(core_id, core->handle)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700438DCVS_PARAM_SHOW(idle_enabled, (core->idle_driver != NULL))
439DCVS_PARAM_SHOW(freq_change_enabled, (core->freq_driver != NULL))
440DCVS_PARAM_SHOW(actual_freq, (core->actual_freq))
441DCVS_PARAM_SHOW(freq_change_us, (core->freq_change_us))
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700442
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700443DCVS_ALGO_PARAM(disable_pc_threshold)
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700444DCVS_ALGO_PARAM(em_win_size_min_us)
445DCVS_ALGO_PARAM(em_win_size_max_us)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700446DCVS_ALGO_PARAM(em_max_util_pct)
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700447DCVS_ALGO_PARAM(group_id)
448DCVS_ALGO_PARAM(max_freq_chg_time_us)
449DCVS_ALGO_PARAM(slack_mode_dynamic)
450DCVS_ALGO_PARAM(slack_time_min_us)
451DCVS_ALGO_PARAM(slack_time_max_us)
452DCVS_ALGO_PARAM(slack_weight_thresh_pct)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700453DCVS_ALGO_PARAM(ss_iobusy_conv)
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700454DCVS_ALGO_PARAM(ss_win_size_min_us)
455DCVS_ALGO_PARAM(ss_win_size_max_us)
456DCVS_ALGO_PARAM(ss_util_pct)
457
458DCVS_ENERGY_PARAM(active_coeff_a)
459DCVS_ENERGY_PARAM(active_coeff_b)
460DCVS_ENERGY_PARAM(active_coeff_c)
461DCVS_ENERGY_PARAM(leakage_coeff_a)
462DCVS_ENERGY_PARAM(leakage_coeff_b)
463DCVS_ENERGY_PARAM(leakage_coeff_c)
464DCVS_ENERGY_PARAM(leakage_coeff_d)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700465
466static int msm_dcvs_setup_core_sysfs(struct dcvs_core *core)
467{
468 int ret = 0;
469 struct kobject *core_kobj = NULL;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700470 const int attr_count = 27;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700471
472 BUG_ON(!cores_kobj);
473
474 core->attrib.attrib_group.attrs =
475 kzalloc(attr_count * sizeof(struct attribute *), GFP_KERNEL);
476
477 if (!core->attrib.attrib_group.attrs) {
478 ret = -ENOMEM;
479 goto done;
480 }
481
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700482
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700483 DCVS_RO_ATTRIB(0, core_id);
484 DCVS_RO_ATTRIB(1, idle_enabled);
485 DCVS_RO_ATTRIB(2, freq_change_enabled);
486 DCVS_RO_ATTRIB(3, actual_freq);
487 DCVS_RO_ATTRIB(4, freq_change_us);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700488
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700489 DCVS_RW_ATTRIB(5, disable_pc_threshold);
490 DCVS_RW_ATTRIB(6, em_win_size_min_us);
491 DCVS_RW_ATTRIB(7, em_win_size_max_us);
492 DCVS_RW_ATTRIB(8, em_max_util_pct);
493 DCVS_RW_ATTRIB(9, group_id);
494 DCVS_RW_ATTRIB(10, max_freq_chg_time_us);
495 DCVS_RW_ATTRIB(11, slack_mode_dynamic);
496 DCVS_RW_ATTRIB(12, slack_time_min_us);
497 DCVS_RW_ATTRIB(13, slack_time_max_us);
498 DCVS_RW_ATTRIB(14, slack_weight_thresh_pct);
499 DCVS_RW_ATTRIB(15, ss_iobusy_conv);
500 DCVS_RW_ATTRIB(16, ss_win_size_min_us);
501 DCVS_RW_ATTRIB(17, ss_win_size_max_us);
502 DCVS_RW_ATTRIB(18, ss_util_pct);
503
504 DCVS_RW_ATTRIB(19, active_coeff_a);
505 DCVS_RW_ATTRIB(20, active_coeff_b);
506 DCVS_RW_ATTRIB(21, active_coeff_c);
507 DCVS_RW_ATTRIB(22, leakage_coeff_a);
508 DCVS_RW_ATTRIB(23, leakage_coeff_b);
509 DCVS_RW_ATTRIB(24, leakage_coeff_c);
510 DCVS_RW_ATTRIB(25, leakage_coeff_d);
511
512 core->attrib.attrib_group.attrs[26] = NULL;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700513
514 core_kobj = kobject_create_and_add(core->core_name, cores_kobj);
515 if (!core_kobj) {
516 ret = -ENOMEM;
517 goto done;
518 }
519
520 ret = sysfs_create_group(core_kobj, &core->attrib.attrib_group);
521 if (ret)
522 __err("Cannot create core %s attr group\n", core->core_name);
523 else if (msm_dcvs_debug & MSM_DCVS_DEBUG_NOTIFIER)
524 __info("Setting up attributes for core %s\n", core->core_name);
525
526done:
527 if (ret) {
528 kfree(core->attrib.attrib_group.attrs);
529 kobject_del(core_kobj);
530 }
531
532 return ret;
533}
534
535/* Return the core if found or add to list if @add_to_list is true */
536static struct dcvs_core *msm_dcvs_get_core(const char *name, int add_to_list)
537{
538 struct dcvs_core *core = NULL;
539 int i;
540 int empty = -1;
541
542 if (!name[0] ||
543 (strnlen(name, CORE_NAME_MAX - 1) == CORE_NAME_MAX - 1))
544 return core;
545
546 mutex_lock(&core_list_lock);
547 for (i = 0; i < CORES_MAX; i++) {
548 core = &core_list[i];
549 if ((empty < 0) && !core->core_name[0]) {
550 empty = i;
551 continue;
552 }
553 if (!strncmp(name, core->core_name, CORE_NAME_MAX))
554 break;
555 }
556
557 /* Check for core_list full */
558 if ((i == CORES_MAX) && (empty < 0)) {
559 mutex_unlock(&core_list_lock);
560 return NULL;
561 }
562
563 if (i == CORES_MAX && add_to_list) {
564 core = &core_list[empty];
565 strlcpy(core->core_name, name, CORE_NAME_MAX);
566 mutex_init(&core->lock);
567 spin_lock_init(&core->cpu_lock);
568 core->handle = empty + CORE_HANDLE_OFFSET;
569 hrtimer_init(&core->timer,
570 CLOCK_MONOTONIC, HRTIMER_MODE_REL_PINNED);
571 core->timer.function = msm_dcvs_core_slack_timer;
572 }
573 mutex_unlock(&core_list_lock);
574
575 return core;
576}
577
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700578int msm_dcvs_register_core(const char *core_name,
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700579 struct msm_dcvs_core_info *info, int sensor)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700580{
581 int ret = -EINVAL;
582 struct dcvs_core *core = NULL;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700583 uint32_t ret1;
584 uint32_t ret2;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700585
586 if (!core_name || !core_name[0])
587 return ret;
588
589 core = msm_dcvs_get_core(core_name, true);
590 if (!core)
591 return ret;
592
593 mutex_lock(&core->lock);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700594
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700595 core->info = info;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700596 memcpy(&core->algo_param, &info->algo_param,
597 sizeof(struct msm_dcvs_algo_param));
598
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700599 memcpy(&core->coeffs, &info->energy_coeffs,
600 sizeof(struct msm_dcvs_energy_curve_coeffs));
601
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700602 pr_debug("registering core with sensor %d\n", sensor);
603 core->sensor = sensor;
604 ret = msm_dcvs_scm_register_core(core->handle,
605 &info->core_param);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700606 if (ret)
607 goto bail;
608
609 ret = msm_dcvs_scm_set_algo_params(core->handle, &info->algo_param);
610 if (ret)
611 goto bail;
612
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700613 ret = msm_dcvs_scm_set_power_params(core->handle, &info->power_param,
614 &info->freq_tbl[0], &core->coeffs);
615 if (ret)
616 goto bail;
617
618 ret = msm_dcvs_scm_event(core->handle, MSM_DCVS_SCM_CORE_ONLINE,
619 core->actual_freq, 0, &ret1, &ret2);
620 if (ret)
621 goto bail;
622
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700623 ret = msm_dcvs_setup_core_sysfs(core);
624 if (ret) {
625 __err("Unable to setup core %s sysfs\n", core->core_name);
626 core_handles[core->handle - CORE_HANDLE_OFFSET] = NULL;
627 goto bail;
628 }
Abhijeet Dharmapurikarbbb52fe2012-08-31 20:31:16 -0700629 init_waitqueue_head(&core->wait_q);
630 core->task = kthread_run(msm_dcvs_do_freq, (void *)core,
Abhijeet Dharmapurikar74f10832012-08-26 22:40:28 -0700631 "msm_dcvs/%d", core->handle);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700632bail:
633 mutex_unlock(&core->lock);
634 return ret;
635}
636EXPORT_SYMBOL(msm_dcvs_register_core);
637
638int msm_dcvs_freq_sink_register(struct msm_dcvs_freq *drv)
639{
640 int ret = -EINVAL;
641 struct dcvs_core *core = NULL;
Eugene Seah76af9832012-03-28 18:43:53 -0600642 uint32_t ret1;
643 uint32_t ret2;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700644
645 if (!drv || !drv->core_name)
646 return ret;
647
648 core = msm_dcvs_get_core(drv->core_name, true);
649 if (!core)
650 return ret;
651
652 mutex_lock(&core->lock);
653 if (core->freq_driver && (msm_dcvs_debug & MSM_DCVS_DEBUG_NOTIFIER))
654 __info("Frequency notifier for %s being replaced\n",
655 core->core_name);
656 core->freq_driver = drv;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700657 if (IS_ERR(core->task)) {
658 mutex_unlock(&core->lock);
659 return -EFAULT;
660 }
661
662 if (msm_dcvs_debug & MSM_DCVS_DEBUG_IDLE_PULSE)
663 __info("Enabling idle pulse for %s\n", core->core_name);
664
665 if (core->idle_driver) {
666 core->actual_freq = core->freq_driver->get_frequency(drv);
667 /* Notify TZ to start receiving idle info for the core */
Abhijeet Dharmapurikar7b933c52012-08-23 15:51:58 -0700668 ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_DCVS_ENABLE, 1,
Eugene Seah76af9832012-03-28 18:43:53 -0600669 &ret1, &ret2);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700670 core->idle_driver->enable(core->idle_driver,
671 MSM_DCVS_ENABLE_IDLE_PULSE);
672 }
673
674 mutex_unlock(&core->lock);
675
676 return core->handle;
677}
678EXPORT_SYMBOL(msm_dcvs_freq_sink_register);
679
680int msm_dcvs_freq_sink_unregister(struct msm_dcvs_freq *drv)
681{
682 int ret = -EINVAL;
683 struct dcvs_core *core = NULL;
Eugene Seah76af9832012-03-28 18:43:53 -0600684 uint32_t ret1;
685 uint32_t ret2;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700686
687 if (!drv || !drv->core_name)
688 return ret;
689
690 core = msm_dcvs_get_core(drv->core_name, false);
691 if (!core)
692 return ret;
693
694 mutex_lock(&core->lock);
695 if (msm_dcvs_debug & MSM_DCVS_DEBUG_IDLE_PULSE)
696 __info("Disabling idle pulse for %s\n", core->core_name);
697 if (core->idle_driver) {
698 core->idle_driver->enable(core->idle_driver,
699 MSM_DCVS_DISABLE_IDLE_PULSE);
700 /* Notify TZ to stop receiving idle info for the core */
Abhijeet Dharmapurikar7b933c52012-08-23 15:51:58 -0700701 ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_DCVS_ENABLE, 0,
Eugene Seah76af9832012-03-28 18:43:53 -0600702 &ret1, &ret2);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700703 hrtimer_cancel(&core->timer);
704 core->idle_driver->enable(core->idle_driver,
705 MSM_DCVS_ENABLE_HIGH_LATENCY_MODES);
706 if (msm_dcvs_debug & MSM_DCVS_DEBUG_IDLE_PULSE)
707 __info("Enabling LPM for %s\n", core->core_name);
708 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700709 core->freq_driver = NULL;
710 mutex_unlock(&core->lock);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700711
712 return 0;
713}
714EXPORT_SYMBOL(msm_dcvs_freq_sink_unregister);
715
716int msm_dcvs_idle_source_register(struct msm_dcvs_idle *drv)
717{
718 int ret = -EINVAL;
719 struct dcvs_core *core = NULL;
720
721 if (!drv || !drv->core_name)
722 return ret;
723
724 core = msm_dcvs_get_core(drv->core_name, true);
725 if (!core)
726 return ret;
727
728 mutex_lock(&core->lock);
729 if (core->idle_driver && (msm_dcvs_debug & MSM_DCVS_DEBUG_NOTIFIER))
730 __info("Idle notifier for %s being replaced\n",
731 core->core_name);
732 core->idle_driver = drv;
733 mutex_unlock(&core->lock);
734
735 return core->handle;
736}
737EXPORT_SYMBOL(msm_dcvs_idle_source_register);
738
739int msm_dcvs_idle_source_unregister(struct msm_dcvs_idle *drv)
740{
741 int ret = -EINVAL;
742 struct dcvs_core *core = NULL;
743
744 if (!drv || !drv->core_name)
745 return ret;
746
747 core = msm_dcvs_get_core(drv->core_name, false);
748 if (!core)
749 return ret;
750
751 mutex_lock(&core->lock);
752 core->idle_driver = NULL;
753 mutex_unlock(&core->lock);
754
755 return 0;
756}
757EXPORT_SYMBOL(msm_dcvs_idle_source_unregister);
758
759int msm_dcvs_idle(int handle, enum msm_core_idle_state state, uint32_t iowaited)
760{
761 int ret = 0;
762 struct dcvs_core *core = NULL;
Eugene Seah76af9832012-03-28 18:43:53 -0600763 uint32_t timer_interval_us = 0;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700764 uint32_t r0, r1;
Eugene Seah76af9832012-03-28 18:43:53 -0600765 uint32_t freq_changed = 0;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700766
767 if (handle >= CORE_HANDLE_OFFSET &&
768 (handle - CORE_HANDLE_OFFSET) < CORES_MAX)
769 core = &core_list[handle - CORE_HANDLE_OFFSET];
770
771 BUG_ON(!core);
772
773 if (msm_dcvs_debug & MSM_DCVS_DEBUG_IDLE_PULSE)
774 __info("Core %s idle state %d\n", core->core_name, state);
775
776 switch (state) {
777 case MSM_DCVS_IDLE_ENTER:
778 hrtimer_cancel(&core->timer);
779 ret = msm_dcvs_scm_event(core->handle,
780 MSM_DCVS_SCM_IDLE_ENTER, 0, 0, &r0, &r1);
781 if (ret)
782 __err("Error (%d) sending idle enter for %s\n",
783 ret, core->core_name);
784 break;
785
786 case MSM_DCVS_IDLE_EXIT:
787 hrtimer_cancel(&core->timer);
788 ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_IDLE_EXIT,
Eugene Seah76af9832012-03-28 18:43:53 -0600789 iowaited, &timer_interval_us, &freq_changed);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700790 if (ret)
791 __err("Error (%d) sending idle exit for %s\n",
792 ret, core->core_name);
Eugene Seah76af9832012-03-28 18:43:53 -0600793 /* only start slack timer if change_freq won't */
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700794 if (freq_changed)
Eugene Seah76af9832012-03-28 18:43:53 -0600795 break;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700796 if (timer_interval_us && !core->timer_disabled) {
797 ret = hrtimer_start(&core->timer,
798 ktime_set(0, timer_interval_us * 1000),
799 HRTIMER_MODE_REL_PINNED);
Eugene Seah76af9832012-03-28 18:43:53 -0600800
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700801 if (ret)
802 __err("Failed to register timer for core %s\n",
Eugene Seah76af9832012-03-28 18:43:53 -0600803 core->core_name);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700804 }
805 break;
806 }
807
808 return ret;
809}
810EXPORT_SYMBOL(msm_dcvs_idle);
811
812static int __init msm_dcvs_late_init(void)
813{
814 struct kobject *module_kobj = NULL;
815 int ret = 0;
816
817 module_kobj = kset_find_obj(module_kset, KBUILD_MODNAME);
818 if (!module_kobj) {
819 pr_err("%s: cannot find kobject for module %s\n",
820 __func__, KBUILD_MODNAME);
821 ret = -ENOENT;
822 goto err;
823 }
824
825 cores_kobj = kobject_create_and_add("cores", module_kobj);
826 if (!cores_kobj) {
827 __err("Cannot create %s kobject\n", "cores");
828 ret = -ENOMEM;
829 goto err;
830 }
831
832 debugfs_base = debugfs_create_dir("msm_dcvs", NULL);
833 if (!debugfs_base) {
834 __err("Cannot create debugfs base %s\n", "msm_dcvs");
835 ret = -ENOENT;
836 goto err;
837 }
838
839 if (!debugfs_create_u32("debug_mask", S_IRUGO | S_IWUSR,
840 debugfs_base, &msm_dcvs_debug)) {
841 __err("Cannot create debugfs entry %s\n", "debug_mask");
842 ret = -ENOMEM;
843 goto err;
844 }
845
846err:
847 if (ret) {
848 kobject_del(cores_kobj);
849 cores_kobj = NULL;
850 debugfs_remove(debugfs_base);
851 }
852
853 return ret;
854}
855late_initcall(msm_dcvs_late_init);
856
857static int __init msm_dcvs_early_init(void)
858{
859 int ret = 0;
860
861 if (!msm_dcvs_enabled) {
862 __info("Not enabled (%d)\n", msm_dcvs_enabled);
863 return 0;
864 }
865
866 ret = msm_dcvs_scm_init(10 * 1024);
867 if (ret)
868 __err("Unable to initialize DCVS err=%d\n", ret);
869
870 return ret;
871}
872postcore_initcall(msm_dcvs_early_init);