@@ -415,17 +415,20 @@ int cxl_hdm_decode_init(struct cxl_dev_state *cxlds, struct cxl_hdm *cxlhdm,
415415 */
416416 if (global_ctrl & CXL_HDM_DECODER_ENABLE || (!hdm && info -> mem_enabled ))
417417 return devm_cxl_enable_mem (& port -> dev , cxlds );
418- else if (!hdm )
419- return - ENODEV ;
420418
421- root = to_cxl_port ( port -> dev . parent );
422- while (! is_cxl_root ( root ) && is_cxl_port ( root -> dev . parent ))
423- root = to_cxl_port ( root -> dev . parent );
424- if (! is_cxl_root ( root )) {
425- dev_err ( dev , "Failed to acquire root port for HDM enable\n" );
419+ /*
420+ * If the HDM Decoder Capability does not exist and DVSEC was
421+ * not setup, the DVSEC based emulation cannot be used.
422+ */
423+ if (! hdm )
426424 return - ENODEV ;
427- }
428425
426+ /* The HDM Decoder Capability exists but is globally disabled. */
427+
428+ /*
429+ * If the DVSEC CXL Range registers are not enabled, just
430+ * enable and use the HDM Decoder Capability registers.
431+ */
429432 if (!info -> mem_enabled ) {
430433 rc = devm_cxl_enable_hdm (& port -> dev , cxlhdm );
431434 if (rc )
@@ -434,6 +437,26 @@ int cxl_hdm_decode_init(struct cxl_dev_state *cxlds, struct cxl_hdm *cxlhdm,
434437 return devm_cxl_enable_mem (& port -> dev , cxlds );
435438 }
436439
440+ /*
441+ * Per CXL 2.0 Section 8.1.3.8.3 and 8.1.3.8.4 DVSEC CXL Range 1 Base
442+ * [High,Low] when HDM operation is enabled the range register values
443+ * are ignored by the device, but the spec also recommends matching the
444+ * DVSEC Range 1,2 to HDM Decoder Range 0,1. So, non-zero info->ranges
445+ * are expected even though Linux does not require or maintain that
446+ * match. Check if at least one DVSEC range is enabled and allowed by
447+ * the platform. That is, the DVSEC range must be covered by a locked
448+ * platform window (CFMWS). Fail otherwise as the endpoint's decoders
449+ * cannot be used.
450+ */
451+
452+ root = to_cxl_port (port -> dev .parent );
453+ while (!is_cxl_root (root ) && is_cxl_port (root -> dev .parent ))
454+ root = to_cxl_port (root -> dev .parent );
455+ if (!is_cxl_root (root )) {
456+ dev_err (dev , "Failed to acquire root port for HDM enable\n" );
457+ return - ENODEV ;
458+ }
459+
437460 for (i = 0 , allowed = 0 ; i < info -> ranges ; i ++ ) {
438461 struct device * cxld_dev ;
439462
@@ -453,15 +476,6 @@ int cxl_hdm_decode_init(struct cxl_dev_state *cxlds, struct cxl_hdm *cxlhdm,
453476 return - ENXIO ;
454477 }
455478
456- /*
457- * Per CXL 2.0 Section 8.1.3.8.3 and 8.1.3.8.4 DVSEC CXL Range 1 Base
458- * [High,Low] when HDM operation is enabled the range register values
459- * are ignored by the device, but the spec also recommends matching the
460- * DVSEC Range 1,2 to HDM Decoder Range 0,1. So, non-zero info->ranges
461- * are expected even though Linux does not require or maintain that
462- * match. If at least one DVSEC range is enabled and allowed, skip HDM
463- * Decoder Capability Enable.
464- */
465479 return 0 ;
466480}
467481EXPORT_SYMBOL_NS_GPL (cxl_hdm_decode_init , "CXL" );
0 commit comments