diff --git a/drivers/pci/controller/pcie-apple.c b/drivers/pci/controller/pcie-apple.c index dec95de353ca..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; @@ -5744,9 +5770,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) ||