/* * For most BOs, the address on the allocating tile is fine. However for * some, e.g. G2G CTB, the address on a specific tile is required as it * might be different for each tile. So, just always ask for the address * on the target GuC.
*/
addr = __xe_bo_ggtt_addr(bo, gt_to_tile(guc_to_gt(guc))->id);
staticbool needs_wa_dual_queue(struct xe_gt *gt)
{ /* * The DUAL_QUEUE_WA tells the GuC to not allow concurrent submissions * on RCS and CCSes with different address spaces, which on DG2 is * required as a WA for an HW bug.
*/ if (XE_WA(gt, 22011391025)) returntrue;
/* * On newer platforms, the HW has been updated to not allow parallel * execution of different address spaces, so the RCS/CCS will stall the * context switch if one of the other RCS/CCSes is busy with a different * address space. While functionally correct, having a submission * stalled on the HW limits the GuC ability to shuffle things around and * can cause complications if the non-stalled submission runs for a long * time, because the GuC doesn't know that the stalled submission isn't * actually running and might declare it as hung. Therefore, we enable * the DUAL_QUEUE_WA on all newer platforms on GTs that have CCS engines * to move management back to the GuC.
*/ if (CCS_MASK(gt) && GRAPHICS_VERx100(gt_to_xe(gt)) >= 1270) returntrue;
if (XE_WA(gt, 22012773006))
flags |= GUC_WA_POLLCS;
if (XE_WA(gt, 14014475959))
flags |= GUC_WA_HOLD_CCS_SWITCHOUT;
if (needs_wa_dual_queue(gt))
flags |= GUC_WA_DUAL_QUEUE;
/* * Wa_22011802037: FIXME - there's more to be done than simply setting * this flag: make sure each CS is stopped when preparing for GT reset * and wait for pending MI_FW.
*/ if (GRAPHICS_VERx100(xe) < 1270)
flags |= GUC_WA_PRE_PARSER;
if (XE_WA(gt, 22012727170) || XE_WA(gt, 22012727685))
flags |= GUC_WA_CONTEXT_ISOLATION;
if (XE_WA(gt, 18020744125) &&
!xe_hw_engine_mask_per_class(gt, XE_ENGINE_CLASS_RENDER))
flags |= GUC_WA_RCS_REGS_IN_CCS_REGS_LIST;
if (XE_WA(gt, 1509372804))
flags |= GUC_WA_RENDER_RST_RC6_EXIT;
if (XE_WA(gt, 14018913170))
flags |= GUC_WA_ENABLE_TSC_CHECK_ON_RC6;
/* * Initialize the GuC parameter block before starting the firmware * transfer. These parameters are read by the firmware on startup * and cannot be changed thereafter.
*/ staticvoid guc_write_params(struct xe_guc *guc)
{ struct xe_gt *gt = guc_to_gt(guc); int i;
/* * Generate a unique id for each bi-directional CTB for each pair of * near and far tiles/devices. The id can then be used as an index into * a single allocation that is sub-divided into multiple CTBs. * * For example, with two devices per tile and two tiles, the table should * look like: * Far <tile>.<dev> * 0.0 0.1 1.0 1.1 * N 0.0 --/-- 00/01 02/03 04/05 * e 0.1 01/00 --/-- 06/07 08/09 * a 1.0 03/02 07/06 --/-- 10/11 * r 1.1 05/04 09/08 11/10 --/-- * * Where each entry is Rx/Tx channel id. * * So GuC #3 (tile 1, dev 1) talking to GuC #2 (tile 1, dev 0) would * be reading from channel #11 and writing to channel #10. Whereas, * GuC #2 talking to GuC #3 would be read on #10 and write to #11.
*/ staticunsignedint g2g_slot(u32 near_tile, u32 near_dev, u32 far_tile, u32 far_dev,
u32 type, u32 max_inst, bool have_dev)
{
u32 near = near_tile, far = far_tile;
u32 idx = 0, x, y, direction; int i;
if (have_dev) {
near = (near << 1) | near_dev;
far = (far << 1) | far_dev;
}
/* No need to send to one's self */ if (far == near) return -1;
if (far > near) { /* Top right table half */
x = far;
y = near;
/* T/R is 'forwards' direction */
direction = type;
} else { /* Bottom left table half */
x = near;
y = far;
/* B/L is 'backwards' direction */
direction = (1 - type);
}
/* Count the rows prior to the target */ for (i = y; i > 0; i--)
idx += max_inst - i;
/* Count this row up to the target */
idx += (x - 1 - y);
staticbool xe_guc_g2g_wanted(struct xe_device *xe)
{ /* Can't do GuC to GuC communication if there is only one GuC */ if (xe->info.gt_count <= 1) returnfalse;
/* GuC interface will need extending if more GT device types are ever created. */
xe_gt_assert(gt, (gt->info.type == XE_GT_TYPE_MAIN) || (gt->info.type == XE_GT_TYPE_MEDIA));
/* Channel numbering depends on whether there are multiple GTs per tile */
have_dev = xe->info.gt_count > xe->info.tile_count;
/* Dynamic ICS is available for PVC and Xe2 and newer platforms. */ if (xe->info.platform != XE_PVC && GRAPHICS_VER(xe) < 20) returnfalse;
/* * The feature is currently not compatible with multi-lrc, so the GuC * does not support it at all on the media engines (which are the main * users of mlrc). On the primary GT side, to avoid it being used in * conjunction with mlrc, we only enable it if we are in single CCS * mode.
*/ if (xe_gt_is_media_type(gt) || gt->ccs_mode > 1) returnfalse;
/* * Dynamic ICS requires GuC v70.40.1, which maps to compatibility * version v1.18.4.
*/ return GUC_SUBMIT_VER(guc) >= MAKE_GUC_VER(1, 18, 4);
}
/* * The extra CAT error type opt-in was added in GuC v70.17.0, which maps * to compatibility version v1.7.0. * Note that the GuC allows enabling this KLV even on platforms that do * not support the extra type; in such case the returned type variable * will be set to a known invalid value which we can check against.
*/ if (GUC_SUBMIT_VER(guc) >= MAKE_GUC_VER(1, 7, 0))
klvs[count++] = PREP_GUC_KLV_TAG(OPT_IN_FEATURE_EXT_CAT_ERR_TYPE);
if (supports_dynamic_ics(guc))
klvs[count++] = PREP_GUC_KLV_TAG(OPT_IN_FEATURE_DYNAMIC_INHIBIT_CONTEXT_SWITCH);
if (count) {
xe_assert(xe, count <= OPT_IN_MAX_DWORDS);
ret = __guc_opt_in_features_enable(guc, xe_guc_buf_flush(buf), count); if (ret < 0) {
xe_gt_err(guc_to_gt(guc), "failed to enable GuC opt-in features: %pe\n",
ERR_PTR(ret)); return ret;
}
}
/** * xe_guc_comm_init_early - early initialization of GuC communication * @guc: the &xe_guc to initialize * * Must be called prior to first MMIO communication with GuC firmware.
*/ void xe_guc_comm_init_early(struct xe_guc *guc)
{ struct xe_gt *gt = guc_to_gt(guc);
if (xe_gt_is_media_type(gt))
guc->notify_reg = MED_GUC_HOST_INTERRUPT; else
guc->notify_reg = GUC_HOST_INTERRUPT;
}
guc_status = xe_mmio_read32(mmio, GUC_STATUS); if (!(guc_status & GS_MIA_IN_RESET)) {
xe_gt_err(gt, "GuC status: %#x, MIA core expected to be in reset\n",
guc_status);
ret = -EIO; goto err_out;
}
for (i = 0; i < UOS_RSA_SCRATCH_COUNT; i++)
xe_mmio_write32(>->mmio, UOS_RSA_SCRATCH(i), rsa[i]);
return 0;
}
/* * Check a previously read GuC status register (GUC_STATUS) looking for * known terminal states (either completion or failure) of either the * microkernel status field or the boot ROM status field. Returns +1 for * successful completion, -1 for failure and 0 for any intermediate state.
*/ staticint guc_load_done(u32 status)
{
u32 uk_val = REG_FIELD_GET(GS_UKERNEL_MASK, status);
u32 br_val = REG_FIELD_GET(GS_BOOTROM_MASK, status);
switch (uk_val) { case XE_GUC_LOAD_STATUS_READY: return 1;
case XE_GUC_LOAD_STATUS_ERROR_DEVID_BUILD_MISMATCH: case XE_GUC_LOAD_STATUS_GUC_PREPROD_BUILD_MISMATCH: case XE_GUC_LOAD_STATUS_ERROR_DEVID_INVALID_GUCTYPE: case XE_GUC_LOAD_STATUS_HWCONFIG_ERROR: case XE_GUC_LOAD_STATUS_BOOTROM_VERSION_MISMATCH: case XE_GUC_LOAD_STATUS_DPC_ERROR: case XE_GUC_LOAD_STATUS_EXCEPTION: case XE_GUC_LOAD_STATUS_INIT_DATA_INVALID: case XE_GUC_LOAD_STATUS_MPU_DATA_INVALID: case XE_GUC_LOAD_STATUS_INIT_MMIO_SAVE_RESTORE_INVALID: case XE_GUC_LOAD_STATUS_KLV_WORKAROUND_INIT_ERROR: case XE_GUC_LOAD_STATUS_INVALID_FTR_FLAG: return -1;
}
switch (br_val) { case XE_BOOTROM_STATUS_NO_KEY_FOUND: case XE_BOOTROM_STATUS_RSA_FAILED: case XE_BOOTROM_STATUS_PAVPC_FAILED: case XE_BOOTROM_STATUS_WOPCM_FAILED: case XE_BOOTROM_STATUS_LOADLOC_FAILED: case XE_BOOTROM_STATUS_JUMP_FAILED: case XE_BOOTROM_STATUS_RC6CTXCONFIG_FAILED: case XE_BOOTROM_STATUS_MPUMAP_INCORRECT: case XE_BOOTROM_STATUS_EXCEPTION: case XE_BOOTROM_STATUS_PROD_KEY_CHECK_FAILURE: return -1;
}
return 0;
}
static s32 guc_pc_get_cur_freq(struct xe_guc_pc *guc_pc)
{
u32 freq; int ret = xe_guc_pc_get_cur_freq(guc_pc, &freq);
return ret ? ret : freq;
}
/* * Wait for the GuC to start up. * * Measurements indicate this should take no more than 20ms (assuming the GT * clock is at maximum frequency). However, thermal throttling and other issues * can prevent the clock hitting max and thus making the load take significantly * longer. Allow up to 200ms as a safety margin for real world worst case situations. * * However, bugs anywhere from KMD to GuC to PCODE to fan failure in a CI farm can * lead to even longer times. E.g. if the GT is clamped to minimum frequency then * the load times can be in the seconds range. So the timeout is increased for debug * builds to ensure that problems can be correctly analysed. For release builds, the * timeout is kept short so that users don't wait forever to find out that there is a * problem. In either case, if the load took longer than is reasonable even with some * 'sensible' throttling, then flag a warning because something is not right. * * Note that there is a limit on how long an individual usleep_range() can wait for, * hence longer waits require wrapping a shorter wait in a loop. * * Note that the only reason an end user should hit the shorter timeout is in case of * extreme thermal throttling. And a system that is that hot during boot is probably * dead anyway!
*/ #if IS_ENABLED(CONFIG_DRM_XE_DEBUG) #define GUC_LOAD_RETRY_LIMIT 20 #else #define GUC_LOAD_RETRY_LIMIT 3 #endif #define GUC_LOAD_TIME_WARN_MS 200
before_freq = xe_guc_pc_get_act_freq(guc_pc);
before = ktime_get(); /* * Note, can't use any kind of timing information from the call to xe_mmio_wait. * It could return a thousand intermediate stages at random times. Instead, must * manually track the total time taken and locally implement the timeout.
*/ do {
u32 last_status = status & (GS_UKERNEL_MASK | GS_BOOTROM_MASK); int ret;
/* * Wait for any change (intermediate or terminal) in the status register. * Note, the return value is a don't care. The only failure code is timeout * but the timeouts need to be accumulated over all the intermediate partial * timeouts rather than allowing a huge timeout each time. So basically, need * to treat a timeout no different to a value change.
*/
ret = xe_mmio_wait32_not(mmio, GUC_STATUS, GS_UKERNEL_MASK | GS_BOOTROM_MASK,
last_status, 1000 * 1000, &status, false); if (ret < 0)
count++;
after = ktime_get();
delta = ktime_sub(after, before);
delta_ms = ktime_to_ms(delta);
load_done = guc_load_done(status); if (load_done != 0) break;
if (delta_ms >= (GUC_LOAD_RETRY_LIMIT * 1000)) break;
xe_gt_dbg(gt, "load still in progress, timeouts = %d, freq = %dMHz (req %dMHz), status = 0x%08X [0x%02X/%02X]\n",
count, xe_guc_pc_get_act_freq(guc_pc),
guc_pc_get_cur_freq(guc_pc), status,
REG_FIELD_GET(GS_BOOTROM_MASK, status),
REG_FIELD_GET(GS_UKERNEL_MASK, status));
} while (1);
staticint __xe_guc_upload(struct xe_guc *guc)
{ int ret;
/* Raise GT freq to speed up HuC/GuC load */
xe_guc_pc_raise_unslice(&guc->pc);
guc_write_params(guc);
guc_prepare_xfer(guc);
/* * Note that GuC needs the CSS header plus uKernel code to be copied * by the DMA engine in one operation, whereas the RSA signature is * loaded separately, either by copying it to the UOS_RSA_SCRATCH * register (if key size <= 256) or through a ggtt-pinned vma (if key * size > 256). The RSA size and therefore the way we provide it to the * HW is fixed for each platform and hard-coded in the bootrom.
*/
ret = guc_xfer_rsa(guc); if (ret) goto out; /* * Current uCode expects the code to be loaded at 8k; locations below * this are used for the stack.
*/
ret = xe_uc_fw_upload(&guc->fw, 0x2000, UOS_MOVE); if (ret) goto out;
/* Wait for authentication */
ret = guc_wait_ucode(guc); if (ret) goto out;
ret = xe_guc_hwconfig_init(guc); if (ret) return ret;
ret = xe_guc_enable_communication(guc); if (ret) return ret;
ret = xe_gt_sriov_vf_connect(gt); if (ret) goto err_out;
ret = xe_gt_sriov_vf_query_runtime(gt); if (ret) goto err_out;
return 0;
err_out:
xe_guc_sanitize(guc); return ret;
}
/** * xe_guc_min_load_for_hwconfig - load minimal GuC and read hwconfig table * @guc: The GuC object * * This function uploads a minimal GuC that does not support submissions but * in a state where the hwconfig table can be read. Next, it reads and parses * the hwconfig table so it can be used for subsequent steps in the driver load. * Lastly, it enables CT communication (XXX: this is needed for PFs/VFs only). * * Return: 0 on success, negative error code on error.
*/ int xe_guc_min_load_for_hwconfig(struct xe_guc *guc)
{ int ret;
if (IS_SRIOV_VF(guc_to_xe(guc))) return vf_guc_min_load_for_hwconfig(guc);
xe_guc_ads_populate_minimal(&guc->ads);
xe_guc_pc_init_early(&guc->pc);
ret = __xe_guc_upload(guc); if (ret) return ret;
ret = xe_guc_hwconfig_init(guc); if (ret) return ret;
ret = xe_guc_enable_communication(guc); if (ret) return ret;
return 0;
}
int xe_guc_upload(struct xe_guc *guc)
{
xe_guc_ads_populate(&guc->ads);
/* Primary GuC and media GuC share a single enable bit */
xe_mmio_write32(>->mmio, GUC_SG_INTR_ENABLE,
REG_FIELD_PREP(ENGINE1_MASK, GUC_INTR_GUC2HOST));
/* * There are separate mask bits for primary and media GuCs, so use * a RMW operation to avoid clobbering the other GuC's setting.
*/
xe_mmio_rmw32(>->mmio, GUC_SG_INTR_MASK, events, 0);
}
int xe_guc_enable_communication(struct xe_guc *guc)
{ struct xe_device *xe = guc_to_xe(guc); int err;
/* * Both GUC_HOST_INTERRUPT and MED_GUC_HOST_INTERRUPT can pass * additional payload data to the GuC but this capability is not * used by the firmware yet. Use default value in the meantime.
*/
xe_mmio_write32(>->mmio, guc->notify_reg, default_notify_data);
}
xe_assert(xe, len);
xe_assert(xe, len <= VF_SW_FLAG_COUNT);
xe_assert(xe, len <= MED_VF_SW_FLAG_COUNT);
xe_assert(xe, FIELD_GET(GUC_HXG_MSG_0_ORIGIN, request[0]) ==
GUC_HXG_ORIGIN_HOST);
xe_assert(xe, FIELD_GET(GUC_HXG_MSG_0_TYPE, request[0]) ==
GUC_HXG_TYPE_REQUEST);
retry: /* Not in critical data-path, just do if else for GT type */ if (xe_gt_is_media_type(gt)) { for (i = 0; i < len; ++i)
xe_mmio_write32(mmio, MED_VF_SW_FLAG(i),
request[i]);
xe_mmio_read32(mmio, MED_VF_SW_FLAG(LAST_INDEX));
} else { for (i = 0; i < len; ++i)
xe_mmio_write32(mmio, VF_SW_FLAG(i),
request[i]);
xe_mmio_read32(mmio, VF_SW_FLAG(LAST_INDEX));
}
xe_guc_notify(guc);
ret = xe_mmio_wait32(mmio, reply_reg, GUC_HXG_MSG_0_ORIGIN,
FIELD_PREP(GUC_HXG_MSG_0_ORIGIN, GUC_HXG_ORIGIN_GUC),
50000, &reply, false); if (ret) { /* scratch registers might be cleared during FLR, try once more */ if (!reply && !lost) {
xe_gt_dbg(gt, "GuC mmio request %#x: lost, trying again\n", request[0]);
lost = true; goto retry;
}
timeout:
xe_gt_err(gt, "GuC mmio request %#x: no reply %#x\n",
request[0], reply); return ret;
}
header = xe_mmio_read32(mmio, reply_reg); if (FIELD_GET(GUC_HXG_MSG_0_TYPE, header) ==
GUC_HXG_TYPE_NO_RESPONSE_BUSY) { /* * Once we got a BUSY reply we must wait again for the final * response but this time we can't use ORIGIN mask anymore. * To spot a right change in the reply, we take advantage that * response SUCCESS and FAILURE differ only by the single bit * and all other bits are set and can be used as a new mask.
*/
u32 resp_bits = GUC_HXG_TYPE_RESPONSE_SUCCESS & GUC_HXG_TYPE_RESPONSE_FAILURE;
u32 resp_mask = FIELD_PREP(GUC_HXG_MSG_0_TYPE, resp_bits);
/* Just copy entire possible message response */ if (response_buf) {
response_buf[0] = header;
for (i = 1; i < VF_SW_FLAG_COUNT; i++) {
reply_reg.addr += sizeof(u32);
response_buf[i] = xe_mmio_read32(mmio, reply_reg);
}
}
/* Use data from the GuC response as our return value */ return FIELD_GET(GUC_HXG_RESPONSE_MSG_0_DATA0, header);
}
ALLOW_ERROR_INJECTION(xe_guc_mmio_send_recv, ERRNO);
if (!IS_SRIOV_VF(gt_to_xe(gt))) {
fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT); if (!fw_ref) return;
status = xe_mmio_read32(>->mmio, GUC_STATUS);
drm_printf(p, "\nGuC status 0x%08x:\n", status);
drm_printf(p, "\tBootrom status = 0x%x\n",
REG_FIELD_GET(GS_BOOTROM_MASK, status));
drm_printf(p, "\tuKernel status = 0x%x\n",
REG_FIELD_GET(GS_UKERNEL_MASK, status));
drm_printf(p, "\tMIA Core status = 0x%x\n",
REG_FIELD_GET(GS_MIA_MASK, status));
drm_printf(p, "\tLog level = %d\n",
xe_guc_log_get_level(&guc->log));
drm_puts(p, "\nScratch registers:\n"); for (i = 0; i < SOFT_SCRATCH_COUNT; i++) {
drm_printf(p, "\t%2d: \t0x%x\n",
i, xe_mmio_read32(>->mmio, SOFT_SCRATCH(i)));
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.