/* * Suspend the Apps processor. * * Return value: * -EPERM: Suspend happened by a not permitted core * -EAGAIN: modem reset occurred or early exit from suspend * -EBUSY: modem not ready for our suspend * -EINVAL: invalid sleep mode * -EIO: could not ramp Apps processor clock * -ETIMEDOUT: timed out waiting for modem's handshake * 0: success */ static int msm_pm_enter(suspend_state_t state) { bool allow[MSM_PM_SLEEP_MODE_NR]; uint32_t sleep_limit = SLEEP_LIMIT_NONE; int ret = -EPERM; int i; int64_t period = 0; int64_t time = 0; /* Must executed by CORE0 */ if (smp_processor_id()) { __WARN(); goto suspend_exit; } time = msm_pm_timer_enter_suspend(&period); MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND, KERN_INFO, "%s(): sleep limit %u\n", __func__, sleep_limit); for (i = 0; i < ARRAY_SIZE(allow); i++) allow[i] = true; for (i = 0; i < ARRAY_SIZE(allow); i++) { struct msm_pm_platform_data *mode; mode = &msm_pm_modes[MSM_PM_MODE(0, i)]; if (!mode->suspend_supported || !mode->suspend_enabled) allow[i] = false; } if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE] || allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN]) { enum msm_pm_time_stats_id id; clock_debug_print_enabled(); #ifdef CONFIG_MSM_SLEEP_TIME_OVERRIDE if (msm_pm_sleep_time_override > 0) { int64_t ns; ns = NSEC_PER_SEC * (int64_t)msm_pm_sleep_time_override; msm_pm_set_max_sleep_time(ns); msm_pm_sleep_time_override = 0; } #endif if (!allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE]) sleep_limit = SLEEP_LIMIT_NO_TCXO_SHUTDOWN; #if defined(CONFIG_MSM_MEMORY_LOW_POWER_MODE_SUSPEND_ACTIVE) sleep_limit |= SLEEP_RESOURCE_MEMORY_BIT1; #elif defined(CONFIG_MSM_MEMORY_LOW_POWER_MODE_SUSPEND_RETENTION) sleep_limit |= SLEEP_RESOURCE_MEMORY_BIT0; #elif defined(CONFIG_MSM_MEMORY_LOW_POWER_MODE_SUSPEND_DEEP_POWER_DOWN) if (get_msm_migrate_pages_status() != MEM_OFFLINE) sleep_limit |= SLEEP_RESOURCE_MEMORY_BIT0; #endif for (i = 0; i < 30 && msm_pm_modem_busy(); i++) udelay(500); ret = msm_pm_power_collapse( false, msm_pm_max_sleep_time, sleep_limit); if (ret) id = MSM_PM_STAT_FAILED_SUSPEND; else { id = MSM_PM_STAT_SUSPEND; msm_pm_sleep_limit = sleep_limit; } time = msm_pm_timer_exit_suspend(time, period); msm_pm_add_stat(id, time); } else if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE]) { ret = msm_pm_power_collapse_standalone(false); } else if (allow[MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT]) { ret = msm_pm_swfi(true); if (ret) while (!msm_pm_irq_extns->irq_pending()) udelay(1); } else if (allow[MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT]) { msm_pm_swfi(false); } suspend_exit: MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND, KERN_INFO, "%s(): return %d\n", __func__, ret); return ret; }
s32 msm_cpuidle_get_deep_idle_latency(void) { int i = MSM_PM_MODE(0, MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN); return msm_pm_modes[i].latency - 1; }
/* * Put CPU in low power mode. */ void arch_idle(void) { bool allow[MSM_PM_SLEEP_MODE_NR]; uint32_t sleep_limit = SLEEP_LIMIT_NONE; int64_t timer_expiration; int latency_qos; int ret; int i; unsigned int cpu; int64_t t1; static DEFINE_PER_CPU(int64_t, t2); int exit_stat; if (!atomic_read(&msm_pm_init_done)) return; cpu = smp_processor_id(); latency_qos = pm_qos_request(PM_QOS_CPU_DMA_LATENCY); /* get the next timer expiration */ timer_expiration = ktime_to_ns(tick_nohz_get_sleep_length()); t1 = ktime_to_ns(ktime_get()); msm_pm_add_stat(MSM_PM_STAT_NOT_IDLE, t1 - __get_cpu_var(t2)); msm_pm_add_stat(MSM_PM_STAT_REQUESTED_IDLE, timer_expiration); exit_stat = MSM_PM_STAT_IDLE_SPIN; for (i = 0; i < ARRAY_SIZE(allow); i++) allow[i] = true; if (num_online_cpus() > 1 || (timer_expiration < msm_pm_idle_sleep_min_time) || !msm_pm_irq_extns->idle_sleep_allowed()) { allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE] = false; allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN] = false; } for (i = 0; i < ARRAY_SIZE(allow); i++) { struct msm_pm_platform_data *mode = &msm_pm_modes[MSM_PM_MODE(cpu, i)]; if (!mode->idle_supported || !mode->idle_enabled || mode->latency >= latency_qos || mode->residency * 1000ULL >= timer_expiration) allow[i] = false; } if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE] || allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN]) { uint32_t wait_us = CONFIG_MSM_IDLE_WAIT_ON_MODEM; while (msm_pm_modem_busy() && wait_us) { if (wait_us > 100) { udelay(100); wait_us -= 100; } else { udelay(wait_us); wait_us = 0; } } if (msm_pm_modem_busy()) { allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE] = false; allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN] = false; } } MSM_PM_DPRINTK(MSM_PM_DEBUG_IDLE, KERN_INFO, "%s(): latency qos %d, next timer %lld, sleep limit %u\n", __func__, latency_qos, timer_expiration, sleep_limit); for (i = 0; i < ARRAY_SIZE(allow); i++) MSM_PM_DPRINTK(MSM_PM_DEBUG_IDLE, KERN_INFO, "%s(): allow %s: %d\n", __func__, msm_pm_sleep_mode_labels[i], (int)allow[i]); if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE] || allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN]) { /* Sync the timer with SCLK, it is needed only for modem * assissted pollapse case. */ int64_t next_timer_exp = msm_timer_enter_idle(); uint32_t sleep_delay; bool low_power = false; sleep_delay = (uint32_t) msm_pm_convert_and_cap_time( next_timer_exp, MSM_PM_SLEEP_TICK_LIMIT); if (sleep_delay == 0) /* 0 would mean infinite time */ sleep_delay = 1; if (!allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE]) sleep_limit = SLEEP_LIMIT_NO_TCXO_SHUTDOWN; #if defined(CONFIG_MSM_MEMORY_LOW_POWER_MODE_IDLE_ACTIVE) sleep_limit |= SLEEP_RESOURCE_MEMORY_BIT1; #elif defined(CONFIG_MSM_MEMORY_LOW_POWER_MODE_IDLE_RETENTION) sleep_limit |= SLEEP_RESOURCE_MEMORY_BIT0; #endif ret = msm_pm_power_collapse(true, sleep_delay, sleep_limit); low_power = (ret != -EBUSY && ret != -ETIMEDOUT); msm_timer_exit_idle(low_power); if (ret) exit_stat = MSM_PM_STAT_IDLE_FAILED_POWER_COLLAPSE; else { exit_stat = MSM_PM_STAT_IDLE_POWER_COLLAPSE; msm_pm_sleep_limit = sleep_limit; } } else if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE]) { ret = msm_pm_power_collapse_standalone(true); exit_stat = ret ? MSM_PM_STAT_IDLE_FAILED_STANDALONE_POWER_COLLAPSE : MSM_PM_STAT_IDLE_STANDALONE_POWER_COLLAPSE; } else if (allow[MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT]) { ret = msm_pm_swfi(true); if (ret) while (!msm_pm_irq_extns->irq_pending()) udelay(1); exit_stat = ret ? MSM_PM_STAT_IDLE_SPIN : MSM_PM_STAT_IDLE_WFI; } else if (allow[MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT]) { msm_pm_swfi(false); exit_stat = MSM_PM_STAT_IDLE_WFI; } else { while (!msm_pm_irq_extns->irq_pending()) udelay(1); exit_stat = MSM_PM_STAT_IDLE_SPIN; } __get_cpu_var(t2) = ktime_to_ns(ktime_get()); msm_pm_add_stat(exit_stat, __get_cpu_var(t2) - t1); }
/* Add sysfs entries for one cpu. */ static int __init msm_pm_mode_sysfs_add_cpu( unsigned int cpu, struct kobject *modes_kobj) { char cpu_name[8]; struct kobject *cpu_kobj; struct msm_pm_sysfs_sleep_mode *mode = NULL; int i, j, k; int ret; snprintf(cpu_name, sizeof(cpu_name), "cpu%u", cpu); cpu_kobj = kobject_create_and_add(cpu_name, modes_kobj); if (!cpu_kobj) { pr_err("%s: cannot create %s kobject\n", __func__, cpu_name); ret = -ENOMEM; goto mode_sysfs_add_cpu_exit; } for (i = 0; i < MSM_PM_SLEEP_MODE_NR; i++) { int idx = MSM_PM_MODE(cpu, i); if ((!msm_pm_modes[idx].suspend_supported) && (!msm_pm_modes[idx].idle_supported)) continue; mode = kzalloc(sizeof(*mode), GFP_KERNEL); if (!mode) { pr_err("%s: cannot allocate memory for attributes\n", __func__); ret = -ENOMEM; goto mode_sysfs_add_cpu_exit; } mode->kobj = kobject_create_and_add( msm_pm_sleep_mode_labels[i], cpu_kobj); if (!mode->kobj) { pr_err("%s: cannot create kobject\n", __func__); ret = -ENOMEM; goto mode_sysfs_add_cpu_exit; } for (k = 0, j = 0; k < MSM_PM_MODE_ATTR_NR; k++) { if ((k == MSM_PM_MODE_ATTR_IDLE) && !msm_pm_modes[idx].idle_supported) continue; if ((k == MSM_PM_MODE_ATTR_SUSPEND) && !msm_pm_modes[idx].suspend_supported) continue; mode->kas[j].cpu = cpu; mode->kas[j].ka.attr.mode = 0644; mode->kas[j].ka.show = msm_pm_mode_attr_show; mode->kas[j].ka.store = msm_pm_mode_attr_store; mode->kas[j].ka.attr.name = msm_pm_mode_attr_labels[k]; mode->attrs[j] = &mode->kas[j].ka.attr; j++; } mode->attrs[j] = NULL; mode->attr_group.attrs = mode->attrs; ret = sysfs_create_group(mode->kobj, &mode->attr_group); if (ret) { printk(KERN_ERR "%s: cannot create kobject attribute group\n", __func__); goto mode_sysfs_add_cpu_exit; } } ret = 0; mode_sysfs_add_cpu_exit: if (ret) { if (mode && mode->kobj) kobject_del(mode->kobj); kfree(mode); } return ret; }