[PATCH V2 18/20] accel/amdxdna: Link SR-IOV VFs for power management sequencing

From: David Zhang

Date: Tue Oct 06 2026 - 00:29:13 EST


Add PM device links between Physical Function (PF) supplier and Virtual
Function (VF) consumers via device_link_add() upon SR-IOV enablement.
This ensures the PM core enforces the proper power management sequence:
suspending VFs before the PF, and resuming the PF before VFs. If linking
fails, roll back SR-IOV initialization.

Also add a comment clarifying that pci_disable_sriov() removes VF drivers
before firmware VF contexts are destroyed.

The links are created only when SR-IOV is enabled and are removed
when a VF driver unbinds. Rebinding a VF driver alone is not
supported; to rebind, disable and re-enable SR-IOV by writing 0 and
then the number of VFs to sriov_numvfs, which recreates the VFs and
their links.

Signed-off-by: David Zhang <yidong.zhang@xxxxxxx>
---
drivers/accel/amdxdna/aie4_sriov.c | 73 ++++++++++++++++++++++++++++++
1 file changed, 73 insertions(+)

diff --git a/drivers/accel/amdxdna/aie4_sriov.c b/drivers/accel/amdxdna/aie4_sriov.c
index 0eea28f62676..3cad0526087d 100644
--- a/drivers/accel/amdxdna/aie4_sriov.c
+++ b/drivers/accel/amdxdna/aie4_sriov.c
@@ -56,9 +56,74 @@ int aie4_sriov_stop(struct amdxdna_dev_hdl *ndev)

pci_disable_sriov(pdev);
ndev->num_vfs = 0;
+
+ /*
+ * pci_disable_sriov() removes VF drivers first; call destroy_vfs after
+ * so firmware VF contexts are not cleared before VF drivers finish cleanup.
+ */
return aie4_destroy_vfs(ndev);
}

+static int aie4_for_each_vfs(struct amdxdna_dev *xdna,
+ int (*cb)(struct amdxdna_dev *, struct pci_dev *))
+{
+ struct pci_dev *pdev_pf = to_pci_dev(xdna->ddev.dev);
+ struct pci_dev *pdev_vf;
+ int pos, ret;
+ u16 vf_did;
+
+ pos = pci_find_ext_capability(pdev_pf, PCI_EXT_CAP_ID_SRIOV);
+ if (!pos)
+ return 0;
+ ret = pci_read_config_word(pdev_pf, pos + PCI_SRIOV_VF_DID, &vf_did);
+ if (ret) {
+ XDNA_ERR(xdna, "read VF Device ID failed %d", ret);
+ return -ENODEV;
+ }
+
+ for (pdev_vf = pci_get_device(pdev_pf->vendor, vf_did, NULL);
+ pdev_vf;
+ pdev_vf = pci_get_device(pdev_pf->vendor, vf_did, pdev_vf)) {
+ if (!pdev_vf->is_virtfn || pdev_vf->physfn != pdev_pf)
+ continue;
+
+ ret = cb(xdna, pdev_vf);
+ if (ret) {
+ /* Release reference on early return. */
+ pci_dev_put(pdev_vf);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int aie4_link_vf(struct amdxdna_dev *xdna, struct pci_dev *pdev_vf)
+{
+ struct pci_dev *pdev_pf = to_pci_dev(xdna->ddev.dev);
+ struct device_link *link;
+
+ /*
+ * The link is removed when the VF driver unbinds. To rebind VF
+ * drivers, re-enable SR-IOV via sriov_numvfs (0, then N).
+ */
+ link = device_link_add(&pdev_vf->dev, /* consumer = VF */
+ &pdev_pf->dev, /* supplier = PF */
+ DL_FLAG_PM_RUNTIME | DL_FLAG_AUTOREMOVE_CONSUMER);
+ if (!link) {
+ XDNA_ERR(xdna, "Failed to link VF %s", pci_name(pdev_vf));
+ return -EINVAL;
+ }
+
+ XDNA_DBG(xdna, "Linked VF %s", pci_name(pdev_vf));
+ return 0;
+}
+
+static int aie4_link_vfs(struct amdxdna_dev *xdna)
+{
+ return aie4_for_each_vfs(xdna, aie4_link_vf);
+}
+
static int aie4_sriov_start(struct amdxdna_dev_hdl *ndev, int num_vfs)
{
struct amdxdna_dev *xdna = ndev->aie.xdna;
@@ -76,6 +141,14 @@ static int aie4_sriov_start(struct amdxdna_dev_hdl *ndev, int num_vfs)
return ret;
}

+ ret = aie4_link_vfs(xdna);
+ if (ret) {
+ XDNA_ERR(xdna, "link VFs failed, ret: %d", ret);
+ pci_disable_sriov(pdev);
+ aie4_destroy_vfs(ndev);
+ return ret;
+ }
+
ndev->num_vfs = num_vfs;
return num_vfs;
}
--
2.34.1