blob: b519e9eca03a0d57957eba7faa63dfe46035f2f3 [file] [log] [blame]
/*
* Copyright (C) 2021, HENSOLDT Cyber GmbH
*
* SPDX-License-Identifier: BSD-2-Clause
*/
/*
* More information about the mailboxes:
* - https://github.com/raspberrypi/firmware/wiki/Mailboxes
* - https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface
* - https://github.com/raspberrypi/documentation/blob/JamesH65-mailbox_docs/configuration/mailboxes/propertiesARM-VC.md
*/
#include <stdint.h>
#include <string.h>
#include <utils/util.h>
#include <platsupport/plat/mailbox.h>
#include "../../services.h"
#define DMA_PAGE_SIZE 4096
#define DMA_ALIGNEMENT 4096
// See: https://github.com/raspberrypi/firmware/wiki/Accessing-mailboxes#addresses-as-data
#define VC_BASE_CACHED 0x40000000
#define VC_BASE_UNCACHED 0xC0000000
#define VC_BASE VC_BASE_UNCACHED
// See: https://github.com/raspberrypi/firmware/wiki/Accessing-mailboxes#sample-code
#define MAILBOX_EMPTY 0x40000000
#define MAILBOX_FULL 0x80000000
// See: https://github.com/raspberrypi/firmware/wiki/Mailboxes#channels
#define MAILBOX_CHANNEL 8
typedef volatile struct {
uint32_t read; // 0x00
uint32_t reg_04; // 0x04
uint32_t reg_08; // 0x08
uint32_t reg_0c; // 0x0c
uint32_t reg_10; // 0x10
uint32_t reg_14; // 0x14
uint32_t status0; // 0x18
uint32_t reg_1c; // 0x1c
uint32_t write; // 0x20
uint32_t reg_24; // 0x24
uint32_t reg_28; // 0x28
uint32_t reg_2c; // 0x2c
uint32_t reg_30; // 0x30
uint32_t reg_34; // 0x34
uint32_t status1; // 0x38
} mailbox_regs_t;
static inline mailbox_regs_t *get_mailbox_regs(mailbox_t *mbox)
{
return (mbox != NULL && mbox->priv != NULL) ? (mailbox_regs_t *)((uintptr_t)mbox->priv + 0x880)
: NULL;
}
static inline uint32_t get_bus_address(uint32_t addr)
{
return (((addr) & ~VC_BASE) | VC_BASE);
}
/*
* Mailbox read operation
*
* 1. Read the status register until the empty flag is not set
* 2. Read data from the read register
* 3. If the lower four bits do not match the channel number desired then repeat
* from 1
* 4. The upper 28 bits are the returned data
*
* See: https://github.com/raspberrypi/documentation/blob/JamesH65-mailbox_docs/configuration/mailboxes/accessing.md#general-procedure
*/
static int mailbox_read(
mailbox_regs_t *mailbox,
uint32_t channel,
uint32_t *rsp
)
{
uint32_t value;
do {
while (mailbox->status0 & MAILBOX_EMPTY) {
// busy loop
}
value = mailbox->read;
} while ((value & 0xF) != channel);
*rsp = value & ~0xF;
return 0;
}
/*
* Mailbox write operation
*
* 1. Read the status register until the full flag is not set
* 2. Write the data (shifted into the upper 28 bits) combined with the channel
* (in the lower four bits) to the write register
*
* See: https://github.com/raspberrypi/documentation/blob/JamesH65-mailbox_docs/configuration/mailboxes/accessing.md#general-procedure
*/
static int mailbox_write(
mailbox_regs_t *mailbox,
uint32_t channel,
uint32_t data
)
{
while (mailbox->status1 & MAILBOX_FULL) {
// busy loop
}
mailbox->write = (data & ~0xF) | (channel & 0xF);
return 0;
}
/*
* Mailbox Command
*
* A command should only be issued in case the mailbox is empty. If the mailbox
* is not empty, it is said to be busy. In this case, it is the reponsibility of
* the caller to handle this situation.
*
* Every mailbox command consists of a request and a response. For this purpose
* PropertyTags must be created and configured for request. Afterwards, the
* mailbox interface is notified that there is a message waiting in the buffer.
* This signalling is done with the mailbox_write operation, that writes the
* buffer_address and the channel number into the write register. The
* mailbox_read operation makes sure that the response waiting in the mailbox
* interface belongs to the issued request by checking the current
* buffer_address and channel number in the read register.
*/
static int mailbox_command(
mailbox_t *mbox,
uint32_t channel,
uint32_t data,
uint32_t *rsp
)
{
mailbox_regs_t *mailbox = get_mailbox_regs(mbox);
if (mailbox == NULL) {
ZF_LOGE("Mailbox is invalid!");
return MAILBOX_ERR_INVALID;
}
// mailbox command should start operation with an empty mailbox
if (!(mailbox->status0 & MAILBOX_EMPTY)) {
ZF_LOGE("Mailbox is busy!");
return MAILBOX_ERR_BUSY;
}
if (mailbox_write(mailbox, channel, data) != 0) {
ZF_LOGE("Error when writing to mailbox!");
return MAILBOX_ERR_WRITE;
}
if (mailbox_read(mailbox, channel, rsp) != 0) {
ZF_LOGE("Error when reading from mailbox!");
return MAILBOX_ERR_READ;
}
return 0;
}
static int mailbox_message(
mailbox_t *mbox,
uint32_t tag_id,
void *request_tag,
uint32_t request_tag_size,
void *response_tag,
uint32_t response_tag_size
)
{
// ToDo: Implement the mailbox interface to handle multiple concatenated
// tags per message. Currently, we assume only one tag per message.
// See: https://github.com/raspberrypi/documentation/blob/JamesH65-mailbox_docs/configuration/mailboxes/propertiesARM-VC.md#message-content
// Prepare Mailbox
uint32_t tag_size = MAX(request_tag_size, response_tag_size);
MailboxInterface_PropertyBuffer_t *buffer = (MailboxInterface_PropertyBuffer_t *)mbox->buffer;
buffer->buffer_size = sizeof(MailboxInterface_PropertyBuffer_t)
+ tag_size
+ sizeof(uint32_t);
buffer->code = CODE_BUFFER_REQUEST_PROCESS;
MailboxInterface_PropertyTag_t *tags = (MailboxInterface_PropertyTag_t *)buffer->tags;
memcpy(tags, request_tag, request_tag_size);
tags->tag_id = tag_id;
tags->value_buffer_size = tag_size - sizeof(MailboxInterface_PropertyTag_t);
tags->value_length = tags->value_buffer_size & ~VALUE_LENGTH_RESPONSE;
uint32_t *end_tag = (uint32_t *)(tags + tag_size);
*end_tag = 0;
assert((uintptr_t)(const void *)end_tag % 4 == 0);
// Mailbox command
uint32_t rsp = 0;
uint32_t buffer_address = get_bus_address((uint32_t) mbox->phys_addr);
int status = mailbox_command(mbox,
MAILBOX_CHANNEL,
buffer_address,
&rsp);
// Response Evaluation
// Buffer format: https://github.com/raspberrypi/documentation/blob/JamesH65-mailbox_docs/configuration/mailboxes/propertiesARM-VC.md#buffer-contents
// Tag format: https://github.com/raspberrypi/documentation/blob/JamesH65-mailbox_docs/configuration/mailboxes/propertiesARM-VC.md#tag-format
if (status != 0) {
ZF_LOGE("Mailbox command error - code: %d!", status);
return status;
}
if (rsp != buffer_address) {
ZF_LOGE("Mailbox response should be buffer address!");
return MAILBOX_ERR_INTERNAL;
}
if (buffer->code != CODE_BUFFER_RESPONSE_SUCCESS) {
ZF_LOGE("Mailbox response is not successful!");
return MAILBOX_ERR_BUFFER;
}
if (tags->tag_id != tag_id) {
ZF_LOGE("Wrong tag id returned!");
return MAILBOX_ERR_TAG;
}
if (!(tags->value_length & VALUE_LENGTH_RESPONSE)) {
ZF_LOGE("Received tag is not a response!");
return MAILBOX_ERR_TAG;
}
if ((tags->value_length &= ~VALUE_LENGTH_RESPONSE) == 0) {
ZF_LOGE("Value buffer has length 0 bytes!");
return MAILBOX_ERR_TAG;
}
memcpy(response_tag, tags, response_tag_size);
return MAILBOX_OK;
}
int mailbox_init(ps_io_ops_t *io_ops, mailbox_t *mailbox)
{
void *reg = NULL;
MAP_IF_NULL(io_ops, MAILBOX, reg);
mailbox->priv = reg;
mailbox->dma_man = &io_ops->dma_manager;
mailbox->message = &mailbox_message;
mailbox->buffer = mailbox->dma_man->dma_alloc_fn(
mailbox->dma_man->cookie,
DMA_PAGE_SIZE,
DMA_ALIGNEMENT,
0,
PS_MEM_NORMAL);
if (mailbox->buffer == NULL) {
ZF_LOGE("DMA allocation failed.");
return MAILBOX_ERR_DMA;
}
mailbox->phys_addr = mailbox->dma_man->dma_pin_fn(mailbox->dma_man->cookie,
mailbox->buffer,
DMA_PAGE_SIZE);
return 0;
}
int mailbox_destroy(mailbox_t *mailbox)
{
mailbox->dma_man->dma_free_fn(mailbox->dma_man->cookie,
mailbox->buffer,
DMA_PAGE_SIZE);
mailbox->dma_man->dma_unpin_fn(mailbox->dma_man->cookie,
mailbox->buffer,
DMA_PAGE_SIZE);
return 0;
}