Skip to content

Commit

Permalink
accel/ivpu: Add PM support
Browse files Browse the repository at this point in the history
  - Implement cold and warm firmware boot flows
  - Add hang recovery support
  - Add runtime power management support

Co-developed-by: Krystian Pradzynski <krystian.pradzynski@linux.intel.com>
Signed-off-by: Krystian Pradzynski <krystian.pradzynski@linux.intel.com>
Signed-off-by: Jacek Lawrynowicz <jacek.lawrynowicz@linux.intel.com>
Reviewed-by: Jeffrey Hugo <quic_jhugo@quicinc.com>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Link: https://patchwork.freedesktop.org/patch/msgid/20230117092723.60441-8-jacek.lawrynowicz@linux.intel.com
  • Loading branch information
Jacek Lawrynowicz authored and Daniel Vetter committed Jan 19, 2023
1 parent cd72722 commit 852be13
Show file tree
Hide file tree
Showing 10 changed files with 451 additions and 8 deletions.
3 changes: 2 additions & 1 deletion drivers/accel/ivpu/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ intel_vpu-y := \
ivpu_job.o \
ivpu_jsm_msg.o \
ivpu_mmu.o \
ivpu_mmu_context.o
ivpu_mmu_context.o \
ivpu_pm.o

obj-$(CONFIG_DRM_ACCEL_IVPU) += intel_vpu.o
28 changes: 28 additions & 0 deletions drivers/accel/ivpu/ivpu_drv.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "ivpu_jsm_msg.h"
#include "ivpu_mmu.h"
#include "ivpu_mmu_context.h"
#include "ivpu_pm.h"

#ifndef DRIVER_VERSION_STR
#define DRIVER_VERSION_STR __stringify(DRM_IVPU_DRIVER_MAJOR) "." \
Expand Down Expand Up @@ -462,6 +463,10 @@ static int ivpu_dev_init(struct ivpu_device *vdev)
if (!vdev->ipc)
return -ENOMEM;

vdev->pm = drmm_kzalloc(&vdev->drm, sizeof(*vdev->pm), GFP_KERNEL);
if (!vdev->pm)
return -ENOMEM;

vdev->hw->ops = &ivpu_hw_mtl_ops;
vdev->platform = IVPU_PLATFORM_INVALID;
vdev->context_xa_limit.min = IVPU_GLOBAL_CONTEXT_MMU_SSID + 1;
Expand Down Expand Up @@ -521,6 +526,12 @@ static int ivpu_dev_init(struct ivpu_device *vdev)
goto err_fw_fini;
}

ret = ivpu_pm_init(vdev);
if (ret) {
ivpu_err(vdev, "Failed to initialize PM: %d\n", ret);
goto err_ipc_fini;
}

ret = ivpu_job_done_thread_init(vdev);
if (ret) {
ivpu_err(vdev, "Failed to initialize job done thread: %d\n", ret);
Expand All @@ -539,6 +550,8 @@ static int ivpu_dev_init(struct ivpu_device *vdev)
goto err_job_done_thread_fini;
}

ivpu_pm_enable(vdev);

return 0;

err_job_done_thread_fini:
Expand All @@ -559,6 +572,7 @@ static int ivpu_dev_init(struct ivpu_device *vdev)

static void ivpu_dev_fini(struct ivpu_device *vdev)
{
ivpu_pm_disable(vdev);
ivpu_shutdown(vdev);
ivpu_job_done_thread_fini(vdev);
ivpu_ipc_fini(vdev);
Expand Down Expand Up @@ -611,11 +625,25 @@ static void ivpu_remove(struct pci_dev *pdev)
ivpu_dev_fini(vdev);
}

static const struct dev_pm_ops ivpu_drv_pci_pm = {
SET_SYSTEM_SLEEP_PM_OPS(ivpu_pm_suspend_cb, ivpu_pm_resume_cb)
SET_RUNTIME_PM_OPS(ivpu_pm_runtime_suspend_cb, ivpu_pm_runtime_resume_cb, NULL)
};

static const struct pci_error_handlers ivpu_drv_pci_err = {
.reset_prepare = ivpu_pm_reset_prepare_cb,
.reset_done = ivpu_pm_reset_done_cb,
};

static struct pci_driver ivpu_pci_driver = {
.name = KBUILD_MODNAME,
.id_table = ivpu_pci_ids,
.probe = ivpu_probe,
.remove = ivpu_remove,
.driver = {
.pm = &ivpu_drv_pci_pm,
},
.err_handler = &ivpu_drv_pci_err,
};

module_pci_driver(ivpu_pci_driver);
Expand Down
2 changes: 2 additions & 0 deletions drivers/accel/ivpu/ivpu_drv.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ struct ivpu_hw_info;
struct ivpu_mmu_info;
struct ivpu_fw_info;
struct ivpu_ipc_info;
struct ivpu_pm_info;

struct ivpu_device {
struct drm_device drm;
Expand All @@ -89,6 +90,7 @@ struct ivpu_device {
struct ivpu_mmu_info *mmu;
struct ivpu_fw_info *fw;
struct ivpu_ipc_info *ipc;
struct ivpu_pm_info *pm;

struct ivpu_mmu_context gctx;
struct xarray context_xa;
Expand Down
4 changes: 4 additions & 0 deletions drivers/accel/ivpu/ivpu_fw.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "ivpu_gem.h"
#include "ivpu_hw.h"
#include "ivpu_ipc.h"
#include "ivpu_pm.h"

#define FW_GLOBAL_MEM_START (2ull * SZ_1G)
#define FW_GLOBAL_MEM_END (3ull * SZ_1G)
Expand Down Expand Up @@ -361,9 +362,12 @@ void ivpu_fw_boot_params_setup(struct ivpu_device *vdev, struct vpu_boot_params
/* In case of warm boot we only have to reset the entrypoint addr */
if (!ivpu_fw_is_cold_boot(vdev)) {
boot_params->save_restore_ret_address = 0;
vdev->pm->is_warmboot = true;
return;
}

vdev->pm->is_warmboot = false;

boot_params->magic = VPU_BOOT_PARAMS_MAGIC;
boot_params->vpu_id = to_pci_dev(vdev->drm.dev)->bus->number;
boot_params->frequency = ivpu_hw_reg_pll_freq_get(vdev);
Expand Down
12 changes: 12 additions & 0 deletions drivers/accel/ivpu/ivpu_hw_mtl.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "ivpu_hw.h"
#include "ivpu_ipc.h"
#include "ivpu_mmu.h"
#include "ivpu_pm.h"

#define TILE_FUSE_ENABLE_BOTH 0x0
#define TILE_FUSE_ENABLE_UPPER 0x1
Expand Down Expand Up @@ -921,18 +922,23 @@ static void ivpu_hw_mtl_irq_disable(struct ivpu_device *vdev)
static void ivpu_hw_mtl_irq_wdt_nce_handler(struct ivpu_device *vdev)
{
ivpu_err_ratelimited(vdev, "WDT NCE irq\n");

ivpu_pm_schedule_recovery(vdev);
}

static void ivpu_hw_mtl_irq_wdt_mss_handler(struct ivpu_device *vdev)
{
ivpu_err_ratelimited(vdev, "WDT MSS irq\n");

ivpu_hw_wdt_disable(vdev);
ivpu_pm_schedule_recovery(vdev);
}

static void ivpu_hw_mtl_irq_noc_firewall_handler(struct ivpu_device *vdev)
{
ivpu_err_ratelimited(vdev, "NOC Firewall irq\n");

ivpu_pm_schedule_recovery(vdev);
}

/* Handler for IRQs from VPU core (irqV) */
Expand Down Expand Up @@ -970,6 +976,7 @@ static u32 ivpu_hw_mtl_irqv_handler(struct ivpu_device *vdev, int irq)
static u32 ivpu_hw_mtl_irqb_handler(struct ivpu_device *vdev, int irq)
{
u32 status = REGB_RD32(MTL_BUTTRESS_INTERRUPT_STAT) & BUTTRESS_IRQ_MASK;
bool schedule_recovery = false;

if (status == 0)
return 0;
Expand All @@ -983,6 +990,7 @@ static u32 ivpu_hw_mtl_irqb_handler(struct ivpu_device *vdev, int irq)
if (REG_TEST_FLD(MTL_BUTTRESS_INTERRUPT_STAT, ATS_ERR, status)) {
ivpu_err(vdev, "ATS_ERR irq 0x%016llx", REGB_RD64(MTL_BUTTRESS_ATS_ERR_LOG_0));
REGB_WR32(MTL_BUTTRESS_ATS_ERR_CLEAR, 0x1);
schedule_recovery = true;
}

if (REG_TEST_FLD(MTL_BUTTRESS_INTERRUPT_STAT, UFI_ERR, status)) {
Expand All @@ -993,6 +1001,7 @@ static u32 ivpu_hw_mtl_irqb_handler(struct ivpu_device *vdev, int irq)
REG_GET_FLD(MTL_BUTTRESS_UFI_ERR_LOG, AXI_ID, ufi_log),
REG_GET_FLD(MTL_BUTTRESS_UFI_ERR_LOG, CQ_ID, ufi_log));
REGB_WR32(MTL_BUTTRESS_UFI_ERR_CLEAR, 0x1);
schedule_recovery = true;
}

/*
Expand All @@ -1005,6 +1014,9 @@ static u32 ivpu_hw_mtl_irqb_handler(struct ivpu_device *vdev, int irq)
/* Re-enable global interrupt */
REGB_WR32(MTL_BUTTRESS_GLOBAL_INT_MASK, 0x0);

if (schedule_recovery)
ivpu_pm_schedule_recovery(vdev);

return status;
}

Expand Down
20 changes: 15 additions & 5 deletions drivers/accel/ivpu/ivpu_ipc.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "ivpu_hw_reg_io.h"
#include "ivpu_ipc.h"
#include "ivpu_jsm_msg.h"
#include "ivpu_pm.h"

#define IPC_MAX_RX_MSG 128
#define IS_KTHREAD() (get_current()->flags & PF_KTHREAD)
Expand Down Expand Up @@ -294,18 +295,27 @@ int ivpu_ipc_send_receive(struct ivpu_device *vdev, struct vpu_jsm_msg *req,
{
struct vpu_jsm_msg hb_req = { .type = VPU_JSM_MSG_QUERY_ENGINE_HB };
struct vpu_jsm_msg hb_resp;
int ret;
int ret, hb_ret;

ret = ivpu_rpm_get(vdev);
if (ret < 0)
return ret;

ret = ivpu_ipc_send_receive_internal(vdev, req, expected_resp_type, resp,
channel, timeout_ms);
if (ret != -ETIMEDOUT)
return ret;
goto rpm_put;

ret = ivpu_ipc_send_receive_internal(vdev, &hb_req, VPU_JSM_MSG_QUERY_ENGINE_HB_DONE,
&hb_resp, VPU_IPC_CHAN_ASYNC_CMD, vdev->timeout.jsm);
if (ret == -ETIMEDOUT)
hb_ret = ivpu_ipc_send_receive_internal(vdev, &hb_req, VPU_JSM_MSG_QUERY_ENGINE_HB_DONE,
&hb_resp, VPU_IPC_CHAN_ASYNC_CMD,
vdev->timeout.jsm);
if (hb_ret == -ETIMEDOUT) {
ivpu_hw_diagnose_failure(vdev);
ivpu_pm_schedule_recovery(vdev);
}

rpm_put:
ivpu_rpm_put(vdev);
return ret;
}

Expand Down
14 changes: 13 additions & 1 deletion drivers/accel/ivpu/ivpu_job.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "ivpu_ipc.h"
#include "ivpu_job.h"
#include "ivpu_jsm_msg.h"
#include "ivpu_pm.h"

#define CMD_BUF_IDX 0
#define JOB_ID_JOB_MASK GENMASK(7, 0)
Expand Down Expand Up @@ -270,6 +271,9 @@ static void job_release(struct kref *ref)

ivpu_dbg(vdev, KREF, "Job released: id %u\n", job->job_id);
kfree(job);

/* Allow the VPU to get suspended, must be called after ivpu_file_priv_put() */
ivpu_rpm_put(vdev);
}

static void job_put(struct ivpu_job *job)
Expand All @@ -286,11 +290,16 @@ ivpu_create_job(struct ivpu_file_priv *file_priv, u32 engine_idx, u32 bo_count)
struct ivpu_device *vdev = file_priv->vdev;
struct ivpu_job *job;
size_t buf_size;
int ret;

ret = ivpu_rpm_get(vdev);
if (ret < 0)
return NULL;

buf_size = sizeof(*job) + bo_count * sizeof(struct ivpu_bo *);
job = kzalloc(buf_size, GFP_KERNEL);
if (!job)
return NULL;
goto err_rpm_put;

kref_init(&job->ref);

Expand All @@ -311,6 +320,8 @@ ivpu_create_job(struct ivpu_file_priv *file_priv, u32 engine_idx, u32 bo_count)

err_free_job:
kfree(job);
err_rpm_put:
ivpu_rpm_put(vdev);
return NULL;
}

Expand Down Expand Up @@ -565,6 +576,7 @@ static int ivpu_job_done_thread(void *arg)
if (jobs_submitted && !xa_empty(&vdev->submitted_jobs_xa)) {
ivpu_err(vdev, "TDR detected, timeout %d ms", timeout);
ivpu_hw_diagnose_failure(vdev);
ivpu_pm_schedule_recovery(vdev);
}
}
}
Expand Down
9 changes: 8 additions & 1 deletion drivers/accel/ivpu/ivpu_mmu.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "ivpu_hw_reg_io.h"
#include "ivpu_mmu.h"
#include "ivpu_mmu_context.h"
#include "ivpu_pm.h"

#define IVPU_MMU_IDR0_REF 0x080f3e0f
#define IVPU_MMU_IDR0_REF_SIMICS 0x080f3e1f
Expand Down Expand Up @@ -814,6 +815,7 @@ static u32 *ivpu_mmu_get_event(struct ivpu_device *vdev)

void ivpu_mmu_irq_evtq_handler(struct ivpu_device *vdev)
{
bool schedule_recovery = false;
u32 *event;
u32 ssid;

Expand All @@ -823,9 +825,14 @@ void ivpu_mmu_irq_evtq_handler(struct ivpu_device *vdev)
ivpu_mmu_dump_event(vdev, event);

ssid = FIELD_GET(IVPU_MMU_EVT_SSID_MASK, event[0]);
if (ssid != IVPU_GLOBAL_CONTEXT_MMU_SSID)
if (ssid == IVPU_GLOBAL_CONTEXT_MMU_SSID)
schedule_recovery = true;
else
ivpu_mmu_user_context_mark_invalid(vdev, ssid);
}

if (schedule_recovery)
ivpu_pm_schedule_recovery(vdev);
}

void ivpu_mmu_irq_gerr_handler(struct ivpu_device *vdev)
Expand Down
Loading

0 comments on commit 852be13

Please sign in to comment.