All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v3 0/9] mtd: rawnand: denali: exec_op(), controller/chip separation, and cleanups
@ 2019-03-12  8:44 ` Masahiro Yamada
  0 siblings, 0 replies; 40+ messages in thread
From: Masahiro Yamada @ 2019-03-12  8:44 UTC (permalink / raw)
  To: linux-mtd, Miquel Raynal
  Cc: Boris Brezillon, Masahiro Yamada, devicetree, Brian Norris,
	linux-kernel, Marek Vasut, Richard Weinberger, David Woodhouse,
	Rob Herring, Mark Rutland


I took time for the Denali driver to catch up with the latest framework.

 - switch over to exec_op() and remove legacy hooks

 - separate controller/chips

 - various cleanups

Major changes in v3:
  - Drop "mtd: rawnand: denali: use more precise timeout for NAND_OP_WAITRDT_INSTR"
    entirely according to the review comments in v2

  - Add comments to helpers in 2/9



Masahiro Yamada (9):
  mtd: rawnand: denali: use nand_chip pointer more for internal
    functions
  mtd: rawnand: denali: refactor syndrome layout handling for raw access
  mtd: rawnand: denali: remove unneeded casts in denali_{read,write}_pio
  mtd: rawnand: denali: switch over to ->exec_op() from legacy hooks
  mtd: rawnand: denali: use bool type instead of int where appropriate
  mtd: rawnand: denali_pci: rename goto labels
  mtd: rawnand: denali: decouple controller and NAND chips
  mtd: rawnand: denali: remove DENALI_NR_BANKS macro
  mtd: rawnand: denali: clean up coding style

 .../devicetree/bindings/mtd/denali-nand.txt        |   39 +-
 drivers/mtd/nand/raw/denali.c                      | 1141 +++++++++++---------
 drivers/mtd/nand/raw/denali.h                      |  119 +-
 drivers/mtd/nand/raw/denali_dt.c                   |   98 +-
 drivers/mtd/nand/raw/denali_pci.c                  |   38 +-
 5 files changed, 852 insertions(+), 583 deletions(-)

-- 
2.7.4


^ permalink raw reply	[flat|nested] 40+ messages in thread

* [PATCH v3 0/9] mtd: rawnand: denali: exec_op(), controller/chip separation, and cleanups
@ 2019-03-12  8:44 ` Masahiro Yamada
  0 siblings, 0 replies; 40+ messages in thread
From: Masahiro Yamada @ 2019-03-12  8:44 UTC (permalink / raw)
  To: linux-mtd, Miquel Raynal
  Cc: Mark Rutland, devicetree, Masahiro Yamada, Richard Weinberger,
	Boris Brezillon, linux-kernel, Marek Vasut, Rob Herring,
	Brian Norris, David Woodhouse


I took time for the Denali driver to catch up with the latest framework.

 - switch over to exec_op() and remove legacy hooks

 - separate controller/chips

 - various cleanups

Major changes in v3:
  - Drop "mtd: rawnand: denali: use more precise timeout for NAND_OP_WAITRDT_INSTR"
    entirely according to the review comments in v2

  - Add comments to helpers in 2/9



Masahiro Yamada (9):
  mtd: rawnand: denali: use nand_chip pointer more for internal
    functions
  mtd: rawnand: denali: refactor syndrome layout handling for raw access
  mtd: rawnand: denali: remove unneeded casts in denali_{read,write}_pio
  mtd: rawnand: denali: switch over to ->exec_op() from legacy hooks
  mtd: rawnand: denali: use bool type instead of int where appropriate
  mtd: rawnand: denali_pci: rename goto labels
  mtd: rawnand: denali: decouple controller and NAND chips
  mtd: rawnand: denali: remove DENALI_NR_BANKS macro
  mtd: rawnand: denali: clean up coding style

 .../devicetree/bindings/mtd/denali-nand.txt        |   39 +-
 drivers/mtd/nand/raw/denali.c                      | 1141 +++++++++++---------
 drivers/mtd/nand/raw/denali.h                      |  119 +-
 drivers/mtd/nand/raw/denali_dt.c                   |   98 +-
 drivers/mtd/nand/raw/denali_pci.c                  |   38 +-
 5 files changed, 852 insertions(+), 583 deletions(-)

-- 
2.7.4


______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/

^ permalink raw reply	[flat|nested] 40+ messages in thread

* [PATCH v3 1/9] mtd: rawnand: denali: use nand_chip pointer more for internal functions
  2019-03-12  8:44 ` Masahiro Yamada
@ 2019-03-12  8:44   ` Masahiro Yamada
  -1 siblings, 0 replies; 40+ messages in thread
From: Masahiro Yamada @ 2019-03-12  8:44 UTC (permalink / raw)
  To: linux-mtd, Miquel Raynal
  Cc: Boris Brezillon, Masahiro Yamada, Brian Norris, linux-kernel,
	Marek Vasut, Richard Weinberger, David Woodhouse

With the recent refactoring, the NAND driver hooks now take a pointer
to nand_chip. Add to_denali() in order to convert (struct nand_chip *)
to (struct denali_nand_info *) directly. It is more useful than the
current mtd_to_denali().

I changed some helper functions to take (struct nand_chip *). This will
avoid pointer conversion back and forth, and ease further development.

Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
---

Changes in v3: None
Changes in v2: None

 drivers/mtd/nand/raw/denali.c | 57 ++++++++++++++++++++++++-------------------
 1 file changed, 32 insertions(+), 25 deletions(-)

diff --git a/drivers/mtd/nand/raw/denali.c b/drivers/mtd/nand/raw/denali.c
index 24aeafc..4ac1314 100644
--- a/drivers/mtd/nand/raw/denali.c
+++ b/drivers/mtd/nand/raw/denali.c
@@ -47,6 +47,11 @@ static inline struct denali_nand_info *mtd_to_denali(struct mtd_info *mtd)
 	return container_of(mtd_to_nand(mtd), struct denali_nand_info, nand);
 }
 
+static struct denali_nand_info *to_denali(struct nand_chip *chip)
+{
+	return container_of(chip, struct denali_nand_info, nand);
+}
+
 /*
  * Direct Addressing - the slave address forms the control information (command
  * type, bank, block, and page address).  The slave data is the actual data to
@@ -282,12 +287,12 @@ static void denali_cmd_ctrl(struct nand_chip *chip, int dat, unsigned int ctrl)
 	denali->host_write(denali, DENALI_BANK(denali) | type, dat);
 }
 
-static int denali_check_erased_page(struct mtd_info *mtd,
-				    struct nand_chip *chip, uint8_t *buf,
+static int denali_check_erased_page(struct nand_chip *chip,
+				    struct denali_nand_info *denali, u8 *buf,
 				    unsigned long uncor_ecc_flags,
 				    unsigned int max_bitflips)
 {
-	struct denali_nand_info *denali = mtd_to_denali(mtd);
+	struct mtd_ecc_stats *ecc_stats = &nand_to_mtd(chip)->ecc_stats;
 	uint8_t *ecc_code = chip->oob_poi + denali->oob_skip_bytes;
 	int ecc_steps = chip->ecc.steps;
 	int ecc_size = chip->ecc.size;
@@ -303,9 +308,9 @@ static int denali_check_erased_page(struct mtd_info *mtd,
 						  NULL, 0,
 						  chip->ecc.strength);
 		if (stat < 0) {
-			mtd->ecc_stats.failed++;
+			ecc_stats->failed++;
 		} else {
-			mtd->ecc_stats.corrected += stat;
+			ecc_stats->corrected += stat;
 			max_bitflips = max_t(unsigned int, max_bitflips, stat);
 		}
 
@@ -316,11 +321,11 @@ static int denali_check_erased_page(struct mtd_info *mtd,
 	return max_bitflips;
 }
 
-static int denali_hw_ecc_fixup(struct mtd_info *mtd,
+static int denali_hw_ecc_fixup(struct nand_chip *chip,
 			       struct denali_nand_info *denali,
 			       unsigned long *uncor_ecc_flags)
 {
-	struct nand_chip *chip = mtd_to_nand(mtd);
+	struct mtd_ecc_stats *ecc_stats = &nand_to_mtd(chip)->ecc_stats;
 	int bank = denali->active_bank;
 	uint32_t ecc_cor;
 	unsigned int max_bitflips;
@@ -346,16 +351,17 @@ static int denali_hw_ecc_fixup(struct mtd_info *mtd,
 	 * Unfortunately, we can not know the total number of corrected bits in
 	 * the page.  Increase the stats by max_bitflips. (compromised solution)
 	 */
-	mtd->ecc_stats.corrected += max_bitflips;
+	ecc_stats->corrected += max_bitflips;
 
 	return max_bitflips;
 }
 
-static int denali_sw_ecc_fixup(struct mtd_info *mtd,
+static int denali_sw_ecc_fixup(struct nand_chip *chip,
 			       struct denali_nand_info *denali,
 			       unsigned long *uncor_ecc_flags, uint8_t *buf)
 {
-	unsigned int ecc_size = denali->nand.ecc.size;
+	struct mtd_ecc_stats *ecc_stats = &nand_to_mtd(chip)->ecc_stats;
+	unsigned int ecc_size = chip->ecc.size;
 	unsigned int bitflips = 0;
 	unsigned int max_bitflips = 0;
 	uint32_t err_addr, err_cor_info;
@@ -404,7 +410,7 @@ static int denali_sw_ecc_fixup(struct mtd_info *mtd,
 			/* correct the ECC error */
 			flips_in_byte = hweight8(buf[offset] ^ err_cor_value);
 			buf[offset] ^= err_cor_value;
-			mtd->ecc_stats.corrected += flips_in_byte;
+			ecc_stats->corrected += flips_in_byte;
 			bitflips += flips_in_byte;
 
 			max_bitflips = max(max_bitflips, bitflips);
@@ -587,9 +593,11 @@ static int denali_dma_xfer(struct denali_nand_info *denali, void *buf,
 	return ret;
 }
 
-static int denali_data_xfer(struct denali_nand_info *denali, void *buf,
-			    size_t size, int page, int raw, int write)
+static int denali_data_xfer(struct nand_chip *chip, void *buf, size_t size,
+			    int page, int raw, int write)
 {
+	struct denali_nand_info *denali = to_denali(chip);
+
 	iowrite32(raw ? 0 : ECC_ENABLE__FLAG, denali->reg + ECC_ENABLE);
 	iowrite32(raw ? TRANSFER_SPARE_REG__FLAG : 0,
 		  denali->reg + TRANSFER_SPARE_REG);
@@ -678,7 +686,7 @@ static int denali_read_page_raw(struct nand_chip *chip, uint8_t *buf,
 	size_t size = writesize + oobsize;
 	int ret, i, pos, len;
 
-	ret = denali_data_xfer(denali, tmp_buf, size, page, 1, 0);
+	ret = denali_data_xfer(chip, tmp_buf, size, page, 1, 0);
 	if (ret)
 		return ret;
 
@@ -766,14 +774,14 @@ static int denali_read_page(struct nand_chip *chip, uint8_t *buf,
 	int stat = 0;
 	int ret;
 
-	ret = denali_data_xfer(denali, buf, mtd->writesize, page, 0, 0);
+	ret = denali_data_xfer(chip, buf, mtd->writesize, page, 0, 0);
 	if (ret && ret != -EBADMSG)
 		return ret;
 
 	if (denali->caps & DENALI_CAP_HW_ECC_FIXUP)
-		stat = denali_hw_ecc_fixup(mtd, denali, &uncor_ecc_flags);
+		stat = denali_hw_ecc_fixup(chip, denali, &uncor_ecc_flags);
 	else if (ret == -EBADMSG)
-		stat = denali_sw_ecc_fixup(mtd, denali, &uncor_ecc_flags, buf);
+		stat = denali_sw_ecc_fixup(chip, denali, &uncor_ecc_flags, buf);
 
 	if (stat < 0)
 		return stat;
@@ -783,7 +791,7 @@ static int denali_read_page(struct nand_chip *chip, uint8_t *buf,
 		if (ret)
 			return ret;
 
-		stat = denali_check_erased_page(mtd, chip, buf,
+		stat = denali_check_erased_page(chip, denali, buf,
 						uncor_ecc_flags, stat);
 	}
 
@@ -866,17 +874,16 @@ static int denali_write_page_raw(struct nand_chip *chip, const uint8_t *buf,
 		memcpy(tmp_buf + size - len, oob, len);
 	}
 
-	return denali_data_xfer(denali, tmp_buf, size, page, 1, 1);
+	return denali_data_xfer(chip, tmp_buf, size, page, 1, 1);
 }
 
 static int denali_write_page(struct nand_chip *chip, const uint8_t *buf,
 			     int oob_required, int page)
 {
 	struct mtd_info *mtd = nand_to_mtd(chip);
-	struct denali_nand_info *denali = mtd_to_denali(mtd);
 
-	return denali_data_xfer(denali, (void *)buf, mtd->writesize,
-				page, 0, 1);
+	return denali_data_xfer(chip, (void *)buf, mtd->writesize, page,
+				0, 1);
 }
 
 static void denali_select_chip(struct nand_chip *chip, int cs)
@@ -1092,9 +1099,9 @@ static const struct mtd_ooblayout_ops denali_ooblayout_ops = {
 	.free = denali_ooblayout_free,
 };
 
-static int denali_multidev_fixup(struct denali_nand_info *denali)
+static int denali_multidev_fixup(struct nand_chip *chip)
 {
-	struct nand_chip *chip = &denali->nand;
+	struct denali_nand_info *denali = to_denali(chip);
 	struct mtd_info *mtd = nand_to_mtd(chip);
 
 	/*
@@ -1222,7 +1229,7 @@ static int denali_attach_chip(struct nand_chip *chip)
 	chip->ecc.read_oob = denali_read_oob;
 	chip->ecc.write_oob = denali_write_oob;
 
-	ret = denali_multidev_fixup(denali);
+	ret = denali_multidev_fixup(chip);
 	if (ret)
 		return ret;
 
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH v3 1/9] mtd: rawnand: denali: use nand_chip pointer more for internal functions
@ 2019-03-12  8:44   ` Masahiro Yamada
  0 siblings, 0 replies; 40+ messages in thread
From: Masahiro Yamada @ 2019-03-12  8:44 UTC (permalink / raw)
  To: linux-mtd, Miquel Raynal
  Cc: Masahiro Yamada, Richard Weinberger, Boris Brezillon,
	linux-kernel, Marek Vasut, Brian Norris, David Woodhouse

With the recent refactoring, the NAND driver hooks now take a pointer
to nand_chip. Add to_denali() in order to convert (struct nand_chip *)
to (struct denali_nand_info *) directly. It is more useful than the
current mtd_to_denali().

I changed some helper functions to take (struct nand_chip *). This will
avoid pointer conversion back and forth, and ease further development.

Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
---

Changes in v3: None
Changes in v2: None

 drivers/mtd/nand/raw/denali.c | 57 ++++++++++++++++++++++++-------------------
 1 file changed, 32 insertions(+), 25 deletions(-)

diff --git a/drivers/mtd/nand/raw/denali.c b/drivers/mtd/nand/raw/denali.c
index 24aeafc..4ac1314 100644
--- a/drivers/mtd/nand/raw/denali.c
+++ b/drivers/mtd/nand/raw/denali.c
@@ -47,6 +47,11 @@ static inline struct denali_nand_info *mtd_to_denali(struct mtd_info *mtd)
 	return container_of(mtd_to_nand(mtd), struct denali_nand_info, nand);
 }
 
+static struct denali_nand_info *to_denali(struct nand_chip *chip)
+{
+	return container_of(chip, struct denali_nand_info, nand);
+}
+
 /*
  * Direct Addressing - the slave address forms the control information (command
  * type, bank, block, and page address).  The slave data is the actual data to
@@ -282,12 +287,12 @@ static void denali_cmd_ctrl(struct nand_chip *chip, int dat, unsigned int ctrl)
 	denali->host_write(denali, DENALI_BANK(denali) | type, dat);
 }
 
-static int denali_check_erased_page(struct mtd_info *mtd,
-				    struct nand_chip *chip, uint8_t *buf,
+static int denali_check_erased_page(struct nand_chip *chip,
+				    struct denali_nand_info *denali, u8 *buf,
 				    unsigned long uncor_ecc_flags,
 				    unsigned int max_bitflips)
 {
-	struct denali_nand_info *denali = mtd_to_denali(mtd);
+	struct mtd_ecc_stats *ecc_stats = &nand_to_mtd(chip)->ecc_stats;
 	uint8_t *ecc_code = chip->oob_poi + denali->oob_skip_bytes;
 	int ecc_steps = chip->ecc.steps;
 	int ecc_size = chip->ecc.size;
@@ -303,9 +308,9 @@ static int denali_check_erased_page(struct mtd_info *mtd,
 						  NULL, 0,
 						  chip->ecc.strength);
 		if (stat < 0) {
-			mtd->ecc_stats.failed++;
+			ecc_stats->failed++;
 		} else {
-			mtd->ecc_stats.corrected += stat;
+			ecc_stats->corrected += stat;
 			max_bitflips = max_t(unsigned int, max_bitflips, stat);
 		}
 
@@ -316,11 +321,11 @@ static int denali_check_erased_page(struct mtd_info *mtd,
 	return max_bitflips;
 }
 
-static int denali_hw_ecc_fixup(struct mtd_info *mtd,
+static int denali_hw_ecc_fixup(struct nand_chip *chip,
 			       struct denali_nand_info *denali,
 			       unsigned long *uncor_ecc_flags)
 {
-	struct nand_chip *chip = mtd_to_nand(mtd);
+	struct mtd_ecc_stats *ecc_stats = &nand_to_mtd(chip)->ecc_stats;
 	int bank = denali->active_bank;
 	uint32_t ecc_cor;
 	unsigned int max_bitflips;
@@ -346,16 +351,17 @@ static int denali_hw_ecc_fixup(struct mtd_info *mtd,
 	 * Unfortunately, we can not know the total number of corrected bits in
 	 * the page.  Increase the stats by max_bitflips. (compromised solution)
 	 */
-	mtd->ecc_stats.corrected += max_bitflips;
+	ecc_stats->corrected += max_bitflips;
 
 	return max_bitflips;
 }
 
-static int denali_sw_ecc_fixup(struct mtd_info *mtd,
+static int denali_sw_ecc_fixup(struct nand_chip *chip,
 			       struct denali_nand_info *denali,
 			       unsigned long *uncor_ecc_flags, uint8_t *buf)
 {
-	unsigned int ecc_size = denali->nand.ecc.size;
+	struct mtd_ecc_stats *ecc_stats = &nand_to_mtd(chip)->ecc_stats;
+	unsigned int ecc_size = chip->ecc.size;
 	unsigned int bitflips = 0;
 	unsigned int max_bitflips = 0;
 	uint32_t err_addr, err_cor_info;
@@ -404,7 +410,7 @@ static int denali_sw_ecc_fixup(struct mtd_info *mtd,
 			/* correct the ECC error */
 			flips_in_byte = hweight8(buf[offset] ^ err_cor_value);
 			buf[offset] ^= err_cor_value;
-			mtd->ecc_stats.corrected += flips_in_byte;
+			ecc_stats->corrected += flips_in_byte;
 			bitflips += flips_in_byte;
 
 			max_bitflips = max(max_bitflips, bitflips);
@@ -587,9 +593,11 @@ static int denali_dma_xfer(struct denali_nand_info *denali, void *buf,
 	return ret;
 }
 
-static int denali_data_xfer(struct denali_nand_info *denali, void *buf,
-			    size_t size, int page, int raw, int write)
+static int denali_data_xfer(struct nand_chip *chip, void *buf, size_t size,
+			    int page, int raw, int write)
 {
+	struct denali_nand_info *denali = to_denali(chip);
+
 	iowrite32(raw ? 0 : ECC_ENABLE__FLAG, denali->reg + ECC_ENABLE);
 	iowrite32(raw ? TRANSFER_SPARE_REG__FLAG : 0,
 		  denali->reg + TRANSFER_SPARE_REG);
@@ -678,7 +686,7 @@ static int denali_read_page_raw(struct nand_chip *chip, uint8_t *buf,
 	size_t size = writesize + oobsize;
 	int ret, i, pos, len;
 
-	ret = denali_data_xfer(denali, tmp_buf, size, page, 1, 0);
+	ret = denali_data_xfer(chip, tmp_buf, size, page, 1, 0);
 	if (ret)
 		return ret;
 
@@ -766,14 +774,14 @@ static int denali_read_page(struct nand_chip *chip, uint8_t *buf,
 	int stat = 0;
 	int ret;
 
-	ret = denali_data_xfer(denali, buf, mtd->writesize, page, 0, 0);
+	ret = denali_data_xfer(chip, buf, mtd->writesize, page, 0, 0);
 	if (ret && ret != -EBADMSG)
 		return ret;
 
 	if (denali->caps & DENALI_CAP_HW_ECC_FIXUP)
-		stat = denali_hw_ecc_fixup(mtd, denali, &uncor_ecc_flags);
+		stat = denali_hw_ecc_fixup(chip, denali, &uncor_ecc_flags);
 	else if (ret == -EBADMSG)
-		stat = denali_sw_ecc_fixup(mtd, denali, &uncor_ecc_flags, buf);
+		stat = denali_sw_ecc_fixup(chip, denali, &uncor_ecc_flags, buf);
 
 	if (stat < 0)
 		return stat;
@@ -783,7 +791,7 @@ static int denali_read_page(struct nand_chip *chip, uint8_t *buf,
 		if (ret)
 			return ret;
 
-		stat = denali_check_erased_page(mtd, chip, buf,
+		stat = denali_check_erased_page(chip, denali, buf,
 						uncor_ecc_flags, stat);
 	}
 
@@ -866,17 +874,16 @@ static int denali_write_page_raw(struct nand_chip *chip, const uint8_t *buf,
 		memcpy(tmp_buf + size - len, oob, len);
 	}
 
-	return denali_data_xfer(denali, tmp_buf, size, page, 1, 1);
+	return denali_data_xfer(chip, tmp_buf, size, page, 1, 1);
 }
 
 static int denali_write_page(struct nand_chip *chip, const uint8_t *buf,
 			     int oob_required, int page)
 {
 	struct mtd_info *mtd = nand_to_mtd(chip);
-	struct denali_nand_info *denali = mtd_to_denali(mtd);
 
-	return denali_data_xfer(denali, (void *)buf, mtd->writesize,
-				page, 0, 1);
+	return denali_data_xfer(chip, (void *)buf, mtd->writesize, page,
+				0, 1);
 }
 
 static void denali_select_chip(struct nand_chip *chip, int cs)
@@ -1092,9 +1099,9 @@ static const struct mtd_ooblayout_ops denali_ooblayout_ops = {
 	.free = denali_ooblayout_free,
 };
 
-static int denali_multidev_fixup(struct denali_nand_info *denali)
+static int denali_multidev_fixup(struct nand_chip *chip)
 {
-	struct nand_chip *chip = &denali->nand;
+	struct denali_nand_info *denali = to_denali(chip);
 	struct mtd_info *mtd = nand_to_mtd(chip);
 
 	/*
@@ -1222,7 +1229,7 @@ static int denali_attach_chip(struct nand_chip *chip)
 	chip->ecc.read_oob = denali_read_oob;
 	chip->ecc.write_oob = denali_write_oob;
 
-	ret = denali_multidev_fixup(denali);
+	ret = denali_multidev_fixup(chip);
 	if (ret)
 		return ret;
 
-- 
2.7.4


______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/

^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH v3 2/9] mtd: rawnand: denali: refactor syndrome layout handling for raw access
  2019-03-12  8:44 ` Masahiro Yamada
@ 2019-03-12  8:44   ` Masahiro Yamada
  -1 siblings, 0 replies; 40+ messages in thread
From: Masahiro Yamada @ 2019-03-12  8:44 UTC (permalink / raw)
  To: linux-mtd, Miquel Raynal
  Cc: Boris Brezillon, Masahiro Yamada, Brian Norris, linux-kernel,
	Marek Vasut, Richard Weinberger, David Woodhouse

The Denali IP adopts the syndrome page layout (payload and ECC are
interleaved). The *_page_raw() and *_oob() callbacks are complicated
because they must hide the underlying layout used by the hardware,
and always return contiguous in-band and out-of-band data.

Currently, similar code is duplicated to reorganize the data layout.
For example, denali_read_page_raw() and denali_write_page_raw() look
almost the same.

The idea for refactoring is to split the code into two parts:
  [1] conversion of page layout
  [2] what to do at every ECC chunk boundary

For [1], I wrote denali_raw_payload_op() and denali_raw_oob_op().
They manipulate data for the Denali controller's specific page layout
of in-band, out-of-band, respectively.

The difference between write and read is just the operation at
ECC chunk boundaries. For example, denali_read_oob() calls
nand_change_read_column_op(), whereas denali_write_oob() calls
nand_change_write_column_op(). So, I implemented [2] as a callback
passed into [1].

Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
---

Changes in v3:
  - Add comments to denali_raw_payload_op() and denali_oob_payload_op()

Changes in v2: None

 drivers/mtd/nand/raw/denali.c | 380 +++++++++++++++++++++---------------------
 1 file changed, 189 insertions(+), 191 deletions(-)

diff --git a/drivers/mtd/nand/raw/denali.c b/drivers/mtd/nand/raw/denali.c
index 4ac1314..ebeedbd 100644
--- a/drivers/mtd/nand/raw/denali.c
+++ b/drivers/mtd/nand/raw/denali.c
@@ -608,159 +608,236 @@ static int denali_data_xfer(struct nand_chip *chip, void *buf, size_t size,
 		return denali_pio_xfer(denali, buf, size, page, write);
 }
 
-static void denali_oob_xfer(struct mtd_info *mtd, struct nand_chip *chip,
-			    int page, int write)
+typedef int denali_change_column_callback(void *buf, unsigned int offset,
+					  unsigned int len, void *priv);
+
+/**
+ * denali_raw_payload_op() - arrange the payload data for syndrome layout
+ *
+ * The NAND framework passes the payload and ECC separately, but the Denali ECC
+ * engine cannot handle it directly because it writes/reads page data in the
+ * syndrome layout (payload and ECC are interleaved). This helper is useful to
+ * convert the layout between the two formats.
+ *
+ * @chip: NAND chip structure
+ * @buf:  buffer passed from the NAND framework
+ * @cb:   callback invoked whenever the column address is changed
+ * @priv: private data passed to @cb
+ */
+static int denali_raw_payload_op(struct nand_chip *chip, void *buf,
+				 denali_change_column_callback *cb, void *priv)
 {
-	struct denali_nand_info *denali = mtd_to_denali(mtd);
+	struct denali_nand_info *denali = to_denali(chip);
+	struct mtd_info *mtd = nand_to_mtd(chip);
+	struct nand_ecc_ctrl *ecc = &chip->ecc;
+	int writesize = mtd->writesize;
+	int oob_skip = denali->oob_skip_bytes;
+	int ret, i, pos, len;
+
+	for (i = 0; i < ecc->steps; i++) {
+		pos = i * (ecc->size + ecc->bytes);
+		len = ecc->size;
+
+		if (pos >= writesize) {
+			pos += oob_skip;
+		} else if (pos + len > writesize) {
+			/* This chunk overwraps the BBM area. Must be split */
+			ret = cb(buf, pos, writesize - pos, priv);
+			if (ret)
+				return ret;
+
+			buf += writesize - pos;
+			len -= writesize - pos;
+			pos = writesize + oob_skip;
+		}
+
+		ret = cb(buf, pos, len, priv);
+		if (ret)
+			return ret;
+
+		buf += len;
+	}
+
+	return 0;
+}
+
+/**
+ * denali_raw_oob_op() - arrange the oob data for syndrome layout
+ *
+ * The NAND framework passes the payload and ECC separately, but the Denali ECC
+ * engine cannot handle it directly because it writes/reads page data in the
+ * syndrome layout (payload and ECC are interleaved). This helper is useful to
+ * convert the layout between the two formats.
+ *
+ * @chip: NAND chip structure
+ * @buf:  buffer passed from the NAND framework
+ * @cb:   callback invoked whenever the column address is changed
+ * @priv: private data passed to @cb
+ */
+static int denali_raw_oob_op(struct nand_chip *chip, void *buf,
+			     denali_change_column_callback *cb, void *priv)
+{
+	struct denali_nand_info *denali = to_denali(chip);
+	struct mtd_info *mtd = nand_to_mtd(chip);
+	struct nand_ecc_ctrl *ecc = &chip->ecc;
 	int writesize = mtd->writesize;
 	int oobsize = mtd->oobsize;
-	uint8_t *bufpoi = chip->oob_poi;
-	int ecc_steps = chip->ecc.steps;
-	int ecc_size = chip->ecc.size;
-	int ecc_bytes = chip->ecc.bytes;
 	int oob_skip = denali->oob_skip_bytes;
-	size_t size = writesize + oobsize;
-	int i, pos, len;
+	int ret, i, pos, len;
 
 	/* BBM at the beginning of the OOB area */
-	if (write)
-		nand_prog_page_begin_op(chip, page, writesize, bufpoi,
-					oob_skip);
-	else
-		nand_read_page_op(chip, page, writesize, bufpoi, oob_skip);
-	bufpoi += oob_skip;
+	ret = cb(buf, writesize, oob_skip, priv);
+	if (ret)
+		return ret;
 
-	/* OOB ECC */
-	for (i = 0; i < ecc_steps; i++) {
-		pos = ecc_size + i * (ecc_size + ecc_bytes);
-		len = ecc_bytes;
+	buf += oob_skip;
 
-		if (pos >= writesize)
-			pos += oob_skip;
-		else if (pos + len > writesize)
-			len = writesize - pos;
+	for (i = 0; i < ecc->steps; i++) {
+		pos = ecc->size + i * (ecc->size + ecc->bytes);
 
-		if (write)
-			nand_change_write_column_op(chip, pos, bufpoi, len,
-						    false);
+		if (i == ecc->steps - 1)
+			/* The last chunk includes OOB free */
+			len = writesize + oobsize - pos - oob_skip;
 		else
-			nand_change_read_column_op(chip, pos, bufpoi, len,
-						   false);
-		bufpoi += len;
-		if (len < ecc_bytes) {
-			len = ecc_bytes - len;
-			if (write)
-				nand_change_write_column_op(chip, writesize +
-							    oob_skip, bufpoi,
-							    len, false);
-			else
-				nand_change_read_column_op(chip, writesize +
-							   oob_skip, bufpoi,
-							   len, false);
-			bufpoi += len;
+			len = ecc->bytes;
+
+		if (pos >= writesize) {
+			pos += oob_skip;
+		} else if (pos + len > writesize) {
+			/* This chunk overwraps the BBM area. Must be split */
+			ret = cb(buf, pos, writesize - pos, priv);
+			if (ret)
+				return ret;
+
+			buf += writesize - pos;
+			len -= writesize - pos;
+			pos = writesize + oob_skip;
 		}
+
+		ret = cb(buf, pos, len, priv);
+		if (ret)
+			return ret;
+
+		buf += len;
 	}
 
-	/* OOB free */
-	len = oobsize - (bufpoi - chip->oob_poi);
-	if (write)
-		nand_change_write_column_op(chip, size - len, bufpoi, len,
-					    false);
-	else
-		nand_change_read_column_op(chip, size - len, bufpoi, len,
-					   false);
+	return 0;
+}
+
+static int denali_memcpy_in(void *buf, unsigned int offset, unsigned int len,
+			    void *priv)
+{
+	memcpy(buf, priv + offset, len);
+	return 0;
 }
 
 static int denali_read_page_raw(struct nand_chip *chip, uint8_t *buf,
 				int oob_required, int page)
 {
+	struct denali_nand_info *denali = to_denali(chip);
 	struct mtd_info *mtd = nand_to_mtd(chip);
-	struct denali_nand_info *denali = mtd_to_denali(mtd);
-	int writesize = mtd->writesize;
-	int oobsize = mtd->oobsize;
-	int ecc_steps = chip->ecc.steps;
-	int ecc_size = chip->ecc.size;
-	int ecc_bytes = chip->ecc.bytes;
 	void *tmp_buf = denali->buf;
-	int oob_skip = denali->oob_skip_bytes;
-	size_t size = writesize + oobsize;
-	int ret, i, pos, len;
+	size_t size = mtd->writesize + mtd->oobsize;
+	int ret;
+
+	if (!buf)
+		return -EINVAL;
 
 	ret = denali_data_xfer(chip, tmp_buf, size, page, 1, 0);
 	if (ret)
 		return ret;
 
-	/* Arrange the buffer for syndrome payload/ecc layout */
-	if (buf) {
-		for (i = 0; i < ecc_steps; i++) {
-			pos = i * (ecc_size + ecc_bytes);
-			len = ecc_size;
-
-			if (pos >= writesize)
-				pos += oob_skip;
-			else if (pos + len > writesize)
-				len = writesize - pos;
-
-			memcpy(buf, tmp_buf + pos, len);
-			buf += len;
-			if (len < ecc_size) {
-				len = ecc_size - len;
-				memcpy(buf, tmp_buf + writesize + oob_skip,
-				       len);
-				buf += len;
-			}
-		}
-	}
+	ret = denali_raw_payload_op(chip, buf, denali_memcpy_in, tmp_buf);
+	if (ret)
+		return ret;
 
 	if (oob_required) {
-		uint8_t *oob = chip->oob_poi;
-
-		/* BBM at the beginning of the OOB area */
-		memcpy(oob, tmp_buf + writesize, oob_skip);
-		oob += oob_skip;
-
-		/* OOB ECC */
-		for (i = 0; i < ecc_steps; i++) {
-			pos = ecc_size + i * (ecc_size + ecc_bytes);
-			len = ecc_bytes;
-
-			if (pos >= writesize)
-				pos += oob_skip;
-			else if (pos + len > writesize)
-				len = writesize - pos;
-
-			memcpy(oob, tmp_buf + pos, len);
-			oob += len;
-			if (len < ecc_bytes) {
-				len = ecc_bytes - len;
-				memcpy(oob, tmp_buf + writesize + oob_skip,
-				       len);
-				oob += len;
-			}
-		}
-
-		/* OOB free */
-		len = oobsize - (oob - chip->oob_poi);
-		memcpy(oob, tmp_buf + size - len, len);
+		ret = denali_raw_oob_op(chip, chip->oob_poi, denali_memcpy_in,
+					tmp_buf);
+		if (ret)
+			return ret;
 	}
 
 	return 0;
 }
 
-static int denali_read_oob(struct nand_chip *chip, int page)
+static int denali_memcpy_out(void *buf, unsigned int offset, unsigned int len,
+			     void *priv)
 {
+	memcpy(priv + offset, buf, len);
+	return 0;
+}
+
+static int denali_write_page_raw(struct nand_chip *chip, const uint8_t *buf,
+				 int oob_required, int page)
+{
+	struct denali_nand_info *denali = to_denali(chip);
 	struct mtd_info *mtd = nand_to_mtd(chip);
+	void *tmp_buf = denali->buf;
+	size_t size = mtd->writesize + mtd->oobsize;
+	int ret;
 
-	denali_oob_xfer(mtd, chip, page, 0);
+	if (!buf)
+		return -EINVAL;
 
-	return 0;
+	/*
+	 * Fill the buffer with 0xff first except the full page transfer.
+	 * This simplifies the logic.
+	 */
+	if (!oob_required)
+		memset(tmp_buf, 0xff, size);
+
+	ret = denali_raw_payload_op(chip, (void *)buf, denali_memcpy_out,
+				    tmp_buf);
+	if (ret)
+		return ret;
+
+	if (oob_required) {
+		ret = denali_raw_oob_op(chip, chip->oob_poi, denali_memcpy_out,
+					tmp_buf);
+		if (ret)
+			return ret;
+	}
+
+	return denali_data_xfer(chip, tmp_buf, size, page, 1, 1);
+}
+
+static int denali_change_read_column_op(void *buf, unsigned int offset,
+					unsigned int len, void *priv)
+{
+	return nand_change_read_column_op(priv, offset, buf, len, false);
+}
+
+static int denali_read_oob(struct nand_chip *chip, int page)
+{
+	int ret;
+
+	ret = nand_read_page_op(chip, page, 0, NULL, 0);
+	if (ret)
+		return ret;
+
+	return denali_raw_oob_op(chip, chip->oob_poi,
+				 denali_change_read_column_op, chip);
+}
+
+static int denali_change_write_column_op(void *buf, unsigned int offset,
+					 unsigned int len, void *priv)
+{
+	return nand_change_write_column_op(priv, offset, buf, len, false);
 }
 
 static int denali_write_oob(struct nand_chip *chip, int page)
 {
-	struct mtd_info *mtd = nand_to_mtd(chip);
+	int ret;
+
+	ret = nand_prog_page_begin_op(chip, page, 0, NULL, 0);
+	if (ret)
+		return ret;
 
-	denali_oob_xfer(mtd, chip, page, 1);
+	ret = denali_raw_oob_op(chip, chip->oob_poi,
+				denali_change_write_column_op, chip);
+	if (ret)
+		return ret;
 
 	return nand_prog_page_end_op(chip);
 }
@@ -798,85 +875,6 @@ static int denali_read_page(struct nand_chip *chip, uint8_t *buf,
 	return stat;
 }
 
-static int denali_write_page_raw(struct nand_chip *chip, const uint8_t *buf,
-				 int oob_required, int page)
-{
-	struct mtd_info *mtd = nand_to_mtd(chip);
-	struct denali_nand_info *denali = mtd_to_denali(mtd);
-	int writesize = mtd->writesize;
-	int oobsize = mtd->oobsize;
-	int ecc_steps = chip->ecc.steps;
-	int ecc_size = chip->ecc.size;
-	int ecc_bytes = chip->ecc.bytes;
-	void *tmp_buf = denali->buf;
-	int oob_skip = denali->oob_skip_bytes;
-	size_t size = writesize + oobsize;
-	int i, pos, len;
-
-	/*
-	 * Fill the buffer with 0xff first except the full page transfer.
-	 * This simplifies the logic.
-	 */
-	if (!buf || !oob_required)
-		memset(tmp_buf, 0xff, size);
-
-	/* Arrange the buffer for syndrome payload/ecc layout */
-	if (buf) {
-		for (i = 0; i < ecc_steps; i++) {
-			pos = i * (ecc_size + ecc_bytes);
-			len = ecc_size;
-
-			if (pos >= writesize)
-				pos += oob_skip;
-			else if (pos + len > writesize)
-				len = writesize - pos;
-
-			memcpy(tmp_buf + pos, buf, len);
-			buf += len;
-			if (len < ecc_size) {
-				len = ecc_size - len;
-				memcpy(tmp_buf + writesize + oob_skip, buf,
-				       len);
-				buf += len;
-			}
-		}
-	}
-
-	if (oob_required) {
-		const uint8_t *oob = chip->oob_poi;
-
-		/* BBM at the beginning of the OOB area */
-		memcpy(tmp_buf + writesize, oob, oob_skip);
-		oob += oob_skip;
-
-		/* OOB ECC */
-		for (i = 0; i < ecc_steps; i++) {
-			pos = ecc_size + i * (ecc_size + ecc_bytes);
-			len = ecc_bytes;
-
-			if (pos >= writesize)
-				pos += oob_skip;
-			else if (pos + len > writesize)
-				len = writesize - pos;
-
-			memcpy(tmp_buf + pos, oob, len);
-			oob += len;
-			if (len < ecc_bytes) {
-				len = ecc_bytes - len;
-				memcpy(tmp_buf + writesize + oob_skip, oob,
-				       len);
-				oob += len;
-			}
-		}
-
-		/* OOB free */
-		len = oobsize - (oob - chip->oob_poi);
-		memcpy(tmp_buf + size - len, oob, len);
-	}
-
-	return denali_data_xfer(chip, tmp_buf, size, page, 1, 1);
-}
-
 static int denali_write_page(struct nand_chip *chip, const uint8_t *buf,
 			     int oob_required, int page)
 {
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH v3 2/9] mtd: rawnand: denali: refactor syndrome layout handling for raw access
@ 2019-03-12  8:44   ` Masahiro Yamada
  0 siblings, 0 replies; 40+ messages in thread
From: Masahiro Yamada @ 2019-03-12  8:44 UTC (permalink / raw)
  To: linux-mtd, Miquel Raynal
  Cc: Masahiro Yamada, Richard Weinberger, Boris Brezillon,
	linux-kernel, Marek Vasut, Brian Norris, David Woodhouse

The Denali IP adopts the syndrome page layout (payload and ECC are
interleaved). The *_page_raw() and *_oob() callbacks are complicated
because they must hide the underlying layout used by the hardware,
and always return contiguous in-band and out-of-band data.

Currently, similar code is duplicated to reorganize the data layout.
For example, denali_read_page_raw() and denali_write_page_raw() look
almost the same.

The idea for refactoring is to split the code into two parts:
  [1] conversion of page layout
  [2] what to do at every ECC chunk boundary

For [1], I wrote denali_raw_payload_op() and denali_raw_oob_op().
They manipulate data for the Denali controller's specific page layout
of in-band, out-of-band, respectively.

The difference between write and read is just the operation at
ECC chunk boundaries. For example, denali_read_oob() calls
nand_change_read_column_op(), whereas denali_write_oob() calls
nand_change_write_column_op(). So, I implemented [2] as a callback
passed into [1].

Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
---

Changes in v3:
  - Add comments to denali_raw_payload_op() and denali_oob_payload_op()

Changes in v2: None

 drivers/mtd/nand/raw/denali.c | 380 +++++++++++++++++++++---------------------
 1 file changed, 189 insertions(+), 191 deletions(-)

diff --git a/drivers/mtd/nand/raw/denali.c b/drivers/mtd/nand/raw/denali.c
index 4ac1314..ebeedbd 100644
--- a/drivers/mtd/nand/raw/denali.c
+++ b/drivers/mtd/nand/raw/denali.c
@@ -608,159 +608,236 @@ static int denali_data_xfer(struct nand_chip *chip, void *buf, size_t size,
 		return denali_pio_xfer(denali, buf, size, page, write);
 }
 
-static void denali_oob_xfer(struct mtd_info *mtd, struct nand_chip *chip,
-			    int page, int write)
+typedef int denali_change_column_callback(void *buf, unsigned int offset,
+					  unsigned int len, void *priv);
+
+/**
+ * denali_raw_payload_op() - arrange the payload data for syndrome layout
+ *
+ * The NAND framework passes the payload and ECC separately, but the Denali ECC
+ * engine cannot handle it directly because it writes/reads page data in the
+ * syndrome layout (payload and ECC are interleaved). This helper is useful to
+ * convert the layout between the two formats.
+ *
+ * @chip: NAND chip structure
+ * @buf:  buffer passed from the NAND framework
+ * @cb:   callback invoked whenever the column address is changed
+ * @priv: private data passed to @cb
+ */
+static int denali_raw_payload_op(struct nand_chip *chip, void *buf,
+				 denali_change_column_callback *cb, void *priv)
 {
-	struct denali_nand_info *denali = mtd_to_denali(mtd);
+	struct denali_nand_info *denali = to_denali(chip);
+	struct mtd_info *mtd = nand_to_mtd(chip);
+	struct nand_ecc_ctrl *ecc = &chip->ecc;
+	int writesize = mtd->writesize;
+	int oob_skip = denali->oob_skip_bytes;
+	int ret, i, pos, len;
+
+	for (i = 0; i < ecc->steps; i++) {
+		pos = i * (ecc->size + ecc->bytes);
+		len = ecc->size;
+
+		if (pos >= writesize) {
+			pos += oob_skip;
+		} else if (pos + len > writesize) {
+			/* This chunk overwraps the BBM area. Must be split */
+			ret = cb(buf, pos, writesize - pos, priv);
+			if (ret)
+				return ret;
+
+			buf += writesize - pos;
+			len -= writesize - pos;
+			pos = writesize + oob_skip;
+		}
+
+		ret = cb(buf, pos, len, priv);
+		if (ret)
+			return ret;
+
+		buf += len;
+	}
+
+	return 0;
+}
+
+/**
+ * denali_raw_oob_op() - arrange the oob data for syndrome layout
+ *
+ * The NAND framework passes the payload and ECC separately, but the Denali ECC
+ * engine cannot handle it directly because it writes/reads page data in the
+ * syndrome layout (payload and ECC are interleaved). This helper is useful to
+ * convert the layout between the two formats.
+ *
+ * @chip: NAND chip structure
+ * @buf:  buffer passed from the NAND framework
+ * @cb:   callback invoked whenever the column address is changed
+ * @priv: private data passed to @cb
+ */
+static int denali_raw_oob_op(struct nand_chip *chip, void *buf,
+			     denali_change_column_callback *cb, void *priv)
+{
+	struct denali_nand_info *denali = to_denali(chip);
+	struct mtd_info *mtd = nand_to_mtd(chip);
+	struct nand_ecc_ctrl *ecc = &chip->ecc;
 	int writesize = mtd->writesize;
 	int oobsize = mtd->oobsize;
-	uint8_t *bufpoi = chip->oob_poi;
-	int ecc_steps = chip->ecc.steps;
-	int ecc_size = chip->ecc.size;
-	int ecc_bytes = chip->ecc.bytes;
 	int oob_skip = denali->oob_skip_bytes;
-	size_t size = writesize + oobsize;
-	int i, pos, len;
+	int ret, i, pos, len;
 
 	/* BBM at the beginning of the OOB area */
-	if (write)
-		nand_prog_page_begin_op(chip, page, writesize, bufpoi,
-					oob_skip);
-	else
-		nand_read_page_op(chip, page, writesize, bufpoi, oob_skip);
-	bufpoi += oob_skip;
+	ret = cb(buf, writesize, oob_skip, priv);
+	if (ret)
+		return ret;
 
-	/* OOB ECC */
-	for (i = 0; i < ecc_steps; i++) {
-		pos = ecc_size + i * (ecc_size + ecc_bytes);
-		len = ecc_bytes;
+	buf += oob_skip;
 
-		if (pos >= writesize)
-			pos += oob_skip;
-		else if (pos + len > writesize)
-			len = writesize - pos;
+	for (i = 0; i < ecc->steps; i++) {
+		pos = ecc->size + i * (ecc->size + ecc->bytes);
 
-		if (write)
-			nand_change_write_column_op(chip, pos, bufpoi, len,
-						    false);
+		if (i == ecc->steps - 1)
+			/* The last chunk includes OOB free */
+			len = writesize + oobsize - pos - oob_skip;
 		else
-			nand_change_read_column_op(chip, pos, bufpoi, len,
-						   false);
-		bufpoi += len;
-		if (len < ecc_bytes) {
-			len = ecc_bytes - len;
-			if (write)
-				nand_change_write_column_op(chip, writesize +
-							    oob_skip, bufpoi,
-							    len, false);
-			else
-				nand_change_read_column_op(chip, writesize +
-							   oob_skip, bufpoi,
-							   len, false);
-			bufpoi += len;
+			len = ecc->bytes;
+
+		if (pos >= writesize) {
+			pos += oob_skip;
+		} else if (pos + len > writesize) {
+			/* This chunk overwraps the BBM area. Must be split */
+			ret = cb(buf, pos, writesize - pos, priv);
+			if (ret)
+				return ret;
+
+			buf += writesize - pos;
+			len -= writesize - pos;
+			pos = writesize + oob_skip;
 		}
+
+		ret = cb(buf, pos, len, priv);
+		if (ret)
+			return ret;
+
+		buf += len;
 	}
 
-	/* OOB free */
-	len = oobsize - (bufpoi - chip->oob_poi);
-	if (write)
-		nand_change_write_column_op(chip, size - len, bufpoi, len,
-					    false);
-	else
-		nand_change_read_column_op(chip, size - len, bufpoi, len,
-					   false);
+	return 0;
+}
+
+static int denali_memcpy_in(void *buf, unsigned int offset, unsigned int len,
+			    void *priv)
+{
+	memcpy(buf, priv + offset, len);
+	return 0;
 }
 
 static int denali_read_page_raw(struct nand_chip *chip, uint8_t *buf,
 				int oob_required, int page)
 {
+	struct denali_nand_info *denali = to_denali(chip);
 	struct mtd_info *mtd = nand_to_mtd(chip);
-	struct denali_nand_info *denali = mtd_to_denali(mtd);
-	int writesize = mtd->writesize;
-	int oobsize = mtd->oobsize;
-	int ecc_steps = chip->ecc.steps;
-	int ecc_size = chip->ecc.size;
-	int ecc_bytes = chip->ecc.bytes;
 	void *tmp_buf = denali->buf;
-	int oob_skip = denali->oob_skip_bytes;
-	size_t size = writesize + oobsize;
-	int ret, i, pos, len;
+	size_t size = mtd->writesize + mtd->oobsize;
+	int ret;
+
+	if (!buf)
+		return -EINVAL;
 
 	ret = denali_data_xfer(chip, tmp_buf, size, page, 1, 0);
 	if (ret)
 		return ret;
 
-	/* Arrange the buffer for syndrome payload/ecc layout */
-	if (buf) {
-		for (i = 0; i < ecc_steps; i++) {
-			pos = i * (ecc_size + ecc_bytes);
-			len = ecc_size;
-
-			if (pos >= writesize)
-				pos += oob_skip;
-			else if (pos + len > writesize)
-				len = writesize - pos;
-
-			memcpy(buf, tmp_buf + pos, len);
-			buf += len;
-			if (len < ecc_size) {
-				len = ecc_size - len;
-				memcpy(buf, tmp_buf + writesize + oob_skip,
-				       len);
-				buf += len;
-			}
-		}
-	}
+	ret = denali_raw_payload_op(chip, buf, denali_memcpy_in, tmp_buf);
+	if (ret)
+		return ret;
 
 	if (oob_required) {
-		uint8_t *oob = chip->oob_poi;
-
-		/* BBM at the beginning of the OOB area */
-		memcpy(oob, tmp_buf + writesize, oob_skip);
-		oob += oob_skip;
-
-		/* OOB ECC */
-		for (i = 0; i < ecc_steps; i++) {
-			pos = ecc_size + i * (ecc_size + ecc_bytes);
-			len = ecc_bytes;
-
-			if (pos >= writesize)
-				pos += oob_skip;
-			else if (pos + len > writesize)
-				len = writesize - pos;
-
-			memcpy(oob, tmp_buf + pos, len);
-			oob += len;
-			if (len < ecc_bytes) {
-				len = ecc_bytes - len;
-				memcpy(oob, tmp_buf + writesize + oob_skip,
-				       len);
-				oob += len;
-			}
-		}
-
-		/* OOB free */
-		len = oobsize - (oob - chip->oob_poi);
-		memcpy(oob, tmp_buf + size - len, len);
+		ret = denali_raw_oob_op(chip, chip->oob_poi, denali_memcpy_in,
+					tmp_buf);
+		if (ret)
+			return ret;
 	}
 
 	return 0;
 }
 
-static int denali_read_oob(struct nand_chip *chip, int page)
+static int denali_memcpy_out(void *buf, unsigned int offset, unsigned int len,
+			     void *priv)
 {
+	memcpy(priv + offset, buf, len);
+	return 0;
+}
+
+static int denali_write_page_raw(struct nand_chip *chip, const uint8_t *buf,
+				 int oob_required, int page)
+{
+	struct denali_nand_info *denali = to_denali(chip);
 	struct mtd_info *mtd = nand_to_mtd(chip);
+	void *tmp_buf = denali->buf;
+	size_t size = mtd->writesize + mtd->oobsize;
+	int ret;
 
-	denali_oob_xfer(mtd, chip, page, 0);
+	if (!buf)
+		return -EINVAL;
 
-	return 0;
+	/*
+	 * Fill the buffer with 0xff first except the full page transfer.
+	 * This simplifies the logic.
+	 */
+	if (!oob_required)
+		memset(tmp_buf, 0xff, size);
+
+	ret = denali_raw_payload_op(chip, (void *)buf, denali_memcpy_out,
+				    tmp_buf);
+	if (ret)
+		return ret;
+
+	if (oob_required) {
+		ret = denali_raw_oob_op(chip, chip->oob_poi, denali_memcpy_out,
+					tmp_buf);
+		if (ret)
+			return ret;
+	}
+
+	return denali_data_xfer(chip, tmp_buf, size, page, 1, 1);
+}
+
+static int denali_change_read_column_op(void *buf, unsigned int offset,
+					unsigned int len, void *priv)
+{
+	return nand_change_read_column_op(priv, offset, buf, len, false);
+}
+
+static int denali_read_oob(struct nand_chip *chip, int page)
+{
+	int ret;
+
+	ret = nand_read_page_op(chip, page, 0, NULL, 0);
+	if (ret)
+		return ret;
+
+	return denali_raw_oob_op(chip, chip->oob_poi,
+				 denali_change_read_column_op, chip);
+}
+
+static int denali_change_write_column_op(void *buf, unsigned int offset,
+					 unsigned int len, void *priv)
+{
+	return nand_change_write_column_op(priv, offset, buf, len, false);
 }
 
 static int denali_write_oob(struct nand_chip *chip, int page)
 {
-	struct mtd_info *mtd = nand_to_mtd(chip);
+	int ret;
+
+	ret = nand_prog_page_begin_op(chip, page, 0, NULL, 0);
+	if (ret)
+		return ret;
 
-	denali_oob_xfer(mtd, chip, page, 1);
+	ret = denali_raw_oob_op(chip, chip->oob_poi,
+				denali_change_write_column_op, chip);
+	if (ret)
+		return ret;
 
 	return nand_prog_page_end_op(chip);
 }
@@ -798,85 +875,6 @@ static int denali_read_page(struct nand_chip *chip, uint8_t *buf,
 	return stat;
 }
 
-static int denali_write_page_raw(struct nand_chip *chip, const uint8_t *buf,
-				 int oob_required, int page)
-{
-	struct mtd_info *mtd = nand_to_mtd(chip);
-	struct denali_nand_info *denali = mtd_to_denali(mtd);
-	int writesize = mtd->writesize;
-	int oobsize = mtd->oobsize;
-	int ecc_steps = chip->ecc.steps;
-	int ecc_size = chip->ecc.size;
-	int ecc_bytes = chip->ecc.bytes;
-	void *tmp_buf = denali->buf;
-	int oob_skip = denali->oob_skip_bytes;
-	size_t size = writesize + oobsize;
-	int i, pos, len;
-
-	/*
-	 * Fill the buffer with 0xff first except the full page transfer.
-	 * This simplifies the logic.
-	 */
-	if (!buf || !oob_required)
-		memset(tmp_buf, 0xff, size);
-
-	/* Arrange the buffer for syndrome payload/ecc layout */
-	if (buf) {
-		for (i = 0; i < ecc_steps; i++) {
-			pos = i * (ecc_size + ecc_bytes);
-			len = ecc_size;
-
-			if (pos >= writesize)
-				pos += oob_skip;
-			else if (pos + len > writesize)
-				len = writesize - pos;
-
-			memcpy(tmp_buf + pos, buf, len);
-			buf += len;
-			if (len < ecc_size) {
-				len = ecc_size - len;
-				memcpy(tmp_buf + writesize + oob_skip, buf,
-				       len);
-				buf += len;
-			}
-		}
-	}
-
-	if (oob_required) {
-		const uint8_t *oob = chip->oob_poi;
-
-		/* BBM at the beginning of the OOB area */
-		memcpy(tmp_buf + writesize, oob, oob_skip);
-		oob += oob_skip;
-
-		/* OOB ECC */
-		for (i = 0; i < ecc_steps; i++) {
-			pos = ecc_size + i * (ecc_size + ecc_bytes);
-			len = ecc_bytes;
-
-			if (pos >= writesize)
-				pos += oob_skip;
-			else if (pos + len > writesize)
-				len = writesize - pos;
-
-			memcpy(tmp_buf + pos, oob, len);
-			oob += len;
-			if (len < ecc_bytes) {
-				len = ecc_bytes - len;
-				memcpy(tmp_buf + writesize + oob_skip, oob,
-				       len);
-				oob += len;
-			}
-		}
-
-		/* OOB free */
-		len = oobsize - (oob - chip->oob_poi);
-		memcpy(tmp_buf + size - len, oob, len);
-	}
-
-	return denali_data_xfer(chip, tmp_buf, size, page, 1, 1);
-}
-
 static int denali_write_page(struct nand_chip *chip, const uint8_t *buf,
 			     int oob_required, int page)
 {
-- 
2.7.4


______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/

^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH v3 3/9] mtd: rawnand: denali: remove unneeded casts in denali_{read,write}_pio
  2019-03-12  8:44 ` Masahiro Yamada
@ 2019-03-12  8:44   ` Masahiro Yamada
  -1 siblings, 0 replies; 40+ messages in thread
From: Masahiro Yamada @ 2019-03-12  8:44 UTC (permalink / raw)
  To: linux-mtd, Miquel Raynal
  Cc: Boris Brezillon, Masahiro Yamada, Brian Norris, linux-kernel,
	Marek Vasut, Richard Weinberger, David Woodhouse

Since (u32 *) can accept an opaque pointer, the explicit casting
from (void *) to (u32 *) is redundant. Change the function argument type
to remove the casts.

Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
---

Changes in v3: None
Changes in v2: None

 drivers/mtd/nand/raw/denali.c | 12 +++++-------
 1 file changed, 5 insertions(+), 7 deletions(-)

diff --git a/drivers/mtd/nand/raw/denali.c b/drivers/mtd/nand/raw/denali.c
index ebeedbd..d2040b7 100644
--- a/drivers/mtd/nand/raw/denali.c
+++ b/drivers/mtd/nand/raw/denali.c
@@ -478,11 +478,10 @@ static void denali_setup_dma32(struct denali_nand_info *denali,
 	denali->host_write(denali, mode | 0x14000, 0x2400);
 }
 
-static int denali_pio_read(struct denali_nand_info *denali, void *buf,
+static int denali_pio_read(struct denali_nand_info *denali, u32 *buf,
 			   size_t size, int page)
 {
 	u32 addr = DENALI_MAP01 | DENALI_BANK(denali) | page;
-	uint32_t *buf32 = (uint32_t *)buf;
 	uint32_t irq_status, ecc_err_mask;
 	int i;
 
@@ -494,7 +493,7 @@ static int denali_pio_read(struct denali_nand_info *denali, void *buf,
 	denali_reset_irq(denali);
 
 	for (i = 0; i < size / 4; i++)
-		*buf32++ = denali->host_read(denali, addr);
+		buf[i] = denali->host_read(denali, addr);
 
 	irq_status = denali_wait_for_irq(denali, INTR__PAGE_XFER_INC);
 	if (!(irq_status & INTR__PAGE_XFER_INC))
@@ -506,18 +505,17 @@ static int denali_pio_read(struct denali_nand_info *denali, void *buf,
 	return irq_status & ecc_err_mask ? -EBADMSG : 0;
 }
 
-static int denali_pio_write(struct denali_nand_info *denali,
-			    const void *buf, size_t size, int page)
+static int denali_pio_write(struct denali_nand_info *denali, const u32 *buf,
+			    size_t size, int page)
 {
 	u32 addr = DENALI_MAP01 | DENALI_BANK(denali) | page;
-	const uint32_t *buf32 = (uint32_t *)buf;
 	uint32_t irq_status;
 	int i;
 
 	denali_reset_irq(denali);
 
 	for (i = 0; i < size / 4; i++)
-		denali->host_write(denali, addr, *buf32++);
+		denali->host_write(denali, addr, buf[i]);
 
 	irq_status = denali_wait_for_irq(denali,
 				INTR__PROGRAM_COMP | INTR__PROGRAM_FAIL);
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH v3 3/9] mtd: rawnand: denali: remove unneeded casts in denali_{read, write}_pio
@ 2019-03-12  8:44   ` Masahiro Yamada
  0 siblings, 0 replies; 40+ messages in thread
From: Masahiro Yamada @ 2019-03-12  8:44 UTC (permalink / raw)
  To: linux-mtd, Miquel Raynal
  Cc: Masahiro Yamada, Richard Weinberger, Boris Brezillon,
	linux-kernel, Marek Vasut, Brian Norris, David Woodhouse

Since (u32 *) can accept an opaque pointer, the explicit casting
from (void *) to (u32 *) is redundant. Change the function argument type
to remove the casts.

Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
---

Changes in v3: None
Changes in v2: None

 drivers/mtd/nand/raw/denali.c | 12 +++++-------
 1 file changed, 5 insertions(+), 7 deletions(-)

diff --git a/drivers/mtd/nand/raw/denali.c b/drivers/mtd/nand/raw/denali.c
index ebeedbd..d2040b7 100644
--- a/drivers/mtd/nand/raw/denali.c
+++ b/drivers/mtd/nand/raw/denali.c
@@ -478,11 +478,10 @@ static void denali_setup_dma32(struct denali_nand_info *denali,
 	denali->host_write(denali, mode | 0x14000, 0x2400);
 }
 
-static int denali_pio_read(struct denali_nand_info *denali, void *buf,
+static int denali_pio_read(struct denali_nand_info *denali, u32 *buf,
 			   size_t size, int page)
 {
 	u32 addr = DENALI_MAP01 | DENALI_BANK(denali) | page;
-	uint32_t *buf32 = (uint32_t *)buf;
 	uint32_t irq_status, ecc_err_mask;
 	int i;
 
@@ -494,7 +493,7 @@ static int denali_pio_read(struct denali_nand_info *denali, void *buf,
 	denali_reset_irq(denali);
 
 	for (i = 0; i < size / 4; i++)
-		*buf32++ = denali->host_read(denali, addr);
+		buf[i] = denali->host_read(denali, addr);
 
 	irq_status = denali_wait_for_irq(denali, INTR__PAGE_XFER_INC);
 	if (!(irq_status & INTR__PAGE_XFER_INC))
@@ -506,18 +505,17 @@ static int denali_pio_read(struct denali_nand_info *denali, void *buf,
 	return irq_status & ecc_err_mask ? -EBADMSG : 0;
 }
 
-static int denali_pio_write(struct denali_nand_info *denali,
-			    const void *buf, size_t size, int page)
+static int denali_pio_write(struct denali_nand_info *denali, const u32 *buf,
+			    size_t size, int page)
 {
 	u32 addr = DENALI_MAP01 | DENALI_BANK(denali) | page;
-	const uint32_t *buf32 = (uint32_t *)buf;
 	uint32_t irq_status;
 	int i;
 
 	denali_reset_irq(denali);
 
 	for (i = 0; i < size / 4; i++)
-		denali->host_write(denali, addr, *buf32++);
+		denali->host_write(denali, addr, buf[i]);
 
 	irq_status = denali_wait_for_irq(denali,
 				INTR__PROGRAM_COMP | INTR__PROGRAM_FAIL);
-- 
2.7.4


______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/

^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH v3 4/9] mtd: rawnand: denali: switch over to ->exec_op() from legacy hooks
  2019-03-12  8:44 ` Masahiro Yamada
@ 2019-03-12  8:44   ` Masahiro Yamada
  -1 siblings, 0 replies; 40+ messages in thread
From: Masahiro Yamada @ 2019-03-12  8:44 UTC (permalink / raw)
  To: linux-mtd, Miquel Raynal
  Cc: Boris Brezillon, Masahiro Yamada, Brian Norris, linux-kernel,
	Marek Vasut, Richard Weinberger, David Woodhouse

Implement ->exec_op(), and remove the deprecated hooks.

Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
---

Changes in v3:
  - Fix byte-swap in denali_exec_in16()

Changes in v2: None

 drivers/mtd/nand/raw/denali.c | 234 +++++++++++++++++++++++-------------------
 1 file changed, 126 insertions(+), 108 deletions(-)

diff --git a/drivers/mtd/nand/raw/denali.c b/drivers/mtd/nand/raw/denali.c
index d2040b7..2c7dc9b 100644
--- a/drivers/mtd/nand/raw/denali.c
+++ b/drivers/mtd/nand/raw/denali.c
@@ -206,85 +206,11 @@ static uint32_t denali_wait_for_irq(struct denali_nand_info *denali,
 	return denali->irq_status;
 }
 
-static void denali_read_buf(struct nand_chip *chip, uint8_t *buf, int len)
+static void denali_select_target(struct nand_chip *chip, int cs)
 {
-	struct mtd_info *mtd = nand_to_mtd(chip);
-	struct denali_nand_info *denali = mtd_to_denali(mtd);
-	u32 addr = DENALI_MAP11_DATA | DENALI_BANK(denali);
-	int i;
-
-	for (i = 0; i < len; i++)
-		buf[i] = denali->host_read(denali, addr);
-}
-
-static void denali_write_buf(struct nand_chip *chip, const uint8_t *buf,
-			     int len)
-{
-	struct denali_nand_info *denali = mtd_to_denali(nand_to_mtd(chip));
-	u32 addr = DENALI_MAP11_DATA | DENALI_BANK(denali);
-	int i;
-
-	for (i = 0; i < len; i++)
-		denali->host_write(denali, addr, buf[i]);
-}
-
-static void denali_read_buf16(struct nand_chip *chip, uint8_t *buf, int len)
-{
-	struct denali_nand_info *denali = mtd_to_denali(nand_to_mtd(chip));
-	u32 addr = DENALI_MAP11_DATA | DENALI_BANK(denali);
-	uint16_t *buf16 = (uint16_t *)buf;
-	int i;
-
-	for (i = 0; i < len / 2; i++)
-		buf16[i] = denali->host_read(denali, addr);
-}
-
-static void denali_write_buf16(struct nand_chip *chip, const uint8_t *buf,
-			       int len)
-{
-	struct denali_nand_info *denali = mtd_to_denali(nand_to_mtd(chip));
-	u32 addr = DENALI_MAP11_DATA | DENALI_BANK(denali);
-	const uint16_t *buf16 = (const uint16_t *)buf;
-	int i;
-
-	for (i = 0; i < len / 2; i++)
-		denali->host_write(denali, addr, buf16[i]);
-}
-
-static uint8_t denali_read_byte(struct nand_chip *chip)
-{
-	uint8_t byte;
-
-	denali_read_buf(chip, &byte, 1);
-
-	return byte;
-}
-
-static void denali_write_byte(struct nand_chip *chip, uint8_t byte)
-{
-	denali_write_buf(chip, &byte, 1);
-}
-
-static void denali_cmd_ctrl(struct nand_chip *chip, int dat, unsigned int ctrl)
-{
-	struct denali_nand_info *denali = mtd_to_denali(nand_to_mtd(chip));
-	uint32_t type;
-
-	if (ctrl & NAND_CLE)
-		type = DENALI_MAP11_CMD;
-	else if (ctrl & NAND_ALE)
-		type = DENALI_MAP11_ADDR;
-	else
-		return;
-
-	/*
-	 * Some commands are followed by chip->legacy.waitfunc.
-	 * irq_status must be cleared here to catch the R/B# interrupt later.
-	 */
-	if (ctrl & NAND_CTRL_CHANGE)
-		denali_reset_irq(denali);
+	struct denali_nand_info *denali = to_denali(chip);
 
-	denali->host_write(denali, DENALI_BANK(denali) | type, dat);
+	denali->active_bank = cs;
 }
 
 static int denali_check_erased_page(struct nand_chip *chip,
@@ -596,6 +522,8 @@ static int denali_data_xfer(struct nand_chip *chip, void *buf, size_t size,
 {
 	struct denali_nand_info *denali = to_denali(chip);
 
+	denali_select_target(chip, chip->cur_cs);
+
 	iowrite32(raw ? 0 : ECC_ENABLE__FLAG, denali->reg + ECC_ENABLE);
 	iowrite32(raw ? TRANSFER_SPARE_REG__FLAG : 0,
 		  denali->reg + TRANSFER_SPARE_REG);
@@ -882,24 +810,6 @@ static int denali_write_page(struct nand_chip *chip, const uint8_t *buf,
 				0, 1);
 }
 
-static void denali_select_chip(struct nand_chip *chip, int cs)
-{
-	struct denali_nand_info *denali = mtd_to_denali(nand_to_mtd(chip));
-
-	denali->active_bank = cs;
-}
-
-static int denali_waitfunc(struct nand_chip *chip)
-{
-	struct denali_nand_info *denali = mtd_to_denali(nand_to_mtd(chip));
-	uint32_t irq_status;
-
-	/* R/B# pin transitioned from low to high? */
-	irq_status = denali_wait_for_irq(denali, INTR__INT_ACT);
-
-	return irq_status & INTR__INT_ACT ? 0 : NAND_STATUS_FAIL;
-}
-
 static int denali_setup_data_interface(struct nand_chip *chip, int chipnr,
 				       const struct nand_data_interface *conf)
 {
@@ -1211,13 +1121,6 @@ static int denali_attach_chip(struct nand_chip *chip)
 
 	mtd_set_ooblayout(mtd, &denali_ooblayout_ops);
 
-	if (chip->options & NAND_BUSWIDTH_16) {
-		chip->legacy.read_buf = denali_read_buf16;
-		chip->legacy.write_buf = denali_write_buf16;
-	} else {
-		chip->legacy.read_buf = denali_read_buf;
-		chip->legacy.write_buf = denali_write_buf;
-	}
 	chip->ecc.read_page = denali_read_page;
 	chip->ecc.read_page_raw = denali_read_page_raw;
 	chip->ecc.write_page = denali_write_page;
@@ -1249,9 +1152,130 @@ static void denali_detach_chip(struct nand_chip *chip)
 	kfree(denali->buf);
 }
 
+static void denali_exec_in8(struct denali_nand_info *denali, u32 type,
+			    u8 *buf, unsigned int len)
+{
+	int i;
+
+	for (i = 0; i < len; i++)
+		buf[i] = denali->host_read(denali, type | DENALI_BANK(denali));
+}
+
+static void denali_exec_in16(struct denali_nand_info *denali, u32 type,
+			     u8 *buf, unsigned int len)
+{
+	u32 data;
+	int i;
+
+	for (i = 0; i < len; i += 2) {
+		data = denali->host_read(denali, type | DENALI_BANK(denali));
+		/* bit 31:24 and 15:8 are used for DDR */
+		buf[i] = data;
+		buf[i + 1] = data >> 16;
+	}
+}
+
+static void denali_exec_out8(struct denali_nand_info *denali, u32 type,
+			     const u8 *buf, unsigned int len)
+{
+	int i;
+
+	for (i = 0; i < len; i++)
+		denali->host_write(denali, type | DENALI_BANK(denali), buf[i]);
+}
+
+static void denali_exec_out16(struct denali_nand_info *denali, u32 type,
+			      const u8 *buf, unsigned int len)
+{
+	int i;
+
+	for (i = 0; i < len; i += 2)
+		denali->host_write(denali, type | DENALI_BANK(denali),
+				   buf[i + 1] << 16 | buf[i]);
+}
+
+static int denali_exec_waitrdy(struct denali_nand_info *denali)
+{
+	u32 irq_stat;
+
+	/* R/B# pin transitioned from low to high? */
+	irq_stat = denali_wait_for_irq(denali, INTR__INT_ACT);
+
+	/* Just in case nand_operation has multiple NAND_OP_WAITRDY_INSTR. */
+	denali_reset_irq(denali);
+
+	return irq_stat & INTR__INT_ACT ? 0 : -EIO;
+}
+
+static int denali_exec_instr(struct nand_chip *chip,
+			     const struct nand_op_instr *instr)
+{
+	struct denali_nand_info *denali = to_denali(chip);
+	bool width16 = chip->options & NAND_BUSWIDTH_16;
+
+	switch (instr->type) {
+	case NAND_OP_CMD_INSTR:
+		denali_exec_out8(denali, DENALI_MAP11_CMD,
+				 &instr->ctx.cmd.opcode, 1);
+		return 0;
+	case NAND_OP_ADDR_INSTR:
+		denali_exec_out8(denali, DENALI_MAP11_ADDR,
+				 instr->ctx.addr.addrs,
+				 instr->ctx.addr.naddrs);
+		return 0;
+	case NAND_OP_DATA_IN_INSTR:
+		(!instr->ctx.data.force_8bit && width16 ?
+		 denali_exec_in16 :
+		 denali_exec_in8)(denali, DENALI_MAP11_DATA,
+				  instr->ctx.data.buf.in,
+				  instr->ctx.data.len);
+		return 0;
+	case NAND_OP_DATA_OUT_INSTR:
+		(!instr->ctx.data.force_8bit && width16 ?
+		 denali_exec_out16 :
+		 denali_exec_out8)(denali, DENALI_MAP11_DATA,
+				   instr->ctx.data.buf.out,
+				   instr->ctx.data.len);
+		return 0;
+	case NAND_OP_WAITRDY_INSTR:
+		return denali_exec_waitrdy(denali);
+	default:
+		WARN_ONCE(1, "unsupported NAND instruction type: %d\n",
+			  instr->type);
+
+		return -EINVAL;
+	}
+}
+
+static int denali_exec_op(struct nand_chip *chip,
+			  const struct nand_operation *op, bool check_only)
+{
+	int i, ret;
+
+	if (check_only)
+		return 0;
+
+	denali_select_target(chip, op->cs);
+
+	/*
+	 * Some commands contain NAND_OP_WAITRDY_INSTR.
+	 * irq must be cleared here to catch the R/B# interrupt there.
+	 */
+	denali_reset_irq(to_denali(chip));
+
+	for (i = 0; i < op->ninstrs; i++) {
+		ret = denali_exec_instr(chip, &op->instrs[i]);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
 static const struct nand_controller_ops denali_controller_ops = {
 	.attach_chip = denali_attach_chip,
 	.detach_chip = denali_detach_chip,
+	.exec_op = denali_exec_op,
 	.setup_data_interface = denali_setup_data_interface,
 };
 
@@ -1286,12 +1310,6 @@ int denali_init(struct denali_nand_info *denali)
 	if (!mtd->name)
 		mtd->name = "denali-nand";
 
-	chip->legacy.select_chip = denali_select_chip;
-	chip->legacy.read_byte = denali_read_byte;
-	chip->legacy.write_byte = denali_write_byte;
-	chip->legacy.cmd_ctrl = denali_cmd_ctrl;
-	chip->legacy.waitfunc = denali_waitfunc;
-
 	if (features & FEATURES__INDEX_ADDR) {
 		denali->host_read = denali_indexed_read;
 		denali->host_write = denali_indexed_write;
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH v3 4/9] mtd: rawnand: denali: switch over to ->exec_op() from legacy hooks
@ 2019-03-12  8:44   ` Masahiro Yamada
  0 siblings, 0 replies; 40+ messages in thread
From: Masahiro Yamada @ 2019-03-12  8:44 UTC (permalink / raw)
  To: linux-mtd, Miquel Raynal
  Cc: Masahiro Yamada, Richard Weinberger, Boris Brezillon,
	linux-kernel, Marek Vasut, Brian Norris, David Woodhouse

Implement ->exec_op(), and remove the deprecated hooks.

Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
---

Changes in v3:
  - Fix byte-swap in denali_exec_in16()

Changes in v2: None

 drivers/mtd/nand/raw/denali.c | 234 +++++++++++++++++++++++-------------------
 1 file changed, 126 insertions(+), 108 deletions(-)

diff --git a/drivers/mtd/nand/raw/denali.c b/drivers/mtd/nand/raw/denali.c
index d2040b7..2c7dc9b 100644
--- a/drivers/mtd/nand/raw/denali.c
+++ b/drivers/mtd/nand/raw/denali.c
@@ -206,85 +206,11 @@ static uint32_t denali_wait_for_irq(struct denali_nand_info *denali,
 	return denali->irq_status;
 }
 
-static void denali_read_buf(struct nand_chip *chip, uint8_t *buf, int len)
+static void denali_select_target(struct nand_chip *chip, int cs)
 {
-	struct mtd_info *mtd = nand_to_mtd(chip);
-	struct denali_nand_info *denali = mtd_to_denali(mtd);
-	u32 addr = DENALI_MAP11_DATA | DENALI_BANK(denali);
-	int i;
-
-	for (i = 0; i < len; i++)
-		buf[i] = denali->host_read(denali, addr);
-}
-
-static void denali_write_buf(struct nand_chip *chip, const uint8_t *buf,
-			     int len)
-{
-	struct denali_nand_info *denali = mtd_to_denali(nand_to_mtd(chip));
-	u32 addr = DENALI_MAP11_DATA | DENALI_BANK(denali);
-	int i;
-
-	for (i = 0; i < len; i++)
-		denali->host_write(denali, addr, buf[i]);
-}
-
-static void denali_read_buf16(struct nand_chip *chip, uint8_t *buf, int len)
-{
-	struct denali_nand_info *denali = mtd_to_denali(nand_to_mtd(chip));
-	u32 addr = DENALI_MAP11_DATA | DENALI_BANK(denali);
-	uint16_t *buf16 = (uint16_t *)buf;
-	int i;
-
-	for (i = 0; i < len / 2; i++)
-		buf16[i] = denali->host_read(denali, addr);
-}
-
-static void denali_write_buf16(struct nand_chip *chip, const uint8_t *buf,
-			       int len)
-{
-	struct denali_nand_info *denali = mtd_to_denali(nand_to_mtd(chip));
-	u32 addr = DENALI_MAP11_DATA | DENALI_BANK(denali);
-	const uint16_t *buf16 = (const uint16_t *)buf;
-	int i;
-
-	for (i = 0; i < len / 2; i++)
-		denali->host_write(denali, addr, buf16[i]);
-}
-
-static uint8_t denali_read_byte(struct nand_chip *chip)
-{
-	uint8_t byte;
-
-	denali_read_buf(chip, &byte, 1);
-
-	return byte;
-}
-
-static void denali_write_byte(struct nand_chip *chip, uint8_t byte)
-{
-	denali_write_buf(chip, &byte, 1);
-}
-
-static void denali_cmd_ctrl(struct nand_chip *chip, int dat, unsigned int ctrl)
-{
-	struct denali_nand_info *denali = mtd_to_denali(nand_to_mtd(chip));
-	uint32_t type;
-
-	if (ctrl & NAND_CLE)
-		type = DENALI_MAP11_CMD;
-	else if (ctrl & NAND_ALE)
-		type = DENALI_MAP11_ADDR;
-	else
-		return;
-
-	/*
-	 * Some commands are followed by chip->legacy.waitfunc.
-	 * irq_status must be cleared here to catch the R/B# interrupt later.
-	 */
-	if (ctrl & NAND_CTRL_CHANGE)
-		denali_reset_irq(denali);
+	struct denali_nand_info *denali = to_denali(chip);
 
-	denali->host_write(denali, DENALI_BANK(denali) | type, dat);
+	denali->active_bank = cs;
 }
 
 static int denali_check_erased_page(struct nand_chip *chip,
@@ -596,6 +522,8 @@ static int denali_data_xfer(struct nand_chip *chip, void *buf, size_t size,
 {
 	struct denali_nand_info *denali = to_denali(chip);
 
+	denali_select_target(chip, chip->cur_cs);
+
 	iowrite32(raw ? 0 : ECC_ENABLE__FLAG, denali->reg + ECC_ENABLE);
 	iowrite32(raw ? TRANSFER_SPARE_REG__FLAG : 0,
 		  denali->reg + TRANSFER_SPARE_REG);
@@ -882,24 +810,6 @@ static int denali_write_page(struct nand_chip *chip, const uint8_t *buf,
 				0, 1);
 }
 
-static void denali_select_chip(struct nand_chip *chip, int cs)
-{
-	struct denali_nand_info *denali = mtd_to_denali(nand_to_mtd(chip));
-
-	denali->active_bank = cs;
-}
-
-static int denali_waitfunc(struct nand_chip *chip)
-{
-	struct denali_nand_info *denali = mtd_to_denali(nand_to_mtd(chip));
-	uint32_t irq_status;
-
-	/* R/B# pin transitioned from low to high? */
-	irq_status = denali_wait_for_irq(denali, INTR__INT_ACT);
-
-	return irq_status & INTR__INT_ACT ? 0 : NAND_STATUS_FAIL;
-}
-
 static int denali_setup_data_interface(struct nand_chip *chip, int chipnr,
 				       const struct nand_data_interface *conf)
 {
@@ -1211,13 +1121,6 @@ static int denali_attach_chip(struct nand_chip *chip)
 
 	mtd_set_ooblayout(mtd, &denali_ooblayout_ops);
 
-	if (chip->options & NAND_BUSWIDTH_16) {
-		chip->legacy.read_buf = denali_read_buf16;
-		chip->legacy.write_buf = denali_write_buf16;
-	} else {
-		chip->legacy.read_buf = denali_read_buf;
-		chip->legacy.write_buf = denali_write_buf;
-	}
 	chip->ecc.read_page = denali_read_page;
 	chip->ecc.read_page_raw = denali_read_page_raw;
 	chip->ecc.write_page = denali_write_page;
@@ -1249,9 +1152,130 @@ static void denali_detach_chip(struct nand_chip *chip)
 	kfree(denali->buf);
 }
 
+static void denali_exec_in8(struct denali_nand_info *denali, u32 type,
+			    u8 *buf, unsigned int len)
+{
+	int i;
+
+	for (i = 0; i < len; i++)
+		buf[i] = denali->host_read(denali, type | DENALI_BANK(denali));
+}
+
+static void denali_exec_in16(struct denali_nand_info *denali, u32 type,
+			     u8 *buf, unsigned int len)
+{
+	u32 data;
+	int i;
+
+	for (i = 0; i < len; i += 2) {
+		data = denali->host_read(denali, type | DENALI_BANK(denali));
+		/* bit 31:24 and 15:8 are used for DDR */
+		buf[i] = data;
+		buf[i + 1] = data >> 16;
+	}
+}
+
+static void denali_exec_out8(struct denali_nand_info *denali, u32 type,
+			     const u8 *buf, unsigned int len)
+{
+	int i;
+
+	for (i = 0; i < len; i++)
+		denali->host_write(denali, type | DENALI_BANK(denali), buf[i]);
+}
+
+static void denali_exec_out16(struct denali_nand_info *denali, u32 type,
+			      const u8 *buf, unsigned int len)
+{
+	int i;
+
+	for (i = 0; i < len; i += 2)
+		denali->host_write(denali, type | DENALI_BANK(denali),
+				   buf[i + 1] << 16 | buf[i]);
+}
+
+static int denali_exec_waitrdy(struct denali_nand_info *denali)
+{
+	u32 irq_stat;
+
+	/* R/B# pin transitioned from low to high? */
+	irq_stat = denali_wait_for_irq(denali, INTR__INT_ACT);
+
+	/* Just in case nand_operation has multiple NAND_OP_WAITRDY_INSTR. */
+	denali_reset_irq(denali);
+
+	return irq_stat & INTR__INT_ACT ? 0 : -EIO;
+}
+
+static int denali_exec_instr(struct nand_chip *chip,
+			     const struct nand_op_instr *instr)
+{
+	struct denali_nand_info *denali = to_denali(chip);
+	bool width16 = chip->options & NAND_BUSWIDTH_16;
+
+	switch (instr->type) {
+	case NAND_OP_CMD_INSTR:
+		denali_exec_out8(denali, DENALI_MAP11_CMD,
+				 &instr->ctx.cmd.opcode, 1);
+		return 0;
+	case NAND_OP_ADDR_INSTR:
+		denali_exec_out8(denali, DENALI_MAP11_ADDR,
+				 instr->ctx.addr.addrs,
+				 instr->ctx.addr.naddrs);
+		return 0;
+	case NAND_OP_DATA_IN_INSTR:
+		(!instr->ctx.data.force_8bit && width16 ?
+		 denali_exec_in16 :
+		 denali_exec_in8)(denali, DENALI_MAP11_DATA,
+				  instr->ctx.data.buf.in,
+				  instr->ctx.data.len);
+		return 0;
+	case NAND_OP_DATA_OUT_INSTR:
+		(!instr->ctx.data.force_8bit && width16 ?
+		 denali_exec_out16 :
+		 denali_exec_out8)(denali, DENALI_MAP11_DATA,
+				   instr->ctx.data.buf.out,
+				   instr->ctx.data.len);
+		return 0;
+	case NAND_OP_WAITRDY_INSTR:
+		return denali_exec_waitrdy(denali);
+	default:
+		WARN_ONCE(1, "unsupported NAND instruction type: %d\n",
+			  instr->type);
+
+		return -EINVAL;
+	}
+}
+
+static int denali_exec_op(struct nand_chip *chip,
+			  const struct nand_operation *op, bool check_only)
+{
+	int i, ret;
+
+	if (check_only)
+		return 0;
+
+	denali_select_target(chip, op->cs);
+
+	/*
+	 * Some commands contain NAND_OP_WAITRDY_INSTR.
+	 * irq must be cleared here to catch the R/B# interrupt there.
+	 */
+	denali_reset_irq(to_denali(chip));
+
+	for (i = 0; i < op->ninstrs; i++) {
+		ret = denali_exec_instr(chip, &op->instrs[i]);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
 static const struct nand_controller_ops denali_controller_ops = {
 	.attach_chip = denali_attach_chip,
 	.detach_chip = denali_detach_chip,
+	.exec_op = denali_exec_op,
 	.setup_data_interface = denali_setup_data_interface,
 };
 
@@ -1286,12 +1310,6 @@ int denali_init(struct denali_nand_info *denali)
 	if (!mtd->name)
 		mtd->name = "denali-nand";
 
-	chip->legacy.select_chip = denali_select_chip;
-	chip->legacy.read_byte = denali_read_byte;
-	chip->legacy.write_byte = denali_write_byte;
-	chip->legacy.cmd_ctrl = denali_cmd_ctrl;
-	chip->legacy.waitfunc = denali_waitfunc;
-
 	if (features & FEATURES__INDEX_ADDR) {
 		denali->host_read = denali_indexed_read;
 		denali->host_write = denali_indexed_write;
-- 
2.7.4


______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/

^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH v3 5/9] mtd: rawnand: denali: use bool type instead of int where appropriate
  2019-03-12  8:44 ` Masahiro Yamada
@ 2019-03-12  8:44   ` Masahiro Yamada
  -1 siblings, 0 replies; 40+ messages in thread
From: Masahiro Yamada @ 2019-03-12  8:44 UTC (permalink / raw)
  To: linux-mtd, Miquel Raynal
  Cc: Boris Brezillon, Masahiro Yamada, Brian Norris, linux-kernel,
	Marek Vasut, Richard Weinberger, David Woodhouse

Use 'bool' type for the following boolean parameters.

 - write (write or read?)
 - raw (raw access or not?)
 - dma_avail (DMA engine available or not?)

Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
---

Changes in v3: None
Changes in v2:
  - Use bool for dma_avail as well

 drivers/mtd/nand/raw/denali.c | 27 ++++++++++++++-------------
 drivers/mtd/nand/raw/denali.h |  4 ++--
 2 files changed, 16 insertions(+), 15 deletions(-)

diff --git a/drivers/mtd/nand/raw/denali.c b/drivers/mtd/nand/raw/denali.c
index 2c7dc9b..05fbe8f 100644
--- a/drivers/mtd/nand/raw/denali.c
+++ b/drivers/mtd/nand/raw/denali.c
@@ -357,7 +357,7 @@ static int denali_sw_ecc_fixup(struct nand_chip *chip,
 }
 
 static void denali_setup_dma64(struct denali_nand_info *denali,
-			       dma_addr_t dma_addr, int page, int write)
+			       dma_addr_t dma_addr, int page, bool write)
 {
 	uint32_t mode;
 	const int page_count = 1;
@@ -371,7 +371,8 @@ static void denali_setup_dma64(struct denali_nand_info *denali,
 	 *    burst len = 64 bytes, the number of pages
 	 */
 	denali->host_write(denali, mode,
-			   0x01002000 | (64 << 16) | (write << 8) | page_count);
+			   0x01002000 | (64 << 16) |
+			   (write ? BIT(8) : 0) | page_count);
 
 	/* 2. set memory low address */
 	denali->host_write(denali, mode, lower_32_bits(dma_addr));
@@ -381,7 +382,7 @@ static void denali_setup_dma64(struct denali_nand_info *denali,
 }
 
 static void denali_setup_dma32(struct denali_nand_info *denali,
-			       dma_addr_t dma_addr, int page, int write)
+			       dma_addr_t dma_addr, int page, bool write)
 {
 	uint32_t mode;
 	const int page_count = 1;
@@ -392,7 +393,7 @@ static void denali_setup_dma32(struct denali_nand_info *denali,
 
 	/* 1. setup transfer type and # of pages */
 	denali->host_write(denali, mode | page,
-			   0x2000 | (write << 8) | page_count);
+			   0x2000 | (write ? BIT(8) : 0) | page_count);
 
 	/* 2. set memory high address bits 23:8 */
 	denali->host_write(denali, mode | ((dma_addr >> 16) << 8), 0x2200);
@@ -452,7 +453,7 @@ static int denali_pio_write(struct denali_nand_info *denali, const u32 *buf,
 }
 
 static int denali_pio_xfer(struct denali_nand_info *denali, void *buf,
-			   size_t size, int page, int write)
+			   size_t size, int page, bool write)
 {
 	if (write)
 		return denali_pio_write(denali, buf, size, page);
@@ -461,7 +462,7 @@ static int denali_pio_xfer(struct denali_nand_info *denali, void *buf,
 }
 
 static int denali_dma_xfer(struct denali_nand_info *denali, void *buf,
-			   size_t size, int page, int write)
+			   size_t size, int page, bool write)
 {
 	dma_addr_t dma_addr;
 	uint32_t irq_mask, irq_status, ecc_err_mask;
@@ -518,7 +519,7 @@ static int denali_dma_xfer(struct denali_nand_info *denali, void *buf,
 }
 
 static int denali_data_xfer(struct nand_chip *chip, void *buf, size_t size,
-			    int page, int raw, int write)
+			    int page, bool raw, bool write)
 {
 	struct denali_nand_info *denali = to_denali(chip);
 
@@ -669,7 +670,7 @@ static int denali_read_page_raw(struct nand_chip *chip, uint8_t *buf,
 	if (!buf)
 		return -EINVAL;
 
-	ret = denali_data_xfer(chip, tmp_buf, size, page, 1, 0);
+	ret = denali_data_xfer(chip, tmp_buf, size, page, true, false);
 	if (ret)
 		return ret;
 
@@ -725,7 +726,7 @@ static int denali_write_page_raw(struct nand_chip *chip, const uint8_t *buf,
 			return ret;
 	}
 
-	return denali_data_xfer(chip, tmp_buf, size, page, 1, 1);
+	return denali_data_xfer(chip, tmp_buf, size, page, true, true);
 }
 
 static int denali_change_read_column_op(void *buf, unsigned int offset,
@@ -777,7 +778,7 @@ static int denali_read_page(struct nand_chip *chip, uint8_t *buf,
 	int stat = 0;
 	int ret;
 
-	ret = denali_data_xfer(chip, buf, mtd->writesize, page, 0, 0);
+	ret = denali_data_xfer(chip, buf, mtd->writesize, page, false, false);
 	if (ret && ret != -EBADMSG)
 		return ret;
 
@@ -807,7 +808,7 @@ static int denali_write_page(struct nand_chip *chip, const uint8_t *buf,
 	struct mtd_info *mtd = nand_to_mtd(chip);
 
 	return denali_data_xfer(chip, (void *)buf, mtd->writesize, page,
-				0, 1);
+				false, true);
 }
 
 static int denali_setup_data_interface(struct nand_chip *chip, int chipnr,
@@ -1063,7 +1064,7 @@ static int denali_attach_chip(struct nand_chip *chip)
 	int ret;
 
 	if (ioread32(denali->reg + FEATURES) & FEATURES__DMA)
-		denali->dma_avail = 1;
+		denali->dma_avail = true;
 
 	if (denali->dma_avail) {
 		int dma_bit = denali->caps & DENALI_CAP_DMA_64BIT ? 64 : 32;
@@ -1072,7 +1073,7 @@ static int denali_attach_chip(struct nand_chip *chip)
 		if (ret) {
 			dev_info(denali->dev,
 				 "Failed to set DMA mask. Disabling DMA.\n");
-			denali->dma_avail = 0;
+			denali->dma_avail = false;
 		}
 	}
 
diff --git a/drivers/mtd/nand/raw/denali.h b/drivers/mtd/nand/raw/denali.h
index c8c2620..8552b3f 100644
--- a/drivers/mtd/nand/raw/denali.h
+++ b/drivers/mtd/nand/raw/denali.h
@@ -304,7 +304,7 @@ struct denali_nand_info {
 	u32 irq_status;			/* interrupts that have happened */
 	int irq;
 	void *buf;			/* for syndrome layout conversion */
-	int dma_avail;			/* can support DMA? */
+	bool dma_avail;			/* can support DMA? */
 	int devs_per_cs;		/* devices connected in parallel */
 	int oob_skip_bytes;		/* number of bytes reserved for BBM */
 	int max_banks;
@@ -314,7 +314,7 @@ struct denali_nand_info {
 	u32 (*host_read)(struct denali_nand_info *denali, u32 addr);
 	void (*host_write)(struct denali_nand_info *denali, u32 addr, u32 data);
 	void (*setup_dma)(struct denali_nand_info *denali, dma_addr_t dma_addr,
-			  int page, int write);
+			  int page, bool write);
 };
 
 #define DENALI_CAP_HW_ECC_FIXUP			BIT(0)
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH v3 5/9] mtd: rawnand: denali: use bool type instead of int where appropriate
@ 2019-03-12  8:44   ` Masahiro Yamada
  0 siblings, 0 replies; 40+ messages in thread
From: Masahiro Yamada @ 2019-03-12  8:44 UTC (permalink / raw)
  To: linux-mtd, Miquel Raynal
  Cc: Masahiro Yamada, Richard Weinberger, Boris Brezillon,
	linux-kernel, Marek Vasut, Brian Norris, David Woodhouse

Use 'bool' type for the following boolean parameters.

 - write (write or read?)
 - raw (raw access or not?)
 - dma_avail (DMA engine available or not?)

Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
---

Changes in v3: None
Changes in v2:
  - Use bool for dma_avail as well

 drivers/mtd/nand/raw/denali.c | 27 ++++++++++++++-------------
 drivers/mtd/nand/raw/denali.h |  4 ++--
 2 files changed, 16 insertions(+), 15 deletions(-)

diff --git a/drivers/mtd/nand/raw/denali.c b/drivers/mtd/nand/raw/denali.c
index 2c7dc9b..05fbe8f 100644
--- a/drivers/mtd/nand/raw/denali.c
+++ b/drivers/mtd/nand/raw/denali.c
@@ -357,7 +357,7 @@ static int denali_sw_ecc_fixup(struct nand_chip *chip,
 }
 
 static void denali_setup_dma64(struct denali_nand_info *denali,
-			       dma_addr_t dma_addr, int page, int write)
+			       dma_addr_t dma_addr, int page, bool write)
 {
 	uint32_t mode;
 	const int page_count = 1;
@@ -371,7 +371,8 @@ static void denali_setup_dma64(struct denali_nand_info *denali,
 	 *    burst len = 64 bytes, the number of pages
 	 */
 	denali->host_write(denali, mode,
-			   0x01002000 | (64 << 16) | (write << 8) | page_count);
+			   0x01002000 | (64 << 16) |
+			   (write ? BIT(8) : 0) | page_count);
 
 	/* 2. set memory low address */
 	denali->host_write(denali, mode, lower_32_bits(dma_addr));
@@ -381,7 +382,7 @@ static void denali_setup_dma64(struct denali_nand_info *denali,
 }
 
 static void denali_setup_dma32(struct denali_nand_info *denali,
-			       dma_addr_t dma_addr, int page, int write)
+			       dma_addr_t dma_addr, int page, bool write)
 {
 	uint32_t mode;
 	const int page_count = 1;
@@ -392,7 +393,7 @@ static void denali_setup_dma32(struct denali_nand_info *denali,
 
 	/* 1. setup transfer type and # of pages */
 	denali->host_write(denali, mode | page,
-			   0x2000 | (write << 8) | page_count);
+			   0x2000 | (write ? BIT(8) : 0) | page_count);
 
 	/* 2. set memory high address bits 23:8 */
 	denali->host_write(denali, mode | ((dma_addr >> 16) << 8), 0x2200);
@@ -452,7 +453,7 @@ static int denali_pio_write(struct denali_nand_info *denali, const u32 *buf,
 }
 
 static int denali_pio_xfer(struct denali_nand_info *denali, void *buf,
-			   size_t size, int page, int write)
+			   size_t size, int page, bool write)
 {
 	if (write)
 		return denali_pio_write(denali, buf, size, page);
@@ -461,7 +462,7 @@ static int denali_pio_xfer(struct denali_nand_info *denali, void *buf,
 }
 
 static int denali_dma_xfer(struct denali_nand_info *denali, void *buf,
-			   size_t size, int page, int write)
+			   size_t size, int page, bool write)
 {
 	dma_addr_t dma_addr;
 	uint32_t irq_mask, irq_status, ecc_err_mask;
@@ -518,7 +519,7 @@ static int denali_dma_xfer(struct denali_nand_info *denali, void *buf,
 }
 
 static int denali_data_xfer(struct nand_chip *chip, void *buf, size_t size,
-			    int page, int raw, int write)
+			    int page, bool raw, bool write)
 {
 	struct denali_nand_info *denali = to_denali(chip);
 
@@ -669,7 +670,7 @@ static int denali_read_page_raw(struct nand_chip *chip, uint8_t *buf,
 	if (!buf)
 		return -EINVAL;
 
-	ret = denali_data_xfer(chip, tmp_buf, size, page, 1, 0);
+	ret = denali_data_xfer(chip, tmp_buf, size, page, true, false);
 	if (ret)
 		return ret;
 
@@ -725,7 +726,7 @@ static int denali_write_page_raw(struct nand_chip *chip, const uint8_t *buf,
 			return ret;
 	}
 
-	return denali_data_xfer(chip, tmp_buf, size, page, 1, 1);
+	return denali_data_xfer(chip, tmp_buf, size, page, true, true);
 }
 
 static int denali_change_read_column_op(void *buf, unsigned int offset,
@@ -777,7 +778,7 @@ static int denali_read_page(struct nand_chip *chip, uint8_t *buf,
 	int stat = 0;
 	int ret;
 
-	ret = denali_data_xfer(chip, buf, mtd->writesize, page, 0, 0);
+	ret = denali_data_xfer(chip, buf, mtd->writesize, page, false, false);
 	if (ret && ret != -EBADMSG)
 		return ret;
 
@@ -807,7 +808,7 @@ static int denali_write_page(struct nand_chip *chip, const uint8_t *buf,
 	struct mtd_info *mtd = nand_to_mtd(chip);
 
 	return denali_data_xfer(chip, (void *)buf, mtd->writesize, page,
-				0, 1);
+				false, true);
 }
 
 static int denali_setup_data_interface(struct nand_chip *chip, int chipnr,
@@ -1063,7 +1064,7 @@ static int denali_attach_chip(struct nand_chip *chip)
 	int ret;
 
 	if (ioread32(denali->reg + FEATURES) & FEATURES__DMA)
-		denali->dma_avail = 1;
+		denali->dma_avail = true;
 
 	if (denali->dma_avail) {
 		int dma_bit = denali->caps & DENALI_CAP_DMA_64BIT ? 64 : 32;
@@ -1072,7 +1073,7 @@ static int denali_attach_chip(struct nand_chip *chip)
 		if (ret) {
 			dev_info(denali->dev,
 				 "Failed to set DMA mask. Disabling DMA.\n");
-			denali->dma_avail = 0;
+			denali->dma_avail = false;
 		}
 	}
 
diff --git a/drivers/mtd/nand/raw/denali.h b/drivers/mtd/nand/raw/denali.h
index c8c2620..8552b3f 100644
--- a/drivers/mtd/nand/raw/denali.h
+++ b/drivers/mtd/nand/raw/denali.h
@@ -304,7 +304,7 @@ struct denali_nand_info {
 	u32 irq_status;			/* interrupts that have happened */
 	int irq;
 	void *buf;			/* for syndrome layout conversion */
-	int dma_avail;			/* can support DMA? */
+	bool dma_avail;			/* can support DMA? */
 	int devs_per_cs;		/* devices connected in parallel */
 	int oob_skip_bytes;		/* number of bytes reserved for BBM */
 	int max_banks;
@@ -314,7 +314,7 @@ struct denali_nand_info {
 	u32 (*host_read)(struct denali_nand_info *denali, u32 addr);
 	void (*host_write)(struct denali_nand_info *denali, u32 addr, u32 data);
 	void (*setup_dma)(struct denali_nand_info *denali, dma_addr_t dma_addr,
-			  int page, int write);
+			  int page, bool write);
 };
 
 #define DENALI_CAP_HW_ECC_FIXUP			BIT(0)
-- 
2.7.4


______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/

^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH v3 6/9] mtd: rawnand: denali_pci: rename goto labels
  2019-03-12  8:44 ` Masahiro Yamada
@ 2019-03-12  8:44   ` Masahiro Yamada
  -1 siblings, 0 replies; 40+ messages in thread
From: Masahiro Yamada @ 2019-03-12  8:44 UTC (permalink / raw)
  To: linux-mtd, Miquel Raynal
  Cc: Boris Brezillon, Masahiro Yamada, Brian Norris, linux-kernel,
	Marek Vasut, Richard Weinberger, David Woodhouse

As Documentation/process/coding-style.rst says, choose label names
which say what the goto does. The out_<action> label style is already
used in denali_dt.c. Rename likewise for denali_pci.c

Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
---

Changes in v3: None
Changes in v2: None

 drivers/mtd/nand/raw/denali_pci.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/drivers/mtd/nand/raw/denali_pci.c b/drivers/mtd/nand/raw/denali_pci.c
index 48e9ac5..02eb599 100644
--- a/drivers/mtd/nand/raw/denali_pci.c
+++ b/drivers/mtd/nand/raw/denali_pci.c
@@ -84,20 +84,20 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
 	if (!denali->host) {
 		dev_err(&dev->dev, "Spectra: ioremap_nocache failed!");
 		ret = -ENOMEM;
-		goto failed_remap_reg;
+		goto out_unmap_reg;
 	}
 
 	ret = denali_init(denali);
 	if (ret)
-		goto failed_remap_mem;
+		goto out_unmap_host;
 
 	pci_set_drvdata(dev, denali);
 
 	return 0;
 
-failed_remap_mem:
+out_unmap_host:
 	iounmap(denali->host);
-failed_remap_reg:
+out_unmap_reg:
 	iounmap(denali->reg);
 	return ret;
 }
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH v3 6/9] mtd: rawnand: denali_pci: rename goto labels
@ 2019-03-12  8:44   ` Masahiro Yamada
  0 siblings, 0 replies; 40+ messages in thread
From: Masahiro Yamada @ 2019-03-12  8:44 UTC (permalink / raw)
  To: linux-mtd, Miquel Raynal
  Cc: Masahiro Yamada, Richard Weinberger, Boris Brezillon,
	linux-kernel, Marek Vasut, Brian Norris, David Woodhouse

As Documentation/process/coding-style.rst says, choose label names
which say what the goto does. The out_<action> label style is already
used in denali_dt.c. Rename likewise for denali_pci.c

Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
---

Changes in v3: None
Changes in v2: None

 drivers/mtd/nand/raw/denali_pci.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/drivers/mtd/nand/raw/denali_pci.c b/drivers/mtd/nand/raw/denali_pci.c
index 48e9ac5..02eb599 100644
--- a/drivers/mtd/nand/raw/denali_pci.c
+++ b/drivers/mtd/nand/raw/denali_pci.c
@@ -84,20 +84,20 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
 	if (!denali->host) {
 		dev_err(&dev->dev, "Spectra: ioremap_nocache failed!");
 		ret = -ENOMEM;
-		goto failed_remap_reg;
+		goto out_unmap_reg;
 	}
 
 	ret = denali_init(denali);
 	if (ret)
-		goto failed_remap_mem;
+		goto out_unmap_host;
 
 	pci_set_drvdata(dev, denali);
 
 	return 0;
 
-failed_remap_mem:
+out_unmap_host:
 	iounmap(denali->host);
-failed_remap_reg:
+out_unmap_reg:
 	iounmap(denali->reg);
 	return ret;
 }
-- 
2.7.4


______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/

^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH v3 7/9] mtd: rawnand: denali: decouple controller and NAND chips
  2019-03-12  8:44 ` Masahiro Yamada
@ 2019-03-12  8:44   ` Masahiro Yamada
  -1 siblings, 0 replies; 40+ messages in thread
From: Masahiro Yamada @ 2019-03-12  8:44 UTC (permalink / raw)
  To: linux-mtd, Miquel Raynal
  Cc: Boris Brezillon, Masahiro Yamada, devicetree, Brian Norris,
	linux-kernel, Marek Vasut, Richard Weinberger, David Woodhouse,
	Rob Herring, Mark Rutland

Currently, this driver sticks to the legacy NAND model because it was
upstreamed before commit 2d472aba15ff ("mtd: nand: document the NAND
controller/NAND chip DT representation"). However, relying on the
dummy_controller is already deprecated.

Switch over to the new controller/chip representation.

The struct denali_nand_info has been split into denali_controller
and denali_chip, to contain the controller data, per-chip data,
respectively.

One problem is, this commit changes the DT binding. So, as always,
the backward compatibility must be taken into consideration.

In the new binding, the controller node expects

  #address-cells = <1>;
  #size-cells = <0>;

... since the child nodes represent NAND chips.

In the old binding, the controller node may have subnodes, but they
are MTD partitions.

The denali_dt_is_legacy_binding() exploits it to distinguish old/new
platforms.

Going forward, the old binding is only allowed for existing DT files.
I updated the binding document.

Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
Acked-by: Rob Herring <robh@kernel.org>
---

Changes in v3:
 - simplify mtd->name fallback
 - Add Rob's Ack

Changes in v2: None

 .../devicetree/bindings/mtd/denali-nand.txt        |  39 +-
 drivers/mtd/nand/raw/denali.c                      | 441 ++++++++++++---------
 drivers/mtd/nand/raw/denali.h                      | 117 +++++-
 drivers/mtd/nand/raw/denali_dt.c                   |  98 ++++-
 drivers/mtd/nand/raw/denali_pci.c                  |  30 +-
 5 files changed, 488 insertions(+), 237 deletions(-)

diff --git a/Documentation/devicetree/bindings/mtd/denali-nand.txt b/Documentation/devicetree/bindings/mtd/denali-nand.txt
index f33da87..de4ddff 100644
--- a/Documentation/devicetree/bindings/mtd/denali-nand.txt
+++ b/Documentation/devicetree/bindings/mtd/denali-nand.txt
@@ -7,34 +7,47 @@ Required properties:
       "socionext,uniphier-denali-nand-v5b"  - for Socionext UniPhier (v5b)
   - reg : should contain registers location and length for data and reg.
   - reg-names: Should contain the reg names "nand_data" and "denali_reg"
+  - #address-cells: should be 1. The cell encodes the chip select connection.
+  - #size-cells : should be 0.
   - interrupts : The interrupt number.
   - clocks: should contain phandle of the controller core clock, the bus
     interface clock, and the ECC circuit clock.
   - clock-names: should contain "nand", "nand_x", "ecc"
 
-Optional properties:
-  - nand-ecc-step-size: see nand.txt for details.  If present, the value must be
-      512        for "altr,socfpga-denali-nand"
-      1024       for "socionext,uniphier-denali-nand-v5a"
-      1024       for "socionext,uniphier-denali-nand-v5b"
-  - nand-ecc-strength: see nand.txt for details.  Valid values are:
-      8, 15      for "altr,socfpga-denali-nand"
-      8, 16, 24  for "socionext,uniphier-denali-nand-v5a"
-      8, 16      for "socionext,uniphier-denali-nand-v5b"
-  - nand-ecc-maximize: see nand.txt for details
-
-The device tree may optionally contain sub-nodes describing partitions of the
+Sub-nodes:
+  Sub-nodes represent available NAND chips.
+
+  Required properties:
+    - reg: should contain the bank ID of the controller to which each chip
+      select is connected.
+
+  Optional properties:
+    - nand-ecc-step-size: see nand.txt for details.  If present, the value must be
+        512        for "altr,socfpga-denali-nand"
+        1024       for "socionext,uniphier-denali-nand-v5a"
+        1024       for "socionext,uniphier-denali-nand-v5b"
+    - nand-ecc-strength: see nand.txt for details.  Valid values are:
+        8, 15      for "altr,socfpga-denali-nand"
+        8, 16, 24  for "socionext,uniphier-denali-nand-v5a"
+        8, 16      for "socionext,uniphier-denali-nand-v5b"
+    - nand-ecc-maximize: see nand.txt for details
+
+The chip nodes may optionally contain sub-nodes describing partitions of the
 address space. See partition.txt for more detail.
 
 Examples:
 
 nand: nand@ff900000 {
 	#address-cells = <1>;
-	#size-cells = <1>;
+	#size-cells = <0>;
 	compatible = "altr,socfpga-denali-nand";
 	reg = <0xff900000 0x20>, <0xffb80000 0x1000>;
 	reg-names = "nand_data", "denali_reg";
 	clocks = <&nand_clk>, <&nand_x_clk>, <&nand_ecc_clk>;
 	clock-names = "nand", "nand_x", "ecc";
 	interrupts = <0 144 4>;
+
+	nand@0 {
+		reg = <0>;
+	}
 };
diff --git a/drivers/mtd/nand/raw/denali.c b/drivers/mtd/nand/raw/denali.c
index 05fbe8f..0c47c56 100644
--- a/drivers/mtd/nand/raw/denali.c
+++ b/drivers/mtd/nand/raw/denali.c
@@ -3,7 +3,7 @@
  * NAND Flash Controller Device Driver
  * Copyright © 2009-2010, Intel Corporation and its suppliers.
  *
- * Copyright (c) 2017 Socionext Inc.
+ * Copyright (c) 2017-2019 Socionext Inc.
  *   Reworked by Masahiro Yamada <yamada.masahiro@socionext.com>
  */
 
@@ -42,14 +42,15 @@
 #define DENALI_INVALID_BANK	-1
 #define DENALI_NR_BANKS		4
 
-static inline struct denali_nand_info *mtd_to_denali(struct mtd_info *mtd)
+static struct denali_chip *to_denali_chip(struct nand_chip *chip)
 {
-	return container_of(mtd_to_nand(mtd), struct denali_nand_info, nand);
+	return container_of(chip, struct denali_chip, chip);
 }
 
-static struct denali_nand_info *to_denali(struct nand_chip *chip)
+static struct denali_controller *to_denali_controller(struct nand_chip *chip)
 {
-	return container_of(chip, struct denali_nand_info, nand);
+	return container_of(chip->controller, struct denali_controller,
+			    controller);
 }
 
 /*
@@ -57,12 +58,12 @@ static struct denali_nand_info *to_denali(struct nand_chip *chip)
  * type, bank, block, and page address).  The slave data is the actual data to
  * be transferred.  This mode requires 28 bits of address region allocated.
  */
-static u32 denali_direct_read(struct denali_nand_info *denali, u32 addr)
+static u32 denali_direct_read(struct denali_controller *denali, u32 addr)
 {
 	return ioread32(denali->host + addr);
 }
 
-static void denali_direct_write(struct denali_nand_info *denali, u32 addr,
+static void denali_direct_write(struct denali_controller *denali, u32 addr,
 				u32 data)
 {
 	iowrite32(data, denali->host + addr);
@@ -74,35 +75,20 @@ static void denali_direct_write(struct denali_nand_info *denali, u32 addr,
  * control information and transferred data are latched by the registers in
  * the translation module.
  */
-static u32 denali_indexed_read(struct denali_nand_info *denali, u32 addr)
+static u32 denali_indexed_read(struct denali_controller *denali, u32 addr)
 {
 	iowrite32(addr, denali->host + DENALI_INDEXED_CTRL);
 	return ioread32(denali->host + DENALI_INDEXED_DATA);
 }
 
-static void denali_indexed_write(struct denali_nand_info *denali, u32 addr,
+static void denali_indexed_write(struct denali_controller *denali, u32 addr,
 				 u32 data)
 {
 	iowrite32(addr, denali->host + DENALI_INDEXED_CTRL);
 	iowrite32(data, denali->host + DENALI_INDEXED_DATA);
 }
 
-/*
- * Use the configuration feature register to determine the maximum number of
- * banks that the hardware supports.
- */
-static void denali_detect_max_banks(struct denali_nand_info *denali)
-{
-	uint32_t features = ioread32(denali->reg + FEATURES);
-
-	denali->max_banks = 1 << FIELD_GET(FEATURES__N_BANKS, features);
-
-	/* the encoding changed from rev 5.0 to 5.1 */
-	if (denali->revision < 0x0501)
-		denali->max_banks <<= 1;
-}
-
-static void denali_enable_irq(struct denali_nand_info *denali)
+static void denali_enable_irq(struct denali_controller *denali)
 {
 	int i;
 
@@ -111,7 +97,7 @@ static void denali_enable_irq(struct denali_nand_info *denali)
 	iowrite32(GLOBAL_INT_EN_FLAG, denali->reg + GLOBAL_INT_ENABLE);
 }
 
-static void denali_disable_irq(struct denali_nand_info *denali)
+static void denali_disable_irq(struct denali_controller *denali)
 {
 	int i;
 
@@ -120,14 +106,14 @@ static void denali_disable_irq(struct denali_nand_info *denali)
 	iowrite32(0, denali->reg + GLOBAL_INT_ENABLE);
 }
 
-static void denali_clear_irq(struct denali_nand_info *denali,
+static void denali_clear_irq(struct denali_controller *denali,
 			     int bank, uint32_t irq_status)
 {
 	/* write one to clear bits */
 	iowrite32(irq_status, denali->reg + INTR_STATUS(bank));
 }
 
-static void denali_clear_irq_all(struct denali_nand_info *denali)
+static void denali_clear_irq_all(struct denali_controller *denali)
 {
 	int i;
 
@@ -137,7 +123,7 @@ static void denali_clear_irq_all(struct denali_nand_info *denali)
 
 static irqreturn_t denali_isr(int irq, void *dev_id)
 {
-	struct denali_nand_info *denali = dev_id;
+	struct denali_controller *denali = dev_id;
 	irqreturn_t ret = IRQ_NONE;
 	uint32_t irq_status;
 	int i;
@@ -165,7 +151,7 @@ static irqreturn_t denali_isr(int irq, void *dev_id)
 	return ret;
 }
 
-static void denali_reset_irq(struct denali_nand_info *denali)
+static void denali_reset_irq(struct denali_controller *denali)
 {
 	unsigned long flags;
 
@@ -175,8 +161,7 @@ static void denali_reset_irq(struct denali_nand_info *denali)
 	spin_unlock_irqrestore(&denali->irq_lock, flags);
 }
 
-static uint32_t denali_wait_for_irq(struct denali_nand_info *denali,
-				    uint32_t irq_mask)
+static u32 denali_wait_for_irq(struct denali_controller *denali, u32 irq_mask)
 {
 	unsigned long time_left, flags;
 	uint32_t irq_status;
@@ -208,13 +193,45 @@ static uint32_t denali_wait_for_irq(struct denali_nand_info *denali,
 
 static void denali_select_target(struct nand_chip *chip, int cs)
 {
-	struct denali_nand_info *denali = to_denali(chip);
+	struct denali_controller *denali = to_denali_controller(chip);
+	struct denali_chip_sel *sel = &to_denali_chip(chip)->sels[cs];
+	struct mtd_info *mtd = nand_to_mtd(chip);
+
+	denali->active_bank = sel->bank;
 
-	denali->active_bank = cs;
+	iowrite32(1 << (chip->phys_erase_shift - chip->page_shift),
+		  denali->reg + PAGES_PER_BLOCK);
+	iowrite32(chip->options & NAND_BUSWIDTH_16 ? 1 : 0,
+		  denali->reg + DEVICE_WIDTH);
+	iowrite32(mtd->writesize, denali->reg + DEVICE_MAIN_AREA_SIZE);
+	iowrite32(mtd->oobsize, denali->reg + DEVICE_SPARE_AREA_SIZE);
+	iowrite32(chip->options & NAND_ROW_ADDR_3 ?
+		  0 : TWO_ROW_ADDR_CYCLES__FLAG,
+		  denali->reg + TWO_ROW_ADDR_CYCLES);
+	iowrite32(FIELD_PREP(ECC_CORRECTION__ERASE_THRESHOLD, 1) |
+		  FIELD_PREP(ECC_CORRECTION__VALUE, chip->ecc.strength),
+		  denali->reg + ECC_CORRECTION);
+	iowrite32(chip->ecc.size, denali->reg + CFG_DATA_BLOCK_SIZE);
+	iowrite32(chip->ecc.size, denali->reg + CFG_LAST_DATA_BLOCK_SIZE);
+	iowrite32(chip->ecc.steps, denali->reg + CFG_NUM_DATA_BLOCKS);
+
+	if (chip->options & NAND_KEEP_TIMINGS)
+		return;
+
+	/* update timing registers unless NAND_KEEP_TIMINGS is set */
+	iowrite32(sel->hwhr2_and_we_2_re, denali->reg + TWHR2_AND_WE_2_RE);
+	iowrite32(sel->tcwaw_and_addr_2_data,
+		  denali->reg + TCWAW_AND_ADDR_2_DATA);
+	iowrite32(sel->re_2_we, denali->reg + RE_2_WE);
+	iowrite32(sel->acc_clks, denali->reg + ACC_CLKS);
+	iowrite32(sel->rdwr_en_lo_cnt, denali->reg + RDWR_EN_LO_CNT);
+	iowrite32(sel->rdwr_en_hi_cnt, denali->reg + RDWR_EN_HI_CNT);
+	iowrite32(sel->cs_setup_cnt, denali->reg + CS_SETUP_CNT);
+	iowrite32(sel->re_2_re, denali->reg + RE_2_RE);
 }
 
 static int denali_check_erased_page(struct nand_chip *chip,
-				    struct denali_nand_info *denali, u8 *buf,
+				    struct denali_controller *denali, u8 *buf,
 				    unsigned long uncor_ecc_flags,
 				    unsigned int max_bitflips)
 {
@@ -248,7 +265,7 @@ static int denali_check_erased_page(struct nand_chip *chip,
 }
 
 static int denali_hw_ecc_fixup(struct nand_chip *chip,
-			       struct denali_nand_info *denali,
+			       struct denali_controller *denali,
 			       unsigned long *uncor_ecc_flags)
 {
 	struct mtd_ecc_stats *ecc_stats = &nand_to_mtd(chip)->ecc_stats;
@@ -283,7 +300,7 @@ static int denali_hw_ecc_fixup(struct nand_chip *chip,
 }
 
 static int denali_sw_ecc_fixup(struct nand_chip *chip,
-			       struct denali_nand_info *denali,
+			       struct denali_controller *denali,
 			       unsigned long *uncor_ecc_flags, uint8_t *buf)
 {
 	struct mtd_ecc_stats *ecc_stats = &nand_to_mtd(chip)->ecc_stats;
@@ -356,7 +373,7 @@ static int denali_sw_ecc_fixup(struct nand_chip *chip,
 	return max_bitflips;
 }
 
-static void denali_setup_dma64(struct denali_nand_info *denali,
+static void denali_setup_dma64(struct denali_controller *denali,
 			       dma_addr_t dma_addr, int page, bool write)
 {
 	uint32_t mode;
@@ -381,7 +398,7 @@ static void denali_setup_dma64(struct denali_nand_info *denali,
 	denali->host_write(denali, mode, upper_32_bits(dma_addr));
 }
 
-static void denali_setup_dma32(struct denali_nand_info *denali,
+static void denali_setup_dma32(struct denali_controller *denali,
 			       dma_addr_t dma_addr, int page, bool write)
 {
 	uint32_t mode;
@@ -405,7 +422,7 @@ static void denali_setup_dma32(struct denali_nand_info *denali,
 	denali->host_write(denali, mode | 0x14000, 0x2400);
 }
 
-static int denali_pio_read(struct denali_nand_info *denali, u32 *buf,
+static int denali_pio_read(struct denali_controller *denali, u32 *buf,
 			   size_t size, int page)
 {
 	u32 addr = DENALI_MAP01 | DENALI_BANK(denali) | page;
@@ -432,7 +449,7 @@ static int denali_pio_read(struct denali_nand_info *denali, u32 *buf,
 	return irq_status & ecc_err_mask ? -EBADMSG : 0;
 }
 
-static int denali_pio_write(struct denali_nand_info *denali, const u32 *buf,
+static int denali_pio_write(struct denali_controller *denali, const u32 *buf,
 			    size_t size, int page)
 {
 	u32 addr = DENALI_MAP01 | DENALI_BANK(denali) | page;
@@ -452,7 +469,7 @@ static int denali_pio_write(struct denali_nand_info *denali, const u32 *buf,
 	return 0;
 }
 
-static int denali_pio_xfer(struct denali_nand_info *denali, void *buf,
+static int denali_pio_xfer(struct denali_controller *denali, void *buf,
 			   size_t size, int page, bool write)
 {
 	if (write)
@@ -461,7 +478,7 @@ static int denali_pio_xfer(struct denali_nand_info *denali, void *buf,
 		return denali_pio_read(denali, buf, size, page);
 }
 
-static int denali_dma_xfer(struct denali_nand_info *denali, void *buf,
+static int denali_dma_xfer(struct denali_controller *denali, void *buf,
 			   size_t size, int page, bool write)
 {
 	dma_addr_t dma_addr;
@@ -521,7 +538,7 @@ static int denali_dma_xfer(struct denali_nand_info *denali, void *buf,
 static int denali_data_xfer(struct nand_chip *chip, void *buf, size_t size,
 			    int page, bool raw, bool write)
 {
-	struct denali_nand_info *denali = to_denali(chip);
+	struct denali_controller *denali = to_denali_controller(chip);
 
 	denali_select_target(chip, chip->cur_cs);
 
@@ -554,7 +571,7 @@ typedef int denali_change_column_callback(void *buf, unsigned int offset,
 static int denali_raw_payload_op(struct nand_chip *chip, void *buf,
 				 denali_change_column_callback *cb, void *priv)
 {
-	struct denali_nand_info *denali = to_denali(chip);
+	struct denali_controller *denali = to_denali_controller(chip);
 	struct mtd_info *mtd = nand_to_mtd(chip);
 	struct nand_ecc_ctrl *ecc = &chip->ecc;
 	int writesize = mtd->writesize;
@@ -604,7 +621,7 @@ static int denali_raw_payload_op(struct nand_chip *chip, void *buf,
 static int denali_raw_oob_op(struct nand_chip *chip, void *buf,
 			     denali_change_column_callback *cb, void *priv)
 {
-	struct denali_nand_info *denali = to_denali(chip);
+	struct denali_controller *denali = to_denali_controller(chip);
 	struct mtd_info *mtd = nand_to_mtd(chip);
 	struct nand_ecc_ctrl *ecc = &chip->ecc;
 	int writesize = mtd->writesize;
@@ -661,9 +678,9 @@ static int denali_memcpy_in(void *buf, unsigned int offset, unsigned int len,
 static int denali_read_page_raw(struct nand_chip *chip, uint8_t *buf,
 				int oob_required, int page)
 {
-	struct denali_nand_info *denali = to_denali(chip);
+	struct denali_chip *dchip = to_denali_chip(chip);
 	struct mtd_info *mtd = nand_to_mtd(chip);
-	void *tmp_buf = denali->buf;
+	void *tmp_buf = dchip->buf;
 	size_t size = mtd->writesize + mtd->oobsize;
 	int ret;
 
@@ -698,9 +715,9 @@ static int denali_memcpy_out(void *buf, unsigned int offset, unsigned int len,
 static int denali_write_page_raw(struct nand_chip *chip, const uint8_t *buf,
 				 int oob_required, int page)
 {
-	struct denali_nand_info *denali = to_denali(chip);
+	struct denali_chip *dchip = to_denali_chip(chip);
 	struct mtd_info *mtd = nand_to_mtd(chip);
-	void *tmp_buf = denali->buf;
+	void *tmp_buf = dchip->buf;
 	size_t size = mtd->writesize + mtd->oobsize;
 	int ret;
 
@@ -772,8 +789,8 @@ static int denali_write_oob(struct nand_chip *chip, int page)
 static int denali_read_page(struct nand_chip *chip, uint8_t *buf,
 			    int oob_required, int page)
 {
+	struct denali_controller *denali = to_denali_controller(chip);
 	struct mtd_info *mtd = nand_to_mtd(chip);
-	struct denali_nand_info *denali = mtd_to_denali(mtd);
 	unsigned long uncor_ecc_flags = 0;
 	int stat = 0;
 	int ret;
@@ -814,7 +831,8 @@ static int denali_write_page(struct nand_chip *chip, const uint8_t *buf,
 static int denali_setup_data_interface(struct nand_chip *chip, int chipnr,
 				       const struct nand_data_interface *conf)
 {
-	struct denali_nand_info *denali = mtd_to_denali(nand_to_mtd(chip));
+	struct denali_controller *denali = to_denali_controller(chip);
+	struct denali_chip_sel *sel;
 	const struct nand_sdr_timings *timings;
 	unsigned long t_x, mult_x;
 	int acc_clks, re_2_we, re_2_re, we_2_re, addr_2_data;
@@ -843,6 +861,8 @@ static int denali_setup_data_interface(struct nand_chip *chip, int chipnr,
 	if (chipnr == NAND_DATA_IFACE_CHECK_ONLY)
 		return 0;
 
+	sel = &to_denali_chip(chip)->sels[chipnr];
+
 	/* tREA -> ACC_CLKS */
 	acc_clks = DIV_ROUND_UP(timings->tREA_max, t_x);
 	acc_clks = min_t(int, acc_clks, ACC_CLKS__VALUE);
@@ -850,7 +870,7 @@ static int denali_setup_data_interface(struct nand_chip *chip, int chipnr,
 	tmp = ioread32(denali->reg + ACC_CLKS);
 	tmp &= ~ACC_CLKS__VALUE;
 	tmp |= FIELD_PREP(ACC_CLKS__VALUE, acc_clks);
-	iowrite32(tmp, denali->reg + ACC_CLKS);
+	sel->acc_clks = tmp;
 
 	/* tRWH -> RE_2_WE */
 	re_2_we = DIV_ROUND_UP(timings->tRHW_min, t_x);
@@ -859,7 +879,7 @@ static int denali_setup_data_interface(struct nand_chip *chip, int chipnr,
 	tmp = ioread32(denali->reg + RE_2_WE);
 	tmp &= ~RE_2_WE__VALUE;
 	tmp |= FIELD_PREP(RE_2_WE__VALUE, re_2_we);
-	iowrite32(tmp, denali->reg + RE_2_WE);
+	sel->re_2_we = tmp;
 
 	/* tRHZ -> RE_2_RE */
 	re_2_re = DIV_ROUND_UP(timings->tRHZ_max, t_x);
@@ -868,7 +888,7 @@ static int denali_setup_data_interface(struct nand_chip *chip, int chipnr,
 	tmp = ioread32(denali->reg + RE_2_RE);
 	tmp &= ~RE_2_RE__VALUE;
 	tmp |= FIELD_PREP(RE_2_RE__VALUE, re_2_re);
-	iowrite32(tmp, denali->reg + RE_2_RE);
+	sel->re_2_re = tmp;
 
 	/*
 	 * tCCS, tWHR -> WE_2_RE
@@ -882,7 +902,7 @@ static int denali_setup_data_interface(struct nand_chip *chip, int chipnr,
 	tmp = ioread32(denali->reg + TWHR2_AND_WE_2_RE);
 	tmp &= ~TWHR2_AND_WE_2_RE__WE_2_RE;
 	tmp |= FIELD_PREP(TWHR2_AND_WE_2_RE__WE_2_RE, we_2_re);
-	iowrite32(tmp, denali->reg + TWHR2_AND_WE_2_RE);
+	sel->hwhr2_and_we_2_re = tmp;
 
 	/* tADL -> ADDR_2_DATA */
 
@@ -897,7 +917,7 @@ static int denali_setup_data_interface(struct nand_chip *chip, int chipnr,
 	tmp = ioread32(denali->reg + TCWAW_AND_ADDR_2_DATA);
 	tmp &= ~TCWAW_AND_ADDR_2_DATA__ADDR_2_DATA;
 	tmp |= FIELD_PREP(TCWAW_AND_ADDR_2_DATA__ADDR_2_DATA, addr_2_data);
-	iowrite32(tmp, denali->reg + TCWAW_AND_ADDR_2_DATA);
+	sel->tcwaw_and_addr_2_data = tmp;
 
 	/* tREH, tWH -> RDWR_EN_HI_CNT */
 	rdwr_en_hi = DIV_ROUND_UP(max(timings->tREH_min, timings->tWH_min),
@@ -907,7 +927,7 @@ static int denali_setup_data_interface(struct nand_chip *chip, int chipnr,
 	tmp = ioread32(denali->reg + RDWR_EN_HI_CNT);
 	tmp &= ~RDWR_EN_HI_CNT__VALUE;
 	tmp |= FIELD_PREP(RDWR_EN_HI_CNT__VALUE, rdwr_en_hi);
-	iowrite32(tmp, denali->reg + RDWR_EN_HI_CNT);
+	sel->rdwr_en_hi_cnt = tmp;
 
 	/* tRP, tWP -> RDWR_EN_LO_CNT */
 	rdwr_en_lo = DIV_ROUND_UP(max(timings->tRP_min, timings->tWP_min), t_x);
@@ -920,7 +940,7 @@ static int denali_setup_data_interface(struct nand_chip *chip, int chipnr,
 	tmp = ioread32(denali->reg + RDWR_EN_LO_CNT);
 	tmp &= ~RDWR_EN_LO_CNT__VALUE;
 	tmp |= FIELD_PREP(RDWR_EN_LO_CNT__VALUE, rdwr_en_lo);
-	iowrite32(tmp, denali->reg + RDWR_EN_LO_CNT);
+	sel->rdwr_en_lo_cnt = tmp;
 
 	/* tCS, tCEA -> CS_SETUP_CNT */
 	cs_setup = max3((int)DIV_ROUND_UP(timings->tCS_min, t_x) - rdwr_en_lo,
@@ -931,39 +951,11 @@ static int denali_setup_data_interface(struct nand_chip *chip, int chipnr,
 	tmp = ioread32(denali->reg + CS_SETUP_CNT);
 	tmp &= ~CS_SETUP_CNT__VALUE;
 	tmp |= FIELD_PREP(CS_SETUP_CNT__VALUE, cs_setup);
-	iowrite32(tmp, denali->reg + CS_SETUP_CNT);
+	sel->cs_setup_cnt = tmp;
 
 	return 0;
 }
 
-static void denali_hw_init(struct denali_nand_info *denali)
-{
-	/*
-	 * The REVISION register may not be reliable.  Platforms are allowed to
-	 * override it.
-	 */
-	if (!denali->revision)
-		denali->revision = swab16(ioread32(denali->reg + REVISION));
-
-	/*
-	 * Set how many bytes should be skipped before writing data in OOB.
-	 * If a non-zero value has already been set (by firmware or something),
-	 * just use it.  Otherwise, set the driver default.
-	 */
-	denali->oob_skip_bytes = ioread32(denali->reg + SPARE_AREA_SKIP_BYTES);
-	if (!denali->oob_skip_bytes) {
-		denali->oob_skip_bytes = DENALI_DEFAULT_OOB_SKIP_BYTES;
-		iowrite32(denali->oob_skip_bytes,
-			  denali->reg + SPARE_AREA_SKIP_BYTES);
-	}
-
-	denali_detect_max_banks(denali);
-	iowrite32(0x0F, denali->reg + RB_PIN_ENABLED);
-	iowrite32(CHIP_EN_DONT_CARE__FLAG, denali->reg + CHIP_ENABLE_DONT_CARE);
-
-	iowrite32(0xffff, denali->reg + SPARE_AREA_MARKER);
-}
-
 int denali_calc_ecc_bytes(int step_size, int strength)
 {
 	/* BCH code.  Denali requires ecc.bytes to be multiple of 2 */
@@ -974,10 +966,10 @@ EXPORT_SYMBOL(denali_calc_ecc_bytes);
 static int denali_ooblayout_ecc(struct mtd_info *mtd, int section,
 				struct mtd_oob_region *oobregion)
 {
-	struct denali_nand_info *denali = mtd_to_denali(mtd);
 	struct nand_chip *chip = mtd_to_nand(mtd);
+	struct denali_controller *denali = to_denali_controller(chip);
 
-	if (section)
+	if (section > 0)
 		return -ERANGE;
 
 	oobregion->offset = denali->oob_skip_bytes;
@@ -989,10 +981,10 @@ static int denali_ooblayout_ecc(struct mtd_info *mtd, int section,
 static int denali_ooblayout_free(struct mtd_info *mtd, int section,
 				 struct mtd_oob_region *oobregion)
 {
-	struct denali_nand_info *denali = mtd_to_denali(mtd);
 	struct nand_chip *chip = mtd_to_nand(mtd);
+	struct denali_controller *denali = to_denali_controller(chip);
 
-	if (section)
+	if (section > 0)
 		return -ERANGE;
 
 	oobregion->offset = chip->ecc.total + denali->oob_skip_bytes;
@@ -1008,7 +1000,7 @@ static const struct mtd_ooblayout_ops denali_ooblayout_ops = {
 
 static int denali_multidev_fixup(struct nand_chip *chip)
 {
-	struct denali_nand_info *denali = to_denali(chip);
+	struct denali_controller *denali = to_denali_controller(chip);
 	struct mtd_info *mtd = nand_to_mtd(chip);
 
 	/*
@@ -1059,38 +1051,11 @@ static int denali_multidev_fixup(struct nand_chip *chip)
 
 static int denali_attach_chip(struct nand_chip *chip)
 {
+	struct denali_controller *denali = to_denali_controller(chip);
+	struct denali_chip *dchip = to_denali_chip(chip);
 	struct mtd_info *mtd = nand_to_mtd(chip);
-	struct denali_nand_info *denali = mtd_to_denali(mtd);
 	int ret;
 
-	if (ioread32(denali->reg + FEATURES) & FEATURES__DMA)
-		denali->dma_avail = true;
-
-	if (denali->dma_avail) {
-		int dma_bit = denali->caps & DENALI_CAP_DMA_64BIT ? 64 : 32;
-
-		ret = dma_set_mask(denali->dev, DMA_BIT_MASK(dma_bit));
-		if (ret) {
-			dev_info(denali->dev,
-				 "Failed to set DMA mask. Disabling DMA.\n");
-			denali->dma_avail = false;
-		}
-	}
-
-	if (denali->dma_avail) {
-		chip->options |= NAND_USE_BOUNCE_BUFFER;
-		chip->buf_align = 16;
-		if (denali->caps & DENALI_CAP_DMA_64BIT)
-			denali->setup_dma = denali_setup_dma64;
-		else
-			denali->setup_dma = denali_setup_dma32;
-	}
-
-	chip->bbt_options |= NAND_BBT_USE_FLASH;
-	chip->bbt_options |= NAND_BBT_NO_OOB;
-	chip->ecc.mode = NAND_ECC_HW_SYNDROME;
-	chip->options |= NAND_NO_SUBPAGE_WRITE;
-
 	ret = nand_ecc_choose_conf(chip, denali->ecc_caps,
 				   mtd->oobsize - denali->oob_skip_bytes);
 	if (ret) {
@@ -1102,33 +1067,6 @@ static int denali_attach_chip(struct nand_chip *chip)
 		"chosen ECC settings: step=%d, strength=%d, bytes=%d\n",
 		chip->ecc.size, chip->ecc.strength, chip->ecc.bytes);
 
-	iowrite32(FIELD_PREP(ECC_CORRECTION__ERASE_THRESHOLD, 1) |
-		  FIELD_PREP(ECC_CORRECTION__VALUE, chip->ecc.strength),
-		  denali->reg + ECC_CORRECTION);
-	iowrite32(mtd->erasesize / mtd->writesize,
-		  denali->reg + PAGES_PER_BLOCK);
-	iowrite32(chip->options & NAND_BUSWIDTH_16 ? 1 : 0,
-		  denali->reg + DEVICE_WIDTH);
-	iowrite32(chip->options & NAND_ROW_ADDR_3 ? 0 : TWO_ROW_ADDR_CYCLES__FLAG,
-		  denali->reg + TWO_ROW_ADDR_CYCLES);
-	iowrite32(mtd->writesize, denali->reg + DEVICE_MAIN_AREA_SIZE);
-	iowrite32(mtd->oobsize, denali->reg + DEVICE_SPARE_AREA_SIZE);
-
-	iowrite32(chip->ecc.size, denali->reg + CFG_DATA_BLOCK_SIZE);
-	iowrite32(chip->ecc.size, denali->reg + CFG_LAST_DATA_BLOCK_SIZE);
-	/* chip->ecc.steps is set by nand_scan_tail(); not available here */
-	iowrite32(mtd->writesize / chip->ecc.size,
-		  denali->reg + CFG_NUM_DATA_BLOCKS);
-
-	mtd_set_ooblayout(mtd, &denali_ooblayout_ops);
-
-	chip->ecc.read_page = denali_read_page;
-	chip->ecc.read_page_raw = denali_read_page_raw;
-	chip->ecc.write_page = denali_write_page;
-	chip->ecc.write_page_raw = denali_write_page_raw;
-	chip->ecc.read_oob = denali_read_oob;
-	chip->ecc.write_oob = denali_write_oob;
-
 	ret = denali_multidev_fixup(chip);
 	if (ret)
 		return ret;
@@ -1138,8 +1076,8 @@ static int denali_attach_chip(struct nand_chip *chip)
 	 * use devm_kmalloc() because the memory allocated by devm_ does not
 	 * guarantee DMA-safe alignment.
 	 */
-	denali->buf = kmalloc(mtd->writesize + mtd->oobsize, GFP_KERNEL);
-	if (!denali->buf)
+	dchip->buf = kmalloc(mtd->writesize + mtd->oobsize, GFP_KERNEL);
+	if (!dchip->buf)
 		return -ENOMEM;
 
 	return 0;
@@ -1147,13 +1085,12 @@ static int denali_attach_chip(struct nand_chip *chip)
 
 static void denali_detach_chip(struct nand_chip *chip)
 {
-	struct mtd_info *mtd = nand_to_mtd(chip);
-	struct denali_nand_info *denali = mtd_to_denali(mtd);
+	struct denali_chip *dchip = to_denali_chip(chip);
 
-	kfree(denali->buf);
+	kfree(dchip->buf);
 }
 
-static void denali_exec_in8(struct denali_nand_info *denali, u32 type,
+static void denali_exec_in8(struct denali_controller *denali, u32 type,
 			    u8 *buf, unsigned int len)
 {
 	int i;
@@ -1162,7 +1099,7 @@ static void denali_exec_in8(struct denali_nand_info *denali, u32 type,
 		buf[i] = denali->host_read(denali, type | DENALI_BANK(denali));
 }
 
-static void denali_exec_in16(struct denali_nand_info *denali, u32 type,
+static void denali_exec_in16(struct denali_controller *denali, u32 type,
 			     u8 *buf, unsigned int len)
 {
 	u32 data;
@@ -1176,7 +1113,7 @@ static void denali_exec_in16(struct denali_nand_info *denali, u32 type,
 	}
 }
 
-static void denali_exec_out8(struct denali_nand_info *denali, u32 type,
+static void denali_exec_out8(struct denali_controller *denali, u32 type,
 			     const u8 *buf, unsigned int len)
 {
 	int i;
@@ -1185,7 +1122,7 @@ static void denali_exec_out8(struct denali_nand_info *denali, u32 type,
 		denali->host_write(denali, type | DENALI_BANK(denali), buf[i]);
 }
 
-static void denali_exec_out16(struct denali_nand_info *denali, u32 type,
+static void denali_exec_out16(struct denali_controller *denali, u32 type,
 			      const u8 *buf, unsigned int len)
 {
 	int i;
@@ -1195,7 +1132,7 @@ static void denali_exec_out16(struct denali_nand_info *denali, u32 type,
 				   buf[i + 1] << 16 | buf[i]);
 }
 
-static int denali_exec_waitrdy(struct denali_nand_info *denali)
+static int denali_exec_waitrdy(struct denali_controller *denali)
 {
 	u32 irq_stat;
 
@@ -1211,7 +1148,7 @@ static int denali_exec_waitrdy(struct denali_nand_info *denali)
 static int denali_exec_instr(struct nand_chip *chip,
 			     const struct nand_op_instr *instr)
 {
-	struct denali_nand_info *denali = to_denali(chip);
+	struct denali_controller *denali = to_denali_controller(chip);
 	bool width16 = chip->options & NAND_BUSWIDTH_16;
 
 	switch (instr->type) {
@@ -1262,7 +1199,7 @@ static int denali_exec_op(struct nand_chip *chip,
 	 * Some commands contain NAND_OP_WAITRDY_INSTR.
 	 * irq must be cleared here to catch the R/B# interrupt there.
 	 */
-	denali_reset_irq(to_denali(chip));
+	denali_reset_irq(to_denali_controller(chip));
 
 	for (i = 0; i < op->ninstrs; i++) {
 		ret = denali_exec_instr(chip, &op->instrs[i]);
@@ -1280,53 +1217,80 @@ static const struct nand_controller_ops denali_controller_ops = {
 	.setup_data_interface = denali_setup_data_interface,
 };
 
-int denali_init(struct denali_nand_info *denali)
+int denali_chip_init(struct denali_controller *denali,
+		     struct denali_chip *dchip)
 {
-	struct nand_chip *chip = &denali->nand;
+	struct nand_chip *chip = &dchip->chip;
 	struct mtd_info *mtd = nand_to_mtd(chip);
-	u32 features = ioread32(denali->reg + FEATURES);
-	int ret;
+	struct denali_chip *dchip2;
+	int i, j, ret;
 
-	mtd->dev.parent = denali->dev;
-	denali_hw_init(denali);
+	chip->controller = &denali->controller;
 
-	init_completion(&denali->complete);
-	spin_lock_init(&denali->irq_lock);
+	/* sanity checks for bank numbers */
+	for (i = 0; i < dchip->nsels; i++) {
+		unsigned int bank = dchip->sels[i].bank;
 
-	denali_clear_irq_all(denali);
+		if (bank >= denali->nbanks) {
+			dev_err(denali->dev, "unsupported bank %d\n", bank);
+			return -EINVAL;
+		}
 
-	ret = devm_request_irq(denali->dev, denali->irq, denali_isr,
-			       IRQF_SHARED, DENALI_NAND_NAME, denali);
-	if (ret) {
-		dev_err(denali->dev, "Unable to request IRQ\n");
-		return ret;
-	}
+		for (j = 0; j < i; j++) {
+			if (bank == dchip->sels[j].bank) {
+				dev_err(denali->dev,
+					"bank %d is assigned twice in the same chip\n",
+					bank);
+				return -EINVAL;
+			}
+		}
 
-	denali_enable_irq(denali);
+		list_for_each_entry(dchip2, &denali->chips, node) {
+			for (j = 0; j < dchip2->nsels; j++) {
+				if (bank == dchip2->sels[j].bank) {
+					dev_err(denali->dev,
+						"bank %d is already used\n",
+						bank);
+					return -EINVAL;
+				}
+			}
+		}
+	}
 
-	denali->active_bank = DENALI_INVALID_BANK;
+	mtd->dev.parent = denali->dev;
 
-	nand_set_flash_node(chip, denali->dev->of_node);
-	/* Fallback to the default name if DT did not give "label" property */
-	if (!mtd->name)
+	/*
+	 * Fallback to the default name if DT did not give "label" property.
+	 * Use "label" property if multiple chips are connected.
+	 */
+	if (!mtd->name && list_empty(&denali->chips))
 		mtd->name = "denali-nand";
 
-	if (features & FEATURES__INDEX_ADDR) {
-		denali->host_read = denali_indexed_read;
-		denali->host_write = denali_indexed_write;
-	} else {
-		denali->host_read = denali_direct_read;
-		denali->host_write = denali_direct_write;
+	if (denali->dma_avail) {
+		chip->options |= NAND_USE_BOUNCE_BUFFER;
+		chip->buf_align = 16;
 	}
 
 	/* clk rate info is needed for setup_data_interface */
 	if (!denali->clk_rate || !denali->clk_x_rate)
 		chip->options |= NAND_KEEP_TIMINGS;
 
-	chip->legacy.dummy_controller.ops = &denali_controller_ops;
-	ret = nand_scan(chip, denali->max_banks);
+	chip->bbt_options |= NAND_BBT_USE_FLASH;
+	chip->bbt_options |= NAND_BBT_NO_OOB;
+	chip->options |= NAND_NO_SUBPAGE_WRITE;
+	chip->ecc.mode = NAND_ECC_HW_SYNDROME;
+	chip->ecc.read_page = denali_read_page;
+	chip->ecc.write_page = denali_write_page;
+	chip->ecc.read_page_raw = denali_read_page_raw;
+	chip->ecc.write_page_raw = denali_write_page_raw;
+	chip->ecc.read_oob = denali_read_oob;
+	chip->ecc.write_oob = denali_write_oob;
+
+	mtd_set_ooblayout(mtd, &denali_ooblayout_ops);
+
+	ret = nand_scan(chip, dchip->nsels);
 	if (ret)
-		goto disable_irq;
+		return ret;
 
 	ret = mtd_device_register(mtd, NULL, 0);
 	if (ret) {
@@ -1334,20 +1298,109 @@ int denali_init(struct denali_nand_info *denali)
 		goto cleanup_nand;
 	}
 
+	list_add_tail(&dchip->node, &denali->chips);
+
 	return 0;
 
 cleanup_nand:
 	nand_cleanup(chip);
-disable_irq:
-	denali_disable_irq(denali);
 
 	return ret;
 }
+EXPORT_SYMBOL_GPL(denali_chip_init);
+
+int denali_init(struct denali_controller *denali)
+{
+	u32 features = ioread32(denali->reg + FEATURES);
+	int ret;
+
+	nand_controller_init(&denali->controller);
+	denali->controller.ops = &denali_controller_ops;
+	init_completion(&denali->complete);
+	spin_lock_init(&denali->irq_lock);
+	INIT_LIST_HEAD(&denali->chips);
+	denali->active_bank = DENALI_INVALID_BANK;
+
+	/*
+	 * The REVISION register may not be reliable. Platforms are allowed to
+	 * override it.
+	 */
+	if (!denali->revision)
+		denali->revision = swab16(ioread32(denali->reg + REVISION));
+
+	denali->nbanks = 1 << FIELD_GET(FEATURES__N_BANKS, features);
+
+	/* the encoding changed from rev 5.0 to 5.1 */
+	if (denali->revision < 0x0501)
+		denali->nbanks <<= 1;
+
+	if (features & FEATURES__DMA)
+		denali->dma_avail = true;
+
+	if (denali->dma_avail) {
+		int dma_bit = denali->caps & DENALI_CAP_DMA_64BIT ? 64 : 32;
+
+		ret = dma_set_mask(denali->dev, DMA_BIT_MASK(dma_bit));
+		if (ret) {
+			dev_info(denali->dev,
+				 "Failed to set DMA mask. Disabling DMA.\n");
+			denali->dma_avail = false;
+		}
+	}
+
+	if (denali->dma_avail) {
+		if (denali->caps & DENALI_CAP_DMA_64BIT)
+			denali->setup_dma = denali_setup_dma64;
+		else
+			denali->setup_dma = denali_setup_dma32;
+	}
+
+	if (features & FEATURES__INDEX_ADDR) {
+		denali->host_read = denali_indexed_read;
+		denali->host_write = denali_indexed_write;
+	} else {
+		denali->host_read = denali_direct_read;
+		denali->host_write = denali_direct_write;
+	}
+
+	/*
+	 * Set how many bytes should be skipped before writing data in OOB.
+	 * If a non-zero value has already been set (by firmware or something),
+	 * just use it. Otherwise, set the driver's default.
+	 */
+	denali->oob_skip_bytes = ioread32(denali->reg + SPARE_AREA_SKIP_BYTES);
+	if (!denali->oob_skip_bytes) {
+		denali->oob_skip_bytes = DENALI_DEFAULT_OOB_SKIP_BYTES;
+		iowrite32(denali->oob_skip_bytes,
+			  denali->reg + SPARE_AREA_SKIP_BYTES);
+	}
+
+	iowrite32(GENMASK(denali->nbanks - 1, 0), denali->reg + RB_PIN_ENABLED);
+	iowrite32(CHIP_EN_DONT_CARE__FLAG, denali->reg + CHIP_ENABLE_DONT_CARE);
+	iowrite32(0xffff, denali->reg + SPARE_AREA_MARKER);
+
+	denali_clear_irq_all(denali);
+
+	ret = devm_request_irq(denali->dev, denali->irq, denali_isr,
+			       IRQF_SHARED, DENALI_NAND_NAME, denali);
+	if (ret) {
+		dev_err(denali->dev, "Unable to request IRQ\n");
+		return ret;
+	}
+
+	denali_enable_irq(denali);
+
+	return 0;
+}
 EXPORT_SYMBOL(denali_init);
 
-void denali_remove(struct denali_nand_info *denali)
+void denali_remove(struct denali_controller *denali)
 {
-	nand_release(&denali->nand);
+	struct denali_chip *dchip;
+
+	list_for_each_entry(dchip, &denali->chips, node)
+		nand_release(&dchip->chip);
+
 	denali_disable_irq(denali);
 }
 EXPORT_SYMBOL(denali_remove);
diff --git a/drivers/mtd/nand/raw/denali.h b/drivers/mtd/nand/raw/denali.h
index 8552b3f..138b239 100644
--- a/drivers/mtd/nand/raw/denali.h
+++ b/drivers/mtd/nand/raw/denali.h
@@ -9,6 +9,7 @@
 
 #include <linux/bits.h>
 #include <linux/completion.h>
+#include <linux/list.h>
 #include <linux/mtd/rawnand.h>
 #include <linux/spinlock_types.h>
 #include <linux/types.h>
@@ -290,30 +291,100 @@
 #define     CHNL_ACTIVE__CHANNEL2			BIT(2)
 #define     CHNL_ACTIVE__CHANNEL3			BIT(3)
 
-struct denali_nand_info {
-	struct nand_chip nand;
-	unsigned long clk_rate;		/* core clock rate */
-	unsigned long clk_x_rate;	/* bus interface clock rate */
-	int active_bank;		/* currently selected bank */
+/**
+ * struct denali_chip_sel - per-CS data of Denali NAND
+ *
+ * @bank:                  bank id of the controller this CS is connected to
+ * @hwhr2_and_we_2_re:     value of timing register HWHR2_AND_WE_2_RE
+ * @tcwaw_and_addr_2_data: value of timing register TCWAW_AND_ADDR_2_DATA
+ * @re_2_we:               value of timing register RE_2_WE
+ * @acc_clks:              value of timing register ACC_CLKS
+ * @rdwr_en_lo_cnt:        value of timing register RDWR_EN_LO_CNT
+ * @rdwr_en_hi_cnt:        value of timing register RDWR_EN_HI_CNT
+ * @cs_setup_cnt:          value of timing register CS_SETUP_CNT
+ * @re_2_re:               value of timing register RE_2_RE
+ */
+struct denali_chip_sel {
+	int bank;
+	u32 hwhr2_and_we_2_re;
+	u32 tcwaw_and_addr_2_data;
+	u32 re_2_we;
+	u32 acc_clks;
+	u32 rdwr_en_lo_cnt;
+	u32 rdwr_en_hi_cnt;
+	u32 cs_setup_cnt;
+	u32 re_2_re;
+};
+
+/**
+ * struct denali_chip - per-chip data of Denali NAND
+ *
+ * @chip:  base NAND chip structure
+ * @buf:   internal buffer for syndrome layout conversion
+ * @node:  node to be used to associate this chip with the controller
+ * @nsels: the number of CS lines of this chip
+ * @sels:  the array of per-cs data
+ */
+struct denali_chip {
+	struct nand_chip chip;
+	void *buf;
+	struct list_head node;
+	unsigned int nsels;
+	struct denali_chip_sel sels[0];
+};
+
+/**
+ * struct denali_controller - Denali NAND controller data
+ *
+ * @controller:     base NAND controller structure
+ * @dev:            device
+ * @chips:          the list of chips attached to this controller
+ * @clk_rate:       frequency of core clock
+ * @clk_x_rate:     frequency of bus interface clock
+ * @reg:            base of Register Interface
+ * @host:           base of Host Data/Command interface
+ * @complete:       completion used to wait for interrupts
+ * @irq:            interrupt number
+ * @irq_mask:       interrupt bits the controller is waiting for
+ * @irq_status:     interrupt bits of events that have happened
+ * @irq_lock:       lock to protect @irq_mask and @irq_status
+ * @dma_avail:      set if DMA engine is available
+ * @devs_per_cs:    number of devices connected in parallel
+ * @oob_skip_bytes: number of bytes in OOB skipped by the ECC engine
+ * @active_bank:    active bank id
+ * @nbanks:         the number of banks supported by this controller
+ * @revision:       IP revision
+ * @caps:           controller capabilities that cannot be detected run-time
+ * @ecc_caps:       ECC engine capabilities
+ * @host_read:      callback for read access of Host Data/Command Interface
+ * @host_write:     callback for write access of Host Data/Command Interface
+ * @setup_dma:      callback for setup of the Data DMA
+ */
+struct denali_controller {
+	struct nand_controller controller;
 	struct device *dev;
-	void __iomem *reg;		/* Register Interface */
-	void __iomem *host;		/* Host Data/Command Interface */
+	struct list_head chips;
+	unsigned long clk_rate;
+	unsigned long clk_x_rate;
+	void __iomem *reg;
+	void __iomem *host;
 	struct completion complete;
-	spinlock_t irq_lock;		/* protect irq_mask and irq_status */
-	u32 irq_mask;			/* interrupts we are waiting for */
-	u32 irq_status;			/* interrupts that have happened */
 	int irq;
-	void *buf;			/* for syndrome layout conversion */
-	bool dma_avail;			/* can support DMA? */
-	int devs_per_cs;		/* devices connected in parallel */
-	int oob_skip_bytes;		/* number of bytes reserved for BBM */
-	int max_banks;
-	unsigned int revision;		/* IP revision */
-	unsigned int caps;		/* IP capability (or quirk) */
+	u32 irq_mask;
+	u32 irq_status;
+	spinlock_t irq_lock;
+	bool dma_avail;
+	int devs_per_cs;
+	int oob_skip_bytes;
+	int active_bank;
+	int nbanks;
+	unsigned int revision;
+	unsigned int caps;
 	const struct nand_ecc_caps *ecc_caps;
-	u32 (*host_read)(struct denali_nand_info *denali, u32 addr);
-	void (*host_write)(struct denali_nand_info *denali, u32 addr, u32 data);
-	void (*setup_dma)(struct denali_nand_info *denali, dma_addr_t dma_addr,
+	u32 (*host_read)(struct denali_controller *denali, u32 addr);
+	void (*host_write)(struct denali_controller *denali, u32 addr,
+			   u32 data);
+	void (*setup_dma)(struct denali_controller *denali, dma_addr_t dma_addr,
 			  int page, bool write);
 };
 
@@ -321,7 +392,9 @@ struct denali_nand_info {
 #define DENALI_CAP_DMA_64BIT			BIT(1)
 
 int denali_calc_ecc_bytes(int step_size, int strength);
-int denali_init(struct denali_nand_info *denali);
-void denali_remove(struct denali_nand_info *denali);
+int denali_chip_init(struct denali_controller *denali,
+		     struct denali_chip *dchip);
+int denali_init(struct denali_controller *denali);
+void denali_remove(struct denali_controller *denali);
 
 #endif /* __DENALI_H__ */
diff --git a/drivers/mtd/nand/raw/denali_dt.c b/drivers/mtd/nand/raw/denali_dt.c
index 0b5ae24..5e14836 100644
--- a/drivers/mtd/nand/raw/denali_dt.c
+++ b/drivers/mtd/nand/raw/denali_dt.c
@@ -18,7 +18,7 @@
 #include "denali.h"
 
 struct denali_dt {
-	struct denali_nand_info	denali;
+	struct denali_controller controller;
 	struct clk *clk;	/* core clock */
 	struct clk *clk_x;	/* bus interface clock */
 	struct clk *clk_ecc;	/* ECC circuit clock */
@@ -71,19 +71,92 @@ static const struct of_device_id denali_nand_dt_ids[] = {
 };
 MODULE_DEVICE_TABLE(of, denali_nand_dt_ids);
 
+static int denali_dt_chip_init(struct denali_controller *denali,
+			       struct device_node *chip_np)
+{
+	struct denali_chip *dchip;
+	u32 bank;
+	int nsels, i, ret;
+
+	nsels = of_property_count_u32_elems(chip_np, "reg");
+	if (nsels < 0)
+		return nsels;
+
+	dchip = devm_kzalloc(denali->dev, struct_size(dchip, sels, nsels),
+			     GFP_KERNEL);
+	if (!dchip)
+		return -ENOMEM;
+
+	dchip->nsels = nsels;
+
+	for (i = 0; i < nsels; i++) {
+		ret = of_property_read_u32_index(chip_np, "reg", i, &bank);
+		if (ret)
+			return ret;
+
+		dchip->sels[i].bank = bank;
+
+		nand_set_flash_node(&dchip->chip, chip_np);
+	}
+
+	return denali_chip_init(denali, dchip);
+}
+
+/* Backward compatibility for old platforms */
+static int denali_dt_legacy_chip_init(struct denali_controller *denali)
+{
+	struct denali_chip *dchip;
+	int nsels, i;
+
+	nsels = denali->nbanks;
+
+	dchip = devm_kzalloc(denali->dev, struct_size(dchip, sels, nsels),
+			     GFP_KERNEL);
+	if (!dchip)
+		return -ENOMEM;
+
+	dchip->nsels = nsels;
+
+	for (i = 0; i < nsels; i++)
+		dchip->sels[i].bank = i;
+
+	nand_set_flash_node(&dchip->chip, denali->dev->of_node);
+
+	return denali_chip_init(denali, dchip);
+}
+
+/*
+ * Check the DT binding.
+ * The new binding expects chip subnodes in the controller node.
+ * So, #address-cells = <1>; #size-cells = <0>; are required.
+ * Check the #size-cells to distinguish the binding.
+ */
+static bool denali_dt_is_legacy_binding(struct device_node *np)
+{
+	u32 cells;
+	int ret;
+
+	ret = of_property_read_u32(np, "#size-cells", &cells);
+	if (ret)
+		return true;
+
+	return cells != 0;
+}
+
 static int denali_dt_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
 	struct resource *res;
 	struct denali_dt *dt;
 	const struct denali_dt_data *data;
-	struct denali_nand_info *denali;
+	struct denali_controller *denali;
+	struct device_node *np;
 	int ret;
 
 	dt = devm_kzalloc(dev, sizeof(*dt), GFP_KERNEL);
 	if (!dt)
 		return -ENOMEM;
-	denali = &dt->denali;
+	denali = &dt->controller;
 
 	data = of_device_get_match_data(dev);
 	if (data) {
@@ -140,9 +213,26 @@ static int denali_dt_probe(struct platform_device *pdev)
 	if (ret)
 		goto out_disable_clk_ecc;
 
+	if (denali_dt_is_legacy_binding(dev->of_node)) {
+		ret = denali_dt_legacy_chip_init(denali);
+		if (ret)
+			goto out_remove_denali;
+	} else {
+		for_each_child_of_node(dev->of_node, np) {
+			ret = denali_dt_chip_init(denali, np);
+			if (ret) {
+				of_node_put(np);
+				goto out_remove_denali;
+			}
+		}
+	}
+
 	platform_set_drvdata(pdev, dt);
+
 	return 0;
 
+out_remove_denali:
+	denali_remove(denali);
 out_disable_clk_ecc:
 	clk_disable_unprepare(dt->clk_ecc);
 out_disable_clk_x:
@@ -157,7 +247,7 @@ static int denali_dt_remove(struct platform_device *pdev)
 {
 	struct denali_dt *dt = platform_get_drvdata(pdev);
 
-	denali_remove(&dt->denali);
+	denali_remove(&dt->controller);
 	clk_disable_unprepare(dt->clk_ecc);
 	clk_disable_unprepare(dt->clk_x);
 	clk_disable_unprepare(dt->clk);
diff --git a/drivers/mtd/nand/raw/denali_pci.c b/drivers/mtd/nand/raw/denali_pci.c
index 02eb599..d62aa52 100644
--- a/drivers/mtd/nand/raw/denali_pci.c
+++ b/drivers/mtd/nand/raw/denali_pci.c
@@ -29,10 +29,11 @@ NAND_ECC_CAPS_SINGLE(denali_pci_ecc_caps, denali_calc_ecc_bytes, 512, 8, 15);
 
 static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
 {
-	int ret;
 	resource_size_t csr_base, mem_base;
 	unsigned long csr_len, mem_len;
-	struct denali_nand_info *denali;
+	struct denali_controller *denali;
+	struct denali_chip *dchip;
+	int nsels, ret, i;
 
 	denali = devm_kzalloc(&dev->dev, sizeof(*denali), GFP_KERNEL);
 	if (!denali)
@@ -64,7 +65,6 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
 	denali->dev = &dev->dev;
 	denali->irq = dev->irq;
 	denali->ecc_caps = &denali_pci_ecc_caps;
-	denali->nand.ecc.options |= NAND_ECC_MAXIMIZE;
 	denali->clk_rate = 50000000;		/* 50 MHz */
 	denali->clk_x_rate = 200000000;		/* 200 MHz */
 
@@ -91,10 +91,32 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
 	if (ret)
 		goto out_unmap_host;
 
+	nsels = denali->nbanks;
+
+	dchip = devm_kzalloc(denali->dev, struct_size(dchip, sels, nsels),
+			     GFP_KERNEL);
+	if (!dchip) {
+		ret = -ENOMEM;
+		goto out_remove_denali;
+	}
+
+	dchip->chip.ecc.options |= NAND_ECC_MAXIMIZE;
+
+	dchip->nsels = nsels;
+
+	for (i = 0; i < nsels; i++)
+		dchip->sels[i].bank = i;
+
+	ret = denali_chip_init(denali, dchip);
+	if (ret)
+		goto out_remove_denali;
+
 	pci_set_drvdata(dev, denali);
 
 	return 0;
 
+out_remove_denali:
+	denali_remove(denali);
 out_unmap_host:
 	iounmap(denali->host);
 out_unmap_reg:
@@ -104,7 +126,7 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
 
 static void denali_pci_remove(struct pci_dev *dev)
 {
-	struct denali_nand_info *denali = pci_get_drvdata(dev);
+	struct denali_controller *denali = pci_get_drvdata(dev);
 
 	denali_remove(denali);
 	iounmap(denali->reg);
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH v3 7/9] mtd: rawnand: denali: decouple controller and NAND chips
@ 2019-03-12  8:44   ` Masahiro Yamada
  0 siblings, 0 replies; 40+ messages in thread
From: Masahiro Yamada @ 2019-03-12  8:44 UTC (permalink / raw)
  To: linux-mtd, Miquel Raynal
  Cc: Mark Rutland, devicetree, Masahiro Yamada, Richard Weinberger,
	Boris Brezillon, linux-kernel, Marek Vasut, Rob Herring,
	Brian Norris, David Woodhouse

Currently, this driver sticks to the legacy NAND model because it was
upstreamed before commit 2d472aba15ff ("mtd: nand: document the NAND
controller/NAND chip DT representation"). However, relying on the
dummy_controller is already deprecated.

Switch over to the new controller/chip representation.

The struct denali_nand_info has been split into denali_controller
and denali_chip, to contain the controller data, per-chip data,
respectively.

One problem is, this commit changes the DT binding. So, as always,
the backward compatibility must be taken into consideration.

In the new binding, the controller node expects

  #address-cells = <1>;
  #size-cells = <0>;

... since the child nodes represent NAND chips.

In the old binding, the controller node may have subnodes, but they
are MTD partitions.

The denali_dt_is_legacy_binding() exploits it to distinguish old/new
platforms.

Going forward, the old binding is only allowed for existing DT files.
I updated the binding document.

Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
Acked-by: Rob Herring <robh@kernel.org>
---

Changes in v3:
 - simplify mtd->name fallback
 - Add Rob's Ack

Changes in v2: None

 .../devicetree/bindings/mtd/denali-nand.txt        |  39 +-
 drivers/mtd/nand/raw/denali.c                      | 441 ++++++++++++---------
 drivers/mtd/nand/raw/denali.h                      | 117 +++++-
 drivers/mtd/nand/raw/denali_dt.c                   |  98 ++++-
 drivers/mtd/nand/raw/denali_pci.c                  |  30 +-
 5 files changed, 488 insertions(+), 237 deletions(-)

diff --git a/Documentation/devicetree/bindings/mtd/denali-nand.txt b/Documentation/devicetree/bindings/mtd/denali-nand.txt
index f33da87..de4ddff 100644
--- a/Documentation/devicetree/bindings/mtd/denali-nand.txt
+++ b/Documentation/devicetree/bindings/mtd/denali-nand.txt
@@ -7,34 +7,47 @@ Required properties:
       "socionext,uniphier-denali-nand-v5b"  - for Socionext UniPhier (v5b)
   - reg : should contain registers location and length for data and reg.
   - reg-names: Should contain the reg names "nand_data" and "denali_reg"
+  - #address-cells: should be 1. The cell encodes the chip select connection.
+  - #size-cells : should be 0.
   - interrupts : The interrupt number.
   - clocks: should contain phandle of the controller core clock, the bus
     interface clock, and the ECC circuit clock.
   - clock-names: should contain "nand", "nand_x", "ecc"
 
-Optional properties:
-  - nand-ecc-step-size: see nand.txt for details.  If present, the value must be
-      512        for "altr,socfpga-denali-nand"
-      1024       for "socionext,uniphier-denali-nand-v5a"
-      1024       for "socionext,uniphier-denali-nand-v5b"
-  - nand-ecc-strength: see nand.txt for details.  Valid values are:
-      8, 15      for "altr,socfpga-denali-nand"
-      8, 16, 24  for "socionext,uniphier-denali-nand-v5a"
-      8, 16      for "socionext,uniphier-denali-nand-v5b"
-  - nand-ecc-maximize: see nand.txt for details
-
-The device tree may optionally contain sub-nodes describing partitions of the
+Sub-nodes:
+  Sub-nodes represent available NAND chips.
+
+  Required properties:
+    - reg: should contain the bank ID of the controller to which each chip
+      select is connected.
+
+  Optional properties:
+    - nand-ecc-step-size: see nand.txt for details.  If present, the value must be
+        512        for "altr,socfpga-denali-nand"
+        1024       for "socionext,uniphier-denali-nand-v5a"
+        1024       for "socionext,uniphier-denali-nand-v5b"
+    - nand-ecc-strength: see nand.txt for details.  Valid values are:
+        8, 15      for "altr,socfpga-denali-nand"
+        8, 16, 24  for "socionext,uniphier-denali-nand-v5a"
+        8, 16      for "socionext,uniphier-denali-nand-v5b"
+    - nand-ecc-maximize: see nand.txt for details
+
+The chip nodes may optionally contain sub-nodes describing partitions of the
 address space. See partition.txt for more detail.
 
 Examples:
 
 nand: nand@ff900000 {
 	#address-cells = <1>;
-	#size-cells = <1>;
+	#size-cells = <0>;
 	compatible = "altr,socfpga-denali-nand";
 	reg = <0xff900000 0x20>, <0xffb80000 0x1000>;
 	reg-names = "nand_data", "denali_reg";
 	clocks = <&nand_clk>, <&nand_x_clk>, <&nand_ecc_clk>;
 	clock-names = "nand", "nand_x", "ecc";
 	interrupts = <0 144 4>;
+
+	nand@0 {
+		reg = <0>;
+	}
 };
diff --git a/drivers/mtd/nand/raw/denali.c b/drivers/mtd/nand/raw/denali.c
index 05fbe8f..0c47c56 100644
--- a/drivers/mtd/nand/raw/denali.c
+++ b/drivers/mtd/nand/raw/denali.c
@@ -3,7 +3,7 @@
  * NAND Flash Controller Device Driver
  * Copyright © 2009-2010, Intel Corporation and its suppliers.
  *
- * Copyright (c) 2017 Socionext Inc.
+ * Copyright (c) 2017-2019 Socionext Inc.
  *   Reworked by Masahiro Yamada <yamada.masahiro@socionext.com>
  */
 
@@ -42,14 +42,15 @@
 #define DENALI_INVALID_BANK	-1
 #define DENALI_NR_BANKS		4
 
-static inline struct denali_nand_info *mtd_to_denali(struct mtd_info *mtd)
+static struct denali_chip *to_denali_chip(struct nand_chip *chip)
 {
-	return container_of(mtd_to_nand(mtd), struct denali_nand_info, nand);
+	return container_of(chip, struct denali_chip, chip);
 }
 
-static struct denali_nand_info *to_denali(struct nand_chip *chip)
+static struct denali_controller *to_denali_controller(struct nand_chip *chip)
 {
-	return container_of(chip, struct denali_nand_info, nand);
+	return container_of(chip->controller, struct denali_controller,
+			    controller);
 }
 
 /*
@@ -57,12 +58,12 @@ static struct denali_nand_info *to_denali(struct nand_chip *chip)
  * type, bank, block, and page address).  The slave data is the actual data to
  * be transferred.  This mode requires 28 bits of address region allocated.
  */
-static u32 denali_direct_read(struct denali_nand_info *denali, u32 addr)
+static u32 denali_direct_read(struct denali_controller *denali, u32 addr)
 {
 	return ioread32(denali->host + addr);
 }
 
-static void denali_direct_write(struct denali_nand_info *denali, u32 addr,
+static void denali_direct_write(struct denali_controller *denali, u32 addr,
 				u32 data)
 {
 	iowrite32(data, denali->host + addr);
@@ -74,35 +75,20 @@ static void denali_direct_write(struct denali_nand_info *denali, u32 addr,
  * control information and transferred data are latched by the registers in
  * the translation module.
  */
-static u32 denali_indexed_read(struct denali_nand_info *denali, u32 addr)
+static u32 denali_indexed_read(struct denali_controller *denali, u32 addr)
 {
 	iowrite32(addr, denali->host + DENALI_INDEXED_CTRL);
 	return ioread32(denali->host + DENALI_INDEXED_DATA);
 }
 
-static void denali_indexed_write(struct denali_nand_info *denali, u32 addr,
+static void denali_indexed_write(struct denali_controller *denali, u32 addr,
 				 u32 data)
 {
 	iowrite32(addr, denali->host + DENALI_INDEXED_CTRL);
 	iowrite32(data, denali->host + DENALI_INDEXED_DATA);
 }
 
-/*
- * Use the configuration feature register to determine the maximum number of
- * banks that the hardware supports.
- */
-static void denali_detect_max_banks(struct denali_nand_info *denali)
-{
-	uint32_t features = ioread32(denali->reg + FEATURES);
-
-	denali->max_banks = 1 << FIELD_GET(FEATURES__N_BANKS, features);
-
-	/* the encoding changed from rev 5.0 to 5.1 */
-	if (denali->revision < 0x0501)
-		denali->max_banks <<= 1;
-}
-
-static void denali_enable_irq(struct denali_nand_info *denali)
+static void denali_enable_irq(struct denali_controller *denali)
 {
 	int i;
 
@@ -111,7 +97,7 @@ static void denali_enable_irq(struct denali_nand_info *denali)
 	iowrite32(GLOBAL_INT_EN_FLAG, denali->reg + GLOBAL_INT_ENABLE);
 }
 
-static void denali_disable_irq(struct denali_nand_info *denali)
+static void denali_disable_irq(struct denali_controller *denali)
 {
 	int i;
 
@@ -120,14 +106,14 @@ static void denali_disable_irq(struct denali_nand_info *denali)
 	iowrite32(0, denali->reg + GLOBAL_INT_ENABLE);
 }
 
-static void denali_clear_irq(struct denali_nand_info *denali,
+static void denali_clear_irq(struct denali_controller *denali,
 			     int bank, uint32_t irq_status)
 {
 	/* write one to clear bits */
 	iowrite32(irq_status, denali->reg + INTR_STATUS(bank));
 }
 
-static void denali_clear_irq_all(struct denali_nand_info *denali)
+static void denali_clear_irq_all(struct denali_controller *denali)
 {
 	int i;
 
@@ -137,7 +123,7 @@ static void denali_clear_irq_all(struct denali_nand_info *denali)
 
 static irqreturn_t denali_isr(int irq, void *dev_id)
 {
-	struct denali_nand_info *denali = dev_id;
+	struct denali_controller *denali = dev_id;
 	irqreturn_t ret = IRQ_NONE;
 	uint32_t irq_status;
 	int i;
@@ -165,7 +151,7 @@ static irqreturn_t denali_isr(int irq, void *dev_id)
 	return ret;
 }
 
-static void denali_reset_irq(struct denali_nand_info *denali)
+static void denali_reset_irq(struct denali_controller *denali)
 {
 	unsigned long flags;
 
@@ -175,8 +161,7 @@ static void denali_reset_irq(struct denali_nand_info *denali)
 	spin_unlock_irqrestore(&denali->irq_lock, flags);
 }
 
-static uint32_t denali_wait_for_irq(struct denali_nand_info *denali,
-				    uint32_t irq_mask)
+static u32 denali_wait_for_irq(struct denali_controller *denali, u32 irq_mask)
 {
 	unsigned long time_left, flags;
 	uint32_t irq_status;
@@ -208,13 +193,45 @@ static uint32_t denali_wait_for_irq(struct denali_nand_info *denali,
 
 static void denali_select_target(struct nand_chip *chip, int cs)
 {
-	struct denali_nand_info *denali = to_denali(chip);
+	struct denali_controller *denali = to_denali_controller(chip);
+	struct denali_chip_sel *sel = &to_denali_chip(chip)->sels[cs];
+	struct mtd_info *mtd = nand_to_mtd(chip);
+
+	denali->active_bank = sel->bank;
 
-	denali->active_bank = cs;
+	iowrite32(1 << (chip->phys_erase_shift - chip->page_shift),
+		  denali->reg + PAGES_PER_BLOCK);
+	iowrite32(chip->options & NAND_BUSWIDTH_16 ? 1 : 0,
+		  denali->reg + DEVICE_WIDTH);
+	iowrite32(mtd->writesize, denali->reg + DEVICE_MAIN_AREA_SIZE);
+	iowrite32(mtd->oobsize, denali->reg + DEVICE_SPARE_AREA_SIZE);
+	iowrite32(chip->options & NAND_ROW_ADDR_3 ?
+		  0 : TWO_ROW_ADDR_CYCLES__FLAG,
+		  denali->reg + TWO_ROW_ADDR_CYCLES);
+	iowrite32(FIELD_PREP(ECC_CORRECTION__ERASE_THRESHOLD, 1) |
+		  FIELD_PREP(ECC_CORRECTION__VALUE, chip->ecc.strength),
+		  denali->reg + ECC_CORRECTION);
+	iowrite32(chip->ecc.size, denali->reg + CFG_DATA_BLOCK_SIZE);
+	iowrite32(chip->ecc.size, denali->reg + CFG_LAST_DATA_BLOCK_SIZE);
+	iowrite32(chip->ecc.steps, denali->reg + CFG_NUM_DATA_BLOCKS);
+
+	if (chip->options & NAND_KEEP_TIMINGS)
+		return;
+
+	/* update timing registers unless NAND_KEEP_TIMINGS is set */
+	iowrite32(sel->hwhr2_and_we_2_re, denali->reg + TWHR2_AND_WE_2_RE);
+	iowrite32(sel->tcwaw_and_addr_2_data,
+		  denali->reg + TCWAW_AND_ADDR_2_DATA);
+	iowrite32(sel->re_2_we, denali->reg + RE_2_WE);
+	iowrite32(sel->acc_clks, denali->reg + ACC_CLKS);
+	iowrite32(sel->rdwr_en_lo_cnt, denali->reg + RDWR_EN_LO_CNT);
+	iowrite32(sel->rdwr_en_hi_cnt, denali->reg + RDWR_EN_HI_CNT);
+	iowrite32(sel->cs_setup_cnt, denali->reg + CS_SETUP_CNT);
+	iowrite32(sel->re_2_re, denali->reg + RE_2_RE);
 }
 
 static int denali_check_erased_page(struct nand_chip *chip,
-				    struct denali_nand_info *denali, u8 *buf,
+				    struct denali_controller *denali, u8 *buf,
 				    unsigned long uncor_ecc_flags,
 				    unsigned int max_bitflips)
 {
@@ -248,7 +265,7 @@ static int denali_check_erased_page(struct nand_chip *chip,
 }
 
 static int denali_hw_ecc_fixup(struct nand_chip *chip,
-			       struct denali_nand_info *denali,
+			       struct denali_controller *denali,
 			       unsigned long *uncor_ecc_flags)
 {
 	struct mtd_ecc_stats *ecc_stats = &nand_to_mtd(chip)->ecc_stats;
@@ -283,7 +300,7 @@ static int denali_hw_ecc_fixup(struct nand_chip *chip,
 }
 
 static int denali_sw_ecc_fixup(struct nand_chip *chip,
-			       struct denali_nand_info *denali,
+			       struct denali_controller *denali,
 			       unsigned long *uncor_ecc_flags, uint8_t *buf)
 {
 	struct mtd_ecc_stats *ecc_stats = &nand_to_mtd(chip)->ecc_stats;
@@ -356,7 +373,7 @@ static int denali_sw_ecc_fixup(struct nand_chip *chip,
 	return max_bitflips;
 }
 
-static void denali_setup_dma64(struct denali_nand_info *denali,
+static void denali_setup_dma64(struct denali_controller *denali,
 			       dma_addr_t dma_addr, int page, bool write)
 {
 	uint32_t mode;
@@ -381,7 +398,7 @@ static void denali_setup_dma64(struct denali_nand_info *denali,
 	denali->host_write(denali, mode, upper_32_bits(dma_addr));
 }
 
-static void denali_setup_dma32(struct denali_nand_info *denali,
+static void denali_setup_dma32(struct denali_controller *denali,
 			       dma_addr_t dma_addr, int page, bool write)
 {
 	uint32_t mode;
@@ -405,7 +422,7 @@ static void denali_setup_dma32(struct denali_nand_info *denali,
 	denali->host_write(denali, mode | 0x14000, 0x2400);
 }
 
-static int denali_pio_read(struct denali_nand_info *denali, u32 *buf,
+static int denali_pio_read(struct denali_controller *denali, u32 *buf,
 			   size_t size, int page)
 {
 	u32 addr = DENALI_MAP01 | DENALI_BANK(denali) | page;
@@ -432,7 +449,7 @@ static int denali_pio_read(struct denali_nand_info *denali, u32 *buf,
 	return irq_status & ecc_err_mask ? -EBADMSG : 0;
 }
 
-static int denali_pio_write(struct denali_nand_info *denali, const u32 *buf,
+static int denali_pio_write(struct denali_controller *denali, const u32 *buf,
 			    size_t size, int page)
 {
 	u32 addr = DENALI_MAP01 | DENALI_BANK(denali) | page;
@@ -452,7 +469,7 @@ static int denali_pio_write(struct denali_nand_info *denali, const u32 *buf,
 	return 0;
 }
 
-static int denali_pio_xfer(struct denali_nand_info *denali, void *buf,
+static int denali_pio_xfer(struct denali_controller *denali, void *buf,
 			   size_t size, int page, bool write)
 {
 	if (write)
@@ -461,7 +478,7 @@ static int denali_pio_xfer(struct denali_nand_info *denali, void *buf,
 		return denali_pio_read(denali, buf, size, page);
 }
 
-static int denali_dma_xfer(struct denali_nand_info *denali, void *buf,
+static int denali_dma_xfer(struct denali_controller *denali, void *buf,
 			   size_t size, int page, bool write)
 {
 	dma_addr_t dma_addr;
@@ -521,7 +538,7 @@ static int denali_dma_xfer(struct denali_nand_info *denali, void *buf,
 static int denali_data_xfer(struct nand_chip *chip, void *buf, size_t size,
 			    int page, bool raw, bool write)
 {
-	struct denali_nand_info *denali = to_denali(chip);
+	struct denali_controller *denali = to_denali_controller(chip);
 
 	denali_select_target(chip, chip->cur_cs);
 
@@ -554,7 +571,7 @@ typedef int denali_change_column_callback(void *buf, unsigned int offset,
 static int denali_raw_payload_op(struct nand_chip *chip, void *buf,
 				 denali_change_column_callback *cb, void *priv)
 {
-	struct denali_nand_info *denali = to_denali(chip);
+	struct denali_controller *denali = to_denali_controller(chip);
 	struct mtd_info *mtd = nand_to_mtd(chip);
 	struct nand_ecc_ctrl *ecc = &chip->ecc;
 	int writesize = mtd->writesize;
@@ -604,7 +621,7 @@ static int denali_raw_payload_op(struct nand_chip *chip, void *buf,
 static int denali_raw_oob_op(struct nand_chip *chip, void *buf,
 			     denali_change_column_callback *cb, void *priv)
 {
-	struct denali_nand_info *denali = to_denali(chip);
+	struct denali_controller *denali = to_denali_controller(chip);
 	struct mtd_info *mtd = nand_to_mtd(chip);
 	struct nand_ecc_ctrl *ecc = &chip->ecc;
 	int writesize = mtd->writesize;
@@ -661,9 +678,9 @@ static int denali_memcpy_in(void *buf, unsigned int offset, unsigned int len,
 static int denali_read_page_raw(struct nand_chip *chip, uint8_t *buf,
 				int oob_required, int page)
 {
-	struct denali_nand_info *denali = to_denali(chip);
+	struct denali_chip *dchip = to_denali_chip(chip);
 	struct mtd_info *mtd = nand_to_mtd(chip);
-	void *tmp_buf = denali->buf;
+	void *tmp_buf = dchip->buf;
 	size_t size = mtd->writesize + mtd->oobsize;
 	int ret;
 
@@ -698,9 +715,9 @@ static int denali_memcpy_out(void *buf, unsigned int offset, unsigned int len,
 static int denali_write_page_raw(struct nand_chip *chip, const uint8_t *buf,
 				 int oob_required, int page)
 {
-	struct denali_nand_info *denali = to_denali(chip);
+	struct denali_chip *dchip = to_denali_chip(chip);
 	struct mtd_info *mtd = nand_to_mtd(chip);
-	void *tmp_buf = denali->buf;
+	void *tmp_buf = dchip->buf;
 	size_t size = mtd->writesize + mtd->oobsize;
 	int ret;
 
@@ -772,8 +789,8 @@ static int denali_write_oob(struct nand_chip *chip, int page)
 static int denali_read_page(struct nand_chip *chip, uint8_t *buf,
 			    int oob_required, int page)
 {
+	struct denali_controller *denali = to_denali_controller(chip);
 	struct mtd_info *mtd = nand_to_mtd(chip);
-	struct denali_nand_info *denali = mtd_to_denali(mtd);
 	unsigned long uncor_ecc_flags = 0;
 	int stat = 0;
 	int ret;
@@ -814,7 +831,8 @@ static int denali_write_page(struct nand_chip *chip, const uint8_t *buf,
 static int denali_setup_data_interface(struct nand_chip *chip, int chipnr,
 				       const struct nand_data_interface *conf)
 {
-	struct denali_nand_info *denali = mtd_to_denali(nand_to_mtd(chip));
+	struct denali_controller *denali = to_denali_controller(chip);
+	struct denali_chip_sel *sel;
 	const struct nand_sdr_timings *timings;
 	unsigned long t_x, mult_x;
 	int acc_clks, re_2_we, re_2_re, we_2_re, addr_2_data;
@@ -843,6 +861,8 @@ static int denali_setup_data_interface(struct nand_chip *chip, int chipnr,
 	if (chipnr == NAND_DATA_IFACE_CHECK_ONLY)
 		return 0;
 
+	sel = &to_denali_chip(chip)->sels[chipnr];
+
 	/* tREA -> ACC_CLKS */
 	acc_clks = DIV_ROUND_UP(timings->tREA_max, t_x);
 	acc_clks = min_t(int, acc_clks, ACC_CLKS__VALUE);
@@ -850,7 +870,7 @@ static int denali_setup_data_interface(struct nand_chip *chip, int chipnr,
 	tmp = ioread32(denali->reg + ACC_CLKS);
 	tmp &= ~ACC_CLKS__VALUE;
 	tmp |= FIELD_PREP(ACC_CLKS__VALUE, acc_clks);
-	iowrite32(tmp, denali->reg + ACC_CLKS);
+	sel->acc_clks = tmp;
 
 	/* tRWH -> RE_2_WE */
 	re_2_we = DIV_ROUND_UP(timings->tRHW_min, t_x);
@@ -859,7 +879,7 @@ static int denali_setup_data_interface(struct nand_chip *chip, int chipnr,
 	tmp = ioread32(denali->reg + RE_2_WE);
 	tmp &= ~RE_2_WE__VALUE;
 	tmp |= FIELD_PREP(RE_2_WE__VALUE, re_2_we);
-	iowrite32(tmp, denali->reg + RE_2_WE);
+	sel->re_2_we = tmp;
 
 	/* tRHZ -> RE_2_RE */
 	re_2_re = DIV_ROUND_UP(timings->tRHZ_max, t_x);
@@ -868,7 +888,7 @@ static int denali_setup_data_interface(struct nand_chip *chip, int chipnr,
 	tmp = ioread32(denali->reg + RE_2_RE);
 	tmp &= ~RE_2_RE__VALUE;
 	tmp |= FIELD_PREP(RE_2_RE__VALUE, re_2_re);
-	iowrite32(tmp, denali->reg + RE_2_RE);
+	sel->re_2_re = tmp;
 
 	/*
 	 * tCCS, tWHR -> WE_2_RE
@@ -882,7 +902,7 @@ static int denali_setup_data_interface(struct nand_chip *chip, int chipnr,
 	tmp = ioread32(denali->reg + TWHR2_AND_WE_2_RE);
 	tmp &= ~TWHR2_AND_WE_2_RE__WE_2_RE;
 	tmp |= FIELD_PREP(TWHR2_AND_WE_2_RE__WE_2_RE, we_2_re);
-	iowrite32(tmp, denali->reg + TWHR2_AND_WE_2_RE);
+	sel->hwhr2_and_we_2_re = tmp;
 
 	/* tADL -> ADDR_2_DATA */
 
@@ -897,7 +917,7 @@ static int denali_setup_data_interface(struct nand_chip *chip, int chipnr,
 	tmp = ioread32(denali->reg + TCWAW_AND_ADDR_2_DATA);
 	tmp &= ~TCWAW_AND_ADDR_2_DATA__ADDR_2_DATA;
 	tmp |= FIELD_PREP(TCWAW_AND_ADDR_2_DATA__ADDR_2_DATA, addr_2_data);
-	iowrite32(tmp, denali->reg + TCWAW_AND_ADDR_2_DATA);
+	sel->tcwaw_and_addr_2_data = tmp;
 
 	/* tREH, tWH -> RDWR_EN_HI_CNT */
 	rdwr_en_hi = DIV_ROUND_UP(max(timings->tREH_min, timings->tWH_min),
@@ -907,7 +927,7 @@ static int denali_setup_data_interface(struct nand_chip *chip, int chipnr,
 	tmp = ioread32(denali->reg + RDWR_EN_HI_CNT);
 	tmp &= ~RDWR_EN_HI_CNT__VALUE;
 	tmp |= FIELD_PREP(RDWR_EN_HI_CNT__VALUE, rdwr_en_hi);
-	iowrite32(tmp, denali->reg + RDWR_EN_HI_CNT);
+	sel->rdwr_en_hi_cnt = tmp;
 
 	/* tRP, tWP -> RDWR_EN_LO_CNT */
 	rdwr_en_lo = DIV_ROUND_UP(max(timings->tRP_min, timings->tWP_min), t_x);
@@ -920,7 +940,7 @@ static int denali_setup_data_interface(struct nand_chip *chip, int chipnr,
 	tmp = ioread32(denali->reg + RDWR_EN_LO_CNT);
 	tmp &= ~RDWR_EN_LO_CNT__VALUE;
 	tmp |= FIELD_PREP(RDWR_EN_LO_CNT__VALUE, rdwr_en_lo);
-	iowrite32(tmp, denali->reg + RDWR_EN_LO_CNT);
+	sel->rdwr_en_lo_cnt = tmp;
 
 	/* tCS, tCEA -> CS_SETUP_CNT */
 	cs_setup = max3((int)DIV_ROUND_UP(timings->tCS_min, t_x) - rdwr_en_lo,
@@ -931,39 +951,11 @@ static int denali_setup_data_interface(struct nand_chip *chip, int chipnr,
 	tmp = ioread32(denali->reg + CS_SETUP_CNT);
 	tmp &= ~CS_SETUP_CNT__VALUE;
 	tmp |= FIELD_PREP(CS_SETUP_CNT__VALUE, cs_setup);
-	iowrite32(tmp, denali->reg + CS_SETUP_CNT);
+	sel->cs_setup_cnt = tmp;
 
 	return 0;
 }
 
-static void denali_hw_init(struct denali_nand_info *denali)
-{
-	/*
-	 * The REVISION register may not be reliable.  Platforms are allowed to
-	 * override it.
-	 */
-	if (!denali->revision)
-		denali->revision = swab16(ioread32(denali->reg + REVISION));
-
-	/*
-	 * Set how many bytes should be skipped before writing data in OOB.
-	 * If a non-zero value has already been set (by firmware or something),
-	 * just use it.  Otherwise, set the driver default.
-	 */
-	denali->oob_skip_bytes = ioread32(denali->reg + SPARE_AREA_SKIP_BYTES);
-	if (!denali->oob_skip_bytes) {
-		denali->oob_skip_bytes = DENALI_DEFAULT_OOB_SKIP_BYTES;
-		iowrite32(denali->oob_skip_bytes,
-			  denali->reg + SPARE_AREA_SKIP_BYTES);
-	}
-
-	denali_detect_max_banks(denali);
-	iowrite32(0x0F, denali->reg + RB_PIN_ENABLED);
-	iowrite32(CHIP_EN_DONT_CARE__FLAG, denali->reg + CHIP_ENABLE_DONT_CARE);
-
-	iowrite32(0xffff, denali->reg + SPARE_AREA_MARKER);
-}
-
 int denali_calc_ecc_bytes(int step_size, int strength)
 {
 	/* BCH code.  Denali requires ecc.bytes to be multiple of 2 */
@@ -974,10 +966,10 @@ EXPORT_SYMBOL(denali_calc_ecc_bytes);
 static int denali_ooblayout_ecc(struct mtd_info *mtd, int section,
 				struct mtd_oob_region *oobregion)
 {
-	struct denali_nand_info *denali = mtd_to_denali(mtd);
 	struct nand_chip *chip = mtd_to_nand(mtd);
+	struct denali_controller *denali = to_denali_controller(chip);
 
-	if (section)
+	if (section > 0)
 		return -ERANGE;
 
 	oobregion->offset = denali->oob_skip_bytes;
@@ -989,10 +981,10 @@ static int denali_ooblayout_ecc(struct mtd_info *mtd, int section,
 static int denali_ooblayout_free(struct mtd_info *mtd, int section,
 				 struct mtd_oob_region *oobregion)
 {
-	struct denali_nand_info *denali = mtd_to_denali(mtd);
 	struct nand_chip *chip = mtd_to_nand(mtd);
+	struct denali_controller *denali = to_denali_controller(chip);
 
-	if (section)
+	if (section > 0)
 		return -ERANGE;
 
 	oobregion->offset = chip->ecc.total + denali->oob_skip_bytes;
@@ -1008,7 +1000,7 @@ static const struct mtd_ooblayout_ops denali_ooblayout_ops = {
 
 static int denali_multidev_fixup(struct nand_chip *chip)
 {
-	struct denali_nand_info *denali = to_denali(chip);
+	struct denali_controller *denali = to_denali_controller(chip);
 	struct mtd_info *mtd = nand_to_mtd(chip);
 
 	/*
@@ -1059,38 +1051,11 @@ static int denali_multidev_fixup(struct nand_chip *chip)
 
 static int denali_attach_chip(struct nand_chip *chip)
 {
+	struct denali_controller *denali = to_denali_controller(chip);
+	struct denali_chip *dchip = to_denali_chip(chip);
 	struct mtd_info *mtd = nand_to_mtd(chip);
-	struct denali_nand_info *denali = mtd_to_denali(mtd);
 	int ret;
 
-	if (ioread32(denali->reg + FEATURES) & FEATURES__DMA)
-		denali->dma_avail = true;
-
-	if (denali->dma_avail) {
-		int dma_bit = denali->caps & DENALI_CAP_DMA_64BIT ? 64 : 32;
-
-		ret = dma_set_mask(denali->dev, DMA_BIT_MASK(dma_bit));
-		if (ret) {
-			dev_info(denali->dev,
-				 "Failed to set DMA mask. Disabling DMA.\n");
-			denali->dma_avail = false;
-		}
-	}
-
-	if (denali->dma_avail) {
-		chip->options |= NAND_USE_BOUNCE_BUFFER;
-		chip->buf_align = 16;
-		if (denali->caps & DENALI_CAP_DMA_64BIT)
-			denali->setup_dma = denali_setup_dma64;
-		else
-			denali->setup_dma = denali_setup_dma32;
-	}
-
-	chip->bbt_options |= NAND_BBT_USE_FLASH;
-	chip->bbt_options |= NAND_BBT_NO_OOB;
-	chip->ecc.mode = NAND_ECC_HW_SYNDROME;
-	chip->options |= NAND_NO_SUBPAGE_WRITE;
-
 	ret = nand_ecc_choose_conf(chip, denali->ecc_caps,
 				   mtd->oobsize - denali->oob_skip_bytes);
 	if (ret) {
@@ -1102,33 +1067,6 @@ static int denali_attach_chip(struct nand_chip *chip)
 		"chosen ECC settings: step=%d, strength=%d, bytes=%d\n",
 		chip->ecc.size, chip->ecc.strength, chip->ecc.bytes);
 
-	iowrite32(FIELD_PREP(ECC_CORRECTION__ERASE_THRESHOLD, 1) |
-		  FIELD_PREP(ECC_CORRECTION__VALUE, chip->ecc.strength),
-		  denali->reg + ECC_CORRECTION);
-	iowrite32(mtd->erasesize / mtd->writesize,
-		  denali->reg + PAGES_PER_BLOCK);
-	iowrite32(chip->options & NAND_BUSWIDTH_16 ? 1 : 0,
-		  denali->reg + DEVICE_WIDTH);
-	iowrite32(chip->options & NAND_ROW_ADDR_3 ? 0 : TWO_ROW_ADDR_CYCLES__FLAG,
-		  denali->reg + TWO_ROW_ADDR_CYCLES);
-	iowrite32(mtd->writesize, denali->reg + DEVICE_MAIN_AREA_SIZE);
-	iowrite32(mtd->oobsize, denali->reg + DEVICE_SPARE_AREA_SIZE);
-
-	iowrite32(chip->ecc.size, denali->reg + CFG_DATA_BLOCK_SIZE);
-	iowrite32(chip->ecc.size, denali->reg + CFG_LAST_DATA_BLOCK_SIZE);
-	/* chip->ecc.steps is set by nand_scan_tail(); not available here */
-	iowrite32(mtd->writesize / chip->ecc.size,
-		  denali->reg + CFG_NUM_DATA_BLOCKS);
-
-	mtd_set_ooblayout(mtd, &denali_ooblayout_ops);
-
-	chip->ecc.read_page = denali_read_page;
-	chip->ecc.read_page_raw = denali_read_page_raw;
-	chip->ecc.write_page = denali_write_page;
-	chip->ecc.write_page_raw = denali_write_page_raw;
-	chip->ecc.read_oob = denali_read_oob;
-	chip->ecc.write_oob = denali_write_oob;
-
 	ret = denali_multidev_fixup(chip);
 	if (ret)
 		return ret;
@@ -1138,8 +1076,8 @@ static int denali_attach_chip(struct nand_chip *chip)
 	 * use devm_kmalloc() because the memory allocated by devm_ does not
 	 * guarantee DMA-safe alignment.
 	 */
-	denali->buf = kmalloc(mtd->writesize + mtd->oobsize, GFP_KERNEL);
-	if (!denali->buf)
+	dchip->buf = kmalloc(mtd->writesize + mtd->oobsize, GFP_KERNEL);
+	if (!dchip->buf)
 		return -ENOMEM;
 
 	return 0;
@@ -1147,13 +1085,12 @@ static int denali_attach_chip(struct nand_chip *chip)
 
 static void denali_detach_chip(struct nand_chip *chip)
 {
-	struct mtd_info *mtd = nand_to_mtd(chip);
-	struct denali_nand_info *denali = mtd_to_denali(mtd);
+	struct denali_chip *dchip = to_denali_chip(chip);
 
-	kfree(denali->buf);
+	kfree(dchip->buf);
 }
 
-static void denali_exec_in8(struct denali_nand_info *denali, u32 type,
+static void denali_exec_in8(struct denali_controller *denali, u32 type,
 			    u8 *buf, unsigned int len)
 {
 	int i;
@@ -1162,7 +1099,7 @@ static void denali_exec_in8(struct denali_nand_info *denali, u32 type,
 		buf[i] = denali->host_read(denali, type | DENALI_BANK(denali));
 }
 
-static void denali_exec_in16(struct denali_nand_info *denali, u32 type,
+static void denali_exec_in16(struct denali_controller *denali, u32 type,
 			     u8 *buf, unsigned int len)
 {
 	u32 data;
@@ -1176,7 +1113,7 @@ static void denali_exec_in16(struct denali_nand_info *denali, u32 type,
 	}
 }
 
-static void denali_exec_out8(struct denali_nand_info *denali, u32 type,
+static void denali_exec_out8(struct denali_controller *denali, u32 type,
 			     const u8 *buf, unsigned int len)
 {
 	int i;
@@ -1185,7 +1122,7 @@ static void denali_exec_out8(struct denali_nand_info *denali, u32 type,
 		denali->host_write(denali, type | DENALI_BANK(denali), buf[i]);
 }
 
-static void denali_exec_out16(struct denali_nand_info *denali, u32 type,
+static void denali_exec_out16(struct denali_controller *denali, u32 type,
 			      const u8 *buf, unsigned int len)
 {
 	int i;
@@ -1195,7 +1132,7 @@ static void denali_exec_out16(struct denali_nand_info *denali, u32 type,
 				   buf[i + 1] << 16 | buf[i]);
 }
 
-static int denali_exec_waitrdy(struct denali_nand_info *denali)
+static int denali_exec_waitrdy(struct denali_controller *denali)
 {
 	u32 irq_stat;
 
@@ -1211,7 +1148,7 @@ static int denali_exec_waitrdy(struct denali_nand_info *denali)
 static int denali_exec_instr(struct nand_chip *chip,
 			     const struct nand_op_instr *instr)
 {
-	struct denali_nand_info *denali = to_denali(chip);
+	struct denali_controller *denali = to_denali_controller(chip);
 	bool width16 = chip->options & NAND_BUSWIDTH_16;
 
 	switch (instr->type) {
@@ -1262,7 +1199,7 @@ static int denali_exec_op(struct nand_chip *chip,
 	 * Some commands contain NAND_OP_WAITRDY_INSTR.
 	 * irq must be cleared here to catch the R/B# interrupt there.
 	 */
-	denali_reset_irq(to_denali(chip));
+	denali_reset_irq(to_denali_controller(chip));
 
 	for (i = 0; i < op->ninstrs; i++) {
 		ret = denali_exec_instr(chip, &op->instrs[i]);
@@ -1280,53 +1217,80 @@ static const struct nand_controller_ops denali_controller_ops = {
 	.setup_data_interface = denali_setup_data_interface,
 };
 
-int denali_init(struct denali_nand_info *denali)
+int denali_chip_init(struct denali_controller *denali,
+		     struct denali_chip *dchip)
 {
-	struct nand_chip *chip = &denali->nand;
+	struct nand_chip *chip = &dchip->chip;
 	struct mtd_info *mtd = nand_to_mtd(chip);
-	u32 features = ioread32(denali->reg + FEATURES);
-	int ret;
+	struct denali_chip *dchip2;
+	int i, j, ret;
 
-	mtd->dev.parent = denali->dev;
-	denali_hw_init(denali);
+	chip->controller = &denali->controller;
 
-	init_completion(&denali->complete);
-	spin_lock_init(&denali->irq_lock);
+	/* sanity checks for bank numbers */
+	for (i = 0; i < dchip->nsels; i++) {
+		unsigned int bank = dchip->sels[i].bank;
 
-	denali_clear_irq_all(denali);
+		if (bank >= denali->nbanks) {
+			dev_err(denali->dev, "unsupported bank %d\n", bank);
+			return -EINVAL;
+		}
 
-	ret = devm_request_irq(denali->dev, denali->irq, denali_isr,
-			       IRQF_SHARED, DENALI_NAND_NAME, denali);
-	if (ret) {
-		dev_err(denali->dev, "Unable to request IRQ\n");
-		return ret;
-	}
+		for (j = 0; j < i; j++) {
+			if (bank == dchip->sels[j].bank) {
+				dev_err(denali->dev,
+					"bank %d is assigned twice in the same chip\n",
+					bank);
+				return -EINVAL;
+			}
+		}
 
-	denali_enable_irq(denali);
+		list_for_each_entry(dchip2, &denali->chips, node) {
+			for (j = 0; j < dchip2->nsels; j++) {
+				if (bank == dchip2->sels[j].bank) {
+					dev_err(denali->dev,
+						"bank %d is already used\n",
+						bank);
+					return -EINVAL;
+				}
+			}
+		}
+	}
 
-	denali->active_bank = DENALI_INVALID_BANK;
+	mtd->dev.parent = denali->dev;
 
-	nand_set_flash_node(chip, denali->dev->of_node);
-	/* Fallback to the default name if DT did not give "label" property */
-	if (!mtd->name)
+	/*
+	 * Fallback to the default name if DT did not give "label" property.
+	 * Use "label" property if multiple chips are connected.
+	 */
+	if (!mtd->name && list_empty(&denali->chips))
 		mtd->name = "denali-nand";
 
-	if (features & FEATURES__INDEX_ADDR) {
-		denali->host_read = denali_indexed_read;
-		denali->host_write = denali_indexed_write;
-	} else {
-		denali->host_read = denali_direct_read;
-		denali->host_write = denali_direct_write;
+	if (denali->dma_avail) {
+		chip->options |= NAND_USE_BOUNCE_BUFFER;
+		chip->buf_align = 16;
 	}
 
 	/* clk rate info is needed for setup_data_interface */
 	if (!denali->clk_rate || !denali->clk_x_rate)
 		chip->options |= NAND_KEEP_TIMINGS;
 
-	chip->legacy.dummy_controller.ops = &denali_controller_ops;
-	ret = nand_scan(chip, denali->max_banks);
+	chip->bbt_options |= NAND_BBT_USE_FLASH;
+	chip->bbt_options |= NAND_BBT_NO_OOB;
+	chip->options |= NAND_NO_SUBPAGE_WRITE;
+	chip->ecc.mode = NAND_ECC_HW_SYNDROME;
+	chip->ecc.read_page = denali_read_page;
+	chip->ecc.write_page = denali_write_page;
+	chip->ecc.read_page_raw = denali_read_page_raw;
+	chip->ecc.write_page_raw = denali_write_page_raw;
+	chip->ecc.read_oob = denali_read_oob;
+	chip->ecc.write_oob = denali_write_oob;
+
+	mtd_set_ooblayout(mtd, &denali_ooblayout_ops);
+
+	ret = nand_scan(chip, dchip->nsels);
 	if (ret)
-		goto disable_irq;
+		return ret;
 
 	ret = mtd_device_register(mtd, NULL, 0);
 	if (ret) {
@@ -1334,20 +1298,109 @@ int denali_init(struct denali_nand_info *denali)
 		goto cleanup_nand;
 	}
 
+	list_add_tail(&dchip->node, &denali->chips);
+
 	return 0;
 
 cleanup_nand:
 	nand_cleanup(chip);
-disable_irq:
-	denali_disable_irq(denali);
 
 	return ret;
 }
+EXPORT_SYMBOL_GPL(denali_chip_init);
+
+int denali_init(struct denali_controller *denali)
+{
+	u32 features = ioread32(denali->reg + FEATURES);
+	int ret;
+
+	nand_controller_init(&denali->controller);
+	denali->controller.ops = &denali_controller_ops;
+	init_completion(&denali->complete);
+	spin_lock_init(&denali->irq_lock);
+	INIT_LIST_HEAD(&denali->chips);
+	denali->active_bank = DENALI_INVALID_BANK;
+
+	/*
+	 * The REVISION register may not be reliable. Platforms are allowed to
+	 * override it.
+	 */
+	if (!denali->revision)
+		denali->revision = swab16(ioread32(denali->reg + REVISION));
+
+	denali->nbanks = 1 << FIELD_GET(FEATURES__N_BANKS, features);
+
+	/* the encoding changed from rev 5.0 to 5.1 */
+	if (denali->revision < 0x0501)
+		denali->nbanks <<= 1;
+
+	if (features & FEATURES__DMA)
+		denali->dma_avail = true;
+
+	if (denali->dma_avail) {
+		int dma_bit = denali->caps & DENALI_CAP_DMA_64BIT ? 64 : 32;
+
+		ret = dma_set_mask(denali->dev, DMA_BIT_MASK(dma_bit));
+		if (ret) {
+			dev_info(denali->dev,
+				 "Failed to set DMA mask. Disabling DMA.\n");
+			denali->dma_avail = false;
+		}
+	}
+
+	if (denali->dma_avail) {
+		if (denali->caps & DENALI_CAP_DMA_64BIT)
+			denali->setup_dma = denali_setup_dma64;
+		else
+			denali->setup_dma = denali_setup_dma32;
+	}
+
+	if (features & FEATURES__INDEX_ADDR) {
+		denali->host_read = denali_indexed_read;
+		denali->host_write = denali_indexed_write;
+	} else {
+		denali->host_read = denali_direct_read;
+		denali->host_write = denali_direct_write;
+	}
+
+	/*
+	 * Set how many bytes should be skipped before writing data in OOB.
+	 * If a non-zero value has already been set (by firmware or something),
+	 * just use it. Otherwise, set the driver's default.
+	 */
+	denali->oob_skip_bytes = ioread32(denali->reg + SPARE_AREA_SKIP_BYTES);
+	if (!denali->oob_skip_bytes) {
+		denali->oob_skip_bytes = DENALI_DEFAULT_OOB_SKIP_BYTES;
+		iowrite32(denali->oob_skip_bytes,
+			  denali->reg + SPARE_AREA_SKIP_BYTES);
+	}
+
+	iowrite32(GENMASK(denali->nbanks - 1, 0), denali->reg + RB_PIN_ENABLED);
+	iowrite32(CHIP_EN_DONT_CARE__FLAG, denali->reg + CHIP_ENABLE_DONT_CARE);
+	iowrite32(0xffff, denali->reg + SPARE_AREA_MARKER);
+
+	denali_clear_irq_all(denali);
+
+	ret = devm_request_irq(denali->dev, denali->irq, denali_isr,
+			       IRQF_SHARED, DENALI_NAND_NAME, denali);
+	if (ret) {
+		dev_err(denali->dev, "Unable to request IRQ\n");
+		return ret;
+	}
+
+	denali_enable_irq(denali);
+
+	return 0;
+}
 EXPORT_SYMBOL(denali_init);
 
-void denali_remove(struct denali_nand_info *denali)
+void denali_remove(struct denali_controller *denali)
 {
-	nand_release(&denali->nand);
+	struct denali_chip *dchip;
+
+	list_for_each_entry(dchip, &denali->chips, node)
+		nand_release(&dchip->chip);
+
 	denali_disable_irq(denali);
 }
 EXPORT_SYMBOL(denali_remove);
diff --git a/drivers/mtd/nand/raw/denali.h b/drivers/mtd/nand/raw/denali.h
index 8552b3f..138b239 100644
--- a/drivers/mtd/nand/raw/denali.h
+++ b/drivers/mtd/nand/raw/denali.h
@@ -9,6 +9,7 @@
 
 #include <linux/bits.h>
 #include <linux/completion.h>
+#include <linux/list.h>
 #include <linux/mtd/rawnand.h>
 #include <linux/spinlock_types.h>
 #include <linux/types.h>
@@ -290,30 +291,100 @@
 #define     CHNL_ACTIVE__CHANNEL2			BIT(2)
 #define     CHNL_ACTIVE__CHANNEL3			BIT(3)
 
-struct denali_nand_info {
-	struct nand_chip nand;
-	unsigned long clk_rate;		/* core clock rate */
-	unsigned long clk_x_rate;	/* bus interface clock rate */
-	int active_bank;		/* currently selected bank */
+/**
+ * struct denali_chip_sel - per-CS data of Denali NAND
+ *
+ * @bank:                  bank id of the controller this CS is connected to
+ * @hwhr2_and_we_2_re:     value of timing register HWHR2_AND_WE_2_RE
+ * @tcwaw_and_addr_2_data: value of timing register TCWAW_AND_ADDR_2_DATA
+ * @re_2_we:               value of timing register RE_2_WE
+ * @acc_clks:              value of timing register ACC_CLKS
+ * @rdwr_en_lo_cnt:        value of timing register RDWR_EN_LO_CNT
+ * @rdwr_en_hi_cnt:        value of timing register RDWR_EN_HI_CNT
+ * @cs_setup_cnt:          value of timing register CS_SETUP_CNT
+ * @re_2_re:               value of timing register RE_2_RE
+ */
+struct denali_chip_sel {
+	int bank;
+	u32 hwhr2_and_we_2_re;
+	u32 tcwaw_and_addr_2_data;
+	u32 re_2_we;
+	u32 acc_clks;
+	u32 rdwr_en_lo_cnt;
+	u32 rdwr_en_hi_cnt;
+	u32 cs_setup_cnt;
+	u32 re_2_re;
+};
+
+/**
+ * struct denali_chip - per-chip data of Denali NAND
+ *
+ * @chip:  base NAND chip structure
+ * @buf:   internal buffer for syndrome layout conversion
+ * @node:  node to be used to associate this chip with the controller
+ * @nsels: the number of CS lines of this chip
+ * @sels:  the array of per-cs data
+ */
+struct denali_chip {
+	struct nand_chip chip;
+	void *buf;
+	struct list_head node;
+	unsigned int nsels;
+	struct denali_chip_sel sels[0];
+};
+
+/**
+ * struct denali_controller - Denali NAND controller data
+ *
+ * @controller:     base NAND controller structure
+ * @dev:            device
+ * @chips:          the list of chips attached to this controller
+ * @clk_rate:       frequency of core clock
+ * @clk_x_rate:     frequency of bus interface clock
+ * @reg:            base of Register Interface
+ * @host:           base of Host Data/Command interface
+ * @complete:       completion used to wait for interrupts
+ * @irq:            interrupt number
+ * @irq_mask:       interrupt bits the controller is waiting for
+ * @irq_status:     interrupt bits of events that have happened
+ * @irq_lock:       lock to protect @irq_mask and @irq_status
+ * @dma_avail:      set if DMA engine is available
+ * @devs_per_cs:    number of devices connected in parallel
+ * @oob_skip_bytes: number of bytes in OOB skipped by the ECC engine
+ * @active_bank:    active bank id
+ * @nbanks:         the number of banks supported by this controller
+ * @revision:       IP revision
+ * @caps:           controller capabilities that cannot be detected run-time
+ * @ecc_caps:       ECC engine capabilities
+ * @host_read:      callback for read access of Host Data/Command Interface
+ * @host_write:     callback for write access of Host Data/Command Interface
+ * @setup_dma:      callback for setup of the Data DMA
+ */
+struct denali_controller {
+	struct nand_controller controller;
 	struct device *dev;
-	void __iomem *reg;		/* Register Interface */
-	void __iomem *host;		/* Host Data/Command Interface */
+	struct list_head chips;
+	unsigned long clk_rate;
+	unsigned long clk_x_rate;
+	void __iomem *reg;
+	void __iomem *host;
 	struct completion complete;
-	spinlock_t irq_lock;		/* protect irq_mask and irq_status */
-	u32 irq_mask;			/* interrupts we are waiting for */
-	u32 irq_status;			/* interrupts that have happened */
 	int irq;
-	void *buf;			/* for syndrome layout conversion */
-	bool dma_avail;			/* can support DMA? */
-	int devs_per_cs;		/* devices connected in parallel */
-	int oob_skip_bytes;		/* number of bytes reserved for BBM */
-	int max_banks;
-	unsigned int revision;		/* IP revision */
-	unsigned int caps;		/* IP capability (or quirk) */
+	u32 irq_mask;
+	u32 irq_status;
+	spinlock_t irq_lock;
+	bool dma_avail;
+	int devs_per_cs;
+	int oob_skip_bytes;
+	int active_bank;
+	int nbanks;
+	unsigned int revision;
+	unsigned int caps;
 	const struct nand_ecc_caps *ecc_caps;
-	u32 (*host_read)(struct denali_nand_info *denali, u32 addr);
-	void (*host_write)(struct denali_nand_info *denali, u32 addr, u32 data);
-	void (*setup_dma)(struct denali_nand_info *denali, dma_addr_t dma_addr,
+	u32 (*host_read)(struct denali_controller *denali, u32 addr);
+	void (*host_write)(struct denali_controller *denali, u32 addr,
+			   u32 data);
+	void (*setup_dma)(struct denali_controller *denali, dma_addr_t dma_addr,
 			  int page, bool write);
 };
 
@@ -321,7 +392,9 @@ struct denali_nand_info {
 #define DENALI_CAP_DMA_64BIT			BIT(1)
 
 int denali_calc_ecc_bytes(int step_size, int strength);
-int denali_init(struct denali_nand_info *denali);
-void denali_remove(struct denali_nand_info *denali);
+int denali_chip_init(struct denali_controller *denali,
+		     struct denali_chip *dchip);
+int denali_init(struct denali_controller *denali);
+void denali_remove(struct denali_controller *denali);
 
 #endif /* __DENALI_H__ */
diff --git a/drivers/mtd/nand/raw/denali_dt.c b/drivers/mtd/nand/raw/denali_dt.c
index 0b5ae24..5e14836 100644
--- a/drivers/mtd/nand/raw/denali_dt.c
+++ b/drivers/mtd/nand/raw/denali_dt.c
@@ -18,7 +18,7 @@
 #include "denali.h"
 
 struct denali_dt {
-	struct denali_nand_info	denali;
+	struct denali_controller controller;
 	struct clk *clk;	/* core clock */
 	struct clk *clk_x;	/* bus interface clock */
 	struct clk *clk_ecc;	/* ECC circuit clock */
@@ -71,19 +71,92 @@ static const struct of_device_id denali_nand_dt_ids[] = {
 };
 MODULE_DEVICE_TABLE(of, denali_nand_dt_ids);
 
+static int denali_dt_chip_init(struct denali_controller *denali,
+			       struct device_node *chip_np)
+{
+	struct denali_chip *dchip;
+	u32 bank;
+	int nsels, i, ret;
+
+	nsels = of_property_count_u32_elems(chip_np, "reg");
+	if (nsels < 0)
+		return nsels;
+
+	dchip = devm_kzalloc(denali->dev, struct_size(dchip, sels, nsels),
+			     GFP_KERNEL);
+	if (!dchip)
+		return -ENOMEM;
+
+	dchip->nsels = nsels;
+
+	for (i = 0; i < nsels; i++) {
+		ret = of_property_read_u32_index(chip_np, "reg", i, &bank);
+		if (ret)
+			return ret;
+
+		dchip->sels[i].bank = bank;
+
+		nand_set_flash_node(&dchip->chip, chip_np);
+	}
+
+	return denali_chip_init(denali, dchip);
+}
+
+/* Backward compatibility for old platforms */
+static int denali_dt_legacy_chip_init(struct denali_controller *denali)
+{
+	struct denali_chip *dchip;
+	int nsels, i;
+
+	nsels = denali->nbanks;
+
+	dchip = devm_kzalloc(denali->dev, struct_size(dchip, sels, nsels),
+			     GFP_KERNEL);
+	if (!dchip)
+		return -ENOMEM;
+
+	dchip->nsels = nsels;
+
+	for (i = 0; i < nsels; i++)
+		dchip->sels[i].bank = i;
+
+	nand_set_flash_node(&dchip->chip, denali->dev->of_node);
+
+	return denali_chip_init(denali, dchip);
+}
+
+/*
+ * Check the DT binding.
+ * The new binding expects chip subnodes in the controller node.
+ * So, #address-cells = <1>; #size-cells = <0>; are required.
+ * Check the #size-cells to distinguish the binding.
+ */
+static bool denali_dt_is_legacy_binding(struct device_node *np)
+{
+	u32 cells;
+	int ret;
+
+	ret = of_property_read_u32(np, "#size-cells", &cells);
+	if (ret)
+		return true;
+
+	return cells != 0;
+}
+
 static int denali_dt_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
 	struct resource *res;
 	struct denali_dt *dt;
 	const struct denali_dt_data *data;
-	struct denali_nand_info *denali;
+	struct denali_controller *denali;
+	struct device_node *np;
 	int ret;
 
 	dt = devm_kzalloc(dev, sizeof(*dt), GFP_KERNEL);
 	if (!dt)
 		return -ENOMEM;
-	denali = &dt->denali;
+	denali = &dt->controller;
 
 	data = of_device_get_match_data(dev);
 	if (data) {
@@ -140,9 +213,26 @@ static int denali_dt_probe(struct platform_device *pdev)
 	if (ret)
 		goto out_disable_clk_ecc;
 
+	if (denali_dt_is_legacy_binding(dev->of_node)) {
+		ret = denali_dt_legacy_chip_init(denali);
+		if (ret)
+			goto out_remove_denali;
+	} else {
+		for_each_child_of_node(dev->of_node, np) {
+			ret = denali_dt_chip_init(denali, np);
+			if (ret) {
+				of_node_put(np);
+				goto out_remove_denali;
+			}
+		}
+	}
+
 	platform_set_drvdata(pdev, dt);
+
 	return 0;
 
+out_remove_denali:
+	denali_remove(denali);
 out_disable_clk_ecc:
 	clk_disable_unprepare(dt->clk_ecc);
 out_disable_clk_x:
@@ -157,7 +247,7 @@ static int denali_dt_remove(struct platform_device *pdev)
 {
 	struct denali_dt *dt = platform_get_drvdata(pdev);
 
-	denali_remove(&dt->denali);
+	denali_remove(&dt->controller);
 	clk_disable_unprepare(dt->clk_ecc);
 	clk_disable_unprepare(dt->clk_x);
 	clk_disable_unprepare(dt->clk);
diff --git a/drivers/mtd/nand/raw/denali_pci.c b/drivers/mtd/nand/raw/denali_pci.c
index 02eb599..d62aa52 100644
--- a/drivers/mtd/nand/raw/denali_pci.c
+++ b/drivers/mtd/nand/raw/denali_pci.c
@@ -29,10 +29,11 @@ NAND_ECC_CAPS_SINGLE(denali_pci_ecc_caps, denali_calc_ecc_bytes, 512, 8, 15);
 
 static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
 {
-	int ret;
 	resource_size_t csr_base, mem_base;
 	unsigned long csr_len, mem_len;
-	struct denali_nand_info *denali;
+	struct denali_controller *denali;
+	struct denali_chip *dchip;
+	int nsels, ret, i;
 
 	denali = devm_kzalloc(&dev->dev, sizeof(*denali), GFP_KERNEL);
 	if (!denali)
@@ -64,7 +65,6 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
 	denali->dev = &dev->dev;
 	denali->irq = dev->irq;
 	denali->ecc_caps = &denali_pci_ecc_caps;
-	denali->nand.ecc.options |= NAND_ECC_MAXIMIZE;
 	denali->clk_rate = 50000000;		/* 50 MHz */
 	denali->clk_x_rate = 200000000;		/* 200 MHz */
 
@@ -91,10 +91,32 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
 	if (ret)
 		goto out_unmap_host;
 
+	nsels = denali->nbanks;
+
+	dchip = devm_kzalloc(denali->dev, struct_size(dchip, sels, nsels),
+			     GFP_KERNEL);
+	if (!dchip) {
+		ret = -ENOMEM;
+		goto out_remove_denali;
+	}
+
+	dchip->chip.ecc.options |= NAND_ECC_MAXIMIZE;
+
+	dchip->nsels = nsels;
+
+	for (i = 0; i < nsels; i++)
+		dchip->sels[i].bank = i;
+
+	ret = denali_chip_init(denali, dchip);
+	if (ret)
+		goto out_remove_denali;
+
 	pci_set_drvdata(dev, denali);
 
 	return 0;
 
+out_remove_denali:
+	denali_remove(denali);
 out_unmap_host:
 	iounmap(denali->host);
 out_unmap_reg:
@@ -104,7 +126,7 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
 
 static void denali_pci_remove(struct pci_dev *dev)
 {
-	struct denali_nand_info *denali = pci_get_drvdata(dev);
+	struct denali_controller *denali = pci_get_drvdata(dev);
 
 	denali_remove(denali);
 	iounmap(denali->reg);
-- 
2.7.4


______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/

^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH v3 8/9] mtd: rawnand: denali: remove DENALI_NR_BANKS macro
  2019-03-12  8:44 ` Masahiro Yamada
@ 2019-03-12  8:44   ` Masahiro Yamada
  -1 siblings, 0 replies; 40+ messages in thread
From: Masahiro Yamada @ 2019-03-12  8:44 UTC (permalink / raw)
  To: linux-mtd, Miquel Raynal
  Cc: Boris Brezillon, Masahiro Yamada, Brian Norris, linux-kernel,
	Marek Vasut, Richard Weinberger, David Woodhouse

Use the runtime-detected denali->nbanks instead of hard-coded
DENALI_NR_BANKS (=4).

The actual number of banks depends on the IP configuration, and
can be less than DENALI_NR_BANKS. It is pointless to touch
registers of unsupported banks.

Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
---

Changes in v3: None
Changes in v2: None

 drivers/mtd/nand/raw/denali.c | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/drivers/mtd/nand/raw/denali.c b/drivers/mtd/nand/raw/denali.c
index 0c47c56..b1a4d9c 100644
--- a/drivers/mtd/nand/raw/denali.c
+++ b/drivers/mtd/nand/raw/denali.c
@@ -40,7 +40,6 @@
 #define DENALI_BANK(denali)	((denali)->active_bank << 24)
 
 #define DENALI_INVALID_BANK	-1
-#define DENALI_NR_BANKS		4
 
 static struct denali_chip *to_denali_chip(struct nand_chip *chip)
 {
@@ -92,7 +91,7 @@ static void denali_enable_irq(struct denali_controller *denali)
 {
 	int i;
 
-	for (i = 0; i < DENALI_NR_BANKS; i++)
+	for (i = 0; i < denali->nbanks; i++)
 		iowrite32(U32_MAX, denali->reg + INTR_EN(i));
 	iowrite32(GLOBAL_INT_EN_FLAG, denali->reg + GLOBAL_INT_ENABLE);
 }
@@ -101,7 +100,7 @@ static void denali_disable_irq(struct denali_controller *denali)
 {
 	int i;
 
-	for (i = 0; i < DENALI_NR_BANKS; i++)
+	for (i = 0; i < denali->nbanks; i++)
 		iowrite32(0, denali->reg + INTR_EN(i));
 	iowrite32(0, denali->reg + GLOBAL_INT_ENABLE);
 }
@@ -117,7 +116,7 @@ static void denali_clear_irq_all(struct denali_controller *denali)
 {
 	int i;
 
-	for (i = 0; i < DENALI_NR_BANKS; i++)
+	for (i = 0; i < denali->nbanks; i++)
 		denali_clear_irq(denali, i, U32_MAX);
 }
 
@@ -130,7 +129,7 @@ static irqreturn_t denali_isr(int irq, void *dev_id)
 
 	spin_lock(&denali->irq_lock);
 
-	for (i = 0; i < DENALI_NR_BANKS; i++) {
+	for (i = 0; i < denali->nbanks; i++) {
 		irq_status = ioread32(denali->reg + INTR_STATUS(i));
 		if (irq_status)
 			ret = IRQ_HANDLED;
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH v3 8/9] mtd: rawnand: denali: remove DENALI_NR_BANKS macro
@ 2019-03-12  8:44   ` Masahiro Yamada
  0 siblings, 0 replies; 40+ messages in thread
From: Masahiro Yamada @ 2019-03-12  8:44 UTC (permalink / raw)
  To: linux-mtd, Miquel Raynal
  Cc: Masahiro Yamada, Richard Weinberger, Boris Brezillon,
	linux-kernel, Marek Vasut, Brian Norris, David Woodhouse

Use the runtime-detected denali->nbanks instead of hard-coded
DENALI_NR_BANKS (=4).

The actual number of banks depends on the IP configuration, and
can be less than DENALI_NR_BANKS. It is pointless to touch
registers of unsupported banks.

Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
---

Changes in v3: None
Changes in v2: None

 drivers/mtd/nand/raw/denali.c | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/drivers/mtd/nand/raw/denali.c b/drivers/mtd/nand/raw/denali.c
index 0c47c56..b1a4d9c 100644
--- a/drivers/mtd/nand/raw/denali.c
+++ b/drivers/mtd/nand/raw/denali.c
@@ -40,7 +40,6 @@
 #define DENALI_BANK(denali)	((denali)->active_bank << 24)
 
 #define DENALI_INVALID_BANK	-1
-#define DENALI_NR_BANKS		4
 
 static struct denali_chip *to_denali_chip(struct nand_chip *chip)
 {
@@ -92,7 +91,7 @@ static void denali_enable_irq(struct denali_controller *denali)
 {
 	int i;
 
-	for (i = 0; i < DENALI_NR_BANKS; i++)
+	for (i = 0; i < denali->nbanks; i++)
 		iowrite32(U32_MAX, denali->reg + INTR_EN(i));
 	iowrite32(GLOBAL_INT_EN_FLAG, denali->reg + GLOBAL_INT_ENABLE);
 }
@@ -101,7 +100,7 @@ static void denali_disable_irq(struct denali_controller *denali)
 {
 	int i;
 
-	for (i = 0; i < DENALI_NR_BANKS; i++)
+	for (i = 0; i < denali->nbanks; i++)
 		iowrite32(0, denali->reg + INTR_EN(i));
 	iowrite32(0, denali->reg + GLOBAL_INT_ENABLE);
 }
@@ -117,7 +116,7 @@ static void denali_clear_irq_all(struct denali_controller *denali)
 {
 	int i;
 
-	for (i = 0; i < DENALI_NR_BANKS; i++)
+	for (i = 0; i < denali->nbanks; i++)
 		denali_clear_irq(denali, i, U32_MAX);
 }
 
@@ -130,7 +129,7 @@ static irqreturn_t denali_isr(int irq, void *dev_id)
 
 	spin_lock(&denali->irq_lock);
 
-	for (i = 0; i < DENALI_NR_BANKS; i++) {
+	for (i = 0; i < denali->nbanks; i++) {
 		irq_status = ioread32(denali->reg + INTR_STATUS(i));
 		if (irq_status)
 			ret = IRQ_HANDLED;
-- 
2.7.4


______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/

^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH v3 9/9] mtd: rawnand: denali: clean up coding style
  2019-03-12  8:44 ` Masahiro Yamada
@ 2019-03-12  8:44   ` Masahiro Yamada
  -1 siblings, 0 replies; 40+ messages in thread
From: Masahiro Yamada @ 2019-03-12  8:44 UTC (permalink / raw)
  To: linux-mtd, Miquel Raynal
  Cc: Boris Brezillon, Masahiro Yamada, Brian Norris, linux-kernel,
	Marek Vasut, Richard Weinberger, David Woodhouse

Eliminate the following reports from 'scripts/checkpatch.pl --strict'.

  CHECK: Prefer kernel type 'u8' over 'uint8_t'
  CHECK: Prefer kernel type 'u32' over 'uint32_t'
  CHECK: Alignment should match open parenthesis

I slightly changed denali_check_erased_page() to make it shorter.

Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
---

Changes in v3: None
Changes in v2: None

 drivers/mtd/nand/raw/denali.c | 53 ++++++++++++++++++++-----------------------
 1 file changed, 25 insertions(+), 28 deletions(-)

diff --git a/drivers/mtd/nand/raw/denali.c b/drivers/mtd/nand/raw/denali.c
index b1a4d9c..9c30e744 100644
--- a/drivers/mtd/nand/raw/denali.c
+++ b/drivers/mtd/nand/raw/denali.c
@@ -124,7 +124,7 @@ static irqreturn_t denali_isr(int irq, void *dev_id)
 {
 	struct denali_controller *denali = dev_id;
 	irqreturn_t ret = IRQ_NONE;
-	uint32_t irq_status;
+	u32 irq_status;
 	int i;
 
 	spin_lock(&denali->irq_lock);
@@ -163,7 +163,7 @@ static void denali_reset_irq(struct denali_controller *denali)
 static u32 denali_wait_for_irq(struct denali_controller *denali, u32 irq_mask)
 {
 	unsigned long time_left, flags;
-	uint32_t irq_status;
+	u32 irq_status;
 
 	spin_lock_irqsave(&denali->irq_lock, flags);
 
@@ -235,20 +235,17 @@ static int denali_check_erased_page(struct nand_chip *chip,
 				    unsigned int max_bitflips)
 {
 	struct mtd_ecc_stats *ecc_stats = &nand_to_mtd(chip)->ecc_stats;
-	uint8_t *ecc_code = chip->oob_poi + denali->oob_skip_bytes;
-	int ecc_steps = chip->ecc.steps;
-	int ecc_size = chip->ecc.size;
-	int ecc_bytes = chip->ecc.bytes;
+	struct nand_ecc_ctrl *ecc = &chip->ecc;
+	u8 *ecc_code = chip->oob_poi + denali->oob_skip_bytes;
 	int i, stat;
 
-	for (i = 0; i < ecc_steps; i++) {
+	for (i = 0; i < ecc->steps; i++) {
 		if (!(uncor_ecc_flags & BIT(i)))
 			continue;
 
-		stat = nand_check_erased_ecc_chunk(buf, ecc_size,
-						  ecc_code, ecc_bytes,
-						  NULL, 0,
-						  chip->ecc.strength);
+		stat = nand_check_erased_ecc_chunk(buf, ecc->size, ecc_code,
+						   ecc->bytes, NULL, 0,
+						   ecc->strength);
 		if (stat < 0) {
 			ecc_stats->failed++;
 		} else {
@@ -256,8 +253,8 @@ static int denali_check_erased_page(struct nand_chip *chip,
 			max_bitflips = max_t(unsigned int, max_bitflips, stat);
 		}
 
-		buf += ecc_size;
-		ecc_code += ecc_bytes;
+		buf += ecc->size;
+		ecc_code += ecc->bytes;
 	}
 
 	return max_bitflips;
@@ -269,7 +266,7 @@ static int denali_hw_ecc_fixup(struct nand_chip *chip,
 {
 	struct mtd_ecc_stats *ecc_stats = &nand_to_mtd(chip)->ecc_stats;
 	int bank = denali->active_bank;
-	uint32_t ecc_cor;
+	u32 ecc_cor;
 	unsigned int max_bitflips;
 
 	ecc_cor = ioread32(denali->reg + ECC_COR_INFO(bank));
@@ -300,17 +297,17 @@ static int denali_hw_ecc_fixup(struct nand_chip *chip,
 
 static int denali_sw_ecc_fixup(struct nand_chip *chip,
 			       struct denali_controller *denali,
-			       unsigned long *uncor_ecc_flags, uint8_t *buf)
+			       unsigned long *uncor_ecc_flags, u8 *buf)
 {
 	struct mtd_ecc_stats *ecc_stats = &nand_to_mtd(chip)->ecc_stats;
 	unsigned int ecc_size = chip->ecc.size;
 	unsigned int bitflips = 0;
 	unsigned int max_bitflips = 0;
-	uint32_t err_addr, err_cor_info;
+	u32 err_addr, err_cor_info;
 	unsigned int err_byte, err_sector, err_device;
-	uint8_t err_cor_value;
+	u8 err_cor_value;
 	unsigned int prev_sector = 0;
-	uint32_t irq_status;
+	u32 irq_status;
 
 	denali_reset_irq(denali);
 
@@ -375,7 +372,7 @@ static int denali_sw_ecc_fixup(struct nand_chip *chip,
 static void denali_setup_dma64(struct denali_controller *denali,
 			       dma_addr_t dma_addr, int page, bool write)
 {
-	uint32_t mode;
+	u32 mode;
 	const int page_count = 1;
 
 	mode = DENALI_MAP10 | DENALI_BANK(denali) | page;
@@ -400,7 +397,7 @@ static void denali_setup_dma64(struct denali_controller *denali,
 static void denali_setup_dma32(struct denali_controller *denali,
 			       dma_addr_t dma_addr, int page, bool write)
 {
-	uint32_t mode;
+	u32 mode;
 	const int page_count = 1;
 
 	mode = DENALI_MAP10 | DENALI_BANK(denali);
@@ -425,7 +422,7 @@ static int denali_pio_read(struct denali_controller *denali, u32 *buf,
 			   size_t size, int page)
 {
 	u32 addr = DENALI_MAP01 | DENALI_BANK(denali) | page;
-	uint32_t irq_status, ecc_err_mask;
+	u32 irq_status, ecc_err_mask;
 	int i;
 
 	if (denali->caps & DENALI_CAP_HW_ECC_FIXUP)
@@ -452,7 +449,7 @@ static int denali_pio_write(struct denali_controller *denali, const u32 *buf,
 			    size_t size, int page)
 {
 	u32 addr = DENALI_MAP01 | DENALI_BANK(denali) | page;
-	uint32_t irq_status;
+	u32 irq_status;
 	int i;
 
 	denali_reset_irq(denali);
@@ -481,7 +478,7 @@ static int denali_dma_xfer(struct denali_controller *denali, void *buf,
 			   size_t size, int page, bool write)
 {
 	dma_addr_t dma_addr;
-	uint32_t irq_mask, irq_status, ecc_err_mask;
+	u32 irq_mask, irq_status, ecc_err_mask;
 	enum dma_data_direction dir = write ? DMA_TO_DEVICE : DMA_FROM_DEVICE;
 	int ret = 0;
 
@@ -674,7 +671,7 @@ static int denali_memcpy_in(void *buf, unsigned int offset, unsigned int len,
 	return 0;
 }
 
-static int denali_read_page_raw(struct nand_chip *chip, uint8_t *buf,
+static int denali_read_page_raw(struct nand_chip *chip, u8 *buf,
 				int oob_required, int page)
 {
 	struct denali_chip *dchip = to_denali_chip(chip);
@@ -711,7 +708,7 @@ static int denali_memcpy_out(void *buf, unsigned int offset, unsigned int len,
 	return 0;
 }
 
-static int denali_write_page_raw(struct nand_chip *chip, const uint8_t *buf,
+static int denali_write_page_raw(struct nand_chip *chip, const u8 *buf,
 				 int oob_required, int page)
 {
 	struct denali_chip *dchip = to_denali_chip(chip);
@@ -785,7 +782,7 @@ static int denali_write_oob(struct nand_chip *chip, int page)
 	return nand_prog_page_end_op(chip);
 }
 
-static int denali_read_page(struct nand_chip *chip, uint8_t *buf,
+static int denali_read_page(struct nand_chip *chip, u8 *buf,
 			    int oob_required, int page)
 {
 	struct denali_controller *denali = to_denali_controller(chip);
@@ -818,7 +815,7 @@ static int denali_read_page(struct nand_chip *chip, uint8_t *buf,
 	return stat;
 }
 
-static int denali_write_page(struct nand_chip *chip, const uint8_t *buf,
+static int denali_write_page(struct nand_chip *chip, const u8 *buf,
 			     int oob_required, int page)
 {
 	struct mtd_info *mtd = nand_to_mtd(chip);
@@ -837,7 +834,7 @@ static int denali_setup_data_interface(struct nand_chip *chip, int chipnr,
 	int acc_clks, re_2_we, re_2_re, we_2_re, addr_2_data;
 	int rdwr_en_lo, rdwr_en_hi, rdwr_en_lo_hi, cs_setup;
 	int addr_2_data_mask;
-	uint32_t tmp;
+	u32 tmp;
 
 	timings = nand_get_sdr_timings(conf);
 	if (IS_ERR(timings))
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH v3 9/9] mtd: rawnand: denali: clean up coding style
@ 2019-03-12  8:44   ` Masahiro Yamada
  0 siblings, 0 replies; 40+ messages in thread
From: Masahiro Yamada @ 2019-03-12  8:44 UTC (permalink / raw)
  To: linux-mtd, Miquel Raynal
  Cc: Masahiro Yamada, Richard Weinberger, Boris Brezillon,
	linux-kernel, Marek Vasut, Brian Norris, David Woodhouse

Eliminate the following reports from 'scripts/checkpatch.pl --strict'.

  CHECK: Prefer kernel type 'u8' over 'uint8_t'
  CHECK: Prefer kernel type 'u32' over 'uint32_t'
  CHECK: Alignment should match open parenthesis

I slightly changed denali_check_erased_page() to make it shorter.

Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
---

Changes in v3: None
Changes in v2: None

 drivers/mtd/nand/raw/denali.c | 53 ++++++++++++++++++++-----------------------
 1 file changed, 25 insertions(+), 28 deletions(-)

diff --git a/drivers/mtd/nand/raw/denali.c b/drivers/mtd/nand/raw/denali.c
index b1a4d9c..9c30e744 100644
--- a/drivers/mtd/nand/raw/denali.c
+++ b/drivers/mtd/nand/raw/denali.c
@@ -124,7 +124,7 @@ static irqreturn_t denali_isr(int irq, void *dev_id)
 {
 	struct denali_controller *denali = dev_id;
 	irqreturn_t ret = IRQ_NONE;
-	uint32_t irq_status;
+	u32 irq_status;
 	int i;
 
 	spin_lock(&denali->irq_lock);
@@ -163,7 +163,7 @@ static void denali_reset_irq(struct denali_controller *denali)
 static u32 denali_wait_for_irq(struct denali_controller *denali, u32 irq_mask)
 {
 	unsigned long time_left, flags;
-	uint32_t irq_status;
+	u32 irq_status;
 
 	spin_lock_irqsave(&denali->irq_lock, flags);
 
@@ -235,20 +235,17 @@ static int denali_check_erased_page(struct nand_chip *chip,
 				    unsigned int max_bitflips)
 {
 	struct mtd_ecc_stats *ecc_stats = &nand_to_mtd(chip)->ecc_stats;
-	uint8_t *ecc_code = chip->oob_poi + denali->oob_skip_bytes;
-	int ecc_steps = chip->ecc.steps;
-	int ecc_size = chip->ecc.size;
-	int ecc_bytes = chip->ecc.bytes;
+	struct nand_ecc_ctrl *ecc = &chip->ecc;
+	u8 *ecc_code = chip->oob_poi + denali->oob_skip_bytes;
 	int i, stat;
 
-	for (i = 0; i < ecc_steps; i++) {
+	for (i = 0; i < ecc->steps; i++) {
 		if (!(uncor_ecc_flags & BIT(i)))
 			continue;
 
-		stat = nand_check_erased_ecc_chunk(buf, ecc_size,
-						  ecc_code, ecc_bytes,
-						  NULL, 0,
-						  chip->ecc.strength);
+		stat = nand_check_erased_ecc_chunk(buf, ecc->size, ecc_code,
+						   ecc->bytes, NULL, 0,
+						   ecc->strength);
 		if (stat < 0) {
 			ecc_stats->failed++;
 		} else {
@@ -256,8 +253,8 @@ static int denali_check_erased_page(struct nand_chip *chip,
 			max_bitflips = max_t(unsigned int, max_bitflips, stat);
 		}
 
-		buf += ecc_size;
-		ecc_code += ecc_bytes;
+		buf += ecc->size;
+		ecc_code += ecc->bytes;
 	}
 
 	return max_bitflips;
@@ -269,7 +266,7 @@ static int denali_hw_ecc_fixup(struct nand_chip *chip,
 {
 	struct mtd_ecc_stats *ecc_stats = &nand_to_mtd(chip)->ecc_stats;
 	int bank = denali->active_bank;
-	uint32_t ecc_cor;
+	u32 ecc_cor;
 	unsigned int max_bitflips;
 
 	ecc_cor = ioread32(denali->reg + ECC_COR_INFO(bank));
@@ -300,17 +297,17 @@ static int denali_hw_ecc_fixup(struct nand_chip *chip,
 
 static int denali_sw_ecc_fixup(struct nand_chip *chip,
 			       struct denali_controller *denali,
-			       unsigned long *uncor_ecc_flags, uint8_t *buf)
+			       unsigned long *uncor_ecc_flags, u8 *buf)
 {
 	struct mtd_ecc_stats *ecc_stats = &nand_to_mtd(chip)->ecc_stats;
 	unsigned int ecc_size = chip->ecc.size;
 	unsigned int bitflips = 0;
 	unsigned int max_bitflips = 0;
-	uint32_t err_addr, err_cor_info;
+	u32 err_addr, err_cor_info;
 	unsigned int err_byte, err_sector, err_device;
-	uint8_t err_cor_value;
+	u8 err_cor_value;
 	unsigned int prev_sector = 0;
-	uint32_t irq_status;
+	u32 irq_status;
 
 	denali_reset_irq(denali);
 
@@ -375,7 +372,7 @@ static int denali_sw_ecc_fixup(struct nand_chip *chip,
 static void denali_setup_dma64(struct denali_controller *denali,
 			       dma_addr_t dma_addr, int page, bool write)
 {
-	uint32_t mode;
+	u32 mode;
 	const int page_count = 1;
 
 	mode = DENALI_MAP10 | DENALI_BANK(denali) | page;
@@ -400,7 +397,7 @@ static void denali_setup_dma64(struct denali_controller *denali,
 static void denali_setup_dma32(struct denali_controller *denali,
 			       dma_addr_t dma_addr, int page, bool write)
 {
-	uint32_t mode;
+	u32 mode;
 	const int page_count = 1;
 
 	mode = DENALI_MAP10 | DENALI_BANK(denali);
@@ -425,7 +422,7 @@ static int denali_pio_read(struct denali_controller *denali, u32 *buf,
 			   size_t size, int page)
 {
 	u32 addr = DENALI_MAP01 | DENALI_BANK(denali) | page;
-	uint32_t irq_status, ecc_err_mask;
+	u32 irq_status, ecc_err_mask;
 	int i;
 
 	if (denali->caps & DENALI_CAP_HW_ECC_FIXUP)
@@ -452,7 +449,7 @@ static int denali_pio_write(struct denali_controller *denali, const u32 *buf,
 			    size_t size, int page)
 {
 	u32 addr = DENALI_MAP01 | DENALI_BANK(denali) | page;
-	uint32_t irq_status;
+	u32 irq_status;
 	int i;
 
 	denali_reset_irq(denali);
@@ -481,7 +478,7 @@ static int denali_dma_xfer(struct denali_controller *denali, void *buf,
 			   size_t size, int page, bool write)
 {
 	dma_addr_t dma_addr;
-	uint32_t irq_mask, irq_status, ecc_err_mask;
+	u32 irq_mask, irq_status, ecc_err_mask;
 	enum dma_data_direction dir = write ? DMA_TO_DEVICE : DMA_FROM_DEVICE;
 	int ret = 0;
 
@@ -674,7 +671,7 @@ static int denali_memcpy_in(void *buf, unsigned int offset, unsigned int len,
 	return 0;
 }
 
-static int denali_read_page_raw(struct nand_chip *chip, uint8_t *buf,
+static int denali_read_page_raw(struct nand_chip *chip, u8 *buf,
 				int oob_required, int page)
 {
 	struct denali_chip *dchip = to_denali_chip(chip);
@@ -711,7 +708,7 @@ static int denali_memcpy_out(void *buf, unsigned int offset, unsigned int len,
 	return 0;
 }
 
-static int denali_write_page_raw(struct nand_chip *chip, const uint8_t *buf,
+static int denali_write_page_raw(struct nand_chip *chip, const u8 *buf,
 				 int oob_required, int page)
 {
 	struct denali_chip *dchip = to_denali_chip(chip);
@@ -785,7 +782,7 @@ static int denali_write_oob(struct nand_chip *chip, int page)
 	return nand_prog_page_end_op(chip);
 }
 
-static int denali_read_page(struct nand_chip *chip, uint8_t *buf,
+static int denali_read_page(struct nand_chip *chip, u8 *buf,
 			    int oob_required, int page)
 {
 	struct denali_controller *denali = to_denali_controller(chip);
@@ -818,7 +815,7 @@ static int denali_read_page(struct nand_chip *chip, uint8_t *buf,
 	return stat;
 }
 
-static int denali_write_page(struct nand_chip *chip, const uint8_t *buf,
+static int denali_write_page(struct nand_chip *chip, const u8 *buf,
 			     int oob_required, int page)
 {
 	struct mtd_info *mtd = nand_to_mtd(chip);
@@ -837,7 +834,7 @@ static int denali_setup_data_interface(struct nand_chip *chip, int chipnr,
 	int acc_clks, re_2_we, re_2_re, we_2_re, addr_2_data;
 	int rdwr_en_lo, rdwr_en_hi, rdwr_en_lo_hi, cs_setup;
 	int addr_2_data_mask;
-	uint32_t tmp;
+	u32 tmp;
 
 	timings = nand_get_sdr_timings(conf);
 	if (IS_ERR(timings))
-- 
2.7.4


______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/

^ permalink raw reply related	[flat|nested] 40+ messages in thread

* Re: [PATCH v3 4/9] mtd: rawnand: denali: switch over to ->exec_op() from legacy hooks
  2019-03-12  8:44   ` Masahiro Yamada
@ 2019-03-12  9:02     ` Boris Brezillon
  -1 siblings, 0 replies; 40+ messages in thread
From: Boris Brezillon @ 2019-03-12  9:02 UTC (permalink / raw)
  To: Masahiro Yamada
  Cc: linux-mtd, Miquel Raynal, Boris Brezillon, Brian Norris,
	linux-kernel, Marek Vasut, Richard Weinberger, David Woodhouse

On Tue, 12 Mar 2019 17:44:45 +0900
Masahiro Yamada <yamada.masahiro@socionext.com> wrote:


> +
> +static int denali_exec_instr(struct nand_chip *chip,
> +			     const struct nand_op_instr *instr)
> +{
> +	struct denali_nand_info *denali = to_denali(chip);
> +	bool width16 = chip->options & NAND_BUSWIDTH_16;
> +
> +	switch (instr->type) {
> +	case NAND_OP_CMD_INSTR:
> +		denali_exec_out8(denali, DENALI_MAP11_CMD,
> +				 &instr->ctx.cmd.opcode, 1);
> +		return 0;
> +	case NAND_OP_ADDR_INSTR:
> +		denali_exec_out8(denali, DENALI_MAP11_ADDR,
> +				 instr->ctx.addr.addrs,
> +				 instr->ctx.addr.naddrs);
> +		return 0;
> +	case NAND_OP_DATA_IN_INSTR:
> +		(!instr->ctx.data.force_8bit && width16 ?
> +		 denali_exec_in16 :
> +		 denali_exec_in8)(denali, DENALI_MAP11_DATA,
> +				  instr->ctx.data.buf.in,
> +				  instr->ctx.data.len);

I agree with Miquel, this statement tends to obfuscate the code, and
it's not like an extra if will make a huge difference in term of LOC.
	

> +		return 0;
> +	case NAND_OP_DATA_OUT_INSTR:
> +		(!instr->ctx.data.force_8bit && width16 ?
> +		 denali_exec_out16 :
> +		 denali_exec_out8)(denali, DENALI_MAP11_DATA,
> +				   instr->ctx.data.buf.out,
> +				   instr->ctx.data.len);

Ditto.

> +		return 0;
> +	case NAND_OP_WAITRDY_INSTR:
> +		return denali_exec_waitrdy(denali);
> +	default:
> +		WARN_ONCE(1, "unsupported NAND instruction type: %d\n",
> +			  instr->type);
> +
> +		return -EINVAL;
> +	}
> +}

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v3 4/9] mtd: rawnand: denali: switch over to ->exec_op() from legacy hooks
@ 2019-03-12  9:02     ` Boris Brezillon
  0 siblings, 0 replies; 40+ messages in thread
From: Boris Brezillon @ 2019-03-12  9:02 UTC (permalink / raw)
  To: Masahiro Yamada
  Cc: Boris Brezillon, Richard Weinberger, linux-kernel, Marek Vasut,
	linux-mtd, Miquel Raynal, Brian Norris, David Woodhouse

On Tue, 12 Mar 2019 17:44:45 +0900
Masahiro Yamada <yamada.masahiro@socionext.com> wrote:


> +
> +static int denali_exec_instr(struct nand_chip *chip,
> +			     const struct nand_op_instr *instr)
> +{
> +	struct denali_nand_info *denali = to_denali(chip);
> +	bool width16 = chip->options & NAND_BUSWIDTH_16;
> +
> +	switch (instr->type) {
> +	case NAND_OP_CMD_INSTR:
> +		denali_exec_out8(denali, DENALI_MAP11_CMD,
> +				 &instr->ctx.cmd.opcode, 1);
> +		return 0;
> +	case NAND_OP_ADDR_INSTR:
> +		denali_exec_out8(denali, DENALI_MAP11_ADDR,
> +				 instr->ctx.addr.addrs,
> +				 instr->ctx.addr.naddrs);
> +		return 0;
> +	case NAND_OP_DATA_IN_INSTR:
> +		(!instr->ctx.data.force_8bit && width16 ?
> +		 denali_exec_in16 :
> +		 denali_exec_in8)(denali, DENALI_MAP11_DATA,
> +				  instr->ctx.data.buf.in,
> +				  instr->ctx.data.len);

I agree with Miquel, this statement tends to obfuscate the code, and
it's not like an extra if will make a huge difference in term of LOC.
	

> +		return 0;
> +	case NAND_OP_DATA_OUT_INSTR:
> +		(!instr->ctx.data.force_8bit && width16 ?
> +		 denali_exec_out16 :
> +		 denali_exec_out8)(denali, DENALI_MAP11_DATA,
> +				   instr->ctx.data.buf.out,
> +				   instr->ctx.data.len);

Ditto.

> +		return 0;
> +	case NAND_OP_WAITRDY_INSTR:
> +		return denali_exec_waitrdy(denali);
> +	default:
> +		WARN_ONCE(1, "unsupported NAND instruction type: %d\n",
> +			  instr->type);
> +
> +		return -EINVAL;
> +	}
> +}

______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v3 4/9] mtd: rawnand: denali: switch over to ->exec_op() from legacy hooks
  2019-03-12  9:02     ` Boris Brezillon
@ 2019-03-12  9:47       ` Masahiro Yamada
  -1 siblings, 0 replies; 40+ messages in thread
From: Masahiro Yamada @ 2019-03-12  9:47 UTC (permalink / raw)
  To: Boris Brezillon
  Cc: Boris Brezillon, Richard Weinberger, Linux Kernel Mailing List,
	Marek Vasut, linux-mtd, Miquel Raynal, Brian Norris,
	David Woodhouse

On Tue, Mar 12, 2019 at 6:03 PM Boris Brezillon
<boris.brezillon@collabora.com> wrote:
>
> On Tue, 12 Mar 2019 17:44:45 +0900
> Masahiro Yamada <yamada.masahiro@socionext.com> wrote:
>
>
> > +
> > +static int denali_exec_instr(struct nand_chip *chip,
> > +                          const struct nand_op_instr *instr)
> > +{
> > +     struct denali_nand_info *denali = to_denali(chip);
> > +     bool width16 = chip->options & NAND_BUSWIDTH_16;
> > +
> > +     switch (instr->type) {
> > +     case NAND_OP_CMD_INSTR:
> > +             denali_exec_out8(denali, DENALI_MAP11_CMD,
> > +                              &instr->ctx.cmd.opcode, 1);
> > +             return 0;
> > +     case NAND_OP_ADDR_INSTR:
> > +             denali_exec_out8(denali, DENALI_MAP11_ADDR,
> > +                              instr->ctx.addr.addrs,
> > +                              instr->ctx.addr.naddrs);
> > +             return 0;
> > +     case NAND_OP_DATA_IN_INSTR:
> > +             (!instr->ctx.data.force_8bit && width16 ?
> > +              denali_exec_in16 :
> > +              denali_exec_in8)(denali, DENALI_MAP11_DATA,
> > +                               instr->ctx.data.buf.in,
> > +                               instr->ctx.data.len);
>
> I agree with Miquel, this statement tends to obfuscate the code, and
> it's not like an extra if will make a huge difference in term of LOC.


OK, I will add the following helpers. Before sending v4, I will wait
for more comments.



static void denali_exec_in(struct denali_controller *denali, u32 type,
                           u8 *buf, unsigned int len, bool width16)
{
        if (width16)
                denali_exec_in16(denali, type, buf, len);
        else
                denali_exec_in8(denali, type, buf, len);
}


static void denali_exec_out(struct denali_controller *denali, u32 type,
                            const u8 *buf, unsigned int len)
{
        if (width16)
                denali_exec_out16(denali, type, buf, len);
        else
                denali_exec_out8(denali, type, buf, len);
}




>
> > +             return 0;
> > +     case NAND_OP_DATA_OUT_INSTR:
> > +             (!instr->ctx.data.force_8bit && width16 ?
> > +              denali_exec_out16 :
> > +              denali_exec_out8)(denali, DENALI_MAP11_DATA,
> > +                                instr->ctx.data.buf.out,
> > +                                instr->ctx.data.len);
>
> Ditto.
>
> > +             return 0;
> > +     case NAND_OP_WAITRDY_INSTR:
> > +             return denali_exec_waitrdy(denali);
> > +     default:
> > +             WARN_ONCE(1, "unsupported NAND instruction type: %d\n",
> > +                       instr->type);
> > +
> > +             return -EINVAL;
> > +     }
> > +}
>
> ______________________________________________________
> Linux MTD discussion mailing list
> http://lists.infradead.org/mailman/listinfo/linux-mtd/



-- 
Best Regards
Masahiro Yamada

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v3 4/9] mtd: rawnand: denali: switch over to ->exec_op() from legacy hooks
@ 2019-03-12  9:47       ` Masahiro Yamada
  0 siblings, 0 replies; 40+ messages in thread
From: Masahiro Yamada @ 2019-03-12  9:47 UTC (permalink / raw)
  To: Boris Brezillon
  Cc: Boris Brezillon, Richard Weinberger, Linux Kernel Mailing List,
	Marek Vasut, linux-mtd, Miquel Raynal, Brian Norris,
	David Woodhouse

On Tue, Mar 12, 2019 at 6:03 PM Boris Brezillon
<boris.brezillon@collabora.com> wrote:
>
> On Tue, 12 Mar 2019 17:44:45 +0900
> Masahiro Yamada <yamada.masahiro@socionext.com> wrote:
>
>
> > +
> > +static int denali_exec_instr(struct nand_chip *chip,
> > +                          const struct nand_op_instr *instr)
> > +{
> > +     struct denali_nand_info *denali = to_denali(chip);
> > +     bool width16 = chip->options & NAND_BUSWIDTH_16;
> > +
> > +     switch (instr->type) {
> > +     case NAND_OP_CMD_INSTR:
> > +             denali_exec_out8(denali, DENALI_MAP11_CMD,
> > +                              &instr->ctx.cmd.opcode, 1);
> > +             return 0;
> > +     case NAND_OP_ADDR_INSTR:
> > +             denali_exec_out8(denali, DENALI_MAP11_ADDR,
> > +                              instr->ctx.addr.addrs,
> > +                              instr->ctx.addr.naddrs);
> > +             return 0;
> > +     case NAND_OP_DATA_IN_INSTR:
> > +             (!instr->ctx.data.force_8bit && width16 ?
> > +              denali_exec_in16 :
> > +              denali_exec_in8)(denali, DENALI_MAP11_DATA,
> > +                               instr->ctx.data.buf.in,
> > +                               instr->ctx.data.len);
>
> I agree with Miquel, this statement tends to obfuscate the code, and
> it's not like an extra if will make a huge difference in term of LOC.


OK, I will add the following helpers. Before sending v4, I will wait
for more comments.



static void denali_exec_in(struct denali_controller *denali, u32 type,
                           u8 *buf, unsigned int len, bool width16)
{
        if (width16)
                denali_exec_in16(denali, type, buf, len);
        else
                denali_exec_in8(denali, type, buf, len);
}


static void denali_exec_out(struct denali_controller *denali, u32 type,
                            const u8 *buf, unsigned int len)
{
        if (width16)
                denali_exec_out16(denali, type, buf, len);
        else
                denali_exec_out8(denali, type, buf, len);
}




>
> > +             return 0;
> > +     case NAND_OP_DATA_OUT_INSTR:
> > +             (!instr->ctx.data.force_8bit && width16 ?
> > +              denali_exec_out16 :
> > +              denali_exec_out8)(denali, DENALI_MAP11_DATA,
> > +                                instr->ctx.data.buf.out,
> > +                                instr->ctx.data.len);
>
> Ditto.
>
> > +             return 0;
> > +     case NAND_OP_WAITRDY_INSTR:
> > +             return denali_exec_waitrdy(denali);
> > +     default:
> > +             WARN_ONCE(1, "unsupported NAND instruction type: %d\n",
> > +                       instr->type);
> > +
> > +             return -EINVAL;
> > +     }
> > +}
>
> ______________________________________________________
> Linux MTD discussion mailing list
> http://lists.infradead.org/mailman/listinfo/linux-mtd/



-- 
Best Regards
Masahiro Yamada

______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v3 2/9] mtd: rawnand: denali: refactor syndrome layout handling for raw access
  2019-03-12  8:44   ` Masahiro Yamada
@ 2019-03-12 10:28     ` Miquel Raynal
  -1 siblings, 0 replies; 40+ messages in thread
From: Miquel Raynal @ 2019-03-12 10:28 UTC (permalink / raw)
  To: Masahiro Yamada
  Cc: linux-mtd, Boris Brezillon, Brian Norris, linux-kernel,
	Marek Vasut, Richard Weinberger, David Woodhouse

Hi Masahiro,

Masahiro Yamada <yamada.masahiro@socionext.com> wrote on Tue, 12 Mar
2019 17:44:43 +0900:

> The Denali IP adopts the syndrome page layout (payload and ECC are
> interleaved). The *_page_raw() and *_oob() callbacks are complicated
> because they must hide the underlying layout used by the hardware,
> and always return contiguous in-band and out-of-band data.
> 
> Currently, similar code is duplicated to reorganize the data layout.
> For example, denali_read_page_raw() and denali_write_page_raw() look
> almost the same.
> 
> The idea for refactoring is to split the code into two parts:
>   [1] conversion of page layout
>   [2] what to do at every ECC chunk boundary
> 
> For [1], I wrote denali_raw_payload_op() and denali_raw_oob_op().
> They manipulate data for the Denali controller's specific page layout
> of in-band, out-of-band, respectively.
> 
> The difference between write and read is just the operation at
> ECC chunk boundaries. For example, denali_read_oob() calls
> nand_change_read_column_op(), whereas denali_write_oob() calls
> nand_change_write_column_op(). So, I implemented [2] as a callback
> passed into [1].
> 
> Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
> ---
> 

[...]

>  static int denali_read_page_raw(struct nand_chip *chip, uint8_t *buf,
>  				int oob_required, int page)
>  {
> +	struct denali_nand_info *denali = to_denali(chip);
>  	struct mtd_info *mtd = nand_to_mtd(chip);
> -	struct denali_nand_info *denali = mtd_to_denali(mtd);
> -	int writesize = mtd->writesize;
> -	int oobsize = mtd->oobsize;
> -	int ecc_steps = chip->ecc.steps;
> -	int ecc_size = chip->ecc.size;
> -	int ecc_bytes = chip->ecc.bytes;
>  	void *tmp_buf = denali->buf;
> -	int oob_skip = denali->oob_skip_bytes;
> -	size_t size = writesize + oobsize;
> -	int ret, i, pos, len;
> +	size_t size = mtd->writesize + mtd->oobsize;
> +	int ret;
> +
> +	if (!buf)
> +		return -EINVAL;
>  
>  	ret = denali_data_xfer(chip, tmp_buf, size, page, 1, 0);
>  	if (ret)
>  		return ret;
>  
> -	/* Arrange the buffer for syndrome payload/ecc layout */
> -	if (buf) {
> -		for (i = 0; i < ecc_steps; i++) {
> -			pos = i * (ecc_size + ecc_bytes);
> -			len = ecc_size;
> -
> -			if (pos >= writesize)
> -				pos += oob_skip;
> -			else if (pos + len > writesize)
> -				len = writesize - pos;
> -
> -			memcpy(buf, tmp_buf + pos, len);
> -			buf += len;
> -			if (len < ecc_size) {
> -				len = ecc_size - len;
> -				memcpy(buf, tmp_buf + writesize + oob_skip,
> -				       len);
> -				buf += len;
> -			}
> -		}
> -	}
> +	ret = denali_raw_payload_op(chip, buf, denali_memcpy_in, tmp_buf);

Honestly, I still don't like passing denali_memcpy_in/out as parameter.

Besides that, once you'll have added helpers to avoid abusing the
ternary operator in 4/9, the rest looks fine by me.

Thanks,
Miquèl

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v3 2/9] mtd: rawnand: denali: refactor syndrome layout handling for raw access
@ 2019-03-12 10:28     ` Miquel Raynal
  0 siblings, 0 replies; 40+ messages in thread
From: Miquel Raynal @ 2019-03-12 10:28 UTC (permalink / raw)
  To: Masahiro Yamada
  Cc: Boris Brezillon, Richard Weinberger, linux-kernel, Marek Vasut,
	linux-mtd, Brian Norris, David Woodhouse

Hi Masahiro,

Masahiro Yamada <yamada.masahiro@socionext.com> wrote on Tue, 12 Mar
2019 17:44:43 +0900:

> The Denali IP adopts the syndrome page layout (payload and ECC are
> interleaved). The *_page_raw() and *_oob() callbacks are complicated
> because they must hide the underlying layout used by the hardware,
> and always return contiguous in-band and out-of-band data.
> 
> Currently, similar code is duplicated to reorganize the data layout.
> For example, denali_read_page_raw() and denali_write_page_raw() look
> almost the same.
> 
> The idea for refactoring is to split the code into two parts:
>   [1] conversion of page layout
>   [2] what to do at every ECC chunk boundary
> 
> For [1], I wrote denali_raw_payload_op() and denali_raw_oob_op().
> They manipulate data for the Denali controller's specific page layout
> of in-band, out-of-band, respectively.
> 
> The difference between write and read is just the operation at
> ECC chunk boundaries. For example, denali_read_oob() calls
> nand_change_read_column_op(), whereas denali_write_oob() calls
> nand_change_write_column_op(). So, I implemented [2] as a callback
> passed into [1].
> 
> Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
> ---
> 

[...]

>  static int denali_read_page_raw(struct nand_chip *chip, uint8_t *buf,
>  				int oob_required, int page)
>  {
> +	struct denali_nand_info *denali = to_denali(chip);
>  	struct mtd_info *mtd = nand_to_mtd(chip);
> -	struct denali_nand_info *denali = mtd_to_denali(mtd);
> -	int writesize = mtd->writesize;
> -	int oobsize = mtd->oobsize;
> -	int ecc_steps = chip->ecc.steps;
> -	int ecc_size = chip->ecc.size;
> -	int ecc_bytes = chip->ecc.bytes;
>  	void *tmp_buf = denali->buf;
> -	int oob_skip = denali->oob_skip_bytes;
> -	size_t size = writesize + oobsize;
> -	int ret, i, pos, len;
> +	size_t size = mtd->writesize + mtd->oobsize;
> +	int ret;
> +
> +	if (!buf)
> +		return -EINVAL;
>  
>  	ret = denali_data_xfer(chip, tmp_buf, size, page, 1, 0);
>  	if (ret)
>  		return ret;
>  
> -	/* Arrange the buffer for syndrome payload/ecc layout */
> -	if (buf) {
> -		for (i = 0; i < ecc_steps; i++) {
> -			pos = i * (ecc_size + ecc_bytes);
> -			len = ecc_size;
> -
> -			if (pos >= writesize)
> -				pos += oob_skip;
> -			else if (pos + len > writesize)
> -				len = writesize - pos;
> -
> -			memcpy(buf, tmp_buf + pos, len);
> -			buf += len;
> -			if (len < ecc_size) {
> -				len = ecc_size - len;
> -				memcpy(buf, tmp_buf + writesize + oob_skip,
> -				       len);
> -				buf += len;
> -			}
> -		}
> -	}
> +	ret = denali_raw_payload_op(chip, buf, denali_memcpy_in, tmp_buf);

Honestly, I still don't like passing denali_memcpy_in/out as parameter.

Besides that, once you'll have added helpers to avoid abusing the
ternary operator in 4/9, the rest looks fine by me.

Thanks,
Miquèl

______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v3 2/9] mtd: rawnand: denali: refactor syndrome layout handling for raw access
  2019-03-12 10:28     ` Miquel Raynal
@ 2019-03-12 10:51       ` Masahiro Yamada
  -1 siblings, 0 replies; 40+ messages in thread
From: Masahiro Yamada @ 2019-03-12 10:51 UTC (permalink / raw)
  To: Miquel Raynal
  Cc: Boris Brezillon, Richard Weinberger, Linux Kernel Mailing List,
	Marek Vasut, linux-mtd, Brian Norris, David Woodhouse

On Tue, Mar 12, 2019 at 7:28 PM Miquel Raynal <miquel.raynal@bootlin.com> wrote:
>
> Hi Masahiro,
>
> Masahiro Yamada <yamada.masahiro@socionext.com> wrote on Tue, 12 Mar
> 2019 17:44:43 +0900:
>
> > The Denali IP adopts the syndrome page layout (payload and ECC are
> > interleaved). The *_page_raw() and *_oob() callbacks are complicated
> > because they must hide the underlying layout used by the hardware,
> > and always return contiguous in-band and out-of-band data.
> >
> > Currently, similar code is duplicated to reorganize the data layout.
> > For example, denali_read_page_raw() and denali_write_page_raw() look
> > almost the same.
> >
> > The idea for refactoring is to split the code into two parts:
> >   [1] conversion of page layout
> >   [2] what to do at every ECC chunk boundary
> >
> > For [1], I wrote denali_raw_payload_op() and denali_raw_oob_op().
> > They manipulate data for the Denali controller's specific page layout
> > of in-band, out-of-band, respectively.
> >
> > The difference between write and read is just the operation at
> > ECC chunk boundaries. For example, denali_read_oob() calls
> > nand_change_read_column_op(), whereas denali_write_oob() calls
> > nand_change_write_column_op(). So, I implemented [2] as a callback
> > passed into [1].
> >
> > Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
> > ---
> >
>
> [...]
>
> >  static int denali_read_page_raw(struct nand_chip *chip, uint8_t *buf,
> >                               int oob_required, int page)
> >  {
> > +     struct denali_nand_info *denali = to_denali(chip);
> >       struct mtd_info *mtd = nand_to_mtd(chip);
> > -     struct denali_nand_info *denali = mtd_to_denali(mtd);
> > -     int writesize = mtd->writesize;
> > -     int oobsize = mtd->oobsize;
> > -     int ecc_steps = chip->ecc.steps;
> > -     int ecc_size = chip->ecc.size;
> > -     int ecc_bytes = chip->ecc.bytes;
> >       void *tmp_buf = denali->buf;
> > -     int oob_skip = denali->oob_skip_bytes;
> > -     size_t size = writesize + oobsize;
> > -     int ret, i, pos, len;
> > +     size_t size = mtd->writesize + mtd->oobsize;
> > +     int ret;
> > +
> > +     if (!buf)
> > +             return -EINVAL;
> >
> >       ret = denali_data_xfer(chip, tmp_buf, size, page, 1, 0);
> >       if (ret)
> >               return ret;
> >
> > -     /* Arrange the buffer for syndrome payload/ecc layout */
> > -     if (buf) {
> > -             for (i = 0; i < ecc_steps; i++) {
> > -                     pos = i * (ecc_size + ecc_bytes);
> > -                     len = ecc_size;
> > -
> > -                     if (pos >= writesize)
> > -                             pos += oob_skip;
> > -                     else if (pos + len > writesize)
> > -                             len = writesize - pos;
> > -
> > -                     memcpy(buf, tmp_buf + pos, len);
> > -                     buf += len;
> > -                     if (len < ecc_size) {
> > -                             len = ecc_size - len;
> > -                             memcpy(buf, tmp_buf + writesize + oob_skip,
> > -                                    len);
> > -                             buf += len;
> > -                     }
> > -             }
> > -     }
> > +     ret = denali_raw_payload_op(chip, buf, denali_memcpy_in, tmp_buf);
>
> Honestly, I still don't like passing denali_memcpy_in/out as parameter.
>
> Besides that, once you'll have added helpers to avoid abusing the
> ternary operator in 4/9, the rest looks fine by me.
>


Do you have any suggestion?

There are 4 callbacks depending on the combination
of full-raw/oob, read/write.

I do not know how to make it cleaner.



-- 
Best Regards
Masahiro Yamada

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v3 2/9] mtd: rawnand: denali: refactor syndrome layout handling for raw access
@ 2019-03-12 10:51       ` Masahiro Yamada
  0 siblings, 0 replies; 40+ messages in thread
From: Masahiro Yamada @ 2019-03-12 10:51 UTC (permalink / raw)
  To: Miquel Raynal
  Cc: Boris Brezillon, Richard Weinberger, Linux Kernel Mailing List,
	Marek Vasut, linux-mtd, Brian Norris, David Woodhouse

On Tue, Mar 12, 2019 at 7:28 PM Miquel Raynal <miquel.raynal@bootlin.com> wrote:
>
> Hi Masahiro,
>
> Masahiro Yamada <yamada.masahiro@socionext.com> wrote on Tue, 12 Mar
> 2019 17:44:43 +0900:
>
> > The Denali IP adopts the syndrome page layout (payload and ECC are
> > interleaved). The *_page_raw() and *_oob() callbacks are complicated
> > because they must hide the underlying layout used by the hardware,
> > and always return contiguous in-band and out-of-band data.
> >
> > Currently, similar code is duplicated to reorganize the data layout.
> > For example, denali_read_page_raw() and denali_write_page_raw() look
> > almost the same.
> >
> > The idea for refactoring is to split the code into two parts:
> >   [1] conversion of page layout
> >   [2] what to do at every ECC chunk boundary
> >
> > For [1], I wrote denali_raw_payload_op() and denali_raw_oob_op().
> > They manipulate data for the Denali controller's specific page layout
> > of in-band, out-of-band, respectively.
> >
> > The difference between write and read is just the operation at
> > ECC chunk boundaries. For example, denali_read_oob() calls
> > nand_change_read_column_op(), whereas denali_write_oob() calls
> > nand_change_write_column_op(). So, I implemented [2] as a callback
> > passed into [1].
> >
> > Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
> > ---
> >
>
> [...]
>
> >  static int denali_read_page_raw(struct nand_chip *chip, uint8_t *buf,
> >                               int oob_required, int page)
> >  {
> > +     struct denali_nand_info *denali = to_denali(chip);
> >       struct mtd_info *mtd = nand_to_mtd(chip);
> > -     struct denali_nand_info *denali = mtd_to_denali(mtd);
> > -     int writesize = mtd->writesize;
> > -     int oobsize = mtd->oobsize;
> > -     int ecc_steps = chip->ecc.steps;
> > -     int ecc_size = chip->ecc.size;
> > -     int ecc_bytes = chip->ecc.bytes;
> >       void *tmp_buf = denali->buf;
> > -     int oob_skip = denali->oob_skip_bytes;
> > -     size_t size = writesize + oobsize;
> > -     int ret, i, pos, len;
> > +     size_t size = mtd->writesize + mtd->oobsize;
> > +     int ret;
> > +
> > +     if (!buf)
> > +             return -EINVAL;
> >
> >       ret = denali_data_xfer(chip, tmp_buf, size, page, 1, 0);
> >       if (ret)
> >               return ret;
> >
> > -     /* Arrange the buffer for syndrome payload/ecc layout */
> > -     if (buf) {
> > -             for (i = 0; i < ecc_steps; i++) {
> > -                     pos = i * (ecc_size + ecc_bytes);
> > -                     len = ecc_size;
> > -
> > -                     if (pos >= writesize)
> > -                             pos += oob_skip;
> > -                     else if (pos + len > writesize)
> > -                             len = writesize - pos;
> > -
> > -                     memcpy(buf, tmp_buf + pos, len);
> > -                     buf += len;
> > -                     if (len < ecc_size) {
> > -                             len = ecc_size - len;
> > -                             memcpy(buf, tmp_buf + writesize + oob_skip,
> > -                                    len);
> > -                             buf += len;
> > -                     }
> > -             }
> > -     }
> > +     ret = denali_raw_payload_op(chip, buf, denali_memcpy_in, tmp_buf);
>
> Honestly, I still don't like passing denali_memcpy_in/out as parameter.
>
> Besides that, once you'll have added helpers to avoid abusing the
> ternary operator in 4/9, the rest looks fine by me.
>


Do you have any suggestion?

There are 4 callbacks depending on the combination
of full-raw/oob, read/write.

I do not know how to make it cleaner.



-- 
Best Regards
Masahiro Yamada

______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v3 2/9] mtd: rawnand: denali: refactor syndrome layout handling for raw access
  2019-03-12 10:51       ` Masahiro Yamada
@ 2019-03-12 10:54         ` Miquel Raynal
  -1 siblings, 0 replies; 40+ messages in thread
From: Miquel Raynal @ 2019-03-12 10:54 UTC (permalink / raw)
  To: Masahiro Yamada
  Cc: Boris Brezillon, Richard Weinberger, Linux Kernel Mailing List,
	Marek Vasut, linux-mtd, Brian Norris, David Woodhouse

Hi Masahiro,

Masahiro Yamada <yamada.masahiro@socionext.com> wrote on Tue, 12 Mar
2019 19:51:21 +0900:

> On Tue, Mar 12, 2019 at 7:28 PM Miquel Raynal <miquel.raynal@bootlin.com> wrote:
> >
> > Hi Masahiro,
> >
> > Masahiro Yamada <yamada.masahiro@socionext.com> wrote on Tue, 12 Mar
> > 2019 17:44:43 +0900:
> >  
> > > The Denali IP adopts the syndrome page layout (payload and ECC are
> > > interleaved). The *_page_raw() and *_oob() callbacks are complicated
> > > because they must hide the underlying layout used by the hardware,
> > > and always return contiguous in-band and out-of-band data.
> > >
> > > Currently, similar code is duplicated to reorganize the data layout.
> > > For example, denali_read_page_raw() and denali_write_page_raw() look
> > > almost the same.
> > >
> > > The idea for refactoring is to split the code into two parts:
> > >   [1] conversion of page layout
> > >   [2] what to do at every ECC chunk boundary
> > >
> > > For [1], I wrote denali_raw_payload_op() and denali_raw_oob_op().
> > > They manipulate data for the Denali controller's specific page layout
> > > of in-band, out-of-band, respectively.
> > >
> > > The difference between write and read is just the operation at
> > > ECC chunk boundaries. For example, denali_read_oob() calls
> > > nand_change_read_column_op(), whereas denali_write_oob() calls
> > > nand_change_write_column_op(). So, I implemented [2] as a callback
> > > passed into [1].
> > >
> > > Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
> > > ---
> > >  
> >
> > [...]
> >  
> > >  static int denali_read_page_raw(struct nand_chip *chip, uint8_t *buf,
> > >                               int oob_required, int page)
> > >  {
> > > +     struct denali_nand_info *denali = to_denali(chip);
> > >       struct mtd_info *mtd = nand_to_mtd(chip);
> > > -     struct denali_nand_info *denali = mtd_to_denali(mtd);
> > > -     int writesize = mtd->writesize;
> > > -     int oobsize = mtd->oobsize;
> > > -     int ecc_steps = chip->ecc.steps;
> > > -     int ecc_size = chip->ecc.size;
> > > -     int ecc_bytes = chip->ecc.bytes;
> > >       void *tmp_buf = denali->buf;
> > > -     int oob_skip = denali->oob_skip_bytes;
> > > -     size_t size = writesize + oobsize;
> > > -     int ret, i, pos, len;
> > > +     size_t size = mtd->writesize + mtd->oobsize;
> > > +     int ret;
> > > +
> > > +     if (!buf)
> > > +             return -EINVAL;
> > >
> > >       ret = denali_data_xfer(chip, tmp_buf, size, page, 1, 0);
> > >       if (ret)
> > >               return ret;
> > >
> > > -     /* Arrange the buffer for syndrome payload/ecc layout */
> > > -     if (buf) {
> > > -             for (i = 0; i < ecc_steps; i++) {
> > > -                     pos = i * (ecc_size + ecc_bytes);
> > > -                     len = ecc_size;
> > > -
> > > -                     if (pos >= writesize)
> > > -                             pos += oob_skip;
> > > -                     else if (pos + len > writesize)
> > > -                             len = writesize - pos;
> > > -
> > > -                     memcpy(buf, tmp_buf + pos, len);
> > > -                     buf += len;
> > > -                     if (len < ecc_size) {
> > > -                             len = ecc_size - len;
> > > -                             memcpy(buf, tmp_buf + writesize + oob_skip,
> > > -                                    len);
> > > -                             buf += len;
> > > -                     }
> > > -             }
> > > -     }
> > > +     ret = denali_raw_payload_op(chip, buf, denali_memcpy_in, tmp_buf);  
> >
> > Honestly, I still don't like passing denali_memcpy_in/out as parameter.
> >
> > Besides that, once you'll have added helpers to avoid abusing the
> > ternary operator in 4/9, the rest looks fine by me.
> >  
> 
> 
> Do you have any suggestion?

Maybe register these two helpers at probe as controller specific hooks,
then just pass an in/out boolean to the function?

Thanks,
Miquèl

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v3 2/9] mtd: rawnand: denali: refactor syndrome layout handling for raw access
@ 2019-03-12 10:54         ` Miquel Raynal
  0 siblings, 0 replies; 40+ messages in thread
From: Miquel Raynal @ 2019-03-12 10:54 UTC (permalink / raw)
  To: Masahiro Yamada
  Cc: Boris Brezillon, Richard Weinberger, Linux Kernel Mailing List,
	Marek Vasut, linux-mtd, Brian Norris, David Woodhouse

Hi Masahiro,

Masahiro Yamada <yamada.masahiro@socionext.com> wrote on Tue, 12 Mar
2019 19:51:21 +0900:

> On Tue, Mar 12, 2019 at 7:28 PM Miquel Raynal <miquel.raynal@bootlin.com> wrote:
> >
> > Hi Masahiro,
> >
> > Masahiro Yamada <yamada.masahiro@socionext.com> wrote on Tue, 12 Mar
> > 2019 17:44:43 +0900:
> >  
> > > The Denali IP adopts the syndrome page layout (payload and ECC are
> > > interleaved). The *_page_raw() and *_oob() callbacks are complicated
> > > because they must hide the underlying layout used by the hardware,
> > > and always return contiguous in-band and out-of-band data.
> > >
> > > Currently, similar code is duplicated to reorganize the data layout.
> > > For example, denali_read_page_raw() and denali_write_page_raw() look
> > > almost the same.
> > >
> > > The idea for refactoring is to split the code into two parts:
> > >   [1] conversion of page layout
> > >   [2] what to do at every ECC chunk boundary
> > >
> > > For [1], I wrote denali_raw_payload_op() and denali_raw_oob_op().
> > > They manipulate data for the Denali controller's specific page layout
> > > of in-band, out-of-band, respectively.
> > >
> > > The difference between write and read is just the operation at
> > > ECC chunk boundaries. For example, denali_read_oob() calls
> > > nand_change_read_column_op(), whereas denali_write_oob() calls
> > > nand_change_write_column_op(). So, I implemented [2] as a callback
> > > passed into [1].
> > >
> > > Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
> > > ---
> > >  
> >
> > [...]
> >  
> > >  static int denali_read_page_raw(struct nand_chip *chip, uint8_t *buf,
> > >                               int oob_required, int page)
> > >  {
> > > +     struct denali_nand_info *denali = to_denali(chip);
> > >       struct mtd_info *mtd = nand_to_mtd(chip);
> > > -     struct denali_nand_info *denali = mtd_to_denali(mtd);
> > > -     int writesize = mtd->writesize;
> > > -     int oobsize = mtd->oobsize;
> > > -     int ecc_steps = chip->ecc.steps;
> > > -     int ecc_size = chip->ecc.size;
> > > -     int ecc_bytes = chip->ecc.bytes;
> > >       void *tmp_buf = denali->buf;
> > > -     int oob_skip = denali->oob_skip_bytes;
> > > -     size_t size = writesize + oobsize;
> > > -     int ret, i, pos, len;
> > > +     size_t size = mtd->writesize + mtd->oobsize;
> > > +     int ret;
> > > +
> > > +     if (!buf)
> > > +             return -EINVAL;
> > >
> > >       ret = denali_data_xfer(chip, tmp_buf, size, page, 1, 0);
> > >       if (ret)
> > >               return ret;
> > >
> > > -     /* Arrange the buffer for syndrome payload/ecc layout */
> > > -     if (buf) {
> > > -             for (i = 0; i < ecc_steps; i++) {
> > > -                     pos = i * (ecc_size + ecc_bytes);
> > > -                     len = ecc_size;
> > > -
> > > -                     if (pos >= writesize)
> > > -                             pos += oob_skip;
> > > -                     else if (pos + len > writesize)
> > > -                             len = writesize - pos;
> > > -
> > > -                     memcpy(buf, tmp_buf + pos, len);
> > > -                     buf += len;
> > > -                     if (len < ecc_size) {
> > > -                             len = ecc_size - len;
> > > -                             memcpy(buf, tmp_buf + writesize + oob_skip,
> > > -                                    len);
> > > -                             buf += len;
> > > -                     }
> > > -             }
> > > -     }
> > > +     ret = denali_raw_payload_op(chip, buf, denali_memcpy_in, tmp_buf);  
> >
> > Honestly, I still don't like passing denali_memcpy_in/out as parameter.
> >
> > Besides that, once you'll have added helpers to avoid abusing the
> > ternary operator in 4/9, the rest looks fine by me.
> >  
> 
> 
> Do you have any suggestion?

Maybe register these two helpers at probe as controller specific hooks,
then just pass an in/out boolean to the function?

Thanks,
Miquèl

______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v3 2/9] mtd: rawnand: denali: refactor syndrome layout handling for raw access
  2019-03-12 10:54         ` Miquel Raynal
@ 2019-03-12 11:07           ` Masahiro Yamada
  -1 siblings, 0 replies; 40+ messages in thread
From: Masahiro Yamada @ 2019-03-12 11:07 UTC (permalink / raw)
  To: Miquel Raynal
  Cc: Boris Brezillon, Richard Weinberger, Linux Kernel Mailing List,
	Marek Vasut, linux-mtd, Brian Norris, David Woodhouse

Hi Miquel,


On Tue, Mar 12, 2019 at 7:54 PM Miquel Raynal <miquel.raynal@bootlin.com> wrote:
>
> Hi Masahiro,
>
> Masahiro Yamada <yamada.masahiro@socionext.com> wrote on Tue, 12 Mar
> 2019 19:51:21 +0900:
>
> > On Tue, Mar 12, 2019 at 7:28 PM Miquel Raynal <miquel.raynal@bootlin.com> wrote:
> > >
> > > Hi Masahiro,
> > >
> > > Masahiro Yamada <yamada.masahiro@socionext.com> wrote on Tue, 12 Mar
> > > 2019 17:44:43 +0900:
> > >
> > > > The Denali IP adopts the syndrome page layout (payload and ECC are
> > > > interleaved). The *_page_raw() and *_oob() callbacks are complicated
> > > > because they must hide the underlying layout used by the hardware,
> > > > and always return contiguous in-band and out-of-band data.
> > > >
> > > > Currently, similar code is duplicated to reorganize the data layout.
> > > > For example, denali_read_page_raw() and denali_write_page_raw() look
> > > > almost the same.
> > > >
> > > > The idea for refactoring is to split the code into two parts:
> > > >   [1] conversion of page layout
> > > >   [2] what to do at every ECC chunk boundary
> > > >
> > > > For [1], I wrote denali_raw_payload_op() and denali_raw_oob_op().
> > > > They manipulate data for the Denali controller's specific page layout
> > > > of in-band, out-of-band, respectively.
> > > >
> > > > The difference between write and read is just the operation at
> > > > ECC chunk boundaries. For example, denali_read_oob() calls
> > > > nand_change_read_column_op(), whereas denali_write_oob() calls
> > > > nand_change_write_column_op(). So, I implemented [2] as a callback
> > > > passed into [1].
> > > >
> > > > Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
> > > > ---
> > > >
> > >
> > > [...]
> > >
> > > >  static int denali_read_page_raw(struct nand_chip *chip, uint8_t *buf,
> > > >                               int oob_required, int page)
> > > >  {
> > > > +     struct denali_nand_info *denali = to_denali(chip);
> > > >       struct mtd_info *mtd = nand_to_mtd(chip);
> > > > -     struct denali_nand_info *denali = mtd_to_denali(mtd);
> > > > -     int writesize = mtd->writesize;
> > > > -     int oobsize = mtd->oobsize;
> > > > -     int ecc_steps = chip->ecc.steps;
> > > > -     int ecc_size = chip->ecc.size;
> > > > -     int ecc_bytes = chip->ecc.bytes;
> > > >       void *tmp_buf = denali->buf;
> > > > -     int oob_skip = denali->oob_skip_bytes;
> > > > -     size_t size = writesize + oobsize;
> > > > -     int ret, i, pos, len;
> > > > +     size_t size = mtd->writesize + mtd->oobsize;
> > > > +     int ret;
> > > > +
> > > > +     if (!buf)
> > > > +             return -EINVAL;
> > > >
> > > >       ret = denali_data_xfer(chip, tmp_buf, size, page, 1, 0);
> > > >       if (ret)
> > > >               return ret;
> > > >
> > > > -     /* Arrange the buffer for syndrome payload/ecc layout */
> > > > -     if (buf) {
> > > > -             for (i = 0; i < ecc_steps; i++) {
> > > > -                     pos = i * (ecc_size + ecc_bytes);
> > > > -                     len = ecc_size;
> > > > -
> > > > -                     if (pos >= writesize)
> > > > -                             pos += oob_skip;
> > > > -                     else if (pos + len > writesize)
> > > > -                             len = writesize - pos;
> > > > -
> > > > -                     memcpy(buf, tmp_buf + pos, len);
> > > > -                     buf += len;
> > > > -                     if (len < ecc_size) {
> > > > -                             len = ecc_size - len;
> > > > -                             memcpy(buf, tmp_buf + writesize + oob_skip,
> > > > -                                    len);
> > > > -                             buf += len;
> > > > -                     }
> > > > -             }
> > > > -     }
> > > > +     ret = denali_raw_payload_op(chip, buf, denali_memcpy_in, tmp_buf);
> > >
> > > Honestly, I still don't like passing denali_memcpy_in/out as parameter.
> > >
> > > Besides that, once you'll have added helpers to avoid abusing the
> > > ternary operator in 4/9, the rest looks fine by me.
> > >
> >
> >
> > Do you have any suggestion?
>
> Maybe register these two helpers at probe as controller specific hooks,
> then just pass an in/out boolean to the function?
>

Sorry, I do not understand.

Are you suggesting to do like follows in probe ?

denali->change_column_read_raw = denali_memcpy_in;
denali->change_column_write_raw = denali_memcpy_out;
denali->change_column_read_oob = denali_change_read_column_op;
denali->change_column_write_oob = denali_change_write_column_op;


All the 4 hooks are always needed
regardless of any probed features.


The result is just textual replacement
denali_* with denali->*.

What's the point of copying fixed function addresses
to denali structure?


-- 
Best Regards
Masahiro Yamada

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v3 2/9] mtd: rawnand: denali: refactor syndrome layout handling for raw access
@ 2019-03-12 11:07           ` Masahiro Yamada
  0 siblings, 0 replies; 40+ messages in thread
From: Masahiro Yamada @ 2019-03-12 11:07 UTC (permalink / raw)
  To: Miquel Raynal
  Cc: Boris Brezillon, Richard Weinberger, Linux Kernel Mailing List,
	Marek Vasut, linux-mtd, Brian Norris, David Woodhouse

Hi Miquel,


On Tue, Mar 12, 2019 at 7:54 PM Miquel Raynal <miquel.raynal@bootlin.com> wrote:
>
> Hi Masahiro,
>
> Masahiro Yamada <yamada.masahiro@socionext.com> wrote on Tue, 12 Mar
> 2019 19:51:21 +0900:
>
> > On Tue, Mar 12, 2019 at 7:28 PM Miquel Raynal <miquel.raynal@bootlin.com> wrote:
> > >
> > > Hi Masahiro,
> > >
> > > Masahiro Yamada <yamada.masahiro@socionext.com> wrote on Tue, 12 Mar
> > > 2019 17:44:43 +0900:
> > >
> > > > The Denali IP adopts the syndrome page layout (payload and ECC are
> > > > interleaved). The *_page_raw() and *_oob() callbacks are complicated
> > > > because they must hide the underlying layout used by the hardware,
> > > > and always return contiguous in-band and out-of-band data.
> > > >
> > > > Currently, similar code is duplicated to reorganize the data layout.
> > > > For example, denali_read_page_raw() and denali_write_page_raw() look
> > > > almost the same.
> > > >
> > > > The idea for refactoring is to split the code into two parts:
> > > >   [1] conversion of page layout
> > > >   [2] what to do at every ECC chunk boundary
> > > >
> > > > For [1], I wrote denali_raw_payload_op() and denali_raw_oob_op().
> > > > They manipulate data for the Denali controller's specific page layout
> > > > of in-band, out-of-band, respectively.
> > > >
> > > > The difference between write and read is just the operation at
> > > > ECC chunk boundaries. For example, denali_read_oob() calls
> > > > nand_change_read_column_op(), whereas denali_write_oob() calls
> > > > nand_change_write_column_op(). So, I implemented [2] as a callback
> > > > passed into [1].
> > > >
> > > > Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
> > > > ---
> > > >
> > >
> > > [...]
> > >
> > > >  static int denali_read_page_raw(struct nand_chip *chip, uint8_t *buf,
> > > >                               int oob_required, int page)
> > > >  {
> > > > +     struct denali_nand_info *denali = to_denali(chip);
> > > >       struct mtd_info *mtd = nand_to_mtd(chip);
> > > > -     struct denali_nand_info *denali = mtd_to_denali(mtd);
> > > > -     int writesize = mtd->writesize;
> > > > -     int oobsize = mtd->oobsize;
> > > > -     int ecc_steps = chip->ecc.steps;
> > > > -     int ecc_size = chip->ecc.size;
> > > > -     int ecc_bytes = chip->ecc.bytes;
> > > >       void *tmp_buf = denali->buf;
> > > > -     int oob_skip = denali->oob_skip_bytes;
> > > > -     size_t size = writesize + oobsize;
> > > > -     int ret, i, pos, len;
> > > > +     size_t size = mtd->writesize + mtd->oobsize;
> > > > +     int ret;
> > > > +
> > > > +     if (!buf)
> > > > +             return -EINVAL;
> > > >
> > > >       ret = denali_data_xfer(chip, tmp_buf, size, page, 1, 0);
> > > >       if (ret)
> > > >               return ret;
> > > >
> > > > -     /* Arrange the buffer for syndrome payload/ecc layout */
> > > > -     if (buf) {
> > > > -             for (i = 0; i < ecc_steps; i++) {
> > > > -                     pos = i * (ecc_size + ecc_bytes);
> > > > -                     len = ecc_size;
> > > > -
> > > > -                     if (pos >= writesize)
> > > > -                             pos += oob_skip;
> > > > -                     else if (pos + len > writesize)
> > > > -                             len = writesize - pos;
> > > > -
> > > > -                     memcpy(buf, tmp_buf + pos, len);
> > > > -                     buf += len;
> > > > -                     if (len < ecc_size) {
> > > > -                             len = ecc_size - len;
> > > > -                             memcpy(buf, tmp_buf + writesize + oob_skip,
> > > > -                                    len);
> > > > -                             buf += len;
> > > > -                     }
> > > > -             }
> > > > -     }
> > > > +     ret = denali_raw_payload_op(chip, buf, denali_memcpy_in, tmp_buf);
> > >
> > > Honestly, I still don't like passing denali_memcpy_in/out as parameter.
> > >
> > > Besides that, once you'll have added helpers to avoid abusing the
> > > ternary operator in 4/9, the rest looks fine by me.
> > >
> >
> >
> > Do you have any suggestion?
>
> Maybe register these two helpers at probe as controller specific hooks,
> then just pass an in/out boolean to the function?
>

Sorry, I do not understand.

Are you suggesting to do like follows in probe ?

denali->change_column_read_raw = denali_memcpy_in;
denali->change_column_write_raw = denali_memcpy_out;
denali->change_column_read_oob = denali_change_read_column_op;
denali->change_column_write_oob = denali_change_write_column_op;


All the 4 hooks are always needed
regardless of any probed features.


The result is just textual replacement
denali_* with denali->*.

What's the point of copying fixed function addresses
to denali structure?


-- 
Best Regards
Masahiro Yamada

______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v3 2/9] mtd: rawnand: denali: refactor syndrome layout handling for raw access
  2019-03-12 11:07           ` Masahiro Yamada
@ 2019-03-12 13:13             ` Miquel Raynal
  -1 siblings, 0 replies; 40+ messages in thread
From: Miquel Raynal @ 2019-03-12 13:13 UTC (permalink / raw)
  To: Masahiro Yamada
  Cc: Boris Brezillon, Richard Weinberger, Linux Kernel Mailing List,
	Marek Vasut, linux-mtd, Brian Norris, David Woodhouse

Hi Masahiro,

Masahiro Yamada <yamada.masahiro@socionext.com> wrote on Tue, 12 Mar
2019 20:07:27 +0900:

> Hi Miquel,
> 
> 
> On Tue, Mar 12, 2019 at 7:54 PM Miquel Raynal <miquel.raynal@bootlin.com> wrote:
> >
> > Hi Masahiro,
> >
> > Masahiro Yamada <yamada.masahiro@socionext.com> wrote on Tue, 12 Mar
> > 2019 19:51:21 +0900:
> >  
> > > On Tue, Mar 12, 2019 at 7:28 PM Miquel Raynal <miquel.raynal@bootlin.com> wrote:  
> > > >
> > > > Hi Masahiro,
> > > >
> > > > Masahiro Yamada <yamada.masahiro@socionext.com> wrote on Tue, 12 Mar
> > > > 2019 17:44:43 +0900:
> > > >  
> > > > > The Denali IP adopts the syndrome page layout (payload and ECC are
> > > > > interleaved). The *_page_raw() and *_oob() callbacks are complicated
> > > > > because they must hide the underlying layout used by the hardware,
> > > > > and always return contiguous in-band and out-of-band data.
> > > > >
> > > > > Currently, similar code is duplicated to reorganize the data layout.
> > > > > For example, denali_read_page_raw() and denali_write_page_raw() look
> > > > > almost the same.
> > > > >
> > > > > The idea for refactoring is to split the code into two parts:
> > > > >   [1] conversion of page layout
> > > > >   [2] what to do at every ECC chunk boundary
> > > > >
> > > > > For [1], I wrote denali_raw_payload_op() and denali_raw_oob_op().
> > > > > They manipulate data for the Denali controller's specific page layout
> > > > > of in-band, out-of-band, respectively.
> > > > >
> > > > > The difference between write and read is just the operation at
> > > > > ECC chunk boundaries. For example, denali_read_oob() calls
> > > > > nand_change_read_column_op(), whereas denali_write_oob() calls
> > > > > nand_change_write_column_op(). So, I implemented [2] as a callback
> > > > > passed into [1].
> > > > >
> > > > > Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
> > > > > ---
> > > > >  
> > > >
> > > > [...]
> > > >  
> > > > >  static int denali_read_page_raw(struct nand_chip *chip, uint8_t *buf,
> > > > >                               int oob_required, int page)
> > > > >  {
> > > > > +     struct denali_nand_info *denali = to_denali(chip);
> > > > >       struct mtd_info *mtd = nand_to_mtd(chip);
> > > > > -     struct denali_nand_info *denali = mtd_to_denali(mtd);
> > > > > -     int writesize = mtd->writesize;
> > > > > -     int oobsize = mtd->oobsize;
> > > > > -     int ecc_steps = chip->ecc.steps;
> > > > > -     int ecc_size = chip->ecc.size;
> > > > > -     int ecc_bytes = chip->ecc.bytes;
> > > > >       void *tmp_buf = denali->buf;
> > > > > -     int oob_skip = denali->oob_skip_bytes;
> > > > > -     size_t size = writesize + oobsize;
> > > > > -     int ret, i, pos, len;
> > > > > +     size_t size = mtd->writesize + mtd->oobsize;
> > > > > +     int ret;
> > > > > +
> > > > > +     if (!buf)
> > > > > +             return -EINVAL;
> > > > >
> > > > >       ret = denali_data_xfer(chip, tmp_buf, size, page, 1, 0);
> > > > >       if (ret)
> > > > >               return ret;
> > > > >
> > > > > -     /* Arrange the buffer for syndrome payload/ecc layout */
> > > > > -     if (buf) {
> > > > > -             for (i = 0; i < ecc_steps; i++) {
> > > > > -                     pos = i * (ecc_size + ecc_bytes);
> > > > > -                     len = ecc_size;
> > > > > -
> > > > > -                     if (pos >= writesize)
> > > > > -                             pos += oob_skip;
> > > > > -                     else if (pos + len > writesize)
> > > > > -                             len = writesize - pos;
> > > > > -
> > > > > -                     memcpy(buf, tmp_buf + pos, len);
> > > > > -                     buf += len;
> > > > > -                     if (len < ecc_size) {
> > > > > -                             len = ecc_size - len;
> > > > > -                             memcpy(buf, tmp_buf + writesize + oob_skip,
> > > > > -                                    len);
> > > > > -                             buf += len;
> > > > > -                     }
> > > > > -             }
> > > > > -     }
> > > > > +     ret = denali_raw_payload_op(chip, buf, denali_memcpy_in, tmp_buf);  
> > > >
> > > > Honestly, I still don't like passing denali_memcpy_in/out as parameter.
> > > >
> > > > Besides that, once you'll have added helpers to avoid abusing the
> > > > ternary operator in 4/9, the rest looks fine by me.
> > > >  
> > >
> > >
> > > Do you have any suggestion?  
> >
> > Maybe register these two helpers at probe as controller specific hooks,
> > then just pass an in/out boolean to the function?
> >  
> 
> Sorry, I do not understand.
> 
> Are you suggesting to do like follows in probe ?
> 
> denali->change_column_read_raw = denali_memcpy_in;
> denali->change_column_write_raw = denali_memcpy_out;
> denali->change_column_read_oob = denali_change_read_column_op;
> denali->change_column_write_oob = denali_change_write_column_op;
> 
> 
> All the 4 hooks are always needed
> regardless of any probed features.
> 
> 
> The result is just textual replacement
> denali_* with denali->*.
> 
> What's the point of copying fixed function addresses
> to denali structure?
> 
> 

What I don't like is the function pointer as a function parameter. You
can use the functions defined statically if you prefer as long as the
parameter is just a boolean for instance?

Thanks,
Miquèl

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v3 2/9] mtd: rawnand: denali: refactor syndrome layout handling for raw access
@ 2019-03-12 13:13             ` Miquel Raynal
  0 siblings, 0 replies; 40+ messages in thread
From: Miquel Raynal @ 2019-03-12 13:13 UTC (permalink / raw)
  To: Masahiro Yamada
  Cc: Boris Brezillon, Richard Weinberger, Linux Kernel Mailing List,
	Marek Vasut, linux-mtd, Brian Norris, David Woodhouse

Hi Masahiro,

Masahiro Yamada <yamada.masahiro@socionext.com> wrote on Tue, 12 Mar
2019 20:07:27 +0900:

> Hi Miquel,
> 
> 
> On Tue, Mar 12, 2019 at 7:54 PM Miquel Raynal <miquel.raynal@bootlin.com> wrote:
> >
> > Hi Masahiro,
> >
> > Masahiro Yamada <yamada.masahiro@socionext.com> wrote on Tue, 12 Mar
> > 2019 19:51:21 +0900:
> >  
> > > On Tue, Mar 12, 2019 at 7:28 PM Miquel Raynal <miquel.raynal@bootlin.com> wrote:  
> > > >
> > > > Hi Masahiro,
> > > >
> > > > Masahiro Yamada <yamada.masahiro@socionext.com> wrote on Tue, 12 Mar
> > > > 2019 17:44:43 +0900:
> > > >  
> > > > > The Denali IP adopts the syndrome page layout (payload and ECC are
> > > > > interleaved). The *_page_raw() and *_oob() callbacks are complicated
> > > > > because they must hide the underlying layout used by the hardware,
> > > > > and always return contiguous in-band and out-of-band data.
> > > > >
> > > > > Currently, similar code is duplicated to reorganize the data layout.
> > > > > For example, denali_read_page_raw() and denali_write_page_raw() look
> > > > > almost the same.
> > > > >
> > > > > The idea for refactoring is to split the code into two parts:
> > > > >   [1] conversion of page layout
> > > > >   [2] what to do at every ECC chunk boundary
> > > > >
> > > > > For [1], I wrote denali_raw_payload_op() and denali_raw_oob_op().
> > > > > They manipulate data for the Denali controller's specific page layout
> > > > > of in-band, out-of-band, respectively.
> > > > >
> > > > > The difference between write and read is just the operation at
> > > > > ECC chunk boundaries. For example, denali_read_oob() calls
> > > > > nand_change_read_column_op(), whereas denali_write_oob() calls
> > > > > nand_change_write_column_op(). So, I implemented [2] as a callback
> > > > > passed into [1].
> > > > >
> > > > > Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
> > > > > ---
> > > > >  
> > > >
> > > > [...]
> > > >  
> > > > >  static int denali_read_page_raw(struct nand_chip *chip, uint8_t *buf,
> > > > >                               int oob_required, int page)
> > > > >  {
> > > > > +     struct denali_nand_info *denali = to_denali(chip);
> > > > >       struct mtd_info *mtd = nand_to_mtd(chip);
> > > > > -     struct denali_nand_info *denali = mtd_to_denali(mtd);
> > > > > -     int writesize = mtd->writesize;
> > > > > -     int oobsize = mtd->oobsize;
> > > > > -     int ecc_steps = chip->ecc.steps;
> > > > > -     int ecc_size = chip->ecc.size;
> > > > > -     int ecc_bytes = chip->ecc.bytes;
> > > > >       void *tmp_buf = denali->buf;
> > > > > -     int oob_skip = denali->oob_skip_bytes;
> > > > > -     size_t size = writesize + oobsize;
> > > > > -     int ret, i, pos, len;
> > > > > +     size_t size = mtd->writesize + mtd->oobsize;
> > > > > +     int ret;
> > > > > +
> > > > > +     if (!buf)
> > > > > +             return -EINVAL;
> > > > >
> > > > >       ret = denali_data_xfer(chip, tmp_buf, size, page, 1, 0);
> > > > >       if (ret)
> > > > >               return ret;
> > > > >
> > > > > -     /* Arrange the buffer for syndrome payload/ecc layout */
> > > > > -     if (buf) {
> > > > > -             for (i = 0; i < ecc_steps; i++) {
> > > > > -                     pos = i * (ecc_size + ecc_bytes);
> > > > > -                     len = ecc_size;
> > > > > -
> > > > > -                     if (pos >= writesize)
> > > > > -                             pos += oob_skip;
> > > > > -                     else if (pos + len > writesize)
> > > > > -                             len = writesize - pos;
> > > > > -
> > > > > -                     memcpy(buf, tmp_buf + pos, len);
> > > > > -                     buf += len;
> > > > > -                     if (len < ecc_size) {
> > > > > -                             len = ecc_size - len;
> > > > > -                             memcpy(buf, tmp_buf + writesize + oob_skip,
> > > > > -                                    len);
> > > > > -                             buf += len;
> > > > > -                     }
> > > > > -             }
> > > > > -     }
> > > > > +     ret = denali_raw_payload_op(chip, buf, denali_memcpy_in, tmp_buf);  
> > > >
> > > > Honestly, I still don't like passing denali_memcpy_in/out as parameter.
> > > >
> > > > Besides that, once you'll have added helpers to avoid abusing the
> > > > ternary operator in 4/9, the rest looks fine by me.
> > > >  
> > >
> > >
> > > Do you have any suggestion?  
> >
> > Maybe register these two helpers at probe as controller specific hooks,
> > then just pass an in/out boolean to the function?
> >  
> 
> Sorry, I do not understand.
> 
> Are you suggesting to do like follows in probe ?
> 
> denali->change_column_read_raw = denali_memcpy_in;
> denali->change_column_write_raw = denali_memcpy_out;
> denali->change_column_read_oob = denali_change_read_column_op;
> denali->change_column_write_oob = denali_change_write_column_op;
> 
> 
> All the 4 hooks are always needed
> regardless of any probed features.
> 
> 
> The result is just textual replacement
> denali_* with denali->*.
> 
> What's the point of copying fixed function addresses
> to denali structure?
> 
> 

What I don't like is the function pointer as a function parameter. You
can use the functions defined statically if you prefer as long as the
parameter is just a boolean for instance?

Thanks,
Miquèl

______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v3 2/9] mtd: rawnand: denali: refactor syndrome layout handling for raw access
  2019-03-12 13:13             ` Miquel Raynal
@ 2019-03-14  8:24               ` Masahiro Yamada
  -1 siblings, 0 replies; 40+ messages in thread
From: Masahiro Yamada @ 2019-03-14  8:24 UTC (permalink / raw)
  To: Miquel Raynal
  Cc: Boris Brezillon, Richard Weinberger, Linux Kernel Mailing List,
	Marek Vasut, linux-mtd, Brian Norris, David Woodhouse

Hi Miquel,

On Tue, Mar 12, 2019 at 10:13 PM Miquel Raynal
<miquel.raynal@bootlin.com> wrote:
>
> Hi Masahiro,
>
> Masahiro Yamada <yamada.masahiro@socionext.com> wrote on Tue, 12 Mar
> 2019 20:07:27 +0900:
>
> > Hi Miquel,
> >
> >
> > On Tue, Mar 12, 2019 at 7:54 PM Miquel Raynal <miquel.raynal@bootlin.com> wrote:
> > >
> > > Hi Masahiro,
> > >
> > > Masahiro Yamada <yamada.masahiro@socionext.com> wrote on Tue, 12 Mar
> > > 2019 19:51:21 +0900:
> > >
> > > > On Tue, Mar 12, 2019 at 7:28 PM Miquel Raynal <miquel.raynal@bootlin.com> wrote:
> > > > >
> > > > > Hi Masahiro,
> > > > >
> > > > > Masahiro Yamada <yamada.masahiro@socionext.com> wrote on Tue, 12 Mar
> > > > > 2019 17:44:43 +0900:
> > > > >
> > > > > > The Denali IP adopts the syndrome page layout (payload and ECC are
> > > > > > interleaved). The *_page_raw() and *_oob() callbacks are complicated
> > > > > > because they must hide the underlying layout used by the hardware,
> > > > > > and always return contiguous in-band and out-of-band data.
> > > > > >
> > > > > > Currently, similar code is duplicated to reorganize the data layout.
> > > > > > For example, denali_read_page_raw() and denali_write_page_raw() look
> > > > > > almost the same.
> > > > > >
> > > > > > The idea for refactoring is to split the code into two parts:
> > > > > >   [1] conversion of page layout
> > > > > >   [2] what to do at every ECC chunk boundary
> > > > > >
> > > > > > For [1], I wrote denali_raw_payload_op() and denali_raw_oob_op().
> > > > > > They manipulate data for the Denali controller's specific page layout
> > > > > > of in-band, out-of-band, respectively.
> > > > > >
> > > > > > The difference between write and read is just the operation at
> > > > > > ECC chunk boundaries. For example, denali_read_oob() calls
> > > > > > nand_change_read_column_op(), whereas denali_write_oob() calls
> > > > > > nand_change_write_column_op(). So, I implemented [2] as a callback
> > > > > > passed into [1].
> > > > > >
> > > > > > Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
> > > > > > ---
> > > > > >
> > > > >
> > > > > [...]
> > > > >
> > > > > >  static int denali_read_page_raw(struct nand_chip *chip, uint8_t *buf,
> > > > > >                               int oob_required, int page)
> > > > > >  {
> > > > > > +     struct denali_nand_info *denali = to_denali(chip);
> > > > > >       struct mtd_info *mtd = nand_to_mtd(chip);
> > > > > > -     struct denali_nand_info *denali = mtd_to_denali(mtd);
> > > > > > -     int writesize = mtd->writesize;
> > > > > > -     int oobsize = mtd->oobsize;
> > > > > > -     int ecc_steps = chip->ecc.steps;
> > > > > > -     int ecc_size = chip->ecc.size;
> > > > > > -     int ecc_bytes = chip->ecc.bytes;
> > > > > >       void *tmp_buf = denali->buf;
> > > > > > -     int oob_skip = denali->oob_skip_bytes;
> > > > > > -     size_t size = writesize + oobsize;
> > > > > > -     int ret, i, pos, len;
> > > > > > +     size_t size = mtd->writesize + mtd->oobsize;
> > > > > > +     int ret;
> > > > > > +
> > > > > > +     if (!buf)
> > > > > > +             return -EINVAL;
> > > > > >
> > > > > >       ret = denali_data_xfer(chip, tmp_buf, size, page, 1, 0);
> > > > > >       if (ret)
> > > > > >               return ret;
> > > > > >
> > > > > > -     /* Arrange the buffer for syndrome payload/ecc layout */
> > > > > > -     if (buf) {
> > > > > > -             for (i = 0; i < ecc_steps; i++) {
> > > > > > -                     pos = i * (ecc_size + ecc_bytes);
> > > > > > -                     len = ecc_size;
> > > > > > -
> > > > > > -                     if (pos >= writesize)
> > > > > > -                             pos += oob_skip;
> > > > > > -                     else if (pos + len > writesize)
> > > > > > -                             len = writesize - pos;
> > > > > > -
> > > > > > -                     memcpy(buf, tmp_buf + pos, len);
> > > > > > -                     buf += len;
> > > > > > -                     if (len < ecc_size) {
> > > > > > -                             len = ecc_size - len;
> > > > > > -                             memcpy(buf, tmp_buf + writesize + oob_skip,
> > > > > > -                                    len);
> > > > > > -                             buf += len;
> > > > > > -                     }
> > > > > > -             }
> > > > > > -     }
> > > > > > +     ret = denali_raw_payload_op(chip, buf, denali_memcpy_in, tmp_buf);
> > > > >
> > > > > Honestly, I still don't like passing denali_memcpy_in/out as parameter.
> > > > >
> > > > > Besides that, once you'll have added helpers to avoid abusing the
> > > > > ternary operator in 4/9, the rest looks fine by me.
> > > > >
> > > >
> > > >
> > > > Do you have any suggestion?
> > >
> > > Maybe register these two helpers at probe as controller specific hooks,
> > > then just pass an in/out boolean to the function?
> > >
> >
> > Sorry, I do not understand.
> >
> > Are you suggesting to do like follows in probe ?
> >
> > denali->change_column_read_raw = denali_memcpy_in;
> > denali->change_column_write_raw = denali_memcpy_out;
> > denali->change_column_read_oob = denali_change_read_column_op;
> > denali->change_column_write_oob = denali_change_write_column_op;
> >
> >
> > All the 4 hooks are always needed
> > regardless of any probed features.
> >
> >
> > The result is just textual replacement
> > denali_* with denali->*.
> >
> > What's the point of copying fixed function addresses
> > to denali structure?
> >
> >
>
> What I don't like is the function pointer as a function parameter.

This is a usual way to handle callback.

> You
> can use the functions defined statically if you prefer as long as the
> parameter is just a boolean for instance?



I still do not understand your concern,
but if you ban the use of function pointer,
the following is the best I can do
since there are 4 hooks depending on the
combination of oob/raw, write/read.



if (oob) {
        if (write)
                return nand_change_write_column_op(chip, offset, buf,
                                                   len, false);
        else
                return nand_change_read_column_op(chip, offset, buf,
                                                  len, false);
}

if (write)
        memcpy(denali->buf + offset, buf, len);
else
        memcpy(buf, denali->buf + offset, len);

return 0;




BTW, when are .read_page_raw / .write_page_raw used?

Currently, I use "whole page access && memcpy" for better performance.

If those hooks are rarely used, I use
nand_change_write_column_op / nand_change_read_column_op,
which will reduce the if-conditional.




>
> Thanks,
> Miquèl
>
> ______________________________________________________
> Linux MTD discussion mailing list
> http://lists.infradead.org/mailman/listinfo/linux-mtd/



-- 
Best Regards
Masahiro Yamada

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v3 2/9] mtd: rawnand: denali: refactor syndrome layout handling for raw access
@ 2019-03-14  8:24               ` Masahiro Yamada
  0 siblings, 0 replies; 40+ messages in thread
From: Masahiro Yamada @ 2019-03-14  8:24 UTC (permalink / raw)
  To: Miquel Raynal
  Cc: Boris Brezillon, Richard Weinberger, Linux Kernel Mailing List,
	Marek Vasut, linux-mtd, Brian Norris, David Woodhouse

Hi Miquel,

On Tue, Mar 12, 2019 at 10:13 PM Miquel Raynal
<miquel.raynal@bootlin.com> wrote:
>
> Hi Masahiro,
>
> Masahiro Yamada <yamada.masahiro@socionext.com> wrote on Tue, 12 Mar
> 2019 20:07:27 +0900:
>
> > Hi Miquel,
> >
> >
> > On Tue, Mar 12, 2019 at 7:54 PM Miquel Raynal <miquel.raynal@bootlin.com> wrote:
> > >
> > > Hi Masahiro,
> > >
> > > Masahiro Yamada <yamada.masahiro@socionext.com> wrote on Tue, 12 Mar
> > > 2019 19:51:21 +0900:
> > >
> > > > On Tue, Mar 12, 2019 at 7:28 PM Miquel Raynal <miquel.raynal@bootlin.com> wrote:
> > > > >
> > > > > Hi Masahiro,
> > > > >
> > > > > Masahiro Yamada <yamada.masahiro@socionext.com> wrote on Tue, 12 Mar
> > > > > 2019 17:44:43 +0900:
> > > > >
> > > > > > The Denali IP adopts the syndrome page layout (payload and ECC are
> > > > > > interleaved). The *_page_raw() and *_oob() callbacks are complicated
> > > > > > because they must hide the underlying layout used by the hardware,
> > > > > > and always return contiguous in-band and out-of-band data.
> > > > > >
> > > > > > Currently, similar code is duplicated to reorganize the data layout.
> > > > > > For example, denali_read_page_raw() and denali_write_page_raw() look
> > > > > > almost the same.
> > > > > >
> > > > > > The idea for refactoring is to split the code into two parts:
> > > > > >   [1] conversion of page layout
> > > > > >   [2] what to do at every ECC chunk boundary
> > > > > >
> > > > > > For [1], I wrote denali_raw_payload_op() and denali_raw_oob_op().
> > > > > > They manipulate data for the Denali controller's specific page layout
> > > > > > of in-band, out-of-band, respectively.
> > > > > >
> > > > > > The difference between write and read is just the operation at
> > > > > > ECC chunk boundaries. For example, denali_read_oob() calls
> > > > > > nand_change_read_column_op(), whereas denali_write_oob() calls
> > > > > > nand_change_write_column_op(). So, I implemented [2] as a callback
> > > > > > passed into [1].
> > > > > >
> > > > > > Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
> > > > > > ---
> > > > > >
> > > > >
> > > > > [...]
> > > > >
> > > > > >  static int denali_read_page_raw(struct nand_chip *chip, uint8_t *buf,
> > > > > >                               int oob_required, int page)
> > > > > >  {
> > > > > > +     struct denali_nand_info *denali = to_denali(chip);
> > > > > >       struct mtd_info *mtd = nand_to_mtd(chip);
> > > > > > -     struct denali_nand_info *denali = mtd_to_denali(mtd);
> > > > > > -     int writesize = mtd->writesize;
> > > > > > -     int oobsize = mtd->oobsize;
> > > > > > -     int ecc_steps = chip->ecc.steps;
> > > > > > -     int ecc_size = chip->ecc.size;
> > > > > > -     int ecc_bytes = chip->ecc.bytes;
> > > > > >       void *tmp_buf = denali->buf;
> > > > > > -     int oob_skip = denali->oob_skip_bytes;
> > > > > > -     size_t size = writesize + oobsize;
> > > > > > -     int ret, i, pos, len;
> > > > > > +     size_t size = mtd->writesize + mtd->oobsize;
> > > > > > +     int ret;
> > > > > > +
> > > > > > +     if (!buf)
> > > > > > +             return -EINVAL;
> > > > > >
> > > > > >       ret = denali_data_xfer(chip, tmp_buf, size, page, 1, 0);
> > > > > >       if (ret)
> > > > > >               return ret;
> > > > > >
> > > > > > -     /* Arrange the buffer for syndrome payload/ecc layout */
> > > > > > -     if (buf) {
> > > > > > -             for (i = 0; i < ecc_steps; i++) {
> > > > > > -                     pos = i * (ecc_size + ecc_bytes);
> > > > > > -                     len = ecc_size;
> > > > > > -
> > > > > > -                     if (pos >= writesize)
> > > > > > -                             pos += oob_skip;
> > > > > > -                     else if (pos + len > writesize)
> > > > > > -                             len = writesize - pos;
> > > > > > -
> > > > > > -                     memcpy(buf, tmp_buf + pos, len);
> > > > > > -                     buf += len;
> > > > > > -                     if (len < ecc_size) {
> > > > > > -                             len = ecc_size - len;
> > > > > > -                             memcpy(buf, tmp_buf + writesize + oob_skip,
> > > > > > -                                    len);
> > > > > > -                             buf += len;
> > > > > > -                     }
> > > > > > -             }
> > > > > > -     }
> > > > > > +     ret = denali_raw_payload_op(chip, buf, denali_memcpy_in, tmp_buf);
> > > > >
> > > > > Honestly, I still don't like passing denali_memcpy_in/out as parameter.
> > > > >
> > > > > Besides that, once you'll have added helpers to avoid abusing the
> > > > > ternary operator in 4/9, the rest looks fine by me.
> > > > >
> > > >
> > > >
> > > > Do you have any suggestion?
> > >
> > > Maybe register these two helpers at probe as controller specific hooks,
> > > then just pass an in/out boolean to the function?
> > >
> >
> > Sorry, I do not understand.
> >
> > Are you suggesting to do like follows in probe ?
> >
> > denali->change_column_read_raw = denali_memcpy_in;
> > denali->change_column_write_raw = denali_memcpy_out;
> > denali->change_column_read_oob = denali_change_read_column_op;
> > denali->change_column_write_oob = denali_change_write_column_op;
> >
> >
> > All the 4 hooks are always needed
> > regardless of any probed features.
> >
> >
> > The result is just textual replacement
> > denali_* with denali->*.
> >
> > What's the point of copying fixed function addresses
> > to denali structure?
> >
> >
>
> What I don't like is the function pointer as a function parameter.

This is a usual way to handle callback.

> You
> can use the functions defined statically if you prefer as long as the
> parameter is just a boolean for instance?



I still do not understand your concern,
but if you ban the use of function pointer,
the following is the best I can do
since there are 4 hooks depending on the
combination of oob/raw, write/read.



if (oob) {
        if (write)
                return nand_change_write_column_op(chip, offset, buf,
                                                   len, false);
        else
                return nand_change_read_column_op(chip, offset, buf,
                                                  len, false);
}

if (write)
        memcpy(denali->buf + offset, buf, len);
else
        memcpy(buf, denali->buf + offset, len);

return 0;




BTW, when are .read_page_raw / .write_page_raw used?

Currently, I use "whole page access && memcpy" for better performance.

If those hooks are rarely used, I use
nand_change_write_column_op / nand_change_read_column_op,
which will reduce the if-conditional.




>
> Thanks,
> Miquèl
>
> ______________________________________________________
> Linux MTD discussion mailing list
> http://lists.infradead.org/mailman/listinfo/linux-mtd/



-- 
Best Regards
Masahiro Yamada

______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v3 2/9] mtd: rawnand: denali: refactor syndrome layout handling for raw access
  2019-03-14  8:24               ` Masahiro Yamada
@ 2019-03-15  8:34                 ` Miquel Raynal
  -1 siblings, 0 replies; 40+ messages in thread
From: Miquel Raynal @ 2019-03-15  8:34 UTC (permalink / raw)
  To: Masahiro Yamada
  Cc: Boris Brezillon, Richard Weinberger, Linux Kernel Mailing List,
	Marek Vasut, linux-mtd, Brian Norris, David Woodhouse

Hi Masahiro,

Masahiro Yamada <yamada.masahiro@socionext.com> wrote on Thu, 14 Mar
2019 17:24:41 +0900:

> Hi Miquel,
> 
> On Tue, Mar 12, 2019 at 10:13 PM Miquel Raynal
> <miquel.raynal@bootlin.com> wrote:
> >
> > Hi Masahiro,
> >
> > Masahiro Yamada <yamada.masahiro@socionext.com> wrote on Tue, 12 Mar
> > 2019 20:07:27 +0900:
> >  
> > > Hi Miquel,
> > >
> > >
> > > On Tue, Mar 12, 2019 at 7:54 PM Miquel Raynal <miquel.raynal@bootlin.com> wrote:  
> > > >
> > > > Hi Masahiro,
> > > >
> > > > Masahiro Yamada <yamada.masahiro@socionext.com> wrote on Tue, 12 Mar
> > > > 2019 19:51:21 +0900:
> > > >  
> > > > > On Tue, Mar 12, 2019 at 7:28 PM Miquel Raynal <miquel.raynal@bootlin.com> wrote:  
> > > > > >
> > > > > > Hi Masahiro,
> > > > > >
> > > > > > Masahiro Yamada <yamada.masahiro@socionext.com> wrote on Tue, 12 Mar
> > > > > > 2019 17:44:43 +0900:
> > > > > >  
> > > > > > > The Denali IP adopts the syndrome page layout (payload and ECC are
> > > > > > > interleaved). The *_page_raw() and *_oob() callbacks are complicated
> > > > > > > because they must hide the underlying layout used by the hardware,
> > > > > > > and always return contiguous in-band and out-of-band data.
> > > > > > >
> > > > > > > Currently, similar code is duplicated to reorganize the data layout.
> > > > > > > For example, denali_read_page_raw() and denali_write_page_raw() look
> > > > > > > almost the same.
> > > > > > >
> > > > > > > The idea for refactoring is to split the code into two parts:
> > > > > > >   [1] conversion of page layout
> > > > > > >   [2] what to do at every ECC chunk boundary
> > > > > > >
> > > > > > > For [1], I wrote denali_raw_payload_op() and denali_raw_oob_op().
> > > > > > > They manipulate data for the Denali controller's specific page layout
> > > > > > > of in-band, out-of-band, respectively.
> > > > > > >
> > > > > > > The difference between write and read is just the operation at
> > > > > > > ECC chunk boundaries. For example, denali_read_oob() calls
> > > > > > > nand_change_read_column_op(), whereas denali_write_oob() calls
> > > > > > > nand_change_write_column_op(). So, I implemented [2] as a callback
> > > > > > > passed into [1].
> > > > > > >
> > > > > > > Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
> > > > > > > ---
> > > > > > >  
> > > > > >
> > > > > > [...]
> > > > > >  
> > > > > > >  static int denali_read_page_raw(struct nand_chip *chip, uint8_t *buf,
> > > > > > >                               int oob_required, int page)
> > > > > > >  {
> > > > > > > +     struct denali_nand_info *denali = to_denali(chip);
> > > > > > >       struct mtd_info *mtd = nand_to_mtd(chip);
> > > > > > > -     struct denali_nand_info *denali = mtd_to_denali(mtd);
> > > > > > > -     int writesize = mtd->writesize;
> > > > > > > -     int oobsize = mtd->oobsize;
> > > > > > > -     int ecc_steps = chip->ecc.steps;
> > > > > > > -     int ecc_size = chip->ecc.size;
> > > > > > > -     int ecc_bytes = chip->ecc.bytes;
> > > > > > >       void *tmp_buf = denali->buf;
> > > > > > > -     int oob_skip = denali->oob_skip_bytes;
> > > > > > > -     size_t size = writesize + oobsize;
> > > > > > > -     int ret, i, pos, len;
> > > > > > > +     size_t size = mtd->writesize + mtd->oobsize;
> > > > > > > +     int ret;
> > > > > > > +
> > > > > > > +     if (!buf)
> > > > > > > +             return -EINVAL;
> > > > > > >
> > > > > > >       ret = denali_data_xfer(chip, tmp_buf, size, page, 1, 0);
> > > > > > >       if (ret)
> > > > > > >               return ret;
> > > > > > >
> > > > > > > -     /* Arrange the buffer for syndrome payload/ecc layout */
> > > > > > > -     if (buf) {
> > > > > > > -             for (i = 0; i < ecc_steps; i++) {
> > > > > > > -                     pos = i * (ecc_size + ecc_bytes);
> > > > > > > -                     len = ecc_size;
> > > > > > > -
> > > > > > > -                     if (pos >= writesize)
> > > > > > > -                             pos += oob_skip;
> > > > > > > -                     else if (pos + len > writesize)
> > > > > > > -                             len = writesize - pos;
> > > > > > > -
> > > > > > > -                     memcpy(buf, tmp_buf + pos, len);
> > > > > > > -                     buf += len;
> > > > > > > -                     if (len < ecc_size) {
> > > > > > > -                             len = ecc_size - len;
> > > > > > > -                             memcpy(buf, tmp_buf + writesize + oob_skip,
> > > > > > > -                                    len);
> > > > > > > -                             buf += len;
> > > > > > > -                     }
> > > > > > > -             }
> > > > > > > -     }
> > > > > > > +     ret = denali_raw_payload_op(chip, buf, denali_memcpy_in, tmp_buf);  
> > > > > >
> > > > > > Honestly, I still don't like passing denali_memcpy_in/out as parameter.
> > > > > >
> > > > > > Besides that, once you'll have added helpers to avoid abusing the
> > > > > > ternary operator in 4/9, the rest looks fine by me.
> > > > > >  
> > > > >
> > > > >
> > > > > Do you have any suggestion?  
> > > >
> > > > Maybe register these two helpers at probe as controller specific hooks,
> > > > then just pass an in/out boolean to the function?
> > > >  
> > >
> > > Sorry, I do not understand.
> > >
> > > Are you suggesting to do like follows in probe ?
> > >
> > > denali->change_column_read_raw = denali_memcpy_in;
> > > denali->change_column_write_raw = denali_memcpy_out;
> > > denali->change_column_read_oob = denali_change_read_column_op;
> > > denali->change_column_write_oob = denali_change_write_column_op;
> > >
> > >
> > > All the 4 hooks are always needed
> > > regardless of any probed features.
> > >
> > >
> > > The result is just textual replacement
> > > denali_* with denali->*.
> > >
> > > What's the point of copying fixed function addresses
> > > to denali structure?
> > >
> > >  
> >
> > What I don't like is the function pointer as a function parameter.  
> 
> This is a usual way to handle callback.
> 
> > You
> > can use the functions defined statically if you prefer as long as the
> > parameter is just a boolean for instance?  
> 
> 
> 
> I still do not understand your concern,
> but if you ban the use of function pointer,
> the following is the best I can do
> since there are 4 hooks depending on the
> combination of oob/raw, write/read.
> 
> 
> 
> if (oob) {
>         if (write)
>                 return nand_change_write_column_op(chip, offset, buf,
>                                                    len, false);
>         else
>                 return nand_change_read_column_op(chip, offset, buf,
>                                                   len, false);
> }
> 
> if (write)
>         memcpy(denali->buf + offset, buf, len);
> else
>         memcpy(buf, denali->buf + offset, len);
> 
> return 0;

No, I meant passing a boolean to denali_raw_payload_op() instead of a
function pointer. Then from denali_raw_payload_op(), intead of doing

ret = cb();
if (ret)
        ...

doing:

if (read)
        ret = denali_memcpy_in()
else
        ret = denali_memcpy_out()

if (ret)
        ...

But nevermind, if this is bothering you too much let's keep the current
form, it's fine.

> 
> 
> BTW, when are .read_page_raw / .write_page_raw used?

I'm not sure what is the question here but these hooks are important
and allow to test the driver. nandbiterrs use them (although we do
not care about the performance in these hooks).

> 
> Currently, I use "whole page access && memcpy" for better performance.
> 
> If those hooks are rarely used, I use
> nand_change_write_column_op / nand_change_read_column_op,
> which will reduce the if-conditional.

Yes you can. We do not care about performance in raw accessors.

Thanks,
Miquèl

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v3 2/9] mtd: rawnand: denali: refactor syndrome layout handling for raw access
@ 2019-03-15  8:34                 ` Miquel Raynal
  0 siblings, 0 replies; 40+ messages in thread
From: Miquel Raynal @ 2019-03-15  8:34 UTC (permalink / raw)
  To: Masahiro Yamada
  Cc: Boris Brezillon, Richard Weinberger, Linux Kernel Mailing List,
	Marek Vasut, linux-mtd, Brian Norris, David Woodhouse

Hi Masahiro,

Masahiro Yamada <yamada.masahiro@socionext.com> wrote on Thu, 14 Mar
2019 17:24:41 +0900:

> Hi Miquel,
> 
> On Tue, Mar 12, 2019 at 10:13 PM Miquel Raynal
> <miquel.raynal@bootlin.com> wrote:
> >
> > Hi Masahiro,
> >
> > Masahiro Yamada <yamada.masahiro@socionext.com> wrote on Tue, 12 Mar
> > 2019 20:07:27 +0900:
> >  
> > > Hi Miquel,
> > >
> > >
> > > On Tue, Mar 12, 2019 at 7:54 PM Miquel Raynal <miquel.raynal@bootlin.com> wrote:  
> > > >
> > > > Hi Masahiro,
> > > >
> > > > Masahiro Yamada <yamada.masahiro@socionext.com> wrote on Tue, 12 Mar
> > > > 2019 19:51:21 +0900:
> > > >  
> > > > > On Tue, Mar 12, 2019 at 7:28 PM Miquel Raynal <miquel.raynal@bootlin.com> wrote:  
> > > > > >
> > > > > > Hi Masahiro,
> > > > > >
> > > > > > Masahiro Yamada <yamada.masahiro@socionext.com> wrote on Tue, 12 Mar
> > > > > > 2019 17:44:43 +0900:
> > > > > >  
> > > > > > > The Denali IP adopts the syndrome page layout (payload and ECC are
> > > > > > > interleaved). The *_page_raw() and *_oob() callbacks are complicated
> > > > > > > because they must hide the underlying layout used by the hardware,
> > > > > > > and always return contiguous in-band and out-of-band data.
> > > > > > >
> > > > > > > Currently, similar code is duplicated to reorganize the data layout.
> > > > > > > For example, denali_read_page_raw() and denali_write_page_raw() look
> > > > > > > almost the same.
> > > > > > >
> > > > > > > The idea for refactoring is to split the code into two parts:
> > > > > > >   [1] conversion of page layout
> > > > > > >   [2] what to do at every ECC chunk boundary
> > > > > > >
> > > > > > > For [1], I wrote denali_raw_payload_op() and denali_raw_oob_op().
> > > > > > > They manipulate data for the Denali controller's specific page layout
> > > > > > > of in-band, out-of-band, respectively.
> > > > > > >
> > > > > > > The difference between write and read is just the operation at
> > > > > > > ECC chunk boundaries. For example, denali_read_oob() calls
> > > > > > > nand_change_read_column_op(), whereas denali_write_oob() calls
> > > > > > > nand_change_write_column_op(). So, I implemented [2] as a callback
> > > > > > > passed into [1].
> > > > > > >
> > > > > > > Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
> > > > > > > ---
> > > > > > >  
> > > > > >
> > > > > > [...]
> > > > > >  
> > > > > > >  static int denali_read_page_raw(struct nand_chip *chip, uint8_t *buf,
> > > > > > >                               int oob_required, int page)
> > > > > > >  {
> > > > > > > +     struct denali_nand_info *denali = to_denali(chip);
> > > > > > >       struct mtd_info *mtd = nand_to_mtd(chip);
> > > > > > > -     struct denali_nand_info *denali = mtd_to_denali(mtd);
> > > > > > > -     int writesize = mtd->writesize;
> > > > > > > -     int oobsize = mtd->oobsize;
> > > > > > > -     int ecc_steps = chip->ecc.steps;
> > > > > > > -     int ecc_size = chip->ecc.size;
> > > > > > > -     int ecc_bytes = chip->ecc.bytes;
> > > > > > >       void *tmp_buf = denali->buf;
> > > > > > > -     int oob_skip = denali->oob_skip_bytes;
> > > > > > > -     size_t size = writesize + oobsize;
> > > > > > > -     int ret, i, pos, len;
> > > > > > > +     size_t size = mtd->writesize + mtd->oobsize;
> > > > > > > +     int ret;
> > > > > > > +
> > > > > > > +     if (!buf)
> > > > > > > +             return -EINVAL;
> > > > > > >
> > > > > > >       ret = denali_data_xfer(chip, tmp_buf, size, page, 1, 0);
> > > > > > >       if (ret)
> > > > > > >               return ret;
> > > > > > >
> > > > > > > -     /* Arrange the buffer for syndrome payload/ecc layout */
> > > > > > > -     if (buf) {
> > > > > > > -             for (i = 0; i < ecc_steps; i++) {
> > > > > > > -                     pos = i * (ecc_size + ecc_bytes);
> > > > > > > -                     len = ecc_size;
> > > > > > > -
> > > > > > > -                     if (pos >= writesize)
> > > > > > > -                             pos += oob_skip;
> > > > > > > -                     else if (pos + len > writesize)
> > > > > > > -                             len = writesize - pos;
> > > > > > > -
> > > > > > > -                     memcpy(buf, tmp_buf + pos, len);
> > > > > > > -                     buf += len;
> > > > > > > -                     if (len < ecc_size) {
> > > > > > > -                             len = ecc_size - len;
> > > > > > > -                             memcpy(buf, tmp_buf + writesize + oob_skip,
> > > > > > > -                                    len);
> > > > > > > -                             buf += len;
> > > > > > > -                     }
> > > > > > > -             }
> > > > > > > -     }
> > > > > > > +     ret = denali_raw_payload_op(chip, buf, denali_memcpy_in, tmp_buf);  
> > > > > >
> > > > > > Honestly, I still don't like passing denali_memcpy_in/out as parameter.
> > > > > >
> > > > > > Besides that, once you'll have added helpers to avoid abusing the
> > > > > > ternary operator in 4/9, the rest looks fine by me.
> > > > > >  
> > > > >
> > > > >
> > > > > Do you have any suggestion?  
> > > >
> > > > Maybe register these two helpers at probe as controller specific hooks,
> > > > then just pass an in/out boolean to the function?
> > > >  
> > >
> > > Sorry, I do not understand.
> > >
> > > Are you suggesting to do like follows in probe ?
> > >
> > > denali->change_column_read_raw = denali_memcpy_in;
> > > denali->change_column_write_raw = denali_memcpy_out;
> > > denali->change_column_read_oob = denali_change_read_column_op;
> > > denali->change_column_write_oob = denali_change_write_column_op;
> > >
> > >
> > > All the 4 hooks are always needed
> > > regardless of any probed features.
> > >
> > >
> > > The result is just textual replacement
> > > denali_* with denali->*.
> > >
> > > What's the point of copying fixed function addresses
> > > to denali structure?
> > >
> > >  
> >
> > What I don't like is the function pointer as a function parameter.  
> 
> This is a usual way to handle callback.
> 
> > You
> > can use the functions defined statically if you prefer as long as the
> > parameter is just a boolean for instance?  
> 
> 
> 
> I still do not understand your concern,
> but if you ban the use of function pointer,
> the following is the best I can do
> since there are 4 hooks depending on the
> combination of oob/raw, write/read.
> 
> 
> 
> if (oob) {
>         if (write)
>                 return nand_change_write_column_op(chip, offset, buf,
>                                                    len, false);
>         else
>                 return nand_change_read_column_op(chip, offset, buf,
>                                                   len, false);
> }
> 
> if (write)
>         memcpy(denali->buf + offset, buf, len);
> else
>         memcpy(buf, denali->buf + offset, len);
> 
> return 0;

No, I meant passing a boolean to denali_raw_payload_op() instead of a
function pointer. Then from denali_raw_payload_op(), intead of doing

ret = cb();
if (ret)
        ...

doing:

if (read)
        ret = denali_memcpy_in()
else
        ret = denali_memcpy_out()

if (ret)
        ...

But nevermind, if this is bothering you too much let's keep the current
form, it's fine.

> 
> 
> BTW, when are .read_page_raw / .write_page_raw used?

I'm not sure what is the question here but these hooks are important
and allow to test the driver. nandbiterrs use them (although we do
not care about the performance in these hooks).

> 
> Currently, I use "whole page access && memcpy" for better performance.
> 
> If those hooks are rarely used, I use
> nand_change_write_column_op / nand_change_read_column_op,
> which will reduce the if-conditional.

Yes you can. We do not care about performance in raw accessors.

Thanks,
Miquèl

______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v3 2/9] mtd: rawnand: denali: refactor syndrome layout handling for raw access
  2019-03-15  8:34                 ` Miquel Raynal
@ 2019-03-29  7:02                   ` Masahiro Yamada
  -1 siblings, 0 replies; 40+ messages in thread
From: Masahiro Yamada @ 2019-03-29  7:02 UTC (permalink / raw)
  To: Miquel Raynal
  Cc: Boris Brezillon, Richard Weinberger, Linux Kernel Mailing List,
	Marek Vasut, linux-mtd, Brian Norris, David Woodhouse

Hi Miquel,


On Fri, Mar 15, 2019 at 5:34 PM Miquel Raynal <miquel.raynal@bootlin.com> wrote:
>
> Hi Masahiro,
>
> Masahiro Yamada <yamada.masahiro@socionext.com> wrote on Thu, 14 Mar
> 2019 17:24:41 +0900:
>
> > Hi Miquel,
> >
> > On Tue, Mar 12, 2019 at 10:13 PM Miquel Raynal
> > <miquel.raynal@bootlin.com> wrote:
> > >
> > > Hi Masahiro,
> > >
> > > Masahiro Yamada <yamada.masahiro@socionext.com> wrote on Tue, 12 Mar
> > > 2019 20:07:27 +0900:
> > >
> > > > Hi Miquel,
> > > >
> > > >
> > > > On Tue, Mar 12, 2019 at 7:54 PM Miquel Raynal <miquel.raynal@bootlin.com> wrote:
> > > > >
> > > > > Hi Masahiro,
> > > > >
> > > > > Masahiro Yamada <yamada.masahiro@socionext.com> wrote on Tue, 12 Mar
> > > > > 2019 19:51:21 +0900:
> > > > >
> > > > > > On Tue, Mar 12, 2019 at 7:28 PM Miquel Raynal <miquel.raynal@bootlin.com> wrote:
> > > > > > >
> > > > > > > Hi Masahiro,
> > > > > > >
> > > > > > > Masahiro Yamada <yamada.masahiro@socionext.com> wrote on Tue, 12 Mar
> > > > > > > 2019 17:44:43 +0900:
> > > > > > >
> > > > > > > > The Denali IP adopts the syndrome page layout (payload and ECC are
> > > > > > > > interleaved). The *_page_raw() and *_oob() callbacks are complicated
> > > > > > > > because they must hide the underlying layout used by the hardware,
> > > > > > > > and always return contiguous in-band and out-of-band data.
> > > > > > > >
> > > > > > > > Currently, similar code is duplicated to reorganize the data layout.
> > > > > > > > For example, denali_read_page_raw() and denali_write_page_raw() look
> > > > > > > > almost the same.
> > > > > > > >
> > > > > > > > The idea for refactoring is to split the code into two parts:
> > > > > > > >   [1] conversion of page layout
> > > > > > > >   [2] what to do at every ECC chunk boundary
> > > > > > > >
> > > > > > > > For [1], I wrote denali_raw_payload_op() and denali_raw_oob_op().
> > > > > > > > They manipulate data for the Denali controller's specific page layout
> > > > > > > > of in-band, out-of-band, respectively.
> > > > > > > >
> > > > > > > > The difference between write and read is just the operation at
> > > > > > > > ECC chunk boundaries. For example, denali_read_oob() calls
> > > > > > > > nand_change_read_column_op(), whereas denali_write_oob() calls
> > > > > > > > nand_change_write_column_op(). So, I implemented [2] as a callback
> > > > > > > > passed into [1].
> > > > > > > >
> > > > > > > > Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
> > > > > > > > ---
> > > > > > > >
> > > > > > >
> > > > > > > [...]
> > > > > > >
> > > > > > > >  static int denali_read_page_raw(struct nand_chip *chip, uint8_t *buf,
> > > > > > > >                               int oob_required, int page)
> > > > > > > >  {
> > > > > > > > +     struct denali_nand_info *denali = to_denali(chip);
> > > > > > > >       struct mtd_info *mtd = nand_to_mtd(chip);
> > > > > > > > -     struct denali_nand_info *denali = mtd_to_denali(mtd);
> > > > > > > > -     int writesize = mtd->writesize;
> > > > > > > > -     int oobsize = mtd->oobsize;
> > > > > > > > -     int ecc_steps = chip->ecc.steps;
> > > > > > > > -     int ecc_size = chip->ecc.size;
> > > > > > > > -     int ecc_bytes = chip->ecc.bytes;
> > > > > > > >       void *tmp_buf = denali->buf;
> > > > > > > > -     int oob_skip = denali->oob_skip_bytes;
> > > > > > > > -     size_t size = writesize + oobsize;
> > > > > > > > -     int ret, i, pos, len;
> > > > > > > > +     size_t size = mtd->writesize + mtd->oobsize;
> > > > > > > > +     int ret;
> > > > > > > > +
> > > > > > > > +     if (!buf)
> > > > > > > > +             return -EINVAL;
> > > > > > > >
> > > > > > > >       ret = denali_data_xfer(chip, tmp_buf, size, page, 1, 0);
> > > > > > > >       if (ret)
> > > > > > > >               return ret;
> > > > > > > >
> > > > > > > > -     /* Arrange the buffer for syndrome payload/ecc layout */
> > > > > > > > -     if (buf) {
> > > > > > > > -             for (i = 0; i < ecc_steps; i++) {
> > > > > > > > -                     pos = i * (ecc_size + ecc_bytes);
> > > > > > > > -                     len = ecc_size;
> > > > > > > > -
> > > > > > > > -                     if (pos >= writesize)
> > > > > > > > -                             pos += oob_skip;
> > > > > > > > -                     else if (pos + len > writesize)
> > > > > > > > -                             len = writesize - pos;
> > > > > > > > -
> > > > > > > > -                     memcpy(buf, tmp_buf + pos, len);
> > > > > > > > -                     buf += len;
> > > > > > > > -                     if (len < ecc_size) {
> > > > > > > > -                             len = ecc_size - len;
> > > > > > > > -                             memcpy(buf, tmp_buf + writesize + oob_skip,
> > > > > > > > -                                    len);
> > > > > > > > -                             buf += len;
> > > > > > > > -                     }
> > > > > > > > -             }
> > > > > > > > -     }
> > > > > > > > +     ret = denali_raw_payload_op(chip, buf, denali_memcpy_in, tmp_buf);
> > > > > > >
> > > > > > > Honestly, I still don't like passing denali_memcpy_in/out as parameter.
> > > > > > >
> > > > > > > Besides that, once you'll have added helpers to avoid abusing the
> > > > > > > ternary operator in 4/9, the rest looks fine by me.
> > > > > > >
> > > > > >
> > > > > >
> > > > > > Do you have any suggestion?
> > > > >
> > > > > Maybe register these two helpers at probe as controller specific hooks,
> > > > > then just pass an in/out boolean to the function?
> > > > >
> > > >
> > > > Sorry, I do not understand.
> > > >
> > > > Are you suggesting to do like follows in probe ?
> > > >
> > > > denali->change_column_read_raw = denali_memcpy_in;
> > > > denali->change_column_write_raw = denali_memcpy_out;
> > > > denali->change_column_read_oob = denali_change_read_column_op;
> > > > denali->change_column_write_oob = denali_change_write_column_op;
> > > >
> > > >
> > > > All the 4 hooks are always needed
> > > > regardless of any probed features.
> > > >
> > > >
> > > > The result is just textual replacement
> > > > denali_* with denali->*.
> > > >
> > > > What's the point of copying fixed function addresses
> > > > to denali structure?
> > > >
> > > >
> > >
> > > What I don't like is the function pointer as a function parameter.
> >
> > This is a usual way to handle callback.
> >
> > > You
> > > can use the functions defined statically if you prefer as long as the
> > > parameter is just a boolean for instance?
> >
> >
> >
> > I still do not understand your concern,
> > but if you ban the use of function pointer,
> > the following is the best I can do
> > since there are 4 hooks depending on the
> > combination of oob/raw, write/read.
> >
> >
> >
> > if (oob) {
> >         if (write)
> >                 return nand_change_write_column_op(chip, offset, buf,
> >                                                    len, false);
> >         else
> >                 return nand_change_read_column_op(chip, offset, buf,
> >                                                   len, false);
> > }
> >
> > if (write)
> >         memcpy(denali->buf + offset, buf, len);
> > else
> >         memcpy(buf, denali->buf + offset, len);
> >
> > return 0;
>
> No, I meant passing a boolean to denali_raw_payload_op() instead of a
> function pointer. Then from denali_raw_payload_op(), intead of doing
>
> ret = cb();
> if (ret)
>         ...
>
> doing:
>
> if (read)
>         ret = denali_memcpy_in()
> else
>         ret = denali_memcpy_out()
>
> if (ret)
>         ...


If you look at my code closely,
you will notice 4 callbacks passed in
denali_raw_payload_op().

So, if-conditional would end up like follows:


    if (oob) {
            if (write)
                    ret = nand_change_write_column_op(chip, offset, buf,
                                                   len, false);
            else
                    ret = nand_change_read_column_op(chip, offset, buf,
                                                  len, false);
            if (ret)
                    return ret;
     } else {
            if (write)
                  memcpy(denali->buf + offset, buf, len);
            else
                  memcpy(buf, denali->buf + offset, len);
     }


This is extremely ugly.
That's why I passed a function pointer
instead of two boolean parameters 'oob', 'write'.




> But nevermind, if this is bothering you too much let's keep the current
> form, it's fine.
>
> >
> >
> > BTW, when are .read_page_raw / .write_page_raw used?
>
> I'm not sure what is the question here but these hooks are important
> and allow to test the driver. nandbiterrs use them (although we do
> not care about the performance in these hooks).


Currently, I use DMA transfer + memcpy()
in order to get better performance for
.read_page_raw() and .write_page_raw()


nand_change_write_column_op() and
nand_change_read_column_op() are slow
since they are low-level hardware accesses.


If we do not have to care about the performance,
I will only use nand_change_{write,read}_column_op().




> >
> > Currently, I use "whole page access && memcpy" for better performance.
> >
> > If those hooks are rarely used, I use
> > nand_change_write_column_op / nand_change_read_column_op,
> > which will reduce the if-conditional.
>
> Yes you can. We do not care about performance in raw accessors.

OK, I will do this in v4.




> Thanks,
> Miquèl
>
> ______________________________________________________
> Linux MTD discussion mailing list
> http://lists.infradead.org/mailman/listinfo/linux-mtd/



-- 
Best Regards
Masahiro Yamada

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v3 2/9] mtd: rawnand: denali: refactor syndrome layout handling for raw access
@ 2019-03-29  7:02                   ` Masahiro Yamada
  0 siblings, 0 replies; 40+ messages in thread
From: Masahiro Yamada @ 2019-03-29  7:02 UTC (permalink / raw)
  To: Miquel Raynal
  Cc: Boris Brezillon, Richard Weinberger, Linux Kernel Mailing List,
	Marek Vasut, linux-mtd, Brian Norris, David Woodhouse

Hi Miquel,


On Fri, Mar 15, 2019 at 5:34 PM Miquel Raynal <miquel.raynal@bootlin.com> wrote:
>
> Hi Masahiro,
>
> Masahiro Yamada <yamada.masahiro@socionext.com> wrote on Thu, 14 Mar
> 2019 17:24:41 +0900:
>
> > Hi Miquel,
> >
> > On Tue, Mar 12, 2019 at 10:13 PM Miquel Raynal
> > <miquel.raynal@bootlin.com> wrote:
> > >
> > > Hi Masahiro,
> > >
> > > Masahiro Yamada <yamada.masahiro@socionext.com> wrote on Tue, 12 Mar
> > > 2019 20:07:27 +0900:
> > >
> > > > Hi Miquel,
> > > >
> > > >
> > > > On Tue, Mar 12, 2019 at 7:54 PM Miquel Raynal <miquel.raynal@bootlin.com> wrote:
> > > > >
> > > > > Hi Masahiro,
> > > > >
> > > > > Masahiro Yamada <yamada.masahiro@socionext.com> wrote on Tue, 12 Mar
> > > > > 2019 19:51:21 +0900:
> > > > >
> > > > > > On Tue, Mar 12, 2019 at 7:28 PM Miquel Raynal <miquel.raynal@bootlin.com> wrote:
> > > > > > >
> > > > > > > Hi Masahiro,
> > > > > > >
> > > > > > > Masahiro Yamada <yamada.masahiro@socionext.com> wrote on Tue, 12 Mar
> > > > > > > 2019 17:44:43 +0900:
> > > > > > >
> > > > > > > > The Denali IP adopts the syndrome page layout (payload and ECC are
> > > > > > > > interleaved). The *_page_raw() and *_oob() callbacks are complicated
> > > > > > > > because they must hide the underlying layout used by the hardware,
> > > > > > > > and always return contiguous in-band and out-of-band data.
> > > > > > > >
> > > > > > > > Currently, similar code is duplicated to reorganize the data layout.
> > > > > > > > For example, denali_read_page_raw() and denali_write_page_raw() look
> > > > > > > > almost the same.
> > > > > > > >
> > > > > > > > The idea for refactoring is to split the code into two parts:
> > > > > > > >   [1] conversion of page layout
> > > > > > > >   [2] what to do at every ECC chunk boundary
> > > > > > > >
> > > > > > > > For [1], I wrote denali_raw_payload_op() and denali_raw_oob_op().
> > > > > > > > They manipulate data for the Denali controller's specific page layout
> > > > > > > > of in-band, out-of-band, respectively.
> > > > > > > >
> > > > > > > > The difference between write and read is just the operation at
> > > > > > > > ECC chunk boundaries. For example, denali_read_oob() calls
> > > > > > > > nand_change_read_column_op(), whereas denali_write_oob() calls
> > > > > > > > nand_change_write_column_op(). So, I implemented [2] as a callback
> > > > > > > > passed into [1].
> > > > > > > >
> > > > > > > > Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
> > > > > > > > ---
> > > > > > > >
> > > > > > >
> > > > > > > [...]
> > > > > > >
> > > > > > > >  static int denali_read_page_raw(struct nand_chip *chip, uint8_t *buf,
> > > > > > > >                               int oob_required, int page)
> > > > > > > >  {
> > > > > > > > +     struct denali_nand_info *denali = to_denali(chip);
> > > > > > > >       struct mtd_info *mtd = nand_to_mtd(chip);
> > > > > > > > -     struct denali_nand_info *denali = mtd_to_denali(mtd);
> > > > > > > > -     int writesize = mtd->writesize;
> > > > > > > > -     int oobsize = mtd->oobsize;
> > > > > > > > -     int ecc_steps = chip->ecc.steps;
> > > > > > > > -     int ecc_size = chip->ecc.size;
> > > > > > > > -     int ecc_bytes = chip->ecc.bytes;
> > > > > > > >       void *tmp_buf = denali->buf;
> > > > > > > > -     int oob_skip = denali->oob_skip_bytes;
> > > > > > > > -     size_t size = writesize + oobsize;
> > > > > > > > -     int ret, i, pos, len;
> > > > > > > > +     size_t size = mtd->writesize + mtd->oobsize;
> > > > > > > > +     int ret;
> > > > > > > > +
> > > > > > > > +     if (!buf)
> > > > > > > > +             return -EINVAL;
> > > > > > > >
> > > > > > > >       ret = denali_data_xfer(chip, tmp_buf, size, page, 1, 0);
> > > > > > > >       if (ret)
> > > > > > > >               return ret;
> > > > > > > >
> > > > > > > > -     /* Arrange the buffer for syndrome payload/ecc layout */
> > > > > > > > -     if (buf) {
> > > > > > > > -             for (i = 0; i < ecc_steps; i++) {
> > > > > > > > -                     pos = i * (ecc_size + ecc_bytes);
> > > > > > > > -                     len = ecc_size;
> > > > > > > > -
> > > > > > > > -                     if (pos >= writesize)
> > > > > > > > -                             pos += oob_skip;
> > > > > > > > -                     else if (pos + len > writesize)
> > > > > > > > -                             len = writesize - pos;
> > > > > > > > -
> > > > > > > > -                     memcpy(buf, tmp_buf + pos, len);
> > > > > > > > -                     buf += len;
> > > > > > > > -                     if (len < ecc_size) {
> > > > > > > > -                             len = ecc_size - len;
> > > > > > > > -                             memcpy(buf, tmp_buf + writesize + oob_skip,
> > > > > > > > -                                    len);
> > > > > > > > -                             buf += len;
> > > > > > > > -                     }
> > > > > > > > -             }
> > > > > > > > -     }
> > > > > > > > +     ret = denali_raw_payload_op(chip, buf, denali_memcpy_in, tmp_buf);
> > > > > > >
> > > > > > > Honestly, I still don't like passing denali_memcpy_in/out as parameter.
> > > > > > >
> > > > > > > Besides that, once you'll have added helpers to avoid abusing the
> > > > > > > ternary operator in 4/9, the rest looks fine by me.
> > > > > > >
> > > > > >
> > > > > >
> > > > > > Do you have any suggestion?
> > > > >
> > > > > Maybe register these two helpers at probe as controller specific hooks,
> > > > > then just pass an in/out boolean to the function?
> > > > >
> > > >
> > > > Sorry, I do not understand.
> > > >
> > > > Are you suggesting to do like follows in probe ?
> > > >
> > > > denali->change_column_read_raw = denali_memcpy_in;
> > > > denali->change_column_write_raw = denali_memcpy_out;
> > > > denali->change_column_read_oob = denali_change_read_column_op;
> > > > denali->change_column_write_oob = denali_change_write_column_op;
> > > >
> > > >
> > > > All the 4 hooks are always needed
> > > > regardless of any probed features.
> > > >
> > > >
> > > > The result is just textual replacement
> > > > denali_* with denali->*.
> > > >
> > > > What's the point of copying fixed function addresses
> > > > to denali structure?
> > > >
> > > >
> > >
> > > What I don't like is the function pointer as a function parameter.
> >
> > This is a usual way to handle callback.
> >
> > > You
> > > can use the functions defined statically if you prefer as long as the
> > > parameter is just a boolean for instance?
> >
> >
> >
> > I still do not understand your concern,
> > but if you ban the use of function pointer,
> > the following is the best I can do
> > since there are 4 hooks depending on the
> > combination of oob/raw, write/read.
> >
> >
> >
> > if (oob) {
> >         if (write)
> >                 return nand_change_write_column_op(chip, offset, buf,
> >                                                    len, false);
> >         else
> >                 return nand_change_read_column_op(chip, offset, buf,
> >                                                   len, false);
> > }
> >
> > if (write)
> >         memcpy(denali->buf + offset, buf, len);
> > else
> >         memcpy(buf, denali->buf + offset, len);
> >
> > return 0;
>
> No, I meant passing a boolean to denali_raw_payload_op() instead of a
> function pointer. Then from denali_raw_payload_op(), intead of doing
>
> ret = cb();
> if (ret)
>         ...
>
> doing:
>
> if (read)
>         ret = denali_memcpy_in()
> else
>         ret = denali_memcpy_out()
>
> if (ret)
>         ...


If you look at my code closely,
you will notice 4 callbacks passed in
denali_raw_payload_op().

So, if-conditional would end up like follows:


    if (oob) {
            if (write)
                    ret = nand_change_write_column_op(chip, offset, buf,
                                                   len, false);
            else
                    ret = nand_change_read_column_op(chip, offset, buf,
                                                  len, false);
            if (ret)
                    return ret;
     } else {
            if (write)
                  memcpy(denali->buf + offset, buf, len);
            else
                  memcpy(buf, denali->buf + offset, len);
     }


This is extremely ugly.
That's why I passed a function pointer
instead of two boolean parameters 'oob', 'write'.




> But nevermind, if this is bothering you too much let's keep the current
> form, it's fine.
>
> >
> >
> > BTW, when are .read_page_raw / .write_page_raw used?
>
> I'm not sure what is the question here but these hooks are important
> and allow to test the driver. nandbiterrs use them (although we do
> not care about the performance in these hooks).


Currently, I use DMA transfer + memcpy()
in order to get better performance for
.read_page_raw() and .write_page_raw()


nand_change_write_column_op() and
nand_change_read_column_op() are slow
since they are low-level hardware accesses.


If we do not have to care about the performance,
I will only use nand_change_{write,read}_column_op().




> >
> > Currently, I use "whole page access && memcpy" for better performance.
> >
> > If those hooks are rarely used, I use
> > nand_change_write_column_op / nand_change_read_column_op,
> > which will reduce the if-conditional.
>
> Yes you can. We do not care about performance in raw accessors.

OK, I will do this in v4.




> Thanks,
> Miquèl
>
> ______________________________________________________
> Linux MTD discussion mailing list
> http://lists.infradead.org/mailman/listinfo/linux-mtd/



-- 
Best Regards
Masahiro Yamada

______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/

^ permalink raw reply	[flat|nested] 40+ messages in thread

end of thread, other threads:[~2019-03-29  7:03 UTC | newest]

Thread overview: 40+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-03-12  8:44 [PATCH v3 0/9] mtd: rawnand: denali: exec_op(), controller/chip separation, and cleanups Masahiro Yamada
2019-03-12  8:44 ` Masahiro Yamada
2019-03-12  8:44 ` [PATCH v3 1/9] mtd: rawnand: denali: use nand_chip pointer more for internal functions Masahiro Yamada
2019-03-12  8:44   ` Masahiro Yamada
2019-03-12  8:44 ` [PATCH v3 2/9] mtd: rawnand: denali: refactor syndrome layout handling for raw access Masahiro Yamada
2019-03-12  8:44   ` Masahiro Yamada
2019-03-12 10:28   ` Miquel Raynal
2019-03-12 10:28     ` Miquel Raynal
2019-03-12 10:51     ` Masahiro Yamada
2019-03-12 10:51       ` Masahiro Yamada
2019-03-12 10:54       ` Miquel Raynal
2019-03-12 10:54         ` Miquel Raynal
2019-03-12 11:07         ` Masahiro Yamada
2019-03-12 11:07           ` Masahiro Yamada
2019-03-12 13:13           ` Miquel Raynal
2019-03-12 13:13             ` Miquel Raynal
2019-03-14  8:24             ` Masahiro Yamada
2019-03-14  8:24               ` Masahiro Yamada
2019-03-15  8:34               ` Miquel Raynal
2019-03-15  8:34                 ` Miquel Raynal
2019-03-29  7:02                 ` Masahiro Yamada
2019-03-29  7:02                   ` Masahiro Yamada
2019-03-12  8:44 ` [PATCH v3 3/9] mtd: rawnand: denali: remove unneeded casts in denali_{read,write}_pio Masahiro Yamada
2019-03-12  8:44   ` [PATCH v3 3/9] mtd: rawnand: denali: remove unneeded casts in denali_{read, write}_pio Masahiro Yamada
2019-03-12  8:44 ` [PATCH v3 4/9] mtd: rawnand: denali: switch over to ->exec_op() from legacy hooks Masahiro Yamada
2019-03-12  8:44   ` Masahiro Yamada
2019-03-12  9:02   ` Boris Brezillon
2019-03-12  9:02     ` Boris Brezillon
2019-03-12  9:47     ` Masahiro Yamada
2019-03-12  9:47       ` Masahiro Yamada
2019-03-12  8:44 ` [PATCH v3 5/9] mtd: rawnand: denali: use bool type instead of int where appropriate Masahiro Yamada
2019-03-12  8:44   ` Masahiro Yamada
2019-03-12  8:44 ` [PATCH v3 6/9] mtd: rawnand: denali_pci: rename goto labels Masahiro Yamada
2019-03-12  8:44   ` Masahiro Yamada
2019-03-12  8:44 ` [PATCH v3 7/9] mtd: rawnand: denali: decouple controller and NAND chips Masahiro Yamada
2019-03-12  8:44   ` Masahiro Yamada
2019-03-12  8:44 ` [PATCH v3 8/9] mtd: rawnand: denali: remove DENALI_NR_BANKS macro Masahiro Yamada
2019-03-12  8:44   ` Masahiro Yamada
2019-03-12  8:44 ` [PATCH v3 9/9] mtd: rawnand: denali: clean up coding style Masahiro Yamada
2019-03-12  8:44   ` Masahiro Yamada

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.