Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Copyright (C) 2014 Freescale Semiconductor, Inc.
0004  */
0005 
0006 #include <linux/cpuidle.h>
0007 #include <linux/cpu_pm.h>
0008 #include <linux/module.h>
0009 #include <asm/cacheflush.h>
0010 #include <asm/cpuidle.h>
0011 #include <asm/suspend.h>
0012 
0013 #include "common.h"
0014 #include "cpuidle.h"
0015 #include "hardware.h"
0016 
0017 static int imx6sx_idle_finish(unsigned long val)
0018 {
0019     /*
0020      * for Cortex-A7 which has an internal L2
0021      * cache, need to flush it before powering
0022      * down ARM platform, since flushing L1 cache
0023      * here again has very small overhead, compared
0024      * to adding conditional code for L2 cache type,
0025      * just call flush_cache_all() is fine.
0026      */
0027     flush_cache_all();
0028     cpu_do_idle();
0029 
0030     return 0;
0031 }
0032 
0033 static int imx6sx_enter_wait(struct cpuidle_device *dev,
0034                 struct cpuidle_driver *drv, int index)
0035 {
0036     imx6_set_lpm(WAIT_UNCLOCKED);
0037 
0038     switch (index) {
0039     case 1:
0040         cpu_do_idle();
0041         break;
0042     case 2:
0043         imx6_enable_rbc(true);
0044         imx_gpc_set_arm_power_in_lpm(true);
0045         imx_set_cpu_jump(0, v7_cpu_resume);
0046         /* Need to notify there is a cpu pm operation. */
0047         cpu_pm_enter();
0048         cpu_cluster_pm_enter();
0049 
0050         cpu_suspend(0, imx6sx_idle_finish);
0051 
0052         cpu_cluster_pm_exit();
0053         cpu_pm_exit();
0054         imx_gpc_set_arm_power_in_lpm(false);
0055         imx6_enable_rbc(false);
0056         break;
0057     default:
0058         break;
0059     }
0060 
0061     imx6_set_lpm(WAIT_CLOCKED);
0062 
0063     return index;
0064 }
0065 
0066 static struct cpuidle_driver imx6sx_cpuidle_driver = {
0067     .name = "imx6sx_cpuidle",
0068     .owner = THIS_MODULE,
0069     .states = {
0070         /* WFI */
0071         ARM_CPUIDLE_WFI_STATE,
0072         /* WAIT */
0073         {
0074             .exit_latency = 50,
0075             .target_residency = 75,
0076             .flags = CPUIDLE_FLAG_TIMER_STOP,
0077             .enter = imx6sx_enter_wait,
0078             .name = "WAIT",
0079             .desc = "Clock off",
0080         },
0081         /* WAIT + ARM power off  */
0082         {
0083             /*
0084              * ARM gating 31us * 5 + RBC clear 65us
0085              * and some margin for SW execution, here set it
0086              * to 300us.
0087              */
0088             .exit_latency = 300,
0089             .target_residency = 500,
0090             .flags = CPUIDLE_FLAG_TIMER_STOP,
0091             .enter = imx6sx_enter_wait,
0092             .name = "LOW-POWER-IDLE",
0093             .desc = "ARM power off",
0094         },
0095     },
0096     .state_count = 3,
0097     .safe_state_index = 0,
0098 };
0099 
0100 int __init imx6sx_cpuidle_init(void)
0101 {
0102     imx6_set_int_mem_clk_lpm(true);
0103     imx6_enable_rbc(false);
0104     imx_gpc_set_l2_mem_power_in_lpm(false);
0105     /*
0106      * set ARM power up/down timing to the fastest,
0107      * sw2iso and sw can be set to one 32K cycle = 31us
0108      * except for power up sw2iso which need to be
0109      * larger than LDO ramp up time.
0110      */
0111     imx_gpc_set_arm_power_up_timing(cpu_is_imx6sx() ? 0xf : 0x2, 1);
0112     imx_gpc_set_arm_power_down_timing(1, 1);
0113 
0114     return cpuidle_register(&imx6sx_cpuidle_driver, NULL);
0115 }