linux Regulator电源设备驱动框架

这篇具有很好参考价值的文章主要介绍了linux Regulator电源设备驱动框架。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

        调节器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消费者实例;文章来源地址https://www.toymoban.com/news/detail-803342.html

// 其中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");

到了这里,关于linux Regulator电源设备驱动框架的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Linux驱动开发笔记(四):设备驱动介绍、熟悉杂项设备驱动和ubuntu开发杂项设备Demo

    若该文为原创文章,转载请注明原文出处 本文章博客地址:https://hpzwl.blog.csdn.net/article/details/134533533 红胖子网络科技博文大全:开发技术集合(包含Qt实用技术、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软硬结合等等)持续更新中… 上一篇:《Linux驱动开发笔记(三

    2024年02月05日
    浏览(53)
  • 4、Linux驱动开发:设备-设备号&设备号注册

    🍅点击这里查看所有博文   随着自己工作的进行,接触到的技术栈也越来越多。给我一个很直观的感受就是,某一项技术/经验在刚开始接触的时候都记得很清楚。往往过了几个月都会忘记的差不多了,只有经常会用到的东西才有可能真正记下来。存在很多在特殊情况下有

    2024年02月15日
    浏览(57)
  • Linux驱动之INPUT设备驱动

    目录 一、开发环境 二、编写按键input设备的注册与事件上报         2.1 修改设备树文件                 1 添加 pinctrl 节点                 2、添加 KEY 设备节点                 3、检查 PIN 是否被其他外设使用         2.2 驱动程序编写      

    2024年02月07日
    浏览(50)
  • Linux设备驱动之SPI驱动

    Linux下SPI驱动分成两部分:主机驱动和设备驱动。 主机驱动:         主机侧SPI控制器使用 struct spi_master 描述,该结构体中包含了SPI控制器的序号(很多SoC中存在多个SPI控制器),片选数量,SPI信息传输的速率,配置SPI模式的函数指针(4种模式),实现数据传输的函数指针

    2023年04月11日
    浏览(55)
  • Linux 驱动学习笔记 ——(1)字符设备驱动

    《【正点原子】I.MX6U嵌入式Linux驱动开发指南》学习笔记 字符设备是 Linux 驱动中最基本的一类设备驱动,字节设备就是按照字节流来读写的设备,常见的字符设备包括:LED、蜂鸣器、按键、I2C 以及 SPI 等。 Linux 中一切皆文件,字符设备驱动加载成功后会在 /dev 目录下生成相

    2024年02月08日
    浏览(56)
  • Linux设备驱动——第三章字符驱动

    当对幸福的憧憬过于急切,那痛苦就在人的心灵深处升起。——加缪 本章的目的是编写一个完整的字符设备驱动。我们开发一个字符驱动是因为这一类适合大部分简单的硬件设备。字符驱动也比块驱动易于理解。本章的最终目的是编写一个模块化的字符驱动,但是我们不会在

    2024年02月08日
    浏览(83)
  • Linux驱动开发实战(一)——设备驱动模型

    在早期的Linux内核中并没有为设备驱动提供统一的设备模型。随着内核的不断扩大及系统更加复杂,编写一个驱动程序越来越困难,所以在Linux2.6内核中添加了一个统一的设备模型。这样,写设备驱动程序就稍微容易一些了。本章将对设备模型进行详细的介绍。 设备驱动模型

    2024年02月16日
    浏览(51)
  • I.MX6ULL ARM驱动开发---网络设备驱动框架

      网络驱动是 linux 里面驱动三巨头之一,linux 下的网络功能非常强大,嵌入式 linux 中也常常用到网络功能。前面我们已经讲过了字符设备驱动和块设备驱动,本章我们就来学习一下 linux 里面的网络设备驱动。   网络设备驱动程序的体系结构分为4层,依次为网络协议驱

    2023年04月17日
    浏览(38)
  • Linux设备驱动开发 - 虚拟时钟Clock驱动示例

    By: fulinux E-mail: fulinux@sina.com Blog: https://blog.csdn.net/fulinus 喜欢的盆友欢迎点赞和订阅! 你的喜欢就是我写作的动力! 很多设备里面系统时钟架构极其复杂,让学习Clock驱动的盆友头大。这里我参考S3C2440的clock驱动写了一个virtual clock,即虚拟时钟驱动,分别包含clock的provider和

    2023年04月21日
    浏览(43)
  • Linux设备驱动模型(二)

    基于linux-3.14.16 设备模型(LDM)包括,总线、驱动、设备 以i2c总线为例,下面基本表现出了注册一个总线的过程。 1、定义一个总线bus_type,填充几个回调 其中几个比较重要 , match,总线设备和总线驱动的匹配规则 probe,总线设备和总线驱动匹配后将会执行的回调 2、调用b

    2024年02月05日
    浏览(55)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包