天天看點

電源管理之regulator機制流程

下文轉載自:點選打開連結

現在android/linux為子產品裝置供電有兩種,一種GPIO供電,另一個就是電源管理晶片。

電源管理晶片可以為多裝置供電,且這些裝置電壓電流有所同。為這些裝置提供的穩壓器代碼模型即為regulator。

下面通過下面三個過程分析regulartor供電機制:

1.分析regulator結構體

2.regulator 注冊過程

3.裝置使用regulator過程

一.分析regulator結構體

Regulator子產品用于控制系統中某些裝置的電壓/電流供應。在嵌入式系統(尤其是手機)中,控制耗電量很重要,直接影響到電池的續航時間。是以,如果系統中某一個子產品暫時不需要使用,就可以通過regulator關閉其電源供應;或者降低提供給該子產品的電壓、電流大小。

Regulator的文檔在KERNEL/Documentation/Power/Regulator中。

Regulator與子產品之間是樹狀關系。父regulator可以給裝置供電,也可以給子regulator供電:

父Regulator

--> 子Regulator --> [supply]

--> 裝置[Consumer]

具體細節可參考核心文檔machine.txt。

regulator_dev

regulator_dev代表一個regulator裝置。

struct regulator_dev {

struct regulator_desc *desc; // 描述符,包括regulator的名稱、ID、regulator_ops等

int use_count; // 使用計數

struct list_head list; // regulator通過此結構挂到regulator_list連結清單中

struct list_head slist; // 如果有父regulator,通過此域挂到父regulator的連結清單

struct list_head consumer_list; // 此regulator負責供電的裝置清單

struct list_head supply_list; //此regulator負責供電的子regulator

struct blocking_notifier_head notifier; // notifier,具體的值在consumer.h中,比如REGULATOR_EVENT_FAIL

struct mutex mutex;

struct module *owner;

struct device dev; // device結構,屬于class regulator_class

struct regulation_constraints *constraints; // 限制,比如最大電壓/電流、最小電壓/電流

struct regulator_dev *supply; // 父regulator的指針,即由此regulator 供電

void *reg_data;

};

regulator_init_data

regulator_init_data在初始化時使用,用來建立父子regulator、受電子產品之間的樹狀結構,以及一些regulator的基本參數。

struct regulator_init_data {

struct device *supply_regulator_dev; // 父regulator的指針

struct regulation_constraints constraints;

int num_consumer_supplies;

struct regulator_consumer_supply *consumer_supplies; // 負責供電的裝置數組

int (*regulator_init)(void *driver_data); // 初始化函數,在regulator_register被調用

void *driver_data;

};

其它結構體自己可以看看~如

struct regulator               -------> 裝置驅動直接操作的結構體

struct regulation_constraints       ----->regulator限制範圍,其它資訊,在于

                       struct regulator_init_data,用于初始化

struct regulator_consumer_supply   ----->consumer資訊

struct regulator_desc                           ----->這個多關注些,内有正真操作裝置函數結構體~

struct regulator_map                           ----->這個為consumers與regulator對應表

........................................

............................................

二. 注冊regulator過程

先說明下兩具在regulator的core中有兩個關鍵的全局變量連結清單:

regulator_list 每注冊一個regulator都會挂到這裡

regulator_map_list  每一個regulator都會為多個consumer供電,此表為挂consumer

regulator注冊過程是通過platform平台注冊,當然一個電源管理晶片可以供幾十個裝置供電,是以不可能每個regulator一個驅動檔案,它們是通過,在電源管理晶片驅動中一塊注冊到regulato的core中。

對于電源管理晶片驅動的注冊則通過I2C注冊的。接下來以中星微方案過下,

首先,在平台裝置檔案中,有關struct regulator_init_data XX定義~

如:

….........................

static struct regulator_init_data va7882_ldo13_data = {

.constraints = {

.name = "LDO13-HDMI1V2", //Default: 1.5V , Powered By DCDC5, C-class

.min_uV = 1200000,

.max_uV = 1800000,

.apply_uV = 1,

// TEMP_ON

.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_MODE,

.initial_mode = REGULATOR_MODE_NORMAL,

.valid_modes_mask = REGULATOR_MODE_NORMAL | REGULATOR_MODE_STANDBY,

},

.supply_regulator = supply_regulator_name_arrary[ID_VA7882_LDO13],

.num_consumer_supplies = 2,

.consumer_supplies = (struct regulator_consumer_supply []) {

{ .supply = regulator_name_arrary[ID_VA7882_LDO13][0] },

{ .supply = regulator_name_arrary[ID_VA7882_LDO13][1] },

{ .supply = regulator_name_arrary[ID_VA7882_LDO13][2] },

},

};

…........................................

其實這些結構體又會被同一檔案中的自定義檔案初始化函數 v8_va7882_init調用,其實就是

va7882_register_regulator---->為每個regulator配置設定對應的struct platform_device---->再platform_device_add 

那platform_driver在哪?

static struct platform_driver va7882_regulator_driver = {

.driver = {

.name = "va7882-regulator",

.owner = THIS_MODULE,

},

.probe = va7882_regulator_probe,

.remove = __exit_p(va7882_regulator_remove),

.suspend = va7882_regulator_suspend,    //可見休眠喚醒是使用同一個,

.resume = va7882_regulator_resume,      //再次說明它們是一統管理,它們也是為了節省.

.shutdown = va7882_regulator_shutdown,

};

這個platform_driver是每個regulator共用的的,因為name都是" va7882-regulator"。

在這個platform_driver中probe中就幹了一件事regulator_register,進而獲得regulator_dev。

接下來分析下regulator_register注冊

Regulator的注冊由regulator_register完成。

一般來說,為了添加regulator_dev,需要實作一個裝置驅動程式,以及在闆子的裝置清單中增加一個該驅動對應的裝置(比如platform_device)。在這個裝置的struct device->platform_data域,需要設定regulator_init結構,填寫該regulator的相關資訊。另外,還需要定義一個regulator_desc結構。這樣,在這個實體裝置的驅動程式中,就可以通過regulator_register函數登記生成一個regulator_dev。

struct regulator_dev *regulator_register(struct regulator_desc *regulator_desc, struct device *dev, void *driver_data)

struct regulator_init_data *init_data = dev->platform_data;// 得到init_data

// 完整性檢查

// 配置設定regulator_dev結構

struct regulator_dev *rdev = kzalloc (sizeof(struct regulator_dev), GFP_KERNEL);

// 初始化regulator_dev結構

// 執行regulator_init,該函數中實作regulator代表的硬體裝置的初始化

if (init_data->regulator_init)

ret = init_data->regulator_init(rdev->reg_data);

rdev->dev.class = &regulator_class; // 指定class為regulator_class

rdev->dev.parent = dev;

device_register(&rdev->dev); // 注冊裝置

// 設定constraints,其中可能會包括供電狀态的初始化(設定初始電壓,enable/disable等等)

set_machine_constraints(rdev, &init_data->constraints);

add_regulator_attributes (rdev);

// 如果此regulator有父regulator,設定父regulator

if (init_data->supply_regulator_dev) {

ret = set_supply(rdev,

dev_get_drvdata(init_data->supply_regulator_dev));

if (ret < 0)

goto scrub;

}

// 設定此regulator與其負責供電的裝置之間的聯系

for (i = 0; i < init_data->num_consumer_supplies; i++)

ret = set_consumer_device_supply(rdev, init_data->consumer_supplies[i].dev,

init_data->consumer_supplies[i].supply);

// 将regulator加入一個連結清單,該連結清單包含所有regulator

list_add(&rdev->list, &regulator_list);

….............................

那個regulator 根據在regulator_list中用init_data->supply_regulator來比對

比對成功用set_supply()來設定注冊的regulator是由那個regulator供電,rdev->supply = supply_rdev; list_add(&rdev->slist, &supply_rdev->supply_list);

多個consumer用set_consumer_device_supply(),先檢查

list_for_each_entry(node, &regulator_map_list, list) 後添加

list_add(&node->list, &regulator_map_list);當然node已經在

         node->regulator = rdev;

         node->supply = supply;

形成 對于每一個regulator_dev—comsumer_dev的配對

最後在把regulator通過list_add(&rdev->list, &regulator_list);加到 regulator_list連結清單中。

三.裝置使用regulator過程

 在裝置驅動使用regulator對其驅動的裝置供電時,首先要確定裝置與對應regulator之間的比對關系已經被登記到regulator架構中。

之後,裝置驅動通過regulator_get函數得到regulator結構,此函數通過前文所述regulator_map_list找到對應regulator_dev,再生成regulator結構給使用者使用。

通過regulator_enable / regulator_disable打開、關閉regulator,這兩個函數最終都是調用struct regulator_ops裡的對應成員。

除此之外,還有regualtor_set_voltage / regulator_get_voltage等等。

Regulator能夠支援的所有功能清單都在struct regulator_ops中定義,具體可參考代碼中的注釋。

struct regulator_ops {

int (*set_voltage) (struct regulator_dev *, int min_uV, int max_uV);

int (*get_voltage) (struct regulator_dev *);

int (*set_current_limit) (struct regulator_dev *,

int min_uA, int max_uA);

int (*get_current_limit) (struct regulator_dev *);

int (*enable) (struct regulator_dev *);

int (*disable) (struct regulator_dev *);

int (*is_enabled) (struct regulator_dev *);

int (*set_mode) (struct regulator_dev *, unsigned int mode);

unsigned int (*get_mode) (struct regulator_dev *);

unsigned int (*get_optimum_mode) (struct regulator_dev *, int input_uV,

int output_uV, int load_uA);

int (*set_suspend_voltage) (struct regulator_dev *, int uV);

int (*set_suspend_enable) (struct regulator_dev *);

int (*set_suspend_disable) (struct regulator_dev *);

int (*set_suspend_mode) (struct regulator_dev *, unsigned int mode);

};

接下來我就以中星型的HDMI驅動使用regulator過一遍。

vc088x_hdmi.c檔案:

HDMI驅動也是通過platform平台注冊上去,是以在platform_driver的probe中有這個句

ret = v8hdmi_pwr_get(&pdev->dev);

regulator_get(dev,id);

_regulator_get(dev, id, 0);

{

…........

…...........

if (dev)

devname = dev_name(dev);

list_for_each_entry(map, &regulator_map_list, list) {

if (map->dev_name &&(!devname || strcmp(map->dev_name, devname)))

continue;

if (strcmp(map->supply, id) == 0) {//查找配對

rdev = map->regulator;

goto found;

}

}

….....................

…..........................

//這個用于在sys目錄下建立對應的regulator檔案,用于使用者空間操作

regulator = create_regulator(rdev, dev, id); //regulator -->struct regulator

….............................

return regulator;

}

傳回的regulator會賦給全局變量,如 hdmi_core_consumer = regulator,//這隻是例子,不同方案處理不一樣。

在恰當的時候使能它,如

ret = v8hdmi_pwr_enable();

regulator_enable(hdmi_io_consumer);

struct regulator_dev *rdev = regulator->rdev;

ret = _regulator_enable(rdev);

….................

if (rdev->use_count == 0) {

if (rdev->supply) {

mutex_lock(&rdev->supply->mutex);

ret = _regulator_enable(rdev->supply);//使能父regulator

mutex_unlock(&rdev->supply->mutex);

If (ret < 0) {

rdev_err(rdev, "failed to enable: %d\n", ret);

return ret;

}

}

}

…...............................

ret = rdev->desc->ops->enable(rdev);//調用真正使能操作.

….......................

使能函數到此結束.

總的來看,使用也是通過regulator_get()-->regulator_enable()就可以了

想關時,regulator_disable()--->regulator_put(),反操作~

其實我疑惑是真正操作電源管理晶片那些操作,存放在struct regulator_ops 結構體内

而這個結構體包含在于struct regulator_desc内,這個結構體,在執行注冊regulator時,使用到,被賦到regulator_dev-->desc中~

對于struct regulator_ops中的操作方法,就涉及到電源晶片驅動,下面是va7882的操作方法

static struct regulator_ops va7882_dcdc_ops = {

.set_voltage = va7882_dcdc_set_voltage,

.get_voltage = va7882_dcdc_get_voltage,

.list_voltage = va7882_dcdc_list_voltage,

.enable = va7882_dcdc_enable,

.disable = va7882_dcdc_disable,

.is_enabled = va7882_dcdc_is_enabled,

.get_mode = va7882_dcdc_get_mode,

.set_mode = va7882_dcdc_set_mode,

.get_optimum_mode = va7882_dcdc_get_optimum_mode,

.set_suspend_voltage = va7882_dcdc_set_suspend_voltage,

.set_suspend_enable = va7882_dcdc_set_suspend_enable,

.set_suspend_disable = va7882_dcdc_set_suspend_disable,

.set_suspend_mode = va7882_dcdc_set_suspend_mode,

.enable_time = va7882_enable_time,

};

接後面再讨論電源管理晶片驅動,還有像v8_va7882_init函數來初始化及注冊各種regulator,那誰去調用這個初始函數呢?怎麼的調用流程呢?這些都在電源管理晶片驅動會講到!

繼續閱讀