179 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Copyright (C) 2014 Imagination Technologies4 * 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_COUNT21};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 the31 * system to have any hope of functioning.32 *33 * TODO: don't treat core 0 specially, just prevent the final core34 * TODO: remap interrupt affinity temporarily35 */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 system146 * 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_COUPLED162 cpumask_copy(&device->coupled_cpus, &cpu_sibling_map[cpu]);163#endif164 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);179