Linux核心regulator架構和編寫

來源:互聯網
上載者:User

標籤:ble   ons   _id   濾波   eve   fse   add   content   優先   

電源種類介紹

      (百度百科)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(&regulator_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,&regulator_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;

 

 


Linux核心regulator架構和編寫

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.