zoukankan      html  css  js  c++  java
  • 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
    ");
    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
    ",
    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
    ",
    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
    ", 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

  • 相关阅读:
    编译java蛋疼的一小时
    最简单的Java Applet程序
    枚举任意进程内核对象句柄的方法
    RvmTranslator7.3.2
    PipeCAD
    Open CASCADE Technology 7.5.0 released
    IsoAlgo 环路处理
    OpenCASCADE STEP Color
    RvmTranslator 3D PDF in Ubuntu
    IsoAlgo Symbols
  • 原文地址:https://www.cnblogs.com/wanglouxiaozi/p/15089298.html
Copyright © 2011-2022 走看看