1// Copyright 2016 The Fuchsia Authors
2// Copyright (c) 2012-2015 Travis Geiselbrecht
3//
4// Use of this source code is governed by a MIT-style
5// license that can be found in the LICENSE file or at
6// https://opensource.org/licenses/MIT
7
8#if WITH_KERNEL_PCIE
9#include <dev/interrupt/arm_gicv2m_msi.h>
10#include <dev/pcie_bus_driver.h>
11#include <dev/pcie_platform.h>
12#include <dev/pcie_root.h>
13#include <fbl/alloc_checker.h>
14#include <fbl/ref_ptr.h>
15#include <inttypes.h>
16#include <lk/init.h>
17#include <pdev/driver.h>
18#include <pdev/interrupt.h>
19#include <trace.h>
20#include <zircon/boot/driver-config.h>
21#include <zircon/types.h>
22
23class ArmGicV2PciePlatformSupport : public PciePlatformInterface {
24public:
25    ArmGicV2PciePlatformSupport(bool has_msi_gic)
26        : PciePlatformInterface(has_msi_gic ? MsiSupportLevel::MSI_WITH_MASKING
27                                            : MsiSupportLevel::NONE) {}
28
29    zx_status_t AllocMsiBlock(uint requested_irqs,
30                              bool can_target_64bit,
31                              bool is_msix,
32                              msi_block_t* out_block) override {
33        return arm_gicv2m_msi_alloc_block(requested_irqs, can_target_64bit, is_msix, out_block);
34    }
35
36    void FreeMsiBlock(msi_block_t* block) override {
37        arm_gicv2m_msi_free_block(block);
38    }
39
40    void RegisterMsiHandler(const msi_block_t* block,
41                            uint msi_id,
42                            int_handler handler,
43                            void* ctx) override {
44        arm_gicv2m_msi_register_handler(block, msi_id, handler, ctx);
45    }
46
47    void MaskUnmaskMsi(const msi_block_t* block,
48                       uint msi_id,
49                       bool mask) override {
50        arm_gicv2m_msi_mask_unmask(block, msi_id, mask);
51    }
52};
53
54static void arm_gicv2_pcie_init(const void* driver_data, uint32_t length) {
55    ASSERT(length >= sizeof(dcfg_arm_gicv2_driver_t));
56    const dcfg_arm_gicv2_driver_t* driver =
57        reinterpret_cast<const dcfg_arm_gicv2_driver_t*>(driver_data);
58
59    // based on whether or not ZBI says we support MSI, initialize the v2m allocator
60    zx_status_t res;
61    bool use_msi;
62    if (driver->use_msi) {
63        dprintf(SPEW, "GICv2 MSI init\n");
64
65        // Initialize the MSI allocator
66        res = arm_gicv2m_msi_init();
67        if (res != ZX_OK) {
68            TRACEF("Failed to initialize MSI allocator (res = %d).  PCI will be "
69                   "restricted to legacy IRQ mode.\n",
70                   res);
71        }
72        use_msi = (res == ZX_OK);
73    } else {
74        use_msi = false;
75    }
76
77    // Initialize the PCI platform supported based on whether or not we support MSI
78    static ArmGicV2PciePlatformSupport platform_pcie_support(use_msi);
79
80    res = PcieBusDriver::InitializeDriver(platform_pcie_support);
81    if (res != ZX_OK) {
82        TRACEF("Failed to initialize PCI bus driver (res %d).  "
83               "PCI will be non-functional.\n",
84               res);
85    }
86}
87
88LK_PDEV_INIT(arm_gicv2_pcie_init, KDRV_ARM_GIC_V2, arm_gicv2_pcie_init, LK_INIT_LEVEL_PLATFORM);
89
90#endif // if WITH_KERNEL_PCIE
91