@@ -189,6 +189,7 @@ i915-y += \
# general-purpose microcontroller (GuC) support
i915-y += \
+ gt/uc/intel_gsc_fw.o \
gt/uc/intel_gsc_uc.o \
gt/uc/intel_guc.o \
gt/uc/intel_guc_ads.o \
@@ -172,6 +172,8 @@ intel_write_status_page(struct intel_engine_cs *engine, int reg, u32 value)
#define I915_GEM_HWS_MIGRATE (0x42 * sizeof(u32))
#define I915_GEM_HWS_PXP 0x60
#define I915_GEM_HWS_PXP_ADDR (I915_GEM_HWS_PXP * sizeof(u32))
+#define I915_GEM_HWS_GSC 0x62
+#define I915_GEM_HWS_GSC_ADDR (I915_GEM_HWS_GSC * sizeof(u32))
#define I915_GEM_HWS_SCRATCH 0x80
#define I915_HWS_CSB_BUF0_INDEX 0x10
@@ -21,6 +21,7 @@
#define INSTR_CLIENT_SHIFT 29
#define INSTR_MI_CLIENT 0x0
#define INSTR_BC_CLIENT 0x2
+#define INSTR_GSC_CLIENT 0x2 /* MTL+ */
#define INSTR_RC_CLIENT 0x3
#define INSTR_SUBCLIENT_SHIFT 27
#define INSTR_SUBCLIENT_MASK 0x18000000
@@ -432,6 +433,12 @@
#define COLOR_BLT ((0x2<<29)|(0x40<<22))
#define SRC_COPY_BLT ((0x2<<29)|(0x43<<22))
+#define GSC_INSTR(opcode, data, flags) \
+ (__INSTR(INSTR_GSC_CLIENT) | (opcode) << 22 | (data) << 9 | (flags))
+
+#define GSC_FW_LOAD GSC_INSTR(1, 0, 2)
+#define HECI1_FW_LIMIT_VALID (1 << 31)
+
/*
* Used to convert any address to canonical form.
* Starting from gen8, some commands (e.g. STATE_BASE_ADDRESS,
new file mode 100644
@@ -0,0 +1,187 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2022 Intel Corporation
+ */
+
+#include "gt/intel_engine_pm.h"
+#include "gt/intel_gpu_commands.h"
+#include "gt/intel_gt.h"
+#include "gt/intel_ring.h"
+#include "intel_gsc_fw.h"
+
+#define GSC_FW_STATUS_REG _MMIO(0x116C40)
+#define GSC_FW_CURRENT_STATE REG_GENMASK(3, 0)
+#define GSC_FW_CURRENT_STATE_RESET 0
+#define GSC_FW_INIT_COMPLETE_BIT REG_BIT(9)
+
+static bool gsc_is_in_reset(struct intel_uncore *uncore)
+{
+ u32 fw_status = intel_uncore_read(uncore, GSC_FW_STATUS_REG);
+
+ return REG_FIELD_GET(GSC_FW_CURRENT_STATE, fw_status) ==
+ GSC_FW_CURRENT_STATE_RESET;
+}
+
+bool intel_gsc_uc_fw_init_done(struct intel_gsc_uc *gsc)
+{
+ struct intel_uncore *uncore = gsc_uc_to_gt(gsc)->uncore;
+ u32 fw_status = intel_uncore_read(uncore, GSC_FW_STATUS_REG);
+
+ return fw_status & GSC_FW_INIT_COMPLETE_BIT;
+}
+
+static int emit_gsc_fw_load(struct i915_request *rq, struct intel_gsc_uc *gsc)
+{
+ u32 offset = i915_ggtt_offset(gsc->local);
+ u32 *cs;
+
+ cs = intel_ring_begin(rq, 4);
+ if (IS_ERR(cs))
+ return PTR_ERR(cs);
+
+ *cs++ = GSC_FW_LOAD;
+ *cs++ = lower_32_bits(offset);
+ *cs++ = upper_32_bits(offset);
+ *cs++ = (gsc->local->size / SZ_4K) | HECI1_FW_LIMIT_VALID;
+
+ intel_ring_advance(rq, cs);
+
+ return 0;
+}
+
+static int gsc_fw_load(struct intel_gsc_uc *gsc)
+{
+ struct intel_context *ce = gsc->ce;
+ struct i915_request *rq;
+ int err;
+
+ if (!ce)
+ return -ENODEV;
+
+ rq = i915_request_create(ce);
+ if (IS_ERR(rq))
+ return PTR_ERR(rq);
+
+ if (ce->engine->emit_init_breadcrumb) {
+ err = ce->engine->emit_init_breadcrumb(rq);
+ if (err)
+ goto out_rq;
+ }
+
+ err = emit_gsc_fw_load(rq, gsc);
+ if (err)
+ goto out_rq;
+
+ err = ce->engine->emit_flush(rq, 0);
+
+out_rq:
+ i915_request_get(rq);
+
+ if (unlikely(err))
+ i915_request_set_error_once(rq, err);
+
+ i915_request_add(rq);
+
+ if (!err && i915_request_wait(rq, 0, msecs_to_jiffies(500)) < 0)
+ err = -ETIME;
+
+ i915_request_put(rq);
+
+ if (err)
+ drm_err(&gsc_uc_to_gt(gsc)->i915->drm,
+ "Request submission for GSC load failed (%d)\n",
+ err);
+
+ return err;
+}
+
+static int gsc_fw_load_prepare(struct intel_gsc_uc *gsc)
+{
+ struct intel_gt *gt = gsc_uc_to_gt(gsc);
+ struct drm_i915_private *i915 = gt->i915;
+ struct drm_i915_gem_object *obj;
+ void *src, *dst;
+
+ if (!gsc->local)
+ return -ENODEV;
+
+ obj = gsc->local->obj;
+
+ if (obj->base.size < gsc->fw.size)
+ return -ENOSPC;
+
+ dst = i915_gem_object_pin_map_unlocked(obj,
+ i915_coherent_map_type(i915, obj, true));
+ if (IS_ERR(dst))
+ return PTR_ERR(dst);
+
+ src = i915_gem_object_pin_map_unlocked(gsc->fw.obj,
+ i915_coherent_map_type(i915, gsc->fw.obj, true));
+ if (IS_ERR(src)) {
+ i915_gem_object_unpin_map(obj);
+ return PTR_ERR(src);
+ }
+
+ memset(dst, 0, obj->base.size);
+ memcpy(dst, src, gsc->fw.size);
+
+ i915_gem_object_unpin_map(gsc->fw.obj);
+ i915_gem_object_unpin_map(obj);
+
+ return 0;
+}
+
+static int gsc_fw_wait(struct intel_gt *gt)
+{
+ return intel_wait_for_register(gt->uncore,
+ GSC_FW_STATUS_REG,
+ GSC_FW_INIT_COMPLETE_BIT,
+ GSC_FW_INIT_COMPLETE_BIT,
+ 500);
+}
+
+int intel_gsc_uc_fw_upload(struct intel_gsc_uc *gsc)
+{
+ struct intel_gt *gt = gsc_uc_to_gt(gsc);
+ struct intel_uc_fw *gsc_fw = &gsc->fw;
+ int err;
+
+ /* check current fw status */
+ if (intel_gsc_uc_fw_init_done(gsc)) {
+ if (GEM_WARN_ON(!intel_uc_fw_is_loaded(gsc_fw)))
+ intel_uc_fw_change_status(gsc_fw, INTEL_UC_FIRMWARE_TRANSFERRED);
+ return -EEXIST;
+ }
+
+ if (!intel_uc_fw_is_loadable(gsc_fw))
+ return -ENOEXEC;
+
+ /* FW blob is ok, so clean the status */
+ intel_uc_fw_sanitize(&gsc->fw);
+
+ if (!gsc_is_in_reset(gt->uncore))
+ return -EIO;
+
+ err = gsc_fw_load_prepare(gsc);
+ if (err)
+ goto fail;
+
+ err = gsc_fw_load(gsc);
+ if (err)
+ goto fail;
+
+ err = gsc_fw_wait(gt);
+ if (err)
+ goto fail;
+
+ /* FW is not fully operational until we enable SW proxy */
+ intel_uc_fw_change_status(gsc_fw, INTEL_UC_FIRMWARE_TRANSFERRED);
+
+ drm_info(>->i915->drm, "Loaded GSC firmware %s\n",
+ gsc_fw->file_selected.path);
+
+ return 0;
+
+fail:
+ return intel_uc_fw_mark_load_failed(gsc_fw, err);
+}
new file mode 100644
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2022 Intel Corporation
+ */
+
+#ifndef _INTEL_GSC_FW_H_
+#define _INTEL_GSC_FW_H_
+
+#include <linux/types.h>
+
+struct intel_gsc_uc;
+
+int intel_gsc_uc_fw_upload(struct intel_gsc_uc *gsc);
+bool intel_gsc_uc_fw_init_done(struct intel_gsc_uc *gsc);
+#endif
@@ -7,8 +7,19 @@
#include "gt/intel_gt.h"
#include "intel_gsc_uc.h"
+#include "intel_gsc_fw.h"
#include "i915_drv.h"
+static void gsc_work(struct work_struct *work)
+{
+ struct intel_gsc_uc *gsc = container_of(work, typeof(*gsc), work);
+ struct intel_gt *gt = gsc_uc_to_gt(gsc);
+ intel_wakeref_t wakeref;
+
+ with_intel_runtime_pm(gt->uncore->rpm, wakeref)
+ intel_gsc_uc_fw_upload(gsc);
+}
+
static bool gsc_engine_supported(struct intel_gt *gt)
{
intel_engine_mask_t mask;
@@ -32,6 +43,7 @@ static bool gsc_engine_supported(struct intel_gt *gt)
void intel_gsc_uc_init_early(struct intel_gsc_uc *gsc)
{
intel_uc_fw_init_early(&gsc->fw, INTEL_UC_FW_TYPE_GSC);
+ INIT_WORK(&gsc->work, gsc_work);
/* we can arrive here from i915_driver_early_probe for primary
* GT with it being not fully setup hence check device info's
@@ -45,17 +57,46 @@ void intel_gsc_uc_init_early(struct intel_gsc_uc *gsc)
int intel_gsc_uc_init(struct intel_gsc_uc *gsc)
{
- struct drm_i915_private *i915 = gsc_uc_to_gt(gsc)->i915;
+ static struct lock_class_key gsc_lock;
+ struct intel_gt *gt = gsc_uc_to_gt(gsc);
+ struct drm_i915_private *i915 = gt->i915;
+ struct intel_engine_cs *engine = gt->engine[GSC0];
+ struct intel_context *ce;
+ struct i915_vma *vma;
int err;
err = intel_uc_fw_init(&gsc->fw);
if (err)
goto out;
+ vma = intel_guc_allocate_vma(>->uc.guc, SZ_8M);
+ if (IS_ERR(vma)) {
+ err = PTR_ERR(vma);
+ goto out_fw;
+ }
+
+ gsc->local = vma;
+
+ ce = intel_engine_create_pinned_context(engine, engine->gt->vm, SZ_4K,
+ I915_GEM_HWS_GSC_ADDR,
+ &gsc_lock, "gsc_context");
+ if (IS_ERR(ce)) {
+ drm_err(>->i915->drm,
+ "failed to create GSC CS ctx for FW communication\n");
+ err = PTR_ERR(ce);
+ goto out_vma;
+ }
+
+ gsc->ce = ce;
+
intel_uc_fw_change_status(&gsc->fw, INTEL_UC_FIRMWARE_LOADABLE);
return 0;
+out_vma:
+ i915_vma_unpin_and_release(&gsc->local, 0);
+out_fw:
+ intel_uc_fw_fini(&gsc->fw);
out:
i915_probe_error(i915, "failed with %d\n", err);
return err;
@@ -66,5 +107,31 @@ void intel_gsc_uc_fini(struct intel_gsc_uc *gsc)
if (!intel_uc_fw_is_loadable(&gsc->fw))
return;
+ flush_work(&gsc->work);
+
+ if (gsc->ce)
+ intel_engine_destroy_pinned_context(fetch_and_zero(&gsc->ce));
+
+ i915_vma_unpin_and_release(&gsc->local, 0);
+
intel_uc_fw_fini(&gsc->fw);
}
+
+void intel_gsc_uc_suspend(struct intel_gsc_uc *gsc)
+{
+ if (!intel_uc_fw_is_loadable(&gsc->fw))
+ return;
+
+ flush_work(&gsc->work);
+}
+
+void intel_gsc_uc_load_start(struct intel_gsc_uc *gsc)
+{
+ if (!intel_uc_fw_is_loadable(&gsc->fw))
+ return;
+
+ if (intel_gsc_uc_fw_init_done(gsc))
+ return;
+
+ queue_work(system_unbound_wq, &gsc->work);
+}
@@ -8,14 +8,25 @@
#include "intel_uc_fw.h"
+struct i915_vma;
+struct intel_context;
+
struct intel_gsc_uc {
/* Generic uC firmware management */
struct intel_uc_fw fw;
+
+ /* GSC-specific additions */
+ struct i915_vma *local; /* private memory for GSC usage */
+ struct intel_context *ce; /* for submission to GSC FW via GSC engine */
+
+ struct work_struct work; /* for delayed load */
};
void intel_gsc_uc_init_early(struct intel_gsc_uc *gsc);
int intel_gsc_uc_init(struct intel_gsc_uc *gsc);
void intel_gsc_uc_fini(struct intel_gsc_uc *gsc);
+void intel_gsc_uc_suspend(struct intel_gsc_uc *gsc);
+void intel_gsc_uc_load_start(struct intel_gsc_uc *gsc);
static inline bool intel_gsc_uc_is_supported(struct intel_gsc_uc *gsc)
{
@@ -7,6 +7,7 @@
#include "gt/intel_gt.h"
#include "gt/intel_reset.h"
+#include "intel_gsc_fw.h"
#include "intel_gsc_uc.h"
#include "intel_guc.h"
#include "intel_guc_ads.h"
@@ -548,6 +549,8 @@ static int __uc_init_hw(struct intel_uc *uc)
intel_rps_lower_unslice(&uc_to_gt(uc)->rps);
}
+ intel_gsc_uc_load_start(&uc->gsc);
+
drm_info(&i915->drm, "GuC submission %s\n",
str_enabled_disabled(intel_uc_uses_guc_submission(uc)));
drm_info(&i915->drm, "GuC SLPC %s\n",
@@ -676,6 +679,9 @@ void intel_uc_suspend(struct intel_uc *uc)
intel_wakeref_t wakeref;
int err;
+ /* flush the GSC worker */
+ intel_gsc_uc_suspend(&uc->gsc);
+
if (!intel_guc_is_ready(guc)) {
guc->interrupts.enabled = false;
return;
@@ -930,6 +930,20 @@ static int uc_fw_xfer(struct intel_uc_fw *uc_fw, u32 dst_offset, u32 dma_flags)
return ret;
}
+int intel_uc_fw_mark_load_failed(struct intel_uc_fw *uc_fw, int err)
+{
+ struct intel_gt *gt = __uc_fw_to_gt(uc_fw);
+
+ GEM_BUG_ON(!intel_uc_fw_is_loadable(uc_fw));
+
+ i915_probe_error(gt->i915, "Failed to load %s firmware %s (%d)\n",
+ intel_uc_fw_type_repr(uc_fw->type), uc_fw->file_selected.path,
+ err);
+ intel_uc_fw_change_status(uc_fw, INTEL_UC_FIRMWARE_LOAD_FAIL);
+
+ return err;
+}
+
/**
* intel_uc_fw_upload - load uC firmware using custom loader
* @uc_fw: uC firmware
@@ -966,11 +980,7 @@ int intel_uc_fw_upload(struct intel_uc_fw *uc_fw, u32 dst_offset, u32 dma_flags)
return 0;
fail:
- i915_probe_error(gt->i915, "Failed to load %s firmware %s (%d)\n",
- intel_uc_fw_type_repr(uc_fw->type), uc_fw->file_selected.path,
- err);
- intel_uc_fw_change_status(uc_fw, INTEL_UC_FIRMWARE_LOAD_FAIL);
- return err;
+ return intel_uc_fw_mark_load_failed(uc_fw, err);
}
static inline bool uc_fw_need_rsa_in_memory(struct intel_uc_fw *uc_fw)
@@ -289,6 +289,7 @@ int intel_uc_fw_upload(struct intel_uc_fw *uc_fw, u32 offset, u32 dma_flags);
int intel_uc_fw_init(struct intel_uc_fw *uc_fw);
void intel_uc_fw_fini(struct intel_uc_fw *uc_fw);
size_t intel_uc_fw_copy_rsa(struct intel_uc_fw *uc_fw, void *dst, u32 max_len);
+int intel_uc_fw_mark_load_failed(struct intel_uc_fw *uc_fw, int err);
void intel_uc_fw_dump(const struct intel_uc_fw *uc_fw, struct drm_printer *p);
#endif