| // Copyright 2018 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 "aml-mailbox.h" |
| |
| #include <stdint.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <unistd.h> |
| |
| #include <fbl/algorithm.h> |
| #include <fbl/alloc_checker.h> |
| #include <fbl/auto_lock.h> |
| |
| #include "aml-mailbox-hw.h" |
| #include "src/devices/scpi/drivers/aml-scpi-s912/aml-mailbox-s912-bind.h" |
| |
| namespace mailbox { |
| |
| mailbox_type_t AmlMailbox::GetRxMailbox(mailbox_type_t tx_mailbox) { |
| switch (tx_mailbox) { |
| case MAILBOX_TYPE_AP_SECURE_MAILBOX: |
| return MAILBOX_TYPE_SCP_SECURE_MAILBOX; |
| case MAILBOX_TYPE_AP_NS_LOW_PRIORITY_MAILBOX: |
| return MAILBOX_TYPE_SCP_NS_LOW_PRIORITY_MAILBOX; |
| case MAILBOX_TYPE_AP_NS_HIGH_PRIORITY_MAILBOX: |
| return MAILBOX_TYPE_SCP_NS_HIGH_PRIORITY_MAILBOX; |
| default: |
| return MAILBOX_TYPE_INVALID_MAILBOX; |
| } |
| } |
| |
| size_t AmlMailbox::GetNumWords(size_t size) { return (size / 4 + ((size % 4) ? 1 : 0)); } |
| |
| void AmlMailbox::DdkUnbind(ddk::UnbindTxn txn) { txn.Reply(); } |
| |
| void AmlMailbox::DdkRelease() { delete this; } |
| |
| zx_status_t AmlMailbox::MailboxSendCommand(const mailbox_channel_t* channel, |
| const mailbox_data_buf_t* mdata) { |
| if (!channel || !mdata) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| |
| mailbox_type_t rx_mailbox_id; |
| if (MAILBOX_TYPE_INVALID_MAILBOX == (rx_mailbox_id = GetRxMailbox(channel->mailbox))) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| |
| fbl::AutoLock mailbox_lock(&mailbox_chan_lock_[channel->mailbox]); |
| |
| aml_mailbox_block_t* rx_mailbox = &vim2_mailbox_block[rx_mailbox_id]; |
| aml_mailbox_block_t* tx_mailbox = &vim2_mailbox_block[channel->mailbox]; |
| |
| if (mdata->tx_size != 0) { |
| ZX_DEBUG_ASSERT(mdata->tx_size % sizeof(uint32_t) == 0); |
| |
| size_t num = GetNumWords(mdata->tx_size); |
| uint32_t* tx_payload = (uint32_t*)(mdata->tx_buffer); |
| for (size_t i = 0; i < num; i++) { |
| // AP writes parameters to Payload |
| mailbox_payload_mmio_->Write32(tx_payload[i], tx_mailbox->payload_offset + (i << 2)); |
| } |
| } |
| |
| // AP writes command to AP Mailbox |
| mailbox_mmio_->Write32(mdata->cmd, tx_mailbox->set_offset); |
| |
| zx_status_t status = inth_[rx_mailbox_id].wait(nullptr); |
| if (status != ZX_OK) { |
| zxlogf(ERROR, "aml-mailbox: zx_interrupt_wait failed: %d", status); |
| return status; |
| } |
| |
| // AP reads the Payload to get requested information |
| if (channel->rx_size != 0) { |
| ZX_DEBUG_ASSERT(channel->rx_size % sizeof(uint32_t) == 0); |
| |
| size_t num = GetNumWords(channel->rx_size); |
| uint32_t* rx_payload = (uint32_t*)(channel->rx_buffer); |
| for (size_t i = 0; i < num; i++) { |
| rx_payload[i] = mailbox_payload_mmio_->Read32(rx_mailbox->payload_offset + (i << 2)); |
| } |
| } |
| |
| // AP writes to the Mailbox CLR register |
| mailbox_mmio_->Write32(1, rx_mailbox->clr_offset); |
| return ZX_OK; |
| } |
| |
| zx_status_t AmlMailbox::Bind() { |
| zx_device_prop_t props[] = { |
| {BIND_PLATFORM_DEV_VID, 0, PDEV_VID_AMLOGIC}, |
| {BIND_PLATFORM_DEV_DID, 0, PDEV_DID_AMLOGIC_MAILBOX}, |
| }; |
| |
| return DdkAdd(ddk::DeviceAddArgs("aml-mailbox").set_props(props)); |
| } |
| |
| zx_status_t AmlMailbox::InitPdev() { |
| if (!pdev_.is_valid()) { |
| return ZX_ERR_NO_RESOURCES; |
| } |
| |
| // Map MMIOs |
| zx_status_t status = pdev_.MapMmio(MMIO_MAILBOX, &mailbox_mmio_); |
| if (status != ZX_OK) { |
| zxlogf(ERROR, "aml-mailbox: could not map mailbox mmio: %d", status); |
| return status; |
| } |
| |
| status = pdev_.MapMmio(MMIO_MAILBOX_PAYLOAD, &mailbox_payload_mmio_); |
| if (status != ZX_OK) { |
| zxlogf(ERROR, "aml-mailbox: could not map payload mmio: %d", status); |
| return status; |
| } |
| |
| for (uint32_t i = 0; i < kNumMailboxes; i++) { |
| status = pdev_.GetInterrupt(i, &inth_[i]); |
| if (status != ZX_OK) { |
| zxlogf(ERROR, "aml-mailbox: could not map interrupt: %d", status); |
| return status; |
| } |
| |
| mtx_init(&mailbox_chan_lock_[i], mtx_plain); |
| } |
| |
| return status; |
| } |
| |
| zx_status_t AmlMailbox::Create(zx_device_t* parent) { |
| fbl::AllocChecker ac; |
| auto mailbox_device = fbl::make_unique_checked<AmlMailbox>(&ac, parent); |
| if (!ac.check()) { |
| return ZX_ERR_NO_MEMORY; |
| } |
| |
| zx_status_t status = mailbox_device->InitPdev(); |
| if (status != ZX_OK) { |
| return status; |
| } |
| |
| status = mailbox_device->Bind(); |
| if (status != ZX_OK) { |
| zxlogf(ERROR, "aml-mailbox driver failed to get added: %d", status); |
| return status; |
| } else { |
| zxlogf(INFO, "aml-mailbox driver added"); |
| } |
| |
| // mailbox_device intentionally leaked as it is now held by DevMgr |
| __UNUSED auto ptr = mailbox_device.release(); |
| |
| return ZX_OK; |
| } |
| |
| zx_status_t aml_mailbox_bind(void* ctx, zx_device_t* parent) { |
| return mailbox::AmlMailbox::Create(parent); |
| } |
| |
| static constexpr zx_driver_ops_t driver_ops = []() { |
| zx_driver_ops_t ops = {}; |
| ops.version = DRIVER_OPS_VERSION; |
| ops.bind = aml_mailbox_bind; |
| return ops; |
| }(); |
| |
| } // namespace mailbox |
| |
| ZIRCON_DRIVER(aml_mailbox, mailbox::driver_ops, "zircon", "0.1"); |