blob: 58ad6f15f2cce7a852b6bd218b43092084f3b010 [file] [log] [blame]
// Copyright 2022 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 "multiplier.h"
#include "registers.h"
namespace acpi_multiply {
// Configure the hardware resources for the ACPI multiplier device.
zx::status<> AcpiMultiplier::SetupMmioAndInterrupts() {
// Get interrupt.
auto irq_result = acpi_->MapInterrupt(0);
if (!irq_result.ok() || irq_result->is_error()) {
return irq_result.ok() ? irq_result->take_error() : zx::error(irq_result.error().status());
}
irq_ = std::move(irq_result->value()->irq);
// Start listening for interrupts.
irq_method_.set_object(irq_.get());
irq_method_.Begin(dispatcher_);
// Get MMIO region.
auto mmio_result = acpi_->GetMmio(0);
if (!mmio_result.ok() || mmio_result->is_error()) {
return mmio_result.ok() ? mmio_result->take_error() : zx::error(mmio_result.error().status());
}
auto& mmio = mmio_result->value()->mmio;
auto buf_result = fdf::MmioBuffer::Create(mmio.offset, mmio.size, std::move(mmio.vmo),
ZX_CACHE_POLICY_UNCACHED_DEVICE);
if (buf_result.is_error()) {
return buf_result.take_error();
}
mmio_ = std::move(buf_result.value());
return zx::ok();
}
// Call the underlying hardware resources (MMIO, Interrupt) to perform a multiply operation.
void AcpiMultiplier::DoMultiply(Operation operation) {
// Write the operands to the two MMIO registers.
MultiplyArgumentReg::Get(true).FromValue(0).set_operand(operation.a).WriteTo(&mmio_.value());
MultiplyArgumentReg::Get(false).FromValue(0).set_operand(operation.b).WriteTo(&mmio_.value());
current_op_ = std::move(operation);
fidl::Arena arena;
// Call the ACPI method that will trigger the multiply operation.
auto result =
acpi_->EvaluateObject(fidl::StringView::FromExternal("_MUL"),
fuchsia_hardware_acpi::wire::EvaluateObjectMode::kPlainObject,
fidl::VectorView<fuchsia_hardware_acpi::wire::Object>(arena, 0));
if (!result.ok() || result->is_error()) {
FDF_SLOG(ERROR, "Failed to send EvaluateObject",
KV("error", (const char*)(result.ok() ? std::to_string(result->error_value()).data()
: result.FormatDescription().data())));
current_op_->callback(zx::error(ZX_ERR_INTERNAL));
current_op_ = std::nullopt;
return;
}
// We will receive an interrupt when the operation is done.
}
// Callback when the ACPI device raises an interrupt, signaling the multiply operation is complete.
void AcpiMultiplier::HandleIrq(async_dispatcher_t* dispatcher, async::IrqBase* irq,
zx_status_t status, const zx_packet_interrupt_t* interrupt) {
irq_.ack();
if (!current_op_.has_value()) {
FDF_LOG(ERROR, "Spurious interrupt!");
return;
}
auto callback = std::move(current_op_->callback);
current_op_ = std::nullopt;
if (status != ZX_OK) {
FDF_SLOG(ERROR, "Failed to wait for interrupt", KV("status", zx_status_get_string(status)));
callback(zx::error(status));
return;
}
auto status_reg = MultiplyStatusReg::Get().ReadFrom(&mmio_.value());
if (!status_reg.finished()) {
FDF_LOG(ERROR, "Interrupt came too soon!");
callback(zx::error(ZX_ERR_BAD_STATE));
return;
}
auto result = MultiplyResultReg::Get().ReadFrom(&mmio_.value());
callback(zx::ok(AcpiMultiplier::MultiplyResult{
.value = result.result(),
.overflow = status_reg.overflow(),
}));
// Start the next operation if there is one.
if (!operation_queue_.empty()) {
DoMultiply(std::move(operation_queue_.front()));
operation_queue_.pop_front();
}
}
// Enqueue a multiply operation received from a FIDL client.
void AcpiMultiplier::QueueMultiplyOperation(Operation operation) {
// Queue operations if there is already one in-flight.
if (current_op_.has_value()) {
operation_queue_.emplace_back(std::move(operation));
return;
}
DoMultiply(std::move(operation));
}
} // namespace acpi_multiply