Skip to content

Commit 8fe3f1c

Browse files
committed
Merge branch 'bits/010-soc' into asahi-wip
2 parents 6a8058c + cfefd84 commit 8fe3f1c

11 files changed

Lines changed: 458 additions & 16 deletions

File tree

Documentation/devicetree/bindings/power/apple,pmgr-pwrstate.yaml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,18 @@ properties:
7575
minimum: 0
7676
maximum: 15
7777

78+
apple,force-disable:
79+
description:
80+
Forces this device to be disabled (bus access blocked) when the power
81+
domain is powered down.
82+
type: boolean
83+
84+
apple,force-reset:
85+
description:
86+
Forces a reset/error recovery of the power control logic when the power
87+
domain is powered down.
88+
type: boolean
89+
7890
required:
7991
- compatible
8092
- reg

drivers/cpuidle/Kconfig.arm

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,3 +130,11 @@ config ARM_QCOM_SPM_CPUIDLE
130130
The Subsystem Power Manager (SPM) controls low power modes for the
131131
CPU and L2 cores. It interface with various system drivers to put
132132
the cores in low power modes.
133+
134+
config ARM_APPLE_CPUIDLE
135+
bool "Apple SoC CPU idle driver"
136+
depends on ARM64
137+
default ARCH_APPLE
138+
select CPU_IDLE_MULTIPLE_DRIVERS
139+
help
140+
Select this to enable cpuidle on Apple SoCs.

drivers/cpuidle/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ obj-$(CONFIG_ARM_PSCI_CPUIDLE) += cpuidle-psci.o
2929
obj-$(CONFIG_ARM_PSCI_CPUIDLE_DOMAIN) += cpuidle-psci-domain.o
3030
obj-$(CONFIG_ARM_TEGRA_CPUIDLE) += cpuidle-tegra.o
3131
obj-$(CONFIG_ARM_QCOM_SPM_CPUIDLE) += cpuidle-qcom-spm.o
32+
obj-$(CONFIG_ARM_APPLE_CPUIDLE) += cpuidle-apple.o
3233

3334
###############################################################################
3435
# MIPS drivers

drivers/cpuidle/cpuidle-apple.c

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
// SPDX-License-Identifier: GPL-2.0-only OR MIT
2+
/*
3+
* Copyright The Asahi Linux Contributors
4+
*
5+
* CPU idle support for Apple SoCs
6+
*/
7+
8+
#include <linux/init.h>
9+
#include <linux/bitfield.h>
10+
#include <linux/cpuidle.h>
11+
#include <linux/cpu_pm.h>
12+
#include <linux/platform_device.h>
13+
#include <linux/of.h>
14+
#include <asm/cpuidle.h>
15+
16+
#define DEEP_WFI_STATE_RETENTION BIT(2) // retains base CPU registers in deep WFI
17+
18+
enum idle_state {
19+
STATE_WFI,
20+
STATE_PWRDOWN,
21+
STATE_COUNT
22+
};
23+
24+
asm(
25+
".pushsection .cpuidle.text, \"ax\"\n"
26+
".type apple_cpu_deep_wfi, @function\n"
27+
"apple_cpu_deep_wfi:\n"
28+
"str x30, [sp, #-16]!\n"
29+
"stp x28, x29, [sp, #-16]!\n"
30+
"stp x26, x27, [sp, #-16]!\n"
31+
"stp x24, x25, [sp, #-16]!\n"
32+
"stp x22, x23, [sp, #-16]!\n"
33+
"stp x20, x21, [sp, #-16]!\n"
34+
"stp x18, x19, [sp, #-16]!\n"
35+
36+
"mrs x0, s3_5_c15_c5_0\n"
37+
"orr x0, x0, #(3L << 24)\n"
38+
"msr s3_5_c15_c5_0, x0\n"
39+
40+
"1:\n"
41+
"dsb sy\n"
42+
"wfi\n"
43+
44+
"mrs x0, ISR_EL1\n"
45+
"cbz x0, 1b\n"
46+
47+
"mrs x0, s3_5_c15_c5_0\n"
48+
"bic x0, x0, #(1L << 24)\n"
49+
"msr s3_5_c15_c5_0, x0\n"
50+
51+
"ldp x18, x19, [sp], #16\n"
52+
"ldp x20, x21, [sp], #16\n"
53+
"ldp x22, x23, [sp], #16\n"
54+
"ldp x24, x25, [sp], #16\n"
55+
"ldp x26, x27, [sp], #16\n"
56+
"ldp x28, x29, [sp], #16\n"
57+
"ldr x30, [sp], #16\n"
58+
59+
"ret\n"
60+
".popsection\n"
61+
);
62+
63+
void apple_cpu_deep_wfi(void);
64+
65+
static __cpuidle int apple_enter_wfi(struct cpuidle_device *dev, struct cpuidle_driver *drv, int index)
66+
{
67+
cpu_do_idle();
68+
return index;
69+
}
70+
71+
static __cpuidle int apple_enter_idle(struct cpuidle_device *dev, struct cpuidle_driver *drv, int index)
72+
{
73+
/*
74+
* Deep WFI will clobber FP state, among other things.
75+
* The CPU PM notifier will take care of saving that and anything else
76+
* that needs to be notified of the CPU powering down.
77+
*/
78+
if (cpu_pm_enter())
79+
return -1;
80+
81+
ct_cpuidle_enter();
82+
83+
switch(index) {
84+
case STATE_PWRDOWN:
85+
apple_cpu_deep_wfi();
86+
break;
87+
default:
88+
WARN_ON(1);
89+
break;
90+
}
91+
92+
ct_cpuidle_exit();
93+
94+
cpu_pm_exit();
95+
96+
return index;
97+
}
98+
99+
static struct cpuidle_driver apple_idle_driver = {
100+
.name = "apple_idle",
101+
.owner = THIS_MODULE,
102+
.states = {
103+
[STATE_WFI] = {
104+
.enter = apple_enter_wfi,
105+
.enter_s2idle = apple_enter_wfi,
106+
.exit_latency = 1,
107+
.target_residency = 1,
108+
.power_usage = UINT_MAX,
109+
.name = "WFI",
110+
.desc = "CPU clock-gated",
111+
.flags = 0,
112+
},
113+
[STATE_PWRDOWN] = {
114+
.enter = apple_enter_idle,
115+
.enter_s2idle = apple_enter_idle,
116+
.exit_latency = 10,
117+
.target_residency = 10000,
118+
.power_usage = 0,
119+
.name = "CPU PD",
120+
.desc = "CPU/cluster powered down",
121+
.flags = CPUIDLE_FLAG_RCU_IDLE,
122+
},
123+
},
124+
.safe_state_index = STATE_WFI,
125+
.state_count = STATE_COUNT,
126+
};
127+
128+
static int apple_cpuidle_probe(struct platform_device *pdev)
129+
{
130+
return cpuidle_register(&apple_idle_driver, NULL);
131+
}
132+
133+
static struct platform_driver apple_cpuidle_driver = {
134+
.driver = {
135+
.name = "cpuidle-apple",
136+
},
137+
.probe = apple_cpuidle_probe,
138+
};
139+
140+
static int __init apple_cpuidle_init(void)
141+
{
142+
struct platform_device *pdev;
143+
int ret;
144+
145+
ret = platform_driver_register(&apple_cpuidle_driver);
146+
if (ret)
147+
return ret;
148+
149+
if (!of_machine_is_compatible("apple,arm-platform"))
150+
return 0;
151+
152+
if (!FIELD_GET(DEEP_WFI_STATE_RETENTION, read_sysreg(aidr_el1))) {
153+
pr_info("cpuidle-apple: CPU does not retain state in deep WFI\n");
154+
return 0;
155+
}
156+
157+
pdev = platform_device_register_simple("cpuidle-apple", -1, NULL, 0);
158+
if (IS_ERR(pdev)) {
159+
platform_driver_unregister(&apple_cpuidle_driver);
160+
return PTR_ERR(pdev);
161+
}
162+
163+
return 0;
164+
}
165+
device_initcall(apple_cpuidle_init);

drivers/irqchip/irq-apple-aic.c

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
#include <linux/irqdomain.h>
5555
#include <linux/jump_label.h>
5656
#include <linux/limits.h>
57+
#include <linux/of.h>
5758
#include <linux/of_address.h>
5859
#include <linux/slab.h>
5960
#include <asm/apple_m1_pmu.h>
@@ -251,6 +252,9 @@ struct aic_info {
251252
u32 mask_set;
252253
u32 mask_clr;
253254

255+
u32 cap0_off;
256+
u32 maxnumirq_off;
257+
254258
u32 die_stride;
255259

256260
/* Features */
@@ -288,6 +292,14 @@ static const struct aic_info aic2_info __initconst = {
288292
.version = 2,
289293

290294
.irq_cfg = AIC2_IRQ_CFG,
295+
.cap0_off = AIC2_INFO1,
296+
.maxnumirq_off = AIC2_INFO3,
297+
298+
.fast_ipi = true,
299+
};
300+
301+
static const struct aic_info aic3_info __initconst = {
302+
.version = 3,
291303

292304
.fast_ipi = true,
293305
.local_fast_ipi = true,
@@ -310,6 +322,10 @@ static const struct of_device_id aic_info_match[] = {
310322
.compatible = "apple,aic2",
311323
.data = &aic2_info,
312324
},
325+
{
326+
.compatible = "apple,aic3",
327+
.data = &aic3_info,
328+
},
313329
{}
314330
};
315331

@@ -624,7 +640,7 @@ static int aic_irq_domain_map(struct irq_domain *id, unsigned int irq,
624640
u32 type = FIELD_GET(AIC_EVENT_TYPE, hw);
625641
struct irq_chip *chip = &aic_chip;
626642

627-
if (ic->info.version == 2)
643+
if (ic->info.version == 2 || ic->info.version == 3)
628644
chip = &aic2_chip;
629645

630646
if (type == AIC_EVENT_TYPE_IRQ) {
@@ -931,6 +947,7 @@ static void build_fiq_affinity(struct aic_irq_chip *ic, struct device_node *aff)
931947

932948
static int __init aic_of_ic_init(struct device_node *node, struct device_node *parent)
933949
{
950+
int ret;
934951
int i, die;
935952
u32 off, start_off;
936953
void __iomem *regs;
@@ -974,11 +991,24 @@ static int __init aic_of_ic_init(struct device_node *node, struct device_node *p
974991

975992
break;
976993
}
994+
case 3:
995+
/* read offsets from device tree for aic version 3 */
996+
/* extint-baseaddress? */
997+
ret = of_property_read_u32(node, "config-offset", &irqc->info.irq_cfg);
998+
if (ret < 0)
999+
return ret;
1000+
ret = of_property_read_u32(node, "cap0-offset", &irqc->info.cap0_off);
1001+
if (ret < 0)
1002+
return ret;
1003+
ret = of_property_read_u32(node, "maxnumirq-offset", &irqc->info.maxnumirq_off);
1004+
if (ret < 0)
1005+
return ret;
1006+
fallthrough;
9771007
case 2: {
9781008
u32 info1, info3;
9791009

980-
info1 = aic_ic_read(irqc, AIC2_INFO1);
981-
info3 = aic_ic_read(irqc, AIC2_INFO3);
1010+
info1 = aic_ic_read(irqc, irqc->info.cap0_off);
1011+
info3 = aic_ic_read(irqc, irqc->info.maxnumirq_off);
9821012

9831013
irqc->nr_irq = FIELD_GET(AIC2_INFO1_NR_IRQ, info1);
9841014
irqc->max_irq = FIELD_GET(AIC2_INFO3_MAX_IRQ, info3);
@@ -1048,7 +1078,7 @@ static int __init aic_of_ic_init(struct device_node *node, struct device_node *p
10481078
off += irqc->info.die_stride;
10491079
}
10501080

1051-
if (irqc->info.version == 2) {
1081+
if (irqc->info.version == 2 || irqc->info.version == 3) {
10521082
u32 config = aic_ic_read(irqc, AIC2_CONFIG);
10531083

10541084
config |= AIC2_CONFIG_ENABLE;
@@ -1099,3 +1129,4 @@ static int __init aic_of_ic_init(struct device_node *node, struct device_node *p
10991129

11001130
IRQCHIP_DECLARE(apple_aic, "apple,aic", aic_of_ic_init);
11011131
IRQCHIP_DECLARE(apple_aic2, "apple,aic2", aic_of_ic_init);
1132+
IRQCHIP_DECLARE(apple_aic3, "apple,aic3", aic_of_ic_init);

0 commit comments

Comments
 (0)