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, 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 <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,cmd, 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,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;

}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章