smc.c

来自「上传linux-jx2410的源代码」· C语言 代码 · 共 1,404 行 · 第 1/3 页

C
1,404
字号
/* -*- c-basic-offset: 2; tab-width: 8 -*- * Based on nand_smc.c * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * */#include <linux/config.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/delay.h>#include <linux/errno.h>#include <linux/sched.h>#include <linux/types.h>#include <linux/interrupt.h>#include <asm/io.h>#define CONFIG_MTD_DEBUG_VERBOSE	0#include <linux/mtd/mtd.h>#include <linux/mtd/nand.h>#include <linux/mtd/nand_ids.h>#include <linux/mtd/nand_ecc.h>/* * Macros for low-level register control */#define nand_select()	this->hwcontrol(NAND_CTL_SETNCE); \			nand_command(mtd, NAND_CMD_RESET, -1, -1); \			udelay (10);#define nand_deselect() this->hwcontrol(NAND_CTL_CLRNCE);static inline void sm_swap(u_char *x, u_char *y) {    u_char tmp;    tmp = *x;    *x = *y;    *y = tmp;}/* define if you'll be using < 2M SMC device */#undef USE_256BYTE_NAND_FLASH/* * Send command to NAND device */static void nand_command (struct mtd_info *mtd, unsigned command,				int column, int page_addr){	register struct nand_chip *this = mtd->priv;	/* Begin command latch cycle */	this->hwcontrol(NAND_CTL_SETCLE);	this->hwcontrol(NAND_CTL_DAT_OUT);	/*	 * Write out the command to the device.	 */	if (command != NAND_CMD_SEQIN)			this->write_cmd (command);	else {		if (mtd->oobblock == 256 && column >= 256) {			column -= 256;			this->write_cmd(NAND_CMD_RESET);			this->write_cmd(NAND_CMD_READOOB);			this->write_cmd(NAND_CMD_SEQIN);		}		else if (mtd->oobblock == 512 && column >= 256) {			if (column < 512) {				column -= 256;				this->write_cmd(NAND_CMD_READ1);				this->write_cmd(NAND_CMD_SEQIN);			}			else {				column -= 512;				this->write_cmd(NAND_CMD_READOOB);				this->write_cmd(NAND_CMD_SEQIN);			}		}		else {			this->write_cmd(NAND_CMD_READ0);			this->write_cmd(NAND_CMD_SEQIN);		}	}	/* Set ALE and clear CLE to start address cycle */	this->hwcontrol(NAND_CTL_CLRCLE);	this->hwcontrol(NAND_CTL_SETALE);	/* Serially input address */	if (column != -1)		this->write_addr (column);	if (page_addr != -1) {		this->write_addr ((u_char) (page_addr & 0xff));		this->write_addr ((u_char) ((page_addr >> 8) & 0xff));		/* One more address cycle for higher density devices */		if (mtd->size & 0x0c000000) {			this->write_addr ((u_char) ((page_addr >> 16) & 0x0f));		}	}	/* Latch in address */	this->hwcontrol(NAND_CTL_CLRALE);	this->hwcontrol(NAND_CTL_DAT_IN);	/* Pause for 15us */	udelay (15);}/* * NAND read */static int nand_read (struct mtd_info *mtd, loff_t from, size_t len,			size_t *retlen, u_char *buf){	int j, col, page, state;	int erase_state = 0;	struct nand_chip *this = mtd->priv;	DECLARE_WAITQUEUE(wait, current);	DEBUG (MTD_DEBUG_LEVEL3,		__FUNCTION__ ": from = 0x%08x, len = %i\n",		(unsigned int) from, (int) len);	/* Do not allow reads past end of device */	if ((from + len) > mtd->size) {		DEBUG (MTD_DEBUG_LEVEL0,			__FUNCTION__ ": Attempt read beyond end of device\n");		*retlen = 0;		return -EINVAL;	}	/* Grab the lock and see if the device is available */retry:	spin_lock_bh (&this->chip_lock);	switch (this->state) {	case FL_READY:		this->state = FL_READING;		spin_unlock_bh (&this->chip_lock);		break;	case FL_ERASING:		this->state = FL_READING;		erase_state = 1;		spin_unlock_bh (&this->chip_lock);		break;	default:		set_current_state (TASK_UNINTERRUPTIBLE);		add_wait_queue (&this->wq, &wait);		spin_unlock_bh (&this->chip_lock);		schedule();		remove_wait_queue (&this->wq, &wait);		goto retry;	};	/* First we calculate the starting page */	page = from >> this->page_shift;	/* Get raw starting column */	col = from & (mtd->oobblock - 1);	/* State machine for devices having pages larger than 256 bytes */	state = (col < mtd->eccsize) ? 0 : 1;	/* Calculate column address within ECC block context */	col = (col >= mtd->eccsize) ? (col - mtd->eccsize) : col;	/* Initialize return value */	*retlen = 0;	/* Select the NAND device */	nand_select ();	/* Loop until all data read */	while (*retlen < len) {		/* Send the read command */		if (!state)			nand_command (mtd, NAND_CMD_READ0, col, page);		else 			nand_command (mtd, NAND_CMD_READ1, col, page);		this->wait_for_ready();		/* Read the data directly into the return buffer */ 		if ((*retlen + (mtd->eccsize - col)) >= len) {			while (*retlen < len)				buf[(*retlen)++] = this->read_data();			/* We're done */			continue;		}		else			for (j=col ; j < mtd->eccsize ; j++)				buf[(*retlen)++] = this->read_data();		/*		 * If the amount of data to be read is greater than		 * (256 - col), then all subsequent reads will take		 * place on page or half-page (in the case of 512 byte		 * page devices) aligned boundaries and the column		 * address will be zero. Setting the column address to		 * to zero after the first read allows us to simplify		 * the reading of data and the if/else statements above.		 */		if (col)			col = 0x00;		/* Increment page address */		if ((mtd->oobblock == 256) || state)			page++;		/* Toggle state machine */		if (mtd->oobblock == 512)			state = state ? 0 : 1;	}	/* De-select the NAND device */	nand_deselect ();	/* Wake up anyone waiting on the device */	spin_lock_bh (&this->chip_lock);	if (erase_state)		this->state = FL_ERASING;	else		this->state = FL_READY;	wake_up (&this->wq);	spin_unlock_bh (&this->chip_lock);		/* Return happy */	return 0;}/* * if mtd->oobblock == 512 */inline int smc_read_ecc_512(struct mtd_info *mtd, u_char *ecc_code){    struct nand_chip *this = mtd->priv;    u_char ecc_calc[3];    int j, ret;    /* Read in a block big enough for ECC */    for (j=0; j < (mtd->oobblock + mtd->oobsize); j++)      this->data_buf[j] = this->read_data ();#if 0	/* for debugging, tolkien@mizi.com */    printk("Block + OOB");    for (j=0; j < (mtd->oobblock + mtd->oobsize); j++) {      if (j % 16 == 0)	printk("\n");      printk("%02x ", this->data_buf[j]);    }    printk("\n");#endif    for (j=0; j < mtd->oobsize; j++)      ecc_code[j] = this->data_buf[(mtd->oobblock + j)];#if 0	/* for debugging, tolkien@mizi.com */    printk("OOB");    for (j=0; j < mtd->oobsize; j++) {      if (j % 16 == 0)	printk("\n");      printk("%02x ", this->data_buf[(mtd->oobblock + j)]);    }    printk("\n");#endif    nand_calculate_ecc(&this->data_buf[0], &ecc_calc[0]);    sm_swap(&ecc_calc[0], &ecc_calc[1]);    DEBUG (MTD_DEBUG_LEVEL3,	   __FUNCTION__ ": ECC [%02x%02x%02x : %02x%02x%02x]\n",	   ecc_code[SMC_OOB_ECC1], ecc_code[SMC_OOB_ECC1+1],	   ecc_code[SMC_OOB_ECC1+2], ecc_calc[0], ecc_calc[1], ecc_calc[2]);    ret = nand_correct_data(&this->data_buf[0],			    &(ecc_code[SMC_OOB_ECC1]), &ecc_calc[0]);    if (ret == -1)      return ret;    nand_calculate_ecc(&this->data_buf[mtd->eccsize], &ecc_calc[0]);    sm_swap(&ecc_calc[0], &ecc_calc[1]);    DEBUG (MTD_DEBUG_LEVEL3,	   __FUNCTION__ ": ECC [%02x%02x%02x : %02x%02x%02x]\n",	   ecc_code[SMC_OOB_ECC2], ecc_code[SMC_OOB_ECC2+1],	   ecc_code[SMC_OOB_ECC2+2], ecc_calc[0], ecc_calc[1], ecc_calc[2]);    ret = nand_correct_data(&this->data_buf[mtd->eccsize],			    &(ecc_code[SMC_OOB_ECC2]), &ecc_calc[0]);    if (ret == -1)      return ret;    return 0;}/* * NAND read with ECC */static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,				size_t *retlen, u_char *buf, u_char *ecc_code){    int j, offset, page;    int erase_state = 0;    struct nand_chip *this = mtd->priv;    DECLARE_WAITQUEUE(wait, current);    int ret;    DEBUG (MTD_DEBUG_LEVEL3,	   __FUNCTION__ ": from = 0x%08x, len = %i\n", (unsigned int) from,	   (int) len);    /* Do not allow reads past end of device */    if ((from + len) > mtd->size) {      DEBUG (MTD_DEBUG_LEVEL0,	     __FUNCTION__ ": Attempt read beyond end of device\n");      *retlen = 0;      return -EINVAL;    }    /* Grab the lock and see if the device is available */ retry:    spin_lock_bh (&this->chip_lock);    switch (this->state) {    case FL_READY:      this->state = FL_READING;      spin_unlock_bh (&this->chip_lock);      break;    case FL_ERASING:      this->state = FL_READING;      erase_state = 1;      spin_unlock_bh (&this->chip_lock);      break;    default:      set_current_state (TASK_UNINTERRUPTIBLE);      add_wait_queue (&this->wq, &wait);      spin_unlock_bh (&this->chip_lock);      schedule();      remove_wait_queue (&this->wq, &wait);      goto retry;    };    /* Select the NAND device */    nand_select ();    /* Initialize return value */    *retlen = 0;#ifdef USE_256BYTE_NAND_FLASH    /* First we calculate the starting page */    page = from >> this->page_shift;    /* Get raw starting column */    offset = from & (mtd->oobblock - 1);    /* if the length of Page is 256 bytes,       2 Pages must be taken for 1 Sector and as a result,       higher level 8 bytes of information       among the above 16 byte information must coincide with       Spare(or OOB) of Even-Page while lowel level 8 bytes       coincide with Spare(or OOB) of Odd-page.	   i.e, [0 block][oob][1 block][oob]		[2 block][oob][3 block][oob]...    */    if (mtd->oobblock == 256) {	u_char ecc_calc[3];	int oob_offset;	/* Loop until all data read */	while (*retlen < len) {	  nand_command(mtd, NAND_CMD_READ0, 0x00, page);	  this->wait_for_ready();	  /* Read in a block big enough for ECC */	  for(j=0; j < mtd->eccsize; j++)	    this->data_buf[j] = this->read_data ();	  if (!(page & 0x1)) {	/* page is odd! */	    nand_command (mtd, NAND_CMD_READOOB, SMC_OOB256_ECC1, page + 1);	    oob_offset = SMC_OOB_ECC1;	  } else {	    nand_command (mtd, NAND_CMD_READOOB, SMC_OOB256_ECC2, page);	    oob_offset = SMC_OOB_ECC2;	  }	  this->wait_for_ready();	  for(j=0; j < 3; j++)	    ecc_code[oob_offset + j] = this->read_data ();	  nand_calculate_ecc (&this->data_buf[0], &ecc_calc[0]);	  sm_swap(&ecc_calc[0], &ecc_calc[1]);	  ret = nand_correct_data (&this->data_buf[0],				   &(ecc_code[oob_offset]), &ecc_calc[0]);	  if (ret == -1)	    goto nand_read_ecc_err;	  /* Read the data from ECC data buffer into return buffer */	  if ((*retlen + (mtd->eccsize - offset)) >= len) {	    while (*retlen < len)	      buf[(*retlen)++] = this->data_buf[offset++];	    /* We're done */	    continue;	  } else	    for (j=offset ; j < mtd->eccsize ; j++)	      buf[(*retlen)++] = this->data_buf[j];	  /*	   * If the amount of data to be read is greater than	   * (256 - offset), then all subsequent reads will take	   * place on page or half-page (in the case of 512 byte	   * page devices) aligned boundaries and the column	   * address will be zero. Setting the column address to	   * to zero after the first read allows us to simplify	   * the reading of data and the if/else statements above.	   */	  if (offset)	    offset = 0x00;	  /* Increment page address */	  page++;	}    }    else#endif	/* USE_256BYTE_NAND_FLASH */    {	/* mtd->oobblock == 512 */      size_t last, next, len2;      last = from + len;      for(next=from; from < last;) {	page = from >> this->page_shift;	offset = from & (mtd->oobblock - 1);	len2 = mtd->oobblock - offset;	next += len2;	nand_command(mtd, NAND_CMD_READ0, 0x00, page);	this->wait_for_ready();	ret = smc_read_ecc_512(mtd, ecc_code);	if (ret == -1)	  goto nand_read_ecc_err;	if (next >= last)	  if ((last & (mtd->oobblock - 1)) != 0)	    len2 = (last & (mtd->oobblock - 1)) - offset;	for(j = 0; j < len2; j++)	  buf[(*retlen) + j] = this->data_buf[offset + j];	*retlen += len2;	from = next;      }    }    ret = 0;    goto nand_read_ecc_exit; nand_read_ecc_err:    DEBUG (MTD_DEBUG_LEVEL0,	   __FUNCTION__ ": Failed ECC read, page 0x%08x\n", page);    ret = -EIO; nand_read_ecc_exit:    /* De-select the NAND device */    nand_deselect ();

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?