From 961d3301a29a67f6ae7b036428cbf9b52f09dd89 Mon Sep 17 00:00:00 2001 From: Abhishek Sahu Date: Tue, 22 May 2018 11:20:36 +0530 Subject: [PATCH] mtd: nand: qcom: support for non-page aligned read start address Currently, the nand driver expects the start address to be page aligned. This patch adds the support for reading data from non-page aligned start address by using intermediate buffer. 1. Determine the number of pages with start address and length. 2. Do the full page read for all the pages. For first page, check the start address and determine the column. If column is non-zero then use the intermediate buffer for page data and copy the required number of bytes from this intermediate buffer to actual buffer. Change-Id: I05a4b98547c83f785096027596cedd83a283edd8 Signed-off-by: Abhishek Sahu --- drivers/mtd/nand/qpic_nand.c | 44 ++++++++++++++++++++++++------------ 1 file changed, 30 insertions(+), 14 deletions(-) diff --git a/drivers/mtd/nand/qpic_nand.c b/drivers/mtd/nand/qpic_nand.c index c71525acdf..bb9f9dc01c 100644 --- a/drivers/mtd/nand/qpic_nand.c +++ b/drivers/mtd/nand/qpic_nand.c @@ -1538,13 +1538,20 @@ static int qpic_nand_scan_bbt_nop(struct mtd_info *mtd) * length. */ static u_long qpic_get_read_page_count(struct mtd_info *mtd, - struct mtd_oob_ops *ops) + struct mtd_oob_ops *ops, loff_t to) { struct qpic_nand_dev *dev = MTD_QPIC_NAND_DEV(mtd); struct nand_chip *chip = MTD_NAND_CHIP(mtd); + uint32_t start_page, end_page; if (ops->datbuf != NULL) { - return (ops->len + mtd->writesize - 1) >> chip->page_shift; + /* + * Determine start page (can be non page aligned) and end page + * and calculate number of pages. + */ + start_page = to >> chip->page_shift; + end_page = (to + ops->len - 1) >> chip->page_shift; + return end_page - start_page + 1; } else { if (dev->oob_per_page == 0) return 0; @@ -1579,7 +1586,7 @@ static uint8_t *qpic_nand_read_oobbuf(struct mtd_info *mtd, * data, return an internal buffer, to hold the data temporarily. */ static uint8_t *qpic_nand_read_datbuf(struct mtd_info *mtd, - struct mtd_oob_ops *ops) + struct mtd_oob_ops *ops, uint32_t col) { size_t read_datlen; struct qpic_nand_dev *dev = MTD_QPIC_NAND_DEV(mtd); @@ -1588,7 +1595,7 @@ static uint8_t *qpic_nand_read_datbuf(struct mtd_info *mtd, return NULL; read_datlen = ops->len - ops->retlen; - if (read_datlen < mtd->writesize) + if (read_datlen < mtd->writesize || col) return dev->pad_dat; return ops->datbuf + ops->retlen; @@ -1620,7 +1627,9 @@ static void qpic_nand_read_oobcopy(struct mtd_info *mtd, * Copy the in-band data from the internal buffer, to the user buffer, * if the internal buffer was used for the read. */ -static void qpic_nand_read_datcopy(struct mtd_info *mtd, struct mtd_oob_ops *ops) +static void +qpic_nand_read_datcopy(struct mtd_info *mtd, + struct mtd_oob_ops *ops, uint32_t col) { size_t datlen; size_t read_datlen; @@ -1630,10 +1639,13 @@ static void qpic_nand_read_datcopy(struct mtd_info *mtd, struct mtd_oob_ops *ops return; read_datlen = ops->len - ops->retlen; - datlen = (read_datlen < mtd->writesize ? read_datlen : mtd->writesize); - if (read_datlen < mtd->writesize) - memcpy(ops->datbuf + ops->retlen, dev->pad_dat, datlen); + if (col == 0 && read_datlen >= mtd->writesize) { + datlen = mtd->writesize; + } else { + datlen = min(read_datlen, mtd->writesize - col); + memcpy(ops->datbuf + ops->retlen, dev->pad_dat + col, datlen); + } ops->retlen += datlen; } @@ -1998,6 +2010,7 @@ static int qpic_nand_read_oob(struct mtd_info *mtd, loff_t to, struct nand_chip *chip = MTD_NAND_CHIP(mtd); uint32_t start_page; uint32_t num_pages; + uint32_t col; enum nand_cfg_value cfg_mode; unsigned int max_bitflips = 0; unsigned int ecc_failures = mtd->ecc_stats.failed; @@ -2010,9 +2023,6 @@ static int qpic_nand_read_oob(struct mtd_info *mtd, loff_t to, if (ops->datbuf && (to + ops->len) > mtd->size) return -EINVAL; - if (to & (mtd->writesize - 1)) - return -EINVAL; - if (ops->ooboffs != 0) return -EINVAL; @@ -2025,14 +2035,20 @@ static int qpic_nand_read_oob(struct mtd_info *mtd, loff_t to, } start_page = ((to >> chip->page_shift)); - num_pages = qpic_get_read_page_count(mtd, ops); + num_pages = qpic_get_read_page_count(mtd, ops, to); for (i = 0; i < num_pages; i++) { struct mtd_oob_ops page_ops; + + /* + * If start address is non page alinged then determine the + * column offset + */ + col = i == 0 ? to & (mtd->writesize - 1) : 0; page_ops.mode = ops->mode; page_ops.len = mtd->writesize; page_ops.ooblen = dev->oob_per_page; - page_ops.datbuf = qpic_nand_read_datbuf(mtd, ops); + page_ops.datbuf = qpic_nand_read_datbuf(mtd, ops, col); page_ops.oobbuf = qpic_nand_read_oobbuf(mtd, ops); page_ops.retlen = 0; page_ops.oobretlen = 0; @@ -2046,7 +2062,7 @@ static int qpic_nand_read_oob(struct mtd_info *mtd, loff_t to, } max_bitflips = max_t(unsigned int, max_bitflips, ret); - qpic_nand_read_datcopy(mtd, ops); + qpic_nand_read_datcopy(mtd, ops, col); qpic_nand_read_oobcopy(mtd, ops); }