Skip to content
Snippets Groups Projects
nand_base.c 70.6 KiB
Newer Older
  • Learn to ignore specific revisions
  • 
    		if (chip->ecc.postpad) {
    			chip->read_buf(mtd, oob, chip->ecc.postpad);
    			oob += chip->ecc.postpad;
    		}
    	}
    
    	/* Calculate remaining oob bytes */
    	i = mtd->oobsize - (oob - chip->oob_poi);
    	if (i)
    		chip->read_buf(mtd, oob, i);
    
    	return 0;
    
    /**
     * nand_transfer_oob - [Internal] Transfer oob to client buffer
     * @chip:	nand chip structure
     * @oob:	oob destination address
     * @ops:	oob ops structure
     * @len:	size of oob to transfer
     */
    static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob,
    				  struct mtd_oob_ops *ops, size_t len)
    {
    	switch(ops->mode) {
    
    	case MTD_OOB_PLACE:
    	case MTD_OOB_RAW:
    		memcpy(oob, chip->oob_poi + ops->ooboffs, len);
    		return oob + len;
    
    	case MTD_OOB_AUTO: {
    		struct nand_oobfree *free = chip->ecc.layout->oobfree;
    		uint32_t boffs = 0, roffs = ops->ooboffs;
    		size_t bytes = 0;
    
    		for(; free->length && len; free++, len -= bytes) {
    			/* Read request not from offset 0 ? */
    			if (unlikely(roffs)) {
    				if (roffs >= free->length) {
    					roffs -= free->length;
    					continue;
    				}
    				boffs = free->offset + roffs;
    				bytes = min_t(size_t, len,
    					      (free->length - roffs));
    				roffs = 0;
    			} else {
    				bytes = min_t(size_t, len, free->length);
    				boffs = free->offset;
    			}
    			memcpy(oob, chip->oob_poi + boffs, bytes);
    			oob += bytes;
    		}
    		return oob;
    	}
    	default:
    		BUG();
    	}
    	return NULL;
    }
    
     * nand_do_read_ops - [Internal] Read data with ECC
     *
    
     * @mtd:	MTD device structure
     * @from:	offset to read from
    
     * @ops:	oob ops structure
    
     * Internal function. Called with chip held.
    
    static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
    			    struct mtd_oob_ops *ops)
    
    	int chipnr, page, realpage, col, bytes, aligned;
    	struct nand_chip *chip = mtd->priv;
    	struct mtd_ecc_stats stats;
    	int blkcheck = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1;
    	int sndcmd = 1;
    	int ret = 0;
    	uint32_t readlen = ops->len;
    	uint32_t oobreadlen = ops->ooblen;
    	uint8_t *bufpoi, *oob, *buf;
    
    	stats = mtd->ecc_stats;
    
    	chipnr = (int)(from >> chip->chip_shift);
    	chip->select_chip(mtd, chipnr);
    
    	realpage = (int)(from >> chip->page_shift);
    	page = realpage & chip->pagemask;
    
    	col = (int)(from & (mtd->writesize - 1));
    
    	buf = ops->datbuf;
    	oob = ops->oobbuf;
    
    	while(1) {
    		bytes = min(mtd->writesize - col, readlen);
    		aligned = (bytes == mtd->writesize);
    
    		/* Is the current page in the buffer ? */
    		if (realpage != chip->pagebuf || oob) {
    			bufpoi = aligned ? buf : chip->buffers->databuf;
    
    			if (likely(sndcmd)) {
    				chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page);
    				sndcmd = 0;
    			}
    
    			/* Now read the page into the buffer */
    			if (unlikely(ops->mode == MTD_OOB_RAW))
    				ret = chip->ecc.read_page_raw(mtd, chip, bufpoi);
    			else
    				ret = chip->ecc.read_page(mtd, chip, bufpoi);
    			if (ret < 0)
    				break;
    
    			/* Transfer not aligned data */
    			if (!aligned) {
    				chip->pagebuf = realpage;
    				memcpy(buf, chip->buffers->databuf + col, bytes);
    			}
    
    			buf += bytes;
    
    			if (unlikely(oob)) {
    				/* Raw mode does data:oob:data:oob */
    				if (ops->mode != MTD_OOB_RAW) {
    					int toread = min(oobreadlen,
    						chip->ecc.layout->oobavail);
    					if (toread) {
    						oob = nand_transfer_oob(chip,
    							oob, ops, toread);
    						oobreadlen -= toread;
    					}
    				} else
    					buf = nand_transfer_oob(chip,
    						buf, ops, mtd->oobsize);
    			}
    
    			if (!(chip->options & NAND_NO_READRDY)) {
    				/*
    				 * Apply delay or wait for ready/busy pin. Do
    				 * this before the AUTOINCR check, so no
    				 * problems arise if a chip which does auto
    				 * increment is marked as NOAUTOINCR by the
    				 * board driver.
    				 */
    				if (!chip->dev_ready)
    					udelay(chip->chip_delay);
    				else
    					nand_wait_ready(mtd);
    			}
    		} else {
    			memcpy(buf, chip->buffers->databuf + col, bytes);
    			buf += bytes;
    		}
    
    		readlen -= bytes;
    
    		if (!readlen)
    			break;
    
    		/* For subsequent reads align to page boundary. */
    		col = 0;
    		/* Increment page address */
    		realpage++;
    
    		page = realpage & chip->pagemask;
    		/* Check, if we cross a chip boundary */
    		if (!page) {
    			chipnr++;
    			chip->select_chip(mtd, -1);
    			chip->select_chip(mtd, chipnr);
    
    		/* Check, if the chip supports auto page increment
    		 * or if we have hit a block boundary.
    		 */
    		if (!NAND_CANAUTOINCR(chip) || !(page & blkcheck))
    			sndcmd = 1;
    	}
    
    	ops->retlen = ops->len - (size_t) readlen;
    	if (oob)
    		ops->oobretlen = ops->ooblen - oobreadlen;
    
    	if (ret)
    		return ret;
    
    	if (mtd->ecc_stats.failed - stats.failed)
    		return -EBADMSG;
    
    	return  mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0;
    }
    
    /**
     * nand_read - [MTD Interface] MTD compability function for nand_do_read_ecc
     * @mtd:	MTD device structure
     * @from:	offset to read from
     * @len:	number of bytes to read
     * @retlen:	pointer to variable to store the number of read bytes
     * @buf:	the databuffer to put data
     *
     * Get hold of the chip and call nand_do_read
     */
    static int nand_read(struct mtd_info *mtd, loff_t from, size_t len,
    		     size_t *retlen, uint8_t *buf)
    {
    	struct nand_chip *chip = mtd->priv;
    	int ret;
    
    	/* Do not allow reads past end of device */
    	if ((from + len) > mtd->size)
    		return -EINVAL;
    	if (!len)
    		return 0;
    
    	nand_get_device(chip, mtd, FL_READING);
    
    	chip->ops.len = len;
    	chip->ops.datbuf = buf;
    	chip->ops.oobbuf = NULL;
    
    	ret = nand_do_read_ops(mtd, from, &chip->ops);
    
    	*retlen = chip->ops.retlen;
    
    	nand_release_device(mtd);
    
    /**
     * nand_read_oob_std - [REPLACABLE] the most common OOB data read function
     * @mtd:	mtd info structure
     * @chip:	nand chip info structure
     * @page:	page number to read
     * @sndcmd:	flag whether to issue read command or not
     */
    static int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip,
    			     int page, int sndcmd)
    {
    	if (sndcmd) {
    		chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
    		sndcmd = 0;
    	}
    	chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
    	return sndcmd;
    }
    
    /**
     * nand_read_oob_syndrome - [REPLACABLE] OOB data read function for HW ECC
     *			    with syndromes
     * @mtd:	mtd info structure
     * @chip:	nand chip info structure
     * @page:	page number to read
     * @sndcmd:	flag whether to issue read command or not
     */
    static int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
    				  int page, int sndcmd)
    {
    	uint8_t *buf = chip->oob_poi;
    	int length = mtd->oobsize;
    	int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad;
    	int eccsize = chip->ecc.size;
    	uint8_t *bufpoi = buf;
    	int i, toread, sndrnd = 0, pos;
    
    	chip->cmdfunc(mtd, NAND_CMD_READ0, chip->ecc.size, page);
    	for (i = 0; i < chip->ecc.steps; i++) {
    		if (sndrnd) {
    			pos = eccsize + i * (eccsize + chunk);
    			if (mtd->writesize > 512)
    				chip->cmdfunc(mtd, NAND_CMD_RNDOUT, pos, -1);
    			else
    				chip->cmdfunc(mtd, NAND_CMD_READ0, pos, page);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		} else
    
    			sndrnd = 1;
    		toread = min_t(int, length, chunk);
    		chip->read_buf(mtd, bufpoi, toread);
    		bufpoi += toread;
    		length -= toread;
    	}
    	if (length > 0)
    		chip->read_buf(mtd, bufpoi, length);
    
    /**
     * nand_write_oob_std - [REPLACABLE] the most common OOB data write function
     * @mtd:	mtd info structure
     * @chip:	nand chip info structure
     * @page:	page number to write
     */
    static int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip,
    			      int page)
    {
    	int status = 0;
    	const uint8_t *buf = chip->oob_poi;
    	int length = mtd->oobsize;
    
    	chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page);
    	chip->write_buf(mtd, buf, length);
    	/* Send command to program the OOB data */
    	chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
    
    	status = chip->waitfunc(mtd, chip);
    
    	return status & NAND_STATUS_FAIL ? -EIO : 0;
    }
    
    /**
     * nand_write_oob_syndrome - [REPLACABLE] OOB data write function for HW ECC
     *			     with syndrome - only for large page flash !
     * @mtd:	mtd info structure
     * @chip:	nand chip info structure
     * @page:	page number to write
     */
    static int nand_write_oob_syndrome(struct mtd_info *mtd,
    				   struct nand_chip *chip, int page)
    {
    	int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad;
    	int eccsize = chip->ecc.size, length = mtd->oobsize;
    	int i, len, pos, status = 0, sndcmd = 0, steps = chip->ecc.steps;
    	const uint8_t *bufpoi = chip->oob_poi;
    
    	 * data-ecc-data-ecc ... ecc-oob
    	 * or
    	 * data-pad-ecc-pad-data-pad .... ecc-pad-oob
    
    	if (!chip->ecc.prepad && !chip->ecc.postpad) {
    		pos = steps * (eccsize + chunk);
    		steps = 0;
    	} else
    		pos = eccsize;
    
    	chip->cmdfunc(mtd, NAND_CMD_SEQIN, pos, page);
    	for (i = 0; i < steps; i++) {
    		if (sndcmd) {
    			if (mtd->writesize <= 512) {
    				uint32_t fill = 0xFFFFFFFF;
    
    				len = eccsize;
    				while (len > 0) {
    					int num = min_t(int, len, 4);
    					chip->write_buf(mtd, (uint8_t *)&fill,
    							num);
    					len -= num;
    				}
    			} else {
    				pos = eccsize + i * (eccsize + chunk);
    				chip->cmdfunc(mtd, NAND_CMD_RNDIN, pos, -1);
    			}
    		} else
    			sndcmd = 1;
    		len = min_t(int, length, chunk);
    		chip->write_buf(mtd, bufpoi, len);
    		bufpoi += len;
    		length -= len;
    	}
    	if (length > 0)
    		chip->write_buf(mtd, bufpoi, length);
    
    	chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
    	status = chip->waitfunc(mtd, chip);
    
    	return status & NAND_STATUS_FAIL ? -EIO : 0;
    
     * nand_do_read_oob - [Intern] NAND read out-of-band
    
     * @mtd:	MTD device structure
     * @from:	offset to read from
    
     * @ops:	oob operations description structure
    
     *
     * NAND read out-of-band data from the spare area
     */
    
    static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
    			    struct mtd_oob_ops *ops)
    
    	int page, realpage, chipnr, sndcmd = 1;
    	struct nand_chip *chip = mtd->priv;
    	int blkcheck = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1;
    	int readlen = ops->ooblen;
    	int len;
    	uint8_t *buf = ops->oobbuf;
    
    	MTDDEBUG (MTD_DEBUG_LEVEL3, "nand_read_oob: from = 0x%08Lx, len = %i\n",
    	          (unsigned long long)from, readlen);
    
    	if (ops->mode == MTD_OOB_AUTO)
    		len = chip->ecc.layout->oobavail;
    	else
    		len = mtd->oobsize;
    
    	if (unlikely(ops->ooboffs >= len)) {
    		MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_read_oob: "
    		          "Attempt to start read outside oob\n");
    		return -EINVAL;
    	}
    
    
    	/* Do not allow reads past end of device */
    
    	if (unlikely(from >= mtd->size ||
    		     ops->ooboffs + readlen > ((mtd->size >> chip->page_shift) -
    					(from >> chip->page_shift)) * len)) {
    		MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_read_oob: "
    		          "Attempt read beyond end of device\n");
    
    	chipnr = (int)(from >> chip->chip_shift);
    	chip->select_chip(mtd, chipnr);
    
    	/* Shift to get page */
    	realpage = (int)(from >> chip->page_shift);
    	page = realpage & chip->pagemask;
    
    	while(1) {
    		sndcmd = chip->ecc.read_oob(mtd, chip, page, sndcmd);
    
    		len = min(len, readlen);
    		buf = nand_transfer_oob(chip, buf, ops, len);
    
    		if (!(chip->options & NAND_NO_READRDY)) {
    			/*
    			 * Apply delay or wait for ready/busy pin. Do this
    			 * before the AUTOINCR check, so no problems arise if a
    			 * chip which does auto increment is marked as
    			 * NOAUTOINCR by the board driver.
    			 */
    			if (!chip->dev_ready)
    				udelay(chip->chip_delay);
    			else
    				nand_wait_ready(mtd);
    
    		readlen -= len;
    		if (!readlen)
    			break;
    
    		/* Increment page address */
    		realpage++;
    
    		page = realpage & chip->pagemask;
    		/* Check, if we cross a chip boundary */
    		if (!page) {
    			chipnr++;
    			chip->select_chip(mtd, -1);
    			chip->select_chip(mtd, chipnr);
    		}
    
    		/* Check, if the chip supports auto page increment
    		 * or if we have hit a block boundary.
    		 */
    		if (!NAND_CANAUTOINCR(chip) || !(page & blkcheck))
    			sndcmd = 1;
    	}
    
    	ops->oobretlen = ops->ooblen;
    
     * nand_read_oob - [MTD Interface] NAND read data and/or out-of-band
    
     * @mtd:	MTD device structure
     * @from:	offset to read from
    
     * @ops:	oob operation description structure
    
     * NAND read data and/or out-of-band data
    
    static int nand_read_oob(struct mtd_info *mtd, loff_t from,
    			 struct mtd_oob_ops *ops)
    
    	struct nand_chip *chip = mtd->priv;
    	int ret = -ENOTSUPP;
    
    	ops->retlen = 0;
    
    
    	/* Do not allow reads past end of device */
    
    	if (ops->datbuf && (from + ops->len) > mtd->size) {
    		MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_read_oob: "
    		          "Attempt read beyond end of device\n");
    
    	nand_get_device(chip, mtd, FL_READING);
    
    	switch(ops->mode) {
    	case MTD_OOB_PLACE:
    	case MTD_OOB_AUTO:
    	case MTD_OOB_RAW:
    		break;
    
    	if (!ops->datbuf)
    		ret = nand_do_read_oob(mtd, from, ops);
    	else
    		ret = nand_do_read_ops(mtd, from, ops);
    
     out:
    	nand_release_device(mtd);
    	return ret;
    }
    
    /**
     * nand_write_page_raw - [Intern] raw page write function
     * @mtd:	mtd info structure
     * @chip:	nand chip info structure
     * @buf:	data buffer
     */
    static void nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
    				const uint8_t *buf)
    {
    	chip->write_buf(mtd, buf, mtd->writesize);
    	chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
    }
    
    /**
     * nand_write_page_swecc - [REPLACABLE] software ecc based page write function
     * @mtd:	mtd info structure
     * @chip:	nand chip info structure
     * @buf:	data buffer
     */
    static void nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
    				  const uint8_t *buf)
    {
    	int i, eccsize = chip->ecc.size;
    	int eccbytes = chip->ecc.bytes;
    	int eccsteps = chip->ecc.steps;
    	uint8_t *ecc_calc = chip->buffers->ecccalc;
    	const uint8_t *p = buf;
    	uint32_t *eccpos = chip->ecc.layout->eccpos;
    
    	/* Software ecc calculation */
    	for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize)
    		chip->ecc.calculate(mtd, p, &ecc_calc[i]);
    
    	for (i = 0; i < chip->ecc.total; i++)
    		chip->oob_poi[eccpos[i]] = ecc_calc[i];
    
    	chip->ecc.write_page_raw(mtd, chip, buf);
    
    /**
     * nand_write_page_hwecc - [REPLACABLE] hardware ecc based page write function
     * @mtd:	mtd info structure
     * @chip:	nand chip info structure
     * @buf:	data buffer
     */
    static void nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
    				  const uint8_t *buf)
    {
    	int i, eccsize = chip->ecc.size;
    	int eccbytes = chip->ecc.bytes;
    	int eccsteps = chip->ecc.steps;
    	uint8_t *ecc_calc = chip->buffers->ecccalc;
    	const uint8_t *p = buf;
    	uint32_t *eccpos = chip->ecc.layout->eccpos;
    
    	for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
    		chip->ecc.hwctl(mtd, NAND_ECC_WRITE);
    		chip->write_buf(mtd, p, eccsize);
    		chip->ecc.calculate(mtd, p, &ecc_calc[i]);
    	}
    
    	for (i = 0; i < chip->ecc.total; i++)
    		chip->oob_poi[eccpos[i]] = ecc_calc[i];
    
    	chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
    }
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    /**
    
     * nand_write_page_syndrome - [REPLACABLE] hardware ecc syndrom based page write
     * @mtd:	mtd info structure
     * @chip:	nand chip info structure
     * @buf:	data buffer
    
     * The hw generator calculates the error syndrome automatically. Therefor
     * we need a special oob layout and handling.
     */
    static void nand_write_page_syndrome(struct mtd_info *mtd,
    				    struct nand_chip *chip, const uint8_t *buf)
    
    	int i, eccsize = chip->ecc.size;
    	int eccbytes = chip->ecc.bytes;
    	int eccsteps = chip->ecc.steps;
    	const uint8_t *p = buf;
    	uint8_t *oob = chip->oob_poi;
    
    	for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
    
    		chip->ecc.hwctl(mtd, NAND_ECC_WRITE);
    		chip->write_buf(mtd, p, eccsize);
    
    		if (chip->ecc.prepad) {
    			chip->write_buf(mtd, oob, chip->ecc.prepad);
    			oob += chip->ecc.prepad;
    		}
    
    		chip->ecc.calculate(mtd, p, oob);
    		chip->write_buf(mtd, oob, eccbytes);
    		oob += eccbytes;
    
    		if (chip->ecc.postpad) {
    			chip->write_buf(mtd, oob, chip->ecc.postpad);
    			oob += chip->ecc.postpad;
    
    	/* Calculate remaining oob bytes */
    	i = mtd->oobsize - (oob - chip->oob_poi);
    	if (i)
    		chip->write_buf(mtd, oob, i);
    }
    
     * nand_write_page - [REPLACEABLE] write one page
    
     * @mtd:	MTD device structure
    
     * @chip:	NAND chip descriptor
    
     * @page:	page number to write
     * @cached:	cached programming
     * @raw:	use _raw version of write_page
     */
    static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
    			   const uint8_t *buf, int page, int cached, int raw)
    
    	int status;
    
    	chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page);
    
    	if (unlikely(raw))
    		chip->ecc.write_page_raw(mtd, chip, buf);
    	else
    		chip->ecc.write_page(mtd, chip, buf);
    
    	/*
    	 * Cached progamming disabled for now, Not sure if its worth the
    	 * trouble. The speed gain is not very impressive. (2.3->2.6Mib/s)
    	 */
    	cached = 0;
    
    	if (!cached || !(chip->options & NAND_CACHEPRG)) {
    
    		chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
    		status = chip->waitfunc(mtd, chip);
    		/*
    		 * See if operation failed and additional status checks are
    		 * available
    		 */
    		if ((status & NAND_STATUS_FAIL) && (chip->errstat))
    			status = chip->errstat(mtd, chip, FL_WRITING, status,
    					       page);
    
    		if (status & NAND_STATUS_FAIL)
    			return -EIO;
    	} else {
    		chip->cmdfunc(mtd, NAND_CMD_CACHEDPROG, -1, -1);
    		status = chip->waitfunc(mtd, chip);
    	}
    
    #ifdef CONFIG_MTD_NAND_VERIFY_WRITE
    	/* Send command to read back the data */
    	chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page);
    
    	if (chip->verify_buf(mtd, buf, mtd->writesize))
    		return -EIO;
    #endif
    	return 0;
    }
    
    /**
     * nand_fill_oob - [Internal] Transfer client buffer to oob
     * @chip:	nand chip structure
     * @oob:	oob data buffer
     * @ops:	oob ops structure
     */
    static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob,
    				  struct mtd_oob_ops *ops)
    {
    	size_t len = ops->ooblen;
    
    	switch(ops->mode) {
    
    	case MTD_OOB_PLACE:
    	case MTD_OOB_RAW:
    		memcpy(chip->oob_poi + ops->ooboffs, oob, len);
    		return oob + len;
    
    	case MTD_OOB_AUTO: {
    		struct nand_oobfree *free = chip->ecc.layout->oobfree;
    		uint32_t boffs = 0, woffs = ops->ooboffs;
    		size_t bytes = 0;
    
    		for(; free->length && len; free++, len -= bytes) {
    			/* Write request not from offset 0 ? */
    			if (unlikely(woffs)) {
    				if (woffs >= free->length) {
    					woffs -= free->length;
    					continue;
    				}
    				boffs = free->offset + woffs;
    				bytes = min_t(size_t, len,
    					      (free->length - woffs));
    				woffs = 0;
    			} else {
    				bytes = min_t(size_t, len, free->length);
    				boffs = free->offset;
    			}
    			memcpy(chip->oob_poi + boffs, oob, bytes);
    			oob += bytes;
    		}
    		return oob;
    	}
    	default:
    		BUG();
    	}
    	return NULL;
    
    #define NOTALIGNED(x)	(x & (chip->subpagesize - 1)) != 0
    
    
     * nand_do_write_ops - [Internal] NAND write with ECC
    
     * @mtd:	MTD device structure
     * @to:		offset to write to
    
     * @ops:	oob operations description structure
    
    static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
    			     struct mtd_oob_ops *ops)
    
    	int chipnr, realpage, page, blockmask, column;
    	struct nand_chip *chip = mtd->priv;
    	uint32_t writelen = ops->len;
    	uint8_t *oob = ops->oobbuf;
    	uint8_t *buf = ops->datbuf;
    	int ret, subpage;
    
    	ops->retlen = 0;
    	if (!writelen)
    		return 0;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	/* reject writes, which are not page aligned */
    
    	if (NOTALIGNED(to) || NOTALIGNED(ops->len)) {
    		printk(KERN_NOTICE "nand_write: "
    		       "Attempt to write not page aligned data\n");
    
    	column = to & (mtd->writesize - 1);
    	subpage = column || (writelen & (mtd->writesize - 1));
    
    	if (subpage && oob)
    		return -EINVAL;
    
    	chipnr = (int)(to >> chip->chip_shift);
    	chip->select_chip(mtd, chipnr);
    
    
    	/* Check, if it is write protected */
    
    	if (nand_check_wp(mtd)) {
    
    		printk (KERN_NOTICE "nand_do_write_ops: Device is write protected\n");
    		return -EIO;
    
    	realpage = (int)(to >> chip->page_shift);
    	page = realpage & chip->pagemask;
    	blockmask = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1;
    
    	/* Invalidate the page cache, when we write to the cached page */
    	if (to <= (chip->pagebuf << chip->page_shift) &&
    	    (chip->pagebuf << chip->page_shift) < (to + ops->len))
    		chip->pagebuf = -1;
    
    	/* If we're not given explicit OOB data, let it be 0xFF */
    	if (likely(!oob))
    		memset(chip->oob_poi, 0xff, mtd->oobsize);
    
    	while(1) {
    		int bytes = mtd->writesize;
    		int cached = writelen > bytes && page != blockmask;
    		uint8_t *wbuf = buf;
    
    		/* Partial page write ? */
    		if (unlikely(column || writelen < (mtd->writesize - 1))) {
    			cached = 0;
    			bytes = min_t(int, bytes - column, (int) writelen);
    			chip->pagebuf = -1;
    			memset(chip->buffers->databuf, 0xff, mtd->writesize);
    			memcpy(&chip->buffers->databuf[column], buf, bytes);
    			wbuf = chip->buffers->databuf;
    		}
    
    		if (unlikely(oob))
    			oob = nand_fill_oob(chip, oob, ops);
    
    		ret = chip->write_page(mtd, chip, wbuf, page, cached,
    				       (ops->mode == MTD_OOB_RAW));
    		if (ret)
    			break;
    
    		writelen -= bytes;
    		if (!writelen)
    			break;
    
    		column = 0;
    		buf += bytes;
    		realpage++;
    
    		page = realpage & chip->pagemask;
    		/* Check, if we cross a chip boundary */
    		if (!page) {
    			chipnr++;
    			chip->select_chip(mtd, -1);
    			chip->select_chip(mtd, chipnr);
    
    	ops->retlen = ops->len - writelen;
    	if (unlikely(oob))
    		ops->oobretlen = ops->ooblen;
    
     * nand_write - [MTD Interface] NAND write with ECC
    
     * @mtd:	MTD device structure
     * @to:		offset to write to
     * @len:	number of bytes to write
     * @retlen:	pointer to variable to store the number of written bytes
     * @buf:	the data to write
     *
    
     * NAND write with ECC
    
    static int nand_write(struct mtd_info *mtd, loff_t to, size_t len,
    			  size_t *retlen, const uint8_t *buf)
    
    	struct nand_chip *chip = mtd->priv;
    	int ret;
    
    	/* Do not allow reads past end of device */
    	if ((to + len) > mtd->size)
    		return -EINVAL;
    	if (!len)
    		return 0;
    
    	nand_get_device(chip, mtd, FL_WRITING);
    
    	chip->ops.len = len;
    	chip->ops.datbuf = (uint8_t *)buf;
    	chip->ops.oobbuf = NULL;
    
    	ret = nand_do_write_ops(mtd, to, &chip->ops);
    
    	*retlen = chip->ops.retlen;
    
    	nand_release_device(mtd);
    
    	return ret;
    }
    
    /**
     * nand_do_write_oob - [MTD Interface] NAND write out-of-band
     * @mtd:	MTD device structure
     * @to:		offset to write to
     * @ops:	oob operation description structure
     *
     * NAND write out-of-band
     */
    static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
    			     struct mtd_oob_ops *ops)
    {
    	int chipnr, page, status, len;
    	struct nand_chip *chip = mtd->priv;
    
    	MTDDEBUG (MTD_DEBUG_LEVEL3, "nand_write_oob: to = 0x%08x, len = %i\n",
    	          (unsigned int)to, (int)ops->ooblen);
    
    	if (ops->mode == MTD_OOB_AUTO)
    		len = chip->ecc.layout->oobavail;
    	else
    		len = mtd->oobsize;
    
    
    	/* Do not allow write past end of page */
    
    	if ((ops->ooboffs + ops->ooblen) > len) {
    
    		MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: "
    		          "Attempt to write past end of page\n");
    
    	if (unlikely(ops->ooboffs >= len)) {
    		MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_read_oob: "
    		          "Attempt to start write outside oob\n");
    		return -EINVAL;
    	}
    
    	/* Do not allow reads past end of device */
    	if (unlikely(to >= mtd->size ||
    		     ops->ooboffs + ops->ooblen >
    			((mtd->size >> chip->page_shift) -
    			 (to >> chip->page_shift)) * len)) {
    		MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_read_oob: "
    		          "Attempt write beyond end of device\n");
    		return -EINVAL;
    	}
    
    	chipnr = (int)(to >> chip->chip_shift);
    	chip->select_chip(mtd, chipnr);
    
    	/* Shift to get page */
    	page = (int)(to >> chip->page_shift);
    
    	/*
    	 * Reset the chip. Some chips (like the Toshiba TC5832DC found in one
    	 * of my DiskOnChip 2000 test units) will clear the whole data page too
    	 * if we don't do this. I have no clue why, but I seem to have 'fixed'
    	 * it in the doc2000 driver in August 1999.  dwmw2.
    	 */
    	chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
    
    
    	/* Check, if it is write protected */
    	if (nand_check_wp(mtd))
    
    		return -EROFS;
    
    	/* Invalidate the page cache, if we write to the cached page */
    
    	if (page == chip->pagebuf)
    		chip->pagebuf = -1;
    
    	memset(chip->oob_poi, 0xff, mtd->oobsize);
    	nand_fill_oob(chip, ops->oobbuf, ops);
    	status = chip->ecc.write_oob(mtd, chip, page & chip->pagemask);
    	memset(chip->oob_poi, 0xff, mtd->oobsize);
    
    	if (status)
    		return status;
    
    	ops->oobretlen = ops->ooblen;
    
     * nand_write_oob - [MTD Interface] NAND write data and/or out-of-band
    
     * @mtd:	MTD device structure
     * @to:		offset to write to
    
     * @ops:	oob operation description structure
    
    static int nand_write_oob(struct mtd_info *mtd, loff_t to,
    			  struct mtd_oob_ops *ops)
    
    	struct nand_chip *chip = mtd->priv;
    	int ret = -ENOTSUPP;
    
    	ops->retlen = 0;
    
    	/* Do not allow writes past end of device */
    	if (ops->datbuf && (to + ops->len) > mtd->size) {
    		MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_read_oob: "
    		          "Attempt read beyond end of device\n");
    
    	nand_get_device(chip, mtd, FL_WRITING);
    
    	switch(ops->mode) {
    	case MTD_OOB_PLACE:
    	case MTD_OOB_AUTO:
    	case MTD_OOB_RAW:
    		break;
    
    	if (!ops->datbuf)
    		ret = nand_do_write_oob(mtd, to, ops);
    	else
    		ret = nand_do_write_ops(mtd, to, ops);
    
    	nand_release_device(mtd);
    	return ret;
    }
    
    /**
     * single_erease_cmd - [GENERIC] NAND standard block erase command function
     * @mtd:	MTD device structure
     * @page:	the page address of the block which will be erased
     *
     * Standard erase command for NAND chips