blob: 9601b7e3ed1a82ea2bc4f164be1b188a8c3ebb6d [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>
26#include <asm/atomic.h>
27#include <asm/page.h>
28#include <mach/msm_dcvs.h>
29
30#define CORE_HANDLE_OFFSET (0xA0)
31#define __err(f, ...) pr_err("MSM_DCVS: %s: " f, __func__, __VA_ARGS__)
32#define __info(f, ...) pr_info("MSM_DCVS: %s: " f, __func__, __VA_ARGS__)
33#define MAX_PENDING (5)
34
35enum {
36 MSM_DCVS_DEBUG_NOTIFIER = BIT(0),
37 MSM_DCVS_DEBUG_IDLE_PULSE = BIT(1),
38 MSM_DCVS_DEBUG_FREQ_CHANGE = BIT(2),
39};
40
41struct core_attribs {
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -070042 struct kobj_attribute core_id;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070043 struct kobj_attribute idle_enabled;
44 struct kobj_attribute freq_change_enabled;
45 struct kobj_attribute actual_freq;
46 struct kobj_attribute freq_change_us;
47
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070048 struct kobj_attribute disable_pc_threshold;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -070049 struct kobj_attribute em_win_size_min_us;
50 struct kobj_attribute em_win_size_max_us;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070051 struct kobj_attribute em_max_util_pct;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -070052 struct kobj_attribute group_id;
53 struct kobj_attribute max_freq_chg_time_us;
54 struct kobj_attribute slack_mode_dynamic;
55 struct kobj_attribute slack_time_min_us;
56 struct kobj_attribute slack_time_max_us;
57 struct kobj_attribute slack_weight_thresh_pct;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070058 struct kobj_attribute ss_iobusy_conv;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -070059 struct kobj_attribute ss_win_size_min_us;
60 struct kobj_attribute ss_win_size_max_us;
61 struct kobj_attribute ss_util_pct;
62
63 struct kobj_attribute active_coeff_a;
64 struct kobj_attribute active_coeff_b;
65 struct kobj_attribute active_coeff_c;
66 struct kobj_attribute leakage_coeff_a;
67 struct kobj_attribute leakage_coeff_b;
68 struct kobj_attribute leakage_coeff_c;
69 struct kobj_attribute leakage_coeff_d;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070070
71 struct attribute_group attrib_group;
72};
73
74struct dcvs_core {
75 char core_name[CORE_NAME_MAX];
76 uint32_t new_freq[MAX_PENDING];
77 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 uint32_t freq_pending;
95 struct hrtimer timer;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070096 int32_t timer_disabled;
Eugene Seah76af9832012-03-28 18:43:53 -060097 /* track if kthread for change_freq is active */
98 int32_t change_freq_activated;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -070099 struct msm_dcvs_core_info *info;
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
106static struct dentry *debugfs_base;
107
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 }
130repeat:
131 spin_lock_irqsave(&core->cpu_lock, flags);
132 if (unlikely(!core->freq_pending)) {
133 spin_unlock_irqrestore(&core->cpu_lock, flags);
134 return ret;
135 }
136 requested_freq = core->new_freq[core->freq_pending - 1];
137 if (unlikely(core->freq_pending > 1) &&
138 (msm_dcvs_debug & MSM_DCVS_DEBUG_FREQ_CHANGE)) {
139 int i;
140 for (i = 0; i < core->freq_pending - 1; i++) {
141 __info("Core %s missing freq %u\n",
142 core->core_name, core->new_freq[i]);
143 }
144 }
145 time_start = core->time_start;
146 core->time_start = 0;
147 core->freq_pending = 0;
148 /**
149 * Cancel the timers, we dont want the timer firing as we are
150 * changing the clock rate. Dont let idle_exit and others setup
151 * timers as well.
152 */
153 hrtimer_cancel(&core->timer);
154 core->timer_disabled = 1;
155 spin_unlock_irqrestore(&core->cpu_lock, flags);
156
157 if (requested_freq == core->actual_freq)
158 return ret;
159
160 /**
161 * Call the frequency sink driver to change the frequency
162 * We will need to get back the actual frequency in KHz and
163 * the record the time taken to change it.
164 */
165 ret = core->freq_driver->set_frequency(core->freq_driver,
166 requested_freq);
167 if (ret <= 0) {
168 __err("Core %s failed to set freq %u\n",
169 core->core_name, requested_freq);
Eugene Seah76af9832012-03-28 18:43:53 -0600170 /* continue to call TZ to get updated slack timer */
171 } else {
172 prev_freq = core->actual_freq;
173 core->actual_freq = ret;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700174 }
175
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700176 time_end = ktime_to_ns(ktime_get());
177 if (msm_dcvs_debug & MSM_DCVS_DEBUG_FREQ_CHANGE)
178 __info("Core %s Time end %llu Time start: %llu\n",
179 core->core_name, time_end, time_start);
180 time_end -= time_start;
181 do_div(time_end, NSEC_PER_USEC);
182 core->freq_change_us = (uint32_t)time_end;
183
184 /**
185 * Disable low power modes if the actual frequency is >
186 * disable_pc_threshold.
187 */
188 if (core->actual_freq >
189 core->algo_param.disable_pc_threshold) {
190 core->idle_driver->enable(core->idle_driver,
191 MSM_DCVS_DISABLE_HIGH_LATENCY_MODES);
192 if (msm_dcvs_debug & MSM_DCVS_DEBUG_IDLE_PULSE)
193 __info("Disabling LPM for %s\n", core->core_name);
194 } else if (core->actual_freq <=
195 core->algo_param.disable_pc_threshold) {
196 core->idle_driver->enable(core->idle_driver,
197 MSM_DCVS_ENABLE_HIGH_LATENCY_MODES);
198 if (msm_dcvs_debug & MSM_DCVS_DEBUG_IDLE_PULSE)
199 __info("Enabling LPM for %s\n", core->core_name);
200 }
201
202 /**
203 * Update algorithm with new freq and time taken to change
204 * to this frequency and that will get us the new slack
205 * timer
206 */
207 ret = msm_dcvs_scm_event(core->handle, MSM_DCVS_SCM_CLOCK_FREQ_UPDATE,
208 core->actual_freq, (uint32_t)time_end, &slack_us, &ret1);
209 if (!ret) {
210 /* Reset the slack timer */
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700211 if (slack_us) {
212 core->timer_disabled = 0;
213 ret = hrtimer_start(&core->timer,
214 ktime_set(0, slack_us * 1000),
215 HRTIMER_MODE_REL_PINNED);
216 if (ret)
217 __err("Failed to register timer for core %s\n",
218 core->core_name);
219 }
220 } else {
221 __err("Error sending core (%s) freq change (%u)\n",
222 core->core_name, core->actual_freq);
223 }
224
225 if (msm_dcvs_debug & MSM_DCVS_DEBUG_FREQ_CHANGE)
226 __info("Freq %u requested for core %s (actual %u prev %u) "
227 "change time %u us slack time %u us\n",
228 requested_freq, core->core_name,
229 core->actual_freq, prev_freq,
Eugene Seah76af9832012-03-28 18:43:53 -0600230 core->freq_change_us, slack_us);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700231
232 /**
233 * By the time we are done with freq changes, we could be asked to
234 * change again. Check before exiting.
235 */
236 if (core->freq_pending)
237 goto repeat;
238
Eugene Seah76af9832012-03-28 18:43:53 -0600239 core->change_freq_activated = 0;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700240 return ret;
241}
242
243static int msm_dcvs_do_freq(void *data)
244{
245 struct dcvs_core *core = (struct dcvs_core *)data;
246 static struct sched_param param = {.sched_priority = MAX_RT_PRIO - 1};
247
248 sched_setscheduler(current, SCHED_FIFO, &param);
249 set_current_state(TASK_UNINTERRUPTIBLE);
250
251 while (!kthread_should_stop()) {
252 mutex_lock(&core->lock);
253 __msm_dcvs_change_freq(core);
254 mutex_unlock(&core->lock);
255
256 schedule();
257
258 if (kthread_should_stop())
259 break;
260
261 set_current_state(TASK_UNINTERRUPTIBLE);
262 }
263
264 __set_current_state(TASK_RUNNING);
265
266 return 0;
267}
268
269static int msm_dcvs_update_freq(struct dcvs_core *core,
Eugene Seah76af9832012-03-28 18:43:53 -0600270 enum msm_dcvs_scm_event event, uint32_t param0,
271 uint32_t *ret1, int *freq_changed)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700272{
273 int ret = 0;
274 unsigned long flags = 0;
275 uint32_t new_freq = 0;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700276
277 spin_lock_irqsave(&core->cpu_lock, flags);
278 ret = msm_dcvs_scm_event(core->handle, event, param0,
Eugene Seah76af9832012-03-28 18:43:53 -0600279 core->actual_freq, &new_freq, ret1);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700280 if (ret) {
281 __err("Error (%d) sending SCM event %d for core %s\n",
282 ret, event, core->core_name);
283 goto freq_done;
284 }
285
286 if ((core->actual_freq != new_freq) &&
287 (core->new_freq[core->freq_pending] != new_freq)) {
288 if (core->freq_pending >= MAX_PENDING - 1)
289 core->freq_pending = MAX_PENDING - 1;
290 core->new_freq[core->freq_pending++] = new_freq;
291 core->time_start = ktime_to_ns(ktime_get());
292
293 /* Schedule the frequency change */
294 if (!core->task)
295 __err("Uninitialized task for core %s\n",
296 core->core_name);
Eugene Seah76af9832012-03-28 18:43:53 -0600297 else {
298 if (freq_changed)
299 *freq_changed = 1;
300 core->change_freq_activated = 1;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700301 wake_up_process(core->task);
Eugene Seah76af9832012-03-28 18:43:53 -0600302 }
303 } else {
304 if (freq_changed)
305 *freq_changed = 0;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700306 }
307freq_done:
308 spin_unlock_irqrestore(&core->cpu_lock, flags);
309
310 return ret;
311}
312
313static enum hrtimer_restart msm_dcvs_core_slack_timer(struct hrtimer *timer)
314{
315 int ret = 0;
316 struct dcvs_core *core = container_of(timer, struct dcvs_core, timer);
Eugene Seah76af9832012-03-28 18:43:53 -0600317 uint32_t ret1;
318 uint32_t ret2;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700319
320 if (msm_dcvs_debug & MSM_DCVS_DEBUG_FREQ_CHANGE)
321 __info("Slack timer fired for core %s\n", core->core_name);
322
323 /**
324 * Timer expired, notify TZ
325 * Dont care about the third arg.
326 */
Eugene Seah76af9832012-03-28 18:43:53 -0600327 ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_QOS_TIMER_EXPIRED, 0,
328 &ret1, &ret2);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700329 if (ret)
330 __err("Timer expired for core %s but failed to notify.\n",
331 core->core_name);
332
333 return HRTIMER_NORESTART;
334}
335
336/* Helper functions and macros for sysfs nodes for a core */
337#define CORE_FROM_ATTRIBS(attr, name) \
338 container_of(container_of(attr, struct core_attribs, name), \
339 struct dcvs_core, attrib);
340
341#define DCVS_PARAM_SHOW(_name, v) \
342static ssize_t msm_dcvs_attr_##_name##_show(struct kobject *kobj, \
343 struct kobj_attribute *attr, char *buf) \
344{ \
345 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
346 return snprintf(buf, PAGE_SIZE, "%d\n", v); \
347}
348
349#define DCVS_ALGO_PARAM(_name) \
350static ssize_t msm_dcvs_attr_##_name##_show(struct kobject *kobj,\
351 struct kobj_attribute *attr, char *buf) \
352{ \
353 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
354 return snprintf(buf, PAGE_SIZE, "%d\n", core->algo_param._name); \
355} \
356static ssize_t msm_dcvs_attr_##_name##_store(struct kobject *kobj, \
357 struct kobj_attribute *attr, const char *buf, size_t count) \
358{ \
359 int ret = 0; \
360 uint32_t val = 0; \
361 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
362 mutex_lock(&core->lock); \
363 ret = kstrtouint(buf, 10, &val); \
364 if (ret) { \
365 __err("Invalid input %s for %s\n", buf, __stringify(_name));\
366 } else { \
367 uint32_t old_val = core->algo_param._name; \
368 core->algo_param._name = val; \
369 ret = msm_dcvs_scm_set_algo_params(core->handle, \
370 &core->algo_param); \
371 if (ret) { \
372 core->algo_param._name = old_val; \
373 __err("Error(%d) in setting %d for algo param %s\n",\
374 ret, val, __stringify(_name)); \
375 } \
376 } \
377 mutex_unlock(&core->lock); \
378 return count; \
379}
380
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700381#define DCVS_ENERGY_PARAM(_name) \
382static ssize_t msm_dcvs_attr_##_name##_show(struct kobject *kobj,\
383 struct kobj_attribute *attr, char *buf) \
384{ \
385 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
386 return snprintf(buf, PAGE_SIZE, "%d\n", core->coeffs._name); \
387} \
388static ssize_t msm_dcvs_attr_##_name##_store(struct kobject *kobj, \
389 struct kobj_attribute *attr, const char *buf, size_t count) \
390{ \
391 int ret = 0; \
392 int32_t val = 0; \
393 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
394 mutex_lock(&core->lock); \
395 ret = kstrtoint(buf, 10, &val); \
396 if (ret) { \
397 __err("Invalid input %s for %s\n", buf, __stringify(_name));\
398 } else { \
399 int32_t old_val = core->coeffs._name; \
400 core->coeffs._name = val; \
401 ret = msm_dcvs_scm_set_power_params(core->handle, \
402 &core->info->power_param, &core->info->freq_tbl[0], \
403 &core->coeffs); \
404 if (ret) { \
405 core->coeffs._name = old_val; \
406 __err("Error(%d) in setting %d for coeffs param %s\n",\
407 ret, val, __stringify(_name)); \
408 } \
409 } \
410 mutex_unlock(&core->lock); \
411 return count; \
412}
413
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700414#define DCVS_RO_ATTRIB(i, _name) \
415 core->attrib._name.attr.name = __stringify(_name); \
416 core->attrib._name.attr.mode = S_IRUGO; \
417 core->attrib._name.show = msm_dcvs_attr_##_name##_show; \
418 core->attrib._name.store = NULL; \
419 core->attrib.attrib_group.attrs[i] = &core->attrib._name.attr;
420
421#define DCVS_RW_ATTRIB(i, _name) \
422 core->attrib._name.attr.name = __stringify(_name); \
423 core->attrib._name.attr.mode = S_IRUGO | S_IWUSR; \
424 core->attrib._name.show = msm_dcvs_attr_##_name##_show; \
425 core->attrib._name.store = msm_dcvs_attr_##_name##_store; \
426 core->attrib.attrib_group.attrs[i] = &core->attrib._name.attr;
427
428/**
429 * Function declarations for different attributes.
430 * Gets used when setting the attribute show and store parameters.
431 */
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700432DCVS_PARAM_SHOW(core_id, core->handle)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700433DCVS_PARAM_SHOW(idle_enabled, (core->idle_driver != NULL))
434DCVS_PARAM_SHOW(freq_change_enabled, (core->freq_driver != NULL))
435DCVS_PARAM_SHOW(actual_freq, (core->actual_freq))
436DCVS_PARAM_SHOW(freq_change_us, (core->freq_change_us))
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700437
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700438DCVS_ALGO_PARAM(disable_pc_threshold)
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700439DCVS_ALGO_PARAM(em_win_size_min_us)
440DCVS_ALGO_PARAM(em_win_size_max_us)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700441DCVS_ALGO_PARAM(em_max_util_pct)
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700442DCVS_ALGO_PARAM(group_id)
443DCVS_ALGO_PARAM(max_freq_chg_time_us)
444DCVS_ALGO_PARAM(slack_mode_dynamic)
445DCVS_ALGO_PARAM(slack_time_min_us)
446DCVS_ALGO_PARAM(slack_time_max_us)
447DCVS_ALGO_PARAM(slack_weight_thresh_pct)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700448DCVS_ALGO_PARAM(ss_iobusy_conv)
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700449DCVS_ALGO_PARAM(ss_win_size_min_us)
450DCVS_ALGO_PARAM(ss_win_size_max_us)
451DCVS_ALGO_PARAM(ss_util_pct)
452
453DCVS_ENERGY_PARAM(active_coeff_a)
454DCVS_ENERGY_PARAM(active_coeff_b)
455DCVS_ENERGY_PARAM(active_coeff_c)
456DCVS_ENERGY_PARAM(leakage_coeff_a)
457DCVS_ENERGY_PARAM(leakage_coeff_b)
458DCVS_ENERGY_PARAM(leakage_coeff_c)
459DCVS_ENERGY_PARAM(leakage_coeff_d)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700460
461static int msm_dcvs_setup_core_sysfs(struct dcvs_core *core)
462{
463 int ret = 0;
464 struct kobject *core_kobj = NULL;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700465 const int attr_count = 27;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700466
467 BUG_ON(!cores_kobj);
468
469 core->attrib.attrib_group.attrs =
470 kzalloc(attr_count * sizeof(struct attribute *), GFP_KERNEL);
471
472 if (!core->attrib.attrib_group.attrs) {
473 ret = -ENOMEM;
474 goto done;
475 }
476
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700477
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700478 DCVS_RO_ATTRIB(0, core_id);
479 DCVS_RO_ATTRIB(1, idle_enabled);
480 DCVS_RO_ATTRIB(2, freq_change_enabled);
481 DCVS_RO_ATTRIB(3, actual_freq);
482 DCVS_RO_ATTRIB(4, freq_change_us);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700483
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700484 DCVS_RW_ATTRIB(5, disable_pc_threshold);
485 DCVS_RW_ATTRIB(6, em_win_size_min_us);
486 DCVS_RW_ATTRIB(7, em_win_size_max_us);
487 DCVS_RW_ATTRIB(8, em_max_util_pct);
488 DCVS_RW_ATTRIB(9, group_id);
489 DCVS_RW_ATTRIB(10, max_freq_chg_time_us);
490 DCVS_RW_ATTRIB(11, slack_mode_dynamic);
491 DCVS_RW_ATTRIB(12, slack_time_min_us);
492 DCVS_RW_ATTRIB(13, slack_time_max_us);
493 DCVS_RW_ATTRIB(14, slack_weight_thresh_pct);
494 DCVS_RW_ATTRIB(15, ss_iobusy_conv);
495 DCVS_RW_ATTRIB(16, ss_win_size_min_us);
496 DCVS_RW_ATTRIB(17, ss_win_size_max_us);
497 DCVS_RW_ATTRIB(18, ss_util_pct);
498
499 DCVS_RW_ATTRIB(19, active_coeff_a);
500 DCVS_RW_ATTRIB(20, active_coeff_b);
501 DCVS_RW_ATTRIB(21, active_coeff_c);
502 DCVS_RW_ATTRIB(22, leakage_coeff_a);
503 DCVS_RW_ATTRIB(23, leakage_coeff_b);
504 DCVS_RW_ATTRIB(24, leakage_coeff_c);
505 DCVS_RW_ATTRIB(25, leakage_coeff_d);
506
507 core->attrib.attrib_group.attrs[26] = NULL;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700508
509 core_kobj = kobject_create_and_add(core->core_name, cores_kobj);
510 if (!core_kobj) {
511 ret = -ENOMEM;
512 goto done;
513 }
514
515 ret = sysfs_create_group(core_kobj, &core->attrib.attrib_group);
516 if (ret)
517 __err("Cannot create core %s attr group\n", core->core_name);
518 else if (msm_dcvs_debug & MSM_DCVS_DEBUG_NOTIFIER)
519 __info("Setting up attributes for core %s\n", core->core_name);
520
521done:
522 if (ret) {
523 kfree(core->attrib.attrib_group.attrs);
524 kobject_del(core_kobj);
525 }
526
527 return ret;
528}
529
530/* Return the core if found or add to list if @add_to_list is true */
531static struct dcvs_core *msm_dcvs_get_core(const char *name, int add_to_list)
532{
533 struct dcvs_core *core = NULL;
534 int i;
535 int empty = -1;
536
537 if (!name[0] ||
538 (strnlen(name, CORE_NAME_MAX - 1) == CORE_NAME_MAX - 1))
539 return core;
540
541 mutex_lock(&core_list_lock);
542 for (i = 0; i < CORES_MAX; i++) {
543 core = &core_list[i];
544 if ((empty < 0) && !core->core_name[0]) {
545 empty = i;
546 continue;
547 }
548 if (!strncmp(name, core->core_name, CORE_NAME_MAX))
549 break;
550 }
551
552 /* Check for core_list full */
553 if ((i == CORES_MAX) && (empty < 0)) {
554 mutex_unlock(&core_list_lock);
555 return NULL;
556 }
557
558 if (i == CORES_MAX && add_to_list) {
559 core = &core_list[empty];
560 strlcpy(core->core_name, name, CORE_NAME_MAX);
561 mutex_init(&core->lock);
562 spin_lock_init(&core->cpu_lock);
563 core->handle = empty + CORE_HANDLE_OFFSET;
564 hrtimer_init(&core->timer,
565 CLOCK_MONOTONIC, HRTIMER_MODE_REL_PINNED);
566 core->timer.function = msm_dcvs_core_slack_timer;
567 }
568 mutex_unlock(&core_list_lock);
569
570 return core;
571}
572
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700573int msm_dcvs_register_core(const char *core_name,
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700574 struct msm_dcvs_core_info *info)
575{
576 int ret = -EINVAL;
577 struct dcvs_core *core = NULL;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700578 uint32_t ret1;
579 uint32_t ret2;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700580
581 if (!core_name || !core_name[0])
582 return ret;
583
584 core = msm_dcvs_get_core(core_name, true);
585 if (!core)
586 return ret;
587
588 mutex_lock(&core->lock);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700589
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700590 core->info = info;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700591 memcpy(&core->algo_param, &info->algo_param,
592 sizeof(struct msm_dcvs_algo_param));
593
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700594 memcpy(&core->coeffs, &info->energy_coeffs,
595 sizeof(struct msm_dcvs_energy_curve_coeffs));
596
597 ret = msm_dcvs_scm_register_core(core->handle, &info->core_param);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700598 if (ret)
599 goto bail;
600
601 ret = msm_dcvs_scm_set_algo_params(core->handle, &info->algo_param);
602 if (ret)
603 goto bail;
604
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700605 ret = msm_dcvs_scm_set_power_params(core->handle, &info->power_param,
606 &info->freq_tbl[0], &core->coeffs);
607 if (ret)
608 goto bail;
609
610 ret = msm_dcvs_scm_event(core->handle, MSM_DCVS_SCM_CORE_ONLINE,
611 core->actual_freq, 0, &ret1, &ret2);
612 if (ret)
613 goto bail;
614
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700615 ret = msm_dcvs_setup_core_sysfs(core);
616 if (ret) {
617 __err("Unable to setup core %s sysfs\n", core->core_name);
618 core_handles[core->handle - CORE_HANDLE_OFFSET] = NULL;
619 goto bail;
620 }
621
622bail:
623 mutex_unlock(&core->lock);
624 return ret;
625}
626EXPORT_SYMBOL(msm_dcvs_register_core);
627
628int msm_dcvs_freq_sink_register(struct msm_dcvs_freq *drv)
629{
630 int ret = -EINVAL;
631 struct dcvs_core *core = NULL;
Eugene Seah76af9832012-03-28 18:43:53 -0600632 uint32_t ret1;
633 uint32_t ret2;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700634
635 if (!drv || !drv->core_name)
636 return ret;
637
638 core = msm_dcvs_get_core(drv->core_name, true);
639 if (!core)
640 return ret;
641
642 mutex_lock(&core->lock);
643 if (core->freq_driver && (msm_dcvs_debug & MSM_DCVS_DEBUG_NOTIFIER))
644 __info("Frequency notifier for %s being replaced\n",
645 core->core_name);
646 core->freq_driver = drv;
647 core->task = kthread_create(msm_dcvs_do_freq, (void *)core,
648 "msm_dcvs/%d", core->handle);
649 if (IS_ERR(core->task)) {
650 mutex_unlock(&core->lock);
651 return -EFAULT;
652 }
653
654 if (msm_dcvs_debug & MSM_DCVS_DEBUG_IDLE_PULSE)
655 __info("Enabling idle pulse for %s\n", core->core_name);
656
657 if (core->idle_driver) {
658 core->actual_freq = core->freq_driver->get_frequency(drv);
659 /* Notify TZ to start receiving idle info for the core */
Abhijeet Dharmapurikar7b933c52012-08-23 15:51:58 -0700660 ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_DCVS_ENABLE, 1,
Eugene Seah76af9832012-03-28 18:43:53 -0600661 &ret1, &ret2);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700662 core->idle_driver->enable(core->idle_driver,
663 MSM_DCVS_ENABLE_IDLE_PULSE);
664 }
665
666 mutex_unlock(&core->lock);
667
668 return core->handle;
669}
670EXPORT_SYMBOL(msm_dcvs_freq_sink_register);
671
672int msm_dcvs_freq_sink_unregister(struct msm_dcvs_freq *drv)
673{
674 int ret = -EINVAL;
675 struct dcvs_core *core = NULL;
Eugene Seah76af9832012-03-28 18:43:53 -0600676 uint32_t ret1;
677 uint32_t ret2;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700678
679 if (!drv || !drv->core_name)
680 return ret;
681
682 core = msm_dcvs_get_core(drv->core_name, false);
683 if (!core)
684 return ret;
685
686 mutex_lock(&core->lock);
687 if (msm_dcvs_debug & MSM_DCVS_DEBUG_IDLE_PULSE)
688 __info("Disabling idle pulse for %s\n", core->core_name);
689 if (core->idle_driver) {
690 core->idle_driver->enable(core->idle_driver,
691 MSM_DCVS_DISABLE_IDLE_PULSE);
692 /* Notify TZ to stop receiving idle info for the core */
Abhijeet Dharmapurikar7b933c52012-08-23 15:51:58 -0700693 ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_DCVS_ENABLE, 0,
Eugene Seah76af9832012-03-28 18:43:53 -0600694 &ret1, &ret2);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700695 hrtimer_cancel(&core->timer);
696 core->idle_driver->enable(core->idle_driver,
697 MSM_DCVS_ENABLE_HIGH_LATENCY_MODES);
698 if (msm_dcvs_debug & MSM_DCVS_DEBUG_IDLE_PULSE)
699 __info("Enabling LPM for %s\n", core->core_name);
700 }
701 core->freq_pending = 0;
702 core->freq_driver = NULL;
703 mutex_unlock(&core->lock);
704 kthread_stop(core->task);
705
706 return 0;
707}
708EXPORT_SYMBOL(msm_dcvs_freq_sink_unregister);
709
710int msm_dcvs_idle_source_register(struct msm_dcvs_idle *drv)
711{
712 int ret = -EINVAL;
713 struct dcvs_core *core = NULL;
714
715 if (!drv || !drv->core_name)
716 return ret;
717
718 core = msm_dcvs_get_core(drv->core_name, true);
719 if (!core)
720 return ret;
721
722 mutex_lock(&core->lock);
723 if (core->idle_driver && (msm_dcvs_debug & MSM_DCVS_DEBUG_NOTIFIER))
724 __info("Idle notifier for %s being replaced\n",
725 core->core_name);
726 core->idle_driver = drv;
727 mutex_unlock(&core->lock);
728
729 return core->handle;
730}
731EXPORT_SYMBOL(msm_dcvs_idle_source_register);
732
733int msm_dcvs_idle_source_unregister(struct msm_dcvs_idle *drv)
734{
735 int ret = -EINVAL;
736 struct dcvs_core *core = NULL;
737
738 if (!drv || !drv->core_name)
739 return ret;
740
741 core = msm_dcvs_get_core(drv->core_name, false);
742 if (!core)
743 return ret;
744
745 mutex_lock(&core->lock);
746 core->idle_driver = NULL;
747 mutex_unlock(&core->lock);
748
749 return 0;
750}
751EXPORT_SYMBOL(msm_dcvs_idle_source_unregister);
752
753int msm_dcvs_idle(int handle, enum msm_core_idle_state state, uint32_t iowaited)
754{
755 int ret = 0;
756 struct dcvs_core *core = NULL;
Eugene Seah76af9832012-03-28 18:43:53 -0600757 uint32_t timer_interval_us = 0;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700758 uint32_t r0, r1;
Eugene Seah76af9832012-03-28 18:43:53 -0600759 uint32_t freq_changed = 0;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700760
761 if (handle >= CORE_HANDLE_OFFSET &&
762 (handle - CORE_HANDLE_OFFSET) < CORES_MAX)
763 core = &core_list[handle - CORE_HANDLE_OFFSET];
764
765 BUG_ON(!core);
766
767 if (msm_dcvs_debug & MSM_DCVS_DEBUG_IDLE_PULSE)
768 __info("Core %s idle state %d\n", core->core_name, state);
769
770 switch (state) {
771 case MSM_DCVS_IDLE_ENTER:
772 hrtimer_cancel(&core->timer);
773 ret = msm_dcvs_scm_event(core->handle,
774 MSM_DCVS_SCM_IDLE_ENTER, 0, 0, &r0, &r1);
775 if (ret)
776 __err("Error (%d) sending idle enter for %s\n",
777 ret, core->core_name);
778 break;
779
780 case MSM_DCVS_IDLE_EXIT:
781 hrtimer_cancel(&core->timer);
782 ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_IDLE_EXIT,
Eugene Seah76af9832012-03-28 18:43:53 -0600783 iowaited, &timer_interval_us, &freq_changed);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700784 if (ret)
785 __err("Error (%d) sending idle exit for %s\n",
786 ret, core->core_name);
Eugene Seah76af9832012-03-28 18:43:53 -0600787 /* only start slack timer if change_freq won't */
788 if (freq_changed || core->change_freq_activated)
789 break;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700790 if (timer_interval_us && !core->timer_disabled) {
791 ret = hrtimer_start(&core->timer,
792 ktime_set(0, timer_interval_us * 1000),
793 HRTIMER_MODE_REL_PINNED);
Eugene Seah76af9832012-03-28 18:43:53 -0600794
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700795 if (ret)
796 __err("Failed to register timer for core %s\n",
Eugene Seah76af9832012-03-28 18:43:53 -0600797 core->core_name);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700798 }
799 break;
800 }
801
802 return ret;
803}
804EXPORT_SYMBOL(msm_dcvs_idle);
805
806static int __init msm_dcvs_late_init(void)
807{
808 struct kobject *module_kobj = NULL;
809 int ret = 0;
810
811 module_kobj = kset_find_obj(module_kset, KBUILD_MODNAME);
812 if (!module_kobj) {
813 pr_err("%s: cannot find kobject for module %s\n",
814 __func__, KBUILD_MODNAME);
815 ret = -ENOENT;
816 goto err;
817 }
818
819 cores_kobj = kobject_create_and_add("cores", module_kobj);
820 if (!cores_kobj) {
821 __err("Cannot create %s kobject\n", "cores");
822 ret = -ENOMEM;
823 goto err;
824 }
825
826 debugfs_base = debugfs_create_dir("msm_dcvs", NULL);
827 if (!debugfs_base) {
828 __err("Cannot create debugfs base %s\n", "msm_dcvs");
829 ret = -ENOENT;
830 goto err;
831 }
832
833 if (!debugfs_create_u32("debug_mask", S_IRUGO | S_IWUSR,
834 debugfs_base, &msm_dcvs_debug)) {
835 __err("Cannot create debugfs entry %s\n", "debug_mask");
836 ret = -ENOMEM;
837 goto err;
838 }
839
840err:
841 if (ret) {
842 kobject_del(cores_kobj);
843 cores_kobj = NULL;
844 debugfs_remove(debugfs_base);
845 }
846
847 return ret;
848}
849late_initcall(msm_dcvs_late_init);
850
851static int __init msm_dcvs_early_init(void)
852{
853 int ret = 0;
854
855 if (!msm_dcvs_enabled) {
856 __info("Not enabled (%d)\n", msm_dcvs_enabled);
857 return 0;
858 }
859
860 ret = msm_dcvs_scm_init(10 * 1024);
861 if (ret)
862 __err("Unable to initialize DCVS err=%d\n", ret);
863
864 return ret;
865}
866postcore_initcall(msm_dcvs_early_init);