ad80e36744
We pass a ResetType argument to the Resettable class enter phase method, but we don't pass it to hold and exit, even though the callsites have it readily available. This means that if a device cared about the ResetType it would need to record it in the enter phase method to use later on. Pass the type to all three of the phase methods to avoid having to do that. Commit created with for dir in hw target include; do \ spatch --macro-file scripts/cocci-macro-file.h \ --sp-file scripts/coccinelle/reset-type.cocci \ --keep-comments --smpl-spacing --in-place \ --include-headers --dir $dir; done and no manual edits. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Edgar E. Iglesias <edgar.iglesias@amd.com> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Luc Michel <luc.michel@amd.com> Message-id: 20240412160809.1260625-5-peter.maydell@linaro.org
204 lines
5.1 KiB
C
204 lines
5.1 KiB
C
/*
|
|
* Base class for PCI Express Root Ports
|
|
*
|
|
* Copyright (C) 2017 Red Hat Inc
|
|
*
|
|
* Authors:
|
|
* Marcel Apfelbaum <marcel@redhat.com>
|
|
*
|
|
* 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/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 && !s->disable_acs) {
|
|
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 Property rp_props[] = {
|
|
DEFINE_PROP_BIT(COMPAT_PROP_PCP, PCIDevice, cap_present,
|
|
QEMU_PCIE_SLTCAP_PCP_BITNR, true),
|
|
DEFINE_PROP_BOOL("disable-acs", PCIESlot, disable_acs, false),
|
|
DEFINE_PROP_END_OF_LIST()
|
|
};
|
|
|
|
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, 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 = (InterfaceInfo[]) {
|
|
{ INTERFACE_PCIE_DEVICE },
|
|
{ }
|
|
},
|
|
};
|
|
|
|
static void rp_register_types(void)
|
|
{
|
|
type_register_static(&rp_info);
|
|
}
|
|
|
|
type_init(rp_register_types)
|