diff --git a/Documentation/devicetree/bindings/soc/apple/apple,t8140-sleep.yaml b/Documentation/devicetree/bindings/soc/apple/apple,t8140-sleep.yaml index adcd550f3d39a..3498a9e913cdc 100644 --- a/Documentation/devicetree/bindings/soc/apple/apple,t8140-sleep.yaml +++ b/Documentation/devicetree/bindings/soc/apple/apple,t8140-sleep.yaml @@ -4,20 +4,22 @@ $id: http://devicetree.org/schemas/soc/apple/apple,t8140-sleep.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: Apple T8140 system sleep controls +title: Apple SoC system sleep controls maintainers: - Diljit Singh description: | - The registers a kernel running directly at EL2 on T8140 writes to enter and - return from suspend-to-RAM and to power its secondary CPUs off. They belong to - several hardware blocks, some of which other drivers also use, so this node + The registers a kernel running directly at EL2 on an Apple SoC writes to enter + and return from suspend-to-RAM and to power its secondary CPUs off. They belong + to several hardware blocks, some of which other drivers also use, so this node overlaps those drivers' register ranges and the driver does not claim them. + Each SoC has its own compatible, as its power domains differ. properties: compatible: - const: apple,t8140-sleep + enum: + - apple,t8140-sleep reg: maxItems: 28 diff --git a/MAINTAINERS b/MAINTAINERS index 0cdac3657b043..3575c79f6b7fd 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2132,7 +2132,7 @@ F: drivers/phy/apple/atc-j700-dp.h F: drivers/rtc/rtc-apple-pmu.c F: drivers/soc/apple/aop.rs F: drivers/soc/apple/mcc.c -F: drivers/soc/apple/t8140-sleep.c +F: drivers/soc/apple/sleep*.c F: drivers/tty/serial/apple_dockchannel* F: include/linux/soc/apple/pci-apple-piodma.h F: include/linux/soc/apple/sleep.h diff --git a/arch/arm64/configs/asahi.config b/arch/arm64/configs/asahi.config index 847904f7eedbf..78795a0689799 100644 --- a/arch/arm64/configs/asahi.config +++ b/arch/arm64/configs/asahi.config @@ -160,4 +160,4 @@ CONFIG_EDAC_APPLE_SOC=m # unless the bootloader supplies a window (/chosen/aurora,ctxr-exec-window) or # the ctxr_exec_window= command-line option is given. CONFIG_ARM64_CTXR_EXECMEM=y -CONFIG_APPLE_T8140_SLEEP=y +CONFIG_APPLE_SOC_SLEEP=y diff --git a/drivers/gpu/drm/apple/dcp-internal.h b/drivers/gpu/drm/apple/dcp-internal.h index a41c4bf611784..305c0d3d6f152 100644 --- a/drivers/gpu/drm/apple/dcp-internal.h +++ b/drivers/gpu/drm/apple/dcp-internal.h @@ -261,6 +261,13 @@ struct apple_dcp_hw_data { * (desktop 26.x), see dcp_sink_metadata(). */ bool sink_metadata_edid; + /* + * With H17P firmware: a firmware-driven system sleep stops the internal processor's + * firmware and powers it down, as DPMS off does, then restarts it warm, see + * dcp_s2r_suspend(). @s2r_ext: the same for an external one, see dcp_s2r_ext_suspend(). + */ + bool s2r; + bool s2r_ext; }; enum dcp_test_point { @@ -641,7 +648,7 @@ struct apple_dcp { /* Messages from the coprocessor are held in the mailbox for sleep. */ bool rx_held; - /* T8140 suspend-to-RAM stops the firmware, see dcp_s2r_suspend(). */ + /* A firmware-driven suspend stops the firmware, see dcp_s2r_suspend(). */ bool s2r_active; bool s2r_stopped; /* DPMS off stopped the firmware and lowered the domains; DPMS on brings them back. */ diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index 1dd0ff5fc518a..1fa457cf0f599 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -4533,7 +4533,7 @@ void dcp_poweroff(struct platform_device *pdev) cancel_delayed_work(&dcp->swap_watchdog_wq); /* - * Internal H17P DPMS is a brightness present, never a pipe stop. On T8140 the + * Internal H17P DPMS is a brightness present, never a pipe stop. With hw.s2r the * display then powers off as well, after the backlight. */ if (dcp_uses_soft_dpms(dcp)) { @@ -5630,8 +5630,7 @@ static int dcp_comp_bind_pipe(struct device *dev, struct device *main, void *dat if (ret) goto err_piodma; /* After a warm restart its log counters need echoing, or it refuses its next idle. */ - if (of_device_is_compatible(dev->of_node, "apple,t8140-dcp") || - of_device_is_compatible(dev->of_node, "apple,t8140-dcpext")) + if (dcp->hw.s2r || dcp->hw.s2r_ext) apple_rtkit_set_oslog_echo(dcp->rtk, true); ret = dcp_secure_port_init(dcp); if (ret) @@ -6896,8 +6895,8 @@ static void dcp_platform_complete(struct device *dev) } /* - * T8140 suspend-to-RAM: the sleep engine waits for the DCP's domain to be off, so - * the firmware has to stop first. Measured from macOS, in order: + * Suspend-to-RAM with hw.s2r: the sleep engine waits for the DCP's domain to be off, so + * the firmware has to stop first. Measured from macOS on T8140, in order: * * 1. A414(0), A478 x2, A476(1), A478, A412, A473(0). * 2. D572(0) from the firmware: the display DART's streams go off. @@ -6967,8 +6966,7 @@ void dcp_s2r_dart_power(struct apple_dcp *dcp, u32 on) static bool dcp_s2r_capable(struct apple_dcp *dcp) { - return dcp->fw_compat == DCP_FIRMWARE_H17P && - of_device_is_compatible(dcp->dev->of_node, "apple,t8140-dcp"); + return dcp->fw_compat == DCP_FIRMWARE_H17P && dcp->hw.s2r; } static void dcp_s2r_cpu_stop(struct apple_dcp *dcp) @@ -7158,8 +7156,7 @@ static int dcp_s2r_stop_firmware(struct apple_dcp *dcp) */ static bool dcp_s2r_ext_capable(struct apple_dcp *dcp) { - return dcp->fw_compat == DCP_FIRMWARE_H17P && - of_device_is_compatible(dcp->dev->of_node, "apple,t8140-dcpext"); + return dcp->fw_compat == DCP_FIRMWARE_H17P && dcp->hw.s2r_ext; } /* @@ -7940,7 +7937,7 @@ static void dcp_s2r_restart_failed_stop(struct apple_dcp *dcp) } /* - * DPMS on the internal T8140 panel as macOS does a display sleep: the backlight to 0, + * DPMS on an internal panel with hw.s2r as macOS does a display sleep: the backlight to 0, * then the firmware stopped and the display domains off, and the reverse on wake. Not * during a system sleep: that path stops the firmware itself. */ @@ -8211,6 +8208,7 @@ static const struct apple_dcp_hw_data apple_dcp_hw_t8140 = { .firmware_display_windows = 5, .firmware_scratch = 0x20000, .firmware_request = 0x68000, + .s2r = true, }; /* Separate from the established internal-panel H17P profile. */ @@ -8224,6 +8222,7 @@ static const struct apple_dcp_hw_data apple_dcp_hw_t8140_external = { /* Native PMGR BWR agent 2 and boost agent 3 for DISPEXT0. */ .firmware_scratch = 0x28000, .firmware_request = 0x70000, + .s2r_ext = true, }; /* diff --git a/drivers/irqchip/irq-apple-aic.c b/drivers/irqchip/irq-apple-aic.c index 1afc73dd62617..393596ff6231b 100644 --- a/drivers/irqchip/irq-apple-aic.c +++ b/drivers/irqchip/irq-apple-aic.c @@ -284,6 +284,8 @@ struct aic_info { * comes online. */ bool siq_cfg_routing; + /* A firmware-driven suspend powers the controller down: aic_syscore_ops keep it. */ + bool fw_suspend; }; static const struct aic_info aic1_info __initconst = { @@ -336,6 +338,7 @@ static const struct aic_info aic3_t8140_info __initconst = { .fast_ipi = true, .local_fast_ipi = true, .siq_cfg_routing = true, + .fw_suspend = true, }; static const struct aic_info aic3_t8142_info __initconst = { @@ -1131,7 +1134,7 @@ static void build_fiq_affinity(struct aic_irq_chip *ic, struct device_node *aff) #ifdef CONFIG_PM_SLEEP /* - * T8140's firmware-driven suspend powers the controller down and back up. How + * A firmware-driven suspend powers the controller down and back up. How * much it keeps isn't measured, and macOS saves and restores it in software. So * mask everything before the commit, so that no line can end the final WFI, and * put it all back after. Linux never writes IRQ_CFG or these global registers @@ -1239,10 +1242,9 @@ static struct syscore aic_syscore = { .ops = &aic_syscore_ops, }; -static void __init aic_sleep_init(struct aic_irq_chip *irqc, struct device_node *node) +static void __init aic_sleep_init(struct aic_irq_chip *irqc) { - /* The only Apple SoC with a suspend-to-RAM path. */ - if (!of_device_is_compatible(node, "apple,t8140-aic3")) + if (!irqc->info.fw_suspend) return; aic_sleep.irq_cfg = kcalloc(irqc->nr_die * irqc->nr_irq, sizeof(u32), GFP_KERNEL); @@ -1252,7 +1254,7 @@ static void __init aic_sleep_init(struct aic_irq_chip *irqc, struct device_node register_syscore(&aic_syscore); } #else -static void __init aic_sleep_init(struct aic_irq_chip *irqc, struct device_node *node) +static void __init aic_sleep_init(struct aic_irq_chip *irqc) { } #endif @@ -1398,7 +1400,7 @@ static int __init aic_of_ic_init(struct device_node *node, struct device_node *p "irqchip/apple-aic/ipi:starting", aic_init_cpu, NULL); - aic_sleep_init(irqc, node); + aic_sleep_init(irqc); if (is_kernel_in_hyp_mode()) { struct irq_fwspec mi = { diff --git a/drivers/soc/apple/Kconfig b/drivers/soc/apple/Kconfig index d144b1adb690e..d2aefd22fdd12 100644 --- a/drivers/soc/apple/Kconfig +++ b/drivers/soc/apple/Kconfig @@ -105,17 +105,34 @@ config APPLE_CLUSTER_PWRGATE started and PMGR never powered on, such as the boot CPU, only clock-gates in WFI. -config APPLE_T8140_SLEEP - bool "Apple T8140 system sleep at EL2" +config APPLE_SOC_SLEEP + bool "Apple SoC system sleep at EL2" + default APPLE_T8140_SLEEP depends on ARM64 && (ARCH_APPLE || COMPILE_TEST) depends on HOTPLUG_CPU && SUSPEND select APPLE_TUNABLE help Powers the secondary CPUs off and suspends the system to RAM on an - Apple T8140 whose kernel runs directly at EL2, with no monitor to ask. + Apple SoC whose kernel runs directly at EL2, with no monitor to ask. It does nothing unless the boot object asks for it through /chosen/aurora,s2r-mailbox. + Only SoCs whose sleep has been measured and tested are supported, + each by its match data: today T8140. On every other SoC the driver + doesn't bind, and s2idle stays the only system sleep. + +config APPLE_SOC_SLEEP_KUNIT_TEST + bool "KUnit tests for the Apple SoC sleep match data" if !KUNIT_ALL_TESTS + depends on APPLE_SOC_SLEEP && KUNIT=y + default KUNIT_ALL_TESTS + help + Checks each matched SoC's sleep data: that only tested SoCs are + matched, that every list names only words of the SoC's full lists, + with no duplicates, and that the lists fit the driver's books. The + tests touch no hardware. + + If unsure, say N. + config APPLE_RTKIT tristate "Apple RTKit co-processor IPC protocol" depends on APPLE_MAILBOX @@ -379,3 +396,9 @@ config APPLE_DISPLAY_GATE_KUNIT_TEST endmenu endif + +config APPLE_T8140_SLEEP + bool + transitional + help + Transitional config for APPLE_T8140_SLEEP to APPLE_SOC_SLEEP migration. diff --git a/drivers/soc/apple/Makefile b/drivers/soc/apple/Makefile index b6d60b1705af2..61c96d6048889 100644 --- a/drivers/soc/apple/Makefile +++ b/drivers/soc/apple/Makefile @@ -15,7 +15,8 @@ obj-$(CONFIG_APPLE_PMGR_MISC) += apple-pmgr-misc.o obj-$(CONFIG_APPLE_CLUSTER_PWRGATE) += apple-cluster-pwrgate.o -obj-$(CONFIG_APPLE_T8140_SLEEP) += t8140-sleep.o +obj-$(CONFIG_APPLE_SOC_SLEEP) += apple-sleep.o +apple-sleep-y = sleep.o obj-$(CONFIG_APPLE_RTKIT) += apple-rtkit.o apple-rtkit-y = rtkit.o rtkit-crashlog.o diff --git a/drivers/soc/apple/pm_shim.c b/drivers/soc/apple/pm_shim.c index 5f15672fee69c..b0d6a1c76746f 100644 --- a/drivers/soc/apple/pm_shim.c +++ b/drivers/soc/apple/pm_shim.c @@ -325,7 +325,7 @@ int sep_pm_commit_prepare(void *ptr, void *mbox, u64 msg0, u32 msg1, u64 reply, int ret; /* Check capability before resources that legacy providers need not have. */ - if (!READ_ONCE(sep_pm_suspending) || !IS_ENABLED(CONFIG_APPLE_T8140_SLEEP) || + if (!READ_ONCE(sep_pm_suspending) || !IS_ENABLED(CONFIG_APPLE_SOC_SLEEP) || !is_kernel_in_hyp_mode()) return -EOPNOTSUPP; if (c->registered) diff --git a/drivers/soc/apple/sleep-test.c b/drivers/soc/apple/sleep-test.c new file mode 100644 index 0000000000000..90b7661afd198 --- /dev/null +++ b/drivers/soc/apple/sleep-test.c @@ -0,0 +1,156 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* + * Included by sleep.c: each matched SoC's data. A word a list names but the SoC's full list + * lacks is skipped silently at the wake, and a duplicate splits its books. + */ +#include + +/* Matched only once its sleep has been tested on hardware. */ +static const char *const apple_sleep_test_tested[] = { + "apple,t8140-sleep", +}; + +static bool apple_sleep_test_has(const struct apple_sleep_words *all, u32 offset) +{ + return sleep_ps_find(all->offset, all->count, offset) >= 0; +} + +static void apple_sleep_test_in(struct kunit *test, const char *compat, const char *list, + const struct apple_sleep_words *all, u32 offset) +{ + KUNIT_EXPECT_TRUE_MSG(test, apple_sleep_test_has(all, offset), + "%s: %s word %#x isn't in its full list", compat, list, offset); +} + +static void apple_sleep_test_list_in(struct kunit *test, const char *compat, const char *list, + const struct apple_sleep_words *all, + const struct apple_sleep_words *words) +{ + int i; + + for (i = 0; i < words->count; i++) + apple_sleep_test_in(test, compat, list, all, words->offset[i]); +} + +static void apple_sleep_test_full(struct kunit *test, const char *compat, const char *list, + const struct apple_sleep_words *all) +{ + int i; + + KUNIT_EXPECT_GT_MSG(test, all->count, 0, "%s: %s is empty", compat, list); + for (i = 0; i < all->count; i++) { + KUNIT_EXPECT_EQ_MSG(test, all->offset[i] % 4, 0, "%s: %s word %#x", compat, list, + all->offset[i]); + KUNIT_EXPECT_EQ_MSG(test, sleep_ps_find(all->offset, all->count, all->offset[i]), i, + "%s: %s has %#x twice", compat, list, all->offset[i]); + } +} + +static void apple_sleep_test_only_tested(struct kunit *test) +{ + const struct of_device_id *id; + int n = 0; + + for (id = apple_sleep_of_match; id->compatible[0]; id++, n++) { + KUNIT_EXPECT_NOT_NULL_MSG(test, id->data, "%s has no data", id->compatible); + KUNIT_ASSERT_LT(test, n, ARRAY_SIZE(apple_sleep_test_tested)); + KUNIT_EXPECT_STREQ(test, id->compatible, apple_sleep_test_tested[n]); + } + KUNIT_EXPECT_EQ(test, n, ARRAY_SIZE(apple_sleep_test_tested)); +} + +static void apple_sleep_test_words(struct kunit *test) +{ + const struct of_device_id *id; + + for (id = apple_sleep_of_match; id->compatible[0]; id++) { + const struct apple_sleep_soc *soc = id->data; + const struct apple_sleep_domains *d = &soc->ps; + const struct apple_sleep_words *all = &soc->ps_all, *aon = &soc->ps_aon_all; + const char *c = id->compatible; + u32 named[] = { + d->disp[0][0], d->disp[0][1], d->disp[0][2], d->disp[1][0], d->disp[1][1], + d->disp[1][2], d->sio_cpu, d->sep, d->usb_aon, d->usb_common, d->usb, + d->ap_tmm, d->gpio, d->ans, d->apcie_gp, d->apcie_sys_gp, d->apcie_st, + d->apcie_sys_st, d->apcie_phy_sw, + }; + int i; + + apple_sleep_test_full(test, c, "ps_all", all); + apple_sleep_test_full(test, c, "ps_aon_all", aon); + for (i = 0; i < ARRAY_SIZE(named); i++) + apple_sleep_test_in(test, c, "named", all, named[i]); + apple_sleep_test_in(test, c, "debug switch", aon, soc->ps_aon_debug_switch); + apple_sleep_test_list_in(test, c, "ps_macos_on", all, &soc->ps_macos_on); + apple_sleep_test_list_in(test, c, "ps_macos_off", all, &soc->ps_macos_off); + apple_sleep_test_list_in(test, c, "ps_wake_order", all, &soc->ps_wake_order); + apple_sleep_test_list_in(test, c, "ps_still_on", all, &soc->ps_still_on); + apple_sleep_test_list_in(test, c, "ps_aon_macos_off", aon, &soc->ps_aon_macos_off); + apple_sleep_test_list_in(test, c, "ps_aon_macos_no_auto", aon, + &soc->ps_aon_macos_no_auto); + apple_sleep_test_list_in(test, c, "ps_aon_wake_order", aon, + &soc->ps_aon_wake_order); + for (i = 0; i < soc->nstray; i++) + apple_sleep_test_in(test, c, soc->stray[i].name, all, soc->stray[i].offset); + for (i = 0; i < soc->ngate_off; i++) + apple_sleep_test_in(test, c, soc->gate_off[i].name, all, + soc->gate_off[i].offset); + for (i = 0; i < soc->ngate_coprocs; i++) { + KUNIT_EXPECT_TRUE(test, soc->gate_coprocs[i].path[0] == '/'); + if (soc->gate_coprocs[i].ps) + apple_sleep_test_in(test, c, soc->gate_coprocs[i].path, all, + soc->gate_coprocs[i].ps); + } + } +} + +/* The books keep one bit for each stray word and one gate for each coprocessor. */ +static void apple_sleep_test_books(struct kunit *test) +{ + const struct of_device_id *id; + + for (id = apple_sleep_of_match; id->compatible[0]; id++) { + const struct apple_sleep_soc *soc = id->data; + + KUNIT_EXPECT_LE(test, soc->nstray, BITS_PER_LONG); + KUNIT_EXPECT_LE(test, soc->ngate_coprocs, + ARRAY_SIZE(((struct apple_sleep *)0)->gate)); + } +} + +static void apple_sleep_test_wake_names(struct kunit *test) +{ + const struct of_device_id *id; + + for (id = apple_sleep_of_match; id->compatible[0]; id++) { + const struct apple_sleep_soc *soc = id->data; + int i, j; + + KUNIT_EXPECT_NE(test, soc->wake_last_word, 0); + for (i = 0; i < soc->nwake_names; i++) { + u32 n = soc->wake_names[i].id; + + KUNIT_EXPECT_LT_MSG(test, n, WAKE_WORDS * 32, "%s", + soc->wake_names[i].name); + if (n / 32 == WAKE_WORDS - 1) + KUNIT_EXPECT_TRUE_MSG(test, soc->wake_last_word & BIT(n % 32), "%s", + soc->wake_names[i].name); + for (j = 0; j < i; j++) + KUNIT_EXPECT_NE(test, soc->wake_names[j].id, n); + } + } +} + +static struct kunit_case apple_sleep_test_cases[] = { + KUNIT_CASE(apple_sleep_test_only_tested), + KUNIT_CASE(apple_sleep_test_words), + KUNIT_CASE(apple_sleep_test_books), + KUNIT_CASE(apple_sleep_test_wake_names), + {} +}; + +static struct kunit_suite apple_sleep_test_suite = { + .name = "apple-sleep-soc-data", + .test_cases = apple_sleep_test_cases, +}; +kunit_test_suite(apple_sleep_test_suite); diff --git a/drivers/soc/apple/t8140-sleep.c b/drivers/soc/apple/sleep.c similarity index 66% rename from drivers/soc/apple/t8140-sleep.c rename to drivers/soc/apple/sleep.c index 4ac3959df122f..fb958eb63d6f2 100644 --- a/drivers/soc/apple/t8140-sleep.c +++ b/drivers/soc/apple/sleep.c @@ -1,11 +1,11 @@ // SPDX-License-Identifier: GPL-2.0-only OR MIT /* - * Apple T8140 system sleep + * Apple SoC system sleep * - * On T8140 a kernel can run directly at EL2 behind the CTXR execute window - * (Documentation/arch/arm64/ctxr-execmem.rst), with no monitor below it. So no - * PSCI answers a request to power a CPU off or to suspend the system, and the - * kernel does both itself. + * On some Apple SoCs a kernel can run directly at EL2 behind the CTXR execute + * window (Documentation/arch/arm64/ctxr-execmem.rst), with no monitor below it. + * So no PSCI answers a request to power a CPU off or to suspend the system, and + * the kernel does both itself. * * iBoot locks every core's reset vector to the boot object. A core that comes * back from a power-off therefore restarts inside the boot object (aura), and @@ -22,7 +22,10 @@ * otherwise explained here were measured from a macOS sleep under aura. * * What the kernel does in suspend-to-RAM is what macOS does in its last 14 ms, - * with the order kept. + * with the order kept. What differs between SoCs, their power words and the + * lists macOS was measured with, is each SoC's match data. Only SoCs measured + * and tested that way are matched: on the others this driver doesn't bind, and + * s2idle stays the only sleep. */ #include @@ -63,9 +66,9 @@ #include /* Must match aura's struct aura_s2r_mailbox. */ -#define T8140_S2R_MAGIC 0x3152325341525541ULL /* "AURAS2R1" */ +#define AURA_S2R_MAGIC 0x3152325341525541ULL /* "AURAS2R1" */ -struct t8140_s2r_mailbox { +struct aura_s2r_mailbox { u64 magic; /* aura, before the kernel starts */ u64 boot_mpidr; /* aura: the only core that may take resume_pa */ u64 resume_pa; /* kernel, armed before a commit; aura clears it */ @@ -80,19 +83,6 @@ struct t8140_s2r_mailbox { #define PS_TARGET GENMASK(3, 0) #define PS_OFF_TIMEOUT_US 5000 -#define PS_DISP_CPU 0x10000 -#define PS_DISP_FE 0x318 -#define PS_DISP_SYS 0x280 -#define PS_DISPEXT0_CPU 0x328 -#define PS_DISPEXT0_FE 0x320 -#define PS_DISPEXT0_SYS 0x288 -#define PS_SIO_CPU 0x2a8 -#define PS_SEP 0xc00 -#define PS_ATC0_USB_AON 0x3e0 -#define PS_ATC0_COMMON 0x3e8 -#define PS_ATC0_USB 0x3f0 -#define PS_AP_TMM 0x1a0 - /* CPU start block, as aura's smp_start_cpu() uses it */ #define CPU_START_STOP 0x0 #define CPU_START_SYS 0x4 @@ -100,12 +90,6 @@ struct t8140_s2r_mailbox { /* Power-gate page: reads back this value already. Purpose unknown. */ #define PWRGATE_ON 0x80000000 -#define PWRGATE_WAKE_0 0x000 -#define PWRGATE_WAKE_1 0x040 -#define PWRGATE_CPM_0 0x060 -#define PWRGATE_CPM_1 0x068 -#define PWRGATE_DISP_0 0xc8c -#define PWRGATE_DISP_1 0xcd0 /* Three clock muxes macOS writes before the commit. */ #define CLK_MUX_PARKED 0x80100000 @@ -150,8 +134,6 @@ struct t8140_s2r_mailbox { #define WAKE_MASK(word) (0x08 + (word) * 4) #define WAKE_STATUS(word) (0x28 + (word) * 4) #define WAKE_WORDS 8 -/* Word 7 has 21 sources. Measured: macOS reads 0x001fef77 there. */ -#define WAKE_WORD7_ALL GENMASK(20, 0) #define WAKE_SOURCES_MAX 32 /* The AP queue of the PMU's SPMI controller, and its response vector's enable and ack */ @@ -209,9 +191,6 @@ struct t8140_s2r_mailbox { #define CLK_PERF_SOC 0x0 #define SOC_SLEEP_STATE 3 -/* An always-on word that a live debug link holds up. macOS has it off. */ -#define PS_AON_DEBUG_SWITCH 0xb0 - /* The PMC's voter agents, each with a word holding its floor for every rail. */ #define PMC_VOTER_AGENTS 4 #define PMC_VOTER_STRIDE 0x4000 @@ -248,14 +227,6 @@ struct t8140_s2r_mailbox { #define ASC_CPU_CONTROL_RUN BIT(4) #define ASC_CPU_STATUS 0x48 -#define PS_GPIO 0x120 -#define PS_ANS 0x278 -#define PS_APCIE_GP 0x258 -#define PS_APCIE_SYS_GP 0x2a0 -#define PS_APCIE_ST 0x310 -#define PS_APCIE_SYS_ST 0x3a0 -#define PS_APCIE_PHY_SW 0x3c0 - /* The part of the PCIe block that ANS shares, whether or not its host is bound. */ #define APCIE_AXI_EN0 0x104 #define APCIE_AXI_EN1 0x108 @@ -310,7 +281,7 @@ struct t8140_s2r_mailbox { /* The coprocessors the last mile stops. Each driver idles its own first. */ #define SLEEP_COPROCS 4 -static struct t8140_sleep_coproc { +static struct apple_sleep_coproc { struct device *dev; void __iomem *asc; bool pcie_ans; @@ -323,15 +294,15 @@ static struct t8140_sleep_coproc { /* No ASC: its last stop takes it whole, power word @ps included, before the others. */ bool first; u32 ps; -} t8140_sleep_coprocs[SLEEP_COPROCS]; -static DEFINE_MUTEX(t8140_sleep_coprocs_lock); +} apple_sleep_coprocs[SLEEP_COPROCS]; +static DEFINE_MUTEX(apple_sleep_coprocs_lock); -/* The enclave's sleep request, left to the last refusal. Under t8140_sleep_coprocs_lock. */ +/* The enclave's sleep request, left to the last refusal. Under apple_sleep_coprocs_lock. */ static struct { struct device *dev; int (*tell)(void *data); void *data; -} t8140_sleep_enclave; +} apple_sleep_enclave; /* * Written with bits 0 and 63 by every core right before its last WFI, measured @@ -344,27 +315,10 @@ static struct { #define IPI_SR_PENDING BIT(0) #define SYS_APL_SIQ_CFG_EL1 sys_reg(3, 4, 15, 10, 4) -/* The names of ADT pmgr/ap-wake-sources: for the log, and to tell the SMC's apart */ -static const struct { - u32 id; - const char *name; -} t8140_wake_names[] = { - { 0, "AOP.AICWakeupAP" }, { 5, "AOP.CPUWakeupAP" }, - { 42, "AOP.OutboxNotEmpty" }, { 62, "AOP.SPMI0.SW3" }, - { 66, "AOP.SPMI1.SW3" }, { 70, "ATC0_PMGR_USB_WAKEUP" }, - { 80, "MTP.DOCK.CHANNELS.AP0.IRQ" }, { 99, "MTP.OUTBOX.N.EMPTY_IRQ" }, - { 109, "NUB.APWakeupTime" }, { 110, "NUB.APWatchdog" }, - { 125, "NUB.GPIO.IRQ6" }, { 131, "NUB.SPMI0.SW3" }, - { 136, "NUB.SPMI1.SW3" }, { 140, "NUB.WakeupAP" }, - { 145, "SMC.CPUWakeupAP" }, { 165, "SMC.OutboxNotEmpty" }, - { 211, "AOP.TrustedOutboxNotEmpty" }, { 227, "AOP2.OutboxNotEmpty" }, - { 231, "AOP2.TrustedOutboxNotEmpty" }, { 236, "AOP2.CPUWakeupAP" }, -}; - -#define T8140_DISPLAY_COUNT 2 +#define SLEEP_DISPLAY_COUNT 2 -/* CPU clusters: 0 is E, 1 is P. ADT /arm-io/pmgr reg[5], [7], [9] and [14], [16], [18]. */ -#define T8140_CLUSTERS 2 +/* CPU clusters: 0 is E, 1 is P */ +#define SLEEP_CLUSTERS 2 /* cpm-dvfm */ #define DVFM_CTL 0x178 @@ -375,12 +329,108 @@ static const struct { #define DVFM_ENABLE BIT_ULL(63) /* In macOS's order, on the way down. */ -static const u32 t8140_dvfm_words[] = { +static const u32 sleep_dvfm_words[] = { DVFM_WORD_0, DVFM_WORD_1, DVFM_WORD_2, DVFM_WORD_3, }; -struct t8140_sleep { +/* A power word by its offset, named for the log */ +struct apple_sleep_word { + const char *name; + u32 offset; +}; + +/* Power words by their offsets */ +struct apple_sleep_words { + const u32 *offset; + unsigned int count; +}; + +#define SLEEP_WORDS(a) { .offset = (a), .count = ARRAY_SIZE(a) } + +/* + * A coprocessor that must be bound for a sleep, by its path. @must_stop: it must also have + * stopped by the last step. @ps: its CPU's own domain, where a driver may power it off. + */ +struct apple_sleep_gate_coproc { + const char *path; + bool must_stop; + u32 ps; +}; + +/* A source in the ADT's pmgr/ap-wake-sources: for the log, and to tell the SMC's apart */ +struct apple_sleep_wake_name { + u32 id; + const char *name; +}; + +/* The power words in "ps" that the sleep drives itself */ +struct apple_sleep_domains { + /* CPU, front end and system, of the DCP and then of the external DCP */ + u32 disp[SLEEP_DISPLAY_COUNT][3]; + u32 sio_cpu; + u32 sep; + /* The USB host's complex: always-on, common and the controller */ + u32 usb_aon; + u32 usb_common; + u32 usb; + /* Only the deep path's wake turns it on. */ + u32 ap_tmm; + u32 gpio; + u32 ans; + /* ANS's PCIe block */ + u32 apcie_gp; + u32 apcie_sys_gp; + u32 apcie_st; + u32 apcie_sys_st; + u32 apcie_phy_sw; +}; + +/* + * What differs between SoCs. Power words are offsets in "ps", or in "ps-aon" where named + * so, and the lists keep the order macOS was measured with. + */ +struct apple_sleep_soc { + struct apple_sleep_domains ps; + /* In "ps-aon": held up by a live debug link, and off on macOS */ + u32 ps_aon_debug_switch; + /* In "pwrgate": each pair written as macOS does */ + u32 pwrgate_wake[2]; + u32 pwrgate_cpm[2]; + u32 pwrgate_disp[2]; + /* Every power word, saved at the commit and checked at the wake */ + struct apple_sleep_words ps_all; + struct apple_sleep_words ps_aon_all; + /* On at macOS's commit and left off by Linux */ + struct apple_sleep_words ps_macos_on; + /* Off at macOS's commit, in the order it lowers them */ + struct apple_sleep_words ps_macos_off; + struct apple_sleep_words ps_aon_macos_off; + /* In "ps-aon": on at macOS's commit, with AUTO_ENABLE off */ + struct apple_sleep_words ps_aon_macos_no_auto; + /* Written back on by macOS's wake before any driver runs, in its order */ + struct apple_sleep_words ps_wake_order; + struct apple_sleep_words ps_aon_wake_order; + /* Polled off just before the complex words */ + struct apple_sleep_words ps_still_on; + /* Lowered at the last step if still on, with no user left */ + const struct apple_sleep_word *stray; + unsigned int nstray; + /* Off on macOS by the last step: one still on only warns. */ + const struct apple_sleep_word *gate_off; + unsigned int ngate_off; + const struct apple_sleep_gate_coproc *gate_coprocs; + unsigned int ngate_coprocs; + const struct apple_sleep_wake_name *wake_names; + unsigned int nwake_names; + /* The sources in the wake block's last word */ + u32 wake_last_word; + /* The PCIe host whose port the radio is on, for its PERST#, or NULL */ + const char *radio_pcie; +}; + +struct apple_sleep { struct device *dev; + const struct apple_sleep_soc *soc; void __iomem *ps; void __iomem *pwrgate; void __iomem *cpu_start; @@ -414,14 +464,25 @@ struct t8140_sleep { void __iomem *ptd; void __iomem *clk_perf; void __iomem *cluster_dcs; - u32 ps_on_saved[10]; - u32 ps_off_saved[64]; - u32 ps_aon_saved[4]; - u32 ps_aon_auto_saved[2]; + /* As the match found them: one for each word of the SoC's lists */ + u32 *ps_on_saved; + u32 *ps_off_saved; + u32 *ps_aon_saved; + u32 *ps_aon_auto_saved; + /* For each word of ps_all and ps_aon_all: at the commit, as the wake found it, late */ + u32 *ps_commit; + u32 *ps_aon_commit; + u32 *ps_woke; + u32 *ps_aon_woke; + bool *ps_late; + bool *ps_aon_late; + /* The rewake's own books */ + bool *ps_done; + bool *ps_aon_done; u32 spmi_saved[2]; bool matched; - void __iomem *dvfm[T8140_CLUSTERS]; - struct t8140_s2r_mailbox *mbox; + void __iomem *dvfm[SLEEP_CLUSTERS]; + struct aura_s2r_mailbox *mbox; u32 wake_enable[WAKE_SOURCES_MAX]; int wake_enable_count; @@ -433,12 +494,12 @@ struct t8140_sleep { struct device_node *np; void __iomem *asc; bool bound; - } display[T8140_DISPLAY_COUNT]; + } display[SLEEP_DISPLAY_COUNT]; /* Filled in for the report after a return. */ u32 wake_status[WAKE_WORDS]; - u64 dvfm_ctl[T8140_CLUSTERS]; - u64 dvfm_saved[T8140_CLUSTERS][ARRAY_SIZE(t8140_dvfm_words)]; + u64 dvfm_ctl[SLEEP_CLUSTERS]; + u64 dvfm_saved[SLEEP_CLUSTERS][ARRAY_SIZE(sleep_dvfm_words)]; bool dvfm_parked; u64 resumes_before; u64 resumes_after; @@ -471,10 +532,10 @@ struct t8140_sleep { struct apple_tunable *apcie_phy_ip_pll; struct apple_tunable *apcie_phy_ip_lanes; bool pcie_ans_stopped; - /* The words t8140_sleep_lower_stray() lowered, one bit each */ - u8 stray_lowered; + /* The words apple_sleep_lower_stray() lowered, one bit each */ + unsigned long stray_lowered; /* What must be bound for a sleep, and what must have stopped by the last step */ - struct t8140_sleep_gate { + struct apple_sleep_gate { const char *name; /* Compared only, never dereferenced: nodes outlive the gate. */ const struct device_node *np; @@ -503,111 +564,111 @@ struct t8140_sleep { bool smc_woke_alone; }; -static struct t8140_sleep *t8140_sleep; +static struct apple_sleep *apple_sleep; -static struct t8140_sleep_coproc *t8140_sleep_coproc_find(struct device *dev) +static struct apple_sleep_coproc *apple_sleep_coproc_find(struct device *dev) { int i; for (i = 0; i < SLEEP_COPROCS; i++) - if (t8140_sleep_coprocs[i].dev == dev) - return &t8140_sleep_coprocs[i]; + if (apple_sleep_coprocs[i].dev == dev) + return &apple_sleep_coprocs[i]; return NULL; } /* A registered coprocessor its driver has readied, whose run bit the last step clears. */ -static bool t8140_sleep_coproc_ready_node(const struct device_node *np) +static bool apple_sleep_coproc_ready_node(const struct device_node *np) { int i; for (i = 0; np && i < SLEEP_COPROCS; i++) - if (t8140_sleep_coprocs[i].dev && t8140_sleep_coprocs[i].ready && - t8140_sleep_coprocs[i].dev->of_node == np) + if (apple_sleep_coprocs[i].dev && apple_sleep_coprocs[i].ready && + apple_sleep_coprocs[i].dev->of_node == np) return true; return false; } -static bool t8140_usb_asleep; +static bool sleep_usb_asleep; void apple_sleep_usb_set_asleep(bool asleep) { - WRITE_ONCE(t8140_usb_asleep, asleep); + WRITE_ONCE(sleep_usb_asleep, asleep); } EXPORT_SYMBOL_GPL(apple_sleep_usb_set_asleep); -static unsigned long t8140_usb_wake_ports; +static unsigned long sleep_usb_wake_ports; void apple_sleep_usb_set_wake_ports(unsigned long ports) { - WRITE_ONCE(t8140_usb_wake_ports, ports); + WRITE_ONCE(sleep_usb_wake_ports, ports); } EXPORT_SYMBOL_GPL(apple_sleep_usb_set_wake_ports); bool apple_sleep_usb_port_can_wake(unsigned int port) { - return port < BITS_PER_LONG && (READ_ONCE(t8140_usb_wake_ports) & BIT(port)); + return port < BITS_PER_LONG && (READ_ONCE(sleep_usb_wake_ports) & BIT(port)); } EXPORT_SYMBOL_GPL(apple_sleep_usb_port_can_wake); /* The USB host's vote, kept until the word is mapped. Only bit 0 is touched, as on macOS. */ -static DEFINE_SPINLOCK(t8140_slp_ddr_lock); -static bool t8140_slp_ddr_usb; -static void __iomem *t8140_slp_ddr_word; +static DEFINE_SPINLOCK(sleep_slp_ddr_lock); +static bool sleep_slp_ddr_usb; +static void __iomem *sleep_slp_ddr_word; /* * The SLP_DDR wake runs on the SoC-side state the system left. After a Linux boot it stalls in * the sleep engine, and once one deep sleep has rebuilt that state it gets through every time. * So the word stays clear, whatever the host votes, until a wake shows the deep path ran. */ -static bool t8140_slp_ddr_ready; +static bool sleep_slp_ddr_ready; -static void t8140_slp_ddr_write(void) +static void sleep_slp_ddr_write(void) { u32 val; - lockdep_assert_held(&t8140_slp_ddr_lock); - if (!t8140_slp_ddr_word) + lockdep_assert_held(&sleep_slp_ddr_lock); + if (!sleep_slp_ddr_word) return; - val = readl(t8140_slp_ddr_word) & ~WAKE_SLP_DDR_USB; - if (t8140_slp_ddr_usb && t8140_slp_ddr_ready) + val = readl(sleep_slp_ddr_word) & ~WAKE_SLP_DDR_USB; + if (sleep_slp_ddr_usb && sleep_slp_ddr_ready) val |= WAKE_SLP_DDR_USB; - writel(val, t8140_slp_ddr_word); + writel(val, sleep_slp_ddr_word); } void apple_sleep_usb_slp_ddr(bool up) { - guard(spinlock_irqsave)(&t8140_slp_ddr_lock); - t8140_slp_ddr_usb = up; - t8140_slp_ddr_write(); + guard(spinlock_irqsave)(&sleep_slp_ddr_lock); + sleep_slp_ddr_usb = up; + sleep_slp_ddr_write(); } EXPORT_SYMBOL_GPL(apple_sleep_usb_slp_ddr); -static bool t8140_debug_link_cut; +static bool sleep_debug_link_cut; /* Only the debug port's controller took the cold reset. */ -static bool t8140_debug_port_is(struct device *dev) +static bool sleep_debug_port_is(struct device *dev) { - return t8140_sleep && t8140_sleep->hpm_np && dev->of_node == t8140_sleep->hpm_np; + return apple_sleep && apple_sleep->hpm_np && dev->of_node == apple_sleep->hpm_np; } bool apple_sleep_debug_link_was_cut(struct device *dev) { - return t8140_debug_port_is(dev) && xchg(&t8140_debug_link_cut, false); + return sleep_debug_port_is(dev) && xchg(&sleep_debug_link_cut, false); } EXPORT_SYMBOL_GPL(apple_sleep_debug_link_was_cut); bool apple_sleep_debug_link_cut_peek(struct device *dev) { - return t8140_debug_port_is(dev) && READ_ONCE(t8140_debug_link_cut); + return sleep_debug_port_is(dev) && READ_ONCE(sleep_debug_link_cut); } EXPORT_SYMBOL_GPL(apple_sleep_debug_link_cut_peek); /* The radio's host set its port up again after a real sleep: the next commit may stop it. */ void apple_sleep_radio_port_up(void) { - if (t8140_sleep) - WRITE_ONCE(t8140_sleep->apcie_port_down, false); + if (apple_sleep) + WRITE_ONCE(apple_sleep->apcie_port_down, false); } EXPORT_SYMBOL_GPL(apple_sleep_radio_port_up); @@ -618,7 +679,7 @@ EXPORT_SYMBOL_GPL(apple_sleep_radio_port_up); #define PIN_IRQ(g, w) (0x800 + 0x40 * (g) + 4 * (w)) /* Before any device suspends, with the pins' domain on. Skipped pins aren't read. */ -static void t8140_pins_save(struct t8140_sleep *s) +static void sleep_pins_save(struct apple_sleep *s) { unsigned int pin; @@ -641,7 +702,7 @@ static void t8140_pins_save(struct t8140_sleep *s) */ void apple_sleep_restore_pins(void) { - struct t8140_sleep *s = t8140_sleep; + struct apple_sleep *s = apple_sleep; unsigned int pin, grp, w, changed = 0; if (!s || !s->pins_lost) @@ -674,15 +735,15 @@ EXPORT_SYMBOL_GPL(apple_sleep_restore_pins); int apple_sleep_coproc_register(struct device *dev, void __iomem *asc, bool pcie_ans, int (*last_stop)(void *data), void *data) { - struct t8140_sleep_coproc *c; + struct apple_sleep_coproc *c; if (!is_kernel_in_hyp_mode()) return -ENODEV; - guard(mutex)(&t8140_sleep_coprocs_lock); - if (t8140_sleep_coproc_find(dev)) + guard(mutex)(&apple_sleep_coprocs_lock); + if (apple_sleep_coproc_find(dev)) return -EEXIST; - c = t8140_sleep_coproc_find(NULL); + c = apple_sleep_coproc_find(NULL); if (!c) return -ENOSPC; c->asc = asc; @@ -703,7 +764,7 @@ EXPORT_SYMBOL_GPL(apple_sleep_coproc_register); int apple_sleep_coproc_register_first(struct device *dev, int (*last_stop)(void *data), void *data) { - struct t8140_sleep_coproc *c; + struct apple_sleep_coproc *c; struct device_node *np; u32 ps = 0; int ret; @@ -719,8 +780,8 @@ int apple_sleep_coproc_register_first(struct device *dev, int (*last_stop)(void if (ret) return ret; - guard(mutex)(&t8140_sleep_coprocs_lock); - c = t8140_sleep_coproc_find(dev); + guard(mutex)(&apple_sleep_coprocs_lock); + c = apple_sleep_coproc_find(dev); if (c) { c->first = true; c->ps = ps; @@ -732,10 +793,10 @@ EXPORT_SYMBOL_GPL(apple_sleep_coproc_register_first); int apple_sleep_coproc_set_last_check(struct device *dev, int (*last_check)(void *data)) { - struct t8140_sleep_coproc *c; + struct apple_sleep_coproc *c; - guard(mutex)(&t8140_sleep_coprocs_lock); - c = t8140_sleep_coproc_find(dev); + guard(mutex)(&apple_sleep_coprocs_lock); + c = apple_sleep_coproc_find(dev); if (!c) return -ENOENT; c->last_check = last_check; @@ -749,12 +810,12 @@ int apple_sleep_enclave_register(struct device *dev, int (*tell)(void *data), vo if (!is_kernel_in_hyp_mode()) return -ENODEV; - guard(mutex)(&t8140_sleep_coprocs_lock); - if (t8140_sleep_enclave.dev) + guard(mutex)(&apple_sleep_coprocs_lock); + if (apple_sleep_enclave.dev) return -EBUSY; - t8140_sleep_enclave.tell = tell; - t8140_sleep_enclave.data = data; - t8140_sleep_enclave.dev = dev; + apple_sleep_enclave.tell = tell; + apple_sleep_enclave.data = data; + apple_sleep_enclave.dev = dev; return 0; } @@ -762,18 +823,18 @@ EXPORT_SYMBOL_GPL(apple_sleep_enclave_register); void apple_sleep_enclave_unregister(struct device *dev) { - guard(mutex)(&t8140_sleep_coprocs_lock); - if (t8140_sleep_enclave.dev == dev) - memset(&t8140_sleep_enclave, 0, sizeof(t8140_sleep_enclave)); + guard(mutex)(&apple_sleep_coprocs_lock); + if (apple_sleep_enclave.dev == dev) + memset(&apple_sleep_enclave, 0, sizeof(apple_sleep_enclave)); } EXPORT_SYMBOL_GPL(apple_sleep_enclave_unregister); void apple_sleep_coproc_unregister(struct device *dev) { - struct t8140_sleep_coproc *c; + struct apple_sleep_coproc *c; - guard(mutex)(&t8140_sleep_coprocs_lock); - c = t8140_sleep_coproc_find(dev); + guard(mutex)(&apple_sleep_coprocs_lock); + c = apple_sleep_coproc_find(dev); if (c) memset(c, 0, sizeof(*c)); } @@ -781,10 +842,10 @@ EXPORT_SYMBOL_GPL(apple_sleep_coproc_unregister); void apple_sleep_coproc_set_ready(struct device *dev, bool ready) { - struct t8140_sleep_coproc *c; + struct apple_sleep_coproc *c; - guard(mutex)(&t8140_sleep_coprocs_lock); - c = t8140_sleep_coproc_find(dev); + guard(mutex)(&apple_sleep_coprocs_lock); + c = apple_sleep_coproc_find(dev); if (!c) return; c->ready = ready; @@ -794,17 +855,17 @@ EXPORT_SYMBOL_GPL(apple_sleep_coproc_set_ready); bool apple_sleep_coproc_stopped(struct device *dev) { - struct t8140_sleep_coproc *c; + struct apple_sleep_coproc *c; - guard(mutex)(&t8140_sleep_coprocs_lock); - c = t8140_sleep_coproc_find(dev); + guard(mutex)(&apple_sleep_coprocs_lock); + c = apple_sleep_coproc_find(dev); return c && c->stopped; } EXPORT_SYMBOL_GPL(apple_sleep_coproc_stopped); bool apple_sleep_commit_stops_coprocs(void) { - return READ_ONCE(t8140_sleep) && pm_suspend_via_firmware(); + return READ_ONCE(apple_sleep) && pm_suspend_via_firmware(); } EXPORT_SYMBOL_GPL(apple_sleep_commit_stops_coprocs); @@ -815,7 +876,7 @@ EXPORT_SYMBOL_GPL(apple_sleep_commit_stops_coprocs); * go back. In the order measured on macOS, on ANS itself: whatever its driver does last, * status and control read, then 0 written twice. */ -static void t8140_sleep_coproc_run_off(struct t8140_sleep_coproc *c) +static void apple_sleep_coproc_run_off(struct apple_sleep_coproc *c) { /* Its own last stop cleared its run bits. */ if (!c->asc) { @@ -829,24 +890,24 @@ static void t8140_sleep_coproc_run_off(struct t8140_sleep_coproc *c) } /* A power word that a ready coprocessor's last stop lowers: on until the commit. */ -static bool t8140_sleep_coproc_lowers(u32 ps) +static bool apple_sleep_coproc_lowers(u32 ps) { int i; for (i = 0; i < SLEEP_COPROCS; i++) - if (t8140_sleep_coprocs[i].dev && t8140_sleep_coprocs[i].ready && - t8140_sleep_coprocs[i].first && t8140_sleep_coprocs[i].ps == ps) + if (apple_sleep_coprocs[i].dev && apple_sleep_coprocs[i].ready && + apple_sleep_coprocs[i].first && apple_sleep_coprocs[i].ps == ps) return true; return false; } /* The last refusals, each coprocessor's own: nothing has stopped yet. */ -static int t8140_sleep_coprocs_check(struct t8140_sleep *s) +static int apple_sleep_coprocs_check(struct apple_sleep *s) { int i, ret; for (i = 0; i < SLEEP_COPROCS; i++) { - struct t8140_sleep_coproc *c = &t8140_sleep_coprocs[i]; + struct apple_sleep_coproc *c = &apple_sleep_coprocs[i]; if (!c->dev || !c->ready || c->stopped || !c->last_check) continue; @@ -865,7 +926,7 @@ static int t8140_sleep_coprocs_check(struct t8140_sleep *s) * until WD2 resets the SoC. Each answers here, before the display goes off, and waits for the * stop or a refusal. */ -static int t8140_sleep_cpus_hold(struct t8140_sleep *s) +static int apple_sleep_cpus_hold(struct apple_sleep *s) { int ret = spin_table_hold_parked(); @@ -876,13 +937,13 @@ static int t8140_sleep_cpus_hold(struct t8140_sleep *s) } /* Last of all the refusals: an enclave that gated has to be started again. */ -static int t8140_sleep_enclave_tell(struct t8140_sleep *s) +static int apple_sleep_enclave_tell(struct apple_sleep *s) { int ret; - if (!t8140_sleep_enclave.tell) + if (!apple_sleep_enclave.tell) return 0; - ret = t8140_sleep_enclave.tell(t8140_sleep_enclave.data); + ret = apple_sleep_enclave.tell(apple_sleep_enclave.data); if (ret) dev_err(s->dev, "refused: the enclave didn't take the sleep request: %d\n", ret); @@ -895,14 +956,14 @@ static int t8140_sleep_enclave_tell(struct t8140_sleep *s) * before macOS's off words drop under them: the GPU withdraws its PMP request there, and the * PMP has to see that before its own stop. The rest after the match. */ -static void t8140_sleep_coprocs_stop(struct t8140_sleep *s, bool first) +static void apple_sleep_coprocs_stop(struct apple_sleep *s, bool first) { int i; if (first) s->pcie_ans_stopped = false; for (i = 0; i < SLEEP_COPROCS; i++) { - struct t8140_sleep_coproc *c = &t8140_sleep_coprocs[i]; + struct apple_sleep_coproc *c = &apple_sleep_coprocs[i]; if (!c->dev || !c->ready || c->first != first) continue; @@ -911,28 +972,28 @@ static void t8140_sleep_coprocs_stop(struct t8140_sleep *s, bool first) continue; if (c->last_stop) c->last_stop(c->data); - t8140_sleep_coproc_run_off(c); + apple_sleep_coproc_run_off(c); } dsb(sy); } -/* MPIDR is cluster 0 cores 0-3 at 0.0.n and cluster 1 cores 0-1 at 1.1.n. */ -static unsigned int t8140_cpu_cluster(u64 mpidr) +/* MPIDR's cluster is affinity 2, its core affinity 0, as 0.0.n and 1.1.n on T8140. */ +static unsigned int sleep_cpu_cluster(u64 mpidr) { return MPIDR_AFFINITY_LEVEL(mpidr, 2); } -static unsigned int t8140_cpu_core(u64 mpidr) +static unsigned int sleep_cpu_core(u64 mpidr) { return MPIDR_AFFINITY_LEVEL(mpidr, 0); } -static u32 t8140_cpu_start_bit(u64 mpidr) +static u32 sleep_cpu_start_bit(u64 mpidr) { - return BIT(4 * t8140_cpu_cluster(mpidr) + t8140_cpu_core(mpidr)); + return BIT(4 * sleep_cpu_cluster(mpidr) + sleep_cpu_core(mpidr)); } -static void t8140_deep_wfi_arm(void) +static void sleep_deep_wfi_arm(void) { /* A timer left running ends the WFI, and costs power until reset. */ write_sysreg(ARCH_TIMER_CTRL_IT_MASK, cntp_ctl_el0); @@ -941,9 +1002,9 @@ static void t8140_deep_wfi_arm(void) } /* A core's last instructions: nothing brings it back but a reset. */ -static void __noreturn t8140_deep_wfi(void) +static void __noreturn sleep_deep_wfi(void) { - t8140_deep_wfi_arm(); + sleep_deep_wfi_arm(); while (true) { dsb(sy); @@ -953,32 +1014,32 @@ static void __noreturn t8140_deep_wfi(void) } } -static void t8140_cpu_stop(u64 mpidr) +static void sleep_cpu_stop(u64 mpidr) { - writel(t8140_cpu_start_bit(mpidr), t8140_sleep->cpu_start + CPU_START_STOP); + writel(sleep_cpu_start_bit(mpidr), apple_sleep->cpu_start + CPU_START_STOP); dsb(sy); } -static void t8140_cpu_off(u64 mpidr) +static void sleep_cpu_off(u64 mpidr) { - t8140_deep_wfi(); + sleep_deep_wfi(); } -static int t8140_cpu_on(u64 mpidr) +static int sleep_cpu_on(u64 mpidr) { - struct t8140_sleep *s = t8140_sleep; - unsigned int cluster = t8140_cpu_cluster(mpidr); + struct apple_sleep *s = apple_sleep; + unsigned int cluster = sleep_cpu_cluster(mpidr); - writel(t8140_cpu_start_bit(mpidr), s->cpu_start + CPU_START_SYS); - writel(BIT(t8140_cpu_core(mpidr)), s->cpu_start + CPU_START_CLUSTER(cluster)); + writel(sleep_cpu_start_bit(mpidr), s->cpu_start + CPU_START_SYS); + writel(BIT(sleep_cpu_core(mpidr)), s->cpu_start + CPU_START_CLUSTER(cluster)); return 0; } -static const struct spin_table_power_ops t8140_cpu_power_ops = { - .cpu_stop = t8140_cpu_stop, - .cpu_off = t8140_cpu_off, - .cpu_on = t8140_cpu_on, +static const struct spin_table_power_ops sleep_cpu_power_ops = { + .cpu_stop = sleep_cpu_stop, + .cpu_off = sleep_cpu_off, + .cpu_on = sleep_cpu_on, }; /* @@ -986,7 +1047,7 @@ static const struct spin_table_power_ops t8140_cpu_power_ops = { * Writing the flags clears them. Values measured, for instance ANS 0x1f0020ff, * 0x0f0020ff, 0x0f0023f0. */ -static u32 t8140_ps_lower_start(struct t8140_sleep *s, u32 offset) +static u32 sleep_ps_lower_start(struct apple_sleep *s, u32 offset) { void __iomem *reg = s->ps + offset; u32 val = readl(reg); @@ -999,21 +1060,21 @@ static u32 t8140_ps_lower_start(struct t8140_sleep *s, u32 offset) return val; } -static void t8140_ps_lower_finish(struct t8140_sleep *s, u32 offset, u32 val) +static void sleep_ps_lower_finish(struct apple_sleep *s, u32 offset, u32 val) { writel((val & ~PS_TARGET) | PS_FLAGS, s->ps + offset); } -static void t8140_ps_lower(struct t8140_sleep *s, u32 offset) +static void sleep_ps_lower(struct apple_sleep *s, u32 offset) { - t8140_ps_lower_finish(s, offset, t8140_ps_lower_start(s, offset)); + sleep_ps_lower_finish(s, offset, sleep_ps_lower_start(s, offset)); } /* * As macOS raises a domain: the target, then AUTO_ENABLE once it reads on. Measured * byte for byte on ANS and PCIe, for instance 0x0f002300, 0x0f00200f, 0x1f00200f. */ -static int t8140_ps_raise_start(struct t8140_sleep *s, u32 offset) +static int sleep_ps_raise_start(struct apple_sleep *s, u32 offset) { void __iomem *reg = s->ps + offset; u32 now; @@ -1024,7 +1085,7 @@ static int t8140_ps_raise_start(struct t8140_sleep *s, u32 offset) PS_OFF_TIMEOUT_US); } -static void t8140_ps_raise_finish(struct t8140_sleep *s, u32 offset) +static void sleep_ps_raise_finish(struct apple_sleep *s, u32 offset) { void __iomem *reg = s->ps + offset; @@ -1032,17 +1093,17 @@ static void t8140_ps_raise_finish(struct t8140_sleep *s, u32 offset) reg); } -static int t8140_ps_raise(struct t8140_sleep *s, u32 offset) +static int sleep_ps_raise(struct apple_sleep *s, u32 offset) { - int ret = t8140_ps_raise_start(s, offset); + int ret = sleep_ps_raise_start(s, offset); - t8140_ps_raise_finish(s, offset); + sleep_ps_raise_finish(s, offset); return ret; } /* A domain the hardware idled under AUTO_ENABLE comes back once that's cleared. */ -static bool t8140_ps_wait_on(struct t8140_sleep *s, u32 offset, u32 val) +static bool sleep_ps_wait_on(struct apple_sleep *s, u32 offset, u32 val) { if (!FIELD_GET(PS_TARGET, val)) return false; @@ -1051,7 +1112,7 @@ static bool t8140_ps_wait_on(struct t8140_sleep *s, u32 offset, u32 val) PS_OFF_TIMEOUT_US); } -static int t8140_ps_wait_off(struct t8140_sleep *s, u32 offset) +static int sleep_ps_wait_off(struct apple_sleep *s, u32 offset) { u32 val; @@ -1059,13 +1120,13 @@ static int t8140_ps_wait_off(struct t8140_sleep *s, u32 offset) 10, PS_OFF_TIMEOUT_US); } -static bool t8140_ps_on(struct t8140_sleep *s, u32 offset) +static bool sleep_ps_on(struct apple_sleep *s, u32 offset) { return FIELD_GET(PS_ACTUAL, readl(s->ps + offset)); } /* @pd's word in "ps": a power controller node, child of the PMGR that "ps" maps. */ -static int t8140_ps_offset(struct t8140_sleep *s, struct device_node *pd) +static int sleep_ps_offset(struct apple_sleep *s, struct device_node *pd) { struct device_node *pmgr __free(device_node) = of_get_parent(pd); struct resource res; @@ -1083,9 +1144,9 @@ int apple_sleep_domain_check(struct device_node *pd) { int off; - if (!t8140_sleep) + if (!apple_sleep) return -ENODEV; - off = t8140_ps_offset(t8140_sleep, pd); + off = sleep_ps_offset(apple_sleep, pd); return off < 0 ? off : 0; } EXPORT_SYMBOL_GPL(apple_sleep_domain_check); @@ -1097,41 +1158,41 @@ EXPORT_SYMBOL_GPL(apple_sleep_domain_check); */ int apple_sleep_domain_set(struct device_node *pd, bool on) { - struct t8140_sleep *s = t8140_sleep; + struct apple_sleep *s = apple_sleep; int off, ret = 0; u32 val; if (!s) return -ENODEV; - off = t8140_ps_offset(s, pd); + off = sleep_ps_offset(s, pd); if (off < 0) return off; val = readl(s->ps + off); if (on) { if (FIELD_GET(PS_TARGET, val) != 0xf || FIELD_GET(PS_ACTUAL, val) != 0xf) - ret = t8140_ps_raise(s, off); + ret = sleep_ps_raise(s, off); else if (!(val & PS_AUTO_ENABLE)) - t8140_ps_raise_finish(s, off); + sleep_ps_raise_finish(s, off); if (!ret) clear_bit(off / 4, s->driver_down); return ret; } set_bit(off / 4, s->driver_down); if (FIELD_GET(PS_TARGET, val)) - t8140_ps_lower(s, off); - return t8140_ps_wait_off(s, off); + sleep_ps_lower(s, off); + return sleep_ps_wait_off(s, off); } EXPORT_SYMBOL_GPL(apple_sleep_domain_set); bool apple_sleep_domain_is_down(struct device_node *pd) { - struct t8140_sleep *s = t8140_sleep; + struct apple_sleep *s = apple_sleep; int off; if (!s) return false; - off = t8140_ps_offset(s, pd); + off = sleep_ps_offset(s, pd); return off >= 0 && test_bit(off / 4, s->driver_down); } EXPORT_SYMBOL_GPL(apple_sleep_domain_is_down); @@ -1150,7 +1211,7 @@ EXPORT_SYMBOL_GPL(apple_sleep_domain_is_down); #define ENGINE_RESET_PS 4 #define ENGINE_RESET_DARTS 2 -static int t8140_engine_reset_wait(struct t8140_sleep *s, u32 offset, u32 actual) +static int sleep_engine_reset_wait(struct apple_sleep *s, u32 offset, u32 actual) { u32 val; @@ -1160,7 +1221,7 @@ static int t8140_engine_reset_wait(struct t8140_sleep *s, u32 offset, u32 actual } /* A domain of the PMGR block mapped here as "ps", and its offset in it. */ -static bool t8140_ps_domain(struct t8140_sleep *s, struct device_node *pd, u32 *offset) +static bool sleep_ps_domain(struct apple_sleep *s, struct device_node *pd, u32 *offset) { int idx = of_property_match_string(s->dev->of_node, "reg-names", "ps"); struct device_node *parent = of_get_parent(pd); @@ -1176,7 +1237,7 @@ static bool t8140_ps_domain(struct t8140_sleep *s, struct device_node *pd, u32 * } /* The @index-th DART in @np's iommus, a reference held, or NULL past the last. */ -static struct device *t8140_iommus_dart(struct device_node *np, int index) +static struct device *sleep_iommus_dart(struct device_node *np, int index) { struct of_phandle_args args; struct platform_device *pdev; @@ -1191,7 +1252,7 @@ static struct device *t8140_iommus_dart(struct device_node *np, int index) int apple_sleep_engine_reset(struct device *dev) { - struct t8140_sleep *s = t8140_sleep; + struct apple_sleep *s = apple_sleep; struct device *darts[ENGINE_RESET_DARTS] = {}; u32 ps[ENGINE_RESET_PS], saved[ENGINE_RESET_PS], val; struct of_phandle_iterator it; @@ -1201,7 +1262,7 @@ int apple_sleep_engine_reset(struct device *dev) if (!s) return -ENODEV; of_for_each_phandle(&it, err, dev->of_node, "power-domains", NULL, 0) { - if (!t8140_ps_domain(s, it.node, &val)) + if (!sleep_ps_domain(s, it.node, &val)) continue; if (n == ENGINE_RESET_PS) { of_node_put(it.node); @@ -1220,7 +1281,7 @@ int apple_sleep_engine_reset(struct device *dev) return -ENODEV; for (i = 0; i < ENGINE_RESET_DARTS; i++) { - darts[i] = t8140_iommus_dart(dev->of_node, i); + darts[i] = sleep_iommus_dart(dev->of_node, i); if (!darts[i]) break; apple_dart_state_save(darts[i]); @@ -1229,10 +1290,10 @@ int apple_sleep_engine_reset(struct device *dev) for (i = 0; i < n && !ret; i++) { saved[i] = readl(s->ps + ps[i]); writel(PS_ENGINE_RESET_4, s->ps + ps[i]); - ret = t8140_engine_reset_wait(s, ps[i], 4); + ret = sleep_engine_reset_wait(s, ps[i], 4); if (!ret) { writel(PS_ENGINE_RESET_0, s->ps + ps[i]); - ret = t8140_engine_reset_wait(s, ps[i], 0); + ret = sleep_engine_reset_wait(s, ps[i], 0); } } if (!ret) { @@ -1241,7 +1302,7 @@ int apple_sleep_engine_reset(struct device *dev) writel(0, s->ps + ps[i]); for (i = n - 1; i >= 0 && !ret; i--) { writel(PS_TARGET, s->ps + ps[i]); - ret = t8140_engine_reset_wait(s, ps[i], FIELD_MAX(PS_ACTUAL)); + ret = sleep_engine_reset_wait(s, ps[i], FIELD_MAX(PS_ACTUAL)); if (!ret) writel(saved[i] & ~(PS_FLAGS | PS_RESET | PS_ACTUAL), s->ps + ps[i]); @@ -1262,31 +1323,31 @@ int apple_sleep_engine_reset(struct device *dev) EXPORT_SYMBOL_GPL(apple_sleep_engine_reset); /* The order macOS does it in, across clusters. */ -static void t8140_dvfm_park(struct t8140_sleep *s) +static void sleep_dvfm_park(struct apple_sleep *s) { int cluster, word; - for (cluster = 0; cluster < T8140_CLUSTERS; cluster++) { + for (cluster = 0; cluster < SLEEP_CLUSTERS; cluster++) { s->dvfm_ctl[cluster] = readq(s->dvfm[cluster] + DVFM_CTL); - for (word = 0; word < ARRAY_SIZE(t8140_dvfm_words); word++) + for (word = 0; word < ARRAY_SIZE(sleep_dvfm_words); word++) s->dvfm_saved[cluster][word] = - readq(s->dvfm[cluster] + t8140_dvfm_words[word]); + readq(s->dvfm[cluster] + sleep_dvfm_words[word]); } writeq(0, s->dvfm[0] + DVFM_CTL); writeq(0, s->dvfm[1] + DVFM_CTL); for (word = 0; word < 2; word++) - for (cluster = 0; cluster < T8140_CLUSTERS; cluster++) + for (cluster = 0; cluster < SLEEP_CLUSTERS; cluster++) writeq(s->dvfm_saved[cluster][word] & ~DVFM_ENABLE, - s->dvfm[cluster] + t8140_dvfm_words[word]); - for (cluster = 0; cluster < T8140_CLUSTERS; cluster++) + s->dvfm[cluster] + sleep_dvfm_words[word]); + for (cluster = 0; cluster < SLEEP_CLUSTERS; cluster++) for (word = 2; word < 4; word++) writeq(s->dvfm_saved[cluster][word] & ~DVFM_ENABLE, - s->dvfm[cluster] + t8140_dvfm_words[word]); + s->dvfm[cluster] + sleep_dvfm_words[word]); s->dvfm_parked = true; } -static void t8140_dvfm_unpark(struct t8140_sleep *s) +static void sleep_dvfm_unpark(struct apple_sleep *s) { int cluster, word; @@ -1296,115 +1357,86 @@ static void t8140_dvfm_unpark(struct t8140_sleep *s) writeq(s->dvfm_ctl[0], s->dvfm[0] + DVFM_CTL); writeq(s->dvfm_ctl[1], s->dvfm[1] + DVFM_CTL); - for (cluster = 0; cluster < T8140_CLUSTERS; cluster++) + for (cluster = 0; cluster < SLEEP_CLUSTERS; cluster++) for (word = 2; word < 4; word++) writeq(s->dvfm_saved[cluster][word], - s->dvfm[cluster] + t8140_dvfm_words[word]); + s->dvfm[cluster] + sleep_dvfm_words[word]); for (word = 1; word >= 0; word--) - for (cluster = 0; cluster < T8140_CLUSTERS; cluster++) + for (cluster = 0; cluster < SLEEP_CLUSTERS; cluster++) writeq(s->dvfm_saved[cluster][word], - s->dvfm[cluster] + t8140_dvfm_words[word]); + s->dvfm[cluster] + sleep_dvfm_words[word]); } /* - * What XNU polls just before its complex words, and panics on if one is on, besides the - * display's, ANS's and ATC0's, which the commit lowers itself. Every device has suspended by - * the last step, so one still on has no user: it's lowered there, and a sleep with one that - * won't go off is refused. They come back on after the sleep or a refusal, for genpd's books. + * Every device has suspended by the last step, so a stray word still on has no user: it's + * lowered there, and a sleep with one that won't go off is refused. They come back on after + * the sleep or a refusal, for genpd's books. */ -static const struct { - const char *name; - u32 offset; -} t8140_stray_words[] = { - { "AVD_SYS", 0x270 }, { "VENC0_SYS", 0x240 }, { "VENC1_SYS", 0x248 }, - { "GFX", 0x1f0 }, { "JPG", 0x230 }, -}; - -static void t8140_sleep_stray_restore(struct t8140_sleep *s) +static void apple_sleep_stray_restore(struct apple_sleep *s) { + const struct apple_sleep_word *stray = s->soc->stray; int i; - for (i = ARRAY_SIZE(t8140_stray_words) - 1; i >= 0; i--) + for (i = s->soc->nstray - 1; i >= 0; i--) if (s->stray_lowered & BIT(i)) - t8140_ps_raise(s, t8140_stray_words[i].offset); + sleep_ps_raise(s, stray[i].offset); s->stray_lowered = 0; } -static int t8140_sleep_lower_stray(struct t8140_sleep *s) +static int apple_sleep_lower_stray(struct apple_sleep *s) { + const struct apple_sleep_word *stray = s->soc->stray; int i, ret = 0; s->stray_lowered = 0; - for (i = 0; i < ARRAY_SIZE(t8140_stray_words); i++) { - u32 off = t8140_stray_words[i].offset; + for (i = 0; i < s->soc->nstray; i++) { + u32 off = stray[i].offset; - if (!t8140_ps_on(s, off)) + if (!sleep_ps_on(s, off)) continue; /* Its driver's own last stop lowers it, at the commit. */ - if (t8140_sleep_coproc_lowers(off)) { + if (apple_sleep_coproc_lowers(off)) { dev_dbg(s->dev, "%s: left on for its driver's last stop\n", - t8140_stray_words[i].name); + stray[i].name); continue; } - t8140_ps_lower(s, off); + sleep_ps_lower(s, off); s->stray_lowered |= BIT(i); - if (t8140_ps_wait_off(s, off)) { + if (sleep_ps_wait_off(s, off)) { dev_err(s->dev, "refused: %s won't go off: %#x\n", - t8140_stray_words[i].name, readl(s->ps + off)); + stray[i].name, readl(s->ps + off)); ret = -EBUSY; } else { dev_info(s->dev, "%s was on at the last step, lowered\n", - t8140_stray_words[i].name); + stray[i].name); } } if (ret) - t8140_sleep_stray_restore(s); + apple_sleep_stray_restore(s); return ret; } /* - * T8140's coprocessors that must be bound for a sleep, the ones that must also have stopped - * by the last step (a commit with the PMP and SIO still running reset the SoC 5.5 s later), - * and the domains macOS has off by then. Taken in process context, since the checks run with - * interrupts off. A driver whose stop failed has already failed the suspend, naming itself, - * so what's left is whether it's bound at all, and what the hardware shows. @ps is the CPU's - * own domain where a driver may power it off, as the DCPs' at DPMS off. + * The SoC's coprocessors that must be bound for a sleep, the ones that must also have stopped + * by the last step, and the domains macOS has off by then. Taken in process context, since + * the checks run with interrupts off. A driver whose stop failed has already failed the + * suspend, naming itself, so what's left is whether it's bound at all, and what the hardware + * shows. */ -static const struct { - const char *path; - bool must_stop; - u32 ps; -} t8140_gate_coprocs[] = { - { "/soc/dcp@412e00000", true, PS_DISP_CPU }, - { "/soc/dcp@416e00000", true, PS_DISPEXT0_CPU }, - { "/soc/pmp@300500000", true }, { "/soc/dma-controller@385e00000", true }, - { "/soc/nvme@3cdcc0000" }, { "/soc/mtp@314600000" }, - { "/soc/aop@310c00000" }, { "/soc/smc@30c600000" }, - { "/soc/sep@282600000" }, { "/soc/gpu@482600000" }, -}; - -static const struct { - const char *name; - u32 offset; -} t8140_gate_off[] = { - { "ane_sys", 0x290 }, { "isp_sys", 0x268 }, { "prores", 0x250 }, - { "avd_sys", 0x270 }, { "jpg", 0x230 }, { "venc0_sys", 0x240 }, -}; - -static void t8140_sleep_gate_prepare(struct t8140_sleep *s) +static void apple_sleep_gate_prepare(struct apple_sleep *s) { + const struct apple_sleep_gate_coproc *coprocs = s->soc->gate_coprocs; int i; s->gate_count = 0; - for (i = 0; i < ARRAY_SIZE(t8140_gate_coprocs) && s->gate_count < ARRAY_SIZE(s->gate); - i++) { - struct device_node *c = of_find_node_by_path(t8140_gate_coprocs[i].path); + for (i = 0; i < s->soc->ngate_coprocs && s->gate_count < ARRAY_SIZE(s->gate); i++) { + struct device_node *c = of_find_node_by_path(coprocs[i].path); struct platform_device *pdev = c ? of_find_device_by_node(c) : NULL; - struct t8140_sleep_gate *g; + struct apple_sleep_gate *g; if (!c) { - dev_warn(s->dev, "%s isn't in this tree\n", t8140_gate_coprocs[i].path); + dev_warn(s->dev, "%s isn't in this tree\n", coprocs[i].path); continue; } g = &s->gate[s->gate_count++]; @@ -1412,8 +1444,8 @@ static void t8140_sleep_gate_prepare(struct t8140_sleep *s) g->np = c; g->bound = pdev && device_is_bound(&pdev->dev); g->sep = of_node_name_eq(c, "sep"); - g->must_stop = t8140_gate_coprocs[i].must_stop; - g->ps = t8140_gate_coprocs[i].ps; + g->must_stop = coprocs[i].must_stop; + g->ps = coprocs[i].ps; if (g->must_stop && !g->asc) { int idx = of_property_match_string(c, "reg-names", "coproc"); @@ -1432,48 +1464,50 @@ static void t8140_sleep_gate_prepare(struct t8140_sleep *s) * Names everything wrong, so one boot shows the whole list. A coprocessor that must stop and * still runs refuses the sleep; the rest only warns, as a real sleep is the only full test. */ -static bool t8140_sleep_gate_failed(struct t8140_sleep *s) +static bool apple_sleep_gate_failed(struct apple_sleep *s) { + u32 sep = s->soc->ps.sep; int i, bad = 0, running = 0; for (i = 0; i < s->gate_count; i++) { - struct t8140_sleep_gate *g = &s->gate[i]; + struct apple_sleep_gate *g = &s->gate[i]; if (!g->bound) { dev_err(s->dev, "%s isn't bound\n", g->name); bad++; /* Nobody told it of the sleep; a commit with it running never returns. */ - if (g->sep && t8140_ps_on(s, PS_SEP)) { + if (g->sep && sleep_ps_on(s, sep)) { dev_err(s->dev, "sep runs with no driver bound: %#x\n", - readl(s->ps + PS_SEP)); + readl(s->ps + sep)); running++; } continue; } /* A CPU whose domain is off isn't running, and its registers aren't there. */ - if (g->ps && !t8140_ps_on(s, g->ps)) + if (g->ps && !sleep_ps_on(s, g->ps)) continue; if (g->must_stop && (!g->asc || ((readl(g->asc + ASC_CPU_CONTROL) & ASC_CPU_CONTROL_RUN) && - !t8140_sleep_coproc_ready_node(g->np)))) { + !apple_sleep_coproc_ready_node(g->np)))) { dev_err(s->dev, "%s didn't stop: control %#x status %#x\n", g->name, g->asc ? readl(g->asc + ASC_CPU_CONTROL) : 0, g->asc ? readl(g->asc + ASC_CPU_STATUS) : 0); bad++; running++; } - /* Told later, as the last refusal: see t8140_sleep_enclave_tell(). */ - if (g->sep && t8140_ps_on(s, PS_SEP) && !t8140_sleep_enclave.tell) { - dev_err(s->dev, "sep hasn't gated itself: %#x\n", readl(s->ps + PS_SEP)); + /* Told later, as the last refusal: see apple_sleep_enclave_tell(). */ + if (g->sep && sleep_ps_on(s, sep) && !apple_sleep_enclave.tell) { + dev_err(s->dev, "sep hasn't gated itself: %#x\n", readl(s->ps + sep)); bad++; running++; } } - for (i = 0; i < ARRAY_SIZE(t8140_gate_off); i++) { - if (t8140_ps_on(s, t8140_gate_off[i].offset)) { - dev_err(s->dev, "%s is on: %#x\n", t8140_gate_off[i].name, - readl(s->ps + t8140_gate_off[i].offset)); + for (i = 0; i < s->soc->ngate_off; i++) { + const struct apple_sleep_word *w = &s->soc->gate_off[i]; + + if (sleep_ps_on(s, w->offset)) { + dev_err(s->dev, "%s is on: %#x\n", w->name, readl(s->ps + w->offset)); bad++; } } @@ -1491,7 +1525,7 @@ static bool t8140_sleep_gate_failed(struct t8140_sleep *s) return false; } -static void t8140_wake_unmask(u32 *val, u32 id) +static void sleep_wake_unmask(u32 *val, u32 id) { if (id / 32 < WAKE_WORDS) val[id / 32] &= ~BIT(id % 32); @@ -1504,24 +1538,25 @@ static void t8140_wake_unmask(u32 *val, u32 id) * source 0 is masked with the rest: that held under macOS with its handler hidden, * as did leaving it enabled with the AMCC enables off. The two together are unmeasured. */ -static void t8140_wake_masks(struct t8140_sleep *s, u32 *val) +static void sleep_wake_masks(struct apple_sleep *s, u32 *val) { int i; for (i = 0; i < WAKE_WORDS; i++) - val[i] = i == WAKE_WORDS - 1 ? WAKE_WORD7_ALL : U32_MAX; + val[i] = i == WAKE_WORDS - 1 ? s->soc->wake_last_word : U32_MAX; for (i = 0; i < s->wake_enable_count; i++) - t8140_wake_unmask(val, s->wake_enable[i]); + sleep_wake_unmask(val, s->wake_enable[i]); } /* The last checks, then the stray words lowered: what has stopped may not return until a wake. */ -static int t8140_sleep_refuse(struct t8140_sleep *s) +static int apple_sleep_refuse(struct apple_sleep *s) { + const struct apple_sleep_domains *d = &s->soc->ps; bool refused = false, idled = false; int i; for (i = 0; i < SLEEP_COPROCS; i++) { - struct t8140_sleep_coproc *c = &t8140_sleep_coprocs[i]; + struct apple_sleep_coproc *c = &apple_sleep_coprocs[i]; if (!c->dev) continue; @@ -1554,46 +1589,46 @@ static int t8140_sleep_refuse(struct t8140_sleep *s) } /* Only ANS's driver can stop it, and it doesn't come back without a real sleep. */ - if (!idled && (t8140_ps_on(s, PS_ANS) || t8140_ps_on(s, PS_APCIE_SYS_GP))) { + if (!idled && (sleep_ps_on(s, d->ans) || sleep_ps_on(s, d->apcie_sys_gp))) { dev_err(s->dev, "refused: ANS or PCIe is on with no driver to stop it\n"); refused = true; } /* The engine waits for DISP_CPU with no timeout, so this must never slip. */ - for (i = 0; i < T8140_DISPLAY_COUNT; i++) { - u32 ps = i ? PS_DISPEXT0_CPU : PS_DISP_CPU; + for (i = 0; i < SLEEP_DISPLAY_COUNT; i++) { + u32 ps = d->disp[i][0]; - if (s->display[i].bound && t8140_ps_on(s, ps) && + if (s->display[i].bound && sleep_ps_on(s, ps) && (readl(s->display[i].asc + ASC_CPU_CONTROL) & ASC_CPU_CONTROL_RUN)) { dev_err(s->dev, "refused: a bound display coprocessor is still running\n"); refused = true; } } - if (t8140_sleep_gate_failed(s)) + if (apple_sleep_gate_failed(s)) refused = true; if (refused) return -EBUSY; - return t8140_sleep_lower_stray(s); + return apple_sleep_lower_stray(s); } -static void t8140_wake_arm(struct t8140_sleep *s) +static void sleep_wake_arm(struct apple_sleep *s) { u32 val[WAKE_WORDS]; int i; for (i = 0; i < WAKE_WORDS; i++) s->wake_saved[i] = readl(s->wake + WAKE_MASK(i)); - t8140_wake_masks(s, val); + sleep_wake_masks(s, val); for (i = 0; i < WAKE_WORDS; i++) { writel(val[i], s->wake + WAKE_MASK(i)); s->wake_armed[i] = ~val[i]; } } -static void t8140_wake_restore(struct t8140_sleep *s) +static void sleep_wake_restore(struct apple_sleep *s) { int i; @@ -1614,22 +1649,19 @@ static void t8140_wake_restore(struct t8140_sleep *s) * writes. The coprocessors' secure mailboxes are not used here. Whether that * holds is open. */ -static int t8140_display_off(struct t8140_sleep *s) +static int sleep_display_off(struct apple_sleep *s) { - static const u32 domains[T8140_DISPLAY_COUNT][3] = { - { PS_DISP_CPU, PS_DISP_FE, PS_DISP_SYS }, - { PS_DISPEXT0_CPU, PS_DISPEXT0_FE, PS_DISPEXT0_SYS }, - }; + const u32 (*domains)[3] = s->soc->ps.disp; int i, ret = 0; - for (i = T8140_DISPLAY_COUNT - 1; i >= 0; i--) { + for (i = SLEEP_DISPLAY_COUNT - 1; i >= 0; i--) { void __iomem *asc = s->display[i].asc; /* The engine doesn't wait for DISPEXT0. */ if (i == 1 && !s->display[i].bound) continue; - if (t8140_ps_on(s, domains[i][0]) && asc && + if (sleep_ps_on(s, domains[i][0]) && asc && (readl(asc + ASC_CPU_CONTROL) & ASC_CPU_CONTROL_RUN)) { writel(0, asc + ASC_CPU_CONTROL); writel(0, asc + ASC_CPU_CONTROL); @@ -1638,24 +1670,24 @@ static int t8140_display_off(struct t8140_sleep *s) /* Reads as a count of the DCPs powered, and macOS ends at 0. */ writel(i, s->pmc_dcp); if (i == 0) { - if (readl(s->ps + PS_DISP_CPU) & PS_AUTO_ENABLE) - writel(readl(s->ps + PS_DISP_CPU) & - ~(PS_AUTO_ENABLE | PS_FLAGS), s->ps + PS_DISP_CPU); - writel(3, s->pwrgate + PWRGATE_DISP_0); - writel(0, s->pwrgate + PWRGATE_DISP_1); + if (readl(s->ps + domains[0][0]) & PS_AUTO_ENABLE) + writel(readl(s->ps + domains[0][0]) & + ~(PS_AUTO_ENABLE | PS_FLAGS), s->ps + domains[0][0]); + writel(3, s->pwrgate + s->soc->pwrgate_disp[0]); + writel(0, s->pwrgate + s->soc->pwrgate_disp[1]); } - t8140_ps_lower(s, domains[i][0]); - t8140_ps_lower(s, domains[i][1]); - t8140_ps_lower(s, domains[i][2]); + sleep_ps_lower(s, domains[i][0]); + sleep_ps_lower(s, domains[i][1]); + sleep_ps_lower(s, domains[i][2]); } - for (i = 0; i < T8140_DISPLAY_COUNT; i++) { + for (i = 0; i < SLEEP_DISPLAY_COUNT; i++) { int err; if (i == 1 && !s->display[i].bound) continue; - err = t8140_ps_wait_off(s, domains[i][0]); + err = sleep_ps_wait_off(s, domains[i][0]); if (err) { dev_err(s->dev, "refused: display domain %#x stayed on: %#x\n", @@ -1668,7 +1700,7 @@ static int t8140_display_off(struct t8140_sleep *s) } /* One command on the port controller's bus, polled: no other CPU, and interrupts are off. */ -static int t8140_hpm_cmd(struct t8140_sleep *s, u32 op, u32 extra, const u32 *in, u32 *out) +static int sleep_hpm_cmd(struct apple_sleep *s, u32 op, u32 extra, const u32 *in, u32 *out) { void __iomem *b = s->hpm_spmi; u32 status, reply; @@ -1699,21 +1731,21 @@ static int t8140_hpm_cmd(struct t8140_sleep *s, u32 op, u32 extra, const u32 *in return op == SPMI_OP_ZERO_WRITE || (reply & 0xff) == op ? 0 : -EIO; } -static int t8140_hpm_select(struct t8140_sleep *s, u32 page) +static int sleep_hpm_select(struct apple_sleep *s, u32 page) { u32 val = ~0U; int i, ret; - ret = t8140_hpm_cmd(s, SPMI_OP_ZERO_WRITE, page << 8, NULL, NULL); + ret = sleep_hpm_cmd(s, SPMI_OP_ZERO_WRITE, page << 8, NULL, NULL); for (i = 0; !ret && i < 2000; i++) { - ret = t8140_hpm_cmd(s, SPMI_OP_EXT_READ(1), HPM_REG_SELECT, NULL, &val); + ret = sleep_hpm_cmd(s, SPMI_OP_EXT_READ(1), HPM_REG_SELECT, NULL, &val); if (!ret && (val & 0xff) == page) return 0; /* A select it dropped, waking, is sent again. page | 0x80 is one in progress. */ if (!ret && (val & 0xff) != (page | 0x80) && i % 20 == 19) { - ret = t8140_hpm_cmd(s, SPMI_OP_WAKEUP, 0, NULL, NULL); + ret = sleep_hpm_cmd(s, SPMI_OP_WAKEUP, 0, NULL, NULL); if (!ret) - ret = t8140_hpm_cmd(s, SPMI_OP_ZERO_WRITE, page << 8, NULL, NULL); + ret = sleep_hpm_cmd(s, SPMI_OP_ZERO_WRITE, page << 8, NULL, NULL); } udelay(10); } @@ -1725,31 +1757,31 @@ static int t8140_hpm_select(struct t8140_sleep *s, u32 page) * What macOS sends the other port controller about a second before a sleep, measured * in three runs: system power state 3, then the bus's sleep command. */ -static int t8140_hpm_sleep(struct t8140_sleep *s) +static int sleep_hpm_sleep(struct apple_sleep *s) { u32 data = HPM_SSPS_S3, cmd = HPM_CMD_SSPS, val = ~0U; int i, ret; - ret = t8140_hpm_cmd(s, SPMI_OP_WAKEUP, 0, NULL, NULL); + ret = sleep_hpm_cmd(s, SPMI_OP_WAKEUP, 0, NULL, NULL); if (!ret) - ret = t8140_hpm_select(s, HPM_PAGE_DATA1); + ret = sleep_hpm_select(s, HPM_PAGE_DATA1); if (!ret) - ret = t8140_hpm_cmd(s, SPMI_OP_EXT_WRITE(1), HPM_REG_DATA_CMD, &data, NULL); + ret = sleep_hpm_cmd(s, SPMI_OP_EXT_WRITE(1), HPM_REG_DATA_CMD, &data, NULL); if (!ret) - ret = t8140_hpm_select(s, HPM_PAGE_CMD1); + ret = sleep_hpm_select(s, HPM_PAGE_CMD1); if (!ret) - ret = t8140_hpm_cmd(s, SPMI_OP_EXT_WRITE(4), HPM_REG_DATA_CMD, &cmd, NULL); + ret = sleep_hpm_cmd(s, SPMI_OP_EXT_WRITE(4), HPM_REG_DATA_CMD, &cmd, NULL); /* Selected afresh for each read, as macOS does. */ for (i = 0; !ret && val && i < 100; i++) { udelay(1000); - ret = t8140_hpm_select(s, HPM_PAGE_CMD1); + ret = sleep_hpm_select(s, HPM_PAGE_CMD1); if (!ret) - ret = t8140_hpm_cmd(s, SPMI_OP_EXT_READ(4), HPM_REG_DATA, NULL, &val); + ret = sleep_hpm_cmd(s, SPMI_OP_EXT_READ(4), HPM_REG_DATA, NULL, &val); } if (!ret && val) ret = -ETIMEDOUT; /* Asleep either way: a failure here only means it wasn't told first. */ - t8140_hpm_cmd(s, SPMI_OP_SLEEP, 0, NULL, NULL); + sleep_hpm_cmd(s, SPMI_OP_SLEEP, 0, NULL, NULL); return ret; } @@ -1759,18 +1791,18 @@ static int t8140_hpm_sleep(struct t8140_sleep *s) * Left to finish across the commit, that sometimes failed: neither side had a contract or * saw the other, and no VDM got through until a replug. Returns the ms it took. */ -static int t8140_hpm_wait_contract(struct t8140_sleep *s) +static int sleep_hpm_wait_contract(struct apple_sleep *s) { bool wake = true; u32 val = 0; int ms, ret; for (ms = 0; ms < HPM_CONTRACT_MS; ms += 10) { - ret = wake ? t8140_hpm_cmd(s, SPMI_OP_WAKEUP, 0, NULL, NULL) : 0; + ret = wake ? sleep_hpm_cmd(s, SPMI_OP_WAKEUP, 0, NULL, NULL) : 0; if (!ret) - ret = t8140_hpm_select(s, HPM_PAGE_POWER_STATUS); + ret = sleep_hpm_select(s, HPM_PAGE_POWER_STATUS); if (!ret) - ret = t8140_hpm_cmd(s, SPMI_OP_EXT_READ(4), HPM_REG_DATA, NULL, &val); + ret = sleep_hpm_cmd(s, SPMI_OP_EXT_READ(4), HPM_REG_DATA, NULL, &val); if (!ret && (val & HPM_POWER_CONNECTED) && FIELD_GET(HPM_POWER_TYPEC_MODE, val) == HPM_POWER_TYPEC_PD) return ms; @@ -1789,7 +1821,7 @@ static int t8140_hpm_wait_contract(struct t8140_sleep *s) * SMC.OutboxNotEmpty is up at the commit, and so does the partner's new contract. * tipd rearms the link after a refusal. */ -static bool t8140_sleep_usb_down(struct t8140_sleep *s) +static bool apple_sleep_usb_down(struct apple_sleep *s) { u32 cmd = HPM_CMD_COLD_RESET; u32 enable; @@ -1799,7 +1831,8 @@ static bool t8140_sleep_usb_down(struct t8140_sleep *s) return false; s->usb_down_ran = true; - if (!s->hpm_spmi || !FIELD_GET(PS_ACTUAL, readl(s->ps_aon + PS_AON_DEBUG_SWITCH))) + if (!s->hpm_spmi || + !FIELD_GET(PS_ACTUAL, readl(s->ps_aon + s->soc->ps_aon_debug_switch))) return false; dev_info(s->dev, "debug link: cutting it for the sleep\n"); @@ -1807,16 +1840,16 @@ static bool t8140_sleep_usb_down(struct t8140_sleep *s) writel(enable & ~SPMI_NOTIFY_REPLY, s->hpm_spmi + SPMI_NOTIFY_ENABLE); /* A reset that the partner doesn't come back from is done again. */ for (i = 1; i <= HPM_RESET_TRIES; i++) { - ret = t8140_hpm_cmd(s, SPMI_OP_WAKEUP, 0, NULL, NULL); + ret = sleep_hpm_cmd(s, SPMI_OP_WAKEUP, 0, NULL, NULL); if (!ret) - ret = t8140_hpm_select(s, HPM_PAGE_CMD1); + ret = sleep_hpm_select(s, HPM_PAGE_CMD1); if (!ret) - ret = t8140_hpm_cmd(s, SPMI_OP_EXT_WRITE(4), HPM_REG_DATA, &cmd, NULL); + ret = sleep_hpm_cmd(s, SPMI_OP_EXT_WRITE(4), HPM_REG_DATA, &cmd, NULL); if (ret) break; - WRITE_ONCE(t8140_debug_link_cut, true); + WRITE_ONCE(sleep_debug_link_cut, true); mdelay(HPM_RESET_MS); - ms = t8140_hpm_wait_contract(s); + ms = sleep_hpm_wait_contract(s); if (ms >= 0) { dev_info(s->dev, "debug link: contract back %d ms after reset %d\n", HPM_RESET_MS + ms, i); @@ -1825,7 +1858,7 @@ static bool t8140_sleep_usb_down(struct t8140_sleep *s) dev_warn(s->dev, "debug link: no contract with the partner after reset %d\n", i); } if (!ret) - ret = t8140_hpm_sleep(s); + ret = sleep_hpm_sleep(s); writel(1, s->hpm_spmi + SPMI_NOTIFY_ACK); writel(enable, s->hpm_spmi + SPMI_NOTIFY_ENABLE); s->usb_down_ret = ret; @@ -1835,7 +1868,7 @@ static bool t8140_sleep_usb_down(struct t8140_sleep *s) bool apple_sleep_debug_link_cut(void) { - return t8140_sleep ? t8140_sleep_usb_down(t8140_sleep) : false; + return apple_sleep ? apple_sleep_usb_down(apple_sleep) : false; } EXPORT_SYMBOL_GPL(apple_sleep_debug_link_cut); @@ -1845,7 +1878,7 @@ EXPORT_SYMBOL_GPL(apple_sleep_debug_link_cut); */ void apple_sleep_lid_closed_wake(void) { - struct t8140_sleep *s = t8140_sleep; + struct apple_sleep *s = apple_sleep; if (!s || !s->smc_woke_alone) return; @@ -1861,7 +1894,7 @@ EXPORT_SYMBOL_GPL(apple_sleep_lid_closed_wake); * the link untouched. A link that's down or a radio that's gone only makes the poll time * out, and the power goes next either way. */ -static void t8140_apcie_port_stop(struct t8140_sleep *s) +static void sleep_apcie_port_stop(struct apple_sleep *s) { void __iomem *port = s->apcie_port; void __iomem *phy = s->apcie_port_phy; @@ -1878,7 +1911,7 @@ static void t8140_apcie_port_stop(struct t8140_sleep *s) 10, APCIE_PORT_STOP_US); } /* The radio keeps its power and firmware, in reset until its host releases it. */ - if (s->radio_perst >= 0 && t8140_ps_on(s, PS_GPIO)) { + if (s->radio_perst >= 0 && sleep_ps_on(s, s->soc->ps.gpio)) { void __iomem *reg = s->pins + PIN_REG(s->radio_perst); val = readl(reg); @@ -1910,18 +1943,18 @@ static void t8140_apcie_port_stop(struct t8140_sleep *s) writel(readl(port + APCIE_PORT_INTSTAT), port + APCIE_PORT_INTSTAT); } -static void t8140_apcie_port_stop_once(struct t8140_sleep *s) +static void sleep_apcie_port_stop_once(struct apple_sleep *s) { /* Only with the port set up, by the loader or by its host after a wake, or it aborts. */ if (s->apcie_port_stopped || s->apcie_port_down || !s->apcie_port || !s->apcie_port_phy || - !t8140_ps_on(s, PS_APCIE_GP) || !t8140_ps_on(s, PS_APCIE_PHY_SW)) + !sleep_ps_on(s, s->soc->ps.apcie_gp) || !sleep_ps_on(s, s->soc->ps.apcie_phy_sw)) return; s->apcie_port_stopped = true; - t8140_apcie_port_stop(s); + sleep_apcie_port_stop(s); } /* A poll here only paces the steps: the power goes next whatever it reads. */ -static void t8140_apcie_phy_off(struct t8140_sleep *s) +static void sleep_apcie_phy_off(struct apple_sleep *s) { void __iomem *ctrl = s->apcie_phy + APCIE_PHY_CTRL; void __iomem *run = s->apcie_phy + APCIE_PHY_RUN; @@ -1937,7 +1970,7 @@ static void t8140_apcie_phy_off(struct t8140_sleep *s) writel(readl(run) & ~APCIE_PHY_RUN_EN, run); } -static void t8140_apcie_clk_off(struct t8140_sleep *s) +static void sleep_apcie_clk_off(struct apple_sleep *s) { void __iomem *clk = s->apcie_clkgen + APCIE_CLK_CTRL; u32 val; @@ -1953,39 +1986,40 @@ static void t8140_apcie_clk_off(struct t8140_sleep *s) * order, measured on one sleep, in the same microseconds as the power words. A block's * registers fault with its domain off, and a loader may have left any of these off. */ -static void t8140_sleep_lower_apcie(struct t8140_sleep *s) +static void apple_sleep_lower_apcie(struct apple_sleep *s) { + const struct apple_sleep_domains *d = &s->soc->ps; u32 val; - t8140_apcie_port_stop_once(s); + sleep_apcie_port_stop_once(s); /* The PHY's registers need both domains on. */ - if (s->apcie_phy && t8140_ps_on(s, PS_APCIE_PHY_SW) && t8140_ps_on(s, PS_APCIE_SYS_ST)) - t8140_apcie_phy_off(s); - t8140_ps_lower(s, PS_APCIE_PHY_SW); + if (s->apcie_phy && sleep_ps_on(s, d->apcie_phy_sw) && sleep_ps_on(s, d->apcie_sys_st)) + sleep_apcie_phy_off(s); + sleep_ps_lower(s, d->apcie_phy_sw); - if (s->apcie_clkgen && t8140_ps_on(s, PS_APCIE_SYS_ST)) - t8140_apcie_clk_off(s); - t8140_ps_lower(s, PS_APCIE_SYS_ST); - t8140_ps_lower(s, PS_APCIE_ST); + if (s->apcie_clkgen && sleep_ps_on(s, d->apcie_sys_st)) + sleep_apcie_clk_off(s); + sleep_ps_lower(s, d->apcie_sys_st); + sleep_ps_lower(s, d->apcie_st); /* * The AXI enables are the block's link into the fabric. macOS reads the domain on * before clearing them: powered off with them set, SoC NI 1 latches a PCIe receive * link error once the block's last domain goes off. */ - val = t8140_ps_lower_start(s, PS_APCIE_SYS_GP); - if (s->apcie_axi && t8140_ps_wait_on(s, PS_APCIE_SYS_GP, val)) { + val = sleep_ps_lower_start(s, d->apcie_sys_gp); + if (s->apcie_axi && sleep_ps_wait_on(s, d->apcie_sys_gp, val)) { writel(readl(s->apcie_axi + APCIE_AXI_EN0) & ~BIT(0), s->apcie_axi + APCIE_AXI_EN0); writel(readl(s->apcie_axi + APCIE_AXI_EN1) & ~BIT(0), s->apcie_axi + APCIE_AXI_EN1); } - t8140_ps_lower_finish(s, PS_APCIE_SYS_GP, val); + sleep_ps_lower_finish(s, d->apcie_sys_gp, val); - t8140_ps_lower(s, PS_APCIE_GP); - t8140_ps_lower(s, PS_ANS); + sleep_ps_lower(s, d->apcie_gp); + sleep_ps_lower(s, d->ans); } /* Read-modify-write in table order, with no write where nothing changes. */ -static void t8140_tunable_apply_changed(void __iomem *regs, const struct apple_tunable *t) +static void sleep_tunable_apply_changed(void __iomem *regs, const struct apple_tunable *t) { size_t i; @@ -2004,12 +2038,12 @@ static void t8140_tunable_apply_changed(void __iomem *regs, const struct apple_t * train without them. Applied as macOS does: the PLL table, then the lanes, then the * PLL's hold released. That takes about the settling time the PHY needs here. */ -static void t8140_apcie_phy_ip_apply(struct t8140_sleep *s) +static void sleep_apcie_phy_ip_apply(struct apple_sleep *s) { void __iomem *ctl = s->apcie_phy_ip + APCIE_PHY_IP_PLL_CTL; - t8140_tunable_apply_changed(s->apcie_phy_ip, s->apcie_phy_ip_pll); - t8140_tunable_apply_changed(s->apcie_phy_ip, s->apcie_phy_ip_lanes); + sleep_tunable_apply_changed(s->apcie_phy_ip, s->apcie_phy_ip_pll); + sleep_tunable_apply_changed(s->apcie_phy_ip, s->apcie_phy_ip_lanes); writel(readl(ctl) & ~APCIE_PHY_IP_PLL_HOLD, ctl); } @@ -2018,23 +2052,24 @@ static void t8140_apcie_phy_ip_apply(struct t8140_sleep *s) * the domains on the waking core, well before ANS runs. Whether ANS needs each step is * inferred. Nothing else does this if no host driver is bound. */ -static int t8140_sleep_raise_apcie(struct t8140_sleep *s) +static int apple_sleep_raise_apcie(struct apple_sleep *s) { + const struct apple_sleep_domains *d = &s->soc->ps; void __iomem *ctrl = s->apcie_phy + APCIE_PHY_CTRL; void __iomem *run = s->apcie_phy + APCIE_PHY_RUN; u32 val; int ret; - ret = t8140_ps_raise(s, PS_ANS); - ret |= t8140_ps_raise(s, PS_APCIE_GP); - ret |= t8140_ps_raise_start(s, PS_APCIE_SYS_GP); + ret = sleep_ps_raise(s, d->ans); + ret |= sleep_ps_raise(s, d->apcie_gp); + ret |= sleep_ps_raise_start(s, d->apcie_sys_gp); if (!ret) { writel(readl(s->apcie_axi + APCIE_AXI_EN0) | BIT(0), s->apcie_axi + APCIE_AXI_EN0); writel(readl(s->apcie_axi + APCIE_AXI_EN1) | BIT(0), s->apcie_axi + APCIE_AXI_EN1); } - t8140_ps_raise_finish(s, PS_APCIE_SYS_GP); - ret |= t8140_ps_raise(s, PS_APCIE_ST); - ret |= t8140_ps_raise(s, PS_APCIE_SYS_ST); + sleep_ps_raise_finish(s, d->apcie_sys_gp); + ret |= sleep_ps_raise(s, d->apcie_st); + ret |= sleep_ps_raise(s, d->apcie_sys_st); if (ret) return ret; @@ -2051,7 +2086,7 @@ static int t8140_sleep_raise_apcie(struct t8140_sleep *s) if (ret) return ret; - ret = t8140_ps_raise(s, PS_APCIE_PHY_SW); + ret = sleep_ps_raise(s, d->apcie_phy_sw); if (ret) return ret; @@ -2067,7 +2102,7 @@ static int t8140_sleep_raise_apcie(struct t8140_sleep *s) writel(readl(ctrl) & ~APCIE_PHY_CTRL_RESET, ctrl); writel(readl(run) | APCIE_PHY_RUN_EN, run); - t8140_apcie_phy_ip_apply(s); + sleep_apcie_phy_ip_apply(s); writel(readl(run) | APCIE_PHY_RUN_EN2, run); ret = readl_poll_timeout_atomic(s->apcie_phy + APCIE_PHY_READY, val, val & BIT(0), 1, APCIE_POLL_US); @@ -2085,26 +2120,28 @@ static int t8140_sleep_raise_apcie(struct t8140_sleep *s) } /* About 4 ms after the other cores stop on macOS: deepest first, ANS last. */ -static void t8140_sleep_lower_io(struct t8140_sleep *s) +static void apple_sleep_lower_io(struct apple_sleep *s) { + const struct apple_sleep_domains *d = &s->soc->ps; + /* - * USB's complex, deepest first. macOS's host lowers ATC0_USB and ATC0_COMMON in its own - * suspend, so here they're only lowered for a host that didn't. A host that sleeps in - * place keeps ATC0_USB_AON, as macOS's does. + * USB's complex, deepest first. macOS's host lowers the controller's and the common + * domain in its own suspend, so here they're only lowered for a host that didn't. A host + * that sleeps in place keeps the always-on one, as macOS's does. */ - if (FIELD_GET(PS_TARGET, readl(s->ps + PS_ATC0_USB))) - t8140_ps_lower(s, PS_ATC0_USB); - if (FIELD_GET(PS_TARGET, readl(s->ps + PS_ATC0_COMMON))) - t8140_ps_lower(s, PS_ATC0_COMMON); - if (!READ_ONCE(t8140_usb_asleep)) - t8140_ps_lower(s, PS_ATC0_USB_AON); + if (FIELD_GET(PS_TARGET, readl(s->ps + d->usb))) + sleep_ps_lower(s, d->usb); + if (FIELD_GET(PS_TARGET, readl(s->ps + d->usb_common))) + sleep_ps_lower(s, d->usb_common); + if (!READ_ONCE(sleep_usb_asleep)) + sleep_ps_lower(s, d->usb_aon); if (s->pcie_ans_stopped) { - t8140_sleep_lower_apcie(s); - s->ans_off_ret = t8140_ps_wait_off(s, PS_ANS); + apple_sleep_lower_apcie(s); + s->ans_off_ret = sleep_ps_wait_off(s, d->ans); } } -static void t8140_sleep_set_wd2(struct t8140_sleep *s, unsigned int secs) +static void apple_sleep_set_wd2(struct apple_sleep *s, unsigned int secs) { writel(0, s->wdt + WDT_WD2_CTL); writel(0, s->wdt + WDT_WD2_COUNT); @@ -2112,74 +2149,22 @@ static void t8140_sleep_set_wd2(struct t8140_sleep *s, unsigned int secs) writel(1, s->wdt + WDT_WD2_CTL); } -/* - * Every power word in the ADT, read just before ANS's stop so a run shows the whole chip. - */ -static const u32 t8140_ps_all[] = { - 0x0, 0x8, 0x10, 0x18, 0x30, 0x38, 0x50, 0x58, 0x60, 0x100, 0x108, 0x110, 0x118, - 0x120, 0x128, 0x130, 0x138, 0x140, 0x148, 0x150, 0x158, 0x160, 0x168, 0x170, 0x178, - 0x180, 0x188, 0x190, 0x198, 0x1a0, 0x1a8, 0x1b0, 0x1b8, 0x1c0, 0x1c8, 0x1d0, 0x1d8, - 0x1e0, 0x1e8, 0x1f0, 0x1f8, 0x200, 0x208, 0x210, 0x218, 0x220, 0x228, 0x230, 0x238, - 0x240, 0x248, 0x250, 0x258, 0x260, 0x268, 0x270, 0x278, 0x280, 0x288, 0x290, 0x298, - 0x2a0, 0x2a8, 0x2b0, 0x2b8, 0x2c0, 0x2c8, 0x2d0, 0x2d8, 0x2e0, 0x2e8, 0x2f0, 0x2f8, - 0x300, 0x308, 0x310, 0x318, 0x320, 0x328, 0x330, 0x338, 0x340, 0x348, 0x350, 0x358, - 0x360, 0x368, 0x370, 0x378, 0x380, 0x388, 0x390, 0x398, 0x3a0, 0x3a8, 0x3b0, 0x3b8, - 0x3c0, 0x3c8, 0x3d0, 0x3d8, 0x3e0, 0x3e8, 0x3f0, 0xc00, 0x4000, 0x4008, 0x4010, - 0x4018, 0x4020, 0x4028, 0x4030, 0x4038, 0x4040, 0x4048, 0x4050, 0x4058, 0x4060, - 0x8000, 0x8008, 0x8010, 0x8018, 0x8020, 0x8028, 0x8030, 0x8038, 0x8040, 0x8048, - 0xc000, 0xc008, 0xc010, 0x10000, 0x10008, 0x10010, 0x10018, 0x10020, 0x10028, - 0x10030, 0x10038, 0x10040, -}; - -static const u32 t8140_ps_aon_all[] = { - 0x0, 0x58, 0x60, 0x68, 0x70, 0x78, 0x80, 0x88, 0x90, 0x98, 0xa0, 0xa8, 0xb0, - 0x4000, 0x4008, 0x4010, 0x4018, 0x4020, 0x4028, 0x4030, 0x4038, 0x4040, 0x4048, - 0x4050, 0x4058, 0x4060, 0x4068, 0x4070, 0x4078, 0x4080, 0x4088, 0x4090, 0x4098, - 0x40a0, 0x40a8, 0x40b0, 0x40b8, 0x40c0, 0x40c8, 0x40d0, 0x40d8, 0x40e0, 0x40e8, - 0x40f0, 0x40f8, 0x4100, 0x4108, 0x4110, 0x4118, 0x4120, 0x4128, 0x4130, 0x4138, - 0x4140, 0x4148, 0x4150, 0x4158, 0x4160, 0x4168, 0x4170, 0x4178, 0x4180, 0x8000, - 0x8008, 0x8010, 0x8018, 0x8020, 0x8028, 0x8030, 0x8038, 0x8040, 0x8048, 0x8050, - 0x8058, 0x8060, 0x8068, 0x8070, 0x8078, -}; - -/* What macOS has on at the commit and Linux leaves off, in its order: nothing uses them here. */ -static const u32 t8140_ps_macos_on[] = { - 0x2f0, 0x2f8, 0x358, 0x360, 0x368, 0x370, 0x378, 0x380, 0x388, 0x118, -}; - -/* - * The power words macOS's wake writes back on before any driver runs, in its order - * (logs/wakerec-20261105-154137Z). The engine leaves domains like GPIO dark, and genpd still - * has them on, so nothing else would. - */ -static const u32 t8140_ps_wake_order[] = { - 0xc00, 0x110, 0x118, 0x120, 0x168, 0x178, 0x2a8, 0x2f0, 0x2f8, 0x300, 0x358, 0x360, - 0x368, 0x370, 0x378, 0x380, 0x388, 0x3d0, 0x3d8, -}; - -static const u32 t8140_ps_aon_wake_order[] = { 0x78, 0x88, 0x8030, 0x8038 }; - -/* Each word at the commit, and as the wake found it. */ -static u32 t8140_ps_commit[ARRAY_SIZE(t8140_ps_all)]; -static u32 t8140_ps_aon_commit[ARRAY_SIZE(t8140_ps_aon_all)]; -static u32 t8140_ps_woke[ARRAY_SIZE(t8140_ps_all)]; -static u32 t8140_ps_aon_woke[ARRAY_SIZE(t8140_ps_aon_all)]; - #define PS_PARENT_MISSING BIT(11) #define PS_AUTO_STATE GENMASK(27, 24) #define PS_REWAKE_PASSES 2 -static void t8140_ps_save(struct t8140_sleep *s) +static void sleep_ps_save(struct apple_sleep *s) { + const struct apple_sleep_words *all = &s->soc->ps_all, *aon = &s->soc->ps_aon_all; int i; - for (i = 0; i < ARRAY_SIZE(t8140_ps_all); i++) - t8140_ps_commit[i] = readl(s->ps + t8140_ps_all[i]); - for (i = 0; i < ARRAY_SIZE(t8140_ps_aon_all); i++) - t8140_ps_aon_commit[i] = readl(s->ps_aon + t8140_ps_aon_all[i]); + for (i = 0; i < all->count; i++) + s->ps_commit[i] = readl(s->ps + all->offset[i]); + for (i = 0; i < aon->count; i++) + s->ps_aon_commit[i] = readl(s->ps_aon + aon->offset[i]); } -static int t8140_ps_find(const u32 *all, int n, u32 offset) +static int sleep_ps_find(const u32 *all, int n, u32 offset) { int i; @@ -2194,7 +2179,7 @@ static int t8140_ps_find(const u32 *all, int n, u32 offset) * DEV_DISABLE, which keeps it off: the SEP's is set at the commit, and macOS's wake writes * that word without it. The hardware owns the auto state field, so it stays as read. */ -static int t8140_ps_restore(void __iomem *reg, u32 saved) +static int sleep_ps_restore(void __iomem *reg, u32 saved) { u32 keep = (saved & ~(PS_AUTO_ENABLE | PS_AUTO_STATE | PS_PARENT_MISSING | PS_DEV_DISABLE | PS_FLAGS | PS_ACTUAL)) | (readl(reg) & PS_AUTO_STATE); @@ -2221,15 +2206,16 @@ static int t8140_ps_restore(void __iomem *reg, u32 saved) } /* Whether the domain at @offset was on as the wake found it. */ -static bool t8140_ps_woke_on(u32 offset) +static bool sleep_ps_woke_on(struct apple_sleep *s, u32 offset) { - int i = t8140_ps_find(t8140_ps_all, ARRAY_SIZE(t8140_ps_all), offset); + const struct apple_sleep_words *all = &s->soc->ps_all; + int i = sleep_ps_find(all->offset, all->count, offset); - return i >= 0 && FIELD_GET(PS_ACTUAL, t8140_ps_woke[i]); + return i >= 0 && FIELD_GET(PS_ACTUAL, s->ps_woke[i]); } /* Linux had it on and the wake left it otherwise. With AUTO_ENABLE, off is allowed. */ -static bool t8140_ps_dropped(u32 saved, u32 woke) +static bool sleep_ps_dropped(u32 saved, u32 woke) { u32 control = PS_AUTO_ENABLE | PS_DEV_DISABLE | PS_TARGET; @@ -2241,119 +2227,101 @@ static bool t8140_ps_dropped(u32 saved, u32 woke) FIELD_GET(PS_ACTUAL, woke) != FIELD_GET(PS_TARGET, saved); } -/* Words that didn't read on in time, for the report. */ -static bool t8140_ps_late[ARRAY_SIZE(t8140_ps_all)]; -static bool t8140_ps_aon_late[ARRAY_SIZE(t8140_ps_aon_all)]; - /* * Every domain back as it was at the commit, before any driver's resume reads its block: * macOS's wake words first, in its order, whatever they read, if Linux had them on. Then * any other domain the wake left otherwise, a second time for one whose parent came later. */ -static void t8140_ps_rewake(struct t8140_sleep *s) +static void sleep_ps_rewake(struct apple_sleep *s) { - bool *redo = t8140_ps_late, *redo_aon = t8140_ps_aon_late; - bool done[ARRAY_SIZE(t8140_ps_all)] = {}, done_aon[ARRAY_SIZE(t8140_ps_aon_all)] = {}; + const struct apple_sleep_soc *soc = s->soc; + const u32 *all = soc->ps_all.offset, *aon = soc->ps_aon_all.offset; + unsigned int n = soc->ps_all.count, naon = soc->ps_aon_all.count; + bool *redo = s->ps_late, *redo_aon = s->ps_aon_late; + bool *done = s->ps_done, *done_aon = s->ps_aon_done; int i, j, pass; - memset(t8140_ps_late, 0, sizeof(t8140_ps_late)); - memset(t8140_ps_aon_late, 0, sizeof(t8140_ps_aon_late)); + memset(done, 0, n * sizeof(*done)); + memset(done_aon, 0, naon * sizeof(*done_aon)); + memset(s->ps_late, 0, n * sizeof(*s->ps_late)); + memset(s->ps_aon_late, 0, naon * sizeof(*s->ps_aon_late)); s->ps_rewoken = 0; s->ps_rewake_timeouts = 0; - for (i = 0; i < ARRAY_SIZE(t8140_ps_all); i++) - t8140_ps_woke[i] = readl(s->ps + t8140_ps_all[i]); - for (i = 0; i < ARRAY_SIZE(t8140_ps_aon_all); i++) - t8140_ps_aon_woke[i] = readl(s->ps_aon + t8140_ps_aon_all[i]); - - for (j = 0; j < ARRAY_SIZE(t8140_ps_wake_order); j++) { - i = t8140_ps_find(t8140_ps_all, ARRAY_SIZE(t8140_ps_all), t8140_ps_wake_order[j]); - if (i < 0 || !FIELD_GET(PS_TARGET, t8140_ps_commit[i])) + for (i = 0; i < n; i++) + s->ps_woke[i] = readl(s->ps + all[i]); + for (i = 0; i < naon; i++) + s->ps_aon_woke[i] = readl(s->ps_aon + aon[i]); + + for (j = 0; j < soc->ps_wake_order.count; j++) { + i = sleep_ps_find(all, n, soc->ps_wake_order.offset[j]); + if (i < 0 || !FIELD_GET(PS_TARGET, s->ps_commit[i])) continue; done[i] = true; s->ps_rewoken++; - if (t8140_ps_restore(s->ps + t8140_ps_all[i], t8140_ps_commit[i])) + if (sleep_ps_restore(s->ps + all[i], s->ps_commit[i])) redo[i] = true; } - for (j = 0; j < ARRAY_SIZE(t8140_ps_aon_wake_order); j++) { - i = t8140_ps_find(t8140_ps_aon_all, ARRAY_SIZE(t8140_ps_aon_all), - t8140_ps_aon_wake_order[j]); - if (i < 0 || !FIELD_GET(PS_TARGET, t8140_ps_aon_commit[i])) + for (j = 0; j < soc->ps_aon_wake_order.count; j++) { + i = sleep_ps_find(aon, naon, soc->ps_aon_wake_order.offset[j]); + if (i < 0 || !FIELD_GET(PS_TARGET, s->ps_aon_commit[i])) continue; done_aon[i] = true; s->ps_rewoken++; - if (t8140_ps_restore(s->ps_aon + t8140_ps_aon_all[i], t8140_ps_aon_commit[i])) + if (sleep_ps_restore(s->ps_aon + aon[i], s->ps_aon_commit[i])) redo_aon[i] = true; } for (pass = 0; pass < PS_REWAKE_PASSES; pass++) { - for (i = 0; i < ARRAY_SIZE(t8140_ps_all); i++) { + for (i = 0; i < n; i++) { if (!redo[i] && - (done[i] || !t8140_ps_dropped(t8140_ps_commit[i], t8140_ps_woke[i]))) + (done[i] || !sleep_ps_dropped(s->ps_commit[i], s->ps_woke[i]))) continue; if (!done[i]) { done[i] = true; s->ps_rewoken++; } - redo[i] = t8140_ps_restore(s->ps + t8140_ps_all[i], t8140_ps_commit[i]); + redo[i] = sleep_ps_restore(s->ps + all[i], s->ps_commit[i]); } - for (i = 0; i < ARRAY_SIZE(t8140_ps_aon_all); i++) { + for (i = 0; i < naon; i++) { if (!redo_aon[i] && (done_aon[i] || - !t8140_ps_dropped(t8140_ps_aon_commit[i], t8140_ps_aon_woke[i]))) + !sleep_ps_dropped(s->ps_aon_commit[i], s->ps_aon_woke[i]))) continue; if (!done_aon[i]) { done_aon[i] = true; s->ps_rewoken++; } - redo_aon[i] = t8140_ps_restore(s->ps_aon + t8140_ps_aon_all[i], - t8140_ps_aon_commit[i]); + redo_aon[i] = sleep_ps_restore(s->ps_aon + aon[i], s->ps_aon_commit[i]); } } - for (i = 0; i < ARRAY_SIZE(t8140_ps_all); i++) + for (i = 0; i < n; i++) s->ps_rewake_timeouts += redo[i]; - for (i = 0; i < ARRAY_SIZE(t8140_ps_aon_all); i++) + for (i = 0; i < naon; i++) s->ps_rewake_timeouts += redo_aon[i]; } -static void t8140_ps_rewake_report(struct t8140_sleep *s) +static void sleep_ps_rewake_report(struct apple_sleep *s) { + const struct apple_sleep_words *all = &s->soc->ps_all, *aon = &s->soc->ps_aon_all; int i; dev_dbg(s->dev, "power words: %u written back, %u not on in time\n", s->ps_rewoken, s->ps_rewake_timeouts); - for (i = 0; i < ARRAY_SIZE(t8140_ps_all); i++) - if (t8140_ps_late[i]) + for (i = 0; i < all->count; i++) + if (s->ps_late[i]) dev_err(s->dev, "power word %#x not on in time: %#x at the commit, %#x now\n", - t8140_ps_all[i], t8140_ps_commit[i], - readl(s->ps + t8140_ps_all[i])); - for (i = 0; i < ARRAY_SIZE(t8140_ps_aon_all); i++) - if (t8140_ps_aon_late[i]) + all->offset[i], s->ps_commit[i], + readl(s->ps + all->offset[i])); + for (i = 0; i < aon->count; i++) + if (s->ps_aon_late[i]) dev_err(s->dev, "power word aon %#x not on in time: %#x at the commit, %#x now\n", - t8140_ps_aon_all[i], t8140_ps_aon_commit[i], - readl(s->ps_aon + t8140_ps_aon_all[i])); + aon->offset[i], s->ps_aon_commit[i], + readl(s->ps_aon + aon->offset[i])); } -/* What it has off, highest first, as it lowers them. Domains with their own stop aren't here. */ -static const u32 t8140_ps_macos_off[] = { - 0x4060, 0x4058, 0x4050, 0x4048, 0x4040, 0x4038, 0x4030, 0x4028, 0x4020, 0x4018, - 0x4010, 0x4008, 0x4000, 0x398, 0x390, 0x350, 0x348, 0x340, 0x338, 0x330, - 0x308, 0x2e8, 0x2e0, 0x2d8, 0x2d0, 0x2c8, 0x2c0, 0x2b8, 0x2b0, 0x298, - 0x290, 0x270, 0x268, 0x250, 0x248, 0x240, 0x238, 0x230, 0x1a0, 0x190, - 0x188, 0x180, 0x170, 0x160, 0x158, 0x150, 0x148, 0x140, 0x108, -}; - -static const u32 t8140_ps_aon_macos_off[] = { - 0x8028, 0x4140, 0x40f8, 0x80, -}; - -/* The AOP's SPMI words: on, with AUTO_ENABLE off on macOS. */ -static const u32 t8140_ps_aon_macos_no_auto[] = { - 0x4078, 0x4080, -}; - /* As macOS writes the PMU: the reply's vector masked, then acknowledged, so none is left raised. */ -static void t8140_pmu_write(struct t8140_sleep *s, u16 reg, u8 val) +static void sleep_pmu_write(struct apple_sleep *s, u16 reg, u8 val) { u32 status, enable; @@ -2368,42 +2336,43 @@ static void t8140_pmu_write(struct t8140_sleep *s, u16 reg, u8 val) writel(enable, s->spmi + SPMI_NOTIFY_ENABLE); } -static void t8140_sleep_match(struct t8140_sleep *s) +static void apple_sleep_match(struct apple_sleep *s) { + const struct apple_sleep_soc *soc = s->soc; int i, raised = 0, lowered = 0, failed = 0; writel(0, s->wdt + WDT_WD0_COUNT); writel(0, s->wdt + WDT_WD1_COUNT); - t8140_pmu_write(s, PMU_BOOT_STAGE, PMU_BOOT_STAGE_SLEEP); + sleep_pmu_write(s, PMU_BOOT_STAGE, PMU_BOOT_STAGE_SLEEP); s->spmi_saved[0] = readl(s->spmi + SPMI_NOTIFY_ENABLE); s->spmi_saved[1] = readl(s->spmi_a0); writel(SPMI_NOTIFY_ENABLE_SLEEP, s->spmi + SPMI_NOTIFY_ENABLE); writel(SPMI_NOTIFY_ENABLE_SLEEP, s->spmi_a0); - for (i = 0; i < ARRAY_SIZE(t8140_ps_macos_on); i++) { - u32 offset = t8140_ps_macos_on[i]; + for (i = 0; i < soc->ps_macos_on.count; i++) { + u32 offset = soc->ps_macos_on.offset[i]; s->ps_on_saved[i] = readl(s->ps + offset); /* State 4 reads as on, and macOS has these at f. */ if (FIELD_GET(PS_ACTUAL, s->ps_on_saved[i]) == 0xf) continue; /* Measured at the commit with AUTO_ENABLE clear, unlike after a wake. */ - if (t8140_ps_raise_start(s, offset)) + if (sleep_ps_raise_start(s, offset)) failed++; raised++; } - for (i = 0; i < ARRAY_SIZE(t8140_ps_macos_off); i++) { - u32 offset = t8140_ps_macos_off[i]; + for (i = 0; i < soc->ps_macos_off.count; i++) { + u32 offset = soc->ps_macos_off.offset[i]; s->ps_off_saved[i] = readl(s->ps + offset); - if (!t8140_ps_on(s, offset)) + if (!sleep_ps_on(s, offset)) continue; - t8140_ps_lower(s, offset); + sleep_ps_lower(s, offset); lowered++; } - for (i = 0; i < ARRAY_SIZE(t8140_ps_aon_macos_off); i++) { - void __iomem *reg = s->ps_aon + t8140_ps_aon_macos_off[i]; + for (i = 0; i < soc->ps_aon_macos_off.count; i++) { + void __iomem *reg = s->ps_aon + soc->ps_aon_macos_off.offset[i]; u32 val = readl(reg); s->ps_aon_saved[i] = val; @@ -2416,8 +2385,8 @@ static void t8140_sleep_match(struct t8140_sleep *s) writel((val & ~PS_TARGET) | PS_FLAGS, reg); lowered++; } - for (i = 0; i < ARRAY_SIZE(t8140_ps_aon_macos_no_auto); i++) { - void __iomem *reg = s->ps_aon + t8140_ps_aon_macos_no_auto[i]; + for (i = 0; i < soc->ps_aon_macos_no_auto.count; i++) { + void __iomem *reg = s->ps_aon + soc->ps_aon_macos_no_auto.offset[i]; u32 val = readl(reg); s->ps_aon_auto_saved[i] = val; @@ -2425,54 +2394,58 @@ static void t8140_sleep_match(struct t8140_sleep *s) } s->matched = true; - for (i = 0; i < ARRAY_SIZE(t8140_ps_macos_off); i++) - if (t8140_ps_wait_off(s, t8140_ps_macos_off[i])) - dev_dbg(s->dev, "still on, macOS has it off: %x = %x\n", - t8140_ps_macos_off[i], readl(s->ps + t8140_ps_macos_off[i])); + for (i = 0; i < soc->ps_macos_off.count; i++) { + u32 offset = soc->ps_macos_off.offset[i]; + + if (sleep_ps_wait_off(s, offset)) + dev_dbg(s->dev, "still on, macOS has it off: %x = %x\n", offset, + readl(s->ps + offset)); + } dev_dbg(s->dev, "macOS's power words: raised %d, %d of them failed, lowered %d\n", raised, failed, lowered); } /* Back as they were, after a refusal past the match or a real wake. */ -static void t8140_sleep_match_undo(struct t8140_sleep *s) +static void apple_sleep_match_undo(struct apple_sleep *s) { + const struct apple_sleep_soc *soc = s->soc; int i; if (!s->matched) return; s->matched = false; - for (i = ARRAY_SIZE(t8140_ps_aon_macos_no_auto) - 1; i >= 0; i--) + for (i = soc->ps_aon_macos_no_auto.count - 1; i >= 0; i--) writel(s->ps_aon_auto_saved[i] & ~PS_FLAGS, - s->ps_aon + t8140_ps_aon_macos_no_auto[i]); - for (i = ARRAY_SIZE(t8140_ps_aon_macos_off) - 1; i >= 0; i--) + s->ps_aon + soc->ps_aon_macos_no_auto.offset[i]); + for (i = soc->ps_aon_macos_off.count - 1; i >= 0; i--) if (FIELD_GET(PS_ACTUAL, s->ps_aon_saved[i])) writel(s->ps_aon_saved[i] & ~PS_FLAGS, - s->ps_aon + t8140_ps_aon_macos_off[i]); - for (i = ARRAY_SIZE(t8140_ps_macos_off) - 1; i >= 0; i--) + s->ps_aon + soc->ps_aon_macos_off.offset[i]); + for (i = soc->ps_macos_off.count - 1; i >= 0; i--) if (FIELD_GET(PS_ACTUAL, s->ps_off_saved[i])) - t8140_ps_raise(s, t8140_ps_macos_off[i]); - for (i = ARRAY_SIZE(t8140_ps_macos_on) - 1; i >= 0; i--) { + sleep_ps_raise(s, soc->ps_macos_off.offset[i]); + for (i = soc->ps_macos_on.count - 1; i >= 0; i--) { u32 val = s->ps_on_saved[i]; if (!FIELD_GET(PS_ACTUAL, val)) - t8140_ps_lower(s, t8140_ps_macos_on[i]); + sleep_ps_lower(s, soc->ps_macos_on.offset[i]); else if (FIELD_GET(PS_ACTUAL, val) != 0xf) - writel(val & ~PS_FLAGS, s->ps + t8140_ps_macos_on[i]); + writel(val & ~PS_FLAGS, s->ps + soc->ps_macos_on.offset[i]); } writel(s->spmi_saved[0], s->spmi + SPMI_NOTIFY_ENABLE); writel(s->spmi_saved[1], s->spmi_a0); } /* The safety net, as macOS arms it. */ -static void t8140_sleep_arm_wd2(struct t8140_sleep *s) +static void apple_sleep_arm_wd2(struct apple_sleep *s) { writel(0, s->wdt + WDT_WD0_CTL); - t8140_sleep_set_wd2(s, WDT_SLEEP_SECS); + apple_sleep_set_wd2(s, WDT_SLEEP_SECS); writel(0, s->aon_misc + AON_AP_DEADLINE); } -static int t8140_perf_wait(void __iomem *reg, unsigned int state) +static int sleep_perf_wait(void __iomem *reg, unsigned int state) { u32 val; @@ -2482,9 +2455,9 @@ static int t8140_perf_wait(void __iomem *reg, unsigned int state) 1, DCS_TIMEOUT_US); } -static int t8140_dcs_wait(struct t8140_sleep *s, unsigned int state) +static int sleep_dcs_wait(struct apple_sleep *s, unsigned int state) { - return t8140_perf_wait(s->dcs, state); + return sleep_perf_wait(s->dcs, state); } /* @@ -2492,7 +2465,7 @@ static int t8140_dcs_wait(struct t8140_sleep *s, unsigned int state) * then each of four lanes strobed and cleared, two lanes on every channel before the * next two. Each write is built from a fresh read, as the trace has it. */ -static void t8140_sleep_mcc_stop(struct t8140_sleep *s) +static void apple_sleep_mcc_stop(struct apple_sleep *s) { int ch, pass, n; u32 val; @@ -2525,7 +2498,7 @@ static void t8140_sleep_mcc_stop(struct t8140_sleep *s) * macOS has every voter at 0 by now, which is how it lands there, and steps it down itself * on the path without the PMC. Both, here, in that order. Nothing can fail by now. */ -static void t8140_sleep_dcs_down(struct t8140_sleep *s) +static void apple_sleep_dcs_down(struct apple_sleep *s) { unsigned int state; int agent; @@ -2533,24 +2506,24 @@ static void t8140_sleep_dcs_down(struct t8140_sleep *s) for (agent = 0; agent < PMC_VOTER_AGENTS; agent++) if (readl(s->pmc_voters + agent * PMC_VOTER_STRIDE)) writel(0, s->pmc_voters + agent * PMC_VOTER_STRIDE); - if (!t8140_dcs_wait(s, DCS_SLEEP_STATE)) + if (!sleep_dcs_wait(s, DCS_SLEEP_STATE)) return; state = FIELD_GET(DCS_STATE, readl(s->dcs)); if (state > DCS_STEP_STATE) { writel(DCS_STEP_STATE, s->dcs); - t8140_dcs_wait(s, DCS_STEP_STATE); + sleep_dcs_wait(s, DCS_STEP_STATE); } if (state > DCS_SLEEP_STATE) { writel(DCS_SLEEP_STATE, s->dcs); - t8140_dcs_wait(s, DCS_SLEEP_STATE); + sleep_dcs_wait(s, DCS_SLEEP_STATE); } } /* The boot core's last WFI. An event that ends it is taken, and the WFI taken again. */ -static void __noreturn t8140_boot_wfi(struct t8140_sleep *s) +static void __noreturn sleep_boot_wfi(struct apple_sleep *s) { - t8140_deep_wfi_arm(); + sleep_deep_wfi_arm(); while (true) { dsb(sy); @@ -2562,17 +2535,13 @@ static void __noreturn t8140_boot_wfi(struct t8140_sleep *s) } /* XNU polls these off just before its complex words. The sleep goes on whatever they read. */ -static void t8140_sleep_wait_still_on(struct t8140_sleep *s) +static void apple_sleep_wait_still_on(struct apple_sleep *s) { - static const u32 words[] = { - PS_ATC0_COMMON, PS_DISP_SYS, PS_DISPEXT0_SYS, PS_ANS, 0x270 /* AVD_SYS */, - 0x240 /* VENC0_SYS */, 0x248 /* VENC1_SYS */, 0x1f0 /* GFX */, 0x230 /* JPG */, - PS_APCIE_SYS_GP, - }; + const struct apple_sleep_words *words = &s->soc->ps_still_on; int i; - for (i = 0; i < ARRAY_SIZE(words); i++) - t8140_ps_wait_off(s, words[i]); + for (i = 0; i < words->count; i++) + sleep_ps_wait_off(s, words->offset[i]); } /* @@ -2586,9 +2555,9 @@ static void t8140_sleep_wait_still_on(struct t8140_sleep *s) * - The RTC alarm: macOS arms it after the commit. rtcwake has armed it long * before this, with no SPMI traffic near the engine. */ -static int t8140_sleep_commit(unsigned long unused) +static int apple_sleep_commit(unsigned long unused) { - struct t8140_sleep *s = t8140_sleep; + struct apple_sleep *s = apple_sleep; int plane, agent; u32 val; @@ -2599,22 +2568,22 @@ static int t8140_sleep_commit(unsigned long unused) s->siq_cfg = read_sysreg_s(SYS_APL_SIQ_CFG_EL1); s->cpus_stop_ret = spin_table_stop_parked(); - t8140_sleep_lower_io(s); - t8140_sleep_arm_wd2(s); - t8140_sleep_wait_still_on(s); + apple_sleep_lower_io(s); + apple_sleep_arm_wd2(s); + apple_sleep_wait_still_on(s); /* The SoC comes down before memory does, as on macOS. */ - if (t8140_perf_wait(s->clk_perf + CLK_PERF_SOC, SOC_SLEEP_STATE)) { + if (sleep_perf_wait(s->clk_perf + CLK_PERF_SOC, SOC_SLEEP_STATE)) { writel(SOC_SLEEP_STATE, s->clk_perf + CLK_PERF_SOC); - t8140_perf_wait(s->clk_perf + CLK_PERF_SOC, SOC_SLEEP_STATE); + sleep_perf_wait(s->clk_perf + CLK_PERF_SOC, SOC_SLEEP_STATE); } - t8140_sleep_dcs_down(s); - t8140_sleep_mcc_stop(s); + apple_sleep_dcs_down(s); + apple_sleep_mcc_stop(s); /* Gates, PTD, the first voter twice, gates: macOS's order, measured once. */ for (agent = 0; agent < 2; agent++) { - writel(PWRGATE_ON, s->pwrgate + PWRGATE_CPM_0); - writel(PWRGATE_ON, s->pwrgate + PWRGATE_CPM_1); + writel(PWRGATE_ON, s->pwrgate + s->soc->pwrgate_cpm[0]); + writel(PWRGATE_ON, s->pwrgate + s->soc->pwrgate_cpm[1]); } writeq(0, s->ptd + PTD_A); writeq(0, s->ptd + PTD_B); @@ -2623,8 +2592,8 @@ static int t8140_sleep_commit(unsigned long unused) writel(0, s->pmc_voters); readl(s->pmc_voters + 4); } - writel(PWRGATE_ON, s->pwrgate + PWRGATE_CPM_0); - writel(PWRGATE_ON, s->pwrgate + PWRGATE_CPM_1); + writel(PWRGATE_ON, s->pwrgate + s->soc->pwrgate_cpm[0]); + writel(PWRGATE_ON, s->pwrgate + s->soc->pwrgate_cpm[1]); /* Already off since .prepare, as on macOS. */ writel(0, s->cluster + CLUSTER_AVE); @@ -2633,7 +2602,7 @@ static int t8140_sleep_commit(unsigned long unused) writel(0, s->cluster + CLUSTER_E); writel(0, s->cluster + CLUSTER_P); - writel(readl(s->ps + PS_SIO_CPU) & ~PS_AUTO_ENABLE, s->ps + PS_SIO_CPU); + writel(readl(s->ps + s->soc->ps.sio_cpu) & ~PS_AUTO_ENABLE, s->ps + s->soc->ps.sio_cpu); /* The SoC refuses the first, on macOS too. */ writel(CLK_MUX_PARKED, s->clk_mux + CLK_MUX_38); @@ -2648,9 +2617,9 @@ static int t8140_sleep_commit(unsigned long unused) * Two runs put these writes after the CLLT's: 5 to 6 ms before the commit * in one, and the CLLT at 5.75 ms in the other. No run has hooked both. */ - t8140_dvfm_park(s); + sleep_dvfm_park(s); - t8140_ps_save(s); + sleep_ps_save(s); for (plane = 0; plane < ARRAY_SIZE(s->amcc); plane++) { void __iomem *amcc = s->amcc[plane]; @@ -2673,11 +2642,11 @@ static int t8140_sleep_commit(unsigned long unused) /* Each core's deep WFI comes after the commit, this one last. No SPMI from here on. */ spin_table_release_stopped(); - t8140_boot_wfi(s); + sleep_boot_wfi(s); } /* Back on the boot core through the reset vector, MMU on, in macOS's order. */ -static void t8140_sleep_return(struct t8140_sleep *s) +static void apple_sleep_return(struct apple_sleep *s) { u32 val; int i; @@ -2701,36 +2670,36 @@ static void t8140_sleep_return(struct t8140_sleep *s) CLUSTER_TIMEOUT_US); writel(CLUSTER_ON, s->cluster + CLUSTER_P); - writel(PWRGATE_ON, s->pwrgate + PWRGATE_WAKE_0); - writel(PWRGATE_ON, s->pwrgate + PWRGATE_WAKE_1); + writel(PWRGATE_ON, s->pwrgate + s->soc->pwrgate_wake[0]); + writel(PWRGATE_ON, s->pwrgate + s->soc->pwrgate_wake[1]); - t8140_ps_rewake(s); + sleep_ps_rewake(s); /* Only C2S turns AP_TMM on: the deep path ran, so the held vote applies from now on. */ - if (!t8140_slp_ddr_ready && t8140_ps_woke_on(PS_AP_TMM)) { - scoped_guard(spinlock_irqsave, &t8140_slp_ddr_lock) { - t8140_slp_ddr_ready = true; - t8140_slp_ddr_write(); + if (!sleep_slp_ddr_ready && sleep_ps_woke_on(s, s->soc->ps.ap_tmm)) { + scoped_guard(spinlock_irqsave, &sleep_slp_ddr_lock) { + sleep_slp_ddr_ready = true; + sleep_slp_ddr_write(); } s->slp_ddr_now_ready = true; } - val = readl(s->ps + PS_SEP); + val = readl(s->ps + s->soc->ps.sep); if (val & PS_DEV_DISABLE) - writel(val & ~PS_DEV_DISABLE, s->ps + PS_SEP); + writel(val & ~PS_DEV_DISABLE, s->ps + s->soc->ps.sep); /* The complex comes back on, and macOS turns it off. */ writel(0, s->cluster + CLUSTER_AVE); s->cluster_ave_ret = readl_poll_timeout_atomic(s->cluster + CLUSTER_AVE, val, !val, 1, CLUSTER_TIMEOUT_US); - writel(PWRGATE_ON, s->pwrgate + PWRGATE_CPM_0); - writel(PWRGATE_ON, s->pwrgate + PWRGATE_CPM_1); + writel(PWRGATE_ON, s->pwrgate + s->soc->pwrgate_cpm[0]); + writel(PWRGATE_ON, s->pwrgate + s->soc->pwrgate_cpm[1]); - t8140_dvfm_unpark(s); + sleep_dvfm_unpark(s); /* macOS stops it here. Keep a longer one over device resume, until .end. */ - t8140_sleep_set_wd2(s, WDT_SUSPEND_SECS); + apple_sleep_set_wd2(s, WDT_SUSPEND_SECS); /* * What the commit lowered for a driver comes back before anything uses it, in @@ -2738,7 +2707,7 @@ static void t8140_sleep_return(struct t8140_sleep *s) * books decide, as they do for every other domain. */ if (s->pcie_ans_stopped) - s->pcie_ans_on_ret = t8140_sleep_raise_apcie(s); + s->pcie_ans_on_ret = apple_sleep_raise_apcie(s); s->apcie_port_down = true; s->pins_lost = s->pins_saved_valid; @@ -2750,72 +2719,72 @@ static void t8140_sleep_return(struct t8140_sleep *s) s->returned = true; } -static int t8140_sleep_enter(suspend_state_t state) +static int apple_sleep_enter(suspend_state_t state) { - struct t8140_sleep *s = t8140_sleep; + struct apple_sleep *s = apple_sleep; int ret; - ret = t8140_sleep_refuse(s); + ret = apple_sleep_refuse(s); if (ret) return ret; - ret = t8140_sleep_cpus_hold(s); + ret = apple_sleep_cpus_hold(s); /* As on macOS, the display goes off before the GPU. */ if (!ret) - ret = t8140_display_off(s); + ret = sleep_display_off(s); if (!ret) - ret = t8140_sleep_coprocs_check(s); + ret = apple_sleep_coprocs_check(s); if (!ret) - ret = t8140_sleep_enclave_tell(s); + ret = apple_sleep_enclave_tell(s); if (ret) { spin_table_unhold_parked(); - t8140_sleep_stray_restore(s); + apple_sleep_stray_restore(s); return ret; } - t8140_sleep_coprocs_stop(s, true); + apple_sleep_coprocs_stop(s, true); - t8140_wake_arm(s); - t8140_sleep_match(s); + sleep_wake_arm(s); + apple_sleep_match(s); /* Acknowledge a leftover done flag, as the DCS word has none on macOS. */ if (readl(s->cluster_dcs) & CLUSTER_UP_DONE) writel(readl(s->cluster_dcs), s->cluster_dcs); - t8140_sleep_coprocs_stop(s, false); + apple_sleep_coprocs_stop(s, false); /* Done by now unless no SMC driver is bound. */ - t8140_sleep_usb_down(s); + apple_sleep_usb_down(s); s->cntkctl = read_sysreg(cntkctl_el1); pm_set_resume_via_firmware(); - ret = cpu_suspend(0, t8140_sleep_commit); + ret = cpu_suspend(0, apple_sleep_commit); if (!ret) - t8140_sleep_return(s); + apple_sleep_return(s); - t8140_sleep_match_undo(s); - t8140_sleep_stray_restore(s); - t8140_wake_restore(s); + apple_sleep_match_undo(s); + apple_sleep_stray_restore(s); + sleep_wake_restore(s); return ret; } /* From here to .end a hang resets the machine. Nobody has to be there to recover it. */ -static int t8140_sleep_begin(suspend_state_t state) +static int apple_sleep_begin(suspend_state_t state) { /* The last sleep's cut is its port controller's to take, by its .complete. */ - WRITE_ONCE(t8140_debug_link_cut, false); - if (t8140_sleep) { - t8140_sleep->apcie_port_stopped = false; - t8140_sleep->usb_down_ran = false; - t8140_sleep->smc_woke_alone = false; - t8140_pins_save(t8140_sleep); - } - t8140_sleep_set_wd2(t8140_sleep, WDT_SUSPEND_SECS); + WRITE_ONCE(sleep_debug_link_cut, false); + if (apple_sleep) { + apple_sleep->apcie_port_stopped = false; + apple_sleep->usb_down_ran = false; + apple_sleep->smc_woke_alone = false; + sleep_pins_save(apple_sleep); + } + apple_sleep_set_wd2(apple_sleep, WDT_SUSPEND_SECS); pm_set_suspend_via_firmware(); return 0; } -static void t8140_sleep_end(void) +static void apple_sleep_end(void) { - writel(0, t8140_sleep->wdt + WDT_WD2_CTL); + writel(0, apple_sleep->wdt + WDT_WD2_CTL); } /* @@ -2823,7 +2792,7 @@ static void t8140_sleep_end(void) * its word is already 0 and the engine has no sequence to run for it. Turning it off at the * commit instead puts that sequence, with its untimed wait, right before the CPU complexes'. */ -static void t8140_sleep_ave_off(struct t8140_sleep *s) +static void apple_sleep_ave_off(struct apple_sleep *s) { u32 val; int ret; @@ -2835,14 +2804,14 @@ static void t8140_sleep_ave_off(struct t8140_sleep *s) dev_warn(s->dev, "AVE complex didn't go off: %#x\n", val); } -static int t8140_sleep_prepare(void) +static int apple_sleep_prepare(void) { - struct t8140_sleep *s = t8140_sleep; + struct apple_sleep *s = apple_sleep; int i; - t8140_sleep_ave_off(s); + apple_sleep_ave_off(s); - for (i = 0; i < T8140_DISPLAY_COUNT; i++) { + for (i = 0; i < SLEEP_DISPLAY_COUNT; i++) { struct platform_device *pdev; s->display[i].bound = false; @@ -2854,23 +2823,24 @@ static int t8140_sleep_prepare(void) put_device(&pdev->dev); } } - t8140_sleep_gate_prepare(s); + apple_sleep_gate_prepare(s); return 0; } /* Only the SMC's sources woke the SoC. */ -static bool t8140_sleep_smc_woke_alone(struct t8140_sleep *s) +static bool apple_sleep_smc_woke_alone(struct apple_sleep *s) { + const struct apple_sleep_wake_name *names = s->soc->wake_names; u32 smc[WAKE_WORDS] = {}; bool any = false; int i; /* SMC.CPUWakeupAP and SMC.OutboxNotEmpty, by their names. */ - for (i = 0; i < ARRAY_SIZE(t8140_wake_names); i++) { - u32 id = t8140_wake_names[i].id; + for (i = 0; i < s->soc->nwake_names; i++) { + u32 id = names[i].id; - if (strstarts(t8140_wake_names[i].name, "SMC.") && id / 32 < WAKE_WORDS) + if (strstarts(names[i].name, "SMC.") && id / 32 < WAKE_WORDS) smc[id / 32] |= BIT(id % 32); } for (i = 0; i < WAKE_WORDS; i++) { @@ -2884,16 +2854,17 @@ static bool t8140_sleep_smc_woke_alone(struct t8140_sleep *s) return any; } -static void t8140_sleep_report(void) +static void apple_sleep_report(void) { - struct t8140_sleep *s = t8140_sleep; + struct apple_sleep *s = apple_sleep; + const struct apple_sleep_wake_name *names = s->soc->wake_names; int i; s->smc_woke_alone = false; if (!s->returned) return; s->returned = false; - s->smc_woke_alone = t8140_sleep_smc_woke_alone(s); + s->smc_woke_alone = apple_sleep_smc_woke_alone(s); dev_info(s->dev, "back from sleep, boot object resumes %llu -> %llu\n", s->resumes_before, s->resumes_after); @@ -2901,13 +2872,13 @@ static void t8140_sleep_report(void) s->slp_ddr_now_ready = false; dev_info(s->dev, "the deep path ran: later sleeps keep DRAM up for the USB host\n"); } - for (i = 0; i < ARRAY_SIZE(t8140_wake_names); i++) { - u32 id = t8140_wake_names[i].id; + for (i = 0; i < s->soc->nwake_names; i++) { + u32 id = names[i].id; if (s->wake_status[id / 32] & BIT(id % 32)) - dev_info(s->dev, "wake source: %s\n", t8140_wake_names[i].name); + dev_info(s->dev, "wake source: %s\n", names[i].name); } - t8140_ps_rewake_report(s); + sleep_ps_rewake_report(s); if (s->cpus_stop_ret) dev_err(s->dev, "other CPUs did not stop: %d\n", s->cpus_stop_ret); @@ -2923,16 +2894,16 @@ static void t8140_sleep_report(void) s->pcie_ans_on_ret); } -static const struct platform_suspend_ops t8140_sleep_ops = { +static const struct platform_suspend_ops apple_sleep_ops = { .valid = suspend_valid_only_mem, - .begin = t8140_sleep_begin, - .prepare = t8140_sleep_prepare, - .enter = t8140_sleep_enter, - .wake = t8140_sleep_report, - .end = t8140_sleep_end, + .begin = apple_sleep_begin, + .prepare = apple_sleep_prepare, + .enter = apple_sleep_enter, + .wake = apple_sleep_report, + .end = apple_sleep_end, }; -static int t8140_sleep_read_wake_sources(struct t8140_sleep *s) +static int apple_sleep_read_wake_sources(struct apple_sleep *s) { struct device_node *np = s->dev->of_node; int count; @@ -2946,11 +2917,11 @@ static int t8140_sleep_read_wake_sources(struct t8140_sleep *s) return 0; } -static int t8140_sleep_map_displays(struct t8140_sleep *s) +static int apple_sleep_map_displays(struct apple_sleep *s) { int i; - for (i = 0; i < T8140_DISPLAY_COUNT; i++) { + for (i = 0; i < SLEEP_DISPLAY_COUNT; i++) { struct device_node *np; int index; @@ -2970,7 +2941,7 @@ static int t8140_sleep_map_displays(struct t8140_sleep *s) return 0; } -static void __iomem *t8140_sleep_map(struct device *dev, const char *name) +static void __iomem *apple_sleep_map(struct device *dev, const char *name) { struct device_node *np = dev->of_node; struct resource res; @@ -2995,54 +2966,54 @@ static void __iomem *t8140_sleep_map(struct device *dev, const char *name) static const struct { const char *name; size_t offset; -} t8140_sleep_regs[] = { - { "ps", offsetof(struct t8140_sleep, ps) }, - { "pwrgate", offsetof(struct t8140_sleep, pwrgate) }, - { "cpu-start", offsetof(struct t8140_sleep, cpu_start) }, - { "cllt", offsetof(struct t8140_sleep, cllt) }, - { "cluster", offsetof(struct t8140_sleep, cluster) }, - { "commit", offsetof(struct t8140_sleep, commit) }, - { "wdt", offsetof(struct t8140_sleep, wdt) }, - { "aon-misc", offsetof(struct t8140_sleep, aon_misc) }, - { "wake", offsetof(struct t8140_sleep, wake) }, - { "pmc-dcp", offsetof(struct t8140_sleep, pmc_dcp) }, - { "spmi", offsetof(struct t8140_sleep, spmi) }, - { "pmc-voters", offsetof(struct t8140_sleep, pmc_voters) }, - { "dcs", offsetof(struct t8140_sleep, dcs) }, - { "ps-aon", offsetof(struct t8140_sleep, ps_aon) }, - { "spmi-a0", offsetof(struct t8140_sleep, spmi_a0) }, - { "clk-mux", offsetof(struct t8140_sleep, clk_mux) }, - { "mcc0", offsetof(struct t8140_sleep, mcc[0]) }, - { "mcc1", offsetof(struct t8140_sleep, mcc[1]) }, - { "mcc2", offsetof(struct t8140_sleep, mcc[2]) }, - { "ptd", offsetof(struct t8140_sleep, ptd) }, - { "clk-perf", offsetof(struct t8140_sleep, clk_perf) }, - { "cluster-dcs", offsetof(struct t8140_sleep, cluster_dcs) }, - { "dvfm-e", offsetof(struct t8140_sleep, dvfm[0]) }, - { "dvfm-p", offsetof(struct t8140_sleep, dvfm[1]) }, - { "amcc0", offsetof(struct t8140_sleep, amcc[0]) }, - { "amcc1", offsetof(struct t8140_sleep, amcc[1]) }, - { "amcc2", offsetof(struct t8140_sleep, amcc[2]) }, - { "amcc3", offsetof(struct t8140_sleep, amcc[3]) }, +} apple_sleep_regs[] = { + { "ps", offsetof(struct apple_sleep, ps) }, + { "pwrgate", offsetof(struct apple_sleep, pwrgate) }, + { "cpu-start", offsetof(struct apple_sleep, cpu_start) }, + { "cllt", offsetof(struct apple_sleep, cllt) }, + { "cluster", offsetof(struct apple_sleep, cluster) }, + { "commit", offsetof(struct apple_sleep, commit) }, + { "wdt", offsetof(struct apple_sleep, wdt) }, + { "aon-misc", offsetof(struct apple_sleep, aon_misc) }, + { "wake", offsetof(struct apple_sleep, wake) }, + { "pmc-dcp", offsetof(struct apple_sleep, pmc_dcp) }, + { "spmi", offsetof(struct apple_sleep, spmi) }, + { "pmc-voters", offsetof(struct apple_sleep, pmc_voters) }, + { "dcs", offsetof(struct apple_sleep, dcs) }, + { "ps-aon", offsetof(struct apple_sleep, ps_aon) }, + { "spmi-a0", offsetof(struct apple_sleep, spmi_a0) }, + { "clk-mux", offsetof(struct apple_sleep, clk_mux) }, + { "mcc0", offsetof(struct apple_sleep, mcc[0]) }, + { "mcc1", offsetof(struct apple_sleep, mcc[1]) }, + { "mcc2", offsetof(struct apple_sleep, mcc[2]) }, + { "ptd", offsetof(struct apple_sleep, ptd) }, + { "clk-perf", offsetof(struct apple_sleep, clk_perf) }, + { "cluster-dcs", offsetof(struct apple_sleep, cluster_dcs) }, + { "dvfm-e", offsetof(struct apple_sleep, dvfm[0]) }, + { "dvfm-p", offsetof(struct apple_sleep, dvfm[1]) }, + { "amcc0", offsetof(struct apple_sleep, amcc[0]) }, + { "amcc1", offsetof(struct apple_sleep, amcc[1]) }, + { "amcc2", offsetof(struct apple_sleep, amcc[2]) }, + { "amcc3", offsetof(struct apple_sleep, amcc[3]) }, }; -static int t8140_sleep_map_regs(struct t8140_sleep *s) +static int apple_sleep_map_regs(struct apple_sleep *s) { int i; - for (i = 0; i < ARRAY_SIZE(t8140_sleep_regs); i++) { - void __iomem **field = (void __iomem **)((u8 *)s + t8140_sleep_regs[i].offset); + for (i = 0; i < ARRAY_SIZE(apple_sleep_regs); i++) { + void __iomem **field = (void __iomem **)((u8 *)s + apple_sleep_regs[i].offset); - *field = t8140_sleep_map(s->dev, t8140_sleep_regs[i].name); + *field = apple_sleep_map(s->dev, apple_sleep_regs[i].name); if (IS_ERR(*field)) return dev_err_probe(s->dev, PTR_ERR(*field), "no %s registers\n", - t8140_sleep_regs[i].name); + apple_sleep_regs[i].name); } return 0; } -static int t8140_sleep_map_mailbox(struct t8140_sleep *s) +static int apple_sleep_map_mailbox(struct apple_sleep *s) { struct device_node *chosen; u64 pa; @@ -3063,7 +3034,7 @@ static int t8140_sleep_map_mailbox(struct t8140_sleep *s) return PTR_ERR(s->mbox); /* aura wrote it with the MMU off, so nothing of it can be in a cache. */ - if (s->mbox->magic != T8140_S2R_MAGIC) + if (s->mbox->magic != AURA_S2R_MAGIC) return dev_err_probe(s->dev, -EINVAL, "bad mailbox magic %llx\n", s->mbox->magic); /* Probe can run on any CPU. Logical CPU 0 is always the boot CPU. */ @@ -3075,7 +3046,7 @@ static int t8140_sleep_map_mailbox(struct t8140_sleep *s) return 0; } -static struct apple_tunable *t8140_sleep_apcie_tunable(struct t8140_sleep *s, +static struct apple_tunable *apple_sleep_apcie_tunable(struct apple_sleep *s, struct device_node *np, const char *name, const char *prop) { @@ -3090,7 +3061,7 @@ static struct apple_tunable *t8140_sleep_apcie_tunable(struct t8140_sleep *s, return IS_ERR(t) ? NULL : t; } -static void __iomem *t8140_sleep_apcie_block(struct t8140_sleep *s, struct device_node *np, +static void __iomem *apple_sleep_apcie_block(struct apple_sleep *s, struct device_node *np, const char *name, const char *prop, struct apple_tunable **tunable) { @@ -3099,7 +3070,7 @@ static void __iomem *t8140_sleep_apcie_block(struct t8140_sleep *s, struct devic if (idx < 0) return NULL; if (prop) { - *tunable = t8140_sleep_apcie_tunable(s, np, name, prop); + *tunable = apple_sleep_apcie_tunable(s, np, name, prop); if (!*tunable) return NULL; } @@ -3111,25 +3082,25 @@ static void __iomem *t8140_sleep_apcie_block(struct t8140_sleep *s, struct devic * Its registers go with PCIe's node, which a test tree may have disabled, and a loader * publishes the tunables there. Missing ones only refuse a real sleep with ANS stopped. */ -static void t8140_sleep_map_apcie(struct t8140_sleep *s) +static void apple_sleep_map_apcie(struct apple_sleep *s) { struct device_node *np = of_parse_phandle(s->dev->of_node, "apple,apcie", 0); struct device_node *engine; if (!np) return; - s->apcie_axi = t8140_sleep_apcie_block(s, np, "axi2af", "apple,tunable-axi2af", + s->apcie_axi = apple_sleep_apcie_block(s, np, "axi2af", "apple,tunable-axi2af", &s->apcie_axi_tunable); - s->apcie_cio3pll = t8140_sleep_apcie_block(s, np, "cio3pll", "apple,tunable-cio3pll", + s->apcie_cio3pll = apple_sleep_apcie_block(s, np, "cio3pll", "apple,tunable-cio3pll", &s->apcie_cio3pll_tunable); - s->apcie_clkgen = t8140_sleep_apcie_block(s, np, "clkgen", "apple,tunable-clkgen", + s->apcie_clkgen = apple_sleep_apcie_block(s, np, "clkgen", "apple,tunable-clkgen", &s->apcie_clkgen_tunable); - s->apcie_phy = t8140_sleep_apcie_block(s, np, "phy-block", "apple,tunable-phy", + s->apcie_phy = apple_sleep_apcie_block(s, np, "phy-block", "apple,tunable-phy", &s->apcie_phy_tunable); - s->apcie_rc = t8140_sleep_apcie_block(s, np, "rc", NULL, NULL); - s->apcie_phy_ip = t8140_sleep_apcie_block(s, np, "phy-ip", "apple,tunable-phy-ip-pll", + s->apcie_rc = apple_sleep_apcie_block(s, np, "rc", NULL, NULL); + s->apcie_phy_ip = apple_sleep_apcie_block(s, np, "phy-ip", "apple,tunable-phy-ip-pll", &s->apcie_phy_ip_pll); - s->apcie_phy_ip_lanes = t8140_sleep_apcie_tunable(s, np, "phy-ip", + s->apcie_phy_ip_lanes = apple_sleep_apcie_tunable(s, np, "phy-ip", "apple,tunable-phy-ip-auspma"); if (!s->apcie_axi || !s->apcie_cio3pll || !s->apcie_clkgen || !s->apcie_phy || !s->apcie_rc || !s->apcie_phy_ip || !s->apcie_phy_ip_lanes) { @@ -3141,24 +3112,26 @@ static void t8140_sleep_map_apcie(struct t8140_sleep *s) s->apcie_axi = NULL; } - s->apcie_port = t8140_sleep_apcie_block(s, np, "port0", NULL, NULL); - s->apcie_port_phy = t8140_sleep_apcie_block(s, np, "phy0", NULL, NULL); + s->apcie_port = apple_sleep_apcie_block(s, np, "port0", NULL, NULL); + s->apcie_port_phy = apple_sleep_apcie_block(s, np, "phy0", NULL, NULL); engine = of_parse_phandle(np, "apple,piodma", 0); if (engine) { - s->apcie_engine = t8140_sleep_apcie_block(s, engine, "engine", NULL, NULL); - s->apcie_fabric = t8140_sleep_apcie_block(s, engine, "fabric", NULL, NULL); + s->apcie_engine = apple_sleep_apcie_block(s, engine, "engine", NULL, NULL); + s->apcie_fabric = apple_sleep_apcie_block(s, engine, "fabric", NULL, NULL); of_node_put(engine); } of_node_put(np); } -/* The radio port's PERST# on the AP pins @np, from the enabled T8140 PCIe host's ports. */ -static void t8140_sleep_find_radio_perst(struct t8140_sleep *s, struct device_node *np) +/* The radio port's PERST# on the AP pins @np, from the ports of the SoC's enabled PCIe host. */ +static void apple_sleep_find_radio_perst(struct apple_sleep *s, struct device_node *np) { struct device_node *host, *port; struct of_phandle_args args; - host = of_find_compatible_node(NULL, NULL, "apple,t8140-pcie"); + if (!s->soc->radio_pcie) + return; + host = of_find_compatible_node(NULL, NULL, s->soc->radio_pcie); if (!host) return; if (of_device_is_available(host)) { @@ -3178,7 +3151,7 @@ static void t8140_sleep_find_radio_perst(struct t8140_sleep *s, struct device_no } /* macOS leaves the skipped pins alone, and reading one may fault. */ -static void t8140_sleep_map_pins(struct t8140_sleep *s) +static void apple_sleep_map_pins(struct apple_sleep *s) { struct device_node *np = of_parse_phandle(s->dev->of_node, "apple,pinctrl", 0); u32 npins, pin; @@ -3203,12 +3176,12 @@ static void t8140_sleep_map_pins(struct t8140_sleep *s) s->npins = npins; s->pins = of_iomap(np, 0); if (s->pins) - t8140_sleep_find_radio_perst(s, np); + apple_sleep_find_radio_perst(s, np); out: of_node_put(np); } -static void t8140_sleep_map_debug_port(struct t8140_sleep *s) +static void apple_sleep_map_debug_port(struct apple_sleep *s) { struct device_node *np = of_parse_phandle(s->dev->of_node, "apple,debug-port", 0); struct device_node *bus; @@ -3223,10 +3196,43 @@ static void t8140_sleep_map_debug_port(struct t8140_sleep *s) s->hpm_np = np; } -static int t8140_sleep_probe(struct platform_device *pdev) +/* The books kept for each word of the SoC's lists */ +static int apple_sleep_alloc_books(struct apple_sleep *s) +{ + const struct apple_sleep_soc *soc = s->soc; + unsigned int n = soc->ps_all.count, naon = soc->ps_aon_all.count; + struct device *dev = s->dev; + + /* One bit, and one gate, for each */ + if (soc->nstray > BITS_PER_LONG || soc->ngate_coprocs > ARRAY_SIZE(s->gate)) + return dev_err_probe(dev, -EINVAL, "the SoC's lists are too long\n"); + + s->ps_on_saved = devm_kcalloc(dev, soc->ps_macos_on.count, sizeof(u32), GFP_KERNEL); + s->ps_off_saved = devm_kcalloc(dev, soc->ps_macos_off.count, sizeof(u32), GFP_KERNEL); + s->ps_aon_saved = devm_kcalloc(dev, soc->ps_aon_macos_off.count, sizeof(u32), + GFP_KERNEL); + s->ps_aon_auto_saved = devm_kcalloc(dev, soc->ps_aon_macos_no_auto.count, sizeof(u32), + GFP_KERNEL); + s->ps_commit = devm_kcalloc(dev, n, sizeof(u32), GFP_KERNEL); + s->ps_woke = devm_kcalloc(dev, n, sizeof(u32), GFP_KERNEL); + s->ps_late = devm_kcalloc(dev, n, sizeof(bool), GFP_KERNEL); + s->ps_done = devm_kcalloc(dev, n, sizeof(bool), GFP_KERNEL); + s->ps_aon_commit = devm_kcalloc(dev, naon, sizeof(u32), GFP_KERNEL); + s->ps_aon_woke = devm_kcalloc(dev, naon, sizeof(u32), GFP_KERNEL); + s->ps_aon_late = devm_kcalloc(dev, naon, sizeof(bool), GFP_KERNEL); + s->ps_aon_done = devm_kcalloc(dev, naon, sizeof(bool), GFP_KERNEL); + if (!s->ps_on_saved || !s->ps_off_saved || !s->ps_aon_saved || !s->ps_aon_auto_saved || + !s->ps_commit || !s->ps_woke || !s->ps_late || !s->ps_done || !s->ps_aon_commit || + !s->ps_aon_woke || !s->ps_aon_late || !s->ps_aon_done) + return -ENOMEM; + + return 0; +} + +static int apple_sleep_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct t8140_sleep *s; + struct apple_sleep *s; int ret; /* Below EL2 a monitor owns all of this. */ @@ -3237,13 +3243,16 @@ static int t8140_sleep_probe(struct platform_device *pdev) if (!s) return -ENOMEM; s->dev = dev; + s->soc = of_device_get_match_data(dev); + if (!s->soc) + return -ENODEV; /* Without the boot object's half, a wake would rerun its boot flow. */ - ret = t8140_sleep_map_mailbox(s); + ret = apple_sleep_map_mailbox(s); if (ret) return ret; - ret = t8140_sleep_map_regs(s); + ret = apple_sleep_map_regs(s); if (ret) return ret; ret = of_address_to_resource(dev->of_node, @@ -3254,31 +3263,34 @@ static int t8140_sleep_probe(struct platform_device *pdev) s->driver_down = devm_bitmap_zalloc(dev, resource_size(&s->ps_res) / 4, GFP_KERNEL); if (!s->driver_down) return -ENOMEM; - scoped_guard(spinlock_irqsave, &t8140_slp_ddr_lock) { - t8140_slp_ddr_word = s->wake + WAKE_SLP_DDR; - t8140_slp_ddr_write(); + ret = apple_sleep_alloc_books(s); + if (ret) + return ret; + scoped_guard(spinlock_irqsave, &sleep_slp_ddr_lock) { + sleep_slp_ddr_word = s->wake + WAKE_SLP_DDR; + sleep_slp_ddr_write(); } - ret = t8140_sleep_read_wake_sources(s); + ret = apple_sleep_read_wake_sources(s); if (ret) return ret; - ret = t8140_sleep_map_displays(s); + ret = apple_sleep_map_displays(s); if (ret) return ret; - t8140_sleep_map_apcie(s); - t8140_sleep_map_pins(s); - t8140_sleep_map_debug_port(s); + apple_sleep_map_apcie(s); + apple_sleep_map_pins(s); + apple_sleep_map_debug_port(s); platform_set_drvdata(pdev, s); - ret = spin_table_set_power_ops(&t8140_cpu_power_ops); + ret = spin_table_set_power_ops(&sleep_cpu_power_ops); if (ret) return dev_err_probe(dev, ret, "cannot control CPU power\n"); /* Last: the exported helpers take it as a probed driver, and a failure frees s. */ - t8140_sleep = s; + apple_sleep = s; /* Deep becomes the default sleep state, unless mem_sleep_default= chose another. */ - suspend_set_ops(&t8140_sleep_ops); + suspend_set_ops(&apple_sleep_ops); /* Not when every real sleep would refuse because ANS couldn't come back. */ if (!s->apcie_phy_tunable && mem_sleep_default == PM_SUSPEND_MAX) mem_sleep_current = PM_SUSPEND_TO_IDLE; @@ -3290,17 +3302,207 @@ static int t8140_sleep_probe(struct platform_device *pdev) return 0; } -static const struct of_device_id t8140_sleep_of_match[] = { - { .compatible = "apple,t8140-sleep" }, +/* + * T8140 (MacBook Neo). Power words from the ADT's /arm-io/pmgr, measured from macOS's sleep + * under aura. CPU clusters: 0 is E, 1 is P, ADT /arm-io/pmgr reg[5], [7], [9] and [14], [16], + * [18]. + */ +#define T8140_PS_DISP_CPU 0x10000 +#define T8140_PS_DISP_FE 0x318 +#define T8140_PS_DISP_SYS 0x280 +#define T8140_PS_DISPEXT0_CPU 0x328 +#define T8140_PS_DISPEXT0_FE 0x320 +#define T8140_PS_DISPEXT0_SYS 0x288 +#define T8140_PS_SIO_CPU 0x2a8 +#define T8140_PS_SEP 0xc00 +#define T8140_PS_ATC0_USB_AON 0x3e0 +#define T8140_PS_ATC0_COMMON 0x3e8 +#define T8140_PS_ATC0_USB 0x3f0 +#define T8140_PS_AP_TMM 0x1a0 +#define T8140_PS_GPIO 0x120 +#define T8140_PS_ANS 0x278 +#define T8140_PS_APCIE_GP 0x258 +#define T8140_PS_APCIE_SYS_GP 0x2a0 +#define T8140_PS_APCIE_ST 0x310 +#define T8140_PS_APCIE_SYS_ST 0x3a0 +#define T8140_PS_APCIE_PHY_SW 0x3c0 + +/* + * Every power word in the ADT, read just before ANS's stop so a run shows the whole chip. + */ +static const u32 t8140_ps_all[] = { + 0x0, 0x8, 0x10, 0x18, 0x30, 0x38, 0x50, 0x58, 0x60, 0x100, 0x108, 0x110, 0x118, + 0x120, 0x128, 0x130, 0x138, 0x140, 0x148, 0x150, 0x158, 0x160, 0x168, 0x170, 0x178, + 0x180, 0x188, 0x190, 0x198, 0x1a0, 0x1a8, 0x1b0, 0x1b8, 0x1c0, 0x1c8, 0x1d0, 0x1d8, + 0x1e0, 0x1e8, 0x1f0, 0x1f8, 0x200, 0x208, 0x210, 0x218, 0x220, 0x228, 0x230, 0x238, + 0x240, 0x248, 0x250, 0x258, 0x260, 0x268, 0x270, 0x278, 0x280, 0x288, 0x290, 0x298, + 0x2a0, 0x2a8, 0x2b0, 0x2b8, 0x2c0, 0x2c8, 0x2d0, 0x2d8, 0x2e0, 0x2e8, 0x2f0, 0x2f8, + 0x300, 0x308, 0x310, 0x318, 0x320, 0x328, 0x330, 0x338, 0x340, 0x348, 0x350, 0x358, + 0x360, 0x368, 0x370, 0x378, 0x380, 0x388, 0x390, 0x398, 0x3a0, 0x3a8, 0x3b0, 0x3b8, + 0x3c0, 0x3c8, 0x3d0, 0x3d8, 0x3e0, 0x3e8, 0x3f0, 0xc00, 0x4000, 0x4008, 0x4010, + 0x4018, 0x4020, 0x4028, 0x4030, 0x4038, 0x4040, 0x4048, 0x4050, 0x4058, 0x4060, + 0x8000, 0x8008, 0x8010, 0x8018, 0x8020, 0x8028, 0x8030, 0x8038, 0x8040, 0x8048, + 0xc000, 0xc008, 0xc010, 0x10000, 0x10008, 0x10010, 0x10018, 0x10020, 0x10028, + 0x10030, 0x10038, 0x10040, +}; + +static const u32 t8140_ps_aon_all[] = { + 0x0, 0x58, 0x60, 0x68, 0x70, 0x78, 0x80, 0x88, 0x90, 0x98, 0xa0, 0xa8, 0xb0, + 0x4000, 0x4008, 0x4010, 0x4018, 0x4020, 0x4028, 0x4030, 0x4038, 0x4040, 0x4048, + 0x4050, 0x4058, 0x4060, 0x4068, 0x4070, 0x4078, 0x4080, 0x4088, 0x4090, 0x4098, + 0x40a0, 0x40a8, 0x40b0, 0x40b8, 0x40c0, 0x40c8, 0x40d0, 0x40d8, 0x40e0, 0x40e8, + 0x40f0, 0x40f8, 0x4100, 0x4108, 0x4110, 0x4118, 0x4120, 0x4128, 0x4130, 0x4138, + 0x4140, 0x4148, 0x4150, 0x4158, 0x4160, 0x4168, 0x4170, 0x4178, 0x4180, 0x8000, + 0x8008, 0x8010, 0x8018, 0x8020, 0x8028, 0x8030, 0x8038, 0x8040, 0x8048, 0x8050, + 0x8058, 0x8060, 0x8068, 0x8070, 0x8078, +}; + +/* What macOS has on at the commit and Linux leaves off, in its order: nothing uses them here. */ +static const u32 t8140_ps_macos_on[] = { + 0x2f0, 0x2f8, 0x358, 0x360, 0x368, 0x370, 0x378, 0x380, 0x388, 0x118, +}; + +/* + * The power words macOS's wake writes back on before any driver runs, in its order + * (logs/wakerec-20261105-154137Z). The engine leaves domains like GPIO dark, and genpd still + * has them on, so nothing else would. + */ +static const u32 t8140_ps_wake_order[] = { + 0xc00, 0x110, 0x118, 0x120, 0x168, 0x178, 0x2a8, 0x2f0, 0x2f8, 0x300, 0x358, 0x360, + 0x368, 0x370, 0x378, 0x380, 0x388, 0x3d0, 0x3d8, +}; + +static const u32 t8140_ps_aon_wake_order[] = { 0x78, 0x88, 0x8030, 0x8038 }; + +/* What macOS has off, highest first, as it lowers them. Domains with their own stop aren't here. */ +static const u32 t8140_ps_macos_off[] = { + 0x4060, 0x4058, 0x4050, 0x4048, 0x4040, 0x4038, 0x4030, 0x4028, 0x4020, 0x4018, + 0x4010, 0x4008, 0x4000, 0x398, 0x390, 0x350, 0x348, 0x340, 0x338, 0x330, + 0x308, 0x2e8, 0x2e0, 0x2d8, 0x2d0, 0x2c8, 0x2c0, 0x2b8, 0x2b0, 0x298, + 0x290, 0x270, 0x268, 0x250, 0x248, 0x240, 0x238, 0x230, 0x1a0, 0x190, + 0x188, 0x180, 0x170, 0x160, 0x158, 0x150, 0x148, 0x140, 0x108, +}; + +static const u32 t8140_ps_aon_macos_off[] = { + 0x8028, 0x4140, 0x40f8, 0x80, +}; + +/* The AOP's SPMI words: on, with AUTO_ENABLE off on macOS. */ +static const u32 t8140_ps_aon_macos_no_auto[] = { + 0x4078, 0x4080, +}; + +static const u32 t8140_ps_still_on[] = { + T8140_PS_ATC0_COMMON, T8140_PS_DISP_SYS, T8140_PS_DISPEXT0_SYS, T8140_PS_ANS, + 0x270 /* AVD_SYS */, 0x240 /* VENC0_SYS */, 0x248 /* VENC1_SYS */, 0x1f0 /* GFX */, + 0x230 /* JPG */, T8140_PS_APCIE_SYS_GP, +}; + +/* + * What XNU polls just before its complex words, and panics on if one is on, besides the + * display's, ANS's and ATC0's, which the commit lowers itself. + */ +static const struct apple_sleep_word t8140_stray_words[] = { + { "AVD_SYS", 0x270 }, { "VENC0_SYS", 0x240 }, { "VENC1_SYS", 0x248 }, + { "GFX", 0x1f0 }, { "JPG", 0x230 }, +}; + +/* + * A commit with the PMP and SIO still running reset the SoC 5.5 s later. The DCPs' drivers + * power their CPUs off at DPMS off. + */ +static const struct apple_sleep_gate_coproc t8140_gate_coprocs[] = { + { "/soc/dcp@412e00000", true, T8140_PS_DISP_CPU }, + { "/soc/dcp@416e00000", true, T8140_PS_DISPEXT0_CPU }, + { "/soc/pmp@300500000", true }, { "/soc/dma-controller@385e00000", true }, + { "/soc/nvme@3cdcc0000" }, { "/soc/mtp@314600000" }, + { "/soc/aop@310c00000" }, { "/soc/smc@30c600000" }, + { "/soc/sep@282600000" }, { "/soc/gpu@482600000" }, +}; + +static const struct apple_sleep_word t8140_gate_off[] = { + { "ane_sys", 0x290 }, { "isp_sys", 0x268 }, { "prores", 0x250 }, + { "avd_sys", 0x270 }, { "jpg", 0x230 }, { "venc0_sys", 0x240 }, +}; + +static const struct apple_sleep_wake_name t8140_wake_names[] = { + { 0, "AOP.AICWakeupAP" }, { 5, "AOP.CPUWakeupAP" }, + { 42, "AOP.OutboxNotEmpty" }, { 62, "AOP.SPMI0.SW3" }, + { 66, "AOP.SPMI1.SW3" }, { 70, "ATC0_PMGR_USB_WAKEUP" }, + { 80, "MTP.DOCK.CHANNELS.AP0.IRQ" }, { 99, "MTP.OUTBOX.N.EMPTY_IRQ" }, + { 109, "NUB.APWakeupTime" }, { 110, "NUB.APWatchdog" }, + { 125, "NUB.GPIO.IRQ6" }, { 131, "NUB.SPMI0.SW3" }, + { 136, "NUB.SPMI1.SW3" }, { 140, "NUB.WakeupAP" }, + { 145, "SMC.CPUWakeupAP" }, { 165, "SMC.OutboxNotEmpty" }, + { 211, "AOP.TrustedOutboxNotEmpty" }, { 227, "AOP2.OutboxNotEmpty" }, + { 231, "AOP2.TrustedOutboxNotEmpty" }, { 236, "AOP2.CPUWakeupAP" }, +}; + +static const struct apple_sleep_soc t8140_sleep_soc = { + .ps = { + .disp = { + { T8140_PS_DISP_CPU, T8140_PS_DISP_FE, T8140_PS_DISP_SYS }, + { T8140_PS_DISPEXT0_CPU, T8140_PS_DISPEXT0_FE, T8140_PS_DISPEXT0_SYS }, + }, + .sio_cpu = T8140_PS_SIO_CPU, + .sep = T8140_PS_SEP, + .usb_aon = T8140_PS_ATC0_USB_AON, + .usb_common = T8140_PS_ATC0_COMMON, + .usb = T8140_PS_ATC0_USB, + .ap_tmm = T8140_PS_AP_TMM, + .gpio = T8140_PS_GPIO, + .ans = T8140_PS_ANS, + .apcie_gp = T8140_PS_APCIE_GP, + .apcie_sys_gp = T8140_PS_APCIE_SYS_GP, + .apcie_st = T8140_PS_APCIE_ST, + .apcie_sys_st = T8140_PS_APCIE_SYS_ST, + .apcie_phy_sw = T8140_PS_APCIE_PHY_SW, + }, + .ps_aon_debug_switch = 0xb0, + .pwrgate_wake = { 0x000, 0x040 }, + .pwrgate_cpm = { 0x060, 0x068 }, + .pwrgate_disp = { 0xc8c, 0xcd0 }, + .ps_all = SLEEP_WORDS(t8140_ps_all), + .ps_aon_all = SLEEP_WORDS(t8140_ps_aon_all), + .ps_macos_on = SLEEP_WORDS(t8140_ps_macos_on), + .ps_macos_off = SLEEP_WORDS(t8140_ps_macos_off), + .ps_aon_macos_off = SLEEP_WORDS(t8140_ps_aon_macos_off), + .ps_aon_macos_no_auto = SLEEP_WORDS(t8140_ps_aon_macos_no_auto), + .ps_wake_order = SLEEP_WORDS(t8140_ps_wake_order), + .ps_aon_wake_order = SLEEP_WORDS(t8140_ps_aon_wake_order), + .ps_still_on = SLEEP_WORDS(t8140_ps_still_on), + .stray = t8140_stray_words, + .nstray = ARRAY_SIZE(t8140_stray_words), + .gate_off = t8140_gate_off, + .ngate_off = ARRAY_SIZE(t8140_gate_off), + .gate_coprocs = t8140_gate_coprocs, + .ngate_coprocs = ARRAY_SIZE(t8140_gate_coprocs), + .wake_names = t8140_wake_names, + .nwake_names = ARRAY_SIZE(t8140_wake_names), + /* 21 sources. Measured: macOS reads 0x001fef77 there. */ + .wake_last_word = GENMASK(20, 0), + .radio_pcie = "apple,t8140-pcie", +}; + +/* + * Only SoCs whose sleep was measured and tested. On every other the driver doesn't bind, and + * s2idle stays the only sleep. + */ +static const struct of_device_id apple_sleep_of_match[] = { + { .compatible = "apple,t8140-sleep", .data = &t8140_sleep_soc }, { } }; -static struct platform_driver t8140_sleep_driver = { +static struct platform_driver apple_sleep_driver = { .driver = { - .name = "t8140-sleep", - .of_match_table = t8140_sleep_of_match, + .name = "apple-sleep", + .of_match_table = apple_sleep_of_match, .suppress_bind_attrs = true, }, - .probe = t8140_sleep_probe, + .probe = apple_sleep_probe, }; -builtin_platform_driver(t8140_sleep_driver); +builtin_platform_driver(apple_sleep_driver); + +#if IS_ENABLED(CONFIG_APPLE_SOC_SLEEP_KUNIT_TEST) +#include "sleep-test.c" +#endif diff --git a/include/linux/soc/apple/sleep.h b/include/linux/soc/apple/sleep.h index 2a22638377a11..9b745371743e3 100644 --- a/include/linux/soc/apple/sleep.h +++ b/include/linux/soc/apple/sleep.h @@ -14,7 +14,7 @@ struct device; struct device_node; -#if IS_ENABLED(CONFIG_APPLE_T8140_SLEEP) +#if IS_ENABLED(CONFIG_APPLE_SOC_SLEEP) /* * A coprocessor that can't be stopped until a sleep is certain, and can't be * restarted without one. While one is registered and not ready, a suspend is diff --git a/tools/asahi/sep-suspend-errors-tests.py b/tools/asahi/sep-suspend-errors-tests.py index 3c6c2274b253e..f01bdc966f6c7 100644 --- a/tools/asahi/sep-suspend-errors-tests.py +++ b/tools/asahi/sep-suspend-errors-tests.py @@ -49,7 +49,7 @@ def macro(source, name): #define USEC_PER_MSEC 1000 #define READ_ONCE(x) (x) #define IS_ENABLED(x) (config_enabled) -#define CONFIG_APPLE_T8140_SLEEP 1 +#define CONFIG_APPLE_SOC_SLEEP 1 #define __maybe_unused #define dev_err(...) ((void)0) #define dev_dbg(...) ((void)0) @@ -378,7 +378,7 @@ def main(): if args.mutation == 'off-ignores-actual': shim = shim.replace('((val & SEP_PS_DEV_DISABLE) && !(val & SEP_PS_ACTUAL))', '(val & SEP_PS_DEV_DISABLE)') if args.mutation == 'no-capability-check': - shim = shim.replace(' || !IS_ENABLED(CONFIG_APPLE_T8140_SLEEP) ||\n\t !is_kernel_in_hyp_mode()', '') + shim = shim.replace(' || !IS_ENABLED(CONFIG_APPLE_SOC_SLEEP) ||\n\t !is_kernel_in_hyp_mode()', '') iopoll = (contracts / 'include/linux/iopoll.h').read_text() regmap = (contracts / 'include/linux/regmap.h').read_text() c = C_PREFIX + '\n' + '\n'.join([macro(iopoll, 'poll_timeout_us'), macro(iopoll, 'read_poll_timeout'), macro(regmap, 'regmap_read_poll_timeout')]) diff --git a/tools/testing/selftests/drivers/gpu/apple-dcp/lifecycle.py b/tools/testing/selftests/drivers/gpu/apple-dcp/lifecycle.py index 6cea3b059279e..00c87e213d88c 100644 --- a/tools/testing/selftests/drivers/gpu/apple-dcp/lifecycle.py +++ b/tools/testing/selftests/drivers/gpu/apple-dcp/lifecycle.py @@ -1200,6 +1200,7 @@ def block(marker): int iomfb_method_profile,firmware_compat; const struct resource *firmware_clock; u32 firmware_display_windows,firmware_scratch,firmware_request; + bool s2r,s2r_ext; }; struct apple_dcp { struct device *dev; struct apple_dcp_hw_data hw;