blob: 985716245415e271d5d3d729913150a8923fb0c3 [file] [log] [blame]
Mahesh Sivasubramanian208b0d12014-01-08 12:43:19 -07001/* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
Praveen Chidambaram85b7b282012-04-16 13:45:15 -06002 *
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/module.h>
15#include <linux/kernel.h>
16#include <linux/init.h>
17#include <linux/slab.h>
18#include <linux/platform_device.h>
Girish Mahadevanc45b4c72013-04-24 14:07:11 -060019#include <linux/mutex.h>
20#include <linux/cpu.h>
Praveen Chidambaram85b7b282012-04-16 13:45:15 -060021#include <linux/of.h>
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -060022#include <linux/hrtimer.h>
23#include <linux/ktime.h>
24#include <linux/tick.h>
25#include <linux/suspend.h>
26#include <linux/pm_qos.h>
27#include <linux/of_platform.h>
Praveen Chidambaram85b7b282012-04-16 13:45:15 -060028#include <mach/mpm.h>
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -060029#include <mach/cpuidle.h>
30#include <mach/event_timer.h>
Mahesh Sivasubramanian6d06e3a2012-05-16 13:41:07 -060031#include "pm.h"
Mahesh Sivasubramanian11dad772012-07-13 14:00:01 -060032#include "rpm-notifier.h"
Girish Mahadevanc45b4c72013-04-24 14:07:11 -060033#include "spm.h"
34#include "idle.h"
Mahesh Sivasubramanianfae923f2012-10-08 16:22:51 -060035
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -060036#define SCLK_HZ (32768)
37
Mahesh Sivasubramanianfae923f2012-10-08 16:22:51 -060038enum {
39 MSM_LPM_LVL_DBG_SUSPEND_LIMITS = BIT(0),
40 MSM_LPM_LVL_DBG_IDLE_LIMITS = BIT(1),
41};
42
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -060043struct power_params {
Girish Mahadevanc45b4c72013-04-24 14:07:11 -060044 uint32_t latency_us;
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -060045 uint32_t ss_power;
Girish Mahadevanc45b4c72013-04-24 14:07:11 -060046 uint32_t energy_overhead;
47 uint32_t time_overhead_us;
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -060048 uint32_t target_residency_us;
Girish Mahadevanc45b4c72013-04-24 14:07:11 -060049};
50
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -060051struct lpm_cpu_level {
52 const char *name;
53 enum msm_pm_sleep_mode mode;
54 struct power_params pwr;
55 bool use_bc_timer;
56 bool sync;
57};
58
59struct lpm_system_level {
60 const char *name;
61 uint32_t l2_mode;
62 struct power_params pwr;
Mahesh Sivasubramanian65c62362013-05-08 14:30:06 -060063 enum msm_pm_sleep_mode min_cpu_mode;
64 int num_cpu_votes;
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -060065 bool notify_rpm;
66 bool available;
67 bool sync;
68};
69
70struct lpm_system_state {
71 struct lpm_cpu_level *cpu_level;
72 int num_cpu_levels;
73 struct lpm_system_level *system_level;
74 int num_system_levels;
Mahesh Sivasubramanian65c62362013-05-08 14:30:06 -060075 enum msm_pm_sleep_mode sync_cpu_mode;
76 int last_entered_cluster_index;
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -060077 bool allow_synched_levels;
78 bool no_l2_saw;
Mahesh Sivasubramanian65c62362013-05-08 14:30:06 -060079 struct spinlock sync_lock;
80 int num_cores_in_sync;
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -060081};
82
83static struct lpm_system_state sys_state;
84static bool suspend_in_progress;
85
Girish Mahadevanc45b4c72013-04-24 14:07:11 -060086struct lpm_lookup_table {
87 uint32_t modes;
88 const char *mode_name;
89};
90
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -060091static void lpm_system_level_update(void);
92static void setup_broadcast_timer(void *arg);
93static int lpm_cpu_callback(struct notifier_block *cpu_nb,
Girish Mahadevanc45b4c72013-04-24 14:07:11 -060094 unsigned long action, void *hcpu);
95
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -060096static struct notifier_block __refdata lpm_cpu_nblk = {
97 .notifier_call = lpm_cpu_callback,
Girish Mahadevanc45b4c72013-04-24 14:07:11 -060098};
99
100static uint32_t allowed_l2_mode;
101static uint32_t sysfs_dbg_l2_mode = MSM_SPM_L2_MODE_POWER_COLLAPSE;
102static uint32_t default_l2_mode;
103
Girish Mahadevanc45b4c72013-04-24 14:07:11 -0600104
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600105static ssize_t lpm_levels_attr_show(
Girish Mahadevanc45b4c72013-04-24 14:07:11 -0600106 struct kobject *kobj, struct kobj_attribute *attr, char *buf);
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600107static ssize_t lpm_levels_attr_store(struct kobject *kobj,
Girish Mahadevanc45b4c72013-04-24 14:07:11 -0600108 struct kobj_attribute *attr, const char *buf, size_t count);
Archana Sathyakumare6a35102013-01-31 16:18:49 -0700109
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600110
111static int lpm_lvl_dbg_msk;
Mahesh Sivasubramanianfae923f2012-10-08 16:22:51 -0600112
113module_param_named(
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600114 debug_mask, lpm_lvl_dbg_msk, int, S_IRUGO | S_IWUSR | S_IWGRP
Mahesh Sivasubramanianfae923f2012-10-08 16:22:51 -0600115);
116
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600117static bool menu_select;
118module_param_named(
119 menu_select, menu_select, bool, S_IRUGO | S_IWUSR | S_IWGRP
120);
121
122static int msm_pm_sleep_time_override;
123module_param_named(sleep_time_override,
124 msm_pm_sleep_time_override, int, S_IRUGO | S_IWUSR | S_IWGRP);
125
Mahesh Sivasubramanian65c62362013-05-08 14:30:06 -0600126static int num_powered_cores;
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600127static struct hrtimer lpm_hrtimer;
Praveen Chidambaram85b7b282012-04-16 13:45:15 -0600128
Girish Mahadevanc45b4c72013-04-24 14:07:11 -0600129static struct kobj_attribute lpm_l2_kattr = __ATTR(l2, S_IRUGO|S_IWUSR,\
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600130 lpm_levels_attr_show, lpm_levels_attr_store);
Girish Mahadevanc45b4c72013-04-24 14:07:11 -0600131
132static struct attribute *lpm_levels_attr[] = {
133 &lpm_l2_kattr.attr,
134 NULL,
135};
136
137static struct attribute_group lpm_levels_attr_grp = {
138 .attrs = lpm_levels_attr,
139};
140
141/* SYSFS */
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600142static ssize_t lpm_levels_attr_show(
Girish Mahadevanc45b4c72013-04-24 14:07:11 -0600143 struct kobject *kobj, struct kobj_attribute *attr, char *buf)
144{
145 struct kernel_param kp;
146 int rc;
147
148 kp.arg = &sysfs_dbg_l2_mode;
149
150 rc = param_get_uint(buf, &kp);
151
152 if (rc > 0) {
153 strlcat(buf, "\n", PAGE_SIZE);
154 rc++;
155 }
156
157 return rc;
158}
159
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600160static ssize_t lpm_levels_attr_store(struct kobject *kobj,
Girish Mahadevanc45b4c72013-04-24 14:07:11 -0600161 struct kobj_attribute *attr, const char *buf, size_t count)
162{
163 struct kernel_param kp;
164 unsigned int temp;
165 int rc;
166
167 kp.arg = &temp;
168 rc = param_set_uint(buf, &kp);
169 if (rc)
170 return rc;
171
172 sysfs_dbg_l2_mode = temp;
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600173 lpm_system_level_update();
Girish Mahadevanc45b4c72013-04-24 14:07:11 -0600174
175 return count;
176}
Priyanka Mathur3abfd442013-01-11 12:58:51 -0800177
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600178static int msm_pm_get_sleep_mode_value(const char *mode_name)
Archana Sathyakumare6a35102013-01-31 16:18:49 -0700179{
Archana Sathyakumare6a35102013-01-31 16:18:49 -0700180 struct lpm_lookup_table pm_sm_lookup[] = {
181 {MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT,
182 "wfi"},
Archana Sathyakumare6a35102013-01-31 16:18:49 -0700183 {MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE,
184 "standalone_pc"},
185 {MSM_PM_SLEEP_MODE_POWER_COLLAPSE,
186 "pc"},
187 {MSM_PM_SLEEP_MODE_RETENTION,
188 "retention"},
Archana Sathyakumare6a35102013-01-31 16:18:49 -0700189 };
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600190 int i;
191 int ret = -EINVAL;
Archana Sathyakumare6a35102013-01-31 16:18:49 -0700192
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600193 for (i = 0; i < ARRAY_SIZE(pm_sm_lookup); i++) {
194 if (!strcmp(mode_name, pm_sm_lookup[i].mode_name)) {
195 ret = pm_sm_lookup[i].modes;
196 break;
Archana Sathyakumare6a35102013-01-31 16:18:49 -0700197 }
198 }
199 return ret;
200}
201
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600202static int lpm_set_l2_mode(struct lpm_system_state *system_state,
203 int sleep_mode)
Girish Mahadevanc45b4c72013-04-24 14:07:11 -0600204{
205 int lpm = sleep_mode;
206 int rc = 0;
207
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600208 if (system_state->no_l2_saw)
Girish Mahadevanc45b4c72013-04-24 14:07:11 -0600209 goto bail_set_l2_mode;
210
211 msm_pm_set_l2_flush_flag(MSM_SCM_L2_ON);
212
213 switch (sleep_mode) {
214 case MSM_SPM_L2_MODE_POWER_COLLAPSE:
215 msm_pm_set_l2_flush_flag(MSM_SCM_L2_OFF);
216 break;
217 case MSM_SPM_L2_MODE_GDHS:
218 msm_pm_set_l2_flush_flag(MSM_SCM_L2_GDHS);
219 break;
Murali Nalajala82123b02013-10-04 16:00:19 +0530220 case MSM_SPM_L2_MODE_PC_NO_RPM:
221 msm_pm_set_l2_flush_flag(MSM_SCM_L2_OFF);
222 break;
Girish Mahadevanc45b4c72013-04-24 14:07:11 -0600223 case MSM_SPM_L2_MODE_RETENTION:
224 case MSM_SPM_L2_MODE_DISABLED:
225 break;
226 default:
227 lpm = MSM_SPM_L2_MODE_DISABLED;
228 break;
229 }
230
231 rc = msm_spm_l2_set_low_power_mode(lpm, true);
232
233 if (rc) {
234 if (rc == -ENXIO)
235 WARN_ON_ONCE(1);
236 else
237 pr_err("%s: Failed to set L2 low power mode %d, ERR %d",
Mahesh Sivasubramanian65c62362013-05-08 14:30:06 -0600238 __func__, lpm, rc);
Girish Mahadevanc45b4c72013-04-24 14:07:11 -0600239 }
Girish Mahadevanc45b4c72013-04-24 14:07:11 -0600240bail_set_l2_mode:
241 return rc;
242}
243
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600244static void lpm_system_level_update(void)
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600245{
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600246 int i;
247 struct lpm_system_level *l = NULL;
Girish Mahadevanc45b4c72013-04-24 14:07:11 -0600248 uint32_t max_l2_mode;
249 static DEFINE_MUTEX(lpm_lock);
250
251 mutex_lock(&lpm_lock);
252
Mahesh Sivasubramanian65c62362013-05-08 14:30:06 -0600253 if (num_powered_cores == 1)
254 allowed_l2_mode = MSM_SPM_L2_MODE_POWER_COLLAPSE;
255 else if (sys_state.allow_synched_levels)
256 allowed_l2_mode = MSM_SPM_L2_MODE_POWER_COLLAPSE;
257 else
258 allowed_l2_mode = default_l2_mode;
Girish Mahadevanc45b4c72013-04-24 14:07:11 -0600259 max_l2_mode = min(allowed_l2_mode, sysfs_dbg_l2_mode);
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600260
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600261 for (i = 0; i < sys_state.num_system_levels; i++) {
262 l = &sys_state.system_level[i];
263 l->available = !(l->l2_mode > max_l2_mode);
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600264 }
Girish Mahadevanc45b4c72013-04-24 14:07:11 -0600265 mutex_unlock(&lpm_lock);
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600266}
267
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600268static int lpm_system_mode_select(
269 struct lpm_system_state *system_state,
270 uint32_t sleep_us, bool from_idle)
Praveen Chidambaram85b7b282012-04-16 13:45:15 -0600271{
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600272 int best_level = -1;
273 int i;
Murali Nalajala15090a22014-01-22 15:09:09 +0530274 uint32_t best_level_pwr = ~0U;
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600275 uint32_t pwr;
276 uint32_t latency_us = pm_qos_request(PM_QOS_CPU_DMA_LATENCY);
277
278 if (!system_state->system_level)
279 return -EINVAL;
280
281 for (i = 0; i < system_state->num_system_levels; i++) {
282 struct lpm_system_level *system_level =
283 &system_state->system_level[i];
284 struct power_params *pwr_param = &system_level->pwr;
285
286 if (!system_level->available)
287 continue;
288
Mahesh Sivasubramanian65c62362013-05-08 14:30:06 -0600289 if (system_level->sync &&
290 system_level->num_cpu_votes != num_powered_cores)
291 continue;
292
Archana Sathyakumar41e303a2013-12-13 17:37:55 -0700293 if (latency_us < pwr_param->latency_us && from_idle)
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600294 continue;
295
296 if (sleep_us < pwr_param->time_overhead_us)
297 continue;
298
299 /*
300 * After the suspend prepare notifications its possible
301 * for the CPU to enter a system sleep mode. But MPM would have
302 * already requested a XO clock based on the wakeup irqs. To
303 * prevent suspend votes from being overriden by idle irqs, MPM
304 * doesn't send an updated MPM vote after suspend_prepare
305 * callback.
306 * To ensure that XO sleep vote isn't used if and when the
307 * device enters idle PC after suspend prepare callback,
308 * disallow any low power modes that notifies RPM after suspend
309 * prepare function is called
310 */
311 if (suspend_in_progress && system_level->notify_rpm &&
312 from_idle)
313 continue;
314
315 if ((sleep_us >> 10) > pwr_param->time_overhead_us) {
316 pwr = pwr_param->ss_power;
317 } else {
318 pwr = pwr_param->ss_power;
319 pwr -= (pwr_param->time_overhead_us
320 * pwr_param->ss_power) / sleep_us;
321 pwr += pwr_param->energy_overhead / sleep_us;
322 }
323
324 if (best_level_pwr >= pwr) {
325 best_level = i;
326 best_level_pwr = pwr;
327 }
328 }
329 return best_level;
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600330}
331
332static void lpm_system_prepare(struct lpm_system_state *system_state,
333 int index, bool from_idle)
334{
335 struct lpm_system_level *lvl;
Mahesh Sivasubramanian65c62362013-05-08 14:30:06 -0600336 struct clock_event_device *bc = tick_get_broadcast_device()->evtdev;
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600337 uint32_t sclk;
Mahesh Sivasubramanian65c62362013-05-08 14:30:06 -0600338 int64_t us = (~0ULL);
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600339 int dbg_mask;
340 int ret;
Mahesh Sivasubramanian65c62362013-05-08 14:30:06 -0600341 const struct cpumask *nextcpu;
Girish Mahadevan40abbe12012-04-25 14:58:13 -0600342
Mahesh Sivasubramanian65c62362013-05-08 14:30:06 -0600343 spin_lock(&system_state->sync_lock);
Mahesh Sivasubramanian208b0d12014-01-08 12:43:19 -0700344 if (index < 0 ||
345 num_powered_cores != system_state->num_cores_in_sync) {
Mahesh Sivasubramanian65c62362013-05-08 14:30:06 -0600346 spin_unlock(&system_state->sync_lock);
347 return;
348 }
349
350 if (from_idle) {
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600351 dbg_mask = lpm_lvl_dbg_msk & MSM_LPM_LVL_DBG_IDLE_LIMITS;
Mahesh Sivasubramanian65c62362013-05-08 14:30:06 -0600352 us = ktime_to_us(ktime_sub(bc->next_event, ktime_get()));
353 nextcpu = bc->cpumask;
354 } else {
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600355 dbg_mask = lpm_lvl_dbg_msk & MSM_LPM_LVL_DBG_SUSPEND_LIMITS;
Mahesh Sivasubramanian65c62362013-05-08 14:30:06 -0600356 nextcpu = cpumask_of(smp_processor_id());
357 }
Mahesh Sivasubramanian9b0f4122013-02-15 14:20:52 -0700358
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600359 lvl = &system_state->system_level[index];
Mahesh Sivasubramanian9b0f4122013-02-15 14:20:52 -0700360
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600361 ret = lpm_set_l2_mode(system_state, lvl->l2_mode);
Girish Mahadevanc45b4c72013-04-24 14:07:11 -0600362
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600363 if (ret && ret != -ENXIO) {
364 pr_warn("%s(): Cannot set L2 Mode %d, ret:%d\n",
365 __func__, lvl->l2_mode, ret);
366 goto bail_system_sleep;
367 }
368
Mahesh Sivasubramanian65c62362013-05-08 14:30:06 -0600369 if (lvl->notify_rpm) {
370 ret = msm_rpm_enter_sleep(dbg_mask, nextcpu);
371 if (ret) {
372 pr_err("rpm_enter_sleep() failed with rc = %d\n", ret);
373 goto bail_system_sleep;
374 }
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600375
Mahesh Sivasubramanianfc2eb5b2013-11-21 18:30:06 -0700376
Mahesh Sivasubramanian65c62362013-05-08 14:30:06 -0600377 if (!from_idle)
378 us = USEC_PER_SEC * msm_pm_sleep_time_override;
379
380 do_div(us, USEC_PER_SEC/SCLK_HZ);
381 sclk = (uint32_t)us;
382 msm_mpm_enter_sleep(sclk, from_idle, nextcpu);
Mahesh Sivasubramanian0558d4b2012-10-12 18:05:28 -0600383 }
Mahesh Sivasubramanian65c62362013-05-08 14:30:06 -0600384 system_state->last_entered_cluster_index = index;
385 spin_unlock(&system_state->sync_lock);
Praveen Chidambaram85b7b282012-04-16 13:45:15 -0600386 return;
Mahesh Sivasubramanian65c62362013-05-08 14:30:06 -0600387
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600388bail_system_sleep:
Mahesh Sivasubramanian65c62362013-05-08 14:30:06 -0600389 if (default_l2_mode != system_state->system_level[index].l2_mode)
390 lpm_set_l2_mode(system_state, default_l2_mode);
391 spin_unlock(&system_state->sync_lock);
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600392}
393
394static void lpm_system_unprepare(struct lpm_system_state *system_state,
Mahesh Sivasubramanian65c62362013-05-08 14:30:06 -0600395 int cpu_index, bool from_idle)
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600396{
Mahesh Sivasubramanian65c62362013-05-08 14:30:06 -0600397 int index, i;
398 struct lpm_cpu_level *cpu_level = &system_state->cpu_level[cpu_index];
399 bool first_core_up;
400
401 if (cpu_level->mode < system_state->sync_cpu_mode)
402 return;
403
404 spin_lock(&system_state->sync_lock);
405
406 first_core_up = (system_state->num_cores_in_sync == num_powered_cores);
407
408 system_state->num_cores_in_sync--;
409
410 if (!system_state->system_level)
411 goto unlock_and_return;
412
413 index = system_state->last_entered_cluster_index;
414
415 for (i = 0; i < system_state->num_system_levels; i++) {
416 struct lpm_system_level *system_lvl
417 = &system_state->system_level[i];
418 if (cpu_level->mode >= system_lvl->min_cpu_mode)
419 system_lvl->num_cpu_votes--;
420 }
421
Mahesh Sivasubramanian208b0d12014-01-08 12:43:19 -0700422 if (!first_core_up || index < 0)
Mahesh Sivasubramanian65c62362013-05-08 14:30:06 -0600423 goto unlock_and_return;
424
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600425 if (default_l2_mode != system_state->system_level[index].l2_mode)
426 lpm_set_l2_mode(system_state, default_l2_mode);
427
428 if (system_state->system_level[index].notify_rpm) {
429 msm_rpm_exit_sleep();
430 msm_mpm_exit_sleep(from_idle);
431 }
Mahesh Sivasubramanian65c62362013-05-08 14:30:06 -0600432unlock_and_return:
Mahesh Sivasubramanian208b0d12014-01-08 12:43:19 -0700433 system_state->last_entered_cluster_index = -1;
Mahesh Sivasubramanian65c62362013-05-08 14:30:06 -0600434 spin_unlock(&system_state->sync_lock);
Praveen Chidambaram85b7b282012-04-16 13:45:15 -0600435}
436
Stephen Boyd3f4bac22012-05-30 10:03:13 -0700437s32 msm_cpuidle_get_deep_idle_latency(void)
438{
439 int i;
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600440 struct lpm_cpu_level *level = sys_state.cpu_level;
Stephen Boyd3f4bac22012-05-30 10:03:13 -0700441
442 if (!level)
443 return 0;
444
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600445 for (i = 0; i < sys_state.num_cpu_levels; i++, level++) {
446 if (level->mode == MSM_PM_SLEEP_MODE_POWER_COLLAPSE)
447 break;
Stephen Boyd3f4bac22012-05-30 10:03:13 -0700448 }
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600449
450 if (i == sys_state.num_cpu_levels)
451 return 0;
452 else
453 return level->pwr.latency_us;
Stephen Boyd3f4bac22012-05-30 10:03:13 -0700454}
Girish Mahadevanc45b4c72013-04-24 14:07:11 -0600455
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600456static int lpm_cpu_callback(struct notifier_block *cpu_nb,
Girish Mahadevanc45b4c72013-04-24 14:07:11 -0600457 unsigned long action, void *hcpu)
Mahesh Sivasubramanianf1ddf042013-01-08 14:03:32 -0700458{
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600459 switch (action & ~CPU_TASKS_FROZEN) {
Girish Mahadevanc45b4c72013-04-24 14:07:11 -0600460 case CPU_UP_PREPARE:
Mahesh Sivasubramanian65c62362013-05-08 14:30:06 -0600461 ++num_powered_cores;
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600462 lpm_system_level_update();
Girish Mahadevanc45b4c72013-04-24 14:07:11 -0600463 break;
Girish Mahadevanc45b4c72013-04-24 14:07:11 -0600464 case CPU_DEAD:
465 case CPU_UP_CANCELED:
Mahesh Sivasubramanian65c62362013-05-08 14:30:06 -0600466 num_powered_cores = num_online_cpus();
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600467 lpm_system_level_update();
468 break;
469 case CPU_ONLINE:
470 smp_call_function_single((unsigned long)hcpu,
471 setup_broadcast_timer, (void *)true, 1);
472 break;
473 default:
Girish Mahadevanc45b4c72013-04-24 14:07:11 -0600474 break;
475 }
476 return NOTIFY_OK;
Mahesh Sivasubramanianf1ddf042013-01-08 14:03:32 -0700477}
Stephen Boyd3f4bac22012-05-30 10:03:13 -0700478
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600479static enum hrtimer_restart lpm_hrtimer_cb(struct hrtimer *h)
Praveen Chidambaram85b7b282012-04-16 13:45:15 -0600480{
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600481 return HRTIMER_NORESTART;
482}
483
484static void msm_pm_set_timer(uint32_t modified_time_us)
485{
486 u64 modified_time_ns = modified_time_us * NSEC_PER_USEC;
487 ktime_t modified_ktime = ns_to_ktime(modified_time_ns);
488 lpm_hrtimer.function = lpm_hrtimer_cb;
Mahesh Sivasubramanian65c62362013-05-08 14:30:06 -0600489 hrtimer_start(&lpm_hrtimer, modified_ktime, HRTIMER_MODE_REL_PINNED);
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600490}
491
Mahesh Sivasubramanian65c62362013-05-08 14:30:06 -0600492static noinline int lpm_cpu_power_select(struct cpuidle_device *dev, int *index)
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600493{
494 int best_level = -1;
Murali Nalajala15090a22014-01-22 15:09:09 +0530495 uint32_t best_level_pwr = ~0U;
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600496 uint32_t latency_us = pm_qos_request(PM_QOS_CPU_DMA_LATENCY);
497 uint32_t sleep_us =
498 (uint32_t)(ktime_to_us(tick_nohz_get_sleep_length()));
499 uint32_t modified_time_us = 0;
500 uint32_t next_event_us = 0;
501 uint32_t power;
Praveen Chidambaram85b7b282012-04-16 13:45:15 -0600502 int i;
Mahesh Sivasubramanianf1ddf042013-01-08 14:03:32 -0700503
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600504 if (!sys_state.cpu_level)
505 return -EINVAL;
Praveen Chidambaram85b7b282012-04-16 13:45:15 -0600506
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600507 if (!dev->cpu)
508 next_event_us = (uint32_t)(ktime_to_us(get_next_event_time()));
Praveen Chidambaram85b7b282012-04-16 13:45:15 -0600509
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600510 for (i = 0; i < sys_state.num_cpu_levels; i++) {
511 struct lpm_cpu_level *level = &sys_state.cpu_level[i];
512 struct power_params *pwr = &level->pwr;
513 uint32_t next_wakeup_us = sleep_us;
514 enum msm_pm_sleep_mode mode = level->mode;
515 bool allow;
Girish Mahadevana351c0b2013-02-22 11:06:00 -0700516
Mahesh Sivasubramanian65c62362013-05-08 14:30:06 -0600517 if (level->sync && num_online_cpus() > 1
518 && !sys_state.allow_synched_levels)
Praveen Chidambaram85b7b282012-04-16 13:45:15 -0600519 continue;
520
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600521 allow = msm_cpu_pm_check_mode(dev->cpu, mode, true);
522
523 if (!allow)
Praveen Chidambaram85b7b282012-04-16 13:45:15 -0600524 continue;
525
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600526 if (latency_us < pwr->latency_us)
Praveen Chidambaram85b7b282012-04-16 13:45:15 -0600527 continue;
528
Mahesh Sivasubramanianea1c5942014-01-31 13:57:00 -0700529 if (next_event_us) {
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600530 if (next_event_us < pwr->latency_us)
531 continue;
Girish Mahadevana351c0b2013-02-22 11:06:00 -0700532
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600533 if (((next_event_us - pwr->latency_us) < sleep_us)
534 || (next_event_us < sleep_us)) {
535 next_wakeup_us = next_event_us
536 - pwr->latency_us;
Girish Mahadevana351c0b2013-02-22 11:06:00 -0700537 }
Mahesh Sivasubramanianea1c5942014-01-31 13:57:00 -0700538 }
Girish Mahadevana351c0b2013-02-22 11:06:00 -0700539
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600540 if (next_wakeup_us <= pwr->time_overhead_us)
Girish Mahadevana351c0b2013-02-22 11:06:00 -0700541 continue;
542
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600543 if ((MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE == mode)
544 || (MSM_PM_SLEEP_MODE_POWER_COLLAPSE == mode))
545 if (!dev->cpu && msm_rpm_waiting_for_ack())
Mahesh Sivasubramanian9063a292012-11-09 09:15:30 -0700546 break;
547
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600548 if ((next_wakeup_us >> 10) > pwr->latency_us) {
549 power = pwr->ss_power;
Praveen Chidambaram85b7b282012-04-16 13:45:15 -0600550 } else {
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600551 power = pwr->ss_power;
552 power -= (pwr->latency_us * pwr->ss_power)
553 / next_wakeup_us;
554 power += pwr->energy_overhead / next_wakeup_us;
Praveen Chidambaram85b7b282012-04-16 13:45:15 -0600555 }
556
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600557 if (best_level_pwr >= power) {
558 best_level = i;
559 best_level_pwr = power;
560 if (next_event_us < sleep_us &&
561 (mode != MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT))
562 modified_time_us = next_event_us
563 - pwr->latency_us;
Girish Mahadevana351c0b2013-02-22 11:06:00 -0700564 else
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600565 modified_time_us = 0;
Praveen Chidambaram85b7b282012-04-16 13:45:15 -0600566 }
567 }
568
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600569 if (modified_time_us && !dev->cpu)
570 msm_pm_set_timer(modified_time_us);
571
572 return best_level;
Praveen Chidambaram85b7b282012-04-16 13:45:15 -0600573}
Priyanka Mathur3abfd442013-01-11 12:58:51 -0800574
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600575static int lpm_get_l2_cache_value(const char *l2_str)
Girish Mahadevanc45b4c72013-04-24 14:07:11 -0600576{
577 int i;
578 struct lpm_lookup_table l2_mode_lookup[] = {
579 {MSM_SPM_L2_MODE_POWER_COLLAPSE, "l2_cache_pc"},
Murali Nalajala82123b02013-10-04 16:00:19 +0530580 {MSM_SPM_L2_MODE_PC_NO_RPM, "l2_cache_pc_no_rpm"},
Girish Mahadevanc45b4c72013-04-24 14:07:11 -0600581 {MSM_SPM_L2_MODE_GDHS, "l2_cache_gdhs"},
582 {MSM_SPM_L2_MODE_RETENTION, "l2_cache_retention"},
583 {MSM_SPM_L2_MODE_DISABLED, "l2_cache_active"}
584 };
Girish Mahadevanc45b4c72013-04-24 14:07:11 -0600585
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600586 for (i = 0; i < ARRAY_SIZE(l2_mode_lookup); i++)
587 if (!strcmp(l2_str, l2_mode_lookup[i].mode_name))
588 return l2_mode_lookup[i].modes;
589 return -EINVAL;
Girish Mahadevanc45b4c72013-04-24 14:07:11 -0600590}
591
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600592static int lpm_levels_sysfs_add(void)
Girish Mahadevanc45b4c72013-04-24 14:07:11 -0600593{
594 struct kobject *module_kobj = NULL;
595 struct kobject *low_power_kobj = NULL;
596 int rc = 0;
597
598 module_kobj = kset_find_obj(module_kset, KBUILD_MODNAME);
599 if (!module_kobj) {
600 pr_err("%s: cannot find kobject for module %s\n",
601 __func__, KBUILD_MODNAME);
602 rc = -ENOENT;
603 goto resource_sysfs_add_exit;
604 }
605
606 low_power_kobj = kobject_create_and_add(
607 "enable_low_power", module_kobj);
608 if (!low_power_kobj) {
609 pr_err("%s: cannot create kobject\n", __func__);
610 rc = -ENOMEM;
611 goto resource_sysfs_add_exit;
612 }
613
614 rc = sysfs_create_group(low_power_kobj, &lpm_levels_attr_grp);
615resource_sysfs_add_exit:
616 if (rc) {
617 if (low_power_kobj) {
618 sysfs_remove_group(low_power_kobj,
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600619 &lpm_levels_attr_grp);
Girish Mahadevanc45b4c72013-04-24 14:07:11 -0600620 kobject_del(low_power_kobj);
621 }
622 }
623
624 return rc;
625}
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600626static int lpm_cpu_menu_select(struct cpuidle_device *dev, int *index)
Praveen Chidambaram85b7b282012-04-16 13:45:15 -0600627{
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600628 int j;
629
630 for (; *index >= 0; (*index)--) {
631 int mode = 0;
632 bool allow = false;
633
634 allow = msm_cpu_pm_check_mode(dev->cpu, mode, true);
635
636 if (!allow)
637 continue;
638
639 for (j = sys_state.num_cpu_levels; j >= 0; j--) {
640 struct lpm_cpu_level *l = &sys_state.cpu_level[j];
641 if (mode == l->mode)
642 return j;
643 }
644 }
645 return -EPERM;
646}
647
Mahesh Sivasubramanian65c62362013-05-08 14:30:06 -0600648static inline void lpm_cpu_prepare(struct lpm_system_state *system_state,
649 int cpu_index, bool from_idle)
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600650{
Mahesh Sivasubramanian65c62362013-05-08 14:30:06 -0600651 struct lpm_cpu_level *cpu_level = &system_state->cpu_level[cpu_index];
652 unsigned int cpu = smp_processor_id();
653
654 /* Use broadcast timer for aggregating sleep mode within a cluster.
655 * A broadcast timer could be used because of harware restriction or
656 * to ensure that we BC timer is used incase a cpu mode could trigger
657 * a cluster level sleep
658 */
659 if (from_idle && (cpu_level->use_bc_timer ||
660 (cpu_level->mode >= system_state->sync_cpu_mode)))
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600661 clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &cpu);
662}
663
Mahesh Sivasubramanian65c62362013-05-08 14:30:06 -0600664static inline void lpm_cpu_unprepare(struct lpm_system_state *system_state,
665 int cpu_index, bool from_idle)
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600666{
Mahesh Sivasubramanian65c62362013-05-08 14:30:06 -0600667 struct lpm_cpu_level *cpu_level = &system_state->cpu_level[cpu_index];
668 unsigned int cpu = smp_processor_id();
669
670 if (from_idle && (cpu_level->use_bc_timer ||
671 (cpu_level->mode >= system_state->sync_cpu_mode)))
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600672 clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &cpu);
673}
674
675static int lpm_system_select(struct lpm_system_state *system_state,
676 int cpu_index, bool from_idle)
677{
Mahesh Sivasubramanian65c62362013-05-08 14:30:06 -0600678 uint64_t us = (~0ULL);
679 struct clock_event_device *ed;
680 struct lpm_cpu_level *cpu_level = &system_state->cpu_level[cpu_index];
681 int i;
682 bool last_core_down;
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600683
Mahesh Sivasubramanian65c62362013-05-08 14:30:06 -0600684 if (cpu_level->mode < system_state->sync_cpu_mode)
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600685 return -EINVAL;
686
Mahesh Sivasubramanian65c62362013-05-08 14:30:06 -0600687 spin_lock(&system_state->sync_lock);
688
689 last_core_down =
690 (++system_state->num_cores_in_sync == num_powered_cores);
691
692 if (!system_state->system_level) {
693 spin_unlock(&system_state->sync_lock);
694 return -EINVAL;
695 }
696
697 for (i = 0; i < system_state->num_system_levels; i++) {
698 struct lpm_system_level *system_lvl =
699 &system_state->system_level[i];
700 if (cpu_level->mode >= system_lvl->min_cpu_mode)
701 system_lvl->num_cpu_votes++;
702 }
703 spin_unlock(&system_state->sync_lock);
704
705 if (!last_core_down)
706 return -EBUSY;
707
708 ed = tick_get_broadcast_device()->evtdev;
709 if (!ed)
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600710 return -EINVAL;
711
712 if (from_idle)
713 us = ktime_to_us(ktime_sub(ed->next_event, ktime_get()));
714 else
715 us = (~0ULL);
716
717 return lpm_system_mode_select(system_state, (uint32_t)(us), from_idle);
718}
719
720static void lpm_enter_low_power(struct lpm_system_state *system_state,
721 int cpu_index, bool from_idle)
722{
723 int idx;
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600724 struct lpm_cpu_level *cpu_level = &system_state->cpu_level[cpu_index];
725
Mahesh Sivasubramanian65c62362013-05-08 14:30:06 -0600726 lpm_cpu_prepare(system_state, cpu_index, from_idle);
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600727
728 idx = lpm_system_select(system_state, cpu_index, from_idle);
729
Mahesh Sivasubramanian208b0d12014-01-08 12:43:19 -0700730 lpm_system_prepare(system_state, idx, from_idle);
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600731
732 msm_cpu_pm_enter_sleep(cpu_level->mode, from_idle);
733
Mahesh Sivasubramanian65c62362013-05-08 14:30:06 -0600734 lpm_system_unprepare(system_state, cpu_index, from_idle);
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600735
Mahesh Sivasubramanian65c62362013-05-08 14:30:06 -0600736 lpm_cpu_unprepare(system_state, cpu_index, from_idle);
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600737}
738
739static int lpm_cpuidle_enter(struct cpuidle_device *dev,
740 struct cpuidle_driver *drv, int index)
741{
742 int64_t time = ktime_to_ns(ktime_get());
743 int idx;
744
745 idx = menu_select ? lpm_cpu_menu_select(dev, &index) :
746 lpm_cpu_power_select(dev, &index);
747 if (idx < 0) {
748 local_irq_enable();
749 return -EPERM;
750 }
751
752 lpm_enter_low_power(&sys_state, idx, true);
753
754 time = ktime_to_ns(ktime_get()) - time;
755 do_div(time, 1000);
756 dev->last_residency = (int)time;
757 local_irq_enable();
Anil kumar mamidalad6962bd2014-02-06 14:27:50 +0530758 return idx;
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600759}
760
761static int lpm_suspend_enter(suspend_state_t state)
762{
763 int i;
764
765 for (i = sys_state.num_cpu_levels - 1; i >= 0; i--) {
766 bool allow = msm_cpu_pm_check_mode(smp_processor_id(),
767 sys_state.cpu_level[i].mode, false);
768 if (allow)
769 break;
770 }
771
772 if (i < 0)
773 return -EINVAL;
774
775 lpm_enter_low_power(&sys_state, i, false);
776
777 return 0;
778}
779
780static int lpm_suspend_prepare(void)
781{
782 suspend_in_progress = true;
783 msm_mpm_suspend_prepare();
784 return 0;
785}
786
787static void lpm_suspend_wake(void)
788{
789 msm_mpm_suspend_wake();
790 suspend_in_progress = false;
791}
792
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600793static struct platform_device lpm_dev = {
794 .name = "msm_pm",
795 .id = -1,
796};
797
798static const struct platform_suspend_ops lpm_suspend_ops = {
799 .enter = lpm_suspend_enter,
800 .valid = suspend_valid_only_mem,
801 .prepare_late = lpm_suspend_prepare,
802 .wake = lpm_suspend_wake,
803};
804
805static void setup_broadcast_timer(void *arg)
806{
807 unsigned long reason = (unsigned long)arg;
808 int cpu = smp_processor_id();
809
810 reason = reason ?
811 CLOCK_EVT_NOTIFY_BROADCAST_ON : CLOCK_EVT_NOTIFY_BROADCAST_OFF;
812
813 clockevents_notify(reason, &cpu);
814}
Mahesh Sivasubramanian65c62362013-05-08 14:30:06 -0600815
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600816static struct cpuidle_driver msm_cpuidle_driver = {
817 .name = "msm_idle",
818 .owner = THIS_MODULE,
819};
820
821static void lpm_cpuidle_init(void)
822{
823 int i = 0;
824 int state_count = 0;
825
826 if (!sys_state.cpu_level)
827 return;
828 BUG_ON(sys_state.num_cpu_levels > CPUIDLE_STATE_MAX);
829
830 for (i = 0; i < sys_state.num_cpu_levels; i++) {
831 struct cpuidle_state *st = &msm_cpuidle_driver.states[i];
832 struct lpm_cpu_level *cpu_level = &sys_state.cpu_level[i];
833 snprintf(st->name, CPUIDLE_NAME_LEN, "C%u\n", i);
834 snprintf(st->desc, CPUIDLE_DESC_LEN, cpu_level->name);
835 st->flags = 0;
836 st->exit_latency = cpu_level->pwr.latency_us;
837 st->power_usage = cpu_level->pwr.ss_power;
838 st->target_residency = 0;
839 st->enter = lpm_cpuidle_enter;
840 state_count++;
841 }
842 msm_cpuidle_driver.state_count = state_count;
843 msm_cpuidle_driver.safe_state_index = 0;
844
845 if (cpuidle_register(&msm_cpuidle_driver, NULL))
846 pr_err("%s(): Failed to register CPUIDLE device\n", __func__);
847}
848
849static int lpm_parse_power_params(struct device_node *node,
850 struct power_params *pwr)
851{
852 char *key;
853 int ret;
854
855 key = "qcom,latency-us";
856 ret = of_property_read_u32(node, key, &pwr->latency_us);
857 if (ret)
858 goto fail;
859
860 key = "qcom,ss-power";
861 ret = of_property_read_u32(node, key, &pwr->ss_power);
862 if (ret)
863 goto fail;
864
865 key = "qcom,energy-overhead";
866 ret = of_property_read_u32(node, key, &pwr->energy_overhead);
867 if (ret)
868 goto fail;
869
870 key = "qcom,time-overhead";
871 ret = of_property_read_u32(node, key, &pwr->time_overhead_us);
872 if (ret)
873 goto fail;
874fail:
875 if (ret)
876 pr_err("%s(): Error reading %s\n", __func__, key);
877 return ret;
878}
879
880static int lpm_cpu_probe(struct platform_device *pdev)
881{
882 struct lpm_cpu_level *level = NULL, *l;
Praveen Chidambaram85b7b282012-04-16 13:45:15 -0600883 struct device_node *node = NULL;
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600884 int num_levels = 0;
885 char *key;
886 int ret;
887
888 for_each_child_of_node(pdev->dev.of_node, node)
889 num_levels++;
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600890
891 level = kzalloc(num_levels * sizeof(struct lpm_cpu_level),
892 GFP_KERNEL);
893
894 if (!level)
895 return -ENOMEM;
896
897 l = &level[0];
898 for_each_child_of_node(pdev->dev.of_node, node) {
899
900 key = "qcom,mode";
901 ret = of_property_read_string(node, key, &l->name);
902
903 if (ret) {
904 pr_err("%s(): Cannot read cpu mode%s\n", __func__, key);
905 goto fail;
906 }
907
908 l->mode = msm_pm_get_sleep_mode_value(l->name);
909
910 if (l->mode < 0) {
911 pr_err("%s():Cannot parse cpu mode:%s\n", __func__,
912 l->name);
913 goto fail;
914 }
915
916 if (l->mode == MSM_PM_SLEEP_MODE_POWER_COLLAPSE)
917 l->sync = true;
918
919 key = "qcom,use-broadcast-timer";
920 l->use_bc_timer = of_property_read_bool(node, key);
921
922 ret = lpm_parse_power_params(node, &l->pwr);
923 if (ret) {
924 pr_err("%s(): cannot Parse power params\n", __func__);
925 goto fail;
926 }
927 l++;
928 }
929 sys_state.cpu_level = level;
930 sys_state.num_cpu_levels = num_levels;
931 return ret;
932fail:
933 kfree(level);
934 return ret;
935}
936
937static int lpm_system_probe(struct platform_device *pdev)
938{
939 struct lpm_system_level *level = NULL, *l;
940 int num_levels = 0;
941 struct device_node *node;
942 char *key;
943 int ret;
Praveen Chidambaram85b7b282012-04-16 13:45:15 -0600944
945 for_each_child_of_node(pdev->dev.of_node, node)
946 num_levels++;
947
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600948 level = kzalloc(num_levels * sizeof(struct lpm_system_level),
Praveen Chidambaram85b7b282012-04-16 13:45:15 -0600949 GFP_KERNEL);
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600950
951 if (!level)
Praveen Chidambaram85b7b282012-04-16 13:45:15 -0600952 return -ENOMEM;
953
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600954 l = &level[0];
Praveen Chidambaram85b7b282012-04-16 13:45:15 -0600955 for_each_child_of_node(pdev->dev.of_node, node) {
Mahesh Sivasubramanian65c62362013-05-08 14:30:06 -0600956
Praveen Chidambaram85b7b282012-04-16 13:45:15 -0600957 key = "qcom,l2";
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600958 ret = of_property_read_string(node, key, &l->name);
959 if (ret) {
960 pr_err("%s(): Failed to read L2 mode\n", __func__);
Praveen Chidambaram85b7b282012-04-16 13:45:15 -0600961 goto fail;
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600962 }
Mahesh Sivasubramanianb71ce092013-01-08 13:44:23 -0700963
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600964 l->l2_mode = lpm_get_l2_cache_value(l->name);
965
966 if (l->l2_mode < 0) {
967 pr_err("%s(): Failed to read l2 cache mode\n",
968 __func__);
Praveen Chidambaram85b7b282012-04-16 13:45:15 -0600969 goto fail;
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600970 }
Praveen Chidambaram85b7b282012-04-16 13:45:15 -0600971
Anil kumar mamidalaa39a3352014-02-06 20:42:01 +0530972 key = "qcom,send-rpm-sleep-set";
973 l->notify_rpm = of_property_read_bool(node, key);
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600974
975 if (l->l2_mode >= MSM_SPM_L2_MODE_GDHS)
976 l->sync = true;
977
978 ret = lpm_parse_power_params(node, &l->pwr);
979 if (ret) {
980 pr_err("%s(): Failed to parse power params\n",
981 __func__);
Praveen Chidambaram85b7b282012-04-16 13:45:15 -0600982 goto fail;
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -0600983 }
Praveen Chidambaram85b7b282012-04-16 13:45:15 -0600984
Mahesh Sivasubramanian65c62362013-05-08 14:30:06 -0600985 key = "qcom,sync-cpus";
986 l->sync = of_property_read_bool(node, key);
987
988 if (l->sync) {
989 const char *name;
990
991 key = "qcom,min-cpu-mode";
992 ret = of_property_read_string(node, key, &name);
993 if (ret) {
994 pr_err("%s(): Required key %snot found\n",
995 __func__, name);
996 goto fail;
997 }
998
999 l->min_cpu_mode = msm_pm_get_sleep_mode_value(name);
1000
1001 if (l->min_cpu_mode < 0) {
1002 pr_err("%s(): Cannot parse cpu mode:%s\n",
1003 __func__, name);
1004 goto fail;
1005 }
1006
1007 if (l->min_cpu_mode < sys_state.sync_cpu_mode)
1008 sys_state.sync_cpu_mode = l->min_cpu_mode;
1009 }
1010
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -06001011 l++;
Praveen Chidambaram85b7b282012-04-16 13:45:15 -06001012 }
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -06001013 sys_state.system_level = level;
1014 sys_state.num_system_levels = num_levels;
Mahesh Sivasubramanian208b0d12014-01-08 12:43:19 -07001015 sys_state.last_entered_cluster_index = -1;
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -06001016 return ret;
1017fail:
1018 kfree(level);
1019 return ret;
1020}
1021
1022static int lpm_probe(struct platform_device *pdev)
1023{
1024 struct device_node *node = NULL;
1025 char *key = NULL;
1026 int ret;
Praveen Chidambaram85b7b282012-04-16 13:45:15 -06001027
Girish Mahadevanc45b4c72013-04-24 14:07:11 -06001028 node = pdev->dev.of_node;
Girish Mahadevanc45b4c72013-04-24 14:07:11 -06001029
Mahesh Sivasubramanian65c62362013-05-08 14:30:06 -06001030 key = "qcom,allow-synced-levels";
1031 sys_state.allow_synched_levels = of_property_read_bool(node, key);
1032
1033 key = "qcom,no-l2-saw";
1034 sys_state.no_l2_saw = of_property_read_bool(node, key);
1035
1036 sys_state.sync_cpu_mode = MSM_PM_SLEEP_MODE_NR;
1037 spin_lock_init(&sys_state.sync_lock);
1038 sys_state.num_cores_in_sync = 0;
Praveen Chidambaram85b7b282012-04-16 13:45:15 -06001039
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -06001040 ret = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
1041
1042 if (ret)
1043 goto fail;
Priyanka Mathur3abfd442013-01-11 12:58:51 -08001044
Girish Mahadevanc45b4c72013-04-24 14:07:11 -06001045 /* Do the following two steps only if L2 SAW is present */
Mahesh Sivasubramanian65c62362013-05-08 14:30:06 -06001046 num_powered_cores = num_online_cpus();
1047
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -06001048 if (!sys_state.no_l2_saw) {
1049 int ret;
1050 const char *l2;
Girish Mahadevanc45b4c72013-04-24 14:07:11 -06001051 key = "qcom,default-l2-state";
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -06001052 ret = of_property_read_string(node, key, &l2);
1053 if (ret) {
Mahesh Sivasubramanian65c62362013-05-08 14:30:06 -06001054 pr_err("%s(): Failed to read default L2 mode\n",
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -06001055 __func__);
Girish Mahadevanc45b4c72013-04-24 14:07:11 -06001056 goto fail;
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -06001057 }
Priyanka Mathur3abfd442013-01-11 12:58:51 -08001058
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -06001059 default_l2_mode = lpm_get_l2_cache_value(l2);
1060 if (default_l2_mode < 0) {
1061 pr_err("%s(): Unable to parse default L2 mode\n",
1062 __func__);
Girish Mahadevanc45b4c72013-04-24 14:07:11 -06001063 goto fail;
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -06001064 }
1065
1066 if (lpm_levels_sysfs_add())
1067 goto fail;
1068 msm_pm_set_l2_flush_flag(MSM_SCM_L2_ON);
Girish Mahadevanc45b4c72013-04-24 14:07:11 -06001069 } else {
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -06001070 msm_pm_set_l2_flush_flag(MSM_SCM_L2_OFF);
Girish Mahadevanc45b4c72013-04-24 14:07:11 -06001071 default_l2_mode = MSM_SPM_L2_MODE_POWER_COLLAPSE;
1072 }
1073
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -06001074 get_cpu();
1075 on_each_cpu(setup_broadcast_timer, (void *)true, 1);
1076 put_cpu();
1077
1078 register_hotcpu_notifier(&lpm_cpu_nblk);
1079
1080 lpm_system_level_update();
1081 platform_device_register(&lpm_dev);
1082 suspend_set_ops(&lpm_suspend_ops);
1083 hrtimer_init(&lpm_hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
1084 lpm_cpuidle_init();
Praveen Chidambaram85b7b282012-04-16 13:45:15 -06001085 return 0;
1086fail:
1087 pr_err("%s: Error in name %s key %s\n", __func__, node->full_name, key);
Praveen Chidambaram85b7b282012-04-16 13:45:15 -06001088 return -EFAULT;
1089}
1090
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -06001091static struct of_device_id cpu_modes_mtch_tbl[] = {
1092 {.compatible = "qcom,cpu-modes"},
1093 {},
1094};
1095
1096static struct platform_driver cpu_modes_driver = {
1097 .probe = lpm_cpu_probe,
1098 .driver = {
1099 .name = "cpu-modes",
1100 .owner = THIS_MODULE,
1101 .of_match_table = cpu_modes_mtch_tbl,
1102 },
1103};
1104
1105static struct of_device_id system_modes_mtch_tbl[] = {
1106 {.compatible = "qcom,system-modes"},
1107 {},
1108};
1109
1110static struct platform_driver system_modes_driver = {
1111 .probe = lpm_system_probe,
1112 .driver = {
1113 .name = "system-modes",
1114 .owner = THIS_MODULE,
1115 .of_match_table = system_modes_mtch_tbl,
1116 },
1117};
1118
1119static struct of_device_id lpm_levels_match_table[] = {
Praveen Chidambaram85b7b282012-04-16 13:45:15 -06001120 {.compatible = "qcom,lpm-levels"},
1121 {},
1122};
1123
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -06001124static struct platform_driver lpm_levels_driver = {
1125 .probe = lpm_probe,
Praveen Chidambaram85b7b282012-04-16 13:45:15 -06001126 .driver = {
1127 .name = "lpm-levels",
1128 .owner = THIS_MODULE,
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -06001129 .of_match_table = lpm_levels_match_table,
Praveen Chidambaram85b7b282012-04-16 13:45:15 -06001130 },
1131};
1132
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -06001133static int __init lpm_levels_module_init(void)
Praveen Chidambaram85b7b282012-04-16 13:45:15 -06001134{
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -06001135 int rc;
1136 rc = platform_driver_register(&cpu_modes_driver);
1137 if (rc) {
Mahesh Sivasubramanian65c62362013-05-08 14:30:06 -06001138 pr_err("Error registering %s\n", cpu_modes_driver.driver.name);
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -06001139 goto fail;
1140 }
1141
1142 rc = platform_driver_register(&system_modes_driver);
1143 if (rc) {
1144 platform_driver_unregister(&cpu_modes_driver);
Mahesh Sivasubramanian65c62362013-05-08 14:30:06 -06001145 pr_err("Error registering %s\n",
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -06001146 system_modes_driver.driver.name);
1147 goto fail;
1148 }
1149
1150 rc = platform_driver_register(&lpm_levels_driver);
1151 if (rc) {
1152 platform_driver_unregister(&cpu_modes_driver);
1153 platform_driver_unregister(&system_modes_driver);
Mahesh Sivasubramanian65c62362013-05-08 14:30:06 -06001154 pr_err("Error registering %s\n",
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -06001155 lpm_levels_driver.driver.name);
1156 }
1157fail:
1158 return rc;
Praveen Chidambaram85b7b282012-04-16 13:45:15 -06001159}
Mahesh Sivasubramaniandab0ec32013-10-24 15:09:31 -06001160late_initcall(lpm_levels_module_init);