《Linux 设备驱动开发详解:基于最新的 Linux 4.0 内核》 第 22 章 Linux 电源管理

《Linux 设备驱动开发详解:基于最新的 Linux 4.0 内核》

第 22 章 Linux 电源管理

参考:宋宝华 著,机械工业出版社,2015年版


22.1 Linux 电源管理概述

22.1.1 电源管理的重要性

电源管理是现代嵌入式系统和移动设备的核心需求:

复制代码
电源管理的重要性:

移动设备(手机、平板):
  - 电池容量有限,需要最大化续航时间
  - 用户不使用时,设备应尽量降低功耗
  - 屏幕、Wi-Fi、GPS 等是主要耗电大户

嵌入式系统(IoT 设备):
  - 可能使用电池供电,需要长期运行
  - 某些设备需要超低功耗(μA 级别)

服务器:
  - 降低电费成本
  - 减少散热需求
  - 提高数据中心能效

Linux 电源管理的目标:
  ✓ 在不影响性能的前提下降低功耗
  ✓ 根据负载动态调整 CPU 频率和电压
  ✓ 空闲时让 CPU 进入低功耗状态
  ✓ 不使用的设备自动断电
  ✓ 系统挂起/休眠时最大化节能

22.1.2 Linux 电源管理框架

复制代码
Linux 电源管理框架全景:

┌─────────────────────────────────────────────────────────────┐
│                      用户空间                                │
│  PowerTOP / TLP / pm-utils / systemd-sleep                  │
└──────────────────────────┬──────────────────────────────────┘
                           │
┌──────────────────────────▼──────────────────────────────────┐
│                   Linux 电源管理框架                          │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────────┐  │
│  │   CPUFreq    │  │   CPUIdle    │  │  System Suspend  │  │
│  │  CPU 频率    │  │  CPU 空闲    │  │  系统挂起/休眠   │  │
│  │  动态调节    │  │  状态管理    │  │                  │  │
│  └──────────────┘  └──────────────┘  └──────────────────┘  │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────────┐  │
│  │  Runtime PM  │  │  Regulator   │  │    Thermal       │  │
│  │  设备运行时  │  │  电压/电流   │  │  温度管理        │  │
│  │  电源管理    │  │  调节框架    │  │                  │  │
│  └──────────────┘  └──────────────┘  └──────────────────┘  │
└──────────────────────────┬──────────────────────────────────┘
                           │
┌──────────────────────────▼──────────────────────────────────┐
│                   硬件层                                     │
│  CPU(DVFS)/ 电源管理芯片(PMIC)/ 各外设                   │
└─────────────────────────────────────────────────────────────┘

22.1.3 功耗的来源

复制代码
系统功耗的主要来源:

动态功耗(Dynamic Power):
  P_dynamic = α × C × V² × f
  α:活动因子(电路翻转率)
  C:负载电容
  V:工作电压
  f:工作频率
  → 降低电压和频率可以显著降低动态功耗

静态功耗(Static Power):
  P_static = V × I_leakage
  I_leakage:漏电流
  → 关闭不使用的电源域可以消除静态功耗

主要耗电组件(以手机为例):
  屏幕:30~50%
  CPU/GPU:20~30%
  Wi-Fi/蜂窝:15~25%
  音频:5~10%
  其他外设:5~10%

22.2 CPUFreq

22.2.1 CPUFreq 的概念

CPUFreq(CPU Frequency Scaling)是 Linux 内核的 CPU 频率动态调节子系统,通过 DVFS(Dynamic Voltage and Frequency Scaling,动态电压频率调节)技术,根据系统负载动态调整 CPU 的工作频率和电压:

复制代码
DVFS 工作原理:

高负载时:
  CPU 频率 ↑ → 处理能力 ↑ → 功耗 ↑
  CPU 电压 ↑(维持稳定运行)

低负载时:
  CPU 频率 ↓ → 处理能力 ↓ → 功耗 ↓↓
  CPU 电压 ↓(进一步降低功耗)

功耗与频率/电压的关系:
  P ∝ V² × f
  频率降低一半:功耗降低约 50%
  电压降低 20%:功耗降低约 36%
  两者同时降低:功耗可降低约 60%

22.2.2 CPUFreq 架构

复制代码
CPUFreq 架构:

┌─────────────────────────────────────────────────────────────┐
│                   CPUFreq 核心层                              │
│  cpufreq_register_driver() / cpufreq_register_governor()    │
└──────────────┬──────────────────────────┬───────────────────┘
               │                          │
┌──────────────▼──────────┐  ┌───────────▼───────────────────┐
│   CPUFreq 驱动           │  │   CPUFreq 调速器(Governor)  │
│  (平台相关)             │  │                               │
│  imx6ul-cpufreq          │  │  performance(最高频率)       │
│  arm_big_little          │  │  powersave(最低频率)         │
│  acpi-cpufreq(x86)     │  │  ondemand(按需调节)          │
│                          │  │  conservative(保守调节)      │
│  实现:                  │  │  schedutil(调度器驱动)       │
│  .target()               │  │  userspace(用户控制)         │
│  .get()                  │  │                               │
│  .init()                 │  │  决定何时切换频率              │
└──────────────────────────┘  └───────────────────────────────┘

22.2.3 CPUFreq 调速器

复制代码
CPUFreq 调速器(Governor)详解:

1. performance(性能优先)
   始终使用最高频率
   适用:需要最大性能的场景(游戏、编译)
   功耗:最高

2. powersave(节能优先)
   始终使用最低频率
   适用:对性能要求极低的场景
   功耗:最低

3. ondemand(按需调节,最常用)
   根据 CPU 使用率动态调节频率
   CPU 使用率高 → 立即提升到最高频率
   CPU 使用率低 → 逐步降低频率
   适用:通用场景(桌面、服务器)

4. conservative(保守调节)
   类似 ondemand,但频率变化更平缓
   避免频率剧烈波动
   适用:对延迟不敏感的场景

5. schedutil(调度器驱动,Linux 4.7+)
   基于调度器的负载信息调节频率
   比 ondemand 更精准
   适用:现代 Linux 系统(推荐)

6. userspace(用户控制)
   允许用户空间程序直接设置频率
   适用:测试和调试

22.2.4 CPUFreq 的使用

bash 复制代码
# 查看 CPU 频率信息
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_available_frequencies
# 1200000 1000000 800000 600000 400000 200000
# 单位:kHz

cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq
# 800000   ← 当前频率 800MHz

cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq
# 200000   ← 最低频率 200MHz

cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq
# 1200000  ← 最高频率 1.2GHz

# 查看可用的调速器
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_available_governors
# conservative ondemand userspace powersave performance schedutil

# 查看当前调速器
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor
# ondemand

# 切换调速器
echo performance > /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor

# 使用 userspace 调速器手动设置频率
echo userspace > /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor
echo 600000 > /sys/devices/system/cpu/cpu0/cpufreq/scaling_setspeed

# 使用 cpupower 工具(推荐)
cpupower frequency-info
cpupower frequency-set -g ondemand
cpupower frequency-set -f 800MHz

22.2.5 CPUFreq 驱动实现

c 复制代码
/*
 * 简化的 CPUFreq 驱动框架
 * 以 i.MX6ULL 为例
 */
#include <linux/cpufreq.h>
#include <linux/clk.h>
#include <linux/regulator/consumer.h>

/* OPP(Operating Performance Point):频率-电压对 */
struct imx6ul_opp {
    unsigned long   freq;   /* 频率(Hz)*/
    unsigned long   volt;   /* 电压(μV)*/
};

static struct imx6ul_opp imx6ul_opps[] = {
    { 198000000,  950000 },   /* 198MHz @ 0.95V */
    { 396000000,  950000 },   /* 396MHz @ 0.95V */
    { 528000000, 1150000 },   /* 528MHz @ 1.15V */
    { 696000000, 1250000 },   /* 696MHz @ 1.25V */
};

struct imx6ul_cpufreq {
    struct clk          *arm_clk;
    struct regulator    *arm_reg;
    struct cpufreq_frequency_table *freq_table;
};

static struct imx6ul_cpufreq *cpufreq_data;

/* 获取当前频率 */
static unsigned int imx6ul_cpufreq_get(unsigned int cpu)
{
    return clk_get_rate(cpufreq_data->arm_clk) / 1000;  /* 返回 kHz */
}

/* 切换到目标频率 */
static int imx6ul_cpufreq_target(struct cpufreq_policy *policy,
                                   unsigned int target_freq,
                                   unsigned int relation)
{
    struct cpufreq_freqs freqs;
    unsigned long new_freq, new_volt;
    int i, ret;

    /* 找到目标频率对应的 OPP */
    for (i = 0; i < ARRAY_SIZE(imx6ul_opps); i++) {
        if (imx6ul_opps[i].freq / 1000 == target_freq) {
            new_freq = imx6ul_opps[i].freq;
            new_volt = imx6ul_opps[i].volt;
            break;
        }
    }

    freqs.old = imx6ul_cpufreq_get(policy->cpu);
    freqs.new = target_freq;

    /* 通知频率变化开始 */
    cpufreq_freq_transition_begin(policy, &freqs);

    if (new_freq > clk_get_rate(cpufreq_data->arm_clk)) {
        /* 升频:先升压,再升频 */
        ret = regulator_set_voltage(cpufreq_data->arm_reg,
                                     new_volt, new_volt + 50000);
        if (ret) goto out;

        ret = clk_set_rate(cpufreq_data->arm_clk, new_freq);
    } else {
        /* 降频:先降频,再降压 */
        ret = clk_set_rate(cpufreq_data->arm_clk, new_freq);
        if (ret) goto out;

        ret = regulator_set_voltage(cpufreq_data->arm_reg,
                                     new_volt, new_volt + 50000);
    }

out:
    /* 通知频率变化结束 */
    cpufreq_freq_transition_end(policy, &freqs, ret);
    return ret;
}

/* 初始化 CPUFreq 策略 */
static int imx6ul_cpufreq_init(struct cpufreq_policy *policy)
{
    policy->min = imx6ul_opps[0].freq / 1000;
    policy->max = imx6ul_opps[ARRAY_SIZE(imx6ul_opps)-1].freq / 1000;
    policy->cur = imx6ul_cpufreq_get(policy->cpu);
    policy->cpuinfo.transition_latency = 50 * NSEC_PER_USEC; /* 50μs */

    /* 设置频率表 */
    cpufreq_table_validate_and_show(policy, cpufreq_data->freq_table);

    return 0;
}

static struct cpufreq_driver imx6ul_cpufreq_driver = {
    .name       = "imx6ul-cpufreq",
    .flags      = CPUFREQ_NEED_INITIAL_FREQ_CHECK,
    .init       = imx6ul_cpufreq_init,
    .verify     = cpufreq_generic_frequency_table_verify,
    .target_index = imx6ul_cpufreq_target,
    .get        = imx6ul_cpufreq_get,
    .attr       = cpufreq_generic_attr,
};

static int __init imx6ul_cpufreq_driver_init(void)
{
    return cpufreq_register_driver(&imx6ul_cpufreq_driver);
}
module_init(imx6ul_cpufreq_driver_init);

22.3 CPUIdle

22.3.1 CPUIdle 的概念

CPUIdle 是 Linux 内核的 CPU 空闲状态管理子系统。当 CPU 没有任务可执行时,CPUIdle 选择合适的低功耗状态(C-State)让 CPU 进入休眠:

复制代码
CPU 空闲状态(C-State):

C0:运行状态(Active)
  CPU 正常执行指令
  功耗:最高

C1:停止时钟(Halt)
  CPU 停止执行指令,但保持上下文
  退出延迟:< 1μs
  功耗:略低于 C0

C2:停止时钟(Stop Grant)
  CPU 进入更深的睡眠
  退出延迟:< 10μs
  功耗:中等

C3:睡眠(Sleep)
  CPU 缓存可能失效,需要刷新
  退出延迟:< 100μs
  功耗:较低

C6:深度睡眠(Deep Sleep)
  CPU 核心断电,状态保存到 LLC
  退出延迟:< 1ms
  功耗:极低

ARM 对应状态:
  WFI(Wait For Interrupt):等待中断,类似 C1
  WFE(Wait For Event):等待事件
  Cluster Power Down:整个 CPU 簇断电,类似 C3/C6

22.3.2 CPUIdle 框架

c 复制代码
#include <linux/cpuidle.h>

/*
 * cpuidle_driver:CPUIdle 驱动
 * 描述 CPU 支持的所有空闲状态
 */
struct cpuidle_driver {
    const char          *name;
    struct module       *owner;

    /* 空闲状态数组 */
    struct cpuidle_state states[CPUIDLE_STATE_MAX];
    int                  state_count;

    /* 安全状态(系统挂起时使用)*/
    int                  safe_state_index;
};

/*
 * cpuidle_state:一个空闲状态的描述
 */
struct cpuidle_state {
    char    name[CPUIDLE_NAME_LEN];     /* 状态名称(如 "C1")*/
    char    desc[CPUIDLE_DESC_LEN];     /* 状态描述 */

    unsigned int    flags;              /* 标志 */
    unsigned int    exit_latency;       /* 退出延迟(μs)*/
    int             power_usage;        /* 功耗(mW)*/
    unsigned int    target_residency;   /* 最小驻留时间(μs)*/

    /* 进入此状态的函数 */
    int (*enter)(struct cpuidle_device *dev,
                 struct cpuidle_driver *drv,
                 int index);
};

/* ARM 平台的 CPUIdle 驱动示例 */
static struct cpuidle_driver arm_idle_driver = {
    .name = "arm_idle",
    .owner = THIS_MODULE,
    .states = {
        {
            /* C1:WFI 状态 */
            .name           = "C1",
            .desc           = "ARM WFI",
            .exit_latency   = 1,        /* 1μs 退出延迟 */
            .target_residency = 1,
            .power_usage    = 100,      /* 100mW */
            .enter          = arm_enter_idle_simple,
        },
        {
            /* C2:Cluster 断电 */
            .name           = "C2",
            .desc           = "Cluster Power Down",
            .flags          = CPUIDLE_FLAG_TIMER_STOP,
            .exit_latency   = 500,      /* 500μs 退出延迟 */
            .target_residency = 1000,   /* 至少驻留 1ms */
            .power_usage    = 10,       /* 10mW */
            .enter          = arm_enter_idle_cluster_off,
        },
    },
    .state_count = 2,
    .safe_state_index = 0,
};

/* 进入 WFI 状态 */
static int arm_enter_idle_simple(struct cpuidle_device *dev,
                                  struct cpuidle_driver *drv,
                                  int index)
{
    /* 执行 WFI 指令,等待中断唤醒 */
    cpu_do_idle();
    return index;
}

/* 注册 CPUIdle 驱动 */
static int __init arm_idle_init(void)
{
    return cpuidle_register_driver(&arm_idle_driver);
}

22.3.3 CPUIdle 的使用

bash 复制代码
# 查看 CPUIdle 信息
cat /sys/devices/system/cpu/cpu0/cpuidle/state0/name
# C1

cat /sys/devices/system/cpu/cpu0/cpuidle/state0/latency
# 1   ← 退出延迟 1μs

cat /sys/devices/system/cpu/cpu0/cpuidle/state0/usage
# 12345   ← 进入此状态的次数

cat /sys/devices/system/cpu/cpu0/cpuidle/state0/time
# 1234567   ← 在此状态停留的总时间(μs)

# 禁用某个空闲状态
echo 1 > /sys/devices/system/cpu/cpu0/cpuidle/state2/disable

# 使用 PowerTOP 分析 CPU 空闲状态
powertop

# 查看 CPUIdle 调速器
cat /sys/module/cpuidle/parameters/governor
# menu   ← 默认调速器

22.4 设备电源管理

22.4.1 设备电源管理的基本概念

Linux 设备模型为每个设备提供了电源管理接口,驱动程序通过实现 dev_pm_ops 中的回调函数来支持电源管理:

c 复制代码
#include <linux/pm.h>

/*
 * dev_pm_ops:设备电源管理操作函数集
 * 驱动通过实现这些函数来支持各种电源管理操作
 */
struct dev_pm_ops {
    /* 系统挂起相关 */
    int (*prepare)(struct device *dev);     /* 挂起准备 */
    void (*complete)(struct device *dev);   /* 恢复完成 */
    int (*suspend)(struct device *dev);     /* 挂起 */
    int (*resume)(struct device *dev);      /* 恢复 */
    int (*freeze)(struct device *dev);      /* 冻结(休眠)*/
    int (*thaw)(struct device *dev);        /* 解冻 */
    int (*poweroff)(struct device *dev);    /* 断电 */
    int (*restore)(struct device *dev);     /* 恢复(休眠后)*/

    /* 运行时电源管理 */
    int (*runtime_suspend)(struct device *dev);  /* 运行时挂起 */
    int (*runtime_resume)(struct device *dev);   /* 运行时恢复 */
    int (*runtime_idle)(struct device *dev);     /* 运行时空闲 */
};

/* 在驱动中使用 dev_pm_ops */
static const struct dev_pm_ops my_driver_pm_ops = {
    .suspend        = my_driver_suspend,
    .resume         = my_driver_resume,
    .runtime_suspend = my_driver_runtime_suspend,
    .runtime_resume  = my_driver_runtime_resume,
};

/* 在 platform_driver 中注册 */
static struct platform_driver my_driver = {
    .driver = {
        .name = "my_device",
        .pm   = &my_driver_pm_ops,
    },
    .probe  = my_probe,
    .remove = my_remove,
};

/* 简化宏:SET_SYSTEM_SLEEP_PM_OPS 和 SET_RUNTIME_PM_OPS */
static const struct dev_pm_ops my_pm_ops = {
    SET_SYSTEM_SLEEP_PM_OPS(my_suspend, my_resume)
    SET_RUNTIME_PM_OPS(my_runtime_suspend, my_runtime_resume, NULL)
};

22.4.2 系统挂起时的设备电源管理

c 复制代码
/*
 * 设备挂起/恢复的实现示例
 */

static int my_driver_suspend(struct device *dev)
{
    struct my_priv *priv = dev_get_drvdata(dev);

    dev_info(dev, "设备挂起\n");

    /* 1. 停止设备操作 */
    my_device_stop(priv);

    /* 2. 保存设备状态(寄存器等)*/
    my_device_save_state(priv);

    /* 3. 关闭设备时钟 */
    clk_disable_unprepare(priv->clk);

    /* 4. 关闭设备电源(可选)*/
    if (priv->power_reg)
        regulator_disable(priv->power_reg);

    return 0;
}

static int my_driver_resume(struct device *dev)
{
    struct my_priv *priv = dev_get_drvdata(dev);
    int ret;

    dev_info(dev, "设备恢复\n");

    /* 1. 恢复设备电源 */
    if (priv->power_reg) {
        ret = regulator_enable(priv->power_reg);
        if (ret) return ret;
    }

    /* 2. 使能设备时钟 */
    ret = clk_prepare_enable(priv->clk);
    if (ret) return ret;

    /* 3. 恢复设备状态 */
    my_device_restore_state(priv);

    /* 4. 重新启动设备 */
    my_device_start(priv);

    return 0;
}

22.5 运行时电源管理(Runtime PM)

22.5.1 Runtime PM 的概念

Runtime PM(运行时电源管理)允许设备在系统运行期间(不是挂起时)根据使用情况自动进入低功耗状态:

复制代码
Runtime PM 的工作原理:

设备使用中:
  pm_runtime_get_sync(dev)  ← 增加引用计数,确保设备上电
  使用设备...
  pm_runtime_put(dev)       ← 减少引用计数

引用计数为 0 时:
  经过 autosuspend_delay 延迟后
  内核调用驱动的 runtime_suspend 回调
  设备进入低功耗状态

设备再次被使用时:
  pm_runtime_get_sync(dev)
  内核调用驱动的 runtime_resume 回调
  设备恢复正常工作

优点:
  ✓ 设备不使用时自动断电,节省功耗
  ✓ 对驱动的其他部分透明(通过 get/put 管理)
  ✓ 支持自动挂起延迟(避免频繁开关)

22.5.2 Runtime PM API

c 复制代码
#include <linux/pm_runtime.h>

/* ── 初始化 Runtime PM ──────────────────────────────────────── */

/* 使能设备的 Runtime PM */
pm_runtime_enable(dev);

/* 禁用设备的 Runtime PM */
pm_runtime_disable(dev);

/* 设置自动挂起延迟(ms)*/
pm_runtime_set_autosuspend_delay(dev, 2000);  /* 2秒后自动挂起 */

/* 使能自动挂起 */
pm_runtime_use_autosuspend(dev);

/* 设置设备初始状态为活跃 */
pm_runtime_set_active(dev);

/* 设置设备初始状态为挂起 */
pm_runtime_set_suspended(dev);

/* ── 使用设备时 ──────────────────────────────────────────────── */

/* 增加引用计数(同步,等待设备恢复)*/
int ret = pm_runtime_get_sync(dev);
if (ret < 0) {
    dev_err(dev, "设备恢复失败\n");
    return ret;
}

/* 增加引用计数(异步,不等待)*/
pm_runtime_get(dev);

/* 减少引用计数(异步,可能触发自动挂起)*/
pm_runtime_put(dev);

/* 减少引用计数(同步,立即挂起)*/
pm_runtime_put_sync(dev);

/* 减少引用计数(使用自动挂起延迟)*/
pm_runtime_put_autosuspend(dev);

/* ── 查询状态 ──────────────────────────────────────────────── */

/* 检查设备是否处于活跃状态 */
bool active = pm_runtime_active(dev);

/* 检查设备是否处于挂起状态 */
bool suspended = pm_runtime_suspended(dev);

/* 获取当前引用计数 */
int count = atomic_read(&dev->power.usage_count);

22.5.3 Runtime PM 驱动实现

c 复制代码
/*
 * Runtime PM 完整实现示例
 * 以 I2C 传感器驱动为例
 */

#include <linux/i2c.h>
#include <linux/pm_runtime.h>

struct sensor_priv {
    struct i2c_client   *client;
    struct regulator    *vdd;       /* 电源调节器 */
    struct clk          *clk;       /* 时钟 */
    bool                 powered;   /* 当前电源状态 */
};

/* ── Runtime PM 回调函数 ──────────────────────────────────────── */

static int sensor_runtime_suspend(struct device *dev)
{
    struct sensor_priv *priv = dev_get_drvdata(dev);

    dev_dbg(dev, "运行时挂起\n");

    /* 关闭传感器 */
    i2c_smbus_write_byte_data(priv->client, SENSOR_REG_CTRL, 0x00);

    /* 关闭时钟 */
    clk_disable_unprepare(priv->clk);

    /* 关闭电源 */
    regulator_disable(priv->vdd);
    priv->powered = false;

    return 0;
}

static int sensor_runtime_resume(struct device *dev)
{
    struct sensor_priv *priv = dev_get_drvdata(dev);
    int ret;

    dev_dbg(dev, "运行时恢复\n");

    /* 开启电源 */
    ret = regulator_enable(priv->vdd);
    if (ret) return ret;

    /* 等待电源稳定 */
    usleep_range(1000, 2000);

    /* 开启时钟 */
    ret = clk_prepare_enable(priv->clk);
    if (ret) {
        regulator_disable(priv->vdd);
        return ret;
    }

    /* 初始化传感器 */
    i2c_smbus_write_byte_data(priv->client, SENSOR_REG_CTRL, 0x01);
    priv->powered = true;

    return 0;
}

/* ── 读取传感器数据(使用 Runtime PM)──────────────────────── */

static int sensor_read_data(struct sensor_priv *priv, int *value)
{
    int ret;

    /* 增加引用计数,确保设备上电 */
    ret = pm_runtime_get_sync(&priv->client->dev);
    if (ret < 0) {
        pm_runtime_put_noidle(&priv->client->dev);
        return ret;
    }

    /* 读取传感器数据 */
    ret = i2c_smbus_read_word_data(priv->client, SENSOR_REG_DATA);
    if (ret < 0) {
        pm_runtime_put_autosuspend(&priv->client->dev);
        return ret;
    }

    *value = ret;

    /* 减少引用计数(使用自动挂起延迟)*/
    pm_runtime_mark_last_busy(&priv->client->dev);
    pm_runtime_put_autosuspend(&priv->client->dev);

    return 0;
}

/* ── probe 函数 ──────────────────────────────────────────────── */

static int sensor_probe(struct i2c_client *client,
                         const struct i2c_device_id *id)
{
    struct sensor_priv *priv;
    int ret;

    priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
    if (!priv)
        return -ENOMEM;

    priv->client = client;

    /* 获取电源和时钟 */
    priv->vdd = devm_regulator_get(&client->dev, "vdd");
    priv->clk = devm_clk_get(&client->dev, NULL);

    i2c_set_clientdata(client, priv);

    /* 初始化 Runtime PM */
    pm_runtime_set_autosuspend_delay(&client->dev, 2000); /* 2秒自动挂起 */
    pm_runtime_use_autosuspend(&client->dev);
    pm_runtime_set_active(&client->dev);
    pm_runtime_enable(&client->dev);

    /* 初始化设备(此时设备处于活跃状态)*/
    ret = sensor_init(priv);
    if (ret) {
        pm_runtime_disable(&client->dev);
        return ret;
    }

    /* 允许设备进入运行时挂起 */
    pm_runtime_put_autosuspend(&client->dev);

    dev_info(&client->dev, "传感器驱动加载成功(支持 Runtime PM)\n");
    return 0;
}

static int sensor_remove(struct i2c_client *client)
{
    /* 禁用 Runtime PM */
    pm_runtime_disable(&client->dev);
    pm_runtime_set_suspended(&client->dev);

    return 0;
}

static const struct dev_pm_ops sensor_pm_ops = {
    SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
                             pm_runtime_force_resume)
    SET_RUNTIME_PM_OPS(sensor_runtime_suspend,
                        sensor_runtime_resume,
                        NULL)
};

static struct i2c_driver sensor_driver = {
    .driver = {
        .name = "my-sensor",
        .pm   = &sensor_pm_ops,
    },
    .probe    = sensor_probe,
    .remove   = sensor_remove,
};
module_i2c_driver(sensor_driver);

22.6 系统级电源管理(Suspend/Hibernate)

22.6.1 系统挂起状态

Linux 支持多种系统级电源管理状态:

复制代码
Linux 系统电源状态:

S0:正常运行(Running)
  系统完全运行
  功耗:最高

S1/freeze:冻结(Freeze,Linux 特有)
  冻结所有进程和设备驱动
  CPU 进入空闲状态
  内存保持供电
  功耗:较低
  恢复时间:极快(< 1秒)

S3/mem:挂起到内存(Suspend to RAM,STR)
  系统状态保存在 RAM 中
  大部分硬件断电,RAM 保持供电
  功耗:极低(约 1~5W)
  恢复时间:快(1~3秒)
  对应 ACPI S3 状态

S4/disk:挂起到磁盘(Suspend to Disk,Hibernate)
  系统状态保存到磁盘(swap 分区)
  所有硬件断电
  功耗:零(完全断电)
  恢复时间:慢(10~30秒)
  对应 ACPI S4 状态

查看支持的挂起状态:
  cat /sys/power/state
  # freeze mem disk

22.6.2 系统挂起的流程

复制代码
系统挂起(Suspend to RAM)流程:

用户执行:echo mem > /sys/power/state
    ↓
1. 冻结用户空间进程(freeze_processes)
    ↓
2. 调用所有设备的 prepare 回调
    ↓
3. 挂起所有设备(suspend 回调,从叶子节点到根节点)
    ↓
4. 挂起 CPU(进入低功耗状态)
    ↓
5. 等待唤醒事件(按键、网络包、RTC 闹钟等)
    ↓
6. 恢复 CPU
    ↓
7. 恢复所有设备(resume 回调,从根节点到叶子节点)
    ↓
8. 调用所有设备的 complete 回调
    ↓
9. 解冻用户空间进程(thaw_processes)
    ↓
系统恢复正常运行

22.6.3 系统挂起的使用

bash 复制代码
# 查看支持的挂起状态
cat /sys/power/state
# freeze mem disk

# 挂起到内存(S3)
echo mem > /sys/power/state

# 冻结(freeze)
echo freeze > /sys/power/state

# 休眠到磁盘(S4,需要 swap 分区)
echo disk > /sys/power/state

# 设置唤醒源
# 使能 RTC 唤醒(30秒后唤醒)
echo +30 > /sys/class/rtc/rtc0/wakealarm
echo mem > /sys/power/state

# 查看唤醒源
cat /sys/power/wakeup_count
cat /proc/acpi/wakeup

# 使用 systemd 管理挂起
systemctl suspend       # 挂起到内存
systemctl hibernate     # 休眠到磁盘
systemctl hybrid-sleep  # 混合睡眠

# 查看挂起/恢复日志
dmesg | grep -E "PM:|suspend|resume"
journalctl -b -1 | grep -E "suspend|resume"

22.6.4 驱动中的挂起/恢复实现

c 复制代码
/*
 * 完整的挂起/恢复实现
 * 包含系统挂起和 Runtime PM 的统一处理
 */

static int my_driver_suspend(struct device *dev)
{
    struct my_priv *priv = dev_get_drvdata(dev);

    dev_info(dev, "系统挂起\n");

    /* 如果设备已经通过 Runtime PM 挂起,直接返回 */
    if (pm_runtime_suspended(dev)) {
        dev_info(dev, "设备已处于运行时挂起状态\n");
        return 0;
    }

    /* 停止设备操作 */
    my_device_stop(priv);

    /* 保存寄存器状态 */
    priv->saved_regs[0] = readl(priv->base + REG0);
    priv->saved_regs[1] = readl(priv->base + REG1);

    /* 关闭时钟和电源 */
    clk_disable_unprepare(priv->clk);
    regulator_disable(priv->vdd);

    return 0;
}

static int my_driver_resume(struct device *dev)
{
    struct my_priv *priv = dev_get_drvdata(dev);
    int ret;

    dev_info(dev, "系统恢复\n");

    /* 如果设备通过 Runtime PM 挂起,由 Runtime PM 处理恢复 */
    if (pm_runtime_suspended(dev))
        return 0;

    /* 恢复电源和时钟 */
    ret = regulator_enable(priv->vdd);
    if (ret) return ret;

    ret = clk_prepare_enable(priv->clk);
    if (ret) {
        regulator_disable(priv->vdd);
        return ret;
    }

    /* 恢复寄存器状态 */
    writel(priv->saved_regs[0], priv->base + REG0);
    writel(priv->saved_regs[1], priv->base + REG1);

    /* 重新启动设备 */
    my_device_start(priv);

    return 0;
}

/* 使用 SIMPLE_DEV_PM_OPS 简化 */
static SIMPLE_DEV_PM_OPS(my_pm_ops, my_driver_suspend, my_driver_resume);

static struct platform_driver my_driver = {
    .driver = {
        .name = "my_device",
        .pm   = &my_pm_ops,
    },
};

22.7 Regulator 框架

22.7.1 Regulator 的概念

Regulator(调节器)框架是 Linux 内核中管理电压和电流调节器的子系统,用于控制 PMIC(Power Management IC,电源管理芯片):

复制代码
Regulator 框架的作用:

硬件层面:
  PMIC(如 TPS65217、AXP209)提供多路可调电压输出
  每路输出连接到不同的芯片或模块(CPU、DDR、LCD 等)

软件层面:
  Regulator 框架统一管理所有电压/电流调节器
  驱动通过 API 请求所需电压,无需关心具体 PMIC 型号
  支持引用计数(多个消费者共享同一调节器)
  支持电压约束(防止设置超出安全范围的电压)

典型应用:
  CPU 核心电压(VCORE):随频率动态调节(DVFS)
  DDR 电压(VDDR):固定电压
  LCD 背光电压(VLED):可调(控制亮度)
  传感器电源(VSENSOR):按需开关

22.7.2 Regulator 框架架构

复制代码
Regulator 框架架构:

┌─────────────────────────────────────────────────────────────┐
│                   消费者驱动(Consumer)                      │
│  CPU 驱动 / LCD 驱动 / 传感器驱动 / Wi-Fi 驱动              │
│  通过 regulator_get() 获取调节器                             │
│  通过 regulator_enable/disable/set_voltage() 控制           │
└──────────────────────────┬──────────────────────────────────┘
                           │
┌──────────────────────────▼──────────────────────────────────┐
│                   Regulator 核心层                            │
│  引用计数管理 / 电压约束检查 / 依赖关系管理                  │
└──────────────────────────┬──────────────────────────────────┘
                           │
┌──────────────────────────▼──────────────────────────────────┐
│                   Regulator 驱动(Provider)                  │
│  PMIC 驱动(tps65217 / axp209 / da9063 等)                 │
│  实现 regulator_ops 中的操作函数                             │
└──────────────────────────┬──────────────────────────────────┘
                           │ I2C/SPI
┌──────────────────────────▼──────────────────────────────────┐
│                   PMIC 硬件                                   │
└─────────────────────────────────────────────────────────────┘

22.7.3 消费者 API

c 复制代码
#include <linux/regulator/consumer.h>

/* ── 获取和释放调节器 ──────────────────────────────────────── */

/*
 * regulator_get:获取调节器
 * dev:消费者设备
 * id:调节器 ID(与设备树中的 supply 名称对应)
 */
struct regulator *reg = regulator_get(dev, "vdd");
if (IS_ERR(reg)) {
    dev_err(dev, "获取 vdd 调节器失败\n");
    return PTR_ERR(reg);
}

/* devm 版本(自动释放)*/
struct regulator *reg = devm_regulator_get(dev, "vdd");

/* 释放调节器 */
regulator_put(reg);

/* ── 使能和禁用 ──────────────────────────────────────────────── */

/* 使能调节器(引用计数 +1)*/
int ret = regulator_enable(reg);

/* 禁用调节器(引用计数 -1,为0时真正关闭)*/
regulator_disable(reg);

/* 检查调节器是否使能 */
int enabled = regulator_is_enabled(reg);

/* ── 电压控制 ──────────────────────────────────────────────── */

/* 设置电压范围(min_uV ~ max_uV,单位:微伏)*/
ret = regulator_set_voltage(reg, 1800000, 1800000);  /* 固定 1.8V */
ret = regulator_set_voltage(reg, 950000, 1250000);   /* 0.95V ~ 1.25V */

/* 获取当前电压 */
int volt = regulator_get_voltage(reg);  /* 返回微伏 */
pr_info("当前电压:%d mV\n", volt / 1000);

/* ── 电流控制 ──────────────────────────────────────────────── */

/* 设置电流限制 */
ret = regulator_set_current_limit(reg, 100000, 500000);  /* 100mA ~ 500mA */

/* 获取当前电流 */
int curr = regulator_get_current_limit(reg);

/* ── 完整使用示例 ──────────────────────────────────────────── */

static int my_sensor_probe(struct i2c_client *client,
                             const struct i2c_device_id *id)
{
    struct regulator *vdd, *vio;
    int ret;

    /* 获取电源调节器 */
    vdd = devm_regulator_get(&client->dev, "vdd");
    if (IS_ERR(vdd)) {
        dev_err(&client->dev, "获取 vdd 失败\n");
        return PTR_ERR(vdd);
    }

    vio = devm_regulator_get(&client->dev, "vio");
    if (IS_ERR(vio)) {
        dev_err(&client->dev, "获取 vio 失败\n");
        return PTR_ERR(vio);
    }

    /* 设置电压 */
    ret = regulator_set_voltage(vdd, 2800000, 2800000);  /* 2.8V */
    if (ret) return ret;

    ret = regulator_set_voltage(vio, 1800000, 1800000);  /* 1.8V */
    if (ret) return ret;

    /* 使能电源 */
    ret = regulator_enable(vdd);
    if (ret) return ret;

    ret = regulator_enable(vio);
    if (ret) {
        regulator_disable(vdd);
        return ret;
    }

    /* 等待电源稳定 */
    usleep_range(1000, 2000);

    /* 初始化传感器 */
    /* ... */

    return 0;
}

22.7.4 设备树中的 Regulator 配置

dts 复制代码
/* PMIC 设备树配置(以 TPS65217 为例)*/
&i2c0 {
    tps65217: pmic@24 {
        compatible = "ti,tps65217";
        reg = <0x24>;
        interrupts = <7>;

        regulators {
            /* DCDC1:CPU 核心电压 */
            dcdc1_reg: regulator@0 {
                regulator-name = "vdd_mpu";
                regulator-min-microvolt = <900000>;
                regulator-max-microvolt = <1500000>;
                regulator-boot-on;
                regulator-always-on;
            };

            /* DCDC2:DDR 电压 */
            dcdc2_reg: regulator@1 {
                regulator-name = "vdd_core";
                regulator-min-microvolt = <925000>;
                regulator-max-microvolt = <1150000>;
                regulator-boot-on;
                regulator-always-on;
            };

            /* LDO1:传感器电源 */
            ldo1_reg: regulator@4 {
                regulator-name = "vdd_sensor";
                regulator-min-microvolt = <1800000>;
                regulator-max-microvolt = <3300000>;
                /* 不设置 always-on,按需开关 */
            };
        };
    };
};

/* 传感器设备树配置(引用调节器)*/
&i2c1 {
    my_sensor: sensor@48 {
        compatible = "myvendor,my-sensor";
        reg = <0x48>;

        /* 声明所需的电源 */
        vdd-supply = <&ldo1_reg>;   /* 连接到 LDO1 */
        vio-supply = <&dcdc2_reg>;  /* 连接到 DCDC2 */
    };
};

22.7.5 Regulator 驱动实现

c 复制代码
/*
 * 简化的 Regulator 驱动框架
 * 以一个简单的 I2C PMIC 为例
 */
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>

/* 调节器操作函数集 */
static int my_pmic_enable(struct regulator_dev *rdev)
{
    /* 通过 I2C 使能对应的 LDO/DCDC */
    return i2c_smbus_write_byte_data(rdev_get_drvdata(rdev),
                                      PMIC_ENABLE_REG,
                                      PMIC_ENABLE_BIT);
}

static int my_pmic_disable(struct regulator_dev *rdev)
{
    return i2c_smbus_write_byte_data(rdev_get_drvdata(rdev),
                                      PMIC_ENABLE_REG,
                                      0);
}

static int my_pmic_is_enabled(struct regulator_dev *rdev)
{
    int val = i2c_smbus_read_byte_data(rdev_get_drvdata(rdev),
                                        PMIC_ENABLE_REG);
    return (val & PMIC_ENABLE_BIT) ? 1 : 0;
}

static int my_pmic_set_voltage(struct regulator_dev *rdev,
                                int min_uV, int max_uV,
                                unsigned *selector)
{
    /* 根据电压值计算寄存器设置值 */
    int sel = (min_uV - 900000) / 25000;  /* 步进 25mV */
    *selector = sel;
    return i2c_smbus_write_byte_data(rdev_get_drvdata(rdev),
                                      PMIC_VOLT_REG, sel);
}

static int my_pmic_get_voltage(struct regulator_dev *rdev)
{
    int sel = i2c_smbus_read_byte_data(rdev_get_drvdata(rdev),
                                        PMIC_VOLT_REG);
    return 900000 + sel * 25000;  /* 转换为微伏 */
}

static const struct regulator_ops my_pmic_ops = {
    .enable         = my_pmic_enable,
    .disable        = my_pmic_disable,
    .is_enabled     = my_pmic_is_enabled,
    .set_voltage    = my_pmic_set_voltage,
    .get_voltage    = my_pmic_get_voltage,
};

/* 调节器描述符 */
static const struct regulator_desc my_pmic_desc = {
    .name           = "my-pmic-ldo1",
    .id             = 0,
    .ops            = &my_pmic_ops,
    .type           = REGULATOR_VOLTAGE,
    .owner          = THIS_MODULE,
    .min_uV         = 900000,   /* 最低 0.9V */
    .uV_step        = 25000,    /* 步进 25mV */
    .n_voltages     = 25,       /* 25 个档位 */
};

static int my_pmic_probe(struct i2c_client *client,
                          const struct i2c_device_id *id)
{
    struct regulator_config config = {};
    struct regulator_dev *rdev;

    config.dev         = &client->dev;
    config.driver_data = client;
    config.of_node     = client->dev.of_node;

    /* 注册调节器 */
    rdev = devm_regulator_register(&client->dev, &my_pmic_desc, &config);
    if (IS_ERR(rdev)) {
        dev_err(&client->dev, "注册调节器失败\n");
        return PTR_ERR(rdev);
    }

    dev_info(&client->dev, "PMIC 调节器注册成功\n");
    return 0;
}

本章小结

章节 核心知识点 关键 API
22.1 电源管理概述 电源管理重要性;Linux电源管理框架全景图;动态/静态功耗来源;DVFS原理 概念理解
22.2 CPUFreq DVFS工作原理;CPUFreq架构(驱动+调速器);6种调速器详解(performance/powersave/ondemand/conservative/schedutil/userspace);CPUFreq驱动实现(target/get/init) cpufreq_register_driver()/sys/devices/system/cpu/cpu0/cpufreq/
22.3 CPUIdle CPU空闲状态(C-State);CPUIdle框架;cpuidle_driver/cpuidle_state结构体;ARM WFI/Cluster断电 cpuidle_register_driver()/sys/devices/system/cpu/cpu0/cpuidle/
22.4 设备电源管理 dev_pm_ops结构体;系统挂起时的设备suspend/resume;SET_SYSTEM_SLEEP_PM_OPS宏 dev_pm_opsSET_SYSTEM_SLEEP_PM_OPS()
22.5 Runtime PM 运行时电源管理原理;pm_runtime_get_sync/put/enable/disable;自动挂起延迟;完整传感器驱动案例 pm_runtime_enable()pm_runtime_get_sync()pm_runtime_put_autosuspend()
22.6 系统级电源管理 系统电源状态(freeze/mem/disk);挂起到内存流程(9步);挂起/恢复驱动实现;SIMPLE_DEV_PM_OPS宏 echo mem > /sys/power/stateSIMPLE_DEV_PM_OPS()
22.7 Regulator框架 Regulator架构(消费者/核心/驱动);消费者API(get/enable/disable/set_voltage);设备树配置(supply属性);Regulator驱动实现 regulator_get()regulator_enable()regulator_set_voltage()

电源管理驱动开发要点

复制代码
1. CPUFreq 驱动
   实现 target/get/init 三个核心函数
   升频时先升压再升频,降频时先降频再降压
   使用 OPP(Operating Performance Point)管理频率-电压对

2. Runtime PM
   probe 中调用 pm_runtime_enable() 使能
   使用设备前调用 pm_runtime_get_sync()
   使用完后调用 pm_runtime_put_autosuspend()
   设置合理的自动挂起延迟(避免频繁开关)

3. 系统挂起/恢复
   suspend 中保存寄存器状态,关闭时钟和电源
   resume 中恢复电源和时钟,恢复寄存器状态
   检查 pm_runtime_suspended() 避免重复操作

4. Regulator 使用
   使用 devm_regulator_get() 获取调节器
   设置电压后再使能
   使用完后禁用(引用计数管理)

5. 调试工具
   PowerTOP:分析系统功耗
   cpupower:管理 CPUFreq
   /sys/power/:系统电源管理接口
   /sys/class/regulator/:调节器状态

参考文献:宋宝华《Linux设备驱动开发详解:基于最新的Linux 4.0内核》,机械工业出版社,2015年