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_TIMER_STOP, 57 .enter = imx6q_enter_wait, 58 .name = "WAIT", 59 .desc = "Clock off", 60 }, 61 }, 62 .state_count = 2, 63 .safe_state_index = 0, 64 }; 65 66 int __init imx6q_cpuidle_init(void) 67 { 68 /* Set INT_MEM_CLK_LPM bit to get a reliable WAIT mode support */ 69 imx6q_set_int_mem_clk_lpm(true); 70 71 return cpuidle_register(&imx6q_cpuidle_driver, NULL); 72 } 73