Runtime PM管理也就是設備驅(qū)動里面的電源管理,即設備驅(qū)動結(jié)構(gòu)體里面的struct dev_pm_ops,只控制設備自己的電源。這樣可以在設備不需要工作的時候可以進入到低功耗狀態(tài),更好的管理設備自己的電源,所謂:“各掃門前雪”。
為什么需要Runtime PM?
不同于系統(tǒng)的電源管理,設備自己的電源管理更加的細化。這就像一個層級關(guān)系,系統(tǒng)整體的是一個大的電源狀態(tài)管理,但是對于眾多的集成外國設備也不能一刀切,就是不能要干活都干活要休息都休息,要細化管理不能懶政,就對每個設備自己也來一套電源狀態(tài)管理,直接把機制從系統(tǒng)哪里復制過來一份一個閹割版的就夠用,采用分而治之的思想,只要系統(tǒng)要統(tǒng)一指揮的時候聽話就可以,其他時候可以自己決策執(zhí)行就是runtime PM管理。這里的設備有可能是外設,比如sensor、lcdc等。這里的設備也有可能是SOC內(nèi)部的某些IP,比如codec、dsp、usb等。
1. 框架介紹
1.1 為什么需要Runtime PM Framework?
系統(tǒng)基本的電源管理,例如關(guān)機休眠等,需要調(diào)用device的電源Runtime API就是ops回調(diào)函數(shù),而且需要按一個順序的queue去實施,而且系統(tǒng)跟設備狀態(tài)發(fā)生沖突的時候也需要去處理,綜上就需要一個Framework去統(tǒng)一做這些事情
設備驅(qū)動需要根據(jù)系統(tǒng)的一些參數(shù)來決定自己的電源狀態(tài),例如CPU是否idle等,就需要系統(tǒng)框架的支持
當設備處于低功耗模式時,wakeup signal常常需要platform或者bus的支持。
1.2 系統(tǒng)框架圖
數(shù)據(jù)結(jié)構(gòu):
image.png
關(guān)機舉例:
休眠舉例:
2. Drivers
Device drivers(包括bus、class、power domain)實現(xiàn)了runtime pm相關(guān)的runtime_idle/runtime_suspend/runtime_resume三個回調(diào):
runtime_suspend用于實現(xiàn)設備的低功耗操作
runtime_resume用于實現(xiàn)設備的低功耗恢復相關(guān)的操作
runtime_idle屬于runtime_suspend的一個過渡,用于緩沖頻繁的suspend與resume,它會判斷設備是否具備suspend的條件,如果具備在合適的時機,就會suspend設備。
runtime_suspend與runtime_resume回調(diào)函數(shù)里會調(diào)用clock framework/reset framework/regulator framework提供的時鐘開關(guān)、復位、電源開關(guān)的接口。這里以SPI驅(qū)動為例進行說明:
subsys_initcall(pl022_init); static int __init pl022_init(void) { return amba_driver_register(&pl022_driver); } static struct amba_driver pl022_driver = { .drv = { .name = "ssp-pl022", .pm = &pl022_dev_pm_ops, }, .id_table = pl022_ids, .probe = pl022_probe, .remove = pl022_remove, }; static const struct dev_pm_ops pl022_dev_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(pl022_suspend, pl022_resume) SET_RUNTIME_PM_OPS(pl022_runtime_suspend, pl022_runtime_resume, NULL) }; #define SET_RUNTIME_PM_OPS(suspend_fn, resume_fn, idle_fn) .runtime_suspend = suspend_fn, .runtime_resume = resume_fn, .runtime_idle = idle_fn,
pm結(jié)構(gòu)體dev_pm_ops 中的有3個以runtime開頭的成員函數(shù):runtime_suspend()、runtime_resume()和runtime_idle(),它們輔助設備完成運行時的電源管理
struct dev_pm_ops { ... int (*runtime_suspend)(struct device *dev); int (*runtime_resume)(struct device *dev); int (*runtime_idle)(struct device *dev); ... };
運行時的PM與前文描述的系統(tǒng)級掛起到RAM時候的PM不太一樣,它是針對單個設備,指系統(tǒng)在非睡眠狀態(tài)的情況下,某個設備在空閑時可以進入運行時掛起狀態(tài),而在不是空閑時執(zhí)行運行時恢復使得設備進入正常工作狀態(tài),如此,這個設備在運行時會省電。
每個設備處理好自己的電源管理,在不需要工作時進入低功耗狀態(tài)。也就是"各人自掃門前雪"。Linux提供了一系列API,以便于設備可以聲明自己的運行時PM狀態(tài):
函數(shù)名字 | 功能 |
---|---|
pm_runtime_suspend | 引發(fā)設備的掛起,執(zhí)行相關(guān)的runtime_suspend()函數(shù)。 |
pm_schedule_suspend | “調(diào)度”設備的掛起,延遲delay毫秒后將掛起工作掛入pm_wq等待隊列,結(jié)果等價于delay毫秒后執(zhí)行相關(guān)的runtime_suspend()函數(shù)。 |
pm_runtime_resume | 引發(fā)設備的恢復,執(zhí)行相關(guān)的runtime_resume()函數(shù)。 |
pm_request_resume | 發(fā)起一個設備恢復的請求,該請求也是掛入pm_wq等待隊列。 |
pm_runtime_idle | 引發(fā)設備的空閑,執(zhí)行相關(guān)的runtime_idle()函數(shù)。 |
pm_request_idle | 發(fā)起一個設備空閑的請求,該請求也是掛入pm_wq等待隊列。 |
pm_runtime_enable | 使能設備的運行時PM支持。 |
pm_runtime_disable | 禁止設備的運行時PM支持。 |
pm_runtime_getpm_runtime_get_sync | 增加設備的引用計數(shù)(usage_count),這類似于clk_get(),會間接引發(fā)設備的runtime_resume()。 |
pm_runtime_putpm_runtime_put_sync | 減小設備的引用計數(shù),這類似于clk_put(),會間接引發(fā)設備的runtime_idle()。 |
3. Runtime PM core
Runtime pm core主要提供了三類函數(shù)接口:
提供enable/disable接口給設備驅(qū)動,用于該設備驅(qū)動決定是否打開或關(guān)閉RPM,
提供get、put類接口給設備驅(qū)動,用于決定什么時候進入或者恢復設備低功耗,
在設備驅(qū)動調(diào)用了get、put接口后RPM會調(diào)用各設備驅(qū)動實現(xiàn)的runtime_suspend/runtime_resume接口。
對于決定設備是否進入低功耗的get/put接口的調(diào)用時機,一般會在操作設備相關(guān)寄存器前調(diào)用get接口,在操作完相關(guān)寄存器后調(diào)用put接口?;蛘咴谠O備驅(qū)動的open、release、start、stop等接口里調(diào)用,用戶層的services通過ioctrl或者驅(qū)動提供的文件節(jié)點調(diào)用驅(qū)動的這些接口。
我們可以這樣簡單地理解Linux運行時PM的機制,每個設備(總線的控制器自身也屬于一個設備)都 有引用計數(shù)usage_count和活躍子設備(Active Children,子設備的意思就是該級總線上掛的設備)計數(shù)child_count,當兩個計數(shù)都為0的時候,就進入空閑狀態(tài),調(diào)用pm_request_idle(dev)。
當設備進入空閑狀態(tài),與pm_request_idle(dev)對應的PM核并不一定直接調(diào)用設備驅(qū)動的runtime_suspend(),它實際上在多數(shù)情況下是調(diào)用與該設備對應的bus_type的runtime_idle()。
在具體的設備驅(qū)動中,一般的用法則是在設備驅(qū)動probe()時運行pm_runtime_enable()使能運行時PM支持,在運行過程中動態(tài)地執(zhí)行“pm_runtime_get_xxx()->做工作->pm_runtime_put_xxx()”的序列。如代碼清單19.19中的drivers/watchdog/omap_wdt.c OMAP的看門狗驅(qū)動。
在omap_wdt_start()中啟動了pm_runtime_get_sync(),
而在omap_wdt_stop()中調(diào)用了pm_runtime_put_sync()。
static const struct watchdog_ops omap_wdt_ops = { .owner = THIS_MODULE, .start = omap_wdt_start, .stop = omap_wdt_stop, .ping = omap_wdt_ping, .set_timeout = omap_wdt_set_timeout, .get_timeleft = omap_wdt_get_timeleft, }; static int omap_wdt_start(struct watchdog_device *wdog) { pm_runtime_get_sync(wdev->dev);//告訴內(nèi)核要開始用看門狗這個設備了,如果看門狗設備已經(jīng)進入省電模式(之前引用計數(shù)為0且執(zhí)行了運行時掛起),會導致該設備的運行時恢復 static int omap_wdt_stop(struct watchdog_device *wdog) { pm_runtime_put_sync(wdev->dev);//告訴內(nèi)核不用這個設備了,如果引用計數(shù)變?yōu)?且活躍子設備為0,則導致該看門狗設備的運行時掛起。
在一些設備上不使用的時候不能立即掛起,,因為掛起狀態(tài)的進入和恢復需要一些時間,如果設備不在掛起之間保留一定的時間,頻繁進出掛起反而會帶來新的開銷。因此,我們可根據(jù)情況決定只有設備在空閑了一段時間后才進入掛起(一般來說,一個一段時間沒有被使用的設備,還會有一段時間不會被使用),基于此,一些設備驅(qū)動也常常使用自動掛動模式進行編程。
在執(zhí)行操作的時候聲明pm_runtime_get(),操作完成后執(zhí)行pm_runtime_mark_last_busy()和pm_runtime_put_autosuspend(),一旦自動掛動的延時到期且設備的使用計數(shù)為0,則引發(fā)相關(guān)runtime_suspend()入口函數(shù)的調(diào)用。
設備驅(qū)動PM成員的runtime_suspend()一般完成保存上下文、切到省電模式的工作,而runtime_resume()一般完成對硬件上電、恢復上下文的工作
4. power domain framework
一個power domain上可能包含多個IP,每個IP可能對應一個或多個設備。這些設備會在dts中描述與power domain的綁定關(guān)系。系統(tǒng)初始化的時候,會將這個power domain放到一個鏈表中,然后根據(jù)設備中dts描述的與power domain的關(guān)系,將設備掛在power domain節(jié)點下的鏈表中。
當某個設備驅(qū)動通過put接口調(diào)用,將usage_count從1減少到0,這時會先調(diào)用power domain注冊的runtime_suspend接口,在這個接口中,會先調(diào)用該設備驅(qū)動的runtime_suspend,然后遍歷該power domain下所有的設備是否都允許suspend(各設備驅(qū)動的usage_count是否為0),若允許就會直接調(diào)用關(guān)閉power domian的接口,否則直接返回。當某個設備驅(qū)動通過get接口調(diào)用,將usage_count從0增加到1,這時會先調(diào)用power domain注冊的runtime_resume接口,在這個接口中,會先將power domain上電,然后再調(diào)用設備驅(qū)動對應的runtime_resume回調(diào)函數(shù),讓設備退出低功耗。
5. runtime pm的sysfs
對于支持rpm的設備,在相應的設備節(jié)點下有多個rpm相關(guān)屬性的文件節(jié)點,分別為control,runtime_susupend_time,runtime_active_time,autosuspend_delay_ms,runtime_status。接口在文件: /kernel/drivers/base/power/sysfs.c中描述。
/sys/devices/.../power/control
on - 調(diào)用pm_runtime_forbid接口,增加設備的引用計數(shù),然后resume設備。
auto - 調(diào)用pm_runtime_allow接口,減少設備的引用計數(shù),如果設備的引用計數(shù)為0,則idle設備。
/sys/devices/.../power/runtime_status
active - 設備的狀態(tài)是正常工作狀態(tài)。
suspend- 設備的狀態(tài)是低功耗模式。
suspending-設備的狀態(tài)正在從active->suspend轉(zhuǎn)化。
resuming-設備的狀態(tài)正在從suspend->active轉(zhuǎn)化。
error-設備runtime出現(xiàn)錯誤,此時runtime_error的標志置位。
unsupported-設備的runtime 沒有使能,此時disable_depth標志置位。
/sys/devices/.../power/runtime_suspend_time
設備在suspend狀態(tài)的時間
/sys/devices/.../power/runtime_active_time
設備在active狀態(tài)的時間
/sys/devices/.../power/autosuspend_delay_ms
設備在idle狀態(tài)多久之后suspend,設置延遲suspend的延遲時間。
后記:
在編寫驅(qū)動的時候,如果涉及電源管理的功耗需求,就需要實現(xiàn)struct dev_pm_ops,為驅(qū)動程序增加一個電源管理的功能,會更加的靈活,也是我們?nèi)?yōu)化系統(tǒng)功耗的一個重要方向。因為大多數(shù)程序估計是供應商提供的,但是我們自己的加的硬件的程序估計是我們自己去寫的,并且做業(yè)務挺耗電,給自己寫的驅(qū)動加個電源管理就挺好。
-
電源管理
+關(guān)注
關(guān)注
115文章
6183瀏覽量
144511 -
cpu
+關(guān)注
關(guān)注
68文章
10863瀏覽量
211797 -
soc
+關(guān)注
關(guān)注
38文章
4166瀏覽量
218280 -
函數(shù)
+關(guān)注
關(guān)注
3文章
4331瀏覽量
62630 -
runtime
+關(guān)注
關(guān)注
0文章
17瀏覽量
2174
原文標題:電源管理入門-16 驅(qū)動Runtime PM管理
文章出處:【微信號:OS與AUTOSAR研究,微信公眾號:OS與AUTOSAR研究】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論