brintos

brintos / linux-shallow public Read only

0
0
Text · 4.2 KiB · dff0ff4 Raw
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