^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0-only
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * Copyright (C) 2014 Freescale Semiconductor, Inc.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) #include <linux/cpuidle.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) #include <linux/cpu_pm.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) #include <linux/module.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) #include <asm/cacheflush.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) #include <asm/cpuidle.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) #include <asm/suspend.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) #include "common.h"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) #include "cpuidle.h"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) #include "hardware.h"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) static int imx6sx_idle_finish(unsigned long val)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) * for Cortex-A7 which has an internal L2
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) * cache, need to flush it before powering
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) * down ARM platform, since flushing L1 cache
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) * here again has very small overhead, compared
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) * to adding conditional code for L2 cache type,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) * just call flush_cache_all() is fine.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) flush_cache_all();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) cpu_do_idle();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) static int imx6sx_enter_wait(struct cpuidle_device *dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) struct cpuidle_driver *drv, int index)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) imx6_set_lpm(WAIT_UNCLOCKED);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) switch (index) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) case 1:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) cpu_do_idle();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) case 2:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) imx6_enable_rbc(true);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) imx_gpc_set_arm_power_in_lpm(true);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) imx_set_cpu_jump(0, v7_cpu_resume);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) /* Need to notify there is a cpu pm operation. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) cpu_pm_enter();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) cpu_cluster_pm_enter();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) cpu_suspend(0, imx6sx_idle_finish);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) cpu_cluster_pm_exit();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) cpu_pm_exit();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) imx_gpc_set_arm_power_in_lpm(false);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) imx6_enable_rbc(false);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) default:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) imx6_set_lpm(WAIT_CLOCKED);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) return index;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) static struct cpuidle_driver imx6sx_cpuidle_driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) .name = "imx6sx_cpuidle",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) .owner = THIS_MODULE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) .states = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) /* WFI */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) ARM_CPUIDLE_WFI_STATE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) /* WAIT */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) .exit_latency = 50,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) .target_residency = 75,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) .flags = CPUIDLE_FLAG_TIMER_STOP,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) .enter = imx6sx_enter_wait,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) .name = "WAIT",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) .desc = "Clock off",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) /* WAIT + ARM power off */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) * ARM gating 31us * 5 + RBC clear 65us
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) * and some margin for SW execution, here set it
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) * to 300us.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) .exit_latency = 300,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) .target_residency = 500,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) .flags = CPUIDLE_FLAG_TIMER_STOP,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) .enter = imx6sx_enter_wait,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) .name = "LOW-POWER-IDLE",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) .desc = "ARM power off",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) .state_count = 3,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) .safe_state_index = 0,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) int __init imx6sx_cpuidle_init(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) imx6_set_int_mem_clk_lpm(true);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) imx6_enable_rbc(false);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) imx_gpc_set_l2_mem_power_in_lpm(false);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) * set ARM power up/down timing to the fastest,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) * sw2iso and sw can be set to one 32K cycle = 31us
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) * except for power up sw2iso which need to be
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) * larger than LDO ramp up time.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) imx_gpc_set_arm_power_up_timing(cpu_is_imx6sx() ? 0xf : 0x2, 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) imx_gpc_set_arm_power_down_timing(1, 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) return cpuidle_register(&imx6sx_cpuidle_driver, NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) }