xref: /linux/drivers/gpu/drm/xe/xe_late_bind_fw.c (revision 284fc30e66e602a5df58393860f67477d6a79339)
1918bd789SBadal Nilawar // SPDX-License-Identifier: MIT
2918bd789SBadal Nilawar /*
3918bd789SBadal Nilawar  * Copyright © 2025 Intel Corporation
4918bd789SBadal Nilawar  */
5918bd789SBadal Nilawar 
6918bd789SBadal Nilawar #include <linux/component.h>
7918bd789SBadal Nilawar #include <linux/delay.h>
845832bf9SBadal Nilawar #include <linux/firmware.h>
9918bd789SBadal Nilawar 
10918bd789SBadal Nilawar #include <drm/drm_managed.h>
11918bd789SBadal Nilawar #include <drm/intel/i915_component.h>
12918bd789SBadal Nilawar #include <drm/intel/intel_lb_mei_interface.h>
13918bd789SBadal Nilawar #include <drm/drm_print.h>
14918bd789SBadal Nilawar 
15918bd789SBadal Nilawar #include "xe_device.h"
16918bd789SBadal Nilawar #include "xe_late_bind_fw.h"
1745832bf9SBadal Nilawar #include "xe_pcode.h"
1845832bf9SBadal Nilawar #include "xe_pcode_api.h"
19691a54adSBadal Nilawar #include "xe_pm.h"
20691a54adSBadal Nilawar 
21691a54adSBadal Nilawar /*
22691a54adSBadal Nilawar  * The component should load quite quickly in most cases, but it could take
23691a54adSBadal Nilawar  * a bit. Using a very big timeout just to cover the worst case scenario
24691a54adSBadal Nilawar  */
25691a54adSBadal Nilawar #define LB_INIT_TIMEOUT_MS 20000
26691a54adSBadal Nilawar 
27691a54adSBadal Nilawar /*
28691a54adSBadal Nilawar  * Retry interval set to 6 seconds, in steps of 200 ms, to allow time for
29691a54adSBadal Nilawar  * other OS components to release the MEI CL handle
30691a54adSBadal Nilawar  */
31691a54adSBadal Nilawar #define LB_FW_LOAD_RETRY_MAXCOUNT 30
32691a54adSBadal Nilawar #define LB_FW_LOAD_RETRY_PAUSE_MS 200
3345832bf9SBadal Nilawar 
3445832bf9SBadal Nilawar static const u32 fw_id_to_type[] = {
3545832bf9SBadal Nilawar 		[XE_LB_FW_FAN_CONTROL] = INTEL_LB_TYPE_FAN_CONTROL,
3645832bf9SBadal Nilawar 	};
3745832bf9SBadal Nilawar 
3845832bf9SBadal Nilawar static const char * const fw_id_to_name[] = {
3945832bf9SBadal Nilawar 		[XE_LB_FW_FAN_CONTROL] = "fan_control",
4045832bf9SBadal Nilawar 	};
41918bd789SBadal Nilawar 
42918bd789SBadal Nilawar static struct xe_device *
late_bind_to_xe(struct xe_late_bind * late_bind)43918bd789SBadal Nilawar late_bind_to_xe(struct xe_late_bind *late_bind)
44918bd789SBadal Nilawar {
45918bd789SBadal Nilawar 	return container_of(late_bind, struct xe_device, late_bind);
46918bd789SBadal Nilawar }
47918bd789SBadal Nilawar 
48efa29317SBadal Nilawar static struct xe_device *
late_bind_fw_to_xe(struct xe_late_bind_fw * lb_fw)49efa29317SBadal Nilawar late_bind_fw_to_xe(struct xe_late_bind_fw *lb_fw)
50efa29317SBadal Nilawar {
51efa29317SBadal Nilawar 	return container_of(lb_fw, struct xe_device, late_bind.late_bind_fw[lb_fw->id]);
52efa29317SBadal Nilawar }
53efa29317SBadal Nilawar 
54efa29317SBadal Nilawar /* Refer to the "Late Bind based Firmware Layout" documentation entry for details */
parse_cpd_header(struct xe_late_bind_fw * lb_fw,const void * data,size_t size,const char * manifest_entry)55efa29317SBadal Nilawar static int parse_cpd_header(struct xe_late_bind_fw *lb_fw,
56efa29317SBadal Nilawar 			    const void *data, size_t size, const char *manifest_entry)
57efa29317SBadal Nilawar {
58efa29317SBadal Nilawar 	struct xe_device *xe = late_bind_fw_to_xe(lb_fw);
59efa29317SBadal Nilawar 	const struct gsc_cpd_header_v2 *header = data;
60efa29317SBadal Nilawar 	const struct gsc_manifest_header *manifest;
61efa29317SBadal Nilawar 	const struct gsc_cpd_entry *entry;
62efa29317SBadal Nilawar 	size_t min_size = sizeof(*header);
635b440a8bSColin Ian King 	u32 offset = 0;
64efa29317SBadal Nilawar 	int i;
65efa29317SBadal Nilawar 
66efa29317SBadal Nilawar 	/* manifest_entry is mandatory */
67efa29317SBadal Nilawar 	xe_assert(xe, manifest_entry);
68efa29317SBadal Nilawar 
69efa29317SBadal Nilawar 	if (size < min_size || header->header_marker != GSC_CPD_HEADER_MARKER)
70efa29317SBadal Nilawar 		return -ENOENT;
71efa29317SBadal Nilawar 
72efa29317SBadal Nilawar 	if (header->header_length < sizeof(struct gsc_cpd_header_v2)) {
73efa29317SBadal Nilawar 		drm_err(&xe->drm, "%s late binding fw: Invalid CPD header length %u!\n",
74efa29317SBadal Nilawar 			fw_id_to_name[lb_fw->id], header->header_length);
75efa29317SBadal Nilawar 		return -EINVAL;
76efa29317SBadal Nilawar 	}
77efa29317SBadal Nilawar 
78efa29317SBadal Nilawar 	min_size = header->header_length + sizeof(struct gsc_cpd_entry) * header->num_of_entries;
79efa29317SBadal Nilawar 	if (size < min_size) {
80efa29317SBadal Nilawar 		drm_err(&xe->drm, "%s late binding fw: too small! %zu < %zu\n",
81efa29317SBadal Nilawar 			fw_id_to_name[lb_fw->id], size, min_size);
82efa29317SBadal Nilawar 		return -ENODATA;
83efa29317SBadal Nilawar 	}
84efa29317SBadal Nilawar 
85efa29317SBadal Nilawar 	/* Look for the manifest first */
86efa29317SBadal Nilawar 	entry = (void *)header + header->header_length;
87efa29317SBadal Nilawar 	for (i = 0; i < header->num_of_entries; i++, entry++)
88efa29317SBadal Nilawar 		if (strcmp(entry->name, manifest_entry) == 0)
89efa29317SBadal Nilawar 			offset = entry->offset & GSC_CPD_ENTRY_OFFSET_MASK;
90efa29317SBadal Nilawar 
91efa29317SBadal Nilawar 	if (!offset) {
92efa29317SBadal Nilawar 		drm_err(&xe->drm, "%s late binding fw: Failed to find manifest_entry\n",
93efa29317SBadal Nilawar 			fw_id_to_name[lb_fw->id]);
94efa29317SBadal Nilawar 		return -ENODATA;
95efa29317SBadal Nilawar 	}
96efa29317SBadal Nilawar 
97efa29317SBadal Nilawar 	min_size = offset + sizeof(struct gsc_manifest_header);
98efa29317SBadal Nilawar 	if (size < min_size) {
99efa29317SBadal Nilawar 		drm_err(&xe->drm, "%s late binding fw: too small! %zu < %zu\n",
100efa29317SBadal Nilawar 			fw_id_to_name[lb_fw->id], size, min_size);
101efa29317SBadal Nilawar 		return -ENODATA;
102efa29317SBadal Nilawar 	}
103efa29317SBadal Nilawar 
104efa29317SBadal Nilawar 	manifest = data + offset;
105efa29317SBadal Nilawar 
106efa29317SBadal Nilawar 	lb_fw->version = manifest->fw_version;
107efa29317SBadal Nilawar 
108efa29317SBadal Nilawar 	return 0;
109efa29317SBadal Nilawar }
110efa29317SBadal Nilawar 
111efa29317SBadal Nilawar /* Refer to the "Late Bind based Firmware Layout" documentation entry for details */
parse_lb_layout(struct xe_late_bind_fw * lb_fw,const void * data,size_t size,const char * fpt_entry)112efa29317SBadal Nilawar static int parse_lb_layout(struct xe_late_bind_fw *lb_fw,
113efa29317SBadal Nilawar 			   const void *data, size_t size, const char *fpt_entry)
114efa29317SBadal Nilawar {
115efa29317SBadal Nilawar 	struct xe_device *xe = late_bind_fw_to_xe(lb_fw);
116efa29317SBadal Nilawar 	const struct csc_fpt_header *header = data;
117efa29317SBadal Nilawar 	const struct csc_fpt_entry *entry;
118efa29317SBadal Nilawar 	size_t min_size = sizeof(*header);
1195b440a8bSColin Ian King 	u32 offset = 0;
120efa29317SBadal Nilawar 	int i;
121efa29317SBadal Nilawar 
122efa29317SBadal Nilawar 	/* fpt_entry is mandatory */
123efa29317SBadal Nilawar 	xe_assert(xe, fpt_entry);
124efa29317SBadal Nilawar 
125efa29317SBadal Nilawar 	if (size < min_size || header->header_marker != CSC_FPT_HEADER_MARKER)
126efa29317SBadal Nilawar 		return -ENOENT;
127efa29317SBadal Nilawar 
128efa29317SBadal Nilawar 	if (header->header_length < sizeof(struct csc_fpt_header)) {
129efa29317SBadal Nilawar 		drm_err(&xe->drm, "%s late binding fw: Invalid FPT header length %u!\n",
130efa29317SBadal Nilawar 			fw_id_to_name[lb_fw->id], header->header_length);
131efa29317SBadal Nilawar 		return -EINVAL;
132efa29317SBadal Nilawar 	}
133efa29317SBadal Nilawar 
134efa29317SBadal Nilawar 	min_size = header->header_length + sizeof(struct csc_fpt_entry) * header->num_of_entries;
135efa29317SBadal Nilawar 	if (size < min_size) {
136efa29317SBadal Nilawar 		drm_err(&xe->drm, "%s late binding fw: too small! %zu < %zu\n",
137efa29317SBadal Nilawar 			fw_id_to_name[lb_fw->id], size, min_size);
138efa29317SBadal Nilawar 		return -ENODATA;
139efa29317SBadal Nilawar 	}
140efa29317SBadal Nilawar 
141efa29317SBadal Nilawar 	/* Look for the cpd header first */
142efa29317SBadal Nilawar 	entry = (void *)header + header->header_length;
143efa29317SBadal Nilawar 	for (i = 0; i < header->num_of_entries; i++, entry++)
144efa29317SBadal Nilawar 		if (strcmp(entry->name, fpt_entry) == 0)
145efa29317SBadal Nilawar 			offset = entry->offset;
146efa29317SBadal Nilawar 
147efa29317SBadal Nilawar 	if (!offset) {
148efa29317SBadal Nilawar 		drm_err(&xe->drm, "%s late binding fw: Failed to find fpt_entry\n",
149efa29317SBadal Nilawar 			fw_id_to_name[lb_fw->id]);
150efa29317SBadal Nilawar 		return -ENODATA;
151efa29317SBadal Nilawar 	}
152efa29317SBadal Nilawar 
153efa29317SBadal Nilawar 	min_size = offset + sizeof(struct gsc_cpd_header_v2);
154efa29317SBadal Nilawar 	if (size < min_size) {
155efa29317SBadal Nilawar 		drm_err(&xe->drm, "%s late binding fw: too small! %zu < %zu\n",
156efa29317SBadal Nilawar 			fw_id_to_name[lb_fw->id], size, min_size);
157efa29317SBadal Nilawar 		return -ENODATA;
158efa29317SBadal Nilawar 	}
159efa29317SBadal Nilawar 
160efa29317SBadal Nilawar 	return parse_cpd_header(lb_fw, data + offset, size - offset, "LTES.man");
161efa29317SBadal Nilawar }
162efa29317SBadal Nilawar 
xe_late_bind_parse_status(uint32_t status)163691a54adSBadal Nilawar static const char *xe_late_bind_parse_status(uint32_t status)
164691a54adSBadal Nilawar {
165691a54adSBadal Nilawar 	switch (status) {
166691a54adSBadal Nilawar 	case INTEL_LB_STATUS_SUCCESS:
167691a54adSBadal Nilawar 		return "success";
168691a54adSBadal Nilawar 	case INTEL_LB_STATUS_4ID_MISMATCH:
169691a54adSBadal Nilawar 		return "4Id Mismatch";
170691a54adSBadal Nilawar 	case INTEL_LB_STATUS_ARB_FAILURE:
171691a54adSBadal Nilawar 		return "ARB Failure";
172691a54adSBadal Nilawar 	case INTEL_LB_STATUS_GENERAL_ERROR:
173691a54adSBadal Nilawar 		return "General Error";
174691a54adSBadal Nilawar 	case INTEL_LB_STATUS_INVALID_PARAMS:
175691a54adSBadal Nilawar 		return "Invalid Params";
176691a54adSBadal Nilawar 	case INTEL_LB_STATUS_INVALID_SIGNATURE:
177691a54adSBadal Nilawar 		return "Invalid Signature";
178691a54adSBadal Nilawar 	case INTEL_LB_STATUS_INVALID_PAYLOAD:
179691a54adSBadal Nilawar 		return "Invalid Payload";
180691a54adSBadal Nilawar 	case INTEL_LB_STATUS_TIMEOUT:
181691a54adSBadal Nilawar 		return "Timeout";
182691a54adSBadal Nilawar 	default:
183691a54adSBadal Nilawar 		return "Unknown error";
184691a54adSBadal Nilawar 	}
185691a54adSBadal Nilawar }
186691a54adSBadal Nilawar 
xe_late_bind_fw_num_fans(struct xe_late_bind * late_bind,u32 * num_fans)187*6982a462SMallesh Koujalagi static int xe_late_bind_fw_num_fans(struct xe_late_bind *late_bind, u32 *num_fans)
18845832bf9SBadal Nilawar {
18945832bf9SBadal Nilawar 	struct xe_device *xe = late_bind_to_xe(late_bind);
19045832bf9SBadal Nilawar 	struct xe_tile *root_tile = xe_device_get_root_tile(xe);
19145832bf9SBadal Nilawar 
192*6982a462SMallesh Koujalagi 	return xe_pcode_read(root_tile,
193*6982a462SMallesh Koujalagi 			     PCODE_MBOX(FAN_SPEED_CONTROL, FSC_READ_NUM_FANS, 0), num_fans, NULL);
19445832bf9SBadal Nilawar }
19545832bf9SBadal Nilawar 
xe_late_bind_wait_for_worker_completion(struct xe_late_bind * late_bind)19669ac1bb8SBadal Nilawar void xe_late_bind_wait_for_worker_completion(struct xe_late_bind *late_bind)
197691a54adSBadal Nilawar {
198691a54adSBadal Nilawar 	struct xe_device *xe = late_bind_to_xe(late_bind);
199691a54adSBadal Nilawar 	struct xe_late_bind_fw *lbfw;
200691a54adSBadal Nilawar 	int fw_id;
201691a54adSBadal Nilawar 
202691a54adSBadal Nilawar 	for (fw_id = 0; fw_id < XE_LB_FW_MAX_ID; fw_id++) {
203691a54adSBadal Nilawar 		lbfw = &late_bind->late_bind_fw[fw_id];
204691a54adSBadal Nilawar 		if (lbfw->payload && late_bind->wq) {
205691a54adSBadal Nilawar 			drm_dbg(&xe->drm, "Flush work: load %s firmware\n",
206691a54adSBadal Nilawar 				fw_id_to_name[lbfw->id]);
207691a54adSBadal Nilawar 			flush_work(&lbfw->work);
208691a54adSBadal Nilawar 		}
209691a54adSBadal Nilawar 	}
210691a54adSBadal Nilawar }
211691a54adSBadal Nilawar 
xe_late_bind_work(struct work_struct * work)212691a54adSBadal Nilawar static void xe_late_bind_work(struct work_struct *work)
213691a54adSBadal Nilawar {
214691a54adSBadal Nilawar 	struct xe_late_bind_fw *lbfw = container_of(work, struct xe_late_bind_fw, work);
215691a54adSBadal Nilawar 	struct xe_late_bind *late_bind = container_of(lbfw, struct xe_late_bind,
216691a54adSBadal Nilawar 						      late_bind_fw[lbfw->id]);
217691a54adSBadal Nilawar 	struct xe_device *xe = late_bind_to_xe(late_bind);
218691a54adSBadal Nilawar 	int retry = LB_FW_LOAD_RETRY_MAXCOUNT;
219691a54adSBadal Nilawar 	int ret;
220691a54adSBadal Nilawar 	int slept;
221691a54adSBadal Nilawar 
222691a54adSBadal Nilawar 	xe_device_assert_mem_access(xe);
223691a54adSBadal Nilawar 
224691a54adSBadal Nilawar 	/* we can queue this before the component is bound */
225691a54adSBadal Nilawar 	for (slept = 0; slept < LB_INIT_TIMEOUT_MS; slept += 100) {
226691a54adSBadal Nilawar 		if (late_bind->component.ops)
227691a54adSBadal Nilawar 			break;
228691a54adSBadal Nilawar 		msleep(100);
229691a54adSBadal Nilawar 	}
230691a54adSBadal Nilawar 
231691a54adSBadal Nilawar 	if (!late_bind->component.ops) {
232691a54adSBadal Nilawar 		drm_err(&xe->drm, "Late bind component not bound\n");
233691a54adSBadal Nilawar 		/* Do not re-attempt fw load */
234691a54adSBadal Nilawar 		drmm_kfree(&xe->drm, (void *)lbfw->payload);
235691a54adSBadal Nilawar 		lbfw->payload = NULL;
236691a54adSBadal Nilawar 		goto out;
237691a54adSBadal Nilawar 	}
238691a54adSBadal Nilawar 
239691a54adSBadal Nilawar 	drm_dbg(&xe->drm, "Load %s firmware\n", fw_id_to_name[lbfw->id]);
240691a54adSBadal Nilawar 
241691a54adSBadal Nilawar 	do {
242691a54adSBadal Nilawar 		ret = late_bind->component.ops->push_payload(late_bind->component.mei_dev,
243691a54adSBadal Nilawar 							     lbfw->type,
244691a54adSBadal Nilawar 							     lbfw->flags,
245691a54adSBadal Nilawar 							     lbfw->payload,
246691a54adSBadal Nilawar 							     lbfw->payload_size);
247691a54adSBadal Nilawar 		if (!ret)
248691a54adSBadal Nilawar 			break;
249691a54adSBadal Nilawar 		msleep(LB_FW_LOAD_RETRY_PAUSE_MS);
250691a54adSBadal Nilawar 	} while (--retry && ret == -EBUSY);
251691a54adSBadal Nilawar 
252691a54adSBadal Nilawar 	if (!ret) {
253691a54adSBadal Nilawar 		drm_dbg(&xe->drm, "Load %s firmware successful\n",
254691a54adSBadal Nilawar 			fw_id_to_name[lbfw->id]);
255691a54adSBadal Nilawar 		goto out;
256691a54adSBadal Nilawar 	}
257691a54adSBadal Nilawar 
258691a54adSBadal Nilawar 	if (ret > 0)
259691a54adSBadal Nilawar 		drm_err(&xe->drm, "Load %s firmware failed with err %d, %s\n",
260691a54adSBadal Nilawar 			fw_id_to_name[lbfw->id], ret, xe_late_bind_parse_status(ret));
261691a54adSBadal Nilawar 	else
262691a54adSBadal Nilawar 		drm_err(&xe->drm, "Load %s firmware failed with err %d",
263691a54adSBadal Nilawar 			fw_id_to_name[lbfw->id], ret);
264691a54adSBadal Nilawar 	/* Do not re-attempt fw load */
265691a54adSBadal Nilawar 	drmm_kfree(&xe->drm, (void *)lbfw->payload);
266691a54adSBadal Nilawar 	lbfw->payload = NULL;
267691a54adSBadal Nilawar 
268691a54adSBadal Nilawar out:
269691a54adSBadal Nilawar 	xe_pm_runtime_put(xe);
270691a54adSBadal Nilawar }
271691a54adSBadal Nilawar 
xe_late_bind_fw_load(struct xe_late_bind * late_bind)272691a54adSBadal Nilawar int xe_late_bind_fw_load(struct xe_late_bind *late_bind)
273691a54adSBadal Nilawar {
274691a54adSBadal Nilawar 	struct xe_device *xe = late_bind_to_xe(late_bind);
275691a54adSBadal Nilawar 	struct xe_late_bind_fw *lbfw;
276691a54adSBadal Nilawar 	int fw_id;
277691a54adSBadal Nilawar 
278691a54adSBadal Nilawar 	if (!late_bind->component_added)
279691a54adSBadal Nilawar 		return -ENODEV;
280691a54adSBadal Nilawar 
28167de7982SBadal Nilawar 	if (late_bind->disable)
28267de7982SBadal Nilawar 		return 0;
28367de7982SBadal Nilawar 
284691a54adSBadal Nilawar 	for (fw_id = 0; fw_id < XE_LB_FW_MAX_ID; fw_id++) {
285691a54adSBadal Nilawar 		lbfw = &late_bind->late_bind_fw[fw_id];
286691a54adSBadal Nilawar 		if (lbfw->payload) {
287691a54adSBadal Nilawar 			xe_pm_runtime_get_noresume(xe);
288691a54adSBadal Nilawar 			queue_work(late_bind->wq, &lbfw->work);
289691a54adSBadal Nilawar 		}
290691a54adSBadal Nilawar 	}
291691a54adSBadal Nilawar 	return 0;
292691a54adSBadal Nilawar }
293691a54adSBadal Nilawar 
__xe_late_bind_fw_init(struct xe_late_bind * late_bind,u32 fw_id)29445832bf9SBadal Nilawar static int __xe_late_bind_fw_init(struct xe_late_bind *late_bind, u32 fw_id)
29545832bf9SBadal Nilawar {
29645832bf9SBadal Nilawar 	struct xe_device *xe = late_bind_to_xe(late_bind);
29745832bf9SBadal Nilawar 	struct pci_dev *pdev = to_pci_dev(xe->drm.dev);
29845832bf9SBadal Nilawar 	struct xe_late_bind_fw *lb_fw;
29945832bf9SBadal Nilawar 	const struct firmware *fw;
30045832bf9SBadal Nilawar 	u32 num_fans;
30145832bf9SBadal Nilawar 	int ret;
30245832bf9SBadal Nilawar 
30345832bf9SBadal Nilawar 	if (fw_id >= XE_LB_FW_MAX_ID)
30445832bf9SBadal Nilawar 		return -EINVAL;
30545832bf9SBadal Nilawar 
30645832bf9SBadal Nilawar 	lb_fw = &late_bind->late_bind_fw[fw_id];
30745832bf9SBadal Nilawar 
30845832bf9SBadal Nilawar 	lb_fw->id = fw_id;
30945832bf9SBadal Nilawar 	lb_fw->type = fw_id_to_type[lb_fw->id];
31045832bf9SBadal Nilawar 	lb_fw->flags &= ~INTEL_LB_FLAG_IS_PERSISTENT;
31145832bf9SBadal Nilawar 
31245832bf9SBadal Nilawar 	if (lb_fw->type == INTEL_LB_TYPE_FAN_CONTROL) {
313*6982a462SMallesh Koujalagi 		ret = xe_late_bind_fw_num_fans(late_bind, &num_fans);
314*6982a462SMallesh Koujalagi 		if (ret) {
315*6982a462SMallesh Koujalagi 			drm_dbg(&xe->drm, "Failed to read number of fans: %d\n", ret);
316*6982a462SMallesh Koujalagi 			return 0; /* Not a fatal error, continue without fan control */
317*6982a462SMallesh Koujalagi 		}
31845832bf9SBadal Nilawar 		drm_dbg(&xe->drm, "Number of Fans: %d\n", num_fans);
31945832bf9SBadal Nilawar 		if (!num_fans)
32045832bf9SBadal Nilawar 			return 0;
32145832bf9SBadal Nilawar 	}
32245832bf9SBadal Nilawar 
32345832bf9SBadal Nilawar 	snprintf(lb_fw->blob_path, sizeof(lb_fw->blob_path), "xe/%s_8086_%04x_%04x_%04x.bin",
32445832bf9SBadal Nilawar 		 fw_id_to_name[lb_fw->id], pdev->device,
32545832bf9SBadal Nilawar 		 pdev->subsystem_vendor, pdev->subsystem_device);
32645832bf9SBadal Nilawar 
32745832bf9SBadal Nilawar 	drm_dbg(&xe->drm, "Request late binding firmware %s\n", lb_fw->blob_path);
32845832bf9SBadal Nilawar 	ret = firmware_request_nowarn(&fw, lb_fw->blob_path, xe->drm.dev);
32945832bf9SBadal Nilawar 	if (ret) {
33045832bf9SBadal Nilawar 		drm_dbg(&xe->drm, "%s late binding fw not available for current device",
33145832bf9SBadal Nilawar 			fw_id_to_name[lb_fw->id]);
33245832bf9SBadal Nilawar 		return 0;
33345832bf9SBadal Nilawar 	}
33445832bf9SBadal Nilawar 
33545832bf9SBadal Nilawar 	if (fw->size > XE_LB_MAX_PAYLOAD_SIZE) {
33645832bf9SBadal Nilawar 		drm_err(&xe->drm, "Firmware %s size %zu is larger than max pay load size %u\n",
33745832bf9SBadal Nilawar 			lb_fw->blob_path, fw->size, XE_LB_MAX_PAYLOAD_SIZE);
33845832bf9SBadal Nilawar 		release_firmware(fw);
33945832bf9SBadal Nilawar 		return -ENODATA;
34045832bf9SBadal Nilawar 	}
34145832bf9SBadal Nilawar 
342efa29317SBadal Nilawar 	ret = parse_lb_layout(lb_fw, fw->data, fw->size, "LTES");
343efa29317SBadal Nilawar 	if (ret)
344efa29317SBadal Nilawar 		return ret;
345efa29317SBadal Nilawar 
34645832bf9SBadal Nilawar 	lb_fw->payload_size = fw->size;
34745832bf9SBadal Nilawar 	lb_fw->payload = drmm_kzalloc(&xe->drm, lb_fw->payload_size, GFP_KERNEL);
34845832bf9SBadal Nilawar 	if (!lb_fw->payload) {
34945832bf9SBadal Nilawar 		release_firmware(fw);
35045832bf9SBadal Nilawar 		return -ENOMEM;
35145832bf9SBadal Nilawar 	}
35245832bf9SBadal Nilawar 
353efa29317SBadal Nilawar 	drm_info(&xe->drm, "Using %s firmware from %s version %u.%u.%u.%u\n",
354efa29317SBadal Nilawar 		 fw_id_to_name[lb_fw->id], lb_fw->blob_path,
355efa29317SBadal Nilawar 		 lb_fw->version.major, lb_fw->version.minor,
356efa29317SBadal Nilawar 		 lb_fw->version.hotfix, lb_fw->version.build);
357efa29317SBadal Nilawar 
35845832bf9SBadal Nilawar 	memcpy((void *)lb_fw->payload, fw->data, lb_fw->payload_size);
35945832bf9SBadal Nilawar 	release_firmware(fw);
360691a54adSBadal Nilawar 	INIT_WORK(&lb_fw->work, xe_late_bind_work);
36145832bf9SBadal Nilawar 
36245832bf9SBadal Nilawar 	return 0;
36345832bf9SBadal Nilawar }
36445832bf9SBadal Nilawar 
xe_late_bind_fw_init(struct xe_late_bind * late_bind)36545832bf9SBadal Nilawar static int xe_late_bind_fw_init(struct xe_late_bind *late_bind)
36645832bf9SBadal Nilawar {
36745832bf9SBadal Nilawar 	int ret;
36845832bf9SBadal Nilawar 	int fw_id;
36945832bf9SBadal Nilawar 
370691a54adSBadal Nilawar 	late_bind->wq = alloc_ordered_workqueue("late-bind-ordered-wq", 0);
371691a54adSBadal Nilawar 	if (!late_bind->wq)
372691a54adSBadal Nilawar 		return -ENOMEM;
373691a54adSBadal Nilawar 
37445832bf9SBadal Nilawar 	for (fw_id = 0; fw_id < XE_LB_FW_MAX_ID; fw_id++) {
37545832bf9SBadal Nilawar 		ret = __xe_late_bind_fw_init(late_bind, fw_id);
37645832bf9SBadal Nilawar 		if (ret)
37745832bf9SBadal Nilawar 			return ret;
37845832bf9SBadal Nilawar 	}
379691a54adSBadal Nilawar 
38045832bf9SBadal Nilawar 	return 0;
38145832bf9SBadal Nilawar }
38245832bf9SBadal Nilawar 
xe_late_bind_component_bind(struct device * xe_kdev,struct device * mei_kdev,void * data)383918bd789SBadal Nilawar static int xe_late_bind_component_bind(struct device *xe_kdev,
384918bd789SBadal Nilawar 				       struct device *mei_kdev, void *data)
385918bd789SBadal Nilawar {
386918bd789SBadal Nilawar 	struct xe_device *xe = kdev_to_xe_device(xe_kdev);
387918bd789SBadal Nilawar 	struct xe_late_bind *late_bind = &xe->late_bind;
388918bd789SBadal Nilawar 
389918bd789SBadal Nilawar 	late_bind->component.ops = data;
390918bd789SBadal Nilawar 	late_bind->component.mei_dev = mei_kdev;
391918bd789SBadal Nilawar 
392918bd789SBadal Nilawar 	return 0;
393918bd789SBadal Nilawar }
394918bd789SBadal Nilawar 
xe_late_bind_component_unbind(struct device * xe_kdev,struct device * mei_kdev,void * data)395918bd789SBadal Nilawar static void xe_late_bind_component_unbind(struct device *xe_kdev,
396918bd789SBadal Nilawar 					  struct device *mei_kdev, void *data)
397918bd789SBadal Nilawar {
398918bd789SBadal Nilawar 	struct xe_device *xe = kdev_to_xe_device(xe_kdev);
399918bd789SBadal Nilawar 	struct xe_late_bind *late_bind = &xe->late_bind;
400918bd789SBadal Nilawar 
401691a54adSBadal Nilawar 	xe_late_bind_wait_for_worker_completion(late_bind);
402691a54adSBadal Nilawar 
403918bd789SBadal Nilawar 	late_bind->component.ops = NULL;
404918bd789SBadal Nilawar }
405918bd789SBadal Nilawar 
406918bd789SBadal Nilawar static const struct component_ops xe_late_bind_component_ops = {
407918bd789SBadal Nilawar 	.bind   = xe_late_bind_component_bind,
408918bd789SBadal Nilawar 	.unbind = xe_late_bind_component_unbind,
409918bd789SBadal Nilawar };
410918bd789SBadal Nilawar 
xe_late_bind_remove(void * arg)411918bd789SBadal Nilawar static void xe_late_bind_remove(void *arg)
412918bd789SBadal Nilawar {
413918bd789SBadal Nilawar 	struct xe_late_bind *late_bind = arg;
414918bd789SBadal Nilawar 	struct xe_device *xe = late_bind_to_xe(late_bind);
415918bd789SBadal Nilawar 
416691a54adSBadal Nilawar 	xe_late_bind_wait_for_worker_completion(late_bind);
417691a54adSBadal Nilawar 
418691a54adSBadal Nilawar 	late_bind->component_added = false;
419691a54adSBadal Nilawar 
420918bd789SBadal Nilawar 	component_del(xe->drm.dev, &xe_late_bind_component_ops);
421691a54adSBadal Nilawar 	if (late_bind->wq) {
422691a54adSBadal Nilawar 		destroy_workqueue(late_bind->wq);
423691a54adSBadal Nilawar 		late_bind->wq = NULL;
424691a54adSBadal Nilawar 	}
425918bd789SBadal Nilawar }
426918bd789SBadal Nilawar 
427918bd789SBadal Nilawar /**
428918bd789SBadal Nilawar  * xe_late_bind_init() - add xe mei late binding component
429918bd789SBadal Nilawar  * @late_bind: pointer to late bind structure.
430918bd789SBadal Nilawar  *
431918bd789SBadal Nilawar  * Return: 0 if the initialization was successful, a negative errno otherwise.
432918bd789SBadal Nilawar  */
xe_late_bind_init(struct xe_late_bind * late_bind)433918bd789SBadal Nilawar int xe_late_bind_init(struct xe_late_bind *late_bind)
434918bd789SBadal Nilawar {
435918bd789SBadal Nilawar 	struct xe_device *xe = late_bind_to_xe(late_bind);
436918bd789SBadal Nilawar 	int err;
437918bd789SBadal Nilawar 
438918bd789SBadal Nilawar 	if (!xe->info.has_late_bind)
439918bd789SBadal Nilawar 		return 0;
440918bd789SBadal Nilawar 
441918bd789SBadal Nilawar 	if (!IS_ENABLED(CONFIG_INTEL_MEI_LB) || !IS_ENABLED(CONFIG_INTEL_MEI_GSC)) {
442918bd789SBadal Nilawar 		drm_info(&xe->drm, "Can't init xe mei late bind missing mei component\n");
443918bd789SBadal Nilawar 		return 0;
444918bd789SBadal Nilawar 	}
445918bd789SBadal Nilawar 
446918bd789SBadal Nilawar 	err = component_add_typed(xe->drm.dev, &xe_late_bind_component_ops,
447918bd789SBadal Nilawar 				  INTEL_COMPONENT_LB);
448918bd789SBadal Nilawar 	if (err < 0) {
449918bd789SBadal Nilawar 		drm_err(&xe->drm, "Failed to add mei late bind component (%pe)\n", ERR_PTR(err));
450918bd789SBadal Nilawar 		return err;
451918bd789SBadal Nilawar 	}
452918bd789SBadal Nilawar 
453691a54adSBadal Nilawar 	late_bind->component_added = true;
454691a54adSBadal Nilawar 
45545832bf9SBadal Nilawar 	err = devm_add_action_or_reset(xe->drm.dev, xe_late_bind_remove, late_bind);
45645832bf9SBadal Nilawar 	if (err)
45745832bf9SBadal Nilawar 		return err;
45845832bf9SBadal Nilawar 
459691a54adSBadal Nilawar 	err = xe_late_bind_fw_init(late_bind);
460691a54adSBadal Nilawar 	if (err)
461691a54adSBadal Nilawar 		return err;
462691a54adSBadal Nilawar 
463691a54adSBadal Nilawar 	return xe_late_bind_fw_load(late_bind);
464918bd789SBadal Nilawar }
465