IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 系统运维 -> 7.嵌入式控制器EC学习 ACPI规范中的电源管理通道PMC(Power Management Channel) -> 正文阅读

[系统运维]7.嵌入式控制器EC学习 ACPI规范中的电源管理通道PMC(Power Management Channel)

前言

在EC中的电源管理通道是定义在ACPI规范中的,是用于主机处理器和嵌入式控制器EC中的通信通道。

一、电源管理通道PMC概述

电源管理通道提供了PMDIR, PMDOR, PMCMDR和PMSTR四个寄存器它用于Host与EC两端的通信。Host通过写入数据到PMDIR中,EC通过读取Host写入PMDIR中的数据来完成Host->Ec端的通信。EC通过写入数据到PMDOR中,Host通过读取EC写入PMDOR中的数据完成功能EC->Host端的通信。PMCMDR/PMSTR两个寄存器则能同时被Host和EC读取。电源管理通道的接口框图如下图所示。
在这里插入图片描述
从上面图片我们也可以得知PMC通道的偏移地址是0x62和0x66。这个框图一步一步来解读,首先是EC Bus的作用,他的作用是用于同步Host和EC端的数据,数据又是如何同步的呢,他是通过在EC端和Host端设置两组数据同步的寄存器来实现数据同步的。比如在Host端有PMDIR, PMDOR和PMSTR三个寄存器,在EC端同样有PMDI,PMDO和PMSTS。如下3张图片是对EC端PMDI,PMDO和PMSTS寄存器的描述。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
我们有这样一个场景,操作系统下发了Shutdown指令,这时操作系统开始完成相关的内容保存,将在运行的任务进行停止,将需要保存的数据保存到磁盘中,在所有的保存工作完成后,需要向EC发送一条关机命令,那么这条命令的数据就是通过电源管理通道来完成的。

二、通过电源管理通道PMC完成EC和BIOS或操作系统通信

这里可以先通过我之前的博文《9.嵌入式控制器EC学习 操作系统或BIOS通过KBC(0x60/0x64)和PMC(0x62/0x66)对EC ram进行读写》了解通过PMC对EC Ram的访问。
通过ACPI EC 接口规范,在《ACPI规范》的第十二章内容中有详细介绍通信细节。接下来结合代码详细讨论:

操作系统或BIOS通过PMC通道与EC通信

假设此时操作系统或BIOS通过LPC的PMC通道下发了数据给EC,那么EC又该如何正确的接收下发的数据呢?
查看EC端PMSTS和Host端PMSTR寄存器,这两个寄存器是实时同步的,只要其中内容发生变化,会同时同步到两边的寄存器中。寄存器中的具体内容如下图所示:
在这里插入图片描述

位数描述作用
bit1IBF这个位置位时表示操作系统或BIOS已经向PMDIR寄存器中写入了数据/命令,当Host端的PMDIR寄存器中有内容时,寄存器中的数据将会同步到EC端的PMDI寄存器中,EC端则可以读取PMDI寄存器来获取操作系统或BIOS向EC发送的数据,当数据读取完成后,芯片会自动帮我们将IBF位清零。
bit0OBF这个位置位时表示EC已经向PMDO寄存器中写入了数据,当EC端的PMDO寄存器中有内容时,寄存器中的数据将会同步到Host端的PMDOR寄存器中,操作系统或BIOS则可以读取PMOR寄存器来获取EC向操作系统或BIOS发送的数据,当数据读取完成后,芯片会自动帮我们将OBF位清零。
在CORE_MAIN.C主函数中有如下代码:
if( IsFlag1(PM1STS, BIT1) )         {   F_Service_PCI2 = 1;     }

当IBF置位时,代表着操作系统或BIOS已经通过PMC通道(0x62/0x66)向EC发送了数据,代码进入CORE_ACPI.C中的Service_PCI2服务函数:

void Service_PCI2(void)
{
    if ( IsFlag0(PM1STS,IBF1) )  return;
    vSetTotalBurstTime();   // Set Burst mode total time (2ms)
    Service_PCI2_Main();    // Process Command/Data

}

首先通过PMSTS的BIT2判断操作系统或BIOS发送给EC的信息时DATA还是CMD:

// PM1STS = Embedded Controller Status, EC_SC (R)
if ( PM1STS & C_D1 )  // CMD:1=Byte in data register is a command byte

如果PMSTS的BIT2为1时,代表发送过来的信息是命令:

if ( PM1STS & C_D1 )  // CMD:1=Byte in data register is a command byte
{
    PM1Cmd  = PM1DI; // Load command from Port Buffer
    PM1Step = 0;

    #if SUPPORTED_RECORDER
    if(En_Record66 )
    {
        RamDebug(0x66); RamDebug(PM1Cmd);
    }
    #endif

    (Port66_Table[PM1Cmd>>4])(); // Handle command

}

在里面通过PMDI寄存器取出操作系统或BIOS发给EC的命令,在将命令右移4位,0x80–0x84这五个命令值右移4位后都为8,查询Port66_Table表:

const FUNCT_PTR_V_V code Port66_Table[16] =
{
    EC_Cmd_0X,          // Process ACPI command 0x
    EC_Cmd_1X,          // Process ACPI command 1x
    EC_Cmd_2X,          // Process ACPI command 2x
    EC_Cmd_3X,          // Process ACPI command 3x
    EC_Cmd_4X,          // Process ACPI command 4x
    EC_Cmd_5X,          // Process ACPI command 5x
    EC_Cmd_6X,          // Process ACPI command 6x
    EC_Cmd_7X,          // Process ACPI command 7x
    EC_Cmd_8X,          // Process ACPI command 8x
    EC_Cmd_9X,          // Process ACPI command 9x
    EC_Cmd_AX,          // Process ACPI command Ax
    EC_Cmd_BX,          // Process ACPI command Bx
    EC_Cmd_CX,          // Process ACPI command Cx
    EC_Cmd_DX,          // Process ACPI command Dx
    EC_Cmd_EX,          // Process ACPI command Ex
    EC_Cmd_FX,          // Process ACPI command Fx
};

代码将执行EC_Cmd_8X函数:

//-----------------------------------------------------------------------------
void EC_Cmd_8X(void)
{
    (EC6266Cmd8X_Table[PM1Cmd&0x0F])();
}

查询命令表EC6266Cmd8X_Table:

//-----------------------------------------------------------------------------
const FUNCT_PTR_V_V code EC6266Cmd8X_Table[16] =
{
    EC6266_CMD_80,          // Process ACPI command 80
    EC6266_CMD_81,          // Process ACPI command 81
    EC6266_CMD_82,          // Process ACPI command 82
    EC6266_CMD_83,          // Process ACPI command 83
    EC6266_CMD_84,          // Process ACPI command 84
    EC6266_CMD_85,          // Process ACPI command 85
    EC6266_CMD_86,          // Process ACPI command 86
    EC6266_CMD_87,          // Process ACPI command 87
    EC6266_CMD_88,          // Process ACPI command 88
    EC6266_CMD_89,          // Process ACPI command 89
    EC6266_CMD_8A,          // Process ACPI command 8A
    EC6266_CMD_8B,          // Process ACPI command 8B
    EC6266_CMD_8C,          // Process ACPI command 8C
    EC6266_CMD_8D,          // Process ACPI command 8D
    EC6266_CMD_8E,          // Process ACPI command 8E
    EC6266_CMD_8F           // Process ACPI command 8F
};

代码将根据PMCmd中的值进入不同的函数中去执行动作,命令范围在0x80–0x84,通过《ACPI规范》的第十二章内容了解各个命令的作用,这里不再重复讲叙,也可以参照之前的博文《9.嵌入式控制器EC学习 操作系统或BIOS通过KBC(0x60/0x64)和PMC(0x62/0x66)对EC ram进行读写》了解。
函数的详细内容如下:

void EC6266_CMD_80(void)
{
    SCI_RESPONSE();     // Interrupt on IBF=0
    PM1Step = _PM1_STEP_1;

}
void EC6266_CMD_81(void)
{
    SCI_RESPONSE();     // Interrupt on IBF=0
    PM1Step = _PM1_STEP_2;

}
void EC6266_CMD_82(void)
{
    SET_BIT(PM1STS,4);  // PM1STS.4 Set Burst mode flag
    PM1DO = 0x90;       // Byte #2 (Burst acknowledge byte)
    OS_ACPI_Mode = 1;   // Auto Set ACPI Mode if Host Do ECCmd82
    SCI_RESPONSE();     // Interrupt on IBF=0
    #if En_Record62
    RamDebug(0x90);
    #endif

}
void EC6266_CMD_83(void)
{
    CLEAR_BIT(PM1STS,4);    /* PM1STS.4 Set Burst mode flag.        */
    /*  Clear Burst bit in secondary Host interface status register.*/
    SCI_RESPONSE();         /* Interrupt on IBF=0.                  */

}
void EC6266_CMD_84(void)
{
    //OS_ACPI_Mode = 1;   // Auto Set ACPI Mode if Host Do ECCmd84
    /* Byte #2 (Query value to host).*/
    //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    #if (SUPPORT_ACPI_SMI)
    if( Is_Flag1(PM1STS,5) )
    {   SCI_LastQueryEvent = ReadSCI_QueryValue();  }
    else if( Is_Flag1(PM1STS,6) )
    {   SCI_LastQueryEvent = ReadSMI_QueryValue();  }
    else
    {
        SCI_LastQueryEvent = 0x00;
    }
    //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    #else
    SCI_LastQueryEvent = ReadSCI_QueryValue();
    #endif
    //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    PM1DO = SCI_LastQueryEvent;
    SCI_RESPONSE(); /* Interrupt on IBF=0.*/
    //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    #if SCI_EVENT_LOG
    if( SCI_LastQueryEvent > 0 )
    {
        SCI_SMI_Event_Record[(SCI_SMI_Index_Record&0x0F)] = SCI_LastQueryEvent;
        SCI_SMI_Index_Record++;
    }
    #endif
    //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    SCI_QueryEvent = 0x00;
    SCI_StepTimer = 0;
    WAIT_SCI_CENTER = 0;
}

如果PMSTS的BIT2为0时,代表发送过来的信息是数据:

else    // CMD:0=Byte in data register is a data byte
{
    //PM1DI,PM1DO=ACPI Sepc.13.2.3 Embedded Controller Data,EC_DATA(R/W)
    PM1Data = PM1DI;         // Load data
    if (PM1Step!=0x00)
    {
        #if SUPPORTED_RECORDER
        if(En_Record62)
        {
            RamDebug(0x62); RamDebug(PM1Data);
        }
        #endif

        (Port62_Table[PM1Step&0x07])(); // Handle command data

    }
    else
    {
        while( ECUnProcessCnt < 0xFF )
        {
            ECUnProcessCnt++;
        }
    }
}

在里面通过PMDI寄存器取出操作系统或BIOS发给EC的数据,这里需要参照博文《9.嵌入式控制器EC学习 操作系统或BIOS通过KBC(0x60/0x64)和PMC(0x62/0x66)对EC ram进行读写》,操作系统或BIOS通过PMC对EC进行读写的具体步骤如下:
读操作
1.往EC的Command Port写入0x80命令
2.往EC的Data Port写入需要读取的数据的Offset
3.读取EC的Data Port,读到的数据便是结果
写操作
1.往EC的Command Port写入0x81命令
2.往EC的Data Port写入所写数据的Offset
3.往EC的Data Port写入所写数据的值

总结,读写操作都分为三步,第一步都是往PMC的Command Port即0x66写入命令,第二步都是往PMC的Data Port即0x62写入地址偏移,第三步有读写的区别。

所以在代码处理数据时也要判断在哪一步,比如在处理读取EC命令操作(往EC的Command Port写入0x80命令)时,会将PM1Step = _PM1_STEP_1,所以后面EC在PMC通道0x62数据端口接收到的数据就是操作系统或BIOS需要读取数据的偏移地址,代码如下:

void EC62_DATA_STEP1(void)
{   // Byte #3 (Data read to host)
    // PM1DO = ACPI Sepc.12.3.3 Embedded Controller Data, EC_DATA (R/W)
    PM1Data1 = Read_MapECSpace(PM1Data);
    PM1DO = PM1Data1;
    SCI_RESPONSE();     // Interrupt on OBF=1
    #if En_Record62
    RamDebug(PM1Data1);
    #endif
    PM1Step = 0;
}

此时将根据PMData的数据(偏移)取出ECSpace(即EC ram)指定位置的内容,并将内容写入PMDO寄存器,该寄存器用于EC通过PMC通道发送数据给操作系统或BIOS,当该寄存器写入数据后,OBF会置位,EC再通过SCI中断通知操作系统或BIOS,我们已经把指定偏移的数据写入了输出寄存器了,操作系统或BIOS可以取走需要的数据了。

在处理操作系统或BIOS写操作(往EC的Command Port写入0x81命令)时,此时PM1Step = _PM1_STEP_2,所以后面EC在PMC通道0x62数据端口接收到的数据就是操作系统或BIOS需要写入数据的偏移地址,代码如下:

void EC62_DATA_STEP2(void)
{
    PM1Data1=PM1Data;   //  Byte #2 Save Address Index
    SCI_RESPONSE();     // Interrupt on OBF=1
    PM1Step = _PM1_STEP_3;
}
void EC62_DATA_STEP3(void)
{   // Byte #3 (Data read to host)
    SCI_RESPONSE(); // Interrupt on OBF=1
    Write_MapECSpace( PM1Data1,PM1Data );
    PM1Step = 0;
}

在EC端的处理代码中,写入操作比读取操作多了一个步骤,因为在读取步骤中,EC在接收到偏移地址后可以立马返回EC ram空间指定偏移位置的内容给操作系统或BIOS,而在写入操作中,EC接收完偏移地址后,EC还需要继续接收内容,所以在写操作中分为了两步进行,EC62_DATA_STEP2用来接收偏移地址,EC62_DATA_STEP3用来往EC ram指定偏移地址写入(Write_MapECSpace)相应的值。

总结

本文在《9.嵌入式控制器EC学习 操作系统或BIOS通过KBC(0x60/0x64)和PMC(0x62/0x66)对EC ram进行读写》的基础上进一步分析代码的运行过程。

  系统运维 最新文章
配置小型公司网络WLAN基本业务(AC通过三层
如何在交付运维过程中建立风险底线意识,提
快速传输大文件,怎么通过网络传大文件给对
从游戏服务端角度分析移动同步(状态同步)
MySQL使用MyCat实现分库分表
如何用DWDM射频光纤技术实现200公里外的站点
国内顺畅下载k8s.gcr.io的镜像
自动化测试appium
ctfshow ssrf
Linux操作系统学习之实用指令(Centos7/8均
上一篇文章      下一篇文章      查看所有文章
加:2022-05-09 13:09:20  更:2022-05-09 13:11:23 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/15 18:31:27-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码