blob: 358027c140fa1a7860e2cf276292341eee5a5590 [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
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070037struct core_attribs {
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070038 struct kobj_attribute freq_change_us;
39
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070040 struct kobj_attribute disable_pc_threshold;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -070041 struct kobj_attribute em_win_size_min_us;
42 struct kobj_attribute em_win_size_max_us;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070043 struct kobj_attribute em_max_util_pct;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -070044 struct kobj_attribute group_id;
45 struct kobj_attribute max_freq_chg_time_us;
46 struct kobj_attribute slack_mode_dynamic;
47 struct kobj_attribute slack_time_min_us;
48 struct kobj_attribute slack_time_max_us;
49 struct kobj_attribute slack_weight_thresh_pct;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070050 struct kobj_attribute ss_iobusy_conv;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -070051 struct kobj_attribute ss_win_size_min_us;
52 struct kobj_attribute ss_win_size_max_us;
53 struct kobj_attribute ss_util_pct;
54
55 struct kobj_attribute active_coeff_a;
56 struct kobj_attribute active_coeff_b;
57 struct kobj_attribute active_coeff_c;
58 struct kobj_attribute leakage_coeff_a;
59 struct kobj_attribute leakage_coeff_b;
60 struct kobj_attribute leakage_coeff_c;
61 struct kobj_attribute leakage_coeff_d;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070062
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -070063 struct kobj_attribute thermal_poll_ms;
64
Steve Muckle118f47b2012-10-17 16:09:37 -070065 struct kobj_attribute freq_tbl;
66
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070067 struct attribute_group attrib_group;
68};
69
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -070070enum pending_freq_state {
71 /*
72 * used by the thread to check if pending_freq was updated while it was
73 * setting previous frequency - this is written to and used by the
74 * freq updating thread
75 */
76 NO_OUTSTANDING_FREQ_CHANGE = 0,
77
78 /*
79 * This request is set to indicate that the governor is stopped and no
80 * more frequency change requests are accepted untill it starts again.
81 * This is checked/used by the threads that want to change the freq
82 */
83 STOP_FREQ_CHANGE = -1,
84
85 /*
86 * Any other +ve value means that a freq change was requested and the
87 * thread has not gotten around to update it
88 *
89 * Any other -ve value means that this is the last freq change i.e. a
90 * freq change was requested but the thread has not run yet and
91 * meanwhile the governor was stopped.
92 */
93};
94
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070095struct dcvs_core {
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -070096 spinlock_t idle_state_change_lock;
97 /* 0 when not idle (busy) 1 when idle and -1 when governor starts and
98 * we dont know whether the next call is going to be idle enter or exit
99 */
100 int idle_entered;
101
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700102 enum msm_dcvs_core_type type;
103 /* this is the number in each type for example cpu 0,1,2 and gpu 0,1 */
104 int type_core_num;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700105 char core_name[CORE_NAME_MAX];
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700106 uint32_t actual_freq;
107 uint32_t freq_change_us;
108
109 uint32_t max_time_us; /* core param */
110
111 struct msm_dcvs_algo_param algo_param;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700112 struct msm_dcvs_energy_curve_coeffs coeffs;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700113
114 /* private */
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700115 ktime_t time_start;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700116 struct task_struct *task;
117 struct core_attribs attrib;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700118 uint32_t dcvs_core_id;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700119 struct msm_dcvs_core_info *info;
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700120 int sensor;
Abhijeet Dharmapurikarbbb52fe2012-08-31 20:31:16 -0700121 wait_queue_head_t wait_q;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700122
123 int (*set_frequency)(int type_core_num, unsigned int freq);
124 unsigned int (*get_frequency)(int type_core_num);
125 int (*idle_enable)(int type_core_num,
Abhijeet Dharmapurikar6e9b34f2012-09-10 16:03:39 -0700126 enum msm_core_control_event event);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700127
128 spinlock_t pending_freq_lock;
129 int pending_freq;
130
131 struct hrtimer slack_timer;
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700132 struct delayed_work temperature_work;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700133};
134
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700135static int msm_dcvs_enabled = 1;
136module_param_named(enable, msm_dcvs_enabled, int, S_IRUGO | S_IWUSR | S_IWGRP);
137
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700138static struct dentry *debugfs_base;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700139
140static struct dcvs_core core_list[CORES_MAX];
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700141
142static struct kobject *cores_kobj;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700143
Steve Mucklea9aac292012-11-02 15:41:00 -0700144#define DCVS_MAX_NUM_FREQS 15
145static struct msm_dcvs_freq_entry cpu_freq_tbl[DCVS_MAX_NUM_FREQS];
146static unsigned num_cpu_freqs;
147static struct msm_dcvs_platform_data *dcvs_pdata;
148
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700149static void force_stop_slack_timer(struct dcvs_core *core)
150{
151 unsigned long flags;
152
153 spin_lock_irqsave(&core->idle_state_change_lock, flags);
154 hrtimer_cancel(&core->slack_timer);
155 spin_unlock_irqrestore(&core->idle_state_change_lock, flags);
156}
157
Abhijeet Dharmapurikar8d843b32012-09-13 18:33:20 -0700158static void force_start_slack_timer(struct dcvs_core *core, int slack_us)
159{
160 unsigned long flags;
161 int ret;
162
163 spin_lock_irqsave(&core->idle_state_change_lock, flags);
164
165 /*
166 * only start the timer if governor is not stopped
167 */
168 if (slack_us != 0) {
169 ret = hrtimer_start(&core->slack_timer,
170 ktime_set(0, slack_us * 1000),
171 HRTIMER_MODE_REL_PINNED);
172 if (ret) {
173 pr_err("%s Failed to start timer ret = %d\n",
174 core->core_name, ret);
175 }
176 }
177
178 spin_unlock_irqrestore(&core->idle_state_change_lock, flags);
179}
180
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700181static void stop_slack_timer(struct dcvs_core *core)
182{
183 unsigned long flags;
184
185 spin_lock_irqsave(&core->idle_state_change_lock, flags);
186 /* err only for cpu type's GPU's can do idle exit consecutively */
187 if (core->idle_entered == 1 && !(core->dcvs_core_id >= GPU_OFFSET))
188 __err("%s trying to reenter idle", core->core_name);
189 core->idle_entered = 1;
190 hrtimer_cancel(&core->slack_timer);
191 core->idle_entered = 1;
192 spin_unlock_irqrestore(&core->idle_state_change_lock, flags);
193}
194
195static void start_slack_timer(struct dcvs_core *core, int slack_us)
196{
197 unsigned long flags1, flags2;
198 int ret;
199
200 spin_lock_irqsave(&core->idle_state_change_lock, flags2);
201
202 spin_lock_irqsave(&core->pending_freq_lock, flags1);
203
204 /* err only for cpu type's GPU's can do idle enter consecutively */
205 if (core->idle_entered == 0 && !(core->dcvs_core_id >= GPU_OFFSET))
206 __err("%s trying to reexit idle", core->core_name);
207 core->idle_entered = 0;
208 /*
209 * only start the timer if governor is not stopped
210 */
211 if (slack_us != 0
212 && !(core->pending_freq < NO_OUTSTANDING_FREQ_CHANGE)) {
213 ret = hrtimer_start(&core->slack_timer,
214 ktime_set(0, slack_us * 1000),
215 HRTIMER_MODE_REL_PINNED);
216 if (ret) {
217 pr_err("%s Failed to start timer ret = %d\n",
218 core->core_name, ret);
219 }
220 }
221 spin_unlock_irqrestore(&core->pending_freq_lock, flags1);
222
223 spin_unlock_irqrestore(&core->idle_state_change_lock, flags2);
224}
225
226static void restart_slack_timer(struct dcvs_core *core, int slack_us)
227{
228 unsigned long flags1, flags2;
229 int ret;
230
231 spin_lock_irqsave(&core->idle_state_change_lock, flags2);
232
233 hrtimer_cancel(&core->slack_timer);
234
235 spin_lock_irqsave(&core->pending_freq_lock, flags1);
236
237 /*
238 * only start the timer if idle is not entered
239 * and governor is not stopped
240 */
241 if (slack_us != 0 && (core->idle_entered != 1)
242 && !(core->pending_freq < NO_OUTSTANDING_FREQ_CHANGE)) {
243 ret = hrtimer_start(&core->slack_timer,
244 ktime_set(0, slack_us * 1000),
245 HRTIMER_MODE_REL_PINNED);
246 if (ret) {
247 pr_err("%s Failed to start timer ret = %d\n",
248 core->core_name, ret);
249 }
250 }
251 spin_unlock_irqrestore(&core->pending_freq_lock, flags1);
252 spin_unlock_irqrestore(&core->idle_state_change_lock, flags2);
253}
254
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700255static int __msm_dcvs_change_freq(struct dcvs_core *core)
256{
257 int ret = 0;
258 unsigned long flags = 0;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700259 int requested_freq = 0;
260 ktime_t time_start;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700261 uint32_t slack_us = 0;
262 uint32_t ret1 = 0;
263
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700264 spin_lock_irqsave(&core->pending_freq_lock, flags);
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700265repeat:
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700266 BUG_ON(!core->pending_freq);
267 if (core->pending_freq == STOP_FREQ_CHANGE)
268 BUG();
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700269
270 requested_freq = core->pending_freq;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700271 time_start = core->time_start;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700272 core->time_start = ns_to_ktime(0);
273
274 if (requested_freq < 0) {
275 requested_freq = -1 * requested_freq;
276 core->pending_freq = STOP_FREQ_CHANGE;
277 } else {
278 core->pending_freq = NO_OUTSTANDING_FREQ_CHANGE;
279 }
280
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700281 if (requested_freq == core->actual_freq)
282 goto out;
283
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700284 spin_unlock_irqrestore(&core->pending_freq_lock, flags);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700285
286 /**
287 * Call the frequency sink driver to change the frequency
288 * We will need to get back the actual frequency in KHz and
289 * the record the time taken to change it.
290 */
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700291 ret = core->set_frequency(core->type_core_num, requested_freq);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700292 if (ret <= 0)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700293 __err("Core %s failed to set freq %u\n",
294 core->core_name, requested_freq);
Eugene Seah76af9832012-03-28 18:43:53 -0600295 /* continue to call TZ to get updated slack timer */
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700296 else
Eugene Seah76af9832012-03-28 18:43:53 -0600297 core->actual_freq = ret;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700298
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700299 core->freq_change_us = (uint32_t)ktime_to_us(
300 ktime_sub(ktime_get(), time_start));
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700301
302 /**
303 * Disable low power modes if the actual frequency is >
304 * disable_pc_threshold.
305 */
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700306 if (core->actual_freq > core->algo_param.disable_pc_threshold) {
307 core->idle_enable(core->type_core_num,
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700308 MSM_DCVS_DISABLE_HIGH_LATENCY_MODES);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700309 } else if (core->actual_freq <= core->algo_param.disable_pc_threshold) {
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700310 core->idle_enable(core->type_core_num,
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700311 MSM_DCVS_ENABLE_HIGH_LATENCY_MODES);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700312 }
313
314 /**
315 * Update algorithm with new freq and time taken to change
316 * to this frequency and that will get us the new slack
317 * timer
318 */
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700319 ret = msm_dcvs_scm_event(core->dcvs_core_id,
320 MSM_DCVS_SCM_CLOCK_FREQ_UPDATE,
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700321 core->actual_freq, core->freq_change_us,
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700322 &slack_us, &ret1);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700323 if (ret) {
324 __err("Error sending core (%s) dcvs_core_id = %d freq change (%u) reqfreq = %d slack_us=%d ret = %d\n",
325 core->core_name, core->dcvs_core_id,
326 core->actual_freq, requested_freq,
327 slack_us, ret);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700328 }
329
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700330 /* TODO confirm that we get a valid freq from SM even when the above
331 * FREQ_UPDATE fails
332 */
333 restart_slack_timer(core, slack_us);
334 spin_lock_irqsave(&core->pending_freq_lock, flags);
335
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700336 /**
337 * By the time we are done with freq changes, we could be asked to
338 * change again. Check before exiting.
339 */
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700340 if (core->pending_freq != NO_OUTSTANDING_FREQ_CHANGE
341 && core->pending_freq != STOP_FREQ_CHANGE) {
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700342 goto repeat;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700343 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700344
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700345out: /* should always be jumped to with the spin_lock held */
346 spin_unlock_irqrestore(&core->pending_freq_lock, flags);
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700347
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700348 return ret;
349}
350
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700351static void msm_dcvs_report_temp_work(struct work_struct *work)
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700352{
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700353 struct dcvs_core *core = container_of(work,
354 struct dcvs_core,
355 temperature_work.work);
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700356 struct msm_dcvs_core_info *info = core->info;
357 struct tsens_device tsens_dev;
358 int ret;
359 unsigned long temp = 0;
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700360 int interval_ms;
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700361
362 tsens_dev.sensor_num = core->sensor;
363 ret = tsens_get_temp(&tsens_dev, &temp);
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700364 if (!temp) {
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700365 tsens_dev.sensor_num = 0;
366 ret = tsens_get_temp(&tsens_dev, &temp);
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700367 if (!temp)
368 goto out;
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700369 }
370
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700371 if (temp == info->power_param.current_temp)
372 goto out;
373 info->power_param.current_temp = temp;
374
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700375 ret = msm_dcvs_scm_set_power_params(core->dcvs_core_id,
376 &info->power_param,
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700377 &info->freq_tbl[0], &core->coeffs);
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700378out:
379 if (info->thermal_poll_ms == 0)
380 interval_ms = 60000;
381 else if (info->thermal_poll_ms < 1000)
382 interval_ms = 1000;
383 else
384 interval_ms = info->thermal_poll_ms;
385
386 schedule_delayed_work(&core->temperature_work,
387 msecs_to_jiffies(interval_ms));
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700388}
389
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700390static int msm_dcvs_do_freq(void *data)
391{
392 struct dcvs_core *core = (struct dcvs_core *)data;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700393
394 while (!kthread_should_stop()) {
Abhijeet Dharmapurikarbbb52fe2012-08-31 20:31:16 -0700395 wait_event(core->wait_q, !(core->pending_freq == 0 ||
396 core->pending_freq == -1) ||
397 kthread_should_stop());
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700398
399 if (kthread_should_stop())
400 break;
401
Abhijeet Dharmapurikarbbb52fe2012-08-31 20:31:16 -0700402 __msm_dcvs_change_freq(core);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700403 }
404
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700405 return 0;
406}
407
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700408/* freq_pending_lock should be held */
409static void request_freq_change(struct dcvs_core *core, int new_freq)
410{
411 if (new_freq == NO_OUTSTANDING_FREQ_CHANGE) {
412 if (core->pending_freq != STOP_FREQ_CHANGE) {
413 __err("%s gov started with earlier pending freq %d\n",
414 core->core_name, core->pending_freq);
415 }
416 core->pending_freq = NO_OUTSTANDING_FREQ_CHANGE;
417 return;
418 }
419
420 if (new_freq == STOP_FREQ_CHANGE) {
421 if (core->pending_freq == NO_OUTSTANDING_FREQ_CHANGE)
422 core->pending_freq = STOP_FREQ_CHANGE;
423 else if (core->pending_freq > 0)
424 core->pending_freq = -1 * core->pending_freq;
425 return;
426 }
427
428 if (core->pending_freq < 0) {
429 /* a value less than 0 means that the governor has stopped
430 * and no more freq changes should be requested
431 */
432 return;
433 }
434
435 if (core->actual_freq != new_freq && core->pending_freq != new_freq) {
436 core->pending_freq = new_freq;
437 core->time_start = ktime_get();
438 wake_up(&core->wait_q);
439 }
440}
441
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700442static int msm_dcvs_update_freq(struct dcvs_core *core,
Eugene Seah76af9832012-03-28 18:43:53 -0600443 enum msm_dcvs_scm_event event, uint32_t param0,
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700444 uint32_t *ret1)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700445{
446 int ret = 0;
447 unsigned long flags = 0;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700448 uint32_t new_freq = -EINVAL;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700449
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700450 spin_lock_irqsave(&core->pending_freq_lock, flags);
451
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700452 ret = msm_dcvs_scm_event(core->dcvs_core_id, event, param0,
Eugene Seah76af9832012-03-28 18:43:53 -0600453 core->actual_freq, &new_freq, ret1);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700454 if (ret) {
Jeff Ohlstein4aa4a2b2012-06-28 19:03:49 -0700455 if (ret == -13)
456 ret = 0;
457 else
458 __err("Error (%d) sending SCM event %d for core %s\n",
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700459 ret, event, core->core_name);
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700460 goto out;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700461 }
462
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700463 if (new_freq == 0) {
464 /*
465 * sometimes TZ gives us a 0 freq back,
466 * do not queue up a request
467 */
468 goto out;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700469 }
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700470
471 request_freq_change(core, new_freq);
472
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700473out:
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700474 spin_unlock_irqrestore(&core->pending_freq_lock, flags);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700475
476 return ret;
477}
478
479static enum hrtimer_restart msm_dcvs_core_slack_timer(struct hrtimer *timer)
480{
481 int ret = 0;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700482 struct dcvs_core *core = container_of(timer,
483 struct dcvs_core, slack_timer);
Eugene Seah76af9832012-03-28 18:43:53 -0600484 uint32_t ret1;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700485
Abhijeet Dharmapurikar07cf2ff2012-09-13 19:05:13 -0700486 trace_printk("dcvs: Slack timer fired for core=%s\n", core->core_name);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700487 /**
488 * Timer expired, notify TZ
489 * Dont care about the third arg.
490 */
Eugene Seah76af9832012-03-28 18:43:53 -0600491 ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_QOS_TIMER_EXPIRED, 0,
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700492 &ret1);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700493 if (ret)
494 __err("Timer expired for core %s but failed to notify.\n",
495 core->core_name);
496
497 return HRTIMER_NORESTART;
498}
499
500/* Helper functions and macros for sysfs nodes for a core */
501#define CORE_FROM_ATTRIBS(attr, name) \
502 container_of(container_of(attr, struct core_attribs, name), \
503 struct dcvs_core, attrib);
504
505#define DCVS_PARAM_SHOW(_name, v) \
506static ssize_t msm_dcvs_attr_##_name##_show(struct kobject *kobj, \
507 struct kobj_attribute *attr, char *buf) \
508{ \
509 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
510 return snprintf(buf, PAGE_SIZE, "%d\n", v); \
511}
512
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700513#define DCVS_PARAM_STORE(_name) \
514static ssize_t msm_dcvs_attr_##_name##_show(struct kobject *kobj,\
515 struct kobj_attribute *attr, char *buf) \
516{ \
517 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
518 return snprintf(buf, PAGE_SIZE, "%d\n", core->info->_name); \
519} \
520static ssize_t msm_dcvs_attr_##_name##_store(struct kobject *kobj, \
521 struct kobj_attribute *attr, const char *buf, size_t count) \
522{ \
523 int ret = 0; \
524 uint32_t val = 0; \
525 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
526 ret = kstrtouint(buf, 10, &val); \
527 if (ret) { \
528 __err("Invalid input %s for %s\n", buf, __stringify(_name));\
529 } else { \
530 core->info->_name = val; \
531 } \
532 return count; \
533}
534
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700535#define DCVS_ALGO_PARAM(_name) \
536static ssize_t msm_dcvs_attr_##_name##_show(struct kobject *kobj,\
537 struct kobj_attribute *attr, char *buf) \
538{ \
539 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
540 return snprintf(buf, PAGE_SIZE, "%d\n", core->algo_param._name); \
541} \
542static ssize_t msm_dcvs_attr_##_name##_store(struct kobject *kobj, \
543 struct kobj_attribute *attr, const char *buf, size_t count) \
544{ \
545 int ret = 0; \
546 uint32_t val = 0; \
547 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700548 ret = kstrtouint(buf, 10, &val); \
549 if (ret) { \
550 __err("Invalid input %s for %s\n", buf, __stringify(_name));\
551 } else { \
552 uint32_t old_val = core->algo_param._name; \
553 core->algo_param._name = val; \
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700554 ret = msm_dcvs_scm_set_algo_params(core->dcvs_core_id, \
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700555 &core->algo_param); \
556 if (ret) { \
557 core->algo_param._name = old_val; \
558 __err("Error(%d) in setting %d for algo param %s\n",\
559 ret, val, __stringify(_name)); \
560 } \
561 } \
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700562 return count; \
563}
564
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700565#define DCVS_ENERGY_PARAM(_name) \
566static ssize_t msm_dcvs_attr_##_name##_show(struct kobject *kobj,\
567 struct kobj_attribute *attr, char *buf) \
568{ \
569 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
570 return snprintf(buf, PAGE_SIZE, "%d\n", core->coeffs._name); \
571} \
572static ssize_t msm_dcvs_attr_##_name##_store(struct kobject *kobj, \
573 struct kobj_attribute *attr, const char *buf, size_t count) \
574{ \
575 int ret = 0; \
576 int32_t val = 0; \
577 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700578 ret = kstrtoint(buf, 10, &val); \
579 if (ret) { \
580 __err("Invalid input %s for %s\n", buf, __stringify(_name));\
581 } else { \
582 int32_t old_val = core->coeffs._name; \
583 core->coeffs._name = val; \
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700584 ret = msm_dcvs_scm_set_power_params(core->dcvs_core_id, \
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700585 &core->info->power_param, &core->info->freq_tbl[0], \
586 &core->coeffs); \
587 if (ret) { \
588 core->coeffs._name = old_val; \
589 __err("Error(%d) in setting %d for coeffs param %s\n",\
590 ret, val, __stringify(_name)); \
591 } \
592 } \
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700593 return count; \
594}
595
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700596#define DCVS_RO_ATTRIB(i, _name) \
597 core->attrib._name.attr.name = __stringify(_name); \
598 core->attrib._name.attr.mode = S_IRUGO; \
599 core->attrib._name.show = msm_dcvs_attr_##_name##_show; \
600 core->attrib._name.store = NULL; \
601 core->attrib.attrib_group.attrs[i] = &core->attrib._name.attr;
602
603#define DCVS_RW_ATTRIB(i, _name) \
604 core->attrib._name.attr.name = __stringify(_name); \
605 core->attrib._name.attr.mode = S_IRUGO | S_IWUSR; \
606 core->attrib._name.show = msm_dcvs_attr_##_name##_show; \
607 core->attrib._name.store = msm_dcvs_attr_##_name##_store; \
608 core->attrib.attrib_group.attrs[i] = &core->attrib._name.attr;
609
610/**
611 * Function declarations for different attributes.
612 * Gets used when setting the attribute show and store parameters.
613 */
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700614DCVS_PARAM_SHOW(freq_change_us, (core->freq_change_us))
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700615
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700616DCVS_ALGO_PARAM(disable_pc_threshold)
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700617DCVS_ALGO_PARAM(em_win_size_min_us)
618DCVS_ALGO_PARAM(em_win_size_max_us)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700619DCVS_ALGO_PARAM(em_max_util_pct)
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700620DCVS_ALGO_PARAM(group_id)
621DCVS_ALGO_PARAM(max_freq_chg_time_us)
622DCVS_ALGO_PARAM(slack_mode_dynamic)
623DCVS_ALGO_PARAM(slack_time_min_us)
624DCVS_ALGO_PARAM(slack_time_max_us)
625DCVS_ALGO_PARAM(slack_weight_thresh_pct)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700626DCVS_ALGO_PARAM(ss_iobusy_conv)
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700627DCVS_ALGO_PARAM(ss_win_size_min_us)
628DCVS_ALGO_PARAM(ss_win_size_max_us)
629DCVS_ALGO_PARAM(ss_util_pct)
630
631DCVS_ENERGY_PARAM(active_coeff_a)
632DCVS_ENERGY_PARAM(active_coeff_b)
633DCVS_ENERGY_PARAM(active_coeff_c)
634DCVS_ENERGY_PARAM(leakage_coeff_a)
635DCVS_ENERGY_PARAM(leakage_coeff_b)
636DCVS_ENERGY_PARAM(leakage_coeff_c)
637DCVS_ENERGY_PARAM(leakage_coeff_d)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700638
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700639DCVS_PARAM_STORE(thermal_poll_ms)
640
Steve Muckle118f47b2012-10-17 16:09:37 -0700641static ssize_t msm_dcvs_attr_freq_tbl_show(struct kobject *kobj,
642 struct kobj_attribute *attr,
643 char *buf)
644{
645 struct msm_dcvs_freq_entry *freq_tbl;
646 char *buf_idx = buf;
647 int i, len;
648 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, freq_tbl);
649
650 freq_tbl = core->info->freq_tbl;
651 *buf_idx = '\0';
652
653 /* limit the number of frequencies we will print into
654 * the PAGE_SIZE sysfs show buffer. */
655 if (core->info->power_param.num_freq > 64)
656 return 0;
657
658 for (i = 0; i < core->info->power_param.num_freq; i++) {
659 if (freq_tbl[i].is_trans_level) {
660 len = snprintf(buf_idx, 10, "%7d ", freq_tbl[i].freq);
661 /* buf_idx always points at terminating null */
662 buf_idx += len;
663 }
664 }
665 /* overwrite final trailing space with newline */
666 if (buf_idx > buf)
667 *(buf_idx - 1) = '\n';
668
669 return buf_idx - buf;
670}
671
672static ssize_t msm_dcvs_attr_freq_tbl_store(struct kobject *kobj,
673 struct kobj_attribute *attr,
674 const char *buf,
675 size_t count)
676{
677 struct msm_dcvs_freq_entry *freq_tbl;
678 uint32_t freq;
679 int i, ret;
680 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, freq_tbl);
681
682 freq_tbl = core->info->freq_tbl;
683
684 ret = kstrtouint(buf, 10, &freq);
685 if (ret) {
686 __err("Invalid input %s for freq_tbl\n", buf);
687 return count;
688 }
689
690 for (i = 0; i < core->info->power_param.num_freq; i++)
691 if (freq_tbl[i].freq == freq) {
692 freq_tbl[i].is_trans_level ^= 1;
693 break;
694 }
695
696 if (i >= core->info->power_param.num_freq) {
697 __err("Invalid frequency for freq_tbl: %d\n", freq);
698 return count;
699 }
700
701 ret = msm_dcvs_scm_set_power_params(core->dcvs_core_id,
702 &core->info->power_param,
703 &core->info->freq_tbl[0],
704 &core->coeffs);
705 if (ret) {
706 freq_tbl[i].is_trans_level ^= 1;
707 __err("Error %d in toggling freq %d (orig enable val %d)\n",
708 ret, freq_tbl[i].freq, freq_tbl[i].is_trans_level);
709 }
710 return count;
711}
712
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700713static int msm_dcvs_setup_core_sysfs(struct dcvs_core *core)
714{
715 int ret = 0;
716 struct kobject *core_kobj = NULL;
Steve Muckle118f47b2012-10-17 16:09:37 -0700717 const int attr_count = 25;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700718
719 BUG_ON(!cores_kobj);
720
721 core->attrib.attrib_group.attrs =
722 kzalloc(attr_count * sizeof(struct attribute *), GFP_KERNEL);
723
724 if (!core->attrib.attrib_group.attrs) {
725 ret = -ENOMEM;
726 goto done;
727 }
728
Abhijeet Dharmapurikar35876382012-09-13 16:09:51 -0700729 DCVS_RO_ATTRIB(0, freq_change_us);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700730
Abhijeet Dharmapurikar35876382012-09-13 16:09:51 -0700731 DCVS_RW_ATTRIB(1, disable_pc_threshold);
732 DCVS_RW_ATTRIB(2, em_win_size_min_us);
733 DCVS_RW_ATTRIB(3, em_win_size_max_us);
734 DCVS_RW_ATTRIB(4, em_max_util_pct);
735 DCVS_RW_ATTRIB(5, group_id);
736 DCVS_RW_ATTRIB(6, max_freq_chg_time_us);
737 DCVS_RW_ATTRIB(7, slack_mode_dynamic);
738 DCVS_RW_ATTRIB(8, slack_weight_thresh_pct);
739 DCVS_RW_ATTRIB(9, slack_time_min_us);
740 DCVS_RW_ATTRIB(10, slack_time_max_us);
741 DCVS_RW_ATTRIB(11, ss_iobusy_conv);
742 DCVS_RW_ATTRIB(12, ss_win_size_min_us);
743 DCVS_RW_ATTRIB(13, ss_win_size_max_us);
744 DCVS_RW_ATTRIB(14, ss_util_pct);
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700745
Abhijeet Dharmapurikar35876382012-09-13 16:09:51 -0700746 DCVS_RW_ATTRIB(15, active_coeff_a);
747 DCVS_RW_ATTRIB(16, active_coeff_b);
748 DCVS_RW_ATTRIB(17, active_coeff_c);
749 DCVS_RW_ATTRIB(18, leakage_coeff_a);
750 DCVS_RW_ATTRIB(19, leakage_coeff_b);
751 DCVS_RW_ATTRIB(20, leakage_coeff_c);
752 DCVS_RW_ATTRIB(21, leakage_coeff_d);
753 DCVS_RW_ATTRIB(22, thermal_poll_ms);
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700754
Steve Muckle118f47b2012-10-17 16:09:37 -0700755 DCVS_RW_ATTRIB(23, freq_tbl);
756
757 core->attrib.attrib_group.attrs[24] = NULL;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700758
759 core_kobj = kobject_create_and_add(core->core_name, cores_kobj);
760 if (!core_kobj) {
761 ret = -ENOMEM;
762 goto done;
763 }
764
765 ret = sysfs_create_group(core_kobj, &core->attrib.attrib_group);
766 if (ret)
767 __err("Cannot create core %s attr group\n", core->core_name);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700768
769done:
770 if (ret) {
771 kfree(core->attrib.attrib_group.attrs);
772 kobject_del(core_kobj);
773 }
774
775 return ret;
776}
777
Steve Muckle5e5a60d2012-10-09 13:25:22 -0700778static int get_core_offset(enum msm_dcvs_core_type type, int num)
779{
780 int offset = -EINVAL;
781
782 switch (type) {
783 case MSM_DCVS_CORE_TYPE_CPU:
784 offset = CPU_OFFSET + num;
785 BUG_ON(offset >= GPU_OFFSET);
786 break;
787 case MSM_DCVS_CORE_TYPE_GPU:
788 offset = GPU_OFFSET + num;
789 BUG_ON(offset >= CORES_MAX);
790 break;
791 default:
792 BUG();
793 }
794
795 return offset;
796}
797
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700798/* Return the core and initialize non platform data specific numbers in it */
799static struct dcvs_core *msm_dcvs_add_core(enum msm_dcvs_core_type type,
800 int num)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700801{
802 struct dcvs_core *core = NULL;
803 int i;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700804 char name[CORE_NAME_MAX];
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700805
Steve Muckle5e5a60d2012-10-09 13:25:22 -0700806 i = get_core_offset(type, num);
807 if (i < 0)
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700808 return NULL;
Steve Muckle5e5a60d2012-10-09 13:25:22 -0700809
810 if (type == MSM_DCVS_CORE_TYPE_CPU)
811 snprintf(name, CORE_NAME_MAX, "cpu%d", num);
812 else
813 snprintf(name, CORE_NAME_MAX, "gpu%d", num);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700814
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700815 core = &core_list[i];
816 core->dcvs_core_id = i;
Abhijeet Dharmapurikar2ebc0fe2012-09-12 14:05:13 -0700817 strlcpy(core->core_name, name, CORE_NAME_MAX);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700818 spin_lock_init(&core->pending_freq_lock);
819 spin_lock_init(&core->idle_state_change_lock);
820 hrtimer_init(&core->slack_timer,
821 CLOCK_MONOTONIC, HRTIMER_MODE_REL_PINNED);
822 core->slack_timer.function = msm_dcvs_core_slack_timer;
Abhijeet Dharmapurikar2ebc0fe2012-09-12 14:05:13 -0700823 return core;
824}
825
826/* Return the core if found or add to list if @add_to_list is true */
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700827static struct dcvs_core *msm_dcvs_get_core(int offset)
Abhijeet Dharmapurikar2ebc0fe2012-09-12 14:05:13 -0700828{
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700829 /* if the handle is still not set bug */
830 BUG_ON(core_list[offset].dcvs_core_id == -1);
831 return &core_list[offset];
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700832}
833
Steve Mucklea9aac292012-11-02 15:41:00 -0700834void msm_dcvs_register_cpu_freq(uint32_t freq, uint32_t voltage)
835{
836 BUG_ON(freq == 0 || voltage == 0 ||
837 num_cpu_freqs == DCVS_MAX_NUM_FREQS);
838
839 cpu_freq_tbl[num_cpu_freqs].freq = freq;
840 cpu_freq_tbl[num_cpu_freqs].voltage = voltage;
841
842 num_cpu_freqs++;
843}
844
845static void update_cpu_dcvs_params(struct msm_dcvs_core_info *info)
846{
847 int i;
848
849 BUG_ON(num_cpu_freqs == 0);
850
851 info->freq_tbl = cpu_freq_tbl;
852 info->power_param.num_freq = num_cpu_freqs;
853
854 if (!dcvs_pdata || dcvs_pdata->num_sync_rules == 0)
855 return;
856
857 /* the first sync rule shows what the turbo frequencies are -
858 * these frequencies need energy offsets set */
859 for (i = 0; i < DCVS_MAX_NUM_FREQS && cpu_freq_tbl[i].freq != 0; i++)
860 if (cpu_freq_tbl[i].freq > dcvs_pdata->sync_rules[0].cpu_khz) {
861 cpu_freq_tbl[i].active_energy_offset = 100;
862 cpu_freq_tbl[i].leakage_energy_offset = 100;
863 }
864}
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700865
866int msm_dcvs_register_core(
867 enum msm_dcvs_core_type type,
868 int type_core_num,
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700869 struct msm_dcvs_core_info *info,
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700870 int (*set_frequency)(int type_core_num, unsigned int freq),
871 unsigned int (*get_frequency)(int type_core_num),
872 int (*idle_enable)(int type_core_num,
Abhijeet Dharmapurikar6e9b34f2012-09-10 16:03:39 -0700873 enum msm_core_control_event event),
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700874 int sensor)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700875{
876 int ret = -EINVAL;
Steve Muckle5e5a60d2012-10-09 13:25:22 -0700877 int offset;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700878 struct dcvs_core *core = NULL;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700879 uint32_t ret1;
880 uint32_t ret2;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700881
Steve Muckle5e5a60d2012-10-09 13:25:22 -0700882 offset = get_core_offset(type, type_core_num);
883 if (offset < 0)
884 return ret;
885 if (core_list[offset].dcvs_core_id != -1)
886 return core_list[offset].dcvs_core_id;
887
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700888 core = msm_dcvs_add_core(type, type_core_num);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700889 if (!core)
890 return ret;
891
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700892 core->type = type;
893 core->type_core_num = type_core_num;
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700894 core->set_frequency = set_frequency;
895 core->get_frequency = get_frequency;
Abhijeet Dharmapurikar6e9b34f2012-09-10 16:03:39 -0700896 core->idle_enable = idle_enable;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700897 core->pending_freq = STOP_FREQ_CHANGE;
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700898
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700899 core->info = info;
Steve Mucklea9aac292012-11-02 15:41:00 -0700900 if (type == MSM_DCVS_CORE_TYPE_CPU)
901 update_cpu_dcvs_params(info);
902
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700903 memcpy(&core->algo_param, &info->algo_param,
904 sizeof(struct msm_dcvs_algo_param));
905
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700906 memcpy(&core->coeffs, &info->energy_coeffs,
907 sizeof(struct msm_dcvs_energy_curve_coeffs));
908
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700909 /*
910 * The tz expects cpu0 to represent bit 0 in the mask, however the
911 * dcvs_core_id needs to start from 1, dcvs_core_id = 0 is used to
912 * indicate that this request is not associated with any core.
913 * mpdecision
914 */
915 info->core_param.core_bitmask_id
916 = 1 << (core->dcvs_core_id - CPU_OFFSET);
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700917 core->sensor = sensor;
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700918
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700919 ret = msm_dcvs_scm_register_core(core->dcvs_core_id, &info->core_param);
920 if (ret) {
921 __err("%s: scm register core fail handle = %d ret = %d\n",
922 __func__, core->dcvs_core_id, ret);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700923 goto bail;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700924 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700925
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700926 ret = msm_dcvs_scm_set_algo_params(core->dcvs_core_id,
927 &info->algo_param);
928 if (ret) {
929 __err("%s: scm algo params failed ret = %d\n", __func__, ret);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700930 goto bail;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700931 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700932
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700933 ret = msm_dcvs_scm_set_power_params(core->dcvs_core_id,
934 &info->power_param,
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700935 &info->freq_tbl[0], &core->coeffs);
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700936 if (ret) {
937 __err("%s: scm power params failed ret = %d\n", __func__, ret);
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700938 goto bail;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700939 }
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700940
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700941 ret = msm_dcvs_scm_event(core->dcvs_core_id, MSM_DCVS_SCM_CORE_ONLINE,
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700942 core->actual_freq, 0, &ret1, &ret2);
943 if (ret)
944 goto bail;
945
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700946 ret = msm_dcvs_setup_core_sysfs(core);
947 if (ret) {
948 __err("Unable to setup core %s sysfs\n", core->core_name);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700949 goto bail;
950 }
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700951 core->idle_entered = -1;
Abhijeet Dharmapurikarbbb52fe2012-08-31 20:31:16 -0700952 init_waitqueue_head(&core->wait_q);
953 core->task = kthread_run(msm_dcvs_do_freq, (void *)core,
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700954 "msm_dcvs/%d", core->dcvs_core_id);
955 ret = core->dcvs_core_id;
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700956
957 INIT_DELAYED_WORK(&core->temperature_work, msm_dcvs_report_temp_work);
958 schedule_delayed_work(&core->temperature_work,
959 msecs_to_jiffies(info->thermal_poll_ms));
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700960 return ret;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700961bail:
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700962 core->dcvs_core_id = -1;
963 return -EINVAL;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700964}
965EXPORT_SYMBOL(msm_dcvs_register_core);
966
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700967void msm_dcvs_update_limits(int dcvs_core_id)
Abhijeet Dharmapurikarc43f0db2012-08-31 20:42:53 -0700968{
969 struct dcvs_core *core;
970
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700971 if (dcvs_core_id < CPU_OFFSET || dcvs_core_id > CORES_MAX) {
972 __err("%s invalid dcvs_core_id = %d returning -EINVAL\n",
973 __func__, dcvs_core_id);
Abhijeet Dharmapurikarc43f0db2012-08-31 20:42:53 -0700974 return;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700975 }
Abhijeet Dharmapurikarc43f0db2012-08-31 20:42:53 -0700976
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700977 core = msm_dcvs_get_core(dcvs_core_id);
978 core->actual_freq = core->get_frequency(core->type_core_num);
Abhijeet Dharmapurikarc43f0db2012-08-31 20:42:53 -0700979}
980
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700981int msm_dcvs_freq_sink_start(int dcvs_core_id)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700982{
983 int ret = -EINVAL;
984 struct dcvs_core *core = NULL;
Eugene Seah76af9832012-03-28 18:43:53 -0600985 uint32_t ret1;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700986 unsigned long flags;
Abhijeet Dharmapurikar8d843b32012-09-13 18:33:20 -0700987 int new_freq;
988 int timer_interval_us;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700989
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700990 if (dcvs_core_id < CPU_OFFSET || dcvs_core_id > CORES_MAX) {
991 __err("%s invalid dcvs_core_id = %d returning -EINVAL\n",
992 __func__, dcvs_core_id);
993 return -EINVAL;
994 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700995
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700996 core = msm_dcvs_get_core(dcvs_core_id);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700997 if (!core)
998 return ret;
999
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001000 core->actual_freq = core->get_frequency(core->type_core_num);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001001
1002 spin_lock_irqsave(&core->pending_freq_lock, flags);
1003 /* mark that we are ready to accept new frequencies */
1004 request_freq_change(core, NO_OUTSTANDING_FREQ_CHANGE);
1005 spin_unlock_irqrestore(&core->pending_freq_lock, flags);
1006
1007 spin_lock_irqsave(&core->idle_state_change_lock, flags);
1008 core->idle_entered = -1;
1009 spin_unlock_irqrestore(&core->idle_state_change_lock, flags);
1010
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001011 /* Notify TZ to start receiving idle info for the core */
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001012 ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_DCVS_ENABLE, 1, &ret1);
1013
Abhijeet Dharmapurikar8d843b32012-09-13 18:33:20 -07001014 ret = msm_dcvs_scm_event(
1015 core->dcvs_core_id, MSM_DCVS_SCM_CORE_ONLINE, core->actual_freq,
1016 0, &new_freq, &timer_interval_us);
1017 if (ret)
1018 __err("Error (%d) DCVS sending online for %s\n",
1019 ret, core->core_name);
1020
1021 if (new_freq != 0) {
1022 spin_lock_irqsave(&core->pending_freq_lock, flags);
1023 request_freq_change(core, new_freq);
1024 spin_unlock_irqrestore(&core->pending_freq_lock, flags);
1025 }
1026 force_start_slack_timer(core, timer_interval_us);
1027
1028
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001029 core->idle_enable(core->type_core_num, MSM_DCVS_ENABLE_IDLE_PULSE);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001030 return 0;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001031}
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -07001032EXPORT_SYMBOL(msm_dcvs_freq_sink_start);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001033
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001034int msm_dcvs_freq_sink_stop(int dcvs_core_id)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001035{
1036 int ret = -EINVAL;
1037 struct dcvs_core *core = NULL;
Eugene Seah76af9832012-03-28 18:43:53 -06001038 uint32_t ret1;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001039 uint32_t freq;
1040 unsigned long flags;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001041
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001042 if (dcvs_core_id < 0 || dcvs_core_id > CORES_MAX) {
1043 pr_err("%s invalid dcvs_core_id = %d returning -EINVAL\n",
1044 __func__, dcvs_core_id);
1045 return -EINVAL;
1046 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001047
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001048 core = msm_dcvs_get_core(dcvs_core_id);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001049 if (!core) {
1050 __err("couldn't find core for coreid = %d\n", dcvs_core_id);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001051 return ret;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001052 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001053
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001054 core->idle_enable(core->type_core_num, MSM_DCVS_DISABLE_IDLE_PULSE);
1055 /* Notify TZ to stop receiving idle info for the core */
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001056 ret = msm_dcvs_scm_event(core->dcvs_core_id, MSM_DCVS_SCM_DCVS_ENABLE,
1057 0, core->actual_freq, &freq, &ret1);
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001058 core->idle_enable(core->type_core_num,
1059 MSM_DCVS_ENABLE_HIGH_LATENCY_MODES);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001060 spin_lock_irqsave(&core->pending_freq_lock, flags);
1061 /* flush out all the pending freq changes */
1062 request_freq_change(core, STOP_FREQ_CHANGE);
1063 spin_unlock_irqrestore(&core->pending_freq_lock, flags);
1064 force_stop_slack_timer(core);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001065
1066 return 0;
1067}
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -07001068EXPORT_SYMBOL(msm_dcvs_freq_sink_stop);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001069
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001070int msm_dcvs_idle(int dcvs_core_id, enum msm_core_idle_state state,
1071 uint32_t iowaited)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001072{
1073 int ret = 0;
1074 struct dcvs_core *core = NULL;
Eugene Seah76af9832012-03-28 18:43:53 -06001075 uint32_t timer_interval_us = 0;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001076 uint32_t r0, r1;
1077
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001078 if (dcvs_core_id < CPU_OFFSET || dcvs_core_id > CORES_MAX) {
1079 pr_err("invalid dcvs_core_id = %d ret -EINVAL\n", dcvs_core_id);
1080 return -EINVAL;
1081 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001082
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001083 core = msm_dcvs_get_core(dcvs_core_id);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001084
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001085 switch (state) {
1086 case MSM_DCVS_IDLE_ENTER:
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001087 stop_slack_timer(core);
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001088 ret = msm_dcvs_scm_event(core->dcvs_core_id,
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001089 MSM_DCVS_SCM_IDLE_ENTER, 0, 0, &r0, &r1);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001090 if (ret < 0 && ret != -13)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001091 __err("Error (%d) sending idle enter for %s\n",
1092 ret, core->core_name);
Abhijeet Dharmapurikar07cf2ff2012-09-13 19:05:13 -07001093 trace_msm_dcvs_idle("idle_enter_exit", core->core_name, 1);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001094 break;
1095
1096 case MSM_DCVS_IDLE_EXIT:
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001097 ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_IDLE_EXIT,
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001098 iowaited, &timer_interval_us);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001099 if (ret)
1100 __err("Error (%d) sending idle exit for %s\n",
1101 ret, core->core_name);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001102 start_slack_timer(core, timer_interval_us);
Abhijeet Dharmapurikar07cf2ff2012-09-13 19:05:13 -07001103 trace_msm_dcvs_idle("idle_enter_exit", core->core_name, 0);
1104 trace_msm_dcvs_iowait("iowait", core->core_name, iowaited);
1105 trace_msm_dcvs_slack_time("slack_timer_dcvs", core->core_name,
1106 timer_interval_us);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001107 break;
1108 }
1109
1110 return ret;
1111}
1112EXPORT_SYMBOL(msm_dcvs_idle);
1113
1114static int __init msm_dcvs_late_init(void)
1115{
1116 struct kobject *module_kobj = NULL;
1117 int ret = 0;
1118
1119 module_kobj = kset_find_obj(module_kset, KBUILD_MODNAME);
1120 if (!module_kobj) {
1121 pr_err("%s: cannot find kobject for module %s\n",
1122 __func__, KBUILD_MODNAME);
1123 ret = -ENOENT;
1124 goto err;
1125 }
1126
1127 cores_kobj = kobject_create_and_add("cores", module_kobj);
1128 if (!cores_kobj) {
1129 __err("Cannot create %s kobject\n", "cores");
1130 ret = -ENOMEM;
1131 goto err;
1132 }
1133
1134 debugfs_base = debugfs_create_dir("msm_dcvs", NULL);
1135 if (!debugfs_base) {
1136 __err("Cannot create debugfs base %s\n", "msm_dcvs");
1137 ret = -ENOENT;
1138 goto err;
1139 }
1140
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001141err:
1142 if (ret) {
1143 kobject_del(cores_kobj);
1144 cores_kobj = NULL;
1145 debugfs_remove(debugfs_base);
1146 }
1147
1148 return ret;
1149}
1150late_initcall(msm_dcvs_late_init);
1151
Steve Mucklea9aac292012-11-02 15:41:00 -07001152static int __devinit dcvs_probe(struct platform_device *pdev)
1153{
1154 if (pdev->dev.platform_data)
1155 dcvs_pdata = pdev->dev.platform_data;
1156
1157 return 0;
1158}
1159
1160static struct platform_driver dcvs_driver = {
1161 .probe = dcvs_probe,
1162 .driver = {
1163 .name = "dcvs",
1164 .owner = THIS_MODULE,
1165 },
1166};
1167
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001168static int __init msm_dcvs_early_init(void)
1169{
1170 int ret = 0;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001171 int i;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001172
Steve Mucklea9aac292012-11-02 15:41:00 -07001173 platform_driver_register(&dcvs_driver);
1174
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001175 if (!msm_dcvs_enabled) {
1176 __info("Not enabled (%d)\n", msm_dcvs_enabled);
1177 return 0;
1178 }
1179
Abhijeet Dharmapurikar3edb5de2012-09-13 11:02:03 -07001180
1181 /* Only need about 32kBytes for normal operation */
1182 ret = msm_dcvs_scm_init(SZ_32K);
1183 if (ret) {
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001184 __err("Unable to initialize DCVS err=%d\n", ret);
Abhijeet Dharmapurikar3edb5de2012-09-13 11:02:03 -07001185 goto done;
1186 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001187
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001188 for (i = 0; i < CORES_MAX; i++)
1189 core_list[i].dcvs_core_id = -1;
Abhijeet Dharmapurikar3edb5de2012-09-13 11:02:03 -07001190done:
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001191 return ret;
1192}
1193postcore_initcall(msm_dcvs_early_init);