| 1 | // SPDX-License-Identifier: MIT |
| 2 | /* |
| 3 | * Copyright © 2025 Intel Corporation |
| 4 | */ |
| 5 | |
| 6 | #include <linux/component.h> |
| 7 | #include <linux/delay.h> |
| 8 | #include <linux/firmware.h> |
| 9 | |
| 10 | #include <drm/drm_managed.h> |
| 11 | #include <drm/intel/i915_component.h> |
| 12 | #include <drm/intel/intel_lb_mei_interface.h> |
| 13 | #include <drm/drm_print.h> |
| 14 | |
| 15 | #include "xe_device.h" |
| 16 | #include "xe_late_bind_fw.h" |
| 17 | #include "xe_pcode.h" |
| 18 | #include "xe_pcode_api.h" |
| 19 | #include "xe_pm.h" |
| 20 | |
| 21 | /* |
| 22 | * The component should load quite quickly in most cases, but it could take |
| 23 | * a bit. Using a very big timeout just to cover the worst case scenario |
| 24 | */ |
| 25 | #define LB_INIT_TIMEOUT_MS 20000 |
| 26 | |
| 27 | /* |
| 28 | * Retry interval set to 6 seconds, in steps of 200 ms, to allow time for |
| 29 | * other OS components to release the MEI CL handle |
| 30 | */ |
| 31 | #define LB_FW_LOAD_RETRY_MAXCOUNT 30 |
| 32 | #define LB_FW_LOAD_RETRY_PAUSE_MS 200 |
| 33 | |
| 34 | static const u32 fw_id_to_type[] = { |
| 35 | [XE_LB_FW_FAN_CONTROL] = INTEL_LB_TYPE_FAN_CONTROL, |
| 36 | }; |
| 37 | |
| 38 | static const char * const fw_id_to_name[] = { |
| 39 | [XE_LB_FW_FAN_CONTROL] = "fan_control" , |
| 40 | }; |
| 41 | |
| 42 | static struct xe_device * |
| 43 | late_bind_to_xe(struct xe_late_bind *late_bind) |
| 44 | { |
| 45 | return container_of(late_bind, struct xe_device, late_bind); |
| 46 | } |
| 47 | |
| 48 | static struct xe_device * |
| 49 | late_bind_fw_to_xe(struct xe_late_bind_fw *lb_fw) |
| 50 | { |
| 51 | return container_of(lb_fw, struct xe_device, late_bind.late_bind_fw[lb_fw->id]); |
| 52 | } |
| 53 | |
| 54 | /* Refer to the "Late Bind based Firmware Layout" documentation entry for details */ |
| 55 | static int (struct xe_late_bind_fw *lb_fw, |
| 56 | const void *data, size_t size, const char *manifest_entry) |
| 57 | { |
| 58 | struct xe_device *xe = late_bind_fw_to_xe(lb_fw); |
| 59 | const struct gsc_cpd_header_v2 * = data; |
| 60 | const struct gsc_manifest_header *manifest; |
| 61 | const struct gsc_cpd_entry *entry; |
| 62 | size_t min_size = sizeof(*header); |
| 63 | u32 offset = 0; |
| 64 | int i; |
| 65 | |
| 66 | /* manifest_entry is mandatory */ |
| 67 | xe_assert(xe, manifest_entry); |
| 68 | |
| 69 | if (size < min_size || header->header_marker != GSC_CPD_HEADER_MARKER) |
| 70 | return -ENOENT; |
| 71 | |
| 72 | if (header->header_length < sizeof(struct gsc_cpd_header_v2)) { |
| 73 | drm_err(&xe->drm, "%s late binding fw: Invalid CPD header length %u!\n" , |
| 74 | fw_id_to_name[lb_fw->id], header->header_length); |
| 75 | return -EINVAL; |
| 76 | } |
| 77 | |
| 78 | min_size = header->header_length + sizeof(struct gsc_cpd_entry) * header->num_of_entries; |
| 79 | if (size < min_size) { |
| 80 | drm_err(&xe->drm, "%s late binding fw: too small! %zu < %zu\n" , |
| 81 | fw_id_to_name[lb_fw->id], size, min_size); |
| 82 | return -ENODATA; |
| 83 | } |
| 84 | |
| 85 | /* Look for the manifest first */ |
| 86 | entry = (void *)header + header->header_length; |
| 87 | for (i = 0; i < header->num_of_entries; i++, entry++) |
| 88 | if (strcmp(entry->name, manifest_entry) == 0) |
| 89 | offset = entry->offset & GSC_CPD_ENTRY_OFFSET_MASK; |
| 90 | |
| 91 | if (!offset) { |
| 92 | drm_err(&xe->drm, "%s late binding fw: Failed to find manifest_entry\n" , |
| 93 | fw_id_to_name[lb_fw->id]); |
| 94 | return -ENODATA; |
| 95 | } |
| 96 | |
| 97 | min_size = offset + sizeof(struct gsc_manifest_header); |
| 98 | if (size < min_size) { |
| 99 | drm_err(&xe->drm, "%s late binding fw: too small! %zu < %zu\n" , |
| 100 | fw_id_to_name[lb_fw->id], size, min_size); |
| 101 | return -ENODATA; |
| 102 | } |
| 103 | |
| 104 | manifest = data + offset; |
| 105 | |
| 106 | lb_fw->version = manifest->fw_version; |
| 107 | |
| 108 | return 0; |
| 109 | } |
| 110 | |
| 111 | /* Refer to the "Late Bind based Firmware Layout" documentation entry for details */ |
| 112 | static int parse_lb_layout(struct xe_late_bind_fw *lb_fw, |
| 113 | const void *data, size_t size, const char *fpt_entry) |
| 114 | { |
| 115 | struct xe_device *xe = late_bind_fw_to_xe(lb_fw); |
| 116 | const struct csc_fpt_header * = data; |
| 117 | const struct csc_fpt_entry *entry; |
| 118 | size_t min_size = sizeof(*header); |
| 119 | u32 offset = 0; |
| 120 | int i; |
| 121 | |
| 122 | /* fpt_entry is mandatory */ |
| 123 | xe_assert(xe, fpt_entry); |
| 124 | |
| 125 | if (size < min_size || header->header_marker != CSC_FPT_HEADER_MARKER) |
| 126 | return -ENOENT; |
| 127 | |
| 128 | if (header->header_length < sizeof(struct csc_fpt_header)) { |
| 129 | drm_err(&xe->drm, "%s late binding fw: Invalid FPT header length %u!\n" , |
| 130 | fw_id_to_name[lb_fw->id], header->header_length); |
| 131 | return -EINVAL; |
| 132 | } |
| 133 | |
| 134 | min_size = header->header_length + sizeof(struct csc_fpt_entry) * header->num_of_entries; |
| 135 | if (size < min_size) { |
| 136 | drm_err(&xe->drm, "%s late binding fw: too small! %zu < %zu\n" , |
| 137 | fw_id_to_name[lb_fw->id], size, min_size); |
| 138 | return -ENODATA; |
| 139 | } |
| 140 | |
| 141 | /* Look for the cpd header first */ |
| 142 | entry = (void *)header + header->header_length; |
| 143 | for (i = 0; i < header->num_of_entries; i++, entry++) |
| 144 | if (strcmp(entry->name, fpt_entry) == 0) |
| 145 | offset = entry->offset; |
| 146 | |
| 147 | if (!offset) { |
| 148 | drm_err(&xe->drm, "%s late binding fw: Failed to find fpt_entry\n" , |
| 149 | fw_id_to_name[lb_fw->id]); |
| 150 | return -ENODATA; |
| 151 | } |
| 152 | |
| 153 | min_size = offset + sizeof(struct gsc_cpd_header_v2); |
| 154 | if (size < min_size) { |
| 155 | drm_err(&xe->drm, "%s late binding fw: too small! %zu < %zu\n" , |
| 156 | fw_id_to_name[lb_fw->id], size, min_size); |
| 157 | return -ENODATA; |
| 158 | } |
| 159 | |
| 160 | return parse_cpd_header(lb_fw, data: data + offset, size: size - offset, manifest_entry: "LTES.man" ); |
| 161 | } |
| 162 | |
| 163 | static const char *xe_late_bind_parse_status(uint32_t status) |
| 164 | { |
| 165 | switch (status) { |
| 166 | case INTEL_LB_STATUS_SUCCESS: |
| 167 | return "success" ; |
| 168 | case INTEL_LB_STATUS_4ID_MISMATCH: |
| 169 | return "4Id Mismatch" ; |
| 170 | case INTEL_LB_STATUS_ARB_FAILURE: |
| 171 | return "ARB Failure" ; |
| 172 | case INTEL_LB_STATUS_GENERAL_ERROR: |
| 173 | return "General Error" ; |
| 174 | case INTEL_LB_STATUS_INVALID_PARAMS: |
| 175 | return "Invalid Params" ; |
| 176 | case INTEL_LB_STATUS_INVALID_SIGNATURE: |
| 177 | return "Invalid Signature" ; |
| 178 | case INTEL_LB_STATUS_INVALID_PAYLOAD: |
| 179 | return "Invalid Payload" ; |
| 180 | case INTEL_LB_STATUS_TIMEOUT: |
| 181 | return "Timeout" ; |
| 182 | default: |
| 183 | return "Unknown error" ; |
| 184 | } |
| 185 | } |
| 186 | |
| 187 | static int xe_late_bind_fw_num_fans(struct xe_late_bind *late_bind, u32 *num_fans) |
| 188 | { |
| 189 | struct xe_device *xe = late_bind_to_xe(late_bind); |
| 190 | struct xe_tile *root_tile = xe_device_get_root_tile(xe); |
| 191 | |
| 192 | return xe_pcode_read(tile: root_tile, |
| 193 | PCODE_MBOX(FAN_SPEED_CONTROL, FSC_READ_NUM_FANS, 0), val: num_fans, NULL); |
| 194 | } |
| 195 | |
| 196 | void xe_late_bind_wait_for_worker_completion(struct xe_late_bind *late_bind) |
| 197 | { |
| 198 | struct xe_device *xe = late_bind_to_xe(late_bind); |
| 199 | struct xe_late_bind_fw *lbfw; |
| 200 | int fw_id; |
| 201 | |
| 202 | for (fw_id = 0; fw_id < XE_LB_FW_MAX_ID; fw_id++) { |
| 203 | lbfw = &late_bind->late_bind_fw[fw_id]; |
| 204 | if (lbfw->payload && late_bind->wq) { |
| 205 | drm_dbg(&xe->drm, "Flush work: load %s firmware\n" , |
| 206 | fw_id_to_name[lbfw->id]); |
| 207 | flush_work(work: &lbfw->work); |
| 208 | } |
| 209 | } |
| 210 | } |
| 211 | |
| 212 | static void xe_late_bind_work(struct work_struct *work) |
| 213 | { |
| 214 | struct xe_late_bind_fw *lbfw = container_of(work, struct xe_late_bind_fw, work); |
| 215 | struct xe_late_bind *late_bind = container_of(lbfw, struct xe_late_bind, |
| 216 | late_bind_fw[lbfw->id]); |
| 217 | struct xe_device *xe = late_bind_to_xe(late_bind); |
| 218 | int retry = LB_FW_LOAD_RETRY_MAXCOUNT; |
| 219 | int ret; |
| 220 | int slept; |
| 221 | |
| 222 | xe_device_assert_mem_access(xe); |
| 223 | |
| 224 | /* we can queue this before the component is bound */ |
| 225 | for (slept = 0; slept < LB_INIT_TIMEOUT_MS; slept += 100) { |
| 226 | if (late_bind->component.ops) |
| 227 | break; |
| 228 | msleep(msecs: 100); |
| 229 | } |
| 230 | |
| 231 | if (!late_bind->component.ops) { |
| 232 | drm_err(&xe->drm, "Late bind component not bound\n" ); |
| 233 | /* Do not re-attempt fw load */ |
| 234 | drmm_kfree(dev: &xe->drm, data: (void *)lbfw->payload); |
| 235 | lbfw->payload = NULL; |
| 236 | goto out; |
| 237 | } |
| 238 | |
| 239 | drm_dbg(&xe->drm, "Load %s firmware\n" , fw_id_to_name[lbfw->id]); |
| 240 | |
| 241 | do { |
| 242 | ret = late_bind->component.ops->push_payload(late_bind->component.mei_dev, |
| 243 | lbfw->type, |
| 244 | lbfw->flags, |
| 245 | lbfw->payload, |
| 246 | lbfw->payload_size); |
| 247 | if (!ret) |
| 248 | break; |
| 249 | msleep(LB_FW_LOAD_RETRY_PAUSE_MS); |
| 250 | } while (--retry && ret == -EBUSY); |
| 251 | |
| 252 | if (!ret) { |
| 253 | drm_dbg(&xe->drm, "Load %s firmware successful\n" , |
| 254 | fw_id_to_name[lbfw->id]); |
| 255 | goto out; |
| 256 | } |
| 257 | |
| 258 | if (ret > 0) |
| 259 | drm_err(&xe->drm, "Load %s firmware failed with err %d, %s\n" , |
| 260 | fw_id_to_name[lbfw->id], ret, xe_late_bind_parse_status(ret)); |
| 261 | else |
| 262 | drm_err(&xe->drm, "Load %s firmware failed with err %d" , |
| 263 | fw_id_to_name[lbfw->id], ret); |
| 264 | /* Do not re-attempt fw load */ |
| 265 | drmm_kfree(dev: &xe->drm, data: (void *)lbfw->payload); |
| 266 | lbfw->payload = NULL; |
| 267 | |
| 268 | out: |
| 269 | xe_pm_runtime_put(xe); |
| 270 | } |
| 271 | |
| 272 | int xe_late_bind_fw_load(struct xe_late_bind *late_bind) |
| 273 | { |
| 274 | struct xe_device *xe = late_bind_to_xe(late_bind); |
| 275 | struct xe_late_bind_fw *lbfw; |
| 276 | int fw_id; |
| 277 | |
| 278 | if (!late_bind->component_added) |
| 279 | return -ENODEV; |
| 280 | |
| 281 | if (late_bind->disable) |
| 282 | return 0; |
| 283 | |
| 284 | for (fw_id = 0; fw_id < XE_LB_FW_MAX_ID; fw_id++) { |
| 285 | lbfw = &late_bind->late_bind_fw[fw_id]; |
| 286 | if (lbfw->payload) { |
| 287 | xe_pm_runtime_get_noresume(xe); |
| 288 | queue_work(wq: late_bind->wq, work: &lbfw->work); |
| 289 | } |
| 290 | } |
| 291 | return 0; |
| 292 | } |
| 293 | |
| 294 | static int __xe_late_bind_fw_init(struct xe_late_bind *late_bind, u32 fw_id) |
| 295 | { |
| 296 | struct xe_device *xe = late_bind_to_xe(late_bind); |
| 297 | struct pci_dev *pdev = to_pci_dev(xe->drm.dev); |
| 298 | struct xe_late_bind_fw *lb_fw; |
| 299 | const struct firmware *fw; |
| 300 | u32 num_fans; |
| 301 | int ret; |
| 302 | |
| 303 | if (fw_id >= XE_LB_FW_MAX_ID) |
| 304 | return -EINVAL; |
| 305 | |
| 306 | lb_fw = &late_bind->late_bind_fw[fw_id]; |
| 307 | |
| 308 | lb_fw->id = fw_id; |
| 309 | lb_fw->type = fw_id_to_type[lb_fw->id]; |
| 310 | lb_fw->flags &= ~INTEL_LB_FLAG_IS_PERSISTENT; |
| 311 | |
| 312 | if (lb_fw->type == INTEL_LB_TYPE_FAN_CONTROL) { |
| 313 | ret = xe_late_bind_fw_num_fans(late_bind, num_fans: &num_fans); |
| 314 | if (ret) { |
| 315 | drm_dbg(&xe->drm, "Failed to read number of fans: %d\n" , ret); |
| 316 | return 0; /* Not a fatal error, continue without fan control */ |
| 317 | } |
| 318 | drm_dbg(&xe->drm, "Number of Fans: %d\n" , num_fans); |
| 319 | if (!num_fans) |
| 320 | return 0; |
| 321 | } |
| 322 | |
| 323 | snprintf(buf: lb_fw->blob_path, size: sizeof(lb_fw->blob_path), fmt: "xe/%s_8086_%04x_%04x_%04x.bin" , |
| 324 | fw_id_to_name[lb_fw->id], pdev->device, |
| 325 | pdev->subsystem_vendor, pdev->subsystem_device); |
| 326 | |
| 327 | drm_dbg(&xe->drm, "Request late binding firmware %s\n" , lb_fw->blob_path); |
| 328 | ret = firmware_request_nowarn(fw: &fw, name: lb_fw->blob_path, device: xe->drm.dev); |
| 329 | if (ret) { |
| 330 | drm_dbg(&xe->drm, "%s late binding fw not available for current device" , |
| 331 | fw_id_to_name[lb_fw->id]); |
| 332 | return 0; |
| 333 | } |
| 334 | |
| 335 | if (fw->size > XE_LB_MAX_PAYLOAD_SIZE) { |
| 336 | drm_err(&xe->drm, "Firmware %s size %zu is larger than max pay load size %u\n" , |
| 337 | lb_fw->blob_path, fw->size, XE_LB_MAX_PAYLOAD_SIZE); |
| 338 | release_firmware(fw); |
| 339 | return -ENODATA; |
| 340 | } |
| 341 | |
| 342 | ret = parse_lb_layout(lb_fw, data: fw->data, size: fw->size, fpt_entry: "LTES" ); |
| 343 | if (ret) |
| 344 | return ret; |
| 345 | |
| 346 | lb_fw->payload_size = fw->size; |
| 347 | lb_fw->payload = drmm_kzalloc(dev: &xe->drm, size: lb_fw->payload_size, GFP_KERNEL); |
| 348 | if (!lb_fw->payload) { |
| 349 | release_firmware(fw); |
| 350 | return -ENOMEM; |
| 351 | } |
| 352 | |
| 353 | drm_info(&xe->drm, "Using %s firmware from %s version %u.%u.%u.%u\n" , |
| 354 | fw_id_to_name[lb_fw->id], lb_fw->blob_path, |
| 355 | lb_fw->version.major, lb_fw->version.minor, |
| 356 | lb_fw->version.hotfix, lb_fw->version.build); |
| 357 | |
| 358 | memcpy((void *)lb_fw->payload, fw->data, lb_fw->payload_size); |
| 359 | release_firmware(fw); |
| 360 | INIT_WORK(&lb_fw->work, xe_late_bind_work); |
| 361 | |
| 362 | return 0; |
| 363 | } |
| 364 | |
| 365 | static int xe_late_bind_fw_init(struct xe_late_bind *late_bind) |
| 366 | { |
| 367 | int ret; |
| 368 | int fw_id; |
| 369 | |
| 370 | late_bind->wq = alloc_ordered_workqueue("late-bind-ordered-wq" , 0); |
| 371 | if (!late_bind->wq) |
| 372 | return -ENOMEM; |
| 373 | |
| 374 | for (fw_id = 0; fw_id < XE_LB_FW_MAX_ID; fw_id++) { |
| 375 | ret = __xe_late_bind_fw_init(late_bind, fw_id); |
| 376 | if (ret) |
| 377 | return ret; |
| 378 | } |
| 379 | |
| 380 | return 0; |
| 381 | } |
| 382 | |
| 383 | static int xe_late_bind_component_bind(struct device *xe_kdev, |
| 384 | struct device *mei_kdev, void *data) |
| 385 | { |
| 386 | struct xe_device *xe = kdev_to_xe_device(kdev: xe_kdev); |
| 387 | struct xe_late_bind *late_bind = &xe->late_bind; |
| 388 | |
| 389 | late_bind->component.ops = data; |
| 390 | late_bind->component.mei_dev = mei_kdev; |
| 391 | |
| 392 | return 0; |
| 393 | } |
| 394 | |
| 395 | static void xe_late_bind_component_unbind(struct device *xe_kdev, |
| 396 | struct device *mei_kdev, void *data) |
| 397 | { |
| 398 | struct xe_device *xe = kdev_to_xe_device(kdev: xe_kdev); |
| 399 | struct xe_late_bind *late_bind = &xe->late_bind; |
| 400 | |
| 401 | xe_late_bind_wait_for_worker_completion(late_bind); |
| 402 | |
| 403 | late_bind->component.ops = NULL; |
| 404 | } |
| 405 | |
| 406 | static const struct component_ops xe_late_bind_component_ops = { |
| 407 | .bind = xe_late_bind_component_bind, |
| 408 | .unbind = xe_late_bind_component_unbind, |
| 409 | }; |
| 410 | |
| 411 | static void xe_late_bind_remove(void *arg) |
| 412 | { |
| 413 | struct xe_late_bind *late_bind = arg; |
| 414 | struct xe_device *xe = late_bind_to_xe(late_bind); |
| 415 | |
| 416 | xe_late_bind_wait_for_worker_completion(late_bind); |
| 417 | |
| 418 | late_bind->component_added = false; |
| 419 | |
| 420 | component_del(xe->drm.dev, &xe_late_bind_component_ops); |
| 421 | if (late_bind->wq) { |
| 422 | destroy_workqueue(wq: late_bind->wq); |
| 423 | late_bind->wq = NULL; |
| 424 | } |
| 425 | } |
| 426 | |
| 427 | /** |
| 428 | * xe_late_bind_init() - add xe mei late binding component |
| 429 | * @late_bind: pointer to late bind structure. |
| 430 | * |
| 431 | * Return: 0 if the initialization was successful, a negative errno otherwise. |
| 432 | */ |
| 433 | int xe_late_bind_init(struct xe_late_bind *late_bind) |
| 434 | { |
| 435 | struct xe_device *xe = late_bind_to_xe(late_bind); |
| 436 | int err; |
| 437 | |
| 438 | if (!xe->info.has_late_bind) |
| 439 | return 0; |
| 440 | |
| 441 | if (!IS_ENABLED(CONFIG_INTEL_MEI_LB) || !IS_ENABLED(CONFIG_INTEL_MEI_GSC)) { |
| 442 | drm_info(&xe->drm, "Can't init xe mei late bind missing mei component\n" ); |
| 443 | return 0; |
| 444 | } |
| 445 | |
| 446 | err = component_add_typed(dev: xe->drm.dev, ops: &xe_late_bind_component_ops, |
| 447 | subcomponent: INTEL_COMPONENT_LB); |
| 448 | if (err < 0) { |
| 449 | drm_err(&xe->drm, "Failed to add mei late bind component (%pe)\n" , ERR_PTR(err)); |
| 450 | return err; |
| 451 | } |
| 452 | |
| 453 | late_bind->component_added = true; |
| 454 | |
| 455 | err = devm_add_action_or_reset(xe->drm.dev, xe_late_bind_remove, late_bind); |
| 456 | if (err) |
| 457 | return err; |
| 458 | |
| 459 | err = xe_late_bind_fw_init(late_bind); |
| 460 | if (err) |
| 461 | return err; |
| 462 | |
| 463 | return xe_late_bind_fw_load(late_bind); |
| 464 | } |
| 465 | |