?上篇介紹了電源管理入門-4子系統reset,提到子系統reset的執行為了安全可以到SCP里面去執行,但是怎么把這個消息傳遞過去呢,答案就是mailbox。
- 在軟件上可以使用SCMI協議+共享內存報文頭,
- 在硬件上可以使用PL320或者MHU。
1. 整體架構介紹
上圖以NPU子模塊的服務為例子,Mailbox的硬件使用PL320,整體流程如下:
- Reset consumer模塊執行devm_reset_control_get()獲取npu_reset復位句柄,然后通過reset_control_reset()觸發復位
- Linux系統reset framework找到復位驅動并執行ops->reset()回調函數
- scmi-reset驅動里面提供.reset函數的實現scmi_reset_deassert(),并執行
- arm-scmi里面提供scmi reset協議的實現模塊reset,里面提供reset函數scmi_reset_domain_reset()
- arm-scmi里面提供scmi協議收發的框架driver,提供do_xfer()
- arm-scmi里面提供mailbox的接口函數mailbox_send_message()
- arm-scmi里面提供共享內存的操作函數shmem_tx_prepare()
- mailbox驅動里面提供硬件PL320的寄存器操作實現pl320_mbox_send_data()
- SCP中PL320驅動模塊接收mailbox中斷
- SCP中SMT模塊從共享內存中讀取SCMI報文數據
- SCP中SCMI模塊對SCMI協議報文進行解析,并進行分發處理
- SCP中SCMI RESET DOMAIN協議模塊對報文進行功能處理
- SCP中RESET DOMAIN模塊屏蔽硬件差異實現統一API
- SCP中JUNO RESET DOMAIN模塊提供具體硬件CRU寄存器操作實現
2 Linux中reset模塊
2.1 Reset consumer
之前的文章電源管理入門-4子系統reset介紹了怎么使用Linux的reset子系統,這里我們就直接使用,需要在DTS中修改即可。
reset使用Linux自帶的reset框架,假定consumer-firmware-npu這個驅動要使用NPU的reset,定義在DTS中有reset consumer的說明:consumer-firmware-npu。
/ {
consumer_firmware@0x0 {
compatible = "consumer-firmware-npu";
reg = < 0x0 0x0 0x0 0x00 >;
resets = <&scmi_reset 0>;
reset-names = "npu_reset";
};
};
drivers/firmware/consumer/consumer.c中驅動需要使用reset功能。
static struct platform_driver consumer_firmware_driver = {
.driver = {
.name = "consumer_firmware",
.of_match_table = consumer_firmware_of_match,
},
.probe = consumer_firmware_probe,
.remove = consumer_firmware_remove,
};
consumer_firmware_probe
--》devm_reset_control_get //獲取"npu_reset"復位句柄
--》consumer_fw_firmware_cb
--》consumer_fw_memcpy //拷貝鏡像
--》consumer_control_reset //通知reset驅動進行reset
這樣DTS探測到consumer_firmware的時候就會觸發reset操作。
reset_control_reset
rstc->rcdev->ops->reset(rstc->rcdev, rstc->id);
reset的provider驅動使用compatible = "scmi-reset";驅動,詳細見后面2.2reset provider中分析。當reset時在drivers/reset/reset-scmi.c中實現
static int
scmi_reset_reset(struct reset_controller_dev *rcdev, unsigned long id)
{
const struct scmi_protocol_handle *ph = to_scmi_handle(rcdev);
return reset_ops->reset(ph, id);
}
static const struct reset_control_ops scmi_reset_ops = {
.assert = scmi_reset_assert,
.deassert = scmi_reset_deassert,
.reset = scmi_reset_reset,
};
reset_ops在scmi_reset_probe的時候會賦值
reset_ops = handle->devm_protocol_get(sdev, SCMI_PROTOCOL_RESET, &ph);
handle->devm_protocol_get為drivers/firmware/arm_scmi/driver.c中scmi_devm_protocol_get,
pi = scmi_get_protocol_instance(handle, protocol_id);
return pi->proto->ops;
在scmi協議初始化的時候,scmi_reset_register會注冊0x16的回調函數,詳細分析見2.2.1SCMI reset協議初始化內容。在drivers/firmware/arm_scmi/reset.c中
static const struct scmi_reset_proto_ops reset_proto_ops = {
.num_domains_get = scmi_reset_num_domains_get,
.name_get = scmi_reset_name_get,
.latency_get = scmi_reset_latency_get,
.reset = scmi_reset_domain_reset,
.assert = scmi_reset_domain_assert,
.deassert = scmi_reset_domain_deassert,
};
scmi_reset_domain_reset--》scmi_domain_reset ret = ph->xops->do_xfer(ph, t); do_xfer在drivers/firmware/arm_scmi/driver.c中實現
do_xfer(ph, xfer);
ret = info->desc->ops->send_message(cinfo, xfer);
send_message在drivers/firmware/arm_scmi/mailbox.c中定義
static const struct scmi_transport_ops scmi_mailbox_ops = {
.chan_available = mailbox_chan_available,
.chan_setup = mailbox_chan_setup,
.chan_free = mailbox_chan_free,
.send_message = mailbox_send_message,
.mark_txdone = mailbox_mark_txdone,
.fetch_response = mailbox_fetch_response,
.fetch_notification = mailbox_fetch_notification,
.clear_channel = mailbox_clear_channel,
.poll_done = mailbox_poll_done,
};
mailbox_send_message見3.2中分析
2.2 Reset provider
reset的provider是scmi-reset驅動,DTS中設置如下:
scmi_reset: protocol@16 {
reg = <0x16>;
#reset-cells = <1>;
};
代碼位置在:drivers/reset/reset-scmi.c
static struct scmi_driver scmi_reset_driver = {
.name = "scmi-reset",
.probe = scmi_reset_probe,
.id_table = scmi_id_table,
};
module_scmi_driver(scmi_reset_driver);
scmi_reset_probe的定義如下:
static int scmi_reset_probe(struct scmi_device *sdev)
{
reset_ops = handle->devm_protocol_get(sdev, SCMI_PROTOCOL_RESET, &ph);
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
data->rcdev.ops = &scmi_reset_ops;
data->rcdev.owner = THIS_MODULE;
data->rcdev.of_node = np;
data->rcdev.nr_resets = reset_ops->num_domains_get(ph);
data->ph = ph;
return devm_reset_controller_register(dev, &data->rcdev);//進行驅動注冊
}
handle->devm_protocol_get為scmi_devm_protocol_get,這里面發送了三條0x16的scmi消息 scmi_devm_protocol_get->scmi_get_protocol_instance
scmi_get_protocol_instance里面發送了三條0x16的scmi消息,來獲取reset的版本號,支持那些devices等信息
3. Linux SCMI reset通信
如上圖中,Linux通過非安全通道跟SCP交互。
3.1 SCMI reset協議初始化
系統初始化的時候會執行subsys_initcall(scmi_driver_init);在drivers/firmware/arm_scmi/driver.c中:
static int __init scmi_driver_init(void)
{
int ret;
/* Bail out if no SCMI transport was configured */
if (WARN_ON(!IS_ENABLED(CONFIG_ARM_SCMI_HAVE_TRANSPORT)))
return -EINVAL;
scmi_bus_init();
/* Initialize any compiled-in transport which provided an init/exit */
ret = scmi_transports_init();
if (ret)
return ret;
scmi_base_register();
scmi_clock_register();
scmi_perf_register();
scmi_power_register();
scmi_reset_register();
scmi_sensors_register();
scmi_voltage_register();
scmi_system_register();
return platform_driver_register(&scmi_driver);
}
scmi_driver的定義為:
static struct platform_driver scmi_driver = {
.driver = {
.name = "arm-scmi",
.suppress_bind_attrs = true,
.of_match_table = scmi_of_match,
.dev_groups = versions_groups,
},
.probe = scmi_probe,
.remove = scmi_remove,
};
drivers/firmware/arm_scmi/driver.c中scmi_probe函數
static int scmi_probe(struct platform_device *pdev)
{
ret = scmi_txrx_setup(info, dev, SCMI_PROTOCOL_BASE);
ret = scmi_xfer_info_init(info);、
ret = scmi_protocol_acquire(handle, SCMI_PROTOCOL_BASE);
scmi_txrx_setup中會調用mailbox_chan_setup函數
size = resource_size(&res);
smbox->shmem = devm_ioremap(dev, res.start, size);
smbox->chan = mbox_request_channel(cl, tx ? 0 : 1);
scmi_protocol_acquire()函數
int scmi_protocol_acquire(const struct scmi_handle *handle, u8 protocol_id)
{
return PTR_ERR_OR_ZERO(scmi_get_protocol_instance(handle, protocol_id));
}
scmi_get_protocol_instance
scmi_alloc_init_protocol_instance(info, proto);
ret = pi->proto->instance_init(&pi->ph);
drivers/firmware/arm_scmi/base.c中定義了instance_init
static int scmi_base_protocol_init(const struct scmi_protocol_handle *ph)
ret = ph->xops->version_get(ph, &version);
static const struct scmi_xfer_ops xfer_ops = {
.version_get = version_get,
.xfer_get_init = xfer_get_init,
.reset_rx_to_maxsz = reset_rx_to_maxsz,
.do_xfer = do_xfer,
.do_xfer_with_response = do_xfer_with_response,
.xfer_put = xfer_put,
};
version_get中進行scmi的發送
static int version_get(const struct scmi_protocol_handle *ph, u32 *version)
{
ret = xfer_get_init(ph, PROTOCOL_VERSION, 0, sizeof(*version), &t);
ret = do_xfer(ph, t);
xfer_put(ph, t);
xfer_get_init中進行了賦值xfer->hdr.id = msg_id; do_xfer進行了發送操作,之后等待回復
ret = info->desc->ops->send_message(cinfo, xfer);
/* And we wait for the response. */
timeout = msecs_to_jiffies(info->desc->max_rx_timeout_ms);
if (!wait_for_completion_timeout(&xfer->done, timeout)) {
dev_err(dev, "timed out in resp(caller: %pS)
",
(void *)_RET_IP_);
ret = -ETIMEDOUT;
}
send_message在drivers/firmware/arm_scmi/mailbox.c中定義
static const struct scmi_transport_ops scmi_mailbox_ops = {
.chan_available = mailbox_chan_available,
.chan_setup = mailbox_chan_setup,
.chan_free = mailbox_chan_free,
.send_message = mailbox_send_message,
.mark_txdone = mailbox_mark_txdone,
.fetch_response = mailbox_fetch_response,
.fetch_notification = mailbox_fetch_notification,
.clear_channel = mailbox_clear_channel,
.poll_done = mailbox_poll_done,
};
mbox_send_message就是mailbox提供的發消息接口函數,詳細介紹見3.2中
初始化的時候不僅初始化了scmi協議還調用了scmi_reset_register();注冊了0x16的scmi reset協議 在drivers/firmware/arm_scmi/reset.c中
static const struct scmi_protocol scmi_reset = {
.id = SCMI_PROTOCOL_RESET,
.owner = THIS_MODULE,
.instance_init = &scmi_reset_protocol_init,
.ops = &reset_proto_ops,
.events = &reset_protocol_events,
};
DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(reset, scmi_reset)
#define DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(name, proto)
static const struct scmi_protocol *__this_proto = &(proto);
int __init scmi_##name##_register(void)
{
return scmi_protocol_register(__this_proto);
}
3.2 SCMI reset消息收發
- A核先往某個指定共享內存空間buffer寫數據,然后寫入共享內存空間的地址信息到相應通道數據寄存器,mailbox觸發中斷給R核;
- M核(SCP)通過得到mailbox中斷,獲取共享內存相應offset,讀取buffer數據;
- M核(SCP)通過mailbox觸發中斷通知A核接收消息完畢。
PL320和MHU硬件的區別?
PL320帶傳輸數據和中斷功能,但是數據量比較小7*32bit。對于新的SoC來說數據傳輸基本都使用共享內存,PL320自帶的數據傳輸基本用不上了,所以其算過時了。新的MHU只保留了中斷功能,并且是1對1的集成,核間通信時成對出現,用幾個加幾個更加的靈活,PL320是一次32個通道集成進SoC的,也可能浪費。
我們以PL320為例,只使用其中斷,數據還是通過共享內存傳輸,驅動跟MHU原理差不多。關于PL320,可以參考ARM官網的文檔,后面會專門寫一個核間通信的專題介紹下。
在drivers/mailbox/mailbox.c中,mailbox_send_message發消息的時候會調用mbox_send_message
mailbox_send_message
--》mbox_send_message
--》msg_submit
static void msg_submit(struct mbox_chan *chan)
{
data = chan->msg_data[idx];
if (chan->cl->tx_prepare)
chan->cl->tx_prepare(chan->cl, data);
err = chan->mbox->ops->send_data(chan, data);
}
tx_prepare-》shmem_tx_prepare會往共享內存里面存入數據,在drivers/firmware/arm_scmi/shmem.c中
void shmem_tx_prepare(struct scmi_shared_mem __iomem *shmem,
struct scmi_xfer *xfer)
{
spin_until_cond(ioread32(&shmem->channel_status) &
SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE);
iowrite32(0x0, &shmem->channel_status);
iowrite32(xfer->hdr.poll_completion ? 0 : SCMI_SHMEM_FLAG_INTR_ENABLED,
&shmem->flags);
iowrite32(sizeof(shmem->msg_header) + xfer->tx.len, &shmem->length);
iowrite32(pack_scmi_header(&xfer->hdr), &shmem->msg_header);
pr_info("#### shmem_tx_prepare shmem->msg_header=0x%x
", shmem->msg_header);
if (xfer->tx.buf){
memcpy_toio(shmem->msg_payload, xfer->tx.buf, xfer->tx.len);
pr_info("#### shmem_tx_prepare shmem->msg_payload[0]=0x%x
", (int)shmem->msg_payload[0]);
}
}
發消息drivers/mailbox/pl320-ipc.c中pl320_mbox_send_data函數
static const struct mbox_chan_ops pl320_mbox_ops = {
.send_data = pl320_mbox_send_data,
};
ops->send_data-》pl320_mbox_send_data-》__ipc_send(pl320_id, ch, buf);會觸發中斷
static void __ipc_send(int pl320_id, int mbox, u32 *data)
{
ipc_base = get_ipc_base(pl320_id);
for (i = 0; i < MBOX_MSG_LEN; i++)
writel_relaxed(data[i], ipc_base + IPCMxDR(mbox, i));
if (mbox % 2 == 0)
writel_relaxed(0x1, ipc_base + IPCMxSEND(mbox));
else
writel_relaxed(0x2, ipc_base + IPCMxSEND(mbox));
}
收消息,drivers/mailbox/pl320-ipc.c中ipc_handler
for (idx = 0; idx < MBOX_CHAN_MAX; idx++)
if (irq_stat & (1 << idx))
receive_flag |= channel_handler(mbox, idx);
channel_handler中會清中斷
4. SCP中reset
4.1 固件新增module
- 根據我們的需求需要處理0x16 scmi reset domain協議,所以在scmi module之后需要新加協議層scmi-reset-domain
- 根據SCP固件的分層結構,協議層之后需要添加HAL層和驅動層
綜上,需要在product/juno/scp_ramfw/CMakeLists.txt中新加如下module模塊:
if(SCP_ENABLE_SCMI_RESET)
target_sources(
juno-bl2
PRIVATE
"${CMAKE_CURRENT_SOURCE_DIR}/config_reset_domain.c"
"${CMAKE_CURRENT_SOURCE_DIR}/config_scmi_reset_domain.c"
"${CMAKE_CURRENT_SOURCE_DIR}/config_juno_reset_domain.c")
endif()
if(SCP_ENABLE_SCMI_RESET)
list(APPEND SCP_MODULES "reset-domain")
list(APPEND SCP_MODULES "scmi-reset-domain")
list(APPEND SCP_MODULES "juno-reset-domain")
endif()
打開SCP_ENABLE_SCMI_RESET宏,在product/juno/scp_ramfw/Firmware.cmake中 set(SCP_ENABLE_SCMI_RESET TRUE) set(SCP_ENABLE_SCMI_RESET_INIT TRUE)
4.2 scmi_reset_domain初始化
新增scmi_reset_domain協議module后,首先初始化的時候需要向scmi注冊,這樣當收到scmi消息的時候,會根據scmi協議號0x16進行協議分發處理。
module/scmi_reset_domain/src/mod_scmi_reset_domain.c中初始化會執行.bind = scmi_reset_bind,函數
status = fwk_module_bind(FWK_ID_MODULE(FWK_MODULE_IDX_SCMI),
FWK_ID_API(FWK_MODULE_IDX_SCMI,
MOD_SCMI_API_IDX_PROTOCOL),
&scmi_rd_ctx.scmi_api);
去綁定scmi模塊,在scmi中執行.process_bind_request = scmi_process_bind_request,
scmi_ctx.protocol_table[PROTOCOL_TABLE_RESERVED_ENTRIES_COUNT +
scmi_ctx.protocol_count++].id = source_id;
*api = &scmi_from_protocol_api;
會填充scmi_ctx.protocol_table,之后.bind = scmi_bind,執行
for (protocol_idx = 0;
protocol_idx < scmi_ctx.protocol_count; protocol_idx++) {
protocol = &scmi_ctx.protocol_table[
PROTOCOL_TABLE_RESERVED_ENTRIES_COUNT + protocol_idx];
//根據module信息進行綁定,拿到api
status = fwk_module_bind(protocol->id,
FWK_ID_API(fwk_id_get_module_idx(protocol->id), 0), &protocol_api);
//使用拿到的api獲取scmi協議id號
status = protocol_api->get_scmi_protocol_id(protocol->id,
&scmi_protocol_id);
FWK_LOG_INFO("[SCMI] Support scmi_protocol_id:0x%x", scmi_protocol_id));
scmi_ctx.scmi_protocol_id_to_idx[scmi_protocol_id] =
(uint8_t)(protocol_idx + PROTOCOL_TABLE_RESERVED_ENTRIES_COUNT);
protocol->message_handler = protocol_api->message_handler;
}
protocol 是scmi協議的module,首先綁定這個module拿到兩個api
static struct mod_scmi_to_protocol_api scmi_reset_mod_scmi_to_protocol_api = {
.get_scmi_protocol_id = scmi_reset_get_scmi_protocol_id,
.message_handler = scmi_reset_message_handler
};
scmi_reset_get_scmi_protocol_id為獲取協議id
/*!
* rief SCMI Reset Domain Protocol
*/
#define MOD_SCMI_PROTOCOL_ID_RESET_DOMAIN UINT32_C(0x16)
static int scmi_reset_get_scmi_protocol_id(fwk_id_t protocol_id,
uint8_t *scmi_protocol_id)
{
*scmi_protocol_id = MOD_SCMI_PROTOCOL_ID_RESET_DOMAIN;
return FWK_SUCCESS;
}
4.3 scmi_reset_domain消息處理
協議模塊負責處理reset相關的所有協議子命令,對于scmi_reset_domain一共支持6個子命令,如下:
enum scmi_command_id {
MOD_SCMI_PROTOCOL_VERSION = 0x000,
MOD_SCMI_PROTOCOL_ATTRIBUTES = 0x001,
MOD_SCMI_PROTOCOL_MESSAGE_ATTRIBUTES = 0x002
};
enum scmi_reset_domain_command_id {
MOD_SCMI_RESET_DOMAIN_ATTRIBUTES = 0x03,
MOD_SCMI_RESET_REQUEST = 0x04,
MOD_SCMI_RESET_NOTIFY = 0x05,
MOD_SCMI_RESET_COMMAND_COUNT,
};
我們需要在協議模塊scmi_reset_domain中,給這些命令設計處理函數如下:
static int (*msg_handler_table[])(fwk_id_t, const uint32_t *) = {
[MOD_SCMI_PROTOCOL_VERSION] = protocol_version_handler,
[MOD_SCMI_PROTOCOL_ATTRIBUTES] = protocol_attributes_handler,
[MOD_SCMI_PROTOCOL_MESSAGE_ATTRIBUTES] =
protocol_message_attributes_handler,
[MOD_SCMI_RESET_DOMAIN_ATTRIBUTES] = reset_attributes_handler,
[MOD_SCMI_RESET_REQUEST] = reset_request_handler,
#ifdef BUILD_HAS_SCMI_NOTIFICATIONS
[MOD_SCMI_RESET_NOTIFY] = reset_notify_handler,
#endif
};
我們以為reset_request_handler例,進行說明
static struct mod_scmi_to_protocol_api scmi_reset_mod_scmi_to_protocol_api = {
.get_scmi_protocol_id = scmi_reset_get_scmi_protocol_id,
.message_handler = scmi_reset_message_handler
};
scmi_reset_message_handler()函數中會根據命令id找到處理函數執行 msg_handler_table[message_id](service_id, payload); message_id就是cmd id,payload就是協議攜帶的數據部分。
[MOD_SCMI_RESET_REQUEST] = reset_request_handler,
reset_request_handler中會解析payload,并對payload的數據大小進行校驗,然后進行解析
struct scmi_reset_domain_request_a2p {
uint32_t domain_id;
uint32_t flags;
uint32_t reset_state;
};
params = *(const struct scmi_reset_domain_request_a2p *)payload;
status = get_reset_device(service_id, params.domain_id, &reset_device);
status = scmi_reset_domain_reset_request_policy(&policy_status,
&mode, &reset_state, agent_id, params.domain_id);
status = reset_api->set_reset_state(reset_device->element_id,
mode,
reset_state,
(uintptr_t)agent_id);
reset_api->set_reset_state是從HAL層拿到的api
4.3.2 reset-domain HAL層
/* HAL API */
static const struct mod_reset_domain_drv_api reset_api = {
.set_reset_state = set_reset_state,
};
static int set_reset_state(fwk_id_t reset_dev_id,
enum mod_reset_domain_mode mode,
uint32_t reset_state,
uintptr_t cookie)
{
struct rd_dev_ctx *reset_ctx;
unsigned int reset_domain_idx = fwk_id_get_element_idx(reset_dev_id);
FWK_LOG_INFO("[RESET DOMAIN] set_reset_state");
reset_ctx = &module_reset_ctx.dev_ctx_table[reset_domain_idx];
return reset_ctx->driver_api->set_reset_state(reset_ctx->config->driver_id,
mode, reset_state, cookie);
}
從driver層拿到api進行處理
4.3.3 juno-reset-domain驅動層
static struct mod_reset_domain_drv_api juno_reset_domain_drv_api = {
.set_reset_state = juno_set_reset_state,
};
static int juno_set_reset_state(
fwk_id_t dev_id,
enum mod_reset_domain_mode mode,
uint32_t reset_state,
uintptr_t cookie)
{
unsigned int domain_idx = fwk_id_get_element_idx(dev_id);
dev_ctx = &module_juno_reset_ctx.dev_ctx_table[domain_idx];
if (domain_idx == juno_RESET_DOMAIN_IDX_NPU) {
status = handle_dev_reset_set_state(dev_ctx);
if (status != FWK_SUCCESS) {
return status;
}
}
return FWK_SUCCESS;
}
handle_dev_reset_set_state里面處理具體的硬件寄存器操作:
/* Helper functions */
static int handle_dev_reset_set_state(struct juno_reset_dev_ctx *dev_ctx)
{
/* Reset device */
dev_ctx->reset_state = DEVICE_STATE_RESET;
*dev_config->reset_reg = 0;
for (int j = 0; j < 10000; j++)
;
*dev_config->reset_reg = 1;
*dev_config->clkctl_reg = 1;
dev_ctx->reset_state = DEVICE_STATE_NORMAL;
根據2.4章節中CRU硬件,這里需要對reset寄存器和clk寄存器進行寫操作來實現其他硬件模塊的reset功能。
5. 硬件CRU設計
CRU(Clock & Reset Unit)位于SCP子系統中,受SCP軟件控制,然后硬件信號會連接到其他子系統上,我們的SCP固件一般運行在ARM的M核心上,還有一堆外圍的器件,例如NXP的imx8qm里面:
將clock gate信號和reset信號送給多個子系統(有時鐘控制、reset控制等需求的子系統)。通過SCP軟件來操作CRU的相關寄存器,從而實現對子系統時鐘和復位信號的控制,
芯片手冊會給出控制CRU的reset及clk的寄存器配置,操作響應的寄存器即可。
后記:
本小節介紹比較詳細,其實很多知識點都是相通的,例如SCMI、SCP、Mailbox、DTS這些東西,早晚都需要掌握,但是通過一個業務流程或者場景就可以學習到,本文就是一個了解這些知識的機會。
-
ARM
+關注
關注
134文章
9084瀏覽量
367384 -
寄存器
+關注
關注
31文章
5336瀏覽量
120230 -
電源管理
+關注
關注
115文章
6177瀏覽量
144444 -
通信
+關注
關注
18文章
6024瀏覽量
135950
原文標題:電源管理入門-5 arm-scmi和mailbox核間通信
文章出處:【微信號:OS與AUTOSAR研究,微信公眾號:OS與AUTOSAR研究】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論