Browse Source

firmware: introduce SCMI core support

Introduce core support for ARM's SCMI (System Control and
Management Interface). This includes:

* shared memory (SHMEM) driver. This consists of a suite
of functions used to interact with the shared memory area.

* shared memory and doorbell-based transport layer driver.
Data is passed between platform and agent via shared
memory. Signaling is done using polling (PRE_KERNEL) and
doorbells (POST_KERNEL). This makes use of Zephyr MBOX API
(for signaling purposes) and the SHMEM driver (for polling
and data transfer).

* core driver - acts as glue between transport and protocol
layers. Provides synchronized access to transport layer
channels and channel assignment/initialization.

* infrastructure for creating SCMI protocols

This is based on ARM's SCMI Platform Design Document: DEN0056E.

Signed-off-by: Laurentiu Mihalcea <laurentiu.mihalcea@nxp.com>
pull/77242/head
Laurentiu Mihalcea 11 months ago committed by Anas Nashif
parent
commit
413c77cf4e
  1. 4
      cmake/linker_script/common/common-ram.cmake
  2. 1
      drivers/CMakeLists.txt
  3. 1
      drivers/Kconfig
  4. 3
      drivers/firmware/CMakeLists.txt
  5. 15
      drivers/firmware/Kconfig
  6. 8
      drivers/firmware/scmi/CMakeLists.txt
  7. 45
      drivers/firmware/scmi/Kconfig
  8. 214
      drivers/firmware/scmi/core.c
  9. 115
      drivers/firmware/scmi/mailbox.c
  10. 117
      drivers/firmware/scmi/mailbox.h
  11. 201
      drivers/firmware/scmi/shmem.c
  12. 12
      dts/bindings/firmware/arm,scmi-shmem.yaml
  13. 68
      dts/bindings/firmware/arm,scmi.yaml
  14. 122
      include/zephyr/drivers/firmware/scmi/protocol.h
  15. 67
      include/zephyr/drivers/firmware/scmi/shmem.h
  16. 274
      include/zephyr/drivers/firmware/scmi/transport.h
  17. 280
      include/zephyr/drivers/firmware/scmi/util.h
  18. 4
      include/zephyr/linker/common-ram.ld

4
cmake/linker_script/common/common-ram.cmake

@ -55,6 +55,10 @@ if(CONFIG_NETWORKING) @@ -55,6 +55,10 @@ if(CONFIG_NETWORKING)
zephyr_iterable_section(NAME eth_bridge GROUP DATA_REGION ${XIP_ALIGN_WITH_INPUT} SUBALIGN CONFIG_LINKER_ITERABLE_SUBALIGN)
endif()
if(CONFIG_ARM_SCMI)
zephyr_iterable_section(NAME scmi_protocol GROUP DATA_REGION ${XIP_ALIGN_WITH_INPUT} SUBALIGN CONFIG_LINKER_ITERABLE_SUBALIGN)
endif()
if(CONFIG_SENSING)
zephyr_iterable_section(NAME sensing_sensor GROUP DATA_REGION ${XIP_ALIGN_WITH_INPUT} SUBALIGN CONFIG_LINKER_ITERABLE_SUBALIGN)
endif()

1
drivers/CMakeLists.txt

@ -7,6 +7,7 @@ add_definitions(-D__ZEPHYR_SUPERVISOR__) @@ -7,6 +7,7 @@ add_definitions(-D__ZEPHYR_SUPERVISOR__)
# zephyr-keep-sorted-start
add_subdirectory(disk)
add_subdirectory(firmware)
add_subdirectory(interrupt_controller)
add_subdirectory(misc)
add_subdirectory(pcie)

1
drivers/Kconfig

@ -30,6 +30,7 @@ source "drivers/eeprom/Kconfig" @@ -30,6 +30,7 @@ source "drivers/eeprom/Kconfig"
source "drivers/entropy/Kconfig"
source "drivers/espi/Kconfig"
source "drivers/ethernet/Kconfig"
source "drivers/firmware/Kconfig"
source "drivers/flash/Kconfig"
source "drivers/fpga/Kconfig"
source "drivers/fuel_gauge/Kconfig"

3
drivers/firmware/CMakeLists.txt

@ -0,0 +1,3 @@ @@ -0,0 +1,3 @@
# SPDX-License-Identifier: Apache-2.0
add_subdirectory_ifdef(CONFIG_ARM_SCMI scmi)

15
drivers/firmware/Kconfig

@ -0,0 +1,15 @@ @@ -0,0 +1,15 @@
# Copyright 2024 NXP
# SPDX-License-Identifier: Apache-2.0
menu "Firmware drivers"
config ARM_SCMI
bool "Support for ARM's SCMI"
depends on ARM || ARM64
help
Enable support for ARM's System Configuration and Management
Interface (SCMI).
source "drivers/firmware/scmi/Kconfig"
endmenu

8
drivers/firmware/scmi/CMakeLists.txt

@ -0,0 +1,8 @@ @@ -0,0 +1,8 @@
# SPDX-License-Identifier: Apache-2.0
zephyr_library()
# SCMI core files
zephyr_library_sources_ifdef(CONFIG_ARM_SCMI core.c)
zephyr_library_sources_ifdef(CONFIG_ARM_SCMI_MAILBOX_TRANSPORT mailbox.c)
zephyr_library_sources_ifdef(CONFIG_ARM_SCMI_SHMEM shmem.c)

45
drivers/firmware/scmi/Kconfig

@ -0,0 +1,45 @@ @@ -0,0 +1,45 @@
# Copyright 2024 NXP
# SPDX-License-Identifier: Apache-2.0
if ARM_SCMI
config ARM_SCMI_MAILBOX_TRANSPORT
bool "SCMI transport based on shared memory and doorbells"
default y
depends on DT_HAS_ARM_SCMI_ENABLED
depends on ARM_SCMI_SHMEM
select ARM_SCMI_TRANSPORT_HAS_STATIC_CHANNELS
help
Enable support for SCMI transport based on shared memory
and doorbells.
config ARM_SCMI_SHMEM
bool "SCMI shared memory (SHMEM) driver"
default y
depends on DT_HAS_ARM_SCMI_SHMEM_ENABLED
help
Enable support for SCMI shared memory (SHMEM) driver.
config ARM_SCMI_SHMEM_INIT_PRIORITY
int "SCMI shared memory (SHMEM) initialization priority"
default 15
help
SCMI SHMEM driver device initialization priority.
config ARM_SCMI_TRANSPORT_HAS_STATIC_CHANNELS
bool "Transport layer has static channels"
help
Enable this if the SCMI transport layer uses static channels.
What this means is that each protocol will have its channels
assigned at compile time. This option is recommended for
transport layer drivers which can use the default channel
allocation scheme (i.e: use protocol-specific channels if
they exist, otherwise use base protocol channels).
config ARM_SCMI_TRANSPORT_INIT_PRIORITY
int "SCMI transport layer initialization priority"
default 20
help
SCMI transport driver device initialization priority.
endif # ARM_SCMI

214
drivers/firmware/scmi/core.c

@ -0,0 +1,214 @@ @@ -0,0 +1,214 @@
/*
* Copyright 2024 NXP
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/drivers/firmware/scmi/protocol.h>
#include <zephyr/drivers/firmware/scmi/transport.h>
#include <zephyr/logging/log.h>
#include <zephyr/device.h>
LOG_MODULE_REGISTER(scmi_core);
#define SCMI_CHAN_LOCK_TIMEOUT_USEC 500
#define SCMI_CHAN_SEM_TIMEOUT_USEC 500
int scmi_status_to_errno(int scmi_status)
{
switch (scmi_status) {
case SCMI_SUCCESS:
return 0;
case SCMI_NOT_SUPPORTED:
return -EOPNOTSUPP;
case SCMI_INVALID_PARAMETERS:
return -EINVAL;
case SCMI_DENIED:
return -EACCES;
case SCMI_NOT_FOUND:
return -ENOENT;
case SCMI_OUT_OF_RANGE:
return -ERANGE;
case SCMI_IN_USE:
case SCMI_BUSY:
return -EBUSY;
case SCMI_PROTOCOL_ERROR:
return -EPROTO;
case SCMI_COMMS_ERROR:
case SCMI_GENERIC_ERROR:
case SCMI_HARDWARE_ERROR:
default:
return -EIO;
}
}
static void scmi_core_reply_cb(struct scmi_channel *chan)
{
if (!k_is_pre_kernel()) {
k_sem_give(&chan->sem);
}
}
static int scmi_core_setup_chan(const struct device *transport,
struct scmi_channel *chan, bool tx)
{
int ret;
if (!chan) {
return -EINVAL;
}
if (chan->ready) {
return 0;
}
/* no support for RX channels ATM */
if (!tx) {
return -ENOTSUP;
}
k_mutex_init(&chan->lock);
k_sem_init(&chan->sem, 0, 1);
chan->cb = scmi_core_reply_cb;
/* setup transport-related channel data */
ret = scmi_transport_setup_chan(transport, chan, tx);
if (ret < 0) {
LOG_ERR("failed to setup channel");
return ret;
}
/* protocols might share a channel. In such cases, this
* will stop them from being initialized again.
*/
chan->ready = true;
return 0;
}
static int scmi_send_message_pre_kernel(struct scmi_protocol *proto,
struct scmi_message *msg,
struct scmi_message *reply)
{
int ret;
ret = scmi_transport_send_message(proto->transport, proto->tx, msg);
if (ret < 0) {
return ret;
}
/* no kernel primitives, we're forced to poll here.
*
* Cortex-M quirk: no interrupts at this point => no timer =>
* no timeout mechanism => this can block the whole system.
*
* TODO: is there a better way to handle this?
*/
while (!scmi_transport_channel_is_free(proto->transport, proto->tx)) {
}
ret = scmi_transport_read_message(proto->transport, proto->tx, reply);
if (ret < 0) {
return ret;
}
return ret;
}
static int scmi_send_message_post_kernel(struct scmi_protocol *proto,
struct scmi_message *msg,
struct scmi_message *reply)
{
int ret = 0;
if (!proto->tx) {
return -ENODEV;
}
/* wait for channel to be free */
ret = k_mutex_lock(&proto->tx->lock, K_USEC(SCMI_CHAN_LOCK_TIMEOUT_USEC));
if (ret < 0) {
LOG_ERR("failed to acquire chan lock");
return ret;
}
ret = scmi_transport_send_message(proto->transport, proto->tx, msg);
if (ret < 0) {
LOG_ERR("failed to send message");
goto out_release_mutex;
}
/* only one protocol instance can wait for a message reply at a time */
ret = k_sem_take(&proto->tx->sem, K_USEC(SCMI_CHAN_SEM_TIMEOUT_USEC));
if (ret < 0) {
LOG_ERR("failed to wait for msg reply");
goto out_release_mutex;
}
ret = scmi_transport_read_message(proto->transport, proto->tx, reply);
if (ret < 0) {
LOG_ERR("failed to read reply");
goto out_release_mutex;
}
out_release_mutex:
k_mutex_unlock(&proto->tx->lock);
return ret;
}
int scmi_send_message(struct scmi_protocol *proto, struct scmi_message *msg,
struct scmi_message *reply)
{
if (!proto->tx) {
return -ENODEV;
}
if (!proto->tx->ready) {
return -EINVAL;
}
if (k_is_pre_kernel()) {
return scmi_send_message_pre_kernel(proto, msg, reply);
} else {
return scmi_send_message_post_kernel(proto, msg, reply);
}
}
static int scmi_core_protocol_setup(const struct device *transport)
{
int ret;
STRUCT_SECTION_FOREACH(scmi_protocol, it) {
it->transport = transport;
#ifndef CONFIG_ARM_SCMI_TRANSPORT_HAS_STATIC_CHANNELS
/* no static channel allocation, attempt dynamic binding */
it->tx = scmi_transport_request_channel(transport, it->id, true);
#endif /* CONFIG_ARM_SCMI_TRANSPORT_HAS_STATIC_CHANNELS */
if (!it->tx) {
return -ENODEV;
}
ret = scmi_core_setup_chan(transport, it->tx, true);
if (ret < 0) {
return ret;
}
}
return 0;
}
int scmi_core_transport_init(const struct device *transport)
{
int ret;
ret = scmi_transport_init(transport);
if (ret < 0) {
return ret;
}
return scmi_core_protocol_setup(transport);
}

115
drivers/firmware/scmi/mailbox.c

@ -0,0 +1,115 @@ @@ -0,0 +1,115 @@
/*
* Copyright 2024 NXP
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/logging/log.h>
#include "mailbox.h"
LOG_MODULE_REGISTER(scmi_mbox);
static void scmi_mbox_cb(const struct device *mbox,
mbox_channel_id_t channel_id,
void *user_data,
struct mbox_msg *data)
{
struct scmi_channel *scmi_chan = user_data;
if (scmi_chan->cb)
scmi_chan->cb(scmi_chan);
}
static int scmi_mbox_send_message(const struct device *transport,
struct scmi_channel *chan,
struct scmi_message *msg)
{
struct scmi_mbox_channel *mbox_chan;
int ret;
mbox_chan = chan->data;
ret = scmi_shmem_write_message(mbox_chan->shmem, msg);
if (ret < 0) {
LOG_ERR("failed to write message to shmem: %d", ret);
return ret;
}
ret = mbox_send_dt(&mbox_chan->tx, NULL);
if (ret < 0) {
LOG_ERR("failed to ring doorbell: %d", ret);
return ret;
}
return 0;
}
static int scmi_mbox_read_message(const struct device *transport,
struct scmi_channel *chan,
struct scmi_message *msg)
{
struct scmi_mbox_channel *mbox_chan;
mbox_chan = chan->data;
return scmi_shmem_read_message(mbox_chan->shmem, msg);
}
static bool scmi_mbox_channel_is_free(const struct device *transport,
struct scmi_channel *chan)
{
struct scmi_mbox_channel *mbox_chan = chan->data;
return scmi_shmem_channel_status(mbox_chan->shmem) &
SCMI_SHMEM_CHAN_STATUS_BUSY_BIT;
}
static int scmi_mbox_setup_chan(const struct device *transport,
struct scmi_channel *chan,
bool tx)
{
int ret;
struct scmi_mbox_channel *mbox_chan;
struct mbox_dt_spec *tx_reply;
mbox_chan = chan->data;
if (!tx) {
return -ENOTSUP;
}
if (mbox_chan->tx_reply.dev) {
tx_reply = &mbox_chan->tx_reply;
} else {
tx_reply = &mbox_chan->tx;
}
ret = mbox_register_callback_dt(tx_reply, scmi_mbox_cb, chan);
if (ret < 0) {
LOG_ERR("failed to register tx reply cb");
return ret;
}
ret = mbox_set_enabled_dt(tx_reply, true);
if (ret < 0) {
LOG_ERR("failed to enable tx reply dbell");
}
/* enable interrupt-based communication */
scmi_shmem_update_flags(mbox_chan->shmem,
SCMI_SHMEM_CHAN_FLAG_IRQ_BIT,
SCMI_SHMEM_CHAN_FLAG_IRQ_BIT);
return 0;
}
static struct scmi_transport_api scmi_mbox_api = {
.setup_chan = scmi_mbox_setup_chan,
.send_message = scmi_mbox_send_message,
.read_message = scmi_mbox_read_message,
.channel_is_free = scmi_mbox_channel_is_free,
};
DT_INST_SCMI_MAILBOX_DEFINE(0, PRE_KERNEL_1,
CONFIG_ARM_SCMI_TRANSPORT_INIT_PRIORITY,
&scmi_mbox_api);

117
drivers/firmware/scmi/mailbox.h

@ -0,0 +1,117 @@ @@ -0,0 +1,117 @@
/*
* Copyright 2024 NXP
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _ZEPHYR_DRIVERS_FIRMWARE_SCMI_MAILBOX_H_
#define _ZEPHYR_DRIVERS_FIRMWARE_SCMI_MAILBOX_H_
#include <zephyr/drivers/firmware/scmi/transport.h>
#include <zephyr/drivers/firmware/scmi/util.h>
#include <zephyr/drivers/firmware/scmi/shmem.h>
#include <zephyr/drivers/mbox.h>
#include <zephyr/kernel.h>
#define DT_DRV_COMPAT arm_scmi
/* get a `struct device` for a protocol's shared memory area */
#define _SCMI_MBOX_SHMEM_BY_IDX(node_id, idx) \
COND_CODE_1(DT_PROP_HAS_IDX(node_id, shmem, idx), \
(DEVICE_DT_GET(DT_PROP_BY_IDX(node_id, shmem, idx))), \
(NULL))
/* get the name of mailbox channel's private data */
#define _SCMI_MBOX_CHAN_NAME(proto, idx)\
CONCAT(SCMI_TRANSPORT_CHAN_NAME(proto, idx), _, priv)
/* fetch a mailbox channel's doorbell */
#define _SCMI_MBOX_CHAN_DBELL(node_id, name) \
COND_CODE_1(DT_PROP_HAS_NAME(node_id, mboxes, name), \
(MBOX_DT_SPEC_GET(node_id, name)), \
({ }))
/* define private data for a protocol TX channel */
#define _SCMI_MBOX_CHAN_DEFINE_PRIV_TX(node_id, proto) \
static struct scmi_mbox_channel _SCMI_MBOX_CHAN_NAME(proto, 0) =\
{ \
.shmem = _SCMI_MBOX_SHMEM_BY_IDX(node_id, 0), \
.tx = _SCMI_MBOX_CHAN_DBELL(node_id, tx), \
.tx_reply = _SCMI_MBOX_CHAN_DBELL(node_id, tx_reply), \
}
/*
* Define a mailbox channel. This does two things:
* 1) Define the mandatory `struct scmi_channel` structure
* 2) Define the mailbox-specific private data for said
* channel (i.e: a struct scmi_mbox_channel)
*/
#define _SCMI_MBOX_CHAN_DEFINE(node_id, proto, idx) \
_SCMI_MBOX_CHAN_DEFINE_PRIV_TX(node_id, proto); \
DT_SCMI_TRANSPORT_CHAN_DEFINE(node_id, idx, proto, \
&(_SCMI_MBOX_CHAN_NAME(proto, idx))); \
/*
* Optionally define a mailbox channel for a protocol. This is optional
* because a protocol might not have a dedicated channel.
*/
#define _SCMI_MBOX_CHAN_DEFINE_OPTIONAL(node_id, proto, idx) \
COND_CODE_1(DT_PROP_HAS_IDX(node_id, shmem, idx), \
(_SCMI_MBOX_CHAN_DEFINE(node_id, proto, idx)), \
())
/* define a TX channel for a protocol node. This is preferred over
* _SCMI_MBOX_CHAN_DEFINE_OPTIONAL() since support for RX channels
* might be added later on. This macro is supposed to also define
* the RX channel
*/
#define SCMI_MBOX_PROTO_CHAN_DEFINE(node_id)\
_SCMI_MBOX_CHAN_DEFINE_OPTIONAL(node_id, DT_REG_ADDR(node_id), 0)
/* define and validate base protocol TX channel */
#define DT_INST_SCMI_MBOX_BASE_CHAN_DEFINE(inst) \
BUILD_ASSERT(DT_INST_PROP_LEN(inst, mboxes) != 1 || \
(DT_INST_PROP_HAS_IDX(inst, shmem, 0) && \
DT_INST_PROP_HAS_NAME(inst, mboxes, tx)), \
"bad bidirectional channel description"); \
\
BUILD_ASSERT(DT_INST_PROP_LEN(inst, mboxes) != 2 || \
(DT_INST_PROP_HAS_NAME(inst, mboxes, tx) && \
DT_INST_PROP_HAS_NAME(inst, mboxes, tx_reply)), \
"bad unidirectional channel description"); \
\
BUILD_ASSERT(DT_INST_PROP_LEN(inst, shmem) == 1, \
"bad SHMEM count"); \
\
BUILD_ASSERT(DT_INST_PROP_LEN(inst, mboxes) <= 2, \
"bad mbox count"); \
\
_SCMI_MBOX_CHAN_DEFINE(DT_INST(inst, DT_DRV_COMPAT), SCMI_PROTOCOL_BASE, 0)
/*
* Define the mailbox-based transport layer. What this does is:
*
* 1) Goes through all protocol nodes (children of the `scmi` node)
* and creates a `struct scmi_channel` and its associated
* `struct scmi_mbox_channel` if the protocol has a dedicated channel.
*
* 2) Creates aforementioned structures for the base protocol
* (identified by the `scmi` node)
*
* 3) "registers" the driver via `DT_INST_SCMI_TRANSPORT_DEFINE()`.
*/
#define DT_INST_SCMI_MAILBOX_DEFINE(inst, level, prio, api) \
DT_INST_FOREACH_CHILD_STATUS_OKAY(inst, SCMI_MBOX_PROTO_CHAN_DEFINE) \
DT_INST_SCMI_MBOX_BASE_CHAN_DEFINE(inst) \
DT_INST_SCMI_TRANSPORT_DEFINE(inst, NULL, NULL, NULL, level, prio, api)
struct scmi_mbox_channel {
/* SHMEM area bound to the channel */
const struct device *shmem;
/* TX dbell */
struct mbox_dt_spec tx;
/* TX reply dbell */
struct mbox_dt_spec tx_reply;
};
#endif /* _ZEPHYR_DRIVERS_FIRMWARE_SCMI_MAILBOX_H_ */

201
drivers/firmware/scmi/shmem.c

@ -0,0 +1,201 @@ @@ -0,0 +1,201 @@
/*
* 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);

12
dts/bindings/firmware/arm,scmi-shmem.yaml

@ -0,0 +1,12 @@ @@ -0,0 +1,12 @@
# Copyright 2024 NXP
# SPDX-License-Identifier: Apache-2.0
description: System Control and Management Interface (SCMI) shared memory (SHMEM)
compatible: "arm,scmi-shmem"
include: [base.yaml]
properties:
reg:
required: true

68
dts/bindings/firmware/arm,scmi.yaml

@ -0,0 +1,68 @@ @@ -0,0 +1,68 @@
# Copyright 2024 NXP
# SPDX-License-Identifier: Apache-2.0
description: |
System Control and Management Interface (SCMI) with doorbell
and shared memory (SHMEM) transport.
Devicetree example:
#include <mem.h>
scmi_res0: memory@44611000 {
compatible = "arm,scmi-shmem";
reg = <0x44611000 0x80>;
};
mu5: mailbox@44610000 {
compatible = "nxp,mbox-imx-mu";
reg = <0x44610000 DT_SIZE_K(4)>;
interrupts = <205 0>;
#mbox-cells = <1>;
};
scmi {
compatible = "arm,scmi";
shmem = <&scmi_res0>;
mboxes = <&mu5 0>;
mbox-names = "tx";
protocol@14 {
compatible = "arm,scmi-clock";
reg = <0x14>;
#clock-cells = <1>;
};
protocol@19 {
compatible = "arm,scmi-pinctrl";
reg = <0x19>;
pinctrl {
compatible = "nxp,imx95-pinctrl", "nxp,imx93-pinctrl";
};
};
};
compatible: "arm,scmi"
include: [base.yaml]
properties:
shmem:
type: phandle
required: true
description: |
Phandle to node describing TX channel shared memory area.
This translates to a **single** SCMI transmit channel.
mboxes:
required: true
description: |
List of mailbox channel specifiers. It should contain one or two specifiers:
1) tx - 1 mbox / 1 shmem (platform and agent use the same
mailbox channel for signaling)
2) tx_reply - 2 mbox / 1 shmem (platform and agent use different
mailbox channels for signaling)
mbox-names:
required: true
description: mailbox channel specifier names

122
include/zephyr/drivers/firmware/scmi/protocol.h

@ -0,0 +1,122 @@ @@ -0,0 +1,122 @@
/*
* Copyright 2024 NXP
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
* @brief SCMI protocol generic functions and structures
*/
#ifndef _INCLUDE_ZEPHYR_DRIVERS_FIRMWARE_SCMI_PROTOCOL_H_
#define _INCLUDE_ZEPHYR_DRIVERS_FIRMWARE_SCMI_PROTOCOL_H_
#include <zephyr/device.h>
#include <zephyr/drivers/firmware/scmi/util.h>
#include <stdint.h>
#include <errno.h>
/**
* @brief Build an SCMI message header
*
* Builds an SCMI message header based on the
* fields that make it up.
*
* @param id message ID
* @param type message type
* @param proto protocol ID
* @param token message token
*/
#define SCMI_MESSAGE_HDR_MAKE(id, type, proto, token) \
(SCMI_FIELD_MAKE(id, GENMASK(7, 0), 0) | \
SCMI_FIELD_MAKE(type, GENMASK(1, 0), 8) | \
SCMI_FIELD_MAKE(proto, GENMASK(7, 0), 10) | \
SCMI_FIELD_MAKE(token, GENMASK(9, 0), 18))
struct scmi_channel;
/**
* @brief SCMI message type
*/
enum scmi_message_type {
/** command message */
SCMI_COMMAND = 0x0,
/** delayed reply message */
SCMI_DELAYED_REPLY = 0x2,
/** notification message */
SCMI_NOTIFICATION = 0x3,
};
/**
* @brief SCMI status codes
*/
enum scmi_status_code {
SCMI_SUCCESS = 0,
SCMI_NOT_SUPPORTED = -1,
SCMI_INVALID_PARAMETERS = -2,
SCMI_DENIED = -3,
SCMI_NOT_FOUND = -4,
SCMI_OUT_OF_RANGE = -5,
SCMI_BUSY = -6,
SCMI_COMMS_ERROR = -7,
SCMI_GENERIC_ERROR = -8,
SCMI_HARDWARE_ERROR = -9,
SCMI_PROTOCOL_ERROR = -10,
SCMI_IN_USE = -11,
};
/**
* @struct scmi_protocol
*
* @brief SCMI protocol structure
*/
struct scmi_protocol {
/** protocol ID */
uint32_t id;
/** TX channel */
struct scmi_channel *tx;
/** transport layer device */
const struct device *transport;
/** protocol private data */
void *data;
};
/**
* @struct scmi_message
*
* @brief SCMI message structure
*/
struct scmi_message {
uint32_t hdr;
uint32_t len;
void *content;
};
/**
* @brief Convert an SCMI status code to its Linux equivalent (if possible)
*
* @param scmi_status SCMI status code as shown in `enum scmi_status_code`
*
* @retval Linux equivalent status code
*/
int scmi_status_to_errno(int scmi_status);
/**
* @brief Send an SCMI message and wait for its reply
*
* Blocking function used to send an SCMI message over
* a given channel and wait for its reply
*
* @param proto pointer to SCMI protocol
* @param msg pointer to SCMI message to send
* @param reply pointer to SCMI message in which the reply is to be
* written
*
* @retval 0 if successful
* @retval negative errno if failure
*/
int scmi_send_message(struct scmi_protocol *proto,
struct scmi_message *msg, struct scmi_message *reply);
#endif /* _INCLUDE_ZEPHYR_DRIVERS_FIRMWARE_SCMI_PROTOCOL_H_ */

67
include/zephyr/drivers/firmware/scmi/shmem.h

@ -0,0 +1,67 @@ @@ -0,0 +1,67 @@
/*
* Copyright 2024 NXP
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
* @brief SCMI SHMEM API
*/
#ifndef _INCLUDE_ZEPHYR_DRIVERS_FIRMWARE_SCMI_SHMEM_H_
#define _INCLUDE_ZEPHYR_DRIVERS_FIRMWARE_SCMI_SHMEM_H_
#include <zephyr/device.h>
#include <zephyr/arch/cpu.h>
#include <errno.h>
#define SCMI_SHMEM_CHAN_STATUS_BUSY_BIT BIT(0)
#define SCMI_SHMEM_CHAN_FLAG_IRQ_BIT BIT(0)
struct scmi_message;
/**
* @brief Write a message in the SHMEM area
*
* @param shmem pointer to shmem device
* @param msg message to write
*
* @retval 0 if successful
* @retval negative errno if failure
*/
int scmi_shmem_write_message(const struct device *shmem,
struct scmi_message *msg);
/**
* @brief Read a message from a SHMEM area
*
* @param shmem pointer to shmem device
* @param msg message to write the data into
*
* @retval 0 if successful
* @retval negative errno if failure
*/
int scmi_shmem_read_message(const struct device *shmem,
struct scmi_message *msg);
/**
* @brief Update the channel flags
*
* @param shmem pointer to shmem device
* @param mask value to negate and bitwise-and the old
* channel flags value
* @param val value to bitwise and with the mask and
* bitwise-or with the masked old value
*/
void scmi_shmem_update_flags(const struct device *shmem,
uint32_t mask, uint32_t val);
/**
* @brief Read a channel's status
*
* @param shmem pointer to shmem device
*/
uint32_t scmi_shmem_channel_status(const struct device *shmem);
#endif /* _INCLUDE_ZEPHYR_DRIVERS_FIRMWARE_SCMI_SHMEM_H_ */

274
include/zephyr/drivers/firmware/scmi/transport.h

@ -0,0 +1,274 @@ @@ -0,0 +1,274 @@
/*
* Copyright 2024 NXP
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
* @brief Public APIs for the SCMI transport layer drivers
*/
#ifndef _INCLUDE_ZEPHYR_DRIVERS_FIRMWARE_SCMI_TRANSPORT_H_
#define _INCLUDE_ZEPHYR_DRIVERS_FIRMWARE_SCMI_TRANSPORT_H_
#include <zephyr/device.h>
#include <zephyr/sys/mutex.h>
struct scmi_message;
struct scmi_channel;
/**
* @typedef scmi_channel_cb
*
* @brief Callback function for message replies
*
* This function should be called by the transport layer
* driver whenever a reply to a previously sent message
* has been received. Its purpose is to notifying the SCMI
* core of the reply's arrival so that proper action can
* be taken.
*
* @param chan pointer to SCMI channel on which the reply
* arrived
*/
typedef void (*scmi_channel_cb)(struct scmi_channel *chan);
/**
* @struct scmi_channel
* @brief SCMI channel structure
*
* An SCMI channel is a medium through which a protocol
* is able to transmit/receive messages. Each of the SCMI
* channels is represented by a `struct scmi_channel`.
*/
struct scmi_channel {
/**
* channel lock. This is meant to be initialized
* and used only by the SCMI core to assure that
* only one protocol can send/receive messages
* through a channel at a given moment.
*/
struct k_mutex lock;
/**
* binary semaphore. This is meant to be initialized
* and used only by the SCMI core. Its purpose is to
* signal that a reply has been received.
*/
struct k_sem sem;
/** channel private data */
void *data;
/**
* callback function. This is meant to be set by
* the SCMI core and should be called by the SCMI
* transport layer driver whenever a reply has
* been received.
*/
scmi_channel_cb cb;
/** is the channel ready to be used by a protocol? */
bool ready;
};
struct scmi_transport_api {
int (*init)(const struct device *transport);
int (*send_message)(const struct device *transport,
struct scmi_channel *chan,
struct scmi_message *msg);
int (*setup_chan)(const struct device *transport,
struct scmi_channel *chan,
bool tx);
int (*read_message)(const struct device *transport,
struct scmi_channel *chan,
struct scmi_message *msg);
bool (*channel_is_free)(const struct device *transport,
struct scmi_channel *chan);
struct scmi_channel *(*request_channel)(const struct device *transport,
uint32_t proto, bool tx);
};
/**
* @brief Request an SCMI channel dynamically
*
* Whenever the SCMI transport layer driver doesn't support
* static channel allocation, the SCMI core will try to bind
* a channel to a protocol dynamically using this function.
* Note that no setup needs to be performed on the channel
* in this function as the core will also call the channel
* setup() function.
*
* @param transport pointer to the device structure for the
* transport layer
* @param proto ID of the protocol for which the core is
* requesting the channel
* @param tx true if the channel is TX, false if RX
*
* @retval pointer to SCMI channel that's to be bound
* to the protocol
* @retval NULL if operation was not successful
*/
static inline struct scmi_channel *
scmi_transport_request_channel(const struct device *transport,
uint32_t proto, bool tx)
{
const struct scmi_transport_api *api =
(const struct scmi_transport_api *)transport->api;
if (api->request_channel) {
return api->request_channel(transport, proto, tx);
}
return NULL;
}
/**
* @brief Perform initialization for the transport layer driver
*
* The transport layer driver can't be initialized directly
* (i.e via a call to its init() function) during system initialization.
* This is because the macro used to define an SCMI transport places
* `scmi_core_transport_init()` in the init section instead of the
* driver's init() function. As such, `scmi_core_transport_init()`
* needs to call this function to perfrom transport layer driver
* initialization if required.
*
* This operation is optional.
*
* @param transport pointer to the device structure for the
* transport layer
*
* @retval 0 if successful
* @retval negative errno code if failure
*/
static inline int scmi_transport_init(const struct device *transport)
{
const struct scmi_transport_api *api =
(const struct scmi_transport_api *)transport->api;
if (api->init) {
return api->init(transport);
}
return 0;
}
/**
* @brief Setup an SCMI channel
*
* Before being able to send/receive messages, an SCMI channel needs
* to be prepared, which is what this function does. If it returns
* successfully, an SCMI protocol will be able to use this channel
* to send/receive messages.
*
* @param transport pointer to the device structure for the
* transport layer
* @param chan pointer to SCMI channel to be prepared
* @param tx true if the channel is TX, false if RX
*
* @retval 0 if successful
* @retval negative errno code if failure
*/
static inline int scmi_transport_setup_chan(const struct device *transport,
struct scmi_channel *chan,
bool tx)
{
const struct scmi_transport_api *api =
(const struct scmi_transport_api *)transport->api;
if (!api || !api->setup_chan) {
return -ENOSYS;
}
return api->setup_chan(transport, chan, tx);
}
/**
* @brief Send an SCMI channel
*
* Send an SCMI message using given SCMI channel. This function is
* not allowed to block.
*
* @param transport pointer to the device structure for the
* transport layer
* @param chan pointer to SCMI channel on which the message
* is to be sent
* @param msg pointer to message the caller wishes to send
*
* @retval 0 if successful
* @retval negative errno code if failure
*/
static inline int scmi_transport_send_message(const struct device *transport,
struct scmi_channel *chan,
struct scmi_message *msg)
{
const struct scmi_transport_api *api =
(const struct scmi_transport_api *)transport->api;
if (!api || !api->send_message) {
return -ENOSYS;
}
return api->send_message(transport, chan, msg);
}
/**
* @brief Read an SCMI message
*
* @param transport pointer to the device structure for the
* transport layer
* @param chan pointer to SCMI channel on which the message
* is to be read
* @param msg pointer to message the caller wishes to read
*
* @retval 0 if successful
* @retval negative errno code if failure
*/
static inline int scmi_transport_read_message(const struct device *transport,
struct scmi_channel *chan,
struct scmi_message *msg)
{
const struct scmi_transport_api *api =
(const struct scmi_transport_api *)transport->api;
if (!api || !api->read_message) {
return -ENOSYS;
}
return api->read_message(transport, chan, msg);
}
/**
* @brief Check if an SCMI channel is free
*
* @param transport pointer to the device structure for
* the transport layer
* @param chan pointer to SCMI channel the query is to be
* performed on
*
* @retval 0 if successful
* @retval negative errno code if failure
*/
static inline bool scmi_transport_channel_is_free(const struct device *transport,
struct scmi_channel *chan)
{
const struct scmi_transport_api *api =
(const struct scmi_transport_api *)transport->api;
if (!api || !api->channel_is_free) {
return -ENOSYS;
}
return api->channel_is_free(transport, chan);
}
/**
* @brief Perfrom SCMI core initialization
*
* @param transport pointer to the device structure for
* the transport layer
*
* @retval 0 if successful
* @retval negative errno code if failure
*/
int scmi_core_transport_init(const struct device *transport);
#endif /* _INCLUDE_ZEPHYR_DRIVERS_FIRMWARE_SCMI_TRANSPORT_H_ */

280
include/zephyr/drivers/firmware/scmi/util.h

@ -0,0 +1,280 @@ @@ -0,0 +1,280 @@
/*
* Copyright 2024 NXP
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
* @brief ARM SCMI utility header
*
* Contains various utility macros and macros used for protocol and
* transport "registration".
*/
#ifndef _INCLUDE_ZEPHYR_DRIVERS_FIRMWARE_SCMI_UTIL_H_
#define _INCLUDE_ZEPHYR_DRIVERS_FIRMWARE_SCMI_UTIL_H_
/**
* @brief Build protocol name from its ID
*
* Given a protocol ID, this macro builds the protocol
* name. This is done by concatenating the scmi_protocol_
* construct with the given protocol ID.
*
* @param proto protocol ID in decimal format
*
* @return protocol name
*/
#define SCMI_PROTOCOL_NAME(proto) CONCAT(scmi_protocol_, proto)
#ifdef CONFIG_ARM_SCMI_TRANSPORT_HAS_STATIC_CHANNELS
#ifdef CONFIG_ARM_SCMI_MAILBOX_TRANSPORT
/** @brief Check if a protocol node has an associated channel
*
* This macro, when applied to a protocol node, checks if
* the node has a dedicated static channel allocated to it.
* This definition is specific to the mailbox driver and
* each new transport layer driver should define its own
* version of this macro based on the devicetree properties
* that indicate the presence of a dedicated channel.
*
* @param node_id protocol node identifier
* @idx channel index. Should be 0 for TX channels and 1 for
* RX channels
*/
#define DT_SCMI_TRANSPORT_PROTO_HAS_CHAN(node_id, idx)\
DT_PROP_HAS_IDX(node_id, shmem, idx)
#else /* CONFIG_ARM_SCMI_MAILBOX_TRANSPORT */
#error "Transport with static channels needs to define HAS_CHAN macro"
#endif /* CONFIG_ARM_SCMI_MAILBOX_TRANSPORT */
#define SCMI_TRANSPORT_CHAN_NAME(proto, idx) CONCAT(scmi_channel_, proto, _, idx)
/**
* @brief Declare a TX SCMI channel
*
* Given a node_id for a protocol, this macro declares the SCMI
* TX channel statically bound to said protocol via the "extern"
* qualifier. This is useful when the transport layer driver
* supports static channels since all channel structures are
* defined inside the transport layer driver.
*
* @param node_id protocol node identifier
*/
#define DT_SCMI_TRANSPORT_TX_CHAN_DECLARE(node_id) \
COND_CODE_1(DT_SCMI_TRANSPORT_PROTO_HAS_CHAN(node_id, 0), \
(extern struct scmi_channel \
SCMI_TRANSPORT_CHAN_NAME(DT_REG_ADDR(node_id), 0);), \
(extern struct scmi_channel \
SCMI_TRANSPORT_CHAN_NAME(SCMI_PROTOCOL_BASE, 0);)) \
/**
* @brief Declare SCMI TX/RX channels
*
* Given a node_id for a protocol, this macro declares the
* SCMI TX and RX channels statically bound to said protocol via
* the "extern" qualifier. Since RX channels are currently not
* supported, this is equivalent to DT_SCMI_TRANSPORT_TX_CHAN_DECLARE().
* Despite this, users should opt for this macro instead of the TX-specific
* one.
*
* @param node_id protocol node identifier
*/
#define DT_SCMI_TRANSPORT_CHANNELS_DECLARE(node_id) \
DT_SCMI_TRANSPORT_TX_CHAN_DECLARE(node_id) \
/**
* @brief Declare SCMI TX/RX channels using node instance number
*
* Same as DT_SCMI_TRANSPORT_CHANNELS_DECLARE() but uses the
* protocol's node instance number and the DT_DRV_COMPAT macro.
*
* @param protocol node instance number
*/
#define DT_INST_SCMI_TRANSPORT_CHANNELS_DECLARE(inst) \
DT_SCMI_TRANSPORT_CHANNELS_DECLARE(DT_INST(inst, DT_DRV_COMPAT))
/**
* @brief Get a reference to a protocol's SCMI TX channel
*
* Given a node_id for a protocol, this macro returns a
* reference to an SCMI TX channel statically bound to said
* protocol.
*
* @param node_id protocol node identifier
*
* @return reference to the struct scmi_channel of the TX channel
* bound to the protocol identifier by node_id
*/
#define DT_SCMI_TRANSPORT_TX_CHAN(node_id) \
COND_CODE_1(DT_SCMI_TRANSPORT_PROTO_HAS_CHAN(node_id, 0), \
(&SCMI_TRANSPORT_CHAN_NAME(DT_REG_ADDR(node_id), 0)), \
(&SCMI_TRANSPORT_CHAN_NAME(SCMI_PROTOCOL_BASE, 0)))
/**
* @brief Define an SCMI channel for a protocol
*
* This macro defines a struct scmi_channel for a given protocol.
* This should be used by the transport layer driver to statically
* define SCMI channels for the protocols.
*
* @param node_id protocol node identifier
* @param idx channel index. Should be 0 for TX channels and 1
* for RX channels
* @param proto protocol ID in decimal format
*/
#define DT_SCMI_TRANSPORT_CHAN_DEFINE(node_id, idx, proto, pdata) \
struct scmi_channel SCMI_TRANSPORT_CHAN_NAME(proto, idx) = \
{ \
.data = pdata, \
}
/**
* @brief Define an SCMI protocol's data
*
* Each SCMI protocol is identified by a struct scmi_protocol
* placed in a linker section called scmi_protocol. Each protocol
* driver is required to use this macro for "registration". Using
* this macro directly is higly discouraged and users should opt
* for macros such as DT_SCMI_PROTOCOL_DEFINE_NODEV() or
* DT_SCMI_PROTOCOL_DEFINE(), which also takes care of the static
* channel declaration (if applicable).
*
* @param node_id protocol node identifier
* @param proto protocol ID in decimal format
* @param pdata protocol private data
*/
#define DT_SCMI_PROTOCOL_DATA_DEFINE(node_id, proto, pdata) \
STRUCT_SECTION_ITERABLE(scmi_protocol, SCMI_PROTOCOL_NAME(proto)) = \
{ \
.id = proto, \
.tx = DT_SCMI_TRANSPORT_TX_CHAN(node_id), \
.data = pdata, \
}
#else /* CONFIG_ARM_SCMI_TRANSPORT_HAS_STATIC_CHANNELS */
#define DT_SCMI_TRANSPORT_CHANNELS_DECLARE(node_id)
#define DT_SCMI_PROTOCOL_DATA_DEFINE(node_id, proto, pdata) \
STRUCT_SECTION_ITERABLE(scmi_protocol, SCMI_PROTOCOL_NAME(proto)) = \
{ \
.id = proto, \
.data = pdata, \
}
#endif /* CONFIG_ARM_SCMI_TRANSPORT_HAS_STATIC_CHANNELS */
/**
* @brief Define an SCMI transport driver
*
* This is merely a wrapper over DEVICE_DT_INST_DEFINE(), but is
* required since transport layer drivers are not allowed to place
* their own init() function in the init section. Instead, transport
* layer drivers place the scmi_core_transport_init() function in the
* init section, which, in turn, will call the transport layer driver
* init() function. This is required because the SCMI core needs to
* perform channel binding and setup during the transport layer driver's
* initialization.
*/
#define DT_INST_SCMI_TRANSPORT_DEFINE(inst, pm, data, config, level, prio, api) \
DEVICE_DT_INST_DEFINE(inst, &scmi_core_transport_init, \
pm, data, config, level, prio, api)
/**
* @brief Define an SCMI protocol
*
* This macro performs three important functions:
* 1) It defines a `struct scmi_protocol`, which is
* needed by all protocol drivers to work with the SCMI API.
*
* 2) It declares the static channels bound to the protocol.
* This is only applicable if the transport layer driver
* supports static channels.
*
* 3) It creates a `struct device` a sets the `data` field
* to the newly defined `struct scmi_protocol`. This is
* needed because the protocol driver needs to work with the
* SCMI API **and** the subsystem API.
*
* @param node_id protocol node identifier
* @param init_fn pointer to protocol's initialization function
* @param api pointer to protocol's subsystem API
* @param pm pointer to the protocol's power management resources
* @param data pointer to protocol's private data
* @param config pointer to protocol's private constant data
* @param level protocol initialization level
* @param prio protocol's priority within its initialization level
*/
#define DT_SCMI_PROTOCOL_DEFINE(node_id, init_fn, pm, data, config, \
level, prio, api) \
DT_SCMI_TRANSPORT_CHANNELS_DECLARE(node_id) \
DT_SCMI_PROTOCOL_DATA_DEFINE(node_id, DT_REG_ADDR(node_id), data); \
DEVICE_DT_DEFINE(node_id, init_fn, pm, \
&SCMI_PROTOCOL_NAME(DT_REG_ADDR(node_id)), \
config, level, prio, api)
/**
* @brief Just like DT_SCMI_PROTOCOL_DEFINE(), but uses an instance
* of a `DT_DRV_COMPAT` compatible instead of a node identifier
*
* @param inst instance number
* @param ... other parameters as expected by DT_SCMI_PROTOCOL_DEFINE()
*/
#define DT_INST_SCMI_PROTOCOL_DEFINE(inst, init_fn, pm, data, config, \
level, prio, api) \
DT_SCMI_PROTOCOL_DEFINE(DT_INST(inst, DT_DRV_COMPAT), init_fn, pm, \
data, config, level, prio, api)
/**
* @brief Define an SCMI protocol with no device
*
* Variant of DT_SCMI_PROTOCOL_DEFINE(), but no `struct device` is
* created and no initialization function is called during system
* initialization. This is useful for protocols that are not really
* part of a subsystem with an API (e.g: pinctrl).
*
* @param node_id protocol node identifier
* @param data protocol private data
*/
#define DT_SCMI_PROTOCOL_DEFINE_NODEV(node_id, data) \
DT_SCMI_TRANSPORT_CHANNELS_DECLARE(node_id) \
DT_SCMI_PROTOCOL_DATA_DEFINE(node_id, DT_REG_ADDR(node_id), data)
/**
* @brief Create an SCMI message field
*
* Data might not necessarily be encoded in the first
* x bits of an SCMI message parameter/return value.
* This comes in handy when building said parameters/
* return values.
*
* @param x value to encode
* @param mask value to perform bitwise-and with `x`
* @param shift value to left-shift masked `x`
*/
#define SCMI_FIELD_MAKE(x, mask, shift)\
(((uint32_t)(x) & (mask)) << (shift))
/**
* @brief SCMI protocol IDs
*
* Each SCMI protocol is identified by an ID. Each
* of these IDs needs to be in decimal since they
* might be used to build protocol and static channel
* names.
*/
#define SCMI_PROTOCOL_BASE 16
#define SCMI_PROTOCOL_POWER_DOMAIN 17
#define SCMI_PROTOCOL_SYSTEM 18
#define SCMI_PROTOCOL_PERF 19
#define SCMI_PROTOCOL_CLOCK 20
#define SCMI_PROTOCOL_SENSOR 21
#define SCMI_PROTOCOL_RESET_DOMAIN 22
#define SCMI_PROTOCOL_VOLTAGE_DOMAIN 23
#define SCMI_PROTOCOL_PCAP_MONITOR 24
#define SCMI_PROTOCOL_PINCTRL 25
#endif /* _INCLUDE_ZEPHYR_DRIVERS_FIRMWARE_SCMI_UTIL_H_ */

4
include/zephyr/linker/common-ram.ld

@ -12,6 +12,10 @@ @@ -12,6 +12,10 @@
#endif
#endif /* NETWORKING */
#ifdef CONFIG_ARM_SCMI
ITERABLE_SECTION_RAM(scmi_protocol, Z_LINK_ITERABLE_SUBALIGN)
#endif /* CONFIG_ARM_SCMI */
#if defined(CONFIG_GEN_SW_ISR_TABLE) && defined(CONFIG_DYNAMIC_INTERRUPTS)
SECTION_DATA_PROLOGUE(sw_isr_table,,)
{

Loading…
Cancel
Save