112bb3440SShawn Guo /* 212bb3440SShawn Guo * Copyright (C) 2012 Freescale Semiconductor, Inc. 312bb3440SShawn Guo * 412bb3440SShawn Guo * This program is free software; you can redistribute it and/or modify 512bb3440SShawn Guo * it under the terms of the GNU General Public License version 2 as 612bb3440SShawn Guo * published by the Free Software Foundation. 712bb3440SShawn Guo */ 812bb3440SShawn Guo 912bb3440SShawn Guo #include <linux/cpuidle.h> 1012bb3440SShawn Guo #include <linux/module.h> 1112bb3440SShawn Guo #include <asm/cpuidle.h> 12e5f9dec8SShawn Guo #include <asm/proc-fns.h> 1312bb3440SShawn Guo 14e5f9dec8SShawn Guo #include "common.h" 1512bb3440SShawn Guo #include "cpuidle.h" 16a25d67a4SAnson Huang #include "hardware.h" 1712bb3440SShawn Guo 18e5f9dec8SShawn Guo static atomic_t master = ATOMIC_INIT(0); 19e5f9dec8SShawn Guo static DEFINE_SPINLOCK(master_lock); 20e5f9dec8SShawn Guo 21e5f9dec8SShawn Guo static int imx6q_enter_wait(struct cpuidle_device *dev, 22e5f9dec8SShawn Guo struct cpuidle_driver *drv, int index) 23e5f9dec8SShawn Guo { 24e5f9dec8SShawn Guo if (atomic_inc_return(&master) == num_online_cpus()) { 25e5f9dec8SShawn Guo /* 26e5f9dec8SShawn Guo * With this lock, we prevent other cpu to exit and enter 27e5f9dec8SShawn Guo * this function again and become the master. 28e5f9dec8SShawn Guo */ 29e5f9dec8SShawn Guo if (!spin_trylock(&master_lock)) 30e5f9dec8SShawn Guo goto idle; 31e5f9dec8SShawn Guo imx6q_set_lpm(WAIT_UNCLOCKED); 32e5f9dec8SShawn Guo cpu_do_idle(); 33e5f9dec8SShawn Guo imx6q_set_lpm(WAIT_CLOCKED); 34e5f9dec8SShawn Guo spin_unlock(&master_lock); 35e5f9dec8SShawn Guo goto done; 36e5f9dec8SShawn Guo } 37e5f9dec8SShawn Guo 38e5f9dec8SShawn Guo idle: 39e5f9dec8SShawn Guo cpu_do_idle(); 40e5f9dec8SShawn Guo done: 41e5f9dec8SShawn Guo atomic_dec(&master); 42e5f9dec8SShawn Guo 43e5f9dec8SShawn Guo return index; 44e5f9dec8SShawn Guo } 45e5f9dec8SShawn Guo 4612bb3440SShawn Guo static struct cpuidle_driver imx6q_cpuidle_driver = { 4712bb3440SShawn Guo .name = "imx6q_cpuidle", 4812bb3440SShawn Guo .owner = THIS_MODULE, 49e5f9dec8SShawn Guo .states = { 50e5f9dec8SShawn Guo /* WFI */ 51e5f9dec8SShawn Guo ARM_CPUIDLE_WFI_STATE, 52e5f9dec8SShawn Guo /* WAIT */ 53e5f9dec8SShawn Guo { 54e5f9dec8SShawn Guo .exit_latency = 50, 55e5f9dec8SShawn Guo .target_residency = 75, 568de46effSDaniel Lezcano .flags = CPUIDLE_FLAG_TIME_VALID | 578de46effSDaniel Lezcano CPUIDLE_FLAG_TIMER_STOP, 58e5f9dec8SShawn Guo .enter = imx6q_enter_wait, 59e5f9dec8SShawn Guo .name = "WAIT", 60e5f9dec8SShawn Guo .desc = "Clock off", 61e5f9dec8SShawn Guo }, 62e5f9dec8SShawn Guo }, 63e5f9dec8SShawn Guo .state_count = 2, 64e5f9dec8SShawn Guo .safe_state_index = 0, 6512bb3440SShawn Guo }; 6612bb3440SShawn Guo 6712bb3440SShawn Guo int __init imx6q_cpuidle_init(void) 6812bb3440SShawn Guo { 69e5f9dec8SShawn Guo /* Need to enable SCU standby for entering WAIT modes */ 70a25d67a4SAnson Huang if (!cpu_is_imx6sx()) 71e5f9dec8SShawn Guo imx_scu_standby_enable(); 72e5f9dec8SShawn Guo 73fa6be65eSFabio Estevam /* Set INT_MEM_CLK_LPM bit to get a reliable WAIT mode support */ 74dfea953aSAnson Huang imx6q_set_int_mem_clk_lpm(true); 75e5f9dec8SShawn Guo 7654a4644bSDaniel Lezcano return cpuidle_register(&imx6q_cpuidle_driver, NULL); 7712bb3440SShawn Guo } 78