[PATCH v2 1/2] drm/xe/gsc: Initialize GSC proxy
Kandpal, Suraj
suraj.kandpal at intel.com
Tue Jan 16 06:13:06 UTC 2024
I was under the impression that this patch series will introduce a function which
Checks the running status of the firmware is it not so or is that going to be a part
Another series.
> Subject: [PATCH v2 1/2] drm/xe/gsc: Initialize GSC proxy
>
> The GSC uC needs to communicate with the CSME to perform certain
> operations. Since the GSC can't perform this communication directly on
> platforms where it is integrated in GT, the graphics driver needs to transfer the
> messages from GSC to CSME and back. The proxy flow must be manually
> started after the GSC is loaded to signal to GSC that we're ready to handle its
> messages and allow it to query its init data from CSME.
>
> Note that the component must be removed before the pci_remove call
> completes, so we can't use a drmm helper for it and we need to instead
Also a typo here *drm helper
Regards,
Suraj Kandpal
> perform the cleanup as part of the removal flow.
>
> v2: add function documentation, more targeted memory clear, clearer logs
> and variable names (Alan)
>
> Signed-off-by: Daniele Ceraolo Spurio <daniele.ceraolospurio at intel.com>
> Cc: Alan Previn <alan.previn.teres.alexis at intel.com>
> Cc: Suraj Kandpal <suraj.kandpal at intel.com>
> ---
> drivers/gpu/drm/xe/Makefile | 1 +
> .../gpu/drm/xe/abi/gsc_proxy_commands_abi.h | 44 ++
> drivers/gpu/drm/xe/xe_device.c | 22 +-
> drivers/gpu/drm/xe/xe_gsc.c | 52 +-
> drivers/gpu/drm/xe/xe_gsc.h | 1 +
> drivers/gpu/drm/xe/xe_gsc_proxy.c | 468 ++++++++++++++++++
> drivers/gpu/drm/xe/xe_gsc_proxy.h | 17 +
> drivers/gpu/drm/xe/xe_gsc_submit.c | 13 +
> drivers/gpu/drm/xe/xe_gsc_submit.h | 1 +
> drivers/gpu/drm/xe/xe_gsc_types.h | 23 +
> drivers/gpu/drm/xe/xe_gt.c | 13 +
> drivers/gpu/drm/xe/xe_gt.h | 1 +
> drivers/gpu/drm/xe/xe_uc.c | 14 +
> drivers/gpu/drm/xe/xe_uc.h | 1 +
> 14 files changed, 658 insertions(+), 13 deletions(-) create mode 100644
> drivers/gpu/drm/xe/abi/gsc_proxy_commands_abi.h
> create mode 100644 drivers/gpu/drm/xe/xe_gsc_proxy.c create mode
> 100644 drivers/gpu/drm/xe/xe_gsc_proxy.h
>
> diff --git a/drivers/gpu/drm/xe/Makefile b/drivers/gpu/drm/xe/Makefile index
> 6952da8979ea..d8dd43bb34b0 100644
> --- a/drivers/gpu/drm/xe/Makefile
> +++ b/drivers/gpu/drm/xe/Makefile
> @@ -77,6 +77,7 @@ xe-y += xe_bb.o \
> xe_ggtt.o \
> xe_gpu_scheduler.o \
> xe_gsc.o \
> + xe_gsc_proxy.o \
> xe_gsc_submit.o \
> xe_gt.o \
> xe_gt_ccs_mode.o \
> diff --git a/drivers/gpu/drm/xe/abi/gsc_proxy_commands_abi.h
> b/drivers/gpu/drm/xe/abi/gsc_proxy_commands_abi.h
> new file mode 100644
> index 000000000000..80bbf06a3eb8
> --- /dev/null
> +++ b/drivers/gpu/drm/xe/abi/gsc_proxy_commands_abi.h
> @@ -0,0 +1,44 @@
> +/* SPDX-License-Identifier: MIT */
> +/*
> + * Copyright © 2023 Intel Corporation
> + */
> +
> +#ifndef _ABI_GSC_PROXY_COMMANDS_ABI_H
> +#define _ABI_GSC_PROXY_COMMANDS_ABI_H
> +
> +#include <linux/types.h>
> +
> +/* Heci client ID for proxy commands */ #define HECI_MEADDRESS_PROXY 10
> +
> +/* FW-defined proxy header */
> +struct xe_gsc_proxy_header {
> + /*
> + * hdr:
> + * Bits 0-7: type of the proxy message (see enum xe_gsc_proxy_type)
> + * Bits 8-15: rsvd
> + * Bits 16-31: length in bytes of the payload following the proxy
> header
> + */
> + u32 hdr;
> +#define GSC_PROXY_TYPE GENMASK(7, 0)
> +#define GSC_PROXY_PAYLOAD_LENGTH GENMASK(31, 16)
> +
> + u32 source; /* Source of the Proxy message */
> + u32 destination; /* Destination of the Proxy message */
> +#define GSC_PROXY_ADDRESSING_KMD 0x10000 #define
> +GSC_PROXY_ADDRESSING_GSC 0x20000 #define
> GSC_PROXY_ADDRESSING_CSME
> +0x30000
> +
> + u32 status; /* Command status */
> +} __packed;
> +
> +/* FW-defined proxy types */
> +enum xe_gsc_proxy_type {
> + GSC_PROXY_MSG_TYPE_PROXY_INVALID = 0,
> + GSC_PROXY_MSG_TYPE_PROXY_QUERY = 1,
> + GSC_PROXY_MSG_TYPE_PROXY_PAYLOAD = 2,
> + GSC_PROXY_MSG_TYPE_PROXY_END = 3,
> + GSC_PROXY_MSG_TYPE_PROXY_NOTIFICATION = 4, };
> +
> +#endif
> diff --git a/drivers/gpu/drm/xe/xe_device.c b/drivers/gpu/drm/xe/xe_device.c
> index 004e65544e8d..7e1d39488472 100644
> --- a/drivers/gpu/drm/xe/xe_device.c
> +++ b/drivers/gpu/drm/xe/xe_device.c
> @@ -26,6 +26,7 @@
> #include "xe_exec_queue.h"
> #include "xe_exec.h"
> #include "xe_ggtt.h"
> +#include "xe_gsc_proxy.h"
> #include "xe_gt.h"
> #include "xe_gt_mcr.h"
> #include "xe_irq.h"
> @@ -434,6 +435,7 @@ int xe_device_probe(struct xe_device *xe)
> struct xe_tile *tile;
> struct xe_gt *gt;
> int err;
> + u8 last_gt;
> u8 id;
>
> xe_pat_init_early(xe);
> @@ -521,16 +523,18 @@ int xe_device_probe(struct xe_device *xe)
> goto err_irq_shutdown;
>
> for_each_gt(gt, xe, id) {
> + last_gt = id;
> +
> err = xe_gt_init(gt);
> if (err)
> - goto err_irq_shutdown;
> + goto err_fini_gt;
> }
>
> xe_heci_gsc_init(xe);
>
> err = xe_display_init(xe);
> if (err)
> - goto err_irq_shutdown;
> + goto err_fini_gt;
>
> err = drm_dev_register(&xe->drm, 0);
> if (err)
> @@ -551,6 +555,14 @@ int xe_device_probe(struct xe_device *xe)
> err_fini_display:
> xe_display_driver_remove(xe);
>
> +err_fini_gt:
> + for_each_gt(gt, xe, id) {
> + if (id < last_gt)
> + xe_gt_remove(gt);
> + else
> + break;
> + }
> +
> err_irq_shutdown:
> xe_irq_shutdown(xe);
> err:
> @@ -568,12 +580,18 @@ static void xe_device_remove_display(struct
> xe_device *xe)
>
> void xe_device_remove(struct xe_device *xe) {
> + struct xe_gt *gt;
> + u8 id;
> +
> xe_device_remove_display(xe);
>
> xe_display_fini(xe);
>
> xe_heci_gsc_fini(xe);
>
> + for_each_gt(gt, xe, id)
> + xe_gt_remove(gt);
> +
> xe_irq_shutdown(xe);
> }
>
> diff --git a/drivers/gpu/drm/xe/xe_gsc.c b/drivers/gpu/drm/xe/xe_gsc.c index
> a8a895cf4b44..b19e3f676a10 100644
> --- a/drivers/gpu/drm/xe/xe_gsc.c
> +++ b/drivers/gpu/drm/xe/xe_gsc.c
> @@ -13,6 +13,7 @@
> #include "xe_bo.h"
> #include "xe_device.h"
> #include "xe_exec_queue.h"
> +#include "xe_gsc_proxy.h"
> #include "xe_gsc_submit.h"
> #include "xe_gt.h"
> #include "xe_gt_printk.h"
> @@ -242,8 +243,31 @@ static int gsc_upload(struct xe_gsc *gsc)
> if (err)
> return err;
>
> + return 0;
> +}
> +
> +static int gsc_upload_and_init(struct xe_gsc *gsc) {
> + struct xe_gt *gt = gsc_to_gt(gsc);
> + int ret;
> +
> + ret = gsc_upload(gsc);
> + if (ret)
> + return ret;
> +
> + xe_uc_fw_change_status(&gsc->fw,
> XE_UC_FIRMWARE_TRANSFERRED);
> xe_gt_dbg(gt, "GSC FW async load completed\n");
>
> + /* HuC auth failure is not fatal */
> + if (xe_huc_is_authenticated(>->uc.huc, XE_HUC_AUTH_VIA_GUC))
> + xe_huc_auth(>->uc.huc, XE_HUC_AUTH_VIA_GSC);
> +
> + ret = xe_gsc_proxy_start(gsc);
> + if (ret)
> + return ret;
> +
> + xe_gt_dbg(gt, "GSC proxy init completed\n");
> +
> return 0;
> }
>
> @@ -257,19 +281,12 @@ static void gsc_work(struct work_struct *work)
> xe_device_mem_access_get(xe);
> xe_force_wake_get(gt_to_fw(gt), XE_FW_GSC);
>
> - ret = gsc_upload(gsc);
> - if (ret && ret != -EEXIST) {
> + ret = gsc_upload_and_init(gsc);
> + if (ret && ret != -EEXIST)
> xe_uc_fw_change_status(&gsc->fw,
> XE_UC_FIRMWARE_LOAD_FAIL);
> - goto out;
> - }
> -
> - xe_uc_fw_change_status(&gsc->fw,
> XE_UC_FIRMWARE_TRANSFERRED);
> -
> - /* HuC auth failure is not fatal */
> - if (xe_huc_is_authenticated(>->uc.huc, XE_HUC_AUTH_VIA_GUC))
> - xe_huc_auth(>->uc.huc, XE_HUC_AUTH_VIA_GSC);
> + else
> + xe_uc_fw_change_status(&gsc->fw,
> XE_UC_FIRMWARE_RUNNING);
>
> -out:
> xe_force_wake_put(gt_to_fw(gt), XE_FW_GSC);
> xe_device_mem_access_put(xe);
> }
> @@ -302,6 +319,10 @@ int xe_gsc_init(struct xe_gsc *gsc)
> else if (ret)
> goto out;
>
> + ret = xe_gsc_proxy_init(gsc);
> + if (ret && ret != -ENODEV)
> + goto out;
> +
> return 0;
>
> out:
> @@ -410,6 +431,15 @@ void xe_gsc_wait_for_worker_completion(struct
> xe_gsc *gsc)
> flush_work(&gsc->work);
> }
>
> +/**
> + * xe_gsc_remove() - Clean up the GSC structures before driver removal
> + * @gsc: the GSC uC
> + */
> +void xe_gsc_remove(struct xe_gsc *gsc)
> +{
> + xe_gsc_proxy_remove(gsc);
> +}
> +
> /*
> * wa_14015076503: if the GSC FW is loaded, we need to alert it before doing
> a
> * GSC engine reset by writing a notification bit in the GS1 register and then
> diff --git a/drivers/gpu/drm/xe/xe_gsc.h b/drivers/gpu/drm/xe/xe_gsc.h index
> bc1ef7f31ea2..c6fb32e3fd79 100644
> --- a/drivers/gpu/drm/xe/xe_gsc.h
> +++ b/drivers/gpu/drm/xe/xe_gsc.h
> @@ -14,6 +14,7 @@ int xe_gsc_init(struct xe_gsc *gsc); int
> xe_gsc_init_post_hwconfig(struct xe_gsc *gsc); void
> xe_gsc_wait_for_worker_completion(struct xe_gsc *gsc); void
> xe_gsc_load_start(struct xe_gsc *gsc);
> +void xe_gsc_remove(struct xe_gsc *gsc);
>
> void xe_gsc_wa_14015076503(struct xe_gt *gt, bool prep);
>
> diff --git a/drivers/gpu/drm/xe/xe_gsc_proxy.c
> b/drivers/gpu/drm/xe/xe_gsc_proxy.c
> new file mode 100644
> index 000000000000..86353c5a81cd
> --- /dev/null
> +++ b/drivers/gpu/drm/xe/xe_gsc_proxy.c
> @@ -0,0 +1,468 @@
> +// SPDX-License-Identifier: MIT
> +/*
> + * Copyright © 2023 Intel Corporation
> + */
> +
> +#include "xe_gsc_proxy.h"
> +
> +#include <linux/component.h>
> +#include <linux/delay.h>
> +
> +#include <drm/drm_managed.h>
> +#include <drm/i915_component.h>
> +#include <drm/i915_gsc_proxy_mei_interface.h>
> +
> +#include "abi/gsc_proxy_commands_abi.h"
> +#include "regs/xe_gsc_regs.h"
> +#include "xe_bo.h"
> +#include "xe_gsc.h"
> +#include "xe_gsc_submit.h"
> +#include "xe_gt.h"
> +#include "xe_gt_printk.h"
> +#include "xe_map.h"
> +#include "xe_mmio.h"
> +
> +/*
> + * GSC proxy:
> + * The GSC uC needs to communicate with the CSME to perform certain
> operations.
> + * Since the GSC can't perform this communication directly on platforms
> +where it
> + * is integrated in GT, the graphics driver needs to transfer the
> +messages from
> + * GSC to CSME and back. The proxy flow must be manually started after
> +the GSC
> + * is loaded to signal to GSC that we're ready to handle its messages
> +and allow
> + * it to query its init data from CSME; GSC will then trigger an HECI2
> +interrupt
> + * if it needs to send messages to CSME again.
> + * The proxy flow is as follow:
> + * 1 - Xe submits a request to GSC asking for the message to CSME
> + * 2 - GSC replies with the proxy header + payload for CSME
> + * 3 - Xe sends the reply from GSC as-is to CSME via the mei proxy
> +component
> + * 4 - CSME replies with the proxy header + payload for GSC
> + * 5 - Xe submits a request to GSC with the reply from CSME
> + * 6 - GSC replies either with a new header + payload (same as step 2, so we
> + * restart from there) or with an end message.
> + */
> +
> +/*
> + * The component should load quite quickly in most cases, but it could
> +take
> + * a bit. Using a very big timeout just to cover the worst case
> +scenario */ #define GSC_PROXY_INIT_TIMEOUT_MS 20000
> +
> +/* shorthand define for code compactness */ #define PROXY_HDR_SIZE
> +(sizeof(struct xe_gsc_proxy_header))
> +
> +/* the protocol supports up to 32K in each direction */ #define
> +GSC_PROXY_BUFFER_SIZE SZ_32K #define GSC_PROXY_CHANNEL_SIZE
> +(GSC_PROXY_BUFFER_SIZE * 2)
> +
> +static struct xe_gt *
> +gsc_to_gt(struct xe_gsc *gsc)
> +{
> + return container_of(gsc, struct xe_gt, uc.gsc); }
> +
> +static inline struct xe_device *kdev_to_xe(struct device *kdev) {
> + return dev_get_drvdata(kdev);
> +}
> +
> +static bool gsc_proxy_init_done(struct xe_gsc *gsc) {
> + struct xe_gt *gt = gsc_to_gt(gsc);
> + u32 fwsts1 = xe_mmio_read32(gt,
> HECI_FWSTS1(MTL_GSC_HECI1_BASE));
> +
> + return REG_FIELD_GET(HECI1_FWSTS1_CURRENT_STATE, fwsts1) ==
> + HECI1_FWSTS1_PROXY_STATE_NORMAL; }
> +
> +static int proxy_send_to_csme(struct xe_gsc *gsc, u32 size) {
> + struct xe_gt *gt = gsc_to_gt(gsc);
> + struct i915_gsc_proxy_component *comp = gsc->proxy.component;
> + int ret;
> +
> + ret = comp->ops->send(comp->mei_dev, gsc->proxy.to_csme, size);
> + if (ret < 0) {
> + xe_gt_err(gt, "Failed to send CSME proxy message\n");
> + return ret;
> + }
> +
> + ret = comp->ops->recv(comp->mei_dev, gsc->proxy.from_csme,
> GSC_PROXY_BUFFER_SIZE);
> + if (ret < 0) {
> + xe_gt_err(gt, "Failed to receive CSME proxy message\n");
> + return ret;
> + }
> +
> + return ret;
> +}
> +
> +static int proxy_send_to_gsc(struct xe_gsc *gsc, u32 size) {
> + struct xe_gt *gt = gsc_to_gt(gsc);
> + u64 addr_in = xe_bo_ggtt_addr(gsc->proxy.bo);
> + u64 addr_out = addr_in + GSC_PROXY_BUFFER_SIZE;
> + int err;
> +
> + /* the message must contain at least the gsc and proxy headers */
> + if (size > GSC_PROXY_BUFFER_SIZE) {
> + xe_gt_err(gt, "Invalid GSC proxy message size: %u\n", size);
> + return -EINVAL;
> + }
> +
> + err = xe_gsc_pkt_submit_kernel(gsc, addr_in, size,
> + addr_out, GSC_PROXY_BUFFER_SIZE);
> + if (err) {
> + xe_gt_err(gt, "Failed to submit gsc proxy rq (%pe)\n",
> ERR_PTR(err));
> + return err;
> + }
> +
> + return 0;
> +}
> +
> +static int validate_proxy_header(struct xe_gsc_proxy_header *header,
> + u32 source, u32 dest, u32 max_size) {
> + u32 type = FIELD_GET(GSC_PROXY_TYPE, header->hdr);
> + u32 length = FIELD_GET(GSC_PROXY_PAYLOAD_LENGTH, header-
> >hdr);
> +
> + if (header->destination != dest || header->source != source)
> + return -ENOEXEC;
> +
> + if (length + PROXY_HDR_SIZE > max_size)
> + return -E2BIG;
> +
> + switch (type) {
> + case GSC_PROXY_MSG_TYPE_PROXY_PAYLOAD:
> + if (length > 0)
> + break;
> + fallthrough;
> + case GSC_PROXY_MSG_TYPE_PROXY_INVALID:
> + return -EIO;
> + default:
> + break;
> + }
> +
> + return 0;
> +}
> +
> +#define proxy_header_wr(xe_, map_, offset_, field_, val_) \
> + xe_map_wr_field(xe_, map_, offset_, struct xe_gsc_proxy_header,
> +field_, val_)
> +
> +#define proxy_header_rd(xe_, map_, offset_, field_) \
> + xe_map_rd_field(xe_, map_, offset_, struct xe_gsc_proxy_header,
> +field_)
> +
> +static u32 emit_proxy_header(struct xe_device *xe, struct iosys_map
> +*map, u32 offset) {
> + xe_map_memset(xe, map, offset, 0, PROXY_HDR_SIZE);
> +
> + proxy_header_wr(xe, map, offset, hdr,
> + FIELD_PREP(GSC_PROXY_TYPE,
> GSC_PROXY_MSG_TYPE_PROXY_QUERY) |
> + FIELD_PREP(GSC_PROXY_PAYLOAD_LENGTH, 0));
> +
> + proxy_header_wr(xe, map, offset, source,
> GSC_PROXY_ADDRESSING_KMD);
> + proxy_header_wr(xe, map, offset, destination,
> GSC_PROXY_ADDRESSING_GSC);
> + proxy_header_wr(xe, map, offset, status, 0);
> +
> + return offset + PROXY_HDR_SIZE;
> +}
> +
> +static int proxy_query(struct xe_gsc *gsc) {
> + struct xe_gt *gt = gsc_to_gt(gsc);
> + struct xe_device *xe = gt_to_xe(gt);
> + struct xe_gsc_proxy_header *to_csme_hdr = gsc->proxy.to_csme;
> + void *to_csme_payload = gsc->proxy.to_csme + PROXY_HDR_SIZE;
> + u32 wr_offset;
> + u32 reply_offset;
> + u32 size;
> + int ret;
> +
> + wr_offset = xe_gsc_emit_header(xe, &gsc->proxy.to_gsc, 0,
> + HECI_MEADDRESS_PROXY, 0,
> PROXY_HDR_SIZE);
> + wr_offset = emit_proxy_header(xe, &gsc->proxy.to_gsc, wr_offset);
> +
> + size = wr_offset;
> +
> + while (1) {
> + /*
> + * Poison the GSC response header space to make sure we
> don't
> + * read a stale reply.
> + */
> + xe_gsc_poison_header(xe, &gsc->proxy.from_gsc, 0);
> +
> + /* send proxy message to GSC */
> + ret = proxy_send_to_gsc(gsc, size);
> + if (ret)
> + goto proxy_error;
> +
> + /* check the reply from GSC */
> + ret = xe_gsc_read_out_header(xe, &gsc->proxy.from_gsc, 0,
> + PROXY_HDR_SIZE, &reply_offset);
> + if (ret) {
> + xe_gt_err(gt, "Invalid gsc header in proxy reply
> (%pe)\n",
> + ERR_PTR(ret));
> + goto proxy_error;
> + }
> +
> + /* copy the proxy header reply from GSC */
> + xe_map_memcpy_from(xe, to_csme_hdr, &gsc-
> >proxy.from_gsc,
> + reply_offset, PROXY_HDR_SIZE);
> +
> + /* stop if this was the last message */
> + if (FIELD_GET(GSC_PROXY_TYPE, to_csme_hdr->hdr) ==
> GSC_PROXY_MSG_TYPE_PROXY_END)
> + break;
> +
> + /* make sure the GSC-to-CSME proxy header is sane */
> + ret = validate_proxy_header(to_csme_hdr,
> + GSC_PROXY_ADDRESSING_GSC,
> + GSC_PROXY_ADDRESSING_CSME,
> + GSC_PROXY_BUFFER_SIZE -
> reply_offset);
> + if (ret) {
> + xe_gt_err(gt, "invalid GSC to CSME proxy header!
> (%pe)\n",
> + ERR_PTR(ret));
> + goto proxy_error;
> + }
> +
> + /* copy the rest of the message */
> + size = FIELD_GET(GSC_PROXY_PAYLOAD_LENGTH,
> to_csme_hdr->hdr);
> + xe_map_memcpy_from(xe, to_csme_payload, &gsc-
> >proxy.from_gsc,
> + reply_offset + PROXY_HDR_SIZE, size);
> +
> + /* send the GSC message to the CSME */
> + ret = proxy_send_to_csme(gsc, size + PROXY_HDR_SIZE);
> + if (ret < 0)
> + goto proxy_error;
> +
> + /* reply size from CSME, including the proxy header */
> + size = ret;
> + if (size < PROXY_HDR_SIZE) {
> + xe_gt_err(gt, "CSME to GSC proxy msg too small:
> 0x%x\n", size);
> + ret = -EPROTO;
> + goto proxy_error;
> + }
> +
> + /* make sure the CSME-to-GSC proxy header is sane */
> + ret = validate_proxy_header(gsc->proxy.from_csme,
> + GSC_PROXY_ADDRESSING_CSME,
> + GSC_PROXY_ADDRESSING_GSC,
> + GSC_PROXY_BUFFER_SIZE -
> reply_offset);
> + if (ret) {
> + xe_gt_err(gt, "invalid CSME to GSC proxy header!
> %d\n", ret);
> + goto proxy_error;
> + }
> +
> + /* Emit a new header for sending the reply to the GSC */
> + wr_offset = xe_gsc_emit_header(xe, &gsc->proxy.to_gsc, 0,
> + HECI_MEADDRESS_PROXY, 0, size);
> +
> + /* copy the CSME reply and update the total msg size to
> include the GSC header */
> + xe_map_memcpy_to(xe, &gsc->proxy.to_gsc, wr_offset,
> +gsc->proxy.from_csme, size);
> +
> + size += wr_offset;
> + }
> +
> +proxy_error:
> + return ret < 0 ? ret : 0;
> +}
> +
> +static int gsc_proxy_request_handler(struct xe_gsc *gsc) {
> + struct xe_gt *gt = gsc_to_gt(gsc);
> + int slept;
> + int err;
> +
> + if (!gsc->proxy.component_added)
> + return -ENODEV;
> +
> + /* when GSC is loaded, we can queue this before the component is
> bound */
> + for (slept = 0; slept < GSC_PROXY_INIT_TIMEOUT_MS; slept += 100) {
> + if (gsc->proxy.component)
> + break;
> +
> + msleep(100);
> + }
> +
> + mutex_lock(&gsc->proxy.mutex);
> + if (!gsc->proxy.component) {
> + xe_gt_err(gt, "GSC proxy component not bound!\n");
> + err = -EIO;
> + } else {
> + err = proxy_query(gsc);
> + }
> + mutex_unlock(&gsc->proxy.mutex);
> + return err;
> +}
> +
> +static int xe_gsc_proxy_component_bind(struct device *xe_kdev,
> + struct device *mei_kdev, void *data) {
> + struct xe_device *xe = kdev_to_xe(xe_kdev);
> + struct xe_gt *gt = xe->tiles[0].media_gt;
> + struct xe_gsc *gsc = >->uc.gsc;
> +
> + mutex_lock(&gsc->proxy.mutex);
> + gsc->proxy.component = data;
> + gsc->proxy.component->mei_dev = mei_kdev;
> + mutex_unlock(&gsc->proxy.mutex);
> +
> + return 0;
> +}
> +
> +static void xe_gsc_proxy_component_unbind(struct device *xe_kdev,
> + struct device *mei_kdev, void *data)
> {
> + struct xe_device *xe = kdev_to_xe(xe_kdev);
> + struct xe_gt *gt = xe->tiles[0].media_gt;
> + struct xe_gsc *gsc = >->uc.gsc;
> +
> + xe_gsc_wait_for_worker_completion(gsc);
> +
> + mutex_lock(&gsc->proxy.mutex);
> + gsc->proxy.component = NULL;
> + mutex_unlock(&gsc->proxy.mutex);
> +}
> +
> +static const struct component_ops xe_gsc_proxy_component_ops = {
> + .bind = xe_gsc_proxy_component_bind,
> + .unbind = xe_gsc_proxy_component_unbind, };
> +
> +static void proxy_channel_free(struct drm_device *drm, void *arg) {
> + struct xe_gsc *gsc = arg;
> +
> + if (!gsc->proxy.bo)
> + return;
> +
> + if (gsc->proxy.to_csme) {
> + kfree(gsc->proxy.to_csme);
> + gsc->proxy.to_csme = NULL;
> + gsc->proxy.from_csme = NULL;
> + }
> +
> + if (gsc->proxy.bo) {
> + iosys_map_clear(&gsc->proxy.to_gsc);
> + iosys_map_clear(&gsc->proxy.from_gsc);
> + xe_bo_unpin_map_no_vm(gsc->proxy.bo);
> + gsc->proxy.bo = NULL;
> + }
> +}
> +
> +static int proxy_channel_alloc(struct xe_gsc *gsc) {
> + struct xe_gt *gt = gsc_to_gt(gsc);
> + struct xe_tile *tile = gt_to_tile(gt);
> + struct xe_device *xe = gt_to_xe(gt);
> + struct xe_bo *bo;
> + void *csme;
> + int err;
> +
> + csme = kzalloc(GSC_PROXY_CHANNEL_SIZE, GFP_KERNEL);
> + if (!csme)
> + return -ENOMEM;
> +
> + bo = xe_bo_create_pin_map(xe, tile, NULL,
> GSC_PROXY_CHANNEL_SIZE,
> + ttm_bo_type_kernel,
> + XE_BO_CREATE_SYSTEM_BIT |
> + XE_BO_CREATE_GGTT_BIT);
> + if (IS_ERR(bo)) {
> + kfree(csme);
> + return PTR_ERR(bo);
> + }
> +
> + gsc->proxy.bo = bo;
> + gsc->proxy.to_gsc = IOSYS_MAP_INIT_OFFSET(&bo->vmap, 0);
> + gsc->proxy.from_gsc = IOSYS_MAP_INIT_OFFSET(&bo->vmap,
> GSC_PROXY_BUFFER_SIZE);
> + gsc->proxy.to_csme = csme;
> + gsc->proxy.from_csme = csme + GSC_PROXY_BUFFER_SIZE;
> +
> + err = drmm_add_action_or_reset(&xe->drm, proxy_channel_free,
> gsc);
> + if (err)
> + return err;
> +
> + return 0;
> +}
> +
> +/**
> + * xe_gsc_proxy_init() - init objects and MEI component required by GSC
> +proxy
> + * @gsc: the GSC uC
> + *
> + * Return: 0 if the initialization was successful, a negative errno otherwise.
> + */
> +int xe_gsc_proxy_init(struct xe_gsc *gsc) {
> + int err;
> + struct xe_gt *gt = gsc_to_gt(gsc);
> + struct xe_tile *tile = gt_to_tile(gt);
> + struct xe_device *xe = tile_to_xe(tile);
> +
> + mutex_init(&gsc->proxy.mutex);
> +
> + if (!IS_ENABLED(CONFIG_INTEL_MEI_GSC_PROXY)) {
> + xe_gt_info(gt, "can't init GSC proxy due to missing mei
> component\n");
> + return -ENODEV;
> + }
> +
> + /* no multi-tile devices with this feature yet */
> + if (tile->id > 0) {
> + xe_gt_err(gt, "unexpected GSC proxy init on tile %u\n", tile-
> >id);
> + return -EINVAL;
> + }
> +
> + err = proxy_channel_alloc(gsc);
> + if (err)
> + return err;
> +
> + err = component_add_typed(xe->drm.dev,
> &xe_gsc_proxy_component_ops,
> + I915_COMPONENT_GSC_PROXY);
> + if (err < 0) {
> + xe_gt_err(gt, "Failed to add GSC_PROXY component (%pe)\n",
> ERR_PTR(err));
> + return err;
> + }
> +
> + gsc->proxy.component_added = true;
> +
> + /* the component must be removed before unload, so can't use drmm
> for
> +cleanup */
> +
> + return 0;
> +}
> +
> +/**
> + * xe_gsc_proxy_remove() - remove the GSC proxy MEI component
> + * @gsc: the GSC uC
> + */
> +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);
> +
> + 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
> + * @gsc: the GSC uC
> + *
> + * Return: 0 if the proxy are now enabled, a negative errno otherwise.
> + */
> +int xe_gsc_proxy_start(struct xe_gsc *gsc) {
> + int err;
> +
> + /*
> + * 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);
> + if (err)
> + return err;
> +
> + if (!gsc_proxy_init_done(gsc)) {
> + xe_gt_err(gsc_to_gt(gsc), "GSC FW reports proxy init not
> completed\n");
> + return -EIO;
> + }
> +
> + return 0;
> +}
> diff --git a/drivers/gpu/drm/xe/xe_gsc_proxy.h
> b/drivers/gpu/drm/xe/xe_gsc_proxy.h
> new file mode 100644
> index 000000000000..5dc6321efbaf
> --- /dev/null
> +++ b/drivers/gpu/drm/xe/xe_gsc_proxy.h
> @@ -0,0 +1,17 @@
> +/* SPDX-License-Identifier: MIT */
> +/*
> + * Copyright © 2023 Intel Corporation
> + */
> +
> +#ifndef _XE_GSC_PROXY_H_
> +#define _XE_GSC_PROXY_H_
> +
> +#include <linux/types.h>
> +
> +struct xe_gsc;
> +
> +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);
> +
> +#endif
> diff --git a/drivers/gpu/drm/xe/xe_gsc_submit.c
> b/drivers/gpu/drm/xe/xe_gsc_submit.c
> index 8c5381e5913f..9ecc1ead6844 100644
> --- a/drivers/gpu/drm/xe/xe_gsc_submit.c
> +++ b/drivers/gpu/drm/xe/xe_gsc_submit.c
> @@ -5,6 +5,8 @@
>
> #include "xe_gsc_submit.h"
>
> +#include <linux/poison.h>
> +
> #include "abi/gsc_command_header_abi.h"
> #include "xe_bb.h"
> #include "xe_exec_queue.h"
> @@ -68,6 +70,17 @@ u32 xe_gsc_emit_header(struct xe_device *xe, struct
> iosys_map *map, u32 offset,
> return offset + GSC_HDR_SIZE;
> };
>
> +/**
> + * xe_gsc_poison_header - poison the MTL GSC header in memory
> + * @xe: the Xe device
> + * @map: the iosys map to write to
> + * @offset: offset from the start of the map at which the header
> +resides */ void xe_gsc_poison_header(struct xe_device *xe, struct
> +iosys_map *map, u32 offset) {
> + xe_map_memset(xe, map, offset, POISON_FREE, GSC_HDR_SIZE); };
> +
> /**
> * xe_gsc_check_and_update_pending - check the pending bit and update the
> input
> * header with the retry handle from the output header diff --git
> a/drivers/gpu/drm/xe/xe_gsc_submit.h
> b/drivers/gpu/drm/xe/xe_gsc_submit.h
> index 0801da5d446a..1939855031a6 100644
> --- a/drivers/gpu/drm/xe/xe_gsc_submit.h
> +++ b/drivers/gpu/drm/xe/xe_gsc_submit.h
> @@ -14,6 +14,7 @@ struct xe_gsc;
>
> u32 xe_gsc_emit_header(struct xe_device *xe, struct iosys_map *map, u32
> offset,
> u8 heci_client_id, u64 host_session_id, u32 payload_size);
> +void xe_gsc_poison_header(struct xe_device *xe, struct iosys_map *map,
> +u32 offset);
>
> bool xe_gsc_check_and_update_pending(struct xe_device *xe,
> struct iosys_map *in, u32 offset_in, diff --
> git a/drivers/gpu/drm/xe/xe_gsc_types.h
> b/drivers/gpu/drm/xe/xe_gsc_types.h
> index 57fefd66a7ea..805f26be4e9b 100644
> --- a/drivers/gpu/drm/xe/xe_gsc_types.h
> +++ b/drivers/gpu/drm/xe/xe_gsc_types.h
> @@ -6,12 +6,16 @@
> #ifndef _XE_GSC_TYPES_H_
> #define _XE_GSC_TYPES_H_
>
> +#include <linux/iosys-map.h>
> +#include <linux/mutex.h>
> +#include <linux/types.h>
> #include <linux/workqueue.h>
>
> #include "xe_uc_fw_types.h"
>
> struct xe_bo;
> struct xe_exec_queue;
> +struct i915_gsc_proxy_component;
>
> /**
> * struct xe_gsc - GSC
> @@ -34,6 +38,25 @@ struct xe_gsc {
>
> /** @work: delayed load and proxy handling work */
> struct work_struct work;
> +
> + struct {
> + /** @component: struct for communication with mei
> component */
> + struct i915_gsc_proxy_component *component;
> + /** @mutex: protects the component binding and usage */
> + struct mutex mutex;
> + /** @component_added: whether the component has been
> added */
> + bool component_added;
> + /** @bo: object to store message to and from the GSC */
> + struct xe_bo *bo;
> + /** @to_gsc: map of the memory used to send messages to
> the GSC */
> + struct iosys_map to_gsc;
> + /** @from_gsc: map of the memory used to recv messages
> from the GSC */
> + struct iosys_map from_gsc;
> + /** @to_csme: pointer to the memory used to send messages
> to CSME */
> + void *to_csme;
> + /** @from_csme: pointer to the memory used to recv
> messages from CSME */
> + void *from_csme;
> + } proxy;
> };
>
> #endif
> diff --git a/drivers/gpu/drm/xe/xe_gt.c b/drivers/gpu/drm/xe/xe_gt.c index
> 3af2adec1295..b4c021e98606 100644
> --- a/drivers/gpu/drm/xe/xe_gt.c
> +++ b/drivers/gpu/drm/xe/xe_gt.c
> @@ -78,6 +78,19 @@ void xe_gt_sanitize(struct xe_gt *gt)
> gt->uc.guc.submission_state.enabled = false; }
>
> +/**
> + * xe_gt_remove() - Clean up the GT structures before driver removal
> + * @uc: the GT object
> + *
> + * This function should only act on objects/structures that must be
> +cleaned
> + * before the driver removal callback is complete and therefore can't
> +be
> + * deferred to a drmm action.
> + */
> +void xe_gt_remove(struct xe_gt *gt)
> +{
> + xe_uc_remove(>->uc);
> +}
> +
> static void gt_fini(struct drm_device *drm, void *arg) {
> struct xe_gt *gt = arg;
> diff --git a/drivers/gpu/drm/xe/xe_gt.h b/drivers/gpu/drm/xe/xe_gt.h index
> 4486e083f5ef..c1675bd44cf6 100644
> --- a/drivers/gpu/drm/xe/xe_gt.h
> +++ b/drivers/gpu/drm/xe/xe_gt.h
> @@ -41,6 +41,7 @@ int xe_gt_suspend(struct xe_gt *gt); int
> xe_gt_resume(struct xe_gt *gt); void xe_gt_reset_async(struct xe_gt *gt);
> void xe_gt_sanitize(struct xe_gt *gt);
> +void xe_gt_remove(struct xe_gt *gt);
>
> /**
> * xe_gt_any_hw_engine_by_reset_domain - scan the list of engines and
> return the diff --git a/drivers/gpu/drm/xe/xe_uc.c
> b/drivers/gpu/drm/xe/xe_uc.c index 4408ea1751e7..8f37a809525f 100644
> --- a/drivers/gpu/drm/xe/xe_uc.c
> +++ b/drivers/gpu/drm/xe/xe_uc.c
> @@ -7,6 +7,7 @@
>
> #include "xe_device.h"
> #include "xe_gsc.h"
> +#include "xe_gsc_proxy.h"
> #include "xe_gt.h"
> #include "xe_guc.h"
> #include "xe_guc_db_mgr.h"
> @@ -261,3 +262,16 @@ int xe_uc_suspend(struct xe_uc *uc)
>
> return xe_guc_suspend(&uc->guc);
> }
> +
> +/**
> + * xe_uc_remove() - Clean up the UC structures before driver removal
> + * @uc: the UC object
> + *
> + * This function should only act on objects/structures that must be
> +cleaned
> + * before the driver removal callback is complete and therefore can't
> +be
> + * deferred to a drmm action.
> + */
> +void xe_uc_remove(struct xe_uc *uc)
> +{
> + xe_gsc_remove(&uc->gsc);
> +}
> diff --git a/drivers/gpu/drm/xe/xe_uc.h b/drivers/gpu/drm/xe/xe_uc.h index
> 5d5110c0c834..e4d4e3c99f0e 100644
> --- a/drivers/gpu/drm/xe/xe_uc.h
> +++ b/drivers/gpu/drm/xe/xe_uc.h
> @@ -20,5 +20,6 @@ int xe_uc_stop(struct xe_uc *uc); int xe_uc_start(struct
> xe_uc *uc); int xe_uc_suspend(struct xe_uc *uc); int
> xe_uc_sanitize_reset(struct xe_uc *uc);
> +void xe_uc_remove(struct xe_uc *uc);
>
> #endif
> --
> 2.43.0
More information about the Intel-xe
mailing list