// 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 "aml-mailbox-hw.h" #include #include #include #include #include #include #include #include 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() { DdkRemove(); } 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) { 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\n", status); return status; } // AP reads the Payload to get requested information if (channel->rx_size != 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_PID, 0, PDEV_PID_AMLOGIC_S912}, {BIND_PLATFORM_DEV_DID, 0, PDEV_DID_AMLOGIC_SCPI}, }; device_add_args_t args = {}; args.version = DEVICE_ADD_ARGS_VERSION; args.name = "aml-mailbox"; args.ctx = this; args.ops = &ddk_device_proto_; args.proto_id = ddk_proto_id_; args.proto_ops = ddk_proto_ops_; args.props = props; args.prop_count = countof(props); return pdev_.DeviceAdd(0, &args, &zxdev_); } 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\n", 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\n", 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\n", 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(&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\n", status); return status; } else { zxlogf(INFO, "aml-mailbox driver added\n"); } // 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 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_KHADAS), BI_ABORT_IF(NE, BIND_PLATFORM_DEV_PID, PDEV_PID_VIM2), BI_MATCH_IF(EQ, BIND_PLATFORM_DEV_DID, PDEV_DID_AMLOGIC_MAILBOX), ZIRCON_DRIVER_END(aml_mailbox)