@@ -42,6 +42,9 @@ i915-y += i915_cmd_parser.o \
# generic ancilliary microcontroller support
i915-y += intel_uc_loader.o
+# general-purpose microcontroller (GuC) support
+i915-y += intel_guc_loader.o
+
# autogenerated null render state
i915-y += intel_renderstate_gen6.o \
intel_renderstate_gen7.o \
@@ -469,6 +469,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);
@@ -866,6 +867,8 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
intel_uncore_init(dev);
+ intel_guc_ucode_init(dev);
+
/* Load CSR Firmware for SKL */
intel_csr_ucode_init(dev);
@@ -1117,6 +1120,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);
@@ -50,6 +50,7 @@
#include <linux/intel-iommu.h>
#include <linux/kref.h>
#include <linux/pm_qos.h>
+#include "intel_guc.h"
/* General customization:
*/
@@ -1676,6 +1677,8 @@ struct drm_i915_private {
struct i915_virtual_gpu vgpu;
+ struct intel_guc guc;
+
struct intel_csr csr;
/* Display CSR-related protection */
@@ -1920,6 +1923,11 @@ static inline struct drm_i915_private *dev_to_i915(struct device *dev)
return to_i915(dev_get_drvdata(dev));
}
+static inline struct drm_i915_private *guc_to_i915(struct intel_guc *guc)
+{
+ return container_of(guc, struct drm_i915_private, guc);
+}
+
/* Iterate over initialised rings */
#define for_each_ring(ring__, dev_priv__, i__) \
for ((i__) = 0; (i__) < I915_NUM_RINGS; (i__)++) \
@@ -2520,6 +2528,9 @@ struct drm_i915_cmd_table {
#define HAS_CSR(dev) (IS_SKYLAKE(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
@@ -5073,6 +5073,9 @@ i915_gem_init_hw(struct drm_device *dev)
goto out;
}
+ /* We can't enable contexts until all firmware is loaded */
+ ret = intel_guc_ucode_load(dev, true);
+
/* Now it is safe to go back round and do everything else: */
for_each_ring(ring, dev_priv, i) {
struct drm_i915_gem_request *req;
@@ -6795,7 +6795,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 */
new file mode 100644
@@ -0,0 +1,49 @@
+/*
+ * 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_uc_loader.h"
+#include "intel_guc_fwif.h"
+#include "i915_guc_reg.h"
+
+struct intel_guc {
+ /* Generic uC firmware management */
+ struct intel_uc_fw guc_fw;
+
+ /* GuC-specific additions */
+ uint16_t fw_major_wanted;
+ uint16_t fw_minor_wanted;
+ uint16_t fw_major_found;
+ uint16_t fw_minor_found;
+
+ uint32_t log_flags;
+};
+
+/* intel_guc_loader.c */
+extern void intel_guc_ucode_init(struct drm_device *dev);
+extern int intel_guc_ucode_load(struct drm_device *dev, bool wait);
+extern void intel_guc_ucode_fini(struct drm_device *dev);
+
+#endif
new file mode 100644
@@ -0,0 +1,440 @@
+/*
+ * 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.
+ *
+ * Authors:
+ * Vinit Azad <vinit.azad@intel.com>
+ * Ben Widawsky <ben@bwidawsk.net>
+ * Dave Gordon <david.s.gordon@intel.com>
+ * Alex Dai <yu.dai@intel.com>
+ */
+#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.
+ *
+ * GuC address space:
+ * GuC does not allow any gfx GGTT address that falls into range [0, WOPCM_TOP),
+ * which is reserved for Boot ROM, SRAM and WOPCM. Currently this top address is
+ * 512K. In order to exclude 0-512K address space from GGTT, all gfx objects
+ * used by GuC is pinned with PIN_OFFSET_BIAS along with size of WOPCM.
+ *
+ * Firmware log:
+ * Firmware log is enabled by setting i915.guc_log_level to non-negative level.
+ * Log data is printed out via reading debugfs i915_guc_log_dump. Reading from
+ * i915_guc_load_status will print out firmware loading status and scratch
+ * registers value.
+ *
+ */
+
+#define I915_SKL_GUC_UCODE "i915/skl_guc_ver3.bin"
+MODULE_FIRMWARE(I915_SKL_GUC_UCODE);
+
+static u32 get_gttype(struct drm_i915_private *dev_priv)
+{
+ /* XXX: GT type based on PCI device ID? field seems unused by fw */
+ return 0;
+}
+
+static u32 get_core_family(struct drm_i915_private *dev_priv)
+{
+ switch (INTEL_INFO(dev_priv)->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)
+{
+ struct intel_guc *guc = &dev_priv->guc;
+ u32 params[GUC_CTL_MAX_DWORDS];
+ int i;
+
+ memset(¶ms, 0, sizeof(params));
+
+ params[GUC_CTL_DEVICE_INFO] |=
+ (get_gttype(dev_priv) << GUC_CTL_GTTYPE_SHIFT) |
+ (get_core_family(dev_priv) << 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;
+
+ if (i915.guc_log_level >= 0) {
+ params[GUC_CTL_LOG_PARAMS] = guc->log_flags;
+ params[GUC_CTL_DEBUG] =
+ i915.guc_log_level << GUC_LOG_VERBOSITY_SHIFT;
+ }
+
+ I915_WRITE(SOFT_SCRATCH(0), 0);
+
+ for (i = 0; i < GUC_CTL_MAX_DWORDS; i++)
+ I915_WRITE(SOFT_SCRATCH(1 + i), params[i]);
+}
+
+/* Read GuC status register (GUC_STATUS)
+ * Return true if get a success code from normal boot or RC6 boot
+ */
+static inline bool guc_ucode_response(struct drm_i915_private *dev_priv,
+ u32 *status)
+{
+ u32 val = I915_READ(GUC_STATUS);
+ *status = val;
+ return ((val & GS_UKERNEL_MASK) == GS_UKERNEL_READY ||
+ (val & GS_UKERNEL_MASK) == GS_UKERNEL_LAPIC_DONE);
+}
+
+/* Transfers the firmware image to RAM for execution by the microcontroller.
+ *
+ * GuC Firmware layout:
+ * +-------------------------------+ ----
+ * | CSS header | 128B
+ * | contains major/minor version |
+ * +-------------------------------+ ----
+ * | uCode |
+ * +-------------------------------+ ----
+ * | RSA signature | 256B
+ * +-------------------------------+ ----
+ * | RSA public Key | 256B
+ * +-------------------------------+ ----
+ * | Public key modulus | 4B
+ * +-------------------------------+ ----
+ *
+ * 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.
+ *
+ * Note that GuC needs the CSS header plus uKernel code to be copied by the
+ * DMA engine in one operation, whereas the RSA signature is loaded via MMIO.
+ */
+
+#define UOS_CSS_HEADER_OFFSET 0
+#define UOS_VER_MINOR_OFFSET 0x44
+#define UOS_VER_MAJOR_OFFSET 0x46
+#define UOS_CSS_HEADER_SIZE 0x80
+#define UOS_RSA_SIG_SIZE 0x100
+#define UOS_CSS_SIGNING_SIZE 0x204
+
+static int guc_ucode_xfer_dma(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, rsa[UOS_RSA_SIG_SIZE / sizeof(u32)];
+ int i, ret = 0;
+
+ /* uCode size, also is where RSA signature starts */
+ offset = ucode_size = guc_fw->uc_fw_size - UOS_CSS_SIGNING_SIZE;
+
+ /* Copy RSA signature from the fw image to HW for verification */
+ sg_pcopy_to_buffer(sg->sgl, sg->nents, rsa, UOS_RSA_SIG_SIZE, offset);
+ for (i = 0; i < UOS_RSA_SIG_SIZE / sizeof(u32); i++)
+ I915_WRITE(UOS_RSA_SCRATCH_0 + i * sizeof(u32), rsa[i]);
+
+ /* 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);
+
+ /* Finally start the DMA */
+ I915_WRITE(DMA_CTRL, _MASKED_BIT_ENABLE(UOS_MOVE | START_DMA));
+
+ /*
+ * Spin-wait for the DMA to complete & the GuC to start up.
+ * NB: Docs recommend not using the interrupt for completion.
+ * Measurements indicate this should take no more than 20ms, so a
+ * timeout here indicates that the GuC has failed and is unusable.
+ * (Higher levels of the driver will attempt to fall back to
+ * execlist mode if this happens.)
+ */
+ ret = wait_for_atomic(guc_ucode_response(dev_priv, &status), 100);
+
+ DRM_DEBUG_DRIVER("DMA status 0x%x, GuC status 0x%x\n",
+ I915_READ(DMA_CTRL), status);
+
+ if ((status & GS_BOOTROM_MASK) == GS_BOOTROM_RSA_FAILED) {
+ DRM_ERROR("%s firmware signature verification failed\n",
+ guc_fw->uc_name);
+ ret = -ENOEXEC;
+ }
+
+ DRM_DEBUG_DRIVER("returning %d\n", ret);
+
+ return ret;
+}
+
+/*
+ * Loads the GuC firmware blob in to the MinuteIA.
+ */
+static int guc_ucode_xfer(struct drm_i915_private *dev_priv)
+{
+ struct intel_uc_fw *guc_fw = &dev_priv->guc.guc_fw;
+ struct drm_device *dev = dev_priv->dev;
+ int ret;
+
+ ret = i915_gem_object_set_to_gtt_domain(guc_fw->uc_fw_obj, false);
+ if (ret) {
+ DRM_DEBUG_DRIVER("set-domain failed %d\n", ret);
+ return ret;
+ }
+
+ ret = i915_gem_obj_ggtt_pin(guc_fw->uc_fw_obj, 0, 0);
+ if (ret) {
+ DRM_DEBUG_DRIVER("pin failed %d\n", ret);
+ return ret;
+ }
+
+ WARN_ON(!mutex_is_locked(&dev->struct_mutex));
+ intel_uncore_forcewake_get(dev_priv, FORCEWAKE_ALL);
+
+ /* init WOPCM */
+ I915_WRITE(GUC_WOPCM_SIZE, GUC_WOPCM_SIZE_VALUE);
+ I915_WRITE(DMA_GUC_WOPCM_OFFSET, GUC_WOPCM_OFFSET);
+
+ /* 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 */
+ I915_WRITE(DRBMISC1, DOORBELL_ENABLE);
+
+ /* 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_SKYLAKE(dev))
+ I915_WRITE(GEN9_GT_PM_CONFIG, GEN8_GT_DOORBELL_ENABLE);
+ else
+ I915_WRITE(GEN8_GT_PM_CONFIG, GEN8_GT_DOORBELL_ENABLE);
+
+ 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 = guc_ucode_xfer_dma(dev_priv);
+
+ intel_uncore_forcewake_put(dev_priv, FORCEWAKE_ALL);
+
+ /*
+ * We keep the object pages for reuse during resume. But we can unpin it
+ * now that DMA has completed, so it doesn't continue to take up space.
+ */
+ i915_gem_object_ggtt_unpin(guc_fw->uc_fw_obj);
+
+ return ret;
+}
+
+/*
+ * Check the firmware that was found; if it's the wrong size or the wrong
+ * version, return FALSE. If it's OK, return TRUE; the generic loader code
+ * will then save the data for later in a pageable (tmpfs-backed) GEM object.
+ *
+ * The GuC firmware image has the version number embedded at a well-known
+ * offset within the firmware blob; note that major / minor version are
+ * TWO bytes each (i.e. u16), although all pointers and offsets are defined
+ * in terms of bytes (u8).
+ */
+static bool guc_ucode_check(struct intel_uc_fw *guc_fw)
+{
+ struct intel_guc *guc = container_of(guc_fw, struct intel_guc, guc_fw);
+ const u8 *css_header = guc_fw->uc_fw_blob->data + UOS_CSS_HEADER_OFFSET;
+ size_t blobsize = guc_fw->uc_fw_blob->size;
+ size_t minsize = UOS_CSS_HEADER_SIZE + UOS_CSS_SIGNING_SIZE;
+ size_t maxsize = GUC_WOPCM_SIZE_VALUE - 0x8000; /* 32k reserved */
+
+ DRM_DEBUG_DRIVER("firmware file size %zu (minimum %zu, maximum %zu)\n",
+ blobsize, minsize, maxsize);
+
+ /* Check the size of the blob befoe examining buffer contents */
+ if (blobsize < minsize || blobsize > maxsize)
+ return false;
+
+ guc->fw_major_found = *(u16 *)(css_header + UOS_VER_MAJOR_OFFSET);
+ guc->fw_minor_found = *(u16 *)(css_header + UOS_VER_MINOR_OFFSET);
+
+ if (guc->fw_major_found != guc->fw_major_wanted ||
+ guc->fw_minor_found < guc->fw_minor_wanted) {
+ DRM_ERROR("GuC firmware version %d.%d, required %d.%d\n",
+ guc->fw_major_found, guc->fw_minor_found,
+ guc->fw_major_wanted, guc->fw_minor_wanted);
+ return false;
+ }
+
+ DRM_DEBUG_DRIVER("firmware version %d.%d OK (minimum %d.%d)\n",
+ guc->fw_major_found, guc->fw_minor_found,
+ guc->fw_major_wanted, guc->fw_minor_wanted);
+
+ /* Override default GEM object allocation-and-save here, if needed */
+ return true;
+}
+
+/**
+ * intel_guc_ucode_init() - initiate a firmware loading request
+ *
+ * Called early during driver load, before GEM is initialised.
+ * Driver is single threaded, so no mutex is required.
+ */
+void intel_guc_ucode_init(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_guc *guc = &dev_priv->guc;
+ struct intel_uc_fw *guc_fw = &guc->guc_fw;
+ const char *path;
+
+ if (!HAS_GUC_SCHED(dev))
+ i915.enable_guc_submission = false;
+
+ if (!HAS_GUC_UCODE(dev)) {
+ path = NULL;
+ } else if (IS_SKYLAKE(dev)) {
+ path = I915_SKL_GUC_UCODE;
+ guc->fw_major_wanted = 3;
+ guc->fw_minor_wanted = 0;
+ } else {
+ i915.enable_guc_submission = false;
+ path = ""; /* unknown device */
+ }
+
+ intel_uc_fw_init(dev, guc_fw, "GuC", path);
+}
+
+/**
+ * intel_guc_ucode_load() - load GuC uCode into the device
+ *
+ * Called from gem_init_hw() during driver loading and also after a GPU reset.
+ * Checks that the firmware fetching process has succeeded, and if so transfers
+ * the loaded image to the hardware.
+ *
+ * However, there are a few checks to do first. The very first call should have
+ * (wait == FALSE), but the fetch_state will still be PENDING as the firmware may
+ * not be available that early. Therefore, on this first call, we just return.
+ *
+ * The second call should come from the first open of the device (wait == TRUE).
+ * This is a good time to load the firmware into the device, as by this point it
+ * must be available.
+ *
+ * Any subsequent calls are expected to have wait == FALSE, and indicate that the
+ * hardware has been reset and so the firmware should be reloaded.
+ */
+int intel_guc_ucode_load(struct drm_device *dev, bool wait)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_uc_fw *guc_fw = &dev_priv->guc.guc_fw;
+ int err;
+
+ DRM_DEBUG_DRIVER("wait %d, fetch %s, load %s\n",
+ wait,
+ intel_uc_fw_status_repr(guc_fw->uc_fw_fetch_status),
+ intel_uc_fw_status_repr(guc_fw->uc_fw_load_status));
+
+ if (guc_fw->uc_fw_fetch_status == INTEL_UC_FIRMWARE_PENDING && !wait)
+ return -EAGAIN;
+
+ if (guc_fw->uc_fw_fetch_status == INTEL_UC_FIRMWARE_NONE)
+ return 0;
+
+ if (guc_fw->uc_fw_fetch_status == INTEL_UC_FIRMWARE_SUCCESS &&
+ guc_fw->uc_fw_load_status == INTEL_UC_FIRMWARE_FAIL)
+ return -ENOEXEC;
+
+ guc_fw->uc_fw_load_status = INTEL_UC_FIRMWARE_PENDING;
+ err = intel_uc_fw_check(guc_fw, guc_ucode_check);
+ if (err)
+ goto fail;
+
+ err = guc_ucode_xfer(dev_priv);
+ if (err)
+ goto fail;
+
+ guc_fw->uc_fw_load_status = INTEL_UC_FIRMWARE_SUCCESS;
+
+ DRM_DEBUG_DRIVER("wait %d, fetch %s, load %s\n",
+ wait,
+ intel_uc_fw_status_repr(guc_fw->uc_fw_fetch_status),
+ intel_uc_fw_status_repr(guc_fw->uc_fw_load_status));
+
+ return 0;
+
+fail:
+ if (guc_fw->uc_fw_load_status == INTEL_UC_FIRMWARE_PENDING)
+ guc_fw->uc_fw_load_status = INTEL_UC_FIRMWARE_FAIL;
+
+ DRM_ERROR("Failed to initialize GuC, error %d\n", err);
+
+ return err;
+}
+
+/**
+ * intel_guc_ucode_fini() - clean up all allocated resources
+ */
+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(guc_fw);
+}