Linux Audio

Check our new training course

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