1 /* 2 * Copyright (C) 2012 Freescale Semiconductor, Inc. 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License version 2 as 6 * published by the Free Software Foundation. 7 */ 8 9 #include <linux/cpuidle.h> 10 #include <linux/module.h> 11 #include <asm/cpuidle.h> 12 #include <asm/proc-fns.h> 13 14 #include "common.h" 15 #include "cpuidle.h" 16 #include "hardware.h" 17 18 static atomic_t master = ATOMIC_INIT(0); 19 static DEFINE_SPINLOCK(master_lock); 20 21 static int imx6q_enter_wait(struct cpuidle_device *dev, 22 struct cpuidle_driver *drv, int index) 23 { 24 if (atomic_inc_return(&master) == num_online_cpus()) { 25 /* 26 * With this lock, we prevent other cpu to exit and enter 27 * this function again and become the master. 28 */ 29 if (!spin_trylock(&master_lock)) 30 goto idle; 31 imx6q_set_lpm(WAIT_UNCLOCKED); 32 cpu_do_idle(); 33 imx6q_set_lpm(WAIT_CLOCKED); 34 spin_unlock(&master_lock); 35 goto done; 36 } 37 38 idle: 39 cpu_do_idle(); 40 done: 41 atomic_dec(&master); 42 43 return index; 44 } 45 46 static struct cpuidle_driver imx6q_cpuidle_driver = { 47 .name = "imx6q_cpuidle", 48 .owner = THIS_MODULE, 49 .states = { 50 /* WFI */ 51 ARM_CPUIDLE_WFI_STATE, 52 /* WAIT */ 53 { 54 .exit_latency = 50, 55 .target_residency = 75, 56 .flags = CPUIDLE_FLAG_TIME_VALID | 57 CPUIDLE_FLAG_TIMER_STOP, 58 .enter = imx6q_enter_wait, 59 .name = "WAIT", 60 .desc = "Clock off", 61 }, 62 }, 63 .state_count = 2, 64 .safe_state_index = 0, 65 }; 66 67 int __init imx6q_cpuidle_init(void) 68 { 69 /* Need to enable SCU standby for entering WAIT modes */ 70 if (!cpu_is_imx6sx()) 71 imx_scu_standby_enable(); 72 73 /* Set INT_MEM_CLK_LPM bit to get a reliable WAIT mode support */ 74 imx6q_set_int_mem_clk_lpm(true); 75 76 return cpuidle_register(&imx6q_cpuidle_driver, NULL); 77 } 78