// SPDX-License-Identifier: MIT
/*
* Copyright © 2023-2024 Intel Corporation
*/
#include <linux/bitfield.h>
#include <linux/bsearch.h>
#include <drm/drm_managed.h>
#include <drm/drm_print.h>
#include "abi/guc_actions_sriov_abi.h"
#include "abi/guc_communication_mmio_abi.h"
#include "abi/guc_klvs_abi.h"
#include "abi/guc_relay_actions_abi.h"
#include "regs/xe_gt_regs.h"
#include "regs/xe_gtt_defs.h"
#include "xe_assert.h"
#include "xe_device.h"
#include "xe_ggtt.h"
#include "xe_gt_sriov_printk.h"
#include "xe_gt_sriov_vf.h"
#include "xe_gt_sriov_vf_types.h"
#include "xe_guc.h"
#include "xe_guc_hxg_helpers.h"
#include "xe_guc_relay.h"
#include "xe_mmio.h"
#include "xe_sriov.h"
#include "xe_uc_fw.h"
#include "xe_wopcm.h"
#define make_u64_from_u32(hi, lo) ((u64)((u64)(u32)(hi) << 32 | (u32)(lo)))
static int guc_action_vf_reset(struct xe_guc *guc)
{
u32 request[GUC_HXG_REQUEST_MSG_MIN_LEN] = {
FIELD_PREP(GUC_HXG_MSG_0_ORIGIN, GUC_HXG_ORIGIN_HOST) |
FIELD_PREP(GUC_HXG_MSG_0_TYPE, GUC_HXG_TYPE_REQUEST) |
FIELD_PREP(GUC_HXG_REQUEST_MSG_0_ACTION, GUC_ACTION_VF2GUC_VF_RESET),
};
int ret;
ret = xe_guc_mmio_send(guc, request, ARRAY_SIZE(request));
return ret > 0 ? -EPROTO : ret;
}
static int vf_reset_guc_state(struct xe_gt *gt)
{
struct xe_guc *guc = >->uc.guc;
int err;
err = guc_action_vf_reset(guc);
if (unlikely(err))
xe_gt_sriov_err(gt, "Failed to reset GuC state (%pe)\n", ERR_PTR(err));
return err;
}
static int guc_action_match_version(struct xe_guc *guc,
u32 wanted_branch, u32 wanted_major, u32 wanted_minor,
u32 *branch, u32 *major, u32 *minor, u32 *patch)
{
u32 request[VF2GUC_MATCH_VERSION_REQUEST_MSG_LEN] = {
FIELD_PREP(GUC_HXG_MSG_0_ORIGIN, GUC_HXG_ORIGIN_HOST) |
FIELD_PREP(GUC_HXG_MSG_0_TYPE, GUC_HXG_TYPE_REQUEST) |
FIELD_PREP(GUC_HXG_REQUEST_MSG_0_ACTION,
GUC_ACTION_VF2GUC_MATCH_VERSION),
FIELD_PREP(VF2GUC_MATCH_VERSION_REQUEST_MSG_1_BRANCH, wanted_branch) |
FIELD_PREP(VF2GUC_MATCH_VERSION_REQUEST_MSG_1_MAJOR, wanted_major) |
FIELD_PREP(VF2GUC_MATCH_VERSION_REQUEST_MSG_1_MINOR, wanted_minor),
};
u32 response[GUC_MAX_MMIO_MSG_LEN];
int ret;
BUILD_BUG_ON(VF2GUC_MATCH_VERSION_RESPONSE_MSG_LEN > GUC_MAX_MMIO_MSG_LEN);
ret = xe_guc_mmio_send_recv(guc, request, ARRAY_SIZE(request), response);
if (unlikely(ret < 0))
return ret;
if (unlikely(FIELD_GET(VF2GUC_MATCH_VERSION_RESPONSE_MSG_0_MBZ, response[0])))
return -EPROTO;
*branch = FIELD_GET(VF2GUC_MATCH_VERSION_RESPONSE_MSG_1_BRANCH, response[1]);
*major = FIELD_GET(VF2GUC_MATCH_VERSION_RESPONSE_MSG_1_MAJOR, response[1]);
*minor = FIELD_GET(VF2GUC_MATCH_VERSION_RESPONSE_MSG_1_MINOR, response[1]);
*patch = FIELD_GET(VF2GUC_MATCH_VERSION_RESPONSE_MSG_1_PATCH, response[1]);
return 0;
}
static void vf_minimum_guc_version(struct xe_gt *gt, u32 *branch, u32 *major, u32 *minor)
{
struct xe_device *xe = gt_to_xe(gt);
switch (xe->info.platform) {
case XE_TIGERLAKE ... XE_PVC:
/* 1.1 this is current baseline for Xe driver */
*branch = 0;
*major = 1;
*minor = 1;
break;
default:
/* 1.2 has support for the GMD_ID KLV */
*branch = 0;
*major = 1;
*minor = 2;
break;
}
}
static void vf_wanted_guc_version(struct xe_gt *gt, u32 *branch, u32 *major, u32 *minor)
{
/* for now it's the same as minimum */
return vf_minimum_guc_version(gt, branch, major, minor);
}
static int vf_handshake_with_guc(struct xe_gt *gt)
{
struct xe_gt_sriov_vf_guc_version *guc_version = >->sriov.vf.guc_version;
struct xe_guc *guc = >->uc.guc;
u32 wanted_branch, wanted_major, wanted_minor;
u32 branch, major, minor, patch;
int err;
xe_gt_assert(gt, IS_SRIOV_VF(gt_to_xe(gt)));
/* select wanted version - prefer previous (if any) */
if (guc_version->major || guc_version->minor) {
wanted_branch = guc_version->branch;
wanted_major = guc_version->major;
wanted_minor = guc_version->minor;
} else {
vf_wanted_guc_version(gt, &wanted_branch, &wanted_major, &wanted_minor);
xe_gt_assert(gt, wanted_major != GUC_VERSION_MAJOR_ANY);
}
err = guc_action_match_version(guc, wanted_branch, wanted_major, wanted_minor,
&branch, &major, &minor, &patch);
if (unlikely(err))
goto fail;
/* we don't support interface version change */
if ((guc_version->major || guc_version->minor) &&
(guc_version->branch != branch || guc_version->major != major ||
guc_version->minor != minor)) {
xe_gt_sriov_err(gt, "New GuC interface version detected: %u.%u.%u.%u\n",
branch, major, minor, patch);
xe_gt_sriov_info(gt, "Previously used version was: %u.%u.%u.%u\n",
guc_version->branch, guc_version->major,
guc_version->minor, guc_version->patch);
err = -EREMCHG;
goto fail;
}
/* illegal */
if (major > wanted_major) {