Skip to content

Commit c7cd129

Browse files
committed
Merge branch 'bits/010-soc' into asahi-wip
2 parents cbe3d82 + 76721d2 commit c7cd129

13 files changed

Lines changed: 599 additions & 14 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
@@ -83,6 +83,18 @@ properties:
8383
minimum: 0
8484
maximum: 15
8585

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

drivers/irqchip/irq-apple-aic.c

Lines changed: 25 additions & 2 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>
@@ -134,8 +135,15 @@
134135

135136
#define AIC2_IRQ_CFG 0x2000
136137

138+
/*
139+
* AIC v3 registers (MMIO)
140+
*/
141+
142+
#define AIC3_IRQ_CFG 0x10000
143+
137144
/*
138145
* AIC2 registers are laid out like this, starting at AIC2_IRQ_CFG:
146+
* AIC3 registers use the same layout but start at AIC3_IRQ_CFG:
139147
*
140148
* Repeat for each die:
141149
* IRQ_CFG: u32 * MAX_IRQS
@@ -293,6 +301,15 @@ static const struct aic_info aic2_info __initconst = {
293301
.local_fast_ipi = true,
294302
};
295303

304+
static const struct aic_info aic3_info __initconst = {
305+
.version = 3,
306+
307+
.irq_cfg = AIC3_IRQ_CFG,
308+
309+
.fast_ipi = true,
310+
.local_fast_ipi = true,
311+
};
312+
296313
static const struct of_device_id aic_info_match[] = {
297314
{
298315
.compatible = "apple,t8103-aic",
@@ -310,6 +327,10 @@ static const struct of_device_id aic_info_match[] = {
310327
.compatible = "apple,aic2",
311328
.data = &aic2_info,
312329
},
330+
{
331+
.compatible = "apple,t8122-aic3",
332+
.data = &aic3_info,
333+
},
313334
{}
314335
};
315336

@@ -624,7 +645,7 @@ static int aic_irq_domain_map(struct irq_domain *id, unsigned int irq,
624645
u32 type = FIELD_GET(AIC_EVENT_TYPE, hw);
625646
struct irq_chip *chip = &aic_chip;
626647

627-
if (ic->info.version == 2)
648+
if (ic->info.version == 2 || ic->info.version == 3)
628649
chip = &aic2_chip;
629650

630651
if (type == AIC_EVENT_TYPE_IRQ) {
@@ -974,6 +995,7 @@ static int __init aic_of_ic_init(struct device_node *node, struct device_node *p
974995

975996
break;
976997
}
998+
case 3:
977999
case 2: {
9781000
u32 info1, info3;
9791001

@@ -1048,7 +1070,7 @@ static int __init aic_of_ic_init(struct device_node *node, struct device_node *p
10481070
off += irqc->info.die_stride;
10491071
}
10501072

1051-
if (irqc->info.version == 2) {
1073+
if (irqc->info.version == 2 || irqc->info.version == 3) {
10521074
u32 config = aic_ic_read(irqc, AIC2_CONFIG);
10531075

10541076
config |= AIC2_CONFIG_ENABLE;
@@ -1099,3 +1121,4 @@ static int __init aic_of_ic_init(struct device_node *node, struct device_node *p
10991121

11001122
IRQCHIP_DECLARE(apple_aic, "apple,aic", aic_of_ic_init);
11011123
IRQCHIP_DECLARE(apple_aic2, "apple,aic2", aic_of_ic_init);
1124+
IRQCHIP_DECLARE(apple_aic3, "apple,t8122-aic3", aic_of_ic_init);

drivers/pmdomain/apple/pmgr-pwrstate.c

Lines changed: 54 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@
2121
#define APPLE_PMGR_AUTO_ENABLE BIT(28)
2222
#define APPLE_PMGR_PS_AUTO GENMASK(27, 24)
2323
#define APPLE_PMGR_PS_MIN GENMASK(19, 16)
24-
#define APPLE_PMGR_PARENT_OFF BIT(11)
24+
#define APPLE_PMGR_PS_RESET BIT(12)
25+
#define APPLE_PMGR_BUSY BIT(11)
2526
#define APPLE_PMGR_DEV_DISABLE BIT(10)
2627
#define APPLE_PMGR_WAS_CLKGATED BIT(9)
2728
#define APPLE_PMGR_WAS_PWRGATED BIT(8)
@@ -44,6 +45,9 @@ struct apple_pmgr_ps {
4445
struct regmap *regmap;
4546
u32 offset;
4647
u32 min_state;
48+
bool force_disable;
49+
bool force_reset;
50+
bool externally_clocked;
4751
};
4852

4953
#define genpd_to_apple_pmgr_ps(_genpd) container_of(_genpd, struct apple_pmgr_ps, genpd)
@@ -53,7 +57,7 @@ static int apple_pmgr_ps_set(struct generic_pm_domain *genpd, u32 pstate, bool a
5357
{
5458
int ret;
5559
struct apple_pmgr_ps *ps = genpd_to_apple_pmgr_ps(genpd);
56-
u32 reg;
60+
u32 reg, cur;
5761

5862
ret = regmap_read(ps->regmap, ps->offset, &reg);
5963
if (ret < 0)
@@ -64,24 +68,57 @@ static int apple_pmgr_ps_set(struct generic_pm_domain *genpd, u32 pstate, bool a
6468
dev_err(ps->dev, "PS %s: powering off with RESET active\n",
6569
genpd->name);
6670

67-
reg &= ~(APPLE_PMGR_AUTO_ENABLE | APPLE_PMGR_FLAGS | APPLE_PMGR_PS_TARGET);
71+
if (pstate != APPLE_PMGR_PS_ACTIVE && (ps->force_disable || ps->force_reset)) {
72+
u32 reg_pre = reg & ~(APPLE_PMGR_AUTO_ENABLE | APPLE_PMGR_FLAGS);
73+
74+
if (ps->force_disable)
75+
reg_pre |= APPLE_PMGR_DEV_DISABLE;
76+
if (ps->force_reset)
77+
reg_pre |= APPLE_PMGR_PS_RESET;
78+
79+
regmap_write(ps->regmap, ps->offset, reg_pre);
80+
81+
ret = regmap_read_poll_timeout_atomic(
82+
ps->regmap, ps->offset, cur,
83+
(cur & (APPLE_PMGR_DEV_DISABLE | APPLE_PMGR_PS_RESET)) ==
84+
(reg_pre & (APPLE_PMGR_DEV_DISABLE | APPLE_PMGR_PS_RESET)), 1,
85+
APPLE_PMGR_PS_SET_TIMEOUT);
86+
87+
if (ret < 0)
88+
dev_err(ps->dev, "PS %s: Failed to set reset/disable bits (now: 0x%x)\n",
89+
genpd->name, reg);
90+
}
91+
92+
reg &= ~(APPLE_PMGR_DEV_DISABLE | APPLE_PMGR_PS_RESET |
93+
APPLE_PMGR_AUTO_ENABLE | APPLE_PMGR_FLAGS | APPLE_PMGR_PS_TARGET);
6894
reg |= FIELD_PREP(APPLE_PMGR_PS_TARGET, pstate);
6995

7096
dev_dbg(ps->dev, "PS %s: pwrstate = 0x%x: 0x%x\n", genpd->name, pstate, reg);
7197

7298
regmap_write(ps->regmap, ps->offset, reg);
7399

74-
ret = regmap_read_poll_timeout_atomic(
75-
ps->regmap, ps->offset, reg,
76-
(FIELD_GET(APPLE_PMGR_PS_ACTUAL, reg) == pstate), 1,
77-
APPLE_PMGR_PS_SET_TIMEOUT);
100+
if (ps->externally_clocked && pstate == APPLE_PMGR_PS_ACTIVE) {
101+
/*
102+
* If this clock domain requires an external clock, then
103+
* consider the "clock gated" state to be good enough.
104+
*/
105+
ret = regmap_read_poll_timeout_atomic(
106+
ps->regmap, ps->offset, cur,
107+
FIELD_GET(APPLE_PMGR_PS_ACTUAL, cur) >= APPLE_PMGR_PS_CLKGATE, 1,
108+
APPLE_PMGR_PS_SET_TIMEOUT);
109+
} else {
110+
ret = regmap_read_poll_timeout_atomic(
111+
ps->regmap, ps->offset, cur,
112+
FIELD_GET(APPLE_PMGR_PS_ACTUAL, cur) == pstate, 1,
113+
APPLE_PMGR_PS_SET_TIMEOUT);
114+
}
115+
78116
if (ret < 0)
79117
dev_err(ps->dev, "PS %s: Failed to reach power state 0x%x (now: 0x%x)\n",
80118
genpd->name, pstate, reg);
81119

82120
if (auto_enable) {
83121
/* Not all devices implement this; this is a no-op where not implemented. */
84-
reg &= ~APPLE_PMGR_FLAGS;
85122
reg |= APPLE_PMGR_AUTO_ENABLE;
86123
regmap_write(ps->regmap, ps->offset, reg);
87124
}
@@ -234,6 +271,15 @@ static int apple_pmgr_ps_probe(struct platform_device *pdev)
234271
regmap_update_bits(regmap, ps->offset, APPLE_PMGR_FLAGS | APPLE_PMGR_PS_MIN,
235272
FIELD_PREP(APPLE_PMGR_PS_MIN, ps->min_state));
236273

274+
if (of_property_read_bool(node, "apple,force-disable"))
275+
ps->force_disable = true;
276+
277+
if (of_property_read_bool(node, "apple,force-reset"))
278+
ps->force_reset = true;
279+
280+
if (of_property_read_bool(node, "apple,externally-clocked"))
281+
ps->externally_clocked = true;
282+
237283
active = apple_pmgr_ps_is_active(ps);
238284
if (of_property_read_bool(node, "apple,always-on")) {
239285
ps->genpd.flags |= GENPD_FLAG_ALWAYS_ON;

0 commit comments

Comments
 (0)