关于S3学习所涉及到的知识(三):Generic PM之Suspend功能&&Gicv3电源/功耗管理

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介: 关于S3学习所涉及到的知识(三):Generic PM之Suspend功能&&Gicv3电源/功耗管理

一、Suspend功能内容有哪些

1. 前言

Linux内核提供了三种Suspend: Freeze、Standby和STR(Suspend to RAM),在用户空间向”/sys/power/state”文件分别写入”freeze”、”standby”和”mem”,即可触发它们。

内核中,Suspend及Resume过程涉及到PM Core、Device PM、各个设备的驱动、Platform dependent PM、CPU control等多个模块,涉及了console switch、process freeze、CPU hotplug、wakeup处理等过个知识点。就让我们跟着内核代码,一一见识它们吧。

2. Suspend功能有关的代码分布

内核中Suspend功能有关的代码包括PM core、Device PM、Platform PM等几大块,具体如下:

1)PM Core

kernel/power/main.c----提供用户空间接口(/sys/power/state)
kernel/power/suspend.c----Suspend功能的主逻辑
kernel/power/suspend_test.c----Suspend功能的测试逻辑
kernel/power/console.c----Suspend过程中对控制台的处理逻辑
kernel/power/process.c----Suspend过程中对进程的处理逻辑

2)Device PM

drivers/base/power/*----具体可参考“Linux电源管理(4)_Power Management Interface”的描述。
设备驱动----具体设备驱动的位置,不再涉及。

3)Platform dependent PM

include/linux/suspend.h----定义platform dependent PM有关的操作函数集
arch/xxx/mach-xxx/xxx.c或者
arch/xxx/plat-xxx/xxx.c----平台相关的电源管理操作

3. suspend&resume过程概述

下面图片对Linux suspend&resume过程做了一个概述,读者可以顺着这个流程阅读内核源代码。具体的说明,可以参考后面的代码分析。

(只介绍和suspend功能有关的,struct dev_pm_ops简称D,struct platform_suspend_ops简称P)

前辈关于这个系列的文章太过精彩,后续慢慢详细学习一下,这里先暂时熟悉了解一下大概的流程!!!

二、Gicv3电源/功耗管理

在整个s3的过程中,为什么会扯到gicv3?其实这是因为在整个s3的过程中,会涉及到非0核的关闭。而这个操作会涉及到中断的关闭。具体涉及到什么?

来看看这个寄存器!!!

1-前言

从gic3开始,cpu interface放到了PE中,因此cpu interface和PE是同一个power domain。而属于gic的其他组件,如redistributor,distributor,是另外一个power domain。因此就有如下一种情况,PE和cpu interface的电源给断掉了,而gic的电源并没有断掉。此时gic给cpu interface发送数据,cpu interface是不会响应的。

在这种情况下,gic提供了power管理功能。–gic中,提供了如下的 GICR_WAKER 寄存器,来支持power功能。

GIC电源管理,ARM官方手册,有一页描述:

  • 1、在符合GICv3体系结构的实现中,CPU接口和PE必须位于同一个位置power domain,但它不必与关联的Redistributor所在的power domain相同。这意味着可能出现PE及其CPU接口断电的情况,Redistributor、Distributor及其子系统都已通电。在这种情况下,GIC架构需要支持,向PE和CPU接口发送通电事件信号机制。
  • 2、ARM强烈建议,如果该PE上的唤醒软件无法处理中断,GIC的配置方式不应使中断唤醒特定的PE。GICv3提供电源管理来控制这种情况,因为该架构的设计允许由一个组织设计的再分配器,用于由一个组织设计的PEs和CPU接口不同的组织。
  • 3、Redistributor上电时,在关闭CPU接口和PE之前,软件必须将CPU接口和Redistributor之间的接口进入静态状态,否则系统将变为不可预期状态。
  • 4、GIC支持通过设置GICR_WAKER,可以启动到静态状态的转换。进程睡眠到1。当CPU处于静止状态时,GICR_WAKER。ChildrenAsleep也设置为1。

那么Redistributor、GICR_WAKER在系统位置是怎么样的呢?

2-Redistributor系统位置

在带有gicv3的soc架构中,其框图如下所示:

gicv3中的redistributor与core中的cpu interface通过AXI-Stream进行通信。

2-唤醒cpu interface和PE

1、系统上电,CPU如何与GIC redistributor connect

当core上电之后,需要将core中cpu interface与gic中的redistributor进行connect,这样将来gic才可以将中断发送给core。

connection的流程如下所示:

2、GICR_WAKER寄存器

当gic要唤醒cpu interface和PE时,也是操作这个 GICR_WAKER寄存器。

将processorsleep,写入0,然后去唤醒该cpu,最后读取childrenasleep,判断PE是否唤醒成功,

上电流程,行为描述

  • 执行在core的程序,将GICR_WAKER.ProcessorSleep位给置低,表示要connect redistributor。
  • redistributor在完成connect之后,将GICR_WAKER.ChildrenAsleep位给置低,表示connect完成。
  • 执行在core的程序,查询GICR_WAKER.ChildAsleep位是否为0,如果不是,表示redistributor还没有完成connect操作,就继续查询。
  • 如果查询到0,表示connect完成,接着做之后的初始化工作。

其汇编代码如下:

/******************************************************************************
 * This function marks the core as awake in the re-distributor and
 * ensures that the interface is active.
 *****************************************************************************/
void gicv3_rdistif_mark_core_awake(uintptr_t gicr_base)
{
  /*
   * The WAKER_PS_BIT should be changed to 0
   * only when WAKER_CA_BIT is 1.
   */
  assert((gicr_read_waker(gicr_base) & WAKER_CA_BIT) != 0U);
  /* Mark the connected core as awake */
  /* 执行在core的程序,将GICR_WAKER.ProcessorSleep位给置低,
   * 表示要connect redistributor。
   */
  gicr_write_waker(gicr_base, gicr_read_waker(gicr_base) & ~WAKER_PS_BIT);
  /* Wait till the WAKER_CA_BIT changes to 0 */
  /* 执行在core的程序,查询GICR_WAKER.ChildAsleep位是否为0,
   * 如果不是,表示redistributor还没有完成connect操作,就继续查询。
   */
  while ((gicr_read_waker(gicr_base) & WAKER_CA_BIT) != 0U) {
  }
}
/*******************************************************************************
 * This function enables the GIC CPU interface of the calling CPU using only
 * system register accesses.
 ******************************************************************************/
void gicv3_cpuif_enable(unsigned int proc_num)
{
  uintptr_t gicr_base;
  u_register_t scr_el3;
  unsigned int icc_sre_el3;
  assert(gicv3_driver_data != NULL);
  assert(proc_num < gicv3_driver_data->rdistif_num);
  assert(gicv3_driver_data->rdistif_base_addrs != NULL);
  assert(IS_IN_EL3());
  /* Mark the connected core as awake */
  /* 表示要connect redistributor */
  gicr_base = gicv3_driver_data->rdistif_base_addrs[proc_num];
  gicv3_rdistif_mark_core_awake(gicr_base);
  /* Disable the legacy interrupt bypass */
  icc_sre_el3 = ICC_SRE_DIB_BIT | ICC_SRE_DFB_BIT;
  /*
   * Enable system register access for EL3 and allow lower exception
   * levels to configure the same for themselves. If the legacy mode is
   * not supported, the SRE bit is RAO/WI
   */
  icc_sre_el3 |= (ICC_SRE_EN_BIT | ICC_SRE_SRE_BIT);
  write_icc_sre_el3(read_icc_sre_el3() | icc_sre_el3);
  scr_el3 = read_scr_el3();
  /*
   * Switch to NS state to write Non secure ICC_SRE_EL1 and
   * ICC_SRE_EL2 registers.
   */
  write_scr_el3(scr_el3 | SCR_NS_BIT);
  isb();
  write_icc_sre_el2(read_icc_sre_el2() | icc_sre_el3);
  write_icc_sre_el1(ICC_SRE_SRE_BIT);
  isb();
  /* Switch to secure state. */
  write_scr_el3(scr_el3 & (~SCR_NS_BIT));
  isb();
  /* Write the secure ICC_SRE_EL1 register */
  write_icc_sre_el1(ICC_SRE_SRE_BIT);
  isb();
  /* Program the idle priority in the PMR */
  write_icc_pmr_el1(GIC_PRI_MASK);
  /* Enable Group0 interrupts */
  write_icc_igrpen0_el1(IGRPEN1_EL1_ENABLE_G0_BIT);
  /* Enable Group1 Secure interrupts */
  write_icc_igrpen1_el3(read_icc_igrpen1_el3() |
        IGRPEN1_EL3_ENABLE_G1S_BIT);
  isb();
}
/*******************************************************************************
 * This function initialises the GIC Redistributor interface of the calling CPU
 * (identified by the 'proc_num' parameter) based upon the data provided by the
 * platform while initialising the driver.
 ******************************************************************************/
void gicv3_rdistif_init(unsigned int proc_num)
{
  uintptr_t gicr_base;
  unsigned int bitmap;
  uint32_t ctlr;
  assert(gicv3_driver_data != NULL);
  assert(proc_num < gicv3_driver_data->rdistif_num);
  assert(gicv3_driver_data->rdistif_base_addrs != NULL);
  assert(gicv3_driver_data->gicd_base != 0U);
  ctlr = gicd_read_ctlr(gicv3_driver_data->gicd_base);
  assert((ctlr & CTLR_ARE_S_BIT) != 0U);
  assert(IS_IN_EL3());
  /* Power on redistributor */
  gicv3_rdistif_on(proc_num);
  gicr_base = gicv3_driver_data->rdistif_base_addrs[proc_num];
  assert(gicr_base != 0U);
  /* Set the default attribute of all SGIs and (E)PPIs */
  gicv3_ppi_sgi_config_defaults(gicr_base);
  bitmap = gicv3_secure_ppi_sgi_config_props(gicr_base,
      gicv3_driver_data->interrupt_props,
      gicv3_driver_data->interrupt_props_num);
  /* Enable interrupt groups as required, if not already */
  if ((ctlr & bitmap) != bitmap) {
    gicd_set_ctlr(gicv3_driver_data->gicd_base, bitmap, RWP_TRUE);
  }
}
/******************************************************************************
 * ARM common helper to initialize the GIC. Only invoked by BL31
 *****************************************************************************/
void __init plat_arm_gic_init(void)
{
  gicv3_distif_init();
  gicv3_rdistif_init(plat_my_core_pos());
  gicv3_cpuif_enable(plat_my_core_pos());
}

3-断电cpu interface和PE

在cpu interface和PE要断电之前,软件要保证,通知redistributor,cpu interface和PE要进入low-power状态。软件,要往GICR_WAKER寄存器的ProcessorSleep字段,写入1,表示PE要进入到low-power状态。cpu interface之后将自己置为low-power状态之后,就将ChildrenAseep字段,设置为1。

当GICR_WAKER.ChildrenAsleep为1之后,redistributor,不会在将中断,发送给cpu interface,distributor,在中断仲裁时,也不会考虑该PE。

1、系统下电,CPU如何与GIC redistributor disconnect

当core下电之后,需要将core中cpu interface与gic中的redistributor进行disconnect,这样将来gic才不会将中断发送给core。

disconnection的流程如下所示:

下电流程,行为描述

  • 执行在core的程序,先将cpu interface的中断组使能给disable。
  • 执行在core的程序,将GICR_WAKER.ProcessorSleep位给置高,表示要disconnect redistributor。
  • redistributor给cpu interface发送 Quiesce包。
  • cpu interface清掉内部所有pending的中断。
  • 清除完毕后,cpu interface回发Quiesce Acknowledge包给redistibutor。
  • redistributor收到cpu interface回发的响应之后,将GICR_WAKER.ChildrenAsleep位给置高,表示disconnect完成。
  • 执行在core的程序,查询GICR_WAKER.ChildAsleep位是否为1,如果不是,表示redistributor还没有完成connect操作,就继续查询。
  • 如果查询到1,表示disconnect完成。

其汇编代码如下:

/******************************************************************************
 * This function marks the core as asleep in the re-distributor and ensures
 * that the interface is quiescent.
 *****************************************************************************/
void gicv3_rdistif_mark_core_asleep(uintptr_t gicr_base)
{
  /* Mark the connected core as asleep */
  /* 执行在core的程序,将GICR_WAKER.ProcessorSleep位给置高,
   * 表示要disconnect redistributor。
   */
  gicr_write_waker(gicr_base, gicr_read_waker(gicr_base) | WAKER_PS_BIT);
  /* Wait till the WAKER_CA_BIT changes to 1 */
  /* 执行在core的程序,查询GICR_WAKER.ChildAsleep位是否为1,
   * 如果不是,表示redistributor还没有完成connect操作,就继续查询。
   */
  while ((gicr_read_waker(gicr_base) & WAKER_CA_BIT) == 0U) {
  }
}
/*******************************************************************************
 * This function disables the GIC CPU interface of the calling CPU using
 * only system register accesses.
 ******************************************************************************/
void gicv3_cpuif_disable(unsigned int proc_num)
{
  uintptr_t gicr_base;
  assert(gicv3_driver_data != NULL);
  assert(proc_num < gicv3_driver_data->rdistif_num);
  assert(gicv3_driver_data->rdistif_base_addrs != NULL);
  assert(IS_IN_EL3());
  /* Disable legacy interrupt bypass */
  write_icc_sre_el3(read_icc_sre_el3() |
        (ICC_SRE_DIB_BIT | ICC_SRE_DFB_BIT));
  /* 1. 执行在core的程序,先将cpu interface的中断组使能给disable。 */
  /* Disable Group0 interrupts */
  write_icc_igrpen0_el1(read_icc_igrpen0_el1() &
            ~IGRPEN1_EL1_ENABLE_G0_BIT);
  /* Disable Group1 Secure and Non-Secure interrupts */
  write_icc_igrpen1_el3(read_icc_igrpen1_el3() &
            ~(IGRPEN1_EL3_ENABLE_G1NS_BIT |
            IGRPEN1_EL3_ENABLE_G1S_BIT));
  /* Synchronise accesses to group enable registers */
  isb();
  /* Mark the connected core as asleep */
  gicr_base = gicv3_driver_data->rdistif_base_addrs[proc_num];
  assert(gicr_base != 0U);
  gicv3_rdistif_mark_core_asleep(gicr_base);
}

以下是GIC-600 支持redistributor单独下电。

static void gic600_pwr_off(uintptr_t base)
{
  /* Wait until group not transitioning */
  gicr_wait_group_not_in_transit(base);
  /* Power off redistributor */
  gicr_write_pwrr(base, PWRR_OFF);
  /*
   * If this is the last man, turning this redistributor frame off will
   * result in the group itself being powered off and RDGPD = 1.
   * In that case, wait as long as it's in transition, or has aborted
   * the transition altogether for any reason.
   */
  if ((gicr_read_pwrr(base) & PWRR_RDGPD) != 0U) {
    /* Wait until group not transitioning */
    gicr_wait_group_not_in_transit(base);
  }
}
/*
 * The Arm GIC-600 and GIC-Clayton models have their redistributors
 * powered down at reset.
 */
/*
 * Power off GIC-600 redistributor (if configured and detected)
 */
void gicv3_rdistif_off(unsigned int proc_num)
{
#if GICV3_SUPPORT_GIC600
  uintptr_t gicr_base = get_gicr_base(proc_num);
  /* Attempt to power redistributor off */
  if (gicv3_redists_need_power_mgmt(gicr_base)) {
    gic600_pwr_off(gicr_base);
  }
#endif
}


相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
相关文章
|
2天前
负载开关IC:简化电源管理与提升系统稳定性的关键
负载开关IC是以串联方式插入电源与负载电路或IC之间的一个半导体开关。 负载开关IC的使用简化了以下工作: 1)为每个系统模块供电; 2)IC的供电时序; 3)可防止系统故障的电路保护。 以锂离子或其它类型电池供电的移动设备和节能设备为例。这类电子系统仅通过启用基本电路块,并关闭所有其它电路块来节能。负载开关IC是具有内部开关的电源管理 IC。由于导通电阻和静态电流都很低,因此负载开关IC表现出最小的压降和功耗。负载开关IC集成了各种保护功能,与由分立器件组成的负载开关电路相比,负载开关IC所需的元器件更少。因此,负载开关IC有助于提高电子系统的稳定性并缩小其尺寸。
|
7月前
GE Fanuc IC200PWR002模块 Versamax系列的扩展电源单元(PSU)
GE Fanuc IC200PWR002模块 Versamax系列的扩展电源单元(PSU)
|
7月前
|
JavaScript C# Windows
【傻瓜级JS-DLL-WINCC-PLC交互】5.​用西门子TIA Portal 博途配置PLC(SIMATIC S7-1200CPU 1215C AC/DC/RLY)
【傻瓜级JS-DLL-WINCC-PLC交互】5.​用西门子TIA Portal 博途配置PLC(SIMATIC S7-1200CPU 1215C AC/DC/RLY)
172 0
|
传感器 算法 Serverless
tb6612电机驱动软件开发(代码pid实现,调试,控制实现)
tb6612电机驱动软件开发(代码pid实现,调试,控制实现)
225 0
|
安全 数据安全/隐私保护
西门子S7-200 SMART系统块的组态,如何组态CPU、信号板和扩展模块?
本篇我们来介绍如何在编程软件STEP7-Micro/WIN SMART中组态系统块。系统块可提供对S7-200 SMART CPU、信号板和扩展模块的组态,下面我们将分别进行介绍。
西门子S7-200 SMART系统块的组态,如何组态CPU、信号板和扩展模块?
|
安全 数据安全/隐私保护
西门子S7-200 SMART如何组态系统块,CPU、信号板和扩展模块的组态
上篇文章中我们学习了西门子S7-200 SMART查看符号名和绝对地址及符号表相关操作,本篇我们来介绍如何在编程软件STEP7-Micro/WIN SMART中组态系统块。系统块可提供对S7-200 SMART CPU、信号板和扩展模块的组态,下面我们将分别进行介绍。
西门子S7-200 SMART如何组态系统块,CPU、信号板和扩展模块的组态
西门子S7-200 SMART PID回路控制,如何调用向导生成的子程序编程
在编程软件STEP7-Micro/WIN SMART的指令树中,展开调用子例程文件夹,选择初始化PID向导子程序拖放到主程序中,该子程序有多个接口参数,切换到该子程序窗口,程序注释中指出需要在每个扫描周期使用SM0.0从主程序中调用该子程序,变量表给出有关接口参数的含义和取值范围。
西门子S7-200 SMART PID回路控制,如何调用向导生成的子程序编程
|
芯片
74HC595芯片的IO扩展(串转并)实验(包含硬件原理和软件编程解析和代码)
74HC595芯片的IO扩展(串转并)实验(包含硬件原理和软件编程解析和代码)
331 1
74HC595芯片的IO扩展(串转并)实验(包含硬件原理和软件编程解析和代码)
|
虚拟化 芯片
CORTEX-A7芯片中断系统基本原理与控制方法
CORTEX-A7芯片中断系统基本原理与控制方法
417 0
CORTEX-A7芯片中断系统基本原理与控制方法
单片机:定时器0和1中断(内含硬件内容详解+软件编程)(ps:此处硬件执行结构间的理解是重中之重)
单片机:定时器0和1中断(内含硬件内容详解+软件编程)(ps:此处硬件执行结构间的理解是重中之重)
539 0
单片机:定时器0和1中断(内含硬件内容详解+软件编程)(ps:此处硬件执行结构间的理解是重中之重)