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 <assert.h>
12 #include <dev/interrupt.h>
13 #include <dev/pcie_platform.h>
14 #include <err.h>
15 #include <kernel/spinlock.h>
16 #include <fbl/intrusive_single_list.h>
17 #include <fbl/macros.h>
18 #include <fbl/ref_counted.h>
19 #include <fbl/ref_ptr.h>
20 #include <region-alloc/region-alloc.h>
21 #include <sys/types.h>
22 
23 /* Fwd decls */
24 class PcieDevice;
25 
26 /**
27  * Enumeration which defines the IRQ modes a PCIe device may be operating in.
28  * IRQ modes are exclusive, a device may be operating in only one mode at any
29  * given point in time.  Drivers may query the maximum number of IRQs supported
30  * by each mode using the pcie_query_irq_mode_capabilities function.  Drivers
31  * may request a particular number of IRQs be allocated when selecting an IRQ
32  * mode with pcie_set_irq_mode.  IRQ identifiers used in the system when
33  * registering, un-registering and dispatching IRQs are on the range [0, N-1]
34  * where N are the number of IRQs successfully allocated using a call to
35  * pcie_set_irq_mode.
36  *
37  * ++ PCIE_IRQ_MODE_DISABLED
38  *    All IRQs are disabled.  0 total IRQs are supported in this mode.
39  *
40  * ++ PCIE_IRQ_MODE_LEGACY
41  *    Devices may support up to 1 legacy IRQ in total.  Exclusive IRQ access
42  *    cannot be guaranteed (the IRQ may be shared with other devices)
43  *
44  * ++ PCIE_IRQ_MODE_MSI
45  *    Devices may support up to 32 MSI IRQs in total.  IRQs may be allocated
46  *    exclusively, resources permitting.
47  *
48  * ++ PCIE_IRQ_MODE_MSI_X
49  *    Devices may support up to 2048 MSI-X IRQs in total.  IRQs may be allocated
50  *    exclusively, resources permitting.
51  */
52 typedef enum pcie_irq_mode {
53     PCIE_IRQ_MODE_DISABLED = 0,
54     PCIE_IRQ_MODE_LEGACY   = 1,
55     PCIE_IRQ_MODE_MSI      = 2,
56     PCIE_IRQ_MODE_MSI_X    = 3,
57     PCIE_IRQ_MODE_COUNT    = 4,
58 } pcie_irq_mode_t;
59 
60 /**
61  * A structure used to hold output parameters when calling
62  * pcie_query_irq_mode_capabilities
63  */
64 typedef struct pcie_irq_mode_caps {
65     uint max_irqs;  /** The maximum number of IRQ supported by the selected mode */
66     /**
67      * For MSI or MSI-X, indicates whether or not per-vector-masking has been
68      * implemented by the hardware.
69      */
70     bool per_vector_masking_supported;
71 } pcie_irq_mode_caps_t;
72 
73 /**
74  * An enumeration of the permitted return values from a PCIe IRQ handler.
75  *
76  * ++ PCIE_IRQRET_NO_ACTION
77  *    Do not mask the IRQ.
78  *
79  * ++ PCIE_IRQRET_MASK
80  *    Mask the IRQ if (and only if) per vector masking is supported.
81  */
82 typedef enum pcie_irq_handler_retval {
83     PCIE_IRQRET_NO_ACTION        = 0x0,
84     PCIE_IRQRET_MASK             = 0x1,
85 } pcie_irq_handler_retval_t;
86 
87 /**
88  * A structure used to hold the details about the currently configured IRQ mode
89  * of a device.  Used in conjunction with pcie_get_irq_mode.
90  */
91 typedef struct pcie_irq_mode_info {
92    pcie_irq_mode_t          mode;                 /// The currently configured mode.
93    uint                     max_handlers;         /// The max number of handlers for the mode.
94    uint                     registered_handlers;  /// The current number of registered handlers.
95 } pcie_irq_mode_info_t;
96 
97 /**
98  * Definition of the callback registered with pcie_register_irq_handler.  This
99  * callback will be called by a bus central IRQ dispatcher any time a chosen
100  * device IRQ occurs.
101  *
102  * @note Masked/unmasked status of an IRQ MUST not be manipulated via the API
103  * during an IRQ handler dispatch.  If an IRQ needs to be masked as part of a
104  * handler's behavior, the appropriate return value should be used instead of in
105  * the API.  @see pcie_irq_handler_retval_t
106  *
107  * @param dev A pointer to the pci device for which this IRQ occurred.
108  * @param irq_id The 0-indexed ID of the IRQ which occurred.
109  * @param ctx The context pointer registered when registering the handler.
110  */
111 typedef pcie_irq_handler_retval_t (*pcie_irq_handler_fn_t)(
112         const PcieDevice& dev,
113         uint irq_id,
114         void* ctx);
115 
116 /**
117  * Structure used internally to hold the state of a registered handler.
118  */
119 struct pcie_irq_handler_state_t {
120     SpinLock              lock;
121     pcie_irq_handler_fn_t handler = nullptr;
122     void*                 ctx = nullptr;
123     PcieDevice*           dev = nullptr;
124     uint                  pci_irq_id;
125     bool                  masked;
126 };
127 
128 /**
129  * Class for managing shared legacy IRQ handlers.
130  * TODO(johngro): Make this an inner class of PcieDevice
131  */
132 class SharedLegacyIrqHandler
133     : public fbl::SinglyLinkedListable<fbl::RefPtr<SharedLegacyIrqHandler>>,
134       public fbl::RefCounted<SharedLegacyIrqHandler> {
135 public:
136     static fbl::RefPtr<SharedLegacyIrqHandler> Create(uint irq_id);
137     ~SharedLegacyIrqHandler();
138 
139     void AddDevice(PcieDevice& dev);
140     void RemoveDevice(PcieDevice& dev);
141 
irq_id()142     uint irq_id() const { return irq_id_; }
143 
144     // Disallow copying, assigning and moving.
145     DISALLOW_COPY_ASSIGN_AND_MOVE(SharedLegacyIrqHandler);
146 
147 private:
148     explicit SharedLegacyIrqHandler(uint irq_id);
149 
HandlerThunk(void * arg)150     static interrupt_eoi HandlerThunk(void *arg) {
151         DEBUG_ASSERT(arg);
152         reinterpret_cast<SharedLegacyIrqHandler*>(arg)->Handler();
153         return IRQ_EOI_DEACTIVATE;
154     }
155 
156     void Handler();
157 
158     struct list_node  device_handler_list_;
159     SpinLock          device_handler_list_lock_;
160     const uint        irq_id_;
161 };
162 
163