CFI命令集現在大體有兩個,在linux內核的MTD下我就可以看到,其中一個是:cfi_cmdset_0001.c,另一個是cfi_cmdset_0002.c,還是一個是cfi_cmdset_0020.c這個是ST後來新加的,我們一般不會去care它的,一般如果你用的是Micrion的Norflash,我們用的是cfi_cmdset_0002.c這信指命令集。這裏有一個關於cfi_cmdset_0002.c中關於do_buffer_write中timeout的討論 http://lists.infradead.org/pipermail/linux-mtd/2013-April/046446.html,這個是在do_buffer_write這個函數中發現有timeout不夠的問題,在最近的內核版本中3.15,關於tiimeout的作法是這樣的.
我們知道一些關於norflash的信息,我們可以通過讀取CFI信息獲取,關於CFI的信息,在linux內核中,主要由兩個結構體來完成。先來看一下CFI總的信息表吧:我們就是Micron的MT28EW來例:
下面這張表就是部的CFI表分佈,其中前三個表是存在結構體cfi_ident,而最後一個是存在結構體cfi_pri_amdstd。
/* Basic Query Structure */
struct cfi_ident {
uint8_t qry[3]; // 0x10
uint16_t P_ID; //0x13
uint16_t P_ADR; //0x15
uint16_t A_ID; //0x17
uint16_t A_ADR;
uint8_t VccMin; // unused 0x1b
uint8_t VccMax; // unused
uint8_t VppMin; // unused
uint8_t VppMax; // unused
uint8_t WordWriteTimeoutTyp; //0x1f used
uint8_t BufWriteTimeoutTyp; // used
uint8_t BlockEraseTimeoutTyp; // used
uint8_t ChipEraseTimeoutTyp; // unused
uint8_t WordWriteTimeoutMax; // unused
uint8_t BufWriteTimeoutMax; // unused
uint8_t BlockEraseTimeoutMax; // unused
uint8_t ChipEraseTimeoutMax; // // unused 0x26
uint8_t DevSize; // used 0x27
uint16_t InterfaceDesc; //used 0x28
uint16_t MaxBufWriteSize; // used 0x2a
uint8_t NumEraseRegions; //used 0x2c
uint32_t EraseRegionInfo[0]; /* Not host ordered */ // used 0x2d
note:now only read region 1 information
} __attribute__((packed));
/* Vendor-Specific PRI for AMD/Fujitsu Extended
Command Set (0x0002) */
struct cfi_pri_amdstd {
uint8_t pri[3]; //addr 0x40
uint8_t MajorVersion;
uint8_t MinorVersion;
uint8_t SiliconRevision; /* bits 1-0: Address Sensitive Unlock */
uint8_t EraseSuspend;
uint8_t BlkProt; //unused
uint8_t TmpBlkUnprotect; //unused
uint8_t BlkProtUnprot;//used for PPB
uint8_t SimultaneousOps;//unused
uint8_t BurstMode; //unused
uint8_t PageMode; //unused
uint8_t VppMin; //unused
uint8_t VppMax; //unused
uint8_t TopBottom; // used 0x4f
} __attribute__((packed));
從上面的代碼中可以看到,在cfi_ident中就中關於各種操作的timeout的定義,但是我們看代碼,發現只用了其中的三個:WordWriteTimeoutTyp,BufWriteTimeoutTy,BlockEraseTimeoutTyp,就以do_buffer_write爲例,
static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip,
unsigned long adr, const u_char *buf,
int len)
{
struct cfi_private *cfi = map->fldrv_priv;
unsigned long timeo = jiffies + HZ;
/* see comments in do_write_oneword() regarding uWriteTimeo. */
unsigned long uWriteTimeout = ( HZ / 1000 ) + 1;//如果你的系統定義HZ爲1000的話,這裏的值就是2MS的時間
int ret = -EIO;
unsigned long cmd_adr;
int z, words;
map_word datum;
adr += chip->start;
cmd_adr = adr;
mutex_lock(&chip->mutex);
ret = get_chip(map, chip, adr, FL_WRITING);
if (ret) {
mutex_unlock(&chip->mutex);
return ret;
}
datum = map_word_load(map, buf);
pr_debug("MTD %s(): WRITE 0x%.8lx(0x%.8lx)\n",
__func__, adr, datum.x[0] );
XIP_INVAL_CACHED_RANGE(map, adr, len);
ENABLE_VPP(map);
xip_disable(map, chip, cmd_adr);
cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL);
/* Write Buffer Load */
map_write(map, CMD(0x25), cmd_adr);
chip->state = FL_WRITING_TO_BUFFER;
/* Write length of data to come */
words = len / map_bankwidth(map);
map_write(map, CMD(words - 1), cmd_adr);
/* Write data */
z = 0;
while(z < words * map_bankwidth(map)) {
datum = map_word_load(map, buf);
map_write(map, datum, adr + z);
z += map_bankwidth(map);
buf += map_bankwidth(map);
}
z -= map_bankwidth(map);
adr += z;
/* Write Buffer Program Confirm: GO GO GO */
map_write(map, CMD(0x29), cmd_adr);
chip->state = FL_WRITING;
INVALIDATE_CACHE_UDELAY(map, chip,
adr, map_bankwidth(map),
chip->word_write_time);//在發送完confirm後,這裏要delay一段時間,而時間的長短是通過chip->word_write_time來指定的,它的值就是我們從CFI中讀出來的WordWriteTimeoutTyp,而它是在cfi_cmdset_0002中完成初始化。
timeo = jiffies + uWriteTimeout;
for (;;) {
if (chip->state != FL_WRITING) {//在這裏有一個判斷有沒有supend的操作,如果有會有一個段sleep
/* Someone's suspended the write. Sleep */
DECLARE_WAITQUEUE(wait, current);
set_current_state(TASK_UNINTERRUPTIBLE);
add_wait_queue(&chip->wq, &wait);
mutex_unlock(&chip->mutex);
schedule();
remove_wait_queue(&chip->wq, &wait);
timeo = jiffies + (HZ / 2); /* FIXME *///在這裏標記爲FIXME表示,後續會有改動,這裏重新設置timeout的值。
mutex_lock(&chip->mutex);
continue;
}
if (time_after(jiffies, timeo) && !chip_ready(map, adr))
break;
if (chip_ready(map, adr)) {
xip_enable(map, chip, adr);
goto op_done;
}
/* Latency issues. Drop the lock, wait a while and retry */
UDELAY(map, chip, adr, 1);
}
/*
* Recovery from write-buffer programming failures requires
* the write-to-buffer-reset sequence. Since the last part
* of the sequence also works as a normal reset, we can run
* the same commands regardless of why we are here.
* See e.g.
* http://www.spansion.com/Support/Application%20Notes/MirrorBit_Write_Buffer_Prog_Page_Buffer_Read_AN.pdf
*/
cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi,
cfi->device_type, NULL);
cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi,
cfi->device_type, NULL);
cfi_send_gen_cmd(0xF0, cfi->addr_unlock1, chip->start, map, cfi,
cfi->device_type, NULL);
xip_enable(map, chip, adr);
/* FIXME - should have reset delay before continuing */
printk(KERN_WARNING "MTD %s(): software timeout, address:0x%.8lx.\n",
__func__, adr);
ret = -EIO;
op_done:
chip->state = FL_READY;
DISABLE_VPP(map);
put_chip(map, chip, adr);
mutex_unlock(&chip->mutex);
return ret;
}