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>
This commit is contained in:
parent
0f7349d3ca
commit
413c77cf4e
18 changed files with 1551 additions and 0 deletions
|
@ -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)
|
zephyr_iterable_section(NAME eth_bridge GROUP DATA_REGION ${XIP_ALIGN_WITH_INPUT} SUBALIGN CONFIG_LINKER_ITERABLE_SUBALIGN)
|
||||||
endif()
|
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)
|
if(CONFIG_SENSING)
|
||||||
zephyr_iterable_section(NAME sensing_sensor GROUP DATA_REGION ${XIP_ALIGN_WITH_INPUT} SUBALIGN CONFIG_LINKER_ITERABLE_SUBALIGN)
|
zephyr_iterable_section(NAME sensing_sensor GROUP DATA_REGION ${XIP_ALIGN_WITH_INPUT} SUBALIGN CONFIG_LINKER_ITERABLE_SUBALIGN)
|
||||||
endif()
|
endif()
|
||||||
|
|
|
@ -7,6 +7,7 @@ add_definitions(-D__ZEPHYR_SUPERVISOR__)
|
||||||
|
|
||||||
# zephyr-keep-sorted-start
|
# zephyr-keep-sorted-start
|
||||||
add_subdirectory(disk)
|
add_subdirectory(disk)
|
||||||
|
add_subdirectory(firmware)
|
||||||
add_subdirectory(interrupt_controller)
|
add_subdirectory(interrupt_controller)
|
||||||
add_subdirectory(misc)
|
add_subdirectory(misc)
|
||||||
add_subdirectory(pcie)
|
add_subdirectory(pcie)
|
||||||
|
|
|
@ -30,6 +30,7 @@ source "drivers/eeprom/Kconfig"
|
||||||
source "drivers/entropy/Kconfig"
|
source "drivers/entropy/Kconfig"
|
||||||
source "drivers/espi/Kconfig"
|
source "drivers/espi/Kconfig"
|
||||||
source "drivers/ethernet/Kconfig"
|
source "drivers/ethernet/Kconfig"
|
||||||
|
source "drivers/firmware/Kconfig"
|
||||||
source "drivers/flash/Kconfig"
|
source "drivers/flash/Kconfig"
|
||||||
source "drivers/fpga/Kconfig"
|
source "drivers/fpga/Kconfig"
|
||||||
source "drivers/fuel_gauge/Kconfig"
|
source "drivers/fuel_gauge/Kconfig"
|
||||||
|
|
3
drivers/firmware/CMakeLists.txt
Normal file
3
drivers/firmware/CMakeLists.txt
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
add_subdirectory_ifdef(CONFIG_ARM_SCMI scmi)
|
15
drivers/firmware/Kconfig
Normal file
15
drivers/firmware/Kconfig
Normal file
|
@ -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
Normal file
8
drivers/firmware/scmi/CMakeLists.txt
Normal file
|
@ -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
Normal file
45
drivers/firmware/scmi/Kconfig
Normal file
|
@ -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
Normal file
214
drivers/firmware/scmi/core.c
Normal file
|
@ -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
Normal file
115
drivers/firmware/scmi/mailbox.c
Normal file
|
@ -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
Normal file
117
drivers/firmware/scmi/mailbox.h
Normal file
|
@ -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
Normal file
201
drivers/firmware/scmi/shmem.c
Normal file
|
@ -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
Normal file
12
dts/bindings/firmware/arm,scmi-shmem.yaml
Normal file
|
@ -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
Normal file
68
dts/bindings/firmware/arm,scmi.yaml
Normal file
|
@ -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
Normal file
122
include/zephyr/drivers/firmware/scmi/protocol.h
Normal file
|
@ -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
Normal file
67
include/zephyr/drivers/firmware/scmi/shmem.h
Normal file
|
@ -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
Normal file
274
include/zephyr/drivers/firmware/scmi/transport.h
Normal file
|
@ -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
Normal file
280
include/zephyr/drivers/firmware/scmi/util.h
Normal file
|
@ -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_ */
|
|
@ -12,6 +12,10 @@
|
||||||
#endif
|
#endif
|
||||||
#endif /* NETWORKING */
|
#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)
|
#if defined(CONFIG_GEN_SW_ISR_TABLE) && defined(CONFIG_DYNAMIC_INTERRUPTS)
|
||||||
SECTION_DATA_PROLOGUE(sw_isr_table,,)
|
SECTION_DATA_PROLOGUE(sw_isr_table,,)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue