Linux Audio

Check our new training course

Loading...
v6.13.7
  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);
v5.4
  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);