spi flash驱动代码分析(一)
一、???????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,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, #endif #ifdefCONFIG_SPI_FRAM_RAMTRON_NON_JEDEC ???????? { 0, #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 <ARRAY_SIZE(winbond_spi_flash_table); 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,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_len,data_len); ???????? //释放总线 ???????? spi_release_bus(spi); ? ???????? return ret; } 3. int spi_flash_cmd_read(struct spi_slave *spi,size_t data_len) { ???????? return spi_flash_read_write(spi,data_len); } ? 代码段4: Spi flash的写操作 int spi_flash_cmd_write_multi(struct spi_flash *flash,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, ?????????????????????????????????????????????? ? 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,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 sizen"); ?????????????????? 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,cmd); ?????????????????? offset += erase_size; ? ?????????????????? ret =spi_flash_cmd_write_enable(flash); ???????? ?????????????????? ret =spi_flash_cmd_write(flash->spi,0); ???????? ?????????????????? ret =spi_flash_cmd_wait_ready(flash,SPI_FLASH_PAGE_ERASE_TIMEOUT); ???????? } ? ?out: ???????? spi_release_bus(flash->spi); ???????? return ret; } (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |