linux下cfi_cmdset_0002中關於timeout探究

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的定義,但是我們看代碼,發現只用了其中的三個:WordWriteTimeoutTypBufWriteTimeoutTy,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;
}







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