Implement PCI power management suspend and resume callbacks.

Co-developed-by: Narendra Gutta <venkatanarendrakumar.gu...@amd.com>
Signed-off-by: Narendra Gutta <venkatanarendrakumar.gu...@amd.com>
Co-developed-by: Xiaoming Ren <xiaoming....@amd.com>
Signed-off-by: Xiaoming Ren <xiaoming....@amd.com>
Co-developed-by: Min Ma <min...@amd.com>
Signed-off-by: Min Ma <min...@amd.com>
Signed-off-by: Lizhi Hou <lizhi....@amd.com>
---
 drivers/accel/amdxdna/aie2_ctx.c        |  30 +++++++
 drivers/accel/amdxdna/aie2_pci.c        |   4 +
 drivers/accel/amdxdna/aie2_pci.h        |   2 +
 drivers/accel/amdxdna/amdxdna_ctx.c     |  26 ++++++
 drivers/accel/amdxdna/amdxdna_ctx.h     |   2 +
 drivers/accel/amdxdna/amdxdna_drm.c     |  17 +++-
 drivers/accel/amdxdna/amdxdna_drm.h     |   5 ++
 drivers/accel/amdxdna/amdxdna_pci_drv.c | 103 +++++++++++++++++++++++-
 8 files changed, 186 insertions(+), 3 deletions(-)

diff --git a/drivers/accel/amdxdna/aie2_ctx.c b/drivers/accel/amdxdna/aie2_ctx.c
index dbbc71c40f5f..1dbb89d28004 100644
--- a/drivers/accel/amdxdna/aie2_ctx.c
+++ b/drivers/accel/amdxdna/aie2_ctx.c
@@ -168,6 +168,36 @@ static int aie2_hwctx_wait_for_idle(struct amdxdna_hwctx 
*hwctx)
        return 0;
 }
 
+void aie2_hwctx_suspend(struct amdxdna_hwctx *hwctx)
+{
+       struct amdxdna_dev *xdna = hwctx->client->xdna;
+
+       /*
+        * Command timeout is unlikely. But if it happens, it doesn't
+        * break the system. aie2_hwctx_stop() will destroy mailbox
+        * and abort all commands.
+        */
+       drm_WARN_ON(&xdna->ddev, !mutex_is_locked(&xdna->dev_lock));
+       aie2_hwctx_wait_for_idle(hwctx);
+       aie2_hwctx_stop(xdna, hwctx, NULL);
+       hwctx->old_status = hwctx->status;
+       hwctx->status = HWCTX_STAT_STOP;
+}
+
+void aie2_hwctx_resume(struct amdxdna_hwctx *hwctx)
+{
+       struct amdxdna_dev *xdna = hwctx->client->xdna;
+
+       /*
+        * The resume path cannot guarantee that mailbox channel can be
+        * regenerated. If this happen, when submit message to this
+        * mailbox channel, error will return.
+        */
+       drm_WARN_ON(&xdna->ddev, !mutex_is_locked(&xdna->dev_lock));
+       hwctx->status = hwctx->old_status;
+       aie2_hwctx_restart(xdna, hwctx);
+}
+
 static void
 aie2_sched_notify(struct amdxdna_sched_job *job)
 {
diff --git a/drivers/accel/amdxdna/aie2_pci.c b/drivers/accel/amdxdna/aie2_pci.c
index 42f6461cd2ec..e52428d2c092 100644
--- a/drivers/accel/amdxdna/aie2_pci.c
+++ b/drivers/accel/amdxdna/aie2_pci.c
@@ -490,10 +490,14 @@ static void aie2_fini(struct amdxdna_dev *xdna)
 const struct amdxdna_dev_ops aie2_ops = {
        .init           = aie2_init,
        .fini           = aie2_fini,
+       .resume         = aie2_hw_start,
+       .suspend        = aie2_hw_stop,
        .hwctx_init     = aie2_hwctx_init,
        .hwctx_fini     = aie2_hwctx_fini,
        .hwctx_config   = aie2_hwctx_config,
        .cmd_submit     = aie2_cmd_submit,
        .cmd_wait       = aie2_cmd_wait,
        .hmm_invalidate = aie2_hmm_invalidate,
+       .hwctx_suspend  = aie2_hwctx_suspend,
+       .hwctx_resume   = aie2_hwctx_resume,
 };
diff --git a/drivers/accel/amdxdna/aie2_pci.h b/drivers/accel/amdxdna/aie2_pci.h
index 26d271a1040b..113395ab5e33 100644
--- a/drivers/accel/amdxdna/aie2_pci.h
+++ b/drivers/accel/amdxdna/aie2_pci.h
@@ -243,6 +243,8 @@ int aie2_sync_bo(struct amdxdna_hwctx *hwctx, struct 
amdxdna_sched_job *job,
 int aie2_hwctx_init(struct amdxdna_hwctx *hwctx);
 void aie2_hwctx_fini(struct amdxdna_hwctx *hwctx);
 int aie2_hwctx_config(struct amdxdna_hwctx *hwctx, u32 type, u64 value, void 
*buf, u32 size);
+void aie2_hwctx_suspend(struct amdxdna_hwctx *hwctx);
+void aie2_hwctx_resume(struct amdxdna_hwctx *hwctx);
 int aie2_cmd_submit(struct amdxdna_hwctx *hwctx, struct amdxdna_sched_job 
*job, u64 *seq);
 int aie2_cmd_wait(struct amdxdna_hwctx *hwctx, u64 seq, u32 timeout);
 void aie2_hmm_invalidate(struct amdxdna_gem_obj *abo, unsigned long cur_seq);
diff --git a/drivers/accel/amdxdna/amdxdna_ctx.c 
b/drivers/accel/amdxdna/amdxdna_ctx.c
index 1bf7bd5a10d7..35f84f8884a7 100644
--- a/drivers/accel/amdxdna/amdxdna_ctx.c
+++ b/drivers/accel/amdxdna/amdxdna_ctx.c
@@ -48,6 +48,32 @@ static struct dma_fence *amdxdna_fence_create(struct 
amdxdna_hwctx *hwctx)
        return &fence->base;
 }
 
+void amdxdna_hwctx_suspend(struct amdxdna_client *client)
+{
+       struct amdxdna_dev *xdna = client->xdna;
+       struct amdxdna_hwctx *hwctx;
+       int next = 0;
+
+       drm_WARN_ON(&xdna->ddev, !mutex_is_locked(&xdna->dev_lock));
+       mutex_lock(&client->hwctx_lock);
+       idr_for_each_entry_continue(&client->hwctx_idr, hwctx, next)
+               xdna->dev_info->ops->hwctx_suspend(hwctx);
+       mutex_unlock(&client->hwctx_lock);
+}
+
+void amdxdna_hwctx_resume(struct amdxdna_client *client)
+{
+       struct amdxdna_dev *xdna = client->xdna;
+       struct amdxdna_hwctx *hwctx;
+       int next = 0;
+
+       drm_WARN_ON(&xdna->ddev, !mutex_is_locked(&xdna->dev_lock));
+       mutex_lock(&client->hwctx_lock);
+       idr_for_each_entry_continue(&client->hwctx_idr, hwctx, next)
+               xdna->dev_info->ops->hwctx_resume(hwctx);
+       mutex_unlock(&client->hwctx_lock);
+}
+
 static void amdxdna_hwctx_destroy_rcu(struct amdxdna_hwctx *hwctx,
                                      struct srcu_struct *ss)
 {
diff --git a/drivers/accel/amdxdna/amdxdna_ctx.h 
b/drivers/accel/amdxdna/amdxdna_ctx.h
index bd86ec137857..e8aa2ed9f392 100644
--- a/drivers/accel/amdxdna/amdxdna_ctx.h
+++ b/drivers/accel/amdxdna/amdxdna_ctx.h
@@ -145,6 +145,8 @@ static inline u32 amdxdna_hwctx_col_map(struct 
amdxdna_hwctx *hwctx)
 void amdxdna_job_put(struct amdxdna_sched_job *job);
 
 void amdxdna_hwctx_remove_all(struct amdxdna_client *client);
+void amdxdna_hwctx_suspend(struct amdxdna_client *client);
+void amdxdna_hwctx_resume(struct amdxdna_client *client);
 
 int amdxdna_cmd_submit(struct amdxdna_client *client,
                       u32 cmd_bo_hdls, u32 *arg_bo_hdls, u32 arg_bo_cnt,
diff --git a/drivers/accel/amdxdna/amdxdna_drm.c 
b/drivers/accel/amdxdna/amdxdna_drm.c
index 3b15eae65e6d..a4c189b4953e 100644
--- a/drivers/accel/amdxdna/amdxdna_drm.c
+++ b/drivers/accel/amdxdna/amdxdna_drm.c
@@ -15,9 +15,17 @@ static int amdxdna_drm_open(struct drm_device *ddev, struct 
drm_file *filp)
        struct amdxdna_client *client;
        int ret;
 
+       ret = pm_runtime_resume_and_get(ddev->dev);
+       if (ret) {
+               XDNA_ERR(xdna, "Failed to get rpm, ret %d", ret);
+               return ret;
+       }
+
        client = kzalloc(sizeof(*client), GFP_KERNEL);
-       if (!client)
-               return -ENOMEM;
+       if (!client) {
+               ret = -ENOMEM;
+               goto put_rpm;
+       }
 
        client->pid = pid_nr(filp->pid);
        client->xdna = xdna;
@@ -53,6 +61,9 @@ static int amdxdna_drm_open(struct drm_device *ddev, struct 
drm_file *filp)
        iommu_sva_unbind_device(client->sva);
 failed:
        kfree(client);
+put_rpm:
+       pm_runtime_mark_last_busy(ddev->dev);
+       pm_runtime_put_autosuspend(ddev->dev);
 
        return ret;
 }
@@ -75,6 +86,8 @@ static void amdxdna_drm_close(struct drm_device *ddev, struct 
drm_file *filp)
 
        XDNA_DBG(xdna, "pid %d closed", client->pid);
        kfree(client);
+       pm_runtime_mark_last_busy(ddev->dev);
+       pm_runtime_put_autosuspend(ddev->dev);
 }
 
 static int amdxdna_flush(struct file *f, fl_owner_t id)
diff --git a/drivers/accel/amdxdna/amdxdna_drm.h 
b/drivers/accel/amdxdna/amdxdna_drm.h
index 6f1eaed46382..79eb9b4f6574 100644
--- a/drivers/accel/amdxdna/amdxdna_drm.h
+++ b/drivers/accel/amdxdna/amdxdna_drm.h
@@ -14,6 +14,7 @@
 #include <drm/drm_print.h>
 #include <drm/drm_file.h>
 #include <linux/hmm.h>
+#include <linux/pm_runtime.h>
 #include <linux/vmalloc.h>
 
 #include "amdxdna_ctx.h"
@@ -38,10 +39,14 @@ struct amdxdna_dev;
 struct amdxdna_dev_ops {
        int (*init)(struct amdxdna_dev *xdna);
        void (*fini)(struct amdxdna_dev *xdna);
+       int (*resume)(struct amdxdna_dev *xdna);
+       void (*suspend)(struct amdxdna_dev *xdna);
        int (*hwctx_init)(struct amdxdna_hwctx *hwctx);
        void (*hwctx_fini)(struct amdxdna_hwctx *hwctx);
        int (*hwctx_config)(struct amdxdna_hwctx *hwctx, u32 type, u64 value, 
void *buf, u32 size);
        void (*hmm_invalidate)(struct amdxdna_gem_obj *abo, unsigned long 
cur_seq);
+       void (*hwctx_suspend)(struct amdxdna_hwctx *hwctx);
+       void (*hwctx_resume)(struct amdxdna_hwctx *hwctx);
        int (*cmd_submit)(struct amdxdna_hwctx *hwctx, struct amdxdna_sched_job 
*job, u64 *seq);
        int (*cmd_wait)(struct amdxdna_hwctx *hwctx, u64 seq, u32 timeout);
 };
diff --git a/drivers/accel/amdxdna/amdxdna_pci_drv.c 
b/drivers/accel/amdxdna/amdxdna_pci_drv.c
index 924ee8b96ebb..b472c4d7342a 100644
--- a/drivers/accel/amdxdna/amdxdna_pci_drv.c
+++ b/drivers/accel/amdxdna/amdxdna_pci_drv.c
@@ -7,6 +7,8 @@
 
 #include "amdxdna_pci_drv.h"
 
+#define AMDXDNA_AUTOSUSPEND_DELAY      5000 /* miliseconds */
+
 /*
  *  There are platforms which share the same PCI device ID
  *  but have different PCI revision IDs. So, let the PCI class
@@ -46,10 +48,11 @@ amdxdna_get_dev_info(struct pci_dev *pdev)
 
 static int amdxdna_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 {
+       struct device *dev = &pdev->dev;
        struct amdxdna_dev *xdna;
        int ret;
 
-       xdna = devm_drm_dev_alloc(&pdev->dev, &amdxdna_drm_drv, typeof(*xdna), 
ddev);
+       xdna = devm_drm_dev_alloc(dev, &amdxdna_drm_drv, typeof(*xdna), ddev);
        if (IS_ERR(xdna))
                return PTR_ERR(xdna);
 
@@ -75,12 +78,19 @@ static int amdxdna_probe(struct pci_dev *pdev, const struct 
pci_device_id *id)
                goto failed_dev_fini;
        }
 
+       pm_runtime_set_autosuspend_delay(dev, AMDXDNA_AUTOSUSPEND_DELAY);
+       pm_runtime_use_autosuspend(dev);
+       pm_runtime_allow(dev);
+
        ret = drm_dev_register(&xdna->ddev, 0);
        if (ret) {
                XDNA_ERR(xdna, "DRM register failed, ret %d", ret);
+               pm_runtime_forbid(dev);
                goto failed_sysfs_fini;
        }
 
+       pm_runtime_mark_last_busy(dev);
+       pm_runtime_put_autosuspend(dev);
        return 0;
 
 failed_sysfs_fini:
@@ -95,8 +105,12 @@ static int amdxdna_probe(struct pci_dev *pdev, const struct 
pci_device_id *id)
 static void amdxdna_remove(struct pci_dev *pdev)
 {
        struct amdxdna_dev *xdna = pci_get_drvdata(pdev);
+       struct device *dev = &pdev->dev;
        struct amdxdna_client *client;
 
+       pm_runtime_get_noresume(dev);
+       pm_runtime_forbid(dev);
+
        drm_dev_unplug(&xdna->ddev);
        amdxdna_sysfs_fini(xdna);
 
@@ -118,11 +132,98 @@ static void amdxdna_remove(struct pci_dev *pdev)
        mutex_unlock(&xdna->dev_lock);
 }
 
+static int amdxdna_dev_suspend_nolock(struct amdxdna_dev *xdna)
+{
+       if (xdna->dev_info->ops->suspend)
+               xdna->dev_info->ops->suspend(xdna);
+
+       return 0;
+}
+
+static int amdxdna_dev_resume_nolock(struct amdxdna_dev *xdna)
+{
+       if (xdna->dev_info->ops->resume)
+               return xdna->dev_info->ops->resume(xdna);
+
+       return 0;
+}
+
+static int amdxdna_pmops_suspend(struct device *dev)
+{
+       struct amdxdna_dev *xdna = pci_get_drvdata(to_pci_dev(dev));
+       struct amdxdna_client *client;
+
+       mutex_lock(&xdna->dev_lock);
+       list_for_each_entry(client, &xdna->client_list, node)
+               amdxdna_hwctx_suspend(client);
+
+       amdxdna_dev_suspend_nolock(xdna);
+       mutex_unlock(&xdna->dev_lock);
+
+       return 0;
+}
+
+static int amdxdna_pmops_resume(struct device *dev)
+{
+       struct amdxdna_dev *xdna = pci_get_drvdata(to_pci_dev(dev));
+       struct amdxdna_client *client;
+       int ret;
+
+       XDNA_INFO(xdna, "firmware resuming...");
+       mutex_lock(&xdna->dev_lock);
+       ret = amdxdna_dev_resume_nolock(xdna);
+       if (ret) {
+               XDNA_ERR(xdna, "resume NPU firmware failed");
+               mutex_unlock(&xdna->dev_lock);
+               return ret;
+       }
+
+       XDNA_INFO(xdna, "hardware context resuming...");
+       list_for_each_entry(client, &xdna->client_list, node)
+               amdxdna_hwctx_resume(client);
+       mutex_unlock(&xdna->dev_lock);
+
+       return 0;
+}
+
+static int amdxdna_rpmops_suspend(struct device *dev)
+{
+       struct amdxdna_dev *xdna = pci_get_drvdata(to_pci_dev(dev));
+       int ret;
+
+       mutex_lock(&xdna->dev_lock);
+       WARN_ON(!list_empty(&xdna->client_list));
+       ret = amdxdna_dev_suspend_nolock(xdna);
+       mutex_unlock(&xdna->dev_lock);
+
+       XDNA_DBG(xdna, "Runtime suspend done ret: %d", ret);
+       return ret;
+}
+
+static int amdxdna_rpmops_resume(struct device *dev)
+{
+       struct amdxdna_dev *xdna = pci_get_drvdata(to_pci_dev(dev));
+       int ret;
+
+       mutex_lock(&xdna->dev_lock);
+       ret = amdxdna_dev_resume_nolock(xdna);
+       mutex_unlock(&xdna->dev_lock);
+
+       XDNA_DBG(xdna, "Runtime resume done ret: %d", ret);
+       return ret;
+}
+
+static const struct dev_pm_ops amdxdna_pm_ops = {
+       SET_SYSTEM_SLEEP_PM_OPS(amdxdna_pmops_suspend, amdxdna_pmops_resume)
+       SET_RUNTIME_PM_OPS(amdxdna_rpmops_suspend, amdxdna_rpmops_resume, NULL)
+};
+
 static struct pci_driver amdxdna_pci_driver = {
        .name = KBUILD_MODNAME,
        .id_table = pci_ids,
        .probe = amdxdna_probe,
        .remove = amdxdna_remove,
+       .driver.pm = &amdxdna_pm_ops,
 };
 
 module_pci_driver(amdxdna_pci_driver);
-- 
2.34.1

Reply via email to