Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 29072b96 authored by Thomas Gleixner's avatar Thomas Gleixner Committed by Artem Bityutskiy
Browse files

[MTD] NAND: add subpage write support



Many SLC NANDs support up to 4 writes at one NAND page. Add support
of this feature.

Signed-off-by: default avatarArtem Bityutskiy <dedekind@infradead.org>
parent f6a7ecb1
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -772,6 +772,7 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c
		concat->mtd.ecc_stats.badblocks +=
			subdev[i]->ecc_stats.badblocks;
		if (concat->mtd.writesize   !=  subdev[i]->writesize ||
		    concat->mtd.subpage_sft != subdev[i]->subpage_sft ||
		    concat->mtd.oobsize    !=  subdev[i]->oobsize ||
		    concat->mtd.ecctype    !=  subdev[i]->ecctype ||
		    concat->mtd.eccsize    !=  subdev[i]->eccsize ||
+1 −0
Original line number Diff line number Diff line
@@ -340,6 +340,7 @@ int add_mtd_partitions(struct mtd_info *master,
		slave->mtd.oobsize = master->oobsize;
		slave->mtd.ecctype = master->ecctype;
		slave->mtd.eccsize = master->eccsize;
		slave->mtd.subpage_sft = master->subpage_sft;

		slave->mtd.name = parts[i].name;
		slave->mtd.bank_size = master->bank_size;
+44 −9
Original line number Diff line number Diff line
@@ -1590,7 +1590,7 @@ static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob,
	return NULL;
}

#define NOTALIGNED(x) (x & (mtd->writesize-1)) != 0
#define NOTALIGNED(x)	(x & (chip->subpagesize - 1)) != 0

/**
 * nand_do_write_ops - [Internal] NAND write with ECC
@@ -1603,15 +1603,16 @@ static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob,
static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
			     struct mtd_oob_ops *ops)
{
	int chipnr, realpage, page, blockmask;
	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 bytes = mtd->writesize;
	int ret;
	int ret, subpage;

	ops->retlen = 0;
	if (!writelen)
		return 0;

	/* reject writes, which are not page aligned */
	if (NOTALIGNED(to) || NOTALIGNED(ops->len)) {
@@ -1620,8 +1621,11 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
		return -EINVAL;
	}

	if (!writelen)
		return 0;
	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);
@@ -1644,12 +1648,24 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
		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, buf, page, cached,
		ret = chip->write_page(mtd, chip, wbuf, page, cached,
				       (ops->mode == MTD_OOB_RAW));
		if (ret)
			break;
@@ -1658,6 +1674,7 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
		if (!writelen)
			break;

		column = 0;
		buf += bytes;
		realpage++;

@@ -2201,8 +2218,8 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
	/* Newer devices have all the information in additional id bytes */
	if (!type->pagesize) {
		int extid;
		/* The 3rd id byte contains non relevant data ATM */
		extid = chip->read_byte(mtd);
		/* The 3rd id byte holds MLC / multichip data */
		chip->cellinfo = chip->read_byte(mtd);
		/* The 4th id byte is the important one */
		extid = chip->read_byte(mtd);
		/* Calc pagesize */
@@ -2482,6 +2499,24 @@ int nand_scan_tail(struct mtd_info *mtd)
	}
	chip->ecc.total = chip->ecc.steps * chip->ecc.bytes;

	/*
	 * Allow subpage writes up to ecc.steps. Not possible for MLC
	 * FLASH.
	 */
	if (!(chip->options & NAND_NO_SUBPAGE_WRITE) &&
	    !(chip->cellinfo & NAND_CI_CELLTYPE_MSK)) {
		switch(chip->ecc.steps) {
		case 2:
			mtd->subpage_sft = 1;
			break;
		case 4:
		case 8:
			mtd->subpage_sft = 2;
			break;
		}
	}
	chip->subpagesize = mtd->writesize >> mtd->subpage_sft;

	/* Initialize state */
	chip->state = FL_READY;

+2 −0
Original line number Diff line number Diff line
@@ -200,6 +200,8 @@ struct mtd_info {

	/* ECC status information */
	struct mtd_ecc_stats ecc_stats;
	/* Subpage shift (NAND) */
	int subpage_sft;

	void *priv;

+9 −0
Original line number Diff line number Diff line
@@ -166,6 +166,9 @@ typedef enum {
 * for all large page devices, as they do not support
 * autoincrement.*/
#define NAND_NO_READRDY		0x00000100
/* Chip does not allow subpage writes */
#define NAND_NO_SUBPAGE_WRITE	0x00000200


/* Options valid for Samsung large page devices */
#define NAND_SAMSUNG_LP_OPTIONS \
@@ -193,6 +196,9 @@ typedef enum {
/* Nand scan has allocated controller struct */
#define NAND_CONTROLLER_ALLOC	0x80000000

/* Cell info constants */
#define NAND_CI_CHIPNR_MSK	0x03
#define NAND_CI_CELLTYPE_MSK	0x0C

/*
 * nand_state_t - chip states
@@ -341,6 +347,7 @@ struct nand_buffers {
 * @chipsize:		[INTERN] the size of one chip for multichip arrays
 * @pagemask:		[INTERN] page number mask = number of (pages / chip) - 1
 * @pagebuf:		[INTERN] holds the pagenumber which is currently in data_buf
 * @subpagesize:	[INTERN] holds the subpagesize
 * @ecclayout:		[REPLACEABLE] the default ecc placement scheme
 * @bbt:		[INTERN] bad block table pointer
 * @bbt_td:		[REPLACEABLE] bad block table descriptor for flash lookup
@@ -388,6 +395,8 @@ struct nand_chip {
	unsigned long	chipsize;
	int		pagemask;
	int		pagebuf;
	int		subpagesize;
	uint8_t		cellinfo;
	int		badblockpos;

	nand_state_t	state;