drm/xe/gsc: add support for GSC proxy interrupt
authorDaniele Ceraolo Spurio <daniele.ceraolospurio@intel.com>
Wed, 17 Jan 2024 18:26:20 +0000 (10:26 -0800)
committerDaniele Ceraolo Spurio <daniele.ceraolospurio@intel.com>
Thu, 18 Jan 2024 19:04:37 +0000 (11:04 -0800)
The GSC notifies us of a proxy request via the HECI2 interrupt. The
interrupt must be enabled both in the HECI layer and in our usual gt irq
programming; for the latter, the interrupt is enabled via the same enable
register as the GSC CS, but it does have its own mask register. When the
interrupt is received, we also need to de-assert it in both layers.

The handling of the proxy request is deferred to the same worker that we
use for GSC load. New flags have been added to distinguish between the
init case and the proxy interrupt.

v2: rename irq define, fix include ordering (Alan)

Signed-off-by: Daniele Ceraolo Spurio <daniele.ceraolospurio@intel.com>
Cc: Alan Previn <alan.previn.teres.alexis@intel.com>
Cc: Suraj Kandpal <suraj.kandpal@intel.com>
Reviewed-by: Alan Previn <alan.previn.teres.alexis@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20240117182621.2653049-3-daniele.ceraolospurio@intel.com
drivers/gpu/drm/xe/regs/xe_gt_regs.h
drivers/gpu/drm/xe/xe_gsc.c
drivers/gpu/drm/xe/xe_gsc_proxy.c
drivers/gpu/drm/xe/xe_gsc_proxy.h
drivers/gpu/drm/xe/xe_gsc_types.h
drivers/gpu/drm/xe/xe_irq.c

index 4017319c63000f4f569f3804f83707fd93cb919d..0d4bfc35ff37efba6760aa9253b0b8340e676bc6 100644 (file)
 #define   INTR_ENGINE_CLASS(x)                 REG_FIELD_GET(GENMASK(18, 16), x)
 #define   INTR_ENGINE_INTR(x)                  REG_FIELD_GET(GENMASK(15, 0), x)
 #define   OTHER_GUC_INSTANCE                   0
+#define   OTHER_GSC_HECI2_INSTANCE             3
 #define   OTHER_GSC_INSTANCE                   6
 
 #define IIR_REG_SELECTOR(x)                    XE_REG(0x190070 + ((x) * 4))
 #define VCS0_VCS1_INTR_MASK                    XE_REG(0x1900a8)
 #define VCS2_VCS3_INTR_MASK                    XE_REG(0x1900ac)
 #define VECS0_VECS1_INTR_MASK                  XE_REG(0x1900d0)
+#define HECI2_RSVD_INTR_MASK                   XE_REG(0x1900e4)
 #define GUC_SG_INTR_MASK                       XE_REG(0x1900e8)
 #define GPM_WGBOXPERF_INTR_MASK                        XE_REG(0x1900ec)
 #define GUNIT_GSC_INTR_MASK                    XE_REG(0x1900f4)
index 85074b7304021a61db709d04ec94a67f7846936c..0b90fd9ef63a4411997548d694240e23e1f32348 100644 (file)
@@ -276,16 +276,27 @@ static void gsc_work(struct work_struct *work)
        struct xe_gsc *gsc = container_of(work, typeof(*gsc), work);
        struct xe_gt *gt = gsc_to_gt(gsc);
        struct xe_device *xe = gt_to_xe(gt);
+       u32 actions;
        int ret;
 
+       spin_lock_irq(&gsc->lock);
+       actions = gsc->work_actions;
+       gsc->work_actions = 0;
+       spin_unlock_irq(&gsc->lock);
+
        xe_device_mem_access_get(xe);
        xe_force_wake_get(gt_to_fw(gt), XE_FW_GSC);
 
-       ret = gsc_upload_and_init(gsc);
-       if (ret && ret != -EEXIST)
-               xe_uc_fw_change_status(&gsc->fw, XE_UC_FIRMWARE_LOAD_FAIL);
-       else
-               xe_uc_fw_change_status(&gsc->fw, XE_UC_FIRMWARE_RUNNING);
+       if (actions & GSC_ACTION_FW_LOAD) {
+               ret = gsc_upload_and_init(gsc);
+               if (ret && ret != -EEXIST)
+                       xe_uc_fw_change_status(&gsc->fw, XE_UC_FIRMWARE_LOAD_FAIL);
+               else
+                       xe_uc_fw_change_status(&gsc->fw, XE_UC_FIRMWARE_RUNNING);
+       }
+
+       if (actions & GSC_ACTION_SW_PROXY)
+               xe_gsc_proxy_request_handler(gsc);
 
        xe_force_wake_put(gt_to_fw(gt), XE_FW_GSC);
        xe_device_mem_access_put(xe);
@@ -299,6 +310,7 @@ int xe_gsc_init(struct xe_gsc *gsc)
 
        gsc->fw.type = XE_UC_FW_TYPE_GSC;
        INIT_WORK(&gsc->work, gsc_work);
+       spin_lock_init(&gsc->lock);
 
        /* The GSC uC is only available on the media GT */
        if (tile->media_gt && (gt != tile->media_gt)) {
@@ -422,6 +434,10 @@ void xe_gsc_load_start(struct xe_gsc *gsc)
                return;
        }
 
+       spin_lock_irq(&gsc->lock);
+       gsc->work_actions |= GSC_ACTION_FW_LOAD;
+       spin_unlock_irq(&gsc->lock);
+
        queue_work(gsc->wq, &gsc->work);
 }
 
index 86353c5a81cd14b3cafeacc6db0bc0da11834e42..309ef80e3b95e8febf0f9bc5e6e01ece11c13376 100644 (file)
@@ -21,6 +21,7 @@
 #include "xe_gt_printk.h"
 #include "xe_map.h"
 #include "xe_mmio.h"
+#include "xe_pm.h"
 
 /*
  * GSC proxy:
@@ -74,6 +75,30 @@ static bool gsc_proxy_init_done(struct xe_gsc *gsc)
               HECI1_FWSTS1_PROXY_STATE_NORMAL;
 }
 
+static void __gsc_proxy_irq_rmw(struct xe_gsc *gsc, u32 clr, u32 set)
+{
+       struct xe_gt *gt = gsc_to_gt(gsc);
+
+       /* make sure we never accidentally write the RST bit */
+       clr |= HECI_H_CSR_RST;
+
+       xe_mmio_rmw32(gt, HECI_H_CSR(MTL_GSC_HECI2_BASE), clr, set);
+}
+
+static void gsc_proxy_irq_clear(struct xe_gsc *gsc)
+{
+       /* The status bit is cleared by writing to it */
+       __gsc_proxy_irq_rmw(gsc, 0, HECI_H_CSR_IS);
+}
+
+static void gsc_proxy_irq_toggle(struct xe_gsc *gsc, bool enabled)
+{
+       u32 set = enabled ? HECI_H_CSR_IE : 0;
+       u32 clr = enabled ? 0 : HECI_H_CSR_IE;
+
+       __gsc_proxy_irq_rmw(gsc, clr, set);
+}
+
 static int proxy_send_to_csme(struct xe_gsc *gsc, u32 size)
 {
        struct xe_gt *gt = gsc_to_gt(gsc);
@@ -264,7 +289,7 @@ proxy_error:
        return ret < 0 ? ret : 0;
 }
 
-static int gsc_proxy_request_handler(struct xe_gsc *gsc)
+int xe_gsc_proxy_request_handler(struct xe_gsc *gsc)
 {
        struct xe_gt *gt = gsc_to_gt(gsc);
        int slept;
@@ -286,12 +311,36 @@ static int gsc_proxy_request_handler(struct xe_gsc *gsc)
                xe_gt_err(gt, "GSC proxy component not bound!\n");
                err = -EIO;
        } else {
+               /*
+                * clear the pending interrupt and allow new proxy requests to
+                * be generated while we handle the current one
+                */
+               gsc_proxy_irq_clear(gsc);
                err = proxy_query(gsc);
        }
        mutex_unlock(&gsc->proxy.mutex);
        return err;
 }
 
+void xe_gsc_proxy_irq_handler(struct xe_gsc *gsc, u32 iir)
+{
+       struct xe_gt *gt = gsc_to_gt(gsc);
+
+       if (unlikely(!iir))
+               return;
+
+       if (!gsc->proxy.component) {
+               xe_gt_err(gt, "GSC proxy irq received without the component being bound!\n");
+               return;
+       }
+
+       spin_lock(&gsc->lock);
+       gsc->work_actions |= GSC_ACTION_SW_PROXY;
+       spin_unlock(&gsc->lock);
+
+       queue_work(gsc->wq, &gsc->work);
+}
+
 static int xe_gsc_proxy_component_bind(struct device *xe_kdev,
                                       struct device *mei_kdev, void *data)
 {
@@ -434,11 +483,28 @@ void xe_gsc_proxy_remove(struct xe_gsc *gsc)
 {
        struct xe_gt *gt = gsc_to_gt(gsc);
        struct xe_device *xe = gt_to_xe(gt);
+       int err = 0;
 
-       if (gsc->proxy.component_added) {
-               component_del(xe->drm.dev, &xe_gsc_proxy_component_ops);
-               gsc->proxy.component_added = false;
-       }
+       if (!gsc->proxy.component_added)
+               return;
+
+       /* disable HECI2 IRQs */
+       xe_pm_runtime_get(xe);
+       err = xe_force_wake_get(gt_to_fw(gt), XE_FW_GSC);
+       if (err)
+               xe_gt_err(gt, "failed to get forcewake to disable GSC interrupts\n");
+
+       /* try do disable irq even if forcewake failed */
+       gsc_proxy_irq_toggle(gsc, false);
+
+       if (!err)
+               xe_force_wake_put(gt_to_fw(gt), XE_FW_GSC);
+       xe_pm_runtime_put(xe);
+
+       xe_gsc_wait_for_worker_completion(gsc);
+
+       component_del(xe->drm.dev, &xe_gsc_proxy_component_ops);
+       gsc->proxy.component_added = false;
 }
 
 /**
@@ -451,11 +517,14 @@ int xe_gsc_proxy_start(struct xe_gsc *gsc)
 {
        int err;
 
+       /* enable the proxy interrupt in the GSC shim layer */
+       gsc_proxy_irq_toggle(gsc, true);
+
        /*
         * The handling of the first proxy request must be manually triggered to
         * notify the GSC that we're ready to support the proxy flow.
         */
-       err = gsc_proxy_request_handler(gsc);
+       err = xe_gsc_proxy_request_handler(gsc);
        if (err)
                return err;
 
index 5dc6321efbafb83f6796262819df06144bdd1384..908f9441f0934031d8bd4071a8b0818e80bfcf39 100644 (file)
@@ -14,4 +14,7 @@ int xe_gsc_proxy_init(struct xe_gsc *gsc);
 void xe_gsc_proxy_remove(struct xe_gsc *gsc);
 int xe_gsc_proxy_start(struct xe_gsc *gsc);
 
+int xe_gsc_proxy_request_handler(struct xe_gsc *gsc);
+void xe_gsc_proxy_irq_handler(struct xe_gsc *gsc, u32 iir);
+
 #endif
index 07bfa87ec0026bf35eedfc4b4f0c5a8577262577..060d0fe848ad7fcdec043e9c406834e45c9dfc1b 100644 (file)
@@ -8,6 +8,7 @@
 
 #include <linux/iosys-map.h>
 #include <linux/mutex.h>
+#include <linux/spinlock.h>
 #include <linux/types.h>
 #include <linux/workqueue.h>
 
@@ -39,6 +40,14 @@ struct xe_gsc {
        /** @work: delayed load and proxy handling work */
        struct work_struct work;
 
+       /** @lock: protects access to the work_actions mask */
+       spinlock_t lock;
+
+       /** @work_actions: mask of actions to be performed in the work */
+       u32 work_actions;
+#define GSC_ACTION_FW_LOAD BIT(0)
+#define GSC_ACTION_SW_PROXY BIT(1)
+
        /** @proxy: sub-structure containing the SW proxy-related variables */
        struct {
                /** @component: struct for communication with mei component */
index 907c8ff0fa21fe3112339f97fc5d9e31ffe6b5b9..2fd8cc26fc9fe09a3f6696d943be1ccd28e6299d 100644 (file)
@@ -14,6 +14,7 @@
 #include "xe_device.h"
 #include "xe_display.h"
 #include "xe_drv.h"
+#include "xe_gsc_proxy.h"
 #include "xe_gt.h"
 #include "xe_guc.h"
 #include "xe_hw_engine.h"
@@ -131,6 +132,7 @@ void xe_irq_enable_hwe(struct xe_gt *gt)
        u32 ccs_mask, bcs_mask;
        u32 irqs, dmask, smask;
        u32 gsc_mask = 0;
+       u32 heci_mask = 0;
 
        if (xe_device_uc_enabled(xe)) {
                irqs = GT_RENDER_USER_INTERRUPT |
@@ -180,14 +182,23 @@ void xe_irq_enable_hwe(struct xe_gt *gt)
                xe_mmio_write32(gt, VCS2_VCS3_INTR_MASK, ~dmask);
                xe_mmio_write32(gt, VECS0_VECS1_INTR_MASK, ~dmask);
 
-               if (xe_hw_engine_mask_per_class(gt, XE_ENGINE_CLASS_OTHER))
+               /*
+                * the heci2 interrupt is enabled via the same register as the
+                * GSCCS interrupts, but it has its own mask register.
+                */
+               if (xe_hw_engine_mask_per_class(gt, XE_ENGINE_CLASS_OTHER)) {
                        gsc_mask = irqs;
-               else if (HAS_HECI_GSCFI(xe))
+                       heci_mask = GSC_IRQ_INTF(1);
+               } else if (HAS_HECI_GSCFI(xe)) {
                        gsc_mask = GSC_IRQ_INTF(1);
+               }
+
                if (gsc_mask) {
-                       xe_mmio_write32(gt, GUNIT_GSC_INTR_ENABLE, gsc_mask);
+                       xe_mmio_write32(gt, GUNIT_GSC_INTR_ENABLE, gsc_mask | heci_mask);
                        xe_mmio_write32(gt, GUNIT_GSC_INTR_MASK, ~gsc_mask);
                }
+               if (heci_mask)
+                       xe_mmio_write32(gt, HECI2_RSVD_INTR_MASK, ~(heci_mask << 16));
        }
 }
 
@@ -234,6 +245,8 @@ gt_other_irq_handler(struct xe_gt *gt, const u8 instance, const u16 iir)
                return xe_guc_irq_handler(&gt->uc.guc, iir);
        if (instance == OTHER_MEDIA_GUC_INSTANCE && xe_gt_is_media_type(gt))
                return xe_guc_irq_handler(&gt->uc.guc, iir);
+       if (instance == OTHER_GSC_HECI2_INSTANCE && xe_gt_is_media_type(gt))
+               return xe_gsc_proxy_irq_handler(&gt->uc.gsc, iir);
 
        if (instance != OTHER_GUC_INSTANCE &&
            instance != OTHER_MEDIA_GUC_INSTANCE) {
@@ -251,15 +264,23 @@ static struct xe_gt *pick_engine_gt(struct xe_tile *tile,
        if (MEDIA_VER(xe) < 13)
                return tile->primary_gt;
 
-       if (class == XE_ENGINE_CLASS_VIDEO_DECODE ||
-           class == XE_ENGINE_CLASS_VIDEO_ENHANCE)
+       switch (class) {
+       case XE_ENGINE_CLASS_VIDEO_DECODE:
+       case XE_ENGINE_CLASS_VIDEO_ENHANCE:
                return tile->media_gt;
-
-       if (class == XE_ENGINE_CLASS_OTHER &&
-           (instance == OTHER_MEDIA_GUC_INSTANCE || instance == OTHER_GSC_INSTANCE))
-               return tile->media_gt;
-
-       return tile->primary_gt;
+       case XE_ENGINE_CLASS_OTHER:
+               switch (instance) {
+               case OTHER_MEDIA_GUC_INSTANCE:
+               case OTHER_GSC_INSTANCE:
+               case OTHER_GSC_HECI2_INSTANCE:
+                       return tile->media_gt;
+               default:
+                       break;
+               };
+               fallthrough;
+       default:
+               return tile->primary_gt;
+       }
 }
 
 static void gt_irq_handler(struct xe_tile *tile,
@@ -486,6 +507,7 @@ static void gt_irq_reset(struct xe_tile *tile)
            HAS_HECI_GSCFI(tile_to_xe(tile))) {
                xe_mmio_write32(mmio, GUNIT_GSC_INTR_ENABLE, 0);
                xe_mmio_write32(mmio, GUNIT_GSC_INTR_MASK, ~0);
+               xe_mmio_write32(mmio, HECI2_RSVD_INTR_MASK, ~0);
        }
 
        xe_mmio_write32(mmio, GPM_WGBOXPERF_INTR_ENABLE, 0);