blob: 41afd242fc04cd6d61bc47349cfe420efc4dc619 [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;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070052 struct kobj_attribute ss_iobusy_conv;
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;
68
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070069 struct attribute_group attrib_group;
70};
71
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -070072enum pending_freq_state {
73 /*
74 * used by the thread to check if pending_freq was updated while it was
75 * setting previous frequency - this is written to and used by the
76 * freq updating thread
77 */
78 NO_OUTSTANDING_FREQ_CHANGE = 0,
79
80 /*
81 * This request is set to indicate that the governor is stopped and no
82 * more frequency change requests are accepted untill it starts again.
83 * This is checked/used by the threads that want to change the freq
84 */
85 STOP_FREQ_CHANGE = -1,
86
87 /*
88 * Any other +ve value means that a freq change was requested and the
89 * thread has not gotten around to update it
90 *
91 * Any other -ve value means that this is the last freq change i.e. a
92 * freq change was requested but the thread has not run yet and
93 * meanwhile the governor was stopped.
94 */
95};
96
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070097struct dcvs_core {
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -070098 spinlock_t idle_state_change_lock;
99 /* 0 when not idle (busy) 1 when idle and -1 when governor starts and
100 * we dont know whether the next call is going to be idle enter or exit
101 */
102 int idle_entered;
103
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700104 enum msm_dcvs_core_type type;
105 /* this is the number in each type for example cpu 0,1,2 and gpu 0,1 */
106 int type_core_num;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700107 char core_name[CORE_NAME_MAX];
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700108 uint32_t actual_freq;
109 uint32_t freq_change_us;
110
111 uint32_t max_time_us; /* core param */
112
113 struct msm_dcvs_algo_param algo_param;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700114 struct msm_dcvs_energy_curve_coeffs coeffs;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700115
116 /* private */
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700117 ktime_t time_start;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700118 struct task_struct *task;
119 struct core_attribs attrib;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700120 uint32_t dcvs_core_id;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700121 struct msm_dcvs_core_info *info;
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700122 int sensor;
Abhijeet Dharmapurikarbbb52fe2012-08-31 20:31:16 -0700123 wait_queue_head_t wait_q;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700124
125 int (*set_frequency)(int type_core_num, unsigned int freq);
126 unsigned int (*get_frequency)(int type_core_num);
127 int (*idle_enable)(int type_core_num,
Abhijeet Dharmapurikar6e9b34f2012-09-10 16:03:39 -0700128 enum msm_core_control_event event);
Steve Muckle93bb4252012-11-12 14:20:39 -0800129 int (*set_floor_frequency)(int type_core_num, unsigned int freq);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700130
131 spinlock_t pending_freq_lock;
132 int pending_freq;
133
134 struct hrtimer slack_timer;
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700135 struct delayed_work temperature_work;
Steve Mucklec1785c32012-11-13 14:27:43 -0800136 int flags;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700137};
138
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700139static int msm_dcvs_enabled = 1;
140module_param_named(enable, msm_dcvs_enabled, int, S_IRUGO | S_IWUSR | S_IWGRP);
141
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700142static struct dentry *debugfs_base;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700143
144static struct dcvs_core core_list[CORES_MAX];
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700145
146static struct kobject *cores_kobj;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700147
Steve Mucklea9aac292012-11-02 15:41:00 -0700148#define DCVS_MAX_NUM_FREQS 15
149static struct msm_dcvs_freq_entry cpu_freq_tbl[DCVS_MAX_NUM_FREQS];
150static unsigned num_cpu_freqs;
151static struct msm_dcvs_platform_data *dcvs_pdata;
152
Steve Muckle43a980c2012-11-19 16:38:55 -0800153static DEFINE_MUTEX(gpu_floor_mutex);
154
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700155static void force_stop_slack_timer(struct dcvs_core *core)
156{
157 unsigned long flags;
158
159 spin_lock_irqsave(&core->idle_state_change_lock, flags);
160 hrtimer_cancel(&core->slack_timer);
161 spin_unlock_irqrestore(&core->idle_state_change_lock, flags);
162}
163
Abhijeet Dharmapurikar8d843b32012-09-13 18:33:20 -0700164static void force_start_slack_timer(struct dcvs_core *core, int slack_us)
165{
166 unsigned long flags;
167 int ret;
168
169 spin_lock_irqsave(&core->idle_state_change_lock, flags);
170
171 /*
172 * only start the timer if governor is not stopped
173 */
174 if (slack_us != 0) {
175 ret = hrtimer_start(&core->slack_timer,
176 ktime_set(0, slack_us * 1000),
177 HRTIMER_MODE_REL_PINNED);
178 if (ret) {
179 pr_err("%s Failed to start timer ret = %d\n",
180 core->core_name, ret);
181 }
182 }
183
184 spin_unlock_irqrestore(&core->idle_state_change_lock, flags);
185}
186
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700187static void stop_slack_timer(struct dcvs_core *core)
188{
189 unsigned long flags;
190
191 spin_lock_irqsave(&core->idle_state_change_lock, flags);
192 /* err only for cpu type's GPU's can do idle exit consecutively */
193 if (core->idle_entered == 1 && !(core->dcvs_core_id >= GPU_OFFSET))
194 __err("%s trying to reenter idle", core->core_name);
195 core->idle_entered = 1;
196 hrtimer_cancel(&core->slack_timer);
197 core->idle_entered = 1;
198 spin_unlock_irqrestore(&core->idle_state_change_lock, flags);
199}
200
201static void start_slack_timer(struct dcvs_core *core, int slack_us)
202{
203 unsigned long flags1, flags2;
204 int ret;
205
206 spin_lock_irqsave(&core->idle_state_change_lock, flags2);
207
208 spin_lock_irqsave(&core->pending_freq_lock, flags1);
209
210 /* err only for cpu type's GPU's can do idle enter consecutively */
211 if (core->idle_entered == 0 && !(core->dcvs_core_id >= GPU_OFFSET))
212 __err("%s trying to reexit idle", core->core_name);
213 core->idle_entered = 0;
214 /*
215 * only start the timer if governor is not stopped
216 */
217 if (slack_us != 0
218 && !(core->pending_freq < NO_OUTSTANDING_FREQ_CHANGE)) {
219 ret = hrtimer_start(&core->slack_timer,
220 ktime_set(0, slack_us * 1000),
221 HRTIMER_MODE_REL_PINNED);
222 if (ret) {
223 pr_err("%s Failed to start timer ret = %d\n",
224 core->core_name, ret);
225 }
226 }
227 spin_unlock_irqrestore(&core->pending_freq_lock, flags1);
228
229 spin_unlock_irqrestore(&core->idle_state_change_lock, flags2);
230}
231
232static void restart_slack_timer(struct dcvs_core *core, int slack_us)
233{
234 unsigned long flags1, flags2;
235 int ret;
236
237 spin_lock_irqsave(&core->idle_state_change_lock, flags2);
238
239 hrtimer_cancel(&core->slack_timer);
240
241 spin_lock_irqsave(&core->pending_freq_lock, flags1);
242
243 /*
244 * only start the timer if idle is not entered
245 * and governor is not stopped
246 */
247 if (slack_us != 0 && (core->idle_entered != 1)
248 && !(core->pending_freq < NO_OUTSTANDING_FREQ_CHANGE)) {
249 ret = hrtimer_start(&core->slack_timer,
250 ktime_set(0, slack_us * 1000),
251 HRTIMER_MODE_REL_PINNED);
252 if (ret) {
253 pr_err("%s Failed to start timer ret = %d\n",
254 core->core_name, ret);
255 }
256 }
257 spin_unlock_irqrestore(&core->pending_freq_lock, flags1);
258 spin_unlock_irqrestore(&core->idle_state_change_lock, flags2);
259}
260
Steve Muckle28ddcdd2012-11-21 10:12:39 -0800261void msm_dcvs_apply_gpu_floor(unsigned long cpu_freq)
Steve Muckle93bb4252012-11-12 14:20:39 -0800262{
Steve Muckle28ddcdd2012-11-21 10:12:39 -0800263 static unsigned long curr_cpu0_freq;
264 unsigned long gpu_floor_freq = 0;
Steve Muckle93bb4252012-11-12 14:20:39 -0800265 struct dcvs_core *gpu;
Steve Muckle28ddcdd2012-11-21 10:12:39 -0800266 int i;
Steve Muckle93bb4252012-11-12 14:20:39 -0800267
268 if (!dcvs_pdata)
269 return;
270
Steve Muckle28ddcdd2012-11-21 10:12:39 -0800271 mutex_lock(&gpu_floor_mutex);
272
273 if (cpu_freq)
274 curr_cpu0_freq = cpu_freq;
275
Steve Muckle93bb4252012-11-12 14:20:39 -0800276 for (i = 0; i < dcvs_pdata->num_sync_rules; i++)
Steve Muckle28ddcdd2012-11-21 10:12:39 -0800277 if (curr_cpu0_freq > dcvs_pdata->sync_rules[i].cpu_khz) {
Steve Muckle93bb4252012-11-12 14:20:39 -0800278 gpu_floor_freq =
279 dcvs_pdata->sync_rules[i].gpu_floor_khz;
280 break;
281 }
282
Steve Muckle28ddcdd2012-11-21 10:12:39 -0800283 if (num_online_cpus() > 1)
284 gpu_floor_freq = max(gpu_floor_freq,
285 dcvs_pdata->gpu_max_nom_khz);
286
287 if (!gpu_floor_freq) {
288 mutex_unlock(&gpu_floor_mutex);
Steve Muckle93bb4252012-11-12 14:20:39 -0800289 return;
Steve Muckle28ddcdd2012-11-21 10:12:39 -0800290 }
Steve Muckle93bb4252012-11-12 14:20:39 -0800291
292 for (i = GPU_OFFSET; i < CORES_MAX; i++) {
293 gpu = &core_list[i];
294 if (gpu->dcvs_core_id == -1)
295 continue;
Steve Muckle28ddcdd2012-11-21 10:12:39 -0800296
Steve Muckle43a980c2012-11-19 16:38:55 -0800297 if (gpu->pending_freq != STOP_FREQ_CHANGE &&
298 gpu->set_floor_frequency)
Steve Muckle93bb4252012-11-12 14:20:39 -0800299 gpu->set_floor_frequency(gpu->type_core_num,
300 gpu_floor_freq);
301 }
Steve Muckle28ddcdd2012-11-21 10:12:39 -0800302
303 mutex_unlock(&gpu_floor_mutex);
Steve Muckle93bb4252012-11-12 14:20:39 -0800304}
305
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700306static int __msm_dcvs_change_freq(struct dcvs_core *core)
307{
308 int ret = 0;
309 unsigned long flags = 0;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700310 int requested_freq = 0;
311 ktime_t time_start;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700312 uint32_t slack_us = 0;
313 uint32_t ret1 = 0;
314
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700315 spin_lock_irqsave(&core->pending_freq_lock, flags);
Steve Muckle80e0b7b2012-11-19 15:46:39 -0800316 if (core->pending_freq == STOP_FREQ_CHANGE)
317 goto out;
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700318repeat:
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700319 BUG_ON(!core->pending_freq);
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700320
321 requested_freq = core->pending_freq;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700322 time_start = core->time_start;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700323 core->time_start = ns_to_ktime(0);
324
Steve Muckle80e0b7b2012-11-19 15:46:39 -0800325 core->pending_freq = NO_OUTSTANDING_FREQ_CHANGE;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700326
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700327 if (requested_freq == core->actual_freq)
328 goto out;
329
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700330 spin_unlock_irqrestore(&core->pending_freq_lock, flags);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700331
Steve Muckle93bb4252012-11-12 14:20:39 -0800332 if (core->type == MSM_DCVS_CORE_TYPE_CPU &&
333 core->type_core_num == 0)
Steve Muckle28ddcdd2012-11-21 10:12:39 -0800334 msm_dcvs_apply_gpu_floor(requested_freq);
Steve Muckle93bb4252012-11-12 14:20:39 -0800335
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700336 /**
337 * Call the frequency sink driver to change the frequency
338 * We will need to get back the actual frequency in KHz and
339 * the record the time taken to change it.
340 */
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700341 ret = core->set_frequency(core->type_core_num, requested_freq);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700342 if (ret <= 0)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700343 __err("Core %s failed to set freq %u\n",
344 core->core_name, requested_freq);
Eugene Seah76af9832012-03-28 18:43:53 -0600345 /* continue to call TZ to get updated slack timer */
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700346 else
Eugene Seah76af9832012-03-28 18:43:53 -0600347 core->actual_freq = ret;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700348
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700349 core->freq_change_us = (uint32_t)ktime_to_us(
350 ktime_sub(ktime_get(), time_start));
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700351
352 /**
353 * Disable low power modes if the actual frequency is >
354 * disable_pc_threshold.
355 */
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700356 if (core->actual_freq > core->algo_param.disable_pc_threshold) {
357 core->idle_enable(core->type_core_num,
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700358 MSM_DCVS_DISABLE_HIGH_LATENCY_MODES);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700359 } else if (core->actual_freq <= core->algo_param.disable_pc_threshold) {
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700360 core->idle_enable(core->type_core_num,
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700361 MSM_DCVS_ENABLE_HIGH_LATENCY_MODES);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700362 }
363
364 /**
365 * Update algorithm with new freq and time taken to change
366 * to this frequency and that will get us the new slack
367 * timer
368 */
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700369 ret = msm_dcvs_scm_event(core->dcvs_core_id,
370 MSM_DCVS_SCM_CLOCK_FREQ_UPDATE,
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700371 core->actual_freq, core->freq_change_us,
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700372 &slack_us, &ret1);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700373 if (ret) {
374 __err("Error sending core (%s) dcvs_core_id = %d freq change (%u) reqfreq = %d slack_us=%d ret = %d\n",
375 core->core_name, core->dcvs_core_id,
376 core->actual_freq, requested_freq,
377 slack_us, ret);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700378 }
379
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700380 /* TODO confirm that we get a valid freq from SM even when the above
381 * FREQ_UPDATE fails
382 */
383 restart_slack_timer(core, slack_us);
384 spin_lock_irqsave(&core->pending_freq_lock, flags);
385
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700386 /**
387 * By the time we are done with freq changes, we could be asked to
388 * change again. Check before exiting.
389 */
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700390 if (core->pending_freq != NO_OUTSTANDING_FREQ_CHANGE
391 && core->pending_freq != STOP_FREQ_CHANGE) {
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700392 goto repeat;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700393 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700394
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700395out: /* should always be jumped to with the spin_lock held */
396 spin_unlock_irqrestore(&core->pending_freq_lock, flags);
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700397
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700398 return ret;
399}
400
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700401static void msm_dcvs_report_temp_work(struct work_struct *work)
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700402{
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700403 struct dcvs_core *core = container_of(work,
404 struct dcvs_core,
405 temperature_work.work);
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700406 struct msm_dcvs_core_info *info = core->info;
407 struct tsens_device tsens_dev;
408 int ret;
409 unsigned long temp = 0;
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700410 int interval_ms;
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700411
Steve Mucklec1785c32012-11-13 14:27:43 -0800412 if (!(core->flags & CORE_FLAG_TEMP_UPDATE))
413 return;
414
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700415 tsens_dev.sensor_num = core->sensor;
416 ret = tsens_get_temp(&tsens_dev, &temp);
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700417 if (!temp) {
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700418 tsens_dev.sensor_num = 0;
419 ret = tsens_get_temp(&tsens_dev, &temp);
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700420 if (!temp)
421 goto out;
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700422 }
423
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700424 if (temp == info->power_param.current_temp)
425 goto out;
426 info->power_param.current_temp = temp;
427
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700428 ret = msm_dcvs_scm_set_power_params(core->dcvs_core_id,
429 &info->power_param,
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700430 &info->freq_tbl[0], &core->coeffs);
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700431out:
432 if (info->thermal_poll_ms == 0)
433 interval_ms = 60000;
434 else if (info->thermal_poll_ms < 1000)
435 interval_ms = 1000;
436 else
437 interval_ms = info->thermal_poll_ms;
438
439 schedule_delayed_work(&core->temperature_work,
440 msecs_to_jiffies(interval_ms));
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700441}
442
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700443static int msm_dcvs_do_freq(void *data)
444{
445 struct dcvs_core *core = (struct dcvs_core *)data;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700446
447 while (!kthread_should_stop()) {
Abhijeet Dharmapurikarbbb52fe2012-08-31 20:31:16 -0700448 wait_event(core->wait_q, !(core->pending_freq == 0 ||
449 core->pending_freq == -1) ||
450 kthread_should_stop());
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700451
452 if (kthread_should_stop())
453 break;
454
Abhijeet Dharmapurikarbbb52fe2012-08-31 20:31:16 -0700455 __msm_dcvs_change_freq(core);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700456 }
457
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700458 return 0;
459}
460
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700461/* freq_pending_lock should be held */
462static void request_freq_change(struct dcvs_core *core, int new_freq)
463{
464 if (new_freq == NO_OUTSTANDING_FREQ_CHANGE) {
465 if (core->pending_freq != STOP_FREQ_CHANGE) {
466 __err("%s gov started with earlier pending freq %d\n",
467 core->core_name, core->pending_freq);
468 }
469 core->pending_freq = NO_OUTSTANDING_FREQ_CHANGE;
470 return;
471 }
472
473 if (new_freq == STOP_FREQ_CHANGE) {
Steve Muckle80e0b7b2012-11-19 15:46:39 -0800474 core->pending_freq = STOP_FREQ_CHANGE;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700475 return;
476 }
477
478 if (core->pending_freq < 0) {
479 /* a value less than 0 means that the governor has stopped
480 * and no more freq changes should be requested
481 */
482 return;
483 }
484
485 if (core->actual_freq != new_freq && core->pending_freq != new_freq) {
486 core->pending_freq = new_freq;
487 core->time_start = ktime_get();
488 wake_up(&core->wait_q);
489 }
490}
491
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700492static int msm_dcvs_update_freq(struct dcvs_core *core,
Eugene Seah76af9832012-03-28 18:43:53 -0600493 enum msm_dcvs_scm_event event, uint32_t param0,
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700494 uint32_t *ret1)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700495{
496 int ret = 0;
497 unsigned long flags = 0;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700498 uint32_t new_freq = -EINVAL;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700499
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700500 spin_lock_irqsave(&core->pending_freq_lock, flags);
501
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700502 ret = msm_dcvs_scm_event(core->dcvs_core_id, event, param0,
Eugene Seah76af9832012-03-28 18:43:53 -0600503 core->actual_freq, &new_freq, ret1);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700504 if (ret) {
Jeff Ohlstein4aa4a2b2012-06-28 19:03:49 -0700505 if (ret == -13)
506 ret = 0;
507 else
508 __err("Error (%d) sending SCM event %d for core %s\n",
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700509 ret, event, core->core_name);
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700510 goto out;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700511 }
512
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700513 if (new_freq == 0) {
514 /*
515 * sometimes TZ gives us a 0 freq back,
516 * do not queue up a request
517 */
518 goto out;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700519 }
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700520
521 request_freq_change(core, new_freq);
522
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700523out:
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700524 spin_unlock_irqrestore(&core->pending_freq_lock, flags);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700525
526 return ret;
527}
528
529static enum hrtimer_restart msm_dcvs_core_slack_timer(struct hrtimer *timer)
530{
531 int ret = 0;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700532 struct dcvs_core *core = container_of(timer,
533 struct dcvs_core, slack_timer);
Eugene Seah76af9832012-03-28 18:43:53 -0600534 uint32_t ret1;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700535
Abhijeet Dharmapurikar07cf2ff2012-09-13 19:05:13 -0700536 trace_printk("dcvs: Slack timer fired for core=%s\n", core->core_name);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700537 /**
538 * Timer expired, notify TZ
539 * Dont care about the third arg.
540 */
Eugene Seah76af9832012-03-28 18:43:53 -0600541 ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_QOS_TIMER_EXPIRED, 0,
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700542 &ret1);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700543 if (ret)
544 __err("Timer expired for core %s but failed to notify.\n",
545 core->core_name);
546
547 return HRTIMER_NORESTART;
548}
549
Steve Muckle388cc2e2012-11-21 15:47:15 -0800550int msm_dcvs_update_algo_params(void)
551{
552 static struct msm_dcvs_algo_param curr_params;
553 static DEFINE_MUTEX(param_update_mutex);
554 struct msm_dcvs_algo_param *new_params;
555 int cpu, ret = 0;
556
557 mutex_lock(&param_update_mutex);
558 new_params = &core_list[CPU_OFFSET + num_online_cpus() - 1].algo_param;
559
560 if (memcmp(&curr_params, new_params,
561 sizeof(struct msm_dcvs_algo_param))) {
562 for_each_possible_cpu(cpu) {
563 ret = msm_dcvs_scm_set_algo_params(CPU_OFFSET + cpu,
564 new_params);
565 if (ret) {
566 pr_err("scm set algo params failed on cpu %d, ret %d\n",
567 cpu, ret);
568 mutex_unlock(&param_update_mutex);
569 return ret;
570 }
571 }
572 memcpy(&curr_params, new_params,
573 sizeof(struct msm_dcvs_algo_param));
574 }
575
576 mutex_unlock(&param_update_mutex);
577 return ret;
578}
579
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700580/* Helper functions and macros for sysfs nodes for a core */
581#define CORE_FROM_ATTRIBS(attr, name) \
582 container_of(container_of(attr, struct core_attribs, name), \
583 struct dcvs_core, attrib);
584
585#define DCVS_PARAM_SHOW(_name, v) \
586static ssize_t msm_dcvs_attr_##_name##_show(struct kobject *kobj, \
587 struct kobj_attribute *attr, char *buf) \
588{ \
589 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
590 return snprintf(buf, PAGE_SIZE, "%d\n", v); \
591}
592
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700593#define DCVS_PARAM_STORE(_name) \
594static ssize_t msm_dcvs_attr_##_name##_show(struct kobject *kobj,\
595 struct kobj_attribute *attr, char *buf) \
596{ \
597 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
598 return snprintf(buf, PAGE_SIZE, "%d\n", core->info->_name); \
599} \
600static ssize_t msm_dcvs_attr_##_name##_store(struct kobject *kobj, \
601 struct kobj_attribute *attr, const char *buf, size_t count) \
602{ \
603 int ret = 0; \
604 uint32_t val = 0; \
605 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
606 ret = kstrtouint(buf, 10, &val); \
607 if (ret) { \
608 __err("Invalid input %s for %s\n", buf, __stringify(_name));\
609 } else { \
610 core->info->_name = val; \
611 } \
612 return count; \
613}
614
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700615#define DCVS_ALGO_PARAM(_name) \
616static ssize_t msm_dcvs_attr_##_name##_show(struct kobject *kobj,\
617 struct kobj_attribute *attr, char *buf) \
618{ \
619 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
620 return snprintf(buf, PAGE_SIZE, "%d\n", core->algo_param._name); \
621} \
622static ssize_t msm_dcvs_attr_##_name##_store(struct kobject *kobj, \
623 struct kobj_attribute *attr, const char *buf, size_t count) \
624{ \
625 int ret = 0; \
626 uint32_t val = 0; \
627 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700628 ret = kstrtouint(buf, 10, &val); \
629 if (ret) { \
630 __err("Invalid input %s for %s\n", buf, __stringify(_name));\
631 } else { \
632 uint32_t old_val = core->algo_param._name; \
633 core->algo_param._name = val; \
Steve Muckle388cc2e2012-11-21 15:47:15 -0800634 ret = msm_dcvs_update_algo_params(); \
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700635 if (ret) { \
636 core->algo_param._name = old_val; \
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700637 } \
638 } \
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700639 return count; \
640}
641
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700642#define DCVS_ENERGY_PARAM(_name) \
643static ssize_t msm_dcvs_attr_##_name##_show(struct kobject *kobj,\
644 struct kobj_attribute *attr, char *buf) \
645{ \
646 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
647 return snprintf(buf, PAGE_SIZE, "%d\n", core->coeffs._name); \
648} \
649static ssize_t msm_dcvs_attr_##_name##_store(struct kobject *kobj, \
650 struct kobj_attribute *attr, const char *buf, size_t count) \
651{ \
652 int ret = 0; \
653 int32_t val = 0; \
654 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700655 ret = kstrtoint(buf, 10, &val); \
656 if (ret) { \
657 __err("Invalid input %s for %s\n", buf, __stringify(_name));\
658 } else { \
659 int32_t old_val = core->coeffs._name; \
660 core->coeffs._name = val; \
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700661 ret = msm_dcvs_scm_set_power_params(core->dcvs_core_id, \
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700662 &core->info->power_param, &core->info->freq_tbl[0], \
663 &core->coeffs); \
664 if (ret) { \
665 core->coeffs._name = old_val; \
666 __err("Error(%d) in setting %d for coeffs param %s\n",\
667 ret, val, __stringify(_name)); \
668 } \
669 } \
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700670 return count; \
671}
672
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700673#define DCVS_RO_ATTRIB(i, _name) \
674 core->attrib._name.attr.name = __stringify(_name); \
675 core->attrib._name.attr.mode = S_IRUGO; \
676 core->attrib._name.show = msm_dcvs_attr_##_name##_show; \
677 core->attrib._name.store = NULL; \
678 core->attrib.attrib_group.attrs[i] = &core->attrib._name.attr;
679
680#define DCVS_RW_ATTRIB(i, _name) \
681 core->attrib._name.attr.name = __stringify(_name); \
682 core->attrib._name.attr.mode = S_IRUGO | S_IWUSR; \
683 core->attrib._name.show = msm_dcvs_attr_##_name##_show; \
684 core->attrib._name.store = msm_dcvs_attr_##_name##_store; \
685 core->attrib.attrib_group.attrs[i] = &core->attrib._name.attr;
686
687/**
688 * Function declarations for different attributes.
689 * Gets used when setting the attribute show and store parameters.
690 */
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700691DCVS_PARAM_SHOW(freq_change_us, (core->freq_change_us))
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700692
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700693DCVS_ALGO_PARAM(disable_pc_threshold)
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700694DCVS_ALGO_PARAM(em_win_size_min_us)
695DCVS_ALGO_PARAM(em_win_size_max_us)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700696DCVS_ALGO_PARAM(em_max_util_pct)
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700697DCVS_ALGO_PARAM(group_id)
698DCVS_ALGO_PARAM(max_freq_chg_time_us)
699DCVS_ALGO_PARAM(slack_mode_dynamic)
700DCVS_ALGO_PARAM(slack_time_min_us)
701DCVS_ALGO_PARAM(slack_time_max_us)
702DCVS_ALGO_PARAM(slack_weight_thresh_pct)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700703DCVS_ALGO_PARAM(ss_iobusy_conv)
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700704DCVS_ALGO_PARAM(ss_win_size_min_us)
705DCVS_ALGO_PARAM(ss_win_size_max_us)
706DCVS_ALGO_PARAM(ss_util_pct)
707
708DCVS_ENERGY_PARAM(active_coeff_a)
709DCVS_ENERGY_PARAM(active_coeff_b)
710DCVS_ENERGY_PARAM(active_coeff_c)
711DCVS_ENERGY_PARAM(leakage_coeff_a)
712DCVS_ENERGY_PARAM(leakage_coeff_b)
713DCVS_ENERGY_PARAM(leakage_coeff_c)
714DCVS_ENERGY_PARAM(leakage_coeff_d)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700715
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700716DCVS_PARAM_STORE(thermal_poll_ms)
717
Steve Muckle118f47b2012-10-17 16:09:37 -0700718static ssize_t msm_dcvs_attr_freq_tbl_show(struct kobject *kobj,
719 struct kobj_attribute *attr,
720 char *buf)
721{
722 struct msm_dcvs_freq_entry *freq_tbl;
723 char *buf_idx = buf;
724 int i, len;
725 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, freq_tbl);
726
727 freq_tbl = core->info->freq_tbl;
728 *buf_idx = '\0';
729
730 /* limit the number of frequencies we will print into
731 * the PAGE_SIZE sysfs show buffer. */
732 if (core->info->power_param.num_freq > 64)
733 return 0;
734
735 for (i = 0; i < core->info->power_param.num_freq; i++) {
736 if (freq_tbl[i].is_trans_level) {
737 len = snprintf(buf_idx, 10, "%7d ", freq_tbl[i].freq);
738 /* buf_idx always points at terminating null */
739 buf_idx += len;
740 }
741 }
742 /* overwrite final trailing space with newline */
743 if (buf_idx > buf)
744 *(buf_idx - 1) = '\n';
745
746 return buf_idx - buf;
747}
748
749static ssize_t msm_dcvs_attr_freq_tbl_store(struct kobject *kobj,
750 struct kobj_attribute *attr,
751 const char *buf,
752 size_t count)
753{
754 struct msm_dcvs_freq_entry *freq_tbl;
755 uint32_t freq;
756 int i, ret;
757 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, freq_tbl);
758
759 freq_tbl = core->info->freq_tbl;
760
761 ret = kstrtouint(buf, 10, &freq);
762 if (ret) {
763 __err("Invalid input %s for freq_tbl\n", buf);
764 return count;
765 }
766
767 for (i = 0; i < core->info->power_param.num_freq; i++)
768 if (freq_tbl[i].freq == freq) {
769 freq_tbl[i].is_trans_level ^= 1;
770 break;
771 }
772
773 if (i >= core->info->power_param.num_freq) {
774 __err("Invalid frequency for freq_tbl: %d\n", freq);
775 return count;
776 }
777
778 ret = msm_dcvs_scm_set_power_params(core->dcvs_core_id,
779 &core->info->power_param,
780 &core->info->freq_tbl[0],
781 &core->coeffs);
782 if (ret) {
783 freq_tbl[i].is_trans_level ^= 1;
784 __err("Error %d in toggling freq %d (orig enable val %d)\n",
785 ret, freq_tbl[i].freq, freq_tbl[i].is_trans_level);
786 }
787 return count;
788}
789
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700790static int msm_dcvs_setup_core_sysfs(struct dcvs_core *core)
791{
792 int ret = 0;
793 struct kobject *core_kobj = NULL;
Steve Muckle118f47b2012-10-17 16:09:37 -0700794 const int attr_count = 25;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700795
796 BUG_ON(!cores_kobj);
797
798 core->attrib.attrib_group.attrs =
799 kzalloc(attr_count * sizeof(struct attribute *), GFP_KERNEL);
800
801 if (!core->attrib.attrib_group.attrs) {
802 ret = -ENOMEM;
803 goto done;
804 }
805
Abhijeet Dharmapurikar35876382012-09-13 16:09:51 -0700806 DCVS_RO_ATTRIB(0, freq_change_us);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700807
Abhijeet Dharmapurikar35876382012-09-13 16:09:51 -0700808 DCVS_RW_ATTRIB(1, disable_pc_threshold);
809 DCVS_RW_ATTRIB(2, em_win_size_min_us);
810 DCVS_RW_ATTRIB(3, em_win_size_max_us);
811 DCVS_RW_ATTRIB(4, em_max_util_pct);
812 DCVS_RW_ATTRIB(5, group_id);
813 DCVS_RW_ATTRIB(6, max_freq_chg_time_us);
814 DCVS_RW_ATTRIB(7, slack_mode_dynamic);
815 DCVS_RW_ATTRIB(8, slack_weight_thresh_pct);
816 DCVS_RW_ATTRIB(9, slack_time_min_us);
817 DCVS_RW_ATTRIB(10, slack_time_max_us);
818 DCVS_RW_ATTRIB(11, ss_iobusy_conv);
819 DCVS_RW_ATTRIB(12, ss_win_size_min_us);
820 DCVS_RW_ATTRIB(13, ss_win_size_max_us);
821 DCVS_RW_ATTRIB(14, ss_util_pct);
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700822
Abhijeet Dharmapurikar35876382012-09-13 16:09:51 -0700823 DCVS_RW_ATTRIB(15, active_coeff_a);
824 DCVS_RW_ATTRIB(16, active_coeff_b);
825 DCVS_RW_ATTRIB(17, active_coeff_c);
826 DCVS_RW_ATTRIB(18, leakage_coeff_a);
827 DCVS_RW_ATTRIB(19, leakage_coeff_b);
828 DCVS_RW_ATTRIB(20, leakage_coeff_c);
829 DCVS_RW_ATTRIB(21, leakage_coeff_d);
830 DCVS_RW_ATTRIB(22, thermal_poll_ms);
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700831
Steve Muckle118f47b2012-10-17 16:09:37 -0700832 DCVS_RW_ATTRIB(23, freq_tbl);
833
834 core->attrib.attrib_group.attrs[24] = NULL;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700835
836 core_kobj = kobject_create_and_add(core->core_name, cores_kobj);
837 if (!core_kobj) {
838 ret = -ENOMEM;
839 goto done;
840 }
841
842 ret = sysfs_create_group(core_kobj, &core->attrib.attrib_group);
843 if (ret)
844 __err("Cannot create core %s attr group\n", core->core_name);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700845
846done:
847 if (ret) {
848 kfree(core->attrib.attrib_group.attrs);
849 kobject_del(core_kobj);
850 }
851
852 return ret;
853}
854
Steve Muckle5e5a60d2012-10-09 13:25:22 -0700855static int get_core_offset(enum msm_dcvs_core_type type, int num)
856{
857 int offset = -EINVAL;
858
859 switch (type) {
860 case MSM_DCVS_CORE_TYPE_CPU:
861 offset = CPU_OFFSET + num;
862 BUG_ON(offset >= GPU_OFFSET);
863 break;
864 case MSM_DCVS_CORE_TYPE_GPU:
865 offset = GPU_OFFSET + num;
866 BUG_ON(offset >= CORES_MAX);
867 break;
868 default:
869 BUG();
870 }
871
872 return offset;
873}
874
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700875/* Return the core and initialize non platform data specific numbers in it */
876static struct dcvs_core *msm_dcvs_add_core(enum msm_dcvs_core_type type,
877 int num)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700878{
879 struct dcvs_core *core = NULL;
880 int i;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700881 char name[CORE_NAME_MAX];
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700882
Steve Muckle5e5a60d2012-10-09 13:25:22 -0700883 i = get_core_offset(type, num);
884 if (i < 0)
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700885 return NULL;
Steve Muckle5e5a60d2012-10-09 13:25:22 -0700886
887 if (type == MSM_DCVS_CORE_TYPE_CPU)
888 snprintf(name, CORE_NAME_MAX, "cpu%d", num);
889 else
890 snprintf(name, CORE_NAME_MAX, "gpu%d", num);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700891
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700892 core = &core_list[i];
893 core->dcvs_core_id = i;
Abhijeet Dharmapurikar2ebc0fe2012-09-12 14:05:13 -0700894 strlcpy(core->core_name, name, CORE_NAME_MAX);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700895 spin_lock_init(&core->pending_freq_lock);
896 spin_lock_init(&core->idle_state_change_lock);
897 hrtimer_init(&core->slack_timer,
898 CLOCK_MONOTONIC, HRTIMER_MODE_REL_PINNED);
899 core->slack_timer.function = msm_dcvs_core_slack_timer;
Abhijeet Dharmapurikar2ebc0fe2012-09-12 14:05:13 -0700900 return core;
901}
902
903/* Return the core if found or add to list if @add_to_list is true */
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700904static struct dcvs_core *msm_dcvs_get_core(int offset)
Abhijeet Dharmapurikar2ebc0fe2012-09-12 14:05:13 -0700905{
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700906 /* if the handle is still not set bug */
907 BUG_ON(core_list[offset].dcvs_core_id == -1);
908 return &core_list[offset];
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700909}
910
Steve Mucklea9aac292012-11-02 15:41:00 -0700911void msm_dcvs_register_cpu_freq(uint32_t freq, uint32_t voltage)
912{
913 BUG_ON(freq == 0 || voltage == 0 ||
914 num_cpu_freqs == DCVS_MAX_NUM_FREQS);
915
916 cpu_freq_tbl[num_cpu_freqs].freq = freq;
917 cpu_freq_tbl[num_cpu_freqs].voltage = voltage;
918
919 num_cpu_freqs++;
920}
921
922static void update_cpu_dcvs_params(struct msm_dcvs_core_info *info)
923{
924 int i;
925
926 BUG_ON(num_cpu_freqs == 0);
927
928 info->freq_tbl = cpu_freq_tbl;
929 info->power_param.num_freq = num_cpu_freqs;
930
931 if (!dcvs_pdata || dcvs_pdata->num_sync_rules == 0)
932 return;
933
934 /* the first sync rule shows what the turbo frequencies are -
935 * these frequencies need energy offsets set */
936 for (i = 0; i < DCVS_MAX_NUM_FREQS && cpu_freq_tbl[i].freq != 0; i++)
937 if (cpu_freq_tbl[i].freq > dcvs_pdata->sync_rules[0].cpu_khz) {
938 cpu_freq_tbl[i].active_energy_offset = 100;
939 cpu_freq_tbl[i].leakage_energy_offset = 100;
940 }
941}
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700942
943int msm_dcvs_register_core(
944 enum msm_dcvs_core_type type,
945 int type_core_num,
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700946 struct msm_dcvs_core_info *info,
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700947 int (*set_frequency)(int type_core_num, unsigned int freq),
948 unsigned int (*get_frequency)(int type_core_num),
949 int (*idle_enable)(int type_core_num,
Abhijeet Dharmapurikar6e9b34f2012-09-10 16:03:39 -0700950 enum msm_core_control_event event),
Steve Muckle93bb4252012-11-12 14:20:39 -0800951 int (*set_floor_frequency)(int type_core_num, unsigned int freq),
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700952 int sensor)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700953{
954 int ret = -EINVAL;
Steve Muckle5e5a60d2012-10-09 13:25:22 -0700955 int offset;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700956 struct dcvs_core *core = NULL;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700957 uint32_t ret1;
958 uint32_t ret2;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700959
Steve Muckle5e5a60d2012-10-09 13:25:22 -0700960 offset = get_core_offset(type, type_core_num);
961 if (offset < 0)
962 return ret;
963 if (core_list[offset].dcvs_core_id != -1)
964 return core_list[offset].dcvs_core_id;
965
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700966 core = msm_dcvs_add_core(type, type_core_num);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700967 if (!core)
968 return ret;
969
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700970 core->type = type;
971 core->type_core_num = type_core_num;
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700972 core->set_frequency = set_frequency;
973 core->get_frequency = get_frequency;
Abhijeet Dharmapurikar6e9b34f2012-09-10 16:03:39 -0700974 core->idle_enable = idle_enable;
Steve Muckle93bb4252012-11-12 14:20:39 -0800975 core->set_floor_frequency = set_floor_frequency;
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700976
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700977 core->info = info;
Steve Mucklea9aac292012-11-02 15:41:00 -0700978 if (type == MSM_DCVS_CORE_TYPE_CPU)
979 update_cpu_dcvs_params(info);
980
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700981 memcpy(&core->algo_param, &info->algo_param,
982 sizeof(struct msm_dcvs_algo_param));
983
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700984 memcpy(&core->coeffs, &info->energy_coeffs,
985 sizeof(struct msm_dcvs_energy_curve_coeffs));
986
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700987 /*
988 * The tz expects cpu0 to represent bit 0 in the mask, however the
989 * dcvs_core_id needs to start from 1, dcvs_core_id = 0 is used to
990 * indicate that this request is not associated with any core.
991 * mpdecision
992 */
993 info->core_param.core_bitmask_id
994 = 1 << (core->dcvs_core_id - CPU_OFFSET);
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700995 core->sensor = sensor;
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700996
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700997 ret = msm_dcvs_scm_register_core(core->dcvs_core_id, &info->core_param);
998 if (ret) {
999 __err("%s: scm register core fail handle = %d ret = %d\n",
1000 __func__, core->dcvs_core_id, ret);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001001 goto bail;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001002 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001003
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001004 ret = msm_dcvs_scm_set_algo_params(core->dcvs_core_id,
1005 &info->algo_param);
1006 if (ret) {
1007 __err("%s: scm algo params failed ret = %d\n", __func__, ret);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001008 goto bail;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001009 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001010
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001011 ret = msm_dcvs_scm_set_power_params(core->dcvs_core_id,
1012 &info->power_param,
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -07001013 &info->freq_tbl[0], &core->coeffs);
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001014 if (ret) {
1015 __err("%s: scm power params failed ret = %d\n", __func__, ret);
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -07001016 goto bail;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001017 }
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -07001018
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001019 ret = msm_dcvs_scm_event(core->dcvs_core_id, MSM_DCVS_SCM_CORE_ONLINE,
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -07001020 core->actual_freq, 0, &ret1, &ret2);
1021 if (ret)
1022 goto bail;
1023
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001024 ret = msm_dcvs_setup_core_sysfs(core);
1025 if (ret) {
1026 __err("Unable to setup core %s sysfs\n", core->core_name);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001027 goto bail;
1028 }
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001029 core->idle_entered = -1;
Abhijeet Dharmapurikarbbb52fe2012-08-31 20:31:16 -07001030 init_waitqueue_head(&core->wait_q);
1031 core->task = kthread_run(msm_dcvs_do_freq, (void *)core,
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001032 "msm_dcvs/%d", core->dcvs_core_id);
1033 ret = core->dcvs_core_id;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001034 return ret;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001035bail:
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001036 core->dcvs_core_id = -1;
1037 return -EINVAL;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001038}
1039EXPORT_SYMBOL(msm_dcvs_register_core);
1040
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001041void msm_dcvs_update_limits(int dcvs_core_id)
Abhijeet Dharmapurikarc43f0db2012-08-31 20:42:53 -07001042{
1043 struct dcvs_core *core;
1044
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001045 if (dcvs_core_id < CPU_OFFSET || dcvs_core_id > CORES_MAX) {
1046 __err("%s invalid dcvs_core_id = %d returning -EINVAL\n",
1047 __func__, dcvs_core_id);
Abhijeet Dharmapurikarc43f0db2012-08-31 20:42:53 -07001048 return;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001049 }
Abhijeet Dharmapurikarc43f0db2012-08-31 20:42:53 -07001050
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001051 core = msm_dcvs_get_core(dcvs_core_id);
1052 core->actual_freq = core->get_frequency(core->type_core_num);
Abhijeet Dharmapurikarc43f0db2012-08-31 20:42:53 -07001053}
1054
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001055int msm_dcvs_freq_sink_start(int dcvs_core_id)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001056{
1057 int ret = -EINVAL;
1058 struct dcvs_core *core = NULL;
Eugene Seah76af9832012-03-28 18:43:53 -06001059 uint32_t ret1;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001060 unsigned long flags;
Abhijeet Dharmapurikar8d843b32012-09-13 18:33:20 -07001061 int new_freq;
1062 int timer_interval_us;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001063
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001064 if (dcvs_core_id < CPU_OFFSET || dcvs_core_id > CORES_MAX) {
1065 __err("%s invalid dcvs_core_id = %d returning -EINVAL\n",
1066 __func__, dcvs_core_id);
1067 return -EINVAL;
1068 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001069
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001070 core = msm_dcvs_get_core(dcvs_core_id);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001071 if (!core)
1072 return ret;
1073
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001074 core->actual_freq = core->get_frequency(core->type_core_num);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001075
1076 spin_lock_irqsave(&core->pending_freq_lock, flags);
1077 /* mark that we are ready to accept new frequencies */
1078 request_freq_change(core, NO_OUTSTANDING_FREQ_CHANGE);
1079 spin_unlock_irqrestore(&core->pending_freq_lock, flags);
1080
1081 spin_lock_irqsave(&core->idle_state_change_lock, flags);
1082 core->idle_entered = -1;
1083 spin_unlock_irqrestore(&core->idle_state_change_lock, flags);
1084
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001085 /* Notify TZ to start receiving idle info for the core */
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001086 ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_DCVS_ENABLE, 1, &ret1);
1087
Abhijeet Dharmapurikar8d843b32012-09-13 18:33:20 -07001088 ret = msm_dcvs_scm_event(
1089 core->dcvs_core_id, MSM_DCVS_SCM_CORE_ONLINE, core->actual_freq,
1090 0, &new_freq, &timer_interval_us);
1091 if (ret)
1092 __err("Error (%d) DCVS sending online for %s\n",
1093 ret, core->core_name);
1094
1095 if (new_freq != 0) {
1096 spin_lock_irqsave(&core->pending_freq_lock, flags);
1097 request_freq_change(core, new_freq);
1098 spin_unlock_irqrestore(&core->pending_freq_lock, flags);
1099 }
1100 force_start_slack_timer(core, timer_interval_us);
1101
Steve Mucklec1785c32012-11-13 14:27:43 -08001102 core->flags |= CORE_FLAG_TEMP_UPDATE;
1103 INIT_DELAYED_WORK(&core->temperature_work, msm_dcvs_report_temp_work);
1104 schedule_delayed_work(&core->temperature_work,
1105 msecs_to_jiffies(core->info->thermal_poll_ms));
Abhijeet Dharmapurikar8d843b32012-09-13 18:33:20 -07001106
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001107 core->idle_enable(core->type_core_num, MSM_DCVS_ENABLE_IDLE_PULSE);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001108 return 0;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001109}
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -07001110EXPORT_SYMBOL(msm_dcvs_freq_sink_start);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001111
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001112int msm_dcvs_freq_sink_stop(int dcvs_core_id)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001113{
1114 int ret = -EINVAL;
1115 struct dcvs_core *core = NULL;
Eugene Seah76af9832012-03-28 18:43:53 -06001116 uint32_t ret1;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001117 uint32_t freq;
1118 unsigned long flags;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001119
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001120 if (dcvs_core_id < 0 || dcvs_core_id > CORES_MAX) {
1121 pr_err("%s invalid dcvs_core_id = %d returning -EINVAL\n",
1122 __func__, dcvs_core_id);
1123 return -EINVAL;
1124 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001125
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001126 core = msm_dcvs_get_core(dcvs_core_id);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001127 if (!core) {
1128 __err("couldn't find core for coreid = %d\n", dcvs_core_id);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001129 return ret;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001130 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001131
Steve Mucklec1785c32012-11-13 14:27:43 -08001132 core->flags &= ~CORE_FLAG_TEMP_UPDATE;
1133 cancel_delayed_work(&core->temperature_work);
1134
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001135 core->idle_enable(core->type_core_num, MSM_DCVS_DISABLE_IDLE_PULSE);
1136 /* Notify TZ to stop receiving idle info for the core */
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001137 ret = msm_dcvs_scm_event(core->dcvs_core_id, MSM_DCVS_SCM_DCVS_ENABLE,
1138 0, core->actual_freq, &freq, &ret1);
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001139 core->idle_enable(core->type_core_num,
1140 MSM_DCVS_ENABLE_HIGH_LATENCY_MODES);
Steve Muckle43a980c2012-11-19 16:38:55 -08001141
1142 if (core->type == MSM_DCVS_CORE_TYPE_GPU)
1143 mutex_lock(&gpu_floor_mutex);
1144
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001145 spin_lock_irqsave(&core->pending_freq_lock, flags);
1146 /* flush out all the pending freq changes */
1147 request_freq_change(core, STOP_FREQ_CHANGE);
1148 spin_unlock_irqrestore(&core->pending_freq_lock, flags);
Steve Muckle43a980c2012-11-19 16:38:55 -08001149
1150 if (core->type == MSM_DCVS_CORE_TYPE_GPU)
1151 mutex_unlock(&gpu_floor_mutex);
1152
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001153 force_stop_slack_timer(core);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001154
1155 return 0;
1156}
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -07001157EXPORT_SYMBOL(msm_dcvs_freq_sink_stop);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001158
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001159int msm_dcvs_idle(int dcvs_core_id, enum msm_core_idle_state state,
1160 uint32_t iowaited)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001161{
1162 int ret = 0;
1163 struct dcvs_core *core = NULL;
Eugene Seah76af9832012-03-28 18:43:53 -06001164 uint32_t timer_interval_us = 0;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001165 uint32_t r0, r1;
1166
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001167 if (dcvs_core_id < CPU_OFFSET || dcvs_core_id > CORES_MAX) {
1168 pr_err("invalid dcvs_core_id = %d ret -EINVAL\n", dcvs_core_id);
1169 return -EINVAL;
1170 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001171
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001172 core = msm_dcvs_get_core(dcvs_core_id);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001173
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001174 switch (state) {
1175 case MSM_DCVS_IDLE_ENTER:
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001176 stop_slack_timer(core);
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001177 ret = msm_dcvs_scm_event(core->dcvs_core_id,
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001178 MSM_DCVS_SCM_IDLE_ENTER, 0, 0, &r0, &r1);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001179 if (ret < 0 && ret != -13)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001180 __err("Error (%d) sending idle enter for %s\n",
1181 ret, core->core_name);
Abhijeet Dharmapurikar07cf2ff2012-09-13 19:05:13 -07001182 trace_msm_dcvs_idle("idle_enter_exit", core->core_name, 1);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001183 break;
1184
1185 case MSM_DCVS_IDLE_EXIT:
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001186 ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_IDLE_EXIT,
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001187 iowaited, &timer_interval_us);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001188 if (ret)
1189 __err("Error (%d) sending idle exit for %s\n",
1190 ret, core->core_name);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001191 start_slack_timer(core, timer_interval_us);
Abhijeet Dharmapurikar07cf2ff2012-09-13 19:05:13 -07001192 trace_msm_dcvs_idle("idle_enter_exit", core->core_name, 0);
1193 trace_msm_dcvs_iowait("iowait", core->core_name, iowaited);
1194 trace_msm_dcvs_slack_time("slack_timer_dcvs", core->core_name,
1195 timer_interval_us);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001196 break;
1197 }
1198
1199 return ret;
1200}
1201EXPORT_SYMBOL(msm_dcvs_idle);
1202
1203static int __init msm_dcvs_late_init(void)
1204{
1205 struct kobject *module_kobj = NULL;
1206 int ret = 0;
1207
1208 module_kobj = kset_find_obj(module_kset, KBUILD_MODNAME);
1209 if (!module_kobj) {
1210 pr_err("%s: cannot find kobject for module %s\n",
1211 __func__, KBUILD_MODNAME);
1212 ret = -ENOENT;
1213 goto err;
1214 }
1215
1216 cores_kobj = kobject_create_and_add("cores", module_kobj);
1217 if (!cores_kobj) {
1218 __err("Cannot create %s kobject\n", "cores");
1219 ret = -ENOMEM;
1220 goto err;
1221 }
1222
1223 debugfs_base = debugfs_create_dir("msm_dcvs", NULL);
1224 if (!debugfs_base) {
1225 __err("Cannot create debugfs base %s\n", "msm_dcvs");
1226 ret = -ENOENT;
1227 goto err;
1228 }
1229
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001230err:
1231 if (ret) {
1232 kobject_del(cores_kobj);
1233 cores_kobj = NULL;
1234 debugfs_remove(debugfs_base);
1235 }
1236
1237 return ret;
1238}
1239late_initcall(msm_dcvs_late_init);
1240
Steve Mucklea9aac292012-11-02 15:41:00 -07001241static int __devinit dcvs_probe(struct platform_device *pdev)
1242{
1243 if (pdev->dev.platform_data)
1244 dcvs_pdata = pdev->dev.platform_data;
1245
1246 return 0;
1247}
1248
1249static struct platform_driver dcvs_driver = {
1250 .probe = dcvs_probe,
1251 .driver = {
1252 .name = "dcvs",
1253 .owner = THIS_MODULE,
1254 },
1255};
1256
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001257static int __init msm_dcvs_early_init(void)
1258{
1259 int ret = 0;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001260 int i;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001261
Steve Mucklea9aac292012-11-02 15:41:00 -07001262 platform_driver_register(&dcvs_driver);
1263
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001264 if (!msm_dcvs_enabled) {
1265 __info("Not enabled (%d)\n", msm_dcvs_enabled);
1266 return 0;
1267 }
1268
Abhijeet Dharmapurikar3edb5de2012-09-13 11:02:03 -07001269
1270 /* Only need about 32kBytes for normal operation */
1271 ret = msm_dcvs_scm_init(SZ_32K);
1272 if (ret) {
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001273 __err("Unable to initialize DCVS err=%d\n", ret);
Abhijeet Dharmapurikar3edb5de2012-09-13 11:02:03 -07001274 goto done;
1275 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001276
Steve Muckle43a980c2012-11-19 16:38:55 -08001277 for (i = 0; i < CORES_MAX; i++) {
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001278 core_list[i].dcvs_core_id = -1;
Steve Muckle43a980c2012-11-19 16:38:55 -08001279 core_list[i].pending_freq = STOP_FREQ_CHANGE;
1280 }
Abhijeet Dharmapurikar3edb5de2012-09-13 11:02:03 -07001281done:
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001282 return ret;
1283}
1284postcore_initcall(msm_dcvs_early_init);