| // 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 <stdint.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <unistd.h> |
| |
| #define READ32_MAILBOX_PL_REG(offset) readl(io_buffer_virt(&mailbox->mmio_mailbox_payload) + \ |
| (offset)*4) |
| #define WRITE32_MAILBOX_PL_REG(offset, value) writel(value, \ |
| io_buffer_virt(&mailbox->mmio_mailbox_payload) \ |
| + (offset)*4) |
| #define READ32_MAILBOX_REG(offset) readl(io_buffer_virt(&mailbox->mmio_mailbox) + (offset)*4) |
| #define WRITE32_MAILBOX_REG(offset, value) writel(value, io_buffer_virt(&mailbox->mmio_mailbox) \ |
| + (offset)*4) |
| |
| static int aml_get_rx_mailbox(uint32_t tx_mailbox) { |
| switch (tx_mailbox) { |
| case AP_SECURE_MAILBOX: |
| return SCP_SECURE_MAILBOX; |
| case AP_NS_LOW_PRIORITY_MAILBOX: |
| return SCP_NS_LOW_PRIORITY_MAILBOX; |
| case AP_NS_HIGH_PRIORITY_MAILBOX: |
| return SCP_NS_HIGH_PRIORITY_MAILBOX; |
| default: |
| return INVALID_MAILBOX; |
| } |
| } |
| |
| static zx_status_t aml_mailbox_send_cmd(void* ctx, |
| const mailbox_channel_t* channel, |
| const mailbox_data_buf_t* mdata) { |
| aml_mailbox_t* mailbox = ctx; |
| int rx_mailbox_id; |
| if (!channel || !mdata) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| |
| if (INVALID_MAILBOX == (rx_mailbox_id = |
| aml_get_rx_mailbox(channel->mailbox))) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| |
| mtx_lock(&mailbox->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) { |
| uint32_t num = GET_NUM_WORDS(mdata->tx_size); |
| uint32_t* tx_payload = (uint32_t*)(mdata->tx_buffer); |
| for (uint32_t i = 0; i < num; i++) { |
| // AP writes parameters to Payload |
| WRITE32_MAILBOX_PL_REG(tx_mailbox->payload_offset + i, tx_payload[i]); |
| } |
| } |
| |
| // AP writes command to AP Mailbox |
| WRITE32_MAILBOX_REG(tx_mailbox->set_offset, mdata->cmd); |
| |
| zx_status_t status = zx_interrupt_wait(mailbox->inth[rx_mailbox_id], NULL); |
| if (status != ZX_OK) { |
| MAILBOX_ERROR("zx_interrupt_wait failed\n"); |
| mtx_unlock(&mailbox->mailbox_chan_lock[channel->mailbox]); |
| return status; |
| } |
| |
| // AP reads the Payload to get requested information |
| if (channel->rx_size != 0) { |
| uint32_t num = GET_NUM_WORDS(channel->rx_size); |
| uint32_t* rx_payload = (uint32_t*)(channel->rx_buffer); |
| for (uint32_t i = 0; i < num; i++) { |
| rx_payload[i] = READ32_MAILBOX_PL_REG(rx_mailbox->payload_offset + i); |
| } |
| } |
| |
| // AP writes to the Mailbox CLR register |
| WRITE32_MAILBOX_REG(rx_mailbox->clr_offset, 1); |
| |
| mtx_unlock(&mailbox->mailbox_chan_lock[channel->mailbox]); |
| return ZX_OK; |
| } |
| |
| static void aml_mailbox_release(void* ctx) { |
| aml_mailbox_t* mailbox = ctx; |
| io_buffer_release(&mailbox->mmio_mailbox); |
| io_buffer_release(&mailbox->mmio_mailbox_payload); |
| for (uint32_t i = 0; i < NUM_MAILBOXES; i++) { |
| zx_interrupt_destroy(mailbox->inth[i]); |
| zx_handle_close(mailbox->inth[i]); |
| } |
| free(mailbox); |
| } |
| |
| static zx_protocol_device_t aml_mailbox_device_protocol = { |
| .version = DEVICE_OPS_VERSION, |
| .release = aml_mailbox_release, |
| }; |
| |
| static mailbox_protocol_ops_t mailbox_ops = { |
| .send_command = aml_mailbox_send_cmd, |
| }; |
| |
| static zx_status_t aml_mailbox_bind(void* ctx, zx_device_t* parent) { |
| zx_status_t status = ZX_OK; |
| |
| aml_mailbox_t* mailbox = calloc(1, sizeof(aml_mailbox_t)); |
| if (!mailbox) { |
| return ZX_ERR_NO_MEMORY; |
| } |
| |
| status = device_get_protocol(parent, ZX_PROTOCOL_PLATFORM_DEV, &mailbox->pdev); |
| if (status != ZX_OK) { |
| MAILBOX_ERROR("Could not get parent protocol\n"); |
| goto fail; |
| } |
| |
| pdev_device_info_t info; |
| status = pdev_get_device_info(&mailbox->pdev, &info); |
| if (status != ZX_OK) { |
| MAILBOX_ERROR("pdev_get_device_info failed\n"); |
| goto fail; |
| } |
| |
| // Map all MMIOs |
| status = pdev_map_mmio_buffer(&mailbox->pdev, MMIO_MAILBOX, |
| ZX_CACHE_POLICY_UNCACHED_DEVICE, |
| &mailbox->mmio_mailbox); |
| if (status != ZX_OK) { |
| MAILBOX_ERROR("Could not map mailbox MMIO_MAILBOX %d\n", status); |
| goto fail; |
| } |
| |
| status = pdev_map_mmio_buffer(&mailbox->pdev, MMIO_MAILBOX_PAYLOAD, |
| ZX_CACHE_POLICY_UNCACHED_DEVICE, |
| &mailbox->mmio_mailbox_payload); |
| if (status != ZX_OK) { |
| MAILBOX_ERROR("Could not map mailbox MMIO_MAILBOX_PAYLOAD %d\n", status); |
| goto fail; |
| } |
| |
| for (uint32_t i = 0; i < NUM_MAILBOXES; i++) { |
| status = pdev_map_interrupt(&mailbox->pdev, i, |
| &mailbox->inth[i]); |
| if (status != ZX_OK) { |
| MAILBOX_ERROR("pdev_map_interrupt failed %d\n", status); |
| goto fail; |
| } |
| |
| mtx_init(&mailbox->mailbox_chan_lock[i], mtx_plain); |
| } |
| |
| 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 = { |
| .version = DEVICE_ADD_ARGS_VERSION, |
| .name = "aml-mailbox", |
| .ctx = mailbox, |
| .ops = &aml_mailbox_device_protocol, |
| .proto_id = ZX_PROTOCOL_MAILBOX, |
| .proto_ops = &mailbox_ops, |
| .props = props, |
| .prop_count = countof(props), |
| }; |
| |
| status = pdev_device_add(&mailbox->pdev, 0, &args, NULL); |
| if (status != ZX_OK) { |
| goto fail; |
| } |
| |
| return ZX_OK; |
| fail: |
| aml_mailbox_release(mailbox); |
| return ZX_OK; |
| } |
| |
| static zx_driver_ops_t aml_mailbox_driver_ops = { |
| .version = DRIVER_OPS_VERSION, |
| .bind = aml_mailbox_bind, |
| }; |
| |
| ZIRCON_DRIVER_BEGIN(aml_mailbox, aml_mailbox_driver_ops, "zircon", "0.1", 4) |
| BI_ABORT_IF(NE, BIND_PROTOCOL, ZX_PROTOCOL_PLATFORM_DEV), |
| 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) |