On 6/6/2025 10:57 AM, Badal Nilawar wrote:
Introducing xe_late_bind_fw to enable firmware loading for the devices,
such as the fan controller, during the driver probe. Typically,
firmware for such devices are part of IFWI flash image but can be
replaced at probe after OEM tuning.
This patch binds late binding component to enable firmware loading
through CSE.

v2:
  - Add devm_add_action_or_reset to remove the component (Daniele)
  - Add INTEL_MEI_GSC check in xe_late_bind_init() (Daniele)

Signed-off-by: Badal Nilawar <badal.nila...@intel.com>
---
  drivers/gpu/drm/xe/Makefile                |  1 +
  drivers/gpu/drm/xe/xe_device.c             |  3 +
  drivers/gpu/drm/xe/xe_device_types.h       |  4 +
  drivers/gpu/drm/xe/xe_late_bind_fw.c       | 96 ++++++++++++++++++++++
  drivers/gpu/drm/xe/xe_late_bind_fw.h       | 15 ++++
  drivers/gpu/drm/xe/xe_late_bind_fw_types.h | 39 +++++++++
  6 files changed, 158 insertions(+)
  create mode 100644 drivers/gpu/drm/xe/xe_late_bind_fw.c
  create mode 100644 drivers/gpu/drm/xe/xe_late_bind_fw.h
  create mode 100644 drivers/gpu/drm/xe/xe_late_bind_fw_types.h

diff --git a/drivers/gpu/drm/xe/Makefile b/drivers/gpu/drm/xe/Makefile
index 01d231777901..134eee21c75e 100644
--- a/drivers/gpu/drm/xe/Makefile
+++ b/drivers/gpu/drm/xe/Makefile
@@ -76,6 +76,7 @@ xe-y += xe_bb.o \
        xe_hw_fence.o \
        xe_irq.o \
        xe_lrc.o \
+       xe_late_bind_fw.o \
        xe_migrate.o \
        xe_mmio.o \
        xe_mocs.o \
diff --git a/drivers/gpu/drm/xe/xe_device.c b/drivers/gpu/drm/xe/xe_device.c
index d4b6e623aa48..e062ddaa83bb 100644
--- a/drivers/gpu/drm/xe/xe_device.c
+++ b/drivers/gpu/drm/xe/xe_device.c
@@ -43,6 +43,7 @@
  #include "xe_hw_engine_group.h"
  #include "xe_hwmon.h"
  #include "xe_irq.h"
+#include "xe_late_bind_fw.h"
  #include "xe_memirq.h"
  #include "xe_mmio.h"
  #include "xe_module.h"
@@ -888,6 +889,8 @@ int xe_device_probe(struct xe_device *xe)
        if (err)
                return err;
+ xe_late_bind_init(&xe->late_bind);

I believe that the xe approach is to always fail the probe if something goes wrong, even if it is not fatal,s oyou should check the error here. However, make sure that the probe is not aborted in the missing mei component case (e.g., only fail if "err && err != -ENODEV")

+
        err = xe_oa_init(xe);
        if (err)
                return err;
diff --git a/drivers/gpu/drm/xe/xe_device_types.h 
b/drivers/gpu/drm/xe/xe_device_types.h
index 043515f8c068..3fda450a0774 100644
--- a/drivers/gpu/drm/xe/xe_device_types.h
+++ b/drivers/gpu/drm/xe/xe_device_types.h
@@ -16,6 +16,7 @@
  #include "xe_devcoredump_types.h"
  #include "xe_heci_gsc.h"
  #include "xe_lmtt_types.h"
+#include "xe_late_bind_fw_types.h"
  #include "xe_memirq_types.h"
  #include "xe_oa_types.h"
  #include "xe_platform_types.h"
@@ -549,6 +550,9 @@ struct xe_device {
        /** @heci_gsc: graphics security controller */
        struct xe_heci_gsc heci_gsc;
+ /** @late_bind: xe mei late bind interface */
+       struct xe_late_bind late_bind;
+
        /** @oa: oa observation subsystem */
        struct xe_oa oa;
diff --git a/drivers/gpu/drm/xe/xe_late_bind_fw.c b/drivers/gpu/drm/xe/xe_late_bind_fw.c
new file mode 100644
index 000000000000..22eb9b51b4ee
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_late_bind_fw.c
@@ -0,0 +1,96 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2025 Intel Corporation
+ */
+
+#include <linux/component.h>
+#include <linux/delay.h>
+
+#include <drm/drm_managed.h>
+#include <drm/intel/i915_component.h>
+#include <drm/intel/late_bind_mei_interface.h>
+#include <drm/drm_print.h>
+
+#include "xe_device.h"
+#include "xe_late_bind_fw.h"
+
+static struct xe_device *
+late_bind_to_xe(struct xe_late_bind *late_bind)
+{
+       return container_of(late_bind, struct xe_device, late_bind);
+}
+
+static int xe_late_bind_component_bind(struct device *xe_kdev,
+                                      struct device *mei_kdev, void *data)
+{
+       struct xe_device *xe = kdev_to_xe_device(xe_kdev);
+       struct xe_late_bind *late_bind = &xe->late_bind;
+
+       mutex_lock(&late_bind->mutex);
+       late_bind->component.ops = data;
+       late_bind->component.mei_dev = mei_kdev;
+       mutex_unlock(&late_bind->mutex);
+
+       return 0;
+}
+
+static void xe_late_bind_component_unbind(struct device *xe_kdev,
+                                         struct device *mei_kdev, void *data)
+{
+       struct xe_device *xe = kdev_to_xe_device(xe_kdev);
+       struct xe_late_bind *late_bind = &xe->late_bind;
+
+       mutex_lock(&late_bind->mutex);
+       late_bind->component.ops = NULL;
+       mutex_unlock(&late_bind->mutex);
+}
+
+static const struct component_ops xe_late_bind_component_ops = {
+       .bind   = xe_late_bind_component_bind,
+       .unbind = xe_late_bind_component_unbind,
+};
+
+static void xe_late_bind_remove(void *arg)

nit: no need for the xe_* prefix for static functions

+{
+       struct xe_late_bind *late_bind = arg;
+       struct xe_device *xe = late_bind_to_xe(late_bind);
+
+       if (!late_bind->component_added)
+               return;

This check against late_bind->component_added doesn't seem necessary, because late_bind_remove() is only added via devm if the component was successfully added. Other components might still have these checks leftover from when we didn't use devm for them.

+
+       component_del(xe->drm.dev, &xe_late_bind_component_ops);
+       late_bind->component_added = false;
+       mutex_destroy(&late_bind->mutex);
+}
+
+/**
+ * xe_late_bind_init() - add xe mei late binding component
+ *
+ * Return: 0 if the initialization was successful, a negative errno otherwise.
+ */
+int xe_late_bind_init(struct xe_late_bind *late_bind)
+{
+       struct xe_device *xe = late_bind_to_xe(late_bind);
+       int err;
+
+       if (xe->info.platform != XE_BATTLEMAGE)

Would this be better as a "has_late_bind" flag in the info? that way we won't need to keep a list of platforms here if new ones are added in the future.

+               return 0;
+
+       mutex_init(&late_bind->mutex);
+
+       if (!IS_ENABLED(CONFIG_INTEL_MEI_LATE_BIND) || 
!IS_ENABLED(CONFIG_INTEL_MEI_GSC)) {
+               drm_info(&xe->drm, "Can't init xe mei late bind missing mei 
component\n");
+               return -ENODEV;
+       }
+
+       err = component_add_typed(xe->drm.dev, &xe_late_bind_component_ops,
+                                 I915_COMPONENT_LATE_BIND);
+       if (err < 0) {
+               drm_info(&xe->drm, "Failed to add mei late bind component 
(%pe)\n", ERR_PTR(err));
+               return err;
+       }
+
+       late_bind->component_added = true;
+
+       return devm_add_action_or_reset(xe->drm.dev, xe_late_bind_remove, 
late_bind);
+}
diff --git a/drivers/gpu/drm/xe/xe_late_bind_fw.h 
b/drivers/gpu/drm/xe/xe_late_bind_fw.h
new file mode 100644
index 000000000000..4c73571c3e62
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_late_bind_fw.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2025 Intel Corporation
+ */
+
+#ifndef _XE_LATE_BIND_FW_H_
+#define _XE_LATE_BIND_FW_H_
+
+#include <linux/types.h>
+
+struct xe_late_bind;
+
+int xe_late_bind_init(struct xe_late_bind *late_bind);
+
+#endif
diff --git a/drivers/gpu/drm/xe/xe_late_bind_fw_types.h 
b/drivers/gpu/drm/xe/xe_late_bind_fw_types.h
new file mode 100644
index 000000000000..afa1917b5f51
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_late_bind_fw_types.h
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2025 Intel Corporation
+ */
+
+#ifndef _XE_MEI_LATE_BIND_TYPES_H_
+#define _XE_MEI_LATE_BIND_TYPES_H_

I think the "MEI" should be dropped from this define.

+
+#include <linux/iosys-map.h>
+#include <linux/mutex.h>
+#include <linux/types.h>
+
+/**
+ * struct xe_late_bind_component - Late Binding services component
+ * @mei_dev: device that provide Late Binding service.
+ * @ops: Ops implemented by Late Binding driver, used by Xe driver.
+ *
+ * Communication between Xe and MEI drivers for Late Binding services
+ */
+struct xe_late_bind_component {
+       /** @late_bind_component.mei_dev: mei device */
+       struct device *mei_dev;
+       /** @late_bind_component.ops: late binding ops */
+       const struct late_bind_component_ops *ops;
+};

It's a bit weird to have those 2 variables in their own struct, since the other 2 variables in struct xe_late_bind are also component-related. I understand we do have these separate for other components, but that's because the MEI driver requires it.
Not a blocker.

Daniele

+
+/**
+ * struct xe_late_bind
+ */
+struct xe_late_bind {
+       /** @late_bind.component: struct for communication with mei component */
+       struct xe_late_bind_component component;
+       /** @late_bind.component_added: whether the component has been added */
+       bool component_added;
+       /** @late_bind.mutex: protects the component binding and usage */
+       struct mutex mutex;
+};
+
+#endif

Reply via email to