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函数可以分为几个步骤,具体如下:
- 为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
本文来自博客园,作者:闹闹爸爸,转载请注明原文链接:https://www.cnblogs.com/wanglouxiaozi/p/15089298.html