linux Regulator电源设备驱动框架

本文详细介绍了Linux内核中电源管理的组成部分,特别是调节器(Regulator)和电源管理集成电路(PMIC)的驱动程序设计。内容涵盖了regulator_desc和regulator_init_data结构,以及如何通过设备树(DT)配置和初始化调节器。此外,还讨论了消费者如何与调节器交互以控制电压和电流。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

        调节器Regulator是为其他设备供电的设备。由regulator供电的设备称为消费者。提供调节器的芯片称为电源集成电路PMIC。调节器可以启用/禁用其输出,也可以控制其输出电压和电流。

一、PMIC/生产者驱动程序

        生产者是产生调节电压电流的设备,这个设备的名称是PMIC,可用于控制加电时序、电池管理、DC-DC转换或简单的电源开关。

1. 驱动程序数据结构

i). 调节器描述结构regulator_desc

        内核通过struct regulator_desc{}描述PMIC提供的每个调节器。所谓的调节器是一个可以独立调节的输出,例如Intersil的ISL6217A是一款具有3个独立调节输出的PMIC,因此其驱动程序应该有3个regulator_desc{}实例。

struct regulator_desc {
    const char* name;
    const char *of_match;
    int id; // 调节器的数字标识
    unsigned n_voltages; // 调节器可输出的数值数量,如果是固定输出电压,则为1.
    const struct regulator_ops *ops;
    int irq; // 调节器的中断号
    enum regulator_type type; // 是电压调节器还是电流调节器,REGULATOR_VOLTAGE, REGULATOR_CURRENT
    struct module *owner;
    uint min_uV; // 调节器可以输出的最小电压值
    uint uV_step; // 每个选择器的电压增量
};

ii). 调节器限制结构regulator_reconstraints{}

        当PMIC向消费者公开调节器时,它必须借助于struct regulator_restraints{}结构为调节器加一些名义上的限制。这个结构收集调节器的安全限制,定义消费者不能跨越的边界。这是调节器驱动程序和消费者驱动程序之间的一种约定:

struct regulator_constraints {
    const char *name;
    //电压输出范围
    int min_uV;
    int max_uV;
    int uV_offset; // 应用于消费者的电压偏移量,以补偿电压下降
    // 电流输出范围
    int min_uA;
    int max_uA;

    uint valid_modes_mask; // 消费者可能配置的模式的掩码
    uint valid_ops_mask; // 消费者可能执行的操作的掩码
    
    // 系统位于磁盘模式、内存模式、待机模式时regulator的状态;
    struct regulator_state state_disk;
    struct regulator_state state_mem;
    struct regulator_state state_standby;
    suspend_state_t initial_state; // 在init处设置挂起状态

    uint initial_mode; // 启动时要设置的模式
    unsigned always_on:1;
    unsigned boot_on:1; 
    unsigned apply_uV:1;  //
};

iii). regulator初始化结构regulator_init_data{}

初始化数据结构struct regulator_init_data{},可以通过SoC文件或DT树把初始化数据结构传递给驱动程序,在DT树模式下可以用of_get_regulator_init_data()获取数据结构:

struct regulator_init_data {
    struct regulation_constraints constraints;
    int (*regulator_init)(void *driver_data);
    void *driver_data;
};

// 将初始化数据放入SoC的开发板文件中,示例intersil的ISL6271A
static struct regulator_init_data isl_init_data[] = {
[0] = { .constraints = { .name = "Core Buck",
    .min_uV = 850000,
    .max_uV = 1600000,
    .valid_modes_mask = REGULATOR_MODE_NORMAL | REGULATOR_MODE_STANDBY,
    .valid_ops_mask = REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_STATUS,
    },
},
[1] = { .constraints = { .name = "LDO1",
    .min_uV = 1100000,
    .max_uV = 1100000,
    .always_on = true,
    .valid_modes_mask = REGULATOR_MODE_NORMAL | REGULATOR_MODE_STANDBY,
    .valid_ops_mask = REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_STATUS,
    },
},
[0] = { .constraints = { .name = "LDO2",
    .min_uV = 1300000,
    .max_uV = 1300000,
    .always_on = true,
    .valid_modes_mask = REGULATOR_MODE_NORMAL | REGULATOR_MODE_STANDBY,
    .valid_ops_mask = REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_STATUS,
    },
},
};

iv). 在DT文件中绑定regulator

DT文件中绑定,每个PMIC设备的节点都需要有一个名为regulators的子节点,在子节点regulators中声明PMIC的每个调节器regulator。各个调节器具体DT节点属性和具体示例如下:

// 和regulator相关的标准属性
/*
    regulator-name:字符串,作为调节器输出的描述性名称;
    regulator-min-microvolt:消费者可以设置的最低电压
    regulator-max-microvolt:消费者可以设置的最高电压
    regulator-microvolt-offset:应用于电压的偏移量,以补偿消费者的电压下降
    regulator-min-microamp:消费者可以设置的最小电流
    regulator-max-microamp:消费者可以设置的最大电流
    regulator-always-on:bool值,说明调节器永不禁用
    regulator-boot-on:由引导加载程序/固件启用的调节器
    <name>-handler : 指向父节点/调节器节点的phandle
    regulator-ramp-delay:调节器的斜坡延迟(单位uV/uS)
    这些属性和regulator_init_data{}.regulator_constraints{}中的字段基本相同
*/

// 示例,ISL6271A的驱动程序的DT如下:
isl6271a@3c {
    compatible = "isl6271a";
    reg = <0x3c>;
    interrupts = <0 86 0x4>;
    
    in-v1-supply = <&some_reg>; // 假设该PMIC由另一个调节器供电
    [...]

    regulators {
        reg1: core_buck {
            regulator-name="Core Buck";
            regulator-min-microvolt = <850000>;
            regulator-max-microvolt = <1600000>;
        };
        reg2: ldo1 {
            regulator-name="LDO1";
            regulator-min-microvolt = <1100000>;
            regulator-max-microvolt = <1100000>;
            regulator-always-on;
        };
        reg3: ldo2 {
            regulator-name="LDO1";
            regulator-min-microvolt = <1300000>;
            regulator-max-microvolt = <1300000>;
            regulator-always-on;
        };
    };
};

        使用DT文件的方式把初始化数据引入到regulator驱动程序中:

// 为了使用DT,首先需要引入数据结构struct struct_regulator_match{}
struct struct_regulator_match{
    const char *name;
    void *driver_data;
    struct regulator_init_data *init_data;
    struct device_node *of_node;
    const struct regulator_desc *desc;
};

        在PMIC驱动程序的probe函数中,使用内核辅助函数of_regulator_match()时,将regulators的子节点作为参数传递给它,该函数将遍历每个调节器的节点,并为其建立一个struct regulator_init_data{}结构;

v). regulator配置结构regulator_config{}

调节器设备通过struct regulator_config{}结构进行配置。在向内核注册调节器时,该结构被传递给regulator框架:

struct regulator_config {
    struct device *dev;  // 调节器设备所属的struct device
    const struct regulator_init_data *init_data;
    void *driver_data; // 保存调节器的私有数据
    struct device_node *of_node;
};

vi). regulator设备操作结构

        调节器设备通过struct regulator_config{}是表示调节器可执行的所有操作的回调列表。

struct regulator_ops {
    // 枚举支持的电压值
    int (*list_voltage)(struct regulator_dev *reg, unsigned selector);
    // 获取或设置电压调节器
    int (*set_voltage)(struct regulator_dev *reg, int min_uV,
                        int max_uV, uint *selector);
    int (*map_voltage)(struct regulator_dev *reg, int min_uV,
                        int max_uV);
    int (*set_voltage_sel)(struct regulator_dev *reg, uint selector);
    int (*get_voltage)(struct regulator_dev *reg);
    int (*get_voltage_sel)(struct regulator_dev *reg);

    // 获取或设置电流调节器
    int (*set_current_limit)(struct regulator_dev *reg, int min_uA, int max_uA);
    int (*get_current_limit)(struct regulator_dev *reg);
    int (*set_input_current_limit)(struct regulator_dev *reg, int lim_uA);
    int (*set_owner_current_protection)(struct regulator_dev *reg);
    int (*set_active_discharge)(struct regulator_dev *reg, bool enable);

    int (*enable)(struct regulator_dev *reg);
    int (*disable)(struct regulator_dev *reg);
    int (*is_enabled)(struct regulator_dev *reg);

    int (*set_mode)(struct regulator_dev *reg, uint mode);
    uint (*get_mode)(struct regulator_dev *reg);
};


// 在这些回调函数中参数为regulator_dev{},调用rdev_get_id()可获得对应调节器的id,
int rdev_get_id(struct regulator_dev *rdev);

2. 驱动程序方法

1. probe函数的实现

PMIC驱动程序的实现可分为几个步骤:

  • 为该PMIC提供的所有调节器定义为regulator_desc{}对象数组。并确保已经定义了有效的struct regulator_ops{}实例。假设所有的调节器都支持相同的操作,则它们都有相同的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()(或者devres版本的devm_regulator_register)将调节器注册到内核,并用regulator_desc和regulator_config作为参数。

其中regulator_register()函数的原型:

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

函数regulator_register()返回struct regulator_dev{},这个结构体代表regulator生产者一端的调节器设备实例。

2. remove函数的实现

        驱动程序的remove回调函数回滚probe函数期间的每个操作。其中从内核中删除调节器的函数是:

void regulator_unregister(struct regulator_dev *rdev);

3. 示例:Inter ISL6271A驱动的实现

        该PMIC提供3个调节器,其中一个可以改变输出值,两个提供固定电压:

  i). PMIC驱动的内部结构

struct isl_pmic {
    struct i2c_client *client;
    struct regulator_dev *rdev[3];
    struct mutex mtx;
};

ii). 实现regulator_ops的回调函数

static int isl6271a_get_voltage_sel(struct regulator_dev *rdev) {
    struct isl_pmic *pmic = rdev_get_drvdata(rdev);
    int idx = rdev_get_id(rdev);
    int ret = i2c_smbu_read_byte(pmic->client);
    if (ret<0) [...]; // 错误处理
    return ret;
}

static int isl6271a_set_voltage_sel(struct regulator_dev *rdev, uint selector) {
    struct isl_pmic *pmic = rdev_get_drvdata(rdev);
    int ret = i2c_smbu_write_byte(pmic->client, selector);
    if (ret<0) [...]; // 错误处理
    return ret;
}

iii). 完成回调定义后,可以构建struct regulator_ops{}

static struct regulator_ops isl_core_ops = {
    .get_voltage_sel = isl6271a_get_voltage_sel,
    .set_voltage_sel = isl6271a_set_voltage_sel,
    .list_voltage = regulator_list_voltage_linear,
    .map_voltage = regulator_map_voltage_linear,
};

static struct regulator_ops isl_fixed_ops = {
    .list_voltage = regulator_list_voltage_linear,
};

// 为所有的调节器构建struct regulator_desc{}数组
static struct regulator_desc isl_rd[] = {
{
    .name = "Core Buck",
    .id = 0,
    .n_voltages = 16,
    .ops = &isl_core_ops,
    .type = REGULATOR_VOLTAGE,
    .owner = THIS_MODULE,
    .min_uV = ISL6217A_VOLTAGE_MIN,
    .uV_step = ISL6217A_VOLTAGE_STEP,
},
{
    .name = "LDO1",
    .id = 1,
    .n_voltages = 1,
    .ops = &isl_fixed_ops,
    .type = REGULATOR_VOLTAGE,
    .owner = THIS_MODULE,
    .min_uV = 1100000,
},
{
    .name = "LDO2",
    .id = 1,
    .n_voltages = 1,
    .ops = &isl_fixed_ops,
    .type = REGULATOR_VOLTAGE,
    .owner = THIS_MODULE,
    .min_uV = 1300000,
},
};

LDO1和LDO2具有固定输出电压,其n_voltages属性为1。

iv). 接着在probe函数中构建struct regulator_init_data结构,这将使用前面接收的struct of_regulator_match{},下面是声明的类型:

static struct of_regulator_match isl6217a_matches[] = {
{.name = "Core_buck",}, {.name="ldo1",},{.name="ldo2",},
};

v). 下面是probe函数的实现,

        PMIC由3个调节器,在probe()中需要调用3次regulator_register()。

static int isl_6271a_probe(struct i2c_client *i2c,
                    const struct i2c_device_id *id) {
    struct regulator_config config = {};
    struct regulator_init_data *init_data = 
                        dev_get_platdata(&i2c->dev);
    struct isl_pmic *pmic;
    int i, ret;
    struct device *dev = &i2c->dev;
    struct device_node *np, *parent;

    if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
        return -EIO;
    pmic = devm_kzalloc(&i2c->dev, sizeof(*pmic), GFP_KERNEL);
    if (!pmic) return -ENOMEM;
    np = of_node_get(dev->of_node);
    parent = of_get_child_by_name(np, "regulators");
    // 1. 此处会给isl6217a_matches赋值
    ret = of_regulator_match(dev, parent, isl6217a_matches,
                                ARRAY_SIZE(isl6217a_matches));
    pmic->client = i2c;
    mutex_init(&pmic->mtx);
    // 2. 循环注册3个调节器
    for (i=0;i<3;i++) {
        struct regulator_init_data *init_data;
        struct regulator_desc *desc;
        int val;
        // 2.1 此处要区分是否使用DT, 获取regulator_init_data{}
        if (pdata) config.init_data = pdata->init_data[i];
        else config.init_data = isl6217a_matches[i].init_data;
        config.dev = &i2c->dev;
        config.of_node = isl6217a_matches[i].of_node;
        config.ena_gpio = -EINVAL;
        // 2.2 注册regulator
        pmic->rdev[i] = devm_regulator_register(&i2c->dev, &isl_rd[i], &config);
    }
    i2c_set_clientdata(i2c, pmic);
    return 0;
}

二、调节器消费者接口

        调节器消费者可以是静态和动态的,静态消费者只需要固定电源,动态消费者则需要在运行时对调节器进行主动管理。从消费者角度来看,调节器在内核中表示为struct regulator{}

struct regulator {
    struct device *dev;
    struct list_head list;
    uint always_on:1;
    uint bypass:1;
    int uA_load;
    int min _uV;
    int max_uV;
    char *supply_name;
    struct device_attribute dev_attr;
    struct regulator_dev *dev;
    struct dentry *debugfs;
};

1 消费者调节器设备请求

        调用regulator_get()获取regulator消费者实例;

// 其中id是regulator在DT中的名称
struct regulator *regulator_get(struct device *dev, const char *id);

//释放regultaor,在释放之前必须确保所有的regulator必须被调用了regulator_disable();
void regulator_put(struct regulator *reg);

//如下示例,一个消费者有可能有多个电源
digital = regulator_get(dev, "vcc");
analog = regulator_get(dev, "Avdd");

2. 控制调节器设备

// 使能或禁止regulator
int regulator_enable(struct regulator *reg);
int regulator_disable(struct regulator *reg);

int regulator_is_enabled(struct regulator *reg);

// 对应共享的调节器,只有其引用计数为0时,才真正会禁用reg

// 设置电压
int regulato_set_voltage(struct regulator *reg, int min_uV, int max_uV);
int regulato_get_voltage(struct regulator *reg);

// 设置电流
int regulato_set_current_limit(struct regulator *reg, int min_uA, int max_uA);
int regulato_get_current_limit(struct regulator *reg);

// 设置regulator的状态
int regulator_set_optimum_mode(struct regulator *reg, int load_uA); // 间接模式,根据电流确定需要的模式
int regulator_set_mode(struct regulator *reg, uint mode);
uint regulator_get_mode(struct regulator *reg);

三、调节器绑定

// 消费者可以使用以下方式绑定若干个电源
<name>-supply: phandle to regulator mode;

// 示例
twl_reg1: {};
twl_reg2: {};

mmc: mmc@0x0 {
    vmmc-supply = <&twl_reg1>;
    vmmcaux-supply = <&twl_reg2>;
};

// 在probe函数中获取regulator
struct regulator *main_regulator = regulator_get(dev, "vmmc");
struct regulator *aux_regulator = regulator_get(dev, "vmmcaux");

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值