From bea4e4a4f831df1c104be60b3caa7205ba1bb4f9 Mon Sep 17 00:00:00 2001 From: Michal Wajdeczko Date: Fri, 7 Apr 2017 16:01:45 +0000 Subject: drm/i915/guc: Use wait_for_register_fw() while waiting for MMIO response Waiting for the response status in scratch register can be done using our generic function. Let's use it. v2: rebased Signed-off-by: Michal Wajdeczko Suggested-by: Tvrtko Ursulin Cc: Tvrtko Ursulin Cc: Joonas Lahtinen Cc: Chris Wilson Link: http://patchwork.freedesktop.org/patch/msgid/20170407160145.181328-2-michal.wajdeczko@intel.com Reviewed-by: Chris Wilson Signed-off-by: Chris Wilson --- drivers/gpu/drm/i915/intel_uc.c | 26 +++++++------------------- 1 file changed, 7 insertions(+), 19 deletions(-) (limited to 'drivers/gpu/drm/i915/intel_uc.c') diff --git a/drivers/gpu/drm/i915/intel_uc.c b/drivers/gpu/drm/i915/intel_uc.c index c117424f1f50..4364b1a9064e 100644 --- a/drivers/gpu/drm/i915/intel_uc.c +++ b/drivers/gpu/drm/i915/intel_uc.c @@ -359,19 +359,6 @@ void intel_uc_fini_hw(struct drm_i915_private *dev_priv) i915_ggtt_disable_guc(dev_priv); } -/* - * Read GuC command/status register (SOFT_SCRATCH_0) - * Return true if it contains a response rather than a command - */ -static bool guc_recv(struct intel_guc *guc, u32 *status) -{ - struct drm_i915_private *dev_priv = guc_to_i915(guc); - - u32 val = I915_READ(SOFT_SCRATCH(0)); - *status = val; - return INTEL_GUC_RECV_IS_RESPONSE(val); -} - /* * This function implements the MMIO based host to GuC interface. */ @@ -399,13 +386,14 @@ int intel_guc_send_mmio(struct intel_guc *guc, const u32 *action, u32 len) I915_WRITE(GUC_SEND_INTERRUPT, GUC_SEND_TRIGGER); /* - * Fast commands should complete in less than 10us, so sample quickly - * up to that length of time, then switch to a slower sleep-wait loop. - * No inte_guc_send command should ever take longer than 10ms. + * No GuC command should ever take longer than 10ms. + * Fast commands should still complete in 10us. */ - ret = wait_for_us(guc_recv(guc, &status), 10); - if (ret) - ret = wait_for(guc_recv(guc, &status), 10); + ret = __intel_wait_for_register_fw(dev_priv, + SOFT_SCRATCH(0), + INTEL_GUC_RECV_MASK, + INTEL_GUC_RECV_MASK, + 10, 10, &status); if (status != INTEL_GUC_STATUS_SUCCESS) { /* * Either the GuC explicitly returned an error (which -- cgit v1.2.3 From 13f6c719eb88f2a0c8981d7a0c45404194aa2ef5 Mon Sep 17 00:00:00 2001 From: "daniele.ceraolospurio@intel.com" Date: Thu, 6 Apr 2017 17:18:52 -0700 Subject: drm/i915/guc: write wopcm related register once during uc init The wopcm registers are write-once, so any write after the first one will just be ignored. The registers survive a GPU reset but not always a suspend/resume cycle, so to keep things simple keep the writes in the intel_uc_init_hw function instead of moving it earlier to make sure we attempt them every time we try to load GuC. Cc: Jeff McGee Cc: Anusha Srivatsa Signed-off-by: Daniele Ceraolo Spurio Reviewed-by: Joonas Lahtinen Signed-off-by: Joonas Lahtinen Link: http://patchwork.freedesktop.org/patch/msgid/1491524332-23860-1-git-send-email-daniele.ceraolospurio@intel.com --- drivers/gpu/drm/i915/intel_guc_loader.c | 4 ---- drivers/gpu/drm/i915/intel_huc.c | 5 ----- drivers/gpu/drm/i915/intel_uc.c | 5 +++++ 3 files changed, 5 insertions(+), 9 deletions(-) (limited to 'drivers/gpu/drm/i915/intel_uc.c') diff --git a/drivers/gpu/drm/i915/intel_guc_loader.c b/drivers/gpu/drm/i915/intel_guc_loader.c index 12f80ece1d05..d9045b6e897b 100644 --- a/drivers/gpu/drm/i915/intel_guc_loader.c +++ b/drivers/gpu/drm/i915/intel_guc_loader.c @@ -285,10 +285,6 @@ static int guc_ucode_xfer(struct drm_i915_private *dev_priv) intel_uncore_forcewake_get(dev_priv, FORCEWAKE_ALL); - /* init WOPCM */ - I915_WRITE(GUC_WOPCM_SIZE, intel_guc_wopcm_size(dev_priv)); - I915_WRITE(DMA_GUC_WOPCM_OFFSET, GUC_WOPCM_OFFSET_VALUE); - /* Enable MIA caching. GuC clock gating is disabled. */ I915_WRITE(GUC_SHIM_CONTROL, GUC_SHIM_CONTROL_VALUE); diff --git a/drivers/gpu/drm/i915/intel_huc.c b/drivers/gpu/drm/i915/intel_huc.c index 7a0bf1593463..88b4cf3f764a 100644 --- a/drivers/gpu/drm/i915/intel_huc.c +++ b/drivers/gpu/drm/i915/intel_huc.c @@ -106,11 +106,6 @@ static int huc_ucode_xfer(struct drm_i915_private *dev_priv) intel_uncore_forcewake_get(dev_priv, FORCEWAKE_ALL); - /* init WOPCM */ - I915_WRITE(GUC_WOPCM_SIZE, intel_guc_wopcm_size(dev_priv)); - I915_WRITE(DMA_GUC_WOPCM_OFFSET, GUC_WOPCM_OFFSET_VALUE | - HUC_LOADING_AGENT_GUC); - /* Set the source address for the uCode */ offset = guc_ggtt_offset(vma) + huc_fw->header_offset; I915_WRITE(DMA_ADDR_0_LOW, lower_32_bits(offset)); diff --git a/drivers/gpu/drm/i915/intel_uc.c b/drivers/gpu/drm/i915/intel_uc.c index 4364b1a9064e..900e3767a899 100644 --- a/drivers/gpu/drm/i915/intel_uc.c +++ b/drivers/gpu/drm/i915/intel_uc.c @@ -274,6 +274,11 @@ int intel_uc_init_hw(struct drm_i915_private *dev_priv) goto err_guc; } + /* init WOPCM */ + I915_WRITE(GUC_WOPCM_SIZE, intel_guc_wopcm_size(dev_priv)); + I915_WRITE(DMA_GUC_WOPCM_OFFSET, + GUC_WOPCM_OFFSET_VALUE | HUC_LOADING_AGENT_GUC); + /* WaEnableuKernelHeaderValidFix:skl */ /* WaEnableGuCBootHashCheckNotSet:skl,bxt,kbl */ if (IS_GEN9(dev_priv)) -- cgit v1.2.3 From 789a625158b0c0c2bc94a5dc404e7608d6100e5e Mon Sep 17 00:00:00 2001 From: Michal Wajdeczko Date: Tue, 2 May 2017 10:32:42 +0000 Subject: drm/i915/guc: Enable send function only after successful init It is safer to setup valid send function after successful GuC hardware initialization. In addition we prepare placeholder where we can setup any alternate GuC communication mechanism. Signed-off-by: Michal Wajdeczko Cc: Joonas Lahtinen Cc: Daniele Ceraolo Spurio Reviewed-by: Daniele Ceraolo Spurio Link: http://patchwork.freedesktop.org/patch/msgid/20170502103243.54940-1-michal.wajdeczko@intel.com [ickle: Fixup ENODEV for an impossible error path] Signed-off-by: Chris Wilson --- drivers/gpu/drm/i915/intel_uc.c | 27 ++++++++++++++++++++++++++- drivers/gpu/drm/i915/intel_uc.h | 1 + 2 files changed, 27 insertions(+), 1 deletion(-) (limited to 'drivers/gpu/drm/i915/intel_uc.c') diff --git a/drivers/gpu/drm/i915/intel_uc.c b/drivers/gpu/drm/i915/intel_uc.c index 900e3767a899..7fd75ca031c3 100644 --- a/drivers/gpu/drm/i915/intel_uc.c +++ b/drivers/gpu/drm/i915/intel_uc.c @@ -99,7 +99,7 @@ void intel_uc_init_early(struct drm_i915_private *dev_priv) struct intel_guc *guc = &dev_priv->guc; mutex_init(&guc->send_mutex); - guc->send = intel_guc_send_mmio; + guc->send = intel_guc_send_nop; } static void fetch_uc_fw(struct drm_i915_private *dev_priv, @@ -252,13 +252,27 @@ void intel_uc_fini_fw(struct drm_i915_private *dev_priv) __intel_uc_fw_fini(&dev_priv->huc.fw); } +static int guc_enable_communication(struct intel_guc *guc) +{ + /* XXX: placeholder for alternate setup */ + guc->send = intel_guc_send_mmio; + return 0; +} + +static void guc_disable_communication(struct intel_guc *guc) +{ + guc->send = intel_guc_send_nop; +} + int intel_uc_init_hw(struct drm_i915_private *dev_priv) { + struct intel_guc *guc = &dev_priv->guc; int ret, attempts; if (!i915.enable_guc_loading) return 0; + guc_disable_communication(guc); gen9_reset_guc_interrupts(dev_priv); /* We need to notify the guc whenever we change the GGTT */ @@ -308,6 +322,10 @@ int intel_uc_init_hw(struct drm_i915_private *dev_priv) if (ret) goto err_submission; + ret = guc_enable_communication(guc); + if (ret) + goto err_submission; + intel_guc_auth_huc(dev_priv); if (i915.enable_guc_submission) { if (i915.guc_log_level >= 0) @@ -330,6 +348,7 @@ int intel_uc_init_hw(struct drm_i915_private *dev_priv) * marks the GPU as wedged until reset). */ err_interrupts: + guc_disable_communication(guc); gen9_disable_guc_interrupts(dev_priv); err_submission: if (i915.enable_guc_submission) @@ -364,6 +383,12 @@ void intel_uc_fini_hw(struct drm_i915_private *dev_priv) i915_ggtt_disable_guc(dev_priv); } +int intel_guc_send_nop(struct intel_guc *guc, const u32 *action, u32 len) +{ + WARN(1, "Unexpected send: action=%#x\n", *action); + return -ENODEV; +} + /* * This function implements the MMIO based host to GuC interface. */ diff --git a/drivers/gpu/drm/i915/intel_uc.h b/drivers/gpu/drm/i915/intel_uc.h index 2f0229da20bb..1e0eecdcedc0 100644 --- a/drivers/gpu/drm/i915/intel_uc.h +++ b/drivers/gpu/drm/i915/intel_uc.h @@ -227,6 +227,7 @@ void intel_uc_fini_fw(struct drm_i915_private *dev_priv); int intel_uc_init_hw(struct drm_i915_private *dev_priv); void intel_uc_fini_hw(struct drm_i915_private *dev_priv); int intel_guc_sample_forcewake(struct intel_guc *guc); +int intel_guc_send_nop(struct intel_guc *guc, const u32 *action, u32 len); int intel_guc_send_mmio(struct intel_guc *guc, const u32 *action, u32 len); static inline int intel_guc_send(struct intel_guc *guc, const u32 *action, u32 len) { -- cgit v1.2.3 From a03aac442d6f4ca4525e526047d831efb77b436a Mon Sep 17 00:00:00 2001 From: Michal Wajdeczko Date: Wed, 10 May 2017 12:59:26 +0000 Subject: drm/i915/guc: Move notification code into virtual function Prepare for alternate GuC notification mechanism. Signed-off-by: Michal Wajdeczko Cc: Joonas Lahtinen Cc: Daniele Ceraolo Spurio Reviewed-by: Daniele Ceraolo Spurio [Joonas: Added newlines] Signed-off-by: Joonas Lahtinen --- drivers/gpu/drm/i915/intel_uc.c | 10 +++++++++- drivers/gpu/drm/i915/intel_uc.h | 9 +++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) (limited to 'drivers/gpu/drm/i915/intel_uc.c') diff --git a/drivers/gpu/drm/i915/intel_uc.c b/drivers/gpu/drm/i915/intel_uc.c index 7fd75ca031c3..72f49e679707 100644 --- a/drivers/gpu/drm/i915/intel_uc.c +++ b/drivers/gpu/drm/i915/intel_uc.c @@ -94,12 +94,20 @@ void intel_uc_sanitize_options(struct drm_i915_private *dev_priv) i915.enable_guc_submission = HAS_GUC_SCHED(dev_priv); } +static void guc_write_irq_trigger(struct intel_guc *guc) +{ + struct drm_i915_private *dev_priv = guc_to_i915(guc); + + I915_WRITE(GUC_SEND_INTERRUPT, GUC_SEND_TRIGGER); +} + void intel_uc_init_early(struct drm_i915_private *dev_priv) { struct intel_guc *guc = &dev_priv->guc; mutex_init(&guc->send_mutex); guc->send = intel_guc_send_nop; + guc->notify = guc_write_irq_trigger; } static void fetch_uc_fw(struct drm_i915_private *dev_priv, @@ -413,7 +421,7 @@ int intel_guc_send_mmio(struct intel_guc *guc, const u32 *action, u32 len) POSTING_READ(SOFT_SCRATCH(i - 1)); - I915_WRITE(GUC_SEND_INTERRUPT, GUC_SEND_TRIGGER); + intel_guc_notify(guc); /* * No GuC command should ever take longer than 10ms. diff --git a/drivers/gpu/drm/i915/intel_uc.h b/drivers/gpu/drm/i915/intel_uc.h index 1e0eecdcedc0..2af5633b8a09 100644 --- a/drivers/gpu/drm/i915/intel_uc.h +++ b/drivers/gpu/drm/i915/intel_uc.h @@ -210,6 +210,9 @@ struct intel_guc { /* GuC's FW specific send function */ int (*send)(struct intel_guc *guc, const u32 *data, u32 len); + + /* GuC's FW specific notify function */ + void (*notify)(struct intel_guc *guc); }; struct intel_huc { @@ -229,11 +232,17 @@ void intel_uc_fini_hw(struct drm_i915_private *dev_priv); int intel_guc_sample_forcewake(struct intel_guc *guc); int intel_guc_send_nop(struct intel_guc *guc, const u32 *action, u32 len); int intel_guc_send_mmio(struct intel_guc *guc, const u32 *action, u32 len); + static inline int intel_guc_send(struct intel_guc *guc, const u32 *action, u32 len) { return guc->send(guc, action, len); } +static inline void intel_guc_notify(struct intel_guc *guc) +{ + guc->notify(guc); +} + /* intel_guc_loader.c */ int intel_guc_select_fw(struct intel_guc *guc); int intel_guc_init_hw(struct intel_guc *guc); -- cgit v1.2.3 From a0c1fe219080d6b23270d5c7f7d773e7d753177a Mon Sep 17 00:00:00 2001 From: Michal Wajdeczko Date: Wed, 10 May 2017 12:59:27 +0000 Subject: drm/i915/guc: Make scratch register base and count flexible We are using some scratch registers in MMIO based send function. Make their base and count flexible in preparation of upcoming GuC firmware/hardware changes. While around, change cmd len parameter verification from WARN_ON to GEM_BUG_ON as we don't need this all the time. v2: call out WARN/GEM_BUG change in the commit msg (Daniele) v3: don't overqualify the ints (Chris) v4: rebase and use proper enum Signed-off-by: Michal Wajdeczko Suggested-by: Daniele Ceraolo Spurio Cc: Daniele Ceraolo Spurio Cc: Joonas Lahtinen Cc: Chris Wilson Cc: Jani Nikula Reviewed-by: Daniele Ceraolo Spurio Reviewed-by: Joonas Lahtinen Acked-by: Jani Nikula Signed-off-by: Joonas Lahtinen --- drivers/gpu/drm/i915/intel_uc.c | 41 ++++++++++++++++++++++++++++++++++------- drivers/gpu/drm/i915/intel_uc.h | 7 +++++++ 2 files changed, 41 insertions(+), 7 deletions(-) (limited to 'drivers/gpu/drm/i915/intel_uc.c') diff --git a/drivers/gpu/drm/i915/intel_uc.c b/drivers/gpu/drm/i915/intel_uc.c index 72f49e679707..07c5658c4b35 100644 --- a/drivers/gpu/drm/i915/intel_uc.c +++ b/drivers/gpu/drm/i915/intel_uc.c @@ -260,9 +260,36 @@ void intel_uc_fini_fw(struct drm_i915_private *dev_priv) __intel_uc_fw_fini(&dev_priv->huc.fw); } +static inline i915_reg_t guc_send_reg(struct intel_guc *guc, u32 i) +{ + GEM_BUG_ON(!guc->send_regs.base); + GEM_BUG_ON(!guc->send_regs.count); + GEM_BUG_ON(i >= guc->send_regs.count); + + return _MMIO(guc->send_regs.base + 4 * i); +} + +static void guc_init_send_regs(struct intel_guc *guc) +{ + struct drm_i915_private *dev_priv = guc_to_i915(guc); + enum forcewake_domains fw_domains = 0; + unsigned int i; + + guc->send_regs.base = i915_mmio_reg_offset(SOFT_SCRATCH(0)); + guc->send_regs.count = SOFT_SCRATCH_COUNT - 1; + + for (i = 0; i < guc->send_regs.count; i++) { + fw_domains |= intel_uncore_forcewake_for_reg(dev_priv, + guc_send_reg(guc, i), + FW_REG_READ | FW_REG_WRITE); + } + guc->send_regs.fw_domains = fw_domains; +} + static int guc_enable_communication(struct intel_guc *guc) { /* XXX: placeholder for alternate setup */ + guc_init_send_regs(guc); guc->send = intel_guc_send_mmio; return 0; } @@ -407,19 +434,19 @@ int intel_guc_send_mmio(struct intel_guc *guc, const u32 *action, u32 len) int i; int ret; - if (WARN_ON(len < 1 || len > 15)) - return -EINVAL; + GEM_BUG_ON(!len); + GEM_BUG_ON(len > guc->send_regs.count); mutex_lock(&guc->send_mutex); - intel_uncore_forcewake_get(dev_priv, FORCEWAKE_BLITTER); + intel_uncore_forcewake_get(dev_priv, guc->send_regs.fw_domains); dev_priv->guc.action_count += 1; dev_priv->guc.action_cmd = action[0]; for (i = 0; i < len; i++) - I915_WRITE(SOFT_SCRATCH(i), action[i]); + I915_WRITE(guc_send_reg(guc, i), action[i]); - POSTING_READ(SOFT_SCRATCH(i - 1)); + POSTING_READ(guc_send_reg(guc, i - 1)); intel_guc_notify(guc); @@ -428,7 +455,7 @@ int intel_guc_send_mmio(struct intel_guc *guc, const u32 *action, u32 len) * Fast commands should still complete in 10us. */ ret = __intel_wait_for_register_fw(dev_priv, - SOFT_SCRATCH(0), + guc_send_reg(guc, 0), INTEL_GUC_RECV_MASK, INTEL_GUC_RECV_MASK, 10, 10, &status); @@ -450,7 +477,7 @@ int intel_guc_send_mmio(struct intel_guc *guc, const u32 *action, u32 len) } dev_priv->guc.action_status = status; - intel_uncore_forcewake_put(dev_priv, FORCEWAKE_BLITTER); + intel_uncore_forcewake_put(dev_priv, guc->send_regs.fw_domains); mutex_unlock(&guc->send_mutex); return ret; diff --git a/drivers/gpu/drm/i915/intel_uc.h b/drivers/gpu/drm/i915/intel_uc.h index 2af5633b8a09..7618b7100175 100644 --- a/drivers/gpu/drm/i915/intel_uc.h +++ b/drivers/gpu/drm/i915/intel_uc.h @@ -205,6 +205,13 @@ struct intel_guc { uint64_t submissions[I915_NUM_ENGINES]; uint32_t last_seqno[I915_NUM_ENGINES]; + /* GuC's FW specific registers used in MMIO send */ + struct { + u32 base; + unsigned int count; + enum forcewake_domains fw_domains; + } send_regs; + /* To serialize the intel_guc_send actions */ struct mutex send_mutex; -- cgit v1.2.3 From 00bd16f257a4cd94305bb6e1c73f7c93c196ffc7 Mon Sep 17 00:00:00 2001 From: Michal Wajdeczko Date: Mon, 15 May 2017 17:06:09 +0000 Subject: drm/i915/guc: Remove action status and statistics from debugfs Usefulness of these stats was over-advertised. v2: remove duplicated engine stats (Chris) Suggested-by: Chris Wilson Signed-off-by: Michal Wajdeczko Cc: Chris Wilson Cc: Daniele Ceraolo Spurio Link: http://patchwork.freedesktop.org/patch/msgid/20170515170610.35528-1-michal.wajdeczko@intel.com Acked-by: Tvrtko Ursulin Reviewed-by: Chris Wilson Signed-off-by: Chris Wilson --- drivers/gpu/drm/i915/i915_debugfs.c | 19 ------------------- drivers/gpu/drm/i915/i915_guc_submission.c | 3 --- drivers/gpu/drm/i915/intel_uc.c | 7 ------- drivers/gpu/drm/i915/intel_uc.h | 10 ---------- 4 files changed, 39 deletions(-) (limited to 'drivers/gpu/drm/i915/intel_uc.c') diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 6bdc903a090b..c51c56af0013 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -2512,9 +2512,6 @@ static int i915_guc_info(struct seq_file *m, void *data) { struct drm_i915_private *dev_priv = node_to_i915(m->private); const struct intel_guc *guc = &dev_priv->guc; - struct intel_engine_cs *engine; - enum intel_engine_id id; - u64 total; if (!check_guc_submission(m)) return 0; @@ -2523,22 +2520,6 @@ static int i915_guc_info(struct seq_file *m, void *data) seq_printf(m, "\t%*pb\n", GUC_NUM_DOORBELLS, guc->doorbell_bitmap); seq_printf(m, "Doorbell next cacheline: 0x%x\n\n", guc->db_cacheline); - seq_printf(m, "GuC total action count: %llu\n", guc->action_count); - seq_printf(m, "GuC action failure count: %u\n", guc->action_fail); - seq_printf(m, "GuC last action command: 0x%x\n", guc->action_cmd); - seq_printf(m, "GuC last action status: 0x%x\n", guc->action_status); - seq_printf(m, "GuC last action error code: %d\n", guc->action_err); - - total = 0; - seq_printf(m, "\nGuC submissions:\n"); - for_each_engine(engine, dev_priv, id) { - u64 submissions = guc->submissions[id]; - total += submissions; - seq_printf(m, "\t%-24s: %10llu, last seqno 0x%08x\n", - engine->name, submissions, guc->last_seqno[id]); - } - seq_printf(m, "\t%s: %llu\n", "Total", total); - seq_printf(m, "\nGuC execbuf client @ %p:\n", guc->execbuf_client); i915_guc_client_info(m, dev_priv, guc->execbuf_client); diff --git a/drivers/gpu/drm/i915/i915_guc_submission.c b/drivers/gpu/drm/i915/i915_guc_submission.c index ee32fa97a18b..777f48e9bb33 100644 --- a/drivers/gpu/drm/i915/i915_guc_submission.c +++ b/drivers/gpu/drm/i915/i915_guc_submission.c @@ -615,9 +615,6 @@ static void __i915_guc_submit(struct drm_i915_gem_request *rq) client->submissions[engine_id] += 1; - guc->submissions[engine_id] += 1; - guc->last_seqno[engine_id] = rq->global_seqno; - spin_unlock_irqrestore(&client->wq_lock, flags); } diff --git a/drivers/gpu/drm/i915/intel_uc.c b/drivers/gpu/drm/i915/intel_uc.c index 07c5658c4b35..d27b5277901c 100644 --- a/drivers/gpu/drm/i915/intel_uc.c +++ b/drivers/gpu/drm/i915/intel_uc.c @@ -440,9 +440,6 @@ int intel_guc_send_mmio(struct intel_guc *guc, const u32 *action, u32 len) mutex_lock(&guc->send_mutex); intel_uncore_forcewake_get(dev_priv, guc->send_regs.fw_domains); - dev_priv->guc.action_count += 1; - dev_priv->guc.action_cmd = action[0]; - for (i = 0; i < len; i++) I915_WRITE(guc_send_reg(guc, i), action[i]); @@ -471,11 +468,7 @@ int intel_guc_send_mmio(struct intel_guc *guc, const u32 *action, u32 len) DRM_WARN("INTEL_GUC_SEND: Action 0x%X failed;" " ret=%d status=0x%08X response=0x%08X\n", action[0], ret, status, I915_READ(SOFT_SCRATCH(15))); - - dev_priv->guc.action_fail += 1; - dev_priv->guc.action_err = ret; } - dev_priv->guc.action_status = status; intel_uncore_forcewake_put(dev_priv, guc->send_regs.fw_domains); mutex_unlock(&guc->send_mutex); diff --git a/drivers/gpu/drm/i915/intel_uc.h b/drivers/gpu/drm/i915/intel_uc.h index 33c8f77f72ce..930f2e17b863 100644 --- a/drivers/gpu/drm/i915/intel_uc.h +++ b/drivers/gpu/drm/i915/intel_uc.h @@ -187,16 +187,6 @@ struct intel_guc { DECLARE_BITMAP(doorbell_bitmap, GUC_NUM_DOORBELLS); uint32_t db_cacheline; /* Cyclic counter mod pagesize */ - /* Action status & statistics */ - uint64_t action_count; /* Total commands issued */ - uint32_t action_cmd; /* Last command word */ - uint32_t action_status; /* Last return status */ - uint32_t action_fail; /* Total number of failures */ - int32_t action_err; /* Last error code */ - - uint64_t submissions[I915_NUM_ENGINES]; - uint32_t last_seqno[I915_NUM_ENGINES]; - /* GuC's FW specific registers used in MMIO send */ struct { u32 base; -- cgit v1.2.3 From 2f64085a75f623f5f5f32237131ea8de1c59be0e Mon Sep 17 00:00:00 2001 From: Michal Wajdeczko Date: Fri, 26 May 2017 11:13:24 +0000 Subject: drm/i915/guc: Disable send function on fini In earlier patch 789a625 we were enabling send function only after successful init. For completeness, we should make sure that we disable it on fini. v2: don't group steps by submission flag (Chris) Signed-off-by: Michal Wajdeczko Cc: Joonas Lahtinen Cc: Daniele Ceraolo Spurio Cc: Chris Wilson Reviewed-by: Chris Wilson Signed-off-by: Chris Wilson Link: http://patchwork.freedesktop.org/patch/msgid/20170526111326.87280-2-michal.wajdeczko@intel.com --- drivers/gpu/drm/i915/intel_uc.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers/gpu/drm/i915/intel_uc.c') diff --git a/drivers/gpu/drm/i915/intel_uc.c b/drivers/gpu/drm/i915/intel_uc.c index d27b5277901c..31dc8c3444cd 100644 --- a/drivers/gpu/drm/i915/intel_uc.c +++ b/drivers/gpu/drm/i915/intel_uc.c @@ -410,11 +410,16 @@ void intel_uc_fini_hw(struct drm_i915_private *dev_priv) if (!i915.enable_guc_loading) return; - if (i915.enable_guc_submission) { + if (i915.enable_guc_submission) i915_guc_submission_disable(dev_priv); + + guc_disable_communication(&dev_priv->guc); + + if (i915.enable_guc_submission) { gen9_disable_guc_interrupts(dev_priv); i915_guc_submission_fini(dev_priv); } + i915_ggtt_disable_guc(dev_priv); } -- cgit v1.2.3 From f8a58d639dd95b0188862b4c1c1cc81c737db612 Mon Sep 17 00:00:00 2001 From: Michal Wajdeczko Date: Fri, 26 May 2017 11:13:25 +0000 Subject: drm/i915/guc: Introduce buffer based cmd transport Buffer based command transport can replace MMIO based mechanism. It may be used to perform host-2-guc and guc-to-host communication. Portions of this patch are based on work by: Michel Thierry Robert Beckett Daniele Ceraolo Spurio v2: use gem_object_pin_map (Chris) don't use DEBUG_RATELIMITED (Chris) don't track action stats (Chris) simplify next fence (Chris) use READ_ONCE (Chris) move blob allocation to new function (Chris) v3: use static owner id (Daniele) v4: but keep channel initialization generic (Daniele) and introduce owner_sub_id (Daniele) Signed-off-by: Michal Wajdeczko Cc: Daniele Ceraolo Spurio Cc: Oscar Mateo Cc: Joonas Lahtinen Cc: Chris Wilson Reviewed-by: Daniele Ceraolo Spurio Signed-off-by: Chris Wilson Link: http://patchwork.freedesktop.org/patch/msgid/20170526111326.87280-3-michal.wajdeczko@intel.com --- drivers/gpu/drm/i915/Makefile | 1 + drivers/gpu/drm/i915/i915_drv.h | 2 + drivers/gpu/drm/i915/intel_guc_ct.c | 461 ++++++++++++++++++++++++++++++++++ drivers/gpu/drm/i915/intel_guc_ct.h | 86 +++++++ drivers/gpu/drm/i915/intel_guc_fwif.h | 43 ++++ drivers/gpu/drm/i915/intel_uc.c | 19 +- drivers/gpu/drm/i915/intel_uc.h | 3 +- 7 files changed, 613 insertions(+), 2 deletions(-) create mode 100644 drivers/gpu/drm/i915/intel_guc_ct.c create mode 100644 drivers/gpu/drm/i915/intel_guc_ct.h (limited to 'drivers/gpu/drm/i915/intel_uc.c') diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile index 7b05fb802f4c..16dccf550412 100644 --- a/drivers/gpu/drm/i915/Makefile +++ b/drivers/gpu/drm/i915/Makefile @@ -58,6 +58,7 @@ i915-y += i915_cmd_parser.o \ # general-purpose microcontroller (GuC) support i915-y += intel_uc.o \ + intel_guc_ct.o \ intel_guc_log.o \ intel_guc_loader.o \ intel_huc.o \ diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 9ba22427c05c..d2a57493ac2e 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -760,6 +760,7 @@ struct intel_csr { func(has_gmbus_irq); \ func(has_gmch_display); \ func(has_guc); \ + func(has_guc_ct); \ func(has_hotplug); \ func(has_l3_dpf); \ func(has_llc); \ @@ -2947,6 +2948,7 @@ intel_info(const struct drm_i915_private *dev_priv) * properties, so we have separate macros to test them. */ #define HAS_GUC(dev_priv) ((dev_priv)->info.has_guc) +#define HAS_GUC_CT(dev_priv) ((dev_priv)->info.has_guc_ct) #define HAS_GUC_UCODE(dev_priv) (HAS_GUC(dev_priv)) #define HAS_GUC_SCHED(dev_priv) (HAS_GUC(dev_priv)) #define HAS_HUC_UCODE(dev_priv) (HAS_GUC(dev_priv)) diff --git a/drivers/gpu/drm/i915/intel_guc_ct.c b/drivers/gpu/drm/i915/intel_guc_ct.c new file mode 100644 index 000000000000..c4cbec140101 --- /dev/null +++ b/drivers/gpu/drm/i915/intel_guc_ct.c @@ -0,0 +1,461 @@ +/* + * Copyright © 2016-2017 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 "i915_drv.h" +#include "intel_guc_ct.h" + +enum { CTB_SEND = 0, CTB_RECV = 1 }; + +enum { CTB_OWNER_HOST = 0 }; + +void intel_guc_ct_init_early(struct intel_guc_ct *ct) +{ + /* we're using static channel owners */ + ct->host_channel.owner = CTB_OWNER_HOST; +} + +static inline const char *guc_ct_buffer_type_to_str(u32 type) +{ + switch (type) { + case INTEL_GUC_CT_BUFFER_TYPE_SEND: + return "SEND"; + case INTEL_GUC_CT_BUFFER_TYPE_RECV: + return "RECV"; + default: + return ""; + } +} + +static void guc_ct_buffer_desc_init(struct guc_ct_buffer_desc *desc, + u32 cmds_addr, u32 size, u32 owner) +{ + DRM_DEBUG_DRIVER("CT: desc %p init addr=%#x size=%u owner=%u\n", + desc, cmds_addr, size, owner); + memset(desc, 0, sizeof(*desc)); + desc->addr = cmds_addr; + desc->size = size; + desc->owner = owner; +} + +static void guc_ct_buffer_desc_reset(struct guc_ct_buffer_desc *desc) +{ + DRM_DEBUG_DRIVER("CT: desc %p reset head=%u tail=%u\n", + desc, desc->head, desc->tail); + desc->head = 0; + desc->tail = 0; + desc->is_in_error = 0; +} + +static int guc_action_register_ct_buffer(struct intel_guc *guc, + u32 desc_addr, + u32 type) +{ + u32 action[] = { + INTEL_GUC_ACTION_REGISTER_COMMAND_TRANSPORT_BUFFER, + desc_addr, + sizeof(struct guc_ct_buffer_desc), + type + }; + int err; + + /* Can't use generic send(), CT registration must go over MMIO */ + err = intel_guc_send_mmio(guc, action, ARRAY_SIZE(action)); + if (err) + DRM_ERROR("CT: register %s buffer failed; err=%d\n", + guc_ct_buffer_type_to_str(type), err); + return err; +} + +static int guc_action_deregister_ct_buffer(struct intel_guc *guc, + u32 owner, + u32 type) +{ + u32 action[] = { + INTEL_GUC_ACTION_DEREGISTER_COMMAND_TRANSPORT_BUFFER, + owner, + type + }; + int err; + + /* Can't use generic send(), CT deregistration must go over MMIO */ + err = intel_guc_send_mmio(guc, action, ARRAY_SIZE(action)); + if (err) + DRM_ERROR("CT: deregister %s buffer failed; owner=%d err=%d\n", + guc_ct_buffer_type_to_str(type), owner, err); + return err; +} + +static bool ctch_is_open(struct intel_guc_ct_channel *ctch) +{ + return ctch->vma != NULL; +} + +static int ctch_init(struct intel_guc *guc, + struct intel_guc_ct_channel *ctch) +{ + struct i915_vma *vma; + void *blob; + int err; + int i; + + GEM_BUG_ON(ctch->vma); + + /* We allocate 1 page to hold both descriptors and both buffers. + * ___________..................... + * |desc (SEND)| : + * |___________| PAGE/4 + * :___________....................: + * |desc (RECV)| : + * |___________| PAGE/4 + * :_______________________________: + * |cmds (SEND) | + * | PAGE/4 + * |_______________________________| + * |cmds (RECV) | + * | PAGE/4 + * |_______________________________| + * + * Each message can use a maximum of 32 dwords and we don't expect to + * have more than 1 in flight at any time, so we have enough space. + * Some logic further ahead will rely on the fact that there is only 1 + * page and that it is always mapped, so if the size is changed the + * other code will need updating as well. + */ + + /* allocate vma */ + vma = intel_guc_allocate_vma(guc, PAGE_SIZE); + if (IS_ERR(vma)) { + err = PTR_ERR(vma); + goto err_out; + } + ctch->vma = vma; + + /* map first page */ + blob = i915_gem_object_pin_map(vma->obj, I915_MAP_WB); + if (IS_ERR(blob)) { + err = PTR_ERR(blob); + goto err_vma; + } + DRM_DEBUG_DRIVER("CT: vma base=%#x\n", guc_ggtt_offset(ctch->vma)); + + /* store pointers to desc and cmds */ + for (i = 0; i < ARRAY_SIZE(ctch->ctbs); i++) { + GEM_BUG_ON((i != CTB_SEND) && (i != CTB_RECV)); + ctch->ctbs[i].desc = blob + PAGE_SIZE/4 * i; + ctch->ctbs[i].cmds = blob + PAGE_SIZE/4 * i + PAGE_SIZE/2; + } + + return 0; + +err_vma: + i915_vma_unpin_and_release(&ctch->vma); +err_out: + DRM_DEBUG_DRIVER("CT: channel %d initialization failed; err=%d\n", + ctch->owner, err); + return err; +} + +static void ctch_fini(struct intel_guc *guc, + struct intel_guc_ct_channel *ctch) +{ + GEM_BUG_ON(!ctch->vma); + + i915_gem_object_unpin_map(ctch->vma->obj); + i915_vma_unpin_and_release(&ctch->vma); +} + +static int ctch_open(struct intel_guc *guc, + struct intel_guc_ct_channel *ctch) +{ + u32 base; + int err; + int i; + + DRM_DEBUG_DRIVER("CT: channel %d reopen=%s\n", + ctch->owner, yesno(ctch_is_open(ctch))); + + if (!ctch->vma) { + err = ctch_init(guc, ctch); + if (unlikely(err)) + goto err_out; + } + + /* vma should be already allocated and map'ed */ + base = guc_ggtt_offset(ctch->vma); + + /* (re)initialize descriptors + * cmds buffers are in the second half of the blob page + */ + for (i = 0; i < ARRAY_SIZE(ctch->ctbs); i++) { + GEM_BUG_ON((i != CTB_SEND) && (i != CTB_RECV)); + guc_ct_buffer_desc_init(ctch->ctbs[i].desc, + base + PAGE_SIZE/4 * i + PAGE_SIZE/2, + PAGE_SIZE/4, + ctch->owner); + } + + /* register buffers, starting wirh RECV buffer + * descriptors are in first half of the blob + */ + err = guc_action_register_ct_buffer(guc, + base + PAGE_SIZE/4 * CTB_RECV, + INTEL_GUC_CT_BUFFER_TYPE_RECV); + if (unlikely(err)) + goto err_fini; + + err = guc_action_register_ct_buffer(guc, + base + PAGE_SIZE/4 * CTB_SEND, + INTEL_GUC_CT_BUFFER_TYPE_SEND); + if (unlikely(err)) + goto err_deregister; + + return 0; + +err_deregister: + guc_action_deregister_ct_buffer(guc, + ctch->owner, + INTEL_GUC_CT_BUFFER_TYPE_RECV); +err_fini: + ctch_fini(guc, ctch); +err_out: + DRM_ERROR("CT: can't open channel %d; err=%d\n", ctch->owner, err); + return err; +} + +static void ctch_close(struct intel_guc *guc, + struct intel_guc_ct_channel *ctch) +{ + GEM_BUG_ON(!ctch_is_open(ctch)); + + guc_action_deregister_ct_buffer(guc, + ctch->owner, + INTEL_GUC_CT_BUFFER_TYPE_SEND); + guc_action_deregister_ct_buffer(guc, + ctch->owner, + INTEL_GUC_CT_BUFFER_TYPE_RECV); + ctch_fini(guc, ctch); +} + +static u32 ctch_get_next_fence(struct intel_guc_ct_channel *ctch) +{ + /* For now it's trivial */ + return ++ctch->next_fence; +} + +static int ctb_write(struct intel_guc_ct_buffer *ctb, + const u32 *action, + u32 len /* in dwords */, + u32 fence) +{ + struct guc_ct_buffer_desc *desc = ctb->desc; + u32 head = desc->head / 4; /* in dwords */ + u32 tail = desc->tail / 4; /* in dwords */ + u32 size = desc->size / 4; /* in dwords */ + u32 used; /* in dwords */ + u32 header; + u32 *cmds = ctb->cmds; + unsigned int i; + + GEM_BUG_ON(desc->size % 4); + GEM_BUG_ON(desc->head % 4); + GEM_BUG_ON(desc->tail % 4); + GEM_BUG_ON(tail >= size); + + /* + * tail == head condition indicates empty. GuC FW does not support + * using up the entire buffer to get tail == head meaning full. + */ + if (tail < head) + used = (size - head) + tail; + else + used = tail - head; + + /* make sure there is a space including extra dw for the fence */ + if (unlikely(used + len + 1 >= size)) + return -ENOSPC; + + /* Write the message. The format is the following: + * DW0: header (including action code) + * DW1: fence + * DW2+: action data + */ + header = (len << GUC_CT_MSG_LEN_SHIFT) | + (GUC_CT_MSG_WRITE_FENCE_TO_DESC) | + (action[0] << GUC_CT_MSG_ACTION_SHIFT); + + cmds[tail] = header; + tail = (tail + 1) % size; + + cmds[tail] = fence; + tail = (tail + 1) % size; + + for (i = 1; i < len; i++) { + cmds[tail] = action[i]; + tail = (tail + 1) % size; + } + + /* now update desc tail (back in bytes) */ + desc->tail = tail * 4; + GEM_BUG_ON(desc->tail > desc->size); + + return 0; +} + +/* Wait for the response from the GuC. + * @fence: response fence + * @status: placeholder for status + * return: 0 response received (status is valid) + * -ETIMEDOUT no response within hardcoded timeout + * -EPROTO no response, ct buffer was in error + */ +static int wait_for_response(struct guc_ct_buffer_desc *desc, + u32 fence, + u32 *status) +{ + int err; + + /* + * Fast commands should complete in less than 10us, so sample quickly + * up to that length of time, then switch to a slower sleep-wait loop. + * No GuC command should ever take longer than 10ms. + */ +#define done (READ_ONCE(desc->fence) == fence) + err = wait_for_us(done, 10); + if (err) + err = wait_for(done, 10); +#undef done + + if (unlikely(err)) { + DRM_ERROR("CT: fence %u failed; reported fence=%u\n", + fence, desc->fence); + + if (WARN_ON(desc->is_in_error)) { + /* Something went wrong with the messaging, try to reset + * the buffer and hope for the best + */ + guc_ct_buffer_desc_reset(desc); + err = -EPROTO; + } + } + + *status = desc->status; + return err; +} + +static int ctch_send(struct intel_guc *guc, + struct intel_guc_ct_channel *ctch, + const u32 *action, + u32 len, + u32 *status) +{ + struct intel_guc_ct_buffer *ctb = &ctch->ctbs[CTB_SEND]; + struct guc_ct_buffer_desc *desc = ctb->desc; + u32 fence; + int err; + + GEM_BUG_ON(!ctch_is_open(ctch)); + GEM_BUG_ON(!len); + GEM_BUG_ON(len & ~GUC_CT_MSG_LEN_MASK); + + fence = ctch_get_next_fence(ctch); + err = ctb_write(ctb, action, len, fence); + if (unlikely(err)) + return err; + + intel_guc_notify(guc); + + err = wait_for_response(desc, fence, status); + if (unlikely(err)) + return err; + if (*status != INTEL_GUC_STATUS_SUCCESS) + return -EIO; + return 0; +} + +/* + * Command Transport (CT) buffer based GuC send function. + */ +static int intel_guc_send_ct(struct intel_guc *guc, const u32 *action, u32 len) +{ + struct intel_guc_ct_channel *ctch = &guc->ct.host_channel; + u32 status = ~0; /* undefined */ + int err; + + mutex_lock(&guc->send_mutex); + + err = ctch_send(guc, ctch, action, len, &status); + if (unlikely(err)) { + DRM_ERROR("CT: send action %#X failed; err=%d status=%#X\n", + action[0], err, status); + } + + mutex_unlock(&guc->send_mutex); + return err; +} + +/** + * Enable buffer based command transport + * Shall only be called for platforms with HAS_GUC_CT. + * @guc: the guc + * return: 0 on success + * non-zero on failure + */ +int intel_guc_enable_ct(struct intel_guc *guc) +{ + struct drm_i915_private *dev_priv = guc_to_i915(guc); + struct intel_guc_ct_channel *ctch = &guc->ct.host_channel; + int err; + + GEM_BUG_ON(!HAS_GUC_CT(dev_priv)); + + err = ctch_open(guc, ctch); + if (unlikely(err)) + return err; + + /* Switch into cmd transport buffer based send() */ + guc->send = intel_guc_send_ct; + DRM_INFO("CT: %s\n", enableddisabled(true)); + return 0; +} + +/** + * Disable buffer based command transport. + * Shall only be called for platforms with HAS_GUC_CT. + * @guc: the guc + */ +void intel_guc_disable_ct(struct intel_guc *guc) +{ + struct drm_i915_private *dev_priv = guc_to_i915(guc); + struct intel_guc_ct_channel *ctch = &guc->ct.host_channel; + + GEM_BUG_ON(!HAS_GUC_CT(dev_priv)); + + if (!ctch_is_open(ctch)) + return; + + ctch_close(guc, ctch); + + /* Disable send */ + guc->send = intel_guc_send_nop; + DRM_INFO("CT: %s\n", enableddisabled(false)); +} diff --git a/drivers/gpu/drm/i915/intel_guc_ct.h b/drivers/gpu/drm/i915/intel_guc_ct.h new file mode 100644 index 000000000000..6d97f36fcc62 --- /dev/null +++ b/drivers/gpu/drm/i915/intel_guc_ct.h @@ -0,0 +1,86 @@ +/* + * Copyright © 2016-2017 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_CT_H_ +#define _INTEL_GUC_CT_H_ + +struct intel_guc; +struct i915_vma; + +#include "intel_guc_fwif.h" + +/** + * DOC: Command Transport (CT). + * + * Buffer based command transport is a replacement for MMIO based mechanism. + * It can be used to perform both host-2-guc and guc-to-host communication. + */ + +/** Represents single command transport buffer. + * + * A single command transport buffer consists of two parts, the header + * record (command transport buffer descriptor) and the actual buffer which + * holds the commands. + * + * @desc: pointer to the buffer descriptor + * @cmds: pointer to the commands buffer + */ +struct intel_guc_ct_buffer { + struct guc_ct_buffer_desc *desc; + u32 *cmds; +}; + +/** Represents pair of command transport buffers. + * + * Buffers go in pairs to allow bi-directional communication. + * To simplify the code we place both of them in the same vma. + * Buffers from the same pair must share unique owner id. + * + * @vma: pointer to the vma with pair of CT buffers + * @ctbs: buffers for sending(0) and receiving(1) commands + * @owner: unique identifier + * @next_fence: fence to be used with next send command + */ +struct intel_guc_ct_channel { + struct i915_vma *vma; + struct intel_guc_ct_buffer ctbs[2]; + u32 owner; + u32 next_fence; +}; + +/** Holds all command transport channels. + * + * @host_channel: main channel used by the host + */ +struct intel_guc_ct { + struct intel_guc_ct_channel host_channel; + /* other channels are tbd */ +}; + +void intel_guc_ct_init_early(struct intel_guc_ct *ct); + +/* XXX: move to intel_uc.h ? don't fit there either */ +int intel_guc_enable_ct(struct intel_guc *guc); +void intel_guc_disable_ct(struct intel_guc *guc); + +#endif /* _INTEL_GUC_CT_H_ */ diff --git a/drivers/gpu/drm/i915/intel_guc_fwif.h b/drivers/gpu/drm/i915/intel_guc_fwif.h index 6156845641a3..5fa286074811 100644 --- a/drivers/gpu/drm/i915/intel_guc_fwif.h +++ b/drivers/gpu/drm/i915/intel_guc_fwif.h @@ -331,6 +331,47 @@ struct guc_stage_desc { u64 desc_private; } __packed; +/* + * Describes single command transport buffer. + * Used by both guc-master and clients. + */ +struct guc_ct_buffer_desc { + u32 addr; /* gfx address */ + u64 host_private; /* host private data */ + u32 size; /* size in bytes */ + u32 head; /* offset updated by GuC*/ + u32 tail; /* offset updated by owner */ + u32 is_in_error; /* error indicator */ + u32 fence; /* fence updated by GuC */ + u32 status; /* status updated by GuC */ + u32 owner; /* id of the channel owner */ + u32 owner_sub_id; /* owner-defined field for extra tracking */ + u32 reserved[5]; +} __packed; + +/* Type of command transport buffer */ +#define INTEL_GUC_CT_BUFFER_TYPE_SEND 0x0u +#define INTEL_GUC_CT_BUFFER_TYPE_RECV 0x1u + +/* + * Definition of the command transport message header (DW0) + * + * bit[4..0] message len (in dwords) + * bit[7..5] reserved + * bit[8] write fence to desc + * bit[9] write status to H2G buff + * bit[10] send status (via G2H) + * bit[15..11] reserved + * bit[31..16] action code + */ +#define GUC_CT_MSG_LEN_SHIFT 0 +#define GUC_CT_MSG_LEN_MASK 0x1F +#define GUC_CT_MSG_WRITE_FENCE_TO_DESC (1 << 8) +#define GUC_CT_MSG_WRITE_STATUS_TO_BUFF (1 << 9) +#define GUC_CT_MSG_SEND_STATUS (1 << 10) +#define GUC_CT_MSG_ACTION_SHIFT 16 +#define GUC_CT_MSG_ACTION_MASK 0xFFFF + #define GUC_FORCEWAKE_RENDER (1 << 0) #define GUC_FORCEWAKE_MEDIA (1 << 1) @@ -515,6 +556,8 @@ enum intel_guc_action { INTEL_GUC_ACTION_EXIT_S_STATE = 0x502, INTEL_GUC_ACTION_SLPC_REQUEST = 0x3003, INTEL_GUC_ACTION_AUTHENTICATE_HUC = 0x4000, + INTEL_GUC_ACTION_REGISTER_COMMAND_TRANSPORT_BUFFER = 0x4505, + INTEL_GUC_ACTION_DEREGISTER_COMMAND_TRANSPORT_BUFFER = 0x4506, INTEL_GUC_ACTION_UK_LOG_ENABLE_LOGGING = 0x0E000, INTEL_GUC_ACTION_LIMIT }; diff --git a/drivers/gpu/drm/i915/intel_uc.c b/drivers/gpu/drm/i915/intel_uc.c index 31dc8c3444cd..d17029c57433 100644 --- a/drivers/gpu/drm/i915/intel_uc.c +++ b/drivers/gpu/drm/i915/intel_uc.c @@ -105,6 +105,8 @@ void intel_uc_init_early(struct drm_i915_private *dev_priv) { struct intel_guc *guc = &dev_priv->guc; + intel_guc_ct_init_early(&guc->ct); + mutex_init(&guc->send_mutex); guc->send = intel_guc_send_nop; guc->notify = guc_write_irq_trigger; @@ -288,14 +290,24 @@ static void guc_init_send_regs(struct intel_guc *guc) static int guc_enable_communication(struct intel_guc *guc) { - /* XXX: placeholder for alternate setup */ + struct drm_i915_private *dev_priv = guc_to_i915(guc); + guc_init_send_regs(guc); + + if (HAS_GUC_CT(dev_priv)) + return intel_guc_enable_ct(guc); + guc->send = intel_guc_send_mmio; return 0; } static void guc_disable_communication(struct intel_guc *guc) { + struct drm_i915_private *dev_priv = guc_to_i915(guc); + + if (HAS_GUC_CT(dev_priv)) + intel_guc_disable_ct(guc); + guc->send = intel_guc_send_nop; } @@ -442,6 +454,11 @@ int intel_guc_send_mmio(struct intel_guc *guc, const u32 *action, u32 len) GEM_BUG_ON(!len); GEM_BUG_ON(len > guc->send_regs.count); + /* If CT is available, we expect to use MMIO only during init/fini */ + GEM_BUG_ON(HAS_GUC_CT(dev_priv) && + *action != INTEL_GUC_ACTION_REGISTER_COMMAND_TRANSPORT_BUFFER && + *action != INTEL_GUC_ACTION_DEREGISTER_COMMAND_TRANSPORT_BUFFER); + mutex_lock(&guc->send_mutex); intel_uncore_forcewake_get(dev_priv, guc->send_regs.fw_domains); diff --git a/drivers/gpu/drm/i915/intel_uc.h b/drivers/gpu/drm/i915/intel_uc.h index 930f2e17b863..fb1d640a1b04 100644 --- a/drivers/gpu/drm/i915/intel_uc.h +++ b/drivers/gpu/drm/i915/intel_uc.h @@ -27,7 +27,7 @@ #include "intel_guc_fwif.h" #include "i915_guc_reg.h" #include "intel_ringbuffer.h" - +#include "intel_guc_ct.h" #include "i915_vma.h" struct drm_i915_gem_request; @@ -173,6 +173,7 @@ struct intel_guc_log { struct intel_guc { struct intel_uc_fw fw; struct intel_guc_log log; + struct intel_guc_ct ct; /* intel_guc_recv interrupt related state */ bool interrupts_enabled; -- cgit v1.2.3 From ac58d2ab0ad9c8b7e41404048a3ba4375db012d3 Mon Sep 17 00:00:00 2001 From: Daniele Ceraolo Spurio Date: Mon, 22 May 2017 10:50:28 -0700 Subject: drm/i915/guc: capture GuC logs if FW fails to load We're currently deleting the GuC logs if the FW fails to load, but those are still useful to understand why the loading failed. Keeping the object around allows us to access them after driver load is completed. v2: keep the object around instead of using kernel memory (chris) don't store the logs in the gpu_error struct (Chris) add a check on guc_log_level to avoid snapshotting empty logs v3: use separate debugfs for error log (Chris) v4: rebased v5: clean up obj selection, move err_load inside guc_log, move err_load cleanup, rename functions (Michal) v6: move obj back to intel_guc, move functions to intel_uc.c, don't clear obj on new GuC load, free object only if enable_guc_loading is set (Michal) Cc: Chris Wilson Cc: Oscar Mateo Cc: Michal Wajdeczko Signed-off-by: Daniele Ceraolo Spurio Link: http://patchwork.freedesktop.org/patch/msgid/1495475428-19295-1-git-send-email-daniele.ceraolospurio@intel.com Reviewed-by: Michal Wajdeczko Tested-by: Michel Thierry Signed-off-by: Chris Wilson --- drivers/gpu/drm/i915/i915_debugfs.c | 39 ++++++++++++++++++++++++------------- drivers/gpu/drm/i915/intel_uc.c | 25 ++++++++++++++++++++++-- drivers/gpu/drm/i915/intel_uc.h | 3 +++ 3 files changed, 51 insertions(+), 16 deletions(-) (limited to 'drivers/gpu/drm/i915/intel_uc.c') diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index c51c56af0013..0615237d1cea 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -2586,27 +2586,37 @@ static int i915_guc_stage_pool(struct seq_file *m, void *data) static int i915_guc_log_dump(struct seq_file *m, void *data) { - struct drm_i915_private *dev_priv = node_to_i915(m->private); - struct drm_i915_gem_object *obj; - int i = 0, pg; - - if (!dev_priv->guc.log.vma) - return 0; + struct drm_info_node *node = m->private; + struct drm_i915_private *dev_priv = node_to_i915(node); + bool dump_load_err = !!node->info_ent->data; + struct drm_i915_gem_object *obj = NULL; + u32 *log; + int i = 0; - obj = dev_priv->guc.log.vma->obj; - for (pg = 0; pg < obj->base.size / PAGE_SIZE; pg++) { - u32 *log = kmap_atomic(i915_gem_object_get_page(obj, pg)); + if (dump_load_err) + obj = dev_priv->guc.load_err_log; + else if (dev_priv->guc.log.vma) + obj = dev_priv->guc.log.vma->obj; - 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)); + if (!obj) + return 0; - kunmap_atomic(log); + log = i915_gem_object_pin_map(obj, I915_MAP_WC); + if (IS_ERR(log)) { + DRM_DEBUG("Failed to pin object\n"); + seq_puts(m, "(log data unaccessible)\n"); + return PTR_ERR(log); } + for (i = 0; i < obj->base.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)); + seq_putc(m, '\n'); + i915_gem_object_unpin_map(obj); + return 0; } @@ -4791,6 +4801,7 @@ static const struct drm_info_list i915_debugfs_list[] = { {"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_guc_load_err_log_dump", i915_guc_log_dump, 0, (void *)1}, {"i915_guc_stage_pool", i915_guc_stage_pool, 0}, {"i915_huc_load_status", i915_huc_load_status_info, 0}, {"i915_frequency_info", i915_frequency_info, 0}, diff --git a/drivers/gpu/drm/i915/intel_uc.c b/drivers/gpu/drm/i915/intel_uc.c index d17029c57433..7a7b07de28a3 100644 --- a/drivers/gpu/drm/i915/intel_uc.c +++ b/drivers/gpu/drm/i915/intel_uc.c @@ -288,6 +288,23 @@ static void guc_init_send_regs(struct intel_guc *guc) guc->send_regs.fw_domains = fw_domains; } +static void guc_capture_load_err_log(struct intel_guc *guc) +{ + if (!guc->log.vma || i915.guc_log_level < 0) + return; + + if (!guc->load_err_log) + guc->load_err_log = i915_gem_object_get(guc->log.vma->obj); + + return; +} + +static void guc_free_load_err_log(struct intel_guc *guc) +{ + if (guc->load_err_log) + i915_gem_object_put(guc->load_err_log); +} + static int guc_enable_communication(struct intel_guc *guc) { struct drm_i915_private *dev_priv = guc_to_i915(guc); @@ -367,11 +384,11 @@ int intel_uc_init_hw(struct drm_i915_private *dev_priv) /* Did we succeded or run out of retries? */ if (ret) - goto err_submission; + goto err_log_capture; ret = guc_enable_communication(guc); if (ret) - goto err_submission; + goto err_log_capture; intel_guc_auth_huc(dev_priv); if (i915.enable_guc_submission) { @@ -397,6 +414,8 @@ int intel_uc_init_hw(struct drm_i915_private *dev_priv) err_interrupts: guc_disable_communication(guc); gen9_disable_guc_interrupts(dev_priv); +err_log_capture: + guc_capture_load_err_log(guc); err_submission: if (i915.enable_guc_submission) i915_guc_submission_fini(dev_priv); @@ -422,6 +441,8 @@ void intel_uc_fini_hw(struct drm_i915_private *dev_priv) if (!i915.enable_guc_loading) return; + guc_free_load_err_log(&dev_priv->guc); + if (i915.enable_guc_submission) i915_guc_submission_disable(dev_priv); diff --git a/drivers/gpu/drm/i915/intel_uc.h b/drivers/gpu/drm/i915/intel_uc.h index fb1d640a1b04..69daf4c01cd0 100644 --- a/drivers/gpu/drm/i915/intel_uc.h +++ b/drivers/gpu/drm/i915/intel_uc.h @@ -175,6 +175,9 @@ struct intel_guc { struct intel_guc_log log; struct intel_guc_ct ct; + /* Log snapshot if GuC errors during load */ + struct drm_i915_gem_object *load_err_log; + /* intel_guc_recv interrupt related state */ bool interrupts_enabled; -- cgit v1.2.3 From c4a8952612f6c1c28bab200c45d773e08dfd24d5 Mon Sep 17 00:00:00 2001 From: Michel Thierry Date: Mon, 5 Jun 2017 10:12:51 -0700 Subject: drm/i915/guc: Clear enable_guc_loading in case of init failure And prevent calling i915_ggtt_disable_guc twice (the first when GuC init failed, and the second time during driver unload / intel_uc_fini_hw), and hitting the GEM_BUG_ON. v2: Clear enable_guc_loading unconditionally (Michal) Make sure guc_free_load_err_log is still called (Daniele) Don't shoot the messenger (Chris) Fixes: 3950bf3dbff10 ("drm/i915/guc: Add onion teardown to the GuC setup") Cc: Chris Wilson Cc: Michal Wajdeczko Cc: Daniele Ceraolo Spurio Cc: Joonas Lahtinen Signed-off-by: Michel Thierry Reviewed-by: Michal Wajdeczko Signed-off-by: Joonas Lahtinen Link: http://patchwork.freedesktop.org/patch/msgid/20170605171251.9905-1-michel.thierry@intel.com --- drivers/gpu/drm/i915/intel_uc.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers/gpu/drm/i915/intel_uc.c') diff --git a/drivers/gpu/drm/i915/intel_uc.c b/drivers/gpu/drm/i915/intel_uc.c index 7a7b07de28a3..27e072cc96eb 100644 --- a/drivers/gpu/drm/i915/intel_uc.c +++ b/drivers/gpu/drm/i915/intel_uc.c @@ -433,16 +433,19 @@ err_guc: DRM_NOTE("Falling back from GuC submission to execlist mode\n"); } + i915.enable_guc_loading = 0; + DRM_NOTE("GuC firmware loading disabled\n"); + return ret; } void intel_uc_fini_hw(struct drm_i915_private *dev_priv) { + guc_free_load_err_log(&dev_priv->guc); + if (!i915.enable_guc_loading) return; - guc_free_load_err_log(&dev_priv->guc); - if (i915.enable_guc_submission) i915_guc_submission_disable(dev_priv); -- cgit v1.2.3