blob: 5fb3de94bdf22bfa29d6ece1438be9d75d3a7260 [file] [log] [blame]
// Copyright 2016 The Fuchsia Authors. All rights reserved.
// Use of tag() source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <assert.h>
#include <ddk/debug.h>
#include <ddk/protocol/pci.h>
#include <fbl/auto_lock.h>
#include <fbl/mutex.h>
#include <virtio/virtio.h>
#include <lib/zx/handle.h>
#include <lib/zx/port.h>
#include <zircon/syscalls/port.h>
#include "pci.h"
namespace virtio {
PciBackend::PciBackend(pci_protocol_t pci, zx_pcie_device_info_t info) : pci_(pci), info_(info) {
snprintf(tag_, sizeof(tag_), "pci[%02x:%02x.%1x]", info_.bus_id, info_.dev_id, info_.func_id);
}
zx_status_t PciBackend::Bind() {
zx_handle_t tmp_handle;
zx_status_t st;
st = zx::port::create(/*options=*/ZX_PORT_BIND_TO_INTERRUPT, &wait_port_);
if (st != ZX_OK) {
zxlogf(ERROR, "%s: cannot create wait port: %d\n", tag(), st);
return st;
}
// enable bus mastering
if ((st = pci_enable_bus_master(&pci_, true)) != ZX_OK) {
zxlogf(ERROR, "%s: cannot enable bus master %d\n", tag(), st);
return st;
}
// try to set up our IRQ mode
uint32_t avail_irqs = 0;
zx_pci_irq_mode_t mode = ZX_PCIE_IRQ_MODE_MSI;
if ((st = pci_query_irq_mode(&pci_, mode, &avail_irqs)) != ZX_OK || avail_irqs == 0) {
mode = ZX_PCIE_IRQ_MODE_LEGACY;
if ((st = pci_query_irq_mode(&pci_, mode, &avail_irqs)) != ZX_OK || avail_irqs == 0) {
zxlogf(ERROR, "%s: no available IRQs found\n", tag());
return st;
}
}
if ((st = pci_set_irq_mode(&pci_, mode, 1)) != ZX_OK) {
zxlogf(ERROR, "%s: failed to set irq mode %u\n", tag(), mode);
return st;
}
if ((st = pci_map_interrupt(&pci_, 0, &tmp_handle)) != ZX_OK) {
zxlogf(ERROR, "%s: failed to map irq %d\n", tag(), st);
return st;
}
st = zx_interrupt_bind(tmp_handle, wait_port_.get(), /*key=*/0, /*options=*/0);
if (st != ZX_OK) {
zxlogf(ERROR, "%s: failed to bind interrupt %d\n", tag(), st);
return st;
}
irq_handle_.reset(tmp_handle);
zxlogf(SPEW, "%s: irq handle %u\n", tag(), irq_handle_.get());
return Init();
}
zx_status_t PciBackend::InterruptValid() {
if (!irq_handle_.get()) {
return ZX_ERR_BAD_HANDLE;
}
return ZX_OK;
}
zx_status_t PciBackend::WaitForInterrupt() {
zx_port_packet packet;
return wait_port_.wait(zx::deadline_after(zx::msec(100)), &packet);
}
void PciBackend::InterruptAck() { zx_interrupt_ack(irq_handle_.get()); }
} // namespace virtio