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
15namespace virtio {
16
17PciBackend::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
22zx_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
59zx_status_t PciBackend::InterruptValid() {
60    if (!irq_handle_.get()) {
61        return ZX_ERR_BAD_HANDLE;
62    }
63    return ZX_OK;
64}
65
66zx_status_t PciBackend::WaitForInterrupt() {
67    return zx_interrupt_wait(irq_handle_.get(), nullptr);
68}
69
70} // namespace virtio
71