xref: /openbmc/linux/arch/arm64/kernel/cpuidle.c (revision 2f8be0e5)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * ARM64 CPU idle arch support
4  *
5  * Copyright (C) 2014 ARM Ltd.
6  * Author: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
7  */
8 
9 #include <linux/acpi.h>
10 #include <linux/cpuidle.h>
11 #include <linux/cpu_pm.h>
12 #include <linux/of.h>
13 #include <linux/of_device.h>
14 #include <linux/psci.h>
15 
16 #include <asm/cpuidle.h>
17 #include <asm/cpu_ops.h>
18 
19 int arm_cpuidle_init(unsigned int cpu)
20 {
21 	const struct cpu_operations *ops = get_cpu_ops(cpu);
22 	int ret = -EOPNOTSUPP;
23 
24 	if (ops && ops->cpu_suspend && ops->cpu_init_idle)
25 		ret = ops->cpu_init_idle(cpu);
26 
27 	return ret;
28 }
29 
30 /**
31  * arm_cpuidle_suspend() - function to enter a low-power idle state
32  * @arg: argument to pass to CPU suspend operations
33  *
34  * Return: 0 on success, -EOPNOTSUPP if CPU suspend hook not initialized, CPU
35  * operations back-end error code otherwise.
36  */
37 int arm_cpuidle_suspend(int index)
38 {
39 	int cpu = smp_processor_id();
40 	const struct cpu_operations *ops = get_cpu_ops(cpu);
41 
42 	return ops->cpu_suspend(index);
43 }
44 
45 #ifdef CONFIG_ACPI
46 
47 #include <acpi/processor.h>
48 
49 #define ARM64_LPI_IS_RETENTION_STATE(arch_flags) (!(arch_flags))
50 
51 static int psci_acpi_cpu_init_idle(unsigned int cpu)
52 {
53 	int i, count;
54 	struct acpi_lpi_state *lpi;
55 	struct acpi_processor *pr = per_cpu(processors, cpu);
56 
57 	/*
58 	 * If the PSCI cpu_suspend function hook has not been initialized
59 	 * idle states must not be enabled, so bail out
60 	 */
61 	if (!psci_ops.cpu_suspend)
62 		return -EOPNOTSUPP;
63 
64 	if (unlikely(!pr || !pr->flags.has_lpi))
65 		return -EINVAL;
66 
67 	count = pr->power.count - 1;
68 	if (count <= 0)
69 		return -ENODEV;
70 
71 	for (i = 0; i < count; i++) {
72 		u32 state;
73 
74 		lpi = &pr->power.lpi_states[i + 1];
75 		/*
76 		 * Only bits[31:0] represent a PSCI power_state while
77 		 * bits[63:32] must be 0x0 as per ARM ACPI FFH Specification
78 		 */
79 		state = lpi->address;
80 		if (!psci_power_state_is_valid(state)) {
81 			pr_warn("Invalid PSCI power state %#x\n", state);
82 			return -EINVAL;
83 		}
84 	}
85 
86 	return 0;
87 }
88 
89 int acpi_processor_ffh_lpi_probe(unsigned int cpu)
90 {
91 	return psci_acpi_cpu_init_idle(cpu);
92 }
93 
94 int acpi_processor_ffh_lpi_enter(struct acpi_lpi_state *lpi)
95 {
96 	u32 state = lpi->address;
97 
98 	if (ARM64_LPI_IS_RETENTION_STATE(lpi->arch_flags))
99 		return CPU_PM_CPU_IDLE_ENTER_RETENTION_PARAM(psci_cpu_suspend_enter,
100 						lpi->index, state);
101 	else
102 		return CPU_PM_CPU_IDLE_ENTER_PARAM(psci_cpu_suspend_enter,
103 					     lpi->index, state);
104 }
105 #endif
106