1e4e6db52SDaniel Henrique Barboza /* 2e4e6db52SDaniel Henrique Barboza * QEMU PowerPC PowerNV Proxy PHB model 3e4e6db52SDaniel Henrique Barboza * 4e4e6db52SDaniel Henrique Barboza * Copyright (c) 2022, IBM Corporation. 5e4e6db52SDaniel Henrique Barboza * 6e4e6db52SDaniel Henrique Barboza * This code is licensed under the GPL version 2 or later. See the 7e4e6db52SDaniel Henrique Barboza * COPYING file in the top-level directory. 8e4e6db52SDaniel Henrique Barboza */ 9e4e6db52SDaniel Henrique Barboza 10e4e6db52SDaniel Henrique Barboza #include "qemu/osdep.h" 11e4e6db52SDaniel Henrique Barboza #include "qemu/log.h" 12e4e6db52SDaniel Henrique Barboza #include "qapi/visitor.h" 13e4e6db52SDaniel Henrique Barboza #include "qapi/error.h" 14e4e6db52SDaniel Henrique Barboza #include "hw/pci-host/pnv_phb.h" 15e4e6db52SDaniel Henrique Barboza #include "hw/pci-host/pnv_phb3.h" 16e4e6db52SDaniel Henrique Barboza #include "hw/pci-host/pnv_phb4.h" 17e4e6db52SDaniel Henrique Barboza #include "hw/ppc/pnv.h" 18e4e6db52SDaniel Henrique Barboza #include "hw/qdev-properties.h" 19e4e6db52SDaniel Henrique Barboza #include "qom/object.h" 20e4e6db52SDaniel Henrique Barboza 21e5ea9436SDaniel Henrique Barboza /* 22e5ea9436SDaniel Henrique Barboza * Attach a root port device. 23e5ea9436SDaniel Henrique Barboza * 24e5ea9436SDaniel Henrique Barboza * 'index' will be used both as a PCIE slot value and to calculate 25e5ea9436SDaniel Henrique Barboza * QOM id. 'chip_id' is going to be used as PCIE chassis for the 26e5ea9436SDaniel Henrique Barboza * root port. 27e5ea9436SDaniel Henrique Barboza */ 28*c2f3f78aSDaniel Henrique Barboza static void pnv_phb_attach_root_port(PCIHostState *pci) 29e5ea9436SDaniel Henrique Barboza { 30e5ea9436SDaniel Henrique Barboza PCIDevice *root = pci_new(PCI_DEVFN(0, 0), TYPE_PNV_PHB_ROOT_PORT); 31e5ea9436SDaniel Henrique Barboza const char *dev_id = DEVICE(root)->id; 32*c2f3f78aSDaniel Henrique Barboza g_autofree char *default_id = NULL; 33*c2f3f78aSDaniel Henrique Barboza int index; 34*c2f3f78aSDaniel Henrique Barboza 35*c2f3f78aSDaniel Henrique Barboza index = object_property_get_int(OBJECT(pci->bus), "phb-id", &error_fatal); 36*c2f3f78aSDaniel Henrique Barboza default_id = g_strdup_printf("%s[%d]", TYPE_PNV_PHB_ROOT_PORT, index); 37e5ea9436SDaniel Henrique Barboza 38e5ea9436SDaniel Henrique Barboza object_property_add_child(OBJECT(pci->bus), dev_id ? dev_id : default_id, 39e5ea9436SDaniel Henrique Barboza OBJECT(root)); 40e5ea9436SDaniel Henrique Barboza 41e5ea9436SDaniel Henrique Barboza pci_realize_and_unref(root, pci->bus, &error_fatal); 42e5ea9436SDaniel Henrique Barboza } 43e4e6db52SDaniel Henrique Barboza 44e4e6db52SDaniel Henrique Barboza static void pnv_phb_realize(DeviceState *dev, Error **errp) 45e4e6db52SDaniel Henrique Barboza { 46e4e6db52SDaniel Henrique Barboza PnvPHB *phb = PNV_PHB(dev); 47e4e6db52SDaniel Henrique Barboza PCIHostState *pci = PCI_HOST_BRIDGE(dev); 48e4e6db52SDaniel Henrique Barboza g_autofree char *phb_typename = NULL; 49e4e6db52SDaniel Henrique Barboza 50e4e6db52SDaniel Henrique Barboza if (!phb->version) { 51e4e6db52SDaniel Henrique Barboza error_setg(errp, "version not specified"); 52e4e6db52SDaniel Henrique Barboza return; 53e4e6db52SDaniel Henrique Barboza } 54e4e6db52SDaniel Henrique Barboza 55e4e6db52SDaniel Henrique Barboza switch (phb->version) { 56e4e6db52SDaniel Henrique Barboza case 3: 57e4e6db52SDaniel Henrique Barboza phb_typename = g_strdup(TYPE_PNV_PHB3); 58e4e6db52SDaniel Henrique Barboza break; 59e4e6db52SDaniel Henrique Barboza case 4: 60e4e6db52SDaniel Henrique Barboza phb_typename = g_strdup(TYPE_PNV_PHB4); 61e4e6db52SDaniel Henrique Barboza break; 62e4e6db52SDaniel Henrique Barboza case 5: 63e4e6db52SDaniel Henrique Barboza phb_typename = g_strdup(TYPE_PNV_PHB5); 64e4e6db52SDaniel Henrique Barboza break; 65e4e6db52SDaniel Henrique Barboza default: 66e4e6db52SDaniel Henrique Barboza g_assert_not_reached(); 67e4e6db52SDaniel Henrique Barboza } 68e4e6db52SDaniel Henrique Barboza 69e4e6db52SDaniel Henrique Barboza phb->backend = object_new(phb_typename); 70e4e6db52SDaniel Henrique Barboza object_property_add_child(OBJECT(dev), "phb-backend", phb->backend); 71e4e6db52SDaniel Henrique Barboza 72e4e6db52SDaniel Henrique Barboza /* Passthrough child device properties to the proxy device */ 73e4e6db52SDaniel Henrique Barboza object_property_set_uint(phb->backend, "index", phb->phb_id, errp); 74e4e6db52SDaniel Henrique Barboza object_property_set_uint(phb->backend, "chip-id", phb->chip_id, errp); 75e4e6db52SDaniel Henrique Barboza object_property_set_link(phb->backend, "phb-base", OBJECT(phb), errp); 76e4e6db52SDaniel Henrique Barboza 77e4e6db52SDaniel Henrique Barboza if (phb->version == 3) { 78e4e6db52SDaniel Henrique Barboza object_property_set_link(phb->backend, "chip", 79e4e6db52SDaniel Henrique Barboza OBJECT(phb->chip), errp); 80e4e6db52SDaniel Henrique Barboza } else { 81e4e6db52SDaniel Henrique Barboza object_property_set_link(phb->backend, "pec", OBJECT(phb->pec), errp); 82e4e6db52SDaniel Henrique Barboza } 83e4e6db52SDaniel Henrique Barboza 84e4e6db52SDaniel Henrique Barboza if (!qdev_realize(DEVICE(phb->backend), NULL, errp)) { 85e4e6db52SDaniel Henrique Barboza return; 86e4e6db52SDaniel Henrique Barboza } 87e4e6db52SDaniel Henrique Barboza 88e4e6db52SDaniel Henrique Barboza if (phb->version == 3) { 89e4e6db52SDaniel Henrique Barboza pnv_phb3_bus_init(dev, PNV_PHB3(phb->backend)); 90fe5bfd4bSDaniel Henrique Barboza } else { 91fe5bfd4bSDaniel Henrique Barboza pnv_phb4_bus_init(dev, PNV_PHB4(phb->backend)); 92e4e6db52SDaniel Henrique Barboza } 93e4e6db52SDaniel Henrique Barboza 94*c2f3f78aSDaniel Henrique Barboza pnv_phb_attach_root_port(pci); 95e4e6db52SDaniel Henrique Barboza } 96e4e6db52SDaniel Henrique Barboza 97e4e6db52SDaniel Henrique Barboza static const char *pnv_phb_root_bus_path(PCIHostState *host_bridge, 98e4e6db52SDaniel Henrique Barboza PCIBus *rootbus) 99e4e6db52SDaniel Henrique Barboza { 100e4e6db52SDaniel Henrique Barboza PnvPHB *phb = PNV_PHB(host_bridge); 101e4e6db52SDaniel Henrique Barboza 102e4e6db52SDaniel Henrique Barboza snprintf(phb->bus_path, sizeof(phb->bus_path), "00%02x:%02x", 103e4e6db52SDaniel Henrique Barboza phb->chip_id, phb->phb_id); 104e4e6db52SDaniel Henrique Barboza return phb->bus_path; 105e4e6db52SDaniel Henrique Barboza } 106e4e6db52SDaniel Henrique Barboza 107e4e6db52SDaniel Henrique Barboza static Property pnv_phb_properties[] = { 108e4e6db52SDaniel Henrique Barboza DEFINE_PROP_UINT32("index", PnvPHB, phb_id, 0), 109e4e6db52SDaniel Henrique Barboza DEFINE_PROP_UINT32("chip-id", PnvPHB, chip_id, 0), 110e4e6db52SDaniel Henrique Barboza DEFINE_PROP_UINT32("version", PnvPHB, version, 0), 111e4e6db52SDaniel Henrique Barboza 112e4e6db52SDaniel Henrique Barboza DEFINE_PROP_LINK("chip", PnvPHB, chip, TYPE_PNV_CHIP, PnvChip *), 113e4e6db52SDaniel Henrique Barboza 114e4e6db52SDaniel Henrique Barboza DEFINE_PROP_LINK("pec", PnvPHB, pec, TYPE_PNV_PHB4_PEC, 115e4e6db52SDaniel Henrique Barboza PnvPhb4PecState *), 116e4e6db52SDaniel Henrique Barboza 117e4e6db52SDaniel Henrique Barboza DEFINE_PROP_END_OF_LIST(), 118e4e6db52SDaniel Henrique Barboza }; 119e4e6db52SDaniel Henrique Barboza 120e4e6db52SDaniel Henrique Barboza static void pnv_phb_class_init(ObjectClass *klass, void *data) 121e4e6db52SDaniel Henrique Barboza { 122e4e6db52SDaniel Henrique Barboza PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(klass); 123e4e6db52SDaniel Henrique Barboza DeviceClass *dc = DEVICE_CLASS(klass); 124e4e6db52SDaniel Henrique Barboza 125e4e6db52SDaniel Henrique Barboza hc->root_bus_path = pnv_phb_root_bus_path; 126e4e6db52SDaniel Henrique Barboza dc->realize = pnv_phb_realize; 127e4e6db52SDaniel Henrique Barboza device_class_set_props(dc, pnv_phb_properties); 128e4e6db52SDaniel Henrique Barboza set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); 129e4e6db52SDaniel Henrique Barboza dc->user_creatable = false; 130e4e6db52SDaniel Henrique Barboza } 131e4e6db52SDaniel Henrique Barboza 1325ba76b61SDaniel Henrique Barboza static void pnv_phb_root_port_reset(DeviceState *dev) 133e4e6db52SDaniel Henrique Barboza { 1345ba76b61SDaniel Henrique Barboza PCIERootPortClass *rpc = PCIE_ROOT_PORT_GET_CLASS(dev); 1355ba76b61SDaniel Henrique Barboza PnvPHBRootPort *phb_rp = PNV_PHB_ROOT_PORT(dev); 1365ba76b61SDaniel Henrique Barboza PCIDevice *d = PCI_DEVICE(dev); 1375ba76b61SDaniel Henrique Barboza uint8_t *conf = d->config; 1385ba76b61SDaniel Henrique Barboza 1395ba76b61SDaniel Henrique Barboza rpc->parent_reset(dev); 1405ba76b61SDaniel Henrique Barboza 1415ba76b61SDaniel Henrique Barboza if (phb_rp->version == 3) { 1425ba76b61SDaniel Henrique Barboza return; 1435ba76b61SDaniel Henrique Barboza } 1445ba76b61SDaniel Henrique Barboza 1455ba76b61SDaniel Henrique Barboza /* PHB4 and later requires these extra reset steps */ 1465ba76b61SDaniel Henrique Barboza pci_byte_test_and_set_mask(conf + PCI_IO_BASE, 1475ba76b61SDaniel Henrique Barboza PCI_IO_RANGE_MASK & 0xff); 1485ba76b61SDaniel Henrique Barboza pci_byte_test_and_clear_mask(conf + PCI_IO_LIMIT, 1495ba76b61SDaniel Henrique Barboza PCI_IO_RANGE_MASK & 0xff); 1505ba76b61SDaniel Henrique Barboza pci_set_word(conf + PCI_MEMORY_BASE, 0); 1515ba76b61SDaniel Henrique Barboza pci_set_word(conf + PCI_MEMORY_LIMIT, 0xfff0); 1525ba76b61SDaniel Henrique Barboza pci_set_word(conf + PCI_PREF_MEMORY_BASE, 0x1); 1535ba76b61SDaniel Henrique Barboza pci_set_word(conf + PCI_PREF_MEMORY_LIMIT, 0xfff1); 1545ba76b61SDaniel Henrique Barboza pci_set_long(conf + PCI_PREF_BASE_UPPER32, 0x1); /* Hack */ 1555ba76b61SDaniel Henrique Barboza pci_set_long(conf + PCI_PREF_LIMIT_UPPER32, 0xffffffff); 1565ba76b61SDaniel Henrique Barboza pci_config_set_interrupt_pin(conf, 0); 1575ba76b61SDaniel Henrique Barboza } 1585ba76b61SDaniel Henrique Barboza 1595ba76b61SDaniel Henrique Barboza static void pnv_phb_root_port_realize(DeviceState *dev, Error **errp) 1605ba76b61SDaniel Henrique Barboza { 1615ba76b61SDaniel Henrique Barboza PCIERootPortClass *rpc = PCIE_ROOT_PORT_GET_CLASS(dev); 1625ba76b61SDaniel Henrique Barboza PnvPHBRootPort *phb_rp = PNV_PHB_ROOT_PORT(dev); 163*c2f3f78aSDaniel Henrique Barboza PCIBus *bus = PCI_BUS(qdev_get_parent_bus(dev)); 1645ba76b61SDaniel Henrique Barboza PCIDevice *pci = PCI_DEVICE(dev); 1655ba76b61SDaniel Henrique Barboza uint16_t device_id = 0; 1665ba76b61SDaniel Henrique Barboza Error *local_err = NULL; 167*c2f3f78aSDaniel Henrique Barboza int chip_id, index; 168*c2f3f78aSDaniel Henrique Barboza 169*c2f3f78aSDaniel Henrique Barboza chip_id = object_property_get_int(OBJECT(bus), "chip-id", &error_fatal); 170*c2f3f78aSDaniel Henrique Barboza index = object_property_get_int(OBJECT(bus), "phb-id", &error_fatal); 171*c2f3f78aSDaniel Henrique Barboza 172*c2f3f78aSDaniel Henrique Barboza /* Set unique chassis/slot values for the root port */ 173*c2f3f78aSDaniel Henrique Barboza qdev_prop_set_uint8(dev, "chassis", chip_id); 174*c2f3f78aSDaniel Henrique Barboza qdev_prop_set_uint16(dev, "slot", index); 1755ba76b61SDaniel Henrique Barboza 1765ba76b61SDaniel Henrique Barboza rpc->parent_realize(dev, &local_err); 1775ba76b61SDaniel Henrique Barboza if (local_err) { 1785ba76b61SDaniel Henrique Barboza error_propagate(errp, local_err); 1795ba76b61SDaniel Henrique Barboza return; 1805ba76b61SDaniel Henrique Barboza } 1815ba76b61SDaniel Henrique Barboza 1825ba76b61SDaniel Henrique Barboza switch (phb_rp->version) { 1835ba76b61SDaniel Henrique Barboza case 3: 1845ba76b61SDaniel Henrique Barboza device_id = PNV_PHB3_DEVICE_ID; 1855ba76b61SDaniel Henrique Barboza break; 1865ba76b61SDaniel Henrique Barboza case 4: 1875ba76b61SDaniel Henrique Barboza device_id = PNV_PHB4_DEVICE_ID; 1885ba76b61SDaniel Henrique Barboza break; 1895ba76b61SDaniel Henrique Barboza case 5: 1905ba76b61SDaniel Henrique Barboza device_id = PNV_PHB5_DEVICE_ID; 1915ba76b61SDaniel Henrique Barboza break; 1925ba76b61SDaniel Henrique Barboza default: 1935ba76b61SDaniel Henrique Barboza g_assert_not_reached(); 1945ba76b61SDaniel Henrique Barboza } 1955ba76b61SDaniel Henrique Barboza 1965ba76b61SDaniel Henrique Barboza pci_config_set_device_id(pci->config, device_id); 1975ba76b61SDaniel Henrique Barboza pci_config_set_interrupt_pin(pci->config, 0); 1985ba76b61SDaniel Henrique Barboza } 1995ba76b61SDaniel Henrique Barboza 2005ba76b61SDaniel Henrique Barboza static Property pnv_phb_root_port_properties[] = { 2015ba76b61SDaniel Henrique Barboza DEFINE_PROP_UINT32("version", PnvPHBRootPort, version, 0), 2025ba76b61SDaniel Henrique Barboza 2035ba76b61SDaniel Henrique Barboza DEFINE_PROP_END_OF_LIST(), 2045ba76b61SDaniel Henrique Barboza }; 2055ba76b61SDaniel Henrique Barboza 2065ba76b61SDaniel Henrique Barboza static void pnv_phb_root_port_class_init(ObjectClass *klass, void *data) 2075ba76b61SDaniel Henrique Barboza { 2085ba76b61SDaniel Henrique Barboza DeviceClass *dc = DEVICE_CLASS(klass); 2095ba76b61SDaniel Henrique Barboza PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); 2105ba76b61SDaniel Henrique Barboza PCIERootPortClass *rpc = PCIE_ROOT_PORT_CLASS(klass); 2115ba76b61SDaniel Henrique Barboza 2125ba76b61SDaniel Henrique Barboza dc->desc = "IBM PHB PCIE Root Port"; 2135ba76b61SDaniel Henrique Barboza 2145ba76b61SDaniel Henrique Barboza device_class_set_props(dc, pnv_phb_root_port_properties); 2155ba76b61SDaniel Henrique Barboza device_class_set_parent_realize(dc, pnv_phb_root_port_realize, 2165ba76b61SDaniel Henrique Barboza &rpc->parent_realize); 2175ba76b61SDaniel Henrique Barboza device_class_set_parent_reset(dc, pnv_phb_root_port_reset, 2185ba76b61SDaniel Henrique Barboza &rpc->parent_reset); 2195ba76b61SDaniel Henrique Barboza dc->reset = &pnv_phb_root_port_reset; 2205ba76b61SDaniel Henrique Barboza dc->user_creatable = false; 2215ba76b61SDaniel Henrique Barboza 2225ba76b61SDaniel Henrique Barboza k->vendor_id = PCI_VENDOR_ID_IBM; 2235ba76b61SDaniel Henrique Barboza /* device_id will be written during realize() */ 2245ba76b61SDaniel Henrique Barboza k->device_id = 0; 2255ba76b61SDaniel Henrique Barboza k->revision = 0; 2265ba76b61SDaniel Henrique Barboza 2275ba76b61SDaniel Henrique Barboza rpc->exp_offset = 0x48; 2285ba76b61SDaniel Henrique Barboza rpc->aer_offset = 0x100; 2295ba76b61SDaniel Henrique Barboza } 2305ba76b61SDaniel Henrique Barboza 231e4e6db52SDaniel Henrique Barboza static const TypeInfo pnv_phb_type_info = { 232e4e6db52SDaniel Henrique Barboza .name = TYPE_PNV_PHB, 233e4e6db52SDaniel Henrique Barboza .parent = TYPE_PCIE_HOST_BRIDGE, 234e4e6db52SDaniel Henrique Barboza .instance_size = sizeof(PnvPHB), 235e4e6db52SDaniel Henrique Barboza .class_init = pnv_phb_class_init, 236e4e6db52SDaniel Henrique Barboza }; 237e4e6db52SDaniel Henrique Barboza 2385ba76b61SDaniel Henrique Barboza static const TypeInfo pnv_phb_root_port_info = { 2395ba76b61SDaniel Henrique Barboza .name = TYPE_PNV_PHB_ROOT_PORT, 2405ba76b61SDaniel Henrique Barboza .parent = TYPE_PCIE_ROOT_PORT, 2415ba76b61SDaniel Henrique Barboza .instance_size = sizeof(PnvPHBRootPort), 2425ba76b61SDaniel Henrique Barboza .class_init = pnv_phb_root_port_class_init, 2435ba76b61SDaniel Henrique Barboza }; 2445ba76b61SDaniel Henrique Barboza 2455ba76b61SDaniel Henrique Barboza static void pnv_phb_register_types(void) 2465ba76b61SDaniel Henrique Barboza { 247e4e6db52SDaniel Henrique Barboza type_register_static(&pnv_phb_type_info); 2485ba76b61SDaniel Henrique Barboza type_register_static(&pnv_phb_root_port_info); 249e4e6db52SDaniel Henrique Barboza } 2505ba76b61SDaniel Henrique Barboza 2515ba76b61SDaniel Henrique Barboza type_init(pnv_phb_register_types) 252