作者 | 趙青窕
審校 | 孫淑娟
Regulator幾乎是每一位驅(qū)動開發(fā)者都會使用到的模塊,在處理過幾起與Regulator相關(guān)的bug后,我終于弄明白了。接下來我來分享下,到底該如何控制Regulator?
本文將從以下五個方面來闡述內(nèi)核中Regulator該如何控制:
- 什么是Regulator
- 設(shè)備樹配置
- 核心API接口
- 驅(qū)動控制方法
- 調(diào)試方法
(Lk和uefi階段的上電控制不屬于本文的范疇。)
1.什么是Regulator
一般來說,soc都會有配套的有限數(shù)量的pmu,而Regulator就是這個pmu的抽象,直白來說就是我們通過控制Regulator,進而控制了pmu,從而達到對電的控制。
下圖是內(nèi)核中Regulator的整體框架圖,由三部分組成,分別提供了供其他驅(qū)動使用的API接口和sysfs口,并可以控制硬件PMIC等這類器件的register,在本文中,將會介紹前兩部分。
2.設(shè)備樹配置
常用的設(shè)備樹配置主要涉及4個部分,共5個屬性,分別是配置對應(yīng)的Regulator,設(shè)備工作需要的電壓范圍,設(shè)置always-on屬性,設(shè)置boot-on屬性。
下面是一個典型的設(shè)備樹配置,供大家參考。
test-avdd-supply
這個屬性是用來指明設(shè)備xxx使用的是哪一個Regulator,該屬性設(shè)置時,需要先從原理圖中獲取對應(yīng)的供電信息,然后轉(zhuǎn)化到軟件上的標識(通常原理圖中的標識和平臺代碼dts中的相同,很容易識別到),從而配置該屬性;
test-avdd-min-uv和test-avdd-max-uv
這兩個屬性是用來指明該Regulator對應(yīng)的電壓范圍,這個范圍不能隨便設(shè)置,因為pmu有它自身的驅(qū)動能力范圍。驅(qū)動能力的范圍可以通過以下方式獲?。?/p>
在平臺代碼的設(shè)備樹中查找,上面設(shè)備樹配置中,我采用了L5A,那我就在平臺的設(shè)備樹配置中找L5A的配置,如下樣例可以看出L5A的驅(qū)動范圍是在1.65V到3.05V之間。
我們雖然知道了驅(qū)動能力范圍,但并不意味著我們就可以通過配置Regulator(后面會說明如何配置),設(shè)置這個范圍內(nèi)的任意電壓值。通過查看Regulator或者pmu的手冊都可以看出,每一個Regulator只能取這個范圍內(nèi)的離散值。
regulator-always-on
該屬性有兩個含義,第一層含義就是設(shè)置系統(tǒng)啟動的時候,進行相應(yīng)Regulator的上電操作,下圖基于MTK平臺的代碼就是對應(yīng)的上電操作。
第二層含義就是禁止對該Regulator進行掉電的操作,如下圖的代碼所示,rdev->constraints->always_on在系統(tǒng)啟動的時候會進行設(shè)置,該變量代表了設(shè)備樹中是否設(shè)置了regulator-always-on屬性,當(dāng)設(shè)置該屬性時,對應(yīng)的rdev->constraints->always_on = 1,則函數(shù)regulator_do_disable就不會執(zhí)行,從而該Regulator無法掉電。
regulator-boot-on
該屬性實際上同regulator-always-on屬性的第一個含義相同,但我個人建議在配置需要開機就上電的Regulator的時候,即使有regulator-always-on屬性,最好同時加上regulator-boot-on屬性,以防有些平臺regulator-always-on屬性沒有第一個含義的情況。
3.核心API接口
首先給大家介紹一下Regulator相關(guān)的API函數(shù)。
struct regulator *regulator_get(struct device *dev, const char *id)
該函數(shù)用來獲取對應(yīng)的Regulator,對應(yīng)到本文中的設(shè)備xxx,其函數(shù)調(diào)用方法時regulator_get(對應(yīng)xxx的struct device *dev,“test-avdd”),注意該函數(shù)中第2個參數(shù)是test-avdd,但設(shè)備樹中是test-avdd-supply,之所以設(shè)備樹和函數(shù)傳參不相同的原因是下圖中紅色方框標注的代碼導(dǎo)致的。
int regulator_is_enabled(struct regulator *regulator)
該函數(shù)用來判斷對應(yīng)的regulator是否已經(jīng)enable。
當(dāng)返回0表示對應(yīng)Regulator處于disable狀態(tài)。
如果配置了always_on,該函數(shù)直接返回1,表示相應(yīng)的Regulator已經(jīng)enabled,否則會去讀取相應(yīng)的寄存器來獲取相應(yīng)Regulator的使能狀態(tài)寄存器。
該函數(shù)有著很重要的作用,但也是大家容易忽略的函數(shù),后面會給大家展示其重要性。
int regulator_set_volatage(struct regulator *regulator, int min_uV, int max_uV)
該函數(shù)中的第二個參數(shù)和第三個參數(shù)可以相同,也可以不同。當(dāng)不同的時候,就是設(shè)置的電壓范圍;當(dāng)相同的時候,就是設(shè)置的電壓值。
只有在設(shè)置值和當(dāng)前值不一樣,且設(shè)置的數(shù)據(jù)合理,才會進行范圍設(shè)置。
- 設(shè)置范圍
當(dāng)設(shè)置的范圍要超出該Regulator的驅(qū)動能力范圍時,且第三個參數(shù)大于第二個參數(shù),這種情況下,regulator_set_voltage會內(nèi)部把范圍縮小到該Regulator能驅(qū)動的最大范圍。
同時用于將電壓設(shè)置為min_uV和max_uV范圍內(nèi),和min_uV最接近的電壓。
- 設(shè)置值
如下面的代碼所示,其目的是對應(yīng)的電壓為2.8V,前面有介紹過,每一個Regulator只能取一定范圍內(nèi)的離散值,當(dāng)2.8V不屬于這些離散值中的任意值時,就會設(shè)置失敗。
int regulator_enable(struct regulator *regulator)
該函數(shù)用來enable對應(yīng)的Regulator,只有enable后,才能真正的供上電。
如果配置了always_on屬性,該函數(shù)直接返回0,其他情況下,需要根據(jù)實際情況來判斷,然后執(zhí)行相應(yīng)的操作,下圖是enable時,具體的執(zhí)行函數(shù)。
從上圖可以看出,調(diào)用regulator_enable時,只有在use_count為0的情況下才會做enable動作,且use_count會自加1。use_count是比較重要的變量,在regulator_disable時也會用到,接下來我們就看以下regulator_disable。
int regulator_disable(struct regulator *regulator)
該函數(shù)用來disable對應(yīng)的Regulator。
如果配置了always_on屬性,該函數(shù)直接返回0。
regulator_disable函數(shù)內(nèi)部會調(diào)用_regulator_disable函數(shù),下圖是_regulator_disable的實現(xiàn)代碼,從圖中可以看出,當(dāng)use_count不為1時,不會執(zhí)行disable動作。
還有很多與Regulator相關(guān)的API函數(shù),如regulator_put,regulator_set_load等,但常用的就是上面的5個函數(shù)。
4. 驅(qū)動控制方法
在驅(qū)動中需要按照下面的步驟來執(zhí)行(針對一個Regulator只給一個設(shè)備供電的情況):
- 通過regulator_get獲取對應(yīng)的Regulator
- 通過regulator_set_voltage設(shè)置電壓
- 通過regulator_is_enabled來判斷當(dāng)前Regulator的狀態(tài)
- 根據(jù)上一步的結(jié)果,如果未enable,則調(diào)用 regulator_enable,否則不需要調(diào)用regulator_enable
- regulator_disable
在實際工作中,我遇到過這樣的情景,沒有使用regulator_is_enabled進行條件判斷,但無意中調(diào)用了兩次regulator_enable,這樣就會導(dǎo)致use_count = 2,在regulator_disable時,由于use_count != 1,從而沒有進行disable動作,導(dǎo)致最后發(fā)現(xiàn)相應(yīng)的這路電無法掉電。
下面是一個簡單的例子:
還有一個驅(qū)動是在收到應(yīng)用層的命令后,才進行regulator的enable或者disable的情況下,建議使用regulator_is_enable來進行判斷,這樣就可以有效避免上層多次發(fā)送enable命令導(dǎo)致use_count增加的情況。
當(dāng)某一個Regulator給多個設(shè)備供電時,需要考慮多個設(shè)備的情況,就不建議使用regulator_is_enabled,因此多設(shè)備通過一路Regulator控制時,會比較復(fù)雜,比如設(shè)備A已經(jīng)enable了某一路Regulator,某一時刻設(shè)備B也需要enable,但由于通過regulator_is_enabled發(fā)現(xiàn)已經(jīng)enable時,從而不進行enable操作,但之后的某一時刻,設(shè)備A需要進行掉電操作,因為之前regulator_enable只調(diào)用了一次,那use_count = 1,那此時設(shè)備A就可以regulator_disable成功,但這個時候設(shè)備B不希望掉電,但設(shè)備A把電掉了,導(dǎo)致設(shè)備B就異常了,因此同一路電給多個設(shè)備供電時,不建議使用regulator_is_enable。針對多種設(shè)備,最簡單的處理方式就是使用regulator-always-on屬性。
5.調(diào)試方法
此處我主要給大家介紹下sys節(jié)點的調(diào)試方式。節(jié)點的路徑是/sys/kernel/debug/regulator/,在這個路徑下面,大家會看到很多Regulator,如下圖所示:
從上圖我們可以看出,根據(jù)名稱就可以找到我們需要的Regulator,比如從原理圖中看出來我們使用的是ldoe9,那么就可以進入路徑/sys/kernel/debug/regulator/18200000.rsc:rpmh-regulator-ldoe9-pm6150a_l9,在該路徑下可以查看對應(yīng)的open_count(cat open_count)或者進行enable或者disable控制(實際上就是echo 1或者0到對應(yīng)的節(jié)點即可)。
至此,Regulator的使用以及調(diào)試就給大家介紹完了,上面的介紹比較簡單,屬于入門級別的內(nèi)容,但這些內(nèi)容已經(jīng)足夠大部分驅(qū)動的使用進行調(diào)試了,希望大家都能通過這篇文章,真正了解到Regulator該如何使用。
作者介紹
趙青窕,51CTO社區(qū)編輯,從事多年驅(qū)動開發(fā)。