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
arm_cpuidle_init(unsigned int cpu)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 * @index: 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 */
arm_cpuidle_suspend(int index)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
psci_acpi_cpu_init_idle(unsigned int cpu)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 if (unlikely(!pr || !pr->flags.has_lpi))
58 return -EINVAL;
59
60 /*
61 * If the PSCI cpu_suspend function hook has not been initialized
62 * idle states must not be enabled, so bail out
63 */
64 if (!psci_ops.cpu_suspend)
65 return -EOPNOTSUPP;
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
acpi_processor_ffh_lpi_probe(unsigned int cpu)89 int acpi_processor_ffh_lpi_probe(unsigned int cpu)
90 {
91 return psci_acpi_cpu_init_idle(cpu);
92 }
93
acpi_processor_ffh_lpi_enter(struct acpi_lpi_state * lpi)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