Commit eb08104f authored by Daniele Ceraolo Spurio's avatar Daniele Ceraolo Spurio
Browse files

drm/xe/gsc: add support for GSC proxy interrupt



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: default avatarDaniele Ceraolo Spurio <daniele.ceraolospurio@intel.com>
Cc: Alan Previn <alan.previn.teres.alexis@intel.com>
Cc: Suraj Kandpal <suraj.kandpal@intel.com>
Reviewed-by: default avatarAlan Previn <alan.previn.teres.alexis@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20240117182621.2653049-3-daniele.ceraolospurio@intel.com
parent 997a55ca
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -459,6 +459,7 @@
#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))
@@ -467,6 +468,7 @@
#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)
+21 −5
Original line number Diff line number Diff line
@@ -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);

	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);
}

+75 −6
Original line number Diff line number Diff line
@@ -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 @@ static int proxy_query(struct xe_gsc *gsc)
	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,12 +483,29 @@ 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)
		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);

	if (gsc->proxy.component_added) {
	component_del(xe->drm.dev, &xe_gsc_proxy_component_ops);
	gsc->proxy.component_added = false;
}
}

/**
 * xe_gsc_proxy_start() - start the proxy by submitting the first request
@@ -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;

+3 −0
Original line number Diff line number Diff line
@@ -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
+9 −0
Original line number Diff line number Diff line
@@ -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 */
Loading