1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (C) 2012 Freescale Semiconductor, Inc. 4 */ 5 6 #include <linux/cpuidle.h> 7 #include <linux/module.h> 8 #include <asm/cpuidle.h> 9 10 #include <soc/imx/cpuidle.h> 11 12 #include "common.h" 13 #include "cpuidle.h" 14 #include "hardware.h" 15 16 static int num_idle_cpus = 0; 17 static DEFINE_RAW_SPINLOCK(cpuidle_lock); 18 19 static int imx6q_enter_wait(struct cpuidle_device *dev, 20 struct cpuidle_driver *drv, int index) 21 { 22 raw_spin_lock(&cpuidle_lock); 23 if (++num_idle_cpus == num_online_cpus()) 24 imx6_set_lpm(WAIT_UNCLOCKED); 25 raw_spin_unlock(&cpuidle_lock); 26 27 rcu_idle_enter(); 28 cpu_do_idle(); 29 rcu_idle_exit(); 30 31 raw_spin_lock(&cpuidle_lock); 32 if (num_idle_cpus-- == num_online_cpus()) 33 imx6_set_lpm(WAIT_CLOCKED); 34 raw_spin_unlock(&cpuidle_lock); 35 36 return index; 37 } 38 39 static struct cpuidle_driver imx6q_cpuidle_driver = { 40 .name = "imx6q_cpuidle", 41 .owner = THIS_MODULE, 42 .states = { 43 /* WFI */ 44 ARM_CPUIDLE_WFI_STATE, 45 /* WAIT */ 46 { 47 .exit_latency = 50, 48 .target_residency = 75, 49 .flags = CPUIDLE_FLAG_TIMER_STOP | CPUIDLE_FLAG_RCU_IDLE, 50 .enter = imx6q_enter_wait, 51 .name = "WAIT", 52 .desc = "Clock off", 53 }, 54 }, 55 .state_count = 2, 56 .safe_state_index = 0, 57 }; 58 59 /* 60 * i.MX6 Q/DL has an erratum (ERR006687) that prevents the FEC from waking the 61 * CPUs when they are in wait(unclocked) state. As the hardware workaround isn't 62 * applicable to all boards, disable the deeper idle state when the workaround 63 * isn't present and the FEC is in use. 64 */ 65 void imx6q_cpuidle_fec_irqs_used(void) 66 { 67 cpuidle_driver_state_disabled(&imx6q_cpuidle_driver, 1, true); 68 } 69 EXPORT_SYMBOL_GPL(imx6q_cpuidle_fec_irqs_used); 70 71 void imx6q_cpuidle_fec_irqs_unused(void) 72 { 73 cpuidle_driver_state_disabled(&imx6q_cpuidle_driver, 1, false); 74 } 75 EXPORT_SYMBOL_GPL(imx6q_cpuidle_fec_irqs_unused); 76 77 int __init imx6q_cpuidle_init(void) 78 { 79 /* Set INT_MEM_CLK_LPM bit to get a reliable WAIT mode support */ 80 imx6_set_int_mem_clk_lpm(true); 81 82 return cpuidle_register(&imx6q_cpuidle_driver, NULL); 83 } 84