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

電源管理入門(mén)-Regulator驅(qū)動(dòng)是什么?Regulator的作用是什么?

電源管理入門(mén):Thermal熱管理

電源管理入門(mén):Power supply子系統(tǒng)

電源管理入門(mén):Power Domain管理

電源管理入門(mén):Hypervisor中的電源管理

LINUX電源管理的相關(guān)資料分享
如何對(duì)PCI設(shè)備的電源進(jìn)行管理呢
Linux電源管理的系統(tǒng)架構(gòu)和驅(qū)動(dòng)
Linux電源管理之Generic PM Suspend功能簡(jiǎn)析
LINUX電源管理

Linux電源管理--PM QoS

驅(qū)動(dòng)篇:inux 電源管理的系統(tǒng)架構(gòu)和驅(qū)動(dòng)(一)

評(píng)論