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