blob: 8593591637612ca000cc1b3fbaf175dab1f85516 [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>
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070017#include <linux/kthread.h>
18#include <linux/kobject.h>
19#include <linux/ktime.h>
20#include <linux/hrtimer.h>
21#include <linux/slab.h>
22#include <linux/spinlock.h>
23#include <linux/stringify.h>
24#include <linux/debugfs.h>
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -070025#include <linux/msm_tsens.h>
Steve Mucklea9aac292012-11-02 15:41:00 -070026#include <linux/platform_device.h>
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070027#include <asm/atomic.h>
28#include <asm/page.h>
29#include <mach/msm_dcvs.h>
Abhijeet Dharmapurikar07cf2ff2012-09-13 19:05:13 -070030#include <trace/events/mpdcvs_trace.h>
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070031
32#define CORE_HANDLE_OFFSET (0xA0)
33#define __err(f, ...) pr_err("MSM_DCVS: %s: " f, __func__, __VA_ARGS__)
34#define __info(f, ...) pr_info("MSM_DCVS: %s: " f, __func__, __VA_ARGS__)
35#define MAX_PENDING (5)
36
Steve Mucklec1785c32012-11-13 14:27:43 -080037#define CORE_FLAG_TEMP_UPDATE 0x1
38
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070039struct core_attribs {
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070040 struct kobj_attribute freq_change_us;
41
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070042 struct kobj_attribute disable_pc_threshold;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -070043 struct kobj_attribute em_win_size_min_us;
44 struct kobj_attribute em_win_size_max_us;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070045 struct kobj_attribute em_max_util_pct;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -070046 struct kobj_attribute group_id;
47 struct kobj_attribute max_freq_chg_time_us;
48 struct kobj_attribute slack_mode_dynamic;
49 struct kobj_attribute slack_time_min_us;
50 struct kobj_attribute slack_time_max_us;
51 struct kobj_attribute slack_weight_thresh_pct;
Steve Muckle8d0782e2012-12-06 14:31:00 -080052 struct kobj_attribute ss_no_corr_below_freq;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -070053 struct kobj_attribute ss_win_size_min_us;
54 struct kobj_attribute ss_win_size_max_us;
55 struct kobj_attribute ss_util_pct;
56
57 struct kobj_attribute active_coeff_a;
58 struct kobj_attribute active_coeff_b;
59 struct kobj_attribute active_coeff_c;
60 struct kobj_attribute leakage_coeff_a;
61 struct kobj_attribute leakage_coeff_b;
62 struct kobj_attribute leakage_coeff_c;
63 struct kobj_attribute leakage_coeff_d;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070064
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -070065 struct kobj_attribute thermal_poll_ms;
66
Steve Muckle118f47b2012-10-17 16:09:37 -070067 struct kobj_attribute freq_tbl;
Steve Muckle74da4a02012-11-28 16:29:46 -080068 struct kobj_attribute offset_tbl;
Steve Muckle118f47b2012-10-17 16:09:37 -070069
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070070 struct attribute_group attrib_group;
71};
72
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -070073enum pending_freq_state {
74 /*
75 * used by the thread to check if pending_freq was updated while it was
76 * setting previous frequency - this is written to and used by the
77 * freq updating thread
78 */
79 NO_OUTSTANDING_FREQ_CHANGE = 0,
80
81 /*
82 * This request is set to indicate that the governor is stopped and no
83 * more frequency change requests are accepted untill it starts again.
84 * This is checked/used by the threads that want to change the freq
85 */
86 STOP_FREQ_CHANGE = -1,
87
88 /*
89 * Any other +ve value means that a freq change was requested and the
90 * thread has not gotten around to update it
91 *
92 * Any other -ve value means that this is the last freq change i.e. a
93 * freq change was requested but the thread has not run yet and
94 * meanwhile the governor was stopped.
95 */
96};
97
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070098struct dcvs_core {
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -070099 spinlock_t idle_state_change_lock;
100 /* 0 when not idle (busy) 1 when idle and -1 when governor starts and
101 * we dont know whether the next call is going to be idle enter or exit
102 */
103 int idle_entered;
104
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700105 enum msm_dcvs_core_type type;
106 /* this is the number in each type for example cpu 0,1,2 and gpu 0,1 */
107 int type_core_num;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700108 char core_name[CORE_NAME_MAX];
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700109 uint32_t actual_freq;
110 uint32_t freq_change_us;
111
112 uint32_t max_time_us; /* core param */
113
114 struct msm_dcvs_algo_param algo_param;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700115 struct msm_dcvs_energy_curve_coeffs coeffs;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700116
117 /* private */
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700118 ktime_t time_start;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700119 struct task_struct *task;
120 struct core_attribs attrib;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700121 uint32_t dcvs_core_id;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700122 struct msm_dcvs_core_info *info;
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700123 int sensor;
Abhijeet Dharmapurikarbbb52fe2012-08-31 20:31:16 -0700124 wait_queue_head_t wait_q;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700125
126 int (*set_frequency)(int type_core_num, unsigned int freq);
127 unsigned int (*get_frequency)(int type_core_num);
128 int (*idle_enable)(int type_core_num,
Abhijeet Dharmapurikar6e9b34f2012-09-10 16:03:39 -0700129 enum msm_core_control_event event);
Steve Muckle93bb4252012-11-12 14:20:39 -0800130 int (*set_floor_frequency)(int type_core_num, unsigned int freq);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700131
132 spinlock_t pending_freq_lock;
133 int pending_freq;
134
135 struct hrtimer slack_timer;
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700136 struct delayed_work temperature_work;
Steve Mucklec1785c32012-11-13 14:27:43 -0800137 int flags;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700138};
139
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700140static int msm_dcvs_enabled = 1;
141module_param_named(enable, msm_dcvs_enabled, int, S_IRUGO | S_IWUSR | S_IWGRP);
142
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700143static struct dentry *debugfs_base;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700144
145static struct dcvs_core core_list[CORES_MAX];
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700146
147static struct kobject *cores_kobj;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700148
Steve Mucklea9aac292012-11-02 15:41:00 -0700149#define DCVS_MAX_NUM_FREQS 15
150static struct msm_dcvs_freq_entry cpu_freq_tbl[DCVS_MAX_NUM_FREQS];
151static unsigned num_cpu_freqs;
152static struct msm_dcvs_platform_data *dcvs_pdata;
153
Steve Muckle43a980c2012-11-19 16:38:55 -0800154static DEFINE_MUTEX(gpu_floor_mutex);
155
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700156static void force_stop_slack_timer(struct dcvs_core *core)
157{
158 unsigned long flags;
159
160 spin_lock_irqsave(&core->idle_state_change_lock, flags);
161 hrtimer_cancel(&core->slack_timer);
162 spin_unlock_irqrestore(&core->idle_state_change_lock, flags);
163}
164
Abhijeet Dharmapurikar8d843b32012-09-13 18:33:20 -0700165static void force_start_slack_timer(struct dcvs_core *core, int slack_us)
166{
167 unsigned long flags;
168 int ret;
169
170 spin_lock_irqsave(&core->idle_state_change_lock, flags);
171
172 /*
173 * only start the timer if governor is not stopped
174 */
175 if (slack_us != 0) {
176 ret = hrtimer_start(&core->slack_timer,
177 ktime_set(0, slack_us * 1000),
178 HRTIMER_MODE_REL_PINNED);
179 if (ret) {
180 pr_err("%s Failed to start timer ret = %d\n",
181 core->core_name, ret);
182 }
183 }
184
185 spin_unlock_irqrestore(&core->idle_state_change_lock, flags);
186}
187
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700188static void stop_slack_timer(struct dcvs_core *core)
189{
190 unsigned long flags;
191
192 spin_lock_irqsave(&core->idle_state_change_lock, flags);
193 /* err only for cpu type's GPU's can do idle exit consecutively */
194 if (core->idle_entered == 1 && !(core->dcvs_core_id >= GPU_OFFSET))
195 __err("%s trying to reenter idle", core->core_name);
196 core->idle_entered = 1;
197 hrtimer_cancel(&core->slack_timer);
198 core->idle_entered = 1;
199 spin_unlock_irqrestore(&core->idle_state_change_lock, flags);
200}
201
202static void start_slack_timer(struct dcvs_core *core, int slack_us)
203{
204 unsigned long flags1, flags2;
205 int ret;
206
207 spin_lock_irqsave(&core->idle_state_change_lock, flags2);
208
209 spin_lock_irqsave(&core->pending_freq_lock, flags1);
210
211 /* err only for cpu type's GPU's can do idle enter consecutively */
212 if (core->idle_entered == 0 && !(core->dcvs_core_id >= GPU_OFFSET))
213 __err("%s trying to reexit idle", core->core_name);
214 core->idle_entered = 0;
215 /*
216 * only start the timer if governor is not stopped
217 */
218 if (slack_us != 0
219 && !(core->pending_freq < NO_OUTSTANDING_FREQ_CHANGE)) {
220 ret = hrtimer_start(&core->slack_timer,
221 ktime_set(0, slack_us * 1000),
222 HRTIMER_MODE_REL_PINNED);
223 if (ret) {
224 pr_err("%s Failed to start timer ret = %d\n",
225 core->core_name, ret);
226 }
227 }
228 spin_unlock_irqrestore(&core->pending_freq_lock, flags1);
229
230 spin_unlock_irqrestore(&core->idle_state_change_lock, flags2);
231}
232
233static void restart_slack_timer(struct dcvs_core *core, int slack_us)
234{
235 unsigned long flags1, flags2;
236 int ret;
237
238 spin_lock_irqsave(&core->idle_state_change_lock, flags2);
239
240 hrtimer_cancel(&core->slack_timer);
241
242 spin_lock_irqsave(&core->pending_freq_lock, flags1);
243
244 /*
245 * only start the timer if idle is not entered
246 * and governor is not stopped
247 */
248 if (slack_us != 0 && (core->idle_entered != 1)
249 && !(core->pending_freq < NO_OUTSTANDING_FREQ_CHANGE)) {
250 ret = hrtimer_start(&core->slack_timer,
251 ktime_set(0, slack_us * 1000),
252 HRTIMER_MODE_REL_PINNED);
253 if (ret) {
254 pr_err("%s Failed to start timer ret = %d\n",
255 core->core_name, ret);
256 }
257 }
258 spin_unlock_irqrestore(&core->pending_freq_lock, flags1);
259 spin_unlock_irqrestore(&core->idle_state_change_lock, flags2);
260}
261
Steve Muckle28ddcdd2012-11-21 10:12:39 -0800262void msm_dcvs_apply_gpu_floor(unsigned long cpu_freq)
Steve Muckle93bb4252012-11-12 14:20:39 -0800263{
Steve Muckle28ddcdd2012-11-21 10:12:39 -0800264 static unsigned long curr_cpu0_freq;
265 unsigned long gpu_floor_freq = 0;
Steve Muckle93bb4252012-11-12 14:20:39 -0800266 struct dcvs_core *gpu;
Steve Muckle28ddcdd2012-11-21 10:12:39 -0800267 int i;
Steve Muckle93bb4252012-11-12 14:20:39 -0800268
269 if (!dcvs_pdata)
270 return;
271
Steve Muckle28ddcdd2012-11-21 10:12:39 -0800272 mutex_lock(&gpu_floor_mutex);
273
274 if (cpu_freq)
275 curr_cpu0_freq = cpu_freq;
276
Steve Muckle93bb4252012-11-12 14:20:39 -0800277 for (i = 0; i < dcvs_pdata->num_sync_rules; i++)
Steve Muckle28ddcdd2012-11-21 10:12:39 -0800278 if (curr_cpu0_freq > dcvs_pdata->sync_rules[i].cpu_khz) {
Steve Muckle93bb4252012-11-12 14:20:39 -0800279 gpu_floor_freq =
280 dcvs_pdata->sync_rules[i].gpu_floor_khz;
281 break;
282 }
283
Steve Muckle28ddcdd2012-11-21 10:12:39 -0800284 if (num_online_cpus() > 1)
285 gpu_floor_freq = max(gpu_floor_freq,
286 dcvs_pdata->gpu_max_nom_khz);
287
288 if (!gpu_floor_freq) {
289 mutex_unlock(&gpu_floor_mutex);
Steve Muckle93bb4252012-11-12 14:20:39 -0800290 return;
Steve Muckle28ddcdd2012-11-21 10:12:39 -0800291 }
Steve Muckle93bb4252012-11-12 14:20:39 -0800292
293 for (i = GPU_OFFSET; i < CORES_MAX; i++) {
294 gpu = &core_list[i];
295 if (gpu->dcvs_core_id == -1)
296 continue;
Steve Muckle28ddcdd2012-11-21 10:12:39 -0800297
Steve Muckle43a980c2012-11-19 16:38:55 -0800298 if (gpu->pending_freq != STOP_FREQ_CHANGE &&
Steve Mucklea46930c2012-11-28 17:00:29 -0800299 gpu->set_floor_frequency) {
Steve Muckle93bb4252012-11-12 14:20:39 -0800300 gpu->set_floor_frequency(gpu->type_core_num,
301 gpu_floor_freq);
Steve Mucklea46930c2012-11-28 17:00:29 -0800302 /* TZ will know about a freq change (if any)
303 * at next idle exit. */
304 gpu->actual_freq =
305 gpu->get_frequency(gpu->type_core_num);
306 }
Steve Muckle93bb4252012-11-12 14:20:39 -0800307 }
Steve Muckle28ddcdd2012-11-21 10:12:39 -0800308
309 mutex_unlock(&gpu_floor_mutex);
Steve Muckle93bb4252012-11-12 14:20:39 -0800310}
311
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700312static int __msm_dcvs_change_freq(struct dcvs_core *core)
313{
314 int ret = 0;
315 unsigned long flags = 0;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700316 int requested_freq = 0;
317 ktime_t time_start;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700318 uint32_t slack_us = 0;
319 uint32_t ret1 = 0;
320
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700321 spin_lock_irqsave(&core->pending_freq_lock, flags);
Steve Muckle80e0b7b2012-11-19 15:46:39 -0800322 if (core->pending_freq == STOP_FREQ_CHANGE)
323 goto out;
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700324repeat:
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700325 BUG_ON(!core->pending_freq);
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700326
327 requested_freq = core->pending_freq;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700328 time_start = core->time_start;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700329 core->time_start = ns_to_ktime(0);
330
Steve Muckle80e0b7b2012-11-19 15:46:39 -0800331 core->pending_freq = NO_OUTSTANDING_FREQ_CHANGE;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700332
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700333 if (requested_freq == core->actual_freq)
334 goto out;
335
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700336 spin_unlock_irqrestore(&core->pending_freq_lock, flags);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700337
Steve Muckle93bb4252012-11-12 14:20:39 -0800338 if (core->type == MSM_DCVS_CORE_TYPE_CPU &&
339 core->type_core_num == 0)
Steve Muckle28ddcdd2012-11-21 10:12:39 -0800340 msm_dcvs_apply_gpu_floor(requested_freq);
Steve Muckle93bb4252012-11-12 14:20:39 -0800341
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700342 /**
343 * Call the frequency sink driver to change the frequency
344 * We will need to get back the actual frequency in KHz and
345 * the record the time taken to change it.
346 */
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700347 ret = core->set_frequency(core->type_core_num, requested_freq);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700348 if (ret <= 0)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700349 __err("Core %s failed to set freq %u\n",
350 core->core_name, requested_freq);
Eugene Seah76af9832012-03-28 18:43:53 -0600351 /* continue to call TZ to get updated slack timer */
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700352 else
Eugene Seah76af9832012-03-28 18:43:53 -0600353 core->actual_freq = ret;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700354
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700355 core->freq_change_us = (uint32_t)ktime_to_us(
356 ktime_sub(ktime_get(), time_start));
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700357
358 /**
359 * Disable low power modes if the actual frequency is >
360 * disable_pc_threshold.
361 */
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700362 if (core->actual_freq > core->algo_param.disable_pc_threshold) {
363 core->idle_enable(core->type_core_num,
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700364 MSM_DCVS_DISABLE_HIGH_LATENCY_MODES);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700365 } else if (core->actual_freq <= core->algo_param.disable_pc_threshold) {
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700366 core->idle_enable(core->type_core_num,
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700367 MSM_DCVS_ENABLE_HIGH_LATENCY_MODES);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700368 }
369
370 /**
371 * Update algorithm with new freq and time taken to change
372 * to this frequency and that will get us the new slack
373 * timer
374 */
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700375 ret = msm_dcvs_scm_event(core->dcvs_core_id,
376 MSM_DCVS_SCM_CLOCK_FREQ_UPDATE,
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700377 core->actual_freq, core->freq_change_us,
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700378 &slack_us, &ret1);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700379 if (ret) {
380 __err("Error sending core (%s) dcvs_core_id = %d freq change (%u) reqfreq = %d slack_us=%d ret = %d\n",
381 core->core_name, core->dcvs_core_id,
382 core->actual_freq, requested_freq,
383 slack_us, ret);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700384 }
385
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700386 /* TODO confirm that we get a valid freq from SM even when the above
387 * FREQ_UPDATE fails
388 */
389 restart_slack_timer(core, slack_us);
390 spin_lock_irqsave(&core->pending_freq_lock, flags);
391
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700392 /**
393 * By the time we are done with freq changes, we could be asked to
394 * change again. Check before exiting.
395 */
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700396 if (core->pending_freq != NO_OUTSTANDING_FREQ_CHANGE
397 && core->pending_freq != STOP_FREQ_CHANGE) {
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700398 goto repeat;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700399 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700400
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700401out: /* should always be jumped to with the spin_lock held */
402 spin_unlock_irqrestore(&core->pending_freq_lock, flags);
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700403
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700404 return ret;
405}
406
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700407static void msm_dcvs_report_temp_work(struct work_struct *work)
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700408{
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700409 struct dcvs_core *core = container_of(work,
410 struct dcvs_core,
411 temperature_work.work);
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700412 struct msm_dcvs_core_info *info = core->info;
413 struct tsens_device tsens_dev;
414 int ret;
415 unsigned long temp = 0;
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700416 int interval_ms;
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700417
Steve Mucklec1785c32012-11-13 14:27:43 -0800418 if (!(core->flags & CORE_FLAG_TEMP_UPDATE))
419 return;
420
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700421 tsens_dev.sensor_num = core->sensor;
422 ret = tsens_get_temp(&tsens_dev, &temp);
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700423 if (!temp) {
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700424 tsens_dev.sensor_num = 0;
425 ret = tsens_get_temp(&tsens_dev, &temp);
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700426 if (!temp)
427 goto out;
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700428 }
429
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700430 if (temp == info->power_param.current_temp)
431 goto out;
432 info->power_param.current_temp = temp;
433
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700434 ret = msm_dcvs_scm_set_power_params(core->dcvs_core_id,
435 &info->power_param,
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700436 &info->freq_tbl[0], &core->coeffs);
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700437out:
438 if (info->thermal_poll_ms == 0)
439 interval_ms = 60000;
440 else if (info->thermal_poll_ms < 1000)
441 interval_ms = 1000;
442 else
443 interval_ms = info->thermal_poll_ms;
444
445 schedule_delayed_work(&core->temperature_work,
446 msecs_to_jiffies(interval_ms));
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700447}
448
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700449static int msm_dcvs_do_freq(void *data)
450{
451 struct dcvs_core *core = (struct dcvs_core *)data;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700452
453 while (!kthread_should_stop()) {
Abhijeet Dharmapurikarbbb52fe2012-08-31 20:31:16 -0700454 wait_event(core->wait_q, !(core->pending_freq == 0 ||
455 core->pending_freq == -1) ||
456 kthread_should_stop());
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700457
458 if (kthread_should_stop())
459 break;
460
Abhijeet Dharmapurikarbbb52fe2012-08-31 20:31:16 -0700461 __msm_dcvs_change_freq(core);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700462 }
463
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700464 return 0;
465}
466
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700467/* freq_pending_lock should be held */
468static void request_freq_change(struct dcvs_core *core, int new_freq)
469{
470 if (new_freq == NO_OUTSTANDING_FREQ_CHANGE) {
471 if (core->pending_freq != STOP_FREQ_CHANGE) {
472 __err("%s gov started with earlier pending freq %d\n",
473 core->core_name, core->pending_freq);
474 }
475 core->pending_freq = NO_OUTSTANDING_FREQ_CHANGE;
476 return;
477 }
478
479 if (new_freq == STOP_FREQ_CHANGE) {
Steve Muckle80e0b7b2012-11-19 15:46:39 -0800480 core->pending_freq = STOP_FREQ_CHANGE;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700481 return;
482 }
483
484 if (core->pending_freq < 0) {
485 /* a value less than 0 means that the governor has stopped
486 * and no more freq changes should be requested
487 */
488 return;
489 }
490
491 if (core->actual_freq != new_freq && core->pending_freq != new_freq) {
492 core->pending_freq = new_freq;
493 core->time_start = ktime_get();
494 wake_up(&core->wait_q);
495 }
496}
497
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700498static int msm_dcvs_update_freq(struct dcvs_core *core,
Eugene Seah76af9832012-03-28 18:43:53 -0600499 enum msm_dcvs_scm_event event, uint32_t param0,
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700500 uint32_t *ret1)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700501{
502 int ret = 0;
503 unsigned long flags = 0;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700504 uint32_t new_freq = -EINVAL;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700505
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700506 spin_lock_irqsave(&core->pending_freq_lock, flags);
507
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700508 ret = msm_dcvs_scm_event(core->dcvs_core_id, event, param0,
Eugene Seah76af9832012-03-28 18:43:53 -0600509 core->actual_freq, &new_freq, ret1);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700510 if (ret) {
Jeff Ohlstein4aa4a2b2012-06-28 19:03:49 -0700511 if (ret == -13)
512 ret = 0;
513 else
514 __err("Error (%d) sending SCM event %d for core %s\n",
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700515 ret, event, core->core_name);
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700516 goto out;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700517 }
518
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700519 if (new_freq == 0) {
520 /*
521 * sometimes TZ gives us a 0 freq back,
522 * do not queue up a request
523 */
524 goto out;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700525 }
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700526
527 request_freq_change(core, new_freq);
528
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700529out:
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700530 spin_unlock_irqrestore(&core->pending_freq_lock, flags);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700531
532 return ret;
533}
534
535static enum hrtimer_restart msm_dcvs_core_slack_timer(struct hrtimer *timer)
536{
537 int ret = 0;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700538 struct dcvs_core *core = container_of(timer,
539 struct dcvs_core, slack_timer);
Eugene Seah76af9832012-03-28 18:43:53 -0600540 uint32_t ret1;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700541
Abhijeet Dharmapurikar07cf2ff2012-09-13 19:05:13 -0700542 trace_printk("dcvs: Slack timer fired for core=%s\n", core->core_name);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700543 /**
544 * Timer expired, notify TZ
545 * Dont care about the third arg.
546 */
Eugene Seah76af9832012-03-28 18:43:53 -0600547 ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_QOS_TIMER_EXPIRED, 0,
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700548 &ret1);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700549 if (ret)
550 __err("Timer expired for core %s but failed to notify.\n",
551 core->core_name);
552
553 return HRTIMER_NORESTART;
554}
555
Steve Muckle388cc2e2012-11-21 15:47:15 -0800556int msm_dcvs_update_algo_params(void)
557{
558 static struct msm_dcvs_algo_param curr_params;
559 static DEFINE_MUTEX(param_update_mutex);
560 struct msm_dcvs_algo_param *new_params;
561 int cpu, ret = 0;
562
563 mutex_lock(&param_update_mutex);
564 new_params = &core_list[CPU_OFFSET + num_online_cpus() - 1].algo_param;
565
566 if (memcmp(&curr_params, new_params,
567 sizeof(struct msm_dcvs_algo_param))) {
568 for_each_possible_cpu(cpu) {
569 ret = msm_dcvs_scm_set_algo_params(CPU_OFFSET + cpu,
570 new_params);
571 if (ret) {
572 pr_err("scm set algo params failed on cpu %d, ret %d\n",
573 cpu, ret);
574 mutex_unlock(&param_update_mutex);
575 return ret;
576 }
577 }
578 memcpy(&curr_params, new_params,
579 sizeof(struct msm_dcvs_algo_param));
580 }
581
582 mutex_unlock(&param_update_mutex);
583 return ret;
584}
585
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700586/* Helper functions and macros for sysfs nodes for a core */
587#define CORE_FROM_ATTRIBS(attr, name) \
588 container_of(container_of(attr, struct core_attribs, name), \
589 struct dcvs_core, attrib);
590
591#define DCVS_PARAM_SHOW(_name, v) \
592static ssize_t msm_dcvs_attr_##_name##_show(struct kobject *kobj, \
593 struct kobj_attribute *attr, char *buf) \
594{ \
595 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
596 return snprintf(buf, PAGE_SIZE, "%d\n", v); \
597}
598
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700599#define DCVS_PARAM_STORE(_name) \
600static ssize_t msm_dcvs_attr_##_name##_show(struct kobject *kobj,\
601 struct kobj_attribute *attr, char *buf) \
602{ \
603 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
604 return snprintf(buf, PAGE_SIZE, "%d\n", core->info->_name); \
605} \
606static ssize_t msm_dcvs_attr_##_name##_store(struct kobject *kobj, \
607 struct kobj_attribute *attr, const char *buf, size_t count) \
608{ \
609 int ret = 0; \
610 uint32_t val = 0; \
611 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
612 ret = kstrtouint(buf, 10, &val); \
613 if (ret) { \
614 __err("Invalid input %s for %s\n", buf, __stringify(_name));\
615 } else { \
616 core->info->_name = val; \
617 } \
618 return count; \
619}
620
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700621#define DCVS_ALGO_PARAM(_name) \
622static ssize_t msm_dcvs_attr_##_name##_show(struct kobject *kobj,\
623 struct kobj_attribute *attr, char *buf) \
624{ \
625 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
626 return snprintf(buf, PAGE_SIZE, "%d\n", core->algo_param._name); \
627} \
628static ssize_t msm_dcvs_attr_##_name##_store(struct kobject *kobj, \
629 struct kobj_attribute *attr, const char *buf, size_t count) \
630{ \
631 int ret = 0; \
632 uint32_t val = 0; \
633 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700634 ret = kstrtouint(buf, 10, &val); \
635 if (ret) { \
636 __err("Invalid input %s for %s\n", buf, __stringify(_name));\
637 } else { \
638 uint32_t old_val = core->algo_param._name; \
639 core->algo_param._name = val; \
Steve Muckle388cc2e2012-11-21 15:47:15 -0800640 ret = msm_dcvs_update_algo_params(); \
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700641 if (ret) { \
642 core->algo_param._name = old_val; \
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700643 } \
644 } \
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700645 return count; \
646}
647
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700648#define DCVS_ENERGY_PARAM(_name) \
649static ssize_t msm_dcvs_attr_##_name##_show(struct kobject *kobj,\
650 struct kobj_attribute *attr, char *buf) \
651{ \
652 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
653 return snprintf(buf, PAGE_SIZE, "%d\n", core->coeffs._name); \
654} \
655static ssize_t msm_dcvs_attr_##_name##_store(struct kobject *kobj, \
656 struct kobj_attribute *attr, const char *buf, size_t count) \
657{ \
658 int ret = 0; \
659 int32_t val = 0; \
660 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700661 ret = kstrtoint(buf, 10, &val); \
662 if (ret) { \
663 __err("Invalid input %s for %s\n", buf, __stringify(_name));\
664 } else { \
665 int32_t old_val = core->coeffs._name; \
666 core->coeffs._name = val; \
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700667 ret = msm_dcvs_scm_set_power_params(core->dcvs_core_id, \
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700668 &core->info->power_param, &core->info->freq_tbl[0], \
669 &core->coeffs); \
670 if (ret) { \
671 core->coeffs._name = old_val; \
672 __err("Error(%d) in setting %d for coeffs param %s\n",\
673 ret, val, __stringify(_name)); \
674 } \
675 } \
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700676 return count; \
677}
678
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700679#define DCVS_RO_ATTRIB(i, _name) \
680 core->attrib._name.attr.name = __stringify(_name); \
681 core->attrib._name.attr.mode = S_IRUGO; \
682 core->attrib._name.show = msm_dcvs_attr_##_name##_show; \
683 core->attrib._name.store = NULL; \
684 core->attrib.attrib_group.attrs[i] = &core->attrib._name.attr;
685
686#define DCVS_RW_ATTRIB(i, _name) \
687 core->attrib._name.attr.name = __stringify(_name); \
688 core->attrib._name.attr.mode = S_IRUGO | S_IWUSR; \
689 core->attrib._name.show = msm_dcvs_attr_##_name##_show; \
690 core->attrib._name.store = msm_dcvs_attr_##_name##_store; \
691 core->attrib.attrib_group.attrs[i] = &core->attrib._name.attr;
692
693/**
694 * Function declarations for different attributes.
695 * Gets used when setting the attribute show and store parameters.
696 */
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700697DCVS_PARAM_SHOW(freq_change_us, (core->freq_change_us))
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700698
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700699DCVS_ALGO_PARAM(disable_pc_threshold)
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700700DCVS_ALGO_PARAM(em_win_size_min_us)
701DCVS_ALGO_PARAM(em_win_size_max_us)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700702DCVS_ALGO_PARAM(em_max_util_pct)
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700703DCVS_ALGO_PARAM(group_id)
704DCVS_ALGO_PARAM(max_freq_chg_time_us)
705DCVS_ALGO_PARAM(slack_mode_dynamic)
706DCVS_ALGO_PARAM(slack_time_min_us)
707DCVS_ALGO_PARAM(slack_time_max_us)
708DCVS_ALGO_PARAM(slack_weight_thresh_pct)
Steve Muckle8d0782e2012-12-06 14:31:00 -0800709DCVS_ALGO_PARAM(ss_no_corr_below_freq)
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700710DCVS_ALGO_PARAM(ss_win_size_min_us)
711DCVS_ALGO_PARAM(ss_win_size_max_us)
712DCVS_ALGO_PARAM(ss_util_pct)
713
714DCVS_ENERGY_PARAM(active_coeff_a)
715DCVS_ENERGY_PARAM(active_coeff_b)
716DCVS_ENERGY_PARAM(active_coeff_c)
717DCVS_ENERGY_PARAM(leakage_coeff_a)
718DCVS_ENERGY_PARAM(leakage_coeff_b)
719DCVS_ENERGY_PARAM(leakage_coeff_c)
720DCVS_ENERGY_PARAM(leakage_coeff_d)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700721
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700722DCVS_PARAM_STORE(thermal_poll_ms)
723
Steve Muckle74da4a02012-11-28 16:29:46 -0800724static ssize_t msm_dcvs_attr_offset_tbl_show(struct kobject *kobj,
725 struct kobj_attribute *attr,
726 char *buf)
727{
728 struct msm_dcvs_freq_entry *freq_tbl;
729 char *buf_idx = buf;
730 int i, len;
731 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, offset_tbl);
732
733 freq_tbl = core->info->freq_tbl;
734 *buf_idx = '\0';
735
736 /* limit the number of frequencies we will print into
737 * the PAGE_SIZE sysfs show buffer. */
738 if (core->info->power_param.num_freq > 64)
739 return 0;
740
741 for (i = 0; i < core->info->power_param.num_freq; i++) {
742 len = snprintf(buf_idx, 30, "%7d %7d %7d\n",
743 freq_tbl[i].freq,
744 freq_tbl[i].active_energy_offset,
745 freq_tbl[i].leakage_energy_offset);
746 /* buf_idx always points at terminating null */
747 buf_idx += len;
748 }
749 return buf_idx - buf;
750}
751
752static ssize_t msm_dcvs_attr_offset_tbl_store(struct kobject *kobj,
753 struct kobj_attribute *attr,
754 const char *buf,
755 size_t count)
756{
757 struct msm_dcvs_freq_entry *freq_tbl;
758 uint32_t freq, active_energy_offset, leakage_energy_offset;
759 int i, ret;
760 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, offset_tbl);
761
762 freq_tbl = core->info->freq_tbl;
763
764 ret = sscanf(buf, "%u %u %u",
765 &freq, &active_energy_offset, &leakage_energy_offset);
766 if (ret != 3) {
767 __err("Invalid input %s for offset_tbl\n", buf);
768 return count;
769 }
770
771 for (i = 0; i < core->info->power_param.num_freq; i++)
772 if (freq_tbl[i].freq == freq) {
773 freq_tbl[i].active_energy_offset =
774 active_energy_offset;
775 freq_tbl[i].leakage_energy_offset =
776 leakage_energy_offset;
777 break;
778 }
779
780 if (i >= core->info->power_param.num_freq) {
781 __err("Invalid frequency for offset_tbl: %d\n", freq);
782 return count;
783 }
784
785 ret = msm_dcvs_scm_set_power_params(core->dcvs_core_id,
786 &core->info->power_param,
787 &core->info->freq_tbl[0],
788 &core->coeffs);
789 if (ret)
790 __err("Error %d in updating active/leakage energy\n", ret);
791
792 return count;
793}
794
Steve Muckle118f47b2012-10-17 16:09:37 -0700795static ssize_t msm_dcvs_attr_freq_tbl_show(struct kobject *kobj,
796 struct kobj_attribute *attr,
797 char *buf)
798{
799 struct msm_dcvs_freq_entry *freq_tbl;
800 char *buf_idx = buf;
801 int i, len;
802 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, freq_tbl);
803
804 freq_tbl = core->info->freq_tbl;
805 *buf_idx = '\0';
806
807 /* limit the number of frequencies we will print into
808 * the PAGE_SIZE sysfs show buffer. */
809 if (core->info->power_param.num_freq > 64)
810 return 0;
811
812 for (i = 0; i < core->info->power_param.num_freq; i++) {
813 if (freq_tbl[i].is_trans_level) {
814 len = snprintf(buf_idx, 10, "%7d ", freq_tbl[i].freq);
815 /* buf_idx always points at terminating null */
816 buf_idx += len;
817 }
818 }
819 /* overwrite final trailing space with newline */
820 if (buf_idx > buf)
821 *(buf_idx - 1) = '\n';
822
823 return buf_idx - buf;
824}
825
826static ssize_t msm_dcvs_attr_freq_tbl_store(struct kobject *kobj,
827 struct kobj_attribute *attr,
828 const char *buf,
829 size_t count)
830{
831 struct msm_dcvs_freq_entry *freq_tbl;
832 uint32_t freq;
833 int i, ret;
834 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, freq_tbl);
835
836 freq_tbl = core->info->freq_tbl;
837
838 ret = kstrtouint(buf, 10, &freq);
839 if (ret) {
840 __err("Invalid input %s for freq_tbl\n", buf);
841 return count;
842 }
843
844 for (i = 0; i < core->info->power_param.num_freq; i++)
845 if (freq_tbl[i].freq == freq) {
846 freq_tbl[i].is_trans_level ^= 1;
847 break;
848 }
849
850 if (i >= core->info->power_param.num_freq) {
851 __err("Invalid frequency for freq_tbl: %d\n", freq);
852 return count;
853 }
854
855 ret = msm_dcvs_scm_set_power_params(core->dcvs_core_id,
856 &core->info->power_param,
857 &core->info->freq_tbl[0],
858 &core->coeffs);
859 if (ret) {
860 freq_tbl[i].is_trans_level ^= 1;
861 __err("Error %d in toggling freq %d (orig enable val %d)\n",
862 ret, freq_tbl[i].freq, freq_tbl[i].is_trans_level);
863 }
864 return count;
865}
866
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700867static int msm_dcvs_setup_core_sysfs(struct dcvs_core *core)
868{
869 int ret = 0;
870 struct kobject *core_kobj = NULL;
Steve Muckle74da4a02012-11-28 16:29:46 -0800871 const int attr_count = 26;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700872
873 BUG_ON(!cores_kobj);
874
875 core->attrib.attrib_group.attrs =
876 kzalloc(attr_count * sizeof(struct attribute *), GFP_KERNEL);
877
878 if (!core->attrib.attrib_group.attrs) {
879 ret = -ENOMEM;
880 goto done;
881 }
882
Abhijeet Dharmapurikar35876382012-09-13 16:09:51 -0700883 DCVS_RO_ATTRIB(0, freq_change_us);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700884
Abhijeet Dharmapurikar35876382012-09-13 16:09:51 -0700885 DCVS_RW_ATTRIB(1, disable_pc_threshold);
886 DCVS_RW_ATTRIB(2, em_win_size_min_us);
887 DCVS_RW_ATTRIB(3, em_win_size_max_us);
888 DCVS_RW_ATTRIB(4, em_max_util_pct);
889 DCVS_RW_ATTRIB(5, group_id);
890 DCVS_RW_ATTRIB(6, max_freq_chg_time_us);
891 DCVS_RW_ATTRIB(7, slack_mode_dynamic);
892 DCVS_RW_ATTRIB(8, slack_weight_thresh_pct);
893 DCVS_RW_ATTRIB(9, slack_time_min_us);
894 DCVS_RW_ATTRIB(10, slack_time_max_us);
Steve Muckle8d0782e2012-12-06 14:31:00 -0800895 DCVS_RW_ATTRIB(11, ss_no_corr_below_freq);
Abhijeet Dharmapurikar35876382012-09-13 16:09:51 -0700896 DCVS_RW_ATTRIB(12, ss_win_size_min_us);
897 DCVS_RW_ATTRIB(13, ss_win_size_max_us);
898 DCVS_RW_ATTRIB(14, ss_util_pct);
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700899
Abhijeet Dharmapurikar35876382012-09-13 16:09:51 -0700900 DCVS_RW_ATTRIB(15, active_coeff_a);
901 DCVS_RW_ATTRIB(16, active_coeff_b);
902 DCVS_RW_ATTRIB(17, active_coeff_c);
903 DCVS_RW_ATTRIB(18, leakage_coeff_a);
904 DCVS_RW_ATTRIB(19, leakage_coeff_b);
905 DCVS_RW_ATTRIB(20, leakage_coeff_c);
906 DCVS_RW_ATTRIB(21, leakage_coeff_d);
907 DCVS_RW_ATTRIB(22, thermal_poll_ms);
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700908
Steve Muckle118f47b2012-10-17 16:09:37 -0700909 DCVS_RW_ATTRIB(23, freq_tbl);
Steve Muckle74da4a02012-11-28 16:29:46 -0800910 DCVS_RW_ATTRIB(24, offset_tbl);
Steve Muckle118f47b2012-10-17 16:09:37 -0700911
Steve Muckle74da4a02012-11-28 16:29:46 -0800912 core->attrib.attrib_group.attrs[25] = NULL;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700913
914 core_kobj = kobject_create_and_add(core->core_name, cores_kobj);
915 if (!core_kobj) {
916 ret = -ENOMEM;
917 goto done;
918 }
919
920 ret = sysfs_create_group(core_kobj, &core->attrib.attrib_group);
921 if (ret)
922 __err("Cannot create core %s attr group\n", core->core_name);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700923
924done:
925 if (ret) {
926 kfree(core->attrib.attrib_group.attrs);
927 kobject_del(core_kobj);
928 }
929
930 return ret;
931}
932
Steve Muckle5e5a60d2012-10-09 13:25:22 -0700933static int get_core_offset(enum msm_dcvs_core_type type, int num)
934{
935 int offset = -EINVAL;
936
937 switch (type) {
938 case MSM_DCVS_CORE_TYPE_CPU:
939 offset = CPU_OFFSET + num;
940 BUG_ON(offset >= GPU_OFFSET);
941 break;
942 case MSM_DCVS_CORE_TYPE_GPU:
943 offset = GPU_OFFSET + num;
944 BUG_ON(offset >= CORES_MAX);
945 break;
946 default:
947 BUG();
948 }
949
950 return offset;
951}
952
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700953/* Return the core and initialize non platform data specific numbers in it */
954static struct dcvs_core *msm_dcvs_add_core(enum msm_dcvs_core_type type,
955 int num)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700956{
957 struct dcvs_core *core = NULL;
958 int i;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700959 char name[CORE_NAME_MAX];
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700960
Steve Muckle5e5a60d2012-10-09 13:25:22 -0700961 i = get_core_offset(type, num);
962 if (i < 0)
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700963 return NULL;
Steve Muckle5e5a60d2012-10-09 13:25:22 -0700964
965 if (type == MSM_DCVS_CORE_TYPE_CPU)
966 snprintf(name, CORE_NAME_MAX, "cpu%d", num);
967 else
968 snprintf(name, CORE_NAME_MAX, "gpu%d", num);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700969
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700970 core = &core_list[i];
971 core->dcvs_core_id = i;
Abhijeet Dharmapurikar2ebc0fe2012-09-12 14:05:13 -0700972 strlcpy(core->core_name, name, CORE_NAME_MAX);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700973 spin_lock_init(&core->pending_freq_lock);
974 spin_lock_init(&core->idle_state_change_lock);
975 hrtimer_init(&core->slack_timer,
976 CLOCK_MONOTONIC, HRTIMER_MODE_REL_PINNED);
977 core->slack_timer.function = msm_dcvs_core_slack_timer;
Abhijeet Dharmapurikar2ebc0fe2012-09-12 14:05:13 -0700978 return core;
979}
980
981/* Return the core if found or add to list if @add_to_list is true */
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700982static struct dcvs_core *msm_dcvs_get_core(int offset)
Abhijeet Dharmapurikar2ebc0fe2012-09-12 14:05:13 -0700983{
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700984 /* if the handle is still not set bug */
985 BUG_ON(core_list[offset].dcvs_core_id == -1);
986 return &core_list[offset];
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700987}
988
Steve Mucklea9aac292012-11-02 15:41:00 -0700989void msm_dcvs_register_cpu_freq(uint32_t freq, uint32_t voltage)
990{
991 BUG_ON(freq == 0 || voltage == 0 ||
992 num_cpu_freqs == DCVS_MAX_NUM_FREQS);
993
994 cpu_freq_tbl[num_cpu_freqs].freq = freq;
995 cpu_freq_tbl[num_cpu_freqs].voltage = voltage;
996
997 num_cpu_freqs++;
998}
999
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001000int msm_dcvs_register_core(
1001 enum msm_dcvs_core_type type,
1002 int type_core_num,
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -07001003 struct msm_dcvs_core_info *info,
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001004 int (*set_frequency)(int type_core_num, unsigned int freq),
1005 unsigned int (*get_frequency)(int type_core_num),
1006 int (*idle_enable)(int type_core_num,
Abhijeet Dharmapurikar6e9b34f2012-09-10 16:03:39 -07001007 enum msm_core_control_event event),
Steve Muckle93bb4252012-11-12 14:20:39 -08001008 int (*set_floor_frequency)(int type_core_num, unsigned int freq),
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -07001009 int sensor)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001010{
1011 int ret = -EINVAL;
Steve Muckle5e5a60d2012-10-09 13:25:22 -07001012 int offset;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001013 struct dcvs_core *core = NULL;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -07001014 uint32_t ret1;
1015 uint32_t ret2;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001016
Steve Muckle5e5a60d2012-10-09 13:25:22 -07001017 offset = get_core_offset(type, type_core_num);
1018 if (offset < 0)
1019 return ret;
1020 if (core_list[offset].dcvs_core_id != -1)
1021 return core_list[offset].dcvs_core_id;
1022
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001023 core = msm_dcvs_add_core(type, type_core_num);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001024 if (!core)
1025 return ret;
1026
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001027 core->type = type;
1028 core->type_core_num = type_core_num;
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -07001029 core->set_frequency = set_frequency;
1030 core->get_frequency = get_frequency;
Abhijeet Dharmapurikar6e9b34f2012-09-10 16:03:39 -07001031 core->idle_enable = idle_enable;
Steve Muckle93bb4252012-11-12 14:20:39 -08001032 core->set_floor_frequency = set_floor_frequency;
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -07001033
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -07001034 core->info = info;
Steve Muckle74da4a02012-11-28 16:29:46 -08001035 if (type == MSM_DCVS_CORE_TYPE_CPU) {
1036 BUG_ON(num_cpu_freqs == 0);
1037 info->freq_tbl = cpu_freq_tbl;
1038 info->power_param.num_freq = num_cpu_freqs;
1039 }
Steve Mucklea9aac292012-11-02 15:41:00 -07001040
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001041 memcpy(&core->algo_param, &info->algo_param,
1042 sizeof(struct msm_dcvs_algo_param));
1043
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -07001044 memcpy(&core->coeffs, &info->energy_coeffs,
1045 sizeof(struct msm_dcvs_energy_curve_coeffs));
1046
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001047 /*
1048 * The tz expects cpu0 to represent bit 0 in the mask, however the
1049 * dcvs_core_id needs to start from 1, dcvs_core_id = 0 is used to
1050 * indicate that this request is not associated with any core.
1051 * mpdecision
1052 */
1053 info->core_param.core_bitmask_id
1054 = 1 << (core->dcvs_core_id - CPU_OFFSET);
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -07001055 core->sensor = sensor;
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -07001056
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001057 ret = msm_dcvs_scm_register_core(core->dcvs_core_id, &info->core_param);
1058 if (ret) {
1059 __err("%s: scm register core fail handle = %d ret = %d\n",
1060 __func__, core->dcvs_core_id, ret);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001061 goto bail;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001062 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001063
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001064 ret = msm_dcvs_scm_set_algo_params(core->dcvs_core_id,
1065 &info->algo_param);
1066 if (ret) {
1067 __err("%s: scm algo params failed ret = %d\n", __func__, ret);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001068 goto bail;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001069 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001070
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001071 ret = msm_dcvs_scm_set_power_params(core->dcvs_core_id,
1072 &info->power_param,
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -07001073 &info->freq_tbl[0], &core->coeffs);
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001074 if (ret) {
1075 __err("%s: scm power params failed ret = %d\n", __func__, ret);
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -07001076 goto bail;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001077 }
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -07001078
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001079 ret = msm_dcvs_scm_event(core->dcvs_core_id, MSM_DCVS_SCM_CORE_ONLINE,
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -07001080 core->actual_freq, 0, &ret1, &ret2);
1081 if (ret)
1082 goto bail;
1083
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001084 ret = msm_dcvs_setup_core_sysfs(core);
1085 if (ret) {
1086 __err("Unable to setup core %s sysfs\n", core->core_name);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001087 goto bail;
1088 }
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001089 core->idle_entered = -1;
Abhijeet Dharmapurikarbbb52fe2012-08-31 20:31:16 -07001090 init_waitqueue_head(&core->wait_q);
1091 core->task = kthread_run(msm_dcvs_do_freq, (void *)core,
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001092 "msm_dcvs/%d", core->dcvs_core_id);
1093 ret = core->dcvs_core_id;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001094 return ret;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001095bail:
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001096 core->dcvs_core_id = -1;
1097 return -EINVAL;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001098}
1099EXPORT_SYMBOL(msm_dcvs_register_core);
1100
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001101void msm_dcvs_update_limits(int dcvs_core_id)
Abhijeet Dharmapurikarc43f0db2012-08-31 20:42:53 -07001102{
1103 struct dcvs_core *core;
1104
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001105 if (dcvs_core_id < CPU_OFFSET || dcvs_core_id > CORES_MAX) {
1106 __err("%s invalid dcvs_core_id = %d returning -EINVAL\n",
1107 __func__, dcvs_core_id);
Abhijeet Dharmapurikarc43f0db2012-08-31 20:42:53 -07001108 return;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001109 }
Abhijeet Dharmapurikarc43f0db2012-08-31 20:42:53 -07001110
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001111 core = msm_dcvs_get_core(dcvs_core_id);
1112 core->actual_freq = core->get_frequency(core->type_core_num);
Abhijeet Dharmapurikarc43f0db2012-08-31 20:42:53 -07001113}
1114
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001115int msm_dcvs_freq_sink_start(int dcvs_core_id)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001116{
1117 int ret = -EINVAL;
1118 struct dcvs_core *core = NULL;
Eugene Seah76af9832012-03-28 18:43:53 -06001119 uint32_t ret1;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001120 unsigned long flags;
Abhijeet Dharmapurikar8d843b32012-09-13 18:33:20 -07001121 int new_freq;
1122 int timer_interval_us;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001123
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001124 if (dcvs_core_id < CPU_OFFSET || dcvs_core_id > CORES_MAX) {
1125 __err("%s invalid dcvs_core_id = %d returning -EINVAL\n",
1126 __func__, dcvs_core_id);
1127 return -EINVAL;
1128 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001129
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001130 core = msm_dcvs_get_core(dcvs_core_id);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001131 if (!core)
1132 return ret;
1133
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001134 core->actual_freq = core->get_frequency(core->type_core_num);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001135
1136 spin_lock_irqsave(&core->pending_freq_lock, flags);
1137 /* mark that we are ready to accept new frequencies */
1138 request_freq_change(core, NO_OUTSTANDING_FREQ_CHANGE);
1139 spin_unlock_irqrestore(&core->pending_freq_lock, flags);
1140
1141 spin_lock_irqsave(&core->idle_state_change_lock, flags);
1142 core->idle_entered = -1;
1143 spin_unlock_irqrestore(&core->idle_state_change_lock, flags);
1144
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001145 /* Notify TZ to start receiving idle info for the core */
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001146 ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_DCVS_ENABLE, 1, &ret1);
1147
Abhijeet Dharmapurikar8d843b32012-09-13 18:33:20 -07001148 ret = msm_dcvs_scm_event(
1149 core->dcvs_core_id, MSM_DCVS_SCM_CORE_ONLINE, core->actual_freq,
1150 0, &new_freq, &timer_interval_us);
1151 if (ret)
1152 __err("Error (%d) DCVS sending online for %s\n",
1153 ret, core->core_name);
1154
1155 if (new_freq != 0) {
1156 spin_lock_irqsave(&core->pending_freq_lock, flags);
1157 request_freq_change(core, new_freq);
1158 spin_unlock_irqrestore(&core->pending_freq_lock, flags);
1159 }
1160 force_start_slack_timer(core, timer_interval_us);
1161
Steve Mucklec1785c32012-11-13 14:27:43 -08001162 core->flags |= CORE_FLAG_TEMP_UPDATE;
1163 INIT_DELAYED_WORK(&core->temperature_work, msm_dcvs_report_temp_work);
1164 schedule_delayed_work(&core->temperature_work,
1165 msecs_to_jiffies(core->info->thermal_poll_ms));
Abhijeet Dharmapurikar8d843b32012-09-13 18:33:20 -07001166
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001167 core->idle_enable(core->type_core_num, MSM_DCVS_ENABLE_IDLE_PULSE);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001168 return 0;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001169}
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -07001170EXPORT_SYMBOL(msm_dcvs_freq_sink_start);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001171
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001172int msm_dcvs_freq_sink_stop(int dcvs_core_id)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001173{
1174 int ret = -EINVAL;
1175 struct dcvs_core *core = NULL;
Eugene Seah76af9832012-03-28 18:43:53 -06001176 uint32_t ret1;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001177 uint32_t freq;
1178 unsigned long flags;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001179
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001180 if (dcvs_core_id < 0 || dcvs_core_id > CORES_MAX) {
1181 pr_err("%s invalid dcvs_core_id = %d returning -EINVAL\n",
1182 __func__, dcvs_core_id);
1183 return -EINVAL;
1184 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001185
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001186 core = msm_dcvs_get_core(dcvs_core_id);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001187 if (!core) {
1188 __err("couldn't find core for coreid = %d\n", dcvs_core_id);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001189 return ret;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001190 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001191
Steve Mucklec1785c32012-11-13 14:27:43 -08001192 core->flags &= ~CORE_FLAG_TEMP_UPDATE;
1193 cancel_delayed_work(&core->temperature_work);
1194
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001195 core->idle_enable(core->type_core_num, MSM_DCVS_DISABLE_IDLE_PULSE);
1196 /* Notify TZ to stop receiving idle info for the core */
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001197 ret = msm_dcvs_scm_event(core->dcvs_core_id, MSM_DCVS_SCM_DCVS_ENABLE,
1198 0, core->actual_freq, &freq, &ret1);
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001199 core->idle_enable(core->type_core_num,
1200 MSM_DCVS_ENABLE_HIGH_LATENCY_MODES);
Steve Muckle43a980c2012-11-19 16:38:55 -08001201
1202 if (core->type == MSM_DCVS_CORE_TYPE_GPU)
1203 mutex_lock(&gpu_floor_mutex);
1204
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001205 spin_lock_irqsave(&core->pending_freq_lock, flags);
1206 /* flush out all the pending freq changes */
1207 request_freq_change(core, STOP_FREQ_CHANGE);
1208 spin_unlock_irqrestore(&core->pending_freq_lock, flags);
Steve Muckle43a980c2012-11-19 16:38:55 -08001209
1210 if (core->type == MSM_DCVS_CORE_TYPE_GPU)
1211 mutex_unlock(&gpu_floor_mutex);
1212
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001213 force_stop_slack_timer(core);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001214
1215 return 0;
1216}
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -07001217EXPORT_SYMBOL(msm_dcvs_freq_sink_stop);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001218
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001219int msm_dcvs_idle(int dcvs_core_id, enum msm_core_idle_state state,
1220 uint32_t iowaited)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001221{
1222 int ret = 0;
1223 struct dcvs_core *core = NULL;
Eugene Seah76af9832012-03-28 18:43:53 -06001224 uint32_t timer_interval_us = 0;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001225 uint32_t r0, r1;
1226
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001227 if (dcvs_core_id < CPU_OFFSET || dcvs_core_id > CORES_MAX) {
1228 pr_err("invalid dcvs_core_id = %d ret -EINVAL\n", dcvs_core_id);
1229 return -EINVAL;
1230 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001231
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001232 core = msm_dcvs_get_core(dcvs_core_id);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001233
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001234 switch (state) {
1235 case MSM_DCVS_IDLE_ENTER:
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001236 stop_slack_timer(core);
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001237 ret = msm_dcvs_scm_event(core->dcvs_core_id,
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001238 MSM_DCVS_SCM_IDLE_ENTER, 0, 0, &r0, &r1);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001239 if (ret < 0 && ret != -13)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001240 __err("Error (%d) sending idle enter for %s\n",
1241 ret, core->core_name);
Abhijeet Dharmapurikar07cf2ff2012-09-13 19:05:13 -07001242 trace_msm_dcvs_idle("idle_enter_exit", core->core_name, 1);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001243 break;
1244
1245 case MSM_DCVS_IDLE_EXIT:
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001246 ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_IDLE_EXIT,
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001247 iowaited, &timer_interval_us);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001248 if (ret)
1249 __err("Error (%d) sending idle exit for %s\n",
1250 ret, core->core_name);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001251 start_slack_timer(core, timer_interval_us);
Abhijeet Dharmapurikar07cf2ff2012-09-13 19:05:13 -07001252 trace_msm_dcvs_idle("idle_enter_exit", core->core_name, 0);
1253 trace_msm_dcvs_iowait("iowait", core->core_name, iowaited);
1254 trace_msm_dcvs_slack_time("slack_timer_dcvs", core->core_name,
1255 timer_interval_us);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001256 break;
1257 }
1258
1259 return ret;
1260}
1261EXPORT_SYMBOL(msm_dcvs_idle);
1262
1263static int __init msm_dcvs_late_init(void)
1264{
1265 struct kobject *module_kobj = NULL;
1266 int ret = 0;
1267
1268 module_kobj = kset_find_obj(module_kset, KBUILD_MODNAME);
1269 if (!module_kobj) {
1270 pr_err("%s: cannot find kobject for module %s\n",
1271 __func__, KBUILD_MODNAME);
1272 ret = -ENOENT;
1273 goto err;
1274 }
1275
1276 cores_kobj = kobject_create_and_add("cores", module_kobj);
1277 if (!cores_kobj) {
1278 __err("Cannot create %s kobject\n", "cores");
1279 ret = -ENOMEM;
1280 goto err;
1281 }
1282
1283 debugfs_base = debugfs_create_dir("msm_dcvs", NULL);
1284 if (!debugfs_base) {
1285 __err("Cannot create debugfs base %s\n", "msm_dcvs");
1286 ret = -ENOENT;
1287 goto err;
1288 }
1289
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001290err:
1291 if (ret) {
1292 kobject_del(cores_kobj);
1293 cores_kobj = NULL;
1294 debugfs_remove(debugfs_base);
1295 }
1296
1297 return ret;
1298}
1299late_initcall(msm_dcvs_late_init);
1300
Steve Mucklea9aac292012-11-02 15:41:00 -07001301static int __devinit dcvs_probe(struct platform_device *pdev)
1302{
1303 if (pdev->dev.platform_data)
1304 dcvs_pdata = pdev->dev.platform_data;
1305
1306 return 0;
1307}
1308
1309static struct platform_driver dcvs_driver = {
1310 .probe = dcvs_probe,
1311 .driver = {
1312 .name = "dcvs",
1313 .owner = THIS_MODULE,
1314 },
1315};
1316
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001317static int __init msm_dcvs_early_init(void)
1318{
1319 int ret = 0;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001320 int i;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001321
Steve Mucklea9aac292012-11-02 15:41:00 -07001322 platform_driver_register(&dcvs_driver);
1323
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001324 if (!msm_dcvs_enabled) {
1325 __info("Not enabled (%d)\n", msm_dcvs_enabled);
1326 return 0;
1327 }
1328
Abhijeet Dharmapurikar3edb5de2012-09-13 11:02:03 -07001329
1330 /* Only need about 32kBytes for normal operation */
1331 ret = msm_dcvs_scm_init(SZ_32K);
1332 if (ret) {
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001333 __err("Unable to initialize DCVS err=%d\n", ret);
Abhijeet Dharmapurikar3edb5de2012-09-13 11:02:03 -07001334 goto done;
1335 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001336
Steve Muckle43a980c2012-11-19 16:38:55 -08001337 for (i = 0; i < CORES_MAX; i++) {
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001338 core_list[i].dcvs_core_id = -1;
Steve Muckle43a980c2012-11-19 16:38:55 -08001339 core_list[i].pending_freq = STOP_FREQ_CHANGE;
1340 }
Abhijeet Dharmapurikar3edb5de2012-09-13 11:02:03 -07001341done:
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001342 return ret;
1343}
1344postcore_initcall(msm_dcvs_early_init);