/* * Aspeed PCIe host controller * * Copyright (c) 2022 Cédric Le Goater * * This code is licensed under the GPL version 2 or later. See * the COPYING file in the top-level directory. */ #include "qemu/osdep.h" #include "qemu/module.h" #include "qemu/units.h" #include "qemu/log.h" #include "qapi/error.h" #include "hw/qdev-properties.h" #include "hw/irq.h" #include "hw/pci-host/aspeed_pcie.h" #include "hw/pci/msi.h" #include "trace.h" #define TO_REG(offset) ((offset) >> 2) #define PCI_VENDOR_ID_ASPEED 0x1A03 #define PCI_DEVICE_ID_AST2600_RC 0x1150 /* * RC */ #define ASPEED_PCIE_RC_CTRL 0x00 #define ASPEED_PCIE_RC_CTRL_RCL BIT(0) #define ASPEED_PCIE_RC_CTRL_RCL_RX BIT(1) #define ASPEED_PCIE_RC_CTRL_RX_WAIT_FW BIT(2) #define ASPEED_PCIE_RC_CTRL_RX_UNLOCK BIT(4) #define ASPEED_PCIE_RC_CTRL_RX_MSI BIT(6) #define ASPEED_PCIE_RC_CTRL_RX_MSI_SELECT BIT(7) #define ASPEED_PCIE_RC_CTRL_RX_LINEAR BIT(8) #define ASPEED_PCIE_RC_CTRL_RX_DMA BIT(9) #define ASPEED_PCIE_RC_CTRL_RX_TAG_MASK 0x00ff0000 #define ASPEED_PCIE_RC_INT_ENABLE 0x04 #define ASPEED_PCIE_RC_INT_ENABLE_INTA BIT(0) #define ASPEED_PCIE_RC_INT_ENABLE_INTB BIT(1) #define ASPEED_PCIE_RC_INT_ENABLE_INTC BIT(2) #define ASPEED_PCIE_RC_INT_ENABLE_INTD BIT(3) #define ASPEED_PCIE_RC_INT_ENABLE_RX BIT(4) #define ASPEED_PCIE_RC_INT_STATUS 0x08 #define ASPEED_PCIE_RC_INT_STATUS_INTA BIT(0) #define ASPEED_PCIE_RC_INT_STATUS_INTB BIT(1) #define ASPEED_PCIE_RC_INT_STATUS_INTC BIT(2) #define ASPEED_PCIE_RC_INT_STATUS_INTD BIT(3) #define ASPEED_PCIE_RC_INT_STATUS_INTX_MASK 0xf #define ASPEED_PCIE_RC_INT_STATUS_RX BIT(4) #define ASPEED_PCIE_RC_RX_DATA 0x0C #define ASPEED_PCIE_RC_RX_DW0 0x10 #define ASPEED_PCIE_RC_RX_DW1 0x14 #define ASPEED_PCIE_RC_RX_DW2 0x18 #define ASPEED_PCIE_RC_RX_DW3 0x1C #define ASPEED_PCIE_RC_MSI_ENABLE0 0x20 #define ASPEED_PCIE_RC_MSI_ENABLE1 0x24 #define ASPEED_PCIE_RC_MSI_STATUS0 0x28 #define ASPEED_PCIE_RC_MSI_STATUS1 0x2C #define ASPEED_PCIE_RC_TX_TAG 0x3C static void aspeed_pcie_rc_update_irq(AspeedPCIERc *s) { bool intx = !!(s->regs[TO_REG(ASPEED_PCIE_RC_INT_STATUS)] & s->regs[TO_REG(ASPEED_PCIE_RC_INT_ENABLE)]); bool msi0 = !!(s->regs[TO_REG(ASPEED_PCIE_RC_MSI_STATUS0)] & s->regs[TO_REG(ASPEED_PCIE_RC_MSI_ENABLE0)]); bool msi1 = !!(s->regs[TO_REG(ASPEED_PCIE_RC_MSI_STATUS1)] & s->regs[TO_REG(ASPEED_PCIE_RC_MSI_ENABLE1)]); bool level = intx || msi0 || msi1; qemu_set_irq(s->irq, level); } static uint64_t aspeed_pcie_rc_read(void *opaque, hwaddr addr, unsigned int size) { AspeedPCIERc *s = ASPEED_PCIE_RC(opaque); uint64_t val = 0; switch (addr) { case ASPEED_PCIE_RC_CTRL: case ASPEED_PCIE_RC_INT_ENABLE: case ASPEED_PCIE_RC_INT_STATUS: case ASPEED_PCIE_RC_RX_DATA: case ASPEED_PCIE_RC_RX_DW0: case ASPEED_PCIE_RC_RX_DW1: case ASPEED_PCIE_RC_RX_DW2: case ASPEED_PCIE_RC_RX_DW3: case ASPEED_PCIE_RC_MSI_ENABLE0: case ASPEED_PCIE_RC_MSI_ENABLE1: case ASPEED_PCIE_RC_MSI_STATUS0: case ASPEED_PCIE_RC_MSI_STATUS1: case ASPEED_PCIE_RC_TX_TAG: val = s->regs[TO_REG(addr)]; break; default: qemu_log_mask(LOG_GUEST_ERROR, "%s: Out-of-bounds read at offset 0x%" HWADDR_PRIx "\n", __func__, addr); break; } trace_aspeed_pcie_rc_read(addr, val); return val; } static void aspeed_pcie_rc_write(void *opaque, hwaddr addr, uint64_t data, unsigned int size) { AspeedPCIERc *s = ASPEED_PCIE_RC(opaque); trace_aspeed_pcie_rc_write(addr, data); switch (addr) { case ASPEED_PCIE_RC_CTRL: /* TODO: unlock/lock RX */ case ASPEED_PCIE_RC_INT_ENABLE: case ASPEED_PCIE_RC_MSI_ENABLE0: case ASPEED_PCIE_RC_MSI_ENABLE1: case ASPEED_PCIE_RC_TX_TAG: s->regs[TO_REG(addr)] = data; break; case ASPEED_PCIE_RC_MSI_STATUS0: case ASPEED_PCIE_RC_MSI_STATUS1: s->regs[TO_REG(addr)] &= ~data; break; case ASPEED_PCIE_RC_INT_STATUS: /* preserve INTx status*/ s->regs[TO_REG(addr)] &= ~data | ASPEED_PCIE_RC_INT_STATUS_INTX_MASK; break; default: qemu_log_mask(LOG_GUEST_ERROR, "%s: Out-of-bounds read at offset 0x%" HWADDR_PRIx "\n", __func__, addr); break; } } static const MemoryRegionOps aspeed_pcie_rc_ops = { .read = aspeed_pcie_rc_read, .write = aspeed_pcie_rc_write, .endianness = DEVICE_LITTLE_ENDIAN, .valid = { .min_access_size = 1, .max_access_size = 4, }, }; static void aspeed_pcie_rc_set_irq(void *opaque, int irq, int level) { AspeedPCIERc *s = (AspeedPCIERc *) opaque; assert(irq < PCI_NUM_PINS); if (level) { s->regs[TO_REG(ASPEED_PCIE_RC_INT_STATUS)] |= BIT(irq); } else { s->regs[TO_REG(ASPEED_PCIE_RC_INT_STATUS)] &= ~BIT(irq); } aspeed_pcie_rc_update_irq(s); } static int aspeed_pcie_rc_map_irq(PCIDevice *pci_dev, int irq_num) { return irq_num % PCI_NUM_PINS; } static void aspeed_pcie_rc_realize(DeviceState *dev, Error **errp) { PCIHostState *pci = PCI_HOST_BRIDGE(dev); AspeedPCIERc *s = ASPEED_PCIE_RC(dev); SysBusDevice *sbd = SYS_BUS_DEVICE(dev); PCIExpressHost *pex = PCIE_HOST_BRIDGE(dev); memory_region_init_io(&s->reg_rc_mmio, OBJECT(s), &aspeed_pcie_rc_ops, s, TYPE_ASPEED_PCIE_RC ".regs", 0x40); /* PCI configuration space */ pcie_host_mmcfg_init(pex, PCIE_MMCFG_SIZE_MAX / 2); sysbus_init_mmio(sbd, &pex->mmio); /* MMIO and IO region */ memory_region_init(&s->mmio, OBJECT(s), "mmio", UINT64_MAX); memory_region_init(&s->io, OBJECT(s), "io", 0x10000); memory_region_init_io(&s->mmio_window, OBJECT(s), &unassigned_io_ops, OBJECT(s), "mmio_window", UINT64_MAX); memory_region_init_io(&s->io_window, OBJECT(s), &unassigned_io_ops, OBJECT(s), "ioport_window", 64 * 1024); memory_region_add_subregion(&s->mmio_window, 0, &s->mmio); memory_region_add_subregion(&s->io_window, 0, &s->io); sysbus_init_mmio(sbd, &s->mmio_window); sysbus_init_mmio(sbd, &s->io_window); sysbus_init_irq(sbd, &s->irq); pci->bus = pci_register_root_bus(dev, dev->id, aspeed_pcie_rc_set_irq, aspeed_pcie_rc_map_irq, s, &s->mmio, &s->io, 0, 4, TYPE_PCIE_BUS); pci->bus->flags |= PCI_BUS_EXTENDED_CONFIG_SPACE; qdev_realize(DEVICE(&s->root), BUS(pci->bus), &error_fatal); } static void aspeed_pcie_rc_reset(DeviceState *dev) { AspeedPCIERc *s = ASPEED_PCIE_RC(dev); memset(s->regs, 0, sizeof(s->regs)); } static const char *aspeed_pcie_rc_root_bus_path(PCIHostState *host_bridge, PCIBus *rootbus) { AspeedPCIERc *s = ASPEED_PCIE_RC(host_bridge); snprintf(s->name, sizeof(s->name), "0000:%02x", s->bus_nr); return s->name; } static void aspeed_pcie_rc_init(Object *obj) { AspeedPCIERc *s = ASPEED_PCIE_RC(obj); AspeedPCIERoot *root = &s->root; object_initialize_child(obj, "root", root, TYPE_ASPEED_PCIE_ROOT); qdev_prop_set_int32(DEVICE(root), "addr", PCI_DEVFN(0, 0)); qdev_prop_set_bit(DEVICE(root), "multifunction", false); } static Property aspeed_pcie_rc_props[] = { DEFINE_PROP_UINT32("bus-nr", AspeedPCIERc, bus_nr, 0), DEFINE_PROP_END_OF_LIST(), }; static void aspeed_pcie_rc_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(klass); hc->root_bus_path = aspeed_pcie_rc_root_bus_path; device_class_set_legacy_reset(dc, aspeed_pcie_rc_reset); dc->realize = aspeed_pcie_rc_realize; set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); dc->fw_name = "pci"; device_class_set_props(dc, aspeed_pcie_rc_props); msi_nonbroken = true; } static const TypeInfo aspeed_pcie_rc_info = { .name = TYPE_ASPEED_PCIE_RC, .parent = TYPE_PCIE_HOST_BRIDGE, .instance_size = sizeof(AspeedPCIERc), .instance_init = aspeed_pcie_rc_init, .class_init = aspeed_pcie_rc_class_init, }; static void aspeed_pcie_root_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); dc->desc = "Aspeed PCIe Host Bridge"; k->vendor_id = PCI_VENDOR_ID_ASPEED; k->device_id = PCI_DEVICE_ID_AST2600_RC; k->revision = 0; k->class_id = PCI_CLASS_BRIDGE_HOST; /* */ dc->user_creatable = false; } static const TypeInfo aspeed_pcie_root_info = { .name = TYPE_ASPEED_PCIE_ROOT, .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(AspeedPCIERoot), .class_init = aspeed_pcie_root_class_init, .interfaces = (InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { } }, }; /* * AHB to PCIe bridge (PCIECFG) */ #define ASPEED_PCIE_CFG_CTRL 0x00 #define ASPEED_PCIE_CFG_CTRL_ENABLE BIT(0) #define ASPEED_PCIE_CFG_CTRL_CLEAR_RX BIT(4) #define ASPEED_PCIE_CFG_EN_IRQ 0x04 #define ASPEED_PCIE_CFG_TX_CLEAR 0x08 #define ASPEED_PCIE_CFG_RX_DATA 0x0C #define ASPEED_PCIE_CFG_TX_DW0 0x10 #define ASPEED_PCIE_CFG_TX_DW1 0x14 #define ASPEED_PCIE_CFG_TX_DW2 0x18 #define ASPEED_PCIE_CFG_TX_DW3 0x1C #define ASPEED_PCIE_CFG_TX_DATA 0x20 #define ASPEED_PCIE_CFG_TX_STATUS 0x24 #define ASPEED_PCIE_CFG_TX_STATUS_IDLE BIT(31) #define ASPEED_PCIE_CFG_TX_STATUS_RC_H_RX_DONE BIT(27) #define ASPEED_PCIE_CFG_TX_STATUS_RC_L_RX_DONE BIT(26) #define ASPEED_PCIE_CFG_TX_STATUS_RC_H_TX_DONE BIT(25) #define ASPEED_PCIE_CFG_TX_STATUS_RC_L_TX_DONE BIT(24) #define ASPEED_PCIE_CFG_TX_STATUS_TRIG BIT(0) #define ASPEED_PCIE_CFG_MSI0 0x58 #define ASPEED_PCIE_CFG_MSI1 0x5C #define ASPEED_PCIE_CFG_REG60 0x60 #define ASPEED_PCIE_CFG_REG64 0x64 #define ASPEED_PCIE_CFG_REG68 0x68 #define ASPEED_PCIE_CFG_RC_MAX_MSI 64 static uint64_t aspeed_pcie_cfg_read(void *opaque, hwaddr addr, unsigned int size) { AspeedPCIECfg *s = ASPEED_PCIE_CFG(opaque); uint64_t val = 0; switch (addr) { case ASPEED_PCIE_CFG_CTRL: case ASPEED_PCIE_CFG_EN_IRQ: case ASPEED_PCIE_CFG_TX_CLEAR: case ASPEED_PCIE_CFG_RX_DATA: case ASPEED_PCIE_CFG_TX_DW0: case ASPEED_PCIE_CFG_TX_DW1: case ASPEED_PCIE_CFG_TX_DW2: case ASPEED_PCIE_CFG_TX_DW3: case ASPEED_PCIE_CFG_TX_STATUS: case ASPEED_PCIE_CFG_REG60: case ASPEED_PCIE_CFG_REG64: case ASPEED_PCIE_CFG_REG68: val = s->regs[TO_REG(addr)]; break; case ASPEED_PCIE_CFG_MSI0: case ASPEED_PCIE_CFG_MSI1: printf("%s: 0x%" HWADDR_PRIx "\n", __func__, addr); val = s->regs[TO_REG(addr)]; break; default: qemu_log_mask(LOG_GUEST_ERROR, "%s: Out-of-bounds read at offset 0x%" HWADDR_PRIx "\n", __func__, addr); break; } trace_aspeed_pcie_cfg_read(addr, val); return val; } #define TLP_FMTTYPE_CFGRD0 0x04 /* Configuration Read Type 0 */ #define TLP_FMTTYPE_CFGWR0 0x44 /* Configuration Write Type 0 */ #define TLP_FMTTYPE_CFGRD1 0x05 /* Configuration Read Type 1 */ #define TLP_FMTTYPE_CFGWR1 0x45 /* Configuration Write Type 1 */ #define PCIE_CFG_FMTTYPE_MASK(dw0) (((dw0) >> 24) & 0xff) #define PCIE_CFG_BYTE_EN(dw1) ((dw1) & 0xf) #define PCIE_MMCFG_ADDR(bus, devfn, offset) \ ((((bus) & PCIE_MMCFG_BUS_MASK) << PCIE_MMCFG_BUS_BIT) | \ (((devfn) & PCIE_MMCFG_DEVFN_MASK) << PCIE_MMCFG_DEVFN_BIT) | \ ((offset) & PCIE_MMCFG_CONFOFFSET_MASK)) /* TODO : find a better way to deduce len/addr/val from byte enable */ static void aspeed_pcie_cfg_translate_write(AspeedPCIECfg *s, uint32_t *addr, uint64_t *val, int *len) { uint8_t byte_en = PCIE_CFG_BYTE_EN(s->regs[TO_REG(ASPEED_PCIE_CFG_TX_DW1)]); *len = ctpop8(byte_en); switch (byte_en) { case 0x1: case 0x3: case 0xf: break; case 0x4: *addr += 2; *val = (*val >> (2 * 8)) & MAKE_64BIT_MASK(0, *len * 8); break; case 0x2: case 0xc: *addr += *len; *val = (*val >> (*len * 8)) & MAKE_64BIT_MASK(0, *len * 8); break; default: qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid byte enable: %d\n", __func__, byte_en); g_assert_not_reached(); } } static void aspeed_pcie_cfg_readwrite(AspeedPCIECfg *s) { bool is_write = !!(s->regs[TO_REG(ASPEED_PCIE_CFG_TX_DW0)] & (1ul << 30)); uint32_t cfg_addr = s->regs[TO_REG(ASPEED_PCIE_CFG_TX_DW2)]; uint8_t bus = (cfg_addr >> 24) & 0xff; uint8_t devfn = (cfg_addr >> 16) & 0xff; uint32_t offset = cfg_addr & 0xffc; uint8_t rc_index = !!(bus & 0x80); AspeedPCIERc *rc = &s->rcs[rc_index]; uint64_t val = ~0; int len; PCIHostState *pci = PCI_HOST_BRIDGE(rc); PCIDevice *pdev; /* HACK: rework host bridge */ if (bus == 0x80) { bus = 0; } pdev = pci_find_device(pci->bus, bus, devfn); if (!pdev) { rc->regs[TO_REG(ASPEED_PCIE_RC_RX_DATA)] = ~0; goto out; } switch (PCIE_CFG_FMTTYPE_MASK(s->regs[TO_REG(ASPEED_PCIE_CFG_TX_DW0)])) { case TLP_FMTTYPE_CFGWR0: case TLP_FMTTYPE_CFGWR1: val = s->regs[TO_REG(ASPEED_PCIE_CFG_TX_DATA)]; aspeed_pcie_cfg_translate_write(s, &offset, &val, &len); pci_host_config_write_common(pdev, offset, pci_config_size(pdev), val, len); break; case TLP_FMTTYPE_CFGRD0: case TLP_FMTTYPE_CFGRD1: val = pci_host_config_read_common(pdev, offset, pci_config_size(pdev), 4); rc->regs[TO_REG(ASPEED_PCIE_RC_RX_DATA)] = val; break; default: qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid CFG type. DW0=0x%x\n", __func__, s->regs[TO_REG(ASPEED_PCIE_CFG_TX_DW0)]); } out: rc->regs[TO_REG(ASPEED_PCIE_RC_INT_STATUS)] |= ASPEED_PCIE_RC_INT_STATUS_RX; s->regs[TO_REG(ASPEED_PCIE_CFG_TX_STATUS)] |= BIT(24 + rc_index); s->regs[TO_REG(ASPEED_PCIE_CFG_TX_STATUS)] |= ASPEED_PCIE_CFG_TX_STATUS_IDLE; trace_aspeed_pcie_cfg_rw(is_write ? "write" : "read", bus, devfn, cfg_addr, val); } static void aspeed_pcie_cfg_msi_notify(AspeedPCIECfg *s, hwaddr addr, uint64_t data) { bool rc_index = !!(addr == ASPEED_PCIE_CFG_MSI1); AspeedPCIERc *rc = &s->rcs[rc_index]; int reg; trace_aspeed_pcie_cfg_msi_notify(addr, data); /* Written data is the HW IRQ number */ if (data >= ASPEED_PCIE_CFG_RC_MAX_MSI) { qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid MSI vector %"PRIx64"\n", __func__, data); return; } /* TODO: what is ASPEED_PCIE_RC_CTRL_RX_MSI_SELECT ? */ if (!(rc->regs[TO_REG(ASPEED_PCIE_RC_CTRL)] & ASPEED_PCIE_RC_CTRL_RX_MSI)) { qemu_log_mask(LOG_GUEST_ERROR, "%s: MSI are not enabled\n", __func__); return; } reg = data < 32 ? ASPEED_PCIE_RC_MSI_STATUS0 : ASPEED_PCIE_RC_MSI_STATUS1; rc->regs[TO_REG(reg)] |= BIT(data % 32); aspeed_pcie_rc_update_irq(rc); } static void aspeed_pcie_cfg_write(void *opaque, hwaddr addr, uint64_t data, unsigned int size) { AspeedPCIECfg *s = ASPEED_PCIE_CFG(opaque); trace_aspeed_pcie_cfg_write(addr, data); /* TODO: test ASPEED_PCIE_CFG_CTRL_ENABLE */ switch (addr) { case ASPEED_PCIE_CFG_EN_IRQ: case ASPEED_PCIE_CFG_TX_DW0: case ASPEED_PCIE_CFG_TX_DW1: case ASPEED_PCIE_CFG_TX_DW2: case ASPEED_PCIE_CFG_TX_DW3: case ASPEED_PCIE_CFG_TX_DATA: case ASPEED_PCIE_CFG_REG60: case ASPEED_PCIE_CFG_REG64: case ASPEED_PCIE_CFG_REG68: s->regs[TO_REG(addr)] = data; break; case ASPEED_PCIE_CFG_MSI0: case ASPEED_PCIE_CFG_MSI1: aspeed_pcie_cfg_msi_notify(s, addr, data); break; case ASPEED_PCIE_CFG_CTRL: if (data & ASPEED_PCIE_CFG_CTRL_CLEAR_RX) { s->regs[TO_REG(ASPEED_PCIE_CFG_RX_DATA)] = ~0; } break; case ASPEED_PCIE_CFG_TX_CLEAR: if (data == 0x1) { s->regs[TO_REG(ASPEED_PCIE_CFG_TX_STATUS)] &= ~ASPEED_PCIE_CFG_TX_STATUS_IDLE; } break; case ASPEED_PCIE_CFG_TX_STATUS: if (data & ASPEED_PCIE_CFG_TX_STATUS_TRIG) { aspeed_pcie_cfg_readwrite(s); } break; default: qemu_log_mask(LOG_GUEST_ERROR, "%s: Out-of-bounds read at offset 0x%" HWADDR_PRIx "\n", __func__, addr); break; } } static const MemoryRegionOps aspeed_pcie_cfg_ops = { .read = aspeed_pcie_cfg_read, .write = aspeed_pcie_cfg_write, .endianness = DEVICE_LITTLE_ENDIAN, .valid = { .min_access_size = 1, .max_access_size = 4, }, }; static void aspeed_pcie_cfg_reset(DeviceState *dev) { AspeedPCIECfg *s = ASPEED_PCIE_CFG(dev); memset(s->regs, 0, sizeof(s->regs)); } static void aspeed_pcie_cfg_instance_init(Object *obj) { AspeedPCIECfg *s = ASPEED_PCIE_CFG(obj); int i; for (i = 0; i < ARRAY_SIZE(s->rcs); i++) { object_initialize_child(obj, "rcs[*]", &s->rcs[i], TYPE_ASPEED_PCIE_RC); } } static void aspeed_pcie_cfg_realize(DeviceState *dev, Error **errp) { AspeedPCIECfg *s = ASPEED_PCIE_CFG(dev); SysBusDevice *sbd = SYS_BUS_DEVICE(dev); int nr_rcs = ARRAY_SIZE(s->rcs); int i; memory_region_init(&s->reg_mmio_container, OBJECT(s), TYPE_ASPEED_PCIE_CFG ".container", 0x100); sysbus_init_mmio(sbd, &s->reg_mmio_container); memory_region_init_io(&s->reg_cfg_mmio, OBJECT(s), &aspeed_pcie_cfg_ops, s, TYPE_ASPEED_PCIE_CFG ".regs", 0x80); memory_region_add_subregion(&s->reg_mmio_container, 0x0, &s->reg_cfg_mmio); for (i = 0; i < nr_rcs; i++) { object_property_set_int(OBJECT(&s->rcs[i]), "bus-nr", i * PCI_BUS_MAX / nr_rcs, &error_abort); if (!sysbus_realize(SYS_BUS_DEVICE(&s->rcs[i]), errp)) { return; } /* RC registers */ memory_region_add_subregion(&s->reg_mmio_container, 0x80 + i * 0x40, &s->rcs[i].reg_rc_mmio); } } static Property aspeed_pcie_cfg_props[] = { DEFINE_PROP_END_OF_LIST(), }; static void aspeed_pcie_cfg_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = aspeed_pcie_cfg_realize; device_class_set_legacy_reset(dc, aspeed_pcie_cfg_reset); device_class_set_props(dc, aspeed_pcie_cfg_props); } static const TypeInfo aspeed_pcie_cfg_info = { .name = TYPE_ASPEED_PCIE_CFG, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(AspeedPCIECfg), .instance_init = aspeed_pcie_cfg_instance_init, .class_init = aspeed_pcie_cfg_class_init, }; /* * PHY */ #define ASPEED_PCIE_PHY_DEVID 0x00 #define ASPEED_PCIE_PHY_CLASS_CODE 0x04 #define ASPEED_PCIE_PHY_CLASS_CODE_A3 0x6 #define ASPEED_PCIE_PHY_DATALINK 0x10 #define ASPEED_PCIE_PHY_HOTPLUG 0x14 #define ASPEED_PCIE_PHY_CTRL1 0x30 #define ASPEED_PCIE_PHY_CTRL1_ROOTPORT (BIT(5) | BIT(4)) #define ASPEED_PCIE_PHY_CTRL1_ENABLE BIT(1) #define ASPEED_PCIE_PHY_PROTECT 0x7C #define ASPEED_PCIE_PHY_LINK 0xC0 #define ASPEED_PCIE_PHY_LINK_STATUS BIT(5) #define ASPEED_PCIE_PHY_BDF 0xC4 #define ASPEED_PCIE_PHY_LINK_STS 0xD0 static uint64_t aspeed_pcie_phy_read(void *opaque, hwaddr addr, unsigned int size) { AspeedPCIEPhy *s = ASPEED_PCIE_PHY(opaque); uint64_t val = 0; switch (addr) { case ASPEED_PCIE_PHY_DEVID: case ASPEED_PCIE_PHY_CLASS_CODE: case ASPEED_PCIE_PHY_DATALINK: case ASPEED_PCIE_PHY_CTRL1: case ASPEED_PCIE_PHY_PROTECT: case ASPEED_PCIE_PHY_LINK: case ASPEED_PCIE_PHY_BDF: case ASPEED_PCIE_PHY_LINK_STS: val = s->regs[TO_REG(addr)]; break; default: qemu_log_mask(LOG_GUEST_ERROR, "%s: Out-of-bounds read at offset 0x%" HWADDR_PRIx "\n", __func__, addr); break; } trace_aspeed_pcie_phy_read(addr, val); return val; } static void aspeed_pcie_phy_write(void *opaque, hwaddr addr, uint64_t data, unsigned int size) { AspeedPCIEPhy *s = ASPEED_PCIE_PHY(opaque); trace_aspeed_pcie_phy_write(addr, data); /* TODO: test protect */ switch (addr) { case ASPEED_PCIE_PHY_PROTECT: data &= 0xff; s->regs[TO_REG(addr)] = !!(data == 0xA8); break; case ASPEED_PCIE_PHY_DEVID: case ASPEED_PCIE_PHY_CLASS_CODE: case ASPEED_PCIE_PHY_DATALINK: case ASPEED_PCIE_PHY_CTRL1: /* 0x30 == root port, else bridge */ s->regs[TO_REG(addr)] = data; break; default: qemu_log_mask(LOG_GUEST_ERROR, "%s: Out-of-bounds read at offset 0x%" HWADDR_PRIx "\n", __func__, addr); break; } } static const MemoryRegionOps aspeed_pcie_phy_ops = { .read = aspeed_pcie_phy_read, .write = aspeed_pcie_phy_write, .endianness = DEVICE_LITTLE_ENDIAN, .valid = { .min_access_size = 1, .max_access_size = 4, }, }; static void aspeed_pcie_phy_reset(DeviceState *dev) { AspeedPCIEPhy *s = ASPEED_PCIE_PHY(dev); memset(s->regs, 0, sizeof(s->regs)); s->regs[TO_REG(ASPEED_PCIE_PHY_DEVID)] = (PCI_DEVICE_ID_AST2600_RC << 16) | PCI_VENDOR_ID_ASPEED; s->regs[TO_REG(ASPEED_PCIE_PHY_CLASS_CODE)] = 0x06040000 | ASPEED_PCIE_PHY_CLASS_CODE_A3; s->regs[TO_REG(ASPEED_PCIE_PHY_DATALINK)] = 0xD7040022; s->regs[TO_REG(ASPEED_PCIE_PHY_LINK)] = ASPEED_PCIE_PHY_LINK_STATUS; s->regs[TO_REG(ASPEED_PCIE_PHY_BDF)] = 0x0; /* TODO: BUS+DEV */ } static void aspeed_pcie_phy_realize(DeviceState *dev, Error **errp) { AspeedPCIEPhy *s = ASPEED_PCIE_PHY(dev); SysBusDevice *sbd = SYS_BUS_DEVICE(dev); memory_region_init_io(&s->mmio, OBJECT(s), &aspeed_pcie_phy_ops, s, TYPE_ASPEED_PCIE_PHY, 0x200); sysbus_init_mmio(sbd, &s->mmio); } static Property aspeed_pcie_phy_props[] = { DEFINE_PROP_END_OF_LIST(), }; static void aspeed_pcie_phy_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = aspeed_pcie_phy_realize; device_class_set_legacy_reset(dc, aspeed_pcie_phy_reset); device_class_set_props(dc, aspeed_pcie_phy_props); } static const TypeInfo aspeed_pcie_phy_info = { .name = TYPE_ASPEED_PCIE_PHY, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(AspeedPCIEPhy), .class_init = aspeed_pcie_phy_class_init, }; static void aspeed_pcie_register(void) { type_register_static(&aspeed_pcie_root_info); type_register_static(&aspeed_pcie_rc_info); type_register_static(&aspeed_pcie_phy_info); type_register_static(&aspeed_pcie_cfg_info); } type_init(aspeed_pcie_register)