UEFI开发历程2—基于SuperIO芯片的寄存器读写

一、SuperIO介绍

目前市面上的主流SuperIO芯片有NCT、ITE等厂商的生产的芯片,本文主要针对IT8613E这款芯片的逻辑设备寄存器的读写进行介绍。

IT8613E主要用来连接外部低速设备,如键鼠、串口、Floppy等外设,可以监控PC硬件中的几个关键参数,包括电源电压、风扇速度和温度等。

Super IO芯片的寄存器空间主要有以逻辑设备划分的空间以及扩展出来的Bank空间,如下图1-1所示。每个逻辑设备和bank分别都有256个寄存器。

逻辑设备的256个寄存器里的地址为0x00到0x29的寄存器为全局寄存器,也就是说每个逻辑设备里的这些寄存器都是共用的,通过对寄存器的读写操作可实现多种功能。

而扩展出来的Bank空间入口点一般需要通过在相应的逻辑设备的相应的寄存器上读取出它的基地址,通过这个基地址才能访问到扩展空间。

图1-1

二、寄存器的读/写操作

1、进入扩展功能模式

要配置寄存器从而实现某些功能的第一步是进入扩展功能模式,而每个芯片都有打开自己寄存器读写的key,只有配对了key,才能对寄存器进行读写操作,IT8613E的key是0x87,0x01,0x55,0x55,因此需要往地址端口2E(另外的芯片可能是4E)里连续输入这四个值以进入扩展功能模式。另外还有个数据端口2F(另外的芯片可能是4F),用于写入数据或读取数据。

以下是进入扩展功能模式以及对寄存器进行读写操作的例子:

//进入扩展功能模式:
IOWrite8(0x2E,0x87);
IOWrite8(0x2E,0x01);
IOWrite8(0x2E,0x55);
IOWrite8(0x2E,0x55);
//写:当需要把0x60写入寄存器0x28时(此处仅例子)
IOWrite8(0x2E,0x60);
IOWrite8(0x2F,0x28);
//读:当需要读取寄存器0x60的值读出来时(此处仅例子)
IOWrite8(0x2E,0x60);
Value = IORead8(0x2F);

2、选择逻辑设备

选择逻辑设备的寄存器是全局寄存器0x07,即将逻辑设备号写入寄存器0x07里即可切换到相应的逻辑设备。

//选择逻辑设备7:
IOWrite8(0x2E,0x07);
IOWrite8(0x2F,0x07);

3、读/写寄存器

读/写操作在2.1节里已经有例子说明。

注释:只有全局寄存器的读/写操作不需要选择逻辑设备

4、退出扩展模式

//IT8613E退出扩展模式的key:往全局寄存器0x02里写入值0x02
IOWrite8(0x2E,0x02);
IOWrite8(0x2F,0x02);

三、SIO.efi的程序制作

1、功能

(1)读取逻辑设备的256个寄存器的值并以十六进制显示(逻辑设备号可选);

(2)将十六进制值写入逻辑设备的寄存器中;

(3)控制GPIO口在输出模式下的高低电平,读取输入模式下的GPIO管脚电平;

2、SIO.efi使用说明

功能1:SIO.efi 7DN(读取逻辑设备7的256个寄存器的值,相同范例:6DN);

功能2:SIO.efi 7DN 23 60(此条语句代表将0x60写入逻辑设备7的0x23寄存器里);

功能3:SIO.efi GPXX O/I Y/N H/L/N(参数1的GPXX是选择的GPIO的管脚,XX是GPIO编号;

参数2的O/I是输出/输入模式;参数3的Y/N是极性反转/不反转;参数4的H/L/N是高电平/低电平/(输入模式这里写N);

3、程序代码

(1)inf文件(由于程序使用ShellMainApp应用程序工程模块入口点,因此源代码是需要在ShellPkg包里编译的,所以需要在ShellPkg.dsc的Component块里添加该应用程序)

[Defines]
  INF_VERSION                    = 0x00010005
  BASE_NAME                      = SIO
  FILE_GUID                      = E7B3948D-13E2-4320-B079-F5E5EADB310F
  MODULE_TYPE                    = UEFI_APPLICATION
  VERSION_STRING                 = 1.0
  ENTRY_POINT                    = ShellCEntryLib

[Sources]
  SIO.c
  SIO.h
[Packages]
  MdePkg/MdePkg.dec
  ShellPkg/ShellPkg.dec

[LibraryClasses]
  ShellCEntryLib
  UefiBootServicesTableLib
  UefiLib
  IoLib
  ShellLib

(2)头文件

#ifndef _SIO_H_
#define _SIO_H_

#define CR_INDEX_PORT 0x2E
#define CR_DATA_PORT 0x2F

UINT8 DeviceNO_GPIONO_List[] = {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F};

CHAR16 ListHeader[] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};

UINT8 Multiple_function[] = {0x25,0x26,0x27,0x28,0x29};

UINT8 Polarity_selection[] = {0xB0,0xB1,0xB2,0xB3,0xB4};

UINT8 Internal_Pull_Up[] = {0xB8,0x00,0xBA,0xBB,0xBC,0xBD};

UINT8 GPIOEnable[] = {0xC0,0xC1,0xC2,0xC3,0xC4};

UINT8 OutputMode[] = {0xC8,0xC9,0xCA,0xCB,0xCC,0xCD};

//Enter extended funtion mode -- This is a math group of enter key
UINT8 EnterExtenedModeData[] = {0x87,0x01,0x55,0x55};
//Enter extended funtion mode -- This is a address
UINT8 ExitExtenedModeAddress = 0x02;
//Enter extended funtion mode -- This is a data
UINT8 ExitExtenedModeData = 0x02;

UINT8
Mypow(
  IN UINT8 Value,
  IN UINT8 Exp
  );

/*
  Control GPIO Output
  *Param GPIONumbr -- GPIO Number
  *Param I_O_Mode -- Input Mode/OutPut Mode
  *Param H_NA_L_Level -- If this Param is H , it play a high level 
  						 If this Param is NA , it play a input mode , 
  						 		 			   this param would be returnning the level of this GPIO Port
  						 If this Param is L , it play a low level 
*/
//SIO.efi GP87 O H
VOID
GPIOOutPut(
  IN UINT8 GPIONumbr,
  IN UINT8 I_O_Mode,
  IN UINT8 InvertOrNot,
  IN UINT8 *H_NA_L_Level
  );

/*
  This function is used to get the value of 256 registers of the relevant logic device and print it.
  *Param DeviceNO --Device Logical Number
*/
//SIO.efi 7dn
VOID
GetValueWithIO (
  IN UINT8 DeviceNO
  );
/*
  This function is used to write a custom value to one of the registers of the relevant logic device
  *Param DeviceNO -- Device Logical Number
  *Param Offset -- Register Number
  *Param Value -- Written value
*/
//SIO.efi 7dn 2d 01
VOID
WriteValueWithIO(
  IN UINT8 DeviceNO,
  IN UINT8 Offset,
  IN UINT8 Value
  );
/*
  This function is used to print the list's header of the whole list
*/
VOID
PrintListHeader(
  VOID
  );

#endif

(3)c文件

#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/IoLib.h>
#include <SIO.h>
#include <Library/ShellLib.h>
#include <Library/BaseLib/BaseLibInternals.h>

UINT8
Mypow(
  IN UINT8 Value,
  IN UINT8 Exp
  ){
  UINT8 result = 1;
  for(UINT8 time = 0 ; time < Exp ; time++){
      result *= Value;
  }
  Value = result;
  return Value;
}
void
GPIOOutPut(
  IN UINT8 GPIONumbr,
  IN UINT8 I_O_Mode,
  IN UINT8 InvertOrNot,
  IN UINT8 *H_NA_L_Level
  ){
  UINT16 BaseAdress;
  UINT16 H_BaseAdress;
  UINT16 L_BaseAdress;
  UINT8 ReadLevel;
  //Enter extended funtion mode
  for(UINT8 Quanlity = 0 ; Quanlity < sizeof(EnterExtenedModeData)/sizeof(UINT8); Quanlity++){
      IoWrite8(CR_INDEX_PORT,EnterExtenedModeData[Quanlity]);
  }
  //Select logical device 7
  IoWrite8(CR_INDEX_PORT,0x07);
  IoWrite8(CR_DATA_PORT,0x07);
  //Multiple function selection
  IoWrite8(CR_INDEX_PORT,Multiple_function[GPIONumbr/10 - 1]);
  IoWrite8(CR_DATA_PORT,IoRead8(CR_DATA_PORT) | Mypow(2,GPIONumbr%10));
  //execute this command while output mode
  if(I_O_Mode == 0){
    //Polarity selection -non inverting
    IoWrite8(CR_INDEX_PORT,Polarity_selection[GPIONumbr/10 - 1]);
    if(InvertOrNot == 1){
      IoWrite8(CR_DATA_PORT,IoRead8(CR_DATA_PORT) | Mypow(2,GPIONumbr%10));
    }else
      IoWrite8(CR_DATA_PORT,IoRead8(CR_DATA_PORT) | 0x00);
  }
  
  //Because GP2X and GP53 have not supported internal pull-up
  if(GPIONumbr/10 != 2 || (GPIONumbr/10 != 5 && GPIONumbr%10 != 3)){
    //internal pull up configuration
    IoWrite8(CR_INDEX_PORT,Internal_Pull_Up[GPIONumbr/10 - 1]);
    IoWrite8(CR_DATA_PORT,IoRead8(CR_DATA_PORT) | Mypow(2,GPIONumbr%10));
  }
  if(GPIONumbr/10 != 6){
    //GPIO port enable -default 0100 0000->0100 0010
    IoWrite8(CR_INDEX_PORT,GPIOEnable[GPIONumbr/10 -1]);
    IoWrite8(CR_DATA_PORT,IoRead8(CR_DATA_PORT) | Mypow(2,GPIONumbr%10));
  }
  //Get high bit base address
  IoWrite8(CR_INDEX_PORT,0x62);
  H_BaseAdress = IoRead8(CR_DATA_PORT);
  //Get low bit base address
  IoWrite8(CR_INDEX_PORT,0x63);
  L_BaseAdress = IoRead8(CR_DATA_PORT);
  //Combination
  BaseAdress = (H_BaseAdress << 8) | L_BaseAdress;

  if(I_O_Mode == 0){/
    //Output mode CB 4
    IoWrite8(CR_INDEX_PORT,OutputMode[GPIONumbr/10 -1]);
    IoWrite8(CR_DATA_PORT,IoRead8(CR_DATA_PORT) | Mypow(2,GPIONumbr%10));
    //OutPut level
    if(*H_NA_L_Level == 1){
      IoWrite8(BaseAdress + GPIONumbr/10 - 1 , IoRead8(BaseAdress + GPIONumbr/10 - 1) | Mypow(2,GPIONumbr%10));
     }else if(*H_NA_L_Level == 0){
      IoWrite8(BaseAdress + GPIONumbr/10 - 1 , IoRead8(BaseAdress + GPIONumbr/10 - 1) && (0xFF-Mypow(2,GPIONumbr%10)));
     }
   

  }else if(I_O_Mode == 1){
    IoWrite8(CR_INDEX_PORT,OutputMode[GPIONumbr/10 -1]);
    IoWrite8(CR_DATA_PORT,IoRead8(OutputMode[GPIONumbr/10 - 1]) && (0xFF-Mypow(2,GPIONumbr%10)));
    ReadLevel = IoRead8(BaseAdress + GPIONumbr/10 - 1);
  }         
  //Exit the extended mode
  IoWrite8(CR_INDEX_PORT,ExitExtenedModeAddress);
  IoWrite8(CR_DATA_PORT,ExitExtenedModeData);
  if(I_O_Mode == 0 && *H_NA_L_Level == 1){
    ShellPrintEx(10 , 3 , L"Set %HGP%d %NOutPut to %Hhigh%N level successfully!",GPIONumbr);
  }else if(I_O_Mode == 0 && *H_NA_L_Level == 0){
    ShellPrintEx(10 , 3 , L"Set %HGP%d %NOutPut to %Hlow%N level successfully!",GPIONumbr);
  }else if(I_O_Mode == 1 && (((ReadLevel >> (GPIONumbr%10))&& 1) == 1)){
    ShellPrintEx(10 , 3 , L"The level read by %HGP%d %Nin input mode is %Hhigh %Nlevel!",GPIONumbr);
  }else if(I_O_Mode == 1 && (((ReadLevel >> (GPIONumbr%10))&& 1) == 0)){
    ShellPrintEx(10 , 3 , L"The level read by %HGP%d %Nin input mode is %Hlow %Nlevel!",GPIONumbr);
  }
}

void
GetValueWithIO(
  IN UINT8 DeviceNO
){
  //Rows that displays location coordinates
  UINT8 RowCR = 5;
  //Columns that display location coordinates
  UINT8 CowCR = 20;
  //Registeres quanlity calculate
  UINT8 Count = 0;
  //The value of the register
  UINT8 Value = 0;
  //The address of the register read
  UINT8 ReadCRValue = 0x00;

  //Enter extended funtion mode
  for(UINT8 Quanlity = 0 ; Quanlity < sizeof(EnterExtenedModeData)/sizeof(UINT8); Quanlity++){
      IoWrite8(CR_INDEX_PORT,EnterExtenedModeData[Quanlity]);
  }
  //Select logical device
  IoWrite8(CR_INDEX_PORT,0x07);
  IoWrite8(CR_DATA_PORT,DeviceNO);

  for(UINT8 index = 0 ; index < 256 ;index++ ){

    IoWrite8(CR_INDEX_PORT,ReadCRValue);
    Value = IoRead8(CR_DATA_PORT);
    if(Value == 0){
      ShellPrintEx(CowCR, RowCR,L"0");
      ShellPrintEx(CowCR+1, RowCR,L"%X",Value);
    }else if(Value!=0 && Value <= 15){
        ShellPrintEx(CowCR, RowCR,L"%H0");
        ShellPrintEx(CowCR+1, RowCR,L"%H%X",Value);
    }else
        ShellPrintEx(CowCR, RowCR,L"%H%X",Value);

    CowCR += 3;
    Count++;

    if(Count % 16 == 0){
      RowCR += 1;
      CowCR = 20;
    }

    ReadCRValue += 1;
    if(index == 255)
      break;

  }
  
  //Exit the extended mode
  IoWrite8(CR_INDEX_PORT,ExitExtenedModeAddress);
  IoWrite8(CR_DATA_PORT,ExitExtenedModeData);
}

void
WriteValueWithIO(
  IN UINT8 DeviceNO,
  IN UINT8 Offset,
  IN UINT8 Value
  ){

  UINT8 Read;

  //Enter extended funtion mode
  for(UINT8 Quanlity = 0 ; Quanlity < sizeof(EnterExtenedModeData)/sizeof(UINT8); Quanlity++){
      IoWrite8(CR_INDEX_PORT,EnterExtenedModeData[Quanlity]);
  }
  //Select logical device
  IoWrite8(CR_INDEX_PORT,0x07);
  IoWrite8(CR_DATA_PORT,DeviceNO);
  //Write Value into register
  IoWrite8(CR_INDEX_PORT,Offset);
  IoWrite8(CR_DATA_PORT,Value);

  IoWrite8(CR_INDEX_PORT,Offset);
  Read = IoRead8(CR_DATA_PORT);
  //Exit the extended mode
  IoWrite8(CR_INDEX_PORT,ExitExtenedModeAddress);
  IoWrite8(CR_DATA_PORT,ExitExtenedModeData);

  if(Read == Value)
    ShellPrintEx(3 , 3 , L"The value of %H%X %Nregister of logical device %H%X %Nhas been written successfully!\n" , Offset , DeviceNO);
}

void
PrintListHeader(
  void
  ){

  UINT8 Index = 0;
  for( UINT8 Cow = 20 ; Cow < 68 ; Cow += 3){
      ShellPrintEx( Cow , 4 , L"%V0");
      ShellPrintEx( Cow+1 , 4 , L"%V%c", ListHeader[Index] );
      Index += 1;
    }
  Index = 0;
  for( UINT8 Row = 5 ; Row < 21 ; Row += 1){
      ShellPrintEx( 17 , Row , L"%V%c", ListHeader[Index] );
      ShellPrintEx( 18 , Row , L"%V0");
      Index += 1;
  }  
  Print(L"\n");

}

INTN 
ShellAppMain(
  IN UINTN Argc,
  IN CHAR16 **Argv )
{ 
  //Device logical number
  UINT8 DeviceNO;
  //Address offset of register
  UINT8 Offset;
  //The Value of writting
  UINT8 Value;
  //GPIO Number
  UINT8 GPIONumbr;
  //Input/OutPut Mode --Value is 'I=1' or 'O=0'
  UINT8 I_O_Mode;
  //Inverting or not invert - 1 non invert - 0
  UINT8 InvertOrNot;
  //OutPut Level or return GPIO level of input mode
  UINT8 H_NA_L_Level;
  
  //Check 'd' of cmd param '7dn',because whatever print 256 register value or write value into register,
  //it must be input example param likes '7dn' 
  
  if(Argc == 2){
    if(*(Argv[1] + 1) == 'D' || *(Argv[1] + 1) == 'd' ){
      DeviceNO = DeviceNO_GPIONO_List[InternalHexCharToUintn(*Argv[1])];
    }
  }else if(Argc == 4){
    if(*(Argv[1] + 1) == 'D' || *(Argv[1] + 1) == 'd' ){
    //Get the device logical number from command line argument 1
    DeviceNO = DeviceNO_GPIONO_List[InternalHexCharToUintn(*Argv[1])];
    //Get register address
    Offset = DeviceNO_GPIONO_List[InternalHexCharToUintn(*Argv[2])]*16 + DeviceNO_GPIONO_List[InternalHexCharToUintn(*((Argv[2])+1))];
    //The value to be written in
    Value = DeviceNO_GPIONO_List[InternalHexCharToUintn(*Argv[3])]*16 + DeviceNO_GPIONO_List[InternalHexCharToUintn(*((Argv[3])+1))];
    }
  }else if(Argc == 5){
    if(*Argv[1]  == 'G' || *Argv[1] == 'g' ){
      //While select GPIO function,the first param that must be input  GPXX,at this,it used a check function  
      //Get GPIO Number
      GPIONumbr = DeviceNO_GPIONO_List[InternalHexCharToUintn(*(Argv[1]+2))]*10 + DeviceNO_GPIONO_List[InternalHexCharToUintn(*(Argv[1]+3))];
      

      //Judge input mode or output mode
      if(*Argv[2] == 'I' || *Argv[2] == 'i'){
        I_O_Mode = 1;
      }else if(*Argv[2] == 'O' || *Argv[2] == 'o')
        I_O_Mode = 0;


      //Judge invert or not
      if(*Argv[3] == 'Y' || *Argv[3] == 'y'){
        InvertOrNot = 1;
      }else if(*Argv[3] == 'N' || *Argv[3] == 'n')
        InvertOrNot = 0;


      //Judge high level or low level while output mode or N while input mode
      if(*Argv[4] == 'H' || *Argv[4] == 'h'){
        H_NA_L_Level = 1;
      }else if(*Argv[4] == 'L' || *Argv[4] == 'l'){
        H_NA_L_Level = 0;
      }else if(*Argv[4] == 'N' || *Argv[4] == 'n')
        H_NA_L_Level = 2;
    }
  }

 

  switch(Argc){
    //Read the value of 256 registers and display
    //Print header of list 
    case 2 : 
            gST->ConOut->ClearScreen(gST->ConOut);
            ShellPrintEx(10 , 3 , L"The 256 registers of logical device %H%X %Nhave the following values:" , DeviceNO);
            PrintListHeader();
            
            GetValueWithIO(DeviceNO);
            break;
    case 4 :
            //Write the value into the register
            gST->ConOut->ClearScreen(gST->ConOut);
            
            WriteValueWithIO(DeviceNO , Offset , Value);
            
            PrintListHeader();
            GetValueWithIO(DeviceNO);
            break;
    case 5:
            //Control GPIO Output or input
            gST->ConOut->ClearScreen(gST->ConOut);
            
            GPIOOutPut(GPIONumbr , I_O_Mode , InvertOrNot , &H_NA_L_Level);
            
            PrintListHeader();
            GetValueWithIO(0x07);
            break;
    //Default situation
    default:
            gST->ConOut->ClearScreen(gST->ConOut);
            Print(L"The count of input parameters are not correct!");
  }
  return 0;
}

  • 6
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
高通UEFI是高通公司为了支持其处理器和芯片组在Android设备上的引导和初始化而开发的软件模块。UEFI(统一扩展固件界面)是一种新型的固件标准,取代了过去的BIOS(基本输入输出系统)。高通UEFI开发Android开发文档主要提供了有关在使用高通处理器和芯片组的Android设备上进行开发的指导和说明。 该文档首先介绍了如何正确设置开发环境,包括安装必要的软件和驱动程序。接着,文档介绍了高通UEFI的架构和工作原理,包括引导流程、启动流程和初始化过程。开发人员可以深入了解UEFI在Android设备上的作用和功能。 文档还提供了如何进行UEFI开发的具体指导,包括如何编写UEFI应用程序、如何调试和测试UEFI应用程序以及如何进行项目集成等。针对不同的开发需求,文档也提供了各种开发和定制UEFI的选项和工具,使开发人员能够根据实际情况进行灵活的开发和调试。 此外,文档还包含了一些实际案例和示例代码,帮助开发人员更好地理解如何使用高通UEFI开发Android设备,并且提供了一些常见问题和解决方案以供参考。 总而言之,高通UEFI开发Android开发文档为开发人员提供了一个全面的指南,帮助他们理解和应用高通UEFI在Android设备上的开发和定制。通过这些文档,开发人员可以更加有效地利用高通UEFI提供的功能和特性,为Android设备开发出更加稳定和高性能的引导和初始化软件。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值