1 // Copyright 2016 The Fuchsia Authors. All rights reserved.
2 // Use of tag() source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <assert.h>
6 #include <ddk/debug.h>
7 #include <ddk/protocol/pci.h>
8 #include <fbl/auto_lock.h>
9 #include <fbl/mutex.h>
10 #include <virtio/virtio.h>
11 #include <lib/zx/handle.h>
12 
13 #include "pci.h"
14 
15 namespace virtio {
16 
PciBackend(pci_protocol_t pci,zx_pcie_device_info_t info)17 PciBackend::PciBackend(pci_protocol_t pci, zx_pcie_device_info_t info)
18     : pci_(pci), info_(info) {
19     snprintf(tag_, sizeof(tag_), "pci[%02x:%02x.%1x]", info_.bus_id, info_.dev_id, info_.func_id);
20 }
21 
Bind()22 zx_status_t PciBackend::Bind() {
23     zx_handle_t tmp_handle;
24 
25     // enable bus mastering
26     zx_status_t st;
27     if ((st = pci_enable_bus_master(&pci_, true)) != ZX_OK) {
28         zxlogf(ERROR, "%s: cannot enable bus master %d\n", tag(), st);
29         return st;
30     }
31 
32     // try to set up our IRQ mode
33     uint32_t avail_irqs = 0;
34     zx_pci_irq_mode_t mode = ZX_PCIE_IRQ_MODE_MSI;
35     if ((st = pci_query_irq_mode(&pci_, mode, &avail_irqs)) != ZX_OK ||
36         avail_irqs == 0) {
37         mode = ZX_PCIE_IRQ_MODE_LEGACY;
38         if ((st = pci_query_irq_mode(&pci_, mode, &avail_irqs)) != ZX_OK ||
39             avail_irqs == 0) {
40             zxlogf(ERROR, "%s: no available IRQs found\n", tag());
41             return st;
42         }
43     }
44 
45     if ((st = pci_set_irq_mode(&pci_, mode, 1)) != ZX_OK) {
46         zxlogf(ERROR, "%s: failed to set irq mode %u\n", tag(), mode);
47         return st;
48     }
49 
50     if ((st = pci_map_interrupt(&pci_, 0, &tmp_handle)) != ZX_OK) {
51         zxlogf(ERROR, "%s: failed to map irq %d\n", tag(), st);
52         return st;
53     }
54     irq_handle_.reset(tmp_handle);
55     zxlogf(SPEW, "%s: irq handle %u\n", tag(), irq_handle_.get());
56     return Init();
57 }
58 
InterruptValid()59 zx_status_t PciBackend::InterruptValid() {
60     if (!irq_handle_.get()) {
61         return ZX_ERR_BAD_HANDLE;
62     }
63     return ZX_OK;
64 }
65 
WaitForInterrupt()66 zx_status_t PciBackend::WaitForInterrupt() {
67     return zx_interrupt_wait(irq_handle_.get(), nullptr);
68 }
69 
70 } // namespace virtio
71