From aa16d423cf8a10e6bee0a79313a13fa9565292c6 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Wed, 11 Nov 2026 10:18:42 -0700 Subject: [PATCH 1/2] PCI: apple: qualify the K116s for suspend-to-idle over the adopted N1 link The adopted link's suspend check admits only the J716c; the K116s is its OLED sibling with the same T6050 host and the N1 on port 0, and the first s2idle attempt there failed with -EBUSY from the noirq suspend. Let it through the same checks. Signed-off-by: Jens Axboe [thomas: rebased onto integration, where the check also admits the K114c] Signed-off-by: Thomas Glanzmann --- drivers/pci/controller/pcie-apple.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/pci/controller/pcie-apple.c b/drivers/pci/controller/pcie-apple.c index dec95de353ca..64cf71162806 100644 --- a/drivers/pci/controller/pcie-apple.c +++ b/drivers/pci/controller/pcie-apple.c @@ -5744,9 +5744,14 @@ static int apple_pcie_check_adopted_s2idle(struct apple_pcie *pcie) struct apple_pcie_port *port; int error = 0; - /* Other adopted platforms and deeper sleep remain unqualified. */ + /* + * Qualified on the J716c, the K114c and the K116s. The K116s is the + * J716c's OLED sibling: the same T6050 host, the N1 on port 0. Other + * adopted platforms and deeper sleep remain unqualified. + */ if (!(of_machine_is_compatible("apple,j716c") || - of_machine_is_compatible("apple,k114c")) || !pm_suspend_no_platform()) + of_machine_is_compatible("apple,k114c") || + of_machine_is_compatible("apple,k116s")) || !pm_suspend_no_platform()) return -EBUSY; if (!(readl(pcie->base + CORE_RC_CTL) & CORE_RC_CTL_RUN) || From 70bf8ed1867b2b322c89f3a74e4f51a6547dc1fe Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Wed, 11 Nov 2026 13:19:41 -0700 Subject: [PATCH 2/2] PCI: apple: let devices below a kernel-trained port leave D0 over s2idle The adopted-link s2idle check walked the whole bus and refused to suspend when any device had left D0. With a kernel-trained second port (the SD reader on the K116s) the PCI core puts that root port into D3hot, and suspend failed with -EBUSY. Below a port Linux trained itself, only the devices that asked to stay awake need to remain in D0; the PCI core restores the rest. Below a port the boot loader trained, such as the N1's, every device must still be in D0, so the J716c and the K114c, whose only port is adopted, are checked as before. Signed-off-by: Jens Axboe [thomas: skip only devices below a port Linux trained (port->pwren), so adopted ports keep requiring D0 for every device] Signed-off-by: Thomas Glanzmann --- drivers/pci/controller/pcie-apple.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/drivers/pci/controller/pcie-apple.c b/drivers/pci/controller/pcie-apple.c index 64cf71162806..1b87ff0fa9ac 100644 --- a/drivers/pci/controller/pcie-apple.c +++ b/drivers/pci/controller/pcie-apple.c @@ -5711,6 +5711,22 @@ static void apple_pcie_stop_for_sleep(struct device *dev) static void apple_pcie_neo_save_functions(struct apple_pcie *pcie); +/* Whether Linux trained the port above pdev itself (cold train), rather than adopting it */ +static bool apple_pcie_below_kernel_trained_port(struct pci_dev *pdev) +{ + struct pci_config_window *cfg = pdev->sysdata; + struct apple_pcie *pcie = apple_pcie_lookup(cfg->parent); + struct pci_dev *rp = pcie_find_root_port(pdev); + struct apple_pcie_port *port; + + if (!rp) + return false; + list_for_each_entry(port, &pcie->ports, entry) + if (port->idx == PCI_SLOT(rp->devfn)) + return !!port->pwren; + return false; +} + /* * The J716c radio drivers save PCI state without entering D3. The PCI core * keeps the root port in D0 as well. Its host and DART have no switchable @@ -5725,6 +5741,16 @@ static int apple_pcie_check_d0(struct pci_dev *pdev, void *data) int *error = data; u16 value; + /* + * Below a port the boot loader trained (the N1's), every device stays + * in D0. Below a port Linux trained itself (the K116s SD reader's), the + * PCI core restores what it suspends to D3hot, so only the devices that + * asked to stay awake need to remain in D0. + */ + if (!device_awake_path(&pdev->dev) && + apple_pcie_below_kernel_trained_port(pdev)) + return 0; + if (pci_read_config_word(pdev, PCI_VENDOR_ID, &value) || value != pdev->vendor) { *error = -ENODEV;