調節器(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函數可以分為幾個步驟,具體如下:
- 為PMIC提供的所有調節器定義一個struct regulator_desc對象數組。在這一步中,您應該定義一個有效的 struct regulator_ops,以鏈接到適當的regulator_desc。所有的regulator_ops都可以是相同的,假設它們都支持相同的操作。
- 現在,在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:
- 處理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