---
Changes in v2:
- Commit message only, no code changes
- Clarify that all known firmware versions setting
preemption_buffer_1_max_size also set preemption_buffer_2_max_size,
so selecting each buffer size separately is not needed
- Clarify that the image header sizes are deprecated in favor of the
boot params size, but are still valid so older kernels keep working
drivers/accel/ivpu/ivpu_drv.c | 11 +++-
drivers/accel/ivpu/ivpu_drv.h | 2 +-
drivers/accel/ivpu/ivpu_fw.c | 91 +++++++++++++++++++++++--------
drivers/accel/ivpu/ivpu_fw.h | 1 +
drivers/accel/ivpu/ivpu_job.c | 3 +
drivers/accel/ivpu/ivpu_pm.c | 2 +-
drivers/accel/ivpu/vpu_boot_api.h | 36 +++++++++---
7 files changed, 112 insertions(+), 34 deletions(-)
diff --git a/drivers/accel/ivpu/ivpu_drv.c b/drivers/accel/ivpu/ivpu_drv.c
index 61149b7c30c2..b60430dcf168 100644
--- a/drivers/accel/ivpu/ivpu_drv.c
+++ b/drivers/accel/ivpu/ivpu_drv.c
@@ -506,11 +506,12 @@ static int ivpu_hw_sched_init(struct ivpu_device *vdev)
/**
* ivpu_boot() - Start VPU firmware
* @vdev: VPU device
+ * @device_init: Indicates if this is the initial boot during device
initialization
*
* This function is paired with ivpu_shutdown() but it doesn't power up the
* VPU because power up has to be called very early in ivpu_probe().
*/
-int ivpu_boot(struct ivpu_device *vdev)
+int ivpu_boot(struct ivpu_device *vdev, bool device_init)
{
int ret;
@@ -547,6 +548,12 @@ int ivpu_boot(struct ivpu_device *vdev)
if (ivpu_test_mode & IVPU_TEST_MODE_FW_TEST)
return 0;
+ if (device_init) {
+ ret = ivpu_fw_postboot_init(vdev);
+ if (ret)
+ goto err_disable_ipc;
+ }
+
if (!ivpu_fw_is_warm_boot(vdev)) {
ret = ivpu_pm_dct_init(vdev);
if (ret)
@@ -841,7 +848,7 @@ static int ivpu_dev_init(struct ivpu_device *vdev)
ivpu_pm_init(vdev);
- ret = ivpu_boot(vdev);
+ ret = ivpu_boot(vdev, true);
if (ret)
goto err_ipc_fini;
diff --git a/drivers/accel/ivpu/ivpu_drv.h b/drivers/accel/ivpu/ivpu_drv.h
index ea5b8dc612d1..70fab9c649d2 100644
--- a/drivers/accel/ivpu/ivpu_drv.h
+++ b/drivers/accel/ivpu/ivpu_drv.h
@@ -243,7 +243,7 @@ extern int ivpu_test_mode;
struct ivpu_file_priv *ivpu_file_priv_get(struct ivpu_file_priv *file_priv);
void ivpu_file_priv_put(struct ivpu_file_priv **link);
-int ivpu_boot(struct ivpu_device *vdev);
+int ivpu_boot(struct ivpu_device *vdev, bool device_init);
int ivpu_shutdown(struct ivpu_device *vdev);
void ivpu_prepare_for_reset(struct ivpu_device *vdev);
bool ivpu_is_capable(struct ivpu_device *vdev, u32 capability);
diff --git a/drivers/accel/ivpu/ivpu_fw.c b/drivers/accel/ivpu/ivpu_fw.c
index 96f5be23a867..6607a5da1934 100644
--- a/drivers/accel/ivpu/ivpu_fw.c
+++ b/drivers/accel/ivpu/ivpu_fw.c
@@ -19,8 +19,13 @@
#define FW_SHAVE_NN_MAX_SIZE SZ_2M
#define FW_FILE_IMAGE_OFFSET (VPU_FW_HEADER_SIZE + FW_VERSION_HEADER_SIZE)
-#define FW_PREEMPT_BUF_MIN_SIZE SZ_4K
-#define FW_PREEMPT_BUF_MAX_SIZE SZ_32M
+
+/* Size limits for preemption buffer sizes from image header */
+#define FW_PREEMPT_BUF_MIN_SIZE SZ_4K
+#define FW_PREEMPT_BUF_MAX_SIZE SZ_32M
+/* Size limits for preemption buffer size from boot parameters */
+#define FW_PREEMPT_BUF_BP_MIN_SIZE SZ_2M
+#define FW_PREEMPT_BUF_BP_MAX_SIZE SZ_32M
#define WATCHDOG_MSS_REDIRECT 32
#define WATCHDOG_NCE_REDIRECT 33
@@ -160,35 +165,31 @@ ivpu_fw_sched_mode_select(struct ivpu_device *vdev)
static void
ivpu_preemption_config_parse(struct ivpu_device *vdev, const struct
vpu_firmware_header *fw_hdr)
{
- struct ivpu_fw_info *fw = vdev->fw;
u32 primary_preempt_buf_size, secondary_preempt_buf_size;
- if (fw_hdr->preemption_buffer_1_max_size)
+ if (fw_hdr->preemption_buffer_1_max_size) {
primary_preempt_buf_size = fw_hdr->preemption_buffer_1_max_size;
- else
- primary_preempt_buf_size = fw_hdr->preemption_buffer_1_size;
-
- if (fw_hdr->preemption_buffer_2_max_size)
secondary_preempt_buf_size =
fw_hdr->preemption_buffer_2_max_size;
- else
+ } else {
+ primary_preempt_buf_size = fw_hdr->preemption_buffer_1_size;
secondary_preempt_buf_size = fw_hdr->preemption_buffer_2_size;
-
- ivpu_dbg(vdev, FW_BOOT, "Preemption buffer size, primary: %u, secondary:
%u\n",
- primary_preempt_buf_size, secondary_preempt_buf_size);
+ }
if (primary_preempt_buf_size < FW_PREEMPT_BUF_MIN_SIZE ||
secondary_preempt_buf_size < FW_PREEMPT_BUF_MIN_SIZE) {
- ivpu_warn(vdev, "Preemption buffers size too small\n");
+ ivpu_warn(vdev, "Preemption buffers size too small, primary %u,
secondary %u\n",
+ primary_preempt_buf_size, secondary_preempt_buf_size);
return;
}
if (primary_preempt_buf_size > FW_PREEMPT_BUF_MAX_SIZE ||
secondary_preempt_buf_size > FW_PREEMPT_BUF_MAX_SIZE) {
- ivpu_warn(vdev, "Preemption buffers size too big\n");
+ ivpu_warn(vdev, "Preemption buffers size too big, primary %u,
secondary %u\n",
+ primary_preempt_buf_size, secondary_preempt_buf_size);
return;
}
- if (fw->sched_mode != VPU_SCHEDULING_MODE_HW)
+ if (vdev->fw->sched_mode != VPU_SCHEDULING_MODE_HW)
return;
if (ivpu_test_mode & IVPU_TEST_MODE_MIP_DISABLE)
@@ -196,6 +197,9 @@ ivpu_preemption_config_parse(struct ivpu_device *vdev,
const struct vpu_firmware
vdev->fw->primary_preempt_buf_size = ALIGN(primary_preempt_buf_size, PAGE_SIZE);
vdev->fw->secondary_preempt_buf_size =
ALIGN(secondary_preempt_buf_size, PAGE_SIZE);
+
+ ivpu_dbg(vdev, FW_BOOT, "Preemption buffer size, primary: %u, secondary:
%u\n",
+ vdev->fw->primary_preempt_buf_size,
vdev->fw->secondary_preempt_buf_size);
}
static int ivpu_fw_parse(struct ivpu_device *vdev)
@@ -318,18 +322,10 @@ static int ivpu_fw_parse(struct ivpu_device *vdev)
fw->shave_nn_size = PAGE_ALIGN(fw_hdr->shave_nn_fw_size);
fw->cold_boot_entry_point = fw_hdr->entry_point;
- fw->trace_level = min(ivpu_fw_log_level, IVPU_FW_LOG_FATAL);
- fw->trace_destination_mask = VPU_TRACE_DESTINATION_VERBOSE_TRACING;
- fw->trace_hw_component_mask = -1;
-
- fw->dvfs_mode = 0;
-
fw->sched_mode = ivpu_fw_sched_mode_select(vdev);
ivpu_info(vdev, "Scheduler mode: %s\n", fw->sched_mode ? "HW" : "OS");
ivpu_preemption_config_parse(vdev, fw_hdr);
- ivpu_dbg(vdev, FW_BOOT, "Mid-inference preemption %s supported\n",
- ivpu_fw_preempt_buf_size(vdev) ? "is" : "is not");
if (fw_hdr->ro_section_start_address &&
!ivpu_is_within_range(fw_hdr->ro_section_start_address,
fw_hdr->ro_section_size,
@@ -363,6 +359,16 @@ static void ivpu_fw_release(struct ivpu_device *vdev)
release_firmware(vdev->fw->file);
}
+static void ivpu_fw_preboot_init(struct ivpu_device *vdev)
+{
+ struct ivpu_fw_info *fw = vdev->fw;
+
+ /* Let the firmware decide optimal DVFS mode */
+ fw->dvfs_mode = 0;
+ fw->trace_level = min(ivpu_fw_log_level, IVPU_FW_LOG_FATAL);
+ fw->trace_destination_mask = VPU_TRACE_DESTINATION_VERBOSE_TRACING;
+ fw->trace_hw_component_mask = -1;
+}
static void ivpu_fw_mem_fini(struct ivpu_device *vdev)
{
@@ -469,6 +475,8 @@ int ivpu_fw_init(struct ivpu_device *vdev)
if (ret)
goto err_fw_release;
+ ivpu_fw_preboot_init(vdev);
+
ret = ivpu_fw_mem_init(vdev);
if (ret)
goto err_fw_release;
@@ -710,3 +718,40 @@ void ivpu_fw_boot_params_setup(struct ivpu_device *vdev,
struct vpu_boot_params
ivpu_fw_boot_params_print(vdev, boot_params);
}
+
+int ivpu_fw_postboot_init(struct ivpu_device *vdev)
+{
+ struct vpu_boot_params *boot_params = ivpu_bo_vaddr(vdev->fw->mem_bp);
+ u32 preempt_buf_size = boot_params->preemption_buffer_size;
+
+ ivpu_dbg(vdev, FW_BOOT, "Post boot init\n");
+
+ if (vdev->fw->sched_mode == VPU_SCHEDULING_MODE_OS ||
+ ivpu_test_mode & IVPU_TEST_MODE_MIP_DISABLE) {
+ drm_WARN_ON(&vdev->drm, ivpu_fw_preempt_buf_size(vdev));
+ ivpu_dbg(vdev, FW_BOOT, "Preemption disabled!\n");
+ return 0;
+ }
+
+ if (preempt_buf_size) {
+ /* Preemption buffer size provided in boot parameters */
+ if (preempt_buf_size < FW_PREEMPT_BUF_BP_MIN_SIZE ||
+ preempt_buf_size > FW_PREEMPT_BUF_BP_MAX_SIZE) {
+ ivpu_err(vdev, "Invalid preemption buffer size in boot
params: %u, expected range [%u, %u]\n",
+ preempt_buf_size,
+ FW_PREEMPT_BUF_BP_MIN_SIZE,
+ FW_PREEMPT_BUF_BP_MAX_SIZE);
+ /* Be strict about invalid preemption buffer size in
boot params */
+ return -EINVAL;
+ }
+ vdev->fw->primary_preempt_buf_size = ALIGN(preempt_buf_size,
PAGE_SIZE);
+ vdev->fw->secondary_preempt_buf_size = 0;
+ }
+
+ ivpu_dbg(vdev, FW_BOOT, "Preemption buffer size: %u\n",
ivpu_fw_preempt_buf_size(vdev));
+
+ if (!ivpu_fw_preempt_buf_size(vdev))
+ ivpu_warn(vdev, "Hardware scheduling without preemption
buffer!\n");
+
+ return 0;
+}
diff --git a/drivers/accel/ivpu/ivpu_fw.h b/drivers/accel/ivpu/ivpu_fw.h
index 45a705431f22..2fcedb6c3e7d 100644
--- a/drivers/accel/ivpu/ivpu_fw.h
+++ b/drivers/accel/ivpu/ivpu_fw.h
@@ -57,6 +57,7 @@ int ivpu_fw_init(struct ivpu_device *vdev);
void ivpu_fw_fini(struct ivpu_device *vdev);
int ivpu_fw_load(struct ivpu_device *vdev);
void ivpu_fw_boot_params_setup(struct ivpu_device *vdev, struct
vpu_boot_params *boot_params);
+int ivpu_fw_postboot_init(struct ivpu_device *vdev);
static inline bool ivpu_fw_is_warm_boot(struct ivpu_device *vdev)
{
diff --git a/drivers/accel/ivpu/ivpu_job.c b/drivers/accel/ivpu/ivpu_job.c
index bfdee29cd91c..6a1307ee2e19 100644
--- a/drivers/accel/ivpu/ivpu_job.c
+++ b/drivers/accel/ivpu/ivpu_job.c
@@ -45,6 +45,9 @@ static int ivpu_preemption_buffers_create(struct ivpu_device
*vdev,
return -ENOMEM;
}
+ if (vdev->fw->secondary_preempt_buf_size == 0)
+ return 0;
+
cmdq->secondary_preempt_buf = ivpu_bo_create(vdev, &file_priv->ctx,
&vdev->hw->ranges.dma,
vdev->fw->secondary_preempt_buf_size,
DRM_IVPU_BO_WC);
diff --git a/drivers/accel/ivpu/ivpu_pm.c b/drivers/accel/ivpu/ivpu_pm.c
index cd4eff72f76c..8a6062bb101c 100644
--- a/drivers/accel/ivpu/ivpu_pm.c
+++ b/drivers/accel/ivpu/ivpu_pm.c
@@ -100,7 +100,7 @@ static int ivpu_resume(struct ivpu_device *vdev)
goto err_power_down;
}
- ret = ivpu_boot(vdev);
+ ret = ivpu_boot(vdev, false);
if (ret)
goto err_mmu_disable;
diff --git a/drivers/accel/ivpu/vpu_boot_api.h b/drivers/accel/ivpu/vpu_boot_api.h
index 06c092d5e675..c0da9e9cba62 100644
--- a/drivers/accel/ivpu/vpu_boot_api.h
+++ b/drivers/accel/ivpu/vpu_boot_api.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT */
/*
- * Copyright (c) 2020-2025, Intel Corporation.
+ * Copyright (c) 2020-2026, Intel Corporation.
*/
/**
@@ -36,12 +36,12 @@
* Minor version changes when API backward compatibility is preserved.
* Resets to 0 if Major version is incremented.
*/
-#define VPU_BOOT_API_VER_MINOR 29
+#define VPU_BOOT_API_VER_MINOR 39
/**
* API header changed (field names, documentation, formatting) but API itself
has not been changed
*/
-#define VPU_BOOT_API_VER_PATCH 5
+#define VPU_BOOT_API_VER_PATCH 1
/**
* Index in the API version table
@@ -74,6 +74,10 @@ struct vpu_firmware_header {
/** Size of memory require for firmware execution */
u32 runtime_size;
u32 shave_nn_fw_size;
+ /*
+ * Preemption buffer sizes in image header are deprecated post NPU50xx.
+ * Use vpu_boot_params::preemption_buffer_size instead.
+ */
/**
* Size of primary preemption buffer, assuming a 2-job submission queue.
* NOTE: host driver is expected to adapt size accordingly to actual
@@ -199,7 +203,7 @@ struct vpu_boot_l2_cache_config {
* Macros to enable various power profiles within the NPU.
* To be defined as part of 32 bit mask.
*/
-#define POWER_PROFILE_SURVIVABILITY 0x1
+#define POWER_PROFILE_SURVIVABILITY BIT(0)
/**
* Enum for dvfs_mode boot param.
@@ -209,7 +213,9 @@ enum vpu_governor {
VPU_GOV_MAX_PERFORMANCE = 1, /** Maximum performance governor */
VPU_GOV_ON_DEMAND = 2, /** On Demand frequency control governor */
VPU_GOV_POWER_SAVE = 3, /** Power save governor */
- VPU_GOV_ON_DEMAND_PRIORITY_AWARE = 4 /** On Demand priority based
governor */
+ VPU_GOV_ON_DEMAND_PRIORITY_AWARE = 4, /** On Demand priority based
governor */
+ VPU_GOV_ON_DEMAND_PER_PRIORITY = 5, /** On Demand frequency
per-priority governor */
+ VPU_GOV_ON_DEMAND_QOS = 6 /** On Demand QOS based governor */
};
struct vpu_boot_params {
@@ -239,8 +245,20 @@ struct vpu_boot_params {
* On VPU2.7 HW this address must be within 2GB range starting from
L2C_PAGE_TABLE base
*/
u64 shave_nn_fw_base;
- u64 save_restore_ret_address; /** stores the address of FW's restore
entry point */
- u32 reserved_4[43];
+ /**
+ * Firmware restore entry point used to resume from D0i3.
+ * Set before entering D0i3 and cleared during the resume sequence.
+ * Deprecated on NPU60xx and later; boot_type selects the warmboot path.
+ */
+ u64 save_restore_ret_address;
+ /**
+ * Required preemption buffer size in bytes.
+ * Firmware writes this before signaling cold-boot completion to the
host;
+ * it is not updated afterward.
+ * Available since NPU50xx.
+ */
+ u32 preemption_buffer_size;
+ u32 reserved_4[42];
/** IRQ re-direct numbers: 0x200 - 0x2FF */
s32 watchdog_irq_mss;
s32 watchdog_irq_nce;
@@ -268,6 +286,10 @@ struct vpu_boot_params {
* TODO: EISW-33556: Move log level definition (mvLog_t) to this file.
*/
u32 default_trace_level;
+ /**
+ * Boot mode requested by the host: VPU_BOOT_TYPE_COLDBOOT or
+ * VPU_BOOT_TYPE_WARMBOOT. Used by the boot loader on NPU60xx+.
+ */
u32 boot_type;
u64 punit_telemetry_sram_base;
u64 punit_telemetry_sram_size;