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