@@ -80,6 +80,12 @@ static void parse_hdm_decoder_caps(struct cxl_hdm *cxlhdm)
cxlhdm->interleave_mask |= GENMASK(11, 8);
if (FIELD_GET(CXL_HDM_DECODER_INTERLEAVE_14_12, hdm_cap))
cxlhdm->interleave_mask |= GENMASK(14, 12);
+
+ cxlhdm->interleave_cap = CXL_HDM_INTERLEAVE_CAP_DEFAULT;
+ if (FIELD_GET(CXL_HDM_DECODER_INTERLEAVE_3_6_12_WAY, hdm_cap))
+ cxlhdm->interleave_cap |= CXL_HDM_INTERLEAVE_CAP_3_6_12;
+ if (FIELD_GET(CXL_HDM_DECODER_INTERLEAVE_16_WAY, hdm_cap))
+ cxlhdm->interleave_cap |= CXL_HDM_INTERLEAVE_CAP_16;
}
static void __iomem *map_hdm_decoder_regs(struct cxl_port *port,
@@ -1017,6 +1017,9 @@ static int cxl_interleave_capable(struct cxl_port *port, struct device *dev,
if (eiw == 0)
return 0;
+ if (!test_bit(ways, &cxlhdm->interleave_cap))
+ return -EINVAL;
+
if (is_power_of_2(eiw))
addr_mask = GENMASK(eig + 8 + eiw - 1, eig + 8);
else
@@ -42,6 +42,8 @@
#define CXL_HDM_DECODER_TARGET_COUNT_MASK GENMASK(7, 4)
#define CXL_HDM_DECODER_INTERLEAVE_11_8 BIT(8)
#define CXL_HDM_DECODER_INTERLEAVE_14_12 BIT(9)
+#define CXL_HDM_DECODER_INTERLEAVE_3_6_12_WAY BIT(11)
+#define CXL_HDM_DECODER_INTERLEAVE_16_WAY BIT(12)
#define CXL_HDM_DECODER_CTRL_OFFSET 0x4
#define CXL_HDM_DECODER_ENABLE BIT(1)
#define CXL_HDM_DECODER0_BASE_LOW_OFFSET(i) (0x20 * (i) + 0x10)
@@ -393,11 +393,16 @@ static inline void cxl_mem_active_dec(void)
}
#endif
+#define CXL_HDM_INTERLEAVE_CAP_DEFAULT BIT(1) | BIT(2) | BIT(4) | BIT(8)
+#define CXL_HDM_INTERLEAVE_CAP_3_6_12 BIT(3) | BIT(6) | BIT(12)
+#define CXL_HDM_INTERLEAVE_CAP_16 BIT(16)
+
struct cxl_hdm {
struct cxl_component_regs regs;
unsigned int decoder_count;
unsigned int target_count;
unsigned int interleave_mask;
+ unsigned long interleave_cap;
struct cxl_port *port;
};