Thanks Mario On Mon, Jul 13, 2026, at 3:53 PM, Mario Limonciello wrote: > Some AMD APU multi-function devices expose an integrated USB xHCI > controller. In some circumstances (such as larger VRAM), the PM core > can resume can fail when the xHCI controller is resuming in parallel > with the GPU/display function.
Minor: extra 'can' in there (before 'resume'). > > On affected systems, the xHCI controller can complete pci_pm_resume > and start resuming USB devices while the GPU is still in its much > longer resume path. This race condition leads to USB device resume > failures followed by: > > xhci_hcd ...: xHCI host not responding to stop endpoint command > xhci_hcd ...: HC died; cleaning up > > Create a device link from any xHCI controller sharing the same PCIe > root port as the APU display function. The link uses DL_FLAG_STATELESS > and DL_FLAG_PM_RUNTIME to ensure the GPU completes its resume before > the xHCI controller begins resuming USB devices. > > This device link is done specifically in amdgpu so that if the > platform firmware has been modified such that this issue doesn't happen > the version can be detected and the workaround skipped. > > Suggested-by: Aaron Ma <[email protected]> > Reported-by: [email protected] > Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221073 > Signed-off-by: Mario Limonciello <[email protected]> > --- > drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c | 12 +++++ > drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h | 11 +++++ > .../drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c | 45 +++++++++++++++++++ > 3 files changed, 68 insertions(+) > > diff --git a/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c > b/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c > index 4314dff4ac996..c1ceec7a1986b 100644 > --- a/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c > +++ b/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c > @@ -1366,6 +1366,14 @@ static void smu_feature_cap_init(struct > smu_context *smu) > bitmap_zero(fea_cap->cap_map, SMU_FEATURE_CAP_ID__COUNT); > } > > +static int smu_set_power_dep(struct smu_context *smu, bool enable) > +{ > + if (!smu->ppt_funcs->set_power_dep) > + return 0; > + > + return smu->ppt_funcs->set_power_dep(smu, enable); > +} > + > static int smu_sw_init(struct amdgpu_ip_block *ip_block) > { > struct amdgpu_device *adev = ip_block->adev; > @@ -1427,6 +1435,8 @@ static int smu_sw_init(struct amdgpu_ip_block *ip_block) > if (!smu->ppt_funcs->get_fan_control_mode) > smu->adev->pm.no_fan = true; > > + smu_set_power_dep(smu, true); > + > return 0; > } > > @@ -1449,6 +1459,8 @@ static int smu_sw_fini(struct amdgpu_ip_block *ip_block) > > smu_fini_microcode(smu); > > + smu_set_power_dep(smu, false); > + > return 0; > } > > diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h > b/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h > index f8fd93999617d..7ea7c4a5279be 100644 > --- a/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h > +++ b/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h > @@ -749,6 +749,9 @@ struct smu_context { > bool pm_enabled; > bool is_apu; > > + /* Power dependency link from an integrated xHCI controller to the GPU > */ > + struct device_link *usb_power_link; > + > uint32_t smc_driver_if_version; > uint32_t smc_fw_if_version; > uint32_t smc_fw_version; > @@ -1618,6 +1621,14 @@ struct pptable_funcs { > */ > int (*ras_send_msg)(struct smu_context *smu, > enum smu_message_type msg, uint32_t param, uint32_t > *read_arg); > + > + /** > + * @set_power_dep: Create or destroy a power dependency link > + * from an integrated xHCI controller to the GPU so that the GPU is > + * resumed before the USB controller during PM resume. @enable is true > + * to create the link and false to tear it down. > + */ > + int (*set_power_dep)(struct smu_context *smu, bool enable); > }; > > typedef enum { > diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c > b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c > index 2fe006de927a3..2abdfef8644e0 100644 > --- a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c > +++ b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c > @@ -1701,6 +1701,50 @@ static int > smu_v14_0_0_restore_user_od_settings(struct smu_context *smu) > return 0; > } > > +/* > + * Link any xHCI controller sharing the GPU's PCIe root port as a > consumer > + * of the GPU so the GPU resumes first, avoiding an xHCI resume race. > + */ > +static int smu_v14_0_0_set_power_dep(struct smu_context *smu, bool > enable) > +{ > + struct amdgpu_device *adev = smu->adev; > + struct pci_dev *gpu_pdev = adev->pdev; > + struct pci_dev *root_port, *usb_pdev = NULL; > + struct device_link *link; > + > + if (!enable) { > + if (smu->usb_power_link) { > + device_link_del(smu->usb_power_link); > + smu->usb_power_link = NULL; > + } > + return 0; > + } > + > + root_port = pcie_find_root_port(gpu_pdev); > + while ((usb_pdev = pci_get_class(PCI_CLASS_SERIAL_USB_XHCI, > usb_pdev))) { > + struct pci_dev *usb_root; > + > + usb_root = pcie_find_root_port(usb_pdev); > + if (usb_root != root_port) > + continue; > + > + /* Create device link: USB (consumer) depends on GPU (supplier) > */ > + link = device_link_add(&usb_pdev->dev, &gpu_pdev->dev, > + DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME); > + if (link) { > + smu->usb_power_link = link; > + drm_info(adev_to_drm(adev), "USB controller %s D0 power > state > depends on %s\n", > + pci_name(usb_pdev), pci_name(gpu_pdev)); > + /* Only create one link for the first USB controller > found */ > + break; > + } > + } > + > + pci_dev_put(usb_pdev); > + > + return 0; > +} > + > static const struct pptable_funcs smu_v14_0_0_ppt_funcs = { > .check_fw_status = smu_v14_0_check_fw_status, > .check_fw_version = smu_cmn_check_fw_version, > @@ -1734,6 +1778,7 @@ static const struct pptable_funcs > smu_v14_0_0_ppt_funcs = { > .dpm_set_umsch_mm_enable = smu_v14_0_0_set_umsch_mm_enable, > .get_dpm_clock_table = smu_v14_0_common_get_dpm_table, > .set_mall_enable = smu_v14_0_common_set_mall_enable, > + .set_power_dep = smu_v14_0_0_set_power_dep, > }; > > static void smu_v14_0_0_init_msg_ctl(struct smu_context *smu) > -- > 2.43.0 Tested on my T14 G7 AMD platform and confirmed it fixes the issue (which I am able to easily reproduce otherwise). Tested-by: Mark Pearson <[email protected]> Mark
