Regulator 框架(一): PMIC /生產者 驅動接口


調節器(regulator)是一種為其他設備供電的電子設備。由調節器供電的設備被稱為消費者。它們消耗調節器提供的電力。大多數調節器可以啟用和禁用他們的輸出,一些也可以控制他們的輸出電壓或電流。驅動程序應該通過特定的函數和數據結構向消費者公開這些功能,這些在稍后討論。
提供物理調節的芯片被稱為電源管理集成電路(PMIC)

 Linux調節器框架被設計用於接口和控制電壓和電流調節器。分為四個單獨的接口,如下所示:

  • PMIC 調節器驅動接口。該接口的結構可以在include/linux/regulator/driver.h中找到。
  • 設備驅動程序的消費者接口。
  • 用於板級配置的機器接口。
  • 用於用戶空間的sysfs接口。

本文章我們將涵蓋以下主題:

  • 介紹PMIC/producer驅動接口、驅動方法和數據結構
  • 研究ISL6271A MIC驅動程序,以及用於測試目的的虛擬穩壓器
  • 調節器消費者接口及其API
  • 調節器(生產者/消費者)與DT(device tree)結合

PMIC /生產者 驅動接口

生產者是產生調節電壓或電流的設備。這種設備的名稱是PMIC,它可以用於功率排序、電池管理、dc - dc轉換或簡單的電源開關(開/關)。它通過軟件控制,從輸入功率調節輸出功率。

它處理調節器驅動程序,特別是生產者PMIC端,這需要幾個頭文件:

#include <linux/platform_device.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/of_regulator.h>

驅動程序的數據結構

我們將首先簡要介紹調節器架使用的數據結構。只介紹生產者接口。

描述結構

內核通過一個 struct regulator_desc 結構來描述PMIC提供的每個調節器,該結構描述了調節器的特征。所謂調節器,我指的是任何獨立的調節輸出。例如,Intersil 的ISL6271A是一個具有三個獨立調節輸出的PMIC。然后在其驅動程序中應該有三個 regulator_desc 實例。這個結構包含了調節器的固定屬性,如下所示:

 1 struct regulator_desc {
 2     const char *name;
 3     const char *of_match;
 4 
 5     int id;
 6     unsigned n_voltages;
 7     const struct regulator_ops *ops;
 8     int irq;
 9     enum regulator_type type;
10     struct module *owner;
11 
12     unsigned int min_uV;
13     unsigned int uV_step;
14 };

為了簡單起見,省略一些字段。完整的結構定義可以在 include/linux/regulator/driver.h 中找到:

  • name:調節器的名稱。
  • of_match: 保存了用於識別DT中的調節器的名稱。
  • id: 調節器的數字標識符。
  • owner: 代表提供調節器的模塊。將該字段設置為 THIS_MODULE。
  • type: 指示調節器是電壓調節器還是電流調節器。它可以是 REGULATOR_VOLTAGE 或 REGULATOR_CURRENT。任何其他值將導致調節器注冊失敗。
  • n_voltage: 表示該調節器可用的選擇器數量。它表示調節器能輸出的值的數量。輸出電壓固定時,n_voltage 應設為1。
  • min_uV:表示該穩壓器能提供的最小電壓值。它是由最低的選擇器給出的電壓。
  • uV_step :表示每個選擇器增加的電壓。
  • ops:ops表示調節器操作表。它是一個指向調節器可以支持的一組操作回調的結構。
  • irq: 調節器的中斷號。

 約束結構

當PMIC向消費者公開一個調節器時,它必須借助結構體regulation_constraints結構對這個調節器施加一些名義上的限制。它是一種結構,收集調節器的安全限制,並定義消費者不能跨越的邊界。這是一種調節器和消費者之間的契約:

 1 struct regulation_constraints {
 2   const char *name;
 3 
 4   /* 電壓輸出范圍(包括)-用於電壓控制 */
 5   int min_uV;
 6   int max_uV;
 7 
 8   int uV_offset;
 9 
10   /* 電流輸出范圍(包括)-用於電流控制 */
11   int min_uA;
12   int max_uA;
13 
14   /* 這台機器有效的調節器操作模式 */
15   unsigned int valid_modes_mask;
16 
17   /* 調節器在本機上的有效操作 */
18   unsigned int valid_ops_mask;
19 
20   struct regulator_state state_disk;
21   struct regulator_state state_mem;
22   struct regulator_state state_standby;
23   suspend_state_t initial_state; /* 在init時設置掛起狀態 */
24 
25   /* 啟動時設置的模式 */
26   unsigned int initial_mode;
27 
28   /* 約束的標志 */
29   unsigned always_on:1; /* 當系統開啟時,調節器永不關閉 */
30   unsigned boot_on:1; /* bootloader/firmware 使能調節器 */
31   unsigned apply_uV:1; /* 當min == max時,應用uV約束 */
32 };

結構中的每個元素描述:

  • min_uV, min_uA, max_uA和max_uV是用戶可以設置的最小電壓/電流值。
  • uV_offset 是應用於電壓從消費者補償電壓下降的偏移量。
  • valid_modes_mask 和 valid_ops_mask 分別是模式和操作的掩碼,可以由消費者配置/執行。
  • always_on 應該被設置, 如果調節器不被禁用。
  • boot_on 應該被設置如果在系統初始啟動時啟用了調節器。如果調節器沒有被硬件或引導加載程序啟用,那么它將在應用約束時啟用。
  • name 是用於顯示目的的約束的描述性名稱。
  • apply_uV 在初始化時應用電壓約束。
  • input_uV 表示該調節器由另一個調節器提供時的輸入電壓。
  • state_disk, state_mem 和 state_standby 定義了當系統在磁盤模式、mem模式或備用模式下掛起時調節器的狀態。
  • initial_state 表示默認設置掛起狀態。
  • initial_mode 是在啟動時設置的模式。

初始化數據的結構

 有兩種方法可以將 regulator_init_data 傳遞給驅動程序,可以通過板級初始化文件中的平台數據或通過 of_get_regulator_init_data 函數的設備樹中的節點來實現:

1 struct regulator_init_data {
2     struct regulation_constraints constraints;
3     /* optional regulator machine specific init */
4     int (*regulator_init)(void *driver_data);
5     void *driver_data; /* core does not touch this */
6 };

結構中各元素的含義如下:

  • constraints 表示調節器的約束。
  • regulator_init 是一個可選的回調函數,在核心注冊調節器時被調用。
  • driver_data 表示傳遞給 regulator_init 函數的數據。

struct regulation_constraints 結構是 init data 的一部分。這可以用以下事實來解釋: 在調節器初始化時,它的約束直接應用於它,遠遠早於任何消費者可以使用它。

將初始化數據填充到板級文件中

該方法包括從驅動程序或板級文件中填充約束數組,並將其用作平台數據的一部分。以下是基於 Intersil 公司 ISL6271A 的樣例,:

 1 static struct regulator_init_data isl_init_data[] = {
 2   [0] = {
 3     .constraints = {
 4       .name = "Core Buck",
 5       .min_uV = 850000,
 6       .max_uV = 1600000,
 7       .valid_modes_mask = REGULATOR_MODE_NORMAL
 8                 | REGULATOR_MODE_STANDBY,
 9       .valid_ops_mask = REGULATOR_CHANGE_MODE
10                 | REGULATOR_CHANGE_STATUS,
11     },
12   },
13   [1] = {
14     .constraints = {
15       .name = "LDO1",
16       .min_uV = 1100000,
17       .max_uV = 1100000,
18       .always_on = true,
19       .valid_modes_mask = REGULATOR_MODE_NORMAL
20                 | REGULATOR_MODE_STANDBY,
21       .valid_ops_mask = REGULATOR_CHANGE_MODE
22                 | REGULATOR_CHANGE_STATUS,
23     },
24   },
25   [2] = {
26     .constraints = {
27       .name = "LDO2",
28       .min_uV = 1300000,
29       .max_uV = 1300000,
30       .always_on = true,
31       .valid_modes_mask = REGULATOR_MODE_NORMAL
32                 | REGULATOR_MODE_STANDBY,
33       .valid_ops_mask = REGULATOR_CHANGE_MODE
34                 | REGULATOR_CHANGE_STATUS,
35     },
36   },
37 };

這個方法現在過時了,盡管在這里詳細描述了一下。新的推薦方法是DT,將在下面介紹。

向DT提供初始化數據

為了提取從DT內部傳遞的init數據,我們需要引入一個新的數據類型,struct of_regulator_match,它看起來像這樣:

struct of_regulator_match {
    const char *name;
    void *driver_data;
    struct regulator_init_data *init_data;
    struct device_node *of_node;
    const struct regulator_desc *desc;
};

在使用這個數據結構之前,我們需要弄清楚如何實現DT文件的調節器綁定。

DT中的每個PMIC節點都應該有一個名為 regulators 的子節點,在這個子節點中,我們必須聲明PMIC提供的每個調節器為專用子節點。換句話說,PMIC的每個調節器都被定義為regulators節點的子節點,而regulators節點又是DT中PMIC節點的子節點。

在調節器節點中可以定義一些標准化的屬性:

  • regulator-name: 這是一個字符串,用作調節器輸出的描述性名稱。
  • regulator-min-microvolt: 這是用戶可以設定的最小電壓。
  • regulator-max-microvolt: 這是用戶可設定的最大電壓。
  • regulator-microvolt-offset: 這是施加在電壓上以補償電壓下降的偏移量。
  • regulator-min-microamp: 這是用戶可以設定的最小電流。
  • regulator-max-microamp: 這是用戶可以設定的最大電流。
  • regulator-always-on: 這是一個布爾值,表示不應該禁用調節器。
  • regulator-boot-on: 這是一個bootloader/firmware 階段使能的調節器。
  • <name>-supply: 這是一個指向父供應/調節節點的phandle。
  • regulator-ramp-delay: 這是調節器的斜坡延遲(用uV/uS表示)

這些屬性實際上很像struct regulator_init_data中的字段。回到ISL6271A驅動程序,它的DT入口應該是這樣的:

 1 isl6271a@3c {
 2   compatible = "isl6271a";
 3   reg = <0x3c>;
 4   interrupts = <0 86 0x4>;
 5 
 6   /* supposing our regulator is powered by another regulator */
 7   in-v1-supply = <&some_reg>;
 8   [...]
 9 
10   regulators {
11     reg1: core_buck {
12       regulator-name = "Core Buck";
13       regulator-min-microvolt = <850000>;
14       regulator-max-microvolt = <1600000>;
15     };
16 
17     reg2: ldo1 {
18       regulator-name = "LDO1";
19       regulator-min-microvolt = <1100000>;
20       regulator-max-microvolt = <1100000>;
21       regulator-always-on;
22     };
23 
24     reg3: ldo2 {
25       regulator-name = "LDO2";
26       regulator-min-microvolt = <1300000>;
27       regulator-max-microvolt = <1300000>;
28       regulator-always-on;
29     };
30   };
31 };

使用內核函數 of_regulator_match(),將regulators子節點作為參數,該函數將遍歷每個調節器設備節點,並為每個設備構建一個 struct regulator_init_data結構體。

配置結構

調節器設備是通過struct regulator_config結構來配置的,該結構包含調節器描述的可變元素。當涉及到用核心注冊一個調節器時,這個結構被傳遞給框架:

struct regulator_config {
    struct device *dev;
    const struct regulator_init_data *init_data;
    void *driver_data;
    struct device_node *of_node;
};

上述代碼可以解釋如下:

  • dev表示調節器所屬的 struct  device 結構。
  • init_data是結構中最重要的字段,因為它包含一個包含調節器約束的元素(特定於機器的結構)。
  • driver_data 持有調節器的私有數據。
  • of_node用於支持DT的驅動。它是用於解析DT綁定的節點。由開發人員來設置這個字段。也可以是NULL。

設備操作結構

struct regulator_ops結構體是一個回調列表,它表示調節器可以執行的所有操作。這些回調函數是輔助函數,由通用內核函數包裝:

 1 struct regulator_ops {
 2   /* enumerate supported voltages */
 3   int (*list_voltage) (struct regulator_dev *,
 4               unsigned selector);
 5 
 6   /* get/set regulator voltage */
 7   int (*set_voltage) (struct regulator_dev *,
 8               int min_uV, int max_uV,
 9               unsigned *selector);
10 
11   int (*map_voltage)(struct regulator_dev *,
12               int min_uV, int max_uV);
13   int (*set_voltage_sel) (struct regulator_dev *,
14               unsigned selector);
15   int (*get_voltage) (struct regulator_dev *);
16   int (*get_voltage_sel) (struct regulator_dev *);
17 
18   /* get/set regulator current */
19   int (*set_current_limit) (struct regulator_dev *,
20                  int min_uA, int max_uA);
21   int (*get_current_limit) (struct regulator_dev *);
22 
23   int (*set_input_current_limit) (struct regulator_dev *,
24                    int lim_uA);
25   int (*set_over_current_protection) (struct regulator_dev *);
26   int (*set_active_discharge) (struct regulator_dev *,
27                   bool enable);
28 
29   /* enable/disable regulator */
30   int (*enable) (struct regulator_dev *);
31   int (*disable) (struct regulator_dev *);
32   int (*is_enabled) (struct regulator_dev *);
33 
34   /* get/set regulator operating mode (defined in consumer.h) */
35   int (*set_mode) (struct regulator_dev *, unsigned int mode);
36   unsigned int (*get_mode) (struct regulator_dev *);
37 };

回調名稱很好地解釋了它們的作用。這里沒有列出其他回調函數,對於這些回調函數,您必須在調節器的約束valid_ops_mask或valid_modes_mask中啟用適當的掩碼,然后消費者才能使用它們。可用的操作掩碼標志在 include/linux/regulator/machine.h 中定義。

因此,給定一個 struct regulator_dev 結構體,您可以通過調用 rdev_get_id() 函數來獲得相應的調節器ID: 

int rdev_get_id(struct regulator_dev *rdev);

驅動方法

驅動程序方法由probe()和remove()函數組成。

probe 函數

PMIC驅動程序的probe函數可以分為幾個步驟,具體如下:

  1. 為PMIC提供的所有調節器定義一個struct regulator_desc對象數組。在這一步中,您應該定義一個有效的 struct regulator_ops,以鏈接到適當的regulator_desc。所有的regulator_ops都可以是相同的,假設它們都支持相同的操作。
  2. 現在,在probe函數中,對於每個調節器,做以下操作:
    • 從平台數據中獲取適當的 struct regulator_init_data 結構體(其中必須已經包含有效的 struct regulator_constraints 結構體),或者從DT中構建一個 struct regulator_constraints 結構體,以便構建一個新的 struct regulator_init_data 對象。
    • 使用前面的 struct regulator_init_data 來設置 struct regulator_config 結構體。如果驅動程序支持 DT,你可以讓 regulator_config.of_node 指向用於提取調節器屬性的節點。
    • 調用 regulator_register()(或托管版本,devm_regulator_register())將調節器注冊到核心,並給出之前的 regulator_desc 和 regulator_config 作為參數。

調節器使用 regulator_register() 函數或 devm_regulator_register() 向內核注冊:

struct regulator_dev * regulator_register(const struct regulator_desc *regulator_desc, const struct regulator_config *cfg)

這個函數返回到目前為止我們還沒有討論過的數據類型:一個 struct regulator_dev 對象,在 include/linux/regulator/driver.h 中定義。該結構代表了生產者端調節器設備的實例(在消費者端是不同的)。struct regulator_dev 結構的實例不應該被任何東西直接使用,除了調節器核心和通知注入(它應該接受互斥而不是其他直接訪問)。也就是說,要從驅動程序中跟蹤已注冊的調節器,您應該保存由注冊函數返回的每個regulator_dev對象的引用。

remove 函數

remove() 函數是之前在探測函數期間執行的所有操作的倒置。因此,當要從系統中刪除一個調節器時,你應該記住的基本函數是 regulator_unregister():

void regulator_unregister(struct regulator_dev *rdev);

這個函數接受一個指向struct regulator_dev 結構體的指針作為參數。這是應保留注冊函數返回的每個regualtor_dev對象的引用的另一個原因。下面是ISL6271A驅動的 remove 函數:

 1 static int __devexit isl6271a_remove(struct i2c_client *i2c)
 2 {
 3   struct isl_pmic *pmic = i2c_get_clientdata(i2c);
 4   int i;
 5 
 6   for (i = 0; i < 3; i++)
 7     regulator_unregister(pmic->rdev[i]);
 8 
 9   kfree(pmic);
10   return 0;
11 }

案例研究- Intersil ISL6271A電壓調節器

該PMIC提供三種調節器裝置,其中只有一種可以改變輸出值。另外兩個提供固定電壓:

1 struct isl_pmic {
2     struct i2c_client *client;
3     struct regulator_dev *rdev[3];
4     struct mutex mtx;
5 };

首先,我們定義了ops回調函數,來建立一個struct regulator_desc:

  1. 處理get_voltage_sel操作的回調函數:
 1 static int isl6271a_get_voltage_sel(struct regulator_dev *rdev)
 2 {
 3   struct isl_pmic *pmic = rdev_get_drvdata(dev);
 4   int idx = rdev_get_id(rdev);
 5   idx = i2c_smbus_read_byte(pmic->client);
 6   if (idx < 0)
 7     [...] /* handle this error */
 8 
 9   return idx;
10 }

下面是處理set_voltage_sel操作的回調函數:

 1 static int isl6271a_set_voltage_sel(
 2               struct regulator_dev *dev, unsigned selector)
 3 {
 4   struct isl_pmic *pmic = rdev_get_drvdata(dev);
 5   int err;
 6 
 7   err = i2c_smbus_write_byte(pmic->client, selector);
 8   if (err < 0)
 9     [...] /* handle this error */
10 
11   return err;
12 }

  2. 既然我們已經完成了回調定義,我們可以構建struct regulator_ops:

 1 tatic struct regulator_ops isl_core_ops = {
 2     .get_voltage_sel = isl6271a_get_voltage_sel,
 3     .set_voltage_sel = isl6271a_set_voltage_sel,
 4     .list_voltage = regulator_list_voltage_linear,
 5     .map_voltage = regulator_map_voltage_linear,
 6 };
 7 
 8 static struct regulator_ops isl_fixed_ops = {
 9     .list_voltage = regulator_list_voltage_linear,
10 };

關於 regulator_list_voltage_linear 和 regulator_list_voltage_linear 函數是從哪里來的。與許多其他調節器助手函數一樣,它們也在 drivers/regulator/helpers.c 中定義。內核為線性輸出調節器提供輔助函數,就像ISL6271A的情況一樣。

現在是時候為所有的調節器構建一個struct regulator_desc數組了:

 1 static const struct regulator_desc isl_rd[] = {
 2   {
 3     .name = "Core Buck",
 4     .id = 0,
 5     .n_voltages = 16,
 6     .ops = &isl_core_ops,
 7     .type = REGULATOR_VOLTAGE,
 8     .owner = THIS_MODULE,
 9     .min_uV = ISL6271A_VOLTAGE_MIN,
10     .uV_step = ISL6271A_VOLTAGE_STEP,
11   }, {
12     .name = "LDO1",
13     .id = 1,
14     .n_voltages = 1,
15     .ops = &isl_fixed_ops,
16     .type = REGULATOR_VOLTAGE,
17     .owner = THIS_MODULE,
18     .min_uV = 1100000,
19   }, {
20     .name = "LDO2",
21     .id = 2,
22     .n_voltages = 1,
23     .ops = &isl_fixed_ops,
24     .type = REGULATOR_VOLTAGE,
25     .owner = THIS_MODULE,
26     .min_uV = 1300000,
27   },
28 };

LDO1和LDO2輸出電壓固定。這就是為什么它們的n_voltage屬性被設置為1,並且它們的操作只提供regulator_list_voltage_linear映射。

  3.現在我們在probe函數中,在這里我們需要構建struct init_data結構體。如果你還記得,我們將使用前面介紹的struct of_regulator_match。我們應該聲明這種類型的數組,在數組中我們應該設置每個調節器的.name屬性,我們需要獲取init_data:

1 static struct of_regulator_match isl6271a_matches[] = {
2     { .name = "core_buck", },
3     { .name = "ldo1", },
4     { .name = "ldo2", },
5 };

仔細看一下,您會注意到.name屬性的值與設備樹中調節器的標簽的值完全相同。這是一個你應該關心和尊重的規則。

現在讓我們看看 probe 函數。ISL6271A提供了三個調節器輸出,這意味着 regulator_register() 函數應該被調用三次:

 1 static int isl6271a_probe(struct i2c_client *i2c,
 2                 const struct i2c_device_id *id)
 3 {
 4   struct regulator_config config = { };
 5   struct regulator_init_data *init_data =
 6                     dev_get_platdata(&i2c->dev);
 7   struct isl_pmic *pmic;
 8   int i, ret;
 9 
10   struct device *dev = &i2c->dev;
11   struct device_node *np, *parent;
12 
13   if (!i2c_check_functionality(i2c->adapter,
14                   I2C_FUNC_SMBUS_BYTE_DATA))
15     return -EIO;
16 
17   pmic = devm_kzalloc(&i2c->dev,
18               sizeof(struct isl_pmic), GFP_KERNEL);
19   if (!pmic)
20     return -ENOMEM;
21 
22   /* Get the device (PMIC) node */
23   np = of_node_get(dev->of_node);
24   if (!np)
25     return -EINVAL;
26 
27   /* Get 'regulators' subnode */
28   parent = of_get_child_by_name(np, "regulators");
29   if (!parent) {
30     dev_err(dev, "regulators node not found\n");
31     return -EINVAL;
32   }
33 
34   /* fill isl6271a_matches array */
35   ret = of_regulator_match(dev, parent, isl6271a_matches,
36                 ARRAY_SIZE(isl6271a_matches));
37   of_node_put(parent);
38   if (ret < 0) {
39     dev_err(dev, "Error parsing regulator init data: %d\n",
40           ret);
41     return ret;
42   }
43 
44   pmic->client = i2c;
45   mutex_init(&pmic->mtx);
46 
47   for (i = 0; i < 3; i++) {
48     struct regulator_init_data *init_data;
49     struct regulator_desc *desc;
50     int val;
51 
52     if (pdata)
53       /* Given as platform data */
54       config.init_data = pdata->init_data[i];
55     else
56       /* Fetched from device tree */
57       config.init_data = isl6271a_matches[i].init_data;
58 
59     config.dev = &i2c->dev;
60     config.of_node = isl6271a_matches[i].of_node;
61     config.ena_gpio = -EINVAL;
62 
63     /*
64     * config is passed by reference because the kernel
65     * internally duplicate it to create its own copy
66     * so that it can override some fields
67     */
68     pmic->rdev[i] = devm_regulator_register(&i2c->dev,
69                               &isl_rd[i], &config);
70     if (IS_ERR(pmic->rdev[i])) {
71       dev_err(&i2c->dev, "failed to register %s\n",
72             id->name);
73       return PTR_ERR(pmic->rdev[i]);
74     }
75   }
76   i2c_set_clientdata(i2c, pmic);
77   return 0;
78 }

對於固定的調節器,init_data可以是NULL。這意味着在ISL6271A中,只有電壓輸出可能改變的調節器可以被分配一個init_data。

1 /* Only the first regulator actually need it */
2 if (i == 0)
3   if(pdata)
4     config.init_data = init_data; /* pdata */
5   else
6     isl6271a_matches[i].init_data; /* DT */
7 else
8   config.init_data = NULL;

前面的驅動程序並沒有填充 struct regulator_desc 的每個字段。這在很大程度上取決於我們為其編寫驅動程序的設備類型。有些驅動程序將整個工作交給調節器核心,只提供調節器核心需要使用的芯片寄存器地址。這類驅動程序使用regmap API,這是一個通用的I2C和SPI寄存器映射庫。例如drivers/regulator/max8649.c就是一個例子。

驅動程序例子

讓我們總結一下前面在一個真實的驅動器中討論的事情,一個帶有兩個調節器的虛擬PMIC,第一個調節器的電壓范圍為850000µV到1600000µV,步長為50000µV,第二個調節器的固定電壓為1300000µV:

  1 #include <linux/init.h>
  2 #include <linux/module.h>
  3 #include <linux/kernel.h>
  4 #include <linux/platform_device.h> /* For platform devices */
  5 #include <linux/interrupt.h> /* For IRQ */
  6 #include <linux/of.h> /* For DT*/
  7 #include <linux/err.h>
  8 #include <linux/regulator/driver.h>
  9 #include <linux/regulator/machine.h>
 10 #define DUMMY_VOLTAGE_MIN 850000
 11 #define DUMMY_VOLTAGE_MAX 1600000
 12 #define DUMMY_VOLTAGE_STEP 50000
 13 
 14 struct my_private_data {
 15     int foo;
 16     int bar;
 17     struct mutex lock;
 18 };
 19 
 20 static const struct of_device_id regulator_dummy_ids[] = {
 21     { .compatible = "packt,regulator-dummy", },
 22     { /* sentinel */ }
 23 };
 24 
 25 static struct regulator_init_data dummy_initdata[] = {
 26     [0] = {
 27         .constraints = {
 28             .always_on = 0,
 29             .min_uV = DUMMY_VOLTAGE_MIN,
 30             .max_uV = DUMMY_VOLTAGE_MAX,
 31         },
 32     },
 33     [1] = {
 34         .constraints = {
 35             .always_on = 1,
 36         },
 37     },
 38 };
 39 
 40 static int isl6271a_get_voltage_sel(struct regulator_dev *dev)
 41 {
 42     return 0;
 43 }
 44 
 45 static int isl6271a_set_voltage_sel(struct regulator_dev *dev, unsigned selector)
 46 {
 47     return 0;
 48 }
 49 
 50 static struct regulator_ops dummy_fixed_ops = {
 51     .list_voltage = regulator_list_voltage_linear,
 52 };
 53 
 54 static struct regulator_ops dummy_core_ops = {
 55     .get_voltage_sel = isl6271a_get_voltage_sel,
 56     .set_voltage_sel = isl6271a_set_voltage_sel,
 57     .list_voltage = regulator_list_voltage_linear,
 58     .map_voltage = regulator_map_voltage_linear,
 59 };
 60 
 61 static const struct regulator_desc dummy_desc[] = {
 62     {
 63         .name = "Dummy Core",
 64         .id = 0,
 65         .n_voltages = 16,
 66         .ops = &dummy_core_ops,
 67         .type = REGULATOR_VOLTAGE,
 68         .owner = THIS_MODULE,
 69         .min_uV = DUMMY_VOLTAGE_MIN,
 70         .uV_step = DUMMY_VOLTAGE_STEP,
 71     }, {
 72         .name = "Dummy Fixed",
 73         .id = 1,
 74         .n_voltages = 1,
 75         .ops = &dummy_fixed_ops,
 76         .type = REGULATOR_VOLTAGE,
 77         .owner = THIS_MODULE,
 78         .min_uV = 1300000,
 79     },
 80 };
 81 
 82 static int my_pdrv_probe (struct platform_device *pdev)
 83 {
 84     struct regulator_config config = { };
 85     config.dev = &pdev->dev;
 86     struct regulator_dev *dummy_regulator_rdev[2];
 87     int ret, i;
 88     for (i = 0; i < 2; i++){
 89         config.init_data = &dummy_initdata[i];
 90         dummy_regulator_rdev[i] = regulator_register(&dummy_desc[i], &config);
 91         if (IS_ERR(dummy_regulator_rdev)) {
 92             ret = PTR_ERR(dummy_regulator_rdev);
 93             pr_err("Failed to register regulator: %d\n", ret);
 94             return ret;
 95         }
 96     }
 97     
 98     platform_set_drvdata(pdev, dummy_regulator_rdev);
 99     return 0;
100 }
101 
102 static void my_pdrv_remove(struct platform_device *pdev)
103 {
104     int i;
105     struct regulator_dev *dummy_regulator_rdev = platform_get_drvdata(pdev);
106     
107     for (i = 0; i < 2; i++)
108         regulator_unregister(&dummy_regulator_rdev[i]);
109 }
110 
111 static struct platform_driver mypdrv = {
112     .probe = my_pdrv_probe,
113     .remove = my_pdrv_remove,
114     .driver = {
115         .name = "regulator-dummy",
116         .of_match_table = of_match_ptr(regulator_dummy_ids),
117         .owner = THIS_MODULE,
118     },
119 };
120 
121 module_platform_driver(mypdrv);
122 MODULE_AUTHOR("xxx");
123 MODULE_LICENSE("GPL");

一旦模塊被加載並且設備匹配,內核將打印如下內容:

Dummy Core: at 850 mV
Dummy Fixed: 1300 mV

然后,你可以檢查下面發生了什么:

# ls /sys/class/regulator/
regulator.0 regulator.11 regulator.14 regulator.4 regulator.7
regulator.1 regulator.12 regulator.2 regulator.5 regulator.8
regulator.10 regulator.13 regulator.3 regulator.6 regulator.9

regulator.13 和 regulator.14 是由我們的驅動添加的。現在讓我們看看它們的屬性:

# cd /sys/class/regulator
# cat regulator.13/name
Dummy Core
# cat regulator.14/name
Dummy Fixed
# cat regulator.14/type
voltage
# cat regulator.14/microvolts
1300000
# cat regulator.13/microvolts
850000

                                                                                                                                              


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM