2874c5fd28426 (Thomas Gleixner 2019-05-27 08:55:01 +0200 1) // SPDX-License-Identifier: GPL-2.0-or-later
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 2) /*
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 3) * Copyright (C) 2014 Imagination Technologies
fb615d61b5583 (Paul Burton 2017-10-25 17:04:33 -0700 4) * Author: Paul Burton <paul.burton@mips.com>
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 5) */
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 6)
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 7) #include <linux/cpu_pm.h>
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 8) #include <linux/cpuidle.h>
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 9) #include <linux/init.h>
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 10)
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 11) #include <asm/idle.h>
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 12) #include <asm/pm-cps.h>
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 13)
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 14) /* Enumeration of the various idle states this driver may enter */
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 15) enum cps_idle_state {
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 16) STATE_WAIT = 0, /* MIPS wait instruction, coherent */
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 17) STATE_NC_WAIT, /* MIPS wait instruction, non-coherent */
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 18) STATE_CLOCK_GATED, /* Core clock gated */
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 19) STATE_POWER_GATED, /* Core power gated */
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 20) STATE_COUNT
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 21) };
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 22)
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 23) static int cps_nc_enter(struct cpuidle_device *dev,
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 24) struct cpuidle_driver *drv, int index)
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 25) {
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 26) enum cps_pm_state pm_state;
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 27) int err;
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 28)
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 29) /*
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 30) * At least one core must remain powered up & clocked in order for the
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 31) * system to have any hope of functioning.
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 32) *
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 33) * TODO: don't treat core 0 specially, just prevent the final core
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 34) * TODO: remap interrupt affinity temporarily
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 35) */
fe7a38c625a2e (Paul Burton 2017-08-12 19:49:37 -0700 36) if (cpus_are_siblings(0, dev->cpu) && (index > STATE_NC_WAIT))
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 37) index = STATE_NC_WAIT;
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 38)
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 39) /* Select the appropriate cps_pm_state */
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 40) switch (index) {
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 41) case STATE_NC_WAIT:
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 42) pm_state = CPS_PM_NC_WAIT;
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 43) break;
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 44) case STATE_CLOCK_GATED:
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 45) pm_state = CPS_PM_CLOCK_GATED;
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 46) break;
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 47) case STATE_POWER_GATED:
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 48) pm_state = CPS_PM_POWER_GATED;
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 49) break;
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 50) default:
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 51) BUG();
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 52) return -EINVAL;
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 53) }
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 54)
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 55) /* Notify listeners the CPU is about to power down */
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 56) if ((pm_state == CPS_PM_POWER_GATED) && cpu_pm_enter())
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 57) return -EINTR;
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 58)
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 59) /* Enter that state */
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 60) err = cps_pm_enter_state(pm_state);
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 61)
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 62) /* Notify listeners the CPU is back up */
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 63) if (pm_state == CPS_PM_POWER_GATED)
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 64) cpu_pm_exit();
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 65)
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 66) return err ?: index;
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 67) }
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 68)
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 69) static struct cpuidle_driver cps_driver = {
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 70) .name = "cpc_cpuidle",
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 71) .owner = THIS_MODULE,
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 72) .states = {
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 73) [STATE_WAIT] = MIPS_CPUIDLE_WAIT_STATE,
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 74) [STATE_NC_WAIT] = {
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 75) .enter = cps_nc_enter,
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 76) .exit_latency = 200,
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 77) .target_residency = 450,
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 78) .name = "nc-wait",
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 79) .desc = "non-coherent MIPS wait",
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 80) },
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 81) [STATE_CLOCK_GATED] = {
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 82) .enter = cps_nc_enter,
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 83) .exit_latency = 300,
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 84) .target_residency = 700,
b82b6cca48807 (Daniel Lezcano 2014-11-12 16:03:50 +0100 85) .flags = CPUIDLE_FLAG_TIMER_STOP,
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 86) .name = "clock-gated",
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 87) .desc = "core clock gated",
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 88) },
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 89) [STATE_POWER_GATED] = {
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 90) .enter = cps_nc_enter,
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 91) .exit_latency = 600,
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 92) .target_residency = 1000,
b82b6cca48807 (Daniel Lezcano 2014-11-12 16:03:50 +0100 93) .flags = CPUIDLE_FLAG_TIMER_STOP,
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 94) .name = "power-gated",
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 95) .desc = "core power gated",
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 96) },
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 97) },
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 98) .state_count = STATE_COUNT,
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 99) .safe_state_index = 0,
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 100) };
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 101)
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 102) static void __init cps_cpuidle_unregister(void)
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 103) {
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 104) int cpu;
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 105) struct cpuidle_device *device;
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 106)
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 107) for_each_possible_cpu(cpu) {
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 108) device = &per_cpu(cpuidle_dev, cpu);
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 109) cpuidle_unregister_device(device);
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 110) }
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 111)
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 112) cpuidle_unregister_driver(&cps_driver);
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 113) }
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 114)
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 115) static int __init cps_cpuidle_init(void)
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 116) {
02018b3929a23 (Marcin Nowakowski 2017-04-19 13:20:54 +0200 117) int err, cpu, i;
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 118) struct cpuidle_device *device;
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 119)
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 120) /* Detect supported states */
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 121) if (!cps_pm_support_state(CPS_PM_POWER_GATED))
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 122) cps_driver.state_count = STATE_CLOCK_GATED + 1;
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 123) if (!cps_pm_support_state(CPS_PM_CLOCK_GATED))
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 124) cps_driver.state_count = STATE_NC_WAIT + 1;
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 125) if (!cps_pm_support_state(CPS_PM_NC_WAIT))
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 126) cps_driver.state_count = STATE_WAIT + 1;
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 127)
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 128) /* Inform the user if some states are unavailable */
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 129) if (cps_driver.state_count < STATE_COUNT) {
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 130) pr_info("cpuidle-cps: limited to ");
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 131) switch (cps_driver.state_count - 1) {
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 132) case STATE_WAIT:
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 133) pr_cont("coherent wait\n");
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 134) break;
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 135) case STATE_NC_WAIT:
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 136) pr_cont("non-coherent wait\n");
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 137) break;
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 138) case STATE_CLOCK_GATED:
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 139) pr_cont("clock gating\n");
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 140) break;
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 141) }
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 142) }
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 143)
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 144) /*
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 145) * Set the coupled flag on the appropriate states if this system
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 146) * requires it.
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 147) */
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 148) if (coupled_coherence)
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 149) for (i = STATE_NC_WAIT; i < cps_driver.state_count; i++)
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 150) cps_driver.states[i].flags |= CPUIDLE_FLAG_COUPLED;
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 151)
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 152) err = cpuidle_register_driver(&cps_driver);
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 153) if (err) {
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 154) pr_err("Failed to register CPS cpuidle driver\n");
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 155) return err;
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 156) }
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 157)
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 158) for_each_possible_cpu(cpu) {
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 159) device = &per_cpu(cpuidle_dev, cpu);
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 160) device->cpu = cpu;
72bc8c75eaf72 (Matt Redfearn 2016-09-07 10:45:20 +0100 161) #ifdef CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 162) cpumask_copy(&device->coupled_cpus, &cpu_sibling_map[cpu]);
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 163) #endif
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 164)
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 165) err = cpuidle_register_device(device);
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 166) if (err) {
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 167) pr_err("Failed to register CPU%d cpuidle device\n",
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 168) cpu);
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 169) goto err_out;
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 170) }
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 171) }
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 172)
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 173) return 0;
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 174) err_out:
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 175) cps_cpuidle_unregister();
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 176) return err;
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 177) }
d050894435cdc (Paul Burton 2014-04-14 16:25:29 +0100 178) device_initcall(cps_cpuidle_init);