123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177 |
- /* Copyright (c) 2010-2013, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
- #include <linux/module.h>
- #include <linux/kernel.h>
- #include <linux/init.h>
- #include <linux/cpuidle.h>
- #include <mach/cpuidle.h>
- #include "pm.h"
- static DEFINE_PER_CPU_SHARED_ALIGNED(struct cpuidle_device, msm_cpuidle_devs);
- static struct cpuidle_driver msm_cpuidle_driver = {
- .name = "msm_idle",
- .owner = THIS_MODULE,
- };
- static struct msm_cpuidle_state msm_cstates[] = {
- {0, 0, "C0", "WFI",
- MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT},
- {0, 1, "C1", "RETENTION",
- MSM_PM_SLEEP_MODE_RETENTION},
- {0, 2, "C2", "STANDALONE_POWER_COLLAPSE",
- MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE},
- {0, 3, "C3", "POWER_COLLAPSE",
- MSM_PM_SLEEP_MODE_POWER_COLLAPSE},
- {1, 0, "C0", "WFI",
- MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT},
- {1, 1, "C1", "RETENTION",
- MSM_PM_SLEEP_MODE_RETENTION},
- {1, 2, "C2", "STANDALONE_POWER_COLLAPSE",
- MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE},
- {2, 0, "C0", "WFI",
- MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT},
- {2, 1, "C1", "RETENTION",
- MSM_PM_SLEEP_MODE_RETENTION},
- {2, 2, "C2", "STANDALONE_POWER_COLLAPSE",
- MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE},
- {3, 0, "C0", "WFI",
- MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT},
- {3, 1, "C1", "RETENTION",
- MSM_PM_SLEEP_MODE_RETENTION},
- {3, 2, "C2", "STANDALONE_POWER_COLLAPSE",
- MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE},
- };
- static int msm_cpuidle_enter(
- struct cpuidle_device *dev, struct cpuidle_driver *drv, int index)
- {
- int ret = 0;
- int i;
- enum msm_pm_sleep_mode pm_mode;
- pm_mode = msm_pm_idle_enter(dev, drv, index);
- for (i = 0; i < dev->state_count; i++) {
- struct cpuidle_state_usage *st_usage = &dev->states_usage[i];
- enum msm_pm_sleep_mode last_mode =
- (enum msm_pm_sleep_mode)cpuidle_get_statedata(st_usage);
- if (last_mode == pm_mode) {
- ret = i;
- break;
- }
- }
- local_irq_enable();
- return ret;
- }
- static void __devinit msm_cpuidle_set_states(void)
- {
- int i = 0;
- int state_count = 0;
- struct msm_cpuidle_state *cstate = NULL;
- struct cpuidle_state *state = NULL;
- for (i = 0; i < ARRAY_SIZE(msm_cstates); i++) {
- cstate = &msm_cstates[i];
- /* We have an asymmetric CPU C-State in MSMs.
- * The primary CPU can do PC while all secondary cpus
- * can only do standalone PC as part of their idle LPM.
- * However, the secondary cpus can do PC when hotplugged
- * We do not care about the hotplug here.
- * Register the C-States available for Core0.
- */
- if (cstate->cpu)
- continue;
- state = &msm_cpuidle_driver.states[state_count];
- snprintf(state->name, CPUIDLE_NAME_LEN, "%s", cstate->name);
- snprintf(state->desc, CPUIDLE_DESC_LEN, "%s", cstate->desc);
- state->flags = 0;
- state->exit_latency = 0;
- state->power_usage = 0;
- state->target_residency = 0;
- state->enter = msm_cpuidle_enter;
- state_count++;
- BUG_ON(state_count >= CPUIDLE_STATE_MAX);
- }
- msm_cpuidle_driver.state_count = state_count;
- msm_cpuidle_driver.safe_state_index = 0;
- }
- static void __init msm_cpuidle_set_cpu_statedata(struct cpuidle_device *dev)
- {
- int i = 0;
- int state_count = 0;
- struct cpuidle_state_usage *st_usage = NULL;
- struct msm_cpuidle_state *cstate = NULL;
- for (i = 0; i < ARRAY_SIZE(msm_cstates); i++) {
- cstate = &msm_cstates[i];
- if (cstate->cpu != dev->cpu)
- continue;
- st_usage = &dev->states_usage[state_count];
- cpuidle_set_statedata(st_usage, (void *)cstate->mode_nr);
- state_count++;
- BUG_ON(state_count > msm_cpuidle_driver.state_count);
- }
- dev->state_count = state_count; /* Per cpu state count */
- }
- int __devinit msm_cpuidle_init(void)
- {
- unsigned int cpu = 0;
- int ret = 0;
- msm_cpuidle_set_states();
- ret = cpuidle_register_driver(&msm_cpuidle_driver);
- if (ret)
- pr_err("%s: failed to register cpuidle driver: %d\n",
- __func__, ret);
- for_each_possible_cpu(cpu) {
- struct cpuidle_device *dev = &per_cpu(msm_cpuidle_devs, cpu);
- dev->cpu = cpu;
- msm_cpuidle_set_cpu_statedata(dev);
- ret = cpuidle_register_device(dev);
- if (ret) {
- pr_err("%s: failed to register cpuidle device for "
- "cpu %u: %d\n", __func__, cpu, ret);
- return ret;
- }
- }
- return 0;
- }
|