diff --git a/arch/arm64/kernel/hibernate.c b/arch/arm64/kernel/hibernate.c index 424291c547f02..9398c8d0ecb03 100644 --- a/arch/arm64/kernel/hibernate.c +++ b/arch/arm64/kernel/hibernate.c @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -336,7 +337,7 @@ int swsusp_arch_suspend(void) unsigned long flags; struct sleep_stack_data state; - if (cpus_are_stuck_in_kernel()) { + if (cpus_are_stuck_in_kernel() || spin_table_cpus_stuck()) { pr_err("Can't hibernate: no mechanism to offline secondary CPUs.\n"); return -EBUSY; } diff --git a/arch/arm64/kernel/smp_spin_table.c b/arch/arm64/kernel/smp_spin_table.c index 2d8819416a489..2d7660bd22f80 100644 --- a/arch/arm64/kernel/smp_spin_table.c +++ b/arch/arm64/kernel/smp_spin_table.c @@ -397,15 +397,13 @@ const struct cpu_operations smp_rvbar_spin_table_ops = { }; /* - * Parking CPUs leave the kernel only through a platform's power ops. Until one - * registers, kexec has to take them as stuck, as on plain spin-table. + * An offlined parking CPU waits in this kernel's text and stack, which a kexec or a + * hibernation restore overwrites: it is stuck for those, power ops or not. */ bool spin_table_cpus_stuck(void) { unsigned int cpu; - if (READ_ONCE(spin_table_power)) - return false; for_each_possible_cpu(cpu) if (get_cpu_ops(cpu) == &smp_rvbar_spin_table_ops) return true; diff --git a/drivers/dma/apple-admac.c b/drivers/dma/apple-admac.c index ee660cc38992f..34599811779fb 100644 --- a/drivers/dma/apple-admac.c +++ b/drivers/dma/apple-admac.c @@ -715,7 +715,11 @@ static int admac_resume(struct dma_chan *chan) if (adchan->current_tx && adchan->current_tx->error) { ret = -EIO; } else if (adchan->lost && adchan->current_tx) { - /* The rings went with the registers: start from the top. */ + struct admac_tx *tx = adchan->current_tx; + + /* The rings went with the registers: start again from the last period reclaimed. */ + tx->submitted_pos = tx->reclaimed_pos; + tx->nperiods_outstanding = 0; adchan->nperiod_acks = 0; admac_start_current_tx(adchan); } else { @@ -1116,9 +1120,9 @@ static int admac_runtime_resume(struct device *dev) } /* - * A system sleep that reached the hardware takes the registers of a channel a - * client still holds, and its client won't configure it again: only a new - * hw_params does that. + * A firmware-driven sleep, refused or not, takes the registers of a channel a + * client still holds (genpd gates the domain at noirq), and its client won't + * configure it again: only a new hw_params does that. */ static int __maybe_unused admac_resume_early(struct device *dev) { @@ -1126,7 +1130,7 @@ static int __maybe_unused admac_resume_early(struct device *dev) unsigned long flags; int i; - if (!pm_resume_via_firmware()) + if (!pm_suspend_via_firmware()) return 0; mutex_lock(&ad->cache_alloc_lock); diff --git a/drivers/dma/apple-sio.c b/drivers/dma/apple-sio.c index 1ecbec8298cdf..3b9d804f206d1 100644 --- a/drivers/dma/apple-sio.c +++ b/drivers/dma/apple-sio.c @@ -2161,14 +2161,15 @@ static __maybe_unused int sio_resume_early(struct device *dev) dev_info(dev, "sleep refused: firmware back after a reset\n"); return 0; } - sio->asleep = false; - /* Endpoints and parameters stay with the firmware: nothing is sent again. */ ret = apple_rtkit_sleep_return(sio->rtk, sio->base + REG_CPU_CONTROL); - if (ret) + if (ret) { + /* Still down: the next sleep restarts it, or resets it if refused. */ dev_err(dev, "firmware did not come back from sleep: %d\n", ret); - else - dev_dbg(dev, "firmware back from sleep\n"); + return 0; + } + sio->asleep = false; + dev_dbg(dev, "firmware back from sleep\n"); return 0; } diff --git a/drivers/gpu/drm/apple/dcp-internal.h b/drivers/gpu/drm/apple/dcp-internal.h index 2f135e7419e95..a41c4bf611784 100644 --- a/drivers/gpu/drm/apple/dcp-internal.h +++ b/drivers/gpu/drm/apple/dcp-internal.h @@ -977,6 +977,7 @@ int iomfb_configure_backlight_h17p(struct apple_dcp *dcp, u32 maximum, bool default_valid, u32 default_nits); void iomfb_queue_init(struct apple_dcp *dcp); void iomfb_queue_stop(struct apple_dcp *dcp); +void iomfb_reset_transport(struct apple_dcp *dcp); bool iomfb_queue_drain(struct apple_dcp *dcp, unsigned long timeout); int iomfb_queue_settle(struct apple_dcp *dcp, unsigned long timeout); void iomfb_pause_presentation(struct apple_dcp *dcp, bool pause); diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index 65655dfcdc1df..1dd0ff5fc518a 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -6775,10 +6775,13 @@ static void dcp_drain_for_sleep(struct apple_dcp *dcp) */ static void dcp_hold_for_sleep(struct apple_dcp *dcp) { - /* A failed component bind leaves no coprocessor session to hold. */ + /* + * A failed component bind leaves no coprocessor session to hold. Firmware that DPMS + * stopped sends nothing, and its presentation stays paused until DPMS restarts it. + */ if (IS_ERR_OR_NULL(dcp->rtk) || !dcp_uses_soft_dpms(dcp) || !dcp_presents_nits(dcp) || - READ_ONCE(dcp->crashed)) + READ_ONCE(dcp->crashed) || READ_ONCE(dcp->s2r_fw_down)) return; iomfb_pause_presentation(dcp, true); @@ -7537,6 +7540,19 @@ static int dcp_s2r_ext_domains_on(struct apple_dcp *dcp) return ret; } +/* + * A firmware stopped while crashed is about to start over: forget the calls and the + * transaction the old one left unanswered, and let the new one's messages through. A failed + * start marks it crashed again. + */ +static void dcp_s2r_forget_crash(struct apple_dcp *dcp) +{ + if (!READ_ONCE(dcp->crashed)) + return; + iomfb_reset_transport(dcp); + WRITE_ONCE(dcp->crashed, false); +} + /* * DCPEXT back after a real sleep, the same way in every macOS wake measured: its domains, * its DART's streams, and a warm restart, with no secure port and no display-on call. After @@ -7547,6 +7563,7 @@ static int dcp_s2r_ext_start(struct apple_dcp *dcp, bool cycled) void __iomem *control = dcp->coproc_reg + APPLE_DCP_COPROC_CPU_CONTROL; int ret; + dcp_s2r_forget_crash(dcp); if (!dcp->s2r_dcp_dart) return -ENODEV; ret = apple_dart_gapf_ready(dcp->s2r_dcp_dart); @@ -7689,6 +7706,7 @@ static int dcp_s2r_warm_restart(struct apple_dcp *dcp) { int ret; + dcp_s2r_forget_crash(dcp); ret = dcp_s2r_domains_on(dcp); if (ret) return ret; diff --git a/drivers/gpu/drm/apple/iomfb.c b/drivers/gpu/drm/apple/iomfb.c index fc88a2b1f8d5c..a39068aac044e 100644 --- a/drivers/gpu/drm/apple/iomfb.c +++ b/drivers/gpu/drm/apple/iomfb.c @@ -582,6 +582,38 @@ void iomfb_queue_init(struct apple_dcp *dcp) atomic_set(&dcp->iomfb.opaque_x_state, IOMFB_OPAQUE_X_WAITING); } +/* + * For firmware stopped cold and started again: the calls and the transaction the old + * one never answered went with it, and would hold the channels and the queue forever. + */ +void iomfb_reset_transport(struct apple_dcp *dcp) +{ + struct iomfb_transaction *transaction; + + if (!iomfb_uses_queue(dcp)) + return; + + cancel_delayed_work_sync(&dcp->iomfb.timeout); + mutex_lock(&dcp->iomfb.lock); + transaction = dcp->iomfb.active; + dcp->iomfb.active = NULL; + if (transaction) { + if (transaction->brightness_only) + dcp->iomfb.backlight_queued = false; + transaction->release(transaction); + } + iomfb_discard_pending(dcp); + dcp->ch_cmd.depth = 0; + dcp->ch_oobcmd.depth = 0; + dcp->ch_cb.depth = 0; + dcp->ch_oobcb.depth = 0; + dcp->ch_async.depth = 0; + dcp->ch_oobasync.depth = 0; + dcp->present_state_h17p.pending = false; + dcp->present_state_h17p.accepted = false; + mutex_unlock(&dcp->iomfb.lock); +} + void iomfb_queue_stop(struct apple_dcp *dcp) { if (!iomfb_uses_queue(dcp)) diff --git a/drivers/nvme/host/apple.c b/drivers/nvme/host/apple.c index f3eb82be43a30..a34cd2dccb002 100644 --- a/drivers/nvme/host/apple.c +++ b/drivers/nvme/host/apple.c @@ -2946,6 +2946,8 @@ static int apple_nvme_suspend(struct device *dev) int ret; if (anv->hw->needs_ioq_registers) { + /* A sleep right after a wake, as from a dark wake: let that reset finish first. */ + flush_work(&anv->ctrl.reset_work); anv->sleep_stop = apple_nvme_sleep_wanted(anv); anv->sleep_idled = false; anv->sleep_failed = false; diff --git a/drivers/phy/apple/atc.c b/drivers/phy/apple/atc.c index ed816d357f5b1..11bf8dfb37055 100644 --- a/drivers/phy/apple/atc.c +++ b/drivers/phy/apple/atc.c @@ -788,6 +788,8 @@ enum atcphy_t8132_cio_tunable { * off for a PIPE or lane change and is not powered on again yet * @usb3_port_work: Retries the power-on of @usb3_port_off after a USB failure * @usb3_port_retries: Retries left for @usb3_port_work + * @sleep_finish_work: Ends a sleep for the controller, outside its device lock + * @sleep_finish_kept: Whether the controller kept its state, for @sleep_finish_work * @mode: Current PHY operating mode * @swap_lanes: True if lanes must be swapped due to cable orientation * @dp_link_rate: DisplayPort link rate @@ -919,6 +921,8 @@ struct apple_atcphy { bool usb3_port_off; struct delayed_work usb3_port_work; unsigned int usb3_port_retries; + struct work_struct sleep_finish_work; + bool sleep_finish_kept; enum atcphy_mode mode; int dp_link_rate; bool swap_lanes; @@ -6556,6 +6560,8 @@ static bool atcphy_has_t8103_usb3_workaround(const struct atcphy_hw *hw, of_device_is_compatible(root, "apple,t8103"); } +static void atcphy_sleep_finish_work(struct work_struct *work); + static int atcphy_probe(struct platform_device *pdev) { struct apple_atcphy *atcphy; @@ -6578,6 +6584,9 @@ static int atcphy_probe(struct platform_device *pdev) mutex_init(&atcphy->lock); platform_set_drvdata(pdev, atcphy); ret = devm_delayed_work_autocancel(dev, &atcphy->usb3_port_work, atcphy_usb3_port_work); + if (ret) + return ret; + ret = devm_work_autocancel(dev, &atcphy->sleep_finish_work, atcphy_sleep_finish_work); if (ret) return ret; @@ -7110,12 +7119,8 @@ EXPORT_SYMBOL_GPL(apple_atcphy_sleep_result); * again. Only the PHY's own failures stop it for the later sleeps; a sleep refused for a * USB change doesn't. */ -void apple_atcphy_sleep_finish(struct phy *usb2_phy, bool kept) +static void atcphy_sleep_finish(struct apple_atcphy *atcphy, bool kept) { - struct apple_atcphy *atcphy = atcphy_from_usb2_phy(usb2_phy); - - if (!atcphy) - return; scoped_guard(mutex, &atcphy->lock) { if (!kept && atcphy->sleep_ret < 0 && atcphy->sleep_ret != -EBUSY) atcphy->sleep_no_keep = true; @@ -7124,8 +7129,47 @@ void apple_atcphy_sleep_finish(struct phy *usb2_phy, bool kept) } atcphy_sleep_ss_up(atcphy); } + +void apple_atcphy_sleep_finish(struct phy *usb2_phy, bool kept) +{ + struct apple_atcphy *atcphy = atcphy_from_usb2_phy(usb2_phy); + + if (atcphy) + atcphy_sleep_finish(atcphy, kept); +} EXPORT_SYMBOL_GPL(apple_atcphy_sleep_finish); +static void atcphy_sleep_finish_work(struct work_struct *work) +{ + struct apple_atcphy *atcphy = container_of(work, struct apple_atcphy, sleep_finish_work); + + atcphy_sleep_finish(atcphy, READ_ONCE(atcphy->sleep_finish_kept)); +} + +/* + * The SuperSpeed side may wait for the controller's device lock, which an unbind of the + * controller holds while it cancels the controller's own work: so this work is the PHY's. + */ +void apple_atcphy_sleep_finish_async(struct phy *usb2_phy, bool kept) +{ + struct apple_atcphy *atcphy = atcphy_from_usb2_phy(usb2_phy); + + if (!atcphy) + return; + WRITE_ONCE(atcphy->sleep_finish_kept, kept); + queue_work(system_unbound_wq, &atcphy->sleep_finish_work); +} +EXPORT_SYMBOL_GPL(apple_atcphy_sleep_finish_async); + +void apple_atcphy_sleep_finish_flush(struct phy *usb2_phy) +{ + struct apple_atcphy *atcphy = atcphy_from_usb2_phy(usb2_phy); + + if (atcphy) + flush_work(&atcphy->sleep_finish_work); +} +EXPORT_SYMBOL_GPL(apple_atcphy_sleep_finish_flush); + static int atcphy_suspend(struct device *dev) { struct apple_atcphy *atcphy = dev_get_drvdata(dev); diff --git a/drivers/pmdomain/apple/pmgr-pwrstate.c b/drivers/pmdomain/apple/pmgr-pwrstate.c index 335f4f4f74f7f..2ee6c3b850098 100644 --- a/drivers/pmdomain/apple/pmgr-pwrstate.c +++ b/drivers/pmdomain/apple/pmgr-pwrstate.c @@ -363,6 +363,12 @@ static int apple_pmgr_ps_power_off(struct generic_pm_domain *genpd) /* A live coprocessor keeps this domain through system sleep. */ if (ps->sleep_retained && ps->system_sleep) return -EBUSY; + /* + * Its driver took it down for the sleep and raises it in its own order. Gating it + * here would take its parents too, and genpd would raise them all first at the wake. + */ + if (ps->system_sleep && apple_sleep_domain_is_down(ps->dev->of_node)) + return -EBUSY; ret = apple_pmgr_ps_set(genpd, APPLE_PMGR_PS_PWRGATE, false); if (ret) diff --git a/drivers/soc/apple/rtkit-helper.c b/drivers/soc/apple/rtkit-helper.c index 498d1413e14ef..59bb373e954ac 100644 --- a/drivers/soc/apple/rtkit-helper.c +++ b/drivers/soc/apple/rtkit-helper.c @@ -43,6 +43,8 @@ struct apple_rtkit_helper { bool ap_idle; /* It was told, gave no answer, and wasn't told otherwise. */ bool idle_unanswered; + /* Ends a sleep whose .prepare failed, which gets no .complete. */ + struct notifier_block pm_nb; void __iomem *asc_base; @@ -241,6 +243,8 @@ static int apple_rtkit_helper_inherit_buffers( return count; } +static int apple_rtkit_helper_pm_register(struct apple_rtkit_helper *helper); + static int apple_rtkit_helper_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -343,6 +347,9 @@ static int apple_rtkit_helper_probe(struct platform_device *pdev) helper->rtk); if (ret) return ret; + ret = apple_rtkit_helper_pm_register(helper); + if (ret) + return ret; /* There is no safe module-unload boundary for the retained session. */ __module_get(THIS_MODULE); @@ -367,7 +374,7 @@ static int apple_rtkit_helper_probe(struct platform_device *pdev) if (ret != 0) return dev_err_probe(dev, ret, "Failed to wake up coprocessor\n"); - return 0; + return apple_rtkit_helper_pm_register(helper); } static void apple_rtkit_helper_remove(struct platform_device *pdev) @@ -393,7 +400,7 @@ static int apple_rtkit_helper_ap_power(struct apple_rtkit_helper *helper, bool o on ? "on" : "idle", ret); /* * Nothing is sent to undo an unanswered idle notice, which macOS never does: - * .complete looks whether a late answer came, and only then sends AP on. + * the end of the sleep looks whether a late answer came, and only then sends AP on. */ if (ret && !on) helper->idle_unanswered = true; @@ -434,16 +441,13 @@ static int __maybe_unused apple_rtkit_helper_prepare(struct device *dev) return apple_rtkit_helper_ap_idle(dev_get_drvdata(dev)); } -/* A sleep refused before this device's .suspend never calls its .resume. */ -static void __maybe_unused apple_rtkit_helper_complete(struct device *dev) +static void apple_rtkit_helper_sleep_end(struct apple_rtkit_helper *helper) { - struct apple_rtkit_helper *helper = dev_get_drvdata(dev); - if (helper->idle_unanswered) { bool late = !apple_rtkit_is_running(helper->rtk); helper->idle_unanswered = false; - dev_info(dev, "after the unanswered idle notice: %s\n", + dev_info(helper->dev, "after the unanswered idle notice: %s\n", late ? "its answer came late, sending AP on" : "still no answer, nothing sent"); helper->ap_idle = late; @@ -451,6 +455,41 @@ static void __maybe_unused apple_rtkit_helper_complete(struct device *dev) apple_rtkit_helper_ap_on(helper); } +/* A sleep refused before this device's .suspend never calls its .resume. */ +static void __maybe_unused apple_rtkit_helper_complete(struct device *dev) +{ + apple_rtkit_helper_sleep_end(dev_get_drvdata(dev)); +} + +/* The PM core calls no .complete after a failed .prepare: end that sleep here. */ +static int apple_rtkit_helper_pm_notify(struct notifier_block *nb, unsigned long action, + void *data) +{ + struct apple_rtkit_helper *helper = container_of(nb, struct apple_rtkit_helper, pm_nb); + + if (action == PM_POST_SUSPEND && helper->idle_unanswered) + apple_rtkit_helper_sleep_end(helper); + return NOTIFY_DONE; +} + +static void apple_rtkit_helper_pm_unregister(void *data) +{ + struct apple_rtkit_helper *helper = data; + + unregister_pm_notifier(&helper->pm_nb); +} + +static int apple_rtkit_helper_pm_register(struct apple_rtkit_helper *helper) +{ + int ret; + + helper->pm_nb.notifier_call = apple_rtkit_helper_pm_notify; + ret = register_pm_notifier(&helper->pm_nb); + if (ret) + return ret; + return devm_add_action_or_reset(helper->dev, apple_rtkit_helper_pm_unregister, helper); +} + static int __maybe_unused apple_rtkit_helper_suspend(struct device *dev) { struct apple_rtkit_helper *helper = dev_get_drvdata(dev); diff --git a/drivers/soc/apple/rtkit.c b/drivers/soc/apple/rtkit.c index 8e324534f8e3e..84ef4ddabc493 100644 --- a/drivers/soc/apple/rtkit.c +++ b/drivers/soc/apple/rtkit.c @@ -2371,10 +2371,11 @@ static int apple_rtkit_sleep_idle(struct apple_rtkit *rtk, void __iomem *cpu_con ret = readl_poll_timeout(cpu_control + APPLE_ASC_CPU_STATUS, status, (status & APPLE_ASC_CPU_STATUS_DOWN) == APPLE_ASC_CPU_STATUS_DOWN, 100, 5000); + /* Both idles took, so it doesn't run on: stop it as if the status had shown it. */ if (ret) - dev_err(rtk->dev, "RTKit: idle, but not stopped: status %#x\n", status); + dev_warn(rtk->dev, "RTKit: idle, but not stopped: status %#x\n", status); - return ret; + return 0; } /* With interrupts off: room in the FIFO, then the message. */ @@ -2521,9 +2522,15 @@ int apple_rtkit_sleep_return(struct apple_rtkit *rtk, void __iomem *cpu_control) * No real sleep: a firmware that idled doesn't come back from that with its power kept, * and stays down until the next sleep restarts it. One whose idle failed still runs. */ - if (!pm_resume_via_firmware()) - return (readl(cpu_control) & APPLE_ASC_CPU_CONTROL_RUN) && - (rtk->iop_power_state & 0xff) == APPLE_RTKIT_PWR_STATE_ON ? 0 : -EAGAIN; + if (!pm_resume_via_firmware()) { + if (!(readl(cpu_control) & APPLE_ASC_CPU_CONTROL_RUN) || + (rtk->iop_power_state & 0xff) != APPLE_RTKIT_PWR_STATE_ON) + return -EAGAIN; + /* Its AP idle took and its IOP idle didn't: endpoints stay shut until AP on. */ + if ((rtk->ap_power_state & 0xff) != APPLE_RTKIT_PWR_STATE_ON) + return apple_rtkit_ap_power(rtk, true); + return 0; + } ret = apple_rtkit_warm_restart(rtk); if (ret) diff --git a/drivers/usb/dwc3/dwc3-apple.c b/drivers/usb/dwc3/dwc3-apple.c index fb4c3b8a926b4..17404410399c9 100644 --- a/drivers/usb/dwc3/dwc3-apple.c +++ b/drivers/usb/dwc3/dwc3-apple.c @@ -187,8 +187,6 @@ struct dwc3_apple { bool sleep_reinit; /* Looks for the fixed hub once a host restarted after a sleep has had time to find it. */ struct delayed_work hub_check; - struct work_struct sleep_finish; - bool sleep_finish_kept; /* The core didn't come back from a sleep, and nothing may touch it until a reboot. */ bool sleep_dead; /* Stopped for a deep sleep: the state to come back to, and the wakeup setting. */ @@ -1350,7 +1348,7 @@ static int dwc3_apple_pm_notify(struct notifier_block *nb, unsigned long action, * check takes the glue's lock, and has nothing to look at until the sleep is over. */ if (action == PM_SUSPEND_PREPARE) { - flush_work(&appledwc->sleep_finish); + apple_atcphy_sleep_finish_flush(appledwc->usb2_phy); cancel_delayed_work_sync(&appledwc->hub_check); } if (action == PM_SUSPEND_PREPARE && !pm_suspend_default_s2idle()) { @@ -1363,24 +1361,18 @@ static int dwc3_apple_pm_notify(struct notifier_block *nb, unsigned long action, scoped_guard(mutex, &appledwc->lock) ret = dwc3_apple_pm_notify_locked(appledwc, action, in_place, &finish, &kept); - /* After a wake, user space doesn't wait for SuperSpeed: it comes back in the background. */ - if (finish && action == PM_POST_SUSPEND) { - WRITE_ONCE(appledwc->sleep_finish_kept, kept); - queue_work(system_unbound_wq, &appledwc->sleep_finish); - } else if (finish) { + /* + * After a wake, user space doesn't wait for SuperSpeed: it comes back in the background, + * from the PHY's work, which an unbind of this device doesn't wait for under its lock. + */ + if (finish && action == PM_POST_SUSPEND) + apple_atcphy_sleep_finish_async(appledwc->usb2_phy, kept); + else if (finish) apple_atcphy_sleep_finish(appledwc->usb2_phy, kept); - } return ret; } -static void dwc3_apple_sleep_finish(struct work_struct *work) -{ - struct dwc3_apple *appledwc = container_of(work, struct dwc3_apple, sleep_finish); - - apple_atcphy_sleep_finish(appledwc->usb2_phy, READ_ONCE(appledwc->sleep_finish_kept)); -} - static void dwc3_apple_pm_notifier_remove(void *data) { struct dwc3_apple *appledwc = data; @@ -1485,9 +1477,6 @@ static int dwc3_apple_probe(struct platform_device *pdev) */ appledwc->state = DWC3_APPLE_PROBE_PENDING; ret = devm_delayed_work_autocancel(dev, &appledwc->hub_check, dwc3_apple_hub_check); - if (ret) - return ret; - ret = devm_work_autocancel(dev, &appledwc->sleep_finish, dwc3_apple_sleep_finish); if (ret) return ret; appledwc->pm_nb.notifier_call = dwc3_apple_pm_notify; diff --git a/include/linux/phy/apple-atc.h b/include/linux/phy/apple-atc.h index 156db66eb7036..bc91b4206310f 100644 --- a/include/linux/phy/apple-atc.h +++ b/include/linux/phy/apple-atc.h @@ -37,6 +37,10 @@ int apple_atcphy_sleep_result(struct phy *usb2_phy); * controller stop for later sleeps. */ void apple_atcphy_sleep_finish(struct phy *usb2_phy, bool kept); + +/* The same from the PHY's work, for after a wake; the flush waits for it. */ +void apple_atcphy_sleep_finish_async(struct phy *usb2_phy, bool kept); +void apple_atcphy_sleep_finish_flush(struct phy *usb2_phy); #else static inline int apple_atcphy_sleep_prepare(struct phy *usb2_phy) { @@ -51,6 +55,14 @@ static inline int apple_atcphy_sleep_result(struct phy *usb2_phy) static inline void apple_atcphy_sleep_finish(struct phy *usb2_phy, bool kept) { } + +static inline void apple_atcphy_sleep_finish_async(struct phy *usb2_phy, bool kept) +{ +} + +static inline void apple_atcphy_sleep_finish_flush(struct phy *usb2_phy) +{ +} #endif #endif diff --git a/include/linux/soc/apple/rtkit.h b/include/linux/soc/apple/rtkit.h index dcd2474b4369f..44dcb386efd76 100644 --- a/include/linux/soc/apple/rtkit.h +++ b/include/linux/soc/apple/rtkit.h @@ -323,7 +323,7 @@ int apple_rtkit_sleep_stop(struct apple_rtkit *rtk, void __iomem *cpu_control); * then sets the AP state. Where it didn't, one that idled gets -EAGAIN: an idled * firmware doesn't wake with its power kept, so it stays down, and comes back the * same way after the next sleep that does reach the hardware. One whose idle - * failed still runs, and gets 0. + * failed still runs, and gets 0 once its AP state is on again. * * @cpu_control: the ASC's CPU control register. */ diff --git a/sound/soc/apple/aop_t8140.rs b/sound/soc/apple/aop_t8140.rs index 929aefbf54f64..751cde0c4eb8e 100644 --- a/sound/soc/apple/aop_t8140.rs +++ b/sound/soc/apple/aop_t8140.rs @@ -1614,6 +1614,9 @@ fn pcm_open_dmaengine( if hw.formats == 0 { return EINVAL.to_errno(); } + // A system sleep stops the services, and only a prepare starts them all again. Offer + // neither resume nor pause: a transfer paused by the suspend would block the next start. + hw.info &= !(bindings::SNDRV_PCM_INFO_PAUSE | bindings::SNDRV_PCM_INFO_RESUME); // SAFETY: the substream's runtime exists for the op's duration; the // dmaengine open attaches `chan` to it on success. unsafe {