#include "xe_gt_mcr.h"
#include "regs/xe_gt_regs.h"
#include "xe_assert.h"
#include "xe_gt_printk.h"
#include "xe_gt_topology.h"
#include "xe_gt_types.h"
#include "xe_guc_hwconfig.h"
#include "xe_mmio.h"
#include "xe_sriov.h"
static inline struct xe_reg to_xe_reg(struct xe_reg_mcr reg_mcr)
{
return reg_mcr.__reg;
}
enum {
MCR_OP_READ,
MCR_OP_WRITE
};
static const struct xe_mmio_range xelp_l3bank_steering_table[] = {
{ 0x00B100, 0x00B3FF },
{},
};
static const struct xe_mmio_range xehp_l3bank_steering_table[] = {
{ 0x008C80, 0x008CFF },
{ 0x00B100, 0x00B3FF },
{},
};
static const struct xe_mmio_range xehp_mslice_steering_table[] = {
{ 0x00DD00, 0x00DDFF },
{ 0x00E900, 0x00FFFF },
{},
};
static const struct xe_mmio_range xehp_lncf_steering_table[] = {
{ 0x00B000, 0x00B0FF },
{ 0x00D880, 0x00D8FF },
{},
};
static const struct xe_mmio_range xehpc_instance0_steering_table[] = {
{ 0x004000, 0x004AFF },
{ 0x008800, 0x00887F },
{ 0x008A80, 0x008AFF },
{ 0x00B000, 0x00B0FF },
{ 0x00B100, 0x00B3FF },
{ 0x00C800, 0x00CFFF },
{ 0x00D800, 0x00D8FF },
{ 0x00DD00, 0x00DDFF },
{ 0x00E900, 0x00E9FF },
{ 0x00EC00, 0x00EEFF },
{ 0x00F000, 0x00FFFF },
{ 0x024180, 0x0241FF },
{},
};
static const struct xe_mmio_range xelpg_instance0_steering_table[] = {
{ 0x000B00, 0x000BFF },
{ 0x001000, 0x001FFF },
{ 0x004000, 0x0048FF },
{ 0x008700, 0x0087FF },
{ 0x00B000, 0x00B0FF },
{ 0x00C800, 0x00CFFF },
{ 0x00D880, 0x00D8FF },
{ 0x00DD00, 0x00DDFF },
{},
};
static const struct xe_mmio_range xelpg_l3bank_steering_table[] = {
{ 0x00B100, 0x00B3FF },
{},
};
static const struct xe_mmio_range xelp_dss_steering_table[] = {
{ 0x008150, 0x00815F },
{ 0x009520, 0x00955F },
{ 0x00DE80, 0x00E8FF },
{ 0x024A00, 0x024A7F },
{},
};
static const struct xe_mmio_range xehp_dss_steering_table[] = {
{ 0x005200, 0x0052FF },
{ 0x005400, 0x007FFF },
{ 0x008140, 0x00815F },
{ 0x008D00, 0x008DFF },
{ 0x0094D0, 0x00955F },
{ 0x009680, 0x0096FF },
{ 0x00D800, 0x00D87F },
{ 0x00DC00, 0x00DCFF },
{ 0x00DE80, 0x00E8FF },
{ 0x017000, 0x017FFF },
{ 0x024A00, 0x024A7F },
{},
};
static const struct xe_mmio_range xehpc_dss_steering_table[] = {
{ 0x008140, 0x00817F },
{ 0x0094D0, 0x00955F },
{ 0x009680, 0x0096FF },
{ 0x00DC00, 0x00DCFF },
{ 0x00DE80, 0x00E7FF },
{},
};
static const struct xe_mmio_range xelpg_dss_steering_table[] = {
{ 0x005200, 0x0052FF },
{ 0x005500, 0x007FFF },
{ 0x008140, 0x00815F },
{ 0x0094D0, 0x00955F },
{ 0x009680, 0x0096FF },
{ 0x00D800, 0x00D87F },
{ 0x00DC00, 0x00DCFF },
{ 0x00DE80, 0x00E8FF },
{},
};
static const struct xe_mmio_range xe3p_xpc_xecore_steering_table[] = {
{ 0x008140, 0x00817F },
{ 0x009480, 0x00955F },
{ 0x00D800, 0x00D87F },
{ 0x00DC00, 0x00E9FF },
{ 0x013000, 0x0135FF },
{},
};
static const struct xe_mmio_range xelpmp_oaddrm_steering_table[] = {
{ 0x393200, 0x39323F },
{ 0x393400, 0x3934FF },
{},
};
static const struct xe_mmio_range dg2_implicit_steering_table[] = {
{ 0x000B00, 0x000BFF },
{ 0x001000, 0x001FFF },
{ 0x004000, 0x004AFF },
{ 0x008700, 0x0087FF },
{ 0x00C800, 0x00CFFF },
{ 0x00F000, 0x00FFFF },
{},
};
static const struct xe_mmio_range xe2lpg_dss_steering_table[] = {
{ 0x005200, 0x0052FF },
{ 0x005500, 0x007FFF },
{ 0x008140, 0x00815F },
{ 0x0094D0, 0x00955F },
{ 0x009680, 0x0096FF },
{ 0x00D800, 0x00D87F },
{ 0x00DC00, 0x00DCFF },
{ 0x00DE80, 0x00E8FF },
{ 0x00E980, 0x00E9FF },
{ 0x013000, 0x0133FF },
{},
};
static const struct xe_mmio_range xe2lpg_sqidi_psmi_steering_table[] = {
{ 0x000B00, 0x000BFF },
{ 0x001000, 0x001FFF },
{},
};
static const struct xe_mmio_range xe2lpg_instance0_steering_table[] = {
{ 0x004000, 0x004AFF },
{ 0x008700, 0x00887F },
{ 0x00B000, 0x00B3FF },
{ 0x00C800, 0x00CFFF },
{ 0x00D880, 0x00D8FF },
{ 0x00DD00, 0x00DDFF },
{ 0x00E900, 0x00E97F },
{ 0x00F000, 0x00FFFF },
{ 0x013400, 0x0135FF },
{},
};
static const struct xe_mmio_range xe2lpm_gpmxmt_steering_table[] = {
{ 0x388160, 0x38817F },
{ 0x389480, 0x3894CF },
{},
};
static const struct xe_mmio_range xe2lpm_instance0_steering_table[] = {
{ 0x384000, 0x3847DF },
{ 0x384900, 0x384AFF },
{ 0x389560, 0x3895FF },
{ 0x38B600, 0x38B8FF },
{ 0x38C800, 0x38D07F },
{ 0x38F000, 0x38F0FF },
{ 0x393C00, 0x393C7F },
{},
};
static const struct xe_mmio_range xe3lpm_instance0_steering_table[] = {
{ 0x384000, 0x3841FF },
{ 0x384400, 0x3847DF },
{ 0x384900, 0x384AFF },
{ 0x389560, 0x3895FF },
{ 0x38B600, 0x38B8FF },
{ 0x38C800, 0x38D07F },
{ 0x38D0D0, 0x38F0FF },
{ 0x393C00, 0x393C7F },
{},
};
static const struct xe_mmio_range xe3p_xpc_gam_grp1_steering_table[] = {
{ 0x004000, 0x004AFF },
{ 0x00F100, 0x00FFFF },
{},
};
static const struct xe_mmio_range xe3p_xpc_node_steering_table[] = {
{ 0x00B000, 0x00B0FF },
{ 0x00D880, 0x00D8FF },
{},
};
static const struct xe_mmio_range xe3p_xpc_instance0_steering_table[] = {
{ 0x00B500, 0x00B6FF },
{ 0x00C800, 0x00CFFF },
{ 0x00F000, 0x00F0FF },
{},
};
static void init_steering_l3bank(struct xe_gt *gt)
{
struct xe_device *xe = gt_to_xe(gt);
struct xe_mmio *mmio = >->mmio;
if (GRAPHICS_VER(xe) >= 35) {
unsigned int first_bank = xe_l3_bank_mask_ffs(gt->fuse_topo.l3_bank_mask);
const int banks_per_node = 4;
unsigned int node = first_bank / banks_per_node;
gt->steering[L3BANK].group_target = node;
gt->steering[L3BANK].instance_target = first_bank % banks_per_node;
gt->steering[NODE].group_target = node >> 1;
gt->steering[NODE].instance_target = node & 1;
} else if (GRAPHICS_VERx100(xe) >= 1270) {
u32 mslice_mask = REG_FIELD_GET(MEML3_EN_MASK,
xe_mmio_read32(mmio, MIRROR_FUSE3));
u32 bank_mask = REG_FIELD_GET(GT_L3_EXC_MASK,
xe_mmio_read32(mmio, XEHP_FUSE4));
gt->steering[L3BANK].group_target = __ffs(mslice_mask);
gt->steering[L3BANK].instance_target =
bank_mask & BIT(0) ? 0 : 2;
} else if (xe->info.platform == XE_DG2) {
u32 mslice_mask = REG_FIELD_GET(MEML3_EN_MASK,
xe_mmio_read32(mmio, MIRROR_FUSE3));
u32 bank = __ffs(mslice_mask) * 8;
gt->steering[L3BANK].group_target = (bank >> 2) & 0x7;
gt->steering[L3BANK].instance_target = bank & 0x3;
} else {
u32 fuse = REG_FIELD_GET(L3BANK_MASK,
~xe_mmio_read32(mmio, MIRROR_FUSE3));
gt->steering[L3BANK].group_target = 0;
gt->steering[L3BANK].instance_target = __ffs(fuse);
}
}
static void init_steering_mslice(struct xe_gt *gt)
{
u32 mask = REG_FIELD_GET(MEML3_EN_MASK,
xe_mmio_read32(>->mmio, MIRROR_FUSE3));
gt->steering[MSLICE].group_target = __ffs(mask);
gt->steering[MSLICE].instance_target = 0;
gt->steering[LNCF].group_target = __ffs(mask) << 1;
gt->steering[LNCF].instance_target = 0;
}
static unsigned int dss_per_group(struct xe_gt *gt)
{
struct xe_guc *guc = >->uc.guc;
u32 max_slices = 0, max_subslices = 0;
int ret;
#define HWCONFIG_ATTR_MAX_SLICES 1
#define HWCONFIG_ATTR_MAX_SUBSLICES 70
ret = xe_guc_hwconfig_lookup_u32(guc, HWCONFIG_ATTR_MAX_SLICES,
&max_slices);
if (ret < 0 || max_slices == 0)
goto fallback;
ret = xe_guc_hwconfig_lookup_u32(guc, HWCONFIG_ATTR_MAX_SUBSLICES,
&max_subslices);
if (ret < 0 || max_subslices == 0)
goto fallback;
return DIV_ROUND_UP(max_subslices, max_slices);
fallback:
if (GRAPHICS_VERx100(gt_to_xe(gt)) >= 2000 &&
!gt_to_xe(gt)->info.force_execlist)
xe_gt_err(gt, "Slice/Subslice counts missing from hwconfig table; using typical fallback values\n");
if (gt_to_xe(gt)->info.platform == XE_PVC)
return 8;
else if (GRAPHICS_VERx100(gt_to_xe(gt)) >= 1250)
return 4;
else
return 6;
}
void xe_gt_mcr_get_dss_steering(const struct xe_gt *gt, unsigned int dss, u16 *group, u16 *instance)
{
xe_gt_assert(gt, dss < XE_MAX_DSS_FUSE_BITS);
*group = dss / gt->steering_dss_per_grp;
*instance = dss % gt->steering_dss_per_grp;
}
u32 xe_gt_mcr_steering_info_to_dss_id(struct xe_gt *gt, u16 group, u16 instance)
{
return group * dss_per_group(gt) + instance;
}
static void init_steering_dss(struct xe_gt *gt)
{
gt->steering_dss_per_grp = dss_per_group(gt);
xe_gt_mcr_get_dss_steering(gt,
min(xe_dss_mask_group_ffs(gt->fuse_topo.g_dss_mask, 0, 0),
xe_dss_mask_group_ffs(gt->fuse_topo.c_dss_mask, 0, 0)),
>->steering[DSS].group_target,
>->steering[DSS].instance_target);
}
static void init_steering_oaddrm(struct xe_gt *gt)
{
if (gt->info.engine_mask & (XE_HW_ENGINE_VCS0 | XE_HW_ENGINE_VECS0))
gt->steering[OADDRM].group_target = 0;
else
gt->steering[OADDRM].group_target = 1;
gt->steering[OADDRM].instance_target = 0;
}
static void init_steering_sqidi_psmi(struct xe_gt *gt)
{
u32 mask = REG_FIELD_GET(XE2_NODE_ENABLE_MASK,
xe_mmio_read32(>->mmio, MIRROR_FUSE3));
u32 select = __ffs(mask);
gt->steering[SQIDI_PSMI].group_target = select >> 1;
gt->steering[SQIDI_PSMI].instance_target = select & 0x1;
}
static void init_steering_gam1(struct xe_gt *gt)
{
gt->steering[GAM1].group_target = 1;
gt->steering[GAM1].instance_target = 0;
}
static const struct {
const char *name;
void (*init)(struct xe_gt *gt);
} xe_steering_types[] = {
[L3BANK] = { "L3BANK", init_steering_l3bank },
[NODE] = { "NODE", NULL },
[MSLICE] = { "MSLICE", init_steering_mslice },
[LNCF] = { "LNCF", NULL },
[DSS] = { "DSS / XeCore", init_steering_dss },
[OADDRM] = { "OADDRM / GPMXMT", init_steering_oaddrm },
[SQIDI_PSMI] = { "SQIDI_PSMI", init_steering_sqidi_psmi },
[GAM1] = { "GAMWKRS / STLB / GAMREQSTRM", init_steering_gam1 },
[INSTANCE0] = { "INSTANCE 0", NULL },
[IMPLICIT_STEERING] = { "IMPLICIT", NULL },
};
void xe_gt_mcr_init_early(struct xe_gt *gt)
{
struct xe_device *xe = gt_to_xe(gt);
BUILD_BUG_ON(IMPLICIT_STEERING + 1 != NUM_STEERING_TYPES);
BUILD_BUG_ON(ARRAY_SIZE(xe_steering_types) != NUM_STEERING_TYPES);
spin_lock_init(>->mcr_lock);
if (IS_SRIOV_VF(xe))
return;
if (gt->info.type == XE_GT_TYPE_MEDIA) {
drm_WARN_ON(&xe->drm, MEDIA_VER(xe) < 13);
if (MEDIA_VER(xe) >= 30) {
gt->steering[OADDRM].ranges = xe2lpm_gpmxmt_steering_table;
gt->steering[INSTANCE0].ranges = xe3lpm_instance0_steering_table;
} else if (MEDIA_VERx100(xe) >= 1301) {
gt->steering[OADDRM].ranges = xe2lpm_gpmxmt_steering_table;
gt->steering[INSTANCE0].ranges = xe2lpm_instance0_steering_table;
} else {
gt->steering[OADDRM].ranges = xelpmp_oaddrm_steering_table;
}
} else {
if (GRAPHICS_VERx100(xe) == 3511) {
gt->steering[DSS].ranges = xe3p_xpc_xecore_steering_table;
gt->steering[GAM1].ranges = xe3p_xpc_gam_grp1_steering_table;
gt->steering[INSTANCE0].ranges = xe3p_xpc_instance0_steering_table;
gt->steering[L3BANK].ranges = xelpg_l3bank_steering_table;
gt->steering[NODE].ranges = xe3p_xpc_node_steering_table;
} else if (GRAPHICS_VER(xe) >= 20) {
gt->steering[DSS].ranges = xe2lpg_dss_steering_table;
gt->steering[SQIDI_PSMI].ranges = xe2lpg_sqidi_psmi_steering_table;
gt->steering[INSTANCE0].ranges = xe2lpg_instance0_steering_table;
} else if (GRAPHICS_VERx100(xe) >= 1270) {
gt->steering[INSTANCE0].ranges = xelpg_instance0_steering_table;
gt->steering[L3BANK].ranges = xelpg_l3bank_steering_table;
gt->steering[DSS].ranges = xelpg_dss_steering_table;
} else if (xe->info.platform == XE_PVC) {
gt->steering[INSTANCE0].ranges = xehpc_instance0_steering_table;
gt->steering[DSS].ranges = xehpc_dss_steering_table;
} else if (xe->info.platform == XE_DG2) {
gt->steering[L3BANK].ranges = xehp_l3bank_steering_table;
gt->steering[MSLICE].ranges = xehp_mslice_steering_table;
gt->steering[LNCF].ranges = xehp_lncf_steering_table;
gt->steering[DSS].ranges = xehp_dss_steering_table;
gt->steering[IMPLICIT_STEERING].ranges = dg2_implicit_steering_table;
} else {
gt->steering[L3BANK].ranges = xelp_l3bank_steering_table;
gt->steering[DSS].ranges = xelp_dss_steering_table;
}
}
gt->steering[INSTANCE0].initialized = true;
}
void xe_gt_mcr_init(struct xe_gt *gt)
{
if (IS_SRIOV_VF(gt_to_xe(gt)))
return;
for (int i = 0; i < NUM_STEERING_TYPES; i++) {
gt->steering[i].initialized = true;
if (gt->steering[i].ranges && xe_steering_types[i].init)
xe_steering_types[i].init(gt);
}
}
void xe_gt_mcr_set_implicit_defaults(struct xe_gt *gt)
{
struct xe_device *xe = gt_to_xe(gt);
if (IS_SRIOV_VF(xe))
return;
if (xe->info.platform == XE_DG2) {
u32 steer_val = REG_FIELD_PREP(MCR_SLICE_MASK, 0) |
REG_FIELD_PREP(MCR_SUBSLICE_MASK, 2);
xe_mmio_write32(>->mmio, STEER_SEMAPHORE, steer_val);
xe_mmio_write32(>->mmio, SF_MCR_SELECTOR, steer_val);
}
}
bool xe_gt_mcr_get_nonterminated_steering(struct xe_gt *gt,
struct xe_reg_mcr reg_mcr,
u8 *group, u8 *instance)
{
const struct xe_reg reg = to_xe_reg(reg_mcr);
const struct xe_mmio_range *implicit_ranges;
for (int type = 0; type < IMPLICIT_STEERING; type++) {
if (!gt->steering[type].ranges)
continue;
for (int i = 0; gt->steering[type].ranges[i].end > 0; i++) {
if (xe_mmio_in_range(>->mmio, >->steering[type].ranges[i], reg)) {
drm_WARN(>_to_xe(gt)->drm, !gt->steering[type].initialized,
"Uninitialized usage of MCR register %s/%#x\n",
xe_steering_types[type].name, reg.addr);
*group = gt->steering[type].group_target;
*instance = gt->steering[type].instance_target;
return true;
}
}
}
implicit_ranges = gt->steering[IMPLICIT_STEERING].ranges;
if (implicit_ranges)
for (int i = 0; implicit_ranges[i].end > 0; i++)
if (xe_mmio_in_range(>->mmio, &implicit_ranges[i], reg))
return false;
drm_WARN(>_to_xe(gt)->drm, true,
"Did not find MCR register %#x in any MCR steering table\n",
reg.addr);
*group = 0;
*instance = 0;
return true;
}
static void mcr_lock(struct xe_gt *gt) __acquires(>->mcr_lock)
{
struct xe_device *xe = gt_to_xe(gt);
int ret = 0;
spin_lock(>->mcr_lock);
if (GRAPHICS_VERx100(xe) >= 1270)
ret = xe_mmio_wait32(>->mmio, STEER_SEMAPHORE, 0x1, 0x1, 10, NULL,
true);
drm_WARN_ON_ONCE(&xe->drm, ret == -ETIMEDOUT);
}
static void mcr_unlock(struct xe_gt *gt) __releases(>->mcr_lock)
{
if (GRAPHICS_VERx100(gt_to_xe(gt)) >= 1270)
xe_mmio_write32(>->mmio, STEER_SEMAPHORE, 0x1);
spin_unlock(>->mcr_lock);
}
static u32 rw_with_mcr_steering(struct xe_gt *gt, struct xe_reg_mcr reg_mcr,
u8 rw_flag, int group, int instance, u32 value)
{
const struct xe_reg reg = to_xe_reg(reg_mcr);
struct xe_mmio *mmio = >->mmio;
struct xe_reg steer_reg;
u32 steer_val, val = 0;
lockdep_assert_held(>->mcr_lock);
if (GRAPHICS_VERx100(gt_to_xe(gt)) >= 1270) {
steer_reg = MTL_MCR_SELECTOR;
steer_val = REG_FIELD_PREP(MTL_MCR_GROUPID, group) |
REG_FIELD_PREP(MTL_MCR_INSTANCEID, instance);
} else {
steer_reg = MCR_SELECTOR;
steer_val = REG_FIELD_PREP(MCR_SLICE_MASK, group) |
REG_FIELD_PREP(MCR_SUBSLICE_MASK, instance);
}
if (rw_flag == MCR_OP_READ)
steer_val |= MCR_MULTICAST;
xe_mmio_write32(mmio, steer_reg, steer_val);
if (rw_flag == MCR_OP_READ)
val = xe_mmio_read32(mmio, reg);
else
xe_mmio_write32(mmio, reg, value);
if (rw_flag == MCR_OP_WRITE)
xe_mmio_write32(mmio, steer_reg, MCR_MULTICAST);
return val;
}
u32 xe_gt_mcr_unicast_read_any(struct xe_gt *gt, struct xe_reg_mcr reg_mcr)
{
const struct xe_reg reg = to_xe_reg(reg_mcr);
u8 group, instance;
u32 val;
bool steer;
xe_gt_assert(gt, !IS_SRIOV_VF(gt_to_xe(gt)));
steer = xe_gt_mcr_get_nonterminated_steering(gt, reg_mcr,
&group, &instance);
if (steer) {
mcr_lock(gt);
val = rw_with_mcr_steering(gt, reg_mcr, MCR_OP_READ,
group, instance, 0);
mcr_unlock(gt);
} else {
val = xe_mmio_read32(>->mmio, reg);
}
return val;
}
u32 xe_gt_mcr_unicast_read(struct xe_gt *gt,
struct xe_reg_mcr reg_mcr,
int group, int instance)
{
u32 val;
xe_gt_assert(gt, !IS_SRIOV_VF(gt_to_xe(gt)));
mcr_lock(gt);
val = rw_with_mcr_steering(gt, reg_mcr, MCR_OP_READ, group, instance, 0);
mcr_unlock(gt);
return val;
}
void xe_gt_mcr_unicast_write(struct xe_gt *gt, struct xe_reg_mcr reg_mcr,
u32 value, int group, int instance)
{
xe_gt_assert(gt, !IS_SRIOV_VF(gt_to_xe(gt)));
mcr_lock(gt);
rw_with_mcr_steering(gt, reg_mcr, MCR_OP_WRITE, group, instance, value);
mcr_unlock(gt);
}
void xe_gt_mcr_multicast_write(struct xe_gt *gt, struct xe_reg_mcr reg_mcr,
u32 value)
{
struct xe_reg reg = to_xe_reg(reg_mcr);
xe_gt_assert(gt, !IS_SRIOV_VF(gt_to_xe(gt)));
mcr_lock(gt);
xe_mmio_write32(>->mmio, reg, value);
mcr_unlock(gt);
}
void xe_gt_mcr_steering_dump(struct xe_gt *gt, struct drm_printer *p)
{
for (int i = 0; i < NUM_STEERING_TYPES; i++) {
if (gt->steering[i].ranges) {
drm_printf(p, "%s steering: group=%#x, instance=%#x\n",
xe_steering_types[i].name,
gt->steering[i].group_target,
gt->steering[i].instance_target);
for (int j = 0; gt->steering[i].ranges[j].end; j++)
drm_printf(p, "\t0x%06x - 0x%06x\n",
gt->steering[i].ranges[j].start,
gt->steering[i].ranges[j].end);
}
}
}