UEFI下的FlashOperationProtocol的实现
这个驱动其实就是读写flash所需要的最底层的驱动,其操作的就是最底层的flash的芯片,flash芯片有好多中,我们这里采用的是spi协议的。每种芯片的读写函数可能不太一样,但是都是大体相同的。下面是几种flash的介绍,大家知道自己的芯片使用的是哪种就好。
几种flash芯片的介绍
1、IIC EEPROM------容量小,采用的是IIC通信协议;用于在掉电时,存系统配置参数,比如屏幕亮度等。常用芯片型号有 AT24C02、FM24C02、CAT24C02等,其常见的封装多为DIP8,SOP8,TSSOP8等;
2、SPI NorFlash------容量略大,采用的是SPI 通信协议;用于存放程序和数据。程序和数据可存放在同一芯片上,拥有独立的数据总线和地址总线,能快速随机读取,允许系统直接从Flash中读取代码执行;可以单字节或单字编程,但不能单字节擦除,必须以Sector为单位或对整片执行擦除操作。常见到的S25FL128,MX25L1605、W25Q64等型号都是SPI NorFlash。
3、SPI NandFlash------采用了SPI NorFlash一样的SPI的通信协议,用于存储数据;在读写的速度上没什么区别,但在存储结构上却采用了与Parallel NandFlash相同的结构,所以SPI nand相对于SPI norFlash具有擦写的次数多,擦写速度快的优势。
4、eMMC Flash------eMMC采用统一的MMC标准接口,eMMC相当于NandFlash+主控IC;自身集成MMC Controller,存储单元与NandFlash相同。常见到的KLMAG8DEDD、THGBMAG8B4JBAIM、EMMC04G-S100等型号都是eMMC Flash。
5、SD卡------它在MMC的基础上发展而来,有两个可选的通信协议:SD模式和SPI模式。
我们办卡上使用的是第二种,现在这种使用居多,即便宜又好用。
驱动的入口函数
下面的代码就是这个驱动的入口函数
EFI_STATUS
FlashServiceInit(
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
FLASH_DEVICE_OPERATION_PROTOCOL *SpiFlashServiceProtocol;
SpiFlashServiceProtocol = NULL;
Status = gBS->AllocatePool(EfiRuntimeServicesCode,sizeof (FLASH_DEVICE_OPERATION_PROTOCOL),(VOID **) &SpiFlashServiceProtocol);
if (EFI_ERROR (Status)){
return Status;
}
SpiFlashServiceProtocol->GetInfo = SpiGetInfo;
SpiFlashServiceProtocol->Read = SpiRead;
SpiFlashServiceProtocol->Write = SpiWrite;
SpiFlashServiceProtocol->Erase = SpiErase;
Status = gBS->InstallMultipleProtocolInterfaces(
&mFlashHandle,
&gFlashDeviceOperationProtocolGuid,
SpiFlashServiceProtocol,
NULL
);
ASSERT_EFI_ERROR (Status);
if (EFI_ERROR (Status)) {
gBS->FreePool (SpiFlashServiceProtocol);
}
return Status;
}
从上面的代码可知,整个驱动的入口函数就是注册了这个protocol中的成员函数。现在来看看这个protocol中的成员都有什么。
struct _FLASH_DEVICE_OPERATION_PROTOCOL{
FLASH_DEVICE_GET_INFO GetInfo;
FLASH_DEVICE_ERASE Erase;
FLASH_DEVICE_WRITE Write;
FLASH_DEVICE_READ Read;
};
上面这个结构就是就是这个protocol的实体,里面包含了四个回调函数指针,要实现这个protocol的任务就是创建这几个函数的实体。这几个函数是需要根据使用的芯片来实现的,不同的芯片实现的方式可能不同。我们初始化这个函数实体的地方都是在驱动的入口函数做的。详细看上面的代码就清楚了。
实体函数的实现
Write函数
EFI_STATUS
SpiWrite(
IN FLASH_DEVICE_OPERATION_PROTOCOL *This,
IN UINTN Offset,
IN UINT8 *Buffer,
IN OUT UINT32 *BytesCount
)
{
EFI_STATUS Status;
EFI_TPL OldTpl;
Status = EFI_SUCCESS;
#if FLASH_DEBUG
DbgPrint(DEBUG_INFO,"SpiWrite[Offset:0x%llx] [Count:%d]\n",Offset,*BytesCount);
#endif
if(INVALID_ADDR(Offset))ASSERT(0);
OldTpl = gBS->RaiseTPL(TPL_HIGH_LEVEL);
SpiFlashWrite (Offset, Buffer, *BytesCount, FALSE, 256);
gBS->RestoreTPL (OldTpl);
ASSERT_EFI_ERROR (Status);
return Status;
}
Read函数
EFI_STATUS
SpiRead(
IN FLASH_DEVICE_OPERATION_PROTOCOL *This,
IN UINTN Offset,
IN UINT8 *Buffer,
IN OUT UINT32 *BytesCount
)
{
EFI_STATUS Status;
EFI_TPL OldTpl;
#if FLASH_DEBUG
DbgPrint(DEBUG_INFO,"SpiRead[Offset:0x%llx]\n",Offset);
#endif
if(INVALID_ADDR(Offset))ASSERT(0);
OldTpl = gBS->RaiseTPL(TPL_HIGH_LEVEL);
*BytesCount = SpiFlashRead (Offset, Buffer, *BytesCount);
gBS->RestoreTPL (OldTpl);
Status = EFI_SUCCESS;
return Status;
}
Erase函数
EFI_STATUS
SpiErase(
IN FLASH_DEVICE_OPERATION_PROTOCOL *This,
IN UINTN Offset,
IN OUT UINTN *BytesCount
)
{
EFI_STATUS Status;
EFI_TPL OldTpl;
#if FLASH_DEBUG
DbgPrint(DEBUG_INFO,"SpiErase[Offset:0x%llx]\n",Offset);
#endif
if(INVALID_ADDR(Offset))ASSERT(0);
OldTpl = gBS->RaiseTPL(TPL_HIGH_LEVEL);
SpiFlashErase (Offset, *BytesCount);
gBS->RestoreTPL (OldTpl);
Status = EFI_SUCCESS;
return Status;
}
Getinfo函数
这个函数在一些芯片内,是没有用的,可以不实现,我们这里就没有实现,只是一个空函数。
上面函数内调用的其他函数就不一一列举了,具体的实现还是要考手册是写,这里之后提供一个框架。
调试中遇到的bug
(1)flash写入后寄存器的状态寄存器没有复位,导致下次再读取数据的时候,所出来的数据不正确的问题
(2)由于disable写保护含函数实现的有问题,不能重复写的问题
(3)写入的字节数大雨256后需要再次发送一个写使能命令,这样写的位置才能从上次写的位置后面开始继续写,否则从这个256字节开头的位置写。将第一次写入的数据冲刷掉的问题。