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

                                                                                                                                              

posted @ 2021-08-02 18:01  闹闹爸爸  阅读(3500)  评论(0编辑  收藏  举报