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 cpu_do_idle(); 28 29 raw_spin_lock(&cpuidle_lock); 30 if (num_idle_cpus-- == num_online_cpus()) 31 imx6_set_lpm(WAIT_CLOCKED); 32 raw_spin_unlock(&cpuidle_lock); 33 34 return index; 35 } 36 37 static struct cpuidle_driver imx6q_cpuidle_driver = { 38 .name = "imx6q_cpuidle", 39 .owner = THIS_MODULE, 40 .states = { 41 /* WFI */ 42 ARM_CPUIDLE_WFI_STATE, 43 /* WAIT */ 44 { 45 .exit_latency = 50, 46 .target_residency = 75, 47 .flags = CPUIDLE_FLAG_TIMER_STOP, 48 .enter = imx6q_enter_wait, 49 .name = "WAIT", 50 .desc = "Clock off", 51 }, 52 }, 53 .state_count = 2, 54 .safe_state_index = 0, 55 }; 56 57 /* 58 * i.MX6 Q/DL has an erratum (ERR006687) that prevents the FEC from waking the 59 * CPUs when they are in wait(unclocked) state. As the hardware workaround isn't 60 * applicable to all boards, disable the deeper idle state when the workaround 61 * isn't present and the FEC is in use. 62 */ 63 void imx6q_cpuidle_fec_irqs_used(void) 64 { 65 cpuidle_driver_state_disabled(&imx6q_cpuidle_driver, 1, true); 66 } 67 EXPORT_SYMBOL_GPL(imx6q_cpuidle_fec_irqs_used); 68 69 void imx6q_cpuidle_fec_irqs_unused(void) 70 { 71 cpuidle_driver_state_disabled(&imx6q_cpuidle_driver, 1, false); 72 } 73 EXPORT_SYMBOL_GPL(imx6q_cpuidle_fec_irqs_unused); 74 75 int __init imx6q_cpuidle_init(void) 76 { 77 /* Set INT_MEM_CLK_LPM bit to get a reliable WAIT mode support */ 78 imx6_set_int_mem_clk_lpm(true); 79 80 return cpuidle_register(&imx6q_cpuidle_driver, NULL); 81 } 82