標籤:kernel
電源種類介紹
(百度百科)LDO是low dropout regulator,意為低壓差線性穩壓器,是相對於傳統的線性穩壓器來說的。傳統的線性穩壓器,如78xx系列的晶片都要求輸入電壓要比輸出電壓高出2v~3V以上,否則就不能正常工作。但是在一些情況下,這樣的條件顯然是太苛刻了,如5v轉3.3v,輸入與輸出的壓差只有1.7v,顯然是不滿足條件的。針對這種情況,才有了LDO類的電源轉換晶片。Ldo適合電壓要求比較穩,但是功率不是很大的裝置。
BUCK電路,降壓式變換電路。就是一種DC-DC轉換器,簡單的講就是通過震蕩電路將一直流電壓轉變為一高頻電源,然後通過脈衝變壓器、整流濾波迴路輸出需要的直流電壓,類似於開關電源
資料結構
structregulator_dev {
structregulator_desc *desc;
structlist_head list; // regulator通過此結構掛到regulator_list鏈表中
structlist_head consumer_list; //此regulator負責供電的裝置列表
structregulation_constraints *constraints;
structregulator *supply; 父regulator的指標
};
regulator_list全域變數 每註冊一個regulator都會掛到這裡
regulator_map_list全域變數 每註冊一個consumer都會掛到這裡
編寫驅動的步驟
概述
核心裡pmu驅動和regulator驅動大多是混合在一起寫的,很不好。比如把regulator_init_data放到platform的driver data裡傳進去。
一般來講,一款soc會有配套有限數量的pmu。regulator作為pmu的抽象層。供電線路分為pmu直接供電和mtcmos供電。mtcmos也是pmu的一路作為父電源進行供電,但是mtcmos是soc片上的供電線路。比如mtcmos用pmu的一路buck供電,但是usb,sd,pcie分別用了3路mtcmos進行供電。
每路供電節點都是一個regulator。如果不考慮mtcmos情況,通常都是一級供電。也就是說,從pmu出來一個regulator下面掛接的就是consumer,而不是多個regulator級聯。
如果出現級聯的regulator,可以在regulator_desc的supply_name欄位設定上級regulator。
推薦的寫法是,regulator本身用什麼實質內容都沒有的platform dev和driver註冊,在probe裡註冊regulator。只有regulator ops才會調用真正的pmu驅動。這樣實現了適配層和具體驅動分離的原則。
自訂regulator id格式
regulator_desc有一個成員是id。這個id在區分不同的regulator裡沒有什麼作用,因為regulator都是通過name字串來尋找和區分的。id的作用集中表現在ops函數指標數組裡。一個PMU晶片通常有多路供電,但是一類供電(如buck)使用同一組ops函數指標數組通常是相同的。但是實際不同的regulator設定的寄存器和方法是有差異的。所以通過id進行區分。通常把id作為offset使用。把寄存器和操作方法放到數組裡,這樣利用id從數組得到資訊,避免了使用大量的if和switch case語句。
struct regulator_desc {
const char*name;
const char*supply_name;
int id;
…
};
更最佳化的一些方法可以是,id的有些bit表示pmu或者regulator本身的資訊索引,而另外一些bit表示位移。這樣,一個系列的多個PMU晶片可能複用同一套regulator適配層代碼。操作資訊可以按照 func[pmu_id][offset]二位元組進行。這些使用方法都是靈活的。
獲得id的函數
int rdev_get_id(struct regulator_dev *rdev)
{
returnrdev->desc->id;
}
step1 準備ops
雖然arm soc pmu通常多達幾十路供電,但是ops數量不多,一般一類裝置同用一組ops函數,常見的類別有buck ops和ldo ops。ops函數的入參是(struct regulator_dev *),所以可以根據regulator_dev得到具體的id資訊,進行不同的寄存器操作。
int offset = rdev_get_id(rdev);
理論上講,一個pmu所有的regulator可以用同一組ops,然後用id區分操作。另一個極端是每個regulator用一組不同的ops。不過為了代碼複用和解耦合,通常是一類regulator用一組ops。
例子:
static structregulator_ops max8660_ldo5_ops = {
.list_voltage = max8660_ldo5_list,
.set_voltage = max8660_ldo5_set,
.get_voltage = max8660_ldo5_get,
};
step2 準備consumer
代碼例子:
static struct regulator_consumer_supplylp3974_buck3_consumer[] = {
REGULATOR_SUPPLY("vdet", "s5p-sdo"),
REGULATOR_SUPPLY("vdd_reg", "0-003c"),
};
consumer指的是regulator樹上的葉子節點。regulator_consumer_supply數組作為為regulator_init_data的成員。
consumer的名字是需要提供給各模組使用的,如上面代碼的“vdet”。所以如果是系列soc的代碼,最好這個名字不能帶有pmu晶片型號或者soc型號,因為同樣的模組,如usb控制器,很可能系列soc上的都一樣,驅動也是複用的。不可能每換個soc就全盤適配regulator。所以這種情況下最好命名為”myregulator-usb”之類。這樣換了pmu或者soc,只要用regulator_get(dev, ”myregulator-usb”)總能適應各種情況,就不需要修改驅動。
step3 準備regulator_desc
注意regulator_register的時候,regulator_desc和regulator_init_data是一一匹配的。所以regulator_desc數組和regulator_init_data數組的順序要保持一致。
static structregulator_desc regulators[] = {
{
.name = "LDO2",
.id = MAX8998_LDO2,
.ops = &max8998_ldo_ops,
.type =REGULATOR_VOLTAGE,
.owner = THIS_MODULE,
}, {
.name = "LDO3",
.id = MAX8998_LDO3,
.ops = &max8998_ldo_ops,
.type = REGULATOR_VOLTAGE,
.owner = THIS_MODULE,
}, {
另外還有一種寫法,可以用用定義的枚舉作為index,按順序填寫數組。只要regulator_desc數組和regulator_init_data數組都用同樣的枚舉做index,那肯定是一一匹配的。
static structregulator_desc regulators[] = {
[REG0] = {
.name = "LDO2",
.id = MAX8998_LDO2,
.ops = &max8998_ldo_ops,
.type = REGULATOR_VOLTAGE,
.owner = THIS_MODULE,
},
[REG1] = {
.name = "LDO3",
.id = MAX8998_LDO3,
.ops = &max8998_ldo_ops,
.type = REGULATOR_VOLTAGE,
.owner = THIS_MODULE,
}, {
step4 準備regulator_init_data
有的驅動程式將 regulator_init_data放入platform_data,不推薦。Regulator應該和pmu的驅動程式分開。只需要在ops裡調用pmu的操作函數就可以。
regulator_init_data應該也是結構體數組,和regulator_desc結構體數組一一匹配。
注意constraints的name欄位在尋找regulator的時候,有比regulator_desc更高的優先順序。不推薦在constraints寫name。
其中的一段如下。
static struct regulator_init_data lp3974_buck3_data ={
.constraints = {
.name = "VCC_1.8V",
.min_uV = 1800000,
.max_uV = 1800000,
.apply_uV = 1,
.always_on = 1,
.state_mem = {
.enabled = 1,
},
},
.num_consumer_supplies = ARRAY_SIZE(lp3974_buck3_consumer),
.consumer_supplies = lp3974_buck3_consumer,
};
step5 準備註冊regulator
最簡單的platform註冊就可以,只需要在probe裡把regulator都註冊上。
static int my_regulator_probe (struct platform_device*pdev)
{
int i;
for(i = 0;i < MAX ; i ++)
{
regulator_register(®ulator_desc[i], &pdev->dev,regulator_init_data[i], NULL, NULL);
}
}
static struct platform_device my_regulator_dev = {
.name =“my_regulator”,
};
static struct platform_driver my_regulator_driver = {
.driver ={
.name= " my_regulator ",
.owner= THIS_MODULE,
},
.probe =my_regulator_probe,
.remove =__devexit_p(my_regulator__remove),
};
在module_init裡註冊device和driver就可以。
platform_device_register(&my_regulator_dev)
platform_driver_register(&my_regulator_driver);
regulator使用
總體來說就是先調用regulator_get供電節點的名字,得到regulator指標,然後調用ops函數集。舉例如下:
regulator = regulator_get(dev, “my_usb”);
就會返回usb名字供電的regulator。
開啟和關閉校準器(regulator)API如下。
int regulator_enable(regulator);
int regulator_disable(regulator);
還有其他一些函數,參見struct regulator_ops。常見的有
struct regulator_ops {
/* get/setregulator voltage */
int(*set_voltage) (struct regulator_dev *, int min_uV, int max_uV,
unsigned *selector);
int(*get_voltage) (struct regulator_dev *);
/* get/setregulator current */
int(*set_current_limit) (struct regulator_dev *,
int min_uA, int max_uA);
int(*get_current_limit) (struct regulator_dev *);
/*enable/disable regulator */
int(*enable) (struct regulator_dev *);
int(*disable) (struct regulator_dev *);
int(*is_enabled) (struct regulator_dev *);
};
核心程式碼分析註冊regulator,包括設定supply和consumer
/**
*regulator_register - register regulator
*@regulator_desc: 就是regulator_desc
* @dev:
* @init_data: 就是regulator_init_data
* @driver_data:使用者私人資訊,不推薦,設定為NULL就可以
* @of_node:device tree可以設定regulator的樹狀結構,先不考慮).
*/
struct regulator_dev *regulator_register(structregulator_desc *regulator_desc,
structdevice *dev, const struct regulator_init_data *init_data,
void*driver_data, struct device_node *of_node)
{
// 編寫驅動只要提供regulator_desc和regulator_init_data就可以,分配regulator_dev結構在這裡
rdev =kzalloc(sizeof(struct regulator_dev), GFP_KERNEL);
//初始化regulator_dev結構
// 設定constraints
ret =set_machine_constraints(rdev, constraints);
// 如果此regulator有父regulator,設定父regulator. 優先選init_data
if(init_data && init_data->supply_regulator)
supply= init_data->supply_regulator;
else if(regulator_desc->supply_name)
supply= regulator_desc->supply_name;
if(supply) {
//用name字串在regulator_list尋找, 如果找到,就把本regulator加入到上級的consumer_list
r =regulator_dev_lookup(dev, supply);
ret= set_supply(rdev, r);
...
}
/*
首先檢查regulator_map_list全域變數裡有沒有衝突或者重複的。如果沒有為consumer申請struct regulator_map,
然後設定regulator_map的regulator設定為本regulator,同時用regulator_map記錄父子資訊,
並且把regulator_map加入到regulator_map_list全域變數裡。
*/
if(init_data) {
for(i = 0; i < init_data->num_consumer_supplies; i++) {
ret= set_consumer_device_supply(rdev,
init_data->consumer_supplies[i].dev_name,
init_data->consumer_supplies[i].supply);
}
}
//將本regulator加入到全域regulator_list
list_add(&rdev->list,®ulator_list);
}
regulator_desc和regulator_init_data的name重複欄位
Regulator的name在struct regulator_init_data的constraints->name欄位可以定義,也可以在regulator_desc的name欄位定義。但是constraints->name優先順序更高。
regulator_register-》regulator_dev_lookup-》rdev_get_name裡代碼如下:
static const char *rdev_get_name(struct regulator_dev*rdev)
{
if(rdev->constraints && rdev->constraints->name)
returnrdev->constraints->name;
else if(rdev->desc->name)
returnrdev->desc->name;
else
return"";
}
static LIST_HEAD(regulator_list); //所有regulator都註冊在這個鏈表裡
上級電源supply重複欄位
除了name,regulator_desc->supply_name和regulator_init_data->supply_regulator也是重複的,都是指向上級regulator的指標,regulator_init_data優先順序更高,可以在regulator_register裡看到代碼如下
if (init_data &&init_data->supply_regulator)
supply= init_data->supply_regulator;
else if(regulator_desc->supply_name)
supply= regulator_desc->supply_name;