^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 Linaro : Daniel Lezcano <daniel.lezcano@linaro.org> (IBM)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) * Based on the work of Rickard Andersson <rickard.andersson@stericsson.com>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) * and Jonas Aaberg <jonas.aberg@stericsson.com>.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) #include <linux/init.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) #include <linux/cpuidle.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) #include <linux/spinlock.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) #include <linux/atomic.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) #include <linux/smp.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) #include <linux/mfd/dbx500-prcmu.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) #include <linux/platform_data/arm-ux500-pm.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) #include <linux/platform_device.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) #include <asm/cpuidle.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) static atomic_t master = ATOMIC_INIT(0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) static DEFINE_SPINLOCK(master_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) static inline int ux500_enter_idle(struct cpuidle_device *dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) struct cpuidle_driver *drv, int index)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) int this_cpu = smp_processor_id();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) bool recouple = false;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) if (atomic_inc_return(&master) == num_online_cpus()) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) /* With this lock, we prevent the other cpu to exit and enter
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) * this function again and become the master */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) if (!spin_trylock(&master_lock))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) goto wfi;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) /* decouple the gic from the A9 cores */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) if (prcmu_gic_decouple()) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) spin_unlock(&master_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) goto out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) /* If an error occur, we will have to recouple the gic
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) * manually */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) recouple = true;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) /* At this state, as the gic is decoupled, if the other
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) * cpu is in WFI, we have the guarantee it won't be wake
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) * up, so we can safely go to retention */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) if (!prcmu_is_cpu_in_wfi(this_cpu ? 0 : 1))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) goto out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) /* The prcmu will be in charge of watching the interrupts
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) * and wake up the cpus */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) if (prcmu_copy_gic_settings())
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) goto out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) /* Check in the meantime an interrupt did
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) * not occur on the gic ... */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) if (prcmu_gic_pending_irq())
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) goto out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) /* ... and the prcmu */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) if (prcmu_pending_irq())
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) goto out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) /* Go to the retention state, the prcmu will wait for the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) * cpu to go WFI and this is what happens after exiting this
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) * 'master' critical section */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) if (prcmu_set_power_state(PRCMU_AP_IDLE, true, true))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) goto out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) /* When we switch to retention, the prcmu is in charge
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) * of recoupling the gic automatically */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) recouple = false;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) spin_unlock(&master_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) wfi:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) cpu_do_idle();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) out:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) atomic_dec(&master);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) if (recouple) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) prcmu_gic_recouple();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) spin_unlock(&master_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) return index;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) static struct cpuidle_driver ux500_idle_driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) .name = "ux500_idle",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) .owner = THIS_MODULE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) .states = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) ARM_CPUIDLE_WFI_STATE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) .enter = ux500_enter_idle,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) .exit_latency = 70,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) .target_residency = 260,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) .flags = CPUIDLE_FLAG_TIMER_STOP,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) .name = "ApIdle",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) .desc = "ARM Retention",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) .safe_state_index = 0,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) .state_count = 2,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) static int dbx500_cpuidle_probe(struct platform_device *pdev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) /* Configure wake up reasons */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) prcmu_enable_wakeups(PRCMU_WAKEUP(ARM) | PRCMU_WAKEUP(RTC) |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) PRCMU_WAKEUP(ABB));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) return cpuidle_register(&ux500_idle_driver, NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) static struct platform_driver dbx500_cpuidle_plat_driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) .driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) .name = "cpuidle-dbx500",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) .probe = dbx500_cpuidle_probe,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) builtin_platform_driver(dbx500_cpuidle_plat_driver);