| 1 | // SPDX-License-Identifier: GPL-2.0-only |
| 2 | /* |
| 3 | * Copyright (C) 2020-2025 Intel Corporation |
| 4 | */ |
| 5 | |
| 6 | #include <linux/firmware.h> |
| 7 | #include <linux/highmem.h> |
| 8 | #include <linux/moduleparam.h> |
| 9 | #include <linux/pci.h> |
| 10 | |
| 11 | #include "vpu_boot_api.h" |
| 12 | #include "ivpu_drv.h" |
| 13 | #include "ivpu_fw.h" |
| 14 | #include "ivpu_fw_log.h" |
| 15 | #include "ivpu_gem.h" |
| 16 | #include "ivpu_hw.h" |
| 17 | #include "ivpu_ipc.h" |
| 18 | #include "ivpu_pm.h" |
| 19 | |
| 20 | #define FW_SHAVE_NN_MAX_SIZE SZ_2M |
| 21 | #define FW_FILE_IMAGE_OFFSET (VPU_FW_HEADER_SIZE + FW_VERSION_HEADER_SIZE) |
| 22 | #define FW_PREEMPT_BUF_MIN_SIZE SZ_4K |
| 23 | #define FW_PREEMPT_BUF_MAX_SIZE SZ_32M |
| 24 | |
| 25 | #define WATCHDOG_MSS_REDIRECT 32 |
| 26 | #define WATCHDOG_NCE_REDIRECT 33 |
| 27 | |
| 28 | #define ADDR_TO_L2_CACHE_CFG(addr) ((addr) >> 31) |
| 29 | |
| 30 | /* Check if FW API is compatible with the driver */ |
| 31 | #define IVPU_FW_CHECK_API_COMPAT(vdev, fw_hdr, name, min_major) \ |
| 32 | ivpu_fw_check_api(vdev, fw_hdr, #name, \ |
| 33 | VPU_##name##_API_VER_INDEX, \ |
| 34 | VPU_##name##_API_VER_MAJOR, \ |
| 35 | VPU_##name##_API_VER_MINOR, min_major) |
| 36 | |
| 37 | /* Check if API version is lower that the given version */ |
| 38 | #define IVPU_FW_CHECK_API_VER_LT(vdev, fw_hdr, name, major, minor) \ |
| 39 | ivpu_fw_check_api_ver_lt(vdev, fw_hdr, #name, VPU_##name##_API_VER_INDEX, major, minor) |
| 40 | |
| 41 | #define IVPU_FOCUS_PRESENT_TIMER_MS 1000 |
| 42 | |
| 43 | static char *ivpu_firmware; |
| 44 | #if IS_ENABLED(CONFIG_DRM_ACCEL_IVPU_DEBUG) |
| 45 | module_param_named_unsafe(firmware, ivpu_firmware, charp, 0644); |
| 46 | MODULE_PARM_DESC(firmware, "NPU firmware binary in /lib/firmware/.." ); |
| 47 | #endif |
| 48 | |
| 49 | static struct { |
| 50 | int gen; |
| 51 | const char *name; |
| 52 | } fw_names[] = { |
| 53 | { IVPU_HW_IP_37XX, "intel/vpu/vpu_37xx_v1.bin" }, |
| 54 | { IVPU_HW_IP_37XX, "intel/vpu/vpu_37xx_v0.0.bin" }, |
| 55 | { IVPU_HW_IP_40XX, "intel/vpu/vpu_40xx_v1.bin" }, |
| 56 | { IVPU_HW_IP_40XX, "intel/vpu/vpu_40xx_v0.0.bin" }, |
| 57 | { IVPU_HW_IP_50XX, "intel/vpu/vpu_50xx_v1.bin" }, |
| 58 | { IVPU_HW_IP_50XX, "intel/vpu/vpu_50xx_v0.0.bin" }, |
| 59 | { IVPU_HW_IP_60XX, "intel/vpu/vpu_60xx_v1.bin" }, |
| 60 | }; |
| 61 | |
| 62 | /* Production fw_names from the table above */ |
| 63 | MODULE_FIRMWARE("intel/vpu/vpu_37xx_v1.bin" ); |
| 64 | MODULE_FIRMWARE("intel/vpu/vpu_40xx_v1.bin" ); |
| 65 | MODULE_FIRMWARE("intel/vpu/vpu_50xx_v1.bin" ); |
| 66 | MODULE_FIRMWARE("intel/vpu/vpu_60xx_v1.bin" ); |
| 67 | |
| 68 | static int ivpu_fw_request(struct ivpu_device *vdev) |
| 69 | { |
| 70 | int ret = -ENOENT; |
| 71 | int i; |
| 72 | |
| 73 | if (ivpu_firmware) { |
| 74 | ret = request_firmware(fw: &vdev->fw->file, name: ivpu_firmware, device: vdev->drm.dev); |
| 75 | if (!ret) |
| 76 | vdev->fw->name = ivpu_firmware; |
| 77 | return ret; |
| 78 | } |
| 79 | |
| 80 | for (i = 0; i < ARRAY_SIZE(fw_names); i++) { |
| 81 | if (fw_names[i].gen != ivpu_hw_ip_gen(vdev)) |
| 82 | continue; |
| 83 | |
| 84 | ret = firmware_request_nowarn(fw: &vdev->fw->file, name: fw_names[i].name, device: vdev->drm.dev); |
| 85 | if (!ret) { |
| 86 | vdev->fw->name = fw_names[i].name; |
| 87 | return 0; |
| 88 | } |
| 89 | } |
| 90 | |
| 91 | ivpu_err(vdev, "Failed to request firmware: %d\n" , ret); |
| 92 | return ret; |
| 93 | } |
| 94 | |
| 95 | static int |
| 96 | ivpu_fw_check_api(struct ivpu_device *vdev, const struct vpu_firmware_header *fw_hdr, |
| 97 | const char *str, int index, u16 expected_major, u16 expected_minor, |
| 98 | u16 min_major) |
| 99 | { |
| 100 | u16 major = (u16)(fw_hdr->api_version[index] >> 16); |
| 101 | u16 minor = (u16)(fw_hdr->api_version[index]); |
| 102 | |
| 103 | if (major < min_major) { |
| 104 | ivpu_err(vdev, "Incompatible FW %s API version: %d.%d, required %d.0 or later\n" , |
| 105 | str, major, minor, min_major); |
| 106 | return -EINVAL; |
| 107 | } |
| 108 | if (major != expected_major) { |
| 109 | ivpu_warn(vdev, "Major FW %s API version different: %d.%d (expected %d.%d)\n" , |
| 110 | str, major, minor, expected_major, expected_minor); |
| 111 | } |
| 112 | ivpu_dbg(vdev, FW_BOOT, "FW %s API version: %d.%d (expected %d.%d)\n" , |
| 113 | str, major, minor, expected_major, expected_minor); |
| 114 | |
| 115 | return 0; |
| 116 | } |
| 117 | |
| 118 | static bool |
| 119 | ivpu_fw_check_api_ver_lt(struct ivpu_device *vdev, const struct vpu_firmware_header *fw_hdr, |
| 120 | const char *str, int index, u16 major, u16 minor) |
| 121 | { |
| 122 | u16 fw_major = (u16)(fw_hdr->api_version[index] >> 16); |
| 123 | u16 fw_minor = (u16)(fw_hdr->api_version[index]); |
| 124 | |
| 125 | if (fw_major < major || (fw_major == major && fw_minor < minor)) |
| 126 | return true; |
| 127 | |
| 128 | return false; |
| 129 | } |
| 130 | |
| 131 | bool ivpu_is_within_range(u64 addr, size_t size, struct ivpu_addr_range *range) |
| 132 | { |
| 133 | u64 addr_end; |
| 134 | |
| 135 | if (!range || check_add_overflow(addr, size, &addr_end)) |
| 136 | return false; |
| 137 | |
| 138 | if (addr < range->start || addr_end > range->end) |
| 139 | return false; |
| 140 | |
| 141 | return true; |
| 142 | } |
| 143 | |
| 144 | static u32 |
| 145 | ivpu_fw_sched_mode_select(struct ivpu_device *vdev, const struct vpu_firmware_header *fw_hdr) |
| 146 | { |
| 147 | if (ivpu_hw_ip_gen(vdev) >= IVPU_HW_IP_60XX && |
| 148 | ivpu_sched_mode == VPU_SCHEDULING_MODE_OS) { |
| 149 | ivpu_warn(vdev, "OS sched mode is not supported, using HW mode\n" ); |
| 150 | return VPU_SCHEDULING_MODE_HW; |
| 151 | } |
| 152 | |
| 153 | if (ivpu_sched_mode != IVPU_SCHED_MODE_AUTO) |
| 154 | return ivpu_sched_mode; |
| 155 | |
| 156 | if (IVPU_FW_CHECK_API_VER_LT(vdev, fw_hdr, JSM, 3, 24)) |
| 157 | return VPU_SCHEDULING_MODE_OS; |
| 158 | |
| 159 | return VPU_SCHEDULING_MODE_HW; |
| 160 | } |
| 161 | |
| 162 | static void |
| 163 | ivpu_preemption_config_parse(struct ivpu_device *vdev, const struct vpu_firmware_header *fw_hdr) |
| 164 | { |
| 165 | struct ivpu_fw_info *fw = vdev->fw; |
| 166 | u32 primary_preempt_buf_size, secondary_preempt_buf_size; |
| 167 | |
| 168 | if (fw_hdr->preemption_buffer_1_max_size) |
| 169 | primary_preempt_buf_size = fw_hdr->preemption_buffer_1_max_size; |
| 170 | else |
| 171 | primary_preempt_buf_size = fw_hdr->preemption_buffer_1_size; |
| 172 | |
| 173 | if (fw_hdr->preemption_buffer_2_max_size) |
| 174 | secondary_preempt_buf_size = fw_hdr->preemption_buffer_2_max_size; |
| 175 | else |
| 176 | secondary_preempt_buf_size = fw_hdr->preemption_buffer_2_size; |
| 177 | |
| 178 | ivpu_dbg(vdev, FW_BOOT, "Preemption buffer size, primary: %u, secondary: %u\n" , |
| 179 | primary_preempt_buf_size, secondary_preempt_buf_size); |
| 180 | |
| 181 | if (primary_preempt_buf_size < FW_PREEMPT_BUF_MIN_SIZE || |
| 182 | secondary_preempt_buf_size < FW_PREEMPT_BUF_MIN_SIZE) { |
| 183 | ivpu_warn(vdev, "Preemption buffers size too small\n" ); |
| 184 | return; |
| 185 | } |
| 186 | |
| 187 | if (primary_preempt_buf_size > FW_PREEMPT_BUF_MAX_SIZE || |
| 188 | secondary_preempt_buf_size > FW_PREEMPT_BUF_MAX_SIZE) { |
| 189 | ivpu_warn(vdev, "Preemption buffers size too big\n" ); |
| 190 | return; |
| 191 | } |
| 192 | |
| 193 | if (fw->sched_mode != VPU_SCHEDULING_MODE_HW) |
| 194 | return; |
| 195 | |
| 196 | if (ivpu_test_mode & IVPU_TEST_MODE_MIP_DISABLE) |
| 197 | return; |
| 198 | |
| 199 | vdev->fw->primary_preempt_buf_size = ALIGN(primary_preempt_buf_size, PAGE_SIZE); |
| 200 | vdev->fw->secondary_preempt_buf_size = ALIGN(secondary_preempt_buf_size, PAGE_SIZE); |
| 201 | } |
| 202 | |
| 203 | static int ivpu_fw_parse(struct ivpu_device *vdev) |
| 204 | { |
| 205 | struct ivpu_fw_info *fw = vdev->fw; |
| 206 | const struct vpu_firmware_header *fw_hdr = (const void *)fw->file->data; |
| 207 | struct ivpu_addr_range fw_image_range; |
| 208 | u64 boot_params_addr, boot_params_size; |
| 209 | u64 fw_version_addr, fw_version_size; |
| 210 | u64 runtime_addr, runtime_size; |
| 211 | u64 image_load_addr, image_size; |
| 212 | |
| 213 | if (fw->file->size <= FW_FILE_IMAGE_OFFSET) { |
| 214 | ivpu_err(vdev, "Firmware file is too small: %zu\n" , fw->file->size); |
| 215 | return -EINVAL; |
| 216 | } |
| 217 | |
| 218 | if (fw_hdr->header_version != VPU_FW_HEADER_VERSION) { |
| 219 | ivpu_err(vdev, "Invalid firmware header version: %u\n" , fw_hdr->header_version); |
| 220 | return -EINVAL; |
| 221 | } |
| 222 | |
| 223 | boot_params_addr = fw_hdr->boot_params_load_address; |
| 224 | boot_params_size = SZ_4K; |
| 225 | |
| 226 | if (!ivpu_is_within_range(addr: boot_params_addr, size: boot_params_size, range: &vdev->hw->ranges.runtime)) { |
| 227 | ivpu_err(vdev, "Invalid boot params address: 0x%llx\n" , boot_params_addr); |
| 228 | return -EINVAL; |
| 229 | } |
| 230 | |
| 231 | fw_version_addr = fw_hdr->firmware_version_load_address; |
| 232 | fw_version_size = ALIGN(fw_hdr->firmware_version_size, SZ_4K); |
| 233 | |
| 234 | if (fw_version_size != SZ_4K) { |
| 235 | ivpu_err(vdev, "Invalid firmware version size: %u\n" , |
| 236 | fw_hdr->firmware_version_size); |
| 237 | return -EINVAL; |
| 238 | } |
| 239 | |
| 240 | if (!ivpu_is_within_range(addr: fw_version_addr, size: fw_version_size, range: &vdev->hw->ranges.runtime)) { |
| 241 | ivpu_err(vdev, "Invalid firmware version address: 0x%llx\n" , fw_version_addr); |
| 242 | return -EINVAL; |
| 243 | } |
| 244 | |
| 245 | runtime_addr = fw_hdr->image_load_address; |
| 246 | runtime_size = fw_hdr->runtime_size - boot_params_size - fw_version_size; |
| 247 | |
| 248 | image_load_addr = fw_hdr->image_load_address; |
| 249 | image_size = fw_hdr->image_size; |
| 250 | |
| 251 | if (!ivpu_is_within_range(addr: runtime_addr, size: runtime_size, range: &vdev->hw->ranges.runtime)) { |
| 252 | ivpu_err(vdev, "Invalid firmware runtime address: 0x%llx and size %llu\n" , |
| 253 | runtime_addr, runtime_size); |
| 254 | return -EINVAL; |
| 255 | } |
| 256 | |
| 257 | if (FW_FILE_IMAGE_OFFSET + image_size > fw->file->size) { |
| 258 | ivpu_err(vdev, "Invalid image size: %llu\n" , image_size); |
| 259 | return -EINVAL; |
| 260 | } |
| 261 | |
| 262 | if (!ivpu_is_within_range(addr: image_load_addr, size: image_size, range: &vdev->hw->ranges.runtime)) { |
| 263 | ivpu_err(vdev, "Invalid firmware load address: 0x%llx and size %llu\n" , |
| 264 | image_load_addr, image_size); |
| 265 | return -EINVAL; |
| 266 | } |
| 267 | |
| 268 | if (ivpu_hw_range_init(vdev, range: &fw_image_range, start: image_load_addr, size: image_size)) |
| 269 | return -EINVAL; |
| 270 | |
| 271 | if (!ivpu_is_within_range(addr: fw_hdr->entry_point, SZ_4K, range: &fw_image_range)) { |
| 272 | ivpu_err(vdev, "Invalid entry point: 0x%llx\n" , fw_hdr->entry_point); |
| 273 | return -EINVAL; |
| 274 | } |
| 275 | |
| 276 | if (fw_hdr->shave_nn_fw_size > FW_SHAVE_NN_MAX_SIZE) { |
| 277 | ivpu_err(vdev, "SHAVE NN firmware is too big: %u\n" , fw_hdr->shave_nn_fw_size); |
| 278 | return -EINVAL; |
| 279 | } |
| 280 | |
| 281 | ivpu_dbg(vdev, FW_BOOT, "Header version: 0x%x, format 0x%x\n" , |
| 282 | fw_hdr->header_version, fw_hdr->image_format); |
| 283 | |
| 284 | if (!scnprintf(buf: fw->version, size: sizeof(fw->version), fmt: "%s" , fw->file->data + VPU_FW_HEADER_SIZE)) |
| 285 | ivpu_warn(vdev, "Missing firmware version\n" ); |
| 286 | |
| 287 | ivpu_info(vdev, "Firmware: %s, version: %s\n" , fw->name, fw->version); |
| 288 | |
| 289 | if (IVPU_FW_CHECK_API_COMPAT(vdev, fw_hdr, BOOT, 3)) |
| 290 | return -EINVAL; |
| 291 | if (IVPU_FW_CHECK_API_COMPAT(vdev, fw_hdr, JSM, 3)) |
| 292 | return -EINVAL; |
| 293 | |
| 294 | fw->boot_params_addr = boot_params_addr; |
| 295 | fw->boot_params_size = boot_params_size; |
| 296 | fw->fw_version_addr = fw_version_addr; |
| 297 | fw->fw_version_size = fw_version_size; |
| 298 | fw->runtime_addr = runtime_addr; |
| 299 | fw->runtime_size = runtime_size; |
| 300 | fw->image_load_offset = image_load_addr - runtime_addr; |
| 301 | fw->image_size = image_size; |
| 302 | fw->shave_nn_size = PAGE_ALIGN(fw_hdr->shave_nn_fw_size); |
| 303 | |
| 304 | fw->cold_boot_entry_point = fw_hdr->entry_point; |
| 305 | fw->entry_point = fw->cold_boot_entry_point; |
| 306 | |
| 307 | fw->trace_level = min_t(u32, ivpu_fw_log_level, IVPU_FW_LOG_FATAL); |
| 308 | fw->trace_destination_mask = VPU_TRACE_DESTINATION_VERBOSE_TRACING; |
| 309 | fw->trace_hw_component_mask = -1; |
| 310 | |
| 311 | fw->dvfs_mode = 0; |
| 312 | |
| 313 | fw->sched_mode = ivpu_fw_sched_mode_select(vdev, fw_hdr); |
| 314 | ivpu_info(vdev, "Scheduler mode: %s\n" , fw->sched_mode ? "HW" : "OS" ); |
| 315 | |
| 316 | ivpu_preemption_config_parse(vdev, fw_hdr); |
| 317 | ivpu_dbg(vdev, FW_BOOT, "Mid-inference preemption %s supported\n" , |
| 318 | ivpu_fw_preempt_buf_size(vdev) ? "is" : "is not" ); |
| 319 | |
| 320 | if (fw_hdr->ro_section_start_address && |
| 321 | !ivpu_is_within_range(addr: fw_hdr->ro_section_start_address, size: fw_hdr->ro_section_size, |
| 322 | range: &fw_image_range)) { |
| 323 | ivpu_err(vdev, "Invalid read-only section: start address 0x%llx, size %u\n" , |
| 324 | fw_hdr->ro_section_start_address, fw_hdr->ro_section_size); |
| 325 | return -EINVAL; |
| 326 | } |
| 327 | |
| 328 | fw->read_only_addr = fw_hdr->ro_section_start_address; |
| 329 | fw->read_only_size = fw_hdr->ro_section_size; |
| 330 | |
| 331 | ivpu_dbg(vdev, FW_BOOT, "Boot params: address 0x%llx, size %llu\n" , |
| 332 | fw->boot_params_addr, fw->boot_params_size); |
| 333 | ivpu_dbg(vdev, FW_BOOT, "FW version: address 0x%llx, size %llu\n" , |
| 334 | fw->fw_version_addr, fw->fw_version_size); |
| 335 | ivpu_dbg(vdev, FW_BOOT, "Runtime: address 0x%llx, size %u\n" , |
| 336 | fw->runtime_addr, fw->runtime_size); |
| 337 | ivpu_dbg(vdev, FW_BOOT, "Image load offset: 0x%llx, size %u\n" , |
| 338 | fw->image_load_offset, fw->image_size); |
| 339 | ivpu_dbg(vdev, FW_BOOT, "Read-only section: address 0x%llx, size %u\n" , |
| 340 | fw->read_only_addr, fw->read_only_size); |
| 341 | ivpu_dbg(vdev, FW_BOOT, "FW entry point: 0x%llx\n" , fw->entry_point); |
| 342 | ivpu_dbg(vdev, FW_BOOT, "SHAVE NN size: %u\n" , fw->shave_nn_size); |
| 343 | |
| 344 | return 0; |
| 345 | } |
| 346 | |
| 347 | static void ivpu_fw_release(struct ivpu_device *vdev) |
| 348 | { |
| 349 | release_firmware(fw: vdev->fw->file); |
| 350 | } |
| 351 | |
| 352 | /* Initialize workarounds that depend on FW version */ |
| 353 | static void |
| 354 | ivpu_fw_init_wa(struct ivpu_device *vdev) |
| 355 | { |
| 356 | const struct vpu_firmware_header *fw_hdr = (const void *)vdev->fw->file->data; |
| 357 | |
| 358 | if (IVPU_FW_CHECK_API_VER_LT(vdev, fw_hdr, BOOT, 3, 17) || |
| 359 | (ivpu_test_mode & IVPU_TEST_MODE_D0I3_MSG_DISABLE)) |
| 360 | vdev->wa.disable_d0i3_msg = true; |
| 361 | |
| 362 | /* Force enable the feature for testing purposes */ |
| 363 | if (ivpu_test_mode & IVPU_TEST_MODE_D0I3_MSG_ENABLE) |
| 364 | vdev->wa.disable_d0i3_msg = false; |
| 365 | |
| 366 | IVPU_PRINT_WA(disable_d0i3_msg); |
| 367 | } |
| 368 | |
| 369 | static int ivpu_fw_mem_init(struct ivpu_device *vdev) |
| 370 | { |
| 371 | struct ivpu_fw_info *fw = vdev->fw; |
| 372 | int log_verb_size; |
| 373 | int ret; |
| 374 | |
| 375 | fw->mem_bp = ivpu_bo_create_runtime(vdev, addr: fw->boot_params_addr, size: fw->boot_params_size, |
| 376 | DRM_IVPU_BO_WC | DRM_IVPU_BO_MAPPABLE); |
| 377 | if (!fw->mem_bp) { |
| 378 | ivpu_err(vdev, "Failed to create firmware boot params memory buffer\n" ); |
| 379 | return -ENOMEM; |
| 380 | } |
| 381 | |
| 382 | fw->mem_fw_ver = ivpu_bo_create_runtime(vdev, addr: fw->fw_version_addr, size: fw->fw_version_size, |
| 383 | DRM_IVPU_BO_WC | DRM_IVPU_BO_MAPPABLE); |
| 384 | if (!fw->mem_fw_ver) { |
| 385 | ivpu_err(vdev, "Failed to create firmware version memory buffer\n" ); |
| 386 | ret = -ENOMEM; |
| 387 | goto err_free_bp; |
| 388 | } |
| 389 | |
| 390 | fw->mem = ivpu_bo_create_runtime(vdev, addr: fw->runtime_addr, size: fw->runtime_size, |
| 391 | DRM_IVPU_BO_WC | DRM_IVPU_BO_MAPPABLE); |
| 392 | if (!fw->mem) { |
| 393 | ivpu_err(vdev, "Failed to create firmware runtime memory buffer\n" ); |
| 394 | ret = -ENOMEM; |
| 395 | goto err_free_fw_ver; |
| 396 | } |
| 397 | |
| 398 | ret = ivpu_mmu_context_set_pages_ro(vdev, ctx: &vdev->gctx, vpu_addr: fw->read_only_addr, |
| 399 | size: fw->read_only_size); |
| 400 | if (ret) { |
| 401 | ivpu_err(vdev, "Failed to set firmware image read-only\n" ); |
| 402 | goto err_free_fw_mem; |
| 403 | } |
| 404 | |
| 405 | fw->mem_log_crit = ivpu_bo_create_global(vdev, IVPU_FW_CRITICAL_BUFFER_SIZE, |
| 406 | DRM_IVPU_BO_CACHED | DRM_IVPU_BO_MAPPABLE); |
| 407 | if (!fw->mem_log_crit) { |
| 408 | ivpu_err(vdev, "Failed to create critical log buffer\n" ); |
| 409 | ret = -ENOMEM; |
| 410 | goto err_free_fw_mem; |
| 411 | } |
| 412 | |
| 413 | if (ivpu_fw_log_level <= IVPU_FW_LOG_INFO) |
| 414 | log_verb_size = IVPU_FW_VERBOSE_BUFFER_LARGE_SIZE; |
| 415 | else |
| 416 | log_verb_size = IVPU_FW_VERBOSE_BUFFER_SMALL_SIZE; |
| 417 | |
| 418 | fw->mem_log_verb = ivpu_bo_create_global(vdev, size: log_verb_size, |
| 419 | DRM_IVPU_BO_CACHED | DRM_IVPU_BO_MAPPABLE); |
| 420 | if (!fw->mem_log_verb) { |
| 421 | ivpu_err(vdev, "Failed to create verbose log buffer\n" ); |
| 422 | ret = -ENOMEM; |
| 423 | goto err_free_log_crit; |
| 424 | } |
| 425 | |
| 426 | if (fw->shave_nn_size) { |
| 427 | fw->mem_shave_nn = ivpu_bo_create(vdev, ctx: &vdev->gctx, range: &vdev->hw->ranges.shave, |
| 428 | size: fw->shave_nn_size, DRM_IVPU_BO_WC); |
| 429 | if (!fw->mem_shave_nn) { |
| 430 | ivpu_err(vdev, "Failed to create shavenn buffer\n" ); |
| 431 | ret = -ENOMEM; |
| 432 | goto err_free_log_verb; |
| 433 | } |
| 434 | } |
| 435 | |
| 436 | return 0; |
| 437 | |
| 438 | err_free_log_verb: |
| 439 | ivpu_bo_free(bo: fw->mem_log_verb); |
| 440 | err_free_log_crit: |
| 441 | ivpu_bo_free(bo: fw->mem_log_crit); |
| 442 | err_free_fw_mem: |
| 443 | ivpu_bo_free(bo: fw->mem); |
| 444 | err_free_fw_ver: |
| 445 | ivpu_bo_free(bo: fw->mem_fw_ver); |
| 446 | err_free_bp: |
| 447 | ivpu_bo_free(bo: fw->mem_bp); |
| 448 | return ret; |
| 449 | } |
| 450 | |
| 451 | static void ivpu_fw_mem_fini(struct ivpu_device *vdev) |
| 452 | { |
| 453 | struct ivpu_fw_info *fw = vdev->fw; |
| 454 | |
| 455 | if (fw->mem_shave_nn) { |
| 456 | ivpu_bo_free(bo: fw->mem_shave_nn); |
| 457 | fw->mem_shave_nn = NULL; |
| 458 | } |
| 459 | |
| 460 | ivpu_bo_free(bo: fw->mem_log_verb); |
| 461 | ivpu_bo_free(bo: fw->mem_log_crit); |
| 462 | ivpu_bo_free(bo: fw->mem); |
| 463 | ivpu_bo_free(bo: fw->mem_fw_ver); |
| 464 | ivpu_bo_free(bo: fw->mem_bp); |
| 465 | |
| 466 | fw->mem_log_verb = NULL; |
| 467 | fw->mem_log_crit = NULL; |
| 468 | fw->mem = NULL; |
| 469 | fw->mem_fw_ver = NULL; |
| 470 | fw->mem_bp = NULL; |
| 471 | } |
| 472 | |
| 473 | int ivpu_fw_init(struct ivpu_device *vdev) |
| 474 | { |
| 475 | int ret; |
| 476 | |
| 477 | ret = ivpu_fw_request(vdev); |
| 478 | if (ret) |
| 479 | return ret; |
| 480 | |
| 481 | ret = ivpu_fw_parse(vdev); |
| 482 | if (ret) |
| 483 | goto err_fw_release; |
| 484 | |
| 485 | ivpu_fw_init_wa(vdev); |
| 486 | |
| 487 | ret = ivpu_fw_mem_init(vdev); |
| 488 | if (ret) |
| 489 | goto err_fw_release; |
| 490 | |
| 491 | ivpu_fw_load(vdev); |
| 492 | |
| 493 | return 0; |
| 494 | |
| 495 | err_fw_release: |
| 496 | ivpu_fw_release(vdev); |
| 497 | return ret; |
| 498 | } |
| 499 | |
| 500 | void ivpu_fw_fini(struct ivpu_device *vdev) |
| 501 | { |
| 502 | ivpu_fw_mem_fini(vdev); |
| 503 | ivpu_fw_release(vdev); |
| 504 | } |
| 505 | |
| 506 | void ivpu_fw_load(struct ivpu_device *vdev) |
| 507 | { |
| 508 | struct ivpu_fw_info *fw = vdev->fw; |
| 509 | u64 image_end_offset = fw->image_load_offset + fw->image_size; |
| 510 | |
| 511 | memset(ivpu_bo_vaddr(fw->mem), 0, fw->image_load_offset); |
| 512 | memcpy(ivpu_bo_vaddr(fw->mem) + fw->image_load_offset, |
| 513 | fw->file->data + FW_FILE_IMAGE_OFFSET, fw->image_size); |
| 514 | |
| 515 | if (IVPU_WA(clear_runtime_mem)) { |
| 516 | u8 *start = ivpu_bo_vaddr(bo: fw->mem) + image_end_offset; |
| 517 | u64 size = ivpu_bo_size(bo: fw->mem) - image_end_offset; |
| 518 | |
| 519 | memset(start, 0, size); |
| 520 | } |
| 521 | |
| 522 | wmb(); /* Flush WC buffers after writing fw->mem */ |
| 523 | } |
| 524 | |
| 525 | static void ivpu_fw_boot_params_print(struct ivpu_device *vdev, struct vpu_boot_params *boot_params) |
| 526 | { |
| 527 | ivpu_dbg(vdev, FW_BOOT, "boot_params.magic = 0x%x\n" , |
| 528 | boot_params->magic); |
| 529 | ivpu_dbg(vdev, FW_BOOT, "boot_params.vpu_id = 0x%x\n" , |
| 530 | boot_params->vpu_id); |
| 531 | ivpu_dbg(vdev, FW_BOOT, "boot_params.vpu_count = 0x%x\n" , |
| 532 | boot_params->vpu_count); |
| 533 | ivpu_dbg(vdev, FW_BOOT, "boot_params.frequency = %u\n" , |
| 534 | boot_params->frequency); |
| 535 | ivpu_dbg(vdev, FW_BOOT, "boot_params.perf_clk_frequency = %u\n" , |
| 536 | boot_params->perf_clk_frequency); |
| 537 | |
| 538 | ivpu_dbg(vdev, FW_BOOT, "boot_params.ipc_header_area_start = 0x%llx\n" , |
| 539 | boot_params->ipc_header_area_start); |
| 540 | ivpu_dbg(vdev, FW_BOOT, "boot_params.ipc_header_area_size = 0x%x\n" , |
| 541 | boot_params->ipc_header_area_size); |
| 542 | ivpu_dbg(vdev, FW_BOOT, "boot_params.shared_region_base = 0x%llx\n" , |
| 543 | boot_params->shared_region_base); |
| 544 | ivpu_dbg(vdev, FW_BOOT, "boot_params.shared_region_size = 0x%x\n" , |
| 545 | boot_params->shared_region_size); |
| 546 | ivpu_dbg(vdev, FW_BOOT, "boot_params.ipc_payload_area_start = 0x%llx\n" , |
| 547 | boot_params->ipc_payload_area_start); |
| 548 | ivpu_dbg(vdev, FW_BOOT, "boot_params.ipc_payload_area_size = 0x%x\n" , |
| 549 | boot_params->ipc_payload_area_size); |
| 550 | ivpu_dbg(vdev, FW_BOOT, "boot_params.global_aliased_pio_base = 0x%llx\n" , |
| 551 | boot_params->global_aliased_pio_base); |
| 552 | ivpu_dbg(vdev, FW_BOOT, "boot_params.global_aliased_pio_size = 0x%x\n" , |
| 553 | boot_params->global_aliased_pio_size); |
| 554 | |
| 555 | ivpu_dbg(vdev, FW_BOOT, "boot_params.autoconfig = 0x%x\n" , |
| 556 | boot_params->autoconfig); |
| 557 | |
| 558 | ivpu_dbg(vdev, FW_BOOT, "boot_params.cache_defaults[VPU_BOOT_L2_CACHE_CFG_NN].use = 0x%x\n" , |
| 559 | boot_params->cache_defaults[VPU_BOOT_L2_CACHE_CFG_NN].use); |
| 560 | ivpu_dbg(vdev, FW_BOOT, "boot_params.cache_defaults[VPU_BOOT_L2_CACHE_CFG_NN].cfg = 0x%x\n" , |
| 561 | boot_params->cache_defaults[VPU_BOOT_L2_CACHE_CFG_NN].cfg); |
| 562 | |
| 563 | ivpu_dbg(vdev, FW_BOOT, "boot_params.shave_nn_fw_base = 0x%llx\n" , |
| 564 | boot_params->shave_nn_fw_base); |
| 565 | |
| 566 | ivpu_dbg(vdev, FW_BOOT, "boot_params.watchdog_irq_mss = 0x%x\n" , |
| 567 | boot_params->watchdog_irq_mss); |
| 568 | ivpu_dbg(vdev, FW_BOOT, "boot_params.watchdog_irq_nce = 0x%x\n" , |
| 569 | boot_params->watchdog_irq_nce); |
| 570 | |
| 571 | ivpu_dbg(vdev, FW_BOOT, "boot_params.host_version_id = 0x%x\n" , |
| 572 | boot_params->host_version_id); |
| 573 | ivpu_dbg(vdev, FW_BOOT, "boot_params.si_stepping = 0x%x\n" , |
| 574 | boot_params->si_stepping); |
| 575 | ivpu_dbg(vdev, FW_BOOT, "boot_params.device_id = 0x%llx\n" , |
| 576 | boot_params->device_id); |
| 577 | ivpu_dbg(vdev, FW_BOOT, "boot_params.feature_exclusion = 0x%llx\n" , |
| 578 | boot_params->feature_exclusion); |
| 579 | ivpu_dbg(vdev, FW_BOOT, "boot_params.sku = 0x%llx\n" , |
| 580 | boot_params->sku); |
| 581 | ivpu_dbg(vdev, FW_BOOT, "boot_params.min_freq_pll_ratio = 0x%x\n" , |
| 582 | boot_params->min_freq_pll_ratio); |
| 583 | ivpu_dbg(vdev, FW_BOOT, "boot_params.pn_freq_pll_ratio = 0x%x\n" , |
| 584 | boot_params->pn_freq_pll_ratio); |
| 585 | ivpu_dbg(vdev, FW_BOOT, "boot_params.max_freq_pll_ratio = 0x%x\n" , |
| 586 | boot_params->max_freq_pll_ratio); |
| 587 | ivpu_dbg(vdev, FW_BOOT, "boot_params.default_trace_level = 0x%x\n" , |
| 588 | boot_params->default_trace_level); |
| 589 | ivpu_dbg(vdev, FW_BOOT, "boot_params.tracing_buff_message_format_mask = 0x%llx\n" , |
| 590 | boot_params->tracing_buff_message_format_mask); |
| 591 | ivpu_dbg(vdev, FW_BOOT, "boot_params.trace_destination_mask = 0x%x\n" , |
| 592 | boot_params->trace_destination_mask); |
| 593 | ivpu_dbg(vdev, FW_BOOT, "boot_params.trace_hw_component_mask = 0x%llx\n" , |
| 594 | boot_params->trace_hw_component_mask); |
| 595 | ivpu_dbg(vdev, FW_BOOT, "boot_params.boot_type = 0x%x\n" , |
| 596 | boot_params->boot_type); |
| 597 | ivpu_dbg(vdev, FW_BOOT, "boot_params.punit_telemetry_sram_base = 0x%llx\n" , |
| 598 | boot_params->punit_telemetry_sram_base); |
| 599 | ivpu_dbg(vdev, FW_BOOT, "boot_params.punit_telemetry_sram_size = 0x%llx\n" , |
| 600 | boot_params->punit_telemetry_sram_size); |
| 601 | ivpu_dbg(vdev, FW_BOOT, "boot_params.vpu_telemetry_enable = 0x%x\n" , |
| 602 | boot_params->vpu_telemetry_enable); |
| 603 | ivpu_dbg(vdev, FW_BOOT, "boot_params.vpu_scheduling_mode = 0x%x\n" , |
| 604 | boot_params->vpu_scheduling_mode); |
| 605 | ivpu_dbg(vdev, FW_BOOT, "boot_params.dvfs_mode = %u\n" , |
| 606 | boot_params->dvfs_mode); |
| 607 | ivpu_dbg(vdev, FW_BOOT, "boot_params.d0i3_delayed_entry = %d\n" , |
| 608 | boot_params->d0i3_delayed_entry); |
| 609 | ivpu_dbg(vdev, FW_BOOT, "boot_params.d0i3_residency_time_us = %lld\n" , |
| 610 | boot_params->d0i3_residency_time_us); |
| 611 | ivpu_dbg(vdev, FW_BOOT, "boot_params.d0i3_entry_vpu_ts = %llu\n" , |
| 612 | boot_params->d0i3_entry_vpu_ts); |
| 613 | ivpu_dbg(vdev, FW_BOOT, "boot_params.system_time_us = %llu\n" , |
| 614 | boot_params->system_time_us); |
| 615 | ivpu_dbg(vdev, FW_BOOT, "boot_params.power_profile = 0x%x\n" , |
| 616 | boot_params->power_profile); |
| 617 | ivpu_dbg(vdev, FW_BOOT, "boot_params.vpu_uses_ecc_mca_signal = 0x%x\n" , |
| 618 | boot_params->vpu_uses_ecc_mca_signal); |
| 619 | } |
| 620 | |
| 621 | void ivpu_fw_boot_params_setup(struct ivpu_device *vdev, struct vpu_boot_params *boot_params) |
| 622 | { |
| 623 | struct ivpu_bo *ipc_mem_rx = vdev->ipc->mem_rx; |
| 624 | |
| 625 | /* In case of warm boot only update variable params */ |
| 626 | if (!ivpu_fw_is_cold_boot(vdev)) { |
| 627 | boot_params->d0i3_residency_time_us = |
| 628 | ktime_us_delta(later: ktime_get_boottime(), earlier: vdev->hw->d0i3_entry_host_ts); |
| 629 | boot_params->d0i3_entry_vpu_ts = vdev->hw->d0i3_entry_vpu_ts; |
| 630 | boot_params->system_time_us = ktime_to_us(kt: ktime_get_real()); |
| 631 | |
| 632 | ivpu_dbg(vdev, FW_BOOT, "boot_params.d0i3_residency_time_us = %lld\n" , |
| 633 | boot_params->d0i3_residency_time_us); |
| 634 | ivpu_dbg(vdev, FW_BOOT, "boot_params.d0i3_entry_vpu_ts = %llu\n" , |
| 635 | boot_params->d0i3_entry_vpu_ts); |
| 636 | ivpu_dbg(vdev, FW_BOOT, "boot_params.system_time_us = %llu\n" , |
| 637 | boot_params->system_time_us); |
| 638 | |
| 639 | boot_params->save_restore_ret_address = 0; |
| 640 | vdev->pm->is_warmboot = true; |
| 641 | wmb(); /* Flush WC buffers after writing save_restore_ret_address */ |
| 642 | return; |
| 643 | } |
| 644 | |
| 645 | memset(boot_params, 0, sizeof(*boot_params)); |
| 646 | vdev->pm->is_warmboot = false; |
| 647 | |
| 648 | boot_params->magic = VPU_BOOT_PARAMS_MAGIC; |
| 649 | boot_params->vpu_id = to_pci_dev(vdev->drm.dev)->bus->number; |
| 650 | |
| 651 | /* |
| 652 | * This param is a debug firmware feature. It switches default clock |
| 653 | * to higher resolution one for fine-grained and more accurate firmware |
| 654 | * task profiling. |
| 655 | */ |
| 656 | boot_params->perf_clk_frequency = ivpu_hw_profiling_freq_get(vdev); |
| 657 | |
| 658 | /* |
| 659 | * Uncached region of VPU address space, covers IPC buffers, job queues |
| 660 | * and log buffers, programmable to L2$ Uncached by VPU MTRR |
| 661 | */ |
| 662 | boot_params->shared_region_base = vdev->hw->ranges.global.start; |
| 663 | boot_params->shared_region_size = vdev->hw->ranges.global.end - |
| 664 | vdev->hw->ranges.global.start; |
| 665 | |
| 666 | boot_params->ipc_header_area_start = ipc_mem_rx->vpu_addr; |
| 667 | boot_params->ipc_header_area_size = ivpu_bo_size(bo: ipc_mem_rx) / 2; |
| 668 | |
| 669 | boot_params->ipc_payload_area_start = ipc_mem_rx->vpu_addr + ivpu_bo_size(bo: ipc_mem_rx) / 2; |
| 670 | boot_params->ipc_payload_area_size = ivpu_bo_size(bo: ipc_mem_rx) / 2; |
| 671 | |
| 672 | if (ivpu_hw_ip_gen(vdev) == IVPU_HW_IP_37XX) { |
| 673 | boot_params->global_aliased_pio_base = vdev->hw->ranges.user.start; |
| 674 | boot_params->global_aliased_pio_size = ivpu_hw_range_size(range: &vdev->hw->ranges.user); |
| 675 | } |
| 676 | |
| 677 | /* Allow configuration for L2C_PAGE_TABLE with boot param value */ |
| 678 | boot_params->autoconfig = 1; |
| 679 | |
| 680 | /* Enable L2 cache for first 2GB of high memory */ |
| 681 | boot_params->cache_defaults[VPU_BOOT_L2_CACHE_CFG_NN].use = 1; |
| 682 | boot_params->cache_defaults[VPU_BOOT_L2_CACHE_CFG_NN].cfg = |
| 683 | ADDR_TO_L2_CACHE_CFG(vdev->hw->ranges.shave.start); |
| 684 | |
| 685 | if (vdev->fw->mem_shave_nn) |
| 686 | boot_params->shave_nn_fw_base = vdev->fw->mem_shave_nn->vpu_addr; |
| 687 | |
| 688 | boot_params->watchdog_irq_mss = WATCHDOG_MSS_REDIRECT; |
| 689 | boot_params->watchdog_irq_nce = WATCHDOG_NCE_REDIRECT; |
| 690 | boot_params->si_stepping = ivpu_revision(vdev); |
| 691 | boot_params->device_id = ivpu_device_id(vdev); |
| 692 | boot_params->feature_exclusion = vdev->hw->tile_fuse; |
| 693 | boot_params->sku = vdev->hw->sku; |
| 694 | |
| 695 | boot_params->min_freq_pll_ratio = vdev->hw->pll.min_ratio; |
| 696 | boot_params->pn_freq_pll_ratio = vdev->hw->pll.pn_ratio; |
| 697 | boot_params->max_freq_pll_ratio = vdev->hw->pll.max_ratio; |
| 698 | |
| 699 | boot_params->default_trace_level = vdev->fw->trace_level; |
| 700 | boot_params->tracing_buff_message_format_mask = BIT(VPU_TRACING_FORMAT_STRING); |
| 701 | boot_params->trace_destination_mask = vdev->fw->trace_destination_mask; |
| 702 | boot_params->trace_hw_component_mask = vdev->fw->trace_hw_component_mask; |
| 703 | boot_params->crit_tracing_buff_addr = vdev->fw->mem_log_crit->vpu_addr; |
| 704 | boot_params->crit_tracing_buff_size = ivpu_bo_size(bo: vdev->fw->mem_log_crit); |
| 705 | boot_params->verbose_tracing_buff_addr = vdev->fw->mem_log_verb->vpu_addr; |
| 706 | boot_params->verbose_tracing_buff_size = ivpu_bo_size(bo: vdev->fw->mem_log_verb); |
| 707 | |
| 708 | boot_params->punit_telemetry_sram_base = ivpu_hw_telemetry_offset_get(vdev); |
| 709 | boot_params->punit_telemetry_sram_size = ivpu_hw_telemetry_size_get(vdev); |
| 710 | boot_params->vpu_telemetry_enable = ivpu_hw_telemetry_enable_get(vdev); |
| 711 | boot_params->vpu_scheduling_mode = vdev->fw->sched_mode; |
| 712 | if (vdev->fw->sched_mode == VPU_SCHEDULING_MODE_HW) |
| 713 | boot_params->vpu_focus_present_timer_ms = IVPU_FOCUS_PRESENT_TIMER_MS; |
| 714 | boot_params->dvfs_mode = vdev->fw->dvfs_mode; |
| 715 | if (!IVPU_WA(disable_d0i3_msg)) |
| 716 | boot_params->d0i3_delayed_entry = 1; |
| 717 | boot_params->d0i3_residency_time_us = 0; |
| 718 | boot_params->d0i3_entry_vpu_ts = 0; |
| 719 | if (IVPU_WA(disable_d0i2)) |
| 720 | boot_params->power_profile |= BIT(1); |
| 721 | boot_params->vpu_uses_ecc_mca_signal = |
| 722 | ivpu_hw_uses_ecc_mca_signal(vdev) ? VPU_BOOT_MCA_ECC_BOTH : 0; |
| 723 | |
| 724 | boot_params->system_time_us = ktime_to_us(kt: ktime_get_real()); |
| 725 | wmb(); /* Flush WC buffers after writing bootparams */ |
| 726 | |
| 727 | ivpu_fw_boot_params_print(vdev, boot_params); |
| 728 | } |
| 729 | |