1 // Copyright 2016 The Fuchsia Authors
2 // Copyright (c) 2016, Google, Inc. All rights reserved
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 
9 #pragma once
10 
11 #include <dev/interrupt.h>
12 #include <dev/pci_common.h>
13 #include <zircon/compiler.h>
14 #include <zircon/errors.h>
15 #include <sys/types.h>
16 
17 #ifdef __cplusplus
18 
19 #include <fbl/ref_counted.h>
20 
21 // PciePlatformInterface
22 //
23 // The definitions of an interface responsible for managing runtime platform
24 // resource allocation.  In particular, blocks of MSI interrupts.  Platforms
25 // must provide an implementation of this interface to the PcieBusDriver when it
26 // gets instantiated.
27 //
28 // TODO(johngro): If/when the kernel interface to interrupt management becomes
29 // more standardized (and includes the concept of MSI IRQ blocks), this
30 // interface can be eliminated and the PCI bus driver can interact with the
31 // omnipresent interrupt management interface instead of an implementation of
32 // this interface.
33 //
34 class PciePlatformInterface {
35 public:
~PciePlatformInterface()36     virtual ~PciePlatformInterface() { }
37 
38     /**
39      * Methods used to determine if a platform supports MSI or not, and if so,
40      * whether or not the platform can mask individual MSI vectors at the
41      * platform level.
42      *
43      * If the platform supports MSI, it must supply valid implementations of
44      * Alloc/FreeMsiBlock, and RegisterMsiHandler.
45      *
46      * If the platform supports MSI masking, it must supply a valid
47      * implementation of MaskUnmaskMsi.
48      */
supports_msi()49     bool supports_msi() const { return supports_msi_; }
supports_msi_masking()50     bool supports_msi_masking() const { return supports_msi_masking_; }
51 
52     /**
53      * Method used for platform allocation of blocks of MSI and MSI-X compatible
54      * IRQ targets.
55      *
56      * @param requested_irqs The total number of irqs being requested.
57      * @param can_target_64bit True if the target address of the MSI block can
58      *        be located past the 4GB boundary.  False if the target address must be
59      *        in low memory.
60      * @param is_msix True if this request is for an MSI-X compatible block.  False
61      *        for plain old MSI.
62      * @param out_block A pointer to the allocation bookkeeping to be filled out
63      *        upon successful allocation of the requested block of IRQs.
64      *
65      * @return A status code indicating the success or failure of the operation.
66      */
AllocMsiBlock(uint requested_irqs,bool can_target_64bit,bool is_msix,msi_block_t * out_block)67     virtual zx_status_t AllocMsiBlock(uint requested_irqs,
68                                       bool can_target_64bit,
69                                       bool is_msix,
70                                       msi_block_t* out_block) {
71         // Bus driver code should not be calling this if the platform does not
72         // indicate support for MSI.
73         DEBUG_ASSERT(false);
74         return ZX_ERR_NOT_SUPPORTED;
75     }
76 
77     /**
78      * Method used by the bus driver to return a block of MSI IRQs previously
79      * allocated with a call to a AllocMsiBlock implementation to the platform
80      * pool.
81      *
82      * @param block A pointer to the block to be returned.
83      */
FreeMsiBlock(msi_block_t * block)84     virtual void FreeMsiBlock(msi_block_t* block) {
85         // Bus driver code should not be calling this if the platform does not
86         // indicate support for MSI.
87         DEBUG_ASSERT(false);
88     }
89 
90     /**
91      * Method used for registration of MSI handlers with the platform.
92      *
93      * @param block A pointer to a block of MSIs allocated using a platform supplied
94      *        platform_msi_alloc_block_t callback.
95      * @param msi_id The ID (indexed from 0) with the block of MSIs to register a
96      *        handler for.
97      * @param handler A pointer to the handler to register, or NULL to unregister.
98      * @param ctx A context pointer to be supplied when the handler is invoked.
99      */
RegisterMsiHandler(const msi_block_t * block,uint msi_id,int_handler handler,void * ctx)100     virtual void RegisterMsiHandler(const msi_block_t* block,
101                                     uint                    msi_id,
102                                     int_handler             handler,
103                                     void*                   ctx) {
104         // Bus driver code should not be calling this if the platform does not
105         // indicate support for MSI.
106         DEBUG_ASSERT(false);
107     }
108 
109     /**
110      * Method used for masking/unmasking of MSI handlers at the platform level.
111      *
112      * @param block A pointer to a block of MSIs allocated using a platform supplied
113      *        platform_msi_alloc_block_t callback.
114      * @param msi_id The ID (indexed from 0) with the block of MSIs to mask or
115      *        unmask.
116      * @param mask If true, mask the handler.  Otherwise, unmask it.
117      */
MaskUnmaskMsi(const msi_block_t * block,uint msi_id,bool mask)118     virtual void MaskUnmaskMsi(const msi_block_t* block,
119                                uint                    msi_id,
120                                bool                    mask) {
121         // Bus driver code should not be calling this if the platform does not
122         // indicate support for MSI masking.
123         DEBUG_ASSERT(false);
124     }
125 
126     DISALLOW_COPY_ASSIGN_AND_MOVE(PciePlatformInterface);
127 protected:
128     enum class MsiSupportLevel { NONE, MSI, MSI_WITH_MASKING };
PciePlatformInterface(MsiSupportLevel msi_support)129     explicit PciePlatformInterface(MsiSupportLevel msi_support)
130         : supports_msi_((msi_support == MsiSupportLevel::MSI) ||
131                         (msi_support == MsiSupportLevel::MSI_WITH_MASKING)),
132           supports_msi_masking_(msi_support == MsiSupportLevel::MSI_WITH_MASKING) { }
133 
134 private:
135     const bool supports_msi_;
136     const bool supports_msi_masking_;
137 };
138 
139 // A thin veneer version that declares no MSI
140 class NoMsiPciePlatformInterface : public PciePlatformInterface {
141 public:
NoMsiPciePlatformInterface()142     NoMsiPciePlatformInterface()
143         : PciePlatformInterface(MsiSupportLevel::NONE) {}
144 
145     DISALLOW_COPY_ASSIGN_AND_MOVE(NoMsiPciePlatformInterface);
146 };
147 
148 #endif  // __cplusplus
149