diff --git a/doc/hardware/arch/arm-scmi.rst b/doc/hardware/arch/arm-scmi.rst index 1055f5d6b8964..fbb370a209518 100644 --- a/doc/hardware/arch/arm-scmi.rst +++ b/doc/hardware/arch/arm-scmi.rst @@ -191,6 +191,17 @@ example, ``CPU_SLEEP_MODE_SET``. This driver is NXP-specific. As such, it may only be used on NXP system that uses SCMI for cpu domain management operations. +NXP - BBNSM management +*************************** + +This protocol is intended provide access to the battery-backed module. +This contains persistent storage (GPR), an RTC, and the ON/OFF button, +for example, ``scmi_bbm_button_notify``. + +.. note:: + This driver is NXP-specific. As such, it may only be used on NXP + system that uses SCMI for bbnsm domain management operations. + Enabling the SCMI support ************************* diff --git a/drivers/clock_control/clock_control_arm_scmi.c b/drivers/clock_control/clock_control_arm_scmi.c index f48d91dae6209..cb90e38eb06dc 100644 --- a/drivers/clock_control/clock_control_arm_scmi.c +++ b/drivers/clock_control/clock_control_arm_scmi.c @@ -121,4 +121,4 @@ static struct scmi_clock_data data; DT_INST_SCMI_PROTOCOL_DEFINE(0, &scmi_clock_init, NULL, &data, NULL, PRE_KERNEL_1, CONFIG_CLOCK_CONTROL_INIT_PRIORITY, - &scmi_clock_api); + &scmi_clock_api, NULL); diff --git a/drivers/firmware/scmi/core.c b/drivers/firmware/scmi/core.c index 3ded59e137e5b..ae307ab5e4a39 100644 --- a/drivers/firmware/scmi/core.c +++ b/drivers/firmware/scmi/core.c @@ -43,8 +43,55 @@ int scmi_status_to_errno(int scmi_status) } } -static void scmi_core_reply_cb(struct scmi_channel *chan) +static int scmi_core_handle_notification(int hdr) { + uint32_t protocol_id, msg_id; + struct scmi_protocol_event *events; + + protocol_id = SCMI_MESSAGE_HDR_EX_PROTOCOL(hdr); + msg_id = SCMI_MESSAGE_HDR_EX_MSGID(hdr); + + STRUCT_SECTION_FOREACH(scmi_protocol, it) { + events = it->events; + if (!events || !events->cb) { + continue; + } + + if (protocol_id == it->id) { + for (uint32_t num = 0; num < events->num_events; num++) { + if (msg_id == events->evts[num]) { + events->cb(msg_id); + return 0; + } + } + } + } + + return -ENOENT; +} + +static void scmi_core_reply_cb(struct scmi_channel *chan, int hdr) +{ + int msg_type; + int status; + + msg_type = SCMI_MESSAGE_HDR_EX_TYPE(hdr); + + switch (msg_type) { + case SCMI_COMMAND: + break; + case SCMI_DELAYED_REPLY: + break; + case SCMI_NOTIFICATION: + status = scmi_core_handle_notification(hdr); + if (status) { + LOG_WRN("Scmi notification event not find"); + } + break; + default: + LOG_WRN("Unexpected message type %u", msg_type); + } + if (!k_is_pre_kernel()) { k_sem_give(&chan->sem); } @@ -63,11 +110,6 @@ static int scmi_core_setup_chan(const struct device *transport, return 0; } - /* no support for RX channels ATM */ - if (!tx) { - return -ENOTSUP; - } - k_mutex_init(&chan->lock); k_sem_init(&chan->sem, 0, 1); @@ -216,6 +258,31 @@ int scmi_send_message(struct scmi_protocol *proto, struct scmi_message *msg, } } +int scmi_read_message(struct scmi_protocol *proto, struct scmi_message *msg) +{ + int ret; + + if (!proto->rx) { + return -ENODEV; + } + + if (!proto->rx->ready) { + return -EINVAL; + } + + /* read message from platform, such as notification event + * + * Unlike scmi_send_message, reading messages with scmi_read_message is not currently + * required in the PRE_KERNEL stage. The interrupt-based logic is used here. + */ + if (k_is_pre_kernel()) { + return -EINVAL; + } + + ret = scmi_transport_read_message(proto->transport, proto->rx, msg); + + return ret; +} static int scmi_core_protocol_setup(const struct device *transport) { int ret; @@ -226,6 +293,7 @@ static int scmi_core_protocol_setup(const struct device *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); + it->rx = scmi_transport_request_channel(transport, it->id, false); #endif /* CONFIG_ARM_SCMI_TRANSPORT_HAS_STATIC_CHANNELS */ if (!it->tx) { @@ -236,6 +304,14 @@ static int scmi_core_protocol_setup(const struct device *transport) if (ret < 0) { return ret; } + + /* notification/delayed reply channel is optional */ + if (it->rx) { + ret = scmi_core_setup_chan(transport, it->rx, false); + if (ret < 0) { + return ret; + } + } } return 0; diff --git a/drivers/firmware/scmi/mailbox.c b/drivers/firmware/scmi/mailbox.c index 2f18ce89cdce7..bc43b17fb6dce 100644 --- a/drivers/firmware/scmi/mailbox.c +++ b/drivers/firmware/scmi/mailbox.c @@ -6,18 +6,61 @@ #include #include "mailbox.h" +#include 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) +static int scmi_mbox_get_pending_msg(struct scmi_channel *chan, + struct scmi_message *msg) +{ + uint32_t context; + int ret; + struct scmi_mbox_channel *mbox_chan = chan->data; + + msg->hdr = 0x0; + msg->len = sizeof(uint32_t); + msg->content = &context; + + ret = scmi_shmem_read_hdr(mbox_chan->shmem, msg); + if (ret < 0) { + LOG_ERR("failed to read message to shmem: %d", ret); + return ret; + } + + return 0; +} + +static void scmi_mbox_tx_reply_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; + struct scmi_message msg; + + scmi_mbox_get_pending_msg(scmi_chan, &msg); if (scmi_chan->cb) { - scmi_chan->cb(scmi_chan); + scmi_chan->cb(scmi_chan, msg.hdr); + } +} + +static void scmi_mbox_notify_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; + struct scmi_mbox_channel *mbox_chan = scmi_chan->data; + const struct device *shmem = mbox_chan->shmem; + bool a2p = false; + struct scmi_message msg; + + scmi_mbox_get_pending_msg(scmi_chan, &msg); + + if (scmi_chan->cb) { + scmi_chan->cb(scmi_chan, msg.hdr); + scmi_shmem_clear_channel_status(shmem, a2p); } } @@ -71,29 +114,34 @@ static int scmi_mbox_setup_chan(const struct device *transport, { int ret; struct scmi_mbox_channel *mbox_chan; - struct mbox_dt_spec *tx_reply; + struct mbox_dt_spec *mbox_spec; mbox_chan = chan->data; - if (!tx) { - return -ENOTSUP; - } - - if (mbox_chan->tx_reply.dev) { - tx_reply = &mbox_chan->tx_reply; + if (tx) { + mbox_spec = mbox_chan->tx_reply.dev ? &mbox_chan->tx_reply : &mbox_chan->tx; + ret = mbox_register_callback_dt(mbox_spec, scmi_mbox_tx_reply_cb, chan); + if (ret < 0) { + LOG_ERR("failed to register reply cb on %s", + mbox_chan->tx_reply.dev ? "tx_reply" : "tx"); + return ret; + } } 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; + if (!mbox_chan->rx.dev) { + LOG_ERR("RX channel not defined"); + return -ENOTSUP; + } + mbox_spec = &mbox_chan->rx; + ret = mbox_register_callback_dt(&mbox_chan->rx, scmi_mbox_notify_cb, chan); + if (ret < 0) { + LOG_ERR("failed to register notify cb on rx"); + return ret; + } } - ret = mbox_set_enabled_dt(tx_reply, true); + ret = mbox_set_enabled_dt(mbox_spec, true); if (ret < 0) { - LOG_ERR("failed to enable tx reply dbell"); + LOG_ERR("failed to enable %s dbell", tx ? "tx" : "rx"); } /* enable interrupt-based communication */ diff --git a/drivers/firmware/scmi/mailbox.h b/drivers/firmware/scmi/mailbox.h index 4e758d8e298b9..179e4584221dd 100644 --- a/drivers/firmware/scmi/mailbox.h +++ b/drivers/firmware/scmi/mailbox.h @@ -40,17 +40,34 @@ .tx_reply = _SCMI_MBOX_CHAN_DBELL(node_id, tx_reply), \ } +/* define private data for a protocol RX channel */ +#define _SCMI_MBOX_CHAN_DEFINE_PRIV_RX(node_id, proto) \ + static struct scmi_mbox_channel _SCMI_MBOX_CHAN_NAME(proto, 1) =\ + { \ + .shmem = _SCMI_MBOX_SHMEM_BY_IDX(node_id, 1), \ + .rx = _SCMI_MBOX_CHAN_DBELL(node_id, rx), \ + } + /* * 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) \ +#define _SCMI_MBOX_CHAN_DEFINE_TX(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))); \ +#define _SCMI_MBOX_CHAN_DEFINE_RX(node_id, proto, idx) \ + _SCMI_MBOX_CHAN_DEFINE_PRIV_RX(node_id, proto); \ + DT_SCMI_TRANSPORT_CHAN_DEFINE(node_id, idx, proto, \ + &(_SCMI_MBOX_CHAN_NAME(proto, idx))); \ + +#define _SCMI_MBOX_CHAN_DEFINE_RX_OPTIONAL(node_id, proto, idx) \ + COND_CODE_1(DT_PROP_HAS_IDX(node_id, shmem, idx), \ + (_SCMI_MBOX_CHAN_DEFINE_RX(node_id, proto, idx)), \ + ()) /* * Optionally define a mailbox channel for a protocol. This is optional * because a protocol might not have a dedicated channel. @@ -69,24 +86,26 @@ _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 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) || \ + DT_INST_PROP_HAS_NAME(inst, mboxes, rx)))), \ + "bad unidirectional channel description"); \ + \ + BUILD_ASSERT(DT_INST_PROP_LEN(inst, shmem) <= 2, \ + "bad SHMEM count"); \ + \ + BUILD_ASSERT(DT_INST_PROP_LEN(inst, mboxes) <= 2, \ + "bad mbox count"); \ + \ + _SCMI_MBOX_CHAN_DEFINE_TX(DT_INST(inst, DT_DRV_COMPAT), SCMI_PROTOCOL_BASE, 0) \ + _SCMI_MBOX_CHAN_DEFINE_RX_OPTIONAL(DT_INST(inst, DT_DRV_COMPAT), SCMI_PROTOCOL_BASE, 1) \ /* * Define the mailbox-based transport layer. What this does is: @@ -112,6 +131,8 @@ struct scmi_mbox_channel { struct mbox_dt_spec tx; /* TX reply dbell */ struct mbox_dt_spec tx_reply; + /* RX dbell */ + struct mbox_dt_spec rx; }; #endif /* _ZEPHYR_DRIVERS_FIRMWARE_SCMI_MAILBOX_H_ */ diff --git a/drivers/firmware/scmi/nxp/CMakeLists.txt b/drivers/firmware/scmi/nxp/CMakeLists.txt index fe8bfa72eaf1b..72ad61f0c1469 100644 --- a/drivers/firmware/scmi/nxp/CMakeLists.txt +++ b/drivers/firmware/scmi/nxp/CMakeLists.txt @@ -5,3 +5,4 @@ zephyr_library_property(ALLOW_EMPTY TRUE) zephyr_library_sources_ifdef(CONFIG_ARM_SCMI_NXP_VENDOR_EXTENSIONS shmem.c) zephyr_library_sources_ifdef(CONFIG_NXP_SCMI_CPU_DOMAIN_HELPERS cpu.c) +zephyr_library_sources_ifdef(CONFIG_NXP_SCMI_BBM bbm.c) diff --git a/drivers/firmware/scmi/nxp/Kconfig b/drivers/firmware/scmi/nxp/Kconfig index 458db5393526b..a98c57ad1ea54 100644 --- a/drivers/firmware/scmi/nxp/Kconfig +++ b/drivers/firmware/scmi/nxp/Kconfig @@ -16,4 +16,11 @@ config NXP_SCMI_CPU_DOMAIN_HELPERS help Enable support for SCMI cpu domain protocol helper functions. +config NXP_SCMI_BBM + bool "NXP BBM SCMI interface protocol" + default y + depends on DT_HAS_NXP_SCMI_BBM_ENABLED + help + Enable support for SCMI NXP BBM functions. + endif # ARM_SCMI_NXP_VENDOR_EXTENSIONS diff --git a/drivers/firmware/scmi/nxp/bbm.c b/drivers/firmware/scmi/nxp/bbm.c new file mode 100644 index 0000000000000..abc738eb4e590 --- /dev/null +++ b/drivers/firmware/scmi/nxp/bbm.c @@ -0,0 +1,94 @@ +/* + * Copyright 2025 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include + +LOG_MODULE_REGISTER(scmi_nxp_bbm); + +static uint32_t scmi_nxp_bbm_events[] = { + SCMI_PROTO_BBM_PROTOCOL_BUTTON_EVENT, +}; + +static struct scmi_protocol_event bbm_event = { + .evts = scmi_nxp_bbm_events, + .num_events = ARRAY_SIZE(scmi_nxp_bbm_events), + .cb = scmi_bbm_event_protocol_cb, +}; + +DT_SCMI_PROTOCOL_DEFINE_NODEV(DT_INST(0, nxp_scmi_bbm), NULL, &bbm_event); + +int scmi_bbm_button_notify(uint32_t flags) +{ + struct scmi_protocol *proto = &SCMI_PROTOCOL_NAME(SCMI_PROTOCOL_NXP_BBM); + struct scmi_message msg, reply; + int32_t status, ret; + + /* sanity checks */ + if (proto->id != SCMI_PROTOCOL_NXP_BBM) { + return -EINVAL; + } + + msg.hdr = SCMI_MESSAGE_HDR_MAKE(SCMI_PROTO_BBM_BBM_BUTTON_NOTIFY, SCMI_COMMAND, + proto->id, 0); + msg.len = sizeof(flags); + msg.content = &flags; + + reply.hdr = msg.hdr; + reply.len = sizeof(status); + reply.content = &status; + + ret = scmi_send_message(proto, &msg, &reply, k_is_pre_kernel()); + if (ret < 0) { + return ret; + } + + return 0; +} + +int scmi_bbm_button_event(uint32_t *flags) +{ + struct scmi_protocol *proto = &SCMI_PROTOCOL_NAME(SCMI_PROTOCOL_NXP_BBM); + struct scmi_message msg; + int32_t ret; + + /* sanity checks */ + if (proto->id != SCMI_PROTOCOL_NXP_BBM) { + return -EINVAL; + } + + /* TODO: Implement token handling for P2A flow to correctly associate notifications */ + msg.hdr = SCMI_MESSAGE_HDR_MAKE(SCMI_PROTO_BBM_PROTOCOL_BUTTON_EVENT, SCMI_NOTIFICATION, + proto->id, 0x0); + msg.len = sizeof(flags); + msg.content = flags; + + ret = scmi_read_message(proto, &msg); + if (ret < 0) { + return ret; + } + + return 0; +} + +int scmi_bbm_event_protocol_cb(int32_t msg_id) +{ + uint32_t flags; + int32_t ret = 0; + + if (msg_id == SCMI_PROTO_BBM_PROTOCOL_BUTTON_EVENT) { + ret = scmi_bbm_button_event(&flags); + if (ret < 0) { + LOG_ERR("failed to read bbm button event to shmem: %d", ret); + } else { + LOG_INF("SCMI BBM BUTTON notification: flags=0x%08X", flags); + } + } + + return ret; +} diff --git a/drivers/firmware/scmi/nxp/cpu.c b/drivers/firmware/scmi/nxp/cpu.c index babd201a4465b..2d5b724be0560 100644 --- a/drivers/firmware/scmi/nxp/cpu.c +++ b/drivers/firmware/scmi/nxp/cpu.c @@ -8,7 +8,7 @@ #include #include -DT_SCMI_PROTOCOL_DEFINE_NODEV(DT_INST(0, nxp_scmi_cpu), NULL); +DT_SCMI_PROTOCOL_DEFINE_NODEV(DT_INST(0, nxp_scmi_cpu), NULL, NULL); int scmi_cpu_sleep_mode_set(struct scmi_cpu_sleep_mode_config *cfg) { diff --git a/drivers/firmware/scmi/pinctrl.c b/drivers/firmware/scmi/pinctrl.c index 73ed000daa0ef..50f73e8ad7715 100644 --- a/drivers/firmware/scmi/pinctrl.c +++ b/drivers/firmware/scmi/pinctrl.c @@ -7,7 +7,7 @@ #include #include -DT_SCMI_PROTOCOL_DEFINE_NODEV(DT_INST(0, arm_scmi_pinctrl), NULL); +DT_SCMI_PROTOCOL_DEFINE_NODEV(DT_INST(0, arm_scmi_pinctrl), NULL, NULL); int scmi_pinctrl_settings_configure(struct scmi_pinctrl_settings *settings) { diff --git a/drivers/firmware/scmi/power.c b/drivers/firmware/scmi/power.c index b560ce4845713..4921670bf7067 100644 --- a/drivers/firmware/scmi/power.c +++ b/drivers/firmware/scmi/power.c @@ -8,7 +8,7 @@ #include #include -DT_SCMI_PROTOCOL_DEFINE_NODEV(DT_INST(0, arm_scmi_power), NULL); +DT_SCMI_PROTOCOL_DEFINE_NODEV(DT_INST(0, arm_scmi_power), NULL, NULL); struct scmi_power_state_get_reply { int32_t status; diff --git a/drivers/firmware/scmi/shmem.c b/drivers/firmware/scmi/shmem.c index 86b1a9659d494..6f66a3adc3f29 100644 --- a/drivers/firmware/scmi/shmem.c +++ b/drivers/firmware/scmi/shmem.c @@ -39,6 +39,23 @@ int scmi_shmem_get_channel_status(const struct device *dev, uint32_t *status) return 0; } +int scmi_shmem_clear_channel_status(const struct device *dev, bool a2p) +{ + struct scmi_shmem_data *data; + struct scmi_shmem_layout *layout; + + data = dev->data; + layout = (struct scmi_shmem_layout *)data->regmap; + + if (a2p) { + layout->chan_status &= ~SCMI_SHMEM_CHAN_STATUS_BUSY_BIT; + } else { + layout->chan_status |= SCMI_SHMEM_CHAN_STATUS_BUSY_BIT; + } + + return 0; +} + static void scmi_shmem_memcpy(mm_reg_t dst, mm_reg_t src, uint32_t bytes) { int i; @@ -48,6 +65,30 @@ static void scmi_shmem_memcpy(mm_reg_t dst, mm_reg_t src, uint32_t bytes) } } +int scmi_shmem_read_hdr(const struct device *shmem, struct scmi_message *msg) +{ + struct scmi_shmem_layout *layout; + struct scmi_shmem_data *data; + + data = shmem->data; + layout = (struct scmi_shmem_layout *)data->regmap; + + /* some sanity checks first */ + if (!msg) { + return -EINVAL; + } + + if (scmi_shmem_vendor_read_message(layout) < 0) { + LOG_ERR("vendor specific validation failed"); + return -EINVAL; + } + + scmi_shmem_memcpy(POINTER_TO_UINT(&msg->hdr), + data->regmap + SCMI_SHMEM_CHAN_MSG_HDR_OFFSET, sizeof(uint32_t)); + + return 0; +} + __weak int scmi_shmem_vendor_read_message(const struct scmi_shmem_layout *layout) { return 0; @@ -115,6 +156,7 @@ int scmi_shmem_write_message(const struct device *shmem, struct scmi_message *ms struct scmi_shmem_layout *layout; struct scmi_shmem_data *data; const struct scmi_shmem_config *cfg; + bool a2p = true; data = shmem->data; cfg = shmem->config; @@ -150,7 +192,7 @@ int scmi_shmem_write_message(const struct device *shmem, struct scmi_message *ms } /* done, mark channel as busy and proceed */ - layout->chan_status &= ~SCMI_SHMEM_CHAN_STATUS_BUSY_BIT; + scmi_shmem_clear_channel_status(shmem, a2p); return 0; } diff --git a/dts/arm/nxp/nxp_imx95_m7.dtsi b/dts/arm/nxp/nxp_imx95_m7.dtsi index d8e66b3e9c27e..3e7f8059d2d1b 100644 --- a/dts/arm/nxp/nxp_imx95_m7.dtsi +++ b/dts/arm/nxp/nxp_imx95_m7.dtsi @@ -63,14 +63,19 @@ compatible = "arm,scmi-shmem"; reg = <0x44611000 0x80>; }; + + scmi_shmem1: memory@44611080 { + compatible = "arm,scmi-shmem"; + reg = <0x44611080 0x80>; + }; }; firmware { scmi { compatible = "arm,scmi"; - shmem = <&scmi_shmem0>; - mboxes = <&mu5 0>; - mbox-names = "tx"; + shmem = <&scmi_shmem0>, <&scmi_shmem1>; + mboxes = <&mu5 0>, <&mu5 1>; + mbox-names = "tx", "rx"; #address-cells = <1>; #size-cells = <0>; @@ -100,6 +105,11 @@ compatible = "nxp,scmi-cpu"; reg = <0x82>; }; + + scmi_bbm: protocol@81 { + compatible = "nxp,scmi-bbm"; + reg = <0x81>; + }; }; }; diff --git a/dts/bindings/firmware/arm,scmi.yaml b/dts/bindings/firmware/arm,scmi.yaml index 5ece8e59201d9..b4d0fcddc963f 100644 --- a/dts/bindings/firmware/arm,scmi.yaml +++ b/dts/bindings/firmware/arm,scmi.yaml @@ -48,11 +48,12 @@ include: [base.yaml] properties: shmem: - type: phandle + type: phandles required: true description: | - Phandle to node describing TX channel shared memory area. - This translates to a **single** SCMI transmit channel. + Phandle array to nodes describing shared memory areas for SCMI transport. + The first entry corresponds to the TX channel, and the second (if present) + corresponds to the RX channel. mboxes: required: true @@ -62,6 +63,8 @@ properties: mailbox channel for signaling) 2) tx_reply - 2 mbox / 1 shmem (platform and agent use different mailbox channels for signaling) + 3) tx + rx - 2 mbox / 2 shmem (platform and agent use different mailbox + channels and shmem between scmi_command and scmi_notification) mbox-names: required: true diff --git a/dts/bindings/firmware/nxp,scmi-bbm.yaml b/dts/bindings/firmware/nxp,scmi-bbm.yaml new file mode 100644 index 0000000000000..3897a8ae1aa68 --- /dev/null +++ b/dts/bindings/firmware/nxp,scmi-bbm.yaml @@ -0,0 +1,13 @@ +# Copyright 2025 NXP +# SPDX-License-Identifier: Apache-2.0 + +description: System Control and Management Interface (SCMI) bbm protocol + +compatible: "nxp,scmi-bbm" + +include: [base.yaml] + +properties: + reg: + required: true + const: [0x81] diff --git a/include/zephyr/drivers/firmware/scmi/nxp/bbm.h b/include/zephyr/drivers/firmware/scmi/nxp/bbm.h new file mode 100644 index 0000000000000..844bff9032bb2 --- /dev/null +++ b/include/zephyr/drivers/firmware/scmi/nxp/bbm.h @@ -0,0 +1,75 @@ +/* + * Copyright 2025 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @brief SCMI NXP BBNSM domain protocol helpers + */ + +#ifndef _INCLUDE_ZEPHYR_DRIVERS_FIRMWARE_SCMI_BBM_H_ +#define _INCLUDE_ZEPHYR_DRIVERS_FIRMWARE_SCMI_BBM_H_ + +#include + +#define SCMI_PROTOCOL_NXP_BBM 129 + +/*! + * @name SCMI NXP BBM button notification flags + */ +/** @{ */ +/*! Enable button notifications */ +#define SCMI_BBM_NOTIFY_BUTTON_DETECT(x) (((x) & 0x1U) << 0U) +/** @} */ + +/** + * @brief Bbm protocol command message IDs + */ +enum scmi_bbm_message { + SCMI_PROTO_BBM_PROTOCOL_RTC_EVENT = 0x0, + SCMI_PROTO_BBM_PROTOCOL_BUTTON_EVENT = 0x1, + SCMI_PROTO_BBM_BBM_GPR_SET = 0x3, + SCMI_PROTO_BBM_BBM_GPR_GET = 0x4, + SCMI_PROTO_BBM_BBM_RTC_ATTRIBUTES = 0x5, + SCMI_PROTO_BBM_BBM_RTC_TIME_SET = 0x6, + SCMI_PROTO_BBM_BBM_RTC_TIME_GET = 0x7, + SCMI_PROTO_BBM_BBM_RTC_ALARM_SET = 0x8, + SCMI_PROTO_BBM_BBM_BUTTON_GET = 0x9, + SCMI_PROTO_BBM_BBM_RTC_NOTIFY = 0xA, + SCMI_PROTO_BBM_BBM_BUTTON_NOTIFY = 0xB, + SCMI_PROTO_BBM_BBM_RTC_STATE = 0xC, + SCMI_PROTO_BBM_NEGOTIATE_PROTOCOL_VERSION = 0x10, +}; + +/** + * @brief Set up BBM button notify message + * + * @param flags to enable or disable button notify for agent + * + * @retval 0 if successful + * @retval negative errno if failure + */ +int scmi_bbm_button_notify(uint32_t flags); + +/** + * @brief Read BBM button event message + * + * @param flags to get notification event from agent + * + * @retval 0 if successful + * @retval negative errno if failure + */ +int scmi_bbm_button_event(uint32_t *flags); + +/** + * @brief BBM event notification callback + * + * @param msg_id to determine and handle the corresponding event. + * + * @retval 0 if successful + * @retval negative errno if failure + */ +int scmi_bbm_event_protocol_cb(int32_t msg_id); +#endif /* _INCLUDE_ZEPHYR_DRIVERS_FIRMWARE_SCMI_BBM_H_ */ diff --git a/include/zephyr/drivers/firmware/scmi/protocol.h b/include/zephyr/drivers/firmware/scmi/protocol.h index 920aaf5c435da..03d2af2624d19 100644 --- a/include/zephyr/drivers/firmware/scmi/protocol.h +++ b/include/zephyr/drivers/firmware/scmi/protocol.h @@ -17,6 +17,19 @@ #include #include +/* The composition and offset of scmi messages */ +#define SCMI_MSGID_SHIFT 0 +#define SCMI_MSGID_MASK GENMASK(7, 0) + +#define SCMI_TYPE_SHIFT 8 +#define SCMI_TYPE_MASK GENMASK(1, 0) + +#define SCMI_PROTOCOL_SHIFT 10 +#define SCMI_PROTOCOL_MASK GENMASK(7, 0) + +#define SCMI_TOKEN_SHIFT 18 +#define SCMI_TOKEN_MASK GENMASK(9, 0) + /** * @brief Build an SCMI message header * @@ -28,11 +41,24 @@ * @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)) +#define SCMI_MESSAGE_HDR_MAKE(id, type, proto, token) \ + (SCMI_FIELD_MAKE((id), SCMI_MSGID_MASK, SCMI_MSGID_SHIFT) | \ + SCMI_FIELD_MAKE((type), SCMI_TYPE_MASK, SCMI_TYPE_SHIFT) | \ + SCMI_FIELD_MAKE((proto), SCMI_PROTOCOL_MASK, SCMI_PROTOCOL_SHIFT) | \ + SCMI_FIELD_MAKE((token), SCMI_TOKEN_MASK, SCMI_TOKEN_SHIFT)) + +/** + * @brief Extract a field from SCMI message header + * @param hdr the 32-bit SCMI message header + * @param FIELD one of: MSGID, TYPE, PROTOCOL, TOKEN + */ +#define SCMI_MESSAGE_HDR_EX(hdr, FIELD) \ + SCMI_FIELD_EX((hdr), SCMI_##FIELD##_MASK, SCMI_##FIELD##_SHIFT) + +#define SCMI_MESSAGE_HDR_EX_MSGID(hdr) SCMI_MESSAGE_HDR_EX(hdr, MSGID) +#define SCMI_MESSAGE_HDR_EX_TYPE(hdr) SCMI_MESSAGE_HDR_EX(hdr, TYPE) +#define SCMI_MESSAGE_HDR_EX_PROTOCOL(hdr) SCMI_MESSAGE_HDR_EX(hdr, PROTOCOL) +#define SCMI_MESSAGE_HDR_EX_TOKEN(hdr) SCMI_MESSAGE_HDR_EX(hdr, TOKEN) struct scmi_channel; @@ -76,10 +102,42 @@ struct scmi_protocol { uint32_t id; /** TX channel */ struct scmi_channel *tx; + /** RX channel */ + struct scmi_channel *rx; /** transport layer device */ const struct device *transport; /** protocol private data */ void *data; + /** protocol event */ + struct scmi_protocol_event *events; +}; + +/** + * @typedef scmi_protocol_event_callback + * @brief Per‑protocol notification callback invoked by the SCMI core. + * + * Define event-specific information during the static registration phase + * of each SCMI protocol. When a P2A notification/interrupt is received, + * the SCMI core decodes the message and dispatches it to the corresponding + * protocol's callback. + * + * @param msg_id is the protocol specific message index. + * @return int 0 on success, negative error code on failure + */ +typedef int (*scmi_protocol_event_callback)(int32_t msg_id); + +/** + * @struct scmi_protocol_event + * + * @brief SCMI protocol event structure + */ +struct scmi_protocol_event { + /** events ids */ + uint32_t *evts; + /** Number of supported protocol's events **/ + uint32_t num_events; + /** protocol private event call back **/ + scmi_protocol_event_callback cb; }; /** @@ -126,4 +184,16 @@ int scmi_send_message(struct scmi_protocol *proto, struct scmi_message *msg, struct scmi_message *reply, bool use_polling); +/** + * @brief Read an SCMI message + * + * Blocking function used to read an SCMI message over a given channel + * + * @param proto pointer to SCMI protocol + * @param msg pointer to SCMI message to read + * + * @retval 0 if successful + * @retval negative errno if failure + */ +int scmi_read_message(struct scmi_protocol *proto, struct scmi_message *msg); #endif /* _INCLUDE_ZEPHYR_DRIVERS_FIRMWARE_SCMI_PROTOCOL_H_ */ diff --git a/include/zephyr/drivers/firmware/scmi/shmem.h b/include/zephyr/drivers/firmware/scmi/shmem.h index 662bf79337356..a36b5253429ab 100644 --- a/include/zephyr/drivers/firmware/scmi/shmem.h +++ b/include/zephyr/drivers/firmware/scmi/shmem.h @@ -19,6 +19,8 @@ #define SCMI_SHMEM_CHAN_STATUS_BUSY_BIT BIT(0) #define SCMI_SHMEM_CHAN_FLAG_IRQ_BIT BIT(0) +#define SCMI_SHMEM_CHAN_MSG_HDR_OFFSET 0x18 + struct scmi_shmem_layout { volatile uint32_t res0; volatile uint32_t chan_status; @@ -93,4 +95,27 @@ int scmi_shmem_vendor_write_message(struct scmi_shmem_layout *layout); */ int scmi_shmem_vendor_read_message(const struct scmi_shmem_layout *layout); +/** + * @brief Clear a SHMEM area channel status + * + * @param dev pointer to shmem device + * @param a2p true if A2P direction, false otherwise + * + * @retval 0 if successful + * @retval negative errno if failure + */ +int scmi_shmem_clear_channel_status(const struct device *dev, bool a2p); + +/** + * @brief Read a message header 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_hdr(const struct device *shmem, struct scmi_message *msg); + #endif /* _INCLUDE_ZEPHYR_DRIVERS_FIRMWARE_SCMI_SHMEM_H_ */ diff --git a/include/zephyr/drivers/firmware/scmi/transport.h b/include/zephyr/drivers/firmware/scmi/transport.h index 2cb72a2b9042c..4ff6344735086 100644 --- a/include/zephyr/drivers/firmware/scmi/transport.h +++ b/include/zephyr/drivers/firmware/scmi/transport.h @@ -31,8 +31,9 @@ struct scmi_channel; * * @param chan pointer to SCMI channel on which the reply * arrived + * @param hdr is the share memory Message header */ -typedef void (*scmi_channel_cb)(struct scmi_channel *chan); +typedef void (*scmi_channel_cb)(struct scmi_channel *chan, int hdr); /** * @struct scmi_channel diff --git a/include/zephyr/drivers/firmware/scmi/util.h b/include/zephyr/drivers/firmware/scmi/util.h index cd05f984ac202..d4c84993366b6 100644 --- a/include/zephyr/drivers/firmware/scmi/util.h +++ b/include/zephyr/drivers/firmware/scmi/util.h @@ -53,7 +53,7 @@ #define SCMI_TRANSPORT_CHAN_NAME(proto, idx) CONCAT(scmi_channel_, proto, _, idx) /** - * @brief Declare a TX SCMI channel + * @brief Declare TX/RX SCMI channel * * Given a node_id for a protocol, this macro declares the SCMI * TX channel statically bound to said protocol via the "extern" @@ -70,6 +70,15 @@ (extern struct scmi_channel \ SCMI_TRANSPORT_CHAN_NAME(SCMI_PROTOCOL_BASE, 0);)) \ +#define DT_SCMI_TRANSPORT_RX_CHAN_DECLARE(node_id) \ + COND_CODE_1(DT_SCMI_TRANSPORT_PROTO_HAS_CHAN(node_id, 1), \ + (extern struct scmi_channel \ + SCMI_TRANSPORT_CHAN_NAME(DT_REG_ADDR_RAW(node_id), 1);), \ + (COND_CODE_1(DT_PROP_HAS_IDX(DT_PARENT(node_id), shmem, 1),\ + (extern struct scmi_channel \ + SCMI_TRANSPORT_CHAN_NAME(SCMI_PROTOCOL_BASE, 1);), \ + (/* no decl when NULL */)))) + /** * @brief Declare SCMI TX/RX channels * @@ -84,6 +93,7 @@ */ #define DT_SCMI_TRANSPORT_CHANNELS_DECLARE(node_id) \ DT_SCMI_TRANSPORT_TX_CHAN_DECLARE(node_id) \ + DT_SCMI_TRANSPORT_RX_CHAN_DECLARE(node_id) \ /** * @brief Declare SCMI TX/RX channels using node instance number @@ -97,7 +107,7 @@ DT_SCMI_TRANSPORT_CHANNELS_DECLARE(DT_INST(inst, DT_DRV_COMPAT)) /** - * @brief Get a reference to a protocol's SCMI TX channel + * @brief Get a reference to a protocol's SCMI TX/RX channel * * Given a node_id for a protocol, this macro returns a * reference to an SCMI TX channel statically bound to said @@ -105,7 +115,7 @@ * * @param node_id protocol node identifier * - * @return reference to the struct scmi_channel of the TX channel + * @return reference to the struct scmi_channel of the TX/RX channel * bound to the protocol identifier by node_id */ #define DT_SCMI_TRANSPORT_TX_CHAN(node_id) \ @@ -113,6 +123,12 @@ (&SCMI_TRANSPORT_CHAN_NAME(DT_REG_ADDR_RAW(node_id), 0)), \ (&SCMI_TRANSPORT_CHAN_NAME(SCMI_PROTOCOL_BASE, 0))) +#define DT_SCMI_TRANSPORT_RX_CHAN(node_id) \ + COND_CODE_1(DT_SCMI_TRANSPORT_PROTO_HAS_CHAN(node_id, 1), \ + (&SCMI_TRANSPORT_CHAN_NAME(DT_REG_ADDR_RAW(node_id), 1)), \ + (COND_CODE_1(DT_PROP_HAS_IDX(DT_PARENT(node_id), shmem, 1), \ + (&SCMI_TRANSPORT_CHAN_NAME(SCMI_PROTOCOL_BASE, 1)), \ + (NULL)))) /** * @brief Define an SCMI channel for a protocol * @@ -146,12 +162,14 @@ * @param proto protocol ID in decimal format * @param pdata protocol private data */ -#define DT_SCMI_PROTOCOL_DATA_DEFINE(node_id, proto, pdata) \ +#define DT_SCMI_PROTOCOL_DATA_DEFINE(node_id, proto, pdata, pevents) \ STRUCT_SECTION_ITERABLE(scmi_protocol, SCMI_PROTOCOL_NAME(proto)) = \ { \ .id = proto, \ .tx = DT_SCMI_TRANSPORT_TX_CHAN(node_id), \ + .rx = DT_SCMI_TRANSPORT_RX_CHAN(node_id), \ .data = pdata, \ + .events = pevents, \ } #else /* CONFIG_ARM_SCMI_TRANSPORT_HAS_STATIC_CHANNELS */ @@ -208,12 +226,12 @@ * @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_RAW(node_id), data); \ - DEVICE_DT_DEFINE(node_id, init_fn, pm, \ - &SCMI_PROTOCOL_NAME(DT_REG_ADDR_RAW(node_id)), \ +#define DT_SCMI_PROTOCOL_DEFINE(node_id, init_fn, pm, data, config, \ + level, prio, api, events) \ + DT_SCMI_TRANSPORT_CHANNELS_DECLARE(node_id) \ + DT_SCMI_PROTOCOL_DATA_DEFINE(node_id, DT_REG_ADDR_RAW(node_id), data, events); \ + DEVICE_DT_DEFINE(node_id, init_fn, pm, \ + &SCMI_PROTOCOL_NAME(DT_REG_ADDR_RAW(node_id)), \ config, level, prio, api) /** @@ -230,9 +248,9 @@ * @param prio protocol's priority within its initialization level */ #define DT_INST_SCMI_PROTOCOL_DEFINE(inst, init_fn, pm, data, config, \ - level, prio, api) \ + level, prio, api, events) \ DT_SCMI_PROTOCOL_DEFINE(DT_INST(inst, DT_DRV_COMPAT), init_fn, pm, \ - data, config, level, prio, api) + data, config, level, prio, api, events) /** * @brief Define an SCMI protocol with no device @@ -245,9 +263,9 @@ * @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_RAW(node_id), data) +#define DT_SCMI_PROTOCOL_DEFINE_NODEV(node_id, data, events_ptr) \ + DT_SCMI_TRANSPORT_CHANNELS_DECLARE(node_id) \ + DT_SCMI_PROTOCOL_DATA_DEFINE(node_id, DT_REG_ADDR_RAW(node_id), data, events_ptr) /** * @brief Create an SCMI message field @@ -264,6 +282,16 @@ #define SCMI_FIELD_MAKE(x, mask, shift)\ (((uint32_t)(x) & (mask)) << (shift)) +/** + * @brief Extract an SCMI message field + * + * @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_EX(x, mask, shift) \ + ((((uint32_t)(x)) >> (shift)) & (uint32_t)(mask)) + /** * @brief SCMI protocol IDs * diff --git a/soc/nxp/imx/imx9/imx95/m7/soc.c b/soc/nxp/imx/imx9/imx95/m7/soc.c index 0554b43d5f19e..18455c4efca3f 100644 --- a/soc/nxp/imx/imx9/imx95/m7/soc.c +++ b/soc/nxp/imx/imx9/imx95/m7/soc.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -85,6 +86,9 @@ static int soc_init(void) } #endif /* CONFIG_NXP_SCMI_CPU_DOMAIN_HELPERS */ +#if defined(CONFIG_NXP_SCMI_BBM) + scmi_bbm_button_notify(SCMI_BBM_NOTIFY_BUTTON_DETECT(1)); +#endif return ret; }