blob: 8a4446447be05bc80f69f35915dc1c23afb63a6e [file] [log] [blame]
// Copyright 2021 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/devices/usb/drivers/xhci/xhci-interrupter.h"
#include <lib/async/cpp/irq.h>
#include <lib/async/cpp/task.h>
#include "src/devices/usb/drivers/xhci/usb-xhci.h"
namespace usb_xhci {
zx_status_t Interrupter::Init(uint16_t interrupter, size_t page_size, fdf::MmioBuffer* buffer,
const RuntimeRegisterOffset& offset, uint32_t erst_max,
DoorbellOffset doorbell_offset, UsbXhci* hci, HCCPARAMS1 hcc_params_1,
uint64_t* dcbaa) {
if (active_) {
// Already active;
return ZX_OK;
}
hci_ = hci;
interrupter_ = interrupter;
// Create our inspect node and start to add our properties.
char name[64];
snprintf(name, sizeof(name), "Interrupter %hu", interrupter_);
inspect_root_ = hci_->inspect_root_node().CreateChild(name);
total_irqs_ = inspect_root_.CreateUint("Total IRQs", 0);
return event_ring_.Init(page_size, hci_->bti(), buffer, hci->Is32BitController(), erst_max,
ERSTSZ::Get(offset, interrupter_).ReadFrom(buffer),
ERDP::Get(offset, interrupter_).ReadFrom(buffer),
IMAN::Get(offset, interrupter_).FromValue(0), hci_->CapLength(),
HCSPARAMS1::Get().ReadFrom(buffer), hci_->GetCommandRing(),
doorbell_offset, hci, hcc_params_1, dcbaa, interrupter_, &inspect_root_);
}
zx_status_t Interrupter::Start(const RuntimeRegisterOffset& offset, fdf::MmioView mmio_view) {
if (active_) {
// Already active;
return ZX_OK;
}
ERDP erdp = ERDP::Get(offset, interrupter_).ReadFrom(&mmio_view);
if (!event_ring_.erdp_phys()) {
return ZX_ERR_BAD_STATE;
}
erdp.set_reg_value(event_ring_.erdp_phys());
erdp.WriteTo(&mmio_view);
ERSTBA ba = ERSTBA::Get(offset, interrupter_).ReadFrom(&mmio_view);
// This enables the interrupter
ba.set_Pointer(event_ring_.erst()).WriteTo(&mmio_view);
IMAN::Get(offset, interrupter_).FromValue(0).set_IE(1).WriteTo(&mmio_view);
thread_ = std::thread([this]() { IrqThread(); });
active_ = true;
return ZX_OK;
}
fpromise::promise<void, zx_status_t> Interrupter::Timeout(zx::time deadline) {
fpromise::bridge<void, zx_status_t> bridge;
zx_status_t status = async::PostTaskForTime(
async_loop_->dispatcher(),
[completer = std::move(bridge.completer), this]() mutable {
completer.complete_ok();
hci_->RunUntilIdle(interrupter_);
},
deadline);
if (status != ZX_OK) {
return fpromise::make_error_promise<zx_status_t>(status);
}
return bridge.consumer.promise().box();
}
zx_status_t Interrupter::IrqThread() {
// TODO(https://fxbug.dev/42105800): See https://fxbug.dev/42105800. Get rid of this. For now we need thread
// priorities so that realtime transactions use the completer which ends
// up getting realtime latency guarantees.
async_loop_config_t config = kAsyncLoopConfigNeverAttachToThread;
config.irq_support = true;
async_loop_.emplace(&config);
async_executor_.emplace(async_loop_->dispatcher());
{
const char* role_name = "fuchsia.devices.usb.drivers.xhci.interrupter";
const size_t role_name_size = strlen(role_name);
const zx_status_t status =
device_set_profile_by_role(hci_->zxdev(), zx_thread_self(), role_name, role_name_size);
if (status != ZX_OK) {
zxlogf(WARNING,
"Failed to apply role \"%s\" to the high priority XHCI completer. Service will be "
"best effort.\n",
role_name);
}
}
async::Irq irq;
irq.set_object(irq_.get());
irq.set_handler([&](async_dispatcher_t* dispatcher, async::Irq* irq, zx_status_t status,
const zx_packet_interrupt_t* interrupt) {
if (!irq_.is_valid()) {
async_loop_->Quit();
}
if (status != ZX_OK) {
async_loop_->Quit();
return;
}
if (event_ring_.HandleIRQ() != ZX_OK) {
zxlogf(ERROR, "Error handling IRQ. Exiting async loop.");
async_loop_->Quit();
return;
}
total_irqs_.Add(1);
irq_.ack();
});
irq.Begin(async_loop_->dispatcher());
if (!interrupter_) {
// Note: We need to run the ring 0 bringup after
// initializing interrupts, since Qemu initialization
// code assumes that interrupts are active and simulates
// a port status changed event.
if (event_ring_.Ring0Bringup()) {
zxlogf(ERROR, "Failed to bring up ring 0");
return ZX_ERR_INTERNAL;
}
}
async_loop_->Run();
return ZX_OK;
}
} // namespace usb_xhci