| // 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 |