-----Original Message-----
From: Nilawar, Badal <[email protected]>
Sent: Wednesday, June 25, 2025 10:30 PM
To: [email protected]; [email protected]; linux-
[email protected]
Cc: Gupta, Anshuman <[email protected]>; Vivi, Rodrigo
<[email protected]>; Usyskin, Alexander <[email protected]>;
[email protected]; Ceraolo Spurio, Daniele
<[email protected]>
Subject: [PATCH v4 02/10] mei: late_bind: add late binding component driver
From: Alexander Usyskin <[email protected]>
Add late binding component driver.
It allows pushing the late binding configuration from, for example, the Xe
graphics driver to the Intel discrete graphics card's CSE device.
Signed-off-by: Alexander Usyskin <[email protected]>
Signed-off-by: Badal Nilawar <[email protected]>
---
v2:
- Use generic naming (Jani)
- Drop xe_late_bind_component struct to move to xe code (Daniele/Sasha)
v3:
- Updated kconfig description
- Move CSC late binding specific flags/defines to late_bind_mei_interface.h
(Daniele)
v4:
- Add match for PCI_CLASS_DISPLAY_OTHER to support headless cards
(Anshuman)
v5:
- Add fixes in push_config (Sasha)
- Use INTEL_ prefix for component, refine doc,
add status enum to headerlate_bind_mei_interface.h (Anshuman)
---
drivers/misc/mei/Kconfig | 1 +
drivers/misc/mei/Makefile | 1 +
drivers/misc/mei/late_bind/Kconfig | 13 +
drivers/misc/mei/late_bind/Makefile | 9 +
drivers/misc/mei/late_bind/mei_late_bind.c | 281 ++++++++++++++++++++
include/drm/intel/i915_component.h | 1 +
include/drm/intel/late_bind_mei_interface.h | 64 +++++
7 files changed, 370 insertions(+)
create mode 100644 drivers/misc/mei/late_bind/Kconfig
create mode 100644 drivers/misc/mei/late_bind/Makefile
create mode 100644 drivers/misc/mei/late_bind/mei_late_bind.c
create mode 100644 include/drm/intel/late_bind_mei_interface.h
diff --git a/drivers/misc/mei/Kconfig b/drivers/misc/mei/Kconfig index
7575fee96cc6..771becc68095 100644
--- a/drivers/misc/mei/Kconfig
+++ b/drivers/misc/mei/Kconfig
@@ -84,5 +84,6 @@ config INTEL_MEI_VSC
source "drivers/misc/mei/hdcp/Kconfig"
source "drivers/misc/mei/pxp/Kconfig"
source "drivers/misc/mei/gsc_proxy/Kconfig"
+source "drivers/misc/mei/late_bind/Kconfig"
endif
diff --git a/drivers/misc/mei/Makefile b/drivers/misc/mei/Makefile index
6f9fdbf1a495..84bfde888d81 100644
--- a/drivers/misc/mei/Makefile
+++ b/drivers/misc/mei/Makefile
@@ -31,6 +31,7 @@ CFLAGS_mei-trace.o = -I$(src)
obj-$(CONFIG_INTEL_MEI_HDCP) += hdcp/
obj-$(CONFIG_INTEL_MEI_PXP) += pxp/
obj-$(CONFIG_INTEL_MEI_GSC_PROXY) += gsc_proxy/
+obj-$(CONFIG_INTEL_MEI_LATE_BIND) += late_bind/
obj-$(CONFIG_INTEL_MEI_VSC_HW) += mei-vsc-hw.o mei-vsc-hw-y := vsc-
tp.o diff --git a/drivers/misc/mei/late_bind/Kconfig
b/drivers/misc/mei/late_bind/Kconfig
new file mode 100644
index 000000000000..65c7180c5678
--- /dev/null
+++ b/drivers/misc/mei/late_bind/Kconfig
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2025, Intel Corporation. All rights reserved.
+#
+config INTEL_MEI_LATE_BIND
+ tristate "Intel late binding support on ME Interface"
+ select INTEL_MEI_ME
+ depends on DRM_XE
+ help
+ MEI Support for Late Binding for Intel graphics card.
+
+ Enables the ME FW interfaces for Late Binding feature,
+ allowing loading of firmware for the devices like Fan
+ Controller during by Intel Xe driver.
diff --git a/drivers/misc/mei/late_bind/Makefile
b/drivers/misc/mei/late_bind/Makefile
new file mode 100644
index 000000000000..a0aeda5853f0
--- /dev/null
+++ b/drivers/misc/mei/late_bind/Makefile
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (c) 2025, Intel Corporation. All rights reserved.
+#
+# Makefile - Late Binding client driver for Intel MEI Bus Driver.
+
+subdir-ccflags-y += -I$(srctree)/drivers/misc/mei/
+
+obj-$(CONFIG_INTEL_MEI_LATE_BIND) += mei_late_bind.o
diff --git a/drivers/misc/mei/late_bind/mei_late_bind.c
b/drivers/misc/mei/late_bind/mei_late_bind.c
new file mode 100644
index 000000000000..ffb89ccdfbb1
--- /dev/null
+++ b/drivers/misc/mei/late_bind/mei_late_bind.c
@@ -0,0 +1,281 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2025 Intel Corporation */ #include
+<drm/intel/i915_component.h> #include
+<drm/intel/late_bind_mei_interface.h>
+#include <linux/component.h>
+#include <linux/pci.h>
+#include <linux/mei_cl_bus.h>
+#include <linux/module.h>
+#include <linux/overflow.h>
+#include <linux/slab.h>
+#include <linux/uuid.h>
+
+#include "mkhi.h"
+
+#define GFX_SRV_MKHI_LATE_BINDING_CMD 0x12 #define
+GFX_SRV_MKHI_LATE_BINDING_RSP (GFX_SRV_MKHI_LATE_BINDING_CMD
| 0x80)
+
+#define LATE_BIND_SEND_TIMEOUT_MSEC 3000 #define
+LATE_BIND_RECV_TIMEOUT_MSEC 3000
+
+/**
+ * struct csc_heci_late_bind_req - late binding request
+ * @header: @ref mkhi_msg_hdr
+ * @type: type of the late binding payload
+ * @flags: flags to be passed to the firmware
+ * @reserved: reserved field
+ * @payload_size: size of the payload data in bytes
+ * @payload: data to be sent to the firmware */ struct
+csc_heci_late_bind_req {
+ struct mkhi_msg_hdr header;
+ u32 type;
+ u32 flags;
+ u32 reserved[2];
+ u32 payload_size;
+ u8 payload[] __counted_by(payload_size); } __packed;
+
+/**
+ * struct csc_heci_late_bind_rsp - late binding response
+ * @header: @ref mkhi_msg_hdr
+ * @type: type of the late binding payload
+ * @reserved: reserved field
+ * @status: status of the late binding command execution by firmware
+*/ struct csc_heci_late_bind_rsp {
+ struct mkhi_msg_hdr header;
+ u32 type;
+ u32 reserved[2];
+ u32 status;
+} __packed;
+
+static int mei_late_bind_check_response(const struct device *dev, const
+struct mkhi_msg_hdr *hdr) {
+ if (hdr->group_id != MKHI_GROUP_ID_GFX) {
+ dev_err(dev, "Mismatch group id: 0x%x instead of 0x%x\n",
+ hdr->group_id, MKHI_GROUP_ID_GFX);
+ return -EINVAL;
+ }
+
+ if (hdr->command != GFX_SRV_MKHI_LATE_BINDING_RSP) {
+ dev_err(dev, "Mismatch command: 0x%x instead of 0x%x\n",
+ hdr->command,
GFX_SRV_MKHI_LATE_BINDING_RSP);
+ return -EINVAL;
+ }
+
+ if (hdr->result) {
+ dev_err(dev, "Error in result: 0x%x\n", hdr->result);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * mei_late_bind_push_config - Sends a config to the firmware.
+ * @dev: device struct corresponding to the mei device
+ * @type: payload type
+ * @flags: payload flags
+ * @payload: payload buffer
+ * @payload_size: payload buffer size
+ *
+ * Return: 0 success, negative errno value on transport failure,
+ * positive status returned by FW
+ */
+static int mei_late_bind_push_config(struct device *dev, u32 type, u32 flags,
+ const void *payload, size_t payload_size) {
+ struct mei_cl_device *cldev;
+ struct csc_heci_late_bind_req *req = NULL;
+ struct csc_heci_late_bind_rsp rsp;
+ size_t req_size;
+ ssize_t ret;
+
+ if (!dev || !payload || !payload_size)
+ return -EINVAL;
+
+ cldev = to_mei_cl_device(dev);
+
+ ret = mei_cldev_enable(cldev);
+ if (ret < 0) {
+ dev_dbg(dev, "mei_cldev_enable failed. %zd\n", ret);
+ return ret;
+ }
+
+ req_size = struct_size(req, payload, payload_size);
+ if (req_size > mei_cldev_mtu(cldev)) {
+ dev_err(dev, "Payload is too big %zu\n", payload_size);
+ ret = -EMSGSIZE;
+ goto end;
+ }
+
+ req = kmalloc(req_size, GFP_KERNEL);
+ if (!req) {
+ ret = -ENOMEM;
+ goto end;
+ }
+
+ req->header.group_id = MKHI_GROUP_ID_GFX;
+ req->header.command = GFX_SRV_MKHI_LATE_BINDING_CMD;
+ req->type = type;
+ req->flags = flags;
+ req->reserved[0] = 0;
+ req->reserved[1] = 0;
+ req->payload_size = payload_size;
+ memcpy(req->payload, payload, payload_size);
+
+ ret = mei_cldev_send_timeout(cldev, (void *)req, req_size,
LATE_BIND_SEND_TIMEOUT_MSEC);
+ if (ret < 0) {
+ dev_err(dev, "mei_cldev_send failed. %zd\n", ret);
+ goto end;
+ }
+
+ ret = mei_cldev_recv_timeout(cldev, (void *)&rsp, sizeof(rsp),
LATE_BIND_RECV_TIMEOUT_MSEC);
+ if (ret < 0) {
+ dev_err(dev, "mei_cldev_recv failed. %zd\n", ret);
+ goto end;
+ }
+ if (ret < sizeof(rsp.header)) {
+ dev_err(dev, "bad response header from the firmware: size
%zd < %zu\n",
+ ret, sizeof(rsp.header));
+ goto end;
+ }
+ if (ret < sizeof(rsp)) {
+ dev_err(dev, "bad response from the firmware: size %zd <
%zu\n",
+ ret, sizeof(rsp));
+ goto end;
+ }
+
+ ret = mei_late_bind_check_response(dev, &rsp.header);
+ if (ret) {
+ dev_err(dev, "bad result response from the firmware:
0x%x\n",
+ *(uint32_t *)&rsp.header);
+ goto end;
+ }
+
+ ret = (int)rsp.status;
+ dev_dbg(dev, "%s status = %zd\n", __func__, ret);
+
+end:
+ mei_cldev_disable(cldev);
+ kfree(req);
+ return ret;
+}
+
+static const struct late_bind_component_ops mei_late_bind_ops = {
+ .owner = THIS_MODULE,
+ .push_config = mei_late_bind_push_config, };
+
+static int mei_component_master_bind(struct device *dev) {
+ return component_bind_all(dev, (void *)&mei_late_bind_ops); }
+
+static void mei_component_master_unbind(struct device *dev) {
+ component_unbind_all(dev, (void *)&mei_late_bind_ops); }
+
+static const struct component_master_ops mei_component_master_ops = {
+ .bind = mei_component_master_bind,
+ .unbind = mei_component_master_unbind, };
+
+/**
+ * mei_late_bind_component_match - compare function for matching mei
late bind.
+ *
+ * The function checks if requester is Intel PCI_CLASS_DISPLAY_VGA or
+ * PCI_CLASS_DISPLAY_OTHER device, and checks if the parent of requester