SoC上有很多核,ATF和Linux占據(jù)了A核,SCP占據(jù)了一個M核,當遇到Linux沒有權限的事情的時候(SMC進入EL3轉(zhuǎn)PSCI協(xié)議,例如電源管理),就需要給SCP打報告,SCP審批完批條子后去執(zhí)行。這其中涉及到了異構核間通信,估計第一時間會想到mailbox,不過mailbox算是一個傳輸層,面向的是bit位數(shù)據(jù)的傳輸,可以把這些傳輸數(shù)據(jù)組織成一個協(xié)議層,在AP與SCP的核間通信中那就是SCMI。
1. SMC系統(tǒng)調(diào)用與PSCI協(xié)議
當Linux想要關機或者休眠的時候,這涉及到整個系統(tǒng)電源狀態(tài)的變化,為了安全性Linux內(nèi)核沒有權利去直接執(zhí)行了,需要陷入到EL3等級去執(zhí)行,可以參考之前文章ARM ATF入門-安全固件軟件介紹和代碼運行,在EL3中處理的程序是BL31,把SMC系統(tǒng)調(diào)用的參數(shù)轉(zhuǎn)化為PSCI協(xié)議去執(zhí)行,這時如果有SCP那A核就憋屈了,自己沒權利執(zhí)行需要通過SCMI協(xié)議上報給SCP了。這就是整個過程的軟件協(xié)議棧如上圖中:- 用戶層:首先用戶發(fā)起的一些操作,通過用戶空間的各service處理,會經(jīng)過內(nèi)核提供的sysfs,操作cpu hotplug、device pm、EAS、IPA等。
- 內(nèi)核層:在linux內(nèi)核中,EAS(energy aware scheduling)通過感知到當前的負載及相應的功耗,經(jīng)過cpuidle、cpu dvfs及調(diào)度選擇idle等級、cpu頻率及大核或者小核上運行。IPA(intrlligent power allocation)經(jīng)過與EAS的交互,做熱相關的管理。
- ATF層:Linux kernel中發(fā)起的操作,會經(jīng)過電源狀態(tài)協(xié)調(diào)接口(Power State Coordination Interface,簡稱PSCI),由操作系統(tǒng)無關的framework(ARM Trusted Firmware,簡稱ATF)做相關的處理后,通過系統(tǒng)控制與管理接口(System Control and Management Interface,簡稱SCMI),向系統(tǒng)控制處理器(system controlprocessor,簡稱SCP)發(fā)起低功耗操作。
- SCP層:SCP(系統(tǒng)控制處理器system control processor)最終會控制芯片上的sensor、clock、power domain、及板級的pmic做低功耗相關的處理。
static void psci_sys_poweroff(void)
{
invoke_psci_fn(PSCI_0_2_FN_SYSTEM_OFF, 0, 0, 0);
}
PSCI_0_2_FN_SYSTEM_OFF的值計算為:0x84000000+8,在規(guī)范的表6-2:分配給不同服務的功能標識符的子范圍中,
表中的各種功能就是走安全通道的,不是SMC或者HVC命令的功能就是非安全通道的,當然也可以根據(jù)自己的需求選擇,一般PSCI協(xié)議中的功能都是走安全通道。
1.2 PSCI協(xié)議PSCI協(xié)議官方地址:https://developer.arm.com/documentation/den0022/d/《Power_State_Coordination_Interface_PDD_v1_1_DEN0022D》本文檔定義了一個電源管理的標準接口,操作系統(tǒng)供應商可用于在ARM設備上使用不同特權級別的監(jiān)控軟件。該接口旨在在以下電源管理場景中代碼通用化:- 內(nèi)核空閑管理。
- 動態(tài)添加和刪除核心,以及輔助核心引導。
- 系統(tǒng)關閉和復位。
- 空閑管理:當操作系統(tǒng)中的內(nèi)核在核心上沒有線程可以調(diào)度時,它會將該核心置于時鐘門控、保留狀態(tài),甚至是完全電源門控狀態(tài)。然而,該核心仍然可用于操作系統(tǒng)。
- 熱插拔:當計算需求低時,核心會物理關閉,當需求增加時恢復在線。該操作系統(tǒng)將遷移所有遠離離線的核心的中斷和線程,并在它們重新聯(lián)機時重新平衡負載。
比如關機就是5.10里面的內(nèi)容。
2. SCMI協(xié)議現(xiàn)在繼續(xù)聊SCP里面的東西,上來就是SCMI協(xié)議,同樣還是去ARM官網(wǎng)找:《DEN0056B_System_Control_and_Management_Interface_v2_0》這個協(xié)議在哪里用到,我們來看一個圖:
SCP會以服務的方式來支持AP參與運行管理,這也就需要SCP和AP之間有一個通信接口。這個通信接口在硬件上可以通過共享存儲和MHU(Message HandlingUnit)實現(xiàn);在軟件上,通過定義一組通信協(xié)議來實現(xiàn)。
主要涉及的模塊如下:
-
mhu模塊:Message HandlingUnit (MHU)在module/mhu/src/mod_mhu.c中實現(xiàn)
-
msg_smt模塊:Shared MemoryTransport 是一種用于描述系統(tǒng)內(nèi)存拓撲的數(shù)據(jù)結構。在ARM 架構中,SCP 固件使用 Shared MemoryTransport來提供有關系統(tǒng)內(nèi)存的信息,如地址范圍、類型、屬性等。SystemMemory Tables 通常由系統(tǒng)固件在啟動過程中生成,并由SCP 固件和其他系統(tǒng)組件使用。它們允許系統(tǒng)軟件了解和管理系統(tǒng)中可用的內(nèi)存資源。
-
SCMI模塊:System Control &Management Interface (SCMI)
-
業(yè)務處理模塊,為scmi protocol模塊例如scmi_power_domain
協(xié)議層:
-
通道(channel)必須是分開獨立的,各個agent不能使用同一個。避免platform無法識別message對應方
-
agent必須是獨立的操作系統(tǒng)
-
通道支持雙向通訊,另外也能夠支持中斷、polling兩種方式,讓agent選擇
-
同步(synchronous),agent返回的時候?qū)?span lang="en-us" style="font-family:Arial, sans-serif;" xml:lang="en-us">platform操作就已經(jīng)完成了。platform返回操作結果命令也是通過agent到platform的通道,同一個通道完成這些操作
-
異步(asynchronoous),當platform完成后,會發(fā)送 delayed response給到agent告知對方工作完成,這是P2A通道。agent發(fā)送完消息后,立馬得到platform的返回,然后釋放通道繼續(xù)做下一次傳輸
scmi transport,channel,agent的對應關系:
1.一個scp可以有多個agent,agent是運行在操作系統(tǒng),安全固件的軟件或者一個使用scmi協(xié)議的設備。例如juno有如下代理,0保留給平臺。
|
enum juno_scmi_agent_idx { /* 0 is reserved for the platform */ JUNO_SCMI_AGENT_IDX_OSPM = 1, JUNO_SCMI_AGENT_IDX_PSCI, JUNO_SCMI_AGENT_IDX_COUNT, };
|
SCMI協(xié)議的message header定義如下,對應代碼module/scmi/include/mod_scmi_std.h中定義
[protocol_id]:
[message id]:
message id是二級功能區(qū)分id算cmd,例如設置狀態(tài)、獲取狀態(tài)等具體操作。如果有新增的協(xié)議,那里面0/1/2這三個message都必須按照協(xié)議走。
?[message type]:
Commands 的message type都是0。對于不支持的協(xié)議和message類型,platform都要回復 NOT_SUPPORTED
Delayed responses 類型都是2
Notifications 為3
傳輸層:
傳輸層文檔也就定義了一種方式,mailbox方式(核間通訊的一種ip)。這種通訊的前提是系統(tǒng)能夠在agents和platform之間存在共享內(nèi)存(ddr和片上flash都行,最好是片上flash)。mailebox能夠完美支持前面提到的通道的需求,中斷、內(nèi)存和完成中斷等都能夠,而且是軟件可操控。比如下面流程指出的中斷和polling方式:
?mailbox通訊怎么定義在flash里面的layout:
?3. Agent scmi消息處理流程這里我們以一個protocol_id為0x11的power domain控制消息為例子進行說明:
scp中scmi消息處理時序圖
1.mhu模塊-中斷產(chǎn)生:scmi底層硬件對應的模塊是mhu模塊,當硬件收到agent的消息時候會產(chǎn)生中斷,中斷處理函數(shù)為mhu_isr。在該函數(shù)中通過中斷源查表獲取對應的設備和smtchannel。然后調(diào)用transport模塊的api(調(diào)用transport_channel->api->signal_message(transport_channel->id);)發(fā)送消息。
2.transport模塊-獲取通道上下文:signal_messageapi中通過channel id獲取channel上下文信息,檢查通道是否ready和locked,調(diào)用scmi模塊的api 處理(channel_ctx->scmi_api->signal_message(channel_ctx->scmi_service_id);)。
3.scmi模塊-產(chǎn)生處理事件:
?scmi的api函數(shù)signal_message中將該消息封裝成事件,通過fwk_put_event發(fā)送一個fwk_event_light。(事件中source_id為scmi模塊,.target_id 為上一級smt 中channel_ctx->scmi_service_id,也是scmi。所以讓該事件是自己發(fā)給自己的)。因為event有隊列,中斷調(diào)用的api是實時的。在scmi的.process_event回調(diào)函數(shù)中處理上面的事件。
?首先通過scmi維護的scmi_ctx.service_ctx_table獲取transport信息找到transport_api(msg_smt模塊提供),然后讀出scmi消息的頭部(scmi_protocol_id、scmi_message_id、scmi_message_type、scmi_token)。
?然后通過get_agent_id(event->target_id, &agent_id)獲取該scmi 協(xié)議的agent_id(OSPM、PSCI等),根據(jù)agent_id獲取到agent_type(psci、ospi等)。
?最后根據(jù)scmi_protocol_id找到protocol(例如0x11是power domain處理),調(diào)用protocol->message_handler(protocol->id,event->target_id,payload, payload_size, ctx->scmi_message_id)執(zhí)行相對應的protocol的消息處理函數(shù)。message_handler函數(shù)執(zhí)行到了scmi_power_domain模塊。
4.scmi_power_domain模塊-解析scmi消息:.message_handle函數(shù)對消息進行檢驗,將進行權限判斷,然后查表調(diào)用具體的消息處理函數(shù)handler_table[message_id](service_id, payload)。例如scmi_protocol_id為scmi_power_domain,scmi_message_type為MOD_SCMI_PD_POWER_STATE_SET,則處理函數(shù)為scmi_pd_power_state_set_handler。該函數(shù)中將會進行策略判斷(大多數(shù)模塊為空),然后調(diào)用scmi_pd_ctx.pd_api->set_state(pd_id,pd_power_state)進行power domain的set,pd_api對應power_domain模塊中對外api函數(shù)。
5.power_domain模塊-調(diào)用driver處理:power_domain模塊的api set_state函數(shù)先組裝了一個event發(fā)給pd_id,也就是自己。pd_process_event()函數(shù)進行處理,process_set_state_request()按照pd的樹形結構對狀態(tài)進行設置,然后調(diào)用initiate_power_state_transition()執(zhí)行status =pd->driver_api->set_state(pd->driver_id, state);更新pd的狀態(tài),并拿到執(zhí)行結果status 。這里driver_api是在product/juno/scp_ramfw/config_power_domain.c的struct fwk_elementelement_table變量中定義,可以看到為FWK_MODULE_IDX_JUNO_PPU中提供
6.juno_ppu模塊-寄存器設置:根據(jù)ppu_id拿到ppu的上下文ppu_ctx,按照傳入的state值(on或者off)執(zhí)行status =ppu_set_state_and_wait(ppu_ctx, mode);最后執(zhí)行reg->POWER_POLICY = (uint32_t)mode;進行寄存器設置生效。
7.scmi_power_domain模塊-返回結果:最后調(diào)用scmi_pd_ctx.scmi_api->respond(service_id, &return_values,....)到scmi 模塊。
8.scmi模塊:scmi中api的respond函數(shù)將會通過service_id查表service_ctx_table獲取transport信息,然后調(diào)用ctx->respond(ctx->transport_id,payload, size),為msg_smt模塊中respond api()(注transport_id在config_scmi.c 中配置。指定transport為smt模塊+smt內(nèi)的具體channel element元素))。
9.transport模塊:msg_smt模塊中的respond api為smt_respond()函數(shù)。通過上一級傳入的transport_id/channel_id的element_idx部分,查表smt_ctx.channel_ctx_table獲取channel消息。 然后填充Shared Memory,并調(diào)用channel_ctx->driver_api->raise_interrupt(channel_ctx->driver_id)產(chǎn)生中斷,通知agent。
10.mhu模塊產(chǎn)生中斷:raise_interrupt()函數(shù)中,根據(jù)slot_id找到設備上下文,然后對寄存器進行設置reg->SET |= (1U << slot);。
從上面可以看到,scmi的處理流程基本是通用的,涉及到不同平臺的就是最后硬件的設置,需要新建一個juno_ppu模塊-寄存器設置,及其配置文件。
SCP中scmi協(xié)議處理:
系統(tǒng)支持兩種agent:PSCI和OSPM,發(fā)來的SCMI消息根據(jù)protocol_id進行分類,然后根據(jù)message_id子命令找到合適的處理函數(shù),最后根據(jù)message_type決定是否進行回復。
關于SCMI協(xié)議的一些參數(shù)定義可以參考代碼:module/scmi/include/mod_scmi_std.h例如上面我們介紹過0x11 powerdomain,其他的處理過程相似可以通過下面表速查到相關模塊,從模塊的static int (*handler_table中根據(jù)message_id下標迅速找到處理函數(shù):
|
protocol_id
|
描述
|
涉及模塊及處理代碼
|
|
0x10
|
Base protocol
|
module/scmi/src/mod_scmi_base.c
|
|
0x11
|
Power domain management protocol
|
module/scmi_power_domain/src/mod_scmi_power_domain.c
|
|
0x12
|
System power management protocol
|
module/scmi_system_power/src/mod_scmi_system_power.c
|
|
0x13
|
Performance domain management protocol
|
module/scmi_perf/src/mod_scmi_perf.c
|
|
0x14
|
Clock management protocol
|
module/scmi_clock/src/mod_scmi_clock.c
|
|
0x15
|
Sensor management protocol
|
module/scmi_sensor/src/mod_scmi_sensor.c
|
|
0x16
|
Reset domain management protocol
|
module/scmi_reset_domain/src/mod_scmi_reset_domain.c
|
|
0x17
|
Voltage domain management protocol
|
module/scmi_voltage_domain/src/mod_scmi_voltage_domain.c
|
|
0x18
|
Power capping and monitoring protocol
|
不支持
|
|
0x19
|
Pin Control protocol
|
不支持
|
|
0x11
|
Power domain management protocol
|
module/scmi_power_domain/src/mod_scmi_power_domain.c
|
|
0x12
|
System power management protocol
|
module/scmi_system_power/src/mod_scmi_system_power.c
|
|
enum mod_pd_type { MOD_PD_TYPE_CORE, MOD_PD_TYPE_CLUSTER, MOD_PD_TYPE_DEVICE, MOD_PD_TYPE_DEVICE_DEBUG, MOD_PD_TYPE_SYSTEM, MOD_PD_TYPE_COUNT };
|
MOD_PD_TYPE_CORE的處理api為core_pd_driver_api,如下:
|
static const struct mod_pd_driver_api core_pd_driver_api = { .set_state = core_set_state, .get_state = pd_get_state, .reset = core_reset, .prepare_core_for_system_suspend = core_prepare_core_for_system_suspend, };
|
首先根據(jù)ppu_id拿到上下文參數(shù)(config_juno_ppu.c中定義),然后根據(jù)要設置的state進行分開處理:
|
static int core_set_state(fwk_id_t ppu_id, unsigned int state) { get_ctx(ppu_id, &ppu_ctx); dev_config = ppu_ctx->config; mode = pd_state_to_ppu_mode[state]; switch ((enum mod_pd_state)state) { case MOD_PD_STATE_OFF: //設置PPU狀態(tài),并等待生效 status = ppu_set_state_and_wait(ppu_ctx, mode); //清空這個PPU對應的中斷消息 status = clear_pending_wakeup_irq(dev_config); //關閉這個PPU對應的中斷消息 status = disable_wakeup_irq(dev_config); //關閉軟重啟中斷消息 status = fwk_interrupt_disable(dev_config->warm_reset_irq); break; case MOD_PD_STATE_SLEEP: status = ppu_set_state_and_wait(ppu_ctx, mode); status = clear_pending_wakeup_irq(dev_config); status = enable_wakeup_irq(dev_config); status = fwk_interrupt_disable(dev_config->warm_reset_irq); break; case MOD_PD_STATE_ON: status = fwk_interrupt_clear_pending(dev_config->warm_reset_irq); status = fwk_interrupt_enable(dev_config->warm_reset_irq); status = ppu_set_state_and_wait(ppu_ctx, mode); break; default: fwk_unexpected(); status = FWK_E_PANIC; break; } //power_domain模塊中api調(diào)用,對這個pd進行訂閱的模塊會收到電源變化通知 status = ppu_ctx->pd_api->report_power_state_transition(ppu_ctx->bound_id, state); return FWK_SUCCESS; }·
|
|
static enum ppu_mode pd_state_to_ppu_mode[] = { [MOD_PD_STATE_OFF] = PPU_MODE_OFF, [MOD_PD_STATE_SLEEP] = PPU_MODE_OFF, [MOD_PD_STATE_ON] = PPU_MODE_ON, [MOD_SYSTEM_POWER_POWER_STATE_SLEEP0] = PPU_MODE_MEM_RET, };
|
|
static int ppu_set_state_and_wait(struct ppu_ctx *ppu_ctx,enum ppu_mode mode) { //對寄存器進行設置 reg = ppu_ctx->reg; reg->POWER_POLICY = (uint32_t)mode; //根據(jù)配置信息等待PPU設置完成 dev_config = ppu_ctx->config; params.mode = mode; params.reg = reg; if (fwk_id_is_equal(dev_config->timer_id, FWK_ID_NONE)) { /* Wait for the PPU to set */ while (!set_power_status_check(¶ms)) { continue; } }
|
|
int fwk_interrupt_disable(unsigned int interrupt) { if (!initialized) { return FWK_E_INIT; } return fwk_interrupt_driver->disable(interrupt); }
|
|
static int disable(unsigned int interrupt) { if (interrupt >= irq_count) { return FWK_E_PARAM; } NVIC_DisableIRQ((enum IRQn)interrupt); return FWK_SUCCESS; } __STATIC_INLINE void __NVIC_DisableIRQ(IRQn_Type IRQn) { if ((int32_t)(IRQn) >= 0) { NVIC->ICER[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); __DSB(); __ISB(); } }
|
其他:SCP入門系列就算講完了,有規(guī)范有源碼,有一點缺陷就是沒用qmeu運行起來,官方也沒給出,只說用ARM的Fixed Virtual Platform (FVP)能運行,不熟悉操作起來估計有點費勁對PC要求也高,這個SCP也比較小眾在大規(guī)模的SoC上才有應用,提出的挺早但是應用的還是不多。其實找一個qemu支持的板子,把代碼改一改應該也能運行起來,有興趣的可以自己嘗試下。 后記:英文規(guī)范+源碼才是一手資料,看二手資料永遠都跟不上別人,比如知乎、CSDN、公眾號、bilibili等中文的總結文檔,甚至我這篇博客。為什么會這樣?因為英文規(guī)范很全面,總結出來的二手中文文檔只是翻譯了其中一部分,但是那個寫二手文檔的人肯定把一手的都看了,所以你看二手的因為不全而永遠落后別人,二手好處就是入門快,要精通還是看一手的吧。不過我這里盡量是簡介和匯總文檔,而不是大篇幅的摘抄翻譯,讓大家好找到出處,知道去看什么英文文檔,去哪里找,一般就是ARM官網(wǎng)(本文的SMC、PSCI、SCMI)或者github。搞一些有點技術含量的研發(fā)特別是靠近底層軟件和芯片技術的,英文是一道坎,中國沒有只能學學老外5-10前的技術已經(jīng)算先進的了,這些領域國內(nèi)基本還是海歸或者外企待過的人把持,說話都夾雜著滿嘴的英文單詞和行業(yè)術語縮寫,不裝逼還真不是一個level的了,現(xiàn)在都是把電腦系統(tǒng)和常用軟件都換英文顯示的了,努力看英文無障礙。
-
ARM
+關注
關注
135文章
9532瀏覽量
390315 -
寄存器
+關注
關注
31文章
5599瀏覽量
129572 -
通信
+關注
關注
18文章
6361瀏覽量
139847
原文標題:ARM SCP入門-AP與SCP通信
文章出處:【微信號:OS與AUTOSAR研究,微信公眾號:OS與AUTOSAR研究】歡迎添加關注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
關于SCP和AP通信的MHU介紹
ARM計算子系統(tǒng)SCP消息接口協(xié)議
SCP-LT8618-BEVALZ SCP-LT8618-BEVALZ評估板
SCP-LT3472-EVALZ SCP-LT3472-EVALZ評估板
SCP-LT1956-BEVALZ SCP-LT1956-BEVALZ評估板
SCP-THRUBRD-EVALZ SCP-THRUBRD-EVALZ評估板
SCP-OUTPUT-EVALZ SCP-OUTPUT-EVALZ評估板
SCP-ADP5070-EVALZ SCP-ADP5070-EVALZ評估板
SCP-INPUT-EVALZ SCP-INPUT-EVALZ評估板
SCP-FILTER-EVALZ SCP-FILTER-EVALZ評估板
SCP-ADP7142-EVALZ SCP-ADP7142-EVALZ評估板
ARM SCP入門-framework框架代碼分析
ARM SCP入門-AP與SCP通信
評論