4.3.16. PMIC 驱动调试指南
4.3.16.1. PMIC 概述
PMIC(Power Management Integrated Circuit,电源
4.3.16.2. PMIC 的功能描述
PMIC 典型应用
PMIC 典型应用简介
电源
转换 与 分配 DC-DC 转换器:高效
转换 输入 电压 为 多路 不同 电压 输出,供 CPU、外设 等 使用。 LDO(低压
差 :适用稳压器) 于 低功耗 场景,提供 稳定 电压 输出(如 传感器、时钟 电路)。
系统
电源 控制 管理
开机/关机 流程:响应 电源 键 信号,控制 CPU 上电 时序 及 复位 操作。 动态
调频 :根据 CPU/GPU 负载调压 (DVFS) 动态 调整 核心 电压,平衡 性能 与 功耗。
保护
机制 集成
过压(OVP)、欠压(UVP)、过流(OCP)、过温(OTP)保护 电路,确保 系统安全 运行。 PMIC 的
典型 应用 之一 就是 进行 热 关断 保护,实施 热 关断 是 为了 防止 因 过热 和 功耗 而 造成 损坏。触发 热 关断 时,PMIC 将 关闭 所有 电源 轨,直到 结温 降 至 设定值 以下,然后 设备 再次 开始 通电。详细 介绍 可以 参考驱动 平台 thermal 系统 章节。调试 说明
HPU3501 典型应用介绍
当前 X5 EVB 上

上
输入
5V IN:外部
电源 输入,提供 给 PMIC 和 其他 外部 DC/DC 转换器 的 电源。
PMIC:
VIN (2.7V-5.5V):PMIC 的
主 电源 输入,接受 2.7V 至 5.5V 的 电压 范围。 I2C:用于
配置 和 监控 PMIC 功能 的 I2C 接口。 POR (Power-On Reset):上电
复位 功能,确保 系统 在 上 电时 处于 已知 状态。 SWx (Switchers):可
配置 的 开关 稳压器,用于 提供 不同 电压 和 电流 的 电源 轨。图中 标识 了 SW1 至 SW5,每个 都 有 不同 的 电压 和 电流 规格。 SW1:0.6V-1.275V,最大
电流 4A SW2:0.6V-1.275V,最大
电流 4A SW3:0.6V-1.275V,最大
电流 4A SW4:0.6V-1.275V,最大
电流 4A SW5:0V-3.875V,最大
电流 3A
LDOx (Low Dropout Regulators):低压
差 稳压器,提供 稳定 的 低噪声 电源 轨。图中 标识 了 LDO1 至 LDO3,每个 都 有 不同 的 电压 和 电流 规格。 LDO1:0.6V-1.3V,最大
电流 0.3A LDO2:1.2V-3.7V,最大
电流 0.3A LDO3:1.2V-3.7V,最大
电流 0.3A
X5 SoC 的
VDDx 系列:这些
是 为 X5 SoC 提供 的 不同 电压 和 电流 的 电源 轨,包括: VDD08_ANON, VDD08_DSP, VDD08_DSP_PLL 等:为 SoC 的
不同 模块(如 模拟、数字 信号处理、PLL 等)提供 电源。 VDD08_DDR, VDDQ_DDR_IV1, VDDA_PLL_DDR 0V8:为 DDR 内存
提供 电源。 VDD08_SOC, VDD08_BPU, VDD08_CPU:为 SoC 的
核心部件(如 CPU、BPU 等)提供 电源。 VDD08_SOC_PLL, VDDAIR_SOC_PLL:为 SoC 的 PLL 提供
电源。 VP_MIPI_PHY 0V8:为 MIPI 接口
提供 电源。 VDDAIR1_USB, VDDAIR1_USER2_VOOH:为 USB 和
用户 接口 提供 电源。 VDDAIR8_PHY 1V8, VDDAIR8_PLL_DDR:为 PHY 和 PLL 提供
电源。 VDD08_GPU:为 GPU 提供
电源。 VDD08_SOC_1V8:为 SoC 的
其他 部分 提供 1.8V 电源。 VDD08_SOC_5V3:为 SoC 提供 5.3V 电源。
在 X5 EVB 中 PMIC 模块
PMIC 功能原理
PMIC 的
电压
调节 电压
转换 :PMIC 内部包含 多个 电压 调节器,能够 将 输入 的 电源 电压 转换 为 不同 的 稳定 输出 电压,为 系统 内 的 不同 模块(如 CPU、GPU、内存、外围设备 等)提供 所 需 的 电源 电压。 电压
调整 :通过调节 输出 电压,PMIC 可以 根据 系统 的 需求 调整 功率,降低功耗 或者 提高 电压 满足 负载 需求。这 通常 与动态 调频 相关,用来调压 (DVFS) 在 不同 负载 下 优化 功耗。
功率
分配 PMIC 管理系统
中 各个 部件 的 电源 轨(电源 通道),确保 各个 模块 得到 适当 的 电源。PMIC 会 分配 电源 给 CPU、GPU、内存、外设 等,同时 根据 系统 负载 调节 电压 和 电流,优化 能效。
电流
和 功率 保护 过
电流 :PMIC 包含保护 过 电流 保护 功能,防止 电流 超过 预定 的 安全值,从而 避免 对 设备 造成 损坏。 过热
保护 :PMIC 会监控 温度,如果 CPU 或 其他 监控 的 组件 温度 过高,会 采取措施 降低功耗 或 关闭 部分 功能,以 防止 设备 过热。
4.3.16.3. PMIC 驱动代码
PMIC 驱动框架
在 Linux 内核
MFD 框架简介
PMIC 驱动
在 MFD 中,每个
MFD 子系统
MFD Core(核心):
提供 MFD 设备
的 注册 和 注销 功能。当 一个 MFD 设备 被 系统 识别 时,MFD Core 负责 将 其 添加 到 系统 中;当 设备 不再 被 使用 或 需要 从 系统 中 移除 时,MFD Core 负责 注销 设备。 管理
对 Cell 的 操作。Cell 是 MFD 设备 中 具有 特定 功能 的 单元,例如 一个 电压 调节器。MFD Core 允许 对 这些 Cell 进行 识别 和 配置。
MFD 设备
驱动 :这
是 与 MFD 设备 交互 的 软件 组件。它 使用 MFD Core 提供 的 接口 来 注册 设备,创建 Cell,并 管理 这些 Cell 的 行为。设备 驱动 负责 实现 与 硬件 交互 的 具体 细节,如 读写 寄存器 等。
MFD Cell 描述符:
描述符
是 一个 数据结构,用于 描述 MFD 设备 中 的 每个 Cell。它 包含 了 Cell 的 特性 和 配置 信息,如 Cell 的 名称、功能、寄存器 映射 等。 MFD 为
每个 Cell 创建 一个 Platform 设备。这 意味着 每个 Cell 都 可以 被 看作 是 一个 独立 的 设备,可以 通过 标准 的 Platform 设备 接口 进行 访问 和 控制。
MFD 的
当
一个 MFD 设备 被 系统 识别(例如,通过 I2C 或 SPI 总线),MFD 设备 驱动 会 使用 MFD Core 的 接口 来 注册 这个 设备。 MFD Core 会为
设备 中 的 每个 Cell 创建 一个 Platform 设备,并 为 这些 Platform 设备 分配资源,如 中断。 驱动程序
和 用户 空间 程序 可以 通过 Platform 设备 接口 来 访问 和 控制 每个 Cell,从而 实现 对 MFD 设备 的 管理 和 控制。
该

Regulator 框架简介
PMIC 中
Regulator 框架
在 Linux voltage and current regulator framework 文档
1. Regulator(调节器):
定义:为
其他 设备 提供 电源 的 电子设备。 功能:大多数
调节器 可以 启用 或 禁用 其 输出,某些 调节器 还 可以 控制 其 输出 电压 和/或 电流。 示例:
Input Voltage -> Regulator -> Output Voltage
2. PMIC(电源
定义:包含
多个 调节器 的 集成电路,通常 还 包含 其他 子系统。 功能:PMIC 用于
管理系统 的 电源 分配 和 电源 管理 功能。
3. Consumer(消费者):
定义:由
调节器 供电 的 电子设备。 分类:
静态
消费者 :不需要 改变 其 供电 电压 或 电流 限制,仅 需要 启用 或 禁用 其 电源。其 供电 电压 由 硬件、引导 程序、固件 或 内核 板 初始化 代码 设置。 动态
消费者 :需要根据 操作 需求 改变 其 供电 电压 或 电流 限制。
4. Power Domain(电源
定义:由
调节器、开关 或 其他 电源 域 的 输出 电源 供电 的 电子电路。 -
Regulator -+-> Switch-1 -+-> Switch-2 --> [Consumer A] | | | +-> [Consumer B], [Consumer C] | +-> [Consumer D], [Consumer E]
这里
描述 了 一个 调节器 和 三个 电源 域: 电源
域1 :Switch-1,消费者 D 和 E。电源
域2 :Switch-2,消费者 B 和 C。电源
域3 :消费者 A。 这三个 电源 域 之间 存在 供给(supplies)关系: Domain-1 --> Domain-2 --> Domain-3
5. Constraints(约束):
定义:用于
定义 性能 和 硬件 保护 的 电源 级别。 分类:
调节器
级别 :由调节器 硬件 的 操作 参数 定义,通常 在 调节器 数据 手册 中 指定。例如: 输出
电压 范围 为800mV 到3500mV。 调节器
电流 输出 限制 为5V 时20mA,10V 时10mA。
电源
域 :在级别 软件 中 由 内核 级板 初始化 代码 定义,用于 限制 电源 域 的 电源 范围。例如 在 上述电源 域 中:结构 电源
域1的 电压 为3300mV。 电源
域2的 电压 范围 为1400mV 到1600mV。 电源
域3的 电流 限制 范围 为0mA 到20mA。
消费者
级别 :由消费者 驱动程序 动态 设置 电压 或 电流 限制 级别。例如: 消费者
背光 驱动程序 请求 将 电流 从5mA 增加 到10mA,以 提高 LCD 照明。
在 Linux 内核
Machine interface(机器
层 :接口) 这个
层 通常 指 的 是 与 特定 硬件平台 相关 的 配置 信息,这些 信息 通过 设备树(Device Tree)来 定义。设备树 描述 了 系统 中 所有 的 硬件 组件,包括 调节器 的 属性,如 支持 的 电压 范围、最大 电流、默认设置 等。 在 Regulator 框架
中,Machine 层 的 信息 用于 初始化 调节器 驱动,确保 调节器 能够 按照 硬件 设计 的 要求 进行 配置。
Regulator Driver interface(调节器
驱动 :接口) 调节器
驱动 是 内核 的 一部分,负责 直接 与 硬件 交互,实现 对 调节器 的 控制。这 包括 启用 或 禁用 调节器、设置 电压 和 电流 等。 调节器
驱动 通常 通过 平台 驱动(Platform Driver)模型 注册 到 内核 中,它们 可以 是 特定 于 硬件 的,也 可以 是 通用 的。
Consumer(消费者):
消费者
是 指 系统 中 依赖 调节器 供电 的 设备 或 模块。这些 设备 通过 Regulator 框架 请求 电源,并 在 不再 需要 时 释放 电源。 消费者
可以 通过 内核 提供 的 API 与 调节器 驱动 交互,如 regulator_get()和regulator_put()来获取 和 释放 调节器,以及 regulator_enable()和regulator_disable()来控制 电源。
Sys-Class-Regulator(用户
空间 :接口) 这个
部分 提供 了 用户 空间 程序 与 内核 调节器 框架 交互 的 接口。通过 sysfs 文件系统,用户 空间 程序 可以 查询 调节器 的 状态,如 当前 电压 和 电流,或者 改变 调节器 的 设置。 这种
接口 使得 用户 空间 程序 能够 动态 地 管理系统 的 电源,例如 在 需要 时 调整 电压 以 优化 性能 或 节省能源。 详细
可以 参考 内核 文档: kernel/Documentation/ABI/testing/sysfs-class-regulator。
Linux Regulator Framework 在

说明
用户
空间 :发起程序(User_Space) 对 调节器 状态 的 读取 或 写入 请求。 Sysfs 接口(Sysfs_Interface):作为
用户 空间 与 内核 空间 的 接口,接收 用户 空间 的 请求 并 转发给 相应 的 Consumer。 Consumer:代表
系统 中 使用 调节器 供电 的 设备 或 模块,它们 通过 Sysfs 接口 请求 电源 管理 操作。 Regulator 驱动(Regulator_Driver):内核
中 的 调节器 驱动程序,负责 处理 Consumer 的 请求,并 与 硬件 交互 以 执行 电源 管理 操作。 Machine:代表
与 特定 硬件平台 相关 的 配置 信息,这些 信息 用于 初始化 调节器 硬件。
Regmap 框架简介
Regmap(寄存器
基于
它
提供 了 一组 统一 的 接口函数(如 regmap_read和regmap_write),用于访问 设备 寄存器,包括 SOC 内部 的 寄存器。 regmap 是 Linux 内核
为了 减少 慢速 I/O 操作 在 驱动 开发 中 的 冗余 开销 而 设计 的,它 提供 了 一种 通用 的 接口 来 操作 硬件 寄存器。 regmap 在
驱动程序 和 硬件 之间 引入 了 缓存 机制,从而 减少 低速 I/O 操作 的 次数,提高 了 访问 效率,但是 可能 会 降低 实时性。
整个 Regmap 分为 3 层,其

该图
regmap 层
regmap_read 和 regmap_write:这是 regmap 子系统
提供 的 两个 主要 接口,用于 从 硬件 寄存器 读取数据 和 向 硬件 寄存器 写入 数据。这些 接口 对 上层 驱动程序 隐藏 了 具体 的 总线 操作 细节。
regcache 层
regcache:这
一层 负责管理 寄存器 的 缓存。regmap 可以 使用 缓存 来 减少 对 硬件 的 访问 次数,从而 提高 性能。图中 列出 了 几种 不同 的 缓存 实现 方式: flat:数组,简单
的 扁平 缓存,不 涉及 复杂 的 数据结构。当 设备 寄存器 很少 时,可以 用 这种 类型 来 缓存 寄存器 值。 lzo:使用 LZO (Lempel–Ziv–Oberhumer) 压缩算法
的 缓存,适用 于 需要 压缩 大量 寄存器 数据 的 场景。这个 算法 有 3 个 特性: 压缩
快 解压
不 需要 额外 内存 压缩比
可以 自动 调节。 可以
理解 为 一个 数组 缓存,套 了 一层 压缩,以 节约 内存。当 设备 寄存器 数量 中等 时,可以 考虑 这种 缓存 类型。
rbtree:使用
红黑树(一种 自 平衡 二叉 查找 树)实现 的 缓存,它 的 特性 是 索引 快,当 设备 寄存器 数量 比较 大,或者 对 寄存器 操作 延时 要求 低时,就 可以 用 这种 缓存 类型。
bus 层
bus:这
一层 定义 了 与 不同 总线 类型 交互 的 具体方法。每种 总线 类型 都 有 其 特定 的 实现,以 支持 regmap 接口。图中 列出 了 几种 常见 的 总线 类型: i2c:用于 I2C 总线
的 实现。 spi:用于 SPI 总线
的 实现。 mmio:用于
内存 映射 I/O(Memory-Mapped I/O)的 实现。 irq:用于
中断 的 实现。
对应
regmap API 函数 这
一层 提供 了 一组 API 函数,用于 简化 寄存器 的 读写操作。这些 函数 是 regmap 框架 对外 的 接口,允许 驱动程序 以 统一 的 方式 访问 硬件 寄存器,而 无需 关心 底层 的 总线 类型 或 具体 的 寄存器 访问 细节。 regmap 核心(core) 这
一层 是 regmap 框架 的 核心,负责管理 寄存器 的 缓存 和 提供 对 不同 缓存 策略 的 支持。缓存 策略 的 选择 可以 影响 性能 和 资源 的 使用。 物理
总线 层 这 一层 定义 了 与 不同 物理 总线 交互 的 具体方法。每种 总线 类型 都 有 其 特定 的 实现,以 支持 regmap API 函数。这些 总线 包括。
regmap 驱动

PMIC 驱动框架说明
先
PMIC 与 MFD:
PMIC 硬件
通过 MFD 框架 注册 到 内核 中。MFD 框架 将 PMIC 的 每个 功能 抽象 为 一个 Cell,并 为 每个 Cell 创建 一个 Platform 设备。 例如,PMIC 芯片 hpu3501通过 MFD 框架
注册,每个 功能(如 LDO 或 DC-DC 转换器)被 抽象 为 一个 Cell。
MFD 与 Regulator:
MFD 框架
作为 中间层,将 PMIC 的 各个 功能模块 抽象 为 独立 的 设备,MFD 框架 为 每个 Cell 创建 的 Platform 设备 可以 被 Regulator 框架 使用。Regulator 框架 通过 这些 Platform 设备 与 PMIC 硬件 通信。 例如,MFD 框架
为 hpu3501的 每个 LDO 创建 一个 Platform 设备,Regulator 框架 通过 这些 设备 控制 电压 和 电流。
Regulator 与 Regmap:
Regulator 框架
通过 Regmap 框架 与 PMIC 硬件 通信。Regmap 框架 提供 了 寄存器 访问 的 API,Regulator 框架 通过 这些 API 读写 PMIC 的 寄存器。 例如,Regulator 框架
通过 regmap_write函数设置 PMIC 的 电压 控制 寄存器。
简而言之,MFD 框架
以 X5 EVB 中 hpu3501 为例,上述
PMIC 驱动

hpu3501 硬件
RTC 主要
Regulator 主要
MFD 驱动代码简介
MFD config 开关
CONFIG_MFD_CORE=y
CONFIG_MFD_CORE 配置
MFD 代码路径
kernel/drivers/mfd/mfd-core.c
kernel/include/linux/mfd/core.h
MFD 关键代码简介
MFD 驱动
// kernel/include/linux/mfd/core.h
/*
* This struct describes the MFD part ("cell").
* After registration the copy of this structure will become the platform data
* of the resulting platform_device
*/
struct mfd_cell {
const char *name;
int id;
int level;
int (*enable)(struct platform_device *dev);
int (*disable)(struct platform_device *dev);
int (*suspend)(struct platform_device *dev);
int (*resume)(struct platform_device *dev);
/* platform data passed to the sub devices drivers */
void *platform_data;
size_t pdata_size;
/* Software node for the device. */
const struct software_node *swnode;
/*
* Device Tree compatible string
* See: Documentation/devicetree/usage-model.rst Chapter 2.2 for details
*/
const char *of_compatible;
/*
* Address as defined in Device Tree. Used to compement 'of_compatible'
* (above) when matching OF nodes with devices that have identical
* compatible strings
*/
u64 of_reg;
/* Set to 'true' to use 'of_reg' (above) - allows for of_reg=0 */
bool use_of_reg;
/* Matches ACPI */
const struct mfd_cell_acpi_match *acpi_match;
/*
* These resources can be specified relative to the parent device.
* For accessing hardware you should use resources from the platform dev
*/
int num_resources;
const struct resource *resources;
/* don't check for resource conflicts */
bool ignore_resource_conflicts;
/*
* Disable runtime PM callbacks for this subdevice - see
* pm_runtime_no_callbacks().
*/
bool pm_runtime_no_callbacks;
/* A list of regulator supplies that should be mapped to the MFD
* device rather than the child device when requested
*/
const char * const *parent_supplies;
int num_parent_supplies;
};
struct mfd_cell 结构
在 Linux 内核
mfd_cell_enable和mfd_cell_disable这
两个 函数 用于 启用 和 禁用 MFD 设备 中 的 单元。它们 会 自动 处理 引用 计数(refcounting),确保 只有 在 设备 第一次 被 启用 或 没有 其他 客户端 使用 时,才 会 调用 单元 的 启用(enable)或 禁用(disable)回调 函数。 extern int mfd_cell_enable(struct platform_device *pdev); extern int mfd_cell_disable(struct platform_device *pdev);
mfd_cell_enable(pdev):启用指定 的 platform 设备(pdev)所 对应 的 MFD 单元。 mfd_cell_disable(pdev):禁用指定 的 platform 设备(pdev)所 对应 的 MFD 单元。
mfd_get_cell这个
内联 函数 用于 获取 创建 特定 platform 设备 的 MFD 单元。 static inline const struct mfd_cell *mfd_get_cell(struct platform_device *pdev) { return pdev->mfd_cell; }
mfd_get_cell(pdev):返回与 给定 的 platform 设备(pdev)关联 的 MFD 单元 的 指针。
mfd_add_devices这个
函数 用于 添加 一组 MFD 设备。它 接受 父 设备、设备 ID、MFD 单元 数组、设备 数量、内存 资源 基 地址、中断 基 地址 和 中断 域 等 参数。 extern int mfd_add_devices(struct device *parent, int id, const struct mfd_cell *cells, int n_devs, struct resource *mem_base, int irq_base, struct irq_domain *irq_domain);
mfd_add_devices(parent, id, cells, n_devs, mem_base, irq_base, irq_domain):在指定 的 父 设备 下 添加 多个 MFD 设备。
mfd_add_hotplug_devices这个
内联 函数 是 mfd_add_devices的一个 特例,用于 添加 热插拔 MFD 设备。它 自动 设置 设备 ID 为 自动 分配(PLATFORM_DEVID_AUTO),并 忽略 内存 资源 基 地址、中断 基 地址 和 中断 域。 static inline int mfd_add_hotplug_devices(struct device *parent, const struct mfd_cell *cells, int n_devs) { return mfd_add_devices(parent, PLATFORM_DEVID_AUTO, cells, n_devs, NULL, 0, NULL); }
mfd_add_hotplug_devices(parent, cells, n_devs):在指定 的 父 设备 下 添加 多个 热插拔 MFD 设备。
mfd_remove_devices和mfd_remove_devices_late这
两个 函数 用于 移除 之前 添加 的 MFD 设备。 extern void mfd_remove_devices(struct device *parent); extern void mfd_remove_devices_late(struct device *parent);
mfd_remove_devices(parent):移除指定 父 设备 下 的 所有 MFD 设备。 mfd_remove_devices_late(parent):在系统 关闭 时 移除 指定 父 设备 下 的 所有 MFD 设备。
devm_mfd_add_devices这个
函数 用于 添加 一组 MFD 设备,并 在 设备 释放 时 自动 移除 它们。它 与 mfd_add_devices类似,但提供 了 设备 模型(devm)支持,以 确保 资源 的 自动 管理。 extern int devm_mfd_add_devices(struct device *dev, int id, const struct mfd_cell *cells, int n_devs, struct resource *mem_base, int irq_base, struct irq_domain *irq_domain);
devm_mfd_add_devices(dev, id, cells, n_devs, mem_base, irq_base, irq_domain):在指定 的 设备 下 添加 多个 MFD 设备,并 在 设备 释放 时 自动 移除 它们。
Regulator 驱动代码简介
Regulator config 开关
CONFIG_REGULATOR=y
CONFIG_REGULATOR_DEBUG=y
CONFIG_REGULATOR_FIXED_VOLTAGE=y
CONFIG_REGULATOR_GPIO=y
CONFIG_REGULATOR配置项 启用 了 调节器 框架。调节器 框架 用于 管理 电压 和 电流 调节器,这些 调节器 负责 为 系统 中 的 不同 组件 提供 稳定 的 电源。 CONFIG_REGULATOR_DEBUG配置项 启用 了 调节器 框架 的 调试 功能。 CONFIG_REGULATOR_FIXED_VOLTAGE配置项 启用 了 固定 电压 调节器 的 支持。固定 电压 调节器 是 一种 简单 的 调节器,它 提供 恒定 的 输出 电压。 CONFIG_REGULATOR_GPIO配置项 启用 了 基于 GPIO(通用 输入输出)的 调节器 支持。
Regulator 代码路径
kernel/include/linux/regulator/driver.h
kernel/drivers/regulator/core.c
kernel/drivers/regulator/devres.c
kernel/drivers/regulator/irq_helpers.c
Regulator 关键代码简介
Regulator 驱动
// kernel/include/linux/regulator/driver.h
struct regulator_desc {
const char *name;
const char *supply_name;
const char *of_match;
bool of_match_full_name;
const char *regulators_node;
int (*of_parse_cb)(struct device_node *,
const struct regulator_desc *,
struct regulator_config *);
int id;
unsigned int continuous_voltage_range:1;
unsigned n_voltages;
unsigned int n_current_limits;
const struct regulator_ops *ops;
int irq;
enum regulator_type type;
struct module *owner;
unsigned int min_uV;
unsigned int uV_step;
unsigned int linear_min_sel;
int fixed_uV;
unsigned int ramp_delay;
int min_dropout_uV;
const struct linear_range *linear_ranges;
const unsigned int *linear_range_selectors;
int n_linear_ranges;
const unsigned int *volt_table;
const unsigned int *curr_table;
......
unsigned int n_ramp_values;
unsigned int enable_time;
unsigned int off_on_delay;
unsigned int poll_enabled_time;
unsigned int (*of_map_mode)(unsigned int mode);
};
结构struct regulator_desc 是 Linux 内核
// kernel/include/linux/regulator/driver.h
struct regulator_dev {
const struct regulator_desc *desc;
int exclusive;
u32 use_count;
u32 open_count;
u32 bypass_count;
/* lists we belong to */
struct list_head list; /* list of all regulators */
/* lists we own */
struct list_head consumer_list; /* consumers we supply */
struct coupling_desc coupling_desc;
struct blocking_notifier_head notifier;
struct ww_mutex mutex; /* consumer lock */
struct task_struct *mutex_owner;
int ref_cnt;
struct module *owner;
struct device dev;
struct regulation_constraints *constraints;
struct regulator *supply; /* for tree */
const char *supply_name;
struct regmap *regmap;
struct delayed_work disable_work;
void *reg_data; /* regulator_dev data */
struct dentry *debugfs;
struct regulator_enable_gpio *ena_pin;
unsigned int ena_gpio_state:1;
unsigned int is_switch:1;
/* time when this regulator was disabled last time */
ktime_t last_off;
int cached_err;
bool use_cached_err;
spinlock_t err_lock;
};
结构struct regulator_dev 定义
// kernel/include/linux/regulator/driver.h
struct regulator_ops {
/* enumerate supported voltages */
int (*list_voltage) (struct regulator_dev *, unsigned selector);
/* get/set regulator voltage */
int (*set_voltage) (struct regulator_dev *, int min_uV, int max_uV,
unsigned *selector);
......
/* get/set regulator current */
int (*set_current_limit) (struct regulator_dev *,
int min_uA, int max_uA);
int (*get_current_limit) (struct regulator_dev *);
......
/* enable/disable regulator */
int (*enable) (struct regulator_dev *);
int (*disable) (struct regulator_dev *);
......
/* report regulator status ... most other accessors report
* control inputs, this reports results of combining inputs
* from Linux (and other sources) with the actual load.
* returns REGULATOR_STATUS_* or negative errno.
*/
int (*get_status)(struct regulator_dev *);
/* get most efficient regulator operating mode for load */
unsigned int (*get_optimum_mode) (struct regulator_dev *, int input_uV,
int output_uV, int load_uA);
/* set the load on the regulator */
int (*set_load)(struct regulator_dev *, int load_uA);
/* control and report on bypass mode */
int (*set_bypass)(struct regulator_dev *dev, bool enable);
int (*get_bypass)(struct regulator_dev *dev, bool *enable);
/* the operations below are for configuration of regulator state when
* its parent PMIC enters a global STANDBY/HIBERNATE state */
/* set regulator suspend voltage */
int (*set_suspend_voltage) (struct regulator_dev *, int uV);
/* enable/disable regulator in suspend state */
int (*set_suspend_enable) (struct regulator_dev *);
int (*set_suspend_disable) (struct regulator_dev *);
/* set regulator suspend operating mode (defined in consumer.h) */
int (*set_suspend_mode) (struct regulator_dev *, unsigned int mode);
int (*resume)(struct regulator_dev *rdev);
int (*set_pull_down) (struct regulator_dev *);
};
结构struct regulator_ops 定义
Linux 内核
调节器
注册 与 注销 下面
这些 函数 用于 在 内核 中 注册 和 注销 调节器 设备: // kernel/drivers/regulator/core.c struct regulator_dev *regulator_register(struct device *dev, const struct regulator_desc *regulator_desc, const struct regulator_config *config); // kernel/drivers/regulator/devres.c struct regulator_dev *devm_regulator_register(struct device *dev, const struct regulator_desc *regulator_desc, const struct regulator_config *config); // kernel/drivers/regulator/devres.c void regulator_unregister(struct regulator_dev *rdev);
regulator_register:注册单个 调节器 设备。 devm_regulator_register:注册调节器 设备,并 在 设备 销毁 时 自动 注销。 regulator_unregister:注销已 注册 的 调节器 设备。
事件
通知 用于
处理 调节器 事件 的 函数: // kernel/drivers/regulator/core.c int regulator_notifier_call_chain(struct regulator_dev *rdev, unsigned long event, void *data);
regulator_notifier_call_chain:触发调节器 事件,通知 所有 注册 的 监听器。
中断
处理 下面
这些 函数 用于 处理 调节器 相关 的 中断: // kernel/drivers/regulator/devres.c void *devm_regulator_irq_helper(struct device *dev, const struct regulator_irq_desc *d, int irq, int irq_flags, int common_errs, int *per_rdev_errs, struct regulator_dev **rdev, int rdev_amount); // kernel/drivers/regulator/irq_helpers.c void *regulator_irq_helper(struct device *dev, const struct regulator_irq_desc *d, int irq, int irq_flags, int common_errs, int *per_rdev_errs, struct regulator_dev **rdev, int rdev_amount); // kernel/drivers/regulator/irq_helpers.c void regulator_irq_helper_cancel(void **handle);
devm_regulator_irq_helper:使用设备 管理器 帮助 处理 调节器 中断。 regulator_irq_helper:帮助处理 调节器 中断。 regulator_irq_helper_cancel:取消之前 注册 的 中断 处理程序。
调节器
操作 下面
这些 函数 用于 获取 调节器 设备 的 信息 和 状态: // kernel/drivers/regulator/core.c void *rdev_get_drvdata(struct regulator_dev *rdev); // kernel/drivers/regulator/core.c struct device *rdev_get_dev(struct regulator_dev *rdev); // kernel/drivers/regulator/core.c struct regmap *rdev_get_regmap(struct regulator_dev *rdev); // kernel/drivers/regulator/core.c int rdev_get_id(struct regulator_dev *rdev);
rdev_get_drvdata:获取调节器 的 私有 数据。 rdev_get_dev:获取调节器 的 设备 结构 体。 rdev_get_regmap:获取调节器 的 寄存器 映射。 rdev_get_id:获取调节器 的 ID。
电压
映射 与 控制 下面
这些 函数 用于 处理 电压 映射 和 调节器 控制 操作: // kernel/drivers/regulator/irq_helpers.c int regulator_list_voltage_linear(struct regulator_dev *rdev, unsigned int selector); // kernel/drivers/regulator/irq_helpers.c int regulator_map_voltage_linear(struct regulator_dev *rdev, int min_uV, int max_uV); // kernel/drivers/regulator/irq_helpers.c int regulator_set_voltage_sel_regmap(struct regulator_dev *rdev, unsigned sel); // kernel/drivers/regulator/irq_helpers.c int regulator_enable_regmap(struct regulator_dev *rdev); // kernel/drivers/regulator/irq_helpers.c int regulator_disable_regmap(struct regulator_dev *rdev);
regulator_list_voltage_linear:列出调节器 支持 的 电压 值(线性 映射)。 regulator_map_voltage_linear:将电压 值 映射 到 调节器 的 选择器 值(线性 映射)。 regulator_set_voltage_sel_regmap:通过 regmap 接口设置 调节器 的 电压 选择器 值。 regulator_enable_regmap:通过 regmap 接口启用 调节器。 regulator_disable_regmap:通过 regmap 接口禁用 调节器。
辅助
函数 下面
这些 函数 用于 在 注册 调节器 之前 进行 一些 辅助 操作: // kernel/drivers/regulator/irq_helpers.c int regulator_desc_list_voltage_linear_range(const struct regulator_desc *desc, unsigned int selector);
regulator_desc_list_voltage_linear_range:根据调节器 描述符 列出 支持 的 电压 值(线性 范围)。
这些
分类 涵盖 了 调节器 子系统 中 的 主要 操作 和 功能,使得 内核 开发者 可以 方便 地 管理 和 操作 电压/电流 调节器。
Regmap 驱动代码简介
Regmap config 开关
CONFIG_REGMAP=y
CONFIG_REGMAP_I2C=y
CONFIG_REGMAP_SPI=y
CONFIG_REGMAP_MMIO=y
CONFIG_REGMAP_TEE=y
CONFIG_REGMAP=y这个
选项 启用 了 regmap 子系统。 y表示该 选项 被 编译 进 内核。regmap 是 一个 通用 的 寄存器 映射 和 访问 框架,它 为 不同 的 总线(如 I2C、SPI 等)提供 了 统一 的 寄存器 访问 接口。
CONFIG_REGMAP_I2C=y这个
选项 启用 了 对 I2C 总线 的 支持。当 启用 此 选项 时,regmap 子系统 将 能够 通过 I2C 总线 与 设备 进行 通信。
CONFIG_REGMAP_SPI=y这个
选项 启用 了 对 SPI 总线 的 支持。启用 后,regmap 子系统 可以 通过 SPI 总线 访问 设备 寄存器。
CONFIG_REGMAP_MMIO=y这个
选项 启用 了 对 内存 映射 I/O(Memory-Mapped I/O)的 支持。这 允许 regmap 子系统 直接 通过 内存地址 访问 硬件 寄存器。
CONFIG_REGMAP_TEE=y这个
选项 启用 了 对 可信 执行 环境(Trusted Execution Environment,TEE)的 支持。TEE 是 一种 安全 机制,用于 保护 敏感 操作 免受 恶意软件 的 攻击。当 启用 此 选项 时,regmap 子系统 将 能够 与 TEE 进行 交互,一般 用于 安全 相关 的 寄存器 访问。
Regmap 代码路径
kernel/include/linux/regmap.h
kernel/drivers/base/regmap/internal.h
Regmap 关键代码简介
Regmap 驱动
// kernel/include/linux/regmap.h
struct regmap_config {
const char *name;
int reg_bits;
int reg_stride;
int reg_downshift;
unsigned int reg_base;
int pad_bits;
int val_bits;
bool (*writeable_reg)(struct device *dev, unsigned int reg);
bool (*readable_reg)(struct device *dev, unsigned int reg);
bool (*volatile_reg)(struct device *dev, unsigned int reg);
bool (*precious_reg)(struct device *dev, unsigned int reg);
bool (*writeable_noinc_reg)(struct device *dev, unsigned int reg);
bool (*readable_noinc_reg)(struct device *dev, unsigned int reg);
......
unsigned int num_ranges;
bool use_hwlock;
bool use_raw_spinlock;
unsigned int hwlock_id;
unsigned int hwlock_mode;
bool can_sleep;
};
struct regmap_config 结构
// kernel/drivers/base/regmap/internal.h
struct regcache_ops {
const char *name;
enum regcache_type type;
int (*init)(struct regmap *map);
int (*exit)(struct regmap *map);
#ifdef CONFIG_DEBUG_FS
void (*debugfs_init)(struct regmap *map);
#endif
int (*read)(struct regmap *map, unsigned int reg, unsigned int *value);
int (*write)(struct regmap *map, unsigned int reg, unsigned int value);
int (*sync)(struct regmap *map, unsigned int min, unsigned int max);
int (*drop)(struct regmap *map, unsigned int min, unsigned int max);
};
struct regcache_ops 结构
// kernel/include/linux/regmap.h
struct regmap_bus {
bool fast_io;
regmap_hw_write write;
regmap_hw_gather_write gather_write;
regmap_hw_async_write async_write;
regmap_hw_reg_write reg_write;
regmap_hw_reg_noinc_write reg_noinc_write;
regmap_hw_reg_update_bits reg_update_bits;
regmap_hw_read read;
regmap_hw_reg_read reg_read;
regmap_hw_reg_noinc_read reg_noinc_read;
regmap_hw_free_context free_context;
regmap_hw_async_alloc async_alloc;
u8 read_flag_mask;
enum regmap_endian reg_format_endian_default;
enum regmap_endian val_format_endian_default;
size_t max_raw_read;
size_t max_raw_write;
bool free_on_exit;
};
regmap_bus 结构
Regmap 中
regmap_read
功能:尝试
从 指定 的 寄存器 地址 读取数据。 函数
原型: static inline int regmap_read(struct regmap *map, unsigned int reg, unsigned int *val);
形参:
struct regmap *map:指向 regmap 结构体 的 指针,该 结构 体 描述 了 寄存器 映射。 unsigned int reg:要读取 的 寄存器 地址。 unsigned int *val:指向存储 读 取值 的 缓冲区 的 指针。
返回值:如果 regmap API 被
禁用,则 返回 -EINVAL,并发出 警告。
regmap_bulk_read
功能:尝试
从 指定 的 寄存器 地址 开始,批量 读取 多个 寄存器 的 数据。 函数
原型: static inline int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val, size_t val_count);
形参:
struct regmap *map:指向 regmap 结构体 的 指针。 unsigned int reg:要读取 的 第一个 寄存器 地址。 void *val:指向存储 读取数据 的 缓冲区 的 指针。 size_t val_count:要读取 的 寄存器 数量。
返回值:如果 regmap API 被
禁用,则 返回 -EINVAL,并发出 警告。
regmap_update_bits_base
功能:尝试
更新 指定 寄存器 的 特定 位。 函数
原型: static inline int regmap_update_bits_base(struct regmap *map, unsigned int reg, unsigned int mask, unsigned int val, bool *change, bool async, bool force);
形参:
struct regmap *map:指向 regmap 结构体 的 指针。 unsigned int reg:要更新 的 寄存器 地址。 unsigned int mask:要更新 的 位 的 掩码。 unsigned int val:要设置 的 新值。 bool *change:指向一个 布尔值 的 指针,用于 指示 寄存器 值 是否 实际 发生 了 变化。 bool async:指示操作 是否 应 异步 执行。 bool force:指示是否 强制 更新 位,即使 它们 没有 变化。
返回值:如果 regmap API 被
禁用,则 返回 -EINVAL,并发出 警告。
regmap_write
功能:尝试
向 指定 的 寄存器 地址 写入 数据。 函数
原型: static inline int regmap_write(struct regmap *map, unsigned int reg, unsigned int val);
形参:
struct regmap *map:指向 regmap 结构体 的 指针。 unsigned int reg:要写入 的 寄存器 地址。 unsigned int val:要写入 的 值。
返回值:如果 regmap API 被
禁用,则 返回 -EINVAL,并发出 警告。
regmap_bulk_write
功能:尝试
从 指定 的 寄存器 地址 开始,批量 写入 多个 寄存器 的 数据。 函数
原型: static inline int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val, size_t val_count);
形参:
struct regmap *map:指向 regmap 结构体 的 指针。 unsigned int reg:要写入 的 第一个 寄存器 地址。 const void *val:指向要 写入 的 数据 缓冲区 的 指针。 size_t val_count:要写入 的 寄存器 数量。
返回值:如果 regmap API 被
禁用,则 返回 -EINVAL,并发出 警告。
4.3.16.4. PMIC 使用说明
以 hpu3501 的
hpu3501 MFD 层驱动代码
在
代码路径
kernel/drivers/mfd/hpu3501.c
kernel/include/linux/mfd/hpu3501.h
config 开关
CONFIG_MFD_HPU3501
MFD 设备树描述
目前
# kernel/scripts/dtc/include-prefixes/arm64/hobot/x5-evb.dtsi
&i2c2 {
status = "okay";
hpu3501@1c {
compatible = "hobot, hpu3501";
reg = <0x1c>;
status = "okay";
......
该
hpu3501@1c:定义了 一个 名为 hpu3501的节点, @1c表示该 设备 在 I2C 总线 上 的 地址 是0x1C。 compatible = "hobot, hpu3501";:指定了 设备 的 兼容性,这 通常 用于 内核 中 的 驱动程序 匹配。 reg = <0x1c>;:再次明确 了 I2C 设备 的 地址。 status = "okay";:表示该 设备 是 可用 的。
hpu3501 MFD 关键函数说明
初始化:
注册 I2C 设备
初始化 regmap 接口
为 Regulator 和 RTC 注册 MFD 设备
// kernel/drivers/mfd/hpu3501.c
static const struct mfd_cell hpu3501_devs[] = {
{
.name = "hpu3501-regulator",
},
{
.name = "hpu3501-rtc",
},
};
static int hpu3501_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
int ret;
struct hpu3501_dev *hpu3501;
hpu3501 = devm_kzalloc(&i2c->dev, sizeof(struct hpu3501_dev),
GFP_KERNEL);
if (!hpu3501)
return -ENOMEM;
i2c_set_clientdata(i2c, hpu3501);
hpu3501->dev = &i2c->dev;
hpu3501->i2c_client = i2c;
hpu3501->regmap = devm_regmap_init_i2c(i2c, &hpu3501_regmap_config);
if (IS_ERR(hpu3501->regmap)) {
ret = PTR_ERR(hpu3501->regmap);
dev_err(&i2c->dev, "regmap init failed: %d\n", ret);
return ret;
}
ret = devm_mfd_add_devices(hpu3501->dev, PLATFORM_DEVID_AUTO,
hpu3501_devs, ARRAY_SIZE(hpu3501_devs), NULL, 0, NULL);
return ret;
}
static const struct i2c_device_id hpu3501_i2c_id[] = {
{ "hpu3501", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, hpu3501_i2c_id);
#ifdef CONFIG_OF
static const struct of_device_id hpu3501_of_match[] = {
{.compatible = "hobot, hpu3501", },
{},
};
MODULE_DEVICE_TABLE(of, hpu3501_of_match);
#endif
static struct i2c_driver hpu3501_i2c_driver = {
.driver = {
.name = "hpu3501",
.of_match_table = of_match_ptr(hpu3501_of_match),
},
.probe = hpu3501_i2c_probe,
.id_table = hpu3501_i2c_id,
};
static int __init hpu3501_i2c_init(void)
{
return i2c_add_driver(&hpu3501_i2c_driver);
}
读写
// kernel/drivers/mfd/hpu3501.c
//对单个寄存器的写。reg:寄存器地址,val:要写的值
int hpu3501_write(struct device *dev, int reg, uint8_t val)
{
struct hpu3501_dev *hpu3501 = dev_to_hpu3501(dev);
return regmap_write(hpu3501->regmap, reg, val);
}
EXPORT_SYMBOL_GPL(hpu3501_write);
//对单个寄存器的读。reg:寄存器地址,val:存放数据的指针
int hpu3501_read(struct device *dev, int reg, uint8_t *val)
{
struct hpu3501_dev *hpu3501 = dev_to_hpu3501(dev);
unsigned int rval;
int ret;
ret = regmap_read(hpu3501->regmap, reg, &rval);
if (!ret)
*val = rval;
return ret;
}
EXPORT_SYMBOL_GPL(hpu3501_read);
//对连续几个寄存器的写。reg:第一个寄存器的地址,len:寄存器个数,val:要写数据的指针
int hpu3501_writes(struct device *dev, int reg, int len, uint8_t *val)
{
struct hpu3501_dev *hpu3501 = dev_to_hpu3501(dev);
return regmap_bulk_write(hpu3501->regmap, reg, val, len);
}
EXPORT_SYMBOL_GPL(hpu3501_writes);
//对连续几个寄存器的读。reg:第一个寄存器的地址,len:寄存器个数,val:存放数据的指针
int hpu3501_reads(struct device *dev, int reg, int len, uint8_t *val)
{
struct hpu3501_dev *hpu3501 = dev_to_hpu3501(dev);
return regmap_bulk_read(hpu3501->regmap, reg, val, len);
}
EXPORT_SYMBOL_GPL(hpu3501_reads);
//将某个寄存器的某各 bit 设置为1。reg:寄存器地址,bit_mask:对应位的 mask,比如要对 bit0
//操作则是0x1
int hpu3501_set_bits(struct device *dev, int reg, uint8_t bit_mask)
{
struct hpu3501_dev *hpu3501 = dev_to_hpu3501(dev);
return regmap_update_bits(hpu3501->regmap, reg, bit_mask, bit_mask);
}
EXPORT_SYMBOL_GPL(hpu3501_set_bits);
//将某个寄存器的某各 bit 设置为0。reg:寄存器地址,bit_mask:对应位的 mask,比如要对 bit0
//操作则是0x1
int hpu3501_clr_bits(struct device *dev, int reg, uint8_t bit_mask)
{
struct hpu3501_dev *hpu3501 = dev_to_hpu3501(dev);
return regmap_update_bits(hpu3501->regmap, reg, bit_mask, 0);
}
EXPORT_SYMBOL_GPL(hpu3501_clr_bits);
hpu3501 Regulator 层驱动代码
hpu3501 Regulator 代码路径
kernel/drivers/regulator/hpu3501-regulator.c
kernel/drivers/regulator/hpu3501-regulator.h
kernel/include/linux/regulator/driver.h
hpu3501 Regulator CONFIG
CONFIG_REGULATOR_HPU3501
依赖于
CONFIG_MFD_HPU3501
Regulator 设备树描述
HPU3501 Regulator 层
# kernel/scripts/dtc/include-prefixes/arm64/hobot/x5-evb.dtsi
&i2c2 {
status = "okay";
hpu3501@1c {
compatible = "hobot, hpu3501";
reg = <0x1c>;
status = "okay";
hpu3501-regulator {
master;
en_pin_map = <0x15>;
regulators {
vdd08_bpu_1_reg: BUCK2 {
regulator-name = "VCC_BPU";
regulator-min-microvolt = <800000>;
regulator-max-microvolt = <800000>;
regulator-enable-ramp-delay = <3000>;
regulator-ramp-delay = <2000>;
};
vdd08_cpu_reg: BUCK3 {
regulator-name = "VDD08_CPU";
regulator-min-microvolt = <600000>;
regulator-max-microvolt = <1000000>;
regulator-enable-ramp-delay = <3000>;
regulator-ramp-delay = <2000>;
regulator-always-on;
regulator-boot-on;
};
};
};
};
......
该
hpu3501-regulator:定义了 PMIC 的 调节器 功能。 master;:表示这个 调节器 是 主 调节器,负责 电源 管理。 en_pin_map = <0x15>;:定义了 PMIC 的 EN_PIN_RMPR 寄存器 的 值,决定 了 EN pin 可以 控制 哪些 LDO 和 BUCK 的 开关。 vdd08_bpu_1_reg: BUCK2和vdd08_cpu_reg: BUCK3:定义了 两个 调节器,分别 命名 为 VCC_BPU和VDD08_CPU。regulator-name:提供了 调节器 的 名称,用于 在 系统 中 引用。 regulator-min-microvolt和regulator-max-microvolt:定义了 调节器 可以 设置 的 最小 和 最大 电压。 regulator-enable-ramp-delay和regulator-ramp-delay:定义了 电压 调整 时 的 延迟,以 微秒 为 单位。 regulator-always-on和regulator-boot-on:这些属性 表明 VDD08_CPU调节器应该 始终 开启,并且 在 系统启动 时 自动 开启。
关于 Regulator 在
结合VCC_BPU 以及 VDD08_CPU 的
hpu3501 Regulator 驱动代码说明
关键
/**
* master: ture for master PMIC, false for slave PMIC
* en_pin_map: rigister 0x06 configuration
* fault_cfgr: rigister 0x0a configuration
* ocp_cfg1r: rigister 0x08 configuration
* ocp_cfg2r: rigister 0x09 configuration
*/
struct hpu3501_regulator {
bool master;
u32 en_pin_map;
u32 fault_cfgr;
u32 ocp_cfg1r;
u32 ocp_cfg2r;
};
master:表示
该 PMIC 是 master 还是 slave en_pin_map:表示 EN PIN 对应
控制 哪 几路 电 fault_cfgr:异常
配置 ocp_cfg1r:电流
阈值 ocp_cfg2r:电流
阈值 en_pin_map、fault_cfgr、ocp_cfg1r、ocp_cfg2r 分别
对应 寄存器0x06、0x0a、0x08、0x09,请 查看 寄存器 手册 查看 具体 信息
Regulator 函数
static const struct regulator_ops hpu3501_ops = {
.list_voltage = regulator_list_voltage_linear_range,
.map_voltage = regulator_map_voltage_linear_range,
.get_voltage_sel = regulator_get_voltage_sel_regmap,
.set_voltage_sel = dr_regulator_set_voltage_sel_regmap,
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
.is_enabled = regulator_is_enabled_regmap,
};
静态
先
REGULATOR_LINEAR_RANGE 是
// kernel/include/linux/regulator/driver.h
/* Initialize struct linear_range for regulators */
#define REGULATOR_LINEAR_RANGE(_min_uV, _min_sel, _max_sel, _step_uV) \
{ \
.min = _min_uV, \
.min_sel = _min_sel, \
.max_sel = _max_sel, \
.step = _step_uV, \
}
起始
电压( :范围min_uV)的 最小 电压,单位 为 微伏(μV)。 起始
寄存器 :最小值( min_sel)电压 对应 的 寄存器 选择 值。这个 值 是 硬件 寄存器 中 的 一个 数值,用于 设置 电压。 结束
寄存器 :范围值( n_values)内 的 最大 寄存器 选择 值。这个 值 决定 了 范围 内 的 最大 电压。。 步长(
step_uV):相邻寄存器 值 之间 的 电压 差,单位 为 微伏(μV)。实际 反应 的 是 电压 输出 可以 精确 到 的 最小 增量,例如,BUCK1 的 步长 为 5mV,意味着 其 电压 输出 可以 精确 到 5mV 的 增量。
hpu3501 输出8路电,每路
// kernel/drivers/regulator/hpu3501-regulator.c
/* Buck1-Buck4: step 5mv, range: 0v and [0.6v, 1.275v] */
static const struct linear_range hpu3501_voltage_ranges1[] = {
REGULATOR_LINEAR_RANGE(0, 0, 0, 0), // 0V
REGULATOR_LINEAR_RANGE(600000, 1, 120, 0), // 0.6V 固定值
REGULATOR_LINEAR_RANGE(605000, 121, 255, 5000), // 1.205V 到 1.275V,步长为 5mV
};
/* Buck5: step 15mv, range: 0v and [0.6v, 3.825v] */
static const struct linear_range hpu3501_voltage_ranges2[] = {
REGULATOR_LINEAR_RANGE(0, 0, 39, 0), // 0V
REGULATOR_LINEAR_RANGE(600000, 40, 255, 15000), // 0.6V 到 3.825V,步长为 15mV
};
/* Ldo1: step 10mv, range: 0v and [0.6v, 1.3v] */
static const struct linear_range hpu3501_voltage_ranges3[] = {
REGULATOR_LINEAR_RANGE(0, 0, 0, 0), // 0V
REGULATOR_LINEAR_RANGE(600000, 1, 60, 0), // 0.6V 固定值
REGULATOR_LINEAR_RANGE(610000, 61, 129, 10000), // 1.21V 到 1.3V,步长为 10mV
REGULATOR_LINEAR_RANGE(1300000, 130, 255, 0), // 以上,1.3V 固定值
};
/* Ldo2-Ldo3: step 20mv, range: 0v and [1.2v, 3.7v] */
static const struct linear_range hpu3501_voltage_ranges4[] = {
REGULATOR_LINEAR_RANGE(0, 0, 0, 0), // 0V
REGULATOR_LINEAR_RANGE(1200000, 1, 60, 0), // 1.2V 固定值
REGULATOR_LINEAR_RANGE(1220000, 61, 184, 20000), // 2.42V 到 3.7V,步长为 20mV
REGULATOR_LINEAR_RANGE(3700000, 185, 255, 0), // 3.7V 以上,固定值
};
注意:上述 linear_range 结构BUCK1_VOUT_CFGR ~ BUCK5_VOUT_CFGR 以及 LDO1_VOUT_CFGR ~ LDO3_VOUT_CFGR ,以 BUCK1_VOUT_CFGR 寄存器
Buck1 Output Voltage Setting Register (Address=18h)
| Bit | Field | Type | Reset | Description |
|---|---|---|---|---|
| 7:0 | BUCK1_VOUT | R/W | 10100000 | Buck1 output voltage setting from 0.6V to 1.275V with step 5mV. 00000000: 0V 00000001~01111000: 0.6V 01111001: 0.605V …… 11111111: 1.275V |
举例说明
REGULATOR_LINEAR_RANGE(600000, 40, 255, 15000)
这个
600000是范围 的 最小 电压 值,单位 是 微伏(μV),即 0.6V。 40是最小 电压 对应 的 寄存器 选择 值。 255是最大 寄存器 选择 值。 15000是相邻 寄存器 选择 值 之间 的 电压 步长,单位 是 微伏(μV),即 15mV。
计算
最小
电压 :600,000μV(即 0.6V)。最大
电压 :可以通过 以下 计算 得出: 最大 电压=最小 电压+(最大 寄存器 选择 值−最小 寄存器 选择 值)×步长 代入
具体 数值: 最大电压=600,000μV+(255−40)×15,000μV =600,000μV+215×15,000μV =600,000μV+3,225,000μV =3,825,000μV(即 3.825V)
电压
当
寄存器 选择 值为 40时,电压为 600,000μV(即 0.6V)。当
寄存器 选择 值为 41时,电压为 600,000μV + 15,000μV = 615,000μV(即 0.615V)。当
寄存器 选择 值为 42时,电压为 600,000μV + 2 x 15,000μV = 630,000μV(即 0.63V)。以此类推,直到
寄存器 选择 值为 255,电压为 3,825,000μV(即 3.825V)。
如
这样
#define HPU3501_REG(_name, _id, _linear, _step, _vset_mask) \
[ID_##_id] = { \
.name = _name, \
.id = ID_##_id, \
.type = REGULATOR_VOLTAGE, \
.ops = &hpu3501_ops, \
.n_voltages = HPU3501_VOLTAGE_NUM##_step, \
.linear_ranges = hpu3501_voltage_ranges##_linear, \
.n_linear_ranges = \
ARRAY_SIZE(hpu3501_voltage_ranges##_linear), \
.vsel_reg = HPU3501##_##_id##_VSET, \
.vsel_mask = HPU3501_VSET_MASK##_vset_mask, \
.enable_reg = HPU3501_ON_OFF_CTRL, \
.enable_mask = HPU3501##_##_id##_ENA_BIT, \
.disable_val = HPU3501_POWER_OFF, \
.owner = THIS_MODULE, \
}
/*
* name:regulator name used
* id: regulator id, define in hpu3501-regulator.h without prefix "ID_"
* linear: which linear id to chose, refer to hpu3501_voltage_rangesxx array above
* step: how many step this regulator have, refer to datasheet
* vset_mask: refer to datasheet, when set register how many bits are used to control
*/
static const struct regulator_desc hpu3501_regulators[] = {
HPU3501_REG("BUCK1", BUCK1, 1, 256, 8),
HPU3501_REG("BUCK2", BUCK2, 1, 256, 8),
HPU3501_REG("BUCK3", BUCK3, 1, 256, 8),
HPU3501_REG("BUCK4", BUCK4, 1, 256, 8),
HPU3501_REG("BUCK5", BUCK5, 2, 256, 8),
HPU3501_REG("LDO1", LDO1, 3, 256, 8),
HPU3501_REG("LDO2_CFG1", LDO2_CFG1, 4, 256, 8),
HPU3501_REG("LDO2_CFG2", LDO2_CFG2, 4, 256, 8),
HPU3501_REG("LDO3", LDO3, 4, 256, 8),
};
根据hpu3501_regulators 的
BUCK1~BUCK4 使用
的 是 hpu3501_voltage_ranges1配置参数。 BUCK5 使用
的 是 hpu3501_voltage_ranges2配置参数。 LDO1 使用
的 是 hpu3501_voltage_ranges3配置参数。 LDO2_CFG1/LDO2_CFG2 使用
的 是 hpu3501_voltage_ranges4配置参数。 LDO3 使用
的 是 hpu3501_voltage_ranges4配置参数。
hpu3501 RTC 功能简介
说明:X5 EVB 中 hpu3501 RTC 功能
hpu3501 RTC 代码路径
kernel/drivers/rtc/rtc-hpu3501.c
hpu3501 RTC CONFIG 开关
CONFIG_RTC_DRV_HPU3501
依赖于
CONFIG_MFD_HPU3501
hpu3501 RTC 关键代码简介
重要
struct hpu3501_rtc {
struct device *dev;
struct rtc_device *rtc;
unsigned long long epoch_start;
u32 opsel;
u32 pin_alarm_n;
int irq;
struct work_struct irq_work;
};
epoch_start: 起始
时间,在 初始化 的 时候 设置 了1970年1月1日0时0分0秒。设置 的 alarm 时间 不得 早 于 这个 时间 opsel:当 alarm 被
触发 后 hpu3501的 第41引脚 输出 高电平 还是 低电平 pin_alarm_n: hpu3501的
第41个 引脚 链接 的 X5 SOC 的 哪个 GPIO irq: 上述 pin_alarm_n 指定 GPIO 的 irq 号
操作
static const struct rtc_class_ops hpu3501_rtc_ops = {
.read_time = hpu3501_rtc_read_time,
.set_time = hpu3501_rtc_set_time,
.set_alarm = hpu3501_rtc_set_alarm,
.read_alarm = hpu3501_rtc_read_alarm,
.alarm_irq_enable = hpu3501_rtc_alarm_irq_enable,
};
中断
由于
static void hpu3501_alarm_work(struct work_struct *work)
{
struct hpu3501_rtc *rtc = container_of(work, struct hpu3501_rtc,
irq_work);
struct device *pa_dev = to_hpu3501_dev(rtc->dev);
int ret;
u8 status;
/* check alarm bit status then clear alarm flag */
ret = hpu3501_read(pa_dev, HPU3501_RTC_ISR, &status);
if (ret < 0) {
dev_err(rtc->dev, "read failed with err %d\n", ret);
}
if (!(status & HPU3501_ALARM_F)) {
dev_err(rtc->dev, "alarm flag not set\n");
} else {
ret = hpu3501_set_bits(pa_dev, HPU3501_RTC_ISR, HPU3501_ALARM_F);
if (ret < 0) {
dev_err(rtc->dev, "failed to set HPU3501_ALARM_F\n");
}
}
rtc_update_irq(rtc->rtc, 1, RTC_IRQF | RTC_AF);
}
static irqreturn_t hpu3501_alarm_irq_handler(int irq, void *data)
{
struct hpu3501_rtc *rtc = data;
schedule_work(&rtc->irq_work);
return IRQ_HANDLED;
}
4.3.16.5. PMIC 测试
下面
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <linux/i2c-dev.h>
#define I2C_BUS 2 // /dev/i2c-2
#define DEVICE_ADDRESS 0x1C
// Structure to describe a power domain
typedef struct {
const char *name; // Name of the power domain (e.g., "b1", "l2")
uint8_t register_address; // I2C register address
int step_mv; // Voltage step in millivolts
int min_voltage_mv; // Minimum voltage in millivolts
int min_register_value; // Minimum register value
int max_register_value; // Maximum register value
} PowerDomain;
// Array of power domains
const PowerDomain power_domains[] = {
{"b1", 0x18, 5, 600, 120, 255}, // BUCK1
{"b2", 0x1A, 5, 600, 120, 255}, // BUCK2
{"b3", 0x1C, 5, 600, 120, 255}, // BUCK3
{"b4", 0x1E, 5, 600, 120, 255}, // BUCK4
{"b5", 0x20, 15, 600, 40, 255}, // BUCK5
{"l1", 0x22, 10, 600, 60, 130}, // LDO1
{"l2", 0x24, 20, 1200, 60, 185}, // LDO2
{"l3", 0x27, 20, 1200, 60, 185}, // LDO3
};
// Function to execute i2cget command
int execute_i2cget(uint8_t register_address) {
char command[128];
snprintf(command, sizeof(command), "sudo i2cget -y -f %d 0x%02X 0x%02X", I2C_BUS, DEVICE_ADDRESS, register_address);
FILE *fp = popen(command, "r");
if (!fp) {
fprintf(stderr, "Failed to execute i2cget command: %s\n", command);
exit(EXIT_FAILURE);
}
int register_value;
fscanf(fp, "0x%x", ®ister_value);
pclose(fp);
return register_value;
}
// Function to print voltage ranges and step sizes for all power domains
void print_voltage_ranges() {
for (size_t i = 0; i < sizeof(power_domains) / sizeof(power_domains[0]); i++) {
printf("%s: %.3fV to %.3fV, step %dmV (Register Address: 0x%02X)\n",
power_domains[i].name,
power_domains[i].min_voltage_mv / 1000.0,
(power_domains[i].min_voltage_mv + power_domains[i].step_mv * (power_domains[i].max_register_value - power_domains[i].min_register_value)) / 1000.0,
power_domains[i].step_mv,
power_domains[i].register_address);
}
}
// Function to print help information
void print_help(const char *program_name) {
printf("Usage: %s <get|info|h> [domain]\n", program_name);
printf(" get <domain>|all: Get the current voltage for the specified power domain or all domains.\n");
printf(" info: Print voltage ranges and step sizes for all power domains.\n");
printf(" h: Print this help message.\n");
}
// Function to find power domain by name
const PowerDomain *find_power_domain(const char *name) {
for (size_t i = 0; i < sizeof(power_domains) / sizeof(power_domains[0]); i++) {
if (strcmp(power_domains[i].name, name) == 0) {
return &power_domains[i];
}
}
return NULL;
}
int main(int argc, char *argv[]) {
if (argc < 2) {
print_help(argv[0]);
exit(EXIT_FAILURE);
}
if (strcmp(argv[1], "info") == 0) {
print_voltage_ranges();
return 0;
} else if (strcmp(argv[1], "h") == 0) {
print_help(argv[0]);
return 0;
}
if (strcmp(argv[1], "get") == 0) {
if (argc != 3) {
fprintf(stderr, "Error: Invalid number of arguments for 'get' command.\n");
exit(EXIT_FAILURE);
}
if (strcmp(argv[2], "all") == 0) {
for (size_t i = 0; i < sizeof(power_domains) / sizeof(power_domains[0]); i++) {
int register_value = execute_i2cget(power_domains[i].register_address);
int voltage_mv = power_domains[i].min_voltage_mv + (register_value - power_domains[i].min_register_value) * power_domains[i].step_mv;
printf("%s current voltage: %d mV (register value: 0x%X)\n", power_domains[i].name, voltage_mv, register_value);
}
} else {
const PowerDomain *domain = find_power_domain(argv[2]);
if (!domain) {
fprintf(stderr, "Error: Invalid regulator name. Please use 'b1', 'b2', 'b3', 'b4', 'b5', 'l1', 'l2', 'l3'.\n");
print_help(argv[0]);
exit(EXIT_FAILURE);
}
int register_value = execute_i2cget(domain->register_address);
int voltage_mv = domain->min_voltage_mv + (register_value - domain->min_register_value) * domain->step_mv;
printf("%s current voltage: %d mV (register value: 0x%X)\n", domain->name, voltage_mv, register_value);
}
} else {
fprintf(stderr, "Error: Invalid command. Use 'get', 'info', or 'h'.\n");
print_help(argv[0]);
exit(EXIT_FAILURE);
}
return 0;
}
编译
arm_gcc pmic_only_get.c -o pmic_only_get
其中 arm_gcc 是.bashrc 中
# ~/.bashrc
alias arm_gcc='/opt/arm-gnu-toolchain-11.3.rel1-x86_64-aarch64-none-linux-gnu/bin/aarch64-none-linux-gnu-gcc'
该
命令
Usage: ./pmic_only_get <get|info|h> [domain]
./pmic_only_get:这是程序 的 执行 文件名。 <get|info|h>:这是必须 提供 的 第一个 参数,用于 指定 要 执行 的 操作。它 有 三种 可能 的 值: get:用于读取 电压。 info:用于打印 电压 范围 和 步进 值。 h:用于打印 帮助 信息。
[domain]:这是一个 可 选 参数,仅 在 使用 get命令时 需要 提供。它 指定 要 操作 的 电源 域名 称,或者 使用 all来表示 所有 电源 域。
具体
get <domain>|all
get <domain>|all: Get the current voltage for the specified power domain or all domains.
功能:读取
指定 电源 域 或 所有 电源 域 的 当前 电压。 参数:
<domain>:电源域 的 名称(如 b1、b2、l2等)。必须是 程序 支持 的 电源 域 之一。 all:表示读取 所有 电源 域 的 当前 电压。
示例:
./pmic_only_get get b1:读取电源 域 b1的当前 电压。 ./pmic_only_get get all:读取所有 电源 域 的 当前 电压。
info
info: Print voltage ranges and step sizes for all power domains.
功能:打印
所有 电源 域 的 电压 范围 和 步进 值。 参数:无。
示例:
./pmic_only_get info:打印所有 电源 域 的 电压 范围 和 步进 值。
h
h: Print this help message.
功能:打印
帮助 信息,说明 程序 的 用法。 参数:无。
示例:
./pmic_only_get h:打印帮助 信息。
测试日志
root@buildroot:/userdata# ./pmic_only_get h
Usage: ./pmic_only_get <get|info|h> [domain]
get <domain>|all: Get the current voltage for the specified power domain or all domains.
info: Print voltage ranges and step sizes for all power domains.
h: Print this help message.
root@buildroot:/userdata# ./pmic_only_get info
b1: 0.600V to 1.275V, step 5mV (Register Address: 0x18)
b2: 0.600V to 1.275V, step 5mV (Register Address: 0x1A)
b3: 0.600V to 1.275V, step 5mV (Register Address: 0x1C)
b4: 0.600V to 1.275V, step 5mV (Register Address: 0x1E)
b5: 0.600V to 3.825V, step 15mV (Register Address: 0x20)
l1: 0.600V to 1.300V, step 10mV (Register Address: 0x22)
l2: 1.200V to 3.700V, step 20mV (Register Address: 0x24)
l3: 1.200V to 3.700V, step 20mV (Register Address: 0x27)
root@buildroot:/userdata# ./pmic_only_get get b1
b1 current voltage: 800 mV (register value: 0xA0)
root@buildroot:/userdata# ./pmic_only_get get b2
b2 current voltage: 0 mV (register value: 0x0)
root@buildroot:/userdata# ./pmic_only_get get l2
l2 current voltage: 3300 mV (register value: 0xA5)
root@buildroot:/userdata# ./pmic_only_get get all
b1 current voltage: 800 mV (register value: 0xA0)
b2 current voltage: 0 mV (register value: 0x0)
b3 current voltage: 850 mV (register value: 0xAA)
b4 current voltage: 1100 mV (register value: 0xDC)
b5 current voltage: 1800 mV (register value: 0x78)
l1 current voltage: 800 mV (register value: 0x50)
l2 current voltage: 3300 mV (register value: 0xA5)
l3 current voltage: 1800 mV (register value: 0x5A)
注意:上面
另外,利用 PMIC Suspend 测试
关于 PMIC 配合 Thermal 系统
echo 105000 > /sys/devices/virtual/thermal/thermal_zone1/trip_point_2_temp
4.3.16.6. PMIC 常见问题
在
电压输出异常
问题
表现 :部分电源 输出 电压 高于 或 低于正常 范围,可能 导致 设备 不 稳定 或 无法 启动。 解决方案:
检查
电源 负载 是否 超出 设计 范围。 确认
电容 是否 有 足够 的 滤波 作用。 检查
动态 电压 调整 参数设置 是否 正确。 增加
去 耦 电容 以 改善 电源 稳定性。
通信故障
问题
表现 :设备无法 与 PMIC 正常 通信,通常 表现 为 I2C 总线 通信 错误。 解决方案:
检查 I2C 总线
的 连接 是否 正确,包括 设备 地址 是否 配置 正确。 确保
通信线路 没有 电气 噪声 干扰。 使用
滤波器 减少 电磁干扰。
噪声干扰或电源质量问题
问题
描述 :PMIC 输出电源 质量 差,产生 较大 的 噪声 或 纹波,影响 系统 稳定性,尤其 在 高性能 设备 中 尤为 明显。 解决方案:
增加
适当 的 滤波 电容 和 电感,以 减少 输出 纹波 和 噪声。 在
设计 时 选择 适当 的 开关 频率,避免 与 其他 敏感 电路 发生 频率 干扰。 优化 PCB 布局,减少
电源线 长距离 和 交叉,增加 良好 的 接地 设计。 使用
适当 的 屏蔽 材料 和 电磁兼容 性(EMC)设计 以 降低 噪声。
4.3.16.7. 参考文档
Linux voltage and current regulator framework
Regulator Consumer Driver Interface
内核
kernel/Documentation/devicetree/bindings/regulator/regulator.yaml
kernel/Documentation/ABI/testing/sysfs-class-mtd
kernel/Documentation/ABI/testing/sysfs-class-regulator

