| // 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 <ddk/binding.h> |
| #include <fbl/auto_lock.h> |
| #include <fbl/unique_ptr.h> |
| #include <stdint.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <unistd.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() { |
| 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<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\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) |