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