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 @@
@@ -0,0 +1,3 @@
|
||||
# SPDX-License-Identifier: Apache-2.0 |
||||
|
||||
add_subdirectory_ifdef(CONFIG_ARM_SCMI scmi) |
@ -0,0 +1,15 @@
@@ -0,0 +1,15 @@
|
||||
# Copyright 2024 NXP |
||||
# SPDX-License-Identifier: Apache-2.0 |
||||
|
||||
menu "Firmware drivers" |
||||
|
||||
config ARM_SCMI |
||||
bool "Support for ARM's SCMI" |
||||
depends on ARM || ARM64 |
||||
help |
||||
Enable support for ARM's System Configuration and Management |
||||
Interface (SCMI). |
||||
|
||||
source "drivers/firmware/scmi/Kconfig" |
||||
|
||||
endmenu |
@ -0,0 +1,8 @@
@@ -0,0 +1,8 @@
|
||||
# SPDX-License-Identifier: Apache-2.0 |
||||
|
||||
zephyr_library() |
||||
|
||||
# SCMI core files |
||||
zephyr_library_sources_ifdef(CONFIG_ARM_SCMI core.c) |
||||
zephyr_library_sources_ifdef(CONFIG_ARM_SCMI_MAILBOX_TRANSPORT mailbox.c) |
||||
zephyr_library_sources_ifdef(CONFIG_ARM_SCMI_SHMEM shmem.c) |
@ -0,0 +1,45 @@
@@ -0,0 +1,45 @@
|
||||
# Copyright 2024 NXP |
||||
# SPDX-License-Identifier: Apache-2.0 |
||||
|
||||
if ARM_SCMI |
||||
|
||||
config ARM_SCMI_MAILBOX_TRANSPORT |
||||
bool "SCMI transport based on shared memory and doorbells" |
||||
default y |
||||
depends on DT_HAS_ARM_SCMI_ENABLED |
||||
depends on ARM_SCMI_SHMEM |
||||
select ARM_SCMI_TRANSPORT_HAS_STATIC_CHANNELS |
||||
help |
||||
Enable support for SCMI transport based on shared memory |
||||
and doorbells. |
||||
|
||||
config ARM_SCMI_SHMEM |
||||
bool "SCMI shared memory (SHMEM) driver" |
||||
default y |
||||
depends on DT_HAS_ARM_SCMI_SHMEM_ENABLED |
||||
help |
||||
Enable support for SCMI shared memory (SHMEM) driver. |
||||
|
||||
config ARM_SCMI_SHMEM_INIT_PRIORITY |
||||
int "SCMI shared memory (SHMEM) initialization priority" |
||||
default 15 |
||||
help |
||||
SCMI SHMEM driver device initialization priority. |
||||
|
||||
config ARM_SCMI_TRANSPORT_HAS_STATIC_CHANNELS |
||||
bool "Transport layer has static channels" |
||||
help |
||||
Enable this if the SCMI transport layer uses static channels. |
||||
What this means is that each protocol will have its channels |
||||
assigned at compile time. This option is recommended for |
||||
transport layer drivers which can use the default channel |
||||
allocation scheme (i.e: use protocol-specific channels if |
||||
they exist, otherwise use base protocol channels). |
||||
|
||||
config ARM_SCMI_TRANSPORT_INIT_PRIORITY |
||||
int "SCMI transport layer initialization priority" |
||||
default 20 |
||||
help |
||||
SCMI transport driver device initialization priority. |
||||
|
||||
endif # ARM_SCMI |
@ -0,0 +1,214 @@
@@ -0,0 +1,214 @@
|
||||
/*
|
||||
* Copyright 2024 NXP |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
|
||||
#include <zephyr/drivers/firmware/scmi/protocol.h> |
||||
#include <zephyr/drivers/firmware/scmi/transport.h> |
||||
#include <zephyr/logging/log.h> |
||||
#include <zephyr/device.h> |
||||
|
||||
LOG_MODULE_REGISTER(scmi_core); |
||||
|
||||
#define SCMI_CHAN_LOCK_TIMEOUT_USEC 500 |
||||
#define SCMI_CHAN_SEM_TIMEOUT_USEC 500 |
||||
|
||||
int scmi_status_to_errno(int scmi_status) |
||||
{ |
||||
switch (scmi_status) { |
||||
case SCMI_SUCCESS: |
||||
return 0; |
||||
case SCMI_NOT_SUPPORTED: |
||||
return -EOPNOTSUPP; |
||||
case SCMI_INVALID_PARAMETERS: |
||||
return -EINVAL; |
||||
case SCMI_DENIED: |
||||
return -EACCES; |
||||
case SCMI_NOT_FOUND: |
||||
return -ENOENT; |
||||
case SCMI_OUT_OF_RANGE: |
||||
return -ERANGE; |
||||
case SCMI_IN_USE: |
||||
case SCMI_BUSY: |
||||
return -EBUSY; |
||||
case SCMI_PROTOCOL_ERROR: |
||||
return -EPROTO; |
||||
case SCMI_COMMS_ERROR: |
||||
case SCMI_GENERIC_ERROR: |
||||
case SCMI_HARDWARE_ERROR: |
||||
default: |
||||
return -EIO; |
||||
} |
||||
} |
||||
|
||||
static void scmi_core_reply_cb(struct scmi_channel *chan) |
||||
{ |
||||
if (!k_is_pre_kernel()) { |
||||
k_sem_give(&chan->sem); |
||||
} |
||||
} |
||||
|
||||
static int scmi_core_setup_chan(const struct device *transport, |
||||
struct scmi_channel *chan, bool tx) |
||||
{ |
||||
int ret; |
||||
|
||||
if (!chan) { |
||||
return -EINVAL; |
||||
} |
||||
|
||||
if (chan->ready) { |
||||
return 0; |
||||
} |
||||
|
||||
/* no support for RX channels ATM */ |
||||
if (!tx) { |
||||
return -ENOTSUP; |
||||
} |
||||
|
||||
k_mutex_init(&chan->lock); |
||||
k_sem_init(&chan->sem, 0, 1); |
||||
|
||||
chan->cb = scmi_core_reply_cb; |
||||
|
||||
/* setup transport-related channel data */ |
||||
ret = scmi_transport_setup_chan(transport, chan, tx); |
||||
if (ret < 0) { |
||||
LOG_ERR("failed to setup channel"); |
||||
return ret; |
||||
} |
||||
|
||||
/* protocols might share a channel. In such cases, this
|
||||
* will stop them from being initialized again. |
||||
*/ |
||||
chan->ready = true; |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int scmi_send_message_pre_kernel(struct scmi_protocol *proto, |
||||
struct scmi_message *msg, |
||||
struct scmi_message *reply) |
||||
{ |
||||
int ret; |
||||
|
||||
ret = scmi_transport_send_message(proto->transport, proto->tx, msg); |
||||
if (ret < 0) { |
||||
return ret; |
||||
} |
||||
|
||||
/* no kernel primitives, we're forced to poll here.
|
||||
* |
||||
* Cortex-M quirk: no interrupts at this point => no timer => |
||||
* no timeout mechanism => this can block the whole system. |
||||
* |
||||
* TODO: is there a better way to handle this? |
||||
*/ |
||||
while (!scmi_transport_channel_is_free(proto->transport, proto->tx)) { |
||||
} |
||||
|
||||
ret = scmi_transport_read_message(proto->transport, proto->tx, reply); |
||||
if (ret < 0) { |
||||
return ret; |
||||
} |
||||
|
||||
return ret; |
||||
} |
||||
|
||||
static int scmi_send_message_post_kernel(struct scmi_protocol *proto, |
||||
struct scmi_message *msg, |
||||
struct scmi_message *reply) |
||||
{ |
||||
int ret = 0; |
||||
|
||||
if (!proto->tx) { |
||||
return -ENODEV; |
||||
} |
||||
|
||||
/* wait for channel to be free */ |
||||
ret = k_mutex_lock(&proto->tx->lock, K_USEC(SCMI_CHAN_LOCK_TIMEOUT_USEC)); |
||||
if (ret < 0) { |
||||
LOG_ERR("failed to acquire chan lock"); |
||||
return ret; |
||||
} |
||||
|
||||
ret = scmi_transport_send_message(proto->transport, proto->tx, msg); |
||||
if (ret < 0) { |
||||
LOG_ERR("failed to send message"); |
||||
goto out_release_mutex; |
||||
} |
||||
|
||||
/* only one protocol instance can wait for a message reply at a time */ |
||||
ret = k_sem_take(&proto->tx->sem, K_USEC(SCMI_CHAN_SEM_TIMEOUT_USEC)); |
||||
if (ret < 0) { |
||||
LOG_ERR("failed to wait for msg reply"); |
||||
goto out_release_mutex; |
||||
} |
||||
|
||||
ret = scmi_transport_read_message(proto->transport, proto->tx, reply); |
||||
if (ret < 0) { |
||||
LOG_ERR("failed to read reply"); |
||||
goto out_release_mutex; |
||||
} |
||||
|
||||
out_release_mutex: |
||||
k_mutex_unlock(&proto->tx->lock); |
||||
|
||||
return ret; |
||||
} |
||||
|
||||
int scmi_send_message(struct scmi_protocol *proto, struct scmi_message *msg, |
||||
struct scmi_message *reply) |
||||
{ |
||||
if (!proto->tx) { |
||||
return -ENODEV; |
||||
} |
||||
|
||||
if (!proto->tx->ready) { |
||||
return -EINVAL; |
||||
} |
||||
|
||||
if (k_is_pre_kernel()) { |
||||
return scmi_send_message_pre_kernel(proto, msg, reply); |
||||
} else { |
||||
return scmi_send_message_post_kernel(proto, msg, reply); |
||||
} |
||||
} |
||||
|
||||
static int scmi_core_protocol_setup(const struct device *transport) |
||||
{ |
||||
int ret; |
||||
|
||||
STRUCT_SECTION_FOREACH(scmi_protocol, it) { |
||||
it->transport = transport; |
||||
|
||||
#ifndef CONFIG_ARM_SCMI_TRANSPORT_HAS_STATIC_CHANNELS |
||||
/* no static channel allocation, attempt dynamic binding */ |
||||
it->tx = scmi_transport_request_channel(transport, it->id, true); |
||||
#endif /* CONFIG_ARM_SCMI_TRANSPORT_HAS_STATIC_CHANNELS */ |
||||
|
||||
if (!it->tx) { |
||||
return -ENODEV; |
||||
} |
||||
|
||||
ret = scmi_core_setup_chan(transport, it->tx, true); |
||||
if (ret < 0) { |
||||
return ret; |
||||
} |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
int scmi_core_transport_init(const struct device *transport) |
||||
{ |
||||
int ret; |
||||
|
||||
ret = scmi_transport_init(transport); |
||||
if (ret < 0) { |
||||
return ret; |
||||
} |
||||
|
||||
return scmi_core_protocol_setup(transport); |
||||
} |
@ -0,0 +1,115 @@
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright 2024 NXP |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
|
||||
#include <zephyr/logging/log.h> |
||||
#include "mailbox.h" |
||||
|
||||
LOG_MODULE_REGISTER(scmi_mbox); |
||||
|
||||
static void scmi_mbox_cb(const struct device *mbox, |
||||
mbox_channel_id_t channel_id, |
||||
void *user_data, |
||||
struct mbox_msg *data) |
||||
{ |
||||
struct scmi_channel *scmi_chan = user_data; |
||||
|
||||
if (scmi_chan->cb) |
||||
scmi_chan->cb(scmi_chan); |
||||
} |
||||
|
||||
static int scmi_mbox_send_message(const struct device *transport, |
||||
struct scmi_channel *chan, |
||||
struct scmi_message *msg) |
||||
{ |
||||
struct scmi_mbox_channel *mbox_chan; |
||||
int ret; |
||||
|
||||
mbox_chan = chan->data; |
||||
|
||||
ret = scmi_shmem_write_message(mbox_chan->shmem, msg); |
||||
if (ret < 0) { |
||||
LOG_ERR("failed to write message to shmem: %d", ret); |
||||
return ret; |
||||
} |
||||
|
||||
ret = mbox_send_dt(&mbox_chan->tx, NULL); |
||||
if (ret < 0) { |
||||
LOG_ERR("failed to ring doorbell: %d", ret); |
||||
return ret; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int scmi_mbox_read_message(const struct device *transport, |
||||
struct scmi_channel *chan, |
||||
struct scmi_message *msg) |
||||
{ |
||||
struct scmi_mbox_channel *mbox_chan; |
||||
|
||||
mbox_chan = chan->data; |
||||
|
||||
return scmi_shmem_read_message(mbox_chan->shmem, msg); |
||||
} |
||||
|
||||
static bool scmi_mbox_channel_is_free(const struct device *transport, |
||||
struct scmi_channel *chan) |
||||
{ |
||||
struct scmi_mbox_channel *mbox_chan = chan->data; |
||||
|
||||
return scmi_shmem_channel_status(mbox_chan->shmem) & |
||||
SCMI_SHMEM_CHAN_STATUS_BUSY_BIT; |
||||
} |
||||
|
||||
static int scmi_mbox_setup_chan(const struct device *transport, |
||||
struct scmi_channel *chan, |
||||
bool tx) |
||||
{ |
||||
int ret; |
||||
struct scmi_mbox_channel *mbox_chan; |
||||
struct mbox_dt_spec *tx_reply; |
||||
|
||||
mbox_chan = chan->data; |
||||
|
||||
if (!tx) { |
||||
return -ENOTSUP; |
||||
} |
||||
|
||||
if (mbox_chan->tx_reply.dev) { |
||||
tx_reply = &mbox_chan->tx_reply; |
||||
} else { |
||||
tx_reply = &mbox_chan->tx; |
||||
} |
||||
|
||||
ret = mbox_register_callback_dt(tx_reply, scmi_mbox_cb, chan); |
||||
if (ret < 0) { |
||||
LOG_ERR("failed to register tx reply cb"); |
||||
return ret; |
||||
} |
||||
|
||||
ret = mbox_set_enabled_dt(tx_reply, true); |
||||
if (ret < 0) { |
||||
LOG_ERR("failed to enable tx reply dbell"); |
||||
} |
||||
|
||||
/* enable interrupt-based communication */ |
||||
scmi_shmem_update_flags(mbox_chan->shmem, |
||||
SCMI_SHMEM_CHAN_FLAG_IRQ_BIT, |
||||
SCMI_SHMEM_CHAN_FLAG_IRQ_BIT); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static struct scmi_transport_api scmi_mbox_api = { |
||||
.setup_chan = scmi_mbox_setup_chan, |
||||
.send_message = scmi_mbox_send_message, |
||||
.read_message = scmi_mbox_read_message, |
||||
.channel_is_free = scmi_mbox_channel_is_free, |
||||
}; |
||||
|
||||
DT_INST_SCMI_MAILBOX_DEFINE(0, PRE_KERNEL_1, |
||||
CONFIG_ARM_SCMI_TRANSPORT_INIT_PRIORITY, |
||||
&scmi_mbox_api); |
@ -0,0 +1,117 @@
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright 2024 NXP |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
|
||||
#ifndef _ZEPHYR_DRIVERS_FIRMWARE_SCMI_MAILBOX_H_ |
||||
#define _ZEPHYR_DRIVERS_FIRMWARE_SCMI_MAILBOX_H_ |
||||
|
||||
#include <zephyr/drivers/firmware/scmi/transport.h> |
||||
#include <zephyr/drivers/firmware/scmi/util.h> |
||||
#include <zephyr/drivers/firmware/scmi/shmem.h> |
||||
#include <zephyr/drivers/mbox.h> |
||||
#include <zephyr/kernel.h> |
||||
|
||||
#define DT_DRV_COMPAT arm_scmi |
||||
|
||||
/* get a `struct device` for a protocol's shared memory area */ |
||||
#define _SCMI_MBOX_SHMEM_BY_IDX(node_id, idx) \ |
||||
COND_CODE_1(DT_PROP_HAS_IDX(node_id, shmem, idx), \ |
||||
(DEVICE_DT_GET(DT_PROP_BY_IDX(node_id, shmem, idx))), \ |
||||
(NULL)) |
||||
|
||||
/* get the name of mailbox channel's private data */ |
||||
#define _SCMI_MBOX_CHAN_NAME(proto, idx)\ |
||||
CONCAT(SCMI_TRANSPORT_CHAN_NAME(proto, idx), _, priv) |
||||
|
||||
/* fetch a mailbox channel's doorbell */ |
||||
#define _SCMI_MBOX_CHAN_DBELL(node_id, name) \ |
||||
COND_CODE_1(DT_PROP_HAS_NAME(node_id, mboxes, name), \ |
||||
(MBOX_DT_SPEC_GET(node_id, name)), \ |
||||
({ })) |
||||
|
||||
/* define private data for a protocol TX channel */ |
||||
#define _SCMI_MBOX_CHAN_DEFINE_PRIV_TX(node_id, proto) \ |
||||
static struct scmi_mbox_channel _SCMI_MBOX_CHAN_NAME(proto, 0) =\ |
||||
{ \ |
||||
.shmem = _SCMI_MBOX_SHMEM_BY_IDX(node_id, 0), \ |
||||
.tx = _SCMI_MBOX_CHAN_DBELL(node_id, tx), \ |
||||
.tx_reply = _SCMI_MBOX_CHAN_DBELL(node_id, tx_reply), \ |
||||
} |
||||
|
||||
/*
|
||||
* Define a mailbox channel. This does two things: |
||||
* 1) Define the mandatory `struct scmi_channel` structure |
||||
* 2) Define the mailbox-specific private data for said |
||||
* channel (i.e: a struct scmi_mbox_channel) |
||||
*/ |
||||
#define _SCMI_MBOX_CHAN_DEFINE(node_id, proto, idx) \ |
||||
_SCMI_MBOX_CHAN_DEFINE_PRIV_TX(node_id, proto); \ |
||||
DT_SCMI_TRANSPORT_CHAN_DEFINE(node_id, idx, proto, \ |
||||
&(_SCMI_MBOX_CHAN_NAME(proto, idx))); \ |
||||
|
||||
/*
|
||||
* Optionally define a mailbox channel for a protocol. This is optional |
||||
* because a protocol might not have a dedicated channel. |
||||
*/ |
||||
#define _SCMI_MBOX_CHAN_DEFINE_OPTIONAL(node_id, proto, idx) \ |
||||
COND_CODE_1(DT_PROP_HAS_IDX(node_id, shmem, idx), \ |
||||
(_SCMI_MBOX_CHAN_DEFINE(node_id, proto, idx)), \ |
||||
()) |
||||
|
||||
/* define a TX channel for a protocol node. This is preferred over
|
||||
* _SCMI_MBOX_CHAN_DEFINE_OPTIONAL() since support for RX channels |
||||
* might be added later on. This macro is supposed to also define |
||||
* the RX channel |
||||
*/ |
||||
#define SCMI_MBOX_PROTO_CHAN_DEFINE(node_id)\ |
||||
_SCMI_MBOX_CHAN_DEFINE_OPTIONAL(node_id, DT_REG_ADDR(node_id), 0) |
||||
|
||||
/* define and validate base protocol TX channel */ |
||||
#define DT_INST_SCMI_MBOX_BASE_CHAN_DEFINE(inst) \ |
||||
BUILD_ASSERT(DT_INST_PROP_LEN(inst, mboxes) != 1 || \ |
||||
(DT_INST_PROP_HAS_IDX(inst, shmem, 0) && \ |
||||
DT_INST_PROP_HAS_NAME(inst, mboxes, tx)), \ |
||||
"bad bidirectional channel description"); \ |
||||
\ |
||||
BUILD_ASSERT(DT_INST_PROP_LEN(inst, mboxes) != 2 || \ |
||||
(DT_INST_PROP_HAS_NAME(inst, mboxes, tx) && \ |
||||
DT_INST_PROP_HAS_NAME(inst, mboxes, tx_reply)), \ |
||||
"bad unidirectional channel description"); \ |
||||
\ |
||||
BUILD_ASSERT(DT_INST_PROP_LEN(inst, shmem) == 1, \ |
||||
"bad SHMEM count"); \ |
||||
\ |
||||
BUILD_ASSERT(DT_INST_PROP_LEN(inst, mboxes) <= 2, \ |
||||
"bad mbox count"); \ |
||||
\ |
||||
_SCMI_MBOX_CHAN_DEFINE(DT_INST(inst, DT_DRV_COMPAT), SCMI_PROTOCOL_BASE, 0) |
||||
|
||||
/*
|
||||
* Define the mailbox-based transport layer. What this does is: |
||||
* |
||||
* 1) Goes through all protocol nodes (children of the `scmi` node) |
||||
* and creates a `struct scmi_channel` and its associated |
||||
* `struct scmi_mbox_channel` if the protocol has a dedicated channel. |
||||
* |
||||
* 2) Creates aforementioned structures for the base protocol |
||||
* (identified by the `scmi` node) |
||||
* |
||||
* 3) "registers" the driver via `DT_INST_SCMI_TRANSPORT_DEFINE()`. |
||||
*/ |
||||
#define DT_INST_SCMI_MAILBOX_DEFINE(inst, level, prio, api) \ |
||||
DT_INST_FOREACH_CHILD_STATUS_OKAY(inst, SCMI_MBOX_PROTO_CHAN_DEFINE) \ |
||||
DT_INST_SCMI_MBOX_BASE_CHAN_DEFINE(inst) \ |
||||
DT_INST_SCMI_TRANSPORT_DEFINE(inst, NULL, NULL, NULL, level, prio, api) |
||||
|
||||
struct scmi_mbox_channel { |
||||
/* SHMEM area bound to the channel */ |
||||
const struct device *shmem; |
||||
/* TX dbell */ |
||||
struct mbox_dt_spec tx; |
||||
/* TX reply dbell */ |
||||
struct mbox_dt_spec tx_reply; |
||||
}; |
||||
|
||||
#endif /* _ZEPHYR_DRIVERS_FIRMWARE_SCMI_MAILBOX_H_ */ |
@ -0,0 +1,201 @@
@@ -0,0 +1,201 @@
|
||||
/*
|
||||
* Copyright 2024 NXP |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
|
||||
#include <zephyr/drivers/firmware/scmi/shmem.h> |
||||
#include <zephyr/drivers/firmware/scmi/protocol.h> |
||||
#include <zephyr/logging/log.h> |
||||
#include <string.h> |
||||
|
||||
LOG_MODULE_REGISTER(arm_scmi_shmem); |
||||
|
||||
#define DT_DRV_COMPAT arm_scmi_shmem |
||||
|
||||
#ifndef DEVICE_MMIO_IS_IN_RAM |
||||
#define device_map(virt, phys, size, flags) *(virt) = (phys) |
||||
#endif /* DEVICE_MMIO_IS_IN_RAM */ |
||||
|
||||
struct scmi_shmem_config { |
||||
uintptr_t phys_addr; |
||||
uint32_t size; |
||||
}; |
||||
|
||||
struct scmi_shmem_data { |
||||
mm_reg_t regmap; |
||||
}; |
||||
|
||||
struct scmi_shmem_layout { |
||||
volatile uint32_t res0; |
||||
volatile uint32_t chan_status; |
||||
volatile uint32_t res1[2]; |
||||
volatile uint32_t chan_flags; |
||||
volatile uint32_t len; |
||||
volatile uint32_t msg_hdr; |
||||
}; |
||||
|
||||
int scmi_shmem_get_channel_status(const struct device *dev, uint32_t *status) |
||||
{ |
||||
struct scmi_shmem_data *data; |
||||
struct scmi_shmem_layout *layout; |
||||
|
||||
data = dev->data; |
||||
layout = (struct scmi_shmem_layout *)data->regmap; |
||||
|
||||
*status = layout->chan_status; |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static void scmi_shmem_memcpy(mm_reg_t dst, mm_reg_t src, uint32_t bytes) |
||||
{ |
||||
int i; |
||||
|
||||
for (i = 0; i < bytes; i++) { |
||||
sys_write8(*(uint8_t *)(src + i), dst + i); |
||||
} |
||||
} |
||||
|
||||
int scmi_shmem_read_message(const struct device *shmem, struct scmi_message *msg) |
||||
{ |
||||
struct scmi_shmem_layout *layout; |
||||
struct scmi_shmem_data *data; |
||||
const struct scmi_shmem_config *cfg; |
||||
|
||||
data = shmem->data; |
||||
cfg = shmem->config; |
||||
layout = (struct scmi_shmem_layout *)data->regmap; |
||||
|
||||
/* some sanity checks first */ |
||||
if (!msg) { |
||||
return -EINVAL; |
||||
} |
||||
|
||||
if (!msg->content && msg->len) { |
||||
return -EINVAL; |
||||
} |
||||
|
||||
if (cfg->size < (sizeof(*layout) + msg->len)) { |
||||
LOG_ERR("message doesn't fit in shmem area"); |
||||
return -EINVAL; |
||||
} |
||||
|
||||
/* mismatch between expected reply size and actual size? */ |
||||
if (msg->len != (layout->len - sizeof(layout->msg_hdr))) { |
||||
LOG_ERR("bad message len. Expected 0x%x, got 0x%x", |
||||
msg->len, |
||||
(uint32_t)(layout->len - sizeof(layout->msg_hdr))); |
||||
return -EINVAL; |
||||
} |
||||
|
||||
/* header match? */ |
||||
if (layout->msg_hdr != msg->hdr) { |
||||
LOG_ERR("bad message header. Expected 0x%x, got 0x%x", |
||||
msg->hdr, layout->msg_hdr); |
||||
return -EINVAL; |
||||
} |
||||
|
||||
if (msg->content) { |
||||
scmi_shmem_memcpy(POINTER_TO_UINT(msg->content), |
||||
data->regmap + sizeof(*layout), msg->len); |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
int scmi_shmem_write_message(const struct device *shmem, struct scmi_message *msg) |
||||
{ |
||||
struct scmi_shmem_layout *layout; |
||||
struct scmi_shmem_data *data; |
||||
const struct scmi_shmem_config *cfg; |
||||
|
||||
data = shmem->data; |
||||
cfg = shmem->config; |
||||
layout = (struct scmi_shmem_layout *)data->regmap; |
||||
|
||||
/* some sanity checks first */ |
||||
if (!msg) { |
||||
return -EINVAL; |
||||
} |
||||
|
||||
if (!msg->content && msg->len) { |
||||
return -EINVAL; |
||||
} |
||||
|
||||
if (cfg->size < (sizeof(*layout) + msg->len)) { |
||||
return -EINVAL; |
||||
} |
||||
|
||||
if (!(layout->chan_status & SCMI_SHMEM_CHAN_STATUS_BUSY_BIT)) { |
||||
return -EBUSY; |
||||
} |
||||
|
||||
layout->len = sizeof(layout->msg_hdr) + msg->len; |
||||
layout->msg_hdr = msg->hdr; |
||||
|
||||
if (msg->content) { |
||||
scmi_shmem_memcpy(data->regmap + sizeof(*layout), |
||||
POINTER_TO_UINT(msg->content), msg->len); |
||||
} |
||||
|
||||
/* done, mark channel as busy and proceed */ |
||||
layout->chan_status &= ~SCMI_SHMEM_CHAN_STATUS_BUSY_BIT; |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
uint32_t scmi_shmem_channel_status(const struct device *shmem) |
||||
{ |
||||
struct scmi_shmem_layout *layout; |
||||
struct scmi_shmem_data *data; |
||||
|
||||
data = shmem->data; |
||||
layout = (struct scmi_shmem_layout *)data->regmap; |
||||
|
||||
return layout->chan_status; |
||||
} |
||||
|
||||
void scmi_shmem_update_flags(const struct device *shmem, uint32_t mask, uint32_t val) |
||||
{ |
||||
struct scmi_shmem_layout *layout; |
||||
struct scmi_shmem_data *data; |
||||
|
||||
data = shmem->data; |
||||
layout = (struct scmi_shmem_layout *)data->regmap; |
||||
|
||||
layout->chan_flags = (layout->chan_flags & ~mask) | (val & mask); |
||||
} |
||||
|
||||
static int scmi_shmem_init(const struct device *dev) |
||||
{ |
||||
const struct scmi_shmem_config *cfg; |
||||
struct scmi_shmem_data *data; |
||||
|
||||
cfg = dev->config; |
||||
data = dev->data; |
||||
|
||||
if (cfg->size < sizeof(struct scmi_shmem_layout)) { |
||||
return -EINVAL; |
||||
} |
||||
|
||||
device_map(&data->regmap, cfg->phys_addr, cfg->size, K_MEM_CACHE_NONE); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
#define SCMI_SHMEM_INIT(inst) \ |
||||
static const struct scmi_shmem_config config_##inst = { \ |
||||
.phys_addr = DT_INST_REG_ADDR(inst), \ |
||||
.size = DT_INST_REG_SIZE(inst), \ |
||||
}; \ |
||||
\ |
||||
static struct scmi_shmem_data data_##inst; \ |
||||
\ |
||||
DEVICE_DT_INST_DEFINE(inst, &scmi_shmem_init, NULL, \ |
||||
&data_##inst, &config_##inst, \ |
||||
PRE_KERNEL_1, \ |
||||
CONFIG_ARM_SCMI_SHMEM_INIT_PRIORITY, \ |
||||
NULL); |
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(SCMI_SHMEM_INIT); |
@ -0,0 +1,12 @@
@@ -0,0 +1,12 @@
|
||||
# Copyright 2024 NXP |
||||
# SPDX-License-Identifier: Apache-2.0 |
||||
|
||||
description: System Control and Management Interface (SCMI) shared memory (SHMEM) |
||||
|
||||
compatible: "arm,scmi-shmem" |
||||
|
||||
include: [base.yaml] |
||||
|
||||
properties: |
||||
reg: |
||||
required: true |
@ -0,0 +1,68 @@
@@ -0,0 +1,68 @@
|
||||
# Copyright 2024 NXP |
||||
# SPDX-License-Identifier: Apache-2.0 |
||||
|
||||
description: | |
||||
System Control and Management Interface (SCMI) with doorbell |
||||
and shared memory (SHMEM) transport. |
||||
|
||||
Devicetree example: |
||||
#include <mem.h> |
||||
|
||||
scmi_res0: memory@44611000 { |
||||
compatible = "arm,scmi-shmem"; |
||||
reg = <0x44611000 0x80>; |
||||
}; |
||||
|
||||
mu5: mailbox@44610000 { |
||||
compatible = "nxp,mbox-imx-mu"; |
||||
reg = <0x44610000 DT_SIZE_K(4)>; |
||||
interrupts = <205 0>; |
||||
#mbox-cells = <1>; |
||||
}; |
||||
|
||||
scmi { |
||||
compatible = "arm,scmi"; |
||||
shmem = <&scmi_res0>; |
||||
mboxes = <&mu5 0>; |
||||
mbox-names = "tx"; |
||||
|
||||
protocol@14 { |
||||
compatible = "arm,scmi-clock"; |
||||
reg = <0x14>; |
||||
#clock-cells = <1>; |
||||
}; |
||||
|
||||
protocol@19 { |
||||
compatible = "arm,scmi-pinctrl"; |
||||
reg = <0x19>; |
||||
|
||||
pinctrl { |
||||
compatible = "nxp,imx95-pinctrl", "nxp,imx93-pinctrl"; |
||||
}; |
||||
}; |
||||
}; |
||||
|
||||
compatible: "arm,scmi" |
||||
|
||||
include: [base.yaml] |
||||
|
||||
properties: |
||||
shmem: |
||||
type: phandle |
||||
required: true |
||||
description: | |
||||
Phandle to node describing TX channel shared memory area. |
||||
This translates to a **single** SCMI transmit channel. |
||||
|
||||
mboxes: |
||||
required: true |
||||
description: | |
||||
List of mailbox channel specifiers. It should contain one or two specifiers: |
||||
1) tx - 1 mbox / 1 shmem (platform and agent use the same |
||||
mailbox channel for signaling) |
||||
2) tx_reply - 2 mbox / 1 shmem (platform and agent use different |
||||
mailbox channels for signaling) |
||||
|
||||
mbox-names: |
||||
required: true |
||||
description: mailbox channel specifier names |
@ -0,0 +1,122 @@
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Copyright 2024 NXP |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
|
||||
/**
|
||||
* @file |
||||
* @brief SCMI protocol generic functions and structures |
||||
*/ |
||||
|
||||
#ifndef _INCLUDE_ZEPHYR_DRIVERS_FIRMWARE_SCMI_PROTOCOL_H_ |
||||
#define _INCLUDE_ZEPHYR_DRIVERS_FIRMWARE_SCMI_PROTOCOL_H_ |
||||
|
||||
#include <zephyr/device.h> |
||||
#include <zephyr/drivers/firmware/scmi/util.h> |
||||
#include <stdint.h> |
||||
#include <errno.h> |
||||
|
||||
/**
|
||||
* @brief Build an SCMI message header |
||||
* |
||||
* Builds an SCMI message header based on the |
||||
* fields that make it up. |
||||
* |
||||
* @param id message ID |
||||
* @param type message type |
||||
* @param proto protocol ID |
||||
* @param token message token |
||||
*/ |
||||
#define SCMI_MESSAGE_HDR_MAKE(id, type, proto, token) \ |
||||
(SCMI_FIELD_MAKE(id, GENMASK(7, 0), 0) | \ |
||||
SCMI_FIELD_MAKE(type, GENMASK(1, 0), 8) | \ |
||||
SCMI_FIELD_MAKE(proto, GENMASK(7, 0), 10) | \ |
||||
SCMI_FIELD_MAKE(token, GENMASK(9, 0), 18)) |
||||
|
||||
struct scmi_channel; |
||||
|
||||
/**
|
||||
* @brief SCMI message type |
||||
*/ |
||||
enum scmi_message_type { |
||||
/** command message */ |
||||
SCMI_COMMAND = 0x0, |
||||
/** delayed reply message */ |
||||
SCMI_DELAYED_REPLY = 0x2, |
||||
/** notification message */ |
||||
SCMI_NOTIFICATION = 0x3, |
||||
}; |
||||
|
||||
/**
|
||||
* @brief SCMI status codes |
||||
*/ |
||||
enum scmi_status_code { |
||||
SCMI_SUCCESS = 0, |
||||
SCMI_NOT_SUPPORTED = -1, |
||||
SCMI_INVALID_PARAMETERS = -2, |
||||
SCMI_DENIED = -3, |
||||
SCMI_NOT_FOUND = -4, |
||||
SCMI_OUT_OF_RANGE = -5, |
||||
SCMI_BUSY = -6, |
||||
SCMI_COMMS_ERROR = -7, |
||||
SCMI_GENERIC_ERROR = -8, |
||||
SCMI_HARDWARE_ERROR = -9, |
||||
SCMI_PROTOCOL_ERROR = -10, |
||||
SCMI_IN_USE = -11, |
||||
}; |
||||
|
||||
/**
|
||||
* @struct scmi_protocol |
||||
* |
||||
* @brief SCMI protocol structure |
||||
*/ |
||||
struct scmi_protocol { |
||||
/** protocol ID */ |
||||
uint32_t id; |
||||
/** TX channel */ |
||||
struct scmi_channel *tx; |
||||
/** transport layer device */ |
||||
const struct device *transport; |
||||
/** protocol private data */ |
||||
void *data; |
||||
}; |
||||
|
||||
/**
|
||||
* @struct scmi_message |
||||
* |
||||
* @brief SCMI message structure |
||||
*/ |
||||
struct scmi_message { |
||||
uint32_t hdr; |
||||
uint32_t len; |
||||
void *content; |
||||
}; |
||||
|
||||
/**
|
||||
* @brief Convert an SCMI status code to its Linux equivalent (if possible) |
||||
* |
||||
* @param scmi_status SCMI status code as shown in `enum scmi_status_code` |
||||
* |
||||
* @retval Linux equivalent status code |
||||
*/ |
||||
int scmi_status_to_errno(int scmi_status); |
||||
|
||||
/**
|
||||
* @brief Send an SCMI message and wait for its reply |
||||
* |
||||
* Blocking function used to send an SCMI message over |
||||
* a given channel and wait for its reply |
||||
* |
||||
* @param proto pointer to SCMI protocol |
||||
* @param msg pointer to SCMI message to send |
||||
* @param reply pointer to SCMI message in which the reply is to be |
||||
* written |
||||
* |
||||
* @retval 0 if successful |
||||
* @retval negative errno if failure |
||||
*/ |
||||
int scmi_send_message(struct scmi_protocol *proto, |
||||
struct scmi_message *msg, struct scmi_message *reply); |
||||
|
||||
#endif /* _INCLUDE_ZEPHYR_DRIVERS_FIRMWARE_SCMI_PROTOCOL_H_ */ |
@ -0,0 +1,67 @@
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright 2024 NXP |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
|
||||
/**
|
||||
* @file |
||||
* @brief SCMI SHMEM API |
||||
*/ |
||||
|
||||
#ifndef _INCLUDE_ZEPHYR_DRIVERS_FIRMWARE_SCMI_SHMEM_H_ |
||||
#define _INCLUDE_ZEPHYR_DRIVERS_FIRMWARE_SCMI_SHMEM_H_ |
||||
|
||||
#include <zephyr/device.h> |
||||
#include <zephyr/arch/cpu.h> |
||||
#include <errno.h> |
||||
|
||||
#define SCMI_SHMEM_CHAN_STATUS_BUSY_BIT BIT(0) |
||||
#define SCMI_SHMEM_CHAN_FLAG_IRQ_BIT BIT(0) |
||||
|
||||
struct scmi_message; |
||||
|
||||
/**
|
||||
* @brief Write a message in the SHMEM area |
||||
* |
||||
* @param shmem pointer to shmem device |
||||
* @param msg message to write |
||||
* |
||||
* @retval 0 if successful |
||||
* @retval negative errno if failure |
||||
*/ |
||||
int scmi_shmem_write_message(const struct device *shmem, |
||||
struct scmi_message *msg); |
||||
|
||||
/**
|
||||
* @brief Read a message from a SHMEM area |
||||
* |
||||
* @param shmem pointer to shmem device |
||||
* @param msg message to write the data into |
||||
* |
||||
* @retval 0 if successful |
||||
* @retval negative errno if failure |
||||
*/ |
||||
int scmi_shmem_read_message(const struct device *shmem, |
||||
struct scmi_message *msg); |
||||
|
||||
/**
|
||||
* @brief Update the channel flags |
||||
* |
||||
* @param shmem pointer to shmem device |
||||
* @param mask value to negate and bitwise-and the old |
||||
* channel flags value |
||||
* @param val value to bitwise and with the mask and |
||||
* bitwise-or with the masked old value |
||||
*/ |
||||
void scmi_shmem_update_flags(const struct device *shmem, |
||||
uint32_t mask, uint32_t val); |
||||
|
||||
/**
|
||||
* @brief Read a channel's status |
||||
* |
||||
* @param shmem pointer to shmem device |
||||
*/ |
||||
uint32_t scmi_shmem_channel_status(const struct device *shmem); |
||||
|
||||
#endif /* _INCLUDE_ZEPHYR_DRIVERS_FIRMWARE_SCMI_SHMEM_H_ */ |
@ -0,0 +1,274 @@
@@ -0,0 +1,274 @@
|
||||
/*
|
||||
* Copyright 2024 NXP |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
|
||||
/**
|
||||
* @file |
||||
* @brief Public APIs for the SCMI transport layer drivers |
||||
*/ |
||||
|
||||
#ifndef _INCLUDE_ZEPHYR_DRIVERS_FIRMWARE_SCMI_TRANSPORT_H_ |
||||
#define _INCLUDE_ZEPHYR_DRIVERS_FIRMWARE_SCMI_TRANSPORT_H_ |
||||
|
||||
#include <zephyr/device.h> |
||||
#include <zephyr/sys/mutex.h> |
||||
|
||||
struct scmi_message; |
||||
struct scmi_channel; |
||||
|
||||
/**
|
||||
* @typedef scmi_channel_cb |
||||
* |
||||
* @brief Callback function for message replies |
||||
* |
||||
* This function should be called by the transport layer |
||||
* driver whenever a reply to a previously sent message |
||||
* has been received. Its purpose is to notifying the SCMI |
||||
* core of the reply's arrival so that proper action can |
||||
* be taken. |
||||
* |
||||
* @param chan pointer to SCMI channel on which the reply |
||||
* arrived |
||||
*/ |
||||
typedef void (*scmi_channel_cb)(struct scmi_channel *chan); |
||||
|
||||
/**
|
||||
* @struct scmi_channel |
||||
* @brief SCMI channel structure |
||||
* |
||||
* An SCMI channel is a medium through which a protocol |
||||
* is able to transmit/receive messages. Each of the SCMI |
||||
* channels is represented by a `struct scmi_channel`. |
||||
*/ |
||||
struct scmi_channel { |
||||
/**
|
||||
* channel lock. This is meant to be initialized |
||||
* and used only by the SCMI core to assure that |
||||
* only one protocol can send/receive messages |
||||
* through a channel at a given moment. |
||||
*/ |
||||
struct k_mutex lock; |
||||
/**
|
||||
* binary semaphore. This is meant to be initialized |
||||
* and used only by the SCMI core. Its purpose is to |
||||
* signal that a reply has been received. |
||||
*/ |
||||
struct k_sem sem; |
||||
/** channel private data */ |
||||
void *data; |
||||
/**
|
||||
* callback function. This is meant to be set by |
||||
* the SCMI core and should be called by the SCMI |
||||
* transport layer driver whenever a reply has |
||||
* been received. |
||||
*/ |
||||
scmi_channel_cb cb; |
||||
/** is the channel ready to be used by a protocol? */ |
||||
bool ready; |
||||
}; |
||||
|
||||
struct scmi_transport_api { |
||||
int (*init)(const struct device *transport); |
||||
int (*send_message)(const struct device *transport, |
||||
struct scmi_channel *chan, |
||||
struct scmi_message *msg); |
||||
int (*setup_chan)(const struct device *transport, |
||||
struct scmi_channel *chan, |
||||
bool tx); |
||||
int (*read_message)(const struct device *transport, |
||||
struct scmi_channel *chan, |
||||
struct scmi_message *msg); |
||||
bool (*channel_is_free)(const struct device *transport, |
||||
struct scmi_channel *chan); |
||||
struct scmi_channel *(*request_channel)(const struct device *transport, |
||||
uint32_t proto, bool tx); |
||||
}; |
||||
|
||||
/**
|
||||
* @brief Request an SCMI channel dynamically |
||||
* |
||||
* Whenever the SCMI transport layer driver doesn't support |
||||
* static channel allocation, the SCMI core will try to bind |
||||
* a channel to a protocol dynamically using this function. |
||||
* Note that no setup needs to be performed on the channel |
||||
* in this function as the core will also call the channel |
||||
* setup() function. |
||||
* |
||||
* @param transport pointer to the device structure for the |
||||
* transport layer |
||||
* @param proto ID of the protocol for which the core is |
||||
* requesting the channel |
||||
* @param tx true if the channel is TX, false if RX |
||||
* |
||||
* @retval pointer to SCMI channel that's to be bound |
||||
* to the protocol |
||||
* @retval NULL if operation was not successful |
||||
*/ |
||||
static inline struct scmi_channel * |
||||
scmi_transport_request_channel(const struct device *transport, |
||||
uint32_t proto, bool tx) |
||||
{ |
||||
const struct scmi_transport_api *api = |
||||
(const struct scmi_transport_api *)transport->api; |
||||
|
||||
if (api->request_channel) { |
||||
return api->request_channel(transport, proto, tx); |
||||
} |
||||
|
||||
return NULL; |
||||
} |
||||
|
||||
/**
|
||||
* @brief Perform initialization for the transport layer driver |
||||
* |
||||
* The transport layer driver can't be initialized directly |
||||
* (i.e via a call to its init() function) during system initialization. |
||||
* This is because the macro used to define an SCMI transport places |
||||
* `scmi_core_transport_init()` in the init section instead of the |
||||
* driver's init() function. As such, `scmi_core_transport_init()` |
||||
* needs to call this function to perfrom transport layer driver |
||||
* initialization if required. |
||||
* |
||||
* This operation is optional. |
||||
* |
||||
* @param transport pointer to the device structure for the |
||||
* transport layer |
||||
* |
||||
* @retval 0 if successful |
||||
* @retval negative errno code if failure |
||||
*/ |
||||
static inline int scmi_transport_init(const struct device *transport) |
||||
{ |
||||
const struct scmi_transport_api *api = |
||||
(const struct scmi_transport_api *)transport->api; |
||||
|
||||
if (api->init) { |
||||
return api->init(transport); |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
/**
|
||||
* @brief Setup an SCMI channel |
||||
* |
||||
* Before being able to send/receive messages, an SCMI channel needs |
||||
* to be prepared, which is what this function does. If it returns |
||||
* successfully, an SCMI protocol will be able to use this channel |
||||
* to send/receive messages. |
||||
* |
||||
* @param transport pointer to the device structure for the |
||||
* transport layer |
||||
* @param chan pointer to SCMI channel to be prepared |
||||
* @param tx true if the channel is TX, false if RX |
||||
* |
||||
* @retval 0 if successful |
||||
* @retval negative errno code if failure |
||||
*/ |
||||
static inline int scmi_transport_setup_chan(const struct device *transport, |
||||
struct scmi_channel *chan, |
||||
bool tx) |
||||
{ |
||||
const struct scmi_transport_api *api = |
||||
(const struct scmi_transport_api *)transport->api; |
||||
|
||||
if (!api || !api->setup_chan) { |
||||
return -ENOSYS; |
||||
} |
||||
|
||||
return api->setup_chan(transport, chan, tx); |
||||
} |
||||
|
||||
/**
|
||||
* @brief Send an SCMI channel |
||||
* |
||||
* Send an SCMI message using given SCMI channel. This function is |
||||
* not allowed to block. |
||||
* |
||||
* @param transport pointer to the device structure for the |
||||
* transport layer |
||||
* @param chan pointer to SCMI channel on which the message |
||||
* is to be sent |
||||
* @param msg pointer to message the caller wishes to send |
||||
* |
||||
* @retval 0 if successful |
||||
* @retval negative errno code if failure |
||||
*/ |
||||
static inline int scmi_transport_send_message(const struct device *transport, |
||||
struct scmi_channel *chan, |
||||
struct scmi_message *msg) |
||||
{ |
||||
const struct scmi_transport_api *api = |
||||
(const struct scmi_transport_api *)transport->api; |
||||
|
||||
if (!api || !api->send_message) { |
||||
return -ENOSYS; |
||||
} |
||||
|
||||
return api->send_message(transport, chan, msg); |
||||
} |
||||
|
||||
/**
|
||||
* @brief Read an SCMI message |
||||
* |
||||
* @param transport pointer to the device structure for the |
||||
* transport layer |
||||
* @param chan pointer to SCMI channel on which the message |
||||
* is to be read |
||||
* @param msg pointer to message the caller wishes to read |
||||
* |
||||
* @retval 0 if successful |
||||
* @retval negative errno code if failure |
||||
*/ |
||||
static inline int scmi_transport_read_message(const struct device *transport, |
||||
struct scmi_channel *chan, |
||||
struct scmi_message *msg) |
||||
{ |
||||
const struct scmi_transport_api *api = |
||||
(const struct scmi_transport_api *)transport->api; |
||||
|
||||
if (!api || !api->read_message) { |
||||
return -ENOSYS; |
||||
} |
||||
|
||||
return api->read_message(transport, chan, msg); |
||||
} |
||||
|
||||
/**
|
||||
* @brief Check if an SCMI channel is free |
||||
* |
||||
* @param transport pointer to the device structure for |
||||
* the transport layer |
||||
* @param chan pointer to SCMI channel the query is to be |
||||
* performed on |
||||
* |
||||
* @retval 0 if successful |
||||
* @retval negative errno code if failure |
||||
*/ |
||||
static inline bool scmi_transport_channel_is_free(const struct device *transport, |
||||
struct scmi_channel *chan) |
||||
{ |
||||
const struct scmi_transport_api *api = |
||||
(const struct scmi_transport_api *)transport->api; |
||||
|
||||
if (!api || !api->channel_is_free) { |
||||
return -ENOSYS; |
||||
} |
||||
|
||||
return api->channel_is_free(transport, chan); |
||||
} |
||||
|
||||
/**
|
||||
* @brief Perfrom SCMI core initialization |
||||
* |
||||
* @param transport pointer to the device structure for |
||||
* the transport layer |
||||
* |
||||
* @retval 0 if successful |
||||
* @retval negative errno code if failure |
||||
*/ |
||||
int scmi_core_transport_init(const struct device *transport); |
||||
|
||||
#endif /* _INCLUDE_ZEPHYR_DRIVERS_FIRMWARE_SCMI_TRANSPORT_H_ */ |
@ -0,0 +1,280 @@
@@ -0,0 +1,280 @@
|
||||
/*
|
||||
* Copyright 2024 NXP |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
|
||||
/**
|
||||
* @file |
||||
* @brief ARM SCMI utility header |
||||
* |
||||
* Contains various utility macros and macros used for protocol and |
||||
* transport "registration". |
||||
*/ |
||||
|
||||
#ifndef _INCLUDE_ZEPHYR_DRIVERS_FIRMWARE_SCMI_UTIL_H_ |
||||
#define _INCLUDE_ZEPHYR_DRIVERS_FIRMWARE_SCMI_UTIL_H_ |
||||
|
||||
/**
|
||||
* @brief Build protocol name from its ID |
||||
* |
||||
* Given a protocol ID, this macro builds the protocol |
||||
* name. This is done by concatenating the scmi_protocol_ |
||||
* construct with the given protocol ID. |
||||
* |
||||
* @param proto protocol ID in decimal format |
||||
* |
||||
* @return protocol name |
||||
*/ |
||||
#define SCMI_PROTOCOL_NAME(proto) CONCAT(scmi_protocol_, proto) |
||||
|
||||
#ifdef CONFIG_ARM_SCMI_TRANSPORT_HAS_STATIC_CHANNELS |
||||
|
||||
#ifdef CONFIG_ARM_SCMI_MAILBOX_TRANSPORT |
||||
/** @brief Check if a protocol node has an associated channel
|
||||
* |
||||
* This macro, when applied to a protocol node, checks if |
||||
* the node has a dedicated static channel allocated to it. |
||||
* This definition is specific to the mailbox driver and |
||||
* each new transport layer driver should define its own |
||||
* version of this macro based on the devicetree properties |
||||
* that indicate the presence of a dedicated channel. |
||||
* |
||||
* @param node_id protocol node identifier |
||||
* @idx channel index. Should be 0 for TX channels and 1 for |
||||
* RX channels |
||||
*/ |
||||
#define DT_SCMI_TRANSPORT_PROTO_HAS_CHAN(node_id, idx)\ |
||||
DT_PROP_HAS_IDX(node_id, shmem, idx) |
||||
#else /* CONFIG_ARM_SCMI_MAILBOX_TRANSPORT */ |
||||
#error "Transport with static channels needs to define HAS_CHAN macro" |
||||
#endif /* CONFIG_ARM_SCMI_MAILBOX_TRANSPORT */ |
||||
|
||||
#define SCMI_TRANSPORT_CHAN_NAME(proto, idx) CONCAT(scmi_channel_, proto, _, idx) |
||||
|
||||
/**
|
||||
* @brief Declare a TX SCMI channel |
||||
* |
||||
* Given a node_id for a protocol, this macro declares the SCMI |
||||
* TX channel statically bound to said protocol via the "extern" |
||||
* qualifier. This is useful when the transport layer driver |
||||
* supports static channels since all channel structures are |
||||
* defined inside the transport layer driver. |
||||
* |
||||
* @param node_id protocol node identifier |
||||
*/ |
||||
#define DT_SCMI_TRANSPORT_TX_CHAN_DECLARE(node_id) \ |
||||
COND_CODE_1(DT_SCMI_TRANSPORT_PROTO_HAS_CHAN(node_id, 0), \ |
||||
(extern struct scmi_channel \ |
||||
SCMI_TRANSPORT_CHAN_NAME(DT_REG_ADDR(node_id), 0);), \ |
||||
(extern struct scmi_channel \ |
||||
SCMI_TRANSPORT_CHAN_NAME(SCMI_PROTOCOL_BASE, 0);)) \ |
||||
|
||||
/**
|
||||
* @brief Declare SCMI TX/RX channels |
||||
* |
||||
* Given a node_id for a protocol, this macro declares the |
||||
* SCMI TX and RX channels statically bound to said protocol via |
||||
* the "extern" qualifier. Since RX channels are currently not |
||||
* supported, this is equivalent to DT_SCMI_TRANSPORT_TX_CHAN_DECLARE(). |
||||
* Despite this, users should opt for this macro instead of the TX-specific |
||||
* one. |
||||
* |
||||
* @param node_id protocol node identifier |
||||
*/ |
||||
#define DT_SCMI_TRANSPORT_CHANNELS_DECLARE(node_id) \ |
||||
DT_SCMI_TRANSPORT_TX_CHAN_DECLARE(node_id) \ |
||||
|
||||
/**
|
||||
* @brief Declare SCMI TX/RX channels using node instance number |
||||
* |
||||
* Same as DT_SCMI_TRANSPORT_CHANNELS_DECLARE() but uses the |
||||
* protocol's node instance number and the DT_DRV_COMPAT macro. |
||||
* |
||||
* @param protocol node instance number |
||||
*/ |
||||
#define DT_INST_SCMI_TRANSPORT_CHANNELS_DECLARE(inst) \ |
||||
DT_SCMI_TRANSPORT_CHANNELS_DECLARE(DT_INST(inst, DT_DRV_COMPAT)) |
||||
|
||||
/**
|
||||
* @brief Get a reference to a protocol's SCMI TX channel |
||||
* |
||||
* Given a node_id for a protocol, this macro returns a |
||||
* reference to an SCMI TX channel statically bound to said |
||||
* protocol. |
||||
* |
||||
* @param node_id protocol node identifier |
||||
* |
||||
* @return reference to the struct scmi_channel of the TX channel |
||||
* bound to the protocol identifier by node_id |
||||
*/ |
||||
#define DT_SCMI_TRANSPORT_TX_CHAN(node_id) \ |
||||
COND_CODE_1(DT_SCMI_TRANSPORT_PROTO_HAS_CHAN(node_id, 0), \ |
||||
(&SCMI_TRANSPORT_CHAN_NAME(DT_REG_ADDR(node_id), 0)), \ |
||||
(&SCMI_TRANSPORT_CHAN_NAME(SCMI_PROTOCOL_BASE, 0))) |
||||
|
||||
/**
|
||||
* @brief Define an SCMI channel for a protocol |
||||
* |
||||
* This macro defines a struct scmi_channel for a given protocol. |
||||
* This should be used by the transport layer driver to statically |
||||
* define SCMI channels for the protocols. |
||||
* |
||||
* @param node_id protocol node identifier |
||||
* @param idx channel index. Should be 0 for TX channels and 1 |
||||
* for RX channels |
||||
* @param proto protocol ID in decimal format |
||||
*/ |
||||
#define DT_SCMI_TRANSPORT_CHAN_DEFINE(node_id, idx, proto, pdata) \ |
||||
struct scmi_channel SCMI_TRANSPORT_CHAN_NAME(proto, idx) = \ |
||||
{ \ |
||||
.data = pdata, \ |
||||
} |
||||
|
||||
/**
|
||||
* @brief Define an SCMI protocol's data |
||||
* |
||||
* Each SCMI protocol is identified by a struct scmi_protocol |
||||
* placed in a linker section called scmi_protocol. Each protocol |
||||
* driver is required to use this macro for "registration". Using |
||||
* this macro directly is higly discouraged and users should opt |
||||
* for macros such as DT_SCMI_PROTOCOL_DEFINE_NODEV() or |
||||
* DT_SCMI_PROTOCOL_DEFINE(), which also takes care of the static |
||||
* channel declaration (if applicable). |
||||
* |
||||
* @param node_id protocol node identifier |
||||
* @param proto protocol ID in decimal format |
||||
* @param pdata protocol private data |
||||
*/ |
||||
#define DT_SCMI_PROTOCOL_DATA_DEFINE(node_id, proto, pdata) \ |
||||
STRUCT_SECTION_ITERABLE(scmi_protocol, SCMI_PROTOCOL_NAME(proto)) = \ |
||||
{ \ |
||||
.id = proto, \ |
||||
.tx = DT_SCMI_TRANSPORT_TX_CHAN(node_id), \ |
||||
.data = pdata, \ |
||||
} |
||||
|
||||
#else /* CONFIG_ARM_SCMI_TRANSPORT_HAS_STATIC_CHANNELS */ |
||||
|
||||
#define DT_SCMI_TRANSPORT_CHANNELS_DECLARE(node_id) |
||||
|
||||
#define DT_SCMI_PROTOCOL_DATA_DEFINE(node_id, proto, pdata) \ |
||||
STRUCT_SECTION_ITERABLE(scmi_protocol, SCMI_PROTOCOL_NAME(proto)) = \ |
||||
{ \ |
||||
.id = proto, \ |
||||
.data = pdata, \ |
||||
} |
||||
|
||||
#endif /* CONFIG_ARM_SCMI_TRANSPORT_HAS_STATIC_CHANNELS */ |
||||
|
||||
/**
|
||||
* @brief Define an SCMI transport driver |
||||
* |
||||
* This is merely a wrapper over DEVICE_DT_INST_DEFINE(), but is |
||||
* required since transport layer drivers are not allowed to place |
||||
* their own init() function in the init section. Instead, transport |
||||
* layer drivers place the scmi_core_transport_init() function in the |
||||
* init section, which, in turn, will call the transport layer driver |
||||
* init() function. This is required because the SCMI core needs to |
||||
* perform channel binding and setup during the transport layer driver's |
||||
* initialization. |
||||
*/ |
||||
#define DT_INST_SCMI_TRANSPORT_DEFINE(inst, pm, data, config, level, prio, api) \ |
||||
DEVICE_DT_INST_DEFINE(inst, &scmi_core_transport_init, \ |
||||
pm, data, config, level, prio, api) |
||||
|
||||
/**
|
||||
* @brief Define an SCMI protocol |
||||
* |
||||
* This macro performs three important functions: |
||||
* 1) It defines a `struct scmi_protocol`, which is |
||||
* needed by all protocol drivers to work with the SCMI API. |
||||
* |
||||
* 2) It declares the static channels bound to the protocol. |
||||
* This is only applicable if the transport layer driver |
||||
* supports static channels. |
||||
* |
||||
* 3) It creates a `struct device` a sets the `data` field |
||||
* to the newly defined `struct scmi_protocol`. This is |
||||
* needed because the protocol driver needs to work with the |
||||
* SCMI API **and** the subsystem API. |
||||
* |
||||
* @param node_id protocol node identifier |
||||
* @param init_fn pointer to protocol's initialization function |
||||
* @param api pointer to protocol's subsystem API |
||||
* @param pm pointer to the protocol's power management resources |
||||
* @param data pointer to protocol's private data |
||||
* @param config pointer to protocol's private constant data |
||||
* @param level protocol initialization level |
||||
* @param prio protocol's priority within its initialization level |
||||
*/ |
||||
#define DT_SCMI_PROTOCOL_DEFINE(node_id, init_fn, pm, data, config, \ |
||||
level, prio, api) \ |
||||
DT_SCMI_TRANSPORT_CHANNELS_DECLARE(node_id) \ |
||||
DT_SCMI_PROTOCOL_DATA_DEFINE(node_id, DT_REG_ADDR(node_id), data); \ |
||||
DEVICE_DT_DEFINE(node_id, init_fn, pm, \ |
||||
&SCMI_PROTOCOL_NAME(DT_REG_ADDR(node_id)), \ |
||||
config, level, prio, api) |
||||
|
||||
/**
|
||||
* @brief Just like DT_SCMI_PROTOCOL_DEFINE(), but uses an instance |
||||
* of a `DT_DRV_COMPAT` compatible instead of a node identifier |
||||
* |
||||
* @param inst instance number |
||||
* @param ... other parameters as expected by DT_SCMI_PROTOCOL_DEFINE() |
||||
*/ |
||||
#define DT_INST_SCMI_PROTOCOL_DEFINE(inst, init_fn, pm, data, config, \ |
||||
level, prio, api) \ |
||||
DT_SCMI_PROTOCOL_DEFINE(DT_INST(inst, DT_DRV_COMPAT), init_fn, pm, \ |
||||
data, config, level, prio, api) |
||||
|
||||
/**
|
||||
* @brief Define an SCMI protocol with no device |
||||
* |
||||
* Variant of DT_SCMI_PROTOCOL_DEFINE(), but no `struct device` is |
||||
* created and no initialization function is called during system |
||||
* initialization. This is useful for protocols that are not really |
||||
* part of a subsystem with an API (e.g: pinctrl). |
||||
* |
||||
* @param node_id protocol node identifier |
||||
* @param data protocol private data |
||||
*/ |
||||
#define DT_SCMI_PROTOCOL_DEFINE_NODEV(node_id, data) \ |
||||
DT_SCMI_TRANSPORT_CHANNELS_DECLARE(node_id) \ |
||||
DT_SCMI_PROTOCOL_DATA_DEFINE(node_id, DT_REG_ADDR(node_id), data) |
||||
|
||||
/**
|
||||
* @brief Create an SCMI message field |
||||
* |
||||
* Data might not necessarily be encoded in the first |
||||
* x bits of an SCMI message parameter/return value. |
||||
* This comes in handy when building said parameters/ |
||||
* return values. |
||||
* |
||||
* @param x value to encode |
||||
* @param mask value to perform bitwise-and with `x` |
||||
* @param shift value to left-shift masked `x` |
||||
*/ |
||||
#define SCMI_FIELD_MAKE(x, mask, shift)\ |
||||
(((uint32_t)(x) & (mask)) << (shift)) |
||||
|
||||
/**
|
||||
* @brief SCMI protocol IDs |
||||
* |
||||
* Each SCMI protocol is identified by an ID. Each |
||||
* of these IDs needs to be in decimal since they |
||||
* might be used to build protocol and static channel |
||||
* names. |
||||
*/ |
||||
#define SCMI_PROTOCOL_BASE 16 |
||||
#define SCMI_PROTOCOL_POWER_DOMAIN 17 |
||||
#define SCMI_PROTOCOL_SYSTEM 18 |
||||
#define SCMI_PROTOCOL_PERF 19 |
||||
#define SCMI_PROTOCOL_CLOCK 20 |
||||
#define SCMI_PROTOCOL_SENSOR 21 |
||||
#define SCMI_PROTOCOL_RESET_DOMAIN 22 |
||||
#define SCMI_PROTOCOL_VOLTAGE_DOMAIN 23 |
||||
#define SCMI_PROTOCOL_PCAP_MONITOR 24 |
||||
#define SCMI_PROTOCOL_PINCTRL 25 |
||||
|
||||
#endif /* _INCLUDE_ZEPHYR_DRIVERS_FIRMWARE_SCMI_UTIL_H_ */ |
Loading…
Reference in new issue