加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

spi flash驱动代码分析(一)

发布时间:2020-12-15 20:08:41 所属栏目:百科 来源:网络整理
导读:一、???????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(C

一、???????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)
{
/* 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;
}
}

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;

}

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读