Browse Source
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
18 changed files with 1551 additions and 0 deletions
@ -0,0 +1,3 @@ |
|||||||
|
# SPDX-License-Identifier: Apache-2.0 |
||||||
|
|
||||||
|
add_subdirectory_ifdef(CONFIG_ARM_SCMI scmi) |
@ -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 |
@ -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) |
@ -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 |
@ -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); |
||||||
|
} |
@ -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); |
@ -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_ */ |
@ -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); |
@ -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 |
@ -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 |
@ -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_ */ |
@ -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_ */ |
@ -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_ */ |
@ -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_ */ |
Loading…
Reference in new issue