cpuidle-imx6q.c (2010B)
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 16static int num_idle_cpus = 0; 17static DEFINE_RAW_SPINLOCK(cpuidle_lock); 18 19static 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 39static 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 */ 65void imx6q_cpuidle_fec_irqs_used(void) 66{ 67 cpuidle_driver_state_disabled(&imx6q_cpuidle_driver, 1, true); 68} 69EXPORT_SYMBOL_GPL(imx6q_cpuidle_fec_irqs_used); 70 71void imx6q_cpuidle_fec_irqs_unused(void) 72{ 73 cpuidle_driver_state_disabled(&imx6q_cpuidle_driver, 1, false); 74} 75EXPORT_SYMBOL_GPL(imx6q_cpuidle_fec_irqs_unused); 76 77int __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}