一、 spi_flash uboot驱动的一个应用实例
1. spi应用程序在操作之前调用spi_flash_probe去初始化spi_flash
2. 初始化完毕即可读写spi flash
void spl_spi_load_image(void)
{
//初始化SPI FLASH
flash =spi_flash_probe(CONFIG_SPL_SPI_BUS, CONFIG_SPL_SPI_CS,
CONFIG_SF_DEFAULT_SPEED,SPI_MODE_3);
//操作读写flash
/*Load u-boot, mkimage header is 64 bytes. */
spi_flash_read(flash, CONFIG_SYS_SPI_U_BOOT_OFFS,0x40,
(void*) header);
spl_parse_image_header(header);
……
}
二、 spi flash驱动分析
spi初始化入口函数:
struct spi_flash *spi_flash_probe(unsigned int bus, unsigned int cs,
unsigned int max_hz, unsignedint spi_mode)
{
1. 配置spi控制器总线、cs片选、速率、spi模式
2. 读取spi flash的设备ID号
3. 根据ID号查表去匹配不同芯片的配置属性。找到对应芯片的初始化钩子函数。(注:见代码段1)
4. 调用SPI flash初始化函数,去匹配芯片名称、配置页、扇区、块的大小,配置flash的读、写、擦除钩子函数。(注:见代码段2)
5. 如果flash容量大于16m,则设置flash 为4字节模式
6. 初始化spi flash完毕,释放spi总线。
}
Spi读写函数:
(注:这个函数的被调 见代码段3)
static int spi_flash_read_write(struct spi_slave *spi,
const u8*cmd, size_t cmd_len,
const u8*data_out, u8 *data_in,
size_tdata_len)
{
if (data_len == 0)
flags |= SPI_XFER_END;
//发送spi flash 命令
ret = spi_xfer(spi, cmd_len * 8, cmd,NULL, flags);
//发送spi flash 数据
if (data_len != 0) {
ret = spi_xfer(spi, data_len* 8, data_out, data_in, SPI_XFER_END);
}
return ret;
}
spi_xfer的实现依赖于具体的处理器。主要的功能是顺序的把数据写入到spi总线。
代码段1:
这个结构体在spi_flash_probe初始化函数的第三步使用。根据芯片厂商类型挂钩子函数。
static const struct {
const u8 shift;
const u8 idcode;
struct spi_flash *(*probe) (structspi_slave *spi, u8 *idcode);
}flashes[] = {
/* Keep it sorted by define name */
#ifdefCONFIG_SPI_FLASH_ATMEL
{ 0, 0x1f, spi_flash_probe_atmel, },
#endif
#ifdefCONFIG_SPI_FLASH_EON
{ 0, 0x1c, spi_flash_probe_eon, },
#endif
#ifdefined(CONFIG_SPI_FLASH_MACRONIX) || defined(CONFIG_SPI_FLASH_MACRONIX_NS)
{ 0, 0xc2, spi_flash_probe_macronix, },
#endif
#ifdefCONFIG_SPI_FLASH_SPANSION
{ 0, 0x01, spi_flash_probe_spansion, },
#endif
#ifdefCONFIG_SPI_FLASH_SST
{ 0, 0xbf, spi_flash_probe_sst, },
#endif
#ifdefined(CONFIG_SPI_FLASH_STMICRO) || defined(CONFIG_SPI_FLASH_STMICRO_NS)
{ 0, 0x20, spi_flash_probe_stmicro, },
#endif
#ifdefCONFIG_SPI_FLASH_WINBOND
{ 0, 0xef, spi_flash_probe_winbond, },
#endif
#ifdefCONFIG_SPI_FRAM_RAMTRON
{ 6, 0xc2, spi_fram_probe_ramtron, },
# undefIDCODE_CONT_LEN
# defineIDCODE_CONT_LEN 6
#endif
/* Keep it sorted by best detection */
#ifdefined(CONFIG_SPI_FLASH_STMICRO) || defined(CONFIG_SPI_FLASH_STMICRO_NS)
{ 0, 0xff, spi_flash_probe_stmicro, },
#endif
#ifdefCONFIG_SPI_FRAM_RAMTRON_NON_JEDEC
{ 0, 0xff, spi_fram_probe_ramtron, },
#endif
};
代码段2:
以华邦为例,华邦的SPI FLASH初始化函数
struct spi_flash *spi_flash_probe_winbond(struct spi_slave *spi, u8 *idcode)
{
const struct winbond_spi_flash_params*params;
struct spi_flash *flash;
unsigned int i;
//找芯片型号
for (i = 0; i params =&winbond_spi_flash_table[i]; if (params->id ==((idcode[1] << 8) | idcode[2])) break; } flash->spi = spi; flash->name = params->name; //挂载钩子函数,下面3个函数是spi flash的读写擦除API flash->write =spi_flash_cmd_write_multi; flash->erase= spi_flash_cmd_erase; flash->read= spi_flash_cmd_read_fast; flash->page_size= 256; flash->sector_size= 4096; flash->size = 4096* 16 * params->nr_blocks; return flash; } 代码段3: 读FLASH的流程: 1. int spi_flash_cmd_read_fast(struct spi_flash *flash, u32 offset, size_t len, void *data) { u8 cmd[6]={0}; int idx = flash->mode_4byte?sizeof(cmd):sizeof(cmd)-1; cmd[0] = CMD_READ_ARRAY_FAST; //构造命令的格式流 spi_flash_addr(flash,offset, cmd); //写入数据到spi总线 return spi_flash_read_common(flash,cmd, idx, data, len); } spi flash的读写和擦除理论上必须是把物理地址转换成页和偏移去读、写和擦除。 由于很多芯片的页大小就是0xff=255个字节。所以直接传地址和把地址转换成页和偏移的结果是等价的。 static void spi_flash_addr(struct spi_flash *flash,u32 addr, u8 *cmd) 2. int spi_flash_read_common(struct spi_flash *flash, const u8 *cmd, size_t cmd_len, void *data,size_t data_len) { struct spi_slave *spi = flash->spi; int ret; //使能总线 spi_claim_bus(spi); //读数据 ret = spi_flash_cmd_read(spi, cmd,cmd_len, data, data_len); //释放总线 spi_release_bus(spi); return ret; } 3. int spi_flash_cmd_read(struct spi_slave *spi, const u8 *cmd, size_t cmd_len, void *data,size_t data_len) { return spi_flash_read_write(spi, cmd,cmd_len, NULL, data, data_len); } 代码段4: Spi flash的写操作 int spi_flash_cmd_write_multi(struct spi_flash *flash, u32 offset, size_t len, const void *buf) { int idx = flash->mode_4byte?sizeof(cmd):sizeof(cmd)-1; page_size = flash->page_size; page_addr = offset / page_size; byte_addr = offset % page_size; ret = spi_claim_bus(flash->spi); cmd[0] = CMD_PAGE_PROGRAM; for (actual = 0; actual < len;actual += chunk_len) { if(flash->mode_4byte){ cmd[1] = page_addr>> 16; cmd[2] = page_addr >> 8; cmd[3] = page_addr; cmd[4] = byte_addr; }else{ cmd[1] = page_addr>> 8; cmd[2] = page_addr; cmd[3] = byte_addr; } //写使能 ret =spi_flash_cmd_write_enable(flash); //写 ret =spi_flash_cmd_write(flash->spi, cmd, idx, buf + actual, chunk_len); //等待完成 ret =spi_flash_cmd_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT); page_addr++; byte_addr = 0; } spi_release_bus(flash->spi); return ret; } 代码段5: Spi flash的擦除 intspi_flash_cmd_erase(struct spi_flash *flash, u32 offset, size_t len) { int idx = flash->mode_4byte?sizeof(cmd):sizeof(cmd)-1; erase_size = flash->sector_size; //擦除必须是整块的擦除 if (offset % erase_size || len %erase_size) { debug("SF: Eraseoffset/length not multiple of erase size\n"); return -1; } ret = spi_claim_bus(flash->spi); if (erase_size == 4096) cmd[0] = CMD_ERASE_4K; else cmd[0] = CMD_ERASE_64K; start = offset; end = start + len; while (offset < end) { spi_flash_addr(flash,offset,cmd); offset += erase_size; ret =spi_flash_cmd_write_enable(flash); ret =spi_flash_cmd_write(flash->spi, cmd,idx, NULL, 0); ret =spi_flash_cmd_wait_ready(flash, SPI_FLASH_PAGE_ERASE_TIMEOUT); } out: spi_release_bus(flash->spi); return ret; }
{
/* cmd[0] is actual command */
if(flash->mode_4byte){
cmd[1] = addr >> 24;
cmd[2] = addr >> 16;
cmd[3] = addr >> 8;
cmd[4] = addr >> 0;
}else{
cmd[1] = addr >> 16;
cmd[2] = addr >> 8;
cmd[3] = addr >> 0;
}
}