blob: 0e08b216466f3341fd2c152397f96758eec3ff1a [file] [log] [blame]
// 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 <ddk/binding.h>
#include <fbl/algorithm.h>
#include <fbl/alloc_checker.h>
#include <fbl/auto_lock.h>
#include "aml-mailbox-hw.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::DdkUnbindNew(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("aml-mailbox", 0, props, fbl::count_of(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
// clang-format off
ZIRCON_DRIVER_BEGIN(aml_mailbox, mailbox::driver_ops, "zircon", "0.1", 4)
BI_ABORT_IF(NE, BIND_PROTOCOL, ZX_PROTOCOL_PDEV),
BI_ABORT_IF(NE, BIND_PLATFORM_DEV_VID, PDEV_VID_AMLOGIC),
BI_ABORT_IF(NE, BIND_PLATFORM_DEV_PID, PDEV_PID_AMLOGIC_S912),
BI_MATCH_IF(EQ, BIND_PLATFORM_DEV_DID, PDEV_DID_AMLOGIC_MAILBOX),
ZIRCON_DRIVER_END(aml_mailbox)