Skip to content

Commit

Permalink
drm/i915/gsc: GSC firmware loading
Browse files Browse the repository at this point in the history
GSC FW is loaded by submitting a dedicated command via the GSC engine.
The memory area used for loading the FW is then re-purposed as local
memory for the GSC itself, so we use a separate allocation instead of
using the one where we keep the firmware stored for reload.

The GSC is not reset as part of GT reset, so we only need to load it on
first boot and S3/S4 exit.

v2: use REG_* for register fields definitions (Rodrigo), move to WQ
    immediately

Bspec: 63347, 65346
Signed-off-by: Daniele Ceraolo Spurio <daniele.ceraolospurio@intel.com>
Cc: Alan Previn <alan.previn.teres.alexis@intel.com>
Cc: John Harrison <John.C.Harrison@Intel.com>
Cc: Rodrigo Vivi <rodrigo.vivi@intel.com>
  • Loading branch information
dceraolo authored and intel-lab-lkp committed Dec 6, 2022
1 parent fe21f44 commit 2af091a
Show file tree
Hide file tree
Showing 10 changed files with 313 additions and 6 deletions.
1 change: 1 addition & 0 deletions drivers/gpu/drm/i915/Makefile
Expand Up @@ -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 \
Expand Down
2 changes: 2 additions & 0 deletions drivers/gpu/drm/i915/gt/intel_engine.h
Expand Up @@ -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
Expand Down
7 changes: 7 additions & 0 deletions drivers/gpu/drm/i915/gt/intel_gpu_commands.h
Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down
187 changes: 187 additions & 0 deletions drivers/gpu/drm/i915/gt/uc/intel_gsc_fw.c
@@ -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(&gt->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);
}
15 changes: 15 additions & 0 deletions drivers/gpu/drm/i915/gt/uc/intel_gsc_fw.h
@@ -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
69 changes: 68 additions & 1 deletion drivers/gpu/drm/i915/gt/uc/intel_gsc_uc.c
Expand Up @@ -7,8 +7,19 @@

#include "gt/intel_gt.h"
#include "intel_gsc_uc.h"
#include "intel_gsc_fw.h"
#include "i915_drv.h"

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;
Expand All @@ -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
Expand All @@ -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(&gt->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(&gt->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;
Expand All @@ -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);
}
11 changes: 11 additions & 0 deletions drivers/gpu/drm/i915/gt/uc/intel_gsc_uc.h
Expand Up @@ -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)
{
Expand Down

0 comments on commit 2af091a

Please sign in to comment.