From: Zhi Wang <[email protected]>

Emulate the component registers of the CXL type-2 device with HDM
decoders, and its CXL DVSECs: the PCIe DVSEC for CXL Devices, the
Register Locator DVSEC and the Flex Bus Port DVSEC.

The DVSEC values are refreshed on every reset, so that registers such
as DVSEC Control return to their defaults across a system reset.

Co-developed-by: Ira Weiny <[email protected]>
Signed-off-by: Ira Weiny <[email protected]>
Signed-off-by: Zhi Wang <[email protected]>
[ clg: rebased on upstream ]
Signed-off-by: Cédric Le Goater <[email protected]>
[ mhonap: - second part of the original patch, renamed to cxl-type2
          - device DVSEC: CXL.io, CXL.mem, HwInit mode and one HDM
            range, with the bit names from cxl_pci.h; CXL.cache is
            added together with the cache flush support later
          - device DVSEC Status2 left at 0 and Control2 read-only, the
            device has no CXL Reset
          - CONFIG_LOCK makes the RWL fields read only until reset
          - one HDM decoder
          - fix "DVS Control" to "DVSEC Control" in the message ]
AI-used-for: code (prototype)
Signed-off-by: Manish Honap <[email protected]>
---
 hw/cxl/cxl-component-utils.c   |   2 +
 hw/mem/cxl_type2.c             | 121 +++++++++++++++++++++++++++++++++
 include/hw/cxl/cxl_component.h |   1 +
 include/hw/cxl/cxl_device.h    |   2 +
 4 files changed, 126 insertions(+)

diff --git a/hw/cxl/cxl-component-utils.c b/hw/cxl/cxl-component-utils.c
index f4a7fdb425..f9bb1684fa 100644
--- a/hw/cxl/cxl-component-utils.c
+++ b/hw/cxl/cxl-component-utils.c
@@ -362,6 +362,7 @@ static void hdm_init_common(uint32_t *reg_state, uint32_t 
*write_msk,
         write_msk[R_CXL_HDM_DECODER0_CTRL + i * hdm_inc] = 0x13ff;
         if (type == CXL2_DEVICE ||
             type == CXL2_TYPE3_DEVICE ||
+            type == CXL3_TYPE2_DEVICE ||
             type == CXL2_LOGICAL_DEVICE) {
             write_msk[R_CXL_HDM_DECODER0_TARGET_LIST_LO + i * hdm_inc] =
                 0xf0000000;
@@ -449,6 +450,7 @@ void cxl_component_register_init_count(uint32_t *reg_state,
         /* fallthrough */
     case CXL2_UPSTREAM_PORT:
     case CXL2_TYPE3_DEVICE:
+    case CXL3_TYPE2_DEVICE:
     case CXL2_LOGICAL_DEVICE:
         /* + HDM */
         init_cap_reg(HDM, 5, 1);
diff --git a/hw/mem/cxl_type2.c b/hw/mem/cxl_type2.c
index 4769793e06..b179d1e884 100644
--- a/hw/mem/cxl_type2.c
+++ b/hw/mem/cxl_type2.c
@@ -48,6 +48,109 @@ static bool setup_memory(PCIDevice *pci_dev, Error **errp)
     return true;
 }
 
+static void update_dvsecs(CXLType2Dev *ct2d)
+{
+    CXLComponentState *cxl_cstate = &ct2d->cxl_cstate;
+    uint64_t size = memory_region_size(
+        host_memory_backend_get_memory(ct2d->hostvmem));
+    uint8_t *dvsec;
+
+    dvsec = (uint8_t *)&(CXLDVSECDevice){
+        .cap = CXL_DVSEC_CAP_IO | CXL_DVSEC_CAP_MEM |
+               CXL_DVSEC_CAP_MEM_HWINIT |
+               1 << CXL_DVSEC_CAP_HDM_COUNT_SHIFT,
+        .ctrl = CXL_DVSEC_CTRL_IO_EN,
+        .range1_size_hi = size >> 32,
+        .range1_size_lo = (2 << 5) | (2 << 2) | 0x3 | (size & 0xF0000000),
+    };
+    cxl_component_update_dvsec(cxl_cstate, PCIE_CXL_DEVICE_DVSEC_LENGTH,
+                               PCIE_CXL_DEVICE_DVSEC, dvsec);
+
+    dvsec = (uint8_t *)&(CXLDVSECRegisterLocator){
+        .reg0_base_lo = RBI_COMPONENT_REG | CXL_COMPONENT_REG_BAR_IDX,
+    };
+    cxl_component_update_dvsec(cxl_cstate, REG_LOC_DVSEC_LENGTH,
+                               REG_LOC_DVSEC, dvsec);
+
+    dvsec = (uint8_t *)&(CXLDVSECPortFlexBus){
+        .cap                     = 0x26, /* 68B, IO, Mem, non-MLD */
+        .ctrl                    = 0x02, /* IO always enabled */
+        .status                  = 0x26, /* same as capabilities */
+        .rcvd_mod_ts_data_phase1 = 0xef,
+    };
+    cxl_component_update_dvsec(cxl_cstate, PCIE_CXL3_FLEXBUS_PORT_DVSEC_LENGTH,
+                               PCIE_FLEXBUS_PORT_DVSEC, dvsec);
+}
+
+static void build_dvsecs(CXLType2Dev *ct2d)
+{
+    CXLComponentState *cxl_cstate = &ct2d->cxl_cstate;
+
+    cxl_component_create_dvsec(cxl_cstate, CXL3_TYPE2_DEVICE,
+                               PCIE_CXL_DEVICE_DVSEC_LENGTH,
+                               PCIE_CXL_DEVICE_DVSEC,
+                               PCIE_CXL31_DEVICE_DVSEC_REVID, NULL);
+    cxl_component_create_dvsec(cxl_cstate, CXL3_TYPE2_DEVICE,
+                               REG_LOC_DVSEC_LENGTH, REG_LOC_DVSEC,
+                               REG_LOC_DVSEC_REVID, NULL);
+    cxl_component_create_dvsec(cxl_cstate, CXL3_TYPE2_DEVICE,
+                               PCIE_CXL3_FLEXBUS_PORT_DVSEC_LENGTH,
+                               PCIE_FLEXBUS_PORT_DVSEC,
+                               PCIE_CXL3_FLEXBUS_PORT_DVSEC_REVID, NULL);
+    update_dvsecs(ct2d);
+
+    /* No CXL.cache and no CXL Reset, so no Control2 bit is writable */
+    pci_set_word(PCI_DEVICE(ct2d)->wmask +
+                 range_lob(&cxl_cstate->dvsecs[PCIE_CXL_DEVICE_DVSEC]) +
+                 offsetof(CXLDVSECDevice, ctrl2), 0);
+
+    memcpy(ct2d->dvsec_wmask, PCI_DEVICE(ct2d)->wmask +
+           range_lob(&cxl_cstate->dvsecs[PCIE_CXL_DEVICE_DVSEC]),
+           sizeof(ct2d->dvsec_wmask));
+}
+
+/* CXL r4.0 Section 8.1.3.6: CONFIG_LOCK makes the RWL fields read only */
+static void cxl_type2_dvsec_lock(CXLType2Dev *ct2d)
+{
+    PCIDevice *pci_dev = PCI_DEVICE(ct2d);
+    uint16_t off = range_lob(&ct2d->cxl_cstate.dvsecs[PCIE_CXL_DEVICE_DVSEC]);
+    uint8_t *wmask = pci_dev->wmask + off;
+
+    if (!(pci_get_word(pci_dev->config + off + offsetof(CXLDVSECDevice, lock)) 
&
+          CXL_DVSEC_LOCK_CONFIG_LOCK)) {
+        return;
+    }
+    pci_set_word(wmask + offsetof(CXLDVSECDevice, ctrl), 0);
+    pci_set_word(wmask + offsetof(CXLDVSECDevice, lock), 0);
+    pci_set_quad(wmask + offsetof(CXLDVSECDevice, range1_base_hi), 0);
+    pci_set_quad(wmask + offsetof(CXLDVSECDevice, range2_base_hi), 0);
+}
+
+static void cxl_type2_config_write(PCIDevice *pci_dev, uint32_t addr,
+                                   uint32_t val, int len)
+{
+    pci_default_write_config(pci_dev, addr, val, len);
+    cxl_type2_dvsec_lock(CXL_TYPE2(pci_dev));
+}
+
+static void setup_cxl_regs(PCIDevice *pci_dev)
+{
+    CXLType2Dev *ct2d = CXL_TYPE2(pci_dev);
+    CXLComponentState *cxl_cstate = &ct2d->cxl_cstate;
+
+    cxl_cstate->dvsec_offset = 0x100;
+    cxl_cstate->pdev = pci_dev;
+
+    build_dvsecs(ct2d);
+
+    cxl_component_register_block_init(OBJECT(pci_dev), cxl_cstate,
+                                      TYPE_CXL_TYPE2);
+    pci_register_bar(pci_dev, CXL_COMPONENT_REG_BAR_IDX,
+                     PCI_BASE_ADDRESS_SPACE_MEMORY |
+                     PCI_BASE_ADDRESS_MEM_TYPE_64,
+                     &cxl_cstate->crb.component_registers);
+}
+
 static void cxl_type2_realize(PCIDevice *pci_dev, Error **errp)
 {
     if (!setup_memory(pci_dev, errp)) {
@@ -55,6 +158,8 @@ static void cxl_type2_realize(PCIDevice *pci_dev, Error 
**errp)
     }
 
     pcie_endpoint_cap_init(pci_dev, 0x80);
+
+    setup_cxl_regs(pci_dev);
 }
 
 static void cxl_type2_exit(PCIDevice *pci_dev)
@@ -62,6 +167,20 @@ static void cxl_type2_exit(PCIDevice *pci_dev)
     clean_memory(pci_dev);
 }
 
+static void cxl_type2_reset(DeviceState *dev)
+{
+    CXLType2Dev *ct2d = CXL_TYPE2(dev);
+    CXLComponentState *cxl_cstate = &ct2d->cxl_cstate;
+
+    update_dvsecs(ct2d);
+    memcpy(PCI_DEVICE(ct2d)->wmask +
+           range_lob(&cxl_cstate->dvsecs[PCIE_CXL_DEVICE_DVSEC]),
+           ct2d->dvsec_wmask, sizeof(ct2d->dvsec_wmask));
+    cxl_component_register_init_count(cxl_cstate->crb.cache_mem_registers,
+                                      
cxl_cstate->crb.cache_mem_regs_write_mask,
+                                      CXL3_TYPE2_DEVICE, false, 1);
+}
+
 static const Property cxl_type2_props[] = {
     DEFINE_PROP_LINK("volatile-memdev", CXLType2Dev, hostvmem,
                      TYPE_MEMORY_BACKEND, HostMemoryBackend *),
@@ -79,6 +198,7 @@ static void cxl_type2_class_init(ObjectClass *oc, const void 
*data)
 
     pc->realize = cxl_type2_realize;
     pc->exit = cxl_type2_exit;
+    pc->config_write = cxl_type2_config_write;
 
     pc->class_id = PCI_CLASS_ACCELERATOR_PROCESSING;
     pc->vendor_id = PCI_VENDOR_ID_REDHAT;
@@ -88,6 +208,7 @@ static void cxl_type2_class_init(ObjectClass *oc, const void 
*data)
     set_bit(DEVICE_CATEGORY_MISC, dc->categories);
     dc->desc = "CXL Type-2 device";
     dc->vmsd = &cxl_type2_vmsd;
+    device_class_set_legacy_reset(dc, cxl_type2_reset);
     device_class_set_props(dc, cxl_type2_props);
 }
 
diff --git a/include/hw/cxl/cxl_component.h b/include/hw/cxl/cxl_component.h
index 3ed8dab92e..b38e81d129 100644
--- a/include/hw/cxl/cxl_component.h
+++ b/include/hw/cxl/cxl_component.h
@@ -29,6 +29,7 @@ enum reg_type {
     CXL2_UPSTREAM_PORT,
     CXL2_DOWNSTREAM_PORT,
     CXL3_SWITCH_MAILBOX_CCI,
+    CXL3_TYPE2_DEVICE,
 };
 
 /*
diff --git a/include/hw/cxl/cxl_device.h b/include/hw/cxl/cxl_device.h
index aeccf42855..9c83421d0f 100644
--- a/include/hw/cxl/cxl_device.h
+++ b/include/hw/cxl/cxl_device.h
@@ -839,6 +839,8 @@ struct CXLType2Dev {
 
     /* State */
     AddressSpace hostvmem_as;
+    CXLComponentState cxl_cstate;
+    uint8_t dvsec_wmask[PCIE_CXL_DEVICE_DVSEC_LENGTH];
 };
 
 #define TYPE_CXL_TYPE2 "cxl-type2"
-- 
2.53.0


Reply via email to