1020 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1020 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
// 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) {
 | 
						|
		err = -EPROTO;
 | 
						|
		goto unsupported;
 | 
						|
	}
 | 
						|
 | 
						|
	/* there's no fallback on major version. */
 | 
						|
	if (major != wanted_major) {
 | 
						|
		err = -ENOPKG;
 | 
						|
		goto unsupported;
 | 
						|
	}
 | 
						|
 | 
						|
	/* check against minimum version supported by us */
 | 
						|
	vf_minimum_guc_version(gt, &wanted_branch, &wanted_major, &wanted_minor);
 | 
						|
	xe_gt_assert(gt, major != GUC_VERSION_MAJOR_ANY);
 | 
						|
	if (major < wanted_major || (major == wanted_major && minor < wanted_minor)) {
 | 
						|
		err = -ENOKEY;
 | 
						|
		goto unsupported;
 | 
						|
	}
 | 
						|
 | 
						|
	xe_gt_sriov_dbg(gt, "using GuC interface version %u.%u.%u.%u\n",
 | 
						|
			branch, major, minor, patch);
 | 
						|
 | 
						|
	guc_version->branch = branch;
 | 
						|
	guc_version->major = major;
 | 
						|
	guc_version->minor = minor;
 | 
						|
	guc_version->patch = patch;
 | 
						|
	return 0;
 | 
						|
 | 
						|
unsupported:
 | 
						|
	xe_gt_sriov_err(gt, "Unsupported GuC version %u.%u.%u.%u (%pe)\n",
 | 
						|
			branch, major, minor, patch, ERR_PTR(err));
 | 
						|
fail:
 | 
						|
	xe_gt_sriov_err(gt, "Unable to confirm GuC version %u.%u (%pe)\n",
 | 
						|
			wanted_major, wanted_minor, ERR_PTR(err));
 | 
						|
 | 
						|
	/* try again with *any* just to query which version is supported */
 | 
						|
	if (!guc_action_match_version(guc, GUC_VERSION_BRANCH_ANY,
 | 
						|
				      GUC_VERSION_MAJOR_ANY, GUC_VERSION_MINOR_ANY,
 | 
						|
				      &branch, &major, &minor, &patch))
 | 
						|
		xe_gt_sriov_notice(gt, "GuC reports interface version %u.%u.%u.%u\n",
 | 
						|
				   branch, major, minor, patch);
 | 
						|
	return err;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * xe_gt_sriov_vf_bootstrap - Query and setup GuC ABI interface version.
 | 
						|
 * @gt: the &xe_gt
 | 
						|
 *
 | 
						|
 * This function is for VF use only.
 | 
						|
 * It requires functional `GuC MMIO based communication`_.
 | 
						|
 *
 | 
						|
 * Return: 0 on success or a negative error code on failure.
 | 
						|
 */
 | 
						|
int xe_gt_sriov_vf_bootstrap(struct xe_gt *gt)
 | 
						|
{
 | 
						|
	int err;
 | 
						|
 | 
						|
	err = vf_reset_guc_state(gt);
 | 
						|
	if (unlikely(err))
 | 
						|
		return err;
 | 
						|
 | 
						|
	err = vf_handshake_with_guc(gt);
 | 
						|
	if (unlikely(err))
 | 
						|
		return err;
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int guc_action_query_single_klv(struct xe_guc *guc, u32 key,
 | 
						|
				       u32 *value, u32 value_len)
 | 
						|
{
 | 
						|
	u32 request[VF2GUC_QUERY_SINGLE_KLV_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_QUERY_SINGLE_KLV),
 | 
						|
		FIELD_PREP(VF2GUC_QUERY_SINGLE_KLV_REQUEST_MSG_1_KEY, key),
 | 
						|
	};
 | 
						|
	u32 response[GUC_MAX_MMIO_MSG_LEN];
 | 
						|
	u32 length;
 | 
						|
	int ret;
 | 
						|
 | 
						|
	BUILD_BUG_ON(VF2GUC_QUERY_SINGLE_KLV_RESPONSE_MSG_MAX_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_QUERY_SINGLE_KLV_RESPONSE_MSG_0_MBZ, response[0])))
 | 
						|
		return -EPROTO;
 | 
						|
 | 
						|
	length = FIELD_GET(VF2GUC_QUERY_SINGLE_KLV_RESPONSE_MSG_0_LENGTH, response[0]);
 | 
						|
	if (unlikely(length > value_len))
 | 
						|
		return -EOVERFLOW;
 | 
						|
	if (unlikely(length < value_len))
 | 
						|
		return -ENODATA;
 | 
						|
 | 
						|
	switch (value_len) {
 | 
						|
	default:
 | 
						|
		xe_gt_WARN_ON(guc_to_gt(guc), value_len > 3);
 | 
						|
		fallthrough;
 | 
						|
	case 3:
 | 
						|
		value[2] = FIELD_GET(VF2GUC_QUERY_SINGLE_KLV_RESPONSE_MSG_3_VALUE96, response[3]);
 | 
						|
		fallthrough;
 | 
						|
	case 2:
 | 
						|
		value[1] = FIELD_GET(VF2GUC_QUERY_SINGLE_KLV_RESPONSE_MSG_2_VALUE64, response[2]);
 | 
						|
		fallthrough;
 | 
						|
	case 1:
 | 
						|
		value[0] = FIELD_GET(VF2GUC_QUERY_SINGLE_KLV_RESPONSE_MSG_1_VALUE32, response[1]);
 | 
						|
		fallthrough;
 | 
						|
	case 0:
 | 
						|
		break;
 | 
						|
	}
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int guc_action_query_single_klv32(struct xe_guc *guc, u32 key, u32 *value32)
 | 
						|
{
 | 
						|
	return guc_action_query_single_klv(guc, key, value32, hxg_sizeof(u32));
 | 
						|
}
 | 
						|
 | 
						|
static int guc_action_query_single_klv64(struct xe_guc *guc, u32 key, u64 *value64)
 | 
						|
{
 | 
						|
	u32 value[2];
 | 
						|
	int err;
 | 
						|
 | 
						|
	err = guc_action_query_single_klv(guc, key, value, hxg_sizeof(value));
 | 
						|
	if (unlikely(err))
 | 
						|
		return err;
 | 
						|
 | 
						|
	*value64 = make_u64_from_u32(value[1], value[0]);
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static bool has_gmdid(struct xe_device *xe)
 | 
						|
{
 | 
						|
	return GRAPHICS_VERx100(xe) >= 1270;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * xe_gt_sriov_vf_gmdid - Query GMDID over MMIO.
 | 
						|
 * @gt: the &xe_gt
 | 
						|
 *
 | 
						|
 * This function is for VF use only.
 | 
						|
 *
 | 
						|
 * Return: value of GMDID KLV on success or 0 on failure.
 | 
						|
 */
 | 
						|
u32 xe_gt_sriov_vf_gmdid(struct xe_gt *gt)
 | 
						|
{
 | 
						|
	const char *type = xe_gt_is_media_type(gt) ? "media" : "graphics";
 | 
						|
	struct xe_guc *guc = >->uc.guc;
 | 
						|
	u32 value;
 | 
						|
	int err;
 | 
						|
 | 
						|
	xe_gt_assert(gt, IS_SRIOV_VF(gt_to_xe(gt)));
 | 
						|
	xe_gt_assert(gt, !GRAPHICS_VERx100(gt_to_xe(gt)) || has_gmdid(gt_to_xe(gt)));
 | 
						|
	xe_gt_assert(gt, gt->sriov.vf.guc_version.major > 1 || gt->sriov.vf.guc_version.minor >= 2);
 | 
						|
 | 
						|
	err = guc_action_query_single_klv32(guc, GUC_KLV_GLOBAL_CFG_GMD_ID_KEY, &value);
 | 
						|
	if (unlikely(err)) {
 | 
						|
		xe_gt_sriov_err(gt, "Failed to obtain %s GMDID (%pe)\n",
 | 
						|
				type, ERR_PTR(err));
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
 | 
						|
	xe_gt_sriov_dbg(gt, "%s GMDID = %#x\n", type, value);
 | 
						|
	return value;
 | 
						|
}
 | 
						|
 | 
						|
static int vf_get_ggtt_info(struct xe_gt *gt)
 | 
						|
{
 | 
						|
	struct xe_gt_sriov_vf_selfconfig *config = >->sriov.vf.self_config;
 | 
						|
	struct xe_guc *guc = >->uc.guc;
 | 
						|
	u64 start, size;
 | 
						|
	int err;
 | 
						|
 | 
						|
	xe_gt_assert(gt, IS_SRIOV_VF(gt_to_xe(gt)));
 | 
						|
 | 
						|
	err = guc_action_query_single_klv64(guc, GUC_KLV_VF_CFG_GGTT_START_KEY, &start);
 | 
						|
	if (unlikely(err))
 | 
						|
		return err;
 | 
						|
 | 
						|
	err = guc_action_query_single_klv64(guc, GUC_KLV_VF_CFG_GGTT_SIZE_KEY, &size);
 | 
						|
	if (unlikely(err))
 | 
						|
		return err;
 | 
						|
 | 
						|
	if (config->ggtt_size && config->ggtt_size != size) {
 | 
						|
		xe_gt_sriov_err(gt, "Unexpected GGTT reassignment: %lluK != %lluK\n",
 | 
						|
				size / SZ_1K, config->ggtt_size / SZ_1K);
 | 
						|
		return -EREMCHG;
 | 
						|
	}
 | 
						|
 | 
						|
	xe_gt_sriov_dbg_verbose(gt, "GGTT %#llx-%#llx = %lluK\n",
 | 
						|
				start, start + size - 1, size / SZ_1K);
 | 
						|
 | 
						|
	config->ggtt_base = start;
 | 
						|
	config->ggtt_size = size;
 | 
						|
 | 
						|
	return config->ggtt_size ? 0 : -ENODATA;
 | 
						|
}
 | 
						|
 | 
						|
static int vf_get_lmem_info(struct xe_gt *gt)
 | 
						|
{
 | 
						|
	struct xe_gt_sriov_vf_selfconfig *config = >->sriov.vf.self_config;
 | 
						|
	struct xe_guc *guc = >->uc.guc;
 | 
						|
	char size_str[10];
 | 
						|
	u64 size;
 | 
						|
	int err;
 | 
						|
 | 
						|
	xe_gt_assert(gt, IS_SRIOV_VF(gt_to_xe(gt)));
 | 
						|
 | 
						|
	err = guc_action_query_single_klv64(guc, GUC_KLV_VF_CFG_LMEM_SIZE_KEY, &size);
 | 
						|
	if (unlikely(err))
 | 
						|
		return err;
 | 
						|
 | 
						|
	if (config->lmem_size && config->lmem_size != size) {
 | 
						|
		xe_gt_sriov_err(gt, "Unexpected LMEM reassignment: %lluM != %lluM\n",
 | 
						|
				size / SZ_1M, config->lmem_size / SZ_1M);
 | 
						|
		return -EREMCHG;
 | 
						|
	}
 | 
						|
 | 
						|
	string_get_size(size, 1, STRING_UNITS_2, size_str, sizeof(size_str));
 | 
						|
	xe_gt_sriov_dbg_verbose(gt, "LMEM %lluM %s\n", size / SZ_1M, size_str);
 | 
						|
 | 
						|
	config->lmem_size = size;
 | 
						|
 | 
						|
	return config->lmem_size ? 0 : -ENODATA;
 | 
						|
}
 | 
						|
 | 
						|
static int vf_get_submission_cfg(struct xe_gt *gt)
 | 
						|
{
 | 
						|
	struct xe_gt_sriov_vf_selfconfig *config = >->sriov.vf.self_config;
 | 
						|
	struct xe_guc *guc = >->uc.guc;
 | 
						|
	u32 num_ctxs, num_dbs;
 | 
						|
	int err;
 | 
						|
 | 
						|
	xe_gt_assert(gt, IS_SRIOV_VF(gt_to_xe(gt)));
 | 
						|
 | 
						|
	err = guc_action_query_single_klv32(guc, GUC_KLV_VF_CFG_NUM_CONTEXTS_KEY, &num_ctxs);
 | 
						|
	if (unlikely(err))
 | 
						|
		return err;
 | 
						|
 | 
						|
	err = guc_action_query_single_klv32(guc, GUC_KLV_VF_CFG_NUM_DOORBELLS_KEY, &num_dbs);
 | 
						|
	if (unlikely(err))
 | 
						|
		return err;
 | 
						|
 | 
						|
	if (config->num_ctxs && config->num_ctxs != num_ctxs) {
 | 
						|
		xe_gt_sriov_err(gt, "Unexpected CTXs reassignment: %u != %u\n",
 | 
						|
				num_ctxs, config->num_ctxs);
 | 
						|
		return -EREMCHG;
 | 
						|
	}
 | 
						|
	if (config->num_dbs && config->num_dbs != num_dbs) {
 | 
						|
		xe_gt_sriov_err(gt, "Unexpected DBs reassignment: %u != %u\n",
 | 
						|
				num_dbs, config->num_dbs);
 | 
						|
		return -EREMCHG;
 | 
						|
	}
 | 
						|
 | 
						|
	xe_gt_sriov_dbg_verbose(gt, "CTXs %u DBs %u\n", num_ctxs, num_dbs);
 | 
						|
 | 
						|
	config->num_ctxs = num_ctxs;
 | 
						|
	config->num_dbs = num_dbs;
 | 
						|
 | 
						|
	return config->num_ctxs ? 0 : -ENODATA;
 | 
						|
}
 | 
						|
 | 
						|
static void vf_cache_gmdid(struct xe_gt *gt)
 | 
						|
{
 | 
						|
	xe_gt_assert(gt, has_gmdid(gt_to_xe(gt)));
 | 
						|
	xe_gt_assert(gt, IS_SRIOV_VF(gt_to_xe(gt)));
 | 
						|
 | 
						|
	gt->sriov.vf.runtime.gmdid = xe_gt_sriov_vf_gmdid(gt);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * xe_gt_sriov_vf_query_config - Query SR-IOV config data over MMIO.
 | 
						|
 * @gt: the &xe_gt
 | 
						|
 *
 | 
						|
 * This function is for VF use only.
 | 
						|
 *
 | 
						|
 * Return: 0 on success or a negative error code on failure.
 | 
						|
 */
 | 
						|
int xe_gt_sriov_vf_query_config(struct xe_gt *gt)
 | 
						|
{
 | 
						|
	struct xe_device *xe = gt_to_xe(gt);
 | 
						|
	int err;
 | 
						|
 | 
						|
	err = vf_get_ggtt_info(gt);
 | 
						|
	if (unlikely(err))
 | 
						|
		return err;
 | 
						|
 | 
						|
	if (IS_DGFX(xe) && !xe_gt_is_media_type(gt)) {
 | 
						|
		err = vf_get_lmem_info(gt);
 | 
						|
		if (unlikely(err))
 | 
						|
			return err;
 | 
						|
	}
 | 
						|
 | 
						|
	err = vf_get_submission_cfg(gt);
 | 
						|
	if (unlikely(err))
 | 
						|
		return err;
 | 
						|
 | 
						|
	if (has_gmdid(xe))
 | 
						|
		vf_cache_gmdid(gt);
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * xe_gt_sriov_vf_guc_ids - VF GuC context IDs configuration.
 | 
						|
 * @gt: the &xe_gt
 | 
						|
 *
 | 
						|
 * This function is for VF use only.
 | 
						|
 *
 | 
						|
 * Return: number of GuC context IDs assigned to VF.
 | 
						|
 */
 | 
						|
u16 xe_gt_sriov_vf_guc_ids(struct xe_gt *gt)
 | 
						|
{
 | 
						|
	xe_gt_assert(gt, IS_SRIOV_VF(gt_to_xe(gt)));
 | 
						|
	xe_gt_assert(gt, gt->sriov.vf.guc_version.major);
 | 
						|
	xe_gt_assert(gt, gt->sriov.vf.self_config.num_ctxs);
 | 
						|
 | 
						|
	return gt->sriov.vf.self_config.num_ctxs;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * xe_gt_sriov_vf_lmem - VF LMEM configuration.
 | 
						|
 * @gt: the &xe_gt
 | 
						|
 *
 | 
						|
 * This function is for VF use only.
 | 
						|
 *
 | 
						|
 * Return: size of the LMEM assigned to VF.
 | 
						|
 */
 | 
						|
u64 xe_gt_sriov_vf_lmem(struct xe_gt *gt)
 | 
						|
{
 | 
						|
	xe_gt_assert(gt, IS_SRIOV_VF(gt_to_xe(gt)));
 | 
						|
	xe_gt_assert(gt, gt->sriov.vf.guc_version.major);
 | 
						|
	xe_gt_assert(gt, gt->sriov.vf.self_config.lmem_size);
 | 
						|
 | 
						|
	return gt->sriov.vf.self_config.lmem_size;
 | 
						|
}
 | 
						|
 | 
						|
static struct xe_ggtt_node *
 | 
						|
vf_balloon_ggtt_node(struct xe_ggtt *ggtt, u64 start, u64 end)
 | 
						|
{
 | 
						|
	struct xe_ggtt_node *node;
 | 
						|
	int err;
 | 
						|
 | 
						|
	node = xe_ggtt_node_init(ggtt);
 | 
						|
	if (IS_ERR(node))
 | 
						|
		return node;
 | 
						|
 | 
						|
	err = xe_ggtt_node_insert_balloon(node, start, end);
 | 
						|
	if (err) {
 | 
						|
		xe_ggtt_node_fini(node);
 | 
						|
		return ERR_PTR(err);
 | 
						|
	}
 | 
						|
 | 
						|
	return node;
 | 
						|
}
 | 
						|
 | 
						|
static int vf_balloon_ggtt(struct xe_gt *gt)
 | 
						|
{
 | 
						|
	struct xe_gt_sriov_vf_selfconfig *config = >->sriov.vf.self_config;
 | 
						|
	struct xe_tile *tile = gt_to_tile(gt);
 | 
						|
	struct xe_ggtt *ggtt = tile->mem.ggtt;
 | 
						|
	struct xe_device *xe = gt_to_xe(gt);
 | 
						|
	u64 start, end;
 | 
						|
 | 
						|
	xe_gt_assert(gt, IS_SRIOV_VF(xe));
 | 
						|
	xe_gt_assert(gt, !xe_gt_is_media_type(gt));
 | 
						|
 | 
						|
	if (!config->ggtt_size)
 | 
						|
		return -ENODATA;
 | 
						|
 | 
						|
	/*
 | 
						|
	 * VF can only use part of the GGTT as allocated by the PF:
 | 
						|
	 *
 | 
						|
	 *      WOPCM                                  GUC_GGTT_TOP
 | 
						|
	 *      |<------------ Total GGTT size ------------------>|
 | 
						|
	 *
 | 
						|
	 *           VF GGTT base -->|<- size ->|
 | 
						|
	 *
 | 
						|
	 *      +--------------------+----------+-----------------+
 | 
						|
	 *      |////////////////////|   block  |\\\\\\\\\\\\\\\\\|
 | 
						|
	 *      +--------------------+----------+-----------------+
 | 
						|
	 *
 | 
						|
	 *      |<--- balloon[0] --->|<-- VF -->|<-- balloon[1] ->|
 | 
						|
	 */
 | 
						|
 | 
						|
	start = xe_wopcm_size(xe);
 | 
						|
	end = config->ggtt_base;
 | 
						|
	if (end != start) {
 | 
						|
		tile->sriov.vf.ggtt_balloon[0] = vf_balloon_ggtt_node(ggtt, start, end);
 | 
						|
		if (IS_ERR(tile->sriov.vf.ggtt_balloon[0]))
 | 
						|
			return PTR_ERR(tile->sriov.vf.ggtt_balloon[0]);
 | 
						|
	}
 | 
						|
 | 
						|
	start = config->ggtt_base + config->ggtt_size;
 | 
						|
	end = GUC_GGTT_TOP;
 | 
						|
	if (end != start) {
 | 
						|
		tile->sriov.vf.ggtt_balloon[1] = vf_balloon_ggtt_node(ggtt, start, end);
 | 
						|
		if (IS_ERR(tile->sriov.vf.ggtt_balloon[1])) {
 | 
						|
			xe_ggtt_node_remove_balloon(tile->sriov.vf.ggtt_balloon[0]);
 | 
						|
			return PTR_ERR(tile->sriov.vf.ggtt_balloon[1]);
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static void deballoon_ggtt(struct drm_device *drm, void *arg)
 | 
						|
{
 | 
						|
	struct xe_tile *tile = arg;
 | 
						|
 | 
						|
	xe_tile_assert(tile, IS_SRIOV_VF(tile_to_xe(tile)));
 | 
						|
	xe_ggtt_node_remove_balloon(tile->sriov.vf.ggtt_balloon[1]);
 | 
						|
	xe_ggtt_node_remove_balloon(tile->sriov.vf.ggtt_balloon[0]);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * xe_gt_sriov_vf_prepare_ggtt - Prepare a VF's GGTT configuration.
 | 
						|
 * @gt: the &xe_gt
 | 
						|
 *
 | 
						|
 * This function is for VF use only.
 | 
						|
 *
 | 
						|
 * Return: 0 on success or a negative error code on failure.
 | 
						|
 */
 | 
						|
int xe_gt_sriov_vf_prepare_ggtt(struct xe_gt *gt)
 | 
						|
{
 | 
						|
	struct xe_tile *tile = gt_to_tile(gt);
 | 
						|
	struct xe_device *xe = tile_to_xe(tile);
 | 
						|
	int err;
 | 
						|
 | 
						|
	if (xe_gt_is_media_type(gt))
 | 
						|
		return 0;
 | 
						|
 | 
						|
	err = vf_balloon_ggtt(gt);
 | 
						|
	if (err)
 | 
						|
		return err;
 | 
						|
 | 
						|
	return drmm_add_action_or_reset(&xe->drm, deballoon_ggtt, tile);
 | 
						|
}
 | 
						|
 | 
						|
static int relay_action_handshake(struct xe_gt *gt, u32 *major, u32 *minor)
 | 
						|
{
 | 
						|
	u32 request[VF2PF_HANDSHAKE_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_RELAY_ACTION_VF2PF_HANDSHAKE),
 | 
						|
		FIELD_PREP(VF2PF_HANDSHAKE_REQUEST_MSG_1_MAJOR, *major) |
 | 
						|
		FIELD_PREP(VF2PF_HANDSHAKE_REQUEST_MSG_1_MINOR, *minor),
 | 
						|
	};
 | 
						|
	u32 response[VF2PF_HANDSHAKE_RESPONSE_MSG_LEN];
 | 
						|
	int ret;
 | 
						|
 | 
						|
	xe_gt_assert(gt, IS_SRIOV_VF(gt_to_xe(gt)));
 | 
						|
 | 
						|
	ret = xe_guc_relay_send_to_pf(>->uc.guc.relay,
 | 
						|
				      request, ARRAY_SIZE(request),
 | 
						|
				      response, ARRAY_SIZE(response));
 | 
						|
	if (unlikely(ret < 0))
 | 
						|
		return ret;
 | 
						|
 | 
						|
	if (unlikely(ret != VF2PF_HANDSHAKE_RESPONSE_MSG_LEN))
 | 
						|
		return -EPROTO;
 | 
						|
 | 
						|
	if (unlikely(FIELD_GET(VF2PF_HANDSHAKE_RESPONSE_MSG_0_MBZ, response[0])))
 | 
						|
		return -EPROTO;
 | 
						|
 | 
						|
	*major = FIELD_GET(VF2PF_HANDSHAKE_RESPONSE_MSG_1_MAJOR, response[1]);
 | 
						|
	*minor = FIELD_GET(VF2PF_HANDSHAKE_RESPONSE_MSG_1_MINOR, response[1]);
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static void vf_connect_pf(struct xe_gt *gt, u16 major, u16 minor)
 | 
						|
{
 | 
						|
	xe_gt_assert(gt, IS_SRIOV_VF(gt_to_xe(gt)));
 | 
						|
 | 
						|
	gt->sriov.vf.pf_version.major = major;
 | 
						|
	gt->sriov.vf.pf_version.minor = minor;
 | 
						|
}
 | 
						|
 | 
						|
static void vf_disconnect_pf(struct xe_gt *gt)
 | 
						|
{
 | 
						|
	vf_connect_pf(gt, 0, 0);
 | 
						|
}
 | 
						|
 | 
						|
static int vf_handshake_with_pf(struct xe_gt *gt)
 | 
						|
{
 | 
						|
	u32 major_wanted = GUC_RELAY_VERSION_LATEST_MAJOR;
 | 
						|
	u32 minor_wanted = GUC_RELAY_VERSION_LATEST_MINOR;
 | 
						|
	u32 major = major_wanted, minor = minor_wanted;
 | 
						|
	int err;
 | 
						|
 | 
						|
	err = relay_action_handshake(gt, &major, &minor);
 | 
						|
	if (unlikely(err))
 | 
						|
		goto failed;
 | 
						|
 | 
						|
	if (!major && !minor) {
 | 
						|
		err = -ENODATA;
 | 
						|
		goto failed;
 | 
						|
	}
 | 
						|
 | 
						|
	xe_gt_sriov_dbg(gt, "using VF/PF ABI %u.%u\n", major, minor);
 | 
						|
	vf_connect_pf(gt, major, minor);
 | 
						|
	return 0;
 | 
						|
 | 
						|
failed:
 | 
						|
	xe_gt_sriov_err(gt, "Unable to confirm VF/PF ABI version %u.%u (%pe)\n",
 | 
						|
			major, minor, ERR_PTR(err));
 | 
						|
	vf_disconnect_pf(gt);
 | 
						|
	return err;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * xe_gt_sriov_vf_connect - Establish connection with the PF driver.
 | 
						|
 * @gt: the &xe_gt
 | 
						|
 *
 | 
						|
 * This function is for VF use only.
 | 
						|
 *
 | 
						|
 * Return: 0 on success or a negative error code on failure.
 | 
						|
 */
 | 
						|
int xe_gt_sriov_vf_connect(struct xe_gt *gt)
 | 
						|
{
 | 
						|
	int err;
 | 
						|
 | 
						|
	err = vf_handshake_with_pf(gt);
 | 
						|
	if (unlikely(err))
 | 
						|
		goto failed;
 | 
						|
 | 
						|
	return 0;
 | 
						|
 | 
						|
failed:
 | 
						|
	xe_gt_sriov_err(gt, "Failed to get version info (%pe)\n", ERR_PTR(err));
 | 
						|
	return err;
 | 
						|
}
 | 
						|
 | 
						|
static bool vf_is_negotiated(struct xe_gt *gt, u16 major, u16 minor)
 | 
						|
{
 | 
						|
	xe_gt_assert(gt, IS_SRIOV_VF(gt_to_xe(gt)));
 | 
						|
 | 
						|
	return major == gt->sriov.vf.pf_version.major &&
 | 
						|
	       minor <= gt->sriov.vf.pf_version.minor;
 | 
						|
}
 | 
						|
 | 
						|
static int vf_prepare_runtime_info(struct xe_gt *gt, unsigned int num_regs)
 | 
						|
{
 | 
						|
	struct vf_runtime_reg *regs = gt->sriov.vf.runtime.regs;
 | 
						|
	unsigned int regs_size = round_up(num_regs, 4);
 | 
						|
	struct xe_device *xe = gt_to_xe(gt);
 | 
						|
 | 
						|
	xe_gt_assert(gt, IS_SRIOV_VF(xe));
 | 
						|
 | 
						|
	if (regs) {
 | 
						|
		if (num_regs <= gt->sriov.vf.runtime.regs_size) {
 | 
						|
			memset(regs, 0, num_regs * sizeof(*regs));
 | 
						|
			gt->sriov.vf.runtime.num_regs = num_regs;
 | 
						|
			return 0;
 | 
						|
		}
 | 
						|
 | 
						|
		drmm_kfree(&xe->drm, regs);
 | 
						|
		gt->sriov.vf.runtime.regs = NULL;
 | 
						|
		gt->sriov.vf.runtime.num_regs = 0;
 | 
						|
		gt->sriov.vf.runtime.regs_size = 0;
 | 
						|
	}
 | 
						|
 | 
						|
	regs = drmm_kcalloc(&xe->drm, regs_size, sizeof(*regs), GFP_KERNEL);
 | 
						|
	if (unlikely(!regs))
 | 
						|
		return -ENOMEM;
 | 
						|
 | 
						|
	gt->sriov.vf.runtime.regs = regs;
 | 
						|
	gt->sriov.vf.runtime.num_regs = num_regs;
 | 
						|
	gt->sriov.vf.runtime.regs_size = regs_size;
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int vf_query_runtime_info(struct xe_gt *gt)
 | 
						|
{
 | 
						|
	u32 request[VF2PF_QUERY_RUNTIME_REQUEST_MSG_LEN];
 | 
						|
	u32 response[VF2PF_QUERY_RUNTIME_RESPONSE_MSG_MIN_LEN + 32]; /* up to 16 regs */
 | 
						|
	u32 limit = (ARRAY_SIZE(response) - VF2PF_QUERY_RUNTIME_RESPONSE_MSG_MIN_LEN) / 2;
 | 
						|
	u32 count, remaining, num, i;
 | 
						|
	u32 start = 0;
 | 
						|
	int ret;
 | 
						|
 | 
						|
	xe_gt_assert(gt, IS_SRIOV_VF(gt_to_xe(gt)));
 | 
						|
	xe_gt_assert(gt, limit);
 | 
						|
 | 
						|
	/* this is part of the 1.0 PF/VF ABI */
 | 
						|
	if (!vf_is_negotiated(gt, 1, 0))
 | 
						|
		return -ENOPKG;
 | 
						|
 | 
						|
	request[0] = 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_RELAY_ACTION_VF2PF_QUERY_RUNTIME) |
 | 
						|
		     FIELD_PREP(VF2PF_QUERY_RUNTIME_REQUEST_MSG_0_LIMIT, limit);
 | 
						|
 | 
						|
repeat:
 | 
						|
	request[1] = FIELD_PREP(VF2PF_QUERY_RUNTIME_REQUEST_MSG_1_START, start);
 | 
						|
	ret = xe_guc_relay_send_to_pf(>->uc.guc.relay,
 | 
						|
				      request, ARRAY_SIZE(request),
 | 
						|
				      response, ARRAY_SIZE(response));
 | 
						|
	if (unlikely(ret < 0))
 | 
						|
		goto failed;
 | 
						|
 | 
						|
	if (unlikely(ret < VF2PF_QUERY_RUNTIME_RESPONSE_MSG_MIN_LEN)) {
 | 
						|
		ret = -EPROTO;
 | 
						|
		goto failed;
 | 
						|
	}
 | 
						|
	if (unlikely((ret - VF2PF_QUERY_RUNTIME_RESPONSE_MSG_MIN_LEN) % 2)) {
 | 
						|
		ret = -EPROTO;
 | 
						|
		goto failed;
 | 
						|
	}
 | 
						|
 | 
						|
	num = (ret - VF2PF_QUERY_RUNTIME_RESPONSE_MSG_MIN_LEN) / 2;
 | 
						|
	count = FIELD_GET(VF2PF_QUERY_RUNTIME_RESPONSE_MSG_0_COUNT, response[0]);
 | 
						|
	remaining = FIELD_GET(VF2PF_QUERY_RUNTIME_RESPONSE_MSG_1_REMAINING, response[1]);
 | 
						|
 | 
						|
	xe_gt_sriov_dbg_verbose(gt, "count=%u num=%u ret=%d start=%u remaining=%u\n",
 | 
						|
				count, num, ret, start, remaining);
 | 
						|
 | 
						|
	if (unlikely(count != num)) {
 | 
						|
		ret = -EPROTO;
 | 
						|
		goto failed;
 | 
						|
	}
 | 
						|
 | 
						|
	if (start == 0) {
 | 
						|
		ret = vf_prepare_runtime_info(gt, num + remaining);
 | 
						|
		if (unlikely(ret < 0))
 | 
						|
			goto failed;
 | 
						|
	} else if (unlikely(start + num > gt->sriov.vf.runtime.num_regs)) {
 | 
						|
		ret = -EPROTO;
 | 
						|
		goto failed;
 | 
						|
	}
 | 
						|
 | 
						|
	for (i = 0; i < num; ++i) {
 | 
						|
		struct vf_runtime_reg *reg = >->sriov.vf.runtime.regs[start + i];
 | 
						|
 | 
						|
		reg->offset = response[VF2PF_QUERY_RUNTIME_RESPONSE_MSG_MIN_LEN + 2 * i];
 | 
						|
		reg->value = response[VF2PF_QUERY_RUNTIME_RESPONSE_MSG_MIN_LEN + 2 * i + 1];
 | 
						|
	}
 | 
						|
 | 
						|
	if (remaining) {
 | 
						|
		start += num;
 | 
						|
		goto repeat;
 | 
						|
	}
 | 
						|
 | 
						|
	return 0;
 | 
						|
 | 
						|
failed:
 | 
						|
	vf_prepare_runtime_info(gt, 0);
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
static void vf_show_runtime_info(struct xe_gt *gt)
 | 
						|
{
 | 
						|
	struct vf_runtime_reg *vf_regs = gt->sriov.vf.runtime.regs;
 | 
						|
	unsigned int size = gt->sriov.vf.runtime.num_regs;
 | 
						|
 | 
						|
	xe_gt_assert(gt, IS_SRIOV_VF(gt_to_xe(gt)));
 | 
						|
 | 
						|
	for (; size--; vf_regs++)
 | 
						|
		xe_gt_sriov_dbg(gt, "runtime(%#x) = %#x\n",
 | 
						|
				vf_regs->offset, vf_regs->value);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * xe_gt_sriov_vf_query_runtime - Query SR-IOV runtime data.
 | 
						|
 * @gt: the &xe_gt
 | 
						|
 *
 | 
						|
 * This function is for VF use only.
 | 
						|
 *
 | 
						|
 * Return: 0 on success or a negative error code on failure.
 | 
						|
 */
 | 
						|
int xe_gt_sriov_vf_query_runtime(struct xe_gt *gt)
 | 
						|
{
 | 
						|
	int err;
 | 
						|
 | 
						|
	err = vf_query_runtime_info(gt);
 | 
						|
	if (unlikely(err))
 | 
						|
		goto failed;
 | 
						|
 | 
						|
	if (IS_ENABLED(CONFIG_DRM_XE_DEBUG))
 | 
						|
		vf_show_runtime_info(gt);
 | 
						|
 | 
						|
	return 0;
 | 
						|
 | 
						|
failed:
 | 
						|
	xe_gt_sriov_err(gt, "Failed to get runtime info (%pe)\n",
 | 
						|
			ERR_PTR(err));
 | 
						|
	return err;
 | 
						|
}
 | 
						|
 | 
						|
static int vf_runtime_reg_cmp(const void *a, const void *b)
 | 
						|
{
 | 
						|
	const struct vf_runtime_reg *ra = a;
 | 
						|
	const struct vf_runtime_reg *rb = b;
 | 
						|
 | 
						|
	return (int)ra->offset - (int)rb->offset;
 | 
						|
}
 | 
						|
 | 
						|
static struct vf_runtime_reg *vf_lookup_reg(struct xe_gt *gt, u32 addr)
 | 
						|
{
 | 
						|
	struct xe_gt_sriov_vf_runtime *runtime = >->sriov.vf.runtime;
 | 
						|
	struct vf_runtime_reg key = { .offset = addr };
 | 
						|
 | 
						|
	xe_gt_assert(gt, IS_SRIOV_VF(gt_to_xe(gt)));
 | 
						|
 | 
						|
	return bsearch(&key, runtime->regs, runtime->num_regs, sizeof(key),
 | 
						|
		       vf_runtime_reg_cmp);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * xe_gt_sriov_vf_read32 - Get a register value from the runtime data.
 | 
						|
 * @gt: the &xe_gt
 | 
						|
 * @reg: the register to read
 | 
						|
 *
 | 
						|
 * This function is for VF use only.
 | 
						|
 * This function shall be called after VF has connected to PF.
 | 
						|
 * This function is dedicated for registers that VFs can't read directly.
 | 
						|
 *
 | 
						|
 * Return: register value obtained from the PF or 0 if not found.
 | 
						|
 */
 | 
						|
u32 xe_gt_sriov_vf_read32(struct xe_gt *gt, struct xe_reg reg)
 | 
						|
{
 | 
						|
	u32 addr = xe_mmio_adjusted_addr(gt, reg.addr);
 | 
						|
	struct vf_runtime_reg *rr;
 | 
						|
 | 
						|
	xe_gt_assert(gt, IS_SRIOV_VF(gt_to_xe(gt)));
 | 
						|
	xe_gt_assert(gt, gt->sriov.vf.pf_version.major);
 | 
						|
	xe_gt_assert(gt, !reg.vf);
 | 
						|
 | 
						|
	if (reg.addr == GMD_ID.addr) {
 | 
						|
		xe_gt_sriov_dbg_verbose(gt, "gmdid(%#x) = %#x\n",
 | 
						|
					addr, gt->sriov.vf.runtime.gmdid);
 | 
						|
		return gt->sriov.vf.runtime.gmdid;
 | 
						|
	}
 | 
						|
 | 
						|
	rr = vf_lookup_reg(gt, addr);
 | 
						|
	if (!rr) {
 | 
						|
		xe_gt_WARN(gt, IS_ENABLED(CONFIG_DRM_XE_DEBUG),
 | 
						|
			   "VF is trying to read an inaccessible register %#x+%#x\n",
 | 
						|
			   reg.addr, addr - reg.addr);
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
 | 
						|
	xe_gt_sriov_dbg_verbose(gt, "runtime[%#x] = %#x\n", addr, rr->value);
 | 
						|
	return rr->value;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * xe_gt_sriov_vf_write32 - Handle a write to an inaccessible register.
 | 
						|
 * @gt: the &xe_gt
 | 
						|
 * @reg: the register to write
 | 
						|
 * @val: value to write
 | 
						|
 *
 | 
						|
 * This function is for VF use only.
 | 
						|
 * Currently it will trigger a WARN if running on debug build.
 | 
						|
 */
 | 
						|
void xe_gt_sriov_vf_write32(struct xe_gt *gt, struct xe_reg reg, u32 val)
 | 
						|
{
 | 
						|
	u32 addr = xe_mmio_adjusted_addr(gt, reg.addr);
 | 
						|
 | 
						|
	xe_gt_assert(gt, IS_SRIOV_VF(gt_to_xe(gt)));
 | 
						|
	xe_gt_assert(gt, !reg.vf);
 | 
						|
 | 
						|
	/*
 | 
						|
	 * In the future, we may want to handle selected writes to inaccessible
 | 
						|
	 * registers in some custom way, but for now let's just log a warning
 | 
						|
	 * about such attempt, as likely we might be doing something wrong.
 | 
						|
	 */
 | 
						|
	xe_gt_WARN(gt, IS_ENABLED(CONFIG_DRM_XE_DEBUG),
 | 
						|
		   "VF is trying to write %#x to an inaccessible register %#x+%#x\n",
 | 
						|
		   val, reg.addr, addr - reg.addr);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * xe_gt_sriov_vf_print_config - Print VF self config.
 | 
						|
 * @gt: the &xe_gt
 | 
						|
 * @p: the &drm_printer
 | 
						|
 *
 | 
						|
 * This function is for VF use only.
 | 
						|
 */
 | 
						|
void xe_gt_sriov_vf_print_config(struct xe_gt *gt, struct drm_printer *p)
 | 
						|
{
 | 
						|
	struct xe_gt_sriov_vf_selfconfig *config = >->sriov.vf.self_config;
 | 
						|
	struct xe_device *xe = gt_to_xe(gt);
 | 
						|
	char buf[10];
 | 
						|
 | 
						|
	xe_gt_assert(gt, IS_SRIOV_VF(gt_to_xe(gt)));
 | 
						|
 | 
						|
	drm_printf(p, "GGTT range:\t%#llx-%#llx\n",
 | 
						|
		   config->ggtt_base,
 | 
						|
		   config->ggtt_base + config->ggtt_size - 1);
 | 
						|
 | 
						|
	string_get_size(config->ggtt_size, 1, STRING_UNITS_2, buf, sizeof(buf));
 | 
						|
	drm_printf(p, "GGTT size:\t%llu (%s)\n", config->ggtt_size, buf);
 | 
						|
 | 
						|
	if (IS_DGFX(xe) && !xe_gt_is_media_type(gt)) {
 | 
						|
		string_get_size(config->lmem_size, 1, STRING_UNITS_2, buf, sizeof(buf));
 | 
						|
		drm_printf(p, "LMEM size:\t%llu (%s)\n", config->lmem_size, buf);
 | 
						|
	}
 | 
						|
 | 
						|
	drm_printf(p, "GuC contexts:\t%u\n", config->num_ctxs);
 | 
						|
	drm_printf(p, "GuC doorbells:\t%u\n", config->num_dbs);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * xe_gt_sriov_vf_print_runtime - Print VF's runtime regs received from PF.
 | 
						|
 * @gt: the &xe_gt
 | 
						|
 * @p: the &drm_printer
 | 
						|
 *
 | 
						|
 * This function is for VF use only.
 | 
						|
 */
 | 
						|
void xe_gt_sriov_vf_print_runtime(struct xe_gt *gt, struct drm_printer *p)
 | 
						|
{
 | 
						|
	struct vf_runtime_reg *vf_regs = gt->sriov.vf.runtime.regs;
 | 
						|
	unsigned int size = gt->sriov.vf.runtime.num_regs;
 | 
						|
 | 
						|
	xe_gt_assert(gt, IS_SRIOV_VF(gt_to_xe(gt)));
 | 
						|
 | 
						|
	for (; size--; vf_regs++)
 | 
						|
		drm_printf(p, "%#x = %#x\n", vf_regs->offset, vf_regs->value);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * xe_gt_sriov_vf_print_version - Print VF ABI versions.
 | 
						|
 * @gt: the &xe_gt
 | 
						|
 * @p: the &drm_printer
 | 
						|
 *
 | 
						|
 * This function is for VF use only.
 | 
						|
 */
 | 
						|
void xe_gt_sriov_vf_print_version(struct xe_gt *gt, struct drm_printer *p)
 | 
						|
{
 | 
						|
	struct xe_gt_sriov_vf_guc_version *guc_version = >->sriov.vf.guc_version;
 | 
						|
	struct xe_gt_sriov_vf_relay_version *pf_version = >->sriov.vf.pf_version;
 | 
						|
	u32 branch, major, minor;
 | 
						|
 | 
						|
	xe_gt_assert(gt, IS_SRIOV_VF(gt_to_xe(gt)));
 | 
						|
 | 
						|
	drm_printf(p, "GuC ABI:\n");
 | 
						|
 | 
						|
	vf_minimum_guc_version(gt, &branch, &major, &minor);
 | 
						|
	drm_printf(p, "\tbase:\t%u.%u.%u.*\n", branch, major, minor);
 | 
						|
 | 
						|
	vf_wanted_guc_version(gt, &branch, &major, &minor);
 | 
						|
	drm_printf(p, "\twanted:\t%u.%u.%u.*\n", branch, major, minor);
 | 
						|
 | 
						|
	drm_printf(p, "\thandshake:\t%u.%u.%u.%u\n",
 | 
						|
		   guc_version->branch, guc_version->major,
 | 
						|
		   guc_version->minor, guc_version->patch);
 | 
						|
 | 
						|
	drm_printf(p, "PF ABI:\n");
 | 
						|
 | 
						|
	drm_printf(p, "\tbase:\t%u.%u\n",
 | 
						|
		   GUC_RELAY_VERSION_BASE_MAJOR, GUC_RELAY_VERSION_BASE_MINOR);
 | 
						|
	drm_printf(p, "\twanted:\t%u.%u\n",
 | 
						|
		   GUC_RELAY_VERSION_LATEST_MAJOR, GUC_RELAY_VERSION_LATEST_MINOR);
 | 
						|
	drm_printf(p, "\thandshake:\t%u.%u\n",
 | 
						|
		   pf_version->major, pf_version->minor);
 | 
						|
}
 |