一、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;
}