Skip to content

Commit

Permalink
mtd: rawnand: Prevent crossing LUN boundaries during sequential reads
Browse files Browse the repository at this point in the history
The ONFI specification states that devices do not need to support
sequential reads across LUN boundaries. In order to prevent such event
from happening and possibly failing, let's introduce the concept of
"pause" in the sequential read to handle these cases. The first/last
pages remain the same but any time we cross a LUN boundary we will end
and restart (if relevant) the sequential read operation.

Cc: stable@vger.kernel.org
Fixes: 003fe4b ("mtd: rawnand: Support for sequential cache reads")
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
Tested-by: Martin Hundebøll <martin@geanix.com>
Link: https://lore.kernel.org/linux-mtd/20231215123208.516590-2-miquel.raynal@bootlin.com
  • Loading branch information
miquelraynal committed Dec 22, 2023
1 parent a43bdc3 commit bbcd80f
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 6 deletions.
43 changes: 37 additions & 6 deletions drivers/mtd/nand/raw/nand_base.c
Original file line number Diff line number Diff line change
Expand Up @@ -1207,6 +1207,23 @@ static int nand_lp_exec_read_page_op(struct nand_chip *chip, unsigned int page,
return nand_exec_op(chip, &op);
}

static void rawnand_cap_cont_reads(struct nand_chip *chip)
{
struct nand_memory_organization *memorg;
unsigned int pages_per_lun, first_lun, last_lun;

memorg = nanddev_get_memorg(&chip->base);
pages_per_lun = memorg->pages_per_eraseblock * memorg->eraseblocks_per_lun;
first_lun = chip->cont_read.first_page / pages_per_lun;
last_lun = chip->cont_read.last_page / pages_per_lun;

/* Prevent sequential cache reads across LUN boundaries */
if (first_lun != last_lun)
chip->cont_read.pause_page = first_lun * pages_per_lun + pages_per_lun - 1;
else
chip->cont_read.pause_page = chip->cont_read.last_page;
}

static int nand_lp_exec_cont_read_page_op(struct nand_chip *chip, unsigned int page,
unsigned int offset_in_page, void *buf,
unsigned int len, bool check_only)
Expand All @@ -1225,7 +1242,7 @@ static int nand_lp_exec_cont_read_page_op(struct nand_chip *chip, unsigned int p
NAND_OP_DATA_IN(len, buf, 0),
};
struct nand_op_instr cont_instrs[] = {
NAND_OP_CMD(page == chip->cont_read.last_page ?
NAND_OP_CMD(page == chip->cont_read.pause_page ?
NAND_CMD_READCACHEEND : NAND_CMD_READCACHESEQ,
NAND_COMMON_TIMING_NS(conf, tWB_max)),
NAND_OP_WAIT_RDY(NAND_COMMON_TIMING_MS(conf, tR_max),
Expand Down Expand Up @@ -1262,16 +1279,29 @@ static int nand_lp_exec_cont_read_page_op(struct nand_chip *chip, unsigned int p
}

if (page == chip->cont_read.first_page)
return nand_exec_op(chip, &start_op);
ret = nand_exec_op(chip, &start_op);
else
return nand_exec_op(chip, &cont_op);
ret = nand_exec_op(chip, &cont_op);
if (ret)
return ret;

if (!chip->cont_read.ongoing)
return 0;

if (page == chip->cont_read.pause_page &&
page != chip->cont_read.last_page) {
chip->cont_read.first_page = chip->cont_read.pause_page + 1;
rawnand_cap_cont_reads(chip);
} else if (page == chip->cont_read.last_page) {
chip->cont_read.ongoing = false;
}

return 0;
}

static bool rawnand_cont_read_ongoing(struct nand_chip *chip, unsigned int page)
{
return chip->cont_read.ongoing &&
page >= chip->cont_read.first_page &&
page <= chip->cont_read.last_page;
return chip->cont_read.ongoing && page >= chip->cont_read.first_page;
}

/**
Expand Down Expand Up @@ -3445,6 +3475,7 @@ static void rawnand_enable_cont_reads(struct nand_chip *chip, unsigned int page,
if (col)
chip->cont_read.first_page++;
chip->cont_read.last_page = page + ((readlen >> chip->page_shift) & chip->pagemask);
rawnand_cap_cont_reads(chip);
}

/**
Expand Down
2 changes: 2 additions & 0 deletions include/linux/mtd/rawnand.h
Original file line number Diff line number Diff line change
Expand Up @@ -1265,6 +1265,7 @@ struct nand_secure_region {
* @cont_read: Sequential page read internals
* @cont_read.ongoing: Whether a continuous read is ongoing or not
* @cont_read.first_page: Start of the continuous read operation
* @cont_read.pause_page: End of the current sequential cache read operation
* @cont_read.last_page: End of the continuous read operation
* @controller: The hardware controller structure which is shared among multiple
* independent devices
Expand Down Expand Up @@ -1321,6 +1322,7 @@ struct nand_chip {
struct {
bool ongoing;
unsigned int first_page;
unsigned int pause_page;
unsigned int last_page;
} cont_read;

Expand Down

0 comments on commit bbcd80f

Please sign in to comment.