* [PATCH 01/26] mtd: Drop asynchronous erase support
2020-11-06 13:38 [PATCH 00/26] Update nand layer to Linux-5.9 Sascha Hauer
@ 2020-11-06 13:38 ` Sascha Hauer
2020-11-06 13:38 ` [PATCH 02/26] mtd: nand: remove unused header file Sascha Hauer
` (24 subsequent siblings)
25 siblings, 0 replies; 27+ messages in thread
From: Sascha Hauer @ 2020-11-06 13:38 UTC (permalink / raw)
To: Barebox List
asynchronous erase support is unused and also dropped from the Kernel,
so remove it from barebox.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
drivers/mtd/core.c | 6 ++----
drivers/mtd/devices/docg3.c | 4 ----
drivers/mtd/devices/mtd_dataflash.c | 4 ----
drivers/mtd/devices/mtdram.c | 2 --
drivers/mtd/mtdconcat.c | 4 +---
drivers/mtd/nand/nand_base.c | 19 +++++--------------
drivers/mtd/nor/cfi_flash.c | 7 +------
drivers/mtd/spi-nor/spi-nor.c | 4 ----
drivers/net/e1000/eeprom.c | 4 ----
include/linux/mtd/mtd.h | 25 -------------------------
10 files changed, 9 insertions(+), 70 deletions(-)
diff --git a/drivers/mtd/core.c b/drivers/mtd/core.c
index 1625d938ea..13f3d61f00 100644
--- a/drivers/mtd/core.c
+++ b/drivers/mtd/core.c
@@ -422,11 +422,9 @@ int mtd_erase(struct mtd_info *mtd, struct erase_info *instr)
if (!(mtd->flags & MTD_WRITEABLE))
return -EROFS;
instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN;
- if (!instr->len) {
- instr->state = MTD_ERASE_DONE;
- mtd_erase_callback(instr);
+ if (!instr->len)
return 0;
- }
+
return mtd->erase(mtd, instr);
}
diff --git a/drivers/mtd/devices/docg3.c b/drivers/mtd/devices/docg3.c
index a40ba25632..8921086a63 100644
--- a/drivers/mtd/devices/docg3.c
+++ b/drivers/mtd/devices/docg3.c
@@ -852,7 +852,6 @@ static int doc_erase(struct mtd_info *mtd, struct erase_info *info)
doc_dbg("doc_erase(from=%lld, len=%lld\n", info->addr, info->len);
doc_set_device_id(docg3, docg3->device_id);
- info->state = MTD_ERASE_PENDING;
calc_block_sector(info->addr + info->len, &block0, &block1, &page,
&ofs, docg3->reliable);
ret = -EINVAL;
@@ -864,7 +863,6 @@ static int doc_erase(struct mtd_info *mtd, struct erase_info *info)
docg3->reliable);
doc_set_reliable_mode(docg3);
for (len = info->len; !ret && len > 0; len -= mtd->erasesize) {
- info->state = MTD_ERASING;
ret = doc_erase_block(docg3, block0, block1);
block0 += 2;
block1 += 2;
@@ -873,11 +871,9 @@ static int doc_erase(struct mtd_info *mtd, struct erase_info *info)
if (ret)
goto reset_err;
- info->state = MTD_ERASE_DONE;
return 0;
reset_err:
- info->state = MTD_ERASE_FAILED;
return ret;
}
diff --git a/drivers/mtd/devices/mtd_dataflash.c b/drivers/mtd/devices/mtd_dataflash.c
index 7980a91e19..cd5ab6c972 100644
--- a/drivers/mtd/devices/mtd_dataflash.c
+++ b/drivers/mtd/devices/mtd_dataflash.c
@@ -213,10 +213,6 @@ static int dataflash_erase(struct mtd_info *mtd, struct erase_info *instr)
}
}
- /* Inform MTD subsystem that erase is complete */
- instr->state = MTD_ERASE_DONE;
- mtd_erase_callback(instr);
-
return 0;
}
diff --git a/drivers/mtd/devices/mtdram.c b/drivers/mtd/devices/mtdram.c
index acaf002258..783e1448d6 100644
--- a/drivers/mtd/devices/mtdram.c
+++ b/drivers/mtd/devices/mtdram.c
@@ -28,8 +28,6 @@ struct mtdram_priv_data {
static int ram_erase(struct mtd_info *mtd, struct erase_info *instr)
{
memset((char *)mtd->priv + instr->addr, 0xff, instr->len);
- instr->state = MTD_ERASE_DONE;
- mtd_erase_callback(instr);
return 0;
}
diff --git a/drivers/mtd/mtdconcat.c b/drivers/mtd/mtdconcat.c
index fa430712d0..d53c4b22e1 100644
--- a/drivers/mtd/mtdconcat.c
+++ b/drivers/mtd/mtdconcat.c
@@ -383,13 +383,11 @@ static int concat_erase(struct mtd_info *mtd, struct erase_info *instr)
erase->addr = 0;
offset += subdev->size;
}
- instr->state = erase->state;
+
kfree(erase);
if (err)
return err;
- if (instr->callback)
- instr->callback(instr);
return 0;
}
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index 3f4c787f49..2b37679d2a 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -952,7 +952,6 @@ int nand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
if (nand_check_wp(mtd)) {
pr_debug("%s: device is write protected!\n",
__func__);
- status = MTD_ERASE_FAILED;
ret = -EIO;
goto out;
}
@@ -2721,15 +2720,13 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
if (nand_check_wp(mtd)) {
pr_debug("%s: device is write protected!\n",
__func__);
- instr->state = MTD_ERASE_FAILED;
+ ret = -EIO;
goto erase_exit;
}
/* Loop through the pages */
len = instr->len;
- instr->state = MTD_ERASING;
-
while (len) {
/* Check if we have a bad block, we do not erase bad blocks! */
if (!mtd->allow_erasebad &&
@@ -2737,7 +2734,7 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
chip->page_shift, 0, allowbbt)) {
pr_warn("%s: attempt to erase a bad block at page 0x%08x\n",
__func__, page);
- instr->state = MTD_ERASE_FAILED;
+ ret = -EIO;
goto erase_exit;
}
@@ -2765,7 +2762,7 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
if (status & NAND_STATUS_FAIL) {
pr_debug("%s: failed erase, page 0x%08x\n",
__func__, page);
- instr->state = MTD_ERASE_FAILED;
+ ret = -EIO;
instr->fail_addr =
((loff_t)page << chip->page_shift);
goto erase_exit;
@@ -2782,20 +2779,14 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
chip->select_chip(mtd, chipnr);
}
}
- instr->state = MTD_ERASE_DONE;
-
-erase_exit:
- ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO;
+ ret = 0;
+erase_exit:
/* Deselect and wake up anyone waiting on the device */
chip->select_chip(mtd, -1);
nand_release_device(mtd);
- /* Do call back function */
- if (!ret)
- mtd_erase_callback(instr);
-
/* Return more or less happy */
return ret;
}
diff --git a/drivers/mtd/nor/cfi_flash.c b/drivers/mtd/nor/cfi_flash.c
index 430f926e8c..0988e67347 100644
--- a/drivers/mtd/nor/cfi_flash.c
+++ b/drivers/mtd/nor/cfi_flash.c
@@ -920,13 +920,8 @@ static int cfi_mtd_erase(struct mtd_info *mtd, struct erase_info *instr)
int ret;
ret = cfi_erase(info, instr->len, instr->addr);
- if (ret) {
- instr->state = MTD_ERASE_FAILED;
+ if (ret)
return -EIO;
- }
-
- instr->state = MTD_ERASE_DONE;
- mtd_erase_callback(instr);
return 0;
}
diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
index 5cc4573178..528cd9c399 100644
--- a/drivers/mtd/spi-nor/spi-nor.c
+++ b/drivers/mtd/spi-nor/spi-nor.c
@@ -540,14 +540,10 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_ERASE);
- instr->state = MTD_ERASE_DONE;
- mtd_erase_callback(instr);
-
return ret;
erase_err:
spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_ERASE);
- instr->state = MTD_ERASE_FAILED;
return ret;
}
diff --git a/drivers/net/e1000/eeprom.c b/drivers/net/e1000/eeprom.c
index 5b34e9b8d1..900be2f09b 100644
--- a/drivers/net/e1000/eeprom.c
+++ b/drivers/net/e1000/eeprom.c
@@ -1426,13 +1426,9 @@ static int e1000_mtd_erase(struct mtd_info *mtd, struct erase_info *instr)
if (ret < 0)
goto fail;
- instr->state = MTD_ERASE_DONE;
- mtd_erase_callback(instr);
-
return 0;
fail:
- instr->state = MTD_ERASE_FAILED;
return ret;
}
diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h
index 16725ac4bc..8257403335 100644
--- a/include/linux/mtd/mtd.h
+++ b/include/linux/mtd/mtd.h
@@ -20,12 +20,6 @@
#define MTD_BLOCK_MAJOR 31
#define MAX_MTD_DEVICES 32
-#define MTD_ERASE_PENDING 0x01
-#define MTD_ERASING 0x02
-#define MTD_ERASE_SUSPEND 0x04
-#define MTD_ERASE_DONE 0x08
-#define MTD_ERASE_FAILED 0x10
-
#define MTD_FAIL_ADDR_UNKNOWN -1LL
/* If the erase fails, fail_addr might indicate exactly which block failed. If
@@ -40,9 +34,7 @@ struct erase_info {
u_long retries;
u_int dev;
u_int cell;
- void (*callback) (struct erase_info *self);
u_long priv;
- u_char state;
struct erase_info *next;
};
@@ -141,13 +133,6 @@ struct mtd_info {
int numeraseregions;
struct mtd_erase_region_info *eraseregions;
- /*
- * Erase is an asynchronous operation. Device drivers are supposed
- * to call instr->callback() whenever the operation completes, even
- * if it completes with a failure.
- * Callers are supposed to pass a callback function and wait for it
- * to be called before writing to the block.
- */
int (*erase) (struct mtd_info *mtd, struct erase_info *instr);
int (*read) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
@@ -298,16 +283,6 @@ int mtd_del_partition(struct mtd_info *mtd);
extern void register_mtd_user (struct mtd_notifier *new);
extern int unregister_mtd_user (struct mtd_notifier *old);
-#ifdef CONFIG_MTD_PARTITIONS
-void mtd_erase_callback(struct erase_info *instr);
-#else
-static inline void mtd_erase_callback(struct erase_info *instr)
-{
- if (instr->callback)
- instr->callback(instr);
-}
-#endif
-
int mtd_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
int mtd_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
int mtd_block_isbad(struct mtd_info *mtd, loff_t ofs);
--
2.20.1
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 27+ messages in thread
* [PATCH 02/26] mtd: nand: remove unused header file
2020-11-06 13:38 [PATCH 00/26] Update nand layer to Linux-5.9 Sascha Hauer
2020-11-06 13:38 ` [PATCH 01/26] mtd: Drop asynchronous erase support Sascha Hauer
@ 2020-11-06 13:38 ` Sascha Hauer
2020-11-06 13:38 ` [PATCH 03/26] mtd: nand: drop unused erase_cmd hook Sascha Hauer
` (23 subsequent siblings)
25 siblings, 0 replies; 27+ messages in thread
From: Sascha Hauer @ 2020-11-06 13:38 UTC (permalink / raw)
To: Barebox List
nand.h is unused, remove it.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
drivers/mtd/nand/nand.h | 32 --------------------------------
1 file changed, 32 deletions(-)
delete mode 100644 drivers/mtd/nand/nand.h
diff --git a/drivers/mtd/nand/nand.h b/drivers/mtd/nand/nand.h
deleted file mode 100644
index eb6652c14f..0000000000
--- a/drivers/mtd/nand/nand.h
+++ /dev/null
@@ -1,32 +0,0 @@
-#ifndef __NAND_H
-#define __NAND_H
-
-int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip,
- int page, int sndcmd);
-int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip,
- int page);
-int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs);
-int nand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int getchip,
- int allowbbt);
-int nand_block_isbad(struct mtd_info *mtd, loff_t offs);
-int nand_block_markbad(struct mtd_info *mtd, loff_t ofs);
-void nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len);
-void nand_write_buf16(struct mtd_info *mtd, const uint8_t *buf, int len);
-void single_erase_cmd(struct mtd_info *mtd, int page);
-void multi_erase_cmd(struct mtd_info *mtd, int page);
-void nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
- const uint8_t *buf);
-int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
- uint32_t offset, int data_len, const uint8_t *buf,
- int oob_required, int page, int cached, int raw);
-int nand_erase(struct mtd_info *mtd, struct erase_info *instr);
-int nand_write(struct mtd_info *mtd, loff_t to, size_t len,
- size_t *retlen, const uint8_t *buf);
-int nand_write_oob(struct mtd_info *mtd, loff_t to,
- struct mtd_oob_ops *ops);
-
-void nand_init_ecc_hw(struct nand_chip *chip);
-void nand_init_ecc_soft(struct nand_chip *chip);
-void nand_init_ecc_hw_syndrome(struct nand_chip *chip);
-
-#endif /* __NAND_H */
--
2.20.1
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 27+ messages in thread
* [PATCH 03/26] mtd: nand: drop unused erase_cmd hook
2020-11-06 13:38 [PATCH 00/26] Update nand layer to Linux-5.9 Sascha Hauer
2020-11-06 13:38 ` [PATCH 01/26] mtd: Drop asynchronous erase support Sascha Hauer
2020-11-06 13:38 ` [PATCH 02/26] mtd: nand: remove unused header file Sascha Hauer
@ 2020-11-06 13:38 ` Sascha Hauer
2020-11-06 13:38 ` [PATCH 04/26] mtd: nand: drop unused errstat hook Sascha Hauer
` (22 subsequent siblings)
25 siblings, 0 replies; 27+ messages in thread
From: Sascha Hauer @ 2020-11-06 13:38 UTC (permalink / raw)
To: Barebox List
chip->erase_cmd is never used, so remove this hook.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
drivers/mtd/nand/nand_base.c | 3 +--
include/linux/mtd/nand.h | 3 ---
2 files changed, 1 insertion(+), 5 deletions(-)
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index 2b37679d2a..999d56c5bd 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -2746,7 +2746,7 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
(page + pages_per_block))
chip->pagebuf = -1;
- chip->erase_cmd(mtd, page & chip->pagemask);
+ single_erase_cmd(mtd, page & chip->pagemask);
status = chip->waitfunc(mtd, chip);
@@ -3484,7 +3484,6 @@ ident_done:
}
chip->badblockbits = 8;
- chip->erase_cmd = single_erase_cmd;
/* Do not replace user supplied command function! */
if (mtd->writesize > 512 && chip->cmdfunc == nand_command)
diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
index 401f630570..c4bdddc61e 100644
--- a/include/linux/mtd/nand.h
+++ b/include/linux/mtd/nand.h
@@ -418,8 +418,6 @@ struct nand_buffers {
* @ecc: [BOARDSPECIFIC] ECC control structure
* @buffers: buffer structure for read/write
* @hwcontrol: platform-specific hardware control structure
- * @erase_cmd: [INTERN] erase command write function, selectable due
- * to AND support.
* @scan_bbt: [REPLACEABLE] function to scan bad block table
* @chip_delay: [BOARDSPECIFIC] chip dependent delay for transferring
* data from array to read regs (tR).
@@ -492,7 +490,6 @@ struct nand_chip {
void (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column,
int page_addr);
int(*waitfunc)(struct mtd_info *mtd, struct nand_chip *this);
- void (*erase_cmd)(struct mtd_info *mtd, int page);
int (*scan_bbt)(struct mtd_info *mtd);
int (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state,
int status, int page);
--
2.20.1
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 27+ messages in thread
* [PATCH 04/26] mtd: nand: drop unused errstat hook
2020-11-06 13:38 [PATCH 00/26] Update nand layer to Linux-5.9 Sascha Hauer
` (2 preceding siblings ...)
2020-11-06 13:38 ` [PATCH 03/26] mtd: nand: drop unused erase_cmd hook Sascha Hauer
@ 2020-11-06 13:38 ` Sascha Hauer
2020-11-06 13:38 ` [PATCH 05/26] mtd: nand: Pass struct nand_chip around Sascha Hauer
` (21 subsequent siblings)
25 siblings, 0 replies; 27+ messages in thread
From: Sascha Hauer @ 2020-11-06 13:38 UTC (permalink / raw)
To: Barebox List
chip->errstat is never set by any driver, so remove it.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
drivers/mtd/nand/nand_base.c | 15 ---------------
include/linux/mtd/nand.h | 5 -----
2 files changed, 20 deletions(-)
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index 999d56c5bd..064b36201e 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -2289,13 +2289,6 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
status = chip->waitfunc(mtd, chip);
- /*
- * See if operation failed and additional status checks are
- * available.
- */
- if ((status & NAND_STATUS_FAIL) && (chip->errstat))
- status = chip->errstat(mtd, chip, FL_WRITING, status,
- page);
if (status & NAND_STATUS_FAIL)
return -EIO;
@@ -2750,14 +2743,6 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
status = chip->waitfunc(mtd, chip);
- /*
- * See if operation failed and additional status checks are
- * available
- */
- if ((status & NAND_STATUS_FAIL) && (chip->errstat))
- status = chip->errstat(mtd, chip, FL_ERASING,
- status, page);
-
/* See if block erase succeeded */
if (status & NAND_STATUS_FAIL) {
pr_debug("%s: failed erase, page 0x%08x\n",
diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
index c4bdddc61e..a8a22210ca 100644
--- a/include/linux/mtd/nand.h
+++ b/include/linux/mtd/nand.h
@@ -466,9 +466,6 @@ struct nand_buffers {
* structure which is shared among multiple independent
* devices.
* @priv: [OPTIONAL] pointer to private chip data
- * @errstat: [OPTIONAL] hardware specific function to perform
- * additional error status checks (determine if errors are
- * correctable).
* @write_page: [REPLACEABLE] High-level page write function
*/
@@ -491,8 +488,6 @@ struct nand_chip {
int page_addr);
int(*waitfunc)(struct mtd_info *mtd, struct nand_chip *this);
int (*scan_bbt)(struct mtd_info *mtd);
- int (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state,
- int status, int page);
int (*write_page)(struct mtd_info *mtd, struct nand_chip *chip,
uint32_t offset, int data_len, const uint8_t *buf,
int oob_required, int page, int cached, int raw);
--
2.20.1
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 27+ messages in thread
* [PATCH 05/26] mtd: nand: Pass struct nand_chip around
2020-11-06 13:38 [PATCH 00/26] Update nand layer to Linux-5.9 Sascha Hauer
` (3 preceding siblings ...)
2020-11-06 13:38 ` [PATCH 04/26] mtd: nand: drop unused errstat hook Sascha Hauer
@ 2020-11-06 13:38 ` Sascha Hauer
2020-11-06 13:38 ` [PATCH 06/26] mtd: Add underscore prefix to mtd hooks Sascha Hauer
` (20 subsequent siblings)
25 siblings, 0 replies; 27+ messages in thread
From: Sascha Hauer @ 2020-11-06 13:38 UTC (permalink / raw)
To: Barebox List
Traditionally Linux passed a struct mtd_info * around as context
between the different functions. This has been changed to a struct
nand_chip *. Do the same for barebox as well as another step towards
updating the NAND layer to current Linux.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
drivers/mtd/nand/atmel_nand.c | 74 ++-
drivers/mtd/nand/nand_base.c | 806 +++++++++++++++---------------
drivers/mtd/nand/nand_bbt.c | 165 +++---
drivers/mtd/nand/nand_bch.c | 10 +-
drivers/mtd/nand/nand_denali.c | 80 +--
drivers/mtd/nand/nand_ecc.c | 4 +-
drivers/mtd/nand/nand_imx.c | 154 +++---
drivers/mtd/nand/nand_mrvl_nfc.c | 75 +--
drivers/mtd/nand/nand_mxs.c | 107 ++--
drivers/mtd/nand/nand_omap_gpmc.c | 88 ++--
drivers/mtd/nand/nand_orion.c | 10 +-
drivers/mtd/nand/nand_s3c24xx.c | 43 +-
drivers/mtd/nand/nomadik_nand.c | 14 +-
include/linux/mtd/nand.h | 97 ++--
include/linux/mtd/nand_bch.h | 14 +-
include/linux/mtd/nand_ecc.h | 6 +-
16 files changed, 845 insertions(+), 902 deletions(-)
diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c
index f3875a5648..e250df82d7 100644
--- a/drivers/mtd/nand/atmel_nand.c
+++ b/drivers/mtd/nand/atmel_nand.c
@@ -127,9 +127,8 @@ static void atmel_nand_disable(struct atmel_nand_host *host)
/*
* Hardware specific access to control-lines
*/
-static void atmel_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
+static void atmel_nand_cmd_ctrl(struct nand_chip *nand_chip, int cmd, unsigned int ctrl)
{
- struct nand_chip *nand_chip = mtd_to_nand(mtd);
struct atmel_nand_host *host = nand_chip->priv;
if (ctrl & NAND_CTRL_CHANGE) {
@@ -150,9 +149,8 @@ static void atmel_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl
/*
* Read the Device Ready pin.
*/
-static int atmel_nand_device_ready(struct mtd_info *mtd)
+static int atmel_nand_device_ready(struct nand_chip *nand_chip)
{
- struct nand_chip *nand_chip = mtd_to_nand(mtd);
struct atmel_nand_host *host = nand_chip->priv;
return gpio_get_value(host->board->rdy_pin);
@@ -161,31 +159,23 @@ static int atmel_nand_device_ready(struct mtd_info *mtd)
/*
* Minimal-overhead PIO for data access.
*/
-static void atmel_read_buf(struct mtd_info *mtd, u8 *buf, int len)
+static void atmel_read_buf(struct nand_chip *nand_chip, u8 *buf, int len)
{
- struct nand_chip *nand_chip = mtd_to_nand(mtd);
-
readsb(nand_chip->IO_ADDR_R, buf, len);
}
-static void atmel_read_buf16(struct mtd_info *mtd, u8 *buf, int len)
+static void atmel_read_buf16(struct nand_chip *nand_chip, u8 *buf, int len)
{
- struct nand_chip *nand_chip = mtd_to_nand(mtd);
-
readsw(nand_chip->IO_ADDR_R, buf, len / 2);
}
-static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
+static void atmel_write_buf(struct nand_chip *nand_chip, const u8 *buf, int len)
{
- struct nand_chip *nand_chip = mtd_to_nand(mtd);
-
writesb(nand_chip->IO_ADDR_W, buf, len);
}
-static void atmel_write_buf16(struct mtd_info *mtd, const u8 *buf, int len)
+static void atmel_write_buf16(struct nand_chip *nand_chip, const u8 *buf, int len)
{
- struct nand_chip *nand_chip = mtd_to_nand(mtd);
-
writesw(nand_chip->IO_ADDR_W, buf, len / 2);
}
@@ -627,9 +617,10 @@ normal_check:
return 0;
}
-static int atmel_nand_pmecc_read_page(struct mtd_info *mtd,
- struct nand_chip *chip, uint8_t *buf, int oob_required, int page)
+static int atmel_nand_pmecc_read_page(struct nand_chip *chip, uint8_t *buf,
+ int oob_required, int page)
{
+ struct mtd_info *mtd = nand_to_mtd(chip);
struct atmel_nand_host *host = chip->priv;
int eccsize = chip->ecc.size;
uint8_t *oob = chip->oob_poi;
@@ -645,8 +636,8 @@ static int atmel_nand_pmecc_read_page(struct mtd_info *mtd,
pmecc_writel(host->ecc, CTRL, PMECC_CTRL_ENABLE);
pmecc_writel(host->ecc, CTRL, PMECC_CTRL_DATA);
- chip->read_buf(mtd, buf, eccsize);
- chip->read_buf(mtd, oob, mtd->oobsize);
+ chip->read_buf(chip, buf, eccsize);
+ chip->read_buf(chip, oob, mtd->oobsize);
ret = wait_on_timeout(PMECC_MAX_TIMEOUT_MS,
!(pmecc_readl_relaxed(host->ecc, SR) & PMECC_SR_BUSY));
@@ -663,10 +654,10 @@ static int atmel_nand_pmecc_read_page(struct mtd_info *mtd,
return 0;
}
-static int atmel_nand_pmecc_write_page(struct mtd_info *mtd,
- struct nand_chip *chip, const uint8_t *buf,
- int oob_required)
+static int atmel_nand_pmecc_write_page(struct nand_chip *chip, const uint8_t *buf,
+ int oob_required)
{
+ struct mtd_info *mtd = nand_to_mtd(chip);
struct atmel_nand_host *host = chip->priv;
uint32_t *eccpos = chip->ecc.layout->eccpos;
int i, j, ret;
@@ -680,7 +671,7 @@ static int atmel_nand_pmecc_write_page(struct mtd_info *mtd,
pmecc_writel(host->ecc, CTRL, PMECC_CTRL_ENABLE);
pmecc_writel(host->ecc, CTRL, PMECC_CTRL_DATA);
- chip->write_buf(mtd, (u8 *)buf, mtd->writesize);
+ chip->write_buf(chip, (u8 *)buf, mtd->writesize);
ret = wait_on_timeout(PMECC_MAX_TIMEOUT_MS,
!(pmecc_readl_relaxed(host->ecc, SR) & PMECC_SR_BUSY));
@@ -698,7 +689,7 @@ static int atmel_nand_pmecc_write_page(struct mtd_info *mtd,
pmecc_readb_ecc_relaxed(host->ecc, i, j);
}
}
- chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
+ chip->write_buf(chip, chip->oob_poi, mtd->oobsize);
return 0;
}
@@ -979,10 +970,9 @@ static int __init atmel_pmecc_nand_init_params(struct device_d *dev,
* dat: raw data (unused)
* ecc_code: buffer for ECC
*/
-static int atmel_nand_calculate(struct mtd_info *mtd,
+static int atmel_nand_calculate(struct nand_chip *nand_chip,
const u_char *dat, unsigned char *ecc_code)
{
- struct nand_chip *nand_chip = mtd_to_nand(mtd);
struct atmel_nand_host *host = nand_chip->priv;
unsigned int ecc_value;
@@ -1008,9 +998,10 @@ static int atmel_nand_calculate(struct mtd_info *mtd,
* chip: nand chip info structure
* buf: buffer to store read data
*/
-static int atmel_nand_read_page(struct mtd_info *mtd,
- struct nand_chip *chip, uint8_t *buf, int oob_required, int page)
+static int atmel_nand_read_page(struct nand_chip *chip, uint8_t *buf,
+ int oob_required, int page)
{
+ struct mtd_info *mtd = nand_to_mtd(chip);
int eccsize = chip->ecc.size;
int eccbytes = chip->ecc.bytes;
uint32_t *eccpos = chip->ecc.layout->eccpos;
@@ -1035,7 +1026,7 @@ static int atmel_nand_read_page(struct mtd_info *mtd,
#endif
/* read the page */
- chip->read_buf(mtd, p, eccsize);
+ chip->read_buf(chip, p, eccsize);
/* move to ECC position if needed */
if (eccpos[0] != 0) {
@@ -1045,16 +1036,16 @@ static int atmel_nand_read_page(struct mtd_info *mtd,
* NAND_CMD_RNDOUT.
* anyway, for small pages, the eccpos[0] == 0
*/
- chip->cmdfunc(mtd, NAND_CMD_RNDOUT,
+ chip->cmdfunc(chip, NAND_CMD_RNDOUT,
mtd->writesize + eccpos[0], -1);
}
/* the ECC controller needs to read the ECC just after the data */
ecc_pos = oob + eccpos[0];
- chip->read_buf(mtd, ecc_pos, eccbytes);
+ chip->read_buf(chip, ecc_pos, eccbytes);
/* check if there's an error */
- stat = chip->ecc.correct(mtd, p, oob, NULL);
+ stat = chip->ecc.correct(chip, p, oob, NULL);
if (stat < 0)
mtd->ecc_stats.failed++;
@@ -1062,10 +1053,10 @@ static int atmel_nand_read_page(struct mtd_info *mtd,
mtd->ecc_stats.corrected += stat;
/* get back to oob start (end of page) */
- chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize, -1);
+ chip->cmdfunc(chip, NAND_CMD_RNDOUT, mtd->writesize, -1);
/* read the oob */
- chip->read_buf(mtd, oob, mtd->oobsize);
+ chip->read_buf(chip, oob, mtd->oobsize);
return 0;
}
@@ -1082,10 +1073,9 @@ static int atmel_nand_read_page(struct mtd_info *mtd,
*
* Detect and correct a 1 bit error for a page
*/
-static int atmel_nand_correct(struct mtd_info *mtd, u_char *dat,
+static int atmel_nand_correct(struct nand_chip *nand_chip, u_char *dat,
u_char *read_ecc, u_char *isnull)
{
- struct nand_chip *nand_chip = mtd_to_nand(mtd);
struct atmel_nand_host *host = nand_chip->priv;
unsigned int ecc_status;
unsigned int ecc_word, ecc_bit;
@@ -1149,7 +1139,7 @@ static int atmel_nand_correct(struct mtd_info *mtd, u_char *dat,
/*
* Enable HW ECC : unused on most chips
*/
-static void atmel_nand_hwctl(struct mtd_info *mtd, int mode)
+static void atmel_nand_hwctl(struct nand_chip *nand_chip, int mode)
{
#if 0
if (cpu_is_at32ap7000()) {
@@ -1442,7 +1432,7 @@ static int __init atmel_nand_probe(struct device_d *dev)
/* first scan to find the device and get the page size */
- if (nand_scan_ident(mtd, 1, NULL)) {
+ if (nand_scan_ident(nand_chip, 1, NULL)) {
res = -ENXIO;
goto err_scan_ident;
}
@@ -1459,17 +1449,17 @@ static int __init atmel_nand_probe(struct device_d *dev)
}
/* second phase scan */
- if (nand_scan_tail(mtd)) {
+ if (nand_scan_tail(nand_chip)) {
res = -ENXIO;
goto err_scan_tail;
}
- add_mtd_nand_device(mtd, "nand");
+ add_mtd_nand_device(nand_chip, "nand");
if (!res)
return res;
- nand_release(mtd);
+ nand_release(nand_chip);
err_scan_tail:
err_hw_ecc:
err_scan_ident:
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index 064b36201e..ef0b15d64e 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -89,15 +89,14 @@ static struct nand_ecclayout nand_oob_128 = {
.length = 78} }
};
-static int nand_get_device(struct mtd_info *mtd, int new_state);
+static int nand_get_device(struct nand_chip *chip, int new_state);
-static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
+static int nand_do_write_oob(struct nand_chip *chip, loff_t to,
struct mtd_oob_ops *ops);
-static int check_offs_len(struct mtd_info *mtd,
+static int check_offs_len(struct nand_chip *chip,
loff_t ofs, uint64_t len)
{
- struct nand_chip *chip = mtd_to_nand(mtd);
int ret = 0;
/* Start address must align on block boundary */
@@ -121,10 +120,8 @@ static int check_offs_len(struct mtd_info *mtd,
*
* Release chip lock and wake up anyone waiting on the device.
*/
-static void nand_release_device(struct mtd_info *mtd)
+static void nand_release_device(struct nand_chip *chip)
{
- struct nand_chip *chip = mtd_to_nand(mtd);
-
/* Release the controller and the chip */
chip->controller->active = NULL;
chip->state = FL_READY;
@@ -136,9 +133,8 @@ static void nand_release_device(struct mtd_info *mtd)
*
* Default read function for 8bit buswidth
*/
-static uint8_t nand_read_byte(struct mtd_info *mtd)
+static uint8_t nand_read_byte(struct nand_chip *chip)
{
- struct nand_chip *chip = mtd_to_nand(mtd);
return readb(chip->IO_ADDR_R);
}
@@ -150,9 +146,8 @@ static uint8_t nand_read_byte(struct mtd_info *mtd)
* Default read function for 16bit buswidth with endianness conversion.
*
*/
-static uint8_t nand_read_byte16(struct mtd_info *mtd)
+static uint8_t nand_read_byte16(struct nand_chip *chip)
{
- struct nand_chip *chip = mtd_to_nand(mtd);
return (uint8_t) cpu_to_le16(readw(chip->IO_ADDR_R));
}
@@ -162,9 +157,8 @@ static uint8_t nand_read_byte16(struct mtd_info *mtd)
*
* Default read function for 16bit buswidth without endianness conversion.
*/
-static u16 nand_read_word(struct mtd_info *mtd)
+static u16 nand_read_word(struct nand_chip *chip)
{
- struct nand_chip *chip = mtd_to_nand(mtd);
return readw(chip->IO_ADDR_R);
}
@@ -175,13 +169,11 @@ static u16 nand_read_word(struct mtd_info *mtd)
*
* Default select function for 1 chip devices.
*/
-static void nand_select_chip(struct mtd_info *mtd, int chipnr)
+static void nand_select_chip(struct nand_chip *chip, int chipnr)
{
- struct nand_chip *chip = mtd_to_nand(mtd);
-
switch (chipnr) {
case -1:
- chip->cmd_ctrl(mtd, NAND_CMD_NONE, 0 | NAND_CTRL_CHANGE);
+ chip->cmd_ctrl(chip, NAND_CMD_NONE, 0 | NAND_CTRL_CHANGE);
break;
case 0:
break;
@@ -199,11 +191,10 @@ static void nand_select_chip(struct mtd_info *mtd, int chipnr)
*
* Default write function for 8bit buswidth.
*/
-static __maybe_unused void nand_write_buf(struct mtd_info *mtd,
+static __maybe_unused void nand_write_buf(struct nand_chip *chip,
const uint8_t *buf, int len)
{
int i;
- struct nand_chip *chip = mtd_to_nand(mtd);
for (i = 0; i < len; i++)
writeb(buf[i], chip->IO_ADDR_W);
@@ -217,10 +208,9 @@ static __maybe_unused void nand_write_buf(struct mtd_info *mtd,
*
* Default read function for 8bit buswidth.
*/
-static void nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+static void nand_read_buf(struct nand_chip *chip, uint8_t *buf, int len)
{
int i;
- struct nand_chip *chip = mtd_to_nand(mtd);
for (i = 0; i < len; i++)
buf[i] = readb(chip->IO_ADDR_R);
@@ -234,11 +224,10 @@ static void nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
*
* Default write function for 16bit buswidth.
*/
-static __maybe_unused void nand_write_buf16(struct mtd_info *mtd,
+static __maybe_unused void nand_write_buf16(struct nand_chip *chip,
const uint8_t *buf, int len)
{
int i;
- struct nand_chip *chip = mtd_to_nand(mtd);
u16 *p = (u16 *) buf;
len >>= 1;
@@ -255,10 +244,9 @@ static __maybe_unused void nand_write_buf16(struct mtd_info *mtd,
*
* Default read function for 16bit buswidth.
*/
-static void nand_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len)
+static void nand_read_buf16(struct nand_chip *chip, uint8_t *buf, int len)
{
int i;
- struct nand_chip *chip = mtd_to_nand(mtd);
u16 *p = (u16 *) buf;
len >>= 1;
@@ -274,53 +262,52 @@ static void nand_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len)
*
* Check, if the block is bad.
*/
-static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
+static int nand_block_bad(struct nand_chip *chip, loff_t ofs, int getchip)
{
int page, chipnr, res = 0, i = 0;
- struct nand_chip *chip = mtd_to_nand(mtd);
u16 bad;
if (chip->bbt_options & NAND_BBT_SCANLASTPAGE)
- ofs += mtd->erasesize - mtd->writesize;
+ ofs += chip->mtd.erasesize - chip->mtd.writesize;
page = (int)(ofs >> chip->page_shift) & chip->pagemask;
if (getchip) {
chipnr = (int)(ofs >> chip->chip_shift);
- nand_get_device(mtd, FL_READING);
+ nand_get_device(chip, FL_READING);
/* Select the NAND device */
- chip->select_chip(mtd, chipnr);
+ chip->select_chip(chip, chipnr);
}
do {
if (chip->options & NAND_BUSWIDTH_16) {
- chip->cmdfunc(mtd, NAND_CMD_READOOB,
+ chip->cmdfunc(chip, NAND_CMD_READOOB,
chip->badblockpos & 0xFE, page);
- bad = cpu_to_le16(chip->read_word(mtd));
+ bad = cpu_to_le16(chip->read_word(chip));
if (chip->badblockpos & 0x1)
bad >>= 8;
else
bad &= 0xFF;
} else {
- chip->cmdfunc(mtd, NAND_CMD_READOOB, chip->badblockpos,
+ chip->cmdfunc(chip, NAND_CMD_READOOB, chip->badblockpos,
page);
- bad = chip->read_byte(mtd);
+ bad = chip->read_byte(chip);
}
if (likely(chip->badblockbits == 8))
res = bad != 0xFF;
else
res = hweight8(bad) < chip->badblockbits;
- ofs += mtd->writesize;
+ ofs += chip->mtd.writesize;
page = (int)(ofs >> chip->page_shift) & chip->pagemask;
i++;
} while (!res && i < 2 && (chip->bbt_options & NAND_BBT_SCAN2NDPAGE));
if (getchip) {
- chip->select_chip(mtd, -1);
- nand_release_device(mtd);
+ chip->select_chip(chip, -1);
+ nand_release_device(chip);
}
return res;
@@ -335,9 +322,8 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
* specific driver. It provides the details for writing a bad block marker to a
* block.
*/
-static __maybe_unused int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
+static __maybe_unused int nand_default_block_markbad(struct nand_chip *chip, loff_t ofs)
{
- struct nand_chip *chip = mtd_to_nand(mtd);
struct mtd_oob_ops ops;
uint8_t buf[2] = { 0, 0 };
int ret = 0, res, i = 0;
@@ -355,14 +341,14 @@ static __maybe_unused int nand_default_block_markbad(struct mtd_info *mtd, loff_
/* Write to first/last page(s) if necessary */
if (chip->bbt_options & NAND_BBT_SCANLASTPAGE)
- ofs += mtd->erasesize - mtd->writesize;
+ ofs += chip->mtd.erasesize - chip->mtd.writesize;
do {
- res = nand_do_write_oob(mtd, ofs, &ops);
+ res = nand_do_write_oob(chip, ofs, &ops);
if (!ret)
ret = res;
i++;
- ofs += mtd->writesize;
+ ofs += chip->mtd.writesize;
} while ((chip->bbt_options & NAND_BBT_SCAN2NDPAGE) && i < 2);
return ret;
@@ -385,9 +371,8 @@ static __maybe_unused int nand_default_block_markbad(struct mtd_info *mtd, loff_
* Note that we retain the first error encountered in (2) or (3), finish the
* procedures, and dump the error in the end.
*/
-static int nand_block_markbad_lowlevel(struct mtd_info *mtd, loff_t ofs)
+static int nand_block_markbad_lowlevel(struct nand_chip *chip, loff_t ofs)
{
- struct nand_chip *chip = mtd_to_nand(mtd);
int res, ret = 0;
if (!(chip->bbt_options & NAND_BBT_NO_OOB_BBM)) {
@@ -395,26 +380,26 @@ static int nand_block_markbad_lowlevel(struct mtd_info *mtd, loff_t ofs)
/* Attempt erase before marking OOB */
memset(&einfo, 0, sizeof(einfo));
- einfo.mtd = mtd;
+ einfo.mtd = &chip->mtd;
einfo.addr = ofs;
einfo.len = 1 << chip->phys_erase_shift;
- nand_erase_nand(mtd, &einfo, 0);
+ nand_erase_nand(chip, &einfo, 0);
/* Write bad block marker to OOB */
- nand_get_device(mtd, FL_WRITING);
- ret = chip->block_markbad(mtd, ofs);
- nand_release_device(mtd);
+ nand_get_device(chip, FL_WRITING);
+ ret = chip->block_markbad(chip, ofs);
+ nand_release_device(chip);
}
/* Mark block bad in BBT */
if (IS_ENABLED(CONFIG_NAND_BBT) && chip->bbt) {
- res = nand_markbad_bbt(mtd, ofs);
+ res = nand_markbad_bbt(chip, ofs);
if (!ret)
ret = res;
}
if (!ret)
- mtd->ecc_stats.badblocks++;
+ chip->mtd.ecc_stats.badblocks++;
return ret;
}
@@ -429,9 +414,8 @@ static int nand_block_markbad_lowlevel(struct mtd_info *mtd, loff_t ofs)
* (2) check bad block marker
* (3) update the BBT
*/
-static int nand_block_markgood_lowlevel(struct mtd_info *mtd, loff_t ofs)
+static int nand_block_markgood_lowlevel(struct nand_chip *chip, loff_t ofs)
{
- struct nand_chip *chip = mtd_to_nand(mtd);
bool allow_erasebad;
int ret;
@@ -439,33 +423,33 @@ static int nand_block_markgood_lowlevel(struct mtd_info *mtd, loff_t ofs)
struct erase_info einfo;
/* Attempt erase possibly bad block */
- allow_erasebad = mtd->allow_erasebad;
- mtd->allow_erasebad = true;
+ allow_erasebad = chip->mtd.allow_erasebad;
+ chip->mtd.allow_erasebad = true;
memset(&einfo, 0, sizeof(einfo));
- einfo.mtd = mtd;
+ einfo.mtd = &chip->mtd;
einfo.addr = ofs;
einfo.len = 1 << chip->phys_erase_shift;
- nand_erase_nand(mtd, &einfo, 0);
- mtd->allow_erasebad = allow_erasebad;
+ nand_erase_nand(chip, &einfo, 0);
+ chip->mtd.allow_erasebad = allow_erasebad;
/*
* Verify erase succeded. We need to select chip again,
* as nand_erase_nand deselected it.
*/
- ret = chip->block_bad(mtd, ofs, 1);
+ ret = chip->block_bad(chip, ofs, 1);
if (ret)
return ret;
}
/* Mark block good in BBT */
if (IS_ENABLED(CONFIG_NAND_BBT) && chip->bbt) {
- ret = nand_markgood_bbt(mtd, ofs);
+ ret = nand_markgood_bbt(chip, ofs);
if (ret)
return ret;
}
- if (mtd->ecc_stats.badblocks > 0)
- mtd->ecc_stats.badblocks--;
+ if (chip->mtd.ecc_stats.badblocks > 0)
+ chip->mtd.ecc_stats.badblocks--;
return 0;
}
@@ -477,17 +461,15 @@ static int nand_block_markgood_lowlevel(struct mtd_info *mtd, loff_t ofs)
* Check, if the device is write protected. The function expects, that the
* device is already selected.
*/
-static int nand_check_wp(struct mtd_info *mtd)
+static int nand_check_wp(struct nand_chip *chip)
{
- struct nand_chip *chip = mtd_to_nand(mtd);
-
/* Broken xD cards report WP despite being writable */
if (chip->options & NAND_BROKEN_XD)
return 0;
/* Check the WP bit */
- chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1);
- return (chip->read_byte(mtd) & NAND_STATUS_WP) ? 0 : 1;
+ chip->cmdfunc(chip, NAND_CMD_STATUS, -1, -1);
+ return (chip->read_byte(chip) & NAND_STATUS_WP) ? 0 : 1;
}
/**
@@ -500,28 +482,25 @@ static int nand_check_wp(struct mtd_info *mtd)
* Check, if the block is bad. Either by reading the bad block table or
* calling of the scan function.
*/
-static int nand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int getchip,
+static int nand_block_checkbad(struct nand_chip *chip, loff_t ofs, int getchip,
int allowbbt)
{
- struct nand_chip *chip = mtd_to_nand(mtd);
-
if (IS_ENABLED(CONFIG_NAND_BBT) && chip->bbt) {
/* Return info from the table */
- return nand_isbad_bbt(mtd, ofs, allowbbt);
+ return nand_isbad_bbt(chip, ofs, allowbbt);
}
- return chip->block_bad(mtd, ofs, getchip);
+ return chip->block_bad(chip, ofs, getchip);
}
/* Wait for the ready pin, after a command. The timeout is caught later. */
-void nand_wait_ready(struct mtd_info *mtd)
+void nand_wait_ready(struct nand_chip *chip)
{
- struct nand_chip *chip = mtd_to_nand(mtd);
uint64_t start = get_time_ns();
/* wait until command is processed or timeout occures */
do {
- if (chip->dev_ready(mtd))
+ if (chip->dev_ready(chip))
break;
} while (!is_timeout(start, SECOND * 2));
}
@@ -536,19 +515,18 @@ void nand_wait_ready(struct mtd_info *mtd)
* Send command to NAND device. This function is used for small page devices
* (512 Bytes per page).
*/
-static void nand_command(struct mtd_info *mtd, unsigned int command,
+static void nand_command(struct nand_chip *chip, unsigned int command,
int column, int page_addr)
{
- register struct nand_chip *chip = mtd_to_nand(mtd);
int ctrl = NAND_CTRL_CLE | NAND_CTRL_CHANGE;
/* Write out the command to the device */
if (IS_ENABLED(CONFIG_MTD_WRITE) && command == NAND_CMD_SEQIN) {
int readcmd;
- if (column >= mtd->writesize) {
+ if (column >= chip->mtd.writesize) {
/* OOB area */
- column -= mtd->writesize;
+ column -= chip->mtd.writesize;
readcmd = NAND_CMD_READOOB;
} else if (column < 256) {
/* First 256 bytes --> READ0 */
@@ -557,10 +535,10 @@ static void nand_command(struct mtd_info *mtd, unsigned int command,
column -= 256;
readcmd = NAND_CMD_READ1;
}
- chip->cmd_ctrl(mtd, readcmd, ctrl);
+ chip->cmd_ctrl(chip, readcmd, ctrl);
ctrl &= ~NAND_CTRL_CHANGE;
}
- chip->cmd_ctrl(mtd, command, ctrl);
+ chip->cmd_ctrl(chip, command, ctrl);
/* Address cycle, when necessary */
ctrl = NAND_CTRL_ALE | NAND_CTRL_CHANGE;
@@ -569,18 +547,18 @@ static void nand_command(struct mtd_info *mtd, unsigned int command,
/* Adjust columns for 16 bit buswidth */
if (chip->options & NAND_BUSWIDTH_16)
column >>= 1;
- chip->cmd_ctrl(mtd, column, ctrl);
+ chip->cmd_ctrl(chip, column, ctrl);
ctrl &= ~NAND_CTRL_CHANGE;
}
if (page_addr != -1) {
- chip->cmd_ctrl(mtd, page_addr, ctrl);
+ chip->cmd_ctrl(chip, page_addr, ctrl);
ctrl &= ~NAND_CTRL_CHANGE;
- chip->cmd_ctrl(mtd, page_addr >> 8, ctrl);
+ chip->cmd_ctrl(chip, page_addr >> 8, ctrl);
/* One more address cycle for devices > 32MiB */
if (chip->chipsize > (32 << 20))
- chip->cmd_ctrl(mtd, page_addr >> 16, ctrl);
+ chip->cmd_ctrl(chip, page_addr >> 16, ctrl);
}
- chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
+ chip->cmd_ctrl(chip, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
/*
* Program and erase have their own busy handlers status and sequential
@@ -599,11 +577,11 @@ static void nand_command(struct mtd_info *mtd, unsigned int command,
if (chip->dev_ready)
break;
udelay(chip->chip_delay);
- chip->cmd_ctrl(mtd, NAND_CMD_STATUS,
+ chip->cmd_ctrl(chip, NAND_CMD_STATUS,
NAND_CTRL_CLE | NAND_CTRL_CHANGE);
- chip->cmd_ctrl(mtd,
+ chip->cmd_ctrl(chip,
NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
- while (!(chip->read_byte(mtd) & NAND_STATUS_READY))
+ while (!(chip->read_byte(chip) & NAND_STATUS_READY))
;
return;
@@ -624,7 +602,7 @@ static void nand_command(struct mtd_info *mtd, unsigned int command,
*/
ndelay(100);
- nand_wait_ready(mtd);
+ nand_wait_ready(chip);
}
/**
@@ -638,19 +616,17 @@ static void nand_command(struct mtd_info *mtd, unsigned int command,
* devices. We don't have the separate regions as we have in the small page
* devices. We must emulate NAND_CMD_READOOB to keep the code compatible.
*/
-static void nand_command_lp(struct mtd_info *mtd, unsigned int command,
+static void nand_command_lp(struct nand_chip *chip, unsigned int command,
int column, int page_addr)
{
- register struct nand_chip *chip = mtd_to_nand(mtd);
-
/* Emulate NAND_CMD_READOOB */
if (command == NAND_CMD_READOOB) {
- column += mtd->writesize;
+ column += chip->mtd.writesize;
command = NAND_CMD_READ0;
}
/* Command latch cycle */
- chip->cmd_ctrl(mtd, command, NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
+ chip->cmd_ctrl(chip, command, NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
if (column != -1 || page_addr != -1) {
int ctrl = NAND_CTRL_CHANGE | NAND_NCE | NAND_ALE;
@@ -660,21 +636,21 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command,
/* Adjust columns for 16 bit buswidth */
if (chip->options & NAND_BUSWIDTH_16)
column >>= 1;
- chip->cmd_ctrl(mtd, column, ctrl);
+ chip->cmd_ctrl(chip, column, ctrl);
ctrl &= ~NAND_CTRL_CHANGE;
- chip->cmd_ctrl(mtd, column >> 8, ctrl);
+ chip->cmd_ctrl(chip, column >> 8, ctrl);
}
if (page_addr != -1) {
- chip->cmd_ctrl(mtd, page_addr, ctrl);
- chip->cmd_ctrl(mtd, page_addr >> 8,
+ chip->cmd_ctrl(chip, page_addr, ctrl);
+ chip->cmd_ctrl(chip, page_addr >> 8,
NAND_NCE | NAND_ALE);
/* One more address cycle for devices > 128MiB */
if (chip->chipsize > (128 << 20))
- chip->cmd_ctrl(mtd, page_addr >> 16,
+ chip->cmd_ctrl(chip, page_addr >> 16,
NAND_NCE | NAND_ALE);
}
}
- chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
+ chip->cmd_ctrl(chip, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
/*
* Program and erase have their own busy handlers status, sequential
@@ -695,26 +671,26 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command,
if (chip->dev_ready)
break;
udelay(chip->chip_delay);
- chip->cmd_ctrl(mtd, NAND_CMD_STATUS,
+ chip->cmd_ctrl(chip, NAND_CMD_STATUS,
NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
- chip->cmd_ctrl(mtd, NAND_CMD_NONE,
+ chip->cmd_ctrl(chip, NAND_CMD_NONE,
NAND_NCE | NAND_CTRL_CHANGE);
- while (!(chip->read_byte(mtd) & NAND_STATUS_READY))
+ while (!(chip->read_byte(chip) & NAND_STATUS_READY))
;
return;
case NAND_CMD_RNDOUT:
/* No ready / busy check necessary */
- chip->cmd_ctrl(mtd, NAND_CMD_RNDOUTSTART,
+ chip->cmd_ctrl(chip, NAND_CMD_RNDOUTSTART,
NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
- chip->cmd_ctrl(mtd, NAND_CMD_NONE,
+ chip->cmd_ctrl(chip, NAND_CMD_NONE,
NAND_NCE | NAND_CTRL_CHANGE);
return;
case NAND_CMD_READ0:
- chip->cmd_ctrl(mtd, NAND_CMD_READSTART,
+ chip->cmd_ctrl(chip, NAND_CMD_READSTART,
NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
- chip->cmd_ctrl(mtd, NAND_CMD_NONE,
+ chip->cmd_ctrl(chip, NAND_CMD_NONE,
NAND_NCE | NAND_CTRL_CHANGE);
/* This applies to read commands */
@@ -735,7 +711,7 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command,
*/
ndelay(100);
- nand_wait_ready(mtd);
+ nand_wait_ready(chip);
}
/**
@@ -746,9 +722,8 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command,
* Get the device and lock it for exclusive access
*/
static int
-nand_get_device(struct mtd_info *mtd, int new_state)
+nand_get_device(struct nand_chip *chip, int new_state)
{
- struct nand_chip *chip = mtd_to_nand(mtd);
retry:
/* Hardware controller shared among independent devices */
@@ -777,7 +752,7 @@ retry:
* Erase can take up to 400ms and program up to 20ms according to
* general NAND and SmartMedia specs
*/
-static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip)
+static int nand_wait(struct nand_chip *chip)
{
uint64_t start = get_time_ns();
@@ -793,19 +768,19 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip)
* any case on any machine. */
ndelay(100);
- chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1);
+ chip->cmdfunc(chip, NAND_CMD_STATUS, -1, -1);
while (!is_timeout(start, timeo)) {
if (chip->dev_ready) {
- if (chip->dev_ready(mtd))
+ if (chip->dev_ready(chip))
break;
} else {
- if (chip->read_byte(mtd) & NAND_STATUS_READY)
+ if (chip->read_byte(chip) & NAND_STATUS_READY)
break;
}
}
- status = (int)chip->read_byte(mtd);
+ status = (int)chip->read_byte(chip);
return status;
}
@@ -821,24 +796,23 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip)
*
* Returs unlock status.
*/
-static int __nand_unlock(struct mtd_info *mtd, loff_t ofs,
+static int __nand_unlock(struct nand_chip *chip, loff_t ofs,
uint64_t len, int invert)
{
int ret = 0;
int status, page;
- struct nand_chip *chip = mtd_to_nand(mtd);
/* Submit address of first page to unlock */
page = ofs >> chip->page_shift;
- chip->cmdfunc(mtd, NAND_CMD_UNLOCK1, -1, page & chip->pagemask);
+ chip->cmdfunc(chip, NAND_CMD_UNLOCK1, -1, page & chip->pagemask);
/* Submit address of last page to unlock */
page = (ofs + len) >> chip->page_shift;
- chip->cmdfunc(mtd, NAND_CMD_UNLOCK2, -1,
+ chip->cmdfunc(chip, NAND_CMD_UNLOCK2, -1,
(page | invert) & chip->pagemask);
/* Call wait ready function */
- status = chip->waitfunc(mtd, chip);
+ status = chip->waitfunc(chip);
/* See if device thinks it succeeded */
if (status & NAND_STATUS_FAIL) {
pr_debug("%s: error status = 0x%08x\n",
@@ -857,28 +831,28 @@ static int __nand_unlock(struct mtd_info *mtd, loff_t ofs,
*
* Returns unlock status.
*/
-int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
+int nand_unlock(struct nand_chip *chip, loff_t ofs, uint64_t len)
{
+ struct mtd_info *mtd = nand_to_mtd(chip);
int ret = 0;
int chipnr;
- struct nand_chip *chip = mtd_to_nand(mtd);
pr_debug("%s: start = 0x%012llx, len = %llu\n",
__func__, (unsigned long long)ofs, len);
- if (check_offs_len(mtd, ofs, len))
+ if (check_offs_len(chip, ofs, len))
ret = -EINVAL;
/* Align to last block address if size addresses end of the device */
if (ofs + len == mtd->size)
len -= mtd->erasesize;
- nand_get_device(mtd, FL_UNLOCKING);
+ nand_get_device(chip, FL_UNLOCKING);
/* Shift to get chip number */
chipnr = ofs >> chip->chip_shift;
- chip->select_chip(mtd, chipnr);
+ chip->select_chip(chip, chipnr);
/*
* Reset the chip.
@@ -887,21 +861,21 @@ int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
* some operation can also clear the bit 7 of status register
* eg. erase/program a locked block
*/
- chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
+ chip->cmdfunc(chip, NAND_CMD_RESET, -1, -1);
/* Check, if it is write protected */
- if (nand_check_wp(mtd)) {
+ if (nand_check_wp(chip)) {
pr_debug("%s: device is write protected!\n",
__func__);
ret = -EIO;
goto out;
}
- ret = __nand_unlock(mtd, ofs, len, 0);
+ ret = __nand_unlock(chip, ofs, len, 0);
out:
- chip->select_chip(mtd, -1);
- nand_release_device(mtd);
+ chip->select_chip(chip, -1);
+ nand_release_device(chip);
return ret;
}
@@ -920,24 +894,23 @@ EXPORT_SYMBOL(nand_unlock);
*
* Returns lock status.
*/
-int nand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
+int nand_lock(struct nand_chip *chip, loff_t ofs, uint64_t len)
{
int ret = 0;
int chipnr, status, page;
- struct nand_chip *chip = mtd_to_nand(mtd);
pr_debug("%s: start = 0x%012llx, len = %llu\n",
__func__, (unsigned long long)ofs, len);
- if (check_offs_len(mtd, ofs, len))
+ if (check_offs_len(chip, ofs, len))
ret = -EINVAL;
- nand_get_device(mtd, FL_LOCKING);
+ nand_get_device(chip, FL_LOCKING);
/* Shift to get chip number */
chipnr = ofs >> chip->chip_shift;
- chip->select_chip(mtd, chipnr);
+ chip->select_chip(chip, chipnr);
/*
* Reset the chip.
@@ -946,10 +919,10 @@ int nand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
* some operation can also clear the bit 7 of status register
* eg. erase/program a locked block
*/
- chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
+ chip->cmdfunc(chip, NAND_CMD_RESET, -1, -1);
/* Check, if it is write protected */
- if (nand_check_wp(mtd)) {
+ if (nand_check_wp(chip)) {
pr_debug("%s: device is write protected!\n",
__func__);
ret = -EIO;
@@ -958,10 +931,10 @@ int nand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
/* Submit address of first page to lock */
page = ofs >> chip->page_shift;
- chip->cmdfunc(mtd, NAND_CMD_LOCK, -1, page & chip->pagemask);
+ chip->cmdfunc(chip, NAND_CMD_LOCK, -1, page & chip->pagemask);
/* Call wait ready function */
- status = chip->waitfunc(mtd, chip);
+ status = chip->waitfunc(chip);
/* See if device thinks it succeeded */
if (status & NAND_STATUS_FAIL) {
pr_debug("%s: error status = 0x%08x\n",
@@ -970,11 +943,11 @@ int nand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
goto out;
}
- ret = __nand_unlock(mtd, ofs, len, 0x1);
+ ret = __nand_unlock(chip, ofs, len, 0x1);
out:
- chip->select_chip(mtd, -1);
- nand_release_device(mtd);
+ chip->select_chip(chip, -1);
+ nand_release_device(chip);
return ret;
}
@@ -1118,12 +1091,13 @@ EXPORT_SYMBOL(nand_check_erased_ecc_chunk);
*
* Not for syndrome calculating ECC controllers, which use a special oob layout.
*/
-static __maybe_unused int nand_read_page_raw(struct mtd_info *mtd,
- struct nand_chip *chip, uint8_t *buf, int oob_required, int page)
+static __maybe_unused int nand_read_page_raw(struct nand_chip *chip,
+ uint8_t *buf, int oob_required,
+ int page)
{
- chip->read_buf(mtd, buf, mtd->writesize);
+ chip->read_buf(chip, buf, chip->mtd.writesize);
if (oob_required)
- chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
+ chip->read_buf(chip, chip->oob_poi, chip->mtd.oobsize);
return 0;
}
@@ -1137,9 +1111,8 @@ static __maybe_unused int nand_read_page_raw(struct mtd_info *mtd,
*
* We need a special oob layout and handling even when OOB isn't used.
*/
-static __maybe_unused int nand_read_page_raw_syndrome(struct mtd_info *mtd,
- struct nand_chip *chip, uint8_t *buf,
- int oob_required, int page)
+static __maybe_unused int nand_read_page_raw_syndrome(struct nand_chip *chip,
+ uint8_t *buf, int oob_required, int page)
{
int eccsize = chip->ecc.size;
int eccbytes = chip->ecc.bytes;
@@ -1147,26 +1120,26 @@ static __maybe_unused int nand_read_page_raw_syndrome(struct mtd_info *mtd,
int steps, size;
for (steps = chip->ecc.steps; steps > 0; steps--) {
- chip->read_buf(mtd, buf, eccsize);
+ chip->read_buf(chip, buf, eccsize);
buf += eccsize;
if (chip->ecc.prepad) {
- chip->read_buf(mtd, oob, chip->ecc.prepad);
+ chip->read_buf(chip, oob, chip->ecc.prepad);
oob += chip->ecc.prepad;
}
- chip->read_buf(mtd, oob, eccbytes);
+ chip->read_buf(chip, oob, eccbytes);
oob += eccbytes;
if (chip->ecc.postpad) {
- chip->read_buf(mtd, oob, chip->ecc.postpad);
+ chip->read_buf(chip, oob, chip->ecc.postpad);
oob += chip->ecc.postpad;
}
}
- size = mtd->oobsize - (oob - chip->oob_poi);
+ size = chip->mtd.oobsize - (oob - chip->oob_poi);
if (size)
- chip->read_buf(mtd, oob, size);
+ chip->read_buf(chip, oob, size);
return 0;
}
@@ -1179,10 +1152,10 @@ static __maybe_unused int nand_read_page_raw_syndrome(struct mtd_info *mtd,
* @oob_required: caller requires OOB data read to chip->oob_poi
* @page: page number to read
*/
-static __maybe_unused int nand_read_page_swecc(struct mtd_info *mtd,
- struct nand_chip *chip, uint8_t *buf, int oob_required,
- int page)
+static __maybe_unused int nand_read_page_swecc(struct nand_chip *chip,
+ uint8_t *buf, int oob_required, int page)
{
+ struct mtd_info *mtd = nand_to_mtd(chip);
int i, eccsize = chip->ecc.size;
int eccbytes = chip->ecc.bytes;
int eccsteps = chip->ecc.steps;
@@ -1192,10 +1165,10 @@ static __maybe_unused int nand_read_page_swecc(struct mtd_info *mtd,
uint32_t *eccpos = chip->ecc.layout->eccpos;
unsigned int max_bitflips = 0;
- chip->ecc.read_page_raw(mtd, chip, buf, 1, page);
+ chip->ecc.read_page_raw(chip, buf, 1, page);
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize)
- chip->ecc.calculate(mtd, p, &ecc_calc[i]);
+ chip->ecc.calculate(chip, p, &ecc_calc[i]);
for (i = 0; i < chip->ecc.total; i++)
ecc_code[i] = chip->oob_poi[eccpos[i]];
@@ -1206,7 +1179,7 @@ static __maybe_unused int nand_read_page_swecc(struct mtd_info *mtd,
for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
int stat;
- stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]);
+ stat = chip->ecc.correct(chip, p, &ecc_code[i], &ecc_calc[i]);
if (stat < 0) {
mtd->ecc_stats.failed++;
} else {
@@ -1225,10 +1198,11 @@ static __maybe_unused int nand_read_page_swecc(struct mtd_info *mtd,
* @readlen: data length
* @bufpoi: buffer to store read data
*/
-static __maybe_unused int nand_read_subpage(struct mtd_info *mtd,
- struct nand_chip *chip, uint32_t data_offs, uint32_t readlen,
+static __maybe_unused int nand_read_subpage(struct nand_chip *chip,
+ uint32_t data_offs, uint32_t readlen,
uint8_t *bufpoi, int page)
{
+ struct mtd_info *mtd = nand_to_mtd(chip);
int start_step, end_step, num_steps;
uint32_t *eccpos = chip->ecc.layout->eccpos;
uint8_t *p;
@@ -1255,14 +1229,14 @@ static __maybe_unused int nand_read_subpage(struct mtd_info *mtd,
data_col_addr = start_step * chip->ecc.size;
/* If we read not a page aligned data */
if (data_col_addr != 0)
- chip->cmdfunc(mtd, NAND_CMD_RNDOUT, data_col_addr, -1);
+ chip->cmdfunc(chip, NAND_CMD_RNDOUT, data_col_addr, -1);
p = bufpoi + data_col_addr;
- chip->read_buf(mtd, p, datafrag_len);
+ chip->read_buf(chip, p, datafrag_len);
/* Calculate ECC */
for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size)
- chip->ecc.calculate(mtd, p, &chip->buffers->ecccalc[i]);
+ chip->ecc.calculate(chip, p, &chip->buffers->ecccalc[i]);
/*
* The performance is faster if we position offsets according to
@@ -1276,8 +1250,8 @@ static __maybe_unused int nand_read_subpage(struct mtd_info *mtd,
}
}
if (gaps) {
- chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize, -1);
- chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
+ chip->cmdfunc(chip, NAND_CMD_RNDOUT, mtd->writesize, -1);
+ chip->read_buf(chip, chip->oob_poi, mtd->oobsize);
} else {
/*
* Send the command to read the particular ECC bytes take care
@@ -1292,9 +1266,9 @@ static __maybe_unused int nand_read_subpage(struct mtd_info *mtd,
if (eccpos[index + (num_steps * chip->ecc.bytes)] & (busw - 1))
aligned_len++;
- chip->cmdfunc(mtd, NAND_CMD_RNDOUT,
+ chip->cmdfunc(chip, NAND_CMD_RNDOUT,
mtd->writesize + aligned_pos, -1);
- chip->read_buf(mtd, &chip->oob_poi[aligned_pos], aligned_len);
+ chip->read_buf(chip, &chip->oob_poi[aligned_pos], aligned_len);
}
for (i = 0; i < eccfrag_len; i++)
@@ -1304,7 +1278,7 @@ static __maybe_unused int nand_read_subpage(struct mtd_info *mtd,
for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size) {
int stat;
- stat = chip->ecc.correct(mtd, p,
+ stat = chip->ecc.correct(chip, p,
&chip->buffers->ecccode[i], &chip->buffers->ecccalc[i]);
if (stat < 0) {
mtd->ecc_stats.failed++;
@@ -1326,9 +1300,10 @@ static __maybe_unused int nand_read_subpage(struct mtd_info *mtd,
*
* Not for syndrome calculating ECC controllers which need a special oob layout.
*/
-static __maybe_unused int nand_read_page_hwecc(struct mtd_info *mtd,
- struct nand_chip *chip, uint8_t *buf, int oob_required, int page)
+static __maybe_unused int nand_read_page_hwecc(struct nand_chip *chip,
+ uint8_t *buf, int oob_required, int page)
{
+ struct mtd_info *mtd = nand_to_mtd(chip);
int i, eccsize = chip->ecc.size;
int eccbytes = chip->ecc.bytes;
int eccsteps = chip->ecc.steps;
@@ -1339,11 +1314,11 @@ static __maybe_unused int nand_read_page_hwecc(struct mtd_info *mtd,
unsigned int max_bitflips = 0;
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
- chip->ecc.hwctl(mtd, NAND_ECC_READ);
- chip->read_buf(mtd, p, eccsize);
- chip->ecc.calculate(mtd, p, &ecc_calc[i]);
+ chip->ecc.hwctl(chip, NAND_ECC_READ);
+ chip->read_buf(chip, p, eccsize);
+ chip->ecc.calculate(chip, p, &ecc_calc[i]);
}
- chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
+ chip->read_buf(chip, chip->oob_poi, mtd->oobsize);
for (i = 0; i < chip->ecc.total; i++)
ecc_code[i] = chip->oob_poi[eccpos[i]];
@@ -1354,7 +1329,7 @@ static __maybe_unused int nand_read_page_hwecc(struct mtd_info *mtd,
for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
int stat;
- stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]);
+ stat = chip->ecc.correct(chip, p, &ecc_code[i], &ecc_calc[i]);
if (stat < 0) {
mtd->ecc_stats.failed++;
} else {
@@ -1379,9 +1354,10 @@ static __maybe_unused int nand_read_page_hwecc(struct mtd_info *mtd,
* multiple ECC steps, follows the "infix ECC" scheme and reads/writes ECC from
* the data area, by overwriting the NAND manufacturer bad block markings.
*/
-static __maybe_unused int nand_read_page_hwecc_oob_first(struct mtd_info *mtd,
- struct nand_chip *chip, uint8_t *buf, int oob_required, int page)
+static __maybe_unused int nand_read_page_hwecc_oob_first(struct nand_chip *chip,
+ uint8_t *buf, int oob_required, int page)
{
+ struct mtd_info *mtd = nand_to_mtd(chip);
int i, eccsize = chip->ecc.size;
int eccbytes = chip->ecc.bytes;
int eccsteps = chip->ecc.steps;
@@ -1392,9 +1368,9 @@ static __maybe_unused int nand_read_page_hwecc_oob_first(struct mtd_info *mtd,
unsigned int max_bitflips = 0;
/* Read the OOB area first */
- chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
- chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
- chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page);
+ chip->cmdfunc(chip, NAND_CMD_READOOB, 0, page);
+ chip->read_buf(chip, chip->oob_poi, mtd->oobsize);
+ chip->cmdfunc(chip, NAND_CMD_READ0, 0, page);
for (i = 0; i < chip->ecc.total; i++)
ecc_code[i] = chip->oob_poi[eccpos[i]];
@@ -1402,11 +1378,11 @@ static __maybe_unused int nand_read_page_hwecc_oob_first(struct mtd_info *mtd,
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
int stat;
- chip->ecc.hwctl(mtd, NAND_ECC_READ);
- chip->read_buf(mtd, p, eccsize);
- chip->ecc.calculate(mtd, p, &ecc_calc[i]);
+ chip->ecc.hwctl(chip, NAND_ECC_READ);
+ chip->read_buf(chip, p, eccsize);
+ chip->ecc.calculate(chip, p, &ecc_calc[i]);
- stat = chip->ecc.correct(mtd, p, &ecc_code[i], NULL);
+ stat = chip->ecc.correct(chip, p, &ecc_code[i], NULL);
if (stat < 0) {
mtd->ecc_stats.failed++;
} else {
@@ -1428,9 +1404,10 @@ static __maybe_unused int nand_read_page_hwecc_oob_first(struct mtd_info *mtd,
* The hw generator calculates the error syndrome automatically. Therefore we
* need a special oob layout and handling.
*/
-static __maybe_unused int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
+static __maybe_unused int nand_read_page_syndrome(struct nand_chip *chip,
uint8_t *buf, int oob_required, int page)
{
+ struct mtd_info *mtd = nand_to_mtd(chip);
int i, eccsize = chip->ecc.size;
int eccbytes = chip->ecc.bytes;
int eccsteps = chip->ecc.steps;
@@ -1441,17 +1418,17 @@ static __maybe_unused int nand_read_page_syndrome(struct mtd_info *mtd, struct
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
int stat;
- chip->ecc.hwctl(mtd, NAND_ECC_READ);
- chip->read_buf(mtd, p, eccsize);
+ chip->ecc.hwctl(chip, NAND_ECC_READ);
+ chip->read_buf(chip, p, eccsize);
if (chip->ecc.prepad) {
- chip->read_buf(mtd, oob, chip->ecc.prepad);
+ chip->read_buf(chip, oob, chip->ecc.prepad);
oob += chip->ecc.prepad;
}
- chip->ecc.hwctl(mtd, NAND_ECC_READSYN);
- chip->read_buf(mtd, oob, eccbytes);
- stat = chip->ecc.correct(mtd, p, oob, NULL);
+ chip->ecc.hwctl(chip, NAND_ECC_READSYN);
+ chip->read_buf(chip, oob, eccbytes);
+ stat = chip->ecc.correct(chip, p, oob, NULL);
if (stat < 0) {
mtd->ecc_stats.failed++;
@@ -1463,7 +1440,7 @@ static __maybe_unused int nand_read_page_syndrome(struct mtd_info *mtd, struct
oob += eccbytes;
if (chip->ecc.postpad) {
- chip->read_buf(mtd, oob, chip->ecc.postpad);
+ chip->read_buf(chip, oob, chip->ecc.postpad);
oob += chip->ecc.postpad;
}
}
@@ -1471,7 +1448,7 @@ static __maybe_unused int nand_read_page_syndrome(struct mtd_info *mtd, struct
/* Calculate remaining oob bytes */
i = mtd->oobsize - (oob - chip->oob_poi);
if (i)
- chip->read_buf(mtd, oob, i);
+ chip->read_buf(chip, oob, i);
return max_bitflips;
}
@@ -1532,17 +1509,17 @@ static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob,
*
* Internal function. Called with chip held.
*/
-static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
+static int nand_do_read_ops(struct nand_chip *chip, loff_t from,
struct mtd_oob_ops *ops)
{
int chipnr, page, realpage, col, bytes, aligned, oob_required;
- struct nand_chip *chip = mtd_to_nand(mtd);
+ struct mtd_info *mtd = nand_to_mtd(chip);
struct mtd_ecc_stats stats;
int ret = 0;
uint32_t readlen = ops->len;
uint32_t oobreadlen = ops->ooblen;
uint32_t max_oobsize = ops->mode == MTD_OPS_AUTO_OOB ?
- mtd->oobavail : mtd->oobsize;
+ chip->mtd.oobavail : chip->mtd.oobsize;
uint8_t *bufpoi, *oob, *buf;
unsigned int max_bitflips = 0;
@@ -1550,7 +1527,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
stats = mtd->ecc_stats;
chipnr = (int)(from >> chip->chip_shift);
- chip->select_chip(mtd, chipnr);
+ chip->select_chip(chip, chipnr);
realpage = (int)(from >> chip->page_shift);
page = realpage & chip->pagemask;
@@ -1569,22 +1546,22 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
if (realpage != chip->pagebuf || oob) {
bufpoi = aligned ? buf : chip->buffers->databuf;
- chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page);
+ chip->cmdfunc(chip, NAND_CMD_READ0, 0x00, page);
/*
* Now read the page into the buffer. Absent an error,
* the read methods return max bitflips per ecc step.
*/
if (unlikely(ops->mode == MTD_OPS_RAW))
- ret = chip->ecc.read_page_raw(mtd, chip, bufpoi,
+ ret = chip->ecc.read_page_raw(chip, bufpoi,
oob_required,
page);
else if (!aligned && NAND_HAS_SUBPAGE_READ(chip) &&
!oob)
- ret = chip->ecc.read_subpage(mtd, chip,
+ ret = chip->ecc.read_subpage(chip,
col, bytes, bufpoi, page);
else
- ret = chip->ecc.read_page(mtd, chip, bufpoi,
+ ret = chip->ecc.read_page(chip, bufpoi,
oob_required, page);
if (ret < 0) {
if (!aligned)
@@ -1626,7 +1603,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
if (!chip->dev_ready)
udelay(chip->chip_delay);
else
- nand_wait_ready(mtd);
+ nand_wait_ready(chip);
}
} else {
memcpy(buf, chip->buffers->databuf + col, bytes);
@@ -1649,11 +1626,11 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
/* Check, if we cross a chip boundary */
if (!page) {
chipnr++;
- chip->select_chip(mtd, -1);
- chip->select_chip(mtd, chipnr);
+ chip->select_chip(chip, -1);
+ chip->select_chip(chip, chipnr);
}
}
- chip->select_chip(mtd, -1);
+ chip->select_chip(chip, -1);
ops->retlen = ops->len - (size_t) readlen;
if (oob)
@@ -1681,18 +1658,19 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
static int nand_read(struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, uint8_t *buf)
{
+ struct nand_chip *chip = mtd_to_nand(mtd);
struct mtd_oob_ops ops;
int ret;
- nand_get_device(mtd, FL_READING);
+ nand_get_device(chip, FL_READING);
ops.len = len;
ops.datbuf = buf;
ops.ooblen = 0;
ops.oobbuf = NULL;
ops.mode = MTD_OPS_PLACE_OOB;
- ret = nand_do_read_ops(mtd, from, &ops);
+ ret = nand_do_read_ops(chip, from, &ops);
*retlen = ops.retlen;
- nand_release_device(mtd);
+ nand_release_device(chip);
return ret;
}
@@ -1702,14 +1680,13 @@ static int nand_read(struct mtd_info *mtd, loff_t from, size_t len,
* @chip: nand chip info structure
* @page: page number to read
*/
-static __maybe_unused int nand_read_oob_std(struct mtd_info *mtd,
- struct nand_chip *chip, int page)
+static __maybe_unused int nand_read_oob_std(struct nand_chip *chip, int page)
{
if (!IS_ENABLED(CONFIG_NAND_READ_OOB))
return -ENOTSUPP;
- chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
- chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
+ chip->cmdfunc(chip, NAND_CMD_READOOB, 0, page);
+ chip->read_buf(chip, chip->oob_poi, chip->mtd.oobsize);
return 0;
}
@@ -1720,11 +1697,11 @@ static __maybe_unused int nand_read_oob_std(struct mtd_info *mtd,
* @chip: nand chip info structure
* @page: page number to read
*/
-static __maybe_unused int nand_read_oob_syndrome(struct mtd_info *mtd,
- struct nand_chip *chip, int page)
+static __maybe_unused int nand_read_oob_syndrome(struct nand_chip *chip,
+ int page)
{
uint8_t *buf = chip->oob_poi;
- int length = mtd->oobsize;
+ int length = chip->mtd.oobsize;
int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad;
int eccsize = chip->ecc.size;
uint8_t *bufpoi = buf;
@@ -1733,23 +1710,23 @@ static __maybe_unused int nand_read_oob_syndrome(struct mtd_info *mtd,
if (!IS_ENABLED(CONFIG_NAND_READ_OOB))
return -ENOTSUPP;
- chip->cmdfunc(mtd, NAND_CMD_READ0, chip->ecc.size, page);
+ chip->cmdfunc(chip, NAND_CMD_READ0, chip->ecc.size, page);
for (i = 0; i < chip->ecc.steps; i++) {
if (sndrnd) {
pos = eccsize + i * (eccsize + chunk);
- if (mtd->writesize > 512)
- chip->cmdfunc(mtd, NAND_CMD_RNDOUT, pos, -1);
+ if (chip->mtd.writesize > 512)
+ chip->cmdfunc(chip, NAND_CMD_RNDOUT, pos, -1);
else
- chip->cmdfunc(mtd, NAND_CMD_READ0, pos, page);
+ chip->cmdfunc(chip, NAND_CMD_READ0, pos, page);
} else
sndrnd = 1;
toread = min_t(int, length, chunk);
- chip->read_buf(mtd, bufpoi, toread);
+ chip->read_buf(chip, bufpoi, toread);
bufpoi += toread;
length -= toread;
}
if (length > 0)
- chip->read_buf(mtd, bufpoi, length);
+ chip->read_buf(chip, bufpoi, length);
return 0;
}
@@ -1760,22 +1737,21 @@ static __maybe_unused int nand_read_oob_syndrome(struct mtd_info *mtd,
* @chip: nand chip info structure
* @page: page number to write
*/
-static __maybe_unused int nand_write_oob_std(struct mtd_info *mtd,
- struct nand_chip *chip, int page)
+static __maybe_unused int nand_write_oob_std(struct nand_chip *chip, int page)
{
int status = 0;
const uint8_t *buf = chip->oob_poi;
- int length = mtd->oobsize;
+ int length = chip->mtd.oobsize;
if (!IS_ENABLED(CONFIG_NAND_READ_OOB) || !IS_ENABLED(CONFIG_MTD_WRITE))
return -ENOTSUPP;
- chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page);
- chip->write_buf(mtd, buf, length);
+ chip->cmdfunc(chip, NAND_CMD_SEQIN, chip->mtd.writesize, page);
+ chip->write_buf(chip, buf, length);
/* Send command to program the OOB data */
- chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
+ chip->cmdfunc(chip, NAND_CMD_PAGEPROG, -1, -1);
- status = chip->waitfunc(mtd, chip);
+ status = chip->waitfunc(chip);
return status & NAND_STATUS_FAIL ? -EIO : 0;
}
@@ -1787,11 +1763,10 @@ static __maybe_unused int nand_write_oob_std(struct mtd_info *mtd,
* @chip: nand chip info structure
* @page: page number to write
*/
-static __maybe_unused int nand_write_oob_syndrome(struct mtd_info *mtd,
- struct nand_chip *chip, int page)
+static __maybe_unused int nand_write_oob_syndrome(struct nand_chip *chip, int page)
{
int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad;
- int eccsize = chip->ecc.size, length = mtd->oobsize;
+ int eccsize = chip->ecc.size, length = chip->mtd.oobsize;
int i, len, pos, status = 0, sndcmd = 0, steps = chip->ecc.steps;
const uint8_t *bufpoi = chip->oob_poi;
@@ -1809,35 +1784,35 @@ static __maybe_unused int nand_write_oob_syndrome(struct mtd_info *mtd,
} else
pos = eccsize;
- chip->cmdfunc(mtd, NAND_CMD_SEQIN, pos, page);
+ chip->cmdfunc(chip, NAND_CMD_SEQIN, pos, page);
for (i = 0; i < steps; i++) {
if (sndcmd) {
- if (mtd->writesize <= 512) {
+ if (chip->mtd.writesize <= 512) {
uint32_t fill = 0xFFFFFFFF;
len = eccsize;
while (len > 0) {
int num = min_t(int, len, 4);
- chip->write_buf(mtd, (uint8_t *)&fill,
+ chip->write_buf(chip, (uint8_t *)&fill,
num);
len -= num;
}
} else {
pos = eccsize + i * (eccsize + chunk);
- chip->cmdfunc(mtd, NAND_CMD_RNDIN, pos, -1);
+ chip->cmdfunc(chip, NAND_CMD_RNDIN, pos, -1);
}
} else
sndcmd = 1;
len = min_t(int, length, chunk);
- chip->write_buf(mtd, bufpoi, len);
+ chip->write_buf(chip, bufpoi, len);
bufpoi += len;
length -= len;
}
if (length > 0)
- chip->write_buf(mtd, bufpoi, length);
+ chip->write_buf(chip, bufpoi, length);
- chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
- status = chip->waitfunc(mtd, chip);
+ chip->cmdfunc(chip, NAND_CMD_PAGEPROG, -1, -1);
+ status = chip->waitfunc(chip);
return status & NAND_STATUS_FAIL ? -EIO : 0;
}
@@ -1850,11 +1825,11 @@ static __maybe_unused int nand_write_oob_syndrome(struct mtd_info *mtd,
*
* NAND read out-of-band data from the spare area.
*/
-static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
+static int nand_do_read_oob(struct nand_chip *chip, loff_t from,
struct mtd_oob_ops *ops)
{
+ struct mtd_info *mtd = nand_to_mtd(chip);
int page, realpage, chipnr;
- struct nand_chip *chip = mtd_to_nand(mtd);
struct mtd_ecc_stats stats;
int readlen = ops->ooblen;
int len;
@@ -1890,7 +1865,7 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
}
chipnr = (int)(from >> chip->chip_shift);
- chip->select_chip(mtd, chipnr);
+ chip->select_chip(chip, chipnr);
/* Shift to get page */
realpage = (int)(from >> chip->page_shift);
@@ -1898,9 +1873,9 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
while (1) {
if (ops->mode == MTD_OPS_RAW)
- ret = chip->ecc.read_oob_raw(mtd, chip, page);
+ ret = chip->ecc.read_oob_raw(chip, page);
else
- ret = chip->ecc.read_oob(mtd, chip, page);
+ ret = chip->ecc.read_oob(chip, page);
if (ret < 0)
break;
@@ -1913,7 +1888,7 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
if (!chip->dev_ready)
udelay(chip->chip_delay);
else
- nand_wait_ready(mtd);
+ nand_wait_ready(chip);
}
readlen -= len;
@@ -1927,11 +1902,11 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
/* Check, if we cross a chip boundary */
if (!page) {
chipnr++;
- chip->select_chip(mtd, -1);
- chip->select_chip(mtd, chipnr);
+ chip->select_chip(chip, -1);
+ chip->select_chip(chip, chipnr);
}
}
- chip->select_chip(mtd, -1);
+ chip->select_chip(chip, -1);
ops->oobretlen = ops->ooblen - readlen;
@@ -1955,6 +1930,7 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
static int nand_read_oob(struct mtd_info *mtd, loff_t from,
struct mtd_oob_ops *ops)
{
+ struct nand_chip *chip = mtd_to_nand(mtd);
int ret = -ENOTSUPP;
if (!IS_ENABLED(CONFIG_NAND_READ_OOB))
@@ -1963,13 +1939,13 @@ static int nand_read_oob(struct mtd_info *mtd, loff_t from,
ops->retlen = 0;
/* Do not allow reads past end of device */
- if (ops->datbuf && (from + ops->len) > mtd->size) {
+ if (ops->datbuf && (from + ops->len) > chip->mtd.size) {
pr_debug("%s: attempt to read beyond end of device\n",
__func__);
return -EINVAL;
}
- nand_get_device(mtd, FL_READING);
+ nand_get_device(chip, FL_READING);
switch (ops->mode) {
case MTD_OPS_PLACE_OOB:
@@ -1982,12 +1958,12 @@ static int nand_read_oob(struct mtd_info *mtd, loff_t from,
}
if (!ops->datbuf)
- ret = nand_do_read_oob(mtd, from, ops);
+ ret = nand_do_read_oob(chip, from, ops);
else
- ret = nand_do_read_ops(mtd, from, ops);
+ ret = nand_do_read_ops(chip, from, ops);
out:
- nand_release_device(mtd);
+ nand_release_device(chip);
return ret;
}
@@ -2001,15 +1977,15 @@ out:
*
* Not for syndrome calculating ECC controllers, which use a special oob layout.
*/
-static __maybe_unused int nand_write_page_raw(struct mtd_info *mtd,
- struct nand_chip *chip, const uint8_t *buf, int oob_required)
+static __maybe_unused int nand_write_page_raw(struct nand_chip *chip,
+ const uint8_t *buf, int oob_required)
{
if (!IS_ENABLED(CONFIG_MTD_WRITE))
return -ENOTSUPP;
- chip->write_buf(mtd, buf, mtd->writesize);
+ chip->write_buf(chip, buf, chip->mtd.writesize);
if (oob_required)
- chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
+ chip->write_buf(chip, chip->oob_poi, chip->mtd.oobsize);
return 0;
}
@@ -2023,8 +1999,7 @@ static __maybe_unused int nand_write_page_raw(struct mtd_info *mtd,
*
* We need a special oob layout and handling even when ECC isn't checked.
*/
-static __maybe_unused int nand_write_page_raw_syndrome(struct mtd_info *mtd,
- struct nand_chip *chip,
+static __maybe_unused int nand_write_page_raw_syndrome(struct nand_chip *chip,
const uint8_t *buf, int oob_required)
{
int eccsize = chip->ecc.size;
@@ -2036,26 +2011,26 @@ static __maybe_unused int nand_write_page_raw_syndrome(struct mtd_info *mtd,
return -ENOTSUPP;
for (steps = chip->ecc.steps; steps > 0; steps--) {
- chip->write_buf(mtd, buf, eccsize);
+ chip->write_buf(chip, buf, eccsize);
buf += eccsize;
if (chip->ecc.prepad) {
- chip->write_buf(mtd, oob, chip->ecc.prepad);
+ chip->write_buf(chip, oob, chip->ecc.prepad);
oob += chip->ecc.prepad;
}
- chip->read_buf(mtd, oob, eccbytes);
+ chip->read_buf(chip, oob, eccbytes);
oob += eccbytes;
if (chip->ecc.postpad) {
- chip->write_buf(mtd, oob, chip->ecc.postpad);
+ chip->write_buf(chip, oob, chip->ecc.postpad);
oob += chip->ecc.postpad;
}
}
- size = mtd->oobsize - (oob - chip->oob_poi);
+ size = chip->mtd.oobsize - (oob - chip->oob_poi);
if (size)
- chip->write_buf(mtd, oob, size);
+ chip->write_buf(chip, oob, size);
return 0;
}
@@ -2066,8 +2041,8 @@ static __maybe_unused int nand_write_page_raw_syndrome(struct mtd_info *mtd,
* @buf: data buffer
* @oob_required: must write chip->oob_poi to OOB
*/
-static __maybe_unused int nand_write_page_swecc(struct mtd_info *mtd,
- struct nand_chip *chip, const uint8_t *buf, int oob_required)
+static __maybe_unused int nand_write_page_swecc(struct nand_chip *chip,
+ const uint8_t *buf, int oob_required)
{
int i, eccsize = chip->ecc.size;
int eccbytes = chip->ecc.bytes;
@@ -2081,12 +2056,12 @@ static __maybe_unused int nand_write_page_swecc(struct mtd_info *mtd,
/* Software ECC calculation */
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize)
- chip->ecc.calculate(mtd, p, &ecc_calc[i]);
+ chip->ecc.calculate(chip, p, &ecc_calc[i]);
for (i = 0; i < chip->ecc.total; i++)
chip->oob_poi[eccpos[i]] = ecc_calc[i];
- return chip->ecc.write_page_raw(mtd, chip, buf, 1);
+ return chip->ecc.write_page_raw(chip, buf, 1);
}
/**
@@ -2096,8 +2071,8 @@ static __maybe_unused int nand_write_page_swecc(struct mtd_info *mtd,
* @buf: data buffer
* @oob_required: must write chip->oob_poi to OOB
*/
-static __maybe_unused int nand_write_page_hwecc(struct mtd_info *mtd,
- struct nand_chip *chip, const uint8_t *buf, int oob_required)
+static __maybe_unused int nand_write_page_hwecc(struct nand_chip *chip,
+ const uint8_t *buf, int oob_required)
{
int i, eccsize = chip->ecc.size;
int eccbytes = chip->ecc.bytes;
@@ -2110,15 +2085,15 @@ static __maybe_unused int nand_write_page_hwecc(struct mtd_info *mtd,
return -ENOTSUPP;
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
- chip->ecc.hwctl(mtd, NAND_ECC_WRITE);
- chip->write_buf(mtd, p, eccsize);
- chip->ecc.calculate(mtd, p, &ecc_calc[i]);
+ chip->ecc.hwctl(chip, NAND_ECC_WRITE);
+ chip->write_buf(chip, p, eccsize);
+ chip->ecc.calculate(chip, p, &ecc_calc[i]);
}
for (i = 0; i < chip->ecc.total; i++)
chip->oob_poi[eccpos[i]] = ecc_calc[i];
- chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
+ chip->write_buf(chip, chip->oob_poi, chip->mtd.oobsize);
return 0;
}
@@ -2132,8 +2107,8 @@ static __maybe_unused int nand_write_page_hwecc(struct mtd_info *mtd,
* @data_len: data length
* @oob_required: must write chip->oob_poi to OOB
*/
-static __maybe_unused int nand_write_subpage_hwecc(struct mtd_info *mtd,
- struct nand_chip *chip, uint32_t offset,
+static __maybe_unused int nand_write_subpage_hwecc(struct nand_chip *chip,
+ uint32_t offset,
uint32_t data_len, const uint8_t *data_buf,
int oob_required)
{
@@ -2145,7 +2120,7 @@ static __maybe_unused int nand_write_subpage_hwecc(struct mtd_info *mtd,
uint32_t *eccpos = chip->ecc.layout->eccpos;
uint32_t start_step = offset / ecc_size;
uint32_t end_step = (offset + data_len - 1) / ecc_size;
- int oob_bytes = mtd->oobsize / ecc_steps;
+ int oob_bytes = chip->mtd.oobsize / ecc_steps;
int step, i;
if (!IS_ENABLED(CONFIG_MTD_WRITE))
@@ -2153,16 +2128,16 @@ static __maybe_unused int nand_write_subpage_hwecc(struct mtd_info *mtd,
for (step = 0; step < ecc_steps; step++) {
/* configure controller for WRITE access */
- chip->ecc.hwctl(mtd, NAND_ECC_WRITE);
+ chip->ecc.hwctl(chip, NAND_ECC_WRITE);
/* write data (untouched subpages already masked by 0xFF) */
- chip->write_buf(mtd, data_buf, ecc_size);
+ chip->write_buf(chip, data_buf, ecc_size);
/* mask ECC of un-touched subpages by padding 0xFF */
if ((step < start_step) || (step > end_step))
memset(ecc_calc, 0xff, ecc_bytes);
else
- chip->ecc.calculate(mtd, data_buf, ecc_calc);
+ chip->ecc.calculate(chip, data_buf, ecc_calc);
/* mask OOB of un-touched subpages by padding 0xFF */
/* if oob_required, preserve OOB metadata of written subpage */
@@ -2181,7 +2156,7 @@ static __maybe_unused int nand_write_subpage_hwecc(struct mtd_info *mtd,
chip->oob_poi[eccpos[i]] = ecc_calc[i];
/* write OOB buffer to NAND device */
- chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
+ chip->write_buf(chip, chip->oob_poi, chip->mtd.oobsize);
return 0;
}
@@ -2197,8 +2172,7 @@ static __maybe_unused int nand_write_subpage_hwecc(struct mtd_info *mtd,
* The hw generator calculates the error syndrome automatically. Therefore we
* need a special oob layout and handling.
*/
-static __maybe_unused int nand_write_page_syndrome(struct mtd_info *mtd,
- struct nand_chip *chip,
+static __maybe_unused int nand_write_page_syndrome(struct nand_chip *chip,
const uint8_t *buf, int oob_required)
{
int i, eccsize = chip->ecc.size;
@@ -2212,28 +2186,28 @@ static __maybe_unused int nand_write_page_syndrome(struct mtd_info *mtd,
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
- chip->ecc.hwctl(mtd, NAND_ECC_WRITE);
- chip->write_buf(mtd, p, eccsize);
+ chip->ecc.hwctl(chip, NAND_ECC_WRITE);
+ chip->write_buf(chip, p, eccsize);
if (chip->ecc.prepad) {
- chip->write_buf(mtd, oob, chip->ecc.prepad);
+ chip->write_buf(chip, oob, chip->ecc.prepad);
oob += chip->ecc.prepad;
}
- chip->ecc.calculate(mtd, p, oob);
- chip->write_buf(mtd, oob, eccbytes);
+ chip->ecc.calculate(chip, p, oob);
+ chip->write_buf(chip, oob, eccbytes);
oob += eccbytes;
if (chip->ecc.postpad) {
- chip->write_buf(mtd, oob, chip->ecc.postpad);
+ chip->write_buf(chip, oob, chip->ecc.postpad);
oob += chip->ecc.postpad;
}
}
/* Calculate remaining oob bytes */
- i = mtd->oobsize - (oob - chip->oob_poi);
+ i = chip->mtd.oobsize - (oob - chip->oob_poi);
if (i)
- chip->write_buf(mtd, oob, i);
+ chip->write_buf(chip, oob, i);
return 0;
}
@@ -2250,7 +2224,7 @@ static __maybe_unused int nand_write_page_syndrome(struct mtd_info *mtd,
* @cached: cached programming
* @raw: use _raw version of write_page
*/
-static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
+static int nand_write_page(struct nand_chip *chip,
uint32_t offset, int data_len, const uint8_t *buf,
int oob_required, int page, int cached, int raw)
{
@@ -2261,20 +2235,20 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
if (!(chip->options & NAND_NO_SUBPAGE_WRITE) &&
chip->ecc.write_subpage)
- subpage = offset || (data_len < mtd->writesize);
+ subpage = offset || (data_len < chip->mtd.writesize);
else
subpage = 0;
- chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page);
+ chip->cmdfunc(chip, NAND_CMD_SEQIN, 0x00, page);
if (unlikely(raw))
- status = chip->ecc.write_page_raw(mtd, chip, buf,
+ status = chip->ecc.write_page_raw(chip, buf,
oob_required);
else if (subpage)
- status = chip->ecc.write_subpage(mtd, chip, offset, data_len,
+ status = chip->ecc.write_subpage(chip, offset, data_len,
buf, oob_required);
else
- status = chip->ecc.write_page(mtd, chip, buf, oob_required);
+ status = chip->ecc.write_page(chip, buf, oob_required);
if (status < 0)
return status;
@@ -2287,14 +2261,14 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
if (!cached || !NAND_HAS_CACHEPROG(chip)) {
- chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
- status = chip->waitfunc(mtd, chip);
+ chip->cmdfunc(chip, NAND_CMD_PAGEPROG, -1, -1);
+ status = chip->waitfunc(chip);
if (status & NAND_STATUS_FAIL)
return -EIO;
} else {
- chip->cmdfunc(mtd, NAND_CMD_CACHEDPROG, -1, -1);
- status = chip->waitfunc(mtd, chip);
+ chip->cmdfunc(chip, NAND_CMD_CACHEDPROG, -1, -1);
+ status = chip->waitfunc(chip);
}
return 0;
@@ -2307,16 +2281,14 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
* @len: oob data write length
* @ops: oob ops structure
*/
-static uint8_t *nand_fill_oob(struct mtd_info *mtd, uint8_t *oob, size_t len,
+static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob, size_t len,
struct mtd_oob_ops *ops)
{
- struct nand_chip *chip = mtd_to_nand(mtd);
-
/*
* Initialise to all 0xFF, to avoid the possibility of left over OOB
* data from a previous OOB read.
*/
- memset(chip->oob_poi, 0xff, mtd->oobsize);
+ memset(chip->oob_poi, 0xff, chip->mtd.oobsize);
switch (ops->mode) {
@@ -2366,11 +2338,11 @@ static uint8_t *nand_fill_oob(struct mtd_info *mtd, uint8_t *oob, size_t len,
*
* NAND write with ECC.
*/
-static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
+static int nand_do_write_ops(struct nand_chip *chip, loff_t to,
struct mtd_oob_ops *ops)
{
+ struct mtd_info *mtd = nand_to_mtd(chip);
int chipnr, realpage, page, blockmask, column;
- struct nand_chip *chip = mtd_to_nand(mtd);
uint32_t writelen = ops->len;
uint32_t oobwritelen = ops->ooblen;
@@ -2392,10 +2364,10 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
column = to & (mtd->writesize - 1);
chipnr = (int)(to >> chip->chip_shift);
- chip->select_chip(mtd, chipnr);
+ chip->select_chip(chip, chipnr);
/* Check, if it is write protected */
- if (nand_check_wp(mtd)) {
+ if (nand_check_wp(chip)) {
ret = -EIO;
goto err_out;
}
@@ -2432,7 +2404,7 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
if (unlikely(oob)) {
size_t len = min(oobwritelen, oobmaxlen);
- oob = nand_fill_oob(mtd, oob, len, ops);
+ oob = nand_fill_oob(chip, oob, len, ops);
oobwritelen -= len;
} else {
/* We still need to erase leftover OOB data */
@@ -2440,7 +2412,7 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
}
if (oob || !mtd_buf_all_ff(wbuf, mtd->writesize)) {
- ret = chip->write_page(mtd, chip, column, bytes, wbuf,
+ ret = chip->write_page(chip, column, bytes, wbuf,
oob_required, page, cached,
(ops->mode == MTD_OPS_RAW));
if (ret)
@@ -2459,8 +2431,8 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
/* Check, if we cross a chip boundary */
if (!page) {
chipnr++;
- chip->select_chip(mtd, -1);
- chip->select_chip(mtd, chipnr);
+ chip->select_chip(chip, -1);
+ chip->select_chip(chip, chipnr);
}
}
@@ -2469,7 +2441,7 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
ops->oobretlen = ops->ooblen;
err_out:
- chip->select_chip(mtd, -1);
+ chip->select_chip(chip, -1);
return ret;
}
@@ -2486,20 +2458,21 @@ err_out:
static int nand_write(struct mtd_info *mtd, loff_t to, size_t len,
size_t *retlen, const uint8_t *buf)
{
+ struct nand_chip *chip = mtd_to_nand(mtd);
struct mtd_oob_ops ops;
int ret;
if (!IS_ENABLED(CONFIG_MTD_WRITE))
return -ENOTSUPP;
- nand_get_device(mtd, FL_WRITING);
+ nand_get_device(chip, FL_WRITING);
ops.len = len;
ops.datbuf = (uint8_t *)buf;
ops.oobbuf = NULL;
ops.mode = MTD_OPS_PLACE_OOB;
- ret = nand_do_write_ops(mtd, to, &ops);
+ ret = nand_do_write_ops(chip, to, &ops);
*retlen = ops.retlen;
- nand_release_device(mtd);
+ nand_release_device(chip);
return ret;
}
@@ -2511,11 +2484,11 @@ static int nand_write(struct mtd_info *mtd, loff_t to, size_t len,
*
* NAND write out-of-band.
*/
-static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
+static int nand_do_write_oob(struct nand_chip *chip, loff_t to,
struct mtd_oob_ops *ops)
{
+ struct mtd_info *mtd = nand_to_mtd(chip);
int chipnr, page, status, len;
- struct nand_chip *chip = mtd_to_nand(mtd);
if (!IS_ENABLED(CONFIG_MTD_WRITE))
return -ENOTSUPP;
@@ -2552,7 +2525,7 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
}
chipnr = (int)(to >> chip->chip_shift);
- chip->select_chip(mtd, chipnr);
+ chip->select_chip(chip, chipnr);
/* Shift to get page */
page = (int)(to >> chip->page_shift);
@@ -2563,11 +2536,11 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
* if we don't do this. I have no clue why, but I seem to have 'fixed'
* it in the doc2000 driver in August 1999. dwmw2.
*/
- chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
+ chip->cmdfunc(chip, NAND_CMD_RESET, -1, -1);
/* Check, if it is write protected */
- if (nand_check_wp(mtd)) {
- chip->select_chip(mtd, -1);
+ if (nand_check_wp(chip)) {
+ chip->select_chip(chip, -1);
return -EROFS;
}
@@ -2575,14 +2548,14 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
if (page == chip->pagebuf)
chip->pagebuf = -1;
- nand_fill_oob(mtd, ops->oobbuf, ops->ooblen, ops);
+ nand_fill_oob(chip, ops->oobbuf, ops->ooblen, ops);
if (ops->mode == MTD_OPS_RAW)
- status = chip->ecc.write_oob_raw(mtd, chip, page & chip->pagemask);
+ status = chip->ecc.write_oob_raw(chip, page & chip->pagemask);
else
- status = chip->ecc.write_oob(mtd, chip, page & chip->pagemask);
+ status = chip->ecc.write_oob(chip, page & chip->pagemask);
- chip->select_chip(mtd, -1);
+ chip->select_chip(chip, -1);
if (status)
return status;
@@ -2601,6 +2574,7 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
static int nand_write_oob(struct mtd_info *mtd, loff_t to,
struct mtd_oob_ops *ops)
{
+ struct nand_chip *chip = mtd_to_nand(mtd);
int ret = -ENOTSUPP;
if (!IS_ENABLED(CONFIG_MTD_WRITE))
@@ -2615,7 +2589,7 @@ static int nand_write_oob(struct mtd_info *mtd, loff_t to,
return -EINVAL;
}
- nand_get_device(mtd, FL_WRITING);
+ nand_get_device(chip, FL_WRITING);
switch (ops->mode) {
case MTD_OPS_PLACE_OOB:
@@ -2628,12 +2602,12 @@ static int nand_write_oob(struct mtd_info *mtd, loff_t to,
}
if (!ops->datbuf)
- ret = nand_do_write_oob(mtd, to, ops);
+ ret = nand_do_write_oob(chip, to, ops);
else
- ret = nand_do_write_ops(mtd, to, ops);
+ ret = nand_do_write_ops(chip, to, ops);
out:
- nand_release_device(mtd);
+ nand_release_device(chip);
return ret;
}
@@ -2644,16 +2618,14 @@ out:
*
* Standard erase command for NAND chips.
*/
-static void single_erase_cmd(struct mtd_info *mtd, int page)
+static void single_erase_cmd(struct nand_chip *chip, int page)
{
- struct nand_chip *chip = mtd_to_nand(mtd);
-
if (!IS_ENABLED(CONFIG_MTD_WRITE))
return;
/* Send commands to erase a block */
- chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page);
- chip->cmdfunc(mtd, NAND_CMD_ERASE2, -1, -1);
+ chip->cmdfunc(chip, NAND_CMD_ERASE1, -1, page);
+ chip->cmdfunc(chip, NAND_CMD_ERASE2, -1, -1);
}
/**
@@ -2665,10 +2637,12 @@ static void single_erase_cmd(struct mtd_info *mtd, int page)
*/
static int nand_erase(struct mtd_info *mtd, struct erase_info *instr)
{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+
if (!IS_ENABLED(CONFIG_MTD_WRITE))
return -ENOTSUPP;
- return nand_erase_nand(mtd, instr, 0);
+ return nand_erase_nand(chip, instr, 0);
}
/**
@@ -2679,11 +2653,10 @@ static int nand_erase(struct mtd_info *mtd, struct erase_info *instr)
*
* Erase one ore more blocks.
*/
-int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
+int nand_erase_nand(struct nand_chip *chip, struct erase_info *instr,
int allowbbt)
{
int page, status, pages_per_block, ret, chipnr;
- struct nand_chip *chip = mtd_to_nand(mtd);
loff_t len;
if (!IS_ENABLED(CONFIG_MTD_WRITE))
@@ -2693,11 +2666,11 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
__func__, (unsigned long long)instr->addr,
(unsigned long long)instr->len);
- if (check_offs_len(mtd, instr->addr, instr->len))
+ if (check_offs_len(chip, instr->addr, instr->len))
return -EINVAL;
/* Grab the lock and see if the device is available */
- nand_get_device(mtd, FL_ERASING);
+ nand_get_device(chip, FL_ERASING);
/* Shift to get first page */
page = (int)(instr->addr >> chip->page_shift);
@@ -2707,10 +2680,10 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
pages_per_block = 1 << (chip->phys_erase_shift - chip->page_shift);
/* Select the NAND device */
- chip->select_chip(mtd, chipnr);
+ chip->select_chip(chip, chipnr);
/* Check, if it is write protected */
- if (nand_check_wp(mtd)) {
+ if (nand_check_wp(chip)) {
pr_debug("%s: device is write protected!\n",
__func__);
ret = -EIO;
@@ -2722,8 +2695,8 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
while (len) {
/* Check if we have a bad block, we do not erase bad blocks! */
- if (!mtd->allow_erasebad &&
- nand_block_checkbad(mtd, ((loff_t) page) <<
+ if (!chip->mtd.allow_erasebad &&
+ nand_block_checkbad(chip, ((loff_t) page) <<
chip->page_shift, 0, allowbbt)) {
pr_warn("%s: attempt to erase a bad block at page 0x%08x\n",
__func__, page);
@@ -2739,9 +2712,9 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
(page + pages_per_block))
chip->pagebuf = -1;
- single_erase_cmd(mtd, page & chip->pagemask);
+ single_erase_cmd(chip, page & chip->pagemask);
- status = chip->waitfunc(mtd, chip);
+ status = chip->waitfunc(chip);
/* See if block erase succeeded */
if (status & NAND_STATUS_FAIL) {
@@ -2760,8 +2733,8 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
/* Check, if we cross a chip boundary */
if (len && !(page & chip->pagemask)) {
chipnr++;
- chip->select_chip(mtd, -1);
- chip->select_chip(mtd, chipnr);
+ chip->select_chip(chip, -1);
+ chip->select_chip(chip, chipnr);
}
}
@@ -2769,8 +2742,8 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
erase_exit:
/* Deselect and wake up anyone waiting on the device */
- chip->select_chip(mtd, -1);
- nand_release_device(mtd);
+ chip->select_chip(chip, -1);
+ nand_release_device(chip);
/* Return more or less happy */
return ret;
@@ -2784,12 +2757,14 @@ erase_exit:
*/
static void nand_sync(struct mtd_info *mtd)
{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+
pr_debug("%s: called\n", __func__);
/* Grab the lock and see if the device is available */
- nand_get_device(mtd, FL_SYNCING);
+ nand_get_device(chip, FL_SYNCING);
/* Release it and go back */
- nand_release_device(mtd);
+ nand_release_device(chip);
}
/**
@@ -2799,7 +2774,9 @@ static void nand_sync(struct mtd_info *mtd)
*/
static int nand_block_isbad(struct mtd_info *mtd, loff_t offs)
{
- return nand_block_checkbad(mtd, offs, 1, 0);
+ struct nand_chip *chip = mtd_to_nand(mtd);
+
+ return nand_block_checkbad(chip, offs, 1, 0);
}
/**
@@ -2809,6 +2786,7 @@ static int nand_block_isbad(struct mtd_info *mtd, loff_t offs)
*/
static int nand_block_markbad(struct mtd_info *mtd, loff_t ofs)
{
+ struct nand_chip *chip = mtd_to_nand(mtd);
int ret;
if (!IS_ENABLED(CONFIG_MTD_WRITE))
@@ -2822,7 +2800,7 @@ static int nand_block_markbad(struct mtd_info *mtd, loff_t ofs)
return ret;
}
- return nand_block_markbad_lowlevel(mtd, ofs);
+ return nand_block_markbad_lowlevel(chip, ofs);
}
/**
@@ -2832,6 +2810,7 @@ static int nand_block_markbad(struct mtd_info *mtd, loff_t ofs)
*/
static int nand_block_markgood(struct mtd_info *mtd, loff_t ofs)
{
+ struct nand_chip *chip = mtd_to_nand(mtd);
int ret;
if (!IS_ENABLED(CONFIG_MTD_WRITE))
@@ -2845,7 +2824,7 @@ static int nand_block_markgood(struct mtd_info *mtd, loff_t ofs)
if (!ret)
return 0;
- return nand_block_markgood_lowlevel(mtd, ofs);
+ return nand_block_markgood_lowlevel(chip, ofs);
}
/**
@@ -2855,7 +2834,7 @@ static int nand_block_markgood(struct mtd_info *mtd, loff_t ofs)
* @addr: feature address.
* @subfeature_param: the subfeature parameters, a four bytes array.
*/
-static int nand_onfi_set_features(struct mtd_info *mtd, struct nand_chip *chip,
+static int nand_onfi_set_features(struct nand_chip *chip,
int addr, uint8_t *subfeature_param)
{
int status;
@@ -2865,9 +2844,9 @@ static int nand_onfi_set_features(struct mtd_info *mtd, struct nand_chip *chip,
& ONFI_OPT_CMD_SET_GET_FEATURES))
return -EINVAL;
- chip->cmdfunc(mtd, NAND_CMD_SET_FEATURES, addr, -1);
- chip->write_buf(mtd, subfeature_param, ONFI_SUBFEATURE_PARAM_LEN);
- status = chip->waitfunc(mtd, chip);
+ chip->cmdfunc(chip, NAND_CMD_SET_FEATURES, addr, -1);
+ chip->write_buf(chip, subfeature_param, ONFI_SUBFEATURE_PARAM_LEN);
+ status = chip->waitfunc(chip);
if (status & NAND_STATUS_FAIL)
return -EIO;
return 0;
@@ -2880,7 +2859,7 @@ static int nand_onfi_set_features(struct mtd_info *mtd, struct nand_chip *chip,
* @addr: feature address.
* @subfeature_param: the subfeature parameters, a four bytes array.
*/
-static int nand_onfi_get_features(struct mtd_info *mtd, struct nand_chip *chip,
+static int nand_onfi_get_features(struct nand_chip *chip,
int addr, uint8_t *subfeature_param)
{
if (!chip->onfi_version ||
@@ -2891,8 +2870,8 @@ static int nand_onfi_get_features(struct mtd_info *mtd, struct nand_chip *chip,
/* clear the sub feature parameters */
memset(subfeature_param, 0, ONFI_SUBFEATURE_PARAM_LEN);
- chip->cmdfunc(mtd, NAND_CMD_GET_FEATURES, addr, -1);
- chip->read_buf(mtd, subfeature_param, ONFI_SUBFEATURE_PARAM_LEN);
+ chip->cmdfunc(chip, NAND_CMD_GET_FEATURES, addr, -1);
+ chip->read_buf(chip, subfeature_param, ONFI_SUBFEATURE_PARAM_LEN);
return 0;
}
@@ -2977,23 +2956,23 @@ static u16 onfi_crc16(u16 crc, u8 const *p, size_t len)
/*
* Check if the NAND chip is ONFI compliant, returns 1 if it is, 0 otherwise.
*/
-static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip,
- int *busw)
+static int nand_flash_detect_onfi(struct nand_chip *chip, int *busw)
{
+ struct mtd_info *mtd = nand_to_mtd(chip);
struct nand_onfi_params *p = &chip->onfi_params;
int i, j;
int val;
/* Try ONFI for unknown chip or LP */
- chip->cmdfunc(mtd, NAND_CMD_READID, 0x20, -1);
- if (chip->read_byte(mtd) != 'O' || chip->read_byte(mtd) != 'N' ||
- chip->read_byte(mtd) != 'F' || chip->read_byte(mtd) != 'I')
+ chip->cmdfunc(chip, NAND_CMD_READID, 0x20, -1);
+ if (chip->read_byte(chip) != 'O' || chip->read_byte(chip) != 'N' ||
+ chip->read_byte(chip) != 'F' || chip->read_byte(chip) != 'I')
return 0;
- chip->cmdfunc(mtd, NAND_CMD_PARAM, 0, -1);
+ chip->cmdfunc(chip, NAND_CMD_PARAM, 0, -1);
for (i = 0; i < 3; i++) {
for (j = 0; j < sizeof(*p); j++)
- ((uint8_t *)p)[j] = chip->read_byte(mtd);
+ ((uint8_t *)p)[j] = chip->read_byte(chip);
if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 254) ==
le16_to_cpu(p->crc)) {
break;
@@ -3314,9 +3293,11 @@ static inline bool is_full_id_nand(struct nand_flash_dev *type)
return type->id_len;
}
-static bool find_full_id_nand(struct mtd_info *mtd, struct nand_chip *chip,
+static bool find_full_id_nand(struct nand_chip *chip,
struct nand_flash_dev *type, u8 *id_data, int *busw)
{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+
if (!strncmp(type->id, id_data, type->id_len)) {
mtd->writesize = type->pagesize;
mtd->erasesize = type->erasesize;
@@ -3336,30 +3317,30 @@ static bool find_full_id_nand(struct mtd_info *mtd, struct nand_chip *chip,
/*
* Get the flash and manufacturer id and lookup if the type is supported.
*/
-static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
- struct nand_chip *chip,
+static struct nand_flash_dev *nand_get_flash_type(struct nand_chip *chip,
int busw,
int *maf_id, int *dev_id,
struct nand_flash_dev *type)
{
+ struct mtd_info *mtd = nand_to_mtd(chip);
int i, maf_idx;
u8 id_data[8];
/* Select the device */
- chip->select_chip(mtd, 0);
+ chip->select_chip(chip, 0);
/*
* Reset the chip, required by some chips (e.g. Micron MT29FxGxxxxx)
* after power-up.
*/
- chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
+ chip->cmdfunc(chip, NAND_CMD_RESET, -1, -1);
/* Send the command for reading device ID */
- chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
+ chip->cmdfunc(chip, NAND_CMD_READID, 0x00, -1);
/* Read manufacturer and device IDs */
- *maf_id = chip->read_byte(mtd);
- *dev_id = chip->read_byte(mtd);
+ *maf_id = chip->read_byte(chip);
+ *dev_id = chip->read_byte(chip);
/*
* Try again to make sure, as some systems the bus-hold or other
@@ -3368,11 +3349,11 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
* not match, ignore the device completely.
*/
- chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
+ chip->cmdfunc(chip, NAND_CMD_READID, 0x00, -1);
/* Read entire ID string */
for (i = 0; i < 8; i++)
- id_data[i] = chip->read_byte(mtd);
+ id_data[i] = chip->read_byte(chip);
if (id_data[0] != *maf_id || id_data[1] != *dev_id) {
pr_info("%s: second ID read did not match "
@@ -3386,7 +3367,7 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
for (; type->name != NULL; type++) {
if (is_full_id_nand(type)) {
- if (find_full_id_nand(mtd, chip, type, id_data, &busw))
+ if (find_full_id_nand(chip, type, id_data, &busw))
goto ident_done;
} else if (*dev_id == type->dev_id) {
break;
@@ -3396,7 +3377,7 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
chip->onfi_version = 0;
if (!type->name || !type->pagesize) {
/* Check is chip is ONFI compliant */
- if (nand_flash_detect_onfi(mtd, chip, &busw))
+ if (nand_flash_detect_onfi(chip, &busw))
goto ident_done;
}
@@ -3408,10 +3389,7 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
chip->chipsize = (uint64_t)type->chipsize << 20;
- if (!type->pagesize && chip->init_size) {
- /* Set the pagesize, oobsize, erasesize by the driver */
- busw = chip->init_size(mtd, chip, id_data);
- } else if (!type->pagesize) {
+ if (!type->pagesize) {
/* Decode parameters from extended ID */
nand_decode_ext_id(mtd, chip, id_data, &busw);
} else {
@@ -3520,11 +3498,11 @@ void nand_of_parse_node(struct mtd_info *mtd, struct device_node *np)
*
* The mtd->owner field must be set to the module of the caller.
*/
-int nand_scan_ident(struct mtd_info *mtd, int maxchips,
+int nand_scan_ident(struct nand_chip *chip, int maxchips,
struct nand_flash_dev *table)
{
+ struct mtd_info *mtd = nand_to_mtd(chip);
int i, busw, nand_maf_id, nand_dev_id;
- struct nand_chip *chip = mtd_to_nand(mtd);
struct nand_flash_dev *type;
/* Get buswidth to select the correct functions */
@@ -3533,32 +3511,32 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips,
nand_set_defaults(chip, busw);
/* Read the flash type */
- type = nand_get_flash_type(mtd, chip, busw,
+ type = nand_get_flash_type(chip, busw,
&nand_maf_id, &nand_dev_id, table);
if (IS_ERR(type)) {
if (!(chip->options & NAND_SCAN_SILENT_NODEV))
pr_warn("No NAND device found\n");
- chip->select_chip(mtd, -1);
+ chip->select_chip(chip, -1);
return PTR_ERR(type);
}
- chip->select_chip(mtd, -1);
+ chip->select_chip(chip, -1);
/* Check for a chip array */
for (i = 1; i < maxchips; i++) {
- chip->select_chip(mtd, i);
+ chip->select_chip(chip, i);
/* See comment in nand_get_flash_type for reset */
- chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
+ chip->cmdfunc(chip, NAND_CMD_RESET, -1, -1);
/* Send the command for reading device ID */
- chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
+ chip->cmdfunc(chip, NAND_CMD_READID, 0x00, -1);
/* Read manufacturer and device IDs */
- if (nand_maf_id != chip->read_byte(mtd) ||
- nand_dev_id != chip->read_byte(mtd)) {
- chip->select_chip(mtd, -1);
+ if (nand_maf_id != chip->read_byte(chip) ||
+ nand_dev_id != chip->read_byte(chip)) {
+ chip->select_chip(chip, -1);
break;
}
- chip->select_chip(mtd, -1);
+ chip->select_chip(chip, -1);
}
if (i > 1)
pr_info("%d NAND chips detected\n", i);
@@ -3580,10 +3558,10 @@ EXPORT_SYMBOL(nand_scan_ident);
* all the uninitialized function pointers with the defaults and scans for a
* bad block table if appropriate.
*/
-int nand_scan_tail(struct mtd_info *mtd)
+int nand_scan_tail(struct nand_chip *chip)
{
+ struct mtd_info *mtd = nand_to_mtd(chip);
int i;
- struct nand_chip *chip = mtd_to_nand(mtd);
/* New bad blocks should be marked in OOB, flash-based BBT, or both */
BUG_ON((chip->bbt_options & NAND_BBT_NO_OOB_BBM) &&
@@ -3738,7 +3716,7 @@ int nand_scan_tail(struct mtd_info *mtd)
/* See nand_bch_init() for details. */
chip->ecc.bytes = 0;
- chip->ecc.priv = nand_bch_init(mtd);
+ chip->ecc.priv = nand_bch_init(chip);
if (!chip->ecc.priv) {
pr_warn("BCH ECC initialization failed!\n");
BUG();
@@ -3853,7 +3831,7 @@ int nand_scan_tail(struct mtd_info *mtd)
return 0;
/* Build bad block table */
- return chip->scan_bbt(mtd);
+ return chip->scan_bbt(chip);
}
EXPORT_SYMBOL(nand_scan_tail);
@@ -3867,13 +3845,13 @@ EXPORT_SYMBOL(nand_scan_tail);
* appropriate values. The mtd->owner field must be set to the module of the
* caller.
*/
-int nand_scan(struct mtd_info *mtd, int maxchips)
+int nand_scan(struct nand_chip *chip, int maxchips)
{
int ret;
- ret = nand_scan_ident(mtd, maxchips, NULL);
+ ret = nand_scan_ident(chip, maxchips, NULL);
if (!ret)
- ret = nand_scan_tail(mtd);
+ ret = nand_scan_tail(chip);
return ret;
}
EXPORT_SYMBOL(nand_scan);
@@ -3882,9 +3860,9 @@ EXPORT_SYMBOL(nand_scan);
* nand_release - [NAND Interface] Free resources held by the NAND device
* @mtd: MTD device structure
*/
-void nand_release(struct mtd_info *mtd)
+void nand_release(struct nand_chip *chip)
{
- struct nand_chip *chip = mtd_to_nand(mtd);
+ struct mtd_info *mtd = nand_to_mtd(chip);
if (chip->ecc.mode == NAND_ECC_SOFT_BCH)
nand_bch_free((struct nand_bch_control *)chip->ecc.priv);
@@ -3952,9 +3930,9 @@ static int mtd_get_bbt_type(struct param_d *p, void *priv)
return 0;
}
-int add_mtd_nand_device(struct mtd_info *mtd, char *devname)
+int add_mtd_nand_device(struct nand_chip *chip, char *devname)
{
- struct nand_chip *chip = mtd_to_nand(mtd);
+ struct mtd_info *mtd = nand_to_mtd(chip);
int ret;
ret = add_mtd_device(mtd, devname, DEVICE_ID_DYNAMIC);
diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c
index ed4104629a..16e77d924e 100644
--- a/drivers/mtd/nand/nand_bbt.c
+++ b/drivers/mtd/nand/nand_bbt.c
@@ -173,11 +173,11 @@ static u32 add_marker_len(struct nand_bbt_descr *td)
*
* Read the bad block table starting from page.
*/
-static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num,
+static int read_bbt(struct nand_chip *this, uint8_t *buf, int page, int num,
struct nand_bbt_descr *td, int offs)
{
+ struct mtd_info *mtd = nand_to_mtd(this);
int res, ret = 0, i, j, act = 0;
- struct nand_chip *this = mtd_to_nand(mtd);
size_t retlen, len, totlen;
loff_t from;
int bits = td->options & NAND_BBT_NRBITS_MSK;
@@ -262,16 +262,17 @@ static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num,
* Read the bad block table for all chips starting at a given page. We assume
* that the bbt bits are in consecutive order.
*/
-static int read_abs_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td, int chip)
+static int read_abs_bbt(struct nand_chip *this, uint8_t *buf, struct nand_bbt_descr *td,
+ int chip)
{
- struct nand_chip *this = mtd_to_nand(mtd);
+ struct mtd_info *mtd = nand_to_mtd(this);
int res = 0, i;
if (td->options & NAND_BBT_PERCHIP) {
int offs = 0;
for (i = 0; i < this->numchips; i++) {
if (chip == -1 || chip == i)
- res = read_bbt(mtd, buf, td->pages[i],
+ res = read_bbt(this, buf, td->pages[i],
this->chipsize >> this->bbt_erase_shift,
td, offs);
if (res)
@@ -279,7 +280,7 @@ static int read_abs_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc
offs += this->chipsize >> this->bbt_erase_shift;
}
} else {
- res = read_bbt(mtd, buf, td->pages[0],
+ res = read_bbt(this, buf, td->pages[0],
mtd->size >> this->bbt_erase_shift, td, 0);
if (res)
return res;
@@ -288,9 +289,10 @@ static int read_abs_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc
}
/* BBT marker is in the first page, no OOB */
-static int scan_read_data(struct mtd_info *mtd, uint8_t *buf, loff_t offs,
+static int scan_read_data(struct nand_chip *this, uint8_t *buf, loff_t offs,
struct nand_bbt_descr *td)
{
+ struct mtd_info *mtd = nand_to_mtd(this);
size_t retlen;
size_t len;
@@ -312,9 +314,10 @@ static int scan_read_data(struct mtd_info *mtd, uint8_t *buf, loff_t offs,
* page,OOB,page,OOB,... in buf. Completes transfer and returns the "strongest"
* ECC condition (error or bitflip). May quit on the first (non-ECC) error.
*/
-static int scan_read_oob(struct mtd_info *mtd, uint8_t *buf, loff_t offs,
+static int scan_read_oob(struct nand_chip *this, uint8_t *buf, loff_t offs,
size_t len)
{
+ struct mtd_info *mtd = nand_to_mtd(this);
struct mtd_oob_ops ops;
int res, ret = 0;
@@ -342,19 +345,20 @@ static int scan_read_oob(struct mtd_info *mtd, uint8_t *buf, loff_t offs,
return ret;
}
-static int scan_read(struct mtd_info *mtd, uint8_t *buf, loff_t offs,
+static int scan_read(struct nand_chip *this, uint8_t *buf, loff_t offs,
size_t len, struct nand_bbt_descr *td)
{
if (td->options & NAND_BBT_NO_OOB)
- return scan_read_data(mtd, buf, offs, td);
+ return scan_read_data(this, buf, offs, td);
else
- return scan_read_oob(mtd, buf, offs, len);
+ return scan_read_oob(this, buf, offs, len);
}
/* Scan write data with oob to flash */
-static int scan_write_bbt(struct mtd_info *mtd, loff_t offs, size_t len,
+static int scan_write_bbt(struct nand_chip *this, loff_t offs, size_t len,
uint8_t *buf, uint8_t *oob)
{
+ struct mtd_info *mtd = nand_to_mtd(this);
struct mtd_oob_ops ops;
ops.mode = MTD_OPS_PLACE_OOB;
@@ -367,8 +371,9 @@ static int scan_write_bbt(struct mtd_info *mtd, loff_t offs, size_t len,
return mtd_write_oob(mtd, offs, &ops);
}
-static u32 bbt_get_ver_offs(struct mtd_info *mtd, struct nand_bbt_descr *td)
+static u32 bbt_get_ver_offs(struct nand_chip *this, struct nand_bbt_descr *td)
{
+ struct mtd_info *mtd = nand_to_mtd(this);
u32 ver_offs = td->veroffs;
if (!(td->options & NAND_BBT_NO_OOB))
@@ -386,34 +391,35 @@ static u32 bbt_get_ver_offs(struct mtd_info *mtd, struct nand_bbt_descr *td)
* Read the bad block table(s) for all chips starting at a given page. We
* assume that the bbt bits are in consecutive order.
*/
-static void read_abs_bbts(struct mtd_info *mtd, uint8_t *buf,
+static void read_abs_bbts(struct nand_chip *this, uint8_t *buf,
struct nand_bbt_descr *td, struct nand_bbt_descr *md)
{
- struct nand_chip *this = mtd_to_nand(mtd);
+ struct mtd_info *mtd = nand_to_mtd(this);
/* Read the primary version, if available */
if (td->options & NAND_BBT_VERSION) {
- scan_read(mtd, buf, (loff_t)td->pages[0] << this->page_shift,
+ scan_read(this, buf, (loff_t)td->pages[0] << this->page_shift,
mtd->writesize, td);
- td->version[0] = buf[bbt_get_ver_offs(mtd, td)];
+ td->version[0] = buf[bbt_get_ver_offs(this, td)];
pr_info("Bad block table at page %d, version 0x%02X\n",
td->pages[0], td->version[0]);
}
/* Read the mirror version, if available */
if (md && (md->options & NAND_BBT_VERSION)) {
- scan_read(mtd, buf, (loff_t)md->pages[0] << this->page_shift,
+ scan_read(this, buf, (loff_t)md->pages[0] << this->page_shift,
mtd->writesize, md);
- md->version[0] = buf[bbt_get_ver_offs(mtd, md)];
+ md->version[0] = buf[bbt_get_ver_offs(this, md)];
pr_info("Bad block table at page %d, version 0x%02X\n",
md->pages[0], md->version[0]);
}
}
/* Scan a given block partially */
-static int scan_block_fast(struct mtd_info *mtd, struct nand_bbt_descr *bd,
+static int scan_block_fast(struct nand_chip *this, struct nand_bbt_descr *bd,
loff_t offs, uint8_t *buf, int numpages)
{
+ struct mtd_info *mtd = nand_to_mtd(this);
struct mtd_oob_ops ops;
int j, ret;
@@ -452,10 +458,10 @@ static int scan_block_fast(struct mtd_info *mtd, struct nand_bbt_descr *bd,
* Create a bad block table by scanning the device for the given good/bad block
* identify pattern.
*/
-static int create_bbt(struct mtd_info *mtd, uint8_t *buf,
+static int create_bbt(struct nand_chip *this, uint8_t *buf,
struct nand_bbt_descr *bd, int chip)
{
- struct nand_chip *this = mtd_to_nand(mtd);
+ struct mtd_info *mtd = nand_to_mtd(this);
int i, numblocks, numpages;
int startblock;
loff_t from;
@@ -491,7 +497,7 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf,
BUG_ON(bd->options & NAND_BBT_NO_OOB);
- ret = scan_block_fast(mtd, bd, from, buf, numpages);
+ ret = scan_block_fast(this, bd, from, buf, numpages);
if (ret < 0)
return ret;
@@ -522,9 +528,9 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf,
*
* The bbt ident pattern resides in the oob area of the first page in a block.
*/
-static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td)
+static int search_bbt(struct nand_chip *this, uint8_t *buf, struct nand_bbt_descr *td)
{
- struct nand_chip *this = mtd_to_nand(mtd);
+ struct mtd_info *mtd = nand_to_mtd(this);
int i, chips;
int startblock, block, dir;
int scanlen = mtd->writesize + mtd->oobsize;
@@ -561,11 +567,11 @@ static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr
loff_t offs = (loff_t)actblock << this->bbt_erase_shift;
/* Read first page */
- scan_read(mtd, buf, offs, mtd->writesize, td);
+ scan_read(this, buf, offs, mtd->writesize, td);
if (!check_pattern(buf, scanlen, mtd->writesize, td)) {
td->pages[i] = actblock << blocktopage;
if (td->options & NAND_BBT_VERSION) {
- offs = bbt_get_ver_offs(mtd, td);
+ offs = bbt_get_ver_offs(this, td);
td->version[i] = buf[offs];
}
break;
@@ -593,16 +599,16 @@ static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr
*
* Search and read the bad block table(s).
*/
-static void search_read_bbts(struct mtd_info *mtd, uint8_t *buf,
+static void search_read_bbts(struct nand_chip *this, uint8_t *buf,
struct nand_bbt_descr *td,
struct nand_bbt_descr *md)
{
/* Search the primary table */
- search_bbt(mtd, buf, td);
+ search_bbt(this, buf, td);
/* Search the mirror table */
if (md)
- search_bbt(mtd, buf, md);
+ search_bbt(this, buf, md);
}
/**
@@ -680,18 +686,17 @@ static int get_bbt_block(struct nand_chip *this, struct nand_bbt_descr *td,
* block as bad using a bad block marker and invalidating the associated
* td->pages[] entry.
*/
-static void mark_bbt_block_bad(struct mtd_info *mtd,
+static void mark_bbt_block_bad(struct nand_chip *this,
struct nand_bbt_descr *td,
int chip, int block)
{
- struct nand_chip *this = mtd_to_nand(mtd);
loff_t to;
int res;
bbt_mark_entry(this, block, BBT_BLOCK_WORN);
to = (loff_t)block << this->bbt_erase_shift;
- res = this->block_markbad(mtd, to);
+ res = this->block_markbad(this, to);
if (res)
pr_warn("nand_bbt: error %d while marking block %d bad\n",
res, block);
@@ -709,11 +714,11 @@ static void mark_bbt_block_bad(struct mtd_info *mtd,
*
* (Re)write the bad block table.
*/
-static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
+static int write_bbt(struct nand_chip *this, uint8_t *buf,
struct nand_bbt_descr *td, struct nand_bbt_descr *md,
int chipsel)
{
- struct nand_chip *this = mtd_to_nand(mtd);
+ struct mtd_info *mtd = nand_to_mtd(this);
struct erase_info einfo;
int i, res, chip = 0;
int bits, page, offs, numblocks, sft, sftmsk;
@@ -856,21 +861,21 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
einfo.mtd = mtd;
einfo.addr = to;
einfo.len = 1 << this->bbt_erase_shift;
- res = nand_erase_nand(mtd, &einfo, 1);
+ res = nand_erase_nand(this, &einfo, 1);
if (res < 0) {
pr_warn("nand_bbt: error while erasing BBT block %d\n",
res);
- mark_bbt_block_bad(mtd, td, chip, block);
+ mark_bbt_block_bad(this, td, chip, block);
continue;
}
- res = scan_write_bbt(mtd, to, len, buf,
+ res = scan_write_bbt(this, to, len, buf,
td->options & NAND_BBT_NO_OOB ? NULL :
&buf[len]);
if (res < 0) {
pr_warn("nand_bbt: error while writing BBT block %d\n",
res);
- mark_bbt_block_bad(mtd, td, chip, block);
+ mark_bbt_block_bad(this, td, chip, block);
continue;
}
@@ -895,11 +900,9 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
* The function creates a memory based bbt by scanning the device for
* manufacturer / software marked good / bad blocks.
*/
-static inline int nand_memory_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
+static inline int nand_memory_bbt(struct nand_chip *this, struct nand_bbt_descr *bd)
{
- struct nand_chip *this = mtd_to_nand(mtd);
-
- return create_bbt(mtd, this->buffers->databuf, bd, -1);
+ return create_bbt(this, this->buffers->databuf, bd, -1);
}
/**
@@ -913,10 +916,9 @@ static inline int nand_memory_bbt(struct mtd_info *mtd, struct nand_bbt_descr *b
* for the chip/device. Update is necessary if one of the tables is missing or
* the version nr. of one table is less than the other.
*/
-static int check_create(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd)
+static int check_create(struct nand_chip *this, uint8_t *buf, struct nand_bbt_descr *bd)
{
int i, chips, writeops, create, chipsel, res, res2;
- struct nand_chip *this = mtd_to_nand(mtd);
struct nand_bbt_descr *td = this->bbt_td;
struct nand_bbt_descr *md = this->bbt_md;
struct nand_bbt_descr *rd, *rd2;
@@ -973,7 +975,7 @@ static int check_create(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc
/* Create the table in memory by scanning the chip(s) */
if (!(this->bbt_options & NAND_BBT_CREATE_EMPTY))
- create_bbt(mtd, buf, bd, chipsel);
+ create_bbt(this, buf, bd, chipsel);
td->version[i] = 1;
if (md)
@@ -982,7 +984,7 @@ static int check_create(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc
/* Read back first? */
if (rd) {
- res = read_abs_bbt(mtd, buf, rd, chipsel);
+ res = read_abs_bbt(this, buf, rd, chipsel);
if (mtd_is_eccerr(res)) {
/* Mark table as invalid */
rd->pages[i] = -1;
@@ -993,7 +995,7 @@ static int check_create(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc
}
/* If they weren't versioned, read both */
if (rd2) {
- res2 = read_abs_bbt(mtd, buf, rd2, chipsel);
+ res2 = read_abs_bbt(this, buf, rd2, chipsel);
if (mtd_is_eccerr(res2)) {
/* Mark table as invalid */
rd2->pages[i] = -1;
@@ -1015,14 +1017,14 @@ static int check_create(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc
/* Write the bad block table to the device? */
if ((writeops & 0x01) && (td->options & NAND_BBT_WRITE)) {
- res = write_bbt(mtd, buf, td, md, chipsel);
+ res = write_bbt(this, buf, td, md, chipsel);
if (res < 0)
return res;
}
/* Write the mirror bad block table to the device? */
if ((writeops & 0x02) && md && (md->options & NAND_BBT_WRITE)) {
- res = write_bbt(mtd, buf, md, td, chipsel);
+ res = write_bbt(this, buf, md, td, chipsel);
if (res < 0)
return res;
}
@@ -1038,9 +1040,8 @@ static int check_create(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc
* The bad block table regions are marked as "bad" to prevent accidental
* erasures / writes. The regions are identified by the mark 0x02.
*/
-static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td)
+static void mark_bbt_region(struct nand_chip *this, struct nand_bbt_descr *td)
{
- struct nand_chip *this = mtd_to_nand(mtd);
int i, j, chips, block, nrblocks, update;
uint8_t oldval;
@@ -1050,7 +1051,7 @@ static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td)
nrblocks = (int)(this->chipsize >> this->bbt_erase_shift);
} else {
chips = 1;
- nrblocks = (int)(mtd->size >> this->bbt_erase_shift);
+ nrblocks = (int)(this->mtd.size >> this->bbt_erase_shift);
}
for (i = 0; i < chips; i++) {
@@ -1063,7 +1064,7 @@ static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td)
bbt_mark_entry(this, block, BBT_BLOCK_RESERVED);
if ((oldval != BBT_BLOCK_RESERVED) &&
td->reserved_block_code)
- nand_update_bbt(mtd, (loff_t)block <<
+ nand_update_bbt(this, (loff_t)block <<
this->bbt_erase_shift);
continue;
}
@@ -1085,7 +1086,7 @@ static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td)
* bbts. This should only happen once.
*/
if (update && td->reserved_block_code)
- nand_update_bbt(mtd, (loff_t)(block - 1) <<
+ nand_update_bbt(this, (loff_t)(block - 1) <<
this->bbt_erase_shift);
}
}
@@ -1098,9 +1099,9 @@ static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td)
* This functions performs a few sanity checks on the bad block description
* table.
*/
-static void verify_bbt_descr(struct mtd_info *mtd, struct nand_bbt_descr *bd)
+static void verify_bbt_descr(struct nand_chip *this, struct nand_bbt_descr *bd)
{
- struct nand_chip *this = mtd_to_nand(mtd);
+ struct mtd_info *mtd = nand_to_mtd(this);
u32 pattern_len;
u32 bits;
u32 table_size;
@@ -1150,9 +1151,9 @@ static void verify_bbt_descr(struct mtd_info *mtd, struct nand_bbt_descr *bd)
* The bad block table memory is allocated here. It must be freed by calling
* the nand_free_bbt function.
*/
-static int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
+static int nand_scan_bbt(struct nand_chip *this, struct nand_bbt_descr *bd)
{
- struct nand_chip *this = mtd_to_nand(mtd);
+ struct mtd_info *mtd = nand_to_mtd(this);
int len, res;
uint8_t *buf;
struct nand_bbt_descr *td = this->bbt_td;
@@ -1172,14 +1173,14 @@ static int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
* memory based bad block table.
*/
if (!td) {
- if ((res = nand_memory_bbt(mtd, bd))) {
+ if ((res = nand_memory_bbt(this, bd))) {
pr_err("nand_bbt: can't scan flash and build the RAM-based BBT\n");
goto err;
}
return 0;
}
- verify_bbt_descr(mtd, td);
- verify_bbt_descr(mtd, md);
+ verify_bbt_descr(this, td);
+ verify_bbt_descr(this, md);
/* Allocate a temporary buffer for one eraseblock incl. oob */
len = (1 << this->bbt_erase_shift);
@@ -1192,20 +1193,20 @@ static int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
/* Is the bbt at a given page? */
if (td->options & NAND_BBT_ABSPAGE) {
- read_abs_bbts(mtd, buf, td, md);
+ read_abs_bbts(this, buf, td, md);
} else {
/* Search the bad block table using a pattern in oob */
- search_read_bbts(mtd, buf, td, md);
+ search_read_bbts(this, buf, td, md);
}
- res = check_create(mtd, buf, bd);
+ res = check_create(this, buf, bd);
if (res)
goto err;
/* Prevent the bbt regions from erasing / writing */
- mark_bbt_region(mtd, td);
+ mark_bbt_region(this, td);
if (md)
- mark_bbt_region(mtd, md);
+ mark_bbt_region(this, md);
vfree(buf);
return 0;
@@ -1223,9 +1224,8 @@ err:
*
* The function updates the bad block table(s).
*/
-int nand_update_bbt(struct mtd_info *mtd, loff_t offs)
+int nand_update_bbt(struct nand_chip *this, loff_t offs)
{
- struct nand_chip *this = mtd_to_nand(mtd);
int len, res = 0;
int chip, chipsel;
uint8_t *buf;
@@ -1237,7 +1237,7 @@ int nand_update_bbt(struct mtd_info *mtd, loff_t offs)
/* Allocate a temporary buffer for one eraseblock incl. oob */
len = (1 << this->bbt_erase_shift);
- len += (len >> this->page_shift) * mtd->oobsize;
+ len += (len >> this->page_shift) * this->mtd.oobsize;
buf = kmalloc(len, GFP_KERNEL);
if (!buf)
return -ENOMEM;
@@ -1257,13 +1257,13 @@ int nand_update_bbt(struct mtd_info *mtd, loff_t offs)
/* Write the bad block table to the device? */
if (td->options & NAND_BBT_WRITE) {
- res = write_bbt(mtd, buf, td, md, chipsel);
+ res = write_bbt(this, buf, td, md, chipsel);
if (res < 0)
goto out;
}
/* Write the mirror bad block table to the device? */
if (md && (md->options & NAND_BBT_WRITE)) {
- res = write_bbt(mtd, buf, md, td, chipsel);
+ res = write_bbt(this, buf, md, td, chipsel);
}
out:
@@ -1357,9 +1357,8 @@ static int nand_create_badblock_pattern(struct nand_chip *this)
* This function selects the default bad block table support for the device and
* calls the nand_scan_bbt function.
*/
-int nand_default_bbt(struct mtd_info *mtd)
+int nand_default_bbt(struct nand_chip *this)
{
- struct nand_chip *this = mtd_to_nand(mtd);
int ret;
/* Is a flash based bad block table requested? */
@@ -1385,7 +1384,7 @@ int nand_default_bbt(struct mtd_info *mtd)
return ret;
}
- return nand_scan_bbt(mtd, this->badblock_pattern);
+ return nand_scan_bbt(this, this->badblock_pattern);
}
/**
@@ -1394,9 +1393,8 @@ int nand_default_bbt(struct mtd_info *mtd)
* @offs: offset in the device
* @allowbbt: allow access to bad block table region
*/
-int nand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt)
+int nand_isbad_bbt(struct nand_chip *this, loff_t offs, int allowbbt)
{
- struct nand_chip *this = mtd_to_nand(mtd);
int block;
uint8_t res;
@@ -1417,9 +1415,8 @@ int nand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt)
return 1;
}
-static int nand_mark_bbt(struct mtd_info *mtd, loff_t offs, uint8_t mark)
+static int nand_mark_bbt(struct nand_chip *this, loff_t offs, uint8_t mark)
{
- struct nand_chip *this = mtd_to_nand(mtd);
int block, ret = 0;
block = (int)(offs >> this->bbt_erase_shift);
@@ -1429,7 +1426,7 @@ static int nand_mark_bbt(struct mtd_info *mtd, loff_t offs, uint8_t mark)
/* Update flash-based bad block table */
if (this->bbt_options & NAND_BBT_USE_FLASH)
- ret = nand_update_bbt(mtd, offs);
+ ret = nand_update_bbt(this, offs);
return ret;
}
@@ -1439,9 +1436,9 @@ static int nand_mark_bbt(struct mtd_info *mtd, loff_t offs, uint8_t mark)
* @mtd: MTD device structure
* @offs: offset of the bad block
*/
-int nand_markbad_bbt(struct mtd_info *mtd, loff_t offs)
+int nand_markbad_bbt(struct nand_chip *this, loff_t offs)
{
- return nand_mark_bbt(mtd, offs, BBT_BLOCK_WORN);
+ return nand_mark_bbt(this, offs, BBT_BLOCK_WORN);
}
/**
@@ -1449,9 +1446,9 @@ int nand_markbad_bbt(struct mtd_info *mtd, loff_t offs)
* @mtd: MTD device structure
* @offs: offset of the good block
*/
-int nand_markgood_bbt(struct mtd_info *mtd, loff_t offs)
+int nand_markgood_bbt(struct nand_chip *this, loff_t offs)
{
- return nand_mark_bbt(mtd, offs, BBT_BLOCK_GOOD);
+ return nand_mark_bbt(this, offs, BBT_BLOCK_GOOD);
}
EXPORT_SYMBOL(nand_scan_bbt);
diff --git a/drivers/mtd/nand/nand_bch.c b/drivers/mtd/nand/nand_bch.c
index b94ecdf8d9..42ffa1b21a 100644
--- a/drivers/mtd/nand/nand_bch.c
+++ b/drivers/mtd/nand/nand_bch.c
@@ -46,10 +46,9 @@ struct nand_bch_control {
* @buf: input buffer with raw data
* @code: output buffer with ECC
*/
-int nand_bch_calculate_ecc(struct mtd_info *mtd, const unsigned char *buf,
+int nand_bch_calculate_ecc(struct nand_chip *chip, const unsigned char *buf,
unsigned char *code)
{
- const struct nand_chip *chip = mtd_to_nand(mtd);
struct nand_bch_control *nbc = chip->ecc.priv;
unsigned int i;
@@ -73,10 +72,9 @@ EXPORT_SYMBOL(nand_bch_calculate_ecc);
*
* Detect and correct bit errors for a data byte block
*/
-int nand_bch_correct_data(struct mtd_info *mtd, unsigned char *buf,
+int nand_bch_correct_data(struct nand_chip *chip, unsigned char *buf,
unsigned char *read_ecc, unsigned char *calc_ecc)
{
- const struct nand_chip *chip = mtd_to_nand(mtd);
struct nand_bch_control *nbc = chip->ecc.priv;
unsigned int *errloc = nbc->errloc;
int i, count;
@@ -117,9 +115,9 @@ EXPORT_SYMBOL(nand_bch_correct_data);
* @eccsize = 512 (thus, m=13 is the smallest integer such that 2^m-1 > 512*8)
* @eccbytes = 7 (7 bytes are required to store m*t = 13*4 = 52 bits)
*/
-struct nand_bch_control *nand_bch_init(struct mtd_info *mtd)
+struct nand_bch_control *nand_bch_init(struct nand_chip *nand)
{
- struct nand_chip *nand = mtd_to_nand(mtd);
+ struct mtd_info *mtd = nand_to_mtd(nand);
unsigned int m, t, eccsteps, i;
struct nand_ecclayout *layout = nand->ecc.layout;
struct nand_bch_control *nbc = NULL;
diff --git a/drivers/mtd/nand/nand_denali.c b/drivers/mtd/nand/nand_denali.c
index 8995845649..77a09ede70 100644
--- a/drivers/mtd/nand/nand_denali.c
+++ b/drivers/mtd/nand/nand_denali.c
@@ -76,10 +76,8 @@ static uint32_t denali_irq_mask = DENALI_IRQ_ALL;
* this macro allows us to convert from an MTD structure to our own
* device context (denali) structure.
*/
-static inline struct denali_nand_info *mtd_to_denali(struct mtd_info *mtd)
+static inline struct denali_nand_info *nand_to_denali(struct nand_chip *nand)
{
- struct nand_chip *nand = mtd_to_nand(mtd);
-
return container_of(nand, struct denali_nand_info, nand);
}
@@ -788,9 +786,10 @@ static int read_data_from_flash_mem(struct denali_nand_info *denali,
}
/* writes OOB data to the device */
-static int write_oob_data(struct mtd_info *mtd, uint8_t *buf, int page)
+static int write_oob_data(struct nand_chip *chip, uint8_t *buf, int page)
{
- struct denali_nand_info *denali = mtd_to_denali(mtd);
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ struct denali_nand_info *denali = nand_to_denali(chip);
uint32_t irq_status;
uint32_t irq_mask = INTR_STATUS__PROGRAM_COMP |
INTR_STATUS__PROGRAM_FAIL;
@@ -827,9 +826,10 @@ static int write_oob_data(struct mtd_info *mtd, uint8_t *buf, int page)
}
/* reads OOB data from the device */
-static void read_oob_data(struct mtd_info *mtd, uint8_t *buf, int page)
+static void read_oob_data(struct nand_chip *chip, uint8_t *buf, int page)
{
- struct denali_nand_info *denali = mtd_to_denali(mtd);
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ struct denali_nand_info *denali = nand_to_denali(chip);
uint32_t irq_mask = INTR_STATUS__LOAD_COMP;
uint32_t irq_status, addr, cmd;
@@ -995,10 +995,10 @@ static void denali_setup_dma(struct denali_nand_info *denali, int op)
* writes a page. user specifies type, and this function handles the
* configuration details.
*/
-static int write_page(struct mtd_info *mtd, struct nand_chip *chip,
- const uint8_t *buf, bool raw_xfer)
+static int write_page(struct nand_chip *chip, const uint8_t *buf, bool raw_xfer)
{
- struct denali_nand_info *denali = mtd_to_denali(mtd);
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ struct denali_nand_info *denali = nand_to_denali(chip);
dma_addr_t addr = (unsigned long)denali->buf.buf;
size_t size = mtd->writesize + mtd->oobsize;
uint32_t irq_status;
@@ -1051,14 +1051,14 @@ static int write_page(struct mtd_info *mtd, struct nand_chip *chip,
* writing a page with ECC or without is similar, all the work is done
* by write_page above.
*/
-static int denali_write_page(struct mtd_info *mtd, struct nand_chip *chip,
+static int denali_write_page(struct nand_chip *chip,
const uint8_t *buf, int oob_required)
{
/*
* for regular page writes, we let HW handle all the ECC
* data written to the device.
*/
- return write_page(mtd, chip, buf, false);
+ return write_page(chip, buf, false);
}
/*
@@ -1066,35 +1066,34 @@ static int denali_write_page(struct mtd_info *mtd, struct nand_chip *chip,
* raw access is similar to ECC page writes, so all the work is done in the
* write_page() function above.
*/
-static int denali_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
+static int denali_write_page_raw(struct nand_chip *chip,
const uint8_t *buf, int oob_required)
{
/*
* for raw page writes, we want to disable ECC and simply write
* whatever data is in the buffer.
*/
- return write_page(mtd, chip, buf, true);
+ return write_page(chip, buf, true);
}
-static int denali_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
- int page)
+static int denali_write_oob(struct nand_chip *chip, int page)
{
- return write_oob_data(mtd, chip->oob_poi, page);
+ return write_oob_data(chip, chip->oob_poi, page);
}
-static int denali_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
- int page)
+static int denali_read_oob(struct nand_chip *chip, int page)
{
- read_oob_data(mtd, chip->oob_poi, page);
+ read_oob_data(chip, chip->oob_poi, page);
return 0;
}
-static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip,
+static int denali_read_page(struct nand_chip *chip,
uint8_t *buf, int oob_required, int page)
{
+ struct mtd_info *mtd = nand_to_mtd(chip);
unsigned int max_bitflips = 0;
- struct denali_nand_info *denali = mtd_to_denali(mtd);
+ struct denali_nand_info *denali = nand_to_denali(chip);
dma_addr_t addr = (unsigned long)denali->buf.buf;
size_t size = mtd->writesize + mtd->oobsize;
@@ -1138,8 +1137,7 @@ static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip,
if (!is_erased(buf, mtd->writesize))
mtd->ecc_stats.failed++;
} else {
- read_oob_data(&denali->nand.mtd, chip->oob_poi,
- denali->page);
+ read_oob_data(chip, chip->oob_poi, denali->page);
/* check ECC failures that may have occurred on
* erased pages */
@@ -1154,10 +1152,11 @@ static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip,
return max_bitflips;
}
-static int denali_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
+static int denali_read_page_raw(struct nand_chip *chip,
uint8_t *buf, int oob_required, int page)
{
- struct denali_nand_info *denali = mtd_to_denali(mtd);
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ struct denali_nand_info *denali = nand_to_denali(chip);
dma_addr_t addr = (unsigned long)denali->buf.buf;
size_t size = mtd->writesize + mtd->oobsize;
uint32_t irq_mask = INTR_STATUS__DMA_CMD_COMP;
@@ -1190,9 +1189,9 @@ static int denali_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
return 0;
}
-static uint8_t denali_read_byte(struct mtd_info *mtd)
+static uint8_t denali_read_byte(struct nand_chip *chip)
{
- struct denali_nand_info *denali = mtd_to_denali(mtd);
+ struct denali_nand_info *denali = nand_to_denali(chip);
uint8_t result = 0xff;
if (denali->buf.head < denali->buf.tail)
@@ -1201,23 +1200,23 @@ static uint8_t denali_read_byte(struct mtd_info *mtd)
return result;
}
-static void denali_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+static void denali_read_buf(struct nand_chip *chip, uint8_t *buf, int len)
{
int i;
for (i = 0; i < len; i++)
- buf[i] = denali_read_byte(mtd);
+ buf[i] = denali_read_byte(chip);
}
-static void denali_select_chip(struct mtd_info *mtd, int chip)
+static void denali_select_chip(struct nand_chip *chip, int num)
{
- struct denali_nand_info *denali = mtd_to_denali(mtd);
+ struct denali_nand_info *denali = nand_to_denali(chip);
- denali->flash_bank = chip;
+ denali->flash_bank = num;
}
-static int denali_waitfunc(struct mtd_info *mtd, struct nand_chip *chip)
+static int denali_waitfunc(struct nand_chip *chip)
{
- struct denali_nand_info *denali = mtd_to_denali(mtd);
+ struct denali_nand_info *denali = nand_to_denali(chip);
int status = denali->status;
denali->status = 0;
@@ -1225,10 +1224,11 @@ static int denali_waitfunc(struct mtd_info *mtd, struct nand_chip *chip)
return status;
}
-static void denali_cmdfunc(struct mtd_info *mtd, unsigned int cmd, int col,
+static void denali_cmdfunc(struct nand_chip *chip, unsigned int cmd, int col,
int page)
{
- struct denali_nand_info *denali = mtd_to_denali(mtd);
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ struct denali_nand_info *denali = nand_to_denali(chip);
uint32_t addr, id;
uint32_t pages_per_block;
uint32_t block;
@@ -1415,7 +1415,7 @@ int denali_init(struct denali_nand_info *denali)
* this is the first stage in a two step process to register
* with the nand subsystem
*/
- if (nand_scan_ident(mtd, denali->max_banks, NULL)) {
+ if (nand_scan_ident(nand, denali->max_banks, NULL)) {
ret = -ENXIO;
goto failed_req_irq;
}
@@ -1537,12 +1537,12 @@ int denali_init(struct denali_nand_info *denali)
MAIN_ACCESS);
}
- if (nand_scan_tail(mtd)) {
+ if (nand_scan_tail(nand)) {
ret = -ENXIO;
goto failed_req_irq;
}
- return add_mtd_nand_device(mtd, "nand");
+ return add_mtd_nand_device(nand, "nand");
failed_req_irq:
denali_irq_cleanup(denali->irq, denali);
diff --git a/drivers/mtd/nand/nand_ecc.c b/drivers/mtd/nand/nand_ecc.c
index fd6ad7edc8..e488130f73 100644
--- a/drivers/mtd/nand/nand_ecc.c
+++ b/drivers/mtd/nand/nand_ecc.c
@@ -66,7 +66,7 @@ static const u_char nand_ecc_precalc_table[] = {
* @dat: raw data
* @ecc_code: buffer for ECC
*/
-int nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
+int nand_calculate_ecc(struct nand_chip *chip, const u_char *dat,
u_char *ecc_code)
{
uint8_t idx, reg1, reg2, reg3, tmp1, tmp2;
@@ -139,7 +139,7 @@ static inline int countbits(uint32_t byte)
*
* Detect and correct a 1 bit error for 256 byte block
*/
-int nand_correct_data(struct mtd_info *mtd, u_char *dat,
+int nand_correct_data(struct nand_chip *chip, u_char *dat,
u_char *read_ecc, u_char *calc_ecc)
{
uint8_t s0, s1, s2;
diff --git a/drivers/mtd/nand/nand_imx.c b/drivers/mtd/nand/nand_imx.c
index d69a012f01..40877eafb6 100644
--- a/drivers/mtd/nand/nand_imx.c
+++ b/drivers/mtd/nand/nand_imx.c
@@ -57,7 +57,7 @@ struct imx_nand_host {
int data_width;
int flash_bbt;
- void (*preset)(struct mtd_info *);
+ void (*preset)(struct nand_chip *);
void (*send_cmd)(struct imx_nand_host *, uint16_t);
void (*send_addr)(struct imx_nand_host *, uint16_t);
void (*send_page)(struct imx_nand_host *, unsigned int);
@@ -65,7 +65,7 @@ struct imx_nand_host {
void (*send_read_param)(struct imx_nand_host *);
uint16_t (*get_dev_status)(struct imx_nand_host *);
int (*check_int)(struct imx_nand_host *);
- int (*correct)(struct mtd_info *mtd);
+ int (*correct)(struct nand_chip *);
void (*enable_hwecc)(struct nand_chip *, bool enable);
};
@@ -429,7 +429,7 @@ static u16 get_dev_status_v1_v2(struct imx_nand_host *host)
*
* @return 0 if device is busy else 1
*/
-static int imx_nand_dev_ready(struct mtd_info *mtd)
+static int imx_nand_dev_ready(struct nand_chip *chip)
{
/*
* NFC handles R/B internally.Therefore,this function
@@ -475,10 +475,10 @@ static void imx_nand_enable_hwecc_v3(struct nand_chip *chip, bool enable)
writel(config2, NFC_V3_CONFIG2);
}
-static int imx_nand_correct_data_v1(struct mtd_info *mtd)
+static int imx_nand_correct_data_v1(struct nand_chip *chip)
{
- struct nand_chip *nand_chip = mtd_to_nand(mtd);
- struct imx_nand_host *host = nand_chip->priv;
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ struct imx_nand_host *host = chip->priv;
if (host->eccstatus_v1 < 0)
return host->eccstatus_v1;
@@ -491,10 +491,10 @@ static int imx_nand_correct_data_v1(struct mtd_info *mtd)
return 0;
}
-static int imx_nand_correct_data_v2_v3(struct mtd_info *mtd)
+static int imx_nand_correct_data_v2_v3(struct nand_chip *chip)
{
- struct nand_chip *nand_chip = mtd_to_nand(mtd);
- struct imx_nand_host *host = nand_chip->priv;
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ struct imx_nand_host *host = chip->priv;
u32 ecc_stat, err;
int no_subpages;
u8 ecc_bit_mask, err_limit, max_bitflips = 0;
@@ -521,7 +521,7 @@ static int imx_nand_correct_data_v2_v3(struct mtd_info *mtd)
return max_bitflips;
}
-static int imx_nand_calculate_ecc(struct mtd_info *mtd, const u_char * dat,
+static int imx_nand_calculate_ecc(struct nand_chip *chip, const u_char * dat,
u_char * ecc_code)
{
return 0;
@@ -534,17 +534,16 @@ static int imx_nand_calculate_ecc(struct mtd_info *mtd, const u_char * dat,
*
* @return data read from the NAND Flash
*/
-static u_char imx_nand_read_byte(struct mtd_info *mtd)
+static u_char imx_nand_read_byte(struct nand_chip *chip)
{
- struct nand_chip *nand_chip = mtd_to_nand(mtd);
- struct imx_nand_host *host = nand_chip->priv;
+ struct imx_nand_host *host = chip->priv;
u_char ret;
/* Check for status request */
if (host->status_request)
return host->get_dev_status(host) & 0xFF;
- if (nand_chip->options & NAND_BUSWIDTH_16) {
+ if (chip->options & NAND_BUSWIDTH_16) {
/* only take the lower byte of each word */
BUG_ON(host->buf_start & 1);
ret = *(uint16_t *)(host->data_buf + host->buf_start);
@@ -565,10 +564,9 @@ static u_char imx_nand_read_byte(struct mtd_info *mtd)
*
* @return data read from the NAND Flash
*/
-static u16 imx_nand_read_word(struct mtd_info *mtd)
+static u16 imx_nand_read_word(struct nand_chip *chip)
{
- struct nand_chip *nand_chip = mtd_to_nand(mtd);
- struct imx_nand_host *host = nand_chip->priv;
+ struct imx_nand_host *host = chip->priv;
uint16_t ret;
ret = *(uint16_t *)(host->data_buf + host->buf_start);
@@ -586,11 +584,11 @@ static u16 imx_nand_read_word(struct mtd_info *mtd)
* @param buf data to be written to NAND Flash
* @param len number of bytes to be written
*/
-static void imx_nand_write_buf(struct mtd_info *mtd,
+static void imx_nand_write_buf(struct nand_chip *chip,
const u_char *buf, int len)
{
- struct nand_chip *nand_chip = mtd_to_nand(mtd);
- struct imx_nand_host *host = nand_chip->priv;
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ struct imx_nand_host *host = chip->priv;
u16 col = host->buf_start;
int n = mtd->oobsize + mtd->writesize - col;
@@ -610,10 +608,10 @@ static void imx_nand_write_buf(struct mtd_info *mtd,
* @param buf data to be read from NAND Flash
* @param len number of bytes to be read
*/
-static void imx_nand_read_buf(struct mtd_info *mtd, u_char * buf, int len)
+static void imx_nand_read_buf(struct nand_chip *chip, u_char * buf, int len)
{
- struct nand_chip *nand_chip = mtd_to_nand(mtd);
- struct imx_nand_host *host = nand_chip->priv;
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ struct imx_nand_host *host = chip->priv;
u16 col = host->buf_start;
int n = mtd->oobsize + mtd->writesize - col;
@@ -631,10 +629,10 @@ static void imx_nand_read_buf(struct mtd_info *mtd, u_char * buf, int len)
/*
* Function to transfer data to/from spare area.
*/
-static void copy_spare(struct mtd_info *mtd, int bfrom, void *buf)
+static void copy_spare(struct nand_chip *chip, int bfrom, void *buf)
{
- struct nand_chip *this = mtd_to_nand(mtd);
- struct imx_nand_host *host = this->priv;
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ struct imx_nand_host *host = chip->priv;
u16 i, j;
u16 n = mtd->writesize >> 9;
u8 *d = buf;
@@ -665,14 +663,14 @@ static void copy_spare(struct mtd_info *mtd, int bfrom, void *buf)
* @param mtd MTD structure for the NAND Flash
* @param chip val indicating select or deselect
*/
-static void imx_nand_select_chip(struct mtd_info *mtd, int chip)
+static void imx_nand_select_chip(struct nand_chip *_chip, int chip)
{
}
-static void mxc_do_addr_cycle(struct mtd_info *mtd, int column, int page_addr)
+static void mxc_do_addr_cycle(struct nand_chip *chip, int column, int page_addr)
{
- struct nand_chip *nand_chip = mtd_to_nand(mtd);
- struct imx_nand_host *host = nand_chip->priv;
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ struct imx_nand_host *host = chip->priv;
/*
* Write out column address, if necessary
@@ -735,10 +733,9 @@ static int get_eccsize(struct mtd_info *mtd)
return 8;
}
-static void preset_v1(struct mtd_info *mtd)
+static void preset_v1(struct nand_chip *chip)
{
- struct nand_chip *nand_chip = mtd_to_nand(mtd);
- struct imx_nand_host *host = nand_chip->priv;
+ struct imx_nand_host *host = chip->priv;
uint16_t config1 = 0;
host->eccsize = 1;
@@ -757,14 +754,14 @@ static void preset_v1(struct mtd_info *mtd)
writew(0x4, host->regs + NFC_V1_V2_WRPROT);
}
-static void preset_v2(struct mtd_info *mtd)
+static void preset_v2(struct nand_chip *chip)
{
- struct nand_chip *nand_chip = mtd_to_nand(mtd);
- struct imx_nand_host *host = nand_chip->priv;
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ struct imx_nand_host *host = chip->priv;
uint16_t config1 = 0;
int mode;
- mode = onfi_get_async_timing_mode(nand_chip);
+ mode = onfi_get_async_timing_mode(chip);
if (mode != ONFI_TIMING_MODE_UNKNOWN && !IS_ERR(host->clk)) {
const struct nand_sdr_timings *timings;
@@ -817,9 +814,9 @@ static void preset_v2(struct mtd_info *mtd)
writew(0x4, host->regs + NFC_V1_V2_WRPROT);
}
-static void preset_v3(struct mtd_info *mtd)
+static void preset_v3(struct nand_chip *chip)
{
- struct nand_chip *chip = mtd_to_nand(mtd);
+ struct mtd_info *mtd = nand_to_mtd(chip);
struct imx_nand_host *host = chip->priv;
uint32_t config2, config3;
int i, addr_phases;
@@ -887,24 +884,25 @@ static void preset_v3(struct mtd_info *mtd)
writel(0, NFC_V3_DELAY_LINE);
}
-static int imx_nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
+static int imx_nand_write_page(struct nand_chip *chip,
uint32_t offset, int data_len, const uint8_t *buf,
int oob_required, int page, int cached, int raw)
{
+ struct mtd_info *mtd = nand_to_mtd(chip);
struct imx_nand_host *host = chip->priv;
int status;
host->enable_hwecc(chip, !raw);
- chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page);
+ chip->cmdfunc(chip, NAND_CMD_SEQIN, 0x00, page);
memcpy32(host->main_area0, buf, mtd->writesize);
if (oob_required)
- copy_spare(mtd, 0, chip->oob_poi);
+ copy_spare(chip, 0, chip->oob_poi);
host->send_page(host, NFC_INPUT);
- chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
- status = chip->waitfunc(mtd, chip);
+ chip->cmdfunc(chip, NAND_CMD_PAGEPROG, -1, -1);
+ status = chip->waitfunc(chip);
if (status & NAND_STATUS_FAIL)
return -EIO;
@@ -912,9 +910,10 @@ static int imx_nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
return 0;
}
-static void imx_nand_do_read_page(struct mtd_info *mtd,
- struct nand_chip *chip, uint8_t *buf, int oob_required)
+static void imx_nand_do_read_page(struct nand_chip *chip, uint8_t *buf,
+ int oob_required)
{
+ struct mtd_info *mtd = nand_to_mtd(chip);
struct imx_nand_host *host = chip->priv;
host->send_page(host, NFC_OUTPUT);
@@ -922,29 +921,29 @@ static void imx_nand_do_read_page(struct mtd_info *mtd,
memcpy32(buf, host->main_area0, mtd->writesize);
if (oob_required)
- copy_spare(mtd, 1, chip->oob_poi);
+ copy_spare(chip, 1, chip->oob_poi);
}
-static int imx_nand_read_page(struct mtd_info *mtd,
- struct nand_chip *chip, uint8_t *buf, int oob_required, int page)
+static int imx_nand_read_page(struct nand_chip *chip, uint8_t *buf,
+ int oob_required, int page)
{
struct imx_nand_host *host = chip->priv;
host->enable_hwecc(chip, true);
- imx_nand_do_read_page(mtd, chip, buf, oob_required);
+ imx_nand_do_read_page(chip, buf, oob_required);
- return host->correct(mtd);
+ return host->correct(chip);
}
-static int imx_nand_read_page_raw(struct mtd_info *mtd,
- struct nand_chip *chip, uint8_t *buf, int oob_required, int page)
+static int imx_nand_read_page_raw(struct nand_chip *chip, uint8_t *buf,
+ int oob_required, int page)
{
struct imx_nand_host *host = chip->priv;
host->enable_hwecc(chip, false);
- imx_nand_do_read_page(mtd, chip, buf, oob_required);
+ imx_nand_do_read_page(chip, buf, oob_required);
return 0;
}
@@ -958,11 +957,11 @@ static int imx_nand_read_page_raw(struct mtd_info *mtd,
* @param column column offset for the page read
* @param page_addr page to be read from NAND Flash
*/
-static void imx_nand_command(struct mtd_info *mtd, unsigned command,
+static void imx_nand_command(struct nand_chip *chip, unsigned command,
int column, int page_addr)
{
- struct nand_chip *nand_chip = mtd_to_nand(mtd);
- struct imx_nand_host *host = nand_chip->priv;
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ struct imx_nand_host *host = chip->priv;
dev_dbg(host->dev,
"imx_nand_command (cmd = 0x%x, col = 0x%x, page = 0x%x)\n",
@@ -978,7 +977,7 @@ static void imx_nand_command(struct mtd_info *mtd, unsigned command,
*/
switch (command) {
case NAND_CMD_RESET:
- host->preset(mtd);
+ host->preset(chip);
host->send_cmd(host, command);
break;
@@ -986,7 +985,7 @@ static void imx_nand_command(struct mtd_info *mtd, unsigned command,
host->buf_start = 0;
host->status_request = 1;
host->send_cmd(host, command);
- mxc_do_addr_cycle(mtd, column, page_addr);
+ mxc_do_addr_cycle(chip, column, page_addr);
break;
case NAND_CMD_READ0:
@@ -999,7 +998,7 @@ static void imx_nand_command(struct mtd_info *mtd, unsigned command,
command = NAND_CMD_READ0;
host->send_cmd(host, command);
- mxc_do_addr_cycle(mtd, column, page_addr);
+ mxc_do_addr_cycle(chip, column, page_addr);
if (host->pagesize_2k)
/* send read confirm command */
@@ -1017,7 +1016,7 @@ static void imx_nand_command(struct mtd_info *mtd, unsigned command,
* whole page including OOB together.
*/
/* call ourself to read a page */
- imx_nand_command(mtd, NAND_CMD_READ0, 0,
+ imx_nand_command(chip, NAND_CMD_READ0, 0,
page_addr);
}
host->buf_start = column;
@@ -1033,25 +1032,25 @@ static void imx_nand_command(struct mtd_info *mtd, unsigned command,
host->send_cmd(host, NAND_CMD_READ0);
}
host->send_cmd(host, command);
- mxc_do_addr_cycle(mtd, column, page_addr);
+ mxc_do_addr_cycle(chip, column, page_addr);
break;
case NAND_CMD_PAGEPROG:
host->send_cmd(host, command);
- mxc_do_addr_cycle(mtd, column, page_addr);
+ mxc_do_addr_cycle(chip, column, page_addr);
break;
case NAND_CMD_READID:
host->send_cmd(host, command);
- mxc_do_addr_cycle(mtd, column, page_addr);
+ mxc_do_addr_cycle(chip, column, page_addr);
host->send_read_id(host);
host->buf_start = 0;
break;
case NAND_CMD_PARAM:
host->send_cmd(host, command);
- mxc_do_addr_cycle(mtd, column, page_addr);
+ mxc_do_addr_cycle(chip, column, page_addr);
host->send_read_param(host);
host->buf_start = 0;
break;
@@ -1059,7 +1058,7 @@ static void imx_nand_command(struct mtd_info *mtd, unsigned command,
case NAND_CMD_ERASE1:
case NAND_CMD_ERASE2:
host->send_cmd(host, command);
- mxc_do_addr_cycle(mtd, column, page_addr);
+ mxc_do_addr_cycle(chip, column, page_addr);
break;
}
}
@@ -1141,8 +1140,9 @@ static int __init mxcnd_probe_dt(struct imx_nand_host *host)
* on the flash BBT.
*
*/
-static int checkbad(struct mtd_info *mtd, loff_t ofs)
+static int checkbad(struct nand_chip *chip, loff_t ofs)
{
+ struct mtd_info *mtd = nand_to_mtd(chip);
int ret;
uint8_t buf[mtd->writesize + mtd->oobsize];
struct mtd_oob_ops ops;
@@ -1164,9 +1164,9 @@ static int checkbad(struct mtd_info *mtd, loff_t ofs)
return 0;
}
-static int imxnd_create_bbt(struct mtd_info *mtd)
+static int imxnd_create_bbt(struct nand_chip *chip)
{
- struct nand_chip *chip = mtd_to_nand(mtd);
+ struct mtd_info *mtd = nand_to_mtd(chip);
int len, i, numblocks, ret;
loff_t from = 0;
uint8_t *bbt;
@@ -1181,7 +1181,7 @@ static int imxnd_create_bbt(struct mtd_info *mtd)
numblocks = mtd->size >> (chip->bbt_erase_shift - 1);
for (i = 0; i < numblocks;) {
- ret = checkbad(mtd, from);
+ ret = checkbad(chip, from);
if (ret < 0)
goto out;
@@ -1201,11 +1201,11 @@ static int imxnd_create_bbt(struct mtd_info *mtd)
free(chip->bbt);
chip->bbt = bbt;
- ret = nand_update_bbt(mtd, 0);
+ ret = nand_update_bbt(chip, 0);
if (ret)
return ret;
- ret = nand_default_bbt(mtd);
+ ret = nand_default_bbt(chip);
if (ret)
return ret;
@@ -1389,13 +1389,13 @@ static int __init imxnd_probe(struct device_d *dev)
}
/* first scan to find the device and get the page size */
- if (nand_scan_ident(mtd, 1, NULL)) {
+ if (nand_scan_ident(this, 1, NULL)) {
err = -ENXIO;
goto escan;
}
/* Call preset again, with correct writesize this time */
- host->preset(mtd);
+ host->preset(this);
imx_nand_set_layout(mtd->writesize, host->data_width == 2 ? 16 : 8);
@@ -1423,21 +1423,21 @@ static int __init imxnd_probe(struct device_d *dev)
this->ecc.strength = host->eccsize;
/* second phase scan */
- if (nand_scan_tail(mtd)) {
+ if (nand_scan_tail(this)) {
err = -ENXIO;
goto escan;
}
if (host->flash_bbt && this->bbt_td->pages[0] == -1 && this->bbt_md->pages[0] == -1) {
dev_info(dev, "no BBT found. creating one\n");
- err = imxnd_create_bbt(mtd);
+ err = imxnd_create_bbt(this);
if (err)
dev_warn(dev, "Failed to create bbt: %s\n",
strerror(-err));
err = 0;
}
- add_mtd_nand_device(mtd, "nand");
+ add_mtd_nand_device(this, "nand");
dev->priv = host;
diff --git a/drivers/mtd/nand/nand_mrvl_nfc.c b/drivers/mtd/nand/nand_mrvl_nfc.c
index 15d052b5a4..0a7c0d4a9a 100644
--- a/drivers/mtd/nand/nand_mrvl_nfc.c
+++ b/drivers/mtd/nand/nand_mrvl_nfc.c
@@ -277,8 +277,10 @@ static struct nand_ecclayout ecc_layout_4KB_bch8bit = {
#define NDTR1_tWHR(c) (min((c), 15) << 4)
#define NDTR1_tAR(c) (min((c), 15) << 0)
-#define mtd_info_to_host(mtd) ((struct mrvl_nand_host *) \
- (((struct nand_chip *)((mtd)->priv))->priv))
+static inline struct mrvl_nand_host *nand_to_host(struct nand_chip *chip)
+{
+ return container_of(chip, struct mrvl_nand_host, chip);
+}
static const struct mrvl_nand_variant pxa3xx_variant = {
.hwflags = 0,
@@ -341,7 +343,7 @@ static struct mrvl_nand_timing timings[] = {
static void mrvl_nand_set_timing(struct mrvl_nand_host *host, bool use_default)
{
- struct mtd_info *mtd = &host->chip.mtd;
+ struct nand_chip *chip = &host->chip;
unsigned long nand_clk = clk_get_rate(host->core_clk);
struct mrvl_nand_timing *t;
uint32_t ndtr0, ndtr1;
@@ -350,8 +352,8 @@ static void mrvl_nand_set_timing(struct mrvl_nand_host *host, bool use_default)
if (use_default) {
id = 0;
} else {
- host->chip.cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
- host->chip.read_buf(mtd, (unsigned char *)&id, sizeof(id));
+ chip->cmdfunc(chip, NAND_CMD_READID, 0x00, -1);
+ chip->read_buf(chip, (unsigned char *)&id, sizeof(id));
}
for (t = &timings[0]; t->id; t++)
if (t->id == id)
@@ -370,9 +372,9 @@ static void mrvl_nand_set_timing(struct mrvl_nand_host *host, bool use_default)
nand_writel(host, NDTR1CS0, ndtr1);
}
-static int mrvl_nand_ready(struct mtd_info *mtd)
+static int mrvl_nand_ready(struct nand_chip *chip)
{
- struct mrvl_nand_host *host = mtd_info_to_host(mtd);
+ struct mrvl_nand_host *host = nand_to_host(chip);
u32 ndcr;
ndcr = nand_readl(host, NDSR);
@@ -396,14 +398,14 @@ static int mrvl_nand_ready(struct mtd_info *mtd)
* Thus, this function is only called when we want *all* blocks to look good,
* so it *always* return success.
*/
-static int mrvl_nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
+static int mrvl_nand_block_bad(struct nand_chip *chip, loff_t ofs, int getchip)
{
return 0;
}
-static void mrvl_nand_select_chip(struct mtd_info *mtd, int chipnr)
+static void mrvl_nand_select_chip(struct nand_chip *chip, int chipnr)
{
- struct mrvl_nand_host *host = mtd_info_to_host(mtd);
+ struct mrvl_nand_host *host = nand_to_host(chip);
if (chipnr <= 0 || chipnr >= 3 || chipnr == host->cs)
return;
@@ -759,10 +761,10 @@ static void mrvl_nand_wait_cmd_done(struct mrvl_nand_host *host,
}
}
-static void mrvl_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
+static void mrvl_nand_cmdfunc(struct nand_chip *chip, unsigned command,
int column, int page_addr)
{
- struct mrvl_nand_host *host = mtd_info_to_host(mtd);
+ struct mrvl_nand_host *host = nand_to_host(chip);
/*
* if this is a x16 device ,then convert the input
@@ -790,10 +792,11 @@ static void mrvl_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
*
* Returns 0
*/
-static int mrvl_nand_write_page_hwecc(struct mtd_info *mtd,
- struct nand_chip *chip, const uint8_t *buf, int oob_required)
+static int mrvl_nand_write_page_hwecc(struct nand_chip *chip, const uint8_t *buf,
+ int oob_required)
{
- struct mrvl_nand_host *host = mtd_info_to_host(mtd);
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ struct mrvl_nand_host *host = nand_to_host(chip);
memcpy(host->data_buff, buf, mtd->writesize);
if (oob_required)
@@ -806,16 +809,16 @@ static int mrvl_nand_write_page_hwecc(struct mtd_info *mtd,
return 0;
}
-static int mrvl_nand_read_page_hwecc(struct mtd_info *mtd,
- struct nand_chip *chip, uint8_t *buf, int oob_required,
- int page)
+static int mrvl_nand_read_page_hwecc(struct nand_chip *chip, uint8_t *buf,
+ int oob_required, int page)
{
- struct mrvl_nand_host *host = mtd_info_to_host(mtd);
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ struct mrvl_nand_host *host = nand_to_host(chip);
u32 ndsr;
int ret = 0;
- chip->read_buf(mtd, buf, mtd->writesize);
- chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
+ chip->read_buf(chip, buf, mtd->writesize);
+ chip->read_buf(chip, chip->oob_poi, mtd->oobsize);
ndsr = nand_readl(host, NDSR);
if (ndsr & NDSR_UNCORERR) {
@@ -837,9 +840,9 @@ static int mrvl_nand_read_page_hwecc(struct mtd_info *mtd,
return ret;
}
-static void mrvl_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+static void mrvl_nand_read_buf(struct nand_chip *chip, uint8_t *buf, int len)
{
- struct mrvl_nand_host *host = mtd_info_to_host(mtd);
+ struct mrvl_nand_host *host = nand_to_host(chip);
int xfer;
xfer = min_t(int, len, host->buf_count);
@@ -848,26 +851,26 @@ static void mrvl_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
host->buf_count -= xfer;
}
-static uint8_t mrvl_nand_read_byte(struct mtd_info *mtd)
+static uint8_t mrvl_nand_read_byte(struct nand_chip *chip)
{
uint8_t ret;
- mrvl_nand_read_buf(mtd, (uint8_t *)&ret, sizeof(ret));
+ mrvl_nand_read_buf(chip, (uint8_t *)&ret, sizeof(ret));
return ret;
}
-static u16 mrvl_nand_read_word(struct mtd_info *mtd)
+static u16 mrvl_nand_read_word(struct nand_chip *chip)
{
u16 ret;
- mrvl_nand_read_buf(mtd, (uint8_t *)&ret, sizeof(ret));
+ mrvl_nand_read_buf(chip, (uint8_t *)&ret, sizeof(ret));
return ret;
}
-static void mrvl_nand_write_buf(struct mtd_info *mtd,
+static void mrvl_nand_write_buf(struct nand_chip *chip,
const uint8_t *buf, int len)
{
- struct mrvl_nand_host *host = mtd_info_to_host(mtd);
+ struct mrvl_nand_host *host = nand_to_host(chip);
memcpy(host->data_buff + host->buf_start, buf, len);
host->buf_start += len;
@@ -1033,9 +1036,9 @@ static int pxa_ecc_init(struct mrvl_nand_host *host,
return 0;
}
-static int mrvl_nand_scan(struct mtd_info *mtd)
+static int mrvl_nand_scan(struct nand_chip *chip)
{
- struct nand_chip *chip = mtd_to_nand(mtd);
+ struct mtd_info *mtd = nand_to_mtd(chip);
struct mrvl_nand_host *host = chip->priv;
int ret;
unsigned int ndcr;
@@ -1053,9 +1056,9 @@ static int mrvl_nand_scan(struct mtd_info *mtd)
nand_readl(host, NDECCCTRL) & ~NDECCCTRL_BCH_EN);
mrvl_nand_set_timing(host, true);
- if (nand_scan_ident(mtd, 1, NULL)) {
+ if (nand_scan_ident(chip, 1, NULL)) {
host->reg_ndcr |= NDCR_DWIDTH_M | NDCR_DWIDTH_C;
- if (nand_scan_ident(mtd, 1, NULL))
+ if (nand_scan_ident(chip, 1, NULL))
return -ENODEV;
}
mrvl_nand_config_flash(host);
@@ -1099,7 +1102,7 @@ static int mrvl_nand_scan(struct mtd_info *mtd)
host->buf_size = mtd->writesize + mtd->oobsize;
host->data_buff = xmalloc(host->buf_size);
- return nand_scan_tail(mtd);
+ return nand_scan_tail(chip);
}
static struct mrvl_nand_host *alloc_nand_resource(struct device_d *dev)
@@ -1214,14 +1217,14 @@ static int mrvl_nand_probe(struct device_d *dev)
return ret;
host->chip.controller = &host->chip.hwcontrol;
- ret = mrvl_nand_scan(&host->chip.mtd);
+ ret = mrvl_nand_scan(&host->chip);
if (ret) {
dev_warn(dev, "failed to scan nand at cs %d\n",
host->cs);
return -ENODEV;
}
- ret = add_mtd_nand_device(&host->chip.mtd, "nand");
+ ret = add_mtd_nand_device(&host->chip, "nand");
return ret;
}
diff --git a/drivers/mtd/nand/nand_mxs.c b/drivers/mtd/nand/nand_mxs.c
index 36b6e7ac22..dd4ea73b78 100644
--- a/drivers/mtd/nand/nand_mxs.c
+++ b/drivers/mtd/nand/nand_mxs.c
@@ -330,9 +330,9 @@ static uint32_t mxs_nand_get_mark_offset(struct mtd_info *mtd)
return block_mark_bit_offset;
}
-static int mxs_nand_calc_geo(struct mtd_info *mtd)
+static int mxs_nand_calc_geo(struct nand_chip *chip)
{
- struct nand_chip *chip = mtd_to_nand(mtd);
+ struct mtd_info *mtd = nand_to_mtd(chip);
struct mxs_nand_info *nand_info = chip->priv;
int ecc_chunk_count = mxs_nand_ecc_chunk_cnt(mtd->writesize);
int gf_len = 13; /* length of Galois Field for non-DDR nand */
@@ -420,9 +420,8 @@ static int mxs_nand_wait_for_bch_complete(struct mxs_nand_info *nand_info)
* ignore the chip enable bit and concentrate only on sending bytes to the NAND
* Flash.
*/
-static void mxs_nand_cmd_ctrl(struct mtd_info *mtd, int data, unsigned int ctrl)
+static void mxs_nand_cmd_ctrl(struct nand_chip *chip, int data, unsigned int ctrl)
{
- struct nand_chip *chip = mtd_to_nand(mtd);
struct mxs_nand_info *nand_info = chip->priv;
struct mxs_dma_desc *d;
uint32_t channel = nand_info->dma_channel_base + nand_info->cur_chip;
@@ -496,9 +495,8 @@ static void mxs_nand_cmd_ctrl(struct mtd_info *mtd, int data, unsigned int ctrl)
/*
* Test if the NAND flash is ready.
*/
-static int mxs_nand_device_ready(struct mtd_info *mtd)
+static int mxs_nand_device_ready(struct nand_chip *chip)
{
- struct nand_chip *chip = mtd_to_nand(mtd);
struct mxs_nand_info *nand_info = chip->priv;
void __iomem *gpmi_regs = nand_info->io_base;
uint32_t tmp;
@@ -522,9 +520,8 @@ static int mxs_nand_device_ready(struct mtd_info *mtd)
/*
* Select the NAND chip.
*/
-static void mxs_nand_select_chip(struct mtd_info *mtd, int chipnum)
+static void mxs_nand_select_chip(struct nand_chip *chip, int chipnum)
{
- struct nand_chip *chip = mtd_to_nand(mtd);
struct mxs_nand_info *nand_info = chip->priv;
nand_info->cur_chip = chipnum;
@@ -537,10 +534,9 @@ static void mxs_nand_select_chip(struct mtd_info *mtd, int chipnum)
* swapping the block mark, or swapping it *back* -- but it doesn't matter
* because the the operation is the same.
*/
-static void mxs_nand_swap_block_mark(struct mtd_info *mtd,
+static void mxs_nand_swap_block_mark(struct nand_chip *chip,
uint8_t *data_buf, uint8_t *oob_buf)
{
- struct nand_chip *chip = mtd_to_nand(mtd);
struct mxs_nand_info *nand_info = chip->priv;
uint32_t bit_offset;
@@ -579,9 +575,8 @@ static void mxs_nand_swap_block_mark(struct mtd_info *mtd,
/*
* Read data from NAND.
*/
-static void mxs_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int length)
+static void mxs_nand_read_buf(struct nand_chip *chip, uint8_t *buf, int length)
{
- struct nand_chip *chip = mtd_to_nand(mtd);
struct mxs_nand_info *nand_info = chip->priv;
struct mxs_dma_desc *d;
uint32_t channel = nand_info->dma_channel_base + nand_info->cur_chip;
@@ -656,10 +651,9 @@ rtn:
/*
* Write data to NAND.
*/
-static void mxs_nand_write_buf(struct mtd_info *mtd, const uint8_t *buf,
+static void mxs_nand_write_buf(struct nand_chip *chip, const uint8_t *buf,
int length)
{
- struct nand_chip *chip = mtd_to_nand(mtd);
struct mxs_nand_info *nand_info = chip->priv;
struct mxs_dma_desc *d;
uint32_t channel = nand_info->dma_channel_base + nand_info->cur_chip;
@@ -707,16 +701,15 @@ static void mxs_nand_write_buf(struct mtd_info *mtd, const uint8_t *buf,
/*
* Read a single byte from NAND.
*/
-static uint8_t mxs_nand_read_byte(struct mtd_info *mtd)
+static uint8_t mxs_nand_read_byte(struct nand_chip *chip)
{
uint8_t buf;
- mxs_nand_read_buf(mtd, &buf, 1);
+ mxs_nand_read_buf(chip, &buf, 1);
return buf;
}
-static void mxs_nand_config_bch(struct mtd_info *mtd, int readlen)
+static void mxs_nand_config_bch(struct nand_chip *chip, int readlen)
{
- struct nand_chip *chip = mtd_to_nand(mtd);
struct mxs_nand_info *nand_info = chip->priv;
int chunk_size;
void __iomem *bch_regs = nand_info->bch_base;
@@ -744,10 +737,11 @@ static void mxs_nand_config_bch(struct mtd_info *mtd, int readlen)
/*
* Read a page from NAND.
*/
-static int __mxs_nand_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
+static int __mxs_nand_ecc_read_page(struct nand_chip *chip,
uint8_t *buf, int oob_required, int page,
int readlen)
{
+ struct mtd_info *mtd = nand_to_mtd(chip);
struct mxs_nand_info *nand_info = chip->priv;
struct mxs_dma_desc *d;
uint32_t channel = nand_info->dma_channel_base + nand_info->cur_chip;
@@ -762,7 +756,7 @@ static int __mxs_nand_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip
readtotal += MXS_NAND_CHUNK_DATA_CHUNK_SIZE * nchunks;
readtotal += DIV_ROUND_UP(13 * chip->ecc.strength * nchunks, 8);
- mxs_nand_config_bch(mtd, readtotal);
+ mxs_nand_config_bch(chip, readtotal);
/* Compile the DMA descriptor - wait for ready. */
d = mxs_nand_get_dma_desc(nand_info);
@@ -850,7 +844,7 @@ static int __mxs_nand_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip
}
/* Read DMA completed, now do the mark swapping. */
- mxs_nand_swap_block_mark(mtd, nand_info->data_buf, nand_info->oob_buf);
+ mxs_nand_swap_block_mark(chip, nand_info->data_buf, nand_info->oob_buf);
memcpy(buf, nand_info->data_buf, readlen);
@@ -931,19 +925,21 @@ static int __mxs_nand_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip
rtn:
mxs_nand_return_dma_descs(nand_info);
- mxs_nand_config_bch(mtd, mtd->writesize + mtd->oobsize);
+ mxs_nand_config_bch(chip, mtd->writesize + mtd->oobsize);
return ret ? ret : max_bitflips;
}
-static int mxs_nand_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
+static int mxs_nand_ecc_read_page(struct nand_chip *chip,
uint8_t *buf, int oob_required, int page)
{
- return __mxs_nand_ecc_read_page(mtd, chip, buf, oob_required, page,
+ struct mtd_info *mtd = nand_to_mtd(chip);
+
+ return __mxs_nand_ecc_read_page(chip, buf, oob_required, page,
mtd->writesize);
}
-static int gpmi_ecc_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
+static int gpmi_ecc_read_subpage(struct nand_chip *chip,
uint32_t offs, uint32_t len, uint8_t *buf, int page)
{
/*
@@ -956,16 +952,16 @@ static int gpmi_ecc_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
offs = 0;
}
- return __mxs_nand_ecc_read_page(mtd, chip, buf, 0, page, len);
+ return __mxs_nand_ecc_read_page(chip, buf, 0, page, len);
}
/*
* Write a page to NAND.
*/
-static int mxs_nand_ecc_write_page(struct mtd_info *mtd,
- struct nand_chip *chip, const uint8_t *buf,
+static int mxs_nand_ecc_write_page(struct nand_chip *chip, const uint8_t *buf,
int oob_required)
{
+ struct mtd_info *mtd = nand_to_mtd(chip);
struct mxs_nand_info *nand_info = chip->priv;
struct mxs_dma_desc *d;
uint32_t channel = nand_info->dma_channel_base + nand_info->cur_chip;
@@ -975,7 +971,7 @@ static int mxs_nand_ecc_write_page(struct mtd_info *mtd,
memcpy(nand_info->oob_buf, chip->oob_poi, mtd->oobsize);
/* Handle block mark swapping. */
- mxs_nand_swap_block_mark(mtd, nand_info->data_buf, nand_info->oob_buf);
+ mxs_nand_swap_block_mark(chip, nand_info->data_buf, nand_info->oob_buf);
/* Compile the DMA descriptor - write data. */
d = mxs_nand_get_dma_desc(nand_info);
@@ -1136,9 +1132,9 @@ static int mxs_nand_hook_block_markbad(struct mtd_info *mtd, loff_t ofs)
* raw_oob_mode field so that, when control finally arrives here, we'll know
* what to do.
*/
-static int mxs_nand_ecc_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
- int page)
+static int mxs_nand_ecc_read_oob(struct nand_chip *chip, int page)
{
+ struct mtd_info *mtd = nand_to_mtd(chip);
struct mxs_nand_info *nand_info = chip->priv;
int column;
@@ -1152,8 +1148,8 @@ static int mxs_nand_ecc_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
* If control arrives here, we're doing a "raw" read. Send the
* command to read the conventional OOB and read it.
*/
- chip->cmdfunc(mtd, NAND_CMD_READ0, mtd->writesize, page);
- chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
+ chip->cmdfunc(chip, NAND_CMD_READ0, mtd->writesize, page);
+ chip->read_buf(chip, chip->oob_poi, mtd->oobsize);
} else {
/*
* If control arrives here, we're not doing a "raw" read. Fill
@@ -1162,8 +1158,8 @@ static int mxs_nand_ecc_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
memset(chip->oob_poi, 0xff, mtd->oobsize);
column = nand_info->version == GPMI_VERSION_TYPE_MX23 ? 0 : mtd->writesize;
- chip->cmdfunc(mtd, NAND_CMD_READ0, column, page);
- mxs_nand_read_buf(mtd, chip->oob_poi, 1);
+ chip->cmdfunc(chip, NAND_CMD_READ0, column, page);
+ mxs_nand_read_buf(chip, chip->oob_poi, 1);
}
return 0;
@@ -1173,9 +1169,9 @@ static int mxs_nand_ecc_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
/*
* Write OOB data to NAND.
*/
-static int mxs_nand_ecc_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
- int page)
+static int mxs_nand_ecc_write_oob(struct nand_chip *chip, int page)
{
+ struct mtd_info *mtd = nand_to_mtd(chip);
struct mxs_nand_info *nand_info = chip->priv;
int column;
uint8_t block_mark = 0;
@@ -1196,12 +1192,12 @@ static int mxs_nand_ecc_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
column = nand_info->version == GPMI_VERSION_TYPE_MX23 ? 0 : mtd->writesize;
/* Write the block mark. */
- chip->cmdfunc(mtd, NAND_CMD_SEQIN, column, page);
- chip->write_buf(mtd, &block_mark, 1);
- chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
+ chip->cmdfunc(chip, NAND_CMD_SEQIN, column, page);
+ chip->write_buf(chip, &block_mark, 1);
+ chip->cmdfunc(chip, NAND_CMD_PAGEPROG, -1, -1);
/* Check if it worked. */
- if (chip->waitfunc(mtd, chip) & NAND_STATUS_FAIL)
+ if (chip->waitfunc(chip) & NAND_STATUS_FAIL)
return -EIO;
return 0;
@@ -1220,7 +1216,7 @@ static int mxs_nand_ecc_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
* Thus, this function is only called when we want *all* blocks to look good,
* so it *always* return success.
*/
-static int mxs_nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
+static int mxs_nand_block_bad(struct nand_chip *chip , loff_t ofs, int getchip)
{
return 0;
}
@@ -1239,9 +1235,9 @@ static int mxs_nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
* call to nand_scan(). Anything other than zero will cause this driver to
* tear everything down and declare failure.
*/
-static int mxs_nand_scan_bbt(struct mtd_info *mtd)
+static int mxs_nand_scan_bbt(struct nand_chip *chip)
{
- struct nand_chip *chip = mtd_to_nand(mtd);
+ struct mtd_info *mtd = nand_to_mtd(chip);
struct mxs_nand_info *nand_info = chip->priv;
void __iomem *bch_regs = nand_info->bch_base;
int ret;
@@ -1252,7 +1248,7 @@ static int mxs_nand_scan_bbt(struct mtd_info *mtd)
if (ret)
return ret;
- mxs_nand_config_bch(mtd, mtd->writesize + mtd->oobsize);
+ mxs_nand_config_bch(chip, mtd->writesize + mtd->oobsize);
/* Set *all* chip selects to use layout 0 */
writel(0, bch_regs + BCH_LAYOUTSELECT);
@@ -1277,7 +1273,7 @@ static int mxs_nand_scan_bbt(struct mtd_info *mtd)
}
/* We use the reference implementation for bad block management. */
- return nand_default_bbt(mtd);
+ return nand_default_bbt(chip);
}
/*
@@ -2029,7 +2025,6 @@ static void mxs_nand_compute_edo_timing(struct mxs_nand_info *info,
static int mxs_nand_enable_edo_mode(struct mxs_nand_info *info)
{
struct nand_chip *chip = &info->nand_chip;
- struct mtd_info *mtd = &chip->mtd;
uint8_t feature[ONFI_SUBFEATURE_PARAM_LEN] = {};
int ret, mode;
@@ -2049,7 +2044,7 @@ static int mxs_nand_enable_edo_mode(struct mxs_nand_info *info)
else
return -EINVAL;
- chip->select_chip(mtd, 0);
+ chip->select_chip(chip, 0);
if (le16_to_cpu(chip->onfi_params.opt_cmd)
& ONFI_OPT_CMD_SET_GET_FEATURES) {
@@ -2057,19 +2052,19 @@ static int mxs_nand_enable_edo_mode(struct mxs_nand_info *info)
/* [1] send SET FEATURE commond to NAND */
feature[0] = mode;
- ret = chip->onfi_set_features(mtd, chip,
+ ret = chip->onfi_set_features(chip,
ONFI_FEATURE_ADDR_TIMING_MODE, feature);
if (ret)
goto err_out;
/* [2] send GET FEATURE command to double-check the timing mode */
- ret = chip->onfi_get_features(mtd, chip,
+ ret = chip->onfi_get_features(chip,
ONFI_FEATURE_ADDR_TIMING_MODE, feature);
if (ret || feature[0] != mode)
goto err_out;
}
- chip->select_chip(mtd, -1);
+ chip->select_chip(chip, -1);
/* [3] set the main IO clock, 100MHz for mode 5, 80MHz for mode 4. */
clk_disable(info->clk);
@@ -2081,7 +2076,7 @@ static int mxs_nand_enable_edo_mode(struct mxs_nand_info *info)
return mode;
err_out:
- chip->select_chip(mtd, -1);
+ chip->select_chip(chip, -1);
return -EINVAL;
}
@@ -2221,11 +2216,11 @@ static int mxs_nand_probe(struct device_d *dev)
chip->ecc.mode = NAND_ECC_HW;
/* first scan to find the device and get the page size */
- err = nand_scan_ident(mtd, 4, NULL);
+ err = nand_scan_ident(chip, 4, NULL);
if (err)
goto err2;
- err = mxs_nand_calc_geo(mtd);
+ err = mxs_nand_calc_geo(chip);
if (err)
goto err2;
@@ -2239,11 +2234,11 @@ static int mxs_nand_probe(struct device_d *dev)
mxs_nand_setup_timing(nand_info);
/* second phase scan */
- err = nand_scan_tail(mtd);
+ err = nand_scan_tail(chip);
if (err)
goto err2;
- err = add_mtd_nand_device(mtd, "nand");
+ err = add_mtd_nand_device(chip, "nand");
if (err)
goto err2;
diff --git a/drivers/mtd/nand/nand_omap_gpmc.c b/drivers/mtd/nand/nand_omap_gpmc.c
index 83fa93b617..b7c35f8899 100644
--- a/drivers/mtd/nand/nand_omap_gpmc.c
+++ b/drivers/mtd/nand/nand_omap_gpmc.c
@@ -141,9 +141,8 @@ static struct nand_bbt_descr bb_descrip_flashbased = {
*
* @return
*/
-static int omap_dev_ready(struct mtd_info *mtd)
+static int omap_dev_ready(struct nand_chip *nand)
{
- struct nand_chip *nand = mtd_to_nand(mtd);
struct gpmc_nand_info *oinfo = (struct gpmc_nand_info *)(nand->priv);
if (readl(oinfo->gpmc_base + GPMC_STATUS) & oinfo->wait_mon_mask)
@@ -187,9 +186,8 @@ static void gpmc_nand_wp(struct gpmc_nand_info *oinfo, int mode)
*
* @return none
*/
-static void omap_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
+static void omap_hwcontrol(struct nand_chip *nand, int cmd, unsigned int ctrl)
{
- struct nand_chip *nand = mtd_to_nand(mtd);
struct gpmc_nand_info *oinfo = (struct gpmc_nand_info *)(nand->priv);
switch (ctrl) {
@@ -231,10 +229,9 @@ static unsigned int gen_true_ecc(u8 *ecc_buf)
((ecc_buf[2] & 0x0F) << 8);
}
-static int __omap_calculate_ecc(struct mtd_info *mtd, uint8_t *ecc_code,
+static int __omap_calculate_ecc(struct nand_chip *nand, uint8_t *ecc_code,
int sblock)
{
- struct nand_chip *nand = mtd_to_nand(mtd);
struct gpmc_nand_info *oinfo = (struct gpmc_nand_info *)(nand->priv);
unsigned int reg, reg1, val;
unsigned int val1 = 0x0, val2 = 0x0;
@@ -324,16 +321,15 @@ static int __omap_calculate_ecc(struct mtd_info *mtd, uint8_t *ecc_code,
return 0;
}
-static int omap_calculate_ecc(struct mtd_info *mtd, const uint8_t *dat,
+static int omap_calculate_ecc(struct nand_chip *nand, const uint8_t *dat,
uint8_t *ecc_code)
{
- return __omap_calculate_ecc(mtd, ecc_code, 0);
+ return __omap_calculate_ecc(nand, ecc_code, 0);
}
-static int omap_correct_bch(struct mtd_info *mtd, uint8_t *dat,
+static int omap_correct_bch(struct nand_chip *nand, uint8_t *dat,
uint8_t *read_ecc, uint8_t *calc_ecc)
{
- struct nand_chip *nand = mtd_to_nand(mtd);
struct gpmc_nand_info *oinfo = (struct gpmc_nand_info *)(nand->priv);
int j, actual_eccsize;
const uint8_t *erased_ecc_vec;
@@ -404,13 +400,12 @@ static int omap_correct_bch(struct mtd_info *mtd, uint8_t *dat,
return bitflip_count;
}
-static int omap_correct_hamming(struct mtd_info *mtd, uint8_t *dat,
+static int omap_correct_hamming(struct nand_chip *nand, uint8_t *dat,
uint8_t *read_ecc, uint8_t *calc_ecc)
{
unsigned int orig_ecc, new_ecc, res, hm;
unsigned short parity_bits, byte;
unsigned char bit;
- struct nand_chip *nand = mtd_to_nand(mtd);
struct gpmc_nand_info *oinfo = (struct gpmc_nand_info *)(nand->priv);
if (read_ecc[0] == 0xff && read_ecc[1] == 0xff &&
@@ -461,23 +456,21 @@ static int omap_correct_hamming(struct mtd_info *mtd, uint8_t *dat,
*
* @return 0 if data is OK or corrected, else returns -1
*/
-static int omap_correct_data(struct mtd_info *mtd, uint8_t *dat,
+static int omap_correct_data(struct nand_chip *nand, uint8_t *dat,
uint8_t *read_ecc, uint8_t *calc_ecc)
{
- struct nand_chip *nand = mtd_to_nand(mtd);
struct gpmc_nand_info *oinfo = (struct gpmc_nand_info *)(nand->priv);
if (oinfo->ecc_mode != OMAP_ECC_HAMMING_CODE_HW_ROMCODE)
return -EINVAL;
- return omap_correct_hamming(mtd, dat, read_ecc, calc_ecc);
+ return omap_correct_hamming(nand, dat, read_ecc, calc_ecc);
return 0;
}
-static void omap_enable_hwecc(struct mtd_info *mtd, int mode)
+static void omap_enable_hwecc(struct nand_chip *nand, int mode)
{
- struct nand_chip *nand = mtd_to_nand(mtd);
struct gpmc_nand_info *oinfo = (struct gpmc_nand_info *)(nand->priv);
unsigned int bch_mod = 0, bch_wrapmode = 0, eccsize1 = 0, eccsize0 = 0;
unsigned int ecc_conf_val = 0, ecc_size_conf_val = 0;
@@ -556,7 +549,7 @@ static void omap_enable_hwecc(struct mtd_info *mtd, int mode)
oinfo->gpmc_base + GPMC_ECC_CONTROL);
}
-static int omap_gpmc_read_buf_manual(struct mtd_info *mtd, struct nand_chip *chip,
+static int omap_gpmc_read_buf_manual(struct nand_chip *chip,
void *buf, int bytes, int result_reg)
{
struct gpmc_nand_info *oinfo = chip->priv;
@@ -568,7 +561,7 @@ static int omap_gpmc_read_buf_manual(struct mtd_info *mtd, struct nand_chip *chi
writel(GPMC_ECC_CONTROL_ECCPOINTER(result_reg),
oinfo->gpmc_base + GPMC_ECC_CONTROL);
- chip->read_buf(mtd, buf, bytes);
+ chip->read_buf(chip, buf, bytes);
return bytes;
}
@@ -579,9 +572,8 @@ static int omap_gpmc_read_buf_manual(struct mtd_info *mtd, struct nand_chip *chi
* @buf: buffer to store date
* @len: number of bytes to read
*/
-static void omap_read_buf_pref(struct mtd_info *mtd, u_char *buf, int len)
+static void omap_read_buf_pref(struct nand_chip *nand_chip, u_char *buf, int len)
{
- struct nand_chip *nand_chip = mtd_to_nand(mtd);
struct gpmc_nand_info *info = nand_chip->priv;
u32 r_count = 0;
u32 *p = (u32 *)buf;
@@ -619,10 +611,9 @@ static void omap_read_buf_pref(struct mtd_info *mtd, u_char *buf, int len)
* @buf: data buffer
* @len: number of bytes to write
*/
-static void omap_write_buf_pref(struct mtd_info *mtd,
+static void omap_write_buf_pref(struct nand_chip *nand_chip,
const u_char *buf, int len)
{
- struct nand_chip *nand_chip = mtd_to_nand(mtd);
struct gpmc_nand_info *info = nand_chip->priv;
u32 w_count = 0;
@@ -684,9 +675,10 @@ static void omap_write_buf_pref(struct mtd_info *mtd,
* generation), so we use the otherwise unused ECC_RESULTx_5 to
* generate dummy eccs for the unprotected oob area.
*/
-static int omap_gpmc_read_page_bch_rom_mode(struct mtd_info *mtd,
- struct nand_chip *chip, uint8_t *buf, int oob_required, int page)
+static int omap_gpmc_read_page_bch_rom_mode(struct nand_chip *chip, uint8_t *buf,
+ int oob_required, int page)
{
+ struct mtd_info *mtd = nand_to_mtd(chip);
struct gpmc_nand_info *oinfo = chip->priv;
int dev_width = chip->options & NAND_BUSWIDTH_16 ? GPMC_ECC_CONFIG_ECC16B : 0;
uint8_t *p = buf;
@@ -720,18 +712,18 @@ static int omap_gpmc_read_page_bch_rom_mode(struct mtd_info *mtd,
oinfo->gpmc_base + GPMC_ECC_CONTROL);
for (i = 0; i < 32; i++)
- p += omap_gpmc_read_buf_manual(mtd, chip, p, 64, (i >> 3) + 1);
+ p += omap_gpmc_read_buf_manual(chip, p, 64, (i >> 3) + 1);
p = chip->oob_poi;
- p += omap_gpmc_read_buf_manual(mtd, chip, p, 2, 5);
+ p += omap_gpmc_read_buf_manual(chip, p, 2, 5);
for (i = 0; i < 4; i++) {
- p += omap_gpmc_read_buf_manual(mtd, chip, p, 13, i + 1);
- p += omap_gpmc_read_buf_manual(mtd, chip, p, 1, 5);
+ p += omap_gpmc_read_buf_manual(chip, p, 13, i + 1);
+ p += omap_gpmc_read_buf_manual(chip, p, 1, 5);
}
- p += omap_gpmc_read_buf_manual(mtd, chip, p, 6, 5);
+ p += omap_gpmc_read_buf_manual(chip, p, 6, 5);
for (i = 0; i < chip->ecc.total; i++)
ecc_code[i] = chip->oob_poi[eccpos[i]];
@@ -739,8 +731,8 @@ static int omap_gpmc_read_page_bch_rom_mode(struct mtd_info *mtd,
p = buf;
for (i = 0, j = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize, j++) {
- __omap_calculate_ecc(mtd, &ecc_calc[i - j], j + 1);
- stat = omap_correct_bch(mtd, p, &ecc_code[i], &ecc_calc[i - j]);
+ __omap_calculate_ecc(chip, &ecc_calc[i - j], j + 1);
+ stat = omap_correct_bch(chip, p, &ecc_code[i], &ecc_calc[i - j]);
if (stat < 0) {
mtd->ecc_stats.failed++;
} else {
@@ -954,10 +946,10 @@ static int omap_elm_correct_data(struct nand_chip *chip, u_char *data,
return (err) ? err : stat;
}
-static int gpmc_read_page_hwecc_elm(struct mtd_info *mtd,
- struct nand_chip *chip, uint8_t *buf,
+static int gpmc_read_page_hwecc_elm(struct nand_chip *chip, uint8_t *buf,
int oob_required, int page)
{
+ struct mtd_info *mtd = nand_to_mtd(chip);
int i;
int eccbytes = chip->ecc.bytes;
int eccsteps = chip->ecc.steps;
@@ -965,9 +957,9 @@ static int gpmc_read_page_hwecc_elm(struct mtd_info *mtd,
uint8_t *ecc_code = chip->buffers->ecccode;
uint32_t *eccpos = chip->ecc.layout->eccpos;
- chip->ecc.hwctl(mtd, NAND_ECC_READ);
- chip->read_buf(mtd, buf, mtd->writesize);
- chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
+ chip->ecc.hwctl(chip, NAND_ECC_READ);
+ chip->read_buf(chip, buf, mtd->writesize);
+ chip->read_buf(chip, chip->oob_poi, mtd->oobsize);
for (i = 0; i < chip->ecc.total; i++)
ecc_code[i] = chip->oob_poi[eccpos[i]];
@@ -975,15 +967,15 @@ static int gpmc_read_page_hwecc_elm(struct mtd_info *mtd,
eccsteps = chip->ecc.steps;
for (i = 0; eccsteps; eccsteps--, i++)
- __omap_calculate_ecc(mtd, &ecc_calc[i * eccbytes], i);
+ __omap_calculate_ecc(chip, &ecc_calc[i * eccbytes], i);
return omap_elm_correct_data(chip, buf, ecc_code, ecc_calc);
}
-static int gpmc_read_page_hwecc(struct mtd_info *mtd,
- struct nand_chip *chip, uint8_t *buf,
+static int gpmc_read_page_hwecc(struct nand_chip *chip, uint8_t *buf,
int oob_required, int page)
{
+ struct mtd_info *mtd = nand_to_mtd(chip);
int i, eccsize = chip->ecc.size;
int eccbytes = chip->ecc.bytes;
int eccsteps = chip->ecc.steps;
@@ -993,9 +985,9 @@ static int gpmc_read_page_hwecc(struct mtd_info *mtd,
uint32_t *eccpos = chip->ecc.layout->eccpos;
unsigned int max_bitflips = 0;
- chip->ecc.hwctl(mtd, NAND_ECC_READ);
- chip->read_buf(mtd, p, mtd->writesize);
- chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
+ chip->ecc.hwctl(chip, NAND_ECC_READ);
+ chip->read_buf(chip, p, mtd->writesize);
+ chip->read_buf(chip, chip->oob_poi, mtd->oobsize);
for (i = 0; i < chip->ecc.total; i++)
ecc_code[i] = chip->oob_poi[eccpos[i]];
@@ -1006,8 +998,8 @@ static int gpmc_read_page_hwecc(struct mtd_info *mtd,
for (i = 0 ; eccsteps; eccsteps--, i++, p += eccsize) {
int stat;
- __omap_calculate_ecc(mtd, &ecc_calc[i * eccbytes], i);
- stat = omap_correct_bch(mtd, p, &ecc_code[i * eccbytes], &ecc_calc[i * eccbytes]);
+ __omap_calculate_ecc(chip, &ecc_calc[i * eccbytes], i);
+ stat = omap_correct_bch(chip, p, &ecc_code[i * eccbytes], &ecc_calc[i * eccbytes]);
if (stat < 0) {
mtd->ecc_stats.failed++;
} else {
@@ -1136,7 +1128,7 @@ static int omap_gpmc_eccmode(struct gpmc_nand_info *oinfo,
oinfo->ecc_mode = mode;
/* second phase scan */
- if (nand_scan_tail(minfo))
+ if (nand_scan_tail(nand))
return -ENXIO;
nand->options |= NAND_SKIP_BBTSCAN;
@@ -1296,7 +1288,7 @@ static int gpmc_nand_probe(struct device_d *pdev)
mdelay(1);
/* first scan to find the device and get the page size */
- if (nand_scan_ident(minfo, 1, NULL)) {
+ if (nand_scan_ident(nand, 1, NULL)) {
err = -ENXIO;
goto out_release_mem;
}
@@ -1318,7 +1310,7 @@ static int gpmc_nand_probe(struct device_d *pdev)
omap_gpmc_eccmode(oinfo, oinfo->ecc_mode);
/* We are all set to register with the system now! */
- err = add_mtd_nand_device(minfo, "nand");
+ err = add_mtd_nand_device(nand, "nand");
if (err) {
dev_dbg(pdev, "device registration failed\n");
goto out_release_mem;
diff --git a/drivers/mtd/nand/nand_orion.c b/drivers/mtd/nand/nand_orion.c
index c8b89cd03c..ac1c8442c0 100644
--- a/drivers/mtd/nand/nand_orion.c
+++ b/drivers/mtd/nand/nand_orion.c
@@ -27,9 +27,8 @@ struct orion_nand {
u8 cle; /* address line number connected to CLE */
};
-static void orion_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
+static void orion_nand_cmd_ctrl(struct nand_chip *chip, int cmd, unsigned int ctrl)
{
- struct nand_chip *chip = mtd_to_nand(mtd);
struct orion_nand *priv = chip->priv;
u32 offs;
@@ -49,9 +48,8 @@ static void orion_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl
writeb(cmd, chip->IO_ADDR_W + offs);
}
-static void orion_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+static void orion_nand_read_buf(struct nand_chip *chip, uint8_t *buf, int len)
{
- struct nand_chip *chip = mtd_to_nand(mtd);
void __iomem *io_base = chip->IO_ADDR_R;
uint64_t *buf64;
int i = 0;
@@ -132,12 +130,12 @@ static int orion_nand_probe(struct device_d *dev)
if (!IS_ERR(clk))
clk_enable(clk);
- if (nand_scan(mtd, 1)) {
+ if (nand_scan(chip, 1)) {
ret = -ENXIO;
goto no_dev;
}
- add_mtd_nand_device(mtd, "orion_nand");
+ add_mtd_nand_device(chip, "orion_nand");
return 0;
no_dev:
if (!IS_ERR(clk))
diff --git a/drivers/mtd/nand/nand_s3c24xx.c b/drivers/mtd/nand/nand_s3c24xx.c
index 9fbc0f90d3..9df7f6cba2 100644
--- a/drivers/mtd/nand/nand_s3c24xx.c
+++ b/drivers/mtd/nand/nand_s3c24xx.c
@@ -200,10 +200,9 @@ static void __nand_boot_init disable_nand_controller(void __iomem *host)
*
* This is a special block read variant for the S3C2440 CPU.
*/
-static void s3c2440_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+static void s3c2440_nand_read_buf(struct nand_chip *chip, uint8_t *buf, int len)
{
- struct nand_chip *nand_chip = mtd_to_nand(mtd);
- struct s3c24x0_nand_host *host = nand_chip->priv;
+ struct s3c24x0_nand_host *host = chip->priv;
readsl(host->base + NFDATA, buf, len >> 2);
@@ -224,11 +223,10 @@ static void s3c2440_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
*
* This is a special block write variant for the S3C2440 CPU.
*/
-static void s3c2440_nand_write_buf(struct mtd_info *mtd, const uint8_t *buf,
+static void s3c2440_nand_write_buf(struct nand_chip *chip, const uint8_t *buf,
int len)
{
- struct nand_chip *nand_chip = mtd_to_nand(mtd);
- struct s3c24x0_nand_host *host = nand_chip->priv;
+ struct s3c24x0_nand_host *host = chip->priv;
writesl(host->base + NFDATA, buf, len >> 2);
@@ -252,7 +250,7 @@ static void s3c2440_nand_write_buf(struct mtd_info *mtd, const uint8_t *buf,
*
* @note: This routine works always on a 24 bit ECC
*/
-static int s3c2410_nand_correct_data(struct mtd_info *mtd, uint8_t *dat,
+static int s3c2410_nand_correct_data(struct nand_chip *chip, uint8_t *dat,
uint8_t *read_ecc, uint8_t *calc_ecc)
{
unsigned int diff0, diff1, diff2;
@@ -313,10 +311,9 @@ static int s3c2410_nand_correct_data(struct mtd_info *mtd, uint8_t *dat,
return -1;
}
-static void s3c2410_nand_enable_hwecc(struct mtd_info *mtd, int mode)
+static void s3c2410_nand_enable_hwecc(struct nand_chip *chip, int mode)
{
- struct nand_chip *nand_chip = mtd_to_nand(mtd);
- struct s3c24x0_nand_host *host = nand_chip->priv;
+ struct s3c24x0_nand_host *host = chip->priv;
#ifdef CONFIG_CPU_S3C2410
writel(readl(host->base + NFCONF) | NFCONF_INITECC , host->base + NFCONF);
@@ -326,10 +323,9 @@ static void s3c2410_nand_enable_hwecc(struct mtd_info *mtd, int mode)
#endif
}
-static int s3c2410_nand_calculate_ecc(struct mtd_info *mtd, const uint8_t *dat, uint8_t *ecc_code)
+static int s3c2410_nand_calculate_ecc(struct nand_chip *chip, const uint8_t *dat, uint8_t *ecc_code)
{
- struct nand_chip *nand_chip = mtd_to_nand(mtd);
- struct s3c24x0_nand_host *host = nand_chip->priv;
+ struct s3c24x0_nand_host *host = chip->priv;
#ifdef CONFIG_CPU_S3C2410
ecc_code[0] = readb(host->base + NFECC);
@@ -346,30 +342,27 @@ static int s3c2410_nand_calculate_ecc(struct mtd_info *mtd, const uint8_t *dat,
return 0;
}
-static void s3c24x0_nand_select_chip(struct mtd_info *mtd, int chip)
+static void s3c24x0_nand_select_chip(struct nand_chip *chip, int num)
{
- struct nand_chip *nand_chip = mtd_to_nand(mtd);
- struct s3c24x0_nand_host *host = nand_chip->priv;
+ struct s3c24x0_nand_host *host = chip->priv;
- if (chip == -1)
+ if (num == -1)
disable_cs(host->base);
else
enable_cs(host->base);
}
-static int s3c24x0_nand_devready(struct mtd_info *mtd)
+static int s3c24x0_nand_devready(struct nand_chip *chip)
{
- struct nand_chip *nand_chip = mtd_to_nand(mtd);
- struct s3c24x0_nand_host *host = nand_chip->priv;
+ struct s3c24x0_nand_host *host = chip->priv;
return readw(host->base + NFSTAT) & NFSTAT_BUSY;
}
-static void s3c24x0_nand_hwcontrol(struct mtd_info *mtd, int cmd,
+static void s3c24x0_nand_hwcontrol(struct nand_chip *chip, int cmd,
unsigned int ctrl)
{
- struct nand_chip *nand_chip = mtd_to_nand(mtd);
- struct s3c24x0_nand_host *host = nand_chip->priv;
+ struct s3c24x0_nand_host *host = chip->priv;
if (cmd == NAND_CMD_NONE)
return;
@@ -482,13 +475,13 @@ static int s3c24x0_nand_probe(struct device_d *dev)
goto on_error;
/* Scan to find existence of the device */
- ret = nand_scan(mtd, 1);
+ ret = nand_scan(chip, 1);
if (ret != 0) {
ret = -ENXIO;
goto on_error;
}
- return add_mtd_nand_device(mtd, "nand");
+ return add_mtd_nand_device(chip, "nand");
on_error:
free(host);
diff --git a/drivers/mtd/nand/nomadik_nand.c b/drivers/mtd/nand/nomadik_nand.c
index 31bc2efc40..9918548357 100644
--- a/drivers/mtd/nand/nomadik_nand.c
+++ b/drivers/mtd/nand/nomadik_nand.c
@@ -58,7 +58,7 @@ static inline int parity(int b) /* uses low 8 bits: returns 0 or all-1 */
* I haven't managed to get the desired data out of it; so do it in sw.
* There is problably some errata involved, but currently miss the info.
*/
-static int nomadik_ecc512_calc(struct mtd_info *mtd, const u_char *data,
+static int nomadik_ecc512_calc(struct nand_chip *nand, const u_char *data,
u_char *ecc)
{
int gpar = 0;
@@ -101,10 +101,9 @@ static int nomadik_ecc512_calc(struct mtd_info *mtd, const u_char *data,
return 0;
}
-static int nomadik_ecc512_correct(struct mtd_info *mtd, uint8_t *dat,
+static int nomadik_ecc512_correct(struct nand_chip *chip, uint8_t *dat,
uint8_t *r_ecc, uint8_t *c_ecc)
{
- struct nand_chip *chip = mtd_to_nand(mtd);
uint32_t r, c, d, diff; /*read, calculated, xor of them */
if (!memcmp(r_ecc, c_ecc, chip->ecc.bytes))
@@ -149,14 +148,13 @@ static struct nand_ecclayout nomadik_ecc_layout = {
.oobfree = { {0x08, 0x08}, {0x18, 0x08}, {0x28, 0x08}, {0x38, 0x08} },
};
-static void nomadik_ecc_control(struct mtd_info *mtd, int mode)
+static void nomadik_ecc_control(struct nand_chip *nand, int mode)
{
/* No need to enable hw ecc, it's on by default */
}
-static void nomadik_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
+static void nomadik_cmd_ctrl(struct nand_chip *nand, int cmd, unsigned int ctrl)
{
- struct nand_chip *nand = mtd_to_nand(mtd);
struct nomadik_nand_host *host = nand->priv;
if (cmd == NAND_CMD_NONE)
@@ -221,13 +219,13 @@ static int nomadik_nand_probe(struct device_d *dev)
/*
* Scan to find existance of the device
*/
- if (nand_scan(mtd, 1)) {
+ if (nand_scan(nand, 1)) {
ret = -ENXIO;
goto err;
}
pr_info("Registering %s as whole device\n", mtd->name);
- add_mtd_nand_device(mtd, "nand");
+ add_mtd_nand_device(nand, "nand");
return 0;
diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
index a8a22210ca..e23b3446a7 100644
--- a/include/linux/mtd/nand.h
+++ b/include/linux/mtd/nand.h
@@ -23,28 +23,29 @@
#include <linux/mtd/bbm.h>
struct mtd_info;
+struct nand_chip;
struct nand_flash_dev;
/* Scan and identify a NAND device */
-extern int nand_scan(struct mtd_info *mtd, int max_chips);
+extern int nand_scan(struct nand_chip *chip, int max_chips);
/*
* Separate phases of nand_scan(), allowing board driver to intervene
* and override command or ECC setup according to flash type.
*/
-extern int nand_scan_ident(struct mtd_info *mtd, int max_chips,
+extern int nand_scan_ident(struct nand_chip *chip, int max_chips,
struct nand_flash_dev *table);
-extern int nand_scan_tail(struct mtd_info *mtd);
+extern int nand_scan_tail(struct nand_chip *chip);
/* Free resources held by the NAND device */
-extern void nand_release(struct mtd_info *mtd);
+extern void nand_release(struct nand_chip *chip);
/* Internal helper for board drivers which need to override command function */
-extern void nand_wait_ready(struct mtd_info *mtd);
+extern void nand_wait_ready(struct nand_chip *chip);
/* locks all blocks present in the device */
-extern int nand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
+extern int nand_lock(struct nand_chip *chip, loff_t ofs, uint64_t len);
/* unlocks specified locked blocks */
-extern int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
+extern int nand_unlock(struct nand_chip *chip, loff_t ofs, uint64_t len);
extern int nand_check_erased_ecc_chunk(void *data, int datalen,
void *ecc, int ecclen,
@@ -346,31 +347,28 @@ struct nand_ecc_ctrl {
int postpad;
struct nand_ecclayout *layout;
void *priv;
- void (*hwctl)(struct mtd_info *mtd, int mode);
- int (*calculate)(struct mtd_info *mtd, const uint8_t *dat,
+ void (*hwctl)(struct nand_chip *chip, int mode);
+ int (*calculate)(struct nand_chip *chip, const uint8_t *dat,
uint8_t *ecc_code);
- int (*correct)(struct mtd_info *mtd, uint8_t *dat, uint8_t *read_ecc,
+ int (*correct)(struct nand_chip *chip, uint8_t *dat, uint8_t *read_ecc,
uint8_t *calc_ecc);
- int (*read_page_raw)(struct mtd_info *mtd, struct nand_chip *chip,
+ int (*read_page_raw)(struct nand_chip *chip,
uint8_t *buf, int oob_required, int page);
- int (*write_page_raw)(struct mtd_info *mtd, struct nand_chip *chip,
+ int (*write_page_raw)(struct nand_chip *chip,
const uint8_t *buf, int oob_required);
- int (*read_page)(struct mtd_info *mtd, struct nand_chip *chip,
+ int (*read_page)(struct nand_chip *chip,
uint8_t *buf, int oob_required, int page);
- int (*read_subpage)(struct mtd_info *mtd, struct nand_chip *chip,
+ int (*read_subpage)(struct nand_chip *chip,
uint32_t offs, uint32_t len, uint8_t *buf, int page);
- int (*write_subpage)(struct mtd_info *mtd, struct nand_chip *chip,
+ int (*write_subpage)(struct nand_chip *chip,
uint32_t offset, uint32_t data_len,
const uint8_t *data_buf, int oob_required);
- int (*write_page)(struct mtd_info *mtd, struct nand_chip *chip,
+ int (*write_page)(struct nand_chip *chip,
const uint8_t *buf, int oob_required);
- int (*write_oob_raw)(struct mtd_info *mtd, struct nand_chip *chip,
- int page);
- int (*read_oob_raw)(struct mtd_info *mtd, struct nand_chip *chip,
- int page);
- int (*read_oob)(struct mtd_info *mtd, struct nand_chip *chip, int page);
- int (*write_oob)(struct mtd_info *mtd, struct nand_chip *chip,
- int page);
+ int (*write_oob_raw)(struct nand_chip *chip, int page);
+ int (*read_oob_raw)(struct nand_chip *chip, int page);
+ int (*read_oob)(struct nand_chip *chip, int page);
+ int (*write_oob)(struct nand_chip *chip, int page);
};
/**
@@ -473,27 +471,25 @@ struct nand_chip {
void __iomem *IO_ADDR_R;
void __iomem *IO_ADDR_W;
- uint8_t (*read_byte)(struct mtd_info *mtd);
- u16 (*read_word)(struct mtd_info *mtd);
- void (*write_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);
- void (*read_buf)(struct mtd_info *mtd, uint8_t *buf, int len);
- void (*select_chip)(struct mtd_info *mtd, int chip);
- int (*block_bad)(struct mtd_info *mtd, loff_t ofs, int getchip);
- int (*block_markbad)(struct mtd_info *mtd, loff_t ofs);
- void (*cmd_ctrl)(struct mtd_info *mtd, int dat, unsigned int ctrl);
- int (*init_size)(struct mtd_info *mtd, struct nand_chip *this,
- u8 *id_data);
- int (*dev_ready)(struct mtd_info *mtd);
- void (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column,
+ uint8_t (*read_byte)(struct nand_chip *chip);
+ u16 (*read_word)(struct nand_chip *chip);
+ void (*write_buf)(struct nand_chip *chip, const uint8_t *buf, int len);
+ void (*read_buf)(struct nand_chip *chip, uint8_t *buf, int len);
+ void (*select_chip)(struct nand_chip *_chip, int chip);
+ int (*block_bad)(struct nand_chip *chip, loff_t ofs, int getchip);
+ int (*block_markbad)(struct nand_chip *chip, loff_t ofs);
+ void (*cmd_ctrl)(struct nand_chip *chip, int dat, unsigned int ctrl);
+ int (*dev_ready)(struct nand_chip *chip);
+ void (*cmdfunc)(struct nand_chip *chip, unsigned command, int column,
int page_addr);
- int(*waitfunc)(struct mtd_info *mtd, struct nand_chip *this);
- int (*scan_bbt)(struct mtd_info *mtd);
- int (*write_page)(struct mtd_info *mtd, struct nand_chip *chip,
+ int(*waitfunc)(struct nand_chip *chip);
+ int (*scan_bbt)(struct nand_chip *chip);
+ int (*write_page)(struct nand_chip *chip,
uint32_t offset, int data_len, const uint8_t *buf,
int oob_required, int page, int cached, int raw);
- int (*onfi_set_features)(struct mtd_info *mtd, struct nand_chip *chip,
+ int (*onfi_set_features)(struct nand_chip *chip,
int feature_addr, uint8_t *subfeature_para);
- int (*onfi_get_features)(struct mtd_info *mtd, struct nand_chip *chip,
+ int (*onfi_get_features)(struct nand_chip *chip,
int feature_addr, uint8_t *subfeature_para);
int chip_delay;
@@ -629,16 +625,16 @@ struct nand_manufacturers {
extern struct nand_flash_dev nand_flash_ids[];
extern struct nand_manufacturers nand_manuf_ids[];
-extern int nand_update_bbt(struct mtd_info *mtd, loff_t offs);
-extern int nand_default_bbt(struct mtd_info *mtd);
-extern int nand_markbad_bbt(struct mtd_info *mtd, loff_t offs);
-extern int nand_markgood_bbt(struct mtd_info *mtd, loff_t offs);
-extern int nand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt);
-extern int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
+extern int nand_update_bbt(struct nand_chip *chip, loff_t offs);
+extern int nand_default_bbt(struct nand_chip *chip);
+extern int nand_markbad_bbt(struct nand_chip *chip, loff_t offs);
+extern int nand_markgood_bbt(struct nand_chip *chip, loff_t offs);
+extern int nand_isbad_bbt(struct nand_chip *chip, loff_t offs, int allowbbt);
+extern int nand_erase_nand(struct nand_chip *chip, struct erase_info *instr,
int allowbbt);
-extern int nand_do_read(struct mtd_info *mtd, loff_t from, size_t len,
+extern int nand_do_read(struct nand_chip *chip, loff_t from, size_t len,
size_t *retlen, uint8_t *buf);
-extern int add_mtd_nand_device(struct mtd_info *mtd, char *devname);
+extern int add_mtd_nand_device(struct nand_chip *chip, char *devname);
/**
* struct platform_nand_chip - chip level device structure
@@ -798,4 +794,9 @@ static inline struct nand_chip *mtd_to_nand(struct mtd_info *mtd)
return container_of(mtd, struct nand_chip, mtd);
}
+static inline struct mtd_info *nand_to_mtd(struct nand_chip *chip)
+{
+ return &chip->mtd;
+}
+
#endif /* __LINUX_MTD_NAND_H */
diff --git a/include/linux/mtd/nand_bch.h b/include/linux/mtd/nand_bch.h
index 5465ddd132..eee80a558c 100644
--- a/include/linux/mtd/nand_bch.h
+++ b/include/linux/mtd/nand_bch.h
@@ -11,7 +11,7 @@
#ifndef __MTD_NAND_BCH_H__
#define __MTD_NAND_BCH_H__
-struct mtd_info;
+struct nand_chip;
struct nand_bch_control;
#if defined(CONFIG_NAND_ECC_BCH)
@@ -21,18 +21,18 @@ static inline int mtd_nand_has_bch(void) { return 1; }
/*
* Calculate BCH ecc code
*/
-int nand_bch_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
+int nand_bch_calculate_ecc(struct nand_chip *chip, const u_char *dat,
u_char *ecc_code);
/*
* Detect and correct bit errors
*/
-int nand_bch_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc,
+int nand_bch_correct_data(struct nand_chip *chip, u_char *dat, u_char *read_ecc,
u_char *calc_ecc);
/*
* Initialize BCH encoder/decoder
*/
-struct nand_bch_control *nand_bch_init(struct mtd_info *mtd);
+struct nand_bch_control *nand_bch_init(struct nand_chip *chip);
/*
* Release BCH encoder/decoder resources
*/
@@ -43,20 +43,20 @@ void nand_bch_free(struct nand_bch_control *nbc);
static inline int mtd_nand_has_bch(void) { return 0; }
static inline int
-nand_bch_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
+nand_bch_calculate_ecc(struct nand_chip *chip, const u_char *dat,
u_char *ecc_code)
{
return -1;
}
static inline int
-nand_bch_correct_data(struct mtd_info *mtd, unsigned char *buf,
+nand_bch_correct_data(struct nand_chip *chip, unsigned char *buf,
unsigned char *read_ecc, unsigned char *calc_ecc)
{
return -1;
}
-static inline struct nand_bch_control *nand_bch_init(struct mtd_info *mtd)
+static inline struct nand_bch_control *nand_bch_init(struct nand_chip *chip)
{
return NULL;
}
diff --git a/include/linux/mtd/nand_ecc.h b/include/linux/mtd/nand_ecc.h
index 12c5bc342e..1dc779581f 100644
--- a/include/linux/mtd/nand_ecc.h
+++ b/include/linux/mtd/nand_ecc.h
@@ -15,16 +15,16 @@
#ifndef __MTD_NAND_ECC_H__
#define __MTD_NAND_ECC_H__
-struct mtd_info;
+struct nand_chip;
/*
* Calculate 3 byte ECC code for 256 byte block
*/
-int nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code);
+int nand_calculate_ecc(struct nand_chip *chip, const u_char *dat, u_char *ecc_code);
/*
* Detect and correct a 1 bit error for 256 byte block
*/
-int nand_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc);
+int nand_correct_data(struct nand_chip *chip, u_char *dat, u_char *read_ecc, u_char *calc_ecc);
#endif /* __MTD_NAND_ECC_H__ */
--
2.20.1
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 27+ messages in thread
* [PATCH 06/26] mtd: Add underscore prefix to mtd hooks
2020-11-06 13:38 [PATCH 00/26] Update nand layer to Linux-5.9 Sascha Hauer
` (4 preceding siblings ...)
2020-11-06 13:38 ` [PATCH 05/26] mtd: nand: Pass struct nand_chip around Sascha Hauer
@ 2020-11-06 13:38 ` Sascha Hauer
2020-11-06 13:38 ` [PATCH 07/26] mtd: Use classdev->parent Sascha Hauer
` (19 subsequent siblings)
25 siblings, 0 replies; 27+ messages in thread
From: Sascha Hauer @ 2020-11-06 13:38 UTC (permalink / raw)
To: Barebox List
In the Kernel the mtd function hooks begin with an underscore. Do the
same in barebox to be better comparable to the Kernel.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
drivers/mtd/core.c | 36 +++++++++++-----------
drivers/mtd/devices/docg3.c | 12 ++++----
drivers/mtd/devices/mtd_dataflash.c | 6 ++--
drivers/mtd/devices/mtdram.c | 6 ++--
drivers/mtd/mtdconcat.c | 34 ++++++++++-----------
drivers/mtd/nand/nand_base.c | 22 +++++++-------
drivers/mtd/nand/nand_mxs.c | 18 +++++------
drivers/mtd/nor/cfi_flash.c | 10 +++----
drivers/mtd/partition.c | 46 ++++++++++++++---------------
drivers/mtd/spi-nor/spi-nor.c | 12 ++++----
include/linux/mtd/mtd.h | 46 ++++++++++++++---------------
11 files changed, 124 insertions(+), 124 deletions(-)
diff --git a/drivers/mtd/core.c b/drivers/mtd/core.c
index 13f3d61f00..71c602dbb7 100644
--- a/drivers/mtd/core.c
+++ b/drivers/mtd/core.c
@@ -199,7 +199,7 @@ static int mtd_op_erase(struct cdev *cdev, loff_t count, loff_t offset)
erase.mtd = mtd;
addr = offset;
- if (!mtd->block_isbad) {
+ if (!mtd->_block_isbad) {
erase.addr = addr;
erase.len = count;
return mtd_erase(mtd, &erase);
@@ -236,7 +236,7 @@ static int mtd_op_protect(struct cdev *cdev, size_t count, loff_t offset, int pr
{
struct mtd_info *mtd = cdev->priv;
- if (!mtd->unlock || !mtd->lock)
+ if (!mtd->_unlock || !mtd->_lock)
return -ENOSYS;
if (prot)
@@ -318,35 +318,35 @@ int mtd_ioctl(struct cdev *cdev, int request, void *buf)
int mtd_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
{
- if (!mtd->lock)
+ if (!mtd->_lock)
return -EOPNOTSUPP;
if (ofs < 0 || ofs > mtd->size || len > mtd->size - ofs)
return -EINVAL;
if (!len)
return 0;
- return mtd->lock(mtd, ofs, len);
+ return mtd->_lock(mtd, ofs, len);
}
int mtd_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
{
- if (!mtd->unlock)
+ if (!mtd->_unlock)
return -EOPNOTSUPP;
if (ofs < 0 || ofs > mtd->size || len > mtd->size - ofs)
return -EINVAL;
if (!len)
return 0;
- return mtd->unlock(mtd, ofs, len);
+ return mtd->_unlock(mtd, ofs, len);
}
int mtd_block_isbad(struct mtd_info *mtd, loff_t ofs)
{
- if (!mtd->block_isbad)
+ if (!mtd->_block_isbad)
return 0;
if (ofs < 0 || ofs > mtd->size)
return -EINVAL;
- return mtd->block_isbad(mtd, ofs);
+ return mtd->_block_isbad(mtd, ofs);
}
int mtd_block_markbad(struct mtd_info *mtd, loff_t ofs)
@@ -356,8 +356,8 @@ int mtd_block_markbad(struct mtd_info *mtd, loff_t ofs)
if (ofs < 0 || ofs >= mtd->size)
return -EINVAL;
- if (mtd->block_markbad)
- ret = mtd->block_markbad(mtd, ofs);
+ if (mtd->_block_markbad)
+ ret = mtd->_block_markbad(mtd, ofs);
else
ret = -ENOSYS;
@@ -368,8 +368,8 @@ int mtd_block_markgood(struct mtd_info *mtd, loff_t ofs)
{
int ret;
- if (mtd->block_markgood)
- ret = mtd->block_markgood(mtd, ofs);
+ if (mtd->_block_markgood)
+ ret = mtd->_block_markgood(mtd, ofs);
else
ret = -ENOSYS;
@@ -392,7 +392,7 @@ int mtd_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen,
* representing the maximum number of bitflips that were corrected on
* any one ecc region (if applicable; zero otherwise).
*/
- ret_code = mtd->read(mtd, from, len, retlen, buf);
+ ret_code = mtd->_read(mtd, from, len, retlen, buf);
if (unlikely(ret_code < 0))
return ret_code;
if (mtd->ecc_strength == 0)
@@ -407,12 +407,12 @@ int mtd_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen,
if (to < 0 || to >= mtd->size || len > mtd->size - to)
return -EINVAL;
- if (!mtd->write || !(mtd->flags & MTD_WRITEABLE))
+ if (!mtd->_write || !(mtd->flags & MTD_WRITEABLE))
return -EROFS;
if (!len)
return 0;
- return mtd->write(mtd, to, len, retlen, buf);
+ return mtd->_write(mtd, to, len, retlen, buf);
}
int mtd_erase(struct mtd_info *mtd, struct erase_info *instr)
@@ -425,7 +425,7 @@ int mtd_erase(struct mtd_info *mtd, struct erase_info *instr)
if (!instr->len)
return 0;
- return mtd->erase(mtd, instr);
+ return mtd->_erase(mtd, instr);
}
int mtd_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops)
@@ -433,7 +433,7 @@ int mtd_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops)
int ret_code;
ops->retlen = ops->oobretlen = 0;
- if (!mtd->read_oob)
+ if (!mtd->_read_oob)
return -EOPNOTSUPP;
/*
* In cases where ops->datbuf != NULL, mtd->_read_oob() has semantics
@@ -441,7 +441,7 @@ int mtd_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops)
* representing max bitflips. In other cases, mtd->_read_oob() may
* return -EUCLEAN. In all cases, perform similar logic to mtd_read().
*/
- ret_code = mtd->read_oob(mtd, from, ops);
+ ret_code = mtd->_read_oob(mtd, from, ops);
if (unlikely(ret_code < 0))
return ret_code;
if (mtd->ecc_strength == 0)
diff --git a/drivers/mtd/devices/docg3.c b/drivers/mtd/devices/docg3.c
index 8921086a63..9b1e774995 100644
--- a/drivers/mtd/devices/docg3.c
+++ b/drivers/mtd/devices/docg3.c
@@ -1069,13 +1069,13 @@ static void __init doc_set_driver_info(int chip_id, struct mtd_info *mtd)
mtd->erasesize /= 2;
mtd->writesize = DOC_LAYOUT_PAGE_SIZE;
mtd->oobsize = DOC_LAYOUT_OOB_SIZE;
- mtd->read = doc_read;
- mtd->read_oob = doc_read_oob;
- mtd->block_isbad = doc_block_isbad;
+ mtd->_read = doc_read;
+ mtd->_read_oob = doc_read_oob;
+ mtd->_block_isbad = doc_block_isbad;
#ifdef CONFIG_MTD_WRITE
- mtd->erase = doc_erase;
- mtd->write = doc_write;
- mtd->write_oob = doc_write_oob;
+ mtd->_erase = doc_erase;
+ mtd->_write = doc_write;
+ mtd->_write_oob = doc_write_oob;
#endif
}
diff --git a/drivers/mtd/devices/mtd_dataflash.c b/drivers/mtd/devices/mtd_dataflash.c
index cd5ab6c972..77af940644 100644
--- a/drivers/mtd/devices/mtd_dataflash.c
+++ b/drivers/mtd/devices/mtd_dataflash.c
@@ -627,9 +627,9 @@ add_dataflash_otp(struct spi_device *spi, char *name,
device->writesize = pagesize;
device->type = MTD_DATAFLASH;
device->flags = MTD_WRITEABLE;
- device->erase = dataflash_erase;
- device->read = dataflash_read;
- device->write = dataflash_write;
+ device->_erase = dataflash_erase;
+ device->_read = dataflash_read;
+ device->_write = dataflash_write;
device->priv = priv;
device->parent = &spi->dev;
diff --git a/drivers/mtd/devices/mtdram.c b/drivers/mtd/devices/mtdram.c
index 783e1448d6..854aee4773 100644
--- a/drivers/mtd/devices/mtdram.c
+++ b/drivers/mtd/devices/mtdram.c
@@ -86,9 +86,9 @@ static int mtdram_probe(struct device_d *dev)
mtd->flags = MTD_CAP_RAM;
mtd->size = size;
- mtd->read = ram_read;
- mtd->write = ram_write;
- mtd->erase = ram_erase;
+ mtd->_read = ram_read;
+ mtd->_write = ram_write;
+ mtd->_erase = ram_erase;
mtd->erasesize = 1;
mtd->parent = dev;
diff --git a/drivers/mtd/mtdconcat.c b/drivers/mtd/mtdconcat.c
index d53c4b22e1..3032c5a16d 100644
--- a/drivers/mtd/mtdconcat.c
+++ b/drivers/mtd/mtdconcat.c
@@ -577,16 +577,16 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c
concat->mtd.subpage_sft = subdev[0]->subpage_sft;
concat->mtd.oobsize = subdev[0]->oobsize;
concat->mtd.oobavail = subdev[0]->oobavail;
- if (subdev[0]->read_oob)
- concat->mtd.read_oob = concat_read_oob;
- if (subdev[0]->write_oob)
- concat->mtd.write_oob = concat_write_oob;
- if (subdev[0]->block_isbad)
- concat->mtd.block_isbad = concat_block_isbad;
- if (subdev[0]->block_markbad)
- concat->mtd.block_markbad = concat_block_markbad;
- if (subdev[0]->block_markgood)
- concat->mtd.block_markgood = concat_block_markgood;
+ if (subdev[0]->_read_oob)
+ concat->mtd._read_oob = concat_read_oob;
+ if (subdev[0]->_write_oob)
+ concat->mtd._write_oob = concat_write_oob;
+ if (subdev[0]->_block_isbad)
+ concat->mtd._block_isbad = concat_block_isbad;
+ if (subdev[0]->_block_markbad)
+ concat->mtd._block_markbad = concat_block_markbad;
+ if (subdev[0]->_block_markgood)
+ concat->mtd._block_markgood = concat_block_markgood;
concat->mtd.ecc_stats.badblocks = subdev[0]->ecc_stats.badblocks;
@@ -623,8 +623,8 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c
if (concat->mtd.writesize != subdev[i]->writesize ||
concat->mtd.subpage_sft != subdev[i]->subpage_sft ||
concat->mtd.oobsize != subdev[i]->oobsize ||
- !concat->mtd.read_oob != !subdev[i]->read_oob ||
- !concat->mtd.write_oob != !subdev[i]->write_oob) {
+ !concat->mtd._read_oob != !subdev[i]->_read_oob ||
+ !concat->mtd._write_oob != !subdev[i]->_write_oob) {
kfree(concat);
printk("Incompatible OOB or ECC data on \"%s\"\n",
subdev[i]->name);
@@ -639,11 +639,11 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c
concat->num_subdev = num_devs;
concat->mtd.name = xstrdup(name);
- concat->mtd.erase = concat_erase;
- concat->mtd.read = concat_read;
- concat->mtd.write = concat_write;
- concat->mtd.lock = concat_lock;
- concat->mtd.unlock = concat_unlock;
+ concat->mtd._erase = concat_erase;
+ concat->mtd._read = concat_read;
+ concat->mtd._write = concat_write;
+ concat->mtd._lock = concat_lock;
+ concat->mtd._unlock = concat_unlock;
/*
* Combine the erase block size info of the subdevices:
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index ef0b15d64e..b8438b2891 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -3799,17 +3799,17 @@ int nand_scan_tail(struct nand_chip *chip)
mtd->type = MTD_NANDFLASH;
mtd->flags = (chip->options & NAND_ROM) ? MTD_CAP_ROM :
MTD_CAP_NANDFLASH;
- mtd->erase = nand_erase;
- mtd->read = nand_read;
- mtd->write = nand_write;
- mtd->read_oob = nand_read_oob;
- mtd->write_oob = nand_write_oob;
- mtd->sync = nand_sync;
- mtd->lock = NULL;
- mtd->unlock = NULL;
- mtd->block_isbad = nand_block_isbad;
- mtd->block_markbad = nand_block_markbad;
- mtd->block_markgood = nand_block_markgood;
+ mtd->_erase = nand_erase;
+ mtd->_read = nand_read;
+ mtd->_write = nand_write;
+ mtd->_read_oob = nand_read_oob;
+ mtd->_write_oob = nand_write_oob;
+ mtd->_sync = nand_sync;
+ mtd->_lock = NULL;
+ mtd->_unlock = NULL;
+ mtd->_block_isbad = nand_block_isbad;
+ mtd->_block_markbad = nand_block_markbad;
+ mtd->_block_markgood = nand_block_markgood;
mtd->writebufsize = mtd->writesize;
/* propagate ecc info to mtd_info */
diff --git a/drivers/mtd/nand/nand_mxs.c b/drivers/mtd/nand/nand_mxs.c
index dd4ea73b78..d4f0262755 100644
--- a/drivers/mtd/nand/nand_mxs.c
+++ b/drivers/mtd/nand/nand_mxs.c
@@ -1257,19 +1257,19 @@ static int mxs_nand_scan_bbt(struct nand_chip *chip)
writel(BCH_CTRL_COMPLETE_IRQ_EN, bch_regs + BCH_CTRL + STMP_OFFSET_REG_SET);
/* Hook some operations at the MTD level. */
- if (mtd->read_oob != mxs_nand_hook_read_oob) {
- nand_info->hooked_read_oob = mtd->read_oob;
- mtd->read_oob = mxs_nand_hook_read_oob;
+ if (mtd->_read_oob != mxs_nand_hook_read_oob) {
+ nand_info->hooked_read_oob = mtd->_read_oob;
+ mtd->_read_oob = mxs_nand_hook_read_oob;
}
- if (mtd->write_oob != mxs_nand_hook_write_oob) {
- nand_info->hooked_write_oob = mtd->write_oob;
- mtd->write_oob = mxs_nand_hook_write_oob;
+ if (mtd->_write_oob != mxs_nand_hook_write_oob) {
+ nand_info->hooked_write_oob = mtd->_write_oob;
+ mtd->_write_oob = mxs_nand_hook_write_oob;
}
- if (mtd->block_markbad != mxs_nand_hook_block_markbad) {
- nand_info->hooked_block_markbad = mtd->block_markbad;
- mtd->block_markbad = mxs_nand_hook_block_markbad;
+ if (mtd->_block_markbad != mxs_nand_hook_block_markbad) {
+ nand_info->hooked_block_markbad = mtd->_block_markbad;
+ mtd->_block_markbad = mxs_nand_hook_block_markbad;
}
/* We use the reference implementation for bad block management. */
diff --git a/drivers/mtd/nor/cfi_flash.c b/drivers/mtd/nor/cfi_flash.c
index 0988e67347..5ee05e1e6e 100644
--- a/drivers/mtd/nor/cfi_flash.c
+++ b/drivers/mtd/nor/cfi_flash.c
@@ -932,11 +932,11 @@ static void cfi_init_mtd(struct flash_info *info)
u32 erasesize;
int i;
- mtd->read = cfi_mtd_read;
- mtd->write = cfi_mtd_write;
- mtd->erase = cfi_mtd_erase;
- mtd->lock = cfi_mtd_lock;
- mtd->unlock = cfi_mtd_unlock;
+ mtd->_read = cfi_mtd_read;
+ mtd->_write = cfi_mtd_write;
+ mtd->_erase = cfi_mtd_erase;
+ mtd->_lock = cfi_mtd_lock;
+ mtd->_unlock = cfi_mtd_unlock;
mtd->size = info->size;
erasesize = 0;
diff --git a/drivers/mtd/partition.c b/drivers/mtd/partition.c
index 013697732d..27ff9f039f 100644
--- a/drivers/mtd/partition.c
+++ b/drivers/mtd/partition.c
@@ -13,7 +13,7 @@ static int mtd_part_read(struct mtd_info *mtd, loff_t from, size_t len,
len = 0;
else if (from + len > mtd->size)
len = mtd->size - from;
- res = mtd->master->read(mtd->master, from + mtd->master_offset,
+ res = mtd->master->_read(mtd->master, from + mtd->master_offset,
len, retlen, buf);
return res;
}
@@ -28,7 +28,7 @@ static int mtd_part_read_oob(struct mtd_info *mtd, loff_t from,
if (ops->datbuf && from + ops->len > mtd->size)
return -EINVAL;
- res = mtd->master->read_oob(mtd->master, from + mtd->master_offset, ops);
+ res = mtd->master->_read_oob(mtd->master, from + mtd->master_offset, ops);
if (unlikely(res)) {
if (mtd_is_bitflip(res))
mtd->ecc_stats.corrected++;
@@ -47,7 +47,7 @@ static int mtd_part_write(struct mtd_info *mtd, loff_t to, size_t len,
len = 0;
else if (to + len > mtd->size)
len = mtd->size - to;
- return mtd->master->write(mtd->master, to + mtd->master_offset,
+ return mtd->master->_write(mtd->master, to + mtd->master_offset,
len, retlen, buf);
}
@@ -58,7 +58,7 @@ static int mtd_part_write_oob(struct mtd_info *mtd, loff_t to,
return -EINVAL;
if (ops->datbuf && to + ops->len > mtd->size)
return -EINVAL;
- return mtd->master->write_oob(mtd->master, to + mtd->master_offset, ops);
+ return mtd->master->_write_oob(mtd->master, to + mtd->master_offset, ops);
}
static int mtd_part_erase(struct mtd_info *mtd, struct erase_info *instr)
@@ -70,7 +70,7 @@ static int mtd_part_erase(struct mtd_info *mtd, struct erase_info *instr)
if (instr->addr >= mtd->size)
return -EINVAL;
instr->addr += mtd->master_offset;
- ret = mtd->master->erase(mtd->master, instr);
+ ret = mtd->master->_erase(mtd->master, instr);
if (ret) {
if (instr->fail_addr != 0xffffffff)
instr->fail_addr -= mtd->master_offset;
@@ -81,7 +81,7 @@ static int mtd_part_erase(struct mtd_info *mtd, struct erase_info *instr)
static int mtd_part_lock(struct mtd_info *mtd, loff_t offset, size_t len)
{
- if (!mtd->master->lock)
+ if (!mtd->master->_lock)
return -ENOSYS;
if (!(mtd->flags & MTD_WRITEABLE))
@@ -92,12 +92,12 @@ static int mtd_part_lock(struct mtd_info *mtd, loff_t offset, size_t len)
offset += mtd->master_offset;
- return mtd->master->lock(mtd->master, offset, len);
+ return mtd->master->_lock(mtd->master, offset, len);
}
static int mtd_part_unlock(struct mtd_info *mtd, loff_t offset, size_t len)
{
- if (!mtd->master->unlock)
+ if (!mtd->master->_unlock)
return -ENOSYS;
if (!(mtd->flags & MTD_WRITEABLE))
@@ -108,7 +108,7 @@ static int mtd_part_unlock(struct mtd_info *mtd, loff_t offset, size_t len)
offset += mtd->master_offset;
- return mtd->master->unlock(mtd->master, offset, len);
+ return mtd->master->_unlock(mtd->master, offset, len);
}
static int mtd_part_block_isbad(struct mtd_info *mtd, loff_t ofs)
@@ -128,7 +128,7 @@ static int mtd_part_block_markbad(struct mtd_info *mtd, loff_t ofs)
if (ofs >= mtd->size)
return -EINVAL;
ofs += mtd->master_offset;
- res = mtd->master->block_markbad(mtd->master, ofs);
+ res = mtd->master->_block_markbad(mtd->master, ofs);
if (!res)
mtd->ecc_stats.badblocks++;
return res;
@@ -143,7 +143,7 @@ static int mtd_part_block_markgood(struct mtd_info *mtd, loff_t ofs)
if (ofs >= mtd->size)
return -EINVAL;
ofs += mtd->master_offset;
- res = mtd->master->block_markgood(mtd->master, ofs);
+ res = mtd->master->_block_markgood(mtd->master, ofs);
if (!res)
mtd->ecc_stats.badblocks--;
return res;
@@ -196,22 +196,22 @@ struct mtd_info *mtd_add_partition(struct mtd_info *mtd, off_t offset,
part->erasesize = mtd->erasesize;
}
- part->read = mtd_part_read;
+ part->_read = mtd_part_read;
if (IS_ENABLED(CONFIG_MTD_WRITE)) {
- part->write = mtd_part_write;
- part->erase = mtd_part_erase;
- part->lock = mtd_part_lock;
- part->unlock = mtd_part_unlock;
- part->block_markbad = mtd->block_markbad ? mtd_part_block_markbad : NULL;
- part->block_markgood = mtd->block_markgood ? mtd_part_block_markgood : NULL;
+ part->_write = mtd_part_write;
+ part->_erase = mtd_part_erase;
+ part->_lock = mtd_part_lock;
+ part->_unlock = mtd_part_unlock;
+ part->_block_markbad = mtd->_block_markbad ? mtd_part_block_markbad : NULL;
+ part->_block_markgood = mtd->_block_markgood ? mtd_part_block_markgood : NULL;
}
- if (mtd->write_oob)
- part->write_oob = mtd_part_write_oob;
- if (mtd->read_oob)
- part->read_oob = mtd_part_read_oob;
+ if (mtd->_write_oob)
+ part->_write_oob = mtd_part_write_oob;
+ if (mtd->_read_oob)
+ part->_read_oob = mtd_part_read_oob;
- part->block_isbad = mtd->block_isbad ? mtd_part_block_isbad : NULL;
+ part->_block_isbad = mtd->_block_isbad ? mtd_part_block_isbad : NULL;
part->size = size;
part->name = xstrdup(name);
diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
index 528cd9c399..350b82a6be 100644
--- a/drivers/mtd/spi-nor/spi-nor.c
+++ b/drivers/mtd/spi-nor/spi-nor.c
@@ -1437,20 +1437,20 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,
mtd->writesize = 1;
mtd->flags = MTD_CAP_NORFLASH;
mtd->size = params.size;
- mtd->erase = spi_nor_erase;
- mtd->read = spi_nor_read;
+ mtd->_erase = spi_nor_erase;
+ mtd->_read = spi_nor_read;
/* nor protection support for STmicro chips */
if (JEDEC_MFR(info) == CFI_MFR_ST) {
- mtd->lock = spi_nor_lock;
- mtd->unlock = spi_nor_unlock;
+ mtd->_lock = spi_nor_lock;
+ mtd->_unlock = spi_nor_unlock;
}
/* sst nor chips use AAI word program */
if (info->flags & SST_WRITE)
- mtd->write = sst_write;
+ mtd->_write = sst_write;
else
- mtd->write = spi_nor_write;
+ mtd->_write = spi_nor_write;
if (info->flags & USE_FSR)
nor->flags |= SNOR_F_USE_FSR;
diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h
index 8257403335..4a14cfd9d0 100644
--- a/include/linux/mtd/mtd.h
+++ b/include/linux/mtd/mtd.h
@@ -133,10 +133,10 @@ struct mtd_info {
int numeraseregions;
struct mtd_erase_region_info *eraseregions;
- int (*erase) (struct mtd_info *mtd, struct erase_info *instr);
+ int (*_erase) (struct mtd_info *mtd, struct erase_info *instr);
- int (*read) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
- int (*write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);
+ int (*_read) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
+ int (*_write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);
/* In blackbox flight recorder like scenarios we want to make successful
writes in interrupt context. panic_write() is only intended to be
@@ -145,11 +145,11 @@ struct mtd_info {
longer, this function can break locks and delay to ensure the write
succeeds (but not sleep). */
- int (*panic_write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);
+ int (*_panic_write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);
- int (*read_oob) (struct mtd_info *mtd, loff_t from,
+ int (*_read_oob) (struct mtd_info *mtd, loff_t from,
struct mtd_oob_ops *ops);
- int (*write_oob) (struct mtd_info *mtd, loff_t to,
+ int (*_write_oob) (struct mtd_info *mtd, loff_t to,
struct mtd_oob_ops *ops);
/*
@@ -157,24 +157,24 @@ struct mtd_info {
* flash devices. The user data is one time programmable but the
* factory data is read only.
*/
- int (*get_fact_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);
- int (*read_fact_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
- int (*get_user_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);
- int (*read_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
- int (*write_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
- int (*lock_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len);
+ int (*_get_fact_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);
+ int (*_read_fact_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
+ int (*_get_user_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);
+ int (*_read_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
+ int (*_write_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
+ int (*_lock_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len);
/* Sync */
- void (*sync) (struct mtd_info *mtd);
+ void (*_sync) (struct mtd_info *mtd);
/* Chip-supported device locking */
- int (*lock) (struct mtd_info *mtd, loff_t ofs, size_t len);
- int (*unlock) (struct mtd_info *mtd, loff_t ofs, size_t len);
+ int (*_lock) (struct mtd_info *mtd, loff_t ofs, size_t len);
+ int (*_unlock) (struct mtd_info *mtd, loff_t ofs, size_t len);
/* Bad block management functions */
- int (*block_isbad) (struct mtd_info *mtd, loff_t ofs);
- int (*block_markbad) (struct mtd_info *mtd, loff_t ofs);
- int (*block_markgood) (struct mtd_info *mtd, loff_t ofs);
+ int (*_block_isbad) (struct mtd_info *mtd, loff_t ofs);
+ int (*_block_markbad) (struct mtd_info *mtd, loff_t ofs);
+ int (*_block_markgood) (struct mtd_info *mtd, loff_t ofs);
/* ECC status information */
struct mtd_ecc_stats ecc_stats;
@@ -190,8 +190,8 @@ struct mtd_info {
* its own reference counting. The below functions are only for driver.
* The driver may register its callbacks. These callbacks are not
* supposed to be called by MTD users */
- int (*get_device) (struct mtd_info *mtd);
- void (*put_device) (struct mtd_info *mtd);
+ int (*_get_device) (struct mtd_info *mtd);
+ void (*_put_device) (struct mtd_info *mtd);
struct device_d class_dev;
struct device_d *parent;
@@ -229,16 +229,16 @@ static inline int mtd_write_oob(struct mtd_info *mtd, loff_t to,
struct mtd_oob_ops *ops)
{
ops->retlen = ops->oobretlen = 0;
- if (!mtd->write_oob)
+ if (!mtd->_write_oob)
return -EOPNOTSUPP;
if (!(mtd->flags & MTD_WRITEABLE))
return -EROFS;
- return mtd->write_oob(mtd, to, ops);
+ return mtd->_write_oob(mtd, to, ops);
}
static inline int mtd_can_have_bb(const struct mtd_info *mtd)
{
- return !!mtd->block_isbad;
+ return !!mtd->_block_isbad;
}
static inline uint32_t mtd_div_by_eb(uint64_t sz, struct mtd_info *mtd)
--
2.20.1
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 27+ messages in thread
* [PATCH 07/26] mtd: Use classdev->parent
2020-11-06 13:38 [PATCH 00/26] Update nand layer to Linux-5.9 Sascha Hauer
` (5 preceding siblings ...)
2020-11-06 13:38 ` [PATCH 06/26] mtd: Add underscore prefix to mtd hooks Sascha Hauer
@ 2020-11-06 13:38 ` Sascha Hauer
2020-11-06 13:38 ` [PATCH 08/26] mtd: rename class_dev to dev Sascha Hauer
` (18 subsequent siblings)
25 siblings, 0 replies; 27+ messages in thread
From: Sascha Hauer @ 2020-11-06 13:38 UTC (permalink / raw)
To: Barebox List
Instead of mtd->parent we can use mtd->classdev.parent which points to
the same device. With this we can remove the 'parent' member of struct
mtd_info. This member exists in the Linux kernel as well, but is of type
struct mtd_info, so this is done as preparation to re-add mtd->parent
with the same type as in Linux.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
drivers/mtd/core.c | 10 ++++------
drivers/mtd/devices/docg3.c | 2 +-
drivers/mtd/devices/m25p80.c | 2 +-
drivers/mtd/devices/mtd_dataflash.c | 2 +-
drivers/mtd/devices/mtdram.c | 2 +-
drivers/mtd/nand/atmel_nand.c | 2 +-
drivers/mtd/nand/nand_denali.c | 2 +-
drivers/mtd/nand/nand_imx.c | 4 ++--
drivers/mtd/nand/nand_mrvl_nfc.c | 2 +-
drivers/mtd/nand/nand_mxs.c | 4 ++--
drivers/mtd/nand/nand_omap_gpmc.c | 2 +-
drivers/mtd/nand/nand_orion.c | 2 +-
drivers/mtd/nand/nand_s3c24xx.c | 2 +-
drivers/mtd/nand/nomadik_nand.c | 2 +-
drivers/mtd/nor/cfi_flash.c | 4 ++--
drivers/mtd/partition.c | 2 +-
drivers/mtd/spi-nor/cadence-quadspi.c | 2 +-
include/linux/mtd/mtd.h | 1 -
18 files changed, 23 insertions(+), 26 deletions(-)
diff --git a/drivers/mtd/core.c b/drivers/mtd/core.c
index 71c602dbb7..fe7952c803 100644
--- a/drivers/mtd/core.c
+++ b/drivers/mtd/core.c
@@ -619,8 +619,6 @@ int add_mtd_device(struct mtd_info *mtd, const char *devname, int device_id)
devname = "mtd";
dev_set_name(&mtd->class_dev, devname);
mtd->class_dev.id = device_id;
- if (mtd->parent)
- mtd->class_dev.parent = mtd->parent;
if (IS_ENABLED(CONFIG_MTD_UBI))
mtd->class_dev.detect = mtd_detect;
@@ -673,11 +671,11 @@ int add_mtd_device(struct mtd_info *mtd, const char *devname, int device_id)
if (mtd_can_have_bb(mtd))
mtd->cdev_bb = mtd_add_bb(mtd, NULL);
- if (mtd->parent && !mtd->master) {
+ if (mtd->class_dev.parent && !mtd->master) {
dev_add_param_string(&mtd->class_dev, "partitions", mtd_partition_set, mtd_partition_get, &mtd->partition_string, mtd);
- of_parse_partitions(&mtd->cdev, mtd->parent->device_node);
- if (IS_ENABLED(CONFIG_OFDEVICE) && mtd->parent->device_node) {
- mtd->of_path = xstrdup(mtd->parent->device_node->full_name);
+ of_parse_partitions(&mtd->cdev, mtd->class_dev.parent->device_node);
+ if (IS_ENABLED(CONFIG_OFDEVICE) && mtd->class_dev.parent->device_node) {
+ mtd->of_path = xstrdup(mtd->class_dev.parent->device_node->full_name);
ret = of_partitions_register_fixup(&mtd->cdev);
if (ret)
goto err1;
diff --git a/drivers/mtd/devices/docg3.c b/drivers/mtd/devices/docg3.c
index 9b1e774995..e4104964bc 100644
--- a/drivers/mtd/devices/docg3.c
+++ b/drivers/mtd/devices/docg3.c
@@ -1172,7 +1172,7 @@ static int __init docg3_probe(struct device_d *dev)
continue;
}
docg3_floors[floor] = mtd;
- mtd->parent = dev;
+ mtd->class_dev.parent = dev;
ret = add_mtd_device(mtd, NULL, DEVICE_ID_DYNAMIC);
if (ret)
goto err_probe;
diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
index 09a8714247..f4b0d604f5 100644
--- a/drivers/mtd/devices/m25p80.c
+++ b/drivers/mtd/devices/m25p80.c
@@ -239,7 +239,7 @@ static int m25p_probe(struct device_d *dev)
nor->priv = flash;
flash->mtd.priv = nor;
- flash->mtd.parent = &spi->dev;
+ flash->mtd.class_dev.parent = &spi->dev;
flash->spimem = spimem;
if (spi->mode & SPI_RX_QUAD)
diff --git a/drivers/mtd/devices/mtd_dataflash.c b/drivers/mtd/devices/mtd_dataflash.c
index 77af940644..f024ffb0ca 100644
--- a/drivers/mtd/devices/mtd_dataflash.c
+++ b/drivers/mtd/devices/mtd_dataflash.c
@@ -632,7 +632,7 @@ add_dataflash_otp(struct spi_device *spi, char *name,
device->_write = dataflash_write;
device->priv = priv;
- device->parent = &spi->dev;
+ device->class_dev.parent = &spi->dev;
if (revision >= 'c')
otp_tag = otp_setup(device, revision);
diff --git a/drivers/mtd/devices/mtdram.c b/drivers/mtd/devices/mtdram.c
index 854aee4773..c2cecd20ec 100644
--- a/drivers/mtd/devices/mtdram.c
+++ b/drivers/mtd/devices/mtdram.c
@@ -91,7 +91,7 @@ static int mtdram_probe(struct device_d *dev)
mtd->_erase = ram_erase;
mtd->erasesize = 1;
- mtd->parent = dev;
+ mtd->class_dev.parent = dev;
ret = add_mtd_device(mtd, mtd->name, device_id);
return ret;
diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c
index e250df82d7..80634e3798 100644
--- a/drivers/mtd/nand/atmel_nand.c
+++ b/drivers/mtd/nand/atmel_nand.c
@@ -1331,7 +1331,7 @@ static int __init atmel_nand_probe(struct device_d *dev)
}
nand_chip->priv = host; /* link the private data structures */
- mtd->parent = dev;
+ mtd->class_dev.parent = dev;
/* Set address of NAND IO lines */
nand_chip->IO_ADDR_R = host->io_base;
diff --git a/drivers/mtd/nand/nand_denali.c b/drivers/mtd/nand/nand_denali.c
index 77a09ede70..ba3a307c62 100644
--- a/drivers/mtd/nand/nand_denali.c
+++ b/drivers/mtd/nand/nand_denali.c
@@ -1396,7 +1396,7 @@ int denali_init(struct denali_nand_info *denali)
if (!denali->buf.buf)
return -ENOMEM;
- mtd->parent = denali->dev;
+ mtd->class_dev.parent = denali->dev;
denali_hw_init(denali);
denali_drv_init(denali);
diff --git a/drivers/mtd/nand/nand_imx.c b/drivers/mtd/nand/nand_imx.c
index 40877eafb6..cc3494e7d3 100644
--- a/drivers/mtd/nand/nand_imx.c
+++ b/drivers/mtd/nand/nand_imx.c
@@ -1187,7 +1187,7 @@ static int imxnd_create_bbt(struct nand_chip *chip)
if (ret) {
bbt[i >> 3] |= 0x03 << (i & 0x6);
- dev_info(mtd->parent, "Bad eraseblock %d at 0x%08x\n",
+ dev_info(mtd->class_dev.parent, "Bad eraseblock %d at 0x%08x\n",
i >> 1, (unsigned int)from);
}
@@ -1337,7 +1337,7 @@ static int __init imxnd_probe(struct device_d *dev)
/* structures must be linked */
this = &host->nand;
mtd = &this->mtd;
- mtd->parent = dev;
+ mtd->class_dev.parent = dev;
mtd->name = "imx_nand";
/* 50 us command delay time */
diff --git a/drivers/mtd/nand/nand_mrvl_nfc.c b/drivers/mtd/nand/nand_mrvl_nfc.c
index 0a7c0d4a9a..c6636120ea 100644
--- a/drivers/mtd/nand/nand_mrvl_nfc.c
+++ b/drivers/mtd/nand/nand_mrvl_nfc.c
@@ -1118,7 +1118,7 @@ static struct mrvl_nand_host *alloc_nand_resource(struct device_d *dev)
host->num_cs = 1;
host->cs = 0;
mtd = &host->chip.mtd;
- mtd->parent = dev;
+ mtd->class_dev.parent = dev;
mtd->name = "mrvl_nand";
chip = &host->chip;
diff --git a/drivers/mtd/nand/nand_mxs.c b/drivers/mtd/nand/nand_mxs.c
index d4f0262755..69d0966892 100644
--- a/drivers/mtd/nand/nand_mxs.c
+++ b/drivers/mtd/nand/nand_mxs.c
@@ -338,7 +338,7 @@ static int mxs_nand_calc_geo(struct nand_chip *chip)
int gf_len = 13; /* length of Galois Field for non-DDR nand */
int max_ecc_strength;
- nand_of_parse_node(mtd, mtd->parent->device_node);
+ nand_of_parse_node(mtd, mtd->dev.parent->device_node);
max_ecc_strength = ((mtd->oobsize - MXS_NAND_METADATA_SIZE) * 8)
/ (gf_len * ecc_chunk_count);
@@ -2191,7 +2191,7 @@ static int mxs_nand_probe(struct device_d *dev)
/* structures must be linked */
chip = &nand_info->nand_chip;
mtd = &nand_info->nand_chip.mtd;
- mtd->parent = dev;
+ mtd->class_dev.parent = dev;
chip->priv = nand_info;
diff --git a/drivers/mtd/nand/nand_omap_gpmc.c b/drivers/mtd/nand/nand_omap_gpmc.c
index b7c35f8899..52bd2cf542 100644
--- a/drivers/mtd/nand/nand_omap_gpmc.c
+++ b/drivers/mtd/nand/nand_omap_gpmc.c
@@ -1192,7 +1192,7 @@ static int gpmc_nand_probe(struct device_d *pdev)
nand->priv = (void *)oinfo;
minfo = &nand->mtd;
- minfo->parent = pdev;
+ minfo->class_dev.parent = pdev;
if (pdata->cs >= GPMC_NUM_CS) {
dev_dbg(pdev, "Invalid CS!\n");
diff --git a/drivers/mtd/nand/nand_orion.c b/drivers/mtd/nand/nand_orion.c
index ac1c8442c0..445d48c363 100644
--- a/drivers/mtd/nand/nand_orion.c
+++ b/drivers/mtd/nand/nand_orion.c
@@ -114,7 +114,7 @@ static int orion_nand_probe(struct device_d *dev)
if (!of_property_read_u32(dev_node, "chip-delay", &val))
chip->chip_delay = (u8)val;
- mtd->parent = dev;
+ mtd->class_dev.parent = dev;
chip->priv = priv;
chip->IO_ADDR_R = chip->IO_ADDR_W = io_base;
chip->cmd_ctrl = orion_nand_cmd_ctrl;
diff --git a/drivers/mtd/nand/nand_s3c24xx.c b/drivers/mtd/nand/nand_s3c24xx.c
index 9df7f6cba2..2b539395fd 100644
--- a/drivers/mtd/nand/nand_s3c24xx.c
+++ b/drivers/mtd/nand/nand_s3c24xx.c
@@ -421,7 +421,7 @@ static int s3c24x0_nand_probe(struct device_d *dev)
/* structures must be linked */
chip = &host->nand;
mtd = &chip->mtd;
- mtd->parent = dev;
+ mtd->class_dev.parent = dev;
/* init the default settings */
diff --git a/drivers/mtd/nand/nomadik_nand.c b/drivers/mtd/nand/nomadik_nand.c
index 9918548357..286044a963 100644
--- a/drivers/mtd/nand/nomadik_nand.c
+++ b/drivers/mtd/nand/nomadik_nand.c
@@ -198,7 +198,7 @@ static int nomadik_nand_probe(struct device_d *dev)
nand = &host->nand;
mtd = &nand->mtd;
nand->priv = host;
- mtd->parent = dev;
+ mtd->class_dev.parent = dev;
nand->IO_ADDR_W = nand->IO_ADDR_R = dev_request_mem_region_by_name(dev, "nand_data");
if (IS_ERR(nand->IO_ADDR_W))
diff --git a/drivers/mtd/nor/cfi_flash.c b/drivers/mtd/nor/cfi_flash.c
index 5ee05e1e6e..86d4f0792d 100644
--- a/drivers/mtd/nor/cfi_flash.c
+++ b/drivers/mtd/nor/cfi_flash.c
@@ -954,7 +954,7 @@ static void cfi_init_mtd(struct flash_info *info)
mtd->numeraseregions = info->numeraseregions;
mtd->flags = MTD_CAP_NORFLASH;
mtd->type = MTD_NORFLASH;
- mtd->parent = info->dev;
+ mtd->class_dev.parent = info->dev;
}
static int cfi_probe_one(struct flash_info *info, int num)
@@ -1030,7 +1030,7 @@ static int cfi_probe(struct device_d *dev)
mtd = &priv->infos[0].mtd;
}
- mtd->parent = dev;
+ mtd->class_dev.parent = dev;
ret = add_mtd_device(mtd, "nor", DEVICE_ID_DYNAMIC);
if (ret)
diff --git a/drivers/mtd/partition.c b/drivers/mtd/partition.c
index 27ff9f039f..e52e711663 100644
--- a/drivers/mtd/partition.c
+++ b/drivers/mtd/partition.c
@@ -159,7 +159,7 @@ struct mtd_info *mtd_add_partition(struct mtd_info *mtd, off_t offset,
part->type = mtd->type;
part->flags = mtd->flags;
- part->parent = &mtd->class_dev;
+ part->class_dev.parent = &mtd->class_dev;
part->writesize = mtd->writesize;
part->writebufsize = mtd->writebufsize;
part->oobsize = mtd->oobsize;
diff --git a/drivers/mtd/spi-nor/cadence-quadspi.c b/drivers/mtd/spi-nor/cadence-quadspi.c
index 2372482ac1..bcf3c8f886 100644
--- a/drivers/mtd/spi-nor/cadence-quadspi.c
+++ b/drivers/mtd/spi-nor/cadence-quadspi.c
@@ -1120,7 +1120,7 @@ static int cqspi_setup_flash(struct device_d *dev,
if (ret)
return ret;
- mtd->parent = nor->dev;
+ mtd->class_dev.parent = nor->dev;
} else {
nor->dev = dev;
}
diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h
index 4a14cfd9d0..219a5279cd 100644
--- a/include/linux/mtd/mtd.h
+++ b/include/linux/mtd/mtd.h
@@ -194,7 +194,6 @@ struct mtd_info {
void (*_put_device) (struct mtd_info *mtd);
struct device_d class_dev;
- struct device_d *parent;
struct cdev cdev;
struct cdev *cdev_bb;
--
2.20.1
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 27+ messages in thread
* [PATCH 08/26] mtd: rename class_dev to dev
2020-11-06 13:38 [PATCH 00/26] Update nand layer to Linux-5.9 Sascha Hauer
` (6 preceding siblings ...)
2020-11-06 13:38 ` [PATCH 07/26] mtd: Use classdev->parent Sascha Hauer
@ 2020-11-06 13:38 ` Sascha Hauer
2020-11-06 13:38 ` [PATCH 09/26] mtd: rename master to parent Sascha Hauer
` (17 subsequent siblings)
25 siblings, 0 replies; 27+ messages in thread
From: Sascha Hauer @ 2020-11-06 13:38 UTC (permalink / raw)
To: Barebox List
The mtds own device is named 'dev' in the Kernel whereas it's named
'class_dev' in barebox. Rename it to 'dev' for better compatilibility
to the Kernel.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
drivers/mtd/core.c | 38 ++++++++++++------------
drivers/mtd/devices/docg3.c | 2 +-
drivers/mtd/devices/m25p80.c | 2 +-
drivers/mtd/devices/mtd_dataflash.c | 2 +-
drivers/mtd/devices/mtdram.c | 2 +-
drivers/mtd/mtdoob.c | 2 +-
drivers/mtd/mtdraw.c | 6 ++--
drivers/mtd/nand/atmel_nand.c | 2 +-
drivers/mtd/nand/nand_base.c | 12 ++++----
drivers/mtd/nand/nand_denali.c | 2 +-
drivers/mtd/nand/nand_imx.c | 4 +--
drivers/mtd/nand/nand_mrvl_nfc.c | 2 +-
drivers/mtd/nand/nand_mxs.c | 2 +-
drivers/mtd/nand/nand_omap_gpmc.c | 2 +-
drivers/mtd/nand/nand_orion.c | 2 +-
drivers/mtd/nand/nand_s3c24xx.c | 2 +-
drivers/mtd/nand/nomadik_nand.c | 2 +-
drivers/mtd/nor/cfi_flash.c | 4 +--
drivers/mtd/partition.c | 2 +-
drivers/mtd/peb.c | 42 +++++++++++++--------------
drivers/mtd/spi-nor/cadence-quadspi.c | 2 +-
drivers/mtd/ubi/build.c | 2 +-
include/linux/mtd/mtd.h | 2 +-
23 files changed, 70 insertions(+), 70 deletions(-)
diff --git a/drivers/mtd/core.c b/drivers/mtd/core.c
index fe7952c803..9d3b62762c 100644
--- a/drivers/mtd/core.c
+++ b/drivers/mtd/core.c
@@ -568,7 +568,7 @@ static int mtd_part_compare(struct list_head *a, struct list_head *b)
static int mtd_detect(struct device_d *dev)
{
- struct mtd_info *mtd = container_of(dev, struct mtd_info, class_dev);
+ struct mtd_info *mtd = container_of(dev, struct mtd_info, dev);
int bufsize = 512;
void *buf;
int ret = 0, i;
@@ -617,13 +617,13 @@ int add_mtd_device(struct mtd_info *mtd, const char *devname, int device_id)
if (!devname)
devname = "mtd";
- dev_set_name(&mtd->class_dev, devname);
- mtd->class_dev.id = device_id;
+ dev_set_name(&mtd->dev, devname);
+ mtd->dev.id = device_id;
if (IS_ENABLED(CONFIG_MTD_UBI))
- mtd->class_dev.detect = mtd_detect;
+ mtd->dev.detect = mtd_detect;
- ret = register_device(&mtd->class_dev);
+ ret = register_device(&mtd->dev);
if (ret)
return ret;
@@ -633,19 +633,19 @@ int add_mtd_device(struct mtd_info *mtd, const char *devname, int device_id)
mtd->cdev.name = xstrdup(devname);
else
mtd->cdev.name = basprintf("%s%d", devname,
- mtd->class_dev.id);
+ mtd->dev.id);
INIT_LIST_HEAD(&mtd->partitions);
mtd->cdev.priv = mtd;
- mtd->cdev.dev = &mtd->class_dev;
+ mtd->cdev.dev = &mtd->dev;
mtd->cdev.mtd = mtd;
if (IS_ENABLED(CONFIG_PARAMETER)) {
- dev_add_param_uint64_ro(&mtd->class_dev, "size", &mtd->size, "%llu");
- dev_add_param_uint32_ro(&mtd->class_dev, "erasesize", &mtd->erasesize, "%u");
- dev_add_param_uint32_ro(&mtd->class_dev, "writesize", &mtd->writesize, "%u");
- dev_add_param_uint32_ro(&mtd->class_dev, "oobsize", &mtd->oobsize, "%u");
+ dev_add_param_uint64_ro(&mtd->dev, "size", &mtd->size, "%llu");
+ dev_add_param_uint32_ro(&mtd->dev, "erasesize", &mtd->erasesize, "%u");
+ dev_add_param_uint32_ro(&mtd->dev, "writesize", &mtd->writesize, "%u");
+ dev_add_param_uint32_ro(&mtd->dev, "oobsize", &mtd->oobsize, "%u");
}
ret = devfs_create(&mtd->cdev);
@@ -660,7 +660,7 @@ int add_mtd_device(struct mtd_info *mtd, const char *devname, int device_id)
continue;
if (mtd->master_offset + mtd->size <= mtdpart->master_offset)
continue;
- dev_err(&mtd->class_dev, "New partition %s conflicts with %s\n",
+ dev_err(&mtd->dev, "New partition %s conflicts with %s\n",
mtd->name, mtdpart->name);
goto err1;
}
@@ -671,11 +671,11 @@ int add_mtd_device(struct mtd_info *mtd, const char *devname, int device_id)
if (mtd_can_have_bb(mtd))
mtd->cdev_bb = mtd_add_bb(mtd, NULL);
- if (mtd->class_dev.parent && !mtd->master) {
- dev_add_param_string(&mtd->class_dev, "partitions", mtd_partition_set, mtd_partition_get, &mtd->partition_string, mtd);
- of_parse_partitions(&mtd->cdev, mtd->class_dev.parent->device_node);
- if (IS_ENABLED(CONFIG_OFDEVICE) && mtd->class_dev.parent->device_node) {
- mtd->of_path = xstrdup(mtd->class_dev.parent->device_node->full_name);
+ if (mtd->dev.parent && !mtd->master) {
+ dev_add_param_string(&mtd->dev, "partitions", mtd_partition_set, mtd_partition_get, &mtd->partition_string, mtd);
+ of_parse_partitions(&mtd->cdev, mtd->dev.parent->device_node);
+ if (IS_ENABLED(CONFIG_OFDEVICE) && mtd->dev.parent->device_node) {
+ mtd->of_path = xstrdup(mtd->dev.parent->device_node->full_name);
ret = of_partitions_register_fixup(&mtd->cdev);
if (ret)
goto err1;
@@ -691,7 +691,7 @@ err1:
devfs_remove(&mtd->cdev);
err:
free(mtd->cdev.name);
- unregister_device(&mtd->class_dev);
+ unregister_device(&mtd->dev);
return ret;
}
@@ -707,7 +707,7 @@ int del_mtd_device (struct mtd_info *mtd)
devfs_remove(&mtd->cdev);
if (mtd->cdev_bb)
mtd_del_bb(mtd);
- unregister_device(&mtd->class_dev);
+ unregister_device(&mtd->dev);
free(mtd->param_size.value);
free(mtd->cdev.name);
if (mtd->master)
diff --git a/drivers/mtd/devices/docg3.c b/drivers/mtd/devices/docg3.c
index e4104964bc..cb0bb5cfec 100644
--- a/drivers/mtd/devices/docg3.c
+++ b/drivers/mtd/devices/docg3.c
@@ -1172,7 +1172,7 @@ static int __init docg3_probe(struct device_d *dev)
continue;
}
docg3_floors[floor] = mtd;
- mtd->class_dev.parent = dev;
+ mtd->dev.parent = dev;
ret = add_mtd_device(mtd, NULL, DEVICE_ID_DYNAMIC);
if (ret)
goto err_probe;
diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
index f4b0d604f5..8845ec3a3e 100644
--- a/drivers/mtd/devices/m25p80.c
+++ b/drivers/mtd/devices/m25p80.c
@@ -239,7 +239,7 @@ static int m25p_probe(struct device_d *dev)
nor->priv = flash;
flash->mtd.priv = nor;
- flash->mtd.class_dev.parent = &spi->dev;
+ flash->mtd.dev.parent = &spi->dev;
flash->spimem = spimem;
if (spi->mode & SPI_RX_QUAD)
diff --git a/drivers/mtd/devices/mtd_dataflash.c b/drivers/mtd/devices/mtd_dataflash.c
index f024ffb0ca..9d4105f82b 100644
--- a/drivers/mtd/devices/mtd_dataflash.c
+++ b/drivers/mtd/devices/mtd_dataflash.c
@@ -632,7 +632,7 @@ add_dataflash_otp(struct spi_device *spi, char *name,
device->_write = dataflash_write;
device->priv = priv;
- device->class_dev.parent = &spi->dev;
+ device->dev.parent = &spi->dev;
if (revision >= 'c')
otp_tag = otp_setup(device, revision);
diff --git a/drivers/mtd/devices/mtdram.c b/drivers/mtd/devices/mtdram.c
index c2cecd20ec..9cc8194b5d 100644
--- a/drivers/mtd/devices/mtdram.c
+++ b/drivers/mtd/devices/mtdram.c
@@ -91,7 +91,7 @@ static int mtdram_probe(struct device_d *dev)
mtd->_erase = ram_erase;
mtd->erasesize = 1;
- mtd->class_dev.parent = dev;
+ mtd->dev.parent = dev;
ret = add_mtd_device(mtd, mtd->name, device_id);
return ret;
diff --git a/drivers/mtd/mtdoob.c b/drivers/mtd/mtdoob.c
index 4aef844485..3229a1cce5 100644
--- a/drivers/mtd/mtdoob.c
+++ b/drivers/mtd/mtdoob.c
@@ -80,7 +80,7 @@ static int add_mtdoob_device(struct mtd_info *mtd, const char *devname, void **p
mtdoob->cdev.size = mtd_div_by_wb(mtd->size, mtd) * mtd->oobsize;
mtdoob->cdev.name = basprintf("%s.oob", mtd->cdev.name);
mtdoob->cdev.priv = mtdoob;
- mtdoob->cdev.dev = &mtd->class_dev;
+ mtdoob->cdev.dev = &mtd->dev;
mtdoob->mtd = mtd;
*priv = mtdoob;
devfs_create(&mtdoob->cdev);
diff --git a/drivers/mtd/mtdraw.c b/drivers/mtd/mtdraw.c
index b71619244b..6bebe0409e 100644
--- a/drivers/mtd/mtdraw.c
+++ b/drivers/mtd/mtdraw.c
@@ -252,7 +252,7 @@ static int mtdraw_erase(struct cdev *cdev, loff_t count, loff_t offset)
erase.len = mtd->erasesize;
while (count > 0) {
- dev_dbg(&mtd->class_dev, "erase 0x%08llx len: 0x%08llx\n",
+ dev_dbg(&mtd->dev, "erase 0x%08llx len: 0x%08llx\n",
erase.addr, erase.len);
if (!mtd->allow_erasebad)
@@ -261,7 +261,7 @@ static int mtdraw_erase(struct cdev *cdev, loff_t count, loff_t offset)
ret = 0;
if (ret > 0) {
- dev_info(&mtd->class_dev, "Skipping bad block at 0x%08llx\n",
+ dev_info(&mtd->dev, "Skipping bad block at 0x%08llx\n",
erase.addr);
} else {
ret = mtd_erase(mtd, &erase);
@@ -309,7 +309,7 @@ static int add_mtdraw_device(struct mtd_info *mtd, const char *devname, void **p
mtdraw->cdev.size = (loff_t)mtd_div_by_wb(mtd->size, mtd) * mtdraw->rps;
mtdraw->cdev.name = basprintf("%s.raw", mtd->cdev.name);
mtdraw->cdev.priv = mtdraw;
- mtdraw->cdev.dev = &mtd->class_dev;
+ mtdraw->cdev.dev = &mtd->dev;
mtdraw->cdev.mtd = mtd;
*priv = mtdraw;
devfs_create(&mtdraw->cdev);
diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c
index 80634e3798..90656bcb5d 100644
--- a/drivers/mtd/nand/atmel_nand.c
+++ b/drivers/mtd/nand/atmel_nand.c
@@ -1331,7 +1331,7 @@ static int __init atmel_nand_probe(struct device_d *dev)
}
nand_chip->priv = host; /* link the private data structures */
- mtd->class_dev.parent = dev;
+ mtd->dev.parent = dev;
/* Set address of NAND IO lines */
nand_chip->IO_ADDR_R = host->io_base;
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index b8438b2891..f869c21705 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -3891,7 +3891,7 @@ static int mtd_set_erasebad(struct param_d *param, void *priv)
}
if (!mtd->allow_erasebad)
- dev_warn(&mtd->class_dev,
+ dev_warn(&mtd->dev,
"Allowing to erase bad blocks. This may be dangerous!\n");
mtd->allow_erasebad = true;
@@ -3940,17 +3940,17 @@ int add_mtd_nand_device(struct nand_chip *chip, char *devname)
return ret;
if (IS_ENABLED(CONFIG_NAND_ALLOW_ERASE_BAD))
- dev_add_param_bool(&mtd->class_dev, "erasebad", mtd_set_erasebad,
+ dev_add_param_bool(&mtd->dev, "erasebad", mtd_set_erasebad,
NULL, &mtd->p_allow_erasebad, mtd);
- dev_add_param_enum(&mtd->class_dev, "bbt", NULL, mtd_get_bbt_type,
+ dev_add_param_enum(&mtd->dev, "bbt", NULL, mtd_get_bbt_type,
&chip->bbt_type, bbt_type_strings,
ARRAY_SIZE(bbt_type_strings),
mtd);
- dev_add_param_uint32_ro(&mtd->class_dev, "ecc.bytes", &chip->ecc.bytes, "%u");
- dev_add_param_uint32_ro(&mtd->class_dev, "ecc.strength", &chip->ecc.strength, "%u");
- dev_add_param_uint32_ro(&mtd->class_dev, "ecc.size", &chip->ecc.size, "%u");
+ dev_add_param_uint32_ro(&mtd->dev, "ecc.bytes", &chip->ecc.bytes, "%u");
+ dev_add_param_uint32_ro(&mtd->dev, "ecc.strength", &chip->ecc.strength, "%u");
+ dev_add_param_uint32_ro(&mtd->dev, "ecc.size", &chip->ecc.size, "%u");
return ret;
}
diff --git a/drivers/mtd/nand/nand_denali.c b/drivers/mtd/nand/nand_denali.c
index ba3a307c62..629281061b 100644
--- a/drivers/mtd/nand/nand_denali.c
+++ b/drivers/mtd/nand/nand_denali.c
@@ -1396,7 +1396,7 @@ int denali_init(struct denali_nand_info *denali)
if (!denali->buf.buf)
return -ENOMEM;
- mtd->class_dev.parent = denali->dev;
+ mtd->dev.parent = denali->dev;
denali_hw_init(denali);
denali_drv_init(denali);
diff --git a/drivers/mtd/nand/nand_imx.c b/drivers/mtd/nand/nand_imx.c
index cc3494e7d3..d566c941e7 100644
--- a/drivers/mtd/nand/nand_imx.c
+++ b/drivers/mtd/nand/nand_imx.c
@@ -1187,7 +1187,7 @@ static int imxnd_create_bbt(struct nand_chip *chip)
if (ret) {
bbt[i >> 3] |= 0x03 << (i & 0x6);
- dev_info(mtd->class_dev.parent, "Bad eraseblock %d at 0x%08x\n",
+ dev_info(mtd->dev.parent, "Bad eraseblock %d at 0x%08x\n",
i >> 1, (unsigned int)from);
}
@@ -1337,7 +1337,7 @@ static int __init imxnd_probe(struct device_d *dev)
/* structures must be linked */
this = &host->nand;
mtd = &this->mtd;
- mtd->class_dev.parent = dev;
+ mtd->dev.parent = dev;
mtd->name = "imx_nand";
/* 50 us command delay time */
diff --git a/drivers/mtd/nand/nand_mrvl_nfc.c b/drivers/mtd/nand/nand_mrvl_nfc.c
index c6636120ea..3f5e04b716 100644
--- a/drivers/mtd/nand/nand_mrvl_nfc.c
+++ b/drivers/mtd/nand/nand_mrvl_nfc.c
@@ -1118,7 +1118,7 @@ static struct mrvl_nand_host *alloc_nand_resource(struct device_d *dev)
host->num_cs = 1;
host->cs = 0;
mtd = &host->chip.mtd;
- mtd->class_dev.parent = dev;
+ mtd->dev.parent = dev;
mtd->name = "mrvl_nand";
chip = &host->chip;
diff --git a/drivers/mtd/nand/nand_mxs.c b/drivers/mtd/nand/nand_mxs.c
index 69d0966892..8075f6a854 100644
--- a/drivers/mtd/nand/nand_mxs.c
+++ b/drivers/mtd/nand/nand_mxs.c
@@ -2191,7 +2191,7 @@ static int mxs_nand_probe(struct device_d *dev)
/* structures must be linked */
chip = &nand_info->nand_chip;
mtd = &nand_info->nand_chip.mtd;
- mtd->class_dev.parent = dev;
+ mtd->dev.parent = dev;
chip->priv = nand_info;
diff --git a/drivers/mtd/nand/nand_omap_gpmc.c b/drivers/mtd/nand/nand_omap_gpmc.c
index 52bd2cf542..6ad06288eb 100644
--- a/drivers/mtd/nand/nand_omap_gpmc.c
+++ b/drivers/mtd/nand/nand_omap_gpmc.c
@@ -1192,7 +1192,7 @@ static int gpmc_nand_probe(struct device_d *pdev)
nand->priv = (void *)oinfo;
minfo = &nand->mtd;
- minfo->class_dev.parent = pdev;
+ minfo->dev.parent = pdev;
if (pdata->cs >= GPMC_NUM_CS) {
dev_dbg(pdev, "Invalid CS!\n");
diff --git a/drivers/mtd/nand/nand_orion.c b/drivers/mtd/nand/nand_orion.c
index 445d48c363..796c53e4c4 100644
--- a/drivers/mtd/nand/nand_orion.c
+++ b/drivers/mtd/nand/nand_orion.c
@@ -114,7 +114,7 @@ static int orion_nand_probe(struct device_d *dev)
if (!of_property_read_u32(dev_node, "chip-delay", &val))
chip->chip_delay = (u8)val;
- mtd->class_dev.parent = dev;
+ mtd->dev.parent = dev;
chip->priv = priv;
chip->IO_ADDR_R = chip->IO_ADDR_W = io_base;
chip->cmd_ctrl = orion_nand_cmd_ctrl;
diff --git a/drivers/mtd/nand/nand_s3c24xx.c b/drivers/mtd/nand/nand_s3c24xx.c
index 2b539395fd..ac95973194 100644
--- a/drivers/mtd/nand/nand_s3c24xx.c
+++ b/drivers/mtd/nand/nand_s3c24xx.c
@@ -421,7 +421,7 @@ static int s3c24x0_nand_probe(struct device_d *dev)
/* structures must be linked */
chip = &host->nand;
mtd = &chip->mtd;
- mtd->class_dev.parent = dev;
+ mtd->dev.parent = dev;
/* init the default settings */
diff --git a/drivers/mtd/nand/nomadik_nand.c b/drivers/mtd/nand/nomadik_nand.c
index 286044a963..55aa756826 100644
--- a/drivers/mtd/nand/nomadik_nand.c
+++ b/drivers/mtd/nand/nomadik_nand.c
@@ -198,7 +198,7 @@ static int nomadik_nand_probe(struct device_d *dev)
nand = &host->nand;
mtd = &nand->mtd;
nand->priv = host;
- mtd->class_dev.parent = dev;
+ mtd->dev.parent = dev;
nand->IO_ADDR_W = nand->IO_ADDR_R = dev_request_mem_region_by_name(dev, "nand_data");
if (IS_ERR(nand->IO_ADDR_W))
diff --git a/drivers/mtd/nor/cfi_flash.c b/drivers/mtd/nor/cfi_flash.c
index 86d4f0792d..225b03ec3d 100644
--- a/drivers/mtd/nor/cfi_flash.c
+++ b/drivers/mtd/nor/cfi_flash.c
@@ -954,7 +954,7 @@ static void cfi_init_mtd(struct flash_info *info)
mtd->numeraseregions = info->numeraseregions;
mtd->flags = MTD_CAP_NORFLASH;
mtd->type = MTD_NORFLASH;
- mtd->class_dev.parent = info->dev;
+ mtd->dev.parent = info->dev;
}
static int cfi_probe_one(struct flash_info *info, int num)
@@ -1030,7 +1030,7 @@ static int cfi_probe(struct device_d *dev)
mtd = &priv->infos[0].mtd;
}
- mtd->class_dev.parent = dev;
+ mtd->dev.parent = dev;
ret = add_mtd_device(mtd, "nor", DEVICE_ID_DYNAMIC);
if (ret)
diff --git a/drivers/mtd/partition.c b/drivers/mtd/partition.c
index e52e711663..6a6988432c 100644
--- a/drivers/mtd/partition.c
+++ b/drivers/mtd/partition.c
@@ -159,7 +159,7 @@ struct mtd_info *mtd_add_partition(struct mtd_info *mtd, off_t offset,
part->type = mtd->type;
part->flags = mtd->flags;
- part->class_dev.parent = &mtd->class_dev;
+ part->dev.parent = &mtd->dev;
part->writesize = mtd->writesize;
part->writebufsize = mtd->writebufsize;
part->oobsize = mtd->oobsize;
diff --git a/drivers/mtd/peb.c b/drivers/mtd/peb.c
index d10a8a024d..27996d7e86 100644
--- a/drivers/mtd/peb.c
+++ b/drivers/mtd/peb.c
@@ -241,7 +241,7 @@ retry:
* enabled. A corresponding message will be printed
* later, when it is has been scrubbed.
*/
- dev_dbg(&mtd->class_dev, "fixable bit-flip detected at PEB %d\n", pnum);
+ dev_dbg(&mtd->dev, "fixable bit-flip detected at PEB %d\n", pnum);
if (len != read)
return -EIO;
return -EUCLEAN;
@@ -250,7 +250,7 @@ retry:
if (mtd_is_eccerr(err) && retries++ < MTD_IO_RETRIES)
goto retry;
- dev_err(&mtd->class_dev, "error %d%s while reading %d bytes from PEB %d:%d\n",
+ dev_err(&mtd->dev, "error %d%s while reading %d bytes from PEB %d:%d\n",
err, errstr, len, pnum, offset);
return err;
}
@@ -288,7 +288,7 @@ int mtd_peb_check_all_ff(struct mtd_info *mtd, int pnum, int offset, int len,
err = mtd_peb_read(mtd, buf, pnum, offset, len);
if (err && !mtd_is_bitflip(err)) {
- dev_err(&mtd->class_dev,
+ dev_err(&mtd->dev,
"error %d while reading %d bytes from PEB %d:%d\n",
err, len, pnum, offset);
goto out;
@@ -297,7 +297,7 @@ int mtd_peb_check_all_ff(struct mtd_info *mtd, int pnum, int offset, int len,
err = mtd_buf_all_ff(buf, len);
if (err == 0) {
if (warn)
- dev_err(&mtd->class_dev, "all-ff check failed for PEB %d\n",
+ dev_err(&mtd->dev, "all-ff check failed for PEB %d\n",
pnum);
err = -EBADMSG;
goto out;
@@ -347,15 +347,15 @@ int mtd_peb_verify(struct mtd_info *mtd, const void *buf, int pnum,
if (c == c1)
continue;
- dev_err(&mtd->class_dev, "self-check failed for PEB %d:%d, len %d\n",
+ dev_err(&mtd->dev, "self-check failed for PEB %d:%d, len %d\n",
pnum, offset, len);
- dev_info(&mtd->class_dev, "data differs at position %d\n", i);
+ dev_info(&mtd->dev, "data differs at position %d\n", i);
dump_len = max_t(int, 128, len - i);
#ifdef DEBUG
- dev_info(&mtd->class_dev, "hex dump of the original buffer from %d to %d\n",
+ dev_info(&mtd->dev, "hex dump of the original buffer from %d to %d\n",
i, i + dump_len);
memory_display(buf + i, i, dump_len, 4, 0);
- dev_info(&mtd->class_dev, "hex dump of the read buffer from %d to %d\n",
+ dev_info(&mtd->dev, "hex dump of the read buffer from %d to %d\n",
i, i + dump_len);
memory_display(buf1 + i, i, dump_len, 4, 0);
dump_stack();
@@ -395,7 +395,7 @@ int mtd_peb_write(struct mtd_info *mtd, const void *buf, int pnum, int offset,
size_t written;
loff_t addr;
- dev_dbg(&mtd->class_dev, "write %d bytes to PEB %d:%d\n", len, pnum, offset);
+ dev_dbg(&mtd->dev, "write %d bytes to PEB %d:%d\n", len, pnum, offset);
if (!mtd_peb_valid(mtd, pnum))
return -EINVAL;
@@ -409,7 +409,7 @@ int mtd_peb_write(struct mtd_info *mtd, const void *buf, int pnum, int offset,
return -EINVAL;
if (mtd_peb_emulate_write_failure()) {
- dev_err(&mtd->class_dev, "Cannot write %d bytes to PEB %d:%d (emulated)\n",
+ dev_err(&mtd->dev, "Cannot write %d bytes to PEB %d:%d (emulated)\n",
len, pnum, offset);
return -EIO;
}
@@ -424,7 +424,7 @@ int mtd_peb_write(struct mtd_info *mtd, const void *buf, int pnum, int offset,
addr = (loff_t)pnum * mtd->erasesize + offset;
err = mtd_write(mtd, addr, len, &written, buf);
if (err) {
- dev_err(&mtd->class_dev, "error %d while writing %d bytes to PEB %d:%d, written %zu bytes\n",
+ dev_err(&mtd->dev, "error %d while writing %d bytes to PEB %d:%d, written %zu bytes\n",
err, len, pnum, offset, written);
} else {
if (written != len)
@@ -537,7 +537,7 @@ int mtd_peb_erase(struct mtd_info *mtd, int pnum)
int ret;
struct erase_info ei = {};
- dev_dbg(&mtd->class_dev, "erase PEB %d\n", pnum);
+ dev_dbg(&mtd->dev, "erase PEB %d\n", pnum);
if (!mtd_peb_valid(mtd, pnum))
return -EINVAL;
@@ -557,7 +557,7 @@ int mtd_peb_erase(struct mtd_info *mtd, int pnum)
}
if (mtd_peb_emulate_erase_failure()) {
- dev_err(&mtd->class_dev, "cannot erase PEB %d (emulated)", pnum);
+ dev_err(&mtd->dev, "cannot erase PEB %d (emulated)", pnum);
return -EIO;
}
@@ -592,7 +592,7 @@ int mtd_peb_torture(struct mtd_info *mtd, int pnum)
if (!peb_buf)
return -ENOMEM;
- dev_dbg(&mtd->class_dev, "run torture test for PEB %d\n", pnum);
+ dev_dbg(&mtd->dev, "run torture test for PEB %d\n", pnum);
patt_count = ARRAY_SIZE(patterns);
@@ -622,7 +622,7 @@ int mtd_peb_torture(struct mtd_info *mtd, int pnum)
goto out;
err = patt_count + 1;
- dev_dbg(&mtd->class_dev, "PEB %d passed torture test, do not mark it as bad\n",
+ dev_dbg(&mtd->dev, "PEB %d passed torture test, do not mark it as bad\n",
pnum);
out:
@@ -632,7 +632,7 @@ out:
* has not passed because it happened on a freshly erased
* physical eraseblock which means something is wrong with it.
*/
- dev_err(&mtd->class_dev, "read problems on freshly erased PEB %d, must be bad\n",
+ dev_err(&mtd->dev, "read problems on freshly erased PEB %d, must be bad\n",
pnum);
err = -EIO;
@@ -707,7 +707,7 @@ int mtd_peb_create_bitflips(struct mtd_info *mtd, int pnum, int offset,
ret = mtd_read_oob(mtd, offs, &ops);
if (ret) {
- dev_err(&mtd->class_dev, "Cannot read raw data at 0x%08llx\n", offs);
+ dev_err(&mtd->dev, "Cannot read raw data at 0x%08llx\n", offs);
goto err;
}
}
@@ -733,12 +733,12 @@ int mtd_peb_create_bitflips(struct mtd_info *mtd, int pnum, int offset,
pos[offs] ^= 1 << bit;
if (info)
- dev_info(&mtd->class_dev, "Flipping bit %d @ %d\n", bit, offs);
+ dev_info(&mtd->dev, "Flipping bit %d @ %d\n", bit, offs);
}
ret = mtd_peb_erase(mtd, pnum);
if (ret < 0) {
- dev_err(&mtd->class_dev, "Cannot erase PEB %d\n", pnum);
+ dev_err(&mtd->dev, "Cannot erase PEB %d\n", pnum);
goto err;
}
@@ -750,7 +750,7 @@ int mtd_peb_create_bitflips(struct mtd_info *mtd, int pnum, int offset,
ret = mtd_write_oob(mtd, offs, &ops);
if (ret) {
- dev_err(&mtd->class_dev, "Cannot write page at 0x%08llx\n", offs);
+ dev_err(&mtd->dev, "Cannot write page at 0x%08llx\n", offs);
goto err;
}
}
@@ -758,7 +758,7 @@ int mtd_peb_create_bitflips(struct mtd_info *mtd, int pnum, int offset,
ret = 0;
err:
if (ret)
- dev_err(&mtd->class_dev, "Failed to create bitflips: %s\n", strerror(-ret));
+ dev_err(&mtd->dev, "Failed to create bitflips: %s\n", strerror(-ret));
free(buf);
free(oobbuf);
diff --git a/drivers/mtd/spi-nor/cadence-quadspi.c b/drivers/mtd/spi-nor/cadence-quadspi.c
index bcf3c8f886..ea53d2cd84 100644
--- a/drivers/mtd/spi-nor/cadence-quadspi.c
+++ b/drivers/mtd/spi-nor/cadence-quadspi.c
@@ -1120,7 +1120,7 @@ static int cqspi_setup_flash(struct device_d *dev,
if (ret)
return ret;
- mtd->class_dev.parent = nor->dev;
+ mtd->dev.parent = nor->dev;
} else {
nor->dev = dev;
}
diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c
index da409010f7..8a6d0ebde0 100644
--- a/drivers/mtd/ubi/build.c
+++ b/drivers/mtd/ubi/build.c
@@ -159,7 +159,7 @@ static int uif_init(struct ubi_device *ubi, int *ref)
dev_set_name(&ubi->dev, "%s.ubi", ubi->mtd->cdev.name);
ubi->dev.id = DEVICE_ID_SINGLE;
- ubi->dev.parent = &ubi->mtd->class_dev;
+ ubi->dev.parent = &ubi->mtd->dev;
err = register_device(&ubi->dev);
if (err)
diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h
index 219a5279cd..e4ce2ae233 100644
--- a/include/linux/mtd/mtd.h
+++ b/include/linux/mtd/mtd.h
@@ -193,7 +193,7 @@ struct mtd_info {
int (*_get_device) (struct mtd_info *mtd);
void (*_put_device) (struct mtd_info *mtd);
- struct device_d class_dev;
+ struct device_d dev;
struct cdev cdev;
struct cdev *cdev_bb;
--
2.20.1
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 27+ messages in thread
* [PATCH 09/26] mtd: rename master to parent
2020-11-06 13:38 [PATCH 00/26] Update nand layer to Linux-5.9 Sascha Hauer
` (7 preceding siblings ...)
2020-11-06 13:38 ` [PATCH 08/26] mtd: rename class_dev to dev Sascha Hauer
@ 2020-11-06 13:38 ` Sascha Hauer
2020-11-06 13:38 ` [PATCH 10/26] lib: Add match_string() Sascha Hauer
` (16 subsequent siblings)
25 siblings, 0 replies; 27+ messages in thread
From: Sascha Hauer @ 2020-11-06 13:38 UTC (permalink / raw)
To: Barebox List
In Linux mtd->parent is what in barebox is mtd->master. Rename this
to get closer to the Linux mtd layer.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
drivers/mtd/core.c | 12 ++++++------
drivers/mtd/mtdoob.c | 4 ++--
drivers/mtd/mtdraw.c | 4 ++--
drivers/mtd/partition.c | 30 +++++++++++++++---------------
drivers/mtd/ubi/build.c | 4 ++--
include/linux/mtd/mtd.h | 8 +++++++-
6 files changed, 34 insertions(+), 28 deletions(-)
diff --git a/drivers/mtd/core.c b/drivers/mtd/core.c
index 9d3b62762c..8d018fde57 100644
--- a/drivers/mtd/core.c
+++ b/drivers/mtd/core.c
@@ -210,7 +210,7 @@ static int mtd_op_erase(struct cdev *cdev, loff_t count, loff_t offset)
while (count > 0) {
dev_dbg(cdev->dev, "erase 0x%08llx len: 0x%08llx\n", addr, erase.len);
- if (mtd->allow_erasebad || (mtd->master && mtd->master->allow_erasebad))
+ if (mtd->allow_erasebad || (mtd->parent && mtd->parent->allow_erasebad))
ret = 0;
else
ret = mtd_block_isbad(mtd, addr);
@@ -652,10 +652,10 @@ int add_mtd_device(struct mtd_info *mtd, const char *devname, int device_id)
if (ret)
goto err;
- if (mtd->master && !(mtd->cdev.flags & DEVFS_PARTITION_FIXED)) {
+ if (mtd->parent && !(mtd->cdev.flags & DEVFS_PARTITION_FIXED)) {
struct mtd_info *mtdpart;
- list_for_each_entry(mtdpart, &mtd->master->partitions, partitions_entry) {
+ list_for_each_entry(mtdpart, &mtd->parent->partitions, partitions_entry) {
if (mtdpart->master_offset + mtdpart->size <= mtd->master_offset)
continue;
if (mtd->master_offset + mtd->size <= mtdpart->master_offset)
@@ -665,13 +665,13 @@ int add_mtd_device(struct mtd_info *mtd, const char *devname, int device_id)
goto err1;
}
- list_add_sort(&mtd->partitions_entry, &mtd->master->partitions, mtd_part_compare);
+ list_add_sort(&mtd->partitions_entry, &mtd->parent->partitions, mtd_part_compare);
}
if (mtd_can_have_bb(mtd))
mtd->cdev_bb = mtd_add_bb(mtd, NULL);
- if (mtd->dev.parent && !mtd->master) {
+ if (mtd->dev.parent && !mtd->parent) {
dev_add_param_string(&mtd->dev, "partitions", mtd_partition_set, mtd_partition_get, &mtd->partition_string, mtd);
of_parse_partitions(&mtd->cdev, mtd->dev.parent->device_node);
if (IS_ENABLED(CONFIG_OFDEVICE) && mtd->dev.parent->device_node) {
@@ -710,7 +710,7 @@ int del_mtd_device (struct mtd_info *mtd)
unregister_device(&mtd->dev);
free(mtd->param_size.value);
free(mtd->cdev.name);
- if (mtd->master)
+ if (mtd->parent)
list_del(&mtd->partitions_entry);
return 0;
diff --git a/drivers/mtd/mtdoob.c b/drivers/mtd/mtdoob.c
index 3229a1cce5..04e064b227 100644
--- a/drivers/mtd/mtdoob.c
+++ b/drivers/mtd/mtdoob.c
@@ -72,7 +72,7 @@ static int add_mtdoob_device(struct mtd_info *mtd, const char *devname, void **p
{
struct mtdoob *mtdoob;
- if (mtd->master || mtd->oobsize == 0)
+ if (mtd->parent || mtd->oobsize == 0)
return 0;
mtdoob = xzalloc(sizeof(*mtdoob));
@@ -92,7 +92,7 @@ static int del_mtdoob_device(struct mtd_info *mtd, void **priv)
{
struct mtdoob *mtdoob;
- if (mtd->master || mtd->oobsize == 0)
+ if (mtd->parent || mtd->oobsize == 0)
return 0;
mtdoob = *priv;
diff --git a/drivers/mtd/mtdraw.c b/drivers/mtd/mtdraw.c
index 6bebe0409e..19a24cc650 100644
--- a/drivers/mtd/mtdraw.c
+++ b/drivers/mtd/mtdraw.c
@@ -297,7 +297,7 @@ static int add_mtdraw_device(struct mtd_info *mtd, const char *devname, void **p
{
struct mtdraw *mtdraw;
- if (mtd->master || mtd->oobsize == 0)
+ if (mtd->parent || mtd->oobsize == 0)
return 0;
mtdraw = xzalloc(sizeof(*mtdraw));
@@ -321,7 +321,7 @@ static int del_mtdraw_device(struct mtd_info *mtd, void **priv)
{
struct mtdraw *mtdraw;
- if (mtd->master || mtd->oobsize == 0)
+ if (mtd->parent || mtd->oobsize == 0)
return 0;
mtdraw = *priv;
diff --git a/drivers/mtd/partition.c b/drivers/mtd/partition.c
index 6a6988432c..917122a844 100644
--- a/drivers/mtd/partition.c
+++ b/drivers/mtd/partition.c
@@ -13,7 +13,7 @@ static int mtd_part_read(struct mtd_info *mtd, loff_t from, size_t len,
len = 0;
else if (from + len > mtd->size)
len = mtd->size - from;
- res = mtd->master->_read(mtd->master, from + mtd->master_offset,
+ res = mtd->parent->_read(mtd->parent, from + mtd->master_offset,
len, retlen, buf);
return res;
}
@@ -28,7 +28,7 @@ static int mtd_part_read_oob(struct mtd_info *mtd, loff_t from,
if (ops->datbuf && from + ops->len > mtd->size)
return -EINVAL;
- res = mtd->master->_read_oob(mtd->master, from + mtd->master_offset, ops);
+ res = mtd->parent->_read_oob(mtd->parent, from + mtd->master_offset, ops);
if (unlikely(res)) {
if (mtd_is_bitflip(res))
mtd->ecc_stats.corrected++;
@@ -47,7 +47,7 @@ static int mtd_part_write(struct mtd_info *mtd, loff_t to, size_t len,
len = 0;
else if (to + len > mtd->size)
len = mtd->size - to;
- return mtd->master->_write(mtd->master, to + mtd->master_offset,
+ return mtd->parent->_write(mtd->parent, to + mtd->master_offset,
len, retlen, buf);
}
@@ -58,7 +58,7 @@ static int mtd_part_write_oob(struct mtd_info *mtd, loff_t to,
return -EINVAL;
if (ops->datbuf && to + ops->len > mtd->size)
return -EINVAL;
- return mtd->master->_write_oob(mtd->master, to + mtd->master_offset, ops);
+ return mtd->parent->_write_oob(mtd->parent, to + mtd->master_offset, ops);
}
static int mtd_part_erase(struct mtd_info *mtd, struct erase_info *instr)
@@ -70,7 +70,7 @@ static int mtd_part_erase(struct mtd_info *mtd, struct erase_info *instr)
if (instr->addr >= mtd->size)
return -EINVAL;
instr->addr += mtd->master_offset;
- ret = mtd->master->_erase(mtd->master, instr);
+ ret = mtd->parent->_erase(mtd->parent, instr);
if (ret) {
if (instr->fail_addr != 0xffffffff)
instr->fail_addr -= mtd->master_offset;
@@ -81,7 +81,7 @@ static int mtd_part_erase(struct mtd_info *mtd, struct erase_info *instr)
static int mtd_part_lock(struct mtd_info *mtd, loff_t offset, size_t len)
{
- if (!mtd->master->_lock)
+ if (!mtd->parent->_lock)
return -ENOSYS;
if (!(mtd->flags & MTD_WRITEABLE))
@@ -92,12 +92,12 @@ static int mtd_part_lock(struct mtd_info *mtd, loff_t offset, size_t len)
offset += mtd->master_offset;
- return mtd->master->_lock(mtd->master, offset, len);
+ return mtd->parent->_lock(mtd->parent, offset, len);
}
static int mtd_part_unlock(struct mtd_info *mtd, loff_t offset, size_t len)
{
- if (!mtd->master->_unlock)
+ if (!mtd->parent->_unlock)
return -ENOSYS;
if (!(mtd->flags & MTD_WRITEABLE))
@@ -108,7 +108,7 @@ static int mtd_part_unlock(struct mtd_info *mtd, loff_t offset, size_t len)
offset += mtd->master_offset;
- return mtd->master->_unlock(mtd->master, offset, len);
+ return mtd->parent->_unlock(mtd->parent, offset, len);
}
static int mtd_part_block_isbad(struct mtd_info *mtd, loff_t ofs)
@@ -116,7 +116,7 @@ static int mtd_part_block_isbad(struct mtd_info *mtd, loff_t ofs)
if (ofs >= mtd->size)
return -EINVAL;
ofs += mtd->master_offset;
- return mtd_block_isbad(mtd->master, ofs);
+ return mtd_block_isbad(mtd->parent, ofs);
}
static int mtd_part_block_markbad(struct mtd_info *mtd, loff_t ofs)
@@ -128,7 +128,7 @@ static int mtd_part_block_markbad(struct mtd_info *mtd, loff_t ofs)
if (ofs >= mtd->size)
return -EINVAL;
ofs += mtd->master_offset;
- res = mtd->master->_block_markbad(mtd->master, ofs);
+ res = mtd->parent->_block_markbad(mtd->parent, ofs);
if (!res)
mtd->ecc_stats.badblocks++;
return res;
@@ -143,7 +143,7 @@ static int mtd_part_block_markgood(struct mtd_info *mtd, loff_t ofs)
if (ofs >= mtd->size)
return -EINVAL;
ofs += mtd->master_offset;
- res = mtd->master->_block_markgood(mtd->master, ofs);
+ res = mtd->parent->_block_markgood(mtd->parent, ofs);
if (!res)
mtd->ecc_stats.badblocks--;
return res;
@@ -216,7 +216,7 @@ struct mtd_info *mtd_add_partition(struct mtd_info *mtd, off_t offset,
part->name = xstrdup(name);
part->master_offset = offset;
- part->master = mtd;
+ part->parent = mtd;
if (!strncmp(mtd->cdev.name, name, strlen(mtd->cdev.name)))
part->cdev.partname = xstrdup(name + strlen(mtd->cdev.name) + 1);
@@ -225,7 +225,7 @@ struct mtd_info *mtd_add_partition(struct mtd_info *mtd, off_t offset,
if (ret)
goto err;
- part->cdev.master = &part->master->cdev;
+ part->cdev.master = &part->parent->cdev;
return part;
err:
@@ -238,7 +238,7 @@ err:
int mtd_del_partition(struct mtd_info *part)
{
- if (!part->master)
+ if (!part->parent)
return -EINVAL;
del_mtd_device(part);
diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c
index 8a6d0ebde0..75fdee3692 100644
--- a/drivers/mtd/ubi/build.c
+++ b/drivers/mtd/ubi/build.c
@@ -241,8 +241,8 @@ static int get_bad_peb_limit(const struct ubi_device *ubi, int max_beb_per1024)
* is that all the bad eraseblocks of the chip are in
* the MTD partition we are attaching (ubi->mtd).
*/
- if (ubi->mtd->master)
- device_size = ubi->mtd->master->size;
+ if (ubi->mtd->parent)
+ device_size = ubi->mtd->parent->size;
else
device_size = ubi->mtd->size;
diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h
index e4ce2ae233..75407a9790 100644
--- a/include/linux/mtd/mtd.h
+++ b/include/linux/mtd/mtd.h
@@ -205,7 +205,13 @@ struct mtd_info {
bool allow_erasebad;
int p_allow_erasebad;
- struct mtd_info *master;
+ /*
+ * Parent device from the MTD partition point of view.
+ *
+ * MTD masters do not have any parent, MTD partitions do. The parent
+ * MTD device can itself be a partition.
+ */
+ struct mtd_info *parent;
loff_t master_offset;
struct list_head partitions;
--
2.20.1
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 27+ messages in thread
* [PATCH 10/26] lib: Add match_string()
2020-11-06 13:38 [PATCH 00/26] Update nand layer to Linux-5.9 Sascha Hauer
` (8 preceding siblings ...)
2020-11-06 13:38 ` [PATCH 09/26] mtd: rename master to parent Sascha Hauer
@ 2020-11-06 13:38 ` Sascha Hauer
2020-11-06 13:38 ` [PATCH 11/26] mtd: nand: move function hooks to struct nand_legacy Sascha Hauer
` (15 subsequent siblings)
25 siblings, 0 replies; 27+ messages in thread
From: Sascha Hauer @ 2020-11-06 13:38 UTC (permalink / raw)
To: Barebox List
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
include/linux/string.h | 2 ++
lib/string.c | 34 ++++++++++++++++++++++++++++++++++
2 files changed, 36 insertions(+)
diff --git a/include/linux/string.h b/include/linux/string.h
index 2b699957e8..e7f471b139 100644
--- a/include/linux/string.h
+++ b/include/linux/string.h
@@ -135,4 +135,6 @@ static inline void *kmemdup(const void *src, size_t len, gfp_t gfp)
extern int kstrtobool(const char *s, bool *res);
+int match_string(const char * const *array, size_t n, const char *string);
+
#endif /* _LINUX_STRING_H_ */
diff --git a/lib/string.c b/lib/string.c
index 003070fa53..2d0a99eab7 100644
--- a/lib/string.c
+++ b/lib/string.c
@@ -852,3 +852,37 @@ int strtobool(const char *str, int *val)
return -EINVAL;
}
EXPORT_SYMBOL(strtobool);
+
+/**
+ * match_string - matches given string in an array
+ * @array: array of strings
+ * @n: number of strings in the array or -1 for NULL terminated arrays
+ * @string: string to match with
+ *
+ * This routine will look for a string in an array of strings up to the
+ * n-th element in the array or until the first NULL element.
+ *
+ * Historically the value of -1 for @n, was used to search in arrays that
+ * are NULL terminated. However, the function does not make a distinction
+ * when finishing the search: either @n elements have been compared OR
+ * the first NULL element was found.
+ *
+ * Return:
+ * index of a @string in the @array if matches, or %-EINVAL otherwise.
+ */
+int match_string(const char * const *array, size_t n, const char *string)
+{
+ int index;
+ const char *item;
+
+ for (index = 0; index < n; index++) {
+ item = array[index];
+ if (!item)
+ break;
+ if (!strcmp(item, string))
+ return index;
+ }
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL(match_string);
--
2.20.1
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 27+ messages in thread
* [PATCH 11/26] mtd: nand: move function hooks to struct nand_legacy
2020-11-06 13:38 [PATCH 00/26] Update nand layer to Linux-5.9 Sascha Hauer
` (9 preceding siblings ...)
2020-11-06 13:38 ` [PATCH 10/26] lib: Add match_string() Sascha Hauer
@ 2020-11-06 13:38 ` Sascha Hauer
2020-11-06 13:38 ` [PATCH 12/26] mtd: Add ecc_step_size Sascha Hauer
` (14 subsequent siblings)
25 siblings, 0 replies; 27+ messages in thread
From: Sascha Hauer @ 2020-11-06 13:38 UTC (permalink / raw)
To: Barebox List
Linux had moved the traditional nand function hooks to an
extra struct nand_legacy. Do the same in barebox for compatibility.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
drivers/mtd/nand/atmel_nand.c | 44 +--
drivers/mtd/nand/nand_base.c | 426 +++++++++++++++---------------
drivers/mtd/nand/nand_bbt.c | 2 +-
drivers/mtd/nand/nand_denali.c | 10 +-
drivers/mtd/nand/nand_imx.c | 22 +-
drivers/mtd/nand/nand_mrvl_nfc.c | 32 +--
drivers/mtd/nand/nand_mxs.c | 40 +--
drivers/mtd/nand/nand_omap_gpmc.c | 40 +--
drivers/mtd/nand/nand_orion.c | 12 +-
drivers/mtd/nand/nand_s3c24xx.c | 14 +-
drivers/mtd/nand/nomadik_nand.c | 9 +-
include/linux/mtd/nand.h | 96 ++++---
12 files changed, 379 insertions(+), 368 deletions(-)
diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c
index 90656bcb5d..d86498c86e 100644
--- a/drivers/mtd/nand/atmel_nand.c
+++ b/drivers/mtd/nand/atmel_nand.c
@@ -161,22 +161,22 @@ static int atmel_nand_device_ready(struct nand_chip *nand_chip)
*/
static void atmel_read_buf(struct nand_chip *nand_chip, u8 *buf, int len)
{
- readsb(nand_chip->IO_ADDR_R, buf, len);
+ readsb(nand_chip->legacy.IO_ADDR_R, buf, len);
}
static void atmel_read_buf16(struct nand_chip *nand_chip, u8 *buf, int len)
{
- readsw(nand_chip->IO_ADDR_R, buf, len / 2);
+ readsw(nand_chip->legacy.IO_ADDR_R, buf, len / 2);
}
static void atmel_write_buf(struct nand_chip *nand_chip, const u8 *buf, int len)
{
- writesb(nand_chip->IO_ADDR_W, buf, len);
+ writesb(nand_chip->legacy.IO_ADDR_W, buf, len);
}
static void atmel_write_buf16(struct nand_chip *nand_chip, const u8 *buf, int len)
{
- writesw(nand_chip->IO_ADDR_W, buf, len / 2);
+ writesw(nand_chip->legacy.IO_ADDR_W, buf, len / 2);
}
/*
@@ -636,8 +636,8 @@ static int atmel_nand_pmecc_read_page(struct nand_chip *chip, uint8_t *buf,
pmecc_writel(host->ecc, CTRL, PMECC_CTRL_ENABLE);
pmecc_writel(host->ecc, CTRL, PMECC_CTRL_DATA);
- chip->read_buf(chip, buf, eccsize);
- chip->read_buf(chip, oob, mtd->oobsize);
+ chip->legacy.read_buf(chip, buf, eccsize);
+ chip->legacy.read_buf(chip, oob, mtd->oobsize);
ret = wait_on_timeout(PMECC_MAX_TIMEOUT_MS,
!(pmecc_readl_relaxed(host->ecc, SR) & PMECC_SR_BUSY));
@@ -671,7 +671,7 @@ static int atmel_nand_pmecc_write_page(struct nand_chip *chip, const uint8_t *bu
pmecc_writel(host->ecc, CTRL, PMECC_CTRL_ENABLE);
pmecc_writel(host->ecc, CTRL, PMECC_CTRL_DATA);
- chip->write_buf(chip, (u8 *)buf, mtd->writesize);
+ chip->legacy.write_buf(chip, (u8 *)buf, mtd->writesize);
ret = wait_on_timeout(PMECC_MAX_TIMEOUT_MS,
!(pmecc_readl_relaxed(host->ecc, SR) & PMECC_SR_BUSY));
@@ -689,7 +689,7 @@ static int atmel_nand_pmecc_write_page(struct nand_chip *chip, const uint8_t *bu
pmecc_readb_ecc_relaxed(host->ecc, i, j);
}
}
- chip->write_buf(chip, chip->oob_poi, mtd->oobsize);
+ chip->legacy.write_buf(chip, chip->oob_poi, mtd->oobsize);
return 0;
}
@@ -1026,7 +1026,7 @@ static int atmel_nand_read_page(struct nand_chip *chip, uint8_t *buf,
#endif
/* read the page */
- chip->read_buf(chip, p, eccsize);
+ chip->legacy.read_buf(chip, p, eccsize);
/* move to ECC position if needed */
if (eccpos[0] != 0) {
@@ -1036,13 +1036,13 @@ static int atmel_nand_read_page(struct nand_chip *chip, uint8_t *buf,
* NAND_CMD_RNDOUT.
* anyway, for small pages, the eccpos[0] == 0
*/
- chip->cmdfunc(chip, NAND_CMD_RNDOUT,
+ chip->legacy.cmdfunc(chip, NAND_CMD_RNDOUT,
mtd->writesize + eccpos[0], -1);
}
/* the ECC controller needs to read the ECC just after the data */
ecc_pos = oob + eccpos[0];
- chip->read_buf(chip, ecc_pos, eccbytes);
+ chip->legacy.read_buf(chip, ecc_pos, eccbytes);
/* check if there's an error */
stat = chip->ecc.correct(chip, p, oob, NULL);
@@ -1053,10 +1053,10 @@ static int atmel_nand_read_page(struct nand_chip *chip, uint8_t *buf,
mtd->ecc_stats.corrected += stat;
/* get back to oob start (end of page) */
- chip->cmdfunc(chip, NAND_CMD_RNDOUT, mtd->writesize, -1);
+ chip->legacy.cmdfunc(chip, NAND_CMD_RNDOUT, mtd->writesize, -1);
/* read the oob */
- chip->read_buf(chip, oob, mtd->oobsize);
+ chip->legacy.read_buf(chip, oob, mtd->oobsize);
return 0;
}
@@ -1334,9 +1334,9 @@ static int __init atmel_nand_probe(struct device_d *dev)
mtd->dev.parent = dev;
/* Set address of NAND IO lines */
- nand_chip->IO_ADDR_R = host->io_base;
- nand_chip->IO_ADDR_W = host->io_base;
- nand_chip->cmd_ctrl = atmel_nand_cmd_ctrl;
+ nand_chip->legacy.IO_ADDR_R = host->io_base;
+ nand_chip->legacy.IO_ADDR_W = host->io_base;
+ nand_chip->legacy.cmd_ctrl = atmel_nand_cmd_ctrl;
if (gpio_is_valid(host->board->rdy_pin)) {
res = gpio_request(host->board->rdy_pin, "nand_rdy");
@@ -1354,7 +1354,7 @@ static int __init atmel_nand_probe(struct device_d *dev)
goto err_no_card;
}
- nand_chip->dev_ready = atmel_nand_device_ready;
+ nand_chip->legacy.dev_ready = atmel_nand_device_ready;
}
if (gpio_is_valid(host->board->enable_pin)) {
@@ -1384,7 +1384,7 @@ static int __init atmel_nand_probe(struct device_d *dev)
nand_chip->ecc.mode = NAND_ECC_HW;
}
- nand_chip->chip_delay = 40; /* 40us command delay time */
+ nand_chip->legacy.chip_delay = 40; /* 40us command delay time */
if (IS_ENABLED(CONFIG_NAND_ECC_BCH) &&
pdata->ecc_mode == NAND_ECC_SOFT_BCH) {
@@ -1393,11 +1393,11 @@ static int __init atmel_nand_probe(struct device_d *dev)
if (host->board->bus_width_16) { /* 16-bit bus width */
nand_chip->options |= NAND_BUSWIDTH_16;
- nand_chip->read_buf = atmel_read_buf16;
- nand_chip->write_buf = atmel_write_buf16;
+ nand_chip->legacy.read_buf = atmel_read_buf16;
+ nand_chip->legacy.write_buf = atmel_write_buf16;
} else {
- nand_chip->read_buf = atmel_read_buf;
- nand_chip->write_buf = atmel_write_buf;
+ nand_chip->legacy.read_buf = atmel_read_buf;
+ nand_chip->legacy.write_buf = atmel_write_buf;
}
atmel_nand_enable(host);
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index f869c21705..1816822e78 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -135,7 +135,7 @@ static void nand_release_device(struct nand_chip *chip)
*/
static uint8_t nand_read_byte(struct nand_chip *chip)
{
- return readb(chip->IO_ADDR_R);
+ return readb(chip->legacy.IO_ADDR_R);
}
/**
@@ -148,7 +148,7 @@ static uint8_t nand_read_byte(struct nand_chip *chip)
*/
static uint8_t nand_read_byte16(struct nand_chip *chip)
{
- return (uint8_t) cpu_to_le16(readw(chip->IO_ADDR_R));
+ return (uint8_t) cpu_to_le16(readw(chip->legacy.IO_ADDR_R));
}
/**
@@ -159,7 +159,7 @@ static uint8_t nand_read_byte16(struct nand_chip *chip)
*/
static u16 nand_read_word(struct nand_chip *chip)
{
- return readw(chip->IO_ADDR_R);
+ return readw(chip->legacy.IO_ADDR_R);
}
/**
@@ -173,7 +173,7 @@ static void nand_select_chip(struct nand_chip *chip, int chipnr)
{
switch (chipnr) {
case -1:
- chip->cmd_ctrl(chip, NAND_CMD_NONE, 0 | NAND_CTRL_CHANGE);
+ chip->legacy.cmd_ctrl(chip, NAND_CMD_NONE, 0 | NAND_CTRL_CHANGE);
break;
case 0:
break;
@@ -197,7 +197,7 @@ static __maybe_unused void nand_write_buf(struct nand_chip *chip,
int i;
for (i = 0; i < len; i++)
- writeb(buf[i], chip->IO_ADDR_W);
+ writeb(buf[i], chip->legacy.IO_ADDR_W);
}
/**
@@ -213,7 +213,7 @@ static void nand_read_buf(struct nand_chip *chip, uint8_t *buf, int len)
int i;
for (i = 0; i < len; i++)
- buf[i] = readb(chip->IO_ADDR_R);
+ buf[i] = readb(chip->legacy.IO_ADDR_R);
}
/**
@@ -232,7 +232,7 @@ static __maybe_unused void nand_write_buf16(struct nand_chip *chip,
len >>= 1;
for (i = 0; i < len; i++)
- writew(p[i], chip->IO_ADDR_W);
+ writew(p[i], chip->legacy.IO_ADDR_W);
}
@@ -251,7 +251,7 @@ static void nand_read_buf16(struct nand_chip *chip, uint8_t *buf, int len)
len >>= 1;
for (i = 0; i < len; i++)
- p[i] = readw(chip->IO_ADDR_R);
+ p[i] = readw(chip->legacy.IO_ADDR_R);
}
/**
@@ -278,22 +278,22 @@ static int nand_block_bad(struct nand_chip *chip, loff_t ofs, int getchip)
nand_get_device(chip, FL_READING);
/* Select the NAND device */
- chip->select_chip(chip, chipnr);
+ chip->legacy.select_chip(chip, chipnr);
}
do {
if (chip->options & NAND_BUSWIDTH_16) {
- chip->cmdfunc(chip, NAND_CMD_READOOB,
+ chip->legacy.cmdfunc(chip, NAND_CMD_READOOB,
chip->badblockpos & 0xFE, page);
- bad = cpu_to_le16(chip->read_word(chip));
+ bad = cpu_to_le16(chip->legacy.read_word(chip));
if (chip->badblockpos & 0x1)
bad >>= 8;
else
bad &= 0xFF;
} else {
- chip->cmdfunc(chip, NAND_CMD_READOOB, chip->badblockpos,
+ chip->legacy.cmdfunc(chip, NAND_CMD_READOOB, chip->badblockpos,
page);
- bad = chip->read_byte(chip);
+ bad = chip->legacy.read_byte(chip);
}
if (likely(chip->badblockbits == 8))
@@ -306,7 +306,7 @@ static int nand_block_bad(struct nand_chip *chip, loff_t ofs, int getchip)
} while (!res && i < 2 && (chip->bbt_options & NAND_BBT_SCAN2NDPAGE));
if (getchip) {
- chip->select_chip(chip, -1);
+ chip->legacy.select_chip(chip, -1);
nand_release_device(chip);
}
@@ -361,7 +361,7 @@ static __maybe_unused int nand_default_block_markbad(struct nand_chip *chip, lof
*
* This function performs the generic NAND bad block marking steps (i.e., bad
* block table(s) and/or marker(s)). We only allow the hardware driver to
- * specify how to write bad block markers to OOB (chip->block_markbad).
+ * specify how to write bad block markers to OOB (chip->legacy.block_markbad).
*
* We try operations in the following order:
* (1) erase the affected block, to allow OOB marker to be written cleanly
@@ -387,7 +387,7 @@ static int nand_block_markbad_lowlevel(struct nand_chip *chip, loff_t ofs)
/* Write bad block marker to OOB */
nand_get_device(chip, FL_WRITING);
- ret = chip->block_markbad(chip, ofs);
+ ret = chip->legacy.block_markbad(chip, ofs);
nand_release_device(chip);
}
@@ -436,7 +436,7 @@ static int nand_block_markgood_lowlevel(struct nand_chip *chip, loff_t ofs)
* Verify erase succeded. We need to select chip again,
* as nand_erase_nand deselected it.
*/
- ret = chip->block_bad(chip, ofs, 1);
+ ret = chip->legacy.block_bad(chip, ofs, 1);
if (ret)
return ret;
}
@@ -468,8 +468,8 @@ static int nand_check_wp(struct nand_chip *chip)
return 0;
/* Check the WP bit */
- chip->cmdfunc(chip, NAND_CMD_STATUS, -1, -1);
- return (chip->read_byte(chip) & NAND_STATUS_WP) ? 0 : 1;
+ chip->legacy.cmdfunc(chip, NAND_CMD_STATUS, -1, -1);
+ return (chip->legacy.read_byte(chip) & NAND_STATUS_WP) ? 0 : 1;
}
/**
@@ -490,7 +490,7 @@ static int nand_block_checkbad(struct nand_chip *chip, loff_t ofs, int getchip,
return nand_isbad_bbt(chip, ofs, allowbbt);
}
- return chip->block_bad(chip, ofs, getchip);
+ return chip->legacy.block_bad(chip, ofs, getchip);
}
/* Wait for the ready pin, after a command. The timeout is caught later. */
@@ -500,7 +500,7 @@ void nand_wait_ready(struct nand_chip *chip)
/* wait until command is processed or timeout occures */
do {
- if (chip->dev_ready(chip))
+ if (chip->legacy.dev_ready(chip))
break;
} while (!is_timeout(start, SECOND * 2));
}
@@ -535,10 +535,10 @@ static void nand_command(struct nand_chip *chip, unsigned int command,
column -= 256;
readcmd = NAND_CMD_READ1;
}
- chip->cmd_ctrl(chip, readcmd, ctrl);
+ chip->legacy.cmd_ctrl(chip, readcmd, ctrl);
ctrl &= ~NAND_CTRL_CHANGE;
}
- chip->cmd_ctrl(chip, command, ctrl);
+ chip->legacy.cmd_ctrl(chip, command, ctrl);
/* Address cycle, when necessary */
ctrl = NAND_CTRL_ALE | NAND_CTRL_CHANGE;
@@ -547,18 +547,18 @@ static void nand_command(struct nand_chip *chip, unsigned int command,
/* Adjust columns for 16 bit buswidth */
if (chip->options & NAND_BUSWIDTH_16)
column >>= 1;
- chip->cmd_ctrl(chip, column, ctrl);
+ chip->legacy.cmd_ctrl(chip, column, ctrl);
ctrl &= ~NAND_CTRL_CHANGE;
}
if (page_addr != -1) {
- chip->cmd_ctrl(chip, page_addr, ctrl);
+ chip->legacy.cmd_ctrl(chip, page_addr, ctrl);
ctrl &= ~NAND_CTRL_CHANGE;
- chip->cmd_ctrl(chip, page_addr >> 8, ctrl);
+ chip->legacy.cmd_ctrl(chip, page_addr >> 8, ctrl);
/* One more address cycle for devices > 32MiB */
if (chip->chipsize > (32 << 20))
- chip->cmd_ctrl(chip, page_addr >> 16, ctrl);
+ chip->legacy.cmd_ctrl(chip, page_addr >> 16, ctrl);
}
- chip->cmd_ctrl(chip, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
+ chip->legacy.cmd_ctrl(chip, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
/*
* Program and erase have their own busy handlers status and sequential
@@ -574,14 +574,14 @@ static void nand_command(struct nand_chip *chip, unsigned int command,
return;
case NAND_CMD_RESET:
- if (chip->dev_ready)
+ if (chip->legacy.dev_ready)
break;
- udelay(chip->chip_delay);
- chip->cmd_ctrl(chip, NAND_CMD_STATUS,
+ udelay(chip->legacy.chip_delay);
+ chip->legacy.cmd_ctrl(chip, NAND_CMD_STATUS,
NAND_CTRL_CLE | NAND_CTRL_CHANGE);
- chip->cmd_ctrl(chip,
+ chip->legacy.cmd_ctrl(chip,
NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
- while (!(chip->read_byte(chip) & NAND_STATUS_READY))
+ while (!(chip->legacy.read_byte(chip) & NAND_STATUS_READY))
;
return;
@@ -591,8 +591,8 @@ static void nand_command(struct nand_chip *chip, unsigned int command,
* If we don't have access to the busy pin, we apply the given
* command delay
*/
- if (!chip->dev_ready) {
- udelay(chip->chip_delay);
+ if (!chip->legacy.dev_ready) {
+ udelay(chip->legacy.chip_delay);
return;
}
}
@@ -626,7 +626,7 @@ static void nand_command_lp(struct nand_chip *chip, unsigned int command,
}
/* Command latch cycle */
- chip->cmd_ctrl(chip, command, NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
+ chip->legacy.cmd_ctrl(chip, command, NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
if (column != -1 || page_addr != -1) {
int ctrl = NAND_CTRL_CHANGE | NAND_NCE | NAND_ALE;
@@ -636,21 +636,21 @@ static void nand_command_lp(struct nand_chip *chip, unsigned int command,
/* Adjust columns for 16 bit buswidth */
if (chip->options & NAND_BUSWIDTH_16)
column >>= 1;
- chip->cmd_ctrl(chip, column, ctrl);
+ chip->legacy.cmd_ctrl(chip, column, ctrl);
ctrl &= ~NAND_CTRL_CHANGE;
- chip->cmd_ctrl(chip, column >> 8, ctrl);
+ chip->legacy.cmd_ctrl(chip, column >> 8, ctrl);
}
if (page_addr != -1) {
- chip->cmd_ctrl(chip, page_addr, ctrl);
- chip->cmd_ctrl(chip, page_addr >> 8,
+ chip->legacy.cmd_ctrl(chip, page_addr, ctrl);
+ chip->legacy.cmd_ctrl(chip, page_addr >> 8,
NAND_NCE | NAND_ALE);
/* One more address cycle for devices > 128MiB */
if (chip->chipsize > (128 << 20))
- chip->cmd_ctrl(chip, page_addr >> 16,
+ chip->legacy.cmd_ctrl(chip, page_addr >> 16,
NAND_NCE | NAND_ALE);
}
}
- chip->cmd_ctrl(chip, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
+ chip->legacy.cmd_ctrl(chip, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
/*
* Program and erase have their own busy handlers status, sequential
@@ -668,29 +668,29 @@ static void nand_command_lp(struct nand_chip *chip, unsigned int command,
return;
case NAND_CMD_RESET:
- if (chip->dev_ready)
+ if (chip->legacy.dev_ready)
break;
- udelay(chip->chip_delay);
- chip->cmd_ctrl(chip, NAND_CMD_STATUS,
+ udelay(chip->legacy.chip_delay);
+ chip->legacy.cmd_ctrl(chip, NAND_CMD_STATUS,
NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
- chip->cmd_ctrl(chip, NAND_CMD_NONE,
+ chip->legacy.cmd_ctrl(chip, NAND_CMD_NONE,
NAND_NCE | NAND_CTRL_CHANGE);
- while (!(chip->read_byte(chip) & NAND_STATUS_READY))
+ while (!(chip->legacy.read_byte(chip) & NAND_STATUS_READY))
;
return;
case NAND_CMD_RNDOUT:
/* No ready / busy check necessary */
- chip->cmd_ctrl(chip, NAND_CMD_RNDOUTSTART,
+ chip->legacy.cmd_ctrl(chip, NAND_CMD_RNDOUTSTART,
NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
- chip->cmd_ctrl(chip, NAND_CMD_NONE,
+ chip->legacy.cmd_ctrl(chip, NAND_CMD_NONE,
NAND_NCE | NAND_CTRL_CHANGE);
return;
case NAND_CMD_READ0:
- chip->cmd_ctrl(chip, NAND_CMD_READSTART,
+ chip->legacy.cmd_ctrl(chip, NAND_CMD_READSTART,
NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
- chip->cmd_ctrl(chip, NAND_CMD_NONE,
+ chip->legacy.cmd_ctrl(chip, NAND_CMD_NONE,
NAND_NCE | NAND_CTRL_CHANGE);
/* This applies to read commands */
@@ -699,8 +699,8 @@ static void nand_command_lp(struct nand_chip *chip, unsigned int command,
* If we don't have access to the busy pin, we apply the given
* command delay.
*/
- if (!chip->dev_ready) {
- udelay(chip->chip_delay);
+ if (!chip->legacy.dev_ready) {
+ udelay(chip->legacy.chip_delay);
return;
}
}
@@ -768,19 +768,19 @@ static int nand_wait(struct nand_chip *chip)
* any case on any machine. */
ndelay(100);
- chip->cmdfunc(chip, NAND_CMD_STATUS, -1, -1);
+ chip->legacy.cmdfunc(chip, NAND_CMD_STATUS, -1, -1);
while (!is_timeout(start, timeo)) {
- if (chip->dev_ready) {
- if (chip->dev_ready(chip))
+ if (chip->legacy.dev_ready) {
+ if (chip->legacy.dev_ready(chip))
break;
} else {
- if (chip->read_byte(chip) & NAND_STATUS_READY)
+ if (chip->legacy.read_byte(chip) & NAND_STATUS_READY)
break;
}
}
- status = (int)chip->read_byte(chip);
+ status = (int)chip->legacy.read_byte(chip);
return status;
}
@@ -804,15 +804,15 @@ static int __nand_unlock(struct nand_chip *chip, loff_t ofs,
/* Submit address of first page to unlock */
page = ofs >> chip->page_shift;
- chip->cmdfunc(chip, NAND_CMD_UNLOCK1, -1, page & chip->pagemask);
+ chip->legacy.cmdfunc(chip, NAND_CMD_UNLOCK1, -1, page & chip->pagemask);
/* Submit address of last page to unlock */
page = (ofs + len) >> chip->page_shift;
- chip->cmdfunc(chip, NAND_CMD_UNLOCK2, -1,
+ chip->legacy.cmdfunc(chip, NAND_CMD_UNLOCK2, -1,
(page | invert) & chip->pagemask);
/* Call wait ready function */
- status = chip->waitfunc(chip);
+ status = chip->legacy.waitfunc(chip);
/* See if device thinks it succeeded */
if (status & NAND_STATUS_FAIL) {
pr_debug("%s: error status = 0x%08x\n",
@@ -852,7 +852,7 @@ int nand_unlock(struct nand_chip *chip, loff_t ofs, uint64_t len)
/* Shift to get chip number */
chipnr = ofs >> chip->chip_shift;
- chip->select_chip(chip, chipnr);
+ chip->legacy.select_chip(chip, chipnr);
/*
* Reset the chip.
@@ -861,7 +861,7 @@ int nand_unlock(struct nand_chip *chip, loff_t ofs, uint64_t len)
* some operation can also clear the bit 7 of status register
* eg. erase/program a locked block
*/
- chip->cmdfunc(chip, NAND_CMD_RESET, -1, -1);
+ chip->legacy.cmdfunc(chip, NAND_CMD_RESET, -1, -1);
/* Check, if it is write protected */
if (nand_check_wp(chip)) {
@@ -874,7 +874,7 @@ int nand_unlock(struct nand_chip *chip, loff_t ofs, uint64_t len)
ret = __nand_unlock(chip, ofs, len, 0);
out:
- chip->select_chip(chip, -1);
+ chip->legacy.select_chip(chip, -1);
nand_release_device(chip);
return ret;
@@ -910,7 +910,7 @@ int nand_lock(struct nand_chip *chip, loff_t ofs, uint64_t len)
/* Shift to get chip number */
chipnr = ofs >> chip->chip_shift;
- chip->select_chip(chip, chipnr);
+ chip->legacy.select_chip(chip, chipnr);
/*
* Reset the chip.
@@ -919,7 +919,7 @@ int nand_lock(struct nand_chip *chip, loff_t ofs, uint64_t len)
* some operation can also clear the bit 7 of status register
* eg. erase/program a locked block
*/
- chip->cmdfunc(chip, NAND_CMD_RESET, -1, -1);
+ chip->legacy.cmdfunc(chip, NAND_CMD_RESET, -1, -1);
/* Check, if it is write protected */
if (nand_check_wp(chip)) {
@@ -931,10 +931,10 @@ int nand_lock(struct nand_chip *chip, loff_t ofs, uint64_t len)
/* Submit address of first page to lock */
page = ofs >> chip->page_shift;
- chip->cmdfunc(chip, NAND_CMD_LOCK, -1, page & chip->pagemask);
+ chip->legacy.cmdfunc(chip, NAND_CMD_LOCK, -1, page & chip->pagemask);
/* Call wait ready function */
- status = chip->waitfunc(chip);
+ status = chip->legacy.waitfunc(chip);
/* See if device thinks it succeeded */
if (status & NAND_STATUS_FAIL) {
pr_debug("%s: error status = 0x%08x\n",
@@ -946,7 +946,7 @@ int nand_lock(struct nand_chip *chip, loff_t ofs, uint64_t len)
ret = __nand_unlock(chip, ofs, len, 0x1);
out:
- chip->select_chip(chip, -1);
+ chip->legacy.select_chip(chip, -1);
nand_release_device(chip);
return ret;
@@ -1095,9 +1095,9 @@ static __maybe_unused int nand_read_page_raw(struct nand_chip *chip,
uint8_t *buf, int oob_required,
int page)
{
- chip->read_buf(chip, buf, chip->mtd.writesize);
+ chip->legacy.read_buf(chip, buf, chip->mtd.writesize);
if (oob_required)
- chip->read_buf(chip, chip->oob_poi, chip->mtd.oobsize);
+ chip->legacy.read_buf(chip, chip->oob_poi, chip->mtd.oobsize);
return 0;
}
@@ -1120,26 +1120,26 @@ static __maybe_unused int nand_read_page_raw_syndrome(struct nand_chip *chip,
int steps, size;
for (steps = chip->ecc.steps; steps > 0; steps--) {
- chip->read_buf(chip, buf, eccsize);
+ chip->legacy.read_buf(chip, buf, eccsize);
buf += eccsize;
if (chip->ecc.prepad) {
- chip->read_buf(chip, oob, chip->ecc.prepad);
+ chip->legacy.read_buf(chip, oob, chip->ecc.prepad);
oob += chip->ecc.prepad;
}
- chip->read_buf(chip, oob, eccbytes);
+ chip->legacy.read_buf(chip, oob, eccbytes);
oob += eccbytes;
if (chip->ecc.postpad) {
- chip->read_buf(chip, oob, chip->ecc.postpad);
+ chip->legacy.read_buf(chip, oob, chip->ecc.postpad);
oob += chip->ecc.postpad;
}
}
size = chip->mtd.oobsize - (oob - chip->oob_poi);
if (size)
- chip->read_buf(chip, oob, size);
+ chip->legacy.read_buf(chip, oob, size);
return 0;
}
@@ -1229,10 +1229,10 @@ static __maybe_unused int nand_read_subpage(struct nand_chip *chip,
data_col_addr = start_step * chip->ecc.size;
/* If we read not a page aligned data */
if (data_col_addr != 0)
- chip->cmdfunc(chip, NAND_CMD_RNDOUT, data_col_addr, -1);
+ chip->legacy.cmdfunc(chip, NAND_CMD_RNDOUT, data_col_addr, -1);
p = bufpoi + data_col_addr;
- chip->read_buf(chip, p, datafrag_len);
+ chip->legacy.read_buf(chip, p, datafrag_len);
/* Calculate ECC */
for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size)
@@ -1250,8 +1250,8 @@ static __maybe_unused int nand_read_subpage(struct nand_chip *chip,
}
}
if (gaps) {
- chip->cmdfunc(chip, NAND_CMD_RNDOUT, mtd->writesize, -1);
- chip->read_buf(chip, chip->oob_poi, mtd->oobsize);
+ chip->legacy.cmdfunc(chip, NAND_CMD_RNDOUT, mtd->writesize, -1);
+ chip->legacy.read_buf(chip, chip->oob_poi, mtd->oobsize);
} else {
/*
* Send the command to read the particular ECC bytes take care
@@ -1266,9 +1266,9 @@ static __maybe_unused int nand_read_subpage(struct nand_chip *chip,
if (eccpos[index + (num_steps * chip->ecc.bytes)] & (busw - 1))
aligned_len++;
- chip->cmdfunc(chip, NAND_CMD_RNDOUT,
+ chip->legacy.cmdfunc(chip, NAND_CMD_RNDOUT,
mtd->writesize + aligned_pos, -1);
- chip->read_buf(chip, &chip->oob_poi[aligned_pos], aligned_len);
+ chip->legacy.read_buf(chip, &chip->oob_poi[aligned_pos], aligned_len);
}
for (i = 0; i < eccfrag_len; i++)
@@ -1315,10 +1315,10 @@ static __maybe_unused int nand_read_page_hwecc(struct nand_chip *chip,
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
chip->ecc.hwctl(chip, NAND_ECC_READ);
- chip->read_buf(chip, p, eccsize);
+ chip->legacy.read_buf(chip, p, eccsize);
chip->ecc.calculate(chip, p, &ecc_calc[i]);
}
- chip->read_buf(chip, chip->oob_poi, mtd->oobsize);
+ chip->legacy.read_buf(chip, chip->oob_poi, mtd->oobsize);
for (i = 0; i < chip->ecc.total; i++)
ecc_code[i] = chip->oob_poi[eccpos[i]];
@@ -1368,9 +1368,9 @@ static __maybe_unused int nand_read_page_hwecc_oob_first(struct nand_chip *chip,
unsigned int max_bitflips = 0;
/* Read the OOB area first */
- chip->cmdfunc(chip, NAND_CMD_READOOB, 0, page);
- chip->read_buf(chip, chip->oob_poi, mtd->oobsize);
- chip->cmdfunc(chip, NAND_CMD_READ0, 0, page);
+ chip->legacy.cmdfunc(chip, NAND_CMD_READOOB, 0, page);
+ chip->legacy.read_buf(chip, chip->oob_poi, mtd->oobsize);
+ chip->legacy.cmdfunc(chip, NAND_CMD_READ0, 0, page);
for (i = 0; i < chip->ecc.total; i++)
ecc_code[i] = chip->oob_poi[eccpos[i]];
@@ -1379,7 +1379,7 @@ static __maybe_unused int nand_read_page_hwecc_oob_first(struct nand_chip *chip,
int stat;
chip->ecc.hwctl(chip, NAND_ECC_READ);
- chip->read_buf(chip, p, eccsize);
+ chip->legacy.read_buf(chip, p, eccsize);
chip->ecc.calculate(chip, p, &ecc_calc[i]);
stat = chip->ecc.correct(chip, p, &ecc_code[i], NULL);
@@ -1419,15 +1419,15 @@ static __maybe_unused int nand_read_page_syndrome(struct nand_chip *chip,
int stat;
chip->ecc.hwctl(chip, NAND_ECC_READ);
- chip->read_buf(chip, p, eccsize);
+ chip->legacy.read_buf(chip, p, eccsize);
if (chip->ecc.prepad) {
- chip->read_buf(chip, oob, chip->ecc.prepad);
+ chip->legacy.read_buf(chip, oob, chip->ecc.prepad);
oob += chip->ecc.prepad;
}
chip->ecc.hwctl(chip, NAND_ECC_READSYN);
- chip->read_buf(chip, oob, eccbytes);
+ chip->legacy.read_buf(chip, oob, eccbytes);
stat = chip->ecc.correct(chip, p, oob, NULL);
if (stat < 0) {
@@ -1440,7 +1440,7 @@ static __maybe_unused int nand_read_page_syndrome(struct nand_chip *chip,
oob += eccbytes;
if (chip->ecc.postpad) {
- chip->read_buf(chip, oob, chip->ecc.postpad);
+ chip->legacy.read_buf(chip, oob, chip->ecc.postpad);
oob += chip->ecc.postpad;
}
}
@@ -1448,7 +1448,7 @@ static __maybe_unused int nand_read_page_syndrome(struct nand_chip *chip,
/* Calculate remaining oob bytes */
i = mtd->oobsize - (oob - chip->oob_poi);
if (i)
- chip->read_buf(chip, oob, i);
+ chip->legacy.read_buf(chip, oob, i);
return max_bitflips;
}
@@ -1527,7 +1527,7 @@ static int nand_do_read_ops(struct nand_chip *chip, loff_t from,
stats = mtd->ecc_stats;
chipnr = (int)(from >> chip->chip_shift);
- chip->select_chip(chip, chipnr);
+ chip->legacy.select_chip(chip, chipnr);
realpage = (int)(from >> chip->page_shift);
page = realpage & chip->pagemask;
@@ -1546,7 +1546,7 @@ static int nand_do_read_ops(struct nand_chip *chip, loff_t from,
if (realpage != chip->pagebuf || oob) {
bufpoi = aligned ? buf : chip->buffers->databuf;
- chip->cmdfunc(chip, NAND_CMD_READ0, 0x00, page);
+ chip->legacy.cmdfunc(chip, NAND_CMD_READ0, 0x00, page);
/*
* Now read the page into the buffer. Absent an error,
@@ -1600,8 +1600,8 @@ static int nand_do_read_ops(struct nand_chip *chip, loff_t from,
if (chip->options & NAND_NEED_READRDY) {
/* Apply delay or wait for ready/busy pin */
- if (!chip->dev_ready)
- udelay(chip->chip_delay);
+ if (!chip->legacy.dev_ready)
+ udelay(chip->legacy.chip_delay);
else
nand_wait_ready(chip);
}
@@ -1626,11 +1626,11 @@ static int nand_do_read_ops(struct nand_chip *chip, loff_t from,
/* Check, if we cross a chip boundary */
if (!page) {
chipnr++;
- chip->select_chip(chip, -1);
- chip->select_chip(chip, chipnr);
+ chip->legacy.select_chip(chip, -1);
+ chip->legacy.select_chip(chip, chipnr);
}
}
- chip->select_chip(chip, -1);
+ chip->legacy.select_chip(chip, -1);
ops->retlen = ops->len - (size_t) readlen;
if (oob)
@@ -1685,8 +1685,8 @@ static __maybe_unused int nand_read_oob_std(struct nand_chip *chip, int page)
if (!IS_ENABLED(CONFIG_NAND_READ_OOB))
return -ENOTSUPP;
- chip->cmdfunc(chip, NAND_CMD_READOOB, 0, page);
- chip->read_buf(chip, chip->oob_poi, chip->mtd.oobsize);
+ chip->legacy.cmdfunc(chip, NAND_CMD_READOOB, 0, page);
+ chip->legacy.read_buf(chip, chip->oob_poi, chip->mtd.oobsize);
return 0;
}
@@ -1710,23 +1710,23 @@ static __maybe_unused int nand_read_oob_syndrome(struct nand_chip *chip,
if (!IS_ENABLED(CONFIG_NAND_READ_OOB))
return -ENOTSUPP;
- chip->cmdfunc(chip, NAND_CMD_READ0, chip->ecc.size, page);
+ chip->legacy.cmdfunc(chip, NAND_CMD_READ0, chip->ecc.size, page);
for (i = 0; i < chip->ecc.steps; i++) {
if (sndrnd) {
pos = eccsize + i * (eccsize + chunk);
if (chip->mtd.writesize > 512)
- chip->cmdfunc(chip, NAND_CMD_RNDOUT, pos, -1);
+ chip->legacy.cmdfunc(chip, NAND_CMD_RNDOUT, pos, -1);
else
- chip->cmdfunc(chip, NAND_CMD_READ0, pos, page);
+ chip->legacy.cmdfunc(chip, NAND_CMD_READ0, pos, page);
} else
sndrnd = 1;
toread = min_t(int, length, chunk);
- chip->read_buf(chip, bufpoi, toread);
+ chip->legacy.read_buf(chip, bufpoi, toread);
bufpoi += toread;
length -= toread;
}
if (length > 0)
- chip->read_buf(chip, bufpoi, length);
+ chip->legacy.read_buf(chip, bufpoi, length);
return 0;
}
@@ -1746,12 +1746,12 @@ static __maybe_unused int nand_write_oob_std(struct nand_chip *chip, int page)
if (!IS_ENABLED(CONFIG_NAND_READ_OOB) || !IS_ENABLED(CONFIG_MTD_WRITE))
return -ENOTSUPP;
- chip->cmdfunc(chip, NAND_CMD_SEQIN, chip->mtd.writesize, page);
- chip->write_buf(chip, buf, length);
+ chip->legacy.cmdfunc(chip, NAND_CMD_SEQIN, chip->mtd.writesize, page);
+ chip->legacy.write_buf(chip, buf, length);
/* Send command to program the OOB data */
- chip->cmdfunc(chip, NAND_CMD_PAGEPROG, -1, -1);
+ chip->legacy.cmdfunc(chip, NAND_CMD_PAGEPROG, -1, -1);
- status = chip->waitfunc(chip);
+ status = chip->legacy.waitfunc(chip);
return status & NAND_STATUS_FAIL ? -EIO : 0;
}
@@ -1784,7 +1784,7 @@ static __maybe_unused int nand_write_oob_syndrome(struct nand_chip *chip, int p
} else
pos = eccsize;
- chip->cmdfunc(chip, NAND_CMD_SEQIN, pos, page);
+ chip->legacy.cmdfunc(chip, NAND_CMD_SEQIN, pos, page);
for (i = 0; i < steps; i++) {
if (sndcmd) {
if (chip->mtd.writesize <= 512) {
@@ -1793,26 +1793,26 @@ static __maybe_unused int nand_write_oob_syndrome(struct nand_chip *chip, int p
len = eccsize;
while (len > 0) {
int num = min_t(int, len, 4);
- chip->write_buf(chip, (uint8_t *)&fill,
+ chip->legacy.write_buf(chip, (uint8_t *)&fill,
num);
len -= num;
}
} else {
pos = eccsize + i * (eccsize + chunk);
- chip->cmdfunc(chip, NAND_CMD_RNDIN, pos, -1);
+ chip->legacy.cmdfunc(chip, NAND_CMD_RNDIN, pos, -1);
}
} else
sndcmd = 1;
len = min_t(int, length, chunk);
- chip->write_buf(chip, bufpoi, len);
+ chip->legacy.write_buf(chip, bufpoi, len);
bufpoi += len;
length -= len;
}
if (length > 0)
- chip->write_buf(chip, bufpoi, length);
+ chip->legacy.write_buf(chip, bufpoi, length);
- chip->cmdfunc(chip, NAND_CMD_PAGEPROG, -1, -1);
- status = chip->waitfunc(chip);
+ chip->legacy.cmdfunc(chip, NAND_CMD_PAGEPROG, -1, -1);
+ status = chip->legacy.waitfunc(chip);
return status & NAND_STATUS_FAIL ? -EIO : 0;
}
@@ -1865,7 +1865,7 @@ static int nand_do_read_oob(struct nand_chip *chip, loff_t from,
}
chipnr = (int)(from >> chip->chip_shift);
- chip->select_chip(chip, chipnr);
+ chip->legacy.select_chip(chip, chipnr);
/* Shift to get page */
realpage = (int)(from >> chip->page_shift);
@@ -1885,8 +1885,8 @@ static int nand_do_read_oob(struct nand_chip *chip, loff_t from,
if (chip->options & NAND_NEED_READRDY) {
/* Apply delay or wait for ready/busy pin */
- if (!chip->dev_ready)
- udelay(chip->chip_delay);
+ if (!chip->legacy.dev_ready)
+ udelay(chip->legacy.chip_delay);
else
nand_wait_ready(chip);
}
@@ -1902,11 +1902,11 @@ static int nand_do_read_oob(struct nand_chip *chip, loff_t from,
/* Check, if we cross a chip boundary */
if (!page) {
chipnr++;
- chip->select_chip(chip, -1);
- chip->select_chip(chip, chipnr);
+ chip->legacy.select_chip(chip, -1);
+ chip->legacy.select_chip(chip, chipnr);
}
}
- chip->select_chip(chip, -1);
+ chip->legacy.select_chip(chip, -1);
ops->oobretlen = ops->ooblen - readlen;
@@ -1983,9 +1983,9 @@ static __maybe_unused int nand_write_page_raw(struct nand_chip *chip,
if (!IS_ENABLED(CONFIG_MTD_WRITE))
return -ENOTSUPP;
- chip->write_buf(chip, buf, chip->mtd.writesize);
+ chip->legacy.write_buf(chip, buf, chip->mtd.writesize);
if (oob_required)
- chip->write_buf(chip, chip->oob_poi, chip->mtd.oobsize);
+ chip->legacy.write_buf(chip, chip->oob_poi, chip->mtd.oobsize);
return 0;
}
@@ -2011,26 +2011,26 @@ static __maybe_unused int nand_write_page_raw_syndrome(struct nand_chip *chip,
return -ENOTSUPP;
for (steps = chip->ecc.steps; steps > 0; steps--) {
- chip->write_buf(chip, buf, eccsize);
+ chip->legacy.write_buf(chip, buf, eccsize);
buf += eccsize;
if (chip->ecc.prepad) {
- chip->write_buf(chip, oob, chip->ecc.prepad);
+ chip->legacy.write_buf(chip, oob, chip->ecc.prepad);
oob += chip->ecc.prepad;
}
- chip->read_buf(chip, oob, eccbytes);
+ chip->legacy.read_buf(chip, oob, eccbytes);
oob += eccbytes;
if (chip->ecc.postpad) {
- chip->write_buf(chip, oob, chip->ecc.postpad);
+ chip->legacy.write_buf(chip, oob, chip->ecc.postpad);
oob += chip->ecc.postpad;
}
}
size = chip->mtd.oobsize - (oob - chip->oob_poi);
if (size)
- chip->write_buf(chip, oob, size);
+ chip->legacy.write_buf(chip, oob, size);
return 0;
}
@@ -2086,14 +2086,14 @@ static __maybe_unused int nand_write_page_hwecc(struct nand_chip *chip,
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
chip->ecc.hwctl(chip, NAND_ECC_WRITE);
- chip->write_buf(chip, p, eccsize);
+ chip->legacy.write_buf(chip, p, eccsize);
chip->ecc.calculate(chip, p, &ecc_calc[i]);
}
for (i = 0; i < chip->ecc.total; i++)
chip->oob_poi[eccpos[i]] = ecc_calc[i];
- chip->write_buf(chip, chip->oob_poi, chip->mtd.oobsize);
+ chip->legacy.write_buf(chip, chip->oob_poi, chip->mtd.oobsize);
return 0;
}
@@ -2131,7 +2131,7 @@ static __maybe_unused int nand_write_subpage_hwecc(struct nand_chip *chip,
chip->ecc.hwctl(chip, NAND_ECC_WRITE);
/* write data (untouched subpages already masked by 0xFF) */
- chip->write_buf(chip, data_buf, ecc_size);
+ chip->legacy.write_buf(chip, data_buf, ecc_size);
/* mask ECC of un-touched subpages by padding 0xFF */
if ((step < start_step) || (step > end_step))
@@ -2156,7 +2156,7 @@ static __maybe_unused int nand_write_subpage_hwecc(struct nand_chip *chip,
chip->oob_poi[eccpos[i]] = ecc_calc[i];
/* write OOB buffer to NAND device */
- chip->write_buf(chip, chip->oob_poi, chip->mtd.oobsize);
+ chip->legacy.write_buf(chip, chip->oob_poi, chip->mtd.oobsize);
return 0;
}
@@ -2187,19 +2187,19 @@ static __maybe_unused int nand_write_page_syndrome(struct nand_chip *chip,
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
chip->ecc.hwctl(chip, NAND_ECC_WRITE);
- chip->write_buf(chip, p, eccsize);
+ chip->legacy.write_buf(chip, p, eccsize);
if (chip->ecc.prepad) {
- chip->write_buf(chip, oob, chip->ecc.prepad);
+ chip->legacy.write_buf(chip, oob, chip->ecc.prepad);
oob += chip->ecc.prepad;
}
chip->ecc.calculate(chip, p, oob);
- chip->write_buf(chip, oob, eccbytes);
+ chip->legacy.write_buf(chip, oob, eccbytes);
oob += eccbytes;
if (chip->ecc.postpad) {
- chip->write_buf(chip, oob, chip->ecc.postpad);
+ chip->legacy.write_buf(chip, oob, chip->ecc.postpad);
oob += chip->ecc.postpad;
}
}
@@ -2207,7 +2207,7 @@ static __maybe_unused int nand_write_page_syndrome(struct nand_chip *chip,
/* Calculate remaining oob bytes */
i = chip->mtd.oobsize - (oob - chip->oob_poi);
if (i)
- chip->write_buf(chip, oob, i);
+ chip->legacy.write_buf(chip, oob, i);
return 0;
}
@@ -2239,7 +2239,7 @@ static int nand_write_page(struct nand_chip *chip,
else
subpage = 0;
- chip->cmdfunc(chip, NAND_CMD_SEQIN, 0x00, page);
+ chip->legacy.cmdfunc(chip, NAND_CMD_SEQIN, 0x00, page);
if (unlikely(raw))
status = chip->ecc.write_page_raw(chip, buf,
@@ -2261,14 +2261,14 @@ static int nand_write_page(struct nand_chip *chip,
if (!cached || !NAND_HAS_CACHEPROG(chip)) {
- chip->cmdfunc(chip, NAND_CMD_PAGEPROG, -1, -1);
- status = chip->waitfunc(chip);
+ chip->legacy.cmdfunc(chip, NAND_CMD_PAGEPROG, -1, -1);
+ status = chip->legacy.waitfunc(chip);
if (status & NAND_STATUS_FAIL)
return -EIO;
} else {
- chip->cmdfunc(chip, NAND_CMD_CACHEDPROG, -1, -1);
- status = chip->waitfunc(chip);
+ chip->legacy.cmdfunc(chip, NAND_CMD_CACHEDPROG, -1, -1);
+ status = chip->legacy.waitfunc(chip);
}
return 0;
@@ -2364,7 +2364,7 @@ static int nand_do_write_ops(struct nand_chip *chip, loff_t to,
column = to & (mtd->writesize - 1);
chipnr = (int)(to >> chip->chip_shift);
- chip->select_chip(chip, chipnr);
+ chip->legacy.select_chip(chip, chipnr);
/* Check, if it is write protected */
if (nand_check_wp(chip)) {
@@ -2431,8 +2431,8 @@ static int nand_do_write_ops(struct nand_chip *chip, loff_t to,
/* Check, if we cross a chip boundary */
if (!page) {
chipnr++;
- chip->select_chip(chip, -1);
- chip->select_chip(chip, chipnr);
+ chip->legacy.select_chip(chip, -1);
+ chip->legacy.select_chip(chip, chipnr);
}
}
@@ -2441,7 +2441,7 @@ static int nand_do_write_ops(struct nand_chip *chip, loff_t to,
ops->oobretlen = ops->ooblen;
err_out:
- chip->select_chip(chip, -1);
+ chip->legacy.select_chip(chip, -1);
return ret;
}
@@ -2525,7 +2525,7 @@ static int nand_do_write_oob(struct nand_chip *chip, loff_t to,
}
chipnr = (int)(to >> chip->chip_shift);
- chip->select_chip(chip, chipnr);
+ chip->legacy.select_chip(chip, chipnr);
/* Shift to get page */
page = (int)(to >> chip->page_shift);
@@ -2536,11 +2536,11 @@ static int nand_do_write_oob(struct nand_chip *chip, loff_t to,
* if we don't do this. I have no clue why, but I seem to have 'fixed'
* it in the doc2000 driver in August 1999. dwmw2.
*/
- chip->cmdfunc(chip, NAND_CMD_RESET, -1, -1);
+ chip->legacy.cmdfunc(chip, NAND_CMD_RESET, -1, -1);
/* Check, if it is write protected */
if (nand_check_wp(chip)) {
- chip->select_chip(chip, -1);
+ chip->legacy.select_chip(chip, -1);
return -EROFS;
}
@@ -2555,7 +2555,7 @@ static int nand_do_write_oob(struct nand_chip *chip, loff_t to,
else
status = chip->ecc.write_oob(chip, page & chip->pagemask);
- chip->select_chip(chip, -1);
+ chip->legacy.select_chip(chip, -1);
if (status)
return status;
@@ -2624,8 +2624,8 @@ static void single_erase_cmd(struct nand_chip *chip, int page)
return;
/* Send commands to erase a block */
- chip->cmdfunc(chip, NAND_CMD_ERASE1, -1, page);
- chip->cmdfunc(chip, NAND_CMD_ERASE2, -1, -1);
+ chip->legacy.cmdfunc(chip, NAND_CMD_ERASE1, -1, page);
+ chip->legacy.cmdfunc(chip, NAND_CMD_ERASE2, -1, -1);
}
/**
@@ -2680,7 +2680,7 @@ int nand_erase_nand(struct nand_chip *chip, struct erase_info *instr,
pages_per_block = 1 << (chip->phys_erase_shift - chip->page_shift);
/* Select the NAND device */
- chip->select_chip(chip, chipnr);
+ chip->legacy.select_chip(chip, chipnr);
/* Check, if it is write protected */
if (nand_check_wp(chip)) {
@@ -2714,7 +2714,7 @@ int nand_erase_nand(struct nand_chip *chip, struct erase_info *instr,
single_erase_cmd(chip, page & chip->pagemask);
- status = chip->waitfunc(chip);
+ status = chip->legacy.waitfunc(chip);
/* See if block erase succeeded */
if (status & NAND_STATUS_FAIL) {
@@ -2733,8 +2733,8 @@ int nand_erase_nand(struct nand_chip *chip, struct erase_info *instr,
/* Check, if we cross a chip boundary */
if (len && !(page & chip->pagemask)) {
chipnr++;
- chip->select_chip(chip, -1);
- chip->select_chip(chip, chipnr);
+ chip->legacy.select_chip(chip, -1);
+ chip->legacy.select_chip(chip, chipnr);
}
}
@@ -2742,7 +2742,7 @@ int nand_erase_nand(struct nand_chip *chip, struct erase_info *instr,
erase_exit:
/* Deselect and wake up anyone waiting on the device */
- chip->select_chip(chip, -1);
+ chip->legacy.select_chip(chip, -1);
nand_release_device(chip);
/* Return more or less happy */
@@ -2844,9 +2844,9 @@ static int nand_onfi_set_features(struct nand_chip *chip,
& ONFI_OPT_CMD_SET_GET_FEATURES))
return -EINVAL;
- chip->cmdfunc(chip, NAND_CMD_SET_FEATURES, addr, -1);
- chip->write_buf(chip, subfeature_param, ONFI_SUBFEATURE_PARAM_LEN);
- status = chip->waitfunc(chip);
+ chip->legacy.cmdfunc(chip, NAND_CMD_SET_FEATURES, addr, -1);
+ chip->legacy.write_buf(chip, subfeature_param, ONFI_SUBFEATURE_PARAM_LEN);
+ status = chip->legacy.waitfunc(chip);
if (status & NAND_STATUS_FAIL)
return -EIO;
return 0;
@@ -2870,8 +2870,8 @@ static int nand_onfi_get_features(struct nand_chip *chip,
/* clear the sub feature parameters */
memset(subfeature_param, 0, ONFI_SUBFEATURE_PARAM_LEN);
- chip->cmdfunc(chip, NAND_CMD_GET_FEATURES, addr, -1);
- chip->read_buf(chip, subfeature_param, ONFI_SUBFEATURE_PARAM_LEN);
+ chip->legacy.cmdfunc(chip, NAND_CMD_GET_FEATURES, addr, -1);
+ chip->legacy.read_buf(chip, subfeature_param, ONFI_SUBFEATURE_PARAM_LEN);
return 0;
}
@@ -2879,40 +2879,40 @@ static int nand_onfi_get_features(struct nand_chip *chip,
static void nand_set_defaults(struct nand_chip *chip, int busw)
{
/* check for proper chip_delay setup, set 20us if not */
- if (!chip->chip_delay)
- chip->chip_delay = 20;
+ if (!chip->legacy.chip_delay)
+ chip->legacy.chip_delay = 20;
/* check, if a user supplied command function given */
- if (chip->cmdfunc == NULL)
- chip->cmdfunc = nand_command;
+ if (chip->legacy.cmdfunc == NULL)
+ chip->legacy.cmdfunc = nand_command;
/* check, if a user supplied wait function given */
- if (chip->waitfunc == NULL)
- chip->waitfunc = nand_wait;
+ if (chip->legacy.waitfunc == NULL)
+ chip->legacy.waitfunc = nand_wait;
- if (!chip->select_chip)
- chip->select_chip = nand_select_chip;
+ if (!chip->legacy.select_chip)
+ chip->legacy.select_chip = nand_select_chip;
/* set for ONFI nand */
- if (!chip->onfi_set_features)
- chip->onfi_set_features = nand_onfi_set_features;
- if (!chip->onfi_get_features)
- chip->onfi_get_features = nand_onfi_get_features;
-
- if (!chip->read_byte)
- chip->read_byte = busw ? nand_read_byte16 : nand_read_byte;
- if (!chip->read_word)
- chip->read_word = nand_read_word;
- if (!chip->block_bad)
- chip->block_bad = nand_block_bad;
+ if (!chip->legacy.set_features)
+ chip->legacy.set_features = nand_onfi_set_features;
+ if (!chip->legacy.get_features)
+ chip->legacy.get_features = nand_onfi_get_features;
+
+ if (!chip->legacy.read_byte)
+ chip->legacy.read_byte = busw ? nand_read_byte16 : nand_read_byte;
+ if (!chip->legacy.read_word)
+ chip->legacy.read_word = nand_read_word;
+ if (!chip->legacy.block_bad)
+ chip->legacy.block_bad = nand_block_bad;
#ifdef CONFIG_MTD_WRITE
- if (!chip->block_markbad)
- chip->block_markbad = nand_default_block_markbad;
- if (!chip->write_buf)
- chip->write_buf = busw ? nand_write_buf16 : nand_write_buf;
+ if (!chip->legacy.block_markbad)
+ chip->legacy.block_markbad = nand_default_block_markbad;
+ if (!chip->legacy.write_buf)
+ chip->legacy.write_buf = busw ? nand_write_buf16 : nand_write_buf;
#endif
- if (!chip->read_buf)
- chip->read_buf = busw ? nand_read_buf16 : nand_read_buf;
+ if (!chip->legacy.read_buf)
+ chip->legacy.read_buf = busw ? nand_read_buf16 : nand_read_buf;
#ifdef CONFIG_NAND_BBT
if (!chip->scan_bbt)
chip->scan_bbt = nand_default_bbt;
@@ -2964,15 +2964,15 @@ static int nand_flash_detect_onfi(struct nand_chip *chip, int *busw)
int val;
/* Try ONFI for unknown chip or LP */
- chip->cmdfunc(chip, NAND_CMD_READID, 0x20, -1);
- if (chip->read_byte(chip) != 'O' || chip->read_byte(chip) != 'N' ||
- chip->read_byte(chip) != 'F' || chip->read_byte(chip) != 'I')
+ chip->legacy.cmdfunc(chip, NAND_CMD_READID, 0x20, -1);
+ if (chip->legacy.read_byte(chip) != 'O' || chip->legacy.read_byte(chip) != 'N' ||
+ chip->legacy.read_byte(chip) != 'F' || chip->legacy.read_byte(chip) != 'I')
return 0;
- chip->cmdfunc(chip, NAND_CMD_PARAM, 0, -1);
+ chip->legacy.cmdfunc(chip, NAND_CMD_PARAM, 0, -1);
for (i = 0; i < 3; i++) {
for (j = 0; j < sizeof(*p); j++)
- ((uint8_t *)p)[j] = chip->read_byte(chip);
+ ((uint8_t *)p)[j] = chip->legacy.read_byte(chip);
if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 254) ==
le16_to_cpu(p->crc)) {
break;
@@ -3327,20 +3327,20 @@ static struct nand_flash_dev *nand_get_flash_type(struct nand_chip *chip,
u8 id_data[8];
/* Select the device */
- chip->select_chip(chip, 0);
+ chip->legacy.select_chip(chip, 0);
/*
* Reset the chip, required by some chips (e.g. Micron MT29FxGxxxxx)
* after power-up.
*/
- chip->cmdfunc(chip, NAND_CMD_RESET, -1, -1);
+ chip->legacy.cmdfunc(chip, NAND_CMD_RESET, -1, -1);
/* Send the command for reading device ID */
- chip->cmdfunc(chip, NAND_CMD_READID, 0x00, -1);
+ chip->legacy.cmdfunc(chip, NAND_CMD_READID, 0x00, -1);
/* Read manufacturer and device IDs */
- *maf_id = chip->read_byte(chip);
- *dev_id = chip->read_byte(chip);
+ *maf_id = chip->legacy.read_byte(chip);
+ *dev_id = chip->legacy.read_byte(chip);
/*
* Try again to make sure, as some systems the bus-hold or other
@@ -3349,11 +3349,11 @@ static struct nand_flash_dev *nand_get_flash_type(struct nand_chip *chip,
* not match, ignore the device completely.
*/
- chip->cmdfunc(chip, NAND_CMD_READID, 0x00, -1);
+ chip->legacy.cmdfunc(chip, NAND_CMD_READID, 0x00, -1);
/* Read entire ID string */
for (i = 0; i < 8; i++)
- id_data[i] = chip->read_byte(chip);
+ id_data[i] = chip->legacy.read_byte(chip);
if (id_data[0] != *maf_id || id_data[1] != *dev_id) {
pr_info("%s: second ID read did not match "
@@ -3449,8 +3449,8 @@ ident_done:
chip->badblockbits = 8;
/* Do not replace user supplied command function! */
- if (mtd->writesize > 512 && chip->cmdfunc == nand_command)
- chip->cmdfunc = nand_command_lp;
+ if (mtd->writesize > 512 && chip->legacy.cmdfunc == nand_command)
+ chip->legacy.cmdfunc = nand_command_lp;
pr_info("NAND device: Manufacturer ID: 0x%02x, Chip ID: 0x%02x (%s %s),"
" %dMiB, page size: %d, OOB size: %d\n",
@@ -3517,26 +3517,26 @@ int nand_scan_ident(struct nand_chip *chip, int maxchips,
if (IS_ERR(type)) {
if (!(chip->options & NAND_SCAN_SILENT_NODEV))
pr_warn("No NAND device found\n");
- chip->select_chip(chip, -1);
+ chip->legacy.select_chip(chip, -1);
return PTR_ERR(type);
}
- chip->select_chip(chip, -1);
+ chip->legacy.select_chip(chip, -1);
/* Check for a chip array */
for (i = 1; i < maxchips; i++) {
- chip->select_chip(chip, i);
+ chip->legacy.select_chip(chip, i);
/* See comment in nand_get_flash_type for reset */
- chip->cmdfunc(chip, NAND_CMD_RESET, -1, -1);
+ chip->legacy.cmdfunc(chip, NAND_CMD_RESET, -1, -1);
/* Send the command for reading device ID */
- chip->cmdfunc(chip, NAND_CMD_READID, 0x00, -1);
+ chip->legacy.cmdfunc(chip, NAND_CMD_READID, 0x00, -1);
/* Read manufacturer and device IDs */
- if (nand_maf_id != chip->read_byte(chip) ||
- nand_dev_id != chip->read_byte(chip)) {
- chip->select_chip(chip, -1);
+ if (nand_maf_id != chip->legacy.read_byte(chip) ||
+ nand_dev_id != chip->legacy.read_byte(chip)) {
+ chip->legacy.select_chip(chip, -1);
break;
}
- chip->select_chip(chip, -1);
+ chip->legacy.select_chip(chip, -1);
}
if (i > 1)
pr_info("%d NAND chips detected\n", i);
diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c
index 16e77d924e..7d04c89d76 100644
--- a/drivers/mtd/nand/nand_bbt.c
+++ b/drivers/mtd/nand/nand_bbt.c
@@ -696,7 +696,7 @@ static void mark_bbt_block_bad(struct nand_chip *this,
bbt_mark_entry(this, block, BBT_BLOCK_WORN);
to = (loff_t)block << this->bbt_erase_shift;
- res = this->block_markbad(this, to);
+ res = this->legacy.block_markbad(this, to);
if (res)
pr_warn("nand_bbt: error %d while marking block %d bad\n",
res, block);
diff --git a/drivers/mtd/nand/nand_denali.c b/drivers/mtd/nand/nand_denali.c
index 629281061b..1bacbd3be2 100644
--- a/drivers/mtd/nand/nand_denali.c
+++ b/drivers/mtd/nand/nand_denali.c
@@ -1404,11 +1404,11 @@ int denali_init(struct denali_nand_info *denali)
mtd->name = "denali-nand";
/* register the driver with the NAND core subsystem */
- nand->read_buf = denali_read_buf;
- nand->select_chip = denali_select_chip;
- nand->cmdfunc = denali_cmdfunc;
- nand->read_byte = denali_read_byte;
- nand->waitfunc = denali_waitfunc;
+ nand->legacy.read_buf = denali_read_buf;
+ nand->legacy.select_chip = denali_select_chip;
+ nand->legacy.cmdfunc = denali_cmdfunc;
+ nand->legacy.read_byte = denali_read_byte;
+ nand->legacy.waitfunc = denali_waitfunc;
/*
* scan for NAND devices attached to the controller
diff --git a/drivers/mtd/nand/nand_imx.c b/drivers/mtd/nand/nand_imx.c
index d566c941e7..993fb2e249 100644
--- a/drivers/mtd/nand/nand_imx.c
+++ b/drivers/mtd/nand/nand_imx.c
@@ -894,15 +894,15 @@ static int imx_nand_write_page(struct nand_chip *chip,
host->enable_hwecc(chip, !raw);
- chip->cmdfunc(chip, NAND_CMD_SEQIN, 0x00, page);
+ chip->legacy.cmdfunc(chip, NAND_CMD_SEQIN, 0x00, page);
memcpy32(host->main_area0, buf, mtd->writesize);
if (oob_required)
copy_spare(chip, 0, chip->oob_poi);
host->send_page(host, NFC_INPUT);
- chip->cmdfunc(chip, NAND_CMD_PAGEPROG, -1, -1);
- status = chip->waitfunc(chip);
+ chip->legacy.cmdfunc(chip, NAND_CMD_PAGEPROG, -1, -1);
+ status = chip->legacy.waitfunc(chip);
if (status & NAND_STATUS_FAIL)
return -EIO;
@@ -1341,16 +1341,16 @@ static int __init imxnd_probe(struct device_d *dev)
mtd->name = "imx_nand";
/* 50 us command delay time */
- this->chip_delay = 5;
+ this->legacy.chip_delay = 5;
this->priv = host;
- this->dev_ready = imx_nand_dev_ready;
- this->cmdfunc = imx_nand_command;
- this->select_chip = imx_nand_select_chip;
- this->read_byte = imx_nand_read_byte;
- this->read_word = imx_nand_read_word;
- this->write_buf = imx_nand_write_buf;
- this->read_buf = imx_nand_read_buf;
+ this->legacy.dev_ready = imx_nand_dev_ready;
+ this->legacy.cmdfunc = imx_nand_command;
+ this->legacy.select_chip = imx_nand_select_chip;
+ this->legacy.read_byte = imx_nand_read_byte;
+ this->legacy.read_word = imx_nand_read_word;
+ this->legacy.write_buf = imx_nand_write_buf;
+ this->legacy.read_buf = imx_nand_read_buf;
this->write_page = imx_nand_write_page;
if (host->hw_ecc) {
diff --git a/drivers/mtd/nand/nand_mrvl_nfc.c b/drivers/mtd/nand/nand_mrvl_nfc.c
index 3f5e04b716..76b487c3f8 100644
--- a/drivers/mtd/nand/nand_mrvl_nfc.c
+++ b/drivers/mtd/nand/nand_mrvl_nfc.c
@@ -352,8 +352,8 @@ static void mrvl_nand_set_timing(struct mrvl_nand_host *host, bool use_default)
if (use_default) {
id = 0;
} else {
- chip->cmdfunc(chip, NAND_CMD_READID, 0x00, -1);
- chip->read_buf(chip, (unsigned char *)&id, sizeof(id));
+ chip->legacy.cmdfunc(chip, NAND_CMD_READID, 0x00, -1);
+ chip->legacy.read_buf(chip, (unsigned char *)&id, sizeof(id));
}
for (t = &timings[0]; t->id; t++)
if (t->id == id)
@@ -472,7 +472,7 @@ static void mrvl_nand_start(struct mrvl_nand_host *host)
nand_writel(host, NDSR, NDSR_MASK);
nand_writel(host, NDCR, ndcr | NDCR_ND_RUN);
- if (wait_on_timeout(host->chip.chip_delay * USECOND,
+ if (wait_on_timeout(host->chip.legacy.chip_delay * USECOND,
nand_readl(host, NDSR) & NDSR_WRCMDREQ)) {
dev_err(host->dev, "Waiting for command request failed\n");
} else {
@@ -719,7 +719,7 @@ static void mrvl_data_stage(struct mrvl_nand_host *host)
if (!host->data_size)
return;
- wait_on_timeout(host->chip.chip_delay * USECOND,
+ wait_on_timeout(host->chip.legacy.chip_delay * USECOND,
nand_readl(host, NDSR) & mask);
if (!(nand_readl(host, NDSR) & mask)) {
dev_err(host->dev, "Timeout waiting for data ndsr=0x%08x\n",
@@ -752,7 +752,7 @@ static void mrvl_nand_wait_cmd_done(struct mrvl_nand_host *host,
mask = NDSR_CS0_CMDD;
else
mask = NDSR_CS1_CMDD;
- wait_on_timeout(host->chip.chip_delay * USECOND,
+ wait_on_timeout(host->chip.legacy.chip_delay * USECOND,
(nand_readl(host, NDSR) & mask) == mask);
if ((nand_readl(host, NDSR) & mask) != mask) {
dev_err(host->dev, "Waiting end of command %dth %d timeout, ndsr=0x%08x ndcr=0x%08x\n",
@@ -817,8 +817,8 @@ static int mrvl_nand_read_page_hwecc(struct nand_chip *chip, uint8_t *buf,
u32 ndsr;
int ret = 0;
- chip->read_buf(chip, buf, mtd->writesize);
- chip->read_buf(chip, chip->oob_poi, mtd->oobsize);
+ chip->legacy.read_buf(chip, buf, mtd->writesize);
+ chip->legacy.read_buf(chip, chip->oob_poi, mtd->oobsize);
ndsr = nand_readl(host, NDSR);
if (ndsr & NDSR_UNCORERR) {
@@ -1122,19 +1122,19 @@ static struct mrvl_nand_host *alloc_nand_resource(struct device_d *dev)
mtd->name = "mrvl_nand";
chip = &host->chip;
- chip->read_byte = mrvl_nand_read_byte;
- chip->read_word = mrvl_nand_read_word;
+ chip->legacy.read_byte = mrvl_nand_read_byte;
+ chip->legacy.read_word = mrvl_nand_read_word;
chip->ecc.read_page = mrvl_nand_read_page_hwecc;
chip->ecc.write_page = mrvl_nand_write_page_hwecc;
- chip->dev_ready = mrvl_nand_ready;
- chip->select_chip = mrvl_nand_select_chip;
- chip->block_bad = mrvl_nand_block_bad;
- chip->read_buf = mrvl_nand_read_buf;
- chip->write_buf = mrvl_nand_write_buf;
+ chip->legacy.dev_ready = mrvl_nand_ready;
+ chip->legacy.select_chip = mrvl_nand_select_chip;
+ chip->legacy.block_bad = mrvl_nand_block_bad;
+ chip->legacy.read_buf = mrvl_nand_read_buf;
+ chip->legacy.write_buf = mrvl_nand_write_buf;
chip->options |= NAND_NO_SUBPAGE_WRITE;
- chip->cmdfunc = mrvl_nand_cmdfunc;
+ chip->legacy.cmdfunc = mrvl_nand_cmdfunc;
chip->priv = host;
- chip->chip_delay = CHIP_DELAY_TIMEOUT_US;
+ chip->legacy.chip_delay = CHIP_DELAY_TIMEOUT_US;
host->dev = dev;
iores = dev_request_mem_resource(dev, 0);
diff --git a/drivers/mtd/nand/nand_mxs.c b/drivers/mtd/nand/nand_mxs.c
index 8075f6a854..2cf72178bf 100644
--- a/drivers/mtd/nand/nand_mxs.c
+++ b/drivers/mtd/nand/nand_mxs.c
@@ -1148,8 +1148,8 @@ static int mxs_nand_ecc_read_oob(struct nand_chip *chip, int page)
* If control arrives here, we're doing a "raw" read. Send the
* command to read the conventional OOB and read it.
*/
- chip->cmdfunc(chip, NAND_CMD_READ0, mtd->writesize, page);
- chip->read_buf(chip, chip->oob_poi, mtd->oobsize);
+ chip->legacy.cmdfunc(chip, NAND_CMD_READ0, mtd->writesize, page);
+ chip->legacy.read_buf(chip, chip->oob_poi, mtd->oobsize);
} else {
/*
* If control arrives here, we're not doing a "raw" read. Fill
@@ -1158,7 +1158,7 @@ static int mxs_nand_ecc_read_oob(struct nand_chip *chip, int page)
memset(chip->oob_poi, 0xff, mtd->oobsize);
column = nand_info->version == GPMI_VERSION_TYPE_MX23 ? 0 : mtd->writesize;
- chip->cmdfunc(chip, NAND_CMD_READ0, column, page);
+ chip->legacy.cmdfunc(chip, NAND_CMD_READ0, column, page);
mxs_nand_read_buf(chip, chip->oob_poi, 1);
}
@@ -1192,12 +1192,12 @@ static int mxs_nand_ecc_write_oob(struct nand_chip *chip, int page)
column = nand_info->version == GPMI_VERSION_TYPE_MX23 ? 0 : mtd->writesize;
/* Write the block mark. */
- chip->cmdfunc(chip, NAND_CMD_SEQIN, column, page);
- chip->write_buf(chip, &block_mark, 1);
- chip->cmdfunc(chip, NAND_CMD_PAGEPROG, -1, -1);
+ chip->legacy.cmdfunc(chip, NAND_CMD_SEQIN, column, page);
+ chip->legacy.write_buf(chip, &block_mark, 1);
+ chip->legacy.cmdfunc(chip, NAND_CMD_PAGEPROG, -1, -1);
/* Check if it worked. */
- if (chip->waitfunc(chip) & NAND_STATUS_FAIL)
+ if (chip->legacy.waitfunc(chip) & NAND_STATUS_FAIL)
return -EIO;
return 0;
@@ -2044,7 +2044,7 @@ static int mxs_nand_enable_edo_mode(struct mxs_nand_info *info)
else
return -EINVAL;
- chip->select_chip(chip, 0);
+ chip->legacy.select_chip(chip, 0);
if (le16_to_cpu(chip->onfi_params.opt_cmd)
& ONFI_OPT_CMD_SET_GET_FEATURES) {
@@ -2052,19 +2052,19 @@ static int mxs_nand_enable_edo_mode(struct mxs_nand_info *info)
/* [1] send SET FEATURE commond to NAND */
feature[0] = mode;
- ret = chip->onfi_set_features(chip,
+ ret = chip->legacy.set_features(chip,
ONFI_FEATURE_ADDR_TIMING_MODE, feature);
if (ret)
goto err_out;
/* [2] send GET FEATURE command to double-check the timing mode */
- ret = chip->onfi_get_features(chip,
+ ret = chip->legacy.get_features(chip,
ONFI_FEATURE_ADDR_TIMING_MODE, feature);
if (ret || feature[0] != mode)
goto err_out;
}
- chip->select_chip(chip, -1);
+ chip->legacy.select_chip(chip, -1);
/* [3] set the main IO clock, 100MHz for mode 5, 80MHz for mode 4. */
clk_disable(info->clk);
@@ -2076,7 +2076,7 @@ static int mxs_nand_enable_edo_mode(struct mxs_nand_info *info)
return mode;
err_out:
- chip->select_chip(chip, -1);
+ chip->legacy.select_chip(chip, -1);
return -EINVAL;
}
@@ -2195,17 +2195,17 @@ static int mxs_nand_probe(struct device_d *dev)
chip->priv = nand_info;
- chip->cmd_ctrl = mxs_nand_cmd_ctrl;
+ chip->legacy.cmd_ctrl = mxs_nand_cmd_ctrl;
- chip->dev_ready = mxs_nand_device_ready;
- chip->select_chip = mxs_nand_select_chip;
- chip->block_bad = mxs_nand_block_bad;
- chip->scan_bbt = mxs_nand_scan_bbt;
+ chip->legacy.dev_ready = mxs_nand_device_ready;
+ chip->legacy.select_chip = mxs_nand_select_chip;
+ chip->legacy.block_bad = mxs_nand_block_bad;
+ chip->scan_bbt = mxs_nand_scan_bbt;
- chip->read_byte = mxs_nand_read_byte;
+ chip->legacy.read_byte = mxs_nand_read_byte;
- chip->read_buf = mxs_nand_read_buf;
- chip->write_buf = mxs_nand_write_buf;
+ chip->legacy.read_buf = mxs_nand_read_buf;
+ chip->legacy.write_buf = mxs_nand_write_buf;
chip->ecc.read_page = mxs_nand_ecc_read_page;
chip->ecc.write_page = mxs_nand_ecc_write_page;
diff --git a/drivers/mtd/nand/nand_omap_gpmc.c b/drivers/mtd/nand/nand_omap_gpmc.c
index 6ad06288eb..3799406769 100644
--- a/drivers/mtd/nand/nand_omap_gpmc.c
+++ b/drivers/mtd/nand/nand_omap_gpmc.c
@@ -192,23 +192,23 @@ static void omap_hwcontrol(struct nand_chip *nand, int cmd, unsigned int ctrl)
switch (ctrl) {
case NAND_CTRL_CHANGE | NAND_CTRL_CLE:
- nand->IO_ADDR_W = oinfo->gpmc_command;
- nand->IO_ADDR_R = oinfo->gpmc_data;
+ nand->legacy.IO_ADDR_W = oinfo->gpmc_command;
+ nand->legacy.IO_ADDR_R = oinfo->gpmc_data;
break;
case NAND_CTRL_CHANGE | NAND_CTRL_ALE:
- nand->IO_ADDR_W = oinfo->gpmc_address;
- nand->IO_ADDR_R = oinfo->gpmc_data;
+ nand->legacy.IO_ADDR_W = oinfo->gpmc_address;
+ nand->legacy.IO_ADDR_R = oinfo->gpmc_data;
break;
case NAND_CTRL_CHANGE | NAND_NCE:
- nand->IO_ADDR_W = oinfo->gpmc_data;
- nand->IO_ADDR_R = oinfo->gpmc_data;
+ nand->legacy.IO_ADDR_W = oinfo->gpmc_data;
+ nand->legacy.IO_ADDR_R = oinfo->gpmc_data;
break;
}
if (cmd != NAND_CMD_NONE)
- writeb(cmd, nand->IO_ADDR_W);
+ writeb(cmd, nand->legacy.IO_ADDR_W);
return;
}
@@ -561,7 +561,7 @@ static int omap_gpmc_read_buf_manual(struct nand_chip *chip,
writel(GPMC_ECC_CONTROL_ECCPOINTER(result_reg),
oinfo->gpmc_base + GPMC_ECC_CONTROL);
- chip->read_buf(chip, buf, bytes);
+ chip->legacy.read_buf(chip, buf, bytes);
return bytes;
}
@@ -623,7 +623,7 @@ static void omap_write_buf_pref(struct nand_chip *nand_chip,
/* take care of subpage writes */
while (len % 4 != 0) {
- writeb(*buf, info->nand.IO_ADDR_W);
+ writeb(*buf, info->nand.legacy.IO_ADDR_W);
buf1++;
p32 = (u32 *)buf1;
len--;
@@ -958,8 +958,8 @@ static int gpmc_read_page_hwecc_elm(struct nand_chip *chip, uint8_t *buf,
uint32_t *eccpos = chip->ecc.layout->eccpos;
chip->ecc.hwctl(chip, NAND_ECC_READ);
- chip->read_buf(chip, buf, mtd->writesize);
- chip->read_buf(chip, chip->oob_poi, mtd->oobsize);
+ chip->legacy.read_buf(chip, buf, mtd->writesize);
+ chip->legacy.read_buf(chip, chip->oob_poi, mtd->oobsize);
for (i = 0; i < chip->ecc.total; i++)
ecc_code[i] = chip->oob_poi[eccpos[i]];
@@ -986,8 +986,8 @@ static int gpmc_read_page_hwecc(struct nand_chip *chip, uint8_t *buf,
unsigned int max_bitflips = 0;
chip->ecc.hwctl(chip, NAND_ECC_READ);
- chip->read_buf(chip, p, mtd->writesize);
- chip->read_buf(chip, chip->oob_poi, mtd->oobsize);
+ chip->legacy.read_buf(chip, p, mtd->writesize);
+ chip->legacy.read_buf(chip, chip->oob_poi, mtd->oobsize);
for (i = 0; i < chip->ecc.total; i++)
ecc_code[i] = chip->oob_poi[eccpos[i]];
@@ -1230,7 +1230,7 @@ static int gpmc_nand_probe(struct device_d *pdev)
}
/* Same data register for in and out */
- nand->IO_ADDR_W = nand->IO_ADDR_R = (void *)oinfo->gpmc_data;
+ nand->legacy.IO_ADDR_W = nand->legacy.IO_ADDR_R = (void *)oinfo->gpmc_data;
/*
* If RDY/BSY line is connected to OMAP then use the omap ready
* function and the generic nand_wait function which reads the
@@ -1249,16 +1249,16 @@ static int gpmc_nand_probe(struct device_d *pdev)
/* Set up the wait monitoring mask
* This is GPMC_STATUS reg relevant */
oinfo->wait_mon_mask = (0x1 << (pdata->wait_mon_pin - 1)) << 8;
- nand->dev_ready = omap_dev_ready;
- nand->chip_delay = 0;
+ nand->legacy.dev_ready = omap_dev_ready;
+ nand->legacy.chip_delay = 0;
} else {
/* use the default nand_wait function */
- nand->chip_delay = 50;
+ nand->legacy.chip_delay = 50;
}
/* Use default cmdfunc */
/* nand cmd control */
- nand->cmd_ctrl = omap_hwcontrol;
+ nand->legacy.cmd_ctrl = omap_hwcontrol;
/* Dont do a bbt scan at the start */
nand->options |= NAND_SKIP_BBTSCAN;
@@ -1295,9 +1295,9 @@ static int gpmc_nand_probe(struct device_d *pdev)
gpmc_set_buswidth(nand, nand->options & NAND_BUSWIDTH_16);
- nand->read_buf = omap_read_buf_pref;
+ nand->legacy.read_buf = omap_read_buf_pref;
if (IS_ENABLED(CONFIG_MTD_WRITE))
- nand->write_buf = omap_write_buf_pref;
+ nand->legacy.write_buf = omap_write_buf_pref;
nand->options |= NAND_SKIP_BBTSCAN;
diff --git a/drivers/mtd/nand/nand_orion.c b/drivers/mtd/nand/nand_orion.c
index 796c53e4c4..c6f8dc68b4 100644
--- a/drivers/mtd/nand/nand_orion.c
+++ b/drivers/mtd/nand/nand_orion.c
@@ -45,12 +45,12 @@ static void orion_nand_cmd_ctrl(struct nand_chip *chip, int cmd, unsigned int ct
if (chip->options & NAND_BUSWIDTH_16)
offs <<= 1;
- writeb(cmd, chip->IO_ADDR_W + offs);
+ writeb(cmd, chip->legacy.IO_ADDR_W + offs);
}
static void orion_nand_read_buf(struct nand_chip *chip, uint8_t *buf, int len)
{
- void __iomem *io_base = chip->IO_ADDR_R;
+ void __iomem *io_base = chip->legacy.IO_ADDR_R;
uint64_t *buf64;
int i = 0;
@@ -112,13 +112,13 @@ static int orion_nand_probe(struct device_d *dev)
width = 8;
if (!of_property_read_u32(dev_node, "chip-delay", &val))
- chip->chip_delay = (u8)val;
+ chip->legacy.chip_delay = (u8)val;
mtd->dev.parent = dev;
chip->priv = priv;
- chip->IO_ADDR_R = chip->IO_ADDR_W = io_base;
- chip->cmd_ctrl = orion_nand_cmd_ctrl;
- chip->read_buf = orion_nand_read_buf;
+ chip->legacy.IO_ADDR_R = chip->legacy.IO_ADDR_W = io_base;
+ chip->legacy.cmd_ctrl = orion_nand_cmd_ctrl;
+ chip->legacy.read_buf = orion_nand_read_buf;
chip->ecc.mode = NAND_ECC_SOFT;
WARN(width > 16, "%d bit bus width out of range", width);
diff --git a/drivers/mtd/nand/nand_s3c24xx.c b/drivers/mtd/nand/nand_s3c24xx.c
index ac95973194..e20517ab4a 100644
--- a/drivers/mtd/nand/nand_s3c24xx.c
+++ b/drivers/mtd/nand/nand_s3c24xx.c
@@ -426,18 +426,18 @@ static int s3c24x0_nand_probe(struct device_d *dev)
/* init the default settings */
/* 50 us command delay time */
- chip->chip_delay = 50;
+ chip->legacy.chip_delay = 50;
chip->priv = host;
- chip->IO_ADDR_R = chip->IO_ADDR_W = host->base + NFDATA;
+ chip->legacy.IO_ADDR_R = chip->legacy.IO_ADDR_W = host->base + NFDATA;
#ifdef CONFIG_CPU_S3C2440
- chip->read_buf = s3c2440_nand_read_buf;
- chip->write_buf = s3c2440_nand_write_buf;
+ chip->legacy.read_buf = s3c2440_nand_read_buf;
+ chip->legacy.write_buf = s3c2440_nand_write_buf;
#endif
- chip->cmd_ctrl = s3c24x0_nand_hwcontrol;
- chip->dev_ready = s3c24x0_nand_devready;
- chip->select_chip = s3c24x0_nand_select_chip;
+ chip->legacy.cmd_ctrl = s3c24x0_nand_hwcontrol;
+ chip->legacy.dev_ready = s3c24x0_nand_devready;
+ chip->legacy.select_chip = s3c24x0_nand_select_chip;
/* we are using the hardware ECC feature of this device */
chip->ecc.calculate = s3c2410_nand_calculate_ecc;
diff --git a/drivers/mtd/nand/nomadik_nand.c b/drivers/mtd/nand/nomadik_nand.c
index 55aa756826..2767259f7a 100644
--- a/drivers/mtd/nand/nomadik_nand.c
+++ b/drivers/mtd/nand/nomadik_nand.c
@@ -200,10 +200,11 @@ static int nomadik_nand_probe(struct device_d *dev)
nand->priv = host;
mtd->dev.parent = dev;
- nand->IO_ADDR_W = nand->IO_ADDR_R = dev_request_mem_region_by_name(dev, "nand_data");
- if (IS_ERR(nand->IO_ADDR_W))
- return PTR_ERR(nand->IO_ADDR_W);
- nand->cmd_ctrl = nomadik_cmd_ctrl;
+ nand->legacy.IO_ADDR_W = nand->legacy.IO_ADDR_R =
+ dev_request_mem_region_by_name(dev, "nand_data");
+ if (IS_ERR(nand->legacy.IO_ADDR_W))
+ return PTR_ERR(nand->legacy.IO_ADDR_W);
+ nand->legacy.cmd_ctrl = nomadik_cmd_ctrl;
nand->ecc.mode = NAND_ECC_HW;
nand->ecc.layout = &nomadik_ecc_layout;
diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
index e23b3446a7..f0d3455fd3 100644
--- a/include/linux/mtd/nand.h
+++ b/include/linux/mtd/nand.h
@@ -386,39 +386,67 @@ struct nand_buffers {
uint8_t databuf[NAND_MAX_PAGESIZE + NAND_MAX_OOBSIZE];
};
+/**
+ * struct nand_legacy - NAND chip legacy fields/hooks
+ * @IO_ADDR_R: address to read the 8 I/O lines of the flash device
+ * @IO_ADDR_W: address to write the 8 I/O lines of the flash device
+ * @select_chip: select/deselect a specific target/die
+ * @read_byte: read one byte from the chip
+ * @write_byte: write a single byte to the chip on the low 8 I/O lines
+ * @write_buf: write data from the buffer to the chip
+ * @read_buf: read data from the chip into the buffer
+ * @cmd_ctrl: hardware specific function for controlling ALE/CLE/nCE. Also used
+ * to write command and address
+ * @cmdfunc: hardware specific function for writing commands to the chip.
+ * @dev_ready: hardware specific function for accessing device ready/busy line.
+ * If set to NULL no access to ready/busy is available and the
+ * ready/busy information is read from the chip status register.
+ * @waitfunc: hardware specific function for wait on ready.
+ * @block_bad: check if a block is bad, using OOB markers
+ * @block_markbad: mark a block bad
+ * @set_features: set the NAND chip features
+ * @get_features: get the NAND chip features
+ * @chip_delay: chip dependent delay for transferring data from array to read
+ * regs (tR).
+ * @dummy_controller: dummy controller implementation for drivers that can
+ * only control a single chip
+ *
+ * If you look at this structure you're already wrong. These fields/hooks are
+ * all deprecated.
+ */
+struct nand_legacy {
+ void __iomem *IO_ADDR_R;
+ void __iomem *IO_ADDR_W;
+ void (*select_chip)(struct nand_chip *chip, int cs);
+ u8 (*read_byte)(struct nand_chip *chip);
+ u16 (*read_word)(struct nand_chip *chip);
+ void (*write_byte)(struct nand_chip *chip, u8 byte);
+ void (*write_buf)(struct nand_chip *chip, const u8 *buf, int len);
+ void (*read_buf)(struct nand_chip *chip, u8 *buf, int len);
+ void (*cmd_ctrl)(struct nand_chip *chip, int dat, unsigned int ctrl);
+ void (*cmdfunc)(struct nand_chip *chip, unsigned command, int column,
+ int page_addr);
+ int (*dev_ready)(struct nand_chip *chip);
+ int (*waitfunc)(struct nand_chip *chip);
+ int (*block_bad)(struct nand_chip *chip, loff_t ofs, int getchip);
+ int (*block_markbad)(struct nand_chip *chip, loff_t ofs);
+ int (*set_features)(struct nand_chip *chip, int feature_addr,
+ u8 *subfeature_para);
+ int (*get_features)(struct nand_chip *chip, int feature_addr,
+ u8 *subfeature_para);
+ int chip_delay;
+};
+
/**
* struct nand_chip - NAND Private Flash Chip Data
- * @IO_ADDR_R: [BOARDSPECIFIC] address to read the 8 I/O lines of the
- * flash device
- * @IO_ADDR_W: [BOARDSPECIFIC] address to write the 8 I/O lines of the
- * flash device.
- * @read_byte: [REPLACEABLE] read one byte from the chip
- * @read_word: [REPLACEABLE] read one word from the chip
- * @write_buf: [REPLACEABLE] write data from the buffer to the chip
- * @read_buf: [REPLACEABLE] read data from the chip into the buffer
- * @select_chip: [REPLACEABLE] select chip nr
- * @block_bad: [REPLACEABLE] check, if the block is bad
- * @block_markbad: [REPLACEABLE] mark the block bad
- * @cmd_ctrl: [BOARDSPECIFIC] hardwarespecific function for controlling
- * ALE/CLE/nCE. Also used to write command and address
* @init_size: [BOARDSPECIFIC] hardwarespecific function for setting
* mtd->oobsize, mtd->writesize and so on.
* @id_data contains the 8 bytes values of NAND_CMD_READID.
* Return with the bus width.
- * @dev_ready: [BOARDSPECIFIC] hardwarespecific function for accessing
- * device ready/busy line. If set to NULL no access to
- * ready/busy is available and the ready/busy information
- * is read from the chip status register.
- * @cmdfunc: [REPLACEABLE] hardwarespecific function for writing
- * commands to the chip.
- * @waitfunc: [REPLACEABLE] hardwarespecific function for wait on
- * ready.
* @ecc: [BOARDSPECIFIC] ECC control structure
* @buffers: buffer structure for read/write
* @hwcontrol: platform-specific hardware control structure
* @scan_bbt: [REPLACEABLE] function to scan bad block table
- * @chip_delay: [BOARDSPECIFIC] chip dependent delay for transferring
- * data from array to read regs (tR).
* @state: [INTERN] the current state of the NAND device
* @oob_poi: "poison value buffer," used for laying out OOB data
* before writing
@@ -468,31 +496,13 @@ struct nand_buffers {
*/
struct nand_chip {
- void __iomem *IO_ADDR_R;
- void __iomem *IO_ADDR_W;
-
- uint8_t (*read_byte)(struct nand_chip *chip);
- u16 (*read_word)(struct nand_chip *chip);
- void (*write_buf)(struct nand_chip *chip, const uint8_t *buf, int len);
- void (*read_buf)(struct nand_chip *chip, uint8_t *buf, int len);
- void (*select_chip)(struct nand_chip *_chip, int chip);
- int (*block_bad)(struct nand_chip *chip, loff_t ofs, int getchip);
- int (*block_markbad)(struct nand_chip *chip, loff_t ofs);
- void (*cmd_ctrl)(struct nand_chip *chip, int dat, unsigned int ctrl);
- int (*dev_ready)(struct nand_chip *chip);
- void (*cmdfunc)(struct nand_chip *chip, unsigned command, int column,
- int page_addr);
- int(*waitfunc)(struct nand_chip *chip);
int (*scan_bbt)(struct nand_chip *chip);
int (*write_page)(struct nand_chip *chip,
uint32_t offset, int data_len, const uint8_t *buf,
int oob_required, int page, int cached, int raw);
- int (*onfi_set_features)(struct nand_chip *chip,
- int feature_addr, uint8_t *subfeature_para);
- int (*onfi_get_features)(struct nand_chip *chip,
- int feature_addr, uint8_t *subfeature_para);
- int chip_delay;
+ struct nand_legacy legacy;
+
unsigned int options;
unsigned int bbt_options;
--
2.20.1
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 27+ messages in thread
* [PATCH 12/26] mtd: Add ecc_step_size
2020-11-06 13:38 [PATCH 00/26] Update nand layer to Linux-5.9 Sascha Hauer
` (10 preceding siblings ...)
2020-11-06 13:38 ` [PATCH 11/26] mtd: nand: move function hooks to struct nand_legacy Sascha Hauer
@ 2020-11-06 13:38 ` Sascha Hauer
2020-11-06 13:38 ` [PATCH 13/26] mtd: nand: omap_gpmc: Drop unused variable Sascha Hauer
` (13 subsequent siblings)
25 siblings, 0 replies; 27+ messages in thread
From: Sascha Hauer @ 2020-11-06 13:38 UTC (permalink / raw)
To: Barebox List
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
drivers/mtd/partition.c | 1 +
include/linux/mtd/mtd.h | 3 +++
2 files changed, 4 insertions(+)
diff --git a/drivers/mtd/partition.c b/drivers/mtd/partition.c
index 917122a844..1572038392 100644
--- a/drivers/mtd/partition.c
+++ b/drivers/mtd/partition.c
@@ -166,6 +166,7 @@ struct mtd_info *mtd_add_partition(struct mtd_info *mtd, off_t offset,
part->oobavail = mtd->oobavail;
part->bitflip_threshold = mtd->bitflip_threshold;
part->ecclayout = mtd->ecclayout;
+ part->ecc_step_size = mtd->ecc_step_size;
part->ecc_strength = mtd->ecc_strength;
part->subpage_sft = mtd->subpage_sft;
part->cdev.flags = flags;
diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h
index 75407a9790..710cba7a20 100644
--- a/include/linux/mtd/mtd.h
+++ b/include/linux/mtd/mtd.h
@@ -124,6 +124,9 @@ struct mtd_info {
/* ecc layout structure pointer - read only ! */
struct nand_ecclayout *ecclayout;
+ /* the ecc step size. */
+ unsigned int ecc_step_size;
+
/* max number of correctible bit errors per ecc step */
unsigned int ecc_strength;
--
2.20.1
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 27+ messages in thread
* [PATCH 13/26] mtd: nand: omap_gpmc: Drop unused variable
2020-11-06 13:38 [PATCH 00/26] Update nand layer to Linux-5.9 Sascha Hauer
` (11 preceding siblings ...)
2020-11-06 13:38 ` [PATCH 12/26] mtd: Add ecc_step_size Sascha Hauer
@ 2020-11-06 13:38 ` Sascha Hauer
2020-11-06 13:38 ` [PATCH 14/26] mtd: nand: omap_gpmc: Fix wrong length check Sascha Hauer
` (12 subsequent siblings)
25 siblings, 0 replies; 27+ messages in thread
From: Sascha Hauer @ 2020-11-06 13:38 UTC (permalink / raw)
To: Barebox List
nand->controller is set but never used. Remove it.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
drivers/mtd/nand/nand_omap_gpmc.c | 4 ----
1 file changed, 4 deletions(-)
diff --git a/drivers/mtd/nand/nand_omap_gpmc.c b/drivers/mtd/nand/nand_omap_gpmc.c
index 3799406769..d10ba5a8a8 100644
--- a/drivers/mtd/nand/nand_omap_gpmc.c
+++ b/drivers/mtd/nand/nand_omap_gpmc.c
@@ -106,7 +106,6 @@ static const char *ecc_mode_strings[] = {
/** internal structure maintained for nand information */
struct gpmc_nand_info {
- struct nand_hw_control controller;
struct device_d *pdev;
struct gpmc_nand_platform_data *pdata;
struct nand_chip nand;
@@ -1266,9 +1265,6 @@ static int gpmc_nand_probe(struct device_d *pdev)
nand->options |= NAND_OWN_BUFFERS;
nand->buffers = xzalloc(sizeof(*nand->buffers));
- /* State my controller */
- nand->controller = &oinfo->controller;
-
/* All information is ready.. now lets call setup, if present */
if (pdata->nand_setup) {
err = pdata->nand_setup(pdata);
--
2.20.1
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 27+ messages in thread
* [PATCH 14/26] mtd: nand: omap_gpmc: Fix wrong length check
2020-11-06 13:38 [PATCH 00/26] Update nand layer to Linux-5.9 Sascha Hauer
` (12 preceding siblings ...)
2020-11-06 13:38 ` [PATCH 13/26] mtd: nand: omap_gpmc: Drop unused variable Sascha Hauer
@ 2020-11-06 13:38 ` Sascha Hauer
2020-11-06 13:38 ` [PATCH 15/26] mtd: nand: omap_gpmc: Add missing bch16 string Sascha Hauer
` (11 subsequent siblings)
25 siblings, 0 replies; 27+ messages in thread
From: Sascha Hauer @ 2020-11-06 13:38 UTC (permalink / raw)
To: Barebox List
We may only write more characters when there's actually something left
to write. Fix the wrong check.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
drivers/mtd/nand/nand_omap_gpmc.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/mtd/nand/nand_omap_gpmc.c b/drivers/mtd/nand/nand_omap_gpmc.c
index d10ba5a8a8..670eba4206 100644
--- a/drivers/mtd/nand/nand_omap_gpmc.c
+++ b/drivers/mtd/nand/nand_omap_gpmc.c
@@ -632,7 +632,7 @@ static void omap_write_buf_pref(struct nand_chip *nand_chip,
gpmc_prefetch_enable(info->gpmc_cs,
PREFETCH_FIFOTHRESHOLD_MAX, 0x0, len, 0x1);
- while (len >= 0) {
+ while (len) {
w_count = readl(info->gpmc_base + GPMC_PREFETCH_STATUS);
w_count = GPMC_PREFETCH_STATUS_FIFO_CNT(w_count);
w_count = w_count >> 2;
--
2.20.1
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 27+ messages in thread
* [PATCH 15/26] mtd: nand: omap_gpmc: Add missing bch16 string
2020-11-06 13:38 [PATCH 00/26] Update nand layer to Linux-5.9 Sascha Hauer
` (13 preceding siblings ...)
2020-11-06 13:38 ` [PATCH 14/26] mtd: nand: omap_gpmc: Fix wrong length check Sascha Hauer
@ 2020-11-06 13:38 ` Sascha Hauer
2020-11-06 13:38 ` [PATCH 16/26] mtd: nand: denali: Drop multichip support Sascha Hauer
` (10 subsequent siblings)
25 siblings, 0 replies; 27+ messages in thread
From: Sascha Hauer @ 2020-11-06 13:38 UTC (permalink / raw)
To: Barebox List
We support bch16_hw in the OMAP NAND driver, but we cannot show it in
the eccmode parameter. Add the missing string.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
drivers/mtd/nand/nand_omap_gpmc.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/mtd/nand/nand_omap_gpmc.c b/drivers/mtd/nand/nand_omap_gpmc.c
index 670eba4206..d51a8a8a9e 100644
--- a/drivers/mtd/nand/nand_omap_gpmc.c
+++ b/drivers/mtd/nand/nand_omap_gpmc.c
@@ -102,6 +102,7 @@ static const char *ecc_mode_strings[] = {
"hamming_hw_romcode",
"bch8_hw",
"bch8_hw_romcode",
+ "bch16_hw",
};
/** internal structure maintained for nand information */
--
2.20.1
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 27+ messages in thread
* [PATCH 16/26] mtd: nand: denali: Drop multichip support
2020-11-06 13:38 [PATCH 00/26] Update nand layer to Linux-5.9 Sascha Hauer
` (14 preceding siblings ...)
2020-11-06 13:38 ` [PATCH 15/26] mtd: nand: omap_gpmc: Add missing bch16 string Sascha Hauer
@ 2020-11-06 13:38 ` Sascha Hauer
2020-11-06 13:38 ` [PATCH 17/26] mtd: nand: marvell: Use nand_to_mtd() Sascha Hauer
` (9 subsequent siblings)
25 siblings, 0 replies; 27+ messages in thread
From: Sascha Hauer @ 2020-11-06 13:38 UTC (permalink / raw)
To: Barebox List
Current multichip support doesn't fir well into the coming update to the
NAND layer. Remove it for now, it will be added back when the denali
NAND driver is updated to current Linux.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
drivers/mtd/nand/nand_denali.c | 24 ++++++++----------------
1 file changed, 8 insertions(+), 16 deletions(-)
diff --git a/drivers/mtd/nand/nand_denali.c b/drivers/mtd/nand/nand_denali.c
index 1bacbd3be2..bffcbd8a7f 100644
--- a/drivers/mtd/nand/nand_denali.c
+++ b/drivers/mtd/nand/nand_denali.c
@@ -1435,17 +1435,13 @@ int denali_init(struct denali_nand_info *denali)
* the real pagesize and anything necessery
*/
denali->devnum = ioread32(denali->flash_reg + DEVICES_CONNECTED);
- nand->chipsize <<= (denali->devnum - 1);
- nand->page_shift += (denali->devnum - 1);
- nand->pagemask = (nand->chipsize >> nand->page_shift) - 1;
- nand->bbt_erase_shift += (denali->devnum - 1);
- nand->phys_erase_shift = nand->bbt_erase_shift;
- nand->chip_shift += (denali->devnum - 1);
- mtd->writesize <<= (denali->devnum - 1);
- mtd->oobsize <<= (denali->devnum - 1);
- mtd->erasesize <<= (denali->devnum - 1);
- mtd->size = nand->numchips * nand->chipsize;
- denali->bbtskipbytes *= denali->devnum;
+ if (denali->devnum != 1) {
+ ret = -EINVAL;
+ dev_err(denali->dev,
+ "Multiple devices (%d) detected, not yet supported\n",
+ denali->devnum);
+ goto failed_req_irq;
+ }
/*
* second stage of the NAND scan
@@ -1496,10 +1492,6 @@ int denali_init(struct denali_nand_info *denali)
iowrite32(8, denali->flash_reg + ECC_CORRECTION);
}
- nand->ecc.bytes *= denali->devnum;
- nand->ecc.strength *= denali->devnum;
- nand->ecc.layout->eccbytes *=
- mtd->writesize / ECC_SECTOR_SIZE;
nand->ecc.layout->oobfree[0].offset =
denali->bbtskipbytes + nand->ecc.layout->eccbytes;
nand->ecc.layout->oobfree[0].length =
@@ -1515,7 +1507,7 @@ int denali_init(struct denali_nand_info *denali)
denali->blksperchip = denali->totalblks / nand->numchips;
/* override the default read operations */
- nand->ecc.size = ECC_SECTOR_SIZE * denali->devnum;
+ nand->ecc.size = ECC_SECTOR_SIZE;
nand->ecc.read_page = denali_read_page;
nand->ecc.read_page_raw = denali_read_page_raw;
nand->ecc.write_page = denali_write_page;
--
2.20.1
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 27+ messages in thread
* [PATCH 17/26] mtd: nand: marvell: Use nand_to_mtd()
2020-11-06 13:38 [PATCH 00/26] Update nand layer to Linux-5.9 Sascha Hauer
` (15 preceding siblings ...)
2020-11-06 13:38 ` [PATCH 16/26] mtd: nand: denali: Drop multichip support Sascha Hauer
@ 2020-11-06 13:38 ` Sascha Hauer
2020-11-06 13:38 ` [PATCH 18/26] mtd: nand: gpmi: " Sascha Hauer
` (8 subsequent siblings)
25 siblings, 0 replies; 27+ messages in thread
From: Sascha Hauer @ 2020-11-06 13:38 UTC (permalink / raw)
To: Barebox List
We have a function for getting from the struct nand_chip * to the struct
mtd_info *, so use it.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
drivers/mtd/nand/nand_mrvl_nfc.c | 26 ++++++++++++++++++--------
1 file changed, 18 insertions(+), 8 deletions(-)
diff --git a/drivers/mtd/nand/nand_mrvl_nfc.c b/drivers/mtd/nand/nand_mrvl_nfc.c
index 76b487c3f8..892ce507a5 100644
--- a/drivers/mtd/nand/nand_mrvl_nfc.c
+++ b/drivers/mtd/nand/nand_mrvl_nfc.c
@@ -419,9 +419,12 @@ static void mrvl_nand_select_chip(struct nand_chip *chip, int chipnr)
*/
static unsigned int mrvl_datasize(struct mrvl_nand_host *host)
{
+ struct nand_chip *chip = &host->chip;
+ struct mtd_info *mtd = nand_to_mtd(chip);
+
unsigned int datasize;
- datasize = host->chip.mtd.writesize;
+ datasize = mtd->writesize;
if (host->use_spare) {
datasize += host->spare_size;
if (!host->use_ecc)
@@ -531,6 +534,9 @@ static void set_command_address(struct mrvl_nand_host *host,
static void prepare_start_command(struct mrvl_nand_host *host, int command)
{
+ struct nand_chip *chip = &host->chip;
+ struct mtd_info *mtd = nand_to_mtd(chip);
+
/* reset data and oob column point to handle data */
host->buf_start = 0;
host->buf_count = 0;
@@ -573,7 +579,7 @@ static void prepare_start_command(struct mrvl_nand_host *host, int command)
if (command == NAND_CMD_READ0 ||
command == NAND_CMD_READOOB ||
command == NAND_CMD_SEQIN) {
- host->buf_count = host->chip.mtd.writesize + host->chip.mtd.oobsize;
+ host->buf_count = mtd->writesize + mtd->oobsize;
memset(host->data_buff, 0xFF, host->buf_count);
}
@@ -590,10 +596,10 @@ static void prepare_start_command(struct mrvl_nand_host *host, int command)
static int prepare_set_command(struct mrvl_nand_host *host, int command,
int ext_cmd_type, uint16_t column, int page_addr)
{
+ struct nand_chip *chip = &host->chip;
+ struct mtd_info *mtd = nand_to_mtd(chip);
int addr_cycle, exec_cmd;
- struct mtd_info *mtd;
- mtd = &host->chip.mtd;
exec_cmd = 1;
if (host->cs != 0)
@@ -880,7 +886,7 @@ static void mrvl_nand_write_buf(struct nand_chip *chip,
static void mrvl_nand_config_flash(struct mrvl_nand_host *host)
{
struct nand_chip *chip = &host->chip;
- struct mtd_info *mtd = &host->chip.mtd;
+ struct mtd_info *mtd = nand_to_mtd(chip);
uint32_t ndcr = host->reg_ndcr;
/* calculate flash information */
@@ -1117,7 +1123,7 @@ static struct mrvl_nand_host *alloc_nand_resource(struct device_d *dev)
host = xzalloc(sizeof(*host));
host->num_cs = 1;
host->cs = 0;
- mtd = &host->chip.mtd;
+ mtd = nand_to_mtd(&host->chip);
mtd->dev.parent = dev;
mtd->name = "mrvl_nand";
@@ -1204,6 +1210,8 @@ static int mrvl_nand_probe_dt(struct mrvl_nand_host *host)
static int mrvl_nand_probe(struct device_d *dev)
{
struct mrvl_nand_host *host;
+ struct nand_chip *chip;
+ struct mtd_info *mtd;
int ret;
host = alloc_nand_resource(dev);
@@ -1216,8 +1224,10 @@ static int mrvl_nand_probe(struct device_d *dev)
if (ret)
return ret;
- host->chip.controller = &host->chip.hwcontrol;
- ret = mrvl_nand_scan(&host->chip);
+ chip = &host->chip;
+ mtd = nand_to_mtd(chip);
+
+ ret = mrvl_nand_scan(chip);
if (ret) {
dev_warn(dev, "failed to scan nand at cs %d\n",
host->cs);
--
2.20.1
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 27+ messages in thread
* [PATCH 18/26] mtd: nand: gpmi: Use nand_to_mtd()
2020-11-06 13:38 [PATCH 00/26] Update nand layer to Linux-5.9 Sascha Hauer
` (16 preceding siblings ...)
2020-11-06 13:38 ` [PATCH 17/26] mtd: nand: marvell: Use nand_to_mtd() Sascha Hauer
@ 2020-11-06 13:38 ` Sascha Hauer
2020-11-06 13:38 ` [PATCH 19/26] mtd: nand: orion: " Sascha Hauer
` (7 subsequent siblings)
25 siblings, 0 replies; 27+ messages in thread
From: Sascha Hauer @ 2020-11-06 13:38 UTC (permalink / raw)
To: Barebox List
We have a function for getting from the struct nand_chip * to the struct
mtd_info *, so use it.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
drivers/mtd/nand/nand_mxs.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/mtd/nand/nand_mxs.c b/drivers/mtd/nand/nand_mxs.c
index 2cf72178bf..7504909faa 100644
--- a/drivers/mtd/nand/nand_mxs.c
+++ b/drivers/mtd/nand/nand_mxs.c
@@ -2190,7 +2190,7 @@ static int mxs_nand_probe(struct device_d *dev)
/* structures must be linked */
chip = &nand_info->nand_chip;
- mtd = &nand_info->nand_chip.mtd;
+ mtd = nand_to_mtd(chip);
mtd->dev.parent = dev;
chip->priv = nand_info;
--
2.20.1
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 27+ messages in thread
* [PATCH 19/26] mtd: nand: orion: Use nand_to_mtd()
2020-11-06 13:38 [PATCH 00/26] Update nand layer to Linux-5.9 Sascha Hauer
` (17 preceding siblings ...)
2020-11-06 13:38 ` [PATCH 18/26] mtd: nand: gpmi: " Sascha Hauer
@ 2020-11-06 13:38 ` Sascha Hauer
2020-11-06 13:38 ` [PATCH 20/26] mtd: nand: Update to Linux-5.9 Sascha Hauer
` (6 subsequent siblings)
25 siblings, 0 replies; 27+ messages in thread
From: Sascha Hauer @ 2020-11-06 13:38 UTC (permalink / raw)
To: Barebox List
We have a function for getting from the struct nand_chip * to the struct
mtd_info *, so use it.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
drivers/mtd/nand/nand_orion.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/mtd/nand/nand_orion.c b/drivers/mtd/nand/nand_orion.c
index c6f8dc68b4..785d877b3d 100644
--- a/drivers/mtd/nand/nand_orion.c
+++ b/drivers/mtd/nand/nand_orion.c
@@ -88,8 +88,8 @@ static int orion_nand_probe(struct device_d *dev)
u32 val = 0;
priv = xzalloc(sizeof(struct orion_nand));
- mtd = &priv->chip.mtd;
chip = &priv->chip;
+ mtd = nand_to_mtd(chip);
iores = dev_request_mem_resource(dev, 0);
if (IS_ERR(iores))
--
2.20.1
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 27+ messages in thread
* [PATCH 20/26] mtd: nand: Update to Linux-5.9
2020-11-06 13:38 [PATCH 00/26] Update nand layer to Linux-5.9 Sascha Hauer
` (18 preceding siblings ...)
2020-11-06 13:38 ` [PATCH 19/26] mtd: nand: orion: " Sascha Hauer
@ 2020-11-06 13:38 ` Sascha Hauer
2020-11-06 13:38 ` [PATCH 21/26] mtd: nand: denali: " Sascha Hauer
` (5 subsequent siblings)
25 siblings, 0 replies; 27+ messages in thread
From: Sascha Hauer @ 2020-11-06 13:38 UTC (permalink / raw)
To: Barebox List
This updates the barebox NAND layer and parts of the mtd layer to
Linux-5.9.
This patch is huge, but the barebox NAND layer is so far away from the
Linux NAND layer that a step by step update would have taken ages.
Unlike Linux barebox has functions to mark a block as good. This feature
has been preserved. Also barebox used to make NAND write support
optional, this feature is lost during the update for the sake of better
compatibility to the Linux NAND layer.
This patch has been tested:
- GPMI aka nand_mxs on i.MX6
- nand_imx on i.MX25
- nand_omap_gpmc on AM335x
- atmel_nand on Atmel sama5d3
- nand_denali on SoCFPGA
Currently untested:
- nand_orion
- nand_mrvl_nfc
- nand_s3c24xx
The nand_denali driver is tested with the update of that driver to
Linux-5.9 following in the next patch.
I could only test the drivers with the NAND chips found on my boards, so
there's still enough room for regressions, especially given that the
NAND drivers themselves are mostly not updated. With the NAND layer
being up-to-date with Linux it should hopefully be easy to update
drivers to their Linux counterpart as well if necessary.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
arch/arm/boards/sama5d3_xplained/init.c | 1 +
arch/arm/configs/nhk8815_defconfig | 2 +-
arch/arm/mach-imx/external-nand-boot.c | 1 +
arch/arm/mach-imx/xload-imx-nand.c | 1 +
commands/nand-bitflip.c | 21 +-
drivers/bus/omap-gpmc.c | 1 +
drivers/mtd/Kconfig | 2 -
drivers/mtd/core.c | 557 +-
drivers/mtd/mtdraw.c | 1 -
drivers/mtd/nand/Kconfig | 53 +-
drivers/mtd/nand/Makefile | 11 +-
drivers/mtd/nand/atmel_nand.c | 95 +-
drivers/mtd/nand/bbt.c | 132 +
drivers/mtd/nand/core.c | 275 +
drivers/mtd/nand/denali.h | 1 +
drivers/mtd/nand/internals.h | 170 +
drivers/mtd/nand/nand_amd.c | 53 +
drivers/mtd/nand/nand_base.c | 7755 ++++++++++++++---------
drivers/mtd/nand/nand_bbt.c | 317 +-
drivers/mtd/nand/nand_bch.c | 88 +-
drivers/mtd/nand/nand_denali.c | 67 +-
drivers/mtd/nand/nand_ecc.c | 573 +-
drivers/mtd/nand/nand_esmt.c | 54 +
drivers/mtd/nand/nand_hynix.c | 716 +++
drivers/mtd/nand/nand_ids.c | 258 +-
drivers/mtd/nand/nand_imx.c | 46 +-
drivers/mtd/nand/nand_jedec.c | 135 +
drivers/mtd/nand/nand_legacy.c | 629 ++
drivers/mtd/nand/nand_macronix.c | 334 +
drivers/mtd/nand/nand_micron.c | 595 ++
drivers/mtd/nand/nand_mrvl_nfc.c | 33 +-
drivers/mtd/nand/nand_mxs.c | 37 +-
drivers/mtd/nand/nand_omap_gpmc.c | 60 +-
drivers/mtd/nand/nand_onfi.c | 326 +
drivers/mtd/nand/nand_orion.c | 3 +-
drivers/mtd/nand/nand_s3c24xx.c | 7 +-
drivers/mtd/nand/nand_samsung.c | 135 +
drivers/mtd/nand/nand_timings.c | 574 +-
drivers/mtd/nand/nand_toshiba.c | 274 +
drivers/mtd/nand/nomadik_nand.c | 7 +-
drivers/mtd/partition.c | 1 +
drivers/mtd/peb.c | 1 -
drivers/of/of_mtd.c | 3 +
include/linux/mtd/jedec.h | 91 +
include/linux/mtd/mtd-abi.h | 1 +
include/linux/mtd/mtd.h | 98 +-
include/linux/mtd/nand.h | 1354 ++--
include/linux/mtd/nand_bch.h | 22 +-
include/linux/mtd/nand_ecc.h | 35 +-
include/linux/mtd/onfi.h | 178 +
include/linux/mtd/partitions.h | 115 +
include/linux/mtd/rawnand.h | 1464 +++++
52 files changed, 13209 insertions(+), 4554 deletions(-)
create mode 100644 drivers/mtd/nand/bbt.c
create mode 100644 drivers/mtd/nand/core.c
create mode 100644 drivers/mtd/nand/internals.h
create mode 100644 drivers/mtd/nand/nand_amd.c
create mode 100644 drivers/mtd/nand/nand_esmt.c
create mode 100644 drivers/mtd/nand/nand_hynix.c
create mode 100644 drivers/mtd/nand/nand_jedec.c
create mode 100644 drivers/mtd/nand/nand_legacy.c
create mode 100644 drivers/mtd/nand/nand_macronix.c
create mode 100644 drivers/mtd/nand/nand_micron.c
create mode 100644 drivers/mtd/nand/nand_onfi.c
create mode 100644 drivers/mtd/nand/nand_samsung.c
create mode 100644 drivers/mtd/nand/nand_toshiba.c
create mode 100644 include/linux/mtd/jedec.h
create mode 100644 include/linux/mtd/onfi.h
create mode 100644 include/linux/mtd/partitions.h
create mode 100644 include/linux/mtd/rawnand.h
diff --git a/arch/arm/boards/sama5d3_xplained/init.c b/arch/arm/boards/sama5d3_xplained/init.c
index ccddd01dea..b648d71722 100644
--- a/arch/arm/boards/sama5d3_xplained/init.c
+++ b/arch/arm/boards/sama5d3_xplained/init.c
@@ -23,6 +23,7 @@
#include <mach/at91_pmc.h>
#include <mach/at91_rstc.h>
#include <mach/at91sam9x5_matrix.h>
+#include <linux/mtd/rawnand.h>
#include <readkey.h>
#include <poller.h>
#include <linux/clk.h>
diff --git a/arch/arm/configs/nhk8815_defconfig b/arch/arm/configs/nhk8815_defconfig
index a29e38fad9..bc19301249 100644
--- a/arch/arm/configs/nhk8815_defconfig
+++ b/arch/arm/configs/nhk8815_defconfig
@@ -44,7 +44,7 @@ CONFIG_DRIVER_NET_SMC91111=y
# CONFIG_SPI is not set
CONFIG_MTD=y
CONFIG_NAND=y
-CONFIG_MTD_NAND_ECC_SMC=y
+CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC=y
CONFIG_MTD_NAND_NOMADIK=y
CONFIG_FS_CRAMFS=y
CONFIG_FS_TFTP=y
diff --git a/arch/arm/mach-imx/external-nand-boot.c b/arch/arm/mach-imx/external-nand-boot.c
index 123589c071..893bfdb77f 100644
--- a/arch/arm/mach-imx/external-nand-boot.c
+++ b/arch/arm/mach-imx/external-nand-boot.c
@@ -15,6 +15,7 @@
#include <init.h>
#include <io.h>
#include <linux/mtd/nand.h>
+#include <linux/mtd/rawnand.h>
#include <asm/cache.h>
#include <asm/sections.h>
#include <asm/barebox-arm.h>
diff --git a/arch/arm/mach-imx/xload-imx-nand.c b/arch/arm/mach-imx/xload-imx-nand.c
index 22e41fac77..ff54941a0c 100644
--- a/arch/arm/mach-imx/xload-imx-nand.c
+++ b/arch/arm/mach-imx/xload-imx-nand.c
@@ -15,6 +15,7 @@
#include <common.h>
#include <init.h>
#include <io.h>
+#include <linux/mtd/rawnand.h>
#include <linux/mtd/nand.h>
#include <mach/imx-nand.h>
#include <mach/generic.h>
diff --git a/commands/nand-bitflip.c b/commands/nand-bitflip.c
index 7335f77379..217a243f66 100644
--- a/commands/nand-bitflip.c
+++ b/commands/nand-bitflip.c
@@ -10,6 +10,25 @@
#include <linux/mtd/mtd.h>
#include <mtd/mtd-peb.h>
+static int bitflip_mtd_read(struct mtd_info *mtd, loff_t from, size_t len,
+ size_t *retlen, void *buf)
+{
+ int ret_code;
+
+ if (mtd->_read_oob) {
+ struct mtd_oob_ops ops = {
+ .len = len,
+ .datbuf = buf,
+ };
+
+ ret_code = mtd->_read_oob(mtd, from, &ops);
+ } else {
+ ret_code = mtd->_read(mtd, from, len, retlen, buf);
+ }
+
+ return ret_code;
+}
+
static int do_nand_bitflip(int argc, char *argv[])
{
int opt, ret, fd;
@@ -77,7 +96,7 @@ static int do_nand_bitflip(int argc, char *argv[])
buf = xzalloc(meminfo.writesize);
roffset = (loff_t)block * meminfo.mtd->erasesize + offset;
- ret = meminfo.mtd->read(meminfo.mtd, roffset, meminfo.writesize, &r, buf);
+ ret = bitflip_mtd_read(meminfo.mtd, roffset, meminfo.writesize, &r, buf);
if (ret > 0) {
printf("page at block %d, offset 0x%08llx has %d bitflips%s\n",
block, offset, ret,
diff --git a/drivers/bus/omap-gpmc.c b/drivers/bus/omap-gpmc.c
index 8fd7a91740..cd5b6d5e03 100644
--- a/drivers/bus/omap-gpmc.c
+++ b/drivers/bus/omap-gpmc.c
@@ -18,6 +18,7 @@
#include <of_mtd.h>
#include <linux/clk.h>
#include <linux/err.h>
+#include <linux/mtd/rawnand.h>
#include <mach/gpmc_nand.h>
#include <mach/gpmc.h>
diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig
index db9c287b45..36dbe9f825 100644
--- a/drivers/mtd/Kconfig
+++ b/drivers/mtd/Kconfig
@@ -10,13 +10,11 @@ config MTD_WRITE
config MTD_OOB_DEVICE
bool
- select NAND_READ_OOB if NAND
default y
prompt "Create a device for reading the OOB data"
config MTD_RAW_DEVICE
bool
- select NAND_READ_OOB if NAND
default n
prompt "mtdraw device to read/write both data+oob"
diff --git a/drivers/mtd/core.c b/drivers/mtd/core.c
index 8d018fde57..22ed8d2d54 100644
--- a/drivers/mtd/core.c
+++ b/drivers/mtd/core.c
@@ -15,6 +15,7 @@
#include <common.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
#include <mtd/mtd-peb.h>
#include <mtd/ubi-user.h>
#include <cmdlinepart.h>
@@ -252,9 +253,7 @@ int mtd_ioctl(struct cdev *cdev, int request, void *buf)
int ret = 0;
struct mtd_info *mtd = cdev->priv;
struct mtd_info_user *user = buf;
-#if (defined(CONFIG_NAND_ECC_HW) || defined(CONFIG_NAND_ECC_SOFT))
struct mtd_ecc_stats *ecc = buf;
-#endif
struct region_info_user *reg = buf;
#ifdef CONFIG_MTD_WRITE
struct erase_info_user *ei = buf;
@@ -292,14 +291,12 @@ int mtd_ioctl(struct cdev *cdev, int request, void *buf)
user->ecctype = -1;
user->eccsize = 0;
break;
-#if (defined(CONFIG_NAND_ECC_HW) || defined(CONFIG_NAND_ECC_SOFT))
case ECCGETSTATS:
ecc->corrected = mtd->ecc_stats.corrected;
ecc->failed = mtd->ecc_stats.failed;
ecc->badblocks = mtd->ecc_stats.badblocks;
ecc->bbtblocks = mtd->ecc_stats.bbtblocks;
break;
-#endif
case MEMGETREGIONINFO:
if (cdev->mtd) {
unsigned long size = cdev->size;
@@ -377,42 +374,66 @@ int mtd_block_markgood(struct mtd_info *mtd, loff_t ofs)
}
int mtd_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen,
- u_char *buf)
+ u_char *buf)
{
- int ret_code;
- *retlen = 0;
+ struct mtd_oob_ops ops = {
+ .len = len,
+ .datbuf = buf,
+ };
+ int ret;
if (from < 0 || from >= mtd->size || len > mtd->size - from)
return -EINVAL;
if (!len)
return 0;
- /*
- * In the absence of an error, drivers return a non-negative integer
- * representing the maximum number of bitflips that were corrected on
- * any one ecc region (if applicable; zero otherwise).
- */
- ret_code = mtd->_read(mtd, from, len, retlen, buf);
- if (unlikely(ret_code < 0))
- return ret_code;
- if (mtd->ecc_strength == 0)
- return 0; /* device lacks ecc */
- return ret_code >= mtd->bitflip_threshold ? -EUCLEAN : 0;
+ ret = mtd_read_oob(mtd, from, &ops);
+ *retlen = ops.retlen;
+
+ return ret;
+}
+
+int mtd_write_oob(struct mtd_info *mtd, loff_t to,
+ struct mtd_oob_ops *ops)
+{
+ int ret;
+
+ ops->retlen = ops->oobretlen = 0;
+
+ if (!(mtd->flags & MTD_WRITEABLE))
+ return -EROFS;
+
+ /* Check the validity of a potential fallback on mtd->_write */
+ if (!mtd->_write_oob && (!mtd->_write || ops->oobbuf))
+ return -EOPNOTSUPP;
+
+ if (mtd->_write_oob)
+ ret = mtd->_write_oob(mtd, to, ops);
+ else
+ ret = mtd->_write(mtd, to, ops->len, &ops->retlen,
+ ops->datbuf);
+
+ return ret;
}
int mtd_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen,
- const u_char *buf)
+ const u_char *buf)
{
- *retlen = 0;
+ struct mtd_oob_ops ops = {
+ .len = len,
+ .datbuf = (u8 *)buf,
+ };
+ int ret;
if (to < 0 || to >= mtd->size || len > mtd->size - to)
return -EINVAL;
- if (!mtd->_write || !(mtd->flags & MTD_WRITEABLE))
- return -EROFS;
if (!len)
return 0;
- return mtd->_write(mtd, to, len, retlen, buf);
+ ret = mtd_write_oob(mtd, to, &ops);
+ *retlen = ops.retlen;
+
+ return ret;
}
int mtd_erase(struct mtd_info *mtd, struct erase_info *instr)
@@ -433,15 +454,23 @@ int mtd_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops)
int ret_code;
ops->retlen = ops->oobretlen = 0;
- if (!mtd->_read_oob)
+
+ /* Check the validity of a potential fallback on mtd->_read */
+ if (!mtd->_read_oob && (!mtd->_read || ops->oobbuf))
return -EOPNOTSUPP;
+
/*
* In cases where ops->datbuf != NULL, mtd->_read_oob() has semantics
* similar to mtd->_read(), returning a non-negative integer
* representing max bitflips. In other cases, mtd->_read_oob() may
* return -EUCLEAN. In all cases, perform similar logic to mtd_read().
*/
- ret_code = mtd->_read_oob(mtd, from, ops);
+ if (mtd->_read_oob)
+ ret_code = mtd->_read_oob(mtd, from, ops);
+ else
+ ret_code = mtd->_read(mtd, from, ops->len, &ops->retlen,
+ ops->datbuf);
+
if (unlikely(ret_code < 0))
return ret_code;
if (mtd->ecc_strength == 0)
@@ -742,3 +771,481 @@ const char *mtd_type_str(struct mtd_info *mtd)
return "unknown";
}
}
+
+/**
+ * mtd_ooblayout_ecc - Get the OOB region definition of a specific ECC section
+ * @mtd: MTD device structure
+ * @section: ECC section. Depending on the layout you may have all the ECC
+ * bytes stored in a single contiguous section, or one section
+ * per ECC chunk (and sometime several sections for a single ECC
+ * ECC chunk)
+ * @oobecc: OOB region struct filled with the appropriate ECC position
+ * information
+ *
+ * This function returns ECC section information in the OOB area. If you want
+ * to get all the ECC bytes information, then you should call
+ * mtd_ooblayout_ecc(mtd, section++, oobecc) until it returns -ERANGE.
+ *
+ * Returns zero on success, a negative error code otherwise.
+ */
+int mtd_ooblayout_ecc(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobecc)
+{
+ memset(oobecc, 0, sizeof(*oobecc));
+
+ if (section < 0)
+ return -EINVAL;
+
+ if (!mtd->ooblayout || !mtd->ooblayout->ecc)
+ return -ENOTSUPP;
+
+ return mtd->ooblayout->ecc(mtd, section, oobecc);
+}
+EXPORT_SYMBOL_GPL(mtd_ooblayout_ecc);
+
+/**
+ * mtd_ooblayout_free - Get the OOB region definition of a specific free
+ * section
+ * @mtd: MTD device structure
+ * @section: Free section you are interested in. Depending on the layout
+ * you may have all the free bytes stored in a single contiguous
+ * section, or one section per ECC chunk plus an extra section
+ * for the remaining bytes (or other funky layout).
+ * @oobfree: OOB region struct filled with the appropriate free position
+ * information
+ *
+ * This function returns free bytes position in the OOB area. If you want
+ * to get all the free bytes information, then you should call
+ * mtd_ooblayout_free(mtd, section++, oobfree) until it returns -ERANGE.
+ *
+ * Returns zero on success, a negative error code otherwise.
+ */
+int mtd_ooblayout_free(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobfree)
+{
+ memset(oobfree, 0, sizeof(*oobfree));
+
+ if (section < 0)
+ return -EINVAL;
+
+ if (!mtd->ooblayout || !mtd->ooblayout->free)
+ return -ENOTSUPP;
+
+ return mtd->ooblayout->free(mtd, section, oobfree);
+}
+EXPORT_SYMBOL_GPL(mtd_ooblayout_free);
+
+/**
+ * mtd_ooblayout_find_region - Find the region attached to a specific byte
+ * @mtd: mtd info structure
+ * @byte: the byte we are searching for
+ * @sectionp: pointer where the section id will be stored
+ * @oobregion: used to retrieve the ECC position
+ * @iter: iterator function. Should be either mtd_ooblayout_free or
+ * mtd_ooblayout_ecc depending on the region type you're searching for
+ *
+ * This function returns the section id and oobregion information of a
+ * specific byte. For example, say you want to know where the 4th ECC byte is
+ * stored, you'll use:
+ *
+ * mtd_ooblayout_find_region(mtd, 3, §ion, &oobregion, mtd_ooblayout_ecc);
+ *
+ * Returns zero on success, a negative error code otherwise.
+ */
+static int mtd_ooblayout_find_region(struct mtd_info *mtd, int byte,
+ int *sectionp, struct mtd_oob_region *oobregion,
+ int (*iter)(struct mtd_info *,
+ int section,
+ struct mtd_oob_region *oobregion))
+{
+ int pos = 0, ret, section = 0;
+
+ memset(oobregion, 0, sizeof(*oobregion));
+
+ while (1) {
+ ret = iter(mtd, section, oobregion);
+ if (ret)
+ return ret;
+
+ if (pos + oobregion->length > byte)
+ break;
+
+ pos += oobregion->length;
+ section++;
+ }
+
+ /*
+ * Adjust region info to make it start at the beginning at the
+ * 'start' ECC byte.
+ */
+ oobregion->offset += byte - pos;
+ oobregion->length -= byte - pos;
+ *sectionp = section;
+
+ return 0;
+}
+
+/**
+ * mtd_ooblayout_find_eccregion - Find the ECC region attached to a specific
+ * ECC byte
+ * @mtd: mtd info structure
+ * @eccbyte: the byte we are searching for
+ * @sectionp: pointer where the section id will be stored
+ * @oobregion: OOB region information
+ *
+ * Works like mtd_ooblayout_find_region() except it searches for a specific ECC
+ * byte.
+ *
+ * Returns zero on success, a negative error code otherwise.
+ */
+int mtd_ooblayout_find_eccregion(struct mtd_info *mtd, int eccbyte,
+ int *section,
+ struct mtd_oob_region *oobregion)
+{
+ return mtd_ooblayout_find_region(mtd, eccbyte, section, oobregion,
+ mtd_ooblayout_ecc);
+}
+EXPORT_SYMBOL_GPL(mtd_ooblayout_find_eccregion);
+
+/**
+ * mtd_ooblayout_get_bytes - Extract OOB bytes from the oob buffer
+ * @mtd: mtd info structure
+ * @buf: destination buffer to store OOB bytes
+ * @oobbuf: OOB buffer
+ * @start: first byte to retrieve
+ * @nbytes: number of bytes to retrieve
+ * @iter: section iterator
+ *
+ * Extract bytes attached to a specific category (ECC or free)
+ * from the OOB buffer and copy them into buf.
+ *
+ * Returns zero on success, a negative error code otherwise.
+ */
+static int mtd_ooblayout_get_bytes(struct mtd_info *mtd, u8 *buf,
+ const u8 *oobbuf, int start, int nbytes,
+ int (*iter)(struct mtd_info *,
+ int section,
+ struct mtd_oob_region *oobregion))
+{
+ struct mtd_oob_region oobregion;
+ int section, ret;
+
+ ret = mtd_ooblayout_find_region(mtd, start, §ion,
+ &oobregion, iter);
+
+ while (!ret) {
+ int cnt;
+
+ cnt = min_t(int, nbytes, oobregion.length);
+ memcpy(buf, oobbuf + oobregion.offset, cnt);
+ buf += cnt;
+ nbytes -= cnt;
+
+ if (!nbytes)
+ break;
+
+ ret = iter(mtd, ++section, &oobregion);
+ }
+
+ return ret;
+}
+
+/**
+ * mtd_ooblayout_set_bytes - put OOB bytes into the oob buffer
+ * @mtd: mtd info structure
+ * @buf: source buffer to get OOB bytes from
+ * @oobbuf: OOB buffer
+ * @start: first OOB byte to set
+ * @nbytes: number of OOB bytes to set
+ * @iter: section iterator
+ *
+ * Fill the OOB buffer with data provided in buf. The category (ECC or free)
+ * is selected by passing the appropriate iterator.
+ *
+ * Returns zero on success, a negative error code otherwise.
+ */
+static int mtd_ooblayout_set_bytes(struct mtd_info *mtd, const u8 *buf,
+ u8 *oobbuf, int start, int nbytes,
+ int (*iter)(struct mtd_info *,
+ int section,
+ struct mtd_oob_region *oobregion))
+{
+ struct mtd_oob_region oobregion;
+ int section, ret;
+
+ ret = mtd_ooblayout_find_region(mtd, start, §ion,
+ &oobregion, iter);
+
+ while (!ret) {
+ int cnt;
+
+ cnt = min_t(int, nbytes, oobregion.length);
+ memcpy(oobbuf + oobregion.offset, buf, cnt);
+ buf += cnt;
+ nbytes -= cnt;
+
+ if (!nbytes)
+ break;
+
+ ret = iter(mtd, ++section, &oobregion);
+ }
+
+ return ret;
+}
+
+/**
+ * mtd_ooblayout_count_bytes - count the number of bytes in a OOB category
+ * @mtd: mtd info structure
+ * @iter: category iterator
+ *
+ * Count the number of bytes in a given category.
+ *
+ * Returns a positive value on success, a negative error code otherwise.
+ */
+static int mtd_ooblayout_count_bytes(struct mtd_info *mtd,
+ int (*iter)(struct mtd_info *,
+ int section,
+ struct mtd_oob_region *oobregion))
+{
+ struct mtd_oob_region oobregion;
+ int section = 0, ret, nbytes = 0;
+
+ while (1) {
+ ret = iter(mtd, section++, &oobregion);
+ if (ret) {
+ if (ret == -ERANGE)
+ ret = nbytes;
+ break;
+ }
+
+ nbytes += oobregion.length;
+ }
+
+ return ret;
+}
+
+/**
+ * mtd_ooblayout_get_eccbytes - extract ECC bytes from the oob buffer
+ * @mtd: mtd info structure
+ * @eccbuf: destination buffer to store ECC bytes
+ * @oobbuf: OOB buffer
+ * @start: first ECC byte to retrieve
+ * @nbytes: number of ECC bytes to retrieve
+ *
+ * Works like mtd_ooblayout_get_bytes(), except it acts on ECC bytes.
+ *
+ * Returns zero on success, a negative error code otherwise.
+ */
+int mtd_ooblayout_get_eccbytes(struct mtd_info *mtd, u8 *eccbuf,
+ const u8 *oobbuf, int start, int nbytes)
+{
+ return mtd_ooblayout_get_bytes(mtd, eccbuf, oobbuf, start, nbytes,
+ mtd_ooblayout_ecc);
+}
+EXPORT_SYMBOL_GPL(mtd_ooblayout_get_eccbytes);
+
+/**
+ * mtd_ooblayout_set_eccbytes - set ECC bytes into the oob buffer
+ * @mtd: mtd info structure
+ * @eccbuf: source buffer to get ECC bytes from
+ * @oobbuf: OOB buffer
+ * @start: first ECC byte to set
+ * @nbytes: number of ECC bytes to set
+ *
+ * Works like mtd_ooblayout_set_bytes(), except it acts on ECC bytes.
+ *
+ * Returns zero on success, a negative error code otherwise.
+ */
+int mtd_ooblayout_set_eccbytes(struct mtd_info *mtd, const u8 *eccbuf,
+ u8 *oobbuf, int start, int nbytes)
+{
+ return mtd_ooblayout_set_bytes(mtd, eccbuf, oobbuf, start, nbytes,
+ mtd_ooblayout_ecc);
+}
+EXPORT_SYMBOL_GPL(mtd_ooblayout_set_eccbytes);
+
+/**
+ * mtd_ooblayout_get_databytes - extract data bytes from the oob buffer
+ * @mtd: mtd info structure
+ * @databuf: destination buffer to store ECC bytes
+ * @oobbuf: OOB buffer
+ * @start: first ECC byte to retrieve
+ * @nbytes: number of ECC bytes to retrieve
+ *
+ * Works like mtd_ooblayout_get_bytes(), except it acts on free bytes.
+ *
+ * Returns zero on success, a negative error code otherwise.
+ */
+int mtd_ooblayout_get_databytes(struct mtd_info *mtd, u8 *databuf,
+ const u8 *oobbuf, int start, int nbytes)
+{
+ return mtd_ooblayout_get_bytes(mtd, databuf, oobbuf, start, nbytes,
+ mtd_ooblayout_free);
+}
+EXPORT_SYMBOL_GPL(mtd_ooblayout_get_databytes);
+
+/**
+ * mtd_ooblayout_set_databytes - set data bytes into the oob buffer
+ * @mtd: mtd info structure
+ * @databuf: source buffer to get data bytes from
+ * @oobbuf: OOB buffer
+ * @start: first ECC byte to set
+ * @nbytes: number of ECC bytes to set
+ *
+ * Works like mtd_ooblayout_set_bytes(), except it acts on free bytes.
+ *
+ * Returns zero on success, a negative error code otherwise.
+ */
+int mtd_ooblayout_set_databytes(struct mtd_info *mtd, const u8 *databuf,
+ u8 *oobbuf, int start, int nbytes)
+{
+ return mtd_ooblayout_set_bytes(mtd, databuf, oobbuf, start, nbytes,
+ mtd_ooblayout_free);
+}
+EXPORT_SYMBOL_GPL(mtd_ooblayout_set_databytes);
+
+/**
+ * mtd_ooblayout_count_freebytes - count the number of free bytes in OOB
+ * @mtd: mtd info structure
+ *
+ * Works like mtd_ooblayout_count_bytes(), except it count free bytes.
+ *
+ * Returns zero on success, a negative error code otherwise.
+ */
+int mtd_ooblayout_count_freebytes(struct mtd_info *mtd)
+{
+ return mtd_ooblayout_count_bytes(mtd, mtd_ooblayout_free);
+}
+EXPORT_SYMBOL_GPL(mtd_ooblayout_count_freebytes);
+
+/**
+ * mtd_ooblayout_count_eccbytes - count the number of ECC bytes in OOB
+ * @mtd: mtd info structure
+ *
+ * Works like mtd_ooblayout_count_bytes(), except it count ECC bytes.
+ *
+ * Returns zero on success, a negative error code otherwise.
+ */
+int mtd_ooblayout_count_eccbytes(struct mtd_info *mtd)
+{
+ return mtd_ooblayout_count_bytes(mtd, mtd_ooblayout_ecc);
+}
+EXPORT_SYMBOL_GPL(mtd_ooblayout_count_eccbytes);
+
+
+/**
+ * mtd_ecclayout_ecc - Default ooblayout_ecc iterator implementation
+ * @mtd: MTD device structure
+ * @section: ECC section. Depending on the layout you may have all the ECC
+ * bytes stored in a single contiguous section, or one section
+ * per ECC chunk (and sometime several sections for a single ECC
+ * ECC chunk)
+ * @oobecc: OOB region struct filled with the appropriate ECC position
+ * information
+ *
+ * This function is just a wrapper around the mtd->ecclayout field and is
+ * here to ease the transition to the mtd_ooblayout_ops approach.
+ * All it does is convert the layout->eccpos information into proper oob
+ * region definitions.
+ *
+ * Returns zero on success, a negative error code otherwise.
+ */
+static int mtd_ecclayout_ecc(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobecc)
+{
+ int eccbyte = 0, cursection = 0, length = 0, eccpos = 0;
+
+ if (!mtd->ecclayout)
+ return -ENOTSUPP;
+
+ /*
+ * This logic allows us to reuse the ->ecclayout information and
+ * expose them as ECC regions (as done for the OOB free regions).
+ *
+ * TODO: this should be dropped as soon as we get rid of the
+ * ->ecclayout field.
+ */
+ for (eccbyte = 0; eccbyte < mtd->ecclayout->eccbytes; eccbyte++) {
+ eccpos = mtd->ecclayout->eccpos[eccbyte];
+
+ if (eccbyte < mtd->ecclayout->eccbytes - 1) {
+ int neccpos = mtd->ecclayout->eccpos[eccbyte + 1];
+
+ if (eccpos + 1 == neccpos) {
+ length++;
+ continue;
+ }
+ }
+
+ if (section == cursection)
+ break;
+
+ length = 0;
+ cursection++;
+ }
+
+ if (cursection != section || eccbyte >= mtd->ecclayout->eccbytes)
+ return -ERANGE;
+
+ oobecc->length = length + 1;
+ oobecc->offset = eccpos - length;
+
+ return 0;
+}
+
+/**
+ * mtd_ecclayout_ecc - Default ooblayout_free iterator implementation
+ * @mtd: MTD device structure
+ * @section: Free section. Depending on the layout you may have all the free
+ * bytes stored in a single contiguous section, or one section
+ * per ECC chunk (and sometime several sections for a single ECC
+ * ECC chunk)
+ * @oobfree: OOB region struct filled with the appropriate free position
+ * information
+ *
+ * This function is just a wrapper around the mtd->ecclayout field and is
+ * here to ease the transition to the mtd_ooblayout_ops approach.
+ * All it does is convert the layout->oobfree information into proper oob
+ * region definitions.
+ *
+ * Returns zero on success, a negative error code otherwise.
+ */
+static int mtd_ecclayout_free(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobfree)
+{
+ struct nand_ecclayout *layout = mtd->ecclayout;
+
+ if (!layout)
+ return -ENOTSUPP;
+
+ if (section >= MTD_MAX_OOBFREE_ENTRIES_LARGE ||
+ !layout->oobfree[section].length)
+ return -ERANGE;
+
+ oobfree->offset = layout->oobfree[section].offset;
+ oobfree->length = layout->oobfree[section].length;
+
+ return 0;
+}
+
+static const struct mtd_ooblayout_ops mtd_ecclayout_wrapper_ops = {
+ .ecc = mtd_ecclayout_ecc,
+ .free = mtd_ecclayout_free,
+};
+
+/**
+ * mtd_set_ecclayout - Attach an ecclayout to an MTD device
+ * @mtd: MTD device structure
+ * @ecclayout: The ecclayout to attach to the device
+ *
+ * Returns zero on success, a negative error code otherwise.
+ */
+void mtd_set_ecclayout(struct mtd_info *mtd, struct nand_ecclayout *ecclayout)
+{
+ if (!mtd || !ecclayout)
+ return;
+
+ mtd->ecclayout = ecclayout;
+ mtd_set_ooblayout(mtd, &mtd_ecclayout_wrapper_ops);
+}
+EXPORT_SYMBOL_GPL(mtd_set_ecclayout);
diff --git a/drivers/mtd/mtdraw.c b/drivers/mtd/mtdraw.c
index 19a24cc650..57630647b7 100644
--- a/drivers/mtd/mtdraw.c
+++ b/drivers/mtd/mtdraw.c
@@ -247,7 +247,6 @@ static int mtdraw_erase(struct cdev *cdev, loff_t count, loff_t offset)
count = mtdraw_raw_to_mtd_offset(mtdraw, count);
memset(&erase, 0, sizeof(erase));
- erase.mtd = mtd;
erase.addr = offset;
erase.len = mtd->erasesize;
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index b81e72d6b7..6a162f81f6 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -1,6 +1,5 @@
menuconfig NAND
bool "NAND support"
- select MTD_NAND_IDS
help
This enables support for accessing all type of NAND flash
devices. For further information see
@@ -8,55 +7,11 @@ menuconfig NAND
if NAND
-config NAND_ECC_SOFT
- bool
- default y
- prompt "Support software ecc"
-
-config NAND_ECC_BCH
+config MTD_NAND_ECC_SW_BCH
select BCH
bool
prompt "Support software BCH ecc"
-config NAND_ECC_HW
- bool
- default y
- prompt "Support hardware ecc"
-
-config NAND_ECC_HW_OOB_FIRST
- bool
- prompt "Support hardware ecc (oob first)"
-
-config NAND_ECC_HW_SYNDROME
- bool
- default y
- prompt "Support syndrome hardware ecc controllers"
-
-config NAND_ECC_HW_NONE
- bool
- default y
- prompt "Support skipping ecc support"
-
-config NAND_INFO
- bool
- default y
- prompt "Nand vendor/size information"
- help
- Show informational strings about the vendor and nand flash type
- during startup
-
-config NAND_READ_OOB
- bool
-
-config NAND_BBT
- bool
- select NAND_READ_OOB
- default y
- prompt "support bad block tables"
- help
- Say y here to include support for bad block tables. This speeds
- up the process of checking for bad blocks
-
config NAND_ALLOW_ERASE_BAD
bool
depends on MTD_WRITE
@@ -119,7 +74,6 @@ config NAND_ATMEL_PMECC
bool
prompt "PMECC support"
depends on NAND_ATMEL || COMPILE_TEST
- select NAND_ECC_HW
help
Support for PMECC present on the SoC sam9x5 and sam9n12
@@ -130,16 +84,13 @@ config NAND_S3C24XX
help
Add support for processor's NAND device controller.
-config MTD_NAND_ECC_SMC
+config MTD_NAND_ECC_SW_HAMMING_SMC
bool "NAND ECC Smart Media byte order"
default n
help
Software ECC according to the Smart Media Specification.
The original Linux implementation had byte 0 and 1 swapped.
-config MTD_NAND_IDS
- tristate
-
config MTD_NAND_NOMADIK
tristate "ST Nomadik 8815 NAND support"
depends on ARCH_NOMADIK
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index 274bc29ee7..f6e5b41e94 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -1,10 +1,14 @@
# Generic NAND options
obj-$(CONFIG_NAND) += nand_ecc.o
-obj-$(CONFIG_NAND_ECC_BCH) += nand_bch.o
-obj-$(CONFIG_MTD_NAND_IDS) += nand_ids.o
+obj-$(CONFIG_MTD_NAND_ECC_SW_BCH) += nand_bch.o
+obj-$(CONFIG_NAND) += nand_ids.o
obj-$(CONFIG_NAND) += nand_base.o nand-bb.o nand_timings.o
-obj-$(CONFIG_NAND_BBT) += nand_bbt.o
+obj-$(CONFIG_NAND) += nand_legacy.o nand_onfi.o nand_amd.o
+obj-$(CONFIG_NAND) += nand_esmt.o nand_hynix.o nand_macronix.o
+obj-$(CONFIG_NAND) += nand_micron.o nand_samsung.o nand_toshiba.o
+obj-$(CONFIG_NAND) += nand_jedec.o core.o bbt.o
+obj-$(CONFIG_NAND) += nand_bbt.o
obj-$(CONFIG_MTD_NAND_NOMADIK) += nomadik_nand.o
obj-$(CONFIG_NAND_IMX) += nand_imx.o
@@ -18,4 +22,3 @@ pbl-$(CONFIG_NAND_S3C24XX) += nand_s3c24xx.o
obj-$(CONFIG_NAND_MXS) += nand_mxs.o
obj-$(CONFIG_MTD_NAND_DENALI) += nand_denali.o
obj-$(CONFIG_MTD_NAND_DENALI_DT) += nand_denali_dt.o
-
diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c
index d86498c86e..6903fb4c9d 100644
--- a/drivers/mtd/nand/atmel_nand.c
+++ b/drivers/mtd/nand/atmel_nand.c
@@ -33,6 +33,7 @@
#include <of_mtd.h>
#include <linux/mtd/mtd.h>
+#include <linux/mtd/rawnand.h>
#include <linux/mtd/nand.h>
#include <linux/err.h>
@@ -102,6 +103,8 @@ struct atmel_nand_host {
int *pmecc_mu;
int *pmecc_dmu;
int *pmecc_delta;
+ struct nand_ecclayout *ecclayout;
+ void *ecc_code;
};
static struct nand_ecclayout atmel_pmecc_oobinfo;
@@ -535,7 +538,7 @@ static void pmecc_correct_data(struct mtd_info *mtd, uint8_t *buf, uint8_t *ecc,
struct nand_chip *nand_chip = mtd_to_nand(mtd);
struct atmel_nand_host *host = nand_chip->priv;
int i = 0;
- int byte_pos, bit_pos, sector_size, pos;
+ int byte_pos, bit_pos, sector_size;
uint32_t tmp;
uint8_t err_byte;
@@ -552,20 +555,12 @@ static void pmecc_correct_data(struct mtd_info *mtd, uint8_t *buf, uint8_t *ecc,
if (byte_pos < sector_size) {
err_byte = *(buf + byte_pos);
*(buf + byte_pos) ^= (1 << bit_pos);
-
- pos = sector_num * host->board->pmecc_sector_size + byte_pos;
- dev_info(host->dev, "Bit flip in data area, byte_pos: %d, bit_pos: %d, 0x%02x -> 0x%02x\n",
- pos, bit_pos, err_byte, *(buf + byte_pos));
} else {
/* Bit flip in OOB area */
tmp = sector_num * host->pmecc_bytes_per_sector
+ (byte_pos - sector_size);
err_byte = ecc[tmp];
ecc[tmp] ^= (1 << bit_pos);
-
- pos = tmp + nand_chip->ecc.layout->eccpos[0];
- dev_info(host->dev, "Bit flip in OOB, oob_byte_pos: %d, bit_pos: %d, 0x%02x -> 0x%02x\n",
- pos, bit_pos, err_byte, ecc[tmp]);
}
i++;
@@ -575,17 +570,21 @@ static void pmecc_correct_data(struct mtd_info *mtd, uint8_t *buf, uint8_t *ecc,
return;
}
-static int pmecc_correction(struct mtd_info *mtd, u32 pmecc_stat, uint8_t *buf,
- u8 *ecc)
+static int pmecc_correction(struct mtd_info *mtd, u32 pmecc_stat, uint8_t *buf)
{
struct nand_chip *nand_chip = mtd_to_nand(mtd);
struct atmel_nand_host *host = nand_chip->priv;
- int i, err_nbr, eccbytes;
+ int i, err_nbr, ret;
uint8_t *buf_pos;
+ uint8_t *ecc_code = host->ecc_code;
- eccbytes = nand_chip->ecc.bytes;
- for (i = 0; i < eccbytes; i++)
- if (ecc[i] != 0xff)
+ ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code, nand_chip->oob_poi, 0,
+ nand_chip->ecc.total);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < nand_chip->ecc.bytes; i++)
+ if (ecc_code[i] != 0xff)
goto normal_check;
/* Erased page, return OK */
return 0;
@@ -606,7 +605,7 @@ normal_check:
mtd->ecc_stats.failed++;
return -EIO;
} else {
- pmecc_correct_data(mtd, buf_pos, ecc, i,
+ pmecc_correct_data(mtd, buf_pos, ecc_code, i,
host->pmecc_bytes_per_sector, err_nbr);
mtd->ecc_stats.corrected += err_nbr;
}
@@ -623,8 +622,6 @@ static int atmel_nand_pmecc_read_page(struct nand_chip *chip, uint8_t *buf,
struct mtd_info *mtd = nand_to_mtd(chip);
struct atmel_nand_host *host = chip->priv;
int eccsize = chip->ecc.size;
- uint8_t *oob = chip->oob_poi;
- uint32_t *eccpos = chip->ecc.layout->eccpos;
uint32_t stat;
int ret;
@@ -636,8 +633,10 @@ static int atmel_nand_pmecc_read_page(struct nand_chip *chip, uint8_t *buf,
pmecc_writel(host->ecc, CTRL, PMECC_CTRL_ENABLE);
pmecc_writel(host->ecc, CTRL, PMECC_CTRL_DATA);
+ nand_read_page_op(chip, page, 0, NULL, 0);
+
chip->legacy.read_buf(chip, buf, eccsize);
- chip->legacy.read_buf(chip, oob, mtd->oobsize);
+ chip->legacy.read_buf(chip, chip->oob_poi, mtd->oobsize);
ret = wait_on_timeout(PMECC_MAX_TIMEOUT_MS,
!(pmecc_readl_relaxed(host->ecc, SR) & PMECC_SR_BUSY));
@@ -648,20 +647,24 @@ static int atmel_nand_pmecc_read_page(struct nand_chip *chip, uint8_t *buf,
stat = pmecc_readl_relaxed(host->ecc, ISR);
if (stat != 0)
- if (pmecc_correction(mtd, stat, buf, &oob[eccpos[0]]) != 0)
+ if (pmecc_correction(mtd, stat, buf) != 0)
return -EIO;
return 0;
}
static int atmel_nand_pmecc_write_page(struct nand_chip *chip, const uint8_t *buf,
- int oob_required)
+ int oob_required, int page)
{
struct mtd_info *mtd = nand_to_mtd(chip);
struct atmel_nand_host *host = chip->priv;
- uint32_t *eccpos = chip->ecc.layout->eccpos;
+ uint8_t *ecc_calc = host->ecc_code;
int i, j, ret;
+ ret = nand_prog_page_begin_op(chip, page, 0, NULL, 0);
+ if (ret)
+ return ret;
+
pmecc_writel(host->ecc, CTRL, PMECC_CTRL_RST);
pmecc_writel(host->ecc, CTRL, PMECC_CTRL_DISABLE);
@@ -685,13 +688,22 @@ static int atmel_nand_pmecc_write_page(struct nand_chip *chip, const uint8_t *bu
int pos;
pos = i * host->pmecc_bytes_per_sector + j;
- chip->oob_poi[eccpos[pos]] =
- pmecc_readb_ecc_relaxed(host->ecc, i, j);
+ ecc_calc[pos] = pmecc_readb_ecc_relaxed(host->ecc, i, j);
}
}
+
+ ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc,
+ chip->oob_poi, 0, chip->ecc.total);
+ if (ret)
+ return ret;
+
chip->legacy.write_buf(chip, chip->oob_poi, mtd->oobsize);
- return 0;
+ ret = nand_write_data_op(chip, chip->oob_poi, mtd->oobsize, false);
+ if (ret)
+ return ret;
+
+ return nand_prog_page_end_op(chip);
}
static void atmel_pmecc_core_init(struct mtd_info *mtd)
@@ -746,7 +758,7 @@ static void atmel_pmecc_core_init(struct mtd_info *mtd)
| PMECC_CFG_AUTO_DISABLE);
pmecc_writel(host->ecc, CFG, val);
- ecc_layout = nand_chip->ecc.layout;
+ ecc_layout = host->ecclayout;
pmecc_writel(host->ecc, SAREA, mtd->oobsize - 1);
pmecc_writel(host->ecc, SADDR, ecc_layout->eccpos[0]);
pmecc_writel(host->ecc, EADDR,
@@ -853,7 +865,7 @@ static int __init atmel_pmecc_nand_init_params(struct device_d *dev,
{
struct resource *iores;
struct nand_chip *nand_chip = &host->nand_chip;
- struct mtd_info *mtd = &nand_chip->mtd;
+ struct mtd_info *mtd = nand_to_mtd(nand_chip);
int cap, sector_size, err_no;
int ret;
@@ -930,7 +942,8 @@ static int __init atmel_pmecc_nand_init_params(struct device_d *dev,
pmecc_config_ecc_layout(&atmel_pmecc_oobinfo,
mtd->oobsize,
nand_chip->ecc.bytes);
- nand_chip->ecc.layout = &atmel_pmecc_oobinfo;
+ host->ecclayout = &atmel_pmecc_oobinfo;
+ mtd_set_ecclayout(mtd, host->ecclayout);
break;
case 512:
case 1024:
@@ -1002,14 +1015,17 @@ static int atmel_nand_read_page(struct nand_chip *chip, uint8_t *buf,
int oob_required, int page)
{
struct mtd_info *mtd = nand_to_mtd(chip);
+ struct atmel_nand_host *host = chip->priv;
int eccsize = chip->ecc.size;
int eccbytes = chip->ecc.bytes;
- uint32_t *eccpos = chip->ecc.layout->eccpos;
+ uint32_t *eccpos = host->ecclayout->eccpos;
uint8_t *p = buf;
uint8_t *oob = chip->oob_poi;
uint8_t *ecc_pos;
int stat;
+ nand_read_page_op(chip, page, 0, NULL, 0);
+
/*
* Errata: ALE is incorrectly wired up to the ECC controller
* on the AP7000, so it will include the address cycles in the
@@ -1241,7 +1257,7 @@ static int atmel_hw_nand_init_params(struct device_d *dev,
{
struct resource *iores;
struct nand_chip *nand_chip = &host->nand_chip;
- struct mtd_info *mtd = &nand_chip->mtd;
+ struct mtd_info *mtd = nand_to_mtd(nand_chip);
iores = dev_request_mem_resource(dev, 1);
if (IS_ERR(iores))
@@ -1258,19 +1274,23 @@ static int atmel_hw_nand_init_params(struct device_d *dev,
/* set ECC page size and oob layout */
switch (mtd->writesize) {
case 512:
- nand_chip->ecc.layout = &atmel_oobinfo_small;
+ host->ecclayout = &atmel_oobinfo_small;
+ mtd_set_ecclayout(mtd, host->ecclayout);
ecc_writel(host->ecc, MR, ATMEL_ECC_PAGESIZE_528);
break;
case 1024:
- nand_chip->ecc.layout = &atmel_oobinfo_large;
+ host->ecclayout = &atmel_oobinfo_large;
+ mtd_set_ecclayout(mtd, host->ecclayout);
ecc_writel(host->ecc, MR, ATMEL_ECC_PAGESIZE_1056);
break;
case 2048:
- nand_chip->ecc.layout = &atmel_oobinfo_large;
+ host->ecclayout = &atmel_oobinfo_large;
+ mtd_set_ecclayout(mtd, host->ecclayout);
ecc_writel(host->ecc, MR, ATMEL_ECC_PAGESIZE_2112);
break;
case 4096:
- nand_chip->ecc.layout = &atmel_oobinfo_large;
+ host->ecclayout = &atmel_oobinfo_large;
+ mtd_set_ecclayout(mtd, host->ecclayout);
ecc_writel(host->ecc, MR, ATMEL_ECC_PAGESIZE_4224);
break;
default:
@@ -1318,7 +1338,7 @@ static int __init atmel_nand_probe(struct device_d *dev)
host->io_base = IOMEM(iores->start);
nand_chip = &host->nand_chip;
- mtd = &nand_chip->mtd;
+ mtd = nand_to_mtd(nand_chip);
host->board = pdata;
host->dev = dev;
@@ -1437,6 +1457,8 @@ static int __init atmel_nand_probe(struct device_d *dev)
goto err_scan_ident;
}
+ host->ecc_code = xmalloc(mtd->oobsize);
+
if (IS_ENABLED(CONFIG_NAND_ECC_HW) &&
nand_chip->ecc.mode == NAND_ECC_HW) {
if (IS_ENABLED(CONFIG_NAND_ATMEL_PMECC) && pdata->has_pmecc)
@@ -1454,12 +1476,11 @@ static int __init atmel_nand_probe(struct device_d *dev)
goto err_scan_tail;
}
- add_mtd_nand_device(nand_chip, "nand");
+ add_mtd_nand_device(mtd, "nand");
if (!res)
return res;
- nand_release(nand_chip);
err_scan_tail:
err_hw_ecc:
err_scan_ident:
diff --git a/drivers/mtd/nand/bbt.c b/drivers/mtd/nand/bbt.c
new file mode 100644
index 0000000000..172ab5ffbc
--- /dev/null
+++ b/drivers/mtd/nand/bbt.c
@@ -0,0 +1,132 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2017 Free Electrons
+ *
+ * Authors:
+ * Boris Brezillon <boris.brezillon@free-electrons.com>
+ * Peter Pan <peterpandong@micron.com>
+ */
+
+#define pr_fmt(fmt) "nand-bbt: " fmt
+
+#include <common.h>
+#include <linux/mtd/nand.h>
+#include <linux/slab.h>
+
+/**
+ * nanddev_bbt_init() - Initialize the BBT (Bad Block Table)
+ * @nand: NAND device
+ *
+ * Initialize the in-memory BBT.
+ *
+ * Return: 0 in case of success, a negative error code otherwise.
+ */
+int nanddev_bbt_init(struct nand_device *nand)
+{
+ unsigned int bits_per_block = fls(NAND_BBT_BLOCK_NUM_STATUS);
+ unsigned int nblocks = nanddev_neraseblocks(nand);
+ unsigned int nwords = DIV_ROUND_UP(nblocks * bits_per_block,
+ BITS_PER_LONG);
+
+ nand->bbt.cache = kcalloc(nwords, sizeof(*nand->bbt.cache),
+ GFP_KERNEL);
+ if (!nand->bbt.cache)
+ return -ENOMEM;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(nanddev_bbt_init);
+
+/**
+ * nanddev_bbt_cleanup() - Cleanup the BBT (Bad Block Table)
+ * @nand: NAND device
+ *
+ * Undoes what has been done in nanddev_bbt_init()
+ */
+void nanddev_bbt_cleanup(struct nand_device *nand)
+{
+ kfree(nand->bbt.cache);
+}
+EXPORT_SYMBOL_GPL(nanddev_bbt_cleanup);
+
+/**
+ * nanddev_bbt_update() - Update a BBT
+ * @nand: nand device
+ *
+ * Update the BBT. Currently a NOP function since on-flash bbt is not yet
+ * supported.
+ *
+ * Return: 0 in case of success, a negative error code otherwise.
+ */
+int nanddev_bbt_update(struct nand_device *nand)
+{
+ return 0;
+}
+EXPORT_SYMBOL_GPL(nanddev_bbt_update);
+
+/**
+ * nanddev_bbt_get_block_status() - Return the status of an eraseblock
+ * @nand: nand device
+ * @entry: the BBT entry
+ *
+ * Return: a positive number nand_bbt_block_status status or -%ERANGE if @entry
+ * is bigger than the BBT size.
+ */
+int nanddev_bbt_get_block_status(const struct nand_device *nand,
+ unsigned int entry)
+{
+ unsigned int bits_per_block = fls(NAND_BBT_BLOCK_NUM_STATUS);
+ unsigned long *pos = nand->bbt.cache +
+ ((entry * bits_per_block) / BITS_PER_LONG);
+ unsigned int offs = (entry * bits_per_block) % BITS_PER_LONG;
+ unsigned long status;
+
+ if (entry >= nanddev_neraseblocks(nand))
+ return -ERANGE;
+
+ status = pos[0] >> offs;
+ if (bits_per_block + offs > BITS_PER_LONG)
+ status |= pos[1] << (BITS_PER_LONG - offs);
+
+ return status & GENMASK(bits_per_block - 1, 0);
+}
+EXPORT_SYMBOL_GPL(nanddev_bbt_get_block_status);
+
+/**
+ * nanddev_bbt_set_block_status() - Update the status of an eraseblock in the
+ * in-memory BBT
+ * @nand: nand device
+ * @entry: the BBT entry to update
+ * @status: the new status
+ *
+ * Update an entry of the in-memory BBT. If you want to push the updated BBT
+ * the NAND you should call nanddev_bbt_update().
+ *
+ * Return: 0 in case of success or -%ERANGE if @entry is bigger than the BBT
+ * size.
+ */
+int nanddev_bbt_set_block_status(struct nand_device *nand, unsigned int entry,
+ enum nand_bbt_block_status status)
+{
+ unsigned int bits_per_block = fls(NAND_BBT_BLOCK_NUM_STATUS);
+ unsigned long *pos = nand->bbt.cache +
+ ((entry * bits_per_block) / BITS_PER_LONG);
+ unsigned int offs = (entry * bits_per_block) % BITS_PER_LONG;
+ unsigned long val = status & GENMASK(bits_per_block - 1, 0);
+
+ if (entry >= nanddev_neraseblocks(nand))
+ return -ERANGE;
+
+ pos[0] &= ~GENMASK(offs + bits_per_block - 1, offs);
+ pos[0] |= val << offs;
+
+ if (bits_per_block + offs > BITS_PER_LONG) {
+ unsigned int rbits = bits_per_block + offs - BITS_PER_LONG;
+
+ pos[1] &= ~GENMASK(rbits - 1, 0);
+ pos[1] |= val >> rbits;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(nanddev_bbt_set_block_status);
diff --git a/drivers/mtd/nand/core.c b/drivers/mtd/nand/core.c
new file mode 100644
index 0000000000..ba22662e2f
--- /dev/null
+++ b/drivers/mtd/nand/core.c
@@ -0,0 +1,275 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2017 Free Electrons
+ *
+ * Authors:
+ * Boris Brezillon <boris.brezillon@free-electrons.com>
+ * Peter Pan <peterpandong@micron.com>
+ */
+
+#define pr_fmt(fmt) "nand: " fmt
+
+#include <common.h>
+#include <linux/mtd/nand.h>
+
+/**
+ * nanddev_isbad() - Check if a block is bad
+ * @nand: NAND device
+ * @pos: position pointing to the block we want to check
+ *
+ * Return: true if the block is bad, false otherwise.
+ */
+bool nanddev_isbad(struct nand_device *nand, const struct nand_pos *pos)
+{
+ if (nanddev_bbt_is_initialized(nand)) {
+ unsigned int entry;
+ int status;
+
+ entry = nanddev_bbt_pos_to_entry(nand, pos);
+ status = nanddev_bbt_get_block_status(nand, entry);
+ /* Lazy block status retrieval */
+ if (status == NAND_BBT_BLOCK_STATUS_UNKNOWN) {
+ if (nand->ops->isbad(nand, pos))
+ status = NAND_BBT_BLOCK_FACTORY_BAD;
+ else
+ status = NAND_BBT_BLOCK_GOOD;
+
+ nanddev_bbt_set_block_status(nand, entry, status);
+ }
+
+ if (status == NAND_BBT_BLOCK_WORN ||
+ status == NAND_BBT_BLOCK_FACTORY_BAD)
+ return true;
+
+ return false;
+ }
+
+ return nand->ops->isbad(nand, pos);
+}
+EXPORT_SYMBOL_GPL(nanddev_isbad);
+
+/**
+ * nanddev_markbad() - Mark a block as bad
+ * @nand: NAND device
+ * @pos: position of the block to mark bad
+ *
+ * Mark a block bad. This function is updating the BBT if available and
+ * calls the low-level markbad hook (nand->ops->markbad()).
+ *
+ * Return: 0 in case of success, a negative error code otherwise.
+ */
+int nanddev_markbad(struct nand_device *nand, const struct nand_pos *pos)
+{
+ struct mtd_info *mtd = nanddev_to_mtd(nand);
+ unsigned int entry;
+ int ret = 0;
+
+ if (nanddev_isbad(nand, pos))
+ return 0;
+
+ ret = nand->ops->markbad(nand, pos);
+ if (ret)
+ pr_warn("failed to write BBM to block @%llx (err = %d)\n",
+ nanddev_pos_to_offs(nand, pos), ret);
+
+ if (!nanddev_bbt_is_initialized(nand))
+ goto out;
+
+ entry = nanddev_bbt_pos_to_entry(nand, pos);
+ ret = nanddev_bbt_set_block_status(nand, entry, NAND_BBT_BLOCK_WORN);
+ if (ret)
+ goto out;
+
+ ret = nanddev_bbt_update(nand);
+
+out:
+ if (!ret)
+ mtd->ecc_stats.badblocks++;
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(nanddev_markbad);
+
+/**
+ * nanddev_isreserved() - Check whether an eraseblock is reserved or not
+ * @nand: NAND device
+ * @pos: NAND position to test
+ *
+ * Checks whether the eraseblock pointed by @pos is reserved or not.
+ *
+ * Return: true if the eraseblock is reserved, false otherwise.
+ */
+bool nanddev_isreserved(struct nand_device *nand, const struct nand_pos *pos)
+{
+ unsigned int entry;
+ int status;
+
+ if (!nanddev_bbt_is_initialized(nand))
+ return false;
+
+ /* Return info from the table */
+ entry = nanddev_bbt_pos_to_entry(nand, pos);
+ status = nanddev_bbt_get_block_status(nand, entry);
+ return status == NAND_BBT_BLOCK_RESERVED;
+}
+EXPORT_SYMBOL_GPL(nanddev_isreserved);
+
+/**
+ * nanddev_erase() - Erase a NAND portion
+ * @nand: NAND device
+ * @pos: position of the block to erase
+ *
+ * Erases the block if it's not bad.
+ *
+ * Return: 0 in case of success, a negative error code otherwise.
+ */
+int nanddev_erase(struct nand_device *nand, const struct nand_pos *pos)
+{
+ if (nanddev_isbad(nand, pos) || nanddev_isreserved(nand, pos)) {
+ pr_warn("attempt to erase a bad/reserved block @%llx\n",
+ nanddev_pos_to_offs(nand, pos));
+ return -EIO;
+ }
+
+ return nand->ops->erase(nand, pos);
+}
+EXPORT_SYMBOL_GPL(nanddev_erase);
+
+/**
+ * nanddev_mtd_erase() - Generic mtd->_erase() implementation for NAND devices
+ * @mtd: MTD device
+ * @einfo: erase request
+ *
+ * This is a simple mtd->_erase() implementation iterating over all blocks
+ * concerned by @einfo and calling nand->ops->erase() on each of them.
+ *
+ * Note that mtd->_erase should not be directly assigned to this helper,
+ * because there's no locking here. NAND specialized layers should instead
+ * implement there own wrapper around nanddev_mtd_erase() taking the
+ * appropriate lock before calling nanddev_mtd_erase().
+ *
+ * Return: 0 in case of success, a negative error code otherwise.
+ */
+int nanddev_mtd_erase(struct mtd_info *mtd, struct erase_info *einfo)
+{
+ struct nand_device *nand = mtd_to_nanddev(mtd);
+ struct nand_pos pos, last;
+ int ret;
+
+ nanddev_offs_to_pos(nand, einfo->addr, &pos);
+ nanddev_offs_to_pos(nand, einfo->addr + einfo->len - 1, &last);
+ while (nanddev_pos_cmp(&pos, &last) <= 0) {
+ ret = nanddev_erase(nand, &pos);
+ if (ret) {
+ einfo->fail_addr = nanddev_pos_to_offs(nand, &pos);
+
+ return ret;
+ }
+
+ nanddev_pos_next_eraseblock(nand, &pos);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(nanddev_mtd_erase);
+
+/**
+ * nanddev_mtd_max_bad_blocks() - Get the maximum number of bad eraseblock on
+ * a specific region of the NAND device
+ * @mtd: MTD device
+ * @offs: offset of the NAND region
+ * @len: length of the NAND region
+ *
+ * Default implementation for mtd->_max_bad_blocks(). Only works if
+ * nand->memorg.max_bad_eraseblocks_per_lun is > 0.
+ *
+ * Return: a positive number encoding the maximum number of eraseblocks on a
+ * portion of memory, a negative error code otherwise.
+ */
+int nanddev_mtd_max_bad_blocks(struct mtd_info *mtd, loff_t offs, size_t len)
+{
+ struct nand_device *nand = mtd_to_nanddev(mtd);
+ struct nand_pos pos, end;
+ unsigned int max_bb = 0;
+
+ if (!nand->memorg.max_bad_eraseblocks_per_lun)
+ return -ENOTSUPP;
+
+ nanddev_offs_to_pos(nand, offs, &pos);
+ nanddev_offs_to_pos(nand, offs + len, &end);
+
+ for (nanddev_offs_to_pos(nand, offs, &pos);
+ nanddev_pos_cmp(&pos, &end) < 0;
+ nanddev_pos_next_lun(nand, &pos))
+ max_bb += nand->memorg.max_bad_eraseblocks_per_lun;
+
+ return max_bb;
+}
+EXPORT_SYMBOL_GPL(nanddev_mtd_max_bad_blocks);
+
+/**
+ * nanddev_init() - Initialize a NAND device
+ * @nand: NAND device
+ * @ops: NAND device operations
+ * @owner: NAND device owner
+ *
+ * Initializes a NAND device object. Consistency checks are done on @ops and
+ * @nand->memorg. Also takes care of initializing the BBT.
+ *
+ * Return: 0 in case of success, a negative error code otherwise.
+ */
+int nanddev_init(struct nand_device *nand, const struct nand_ops *ops,
+ struct module *owner)
+{
+ struct mtd_info *mtd = nanddev_to_mtd(nand);
+ struct nand_memory_organization *memorg = nanddev_get_memorg(nand);
+
+ if (!nand || !ops)
+ return -EINVAL;
+
+ if (!ops->erase || !ops->markbad || !ops->isbad)
+ return -EINVAL;
+
+ if (!memorg->bits_per_cell || !memorg->pagesize ||
+ !memorg->pages_per_eraseblock || !memorg->eraseblocks_per_lun ||
+ !memorg->planes_per_lun || !memorg->luns_per_target ||
+ !memorg->ntargets)
+ return -EINVAL;
+
+ nand->rowconv.eraseblock_addr_shift =
+ fls(memorg->pages_per_eraseblock - 1);
+ nand->rowconv.lun_addr_shift = fls(memorg->eraseblocks_per_lun - 1) +
+ nand->rowconv.eraseblock_addr_shift;
+
+ nand->ops = ops;
+
+ mtd->type = memorg->bits_per_cell == 1 ?
+ MTD_NANDFLASH : MTD_MLCNANDFLASH;
+ mtd->flags = MTD_CAP_NANDFLASH;
+ mtd->erasesize = memorg->pagesize * memorg->pages_per_eraseblock;
+ mtd->writesize = memorg->pagesize;
+ mtd->writebufsize = memorg->pagesize;
+ mtd->oobsize = memorg->oobsize;
+ mtd->size = nanddev_size(nand);
+ mtd->owner = owner;
+
+ return nanddev_bbt_init(nand);
+}
+EXPORT_SYMBOL_GPL(nanddev_init);
+
+/**
+ * nanddev_cleanup() - Release resources allocated in nanddev_init()
+ * @nand: NAND device
+ *
+ * Basically undoes what has been done in nanddev_init().
+ */
+void nanddev_cleanup(struct nand_device *nand)
+{
+ if (nanddev_bbt_is_initialized(nand))
+ nanddev_bbt_cleanup(nand);
+}
+EXPORT_SYMBOL_GPL(nanddev_cleanup);
+
+MODULE_DESCRIPTION("Generic NAND framework");
+MODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mtd/nand/denali.h b/drivers/mtd/nand/denali.h
index 7698b59720..699e6ec6b4 100644
--- a/drivers/mtd/nand/denali.h
+++ b/drivers/mtd/nand/denali.h
@@ -21,6 +21,7 @@
#define __DENALI_H__
#include <linux/mtd/nand.h>
+#include <linux/mtd/rawnand.h>
#include <linux/spinlock.h>
#define DEVICE_RESET 0x0
diff --git a/drivers/mtd/nand/internals.h b/drivers/mtd/nand/internals.h
new file mode 100644
index 0000000000..7716470a56
--- /dev/null
+++ b/drivers/mtd/nand/internals.h
@@ -0,0 +1,170 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2018 - Bootlin
+ *
+ * Author: Boris Brezillon <boris.brezillon@bootlin.com>
+ *
+ * Header containing internal definitions to be used only by core files.
+ * NAND controller drivers should not include this file.
+ */
+
+#ifndef __LINUX_RAWNAND_INTERNALS
+#define __LINUX_RAWNAND_INTERNALS
+
+#include <linux/mtd/rawnand.h>
+
+/*
+ * NAND Flash Manufacturer ID Codes
+ */
+#define NAND_MFR_AMD 0x01
+#define NAND_MFR_ATO 0x9b
+#define NAND_MFR_EON 0x92
+#define NAND_MFR_ESMT 0xc8
+#define NAND_MFR_FUJITSU 0x04
+#define NAND_MFR_HYNIX 0xad
+#define NAND_MFR_INTEL 0x89
+#define NAND_MFR_MACRONIX 0xc2
+#define NAND_MFR_MICRON 0x2c
+#define NAND_MFR_NATIONAL 0x8f
+#define NAND_MFR_RENESAS 0x07
+#define NAND_MFR_SAMSUNG 0xec
+#define NAND_MFR_SANDISK 0x45
+#define NAND_MFR_STMICRO 0x20
+/* Kioxia is new name of Toshiba memory. */
+#define NAND_MFR_TOSHIBA 0x98
+#define NAND_MFR_WINBOND 0xef
+
+/**
+ * struct nand_manufacturer_ops - NAND Manufacturer operations
+ * @detect: detect the NAND memory organization and capabilities
+ * @init: initialize all vendor specific fields (like the ->read_retry()
+ * implementation) if any.
+ * @cleanup: the ->init() function may have allocated resources, ->cleanup()
+ * is here to let vendor specific code release those resources.
+ * @fixup_onfi_param_page: apply vendor specific fixups to the ONFI parameter
+ * page. This is called after the checksum is verified.
+ */
+struct nand_manufacturer_ops {
+ void (*detect)(struct nand_chip *chip);
+ int (*init)(struct nand_chip *chip);
+ void (*cleanup)(struct nand_chip *chip);
+ void (*fixup_onfi_param_page)(struct nand_chip *chip,
+ struct nand_onfi_params *p);
+};
+
+/**
+ * struct nand_manufacturer_desc - NAND Flash Manufacturer descriptor
+ * @name: Manufacturer name
+ * @id: manufacturer ID code of device.
+ * @ops: manufacturer operations
+ */
+struct nand_manufacturer_desc {
+ int id;
+ char *name;
+ const struct nand_manufacturer_ops *ops;
+};
+
+
+extern struct nand_flash_dev nand_flash_ids[];
+
+extern const struct nand_manufacturer_ops amd_nand_manuf_ops;
+extern const struct nand_manufacturer_ops esmt_nand_manuf_ops;
+extern const struct nand_manufacturer_ops hynix_nand_manuf_ops;
+extern const struct nand_manufacturer_ops macronix_nand_manuf_ops;
+extern const struct nand_manufacturer_ops micron_nand_manuf_ops;
+extern const struct nand_manufacturer_ops samsung_nand_manuf_ops;
+extern const struct nand_manufacturer_ops toshiba_nand_manuf_ops;
+
+/* MLC pairing schemes */
+extern const struct mtd_pairing_scheme dist3_pairing_scheme;
+
+/* Core functions */
+const struct nand_manufacturer_desc *nand_get_manufacturer_desc(u8 id);
+int nand_bbm_get_next_page(struct nand_chip *chip, int page);
+int nand_markbad_bbm(struct nand_chip *chip, loff_t ofs);
+int nand_erase_nand(struct nand_chip *chip, struct erase_info *instr,
+ int allowbbt);
+void onfi_fill_interface_config(struct nand_chip *chip,
+ struct nand_interface_config *iface,
+ enum nand_interface_type type,
+ unsigned int timing_mode);
+unsigned int
+onfi_find_closest_sdr_mode(const struct nand_sdr_timings *spec_timings);
+int nand_choose_best_sdr_timings(struct nand_chip *chip,
+ struct nand_interface_config *iface,
+ struct nand_sdr_timings *spec_timings);
+const struct nand_interface_config *nand_get_reset_interface_config(void);
+int nand_get_features(struct nand_chip *chip, int addr, u8 *subfeature_param);
+int nand_set_features(struct nand_chip *chip, int addr, u8 *subfeature_param);
+int nand_read_page_raw_notsupp(struct nand_chip *chip, u8 *buf,
+ int oob_required, int page);
+int nand_write_page_raw_notsupp(struct nand_chip *chip, const u8 *buf,
+ int oob_required, int page);
+int nand_exit_status_op(struct nand_chip *chip);
+int nand_read_param_page_op(struct nand_chip *chip, u8 page, void *buf,
+ unsigned int len);
+void nand_decode_ext_id(struct nand_chip *chip);
+void panic_nand_wait(struct nand_chip *chip, unsigned long timeo);
+void sanitize_string(uint8_t *s, size_t len);
+
+static inline bool nand_has_exec_op(struct nand_chip *chip)
+{
+ if (!chip->controller || !chip->controller->ops ||
+ !chip->controller->ops->exec_op)
+ return false;
+
+ return true;
+}
+
+static inline int nand_check_op(struct nand_chip *chip,
+ const struct nand_operation *op)
+{
+ if (!nand_has_exec_op(chip))
+ return 0;
+
+ return chip->controller->ops->exec_op(chip, op, true);
+}
+
+static inline int nand_exec_op(struct nand_chip *chip,
+ const struct nand_operation *op)
+{
+ if (!nand_has_exec_op(chip))
+ return -ENOTSUPP;
+
+ if (WARN_ON(op->cs >= nanddev_ntargets(&chip->base)))
+ return -EINVAL;
+
+ return chip->controller->ops->exec_op(chip, op, false);
+}
+
+static inline bool nand_controller_can_setup_interface(struct nand_chip *chip)
+{
+ if (!chip->controller || !chip->controller->ops ||
+ !chip->controller->ops->setup_interface)
+ return false;
+
+ if (chip->options & NAND_KEEP_TIMINGS)
+ return false;
+
+ return true;
+}
+
+/* BBT functions */
+int nand_markbad_bbt(struct nand_chip *chip, loff_t offs);
+int nand_markgood_bbt(struct nand_chip *chip, loff_t offs);
+int nand_isreserved_bbt(struct nand_chip *chip, loff_t offs);
+int nand_isbad_bbt(struct nand_chip *chip, loff_t offs, int allowbbt);
+
+/* Legacy */
+void nand_legacy_set_defaults(struct nand_chip *chip);
+void nand_legacy_adjust_cmdfunc(struct nand_chip *chip);
+int nand_legacy_check_hooks(struct nand_chip *chip);
+
+/* ONFI functions */
+u16 onfi_crc16(u16 crc, u8 const *p, size_t len);
+int nand_onfi_detect(struct nand_chip *chip);
+
+/* JEDEC functions */
+int nand_jedec_detect(struct nand_chip *chip);
+
+#endif /* __LINUX_RAWNAND_INTERNALS */
diff --git a/drivers/mtd/nand/nand_amd.c b/drivers/mtd/nand/nand_amd.c
new file mode 100644
index 0000000000..c3d4dae3cd
--- /dev/null
+++ b/drivers/mtd/nand/nand_amd.c
@@ -0,0 +1,53 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2017 Free Electrons
+ * Copyright (C) 2017 NextThing Co
+ *
+ * Author: Boris Brezillon <boris.brezillon@free-electrons.com>
+ */
+
+#include "internals.h"
+
+static void amd_nand_decode_id(struct nand_chip *chip)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ struct nand_memory_organization *memorg;
+
+ memorg = nanddev_get_memorg(&chip->base);
+
+ nand_decode_ext_id(chip);
+
+ /*
+ * Check for Spansion/AMD ID + repeating 5th, 6th byte since
+ * some Spansion chips have erasesize that conflicts with size
+ * listed in nand_ids table.
+ * Data sheet (5 byte ID): Spansion S30ML-P ORNAND (p.39)
+ */
+ if (chip->id.data[4] != 0x00 && chip->id.data[5] == 0x00 &&
+ chip->id.data[6] == 0x00 && chip->id.data[7] == 0x00 &&
+ memorg->pagesize == 512) {
+ memorg->pages_per_eraseblock = 256;
+ memorg->pages_per_eraseblock <<= ((chip->id.data[3] & 0x03) << 1);
+ mtd->erasesize = memorg->pages_per_eraseblock *
+ memorg->pagesize;
+ }
+}
+
+static int amd_nand_init(struct nand_chip *chip)
+{
+ if (nand_is_slc(chip))
+ /*
+ * According to the datasheet of some Cypress SLC NANDs,
+ * the bad block markers can be in the first, second or last
+ * page of a block. So let's check all three locations.
+ */
+ chip->options |= NAND_BBM_FIRSTPAGE | NAND_BBM_SECONDPAGE |
+ NAND_BBM_LASTPAGE;
+
+ return 0;
+}
+
+const struct nand_manufacturer_ops amd_nand_manuf_ops = {
+ .detect = amd_nand_decode_id,
+ .init = amd_nand_init,
+};
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index 1816822e78..e9c3d7e7c8 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -1,6 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
- * drivers/mtd/nand.c
- *
* Overview:
* This is the generic MTD driver for NAND flash devices. It should be
* capable of working with almost all NAND chips currently available.
@@ -22,14 +21,9 @@
* Check, if mtd->ecctype should be set to MTD_ECC_HW
* if we have HW ECC support.
* BBT table is not serialized, has to be fixed
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
*/
-#define pr_fmt(fmt) "nand: " fmt
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <common.h>
#include <errno.h>
@@ -45,68 +39,180 @@
#include <module.h>
#include <of_mtd.h>
+#include "internals.h"
+
/* Define default oob placement schemes for large and small page devices */
-static struct nand_ecclayout nand_oob_8 = {
- .eccbytes = 3,
- .eccpos = {0, 1, 2},
- .oobfree = {
- {.offset = 3,
- .length = 2},
- {.offset = 6,
- .length = 2} }
-};
+static int nand_ooblayout_ecc_sp(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct nand_ecc_ctrl *ecc = &chip->ecc;
-static struct nand_ecclayout nand_oob_16 = {
- .eccbytes = 6,
- .eccpos = {0, 1, 2, 3, 6, 7},
- .oobfree = {
- {.offset = 8,
- . length = 8} }
-};
+ if (section > 1)
+ return -ERANGE;
+
+ if (!section) {
+ oobregion->offset = 0;
+ if (mtd->oobsize == 16)
+ oobregion->length = 4;
+ else
+ oobregion->length = 3;
+ } else {
+ if (mtd->oobsize == 8)
+ return -ERANGE;
+
+ oobregion->offset = 6;
+ oobregion->length = ecc->total - 4;
+ }
+
+ return 0;
+}
-static struct nand_ecclayout nand_oob_64 = {
- .eccbytes = 24,
- .eccpos = {
- 40, 41, 42, 43, 44, 45, 46, 47,
- 48, 49, 50, 51, 52, 53, 54, 55,
- 56, 57, 58, 59, 60, 61, 62, 63},
- .oobfree = {
- {.offset = 2,
- .length = 38} }
+static int nand_ooblayout_free_sp(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ if (section > 1)
+ return -ERANGE;
+
+ if (mtd->oobsize == 16) {
+ if (section)
+ return -ERANGE;
+
+ oobregion->length = 8;
+ oobregion->offset = 8;
+ } else {
+ oobregion->length = 2;
+ if (!section)
+ oobregion->offset = 3;
+ else
+ oobregion->offset = 6;
+ }
+
+ return 0;
+}
+
+const struct mtd_ooblayout_ops nand_ooblayout_sp_ops = {
+ .ecc = nand_ooblayout_ecc_sp,
+ .free = nand_ooblayout_free_sp,
};
+EXPORT_SYMBOL_GPL(nand_ooblayout_sp_ops);
+
+static int nand_ooblayout_ecc_lp(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct nand_ecc_ctrl *ecc = &chip->ecc;
+
+ if (section || !ecc->total)
+ return -ERANGE;
+
+ oobregion->length = ecc->total;
+ oobregion->offset = mtd->oobsize - oobregion->length;
+
+ return 0;
+}
+
+static int nand_ooblayout_free_lp(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct nand_ecc_ctrl *ecc = &chip->ecc;
+
+ if (section)
+ return -ERANGE;
+
+ oobregion->length = mtd->oobsize - ecc->total - 2;
+ oobregion->offset = 2;
+
+ return 0;
+}
-static struct nand_ecclayout nand_oob_128 = {
- .eccbytes = 48,
- .eccpos = {
- 80, 81, 82, 83, 84, 85, 86, 87,
- 88, 89, 90, 91, 92, 93, 94, 95,
- 96, 97, 98, 99, 100, 101, 102, 103,
- 104, 105, 106, 107, 108, 109, 110, 111,
- 112, 113, 114, 115, 116, 117, 118, 119,
- 120, 121, 122, 123, 124, 125, 126, 127},
- .oobfree = {
- {.offset = 2,
- .length = 78} }
+const struct mtd_ooblayout_ops nand_ooblayout_lp_ops = {
+ .ecc = nand_ooblayout_ecc_lp,
+ .free = nand_ooblayout_free_lp,
};
+EXPORT_SYMBOL_GPL(nand_ooblayout_lp_ops);
+
+/*
+ * Support the old "large page" layout used for 1-bit Hamming ECC where ECC
+ * are placed at a fixed offset.
+ */
+static int nand_ooblayout_ecc_lp_hamming(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct nand_ecc_ctrl *ecc = &chip->ecc;
-static int nand_get_device(struct nand_chip *chip, int new_state);
+ if (section)
+ return -ERANGE;
-static int nand_do_write_oob(struct nand_chip *chip, loff_t to,
- struct mtd_oob_ops *ops);
+ switch (mtd->oobsize) {
+ case 64:
+ oobregion->offset = 40;
+ break;
+ case 128:
+ oobregion->offset = 80;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ oobregion->length = ecc->total;
+ if (oobregion->offset + oobregion->length > mtd->oobsize)
+ return -ERANGE;
+
+ return 0;
+}
+
+static int nand_ooblayout_free_lp_hamming(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct nand_ecc_ctrl *ecc = &chip->ecc;
+ int ecc_offset = 0;
+
+ if (section < 0 || section > 1)
+ return -ERANGE;
+
+ switch (mtd->oobsize) {
+ case 64:
+ ecc_offset = 40;
+ break;
+ case 128:
+ ecc_offset = 80;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (section == 0) {
+ oobregion->offset = 2;
+ oobregion->length = ecc_offset - 2;
+ } else {
+ oobregion->offset = ecc_offset + ecc->total;
+ oobregion->length = mtd->oobsize - oobregion->offset;
+ }
+
+ return 0;
+}
+
+static const struct mtd_ooblayout_ops nand_ooblayout_lp_hamming_ops = {
+ .ecc = nand_ooblayout_ecc_lp_hamming,
+ .free = nand_ooblayout_free_lp_hamming,
+};
-static int check_offs_len(struct nand_chip *chip,
- loff_t ofs, uint64_t len)
+static int check_offs_len(struct nand_chip *chip, loff_t ofs, uint64_t len)
{
int ret = 0;
/* Start address must align on block boundary */
- if (ofs & ((1 << chip->phys_erase_shift) - 1)) {
+ if (ofs & ((1ULL << chip->phys_erase_shift) - 1)) {
pr_debug("%s: unaligned address\n", __func__);
ret = -EINVAL;
}
/* Length must align on block boundary */
- if (len & ((1 << chip->phys_erase_shift) - 1)) {
+ if (len & ((1ULL << chip->phys_erase_shift) - 1)) {
pr_debug("%s: length not block aligned\n", __func__);
ret = -EINVAL;
}
@@ -115,220 +221,348 @@ static int check_offs_len(struct nand_chip *chip,
}
/**
- * nand_release_device - [GENERIC] release chip
- * @mtd: MTD device structure
+ * nand_extract_bits - Copy unaligned bits from one buffer to another one
+ * @dst: destination buffer
+ * @dst_off: bit offset at which the writing starts
+ * @src: source buffer
+ * @src_off: bit offset at which the reading starts
+ * @nbits: number of bits to copy from @src to @dst
*
- * Release chip lock and wake up anyone waiting on the device.
+ * Copy bits from one memory region to another (overlap authorized).
*/
-static void nand_release_device(struct nand_chip *chip)
+void nand_extract_bits(u8 *dst, unsigned int dst_off, const u8 *src,
+ unsigned int src_off, unsigned int nbits)
{
- /* Release the controller and the chip */
- chip->controller->active = NULL;
- chip->state = FL_READY;
+ unsigned int tmp, n;
+
+ dst += dst_off / 8;
+ dst_off %= 8;
+ src += src_off / 8;
+ src_off %= 8;
+
+ while (nbits) {
+ n = min3(8 - dst_off, 8 - src_off, nbits);
+
+ tmp = (*src >> src_off) & GENMASK(n - 1, 0);
+ *dst &= ~GENMASK(n - 1 + dst_off, dst_off);
+ *dst |= tmp << dst_off;
+
+ dst_off += n;
+ if (dst_off >= 8) {
+ dst++;
+ dst_off -= 8;
+ }
+
+ src_off += n;
+ if (src_off >= 8) {
+ src++;
+ src_off -= 8;
+ }
+
+ nbits -= n;
+ }
}
+EXPORT_SYMBOL_GPL(nand_extract_bits);
/**
- * nand_read_byte - [DEFAULT] read one byte from the chip
- * @mtd: MTD device structure
+ * nand_select_target() - Select a NAND target (A.K.A. die)
+ * @chip: NAND chip object
+ * @cs: the CS line to select. Note that this CS id is always from the chip
+ * PoV, not the controller one
*
- * Default read function for 8bit buswidth
+ * Select a NAND target so that further operations executed on @chip go to the
+ * selected NAND target.
*/
-static uint8_t nand_read_byte(struct nand_chip *chip)
+void nand_select_target(struct nand_chip *chip, unsigned int cs)
{
- return readb(chip->legacy.IO_ADDR_R);
+ /*
+ * cs should always lie between 0 and nanddev_ntargets(), when that's
+ * not the case it's a bug and the caller should be fixed.
+ */
+ if (WARN_ON(cs > nanddev_ntargets(&chip->base)))
+ return;
+
+ chip->cur_cs = cs;
+
+ if (chip->legacy.select_chip)
+ chip->legacy.select_chip(chip, cs);
}
+EXPORT_SYMBOL_GPL(nand_select_target);
/**
- * nand_read_byte16 - [DEFAULT] read one byte endianness aware from the chip
- * nand_read_byte16 - [DEFAULT] read one byte endianness aware from the chip
- * @mtd: MTD device structure
- *
- * Default read function for 16bit buswidth with endianness conversion.
+ * nand_deselect_target() - Deselect the currently selected target
+ * @chip: NAND chip object
*
+ * Deselect the currently selected NAND target. The result of operations
+ * executed on @chip after the target has been deselected is undefined.
*/
-static uint8_t nand_read_byte16(struct nand_chip *chip)
+void nand_deselect_target(struct nand_chip *chip)
{
- return (uint8_t) cpu_to_le16(readw(chip->legacy.IO_ADDR_R));
+ if (chip->legacy.select_chip)
+ chip->legacy.select_chip(chip, -1);
+
+ chip->cur_cs = -1;
}
+EXPORT_SYMBOL_GPL(nand_deselect_target);
/**
- * nand_read_word - [DEFAULT] read one word from the chip
- * @mtd: MTD device structure
+ * nand_release_device - [GENERIC] release chip
+ * @chip: NAND chip object
*
- * Default read function for 16bit buswidth without endianness conversion.
+ * Release chip lock and wake up anyone waiting on the device.
*/
-static u16 nand_read_word(struct nand_chip *chip)
+static void nand_release_device(struct nand_chip *chip)
{
- return readw(chip->legacy.IO_ADDR_R);
+ /* Release the controller and the chip */
+ mutex_unlock(&chip->controller->lock);
+ mutex_unlock(&chip->lock);
}
/**
- * nand_select_chip - [DEFAULT] control CE line
- * @mtd: MTD device structure
- * @chipnr: chipnumber to select, -1 for deselect
+ * nand_bbm_get_next_page - Get the next page for bad block markers
+ * @chip: NAND chip object
+ * @page: First page to start checking for bad block marker usage
*
- * Default select function for 1 chip devices.
+ * Returns an integer that corresponds to the page offset within a block, for
+ * a page that is used to store bad block markers. If no more pages are
+ * available, -EINVAL is returned.
*/
-static void nand_select_chip(struct nand_chip *chip, int chipnr)
+int nand_bbm_get_next_page(struct nand_chip *chip, int page)
{
- switch (chipnr) {
- case -1:
- chip->legacy.cmd_ctrl(chip, NAND_CMD_NONE, 0 | NAND_CTRL_CHANGE);
- break;
- case 0:
- break;
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ int last_page = ((mtd->erasesize - mtd->writesize) >>
+ chip->page_shift) & chip->pagemask;
+ unsigned int bbm_flags = NAND_BBM_FIRSTPAGE | NAND_BBM_SECONDPAGE
+ | NAND_BBM_LASTPAGE;
- default:
- BUG();
- }
+ if (page == 0 && !(chip->options & bbm_flags))
+ return 0;
+ if (page == 0 && chip->options & NAND_BBM_FIRSTPAGE)
+ return 0;
+ if (page <= 1 && chip->options & NAND_BBM_SECONDPAGE)
+ return 1;
+ if (page <= last_page && chip->options & NAND_BBM_LASTPAGE)
+ return last_page;
+
+ return -EINVAL;
}
/**
- * nand_write_buf - [DEFAULT] write buffer to chip
- * @mtd: MTD device structure
- * @buf: data buffer
- * @len: number of bytes to write
+ * nand_block_bad - [DEFAULT] Read bad block marker from the chip
+ * @chip: NAND chip object
+ * @ofs: offset from device start
*
- * Default write function for 8bit buswidth.
+ * Check, if the block is bad.
*/
-static __maybe_unused void nand_write_buf(struct nand_chip *chip,
- const uint8_t *buf, int len)
+static int nand_block_bad(struct nand_chip *chip, loff_t ofs)
{
- int i;
+ int first_page, page_offset;
+ int res;
+ u8 bad;
- for (i = 0; i < len; i++)
- writeb(buf[i], chip->legacy.IO_ADDR_W);
+ first_page = (int)(ofs >> chip->page_shift) & chip->pagemask;
+ page_offset = nand_bbm_get_next_page(chip, 0);
+
+ while (page_offset >= 0) {
+ res = chip->ecc.read_oob(chip, first_page + page_offset);
+ if (res < 0)
+ return res;
+
+ bad = chip->oob_poi[chip->badblockpos];
+
+ if (likely(chip->badblockbits == 8))
+ res = bad != 0xFF;
+ else
+ res = hweight8(bad) < chip->badblockbits;
+ if (res)
+ return res;
+
+ page_offset = nand_bbm_get_next_page(chip, page_offset + 1);
+ }
+
+ return 0;
+}
+
+static int nand_isbad_bbm(struct nand_chip *chip, loff_t ofs)
+{
+ if (chip->options & NAND_NO_BBM_QUIRK)
+ return 0;
+
+ if (chip->legacy.block_bad)
+ return chip->legacy.block_bad(chip, ofs);
+
+ return nand_block_bad(chip, ofs);
}
/**
- * nand_read_buf - [DEFAULT] read chip data into buffer
- * @mtd: MTD device structure
- * @buf: buffer to store date
- * @len: number of bytes to read
+ * nand_get_device - [GENERIC] Get chip for selected access
+ * @chip: NAND chip structure
*
- * Default read function for 8bit buswidth.
+ * Lock the device and its controller for exclusive access
+ *
+ * Return: -EBUSY if the chip has been suspended, 0 otherwise
*/
-static void nand_read_buf(struct nand_chip *chip, uint8_t *buf, int len)
+static int nand_get_device(struct nand_chip *chip)
{
- int i;
+ mutex_lock(&chip->lock);
+ if (chip->suspended) {
+ mutex_unlock(&chip->lock);
+ return -EBUSY;
+ }
+ mutex_lock(&chip->controller->lock);
- for (i = 0; i < len; i++)
- buf[i] = readb(chip->legacy.IO_ADDR_R);
+ return 0;
}
/**
- * nand_write_buf16 - [DEFAULT] write buffer to chip
- * @mtd: MTD device structure
- * @buf: data buffer
- * @len: number of bytes to write
+ * nand_check_wp - [GENERIC] check if the chip is write protected
+ * @chip: NAND chip object
*
- * Default write function for 16bit buswidth.
+ * Check, if the device is write protected. The function expects, that the
+ * device is already selected.
*/
-static __maybe_unused void nand_write_buf16(struct nand_chip *chip,
- const uint8_t *buf, int len)
+static int nand_check_wp(struct nand_chip *chip)
{
- int i;
- u16 *p = (u16 *) buf;
- len >>= 1;
+ u8 status;
+ int ret;
- for (i = 0; i < len; i++)
- writew(p[i], chip->legacy.IO_ADDR_W);
+ /* Broken xD cards report WP despite being writable */
+ if (chip->options & NAND_BROKEN_XD)
+ return 0;
+
+ /* Check the WP bit */
+ ret = nand_status_op(chip, &status);
+ if (ret)
+ return ret;
+ return status & NAND_STATUS_WP ? 0 : 1;
}
/**
- * nand_read_buf16 - [DEFAULT] read chip data into buffer
- * @mtd: MTD device structure
- * @buf: buffer to store date
- * @len: number of bytes to read
- *
- * Default read function for 16bit buswidth.
+ * nand_fill_oob - [INTERN] Transfer client buffer to oob
+ * @chip: NAND chip object
+ * @oob: oob data buffer
+ * @len: oob data write length
+ * @ops: oob ops structure
*/
-static void nand_read_buf16(struct nand_chip *chip, uint8_t *buf, int len)
+static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob, size_t len,
+ struct mtd_oob_ops *ops)
{
- int i;
- u16 *p = (u16 *) buf;
- len >>= 1;
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ int ret;
- for (i = 0; i < len; i++)
- p[i] = readw(chip->legacy.IO_ADDR_R);
+ /*
+ * Initialise to all 0xFF, to avoid the possibility of left over OOB
+ * data from a previous OOB read.
+ */
+ memset(chip->oob_poi, 0xff, mtd->oobsize);
+
+ switch (ops->mode) {
+
+ case MTD_OPS_PLACE_OOB:
+ case MTD_OPS_RAW:
+ memcpy(chip->oob_poi + ops->ooboffs, oob, len);
+ return oob + len;
+
+ case MTD_OPS_AUTO_OOB:
+ ret = mtd_ooblayout_set_databytes(mtd, oob, chip->oob_poi,
+ ops->ooboffs, len);
+ BUG_ON(ret);
+ return oob + len;
+
+ default:
+ BUG();
+ }
+ return NULL;
}
/**
- * nand_block_bad - [DEFAULT] Read bad block marker from the chip
- * @mtd: MTD device structure
- * @ofs: offset from device start
- * @getchip: 0, if the chip is already selected
+ * nand_do_write_oob - [MTD Interface] NAND write out-of-band
+ * @chip: NAND chip object
+ * @to: offset to write to
+ * @ops: oob operation description structure
*
- * Check, if the block is bad.
+ * NAND write out-of-band.
*/
-static int nand_block_bad(struct nand_chip *chip, loff_t ofs, int getchip)
+static int nand_do_write_oob(struct nand_chip *chip, loff_t to,
+ struct mtd_oob_ops *ops)
{
- int page, chipnr, res = 0, i = 0;
- u16 bad;
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ int chipnr, page, status, len, ret;
- if (chip->bbt_options & NAND_BBT_SCANLASTPAGE)
- ofs += chip->mtd.erasesize - chip->mtd.writesize;
+ pr_debug("%s: to = 0x%08x, len = %i\n",
+ __func__, (unsigned int)to, (int)ops->ooblen);
- page = (int)(ofs >> chip->page_shift) & chip->pagemask;
+ len = mtd_oobavail(mtd, ops);
- if (getchip) {
- chipnr = (int)(ofs >> chip->chip_shift);
+ /* Do not allow write past end of page */
+ if ((ops->ooboffs + ops->ooblen) > len) {
+ pr_debug("%s: attempt to write past end of page\n",
+ __func__);
+ return -EINVAL;
+ }
- nand_get_device(chip, FL_READING);
+ chipnr = (int)(to >> chip->chip_shift);
- /* Select the NAND device */
- chip->legacy.select_chip(chip, chipnr);
- }
+ /*
+ * Reset the chip. Some chips (like the Toshiba TC5832DC found in one
+ * of my DiskOnChip 2000 test units) will clear the whole data page too
+ * if we don't do this. I have no clue why, but I seem to have 'fixed'
+ * it in the doc2000 driver in August 1999. dwmw2.
+ */
+ ret = nand_reset(chip, chipnr);
+ if (ret)
+ return ret;
- do {
- if (chip->options & NAND_BUSWIDTH_16) {
- chip->legacy.cmdfunc(chip, NAND_CMD_READOOB,
- chip->badblockpos & 0xFE, page);
- bad = cpu_to_le16(chip->legacy.read_word(chip));
- if (chip->badblockpos & 0x1)
- bad >>= 8;
- else
- bad &= 0xFF;
- } else {
- chip->legacy.cmdfunc(chip, NAND_CMD_READOOB, chip->badblockpos,
- page);
- bad = chip->legacy.read_byte(chip);
- }
+ nand_select_target(chip, chipnr);
- if (likely(chip->badblockbits == 8))
- res = bad != 0xFF;
- else
- res = hweight8(bad) < chip->badblockbits;
- ofs += chip->mtd.writesize;
- page = (int)(ofs >> chip->page_shift) & chip->pagemask;
- i++;
- } while (!res && i < 2 && (chip->bbt_options & NAND_BBT_SCAN2NDPAGE));
+ /* Shift to get page */
+ page = (int)(to >> chip->page_shift);
- if (getchip) {
- chip->legacy.select_chip(chip, -1);
- nand_release_device(chip);
+ /* Check, if it is write protected */
+ if (nand_check_wp(chip)) {
+ nand_deselect_target(chip);
+ return -EROFS;
}
- return res;
+ /* Invalidate the page cache, if we write to the cached page */
+ if (page == chip->pagecache.page)
+ chip->pagecache.page = -1;
+
+ nand_fill_oob(chip, ops->oobbuf, ops->ooblen, ops);
+
+ if (ops->mode == MTD_OPS_RAW)
+ status = chip->ecc.write_oob_raw(chip, page & chip->pagemask);
+ else
+ status = chip->ecc.write_oob(chip, page & chip->pagemask);
+
+ nand_deselect_target(chip);
+
+ if (status)
+ return status;
+
+ ops->oobretlen = ops->ooblen;
+
+ return 0;
}
/**
* nand_default_block_markbad - [DEFAULT] mark a block bad via bad block marker
- * @mtd: MTD device structure
+ * @chip: NAND chip object
* @ofs: offset from device start
*
* This is the default implementation, which can be overridden by a hardware
* specific driver. It provides the details for writing a bad block marker to a
* block.
*/
-static __maybe_unused int nand_default_block_markbad(struct nand_chip *chip, loff_t ofs)
+static int nand_default_block_markbad(struct nand_chip *chip, loff_t ofs)
{
+ struct mtd_info *mtd = nand_to_mtd(chip);
struct mtd_oob_ops ops;
uint8_t buf[2] = { 0, 0 };
- int ret = 0, res, i = 0;
+ int ret = 0, res, page_offset;
- ops.datbuf = NULL;
+ memset(&ops, 0, sizeof(ops));
ops.oobbuf = buf;
ops.ooboffs = chip->badblockpos;
if (chip->options & NAND_BUSWIDTH_16) {
@@ -339,24 +573,38 @@ static __maybe_unused int nand_default_block_markbad(struct nand_chip *chip, lof
}
ops.mode = MTD_OPS_PLACE_OOB;
- /* Write to first/last page(s) if necessary */
- if (chip->bbt_options & NAND_BBT_SCANLASTPAGE)
- ofs += chip->mtd.erasesize - chip->mtd.writesize;
- do {
- res = nand_do_write_oob(chip, ofs, &ops);
- if (!ret)
+ page_offset = nand_bbm_get_next_page(chip, 0);
+
+ while (page_offset >= 0) {
+ res = nand_do_write_oob(chip,
+ ofs + (page_offset * mtd->writesize),
+ &ops);
+
+ if (!ret)
ret = res;
- i++;
- ofs += chip->mtd.writesize;
- } while ((chip->bbt_options & NAND_BBT_SCAN2NDPAGE) && i < 2);
+ page_offset = nand_bbm_get_next_page(chip, page_offset + 1);
+ }
return ret;
}
+/**
+ * nand_markbad_bbm - mark a block by updating the BBM
+ * @chip: NAND chip object
+ * @ofs: offset of the block to mark bad
+ */
+int nand_markbad_bbm(struct nand_chip *chip, loff_t ofs)
+{
+ if (chip->legacy.block_markbad)
+ return chip->legacy.block_markbad(chip, ofs);
+
+ return nand_default_block_markbad(chip, ofs);
+}
+
/**
* nand_block_markbad_lowlevel - mark a block bad
- * @mtd: MTD device structure
+ * @chip: NAND chip object
* @ofs: offset from device start
*
* This function performs the generic NAND bad block marking steps (i.e., bad
@@ -364,15 +612,18 @@ static __maybe_unused int nand_default_block_markbad(struct nand_chip *chip, lof
* specify how to write bad block markers to OOB (chip->legacy.block_markbad).
*
* We try operations in the following order:
+ *
* (1) erase the affected block, to allow OOB marker to be written cleanly
* (2) write bad block marker to OOB area of affected block (unless flag
* NAND_BBT_NO_OOB_BBM is present)
* (3) update the BBT
+ *
* Note that we retain the first error encountered in (2) or (3), finish the
* procedures, and dump the error in the end.
- */
+*/
static int nand_block_markbad_lowlevel(struct nand_chip *chip, loff_t ofs)
{
+ struct mtd_info *mtd = nand_to_mtd(chip);
int res, ret = 0;
if (!(chip->bbt_options & NAND_BBT_NO_OOB_BBM)) {
@@ -380,26 +631,28 @@ static int nand_block_markbad_lowlevel(struct nand_chip *chip, loff_t ofs)
/* Attempt erase before marking OOB */
memset(&einfo, 0, sizeof(einfo));
- einfo.mtd = &chip->mtd;
einfo.addr = ofs;
- einfo.len = 1 << chip->phys_erase_shift;
+ einfo.len = 1ULL << chip->phys_erase_shift;
nand_erase_nand(chip, &einfo, 0);
/* Write bad block marker to OOB */
- nand_get_device(chip, FL_WRITING);
- ret = chip->legacy.block_markbad(chip, ofs);
+ ret = nand_get_device(chip);
+ if (ret)
+ return ret;
+
+ ret = nand_markbad_bbm(chip, ofs);
nand_release_device(chip);
}
/* Mark block bad in BBT */
- if (IS_ENABLED(CONFIG_NAND_BBT) && chip->bbt) {
+ if (chip->bbt) {
res = nand_markbad_bbt(chip, ofs);
if (!ret)
ret = res;
}
if (!ret)
- chip->mtd.ecc_stats.badblocks++;
+ mtd->ecc_stats.badblocks++;
return ret;
}
@@ -416,6 +669,7 @@ static int nand_block_markbad_lowlevel(struct nand_chip *chip, loff_t ofs)
*/
static int nand_block_markgood_lowlevel(struct nand_chip *chip, loff_t ofs)
{
+ struct mtd_info *mtd = nand_to_mtd(chip);
bool allow_erasebad;
int ret;
@@ -423,22 +677,14 @@ static int nand_block_markgood_lowlevel(struct nand_chip *chip, loff_t ofs)
struct erase_info einfo;
/* Attempt erase possibly bad block */
- allow_erasebad = chip->mtd.allow_erasebad;
- chip->mtd.allow_erasebad = true;
+ allow_erasebad = mtd->allow_erasebad;
+ mtd->allow_erasebad = true;
memset(&einfo, 0, sizeof(einfo));
- einfo.mtd = &chip->mtd;
+ einfo.mtd = mtd;
einfo.addr = ofs;
einfo.len = 1 << chip->phys_erase_shift;
nand_erase_nand(chip, &einfo, 0);
- chip->mtd.allow_erasebad = allow_erasebad;
-
- /*
- * Verify erase succeded. We need to select chip again,
- * as nand_erase_nand deselected it.
- */
- ret = chip->legacy.block_bad(chip, ofs, 1);
- if (ret)
- return ret;
+ mtd->allow_erasebad = allow_erasebad;
}
/* Mark block good in BBT */
@@ -448,3111 +694,4790 @@ static int nand_block_markgood_lowlevel(struct nand_chip *chip, loff_t ofs)
return ret;
}
- if (chip->mtd.ecc_stats.badblocks > 0)
- chip->mtd.ecc_stats.badblocks--;
+ if (mtd->ecc_stats.badblocks > 0)
+ mtd->ecc_stats.badblocks--;
return 0;
}
-/**
- * nand_check_wp - [GENERIC] check if the chip is write protected
- * @mtd: MTD device structure
- *
- * Check, if the device is write protected. The function expects, that the
- * device is already selected.
- */
-static int nand_check_wp(struct nand_chip *chip)
-{
- /* Broken xD cards report WP despite being writable */
- if (chip->options & NAND_BROKEN_XD)
- return 0;
-
- /* Check the WP bit */
- chip->legacy.cmdfunc(chip, NAND_CMD_STATUS, -1, -1);
- return (chip->legacy.read_byte(chip) & NAND_STATUS_WP) ? 0 : 1;
-}
-
/**
* nand_block_checkbad - [GENERIC] Check if a block is marked bad
- * @mtd: MTD device structure
+ * @chip: NAND chip object
* @ofs: offset from device start
- * @getchip: 0, if the chip is already selected
* @allowbbt: 1, if its allowed to access the bbt area
*
* Check, if the block is bad. Either by reading the bad block table or
* calling of the scan function.
*/
-static int nand_block_checkbad(struct nand_chip *chip, loff_t ofs, int getchip,
- int allowbbt)
+static int nand_block_checkbad(struct nand_chip *chip, loff_t ofs, int allowbbt)
{
- if (IS_ENABLED(CONFIG_NAND_BBT) && chip->bbt) {
- /* Return info from the table */
+ /* Return info from the table */
+ if (chip->bbt)
return nand_isbad_bbt(chip, ofs, allowbbt);
- }
-
- return chip->legacy.block_bad(chip, ofs, getchip);
-}
-/* Wait for the ready pin, after a command. The timeout is caught later. */
-void nand_wait_ready(struct nand_chip *chip)
-{
- uint64_t start = get_time_ns();
-
- /* wait until command is processed or timeout occures */
- do {
- if (chip->legacy.dev_ready(chip))
- break;
- } while (!is_timeout(start, SECOND * 2));
+ return nand_isbad_bbm(chip, ofs);
}
/**
- * nand_command - [DEFAULT] Send command to NAND device
- * @mtd: MTD device structure
- * @command: the command to be sent
- * @column: the column address for this command, -1 if none
- * @page_addr: the page address for this command, -1 if none
+ * nand_soft_waitrdy - Poll STATUS reg until RDY bit is set to 1
+ * @chip: NAND chip structure
+ * @timeout_ms: Timeout in ms
+ *
+ * Poll the STATUS register using ->exec_op() until the RDY bit becomes 1.
+ * If that does not happen whitin the specified timeout, -ETIMEDOUT is
+ * returned.
+ *
+ * This helper is intended to be used when the controller does not have access
+ * to the NAND R/B pin.
+ *
+ * Be aware that calling this helper from an ->exec_op() implementation means
+ * ->exec_op() must be re-entrant.
*
- * Send command to NAND device. This function is used for small page devices
- * (512 Bytes per page).
+ * Return 0 if the NAND chip is ready, a negative error otherwise.
*/
-static void nand_command(struct nand_chip *chip, unsigned int command,
- int column, int page_addr)
+int nand_soft_waitrdy(struct nand_chip *chip, unsigned long timeout_ms)
{
- int ctrl = NAND_CTRL_CLE | NAND_CTRL_CHANGE;
+ const struct nand_sdr_timings *timings;
+ u8 status = 0;
+ int ret;
+ uint64_t start;
- /* Write out the command to the device */
- if (IS_ENABLED(CONFIG_MTD_WRITE) && command == NAND_CMD_SEQIN) {
- int readcmd;
+ if (!nand_has_exec_op(chip))
+ return -ENOTSUPP;
- if (column >= chip->mtd.writesize) {
- /* OOB area */
- column -= chip->mtd.writesize;
- readcmd = NAND_CMD_READOOB;
- } else if (column < 256) {
- /* First 256 bytes --> READ0 */
- readcmd = NAND_CMD_READ0;
- } else {
- column -= 256;
- readcmd = NAND_CMD_READ1;
- }
- chip->legacy.cmd_ctrl(chip, readcmd, ctrl);
- ctrl &= ~NAND_CTRL_CHANGE;
- }
- chip->legacy.cmd_ctrl(chip, command, ctrl);
-
- /* Address cycle, when necessary */
- ctrl = NAND_CTRL_ALE | NAND_CTRL_CHANGE;
- /* Serially input address */
- if (column != -1) {
- /* Adjust columns for 16 bit buswidth */
- if (chip->options & NAND_BUSWIDTH_16)
- column >>= 1;
- chip->legacy.cmd_ctrl(chip, column, ctrl);
- ctrl &= ~NAND_CTRL_CHANGE;
- }
- if (page_addr != -1) {
- chip->legacy.cmd_ctrl(chip, page_addr, ctrl);
- ctrl &= ~NAND_CTRL_CHANGE;
- chip->legacy.cmd_ctrl(chip, page_addr >> 8, ctrl);
- /* One more address cycle for devices > 32MiB */
- if (chip->chipsize > (32 << 20))
- chip->legacy.cmd_ctrl(chip, page_addr >> 16, ctrl);
- }
- chip->legacy.cmd_ctrl(chip, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
+ /* Wait tWB before polling the STATUS reg. */
+ timings = nand_get_sdr_timings(nand_get_interface_config(chip));
+ ndelay(PSEC_TO_NSEC(timings->tWB_max));
- /*
- * Program and erase have their own busy handlers status and sequential
- * in needs no delay
- */
- switch (command) {
+ ret = nand_status_op(chip, NULL);
+ if (ret)
+ return ret;
- case NAND_CMD_PAGEPROG:
- case NAND_CMD_ERASE1:
- case NAND_CMD_ERASE2:
- case NAND_CMD_SEQIN:
- case NAND_CMD_STATUS:
- return;
+ start = get_time_ns();
+ do {
+ ret = nand_read_data_op(chip, &status, sizeof(status), true,
+ false);
+ if (ret)
+ break;
- case NAND_CMD_RESET:
- if (chip->legacy.dev_ready)
+ if (status & NAND_STATUS_READY)
break;
- udelay(chip->legacy.chip_delay);
- chip->legacy.cmd_ctrl(chip, NAND_CMD_STATUS,
- NAND_CTRL_CLE | NAND_CTRL_CHANGE);
- chip->legacy.cmd_ctrl(chip,
- NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
- while (!(chip->legacy.read_byte(chip) & NAND_STATUS_READY))
- ;
- return;
- /* This applies to read commands */
- default:
/*
- * If we don't have access to the busy pin, we apply the given
- * command delay
+ * Typical lowest execution time for a tR on most NANDs is 10us,
+ * use this as polling delay before doing something smarter (ie.
+ * deriving a delay from the timeout value, timeout_ms/ratio).
*/
- if (!chip->legacy.dev_ready) {
- udelay(chip->legacy.chip_delay);
- return;
- }
- }
+ udelay(10);
+ } while (!is_timeout(start, timeout_ms * MSECOND));
+
/*
- * Apply this short delay always to ensure that we do wait tWB in
- * any case on any machine.
+ * We have to exit READ_STATUS mode in order to read real data on the
+ * bus in case the WAITRDY instruction is preceding a DATA_IN
+ * instruction.
*/
- ndelay(100);
+ nand_exit_status_op(chip);
+
+ if (ret)
+ return ret;
+
+ return status & NAND_STATUS_READY ? 0 : -ETIMEDOUT;
+};
+EXPORT_SYMBOL_GPL(nand_soft_waitrdy);
+
+static bool nand_supports_get_features(struct nand_chip *chip, int addr)
+{
+ return (chip->parameters.supports_set_get_features &&
+ test_bit(addr, chip->parameters.get_feature_list));
+}
- nand_wait_ready(chip);
+bool nand_supports_set_features(struct nand_chip *chip, int addr)
+{
+ return (chip->parameters.supports_set_get_features &&
+ test_bit(addr, chip->parameters.set_feature_list));
}
/**
- * nand_command_lp - [DEFAULT] Send command to NAND large page device
- * @mtd: MTD device structure
- * @command: the command to be sent
- * @column: the column address for this command, -1 if none
- * @page_addr: the page address for this command, -1 if none
+ * nand_reset_interface - Reset data interface and timings
+ * @chip: The NAND chip
+ * @chipnr: Internal die id
*
- * Send command to NAND device. This is the version for the new large page
- * devices. We don't have the separate regions as we have in the small page
- * devices. We must emulate NAND_CMD_READOOB to keep the code compatible.
+ * Reset the Data interface and timings to ONFI mode 0.
+ *
+ * Returns 0 for success or negative error code otherwise.
*/
-static void nand_command_lp(struct nand_chip *chip, unsigned int command,
- int column, int page_addr)
+static int nand_reset_interface(struct nand_chip *chip, int chipnr)
{
- /* Emulate NAND_CMD_READOOB */
- if (command == NAND_CMD_READOOB) {
- column += chip->mtd.writesize;
- command = NAND_CMD_READ0;
- }
+ const struct nand_controller_ops *ops = chip->controller->ops;
+ int ret;
+
+ if (!nand_controller_can_setup_interface(chip))
+ return 0;
- /* Command latch cycle */
- chip->legacy.cmd_ctrl(chip, command, NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
+ /*
+ * The ONFI specification says:
+ * "
+ * To transition from NV-DDR or NV-DDR2 to the SDR data
+ * interface, the host shall use the Reset (FFh) command
+ * using SDR timing mode 0. A device in any timing mode is
+ * required to recognize Reset (FFh) command issued in SDR
+ * timing mode 0.
+ * "
+ *
+ * Configure the data interface in SDR mode and set the
+ * timings to timing mode 0.
+ */
- if (column != -1 || page_addr != -1) {
- int ctrl = NAND_CTRL_CHANGE | NAND_NCE | NAND_ALE;
+ chip->current_interface_config = nand_get_reset_interface_config();
+ ret = ops->setup_interface(chip, chipnr,
+ chip->current_interface_config);
+ if (ret)
+ pr_err("Failed to configure data interface to SDR timing mode 0\n");
- /* Serially input address */
- if (column != -1) {
- /* Adjust columns for 16 bit buswidth */
- if (chip->options & NAND_BUSWIDTH_16)
- column >>= 1;
- chip->legacy.cmd_ctrl(chip, column, ctrl);
- ctrl &= ~NAND_CTRL_CHANGE;
- chip->legacy.cmd_ctrl(chip, column >> 8, ctrl);
- }
- if (page_addr != -1) {
- chip->legacy.cmd_ctrl(chip, page_addr, ctrl);
- chip->legacy.cmd_ctrl(chip, page_addr >> 8,
- NAND_NCE | NAND_ALE);
- /* One more address cycle for devices > 128MiB */
- if (chip->chipsize > (128 << 20))
- chip->legacy.cmd_ctrl(chip, page_addr >> 16,
- NAND_NCE | NAND_ALE);
- }
- }
- chip->legacy.cmd_ctrl(chip, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
+ return ret;
+}
+
+/**
+ * nand_setup_interface - Setup the best data interface and timings
+ * @chip: The NAND chip
+ * @chipnr: Internal die id
+ *
+ * Configure what has been reported to be the best data interface and NAND
+ * timings supported by the chip and the driver.
+ *
+ * Returns 0 for success or negative error code otherwise.
+ */
+static int nand_setup_interface(struct nand_chip *chip, int chipnr)
+{
+ const struct nand_controller_ops *ops = chip->controller->ops;
+ u8 tmode_param[ONFI_SUBFEATURE_PARAM_LEN] = { };
+ int ret;
+
+ if (!nand_controller_can_setup_interface(chip))
+ return 0;
/*
- * Program and erase have their own busy handlers status, sequential
- * in, and deplete1 need no delay.
+ * A nand_reset_interface() put both the NAND chip and the NAND
+ * controller in timings mode 0. If the default mode for this chip is
+ * also 0, no need to proceed to the change again. Plus, at probe time,
+ * nand_setup_interface() uses ->set/get_features() which would
+ * fail anyway as the parameter page is not available yet.
*/
- switch (command) {
-
- case NAND_CMD_CACHEDPROG:
- case NAND_CMD_PAGEPROG:
- case NAND_CMD_ERASE1:
- case NAND_CMD_ERASE2:
- case NAND_CMD_SEQIN:
- case NAND_CMD_RNDIN:
- case NAND_CMD_STATUS:
- return;
+ if (!chip->best_interface_config)
+ return 0;
- case NAND_CMD_RESET:
- if (chip->legacy.dev_ready)
- break;
- udelay(chip->legacy.chip_delay);
- chip->legacy.cmd_ctrl(chip, NAND_CMD_STATUS,
- NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
- chip->legacy.cmd_ctrl(chip, NAND_CMD_NONE,
- NAND_NCE | NAND_CTRL_CHANGE);
- while (!(chip->legacy.read_byte(chip) & NAND_STATUS_READY))
- ;
- return;
+ tmode_param[0] = chip->best_interface_config->timings.mode;
- case NAND_CMD_RNDOUT:
- /* No ready / busy check necessary */
- chip->legacy.cmd_ctrl(chip, NAND_CMD_RNDOUTSTART,
- NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
- chip->legacy.cmd_ctrl(chip, NAND_CMD_NONE,
- NAND_NCE | NAND_CTRL_CHANGE);
- return;
+ /* Change the mode on the chip side (if supported by the NAND chip) */
+ if (nand_supports_set_features(chip, ONFI_FEATURE_ADDR_TIMING_MODE)) {
+ nand_select_target(chip, chipnr);
+ ret = nand_set_features(chip, ONFI_FEATURE_ADDR_TIMING_MODE,
+ tmode_param);
+ nand_deselect_target(chip);
+ if (ret)
+ return ret;
+ }
- case NAND_CMD_READ0:
- chip->legacy.cmd_ctrl(chip, NAND_CMD_READSTART,
- NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
- chip->legacy.cmd_ctrl(chip, NAND_CMD_NONE,
- NAND_NCE | NAND_CTRL_CHANGE);
+ /* Change the mode on the controller side */
+ ret = ops->setup_interface(chip, chipnr, chip->best_interface_config);
+ if (ret)
+ return ret;
- /* This applies to read commands */
- default:
- /*
- * If we don't have access to the busy pin, we apply the given
- * command delay.
- */
- if (!chip->legacy.dev_ready) {
- udelay(chip->legacy.chip_delay);
- return;
- }
+ /* Check the mode has been accepted by the chip, if supported */
+ if (!nand_supports_get_features(chip, ONFI_FEATURE_ADDR_TIMING_MODE))
+ goto update_interface_config;
+
+ memset(tmode_param, 0, ONFI_SUBFEATURE_PARAM_LEN);
+ nand_select_target(chip, chipnr);
+ ret = nand_get_features(chip, ONFI_FEATURE_ADDR_TIMING_MODE,
+ tmode_param);
+ nand_deselect_target(chip);
+ if (ret)
+ goto err_reset_chip;
+
+ if (tmode_param[0] != chip->best_interface_config->timings.mode) {
+ pr_warn("timing mode %d not acknowledged by the NAND chip\n",
+ chip->best_interface_config->timings.mode);
+ goto err_reset_chip;
}
+update_interface_config:
+ chip->current_interface_config = chip->best_interface_config;
+
+ return 0;
+
+err_reset_chip:
/*
- * Apply this short delay always to ensure that we do wait tWB in
- * any case on any machine.
+ * Fallback to mode 0 if the chip explicitly did not ack the chosen
+ * timing mode.
*/
- ndelay(100);
+ nand_reset_interface(chip, chipnr);
+ nand_select_target(chip, chipnr);
+ nand_reset_op(chip);
+ nand_deselect_target(chip);
- nand_wait_ready(chip);
+ return ret;
}
/**
- * nand_get_device - [GENERIC] Get chip for selected access
- * @mtd: MTD device structure
- * @new_state: the state which is requested
+ * nand_choose_best_sdr_timings - Pick up the best SDR timings that both the
+ * NAND controller and the NAND chip support
+ * @chip: the NAND chip
+ * @iface: the interface configuration (can eventually be updated)
+ * @spec_timings: specific timings, when not fitting the ONFI specification
*
- * Get the device and lock it for exclusive access
+ * If specific timings are provided, use them. Otherwise, retrieve supported
+ * timing modes from ONFI information.
*/
-static int
-nand_get_device(struct nand_chip *chip, int new_state)
+int nand_choose_best_sdr_timings(struct nand_chip *chip,
+ struct nand_interface_config *iface,
+ struct nand_sdr_timings *spec_timings)
{
-retry:
+ const struct nand_controller_ops *ops = chip->controller->ops;
+ int best_mode = 0, mode, ret;
- /* Hardware controller shared among independent devices */
- if (!chip->controller->active)
- chip->controller->active = chip;
+ iface->type = NAND_SDR_IFACE;
- if (chip->controller->active == chip && chip->state == FL_READY) {
- chip->state = new_state;
- return 0;
- }
- if (new_state == FL_PM_SUSPENDED) {
- if (chip->controller->active->state == FL_PM_SUSPENDED) {
- chip->state = FL_PM_SUSPENDED;
- return 0;
+ if (spec_timings) {
+ iface->timings.sdr = *spec_timings;
+ iface->timings.mode = onfi_find_closest_sdr_mode(spec_timings);
+
+ /* Verify the controller supports the requested interface */
+ ret = ops->setup_interface(chip, NAND_DATA_IFACE_CHECK_ONLY,
+ iface);
+ if (!ret) {
+ chip->best_interface_config = iface;
+ return ret;
}
+
+ /* Fallback to slower modes */
+ best_mode = iface->timings.mode;
+ } else if (chip->parameters.onfi) {
+ best_mode = fls(chip->parameters.onfi->async_timing_mode) - 1;
+ }
+
+ for (mode = best_mode; mode >= 0; mode--) {
+ onfi_fill_interface_config(chip, iface, NAND_SDR_IFACE, mode);
+
+ ret = ops->setup_interface(chip, NAND_DATA_IFACE_CHECK_ONLY,
+ iface);
+ if (!ret)
+ break;
}
- goto retry;
+
+ chip->best_interface_config = iface;
+
+ return 0;
}
/**
- * nand_wait - [DEFAULT] wait until the command is done
- * @mtd: MTD device structure
- * @chip: NAND chip structure
+ * nand_choose_interface_config - find the best data interface and timings
+ * @chip: The NAND chip
+ *
+ * Find the best data interface and NAND timings supported by the chip
+ * and the driver. Eventually let the NAND manufacturer driver propose his own
+ * set of timings.
+ *
+ * After this function nand_chip->interface_config is initialized with the best
+ * timing mode available.
*
- * Wait for command done. This applies to erase and program only
- * Erase can take up to 400ms and program up to 20ms according to
- * general NAND and SmartMedia specs
+ * Returns 0 for success or negative error code otherwise.
*/
-static int nand_wait(struct nand_chip *chip)
+static int nand_choose_interface_config(struct nand_chip *chip)
{
+ struct nand_interface_config *iface;
+ int ret;
- uint64_t start = get_time_ns();
- uint64_t timeo;
- int status, state = chip->state;
-
- if (state == FL_ERASING)
- timeo = 400 * MSECOND;
- else
- timeo = 20 * MSECOND;
+ if (!nand_controller_can_setup_interface(chip))
+ return 0;
- /* Apply this short delay always to ensure that we do wait tWB in
- * any case on any machine. */
- ndelay(100);
+ iface = kzalloc(sizeof(*iface), GFP_KERNEL);
+ if (!iface)
+ return -ENOMEM;
- chip->legacy.cmdfunc(chip, NAND_CMD_STATUS, -1, -1);
+ if (chip->ops.choose_interface_config)
+ ret = chip->ops.choose_interface_config(chip, iface);
+ else
+ ret = nand_choose_best_sdr_timings(chip, iface, NULL);
- while (!is_timeout(start, timeo)) {
- if (chip->legacy.dev_ready) {
- if (chip->legacy.dev_ready(chip))
- break;
- } else {
- if (chip->legacy.read_byte(chip) & NAND_STATUS_READY)
- break;
- }
- }
+ if (ret)
+ kfree(iface);
- status = (int)chip->legacy.read_byte(chip);
- return status;
+ return ret;
}
/**
- * __nand_unlock - [REPLACEABLE] unlocks specified locked blocks
- * @mtd: mtd info
- * @ofs: offset to start unlock from
- * @len: length to unlock
- * @invert: when = 0, unlock the range of blocks within the lower and
- * upper boundary address
- * when = 1, unlock the range of blocks outside the boundaries
- * of the lower and upper boundary address
+ * nand_fill_column_cycles - fill the column cycles of an address
+ * @chip: The NAND chip
+ * @addrs: Array of address cycles to fill
+ * @offset_in_page: The offset in the page
+ *
+ * Fills the first or the first two bytes of the @addrs field depending
+ * on the NAND bus width and the page size.
*
- * Returs unlock status.
+ * Returns the number of cycles needed to encode the column, or a negative
+ * error code in case one of the arguments is invalid.
*/
-static int __nand_unlock(struct nand_chip *chip, loff_t ofs,
- uint64_t len, int invert)
+static int nand_fill_column_cycles(struct nand_chip *chip, u8 *addrs,
+ unsigned int offset_in_page)
{
- int ret = 0;
- int status, page;
-
- /* Submit address of first page to unlock */
- page = ofs >> chip->page_shift;
- chip->legacy.cmdfunc(chip, NAND_CMD_UNLOCK1, -1, page & chip->pagemask);
-
- /* Submit address of last page to unlock */
- page = (ofs + len) >> chip->page_shift;
- chip->legacy.cmdfunc(chip, NAND_CMD_UNLOCK2, -1,
- (page | invert) & chip->pagemask);
-
- /* Call wait ready function */
- status = chip->legacy.waitfunc(chip);
- /* See if device thinks it succeeded */
- if (status & NAND_STATUS_FAIL) {
- pr_debug("%s: error status = 0x%08x\n",
- __func__, status);
- ret = -EIO;
+ struct mtd_info *mtd = nand_to_mtd(chip);
+
+ /* Make sure the offset is less than the actual page size. */
+ if (offset_in_page > mtd->writesize + mtd->oobsize)
+ return -EINVAL;
+
+ /*
+ * On small page NANDs, there's a dedicated command to access the OOB
+ * area, and the column address is relative to the start of the OOB
+ * area, not the start of the page. Asjust the address accordingly.
+ */
+ if (mtd->writesize <= 512 && offset_in_page >= mtd->writesize)
+ offset_in_page -= mtd->writesize;
+
+ /*
+ * The offset in page is expressed in bytes, if the NAND bus is 16-bit
+ * wide, then it must be divided by 2.
+ */
+ if (chip->options & NAND_BUSWIDTH_16) {
+ if (WARN_ON(offset_in_page % 2))
+ return -EINVAL;
+
+ offset_in_page /= 2;
}
- return ret;
+ addrs[0] = offset_in_page;
+
+ /*
+ * Small page NANDs use 1 cycle for the columns, while large page NANDs
+ * need 2
+ */
+ if (mtd->writesize <= 512)
+ return 1;
+
+ addrs[1] = offset_in_page >> 8;
+
+ return 2;
}
-/**
- * nand_unlock - [REPLACEABLE] unlocks specified locked blocks
- * @mtd: mtd info
- * @ofs: offset to start unlock from
- * @len: length to unlock
- *
- * Returns unlock status.
- */
-int nand_unlock(struct nand_chip *chip, loff_t ofs, uint64_t len)
+static int nand_sp_exec_read_page_op(struct nand_chip *chip, unsigned int page,
+ unsigned int offset_in_page, void *buf,
+ unsigned int len)
{
+ const struct nand_sdr_timings *sdr =
+ nand_get_sdr_timings(nand_get_interface_config(chip));
struct mtd_info *mtd = nand_to_mtd(chip);
- int ret = 0;
- int chipnr;
+ u8 addrs[4];
+ struct nand_op_instr instrs[] = {
+ NAND_OP_CMD(NAND_CMD_READ0, 0),
+ NAND_OP_ADDR(3, addrs, PSEC_TO_NSEC(sdr->tWB_max)),
+ NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tR_max),
+ PSEC_TO_NSEC(sdr->tRR_min)),
+ NAND_OP_DATA_IN(len, buf, 0),
+ };
+ struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);
+ int ret;
- pr_debug("%s: start = 0x%012llx, len = %llu\n",
- __func__, (unsigned long long)ofs, len);
+ /* Drop the DATA_IN instruction if len is set to 0. */
+ if (!len)
+ op.ninstrs--;
- if (check_offs_len(chip, ofs, len))
- ret = -EINVAL;
+ if (offset_in_page >= mtd->writesize)
+ instrs[0].ctx.cmd.opcode = NAND_CMD_READOOB;
+ else if (offset_in_page >= 256 &&
+ !(chip->options & NAND_BUSWIDTH_16))
+ instrs[0].ctx.cmd.opcode = NAND_CMD_READ1;
+
+ ret = nand_fill_column_cycles(chip, addrs, offset_in_page);
+ if (ret < 0)
+ return ret;
- /* Align to last block address if size addresses end of the device */
- if (ofs + len == mtd->size)
- len -= mtd->erasesize;
+ addrs[1] = page;
+ addrs[2] = page >> 8;
- nand_get_device(chip, FL_UNLOCKING);
+ if (chip->options & NAND_ROW_ADDR_3) {
+ addrs[3] = page >> 16;
+ instrs[1].ctx.addr.naddrs++;
+ }
- /* Shift to get chip number */
- chipnr = ofs >> chip->chip_shift;
+ return nand_exec_op(chip, &op);
+}
- chip->legacy.select_chip(chip, chipnr);
+static int nand_lp_exec_read_page_op(struct nand_chip *chip, unsigned int page,
+ unsigned int offset_in_page, void *buf,
+ unsigned int len)
+{
+ const struct nand_sdr_timings *sdr =
+ nand_get_sdr_timings(nand_get_interface_config(chip));
+ u8 addrs[5];
+ struct nand_op_instr instrs[] = {
+ NAND_OP_CMD(NAND_CMD_READ0, 0),
+ NAND_OP_ADDR(4, addrs, 0),
+ NAND_OP_CMD(NAND_CMD_READSTART, PSEC_TO_NSEC(sdr->tWB_max)),
+ NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tR_max),
+ PSEC_TO_NSEC(sdr->tRR_min)),
+ NAND_OP_DATA_IN(len, buf, 0),
+ };
+ struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);
+ int ret;
- /*
- * Reset the chip.
- * If we want to check the WP through READ STATUS and check the bit 7
- * we must reset the chip
- * some operation can also clear the bit 7 of status register
- * eg. erase/program a locked block
- */
- chip->legacy.cmdfunc(chip, NAND_CMD_RESET, -1, -1);
+ /* Drop the DATA_IN instruction if len is set to 0. */
+ if (!len)
+ op.ninstrs--;
- /* Check, if it is write protected */
- if (nand_check_wp(chip)) {
- pr_debug("%s: device is write protected!\n",
- __func__);
- ret = -EIO;
- goto out;
- }
+ ret = nand_fill_column_cycles(chip, addrs, offset_in_page);
+ if (ret < 0)
+ return ret;
- ret = __nand_unlock(chip, ofs, len, 0);
+ addrs[2] = page;
+ addrs[3] = page >> 8;
-out:
- chip->legacy.select_chip(chip, -1);
- nand_release_device(chip);
+ if (chip->options & NAND_ROW_ADDR_3) {
+ addrs[4] = page >> 16;
+ instrs[1].ctx.addr.naddrs++;
+ }
- return ret;
+ return nand_exec_op(chip, &op);
}
-EXPORT_SYMBOL(nand_unlock);
/**
- * nand_lock - [REPLACEABLE] locks all blocks present in the device
- * @mtd: mtd info
- * @ofs: offset to start unlock from
- * @len: length to unlock
+ * nand_read_page_op - Do a READ PAGE operation
+ * @chip: The NAND chip
+ * @page: page to read
+ * @offset_in_page: offset within the page
+ * @buf: buffer used to store the data
+ * @len: length of the buffer
*
- * This feature is not supported in many NAND parts. 'Micron' NAND parts do
- * have this feature, but it allows only to lock all blocks, not for specified
- * range for block. Implementing 'lock' feature by making use of 'unlock', for
- * now.
+ * This function issues a READ PAGE operation.
+ * This function does not select/unselect the CS line.
*
- * Returns lock status.
+ * Returns 0 on success, a negative error code otherwise.
*/
-int nand_lock(struct nand_chip *chip, loff_t ofs, uint64_t len)
+int nand_read_page_op(struct nand_chip *chip, unsigned int page,
+ unsigned int offset_in_page, void *buf, unsigned int len)
{
- int ret = 0;
- int chipnr, status, page;
+ struct mtd_info *mtd = nand_to_mtd(chip);
- pr_debug("%s: start = 0x%012llx, len = %llu\n",
- __func__, (unsigned long long)ofs, len);
+ if (len && !buf)
+ return -EINVAL;
- if (check_offs_len(chip, ofs, len))
- ret = -EINVAL;
+ if (offset_in_page + len > mtd->writesize + mtd->oobsize)
+ return -EINVAL;
- nand_get_device(chip, FL_LOCKING);
+ if (nand_has_exec_op(chip)) {
+ if (mtd->writesize > 512)
+ return nand_lp_exec_read_page_op(chip, page,
+ offset_in_page, buf,
+ len);
- /* Shift to get chip number */
- chipnr = ofs >> chip->chip_shift;
+ return nand_sp_exec_read_page_op(chip, page, offset_in_page,
+ buf, len);
+ }
- chip->legacy.select_chip(chip, chipnr);
+ chip->legacy.cmdfunc(chip, NAND_CMD_READ0, offset_in_page, page);
+ if (len)
+ chip->legacy.read_buf(chip, buf, len);
- /*
- * Reset the chip.
- * If we want to check the WP through READ STATUS and check the bit 7
- * we must reset the chip
- * some operation can also clear the bit 7 of status register
- * eg. erase/program a locked block
- */
- chip->legacy.cmdfunc(chip, NAND_CMD_RESET, -1, -1);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(nand_read_page_op);
- /* Check, if it is write protected */
- if (nand_check_wp(chip)) {
- pr_debug("%s: device is write protected!\n",
- __func__);
- ret = -EIO;
- goto out;
- }
+/**
+ * nand_read_param_page_op - Do a READ PARAMETER PAGE operation
+ * @chip: The NAND chip
+ * @page: parameter page to read
+ * @buf: buffer used to store the data
+ * @len: length of the buffer
+ *
+ * This function issues a READ PARAMETER PAGE operation.
+ * This function does not select/unselect the CS line.
+ *
+ * Returns 0 on success, a negative error code otherwise.
+ */
+int nand_read_param_page_op(struct nand_chip *chip, u8 page, void *buf,
+ unsigned int len)
+{
+ unsigned int i;
+ u8 *p = buf;
- /* Submit address of first page to lock */
- page = ofs >> chip->page_shift;
- chip->legacy.cmdfunc(chip, NAND_CMD_LOCK, -1, page & chip->pagemask);
+ if (len && !buf)
+ return -EINVAL;
- /* Call wait ready function */
- status = chip->legacy.waitfunc(chip);
- /* See if device thinks it succeeded */
- if (status & NAND_STATUS_FAIL) {
- pr_debug("%s: error status = 0x%08x\n",
- __func__, status);
- ret = -EIO;
- goto out;
+ if (nand_has_exec_op(chip)) {
+ const struct nand_sdr_timings *sdr =
+ nand_get_sdr_timings(nand_get_interface_config(chip));
+ struct nand_op_instr instrs[] = {
+ NAND_OP_CMD(NAND_CMD_PARAM, 0),
+ NAND_OP_ADDR(1, &page, PSEC_TO_NSEC(sdr->tWB_max)),
+ NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tR_max),
+ PSEC_TO_NSEC(sdr->tRR_min)),
+ NAND_OP_8BIT_DATA_IN(len, buf, 0),
+ };
+ struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);
+
+ /* Drop the DATA_IN instruction if len is set to 0. */
+ if (!len)
+ op.ninstrs--;
+
+ return nand_exec_op(chip, &op);
}
- ret = __nand_unlock(chip, ofs, len, 0x1);
-
-out:
- chip->legacy.select_chip(chip, -1);
- nand_release_device(chip);
+ chip->legacy.cmdfunc(chip, NAND_CMD_PARAM, page, -1);
+ for (i = 0; i < len; i++)
+ p[i] = chip->legacy.read_byte(chip);
- return ret;
+ return 0;
}
-EXPORT_SYMBOL(nand_lock);
/**
- * nand_check_erased_buf - check if a buffer contains (almost) only 0xff data
- * @buf: buffer to test
- * @len: buffer length
- * @bitflips_threshold: maximum number of bitflips
+ * nand_change_read_column_op - Do a CHANGE READ COLUMN operation
+ * @chip: The NAND chip
+ * @offset_in_page: offset within the page
+ * @buf: buffer used to store the data
+ * @len: length of the buffer
+ * @force_8bit: force 8-bit bus access
*
- * Check if a buffer contains only 0xff, which means the underlying region
- * has been erased and is ready to be programmed.
- * The bitflips_threshold specify the maximum number of bitflips before
- * considering the region is not erased.
- * Note: The logic of this function has been extracted from the memweight
- * implementation, except that nand_check_erased_buf function exit before
- * testing the whole buffer if the number of bitflips exceed the
- * bitflips_threshold value.
+ * This function issues a CHANGE READ COLUMN operation.
+ * This function does not select/unselect the CS line.
*
- * Returns a positive number of bitflips less than or equal to
- * bitflips_threshold, or -ERROR_CODE for bitflips in excess of the
- * threshold.
+ * Returns 0 on success, a negative error code otherwise.
*/
-int nand_check_erased_buf(void *buf, int len, int bitflips_threshold)
+int nand_change_read_column_op(struct nand_chip *chip,
+ unsigned int offset_in_page, void *buf,
+ unsigned int len, bool force_8bit)
{
- const unsigned char *bitmap = buf;
- int bitflips = 0;
- int weight;
+ struct mtd_info *mtd = nand_to_mtd(chip);
- for (; len && ((uintptr_t)bitmap) % sizeof(long);
- len--, bitmap++) {
- weight = hweight8(*bitmap);
- bitflips += BITS_PER_BYTE - weight;
- if (unlikely(bitflips > bitflips_threshold))
- return -EBADMSG;
- }
+ if (len && !buf)
+ return -EINVAL;
- for (; len >= sizeof(long);
- len -= sizeof(long), bitmap += sizeof(long)) {
- weight = hweight_long(*((unsigned long *)bitmap));
- bitflips += BITS_PER_LONG - weight;
- if (unlikely(bitflips > bitflips_threshold))
- return -EBADMSG;
- }
+ if (offset_in_page + len > mtd->writesize + mtd->oobsize)
+ return -EINVAL;
- for (; len > 0; len--, bitmap++) {
- weight = hweight8(*bitmap);
- bitflips += BITS_PER_BYTE - weight;
- if (unlikely(bitflips > bitflips_threshold))
- return -EBADMSG;
+ /* Small page NANDs do not support column change. */
+ if (mtd->writesize <= 512)
+ return -ENOTSUPP;
+
+ if (nand_has_exec_op(chip)) {
+ const struct nand_sdr_timings *sdr =
+ nand_get_sdr_timings(nand_get_interface_config(chip));
+ u8 addrs[2] = {};
+ struct nand_op_instr instrs[] = {
+ NAND_OP_CMD(NAND_CMD_RNDOUT, 0),
+ NAND_OP_ADDR(2, addrs, 0),
+ NAND_OP_CMD(NAND_CMD_RNDOUTSTART,
+ PSEC_TO_NSEC(sdr->tCCS_min)),
+ NAND_OP_DATA_IN(len, buf, 0),
+ };
+ struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);
+ int ret;
+
+ ret = nand_fill_column_cycles(chip, addrs, offset_in_page);
+ if (ret < 0)
+ return ret;
+
+ /* Drop the DATA_IN instruction if len is set to 0. */
+ if (!len)
+ op.ninstrs--;
+
+ instrs[3].ctx.data.force_8bit = force_8bit;
+
+ return nand_exec_op(chip, &op);
}
- return bitflips;
+ chip->legacy.cmdfunc(chip, NAND_CMD_RNDOUT, offset_in_page, -1);
+ if (len)
+ chip->legacy.read_buf(chip, buf, len);
+
+ return 0;
}
+EXPORT_SYMBOL_GPL(nand_change_read_column_op);
/**
- * nand_check_erased_ecc_chunk - check if an ECC chunk contains (almost) only
- * 0xff data
- * @data: data buffer to test
- * @datalen: data length
- * @ecc: ECC buffer
- * @ecclen: ECC length
- * @extraoob: extra OOB buffer
- * @extraooblen: extra OOB length
- * @bitflips_threshold: maximum number of bitflips
- *
- * Check if a data buffer and its associated ECC and OOB data contains only
- * 0xff pattern, which means the underlying region has been erased and is
- * ready to be programmed.
- * The bitflips_threshold specify the maximum number of bitflips before
- * considering the region as not erased.
+ * nand_read_oob_op - Do a READ OOB operation
+ * @chip: The NAND chip
+ * @page: page to read
+ * @offset_in_oob: offset within the OOB area
+ * @buf: buffer used to store the data
+ * @len: length of the buffer
*
- * Note:
- * 1/ ECC algorithms are working on pre-defined block sizes which are usually
- * different from the NAND page size. When fixing bitflips, ECC engines will
- * report the number of errors per chunk, and the NAND core infrastructure
- * expect you to return the maximum number of bitflips for the whole page.
- * This is why you should always use this function on a single chunk and
- * not on the whole page. After checking each chunk you should update your
- * max_bitflips value accordingly.
- * 2/ When checking for bitflips in erased pages you should not only check
- * the payload data but also their associated ECC data, because a user might
- * have programmed almost all bits to 1 but a few. In this case, we
- * shouldn't consider the chunk as erased, and checking ECC bytes prevent
- * this case.
- * 3/ The extraoob argument is optional, and should be used if some of your OOB
- * data are protected by the ECC engine.
- * It could also be used if you support subpages and want to attach some
- * extra OOB data to an ECC chunk.
+ * This function issues a READ OOB operation.
+ * This function does not select/unselect the CS line.
*
- * Returns a positive number of bitflips less than or equal to
- * bitflips_threshold, or -ERROR_CODE for bitflips in excess of the
- * threshold. In case of success, the passed buffers are filled with 0xff.
+ * Returns 0 on success, a negative error code otherwise.
*/
-int nand_check_erased_ecc_chunk(void *data, int datalen,
- void *ecc, int ecclen,
- void *extraoob, int extraooblen,
- int bitflips_threshold)
+int nand_read_oob_op(struct nand_chip *chip, unsigned int page,
+ unsigned int offset_in_oob, void *buf, unsigned int len)
{
- int data_bitflips = 0, ecc_bitflips = 0, extraoob_bitflips = 0;
+ struct mtd_info *mtd = nand_to_mtd(chip);
- data_bitflips = nand_check_erased_buf(data, datalen,
- bitflips_threshold);
- if (data_bitflips < 0)
- return data_bitflips;
+ if (len && !buf)
+ return -EINVAL;
- bitflips_threshold -= data_bitflips;
+ if (offset_in_oob + len > mtd->oobsize)
+ return -EINVAL;
- ecc_bitflips = nand_check_erased_buf(ecc, ecclen, bitflips_threshold);
- if (ecc_bitflips < 0)
- return ecc_bitflips;
+ if (nand_has_exec_op(chip))
+ return nand_read_page_op(chip, page,
+ mtd->writesize + offset_in_oob,
+ buf, len);
- bitflips_threshold -= ecc_bitflips;
+ chip->legacy.cmdfunc(chip, NAND_CMD_READOOB, offset_in_oob, page);
+ if (len)
+ chip->legacy.read_buf(chip, buf, len);
- extraoob_bitflips = nand_check_erased_buf(extraoob, extraooblen,
- bitflips_threshold);
- if (extraoob_bitflips < 0)
- return extraoob_bitflips;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(nand_read_oob_op);
- if (data_bitflips)
- memset(data, 0xff, datalen);
+static int nand_exec_prog_page_op(struct nand_chip *chip, unsigned int page,
+ unsigned int offset_in_page, const void *buf,
+ unsigned int len, bool prog)
+{
+ const struct nand_sdr_timings *sdr =
+ nand_get_sdr_timings(nand_get_interface_config(chip));
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ u8 addrs[5] = {};
+ struct nand_op_instr instrs[] = {
+ /*
+ * The first instruction will be dropped if we're dealing
+ * with a large page NAND and adjusted if we're dealing
+ * with a small page NAND and the page offset is > 255.
+ */
+ NAND_OP_CMD(NAND_CMD_READ0, 0),
+ NAND_OP_CMD(NAND_CMD_SEQIN, 0),
+ NAND_OP_ADDR(0, addrs, PSEC_TO_NSEC(sdr->tADL_min)),
+ NAND_OP_DATA_OUT(len, buf, 0),
+ NAND_OP_CMD(NAND_CMD_PAGEPROG, PSEC_TO_NSEC(sdr->tWB_max)),
+ NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tPROG_max), 0),
+ };
+ struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);
+ int naddrs = nand_fill_column_cycles(chip, addrs, offset_in_page);
+ int ret;
+ u8 status;
- if (ecc_bitflips)
- memset(ecc, 0xff, ecclen);
+ if (naddrs < 0)
+ return naddrs;
- if (extraoob_bitflips)
- memset(extraoob, 0xff, extraooblen);
+ addrs[naddrs++] = page;
+ addrs[naddrs++] = page >> 8;
+ if (chip->options & NAND_ROW_ADDR_3)
+ addrs[naddrs++] = page >> 16;
- return data_bitflips + ecc_bitflips + extraoob_bitflips;
+ instrs[2].ctx.addr.naddrs = naddrs;
+
+ /* Drop the last two instructions if we're not programming the page. */
+ if (!prog) {
+ op.ninstrs -= 2;
+ /* Also drop the DATA_OUT instruction if empty. */
+ if (!len)
+ op.ninstrs--;
+ }
+
+ if (mtd->writesize <= 512) {
+ /*
+ * Small pages need some more tweaking: we have to adjust the
+ * first instruction depending on the page offset we're trying
+ * to access.
+ */
+ if (offset_in_page >= mtd->writesize)
+ instrs[0].ctx.cmd.opcode = NAND_CMD_READOOB;
+ else if (offset_in_page >= 256 &&
+ !(chip->options & NAND_BUSWIDTH_16))
+ instrs[0].ctx.cmd.opcode = NAND_CMD_READ1;
+ } else {
+ /*
+ * Drop the first command if we're dealing with a large page
+ * NAND.
+ */
+ op.instrs++;
+ op.ninstrs--;
+ }
+
+ ret = nand_exec_op(chip, &op);
+ if (!prog || ret)
+ return ret;
+
+ ret = nand_status_op(chip, &status);
+ if (ret)
+ return ret;
+
+ return status;
}
-EXPORT_SYMBOL(nand_check_erased_ecc_chunk);
/**
- * nand_read_page_raw - [INTERN] read raw page data without ecc
- * @mtd: mtd info structure
- * @chip: nand chip info structure
- * @buf: buffer to store read data
- * @oob_required: caller requires OOB data read to chip->oob_poi
- * @page: page number to read
+ * nand_prog_page_begin_op - starts a PROG PAGE operation
+ * @chip: The NAND chip
+ * @page: page to write
+ * @offset_in_page: offset within the page
+ * @buf: buffer containing the data to write to the page
+ * @len: length of the buffer
*
- * Not for syndrome calculating ECC controllers, which use a special oob layout.
+ * This function issues the first half of a PROG PAGE operation.
+ * This function does not select/unselect the CS line.
+ *
+ * Returns 0 on success, a negative error code otherwise.
*/
-static __maybe_unused int nand_read_page_raw(struct nand_chip *chip,
- uint8_t *buf, int oob_required,
- int page)
+int nand_prog_page_begin_op(struct nand_chip *chip, unsigned int page,
+ unsigned int offset_in_page, const void *buf,
+ unsigned int len)
{
- chip->legacy.read_buf(chip, buf, chip->mtd.writesize);
- if (oob_required)
- chip->legacy.read_buf(chip, chip->oob_poi, chip->mtd.oobsize);
+ struct mtd_info *mtd = nand_to_mtd(chip);
+
+ if (len && !buf)
+ return -EINVAL;
+
+ if (offset_in_page + len > mtd->writesize + mtd->oobsize)
+ return -EINVAL;
+
+ if (nand_has_exec_op(chip))
+ return nand_exec_prog_page_op(chip, page, offset_in_page, buf,
+ len, false);
+
+ chip->legacy.cmdfunc(chip, NAND_CMD_SEQIN, offset_in_page, page);
+
+ if (buf)
+ chip->legacy.write_buf(chip, buf, len);
+
return 0;
}
+EXPORT_SYMBOL_GPL(nand_prog_page_begin_op);
/**
- * nand_read_page_raw_syndrome - [INTERN] read raw page data without ecc
- * @mtd: mtd info structure
- * @chip: nand chip info structure
- * @buf: buffer to store read data
- * @oob_required: caller requires OOB data read to chip->oob_poi
- * @page: page number to read
+ * nand_prog_page_end_op - ends a PROG PAGE operation
+ * @chip: The NAND chip
*
- * We need a special oob layout and handling even when OOB isn't used.
+ * This function issues the second half of a PROG PAGE operation.
+ * This function does not select/unselect the CS line.
+ *
+ * Returns 0 on success, a negative error code otherwise.
*/
-static __maybe_unused int nand_read_page_raw_syndrome(struct nand_chip *chip,
- uint8_t *buf, int oob_required, int page)
+int nand_prog_page_end_op(struct nand_chip *chip)
{
- int eccsize = chip->ecc.size;
- int eccbytes = chip->ecc.bytes;
- uint8_t *oob = chip->oob_poi;
- int steps, size;
-
- for (steps = chip->ecc.steps; steps > 0; steps--) {
- chip->legacy.read_buf(chip, buf, eccsize);
- buf += eccsize;
-
- if (chip->ecc.prepad) {
- chip->legacy.read_buf(chip, oob, chip->ecc.prepad);
- oob += chip->ecc.prepad;
- }
+ int ret;
+ u8 status;
+
+ if (nand_has_exec_op(chip)) {
+ const struct nand_sdr_timings *sdr =
+ nand_get_sdr_timings(nand_get_interface_config(chip));
+ struct nand_op_instr instrs[] = {
+ NAND_OP_CMD(NAND_CMD_PAGEPROG,
+ PSEC_TO_NSEC(sdr->tWB_max)),
+ NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tPROG_max), 0),
+ };
+ struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);
+
+ ret = nand_exec_op(chip, &op);
+ if (ret)
+ return ret;
- chip->legacy.read_buf(chip, oob, eccbytes);
- oob += eccbytes;
+ ret = nand_status_op(chip, &status);
+ if (ret)
+ return ret;
+ } else {
+ chip->legacy.cmdfunc(chip, NAND_CMD_PAGEPROG, -1, -1);
+ ret = chip->legacy.waitfunc(chip);
+ if (ret < 0)
+ return ret;
- if (chip->ecc.postpad) {
- chip->legacy.read_buf(chip, oob, chip->ecc.postpad);
- oob += chip->ecc.postpad;
- }
+ status = ret;
}
- size = chip->mtd.oobsize - (oob - chip->oob_poi);
- if (size)
- chip->legacy.read_buf(chip, oob, size);
+ if (status & NAND_STATUS_FAIL)
+ return -EIO;
return 0;
}
+EXPORT_SYMBOL_GPL(nand_prog_page_end_op);
/**
- * nand_read_page_swecc - [REPLACEABLE] software ECC based page read function
- * @mtd: mtd info structure
- * @chip: nand chip info structure
- * @buf: buffer to store read data
- * @oob_required: caller requires OOB data read to chip->oob_poi
- * @page: page number to read
+ * nand_prog_page_op - Do a full PROG PAGE operation
+ * @chip: The NAND chip
+ * @page: page to write
+ * @offset_in_page: offset within the page
+ * @buf: buffer containing the data to write to the page
+ * @len: length of the buffer
+ *
+ * This function issues a full PROG PAGE operation.
+ * This function does not select/unselect the CS line.
+ *
+ * Returns 0 on success, a negative error code otherwise.
*/
-static __maybe_unused int nand_read_page_swecc(struct nand_chip *chip,
- uint8_t *buf, int oob_required, int page)
+int nand_prog_page_op(struct nand_chip *chip, unsigned int page,
+ unsigned int offset_in_page, const void *buf,
+ unsigned int len)
{
struct mtd_info *mtd = nand_to_mtd(chip);
- int i, eccsize = chip->ecc.size;
- int eccbytes = chip->ecc.bytes;
- int eccsteps = chip->ecc.steps;
- uint8_t *p = buf;
- uint8_t *ecc_calc = chip->buffers->ecccalc;
- uint8_t *ecc_code = chip->buffers->ecccode;
- uint32_t *eccpos = chip->ecc.layout->eccpos;
- unsigned int max_bitflips = 0;
-
- chip->ecc.read_page_raw(chip, buf, 1, page);
+ int status;
- for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize)
- chip->ecc.calculate(chip, p, &ecc_calc[i]);
+ if (!len || !buf)
+ return -EINVAL;
- for (i = 0; i < chip->ecc.total; i++)
- ecc_code[i] = chip->oob_poi[eccpos[i]];
+ if (offset_in_page + len > mtd->writesize + mtd->oobsize)
+ return -EINVAL;
- eccsteps = chip->ecc.steps;
- p = buf;
+ if (nand_has_exec_op(chip)) {
+ status = nand_exec_prog_page_op(chip, page, offset_in_page, buf,
+ len, true);
+ } else {
+ chip->legacy.cmdfunc(chip, NAND_CMD_SEQIN, offset_in_page,
+ page);
+ chip->legacy.write_buf(chip, buf, len);
+ chip->legacy.cmdfunc(chip, NAND_CMD_PAGEPROG, -1, -1);
+ status = chip->legacy.waitfunc(chip);
+ }
- for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
- int stat;
+ if (status & NAND_STATUS_FAIL)
+ return -EIO;
- stat = chip->ecc.correct(chip, p, &ecc_code[i], &ecc_calc[i]);
- if (stat < 0) {
- mtd->ecc_stats.failed++;
- } else {
- mtd->ecc_stats.corrected += stat;
- max_bitflips = max_t(unsigned int, max_bitflips, stat);
- }
- }
- return max_bitflips;
+ return 0;
}
+EXPORT_SYMBOL_GPL(nand_prog_page_op);
/**
- * nand_read_subpage - [REPLACEABLE] ECC based sub-page read function
- * @mtd: mtd info structure
- * @chip: nand chip info structure
- * @data_offs: offset of requested data within the page
- * @readlen: data length
- * @bufpoi: buffer to store read data
+ * nand_change_write_column_op - Do a CHANGE WRITE COLUMN operation
+ * @chip: The NAND chip
+ * @offset_in_page: offset within the page
+ * @buf: buffer containing the data to send to the NAND
+ * @len: length of the buffer
+ * @force_8bit: force 8-bit bus access
+ *
+ * This function issues a CHANGE WRITE COLUMN operation.
+ * This function does not select/unselect the CS line.
+ *
+ * Returns 0 on success, a negative error code otherwise.
*/
-static __maybe_unused int nand_read_subpage(struct nand_chip *chip,
- uint32_t data_offs, uint32_t readlen,
- uint8_t *bufpoi, int page)
+int nand_change_write_column_op(struct nand_chip *chip,
+ unsigned int offset_in_page,
+ const void *buf, unsigned int len,
+ bool force_8bit)
{
struct mtd_info *mtd = nand_to_mtd(chip);
- int start_step, end_step, num_steps;
- uint32_t *eccpos = chip->ecc.layout->eccpos;
- uint8_t *p;
- int data_col_addr, i, gaps = 0;
- int datafrag_len, eccfrag_len, aligned_len, aligned_pos;
- int busw = (chip->options & NAND_BUSWIDTH_16) ? 2 : 1;
- int index = 0;
- unsigned int max_bitflips = 0;
- /*
- * Currently we have no users in barebox, so disable this for now
- */
- return -ENOTSUPP;
+ if (len && !buf)
+ return -EINVAL;
- /* Column address within the page aligned to ECC size (256bytes) */
- start_step = data_offs / chip->ecc.size;
- end_step = (data_offs + readlen - 1) / chip->ecc.size;
- num_steps = end_step - start_step + 1;
+ if (offset_in_page + len > mtd->writesize + mtd->oobsize)
+ return -EINVAL;
- /* Data size aligned to ECC ecc.size */
- datafrag_len = num_steps * chip->ecc.size;
- eccfrag_len = num_steps * chip->ecc.bytes;
+ /* Small page NANDs do not support column change. */
+ if (mtd->writesize <= 512)
+ return -ENOTSUPP;
- data_col_addr = start_step * chip->ecc.size;
- /* If we read not a page aligned data */
- if (data_col_addr != 0)
- chip->legacy.cmdfunc(chip, NAND_CMD_RNDOUT, data_col_addr, -1);
+ if (nand_has_exec_op(chip)) {
+ const struct nand_sdr_timings *sdr =
+ nand_get_sdr_timings(nand_get_interface_config(chip));
+ u8 addrs[2];
+ struct nand_op_instr instrs[] = {
+ NAND_OP_CMD(NAND_CMD_RNDIN, 0),
+ NAND_OP_ADDR(2, addrs, PSEC_TO_NSEC(sdr->tCCS_min)),
+ NAND_OP_DATA_OUT(len, buf, 0),
+ };
+ struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);
+ int ret;
+
+ ret = nand_fill_column_cycles(chip, addrs, offset_in_page);
+ if (ret < 0)
+ return ret;
- p = bufpoi + data_col_addr;
- chip->legacy.read_buf(chip, p, datafrag_len);
+ instrs[2].ctx.data.force_8bit = force_8bit;
- /* Calculate ECC */
- for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size)
- chip->ecc.calculate(chip, p, &chip->buffers->ecccalc[i]);
+ /* Drop the DATA_OUT instruction if len is set to 0. */
+ if (!len)
+ op.ninstrs--;
- /*
- * The performance is faster if we position offsets according to
- * ecc.pos. Let's make sure that there are no gaps in ECC positions.
- */
- for (i = 0; i < eccfrag_len - 1; i++) {
- if (eccpos[i + start_step * chip->ecc.bytes] + 1 !=
- eccpos[i + start_step * chip->ecc.bytes + 1]) {
- gaps = 1;
- break;
- }
+ return nand_exec_op(chip, &op);
}
- if (gaps) {
- chip->legacy.cmdfunc(chip, NAND_CMD_RNDOUT, mtd->writesize, -1);
- chip->legacy.read_buf(chip, chip->oob_poi, mtd->oobsize);
- } else {
- /*
- * Send the command to read the particular ECC bytes take care
- * about buswidth alignment in read_buf.
- */
- index = start_step * chip->ecc.bytes;
- aligned_pos = eccpos[index] & ~(busw - 1);
- aligned_len = eccfrag_len;
- if (eccpos[index] & (busw - 1))
- aligned_len++;
- if (eccpos[index + (num_steps * chip->ecc.bytes)] & (busw - 1))
- aligned_len++;
+ chip->legacy.cmdfunc(chip, NAND_CMD_RNDIN, offset_in_page, -1);
+ if (len)
+ chip->legacy.write_buf(chip, buf, len);
- chip->legacy.cmdfunc(chip, NAND_CMD_RNDOUT,
- mtd->writesize + aligned_pos, -1);
- chip->legacy.read_buf(chip, &chip->oob_poi[aligned_pos], aligned_len);
- }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(nand_change_write_column_op);
- for (i = 0; i < eccfrag_len; i++)
- chip->buffers->ecccode[i] = chip->oob_poi[eccpos[i + index]];
+/**
+ * nand_readid_op - Do a READID operation
+ * @chip: The NAND chip
+ * @addr: address cycle to pass after the READID command
+ * @buf: buffer used to store the ID
+ * @len: length of the buffer
+ *
+ * This function sends a READID command and reads back the ID returned by the
+ * NAND.
+ * This function does not select/unselect the CS line.
+ *
+ * Returns 0 on success, a negative error code otherwise.
+ */
+int nand_readid_op(struct nand_chip *chip, u8 addr, void *buf,
+ unsigned int len)
+{
+ unsigned int i;
+ u8 *id = buf;
- p = bufpoi + data_col_addr;
- for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size) {
- int stat;
+ if (len && !buf)
+ return -EINVAL;
- stat = chip->ecc.correct(chip, p,
- &chip->buffers->ecccode[i], &chip->buffers->ecccalc[i]);
- if (stat < 0) {
- mtd->ecc_stats.failed++;
- } else {
- mtd->ecc_stats.corrected += stat;
- max_bitflips = max_t(unsigned int, max_bitflips, stat);
- }
+ if (nand_has_exec_op(chip)) {
+ const struct nand_sdr_timings *sdr =
+ nand_get_sdr_timings(nand_get_interface_config(chip));
+ struct nand_op_instr instrs[] = {
+ NAND_OP_CMD(NAND_CMD_READID, 0),
+ NAND_OP_ADDR(1, &addr, PSEC_TO_NSEC(sdr->tADL_min)),
+ NAND_OP_8BIT_DATA_IN(len, buf, 0),
+ };
+ struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);
+
+ /* Drop the DATA_IN instruction if len is set to 0. */
+ if (!len)
+ op.ninstrs--;
+
+ return nand_exec_op(chip, &op);
}
- return max_bitflips;
+
+ chip->legacy.cmdfunc(chip, NAND_CMD_READID, addr, -1);
+
+ for (i = 0; i < len; i++)
+ id[i] = chip->legacy.read_byte(chip);
+
+ return 0;
}
+EXPORT_SYMBOL_GPL(nand_readid_op);
/**
- * nand_read_page_hwecc - [REPLACEABLE] hardware ECC based page read function
- * @mtd: mtd info structure
- * @chip: nand chip info structure
- * @buf: buffer to store read data
- * @oob_required: caller requires OOB data read to chip->oob_poi
- * @page: page number to read
+ * nand_status_op - Do a STATUS operation
+ * @chip: The NAND chip
+ * @status: out variable to store the NAND status
*
- * Not for syndrome calculating ECC controllers which need a special oob layout.
+ * This function sends a STATUS command and reads back the status returned by
+ * the NAND.
+ * This function does not select/unselect the CS line.
+ *
+ * Returns 0 on success, a negative error code otherwise.
*/
-static __maybe_unused int nand_read_page_hwecc(struct nand_chip *chip,
- uint8_t *buf, int oob_required, int page)
+int nand_status_op(struct nand_chip *chip, u8 *status)
{
- struct mtd_info *mtd = nand_to_mtd(chip);
- int i, eccsize = chip->ecc.size;
- int eccbytes = chip->ecc.bytes;
- int eccsteps = chip->ecc.steps;
- uint8_t *p = buf;
- uint8_t *ecc_calc = chip->buffers->ecccalc;
- uint8_t *ecc_code = chip->buffers->ecccode;
- uint32_t *eccpos = chip->ecc.layout->eccpos;
- unsigned int max_bitflips = 0;
-
- for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
- chip->ecc.hwctl(chip, NAND_ECC_READ);
- chip->legacy.read_buf(chip, p, eccsize);
- chip->ecc.calculate(chip, p, &ecc_calc[i]);
+ if (nand_has_exec_op(chip)) {
+ const struct nand_sdr_timings *sdr =
+ nand_get_sdr_timings(nand_get_interface_config(chip));
+ struct nand_op_instr instrs[] = {
+ NAND_OP_CMD(NAND_CMD_STATUS,
+ PSEC_TO_NSEC(sdr->tADL_min)),
+ NAND_OP_8BIT_DATA_IN(1, status, 0),
+ };
+ struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);
+
+ if (!status)
+ op.ninstrs--;
+
+ return nand_exec_op(chip, &op);
}
- chip->legacy.read_buf(chip, chip->oob_poi, mtd->oobsize);
- for (i = 0; i < chip->ecc.total; i++)
- ecc_code[i] = chip->oob_poi[eccpos[i]];
+ chip->legacy.cmdfunc(chip, NAND_CMD_STATUS, -1, -1);
+ if (status)
+ *status = chip->legacy.read_byte(chip);
- eccsteps = chip->ecc.steps;
- p = buf;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(nand_status_op);
- for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
- int stat;
+/**
+ * nand_exit_status_op - Exit a STATUS operation
+ * @chip: The NAND chip
+ *
+ * This function sends a READ0 command to cancel the effect of the STATUS
+ * command to avoid reading only the status until a new read command is sent.
+ *
+ * This function does not select/unselect the CS line.
+ *
+ * Returns 0 on success, a negative error code otherwise.
+ */
+int nand_exit_status_op(struct nand_chip *chip)
+{
+ if (nand_has_exec_op(chip)) {
+ struct nand_op_instr instrs[] = {
+ NAND_OP_CMD(NAND_CMD_READ0, 0),
+ };
+ struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);
- stat = chip->ecc.correct(chip, p, &ecc_code[i], &ecc_calc[i]);
- if (stat < 0) {
- mtd->ecc_stats.failed++;
- } else {
- mtd->ecc_stats.corrected += stat;
- max_bitflips = max_t(unsigned int, max_bitflips, stat);
- }
+ return nand_exec_op(chip, &op);
}
- return max_bitflips;
+
+ chip->legacy.cmdfunc(chip, NAND_CMD_READ0, -1, -1);
+
+ return 0;
}
/**
- * nand_read_page_hwecc_oob_first - [REPLACEABLE] hw ecc, read oob first
- * @mtd: mtd info structure
- * @chip: nand chip info structure
- * @buf: buffer to store read data
- * @oob_required: caller requires OOB data read to chip->oob_poi
- * @page: page number to read
+ * nand_erase_op - Do an erase operation
+ * @chip: The NAND chip
+ * @eraseblock: block to erase
+ *
+ * This function sends an ERASE command and waits for the NAND to be ready
+ * before returning.
+ * This function does not select/unselect the CS line.
*
- * Hardware ECC for large page chips, require OOB to be read first. For this
- * ECC mode, the write_page method is re-used from ECC_HW. These methods
- * read/write ECC from the OOB area, unlike the ECC_HW_SYNDROME support with
- * multiple ECC steps, follows the "infix ECC" scheme and reads/writes ECC from
- * the data area, by overwriting the NAND manufacturer bad block markings.
+ * Returns 0 on success, a negative error code otherwise.
*/
-static __maybe_unused int nand_read_page_hwecc_oob_first(struct nand_chip *chip,
- uint8_t *buf, int oob_required, int page)
+int nand_erase_op(struct nand_chip *chip, unsigned int eraseblock)
{
- struct mtd_info *mtd = nand_to_mtd(chip);
- int i, eccsize = chip->ecc.size;
- int eccbytes = chip->ecc.bytes;
- int eccsteps = chip->ecc.steps;
- uint8_t *p = buf;
- uint8_t *ecc_code = chip->buffers->ecccode;
- uint32_t *eccpos = chip->ecc.layout->eccpos;
- uint8_t *ecc_calc = chip->buffers->ecccalc;
- unsigned int max_bitflips = 0;
+ unsigned int page = eraseblock <<
+ (chip->phys_erase_shift - chip->page_shift);
+ int ret;
+ u8 status;
+
+ if (nand_has_exec_op(chip)) {
+ const struct nand_sdr_timings *sdr =
+ nand_get_sdr_timings(nand_get_interface_config(chip));
+ u8 addrs[3] = { page, page >> 8, page >> 16 };
+ struct nand_op_instr instrs[] = {
+ NAND_OP_CMD(NAND_CMD_ERASE1, 0),
+ NAND_OP_ADDR(2, addrs, 0),
+ NAND_OP_CMD(NAND_CMD_ERASE2,
+ PSEC_TO_MSEC(sdr->tWB_max)),
+ NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tBERS_max), 0),
+ };
+ struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);
+
+ if (chip->options & NAND_ROW_ADDR_3)
+ instrs[1].ctx.addr.naddrs++;
+
+ ret = nand_exec_op(chip, &op);
+ if (ret)
+ return ret;
- /* Read the OOB area first */
- chip->legacy.cmdfunc(chip, NAND_CMD_READOOB, 0, page);
- chip->legacy.read_buf(chip, chip->oob_poi, mtd->oobsize);
- chip->legacy.cmdfunc(chip, NAND_CMD_READ0, 0, page);
+ ret = nand_status_op(chip, &status);
+ if (ret)
+ return ret;
+ } else {
+ chip->legacy.cmdfunc(chip, NAND_CMD_ERASE1, -1, page);
+ chip->legacy.cmdfunc(chip, NAND_CMD_ERASE2, -1, -1);
- for (i = 0; i < chip->ecc.total; i++)
- ecc_code[i] = chip->oob_poi[eccpos[i]];
+ ret = chip->legacy.waitfunc(chip);
+ if (ret < 0)
+ return ret;
- for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
- int stat;
+ status = ret;
+ }
- chip->ecc.hwctl(chip, NAND_ECC_READ);
- chip->legacy.read_buf(chip, p, eccsize);
- chip->ecc.calculate(chip, p, &ecc_calc[i]);
+ if (status & NAND_STATUS_FAIL)
+ return -EIO;
- stat = chip->ecc.correct(chip, p, &ecc_code[i], NULL);
- if (stat < 0) {
- mtd->ecc_stats.failed++;
- } else {
- mtd->ecc_stats.corrected += stat;
- max_bitflips = max_t(unsigned int, max_bitflips, stat);
- }
- }
- return max_bitflips;
+ return 0;
}
+EXPORT_SYMBOL_GPL(nand_erase_op);
/**
- * nand_read_page_syndrome - [REPLACEABLE] hardware ECC syndrome based page read
- * @mtd: mtd info structure
- * @chip: nand chip info structure
- * @buf: buffer to store read data
- * @oob_required: caller requires OOB data read to chip->oob_poi
- * @page: page number to read
+ * nand_set_features_op - Do a SET FEATURES operation
+ * @chip: The NAND chip
+ * @feature: feature id
+ * @data: 4 bytes of data
*
- * The hw generator calculates the error syndrome automatically. Therefore we
- * need a special oob layout and handling.
+ * This function sends a SET FEATURES command and waits for the NAND to be
+ * ready before returning.
+ * This function does not select/unselect the CS line.
+ *
+ * Returns 0 on success, a negative error code otherwise.
*/
-static __maybe_unused int nand_read_page_syndrome(struct nand_chip *chip,
- uint8_t *buf, int oob_required, int page)
+static int nand_set_features_op(struct nand_chip *chip, u8 feature,
+ const void *data)
{
- struct mtd_info *mtd = nand_to_mtd(chip);
- int i, eccsize = chip->ecc.size;
- int eccbytes = chip->ecc.bytes;
- int eccsteps = chip->ecc.steps;
- uint8_t *p = buf;
- uint8_t *oob = chip->oob_poi;
- unsigned int max_bitflips = 0;
+ const u8 *params = data;
+ int i, ret;
+
+ if (nand_has_exec_op(chip)) {
+ const struct nand_sdr_timings *sdr =
+ nand_get_sdr_timings(nand_get_interface_config(chip));
+ struct nand_op_instr instrs[] = {
+ NAND_OP_CMD(NAND_CMD_SET_FEATURES, 0),
+ NAND_OP_ADDR(1, &feature, PSEC_TO_NSEC(sdr->tADL_min)),
+ NAND_OP_8BIT_DATA_OUT(ONFI_SUBFEATURE_PARAM_LEN, data,
+ PSEC_TO_NSEC(sdr->tWB_max)),
+ NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tFEAT_max), 0),
+ };
+ struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);
+
+ return nand_exec_op(chip, &op);
+ }
- for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
- int stat;
+ chip->legacy.cmdfunc(chip, NAND_CMD_SET_FEATURES, feature, -1);
+ for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i)
+ chip->legacy.write_byte(chip, params[i]);
- chip->ecc.hwctl(chip, NAND_ECC_READ);
- chip->legacy.read_buf(chip, p, eccsize);
+ ret = chip->legacy.waitfunc(chip);
+ if (ret < 0)
+ return ret;
- if (chip->ecc.prepad) {
- chip->legacy.read_buf(chip, oob, chip->ecc.prepad);
- oob += chip->ecc.prepad;
- }
+ if (ret & NAND_STATUS_FAIL)
+ return -EIO;
- chip->ecc.hwctl(chip, NAND_ECC_READSYN);
- chip->legacy.read_buf(chip, oob, eccbytes);
- stat = chip->ecc.correct(chip, p, oob, NULL);
+ return 0;
+}
- if (stat < 0) {
- mtd->ecc_stats.failed++;
- } else {
- mtd->ecc_stats.corrected += stat;
- max_bitflips = max_t(unsigned int, max_bitflips, stat);
- }
+/**
+ * nand_get_features_op - Do a GET FEATURES operation
+ * @chip: The NAND chip
+ * @feature: feature id
+ * @data: 4 bytes of data
+ *
+ * This function sends a GET FEATURES command and waits for the NAND to be
+ * ready before returning.
+ * This function does not select/unselect the CS line.
+ *
+ * Returns 0 on success, a negative error code otherwise.
+ */
+static int nand_get_features_op(struct nand_chip *chip, u8 feature,
+ void *data)
+{
+ u8 *params = data;
+ int i;
- oob += eccbytes;
+ if (nand_has_exec_op(chip)) {
+ const struct nand_sdr_timings *sdr =
+ nand_get_sdr_timings(nand_get_interface_config(chip));
+ struct nand_op_instr instrs[] = {
+ NAND_OP_CMD(NAND_CMD_GET_FEATURES, 0),
+ NAND_OP_ADDR(1, &feature, PSEC_TO_NSEC(sdr->tWB_max)),
+ NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tFEAT_max),
+ PSEC_TO_NSEC(sdr->tRR_min)),
+ NAND_OP_8BIT_DATA_IN(ONFI_SUBFEATURE_PARAM_LEN,
+ data, 0),
+ };
+ struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);
+
+ return nand_exec_op(chip, &op);
+ }
- if (chip->ecc.postpad) {
- chip->legacy.read_buf(chip, oob, chip->ecc.postpad);
- oob += chip->ecc.postpad;
- }
+ chip->legacy.cmdfunc(chip, NAND_CMD_GET_FEATURES, feature, -1);
+ for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i)
+ params[i] = chip->legacy.read_byte(chip);
+
+ return 0;
+}
+
+static int nand_wait_rdy_op(struct nand_chip *chip, unsigned int timeout_ms,
+ unsigned int delay_ns)
+{
+ if (nand_has_exec_op(chip)) {
+ struct nand_op_instr instrs[] = {
+ NAND_OP_WAIT_RDY(PSEC_TO_MSEC(timeout_ms),
+ PSEC_TO_NSEC(delay_ns)),
+ };
+ struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);
+
+ return nand_exec_op(chip, &op);
}
- /* Calculate remaining oob bytes */
- i = mtd->oobsize - (oob - chip->oob_poi);
- if (i)
- chip->legacy.read_buf(chip, oob, i);
+ /* Apply delay or wait for ready/busy pin */
+ if (!chip->legacy.dev_ready)
+ udelay(chip->legacy.chip_delay);
+ else
+ nand_wait_ready(chip);
- return max_bitflips;
+ return 0;
}
/**
- * nand_transfer_oob - [INTERN] Transfer oob to client buffer
- * @chip: nand chip structure
- * @oob: oob destination address
- * @ops: oob ops structure
- * @len: size of oob to transfer
+ * nand_reset_op - Do a reset operation
+ * @chip: The NAND chip
+ *
+ * This function sends a RESET command and waits for the NAND to be ready
+ * before returning.
+ * This function does not select/unselect the CS line.
+ *
+ * Returns 0 on success, a negative error code otherwise.
*/
-static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob,
- struct mtd_oob_ops *ops, size_t len)
+int nand_reset_op(struct nand_chip *chip)
{
- switch (ops->mode) {
+ if (nand_has_exec_op(chip)) {
+ const struct nand_sdr_timings *sdr =
+ nand_get_sdr_timings(nand_get_interface_config(chip));
+ struct nand_op_instr instrs[] = {
+ NAND_OP_CMD(NAND_CMD_RESET, PSEC_TO_NSEC(sdr->tWB_max)),
+ NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tRST_max), 0),
+ };
+ struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);
+
+ return nand_exec_op(chip, &op);
+ }
- case MTD_OPS_PLACE_OOB:
- case MTD_OPS_RAW:
- memcpy(oob, chip->oob_poi + ops->ooboffs, len);
- return oob + len;
+ chip->legacy.cmdfunc(chip, NAND_CMD_RESET, -1, -1);
- case MTD_OPS_AUTO_OOB: {
- struct nand_oobfree *free = chip->ecc.layout->oobfree;
- uint32_t boffs = 0, roffs = ops->ooboffs;
- size_t bytes = 0;
-
- for (; free->length && len; free++, len -= bytes) {
- /* Read request not from offset 0? */
- if (unlikely(roffs)) {
- if (roffs >= free->length) {
- roffs -= free->length;
- continue;
- }
- boffs = free->offset + roffs;
- bytes = min_t(size_t, len,
- (free->length - roffs));
- roffs = 0;
- } else {
- bytes = min_t(size_t, len, free->length);
- boffs = free->offset;
- }
- memcpy(oob, chip->oob_poi + boffs, bytes);
- oob += bytes;
- }
- return oob;
- }
- default:
- BUG();
- }
- return NULL;
+ return 0;
}
+EXPORT_SYMBOL_GPL(nand_reset_op);
/**
- * nand_do_read_ops - [INTERN] Read data with ECC
- * @mtd: MTD device structure
- * @from: offset to read from
- * @ops: oob ops structure
+ * nand_read_data_op - Read data from the NAND
+ * @chip: The NAND chip
+ * @buf: buffer used to store the data
+ * @len: length of the buffer
+ * @force_8bit: force 8-bit bus access
+ * @check_only: do not actually run the command, only checks if the
+ * controller driver supports it
*
- * Internal function. Called with chip held.
+ * This function does a raw data read on the bus. Usually used after launching
+ * another NAND operation like nand_read_page_op().
+ * This function does not select/unselect the CS line.
+ *
+ * Returns 0 on success, a negative error code otherwise.
*/
-static int nand_do_read_ops(struct nand_chip *chip, loff_t from,
- struct mtd_oob_ops *ops)
+int nand_read_data_op(struct nand_chip *chip, void *buf, unsigned int len,
+ bool force_8bit, bool check_only)
{
- int chipnr, page, realpage, col, bytes, aligned, oob_required;
- struct mtd_info *mtd = nand_to_mtd(chip);
- struct mtd_ecc_stats stats;
- int ret = 0;
- uint32_t readlen = ops->len;
- uint32_t oobreadlen = ops->ooblen;
- uint32_t max_oobsize = ops->mode == MTD_OPS_AUTO_OOB ?
- chip->mtd.oobavail : chip->mtd.oobsize;
-
- uint8_t *bufpoi, *oob, *buf;
- unsigned int max_bitflips = 0;
+ if (!len || !buf)
+ return -EINVAL;
- stats = mtd->ecc_stats;
+ if (nand_has_exec_op(chip)) {
+ struct nand_op_instr instrs[] = {
+ NAND_OP_DATA_IN(len, buf, 0),
+ };
+ struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);
- chipnr = (int)(from >> chip->chip_shift);
- chip->legacy.select_chip(chip, chipnr);
+ instrs[0].ctx.data.force_8bit = force_8bit;
- realpage = (int)(from >> chip->page_shift);
- page = realpage & chip->pagemask;
+ if (check_only)
+ return nand_check_op(chip, &op);
- col = (int)(from & (mtd->writesize - 1));
+ return nand_exec_op(chip, &op);
+ }
- buf = ops->datbuf;
- oob = ops->oobbuf;
- oob_required = oob ? 1 : 0;
+ if (check_only)
+ return 0;
- while (1) {
- bytes = min(mtd->writesize - col, readlen);
- aligned = (bytes == mtd->writesize);
+ if (force_8bit) {
+ u8 *p = buf;
+ unsigned int i;
- /* Is the current page in the buffer? */
- if (realpage != chip->pagebuf || oob) {
- bufpoi = aligned ? buf : chip->buffers->databuf;
+ for (i = 0; i < len; i++)
+ p[i] = chip->legacy.read_byte(chip);
+ } else {
+ chip->legacy.read_buf(chip, buf, len);
+ }
- chip->legacy.cmdfunc(chip, NAND_CMD_READ0, 0x00, page);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(nand_read_data_op);
- /*
- * Now read the page into the buffer. Absent an error,
- * the read methods return max bitflips per ecc step.
- */
- if (unlikely(ops->mode == MTD_OPS_RAW))
- ret = chip->ecc.read_page_raw(chip, bufpoi,
- oob_required,
- page);
- else if (!aligned && NAND_HAS_SUBPAGE_READ(chip) &&
- !oob)
- ret = chip->ecc.read_subpage(chip,
- col, bytes, bufpoi, page);
- else
- ret = chip->ecc.read_page(chip, bufpoi,
- oob_required, page);
- if (ret < 0) {
- if (!aligned)
- /* Invalidate page cache */
- chip->pagebuf = -1;
- break;
- }
+/**
+ * nand_write_data_op - Write data from the NAND
+ * @chip: The NAND chip
+ * @buf: buffer containing the data to send on the bus
+ * @len: length of the buffer
+ * @force_8bit: force 8-bit bus access
+ *
+ * This function does a raw data write on the bus. Usually used after launching
+ * another NAND operation like nand_write_page_begin_op().
+ * This function does not select/unselect the CS line.
+ *
+ * Returns 0 on success, a negative error code otherwise.
+ */
+int nand_write_data_op(struct nand_chip *chip, const void *buf,
+ unsigned int len, bool force_8bit)
+{
+ if (!len || !buf)
+ return -EINVAL;
- max_bitflips = max_t(unsigned int, max_bitflips, ret);
+ if (nand_has_exec_op(chip)) {
+ struct nand_op_instr instrs[] = {
+ NAND_OP_DATA_OUT(len, buf, 0),
+ };
+ struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);
- /* Transfer not aligned data */
- if (!aligned) {
- if (!NAND_HAS_SUBPAGE_READ(chip) && !oob &&
- !(mtd->ecc_stats.failed - stats.failed) &&
- (ops->mode != MTD_OPS_RAW)) {
- chip->pagebuf = realpage;
- chip->pagebuf_bitflips = ret;
- } else {
- /* Invalidate page cache */
- chip->pagebuf = -1;
- }
- memcpy(buf, chip->buffers->databuf + col, bytes);
- }
+ instrs[0].ctx.data.force_8bit = force_8bit;
- buf += bytes;
+ return nand_exec_op(chip, &op);
+ }
- if (unlikely(oob)) {
- int toread = min(oobreadlen, max_oobsize);
+ if (force_8bit) {
+ const u8 *p = buf;
+ unsigned int i;
- if (toread) {
- oob = nand_transfer_oob(chip,
- oob, ops, toread);
- oobreadlen -= toread;
- }
- }
-
- if (chip->options & NAND_NEED_READRDY) {
- /* Apply delay or wait for ready/busy pin */
- if (!chip->legacy.dev_ready)
- udelay(chip->legacy.chip_delay);
- else
- nand_wait_ready(chip);
- }
- } else {
- memcpy(buf, chip->buffers->databuf + col, bytes);
- buf += bytes;
- max_bitflips = max_t(unsigned int, max_bitflips,
- chip->pagebuf_bitflips);
- }
-
- readlen -= bytes;
-
- if (!readlen)
- break;
-
- /* For subsequent reads align to page boundary */
- col = 0;
- /* Increment page address */
- realpage++;
-
- page = realpage & chip->pagemask;
- /* Check, if we cross a chip boundary */
- if (!page) {
- chipnr++;
- chip->legacy.select_chip(chip, -1);
- chip->legacy.select_chip(chip, chipnr);
- }
+ for (i = 0; i < len; i++)
+ chip->legacy.write_byte(chip, p[i]);
+ } else {
+ chip->legacy.write_buf(chip, buf, len);
}
- chip->legacy.select_chip(chip, -1);
-
- ops->retlen = ops->len - (size_t) readlen;
- if (oob)
- ops->oobretlen = ops->ooblen - oobreadlen;
-
- if (ret < 0)
- return ret;
-
- if (mtd->ecc_stats.failed - stats.failed)
- return -EBADMSG;
- return max_bitflips;
+ return 0;
}
+EXPORT_SYMBOL_GPL(nand_write_data_op);
/**
- * nand_read - [MTD Interface] MTD compatibility function for nand_do_read_ecc
- * @mtd: MTD device structure
- * @from: offset to read from
- * @len: number of bytes to read
- * @retlen: pointer to variable to store the number of read bytes
- * @buf: the databuffer to put data
+ * struct nand_op_parser_ctx - Context used by the parser
+ * @instrs: array of all the instructions that must be addressed
+ * @ninstrs: length of the @instrs array
+ * @subop: Sub-operation to be passed to the NAND controller
*
- * Get hold of the chip and call nand_do_read.
+ * This structure is used by the core to split NAND operations into
+ * sub-operations that can be handled by the NAND controller.
*/
-static int nand_read(struct mtd_info *mtd, loff_t from, size_t len,
- size_t *retlen, uint8_t *buf)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct mtd_oob_ops ops;
- int ret;
-
- nand_get_device(chip, FL_READING);
- ops.len = len;
- ops.datbuf = buf;
- ops.ooblen = 0;
- ops.oobbuf = NULL;
- ops.mode = MTD_OPS_PLACE_OOB;
- ret = nand_do_read_ops(chip, from, &ops);
- *retlen = ops.retlen;
- nand_release_device(chip);
- return ret;
-}
+struct nand_op_parser_ctx {
+ const struct nand_op_instr *instrs;
+ unsigned int ninstrs;
+ struct nand_subop subop;
+};
/**
- * nand_read_oob_std - [REPLACEABLE] the most common OOB data read function
- * @mtd: mtd info structure
- * @chip: nand chip info structure
- * @page: page number to read
+ * nand_op_parser_must_split_instr - Checks if an instruction must be split
+ * @pat: the parser pattern element that matches @instr
+ * @instr: pointer to the instruction to check
+ * @start_offset: this is an in/out parameter. If @instr has already been
+ * split, then @start_offset is the offset from which to start
+ * (either an address cycle or an offset in the data buffer).
+ * Conversely, if the function returns true (ie. instr must be
+ * split), this parameter is updated to point to the first
+ * data/address cycle that has not been taken care of.
+ *
+ * Some NAND controllers are limited and cannot send X address cycles with a
+ * unique operation, or cannot read/write more than Y bytes at the same time.
+ * In this case, split the instruction that does not fit in a single
+ * controller-operation into two or more chunks.
+ *
+ * Returns true if the instruction must be split, false otherwise.
+ * The @start_offset parameter is also updated to the offset at which the next
+ * bundle of instruction must start (if an address or a data instruction).
*/
-static __maybe_unused int nand_read_oob_std(struct nand_chip *chip, int page)
+static bool
+nand_op_parser_must_split_instr(const struct nand_op_parser_pattern_elem *pat,
+ const struct nand_op_instr *instr,
+ unsigned int *start_offset)
{
- if (!IS_ENABLED(CONFIG_NAND_READ_OOB))
- return -ENOTSUPP;
+ switch (pat->type) {
+ case NAND_OP_ADDR_INSTR:
+ if (!pat->ctx.addr.maxcycles)
+ break;
- chip->legacy.cmdfunc(chip, NAND_CMD_READOOB, 0, page);
- chip->legacy.read_buf(chip, chip->oob_poi, chip->mtd.oobsize);
- return 0;
-}
+ if (instr->ctx.addr.naddrs - *start_offset >
+ pat->ctx.addr.maxcycles) {
+ *start_offset += pat->ctx.addr.maxcycles;
+ return true;
+ }
+ break;
-/**
- * nand_read_oob_syndrome - [REPLACEABLE] OOB data read function for HW ECC
- * with syndromes
- * @mtd: mtd info structure
- * @chip: nand chip info structure
- * @page: page number to read
- */
-static __maybe_unused int nand_read_oob_syndrome(struct nand_chip *chip,
- int page)
-{
- uint8_t *buf = chip->oob_poi;
- int length = chip->mtd.oobsize;
- int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad;
- int eccsize = chip->ecc.size;
- uint8_t *bufpoi = buf;
- int i, toread, sndrnd = 0, pos;
+ case NAND_OP_DATA_IN_INSTR:
+ case NAND_OP_DATA_OUT_INSTR:
+ if (!pat->ctx.data.maxlen)
+ break;
- if (!IS_ENABLED(CONFIG_NAND_READ_OOB))
- return -ENOTSUPP;
+ if (instr->ctx.data.len - *start_offset >
+ pat->ctx.data.maxlen) {
+ *start_offset += pat->ctx.data.maxlen;
+ return true;
+ }
+ break;
- chip->legacy.cmdfunc(chip, NAND_CMD_READ0, chip->ecc.size, page);
- for (i = 0; i < chip->ecc.steps; i++) {
- if (sndrnd) {
- pos = eccsize + i * (eccsize + chunk);
- if (chip->mtd.writesize > 512)
- chip->legacy.cmdfunc(chip, NAND_CMD_RNDOUT, pos, -1);
- else
- chip->legacy.cmdfunc(chip, NAND_CMD_READ0, pos, page);
- } else
- sndrnd = 1;
- toread = min_t(int, length, chunk);
- chip->legacy.read_buf(chip, bufpoi, toread);
- bufpoi += toread;
- length -= toread;
+ default:
+ break;
}
- if (length > 0)
- chip->legacy.read_buf(chip, bufpoi, length);
- return 0;
+ return false;
}
/**
- * nand_write_oob_std - [REPLACEABLE] the most common OOB data write function
- * @mtd: mtd info structure
- * @chip: nand chip info structure
- * @page: page number to write
+ * nand_op_parser_match_pat - Checks if a pattern matches the instructions
+ * remaining in the parser context
+ * @pat: the pattern to test
+ * @ctx: the parser context structure to match with the pattern @pat
+ *
+ * Check if @pat matches the set or a sub-set of instructions remaining in @ctx.
+ * Returns true if this is the case, false ortherwise. When true is returned,
+ * @ctx->subop is updated with the set of instructions to be passed to the
+ * controller driver.
*/
-static __maybe_unused int nand_write_oob_std(struct nand_chip *chip, int page)
+static bool
+nand_op_parser_match_pat(const struct nand_op_parser_pattern *pat,
+ struct nand_op_parser_ctx *ctx)
{
- int status = 0;
- const uint8_t *buf = chip->oob_poi;
- int length = chip->mtd.oobsize;
+ unsigned int instr_offset = ctx->subop.first_instr_start_off;
+ const struct nand_op_instr *end = ctx->instrs + ctx->ninstrs;
+ const struct nand_op_instr *instr = ctx->subop.instrs;
+ unsigned int i, ninstrs;
- if (!IS_ENABLED(CONFIG_NAND_READ_OOB) || !IS_ENABLED(CONFIG_MTD_WRITE))
- return -ENOTSUPP;
-
- chip->legacy.cmdfunc(chip, NAND_CMD_SEQIN, chip->mtd.writesize, page);
- chip->legacy.write_buf(chip, buf, length);
- /* Send command to program the OOB data */
- chip->legacy.cmdfunc(chip, NAND_CMD_PAGEPROG, -1, -1);
-
- status = chip->legacy.waitfunc(chip);
+ for (i = 0, ninstrs = 0; i < pat->nelems && instr < end; i++) {
+ /*
+ * The pattern instruction does not match the operation
+ * instruction. If the instruction is marked optional in the
+ * pattern definition, we skip the pattern element and continue
+ * to the next one. If the element is mandatory, there's no
+ * match and we can return false directly.
+ */
+ if (instr->type != pat->elems[i].type) {
+ if (!pat->elems[i].optional)
+ return false;
- return status & NAND_STATUS_FAIL ? -EIO : 0;
-}
+ continue;
+ }
-/**
- * nand_write_oob_syndrome - [REPLACEABLE] OOB data write function for HW ECC
- * with syndrome - only for large page flash
- * @mtd: mtd info structure
- * @chip: nand chip info structure
- * @page: page number to write
- */
-static __maybe_unused int nand_write_oob_syndrome(struct nand_chip *chip, int page)
-{
- int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad;
- int eccsize = chip->ecc.size, length = chip->mtd.oobsize;
- int i, len, pos, status = 0, sndcmd = 0, steps = chip->ecc.steps;
- const uint8_t *bufpoi = chip->oob_poi;
+ /*
+ * Now check the pattern element constraints. If the pattern is
+ * not able to handle the whole instruction in a single step,
+ * we have to split it.
+ * The last_instr_end_off value comes back updated to point to
+ * the position where we have to split the instruction (the
+ * start of the next subop chunk).
+ */
+ if (nand_op_parser_must_split_instr(&pat->elems[i], instr,
+ &instr_offset)) {
+ ninstrs++;
+ i++;
+ break;
+ }
- if (!IS_ENABLED(CONFIG_NAND_READ_OOB) || !IS_ENABLED(CONFIG_MTD_WRITE))
- return -ENOTSUPP;
+ instr++;
+ ninstrs++;
+ instr_offset = 0;
+ }
/*
- * data-ecc-data-ecc ... ecc-oob
- * or
- * data-pad-ecc-pad-data-pad .... ecc-pad-oob
+ * This can happen if all instructions of a pattern are optional.
+ * Still, if there's not at least one instruction handled by this
+ * pattern, this is not a match, and we should try the next one (if
+ * any).
*/
- if (!chip->ecc.prepad && !chip->ecc.postpad) {
- pos = steps * (eccsize + chunk);
- steps = 0;
- } else
- pos = eccsize;
-
- chip->legacy.cmdfunc(chip, NAND_CMD_SEQIN, pos, page);
- for (i = 0; i < steps; i++) {
- if (sndcmd) {
- if (chip->mtd.writesize <= 512) {
- uint32_t fill = 0xFFFFFFFF;
+ if (!ninstrs)
+ return false;
- len = eccsize;
- while (len > 0) {
- int num = min_t(int, len, 4);
- chip->legacy.write_buf(chip, (uint8_t *)&fill,
- num);
- len -= num;
- }
- } else {
- pos = eccsize + i * (eccsize + chunk);
- chip->legacy.cmdfunc(chip, NAND_CMD_RNDIN, pos, -1);
- }
- } else
- sndcmd = 1;
- len = min_t(int, length, chunk);
- chip->legacy.write_buf(chip, bufpoi, len);
- bufpoi += len;
- length -= len;
+ /*
+ * We had a match on the pattern head, but the pattern may be longer
+ * than the instructions we're asked to execute. We need to make sure
+ * there's no mandatory elements in the pattern tail.
+ */
+ for (; i < pat->nelems; i++) {
+ if (!pat->elems[i].optional)
+ return false;
}
- if (length > 0)
- chip->legacy.write_buf(chip, bufpoi, length);
- chip->legacy.cmdfunc(chip, NAND_CMD_PAGEPROG, -1, -1);
- status = chip->legacy.waitfunc(chip);
+ /*
+ * We have a match: update the subop structure accordingly and return
+ * true.
+ */
+ ctx->subop.ninstrs = ninstrs;
+ ctx->subop.last_instr_end_off = instr_offset;
- return status & NAND_STATUS_FAIL ? -EIO : 0;
+ return true;
}
-/**
- * nand_do_read_oob - [INTERN] NAND read out-of-band
- * @mtd: MTD device structure
- * @from: offset to read from
- * @ops: oob operations description structure
- *
- * NAND read out-of-band data from the spare area.
- */
-static int nand_do_read_oob(struct nand_chip *chip, loff_t from,
- struct mtd_oob_ops *ops)
+#if IS_ENABLED(CONFIG_DYNAMIC_DEBUG) || defined(DEBUG)
+static void nand_op_parser_trace(const struct nand_op_parser_ctx *ctx)
{
- struct mtd_info *mtd = nand_to_mtd(chip);
- int page, realpage, chipnr;
- struct mtd_ecc_stats stats;
- int readlen = ops->ooblen;
- int len;
- uint8_t *buf = ops->oobbuf;
- int ret = 0;
+ const struct nand_op_instr *instr;
+ char *prefix = " ";
+ unsigned int i;
- if (!IS_ENABLED(CONFIG_NAND_READ_OOB))
- return -ENOTSUPP;
+ pr_debug("executing subop (CS%d):\n", ctx->subop.cs);
- pr_debug("%s: from = 0x%08Lx, len = %i\n",
- __func__, (unsigned long long)from, readlen);
+ for (i = 0; i < ctx->ninstrs; i++) {
+ instr = &ctx->instrs[i];
- stats = mtd->ecc_stats;
+ if (instr == &ctx->subop.instrs[0])
+ prefix = " ->";
- if (ops->mode == MTD_OPS_AUTO_OOB)
- len = chip->ecc.layout->oobavail;
- else
- len = mtd->oobsize;
+ nand_op_trace(prefix, instr);
- if (unlikely(ops->ooboffs >= len)) {
- pr_debug("%s: attempt to start read outside oob\n",
- __func__);
- return -EINVAL;
+ if (instr == &ctx->subop.instrs[ctx->subop.ninstrs - 1])
+ prefix = " ";
}
+}
+#else
+static void nand_op_parser_trace(const struct nand_op_parser_ctx *ctx)
+{
+ /* NOP */
+}
+#endif
- /* Do not allow reads past end of device */
- if (unlikely(from >= mtd->size ||
- ops->ooboffs + readlen > ((mtd->size >> chip->page_shift) -
- (from >> chip->page_shift)) * len)) {
- pr_debug("%s: attempt to read beyond end of device\n",
- __func__);
- return -EINVAL;
- }
+static int nand_op_parser_cmp_ctx(const struct nand_op_parser_ctx *a,
+ const struct nand_op_parser_ctx *b)
+{
+ if (a->subop.ninstrs < b->subop.ninstrs)
+ return -1;
+ else if (a->subop.ninstrs > b->subop.ninstrs)
+ return 1;
- chipnr = (int)(from >> chip->chip_shift);
- chip->legacy.select_chip(chip, chipnr);
+ if (a->subop.last_instr_end_off < b->subop.last_instr_end_off)
+ return -1;
+ else if (a->subop.last_instr_end_off > b->subop.last_instr_end_off)
+ return 1;
- /* Shift to get page */
- realpage = (int)(from >> chip->page_shift);
- page = realpage & chip->pagemask;
+ return 0;
+}
- while (1) {
- if (ops->mode == MTD_OPS_RAW)
- ret = chip->ecc.read_oob_raw(chip, page);
- else
- ret = chip->ecc.read_oob(chip, page);
+/**
+ * nand_op_parser_exec_op - exec_op parser
+ * @chip: the NAND chip
+ * @parser: patterns description provided by the controller driver
+ * @op: the NAND operation to address
+ * @check_only: when true, the function only checks if @op can be handled but
+ * does not execute the operation
+ *
+ * Helper function designed to ease integration of NAND controller drivers that
+ * only support a limited set of instruction sequences. The supported sequences
+ * are described in @parser, and the framework takes care of splitting @op into
+ * multiple sub-operations (if required) and pass them back to the ->exec()
+ * callback of the matching pattern if @check_only is set to false.
+ *
+ * NAND controller drivers should call this function from their own ->exec_op()
+ * implementation.
+ *
+ * Returns 0 on success, a negative error code otherwise. A failure can be
+ * caused by an unsupported operation (none of the supported patterns is able
+ * to handle the requested operation), or an error returned by one of the
+ * matching pattern->exec() hook.
+ */
+int nand_op_parser_exec_op(struct nand_chip *chip,
+ const struct nand_op_parser *parser,
+ const struct nand_operation *op, bool check_only)
+{
+ struct nand_op_parser_ctx ctx = {
+ .subop.cs = op->cs,
+ .subop.instrs = op->instrs,
+ .instrs = op->instrs,
+ .ninstrs = op->ninstrs,
+ };
+ unsigned int i;
+
+ while (ctx.subop.instrs < op->instrs + op->ninstrs) {
+ const struct nand_op_parser_pattern *pattern;
+ struct nand_op_parser_ctx best_ctx;
+ int ret, best_pattern = -1;
+
+ for (i = 0; i < parser->npatterns; i++) {
+ struct nand_op_parser_ctx test_ctx = ctx;
+
+ pattern = &parser->patterns[i];
+ if (!nand_op_parser_match_pat(pattern, &test_ctx))
+ continue;
+
+ if (best_pattern >= 0 &&
+ nand_op_parser_cmp_ctx(&test_ctx, &best_ctx) <= 0)
+ continue;
+
+ best_pattern = i;
+ best_ctx = test_ctx;
+ }
- if (ret < 0)
- break;
+ if (best_pattern < 0) {
+ pr_debug("->exec_op() parser: pattern not found!\n");
+ return -ENOTSUPP;
+ }
- len = min(len, readlen);
- buf = nand_transfer_oob(chip, buf, ops, len);
+ ctx = best_ctx;
+ nand_op_parser_trace(&ctx);
- if (chip->options & NAND_NEED_READRDY) {
- /* Apply delay or wait for ready/busy pin */
- if (!chip->legacy.dev_ready)
- udelay(chip->legacy.chip_delay);
- else
- nand_wait_ready(chip);
+ if (!check_only) {
+ pattern = &parser->patterns[best_pattern];
+ ret = pattern->exec(chip, &ctx.subop);
+ if (ret)
+ return ret;
}
- readlen -= len;
- if (!readlen)
- break;
-
- /* Increment page address */
- realpage++;
+ /*
+ * Update the context structure by pointing to the start of the
+ * next subop.
+ */
+ ctx.subop.instrs = ctx.subop.instrs + ctx.subop.ninstrs;
+ if (ctx.subop.last_instr_end_off)
+ ctx.subop.instrs -= 1;
- page = realpage & chip->pagemask;
- /* Check, if we cross a chip boundary */
- if (!page) {
- chipnr++;
- chip->legacy.select_chip(chip, -1);
- chip->legacy.select_chip(chip, chipnr);
- }
+ ctx.subop.first_instr_start_off = ctx.subop.last_instr_end_off;
}
- chip->legacy.select_chip(chip, -1);
- ops->oobretlen = ops->ooblen - readlen;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(nand_op_parser_exec_op);
- if (ret < 0)
- return ret;
+static bool nand_instr_is_data(const struct nand_op_instr *instr)
+{
+ return instr && (instr->type == NAND_OP_DATA_IN_INSTR ||
+ instr->type == NAND_OP_DATA_OUT_INSTR);
+}
- if (mtd->ecc_stats.failed - stats.failed)
- return -EBADMSG;
+static bool nand_subop_instr_is_valid(const struct nand_subop *subop,
+ unsigned int instr_idx)
+{
+ return subop && instr_idx < subop->ninstrs;
+}
- return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0;
+static unsigned int nand_subop_get_start_off(const struct nand_subop *subop,
+ unsigned int instr_idx)
+{
+ if (instr_idx)
+ return 0;
+
+ return subop->first_instr_start_off;
}
/**
- * nand_read_oob - [MTD Interface] NAND read data and/or out-of-band
- * @mtd: MTD device structure
- * @from: offset to read from
- * @ops: oob operation description structure
+ * nand_subop_get_addr_start_off - Get the start offset in an address array
+ * @subop: The entire sub-operation
+ * @instr_idx: Index of the instruction inside the sub-operation
*
- * NAND read data and/or out-of-band data.
+ * During driver development, one could be tempted to directly use the
+ * ->addr.addrs field of address instructions. This is wrong as address
+ * instructions might be split.
+ *
+ * Given an address instruction, returns the offset of the first cycle to issue.
*/
-static int nand_read_oob(struct mtd_info *mtd, loff_t from,
- struct mtd_oob_ops *ops)
+unsigned int nand_subop_get_addr_start_off(const struct nand_subop *subop,
+ unsigned int instr_idx)
{
- struct nand_chip *chip = mtd_to_nand(mtd);
- int ret = -ENOTSUPP;
+ if (WARN_ON(!nand_subop_instr_is_valid(subop, instr_idx) ||
+ subop->instrs[instr_idx].type != NAND_OP_ADDR_INSTR))
+ return 0;
- if (!IS_ENABLED(CONFIG_NAND_READ_OOB))
- return -ENOTSUPP;
+ return nand_subop_get_start_off(subop, instr_idx);
+}
+EXPORT_SYMBOL_GPL(nand_subop_get_addr_start_off);
- ops->retlen = 0;
+/**
+ * nand_subop_get_num_addr_cyc - Get the remaining address cycles to assert
+ * @subop: The entire sub-operation
+ * @instr_idx: Index of the instruction inside the sub-operation
+ *
+ * During driver development, one could be tempted to directly use the
+ * ->addr->naddrs field of a data instruction. This is wrong as instructions
+ * might be split.
+ *
+ * Given an address instruction, returns the number of address cycle to issue.
+ */
+unsigned int nand_subop_get_num_addr_cyc(const struct nand_subop *subop,
+ unsigned int instr_idx)
+{
+ int start_off, end_off;
- /* Do not allow reads past end of device */
- if (ops->datbuf && (from + ops->len) > chip->mtd.size) {
- pr_debug("%s: attempt to read beyond end of device\n",
- __func__);
- return -EINVAL;
- }
+ if (WARN_ON(!nand_subop_instr_is_valid(subop, instr_idx) ||
+ subop->instrs[instr_idx].type != NAND_OP_ADDR_INSTR))
+ return 0;
- nand_get_device(chip, FL_READING);
+ start_off = nand_subop_get_addr_start_off(subop, instr_idx);
- switch (ops->mode) {
- case MTD_OPS_PLACE_OOB:
- case MTD_OPS_AUTO_OOB:
- case MTD_OPS_RAW:
- break;
+ if (instr_idx == subop->ninstrs - 1 &&
+ subop->last_instr_end_off)
+ end_off = subop->last_instr_end_off;
+ else
+ end_off = subop->instrs[instr_idx].ctx.addr.naddrs;
- default:
- goto out;
- }
-
- if (!ops->datbuf)
- ret = nand_do_read_oob(chip, from, ops);
- else
- ret = nand_do_read_ops(chip, from, ops);
-
-out:
- nand_release_device(chip);
- return ret;
+ return end_off - start_off;
}
-
+EXPORT_SYMBOL_GPL(nand_subop_get_num_addr_cyc);
/**
- * nand_write_page_raw - [INTERN] raw page write function
- * @mtd: mtd info structure
- * @chip: nand chip info structure
- * @buf: data buffer
- * @oob_required: must write chip->oob_poi to OOB
+ * nand_subop_get_data_start_off - Get the start offset in a data array
+ * @subop: The entire sub-operation
+ * @instr_idx: Index of the instruction inside the sub-operation
*
- * Not for syndrome calculating ECC controllers, which use a special oob layout.
+ * During driver development, one could be tempted to directly use the
+ * ->data->buf.{in,out} field of data instructions. This is wrong as data
+ * instructions might be split.
+ *
+ * Given a data instruction, returns the offset to start from.
*/
-static __maybe_unused int nand_write_page_raw(struct nand_chip *chip,
- const uint8_t *buf, int oob_required)
+unsigned int nand_subop_get_data_start_off(const struct nand_subop *subop,
+ unsigned int instr_idx)
{
- if (!IS_ENABLED(CONFIG_MTD_WRITE))
- return -ENOTSUPP;
-
- chip->legacy.write_buf(chip, buf, chip->mtd.writesize);
- if (oob_required)
- chip->legacy.write_buf(chip, chip->oob_poi, chip->mtd.oobsize);
+ if (WARN_ON(!nand_subop_instr_is_valid(subop, instr_idx) ||
+ !nand_instr_is_data(&subop->instrs[instr_idx])))
+ return 0;
- return 0;
+ return nand_subop_get_start_off(subop, instr_idx);
}
+EXPORT_SYMBOL_GPL(nand_subop_get_data_start_off);
/**
- * nand_write_page_raw_syndrome - [INTERN] raw page write function
- * @mtd: mtd info structure
- * @chip: nand chip info structure
- * @buf: data buffer
- * @oob_required: must write chip->oob_poi to OOB
+ * nand_subop_get_data_len - Get the number of bytes to retrieve
+ * @subop: The entire sub-operation
+ * @instr_idx: Index of the instruction inside the sub-operation
*
- * We need a special oob layout and handling even when ECC isn't checked.
+ * During driver development, one could be tempted to directly use the
+ * ->data->len field of a data instruction. This is wrong as data instructions
+ * might be split.
+ *
+ * Returns the length of the chunk of data to send/receive.
*/
-static __maybe_unused int nand_write_page_raw_syndrome(struct nand_chip *chip,
- const uint8_t *buf, int oob_required)
+unsigned int nand_subop_get_data_len(const struct nand_subop *subop,
+ unsigned int instr_idx)
{
- int eccsize = chip->ecc.size;
- int eccbytes = chip->ecc.bytes;
- uint8_t *oob = chip->oob_poi;
- int steps, size;
-
- if (!IS_ENABLED(CONFIG_MTD_WRITE))
- return -ENOTSUPP;
-
- for (steps = chip->ecc.steps; steps > 0; steps--) {
- chip->legacy.write_buf(chip, buf, eccsize);
- buf += eccsize;
-
- if (chip->ecc.prepad) {
- chip->legacy.write_buf(chip, oob, chip->ecc.prepad);
- oob += chip->ecc.prepad;
- }
+ int start_off = 0, end_off;
- chip->legacy.read_buf(chip, oob, eccbytes);
- oob += eccbytes;
+ if (WARN_ON(!nand_subop_instr_is_valid(subop, instr_idx) ||
+ !nand_instr_is_data(&subop->instrs[instr_idx])))
+ return 0;
- if (chip->ecc.postpad) {
- chip->legacy.write_buf(chip, oob, chip->ecc.postpad);
- oob += chip->ecc.postpad;
- }
- }
+ start_off = nand_subop_get_data_start_off(subop, instr_idx);
- size = chip->mtd.oobsize - (oob - chip->oob_poi);
- if (size)
- chip->legacy.write_buf(chip, oob, size);
+ if (instr_idx == subop->ninstrs - 1 &&
+ subop->last_instr_end_off)
+ end_off = subop->last_instr_end_off;
+ else
+ end_off = subop->instrs[instr_idx].ctx.data.len;
- return 0;
+ return end_off - start_off;
}
+EXPORT_SYMBOL_GPL(nand_subop_get_data_len);
+
/**
- * nand_write_page_swecc - [REPLACEABLE] software ECC based page write function
- * @mtd: mtd info structure
- * @chip: nand chip info structure
- * @buf: data buffer
- * @oob_required: must write chip->oob_poi to OOB
+ * nand_reset - Reset and initialize a NAND device
+ * @chip: The NAND chip
+ * @chipnr: Internal die id
+ *
+ * Save the timings data structure, then apply SDR timings mode 0 (see
+ * nand_reset_interface for details), do the reset operation, and apply
+ * back the previous timings.
+ *
+ * Returns 0 on success, a negative error code otherwise.
*/
-static __maybe_unused int nand_write_page_swecc(struct nand_chip *chip,
- const uint8_t *buf, int oob_required)
+int nand_reset(struct nand_chip *chip, int chipnr)
{
- int i, eccsize = chip->ecc.size;
- int eccbytes = chip->ecc.bytes;
- int eccsteps = chip->ecc.steps;
- uint8_t *ecc_calc = chip->buffers->ecccalc;
- const uint8_t *p = buf;
- uint32_t *eccpos = chip->ecc.layout->eccpos;
+ int ret;
- if (!IS_ENABLED(CONFIG_MTD_WRITE))
- return -ENOTSUPP;
+ ret = nand_reset_interface(chip, chipnr);
+ if (ret)
+ return ret;
- /* Software ECC calculation */
- for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize)
- chip->ecc.calculate(chip, p, &ecc_calc[i]);
+ /*
+ * The CS line has to be released before we can apply the new NAND
+ * interface settings, hence this weird nand_select_target()
+ * nand_deselect_target() dance.
+ */
+ nand_select_target(chip, chipnr);
+ ret = nand_reset_op(chip);
+ nand_deselect_target(chip);
+ if (ret)
+ return ret;
- for (i = 0; i < chip->ecc.total; i++)
- chip->oob_poi[eccpos[i]] = ecc_calc[i];
+ ret = nand_setup_interface(chip, chipnr);
+ if (ret)
+ return ret;
- return chip->ecc.write_page_raw(chip, buf, 1);
+ return 0;
}
+EXPORT_SYMBOL_GPL(nand_reset);
/**
- * nand_write_page_hwecc - [REPLACEABLE] hardware ECC based page write function
- * @mtd: mtd info structure
- * @chip: nand chip info structure
- * @buf: data buffer
- * @oob_required: must write chip->oob_poi to OOB
+ * nand_get_features - wrapper to perform a GET_FEATURE
+ * @chip: NAND chip info structure
+ * @addr: feature address
+ * @subfeature_param: the subfeature parameters, a four bytes array
+ *
+ * Returns 0 for success, a negative error otherwise. Returns -ENOTSUPP if the
+ * operation cannot be handled.
*/
-static __maybe_unused int nand_write_page_hwecc(struct nand_chip *chip,
- const uint8_t *buf, int oob_required)
+int nand_get_features(struct nand_chip *chip, int addr,
+ u8 *subfeature_param)
{
- int i, eccsize = chip->ecc.size;
- int eccbytes = chip->ecc.bytes;
- int eccsteps = chip->ecc.steps;
- uint8_t *ecc_calc = chip->buffers->ecccalc;
- const uint8_t *p = buf;
- uint32_t *eccpos = chip->ecc.layout->eccpos;
-
- if (!IS_ENABLED(CONFIG_MTD_WRITE))
+ if (!nand_supports_get_features(chip, addr))
return -ENOTSUPP;
- for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
- chip->ecc.hwctl(chip, NAND_ECC_WRITE);
- chip->legacy.write_buf(chip, p, eccsize);
- chip->ecc.calculate(chip, p, &ecc_calc[i]);
- }
-
- for (i = 0; i < chip->ecc.total; i++)
- chip->oob_poi[eccpos[i]] = ecc_calc[i];
+ if (chip->legacy.get_features)
+ return chip->legacy.get_features(chip, addr, subfeature_param);
- chip->legacy.write_buf(chip, chip->oob_poi, chip->mtd.oobsize);
-
- return 0;
+ return nand_get_features_op(chip, addr, subfeature_param);
}
-
/**
- * nand_write_subpage_hwecc - [REPLACABLE] hardware ECC based subpage write
- * @mtd: mtd info structure
- * @chip: nand chip info structure
- * @column: column address of subpage within the page
- * @data_len: data length
- * @oob_required: must write chip->oob_poi to OOB
+ * nand_set_features - wrapper to perform a SET_FEATURE
+ * @chip: NAND chip info structure
+ * @addr: feature address
+ * @subfeature_param: the subfeature parameters, a four bytes array
+ *
+ * Returns 0 for success, a negative error otherwise. Returns -ENOTSUPP if the
+ * operation cannot be handled.
*/
-static __maybe_unused int nand_write_subpage_hwecc(struct nand_chip *chip,
- uint32_t offset,
- uint32_t data_len, const uint8_t *data_buf,
- int oob_required)
+int nand_set_features(struct nand_chip *chip, int addr,
+ u8 *subfeature_param)
{
- uint8_t *oob_buf = chip->oob_poi;
- uint8_t *ecc_calc = chip->buffers->ecccalc;
- int ecc_size = chip->ecc.size;
- int ecc_bytes = chip->ecc.bytes;
- int ecc_steps = chip->ecc.steps;
- uint32_t *eccpos = chip->ecc.layout->eccpos;
- uint32_t start_step = offset / ecc_size;
- uint32_t end_step = (offset + data_len - 1) / ecc_size;
- int oob_bytes = chip->mtd.oobsize / ecc_steps;
- int step, i;
-
- if (!IS_ENABLED(CONFIG_MTD_WRITE))
+ if (!nand_supports_set_features(chip, addr))
return -ENOTSUPP;
- for (step = 0; step < ecc_steps; step++) {
- /* configure controller for WRITE access */
- chip->ecc.hwctl(chip, NAND_ECC_WRITE);
-
- /* write data (untouched subpages already masked by 0xFF) */
- chip->legacy.write_buf(chip, data_buf, ecc_size);
+ if (chip->legacy.set_features)
+ return chip->legacy.set_features(chip, addr, subfeature_param);
- /* mask ECC of un-touched subpages by padding 0xFF */
- if ((step < start_step) || (step > end_step))
- memset(ecc_calc, 0xff, ecc_bytes);
- else
- chip->ecc.calculate(chip, data_buf, ecc_calc);
+ return nand_set_features_op(chip, addr, subfeature_param);
+}
- /* mask OOB of un-touched subpages by padding 0xFF */
- /* if oob_required, preserve OOB metadata of written subpage */
- if (!oob_required || (step < start_step) || (step > end_step))
- memset(oob_buf, 0xff, oob_bytes);
+/**
+ * nand_check_erased_buf - check if a buffer contains (almost) only 0xff data
+ * @buf: buffer to test
+ * @len: buffer length
+ * @bitflips_threshold: maximum number of bitflips
+ *
+ * Check if a buffer contains only 0xff, which means the underlying region
+ * has been erased and is ready to be programmed.
+ * The bitflips_threshold specify the maximum number of bitflips before
+ * considering the region is not erased.
+ * Note: The logic of this function has been extracted from the memweight
+ * implementation, except that nand_check_erased_buf function exit before
+ * testing the whole buffer if the number of bitflips exceed the
+ * bitflips_threshold value.
+ *
+ * Returns a positive number of bitflips less than or equal to
+ * bitflips_threshold, or -ERROR_CODE for bitflips in excess of the
+ * threshold.
+ */
+int nand_check_erased_buf(void *buf, int len, int bitflips_threshold)
+{
+ const unsigned char *bitmap = buf;
+ int bitflips = 0;
+ int weight;
- data_buf += ecc_size;
- ecc_calc += ecc_bytes;
- oob_buf += oob_bytes;
+ for (; len && ((uintptr_t)bitmap) % sizeof(long);
+ len--, bitmap++) {
+ weight = hweight8(*bitmap);
+ bitflips += BITS_PER_BYTE - weight;
+ if (unlikely(bitflips > bitflips_threshold))
+ return -EBADMSG;
}
- /* copy calculated ECC for whole page to chip->buffer->oob */
- /* this include masked-value(0xFF) for unwritten subpages */
- ecc_calc = chip->buffers->ecccalc;
- for (i = 0; i < chip->ecc.total; i++)
- chip->oob_poi[eccpos[i]] = ecc_calc[i];
+ for (; len >= sizeof(long);
+ len -= sizeof(long), bitmap += sizeof(long)) {
+ unsigned long d = *((unsigned long *)bitmap);
+ if (d == ~0UL)
+ continue;
+ weight = hweight_long(d);
+ bitflips += BITS_PER_LONG - weight;
+ if (unlikely(bitflips > bitflips_threshold))
+ return -EBADMSG;
+ }
- /* write OOB buffer to NAND device */
- chip->legacy.write_buf(chip, chip->oob_poi, chip->mtd.oobsize);
+ for (; len > 0; len--, bitmap++) {
+ weight = hweight8(*bitmap);
+ bitflips += BITS_PER_BYTE - weight;
+ if (unlikely(bitflips > bitflips_threshold))
+ return -EBADMSG;
+ }
- return 0;
+ return bitflips;
}
-
/**
- * nand_write_page_syndrome - [REPLACEABLE] hardware ECC syndrome based page write
- * @mtd: mtd info structure
- * @chip: nand chip info structure
- * @buf: data buffer
- * @oob_required: must write chip->oob_poi to OOB
+ * nand_check_erased_ecc_chunk - check if an ECC chunk contains (almost) only
+ * 0xff data
+ * @data: data buffer to test
+ * @datalen: data length
+ * @ecc: ECC buffer
+ * @ecclen: ECC length
+ * @extraoob: extra OOB buffer
+ * @extraooblen: extra OOB length
+ * @bitflips_threshold: maximum number of bitflips
*
- * The hw generator calculates the error syndrome automatically. Therefore we
- * need a special oob layout and handling.
+ * Check if a data buffer and its associated ECC and OOB data contains only
+ * 0xff pattern, which means the underlying region has been erased and is
+ * ready to be programmed.
+ * The bitflips_threshold specify the maximum number of bitflips before
+ * considering the region as not erased.
+ *
+ * Note:
+ * 1/ ECC algorithms are working on pre-defined block sizes which are usually
+ * different from the NAND page size. When fixing bitflips, ECC engines will
+ * report the number of errors per chunk, and the NAND core infrastructure
+ * expect you to return the maximum number of bitflips for the whole page.
+ * This is why you should always use this function on a single chunk and
+ * not on the whole page. After checking each chunk you should update your
+ * max_bitflips value accordingly.
+ * 2/ When checking for bitflips in erased pages you should not only check
+ * the payload data but also their associated ECC data, because a user might
+ * have programmed almost all bits to 1 but a few. In this case, we
+ * shouldn't consider the chunk as erased, and checking ECC bytes prevent
+ * this case.
+ * 3/ The extraoob argument is optional, and should be used if some of your OOB
+ * data are protected by the ECC engine.
+ * It could also be used if you support subpages and want to attach some
+ * extra OOB data to an ECC chunk.
+ *
+ * Returns a positive number of bitflips less than or equal to
+ * bitflips_threshold, or -ERROR_CODE for bitflips in excess of the
+ * threshold. In case of success, the passed buffers are filled with 0xff.
*/
-static __maybe_unused int nand_write_page_syndrome(struct nand_chip *chip,
- const uint8_t *buf, int oob_required)
+int nand_check_erased_ecc_chunk(void *data, int datalen,
+ void *ecc, int ecclen,
+ void *extraoob, int extraooblen,
+ int bitflips_threshold)
{
- int i, eccsize = chip->ecc.size;
- int eccbytes = chip->ecc.bytes;
- int eccsteps = chip->ecc.steps;
- const uint8_t *p = buf;
- uint8_t *oob = chip->oob_poi;
+ int data_bitflips = 0, ecc_bitflips = 0, extraoob_bitflips = 0;
- if (!IS_ENABLED(CONFIG_MTD_WRITE))
- return -ENOTSUPP;
+ data_bitflips = nand_check_erased_buf(data, datalen,
+ bitflips_threshold);
+ if (data_bitflips < 0)
+ return data_bitflips;
- for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
+ bitflips_threshold -= data_bitflips;
- chip->ecc.hwctl(chip, NAND_ECC_WRITE);
- chip->legacy.write_buf(chip, p, eccsize);
+ ecc_bitflips = nand_check_erased_buf(ecc, ecclen, bitflips_threshold);
+ if (ecc_bitflips < 0)
+ return ecc_bitflips;
- if (chip->ecc.prepad) {
- chip->legacy.write_buf(chip, oob, chip->ecc.prepad);
- oob += chip->ecc.prepad;
- }
+ bitflips_threshold -= ecc_bitflips;
- chip->ecc.calculate(chip, p, oob);
- chip->legacy.write_buf(chip, oob, eccbytes);
- oob += eccbytes;
+ extraoob_bitflips = nand_check_erased_buf(extraoob, extraooblen,
+ bitflips_threshold);
+ if (extraoob_bitflips < 0)
+ return extraoob_bitflips;
- if (chip->ecc.postpad) {
- chip->legacy.write_buf(chip, oob, chip->ecc.postpad);
- oob += chip->ecc.postpad;
- }
- }
+ if (data_bitflips)
+ memset(data, 0xff, datalen);
- /* Calculate remaining oob bytes */
- i = chip->mtd.oobsize - (oob - chip->oob_poi);
- if (i)
- chip->legacy.write_buf(chip, oob, i);
+ if (ecc_bitflips)
+ memset(ecc, 0xff, ecclen);
- return 0;
+ if (extraoob_bitflips)
+ memset(extraoob, 0xff, extraooblen);
+
+ return data_bitflips + ecc_bitflips + extraoob_bitflips;
}
+EXPORT_SYMBOL(nand_check_erased_ecc_chunk);
/**
- * nand_write_page - [REPLACEABLE] write one page
- * @mtd: MTD device structure
- * @chip: NAND chip descriptor
- * @offset: address offset within the page
- * @data_len: length of actual data to be written
- * @buf: the data to write
- * @oob_required: must write chip->oob_poi to OOB
- * @page: page number to write
- * @cached: cached programming
- * @raw: use _raw version of write_page
+ * nand_read_page_raw_notsupp - dummy read raw page function
+ * @chip: nand chip info structure
+ * @buf: buffer to store read data
+ * @oob_required: caller requires OOB data read to chip->oob_poi
+ * @page: page number to read
+ *
+ * Returns -ENOTSUPP unconditionally.
*/
-static int nand_write_page(struct nand_chip *chip,
- uint32_t offset, int data_len, const uint8_t *buf,
- int oob_required, int page, int cached, int raw)
+int nand_read_page_raw_notsupp(struct nand_chip *chip, u8 *buf,
+ int oob_required, int page)
{
- int status, subpage;
-
- if (!IS_ENABLED(CONFIG_MTD_WRITE))
- return -ENOTSUPP;
-
- if (!(chip->options & NAND_NO_SUBPAGE_WRITE) &&
- chip->ecc.write_subpage)
- subpage = offset || (data_len < chip->mtd.writesize);
- else
- subpage = 0;
-
- chip->legacy.cmdfunc(chip, NAND_CMD_SEQIN, 0x00, page);
-
- if (unlikely(raw))
- status = chip->ecc.write_page_raw(chip, buf,
- oob_required);
- else if (subpage)
- status = chip->ecc.write_subpage(chip, offset, data_len,
- buf, oob_required);
- else
- status = chip->ecc.write_page(chip, buf, oob_required);
-
- if (status < 0)
- return status;
-
- /*
- * Cached progamming disabled for now. Not sure if it's worth the
- * trouble. The speed gain is not very impressive. (2.3->2.6Mib/s).
- */
- cached = 0;
-
- if (!cached || !NAND_HAS_CACHEPROG(chip)) {
-
- chip->legacy.cmdfunc(chip, NAND_CMD_PAGEPROG, -1, -1);
- status = chip->legacy.waitfunc(chip);
-
- if (status & NAND_STATUS_FAIL)
- return -EIO;
- } else {
- chip->legacy.cmdfunc(chip, NAND_CMD_CACHEDPROG, -1, -1);
- status = chip->legacy.waitfunc(chip);
- }
-
- return 0;
+ return -ENOTSUPP;
}
/**
- * nand_fill_oob - [INTERN] Transfer client buffer to oob
- * @mtd: MTD device structure
- * @oob: oob data buffer
- * @len: oob data write length
- * @ops: oob ops structure
+ * nand_read_page_raw - [INTERN] read raw page data without ecc
+ * @chip: nand chip info structure
+ * @buf: buffer to store read data
+ * @oob_required: caller requires OOB data read to chip->oob_poi
+ * @page: page number to read
+ *
+ * Not for syndrome calculating ECC controllers, which use a special oob layout.
*/
-static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob, size_t len,
- struct mtd_oob_ops *ops)
+int nand_read_page_raw(struct nand_chip *chip, uint8_t *buf, int oob_required,
+ int page)
{
- /*
- * Initialise to all 0xFF, to avoid the possibility of left over OOB
- * data from a previous OOB read.
- */
- memset(chip->oob_poi, 0xff, chip->mtd.oobsize);
-
- switch (ops->mode) {
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ int ret;
- case MTD_OPS_PLACE_OOB:
- case MTD_OPS_RAW:
- memcpy(chip->oob_poi + ops->ooboffs, oob, len);
- return oob + len;
+ ret = nand_read_page_op(chip, page, 0, buf, mtd->writesize);
+ if (ret)
+ return ret;
- case MTD_OPS_AUTO_OOB: {
- struct nand_oobfree *free = chip->ecc.layout->oobfree;
- uint32_t boffs = 0, woffs = ops->ooboffs;
- size_t bytes = 0;
-
- for (; free->length && len; free++, len -= bytes) {
- /* Write request not from offset 0? */
- if (unlikely(woffs)) {
- if (woffs >= free->length) {
- woffs -= free->length;
- continue;
- }
- boffs = free->offset + woffs;
- bytes = min_t(size_t, len,
- (free->length - woffs));
- woffs = 0;
- } else {
- bytes = min_t(size_t, len, free->length);
- boffs = free->offset;
- }
- memcpy(chip->oob_poi + boffs, oob, bytes);
- oob += bytes;
- }
- return oob;
- }
- default:
- BUG();
+ if (oob_required) {
+ ret = nand_read_data_op(chip, chip->oob_poi, mtd->oobsize,
+ false, false);
+ if (ret)
+ return ret;
}
- return NULL;
-}
-#define NOTALIGNED(x) ((x & (chip->subpagesize - 1)) != 0)
+ return 0;
+}
+EXPORT_SYMBOL(nand_read_page_raw);
/**
- * nand_do_write_ops - [INTERN] NAND write with ECC
- * @mtd: MTD device structure
- * @to: offset to write to
- * @ops: oob operations description structure
+ * nand_monolithic_read_page_raw - Monolithic page read in raw mode
+ * @chip: NAND chip info structure
+ * @buf: buffer to store read data
+ * @oob_required: caller requires OOB data read to chip->oob_poi
+ * @page: page number to read
*
- * NAND write with ECC.
+ * This is a raw page read, ie. without any error detection/correction.
+ * Monolithic means we are requesting all the relevant data (main plus
+ * eventually OOB) to be loaded in the NAND cache and sent over the
+ * bus (from the NAND chip to the NAND controller) in a single
+ * operation. This is an alternative to nand_read_page_raw(), which
+ * first reads the main data, and if the OOB data is requested too,
+ * then reads more data on the bus.
*/
-static int nand_do_write_ops(struct nand_chip *chip, loff_t to,
- struct mtd_oob_ops *ops)
+int nand_monolithic_read_page_raw(struct nand_chip *chip, u8 *buf,
+ int oob_required, int page)
{
struct mtd_info *mtd = nand_to_mtd(chip);
- int chipnr, realpage, page, blockmask, column;
- uint32_t writelen = ops->len;
-
- uint32_t oobwritelen = ops->ooblen;
- uint32_t oobmaxlen = ops->mode == MTD_OPS_AUTO_OOB ?
- mtd->oobavail : mtd->oobsize;
-
- uint8_t *oob = ops->oobbuf;
- uint8_t *buf = ops->datbuf;
+ unsigned int size = mtd->writesize;
+ u8 *read_buf = buf;
int ret;
- int oob_required = oob ? 1 : 0;
- if (!IS_ENABLED(CONFIG_MTD_WRITE))
- return -ENOTSUPP;
+ if (oob_required) {
+ size += mtd->oobsize;
- ops->retlen = 0;
- if (!writelen)
- return 0;
+ if (buf != chip->data_buf)
+ read_buf = nand_get_data_buf(chip);
+ }
- column = to & (mtd->writesize - 1);
+ ret = nand_read_page_op(chip, page, 0, read_buf, size);
+ if (ret)
+ return ret;
- chipnr = (int)(to >> chip->chip_shift);
- chip->legacy.select_chip(chip, chipnr);
+ if (buf != chip->data_buf)
+ memcpy(buf, read_buf, mtd->writesize);
- /* Check, if it is write protected */
- if (nand_check_wp(chip)) {
- ret = -EIO;
- goto err_out;
- }
+ return 0;
+}
+EXPORT_SYMBOL(nand_monolithic_read_page_raw);
- realpage = (int)(to >> chip->page_shift);
- page = realpage & chip->pagemask;
- blockmask = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1;
+/**
+ * nand_read_page_raw_syndrome - [INTERN] read raw page data without ecc
+ * @chip: nand chip info structure
+ * @buf: buffer to store read data
+ * @oob_required: caller requires OOB data read to chip->oob_poi
+ * @page: page number to read
+ *
+ * We need a special oob layout and handling even when OOB isn't used.
+ */
+static int nand_read_page_raw_syndrome(struct nand_chip *chip, uint8_t *buf,
+ int oob_required, int page)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ int eccsize = chip->ecc.size;
+ int eccbytes = chip->ecc.bytes;
+ uint8_t *oob = chip->oob_poi;
+ int steps, size, ret;
- /* Invalidate the page cache, when we write to the cached page */
- if (to <= (chip->pagebuf << chip->page_shift) &&
- (chip->pagebuf << chip->page_shift) < (to + ops->len))
- chip->pagebuf = -1;
+ ret = nand_read_page_op(chip, page, 0, NULL, 0);
+ if (ret)
+ return ret;
- /* Don't allow multipage oob writes with offset */
- if (oob && ops->ooboffs && (ops->ooboffs + ops->ooblen > oobmaxlen)) {
- ret = -EINVAL;
- goto err_out;
- }
+ for (steps = chip->ecc.steps; steps > 0; steps--) {
+ ret = nand_read_data_op(chip, buf, eccsize, false, false);
+ if (ret)
+ return ret;
- while (1) {
- int bytes = mtd->writesize;
- int cached = writelen > bytes && page != blockmask;
- uint8_t *wbuf = buf;
+ buf += eccsize;
- /* Partial page write? */
- if (unlikely(column || writelen < (mtd->writesize - 1))) {
- cached = 0;
- bytes = min_t(int, bytes - column, (int) writelen);
- chip->pagebuf = -1;
- memset(chip->buffers->databuf, 0xff, mtd->writesize);
- memcpy(&chip->buffers->databuf[column], buf, bytes);
- wbuf = chip->buffers->databuf;
- }
+ if (chip->ecc.prepad) {
+ ret = nand_read_data_op(chip, oob, chip->ecc.prepad,
+ false, false);
+ if (ret)
+ return ret;
- if (unlikely(oob)) {
- size_t len = min(oobwritelen, oobmaxlen);
- oob = nand_fill_oob(chip, oob, len, ops);
- oobwritelen -= len;
- } else {
- /* We still need to erase leftover OOB data */
- memset(chip->oob_poi, 0xff, mtd->oobsize);
+ oob += chip->ecc.prepad;
}
- if (oob || !mtd_buf_all_ff(wbuf, mtd->writesize)) {
- ret = chip->write_page(chip, column, bytes, wbuf,
- oob_required, page, cached,
- (ops->mode == MTD_OPS_RAW));
- if (ret)
- break;
- }
+ ret = nand_read_data_op(chip, oob, eccbytes, false, false);
+ if (ret)
+ return ret;
- writelen -= bytes;
- if (!writelen)
- break;
+ oob += eccbytes;
- column = 0;
- buf += bytes;
- realpage++;
+ if (chip->ecc.postpad) {
+ ret = nand_read_data_op(chip, oob, chip->ecc.postpad,
+ false, false);
+ if (ret)
+ return ret;
- page = realpage & chip->pagemask;
- /* Check, if we cross a chip boundary */
- if (!page) {
- chipnr++;
- chip->legacy.select_chip(chip, -1);
- chip->legacy.select_chip(chip, chipnr);
+ oob += chip->ecc.postpad;
}
}
- ops->retlen = ops->len - writelen;
- if (unlikely(oob))
- ops->oobretlen = ops->ooblen;
+ size = mtd->oobsize - (oob - chip->oob_poi);
+ if (size) {
+ ret = nand_read_data_op(chip, oob, size, false, false);
+ if (ret)
+ return ret;
+ }
-err_out:
- chip->legacy.select_chip(chip, -1);
- return ret;
+ return 0;
}
/**
- * nand_write - [MTD Interface] NAND write with ECC
- * @mtd: MTD device structure
- * @to: offset to write to
- * @len: number of bytes to write
- * @retlen: pointer to variable to store the number of written bytes
- * @buf: the data to write
- *
- * NAND write with ECC.
+ * nand_read_page_swecc - [REPLACEABLE] software ECC based page read function
+ * @chip: nand chip info structure
+ * @buf: buffer to store read data
+ * @oob_required: caller requires OOB data read to chip->oob_poi
+ * @page: page number to read
*/
-static int nand_write(struct mtd_info *mtd, loff_t to, size_t len,
- size_t *retlen, const uint8_t *buf)
+static int nand_read_page_swecc(struct nand_chip *chip, uint8_t *buf,
+ int oob_required, int page)
{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct mtd_oob_ops ops;
- int ret;
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ int i, eccsize = chip->ecc.size, ret;
+ int eccbytes = chip->ecc.bytes;
+ int eccsteps = chip->ecc.steps;
+ uint8_t *p = buf;
+ uint8_t *ecc_calc = chip->ecc.calc_buf;
+ uint8_t *ecc_code = chip->ecc.code_buf;
+ unsigned int max_bitflips = 0;
- if (!IS_ENABLED(CONFIG_MTD_WRITE))
- return -ENOTSUPP;
+ chip->ecc.read_page_raw(chip, buf, 1, page);
- nand_get_device(chip, FL_WRITING);
- ops.len = len;
- ops.datbuf = (uint8_t *)buf;
- ops.oobbuf = NULL;
- ops.mode = MTD_OPS_PLACE_OOB;
- ret = nand_do_write_ops(chip, to, &ops);
- *retlen = ops.retlen;
- nand_release_device(chip);
- return ret;
+ for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize)
+ chip->ecc.calculate(chip, p, &ecc_calc[i]);
+
+ ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code, chip->oob_poi, 0,
+ chip->ecc.total);
+ if (ret)
+ return ret;
+
+ eccsteps = chip->ecc.steps;
+ p = buf;
+
+ for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
+ int stat;
+
+ stat = chip->ecc.correct(chip, p, &ecc_code[i], &ecc_calc[i]);
+ if (stat < 0) {
+ mtd->ecc_stats.failed++;
+ } else {
+ mtd->ecc_stats.corrected += stat;
+ max_bitflips = max_t(unsigned int, max_bitflips, stat);
+ }
+ }
+ return max_bitflips;
}
/**
- * nand_do_write_oob - [MTD Interface] NAND write out-of-band
- * @mtd: MTD device structure
- * @to: offset to write to
- * @ops: oob operation description structure
- *
- * NAND write out-of-band.
+ * nand_read_subpage - [REPLACEABLE] ECC based sub-page read function
+ * @chip: nand chip info structure
+ * @data_offs: offset of requested data within the page
+ * @readlen: data length
+ * @bufpoi: buffer to store read data
+ * @page: page number to read
*/
-static int nand_do_write_oob(struct nand_chip *chip, loff_t to,
- struct mtd_oob_ops *ops)
+static int nand_read_subpage(struct nand_chip *chip, uint32_t data_offs,
+ uint32_t readlen, uint8_t *bufpoi, int page)
{
struct mtd_info *mtd = nand_to_mtd(chip);
- int chipnr, page, status, len;
+ int start_step, end_step, num_steps, ret;
+ uint8_t *p;
+ int data_col_addr, i, gaps = 0;
+ int datafrag_len, eccfrag_len, aligned_len, aligned_pos;
+ int busw = (chip->options & NAND_BUSWIDTH_16) ? 2 : 1;
+ int index, section = 0;
+ unsigned int max_bitflips = 0;
+ struct mtd_oob_region oobregion = { };
- if (!IS_ENABLED(CONFIG_MTD_WRITE))
- return -ENOTSUPP;
+ /* Column address within the page aligned to ECC size (256bytes) */
+ start_step = data_offs / chip->ecc.size;
+ end_step = (data_offs + readlen - 1) / chip->ecc.size;
+ num_steps = end_step - start_step + 1;
+ index = start_step * chip->ecc.bytes;
- pr_debug("%s: to = 0x%08x, len = %i\n",
- __func__, (unsigned int)to, (int)ops->ooblen);
+ /* Data size aligned to ECC ecc.size */
+ datafrag_len = num_steps * chip->ecc.size;
+ eccfrag_len = num_steps * chip->ecc.bytes;
- if (ops->mode == MTD_OPS_AUTO_OOB)
- len = chip->ecc.layout->oobavail;
- else
- len = mtd->oobsize;
+ data_col_addr = start_step * chip->ecc.size;
+ /* If we read not a page aligned data */
+ p = bufpoi + data_col_addr;
+ ret = nand_read_page_op(chip, page, data_col_addr, p, datafrag_len);
+ if (ret)
+ return ret;
- /* Do not allow write past end of page */
- if ((ops->ooboffs + ops->ooblen) > len) {
- pr_debug("%s: attempt to write past end of page\n",
- __func__);
- return -EINVAL;
- }
+ /* Calculate ECC */
+ for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size)
+ chip->ecc.calculate(chip, p, &chip->ecc.calc_buf[i]);
- if (unlikely(ops->ooboffs >= len)) {
- pr_debug("%s: attempt to start write outside oob\n",
- __func__);
- return -EINVAL;
- }
+ /*
+ * The performance is faster if we position offsets according to
+ * ecc.pos. Let's make sure that there are no gaps in ECC positions.
+ */
+ ret = mtd_ooblayout_find_eccregion(mtd, index, §ion, &oobregion);
+ if (ret)
+ return ret;
- /* Do not allow write past end of device */
- if (unlikely(to >= mtd->size ||
- ops->ooboffs + ops->ooblen >
- ((mtd->size >> chip->page_shift) -
- (to >> chip->page_shift)) * len)) {
- pr_debug("%s: attempt to write beyond end of device\n",
- __func__);
- return -EINVAL;
+ if (oobregion.length < eccfrag_len)
+ gaps = 1;
+
+ if (gaps) {
+ ret = nand_change_read_column_op(chip, mtd->writesize,
+ chip->oob_poi, mtd->oobsize,
+ false);
+ if (ret)
+ return ret;
+ } else {
+ /*
+ * Send the command to read the particular ECC bytes take care
+ * about buswidth alignment in read_buf.
+ */
+ aligned_pos = oobregion.offset & ~(busw - 1);
+ aligned_len = eccfrag_len;
+ if (oobregion.offset & (busw - 1))
+ aligned_len++;
+ if ((oobregion.offset + (num_steps * chip->ecc.bytes)) &
+ (busw - 1))
+ aligned_len++;
+
+ ret = nand_change_read_column_op(chip,
+ mtd->writesize + aligned_pos,
+ &chip->oob_poi[aligned_pos],
+ aligned_len, false);
+ if (ret)
+ return ret;
}
- chipnr = (int)(to >> chip->chip_shift);
- chip->legacy.select_chip(chip, chipnr);
+ ret = mtd_ooblayout_get_eccbytes(mtd, chip->ecc.code_buf,
+ chip->oob_poi, index, eccfrag_len);
+ if (ret)
+ return ret;
- /* Shift to get page */
- page = (int)(to >> chip->page_shift);
+ p = bufpoi + data_col_addr;
+ for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size) {
+ int stat;
- /*
- * Reset the chip. Some chips (like the Toshiba TC5832DC found in one
- * of my DiskOnChip 2000 test units) will clear the whole data page too
- * if we don't do this. I have no clue why, but I seem to have 'fixed'
- * it in the doc2000 driver in August 1999. dwmw2.
- */
- chip->legacy.cmdfunc(chip, NAND_CMD_RESET, -1, -1);
+ stat = chip->ecc.correct(chip, p, &chip->ecc.code_buf[i],
+ &chip->ecc.calc_buf[i]);
+ if (stat == -EBADMSG &&
+ (chip->ecc.options & NAND_ECC_GENERIC_ERASED_CHECK)) {
+ /* check for empty pages with bitflips */
+ stat = nand_check_erased_ecc_chunk(p, chip->ecc.size,
+ &chip->ecc.code_buf[i],
+ chip->ecc.bytes,
+ NULL, 0,
+ chip->ecc.strength);
+ }
- /* Check, if it is write protected */
- if (nand_check_wp(chip)) {
- chip->legacy.select_chip(chip, -1);
- return -EROFS;
+ if (stat < 0) {
+ mtd->ecc_stats.failed++;
+ } else {
+ mtd->ecc_stats.corrected += stat;
+ max_bitflips = max_t(unsigned int, max_bitflips, stat);
+ }
}
+ return max_bitflips;
+}
- /* Invalidate the page cache, if we write to the cached page */
- if (page == chip->pagebuf)
- chip->pagebuf = -1;
+/**
+ * nand_read_page_hwecc - [REPLACEABLE] hardware ECC based page read function
+ * @chip: nand chip info structure
+ * @buf: buffer to store read data
+ * @oob_required: caller requires OOB data read to chip->oob_poi
+ * @page: page number to read
+ *
+ * Not for syndrome calculating ECC controllers which need a special oob layout.
+ */
+static int nand_read_page_hwecc(struct nand_chip *chip, uint8_t *buf,
+ int oob_required, int page)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ int i, eccsize = chip->ecc.size, ret;
+ int eccbytes = chip->ecc.bytes;
+ int eccsteps = chip->ecc.steps;
+ uint8_t *p = buf;
+ uint8_t *ecc_calc = chip->ecc.calc_buf;
+ uint8_t *ecc_code = chip->ecc.code_buf;
+ unsigned int max_bitflips = 0;
- nand_fill_oob(chip, ops->oobbuf, ops->ooblen, ops);
+ ret = nand_read_page_op(chip, page, 0, NULL, 0);
+ if (ret)
+ return ret;
- if (ops->mode == MTD_OPS_RAW)
- status = chip->ecc.write_oob_raw(chip, page & chip->pagemask);
- else
- status = chip->ecc.write_oob(chip, page & chip->pagemask);
+ for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
+ chip->ecc.hwctl(chip, NAND_ECC_READ);
- chip->legacy.select_chip(chip, -1);
+ ret = nand_read_data_op(chip, p, eccsize, false, false);
+ if (ret)
+ return ret;
- if (status)
- return status;
+ chip->ecc.calculate(chip, p, &ecc_calc[i]);
+ }
- ops->oobretlen = ops->ooblen;
+ ret = nand_read_data_op(chip, chip->oob_poi, mtd->oobsize, false,
+ false);
+ if (ret)
+ return ret;
- return 0;
+ ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code, chip->oob_poi, 0,
+ chip->ecc.total);
+ if (ret)
+ return ret;
+
+ eccsteps = chip->ecc.steps;
+ p = buf;
+
+ for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
+ int stat;
+
+ stat = chip->ecc.correct(chip, p, &ecc_code[i], &ecc_calc[i]);
+ if (stat == -EBADMSG &&
+ (chip->ecc.options & NAND_ECC_GENERIC_ERASED_CHECK)) {
+ /* check for empty pages with bitflips */
+ stat = nand_check_erased_ecc_chunk(p, eccsize,
+ &ecc_code[i], eccbytes,
+ NULL, 0,
+ chip->ecc.strength);
+ }
+
+ if (stat < 0) {
+ mtd->ecc_stats.failed++;
+ } else {
+ mtd->ecc_stats.corrected += stat;
+ max_bitflips = max_t(unsigned int, max_bitflips, stat);
+ }
+ }
+ return max_bitflips;
}
/**
- * nand_write_oob - [MTD Interface] NAND write data and/or out-of-band
- * @mtd: MTD device structure
- * @to: offset to write to
- * @ops: oob operation description structure
+ * nand_read_page_syndrome - [REPLACEABLE] hardware ECC syndrome based page read
+ * @chip: nand chip info structure
+ * @buf: buffer to store read data
+ * @oob_required: caller requires OOB data read to chip->oob_poi
+ * @page: page number to read
+ *
+ * The hw generator calculates the error syndrome automatically. Therefore we
+ * need a special oob layout and handling.
*/
-static int nand_write_oob(struct mtd_info *mtd, loff_t to,
- struct mtd_oob_ops *ops)
+static int nand_read_page_syndrome(struct nand_chip *chip, uint8_t *buf,
+ int oob_required, int page)
{
- struct nand_chip *chip = mtd_to_nand(mtd);
- int ret = -ENOTSUPP;
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ int ret, i, eccsize = chip->ecc.size;
+ int eccbytes = chip->ecc.bytes;
+ int eccsteps = chip->ecc.steps;
+ int eccpadbytes = eccbytes + chip->ecc.prepad + chip->ecc.postpad;
+ uint8_t *p = buf;
+ uint8_t *oob = chip->oob_poi;
+ unsigned int max_bitflips = 0;
- if (!IS_ENABLED(CONFIG_MTD_WRITE))
- return -ENOTSUPP;
+ ret = nand_read_page_op(chip, page, 0, NULL, 0);
+ if (ret)
+ return ret;
- ops->retlen = 0;
+ for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
+ int stat;
- /* Do not allow writes past end of device */
- if (ops->datbuf && (to + ops->len) > mtd->size) {
- pr_debug("%s: attempt to write beyond end of device\n",
- __func__);
- return -EINVAL;
+ chip->ecc.hwctl(chip, NAND_ECC_READ);
+
+ ret = nand_read_data_op(chip, p, eccsize, false, false);
+ if (ret)
+ return ret;
+
+ if (chip->ecc.prepad) {
+ ret = nand_read_data_op(chip, oob, chip->ecc.prepad,
+ false, false);
+ if (ret)
+ return ret;
+
+ oob += chip->ecc.prepad;
+ }
+
+ chip->ecc.hwctl(chip, NAND_ECC_READSYN);
+
+ ret = nand_read_data_op(chip, oob, eccbytes, false, false);
+ if (ret)
+ return ret;
+
+ stat = chip->ecc.correct(chip, p, oob, NULL);
+
+ oob += eccbytes;
+
+ if (chip->ecc.postpad) {
+ ret = nand_read_data_op(chip, oob, chip->ecc.postpad,
+ false, false);
+ if (ret)
+ return ret;
+
+ oob += chip->ecc.postpad;
+ }
+
+ if (stat == -EBADMSG &&
+ (chip->ecc.options & NAND_ECC_GENERIC_ERASED_CHECK)) {
+ /* check for empty pages with bitflips */
+ stat = nand_check_erased_ecc_chunk(p, chip->ecc.size,
+ oob - eccpadbytes,
+ eccpadbytes,
+ NULL, 0,
+ chip->ecc.strength);
+ }
+
+ if (stat < 0) {
+ mtd->ecc_stats.failed++;
+ } else {
+ mtd->ecc_stats.corrected += stat;
+ max_bitflips = max_t(unsigned int, max_bitflips, stat);
+ }
+ }
+
+ /* Calculate remaining oob bytes */
+ i = mtd->oobsize - (oob - chip->oob_poi);
+ if (i) {
+ ret = nand_read_data_op(chip, oob, i, false, false);
+ if (ret)
+ return ret;
}
- nand_get_device(chip, FL_WRITING);
+ return max_bitflips;
+}
+
+/**
+ * nand_transfer_oob - [INTERN] Transfer oob to client buffer
+ * @chip: NAND chip object
+ * @oob: oob destination address
+ * @ops: oob ops structure
+ * @len: size of oob to transfer
+ */
+static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob,
+ struct mtd_oob_ops *ops, size_t len)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ int ret;
switch (ops->mode) {
+
case MTD_OPS_PLACE_OOB:
- case MTD_OPS_AUTO_OOB:
case MTD_OPS_RAW:
- break;
+ memcpy(oob, chip->oob_poi + ops->ooboffs, len);
+ return oob + len;
+
+ case MTD_OPS_AUTO_OOB:
+ ret = mtd_ooblayout_get_databytes(mtd, oob, chip->oob_poi,
+ ops->ooboffs, len);
+ BUG_ON(ret);
+ return oob + len;
default:
- goto out;
+ BUG();
+ }
+ return NULL;
+}
+
+/**
+ * nand_setup_read_retry - [INTERN] Set the READ RETRY mode
+ * @chip: NAND chip object
+ * @retry_mode: the retry mode to use
+ *
+ * Some vendors supply a special command to shift the Vt threshold, to be used
+ * when there are too many bitflips in a page (i.e., ECC error). After setting
+ * a new threshold, the host should retry reading the page.
+ */
+static int nand_setup_read_retry(struct nand_chip *chip, int retry_mode)
+{
+ pr_debug("setting READ RETRY mode %d\n", retry_mode);
+
+ if (retry_mode >= chip->read_retries)
+ return -EINVAL;
+
+ if (!chip->ops.setup_read_retry)
+ return -EOPNOTSUPP;
+
+ return chip->ops.setup_read_retry(chip, retry_mode);
+}
+
+static void nand_wait_readrdy(struct nand_chip *chip)
+{
+ const struct nand_sdr_timings *sdr;
+
+ if (!(chip->options & NAND_NEED_READRDY))
+ return;
+
+ sdr = nand_get_sdr_timings(nand_get_interface_config(chip));
+ WARN_ON(nand_wait_rdy_op(chip, PSEC_TO_MSEC(sdr->tR_max), 0));
+}
+
+/**
+ * nand_do_read_ops - [INTERN] Read data with ECC
+ * @chip: NAND chip object
+ * @from: offset to read from
+ * @ops: oob ops structure
+ *
+ * Internal function. Called with chip held.
+ */
+static int nand_do_read_ops(struct nand_chip *chip, loff_t from,
+ struct mtd_oob_ops *ops)
+{
+ int chipnr, page, realpage, col, bytes, aligned, oob_required;
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ int ret = 0;
+ uint32_t readlen = ops->len;
+ uint32_t oobreadlen = ops->ooblen;
+ uint32_t max_oobsize = mtd_oobavail(mtd, ops);
+
+ uint8_t *bufpoi, *oob, *buf;
+ int use_bounce_buf;
+ unsigned int max_bitflips = 0;
+ int retry_mode = 0;
+ bool ecc_fail = false;
+
+ chipnr = (int)(from >> chip->chip_shift);
+ nand_select_target(chip, chipnr);
+
+ realpage = (int)(from >> chip->page_shift);
+ page = realpage & chip->pagemask;
+
+ col = (int)(from & (mtd->writesize - 1));
+
+ buf = ops->datbuf;
+ oob = ops->oobbuf;
+ oob_required = oob ? 1 : 0;
+
+ while (1) {
+ struct mtd_ecc_stats ecc_stats = mtd->ecc_stats;
+
+ bytes = min(mtd->writesize - col, readlen);
+ aligned = (bytes == mtd->writesize);
+
+ if (!aligned)
+ use_bounce_buf = 1;
+ else if (chip->options & NAND_USES_DMA)
+ use_bounce_buf = !IS_ALIGNED((unsigned long)buf,
+ chip->buf_align);
+ else
+ use_bounce_buf = 0;
+
+ /* Is the current page in the buffer? */
+ if (realpage != chip->pagecache.page || oob) {
+ bufpoi = use_bounce_buf ? chip->data_buf : buf;
+
+ if (use_bounce_buf && aligned)
+ pr_debug("%s: using read bounce buffer for buf@%p\n",
+ __func__, buf);
+
+read_retry:
+ /*
+ * Now read the page into the buffer. Absent an error,
+ * the read methods return max bitflips per ecc step.
+ */
+ if (unlikely(ops->mode == MTD_OPS_RAW))
+ ret = chip->ecc.read_page_raw(chip, bufpoi,
+ oob_required,
+ page);
+ else if (!aligned && NAND_HAS_SUBPAGE_READ(chip) &&
+ !oob)
+ ret = chip->ecc.read_subpage(chip, col, bytes,
+ bufpoi, page);
+ else
+ ret = chip->ecc.read_page(chip, bufpoi,
+ oob_required, page);
+ if (ret < 0) {
+ if (use_bounce_buf)
+ /* Invalidate page cache */
+ chip->pagecache.page = -1;
+ break;
+ }
+
+ /*
+ * Copy back the data in the initial buffer when reading
+ * partial pages or when a bounce buffer is required.
+ */
+ if (use_bounce_buf) {
+ if (!NAND_HAS_SUBPAGE_READ(chip) && !oob &&
+ !(mtd->ecc_stats.failed - ecc_stats.failed) &&
+ (ops->mode != MTD_OPS_RAW)) {
+ chip->pagecache.page = realpage;
+ chip->pagecache.bitflips = ret;
+ } else {
+ /* Invalidate page cache */
+ chip->pagecache.page = -1;
+ }
+ memcpy(buf, bufpoi + col, bytes);
+ }
+
+ if (unlikely(oob)) {
+ int toread = min(oobreadlen, max_oobsize);
+
+ if (toread) {
+ oob = nand_transfer_oob(chip, oob, ops,
+ toread);
+ oobreadlen -= toread;
+ }
+ }
+
+ nand_wait_readrdy(chip);
+
+ if (mtd->ecc_stats.failed - ecc_stats.failed) {
+ if (retry_mode + 1 < chip->read_retries) {
+ retry_mode++;
+ ret = nand_setup_read_retry(chip,
+ retry_mode);
+ if (ret < 0)
+ break;
+
+ /* Reset ecc_stats; retry */
+ mtd->ecc_stats = ecc_stats;
+ goto read_retry;
+ } else {
+ /* No more retry modes; real failure */
+ ecc_fail = true;
+ }
+ }
+
+ buf += bytes;
+ max_bitflips = max_t(unsigned int, max_bitflips, ret);
+ } else {
+ memcpy(buf, chip->data_buf + col, bytes);
+ buf += bytes;
+ max_bitflips = max_t(unsigned int, max_bitflips,
+ chip->pagecache.bitflips);
+ }
+
+ readlen -= bytes;
+
+ /* Reset to retry mode 0 */
+ if (retry_mode) {
+ ret = nand_setup_read_retry(chip, 0);
+ if (ret < 0)
+ break;
+ retry_mode = 0;
+ }
+
+ if (!readlen)
+ break;
+
+ /* For subsequent reads align to page boundary */
+ col = 0;
+ /* Increment page address */
+ realpage++;
+
+ page = realpage & chip->pagemask;
+ /* Check, if we cross a chip boundary */
+ if (!page) {
+ chipnr++;
+ nand_deselect_target(chip);
+ nand_select_target(chip, chipnr);
+ }
+ }
+ nand_deselect_target(chip);
+
+ ops->retlen = ops->len - (size_t) readlen;
+ if (oob)
+ ops->oobretlen = ops->ooblen - oobreadlen;
+
+ if (ret < 0)
+ return ret;
+
+ if (ecc_fail)
+ return -EBADMSG;
+
+ return max_bitflips;
+}
+
+/**
+ * nand_read_oob_std - [REPLACEABLE] the most common OOB data read function
+ * @chip: nand chip info structure
+ * @page: page number to read
+ */
+int nand_read_oob_std(struct nand_chip *chip, int page)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+
+ return nand_read_oob_op(chip, page, 0, chip->oob_poi, mtd->oobsize);
+}
+EXPORT_SYMBOL(nand_read_oob_std);
+
+/**
+ * nand_read_oob_syndrome - [REPLACEABLE] OOB data read function for HW ECC
+ * with syndromes
+ * @chip: nand chip info structure
+ * @page: page number to read
+ */
+static int nand_read_oob_syndrome(struct nand_chip *chip, int page)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ int length = mtd->oobsize;
+ int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad;
+ int eccsize = chip->ecc.size;
+ uint8_t *bufpoi = chip->oob_poi;
+ int i, toread, sndrnd = 0, pos, ret;
+
+ ret = nand_read_page_op(chip, page, chip->ecc.size, NULL, 0);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < chip->ecc.steps; i++) {
+ if (sndrnd) {
+ int ret;
+
+ pos = eccsize + i * (eccsize + chunk);
+ if (mtd->writesize > 512)
+ ret = nand_change_read_column_op(chip, pos,
+ NULL, 0,
+ false);
+ else
+ ret = nand_read_page_op(chip, page, pos, NULL,
+ 0);
+
+ if (ret)
+ return ret;
+ } else
+ sndrnd = 1;
+ toread = min_t(int, length, chunk);
+
+ ret = nand_read_data_op(chip, bufpoi, toread, false, false);
+ if (ret)
+ return ret;
+
+ bufpoi += toread;
+ length -= toread;
+ }
+ if (length > 0) {
+ ret = nand_read_data_op(chip, bufpoi, length, false, false);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * nand_write_oob_std - [REPLACEABLE] the most common OOB data write function
+ * @chip: nand chip info structure
+ * @page: page number to write
+ */
+int nand_write_oob_std(struct nand_chip *chip, int page)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+
+ return nand_prog_page_op(chip, page, mtd->writesize, chip->oob_poi,
+ mtd->oobsize);
+}
+EXPORT_SYMBOL(nand_write_oob_std);
+
+/**
+ * nand_write_oob_syndrome - [REPLACEABLE] OOB data write function for HW ECC
+ * with syndrome - only for large page flash
+ * @chip: nand chip info structure
+ * @page: page number to write
+ */
+static int nand_write_oob_syndrome(struct nand_chip *chip, int page)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad;
+ int eccsize = chip->ecc.size, length = mtd->oobsize;
+ int ret, i, len, pos, sndcmd = 0, steps = chip->ecc.steps;
+ const uint8_t *bufpoi = chip->oob_poi;
+
+ /*
+ * data-ecc-data-ecc ... ecc-oob
+ * or
+ * data-pad-ecc-pad-data-pad .... ecc-pad-oob
+ */
+ if (!chip->ecc.prepad && !chip->ecc.postpad) {
+ pos = steps * (eccsize + chunk);
+ steps = 0;
+ } else
+ pos = eccsize;
+
+ ret = nand_prog_page_begin_op(chip, page, pos, NULL, 0);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < steps; i++) {
+ if (sndcmd) {
+ if (mtd->writesize <= 512) {
+ uint32_t fill = 0xFFFFFFFF;
+
+ len = eccsize;
+ while (len > 0) {
+ int num = min_t(int, len, 4);
+
+ ret = nand_write_data_op(chip, &fill,
+ num, false);
+ if (ret)
+ return ret;
+
+ len -= num;
+ }
+ } else {
+ pos = eccsize + i * (eccsize + chunk);
+ ret = nand_change_write_column_op(chip, pos,
+ NULL, 0,
+ false);
+ if (ret)
+ return ret;
+ }
+ } else
+ sndcmd = 1;
+ len = min_t(int, length, chunk);
+
+ ret = nand_write_data_op(chip, bufpoi, len, false);
+ if (ret)
+ return ret;
+
+ bufpoi += len;
+ length -= len;
+ }
+ if (length > 0) {
+ ret = nand_write_data_op(chip, bufpoi, length, false);
+ if (ret)
+ return ret;
+ }
+
+ return nand_prog_page_end_op(chip);
+}
+
+/**
+ * nand_do_read_oob - [INTERN] NAND read out-of-band
+ * @chip: NAND chip object
+ * @from: offset to read from
+ * @ops: oob operations description structure
+ *
+ * NAND read out-of-band data from the spare area.
+ */
+static int nand_do_read_oob(struct nand_chip *chip, loff_t from,
+ struct mtd_oob_ops *ops)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ unsigned int max_bitflips = 0;
+ int page, realpage, chipnr;
+ struct mtd_ecc_stats stats;
+ int readlen = ops->ooblen;
+ int len;
+ uint8_t *buf = ops->oobbuf;
+ int ret = 0;
+
+ pr_debug("%s: from = 0x%08Lx, len = %i\n",
+ __func__, (unsigned long long)from, readlen);
+
+ stats = mtd->ecc_stats;
+
+ len = mtd_oobavail(mtd, ops);
+
+ chipnr = (int)(from >> chip->chip_shift);
+ nand_select_target(chip, chipnr);
+
+ /* Shift to get page */
+ realpage = (int)(from >> chip->page_shift);
+ page = realpage & chip->pagemask;
+
+ while (1) {
+ if (ops->mode == MTD_OPS_RAW)
+ ret = chip->ecc.read_oob_raw(chip, page);
+ else
+ ret = chip->ecc.read_oob(chip, page);
+
+ if (ret < 0)
+ break;
+
+ len = min(len, readlen);
+ buf = nand_transfer_oob(chip, buf, ops, len);
+
+ nand_wait_readrdy(chip);
+
+ max_bitflips = max_t(unsigned int, max_bitflips, ret);
+
+ readlen -= len;
+ if (!readlen)
+ break;
+
+ /* Increment page address */
+ realpage++;
+
+ page = realpage & chip->pagemask;
+ /* Check, if we cross a chip boundary */
+ if (!page) {
+ chipnr++;
+ nand_deselect_target(chip);
+ nand_select_target(chip, chipnr);
+ }
+ }
+ nand_deselect_target(chip);
+
+ ops->oobretlen = ops->ooblen - readlen;
+
+ if (ret < 0)
+ return ret;
+
+ if (mtd->ecc_stats.failed - stats.failed)
+ return -EBADMSG;
+
+ return max_bitflips;
+}
+
+/**
+ * nand_read_oob - [MTD Interface] NAND read data and/or out-of-band
+ * @mtd: MTD device structure
+ * @from: offset to read from
+ * @ops: oob operation description structure
+ *
+ * NAND read data and/or out-of-band data.
+ */
+static int nand_read_oob(struct mtd_info *mtd, loff_t from,
+ struct mtd_oob_ops *ops)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ int ret;
+
+ ops->retlen = 0;
+
+ if (ops->mode != MTD_OPS_PLACE_OOB &&
+ ops->mode != MTD_OPS_AUTO_OOB &&
+ ops->mode != MTD_OPS_RAW)
+ return -ENOTSUPP;
+
+ ret = nand_get_device(chip);
+ if (ret)
+ return ret;
+
+ if (!ops->datbuf)
+ ret = nand_do_read_oob(chip, from, ops);
+ else
+ ret = nand_do_read_ops(chip, from, ops);
+
+ nand_release_device(chip);
+ return ret;
+}
+
+/**
+ * nand_write_page_raw_notsupp - dummy raw page write function
+ * @chip: nand chip info structure
+ * @buf: data buffer
+ * @oob_required: must write chip->oob_poi to OOB
+ * @page: page number to write
+ *
+ * Returns -ENOTSUPP unconditionally.
+ */
+int nand_write_page_raw_notsupp(struct nand_chip *chip, const u8 *buf,
+ int oob_required, int page)
+{
+ return -ENOTSUPP;
+}
+
+/**
+ * nand_write_page_raw - [INTERN] raw page write function
+ * @chip: nand chip info structure
+ * @buf: data buffer
+ * @oob_required: must write chip->oob_poi to OOB
+ * @page: page number to write
+ *
+ * Not for syndrome calculating ECC controllers, which use a special oob layout.
+ */
+int nand_write_page_raw(struct nand_chip *chip, const uint8_t *buf,
+ int oob_required, int page)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ int ret;
+
+ ret = nand_prog_page_begin_op(chip, page, 0, buf, mtd->writesize);
+ if (ret)
+ return ret;
+
+ if (oob_required) {
+ ret = nand_write_data_op(chip, chip->oob_poi, mtd->oobsize,
+ false);
+ if (ret)
+ return ret;
+ }
+
+ return nand_prog_page_end_op(chip);
+}
+EXPORT_SYMBOL(nand_write_page_raw);
+
+/**
+ * nand_monolithic_write_page_raw - Monolithic page write in raw mode
+ * @chip: NAND chip info structure
+ * @buf: data buffer to write
+ * @oob_required: must write chip->oob_poi to OOB
+ * @page: page number to write
+ *
+ * This is a raw page write, ie. without any error detection/correction.
+ * Monolithic means we are requesting all the relevant data (main plus
+ * eventually OOB) to be sent over the bus and effectively programmed
+ * into the NAND chip arrays in a single operation. This is an
+ * alternative to nand_write_page_raw(), which first sends the main
+ * data, then eventually send the OOB data by latching more data
+ * cycles on the NAND bus, and finally sends the program command to
+ * synchronyze the NAND chip cache.
+ */
+int nand_monolithic_write_page_raw(struct nand_chip *chip, const u8 *buf,
+ int oob_required, int page)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ unsigned int size = mtd->writesize;
+ u8 *write_buf = (u8 *)buf;
+
+ if (oob_required) {
+ size += mtd->oobsize;
+
+ if (buf != chip->data_buf) {
+ write_buf = nand_get_data_buf(chip);
+ memcpy(write_buf, buf, mtd->writesize);
+ }
+ }
+
+ return nand_prog_page_op(chip, page, 0, write_buf, size);
+}
+EXPORT_SYMBOL(nand_monolithic_write_page_raw);
+
+/**
+ * nand_write_page_raw_syndrome - [INTERN] raw page write function
+ * @chip: nand chip info structure
+ * @buf: data buffer
+ * @oob_required: must write chip->oob_poi to OOB
+ * @page: page number to write
+ *
+ * We need a special oob layout and handling even when ECC isn't checked.
+ */
+static int nand_write_page_raw_syndrome(struct nand_chip *chip,
+ const uint8_t *buf, int oob_required,
+ int page)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ int eccsize = chip->ecc.size;
+ int eccbytes = chip->ecc.bytes;
+ uint8_t *oob = chip->oob_poi;
+ int steps, size, ret;
+
+ ret = nand_prog_page_begin_op(chip, page, 0, NULL, 0);
+ if (ret)
+ return ret;
+
+ for (steps = chip->ecc.steps; steps > 0; steps--) {
+ ret = nand_write_data_op(chip, buf, eccsize, false);
+ if (ret)
+ return ret;
+
+ buf += eccsize;
+
+ if (chip->ecc.prepad) {
+ ret = nand_write_data_op(chip, oob, chip->ecc.prepad,
+ false);
+ if (ret)
+ return ret;
+
+ oob += chip->ecc.prepad;
+ }
+
+ ret = nand_write_data_op(chip, oob, eccbytes, false);
+ if (ret)
+ return ret;
+
+ oob += eccbytes;
+
+ if (chip->ecc.postpad) {
+ ret = nand_write_data_op(chip, oob, chip->ecc.postpad,
+ false);
+ if (ret)
+ return ret;
+
+ oob += chip->ecc.postpad;
+ }
+ }
+
+ size = mtd->oobsize - (oob - chip->oob_poi);
+ if (size) {
+ ret = nand_write_data_op(chip, oob, size, false);
+ if (ret)
+ return ret;
+ }
+
+ return nand_prog_page_end_op(chip);
+}
+/**
+ * nand_write_page_swecc - [REPLACEABLE] software ECC based page write function
+ * @chip: nand chip info structure
+ * @buf: data buffer
+ * @oob_required: must write chip->oob_poi to OOB
+ * @page: page number to write
+ */
+static int nand_write_page_swecc(struct nand_chip *chip, const uint8_t *buf,
+ int oob_required, int page)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ int i, eccsize = chip->ecc.size, ret;
+ int eccbytes = chip->ecc.bytes;
+ int eccsteps = chip->ecc.steps;
+ uint8_t *ecc_calc = chip->ecc.calc_buf;
+ const uint8_t *p = buf;
+
+ /* Software ECC calculation */
+ for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize)
+ chip->ecc.calculate(chip, p, &ecc_calc[i]);
+
+ ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc, chip->oob_poi, 0,
+ chip->ecc.total);
+ if (ret)
+ return ret;
+
+ return chip->ecc.write_page_raw(chip, buf, 1, page);
+}
+
+/**
+ * nand_write_page_hwecc - [REPLACEABLE] hardware ECC based page write function
+ * @chip: nand chip info structure
+ * @buf: data buffer
+ * @oob_required: must write chip->oob_poi to OOB
+ * @page: page number to write
+ */
+static int nand_write_page_hwecc(struct nand_chip *chip, const uint8_t *buf,
+ int oob_required, int page)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ int i, eccsize = chip->ecc.size, ret;
+ int eccbytes = chip->ecc.bytes;
+ int eccsteps = chip->ecc.steps;
+ uint8_t *ecc_calc = chip->ecc.calc_buf;
+ const uint8_t *p = buf;
+
+ ret = nand_prog_page_begin_op(chip, page, 0, NULL, 0);
+ if (ret)
+ return ret;
+
+ for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
+ chip->ecc.hwctl(chip, NAND_ECC_WRITE);
+
+ ret = nand_write_data_op(chip, p, eccsize, false);
+ if (ret)
+ return ret;
+
+ chip->ecc.calculate(chip, p, &ecc_calc[i]);
+ }
+
+ ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc, chip->oob_poi, 0,
+ chip->ecc.total);
+ if (ret)
+ return ret;
+
+ ret = nand_write_data_op(chip, chip->oob_poi, mtd->oobsize, false);
+ if (ret)
+ return ret;
+
+ return nand_prog_page_end_op(chip);
+}
+
+
+/**
+ * nand_write_subpage_hwecc - [REPLACEABLE] hardware ECC based subpage write
+ * @chip: nand chip info structure
+ * @offset: column address of subpage within the page
+ * @data_len: data length
+ * @buf: data buffer
+ * @oob_required: must write chip->oob_poi to OOB
+ * @page: page number to write
+ */
+static int nand_write_subpage_hwecc(struct nand_chip *chip, uint32_t offset,
+ uint32_t data_len, const uint8_t *buf,
+ int oob_required, int page)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ uint8_t *oob_buf = chip->oob_poi;
+ uint8_t *ecc_calc = chip->ecc.calc_buf;
+ int ecc_size = chip->ecc.size;
+ int ecc_bytes = chip->ecc.bytes;
+ int ecc_steps = chip->ecc.steps;
+ uint32_t start_step = offset / ecc_size;
+ uint32_t end_step = (offset + data_len - 1) / ecc_size;
+ int oob_bytes = mtd->oobsize / ecc_steps;
+ int step, ret;
+
+ ret = nand_prog_page_begin_op(chip, page, 0, NULL, 0);
+ if (ret)
+ return ret;
+
+ for (step = 0; step < ecc_steps; step++) {
+ /* configure controller for WRITE access */
+ chip->ecc.hwctl(chip, NAND_ECC_WRITE);
+
+ /* write data (untouched subpages already masked by 0xFF) */
+ ret = nand_write_data_op(chip, buf, ecc_size, false);
+ if (ret)
+ return ret;
+
+ /* mask ECC of un-touched subpages by padding 0xFF */
+ if ((step < start_step) || (step > end_step))
+ memset(ecc_calc, 0xff, ecc_bytes);
+ else
+ chip->ecc.calculate(chip, buf, ecc_calc);
+
+ /* mask OOB of un-touched subpages by padding 0xFF */
+ /* if oob_required, preserve OOB metadata of written subpage */
+ if (!oob_required || (step < start_step) || (step > end_step))
+ memset(oob_buf, 0xff, oob_bytes);
+
+ buf += ecc_size;
+ ecc_calc += ecc_bytes;
+ oob_buf += oob_bytes;
+ }
+
+ /* copy calculated ECC for whole page to chip->buffer->oob */
+ /* this include masked-value(0xFF) for unwritten subpages */
+ ecc_calc = chip->ecc.calc_buf;
+ ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc, chip->oob_poi, 0,
+ chip->ecc.total);
+ if (ret)
+ return ret;
+
+ /* write OOB buffer to NAND device */
+ ret = nand_write_data_op(chip, chip->oob_poi, mtd->oobsize, false);
+ if (ret)
+ return ret;
+
+ return nand_prog_page_end_op(chip);
+}
+
+
+/**
+ * nand_write_page_syndrome - [REPLACEABLE] hardware ECC syndrome based page write
+ * @chip: nand chip info structure
+ * @buf: data buffer
+ * @oob_required: must write chip->oob_poi to OOB
+ * @page: page number to write
+ *
+ * The hw generator calculates the error syndrome automatically. Therefore we
+ * need a special oob layout and handling.
+ */
+static int nand_write_page_syndrome(struct nand_chip *chip, const uint8_t *buf,
+ int oob_required, int page)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ int i, eccsize = chip->ecc.size;
+ int eccbytes = chip->ecc.bytes;
+ int eccsteps = chip->ecc.steps;
+ const uint8_t *p = buf;
+ uint8_t *oob = chip->oob_poi;
+ int ret;
+
+ ret = nand_prog_page_begin_op(chip, page, 0, NULL, 0);
+ if (ret)
+ return ret;
+
+ for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
+ chip->ecc.hwctl(chip, NAND_ECC_WRITE);
+
+ ret = nand_write_data_op(chip, p, eccsize, false);
+ if (ret)
+ return ret;
+
+ if (chip->ecc.prepad) {
+ ret = nand_write_data_op(chip, oob, chip->ecc.prepad,
+ false);
+ if (ret)
+ return ret;
+
+ oob += chip->ecc.prepad;
+ }
+
+ chip->ecc.calculate(chip, p, oob);
+
+ ret = nand_write_data_op(chip, oob, eccbytes, false);
+ if (ret)
+ return ret;
+
+ oob += eccbytes;
+
+ if (chip->ecc.postpad) {
+ ret = nand_write_data_op(chip, oob, chip->ecc.postpad,
+ false);
+ if (ret)
+ return ret;
+
+ oob += chip->ecc.postpad;
+ }
+ }
+
+ /* Calculate remaining oob bytes */
+ i = mtd->oobsize - (oob - chip->oob_poi);
+ if (i) {
+ ret = nand_write_data_op(chip, oob, i, false);
+ if (ret)
+ return ret;
+ }
+
+ return nand_prog_page_end_op(chip);
+}
+
+/**
+ * nand_write_page - write one page
+ * @chip: NAND chip descriptor
+ * @offset: address offset within the page
+ * @data_len: length of actual data to be written
+ * @buf: the data to write
+ * @oob_required: must write chip->oob_poi to OOB
+ * @page: page number to write
+ * @raw: use _raw version of write_page
+ */
+static int nand_write_page(struct nand_chip *chip, uint32_t offset,
+ int data_len, const uint8_t *buf, int oob_required,
+ int page, int raw)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ int status, subpage;
+
+ if (!(chip->options & NAND_NO_SUBPAGE_WRITE) &&
+ chip->ecc.write_subpage)
+ subpage = offset || (data_len < mtd->writesize);
+ else
+ subpage = 0;
+
+ if (unlikely(raw))
+ status = chip->ecc.write_page_raw(chip, buf, oob_required,
+ page);
+ else if (subpage)
+ status = chip->ecc.write_subpage(chip, offset, data_len, buf,
+ oob_required, page);
+ else
+ status = chip->ecc.write_page(chip, buf, oob_required, page);
+
+ if (status < 0)
+ return status;
+
+ return 0;
+}
+
+#define NOTALIGNED(x) ((x & (chip->subpagesize - 1)) != 0)
+
+/**
+ * nand_do_write_ops - [INTERN] NAND write with ECC
+ * @chip: NAND chip object
+ * @to: offset to write to
+ * @ops: oob operations description structure
+ *
+ * NAND write with ECC.
+ */
+static int nand_do_write_ops(struct nand_chip *chip, loff_t to,
+ struct mtd_oob_ops *ops)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ int chipnr, realpage, page, column;
+ uint32_t writelen = ops->len;
+
+ uint32_t oobwritelen = ops->ooblen;
+ uint32_t oobmaxlen = mtd_oobavail(mtd, ops);
+
+ uint8_t *oob = ops->oobbuf;
+ uint8_t *buf = ops->datbuf;
+ int ret;
+ int oob_required = oob ? 1 : 0;
+
+ ops->retlen = 0;
+ if (!writelen)
+ return 0;
+
+ /* Reject writes, which are not page aligned */
+ if (NOTALIGNED(to) || NOTALIGNED(ops->len)) {
+ pr_notice("%s: attempt to write non page aligned data\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ column = to & (mtd->writesize - 1);
+
+ chipnr = (int)(to >> chip->chip_shift);
+ nand_select_target(chip, chipnr);
+
+ /* Check, if it is write protected */
+ if (nand_check_wp(chip)) {
+ ret = -EIO;
+ goto err_out;
+ }
+
+ realpage = (int)(to >> chip->page_shift);
+ page = realpage & chip->pagemask;
+
+ /* Invalidate the page cache, when we write to the cached page */
+ if (to <= ((loff_t)chip->pagecache.page << chip->page_shift) &&
+ ((loff_t)chip->pagecache.page << chip->page_shift) < (to + ops->len))
+ chip->pagecache.page = -1;
+
+ /* Don't allow multipage oob writes with offset */
+ if (oob && ops->ooboffs && (ops->ooboffs + ops->ooblen > oobmaxlen)) {
+ ret = -EINVAL;
+ goto err_out;
+ }
+
+ while (1) {
+ int bytes = mtd->writesize;
+ uint8_t *wbuf = buf;
+ int use_bounce_buf;
+ int part_pagewr = (column || writelen < mtd->writesize);
+
+ if (part_pagewr)
+ use_bounce_buf = 1;
+ else if (chip->options & NAND_USES_DMA)
+ use_bounce_buf = !IS_ALIGNED((unsigned long)buf,
+ chip->buf_align);
+ else
+ use_bounce_buf = 0;
+
+ /*
+ * Copy the data from the initial buffer when doing partial page
+ * writes or when a bounce buffer is required.
+ */
+ if (use_bounce_buf) {
+ pr_debug("%s: using write bounce buffer for buf@%p\n",
+ __func__, buf);
+ if (part_pagewr)
+ bytes = min_t(int, bytes - column, writelen);
+ wbuf = nand_get_data_buf(chip);
+ memset(wbuf, 0xff, mtd->writesize);
+ memcpy(&wbuf[column], buf, bytes);
+ }
+
+ if (unlikely(oob)) {
+ size_t len = min(oobwritelen, oobmaxlen);
+ oob = nand_fill_oob(chip, oob, len, ops);
+ oobwritelen -= len;
+ } else {
+ /* We still need to erase leftover OOB data */
+ memset(chip->oob_poi, 0xff, mtd->oobsize);
+ }
+
+ ret = nand_write_page(chip, column, bytes, wbuf,
+ oob_required, page,
+ (ops->mode == MTD_OPS_RAW));
+ if (ret)
+ break;
+
+ writelen -= bytes;
+ if (!writelen)
+ break;
+
+ column = 0;
+ buf += bytes;
+ realpage++;
+
+ page = realpage & chip->pagemask;
+ /* Check, if we cross a chip boundary */
+ if (!page) {
+ chipnr++;
+ nand_deselect_target(chip);
+ nand_select_target(chip, chipnr);
+ }
+ }
+
+ ops->retlen = ops->len - writelen;
+ if (unlikely(oob))
+ ops->oobretlen = ops->ooblen;
+
+err_out:
+ nand_deselect_target(chip);
+ return ret;
+}
+
+/**
+ * nand_write_oob - [MTD Interface] NAND write data and/or out-of-band
+ * @mtd: MTD device structure
+ * @to: offset to write to
+ * @ops: oob operation description structure
+ */
+static int nand_write_oob(struct mtd_info *mtd, loff_t to,
+ struct mtd_oob_ops *ops)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ int ret;
+
+ ops->retlen = 0;
+
+ ret = nand_get_device(chip);
+ if (ret)
+ return ret;
+
+ switch (ops->mode) {
+ case MTD_OPS_PLACE_OOB:
+ case MTD_OPS_AUTO_OOB:
+ case MTD_OPS_RAW:
+ break;
+
+ default:
+ goto out;
+ }
+
+ if (!ops->datbuf)
+ ret = nand_do_write_oob(chip, to, ops);
+ else
+ ret = nand_do_write_ops(chip, to, ops);
+
+out:
+ nand_release_device(chip);
+ return ret;
+}
+
+/**
+ * nand_erase - [MTD Interface] erase block(s)
+ * @mtd: MTD device structure
+ * @instr: erase instruction
+ *
+ * Erase one ore more blocks.
+ */
+static int nand_erase(struct mtd_info *mtd, struct erase_info *instr)
+{
+ return nand_erase_nand(mtd_to_nand(mtd), instr, 0);
+}
+
+/**
+ * nand_erase_nand - [INTERN] erase block(s)
+ * @chip: NAND chip object
+ * @instr: erase instruction
+ * @allowbbt: allow erasing the bbt area
+ *
+ * Erase one ore more blocks.
+ */
+int nand_erase_nand(struct nand_chip *chip, struct erase_info *instr,
+ int allowbbt)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+
+ int page, pages_per_block, ret, chipnr;
+ loff_t len;
+
+ pr_debug("%s: start = 0x%012llx, len = %llu\n",
+ __func__, (unsigned long long)instr->addr,
+ (unsigned long long)instr->len);
+
+ if (check_offs_len(chip, instr->addr, instr->len))
+ return -EINVAL;
+
+ /* Grab the lock and see if the device is available */
+ ret = nand_get_device(chip);
+ if (ret)
+ return ret;
+
+ /* Shift to get first page */
+ page = (int)(instr->addr >> chip->page_shift);
+ chipnr = (int)(instr->addr >> chip->chip_shift);
+
+ /* Calculate pages in each block */
+ pages_per_block = 1 << (chip->phys_erase_shift - chip->page_shift);
+
+ /* Select the NAND device */
+ nand_select_target(chip, chipnr);
+
+ /* Check, if it is write protected */
+ if (nand_check_wp(chip)) {
+ pr_debug("%s: device is write protected!\n",
+ __func__);
+ ret = -EIO;
+ goto erase_exit;
+ }
+
+ /* Loop through the pages */
+ len = instr->len;
+
+ while (len) {
+ /* Check if we have a bad block, we do not erase bad blocks! */
+ if (!mtd->allow_erasebad &&
+ nand_block_checkbad(chip, ((loff_t) page) <<
+ chip->page_shift, allowbbt)) {
+ pr_warn("%s: attempt to erase a bad block at page 0x%08x\n",
+ __func__, page);
+ ret = -EIO;
+ goto erase_exit;
+ }
+
+ /*
+ * Invalidate the page cache, if we erase the block which
+ * contains the current cached page.
+ */
+ if (page <= chip->pagecache.page && chip->pagecache.page <
+ (page + pages_per_block))
+ chip->pagecache.page = -1;
+
+ ret = nand_erase_op(chip, (page & chip->pagemask) >>
+ (chip->phys_erase_shift - chip->page_shift));
+ if (ret) {
+ pr_debug("%s: failed erase, page 0x%08x\n",
+ __func__, page);
+ instr->fail_addr =
+ ((loff_t)page << chip->page_shift);
+ goto erase_exit;
+ }
+
+ /* Increment page address and decrement length */
+ len -= (1ULL << chip->phys_erase_shift);
+ page += pages_per_block;
+
+ /* Check, if we cross a chip boundary */
+ if (len && !(page & chip->pagemask)) {
+ chipnr++;
+ nand_deselect_target(chip);
+ nand_select_target(chip, chipnr);
+ }
+ }
+
+ ret = 0;
+erase_exit:
+
+ /* Deselect and wake up anyone waiting on the device */
+ nand_deselect_target(chip);
+ nand_release_device(chip);
+
+ /* Return more or less happy */
+ return ret;
+}
+
+/**
+ * nand_sync - [MTD Interface] sync
+ * @mtd: MTD device structure
+ *
+ * Sync is actually a wait for chip ready function.
+ */
+static void nand_sync(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+
+ pr_debug("%s: called\n", __func__);
+
+ /* Grab the lock and see if the device is available */
+ WARN_ON(nand_get_device(chip));
+ /* Release it and go back */
+ nand_release_device(chip);
+}
+
+/**
+ * nand_block_isbad - [MTD Interface] Check if block at offset is bad
+ * @mtd: MTD device structure
+ * @offs: offset relative to mtd start
+ */
+static int nand_block_isbad(struct mtd_info *mtd, loff_t offs)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ int chipnr = (int)(offs >> chip->chip_shift);
+ int ret;
+
+ /* Select the NAND device */
+ ret = nand_get_device(chip);
+ if (ret)
+ return ret;
+
+ nand_select_target(chip, chipnr);
+
+ ret = nand_block_checkbad(chip, offs, 0);
+
+ nand_deselect_target(chip);
+ nand_release_device(chip);
+
+ return ret;
+}
+
+/**
+ * nand_block_markbad - [MTD Interface] Mark block at the given offset as bad
+ * @mtd: MTD device structure
+ * @ofs: offset relative to mtd start
+ */
+static int nand_block_markbad(struct mtd_info *mtd, loff_t ofs)
+{
+ int ret;
+
+ ret = nand_block_isbad(mtd, ofs);
+ if (ret) {
+ /* If it was bad already, return success and do nothing */
+ if (ret > 0)
+ return 0;
+ return ret;
+ }
+
+ return nand_block_markbad_lowlevel(mtd_to_nand(mtd), ofs);
+}
+
+/**
+ * nand_block_markbad - [MTD Interface] Mark block at the given offset as bad
+ * @mtd: MTD device structure
+ * @ofs: offset relative to mtd start
+ */
+static int nand_block_markgood(struct mtd_info *mtd, loff_t ofs)
+{
+ int ret;
+
+ ret = nand_block_isbad(mtd, ofs);
+ if (ret < 0)
+ return ret;
+
+ if (!ret)
+ /* If it was good already, return success and do nothing */
+ return 0;
+
+ return nand_block_markgood_lowlevel(mtd_to_nand(mtd), ofs);
+}
+
+/**
+ * nand_lock - [MTD Interface] Lock the NAND flash
+ * @mtd: MTD device structure
+ * @ofs: offset byte address
+ * @len: number of bytes to lock (must be a multiple of block/page size)
+ */
+static int nand_lock(struct mtd_info *mtd, loff_t ofs, size_t len)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+
+ if (!chip->ops.lock_area)
+ return -ENOTSUPP;
+
+ return chip->ops.lock_area(chip, ofs, len);
+}
+
+/**
+ * nand_unlock - [MTD Interface] Unlock the NAND flash
+ * @mtd: MTD device structure
+ * @ofs: offset byte address
+ * @len: number of bytes to unlock (must be a multiple of block/page size)
+ */
+static int nand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+
+ if (!chip->ops.unlock_area)
+ return -ENOTSUPP;
+
+ return chip->ops.unlock_area(chip, ofs, len);
+}
+
+/* Set default functions */
+static void nand_set_defaults(struct nand_chip *chip)
+{
+ /* If no controller is provided, use the dummy, legacy one. */
+ if (!chip->controller) {
+ chip->controller = &chip->legacy.dummy_controller;
+ nand_controller_init(chip->controller);
+ }
+
+ nand_legacy_set_defaults(chip);
+
+ if (!chip->buf_align)
+ chip->buf_align = 1;
+}
+
+/* Sanitize ONFI strings so we can safely print them */
+void sanitize_string(uint8_t *s, size_t len)
+{
+ ssize_t i;
+
+ /* Null terminate */
+ s[len - 1] = 0;
+
+ /* Remove non printable chars */
+ for (i = 0; i < len - 1; i++) {
+ if (s[i] < ' ' || s[i] > 127)
+ s[i] = '?';
+ }
+
+ /* Remove trailing spaces */
+ strim(s);
+}
+
+/*
+ * nand_id_has_period - Check if an ID string has a given wraparound period
+ * @id_data: the ID string
+ * @arrlen: the length of the @id_data array
+ * @period: the period of repitition
+ *
+ * Check if an ID string is repeated within a given sequence of bytes at
+ * specific repetition interval period (e.g., {0x20,0x01,0x7F,0x20} has a
+ * period of 3). This is a helper function for nand_id_len(). Returns non-zero
+ * if the repetition has a period of @period; otherwise, returns zero.
+ */
+static int nand_id_has_period(u8 *id_data, int arrlen, int period)
+{
+ int i, j;
+ for (i = 0; i < period; i++)
+ for (j = i + period; j < arrlen; j += period)
+ if (id_data[i] != id_data[j])
+ return 0;
+ return 1;
+}
+
+/*
+ * nand_id_len - Get the length of an ID string returned by CMD_READID
+ * @id_data: the ID string
+ * @arrlen: the length of the @id_data array
+
+ * Returns the length of the ID string, according to known wraparound/trailing
+ * zero patterns. If no pattern exists, returns the length of the array.
+ */
+static int nand_id_len(u8 *id_data, int arrlen)
+{
+ int last_nonzero, period;
+
+ /* Find last non-zero byte */
+ for (last_nonzero = arrlen - 1; last_nonzero >= 0; last_nonzero--)
+ if (id_data[last_nonzero])
+ break;
+
+ /* All zeros */
+ if (last_nonzero < 0)
+ return 0;
+
+ /* Calculate wraparound period */
+ for (period = 1; period < arrlen; period++)
+ if (nand_id_has_period(id_data, arrlen, period))
+ break;
+
+ /* There's a repeated pattern */
+ if (period < arrlen)
+ return period;
+
+ /* There are trailing zeros */
+ if (last_nonzero < arrlen - 1)
+ return last_nonzero + 1;
+
+ /* No pattern detected */
+ return arrlen;
+}
+
+/* Extract the bits of per cell from the 3rd byte of the extended ID */
+static int nand_get_bits_per_cell(u8 cellinfo)
+{
+ int bits;
+
+ bits = cellinfo & NAND_CI_CELLTYPE_MSK;
+ bits >>= NAND_CI_CELLTYPE_SHIFT;
+ return bits + 1;
+}
+
+/*
+ * Many new NAND share similar device ID codes, which represent the size of the
+ * chip. The rest of the parameters must be decoded according to generic or
+ * manufacturer-specific "extended ID" decoding patterns.
+ */
+void nand_decode_ext_id(struct nand_chip *chip)
+{
+ struct nand_memory_organization *memorg;
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ int extid;
+ u8 *id_data = chip->id.data;
+
+ memorg = nanddev_get_memorg(&chip->base);
+
+ /* The 3rd id byte holds MLC / multichip data */
+ memorg->bits_per_cell = nand_get_bits_per_cell(id_data[2]);
+ /* The 4th id byte is the important one */
+ extid = id_data[3];
+
+ /* Calc pagesize */
+ memorg->pagesize = 1024 << (extid & 0x03);
+ mtd->writesize = memorg->pagesize;
+ extid >>= 2;
+ /* Calc oobsize */
+ memorg->oobsize = (8 << (extid & 0x01)) * (mtd->writesize >> 9);
+ mtd->oobsize = memorg->oobsize;
+ extid >>= 2;
+ /* Calc blocksize. Blocksize is multiples of 64KiB */
+ memorg->pages_per_eraseblock = ((64 * 1024) << (extid & 0x03)) /
+ memorg->pagesize;
+ mtd->erasesize = (64 * 1024) << (extid & 0x03);
+ extid >>= 2;
+ /* Get buswidth information */
+ if (extid & 0x1)
+ chip->options |= NAND_BUSWIDTH_16;
+}
+EXPORT_SYMBOL_GPL(nand_decode_ext_id);
+
+/*
+ * Old devices have chip data hardcoded in the device ID table. nand_decode_id
+ * decodes a matching ID table entry and assigns the MTD size parameters for
+ * the chip.
+ */
+static void nand_decode_id(struct nand_chip *chip, struct nand_flash_dev *type)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ struct nand_memory_organization *memorg;
+
+ memorg = nanddev_get_memorg(&chip->base);
+
+ memorg->pages_per_eraseblock = type->erasesize / type->pagesize;
+ mtd->erasesize = type->erasesize;
+ memorg->pagesize = type->pagesize;
+ mtd->writesize = memorg->pagesize;
+ memorg->oobsize = memorg->pagesize / 32;
+ mtd->oobsize = memorg->oobsize;
+
+ /* All legacy ID NAND are small-page, SLC */
+ memorg->bits_per_cell = 1;
+}
+
+/*
+ * Set the bad block marker/indicator (BBM/BBI) patterns according to some
+ * heuristic patterns using various detected parameters (e.g., manufacturer,
+ * page size, cell-type information).
+ */
+static void nand_decode_bbm_options(struct nand_chip *chip)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+
+ /* Set the bad block position */
+ if (mtd->writesize > 512 || (chip->options & NAND_BUSWIDTH_16))
+ chip->badblockpos = NAND_BBM_POS_LARGE;
+ else
+ chip->badblockpos = NAND_BBM_POS_SMALL;
+}
+
+static inline bool is_full_id_nand(struct nand_flash_dev *type)
+{
+ return type->id_len;
+}
+
+static bool find_full_id_nand(struct nand_chip *chip,
+ struct nand_flash_dev *type)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ struct nand_memory_organization *memorg;
+ u8 *id_data = chip->id.data;
+
+ memorg = nanddev_get_memorg(&chip->base);
+
+ if (!strncmp(type->id, id_data, type->id_len)) {
+ memorg->pagesize = type->pagesize;
+ mtd->writesize = memorg->pagesize;
+ memorg->pages_per_eraseblock = type->erasesize /
+ type->pagesize;
+ mtd->erasesize = type->erasesize;
+ memorg->oobsize = type->oobsize;
+ mtd->oobsize = memorg->oobsize;
+
+ memorg->bits_per_cell = nand_get_bits_per_cell(id_data[2]);
+ memorg->eraseblocks_per_lun =
+ DIV_ROUND_DOWN_ULL((u64)type->chipsize << 20,
+ memorg->pagesize *
+ memorg->pages_per_eraseblock);
+ chip->options |= type->options;
+ chip->base.eccreq.strength = NAND_ECC_STRENGTH(type);
+ chip->base.eccreq.step_size = NAND_ECC_STEP(type);
+
+ chip->parameters.model = strdup(type->name);
+ if (!chip->parameters.model)
+ return false;
+
+ return true;
}
+ return false;
+}
- if (!ops->datbuf)
- ret = nand_do_write_oob(chip, to, ops);
- else
- ret = nand_do_write_ops(chip, to, ops);
+/*
+ * Manufacturer detection. Only used when the NAND is not ONFI or JEDEC
+ * compliant and does not have a full-id or legacy-id entry in the nand_ids
+ * table.
+ */
+static void nand_manufacturer_detect(struct nand_chip *chip)
+{
+ /*
+ * Try manufacturer detection if available and use
+ * nand_decode_ext_id() otherwise.
+ */
+ if (chip->manufacturer.desc && chip->manufacturer.desc->ops &&
+ chip->manufacturer.desc->ops->detect) {
+ struct nand_memory_organization *memorg;
-out:
- nand_release_device(chip);
- return ret;
+ memorg = nanddev_get_memorg(&chip->base);
+
+ /* The 3rd id byte holds MLC / multichip data */
+ memorg->bits_per_cell = nand_get_bits_per_cell(chip->id.data[2]);
+ chip->manufacturer.desc->ops->detect(chip);
+ } else {
+ nand_decode_ext_id(chip);
+ }
}
-/**
- * single_erase_cmd - [GENERIC] NAND standard block erase command function
- * @mtd: MTD device structure
- * @page: the page address of the block which will be erased
- *
- * Standard erase command for NAND chips.
+/*
+ * Manufacturer initialization. This function is called for all NANDs including
+ * ONFI and JEDEC compliant ones.
+ * Manufacturer drivers should put all their specific initialization code in
+ * their ->init() hook.
*/
-static void single_erase_cmd(struct nand_chip *chip, int page)
+static int nand_manufacturer_init(struct nand_chip *chip)
{
- if (!IS_ENABLED(CONFIG_MTD_WRITE))
- return;
+ if (!chip->manufacturer.desc || !chip->manufacturer.desc->ops ||
+ !chip->manufacturer.desc->ops->init)
+ return 0;
- /* Send commands to erase a block */
- chip->legacy.cmdfunc(chip, NAND_CMD_ERASE1, -1, page);
- chip->legacy.cmdfunc(chip, NAND_CMD_ERASE2, -1, -1);
+ return chip->manufacturer.desc->ops->init(chip);
}
-/**
- * nand_erase - [MTD Interface] erase block(s)
- * @mtd: MTD device structure
- * @instr: erase instruction
- *
- * Erase one ore more blocks.
+/*
+ * Manufacturer cleanup. This function is called for all NANDs including
+ * ONFI and JEDEC compliant ones.
+ * Manufacturer drivers should put all their specific cleanup code in their
+ * ->cleanup() hook.
*/
-static int nand_erase(struct mtd_info *mtd, struct erase_info *instr)
+static void nand_manufacturer_cleanup(struct nand_chip *chip)
{
- struct nand_chip *chip = mtd_to_nand(mtd);
-
- if (!IS_ENABLED(CONFIG_MTD_WRITE))
- return -ENOTSUPP;
+ /* Release manufacturer private data */
+ if (chip->manufacturer.desc && chip->manufacturer.desc->ops &&
+ chip->manufacturer.desc->ops->cleanup)
+ chip->manufacturer.desc->ops->cleanup(chip);
+}
- return nand_erase_nand(chip, instr, 0);
+static const char *
+nand_manufacturer_name(const struct nand_manufacturer_desc *manufacturer_desc)
+{
+ return manufacturer_desc ? manufacturer_desc->name : "Unknown";
}
-/**
- * nand_erase_nand - [INTERN] erase block(s)
- * @mtd: MTD device structure
- * @instr: erase instruction
- * @allowbbt: allow erasing the bbt area
- *
- * Erase one ore more blocks.
+/*
+ * Get the flash and manufacturer id and lookup if the type is supported.
*/
-int nand_erase_nand(struct nand_chip *chip, struct erase_info *instr,
- int allowbbt)
+static int nand_detect(struct nand_chip *chip, struct nand_flash_dev *type)
{
- int page, status, pages_per_block, ret, chipnr;
- loff_t len;
+ const struct nand_manufacturer_desc *manufacturer_desc;
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ struct nand_memory_organization *memorg;
+ int busw, ret;
+ u8 *id_data = chip->id.data;
+ u8 maf_id, dev_id;
+ u64 targetsize;
- if (!IS_ENABLED(CONFIG_MTD_WRITE))
- return -ENOTSUPP;
+ /*
+ * Let's start by initializing memorg fields that might be left
+ * unassigned by the ID-based detection logic.
+ */
+ memorg = nanddev_get_memorg(&chip->base);
+ memorg->planes_per_lun = 1;
+ memorg->luns_per_target = 1;
- pr_debug("%s: start = 0x%012llx, len = %llu\n",
- __func__, (unsigned long long)instr->addr,
- (unsigned long long)instr->len);
+ /*
+ * Reset the chip, required by some chips (e.g. Micron MT29FxGxxxxx)
+ * after power-up.
+ */
+ ret = nand_reset(chip, 0);
+ if (ret)
+ return ret;
- if (check_offs_len(chip, instr->addr, instr->len))
- return -EINVAL;
+ /* Select the device */
+ nand_select_target(chip, 0);
- /* Grab the lock and see if the device is available */
- nand_get_device(chip, FL_ERASING);
+ /* Send the command for reading device ID */
+ ret = nand_readid_op(chip, 0, id_data, 2);
+ if (ret)
+ return ret;
- /* Shift to get first page */
- page = (int)(instr->addr >> chip->page_shift);
- chipnr = (int)(instr->addr >> chip->chip_shift);
+ /* Read manufacturer and device IDs */
+ maf_id = id_data[0];
+ dev_id = id_data[1];
- /* Calculate pages in each block */
- pages_per_block = 1 << (chip->phys_erase_shift - chip->page_shift);
+ /*
+ * Try again to make sure, as some systems the bus-hold or other
+ * interface concerns can cause random data which looks like a
+ * possibly credible NAND flash to appear. If the two results do
+ * not match, ignore the device completely.
+ */
- /* Select the NAND device */
- chip->legacy.select_chip(chip, chipnr);
+ /* Read entire ID string */
+ ret = nand_readid_op(chip, 0, id_data, sizeof(chip->id.data));
+ if (ret)
+ return ret;
- /* Check, if it is write protected */
- if (nand_check_wp(chip)) {
- pr_debug("%s: device is write protected!\n",
- __func__);
- ret = -EIO;
- goto erase_exit;
+ if (id_data[0] != maf_id || id_data[1] != dev_id) {
+ pr_info("second ID read did not match %02x,%02x against %02x,%02x\n",
+ maf_id, dev_id, id_data[0], id_data[1]);
+ return -ENODEV;
}
- /* Loop through the pages */
- len = instr->len;
+ chip->id.len = nand_id_len(id_data, ARRAY_SIZE(chip->id.data));
- while (len) {
- /* Check if we have a bad block, we do not erase bad blocks! */
- if (!chip->mtd.allow_erasebad &&
- nand_block_checkbad(chip, ((loff_t) page) <<
- chip->page_shift, 0, allowbbt)) {
- pr_warn("%s: attempt to erase a bad block at page 0x%08x\n",
- __func__, page);
- ret = -EIO;
- goto erase_exit;
- }
+ /* Try to identify manufacturer */
+ manufacturer_desc = nand_get_manufacturer_desc(maf_id);
+ chip->manufacturer.desc = manufacturer_desc;
- /*
- * Invalidate the page cache, if we erase the block which
- * contains the current cached page.
- */
- if (page <= chip->pagebuf && chip->pagebuf <
- (page + pages_per_block))
- chip->pagebuf = -1;
+ if (!type)
+ type = nand_flash_ids;
- single_erase_cmd(chip, page & chip->pagemask);
+ /*
+ * Save the NAND_BUSWIDTH_16 flag before letting auto-detection logic
+ * override it.
+ * This is required to make sure initial NAND bus width set by the
+ * NAND controller driver is coherent with the real NAND bus width
+ * (extracted by auto-detection code).
+ */
+ busw = chip->options & NAND_BUSWIDTH_16;
- status = chip->legacy.waitfunc(chip);
+ /*
+ * The flag is only set (never cleared), reset it to its default value
+ * before starting auto-detection.
+ */
+ chip->options &= ~NAND_BUSWIDTH_16;
- /* See if block erase succeeded */
- if (status & NAND_STATUS_FAIL) {
- pr_debug("%s: failed erase, page 0x%08x\n",
- __func__, page);
- ret = -EIO;
- instr->fail_addr =
- ((loff_t)page << chip->page_shift);
- goto erase_exit;
+ for (; type->name != NULL; type++) {
+ if (is_full_id_nand(type)) {
+ if (find_full_id_nand(chip, type))
+ goto ident_done;
+ } else if (dev_id == type->dev_id) {
+ break;
}
+ }
- /* Increment page address and decrement length */
- len -= (1 << chip->phys_erase_shift);
- page += pages_per_block;
+ if (!type->name || !type->pagesize) {
+ /* Check if the chip is ONFI compliant */
+ ret = nand_onfi_detect(chip);
+ if (ret < 0)
+ return ret;
+ else if (ret)
+ goto ident_done;
- /* Check, if we cross a chip boundary */
- if (len && !(page & chip->pagemask)) {
- chipnr++;
- chip->legacy.select_chip(chip, -1);
- chip->legacy.select_chip(chip, chipnr);
- }
+ /* Check if the chip is JEDEC compliant */
+ ret = nand_jedec_detect(chip);
+ if (ret < 0)
+ return ret;
+ else if (ret)
+ goto ident_done;
}
- ret = 0;
+ if (!type->name)
+ return -ENODEV;
-erase_exit:
- /* Deselect and wake up anyone waiting on the device */
- chip->legacy.select_chip(chip, -1);
- nand_release_device(chip);
+ chip->parameters.model = strdup(type->name);
+ if (!chip->parameters.model)
+ return -ENOMEM;
- /* Return more or less happy */
- return ret;
-}
+ if (!type->pagesize)
+ nand_manufacturer_detect(chip);
+ else
+ nand_decode_id(chip, type);
-/**
- * nand_sync - [MTD Interface] sync
- * @mtd: MTD device structure
- *
- * Sync is actually a wait for chip ready function.
- */
-static void nand_sync(struct mtd_info *mtd)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
+ /* Get chip options */
+ chip->options |= type->options;
- pr_debug("%s: called\n", __func__);
+ memorg->eraseblocks_per_lun =
+ DIV_ROUND_DOWN_ULL((u64)type->chipsize << 20,
+ memorg->pagesize *
+ memorg->pages_per_eraseblock);
- /* Grab the lock and see if the device is available */
- nand_get_device(chip, FL_SYNCING);
- /* Release it and go back */
- nand_release_device(chip);
-}
+ident_done:
+ if (!mtd->name)
+ mtd->name = strdup(chip->parameters.model);
-/**
- * nand_block_isbad - [MTD Interface] Check if block at offset is bad
- * @mtd: MTD device structure
- * @offs: offset relative to mtd start
- */
-static int nand_block_isbad(struct mtd_info *mtd, loff_t offs)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
+ if (chip->options & NAND_BUSWIDTH_AUTO) {
+ WARN_ON(busw & NAND_BUSWIDTH_16);
+ nand_set_defaults(chip);
+ } else if (busw != (chip->options & NAND_BUSWIDTH_16)) {
+ /*
+ * Check, if buswidth is correct. Hardware drivers should set
+ * chip correct!
+ */
+ pr_info("device found, Manufacturer ID: 0x%02x, Chip ID: 0x%02x\n",
+ maf_id, dev_id);
+ pr_info("%s %s\n", nand_manufacturer_name(manufacturer_desc),
+ mtd->name);
+ pr_warn("bus width %d instead of %d bits\n", busw ? 16 : 8,
+ (chip->options & NAND_BUSWIDTH_16) ? 16 : 8);
+ ret = -EINVAL;
- return nand_block_checkbad(chip, offs, 1, 0);
-}
+ goto free_detect_allocation;
+ }
-/**
- * nand_block_markbad - [MTD Interface] Mark block at the given offset as bad
- * @mtd: MTD device structure
- * @ofs: offset relative to mtd start
- */
-static int nand_block_markbad(struct mtd_info *mtd, loff_t ofs)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- int ret;
+ nand_decode_bbm_options(chip);
- if (!IS_ENABLED(CONFIG_MTD_WRITE))
- return -ENOTSUPP;
+ /* Calculate the address shift from the page size */
+ chip->page_shift = ffs(mtd->writesize) - 1;
+ /* Convert chipsize to number of pages per chip -1 */
+ targetsize = nanddev_target_size(&chip->base);
+ chip->pagemask = (targetsize >> chip->page_shift) - 1;
- ret = nand_block_isbad(mtd, ofs);
- if (ret) {
- /* If it was bad already, return success and do nothing */
- if (ret > 0)
- return 0;
- return ret;
+ chip->bbt_erase_shift = chip->phys_erase_shift =
+ ffs(mtd->erasesize) - 1;
+ if (targetsize & 0xffffffff)
+ chip->chip_shift = ffs((unsigned)targetsize) - 1;
+ else {
+ chip->chip_shift = ffs((unsigned)(targetsize >> 32));
+ chip->chip_shift += 32 - 1;
}
- return nand_block_markbad_lowlevel(chip, ofs);
-}
+ if (chip->chip_shift - chip->page_shift > 16)
+ chip->options |= NAND_ROW_ADDR_3;
-/**
- * nand_block_markgood - [MTD Interface] Mark block at the given offset as good
- * @mtd: MTD device structure
- * @ofs: offset relative to mtd start
- */
-static int nand_block_markgood(struct mtd_info *mtd, loff_t ofs)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- int ret;
+ chip->badblockbits = 8;
- if (!IS_ENABLED(CONFIG_MTD_WRITE))
- return -ENOTSUPP;
+ nand_legacy_adjust_cmdfunc(chip);
- ret = nand_block_isbad(mtd, ofs);
- if (ret < 0)
- return ret;
+ pr_info("device found, Manufacturer ID: 0x%02x, Chip ID: 0x%02x\n",
+ maf_id, dev_id);
+ pr_info("%s %s\n", nand_manufacturer_name(manufacturer_desc),
+ chip->parameters.model);
+ pr_info("%d MiB, %s, erase size: %d KiB, page size: %d, OOB size: %d\n",
+ (int)(targetsize >> 20), nand_is_slc(chip) ? "SLC" : "MLC",
+ mtd->erasesize >> 10, mtd->writesize, mtd->oobsize);
+ return 0;
- /* If it was good already, return success and do nothing */
- if (!ret)
- return 0;
+free_detect_allocation:
+ kfree(chip->parameters.model);
- return nand_block_markgood_lowlevel(chip, ofs);
+ return ret;
}
-/**
- * nand_onfi_set_features- [REPLACEABLE] set features for ONFI nand
- * @mtd: MTD device structure
- * @chip: nand chip info structure
- * @addr: feature address.
- * @subfeature_param: the subfeature parameters, a four bytes array.
- */
-static int nand_onfi_set_features(struct nand_chip *chip,
- int addr, uint8_t *subfeature_param)
+static const char * const nand_ecc_algos[] = {
+ [NAND_ECC_HAMMING] = "hamming",
+ [NAND_ECC_BCH] = "bch",
+ [NAND_ECC_RS] = "rs",
+};
+
+static enum nand_ecc_algo of_get_nand_ecc_algo(struct device_node *np)
{
- int status;
+ enum nand_ecc_algo ecc_algo;
+ const char *pm;
+ int err;
+
+ err = of_property_read_string(np, "nand-ecc-algo", &pm);
+ if (!err) {
+ for (ecc_algo = NAND_ECC_HAMMING;
+ ecc_algo < ARRAY_SIZE(nand_ecc_algos);
+ ecc_algo++) {
+ if (!strcasecmp(pm, nand_ecc_algos[ecc_algo]))
+ return ecc_algo;
+ }
+ }
- if (!chip->onfi_version ||
- !(le16_to_cpu(chip->onfi_params.opt_cmd)
- & ONFI_OPT_CMD_SET_GET_FEATURES))
- return -EINVAL;
+ /*
+ * For backward compatibility we also read "nand-ecc-mode" checking
+ * for some obsoleted values that were specifying ECC algorithm.
+ */
+ err = of_property_read_string(np, "nand-ecc-mode", &pm);
+ if (!err) {
+ if (!strcasecmp(pm, "soft"))
+ return NAND_ECC_HAMMING;
+ else if (!strcasecmp(pm, "soft_bch"))
+ return NAND_ECC_BCH;
+ }
- chip->legacy.cmdfunc(chip, NAND_CMD_SET_FEATURES, addr, -1);
- chip->legacy.write_buf(chip, subfeature_param, ONFI_SUBFEATURE_PARAM_LEN);
- status = chip->legacy.waitfunc(chip);
- if (status & NAND_STATUS_FAIL)
- return -EIO;
- return 0;
+ return NAND_ECC_UNKNOWN;
}
-/**
- * nand_onfi_get_features- [REPLACEABLE] get features for ONFI nand
- * @mtd: MTD device structure
- * @chip: nand chip info structure
- * @addr: feature address.
- * @subfeature_param: the subfeature parameters, a four bytes array.
- */
-static int nand_onfi_get_features(struct nand_chip *chip,
- int addr, uint8_t *subfeature_param)
+static int nand_dt_init(struct nand_chip *chip)
{
- if (!chip->onfi_version ||
- !(le16_to_cpu(chip->onfi_params.opt_cmd)
- & ONFI_OPT_CMD_SET_GET_FEATURES))
- return -EINVAL;
+ struct device_node *dn = nand_get_flash_node(chip);
+ enum nand_ecc_algo ecc_algo;
+ int ecc_mode, ecc_strength, ecc_step;
- /* clear the sub feature parameters */
- memset(subfeature_param, 0, ONFI_SUBFEATURE_PARAM_LEN);
+ if (!dn)
+ return 0;
- chip->legacy.cmdfunc(chip, NAND_CMD_GET_FEATURES, addr, -1);
- chip->legacy.read_buf(chip, subfeature_param, ONFI_SUBFEATURE_PARAM_LEN);
- return 0;
-}
+ if (of_get_nand_bus_width(dn) == 16)
+ chip->options |= NAND_BUSWIDTH_16;
-/* Set default functions */
-static void nand_set_defaults(struct nand_chip *chip, int busw)
-{
- /* check for proper chip_delay setup, set 20us if not */
- if (!chip->legacy.chip_delay)
- chip->legacy.chip_delay = 20;
-
- /* check, if a user supplied command function given */
- if (chip->legacy.cmdfunc == NULL)
- chip->legacy.cmdfunc = nand_command;
-
- /* check, if a user supplied wait function given */
- if (chip->legacy.waitfunc == NULL)
- chip->legacy.waitfunc = nand_wait;
-
- if (!chip->legacy.select_chip)
- chip->legacy.select_chip = nand_select_chip;
-
- /* set for ONFI nand */
- if (!chip->legacy.set_features)
- chip->legacy.set_features = nand_onfi_set_features;
- if (!chip->legacy.get_features)
- chip->legacy.get_features = nand_onfi_get_features;
-
- if (!chip->legacy.read_byte)
- chip->legacy.read_byte = busw ? nand_read_byte16 : nand_read_byte;
- if (!chip->legacy.read_word)
- chip->legacy.read_word = nand_read_word;
- if (!chip->legacy.block_bad)
- chip->legacy.block_bad = nand_block_bad;
-#ifdef CONFIG_MTD_WRITE
- if (!chip->legacy.block_markbad)
- chip->legacy.block_markbad = nand_default_block_markbad;
- if (!chip->legacy.write_buf)
- chip->legacy.write_buf = busw ? nand_write_buf16 : nand_write_buf;
-#endif
- if (!chip->legacy.read_buf)
- chip->legacy.read_buf = busw ? nand_read_buf16 : nand_read_buf;
-#ifdef CONFIG_NAND_BBT
- if (!chip->scan_bbt)
- chip->scan_bbt = nand_default_bbt;
-#endif
- if (!chip->controller) {
- chip->controller = &chip->hwcontrol;
- }
+ if (of_property_read_bool(dn, "nand-is-boot-medium"))
+ chip->options |= NAND_IS_BOOT_MEDIUM;
-}
+ if (of_get_nand_on_flash_bbt(dn))
+ chip->bbt_options |= NAND_BBT_USE_FLASH;
-/* Sanitize ONFI strings so we can safely print them */
-static void sanitize_string(uint8_t *s, size_t len)
-{
- ssize_t i;
+ ecc_mode = of_get_nand_ecc_mode(dn);
+ ecc_algo = of_get_nand_ecc_algo(dn);
+ ecc_strength = of_get_nand_ecc_strength(dn);
+ ecc_step = of_get_nand_ecc_step_size(dn);
- /* Null terminate */
- s[len - 1] = 0;
+ if (ecc_mode >= 0)
+ chip->ecc.mode = ecc_mode;
- /* Remove non printable chars */
- for (i = 0; i < len - 1; i++) {
- if (s[i] < ' ' || s[i] > 127)
- s[i] = '?';
- }
+ if (ecc_algo != NAND_ECC_UNKNOWN)
+ chip->ecc.algo = ecc_algo;
- /* Remove trailing spaces */
- strim(s);
-}
+ if (ecc_strength >= 0)
+ chip->ecc.strength = ecc_strength;
-static u16 onfi_crc16(u16 crc, u8 const *p, size_t len)
-{
- int i;
- while (len--) {
- crc ^= *p++ << 8;
- for (i = 0; i < 8; i++)
- crc = (crc << 1) ^ ((crc & 0x8000) ? 0x8005 : 0);
- }
+ if (ecc_step > 0)
+ chip->ecc.size = ecc_step;
+
+ if (of_property_read_bool(dn, "nand-ecc-maximize"))
+ chip->ecc.options |= NAND_ECC_MAXIMIZE;
- return crc;
+ return 0;
}
-/*
- * Check if the NAND chip is ONFI compliant, returns 1 if it is, 0 otherwise.
+/**
+ * nand_scan_ident - Scan for the NAND device
+ * @chip: NAND chip object
+ * @maxchips: number of chips to scan for
+ * @table: alternative NAND ID table
+ *
+ * This is the first phase of the normal nand_scan() function. It reads the
+ * flash ID and sets up MTD fields accordingly.
+ *
+ * This helper used to be called directly from controller drivers that needed
+ * to tweak some ECC-related parameters before nand_scan_tail(). This separation
+ * prevented dynamic allocations during this phase which was unconvenient and
+ * as been banned for the benefit of the ->init_ecc()/cleanup_ecc() hooks.
*/
-static int nand_flash_detect_onfi(struct nand_chip *chip, int *busw)
+int nand_scan_ident(struct nand_chip *chip, unsigned int maxchips,
+ struct nand_flash_dev *table)
{
struct mtd_info *mtd = nand_to_mtd(chip);
- struct nand_onfi_params *p = &chip->onfi_params;
- int i, j;
- int val;
+ struct nand_memory_organization *memorg;
+ int nand_maf_id, nand_dev_id;
+ unsigned int i;
+ int ret;
- /* Try ONFI for unknown chip or LP */
- chip->legacy.cmdfunc(chip, NAND_CMD_READID, 0x20, -1);
- if (chip->legacy.read_byte(chip) != 'O' || chip->legacy.read_byte(chip) != 'N' ||
- chip->legacy.read_byte(chip) != 'F' || chip->legacy.read_byte(chip) != 'I')
- return 0;
+ memorg = nanddev_get_memorg(&chip->base);
- chip->legacy.cmdfunc(chip, NAND_CMD_PARAM, 0, -1);
- for (i = 0; i < 3; i++) {
- for (j = 0; j < sizeof(*p); j++)
- ((uint8_t *)p)[j] = chip->legacy.read_byte(chip);
- if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 254) ==
- le16_to_cpu(p->crc)) {
- break;
- }
- }
+ /* Assume all dies are deselected when we enter nand_scan_ident(). */
+ chip->cur_cs = -1;
- if (i == 3) {
- pr_err("Could not find valid ONFI parameter page; aborting\n");
- return 0;
- }
+ mutex_init(&chip->lock);
- /* Check version */
- val = le16_to_cpu(p->revision);
- if (val & (1 << 5))
- chip->onfi_version = 23;
- else if (val & (1 << 4))
- chip->onfi_version = 22;
- else if (val & (1 << 3))
- chip->onfi_version = 21;
- else if (val & (1 << 2))
- chip->onfi_version = 20;
- else if (val & (1 << 1))
- chip->onfi_version = 10;
-
- if (!chip->onfi_version) {
- pr_info("%s: unsupported ONFI version: %d\n", __func__, val);
- return 0;
+ /* Enforce the right timings for reset/detection */
+ chip->current_interface_config = nand_get_reset_interface_config();
+
+ if (IS_ENABLED(CONFIG_OFTREE)) {
+ ret = nand_dt_init(chip);
+ if (ret)
+ return ret;
}
- sanitize_string(p->manufacturer, sizeof(p->manufacturer));
- sanitize_string(p->model, sizeof(p->model));
- if (!mtd->name)
- mtd->name = p->model;
- mtd->writesize = le32_to_cpu(p->byte_per_page);
- mtd->erasesize = le32_to_cpu(p->pages_per_block) * mtd->writesize;
- mtd->oobsize = le16_to_cpu(p->spare_bytes_per_page);
- chip->chipsize = le32_to_cpu(p->blocks_per_lun);
- chip->chipsize *= (uint64_t)mtd->erasesize * p->lun_count;
- chip->bits_per_cell = p->bits_per_cell;
-
- *busw = 0;
- if (le16_to_cpu(p->features) & 1)
- *busw = NAND_BUSWIDTH_16;
-
- pr_info("ONFI flash detected\n");
- return 1;
-}
+ if (!mtd->name && mtd->dev.parent)
+ mtd->name = strdup(dev_name(mtd->dev.parent));
-/*
- * nand_id_has_period - Check if an ID string has a given wraparound period
- * @id_data: the ID string
- * @arrlen: the length of the @id_data array
- * @period: the period of repitition
- *
- * Check if an ID string is repeated within a given sequence of bytes at
- * specific repetition interval period (e.g., {0x20,0x01,0x7F,0x20} has a
- * period of 3). This is a helper function for nand_id_len(). Returns non-zero
- * if the repetition has a period of @period; otherwise, returns zero.
- */
-static int nand_id_has_period(u8 *id_data, int arrlen, int period)
-{
- int i, j;
- for (i = 0; i < period; i++)
- for (j = i + period; j < arrlen; j += period)
- if (id_data[i] != id_data[j])
- return 0;
- return 1;
-}
+ /* Set the default functions */
+ nand_set_defaults(chip);
-/*
- * nand_id_len - Get the length of an ID string returned by CMD_READID
- * @id_data: the ID string
- * @arrlen: the length of the @id_data array
+ ret = nand_legacy_check_hooks(chip);
+ if (ret)
+ return ret;
- * Returns the length of the ID string, according to known wraparound/trailing
- * zero patterns. If no pattern exists, returns the length of the array.
- */
-static int nand_id_len(u8 *id_data, int arrlen)
-{
- int last_nonzero, period;
+ memorg->ntargets = maxchips;
- /* Find last non-zero byte */
- for (last_nonzero = arrlen - 1; last_nonzero >= 0; last_nonzero--)
- if (id_data[last_nonzero])
- break;
+ /* Read the flash type */
+ ret = nand_detect(chip, table);
+ if (ret) {
+ if (!(chip->options & NAND_SCAN_SILENT_NODEV))
+ pr_warn("No NAND device found\n");
+ nand_deselect_target(chip);
+ return ret;
+ }
- /* All zeros */
- if (last_nonzero < 0)
- return 0;
+ nand_maf_id = chip->id.data[0];
+ nand_dev_id = chip->id.data[1];
- /* Calculate wraparound period */
- for (period = 1; period < arrlen; period++)
- if (nand_id_has_period(id_data, arrlen, period))
+ nand_deselect_target(chip);
+
+ /* Check for a chip array */
+ for (i = 1; i < maxchips; i++) {
+ u8 id[2];
+
+ /* See comment in nand_get_flash_type for reset */
+ ret = nand_reset(chip, i);
+ if (ret)
break;
- /* There's a repeated pattern */
- if (period < arrlen)
- return period;
+ nand_select_target(chip, i);
+ /* Send the command for reading device ID */
+ ret = nand_readid_op(chip, 0, id, sizeof(id));
+ if (ret)
+ break;
+ /* Read manufacturer and device IDs */
+ if (nand_maf_id != id[0] || nand_dev_id != id[1]) {
+ nand_deselect_target(chip);
+ break;
+ }
+ nand_deselect_target(chip);
+ }
+ if (i > 1)
+ pr_info("%d chips detected\n", i);
- /* There are trailing zeros */
- if (last_nonzero < arrlen - 1)
- return last_nonzero + 1;
+ /* Store the number of chips and calc total size for mtd */
+ memorg->ntargets = i;
+ mtd->size = i * nanddev_target_size(&chip->base);
- /* No pattern detected */
- return arrlen;
+ return 0;
}
-/* Extract the bits of per cell from the 3rd byte of the extended ID */
-static int nand_get_bits_per_cell(u8 cellinfo)
+static void nand_scan_ident_cleanup(struct nand_chip *chip)
{
- int bits;
-
- bits = cellinfo & NAND_CI_CELLTYPE_MSK;
- bits >>= NAND_CI_CELLTYPE_SHIFT;
- return bits + 1;
+ kfree(chip->parameters.model);
+ kfree(chip->parameters.onfi);
}
-/*
- * Many new NAND share similar device ID codes, which represent the size of the
- * chip. The rest of the parameters must be decoded according to generic or
- * manufacturer-specific "extended ID" decoding patterns.
- */
-static void nand_decode_ext_id(struct mtd_info *mtd, struct nand_chip *chip,
- u8 id_data[8], int *busw)
+static int nand_set_ecc_soft_ops(struct nand_chip *chip)
{
- int extid, id_len;
- /* The 3rd id byte holds MLC / multichip data */
- chip->bits_per_cell = nand_get_bits_per_cell(id_data[2]);
- /* The 4th id byte is the important one */
- extid = id_data[3];
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ struct nand_ecc_ctrl *ecc = &chip->ecc;
- id_len = nand_id_len(id_data, 8);
+ if (WARN_ON(ecc->mode != NAND_ECC_SOFT))
+ return -EINVAL;
- /*
- * Field definitions are in the following datasheets:
- * Old style (4,5 byte ID): Samsung K9GAG08U0M (p.32)
- * New Samsung (6 byte ID): Samsung K9GAG08U0F (p.44)
- * Hynix MLC (6 byte ID): Hynix H27UBG8T2B (p.22)
- *
- * Check for ID length, non-zero 6th byte, cell type, and Hynix/Samsung
- * ID to decide what to do.
- */
- if (id_len == 6 && id_data[0] == NAND_MFR_SAMSUNG &&
- !nand_is_slc(chip) && id_data[5] != 0x00) {
- /* Calc pagesize */
- mtd->writesize = 2048 << (extid & 0x03);
- extid >>= 2;
- /* Calc oobsize */
- switch (((extid >> 2) & 0x04) | (extid & 0x03)) {
- case 1:
- mtd->oobsize = 128;
- break;
- case 2:
- mtd->oobsize = 218;
- break;
- case 3:
- mtd->oobsize = 400;
- break;
- case 4:
- mtd->oobsize = 436;
- break;
- case 5:
- mtd->oobsize = 512;
- break;
- case 6:
- default: /* Other cases are "reserved" (unknown) */
- mtd->oobsize = 640;
- break;
+ switch (ecc->algo) {
+ case NAND_ECC_HAMMING:
+ ecc->calculate = nand_calculate_ecc;
+ ecc->correct = nand_correct_data;
+ ecc->read_page = nand_read_page_swecc;
+ ecc->read_subpage = nand_read_subpage;
+ ecc->write_page = nand_write_page_swecc;
+ if (!ecc->read_page_raw)
+ ecc->read_page_raw = nand_read_page_raw;
+ if (!ecc->write_page_raw)
+ ecc->write_page_raw = nand_write_page_raw;
+ ecc->read_oob = nand_read_oob_std;
+ ecc->write_oob = nand_write_oob_std;
+ if (!ecc->size)
+ ecc->size = 256;
+ ecc->bytes = 3;
+ ecc->strength = 1;
+
+ if (IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC))
+ ecc->options |= NAND_ECC_SOFT_HAMMING_SM_ORDER;
+
+ return 0;
+ case NAND_ECC_BCH:
+ if (!mtd_nand_has_bch()) {
+ WARN(1, "CONFIG_MTD_NAND_ECC_SW_BCH not enabled\n");
+ return -EINVAL;
}
- extid >>= 2;
- /* Calc blocksize */
- mtd->erasesize = (128 * 1024) <<
- (((extid >> 1) & 0x04) | (extid & 0x03));
- *busw = 0;
- } else if (id_len == 6 && id_data[0] == NAND_MFR_HYNIX &&
- !nand_is_slc(chip)) {
- unsigned int tmp;
-
- /* Calc pagesize */
- mtd->writesize = 2048 << (extid & 0x03);
- extid >>= 2;
- /* Calc oobsize */
- switch (((extid >> 2) & 0x04) | (extid & 0x03)) {
- case 0:
- mtd->oobsize = 128;
- break;
- case 1:
- mtd->oobsize = 224;
- break;
- case 2:
- mtd->oobsize = 448;
- break;
- case 3:
- mtd->oobsize = 64;
- break;
- case 4:
- mtd->oobsize = 32;
- break;
- case 5:
- mtd->oobsize = 16;
- break;
- default:
- mtd->oobsize = 640;
- break;
+ ecc->calculate = nand_bch_calculate_ecc;
+ ecc->correct = nand_bch_correct_data;
+ ecc->read_page = nand_read_page_swecc;
+ ecc->read_subpage = nand_read_subpage;
+ ecc->write_page = nand_write_page_swecc;
+ if (!ecc->read_page_raw)
+ ecc->read_page_raw = nand_read_page_raw;
+ if (!ecc->write_page_raw)
+ ecc->write_page_raw = nand_write_page_raw;
+ ecc->read_oob = nand_read_oob_std;
+ ecc->write_oob = nand_write_oob_std;
+
+ /*
+ * Board driver should supply ecc.size and ecc.strength
+ * values to select how many bits are correctable.
+ * Otherwise, default to 4 bits for large page devices.
+ */
+ if (!ecc->size && (mtd->oobsize >= 64)) {
+ ecc->size = 512;
+ ecc->strength = 4;
}
- extid >>= 2;
- /* Calc blocksize */
- tmp = ((extid >> 1) & 0x04) | (extid & 0x03);
- if (tmp < 0x03)
- mtd->erasesize = (128 * 1024) << tmp;
- else if (tmp == 0x03)
- mtd->erasesize = 768 * 1024;
- else
- mtd->erasesize = (64 * 1024) << tmp;
- *busw = 0;
- } else {
- /* Calc pagesize */
- mtd->writesize = 1024 << (extid & 0x03);
- extid >>= 2;
- /* Calc oobsize */
- mtd->oobsize = (8 << (extid & 0x01)) *
- (mtd->writesize >> 9);
- extid >>= 2;
- /* Calc blocksize. Blocksize is multiples of 64KiB */
- mtd->erasesize = (64 * 1024) << (extid & 0x03);
- extid >>= 2;
- /* Get buswidth information */
- *busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0;
+
/*
- * Toshiba 24nm raw SLC (i.e., not BENAND) have 32B OOB per
- * 512B page. For Toshiba SLC, we decode the 5th/6th byte as
- * follows:
- * - ID byte 6, bits[2:0]: 100b -> 43nm, 101b -> 32nm,
- * 110b -> 24nm
- * - ID byte 5, bit[7]: 1 -> BENAND, 0 -> raw SLC
+ * if no ecc placement scheme was provided pickup the default
+ * large page one.
*/
- if (id_len >= 6 && id_data[0] == NAND_MFR_TOSHIBA &&
- nand_is_slc(chip) &&
- (id_data[5] & 0x7) == 0x6 /* 24nm */ &&
- !(id_data[4] & 0x80) /* !BENAND */) {
- mtd->oobsize = 32 * mtd->writesize >> 9;
+ if (!mtd->ooblayout) {
+ /* handle large page devices only */
+ if (mtd->oobsize < 64) {
+ WARN(1, "OOB layout is required when using software BCH on small pages\n");
+ return -EINVAL;
+ }
+
+ mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops);
+
}
- }
-}
-/*
- * Old devices have chip data hardcoded in the device ID table. nand_decode_id
- * decodes a matching ID table entry and assigns the MTD size parameters for
- * the chip.
- */
-static void nand_decode_id(struct mtd_info *mtd, struct nand_chip *chip,
- struct nand_flash_dev *type, u8 id_data[8],
- int *busw)
-{
- int maf_id = id_data[0];
+ /*
+ * We can only maximize ECC config when the default layout is
+ * used, otherwise we don't know how many bytes can really be
+ * used.
+ */
+ if (mtd->ooblayout == &nand_ooblayout_lp_ops &&
+ ecc->options & NAND_ECC_MAXIMIZE) {
+ int steps, bytes;
- mtd->erasesize = type->erasesize;
- mtd->writesize = type->pagesize;
- mtd->oobsize = mtd->writesize / 32;
- *busw = type->options & NAND_BUSWIDTH_16;
+ /* Always prefer 1k blocks over 512bytes ones */
+ ecc->size = 1024;
+ steps = mtd->writesize / ecc->size;
- /* All legacy ID NAND are small-page, SLC */
- chip->bits_per_cell = 1;
+ /* Reserve 2 bytes for the BBM */
+ bytes = (mtd->oobsize - 2) / steps;
+ ecc->strength = bytes * 8 / fls(8 * ecc->size);
+ }
- /*
- * Check for Spansion/AMD ID + repeating 5th, 6th byte since
- * some Spansion chips have erasesize that conflicts with size
- * listed in nand_ids table.
- * Data sheet (5 byte ID): Spansion S30ML-P ORNAND (p.39)
- */
- if (maf_id == NAND_MFR_AMD && id_data[4] != 0x00 && id_data[5] == 0x00
- && id_data[6] == 0x00 && id_data[7] == 0x00
- && mtd->writesize == 512) {
- mtd->erasesize = 128 * 1024;
- mtd->erasesize <<= ((id_data[3] & 0x03) << 1);
+ /* See nand_bch_init() for details. */
+ ecc->bytes = 0;
+ ecc->priv = nand_bch_init(mtd);
+ if (!ecc->priv) {
+ WARN(1, "BCH ECC initialization failed!\n");
+ return -EINVAL;
+ }
+ return 0;
+ default:
+ WARN(1, "Unsupported ECC algorithm!\n");
+ return -EINVAL;
}
}
-/*
- * Set the bad block marker/indicator (BBM/BBI) patterns according to some
- * heuristic patterns using various detected parameters (e.g., manufacturer,
- * page size, cell-type information).
+/**
+ * nand_check_ecc_caps - check the sanity of preset ECC settings
+ * @chip: nand chip info structure
+ * @caps: ECC caps info structure
+ * @oobavail: OOB size that the ECC engine can use
+ *
+ * When ECC step size and strength are already set, check if they are supported
+ * by the controller and the calculated ECC bytes fit within the chip's OOB.
+ * On success, the calculated ECC bytes is set.
*/
-static void nand_decode_bbm_options(struct mtd_info *mtd,
- struct nand_chip *chip, u8 id_data[8])
+static int
+nand_check_ecc_caps(struct nand_chip *chip,
+ const struct nand_ecc_caps *caps, int oobavail)
{
- int maf_id = id_data[0];
-
- /* Set the bad block position */
- if (mtd->writesize > 512 || (chip->options & NAND_BUSWIDTH_16))
- chip->badblockpos = NAND_LARGE_BADBLOCK_POS;
- else
- chip->badblockpos = NAND_SMALL_BADBLOCK_POS;
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ const struct nand_ecc_step_info *stepinfo;
+ int preset_step = chip->ecc.size;
+ int preset_strength = chip->ecc.strength;
+ int ecc_bytes, nsteps = mtd->writesize / preset_step;
+ int i, j;
- /*
- * Bad block marker is stored in the last page of each block on Samsung
- * and Hynix MLC devices; stored in first two pages of each block on
- * Micron devices with 2KiB pages and on SLC Samsung, Hynix, Toshiba,
- * AMD/Spansion, and Macronix. All others scan only the first page.
- */
- if (!nand_is_slc(chip) &&
- (maf_id == NAND_MFR_SAMSUNG ||
- maf_id == NAND_MFR_HYNIX))
- chip->bbt_options |= NAND_BBT_SCANLASTPAGE;
- else if ((nand_is_slc(chip) &&
- (maf_id == NAND_MFR_SAMSUNG ||
- maf_id == NAND_MFR_HYNIX ||
- maf_id == NAND_MFR_TOSHIBA ||
- maf_id == NAND_MFR_AMD ||
- maf_id == NAND_MFR_MACRONIX)) ||
- (mtd->writesize == 2048 &&
- maf_id == NAND_MFR_MICRON))
- chip->bbt_options |= NAND_BBT_SCAN2NDPAGE;
-}
+ for (i = 0; i < caps->nstepinfos; i++) {
+ stepinfo = &caps->stepinfos[i];
-static inline bool is_full_id_nand(struct nand_flash_dev *type)
-{
- return type->id_len;
-}
+ if (stepinfo->stepsize != preset_step)
+ continue;
-static bool find_full_id_nand(struct nand_chip *chip,
- struct nand_flash_dev *type, u8 *id_data, int *busw)
-{
- struct mtd_info *mtd = nand_to_mtd(chip);
+ for (j = 0; j < stepinfo->nstrengths; j++) {
+ if (stepinfo->strengths[j] != preset_strength)
+ continue;
- if (!strncmp(type->id, id_data, type->id_len)) {
- mtd->writesize = type->pagesize;
- mtd->erasesize = type->erasesize;
- mtd->oobsize = type->oobsize;
+ ecc_bytes = caps->calc_ecc_bytes(preset_step,
+ preset_strength);
+ if (WARN_ONCE(ecc_bytes < 0, "%s: eccbytes < 0\n", __func__))
+ return ecc_bytes;
- chip->bits_per_cell = nand_get_bits_per_cell(id_data[2]);
- chip->chipsize = (uint64_t)type->chipsize << 20;
- chip->options |= type->options;
+ if (ecc_bytes * nsteps > oobavail) {
+ pr_err("ECC (step, strength) = (%d, %d) does not fit in OOB",
+ preset_step, preset_strength);
+ return -ENOSPC;
+ }
- *busw = type->options & NAND_BUSWIDTH_16;
+ chip->ecc.bytes = ecc_bytes;
- return true;
+ return 0;
+ }
}
- return false;
+
+ pr_err("ECC (step, strength) = (%d, %d) not supported on this controller",
+ preset_step, preset_strength);
+
+ return -ENOTSUPP;
}
-/*
- * Get the flash and manufacturer id and lookup if the type is supported.
+/**
+ * nand_match_ecc_req - meet the chip's requirement with least ECC bytes
+ * @chip: nand chip info structure
+ * @caps: ECC engine caps info structure
+ * @oobavail: OOB size that the ECC engine can use
+ *
+ * If a chip's ECC requirement is provided, try to meet it with the least
+ * number of ECC bytes (i.e. with the largest number of OOB-free bytes).
+ * On success, the chosen ECC settings are set.
*/
-static struct nand_flash_dev *nand_get_flash_type(struct nand_chip *chip,
- int busw,
- int *maf_id, int *dev_id,
- struct nand_flash_dev *type)
+static int
+nand_match_ecc_req(struct nand_chip *chip,
+ const struct nand_ecc_caps *caps, int oobavail)
{
struct mtd_info *mtd = nand_to_mtd(chip);
- int i, maf_idx;
- u8 id_data[8];
+ const struct nand_ecc_step_info *stepinfo;
+ int req_step = chip->base.eccreq.step_size;
+ int req_strength = chip->base.eccreq.strength;
+ int req_corr, step_size, strength, nsteps, ecc_bytes, ecc_bytes_total;
+ int best_step, best_strength, best_ecc_bytes;
+ int best_ecc_bytes_total = INT_MAX;
+ int i, j;
- /* Select the device */
- chip->legacy.select_chip(chip, 0);
+ /* No information provided by the NAND chip */
+ if (!req_step || !req_strength)
+ return -ENOTSUPP;
- /*
- * Reset the chip, required by some chips (e.g. Micron MT29FxGxxxxx)
- * after power-up.
- */
- chip->legacy.cmdfunc(chip, NAND_CMD_RESET, -1, -1);
+ /* number of correctable bits the chip requires in a page */
+ req_corr = mtd->writesize / req_step * req_strength;
- /* Send the command for reading device ID */
- chip->legacy.cmdfunc(chip, NAND_CMD_READID, 0x00, -1);
+ for (i = 0; i < caps->nstepinfos; i++) {
+ stepinfo = &caps->stepinfos[i];
+ step_size = stepinfo->stepsize;
- /* Read manufacturer and device IDs */
- *maf_id = chip->legacy.read_byte(chip);
- *dev_id = chip->legacy.read_byte(chip);
+ for (j = 0; j < stepinfo->nstrengths; j++) {
+ strength = stepinfo->strengths[j];
- /*
- * Try again to make sure, as some systems the bus-hold or other
- * interface concerns can cause random data which looks like a
- * possibly credible NAND flash to appear. If the two results do
- * not match, ignore the device completely.
- */
+ /*
+ * If both step size and strength are smaller than the
+ * chip's requirement, it is not easy to compare the
+ * resulted reliability.
+ */
+ if (step_size < req_step && strength < req_strength)
+ continue;
- chip->legacy.cmdfunc(chip, NAND_CMD_READID, 0x00, -1);
+ if (mtd->writesize % step_size)
+ continue;
- /* Read entire ID string */
- for (i = 0; i < 8; i++)
- id_data[i] = chip->legacy.read_byte(chip);
+ nsteps = mtd->writesize / step_size;
- if (id_data[0] != *maf_id || id_data[1] != *dev_id) {
- pr_info("%s: second ID read did not match "
- "%02x,%02x against %02x,%02x\n", __func__,
- *maf_id, *dev_id, id_data[0], id_data[1]);
- return ERR_PTR(-ENODEV);
- }
+ ecc_bytes = caps->calc_ecc_bytes(step_size, strength);
+ if (WARN_ONCE(ecc_bytes < 0, "%s: eccbytes < 0\n", __func__))
+ continue;
+ ecc_bytes_total = ecc_bytes * nsteps;
- if (!type)
- type = nand_flash_ids;
+ if (ecc_bytes_total > oobavail ||
+ strength * nsteps < req_corr)
+ continue;
- for (; type->name != NULL; type++) {
- if (is_full_id_nand(type)) {
- if (find_full_id_nand(chip, type, id_data, &busw))
- goto ident_done;
- } else if (*dev_id == type->dev_id) {
- break;
+ /*
+ * We assume the best is to meet the chip's requrement
+ * with the least number of ECC bytes.
+ */
+ if (ecc_bytes_total < best_ecc_bytes_total) {
+ best_ecc_bytes_total = ecc_bytes_total;
+ best_step = step_size;
+ best_strength = strength;
+ best_ecc_bytes = ecc_bytes;
+ }
}
}
- chip->onfi_version = 0;
- if (!type->name || !type->pagesize) {
- /* Check is chip is ONFI compliant */
- if (nand_flash_detect_onfi(chip, &busw))
- goto ident_done;
- }
+ if (best_ecc_bytes_total == INT_MAX)
+ return -ENOTSUPP;
+
+ chip->ecc.size = best_step;
+ chip->ecc.strength = best_strength;
+ chip->ecc.bytes = best_ecc_bytes;
+
+ return 0;
+}
- if (!type->name)
- return ERR_PTR(-ENODEV);
+/**
+ * nand_maximize_ecc - choose the max ECC strength available
+ * @chip: nand chip info structure
+ * @caps: ECC engine caps info structure
+ * @oobavail: OOB size that the ECC engine can use
+ *
+ * Choose the max ECC strength that is supported on the controller, and can fit
+ * within the chip's OOB. On success, the chosen ECC settings are set.
+ */
+static int
+nand_maximize_ecc(struct nand_chip *chip,
+ const struct nand_ecc_caps *caps, int oobavail)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ const struct nand_ecc_step_info *stepinfo;
+ int step_size, strength, nsteps, ecc_bytes, corr;
+ int best_corr = 0;
+ int best_step = 0;
+ int best_strength, best_ecc_bytes;
+ int i, j;
- if (!mtd->name)
- mtd->name = type->name;
+ for (i = 0; i < caps->nstepinfos; i++) {
+ stepinfo = &caps->stepinfos[i];
+ step_size = stepinfo->stepsize;
- chip->chipsize = (uint64_t)type->chipsize << 20;
+ /* If chip->ecc.size is already set, respect it */
+ if (chip->ecc.size && step_size != chip->ecc.size)
+ continue;
- if (!type->pagesize) {
- /* Decode parameters from extended ID */
- nand_decode_ext_id(mtd, chip, id_data, &busw);
- } else {
- nand_decode_id(mtd, chip, type, id_data, &busw);
- }
- /* Get chip options */
- chip->options |= type->options;
+ for (j = 0; j < stepinfo->nstrengths; j++) {
+ strength = stepinfo->strengths[j];
- /*
- * Check if chip is not a Samsung device. Do not clear the
- * options for chips which do not have an extended id.
- */
- if (*maf_id != NAND_MFR_SAMSUNG && !type->pagesize)
- chip->options &= ~NAND_SAMSUNG_LP_OPTIONS;
-ident_done:
+ if (mtd->writesize % step_size)
+ continue;
- /* Try to identify manufacturer */
- for (maf_idx = 0; nand_manuf_ids[maf_idx].id != 0x0; maf_idx++) {
- if (nand_manuf_ids[maf_idx].id == *maf_id)
- break;
- }
+ nsteps = mtd->writesize / step_size;
- if (chip->options & NAND_BUSWIDTH_AUTO) {
- WARN_ON(chip->options & NAND_BUSWIDTH_16);
- chip->options |= busw;
- nand_set_defaults(chip, busw);
- } else if (busw != (chip->options & NAND_BUSWIDTH_16)) {
- /*
- * Check, if buswidth is correct. Hardware drivers should set
- * chip correct!
- */
- pr_info("NAND device: Manufacturer ID:"
- " 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id,
- *dev_id, nand_manuf_ids[maf_idx].name, mtd->name);
- pr_warn("NAND bus width %d instead %d bit\n",
- (chip->options & NAND_BUSWIDTH_16) ? 16 : 8,
- busw ? 16 : 8);
- return ERR_PTR(-EINVAL);
- }
+ ecc_bytes = caps->calc_ecc_bytes(step_size, strength);
+ if (WARN_ONCE(ecc_bytes < 0, "%s: eccbytes < 0\n", __func__))
+ continue;
- nand_decode_bbm_options(mtd, chip, id_data);
+ if (ecc_bytes * nsteps > oobavail)
+ continue;
- /* Calculate the address shift from the page size */
- chip->page_shift = ffs(mtd->writesize) - 1;
- /* Convert chipsize to number of pages per chip -1 */
- chip->pagemask = (chip->chipsize >> chip->page_shift) - 1;
+ corr = strength * nsteps;
- chip->bbt_erase_shift = chip->phys_erase_shift =
- ffs(mtd->erasesize) - 1;
- if (chip->chipsize & 0xffffffff)
- chip->chip_shift = ffs((unsigned)chip->chipsize) - 1;
- else {
- chip->chip_shift = ffs((unsigned)(chip->chipsize >> 32));
- chip->chip_shift += 32 - 1;
+ /*
+ * If the number of correctable bits is the same,
+ * bigger step_size has more reliability.
+ */
+ if (corr > best_corr ||
+ (corr == best_corr && step_size > best_step)) {
+ best_corr = corr;
+ best_step = step_size;
+ best_strength = strength;
+ best_ecc_bytes = ecc_bytes;
+ }
+ }
}
- chip->badblockbits = 8;
-
- /* Do not replace user supplied command function! */
- if (mtd->writesize > 512 && chip->legacy.cmdfunc == nand_command)
- chip->legacy.cmdfunc = nand_command_lp;
+ if (!best_corr)
+ return -ENOTSUPP;
- pr_info("NAND device: Manufacturer ID: 0x%02x, Chip ID: 0x%02x (%s %s),"
- " %dMiB, page size: %d, OOB size: %d\n",
- *maf_id, *dev_id, nand_manuf_ids[maf_idx].name,
- chip->onfi_version ? chip->onfi_params.model : type->name,
- (int)(chip->chipsize >> 20), mtd->writesize, mtd->oobsize);
+ chip->ecc.size = best_step;
+ chip->ecc.strength = best_strength;
+ chip->ecc.bytes = best_ecc_bytes;
- return type;
+ return 0;
}
/**
- * nand_of_parse_node - parse generic NAND properties
- * @mtd: MTD device structure
- * @np: Device node to read information from
+ * nand_ecc_choose_conf - Set the ECC strength and ECC step size
+ * @chip: nand chip info structure
+ * @caps: ECC engine caps info structure
+ * @oobavail: OOB size that the ECC engine can use
+ *
+ * Choose the ECC configuration according to following logic
*
- * This parses device tree properties generic to NAND controllers and fills in
- * the various fields in struct nand_chip.
+ * 1. If both ECC step size and ECC strength are already set (usually by DT)
+ * then check if it is supported by this controller.
+ * 2. If NAND_ECC_MAXIMIZE is set, then select maximum ECC strength.
+ * 3. Otherwise, try to match the ECC step size and ECC strength closest
+ * to the chip's requirement. If available OOB size can't fit the chip
+ * requirement then fallback to the maximum ECC step size and ECC strength.
+ *
+ * On success, the chosen ECC settings are set.
*/
-void nand_of_parse_node(struct mtd_info *mtd, struct device_node *np)
+int nand_ecc_choose_conf(struct nand_chip *chip,
+ const struct nand_ecc_caps *caps, int oobavail)
{
- struct nand_chip *chip = mtd_to_nand(mtd);
- int ecc_strength, ecc_size;
+ struct mtd_info *mtd = nand_to_mtd(chip);
- if (!IS_ENABLED(CONFIG_OFDEVICE))
- return;
+ if (WARN_ON(oobavail < 0 || oobavail > mtd->oobsize))
+ return -EINVAL;
- ecc_strength = of_get_nand_ecc_strength(np);
- ecc_size = of_get_nand_ecc_step_size(np);
+ if (chip->ecc.size && chip->ecc.strength)
+ return nand_check_ecc_caps(chip, caps, oobavail);
- if (ecc_strength >= 0)
- chip->ecc.strength = ecc_strength;
+ if (chip->ecc.options & NAND_ECC_MAXIMIZE)
+ return nand_maximize_ecc(chip, caps, oobavail);
+
+ if (!nand_match_ecc_req(chip, caps, oobavail))
+ return 0;
- if (ecc_size >= 0)
- chip->ecc.size = ecc_size;
+ return nand_maximize_ecc(chip, caps, oobavail);
}
+EXPORT_SYMBOL_GPL(nand_ecc_choose_conf);
-/**
- * nand_scan_ident - [NAND Interface] Scan for the NAND device
- * @mtd: MTD device structure
- * @maxchips: number of chips to scan for
- * @table: alternative NAND ID table
+/*
+ * Check if the chip configuration meet the datasheet requirements.
+
+ * If our configuration corrects A bits per B bytes and the minimum
+ * required correction level is X bits per Y bytes, then we must ensure
+ * both of the following are true:
*
- * This is the first phase of the normal nand_scan() function. It reads the
- * flash ID and sets up MTD fields accordingly.
+ * (1) A / B >= X / Y
+ * (2) A >= X
*
- * The mtd->owner field must be set to the module of the caller.
+ * Requirement (1) ensures we can correct for the required bitflip density.
+ * Requirement (2) ensures we can correct even when all bitflips are clumped
+ * in the same sector.
*/
-int nand_scan_ident(struct nand_chip *chip, int maxchips,
- struct nand_flash_dev *table)
+static bool nand_ecc_strength_good(struct nand_chip *chip)
{
struct mtd_info *mtd = nand_to_mtd(chip);
- int i, busw, nand_maf_id, nand_dev_id;
- struct nand_flash_dev *type;
+ struct nand_ecc_ctrl *ecc = &chip->ecc;
+ int corr, ds_corr;
- /* Get buswidth to select the correct functions */
- busw = chip->options & NAND_BUSWIDTH_16;
- /* Set the default functions */
- nand_set_defaults(chip, busw);
+ if (ecc->size == 0 || chip->base.eccreq.step_size == 0)
+ /* Not enough information */
+ return true;
- /* Read the flash type */
- type = nand_get_flash_type(chip, busw,
- &nand_maf_id, &nand_dev_id, table);
+ /*
+ * We get the number of corrected bits per page to compare
+ * the correction density.
+ */
+ corr = (mtd->writesize * ecc->strength) / ecc->size;
+ ds_corr = (mtd->writesize * chip->base.eccreq.strength) /
+ chip->base.eccreq.step_size;
- if (IS_ERR(type)) {
- if (!(chip->options & NAND_SCAN_SILENT_NODEV))
- pr_warn("No NAND device found\n");
- chip->legacy.select_chip(chip, -1);
- return PTR_ERR(type);
- }
+ return corr >= ds_corr && ecc->strength >= chip->base.eccreq.strength;
+}
+
+static int rawnand_erase(struct nand_device *nand, const struct nand_pos *pos)
+{
+ struct nand_chip *chip = container_of(nand, struct nand_chip,
+ base);
+ unsigned int eb = nanddev_pos_to_row(nand, pos);
+ int ret;
- chip->legacy.select_chip(chip, -1);
+ eb >>= nand->rowconv.eraseblock_addr_shift;
- /* Check for a chip array */
- for (i = 1; i < maxchips; i++) {
- chip->legacy.select_chip(chip, i);
- /* See comment in nand_get_flash_type for reset */
- chip->legacy.cmdfunc(chip, NAND_CMD_RESET, -1, -1);
- /* Send the command for reading device ID */
- chip->legacy.cmdfunc(chip, NAND_CMD_READID, 0x00, -1);
- /* Read manufacturer and device IDs */
- if (nand_maf_id != chip->legacy.read_byte(chip) ||
- nand_dev_id != chip->legacy.read_byte(chip)) {
- chip->legacy.select_chip(chip, -1);
- break;
- }
- chip->legacy.select_chip(chip, -1);
- }
- if (i > 1)
- pr_info("%d NAND chips detected\n", i);
+ nand_select_target(chip, pos->target);
+ ret = nand_erase_op(chip, eb);
+ nand_deselect_target(chip);
- /* Store the number of chips and calc total size for mtd */
- chip->numchips = i;
- mtd->size = i * chip->chipsize;
+ return ret;
+}
- return 0;
+static int rawnand_markbad(struct nand_device *nand,
+ const struct nand_pos *pos)
+{
+ struct nand_chip *chip = container_of(nand, struct nand_chip,
+ base);
+
+ return nand_markbad_bbm(chip, nanddev_pos_to_offs(nand, pos));
+}
+
+static bool rawnand_isbad(struct nand_device *nand, const struct nand_pos *pos)
+{
+ struct nand_chip *chip = container_of(nand, struct nand_chip,
+ base);
+ int ret;
+
+ nand_select_target(chip, pos->target);
+ ret = nand_isbad_bbm(chip, nanddev_pos_to_offs(nand, pos));
+ nand_deselect_target(chip);
+
+ return ret;
}
-EXPORT_SYMBOL(nand_scan_ident);
+static const struct nand_ops rawnand_ops = {
+ .erase = rawnand_erase,
+ .markbad = rawnand_markbad,
+ .isbad = rawnand_isbad,
+};
/**
- * nand_scan_tail - [NAND Interface] Scan for the NAND device
- * @mtd: MTD device structure
+ * nand_scan_tail - Scan for the NAND device
+ * @chip: NAND chip object
*
* This is the second phase of the normal nand_scan() function. It fills out
* all the uninitialized function pointers with the defaults and scans for a
@@ -3561,218 +5486,224 @@ EXPORT_SYMBOL(nand_scan_ident);
int nand_scan_tail(struct nand_chip *chip)
{
struct mtd_info *mtd = nand_to_mtd(chip);
- int i;
+ struct nand_ecc_ctrl *ecc = &chip->ecc;
+ int ret, i;
/* New bad blocks should be marked in OOB, flash-based BBT, or both */
- BUG_ON((chip->bbt_options & NAND_BBT_NO_OOB_BBM) &&
- !(chip->bbt_options & NAND_BBT_USE_FLASH));
+ if (WARN_ON((chip->bbt_options & NAND_BBT_NO_OOB_BBM) &&
+ !(chip->bbt_options & NAND_BBT_USE_FLASH))) {
+ return -EINVAL;
+ }
- if (!(chip->options & NAND_OWN_BUFFERS))
- chip->buffers = kmalloc(sizeof(*chip->buffers), GFP_KERNEL);
- if (!chip->buffers)
+ chip->data_buf = kmalloc(mtd->writesize + mtd->oobsize, GFP_KERNEL);
+ if (!chip->data_buf)
return -ENOMEM;
+ /*
+ * FIXME: some NAND manufacturer drivers expect the first die to be
+ * selected when manufacturer->init() is called. They should be fixed
+ * to explictly select the relevant die when interacting with the NAND
+ * chip.
+ */
+ nand_select_target(chip, 0);
+ ret = nand_manufacturer_init(chip);
+ nand_deselect_target(chip);
+ if (ret)
+ goto err_free_buf;
+
/* Set the internal oob buffer location, just after the page data */
- chip->oob_poi = chip->buffers->databuf + mtd->writesize;
+ chip->oob_poi = chip->data_buf + mtd->writesize;
/*
* If no default placement scheme is given, select an appropriate one.
*/
- if (!chip->ecc.layout && (chip->ecc.mode != NAND_ECC_SOFT_BCH)) {
+ if (!mtd->ooblayout &&
+ !(ecc->mode == NAND_ECC_SOFT && ecc->algo == NAND_ECC_BCH)) {
switch (mtd->oobsize) {
case 8:
- chip->ecc.layout = &nand_oob_8;
- break;
case 16:
- chip->ecc.layout = &nand_oob_16;
+ mtd_set_ooblayout(mtd, &nand_ooblayout_sp_ops);
break;
case 64:
- chip->ecc.layout = &nand_oob_64;
- break;
case 128:
- chip->ecc.layout = &nand_oob_128;
+ mtd_set_ooblayout(mtd, &nand_ooblayout_lp_hamming_ops);
break;
default:
- pr_warn("No oob scheme defined for oobsize %d\n",
- mtd->oobsize);
- BUG();
+ /*
+ * Expose the whole OOB area to users if ECC_NONE
+ * is passed. We could do that for all kind of
+ * ->oobsize, but we must keep the old large/small
+ * page with ECC layout when ->oobsize <= 128 for
+ * compatibility reasons.
+ */
+ if (ecc->mode == NAND_ECC_NONE) {
+ mtd_set_ooblayout(mtd,
+ &nand_ooblayout_lp_ops);
+ break;
+ }
+
+ WARN(1, "No oob scheme defined for oobsize %d\n",
+ mtd->oobsize);
+ ret = -EINVAL;
+ goto err_nand_manuf_cleanup;
}
}
- if (!chip->write_page)
- chip->write_page = nand_write_page;
-
/*
* Check ECC mode, default to software if 3byte/512byte hardware ECC is
* selected and we have 256 byte pagesize fallback to software ECC
*/
- switch (chip->ecc.mode) {
-#ifdef CONFIG_NAND_ECC_HW_OOB_FIRST
- case NAND_ECC_HW_OOB_FIRST:
- /* Similar to NAND_ECC_HW, but a separate read_page handle */
- if (!chip->ecc.calculate || !chip->ecc.correct ||
- !chip->ecc.hwctl) {
- pr_warn("No ECC functions supplied; hardware ECC not possible\n");
- BUG();
- }
- if (!chip->ecc.read_page)
- chip->ecc.read_page = nand_read_page_hwecc_oob_first;
- if (!chip->ecc.write_page)
- chip->ecc.write_page = nand_write_page_hwecc;
- if (!chip->ecc.read_page_raw)
- chip->ecc.read_page_raw = nand_read_page_raw;
- if (!chip->ecc.write_page_raw)
- chip->ecc.write_page_raw = nand_write_page_raw;
- if (!chip->ecc.read_oob)
- chip->ecc.read_oob = nand_read_oob_std;
- if (!chip->ecc.write_oob)
- chip->ecc.write_oob = nand_write_oob_std;
- if (!chip->ecc.read_subpage)
- chip->ecc.read_subpage = nand_read_subpage;
- if (!chip->ecc.write_subpage)
- chip->ecc.write_subpage = nand_write_subpage_hwecc;
- break;
-#endif
-#ifdef CONFIG_NAND_ECC_HW
+ switch (ecc->mode) {
case NAND_ECC_HW:
/* Use standard hwecc read page function? */
- if (!chip->ecc.read_page)
- chip->ecc.read_page = nand_read_page_hwecc;
- if (!chip->ecc.write_page)
- chip->ecc.write_page = nand_write_page_hwecc;
- if (!chip->ecc.read_page_raw)
- chip->ecc.read_page_raw = nand_read_page_raw;
- if (!chip->ecc.write_page_raw)
- chip->ecc.write_page_raw = nand_write_page_raw;
- if (!chip->ecc.read_oob)
- chip->ecc.read_oob = nand_read_oob_std;
- if (!chip->ecc.write_oob)
- chip->ecc.write_oob = nand_write_oob_std;
- if (!chip->ecc.read_subpage)
- chip->ecc.read_subpage = nand_read_subpage;
- if (!chip->ecc.write_subpage)
- chip->ecc.write_subpage = nand_write_subpage_hwecc;
- break;
-#endif
-#ifdef CONFIG_NAND_ECC_HW_SYNDROME
+ if (!ecc->read_page)
+ ecc->read_page = nand_read_page_hwecc;
+ if (!ecc->write_page)
+ ecc->write_page = nand_write_page_hwecc;
+ if (!ecc->read_page_raw)
+ ecc->read_page_raw = nand_read_page_raw;
+ if (!ecc->write_page_raw)
+ ecc->write_page_raw = nand_write_page_raw;
+ if (!ecc->read_oob)
+ ecc->read_oob = nand_read_oob_std;
+ if (!ecc->write_oob)
+ ecc->write_oob = nand_write_oob_std;
+ if (!ecc->read_subpage)
+ ecc->read_subpage = nand_read_subpage;
+ if (!ecc->write_subpage && ecc->hwctl && ecc->calculate)
+ ecc->write_subpage = nand_write_subpage_hwecc;
case NAND_ECC_HW_SYNDROME:
+ if ((!ecc->calculate || !ecc->correct || !ecc->hwctl) &&
+ (!ecc->read_page ||
+ ecc->read_page == nand_read_page_hwecc ||
+ !ecc->write_page ||
+ ecc->write_page == nand_write_page_hwecc)) {
+ WARN(1, "No ECC functions supplied; hardware ECC not possible\n");
+ ret = -EINVAL;
+ goto err_nand_manuf_cleanup;
+ }
/* Use standard syndrome read/write page function? */
- if (!chip->ecc.read_page)
- chip->ecc.read_page = nand_read_page_syndrome;
- if (!chip->ecc.write_page)
- chip->ecc.write_page = nand_write_page_syndrome;
- if (!chip->ecc.read_page_raw)
- chip->ecc.read_page_raw = nand_read_page_raw_syndrome;
- if (!chip->ecc.write_page_raw)
- chip->ecc.write_page_raw = nand_write_page_raw_syndrome;
- if (!chip->ecc.read_oob)
- chip->ecc.read_oob = nand_read_oob_syndrome;
- if (!chip->ecc.write_oob)
- chip->ecc.write_oob = nand_write_oob_syndrome;
- break;
-#endif
-#ifdef CONFIG_NAND_ECC_SOFT
- case NAND_ECC_SOFT:
- chip->ecc.calculate = nand_calculate_ecc;
- chip->ecc.correct = nand_correct_data;
- chip->ecc.read_page = nand_read_page_swecc;
- chip->ecc.read_subpage = nand_read_subpage;
- chip->ecc.write_page = nand_write_page_swecc;
- chip->ecc.read_page_raw = nand_read_page_raw;
- chip->ecc.write_page_raw = nand_write_page_raw;
- chip->ecc.read_oob = nand_read_oob_std;
- chip->ecc.write_oob = nand_write_oob_std;
- if (!chip->ecc.size)
- chip->ecc.size = 256;
- chip->ecc.bytes = 3;
- chip->ecc.strength = 1;
- break;
-#endif
-#ifdef CONFIG_NAND_ECC_BCH
- case NAND_ECC_SOFT_BCH:
- if (!mtd_nand_has_bch()) {
- pr_warn("CONFIG_MTD_ECC_BCH not enabled\n");
- BUG();
+ if (!ecc->read_page)
+ ecc->read_page = nand_read_page_syndrome;
+ if (!ecc->write_page)
+ ecc->write_page = nand_write_page_syndrome;
+ if (!ecc->read_page_raw)
+ ecc->read_page_raw = nand_read_page_raw_syndrome;
+ if (!ecc->write_page_raw)
+ ecc->write_page_raw = nand_write_page_raw_syndrome;
+ if (!ecc->read_oob)
+ ecc->read_oob = nand_read_oob_syndrome;
+ if (!ecc->write_oob)
+ ecc->write_oob = nand_write_oob_syndrome;
+
+ if (mtd->writesize >= ecc->size) {
+ if (!ecc->strength) {
+ WARN(1, "Driver must set ecc.strength when using hardware ECC\n");
+ ret = -EINVAL;
+ goto err_nand_manuf_cleanup;
+ }
+ break;
}
- chip->ecc.calculate = nand_bch_calculate_ecc;
- chip->ecc.correct = nand_bch_correct_data;
- chip->ecc.read_page = nand_read_page_swecc;
- chip->ecc.read_subpage = nand_read_subpage;
- chip->ecc.write_page = nand_write_page_swecc;
- chip->ecc.read_page_raw = nand_read_page_raw;
- chip->ecc.write_page_raw = nand_write_page_raw;
- chip->ecc.read_oob = nand_read_oob_std;
- chip->ecc.write_oob = nand_write_oob_std;
- /*
- * Board driver should supply ecc.size and ecc.strength values
- * to select how many bits are correctable. Otherwise, default
- * to 4 bits for large page devices.
- */
- if (!chip->ecc.size && (mtd->oobsize >= 64)) {
- chip->ecc.size = 512;
- chip->ecc.strength = 4;
+ pr_warn("%d byte HW ECC not possible on %d byte page size, fallback to SW ECC\n",
+ ecc->size, mtd->writesize);
+ ecc->mode = NAND_ECC_SOFT;
+ ecc->algo = NAND_ECC_HAMMING;
+ case NAND_ECC_SOFT:
+ ret = nand_set_ecc_soft_ops(chip);
+ if (ret) {
+ ret = -EINVAL;
+ goto err_nand_manuf_cleanup;
}
+ break;
- /* See nand_bch_init() for details. */
- chip->ecc.bytes = 0;
- chip->ecc.priv = nand_bch_init(chip);
- if (!chip->ecc.priv) {
- pr_warn("BCH ECC initialization failed!\n");
- BUG();
+ case NAND_ECC_ON_DIE:
+ if (!ecc->read_page || !ecc->write_page) {
+ WARN(1, "No ECC functions supplied; on-die ECC not possible\n");
+ ret = -EINVAL;
+ goto err_nand_manuf_cleanup;
}
+ if (!ecc->read_oob)
+ ecc->read_oob = nand_read_oob_std;
+ if (!ecc->write_oob)
+ ecc->write_oob = nand_write_oob_std;
break;
-#endif
-#ifdef CONFIG_NAND_ECC_NONE
+
case NAND_ECC_NONE:
pr_warn("NAND_ECC_NONE selected by board driver. This is not recommended!\n");
- chip->ecc.read_page = nand_read_page_raw;
- chip->ecc.write_page = nand_write_page_raw;
- chip->ecc.read_oob = nand_read_oob_std;
- chip->ecc.read_page_raw = nand_read_page_raw;
- chip->ecc.write_page_raw = nand_write_page_raw;
- chip->ecc.write_oob = nand_write_oob_std;
- chip->ecc.size = mtd->writesize;
- chip->ecc.bytes = 0;
- chip->ecc.strength = 0;
+ ecc->read_page = nand_read_page_raw;
+ ecc->write_page = nand_write_page_raw;
+ ecc->read_oob = nand_read_oob_std;
+ ecc->read_page_raw = nand_read_page_raw;
+ ecc->write_page_raw = nand_write_page_raw;
+ ecc->write_oob = nand_write_oob_std;
+ ecc->size = mtd->writesize;
+ ecc->bytes = 0;
+ ecc->strength = 0;
break;
-#endif
+
default:
- pr_warn("Invalid NAND_ECC_MODE %d\n", chip->ecc.mode);
- BUG();
+ WARN(1, "Invalid NAND_ECC_MODE %d\n", ecc->mode);
+ ret = -EINVAL;
+ goto err_nand_manuf_cleanup;
+ }
+
+ if (ecc->correct || ecc->calculate) {
+ ecc->calc_buf = kmalloc(mtd->oobsize, GFP_KERNEL);
+ ecc->code_buf = kmalloc(mtd->oobsize, GFP_KERNEL);
+ if (!ecc->calc_buf || !ecc->code_buf) {
+ ret = -ENOMEM;
+ goto err_nand_manuf_cleanup;
+ }
}
/* For many systems, the standard OOB write also works for raw */
- if (!chip->ecc.read_oob_raw)
- chip->ecc.read_oob_raw = chip->ecc.read_oob;
- if (!chip->ecc.write_oob_raw)
- chip->ecc.write_oob_raw = chip->ecc.write_oob;
+ if (!ecc->read_oob_raw)
+ ecc->read_oob_raw = ecc->read_oob;
+ if (!ecc->write_oob_raw)
+ ecc->write_oob_raw = ecc->write_oob;
- /*
- * The number of bytes available for a client to place data into
- * the out of band area.
- */
- chip->ecc.layout->oobavail = 0;
- for (i = 0; chip->ecc.layout->oobfree[i].length
- && i < ARRAY_SIZE(chip->ecc.layout->oobfree); i++)
- chip->ecc.layout->oobavail +=
- chip->ecc.layout->oobfree[i].length;
- mtd->oobavail = chip->ecc.layout->oobavail;
+ /* propagate ecc info to mtd_info */
+ mtd->ecc_strength = ecc->strength;
+ mtd->ecc_step_size = ecc->size;
/*
* Set the number of read / write steps for one page depending on ECC
* mode.
*/
- chip->ecc.steps = mtd->writesize / chip->ecc.size;
- if (chip->ecc.steps * chip->ecc.size != mtd->writesize) {
- pr_warn("Invalid ECC parameters\n");
- BUG();
+ ecc->steps = mtd->writesize / ecc->size;
+ if (ecc->steps * ecc->size != mtd->writesize) {
+ WARN(1, "Invalid ECC parameters\n");
+ ret = -EINVAL;
+ goto err_nand_manuf_cleanup;
+ }
+ ecc->total = ecc->steps * ecc->bytes;
+ if (ecc->total > mtd->oobsize) {
+ WARN(1, "Total number of ECC bytes exceeded oobsize\n");
+ ret = -EINVAL;
+ goto err_nand_manuf_cleanup;
}
- chip->ecc.total = chip->ecc.steps * chip->ecc.bytes;
+
+ /*
+ * The number of bytes available for a client to place data into
+ * the out of band area.
+ */
+ ret = mtd_ooblayout_count_freebytes(mtd);
+ if (ret < 0)
+ ret = 0;
+
+ mtd->oobavail = ret;
+
+ /* ECC sanity check: warn if it's too weak */
+ if (!nand_ecc_strength_good(chip))
+ pr_warn("WARNING: %s: the ECC used on your system (%db/%dB) is too weak compared to the one required by the NAND chip (%db/%dB)\n",
+ mtd->name, chip->ecc.strength, chip->ecc.size,
+ chip->base.eccreq.strength,
+ chip->base.eccreq.step_size);
/* Allow subpage writes up to ecc.steps. Not possible for MLC flash */
if (!(chip->options & NAND_NO_SUBPAGE_WRITE) && nand_is_slc(chip)) {
- switch (chip->ecc.steps) {
+ switch (ecc->steps) {
case 2:
mtd->subpage_sft = 1;
break;
@@ -3785,36 +5716,39 @@ int nand_scan_tail(struct nand_chip *chip)
}
chip->subpagesize = mtd->writesize >> mtd->subpage_sft;
- /* Initialize state */
- chip->state = FL_READY;
-
/* Invalidate the pagebuffer reference */
- chip->pagebuf = -1;
+ chip->pagecache.page = -1;
/* Large page NAND with SOFT_ECC should support subpage reads */
- if ((chip->ecc.mode == NAND_ECC_SOFT) && (chip->page_shift > 9))
- chip->options |= NAND_SUBPAGE_READ;
+ switch (ecc->mode) {
+ case NAND_ECC_SOFT:
+ if (chip->page_shift > 9)
+ chip->options |= NAND_SUBPAGE_READ;
+ break;
+
+ default:
+ break;
+ }
+
+ ret = nanddev_init(&chip->base, &rawnand_ops, mtd->owner);
+ if (ret)
+ goto err_nand_manuf_cleanup;
+
+ /* Adjust the MTD_CAP_ flags when NAND_ROM is set. */
+ if (chip->options & NAND_ROM)
+ mtd->flags = MTD_CAP_ROM;
/* Fill in remaining MTD driver data */
- mtd->type = MTD_NANDFLASH;
- mtd->flags = (chip->options & NAND_ROM) ? MTD_CAP_ROM :
- MTD_CAP_NANDFLASH;
mtd->_erase = nand_erase;
- mtd->_read = nand_read;
- mtd->_write = nand_write;
mtd->_read_oob = nand_read_oob;
mtd->_write_oob = nand_write_oob;
mtd->_sync = nand_sync;
- mtd->_lock = NULL;
- mtd->_unlock = NULL;
+ mtd->_lock = nand_lock;
+ mtd->_unlock = nand_unlock;
mtd->_block_isbad = nand_block_isbad;
mtd->_block_markbad = nand_block_markbad;
mtd->_block_markgood = nand_block_markgood;
- mtd->writebufsize = mtd->writesize;
- /* propagate ecc info to mtd_info */
- mtd->ecclayout = chip->ecc.layout;
- mtd->ecc_strength = chip->ecc.strength;
/*
* Initialize bitflip_threshold to its default prior scan_bbt() call.
* scan_bbt() might invoke mtd_read(), thus bitflip_threshold must be
@@ -3823,82 +5757,139 @@ int nand_scan_tail(struct nand_chip *chip)
if (!mtd->bitflip_threshold)
mtd->bitflip_threshold = DIV_ROUND_UP(mtd->ecc_strength * 3, 4);
+ /* Find the fastest data interface for this chip */
+ ret = nand_choose_interface_config(chip);
+ if (ret)
+ goto err_nanddev_cleanup;
+
+ /* Enter fastest possible mode on all dies. */
+ for (i = 0; i < nanddev_ntargets(&chip->base); i++) {
+ ret = nand_setup_interface(chip, i);
+ if (ret)
+ goto err_free_interface_config;
+ }
+
/* Check, if we should skip the bad block table scan */
if (chip->options & NAND_SKIP_BBTSCAN)
return 0;
- if (!IS_ENABLED(CONFIG_NAND_BBT))
- return 0;
-
/* Build bad block table */
- return chip->scan_bbt(chip);
+ ret = nand_create_bbt(chip);
+ if (ret)
+ goto err_free_interface_config;
+
+ return 0;
+
+err_free_interface_config:
+ kfree(chip->best_interface_config);
+
+err_nanddev_cleanup:
+ nanddev_cleanup(&chip->base);
+
+err_nand_manuf_cleanup:
+ nand_manufacturer_cleanup(chip);
+
+err_free_buf:
+ kfree(chip->data_buf);
+ kfree(ecc->code_buf);
+ kfree(ecc->calc_buf);
+
+ return ret;
+}
+
+static int nand_attach(struct nand_chip *chip)
+{
+ if (chip->controller->ops && chip->controller->ops->attach_chip)
+ return chip->controller->ops->attach_chip(chip);
+
+ return 0;
+}
+
+static void nand_detach(struct nand_chip *chip)
+{
+ if (chip->controller->ops && chip->controller->ops->detach_chip)
+ chip->controller->ops->detach_chip(chip);
}
-EXPORT_SYMBOL(nand_scan_tail);
/**
- * nand_scan - [NAND Interface] Scan for the NAND device
- * @mtd: MTD device structure
- * @maxchips: number of chips to scan for
+ * nand_scan_with_ids - [NAND Interface] Scan for the NAND device
+ * @chip: NAND chip object
+ * @maxchips: number of chips to scan for.
+ * @ids: optional flash IDs table
*
* This fills out all the uninitialized function pointers with the defaults.
* The flash ID is read and the mtd/chip structures are filled with the
- * appropriate values. The mtd->owner field must be set to the module of the
- * caller.
+ * appropriate values.
*/
-int nand_scan(struct nand_chip *chip, int maxchips)
+int nand_scan_with_ids(struct nand_chip *chip, unsigned int maxchips,
+ struct nand_flash_dev *ids)
{
int ret;
- ret = nand_scan_ident(chip, maxchips, NULL);
- if (!ret)
- ret = nand_scan_tail(chip);
+ if (!maxchips)
+ return -EINVAL;
+
+ ret = nand_scan_ident(chip, maxchips, ids);
+ if (ret)
+ return ret;
+
+ ret = nand_attach(chip);
+ if (ret)
+ goto cleanup_ident;
+
+ ret = nand_scan_tail(chip);
+ if (ret)
+ goto detach_chip;
+
+ return 0;
+
+detach_chip:
+ nand_detach(chip);
+cleanup_ident:
+ nand_scan_ident_cleanup(chip);
+
return ret;
}
-EXPORT_SYMBOL(nand_scan);
+EXPORT_SYMBOL(nand_scan_with_ids);
/**
- * nand_release - [NAND Interface] Free resources held by the NAND device
- * @mtd: MTD device structure
+ * nand_cleanup - [NAND Interface] Free resources held by the NAND device
+ * @chip: NAND chip object
*/
-void nand_release(struct nand_chip *chip)
+void nand_cleanup(struct nand_chip *chip)
{
- struct mtd_info *mtd = nand_to_mtd(chip);
-
- if (chip->ecc.mode == NAND_ECC_SOFT_BCH)
+ if (chip->ecc.mode == NAND_ECC_SOFT &&
+ chip->ecc.algo == NAND_ECC_BCH)
nand_bch_free((struct nand_bch_control *)chip->ecc.priv);
- del_mtd_device(mtd);
+ nanddev_cleanup(&chip->base);
/* Free bad block table memory */
kfree(chip->bbt);
- if (!(chip->options & NAND_OWN_BUFFERS))
- kfree(chip->buffers);
+ kfree(chip->data_buf);
+ kfree(chip->ecc.code_buf);
+ kfree(chip->ecc.calc_buf);
/* Free bad block descriptor memory */
if (chip->badblock_pattern && chip->badblock_pattern->options
& NAND_BBT_DYNAMICSTRUCT)
kfree(chip->badblock_pattern);
-}
-EXPORT_SYMBOL_GPL(nand_release);
-
-static int mtd_set_erasebad(struct param_d *param, void *priv)
-{
- struct mtd_info *mtd = priv;
- if (!mtd->p_allow_erasebad) {
- mtd->allow_erasebad = false;
- return 0;
- }
+ /* Free the data interface */
+ kfree(chip->best_interface_config);
- if (!mtd->allow_erasebad)
- dev_warn(&mtd->dev,
- "Allowing to erase bad blocks. This may be dangerous!\n");
+ /* Free manufacturer priv data. */
+ nand_manufacturer_cleanup(chip);
- mtd->allow_erasebad = true;
+ /* Free controller specific allocations after chip identification */
+ nand_detach(chip);
- return 0;
+ /* Free identification phase allocations */
+ nand_scan_ident_cleanup(chip);
}
+EXPORT_SYMBOL_GPL(nand_cleanup);
+
enum bbt_type {
BBT_TYPE_NONE = 0,
BBT_TYPE_FLASHBASED,
@@ -3930,9 +5921,27 @@ static int mtd_get_bbt_type(struct param_d *p, void *priv)
return 0;
}
-int add_mtd_nand_device(struct nand_chip *chip, char *devname)
+static int mtd_set_erasebad(struct param_d *param, void *priv)
{
- struct mtd_info *mtd = nand_to_mtd(chip);
+ struct mtd_info *mtd = priv;
+
+ if (!mtd->p_allow_erasebad) {
+ mtd->allow_erasebad = false;
+ return 0;
+ }
+
+ if (!mtd->allow_erasebad)
+ dev_warn(&mtd->dev,
+ "Allowing to erase bad blocks. This may be dangerous!\n");
+
+ mtd->allow_erasebad = true;
+
+ return 0;
+}
+
+int add_mtd_nand_device(struct mtd_info *mtd, char *devname)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
int ret;
ret = add_mtd_device(mtd, devname, DEVICE_ID_DYNAMIC);
@@ -3945,8 +5954,7 @@ int add_mtd_nand_device(struct nand_chip *chip, char *devname)
dev_add_param_enum(&mtd->dev, "bbt", NULL, mtd_get_bbt_type,
&chip->bbt_type, bbt_type_strings,
- ARRAY_SIZE(bbt_type_strings),
- mtd);
+ ARRAY_SIZE(bbt_type_strings), mtd);
dev_add_param_uint32_ro(&mtd->dev, "ecc.bytes", &chip->ecc.bytes, "%u");
dev_add_param_uint32_ro(&mtd->dev, "ecc.strength", &chip->ecc.strength, "%u");
@@ -3954,3 +5962,8 @@ int add_mtd_nand_device(struct nand_chip *chip, char *devname)
return ret;
}
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Steven J. Hill <sjhill@realitydiluted.com>");
+MODULE_AUTHOR("Thomas Gleixner <tglx@linutronix.de>");
+MODULE_DESCRIPTION("Generic NAND flash driver code");
diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c
index 7d04c89d76..f582799636 100644
--- a/drivers/mtd/nand/nand_bbt.c
+++ b/drivers/mtd/nand/nand_bbt.c
@@ -1,15 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
- * drivers/mtd/nand_bbt.c
- *
* Overview:
* Bad block table support for the NAND driver
*
* Copyright © 2004 Thomas Gleixner (tglx@linutronix.de)
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
* Description:
*
* When nand_scan_bbt is called, then it tries to find the bad block table
@@ -56,18 +51,16 @@
* Following assumptions are made:
* - bbts start at a page boundary, if autolocated on a block boundary
* - the space necessary for a bbt in FLASH does not exceed a block boundary
- *
*/
-#include <common.h>
#include <linux/types.h>
#include <linux/mtd/mtd.h>
-#include <linux/mtd/nand.h>
-#include <linux/mtd/nand_ecc.h>
+#include <linux/mtd/bbm.h>
#include <linux/bitops.h>
-#include <clock.h>
-#include <errno.h>
-#include <malloc.h>
+#include <linux/export.h>
+#include <linux/string.h>
+
+#include "internals.h"
#define BBT_BLOCK_GOOD 0x00
#define BBT_BLOCK_WORN 0x01
@@ -164,7 +157,7 @@ static u32 add_marker_len(struct nand_bbt_descr *td)
/**
* read_bbt - [GENERIC] Read the bad block table starting from page
- * @mtd: MTD device structure
+ * @this: NAND chip object
* @buf: temporary buffer
* @page: the starting page
* @num: the number of bbt descriptors to read
@@ -174,7 +167,7 @@ static u32 add_marker_len(struct nand_bbt_descr *td)
* Read the bad block table starting from page.
*/
static int read_bbt(struct nand_chip *this, uint8_t *buf, int page, int num,
- struct nand_bbt_descr *td, int offs)
+ struct nand_bbt_descr *td, int offs)
{
struct mtd_info *mtd = nand_to_mtd(this);
int res, ret = 0, i, j, act = 0;
@@ -232,7 +225,11 @@ static int read_bbt(struct nand_chip *this, uint8_t *buf, int page, int num,
mtd->ecc_stats.bbtblocks++;
continue;
}
- pr_debug("nand_read_bbt: bad block at 0x%012llx\n",
+ /*
+ * Leave it for now, if it's matured we can
+ * move this message to pr_debug.
+ */
+ pr_info("nand_read_bbt: bad block at 0x%012llx\n",
(loff_t)(offs + act) <<
this->bbt_erase_shift);
/* Factory marked bad or worn out? */
@@ -253,7 +250,7 @@ static int read_bbt(struct nand_chip *this, uint8_t *buf, int page, int num,
/**
* read_abs_bbt - [GENERIC] Read the bad block table starting at a given page
- * @mtd: MTD device structure
+ * @this: NAND chip object
* @buf: temporary buffer
* @td: descriptor for the bad block table
* @chip: read the table for a specific chip, -1 read all chips; applies only if
@@ -262,22 +259,23 @@ static int read_bbt(struct nand_chip *this, uint8_t *buf, int page, int num,
* Read the bad block table for all chips starting at a given page. We assume
* that the bbt bits are in consecutive order.
*/
-static int read_abs_bbt(struct nand_chip *this, uint8_t *buf, struct nand_bbt_descr *td,
- int chip)
+static int read_abs_bbt(struct nand_chip *this, uint8_t *buf,
+ struct nand_bbt_descr *td, int chip)
{
struct mtd_info *mtd = nand_to_mtd(this);
+ u64 targetsize = nanddev_target_size(&this->base);
int res = 0, i;
if (td->options & NAND_BBT_PERCHIP) {
int offs = 0;
- for (i = 0; i < this->numchips; i++) {
+ for (i = 0; i < nanddev_ntargets(&this->base); i++) {
if (chip == -1 || chip == i)
res = read_bbt(this, buf, td->pages[i],
- this->chipsize >> this->bbt_erase_shift,
+ targetsize >> this->bbt_erase_shift,
td, offs);
if (res)
return res;
- offs += this->chipsize >> this->bbt_erase_shift;
+ offs += targetsize >> this->bbt_erase_shift;
}
} else {
res = read_bbt(this, buf, td->pages[0],
@@ -290,7 +288,7 @@ static int read_abs_bbt(struct nand_chip *this, uint8_t *buf, struct nand_bbt_de
/* BBT marker is in the first page, no OOB */
static int scan_read_data(struct nand_chip *this, uint8_t *buf, loff_t offs,
- struct nand_bbt_descr *td)
+ struct nand_bbt_descr *td)
{
struct mtd_info *mtd = nand_to_mtd(this);
size_t retlen;
@@ -305,7 +303,7 @@ static int scan_read_data(struct nand_chip *this, uint8_t *buf, loff_t offs,
/**
* scan_read_oob - [GENERIC] Scan data+OOB region to buffer
- * @mtd: MTD device structure
+ * @this: NAND chip object
* @buf: temporary buffer
* @offs: offset at which to scan
* @len: length of data region to read
@@ -346,7 +344,7 @@ static int scan_read_oob(struct nand_chip *this, uint8_t *buf, loff_t offs,
}
static int scan_read(struct nand_chip *this, uint8_t *buf, loff_t offs,
- size_t len, struct nand_bbt_descr *td)
+ size_t len, struct nand_bbt_descr *td)
{
if (td->options & NAND_BBT_NO_OOB)
return scan_read_data(this, buf, offs, td);
@@ -383,7 +381,7 @@ static u32 bbt_get_ver_offs(struct nand_chip *this, struct nand_bbt_descr *td)
/**
* read_abs_bbts - [GENERIC] Read the bad block table(s) for all chips starting at a given page
- * @mtd: MTD device structure
+ * @this: NAND chip object
* @buf: temporary buffer
* @td: descriptor for the bad block table
* @md: descriptor for the bad block table mirror
@@ -399,7 +397,7 @@ static void read_abs_bbts(struct nand_chip *this, uint8_t *buf,
/* Read the primary version, if available */
if (td->options & NAND_BBT_VERSION) {
scan_read(this, buf, (loff_t)td->pages[0] << this->page_shift,
- mtd->writesize, td);
+ mtd->writesize, td);
td->version[0] = buf[bbt_get_ver_offs(this, td)];
pr_info("Bad block table at page %d, version 0x%02X\n",
td->pages[0], td->version[0]);
@@ -408,7 +406,7 @@ static void read_abs_bbts(struct nand_chip *this, uint8_t *buf,
/* Read the mirror version, if available */
if (md && (md->options & NAND_BBT_VERSION)) {
scan_read(this, buf, (loff_t)md->pages[0] << this->page_shift,
- mtd->writesize, md);
+ mtd->writesize, md);
md->version[0] = buf[bbt_get_ver_offs(this, md)];
pr_info("Bad block table at page %d, version 0x%02X\n",
md->pages[0], md->version[0]);
@@ -417,11 +415,12 @@ static void read_abs_bbts(struct nand_chip *this, uint8_t *buf,
/* Scan a given block partially */
static int scan_block_fast(struct nand_chip *this, struct nand_bbt_descr *bd,
- loff_t offs, uint8_t *buf, int numpages)
+ loff_t offs, uint8_t *buf)
{
struct mtd_info *mtd = nand_to_mtd(this);
+
struct mtd_oob_ops ops;
- int j, ret;
+ int ret, page_offset;
ops.ooblen = mtd->oobsize;
ops.oobbuf = buf;
@@ -429,12 +428,15 @@ static int scan_block_fast(struct nand_chip *this, struct nand_bbt_descr *bd,
ops.datbuf = NULL;
ops.mode = MTD_OPS_PLACE_OOB;
- for (j = 0; j < numpages; j++) {
+ page_offset = nand_bbm_get_next_page(this, 0);
+
+ while (page_offset >= 0) {
/*
* Read the full oob until read_oob is fixed to handle single
* byte reads for 16 bit buswidth.
*/
- ret = mtd_read_oob(mtd, offs, &ops);
+ ret = mtd_read_oob(mtd, offs + (page_offset * mtd->writesize),
+ &ops);
/* Ignore ECC errors when checking for BBM */
if (ret && !mtd_is_bitflip_or_eccerr(ret))
return ret;
@@ -442,14 +444,15 @@ static int scan_block_fast(struct nand_chip *this, struct nand_bbt_descr *bd,
if (check_short_pattern(buf, bd))
return 1;
- offs += mtd->writesize;
+ page_offset = nand_bbm_get_next_page(this, page_offset + 1);
}
+
return 0;
}
/**
* create_bbt - [GENERIC] Create a bad block table by scanning the device
- * @mtd: MTD device structure
+ * @this: NAND chip object
* @buf: temporary buffer
* @bd: descriptor for the good/bad block search pattern
* @chip: create the table for a specific chip, -1 read all chips; applies only
@@ -459,45 +462,37 @@ static int scan_block_fast(struct nand_chip *this, struct nand_bbt_descr *bd,
* identify pattern.
*/
static int create_bbt(struct nand_chip *this, uint8_t *buf,
- struct nand_bbt_descr *bd, int chip)
+ struct nand_bbt_descr *bd, int chip)
{
+ u64 targetsize = nanddev_target_size(&this->base);
struct mtd_info *mtd = nand_to_mtd(this);
- int i, numblocks, numpages;
- int startblock;
+ int i, numblocks, startblock;
loff_t from;
pr_info("Scanning device for bad blocks\n");
- if (bd->options & NAND_BBT_SCAN2NDPAGE)
- numpages = 2;
- else
- numpages = 1;
-
if (chip == -1) {
numblocks = mtd->size >> this->bbt_erase_shift;
startblock = 0;
from = 0;
} else {
- if (chip >= this->numchips) {
+ if (chip >= nanddev_ntargets(&this->base)) {
pr_warn("create_bbt(): chipnr (%d) > available chips (%d)\n",
- chip + 1, this->numchips);
+ chip + 1, nanddev_ntargets(&this->base));
return -EINVAL;
}
- numblocks = this->chipsize >> this->bbt_erase_shift;
+ numblocks = targetsize >> this->bbt_erase_shift;
startblock = chip * numblocks;
numblocks += startblock;
from = (loff_t)startblock << this->bbt_erase_shift;
}
- if (this->bbt_options & NAND_BBT_SCANLASTPAGE)
- from += mtd->erasesize - (mtd->writesize * numpages);
-
for (i = startblock; i < numblocks; i++) {
int ret;
BUG_ON(bd->options & NAND_BBT_NO_OOB);
- ret = scan_block_fast(this, bd, from, buf, numpages);
+ ret = scan_block_fast(this, bd, from, buf);
if (ret < 0)
return ret;
@@ -515,7 +510,7 @@ static int create_bbt(struct nand_chip *this, uint8_t *buf,
/**
* search_bbt - [GENERIC] scan the device for a specific bad block table
- * @mtd: MTD device structure
+ * @this: NAND chip object
* @buf: temporary buffer
* @td: descriptor for the bad block table
*
@@ -528,8 +523,10 @@ static int create_bbt(struct nand_chip *this, uint8_t *buf,
*
* The bbt ident pattern resides in the oob area of the first page in a block.
*/
-static int search_bbt(struct nand_chip *this, uint8_t *buf, struct nand_bbt_descr *td)
+static int search_bbt(struct nand_chip *this, uint8_t *buf,
+ struct nand_bbt_descr *td)
{
+ u64 targetsize = nanddev_target_size(&this->base);
struct mtd_info *mtd = nand_to_mtd(this);
int i, chips;
int startblock, block, dir;
@@ -548,8 +545,8 @@ static int search_bbt(struct nand_chip *this, uint8_t *buf, struct nand_bbt_desc
/* Do we have a bbt per chip? */
if (td->options & NAND_BBT_PERCHIP) {
- chips = this->numchips;
- bbtblocks = this->chipsize >> this->bbt_erase_shift;
+ chips = nanddev_ntargets(&this->base);
+ bbtblocks = targetsize >> this->bbt_erase_shift;
startblock &= bbtblocks - 1;
} else {
chips = 1;
@@ -577,7 +574,7 @@ static int search_bbt(struct nand_chip *this, uint8_t *buf, struct nand_bbt_desc
break;
}
}
- startblock += this->chipsize >> this->bbt_erase_shift;
+ startblock += targetsize >> this->bbt_erase_shift;
}
/* Check, if we found a bbt for each requested chip */
for (i = 0; i < chips; i++) {
@@ -592,7 +589,7 @@ static int search_bbt(struct nand_chip *this, uint8_t *buf, struct nand_bbt_desc
/**
* search_read_bbts - [GENERIC] scan the device for bad block table(s)
- * @mtd: MTD device structure
+ * @this: NAND chip object
* @buf: temporary buffer
* @td: descriptor for the bad block table
* @md: descriptor for the bad block table mirror
@@ -627,6 +624,7 @@ static void search_read_bbts(struct nand_chip *this, uint8_t *buf,
static int get_bbt_block(struct nand_chip *this, struct nand_bbt_descr *td,
struct nand_bbt_descr *md, int chip)
{
+ u64 targetsize = nanddev_target_size(&this->base);
int startblock, dir, page, numblocks, i;
/*
@@ -638,9 +636,9 @@ static int get_bbt_block(struct nand_chip *this, struct nand_bbt_descr *td,
return td->pages[chip] >>
(this->bbt_erase_shift - this->page_shift);
- numblocks = (int)(this->chipsize >> this->bbt_erase_shift);
+ numblocks = (int)(targetsize >> this->bbt_erase_shift);
if (!(td->options & NAND_BBT_PERCHIP))
- numblocks *= this->numchips;
+ numblocks *= nanddev_ntargets(&this->base);
/*
* Automatic placement of the bad block table. Search direction
@@ -676,7 +674,7 @@ static int get_bbt_block(struct nand_chip *this, struct nand_bbt_descr *td,
/**
* mark_bbt_block_bad - Mark one of the block reserved for BBT bad
- * @mtd: the MTD device
+ * @this: the NAND device
* @td: the BBT description
* @chip: the CHIP selector
* @block: the BBT block to mark
@@ -696,7 +694,7 @@ static void mark_bbt_block_bad(struct nand_chip *this,
bbt_mark_entry(this, block, BBT_BLOCK_WORN);
to = (loff_t)block << this->bbt_erase_shift;
- res = this->legacy.block_markbad(this, to);
+ res = nand_markbad_bbm(this, to);
if (res)
pr_warn("nand_bbt: error %d while marking block %d bad\n",
res, block);
@@ -706,7 +704,7 @@ static void mark_bbt_block_bad(struct nand_chip *this,
/**
* write_bbt - [GENERIC] (Re)write the bad block table
- * @mtd: MTD device structure
+ * @this: NAND chip object
* @buf: temporary buffer
* @td: descriptor for the bad block table
* @md: descriptor for the bad block table mirror
@@ -718,6 +716,7 @@ static int write_bbt(struct nand_chip *this, uint8_t *buf,
struct nand_bbt_descr *td, struct nand_bbt_descr *md,
int chipsel)
{
+ u64 targetsize = nanddev_target_size(&this->base);
struct mtd_info *mtd = nand_to_mtd(this);
struct erase_info einfo;
int i, res, chip = 0;
@@ -738,10 +737,10 @@ static int write_bbt(struct nand_chip *this, uint8_t *buf,
rcode = 0xff;
/* Write bad block table per chip rather than per device? */
if (td->options & NAND_BBT_PERCHIP) {
- numblocks = (int)(this->chipsize >> this->bbt_erase_shift);
+ numblocks = (int)(targetsize >> this->bbt_erase_shift);
/* Full device write or specific chip? */
if (chipsel == -1) {
- nrchips = this->numchips;
+ nrchips = nanddev_ntargets(&this->base);
} else {
nrchips = chipsel + 1;
chip = chipsel;
@@ -792,7 +791,7 @@ static int write_bbt(struct nand_chip *this, uint8_t *buf,
/* Must we save the block contents? */
if (td->options & NAND_BBT_SAVECONTENT) {
/* Make it block aligned */
- to &= ~((loff_t)((1 << this->bbt_erase_shift) - 1));
+ to &= ~(((loff_t)1 << this->bbt_erase_shift) - 1);
len = 1 << this->bbt_erase_shift;
res = mtd_read(mtd, to, len, &retlen, buf);
if (res < 0) {
@@ -858,7 +857,6 @@ static int write_bbt(struct nand_chip *this, uint8_t *buf,
}
memset(&einfo, 0, sizeof(einfo));
- einfo.mtd = mtd;
einfo.addr = to;
einfo.len = 1 << this->bbt_erase_shift;
res = nand_erase_nand(this, &einfo, 1);
@@ -870,8 +868,8 @@ static int write_bbt(struct nand_chip *this, uint8_t *buf,
}
res = scan_write_bbt(this, to, len, buf,
- td->options & NAND_BBT_NO_OOB ? NULL :
- &buf[len]);
+ td->options & NAND_BBT_NO_OOB ?
+ NULL : &buf[len]);
if (res < 0) {
pr_warn("nand_bbt: error while writing BBT block %d\n",
res);
@@ -894,20 +892,23 @@ static int write_bbt(struct nand_chip *this, uint8_t *buf,
/**
* nand_memory_bbt - [GENERIC] create a memory based bad block table
- * @mtd: MTD device structure
+ * @this: NAND chip object
* @bd: descriptor for the good/bad block search pattern
*
* The function creates a memory based bbt by scanning the device for
* manufacturer / software marked good / bad blocks.
*/
-static inline int nand_memory_bbt(struct nand_chip *this, struct nand_bbt_descr *bd)
+static inline int nand_memory_bbt(struct nand_chip *this,
+ struct nand_bbt_descr *bd)
{
- return create_bbt(this, this->buffers->databuf, bd, -1);
+ u8 *pagebuf = nand_get_data_buf(this);
+
+ return create_bbt(this, pagebuf, bd, -1);
}
/**
* check_create - [GENERIC] create and write bbt(s) if necessary
- * @mtd: MTD device structure
+ * @this: the NAND device
* @buf: temporary buffer
* @bd: descriptor for the good/bad block search pattern
*
@@ -916,7 +917,8 @@ static inline int nand_memory_bbt(struct nand_chip *this, struct nand_bbt_descr
* for the chip/device. Update is necessary if one of the tables is missing or
* the version nr. of one table is less than the other.
*/
-static int check_create(struct nand_chip *this, uint8_t *buf, struct nand_bbt_descr *bd)
+static int check_create(struct nand_chip *this, uint8_t *buf,
+ struct nand_bbt_descr *bd)
{
int i, chips, writeops, create, chipsel, res, res2;
struct nand_bbt_descr *td = this->bbt_td;
@@ -925,7 +927,7 @@ static int check_create(struct nand_chip *this, uint8_t *buf, struct nand_bbt_de
/* Do we have a bbt per chip? */
if (td->options & NAND_BBT_PERCHIP)
- chips = this->numchips;
+ chips = nanddev_ntargets(&this->base);
else
chips = 1;
@@ -1032,9 +1034,64 @@ static int check_create(struct nand_chip *this, uint8_t *buf, struct nand_bbt_de
return 0;
}
+/**
+ * nand_update_bbt - update bad block table(s)
+ * @this: the NAND device
+ * @offs: the offset of the newly marked block
+ *
+ * The function updates the bad block table(s).
+ */
+int nand_update_bbt(struct nand_chip *this, loff_t offs)
+{
+ struct mtd_info *mtd = nand_to_mtd(this);
+ int len, res = 0;
+ int chip, chipsel;
+ uint8_t *buf;
+ struct nand_bbt_descr *td = this->bbt_td;
+ struct nand_bbt_descr *md = this->bbt_md;
+
+ if (!this->bbt || !td)
+ return -EINVAL;
+
+ /* Allocate a temporary buffer for one eraseblock incl. oob */
+ len = (1 << this->bbt_erase_shift);
+ len += (len >> this->page_shift) * mtd->oobsize;
+ buf = kmalloc(len, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ /* Do we have a bbt per chip? */
+ if (td->options & NAND_BBT_PERCHIP) {
+ chip = (int)(offs >> this->chip_shift);
+ chipsel = chip;
+ } else {
+ chip = 0;
+ chipsel = -1;
+ }
+
+ td->version[chip]++;
+ if (md)
+ md->version[chip]++;
+
+ /* Write the bad block table to the device? */
+ if (td->options & NAND_BBT_WRITE) {
+ res = write_bbt(this, buf, td, md, chipsel);
+ if (res < 0)
+ goto out;
+ }
+ /* Write the mirror bad block table to the device? */
+ if (md && (md->options & NAND_BBT_WRITE)) {
+ res = write_bbt(this, buf, md, td, chipsel);
+ }
+
+ out:
+ kfree(buf);
+ return res;
+}
+
/**
* mark_bbt_regions - [GENERIC] mark the bad block table regions
- * @mtd: MTD device structure
+ * @this: the NAND device
* @td: bad block table descriptor
*
* The bad block table regions are marked as "bad" to prevent accidental
@@ -1042,16 +1099,18 @@ static int check_create(struct nand_chip *this, uint8_t *buf, struct nand_bbt_de
*/
static void mark_bbt_region(struct nand_chip *this, struct nand_bbt_descr *td)
{
+ u64 targetsize = nanddev_target_size(&this->base);
+ struct mtd_info *mtd = nand_to_mtd(this);
int i, j, chips, block, nrblocks, update;
uint8_t oldval;
/* Do we have a bbt per chip? */
if (td->options & NAND_BBT_PERCHIP) {
- chips = this->numchips;
- nrblocks = (int)(this->chipsize >> this->bbt_erase_shift);
+ chips = nanddev_ntargets(&this->base);
+ nrblocks = (int)(targetsize >> this->bbt_erase_shift);
} else {
chips = 1;
- nrblocks = (int)(this->mtd.size >> this->bbt_erase_shift);
+ nrblocks = (int)(mtd->size >> this->bbt_erase_shift);
}
for (i = 0; i < chips; i++) {
@@ -1093,7 +1152,7 @@ static void mark_bbt_region(struct nand_chip *this, struct nand_bbt_descr *td)
/**
* verify_bbt_descr - verify the bad block description
- * @mtd: MTD device structure
+ * @this: the NAND device
* @bd: the table to verify
*
* This functions performs a few sanity checks on the bad block description
@@ -1101,6 +1160,7 @@ static void mark_bbt_region(struct nand_chip *this, struct nand_bbt_descr *td)
*/
static void verify_bbt_descr(struct nand_chip *this, struct nand_bbt_descr *bd)
{
+ u64 targetsize = nanddev_target_size(&this->base);
struct mtd_info *mtd = nand_to_mtd(this);
u32 pattern_len;
u32 bits;
@@ -1129,7 +1189,7 @@ static void verify_bbt_descr(struct nand_chip *this, struct nand_bbt_descr *bd)
}
if (bd->options & NAND_BBT_PERCHIP)
- table_size = this->chipsize >> this->bbt_erase_shift;
+ table_size = targetsize >> this->bbt_erase_shift;
else
table_size = mtd->size >> this->bbt_erase_shift;
table_size >>= 3;
@@ -1141,7 +1201,7 @@ static void verify_bbt_descr(struct nand_chip *this, struct nand_bbt_descr *bd)
/**
* nand_scan_bbt - [NAND Interface] scan, find, read and maybe create bad block table(s)
- * @mtd: MTD device structure
+ * @this: the NAND device
* @bd: descriptor for the good/bad block search pattern
*
* The function checks, if a bad block table(s) is/are already available. If
@@ -1159,7 +1219,7 @@ static int nand_scan_bbt(struct nand_chip *this, struct nand_bbt_descr *bd)
struct nand_bbt_descr *td = this->bbt_td;
struct nand_bbt_descr *md = this->bbt_md;
- len = mtd->size >> (this->bbt_erase_shift + 2);
+ len = (mtd->size >> (this->bbt_erase_shift + 2)) ? : 1;
/*
* Allocate memory (2bit per block) and clear the memory bad block
* table.
@@ -1169,13 +1229,13 @@ static int nand_scan_bbt(struct nand_chip *this, struct nand_bbt_descr *bd)
return -ENOMEM;
/*
- * If no primary table decriptor is given, scan the device to build a
+ * If no primary table descriptor is given, scan the device to build a
* memory based bad block table.
*/
if (!td) {
if ((res = nand_memory_bbt(this, bd))) {
pr_err("nand_bbt: can't scan flash and build the RAM-based BBT\n");
- goto err;
+ goto err_free_bbt;
}
return 0;
}
@@ -1188,7 +1248,7 @@ static int nand_scan_bbt(struct nand_chip *this, struct nand_bbt_descr *bd)
buf = vmalloc(len);
if (!buf) {
res = -ENOMEM;
- goto err;
+ goto err_free_bbt;
}
/* Is the bbt at a given page? */
@@ -1201,7 +1261,7 @@ static int nand_scan_bbt(struct nand_chip *this, struct nand_bbt_descr *bd)
res = check_create(this, buf, bd);
if (res)
- goto err;
+ goto err_free_buf;
/* Prevent the bbt regions from erasing / writing */
mark_bbt_region(this, td);
@@ -1211,66 +1271,14 @@ static int nand_scan_bbt(struct nand_chip *this, struct nand_bbt_descr *bd)
vfree(buf);
return 0;
-err:
+err_free_buf:
+ vfree(buf);
+err_free_bbt:
kfree(this->bbt);
this->bbt = NULL;
return res;
}
-/**
- * nand_update_bbt - update bad block table(s)
- * @mtd: MTD device structure
- * @offs: the offset of the newly marked block
- *
- * The function updates the bad block table(s).
- */
-int nand_update_bbt(struct nand_chip *this, loff_t offs)
-{
- int len, res = 0;
- int chip, chipsel;
- uint8_t *buf;
- struct nand_bbt_descr *td = this->bbt_td;
- struct nand_bbt_descr *md = this->bbt_md;
-
- if (!this->bbt || !td)
- return -EINVAL;
-
- /* Allocate a temporary buffer for one eraseblock incl. oob */
- len = (1 << this->bbt_erase_shift);
- len += (len >> this->page_shift) * this->mtd.oobsize;
- buf = kmalloc(len, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
-
- /* Do we have a bbt per chip? */
- if (td->options & NAND_BBT_PERCHIP) {
- chip = (int)(offs >> this->chip_shift);
- chipsel = chip;
- } else {
- chip = 0;
- chipsel = -1;
- }
-
- td->version[chip]++;
- if (md)
- md->version[chip]++;
-
- /* Write the bad block table to the device? */
- if (td->options & NAND_BBT_WRITE) {
- res = write_bbt(this, buf, td, md, chipsel);
- if (res < 0)
- goto out;
- }
- /* Write the mirror bad block table to the device? */
- if (md && (md->options & NAND_BBT_WRITE)) {
- res = write_bbt(this, buf, md, td, chipsel);
- }
-
- out:
- kfree(buf);
- return res;
-}
-
/*
* Define some generic bad / good block scan pattern which are used
* while scanning a device for factory marked good / bad blocks.
@@ -1351,13 +1359,13 @@ static int nand_create_badblock_pattern(struct nand_chip *this)
}
/**
- * nand_default_bbt - [NAND Interface] Select a default bad block table for the device
- * @mtd: MTD device structure
+ * nand_create_bbt - [NAND Interface] Select a default bad block table for the device
+ * @this: NAND chip object
*
* This function selects the default bad block table support for the device and
* calls the nand_scan_bbt function.
*/
-int nand_default_bbt(struct nand_chip *this)
+int nand_create_bbt(struct nand_chip *this)
{
int ret;
@@ -1386,17 +1394,30 @@ int nand_default_bbt(struct nand_chip *this)
return nand_scan_bbt(this, this->badblock_pattern);
}
+EXPORT_SYMBOL(nand_create_bbt);
+
+/**
+ * nand_isreserved_bbt - [NAND Interface] Check if a block is reserved
+ * @this: NAND chip object
+ * @offs: offset in the device
+ */
+int nand_isreserved_bbt(struct nand_chip *this, loff_t offs)
+{
+ int block;
+
+ block = (int)(offs >> this->bbt_erase_shift);
+ return bbt_get_entry(this, block) == BBT_BLOCK_RESERVED;
+}
/**
* nand_isbad_bbt - [NAND Interface] Check if a block is bad
- * @mtd: MTD device structure
+ * @this: NAND chip object
* @offs: offset in the device
* @allowbbt: allow access to bad block table region
*/
int nand_isbad_bbt(struct nand_chip *this, loff_t offs, int allowbbt)
{
- int block;
- uint8_t res;
+ int block, res;
block = (int)(offs >> this->bbt_erase_shift);
res = bbt_get_entry(this, block);
@@ -1404,7 +1425,7 @@ int nand_isbad_bbt(struct nand_chip *this, loff_t offs, int allowbbt)
pr_debug("nand_isbad_bbt(): bbt info for offs 0x%08x: (block %d) 0x%02x\n",
(unsigned int)offs, block, res);
- switch ((int)res) {
+ switch (res) {
case BBT_BLOCK_GOOD:
return 0;
case BBT_BLOCK_WORN:
@@ -1450,7 +1471,3 @@ int nand_markgood_bbt(struct nand_chip *this, loff_t offs)
{
return nand_mark_bbt(this, offs, BBT_BLOCK_GOOD);
}
-
-EXPORT_SYMBOL(nand_scan_bbt);
-EXPORT_SYMBOL(nand_default_bbt);
-EXPORT_SYMBOL_GPL(nand_update_bbt);
diff --git a/drivers/mtd/nand/nand_bch.c b/drivers/mtd/nand/nand_bch.c
index 42ffa1b21a..0d636d9608 100644
--- a/drivers/mtd/nand/nand_bch.c
+++ b/drivers/mtd/nand/nand_bch.c
@@ -1,19 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* This file provides ECC correction for more than 1 bit per block of data,
* using binary BCH codes. It relies on the generic BCH library lib/bch.c.
*
* Copyright © 2011 Ivan Djelic <ivan.djelic@parrot.com>
- *
- * This file is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 or (at your option) any
- * later version.
- *
- * This file is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- *
*/
#include <common.h>
@@ -22,6 +12,7 @@
#include <linux/kernel.h>
#include <linux/bitops.h>
#include <linux/mtd/mtd.h>
+#include <linux/mtd/rawnand.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/nand_bch.h>
#include <linux/bch.h>
@@ -29,20 +20,18 @@
/**
* struct nand_bch_control - private NAND BCH control structure
* @bch: BCH control structure
- * @ecclayout: private ecc layout for this BCH configuration
* @errloc: error location array
* @eccmask: XOR ecc mask, allows erased pages to be decoded as valid
*/
struct nand_bch_control {
struct bch_control *bch;
- struct nand_ecclayout ecclayout;
unsigned int *errloc;
unsigned char *eccmask;
};
/**
* nand_bch_calculate_ecc - [NAND Interface] Calculate ECC for data block
- * @mtd: MTD block structure
+ * @chip: NAND chip object
* @buf: input buffer with raw data
* @code: output buffer with ECC
*/
@@ -65,7 +54,7 @@ EXPORT_SYMBOL(nand_bch_calculate_ecc);
/**
* nand_bch_correct_data - [NAND Interface] Detect and correct bit error(s)
- * @mtd: MTD block structure
+ * @chip: NAND chip object
* @buf: raw data read from the chip
* @read_ecc: ECC from the chip
* @calc_ecc: the ECC calculated from raw data
@@ -92,8 +81,8 @@ int nand_bch_correct_data(struct nand_chip *chip, unsigned char *buf,
errloc[i]);
}
} else if (count < 0) {
- printk(KERN_ERR "ecc unrecoverable error\n");
- count = -1;
+ pr_err("ecc unrecoverable error\n");
+ count = -EBADMSG;
}
return count;
}
@@ -115,11 +104,10 @@ EXPORT_SYMBOL(nand_bch_correct_data);
* @eccsize = 512 (thus, m=13 is the smallest integer such that 2^m-1 > 512*8)
* @eccbytes = 7 (7 bytes are required to store m*t = 13*4 = 52 bits)
*/
-struct nand_bch_control *nand_bch_init(struct nand_chip *nand)
+struct nand_bch_control *nand_bch_init(struct mtd_info *mtd)
{
- struct mtd_info *mtd = nand_to_mtd(nand);
+ struct nand_chip *nand = mtd_to_nand(mtd);
unsigned int m, t, eccsteps, i;
- struct nand_ecclayout *layout = nand->ecc.layout;
struct nand_bch_control *nbc = NULL;
unsigned char *erased_page;
unsigned int eccsize = nand->ecc.size;
@@ -132,7 +120,7 @@ struct nand_bch_control *nand_bch_init(struct nand_chip *nand)
}
if (!eccsize || !eccbytes) {
- printk(KERN_WARNING "ecc parameters not supplied\n");
+ pr_warn("ecc parameters not supplied\n");
goto fail;
}
@@ -149,55 +137,42 @@ struct nand_bch_control *nand_bch_init(struct nand_chip *nand)
/* verify that eccbytes has the expected value */
if (nbc->bch->ecc_bytes != eccbytes) {
- printk(KERN_WARNING "invalid eccbytes %u, should be %u\n",
- eccbytes, nbc->bch->ecc_bytes);
+ pr_warn("invalid eccbytes %u, should be %u\n",
+ eccbytes, nbc->bch->ecc_bytes);
goto fail;
}
eccsteps = mtd->writesize/eccsize;
- /* if no ecc placement scheme was provided, build one */
- if (!layout) {
-
- /* handle large page devices only */
- if (mtd->oobsize < 64) {
- printk(KERN_WARNING "must provide an oob scheme for "
- "oobsize %d\n", mtd->oobsize);
- goto fail;
- }
-
- layout = &nbc->ecclayout;
- layout->eccbytes = eccsteps*eccbytes;
-
- /* reserve 2 bytes for bad block marker */
- if (layout->eccbytes+2 > mtd->oobsize) {
- printk(KERN_WARNING "no suitable oob scheme available "
- "for oobsize %d eccbytes %u\n", mtd->oobsize,
- eccbytes);
- goto fail;
- }
- /* put ecc bytes at oob tail */
- for (i = 0; i < layout->eccbytes; i++)
- layout->eccpos[i] = mtd->oobsize-layout->eccbytes+i;
-
- layout->oobfree[0].offset = 2;
- layout->oobfree[0].length = mtd->oobsize-2-layout->eccbytes;
-
- nand->ecc.layout = layout;
+ /* Check that we have an oob layout description. */
+ if (!mtd->ooblayout) {
+ pr_warn("missing oob scheme");
+ goto fail;
}
/* sanity checks */
if (8*(eccsize+eccbytes) >= (1 << m)) {
- printk(KERN_WARNING "eccsize %u is too large\n", eccsize);
+ pr_warn("eccsize %u is too large\n", eccsize);
goto fail;
}
- if (layout->eccbytes != (eccsteps*eccbytes)) {
- printk(KERN_WARNING "invalid ecc layout\n");
+
+ /*
+ * ecc->steps and ecc->total might be used by mtd->ooblayout->ecc(),
+ * which is called by mtd_ooblayout_count_eccbytes().
+ * Make sure they are properly initialized before calling
+ * mtd_ooblayout_count_eccbytes().
+ * FIXME: we should probably rework the sequencing in nand_scan_tail()
+ * to avoid setting those fields twice.
+ */
+ nand->ecc.steps = eccsteps;
+ nand->ecc.total = eccsteps * eccbytes;
+ if (mtd_ooblayout_count_eccbytes(mtd) != (eccsteps*eccbytes)) {
+ pr_warn("invalid ecc layout\n");
goto fail;
}
- nbc->eccmask = kmalloc(eccbytes, GFP_KERNEL);
- nbc->errloc = kmalloc(t*sizeof(*nbc->errloc), GFP_KERNEL);
+ nbc->eccmask = kzalloc(eccbytes, GFP_KERNEL);
+ nbc->errloc = kmalloc_array(t, sizeof(*nbc->errloc), GFP_KERNEL);
if (!nbc->eccmask || !nbc->errloc)
goto fail;
/*
@@ -208,7 +183,6 @@ struct nand_bch_control *nand_bch_init(struct nand_chip *nand)
goto fail;
memset(erased_page, 0xff, eccsize);
- memset(nbc->eccmask, 0, eccbytes);
encode_bch(nbc->bch, erased_page, eccsize, nbc->eccmask);
kfree(erased_page);
diff --git a/drivers/mtd/nand/nand_denali.c b/drivers/mtd/nand/nand_denali.c
index bffcbd8a7f..49028bf082 100644
--- a/drivers/mtd/nand/nand_denali.c
+++ b/drivers/mtd/nand/nand_denali.c
@@ -23,6 +23,7 @@
#include <malloc.h>
#include <init.h>
#include <linux/mtd/mtd.h>
+#include <linux/mtd/rawnand.h>
#include <linux/mtd/nand.h>
#include <io.h>
#include <clock.h>
@@ -888,6 +889,8 @@ static bool is_erased(uint8_t *buf, int len)
static bool handle_ecc(struct denali_nand_info *denali, uint8_t *buf,
uint32_t irq_status, unsigned int *max_bitflips)
{
+ struct nand_chip *chip = &denali->nand;
+ struct mtd_info *mtd = nand_to_mtd(chip);
bool check_erased_page = false;
unsigned int bitflips = 0;
@@ -933,7 +936,7 @@ static bool handle_ecc(struct denali_nand_info *denali, uint8_t *buf,
err_device;
/* correct the ECC error */
buf[offset] ^= err_correction_value;
- denali->nand.mtd.ecc_stats.corrected++;
+ mtd->ecc_stats.corrected++;
bitflips++;
}
} else {
@@ -995,7 +998,8 @@ static void denali_setup_dma(struct denali_nand_info *denali, int op)
* writes a page. user specifies type, and this function handles the
* configuration details.
*/
-static int write_page(struct nand_chip *chip, const uint8_t *buf, bool raw_xfer)
+static int write_page(struct nand_chip *chip, const uint8_t *buf, bool raw_xfer,
+ int page)
{
struct mtd_info *mtd = nand_to_mtd(chip);
struct denali_nand_info *denali = nand_to_denali(chip);
@@ -1012,6 +1016,8 @@ static int write_page(struct nand_chip *chip, const uint8_t *buf, bool raw_xfer)
*/
setup_ecc_for_xfer(denali, !raw_xfer, raw_xfer);
+ nand_prog_page_begin_op(chip, page, 0, NULL, 0);
+
/* copy buffer into DMA buffer */
memcpy(denali->buf.buf, buf, mtd->writesize);
@@ -1041,7 +1047,7 @@ static int write_page(struct nand_chip *chip, const uint8_t *buf, bool raw_xfer)
denali_enable_dma(denali, false);
dma_sync_single_for_cpu(addr, size, DMA_TO_DEVICE);
- return 0;
+ return nand_prog_page_end_op(chip);
}
/* NAND core entry points */
@@ -1052,13 +1058,13 @@ static int write_page(struct nand_chip *chip, const uint8_t *buf, bool raw_xfer)
* by write_page above.
*/
static int denali_write_page(struct nand_chip *chip,
- const uint8_t *buf, int oob_required)
+ const uint8_t *buf, int oob_required, int page)
{
/*
* for regular page writes, we let HW handle all the ECC
* data written to the device.
*/
- return write_page(chip, buf, false);
+ return write_page(chip, buf, false, page);
}
/*
@@ -1067,13 +1073,13 @@ static int denali_write_page(struct nand_chip *chip,
* write_page() function above.
*/
static int denali_write_page_raw(struct nand_chip *chip,
- const uint8_t *buf, int oob_required)
+ const uint8_t *buf, int oob_required, int page)
{
/*
* for raw page writes, we want to disable ECC and simply write
* whatever data is in the buffer.
*/
- return write_page(chip, buf, true);
+ return write_page(chip, buf, true, page);
}
static int denali_write_oob(struct nand_chip *chip, int page)
@@ -1104,15 +1110,10 @@ static int denali_read_page(struct nand_chip *chip,
(INTR_STATUS__ECC_TRANSACTION_DONE | INTR_STATUS__ECC_ERR);
bool check_erased_page = false;
- if (page != denali->page) {
- dev_err(denali->dev,
- "IN %s: page %d is not equal to denali->page %d",
- __func__, page, denali->page);
- BUG();
- }
-
setup_ecc_for_xfer(denali, true, false);
+ nand_read_page_op(chip, page, 0, NULL, 0);
+
denali_enable_dma(denali, true);
dma_sync_single_for_device(addr, size, DMA_FROM_DEVICE);
@@ -1161,6 +1162,8 @@ static int denali_read_page_raw(struct nand_chip *chip,
size_t size = mtd->writesize + mtd->oobsize;
uint32_t irq_mask = INTR_STATUS__DMA_CMD_COMP;
+ nand_read_page_op(chip, page, 0, NULL, 0);
+
if (page != denali->page) {
dev_err(denali->dev,
"IN %s: page %d is not equal to denali->page %d",
@@ -1227,11 +1230,8 @@ static int denali_waitfunc(struct nand_chip *chip)
static void denali_cmdfunc(struct nand_chip *chip, unsigned int cmd, int col,
int page)
{
- struct mtd_info *mtd = nand_to_mtd(chip);
struct denali_nand_info *denali = nand_to_denali(chip);
uint32_t addr, id;
- uint32_t pages_per_block;
- uint32_t block;
int i;
switch (cmd) {
@@ -1296,18 +1296,6 @@ static void denali_cmdfunc(struct nand_chip *chip, unsigned int cmd, int col,
case NAND_CMD_READOOB:
/* TODO: Read OOB data */
break;
- case NAND_CMD_UNLOCK1:
- pages_per_block = mtd->erasesize / mtd->writesize;
- block = page / pages_per_block;
- addr = (uint32_t)MODE_10 | (block * pages_per_block);
- index_addr(denali, addr, 0x10);
- break;
- case NAND_CMD_UNLOCK2:
- pages_per_block = mtd->erasesize / mtd->writesize;
- block = (page+pages_per_block-1) / pages_per_block;
- addr = (uint32_t)MODE_10 | (block * pages_per_block);
- index_addr(denali, addr, 0x11);
- break;
case NAND_CMD_ERASE1:
case NAND_CMD_ERASE2:
addr = MODE_10 | BANK(denali->flash_bank) | page;
@@ -1376,9 +1364,10 @@ static void denali_drv_init(struct denali_nand_info *denali)
int denali_init(struct denali_nand_info *denali)
{
struct nand_chip *nand = &denali->nand;
- struct mtd_info *mtd = &nand->mtd;
+ struct mtd_info *mtd = nand_to_mtd(nand);
int ret = 0;
uint32_t val;
+ struct nand_ecclayout *ecclayout;
if (denali->platform == INTEL_CE4100) {
/*
@@ -1477,7 +1466,7 @@ int denali_init(struct denali_nand_info *denali)
ECC_SECTOR_SIZE)))) {
/* if MLC OOB size is large enough, use 15bit ECC*/
nand->ecc.strength = 15;
- nand->ecc.layout = &nand_15bit_oob;
+ ecclayout = &nand_15bit_oob;
nand->ecc.bytes = ECC_15BITS;
iowrite32(15, denali->flash_reg + ECC_CORRECTION);
} else if (mtd->oobsize < (denali->bbtskipbytes +
@@ -1487,24 +1476,26 @@ int denali_init(struct denali_nand_info *denali)
goto failed_req_irq;
} else {
nand->ecc.strength = 8;
- nand->ecc.layout = &nand_8bit_oob;
+ ecclayout = &nand_8bit_oob;
nand->ecc.bytes = ECC_8BITS;
iowrite32(8, denali->flash_reg + ECC_CORRECTION);
}
- nand->ecc.layout->oobfree[0].offset =
- denali->bbtskipbytes + nand->ecc.layout->eccbytes;
- nand->ecc.layout->oobfree[0].length =
- mtd->oobsize - nand->ecc.layout->eccbytes -
+ ecclayout->oobfree[0].offset =
+ denali->bbtskipbytes + ecclayout->eccbytes;
+ ecclayout->oobfree[0].length =
+ mtd->oobsize - ecclayout->eccbytes -
denali->bbtskipbytes;
+ mtd_set_ecclayout(mtd, ecclayout);
+
/*
* Let driver know the total blocks number and how many blocks
* contained by each nand chip. blksperchip will help driver to
* know how many blocks is taken by FW.
*/
denali->totalblks = mtd->size >> nand->phys_erase_shift;
- denali->blksperchip = denali->totalblks / nand->numchips;
+ denali->blksperchip = denali->totalblks;
/* override the default read operations */
nand->ecc.size = ECC_SECTOR_SIZE;
@@ -1534,7 +1525,7 @@ int denali_init(struct denali_nand_info *denali)
goto failed_req_irq;
}
- return add_mtd_nand_device(nand, "nand");
+ return add_mtd_nand_device(mtd, "nand");
failed_req_irq:
denali_irq_cleanup(denali->irq, denali);
diff --git a/drivers/mtd/nand/nand_ecc.c b/drivers/mtd/nand/nand_ecc.c
index e488130f73..58fb335bb4 100644
--- a/drivers/mtd/nand/nand_ecc.c
+++ b/drivers/mtd/nand/nand_ecc.c
@@ -1,194 +1,483 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
- * This file contains an ECC algorithm from Toshiba that detects and
- * corrects 1 bit errors in a 256 byte block of data.
+ * This file contains an ECC algorithm that detects and corrects 1 bit
+ * errors in a 256 byte block of data.
*
- * drivers/mtd/nand/nand_ecc.c
+ * Copyright © 2008 Koninklijke Philips Electronics NV.
+ * Author: Frans Meulenbroeks
*
- * Copyright (C) 2000-2004 Steven J. Hill (sjhill@realitydiluted.com)
- * Toshiba America Electronics Components, Inc.
+ * Completely replaces the previous ECC implementation which was written by:
+ * Steven J. Hill (sjhill@realitydiluted.com)
+ * Thomas Gleixner (tglx@linutronix.de)
*
- * Copyright (C) 2006 Thomas Gleixner <tglx@linutronix.de>
- *
- * $Id: nand_ecc.c,v 1.15 2005/11/07 11:14:30 gleixner Exp $
- *
- * This file is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 or (at your option) any
- * later version.
- *
- * This file is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- *
- * As a special exception, if other files instantiate templates or use
- * macros or inline functions from these files, or you compile these
- * files and link them with other works to produce a work based on these
- * files, these files do not by themselves cause the resulting work to be
- * covered by the GNU General Public License. However the source code for
- * these files must still be made available in accordance with section (3)
- * of the GNU General Public License.
- *
- * This exception does not invalidate any other reasons why a work based on
- * this file might be covered by the GNU General Public License.
+ * Information on how this algorithm works and how it was developed
+ * can be found in Documentation/driver-api/mtd/nand_ecc.rst
*/
#include <linux/types.h>
-#include <common.h>
-#include <errno.h>
+#include <linux/kernel.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/rawnand.h>
#include <linux/mtd/nand_ecc.h>
+#include <asm/byteorder.h>
+
+/*
+ * invparity is a 256 byte table that contains the odd parity
+ * for each byte. So if the number of bits in a byte is even,
+ * the array element is 1, and when the number of bits is odd
+ * the array eleemnt is 0.
+ */
+static const char invparity[256] = {
+ 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
+ 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
+ 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
+ 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
+ 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
+ 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
+ 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
+ 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
+ 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
+ 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
+ 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
+ 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
+ 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
+ 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
+ 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
+ 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1
+};
+
+/*
+ * bitsperbyte contains the number of bits per byte
+ * this is only used for testing and repairing parity
+ * (a precalculated value slightly improves performance)
+ */
+static const char bitsperbyte[256] = {
+ 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
+ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+ 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8,
+};
/*
- * Pre-calculated 256-way 1 byte column parity
+ * addressbits is a lookup table to filter out the bits from the xor-ed
+ * ECC data that identify the faulty location.
+ * this is only used for repairing parity
+ * see the comments in nand_correct_data for more details
*/
-static const u_char nand_ecc_precalc_table[] = {
- 0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00,
- 0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65,
- 0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66,
- 0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03,
- 0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69,
- 0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c,
- 0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f,
- 0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a,
- 0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a,
- 0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f,
- 0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c,
- 0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69,
- 0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03,
- 0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66,
- 0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65,
- 0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00
+static const char addressbits[256] = {
+ 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01,
+ 0x02, 0x02, 0x03, 0x03, 0x02, 0x02, 0x03, 0x03,
+ 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01,
+ 0x02, 0x02, 0x03, 0x03, 0x02, 0x02, 0x03, 0x03,
+ 0x04, 0x04, 0x05, 0x05, 0x04, 0x04, 0x05, 0x05,
+ 0x06, 0x06, 0x07, 0x07, 0x06, 0x06, 0x07, 0x07,
+ 0x04, 0x04, 0x05, 0x05, 0x04, 0x04, 0x05, 0x05,
+ 0x06, 0x06, 0x07, 0x07, 0x06, 0x06, 0x07, 0x07,
+ 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01,
+ 0x02, 0x02, 0x03, 0x03, 0x02, 0x02, 0x03, 0x03,
+ 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01,
+ 0x02, 0x02, 0x03, 0x03, 0x02, 0x02, 0x03, 0x03,
+ 0x04, 0x04, 0x05, 0x05, 0x04, 0x04, 0x05, 0x05,
+ 0x06, 0x06, 0x07, 0x07, 0x06, 0x06, 0x07, 0x07,
+ 0x04, 0x04, 0x05, 0x05, 0x04, 0x04, 0x05, 0x05,
+ 0x06, 0x06, 0x07, 0x07, 0x06, 0x06, 0x07, 0x07,
+ 0x08, 0x08, 0x09, 0x09, 0x08, 0x08, 0x09, 0x09,
+ 0x0a, 0x0a, 0x0b, 0x0b, 0x0a, 0x0a, 0x0b, 0x0b,
+ 0x08, 0x08, 0x09, 0x09, 0x08, 0x08, 0x09, 0x09,
+ 0x0a, 0x0a, 0x0b, 0x0b, 0x0a, 0x0a, 0x0b, 0x0b,
+ 0x0c, 0x0c, 0x0d, 0x0d, 0x0c, 0x0c, 0x0d, 0x0d,
+ 0x0e, 0x0e, 0x0f, 0x0f, 0x0e, 0x0e, 0x0f, 0x0f,
+ 0x0c, 0x0c, 0x0d, 0x0d, 0x0c, 0x0c, 0x0d, 0x0d,
+ 0x0e, 0x0e, 0x0f, 0x0f, 0x0e, 0x0e, 0x0f, 0x0f,
+ 0x08, 0x08, 0x09, 0x09, 0x08, 0x08, 0x09, 0x09,
+ 0x0a, 0x0a, 0x0b, 0x0b, 0x0a, 0x0a, 0x0b, 0x0b,
+ 0x08, 0x08, 0x09, 0x09, 0x08, 0x08, 0x09, 0x09,
+ 0x0a, 0x0a, 0x0b, 0x0b, 0x0a, 0x0a, 0x0b, 0x0b,
+ 0x0c, 0x0c, 0x0d, 0x0d, 0x0c, 0x0c, 0x0d, 0x0d,
+ 0x0e, 0x0e, 0x0f, 0x0f, 0x0e, 0x0e, 0x0f, 0x0f,
+ 0x0c, 0x0c, 0x0d, 0x0d, 0x0c, 0x0c, 0x0d, 0x0d,
+ 0x0e, 0x0e, 0x0f, 0x0f, 0x0e, 0x0e, 0x0f, 0x0f
};
/**
- * nand_calculate_ecc - [NAND Interface] Calculate 3-byte ECC for 256-byte block
- * @mtd: MTD block structure
- * @dat: raw data
- * @ecc_code: buffer for ECC
+ * __nand_calculate_ecc - [NAND Interface] Calculate 3-byte ECC for 256/512-byte
+ * block
+ * @buf: input buffer with raw data
+ * @eccsize: data bytes per ECC step (256 or 512)
+ * @code: output buffer with ECC
+ * @sm_order: Smart Media byte ordering
*/
-int nand_calculate_ecc(struct nand_chip *chip, const u_char *dat,
- u_char *ecc_code)
+void __nand_calculate_ecc(const unsigned char *buf, unsigned int eccsize,
+ unsigned char *code, bool sm_order)
{
- uint8_t idx, reg1, reg2, reg3, tmp1, tmp2;
int i;
+ const uint32_t *bp = (uint32_t *)buf;
+ /* 256 or 512 bytes/ecc */
+ const uint32_t eccsize_mult = eccsize >> 8;
+ uint32_t cur; /* current value in buffer */
+ /* rp0..rp15..rp17 are the various accumulated parities (per byte) */
+ uint32_t rp0, rp1, rp2, rp3, rp4, rp5, rp6, rp7;
+ uint32_t rp8, rp9, rp10, rp11, rp12, rp13, rp14, rp15, rp16;
+ uint32_t rp17 = 0;
+ uint32_t par; /* the cumulative parity for all data */
+ uint32_t tmppar; /* the cumulative parity for this iteration;
+ for rp12, rp14 and rp16 at the end of the
+ loop */
- /* Initialize variables */
- reg1 = reg2 = reg3 = 0;
+ par = 0;
+ rp4 = 0;
+ rp6 = 0;
+ rp8 = 0;
+ rp10 = 0;
+ rp12 = 0;
+ rp14 = 0;
+ rp16 = 0;
- /* Build up column parity */
- for(i = 0; i < 256; i++) {
- /* Get CP0 - CP5 from table */
- idx = nand_ecc_precalc_table[*dat++];
- reg1 ^= (idx & 0x3f);
+ /*
+ * The loop is unrolled a number of times;
+ * This avoids if statements to decide on which rp value to update
+ * Also we process the data by longwords.
+ * Note: passing unaligned data might give a performance penalty.
+ * It is assumed that the buffers are aligned.
+ * tmppar is the cumulative sum of this iteration.
+ * needed for calculating rp12, rp14, rp16 and par
+ * also used as a performance improvement for rp6, rp8 and rp10
+ */
+ for (i = 0; i < eccsize_mult << 2; i++) {
+ cur = *bp++;
+ tmppar = cur;
+ rp4 ^= cur;
+ cur = *bp++;
+ tmppar ^= cur;
+ rp6 ^= tmppar;
+ cur = *bp++;
+ tmppar ^= cur;
+ rp4 ^= cur;
+ cur = *bp++;
+ tmppar ^= cur;
+ rp8 ^= tmppar;
- /* All bit XOR = 1 ? */
- if (idx & 0x40) {
- reg3 ^= (uint8_t) i;
- reg2 ^= ~((uint8_t) i);
- }
+ cur = *bp++;
+ tmppar ^= cur;
+ rp4 ^= cur;
+ rp6 ^= cur;
+ cur = *bp++;
+ tmppar ^= cur;
+ rp6 ^= cur;
+ cur = *bp++;
+ tmppar ^= cur;
+ rp4 ^= cur;
+ cur = *bp++;
+ tmppar ^= cur;
+ rp10 ^= tmppar;
+
+ cur = *bp++;
+ tmppar ^= cur;
+ rp4 ^= cur;
+ rp6 ^= cur;
+ rp8 ^= cur;
+ cur = *bp++;
+ tmppar ^= cur;
+ rp6 ^= cur;
+ rp8 ^= cur;
+ cur = *bp++;
+ tmppar ^= cur;
+ rp4 ^= cur;
+ rp8 ^= cur;
+ cur = *bp++;
+ tmppar ^= cur;
+ rp8 ^= cur;
+
+ cur = *bp++;
+ tmppar ^= cur;
+ rp4 ^= cur;
+ rp6 ^= cur;
+ cur = *bp++;
+ tmppar ^= cur;
+ rp6 ^= cur;
+ cur = *bp++;
+ tmppar ^= cur;
+ rp4 ^= cur;
+ cur = *bp++;
+ tmppar ^= cur;
+
+ par ^= tmppar;
+ if ((i & 0x1) == 0)
+ rp12 ^= tmppar;
+ if ((i & 0x2) == 0)
+ rp14 ^= tmppar;
+ if (eccsize_mult == 2 && (i & 0x4) == 0)
+ rp16 ^= tmppar;
}
- /* Create non-inverted ECC code from line parity */
- tmp1 = (reg3 & 0x80) >> 0; /* B7 -> B7 */
- tmp1 |= (reg2 & 0x80) >> 1; /* B7 -> B6 */
- tmp1 |= (reg3 & 0x40) >> 1; /* B6 -> B5 */
- tmp1 |= (reg2 & 0x40) >> 2; /* B6 -> B4 */
- tmp1 |= (reg3 & 0x20) >> 2; /* B5 -> B3 */
- tmp1 |= (reg2 & 0x20) >> 3; /* B5 -> B2 */
- tmp1 |= (reg3 & 0x10) >> 3; /* B4 -> B1 */
- tmp1 |= (reg2 & 0x10) >> 4; /* B4 -> B0 */
-
- tmp2 = (reg3 & 0x08) << 4; /* B3 -> B7 */
- tmp2 |= (reg2 & 0x08) << 3; /* B3 -> B6 */
- tmp2 |= (reg3 & 0x04) << 3; /* B2 -> B5 */
- tmp2 |= (reg2 & 0x04) << 2; /* B2 -> B4 */
- tmp2 |= (reg3 & 0x02) << 2; /* B1 -> B3 */
- tmp2 |= (reg2 & 0x02) << 1; /* B1 -> B2 */
- tmp2 |= (reg3 & 0x01) << 1; /* B0 -> B1 */
- tmp2 |= (reg2 & 0x01) << 0; /* B7 -> B0 */
-
- /* Calculate final ECC code */
-#ifdef CONFIG_MTD_NAND_ECC_SMC
- ecc_code[0] = ~tmp2;
- ecc_code[1] = ~tmp1;
+ /*
+ * handle the fact that we use longword operations
+ * we'll bring rp4..rp14..rp16 back to single byte entities by
+ * shifting and xoring first fold the upper and lower 16 bits,
+ * then the upper and lower 8 bits.
+ */
+ rp4 ^= (rp4 >> 16);
+ rp4 ^= (rp4 >> 8);
+ rp4 &= 0xff;
+ rp6 ^= (rp6 >> 16);
+ rp6 ^= (rp6 >> 8);
+ rp6 &= 0xff;
+ rp8 ^= (rp8 >> 16);
+ rp8 ^= (rp8 >> 8);
+ rp8 &= 0xff;
+ rp10 ^= (rp10 >> 16);
+ rp10 ^= (rp10 >> 8);
+ rp10 &= 0xff;
+ rp12 ^= (rp12 >> 16);
+ rp12 ^= (rp12 >> 8);
+ rp12 &= 0xff;
+ rp14 ^= (rp14 >> 16);
+ rp14 ^= (rp14 >> 8);
+ rp14 &= 0xff;
+ if (eccsize_mult == 2) {
+ rp16 ^= (rp16 >> 16);
+ rp16 ^= (rp16 >> 8);
+ rp16 &= 0xff;
+ }
+
+ /*
+ * we also need to calculate the row parity for rp0..rp3
+ * This is present in par, because par is now
+ * rp3 rp3 rp2 rp2 in little endian and
+ * rp2 rp2 rp3 rp3 in big endian
+ * as well as
+ * rp1 rp0 rp1 rp0 in little endian and
+ * rp0 rp1 rp0 rp1 in big endian
+ * First calculate rp2 and rp3
+ */
+#ifdef __BIG_ENDIAN
+ rp2 = (par >> 16);
+ rp2 ^= (rp2 >> 8);
+ rp2 &= 0xff;
+ rp3 = par & 0xffff;
+ rp3 ^= (rp3 >> 8);
+ rp3 &= 0xff;
#else
- ecc_code[0] = ~tmp1;
- ecc_code[1] = ~tmp2;
+ rp3 = (par >> 16);
+ rp3 ^= (rp3 >> 8);
+ rp3 &= 0xff;
+ rp2 = par & 0xffff;
+ rp2 ^= (rp2 >> 8);
+ rp2 &= 0xff;
#endif
- ecc_code[2] = ((~reg1) << 2) | 0x03;
- return 0;
+ /* reduce par to 16 bits then calculate rp1 and rp0 */
+ par ^= (par >> 16);
+#ifdef __BIG_ENDIAN
+ rp0 = (par >> 8) & 0xff;
+ rp1 = (par & 0xff);
+#else
+ rp1 = (par >> 8) & 0xff;
+ rp0 = (par & 0xff);
+#endif
+
+ /* finally reduce par to 8 bits */
+ par ^= (par >> 8);
+ par &= 0xff;
+
+ /*
+ * and calculate rp5..rp15..rp17
+ * note that par = rp4 ^ rp5 and due to the commutative property
+ * of the ^ operator we can say:
+ * rp5 = (par ^ rp4);
+ * The & 0xff seems superfluous, but benchmarking learned that
+ * leaving it out gives slightly worse results. No idea why, probably
+ * it has to do with the way the pipeline in pentium is organized.
+ */
+ rp5 = (par ^ rp4) & 0xff;
+ rp7 = (par ^ rp6) & 0xff;
+ rp9 = (par ^ rp8) & 0xff;
+ rp11 = (par ^ rp10) & 0xff;
+ rp13 = (par ^ rp12) & 0xff;
+ rp15 = (par ^ rp14) & 0xff;
+ if (eccsize_mult == 2)
+ rp17 = (par ^ rp16) & 0xff;
+
+ /*
+ * Finally calculate the ECC bits.
+ * Again here it might seem that there are performance optimisations
+ * possible, but benchmarks showed that on the system this is developed
+ * the code below is the fastest
+ */
+ if (sm_order) {
+ code[0] = (invparity[rp7] << 7) | (invparity[rp6] << 6) |
+ (invparity[rp5] << 5) | (invparity[rp4] << 4) |
+ (invparity[rp3] << 3) | (invparity[rp2] << 2) |
+ (invparity[rp1] << 1) | (invparity[rp0]);
+ code[1] = (invparity[rp15] << 7) | (invparity[rp14] << 6) |
+ (invparity[rp13] << 5) | (invparity[rp12] << 4) |
+ (invparity[rp11] << 3) | (invparity[rp10] << 2) |
+ (invparity[rp9] << 1) | (invparity[rp8]);
+ } else {
+ code[1] = (invparity[rp7] << 7) | (invparity[rp6] << 6) |
+ (invparity[rp5] << 5) | (invparity[rp4] << 4) |
+ (invparity[rp3] << 3) | (invparity[rp2] << 2) |
+ (invparity[rp1] << 1) | (invparity[rp0]);
+ code[0] = (invparity[rp15] << 7) | (invparity[rp14] << 6) |
+ (invparity[rp13] << 5) | (invparity[rp12] << 4) |
+ (invparity[rp11] << 3) | (invparity[rp10] << 2) |
+ (invparity[rp9] << 1) | (invparity[rp8]);
+ }
+
+ if (eccsize_mult == 1)
+ code[2] =
+ (invparity[par & 0xf0] << 7) |
+ (invparity[par & 0x0f] << 6) |
+ (invparity[par & 0xcc] << 5) |
+ (invparity[par & 0x33] << 4) |
+ (invparity[par & 0xaa] << 3) |
+ (invparity[par & 0x55] << 2) |
+ 3;
+ else
+ code[2] =
+ (invparity[par & 0xf0] << 7) |
+ (invparity[par & 0x0f] << 6) |
+ (invparity[par & 0xcc] << 5) |
+ (invparity[par & 0x33] << 4) |
+ (invparity[par & 0xaa] << 3) |
+ (invparity[par & 0x55] << 2) |
+ (invparity[rp17] << 1) |
+ (invparity[rp16] << 0);
}
-EXPORT_SYMBOL(nand_calculate_ecc);
+EXPORT_SYMBOL(__nand_calculate_ecc);
-static inline int countbits(uint32_t byte)
+/**
+ * nand_calculate_ecc - [NAND Interface] Calculate 3-byte ECC for 256/512-byte
+ * block
+ * @chip: NAND chip object
+ * @buf: input buffer with raw data
+ * @code: output buffer with ECC
+ */
+int nand_calculate_ecc(struct nand_chip *chip, const unsigned char *buf,
+ unsigned char *code)
{
- int res = 0;
+ bool sm_order = chip->ecc.options & NAND_ECC_SOFT_HAMMING_SM_ORDER;
+
+ __nand_calculate_ecc(buf, chip->ecc.size, code, sm_order);
- for (;byte; byte >>= 1)
- res += byte & 0x01;
- return res;
+ return 0;
}
+EXPORT_SYMBOL(nand_calculate_ecc);
/**
- * nand_correct_data - [NAND Interface] Detect and correct bit error(s)
- * @mtd: MTD block structure
- * @dat: raw data read from the chip
+ * __nand_correct_data - [NAND Interface] Detect and correct bit error(s)
+ * @buf: raw data read from the chip
* @read_ecc: ECC from the chip
* @calc_ecc: the ECC calculated from raw data
+ * @eccsize: data bytes per ECC step (256 or 512)
+ * @sm_order: Smart Media byte order
*
- * Detect and correct a 1 bit error for 256 byte block
+ * Detect and correct a 1 bit error for eccsize byte block
*/
-int nand_correct_data(struct nand_chip *chip, u_char *dat,
- u_char *read_ecc, u_char *calc_ecc)
+int __nand_correct_data(unsigned char *buf,
+ unsigned char *read_ecc, unsigned char *calc_ecc,
+ unsigned int eccsize, bool sm_order)
{
- uint8_t s0, s1, s2;
+ unsigned char b0, b1, b2, bit_addr;
+ unsigned int byte_addr;
+ /* 256 or 512 bytes/ecc */
+ const uint32_t eccsize_mult = eccsize >> 8;
-#ifdef CONFIG_MTD_NAND_ECC_SMC
- s0 = calc_ecc[0] ^ read_ecc[0];
- s1 = calc_ecc[1] ^ read_ecc[1];
- s2 = calc_ecc[2] ^ read_ecc[2];
-#else
- s1 = calc_ecc[0] ^ read_ecc[0];
- s0 = calc_ecc[1] ^ read_ecc[1];
- s2 = calc_ecc[2] ^ read_ecc[2];
-#endif
- if ((s0 | s1 | s2) == 0)
- return 0;
-
- /* Check for a single bit error */
- if( ((s0 ^ (s0 >> 1)) & 0x55) == 0x55 &&
- ((s1 ^ (s1 >> 1)) & 0x55) == 0x55 &&
- ((s2 ^ (s2 >> 1)) & 0x54) == 0x54) {
-
- uint32_t byteoffs, bitnum;
+ /*
+ * b0 to b2 indicate which bit is faulty (if any)
+ * we might need the xor result more than once,
+ * so keep them in a local var
+ */
+ if (sm_order) {
+ b0 = read_ecc[0] ^ calc_ecc[0];
+ b1 = read_ecc[1] ^ calc_ecc[1];
+ } else {
+ b0 = read_ecc[1] ^ calc_ecc[1];
+ b1 = read_ecc[0] ^ calc_ecc[0];
+ }
- byteoffs = (s1 << 0) & 0x80;
- byteoffs |= (s1 << 1) & 0x40;
- byteoffs |= (s1 << 2) & 0x20;
- byteoffs |= (s1 << 3) & 0x10;
+ b2 = read_ecc[2] ^ calc_ecc[2];
- byteoffs |= (s0 >> 4) & 0x08;
- byteoffs |= (s0 >> 3) & 0x04;
- byteoffs |= (s0 >> 2) & 0x02;
- byteoffs |= (s0 >> 1) & 0x01;
+ /* check if there are any bitfaults */
- bitnum = (s2 >> 5) & 0x04;
- bitnum |= (s2 >> 4) & 0x02;
- bitnum |= (s2 >> 3) & 0x01;
+ /* repeated if statements are slightly more efficient than switch ... */
+ /* ordered in order of likelihood */
- dat[byteoffs] ^= (1 << bitnum);
+ if ((b0 | b1 | b2) == 0)
+ return 0; /* no error */
+ if ((((b0 ^ (b0 >> 1)) & 0x55) == 0x55) &&
+ (((b1 ^ (b1 >> 1)) & 0x55) == 0x55) &&
+ ((eccsize_mult == 1 && ((b2 ^ (b2 >> 1)) & 0x54) == 0x54) ||
+ (eccsize_mult == 2 && ((b2 ^ (b2 >> 1)) & 0x55) == 0x55))) {
+ /* single bit error */
+ /*
+ * rp17/rp15/13/11/9/7/5/3/1 indicate which byte is the faulty
+ * byte, cp 5/3/1 indicate the faulty bit.
+ * A lookup table (called addressbits) is used to filter
+ * the bits from the byte they are in.
+ * A marginal optimisation is possible by having three
+ * different lookup tables.
+ * One as we have now (for b0), one for b2
+ * (that would avoid the >> 1), and one for b1 (with all values
+ * << 4). However it was felt that introducing two more tables
+ * hardly justify the gain.
+ *
+ * The b2 shift is there to get rid of the lowest two bits.
+ * We could also do addressbits[b2] >> 1 but for the
+ * performance it does not make any difference
+ */
+ if (eccsize_mult == 1)
+ byte_addr = (addressbits[b1] << 4) + addressbits[b0];
+ else
+ byte_addr = (addressbits[b2 & 0x3] << 8) +
+ (addressbits[b1] << 4) + addressbits[b0];
+ bit_addr = addressbits[b2 >> 2];
+ /* flip the bit */
+ buf[byte_addr] ^= (1 << bit_addr);
return 1;
- }
- if(countbits(s0 | ((uint32_t)s1 << 8) | ((uint32_t)s2 <<16)) == 1)
- return 1;
+ }
+ /* count nr of bits; use table lookup, faster than calculating it */
+ if ((bitsperbyte[b0] + bitsperbyte[b1] + bitsperbyte[b2]) == 1)
+ return 1; /* error in ECC data; no action needed */
+ pr_err("%s: uncorrectable ECC error\n", __func__);
return -EBADMSG;
}
+EXPORT_SYMBOL(__nand_correct_data);
+
+/**
+ * nand_correct_data - [NAND Interface] Detect and correct bit error(s)
+ * @chip: NAND chip object
+ * @buf: raw data read from the chip
+ * @read_ecc: ECC from the chip
+ * @calc_ecc: the ECC calculated from raw data
+ *
+ * Detect and correct a 1 bit error for 256/512 byte block
+ */
+int nand_correct_data(struct nand_chip *chip, unsigned char *buf,
+ unsigned char *read_ecc, unsigned char *calc_ecc)
+{
+ bool sm_order = chip->ecc.options & NAND_ECC_SOFT_HAMMING_SM_ORDER;
+
+ return __nand_correct_data(buf, read_ecc, calc_ecc, chip->ecc.size,
+ sm_order);
+}
EXPORT_SYMBOL(nand_correct_data);
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Steven J. Hill <sjhill@realitydiluted.com>");
+MODULE_AUTHOR("Frans Meulenbroeks <fransmeulenbroeks@gmail.com>");
MODULE_DESCRIPTION("Generic NAND ECC support");
diff --git a/drivers/mtd/nand/nand_esmt.c b/drivers/mtd/nand/nand_esmt.c
new file mode 100644
index 0000000000..3338c68aaa
--- /dev/null
+++ b/drivers/mtd/nand/nand_esmt.c
@@ -0,0 +1,54 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2018 Toradex AG
+ *
+ * Author: Marcel Ziswiler <marcel.ziswiler@toradex.com>
+ */
+
+#include <linux/mtd/rawnand.h>
+#include "internals.h"
+
+static void esmt_nand_decode_id(struct nand_chip *chip)
+{
+ nand_decode_ext_id(chip);
+
+ /* Extract ECC requirements from 5th id byte. */
+ if (chip->id.len >= 5 && nand_is_slc(chip)) {
+ chip->base.eccreq.step_size = 512;
+ switch (chip->id.data[4] & 0x3) {
+ case 0x0:
+ chip->base.eccreq.strength = 4;
+ break;
+ case 0x1:
+ chip->base.eccreq.strength = 2;
+ break;
+ case 0x2:
+ chip->base.eccreq.strength = 1;
+ break;
+ default:
+ WARN(1, "Could not get ECC info");
+ chip->base.eccreq.step_size = 0;
+ break;
+ }
+ }
+}
+
+static int esmt_nand_init(struct nand_chip *chip)
+{
+ if (nand_is_slc(chip))
+ /*
+ * It is known that some ESMT SLC NANDs have been shipped
+ * with the factory bad block markers in the first or last page
+ * of the block, instead of the first or second page. To be on
+ * the safe side, let's check all three locations.
+ */
+ chip->options |= NAND_BBM_FIRSTPAGE | NAND_BBM_SECONDPAGE |
+ NAND_BBM_LASTPAGE;
+
+ return 0;
+}
+
+const struct nand_manufacturer_ops esmt_nand_manuf_ops = {
+ .detect = esmt_nand_decode_id,
+ .init = esmt_nand_init,
+};
diff --git a/drivers/mtd/nand/nand_hynix.c b/drivers/mtd/nand/nand_hynix.c
new file mode 100644
index 0000000000..0422ed53aa
--- /dev/null
+++ b/drivers/mtd/nand/nand_hynix.c
@@ -0,0 +1,716 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2017 Free Electrons
+ * Copyright (C) 2017 NextThing Co
+ *
+ * Author: Boris Brezillon <boris.brezillon@free-electrons.com>
+ */
+
+#include <linux/sizes.h>
+
+#include "internals.h"
+
+#define NAND_HYNIX_CMD_SET_PARAMS 0x36
+#define NAND_HYNIX_CMD_APPLY_PARAMS 0x16
+
+#define NAND_HYNIX_1XNM_RR_REPEAT 8
+
+/**
+ * struct hynix_read_retry - read-retry data
+ * @nregs: number of register to set when applying a new read-retry mode
+ * @regs: register offsets (NAND chip dependent)
+ * @values: array of values to set in registers. The array size is equal to
+ * (nregs * nmodes)
+ */
+struct hynix_read_retry {
+ int nregs;
+ const u8 *regs;
+ u8 values[];
+};
+
+/**
+ * struct hynix_nand - private Hynix NAND struct
+ * @nand_technology: manufacturing process expressed in picometer
+ * @read_retry: read-retry information
+ */
+struct hynix_nand {
+ const struct hynix_read_retry *read_retry;
+};
+
+/**
+ * struct hynix_read_retry_otp - structure describing how the read-retry OTP
+ * area
+ * @nregs: number of hynix private registers to set before reading the reading
+ * the OTP area
+ * @regs: registers that should be configured
+ * @values: values that should be set in regs
+ * @page: the address to pass to the READ_PAGE command. Depends on the NAND
+ * chip
+ * @size: size of the read-retry OTP section
+ */
+struct hynix_read_retry_otp {
+ int nregs;
+ const u8 *regs;
+ const u8 *values;
+ int page;
+ int size;
+};
+
+static bool hynix_nand_has_valid_jedecid(struct nand_chip *chip)
+{
+ u8 jedecid[5] = { };
+ int ret;
+
+ ret = nand_readid_op(chip, 0x40, jedecid, sizeof(jedecid));
+ if (ret)
+ return false;
+
+ return !strncmp("JEDEC", jedecid, sizeof(jedecid));
+}
+
+static int hynix_nand_cmd_op(struct nand_chip *chip, u8 cmd)
+{
+ if (nand_has_exec_op(chip)) {
+ struct nand_op_instr instrs[] = {
+ NAND_OP_CMD(cmd, 0),
+ };
+ struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);
+
+ return nand_exec_op(chip, &op);
+ }
+
+ chip->legacy.cmdfunc(chip, cmd, -1, -1);
+
+ return 0;
+}
+
+static int hynix_nand_reg_write_op(struct nand_chip *chip, u8 addr, u8 val)
+{
+ u16 column = ((u16)addr << 8) | addr;
+
+ if (nand_has_exec_op(chip)) {
+ struct nand_op_instr instrs[] = {
+ NAND_OP_ADDR(1, &addr, 0),
+ NAND_OP_8BIT_DATA_OUT(1, &val, 0),
+ };
+ struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);
+
+ return nand_exec_op(chip, &op);
+ }
+
+ chip->legacy.cmdfunc(chip, NAND_CMD_NONE, column, -1);
+ chip->legacy.write_byte(chip, val);
+
+ return 0;
+}
+
+static int hynix_nand_setup_read_retry(struct nand_chip *chip, int retry_mode)
+{
+ struct hynix_nand *hynix = nand_get_manufacturer_data(chip);
+ const u8 *values;
+ int i, ret;
+
+ values = hynix->read_retry->values +
+ (retry_mode * hynix->read_retry->nregs);
+
+ /* Enter 'Set Hynix Parameters' mode */
+ ret = hynix_nand_cmd_op(chip, NAND_HYNIX_CMD_SET_PARAMS);
+ if (ret)
+ return ret;
+
+ /*
+ * Configure the NAND in the requested read-retry mode.
+ * This is done by setting pre-defined values in internal NAND
+ * registers.
+ *
+ * The set of registers is NAND specific, and the values are either
+ * predefined or extracted from an OTP area on the NAND (values are
+ * probably tweaked at production in this case).
+ */
+ for (i = 0; i < hynix->read_retry->nregs; i++) {
+ ret = hynix_nand_reg_write_op(chip, hynix->read_retry->regs[i],
+ values[i]);
+ if (ret)
+ return ret;
+ }
+
+ /* Apply the new settings. */
+ return hynix_nand_cmd_op(chip, NAND_HYNIX_CMD_APPLY_PARAMS);
+}
+
+/**
+ * hynix_get_majority - get the value that is occurring the most in a given
+ * set of values
+ * @in: the array of values to test
+ * @repeat: the size of the in array
+ * @out: pointer used to store the output value
+ *
+ * This function implements the 'majority check' logic that is supposed to
+ * overcome the unreliability of MLC NANDs when reading the OTP area storing
+ * the read-retry parameters.
+ *
+ * It's based on a pretty simple assumption: if we repeat the same value
+ * several times and then take the one that is occurring the most, we should
+ * find the correct value.
+ * Let's hope this dummy algorithm prevents us from losing the read-retry
+ * parameters.
+ */
+static int hynix_get_majority(const u8 *in, int repeat, u8 *out)
+{
+ int i, j, half = repeat / 2;
+
+ /*
+ * We only test the first half of the in array because we must ensure
+ * that the value is at least occurring repeat / 2 times.
+ *
+ * This loop is suboptimal since we may count the occurrences of the
+ * same value several time, but we are doing that on small sets, which
+ * makes it acceptable.
+ */
+ for (i = 0; i < half; i++) {
+ int cnt = 0;
+ u8 val = in[i];
+
+ /* Count all values that are matching the one at index i. */
+ for (j = i + 1; j < repeat; j++) {
+ if (in[j] == val)
+ cnt++;
+ }
+
+ /* We found a value occurring more than repeat / 2. */
+ if (cnt > half) {
+ *out = val;
+ return 0;
+ }
+ }
+
+ return -EIO;
+}
+
+static int hynix_read_rr_otp(struct nand_chip *chip,
+ const struct hynix_read_retry_otp *info,
+ void *buf)
+{
+ int i, ret;
+
+ ret = nand_reset_op(chip);
+ if (ret)
+ return ret;
+
+ ret = hynix_nand_cmd_op(chip, NAND_HYNIX_CMD_SET_PARAMS);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < info->nregs; i++) {
+ ret = hynix_nand_reg_write_op(chip, info->regs[i],
+ info->values[i]);
+ if (ret)
+ return ret;
+ }
+
+ ret = hynix_nand_cmd_op(chip, NAND_HYNIX_CMD_APPLY_PARAMS);
+ if (ret)
+ return ret;
+
+ /* Sequence to enter OTP mode? */
+ ret = hynix_nand_cmd_op(chip, 0x17);
+ if (ret)
+ return ret;
+
+ ret = hynix_nand_cmd_op(chip, 0x4);
+ if (ret)
+ return ret;
+
+ ret = hynix_nand_cmd_op(chip, 0x19);
+ if (ret)
+ return ret;
+
+ /* Now read the page */
+ ret = nand_read_page_op(chip, info->page, 0, buf, info->size);
+ if (ret)
+ return ret;
+
+ /* Put everything back to normal */
+ ret = nand_reset_op(chip);
+ if (ret)
+ return ret;
+
+ ret = hynix_nand_cmd_op(chip, NAND_HYNIX_CMD_SET_PARAMS);
+ if (ret)
+ return ret;
+
+ ret = hynix_nand_reg_write_op(chip, 0x38, 0);
+ if (ret)
+ return ret;
+
+ ret = hynix_nand_cmd_op(chip, NAND_HYNIX_CMD_APPLY_PARAMS);
+ if (ret)
+ return ret;
+
+ return nand_read_page_op(chip, 0, 0, NULL, 0);
+}
+
+#define NAND_HYNIX_1XNM_RR_COUNT_OFFS 0
+#define NAND_HYNIX_1XNM_RR_REG_COUNT_OFFS 8
+#define NAND_HYNIX_1XNM_RR_SET_OFFS(x, setsize, inv) \
+ (16 + ((((x) * 2) + ((inv) ? 1 : 0)) * (setsize)))
+
+static int hynix_mlc_1xnm_rr_value(const u8 *buf, int nmodes, int nregs,
+ int mode, int reg, bool inv, u8 *val)
+{
+ u8 tmp[NAND_HYNIX_1XNM_RR_REPEAT];
+ int val_offs = (mode * nregs) + reg;
+ int set_size = nmodes * nregs;
+ int i, ret;
+
+ for (i = 0; i < NAND_HYNIX_1XNM_RR_REPEAT; i++) {
+ int set_offs = NAND_HYNIX_1XNM_RR_SET_OFFS(i, set_size, inv);
+
+ tmp[i] = buf[val_offs + set_offs];
+ }
+
+ ret = hynix_get_majority(tmp, NAND_HYNIX_1XNM_RR_REPEAT, val);
+ if (ret)
+ return ret;
+
+ if (inv)
+ *val = ~*val;
+
+ return 0;
+}
+
+static u8 hynix_1xnm_mlc_read_retry_regs[] = {
+ 0xcc, 0xbf, 0xaa, 0xab, 0xcd, 0xad, 0xae, 0xaf
+};
+
+static int hynix_mlc_1xnm_rr_init(struct nand_chip *chip,
+ const struct hynix_read_retry_otp *info)
+{
+ struct hynix_nand *hynix = nand_get_manufacturer_data(chip);
+ struct hynix_read_retry *rr = NULL;
+ int ret, i, j;
+ u8 nregs, nmodes;
+ u8 *buf;
+
+ buf = kmalloc(info->size, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ ret = hynix_read_rr_otp(chip, info, buf);
+ if (ret)
+ goto out;
+
+ ret = hynix_get_majority(buf, NAND_HYNIX_1XNM_RR_REPEAT,
+ &nmodes);
+ if (ret)
+ goto out;
+
+ ret = hynix_get_majority(buf + NAND_HYNIX_1XNM_RR_REPEAT,
+ NAND_HYNIX_1XNM_RR_REPEAT,
+ &nregs);
+ if (ret)
+ goto out;
+
+ rr = kzalloc(sizeof(*rr) + (nregs * nmodes), GFP_KERNEL);
+ if (!rr) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ for (i = 0; i < nmodes; i++) {
+ for (j = 0; j < nregs; j++) {
+ u8 *val = rr->values + (i * nregs);
+
+ ret = hynix_mlc_1xnm_rr_value(buf, nmodes, nregs, i, j,
+ false, val);
+ if (!ret)
+ continue;
+
+ ret = hynix_mlc_1xnm_rr_value(buf, nmodes, nregs, i, j,
+ true, val);
+ if (ret)
+ goto out;
+ }
+ }
+
+ rr->nregs = nregs;
+ rr->regs = hynix_1xnm_mlc_read_retry_regs;
+ hynix->read_retry = rr;
+ chip->ops.setup_read_retry = hynix_nand_setup_read_retry;
+ chip->read_retries = nmodes;
+
+out:
+ kfree(buf);
+
+ if (ret)
+ kfree(rr);
+
+ return ret;
+}
+
+static const u8 hynix_mlc_1xnm_rr_otp_regs[] = { 0x38 };
+static const u8 hynix_mlc_1xnm_rr_otp_values[] = { 0x52 };
+
+static const struct hynix_read_retry_otp hynix_mlc_1xnm_rr_otps[] = {
+ {
+ .nregs = ARRAY_SIZE(hynix_mlc_1xnm_rr_otp_regs),
+ .regs = hynix_mlc_1xnm_rr_otp_regs,
+ .values = hynix_mlc_1xnm_rr_otp_values,
+ .page = 0x21f,
+ .size = 784
+ },
+ {
+ .nregs = ARRAY_SIZE(hynix_mlc_1xnm_rr_otp_regs),
+ .regs = hynix_mlc_1xnm_rr_otp_regs,
+ .values = hynix_mlc_1xnm_rr_otp_values,
+ .page = 0x200,
+ .size = 528,
+ },
+};
+
+static int hynix_nand_rr_init(struct nand_chip *chip)
+{
+ int i, ret = 0;
+ bool valid_jedecid;
+
+ valid_jedecid = hynix_nand_has_valid_jedecid(chip);
+
+ /*
+ * We only support read-retry for 1xnm NANDs, and those NANDs all
+ * expose a valid JEDEC ID.
+ */
+ if (valid_jedecid) {
+ u8 nand_tech = chip->id.data[5] >> 4;
+
+ /* 1xnm technology */
+ if (nand_tech == 4) {
+ for (i = 0; i < ARRAY_SIZE(hynix_mlc_1xnm_rr_otps);
+ i++) {
+ /*
+ * FIXME: Hynix recommend to copy the
+ * read-retry OTP area into a normal page.
+ */
+ ret = hynix_mlc_1xnm_rr_init(chip,
+ hynix_mlc_1xnm_rr_otps);
+ if (!ret)
+ break;
+ }
+ }
+ }
+
+ if (ret)
+ pr_warn("failed to initialize read-retry infrastructure");
+
+ return 0;
+}
+
+static void hynix_nand_extract_oobsize(struct nand_chip *chip,
+ bool valid_jedecid)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ struct nand_memory_organization *memorg;
+ u8 oobsize;
+
+ memorg = nanddev_get_memorg(&chip->base);
+
+ oobsize = ((chip->id.data[3] >> 2) & 0x3) |
+ ((chip->id.data[3] >> 4) & 0x4);
+
+ if (valid_jedecid) {
+ switch (oobsize) {
+ case 0:
+ memorg->oobsize = 2048;
+ break;
+ case 1:
+ memorg->oobsize = 1664;
+ break;
+ case 2:
+ memorg->oobsize = 1024;
+ break;
+ case 3:
+ memorg->oobsize = 640;
+ break;
+ default:
+ /*
+ * We should never reach this case, but if that
+ * happens, this probably means Hynix decided to use
+ * a different extended ID format, and we should find
+ * a way to support it.
+ */
+ WARN(1, "Invalid OOB size");
+ break;
+ }
+ } else {
+ switch (oobsize) {
+ case 0:
+ memorg->oobsize = 128;
+ break;
+ case 1:
+ memorg->oobsize = 224;
+ break;
+ case 2:
+ memorg->oobsize = 448;
+ break;
+ case 3:
+ memorg->oobsize = 64;
+ break;
+ case 4:
+ memorg->oobsize = 32;
+ break;
+ case 5:
+ memorg->oobsize = 16;
+ break;
+ case 6:
+ memorg->oobsize = 640;
+ break;
+ default:
+ /*
+ * We should never reach this case, but if that
+ * happens, this probably means Hynix decided to use
+ * a different extended ID format, and we should find
+ * a way to support it.
+ */
+ WARN(1, "Invalid OOB size");
+ break;
+ }
+
+ /*
+ * The datasheet of H27UCG8T2BTR mentions that the "Redundant
+ * Area Size" is encoded "per 8KB" (page size). This chip uses
+ * a page size of 16KiB. The datasheet mentions an OOB size of
+ * 1.280 bytes, but the OOB size encoded in the ID bytes (using
+ * the existing logic above) is 640 bytes.
+ * Update the OOB size for this chip by taking the value
+ * determined above and scaling it to the actual page size (so
+ * the actual OOB size for this chip is: 640 * 16k / 8k).
+ */
+ if (chip->id.data[1] == 0xde)
+ memorg->oobsize *= memorg->pagesize / SZ_8K;
+ }
+
+ mtd->oobsize = memorg->oobsize;
+}
+
+static void hynix_nand_extract_ecc_requirements(struct nand_chip *chip,
+ bool valid_jedecid)
+{
+ u8 ecc_level = (chip->id.data[4] >> 4) & 0x7;
+
+ if (valid_jedecid) {
+ /* Reference: H27UCG8T2E datasheet */
+ chip->base.eccreq.step_size = 1024;
+
+ switch (ecc_level) {
+ case 0:
+ chip->base.eccreq.step_size = 0;
+ chip->base.eccreq.strength = 0;
+ break;
+ case 1:
+ chip->base.eccreq.strength = 4;
+ break;
+ case 2:
+ chip->base.eccreq.strength = 24;
+ break;
+ case 3:
+ chip->base.eccreq.strength = 32;
+ break;
+ case 4:
+ chip->base.eccreq.strength = 40;
+ break;
+ case 5:
+ chip->base.eccreq.strength = 50;
+ break;
+ case 6:
+ chip->base.eccreq.strength = 60;
+ break;
+ default:
+ /*
+ * We should never reach this case, but if that
+ * happens, this probably means Hynix decided to use
+ * a different extended ID format, and we should find
+ * a way to support it.
+ */
+ WARN(1, "Invalid ECC requirements");
+ }
+ } else {
+ /*
+ * The ECC requirements field meaning depends on the
+ * NAND technology.
+ */
+ u8 nand_tech = chip->id.data[5] & 0x7;
+
+ if (nand_tech < 3) {
+ /* > 26nm, reference: H27UBG8T2A datasheet */
+ if (ecc_level < 5) {
+ chip->base.eccreq.step_size = 512;
+ chip->base.eccreq.strength = 1 << ecc_level;
+ } else if (ecc_level < 7) {
+ if (ecc_level == 5)
+ chip->base.eccreq.step_size = 2048;
+ else
+ chip->base.eccreq.step_size = 1024;
+ chip->base.eccreq.strength = 24;
+ } else {
+ /*
+ * We should never reach this case, but if that
+ * happens, this probably means Hynix decided
+ * to use a different extended ID format, and
+ * we should find a way to support it.
+ */
+ WARN(1, "Invalid ECC requirements");
+ }
+ } else {
+ /* <= 26nm, reference: H27UBG8T2B datasheet */
+ if (!ecc_level) {
+ chip->base.eccreq.step_size = 0;
+ chip->base.eccreq.strength = 0;
+ } else if (ecc_level < 5) {
+ chip->base.eccreq.step_size = 512;
+ chip->base.eccreq.strength = 1 << (ecc_level - 1);
+ } else {
+ chip->base.eccreq.step_size = 1024;
+ chip->base.eccreq.strength = 24 +
+ (8 * (ecc_level - 5));
+ }
+ }
+ }
+}
+
+static void hynix_nand_extract_scrambling_requirements(struct nand_chip *chip,
+ bool valid_jedecid)
+{
+ u8 nand_tech;
+
+ /* We need scrambling on all TLC NANDs*/
+ if (nanddev_bits_per_cell(&chip->base) > 2)
+ chip->options |= NAND_NEED_SCRAMBLING;
+
+ /* And on MLC NANDs with sub-3xnm process */
+ if (valid_jedecid) {
+ nand_tech = chip->id.data[5] >> 4;
+
+ /* < 3xnm */
+ if (nand_tech > 0)
+ chip->options |= NAND_NEED_SCRAMBLING;
+ } else {
+ nand_tech = chip->id.data[5] & 0x7;
+
+ /* < 32nm */
+ if (nand_tech > 2)
+ chip->options |= NAND_NEED_SCRAMBLING;
+ }
+}
+
+static void hynix_nand_decode_id(struct nand_chip *chip)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ struct nand_memory_organization *memorg;
+ bool valid_jedecid;
+ u8 tmp;
+
+ memorg = nanddev_get_memorg(&chip->base);
+
+ /*
+ * Exclude all SLC NANDs from this advanced detection scheme.
+ * According to the ranges defined in several datasheets, it might
+ * appear that even SLC NANDs could fall in this extended ID scheme.
+ * If that the case rework the test to let SLC NANDs go through the
+ * detection process.
+ */
+ if (chip->id.len < 6 || nand_is_slc(chip)) {
+ nand_decode_ext_id(chip);
+ return;
+ }
+
+ /* Extract pagesize */
+ memorg->pagesize = 2048 << (chip->id.data[3] & 0x03);
+ mtd->writesize = memorg->pagesize;
+
+ tmp = (chip->id.data[3] >> 4) & 0x3;
+ /*
+ * When bit7 is set that means we start counting at 1MiB, otherwise
+ * we start counting at 128KiB and shift this value the content of
+ * ID[3][4:5].
+ * The only exception is when ID[3][4:5] == 3 and ID[3][7] == 0, in
+ * this case the erasesize is set to 768KiB.
+ */
+ if (chip->id.data[3] & 0x80) {
+ memorg->pages_per_eraseblock = (SZ_1M << tmp) /
+ memorg->pagesize;
+ mtd->erasesize = SZ_1M << tmp;
+ } else if (tmp == 3) {
+ memorg->pages_per_eraseblock = (SZ_512K + SZ_256K) /
+ memorg->pagesize;
+ mtd->erasesize = SZ_512K + SZ_256K;
+ } else {
+ memorg->pages_per_eraseblock = (SZ_128K << tmp) /
+ memorg->pagesize;
+ mtd->erasesize = SZ_128K << tmp;
+ }
+
+ /*
+ * Modern Toggle DDR NANDs have a valid JEDECID even though they are
+ * not exposing a valid JEDEC parameter table.
+ * These NANDs use a different NAND ID scheme.
+ */
+ valid_jedecid = hynix_nand_has_valid_jedecid(chip);
+
+ hynix_nand_extract_oobsize(chip, valid_jedecid);
+ hynix_nand_extract_ecc_requirements(chip, valid_jedecid);
+ hynix_nand_extract_scrambling_requirements(chip, valid_jedecid);
+}
+
+static void hynix_nand_cleanup(struct nand_chip *chip)
+{
+ struct hynix_nand *hynix = nand_get_manufacturer_data(chip);
+
+ if (!hynix)
+ return;
+
+ kfree(hynix->read_retry);
+ kfree(hynix);
+ nand_set_manufacturer_data(chip, NULL);
+}
+
+static int
+h27ucg8t2atrbc_choose_interface_config(struct nand_chip *chip,
+ struct nand_interface_config *iface)
+{
+ onfi_fill_interface_config(chip, iface, NAND_SDR_IFACE, 4);
+
+ return nand_choose_best_sdr_timings(chip, iface, NULL);
+}
+
+static int hynix_nand_init(struct nand_chip *chip)
+{
+ struct hynix_nand *hynix;
+ int ret;
+
+ if (!nand_is_slc(chip))
+ chip->options |= NAND_BBM_LASTPAGE;
+ else
+ chip->options |= NAND_BBM_FIRSTPAGE | NAND_BBM_SECONDPAGE;
+
+ hynix = kzalloc(sizeof(*hynix), GFP_KERNEL);
+ if (!hynix)
+ return -ENOMEM;
+
+ nand_set_manufacturer_data(chip, hynix);
+
+ if (!strncmp("H27UCG8T2ATR-BC", chip->parameters.model,
+ sizeof("H27UCG8T2ATR-BC") - 1))
+ chip->ops.choose_interface_config =
+ h27ucg8t2atrbc_choose_interface_config;
+
+ ret = hynix_nand_rr_init(chip);
+ if (ret)
+ hynix_nand_cleanup(chip);
+
+ return ret;
+}
+
+const struct nand_manufacturer_ops hynix_nand_manuf_ops = {
+ .detect = hynix_nand_decode_id,
+ .init = hynix_nand_init,
+ .cleanup = hynix_nand_cleanup,
+};
diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/nand_ids.c
index 863e8d49ab..b9945791a9 100644
--- a/drivers/mtd/nand/nand_ids.c
+++ b/drivers/mtd/nand/nand_ids.c
@@ -1,24 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
- * drivers/mtd/nandids.c
- *
* Copyright (C) 2002 Thomas Gleixner (tglx@linutronix.de)
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
*/
-#include <common.h>
+
#include <linux/sizes.h>
-#include <linux/mtd/nand.h>
-#ifdef CONFIG_NAND_INFO
-#define __STR(str) str
-#else
-#define __STR(str) ""
-#endif
+#include "internals.h"
-#define LP_OPTIONS NAND_SAMSUNG_LP_OPTIONS
+#define LP_OPTIONS 0
#define LP_OPTIONS16 (LP_OPTIONS | NAND_BUSWIDTH_16)
#define SP_OPTIONS NAND_NEED_READRDY
@@ -37,49 +26,65 @@ struct nand_flash_dev nand_flash_ids[] = {
* listed by full ID. We list them first so that we can easily identify
* the most specific match.
*/
- {__STR("TC58NVG2S0F 4G 3.3V 8-bit"),
+ {"TC58NVG0S3E 1G 3.3V 8-bit",
+ { .id = {0x98, 0xd1, 0x90, 0x15, 0x76, 0x14, 0x01, 0x00} },
+ SZ_2K, SZ_128, SZ_128K, 0, 8, 64, NAND_ECC_INFO(1, SZ_512), },
+ {"TC58NVG2S0F 4G 3.3V 8-bit",
{ .id = {0x98, 0xdc, 0x90, 0x26, 0x76, 0x15, 0x01, 0x08} },
- SZ_4K, SZ_512, SZ_256K, 0, 8, 224},
- {__STR("TC58NVG3S0F 8G 3.3V 8-bit"),
+ SZ_4K, SZ_512, SZ_256K, 0, 8, 224, NAND_ECC_INFO(4, SZ_512) },
+ {"TC58NVG2S0H 4G 3.3V 8-bit",
+ { .id = {0x98, 0xdc, 0x90, 0x26, 0x76, 0x16, 0x08, 0x00} },
+ SZ_4K, SZ_512, SZ_256K, 0, 8, 256, NAND_ECC_INFO(8, SZ_512) },
+ {"TC58NVG3S0F 8G 3.3V 8-bit",
{ .id = {0x98, 0xd3, 0x90, 0x26, 0x76, 0x15, 0x02, 0x08} },
- SZ_4K, SZ_1K, SZ_256K, 0, 8, 232},
- {__STR("TC58NVG5D2 32G 3.3V 8-bit"),
+ SZ_4K, SZ_1K, SZ_256K, 0, 8, 232, NAND_ECC_INFO(4, SZ_512) },
+ {"TC58NVG5D2 32G 3.3V 8-bit",
{ .id = {0x98, 0xd7, 0x94, 0x32, 0x76, 0x56, 0x09, 0x00} },
- SZ_8K, SZ_4K, SZ_1M, 0, 8, 640},
- {__STR("TC58NVG6D2 64G 3.3V 8-bit"),
+ SZ_8K, SZ_4K, SZ_1M, 0, 8, 640, NAND_ECC_INFO(40, SZ_1K) },
+ {"TC58NVG6D2 64G 3.3V 8-bit",
{ .id = {0x98, 0xde, 0x94, 0x82, 0x76, 0x56, 0x04, 0x20} },
- SZ_8K, SZ_8K, SZ_2M, 0, 8, 640},
-
- LEGACY_ID_NAND(__STR("NAND 4MiB 5V 8-bit"), 0x6B, 4, SZ_8K, SP_OPTIONS),
- LEGACY_ID_NAND(__STR("NAND 4MiB 3,3V 8-bit"), 0xE3, 4, SZ_8K, SP_OPTIONS),
- LEGACY_ID_NAND(__STR("NAND 4MiB 3,3V 8-bit"), 0xE5, 4, SZ_8K, SP_OPTIONS),
- LEGACY_ID_NAND(__STR("NAND 8MiB 3,3V 8-bit"), 0xD6, 8, SZ_8K, SP_OPTIONS),
- LEGACY_ID_NAND(__STR("NAND 8MiB 3,3V 8-bit"), 0xE6, 8, SZ_8K, SP_OPTIONS),
-
- LEGACY_ID_NAND(__STR("NAND 16MiB 1,8V 8-bit"), 0x33, 16, SZ_16K, SP_OPTIONS),
- LEGACY_ID_NAND(__STR("NAND 16MiB 3,3V 8-bit"), 0x73, 16, SZ_16K, SP_OPTIONS),
- LEGACY_ID_NAND(__STR("NAND 16MiB 1,8V 16-bit"), 0x43, 16, SZ_16K, SP_OPTIONS16),
- LEGACY_ID_NAND(__STR("NAND 16MiB 3,3V 16-bit"), 0x53, 16, SZ_16K, SP_OPTIONS16),
-
- LEGACY_ID_NAND(__STR("NAND 32MiB 1,8V 8-bit"), 0x35, 32, SZ_16K, SP_OPTIONS),
- LEGACY_ID_NAND(__STR("NAND 32MiB 3,3V 8-bit"), 0x75, 32, SZ_16K, SP_OPTIONS),
- LEGACY_ID_NAND(__STR("NAND 32MiB 1,8V 16-bit"), 0x45, 32, SZ_16K, SP_OPTIONS16),
- LEGACY_ID_NAND(__STR("NAND 32MiB 3,3V 16-bit"), 0x55, 32, SZ_16K, SP_OPTIONS16),
-
- LEGACY_ID_NAND(__STR("NAND 64MiB 1,8V 8-bit"), 0x36, 64, SZ_16K, SP_OPTIONS),
- LEGACY_ID_NAND(__STR("NAND 64MiB 3,3V 8-bit"), 0x76, 64, SZ_16K, SP_OPTIONS),
- LEGACY_ID_NAND(__STR("NAND 64MiB 1,8V 16-bit"), 0x46, 64, SZ_16K, SP_OPTIONS16),
- LEGACY_ID_NAND(__STR("NAND 64MiB 3,3V 16-bit"), 0x56, 64, SZ_16K, SP_OPTIONS16),
-
- LEGACY_ID_NAND(__STR("NAND 128MiB 1,8V 8-bit"), 0x78, 128, SZ_16K, SP_OPTIONS),
- LEGACY_ID_NAND(__STR("NAND 128MiB 1,8V 8-bit"), 0x39, 128, SZ_16K, SP_OPTIONS),
- LEGACY_ID_NAND(__STR("NAND 128MiB 3,3V 8-bit"), 0x79, 128, SZ_16K, SP_OPTIONS),
- LEGACY_ID_NAND(__STR("NAND 128MiB 1,8V 16-bit"), 0x72, 128, SZ_16K, SP_OPTIONS16),
- LEGACY_ID_NAND(__STR("NAND 128MiB 1,8V 16-bit"), 0x49, 128, SZ_16K, SP_OPTIONS16),
- LEGACY_ID_NAND(__STR("NAND 128MiB 3,3V 16-bit"), 0x74, 128, SZ_16K, SP_OPTIONS16),
- LEGACY_ID_NAND(__STR("NAND 128MiB 3,3V 16-bit"), 0x59, 128, SZ_16K, SP_OPTIONS16),
-
- LEGACY_ID_NAND(__STR("NAND 256MiB 3,3V 8-bit"), 0x71, 256, SZ_16K, SP_OPTIONS),
+ SZ_8K, SZ_8K, SZ_2M, 0, 8, 640, NAND_ECC_INFO(40, SZ_1K) },
+ {"SDTNRGAMA 64G 3.3V 8-bit",
+ { .id = {0x45, 0xde, 0x94, 0x93, 0x76, 0x50} },
+ SZ_16K, SZ_8K, SZ_4M, 0, 6, 1280, NAND_ECC_INFO(40, SZ_1K) },
+ {"H27UCG8T2ATR-BC 64G 3.3V 8-bit",
+ { .id = {0xad, 0xde, 0x94, 0xda, 0x74, 0xc4} },
+ SZ_8K, SZ_8K, SZ_2M, NAND_NEED_SCRAMBLING, 6, 640,
+ NAND_ECC_INFO(40, SZ_1K) },
+ {"TH58NVG2S3HBAI4 4G 3.3V 8-bit",
+ { .id = {0x98, 0xdc, 0x91, 0x15, 0x76} },
+ SZ_2K, SZ_512, SZ_128K, 0, 5, 128, NAND_ECC_INFO(8, SZ_512) },
+
+ LEGACY_ID_NAND("NAND 4MiB 5V 8-bit", 0x6B, 4, SZ_8K, SP_OPTIONS),
+ LEGACY_ID_NAND("NAND 4MiB 3,3V 8-bit", 0xE3, 4, SZ_8K, SP_OPTIONS),
+ LEGACY_ID_NAND("NAND 4MiB 3,3V 8-bit", 0xE5, 4, SZ_8K, SP_OPTIONS),
+ LEGACY_ID_NAND("NAND 8MiB 3,3V 8-bit", 0xD6, 8, SZ_8K, SP_OPTIONS),
+ LEGACY_ID_NAND("NAND 8MiB 3,3V 8-bit", 0xE6, 8, SZ_8K, SP_OPTIONS),
+
+ LEGACY_ID_NAND("NAND 16MiB 1,8V 8-bit", 0x33, 16, SZ_16K, SP_OPTIONS),
+ LEGACY_ID_NAND("NAND 16MiB 3,3V 8-bit", 0x73, 16, SZ_16K, SP_OPTIONS),
+ LEGACY_ID_NAND("NAND 16MiB 1,8V 16-bit", 0x43, 16, SZ_16K, SP_OPTIONS16),
+ LEGACY_ID_NAND("NAND 16MiB 3,3V 16-bit", 0x53, 16, SZ_16K, SP_OPTIONS16),
+
+ LEGACY_ID_NAND("NAND 32MiB 1,8V 8-bit", 0x35, 32, SZ_16K, SP_OPTIONS),
+ LEGACY_ID_NAND("NAND 32MiB 3,3V 8-bit", 0x75, 32, SZ_16K, SP_OPTIONS),
+ LEGACY_ID_NAND("NAND 32MiB 1,8V 16-bit", 0x45, 32, SZ_16K, SP_OPTIONS16),
+ LEGACY_ID_NAND("NAND 32MiB 3,3V 16-bit", 0x55, 32, SZ_16K, SP_OPTIONS16),
+
+ LEGACY_ID_NAND("NAND 64MiB 1,8V 8-bit", 0x36, 64, SZ_16K, SP_OPTIONS),
+ LEGACY_ID_NAND("NAND 64MiB 3,3V 8-bit", 0x76, 64, SZ_16K, SP_OPTIONS),
+ LEGACY_ID_NAND("NAND 64MiB 1,8V 16-bit", 0x46, 64, SZ_16K, SP_OPTIONS16),
+ LEGACY_ID_NAND("NAND 64MiB 3,3V 16-bit", 0x56, 64, SZ_16K, SP_OPTIONS16),
+
+ LEGACY_ID_NAND("NAND 128MiB 1,8V 8-bit", 0x78, 128, SZ_16K, SP_OPTIONS),
+ LEGACY_ID_NAND("NAND 128MiB 1,8V 8-bit", 0x39, 128, SZ_16K, SP_OPTIONS),
+ LEGACY_ID_NAND("NAND 128MiB 3,3V 8-bit", 0x79, 128, SZ_16K, SP_OPTIONS),
+ LEGACY_ID_NAND("NAND 128MiB 1,8V 16-bit", 0x72, 128, SZ_16K, SP_OPTIONS16),
+ LEGACY_ID_NAND("NAND 128MiB 1,8V 16-bit", 0x49, 128, SZ_16K, SP_OPTIONS16),
+ LEGACY_ID_NAND("NAND 128MiB 3,3V 16-bit", 0x74, 128, SZ_16K, SP_OPTIONS16),
+ LEGACY_ID_NAND("NAND 128MiB 3,3V 16-bit", 0x59, 128, SZ_16K, SP_OPTIONS16),
+
+ LEGACY_ID_NAND("NAND 256MiB 3,3V 8-bit", 0x71, 256, SZ_16K, SP_OPTIONS),
/*
* These are the new chips with large page size. Their page size and
@@ -87,101 +92,116 @@ struct nand_flash_dev nand_flash_ids[] = {
*/
/* 512 Megabit */
- EXTENDED_ID_NAND(__STR("NAND 64MiB 1,8V 8-bit"), 0xA2, 64, LP_OPTIONS),
- EXTENDED_ID_NAND(__STR("NAND 64MiB 1,8V 8-bit"), 0xA0, 64, LP_OPTIONS),
- EXTENDED_ID_NAND(__STR("NAND 64MiB 3,3V 8-bit"), 0xF2, 64, LP_OPTIONS),
- EXTENDED_ID_NAND(__STR("NAND 64MiB 3,3V 8-bit"), 0xD0, 64, LP_OPTIONS),
- EXTENDED_ID_NAND(__STR("NAND 64MiB 3,3V 8-bit"), 0xF0, 64, LP_OPTIONS),
- EXTENDED_ID_NAND(__STR("NAND 64MiB 1,8V 16-bit"), 0xB2, 64, LP_OPTIONS16),
- EXTENDED_ID_NAND(__STR("NAND 64MiB 1,8V 16-bit"), 0xB0, 64, LP_OPTIONS16),
- EXTENDED_ID_NAND(__STR("NAND 64MiB 3,3V 16-bit"), 0xC2, 64, LP_OPTIONS16),
- EXTENDED_ID_NAND(__STR("NAND 64MiB 3,3V 16-bit"), 0xC0, 64, LP_OPTIONS16),
+ EXTENDED_ID_NAND("NAND 64MiB 1,8V 8-bit", 0xA2, 64, LP_OPTIONS),
+ EXTENDED_ID_NAND("NAND 64MiB 1,8V 8-bit", 0xA0, 64, LP_OPTIONS),
+ EXTENDED_ID_NAND("NAND 64MiB 3,3V 8-bit", 0xF2, 64, LP_OPTIONS),
+ EXTENDED_ID_NAND("NAND 64MiB 3,3V 8-bit", 0xD0, 64, LP_OPTIONS),
+ EXTENDED_ID_NAND("NAND 64MiB 3,3V 8-bit", 0xF0, 64, LP_OPTIONS),
+ EXTENDED_ID_NAND("NAND 64MiB 1,8V 16-bit", 0xB2, 64, LP_OPTIONS16),
+ EXTENDED_ID_NAND("NAND 64MiB 1,8V 16-bit", 0xB0, 64, LP_OPTIONS16),
+ EXTENDED_ID_NAND("NAND 64MiB 3,3V 16-bit", 0xC2, 64, LP_OPTIONS16),
+ EXTENDED_ID_NAND("NAND 64MiB 3,3V 16-bit", 0xC0, 64, LP_OPTIONS16),
/* 1 Gigabit */
- EXTENDED_ID_NAND(__STR("NAND 128MiB 1,8V 8-bit"), 0xA1, 128, LP_OPTIONS),
- EXTENDED_ID_NAND(__STR("NAND 128MiB 3,3V 8-bit"), 0xF1, 128, LP_OPTIONS),
- EXTENDED_ID_NAND(__STR("NAND 128MiB 3,3V 8-bit"), 0xD1, 128, LP_OPTIONS),
- EXTENDED_ID_NAND(__STR("NAND 128MiB 1,8V 16-bit"), 0xB1, 128, LP_OPTIONS16),
- EXTENDED_ID_NAND(__STR("NAND 128MiB 3,3V 16-bit"), 0xC1, 128, LP_OPTIONS16),
- EXTENDED_ID_NAND(__STR("NAND 128MiB 1,8V 16-bit"), 0xAD, 128, LP_OPTIONS16),
+ EXTENDED_ID_NAND("NAND 128MiB 1,8V 8-bit", 0xA1, 128, LP_OPTIONS),
+ EXTENDED_ID_NAND("NAND 128MiB 3,3V 8-bit", 0xF1, 128, LP_OPTIONS),
+ EXTENDED_ID_NAND("NAND 128MiB 3,3V 8-bit", 0xD1, 128, LP_OPTIONS),
+ EXTENDED_ID_NAND("NAND 128MiB 1,8V 16-bit", 0xB1, 128, LP_OPTIONS16),
+ EXTENDED_ID_NAND("NAND 128MiB 3,3V 16-bit", 0xC1, 128, LP_OPTIONS16),
+ EXTENDED_ID_NAND("NAND 128MiB 1,8V 16-bit", 0xAD, 128, LP_OPTIONS16),
/* 2 Gigabit */
- EXTENDED_ID_NAND(__STR("NAND 256MiB 1,8V 8-bit"), 0xAA, 256, LP_OPTIONS),
- EXTENDED_ID_NAND(__STR("NAND 256MiB 3,3V 8-bit"), 0xDA, 256, LP_OPTIONS),
- EXTENDED_ID_NAND(__STR("NAND 256MiB 1,8V 16-bit"), 0xBA, 256, LP_OPTIONS16),
- EXTENDED_ID_NAND(__STR("NAND 256MiB 3,3V 16-bit"), 0xCA, 256, LP_OPTIONS16),
+ EXTENDED_ID_NAND("NAND 256MiB 1,8V 8-bit", 0xAA, 256, LP_OPTIONS),
+ EXTENDED_ID_NAND("NAND 256MiB 3,3V 8-bit", 0xDA, 256, LP_OPTIONS),
+ EXTENDED_ID_NAND("NAND 256MiB 1,8V 16-bit", 0xBA, 256, LP_OPTIONS16),
+ EXTENDED_ID_NAND("NAND 256MiB 3,3V 16-bit", 0xCA, 256, LP_OPTIONS16),
/* 4 Gigabit */
- EXTENDED_ID_NAND(__STR("NAND 512MiB 1,8V 8-bit"), 0xAC, 512, LP_OPTIONS),
- EXTENDED_ID_NAND(__STR("NAND 512MiB 3,3V 8-bit"), 0xDC, 512, LP_OPTIONS),
- EXTENDED_ID_NAND(__STR("NAND 512MiB 1,8V 16-bit"), 0xBC, 512, LP_OPTIONS16),
- EXTENDED_ID_NAND(__STR("NAND 512MiB 3,3V 16-bit"), 0xCC, 512, LP_OPTIONS16),
+ EXTENDED_ID_NAND("NAND 512MiB 1,8V 8-bit", 0xAC, 512, LP_OPTIONS),
+ EXTENDED_ID_NAND("NAND 512MiB 3,3V 8-bit", 0xDC, 512, LP_OPTIONS),
+ EXTENDED_ID_NAND("NAND 512MiB 1,8V 16-bit", 0xBC, 512, LP_OPTIONS16),
+ EXTENDED_ID_NAND("NAND 512MiB 3,3V 16-bit", 0xCC, 512, LP_OPTIONS16),
/* 8 Gigabit */
- EXTENDED_ID_NAND(__STR("NAND 1GiB 1,8V 8-bit"), 0xA3, 1024, LP_OPTIONS),
- EXTENDED_ID_NAND(__STR("NAND 1GiB 3,3V 8-bit"), 0xD3, 1024, LP_OPTIONS),
- EXTENDED_ID_NAND(__STR("NAND 1GiB 1,8V 16-bit"), 0xB3, 1024, LP_OPTIONS16),
- EXTENDED_ID_NAND(__STR("NAND 1GiB 3,3V 16-bit"), 0xC3, 1024, LP_OPTIONS16),
+ EXTENDED_ID_NAND("NAND 1GiB 1,8V 8-bit", 0xA3, 1024, LP_OPTIONS),
+ EXTENDED_ID_NAND("NAND 1GiB 3,3V 8-bit", 0xD3, 1024, LP_OPTIONS),
+ EXTENDED_ID_NAND("NAND 1GiB 1,8V 16-bit", 0xB3, 1024, LP_OPTIONS16),
+ EXTENDED_ID_NAND("NAND 1GiB 3,3V 16-bit", 0xC3, 1024, LP_OPTIONS16),
/* 16 Gigabit */
- EXTENDED_ID_NAND(__STR("NAND 2GiB 1,8V 8-bit"), 0xA5, 2048, LP_OPTIONS),
- EXTENDED_ID_NAND(__STR("NAND 2GiB 3,3V 8-bit"), 0xD5, 2048, LP_OPTIONS),
- EXTENDED_ID_NAND(__STR("NAND 2GiB 1,8V 16-bit"), 0xB5, 2048, LP_OPTIONS16),
- EXTENDED_ID_NAND(__STR("NAND 2GiB 3,3V 16-bit"), 0xC5, 2048, LP_OPTIONS16),
+ EXTENDED_ID_NAND("NAND 2GiB 1,8V 8-bit", 0xA5, 2048, LP_OPTIONS),
+ EXTENDED_ID_NAND("NAND 2GiB 3,3V 8-bit", 0xD5, 2048, LP_OPTIONS),
+ EXTENDED_ID_NAND("NAND 2GiB 1,8V 16-bit", 0xB5, 2048, LP_OPTIONS16),
+ EXTENDED_ID_NAND("NAND 2GiB 3,3V 16-bit", 0xC5, 2048, LP_OPTIONS16),
/* 32 Gigabit */
- EXTENDED_ID_NAND(__STR("NAND 4GiB 1,8V 8-bit"), 0xA7, 4096, LP_OPTIONS),
- EXTENDED_ID_NAND(__STR("NAND 4GiB 3,3V 8-bit"), 0xD7, 4096, LP_OPTIONS),
- EXTENDED_ID_NAND(__STR("NAND 4GiB 1,8V 16-bit"), 0xB7, 4096, LP_OPTIONS16),
- EXTENDED_ID_NAND(__STR("NAND 4GiB 3,3V 16-bit"), 0xC7, 4096, LP_OPTIONS16),
+ EXTENDED_ID_NAND("NAND 4GiB 1,8V 8-bit", 0xA7, 4096, LP_OPTIONS),
+ EXTENDED_ID_NAND("NAND 4GiB 3,3V 8-bit", 0xD7, 4096, LP_OPTIONS),
+ EXTENDED_ID_NAND("NAND 4GiB 1,8V 16-bit", 0xB7, 4096, LP_OPTIONS16),
+ EXTENDED_ID_NAND("NAND 4GiB 3,3V 16-bit", 0xC7, 4096, LP_OPTIONS16),
/* 64 Gigabit */
- EXTENDED_ID_NAND(__STR("NAND 8GiB 1,8V 8-bit"), 0xAE, 8192, LP_OPTIONS),
- EXTENDED_ID_NAND(__STR("NAND 8GiB 3,3V 8-bit"), 0xDE, 8192, LP_OPTIONS),
- EXTENDED_ID_NAND(__STR("NAND 8GiB 1,8V 16-bit"), 0xBE, 8192, LP_OPTIONS16),
- EXTENDED_ID_NAND(__STR("NAND 8GiB 3,3V 16-bit"), 0xCE, 8192, LP_OPTIONS16),
+ EXTENDED_ID_NAND("NAND 8GiB 1,8V 8-bit", 0xAE, 8192, LP_OPTIONS),
+ EXTENDED_ID_NAND("NAND 8GiB 3,3V 8-bit", 0xDE, 8192, LP_OPTIONS),
+ EXTENDED_ID_NAND("NAND 8GiB 1,8V 16-bit", 0xBE, 8192, LP_OPTIONS16),
+ EXTENDED_ID_NAND("NAND 8GiB 3,3V 16-bit", 0xCE, 8192, LP_OPTIONS16),
/* 128 Gigabit */
- EXTENDED_ID_NAND(__STR("NAND 16GiB 1,8V 8-bit"), 0x1A, 16384, LP_OPTIONS),
- EXTENDED_ID_NAND(__STR("NAND 16GiB 3,3V 8-bit"), 0x3A, 16384, LP_OPTIONS),
- EXTENDED_ID_NAND(__STR("NAND 16GiB 1,8V 16-bit"), 0x2A, 16384, LP_OPTIONS16),
- EXTENDED_ID_NAND(__STR("NAND 16GiB 3,3V 16-bit"), 0x4A, 16384, LP_OPTIONS16),
+ EXTENDED_ID_NAND("NAND 16GiB 1,8V 8-bit", 0x1A, 16384, LP_OPTIONS),
+ EXTENDED_ID_NAND("NAND 16GiB 3,3V 8-bit", 0x3A, 16384, LP_OPTIONS),
+ EXTENDED_ID_NAND("NAND 16GiB 1,8V 16-bit", 0x2A, 16384, LP_OPTIONS16),
+ EXTENDED_ID_NAND("NAND 16GiB 3,3V 16-bit", 0x4A, 16384, LP_OPTIONS16),
/* 256 Gigabit */
- EXTENDED_ID_NAND(__STR("NAND 32GiB 1,8V 8-bit"), 0x1C, 32768, LP_OPTIONS),
- EXTENDED_ID_NAND(__STR("NAND 32GiB 3,3V 8-bit"), 0x3C, 32768, LP_OPTIONS),
- EXTENDED_ID_NAND(__STR("NAND 32GiB 1,8V 16-bit"), 0x2C, 32768, LP_OPTIONS16),
- EXTENDED_ID_NAND(__STR("NAND 32GiB 3,3V 16-bit"), 0x4C, 32768, LP_OPTIONS16),
+ EXTENDED_ID_NAND("NAND 32GiB 1,8V 8-bit", 0x1C, 32768, LP_OPTIONS),
+ EXTENDED_ID_NAND("NAND 32GiB 3,3V 8-bit", 0x3C, 32768, LP_OPTIONS),
+ EXTENDED_ID_NAND("NAND 32GiB 1,8V 16-bit", 0x2C, 32768, LP_OPTIONS16),
+ EXTENDED_ID_NAND("NAND 32GiB 3,3V 16-bit", 0x4C, 32768, LP_OPTIONS16),
/* 512 Gigabit */
- EXTENDED_ID_NAND(__STR("NAND 64GiB 1,8V 8-bit"), 0x1E, 65536, LP_OPTIONS),
- EXTENDED_ID_NAND(__STR("NAND 64GiB 3,3V 8-bit"), 0x3E, 65536, LP_OPTIONS),
- EXTENDED_ID_NAND(__STR("NAND 64GiB 1,8V 16-bit"), 0x2E, 65536, LP_OPTIONS16),
- EXTENDED_ID_NAND(__STR("NAND 64GiB 3,3V 16-bit"), 0x4E, 65536, LP_OPTIONS16),
+ EXTENDED_ID_NAND("NAND 64GiB 1,8V 8-bit", 0x1E, 65536, LP_OPTIONS),
+ EXTENDED_ID_NAND("NAND 64GiB 3,3V 8-bit", 0x3E, 65536, LP_OPTIONS),
+ EXTENDED_ID_NAND("NAND 64GiB 1,8V 16-bit", 0x2E, 65536, LP_OPTIONS16),
+ EXTENDED_ID_NAND("NAND 64GiB 3,3V 16-bit", 0x4E, 65536, LP_OPTIONS16),
{NULL}
};
/* Manufacturer IDs */
-struct nand_manufacturers nand_manuf_ids[] = {
- {NAND_MFR_TOSHIBA, "Toshiba"},
- {NAND_MFR_SAMSUNG, "Samsung"},
+static const struct nand_manufacturer_desc nand_manufacturer_descs[] = {
+ {NAND_MFR_AMD, "AMD/Spansion", &amd_nand_manuf_ops},
+ {NAND_MFR_ATO, "ATO"},
+ {NAND_MFR_EON, "Eon"},
+ {NAND_MFR_ESMT, "ESMT", &esmt_nand_manuf_ops},
{NAND_MFR_FUJITSU, "Fujitsu"},
+ {NAND_MFR_HYNIX, "Hynix", &hynix_nand_manuf_ops},
+ {NAND_MFR_INTEL, "Intel"},
+ {NAND_MFR_MACRONIX, "Macronix", ¯onix_nand_manuf_ops},
+ {NAND_MFR_MICRON, "Micron", µn_nand_manuf_ops},
{NAND_MFR_NATIONAL, "National"},
{NAND_MFR_RENESAS, "Renesas"},
+ {NAND_MFR_SAMSUNG, "Samsung", &samsung_nand_manuf_ops},
+ {NAND_MFR_SANDISK, "SanDisk"},
{NAND_MFR_STMICRO, "ST Micro"},
- {NAND_MFR_HYNIX, "Hynix"},
- {NAND_MFR_MICRON, "Micron"},
- {NAND_MFR_AMD, "AMD/Spansion"},
- {NAND_MFR_MACRONIX, "Macronix"},
- {NAND_MFR_EON, "Eon"},
+ {NAND_MFR_TOSHIBA, "Toshiba", &toshiba_nand_manuf_ops},
{NAND_MFR_WINBOND, "Winbond"},
- {0x0, "Unknown"}
};
-EXPORT_SYMBOL(nand_manuf_ids);
-EXPORT_SYMBOL(nand_flash_ids);
+/**
+ * nand_get_manufacturer_desc - Get manufacturer information from the
+ * manufacturer ID
+ * @id: manufacturer ID
+ *
+ * Returns a nand_manufacturer_desc object if the manufacturer is defined
+ * in the NAND manufacturers database, NULL otherwise.
+ */
+const struct nand_manufacturer_desc *nand_get_manufacturer_desc(u8 id)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(nand_manufacturer_descs); i++)
+ if (nand_manufacturer_descs[i].id == id)
+ return &nand_manufacturer_descs[i];
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Thomas Gleixner <tglx@linutronix.de>");
-MODULE_DESCRIPTION("Nand device & manufacturer IDs");
+ return NULL;
+}
diff --git a/drivers/mtd/nand/nand_imx.c b/drivers/mtd/nand/nand_imx.c
index 993fb2e249..5e4504a48e 100644
--- a/drivers/mtd/nand/nand_imx.c
+++ b/drivers/mtd/nand/nand_imx.c
@@ -24,6 +24,7 @@
#include <init.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
+#include <linux/mtd/rawnand.h>
#include <linux/clk.h>
#include <mach/generic.h>
#include <mach/imx-nand.h>
@@ -885,20 +886,18 @@ static void preset_v3(struct nand_chip *chip)
}
static int imx_nand_write_page(struct nand_chip *chip,
- uint32_t offset, int data_len, const uint8_t *buf,
- int oob_required, int page, int cached, int raw)
+ const uint8_t *buf, bool ecc, int page)
{
struct mtd_info *mtd = nand_to_mtd(chip);
struct imx_nand_host *host = chip->priv;
int status;
- host->enable_hwecc(chip, !raw);
+ host->enable_hwecc(chip, ecc);
chip->legacy.cmdfunc(chip, NAND_CMD_SEQIN, 0x00, page);
memcpy32(host->main_area0, buf, mtd->writesize);
- if (oob_required)
- copy_spare(chip, 0, chip->oob_poi);
+ copy_spare(chip, 0, chip->oob_poi);
host->send_page(host, NFC_INPUT);
chip->legacy.cmdfunc(chip, NAND_CMD_PAGEPROG, -1, -1);
@@ -910,12 +909,26 @@ static int imx_nand_write_page(struct nand_chip *chip,
return 0;
}
+static int imx_nand_write_page_ecc(struct nand_chip *chip, const uint8_t *buf,
+ int oob_required, int page)
+{
+ return imx_nand_write_page(chip, buf, true, page);
+}
+
+static int imx_nand_write_page_raw(struct nand_chip *chip, const uint8_t *buf,
+ int oob_required, int page)
+{
+ return imx_nand_write_page(chip, buf, false, page);
+}
+
static void imx_nand_do_read_page(struct nand_chip *chip, uint8_t *buf,
- int oob_required)
+ int oob_required, int page)
{
struct mtd_info *mtd = nand_to_mtd(chip);
struct imx_nand_host *host = chip->priv;
+ nand_read_page_op(chip, page, 0, NULL, 0);
+
host->send_page(host, NFC_OUTPUT);
memcpy32(buf, host->main_area0, mtd->writesize);
@@ -931,7 +944,7 @@ static int imx_nand_read_page(struct nand_chip *chip, uint8_t *buf,
host->enable_hwecc(chip, true);
- imx_nand_do_read_page(chip, buf, oob_required);
+ imx_nand_do_read_page(chip, buf, oob_required, page);
return host->correct(chip);
}
@@ -943,7 +956,7 @@ static int imx_nand_read_page_raw(struct nand_chip *chip, uint8_t *buf,
host->enable_hwecc(chip, false);
- imx_nand_do_read_page(chip, buf, oob_required);
+ imx_nand_do_read_page(chip, buf, oob_required, page);
return 0;
}
@@ -1205,7 +1218,7 @@ static int imxnd_create_bbt(struct nand_chip *chip)
if (ret)
return ret;
- ret = nand_default_bbt(chip);
+ ret = nand_create_bbt(chip);
if (ret)
return ret;
@@ -1336,7 +1349,7 @@ static int __init imxnd_probe(struct device_d *dev)
/* structures must be linked */
this = &host->nand;
- mtd = &this->mtd;
+ mtd = nand_to_mtd(this);
mtd->dev.parent = dev;
mtd->name = "imx_nand";
@@ -1351,7 +1364,8 @@ static int __init imxnd_probe(struct device_d *dev)
this->legacy.read_word = imx_nand_read_word;
this->legacy.write_buf = imx_nand_write_buf;
this->legacy.read_buf = imx_nand_read_buf;
- this->write_page = imx_nand_write_page;
+ this->ecc.write_page = imx_nand_write_page_ecc;
+ this->ecc.write_page_raw = imx_nand_write_page_raw;
if (host->hw_ecc) {
this->ecc.calculate = imx_nand_calculate_ecc;
@@ -1372,12 +1386,12 @@ static int __init imxnd_probe(struct device_d *dev)
this->ecc.mode = NAND_ECC_SOFT;
}
- this->ecc.layout = oob_smallpage;
+ mtd_set_ecclayout(mtd, oob_smallpage);
/* NAND bus width determines access functions used by upper layer */
if (host->data_width == 2) {
this->options |= NAND_BUSWIDTH_16;
- this->ecc.layout = &nandv1_hw_eccoob_smallpage;
+ mtd_set_ecclayout(mtd, &nandv1_hw_eccoob_smallpage);
imx_nand_set_layout(0, 16);
}
@@ -1405,9 +1419,9 @@ static int __init imxnd_probe(struct device_d *dev)
"You will loose factory bad block markers!\n");
if (mtd->writesize == 2048)
- this->ecc.layout = oob_largepage;
+ mtd_set_ecclayout(mtd, oob_largepage);
else
- this->ecc.layout = oob_4kpage;
+ mtd_set_ecclayout(mtd, oob_4kpage);
host->pagesize_2k = 1;
if (nfc_is_v21())
writew(NFC_V2_SPAS_SPARESIZE(64), host->regs + NFC_V2_SPAS);
@@ -1437,7 +1451,7 @@ static int __init imxnd_probe(struct device_d *dev)
err = 0;
}
- add_mtd_nand_device(this, "nand");
+ add_mtd_nand_device(mtd, "nand");
dev->priv = host;
diff --git a/drivers/mtd/nand/nand_jedec.c b/drivers/mtd/nand/nand_jedec.c
new file mode 100644
index 0000000000..5632d2c73f
--- /dev/null
+++ b/drivers/mtd/nand/nand_jedec.c
@@ -0,0 +1,135 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
+ * 2002-2006 Thomas Gleixner (tglx@linutronix.de)
+ *
+ * Credits:
+ * David Woodhouse for adding multichip support
+ *
+ * Aleph One Ltd. and Toby Churchill Ltd. for supporting the
+ * rework for 2K page size chips
+ *
+ * This file contains all ONFI helpers.
+ */
+
+#include <common.h>
+#include <linux/slab.h>
+
+#include "internals.h"
+
+#define JEDEC_PARAM_PAGES 3
+
+/*
+ * Check if the NAND chip is JEDEC compliant, returns 1 if it is, 0 otherwise.
+ */
+int nand_jedec_detect(struct nand_chip *chip)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ struct nand_memory_organization *memorg;
+ struct nand_jedec_params *p;
+ struct jedec_ecc_info *ecc;
+ bool use_datain = false;
+ int jedec_version = 0;
+ char id[5];
+ int i, val, ret;
+ u16 crc;
+
+ memorg = nanddev_get_memorg(&chip->base);
+
+ /* Try JEDEC for unknown chip or LP */
+ ret = nand_readid_op(chip, 0x40, id, sizeof(id));
+ if (ret || strncmp(id, "JEDEC", sizeof(id)))
+ return 0;
+
+ /* JEDEC chip: allocate a buffer to hold its parameter page */
+ p = kzalloc(sizeof(*p), GFP_KERNEL);
+ if (!p)
+ return -ENOMEM;
+
+ if (!nand_has_exec_op(chip) ||
+ !nand_read_data_op(chip, p, sizeof(*p), true, true))
+ use_datain = true;
+
+ for (i = 0; i < JEDEC_PARAM_PAGES; i++) {
+ if (!i)
+ ret = nand_read_param_page_op(chip, 0x40, p,
+ sizeof(*p));
+ else if (use_datain)
+ ret = nand_read_data_op(chip, p, sizeof(*p), true,
+ false);
+ else
+ ret = nand_change_read_column_op(chip, sizeof(*p) * i,
+ p, sizeof(*p), true);
+ if (ret) {
+ ret = 0;
+ goto free_jedec_param_page;
+ }
+
+ crc = onfi_crc16(ONFI_CRC_BASE, (u8 *)p, 510);
+ if (crc == le16_to_cpu(p->crc))
+ break;
+ }
+
+ if (i == JEDEC_PARAM_PAGES) {
+ pr_err("Could not find valid JEDEC parameter page; aborting\n");
+ goto free_jedec_param_page;
+ }
+
+ /* Check version */
+ val = le16_to_cpu(p->revision);
+ if (val & (1 << 2))
+ jedec_version = 10;
+ else if (val & (1 << 1))
+ jedec_version = 1; /* vendor specific version */
+
+ if (!jedec_version) {
+ pr_info("unsupported JEDEC version: %d\n", val);
+ goto free_jedec_param_page;
+ }
+
+ sanitize_string(p->manufacturer, sizeof(p->manufacturer));
+ sanitize_string(p->model, sizeof(p->model));
+ chip->parameters.model = strdup(p->model);
+ if (!chip->parameters.model) {
+ ret = -ENOMEM;
+ goto free_jedec_param_page;
+ }
+
+ memorg->pagesize = le32_to_cpu(p->byte_per_page);
+ mtd->writesize = memorg->pagesize;
+
+ /* Please reference to the comment for nand_flash_detect_onfi. */
+ memorg->pages_per_eraseblock =
+ 1 << (fls(le32_to_cpu(p->pages_per_block)) - 1);
+ mtd->erasesize = memorg->pages_per_eraseblock * memorg->pagesize;
+
+ memorg->oobsize = le16_to_cpu(p->spare_bytes_per_page);
+ mtd->oobsize = memorg->oobsize;
+
+ memorg->luns_per_target = p->lun_count;
+ memorg->planes_per_lun = 1 << p->multi_plane_addr;
+
+ /* Please reference to the comment for nand_flash_detect_onfi. */
+ memorg->eraseblocks_per_lun =
+ 1 << (fls(le32_to_cpu(p->blocks_per_lun)) - 1);
+ memorg->bits_per_cell = p->bits_per_cell;
+
+ if (le16_to_cpu(p->features) & JEDEC_FEATURE_16_BIT_BUS)
+ chip->options |= NAND_BUSWIDTH_16;
+
+ /* ECC info */
+ ecc = &p->ecc_info[0];
+
+ if (ecc->codeword_size >= 9) {
+ chip->base.eccreq.strength = ecc->ecc_bits;
+ chip->base.eccreq.step_size = 1 << ecc->codeword_size;
+ } else {
+ pr_warn("Invalid codeword size\n");
+ }
+
+ ret = 1;
+
+free_jedec_param_page:
+ kfree(p);
+ return ret;
+}
diff --git a/drivers/mtd/nand/nand_legacy.c b/drivers/mtd/nand/nand_legacy.c
new file mode 100644
index 0000000000..0fcafe38f9
--- /dev/null
+++ b/drivers/mtd/nand/nand_legacy.c
@@ -0,0 +1,629 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
+ * 2002-2006 Thomas Gleixner (tglx@linutronix.de)
+ *
+ * Credits:
+ * David Woodhouse for adding multichip support
+ *
+ * Aleph One Ltd. and Toby Churchill Ltd. for supporting the
+ * rework for 2K page size chips
+ *
+ * This file contains all legacy helpers/code that should be removed
+ * at some point.
+ */
+
+#include <common.h>
+#include <errno.h>
+#include <clock.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/nand_bch.h>
+#include <linux/mtd/nand_ecc.h>
+#include <linux/err.h>
+#include <asm/byteorder.h>
+#include <io.h>
+#include <malloc.h>
+#include <module.h>
+
+#include "internals.h"
+
+/**
+ * nand_read_byte - [DEFAULT] read one byte from the chip
+ * @chip: NAND chip object
+ *
+ * Default read function for 8bit buswidth
+ */
+static uint8_t nand_read_byte(struct nand_chip *chip)
+{
+ return readb(chip->legacy.IO_ADDR_R);
+}
+
+/**
+ * nand_read_byte16 - [DEFAULT] read one byte endianness aware from the chip
+ * @chip: NAND chip object
+ *
+ * Default read function for 16bit buswidth with endianness conversion.
+ *
+ */
+static uint8_t nand_read_byte16(struct nand_chip *chip)
+{
+ return (uint8_t) cpu_to_le16(readw(chip->legacy.IO_ADDR_R));
+}
+
+/**
+ * nand_select_chip - [DEFAULT] control CE line
+ * @chip: NAND chip object
+ * @chipnr: chipnumber to select, -1 for deselect
+ *
+ * Default select function for 1 chip devices.
+ */
+static void nand_select_chip(struct nand_chip *chip, int chipnr)
+{
+ switch (chipnr) {
+ case -1:
+ chip->legacy.cmd_ctrl(chip, NAND_CMD_NONE,
+ 0 | NAND_CTRL_CHANGE);
+ break;
+ case 0:
+ break;
+
+ default:
+ BUG();
+ }
+}
+
+/**
+ * nand_write_byte - [DEFAULT] write single byte to chip
+ * @chip: NAND chip object
+ * @byte: value to write
+ *
+ * Default function to write a byte to