Skip to content

Commit 75c8746

Browse files
superm1alexdeucher
authored andcommitted
drm/amd: Create a device link between APU display and XHCI devices
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. 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 <aaron.ma@canonical.com> Reported-by: mrh@frame.work Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221073 Acked-by: Alex Deucher <alexander.deucher@amd.com> Tested-by: Mark Pearson <mpearson-lenovo@squebb.ca> Tested-by: Alexander F <superveridical@gmail.com> Tested-by: Francis DB <francisdb@gmail.com> Link: https://patch.msgid.link/20260713195313.1739762-1-mario.limonciello@amd.com Signed-off-by: Mario Limonciello <mario.limonciello@amd.com> Signed-off-by: Alex Deucher <alexander.deucher@amd.com> (cherry picked from commit 07c93d7) Cc: stable@vger.kernel.org
1 parent 46c3c32 commit 75c8746

3 files changed

Lines changed: 68 additions & 1 deletion

File tree

drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1367,6 +1367,14 @@ static void smu_feature_cap_init(struct smu_context *smu)
13671367
bitmap_zero(fea_cap->cap_map, SMU_FEATURE_CAP_ID__COUNT);
13681368
}
13691369

1370+
static int smu_set_power_dep(struct smu_context *smu, bool enable)
1371+
{
1372+
if (!smu->ppt_funcs->set_power_dep)
1373+
return 0;
1374+
1375+
return smu->ppt_funcs->set_power_dep(smu, enable);
1376+
}
1377+
13701378
static int smu_sw_init(struct amdgpu_ip_block *ip_block)
13711379
{
13721380
struct amdgpu_device *adev = ip_block->adev;
@@ -1428,6 +1436,8 @@ static int smu_sw_init(struct amdgpu_ip_block *ip_block)
14281436
if (!smu->ppt_funcs->get_fan_control_mode)
14291437
smu->adev->pm.no_fan = true;
14301438

1439+
smu_set_power_dep(smu, true);
1440+
14311441
return 0;
14321442
}
14331443

@@ -1450,6 +1460,8 @@ static int smu_sw_fini(struct amdgpu_ip_block *ip_block)
14501460

14511461
smu_fini_microcode(smu);
14521462

1463+
smu_set_power_dep(smu, false);
1464+
14531465
return 0;
14541466
}
14551467

drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -749,6 +749,9 @@ struct smu_context {
749749
bool pm_enabled;
750750
bool is_apu;
751751

752+
/* Power dependency link from an integrated xHCI controller to the GPU */
753+
struct device_link *usb_power_link;
754+
752755
uint32_t smc_driver_if_version;
753756
uint32_t smc_fw_if_version;
754757
uint32_t smc_fw_version;
@@ -1648,12 +1651,19 @@ struct pptable_funcs {
16481651
int (*ras_send_msg)(struct smu_context *smu,
16491652
enum smu_message_type msg, uint32_t param, uint32_t *read_arg);
16501653

1651-
16521654
/**
16531655
* @get_ras_smu_drv: Get RAS smu driver interface
16541656
* Return: ras_smu_drv *
16551657
*/
16561658
int (*get_ras_smu_drv)(struct smu_context *smu, const struct ras_smu_drv **ras_smu_drv);
1659+
1660+
/**
1661+
* @set_power_dep: Create or destroy a power dependency link
1662+
* from an integrated xHCI controller to the GPU so that the GPU is
1663+
* resumed before the USB controller during PM resume. @enable is true
1664+
* to create the link and false to tear it down.
1665+
*/
1666+
int (*set_power_dep)(struct smu_context *smu, bool enable);
16571667
};
16581668

16591669
typedef enum {

drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1701,6 +1701,50 @@ static int smu_v14_0_0_restore_user_od_settings(struct smu_context *smu)
17011701
return 0;
17021702
}
17031703

1704+
/*
1705+
* Link any xHCI controller sharing the GPU's PCIe root port as a consumer
1706+
* of the GPU so the GPU resumes first, avoiding an xHCI resume race.
1707+
*/
1708+
static int smu_v14_0_0_set_power_dep(struct smu_context *smu, bool enable)
1709+
{
1710+
struct amdgpu_device *adev = smu->adev;
1711+
struct pci_dev *gpu_pdev = adev->pdev;
1712+
struct pci_dev *root_port, *usb_pdev = NULL;
1713+
struct device_link *link;
1714+
1715+
if (!enable) {
1716+
if (smu->usb_power_link) {
1717+
device_link_del(smu->usb_power_link);
1718+
smu->usb_power_link = NULL;
1719+
}
1720+
return 0;
1721+
}
1722+
1723+
root_port = pcie_find_root_port(gpu_pdev);
1724+
while ((usb_pdev = pci_get_class(PCI_CLASS_SERIAL_USB_XHCI, usb_pdev))) {
1725+
struct pci_dev *usb_root;
1726+
1727+
usb_root = pcie_find_root_port(usb_pdev);
1728+
if (usb_root != root_port)
1729+
continue;
1730+
1731+
/* Create device link: USB (consumer) depends on GPU (supplier) */
1732+
link = device_link_add(&usb_pdev->dev, &gpu_pdev->dev,
1733+
DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME);
1734+
if (link) {
1735+
smu->usb_power_link = link;
1736+
drm_info(adev_to_drm(adev), "USB controller %s D0 power state depends on %s\n",
1737+
pci_name(usb_pdev), pci_name(gpu_pdev));
1738+
/* Only create one link for the first USB controller found */
1739+
break;
1740+
}
1741+
}
1742+
1743+
pci_dev_put(usb_pdev);
1744+
1745+
return 0;
1746+
}
1747+
17041748
static const struct pptable_funcs smu_v14_0_0_ppt_funcs = {
17051749
.check_fw_status = smu_v14_0_check_fw_status,
17061750
.check_fw_version = smu_cmn_check_fw_version,
@@ -1734,6 +1778,7 @@ static const struct pptable_funcs smu_v14_0_0_ppt_funcs = {
17341778
.dpm_set_umsch_mm_enable = smu_v14_0_0_set_umsch_mm_enable,
17351779
.get_dpm_clock_table = smu_v14_0_common_get_dpm_table,
17361780
.set_mall_enable = smu_v14_0_common_set_mall_enable,
1781+
.set_power_dep = smu_v14_0_0_set_power_dep,
17371782
};
17381783

17391784
static void smu_v14_0_0_init_msg_ctl(struct smu_context *smu)

0 commit comments

Comments
 (0)