@@ -130,8 +130,9 @@ static bool cxl_hdm_find_target(uint32_t *cache_mem, hwaddr addr,
static PCIDevice *cxl_cfmws_find_device(CXLFixedWindow *fw, hwaddr addr)
{
- CXLComponentState *hb_cstate;
+ CXLComponentState *hb_cstate, *usp_cstate;
PCIHostState *hb;
+ CXLUpstreamPort *usp;
int rb_index;
uint32_t *cache_mem;
uint8_t target;
@@ -166,7 +167,46 @@ static PCIDevice *cxl_cfmws_find_device(CXLFixedWindow *fw, hwaddr addr)
d = pci_bridge_get_sec_bus(PCI_BRIDGE(rp))->devices[0];
- if (!d || !object_dynamic_cast(OBJECT(d), TYPE_CXL_TYPE3_DEV)) {
+ if (!d) {
+ return NULL;
+ }
+
+ if (object_dynamic_cast(OBJECT(d), TYPE_CXL_TYPE3_DEV)) {
+ return d;
+ }
+
+ /*
+ * Could also be a switch. Note only one level of switching currently
+ * supported.
+ */
+ if (!object_dynamic_cast(OBJECT(d), TYPE_CXL_USP)) {
+ return NULL;
+ }
+ usp = CXL_USP(d);
+
+ usp_cstate = cxl_usp_to_cstate(usp);
+ if (!usp_cstate) {
+ return NULL;
+ }
+
+ cache_mem = usp_cstate->crb.cache_mem_registers;
+
+ target_found = cxl_hdm_find_target(cache_mem, addr, &target);
+ if (!target_found) {
+ return NULL;
+ }
+
+ d = pcie_find_port_by_pn(&PCI_BRIDGE(d)->sec_bus, target);
+ if (!d) {
+ return NULL;
+ }
+
+ d = pci_bridge_get_sec_bus(PCI_BRIDGE(d))->devices[0];
+ if (!d) {
+ return NULL;
+ }
+
+ if (!object_dynamic_cast(OBJECT(d), TYPE_CXL_TYPE3_DEV)) {
return NULL;
}
Extend the walk of the CXL bus during interleave decoding to take into account one layer of switches. Whilst theoretically CXL 2.0 allows multiple switch levels, in the vast majority of usecases only one level is expected and currently that is all the proposed Linux support provides. Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com> --- hw/cxl/cxl-host.c | 44 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 2 deletions(-)