[PATCH v3 13/15] drm/i915: Enable GuC firmware log

yu.dai
2015-04-17T21:23:27Z

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

Allocate a gem obj to hold GuC log data. Also a debugfs interface
(i915_guc_log_dump) is provided to print out the log content.

Issue: VIZ-4884
Signed-off-by: Alex Dai <yu.dai@intel.com>
---
drivers/gpu/drm/i915/i915_debugfs.c | 29 +++++++++++++++
drivers/gpu/drm/i915/i915_drv.h | 1 +
drivers/gpu/drm/i915/i915_params.c | 5 +++
drivers/gpu/drm/i915/intel_guc.h | 1 +
drivers/gpu/drm/i915/intel_guc_loader.c | 64 ++++++++++++++++++++++++++++++++-
5 files changed, 99 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c
index f12bbee..0ce1e23 100644
--- a/drivers/gpu/drm/i915/i915_debugfs.c
+++ b/drivers/gpu/drm/i915/i915_debugfs.c
@@ -2387,6 +2387,34 @@ static int i915_guc_info(struct seq_file *m, void *data)
return 0;
}

+static int i915_guc_log_dump(struct seq_file *m, void *data)
+{
+ struct drm_info_node *node = m->private;
+ struct drm_device *dev = node->minor->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct drm_i915_gem_object *log_obj = dev_priv->guc.log_obj;
+ u32 *log;
+ int i = 0, pg;
+
+ if (!log_obj)
+ return 0;
+
+ for (pg = 0; pg < log_obj->base.size / PAGE_SIZE; pg++) {
+ log = kmap_atomic(i915_gem_object_get_page(log_obj, pg));
+
+ for (i = 0; i < PAGE_SIZE / sizeof(u32); i += 4)
+ seq_printf(m, "0x%08x 0x%08x 0x%08x 0x%08x\n",
+ *(log + i), *(log + i + 1),
+ *(log + i + 2), *(log + i + 3));
+
+ kunmap_atomic(log);
+ }
+
+ seq_putc(m, '\n');
+
+ return 0;
+}
+
static int i915_edp_psr_status(struct seq_file *m, void *data)
{
struct drm_info_node *node = m->private;
@@ -4855,6 +4883,7 @@ static const struct drm_info_list i915_debugfs_list[] = {
{"i915_gem_batch_pool", i915_gem_batch_pool_info, 0},
{"i915_guc_info", i915_guc_info, 0},
{"i915_guc_load_status", i915_guc_load_status_info, 0},
+ {"i915_guc_log_dump", i915_guc_log_dump, 0},
{"i915_frequency_info", i915_frequency_info, 0},
{"i915_hangcheck_info", i915_hangcheck_info, 0},
{"i915_drpc_info", i915_drpc_info, 0},
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 6760b9a..24b94da 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -2504,6 +2504,7 @@ struct i915_params {
bool disable_display;
bool disable_vtd_wa;
bool enable_guc_scheduling;
+ unsigned int guc_log_level;
int use_mmio_flip;
int mmio_debug;
bool verbose_state_checks;
diff --git a/drivers/gpu/drm/i915/i915_params.c b/drivers/gpu/drm/i915/i915_params.c
index 9ad2e27..95e4eb7 100644
--- a/drivers/gpu/drm/i915/i915_params.c
+++ b/drivers/gpu/drm/i915/i915_params.c
@@ -54,6 +54,7 @@ struct i915_params i915 __read_mostly = {
.verbose_state_checks = 1,
.nuclear_pageflip = 0,
.enable_guc_scheduling = false,
+ .guc_log_level = 0,
};

module_param_named(modeset, i915.modeset, int, 0400);
@@ -188,3 +189,7 @@ MODULE_PARM_DESC(nuclear_pageflip,

module_param_named(enable_guc_scheduling, i915.enable_guc_scheduling, bool, 0400);
MODULE_PARM_DESC(enable_guc_scheduling, "Enable GuC scheduling (default:false)");
+
+module_param_named(guc_log_level, i915.guc_log_level, int, 0400);
+MODULE_PARM_DESC(guc_log_level,
+ "GuC firmware logging level (0:disabled, 1~4:enabled)");
diff --git a/drivers/gpu/drm/i915/intel_guc.h b/drivers/gpu/drm/i915/intel_guc.h
index 641fd14..54572f7 100644
--- a/drivers/gpu/drm/i915/intel_guc.h
+++ b/drivers/gpu/drm/i915/intel_guc.h
@@ -62,6 +62,7 @@ struct intel_guc {
spinlock_t host2guc_lock;

struct drm_i915_gem_object *ctx_pool_obj;
+ struct drm_i915_gem_object *log_obj;

struct i915_guc_client *execbuf_client;

diff --git a/drivers/gpu/drm/i915/intel_guc_loader.c b/drivers/gpu/drm/i915/intel_guc_loader.c
index c784d64..d1fd11e 100644
--- a/drivers/gpu/drm/i915/intel_guc_loader.c
+++ b/drivers/gpu/drm/i915/intel_guc_loader.c
@@ -46,6 +46,12 @@
* 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_ver1.bin"
@@ -53,6 +59,10 @@ MODULE_FIRMWARE(I915_SKL_GUC_UCODE);
#define I915_BXT_GUC_UCODE "i915/bxt_guc_ver1.bin"
MODULE_FIRMWARE(I915_BXT_GUC_UCODE);

+#define GUC_LOG_DPC_PAGES 3
+#define GUC_LOG_ISR_PAGES 3
+#define GUC_LOG_CRASH_PAGES 1
+
/**
* intel_guc_allocate_gem_obj() - Allocate gem object for GuC usage
* @dev: drm device
@@ -211,6 +221,51 @@ static u32 get_core_family(struct drm_device *dev)
}
}

+static void create_guc_log(struct intel_guc *guc, u32 *params)
+{
+ struct drm_i915_private *dev_priv =
+ container_of(guc, struct drm_i915_private, guc);
+ struct drm_i915_gem_object *obj;
+ u32 flags, size;
+
+ /* The first page is to save log buffer state. Allocate one
+ * extra page for others in case for overlap */
+ size = (1 + GUC_LOG_DPC_PAGES + 1 +
+ GUC_LOG_ISR_PAGES + 1 +
+ GUC_LOG_CRASH_PAGES + 1) << PAGE_SHIFT;
+
+ if (!guc->log_obj) {
+ obj = intel_guc_allocate_gem_obj(dev_priv->dev, size);
+ if (!obj) {
+ /* logging will be off */
+ *(params + GUC_CTL_LOG_PARAMS) = 0;
+ i915.guc_log_level = 0;
+ return;
+ }
+
+ guc->log_obj = obj;
+ }
+ else
+ obj = guc->log_obj;
+
+ /* each allocated unit is a page */
+ flags = GUC_LOG_VALID | GUC_LOG_NOTIFY_ON_HALF_FULL |
+ (GUC_LOG_DPC_PAGES << GUC_LOG_DPC_SHIFT) |
+ (GUC_LOG_ISR_PAGES << GUC_LOG_ISR_SHIFT) |
+ (GUC_LOG_CRASH_PAGES << GUC_LOG_CRASH_SHIFT);
+
+ size = i915_gem_obj_ggtt_offset(obj) >> PAGE_SHIFT; /* in pages */
+ flags |= size << GUC_LOG_BUF_ADDR_SHIFT;
+
+ *(params + GUC_CTL_LOG_PARAMS) = flags;
+
+ i915.guc_log_level--;
+ if (i915.guc_log_level > GUC_LOG_VERBOSITY_ULTRA)
+ i915.guc_log_level = GUC_LOG_VERBOSITY_ULTRA;
+
+ *(params + GUC_CTL_DEBUG) |= i915.guc_log_level;
+}
+
static void set_guc_init_params(struct drm_i915_private *dev_priv)
{
u32 params[GUC_CTL_MAX_DWORDS];
@@ -234,7 +289,9 @@ static void set_guc_init_params(struct drm_i915_private *dev_priv)
params[GUC_CTL_FEATURE] |= GUC_CTL_DISABLE_SCHEDULER |
GUC_CTL_VCS2_ENABLED;

- /* XXX: Set up log buffer */
+ /* Set up log buffer */
+ if (i915.guc_log_level > 0)
+ create_guc_log(&dev_priv->guc, params);

/* If GuC scheduling is enabled, setup params here. */
if (i915.enable_guc_scheduling) {
@@ -479,6 +536,11 @@ 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;

+ if (dev_priv->guc.log_obj) {
+ intel_guc_release_gem_obj(dev_priv->guc.log_obj);
+ dev_priv->guc.log_obj = NULL;
+ }
+
guc_scheduler_fini(dev);

intel_uc_fw_fini(dev, guc_fw);
--
1.9.1