1 // Copyright 2016 The Fuchsia Authors
2 //
3 // Use of this source code is governed by a MIT-style
4 // license that can be found in the LICENSE file or at
5 // https://opensource.org/licenses/MIT
6 
7 #if WITH_KERNEL_PCIE
8 
9 #include <object/pci_interrupt_dispatcher.h>
10 
11 #include <kernel/auto_lock.h>
12 #include <zircon/rights.h>
13 #include <fbl/alloc_checker.h>
14 #include <object/pci_device_dispatcher.h>
15 #include <platform.h>
16 
~PciInterruptDispatcher()17 PciInterruptDispatcher::~PciInterruptDispatcher() {
18     // Release our reference to our device.
19     device_ = nullptr;
20 }
21 
IrqThunk(const PcieDevice & dev,uint irq_id,void * ctx)22 pcie_irq_handler_retval_t PciInterruptDispatcher::IrqThunk(const PcieDevice& dev,
23                                                            uint irq_id, void* ctx) {
24     DEBUG_ASSERT(ctx);
25     auto thiz = reinterpret_cast<PciInterruptDispatcher*>(ctx);
26     thiz->InterruptHandler();
27     return PCIE_IRQRET_MASK;
28 }
29 
Create(const fbl::RefPtr<PcieDevice> & device,uint32_t irq_id,bool maskable,zx_rights_t * out_rights,fbl::RefPtr<Dispatcher> * out_interrupt)30 zx_status_t PciInterruptDispatcher::Create(
31         const fbl::RefPtr<PcieDevice>& device,
32         uint32_t irq_id,
33         bool maskable,
34         zx_rights_t* out_rights,
35         fbl::RefPtr<Dispatcher>* out_interrupt) {
36     // Sanity check our args
37     if (!device || !out_rights || !out_interrupt) {
38         return ZX_ERR_INVALID_ARGS;
39     }
40     if (!is_valid_interrupt(irq_id, 0)) {
41         return ZX_ERR_INTERNAL;
42     }
43 
44     fbl::AllocChecker ac;
45     // Attempt to allocate a new dispatcher wrapper.
46     auto interrupt_dispatcher = new (&ac) PciInterruptDispatcher(device, irq_id, maskable);
47     fbl::RefPtr<Dispatcher> dispatcher = fbl::AdoptRef<Dispatcher>(interrupt_dispatcher);
48     if (!ac.check())
49         return ZX_ERR_NO_MEMORY;
50 
51     Guard<fbl::Mutex> guard{interrupt_dispatcher->get_lock()};
52 
53     interrupt_dispatcher->set_flags(INTERRUPT_UNMASK_PREWAIT);
54 
55     // Register the interrupt
56     zx_status_t status = interrupt_dispatcher->RegisterInterruptHandler();
57     if (status != ZX_OK)
58         return status;
59 
60     // Everything seems to have gone well.  Make sure the interrupt is unmasked
61     // (if it is maskable) then transfer our dispatcher refererence to the
62     // caller.
63     if (maskable) {
64         device->UnmaskIrq(irq_id);
65     }
66     *out_interrupt = ktl::move(dispatcher);
67     *out_rights    = ZX_DEFAULT_PCI_INTERRUPT_RIGHTS;
68     return ZX_OK;
69 }
70 
MaskInterrupt()71 void PciInterruptDispatcher::MaskInterrupt() {
72     if (maskable_)
73         device_->MaskIrq(vector_);
74 }
75 
UnmaskInterrupt()76 void PciInterruptDispatcher::UnmaskInterrupt() {
77     if (maskable_)
78         device_->UnmaskIrq(vector_);
79 }
80 
RegisterInterruptHandler()81 zx_status_t PciInterruptDispatcher::RegisterInterruptHandler() {
82     return device_->RegisterIrqHandler(vector_, IrqThunk, this);
83 }
84 
UnregisterInterruptHandler()85 void PciInterruptDispatcher::UnregisterInterruptHandler() {
86     device_->RegisterIrqHandler(vector_, nullptr, nullptr);
87 }
88 
89 #endif  // if WITH_KERNEL_PCIE
90