blob: 778b92b6b0fe65cd5deef96ce0167db53966799d [file] [log] [blame]
// Copyright 2016 The Fuchsia Authors
// Copyright (c) 2012-2015 Travis Geiselbrecht
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#if WITH_DEV_PCIE
#include <dev/pcie_bus_driver.h>
#include <dev/pcie_platform.h>
#include <dev/pcie_root.h>
#include <dev/interrupt/arm_gicv2m_msi.h>
#include <inttypes.h>
#include <lk/init.h>
#include <fbl/alloc_checker.h>
#include <fbl/ref_ptr.h>
#include <dev/qemu-virt.h>
#include <trace.h>
#include <mdi/mdi.h>
#include <mdi/mdi-defs.h>
#include <pdev/driver.h>
#include <pdev/interrupt.h>
class QemuPciePlatformSupport : public PciePlatformInterface {
public:
QemuPciePlatformSupport(bool has_msi_gic)
: PciePlatformInterface(has_msi_gic ? MsiSupportLevel::MSI_WITH_MASKING
: MsiSupportLevel::NONE) { }
status_t AllocMsiBlock(uint requested_irqs,
bool can_target_64bit,
bool is_msix,
pcie_msi_block_t* out_block) override {
return arm_gicv2m_alloc_msi_block(requested_irqs, can_target_64bit, is_msix, out_block);
}
void FreeMsiBlock(pcie_msi_block_t* block) override {
arm_gicv2m_free_msi_block(block);
}
void RegisterMsiHandler(const pcie_msi_block_t* block,
uint msi_id,
int_handler handler,
void* ctx) override {
arm_gicv2m_register_msi_handler(block, msi_id, handler, ctx);
}
void MaskUnmaskMsi(const pcie_msi_block_t* block,
uint msi_id,
bool mask) override {
arm_gicv2m_mask_unmask_msi(block, msi_id, mask);
}
};
class QemuPcieRoot : public PcieRoot {
public:
static fbl::RefPtr<PcieRoot> Create(PcieBusDriver& bus_drv, uint managed_bus_id) {
fbl::AllocChecker ac;
auto root = fbl::AdoptRef(new (&ac) QemuPcieRoot(bus_drv, managed_bus_id));
if (!ac.check()) {
TRACEF("Out of memory attemping to create PCIe root to manage bus ID 0x%02x\n",
managed_bus_id);
return nullptr;
}
return root;
}
status_t Swizzle(uint dev_id, uint func_id, uint pin, uint *irq) override {
if ((irq == nullptr) ||
(dev_id >= PCIE_MAX_DEVICES_PER_BUS) ||
(func_id >= PCIE_MAX_FUNCTIONS_PER_DEVICE) ||
(pin >= PCIE_MAX_LEGACY_IRQ_PINS))
return ZX_ERR_INVALID_ARGS;
// TODO(johngro) : Figure out what to do here if QEMU ever starts to
// create root complexes which manage a bus other than 0.
if (managed_bus_id() != 0)
return ZX_ERR_NOT_FOUND;
*irq = PCIE_INT_BASE + ((pin + dev_id) % PCIE_MAX_LEGACY_IRQ_PINS);
return ZX_OK;
}
private:
QemuPcieRoot(PcieBusDriver& bus_drv, uint managed_bus_id)
: PcieRoot(bus_drv, managed_bus_id) { }
};
static void arm_qemu_pcie_init(mdi_node_ref_t* node, uint level) {
/* Initialize the MSI allocator */
status_t res = arm_gicv2m_msi_init();
if (res != ZX_OK)
TRACEF("Failed to initialize MSI allocator (res = %d). PCI will be "
"restricted to legacy IRQ mode.\n", res);
/* Initialize the PCI platform suppored based on whether or not we support MSI */
static QemuPciePlatformSupport platform_pcie_support(res == ZX_OK);
res = PcieBusDriver::InitializeDriver(platform_pcie_support);
if (res == ZX_OK) {
/* Add the QEMU hardcoded ECAM and bus ranges to the bus driver, if we can. */
auto pcie = PcieBusDriver::GetDriver();
DEBUG_ASSERT(pcie != nullptr);
const PcieBusDriver::EcamRegion ecam {
.phys_base = PCIE_ECAM_BASE_PHYS,
.size = PCIE_ECAM_SIZE,
.bus_start = 0x00,
.bus_end = static_cast<uint8_t>(PCIE_ECAM_SIZE / PCIE_ECAM_BYTE_PER_BUS) - 1,
};
res = pcie->AddEcamRegion(ecam);
if (res != ZX_OK) {
TRACEF("Failed to add ECAM region to PCIe bus driver!\n");
return;
}
constexpr uint64_t MMIO_BASE = PCIE_MMIO_BASE_PHYS;
constexpr uint64_t MMIO_SIZE = PCIE_MMIO_SIZE;
res = pcie->AddBusRegion(MMIO_BASE, MMIO_SIZE, PciAddrSpace::MMIO);
if (res != ZX_OK) {
TRACEF("WARNING - Failed to add initial PCIe MMIO region "
"[%" PRIx64 ", %" PRIx64") to bus driver! (res %d)\n",
MMIO_BASE, MMIO_BASE + MMIO_SIZE, res);
return;
}
constexpr uint64_t PIO_BASE = PCIE_PIO_BASE_PHYS;
constexpr uint64_t PIO_SIZE = PCIE_PIO_SIZE;
res = pcie->AddBusRegion(PIO_BASE, PIO_SIZE, PciAddrSpace::PIO);
if (res != ZX_OK) {
TRACEF("WARNING - Failed to add initial PCIe PIO region "
"[%" PRIx64 ", %" PRIx64") to bus driver! (res %d)\n",
PIO_BASE, PIO_BASE + PIO_SIZE, res);
return;
}
// Create and add the root complex for bus ID #0.
auto root = QemuPcieRoot::Create(*pcie, 0);
if (root == nullptr)
return;
res = pcie->AddRoot(fbl::move(root));
if (res != ZX_OK) {
TRACEF("Failed to add PCIe root complex for bus 0! (res %d)\n", res);
return;
}
res = pcie->StartBusDriver();
if (res != ZX_OK) {
TRACEF("Failed to start PCIe bus driver! (res %d)\n", res);
return;
}
} else {
TRACEF("Failed to initialize PCI bus driver (res %d). "
"PCI will be non-functional.\n", res);
}
}
LK_PDEV_INIT(arm_qemu_pcie_init, MDI_QEMU_PCIE, arm_qemu_pcie_init, LK_INIT_LEVEL_PLATFORM);
#endif // if WITH_DEV_PCIE