0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

電源管理入門-5 arm-scmi和mailbox核間通信

yzcdx ? 來源:OS與AUTOSAR研究 ? 2023-10-16 17:09 ? 次閱讀

?上篇介紹了電源管理入門-4子系統(tǒng)reset,提到子系統(tǒng)reset的執(zhí)行為了安全可以到SCP里面去執(zhí)行,但是怎么把這個消息傳遞過去呢,答案就是mailbox。

Mailbox是核間通信硬件的統(tǒng)稱。

  • 在軟件上可以使用SCMI協(xié)議+共享內(nèi)存報文頭,
  • 在硬件上可以使用PL320或者MHU。

1. 整體架構(gòu)介紹

fedb1840-6bf8-11ee-939d-92fbcf53809c.png

Reset系統(tǒng)架構(gòu)框圖

上圖以NPU子模塊的服務(wù)為例子,Mailbox的硬件使用PL320,整體流程如下:

  1. Reset consumer模塊執(zhí)行devm_reset_control_get()獲取npu_reset復(fù)位句柄,然后通過reset_control_reset()觸發(fā)復(fù)位
  2. Linux系統(tǒng)reset framework找到復(fù)位驅(qū)動并執(zhí)行ops->reset()回調(diào)函數(shù)
  3. scmi-reset驅(qū)動里面提供.reset函數(shù)的實現(xiàn)scmi_reset_deassert(),并執(zhí)行
  4. arm-scmi里面提供scmi reset協(xié)議的實現(xiàn)模塊reset,里面提供reset函數(shù)scmi_reset_domain_reset()
  5. arm-scmi里面提供scmi協(xié)議收發(fā)的框架driver,提供do_xfer()
  6. arm-scmi里面提供mailbox的接口函數(shù)mailbox_send_message()
  7. arm-scmi里面提供共享內(nèi)存的操作函數(shù)shmem_tx_prepare()
  8. mailbox驅(qū)動里面提供硬件PL320的寄存器操作實現(xiàn)pl320_mbox_send_data()
  9. SCP中PL320驅(qū)動模塊接收mailbox中斷
  10. SCP中SMT模塊從共享內(nèi)存中讀取SCMI報文數(shù)據(jù)
  11. SCP中SCMI模塊對SCMI協(xié)議報文進行解析,并進行分發(fā)處理
  12. SCP中SCMI RESET DOMAIN協(xié)議模塊對報文進行功能處理
  13. SCP中RESET DOMAIN模塊屏蔽硬件差異實現(xiàn)統(tǒng)一API
  14. SCP中JUNO RESET DOMAIN模塊提供具體硬件CRU寄存器操作實現(xiàn)

2 Linux中reset模塊

2.1 Reset consumer

之前的文章電源管理入門-4子系統(tǒng)reset介紹了怎么使用Linux的reset子系統(tǒng),這里我們就直接使用,需要在DTS中修改即可。

reset使用Linux自帶的reset框架,假定consumer-firmware-npu這個驅(qū)動要使用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中驅(qū)動需要使用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"復(fù)位句柄
--》consumer_fw_firmware_cb 
  --》consumer_fw_memcpy //拷貝鏡像
  --》consumer_control_reset //通知reset驅(qū)動進行reset

這樣DTS探測到consumer_firmware的時候就會觸發(fā)reset操作。

reset_control_reset
    rstc->rcdev->ops->reset(rstc->rcdev, rstc->id);

reset的provider驅(qū)動使用compatible = "scmi-reset";驅(qū)動,詳細見后面2.2reset provider中分析。當reset時在drivers/reset/reset-scmi.c中實現(xiàn)

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協(xié)議初始化的時候,scmi_reset_register會注冊0x16的回調(diào)函數(shù),詳細分析見2.2.1SCMI reset協(xié)議初始化內(nèi)容。在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中實現(xiàn)

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驅(qū)動,DTS中設(shè)置如下:

    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);//進行驅(qū)動注冊
}

handle->devm_protocol_get為scmi_devm_protocol_get,這里面發(fā)送了三條0x16的scmi消息 scmi_devm_protocol_get->scmi_get_protocol_instance

scmi_get_protocol_instance里面發(fā)送了三條0x16的scmi消息,來獲取reset的版本號,支持那些devices等信息

3. Linux SCMI reset通信

ff0cc872-6bf8-11ee-939d-92fbcf53809c.png

如上圖中,Linux通過非安全通道跟SCP交互。

3.1 SCMI reset協(xié)議初始化

系統(tǒng)初始化的時候會執(zhí)行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函數(shù)

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中會調(diào)用mailbox_chan_setup函數(shù)

    size = resource_size(&res);
    smbox->shmem = devm_ioremap(dev, res.start, size);
    smbox->chan = mbox_request_channel(cl, tx ? 0 : 1);

scmi_protocol_acquire()函數(shù)

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的發(fā)送

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進行了發(fā)送操作,之后等待回復(fù)

    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提供的發(fā)消息接口函數(shù),詳細介紹見3.2

初始化的時候不僅初始化了scmi協(xié)議還調(diào)用了scmi_reset_register();注冊了0x16的scmi reset協(xié)議 在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消息收發(fā)

ff2aa0c2-6bf8-11ee-939d-92fbcf53809c.png

  1. A核先往某個指定共享內(nèi)存空間buffer寫數(shù)據(jù),然后寫入共享內(nèi)存空間的地址信息到相應(yīng)通道數(shù)據(jù)寄存器,mailbox觸發(fā)中斷給R核;
  2. M核(SCP)通過得到mailbox中斷,獲取共享內(nèi)存相應(yīng)offset,讀取buffer數(shù)據(jù);
  3. M核(SCP)通過mailbox觸發(fā)中斷通知A核接收消息完畢。

PL320和MHU硬件的區(qū)別?

PL320帶傳輸數(shù)據(jù)和中斷功能,但是數(shù)據(jù)量比較小7*32bit。對于新的SoC來說數(shù)據(jù)傳輸基本都使用共享內(nèi)存,PL320自帶的數(shù)據(jù)傳輸基本用不上了,所以其算過時了。新的MHU只保留了中斷功能,并且是1對1的集成,核間通信時成對出現(xiàn),用幾個加幾個更加的靈活,PL320是一次32個通道集成進SoC的,也可能浪費。

我們以PL320為例,只使用其中斷,數(shù)據(jù)還是通過共享內(nèi)存?zhèn)鬏?,?qū)動跟MHU原理差不多。關(guān)于PL320,可以參考ARM官網(wǎng)的文檔,后面會專門寫一個核間通信的專題介紹下。

在drivers/mailbox/mailbox.c中,mailbox_send_message發(fā)消息的時候會調(diào)用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會往共享內(nèi)存里面存入數(shù)據(jù),在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]);
        }
}

發(fā)消息drivers/mailbox/pl320-ipc.c中pl320_mbox_send_data函數(shù)

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);會觸發(fā)中斷

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

ff41f092-6bf8-11ee-939d-92fbcf53809c.png

4.1 固件新增module

  • 根據(jù)我們的需求需要處理0x16 scmi reset domain協(xié)議,所以在scmi module之后需要新加協(xié)議層scmi-reset-domain
  • 根據(jù)SCP固件的分層結(jié)構(gòu),協(xié)議層之后需要添加HAL層和驅(qū)動層

綜上,需要在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協(xié)議module后,首先初始化的時候需要向scmi注冊,這樣當收到scmi消息的時候,會根據(jù)scmi協(xié)議號0x16進行協(xié)議分發(fā)處理。

ff55bcf8-6bf8-11ee-939d-92fbcf53809c.png

module/scmi_reset_domain/src/mod_scmi_reset_domain.c中初始化會執(zhí)行.bind = scmi_reset_bind,函數(shù)

    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中執(zhí)行.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,執(zhí)行

    for (protocol_idx = 0;
         protocol_idx < scmi_ctx.protocol_count; protocol_idx++) {
        protocol = &scmi_ctx.protocol_table[
            PROTOCOL_TABLE_RESERVED_ENTRIES_COUNT + protocol_idx];
        //根據(jù)module信息進行綁定,拿到api
        status = fwk_module_bind(protocol->id,
            FWK_ID_API(fwk_id_get_module_idx(protocol->id), 0), &protocol_api);
        //使用拿到的api獲取scmi協(xié)議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協(xié)議的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為獲取協(xié)議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消息處理

協(xié)議模塊負責(zé)處理reset相關(guān)的所有協(xié)議子命令,對于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,
};

我們需要在協(xié)議模塊scmi_reset_domain中,給這些命令設(shè)計處理函數(shù)如下:

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例,進行說明

ff696b7c-6bf8-11ee-939d-92fbcf53809c.png

4.3.1 scmi_reset_domain中處理
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()函數(shù)中會根據(jù)命令id找到處理函數(shù)執(zhí)行 msg_handler_table[message_id](service_id, payload); message_id就是cmd id,payload就是協(xié)議攜帶的數(shù)據(jù)部分。

[MOD_SCMI_RESET_REQUEST] = reset_request_handler,

reset_request_handler中會解析payload,并對payload的數(shù)據(jù)大小進行校驗,然后進行解析

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驅(qū)動層

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;

根據(jù)2.4章節(jié)中CRU硬件,這里需要對reset寄存器和clk寄存器進行寫操作來實現(xiàn)其他硬件模塊的reset功能。

5. 硬件CRU設(shè)計

CRU(Clock & Reset Unit)位于SCP子系統(tǒng)中,受SCP軟件控制,然后硬件信號會連接到其他子系統(tǒng)上,我們的SCP固件一般運行在ARM的M核心上,還有一堆外圍的器件,例如NXPimx8qm里面:

ff7c0aa2-6bf8-11ee-939d-92fbcf53809c.png

將clock gate信號和reset信號送給多個子系統(tǒng)(有時鐘控制、reset控制等需求的子系統(tǒng))。通過SCP軟件來操作CRU的相關(guān)寄存器,從而實現(xiàn)對子系統(tǒng)時鐘和復(fù)位信號的控制,

ff8b404e-6bf8-11ee-939d-92fbcf53809c.png

芯片手冊會給出控制CRU的reset及clk的寄存器配置,操作響應(yīng)的寄存器即可。

后記:

本小節(jié)介紹比較詳細,其實很多知識點都是相通的,例如SCMI、SCP、Mailbox、DTS這些東西,早晚都需要掌握,但是通過一個業(yè)務(wù)流程或者場景就可以學(xué)習(xí)到,本文就是一個了解這些知識的機會。

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • ARM
    ARM
    +關(guān)注

    關(guān)注

    134

    文章

    8967

    瀏覽量

    365041
  • 寄存器
    +關(guān)注

    關(guān)注

    31

    文章

    5253

    瀏覽量

    119205
  • 電源管理
    +關(guān)注

    關(guān)注

    115

    文章

    6100

    瀏覽量

    143368
  • 通信
    +關(guān)注

    關(guān)注

    18

    文章

    5880

    瀏覽量

    135319

原文標題:電源管理入門-5 arm-scmi和mailbox核間通信

文章出處:【微信號:OS與AUTOSAR研究,微信公眾號:OS與AUTOSAR研究】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦

    SoC的通信機制—mailbox介紹

    目前很多芯片都會有幾個core,有的是Cortex M0+、M4、M7、A53、A73等等,有的有著2、3、4甚至68
    發(fā)表于 08-11 10:51 ?4761次閱讀
    SoC的<b class='flag-5'>核</b><b class='flag-5'>間</b><b class='flag-5'>通信</b>機制—<b class='flag-5'>mailbox</b>介紹

    電源管理入門-芯片設(shè)計中的電源管理介紹

    SCP直接控制SoC的電源和時鐘,而AP通過硬件和軟件接口協(xié)同管理。
    的頭像 發(fā)表于 12-06 09:16 ?2448次閱讀
    <b class='flag-5'>電源</b><b class='flag-5'>管理入門</b>-芯片設(shè)計中的<b class='flag-5'>電源</b><b class='flag-5'>管理</b>介紹

    電源管理入門:Hypervisor中的電源管理

    很多時候聽說Hypervisor,但是對底層軟件技術(shù)不了解的人感覺挺神秘。本篇文章簡單介紹下Hypervisor的基本概念,另外介紹下電源管理在Hypervisor之上多OS怎么應(yīng)用。
    的頭像 發(fā)表于 12-06 09:27 ?1179次閱讀
    <b class='flag-5'>電源</b><b class='flag-5'>管理入門</b>:Hypervisor中的<b class='flag-5'>電源</b><b class='flag-5'>管理</b>

    關(guān)于AM5728通信方式(共享內(nèi)存)問題

    您好,我使用的開發(fā)板為AM5728,目前要在兩個ARM-Cotex-A15和兩個DSP上都應(yīng)用OS,初步定為都運行SYS/BIOS,在這種情況下我們想實現(xiàn)實時、快速、可靠的通信
    發(fā)表于 05-15 07:34

    搞定單CPU內(nèi)雙通信和雙CPU通信

    [table][tr][td] 終于搞定了單CPU內(nèi)雙通信和雙CPU通信 +-----+ +
    發(fā)表于 08-19 01:37

    ARM電源管理系列資料匯總(一)

    管理 SoC 中其它設(shè)備的電源。對于 PPU 在 SoC 中的集成,可以在前面 PCSA 的文章中看到,今天重點是看一下 PPU 里面到底包含哪些內(nèi)容。2、ARM系列之SCMI軟件接
    發(fā)表于 03-30 14:17

    通信(IPC)解決方案

    以運行在不同的模式,比如A72可以運行的SMP模式,雙R5F可以運行在Lockstep或Split Mode上,因此,通信方案要充分利
    發(fā)表于 11-03 07:26

    使用ARM SCMI規(guī)范的電源和性能管理

    本白皮書概述了ARM系統(tǒng)控制和管理接口(SCMI)規(guī)范。 它描述了啟用SCMI的系統(tǒng)如何通過操作系統(tǒng)和系統(tǒng)控制器之間的抽象和職責(zé)劃分的組合來優(yōu)化電源
    發(fā)表于 08-23 07:36

    一種基于Mailbox機制的多核處理系統(tǒng)

    基于FPGA的嵌入式應(yīng)用在近幾年來作為一個比較新穎的課題,本文在研究各種通信機制的基礎(chǔ)上,提出了一種基于Mailbox
    發(fā)表于 11-22 17:31 ?1.3w次閱讀

    Jacinto?? 7通信解決方案

    Jacinto?? 7通信解決方案
    發(fā)表于 10-28 11:59 ?3次下載
    Jacinto?? 7<b class='flag-5'>核</b><b class='flag-5'>間</b><b class='flag-5'>通信</b>解決方案

    Jacinto 7通信解決方案

    近年來,處理器性能越來越強,無論是通用處理器還是嵌入式處理器,都進入了多核處理器時代,多核處理器中,每個核心不能獨立工作,需要協(xié)同工作才能充分發(fā)揮處理器的性能,也就是需要高效的通信
    的頭像 發(fā)表于 03-16 09:19 ?1066次閱讀
    Jacinto 7<b class='flag-5'>核</b><b class='flag-5'>間</b><b class='flag-5'>通信</b>解決方案

    SoC通信的實現(xiàn)機制之mailbox中斷

    目前很多芯片都會有幾個core,有的是Cortex M0+、M4、M7、A53、A73等等,有的有著2、3、4甚至68
    的頭像 發(fā)表于 09-05 17:29 ?1417次閱讀
    SoC<b class='flag-5'>核</b><b class='flag-5'>間</b><b class='flag-5'>通信</b>的實現(xiàn)機制之<b class='flag-5'>mailbox</b>中斷

    通信(IPC)的目標和實現(xiàn)機制

    的應(yīng)用程序提供簡潔高效的編程接口。 根據(jù)所使用的硬件特性,通信的實現(xiàn)機制有: ? ·Mailbox中斷 ? ·基于共享內(nèi)
    的頭像 發(fā)表于 09-13 17:32 ?2653次閱讀
    <b class='flag-5'>核</b><b class='flag-5'>間</b><b class='flag-5'>通信</b>(IPC)的目標和實現(xiàn)機制

    通信可能的實現(xiàn)機制

    理解為軟件可自由定義的中斷模塊。 用于在片上處理器之間通信的一種mailbox隊列中斷機制,mailbox隊列中斷機制允許軟件通過一組寄存器和關(guān)聯(lián)的中斷設(shè)置和得到信息在二個處理之間建立通信
    的頭像 發(fā)表于 09-13 17:39 ?1146次閱讀
    <b class='flag-5'>核</b><b class='flag-5'>間</b><b class='flag-5'>通信</b>可能的實現(xiàn)機制

    使用mailbox和MUTEX的處理器通信解決方案

    現(xiàn)在業(yè)界的許多解決方案都包含多個處理器,或者是硬核處理器,如Arm A9、A53或R5,軟如MicroBlaze、Arm Cortex-M1/M3,或者是兩者的組合。
    的頭像 發(fā)表于 09-21 15:23 ?888次閱讀
    使用<b class='flag-5'>mailbox</b>和MUTEX的處理器<b class='flag-5'>間</b><b class='flag-5'>通信</b>解決方案