xref: /openbmc/qemu/hw/pci-bridge/cxl_downstream.c (revision feb58e3b)
1 /*
2  * Emulated CXL Switch Downstream Port
3  *
4  * Copyright (c) 2022 Huawei Technologies.
5  *
6  * Based on xio3130_downstream.c
7  *
8  * SPDX-License-Identifier: GPL-2.0-or-later
9  */
10 
11 #include "qemu/osdep.h"
12 #include "qemu/log.h"
13 #include "hw/pci/msi.h"
14 #include "hw/pci/pcie.h"
15 #include "hw/pci/pcie_port.h"
16 #include "hw/qdev-properties.h"
17 #include "hw/qdev-properties-system.h"
18 #include "hw/cxl/cxl.h"
19 #include "qapi/error.h"
20 
21 typedef struct CXLDownstreamPort {
22     /*< private >*/
23     PCIESlot parent_obj;
24 
25     /*< public >*/
26     CXLComponentState cxl_cstate;
27 } CXLDownstreamPort;
28 
29 #define CXL_DOWNSTREAM_PORT_MSI_OFFSET 0x70
30 #define CXL_DOWNSTREAM_PORT_MSI_NR_VECTOR 1
31 #define CXL_DOWNSTREAM_PORT_EXP_OFFSET 0x90
32 #define CXL_DOWNSTREAM_PORT_AER_OFFSET 0x100
33 #define CXL_DOWNSTREAM_PORT_DVSEC_OFFSET        \
34     (CXL_DOWNSTREAM_PORT_AER_OFFSET + PCI_ERR_SIZEOF)
35 
36 static void latch_registers(CXLDownstreamPort *dsp)
37 {
38     uint32_t *reg_state = dsp->cxl_cstate.crb.cache_mem_registers;
39     uint32_t *write_msk = dsp->cxl_cstate.crb.cache_mem_regs_write_mask;
40 
41     cxl_component_register_init_common(reg_state, write_msk,
42                                        CXL2_DOWNSTREAM_PORT);
43 }
44 
45 /* TODO: Look at sharing this code across all CXL port types */
46 static void cxl_dsp_dvsec_write_config(PCIDevice *dev, uint32_t addr,
47                                       uint32_t val, int len)
48 {
49     CXLDownstreamPort *dsp = CXL_DSP(dev);
50     CXLComponentState *cxl_cstate = &dsp->cxl_cstate;
51 
52     if (range_contains(&cxl_cstate->dvsecs[EXTENSIONS_PORT_DVSEC], addr)) {
53         uint8_t *reg = &dev->config[addr];
54         addr -= cxl_cstate->dvsecs[EXTENSIONS_PORT_DVSEC].lob;
55         if (addr == PORT_CONTROL_OFFSET) {
56             if (pci_get_word(reg) & PORT_CONTROL_UNMASK_SBR) {
57                 /* unmask SBR */
58                 qemu_log_mask(LOG_UNIMP, "SBR mask control is not supported\n");
59             }
60             if (pci_get_word(reg) & PORT_CONTROL_ALT_MEMID_EN) {
61                 /* Alt Memory & ID Space Enable */
62                 qemu_log_mask(LOG_UNIMP,
63                               "Alt Memory & ID space is not supported\n");
64 
65             }
66         }
67     }
68 }
69 
70 static void cxl_dsp_config_write(PCIDevice *d, uint32_t address,
71                                  uint32_t val, int len)
72 {
73     uint16_t slt_ctl, slt_sta;
74 
75     pcie_cap_slot_get(d, &slt_ctl, &slt_sta);
76     pci_bridge_write_config(d, address, val, len);
77     pcie_cap_flr_write_config(d, address, val, len);
78     pcie_cap_slot_write_config(d, slt_ctl, slt_sta, address, val, len);
79     pcie_aer_write_config(d, address, val, len);
80 
81     cxl_dsp_dvsec_write_config(d, address, val, len);
82 }
83 
84 static void cxl_dsp_reset(DeviceState *qdev)
85 {
86     PCIDevice *d = PCI_DEVICE(qdev);
87     CXLDownstreamPort *dsp = CXL_DSP(qdev);
88 
89     pcie_cap_deverr_reset(d);
90     pcie_cap_slot_reset(d);
91     pcie_cap_arifwd_reset(d);
92     pci_bridge_reset(qdev);
93 
94     latch_registers(dsp);
95 }
96 
97 static void build_dvsecs(CXLComponentState *cxl)
98 {
99     uint8_t *dvsec;
100 
101     dvsec = (uint8_t *)&(CXLDVSECPortExt){ 0 };
102     cxl_component_create_dvsec(cxl, CXL2_DOWNSTREAM_PORT,
103                                EXTENSIONS_PORT_DVSEC_LENGTH,
104                                EXTENSIONS_PORT_DVSEC,
105                                EXTENSIONS_PORT_DVSEC_REVID, dvsec);
106 
107     dvsec = (uint8_t *)&(CXLDVSECPortFlexBus){
108         .cap                     = 0x27, /* Cache, IO, Mem, non-MLD */
109         .ctrl                    = 0x02, /* IO always enabled */
110         .status                  = 0x26, /* same */
111         .rcvd_mod_ts_data_phase1 = 0xef, /* WTF? */
112     };
113     cxl_component_create_dvsec(cxl, CXL2_DOWNSTREAM_PORT,
114                                PCIE_CXL3_FLEXBUS_PORT_DVSEC_LENGTH,
115                                PCIE_FLEXBUS_PORT_DVSEC,
116                                PCIE_CXL3_FLEXBUS_PORT_DVSEC_REVID, dvsec);
117 
118     dvsec = (uint8_t *)&(CXLDVSECPortGPF){
119         .rsvd        = 0,
120         .phase1_ctrl = 1, /* 1μs timeout */
121         .phase2_ctrl = 1, /* 1μs timeout */
122     };
123     cxl_component_create_dvsec(cxl, CXL2_DOWNSTREAM_PORT,
124                                GPF_PORT_DVSEC_LENGTH, GPF_PORT_DVSEC,
125                                GPF_PORT_DVSEC_REVID, dvsec);
126 
127     dvsec = (uint8_t *)&(CXLDVSECRegisterLocator){
128         .rsvd         = 0,
129         .reg0_base_lo = RBI_COMPONENT_REG | CXL_COMPONENT_REG_BAR_IDX,
130         .reg0_base_hi = 0,
131     };
132     cxl_component_create_dvsec(cxl, CXL2_DOWNSTREAM_PORT,
133                                REG_LOC_DVSEC_LENGTH, REG_LOC_DVSEC,
134                                REG_LOC_DVSEC_REVID, dvsec);
135 }
136 
137 static void cxl_dsp_realize(PCIDevice *d, Error **errp)
138 {
139     PCIEPort *p = PCIE_PORT(d);
140     PCIESlot *s = PCIE_SLOT(d);
141     CXLDownstreamPort *dsp = CXL_DSP(d);
142     CXLComponentState *cxl_cstate = &dsp->cxl_cstate;
143     ComponentRegisters *cregs = &cxl_cstate->crb;
144     MemoryRegion *component_bar = &cregs->component_registers;
145     int rc;
146 
147     pci_bridge_initfn(d, TYPE_PCIE_BUS);
148     pcie_port_init_reg(d);
149 
150     rc = msi_init(d, CXL_DOWNSTREAM_PORT_MSI_OFFSET,
151                   CXL_DOWNSTREAM_PORT_MSI_NR_VECTOR,
152                   true, true, errp);
153     if (rc) {
154         assert(rc == -ENOTSUP);
155         goto err_bridge;
156     }
157 
158     rc = pcie_cap_init(d, CXL_DOWNSTREAM_PORT_EXP_OFFSET,
159                        PCI_EXP_TYPE_DOWNSTREAM, p->port,
160                        errp);
161     if (rc < 0) {
162         goto err_msi;
163     }
164 
165     pcie_cap_flr_init(d);
166     pcie_cap_deverr_init(d);
167     pcie_cap_slot_init(d, s);
168     pcie_cap_arifwd_init(d);
169 
170     pcie_chassis_create(s->chassis);
171     rc = pcie_chassis_add_slot(s);
172     if (rc < 0) {
173         error_setg(errp, "Can't add chassis slot, error %d", rc);
174         goto err_pcie_cap;
175     }
176 
177     rc = pcie_aer_init(d, PCI_ERR_VER, CXL_DOWNSTREAM_PORT_AER_OFFSET,
178                        PCI_ERR_SIZEOF, errp);
179     if (rc < 0) {
180         goto err_chassis;
181     }
182 
183     cxl_cstate->dvsec_offset = CXL_DOWNSTREAM_PORT_DVSEC_OFFSET;
184     cxl_cstate->pdev = d;
185     build_dvsecs(cxl_cstate);
186     cxl_component_register_block_init(OBJECT(d), cxl_cstate, TYPE_CXL_DSP);
187     pci_register_bar(d, CXL_COMPONENT_REG_BAR_IDX,
188                      PCI_BASE_ADDRESS_SPACE_MEMORY |
189                          PCI_BASE_ADDRESS_MEM_TYPE_64,
190                      component_bar);
191 
192     return;
193 
194  err_chassis:
195     pcie_chassis_del_slot(s);
196  err_pcie_cap:
197     pcie_cap_exit(d);
198  err_msi:
199     msi_uninit(d);
200  err_bridge:
201     pci_bridge_exitfn(d);
202 }
203 
204 static void cxl_dsp_exitfn(PCIDevice *d)
205 {
206     PCIESlot *s = PCIE_SLOT(d);
207 
208     pcie_aer_exit(d);
209     pcie_chassis_del_slot(s);
210     pcie_cap_exit(d);
211     msi_uninit(d);
212     pci_bridge_exitfn(d);
213 }
214 
215 static Property cxl_dsp_props[] = {
216     DEFINE_PROP_PCIE_LINK_SPEED("x-speed", PCIESlot,
217                                 speed, PCIE_LINK_SPEED_64),
218     DEFINE_PROP_PCIE_LINK_WIDTH("x-width", PCIESlot,
219                                 width, PCIE_LINK_WIDTH_16),
220     DEFINE_PROP_END_OF_LIST()
221 };
222 
223 static void cxl_dsp_class_init(ObjectClass *oc, void *data)
224 {
225     DeviceClass *dc = DEVICE_CLASS(oc);
226     PCIDeviceClass *k = PCI_DEVICE_CLASS(oc);
227 
228     device_class_set_props(dc, cxl_dsp_props);
229     k->config_write = cxl_dsp_config_write;
230     k->realize = cxl_dsp_realize;
231     k->exit = cxl_dsp_exitfn;
232     k->vendor_id = 0x19e5; /* Huawei */
233     k->device_id = 0xa129; /* Emulated CXL Switch Downstream Port */
234     k->revision = 0;
235     set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
236     dc->desc = "CXL Switch Downstream Port";
237     device_class_set_legacy_reset(dc, cxl_dsp_reset);
238 }
239 
240 static const TypeInfo cxl_dsp_info = {
241     .name = TYPE_CXL_DSP,
242     .instance_size = sizeof(CXLDownstreamPort),
243     .parent = TYPE_PCIE_SLOT,
244     .class_init = cxl_dsp_class_init,
245     .interfaces = (InterfaceInfo[]) {
246         { INTERFACE_PCIE_DEVICE },
247         { INTERFACE_CXL_DEVICE },
248         { }
249     },
250 };
251 
252 static void cxl_dsp_register_type(void)
253 {
254     type_register_static(&cxl_dsp_info);
255 }
256 
257 type_init(cxl_dsp_register_type);
258