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