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

Reply via email to