[Intel-gfx] [PATCH 04/18] drm/i915: GuC firmware loader
yu.dai at intel.com
yu.dai at intel.com
Thu Mar 26 12:41:11 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 | 6 +
drivers/gpu/drm/i915/i915_gem_stolen.c | 10 ++
drivers/gpu/drm/i915/i915_reg.h | 4 +-
drivers/gpu/drm/i915/intel_guc.h | 95 +++++++++++
drivers/gpu/drm/i915/intel_guc_loader.c | 271 ++++++++++++++++++++++++++++++++
7 files changed, 393 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 d49ed68..a4e6ffb 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);
@@ -828,6 +829,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;
@@ -1071,6 +1076,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_batch_pool_fini(&dev_priv->mm.batch_pool);
i915_gem_context_fini(dev);
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index e4215a0..1a60f86 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:
*/
@@ -1576,6 +1577,7 @@ struct drm_i915_private {
struct intel_gmbus gmbus[GMBUS_NUM_PORTS];
+ struct intel_guc guc;
/** gmbus_mutex protects against concurrent usage of the single hw gmbus
* controller on different i2c buses. */
@@ -2405,6 +2407,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) || IS_GEN8(dev))
+#define HAS_GUC_SCHED(dev) (IS_GEN9(dev) || IS_BROADWELL(dev))
+
#define INTEL_PCH_DEVICE_ID_MASK 0xff00
#define INTEL_PCH_IBX_DEVICE_ID_TYPE 0x3b00
#define INTEL_PCH_CPT_DEVICE_ID_TYPE 0x1c00
@@ -2958,6 +2963,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 f8da716..1377076 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 b522eb6..dfd1eb4 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -6289,7 +6289,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..d8e7c00
--- /dev/null
+++ b/drivers/gpu/drm/i915/intel_guc.h
@@ -0,0 +1,95 @@
+/*
+ * 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_RSA_SCRATCH_0 0xc200
+#define UOS_RSA_SIG_SIZE 0x100
+#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..91af710
--- /dev/null
+++ b/drivers/gpu/drm/i915/intel_guc_loader.c
@@ -0,0 +1,271 @@
+/*
+ * 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"
+
+#define I915_GUC_UCODE_GEN8 "i915/guc_gen8.bin"
+#define I915_GUC_UCODE_GEN9 "i915/guc_gen9.bin"
+MODULE_FIRMWARE(I915_GUC_UCODE_GEN8);
+MODULE_FIRMWARE(I915_GUC_UCODE_GEN9);
+
+static int copy_rsa(struct drm_i915_private *dev_priv)
+{
+ struct sg_table *st = dev_priv->guc.guc_fw.uc_fw_obj->pages;
+ uint32_t rsa[UOS_RSA_SIG_SIZE / sizeof(uint32_t)];
+ size_t bytes;
+ int i;
+
+ bytes = sg_copy_to_buffer(st->sgl, st->nents, rsa, UOS_RSA_SIG_SIZE);
+ if (bytes != UOS_RSA_SIG_SIZE)
+ return -ENXIO;
+
+ for (i = 0; i < UOS_RSA_SIG_SIZE / sizeof(uint32_t); i++)
+ I915_WRITE(UOS_RSA_SCRATCH_0 + i * sizeof(uint32_t), rsa[i]);
+
+ return 0;
+}
+
+/* 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.
+ *
+ * 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.
+ */
+static int ucode_dma_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;
+ const unsigned long offset = i915_gem_obj_ggtt_offset(fw_obj);
+ u32 status;
+ int ret = 0;
+
+ /* Set the source address for the uCode */
+ I915_WRITE(DMA_ADDR_0_LOW, lower_32_bits(offset) + UOS_RSA_SIG_SIZE);
+ 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, guc_fw->uc_fw_size - UOS_RSA_SIG_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(¶ms, 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);
+ }
+
+ /* Copy RSA signature from the fw image to HW for verification */
+ ret = copy_rsa(dev_priv);
+ if (ret)
+ goto out;
+
+ set_guc_init_params(dev_priv);
+
+ /* FIXME: !UPSTREAM - I don't have real keys, so we need to disable the
+ * authentication. This can only work if the part is fused in a special
+ * configuration. Therefore, even if it leaked externally, it won't be
+ * detrimental to security
+ */
+ I915_WRITE(0xc068, 0x3);
+
+ ret = ucode_dma_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;
+}
+
+/*
+ * 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;
+}
+
+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 (IS_GEN8(dev))
+ path = I915_GUC_UCODE_GEN8;
+ else if (IS_GEN9(dev))
+ path = I915_GUC_UCODE_GEN9;
+
+ intel_uc_fw_init(dev, guc_fw, "GuC", path, HAS_GUC_UCODE(dev));
+}
+
+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);
+}
--
1.9.1
More information about the Intel-gfx
mailing list