1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (C) 2024, Ventana Micro Systems Inc |
4 | * Author: Sunil V L <sunilvl@ventanamicro.com> |
5 | * |
6 | */ |
7 | |
8 | #include <linux/acpi.h> |
9 | #include <acpi/processor.h> |
10 | #include <linux/cpu_pm.h> |
11 | #include <linux/cpuidle.h> |
12 | #include <linux/suspend.h> |
13 | #include <asm/cpuidle.h> |
14 | #include <asm/sbi.h> |
15 | #include <asm/suspend.h> |
16 | |
17 | #define RISCV_FFH_LPI_TYPE_MASK GENMASK_ULL(63, 60) |
18 | #define RISCV_FFH_LPI_RSVD_MASK GENMASK_ULL(59, 32) |
19 | |
20 | #define RISCV_FFH_LPI_TYPE_SBI BIT_ULL(60) |
21 | |
22 | static int acpi_cpu_init_idle(unsigned int cpu) |
23 | { |
24 | int i; |
25 | struct acpi_lpi_state *lpi; |
26 | struct acpi_processor *pr = per_cpu(processors, cpu); |
27 | |
28 | if (unlikely(!pr || !pr->flags.has_lpi)) |
29 | return -EINVAL; |
30 | |
31 | if (!riscv_sbi_hsm_is_supported()) |
32 | return -ENODEV; |
33 | |
34 | if (pr->power.count <= 1) |
35 | return -ENODEV; |
36 | |
37 | for (i = 1; i < pr->power.count; i++) { |
38 | u32 state; |
39 | |
40 | lpi = &pr->power.lpi_states[i]; |
41 | |
42 | /* |
43 | * Validate Entry Method as per FFH spec. |
44 | * bits[63:60] should be 0x1 |
45 | * bits[59:32] should be 0x0 |
46 | * bits[31:0] represent a SBI power_state |
47 | */ |
48 | if (((lpi->address & RISCV_FFH_LPI_TYPE_MASK) != RISCV_FFH_LPI_TYPE_SBI) || |
49 | (lpi->address & RISCV_FFH_LPI_RSVD_MASK)) { |
50 | pr_warn("Invalid LPI entry method %#llx\n" , lpi->address); |
51 | return -EINVAL; |
52 | } |
53 | |
54 | state = lpi->address; |
55 | if (!riscv_sbi_suspend_state_is_valid(state)) { |
56 | pr_warn("Invalid SBI power state %#x\n" , state); |
57 | return -EINVAL; |
58 | } |
59 | } |
60 | |
61 | return 0; |
62 | } |
63 | |
64 | int acpi_processor_ffh_lpi_probe(unsigned int cpu) |
65 | { |
66 | return acpi_cpu_init_idle(cpu); |
67 | } |
68 | |
69 | int acpi_processor_ffh_lpi_enter(struct acpi_lpi_state *lpi) |
70 | { |
71 | u32 state = lpi->address; |
72 | |
73 | if (state & SBI_HSM_SUSP_NON_RET_BIT) |
74 | return CPU_PM_CPU_IDLE_ENTER_PARAM(riscv_sbi_hart_suspend, |
75 | lpi->index, |
76 | state); |
77 | else |
78 | return CPU_PM_CPU_IDLE_ENTER_RETENTION_PARAM(riscv_sbi_hart_suspend, |
79 | lpi->index, |
80 | state); |
81 | } |
82 | |