[Intel-gfx] [PATCH v3 04/15] drm/i915: GuC firmware loader

yu.dai at intel.com yu.dai at intel.com
Fri Apr 17 14:21:09 PDT 2015


From: Alex Dai <yu.dai at intel.com>

Add GuC firmware loader. It uses the unified firmware loader to
fetch firmware blob first, then load to hw in driver main thread.

Issue: VIZ-4884
Signed-off-by: Alex Dai <yu.dai at intel.com>
---
 drivers/gpu/drm/i915/Makefile           |   3 +-
 drivers/gpu/drm/i915/i915_dma.c         |   6 +
 drivers/gpu/drm/i915/i915_drv.h         |   7 +
 drivers/gpu/drm/i915/i915_gem_stolen.c  |  10 +
 drivers/gpu/drm/i915/i915_reg.h         |   4 +-
 drivers/gpu/drm/i915/intel_guc.h        | 103 ++++++++++
 drivers/gpu/drm/i915/intel_guc_loader.c | 348 ++++++++++++++++++++++++++++++++
 drivers/gpu/drm/i915/intel_uc_loader.c  |   3 +
 drivers/gpu/drm/i915/intel_uc_loader.h  |   4 +
 9 files changed, 486 insertions(+), 2 deletions(-)
 create mode 100644 drivers/gpu/drm/i915/intel_guc.h
 create mode 100644 drivers/gpu/drm/i915/intel_guc_loader.c

diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile
index 1b027c7..6188302 100644
--- a/drivers/gpu/drm/i915/Makefile
+++ b/drivers/gpu/drm/i915/Makefile
@@ -40,7 +40,8 @@ i915-y += i915_cmd_parser.o \
 	  intel_uncore.o
 
 # ancilliary microcontroller support
-i915-y += intel_uc_loader.o
+i915-y += intel_uc_loader.o \
+	  intel_guc_loader.o
 
 # autogenerated null render state
 i915-y += intel_renderstate_gen6.o \
diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c
index e44116f..5d50b5b 100644
--- a/drivers/gpu/drm/i915/i915_dma.c
+++ b/drivers/gpu/drm/i915/i915_dma.c
@@ -465,6 +465,7 @@ static int i915_load_modeset_init(struct drm_device *dev)
 
 cleanup_gem:
 	mutex_lock(&dev->struct_mutex);
+	intel_guc_ucode_fini(dev);
 	i915_gem_cleanup_ringbuffer(dev);
 	i915_gem_context_fini(dev);
 	mutex_unlock(&dev->struct_mutex);
@@ -861,6 +862,10 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
 
 	intel_uncore_init(dev);
 
+	intel_wopcm_init(dev);
+
+	intel_guc_ucode_init(dev);
+
 	ret = i915_gem_gtt_init(dev);
 	if (ret)
 		goto out_regs;
@@ -1108,6 +1113,7 @@ int i915_driver_unload(struct drm_device *dev)
 	flush_workqueue(dev_priv->wq);
 
 	mutex_lock(&dev->struct_mutex);
+	intel_guc_ucode_fini(dev);
 	i915_gem_cleanup_ringbuffer(dev);
 	i915_gem_context_fini(dev);
 	mutex_unlock(&dev->struct_mutex);
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 6e8d106..235fc08 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -50,6 +50,7 @@
 #include <linux/intel-iommu.h>
 #include <linux/kref.h>
 #include <linux/pm_qos.h>
+#include "intel_guc.h"
 
 /* General customization:
  */
@@ -1581,6 +1582,8 @@ struct drm_i915_private {
 
 	struct intel_gmbus gmbus[GMBUS_NUM_PINS];
 
+	struct intel_guc guc;
+
 	/** gmbus_mutex protects against concurrent usage of the single hw gmbus
 	 * controller on different i2c buses. */
 	struct mutex gmbus_mutex;
@@ -2430,6 +2433,9 @@ struct drm_i915_cmd_table {
 #define HAS_RC6(dev)		(INTEL_INFO(dev)->gen >= 6)
 #define HAS_RC6p(dev)		(INTEL_INFO(dev)->gen == 6 || IS_IVYBRIDGE(dev))
 
+#define HAS_GUC_UCODE(dev)	(IS_GEN9(dev))
+#define HAS_GUC_SCHED(dev)	(IS_GEN9(dev))
+
 #define INTEL_PCH_DEVICE_ID_MASK		0xff00
 #define INTEL_PCH_IBX_DEVICE_ID_TYPE		0x3b00
 #define INTEL_PCH_CPT_DEVICE_ID_TYPE		0x1c00
@@ -3008,6 +3014,7 @@ i915_gem_object_create_stolen_for_preallocated(struct drm_device *dev,
 					       u32 stolen_offset,
 					       u32 gtt_offset,
 					       u32 size);
+void intel_wopcm_init(struct drm_device *dev);
 
 /* i915_gem_shrinker.c */
 unsigned long i915_gem_shrink(struct drm_i915_private *dev_priv,
diff --git a/drivers/gpu/drm/i915/i915_gem_stolen.c b/drivers/gpu/drm/i915/i915_gem_stolen.c
index 348ed5a..fbf7667 100644
--- a/drivers/gpu/drm/i915/i915_gem_stolen.c
+++ b/drivers/gpu/drm/i915/i915_gem_stolen.c
@@ -551,3 +551,13 @@ err_out:
 	drm_gem_object_unreference(&obj->base);
 	return NULL;
 }
+
+void intel_wopcm_init(struct drm_device *dev)
+{
+	struct drm_i915_private *dev_priv = dev->dev_private;
+
+	if (HAS_GUC_UCODE(dev)) {
+		I915_WRITE(GUC_WOPCM_SIZE, GUC_WOPCM_SIZE_VALUE);
+		I915_WRITE(DMA_GUC_WOPCM_OFFSET, GUC_WOPCM_OFFSET);
+	}
+}
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index d47afbc..f7c00dc 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -6693,7 +6693,9 @@ enum skl_disp_power_wells {
 #define   GEN9_PGCTL_SSB_EU311_ACK	(1 << 14)
 
 #define GEN7_MISCCPCTL			(0x9424)
-#define   GEN7_DOP_CLOCK_GATE_ENABLE	(1<<0)
+#define   GEN7_DOP_CLOCK_GATE_ENABLE		(1<<0)
+#define   GEN8_DOP_CLOCK_GATE_CFCLK_ENABLE	(1<<2)
+#define   GEN8_DOP_CLOCK_GATE_GUC_ENABLE	(1<<4)
 
 /* IVYBRIDGE DPF */
 #define GEN7_L3CDERRST1			0xB008 /* L3CD Error Status 1 */
diff --git a/drivers/gpu/drm/i915/intel_guc.h b/drivers/gpu/drm/i915/intel_guc.h
new file mode 100644
index 0000000..4fafcec
--- /dev/null
+++ b/drivers/gpu/drm/i915/intel_guc.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright © 2014 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ */
+#ifndef _INTEL_GUC_H_
+#define _INTEL_GUC_H_
+
+#include "intel_guc_api.h"
+#include "intel_uc_loader.h"
+
+struct intel_guc {
+	struct intel_uc_fw guc_fw;
+};
+
+#define GUC_STATUS		0xc000
+#define   GS_BOOTROM_SHIFT	1
+#define   GS_BOOTROM_MASK	(0x7F << GS_BOOTROM_SHIFT)
+#define   GS_BOOTROM_RSA_FAILED	(0x50 << GS_BOOTROM_SHIFT)
+#define   GS_UKERNEL_SHIFT	8
+#define   GS_UKERNEL_MASK	(0xFF << GS_UKERNEL_SHIFT)
+#define   GS_UKERNEL_LAPIC_DONE	(0x30 << GS_UKERNEL_SHIFT)
+#define   GS_UKERNEL_DPC_ERROR	(0x60 << GS_UKERNEL_SHIFT)
+#define   GS_UKERNEL_READY	(0xF0 << GS_UKERNEL_SHIFT)
+#define   GS_MIA_SHIFT		16
+#define   GS_MIA_MASK		(0x7 << GS_MIA_SHIFT)
+
+#define GUC_WOPCM_SIZE		0xc050
+#define   GUC_WOPCM_SIZE_VALUE  (0x80 << 12)	/* 512KB */
+#define   GUC_WOPCM_OFFSET	0x80000		/* 512KB */
+#define SOFT_SCRATCH(n)		(0xc180 + ((n) * 4))
+
+#define UOS_CSS_HEADER_OFFSET	0
+#define UOS_CSS_HEADER_SIZE	0x80
+#define   UOS_VER_MINOR_OFFSET	0x44
+#define   UOS_VER_MAJOR_OFFSET	0x46
+#define UOS_RSA_SIG_OFFSET	0x80
+#define UOS_RSA_SIG_SIZE	0x100
+#define UOS_CSS_SIGNING_SIZE	0x204
+#define UOS_UCODE_OFFSET	(UOS_CSS_HEADER_SIZE + UOS_CSS_SIGNING_SIZE)
+
+#define UOS_RSA_SCRATCH_0	0xc200
+#define DMA_ADDR_0_LOW		0xc300
+#define DMA_ADDR_0_HIGH		0xc304
+#define DMA_ADDR_1_LOW		0xc308
+#define DMA_ADDR_1_HIGH		0xc30c
+#define   DMA_ADDRESS_SPACE_WOPCM	(7 << 16)
+#define   DMA_ADDRESS_SPACE_GTT		(8 << 16)
+#define DMA_COPY_SIZE		0xc310
+#define DMA_CTRL		0xc314
+#define   UOS_MOVE		(1<<4)
+#define   START_DMA		(1<<0)
+#define DMA_GUC_WOPCM_OFFSET	0xc340
+
+#define GEN8_GT_PM_CONFIG		0x138140
+#define GEN9_GT_PM_CONFIG		0x13816c
+#define   GEN8_GT_DOORBELL_ENABLE	(1<<0)
+
+#define GEN8_GTCR 0x4274
+#define   GEN8_GTCR_INVALIDATE (1<<0)
+
+#define GUC_ARAT_C6DIS		0xA178
+
+#define GUC_SHIM_CONTROL	(0xc064)
+#define   GUC_DISABLE_SRAM_INIT_TO_ZEROES	(1<<0)
+#define   GUC_ENABLE_READ_CACHE_LOGIC		(1<<1)
+#define   GUC_ENABLE_MIA_CACHING		(1<<2)
+#define   GUC_GEN10_MSGCH_ENABLE		(1<<4)
+#define   GUC_ENABLE_READ_CACHE_FOR_SRAM_DATA	(1<<9)
+#define   GUC_ENABLE_READ_CACHE_FOR_WOPCM_DATA	(1<<10)
+#define   GUC_ENABLE_MIA_CLOCK_GATING		(1<<15)
+#define   GUC_GEN10_SHIM_WC_ENABLE		(1<<21)
+
+#define GUC_SHIM_CONTROL_VALUE	(GUC_DISABLE_SRAM_INIT_TO_ZEROES | \
+				 GUC_ENABLE_READ_CACHE_LOGIC | \
+				 GUC_ENABLE_MIA_CACHING | \
+				 GUC_ENABLE_READ_CACHE_FOR_SRAM_DATA | \
+				 GUC_ENABLE_READ_CACHE_FOR_WOPCM_DATA)
+
+/* intel_guc_loader.c */
+extern int intel_guc_load_ucode(struct drm_device *dev);
+extern void intel_guc_ucode_fini(struct drm_device *dev);
+extern void intel_guc_ucode_init(struct drm_device *dev);
+
+#endif
diff --git a/drivers/gpu/drm/i915/intel_guc_loader.c b/drivers/gpu/drm/i915/intel_guc_loader.c
new file mode 100644
index 0000000..a999044
--- /dev/null
+++ b/drivers/gpu/drm/i915/intel_guc_loader.c
@@ -0,0 +1,348 @@
+/*
+ * Copyright © 2014 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ */
+#include <linux/firmware.h>
+#include "i915_drv.h"
+#include "intel_guc.h"
+
+/**
+ * DOC: GuC
+ *
+ * intel_guc:
+ * Top level structure of guc. It handles firmware loading and manages client
+ * pool and doorbells. intel_guc owns a i915_guc_client to replace the legacy
+ * ExecList submission.
+ *
+ * Firmware versioning:
+ * The firmware build process will generate a version header file with major and
+ * minor version defined. The versions are built into CSS header of firmware.
+ * i915 kernel driver set the minimal firmware version required per platform.
+ * The firmware installation package will install (symbolic link) proper version
+ * of firmware.
+ *
+ */
+
+#define I915_SKL_GUC_UCODE "i915/skl_guc_ver1.bin"
+MODULE_FIRMWARE(I915_SKL_GUC_UCODE);
+#define I915_BXT_GUC_UCODE "i915/bxt_guc_ver1.bin"
+MODULE_FIRMWARE(I915_BXT_GUC_UCODE);
+
+/* Read GuC status register (GUC_STATUS)
+ * Return true if get a success code from normal boot or RC6 boot
+ */
+static inline bool i915_guc_get_status(struct drm_i915_private *dev_priv,
+					u32 *status)
+{
+	*status = I915_READ(GUC_STATUS);
+	return (((*status) & GS_UKERNEL_MASK) == GS_UKERNEL_READY ||
+		((*status) & GS_UKERNEL_MASK) == GS_UKERNEL_LAPIC_DONE);
+}
+
+/* Transfers the firmware image to RAM for execution by the microcontroller.
+
+ * GuC Firmware layout:
+ * +-------------------------------+  ----
+ * |          CSS header           |  128B
+ * +-------------------------------+  ----
+ * |         RSA signature         |  256B
+ * +-------------------------------+  ----
+ * |         RSA public Key        |  256B
+ * +-------------------------------+  ----
+ * |       Public key modulus      |    4B
+ * +-------------------------------+  ----
+ * |             uCode             |
+ * +-------------------------------+  ----
+ *
+ * Architecturally, the DMA engine is bidirectional, and in can potentially
+ * even transfer between GTT locations. This functionality is left out of the
+ * API for now as there is no need for it.
+ *
+ * Be note that GuC need the CSS header plus uKernel code to be copied as one
+ * chunk of data. RSA sig data is loaded via MMIO.
+ */
+static int ucode_xfer_sync(struct drm_i915_private *dev_priv)
+{
+	struct intel_uc_fw *guc_fw = &dev_priv->guc.guc_fw;
+	struct drm_i915_gem_object *fw_obj = guc_fw->uc_fw_obj;
+	unsigned long offset;
+	struct sg_table *sg = fw_obj->pages;
+	u32 status, ucode_size, *blob;
+	int i, ret = 0;
+
+	/* Copy RSA signature from the fw image to HW for verification */
+	blob = (uint32_t *)(dev_priv->guc.guc_fw.uc_fw_blob->data
+			+ UOS_RSA_SIG_OFFSET);
+	for (i = 0; i < UOS_RSA_SIG_SIZE / sizeof(uint32_t); i++)
+		I915_WRITE(UOS_RSA_SCRATCH_0 + i * sizeof(uint32_t), blob[i]);
+
+	/* Re-arrange data per GuC request (CSS header + uCode) */
+
+	/* Copy CSS header  */
+	blob = (uint32_t *)(dev_priv->guc.guc_fw.uc_fw_blob->data
+			+ UOS_CSS_HEADER_OFFSET);
+	sg_copy_from_buffer(sg->sgl, sg->nents, blob, UOS_CSS_HEADER_SIZE);
+
+	/* Copy ucode */
+	blob = (uint32_t *)(dev_priv->guc.guc_fw.uc_fw_blob->data
+			+ UOS_UCODE_OFFSET);
+	ucode_size = guc_fw->uc_fw_size - UOS_UCODE_OFFSET;
+	sg_pcopy_from_buffer(sg->sgl, sg->nents, blob,
+			ucode_size, UOS_CSS_HEADER_SIZE);
+
+	/* Set the source address for the new blob */
+	offset = i915_gem_obj_ggtt_offset(fw_obj);
+	I915_WRITE(DMA_ADDR_0_LOW, lower_32_bits(offset));
+	I915_WRITE(DMA_ADDR_0_HIGH, upper_32_bits(offset) & 0xFFFF);
+
+	/* Set the destination. Current uCode expects an 8k stack starting from
+	 * offset 0. */
+	I915_WRITE(DMA_ADDR_1_LOW, 0x2000);
+
+	/* XXX: The image is automatically transfered to SRAM after the RSA
+	 * verification. This is why the address space is chosen as such. */
+	I915_WRITE(DMA_ADDR_1_HIGH, DMA_ADDRESS_SPACE_WOPCM);
+
+	I915_WRITE(DMA_COPY_SIZE, ucode_size + UOS_CSS_HEADER_SIZE);
+
+	/* Finally start the DMA */
+	I915_WRITE(DMA_CTRL, _MASKED_BIT_ENABLE(UOS_MOVE | START_DMA));
+
+	/* NB: Docs recommend not using the interrupt for completion.
+	 * FIXME: what's a valid timeout? */
+	ret = wait_for_atomic(i915_guc_get_status(dev_priv, &status), 10);
+
+	DRM_DEBUG_DRIVER("DMA status = 0x%x, GuC status 0x%x\n",
+			I915_READ(DMA_CTRL), status);
+
+	return ret;
+}
+
+static u32 get_gttype(struct drm_device *dev)
+{
+	/* XXX: GT type based on PCI device ID? field seems unused by fw */
+	return 0;
+}
+
+static u32 get_core_family(struct drm_device *dev)
+{
+	switch (INTEL_INFO(dev)->gen) {
+	case 8:
+		return GFXCORE_FAMILY_GEN8;
+	case 9:
+		return GFXCORE_FAMILY_GEN9;
+	default:
+		DRM_ERROR("GUC: unknown gen for scheduler init\n");
+		return GFXCORE_FAMILY_FORCE_ULONG;
+	}
+}
+
+static void set_guc_init_params(struct drm_i915_private *dev_priv)
+{
+	u32 params[GUC_CTL_MAX_DWORDS];
+	int i;
+
+	memset(&params, 0, sizeof(params));
+
+	params[GUC_CTL_DEVICE_INFO] |=
+		(get_gttype(dev_priv->dev) << GUC_CTL_GTTYPE_SHIFT) |
+		(get_core_family(dev_priv->dev) << GUC_CTL_COREFAMILY_SHIFT);
+
+	/* GuC ARAT increment is 10 ns. GuC default scheduler quantum is one
+	 * second. This ARAR is calculated by:
+	 * Scheduler-Quantum-in-ns / ARAT-increment-in-ns = 1000000000 / 10
+	 */
+	params[GUC_CTL_ARAT_HIGH] = 0;
+	params[GUC_CTL_ARAT_LOW] = 100000000;
+
+	params[GUC_CTL_WA] |= GUC_CTL_WA_UK_BY_DRIVER;
+
+	params[GUC_CTL_FEATURE] |= GUC_CTL_DISABLE_SCHEDULER |
+			GUC_CTL_VCS2_ENABLED;
+
+	/* XXX: Set up log buffer */
+
+	I915_WRITE(SOFT_SCRATCH(0), 0);
+
+	for (i = 0; i < GUC_CTL_MAX_DWORDS; i++)
+		I915_WRITE(SOFT_SCRATCH(1 + i), params[i]);
+}
+
+/*
+ * Loads the GuC firmware blob in to the MinuteIA.
+ */
+static int guc_load_ucode(struct drm_device *dev)
+{
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	struct intel_uc_fw *guc_fw = &dev_priv->guc.guc_fw;
+	bool pinned = false;
+	int ret;
+
+	WARN_ON(!mutex_is_locked(&dev->struct_mutex));
+
+	ret = i915_gem_obj_ggtt_pin(guc_fw->uc_fw_obj, 0, 0);
+	if (ret)
+		goto out;
+	pinned = true;
+
+	/* Invalidate GuC TLB to let GuC take the latest updates to GTT. */
+	I915_WRITE(GEN8_GTCR, GEN8_GTCR_INVALIDATE);
+
+	/* Set MMIO/WA for GuC init */
+
+	/* Enable MIA caching. GuC clock gating is disabled. */
+	I915_WRITE(GUC_SHIM_CONTROL, GUC_SHIM_CONTROL_VALUE);
+
+	/* WaC6DisallowByGfxPause*/
+	I915_WRITE(GEN6_GFXPAUSE, 0x30FFF);
+
+	if (IS_GEN9(dev)) {
+		/* DOP Clock Gating Enable for GuC clocks */
+		I915_WRITE(GEN7_MISCCPCTL, (GEN8_DOP_CLOCK_GATE_GUC_ENABLE |
+					    I915_READ(GEN7_MISCCPCTL)));
+
+		/* allows for 5us before GT can go to RC6 */
+		I915_WRITE(GUC_ARAT_C6DIS, 0x1FF);
+	}
+
+	set_guc_init_params(dev_priv);
+
+	ret = ucode_xfer_sync(dev_priv);
+
+	/* We can free the object pages now, and we would, except we might as
+	 * well keep it around for suspend/resume. Instead, we just wait for the
+	 * DMA to complete, and unpin the object
+	 */
+
+out:
+	DRM_DEBUG_DRIVER("return %d, pinned %d\n", ret, pinned);
+
+	if (pinned)
+		i915_gem_object_ggtt_unpin(guc_fw->uc_fw_obj);
+
+	return ret;
+}
+
+/**
+ * intel_guc_load_ucode() - load ucode to hw
+ *
+ * Called from gem_init_hw() during driver loading and also after a GPU reset.
+ * Check that the firmware fetching process has succeeded, and if so transfer
+ * the loaded image to the hardware.
+ */
+int intel_guc_load_ucode(struct drm_device *dev)
+{
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	struct intel_uc_fw *guc_fw = &dev_priv->guc.guc_fw;
+	int err;
+
+	guc_fw->uc_fw_load_status = INTEL_UC_FIRMWARE_NONE;
+	if (guc_fw->uc_fw_fetch_status == INTEL_UC_FIRMWARE_NONE)
+		return 0;
+
+	guc_fw->uc_fw_load_status = INTEL_UC_FIRMWARE_PENDING;
+	err = intel_uc_fw_check(dev, guc_fw);
+	if (err)
+		goto fail;
+
+	err = guc_load_ucode(dev);
+	if (err)
+		goto fail;
+
+	guc_fw->uc_fw_load_status = INTEL_UC_FIRMWARE_SUCCESS;
+	return 0;
+
+fail:
+	guc_fw->uc_fw_load_status = INTEL_UC_FIRMWARE_FAIL;
+	return err;
+}
+
+/*
+ * Check the firmware that was found; return TRUE if acceptable.
+ *
+ * For the GuC, we just check the version number embedded at a well-known
+ * offset within the firmware blob; note that major / minor version are two
+ * bytes each - in *u16*
+ */
+static bool intel_guc_ucode_check(struct intel_uc_fw *guc_fw)
+{
+	uint32_t major, minor;
+	u8 *css_header = (u8 *)guc_fw->uc_fw_blob->data + UOS_CSS_HEADER_OFFSET;
+
+	major = *(u16 *)(css_header + UOS_VER_MAJOR_OFFSET);
+	minor = *(u16 *)(css_header + UOS_VER_MINOR_OFFSET);
+
+	if (major == guc_fw->uc_fw_ver_major &&
+	    minor >= guc_fw->uc_fw_ver_minor) {
+		DRM_DEBUG_DRIVER("firmware version %d.%d OK (minimum %d.%d)\n",
+			 major, minor,
+			 guc_fw->uc_fw_ver_major, guc_fw->uc_fw_ver_minor);
+		return true;
+	}
+
+	DRM_ERROR("GuC firmware version %d.%d, required %d.%d\n",
+		 major, minor,
+		 guc_fw->uc_fw_ver_major, guc_fw->uc_fw_ver_minor);
+	return false;
+}
+
+/**
+ * intel_guc_ucode_init() - init a firmware loading request
+ */
+void intel_guc_ucode_init(struct drm_device *dev)
+{
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	struct intel_uc_fw *guc_fw = &dev_priv->guc.guc_fw;
+	const char *path = NULL;
+
+	if (!HAS_GUC_UCODE(dev)) {
+		guc_fw->uc_fw_fetch_status = INTEL_UC_FIRMWARE_NONE;
+		guc_fw->uc_fw_load_status = INTEL_UC_FIRMWARE_NONE;
+		return;
+	}
+
+	if (IS_SKYLAKE(dev)) {
+		path = I915_SKL_GUC_UCODE;
+		guc_fw->uc_fw_ver_major = 1;
+		guc_fw->uc_fw_ver_minor = 0;
+		guc_fw->uc_fw_check = intel_guc_ucode_check;
+	}
+	else if (IS_BROXTON(dev)) {
+		path = I915_BXT_GUC_UCODE;
+		guc_fw->uc_fw_ver_major = 1;
+		guc_fw->uc_fw_ver_minor = 0;
+		guc_fw->uc_fw_check = intel_guc_ucode_check;
+	}
+
+	intel_uc_fw_init(dev, guc_fw, "GuC", path);
+}
+
+/**
+ * intel_guc_ucode_fini() - clean up firmware blob allocated
+ */
+void intel_guc_ucode_fini(struct drm_device *dev)
+{
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	struct intel_uc_fw *guc_fw = &dev_priv->guc.guc_fw;
+
+	intel_uc_fw_fini(dev, guc_fw);
+}
diff --git a/drivers/gpu/drm/i915/intel_uc_loader.c b/drivers/gpu/drm/i915/intel_uc_loader.c
index bc499f4..65031ff 100644
--- a/drivers/gpu/drm/i915/intel_uc_loader.c
+++ b/drivers/gpu/drm/i915/intel_uc_loader.c
@@ -77,6 +77,9 @@ static void uc_fw_finish(struct drm_device *dev, struct intel_uc_fw *uc_fw)
 		uc_fw->uc_fw_blob = fw;
 	}
 
+	if (uc_fw->uc_fw_check && !uc_fw->uc_fw_check(uc_fw))
+		goto fail;
+
 	obj = i915_gem_alloc_object(dev, round_up(fw->size, PAGE_SIZE));
 	if (!obj)
 		goto fail;
diff --git a/drivers/gpu/drm/i915/intel_uc_loader.h b/drivers/gpu/drm/i915/intel_uc_loader.h
index 0994f98..4ed6e94 100644
--- a/drivers/gpu/drm/i915/intel_uc_loader.h
+++ b/drivers/gpu/drm/i915/intel_uc_loader.h
@@ -68,11 +68,15 @@ struct intel_uc_fw {
 	struct drm_i915_gem_object *	uc_fw_obj;
 	enum intel_uc_fw_status		uc_fw_fetch_status;
 	enum intel_uc_fw_status		uc_fw_load_status;
+	uint32_t			uc_fw_ver_major;
+	uint32_t			uc_fw_ver_minor;
+	bool				(*uc_fw_check)(struct intel_uc_fw *);
 };
 
 void intel_uc_fw_init(struct drm_device *dev, struct intel_uc_fw *uc_fw,
 	const char *uc_name, const char *fw_path);
 int intel_uc_fw_check(struct drm_device *dev, struct intel_uc_fw *uc_fw);
 void intel_uc_fw_fini(struct drm_device *dev, struct intel_uc_fw *uc_fw);
+bool intel_uc_fw_version_check(struct intel_uc_fw *fw, const char *data);
 
 #endif
-- 
1.9.1



More information about the Intel-gfx mailing list