Import QEMU upstream snapshot d2e570c
Upstream: https://gitlab.com/qemu-project/qemu.git Upstream-Commit: d2e570cc0f97b936902a5b1b86b73c0f5998b475
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
config PCI_BRIDGE
|
||||
bool
|
||||
default y if PCI_DEVICES
|
||||
depends on PCI
|
||||
|
||||
config PCIE_PORT
|
||||
bool
|
||||
default y if PCI_DEVICES
|
||||
depends on PCI_EXPRESS && MSI_NONBROKEN
|
||||
|
||||
config PCIE_PCI_BRIDGE
|
||||
bool
|
||||
default y if PCIE_PORT
|
||||
depends on PCIE_PORT
|
||||
|
||||
config PXB
|
||||
bool
|
||||
default y if Q35 || ARM_VIRT
|
||||
|
||||
config XIO3130
|
||||
bool
|
||||
default y if PCI_DEVICES
|
||||
depends on PCI_EXPRESS && MSI_NONBROKEN
|
||||
|
||||
config IOH3420
|
||||
bool
|
||||
default y if PCI_DEVICES
|
||||
depends on PCI_EXPRESS && MSI_NONBROKEN
|
||||
|
||||
config I82801B11
|
||||
bool
|
||||
default y if PCI_DEVICES
|
||||
depends on PCI_EXPRESS
|
||||
|
||||
config DEC_PCI
|
||||
bool
|
||||
|
||||
config SIMBA
|
||||
bool
|
||||
|
||||
config CXL
|
||||
bool
|
||||
default y if PCI_EXPRESS && PXB
|
||||
depends on PCI_EXPRESS && MSI_NONBROKEN && PXB
|
||||
@@ -0,0 +1,267 @@
|
||||
/*
|
||||
* Emulated CXL Switch Downstream Port
|
||||
*
|
||||
* Copyright (c) 2022 Huawei Technologies.
|
||||
*
|
||||
* Based on xio3130_downstream.c
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/log.h"
|
||||
#include "hw/pci/msi.h"
|
||||
#include "hw/pci/pcie.h"
|
||||
#include "hw/pci/pcie_port.h"
|
||||
#include "hw/pci-bridge/cxl_downstream_port.h"
|
||||
#include "hw/core/qdev-properties.h"
|
||||
#include "hw/core/qdev-properties-system.h"
|
||||
#include "hw/cxl/cxl.h"
|
||||
#include "hw/cxl/cxl_port.h"
|
||||
#include "qapi/error.h"
|
||||
|
||||
typedef struct CXLDownstreamPort {
|
||||
/*< private >*/
|
||||
PCIESlot parent_obj;
|
||||
|
||||
/*< public >*/
|
||||
CXLComponentState cxl_cstate;
|
||||
CXLPhyPortPerst perst;
|
||||
} CXLDownstreamPort;
|
||||
|
||||
#define CXL_DOWNSTREAM_PORT_MSI_OFFSET 0x70
|
||||
#define CXL_DOWNSTREAM_PORT_MSI_NR_VECTOR 1
|
||||
#define CXL_DOWNSTREAM_PORT_EXP_OFFSET 0x90
|
||||
#define CXL_DOWNSTREAM_PORT_AER_OFFSET 0x100
|
||||
#define CXL_DOWNSTREAM_PORT_DVSEC_OFFSET \
|
||||
(CXL_DOWNSTREAM_PORT_AER_OFFSET + PCI_ERR_SIZEOF)
|
||||
|
||||
static void latch_registers(CXLDownstreamPort *dsp)
|
||||
{
|
||||
uint32_t *reg_state = dsp->cxl_cstate.crb.cache_mem_registers;
|
||||
uint32_t *write_msk = dsp->cxl_cstate.crb.cache_mem_regs_write_mask;
|
||||
|
||||
cxl_component_register_init_common(reg_state, write_msk,
|
||||
CXL2_DOWNSTREAM_PORT, true);
|
||||
}
|
||||
|
||||
/* TODO: Look at sharing this code across all CXL port types */
|
||||
static void cxl_dsp_dvsec_write_config(PCIDevice *dev, uint32_t addr,
|
||||
uint32_t val, int len)
|
||||
{
|
||||
CXLDownstreamPort *dsp = CXL_DSP(dev);
|
||||
CXLComponentState *cxl_cstate = &dsp->cxl_cstate;
|
||||
|
||||
if (range_contains(&cxl_cstate->dvsecs[EXTENSIONS_PORT_DVSEC], addr)) {
|
||||
uint8_t *reg = &dev->config[addr];
|
||||
addr -= cxl_cstate->dvsecs[EXTENSIONS_PORT_DVSEC].lob;
|
||||
if (addr == PORT_CONTROL_OFFSET) {
|
||||
if (pci_get_word(reg) & PORT_CONTROL_UNMASK_SBR) {
|
||||
/* unmask SBR */
|
||||
qemu_log_mask(LOG_UNIMP, "SBR mask control is not supported\n");
|
||||
}
|
||||
if (pci_get_word(reg) & PORT_CONTROL_ALT_MEMID_EN) {
|
||||
/* Alt Memory & ID Space Enable */
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"Alt Memory & ID space is not supported\n");
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void cxl_dsp_config_write(PCIDevice *d, uint32_t address,
|
||||
uint32_t val, int len)
|
||||
{
|
||||
uint16_t slt_ctl, slt_sta;
|
||||
|
||||
pcie_cap_slot_get(d, &slt_ctl, &slt_sta);
|
||||
pci_bridge_write_config(d, address, val, len);
|
||||
pcie_cap_flr_write_config(d, address, val, len);
|
||||
pcie_cap_slot_write_config(d, slt_ctl, slt_sta, address, val, len);
|
||||
pcie_aer_write_config(d, address, val, len);
|
||||
|
||||
cxl_dsp_dvsec_write_config(d, address, val, len);
|
||||
}
|
||||
|
||||
CXLPhyPortPerst *cxl_dsp_get_perst(CXLDownstreamPort *dsp)
|
||||
{
|
||||
return &dsp->perst;
|
||||
}
|
||||
|
||||
static void cxl_dsp_reset(DeviceState *qdev)
|
||||
{
|
||||
PCIDevice *d = PCI_DEVICE(qdev);
|
||||
CXLDownstreamPort *dsp = CXL_DSP(qdev);
|
||||
|
||||
pcie_cap_deverr_reset(d);
|
||||
pcie_cap_slot_reset(d);
|
||||
pcie_cap_arifwd_reset(d);
|
||||
pci_bridge_reset(qdev);
|
||||
|
||||
latch_registers(dsp);
|
||||
cxl_init_physical_port_control(&dsp->perst);
|
||||
}
|
||||
|
||||
static void build_dvsecs(PCIDevice *d, CXLComponentState *cxl)
|
||||
{
|
||||
PCIESlot *s = PCIE_SLOT(d);
|
||||
uint8_t *dvsec;
|
||||
|
||||
dvsec = (uint8_t *)&(CXLDVSECPortExt){ 0 };
|
||||
cxl_component_create_dvsec(cxl, CXL2_DOWNSTREAM_PORT,
|
||||
EXTENSIONS_PORT_DVSEC_LENGTH,
|
||||
EXTENSIONS_PORT_DVSEC,
|
||||
EXTENSIONS_PORT_DVSEC_REVID, dvsec);
|
||||
|
||||
dvsec = (uint8_t *)&(CXLDVSECPortFlexBus){
|
||||
.cap = 0x27, /* Cache, IO, Mem, non-MLD */
|
||||
.ctrl = 0x02, /* IO always enabled */
|
||||
.status = s->flitmode ? 0x6 : 0x26, /* lack of 68B */
|
||||
.rcvd_mod_ts_data_phase1 = 0xef, /* WTF? */
|
||||
};
|
||||
cxl_component_create_dvsec(cxl, CXL2_DOWNSTREAM_PORT,
|
||||
PCIE_CXL3_FLEXBUS_PORT_DVSEC_LENGTH,
|
||||
PCIE_FLEXBUS_PORT_DVSEC,
|
||||
PCIE_CXL3_FLEXBUS_PORT_DVSEC_REVID, dvsec);
|
||||
|
||||
dvsec = (uint8_t *)&(CXLDVSECPortGPF){
|
||||
.rsvd = 0,
|
||||
.phase1_ctrl = 1, /* 1μs timeout */
|
||||
.phase2_ctrl = 1, /* 1μs timeout */
|
||||
};
|
||||
cxl_component_create_dvsec(cxl, CXL2_DOWNSTREAM_PORT,
|
||||
GPF_PORT_DVSEC_LENGTH, GPF_PORT_DVSEC,
|
||||
GPF_PORT_DVSEC_REVID, dvsec);
|
||||
|
||||
dvsec = (uint8_t *)&(CXLDVSECRegisterLocator){
|
||||
.rsvd = 0,
|
||||
.reg0_base_lo = RBI_COMPONENT_REG | CXL_COMPONENT_REG_BAR_IDX,
|
||||
.reg0_base_hi = 0,
|
||||
};
|
||||
cxl_component_create_dvsec(cxl, CXL2_DOWNSTREAM_PORT,
|
||||
REG_LOC_DVSEC_LENGTH, REG_LOC_DVSEC,
|
||||
REG_LOC_DVSEC_REVID, dvsec);
|
||||
}
|
||||
|
||||
static void cxl_dsp_realize(PCIDevice *d, Error **errp)
|
||||
{
|
||||
PCIEPort *p = PCIE_PORT(d);
|
||||
PCIESlot *s = PCIE_SLOT(d);
|
||||
CXLDownstreamPort *dsp = CXL_DSP(d);
|
||||
CXLComponentState *cxl_cstate = &dsp->cxl_cstate;
|
||||
ComponentRegisters *cregs = &cxl_cstate->crb;
|
||||
MemoryRegion *component_bar = &cregs->component_registers;
|
||||
int rc;
|
||||
|
||||
pci_bridge_initfn(d, TYPE_PCIE_BUS);
|
||||
pcie_port_init_reg(d);
|
||||
|
||||
rc = msi_init(d, CXL_DOWNSTREAM_PORT_MSI_OFFSET,
|
||||
CXL_DOWNSTREAM_PORT_MSI_NR_VECTOR,
|
||||
true, true, errp);
|
||||
if (rc) {
|
||||
assert(rc == -ENOTSUP);
|
||||
goto err_bridge;
|
||||
}
|
||||
|
||||
rc = pcie_cap_init(d, CXL_DOWNSTREAM_PORT_EXP_OFFSET,
|
||||
PCI_EXP_TYPE_DOWNSTREAM, p->port,
|
||||
errp);
|
||||
if (rc < 0) {
|
||||
goto err_msi;
|
||||
}
|
||||
|
||||
pcie_cap_flr_init(d);
|
||||
pcie_cap_deverr_init(d);
|
||||
pcie_cap_slot_init(d, s);
|
||||
pcie_cap_arifwd_init(d);
|
||||
|
||||
pcie_chassis_create(s->chassis);
|
||||
rc = pcie_chassis_add_slot(s);
|
||||
if (rc < 0) {
|
||||
error_setg(errp, "Can't add chassis slot, error %d", rc);
|
||||
goto err_pcie_cap;
|
||||
}
|
||||
|
||||
rc = pcie_aer_init(d, PCI_ERR_VER, CXL_DOWNSTREAM_PORT_AER_OFFSET,
|
||||
PCI_ERR_SIZEOF, errp);
|
||||
if (rc < 0) {
|
||||
goto err_chassis;
|
||||
}
|
||||
|
||||
cxl_cstate->dvsec_offset = CXL_DOWNSTREAM_PORT_DVSEC_OFFSET;
|
||||
cxl_cstate->pdev = d;
|
||||
build_dvsecs(d, cxl_cstate);
|
||||
cxl_component_register_block_init(OBJECT(d), cxl_cstate, TYPE_CXL_DSP);
|
||||
pci_register_bar(d, CXL_COMPONENT_REG_BAR_IDX,
|
||||
PCI_BASE_ADDRESS_SPACE_MEMORY |
|
||||
PCI_BASE_ADDRESS_MEM_TYPE_64,
|
||||
component_bar);
|
||||
|
||||
return;
|
||||
|
||||
err_chassis:
|
||||
pcie_chassis_del_slot(s);
|
||||
err_pcie_cap:
|
||||
pcie_cap_exit(d);
|
||||
err_msi:
|
||||
msi_uninit(d);
|
||||
err_bridge:
|
||||
pci_bridge_exitfn(d);
|
||||
}
|
||||
|
||||
static void cxl_dsp_exitfn(PCIDevice *d)
|
||||
{
|
||||
PCIESlot *s = PCIE_SLOT(d);
|
||||
|
||||
pcie_aer_exit(d);
|
||||
pcie_chassis_del_slot(s);
|
||||
pcie_cap_exit(d);
|
||||
msi_uninit(d);
|
||||
pci_bridge_exitfn(d);
|
||||
}
|
||||
|
||||
static const Property cxl_dsp_props[] = {
|
||||
DEFINE_PROP_PCIE_LINK_SPEED("x-speed", PCIESlot,
|
||||
speed, PCIE_LINK_SPEED_64),
|
||||
DEFINE_PROP_PCIE_LINK_WIDTH("x-width", PCIESlot,
|
||||
width, PCIE_LINK_WIDTH_16),
|
||||
DEFINE_PROP_BOOL("x-256b-flit", PCIESlot, flitmode, true),
|
||||
};
|
||||
|
||||
static void cxl_dsp_class_init(ObjectClass *oc, const void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||
PCIDeviceClass *k = PCI_DEVICE_CLASS(oc);
|
||||
|
||||
device_class_set_props(dc, cxl_dsp_props);
|
||||
k->config_write = cxl_dsp_config_write;
|
||||
k->realize = cxl_dsp_realize;
|
||||
k->exit = cxl_dsp_exitfn;
|
||||
k->vendor_id = 0x19e5; /* Huawei */
|
||||
k->device_id = 0xa129; /* Emulated CXL Switch Downstream Port */
|
||||
k->revision = 0;
|
||||
set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
|
||||
dc->desc = "CXL Switch Downstream Port";
|
||||
device_class_set_legacy_reset(dc, cxl_dsp_reset);
|
||||
}
|
||||
|
||||
static const TypeInfo cxl_dsp_info = {
|
||||
.name = TYPE_CXL_DSP,
|
||||
.instance_size = sizeof(CXLDownstreamPort),
|
||||
.parent = TYPE_PCIE_SLOT,
|
||||
.class_init = cxl_dsp_class_init,
|
||||
.interfaces = (const InterfaceInfo[]) {
|
||||
{ INTERFACE_PCIE_DEVICE },
|
||||
{ INTERFACE_CXL_DEVICE },
|
||||
{ }
|
||||
},
|
||||
};
|
||||
|
||||
static void cxl_dsp_register_type(void)
|
||||
{
|
||||
type_register_static(&cxl_dsp_info);
|
||||
}
|
||||
|
||||
type_init(cxl_dsp_register_type);
|
||||
@@ -0,0 +1,311 @@
|
||||
/*
|
||||
* CXL 2.0 Root Port Implementation
|
||||
*
|
||||
* Copyright(C) 2020 Intel Corporation.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/log.h"
|
||||
#include "qemu/range.h"
|
||||
#include "hw/pci/pci_bridge.h"
|
||||
#include "hw/pci/pcie_port.h"
|
||||
#include "hw/pci/msi.h"
|
||||
#include "hw/core/qdev-properties.h"
|
||||
#include "hw/core/qdev-properties-system.h"
|
||||
#include "hw/core/sysbus.h"
|
||||
#include "qapi/error.h"
|
||||
#include "hw/cxl/cxl.h"
|
||||
|
||||
#define CXL_ROOT_PORT_DID 0x7075
|
||||
|
||||
#define CXL_RP_MSI_OFFSET 0x60
|
||||
#define CXL_RP_MSI_SUPPORTED_FLAGS PCI_MSI_FLAGS_MASKBIT
|
||||
#define CXL_RP_MSI_NR_VECTOR 2
|
||||
|
||||
/* Copied from the gen root port which we derive */
|
||||
#define GEN_PCIE_ROOT_PORT_AER_OFFSET 0x100
|
||||
#define GEN_PCIE_ROOT_PORT_ACS_OFFSET \
|
||||
(GEN_PCIE_ROOT_PORT_AER_OFFSET + PCI_ERR_SIZEOF)
|
||||
#define CXL_ROOT_PORT_DVSEC_OFFSET \
|
||||
(GEN_PCIE_ROOT_PORT_ACS_OFFSET + PCI_ACS_SIZEOF)
|
||||
|
||||
typedef struct CXLRootPort {
|
||||
/*< private >*/
|
||||
PCIESlot parent_obj;
|
||||
|
||||
CXLComponentState cxl_cstate;
|
||||
PCIResReserve res_reserve;
|
||||
} CXLRootPort;
|
||||
|
||||
#define TYPE_CXL_ROOT_PORT "cxl-rp"
|
||||
DECLARE_INSTANCE_CHECKER(CXLRootPort, CXL_ROOT_PORT, TYPE_CXL_ROOT_PORT)
|
||||
|
||||
/*
|
||||
* If two MSI vector are allocated, Advanced Error Interrupt Message Number
|
||||
* is 1. otherwise 0.
|
||||
* 17.12.5.10 RPERRSTS, 32:27 bit Advanced Error Interrupt Message Number.
|
||||
*/
|
||||
static uint8_t cxl_rp_aer_vector(const PCIDevice *d)
|
||||
{
|
||||
switch (msi_nr_vectors_allocated(d)) {
|
||||
case 1:
|
||||
return 0;
|
||||
case 2:
|
||||
return 1;
|
||||
case 4:
|
||||
case 8:
|
||||
case 16:
|
||||
case 32:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
abort();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cxl_rp_interrupts_init(PCIDevice *d, Error **errp)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = msi_init(d, CXL_RP_MSI_OFFSET, CXL_RP_MSI_NR_VECTOR,
|
||||
CXL_RP_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_64BIT,
|
||||
CXL_RP_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_MASKBIT,
|
||||
errp);
|
||||
if (rc < 0) {
|
||||
assert(rc == -ENOTSUP);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void cxl_rp_interrupts_uninit(PCIDevice *d)
|
||||
{
|
||||
msi_uninit(d);
|
||||
}
|
||||
|
||||
static void latch_registers(CXLRootPort *crp)
|
||||
{
|
||||
uint32_t *reg_state = crp->cxl_cstate.crb.cache_mem_registers;
|
||||
uint32_t *write_msk = crp->cxl_cstate.crb.cache_mem_regs_write_mask;
|
||||
|
||||
cxl_component_register_init_common(reg_state, write_msk, CXL2_ROOT_PORT,
|
||||
true);
|
||||
}
|
||||
|
||||
static void build_dvsecs(PCIDevice *d, CXLComponentState *cxl)
|
||||
{
|
||||
PCIESlot *s = PCIE_SLOT(d);
|
||||
uint8_t *dvsec;
|
||||
|
||||
dvsec = (uint8_t *)&(CXLDVSECPortExt){ 0 };
|
||||
cxl_component_create_dvsec(cxl, CXL2_ROOT_PORT,
|
||||
EXTENSIONS_PORT_DVSEC_LENGTH,
|
||||
EXTENSIONS_PORT_DVSEC,
|
||||
EXTENSIONS_PORT_DVSEC_REVID, dvsec);
|
||||
|
||||
dvsec = (uint8_t *)&(CXLDVSECPortGPF){
|
||||
.rsvd = 0,
|
||||
.phase1_ctrl = 1, /* 1μs timeout */
|
||||
.phase2_ctrl = 1, /* 1μs timeout */
|
||||
};
|
||||
cxl_component_create_dvsec(cxl, CXL2_ROOT_PORT,
|
||||
GPF_PORT_DVSEC_LENGTH, GPF_PORT_DVSEC,
|
||||
GPF_PORT_DVSEC_REVID, dvsec);
|
||||
|
||||
dvsec = (uint8_t *)&(CXLDVSECPortFlexBus){
|
||||
.cap = 0x26, /* IO, Mem, non-MLD */
|
||||
.ctrl = 0x2,
|
||||
.status = s->flitmode ? 0x6 : 0x26, /* lack of 68B */
|
||||
.rcvd_mod_ts_data_phase1 = 0xef,
|
||||
};
|
||||
cxl_component_create_dvsec(cxl, CXL2_ROOT_PORT,
|
||||
PCIE_CXL3_FLEXBUS_PORT_DVSEC_LENGTH,
|
||||
PCIE_FLEXBUS_PORT_DVSEC,
|
||||
PCIE_CXL3_FLEXBUS_PORT_DVSEC_REVID, dvsec);
|
||||
|
||||
dvsec = (uint8_t *)&(CXLDVSECRegisterLocator){
|
||||
.rsvd = 0,
|
||||
.reg0_base_lo = RBI_COMPONENT_REG | CXL_COMPONENT_REG_BAR_IDX,
|
||||
.reg0_base_hi = 0,
|
||||
};
|
||||
cxl_component_create_dvsec(cxl, CXL2_ROOT_PORT,
|
||||
REG_LOC_DVSEC_LENGTH, REG_LOC_DVSEC,
|
||||
REG_LOC_DVSEC_REVID, dvsec);
|
||||
}
|
||||
|
||||
static void cxl_rp_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
PCIDevice *pci_dev = PCI_DEVICE(dev);
|
||||
PCIERootPortClass *rpc = PCIE_ROOT_PORT_GET_CLASS(dev);
|
||||
CXLRootPort *crp = CXL_ROOT_PORT(dev);
|
||||
CXLComponentState *cxl_cstate = &crp->cxl_cstate;
|
||||
ComponentRegisters *cregs = &cxl_cstate->crb;
|
||||
MemoryRegion *component_bar = &cregs->component_registers;
|
||||
Error *local_err = NULL;
|
||||
|
||||
rpc->parent_realize(dev, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
|
||||
int rc =
|
||||
pci_bridge_qemu_reserve_cap_init(pci_dev, 0, crp->res_reserve, errp);
|
||||
if (rc < 0) {
|
||||
rpc->parent_class.exit(pci_dev);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!crp->res_reserve.io || crp->res_reserve.io == -1) {
|
||||
pci_word_test_and_clear_mask(pci_dev->wmask + PCI_COMMAND,
|
||||
PCI_COMMAND_IO);
|
||||
pci_dev->wmask[PCI_IO_BASE] = 0;
|
||||
pci_dev->wmask[PCI_IO_LIMIT] = 0;
|
||||
}
|
||||
|
||||
cxl_cstate->dvsec_offset = CXL_ROOT_PORT_DVSEC_OFFSET;
|
||||
cxl_cstate->pdev = pci_dev;
|
||||
build_dvsecs(pci_dev, cxl_cstate);
|
||||
|
||||
cxl_component_register_block_init(OBJECT(pci_dev), cxl_cstate,
|
||||
TYPE_CXL_ROOT_PORT);
|
||||
|
||||
pci_register_bar(pci_dev, CXL_COMPONENT_REG_BAR_IDX,
|
||||
PCI_BASE_ADDRESS_SPACE_MEMORY |
|
||||
PCI_BASE_ADDRESS_MEM_TYPE_64,
|
||||
component_bar);
|
||||
}
|
||||
|
||||
static void cxl_rp_reset_hold(Object *obj, ResetType type)
|
||||
{
|
||||
PCIERootPortClass *rpc = PCIE_ROOT_PORT_GET_CLASS(obj);
|
||||
CXLRootPort *crp = CXL_ROOT_PORT(obj);
|
||||
|
||||
if (rpc->parent_phases.hold) {
|
||||
rpc->parent_phases.hold(obj, type);
|
||||
}
|
||||
|
||||
latch_registers(crp);
|
||||
}
|
||||
|
||||
static const Property gen_rp_props[] = {
|
||||
DEFINE_PROP_UINT32("bus-reserve", CXLRootPort, res_reserve.bus, -1),
|
||||
DEFINE_PROP_SIZE("io-reserve", CXLRootPort, res_reserve.io, -1),
|
||||
DEFINE_PROP_SIZE("mem-reserve", CXLRootPort, res_reserve.mem_non_pref, -1),
|
||||
DEFINE_PROP_SIZE("pref32-reserve", CXLRootPort, res_reserve.mem_pref_32,
|
||||
-1),
|
||||
DEFINE_PROP_SIZE("pref64-reserve", CXLRootPort, res_reserve.mem_pref_64,
|
||||
-1),
|
||||
DEFINE_PROP_PCIE_LINK_SPEED("x-speed", PCIESlot,
|
||||
speed, PCIE_LINK_SPEED_64),
|
||||
DEFINE_PROP_PCIE_LINK_WIDTH("x-width", PCIESlot,
|
||||
width, PCIE_LINK_WIDTH_32),
|
||||
DEFINE_PROP_BOOL("x-256b-flit", PCIESlot, flitmode, true),
|
||||
};
|
||||
|
||||
static void cxl_rp_dvsec_write_config(PCIDevice *dev, uint32_t addr,
|
||||
uint32_t val, int len)
|
||||
{
|
||||
CXLRootPort *crp = CXL_ROOT_PORT(dev);
|
||||
|
||||
if (range_contains(&crp->cxl_cstate.dvsecs[EXTENSIONS_PORT_DVSEC], addr)) {
|
||||
uint8_t *reg = &dev->config[addr];
|
||||
addr -= crp->cxl_cstate.dvsecs[EXTENSIONS_PORT_DVSEC].lob;
|
||||
if (addr == PORT_CONTROL_OFFSET) {
|
||||
if (pci_get_word(reg) & PORT_CONTROL_UNMASK_SBR) {
|
||||
/* unmask SBR */
|
||||
qemu_log_mask(LOG_UNIMP, "SBR mask control is not supported\n");
|
||||
}
|
||||
if (pci_get_word(reg) & PORT_CONTROL_ALT_MEMID_EN) {
|
||||
/* Alt Memory & ID Space Enable */
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"Alt Memory & ID space is not supported\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void cxl_rp_aer_vector_update(PCIDevice *d)
|
||||
{
|
||||
PCIERootPortClass *rpc = PCIE_ROOT_PORT_GET_CLASS(d);
|
||||
|
||||
if (rpc->aer_vector) {
|
||||
pcie_aer_root_set_vector(d, rpc->aer_vector(d));
|
||||
}
|
||||
}
|
||||
|
||||
static void cxl_rp_write_config(PCIDevice *d, uint32_t address, uint32_t val,
|
||||
int len)
|
||||
{
|
||||
uint16_t slt_ctl, slt_sta;
|
||||
uint32_t root_cmd =
|
||||
pci_get_long(d->config + d->exp.aer_cap + PCI_ERR_ROOT_COMMAND);
|
||||
|
||||
pcie_cap_slot_get(d, &slt_ctl, &slt_sta);
|
||||
pci_bridge_write_config(d, address, val, len);
|
||||
cxl_rp_aer_vector_update(d);
|
||||
pcie_cap_flr_write_config(d, address, val, len);
|
||||
pcie_cap_slot_write_config(d, slt_ctl, slt_sta, address, val, len);
|
||||
pcie_aer_write_config(d, address, val, len);
|
||||
pcie_aer_root_write_config(d, address, val, len, root_cmd);
|
||||
|
||||
cxl_rp_dvsec_write_config(d, address, val, len);
|
||||
}
|
||||
|
||||
static void cxl_root_port_class_init(ObjectClass *oc, const void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||
PCIDeviceClass *k = PCI_DEVICE_CLASS(oc);
|
||||
ResettableClass *rc = RESETTABLE_CLASS(oc);
|
||||
PCIERootPortClass *rpc = PCIE_ROOT_PORT_CLASS(oc);
|
||||
|
||||
k->vendor_id = PCI_VENDOR_ID_INTEL;
|
||||
k->device_id = CXL_ROOT_PORT_DID;
|
||||
dc->desc = "CXL Root Port";
|
||||
k->revision = 0;
|
||||
device_class_set_props(dc, gen_rp_props);
|
||||
k->config_write = cxl_rp_write_config;
|
||||
|
||||
device_class_set_parent_realize(dc, cxl_rp_realize, &rpc->parent_realize);
|
||||
resettable_class_set_parent_phases(rc, NULL, cxl_rp_reset_hold, NULL,
|
||||
&rpc->parent_phases);
|
||||
|
||||
rpc->aer_offset = GEN_PCIE_ROOT_PORT_AER_OFFSET;
|
||||
rpc->acs_offset = GEN_PCIE_ROOT_PORT_ACS_OFFSET;
|
||||
rpc->aer_vector = cxl_rp_aer_vector;
|
||||
rpc->interrupts_init = cxl_rp_interrupts_init;
|
||||
rpc->interrupts_uninit = cxl_rp_interrupts_uninit;
|
||||
|
||||
dc->hotpluggable = false;
|
||||
}
|
||||
|
||||
static const TypeInfo cxl_root_port_info = {
|
||||
.name = TYPE_CXL_ROOT_PORT,
|
||||
.parent = TYPE_PCIE_ROOT_PORT,
|
||||
.instance_size = sizeof(CXLRootPort),
|
||||
.class_init = cxl_root_port_class_init,
|
||||
.interfaces = (const InterfaceInfo[]) {
|
||||
{ INTERFACE_CXL_DEVICE },
|
||||
{ }
|
||||
},
|
||||
};
|
||||
|
||||
static void cxl_register(void)
|
||||
{
|
||||
type_register_static(&cxl_root_port_info);
|
||||
}
|
||||
|
||||
type_init(cxl_register);
|
||||
@@ -0,0 +1,415 @@
|
||||
/*
|
||||
* Emulated CXL Switch Upstream Port
|
||||
*
|
||||
* Copyright (c) 2022 Huawei Technologies.
|
||||
*
|
||||
* Based on xio3130_upstream.c
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/log.h"
|
||||
#include "hw/core/qdev-properties.h"
|
||||
#include "hw/core/qdev-properties-system.h"
|
||||
#include "hw/pci/msi.h"
|
||||
#include "hw/pci/pcie.h"
|
||||
#include "hw/pci/pcie_port.h"
|
||||
#include "hw/pci-bridge/cxl_upstream_port.h"
|
||||
/*
|
||||
* Null value of all Fs suggested by IEEE RA guidelines for use of
|
||||
* EU, OUI and CID
|
||||
*/
|
||||
#define UI64_NULL (~0ULL)
|
||||
|
||||
#define CXL_UPSTREAM_PORT_MSI_NR_VECTOR 2
|
||||
|
||||
#define CXL_UPSTREAM_PORT_MSI_OFFSET 0x70
|
||||
#define CXL_UPSTREAM_PORT_PCIE_CAP_OFFSET 0x90
|
||||
#define CXL_UPSTREAM_PORT_AER_OFFSET 0x100
|
||||
#define CXL_UPSTREAM_PORT_SN_OFFSET \
|
||||
(CXL_UPSTREAM_PORT_AER_OFFSET + PCI_ERR_SIZEOF)
|
||||
#define CXL_UPSTREAM_PORT_DVSEC_OFFSET \
|
||||
(CXL_UPSTREAM_PORT_SN_OFFSET + PCI_EXT_CAP_DSN_SIZEOF)
|
||||
|
||||
CXLComponentState *cxl_usp_to_cstate(CXLUpstreamPort *usp)
|
||||
{
|
||||
return &usp->cxl_cstate;
|
||||
}
|
||||
|
||||
static void cxl_usp_dvsec_write_config(PCIDevice *dev, uint32_t addr,
|
||||
uint32_t val, int len)
|
||||
{
|
||||
CXLUpstreamPort *usp = CXL_USP(dev);
|
||||
|
||||
if (range_contains(&usp->cxl_cstate.dvsecs[EXTENSIONS_PORT_DVSEC], addr)) {
|
||||
uint8_t *reg = &dev->config[addr];
|
||||
addr -= usp->cxl_cstate.dvsecs[EXTENSIONS_PORT_DVSEC].lob;
|
||||
if (addr == PORT_CONTROL_OFFSET) {
|
||||
if (pci_get_word(reg) & PORT_CONTROL_UNMASK_SBR) {
|
||||
/* unmask SBR */
|
||||
qemu_log_mask(LOG_UNIMP, "SBR mask control is not supported\n");
|
||||
}
|
||||
if (pci_get_word(reg) & PORT_CONTROL_ALT_MEMID_EN) {
|
||||
/* Alt Memory & ID Space Enable */
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"Alt Memory & ID space is not supported\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void cxl_usp_write_config(PCIDevice *d, uint32_t address,
|
||||
uint32_t val, int len)
|
||||
{
|
||||
CXLUpstreamPort *usp = CXL_USP(d);
|
||||
|
||||
pcie_doe_write_config(&usp->doe_cdat, address, val, len);
|
||||
pci_bridge_write_config(d, address, val, len);
|
||||
pcie_cap_flr_write_config(d, address, val, len);
|
||||
pcie_aer_write_config(d, address, val, len);
|
||||
|
||||
cxl_usp_dvsec_write_config(d, address, val, len);
|
||||
}
|
||||
|
||||
static uint32_t cxl_usp_read_config(PCIDevice *d, uint32_t address, int len)
|
||||
{
|
||||
CXLUpstreamPort *usp = CXL_USP(d);
|
||||
uint32_t val;
|
||||
|
||||
if (pcie_doe_read_config(&usp->doe_cdat, address, len, &val)) {
|
||||
return val;
|
||||
}
|
||||
|
||||
return pci_default_read_config(d, address, len);
|
||||
}
|
||||
|
||||
static void latch_registers(CXLUpstreamPort *usp)
|
||||
{
|
||||
uint32_t *reg_state = usp->cxl_cstate.crb.cache_mem_registers;
|
||||
uint32_t *write_msk = usp->cxl_cstate.crb.cache_mem_regs_write_mask;
|
||||
|
||||
cxl_component_register_init_common(reg_state, write_msk,
|
||||
CXL2_UPSTREAM_PORT, usp->flitmode);
|
||||
ARRAY_FIELD_DP32(reg_state, CXL_HDM_DECODER_CAPABILITY, TARGET_COUNT, 8);
|
||||
}
|
||||
|
||||
static void cxl_usp_reset(DeviceState *qdev)
|
||||
{
|
||||
PCIDevice *d = PCI_DEVICE(qdev);
|
||||
CXLUpstreamPort *usp = CXL_USP(qdev);
|
||||
|
||||
pci_bridge_reset(qdev);
|
||||
pcie_cap_deverr_reset(d);
|
||||
pcie_cap_fill_link_ep_usp(d, usp->width, usp->speed, usp->flitmode);
|
||||
latch_registers(usp);
|
||||
cxl_init_physical_port_control(&usp->perst);
|
||||
}
|
||||
|
||||
static void build_dvsecs(CXLUpstreamPort *usp)
|
||||
{
|
||||
CXLComponentState *cxl_cstate = &usp->cxl_cstate;
|
||||
uint8_t *dvsec;
|
||||
|
||||
dvsec = (uint8_t *)&(CXLDVSECPortExt){
|
||||
.status = 0x1, /* Port Power Management Init Complete */
|
||||
};
|
||||
cxl_component_create_dvsec(cxl_cstate, CXL2_UPSTREAM_PORT,
|
||||
EXTENSIONS_PORT_DVSEC_LENGTH,
|
||||
EXTENSIONS_PORT_DVSEC,
|
||||
EXTENSIONS_PORT_DVSEC_REVID, dvsec);
|
||||
dvsec = (uint8_t *)&(CXLDVSECPortFlexBus){
|
||||
.cap = 0x27, /* Cache, IO, Mem, non-MLD */
|
||||
.ctrl = 0x27, /* Cache, IO, Mem */
|
||||
.status = usp->flitmode ? 0x6 : 0x26, /* lack of 68B */
|
||||
.rcvd_mod_ts_data_phase1 = 0xef, /* WTF? */
|
||||
};
|
||||
cxl_component_create_dvsec(cxl_cstate, CXL2_UPSTREAM_PORT,
|
||||
PCIE_CXL3_FLEXBUS_PORT_DVSEC_LENGTH,
|
||||
PCIE_FLEXBUS_PORT_DVSEC,
|
||||
PCIE_CXL3_FLEXBUS_PORT_DVSEC_REVID, dvsec);
|
||||
|
||||
dvsec = (uint8_t *)&(CXLDVSECRegisterLocator){
|
||||
.rsvd = 0,
|
||||
.reg0_base_lo = RBI_COMPONENT_REG | CXL_COMPONENT_REG_BAR_IDX,
|
||||
.reg0_base_hi = 0,
|
||||
};
|
||||
cxl_component_create_dvsec(cxl_cstate, CXL2_UPSTREAM_PORT,
|
||||
REG_LOC_DVSEC_LENGTH, REG_LOC_DVSEC,
|
||||
REG_LOC_DVSEC_REVID, dvsec);
|
||||
}
|
||||
|
||||
static bool cxl_doe_cdat_rsp(DOECap *doe_cap)
|
||||
{
|
||||
CDATObject *cdat = &CXL_USP(doe_cap->pdev)->cxl_cstate.cdat;
|
||||
uint16_t ent;
|
||||
void *base;
|
||||
uint32_t len;
|
||||
CDATReq *req = pcie_doe_get_write_mbox_ptr(doe_cap);
|
||||
CDATRsp rsp;
|
||||
|
||||
cxl_doe_cdat_update(&CXL_USP(doe_cap->pdev)->cxl_cstate, &error_fatal);
|
||||
assert(cdat->entry_len);
|
||||
|
||||
/* Discard if request length mismatched */
|
||||
if (pcie_doe_get_obj_len(req) <
|
||||
DIV_ROUND_UP(sizeof(CDATReq), sizeof(uint32_t))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ent = req->entry_handle;
|
||||
if (ent >= cdat->entry_len) {
|
||||
return false;
|
||||
}
|
||||
base = cdat->entry[ent].base;
|
||||
len = cdat->entry[ent].length;
|
||||
|
||||
rsp = (CDATRsp) {
|
||||
.header = {
|
||||
.vendor_id = CXL_VENDOR_ID,
|
||||
.data_obj_type = CXL_DOE_TABLE_ACCESS,
|
||||
.reserved = 0x0,
|
||||
.length = DIV_ROUND_UP((sizeof(rsp) + len), sizeof(uint32_t)),
|
||||
},
|
||||
.rsp_code = CXL_DOE_TAB_RSP,
|
||||
.table_type = CXL_DOE_TAB_TYPE_CDAT,
|
||||
.entry_handle = (ent < cdat->entry_len - 1) ?
|
||||
ent + 1 : CXL_DOE_TAB_ENT_MAX,
|
||||
};
|
||||
|
||||
memcpy(doe_cap->read_mbox, &rsp, sizeof(rsp));
|
||||
memcpy(doe_cap->read_mbox + DIV_ROUND_UP(sizeof(rsp), sizeof(uint32_t)),
|
||||
base, len);
|
||||
|
||||
doe_cap->read_mbox_len += rsp.header.length;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static DOEProtocol doe_cdat_prot[] = {
|
||||
{ CXL_VENDOR_ID, CXL_DOE_TABLE_ACCESS, cxl_doe_cdat_rsp },
|
||||
{ }
|
||||
};
|
||||
|
||||
enum {
|
||||
CXL_USP_CDAT_SSLBIS_LAT,
|
||||
CXL_USP_CDAT_SSLBIS_BW,
|
||||
CXL_USP_CDAT_NUM_ENTRIES
|
||||
};
|
||||
|
||||
static int build_cdat_table(CDATSubHeader ***cdat_table, void *priv)
|
||||
{
|
||||
CDATSslbis *sslbis_latency;
|
||||
CDATSslbis *sslbis_bandwidth;
|
||||
CXLUpstreamPort *us = CXL_USP(priv);
|
||||
PCIBus *bus = &PCI_BRIDGE(us)->sec_bus;
|
||||
int devfn, sslbis_size, i;
|
||||
int count = 0;
|
||||
uint16_t port_ids[256];
|
||||
|
||||
for (devfn = 0; devfn < ARRAY_SIZE(bus->devices); devfn++) {
|
||||
PCIDevice *d = bus->devices[devfn];
|
||||
PCIEPort *port;
|
||||
|
||||
if (!d || !pci_is_express(d) || !d->exp.exp_cap) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Whilst the PCI express spec doesn't allow anything other than
|
||||
* downstream ports on this bus, let us be a little paranoid
|
||||
*/
|
||||
if (!object_dynamic_cast(OBJECT(d), TYPE_PCIE_PORT)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
port = PCIE_PORT(d);
|
||||
port_ids[count] = port->port;
|
||||
count++;
|
||||
}
|
||||
|
||||
/* May not yet have any ports - try again later */
|
||||
if (count == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
sslbis_size = sizeof(CDATSslbis) + sizeof(*sslbis_latency->sslbe) * count;
|
||||
sslbis_latency = g_malloc(sslbis_size);
|
||||
*sslbis_latency = (CDATSslbis) {
|
||||
.sslbis_header = {
|
||||
.header = {
|
||||
.type = CDAT_TYPE_SSLBIS,
|
||||
.length = sslbis_size,
|
||||
},
|
||||
.data_type = HMAT_LB_DATA_TYPE_ACCESS_LATENCY,
|
||||
.entry_base_unit = 10000,
|
||||
},
|
||||
};
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
sslbis_latency->sslbe[i] = (CDATSslbe) {
|
||||
.port_x_id = CDAT_PORT_ID_USP,
|
||||
.port_y_id = port_ids[i],
|
||||
.latency_bandwidth = 15, /* 150ns */
|
||||
};
|
||||
}
|
||||
|
||||
sslbis_bandwidth = g_malloc(sslbis_size);
|
||||
*sslbis_bandwidth = (CDATSslbis) {
|
||||
.sslbis_header = {
|
||||
.header = {
|
||||
.type = CDAT_TYPE_SSLBIS,
|
||||
.length = sslbis_size,
|
||||
},
|
||||
.data_type = HMAT_LB_DATA_TYPE_ACCESS_BANDWIDTH,
|
||||
.entry_base_unit = 1024,
|
||||
},
|
||||
};
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
sslbis_bandwidth->sslbe[i] = (CDATSslbe) {
|
||||
.port_x_id = CDAT_PORT_ID_USP,
|
||||
.port_y_id = port_ids[i],
|
||||
.latency_bandwidth = 16, /* 16 GB/s */
|
||||
};
|
||||
}
|
||||
|
||||
*cdat_table = g_new0(CDATSubHeader *, CXL_USP_CDAT_NUM_ENTRIES);
|
||||
|
||||
/* Header always at start of structure */
|
||||
(*cdat_table)[CXL_USP_CDAT_SSLBIS_LAT] = (CDATSubHeader *)sslbis_latency;
|
||||
(*cdat_table)[CXL_USP_CDAT_SSLBIS_BW] = (CDATSubHeader *)sslbis_bandwidth;
|
||||
|
||||
return CXL_USP_CDAT_NUM_ENTRIES;
|
||||
}
|
||||
|
||||
static void free_default_cdat_table(CDATSubHeader **cdat_table, int num,
|
||||
void *priv)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
g_free(cdat_table[i]);
|
||||
}
|
||||
g_free(cdat_table);
|
||||
}
|
||||
|
||||
static void cxl_usp_realize(PCIDevice *d, Error **errp)
|
||||
{
|
||||
ERRP_GUARD();
|
||||
PCIEPort *p = PCIE_PORT(d);
|
||||
CXLUpstreamPort *usp = CXL_USP(d);
|
||||
CXLComponentState *cxl_cstate = &usp->cxl_cstate;
|
||||
ComponentRegisters *cregs = &cxl_cstate->crb;
|
||||
MemoryRegion *component_bar = &cregs->component_registers;
|
||||
int rc;
|
||||
|
||||
pci_bridge_initfn(d, TYPE_PCIE_BUS);
|
||||
pcie_port_init_reg(d);
|
||||
|
||||
rc = msi_init(d, CXL_UPSTREAM_PORT_MSI_OFFSET,
|
||||
CXL_UPSTREAM_PORT_MSI_NR_VECTOR, true, true, errp);
|
||||
if (rc) {
|
||||
assert(rc == -ENOTSUP);
|
||||
goto err_bridge;
|
||||
}
|
||||
|
||||
rc = pcie_cap_init(d, CXL_UPSTREAM_PORT_PCIE_CAP_OFFSET,
|
||||
PCI_EXP_TYPE_UPSTREAM, p->port, errp);
|
||||
if (rc < 0) {
|
||||
goto err_msi;
|
||||
}
|
||||
|
||||
pcie_cap_flr_init(d);
|
||||
pcie_cap_deverr_init(d);
|
||||
rc = pcie_aer_init(d, PCI_ERR_VER, CXL_UPSTREAM_PORT_AER_OFFSET,
|
||||
PCI_ERR_SIZEOF, errp);
|
||||
if (rc) {
|
||||
goto err_cap;
|
||||
}
|
||||
if (usp->sn != UI64_NULL) {
|
||||
pcie_dev_ser_num_init(d, CXL_UPSTREAM_PORT_SN_OFFSET, usp->sn);
|
||||
}
|
||||
cxl_cstate->dvsec_offset = CXL_UPSTREAM_PORT_DVSEC_OFFSET;
|
||||
cxl_cstate->pdev = d;
|
||||
build_dvsecs(usp);
|
||||
cxl_component_register_block_init(OBJECT(d), cxl_cstate, TYPE_CXL_USP);
|
||||
pci_register_bar(d, CXL_COMPONENT_REG_BAR_IDX,
|
||||
PCI_BASE_ADDRESS_SPACE_MEMORY |
|
||||
PCI_BASE_ADDRESS_MEM_TYPE_64,
|
||||
component_bar);
|
||||
|
||||
pcie_doe_init(d, &usp->doe_cdat, cxl_cstate->dvsec_offset, doe_cdat_prot,
|
||||
true, 1);
|
||||
|
||||
cxl_cstate->cdat.build_cdat_table = build_cdat_table;
|
||||
cxl_cstate->cdat.free_cdat_table = free_default_cdat_table;
|
||||
cxl_cstate->cdat.private = d;
|
||||
if (!cxl_doe_cdat_init(cxl_cstate, errp)) {
|
||||
goto err_cap;
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
err_cap:
|
||||
pcie_cap_exit(d);
|
||||
err_msi:
|
||||
msi_uninit(d);
|
||||
err_bridge:
|
||||
pci_bridge_exitfn(d);
|
||||
}
|
||||
|
||||
static void cxl_usp_exitfn(PCIDevice *d)
|
||||
{
|
||||
pcie_aer_exit(d);
|
||||
pcie_cap_exit(d);
|
||||
msi_uninit(d);
|
||||
pci_bridge_exitfn(d);
|
||||
}
|
||||
|
||||
static const Property cxl_upstream_props[] = {
|
||||
DEFINE_PROP_UINT64("sn", CXLUpstreamPort, sn, UI64_NULL),
|
||||
DEFINE_PROP_STRING("cdat", CXLUpstreamPort, cxl_cstate.cdat.filename),
|
||||
DEFINE_PROP_PCIE_LINK_SPEED("x-speed", CXLUpstreamPort,
|
||||
speed, PCIE_LINK_SPEED_32),
|
||||
DEFINE_PROP_PCIE_LINK_WIDTH("x-width", CXLUpstreamPort,
|
||||
width, PCIE_LINK_WIDTH_16),
|
||||
DEFINE_PROP_BOOL("x-256b-flit", CXLUpstreamPort, flitmode, false),
|
||||
};
|
||||
|
||||
static void cxl_upstream_class_init(ObjectClass *oc, const void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||
PCIDeviceClass *k = PCI_DEVICE_CLASS(oc);
|
||||
|
||||
k->config_write = cxl_usp_write_config;
|
||||
k->config_read = cxl_usp_read_config;
|
||||
k->realize = cxl_usp_realize;
|
||||
k->exit = cxl_usp_exitfn;
|
||||
k->vendor_id = 0x19e5; /* Huawei */
|
||||
k->device_id = 0xa128; /* Emulated CXL Switch Upstream Port */
|
||||
k->revision = 0;
|
||||
set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
|
||||
dc->desc = "CXL Switch Upstream Port";
|
||||
device_class_set_legacy_reset(dc, cxl_usp_reset);
|
||||
device_class_set_props(dc, cxl_upstream_props);
|
||||
}
|
||||
|
||||
static const TypeInfo cxl_usp_info = {
|
||||
.name = TYPE_CXL_USP,
|
||||
.parent = TYPE_PCIE_PORT,
|
||||
.instance_size = sizeof(CXLUpstreamPort),
|
||||
.class_init = cxl_upstream_class_init,
|
||||
.interfaces = (const InterfaceInfo[]) {
|
||||
{ INTERFACE_PCIE_DEVICE },
|
||||
{ INTERFACE_CXL_DEVICE },
|
||||
{ }
|
||||
},
|
||||
};
|
||||
|
||||
static void cxl_usp_register_type(void)
|
||||
{
|
||||
type_register_static(&cxl_usp_info);
|
||||
}
|
||||
|
||||
type_init(cxl_usp_register_type);
|
||||
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
* Generic PCI Express Root Port emulation
|
||||
*
|
||||
* Copyright (C) 2017 Red Hat Inc
|
||||
*
|
||||
* Authors:
|
||||
* Marcel Apfelbaum <[email protected]>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/module.h"
|
||||
#include "hw/pci/msix.h"
|
||||
#include "hw/pci/pcie_port.h"
|
||||
#include "hw/core/qdev-properties.h"
|
||||
#include "hw/core/qdev-properties-system.h"
|
||||
#include "migration/vmstate.h"
|
||||
#include "qom/object.h"
|
||||
|
||||
#define TYPE_GEN_PCIE_ROOT_PORT "pcie-root-port"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(GenPCIERootPort, GEN_PCIE_ROOT_PORT)
|
||||
|
||||
#define GEN_PCIE_ROOT_PORT_AER_OFFSET 0x100
|
||||
#define GEN_PCIE_ROOT_PORT_ACS_OFFSET \
|
||||
(GEN_PCIE_ROOT_PORT_AER_OFFSET + PCI_ERR_SIZEOF)
|
||||
|
||||
#define GEN_PCIE_ROOT_PORT_MSIX_NR_VECTOR 1
|
||||
#define GEN_PCIE_ROOT_DEFAULT_IO_RANGE 4096
|
||||
|
||||
struct GenPCIERootPort {
|
||||
/*< private >*/
|
||||
PCIESlot parent_obj;
|
||||
/*< public >*/
|
||||
|
||||
/* additional resources to reserve */
|
||||
PCIResReserve res_reserve;
|
||||
};
|
||||
|
||||
static uint8_t gen_rp_aer_vector(const PCIDevice *d)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gen_rp_interrupts_init(PCIDevice *d, Error **errp)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = msix_init_exclusive_bar(d, GEN_PCIE_ROOT_PORT_MSIX_NR_VECTOR, 0, errp);
|
||||
|
||||
if (rc < 0) {
|
||||
assert(rc == -ENOTSUP);
|
||||
} else {
|
||||
msix_vector_use(d, 0);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void gen_rp_interrupts_uninit(PCIDevice *d)
|
||||
{
|
||||
msix_uninit_exclusive_bar(d);
|
||||
}
|
||||
|
||||
static void gen_rp_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
PCIDevice *d = PCI_DEVICE(dev);
|
||||
PCIESlot *s = PCIE_SLOT(d);
|
||||
GenPCIERootPort *grp = GEN_PCIE_ROOT_PORT(d);
|
||||
PCIERootPortClass *rpc = PCIE_ROOT_PORT_GET_CLASS(d);
|
||||
Error *local_err = NULL;
|
||||
|
||||
rpc->parent_realize(dev, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* reserving IO space led to worse issues in 6.1, when this hunk was
|
||||
* introduced. (see commit: 211afe5c69b59). Keep this broken for 6.1
|
||||
* machine type ABI compatibility only
|
||||
*/
|
||||
if (s->hide_native_hotplug_cap && grp->res_reserve.io == -1 && s->hotplug) {
|
||||
grp->res_reserve.io = GEN_PCIE_ROOT_DEFAULT_IO_RANGE;
|
||||
}
|
||||
int rc = pci_bridge_qemu_reserve_cap_init(d, 0,
|
||||
grp->res_reserve, errp);
|
||||
|
||||
if (rc < 0) {
|
||||
rpc->parent_class.exit(d);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!grp->res_reserve.io) {
|
||||
pci_word_test_and_clear_mask(d->wmask + PCI_COMMAND,
|
||||
PCI_COMMAND_IO);
|
||||
d->wmask[PCI_IO_BASE] = 0;
|
||||
d->wmask[PCI_IO_LIMIT] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_rp_dev = {
|
||||
.name = "pcie-root-port",
|
||||
.priority = MIG_PRI_PCI_BUS,
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.post_load = pcie_cap_slot_post_load,
|
||||
.fields = (const VMStateField[]) {
|
||||
VMSTATE_PCI_DEVICE(parent_obj.parent_obj.parent_obj, PCIESlot),
|
||||
VMSTATE_STRUCT(parent_obj.parent_obj.parent_obj.exp.aer_log,
|
||||
PCIESlot, 0, vmstate_pcie_aer_log, PCIEAERLog),
|
||||
VMSTATE_MSIX(parent_obj.parent_obj.parent_obj.parent_obj,
|
||||
GenPCIERootPort),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static const Property gen_rp_props[] = {
|
||||
DEFINE_PROP_UINT32("bus-reserve", GenPCIERootPort,
|
||||
res_reserve.bus, -1),
|
||||
DEFINE_PROP_SIZE("io-reserve", GenPCIERootPort,
|
||||
res_reserve.io, -1),
|
||||
DEFINE_PROP_SIZE("mem-reserve", GenPCIERootPort,
|
||||
res_reserve.mem_non_pref, -1),
|
||||
DEFINE_PROP_SIZE("pref32-reserve", GenPCIERootPort,
|
||||
res_reserve.mem_pref_32, -1),
|
||||
DEFINE_PROP_SIZE("pref64-reserve", GenPCIERootPort,
|
||||
res_reserve.mem_pref_64, -1),
|
||||
DEFINE_PROP_PCIE_LINK_SPEED("x-speed", PCIESlot,
|
||||
speed, PCIE_LINK_SPEED_16),
|
||||
DEFINE_PROP_PCIE_LINK_WIDTH("x-width", PCIESlot,
|
||||
width, PCIE_LINK_WIDTH_32),
|
||||
};
|
||||
|
||||
static void gen_rp_dev_class_init(ObjectClass *klass, const void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
|
||||
PCIERootPortClass *rpc = PCIE_ROOT_PORT_CLASS(klass);
|
||||
|
||||
k->vendor_id = PCI_VENDOR_ID_REDHAT;
|
||||
k->device_id = PCI_DEVICE_ID_REDHAT_PCIE_RP;
|
||||
dc->desc = "PCI Express Root Port";
|
||||
dc->vmsd = &vmstate_rp_dev;
|
||||
device_class_set_props(dc, gen_rp_props);
|
||||
|
||||
device_class_set_parent_realize(dc, gen_rp_realize, &rpc->parent_realize);
|
||||
|
||||
rpc->aer_vector = gen_rp_aer_vector;
|
||||
rpc->interrupts_init = gen_rp_interrupts_init;
|
||||
rpc->interrupts_uninit = gen_rp_interrupts_uninit;
|
||||
rpc->aer_offset = GEN_PCIE_ROOT_PORT_AER_OFFSET;
|
||||
rpc->acs_offset = GEN_PCIE_ROOT_PORT_ACS_OFFSET;
|
||||
}
|
||||
|
||||
static const TypeInfo gen_rp_dev_info = {
|
||||
.name = TYPE_GEN_PCIE_ROOT_PORT,
|
||||
.parent = TYPE_PCIE_ROOT_PORT,
|
||||
.instance_size = sizeof(GenPCIERootPort),
|
||||
.class_init = gen_rp_dev_class_init,
|
||||
};
|
||||
|
||||
static void gen_rp_register_types(void)
|
||||
{
|
||||
type_register_static(&gen_rp_dev_info);
|
||||
}
|
||||
type_init(gen_rp_register_types)
|
||||
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Copyright (c) 2006 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
/*
|
||||
* QEMU i82801b11 dmi-to-pci Bridge Emulation
|
||||
*
|
||||
* Copyright (c) 2009, 2010, 2011
|
||||
* Isaku Yamahata <yamahata at valinux co jp>
|
||||
* VA Linux Systems Japan K.K.
|
||||
* Copyright (C) 2012 Jason Baron <[email protected]>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/pci/pci_bridge.h"
|
||||
#include "migration/vmstate.h"
|
||||
#include "qemu/module.h"
|
||||
#include "hw/southbridge/ich9.h"
|
||||
|
||||
/*****************************************************************************/
|
||||
/* ICH9 DMI-to-PCI bridge */
|
||||
#define I82801ba_SSVID_OFFSET 0x50
|
||||
#define I82801ba_SSVID_SVID 0
|
||||
#define I82801ba_SSVID_SSID 0
|
||||
|
||||
typedef struct I82801b11Bridge {
|
||||
/*< private >*/
|
||||
PCIBridge parent_obj;
|
||||
/*< public >*/
|
||||
} I82801b11Bridge;
|
||||
|
||||
static void i82801b11_bridge_realize(PCIDevice *d, Error **errp)
|
||||
{
|
||||
int rc;
|
||||
|
||||
pci_bridge_initfn(d, TYPE_PCI_BUS);
|
||||
|
||||
rc = pci_bridge_ssvid_init(d, I82801ba_SSVID_OFFSET,
|
||||
I82801ba_SSVID_SVID, I82801ba_SSVID_SSID,
|
||||
errp);
|
||||
if (rc < 0) {
|
||||
goto err_bridge;
|
||||
}
|
||||
pci_config_set_prog_interface(d->config, PCI_CLASS_BRIDGE_PCI_INF_SUB);
|
||||
return;
|
||||
|
||||
err_bridge:
|
||||
pci_bridge_exitfn(d);
|
||||
}
|
||||
|
||||
static const VMStateDescription i82801b11_bridge_dev_vmstate = {
|
||||
.name = "i82801b11_bridge",
|
||||
.priority = MIG_PRI_PCI_BUS,
|
||||
.fields = (const VMStateField[]) {
|
||||
VMSTATE_PCI_DEVICE(parent_obj, PCIBridge),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static void i82801b11_bridge_class_init(ObjectClass *klass, const void *data)
|
||||
{
|
||||
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
k->vendor_id = PCI_VENDOR_ID_INTEL;
|
||||
k->device_id = PCI_DEVICE_ID_INTEL_82801BA_11;
|
||||
k->revision = ICH9_D2P_A2_REVISION;
|
||||
k->realize = i82801b11_bridge_realize;
|
||||
k->config_write = pci_bridge_write_config;
|
||||
dc->vmsd = &i82801b11_bridge_dev_vmstate;
|
||||
device_class_set_legacy_reset(dc, pci_bridge_reset);
|
||||
set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
|
||||
}
|
||||
|
||||
static const TypeInfo i82801b11_bridge_info = {
|
||||
.name = "i82801b11-bridge",
|
||||
.parent = TYPE_PCI_BRIDGE,
|
||||
.instance_size = sizeof(I82801b11Bridge),
|
||||
.class_init = i82801b11_bridge_class_init,
|
||||
.interfaces = (const InterfaceInfo[]) {
|
||||
{ INTERFACE_CONVENTIONAL_PCI_DEVICE },
|
||||
{ },
|
||||
},
|
||||
};
|
||||
|
||||
static void d2pbr_register(void)
|
||||
{
|
||||
type_register_static(&i82801b11_bridge_info);
|
||||
}
|
||||
|
||||
type_init(d2pbr_register);
|
||||
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* ioh3420.c
|
||||
* Intel X58 north bridge IOH
|
||||
* PCI Express root port device id 3420
|
||||
*
|
||||
* Copyright (c) 2010 Isaku Yamahata <yamahata at valinux co jp>
|
||||
* VA Linux Systems Japan K.K.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/pci/pci_ids.h"
|
||||
#include "hw/pci/msi.h"
|
||||
#include "hw/pci/pcie.h"
|
||||
#include "hw/pci/pcie_port.h"
|
||||
#include "migration/vmstate.h"
|
||||
#include "qemu/module.h"
|
||||
|
||||
#define PCI_DEVICE_ID_IOH_EPORT 0x3420 /* D0:F0 express mode */
|
||||
#define PCI_DEVICE_ID_IOH_REV 0x2
|
||||
#define IOH_EP_SSVID_OFFSET 0x40
|
||||
#define IOH_EP_SSVID_SVID PCI_VENDOR_ID_INTEL
|
||||
#define IOH_EP_SSVID_SSID 0
|
||||
#define IOH_EP_MSI_OFFSET 0x60
|
||||
#define IOH_EP_MSI_SUPPORTED_FLAGS PCI_MSI_FLAGS_MASKBIT
|
||||
#define IOH_EP_MSI_NR_VECTOR 2
|
||||
#define IOH_EP_EXP_OFFSET 0x90
|
||||
#define IOH_EP_AER_OFFSET 0x100
|
||||
|
||||
/*
|
||||
* If two MSI vector are allocated, Advanced Error Interrupt Message Number
|
||||
* is 1. otherwise 0.
|
||||
* 17.12.5.10 RPERRSTS, 32:27 bit Advanced Error Interrupt Message Number.
|
||||
*/
|
||||
static uint8_t ioh3420_aer_vector(const PCIDevice *d)
|
||||
{
|
||||
switch (msi_nr_vectors_allocated(d)) {
|
||||
case 1:
|
||||
return 0;
|
||||
case 2:
|
||||
return 1;
|
||||
case 4:
|
||||
case 8:
|
||||
case 16:
|
||||
case 32:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
abort();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ioh3420_interrupts_init(PCIDevice *d, Error **errp)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = msi_init(d, IOH_EP_MSI_OFFSET, IOH_EP_MSI_NR_VECTOR,
|
||||
IOH_EP_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_64BIT,
|
||||
IOH_EP_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_MASKBIT,
|
||||
errp);
|
||||
if (rc < 0) {
|
||||
assert(rc == -ENOTSUP);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void ioh3420_interrupts_uninit(PCIDevice *d)
|
||||
{
|
||||
msi_uninit(d);
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_ioh3420 = {
|
||||
.name = "ioh-3240-express-root-port",
|
||||
.priority = MIG_PRI_PCI_BUS,
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.post_load = pcie_cap_slot_post_load,
|
||||
.fields = (const VMStateField[]) {
|
||||
VMSTATE_PCI_DEVICE(parent_obj.parent_obj.parent_obj, PCIESlot),
|
||||
VMSTATE_STRUCT(parent_obj.parent_obj.parent_obj.exp.aer_log,
|
||||
PCIESlot, 0, vmstate_pcie_aer_log, PCIEAERLog),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static void ioh3420_class_init(ObjectClass *klass, const void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
|
||||
PCIERootPortClass *rpc = PCIE_ROOT_PORT_CLASS(klass);
|
||||
|
||||
k->vendor_id = PCI_VENDOR_ID_INTEL;
|
||||
k->device_id = PCI_DEVICE_ID_IOH_EPORT;
|
||||
k->revision = PCI_DEVICE_ID_IOH_REV;
|
||||
dc->desc = "Intel IOH device id 3420 PCIE Root Port";
|
||||
dc->vmsd = &vmstate_ioh3420;
|
||||
rpc->aer_vector = ioh3420_aer_vector;
|
||||
rpc->interrupts_init = ioh3420_interrupts_init;
|
||||
rpc->interrupts_uninit = ioh3420_interrupts_uninit;
|
||||
rpc->exp_offset = IOH_EP_EXP_OFFSET;
|
||||
rpc->aer_offset = IOH_EP_AER_OFFSET;
|
||||
rpc->ssvid_offset = IOH_EP_SSVID_OFFSET;
|
||||
rpc->ssid = IOH_EP_SSVID_SSID;
|
||||
}
|
||||
|
||||
static const TypeInfo ioh3420_info = {
|
||||
.name = "ioh3420",
|
||||
.parent = TYPE_PCIE_ROOT_PORT,
|
||||
.class_init = ioh3420_class_init,
|
||||
};
|
||||
|
||||
static void ioh3420_register_types(void)
|
||||
{
|
||||
type_register_static(&ioh3420_info);
|
||||
}
|
||||
|
||||
type_init(ioh3420_register_types)
|
||||
@@ -0,0 +1,15 @@
|
||||
pci_ss = ss.source_set()
|
||||
pci_ss.add(when: 'CONFIG_PCI_BRIDGE', if_true: files('pci_bridge_dev.c'))
|
||||
pci_ss.add(when: 'CONFIG_I82801B11', if_true: files('i82801b11.c'))
|
||||
pci_ss.add(when: 'CONFIG_IOH3420', if_true: files('ioh3420.c'))
|
||||
pci_ss.add(when: 'CONFIG_PCIE_PORT', if_true: files('pcie_root_port.c', 'gen_pcie_root_port.c'))
|
||||
pci_ss.add(when: 'CONFIG_PCIE_PCI_BRIDGE', if_true: files('pcie_pci_bridge.c'))
|
||||
pci_ss.add(when: 'CONFIG_PXB', if_true: files('pci_expander_bridge.c'),
|
||||
if_false: files('pci_expander_bridge_stubs.c'))
|
||||
pci_ss.add(when: 'CONFIG_XIO3130', if_true: files('xio3130_upstream.c', 'xio3130_downstream.c'))
|
||||
pci_ss.add(when: 'CONFIG_CXL', if_true: files('cxl_root_port.c', 'cxl_upstream.c', 'cxl_downstream.c'))
|
||||
|
||||
# Sun4u
|
||||
pci_ss.add(when: 'CONFIG_SIMBA', if_true: files('simba.c'))
|
||||
|
||||
system_ss.add_all(when: 'CONFIG_PCI', if_true: pci_ss)
|
||||
@@ -0,0 +1,305 @@
|
||||
/*
|
||||
* Standard PCI Bridge Device
|
||||
*
|
||||
* Copyright (c) 2011 Red Hat Inc. Author: Michael S. Tsirkin <[email protected]>
|
||||
*
|
||||
* http://www.pcisig.com/specifications/conventional/pci_to_pci_bridge_architecture/
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/module.h"
|
||||
#include "hw/pci/pci_bridge.h"
|
||||
#include "hw/pci/pci_ids.h"
|
||||
#include "hw/pci/msi.h"
|
||||
#include "hw/pci/shpc.h"
|
||||
#include "hw/pci/slotid_cap.h"
|
||||
#include "hw/core/qdev-properties.h"
|
||||
#include "system/memory.h"
|
||||
#include "hw/pci/pci_bus.h"
|
||||
#include "hw/core/hotplug.h"
|
||||
#include "qom/object.h"
|
||||
|
||||
#define TYPE_PCI_BRIDGE_DEV "pci-bridge"
|
||||
#define TYPE_PCI_BRIDGE_SEAT_DEV "pci-bridge-seat"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(PCIBridgeDev, PCI_BRIDGE_DEV)
|
||||
|
||||
struct PCIBridgeDev {
|
||||
/*< private >*/
|
||||
PCIBridge parent_obj;
|
||||
/*< public >*/
|
||||
|
||||
MemoryRegion bar;
|
||||
uint8_t chassis_nr;
|
||||
#define PCI_BRIDGE_DEV_F_SHPC_REQ 0
|
||||
uint32_t flags;
|
||||
|
||||
OnOffAuto msi;
|
||||
|
||||
/* additional resources to reserve */
|
||||
PCIResReserve res_reserve;
|
||||
};
|
||||
|
||||
static void pci_bridge_dev_realize(PCIDevice *dev, Error **errp)
|
||||
{
|
||||
PCIBridge *br = PCI_BRIDGE(dev);
|
||||
PCIBridgeDev *bridge_dev = PCI_BRIDGE_DEV(dev);
|
||||
int err;
|
||||
Error *local_err = NULL;
|
||||
|
||||
pci_bridge_initfn(dev, TYPE_PCI_BUS);
|
||||
|
||||
if (bridge_dev->flags & (1 << PCI_BRIDGE_DEV_F_SHPC_REQ)) {
|
||||
dev->config[PCI_INTERRUPT_PIN] = 0x1;
|
||||
memory_region_init(&bridge_dev->bar, OBJECT(dev), "shpc-bar",
|
||||
shpc_bar_size(dev));
|
||||
err = shpc_init(dev, &br->sec_bus, &bridge_dev->bar, 0, errp);
|
||||
if (err) {
|
||||
goto shpc_error;
|
||||
}
|
||||
} else {
|
||||
/* MSI is not applicable without SHPC */
|
||||
bridge_dev->msi = ON_OFF_AUTO_OFF;
|
||||
}
|
||||
|
||||
err = slotid_cap_init(dev, 0, bridge_dev->chassis_nr, 0, errp);
|
||||
if (err) {
|
||||
goto slotid_error;
|
||||
}
|
||||
|
||||
if (bridge_dev->msi != ON_OFF_AUTO_OFF) {
|
||||
/* it means SHPC exists, because MSI is needed by SHPC */
|
||||
|
||||
err = msi_init(dev, 0, 1, true, true, &local_err);
|
||||
/* Any error other than -ENOTSUP(board's MSI support is broken)
|
||||
* is a programming error */
|
||||
assert(!err || err == -ENOTSUP);
|
||||
if (err && bridge_dev->msi == ON_OFF_AUTO_ON) {
|
||||
/* Can't satisfy user's explicit msi=on request, fail */
|
||||
error_append_hint(&local_err, "You have to use msi=auto (default) "
|
||||
"or msi=off with this machine type.\n");
|
||||
error_propagate(errp, local_err);
|
||||
goto msi_error;
|
||||
}
|
||||
assert(!local_err || bridge_dev->msi == ON_OFF_AUTO_AUTO);
|
||||
/* With msi=auto, we fall back to MSI off silently */
|
||||
error_free(local_err);
|
||||
}
|
||||
|
||||
err = pci_bridge_qemu_reserve_cap_init(dev, 0,
|
||||
bridge_dev->res_reserve, errp);
|
||||
if (err) {
|
||||
goto cap_error;
|
||||
}
|
||||
|
||||
if (shpc_present(dev)) {
|
||||
/* TODO: spec recommends using 64 bit prefetcheable BAR.
|
||||
* Check whether that works well. */
|
||||
pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY |
|
||||
PCI_BASE_ADDRESS_MEM_TYPE_64, &bridge_dev->bar);
|
||||
}
|
||||
return;
|
||||
|
||||
cap_error:
|
||||
msi_uninit(dev);
|
||||
msi_error:
|
||||
slotid_cap_cleanup(dev);
|
||||
slotid_error:
|
||||
if (shpc_present(dev)) {
|
||||
shpc_cleanup(dev, &bridge_dev->bar);
|
||||
}
|
||||
shpc_error:
|
||||
pci_bridge_exitfn(dev);
|
||||
}
|
||||
|
||||
static void pci_bridge_dev_exitfn(PCIDevice *dev)
|
||||
{
|
||||
PCIBridgeDev *bridge_dev = PCI_BRIDGE_DEV(dev);
|
||||
|
||||
pci_del_capability(dev, PCI_CAP_ID_VNDR, sizeof(PCIBridgeQemuCap));
|
||||
if (msi_present(dev)) {
|
||||
msi_uninit(dev);
|
||||
}
|
||||
slotid_cap_cleanup(dev);
|
||||
if (shpc_present(dev)) {
|
||||
shpc_cleanup(dev, &bridge_dev->bar);
|
||||
}
|
||||
pci_bridge_exitfn(dev);
|
||||
}
|
||||
|
||||
static void pci_bridge_dev_instance_finalize(Object *obj)
|
||||
{
|
||||
/* this function is idempotent and handles (PCIDevice.shpc == NULL) */
|
||||
shpc_free(PCI_DEVICE(obj));
|
||||
}
|
||||
|
||||
static void pci_bridge_dev_write_config(PCIDevice *d,
|
||||
uint32_t address, uint32_t val, int len)
|
||||
{
|
||||
pci_bridge_write_config(d, address, val, len);
|
||||
if (msi_present(d)) {
|
||||
msi_write_config(d, address, val, len);
|
||||
}
|
||||
if (shpc_present(d)) {
|
||||
shpc_cap_write_config(d, address, val, len);
|
||||
}
|
||||
}
|
||||
|
||||
static void qdev_pci_bridge_dev_reset(DeviceState *qdev)
|
||||
{
|
||||
PCIDevice *dev = PCI_DEVICE(qdev);
|
||||
|
||||
pci_bridge_reset(qdev);
|
||||
if (shpc_present(dev)) {
|
||||
shpc_reset(dev);
|
||||
}
|
||||
}
|
||||
|
||||
static const Property pci_bridge_dev_properties[] = {
|
||||
/* Note: 0 is not a legal chassis number. */
|
||||
DEFINE_PROP_UINT8(PCI_BRIDGE_DEV_PROP_CHASSIS_NR, PCIBridgeDev, chassis_nr,
|
||||
0),
|
||||
DEFINE_PROP_ON_OFF_AUTO(PCI_BRIDGE_DEV_PROP_MSI, PCIBridgeDev, msi,
|
||||
ON_OFF_AUTO_AUTO),
|
||||
DEFINE_PROP_BIT(PCI_BRIDGE_DEV_PROP_SHPC, PCIBridgeDev, flags,
|
||||
PCI_BRIDGE_DEV_F_SHPC_REQ, true),
|
||||
DEFINE_PROP_UINT32("bus-reserve", PCIBridgeDev,
|
||||
res_reserve.bus, -1),
|
||||
DEFINE_PROP_SIZE("io-reserve", PCIBridgeDev,
|
||||
res_reserve.io, -1),
|
||||
DEFINE_PROP_SIZE("mem-reserve", PCIBridgeDev,
|
||||
res_reserve.mem_non_pref, -1),
|
||||
DEFINE_PROP_SIZE("pref32-reserve", PCIBridgeDev,
|
||||
res_reserve.mem_pref_32, -1),
|
||||
DEFINE_PROP_SIZE("pref64-reserve", PCIBridgeDev,
|
||||
res_reserve.mem_pref_64, -1),
|
||||
};
|
||||
|
||||
static bool pci_device_shpc_present(void *opaque, int version_id)
|
||||
{
|
||||
PCIDevice *dev = opaque;
|
||||
|
||||
return shpc_present(dev);
|
||||
}
|
||||
|
||||
static const VMStateDescription pci_bridge_dev_vmstate = {
|
||||
.name = "pci_bridge",
|
||||
.priority = MIG_PRI_PCI_BUS,
|
||||
.fields = (const VMStateField[]) {
|
||||
VMSTATE_PCI_DEVICE(parent_obj, PCIBridge),
|
||||
SHPC_VMSTATE(shpc, PCIDevice, pci_device_shpc_present),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
void pci_bridge_dev_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev,
|
||||
Error **errp)
|
||||
{
|
||||
PCIDevice *pci_hotplug_dev = PCI_DEVICE(hotplug_dev);
|
||||
|
||||
if (!shpc_present(pci_hotplug_dev)) {
|
||||
error_setg(errp, "standard hotplug controller has been disabled for "
|
||||
"this %s", object_get_typename(OBJECT(hotplug_dev)));
|
||||
return;
|
||||
}
|
||||
shpc_device_plug_cb(hotplug_dev, dev, errp);
|
||||
}
|
||||
|
||||
void pci_bridge_dev_unplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev,
|
||||
Error **errp)
|
||||
{
|
||||
PCIDevice *pci_hotplug_dev = PCI_DEVICE(hotplug_dev);
|
||||
|
||||
g_assert(shpc_present(pci_hotplug_dev));
|
||||
shpc_device_unplug_cb(hotplug_dev, dev, errp);
|
||||
}
|
||||
|
||||
void pci_bridge_dev_unplug_request_cb(HotplugHandler *hotplug_dev,
|
||||
DeviceState *dev, Error **errp)
|
||||
{
|
||||
PCIDevice *pci_hotplug_dev = PCI_DEVICE(hotplug_dev);
|
||||
|
||||
if (!shpc_present(pci_hotplug_dev)) {
|
||||
error_setg(errp, "standard hotplug controller has been disabled for "
|
||||
"this %s", object_get_typename(OBJECT(hotplug_dev)));
|
||||
return;
|
||||
}
|
||||
shpc_device_unplug_request_cb(hotplug_dev, dev, errp);
|
||||
}
|
||||
|
||||
static void pci_bridge_dev_class_init(ObjectClass *klass, const void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
|
||||
HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass);
|
||||
|
||||
k->realize = pci_bridge_dev_realize;
|
||||
k->exit = pci_bridge_dev_exitfn;
|
||||
k->config_write = pci_bridge_dev_write_config;
|
||||
k->vendor_id = PCI_VENDOR_ID_REDHAT;
|
||||
k->device_id = PCI_DEVICE_ID_REDHAT_BRIDGE;
|
||||
k->class_id = PCI_CLASS_BRIDGE_PCI;
|
||||
dc->desc = "Standard PCI Bridge";
|
||||
device_class_set_legacy_reset(dc, qdev_pci_bridge_dev_reset);
|
||||
device_class_set_props(dc, pci_bridge_dev_properties);
|
||||
dc->vmsd = &pci_bridge_dev_vmstate;
|
||||
set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
|
||||
hc->plug = pci_bridge_dev_plug_cb;
|
||||
hc->unplug = pci_bridge_dev_unplug_cb;
|
||||
hc->unplug_request = pci_bridge_dev_unplug_request_cb;
|
||||
}
|
||||
|
||||
static const TypeInfo pci_bridge_dev_info = {
|
||||
.name = TYPE_PCI_BRIDGE_DEV,
|
||||
.parent = TYPE_PCI_BRIDGE,
|
||||
.instance_size = sizeof(PCIBridgeDev),
|
||||
.class_init = pci_bridge_dev_class_init,
|
||||
.instance_finalize = pci_bridge_dev_instance_finalize,
|
||||
.interfaces = (const InterfaceInfo[]) {
|
||||
{ TYPE_HOTPLUG_HANDLER },
|
||||
{ INTERFACE_CONVENTIONAL_PCI_DEVICE },
|
||||
{ }
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Multiseat bridge. Same as the standard pci bridge, only with a
|
||||
* different pci id, so we can match it easily in the guest for
|
||||
* automagic multiseat configuration. See docs/multiseat.txt for more.
|
||||
*/
|
||||
static void pci_bridge_dev_seat_class_init(ObjectClass *klass, const void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
|
||||
|
||||
k->device_id = PCI_DEVICE_ID_REDHAT_BRIDGE_SEAT;
|
||||
dc->desc = "Standard PCI Bridge (multiseat)";
|
||||
}
|
||||
|
||||
static const TypeInfo pci_bridge_dev_seat_info = {
|
||||
.name = TYPE_PCI_BRIDGE_SEAT_DEV,
|
||||
.parent = TYPE_PCI_BRIDGE_DEV,
|
||||
.instance_size = sizeof(PCIBridgeDev),
|
||||
.class_init = pci_bridge_dev_seat_class_init,
|
||||
};
|
||||
|
||||
static void pci_bridge_dev_register(void)
|
||||
{
|
||||
type_register_static(&pci_bridge_dev_info);
|
||||
type_register_static(&pci_bridge_dev_seat_info);
|
||||
}
|
||||
|
||||
type_init(pci_bridge_dev_register);
|
||||
@@ -0,0 +1,562 @@
|
||||
/*
|
||||
* PCI Expander Bridge Device Emulation
|
||||
*
|
||||
* Copyright (C) 2015 Red Hat Inc
|
||||
*
|
||||
* Authors:
|
||||
* Marcel Apfelbaum <[email protected]>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qapi/error.h"
|
||||
#include "hw/pci/pci.h"
|
||||
#include "hw/pci/pci_bus.h"
|
||||
#include "hw/pci/pci_host.h"
|
||||
#include "hw/pci/pcie_port.h"
|
||||
#include "hw/core/qdev-properties.h"
|
||||
#include "hw/pci/pci_bridge.h"
|
||||
#include "hw/pci-bridge/pci_expander_bridge.h"
|
||||
#include "hw/cxl/cxl.h"
|
||||
#include "qemu/range.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/module.h"
|
||||
#include "system/numa.h"
|
||||
#include "hw/core/boards.h"
|
||||
#include "qom/object.h"
|
||||
|
||||
enum BusType { PCI, PCIE, CXL };
|
||||
|
||||
#define TYPE_PXB_BUS "pxb-bus"
|
||||
typedef struct PXBBus PXBBus;
|
||||
DECLARE_INSTANCE_CHECKER(PXBBus, PXB_BUS,
|
||||
TYPE_PXB_BUS)
|
||||
|
||||
DECLARE_INSTANCE_CHECKER(PXBBus, PXB_PCIE_BUS,
|
||||
TYPE_PXB_PCIE_BUS)
|
||||
|
||||
DECLARE_INSTANCE_CHECKER(PXBBus, PXB_CXL_BUS,
|
||||
TYPE_PXB_CXL_BUS)
|
||||
|
||||
struct PXBBus {
|
||||
/*< private >*/
|
||||
PCIBus parent_obj;
|
||||
/*< public >*/
|
||||
|
||||
char bus_path[8];
|
||||
};
|
||||
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(PXBPCIEDev, PXB_PCIE_DEV)
|
||||
|
||||
static GList *pxb_dev_list;
|
||||
|
||||
#define TYPE_PXB_HOST "pxb-host"
|
||||
|
||||
CXLComponentState *cxl_get_hb_cstate(PCIHostState *hb)
|
||||
{
|
||||
CXLHost *host = PXB_CXL_HOST(hb);
|
||||
|
||||
return &host->cxl_cstate;
|
||||
}
|
||||
|
||||
bool cxl_get_hb_passthrough(PCIHostState *hb)
|
||||
{
|
||||
CXLHost *host = PXB_CXL_HOST(hb);
|
||||
|
||||
return host->passthrough;
|
||||
}
|
||||
|
||||
static int pxb_bus_num(PCIBus *bus)
|
||||
{
|
||||
PXBDev *pxb = PXB_DEV(bus->parent_dev);
|
||||
|
||||
return pxb->bus_nr;
|
||||
}
|
||||
|
||||
static uint16_t pxb_bus_numa_node(PCIBus *bus)
|
||||
{
|
||||
PXBDev *pxb = PXB_DEV(bus->parent_dev);
|
||||
|
||||
return pxb->numa_node;
|
||||
}
|
||||
|
||||
static void prop_pxb_uid_get(Object *obj, Visitor *v, const char *name,
|
||||
void *opaque, Error **errp)
|
||||
{
|
||||
PCIBus *bus = PCI_BUS(obj);
|
||||
uint32_t uid;
|
||||
|
||||
if (!bus->parent_dev) {
|
||||
error_setg(errp, "bus not attached to a device");
|
||||
return;
|
||||
}
|
||||
uid = pci_bus_num(bus);
|
||||
visit_type_uint32(v, name, &uid, errp);
|
||||
}
|
||||
|
||||
static void pxb_bus_class_init(ObjectClass *class, const void *data)
|
||||
{
|
||||
PCIBusClass *pbc = PCI_BUS_CLASS(class);
|
||||
|
||||
pbc->bus_num = pxb_bus_num;
|
||||
pbc->numa_node = pxb_bus_numa_node;
|
||||
|
||||
object_class_property_add(class, "acpi_uid", "uint32",
|
||||
prop_pxb_uid_get, NULL, NULL, NULL);
|
||||
object_class_property_set_description(class, "acpi_uid",
|
||||
"ACPI Unique ID used to distinguish this PCI Host Bridge / ACPI00016");
|
||||
}
|
||||
|
||||
static const TypeInfo pxb_bus_info = {
|
||||
.name = TYPE_PXB_BUS,
|
||||
.parent = TYPE_PCI_BUS,
|
||||
.instance_size = sizeof(PXBBus),
|
||||
.class_init = pxb_bus_class_init,
|
||||
};
|
||||
|
||||
static const TypeInfo pxb_pcie_bus_info = {
|
||||
.name = TYPE_PXB_PCIE_BUS,
|
||||
.parent = TYPE_PCIE_BUS,
|
||||
.instance_size = sizeof(PXBBus),
|
||||
.class_init = pxb_bus_class_init,
|
||||
};
|
||||
|
||||
static const TypeInfo pxb_cxl_bus_info = {
|
||||
.name = TYPE_PXB_CXL_BUS,
|
||||
.parent = TYPE_CXL_BUS,
|
||||
.instance_size = sizeof(PXBBus),
|
||||
.class_init = pxb_bus_class_init,
|
||||
};
|
||||
|
||||
static const char *pxb_host_root_bus_path(PCIHostState *host_bridge,
|
||||
PCIBus *rootbus)
|
||||
{
|
||||
PXBBus *bus = pci_bus_is_cxl(rootbus) ?
|
||||
PXB_CXL_BUS(rootbus) :
|
||||
pci_bus_is_express(rootbus) ? PXB_PCIE_BUS(rootbus) :
|
||||
PXB_BUS(rootbus);
|
||||
|
||||
snprintf(bus->bus_path, 8, "0000:%02x", pxb_bus_num(rootbus));
|
||||
return bus->bus_path;
|
||||
}
|
||||
|
||||
static char *pxb_host_ofw_unit_address(const SysBusDevice *dev)
|
||||
{
|
||||
const PCIHostState *pxb_host;
|
||||
const PCIBus *pxb_bus;
|
||||
const PXBDev *pxb_dev;
|
||||
int position;
|
||||
const DeviceState *pxb_dev_base;
|
||||
const PCIHostState *main_host;
|
||||
const SysBusDevice *main_host_sbd;
|
||||
|
||||
pxb_host = PCI_HOST_BRIDGE(dev);
|
||||
pxb_bus = pxb_host->bus;
|
||||
pxb_dev = PXB_DEV(pxb_bus->parent_dev);
|
||||
position = g_list_index(pxb_dev_list, pxb_dev);
|
||||
assert(position >= 0);
|
||||
|
||||
pxb_dev_base = DEVICE(pxb_dev);
|
||||
main_host = PCI_HOST_BRIDGE(pxb_dev_base->parent_bus->parent);
|
||||
main_host_sbd = SYS_BUS_DEVICE(main_host);
|
||||
|
||||
if (main_host_sbd->num_mmio > 0) {
|
||||
return g_strdup_printf(HWADDR_FMT_plx ",%x",
|
||||
main_host_sbd->mmio[0].addr, position + 1);
|
||||
}
|
||||
if (main_host_sbd->num_pio > 0) {
|
||||
return g_strdup_printf("i%04x,%x",
|
||||
main_host_sbd->pio[0], position + 1);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void pxb_host_class_init(ObjectClass *class, const void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(class);
|
||||
SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(class);
|
||||
PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(class);
|
||||
|
||||
dc->fw_name = "pci";
|
||||
/* Reason: Internal part of the pxb/pxb-pcie device, not usable by itself */
|
||||
dc->user_creatable = false;
|
||||
sbc->explicit_ofw_unit_address = pxb_host_ofw_unit_address;
|
||||
hc->root_bus_path = pxb_host_root_bus_path;
|
||||
}
|
||||
|
||||
static const TypeInfo pxb_host_info = {
|
||||
.name = TYPE_PXB_HOST,
|
||||
.parent = TYPE_PCI_HOST_BRIDGE,
|
||||
.class_init = pxb_host_class_init,
|
||||
};
|
||||
|
||||
static void pxb_cxl_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
|
||||
CXLHost *cxl = PXB_CXL_HOST(dev);
|
||||
CXLComponentState *cxl_cstate = &cxl->cxl_cstate;
|
||||
struct MemoryRegion *mr = &cxl_cstate->crb.component_registers;
|
||||
|
||||
cxl_component_register_block_init(OBJECT(dev), cxl_cstate,
|
||||
TYPE_PXB_CXL_HOST);
|
||||
sysbus_init_mmio(sbd, mr);
|
||||
}
|
||||
|
||||
/*
|
||||
* Host bridge realization has no means of knowning state associated
|
||||
* with a particular machine. As such, it is nececssary to delay
|
||||
* final setup of the host bridge register space until later in the
|
||||
* machine bring up.
|
||||
*/
|
||||
void pxb_cxl_hook_up_registers(CXLState *cxl_state, PCIBus *bus, Error **errp)
|
||||
{
|
||||
PXBCXLDev *pxb = PXB_CXL_DEV(pci_bridge_get_device(bus));
|
||||
CXLHost *cxl = pxb->cxl_host_bridge;
|
||||
CXLComponentState *cxl_cstate = &cxl->cxl_cstate;
|
||||
struct MemoryRegion *mr = &cxl_cstate->crb.component_registers;
|
||||
hwaddr offset;
|
||||
|
||||
offset = memory_region_size(mr) * cxl_state->next_mr_idx;
|
||||
if (offset > memory_region_size(&cxl_state->host_mr)) {
|
||||
error_setg(errp, "Insufficient space for pxb cxl host register space");
|
||||
return;
|
||||
}
|
||||
|
||||
memory_region_add_subregion(&cxl_state->host_mr, offset, mr);
|
||||
cxl_state->next_mr_idx++;
|
||||
}
|
||||
|
||||
static void pxb_cxl_host_class_init(ObjectClass *class, const void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(class);
|
||||
PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(class);
|
||||
|
||||
hc->root_bus_path = pxb_host_root_bus_path;
|
||||
dc->fw_name = "cxl";
|
||||
dc->realize = pxb_cxl_realize;
|
||||
/* Reason: Internal part of the pxb/pxb-pcie device, not usable by itself */
|
||||
dc->user_creatable = false;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is a device to handle the MMIO for a CXL host bridge. It does nothing
|
||||
* else.
|
||||
*/
|
||||
static const TypeInfo cxl_host_info = {
|
||||
.name = TYPE_PXB_CXL_HOST,
|
||||
.parent = TYPE_PCI_HOST_BRIDGE,
|
||||
.instance_size = sizeof(CXLHost),
|
||||
.class_init = pxb_cxl_host_class_init,
|
||||
};
|
||||
|
||||
/*
|
||||
* Registers the PXB bus as a child of pci host root bus.
|
||||
*/
|
||||
static void pxb_register_bus(PCIDevice *dev, PCIBus *pxb_bus, Error **errp)
|
||||
{
|
||||
PCIBus *bus = pci_get_bus(dev);
|
||||
int pxb_bus_num = pci_bus_num(pxb_bus);
|
||||
|
||||
if (bus->parent_dev) {
|
||||
error_setg(errp, "PXB devices can be attached only to root bus");
|
||||
return;
|
||||
}
|
||||
|
||||
QLIST_FOREACH(bus, &bus->child, sibling) {
|
||||
if (pci_bus_num(bus) == pxb_bus_num) {
|
||||
error_setg(errp, "Bus %d is already in use", pxb_bus_num);
|
||||
return;
|
||||
}
|
||||
}
|
||||
QLIST_INSERT_HEAD(&pci_get_bus(dev)->child, pxb_bus, sibling);
|
||||
}
|
||||
|
||||
static int pxb_map_irq_fn(PCIDevice *pci_dev, int pin)
|
||||
{
|
||||
PCIDevice *pxb = pci_get_bus(pci_dev)->parent_dev;
|
||||
|
||||
/*
|
||||
* First carry out normal swizzle to handle
|
||||
* multiple root ports on a pxb instance.
|
||||
*/
|
||||
pin = pci_swizzle_map_irq_fn(pci_dev, pin);
|
||||
|
||||
/*
|
||||
* The bios does not index the pxb slot number when
|
||||
* it computes the IRQ because it resides on bus 0
|
||||
* and not on the current bus.
|
||||
* However QEMU routes the irq through bus 0 and adds
|
||||
* the pxb slot to the IRQ computation of the PXB
|
||||
* device.
|
||||
*
|
||||
* Synchronize between bios and QEMU by canceling
|
||||
* pxb's effect.
|
||||
*/
|
||||
return pin - PCI_SLOT(pxb->devfn);
|
||||
}
|
||||
|
||||
static void pxb_cxl_dev_reset(DeviceState *dev)
|
||||
{
|
||||
CXLHost *cxl = PXB_CXL_DEV(dev)->cxl_host_bridge;
|
||||
CXLComponentState *cxl_cstate = &cxl->cxl_cstate;
|
||||
PCIHostState *hb = PCI_HOST_BRIDGE(cxl);
|
||||
uint32_t *reg_state = cxl_cstate->crb.cache_mem_registers;
|
||||
uint32_t *write_msk = cxl_cstate->crb.cache_mem_regs_write_mask;
|
||||
int dsp_count = 0;
|
||||
|
||||
cxl_component_register_init_common(reg_state, write_msk, CXL2_RC, false);
|
||||
/*
|
||||
* The CXL specification allows for host bridges with no HDM decoders
|
||||
* if they only have a single root port.
|
||||
*/
|
||||
if (!PXB_CXL_DEV(dev)->hdm_for_passthrough) {
|
||||
dsp_count = pcie_count_ds_ports(hb->bus);
|
||||
}
|
||||
/* Initial reset will have 0 dsp so wait until > 0 */
|
||||
if (dsp_count == 1) {
|
||||
cxl->passthrough = true;
|
||||
/* Set Capability ID in header to NONE */
|
||||
ARRAY_FIELD_DP32(reg_state, CXL_HDM_CAPABILITY_HEADER, ID, 0);
|
||||
} else {
|
||||
ARRAY_FIELD_DP32(reg_state, CXL_HDM_DECODER_CAPABILITY, TARGET_COUNT,
|
||||
8);
|
||||
}
|
||||
}
|
||||
|
||||
static gint pxb_compare(gconstpointer a, gconstpointer b)
|
||||
{
|
||||
const PXBDev *pxb_a = a, *pxb_b = b;
|
||||
|
||||
return pxb_a->bus_nr < pxb_b->bus_nr ? -1 :
|
||||
pxb_a->bus_nr > pxb_b->bus_nr ? 1 :
|
||||
0;
|
||||
}
|
||||
|
||||
static bool pxb_dev_realize_common(PCIDevice *dev, enum BusType type,
|
||||
Error **errp)
|
||||
{
|
||||
PXBDev *pxb = PXB_DEV(dev);
|
||||
DeviceState *ds, *bds = NULL;
|
||||
PCIBus *bus;
|
||||
const char *dev_name = NULL;
|
||||
Error *local_err = NULL;
|
||||
MachineState *ms = MACHINE(qdev_get_machine());
|
||||
|
||||
if (ms->numa_state == NULL) {
|
||||
error_setg(errp, "NUMA is not supported by this machine-type");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pxb->numa_node != NUMA_NODE_UNASSIGNED &&
|
||||
pxb->numa_node >= ms->numa_state->num_nodes) {
|
||||
error_setg(errp, "Illegal numa node %d", pxb->numa_node);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (dev->qdev.id && *dev->qdev.id) {
|
||||
dev_name = dev->qdev.id;
|
||||
}
|
||||
|
||||
ds = qdev_new(type == CXL ? TYPE_PXB_CXL_HOST : TYPE_PXB_HOST);
|
||||
if (type == PCIE) {
|
||||
bus = pci_root_bus_new(ds, dev_name, NULL, NULL, 0, TYPE_PXB_PCIE_BUS);
|
||||
} else if (type == CXL) {
|
||||
bus = pci_root_bus_new(ds, dev_name, NULL, NULL, 0, TYPE_PXB_CXL_BUS);
|
||||
bus->flags |= PCI_BUS_CXL;
|
||||
PXB_CXL_DEV(dev)->cxl_host_bridge = PXB_CXL_HOST(ds);
|
||||
} else {
|
||||
bus = pci_root_bus_new(ds, "pxb-internal", NULL, NULL, 0, TYPE_PXB_BUS);
|
||||
bds = qdev_new("pci-bridge");
|
||||
bds->id = g_strdup(dev_name);
|
||||
qdev_prop_set_uint8(bds, PCI_BRIDGE_DEV_PROP_CHASSIS_NR, pxb->bus_nr);
|
||||
qdev_prop_set_bit(bds, PCI_BRIDGE_DEV_PROP_SHPC, false);
|
||||
}
|
||||
|
||||
bus->parent_dev = dev;
|
||||
bus->address_space_mem = pci_get_bus(dev)->address_space_mem;
|
||||
bus->address_space_io = pci_get_bus(dev)->address_space_io;
|
||||
bus->map_irq = pxb_map_irq_fn;
|
||||
|
||||
PCI_HOST_BRIDGE(ds)->bus = bus;
|
||||
PCI_HOST_BRIDGE(ds)->bypass_iommu = pxb->bypass_iommu;
|
||||
|
||||
pxb_register_bus(dev, bus, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
goto err_register_bus;
|
||||
}
|
||||
|
||||
sysbus_realize_and_unref(SYS_BUS_DEVICE(ds), &error_fatal);
|
||||
if (bds) {
|
||||
qdev_realize_and_unref(bds, &bus->qbus, &error_fatal);
|
||||
}
|
||||
|
||||
pci_word_test_and_set_mask(dev->config + PCI_STATUS,
|
||||
PCI_STATUS_66MHZ | PCI_STATUS_FAST_BACK);
|
||||
pci_config_set_class(dev->config, PCI_CLASS_BRIDGE_HOST);
|
||||
|
||||
pxb_dev_list = g_list_insert_sorted(pxb_dev_list, pxb, pxb_compare);
|
||||
return true;
|
||||
|
||||
err_register_bus:
|
||||
object_unref(OBJECT(bds));
|
||||
object_unparent(OBJECT(bus));
|
||||
object_unref(OBJECT(ds));
|
||||
return false;
|
||||
}
|
||||
|
||||
static void pxb_dev_realize(PCIDevice *dev, Error **errp)
|
||||
{
|
||||
if (pci_bus_is_express(pci_get_bus(dev))) {
|
||||
error_setg(errp, "pxb devices cannot reside on a PCIe bus");
|
||||
return;
|
||||
}
|
||||
|
||||
pxb_dev_realize_common(dev, PCI, errp);
|
||||
}
|
||||
|
||||
static void pxb_dev_exitfn(PCIDevice *pci_dev)
|
||||
{
|
||||
PXBDev *pxb = PXB_DEV(pci_dev);
|
||||
|
||||
pxb_dev_list = g_list_remove(pxb_dev_list, pxb);
|
||||
}
|
||||
|
||||
static const Property pxb_dev_properties[] = {
|
||||
/* Note: 0 is not a legal PXB bus number. */
|
||||
DEFINE_PROP_UINT8("bus_nr", PXBDev, bus_nr, 0),
|
||||
DEFINE_PROP_UINT16("numa_node", PXBDev, numa_node, NUMA_NODE_UNASSIGNED),
|
||||
DEFINE_PROP_BOOL("bypass_iommu", PXBDev, bypass_iommu, false),
|
||||
};
|
||||
|
||||
static void pxb_dev_class_init(ObjectClass *klass, const void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
|
||||
|
||||
k->realize = pxb_dev_realize;
|
||||
k->exit = pxb_dev_exitfn;
|
||||
k->vendor_id = PCI_VENDOR_ID_REDHAT;
|
||||
k->device_id = PCI_DEVICE_ID_REDHAT_PXB;
|
||||
k->class_id = PCI_CLASS_BRIDGE_HOST;
|
||||
|
||||
dc->desc = "PCI Expander Bridge";
|
||||
device_class_set_props(dc, pxb_dev_properties);
|
||||
dc->hotpluggable = false;
|
||||
set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
|
||||
}
|
||||
|
||||
static const TypeInfo pxb_dev_info = {
|
||||
.name = TYPE_PXB_DEV,
|
||||
.parent = TYPE_PCI_DEVICE,
|
||||
.instance_size = sizeof(PXBDev),
|
||||
.class_init = pxb_dev_class_init,
|
||||
.interfaces = (const InterfaceInfo[]) {
|
||||
{ INTERFACE_CONVENTIONAL_PCI_DEVICE },
|
||||
{ },
|
||||
},
|
||||
};
|
||||
|
||||
static void pxb_pcie_dev_realize(PCIDevice *dev, Error **errp)
|
||||
{
|
||||
if (!pci_bus_is_express(pci_get_bus(dev))) {
|
||||
error_setg(errp, "pxb-pcie devices cannot reside on a PCI bus");
|
||||
return;
|
||||
}
|
||||
|
||||
pxb_dev_realize_common(dev, PCIE, errp);
|
||||
}
|
||||
|
||||
static void pxb_pcie_dev_class_init(ObjectClass *klass, const void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
|
||||
|
||||
k->realize = pxb_pcie_dev_realize;
|
||||
k->exit = pxb_dev_exitfn;
|
||||
k->vendor_id = PCI_VENDOR_ID_REDHAT;
|
||||
k->device_id = PCI_DEVICE_ID_REDHAT_PXB_PCIE;
|
||||
k->class_id = PCI_CLASS_BRIDGE_HOST;
|
||||
|
||||
dc->desc = "PCI Express Expander Bridge";
|
||||
dc->hotpluggable = false;
|
||||
set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
|
||||
}
|
||||
|
||||
static const TypeInfo pxb_pcie_dev_info = {
|
||||
.name = TYPE_PXB_PCIE_DEV,
|
||||
.parent = TYPE_PXB_DEV,
|
||||
.instance_size = sizeof(PXBPCIEDev),
|
||||
.class_init = pxb_pcie_dev_class_init,
|
||||
.interfaces = (const InterfaceInfo[]) {
|
||||
{ INTERFACE_CONVENTIONAL_PCI_DEVICE },
|
||||
{ },
|
||||
},
|
||||
};
|
||||
|
||||
static void pxb_cxl_dev_realize(PCIDevice *dev, Error **errp)
|
||||
{
|
||||
/* A CXL PXB's parent bus is still PCIe */
|
||||
if (!pci_bus_is_express(pci_get_bus(dev))) {
|
||||
error_setg(errp, "pxb-cxl devices cannot reside on a PCI bus");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!pxb_dev_realize_common(dev, CXL, errp)) {
|
||||
return;
|
||||
}
|
||||
pxb_cxl_dev_reset(DEVICE(dev));
|
||||
}
|
||||
|
||||
static const Property pxb_cxl_dev_properties[] = {
|
||||
DEFINE_PROP_BOOL("hdm_for_passthrough", PXBCXLDev, hdm_for_passthrough, false),
|
||||
};
|
||||
|
||||
static void pxb_cxl_dev_class_init(ObjectClass *klass, const void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
|
||||
|
||||
k->realize = pxb_cxl_dev_realize;
|
||||
k->exit = pxb_dev_exitfn;
|
||||
/*
|
||||
* XXX: These types of bridges don't actually show up in the hierarchy so
|
||||
* vendor, device, class, etc. ids are intentionally left out.
|
||||
*/
|
||||
|
||||
dc->desc = "CXL Host Bridge";
|
||||
device_class_set_props(dc, pxb_cxl_dev_properties);
|
||||
set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
|
||||
|
||||
/* Host bridges aren't hotpluggable. FIXME: spec reference */
|
||||
dc->hotpluggable = false;
|
||||
device_class_set_legacy_reset(dc, pxb_cxl_dev_reset);
|
||||
}
|
||||
|
||||
static const TypeInfo pxb_cxl_dev_info = {
|
||||
.name = TYPE_PXB_CXL_DEV,
|
||||
.parent = TYPE_PXB_PCIE_DEV,
|
||||
.instance_size = sizeof(PXBCXLDev),
|
||||
.class_init = pxb_cxl_dev_class_init,
|
||||
.interfaces =
|
||||
(const InterfaceInfo[]){
|
||||
{ INTERFACE_CONVENTIONAL_PCI_DEVICE },
|
||||
{},
|
||||
},
|
||||
};
|
||||
|
||||
static void pxb_register_types(void)
|
||||
{
|
||||
type_register_static(&pxb_bus_info);
|
||||
type_register_static(&pxb_pcie_bus_info);
|
||||
type_register_static(&pxb_cxl_bus_info);
|
||||
type_register_static(&pxb_host_info);
|
||||
type_register_static(&cxl_host_info);
|
||||
type_register_static(&pxb_dev_info);
|
||||
type_register_static(&pxb_pcie_dev_info);
|
||||
type_register_static(&pxb_cxl_dev_info);
|
||||
}
|
||||
|
||||
type_init(pxb_register_types)
|
||||
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*
|
||||
* Stubs for calls made from machines to handle the case where CONFIG_PXB
|
||||
* is not enabled.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "qapi/error.h"
|
||||
#include "hw/pci/pci.h"
|
||||
#include "hw/pci/pci_bus.h"
|
||||
#include "hw/pci-bridge/pci_expander_bridge.h"
|
||||
#include "hw/cxl/cxl.h"
|
||||
|
||||
void pxb_cxl_hook_up_registers(CXLState *state, PCIBus *bus, Error **errp) {};
|
||||
@@ -0,0 +1,177 @@
|
||||
/*
|
||||
* QEMU Generic PCIE-PCI Bridge
|
||||
*
|
||||
* Copyright (c) 2017 Aleksandr Bezzubikov
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/module.h"
|
||||
#include "hw/pci/pci.h"
|
||||
#include "hw/pci/pci_bus.h"
|
||||
#include "hw/pci/pci_bridge.h"
|
||||
#include "hw/pci/msi.h"
|
||||
#include "hw/pci/shpc.h"
|
||||
#include "hw/pci/slotid_cap.h"
|
||||
#include "hw/core/qdev-properties.h"
|
||||
#include "qom/object.h"
|
||||
|
||||
struct PCIEPCIBridge {
|
||||
/*< private >*/
|
||||
PCIBridge parent_obj;
|
||||
|
||||
OnOffAuto msi;
|
||||
MemoryRegion shpc_bar;
|
||||
/*< public >*/
|
||||
};
|
||||
|
||||
#define TYPE_PCIE_PCI_BRIDGE_DEV "pcie-pci-bridge"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(PCIEPCIBridge, PCIE_PCI_BRIDGE_DEV)
|
||||
|
||||
static void pcie_pci_bridge_realize(PCIDevice *d, Error **errp)
|
||||
{
|
||||
PCIBridge *br = PCI_BRIDGE(d);
|
||||
PCIEPCIBridge *pcie_br = PCIE_PCI_BRIDGE_DEV(d);
|
||||
int rc, pos;
|
||||
|
||||
pci_bridge_initfn(d, TYPE_PCI_BUS);
|
||||
|
||||
d->config[PCI_INTERRUPT_PIN] = 0x1;
|
||||
memory_region_init(&pcie_br->shpc_bar, OBJECT(d), "shpc-bar",
|
||||
shpc_bar_size(d));
|
||||
rc = shpc_init(d, &br->sec_bus, &pcie_br->shpc_bar, 0, errp);
|
||||
if (rc) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
rc = pcie_cap_init(d, 0, PCI_EXP_TYPE_PCI_BRIDGE, 0, errp);
|
||||
if (rc < 0) {
|
||||
goto cap_error;
|
||||
}
|
||||
|
||||
pos = pci_pm_init(d, 0, errp);
|
||||
if (pos < 0) {
|
||||
goto pm_error;
|
||||
}
|
||||
pci_set_word(d->config + pos + PCI_PM_PMC, 0x3);
|
||||
|
||||
pcie_cap_arifwd_init(d);
|
||||
pcie_cap_deverr_init(d);
|
||||
|
||||
rc = pcie_aer_init(d, PCI_ERR_VER, 0x100, PCI_ERR_SIZEOF, errp);
|
||||
if (rc < 0) {
|
||||
goto aer_error;
|
||||
}
|
||||
|
||||
Error *local_err = NULL;
|
||||
if (pcie_br->msi != ON_OFF_AUTO_OFF) {
|
||||
rc = msi_init(d, 0, 1, true, true, &local_err);
|
||||
if (rc < 0) {
|
||||
assert(rc == -ENOTSUP);
|
||||
if (pcie_br->msi != ON_OFF_AUTO_ON) {
|
||||
error_free(local_err);
|
||||
} else {
|
||||
/* failed to satisfy user's explicit request for MSI */
|
||||
error_propagate(errp, local_err);
|
||||
goto msi_error;
|
||||
}
|
||||
}
|
||||
}
|
||||
pci_register_bar(d, 0, PCI_BASE_ADDRESS_SPACE_MEMORY |
|
||||
PCI_BASE_ADDRESS_MEM_TYPE_64, &pcie_br->shpc_bar);
|
||||
return;
|
||||
|
||||
msi_error:
|
||||
pcie_aer_exit(d);
|
||||
aer_error:
|
||||
pm_error:
|
||||
pcie_cap_exit(d);
|
||||
cap_error:
|
||||
shpc_cleanup(d, &pcie_br->shpc_bar);
|
||||
error:
|
||||
pci_bridge_exitfn(d);
|
||||
}
|
||||
|
||||
static void pcie_pci_bridge_exit(PCIDevice *d)
|
||||
{
|
||||
PCIEPCIBridge *bridge_dev = PCIE_PCI_BRIDGE_DEV(d);
|
||||
pcie_cap_exit(d);
|
||||
shpc_cleanup(d, &bridge_dev->shpc_bar);
|
||||
pci_bridge_exitfn(d);
|
||||
}
|
||||
|
||||
static void pcie_pci_bridge_reset(DeviceState *qdev)
|
||||
{
|
||||
PCIDevice *d = PCI_DEVICE(qdev);
|
||||
pci_bridge_reset(qdev);
|
||||
if (msi_present(d)) {
|
||||
msi_reset(d);
|
||||
}
|
||||
shpc_reset(d);
|
||||
}
|
||||
|
||||
static void pcie_pci_bridge_write_config(PCIDevice *d,
|
||||
uint32_t address, uint32_t val, int len)
|
||||
{
|
||||
pci_bridge_write_config(d, address, val, len);
|
||||
if (msi_present(d)) {
|
||||
msi_write_config(d, address, val, len);
|
||||
}
|
||||
shpc_cap_write_config(d, address, val, len);
|
||||
}
|
||||
|
||||
static const Property pcie_pci_bridge_dev_properties[] = {
|
||||
DEFINE_PROP_ON_OFF_AUTO("msi", PCIEPCIBridge, msi, ON_OFF_AUTO_AUTO),
|
||||
};
|
||||
|
||||
static const VMStateDescription pcie_pci_bridge_dev_vmstate = {
|
||||
.name = TYPE_PCIE_PCI_BRIDGE_DEV,
|
||||
.priority = MIG_PRI_PCI_BUS,
|
||||
.fields = (const VMStateField[]) {
|
||||
VMSTATE_PCI_DEVICE(parent_obj, PCIBridge),
|
||||
SHPC_VMSTATE(shpc, PCIDevice, NULL),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static void pcie_pci_bridge_class_init(ObjectClass *klass, const void *data)
|
||||
{
|
||||
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass);
|
||||
|
||||
k->vendor_id = PCI_VENDOR_ID_REDHAT;
|
||||
k->device_id = PCI_DEVICE_ID_REDHAT_PCIE_BRIDGE;
|
||||
k->realize = pcie_pci_bridge_realize;
|
||||
k->exit = pcie_pci_bridge_exit;
|
||||
k->config_write = pcie_pci_bridge_write_config;
|
||||
dc->vmsd = &pcie_pci_bridge_dev_vmstate;
|
||||
device_class_set_props(dc, pcie_pci_bridge_dev_properties);
|
||||
device_class_set_legacy_reset(dc, pcie_pci_bridge_reset);
|
||||
set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
|
||||
hc->plug = pci_bridge_dev_plug_cb;
|
||||
hc->unplug = pci_bridge_dev_unplug_cb;
|
||||
hc->unplug_request = pci_bridge_dev_unplug_request_cb;
|
||||
}
|
||||
|
||||
static const TypeInfo pcie_pci_bridge_info = {
|
||||
.name = TYPE_PCIE_PCI_BRIDGE_DEV,
|
||||
.parent = TYPE_PCI_BRIDGE,
|
||||
.instance_size = sizeof(PCIEPCIBridge),
|
||||
.class_init = pcie_pci_bridge_class_init,
|
||||
.interfaces = (const InterfaceInfo[]) {
|
||||
{ TYPE_HOTPLUG_HANDLER },
|
||||
{ INTERFACE_PCIE_DEVICE },
|
||||
{ },
|
||||
}
|
||||
};
|
||||
|
||||
static void pciepci_register(void)
|
||||
{
|
||||
type_register_static(&pcie_pci_bridge_info);
|
||||
}
|
||||
|
||||
type_init(pciepci_register);
|
||||
@@ -0,0 +1,201 @@
|
||||
/*
|
||||
* Base class for PCI Express Root Ports
|
||||
*
|
||||
* Copyright (C) 2017 Red Hat Inc
|
||||
*
|
||||
* Authors:
|
||||
* Marcel Apfelbaum <[email protected]>
|
||||
*
|
||||
* Most of the code was migrated from hw/pci-bridge/ioh3420.
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/module.h"
|
||||
#include "hw/pci/pcie_port.h"
|
||||
#include "hw/core/qdev-properties.h"
|
||||
|
||||
static void rp_aer_vector_update(PCIDevice *d)
|
||||
{
|
||||
PCIERootPortClass *rpc = PCIE_ROOT_PORT_GET_CLASS(d);
|
||||
|
||||
if (rpc->aer_vector) {
|
||||
pcie_aer_root_set_vector(d, rpc->aer_vector(d));
|
||||
}
|
||||
}
|
||||
|
||||
static void rp_write_config(PCIDevice *d, uint32_t address,
|
||||
uint32_t val, int len)
|
||||
{
|
||||
uint32_t root_cmd =
|
||||
pci_get_long(d->config + d->exp.aer_cap + PCI_ERR_ROOT_COMMAND);
|
||||
uint16_t slt_ctl, slt_sta;
|
||||
|
||||
pcie_cap_slot_get(d, &slt_ctl, &slt_sta);
|
||||
|
||||
pci_bridge_write_config(d, address, val, len);
|
||||
rp_aer_vector_update(d);
|
||||
pcie_cap_slot_write_config(d, slt_ctl, slt_sta, address, val, len);
|
||||
pcie_aer_write_config(d, address, val, len);
|
||||
pcie_aer_root_write_config(d, address, val, len, root_cmd);
|
||||
}
|
||||
|
||||
static void rp_reset_hold(Object *obj, ResetType type)
|
||||
{
|
||||
PCIDevice *d = PCI_DEVICE(obj);
|
||||
DeviceState *qdev = DEVICE(obj);
|
||||
|
||||
rp_aer_vector_update(d);
|
||||
pcie_cap_root_reset(d);
|
||||
pcie_cap_deverr_reset(d);
|
||||
pcie_cap_slot_reset(d);
|
||||
pcie_cap_arifwd_reset(d);
|
||||
pcie_acs_reset(d);
|
||||
pcie_aer_root_reset(d);
|
||||
pci_bridge_reset(qdev);
|
||||
pci_bridge_disable_base_limit(d);
|
||||
}
|
||||
|
||||
static void rp_realize(PCIDevice *d, Error **errp)
|
||||
{
|
||||
PCIEPort *p = PCIE_PORT(d);
|
||||
PCIESlot *s = PCIE_SLOT(d);
|
||||
PCIDeviceClass *dc = PCI_DEVICE_GET_CLASS(d);
|
||||
PCIERootPortClass *rpc = PCIE_ROOT_PORT_GET_CLASS(d);
|
||||
int rc;
|
||||
|
||||
pci_config_set_interrupt_pin(d->config, 1);
|
||||
if (d->cap_present & QEMU_PCIE_CAP_CXL) {
|
||||
pci_bridge_initfn(d, TYPE_CXL_BUS);
|
||||
} else {
|
||||
pci_bridge_initfn(d, TYPE_PCIE_BUS);
|
||||
}
|
||||
pcie_port_init_reg(d);
|
||||
|
||||
rc = pci_bridge_ssvid_init(d, rpc->ssvid_offset, dc->vendor_id,
|
||||
rpc->ssid, errp);
|
||||
if (rc < 0) {
|
||||
error_append_hint(errp, "Can't init SSV ID, error %d\n", rc);
|
||||
goto err_bridge;
|
||||
}
|
||||
|
||||
if (rpc->interrupts_init) {
|
||||
rc = rpc->interrupts_init(d, errp);
|
||||
if (rc < 0) {
|
||||
goto err_bridge;
|
||||
}
|
||||
}
|
||||
|
||||
rc = pcie_cap_init(d, rpc->exp_offset, PCI_EXP_TYPE_ROOT_PORT,
|
||||
p->port, errp);
|
||||
if (rc < 0) {
|
||||
error_append_hint(errp, "Can't add Root Port capability, "
|
||||
"error %d\n", rc);
|
||||
goto err_int;
|
||||
}
|
||||
|
||||
pcie_cap_arifwd_init(d);
|
||||
pcie_cap_deverr_init(d);
|
||||
pcie_cap_slot_init(d, s);
|
||||
pcie_cap_root_init(d);
|
||||
|
||||
pcie_chassis_create(s->chassis);
|
||||
rc = pcie_chassis_add_slot(s);
|
||||
if (rc < 0) {
|
||||
error_setg(errp, "Can't add chassis slot, error %d", rc);
|
||||
goto err_pcie_cap;
|
||||
}
|
||||
|
||||
rc = pcie_aer_init(d, PCI_ERR_VER, rpc->aer_offset,
|
||||
PCI_ERR_SIZEOF, errp);
|
||||
if (rc < 0) {
|
||||
goto err;
|
||||
}
|
||||
pcie_aer_root_init(d);
|
||||
rp_aer_vector_update(d);
|
||||
|
||||
if (rpc->acs_offset) {
|
||||
pcie_acs_init(d, rpc->acs_offset);
|
||||
}
|
||||
return;
|
||||
|
||||
err:
|
||||
pcie_chassis_del_slot(s);
|
||||
err_pcie_cap:
|
||||
pcie_cap_exit(d);
|
||||
err_int:
|
||||
if (rpc->interrupts_uninit) {
|
||||
rpc->interrupts_uninit(d);
|
||||
}
|
||||
err_bridge:
|
||||
pci_bridge_exitfn(d);
|
||||
}
|
||||
|
||||
static void rp_exit(PCIDevice *d)
|
||||
{
|
||||
PCIERootPortClass *rpc = PCIE_ROOT_PORT_GET_CLASS(d);
|
||||
PCIESlot *s = PCIE_SLOT(d);
|
||||
|
||||
pcie_aer_exit(d);
|
||||
pcie_chassis_del_slot(s);
|
||||
pcie_cap_exit(d);
|
||||
if (rpc->interrupts_uninit) {
|
||||
rpc->interrupts_uninit(d);
|
||||
}
|
||||
pci_bridge_exitfn(d);
|
||||
}
|
||||
|
||||
static const Property rp_props[] = {
|
||||
DEFINE_PROP_BIT(COMPAT_PROP_PCP, PCIDevice, cap_present,
|
||||
QEMU_PCIE_SLTCAP_PCP_BITNR, true),
|
||||
};
|
||||
|
||||
static void rp_instance_post_init(Object *obj)
|
||||
{
|
||||
PCIESlot *s = PCIE_SLOT(obj);
|
||||
|
||||
if (!s->speed) {
|
||||
s->speed = QEMU_PCI_EXP_LNK_2_5GT;
|
||||
}
|
||||
|
||||
if (!s->width) {
|
||||
s->width = QEMU_PCI_EXP_LNK_X1;
|
||||
}
|
||||
}
|
||||
|
||||
static void rp_class_init(ObjectClass *klass, const void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
|
||||
ResettableClass *rc = RESETTABLE_CLASS(klass);
|
||||
|
||||
k->config_write = rp_write_config;
|
||||
k->realize = rp_realize;
|
||||
k->exit = rp_exit;
|
||||
set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
|
||||
rc->phases.hold = rp_reset_hold;
|
||||
device_class_set_props(dc, rp_props);
|
||||
}
|
||||
|
||||
static const TypeInfo rp_info = {
|
||||
.name = TYPE_PCIE_ROOT_PORT,
|
||||
.parent = TYPE_PCIE_SLOT,
|
||||
.instance_post_init = rp_instance_post_init,
|
||||
.class_init = rp_class_init,
|
||||
.abstract = true,
|
||||
.class_size = sizeof(PCIERootPortClass),
|
||||
.interfaces = (const InterfaceInfo[]) {
|
||||
{ INTERFACE_PCIE_DEVICE },
|
||||
{ }
|
||||
},
|
||||
};
|
||||
|
||||
static void rp_register_types(void)
|
||||
{
|
||||
type_register_static(&rp_info);
|
||||
}
|
||||
|
||||
type_init(rp_register_types)
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* QEMU Simba PCI bridge
|
||||
*
|
||||
* Copyright (c) 2006 Fabrice Bellard
|
||||
* Copyright (c) 2012,2013 Artyom Tarasenko
|
||||
* Copyright (c) 2018 Mark Cave-Ayland
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/pci/pci.h"
|
||||
#include "hw/pci/pci_bridge.h"
|
||||
#include "hw/pci/pci_bus.h"
|
||||
#include "qemu/module.h"
|
||||
#include "hw/pci-bridge/simba.h"
|
||||
|
||||
/*
|
||||
* Chipset docs:
|
||||
* APB: "Advanced PCI Bridge (APB) User's Manual",
|
||||
* http://www.sun.com/processors/manuals/805-1251.pdf
|
||||
*/
|
||||
|
||||
static void simba_pci_bridge_realize(PCIDevice *dev, Error **errp)
|
||||
{
|
||||
/*
|
||||
* command register:
|
||||
* According to PCI bridge spec, after reset
|
||||
* bus master bit is off
|
||||
* memory space enable bit is off
|
||||
* According to manual (805-1251.pdf).
|
||||
* the reset value should be zero unless the boot pin is tied high
|
||||
* (which is true) and thus it should be PCI_COMMAND_MEMORY.
|
||||
*/
|
||||
SimbaPCIBridge *br = SIMBA_PCI_BRIDGE(dev);
|
||||
|
||||
pci_bridge_initfn(dev, TYPE_PCI_BUS);
|
||||
|
||||
pci_set_word(dev->config + PCI_COMMAND, PCI_COMMAND_MEMORY);
|
||||
pci_set_word(dev->config + PCI_STATUS,
|
||||
PCI_STATUS_FAST_BACK | PCI_STATUS_66MHZ |
|
||||
PCI_STATUS_DEVSEL_MEDIUM);
|
||||
|
||||
/* Allow 32-bit IO addresses */
|
||||
pci_set_word(dev->config + PCI_IO_BASE, PCI_IO_RANGE_TYPE_32);
|
||||
pci_set_word(dev->config + PCI_IO_LIMIT, PCI_IO_RANGE_TYPE_32);
|
||||
pci_set_word(dev->wmask + PCI_IO_BASE_UPPER16, 0xffff);
|
||||
pci_set_word(dev->wmask + PCI_IO_LIMIT_UPPER16, 0xffff);
|
||||
|
||||
pci_bridge_update_mappings(PCI_BRIDGE(br));
|
||||
}
|
||||
|
||||
static void simba_pci_bridge_class_init(ObjectClass *klass, const void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
|
||||
|
||||
k->realize = simba_pci_bridge_realize;
|
||||
k->exit = pci_bridge_exitfn;
|
||||
k->vendor_id = PCI_VENDOR_ID_SUN;
|
||||
k->device_id = PCI_DEVICE_ID_SUN_SIMBA;
|
||||
k->revision = 0x11;
|
||||
k->config_write = pci_bridge_write_config;
|
||||
set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
|
||||
device_class_set_legacy_reset(dc, pci_bridge_reset);
|
||||
dc->vmsd = &vmstate_pci_device;
|
||||
}
|
||||
|
||||
static const TypeInfo simba_pci_bridge_info = {
|
||||
.name = TYPE_SIMBA_PCI_BRIDGE,
|
||||
.parent = TYPE_PCI_BRIDGE,
|
||||
.class_init = simba_pci_bridge_class_init,
|
||||
.instance_size = sizeof(SimbaPCIBridge),
|
||||
.interfaces = (const InterfaceInfo[]) {
|
||||
{ INTERFACE_CONVENTIONAL_PCI_DEVICE },
|
||||
{ },
|
||||
},
|
||||
};
|
||||
|
||||
static void simba_register_types(void)
|
||||
{
|
||||
type_register_static(&simba_pci_bridge_info);
|
||||
}
|
||||
|
||||
type_init(simba_register_types)
|
||||
@@ -0,0 +1,189 @@
|
||||
/*
|
||||
* x3130_downstream.c
|
||||
* TI X3130 pci express downstream port switch
|
||||
*
|
||||
* Copyright (c) 2010 Isaku Yamahata <yamahata at valinux co jp>
|
||||
* VA Linux Systems Japan K.K.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/pci/pci_ids.h"
|
||||
#include "hw/pci/msi.h"
|
||||
#include "hw/pci/pcie.h"
|
||||
#include "hw/pci/pcie_port.h"
|
||||
#include "hw/core/qdev-properties.h"
|
||||
#include "migration/vmstate.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/module.h"
|
||||
#include "hw/pci-bridge/xio3130_downstream.h"
|
||||
|
||||
#define PCI_DEVICE_ID_TI_XIO3130D 0x8233 /* downstream port */
|
||||
#define XIO3130_REVISION 0x1
|
||||
#define XIO3130_MSI_OFFSET 0x70
|
||||
#define XIO3130_MSI_SUPPORTED_FLAGS PCI_MSI_FLAGS_64BIT
|
||||
#define XIO3130_MSI_NR_VECTOR 1
|
||||
#define XIO3130_SSVID_OFFSET 0x80
|
||||
#define XIO3130_SSVID_SVID 0
|
||||
#define XIO3130_SSVID_SSID 0
|
||||
#define XIO3130_EXP_OFFSET 0x90
|
||||
#define XIO3130_AER_OFFSET 0x100
|
||||
|
||||
static void xio3130_downstream_write_config(PCIDevice *d, uint32_t address,
|
||||
uint32_t val, int len)
|
||||
{
|
||||
uint16_t slt_ctl, slt_sta;
|
||||
|
||||
pcie_cap_slot_get(d, &slt_ctl, &slt_sta);
|
||||
pci_bridge_write_config(d, address, val, len);
|
||||
pcie_cap_flr_write_config(d, address, val, len);
|
||||
pcie_cap_slot_write_config(d, slt_ctl, slt_sta, address, val, len);
|
||||
pcie_aer_write_config(d, address, val, len);
|
||||
}
|
||||
|
||||
static void xio3130_downstream_reset(DeviceState *qdev)
|
||||
{
|
||||
PCIDevice *d = PCI_DEVICE(qdev);
|
||||
|
||||
pcie_cap_deverr_reset(d);
|
||||
pcie_cap_slot_reset(d);
|
||||
pcie_cap_arifwd_reset(d);
|
||||
pci_bridge_reset(qdev);
|
||||
}
|
||||
|
||||
static void xio3130_downstream_realize(PCIDevice *d, Error **errp)
|
||||
{
|
||||
PCIEPort *p = PCIE_PORT(d);
|
||||
PCIESlot *s = PCIE_SLOT(d);
|
||||
int rc;
|
||||
|
||||
pci_bridge_initfn(d, TYPE_PCIE_BUS);
|
||||
pcie_port_init_reg(d);
|
||||
|
||||
rc = msi_init(d, XIO3130_MSI_OFFSET, XIO3130_MSI_NR_VECTOR,
|
||||
XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_64BIT,
|
||||
XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_MASKBIT,
|
||||
errp);
|
||||
if (rc < 0) {
|
||||
assert(rc == -ENOTSUP);
|
||||
goto err_bridge;
|
||||
}
|
||||
|
||||
rc = pci_bridge_ssvid_init(d, XIO3130_SSVID_OFFSET,
|
||||
XIO3130_SSVID_SVID, XIO3130_SSVID_SSID,
|
||||
errp);
|
||||
if (rc < 0) {
|
||||
goto err_msi;
|
||||
}
|
||||
|
||||
rc = pcie_cap_init(d, XIO3130_EXP_OFFSET, PCI_EXP_TYPE_DOWNSTREAM,
|
||||
p->port, errp);
|
||||
if (rc < 0) {
|
||||
goto err_msi;
|
||||
}
|
||||
pcie_cap_flr_init(d);
|
||||
pcie_cap_deverr_init(d);
|
||||
pcie_cap_slot_init(d, s);
|
||||
pcie_cap_arifwd_init(d);
|
||||
|
||||
pcie_chassis_create(s->chassis);
|
||||
rc = pcie_chassis_add_slot(s);
|
||||
if (rc < 0) {
|
||||
error_setg(errp, "Can't add chassis slot, error %d", rc);
|
||||
goto err_pcie_cap;
|
||||
}
|
||||
|
||||
rc = pcie_aer_init(d, PCI_ERR_VER, XIO3130_AER_OFFSET,
|
||||
PCI_ERR_SIZEOF, errp);
|
||||
if (rc < 0) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
err:
|
||||
pcie_chassis_del_slot(s);
|
||||
err_pcie_cap:
|
||||
pcie_cap_exit(d);
|
||||
err_msi:
|
||||
msi_uninit(d);
|
||||
err_bridge:
|
||||
pci_bridge_exitfn(d);
|
||||
}
|
||||
|
||||
static void xio3130_downstream_exitfn(PCIDevice *d)
|
||||
{
|
||||
PCIESlot *s = PCIE_SLOT(d);
|
||||
|
||||
pcie_aer_exit(d);
|
||||
pcie_chassis_del_slot(s);
|
||||
pcie_cap_exit(d);
|
||||
msi_uninit(d);
|
||||
pci_bridge_exitfn(d);
|
||||
}
|
||||
|
||||
static const Property xio3130_downstream_props[] = {
|
||||
DEFINE_PROP_BIT(COMPAT_PROP_PCP, PCIDevice, cap_present,
|
||||
QEMU_PCIE_SLTCAP_PCP_BITNR, true),
|
||||
};
|
||||
|
||||
static const VMStateDescription vmstate_xio3130_downstream = {
|
||||
.name = "xio3130-express-downstream-port",
|
||||
.priority = MIG_PRI_PCI_BUS,
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.post_load = pcie_cap_slot_post_load,
|
||||
.fields = (const VMStateField[]) {
|
||||
VMSTATE_PCI_DEVICE(parent_obj.parent_obj.parent_obj, PCIESlot),
|
||||
VMSTATE_STRUCT(parent_obj.parent_obj.parent_obj.exp.aer_log,
|
||||
PCIESlot, 0, vmstate_pcie_aer_log, PCIEAERLog),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static void xio3130_downstream_class_init(ObjectClass *klass, const void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
|
||||
|
||||
k->config_write = xio3130_downstream_write_config;
|
||||
k->realize = xio3130_downstream_realize;
|
||||
k->exit = xio3130_downstream_exitfn;
|
||||
k->vendor_id = PCI_VENDOR_ID_TI;
|
||||
k->device_id = PCI_DEVICE_ID_TI_XIO3130D;
|
||||
k->revision = XIO3130_REVISION;
|
||||
set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
|
||||
dc->desc = "TI X3130 Downstream Port of PCI Express Switch";
|
||||
device_class_set_legacy_reset(dc, xio3130_downstream_reset);
|
||||
dc->vmsd = &vmstate_xio3130_downstream;
|
||||
device_class_set_props(dc, xio3130_downstream_props);
|
||||
}
|
||||
|
||||
static const TypeInfo xio3130_downstream_info = {
|
||||
.name = TYPE_XIO3130_DOWNSTREAM,
|
||||
.parent = TYPE_PCIE_SLOT,
|
||||
.class_init = xio3130_downstream_class_init,
|
||||
.interfaces = (const InterfaceInfo[]) {
|
||||
{ INTERFACE_PCIE_DEVICE },
|
||||
{ }
|
||||
},
|
||||
};
|
||||
|
||||
static void xio3130_downstream_register_types(void)
|
||||
{
|
||||
type_register_static(&xio3130_downstream_info);
|
||||
}
|
||||
|
||||
type_init(xio3130_downstream_register_types)
|
||||
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
* xio3130_upstream.c
|
||||
* TI X3130 pci express upstream port switch
|
||||
*
|
||||
* Copyright (c) 2010 Isaku Yamahata <yamahata at valinux co jp>
|
||||
* VA Linux Systems Japan K.K.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/pci/pci_ids.h"
|
||||
#include "hw/pci/msi.h"
|
||||
#include "hw/pci/pcie.h"
|
||||
#include "hw/pci/pcie_port.h"
|
||||
#include "migration/vmstate.h"
|
||||
#include "qemu/module.h"
|
||||
|
||||
#define PCI_DEVICE_ID_TI_XIO3130U 0x8232 /* upstream port */
|
||||
#define XIO3130_REVISION 0x2
|
||||
#define XIO3130_MSI_OFFSET 0x70
|
||||
#define XIO3130_MSI_SUPPORTED_FLAGS PCI_MSI_FLAGS_64BIT
|
||||
#define XIO3130_MSI_NR_VECTOR 1
|
||||
#define XIO3130_SSVID_OFFSET 0x80
|
||||
#define XIO3130_SSVID_SVID 0
|
||||
#define XIO3130_SSVID_SSID 0
|
||||
#define XIO3130_EXP_OFFSET 0x90
|
||||
#define XIO3130_AER_OFFSET 0x100
|
||||
|
||||
static void xio3130_upstream_write_config(PCIDevice *d, uint32_t address,
|
||||
uint32_t val, int len)
|
||||
{
|
||||
pci_bridge_write_config(d, address, val, len);
|
||||
pcie_cap_flr_write_config(d, address, val, len);
|
||||
pcie_aer_write_config(d, address, val, len);
|
||||
}
|
||||
|
||||
static void xio3130_upstream_reset(DeviceState *qdev)
|
||||
{
|
||||
PCIDevice *d = PCI_DEVICE(qdev);
|
||||
|
||||
pci_bridge_reset(qdev);
|
||||
pcie_cap_deverr_reset(d);
|
||||
}
|
||||
|
||||
static void xio3130_upstream_realize(PCIDevice *d, Error **errp)
|
||||
{
|
||||
PCIEPort *p = PCIE_PORT(d);
|
||||
int rc;
|
||||
|
||||
pci_bridge_initfn(d, TYPE_PCIE_BUS);
|
||||
pcie_port_init_reg(d);
|
||||
|
||||
rc = msi_init(d, XIO3130_MSI_OFFSET, XIO3130_MSI_NR_VECTOR,
|
||||
XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_64BIT,
|
||||
XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_MASKBIT,
|
||||
errp);
|
||||
if (rc < 0) {
|
||||
assert(rc == -ENOTSUP);
|
||||
goto err_bridge;
|
||||
}
|
||||
|
||||
rc = pci_bridge_ssvid_init(d, XIO3130_SSVID_OFFSET,
|
||||
XIO3130_SSVID_SVID, XIO3130_SSVID_SSID,
|
||||
errp);
|
||||
if (rc < 0) {
|
||||
goto err_msi;
|
||||
}
|
||||
|
||||
rc = pcie_cap_init(d, XIO3130_EXP_OFFSET, PCI_EXP_TYPE_UPSTREAM,
|
||||
p->port, errp);
|
||||
if (rc < 0) {
|
||||
goto err_msi;
|
||||
}
|
||||
pcie_cap_flr_init(d);
|
||||
pcie_cap_deverr_init(d);
|
||||
|
||||
rc = pcie_aer_init(d, PCI_ERR_VER, XIO3130_AER_OFFSET,
|
||||
PCI_ERR_SIZEOF, errp);
|
||||
if (rc < 0) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
err:
|
||||
pcie_cap_exit(d);
|
||||
err_msi:
|
||||
msi_uninit(d);
|
||||
err_bridge:
|
||||
pci_bridge_exitfn(d);
|
||||
}
|
||||
|
||||
static void xio3130_upstream_exitfn(PCIDevice *d)
|
||||
{
|
||||
pcie_aer_exit(d);
|
||||
pcie_cap_exit(d);
|
||||
msi_uninit(d);
|
||||
pci_bridge_exitfn(d);
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_xio3130_upstream = {
|
||||
.name = "xio3130-express-upstream-port",
|
||||
.priority = MIG_PRI_PCI_BUS,
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.fields = (const VMStateField[]) {
|
||||
VMSTATE_PCI_DEVICE(parent_obj.parent_obj, PCIEPort),
|
||||
VMSTATE_STRUCT(parent_obj.parent_obj.exp.aer_log, PCIEPort, 0,
|
||||
vmstate_pcie_aer_log, PCIEAERLog),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static void xio3130_upstream_class_init(ObjectClass *klass, const void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
|
||||
|
||||
k->config_write = xio3130_upstream_write_config;
|
||||
k->realize = xio3130_upstream_realize;
|
||||
k->exit = xio3130_upstream_exitfn;
|
||||
k->vendor_id = PCI_VENDOR_ID_TI;
|
||||
k->device_id = PCI_DEVICE_ID_TI_XIO3130U;
|
||||
k->revision = XIO3130_REVISION;
|
||||
set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
|
||||
dc->desc = "TI X3130 Upstream Port of PCI Express Switch";
|
||||
device_class_set_legacy_reset(dc, xio3130_upstream_reset);
|
||||
dc->vmsd = &vmstate_xio3130_upstream;
|
||||
}
|
||||
|
||||
static const TypeInfo xio3130_upstream_info = {
|
||||
.name = "x3130-upstream",
|
||||
.parent = TYPE_PCIE_PORT,
|
||||
.class_init = xio3130_upstream_class_init,
|
||||
.interfaces = (const InterfaceInfo[]) {
|
||||
{ INTERFACE_PCIE_DEVICE },
|
||||
{ }
|
||||
},
|
||||
};
|
||||
|
||||
static void xio3130_upstream_register_types(void)
|
||||
{
|
||||
type_register_static(&xio3130_upstream_info);
|
||||
}
|
||||
|
||||
type_init(xio3130_upstream_register_types)
|
||||
Reference in New Issue
Block a user