blob: 6ec645cbc3096da1db0e3fa501f5bc3bee44749c [file] [log] [blame]
/*
* Copyright 2024 NXP
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/drivers/firmware/scmi/shmem.h>
#include <zephyr/drivers/firmware/scmi/protocol.h>
#include <zephyr/logging/log.h>
#include <string.h>
LOG_MODULE_REGISTER(arm_scmi_shmem);
#define DT_DRV_COMPAT arm_scmi_shmem
#ifndef DEVICE_MMIO_IS_IN_RAM
#define device_map(virt, phys, size, flags) *(virt) = (phys)
#endif /* DEVICE_MMIO_IS_IN_RAM */
struct scmi_shmem_config {
uintptr_t phys_addr;
uint32_t size;
};
struct scmi_shmem_data {
mm_reg_t regmap;
};
struct scmi_shmem_layout {
volatile uint32_t res0;
volatile uint32_t chan_status;
volatile uint32_t res1[2];
volatile uint32_t chan_flags;
volatile uint32_t len;
volatile uint32_t msg_hdr;
};
int scmi_shmem_get_channel_status(const struct device *dev, uint32_t *status)
{
struct scmi_shmem_data *data;
struct scmi_shmem_layout *layout;
data = dev->data;
layout = (struct scmi_shmem_layout *)data->regmap;
*status = layout->chan_status;
return 0;
}
static void scmi_shmem_memcpy(mm_reg_t dst, mm_reg_t src, uint32_t bytes)
{
int i;
for (i = 0; i < bytes; i++) {
sys_write8(*(uint8_t *)(src + i), dst + i);
}
}
int scmi_shmem_read_message(const struct device *shmem, struct scmi_message *msg)
{
struct scmi_shmem_layout *layout;
struct scmi_shmem_data *data;
const struct scmi_shmem_config *cfg;
data = shmem->data;
cfg = shmem->config;
layout = (struct scmi_shmem_layout *)data->regmap;
/* some sanity checks first */
if (!msg) {
return -EINVAL;
}
if (!msg->content && msg->len) {
return -EINVAL;
}
if (cfg->size < (sizeof(*layout) + msg->len)) {
LOG_ERR("message doesn't fit in shmem area");
return -EINVAL;
}
/* mismatch between expected reply size and actual size? */
if (msg->len != (layout->len - sizeof(layout->msg_hdr))) {
LOG_ERR("bad message len. Expected 0x%x, got 0x%x",
msg->len,
(uint32_t)(layout->len - sizeof(layout->msg_hdr)));
return -EINVAL;
}
/* header match? */
if (layout->msg_hdr != msg->hdr) {
LOG_ERR("bad message header. Expected 0x%x, got 0x%x",
msg->hdr, layout->msg_hdr);
return -EINVAL;
}
if (msg->content) {
scmi_shmem_memcpy(POINTER_TO_UINT(msg->content),
data->regmap + sizeof(*layout), msg->len);
}
return 0;
}
int scmi_shmem_write_message(const struct device *shmem, struct scmi_message *msg)
{
struct scmi_shmem_layout *layout;
struct scmi_shmem_data *data;
const struct scmi_shmem_config *cfg;
data = shmem->data;
cfg = shmem->config;
layout = (struct scmi_shmem_layout *)data->regmap;
/* some sanity checks first */
if (!msg) {
return -EINVAL;
}
if (!msg->content && msg->len) {
return -EINVAL;
}
if (cfg->size < (sizeof(*layout) + msg->len)) {
return -EINVAL;
}
if (!(layout->chan_status & SCMI_SHMEM_CHAN_STATUS_BUSY_BIT)) {
return -EBUSY;
}
layout->len = sizeof(layout->msg_hdr) + msg->len;
layout->msg_hdr = msg->hdr;
if (msg->content) {
scmi_shmem_memcpy(data->regmap + sizeof(*layout),
POINTER_TO_UINT(msg->content), msg->len);
}
/* done, mark channel as busy and proceed */
layout->chan_status &= ~SCMI_SHMEM_CHAN_STATUS_BUSY_BIT;
return 0;
}
uint32_t scmi_shmem_channel_status(const struct device *shmem)
{
struct scmi_shmem_layout *layout;
struct scmi_shmem_data *data;
data = shmem->data;
layout = (struct scmi_shmem_layout *)data->regmap;
return layout->chan_status;
}
void scmi_shmem_update_flags(const struct device *shmem, uint32_t mask, uint32_t val)
{
struct scmi_shmem_layout *layout;
struct scmi_shmem_data *data;
data = shmem->data;
layout = (struct scmi_shmem_layout *)data->regmap;
layout->chan_flags = (layout->chan_flags & ~mask) | (val & mask);
}
static int scmi_shmem_init(const struct device *dev)
{
const struct scmi_shmem_config *cfg;
struct scmi_shmem_data *data;
cfg = dev->config;
data = dev->data;
if (cfg->size < sizeof(struct scmi_shmem_layout)) {
return -EINVAL;
}
device_map(&data->regmap, cfg->phys_addr, cfg->size, K_MEM_CACHE_NONE);
return 0;
}
#define SCMI_SHMEM_INIT(inst) \
static const struct scmi_shmem_config config_##inst = { \
.phys_addr = DT_INST_REG_ADDR(inst), \
.size = DT_INST_REG_SIZE(inst), \
}; \
\
static struct scmi_shmem_data data_##inst; \
\
DEVICE_DT_INST_DEFINE(inst, &scmi_shmem_init, NULL, \
&data_##inst, &config_##inst, \
PRE_KERNEL_1, \
CONFIG_ARM_SCMI_SHMEM_INIT_PRIORITY, \
NULL);
DT_INST_FOREACH_STATUS_OKAY(SCMI_SHMEM_INIT);