blob: b2160c518c4aac39e7455b042ac4d6ce424a8ccf [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>
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070026#include <asm/atomic.h>
27#include <asm/page.h>
28#include <mach/msm_dcvs.h>
Abhijeet Dharmapurikar07cf2ff2012-09-13 19:05:13 -070029#include <trace/events/mpdcvs_trace.h>
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070030
31#define CORE_HANDLE_OFFSET (0xA0)
32#define __err(f, ...) pr_err("MSM_DCVS: %s: " f, __func__, __VA_ARGS__)
33#define __info(f, ...) pr_info("MSM_DCVS: %s: " f, __func__, __VA_ARGS__)
34#define MAX_PENDING (5)
35
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070036struct core_attribs {
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070037 struct kobj_attribute freq_change_us;
38
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070039 struct kobj_attribute disable_pc_threshold;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -070040 struct kobj_attribute em_win_size_min_us;
41 struct kobj_attribute em_win_size_max_us;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070042 struct kobj_attribute em_max_util_pct;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -070043 struct kobj_attribute group_id;
44 struct kobj_attribute max_freq_chg_time_us;
45 struct kobj_attribute slack_mode_dynamic;
46 struct kobj_attribute slack_time_min_us;
47 struct kobj_attribute slack_time_max_us;
48 struct kobj_attribute slack_weight_thresh_pct;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070049 struct kobj_attribute ss_iobusy_conv;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -070050 struct kobj_attribute ss_win_size_min_us;
51 struct kobj_attribute ss_win_size_max_us;
52 struct kobj_attribute ss_util_pct;
53
54 struct kobj_attribute active_coeff_a;
55 struct kobj_attribute active_coeff_b;
56 struct kobj_attribute active_coeff_c;
57 struct kobj_attribute leakage_coeff_a;
58 struct kobj_attribute leakage_coeff_b;
59 struct kobj_attribute leakage_coeff_c;
60 struct kobj_attribute leakage_coeff_d;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070061
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -070062 struct kobj_attribute thermal_poll_ms;
63
Steve Muckle118f47b2012-10-17 16:09:37 -070064 struct kobj_attribute freq_tbl;
65
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070066 struct attribute_group attrib_group;
67};
68
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -070069enum pending_freq_state {
70 /*
71 * used by the thread to check if pending_freq was updated while it was
72 * setting previous frequency - this is written to and used by the
73 * freq updating thread
74 */
75 NO_OUTSTANDING_FREQ_CHANGE = 0,
76
77 /*
78 * This request is set to indicate that the governor is stopped and no
79 * more frequency change requests are accepted untill it starts again.
80 * This is checked/used by the threads that want to change the freq
81 */
82 STOP_FREQ_CHANGE = -1,
83
84 /*
85 * Any other +ve value means that a freq change was requested and the
86 * thread has not gotten around to update it
87 *
88 * Any other -ve value means that this is the last freq change i.e. a
89 * freq change was requested but the thread has not run yet and
90 * meanwhile the governor was stopped.
91 */
92};
93
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -070094struct dcvs_core {
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -070095 spinlock_t idle_state_change_lock;
96 /* 0 when not idle (busy) 1 when idle and -1 when governor starts and
97 * we dont know whether the next call is going to be idle enter or exit
98 */
99 int idle_entered;
100
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700101 enum msm_dcvs_core_type type;
102 /* this is the number in each type for example cpu 0,1,2 and gpu 0,1 */
103 int type_core_num;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700104 char core_name[CORE_NAME_MAX];
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700105 uint32_t actual_freq;
106 uint32_t freq_change_us;
107
108 uint32_t max_time_us; /* core param */
109
110 struct msm_dcvs_algo_param algo_param;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700111 struct msm_dcvs_energy_curve_coeffs coeffs;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700112
113 /* private */
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700114 ktime_t time_start;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700115 struct task_struct *task;
116 struct core_attribs attrib;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700117 uint32_t dcvs_core_id;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700118 struct msm_dcvs_core_info *info;
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700119 int sensor;
Abhijeet Dharmapurikarbbb52fe2012-08-31 20:31:16 -0700120 wait_queue_head_t wait_q;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700121
122 int (*set_frequency)(int type_core_num, unsigned int freq);
123 unsigned int (*get_frequency)(int type_core_num);
124 int (*idle_enable)(int type_core_num,
Abhijeet Dharmapurikar6e9b34f2012-09-10 16:03:39 -0700125 enum msm_core_control_event event);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700126
127 spinlock_t pending_freq_lock;
128 int pending_freq;
129
130 struct hrtimer slack_timer;
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700131 struct delayed_work temperature_work;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700132};
133
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700134static int msm_dcvs_enabled = 1;
135module_param_named(enable, msm_dcvs_enabled, int, S_IRUGO | S_IWUSR | S_IWGRP);
136
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700137static struct dentry *debugfs_base;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700138
139static struct dcvs_core core_list[CORES_MAX];
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700140
141static struct kobject *cores_kobj;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700142
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700143static void force_stop_slack_timer(struct dcvs_core *core)
144{
145 unsigned long flags;
146
147 spin_lock_irqsave(&core->idle_state_change_lock, flags);
148 hrtimer_cancel(&core->slack_timer);
149 spin_unlock_irqrestore(&core->idle_state_change_lock, flags);
150}
151
Abhijeet Dharmapurikar8d843b32012-09-13 18:33:20 -0700152static void force_start_slack_timer(struct dcvs_core *core, int slack_us)
153{
154 unsigned long flags;
155 int ret;
156
157 spin_lock_irqsave(&core->idle_state_change_lock, flags);
158
159 /*
160 * only start the timer if governor is not stopped
161 */
162 if (slack_us != 0) {
163 ret = hrtimer_start(&core->slack_timer,
164 ktime_set(0, slack_us * 1000),
165 HRTIMER_MODE_REL_PINNED);
166 if (ret) {
167 pr_err("%s Failed to start timer ret = %d\n",
168 core->core_name, ret);
169 }
170 }
171
172 spin_unlock_irqrestore(&core->idle_state_change_lock, flags);
173}
174
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700175static void stop_slack_timer(struct dcvs_core *core)
176{
177 unsigned long flags;
178
179 spin_lock_irqsave(&core->idle_state_change_lock, flags);
180 /* err only for cpu type's GPU's can do idle exit consecutively */
181 if (core->idle_entered == 1 && !(core->dcvs_core_id >= GPU_OFFSET))
182 __err("%s trying to reenter idle", core->core_name);
183 core->idle_entered = 1;
184 hrtimer_cancel(&core->slack_timer);
185 core->idle_entered = 1;
186 spin_unlock_irqrestore(&core->idle_state_change_lock, flags);
187}
188
189static void start_slack_timer(struct dcvs_core *core, int slack_us)
190{
191 unsigned long flags1, flags2;
192 int ret;
193
194 spin_lock_irqsave(&core->idle_state_change_lock, flags2);
195
196 spin_lock_irqsave(&core->pending_freq_lock, flags1);
197
198 /* err only for cpu type's GPU's can do idle enter consecutively */
199 if (core->idle_entered == 0 && !(core->dcvs_core_id >= GPU_OFFSET))
200 __err("%s trying to reexit idle", core->core_name);
201 core->idle_entered = 0;
202 /*
203 * only start the timer if governor is not stopped
204 */
205 if (slack_us != 0
206 && !(core->pending_freq < NO_OUTSTANDING_FREQ_CHANGE)) {
207 ret = hrtimer_start(&core->slack_timer,
208 ktime_set(0, slack_us * 1000),
209 HRTIMER_MODE_REL_PINNED);
210 if (ret) {
211 pr_err("%s Failed to start timer ret = %d\n",
212 core->core_name, ret);
213 }
214 }
215 spin_unlock_irqrestore(&core->pending_freq_lock, flags1);
216
217 spin_unlock_irqrestore(&core->idle_state_change_lock, flags2);
218}
219
220static void restart_slack_timer(struct dcvs_core *core, int slack_us)
221{
222 unsigned long flags1, flags2;
223 int ret;
224
225 spin_lock_irqsave(&core->idle_state_change_lock, flags2);
226
227 hrtimer_cancel(&core->slack_timer);
228
229 spin_lock_irqsave(&core->pending_freq_lock, flags1);
230
231 /*
232 * only start the timer if idle is not entered
233 * and governor is not stopped
234 */
235 if (slack_us != 0 && (core->idle_entered != 1)
236 && !(core->pending_freq < NO_OUTSTANDING_FREQ_CHANGE)) {
237 ret = hrtimer_start(&core->slack_timer,
238 ktime_set(0, slack_us * 1000),
239 HRTIMER_MODE_REL_PINNED);
240 if (ret) {
241 pr_err("%s Failed to start timer ret = %d\n",
242 core->core_name, ret);
243 }
244 }
245 spin_unlock_irqrestore(&core->pending_freq_lock, flags1);
246 spin_unlock_irqrestore(&core->idle_state_change_lock, flags2);
247}
248
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700249static int __msm_dcvs_change_freq(struct dcvs_core *core)
250{
251 int ret = 0;
252 unsigned long flags = 0;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700253 int requested_freq = 0;
254 ktime_t time_start;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700255 uint32_t slack_us = 0;
256 uint32_t ret1 = 0;
257
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700258 spin_lock_irqsave(&core->pending_freq_lock, flags);
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700259repeat:
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700260 BUG_ON(!core->pending_freq);
261 if (core->pending_freq == STOP_FREQ_CHANGE)
262 BUG();
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700263
264 requested_freq = core->pending_freq;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700265 time_start = core->time_start;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700266 core->time_start = ns_to_ktime(0);
267
268 if (requested_freq < 0) {
269 requested_freq = -1 * requested_freq;
270 core->pending_freq = STOP_FREQ_CHANGE;
271 } else {
272 core->pending_freq = NO_OUTSTANDING_FREQ_CHANGE;
273 }
274
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700275 if (requested_freq == core->actual_freq)
276 goto out;
277
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700278 spin_unlock_irqrestore(&core->pending_freq_lock, flags);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700279
280 /**
281 * Call the frequency sink driver to change the frequency
282 * We will need to get back the actual frequency in KHz and
283 * the record the time taken to change it.
284 */
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700285 ret = core->set_frequency(core->type_core_num, requested_freq);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700286 if (ret <= 0)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700287 __err("Core %s failed to set freq %u\n",
288 core->core_name, requested_freq);
Eugene Seah76af9832012-03-28 18:43:53 -0600289 /* continue to call TZ to get updated slack timer */
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700290 else
Eugene Seah76af9832012-03-28 18:43:53 -0600291 core->actual_freq = ret;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700292
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700293 core->freq_change_us = (uint32_t)ktime_to_us(
294 ktime_sub(ktime_get(), time_start));
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700295
296 /**
297 * Disable low power modes if the actual frequency is >
298 * disable_pc_threshold.
299 */
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700300 if (core->actual_freq > core->algo_param.disable_pc_threshold) {
301 core->idle_enable(core->type_core_num,
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700302 MSM_DCVS_DISABLE_HIGH_LATENCY_MODES);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700303 } else if (core->actual_freq <= core->algo_param.disable_pc_threshold) {
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700304 core->idle_enable(core->type_core_num,
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700305 MSM_DCVS_ENABLE_HIGH_LATENCY_MODES);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700306 }
307
308 /**
309 * Update algorithm with new freq and time taken to change
310 * to this frequency and that will get us the new slack
311 * timer
312 */
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700313 ret = msm_dcvs_scm_event(core->dcvs_core_id,
314 MSM_DCVS_SCM_CLOCK_FREQ_UPDATE,
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700315 core->actual_freq, core->freq_change_us,
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700316 &slack_us, &ret1);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700317 if (ret) {
318 __err("Error sending core (%s) dcvs_core_id = %d freq change (%u) reqfreq = %d slack_us=%d ret = %d\n",
319 core->core_name, core->dcvs_core_id,
320 core->actual_freq, requested_freq,
321 slack_us, ret);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700322 }
323
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700324 /* TODO confirm that we get a valid freq from SM even when the above
325 * FREQ_UPDATE fails
326 */
327 restart_slack_timer(core, slack_us);
328 spin_lock_irqsave(&core->pending_freq_lock, flags);
329
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700330 /**
331 * By the time we are done with freq changes, we could be asked to
332 * change again. Check before exiting.
333 */
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700334 if (core->pending_freq != NO_OUTSTANDING_FREQ_CHANGE
335 && core->pending_freq != STOP_FREQ_CHANGE) {
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700336 goto repeat;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700337 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700338
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700339out: /* should always be jumped to with the spin_lock held */
340 spin_unlock_irqrestore(&core->pending_freq_lock, flags);
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700341
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700342 return ret;
343}
344
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700345static void msm_dcvs_report_temp_work(struct work_struct *work)
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700346{
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700347 struct dcvs_core *core = container_of(work,
348 struct dcvs_core,
349 temperature_work.work);
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700350 struct msm_dcvs_core_info *info = core->info;
351 struct tsens_device tsens_dev;
352 int ret;
353 unsigned long temp = 0;
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700354 int interval_ms;
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700355
356 tsens_dev.sensor_num = core->sensor;
357 ret = tsens_get_temp(&tsens_dev, &temp);
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700358 if (!temp) {
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700359 tsens_dev.sensor_num = 0;
360 ret = tsens_get_temp(&tsens_dev, &temp);
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700361 if (!temp)
362 goto out;
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700363 }
364
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700365 if (temp == info->power_param.current_temp)
366 goto out;
367 info->power_param.current_temp = temp;
368
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700369 ret = msm_dcvs_scm_set_power_params(core->dcvs_core_id,
370 &info->power_param,
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700371 &info->freq_tbl[0], &core->coeffs);
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700372out:
373 if (info->thermal_poll_ms == 0)
374 interval_ms = 60000;
375 else if (info->thermal_poll_ms < 1000)
376 interval_ms = 1000;
377 else
378 interval_ms = info->thermal_poll_ms;
379
380 schedule_delayed_work(&core->temperature_work,
381 msecs_to_jiffies(interval_ms));
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700382}
383
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700384static int msm_dcvs_do_freq(void *data)
385{
386 struct dcvs_core *core = (struct dcvs_core *)data;
387 static struct sched_param param = {.sched_priority = MAX_RT_PRIO - 1};
388
389 sched_setscheduler(current, SCHED_FIFO, &param);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700390
391 while (!kthread_should_stop()) {
Abhijeet Dharmapurikarbbb52fe2012-08-31 20:31:16 -0700392 wait_event(core->wait_q, !(core->pending_freq == 0 ||
393 core->pending_freq == -1) ||
394 kthread_should_stop());
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700395
396 if (kthread_should_stop())
397 break;
398
Abhijeet Dharmapurikarbbb52fe2012-08-31 20:31:16 -0700399 __msm_dcvs_change_freq(core);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700400 }
401
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700402 return 0;
403}
404
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700405/* freq_pending_lock should be held */
406static void request_freq_change(struct dcvs_core *core, int new_freq)
407{
408 if (new_freq == NO_OUTSTANDING_FREQ_CHANGE) {
409 if (core->pending_freq != STOP_FREQ_CHANGE) {
410 __err("%s gov started with earlier pending freq %d\n",
411 core->core_name, core->pending_freq);
412 }
413 core->pending_freq = NO_OUTSTANDING_FREQ_CHANGE;
414 return;
415 }
416
417 if (new_freq == STOP_FREQ_CHANGE) {
418 if (core->pending_freq == NO_OUTSTANDING_FREQ_CHANGE)
419 core->pending_freq = STOP_FREQ_CHANGE;
420 else if (core->pending_freq > 0)
421 core->pending_freq = -1 * core->pending_freq;
422 return;
423 }
424
425 if (core->pending_freq < 0) {
426 /* a value less than 0 means that the governor has stopped
427 * and no more freq changes should be requested
428 */
429 return;
430 }
431
432 if (core->actual_freq != new_freq && core->pending_freq != new_freq) {
433 core->pending_freq = new_freq;
434 core->time_start = ktime_get();
435 wake_up(&core->wait_q);
436 }
437}
438
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700439static int msm_dcvs_update_freq(struct dcvs_core *core,
Eugene Seah76af9832012-03-28 18:43:53 -0600440 enum msm_dcvs_scm_event event, uint32_t param0,
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700441 uint32_t *ret1)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700442{
443 int ret = 0;
444 unsigned long flags = 0;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700445 uint32_t new_freq = -EINVAL;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700446
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700447 spin_lock_irqsave(&core->pending_freq_lock, flags);
448
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700449 ret = msm_dcvs_scm_event(core->dcvs_core_id, event, param0,
Eugene Seah76af9832012-03-28 18:43:53 -0600450 core->actual_freq, &new_freq, ret1);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700451 if (ret) {
Jeff Ohlstein4aa4a2b2012-06-28 19:03:49 -0700452 if (ret == -13)
453 ret = 0;
454 else
455 __err("Error (%d) sending SCM event %d for core %s\n",
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700456 ret, event, core->core_name);
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700457 goto out;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700458 }
459
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700460 if (new_freq == 0) {
461 /*
462 * sometimes TZ gives us a 0 freq back,
463 * do not queue up a request
464 */
465 goto out;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700466 }
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700467
468 request_freq_change(core, new_freq);
469
Abhijeet Dharmapurikar584187d2012-08-31 19:36:08 -0700470out:
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700471 spin_unlock_irqrestore(&core->pending_freq_lock, flags);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700472
473 return ret;
474}
475
476static enum hrtimer_restart msm_dcvs_core_slack_timer(struct hrtimer *timer)
477{
478 int ret = 0;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700479 struct dcvs_core *core = container_of(timer,
480 struct dcvs_core, slack_timer);
Eugene Seah76af9832012-03-28 18:43:53 -0600481 uint32_t ret1;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700482
Abhijeet Dharmapurikar07cf2ff2012-09-13 19:05:13 -0700483 trace_printk("dcvs: Slack timer fired for core=%s\n", core->core_name);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700484 /**
485 * Timer expired, notify TZ
486 * Dont care about the third arg.
487 */
Eugene Seah76af9832012-03-28 18:43:53 -0600488 ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_QOS_TIMER_EXPIRED, 0,
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700489 &ret1);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700490 if (ret)
491 __err("Timer expired for core %s but failed to notify.\n",
492 core->core_name);
493
494 return HRTIMER_NORESTART;
495}
496
497/* Helper functions and macros for sysfs nodes for a core */
498#define CORE_FROM_ATTRIBS(attr, name) \
499 container_of(container_of(attr, struct core_attribs, name), \
500 struct dcvs_core, attrib);
501
502#define DCVS_PARAM_SHOW(_name, v) \
503static ssize_t msm_dcvs_attr_##_name##_show(struct kobject *kobj, \
504 struct kobj_attribute *attr, char *buf) \
505{ \
506 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
507 return snprintf(buf, PAGE_SIZE, "%d\n", v); \
508}
509
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700510#define DCVS_PARAM_STORE(_name) \
511static ssize_t msm_dcvs_attr_##_name##_show(struct kobject *kobj,\
512 struct kobj_attribute *attr, char *buf) \
513{ \
514 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
515 return snprintf(buf, PAGE_SIZE, "%d\n", core->info->_name); \
516} \
517static ssize_t msm_dcvs_attr_##_name##_store(struct kobject *kobj, \
518 struct kobj_attribute *attr, const char *buf, size_t count) \
519{ \
520 int ret = 0; \
521 uint32_t val = 0; \
522 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
523 ret = kstrtouint(buf, 10, &val); \
524 if (ret) { \
525 __err("Invalid input %s for %s\n", buf, __stringify(_name));\
526 } else { \
527 core->info->_name = val; \
528 } \
529 return count; \
530}
531
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700532#define DCVS_ALGO_PARAM(_name) \
533static ssize_t msm_dcvs_attr_##_name##_show(struct kobject *kobj,\
534 struct kobj_attribute *attr, char *buf) \
535{ \
536 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
537 return snprintf(buf, PAGE_SIZE, "%d\n", core->algo_param._name); \
538} \
539static ssize_t msm_dcvs_attr_##_name##_store(struct kobject *kobj, \
540 struct kobj_attribute *attr, const char *buf, size_t count) \
541{ \
542 int ret = 0; \
543 uint32_t val = 0; \
544 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700545 ret = kstrtouint(buf, 10, &val); \
546 if (ret) { \
547 __err("Invalid input %s for %s\n", buf, __stringify(_name));\
548 } else { \
549 uint32_t old_val = core->algo_param._name; \
550 core->algo_param._name = val; \
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700551 ret = msm_dcvs_scm_set_algo_params(core->dcvs_core_id, \
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700552 &core->algo_param); \
553 if (ret) { \
554 core->algo_param._name = old_val; \
555 __err("Error(%d) in setting %d for algo param %s\n",\
556 ret, val, __stringify(_name)); \
557 } \
558 } \
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700559 return count; \
560}
561
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700562#define DCVS_ENERGY_PARAM(_name) \
563static ssize_t msm_dcvs_attr_##_name##_show(struct kobject *kobj,\
564 struct kobj_attribute *attr, char *buf) \
565{ \
566 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
567 return snprintf(buf, PAGE_SIZE, "%d\n", core->coeffs._name); \
568} \
569static ssize_t msm_dcvs_attr_##_name##_store(struct kobject *kobj, \
570 struct kobj_attribute *attr, const char *buf, size_t count) \
571{ \
572 int ret = 0; \
573 int32_t val = 0; \
574 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700575 ret = kstrtoint(buf, 10, &val); \
576 if (ret) { \
577 __err("Invalid input %s for %s\n", buf, __stringify(_name));\
578 } else { \
579 int32_t old_val = core->coeffs._name; \
580 core->coeffs._name = val; \
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700581 ret = msm_dcvs_scm_set_power_params(core->dcvs_core_id, \
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700582 &core->info->power_param, &core->info->freq_tbl[0], \
583 &core->coeffs); \
584 if (ret) { \
585 core->coeffs._name = old_val; \
586 __err("Error(%d) in setting %d for coeffs param %s\n",\
587 ret, val, __stringify(_name)); \
588 } \
589 } \
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700590 return count; \
591}
592
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700593#define DCVS_RO_ATTRIB(i, _name) \
594 core->attrib._name.attr.name = __stringify(_name); \
595 core->attrib._name.attr.mode = S_IRUGO; \
596 core->attrib._name.show = msm_dcvs_attr_##_name##_show; \
597 core->attrib._name.store = NULL; \
598 core->attrib.attrib_group.attrs[i] = &core->attrib._name.attr;
599
600#define DCVS_RW_ATTRIB(i, _name) \
601 core->attrib._name.attr.name = __stringify(_name); \
602 core->attrib._name.attr.mode = S_IRUGO | S_IWUSR; \
603 core->attrib._name.show = msm_dcvs_attr_##_name##_show; \
604 core->attrib._name.store = msm_dcvs_attr_##_name##_store; \
605 core->attrib.attrib_group.attrs[i] = &core->attrib._name.attr;
606
607/**
608 * Function declarations for different attributes.
609 * Gets used when setting the attribute show and store parameters.
610 */
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700611DCVS_PARAM_SHOW(freq_change_us, (core->freq_change_us))
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700612
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700613DCVS_ALGO_PARAM(disable_pc_threshold)
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700614DCVS_ALGO_PARAM(em_win_size_min_us)
615DCVS_ALGO_PARAM(em_win_size_max_us)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700616DCVS_ALGO_PARAM(em_max_util_pct)
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700617DCVS_ALGO_PARAM(group_id)
618DCVS_ALGO_PARAM(max_freq_chg_time_us)
619DCVS_ALGO_PARAM(slack_mode_dynamic)
620DCVS_ALGO_PARAM(slack_time_min_us)
621DCVS_ALGO_PARAM(slack_time_max_us)
622DCVS_ALGO_PARAM(slack_weight_thresh_pct)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700623DCVS_ALGO_PARAM(ss_iobusy_conv)
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700624DCVS_ALGO_PARAM(ss_win_size_min_us)
625DCVS_ALGO_PARAM(ss_win_size_max_us)
626DCVS_ALGO_PARAM(ss_util_pct)
627
628DCVS_ENERGY_PARAM(active_coeff_a)
629DCVS_ENERGY_PARAM(active_coeff_b)
630DCVS_ENERGY_PARAM(active_coeff_c)
631DCVS_ENERGY_PARAM(leakage_coeff_a)
632DCVS_ENERGY_PARAM(leakage_coeff_b)
633DCVS_ENERGY_PARAM(leakage_coeff_c)
634DCVS_ENERGY_PARAM(leakage_coeff_d)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700635
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700636DCVS_PARAM_STORE(thermal_poll_ms)
637
Steve Muckle118f47b2012-10-17 16:09:37 -0700638static ssize_t msm_dcvs_attr_freq_tbl_show(struct kobject *kobj,
639 struct kobj_attribute *attr,
640 char *buf)
641{
642 struct msm_dcvs_freq_entry *freq_tbl;
643 char *buf_idx = buf;
644 int i, len;
645 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, freq_tbl);
646
647 freq_tbl = core->info->freq_tbl;
648 *buf_idx = '\0';
649
650 /* limit the number of frequencies we will print into
651 * the PAGE_SIZE sysfs show buffer. */
652 if (core->info->power_param.num_freq > 64)
653 return 0;
654
655 for (i = 0; i < core->info->power_param.num_freq; i++) {
656 if (freq_tbl[i].is_trans_level) {
657 len = snprintf(buf_idx, 10, "%7d ", freq_tbl[i].freq);
658 /* buf_idx always points at terminating null */
659 buf_idx += len;
660 }
661 }
662 /* overwrite final trailing space with newline */
663 if (buf_idx > buf)
664 *(buf_idx - 1) = '\n';
665
666 return buf_idx - buf;
667}
668
669static ssize_t msm_dcvs_attr_freq_tbl_store(struct kobject *kobj,
670 struct kobj_attribute *attr,
671 const char *buf,
672 size_t count)
673{
674 struct msm_dcvs_freq_entry *freq_tbl;
675 uint32_t freq;
676 int i, ret;
677 struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, freq_tbl);
678
679 freq_tbl = core->info->freq_tbl;
680
681 ret = kstrtouint(buf, 10, &freq);
682 if (ret) {
683 __err("Invalid input %s for freq_tbl\n", buf);
684 return count;
685 }
686
687 for (i = 0; i < core->info->power_param.num_freq; i++)
688 if (freq_tbl[i].freq == freq) {
689 freq_tbl[i].is_trans_level ^= 1;
690 break;
691 }
692
693 if (i >= core->info->power_param.num_freq) {
694 __err("Invalid frequency for freq_tbl: %d\n", freq);
695 return count;
696 }
697
698 ret = msm_dcvs_scm_set_power_params(core->dcvs_core_id,
699 &core->info->power_param,
700 &core->info->freq_tbl[0],
701 &core->coeffs);
702 if (ret) {
703 freq_tbl[i].is_trans_level ^= 1;
704 __err("Error %d in toggling freq %d (orig enable val %d)\n",
705 ret, freq_tbl[i].freq, freq_tbl[i].is_trans_level);
706 }
707 return count;
708}
709
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700710static int msm_dcvs_setup_core_sysfs(struct dcvs_core *core)
711{
712 int ret = 0;
713 struct kobject *core_kobj = NULL;
Steve Muckle118f47b2012-10-17 16:09:37 -0700714 const int attr_count = 25;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700715
716 BUG_ON(!cores_kobj);
717
718 core->attrib.attrib_group.attrs =
719 kzalloc(attr_count * sizeof(struct attribute *), GFP_KERNEL);
720
721 if (!core->attrib.attrib_group.attrs) {
722 ret = -ENOMEM;
723 goto done;
724 }
725
Abhijeet Dharmapurikar35876382012-09-13 16:09:51 -0700726 DCVS_RO_ATTRIB(0, freq_change_us);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700727
Abhijeet Dharmapurikar35876382012-09-13 16:09:51 -0700728 DCVS_RW_ATTRIB(1, disable_pc_threshold);
729 DCVS_RW_ATTRIB(2, em_win_size_min_us);
730 DCVS_RW_ATTRIB(3, em_win_size_max_us);
731 DCVS_RW_ATTRIB(4, em_max_util_pct);
732 DCVS_RW_ATTRIB(5, group_id);
733 DCVS_RW_ATTRIB(6, max_freq_chg_time_us);
734 DCVS_RW_ATTRIB(7, slack_mode_dynamic);
735 DCVS_RW_ATTRIB(8, slack_weight_thresh_pct);
736 DCVS_RW_ATTRIB(9, slack_time_min_us);
737 DCVS_RW_ATTRIB(10, slack_time_max_us);
738 DCVS_RW_ATTRIB(11, ss_iobusy_conv);
739 DCVS_RW_ATTRIB(12, ss_win_size_min_us);
740 DCVS_RW_ATTRIB(13, ss_win_size_max_us);
741 DCVS_RW_ATTRIB(14, ss_util_pct);
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700742
Abhijeet Dharmapurikar35876382012-09-13 16:09:51 -0700743 DCVS_RW_ATTRIB(15, active_coeff_a);
744 DCVS_RW_ATTRIB(16, active_coeff_b);
745 DCVS_RW_ATTRIB(17, active_coeff_c);
746 DCVS_RW_ATTRIB(18, leakage_coeff_a);
747 DCVS_RW_ATTRIB(19, leakage_coeff_b);
748 DCVS_RW_ATTRIB(20, leakage_coeff_c);
749 DCVS_RW_ATTRIB(21, leakage_coeff_d);
750 DCVS_RW_ATTRIB(22, thermal_poll_ms);
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700751
Steve Muckle118f47b2012-10-17 16:09:37 -0700752 DCVS_RW_ATTRIB(23, freq_tbl);
753
754 core->attrib.attrib_group.attrs[24] = NULL;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700755
756 core_kobj = kobject_create_and_add(core->core_name, cores_kobj);
757 if (!core_kobj) {
758 ret = -ENOMEM;
759 goto done;
760 }
761
762 ret = sysfs_create_group(core_kobj, &core->attrib.attrib_group);
763 if (ret)
764 __err("Cannot create core %s attr group\n", core->core_name);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700765
766done:
767 if (ret) {
768 kfree(core->attrib.attrib_group.attrs);
769 kobject_del(core_kobj);
770 }
771
772 return ret;
773}
774
Steve Muckle5e5a60d2012-10-09 13:25:22 -0700775static int get_core_offset(enum msm_dcvs_core_type type, int num)
776{
777 int offset = -EINVAL;
778
779 switch (type) {
780 case MSM_DCVS_CORE_TYPE_CPU:
781 offset = CPU_OFFSET + num;
782 BUG_ON(offset >= GPU_OFFSET);
783 break;
784 case MSM_DCVS_CORE_TYPE_GPU:
785 offset = GPU_OFFSET + num;
786 BUG_ON(offset >= CORES_MAX);
787 break;
788 default:
789 BUG();
790 }
791
792 return offset;
793}
794
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700795/* Return the core and initialize non platform data specific numbers in it */
796static struct dcvs_core *msm_dcvs_add_core(enum msm_dcvs_core_type type,
797 int num)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700798{
799 struct dcvs_core *core = NULL;
800 int i;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700801 char name[CORE_NAME_MAX];
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700802
Steve Muckle5e5a60d2012-10-09 13:25:22 -0700803 i = get_core_offset(type, num);
804 if (i < 0)
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700805 return NULL;
Steve Muckle5e5a60d2012-10-09 13:25:22 -0700806
807 if (type == MSM_DCVS_CORE_TYPE_CPU)
808 snprintf(name, CORE_NAME_MAX, "cpu%d", num);
809 else
810 snprintf(name, CORE_NAME_MAX, "gpu%d", num);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700811
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700812 core = &core_list[i];
813 core->dcvs_core_id = i;
Abhijeet Dharmapurikar2ebc0fe2012-09-12 14:05:13 -0700814 strlcpy(core->core_name, name, CORE_NAME_MAX);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700815 spin_lock_init(&core->pending_freq_lock);
816 spin_lock_init(&core->idle_state_change_lock);
817 hrtimer_init(&core->slack_timer,
818 CLOCK_MONOTONIC, HRTIMER_MODE_REL_PINNED);
819 core->slack_timer.function = msm_dcvs_core_slack_timer;
Abhijeet Dharmapurikar2ebc0fe2012-09-12 14:05:13 -0700820 return core;
821}
822
823/* Return the core if found or add to list if @add_to_list is true */
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700824static struct dcvs_core *msm_dcvs_get_core(int offset)
Abhijeet Dharmapurikar2ebc0fe2012-09-12 14:05:13 -0700825{
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700826 /* if the handle is still not set bug */
827 BUG_ON(core_list[offset].dcvs_core_id == -1);
828 return &core_list[offset];
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700829}
830
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700831
832int msm_dcvs_register_core(
833 enum msm_dcvs_core_type type,
834 int type_core_num,
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700835 struct msm_dcvs_core_info *info,
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700836 int (*set_frequency)(int type_core_num, unsigned int freq),
837 unsigned int (*get_frequency)(int type_core_num),
838 int (*idle_enable)(int type_core_num,
Abhijeet Dharmapurikar6e9b34f2012-09-10 16:03:39 -0700839 enum msm_core_control_event event),
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700840 int sensor)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700841{
842 int ret = -EINVAL;
Steve Muckle5e5a60d2012-10-09 13:25:22 -0700843 int offset;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700844 struct dcvs_core *core = NULL;
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700845 uint32_t ret1;
846 uint32_t ret2;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700847
Steve Muckle5e5a60d2012-10-09 13:25:22 -0700848 offset = get_core_offset(type, type_core_num);
849 if (offset < 0)
850 return ret;
851 if (core_list[offset].dcvs_core_id != -1)
852 return core_list[offset].dcvs_core_id;
853
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700854 core = msm_dcvs_add_core(type, type_core_num);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700855 if (!core)
856 return ret;
857
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700858 core->type = type;
859 core->type_core_num = type_core_num;
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700860 core->set_frequency = set_frequency;
861 core->get_frequency = get_frequency;
Abhijeet Dharmapurikar6e9b34f2012-09-10 16:03:39 -0700862 core->idle_enable = idle_enable;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700863 core->pending_freq = STOP_FREQ_CHANGE;
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700864
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700865 core->info = info;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700866 memcpy(&core->algo_param, &info->algo_param,
867 sizeof(struct msm_dcvs_algo_param));
868
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700869 memcpy(&core->coeffs, &info->energy_coeffs,
870 sizeof(struct msm_dcvs_energy_curve_coeffs));
871
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700872 /*
873 * The tz expects cpu0 to represent bit 0 in the mask, however the
874 * dcvs_core_id needs to start from 1, dcvs_core_id = 0 is used to
875 * indicate that this request is not associated with any core.
876 * mpdecision
877 */
878 info->core_param.core_bitmask_id
879 = 1 << (core->dcvs_core_id - CPU_OFFSET);
Abhijeet Dharmapurikarb6c05772012-08-26 18:27:53 -0700880 core->sensor = sensor;
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700881
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700882 ret = msm_dcvs_scm_register_core(core->dcvs_core_id, &info->core_param);
883 if (ret) {
884 __err("%s: scm register core fail handle = %d ret = %d\n",
885 __func__, core->dcvs_core_id, ret);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700886 goto bail;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700887 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700888
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700889 ret = msm_dcvs_scm_set_algo_params(core->dcvs_core_id,
890 &info->algo_param);
891 if (ret) {
892 __err("%s: scm algo params failed ret = %d\n", __func__, ret);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700893 goto bail;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700894 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700895
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700896 ret = msm_dcvs_scm_set_power_params(core->dcvs_core_id,
897 &info->power_param,
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700898 &info->freq_tbl[0], &core->coeffs);
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700899 if (ret) {
900 __err("%s: scm power params failed ret = %d\n", __func__, ret);
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700901 goto bail;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700902 }
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700903
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700904 ret = msm_dcvs_scm_event(core->dcvs_core_id, MSM_DCVS_SCM_CORE_ONLINE,
Abhijeet Dharmapurikar7e37e6e2012-08-23 18:58:44 -0700905 core->actual_freq, 0, &ret1, &ret2);
906 if (ret)
907 goto bail;
908
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700909 ret = msm_dcvs_setup_core_sysfs(core);
910 if (ret) {
911 __err("Unable to setup core %s sysfs\n", core->core_name);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700912 goto bail;
913 }
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700914 core->idle_entered = -1;
Abhijeet Dharmapurikarbbb52fe2012-08-31 20:31:16 -0700915 init_waitqueue_head(&core->wait_q);
916 core->task = kthread_run(msm_dcvs_do_freq, (void *)core,
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700917 "msm_dcvs/%d", core->dcvs_core_id);
918 ret = core->dcvs_core_id;
Abhijeet Dharmapurikar19cf4742012-09-13 11:11:54 -0700919
920 INIT_DELAYED_WORK(&core->temperature_work, msm_dcvs_report_temp_work);
921 schedule_delayed_work(&core->temperature_work,
922 msecs_to_jiffies(info->thermal_poll_ms));
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700923 return ret;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700924bail:
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700925 core->dcvs_core_id = -1;
926 return -EINVAL;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700927}
928EXPORT_SYMBOL(msm_dcvs_register_core);
929
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700930void msm_dcvs_update_limits(int dcvs_core_id)
Abhijeet Dharmapurikarc43f0db2012-08-31 20:42:53 -0700931{
932 struct dcvs_core *core;
933
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700934 if (dcvs_core_id < CPU_OFFSET || dcvs_core_id > CORES_MAX) {
935 __err("%s invalid dcvs_core_id = %d returning -EINVAL\n",
936 __func__, dcvs_core_id);
Abhijeet Dharmapurikarc43f0db2012-08-31 20:42:53 -0700937 return;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700938 }
Abhijeet Dharmapurikarc43f0db2012-08-31 20:42:53 -0700939
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700940 core = msm_dcvs_get_core(dcvs_core_id);
941 core->actual_freq = core->get_frequency(core->type_core_num);
Abhijeet Dharmapurikarc43f0db2012-08-31 20:42:53 -0700942}
943
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700944int msm_dcvs_freq_sink_start(int dcvs_core_id)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700945{
946 int ret = -EINVAL;
947 struct dcvs_core *core = NULL;
Eugene Seah76af9832012-03-28 18:43:53 -0600948 uint32_t ret1;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700949 unsigned long flags;
Abhijeet Dharmapurikar8d843b32012-09-13 18:33:20 -0700950 int new_freq;
951 int timer_interval_us;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700952
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700953 if (dcvs_core_id < CPU_OFFSET || dcvs_core_id > CORES_MAX) {
954 __err("%s invalid dcvs_core_id = %d returning -EINVAL\n",
955 __func__, dcvs_core_id);
956 return -EINVAL;
957 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700958
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700959 core = msm_dcvs_get_core(dcvs_core_id);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700960 if (!core)
961 return ret;
962
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700963 core->actual_freq = core->get_frequency(core->type_core_num);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700964
965 spin_lock_irqsave(&core->pending_freq_lock, flags);
966 /* mark that we are ready to accept new frequencies */
967 request_freq_change(core, NO_OUTSTANDING_FREQ_CHANGE);
968 spin_unlock_irqrestore(&core->pending_freq_lock, flags);
969
970 spin_lock_irqsave(&core->idle_state_change_lock, flags);
971 core->idle_entered = -1;
972 spin_unlock_irqrestore(&core->idle_state_change_lock, flags);
973
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700974 /* Notify TZ to start receiving idle info for the core */
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700975 ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_DCVS_ENABLE, 1, &ret1);
976
Abhijeet Dharmapurikar8d843b32012-09-13 18:33:20 -0700977 ret = msm_dcvs_scm_event(
978 core->dcvs_core_id, MSM_DCVS_SCM_CORE_ONLINE, core->actual_freq,
979 0, &new_freq, &timer_interval_us);
980 if (ret)
981 __err("Error (%d) DCVS sending online for %s\n",
982 ret, core->core_name);
983
984 if (new_freq != 0) {
985 spin_lock_irqsave(&core->pending_freq_lock, flags);
986 request_freq_change(core, new_freq);
987 spin_unlock_irqrestore(&core->pending_freq_lock, flags);
988 }
989 force_start_slack_timer(core, timer_interval_us);
990
991
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700992 core->idle_enable(core->type_core_num, MSM_DCVS_ENABLE_IDLE_PULSE);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -0700993 return 0;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700994}
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -0700995EXPORT_SYMBOL(msm_dcvs_freq_sink_start);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700996
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -0700997int msm_dcvs_freq_sink_stop(int dcvs_core_id)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -0700998{
999 int ret = -EINVAL;
1000 struct dcvs_core *core = NULL;
Eugene Seah76af9832012-03-28 18:43:53 -06001001 uint32_t ret1;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001002 uint32_t freq;
1003 unsigned long flags;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001004
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001005 if (dcvs_core_id < 0 || dcvs_core_id > CORES_MAX) {
1006 pr_err("%s invalid dcvs_core_id = %d returning -EINVAL\n",
1007 __func__, dcvs_core_id);
1008 return -EINVAL;
1009 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001010
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001011 core = msm_dcvs_get_core(dcvs_core_id);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001012 if (!core) {
1013 __err("couldn't find core for coreid = %d\n", dcvs_core_id);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001014 return ret;
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001015 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001016
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001017 core->idle_enable(core->type_core_num, MSM_DCVS_DISABLE_IDLE_PULSE);
1018 /* Notify TZ to stop receiving idle info for the core */
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001019 ret = msm_dcvs_scm_event(core->dcvs_core_id, MSM_DCVS_SCM_DCVS_ENABLE,
1020 0, core->actual_freq, &freq, &ret1);
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001021 core->idle_enable(core->type_core_num,
1022 MSM_DCVS_ENABLE_HIGH_LATENCY_MODES);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001023 spin_lock_irqsave(&core->pending_freq_lock, flags);
1024 /* flush out all the pending freq changes */
1025 request_freq_change(core, STOP_FREQ_CHANGE);
1026 spin_unlock_irqrestore(&core->pending_freq_lock, flags);
1027 force_stop_slack_timer(core);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001028
1029 return 0;
1030}
Abhijeet Dharmapurikar50bcc832012-08-31 22:10:41 -07001031EXPORT_SYMBOL(msm_dcvs_freq_sink_stop);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001032
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001033int msm_dcvs_idle(int dcvs_core_id, enum msm_core_idle_state state,
1034 uint32_t iowaited)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001035{
1036 int ret = 0;
1037 struct dcvs_core *core = NULL;
Eugene Seah76af9832012-03-28 18:43:53 -06001038 uint32_t timer_interval_us = 0;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001039 uint32_t r0, r1;
1040
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001041 if (dcvs_core_id < CPU_OFFSET || dcvs_core_id > CORES_MAX) {
1042 pr_err("invalid dcvs_core_id = %d ret -EINVAL\n", dcvs_core_id);
1043 return -EINVAL;
1044 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001045
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001046 core = msm_dcvs_get_core(dcvs_core_id);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001047
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001048 switch (state) {
1049 case MSM_DCVS_IDLE_ENTER:
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001050 stop_slack_timer(core);
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001051 ret = msm_dcvs_scm_event(core->dcvs_core_id,
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001052 MSM_DCVS_SCM_IDLE_ENTER, 0, 0, &r0, &r1);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001053 if (ret < 0 && ret != -13)
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001054 __err("Error (%d) sending idle enter for %s\n",
1055 ret, core->core_name);
Abhijeet Dharmapurikar07cf2ff2012-09-13 19:05:13 -07001056 trace_msm_dcvs_idle("idle_enter_exit", core->core_name, 1);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001057 break;
1058
1059 case MSM_DCVS_IDLE_EXIT:
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001060 ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_IDLE_EXIT,
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001061 iowaited, &timer_interval_us);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001062 if (ret)
1063 __err("Error (%d) sending idle exit for %s\n",
1064 ret, core->core_name);
Abhijeet Dharmapurikar080f49d2012-09-12 18:14:01 -07001065 start_slack_timer(core, timer_interval_us);
Abhijeet Dharmapurikar07cf2ff2012-09-13 19:05:13 -07001066 trace_msm_dcvs_idle("idle_enter_exit", core->core_name, 0);
1067 trace_msm_dcvs_iowait("iowait", core->core_name, iowaited);
1068 trace_msm_dcvs_slack_time("slack_timer_dcvs", core->core_name,
1069 timer_interval_us);
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001070 break;
1071 }
1072
1073 return ret;
1074}
1075EXPORT_SYMBOL(msm_dcvs_idle);
1076
1077static int __init msm_dcvs_late_init(void)
1078{
1079 struct kobject *module_kobj = NULL;
1080 int ret = 0;
1081
1082 module_kobj = kset_find_obj(module_kset, KBUILD_MODNAME);
1083 if (!module_kobj) {
1084 pr_err("%s: cannot find kobject for module %s\n",
1085 __func__, KBUILD_MODNAME);
1086 ret = -ENOENT;
1087 goto err;
1088 }
1089
1090 cores_kobj = kobject_create_and_add("cores", module_kobj);
1091 if (!cores_kobj) {
1092 __err("Cannot create %s kobject\n", "cores");
1093 ret = -ENOMEM;
1094 goto err;
1095 }
1096
1097 debugfs_base = debugfs_create_dir("msm_dcvs", NULL);
1098 if (!debugfs_base) {
1099 __err("Cannot create debugfs base %s\n", "msm_dcvs");
1100 ret = -ENOENT;
1101 goto err;
1102 }
1103
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001104err:
1105 if (ret) {
1106 kobject_del(cores_kobj);
1107 cores_kobj = NULL;
1108 debugfs_remove(debugfs_base);
1109 }
1110
1111 return ret;
1112}
1113late_initcall(msm_dcvs_late_init);
1114
1115static int __init msm_dcvs_early_init(void)
1116{
1117 int ret = 0;
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001118 int i;
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001119
1120 if (!msm_dcvs_enabled) {
1121 __info("Not enabled (%d)\n", msm_dcvs_enabled);
1122 return 0;
1123 }
1124
Abhijeet Dharmapurikar3edb5de2012-09-13 11:02:03 -07001125
1126 /* Only need about 32kBytes for normal operation */
1127 ret = msm_dcvs_scm_init(SZ_32K);
1128 if (ret) {
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001129 __err("Unable to initialize DCVS err=%d\n", ret);
Abhijeet Dharmapurikar3edb5de2012-09-13 11:02:03 -07001130 goto done;
1131 }
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001132
Abhijeet Dharmapurikarda4e6de2012-09-12 16:40:20 -07001133 for (i = 0; i < CORES_MAX; i++)
1134 core_list[i].dcvs_core_id = -1;
Abhijeet Dharmapurikar3edb5de2012-09-13 11:02:03 -07001135done:
Praveen Chidambaramf53ef1b2011-12-06 08:27:49 -07001136 return ret;
1137}
1138postcore_initcall(msm_dcvs_early_init);