This driver includes many problems.
One of the biggest one is a bunch of hard-coded parameters. This IP
has many parameters that can be customized when a delivery RTL is
generated. However, this driver was upstreamed by Intel, with
Intel parameters hard-coded. Later, Altera added denali_dt.c to use
this driver for embedded boards, but they did not fix the code in
denali.c So, this driver has never worked. Even some DT bindings
actually turned out wrong.
There are more problems: [1] The driver just retrieves the OOB area as-is
whereas the controller uses syndrome page layout. [2] Many NAND chip
specific parameters are hard-coded in the driver. [3] ONFi devices are
not working [4] It can not read Bad Block Marker
This patch series intends to solve those problems.
Outstanding changes are:
- Fix raw/oob callbacks for syndrome page layout
- Implement setup_data_interface() callback
- Fix/implement more commands for ONFi devices
- Allow to skip the driver internal bounce buffer
- Support PIO in case DMA is not supported
- Switch from ->cmdfunc over to ->cmd_ctrl
18 patches were merged at v2.
Here is the rest of the series.
v1: https://lkml.org/lkml/2016/11/26/144
v2: https://lkml.org/lkml/2017/3/22/804
Masahiro Yamada (37):
mtd: nand: relax ecc.read_page() return value for uncorrectable ECC
mtd: nand: denali: allow to override mtd->name from label DT property
mtd: nand: denali: remove meaningless pipeline read-ahead operation
mtd: nand: denali: fix bitflips calculation in handle_ecc()
mtd: nand: denali: fix erased page checking
mtd: nand: denali: support HW_ECC_FIXUP capability
mtd: nand: denali_dt: enable HW_ECC_FIXUP for Altera SOCFPGA variant
mtd: nand: denali: support 64bit capable DMA engine
mtd: nand: denali_dt: remove dma-mask DT property
mtd: nand: denali_dt: use pdev instead of ofdev for platform_device
mtd: nand: denali: allow to override revision number
mtd: nand: denali: support 1024 byte ECC step size
mtd: nand: denali: avoid hard-coding ecc.strength and ecc.bytes
mtd: nand: denali: support "nand-ecc-strength" DT property
mtd: nand: denali: remove Toshiba and Hynix specific fixup code
mtd: nand: denali_dt: add compatible strings for UniPhier SoC variants
mtd: nand: denali: set NAND_ECC_CUSTOM_PAGE_ACCESS
mtd: nand: denali: do not propagate NAND_STATUS_FAIL to waitfunc()
mtd: nand: denali: use BIT() and GENMASK() for register macros
mtd: nand: denali: remove unneeded find_valid_banks()
mtd: nand: denali: handle timing parameters by setup_data_interface()
mtd: nand: denali: rework interrupt handling
mtd: nand: denali: fix NAND_CMD_STATUS handling
mtd: nand: denali: fix NAND_CMD_PARAM handling
mtd: nand: denali: switch over to cmd_ctrl instead of cmdfunc
mtd: nand: denali: fix bank reset function
mtd: nand: denali: use interrupt instead of polling for bank reset
mtd: nand: denali: propagate page to helpers via function argument
mtd: nand: denali: merge struct nand_buf into struct denali_nand_info
mtd: nand: denali: use flag instead of register macro for direction
mtd: nand: denali: fix raw and oob accessors for syndrome page layout
mtd: nand: denali: support hardware-assisted erased page detection
mtd: nand: allocate aligned buffers if NAND_OWN_BUFFERS is unset
mtd: nand: allow drivers to request minimum alignment for passed
buffer
mtd: nand: denali: skip driver internal bounce buffer when possible
mtd: nand: denali: use non-managed kmalloc() for DMA buffer
mtd: nand: denali: enable bad block table scan
.../devicetree/bindings/mtd/denali-nand.txt | 24 +-
drivers/mtd/nand/denali.c | 1971 ++++++++++----------
drivers/mtd/nand/denali.h | 308 +--
drivers/mtd/nand/denali_dt.c | 90 +-
drivers/mtd/nand/denali_pci.c | 10 +-
drivers/mtd/nand/nand_base.c | 49 +-
include/linux/mtd/nand.h | 4 +-
7 files changed, 1234 insertions(+), 1222 deletions(-)
--
2.7.4
The comment for ecc.read_page() requires that it should return
"0 if bitflips uncorrectable".
Actually, drivers could return positive values when uncorrectable
bitflips occur. For example, nand_read_page_swecc() is the case.
If ecc.correct() returns -EBADMSG for the first ECC sector, and
a positive value for the second one, nand_read_page_swecc() returns
a positive max_bitflips and increments ecc_stats.failed for the same
page.
The requirement can be relaxed by tweaking nand_do_read_ops().
Move the max_bitflips calculation below the retry.
Signed-off-by: Masahiro Yamada <[email protected]>
Suggested-by: Boris Brezillon <[email protected]>
---
Changes in v3:
- Newly added
Changes in v2: None
drivers/mtd/nand/nand_base.c | 3 +--
include/linux/mtd/nand.h | 2 +-
2 files changed, 2 insertions(+), 3 deletions(-)
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index e13f959..f828ad7 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -1993,8 +1993,6 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
break;
}
- max_bitflips = max_t(unsigned int, max_bitflips, ret);
-
/* Transfer not aligned data */
if (use_bufpoi) {
if (!NAND_HAS_SUBPAGE_READ(chip) && !oob &&
@@ -2045,6 +2043,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
}
buf += bytes;
+ max_bitflips = max_t(unsigned int, max_bitflips, ret);
} else {
memcpy(buf, chip->buffers->databuf + col, bytes);
buf += bytes;
diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
index 9591e0f..69cccd1 100644
--- a/include/linux/mtd/nand.h
+++ b/include/linux/mtd/nand.h
@@ -525,7 +525,7 @@ static inline void nand_hw_control_init(struct nand_hw_control *nfc)
* out-of-band data).
* @read_page: function to read a page according to the ECC generator
* requirements; returns maximum number of bitflips corrected in
- * any single ECC step, 0 if bitflips uncorrectable, -EIO hw error
+ * any single ECC step, -EIO hw error
* @read_subpage: function to read parts of the page covered by ECC;
* returns same as read_page()
* @write_subpage: function to write parts of the page covered by ECC.
--
2.7.4
There are various customizable parameters, so several variants for
this IP. A generic compatible like "denali,denali-nand-dt" is
useless. Moreover, there are multiple things wrong with this string.
(Refer to Rob's comment [1])
The "denali,denali-nand-dt" was added by Altera for the SOCFPGA port.
Replace it with a more specific string "altr,socfpga-denali-nand".
There are no users (in upstream) of the old compatible string.
The Denali IP on SOCFPGA incorporates the hardware ECC fixup engine.
So, this capability should be associated with the compatible.
[1] https://lkml.org/lkml/2016/12/1/450
Signed-off-by: Masahiro Yamada <[email protected]>
Acked-by: Rob Herring <[email protected]>
---
Changes in v3:
- Update git-log to mention there are no users of the old binding.
Changes in v2:
- Add caps flag to of_device_id data, not hard-code in driver code
- Add Altera-specific compatible.
Documentation/devicetree/bindings/mtd/denali-nand.txt | 5 +++--
drivers/mtd/nand/denali_dt.c | 14 ++++++++++----
2 files changed, 13 insertions(+), 6 deletions(-)
diff --git a/Documentation/devicetree/bindings/mtd/denali-nand.txt b/Documentation/devicetree/bindings/mtd/denali-nand.txt
index b04d03a..6f4ab4c 100644
--- a/Documentation/devicetree/bindings/mtd/denali-nand.txt
+++ b/Documentation/devicetree/bindings/mtd/denali-nand.txt
@@ -1,7 +1,8 @@
* Denali NAND controller
Required properties:
- - compatible : should be "denali,denali-nand-dt"
+ - compatible : should be one of the following:
+ "altr,socfpga-denali-nand" - for Altera SOCFPGA
- reg : should contain registers location and length for data and reg.
- reg-names: Should contain the reg names "nand_data" and "denali_reg"
- interrupts : The interrupt number.
@@ -15,7 +16,7 @@ Examples:
nand: nand@ff900000 {
#address-cells = <1>;
#size-cells = <1>;
- compatible = "denali,denali-nand-dt";
+ compatible = "altr,socfpga-denali-nand";
reg = <0xff900000 0x100000>, <0xffb80000 0x10000>;
reg-names = "nand_data", "denali_reg";
interrupts = <0 144 4>;
diff --git a/drivers/mtd/nand/denali_dt.c b/drivers/mtd/nand/denali_dt.c
index 293ddb8..9577bfd 100644
--- a/drivers/mtd/nand/denali_dt.c
+++ b/drivers/mtd/nand/denali_dt.c
@@ -33,11 +33,17 @@ struct denali_dt_data {
unsigned int caps;
};
-static const struct of_device_id denali_nand_dt_ids[] = {
- { .compatible = "denali,denali-nand-dt" },
- { /* sentinel */ }
- };
+static const struct denali_dt_data denali_socfpga_data = {
+ .caps = DENALI_CAP_HW_ECC_FIXUP,
+};
+static const struct of_device_id denali_nand_dt_ids[] = {
+ {
+ .compatible = "altr,socfpga-denali-nand",
+ .data = &denali_socfpga_data,
+ },
+ { /* sentinel */ }
+};
MODULE_DEVICE_TABLE(of, denali_nand_dt_ids);
static u64 denali_dma_mask;
--
2.7.4
The Denali IP can automatically detect device parameters such as
page size, oob size, device width, etc. and this driver currently
relies on it. However, this hardware function is known to be
problematic.
[1] Due to a hardware bug, various misdetected cases were reported.
That is why get_toshiba_nand_para() and get_hynix_nand_para()
exist to fix-up the misdetected parameters. It is not realistic
to add a new NAND device to the *black list* every time we are
hit by a misdetected case. We would never be able to guarantee
that all cases are covered.
[2] Because this feature is unreliable, it is disabled on some
platforms.
The nand_scan_ident() detects device parameters in a more tested
way. The hardware should not set the device parameter registers in
a different, unreliable way. Instead, set the parameters from the
nand_scan_ident() back to the registers.
Signed-off-by: Masahiro Yamada <[email protected]>
---
Changes in v3: None
Changes in v2: None
drivers/mtd/nand/denali.c | 40 ++++++----------------------------------
1 file changed, 6 insertions(+), 34 deletions(-)
diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c
index 2f796e3..2b20232 100644
--- a/drivers/mtd/nand/denali.c
+++ b/drivers/mtd/nand/denali.c
@@ -337,36 +337,6 @@ static void get_samsung_nand_para(struct denali_nand_info *denali,
}
}
-static void get_toshiba_nand_para(struct denali_nand_info *denali)
-{
- /*
- * Workaround to fix a controller bug which reports a wrong
- * spare area size for some kind of Toshiba NAND device
- */
- if ((ioread32(denali->flash_reg + DEVICE_MAIN_AREA_SIZE) == 4096) &&
- (ioread32(denali->flash_reg + DEVICE_SPARE_AREA_SIZE) == 64))
- iowrite32(216, denali->flash_reg + DEVICE_SPARE_AREA_SIZE);
-}
-
-static void get_hynix_nand_para(struct denali_nand_info *denali,
- uint8_t device_id)
-{
- switch (device_id) {
- case 0xD5: /* Hynix H27UAG8T2A, H27UBG8U5A or H27UCG8VFA */
- case 0xD7: /* Hynix H27UDG8VEM, H27UCG8UDM or H27UCG8V5A */
- iowrite32(128, denali->flash_reg + PAGES_PER_BLOCK);
- iowrite32(4096, denali->flash_reg + DEVICE_MAIN_AREA_SIZE);
- iowrite32(224, denali->flash_reg + DEVICE_SPARE_AREA_SIZE);
- iowrite32(0, denali->flash_reg + DEVICE_WIDTH);
- break;
- default:
- dev_warn(denali->dev,
- "Unknown Hynix NAND (Device ID: 0x%x).\n"
- "Will use default parameter values instead.\n",
- device_id);
- }
-}
-
/*
* determines how many NAND chips are connected to the controller. Note for
* Intel CE4100 devices we don't support more than one device.
@@ -453,10 +423,6 @@ static uint16_t denali_nand_timing_set(struct denali_nand_info *denali)
return FAIL;
} else if (maf_id == 0xEC) { /* Samsung NAND */
get_samsung_nand_para(denali, device_id);
- } else if (maf_id == 0x98) { /* Toshiba NAND */
- get_toshiba_nand_para(denali);
- } else if (maf_id == 0xAD) { /* Hynix NAND */
- get_hynix_nand_para(denali, device_id);
}
dev_info(denali->dev,
@@ -1663,6 +1629,12 @@ int denali_init(struct denali_nand_info *denali)
chip->ecc.strength);
iowrite32(chip->ecc.strength, denali->flash_reg + ECC_CORRECTION);
+ iowrite32(mtd->erasesize / mtd->writesize,
+ denali->flash_reg + PAGES_PER_BLOCK);
+ iowrite32(denali->nand.options & NAND_BUSWIDTH_16 ? 1 : 0,
+ denali->flash_reg + DEVICE_WIDTH);
+ iowrite32(mtd->writesize, denali->flash_reg + DEVICE_MAIN_AREA_SIZE);
+ iowrite32(mtd->oobsize, denali->flash_reg + DEVICE_SPARE_AREA_SIZE);
iowrite32(chip->ecc.size, denali->flash_reg + CFG_DATA_BLOCK_SIZE);
iowrite32(chip->ecc.size, denali->flash_reg + CFG_LAST_DATA_BLOCK_SIZE);
--
2.7.4
The pipeline read-ahead function of the Denali IP enables continuous
reading from the device; while data is being read out by a CPU, the
controller maintains additional commands for streaming data from the
device. This will reduce the latency of the second page or later.
This feature is obviously no help for per-page accessors of Linux
NAND driver interface.
In the current implementation, the pipeline command is issued to
load a single page, then data are read out immediately. The use of
the pipeline operation is not adding any advantage, but just adding
complexity to the code. Remove.
Signed-off-by: Masahiro Yamada <[email protected]>
---
Changes in v3: None
Changes in v2:
- Newly added
drivers/mtd/nand/denali.c | 42 +++---------------------------------------
1 file changed, 3 insertions(+), 39 deletions(-)
diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c
index 4e6d03d..65cf7cc 100644
--- a/drivers/mtd/nand/denali.c
+++ b/drivers/mtd/nand/denali.c
@@ -84,7 +84,6 @@ static inline struct denali_nand_info *mtd_to_denali(struct mtd_info *mtd)
#define SPARE_ACCESS 0x41
#define MAIN_ACCESS 0x42
#define MAIN_SPARE_ACCESS 0x43
-#define PIPELINE_ACCESS 0x2000
#define DENALI_READ 0
#define DENALI_WRITE 0x100
@@ -683,15 +682,7 @@ static int denali_send_pipeline_cmd(struct denali_nand_info *denali,
int access_type, int op)
{
int status = PASS;
- uint32_t page_count = 1;
- uint32_t addr, cmd, irq_status, irq_mask;
-
- if (op == DENALI_READ)
- irq_mask = INTR__LOAD_COMP;
- else if (op == DENALI_WRITE)
- irq_mask = 0;
- else
- BUG();
+ uint32_t addr, cmd;
setup_ecc_for_xfer(denali, ecc_en, transfer_spare);
@@ -714,35 +705,8 @@ static int denali_send_pipeline_cmd(struct denali_nand_info *denali,
cmd = MODE_10 | addr;
index_addr(denali, cmd, access_type);
- /*
- * page 33 of the NAND controller spec indicates we should not
- * use the pipeline commands in Spare area only mode.
- * So we don't.
- */
- if (access_type == SPARE_ACCESS) {
- cmd = MODE_01 | addr;
- iowrite32(cmd, denali->flash_mem);
- } else {
- index_addr(denali, cmd,
- PIPELINE_ACCESS | op | page_count);
-
- /*
- * wait for command to be accepted
- * can always use status0 bit as the
- * mask is identical for each bank.
- */
- irq_status = wait_for_irq(denali, irq_mask);
-
- if (irq_status == 0) {
- dev_err(denali->dev,
- "cmd, page, addr on timeout (0x%x, 0x%x, 0x%x)\n",
- cmd, denali->page, addr);
- status = FAIL;
- } else {
- cmd = MODE_01 | addr;
- iowrite32(cmd, denali->flash_mem);
- }
- }
+ cmd = MODE_01 | addr;
+ iowrite32(cmd, denali->flash_mem);
}
return status;
}
--
2.7.4
The denali_cmdfunc() actually does nothing valuable for
NAND_CMD_{PAGEPROG,READ0,SEQIN}.
For NAND_CMD_{READ0,SEQIN}, it copies "page" to "denali->page", then
denali_read_page() and denali_read_page_raw() compare them to check
if the NAND framework called the callbacks in correct order.
(Inconsistently, this check is missing from the denali_write_page()
and denali_write_page_raw().)
The framework is widely tested by many drivers, so this kind of
sanity check is unneeded. The Denali controller is equipped with
high level interface for read/write, so let's skip unneeded call
of cmdfunc().
Signed-off-by: Masahiro Yamada <[email protected]>
---
Changes in v3: None
Changes in v2:
- Newly added
drivers/mtd/nand/denali.c | 29 ++++++++---------------------
1 file changed, 8 insertions(+), 21 deletions(-)
diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c
index 2b20232..79851ca 100644
--- a/drivers/mtd/nand/denali.c
+++ b/drivers/mtd/nand/denali.c
@@ -998,7 +998,7 @@ static void denali_setup_dma(struct denali_nand_info *denali, int op)
* configuration details.
*/
static int write_page(struct mtd_info *mtd, struct nand_chip *chip,
- const uint8_t *buf, bool raw_xfer)
+ const uint8_t *buf, int page, bool raw_xfer)
{
struct denali_nand_info *denali = mtd_to_denali(mtd);
dma_addr_t addr = denali->buf.dma_buf;
@@ -1006,6 +1006,8 @@ static int write_page(struct mtd_info *mtd, struct nand_chip *chip,
uint32_t irq_status;
uint32_t irq_mask = INTR__DMA_CMD_COMP | INTR__PROGRAM_FAIL;
+ denali->page = page;
+
/*
* if it is a raw xfer, we want to disable ecc and send the spare area.
* !raw_xfer - enable ecc
@@ -1059,7 +1061,7 @@ static int denali_write_page(struct mtd_info *mtd, struct nand_chip *chip,
* 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(mtd, chip, buf, page, false);
}
/*
@@ -1075,7 +1077,7 @@ static int denali_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
* 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(mtd, chip, buf, page, true);
}
static int denali_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
@@ -1105,12 +1107,7 @@ static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip,
unsigned long uncor_ecc_flags = 0;
int stat = 0;
- if (page != denali->page) {
- dev_err(denali->dev,
- "IN %s: page %d is not equal to denali->page %d",
- __func__, page, denali->page);
- BUG();
- }
+ denali->page = page;
setup_ecc_for_xfer(denali, true, false);
@@ -1154,12 +1151,7 @@ static int denali_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
size_t size = mtd->writesize + mtd->oobsize;
uint32_t irq_mask = INTR__DMA_CMD_COMP;
- if (page != denali->page) {
- dev_err(denali->dev,
- "IN %s: page %d is not equal to denali->page %d",
- __func__, page, denali->page);
- BUG();
- }
+ denali->page = page;
setup_ecc_for_xfer(denali, false, true);
denali_enable_dma(denali, true);
@@ -1238,8 +1230,6 @@ static void denali_cmdfunc(struct mtd_info *mtd, unsigned int cmd, int col,
int i;
switch (cmd) {
- case NAND_CMD_PAGEPROG:
- break;
case NAND_CMD_STATUS:
read_status(denali);
break;
@@ -1259,10 +1249,6 @@ static void denali_cmdfunc(struct mtd_info *mtd, unsigned int cmd, int col,
write_byte_to_buf(denali, id);
}
break;
- case NAND_CMD_READ0:
- case NAND_CMD_SEQIN:
- denali->page = page;
- break;
case NAND_CMD_RESET:
reset_bank(denali);
break;
@@ -1644,6 +1630,7 @@ int denali_init(struct denali_nand_info *denali)
mtd_set_ooblayout(mtd, &denali_ooblayout_ops);
+ chip->ecc.options |= NAND_ECC_CUSTOM_PAGE_ACCESS;
chip->ecc.read_page = denali_read_page;
chip->ecc.read_page_raw = denali_read_page_raw;
chip->ecc.write_page = denali_write_page;
--
2.7.4
The function find_valid_banks() issues the Read ID (0x90) command,
then compares the first byte (Manufacturer ID) of each bank with
the one of bank0.
This is equivalent to what nand_scan_ident() does. The number of
chips is detected there, so this is unneeded.
What is worse for find_valid_banks() is that, if multiple chips are
connected to INTEL_CE4100 platform, it crashes the kernel by BUG().
This is what we should avoid. This function is just harmful and
unneeded.
Signed-off-by: Masahiro Yamada <[email protected]>
---
Changes in v3: None
Changes in v2:
- Newly added
drivers/mtd/nand/denali.c | 47 -----------------------------------------------
drivers/mtd/nand/denali.h | 1 -
2 files changed, 48 deletions(-)
diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c
index 5da8156..0c93a07 100644
--- a/drivers/mtd/nand/denali.c
+++ b/drivers/mtd/nand/denali.c
@@ -338,51 +338,6 @@ static void get_samsung_nand_para(struct denali_nand_info *denali,
}
/*
- * determines how many NAND chips are connected to the controller. Note for
- * Intel CE4100 devices we don't support more than one device.
- */
-static void find_valid_banks(struct denali_nand_info *denali)
-{
- uint32_t id[denali->max_banks];
- int i;
-
- denali->total_used_banks = 1;
- for (i = 0; i < denali->max_banks; i++) {
- index_addr(denali, MODE_11 | (i << 24) | 0, 0x90);
- index_addr(denali, MODE_11 | (i << 24) | 1, 0);
- index_addr_read_data(denali, MODE_11 | (i << 24) | 2, &id[i]);
-
- dev_dbg(denali->dev,
- "Return 1st ID for bank[%d]: %x\n", i, id[i]);
-
- if (i == 0) {
- if (!(id[i] & 0x0ff))
- break; /* WTF? */
- } else {
- if ((id[i] & 0x0ff) == (id[0] & 0x0ff))
- denali->total_used_banks++;
- else
- break;
- }
- }
-
- if (denali->platform == INTEL_CE4100) {
- /*
- * Platform limitations of the CE4100 device limit
- * users to a single chip solution for NAND.
- * Multichip support is not enabled.
- */
- if (denali->total_used_banks != 1) {
- dev_err(denali->dev,
- "Sorry, Intel CE4100 only supports a single NAND device.\n");
- BUG();
- }
- }
- dev_dbg(denali->dev,
- "denali->total_used_banks: %d\n", denali->total_used_banks);
-}
-
-/*
* Use the configuration feature register to determine the maximum number of
* banks that the hardware supports.
*/
@@ -439,8 +394,6 @@ static uint16_t denali_nand_timing_set(struct denali_nand_info *denali)
ioread32(denali->flash_reg + RDWR_EN_HI_CNT),
ioread32(denali->flash_reg + CS_SETUP_CNT));
- find_valid_banks(denali);
-
/*
* If the user specified to override the default timings
* with a specific ONFI mode, we apply those changes here.
diff --git a/drivers/mtd/nand/denali.h b/drivers/mtd/nand/denali.h
index 610b013..1f0a85a 100644
--- a/drivers/mtd/nand/denali.h
+++ b/drivers/mtd/nand/denali.h
@@ -326,7 +326,6 @@ struct denali_nand_info {
int platform;
struct nand_buf buf;
struct device *dev;
- int total_used_banks;
int page;
void __iomem *flash_reg; /* Register Interface */
void __iomem *flash_mem; /* Host Data/Command Interface */
--
2.7.4
The function denali_nand_reset() is called during the driver probe,
and polls the INTR__RST_COMP and INTR__TIME_OUT bits. However,
INTR__RST_COMP is set anyway even if no NAND device is connected to
that bank.
This can be a problem for ONFi devices. The nand_scan_ident()
iterates over maxchips, and calls nand_reset() for each chip.
Now, this driver implements ->setup_data_interface() method, so
nand_setup_data_interface() issues Set Features (0xEF) command to
each chip. This can cause time-out error since denali_nand_reset()
did not check the chip existence. If no chip there, the controller
will wait long for R/B# response, which we know never happens.
(The timeout error is correctly handled in this driver, so the
driver will be successfully probed anyway, but it will take longer
than needed.)
The Reset (0xFF) command also toggles the R/B# pin, and it sets
INTR__INT_ACT bit. The driver should check this bit to see if the
chip has responded, then it can update denali->max_banks.
Signed-off-by: Masahiro Yamada <[email protected]>
---
Changes in v3: None
Changes in v2:
- Newly added
drivers/mtd/nand/denali.c | 52 +++++++++++++++++++++--------------------------
1 file changed, 23 insertions(+), 29 deletions(-)
diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c
index 86b7c75..e4f2699 100644
--- a/drivers/mtd/nand/denali.c
+++ b/drivers/mtd/nand/denali.c
@@ -85,33 +85,6 @@ static void index_addr(struct denali_nand_info *denali,
iowrite32(data, denali->flash_mem + 0x10);
}
-/* Reset the flash controller */
-static uint16_t denali_nand_reset(struct denali_nand_info *denali)
-{
- int i;
-
- for (i = 0; i < denali->max_banks; i++)
- iowrite32(INTR__RST_COMP | INTR__TIME_OUT,
- denali->flash_reg + INTR_STATUS(i));
-
- for (i = 0; i < denali->max_banks; i++) {
- iowrite32(1 << i, denali->flash_reg + DEVICE_RESET);
- while (!(ioread32(denali->flash_reg + INTR_STATUS(i)) &
- (INTR__RST_COMP | INTR__TIME_OUT)))
- cpu_relax();
- if (ioread32(denali->flash_reg + INTR_STATUS(i)) &
- INTR__TIME_OUT)
- dev_dbg(denali->dev,
- "NAND Reset operation timed out on bank %d\n", i);
- }
-
- for (i = 0; i < denali->max_banks; i++)
- iowrite32(INTR__RST_COMP | INTR__TIME_OUT,
- denali->flash_reg + INTR_STATUS(i));
-
- return PASS;
-}
-
/*
* Use the configuration feature register to determine the maximum number of
* banks that the hardware supports.
@@ -999,7 +972,28 @@ static int denali_setup_data_interface(struct mtd_info *mtd,
return 0;
}
-/* Initialization code to bring the device up to a known good state */
+static void denali_reset_banks(struct denali_nand_info *denali)
+{
+ int i;
+
+ denali_clear_irq_all(denali);
+
+ for (i = 0; i < denali->max_banks; i++) {
+ iowrite32(1 << i, denali->flash_reg + DEVICE_RESET);
+ while (!(ioread32(denali->flash_reg + INTR_STATUS(i)) &
+ (INTR__RST_COMP | INTR__TIME_OUT)))
+ cpu_relax();
+ if (!(ioread32(denali->flash_reg + INTR_STATUS(i)) &
+ INTR__INT_ACT))
+ break;
+ }
+
+ dev_dbg(denali->dev, "%d chips connected\n", i);
+ denali->max_banks = i;
+
+ denali_clear_irq_all(denali);
+}
+
static void denali_hw_init(struct denali_nand_info *denali)
{
/*
@@ -1019,7 +1013,7 @@ static void denali_hw_init(struct denali_nand_info *denali)
denali->bbtskipbytes = ioread32(denali->flash_reg +
SPARE_AREA_SKIP_BYTES);
detect_max_banks(denali);
- denali_nand_reset(denali);
+ denali_reset_banks(denali);
iowrite32(0x0F, denali->flash_reg + RB_PIN_ENABLED);
iowrite32(CHIP_EN_DONT_CARE__FLAG,
denali->flash_reg + CHIP_ENABLE_DONT_CARE);
--
2.7.4
Make register field macros more readable especially for comparing
the macros and the register description in the Denali User's Guide.
Signed-off-by: Masahiro Yamada <[email protected]>
---
Changes in v3: None
Changes in v2:
- Newly added
drivers/mtd/nand/denali.h | 244 ++++++++++++++++++++++------------------------
1 file changed, 119 insertions(+), 125 deletions(-)
diff --git a/drivers/mtd/nand/denali.h b/drivers/mtd/nand/denali.h
index 9e2b787..610b013 100644
--- a/drivers/mtd/nand/denali.h
+++ b/drivers/mtd/nand/denali.h
@@ -24,246 +24,240 @@
#include <linux/mtd/nand.h>
#define DEVICE_RESET 0x0
-#define DEVICE_RESET__BANK0 0x0001
-#define DEVICE_RESET__BANK1 0x0002
-#define DEVICE_RESET__BANK2 0x0004
-#define DEVICE_RESET__BANK3 0x0008
+#define DEVICE_RESET__BANK(bank) BIT(bank)
#define TRANSFER_SPARE_REG 0x10
-#define TRANSFER_SPARE_REG__FLAG 0x0001
+#define TRANSFER_SPARE_REG__FLAG BIT(0)
#define LOAD_WAIT_CNT 0x20
-#define LOAD_WAIT_CNT__VALUE 0xffff
+#define LOAD_WAIT_CNT__VALUE GENMASK(15, 0)
#define PROGRAM_WAIT_CNT 0x30
-#define PROGRAM_WAIT_CNT__VALUE 0xffff
+#define PROGRAM_WAIT_CNT__VALUE GENMASK(15, 0)
#define ERASE_WAIT_CNT 0x40
-#define ERASE_WAIT_CNT__VALUE 0xffff
+#define ERASE_WAIT_CNT__VALUE GENMASK(15, 0)
#define INT_MON_CYCCNT 0x50
-#define INT_MON_CYCCNT__VALUE 0xffff
+#define INT_MON_CYCCNT__VALUE GENMASK(15, 0)
#define RB_PIN_ENABLED 0x60
-#define RB_PIN_ENABLED__BANK0 0x0001
-#define RB_PIN_ENABLED__BANK1 0x0002
-#define RB_PIN_ENABLED__BANK2 0x0004
-#define RB_PIN_ENABLED__BANK3 0x0008
+#define RB_PIN_ENABLED__BANK(bank) BIT(bank)
#define MULTIPLANE_OPERATION 0x70
-#define MULTIPLANE_OPERATION__FLAG 0x0001
+#define MULTIPLANE_OPERATION__FLAG BIT(0)
#define MULTIPLANE_READ_ENABLE 0x80
-#define MULTIPLANE_READ_ENABLE__FLAG 0x0001
+#define MULTIPLANE_READ_ENABLE__FLAG BIT(0)
#define COPYBACK_DISABLE 0x90
-#define COPYBACK_DISABLE__FLAG 0x0001
+#define COPYBACK_DISABLE__FLAG BIT(0)
#define CACHE_WRITE_ENABLE 0xa0
-#define CACHE_WRITE_ENABLE__FLAG 0x0001
+#define CACHE_WRITE_ENABLE__FLAG BIT(0)
#define CACHE_READ_ENABLE 0xb0
-#define CACHE_READ_ENABLE__FLAG 0x0001
+#define CACHE_READ_ENABLE__FLAG BIT(0)
#define PREFETCH_MODE 0xc0
-#define PREFETCH_MODE__PREFETCH_EN 0x0001
-#define PREFETCH_MODE__PREFETCH_BURST_LENGTH 0xfff0
+#define PREFETCH_MODE__PREFETCH_EN BIT(0)
+#define PREFETCH_MODE__PREFETCH_BURST_LENGTH GENMASK(15, 4)
#define CHIP_ENABLE_DONT_CARE 0xd0
-#define CHIP_EN_DONT_CARE__FLAG 0x01
+#define CHIP_EN_DONT_CARE__FLAG BIT(0)
#define ECC_ENABLE 0xe0
-#define ECC_ENABLE__FLAG 0x0001
+#define ECC_ENABLE__FLAG BIT(0)
#define GLOBAL_INT_ENABLE 0xf0
-#define GLOBAL_INT_EN_FLAG 0x01
+#define GLOBAL_INT_EN_FLAG BIT(0)
#define WE_2_RE 0x100
-#define WE_2_RE__VALUE 0x003f
+#define WE_2_RE__VALUE GENMASK(5, 0)
#define ADDR_2_DATA 0x110
-#define ADDR_2_DATA__VALUE 0x003f
+#define ADDR_2_DATA__VALUE GENMASK(5, 0)
#define RE_2_WE 0x120
-#define RE_2_WE__VALUE 0x003f
+#define RE_2_WE__VALUE GENMASK(5, 0)
#define ACC_CLKS 0x130
-#define ACC_CLKS__VALUE 0x000f
+#define ACC_CLKS__VALUE GENMASK(3, 0)
#define NUMBER_OF_PLANES 0x140
-#define NUMBER_OF_PLANES__VALUE 0x0007
+#define NUMBER_OF_PLANES__VALUE GENMASK(2, 0)
#define PAGES_PER_BLOCK 0x150
-#define PAGES_PER_BLOCK__VALUE 0xffff
+#define PAGES_PER_BLOCK__VALUE GENMASK(15, 0)
#define DEVICE_WIDTH 0x160
-#define DEVICE_WIDTH__VALUE 0x0003
+#define DEVICE_WIDTH__VALUE GENMASK(1, 0)
#define DEVICE_MAIN_AREA_SIZE 0x170
-#define DEVICE_MAIN_AREA_SIZE__VALUE 0xffff
+#define DEVICE_MAIN_AREA_SIZE__VALUE GENMASK(15, 0)
#define DEVICE_SPARE_AREA_SIZE 0x180
-#define DEVICE_SPARE_AREA_SIZE__VALUE 0xffff
+#define DEVICE_SPARE_AREA_SIZE__VALUE GENMASK(15, 0)
#define TWO_ROW_ADDR_CYCLES 0x190
-#define TWO_ROW_ADDR_CYCLES__FLAG 0x0001
+#define TWO_ROW_ADDR_CYCLES__FLAG BIT(0)
#define MULTIPLANE_ADDR_RESTRICT 0x1a0
-#define MULTIPLANE_ADDR_RESTRICT__FLAG 0x0001
+#define MULTIPLANE_ADDR_RESTRICT__FLAG BIT(0)
#define ECC_CORRECTION 0x1b0
-#define ECC_CORRECTION__VALUE 0x001f
+#define ECC_CORRECTION__VALUE GENMASK(4, 0)
#define READ_MODE 0x1c0
-#define READ_MODE__VALUE 0x000f
+#define READ_MODE__VALUE GENMASK(3, 0)
#define WRITE_MODE 0x1d0
-#define WRITE_MODE__VALUE 0x000f
+#define WRITE_MODE__VALUE GENMASK(3, 0)
#define COPYBACK_MODE 0x1e0
-#define COPYBACK_MODE__VALUE 0x000f
+#define COPYBACK_MODE__VALUE GENMASK(3, 0)
#define RDWR_EN_LO_CNT 0x1f0
-#define RDWR_EN_LO_CNT__VALUE 0x001f
+#define RDWR_EN_LO_CNT__VALUE GENMASK(4, 0)
#define RDWR_EN_HI_CNT 0x200
-#define RDWR_EN_HI_CNT__VALUE 0x001f
+#define RDWR_EN_HI_CNT__VALUE GENMASK(4, 0)
#define MAX_RD_DELAY 0x210
-#define MAX_RD_DELAY__VALUE 0x000f
+#define MAX_RD_DELAY__VALUE GENMASK(3, 0)
#define CS_SETUP_CNT 0x220
-#define CS_SETUP_CNT__VALUE 0x001f
+#define CS_SETUP_CNT__VALUE GENMASK(4, 0)
#define SPARE_AREA_SKIP_BYTES 0x230
-#define SPARE_AREA_SKIP_BYTES__VALUE 0x003f
+#define SPARE_AREA_SKIP_BYTES__VALUE GENMASK(5, 0)
#define SPARE_AREA_MARKER 0x240
-#define SPARE_AREA_MARKER__VALUE 0xffff
+#define SPARE_AREA_MARKER__VALUE GENMASK(15, 0)
#define DEVICES_CONNECTED 0x250
-#define DEVICES_CONNECTED__VALUE 0x0007
+#define DEVICES_CONNECTED__VALUE GENMASK(2, 0)
#define DIE_MASK 0x260
-#define DIE_MASK__VALUE 0x00ff
+#define DIE_MASK__VALUE GENMASK(7, 0)
#define FIRST_BLOCK_OF_NEXT_PLANE 0x270
-#define FIRST_BLOCK_OF_NEXT_PLANE__VALUE 0xffff
+#define FIRST_BLOCK_OF_NEXT_PLANE__VALUE GENMASK(15, 0)
#define WRITE_PROTECT 0x280
-#define WRITE_PROTECT__FLAG 0x0001
+#define WRITE_PROTECT__FLAG BIT(0)
#define RE_2_RE 0x290
-#define RE_2_RE__VALUE 0x003f
+#define RE_2_RE__VALUE GENMASK(5, 0)
#define MANUFACTURER_ID 0x300
-#define MANUFACTURER_ID__VALUE 0x00ff
+#define MANUFACTURER_ID__VALUE GENMASK(7, 0)
#define DEVICE_ID 0x310
-#define DEVICE_ID__VALUE 0x00ff
+#define DEVICE_ID__VALUE GENMASK(7, 0)
#define DEVICE_PARAM_0 0x320
-#define DEVICE_PARAM_0__VALUE 0x00ff
+#define DEVICE_PARAM_0__VALUE GENMASK(7, 0)
#define DEVICE_PARAM_1 0x330
-#define DEVICE_PARAM_1__VALUE 0x00ff
+#define DEVICE_PARAM_1__VALUE GENMASK(7, 0)
#define DEVICE_PARAM_2 0x340
-#define DEVICE_PARAM_2__VALUE 0x00ff
+#define DEVICE_PARAM_2__VALUE GENMASK(7, 0)
#define LOGICAL_PAGE_DATA_SIZE 0x350
-#define LOGICAL_PAGE_DATA_SIZE__VALUE 0xffff
+#define LOGICAL_PAGE_DATA_SIZE__VALUE GENMASK(15, 0)
#define LOGICAL_PAGE_SPARE_SIZE 0x360
-#define LOGICAL_PAGE_SPARE_SIZE__VALUE 0xffff
+#define LOGICAL_PAGE_SPARE_SIZE__VALUE GENMASK(15, 0)
#define REVISION 0x370
-#define REVISION__VALUE 0xffff
+#define REVISION__VALUE GENMASK(15, 0)
#define ONFI_DEVICE_FEATURES 0x380
-#define ONFI_DEVICE_FEATURES__VALUE 0x003f
+#define ONFI_DEVICE_FEATURES__VALUE GENMASK(5, 0)
#define ONFI_OPTIONAL_COMMANDS 0x390
-#define ONFI_OPTIONAL_COMMANDS__VALUE 0x003f
+#define ONFI_OPTIONAL_COMMANDS__VALUE GENMASK(5, 0)
#define ONFI_TIMING_MODE 0x3a0
-#define ONFI_TIMING_MODE__VALUE 0x003f
+#define ONFI_TIMING_MODE__VALUE GENMASK(5, 0)
#define ONFI_PGM_CACHE_TIMING_MODE 0x3b0
-#define ONFI_PGM_CACHE_TIMING_MODE__VALUE 0x003f
+#define ONFI_PGM_CACHE_TIMING_MODE__VALUE GENMASK(5, 0)
#define ONFI_DEVICE_NO_OF_LUNS 0x3c0
-#define ONFI_DEVICE_NO_OF_LUNS__NO_OF_LUNS 0x00ff
-#define ONFI_DEVICE_NO_OF_LUNS__ONFI_DEVICE 0x0100
+#define ONFI_DEVICE_NO_OF_LUNS__NO_OF_LUNS GENMASK(7, 0)
+#define ONFI_DEVICE_NO_OF_LUNS__ONFI_DEVICE BIT(8)
#define ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_L 0x3d0
-#define ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_L__VALUE 0xffff
+#define ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_L__VALUE GENMASK(15, 0)
#define ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_U 0x3e0
-#define ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_U__VALUE 0xffff
-
-#define FEATURES 0x3f0
-#define FEATURES__N_BANKS 0x0003
-#define FEATURES__ECC_MAX_ERR 0x003c
-#define FEATURES__DMA 0x0040
-#define FEATURES__CMD_DMA 0x0080
-#define FEATURES__PARTITION 0x0100
-#define FEATURES__XDMA_SIDEBAND 0x0200
-#define FEATURES__GPREG 0x0400
-#define FEATURES__INDEX_ADDR 0x0800
+#define ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_U__VALUE GENMASK(15, 0)
+
+#define FEATURES 0x3f0
+#define FEATURES__N_BANKS GENMASK(1, 0)
+#define FEATURES__ECC_MAX_ERR GENMASK(5, 2)
+#define FEATURES__DMA BIT(6)
+#define FEATURES__CMD_DMA BIT(7)
+#define FEATURES__PARTITION BIT(8)
+#define FEATURES__XDMA_SIDEBAND BIT(9)
+#define FEATURES__GPREG BIT(10)
+#define FEATURES__INDEX_ADDR BIT(11)
#define TRANSFER_MODE 0x400
-#define TRANSFER_MODE__VALUE 0x0003
+#define TRANSFER_MODE__VALUE GENMASK(1, 0)
-#define INTR_STATUS(__bank) (0x410 + ((__bank) * 0x50))
-#define INTR_EN(__bank) (0x420 + ((__bank) * 0x50))
+#define INTR_STATUS(bank) (0x410 + (bank) * 0x50)
+#define INTR_EN(bank) (0x420 + (bank) * 0x50)
/* bit[1:0] is used differently depending on IP version */
-#define INTR__ECC_UNCOR_ERR 0x0001 /* new IP */
-#define INTR__ECC_TRANSACTION_DONE 0x0001 /* old IP */
-#define INTR__ECC_ERR 0x0002 /* old IP */
-#define INTR__DMA_CMD_COMP 0x0004
-#define INTR__TIME_OUT 0x0008
-#define INTR__PROGRAM_FAIL 0x0010
-#define INTR__ERASE_FAIL 0x0020
-#define INTR__LOAD_COMP 0x0040
-#define INTR__PROGRAM_COMP 0x0080
-#define INTR__ERASE_COMP 0x0100
-#define INTR__PIPE_CPYBCK_CMD_COMP 0x0200
-#define INTR__LOCKED_BLK 0x0400
-#define INTR__UNSUP_CMD 0x0800
-#define INTR__INT_ACT 0x1000
-#define INTR__RST_COMP 0x2000
-#define INTR__PIPE_CMD_ERR 0x4000
-#define INTR__PAGE_XFER_INC 0x8000
-
-#define PAGE_CNT(__bank) (0x430 + ((__bank) * 0x50))
-#define ERR_PAGE_ADDR(__bank) (0x440 + ((__bank) * 0x50))
-#define ERR_BLOCK_ADDR(__bank) (0x450 + ((__bank) * 0x50))
+#define INTR__ECC_UNCOR_ERR BIT(0) /* new IP */
+#define INTR__ECC_TRANSACTION_DONE BIT(0) /* old IP */
+#define INTR__ECC_ERR BIT(1) /* old IP */
+#define INTR__DMA_CMD_COMP BIT(2)
+#define INTR__TIME_OUT BIT(3)
+#define INTR__PROGRAM_FAIL BIT(4)
+#define INTR__ERASE_FAIL BIT(5)
+#define INTR__LOAD_COMP BIT(6)
+#define INTR__PROGRAM_COMP BIT(7)
+#define INTR__ERASE_COMP BIT(8)
+#define INTR__PIPE_CPYBCK_CMD_COMP BIT(9)
+#define INTR__LOCKED_BLK BIT(10)
+#define INTR__UNSUP_CMD BIT(11)
+#define INTR__INT_ACT BIT(12)
+#define INTR__RST_COMP BIT(13)
+#define INTR__PIPE_CMD_ERR BIT(14)
+#define INTR__PAGE_XFER_INC BIT(15)
+
+#define PAGE_CNT(bank) (0x430 + (bank) * 0x50)
+#define ERR_PAGE_ADDR(bank) (0x440 + (bank) * 0x50)
+#define ERR_BLOCK_ADDR(bank) (0x450 + (bank) * 0x50)
#define ECC_THRESHOLD 0x600
-#define ECC_THRESHOLD__VALUE 0x03ff
+#define ECC_THRESHOLD__VALUE GENMASK(9, 0)
#define ECC_ERROR_BLOCK_ADDRESS 0x610
-#define ECC_ERROR_BLOCK_ADDRESS__VALUE 0xffff
+#define ECC_ERROR_BLOCK_ADDRESS__VALUE GENMASK(15, 0)
#define ECC_ERROR_PAGE_ADDRESS 0x620
-#define ECC_ERROR_PAGE_ADDRESS__VALUE 0x0fff
-#define ECC_ERROR_PAGE_ADDRESS__BANK 0xf000
+#define ECC_ERROR_PAGE_ADDRESS__VALUE GENMASK(11, 0)
+#define ECC_ERROR_PAGE_ADDRESS__BANK GENMASK(15, 12)
#define ECC_ERROR_ADDRESS 0x630
-#define ECC_ERROR_ADDRESS__OFFSET 0x0fff
-#define ECC_ERROR_ADDRESS__SECTOR_NR 0xf000
+#define ECC_ERROR_ADDRESS__OFFSET GENMASK(11, 0)
+#define ECC_ERROR_ADDRESS__SECTOR_NR GENMASK(15, 12)
#define ERR_CORRECTION_INFO 0x640
-#define ERR_CORRECTION_INFO__BYTEMASK 0x00ff
-#define ERR_CORRECTION_INFO__DEVICE_NR 0x0f00
-#define ERR_CORRECTION_INFO__ERROR_TYPE 0x4000
-#define ERR_CORRECTION_INFO__LAST_ERR_INFO 0x8000
+#define ERR_CORRECTION_INFO__BYTEMASK GENMASK(7, 0)
+#define ERR_CORRECTION_INFO__DEVICE_NR GENMASK(11, 8)
+#define ERR_CORRECTION_INFO__ERROR_TYPE BIT(14)
+#define ERR_CORRECTION_INFO__LAST_ERR_INFO BIT(15)
#define ECC_COR_INFO(bank) (0x650 + (bank) / 2 * 0x10)
#define ECC_COR_INFO__SHIFT(bank) ((bank) % 2 * 8)
-#define ECC_COR_INFO__MAX_ERRORS 0x007f
-#define ECC_COR_INFO__UNCOR_ERR 0x0080
+#define ECC_COR_INFO__MAX_ERRORS GENMASK(6, 0)
+#define ECC_COR_INFO__UNCOR_ERR BIT(7)
#define CFG_DATA_BLOCK_SIZE 0x6b0
@@ -274,31 +268,31 @@
#define CFG_META_DATA_SIZE 0x6e0
#define DMA_ENABLE 0x700
-#define DMA_ENABLE__FLAG 0x0001
+#define DMA_ENABLE__FLAG BIT(0)
#define IGNORE_ECC_DONE 0x710
-#define IGNORE_ECC_DONE__FLAG 0x0001
+#define IGNORE_ECC_DONE__FLAG BIT(0)
#define DMA_INTR 0x720
#define DMA_INTR_EN 0x730
-#define DMA_INTR__TARGET_ERROR 0x0001
-#define DMA_INTR__DESC_COMP_CHANNEL0 0x0002
-#define DMA_INTR__DESC_COMP_CHANNEL1 0x0004
-#define DMA_INTR__DESC_COMP_CHANNEL2 0x0008
-#define DMA_INTR__DESC_COMP_CHANNEL3 0x0010
-#define DMA_INTR__MEMCOPY_DESC_COMP 0x0020
+#define DMA_INTR__TARGET_ERROR BIT(0)
+#define DMA_INTR__DESC_COMP_CHANNEL0 BIT(1)
+#define DMA_INTR__DESC_COMP_CHANNEL1 BIT(2)
+#define DMA_INTR__DESC_COMP_CHANNEL2 BIT(3)
+#define DMA_INTR__DESC_COMP_CHANNEL3 BIT(4)
+#define DMA_INTR__MEMCOPY_DESC_COMP BIT(5)
#define TARGET_ERR_ADDR_LO 0x740
-#define TARGET_ERR_ADDR_LO__VALUE 0xffff
+#define TARGET_ERR_ADDR_LO__VALUE GENMASK(15, 0)
#define TARGET_ERR_ADDR_HI 0x750
-#define TARGET_ERR_ADDR_HI__VALUE 0xffff
+#define TARGET_ERR_ADDR_HI__VALUE GENMASK(15, 0)
#define CHNL_ACTIVE 0x760
-#define CHNL_ACTIVE__CHANNEL0 0x0001
-#define CHNL_ACTIVE__CHANNEL1 0x0002
-#define CHNL_ACTIVE__CHANNEL2 0x0004
-#define CHNL_ACTIVE__CHANNEL3 0x0008
+#define CHNL_ACTIVE__CHANNEL0 BIT(0)
+#define CHNL_ACTIVE__CHANNEL1 BIT(1)
+#define CHNL_ACTIVE__CHANNEL2 BIT(2)
+#define CHNL_ACTIVE__CHANNEL3 BIT(3)
#define FAIL 1 /*failed flag*/
#define PASS 0 /*success flag*/
--
2.7.4
This driver stores the currently addressed page into denali->page,
which is later read out by helper functions. While I am tackling on
this driver, I often missed to insert "denali->page = page;" where
necessary. This makes page_read/write callbacks to get access to a
wrong page, which is a bug hard to figure out.
Instead, I'd rather pass the page via function argument because the
compiler's prototype checks will help to detect bugs.
For the same reason, propagate dma_addr to the DMA helpers instead
of denali->buf.dma_buf .
Signed-off-by: Masahiro Yamada <[email protected]>
---
Changes in v3: None
Changes in v2:
- Newly added
drivers/mtd/nand/denali.c | 58 ++++++++++++++++++++---------------------------
drivers/mtd/nand/denali.h | 1 -
2 files changed, 24 insertions(+), 35 deletions(-)
diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c
index 9dfed20..00d0e9d 100644
--- a/drivers/mtd/nand/denali.c
+++ b/drivers/mtd/nand/denali.c
@@ -282,7 +282,7 @@ static int denali_dev_ready(struct mtd_info *mtd)
* sends a pipeline command operation to the controller. See the Denali NAND
* controller's user guide for more information (section 4.2.3.6).
*/
-static int denali_send_pipeline_cmd(struct denali_nand_info *denali,
+static int denali_send_pipeline_cmd(struct denali_nand_info *denali, int page,
bool ecc_en, bool transfer_spare,
int access_type, int op)
{
@@ -293,7 +293,7 @@ static int denali_send_pipeline_cmd(struct denali_nand_info *denali,
denali_reset_irq(denali);
- addr = BANK(denali->flash_bank) | denali->page;
+ addr = BANK(denali->flash_bank) | page;
if (op == DENALI_WRITE && access_type != SPARE_ACCESS) {
cmd = MODE_01 | addr;
@@ -366,9 +366,7 @@ static int write_oob_data(struct mtd_info *mtd, uint8_t *buf, int page)
uint32_t irq_mask = INTR__PROGRAM_COMP | INTR__PROGRAM_FAIL;
int status = 0;
- denali->page = page;
-
- if (denali_send_pipeline_cmd(denali, false, false, SPARE_ACCESS,
+ if (denali_send_pipeline_cmd(denali, page, false, false, SPARE_ACCESS,
DENALI_WRITE) == PASS) {
write_data_to_flash_mem(denali, buf, mtd->oobsize);
@@ -393,9 +391,7 @@ static void read_oob_data(struct mtd_info *mtd, uint8_t *buf, int page)
uint32_t irq_mask = INTR__LOAD_COMP;
uint32_t irq_status, addr, cmd;
- denali->page = page;
-
- if (denali_send_pipeline_cmd(denali, false, true, SPARE_ACCESS,
+ if (denali_send_pipeline_cmd(denali, page, false, true, SPARE_ACCESS,
DENALI_READ) == PASS) {
read_data_from_flash_mem(denali, buf, mtd->oobsize);
@@ -407,8 +403,7 @@ static void read_oob_data(struct mtd_info *mtd, uint8_t *buf, int page)
irq_status = denali_wait_for_irq(denali, irq_mask);
if (!(irq_status & INTR__LOAD_COMP))
- dev_err(denali->dev, "page on OOB timeout %d\n",
- denali->page);
+ dev_err(denali->dev, "page on OOB timeout %d\n", page);
/*
* We set the device back to MAIN_ACCESS here as I observed
@@ -417,7 +412,7 @@ static void read_oob_data(struct mtd_info *mtd, uint8_t *buf, int page)
* is reliable (according to the MTD test infrastructure)
* if you are in MAIN_ACCESS.
*/
- addr = BANK(denali->flash_bank) | denali->page;
+ addr = BANK(denali->flash_bank) | page;
cmd = MODE_10 | addr;
index_addr(denali, cmd, MAIN_ACCESS);
}
@@ -582,13 +577,13 @@ static void denali_enable_dma(struct denali_nand_info *denali, bool en)
ioread32(denali->flash_reg + DMA_ENABLE);
}
-static void denali_setup_dma64(struct denali_nand_info *denali, int op)
+static void denali_setup_dma64(struct denali_nand_info *denali,
+ dma_addr_t dma_addr, int page, int op)
{
uint32_t mode;
const int page_count = 1;
- uint64_t addr = denali->buf.dma_buf;
- mode = MODE_10 | BANK(denali->flash_bank) | denali->page;
+ mode = MODE_10 | BANK(denali->flash_bank) | page;
/* DMA is a three step process */
@@ -599,41 +594,42 @@ static void denali_setup_dma64(struct denali_nand_info *denali, int op)
index_addr(denali, mode, 0x01002000 | (64 << 16) | op | page_count);
/* 2. set memory low address */
- index_addr(denali, mode, addr);
+ index_addr(denali, mode, dma_addr);
/* 3. set memory high address */
- index_addr(denali, mode, addr >> 32);
+ index_addr(denali, mode, (uint64_t)dma_addr >> 32);
}
-static void denali_setup_dma32(struct denali_nand_info *denali, int op)
+static void denali_setup_dma32(struct denali_nand_info *denali,
+ dma_addr_t dma_addr, int page, int op)
{
uint32_t mode;
const int page_count = 1;
- uint32_t addr = denali->buf.dma_buf;
mode = MODE_10 | BANK(denali->flash_bank);
/* DMA is a four step process */
/* 1. setup transfer type and # of pages */
- index_addr(denali, mode | denali->page, 0x2000 | op | page_count);
+ index_addr(denali, mode | page, 0x2000 | op | page_count);
/* 2. set memory high address bits 23:8 */
- index_addr(denali, mode | ((addr >> 16) << 8), 0x2200);
+ index_addr(denali, mode | ((dma_addr >> 16) << 8), 0x2200);
/* 3. set memory low address bits 23:8 */
- index_addr(denali, mode | ((addr & 0xffff) << 8), 0x2300);
+ index_addr(denali, mode | ((dma_addr & 0xffff) << 8), 0x2300);
/* 4. interrupt when complete, burst len = 64 bytes */
index_addr(denali, mode | 0x14000, 0x2400);
}
-static void denali_setup_dma(struct denali_nand_info *denali, int op)
+static void denali_setup_dma(struct denali_nand_info *denali,
+ dma_addr_t dma_addr, int page, int op)
{
if (denali->caps & DENALI_CAP_DMA_64BIT)
- denali_setup_dma64(denali, op);
+ denali_setup_dma64(denali, dma_addr, page, op);
else
- denali_setup_dma32(denali, op);
+ denali_setup_dma32(denali, dma_addr, page, op);
}
/*
@@ -650,8 +646,6 @@ static int write_page(struct mtd_info *mtd, struct nand_chip *chip,
uint32_t irq_mask = INTR__DMA_CMD_COMP | INTR__PROGRAM_FAIL;
int ret = 0;
- denali->page = page;
-
/*
* if it is a raw xfer, we want to disable ecc and send the spare area.
* !raw_xfer - enable ecc
@@ -674,7 +668,7 @@ static int write_page(struct mtd_info *mtd, struct nand_chip *chip,
denali_reset_irq(denali);
denali_enable_dma(denali, true);
- denali_setup_dma(denali, DENALI_WRITE);
+ denali_setup_dma(denali, addr, page, DENALI_WRITE);
/* wait for operation to complete */
irq_status = denali_wait_for_irq(denali, irq_mask);
@@ -750,15 +744,13 @@ static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip,
unsigned long uncor_ecc_flags = 0;
int stat = 0;
- denali->page = page;
-
setup_ecc_for_xfer(denali, true, false);
denali_enable_dma(denali, true);
dma_sync_single_for_device(denali->dev, addr, size, DMA_FROM_DEVICE);
denali_reset_irq(denali);
- denali_setup_dma(denali, DENALI_READ);
+ denali_setup_dma(denali, addr, page, DENALI_READ);
/* wait for operation to complete */
irq_status = denali_wait_for_irq(denali, irq_mask);
@@ -777,7 +769,7 @@ static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip,
return stat;
if (uncor_ecc_flags) {
- read_oob_data(mtd, chip->oob_poi, denali->page);
+ read_oob_data(mtd, chip->oob_poi, page);
stat = denali_check_erased_page(mtd, chip, buf,
uncor_ecc_flags, stat);
@@ -795,15 +787,13 @@ static int denali_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
uint32_t irq_mask = INTR__DMA_CMD_COMP;
uint32_t irq_status;
- denali->page = page;
-
setup_ecc_for_xfer(denali, false, true);
denali_enable_dma(denali, true);
dma_sync_single_for_device(denali->dev, addr, size, DMA_FROM_DEVICE);
denali_reset_irq(denali);
- denali_setup_dma(denali, DENALI_READ);
+ denali_setup_dma(denali, addr, page, DENALI_READ);
/* wait for operation to complete */
irq_status = denali_wait_for_irq(denali, irq_mask);
diff --git a/drivers/mtd/nand/denali.h b/drivers/mtd/nand/denali.h
index 7d64167..5f917a0 100644
--- a/drivers/mtd/nand/denali.h
+++ b/drivers/mtd/nand/denali.h
@@ -317,7 +317,6 @@ struct denali_nand_info {
int flash_bank; /* currently selected chip */
struct nand_buf buf;
struct device *dev;
- int page;
void __iomem *flash_reg; /* Register Interface */
void __iomem *flash_mem; /* Host Data/Command Interface */
--
2.7.4
The NAND_CMD_SET_FEATURES support is missing from denali_cmdfunc().
This is needed for nand_onfi_set_features().
Besides, we see /* TODO: Read OOB data */ comment line.
It would be possible to add more commands along with the current
implementation, but having ->cmd_ctrl() seems a better approach from
the discussion with Boris [1].
Rely on the default ->cmdfunc() from the framework and implement the
driver's own ->cmd_ctrl().
Also add ->write_byte(), which is needed for write direction commands.
[1] https://lkml.org/lkml/2017/3/15/97
Signed-off-by: Masahiro Yamada <[email protected]>
---
Changes in v3: None
Changes in v2:
- Newly added
drivers/mtd/nand/denali.c | 104 +++++++++++++++++++++++-----------------------
1 file changed, 52 insertions(+), 52 deletions(-)
diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c
index 0e5e490..86b7c75 100644
--- a/drivers/mtd/nand/denali.c
+++ b/drivers/mtd/nand/denali.c
@@ -230,20 +230,16 @@ static uint32_t denali_wait_for_irq(struct denali_nand_info *denali,
return denali->irq_status;
}
-/* resets a specific device connected to the core */
-static void reset_bank(struct denali_nand_info *denali)
+static uint32_t denali_check_irq(struct denali_nand_info *denali)
{
+ unsigned long flags;
uint32_t irq_status;
- denali_reset_irq(denali);
-
- iowrite32(1 << denali->flash_bank, denali->flash_reg + DEVICE_RESET);
-
- irq_status = denali_wait_for_irq(denali,
- INTR__RST_COMP | INTR__TIME_OUT);
+ spin_lock_irqsave(&denali->irq_lock, flags);
+ irq_status = denali->irq_status;
+ spin_unlock_irqrestore(&denali->irq_lock, flags);
- if (!(irq_status & INTR__RST_COMP))
- dev_err(denali->dev, "reset bank failed.\n");
+ return irq_status;
}
/*
@@ -273,6 +269,42 @@ static uint8_t denali_read_byte(struct mtd_info *mtd)
return ioread32(denali->flash_mem + 0x10);
}
+static void denali_write_byte(struct mtd_info *mtd, uint8_t byte)
+{
+ struct denali_nand_info *denali = mtd_to_denali(mtd);
+
+ index_addr(denali, MODE_11 | BANK(denali->flash_bank) | 2, byte);
+}
+
+static void denali_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl)
+{
+ struct denali_nand_info *denali = mtd_to_denali(mtd);
+ uint32_t type;
+
+ if (ctrl & NAND_CLE)
+ type = 0;
+ else if (ctrl & NAND_ALE)
+ type = 1;
+ else
+ return;
+
+ /*
+ * Some commands are followed by chip->dev_ready or chip->waitfunc.
+ * irq_status must be cleared here to catch the R/B# interrupt later.
+ */
+ if (ctrl & NAND_CTRL_CHANGE)
+ denali_reset_irq(denali);
+
+ index_addr(denali, MODE_11 | BANK(denali->flash_bank) | type, dat);
+}
+
+static int denali_dev_ready(struct mtd_info *mtd)
+{
+ struct denali_nand_info *denali = mtd_to_denali(mtd);
+
+ return !!(denali_check_irq(denali) & INTR__INT_ACT);
+}
+
/*
* sends a pipeline command operation to the controller. See the Denali NAND
* controller's user guide for more information (section 4.2.3.6).
@@ -824,7 +856,13 @@ static void denali_select_chip(struct mtd_info *mtd, int chip)
static int denali_waitfunc(struct mtd_info *mtd, struct nand_chip *chip)
{
- return 0;
+ struct denali_nand_info *denali = mtd_to_denali(mtd);
+ uint32_t irq_status;
+
+ /* R/B# pin transitioned from low to high? */
+ irq_status = denali_wait_for_irq(denali, INTR__INT_ACT);
+
+ return irq_status & INTR__INT_ACT ? 0 : NAND_STATUS_FAIL;
}
static int denali_erase(struct mtd_info *mtd, int page)
@@ -845,46 +883,6 @@ static int denali_erase(struct mtd_info *mtd, int page)
return irq_status & INTR__ERASE_COMP ? 0 : NAND_STATUS_FAIL;
}
-static void denali_cmdfunc(struct mtd_info *mtd, unsigned int cmd, int col,
- int page)
-{
- struct denali_nand_info *denali = mtd_to_denali(mtd);
- uint32_t addr, irq_status;
- int wait_ready = 0;
-
- switch (cmd) {
- case NAND_CMD_PARAM:
- wait_ready = 1;
- break;
- case NAND_CMD_STATUS:
- case NAND_CMD_READID:
- break;
- case NAND_CMD_RESET:
- reset_bank(denali);
- break;
- case NAND_CMD_READOOB:
- /* TODO: Read OOB data */
- return;
- default:
- pr_err(": unsupported command received 0x%x\n", cmd);
- return;
- }
-
- denali_reset_irq(denali);
-
- addr = MODE_11 | BANK(denali->flash_bank);
- index_addr(denali, addr | 0, cmd);
- if (col != -1)
- index_addr(denali, addr | 1, col);
-
- if (!wait_ready)
- return;
-
- irq_status = denali_wait_for_irq(denali, INTR__INT_ACT);
- if (!(irq_status & INTR__INT_ACT))
- dev_err(denali->dev, "failed to issue command 0x%x\n", cmd);
-}
-
#define DIV_ROUND_DOWN_ULL(ll, d) \
({ unsigned long long _tmp = (ll); do_div(_tmp, d); _tmp; })
@@ -1230,8 +1228,10 @@ int denali_init(struct denali_nand_info *denali)
/* register the driver with the NAND core subsystem */
chip->select_chip = denali_select_chip;
- chip->cmdfunc = denali_cmdfunc;
chip->read_byte = denali_read_byte;
+ chip->write_byte = denali_write_byte;
+ chip->cmd_ctrl = denali_cmd_ctrl;
+ chip->dev_ready = denali_dev_ready;
chip->waitfunc = denali_waitfunc;
/* clk rate info is needed for setup_data_interface */
if (denali->clk_x_rate)
--
2.7.4
It is not a good idea to re-use macros that represent a specific
register bit field for the transfer direction.
It is true that bit 8 indicates the direction for the MAP10 pipeline
operation and the data DMA operation, but this is not valid across
the IP.
Use a simple flag (write: 1, read: 0) for the direction.
Signed-off-by: Masahiro Yamada <[email protected]>
---
Changes in v3: None
Changes in v2:
- Newly added
drivers/mtd/nand/denali.c | 36 +++++++++++++++++-------------------
1 file changed, 17 insertions(+), 19 deletions(-)
diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c
index 5feeaf5..6843cfc 100644
--- a/drivers/mtd/nand/denali.c
+++ b/drivers/mtd/nand/denali.c
@@ -53,9 +53,6 @@ static inline struct denali_nand_info *mtd_to_denali(struct mtd_info *mtd)
#define MAIN_ACCESS 0x42
#define MAIN_SPARE_ACCESS 0x43
-#define DENALI_READ 0
-#define DENALI_WRITE 0x100
-
#define DENALI_NR_BANKS 4
/*
@@ -284,7 +281,7 @@ static int denali_dev_ready(struct mtd_info *mtd)
*/
static int denali_send_pipeline_cmd(struct denali_nand_info *denali, int page,
bool ecc_en, bool transfer_spare,
- int access_type, int op)
+ int access_type, int write)
{
int status = PASS;
uint32_t addr, cmd;
@@ -295,17 +292,17 @@ static int denali_send_pipeline_cmd(struct denali_nand_info *denali, int page,
addr = BANK(denali->flash_bank) | page;
- if (op == DENALI_WRITE && access_type != SPARE_ACCESS) {
+ if (write && access_type != SPARE_ACCESS) {
cmd = MODE_01 | addr;
iowrite32(cmd, denali->flash_mem);
- } else if (op == DENALI_WRITE && access_type == SPARE_ACCESS) {
+ } else if (write && access_type == SPARE_ACCESS) {
/* read spare area */
cmd = MODE_10 | addr;
index_addr(denali, cmd, access_type);
cmd = MODE_01 | addr;
iowrite32(cmd, denali->flash_mem);
- } else if (op == DENALI_READ) {
+ } else {
/* setup page read request for access type */
cmd = MODE_10 | addr;
index_addr(denali, cmd, access_type);
@@ -367,7 +364,7 @@ static int write_oob_data(struct mtd_info *mtd, uint8_t *buf, int page)
int status = 0;
if (denali_send_pipeline_cmd(denali, page, false, false, SPARE_ACCESS,
- DENALI_WRITE) == PASS) {
+ 1) == PASS) {
write_data_to_flash_mem(denali, buf, mtd->oobsize);
/* wait for operation to complete */
@@ -392,7 +389,7 @@ static void read_oob_data(struct mtd_info *mtd, uint8_t *buf, int page)
uint32_t irq_status, addr, cmd;
if (denali_send_pipeline_cmd(denali, page, false, true, SPARE_ACCESS,
- DENALI_READ) == PASS) {
+ 0) == PASS) {
read_data_from_flash_mem(denali, buf, mtd->oobsize);
/*
@@ -578,7 +575,7 @@ static void denali_enable_dma(struct denali_nand_info *denali, bool en)
}
static void denali_setup_dma64(struct denali_nand_info *denali,
- dma_addr_t dma_addr, int page, int op)
+ dma_addr_t dma_addr, int page, int write)
{
uint32_t mode;
const int page_count = 1;
@@ -591,7 +588,8 @@ static void denali_setup_dma64(struct denali_nand_info *denali,
* 1. setup transfer type, interrupt when complete,
* burst len = 64 bytes, the number of pages
*/
- index_addr(denali, mode, 0x01002000 | (64 << 16) | op | page_count);
+ index_addr(denali, mode,
+ 0x01002000 | (64 << 16) | (write << 8) | page_count);
/* 2. set memory low address */
index_addr(denali, mode, dma_addr);
@@ -601,7 +599,7 @@ static void denali_setup_dma64(struct denali_nand_info *denali,
}
static void denali_setup_dma32(struct denali_nand_info *denali,
- dma_addr_t dma_addr, int page, int op)
+ dma_addr_t dma_addr, int page, int write)
{
uint32_t mode;
const int page_count = 1;
@@ -611,7 +609,7 @@ static void denali_setup_dma32(struct denali_nand_info *denali,
/* DMA is a four step process */
/* 1. setup transfer type and # of pages */
- index_addr(denali, mode | page, 0x2000 | op | page_count);
+ index_addr(denali, mode | page, 0x2000 | (write << 8) | page_count);
/* 2. set memory high address bits 23:8 */
index_addr(denali, mode | ((dma_addr >> 16) << 8), 0x2200);
@@ -624,12 +622,12 @@ static void denali_setup_dma32(struct denali_nand_info *denali,
}
static void denali_setup_dma(struct denali_nand_info *denali,
- dma_addr_t dma_addr, int page, int op)
+ dma_addr_t dma_addr, int page, int write)
{
if (denali->caps & DENALI_CAP_DMA_64BIT)
- denali_setup_dma64(denali, dma_addr, page, op);
+ denali_setup_dma64(denali, dma_addr, page, write);
else
- denali_setup_dma32(denali, dma_addr, page, op);
+ denali_setup_dma32(denali, dma_addr, page, write);
}
/*
@@ -668,7 +666,7 @@ static int write_page(struct mtd_info *mtd, struct nand_chip *chip,
denali_reset_irq(denali);
denali_enable_dma(denali, true);
- denali_setup_dma(denali, addr, page, DENALI_WRITE);
+ denali_setup_dma(denali, addr, page, 1);
/* wait for operation to complete */
irq_status = denali_wait_for_irq(denali, irq_mask);
@@ -750,7 +748,7 @@ static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip,
dma_sync_single_for_device(denali->dev, addr, size, DMA_FROM_DEVICE);
denali_reset_irq(denali);
- denali_setup_dma(denali, addr, page, DENALI_READ);
+ denali_setup_dma(denali, addr, page, 0);
/* wait for operation to complete */
irq_status = denali_wait_for_irq(denali, irq_mask);
@@ -793,7 +791,7 @@ static int denali_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
dma_sync_single_for_device(denali->dev, addr, size, DMA_FROM_DEVICE);
denali_reset_irq(denali);
- denali_setup_dma(denali, addr, page, DENALI_READ);
+ denali_setup_dma(denali, addr, page, 0);
/* wait for operation to complete */
irq_status = denali_wait_for_irq(denali, irq_mask);
--
2.7.4
Commit 28309572aac4 ("mtd: name the mtd device with an optional
label property") allow us to identify a chip in a user-friendly way.
If nand_set_flash_node() picks up the "label" from DT, let's respect
it. Otherwise, let it fallback to the current name "denali-nand".
Signed-off-by: Masahiro Yamada <[email protected]>
Suggested-by: Boris Brezillon <[email protected]>
---
Changes in v3:
- Replaced "mtd: nand: denali: do not set mtd->name" in v2
Changes in v2: None
drivers/mtd/nand/denali.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c
index 4ca75d3..4e6d03d 100644
--- a/drivers/mtd/nand/denali.c
+++ b/drivers/mtd/nand/denali.c
@@ -1462,8 +1462,10 @@ int denali_init(struct denali_nand_info *denali)
/* now that our ISR is registered, we can enable interrupts */
denali_set_intr_modes(denali, true);
- mtd->name = "denali-nand";
nand_set_flash_node(chip, denali->dev->of_node);
+ /* Fallback to the default name if DT did not give "label" property */
+ if (!mtd->name)
+ mtd->name = "denali-nand";
/* register the driver with the NAND core subsystem */
chip->select_chip = denali_select_chip;
--
2.7.4
The current bank reset implementation polls the INTR_STATUS register
until interested bits are set. This is not good because:
- it simply wastes time-slice of the thread which the driver module
is loaded from.
- The while() loop may continue eternally if no bit is set, for
example, due to the controller problem. The denali_wait_for_irq()
uses wait_for_completion_timeout(), which is safer.
We can use interrupt by moving the denali_reset_bank() call below
the interrupt setup.
Signed-off-by: Masahiro Yamada <[email protected]>
---
Changes in v3: None
Changes in v2:
- Newly added
drivers/mtd/nand/denali.c | 28 ++++++++++++++--------------
1 file changed, 14 insertions(+), 14 deletions(-)
diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c
index e4f2699..9dfed20 100644
--- a/drivers/mtd/nand/denali.c
+++ b/drivers/mtd/nand/denali.c
@@ -974,24 +974,25 @@ static int denali_setup_data_interface(struct mtd_info *mtd,
static void denali_reset_banks(struct denali_nand_info *denali)
{
+ u32 irq_status;
int i;
- denali_clear_irq_all(denali);
-
for (i = 0; i < denali->max_banks; i++) {
- iowrite32(1 << i, denali->flash_reg + DEVICE_RESET);
- while (!(ioread32(denali->flash_reg + INTR_STATUS(i)) &
- (INTR__RST_COMP | INTR__TIME_OUT)))
- cpu_relax();
- if (!(ioread32(denali->flash_reg + INTR_STATUS(i)) &
- INTR__INT_ACT))
+ denali->flash_bank = i;
+
+ denali_reset_irq(denali);
+
+ iowrite32(DEVICE_RESET__BANK(i),
+ denali->flash_reg + DEVICE_RESET);
+
+ irq_status = denali_wait_for_irq(denali,
+ INTR__RST_COMP | INTR__INT_ACT | INTR__TIME_OUT);
+ if (!(irq_status & INTR__INT_ACT))
break;
}
dev_dbg(denali->dev, "%d chips connected\n", i);
denali->max_banks = i;
-
- denali_clear_irq_all(denali);
}
static void denali_hw_init(struct denali_nand_info *denali)
@@ -1013,7 +1014,6 @@ static void denali_hw_init(struct denali_nand_info *denali)
denali->bbtskipbytes = ioread32(denali->flash_reg +
SPARE_AREA_SKIP_BYTES);
detect_max_banks(denali);
- denali_reset_banks(denali);
iowrite32(0x0F, denali->flash_reg + RB_PIN_ENABLED);
iowrite32(CHIP_EN_DONT_CARE__FLAG,
denali->flash_reg + CHIP_ENABLE_DONT_CARE);
@@ -1137,9 +1137,6 @@ static void denali_drv_init(struct denali_nand_info *denali)
* element that might be access shared data (interrupt status)
*/
spin_lock_init(&denali->irq_lock);
-
- /* indicate that MTD has not selected a valid bank yet */
- denali->flash_bank = CHIP_SELECT_INVALID;
}
static int denali_multidev_fixup(struct denali_nand_info *denali)
@@ -1214,6 +1211,9 @@ int denali_init(struct denali_nand_info *denali)
}
denali_enable_irq(denali);
+ denali_reset_banks(denali);
+
+ denali->flash_bank = CHIP_SELECT_INVALID;
nand_set_flash_node(chip, denali->dev->of_node);
/* Fallback to the default name if DT did not give "label" property */
--
2.7.4
The timing parameter handling in this driver is bad in multiple ways.
[1] The ONFi Timing Mode is specified by the module parameter
"onfi_timing_mode". Users are free to set any Timing Mode that
may not be supported on the device.
[2] The function nand_onfi_timing_set() holds many parameters in it.
It is duplicating efforts of drivers/mtd/nand/nand_timings.c
[3] The function get_onfi_nand_para() detects the supported Timing
Modes. The driver need/should not have its own way because the
ONFi parameters are read by nand_flash_detect_onfi() and the
supported Timing Modes are detected by nand_init_data_interface().
[4] Intel specific parameters such as CLK_X=5, CLK_MULTI=4, etc. are
hard-coded in the driver, so this function is not working for
other platforms.
[5] It is weird for a driver to take care of a particular device
(Samsung K9WAG08U1A) by get_samsung_nand_para().
Now, the core framework provides a common logic to handle timings.
Implement .setup_data_interface().
While I am working on this, I found even more issues in the current
code, so fixed the following as well:
- In recent IP versions, WE_2_RE and TWHR2 share the same register.
Likewise for ADDR_2_DATA and TCWAW, CS_SETUP_CNT and TWB. When
updating one, the other must be masked. Otherwise, the other will
be set to 0, then timing settings will be broken.
- The recent IP release expanded the ADDR_2_DATA to 7-bit wide.
This register is related to tADL. As commit 74a332e78e8f ("mtd:
nand: timings: Fix tADL_min for ONFI 4.0 chips") addressed, the
ONFi 4.0 increased the minimum of tADL to 400 nsec. This may not
fit in the 6-bit ADDR_2_DATA in older versions. Check the IP
revision and handle this correctly, otherwise the register value
would wrap around.
Signed-off-by: Masahiro Yamada <[email protected]>
---
Changes in v3: None
Changes in v2:
- Newly added
drivers/mtd/nand/denali.c | 351 +++++++++++++++---------------------------
drivers/mtd/nand/denali.h | 25 ++-
drivers/mtd/nand/denali_dt.c | 3 +-
drivers/mtd/nand/denali_pci.c | 6 +-
4 files changed, 142 insertions(+), 243 deletions(-)
diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c
index 0c93a07..ea566ea 100644
--- a/drivers/mtd/nand/denali.c
+++ b/drivers/mtd/nand/denali.c
@@ -28,17 +28,6 @@
MODULE_LICENSE("GPL");
-/*
- * We define a module parameter that allows the user to override
- * the hardware and decide what timing mode should be used.
- */
-#define NAND_DEFAULT_TIMINGS -1
-
-static int onfi_timing_mode = NAND_DEFAULT_TIMINGS;
-module_param(onfi_timing_mode, int, S_IRUGO);
-MODULE_PARM_DESC(onfi_timing_mode,
- "Overrides default ONFI setting. -1 indicates use default timings");
-
#define DENALI_NAND_NAME "denali-nand"
/*
@@ -63,12 +52,6 @@ MODULE_PARM_DESC(onfi_timing_mode,
#define CHIP_SELECT_INVALID -1
/*
- * This macro divides two integers and rounds fractional values up
- * to the nearest integer value.
- */
-#define CEIL_DIV(X, Y) (((X)%(Y)) ? ((X)/(Y)+1) : ((X)/(Y)))
-
-/*
* this macro allows us to convert from an MTD structure to our own
* device context (denali) structure.
*/
@@ -103,6 +86,14 @@ static void denali_irq_enable(struct denali_nand_info *denali,
static uint32_t read_interrupt_status(struct denali_nand_info *denali);
/*
+ * The bus interface clock, clk_x, is phase aligned with the core clock. The
+ * clk_x is an integral multiple N of the core clk. The value N is configured
+ * at IP delivery time, and its available value is 4, 5, or 6. We need to align
+ * to the largest value to make it work with any possible configuration.
+ */
+#define DENALI_CLK_X_MULT 6
+
+/*
* Certain operations for the denali NAND controller use an indexed mode to
* read/write data. The operation is performed by writing the address value
* of the command to the device memory followed by the data. This function
@@ -196,148 +187,6 @@ static uint16_t denali_nand_reset(struct denali_nand_info *denali)
}
/*
- * this routine calculates the ONFI timing values for a given mode and
- * programs the clocking register accordingly. The mode is determined by
- * the get_onfi_nand_para routine.
- */
-static void nand_onfi_timing_set(struct denali_nand_info *denali,
- uint16_t mode)
-{
- uint16_t Trea[6] = {40, 30, 25, 20, 20, 16};
- uint16_t Trp[6] = {50, 25, 17, 15, 12, 10};
- uint16_t Treh[6] = {30, 15, 15, 10, 10, 7};
- uint16_t Trc[6] = {100, 50, 35, 30, 25, 20};
- uint16_t Trhoh[6] = {0, 15, 15, 15, 15, 15};
- uint16_t Trloh[6] = {0, 0, 0, 0, 5, 5};
- uint16_t Tcea[6] = {100, 45, 30, 25, 25, 25};
- uint16_t Tadl[6] = {200, 100, 100, 100, 70, 70};
- uint16_t Trhw[6] = {200, 100, 100, 100, 100, 100};
- uint16_t Trhz[6] = {200, 100, 100, 100, 100, 100};
- uint16_t Twhr[6] = {120, 80, 80, 60, 60, 60};
- uint16_t Tcs[6] = {70, 35, 25, 25, 20, 15};
-
- uint16_t data_invalid_rhoh, data_invalid_rloh, data_invalid;
- uint16_t dv_window = 0;
- uint16_t en_lo, en_hi;
- uint16_t acc_clks;
- uint16_t addr_2_data, re_2_we, re_2_re, we_2_re, cs_cnt;
-
- en_lo = CEIL_DIV(Trp[mode], CLK_X);
- en_hi = CEIL_DIV(Treh[mode], CLK_X);
-#if ONFI_BLOOM_TIME
- if ((en_hi * CLK_X) < (Treh[mode] + 2))
- en_hi++;
-#endif
-
- if ((en_lo + en_hi) * CLK_X < Trc[mode])
- en_lo += CEIL_DIV((Trc[mode] - (en_lo + en_hi) * CLK_X), CLK_X);
-
- if ((en_lo + en_hi) < CLK_MULTI)
- en_lo += CLK_MULTI - en_lo - en_hi;
-
- while (dv_window < 8) {
- data_invalid_rhoh = en_lo * CLK_X + Trhoh[mode];
-
- data_invalid_rloh = (en_lo + en_hi) * CLK_X + Trloh[mode];
-
- data_invalid = data_invalid_rhoh < data_invalid_rloh ?
- data_invalid_rhoh : data_invalid_rloh;
-
- dv_window = data_invalid - Trea[mode];
-
- if (dv_window < 8)
- en_lo++;
- }
-
- acc_clks = CEIL_DIV(Trea[mode], CLK_X);
-
- while (acc_clks * CLK_X - Trea[mode] < 3)
- acc_clks++;
-
- if (data_invalid - acc_clks * CLK_X < 2)
- dev_warn(denali->dev, "%s, Line %d: Warning!\n",
- __FILE__, __LINE__);
-
- addr_2_data = CEIL_DIV(Tadl[mode], CLK_X);
- re_2_we = CEIL_DIV(Trhw[mode], CLK_X);
- re_2_re = CEIL_DIV(Trhz[mode], CLK_X);
- we_2_re = CEIL_DIV(Twhr[mode], CLK_X);
- cs_cnt = CEIL_DIV((Tcs[mode] - Trp[mode]), CLK_X);
- if (cs_cnt == 0)
- cs_cnt = 1;
-
- if (Tcea[mode]) {
- while (cs_cnt * CLK_X + Trea[mode] < Tcea[mode])
- cs_cnt++;
- }
-
-#if MODE5_WORKAROUND
- if (mode == 5)
- acc_clks = 5;
-#endif
-
- /* Sighting 3462430: Temporary hack for MT29F128G08CJABAWP:B */
- if (ioread32(denali->flash_reg + MANUFACTURER_ID) == 0 &&
- ioread32(denali->flash_reg + DEVICE_ID) == 0x88)
- acc_clks = 6;
-
- iowrite32(acc_clks, denali->flash_reg + ACC_CLKS);
- iowrite32(re_2_we, denali->flash_reg + RE_2_WE);
- iowrite32(re_2_re, denali->flash_reg + RE_2_RE);
- iowrite32(we_2_re, denali->flash_reg + WE_2_RE);
- iowrite32(addr_2_data, denali->flash_reg + ADDR_2_DATA);
- iowrite32(en_lo, denali->flash_reg + RDWR_EN_LO_CNT);
- iowrite32(en_hi, denali->flash_reg + RDWR_EN_HI_CNT);
- iowrite32(cs_cnt, denali->flash_reg + CS_SETUP_CNT);
-}
-
-/* queries the NAND device to see what ONFI modes it supports. */
-static uint16_t get_onfi_nand_para(struct denali_nand_info *denali)
-{
- int i;
-
- /*
- * we needn't to do a reset here because driver has already
- * reset all the banks before
- */
- if (!(ioread32(denali->flash_reg + ONFI_TIMING_MODE) &
- ONFI_TIMING_MODE__VALUE))
- return FAIL;
-
- for (i = 5; i > 0; i--) {
- if (ioread32(denali->flash_reg + ONFI_TIMING_MODE) &
- (0x01 << i))
- break;
- }
-
- nand_onfi_timing_set(denali, i);
-
- /*
- * By now, all the ONFI devices we know support the page cache
- * rw feature. So here we enable the pipeline_rw_ahead feature
- */
- /* iowrite32(1, denali->flash_reg + CACHE_WRITE_ENABLE); */
- /* iowrite32(1, denali->flash_reg + CACHE_READ_ENABLE); */
-
- return PASS;
-}
-
-static void get_samsung_nand_para(struct denali_nand_info *denali,
- uint8_t device_id)
-{
- if (device_id == 0xd3) { /* Samsung K9WAG08U1A */
- /* Set timing register values according to datasheet */
- iowrite32(5, denali->flash_reg + ACC_CLKS);
- iowrite32(20, denali->flash_reg + RE_2_WE);
- iowrite32(12, denali->flash_reg + WE_2_RE);
- iowrite32(14, denali->flash_reg + ADDR_2_DATA);
- iowrite32(3, denali->flash_reg + RDWR_EN_LO_CNT);
- iowrite32(2, denali->flash_reg + RDWR_EN_HI_CNT);
- iowrite32(2, denali->flash_reg + CS_SETUP_CNT);
- }
-}
-
-/*
* Use the configuration feature register to determine the maximum number of
* banks that the hardware supports.
*/
@@ -352,58 +201,6 @@ static void detect_max_banks(struct denali_nand_info *denali)
denali->max_banks <<= 1;
}
-static uint16_t denali_nand_timing_set(struct denali_nand_info *denali)
-{
- uint16_t status = PASS;
- uint32_t id_bytes[8], addr;
- uint8_t maf_id, device_id;
- int i;
-
- /*
- * Use read id method to get device ID and other params.
- * For some NAND chips, controller can't report the correct
- * device ID by reading from DEVICE_ID register
- */
- addr = MODE_11 | BANK(denali->flash_bank);
- index_addr(denali, addr | 0, 0x90);
- index_addr(denali, addr | 1, 0);
- for (i = 0; i < 8; i++)
- index_addr_read_data(denali, addr | 2, &id_bytes[i]);
- maf_id = id_bytes[0];
- device_id = id_bytes[1];
-
- if (ioread32(denali->flash_reg + ONFI_DEVICE_NO_OF_LUNS) &
- ONFI_DEVICE_NO_OF_LUNS__ONFI_DEVICE) { /* ONFI 1.0 NAND */
- if (FAIL == get_onfi_nand_para(denali))
- return FAIL;
- } else if (maf_id == 0xEC) { /* Samsung NAND */
- get_samsung_nand_para(denali, device_id);
- }
-
- dev_info(denali->dev,
- "Dump timing register values:\n"
- "acc_clks: %d, re_2_we: %d, re_2_re: %d\n"
- "we_2_re: %d, addr_2_data: %d, rdwr_en_lo_cnt: %d\n"
- "rdwr_en_hi_cnt: %d, cs_setup_cnt: %d\n",
- ioread32(denali->flash_reg + ACC_CLKS),
- ioread32(denali->flash_reg + RE_2_WE),
- ioread32(denali->flash_reg + RE_2_RE),
- ioread32(denali->flash_reg + WE_2_RE),
- ioread32(denali->flash_reg + ADDR_2_DATA),
- ioread32(denali->flash_reg + RDWR_EN_LO_CNT),
- ioread32(denali->flash_reg + RDWR_EN_HI_CNT),
- ioread32(denali->flash_reg + CS_SETUP_CNT));
-
- /*
- * If the user specified to override the default timings
- * with a specific ONFI mode, we apply those changes here.
- */
- if (onfi_timing_mode != NAND_DEFAULT_TIMINGS)
- nand_onfi_timing_set(denali, onfi_timing_mode);
-
- return status;
-}
-
static void denali_set_intr_modes(struct denali_nand_info *denali,
uint16_t INT_ENABLE)
{
@@ -1209,7 +1006,122 @@ static void denali_cmdfunc(struct mtd_info *mtd, unsigned int cmd, int col,
break;
}
}
-/* end NAND core entry points */
+
+#define DIV_ROUND_DOWN_ULL(ll, d) \
+ ({ unsigned long long _tmp = (ll); do_div(_tmp, d); _tmp; })
+
+static int denali_setup_data_interface(struct mtd_info *mtd,
+ const struct nand_data_interface *conf,
+ bool check_only)
+{
+ struct denali_nand_info *denali = mtd_to_denali(mtd);
+ const struct nand_sdr_timings *timings;
+ unsigned long t_clk;
+ int acc_clks, re_2_we, re_2_re, we_2_re, addr_2_data;
+ int rdwr_en_lo, rdwr_en_hi, rdwr_en_lo_hi, cs_setup;
+ int addr_2_data_mask;
+ uint32_t tmp;
+
+ timings = nand_get_sdr_timings(conf);
+ if (IS_ERR(timings))
+ return PTR_ERR(timings);
+
+ /* clk_x period in picoseconds */
+ t_clk = DIV_ROUND_DOWN_ULL(1000000000000ULL, denali->clk_x_rate);
+ if (!t_clk)
+ return -EINVAL;
+
+ if (check_only)
+ return 0;
+
+ /* tREA -> ACC_CLKS */
+ acc_clks = DIV_ROUND_UP(timings->tREA_max, t_clk);
+ acc_clks = min_t(int, acc_clks, ACC_CLKS__VALUE);
+
+ tmp = ioread32(denali->flash_reg + ACC_CLKS);
+ tmp &= ~ACC_CLKS__VALUE;
+ tmp |= acc_clks;
+ iowrite32(tmp, denali->flash_reg + ACC_CLKS);
+
+ /* tRWH -> RE_2_WE */
+ re_2_we = DIV_ROUND_UP(timings->tRHW_min, t_clk);
+ re_2_we = min_t(int, re_2_we, RE_2_WE__VALUE);
+
+ tmp = ioread32(denali->flash_reg + RE_2_WE);
+ tmp &= ~RE_2_WE__VALUE;
+ tmp |= re_2_we;
+ iowrite32(tmp, denali->flash_reg + RE_2_WE);
+
+ /* tRHZ -> RE_2_RE */
+ re_2_re = DIV_ROUND_UP(timings->tRHZ_max, t_clk);
+ re_2_re = min_t(int, re_2_re, RE_2_RE__VALUE);
+
+ tmp = ioread32(denali->flash_reg + RE_2_RE);
+ tmp &= ~RE_2_RE__VALUE;
+ tmp |= re_2_re;
+ iowrite32(tmp, denali->flash_reg + RE_2_RE);
+
+ /* tWHR -> WE_2_RE */
+ we_2_re = DIV_ROUND_UP(timings->tWHR_min, t_clk);
+ we_2_re = min_t(int, we_2_re, TWHR2_AND_WE_2_RE__WE_2_RE);
+
+ tmp = ioread32(denali->flash_reg + TWHR2_AND_WE_2_RE);
+ tmp &= ~TWHR2_AND_WE_2_RE__WE_2_RE;
+ tmp |= we_2_re;
+ iowrite32(tmp, denali->flash_reg + TWHR2_AND_WE_2_RE);
+
+ /* tADL -> ADDR_2_DATA */
+
+ /* for older versions, ADDR_2_DATA is only 6 bit wide */
+ addr_2_data_mask = TCWAW_AND_ADDR_2_DATA__ADDR_2_DATA;
+ if (denali->revision < 0x0501)
+ addr_2_data_mask >>= 1;
+
+ addr_2_data = DIV_ROUND_UP(timings->tADL_min, t_clk);
+ addr_2_data = min_t(int, addr_2_data, addr_2_data_mask);
+
+ tmp = ioread32(denali->flash_reg + TCWAW_AND_ADDR_2_DATA);
+ tmp &= ~addr_2_data_mask;
+ tmp |= addr_2_data;
+ iowrite32(tmp, denali->flash_reg + TCWAW_AND_ADDR_2_DATA);
+
+ /* tREH, tWH -> RDWR_EN_HI_CNT */
+ rdwr_en_hi = DIV_ROUND_UP(max(timings->tREH_min, timings->tWH_min),
+ t_clk);
+ rdwr_en_hi = min_t(int, rdwr_en_hi, RDWR_EN_HI_CNT__VALUE);
+
+ tmp = ioread32(denali->flash_reg + RDWR_EN_HI_CNT);
+ tmp &= ~RDWR_EN_HI_CNT__VALUE;
+ tmp |= rdwr_en_hi;
+ iowrite32(tmp, denali->flash_reg + RDWR_EN_HI_CNT);
+
+ /* tRP, tWP -> RDWR_EN_LO_CNT */
+ rdwr_en_lo = DIV_ROUND_UP(max(timings->tRP_min, timings->tWP_min),
+ t_clk);
+ rdwr_en_lo_hi = DIV_ROUND_UP(max(timings->tRC_min, timings->tWC_min),
+ t_clk);
+ rdwr_en_lo_hi = max(rdwr_en_lo_hi, DENALI_CLK_X_MULT);
+ rdwr_en_lo = max(rdwr_en_lo, rdwr_en_lo_hi - rdwr_en_hi);
+ rdwr_en_lo = min_t(int, rdwr_en_lo, RDWR_EN_LO_CNT__VALUE);
+
+ tmp = ioread32(denali->flash_reg + RDWR_EN_LO_CNT);
+ tmp &= ~RDWR_EN_LO_CNT__VALUE;
+ tmp |= rdwr_en_lo;
+ iowrite32(tmp, denali->flash_reg + RDWR_EN_LO_CNT);
+
+ /* tCS, tCEA -> CS_SETUP_CNT */
+ cs_setup = max3((int)DIV_ROUND_UP(timings->tCS_min, t_clk) - rdwr_en_lo,
+ (int)DIV_ROUND_UP(timings->tCEA_max, t_clk) - acc_clks,
+ 0);
+ cs_setup = min_t(int, cs_setup, CS_SETUP_CNT__VALUE);
+
+ tmp = ioread32(denali->flash_reg + CS_SETUP_CNT);
+ tmp &= ~CS_SETUP_CNT__VALUE;
+ tmp |= cs_setup;
+ iowrite32(tmp, denali->flash_reg + CS_SETUP_CNT);
+
+ return 0;
+}
/* Initialization code to bring the device up to a known good state */
static void denali_hw_init(struct denali_nand_info *denali)
@@ -1241,7 +1153,6 @@ static void denali_hw_init(struct denali_nand_info *denali)
/* Should set value for these registers when init */
iowrite32(0, denali->flash_reg + TWO_ROW_ADDR_CYCLES);
iowrite32(1, denali->flash_reg + ECC_ENABLE);
- denali_nand_timing_set(denali);
denali_irq_init(denali);
}
@@ -1422,17 +1333,6 @@ int denali_init(struct denali_nand_info *denali)
struct mtd_info *mtd = nand_to_mtd(chip);
int ret;
- if (denali->platform == INTEL_CE4100) {
- /*
- * Due to a silicon limitation, we can only support
- * ONFI timing mode 1 and below.
- */
- if (onfi_timing_mode < -1 || onfi_timing_mode > 1) {
- pr_err("Intel CE4100 only supports ONFI timing mode 1 or below\n");
- return -EINVAL;
- }
- }
-
/* allocate a temporary buffer for nand_scan_ident() */
denali->buf.buf = devm_kzalloc(denali->dev, PAGE_SIZE,
GFP_DMA | GFP_KERNEL);
@@ -1463,6 +1363,9 @@ int denali_init(struct denali_nand_info *denali)
chip->cmdfunc = denali_cmdfunc;
chip->read_byte = denali_read_byte;
chip->waitfunc = denali_waitfunc;
+ /* clk rate info is needed for setup_data_interface */
+ if (denali->clk_x_rate)
+ chip->setup_data_interface = denali_setup_data_interface;
/*
* scan for NAND devices attached to the controller
diff --git a/drivers/mtd/nand/denali.h b/drivers/mtd/nand/denali.h
index 1f0a85a..e55bb2e 100644
--- a/drivers/mtd/nand/denali.h
+++ b/drivers/mtd/nand/denali.h
@@ -72,11 +72,14 @@
#define GLOBAL_INT_ENABLE 0xf0
#define GLOBAL_INT_EN_FLAG BIT(0)
-#define WE_2_RE 0x100
-#define WE_2_RE__VALUE GENMASK(5, 0)
+#define TWHR2_AND_WE_2_RE 0x100
+#define TWHR2_AND_WE_2_RE__WE_2_RE GENMASK(5, 0)
+#define TWHR2_AND_WE_2_RE__TWHR2 GENMASK(13, 8)
-#define ADDR_2_DATA 0x110
-#define ADDR_2_DATA__VALUE GENMASK(5, 0)
+#define TCWAW_AND_ADDR_2_DATA 0x110
+/* The width of ADDR_2_DATA is 6 bit for old IP, 7 bit for new IP */
+#define TCWAW_AND_ADDR_2_DATA__ADDR_2_DATA GENMASK(6, 0)
+#define TCWAW_AND_ADDR_2_DATA__TCWAW GENMASK(13, 8)
#define RE_2_WE 0x120
#define RE_2_WE__VALUE GENMASK(5, 0)
@@ -128,6 +131,7 @@
#define CS_SETUP_CNT 0x220
#define CS_SETUP_CNT__VALUE GENMASK(4, 0)
+#define CS_SETUP_CNT__TWB GENMASK(17, 12)
#define SPARE_AREA_SKIP_BYTES 0x230
#define SPARE_AREA_SKIP_BYTES__VALUE GENMASK(5, 0)
@@ -297,13 +301,6 @@
#define FAIL 1 /*failed flag*/
#define PASS 0 /*success flag*/
-#define CLK_X 5
-#define CLK_MULTI 4
-
-#define ONFI_BLOOM_TIME 1
-#define MODE5_WORKAROUND 0
-
-
#define MODE_00 0x00000000
#define MODE_01 0x04000000
#define MODE_10 0x08000000
@@ -316,14 +313,10 @@ struct nand_buf {
dma_addr_t dma_buf;
};
-#define INTEL_CE4100 1
-#define INTEL_MRST 2
-#define DT 3
-
struct denali_nand_info {
struct nand_chip nand;
+ unsigned long clk_x_rate; /* bus interface clock rate */
int flash_bank; /* currently selected chip */
- int platform;
struct nand_buf buf;
struct device *dev;
int page;
diff --git a/drivers/mtd/nand/denali_dt.c b/drivers/mtd/nand/denali_dt.c
index 1f2f68a..97a2633 100644
--- a/drivers/mtd/nand/denali_dt.c
+++ b/drivers/mtd/nand/denali_dt.c
@@ -93,7 +93,6 @@ static int denali_dt_probe(struct platform_device *pdev)
denali->caps = data->caps;
}
- denali->platform = DT;
denali->dev = &pdev->dev;
denali->irq = platform_get_irq(pdev, 0);
if (denali->irq < 0) {
@@ -120,6 +119,8 @@ static int denali_dt_probe(struct platform_device *pdev)
}
clk_prepare_enable(dt->clk);
+ denali->clk_x_rate = clk_get_rate(dt->clk);
+
ret = denali_init(denali);
if (ret)
goto out_disable_clk;
diff --git a/drivers/mtd/nand/denali_pci.c b/drivers/mtd/nand/denali_pci.c
index a39682a5..117c182 100644
--- a/drivers/mtd/nand/denali_pci.c
+++ b/drivers/mtd/nand/denali_pci.c
@@ -19,6 +19,9 @@
#define DENALI_NAND_NAME "denali-nand-pci"
+#define INTEL_CE4100 1
+#define INTEL_MRST 2
+
/* List of platforms this NAND controller has be integrated into */
static const struct pci_device_id denali_pci_ids[] = {
{ PCI_VDEVICE(INTEL, 0x0701), INTEL_CE4100 },
@@ -45,13 +48,11 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
}
if (id->driver_data == INTEL_CE4100) {
- denali->platform = INTEL_CE4100;
mem_base = pci_resource_start(dev, 0);
mem_len = pci_resource_len(dev, 1);
csr_base = pci_resource_start(dev, 1);
csr_len = pci_resource_len(dev, 1);
} else {
- denali->platform = INTEL_MRST;
csr_base = pci_resource_start(dev, 0);
csr_len = pci_resource_len(dev, 0);
mem_base = pci_resource_start(dev, 1);
@@ -65,6 +66,7 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
pci_set_master(dev);
denali->dev = &dev->dev;
denali->irq = dev->irq;
+ denali->clk_x_rate = 200000000; /* 200 MHz */
ret = pci_request_regions(dev, DENALI_NAND_NAME);
if (ret) {
--
2.7.4
Commit 271707b1d817 ("mtd: nand: denali: max_banks calculation
changed in revision 5.1") added a revision check to support the
new max_banks encoding. Its git-log states "The encoding of
max_banks changed in Denali revision 5.1".
There are exceptional cases, for example, the revision register on
some UniPhier SoCs says the IP is 5.0 but the max_banks is encoded
in the new format.
This IP updates the resister specification from time to time (often
breaking the backward compatibility), but the revision number is not
incremented correctly.
The max_banks is not only the case that needs revision checking.
Let's allow to override an incorrect revision number.
Signed-off-by: Masahiro Yamada <[email protected]>
---
Changes in v3: None
Changes in v2:
- Replace the NEW_N_BANKS_FORMAT approach
drivers/mtd/nand/denali.c | 23 +++++++++++++----------
drivers/mtd/nand/denali.h | 3 +--
drivers/mtd/nand/denali_dt.c | 5 ++++-
3 files changed, 18 insertions(+), 13 deletions(-)
diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c
index 417a895..16634df 100644
--- a/drivers/mtd/nand/denali.c
+++ b/drivers/mtd/nand/denali.c
@@ -419,17 +419,12 @@ static void find_valid_banks(struct denali_nand_info *denali)
static void detect_max_banks(struct denali_nand_info *denali)
{
uint32_t features = ioread32(denali->flash_reg + FEATURES);
- /*
- * Read the revision register, so we can calculate the max_banks
- * properly: the encoding changed from rev 5.0 to 5.1
- */
- u32 revision = MAKE_COMPARABLE_REVISION(
- ioread32(denali->flash_reg + REVISION));
- if (revision < REVISION_5_1)
- denali->max_banks = 2 << (features & FEATURES__N_BANKS);
- else
- denali->max_banks = 1 << (features & FEATURES__N_BANKS);
+ denali->max_banks = 1 << (features & FEATURES__N_BANKS);
+
+ /* the encoding changed from rev 5.0 to 5.1 */
+ if (denali->revision < 0x0501)
+ denali->max_banks <<= 1;
}
static uint16_t denali_nand_timing_set(struct denali_nand_info *denali)
@@ -1320,6 +1315,14 @@ static void denali_cmdfunc(struct mtd_info *mtd, unsigned int cmd, int col,
static void denali_hw_init(struct denali_nand_info *denali)
{
/*
+ * The REVISION register may not be reliable. Platforms are allowed to
+ * override it.
+ */
+ if (!denali->revision)
+ denali->revision =
+ swab16(ioread32(denali->flash_reg + REVISION));
+
+ /*
* tell driver how many bit controller will skip before
* writing ECC code in OOB, this register may be already
* set by firmware. So we read this value out.
diff --git a/drivers/mtd/nand/denali.h b/drivers/mtd/nand/denali.h
index 1f413d0..ec00485 100644
--- a/drivers/mtd/nand/denali.h
+++ b/drivers/mtd/nand/denali.h
@@ -179,8 +179,6 @@
#define REVISION 0x370
#define REVISION__VALUE 0xffff
-#define MAKE_COMPARABLE_REVISION(x) swab16((x) & REVISION__VALUE)
-#define REVISION_5_1 0x00000501
#define ONFI_DEVICE_FEATURES 0x380
#define ONFI_DEVICE_FEATURES__VALUE 0x003f
@@ -343,6 +341,7 @@ struct denali_nand_info {
int devnum; /* represent how many nands connected */
int bbtskipbytes;
int max_banks;
+ unsigned int revision;
unsigned int caps;
};
diff --git a/drivers/mtd/nand/denali_dt.c b/drivers/mtd/nand/denali_dt.c
index ada3863..df9ef36 100644
--- a/drivers/mtd/nand/denali_dt.c
+++ b/drivers/mtd/nand/denali_dt.c
@@ -30,6 +30,7 @@ struct denali_dt {
};
struct denali_dt_data {
+ unsigned int revision;
unsigned int caps;
};
@@ -60,8 +61,10 @@ static int denali_dt_probe(struct platform_device *pdev)
denali = &dt->denali;
data = of_device_get_match_data(&pdev->dev);
- if (data)
+ if (data) {
+ denali->revision = data->revision;
denali->caps = data->caps;
+ }
denali->platform = DT;
denali->dev = &pdev->dev;
--
2.7.4
Some old versions of the Denali IP (perhaps used only for Intel?)
detects ECC errors and provides correct data via a register, but
does not touch the transferred data. So, the software must fixup
the data in the buffer according to the provided ECC correction
information.
Newer versions perform ECC correction before transferring the data.
No more software intervention is needed. The ECC_ERROR_ADDRESS and
ECC_CORRECTION_INFO registers were deprecated. Instead, the number
of corrected bit-flips are reported via the ECC_COR_INFO register.
When an uncorrectable ECC error happens, a status flag is set to the
INTR_STATUS and ECC_COR_INFO registers.
As is often the case with this IP, the register view of INTR_STATUS
had broken compatibility.
For older versions (SW ECC fixup):
bit 0: ECC_TRANSACTION_DONE
bit 1: ECC_ERR
For newer versions (HW ECC fixup):
bit 0: ECC_UNCOR_ERR
bit 1: Reserved
Due to this difference, the irq_mask must be fixed too.
The existing handle_ecc() has been renamed to denali_sw_ecc_fixup()
for clarification.
What is unfortunate with this feature is we can not know the total
number of corrected/uncorrected errors in a page. The register
ECC_COR_INFO reports the maximum of per-sector bitflips. This is
useful for ->read_page return value, but ecc_stats.{corrected,failed}
increments may not be precise.
Signed-off-by: Masahiro Yamada <[email protected]>
---
Changes in v3:
- Adjusted for the correct erased page check.
- Move DENALI_CAP_ define out of struct denali_nand_info
Changes in v2:
- Change the capability prefix DENALI_CAPS_ -> DENALI_CAP_
drivers/mtd/nand/denali.c | 52 ++++++++++++++++++++++++++++++++++++++++-------
drivers/mtd/nand/denali.h | 14 +++++++++++--
2 files changed, 57 insertions(+), 9 deletions(-)
diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c
index 64a3bdc..b95e33a 100644
--- a/drivers/mtd/nand/denali.c
+++ b/drivers/mtd/nand/denali.c
@@ -856,6 +856,41 @@ static int denali_check_erased_page(struct mtd_info *mtd,
return max_bitflips;
}
+static int denali_hw_ecc_fixup(struct mtd_info *mtd,
+ struct denali_nand_info *denali,
+ unsigned long *uncor_ecc_flags)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ int bank = denali->flash_bank;
+ uint32_t ecc_cor;
+ unsigned int max_bitflips;
+
+ ecc_cor = ioread32(denali->flash_reg + ECC_COR_INFO(bank));
+ ecc_cor >>= ECC_COR_INFO__SHIFT(bank);
+
+ if (ecc_cor & ECC_COR_INFO__UNCOR_ERR) {
+ /*
+ * This flag is set when uncorrectable error occurs at least in
+ * one ECC sector. We can not know "how many sectors", or
+ * "which sector(s)". We need erase-page check for all sectors.
+ */
+ *uncor_ecc_flags = GENMASK(chip->ecc.steps - 1, 0);
+ return 0;
+ }
+
+ max_bitflips = ecc_cor & ECC_COR_INFO__MAX_ERRORS;
+
+ /*
+ * The register holds the maximum of per-sector corrected bitflips.
+ * This is suitable for the return value of the ->read_page() callback.
+ * Unfortunately, we can not know the total number of corrected bits in
+ * the page. Increase the stats by max_bitflips. (compromised solution)
+ */
+ mtd->ecc_stats.corrected += max_bitflips;
+
+ return max_bitflips;
+}
+
#define ECC_SECTOR_SIZE 512
#define ECC_SECTOR(x) (((x) & ECC_ERROR_ADDRESS__SECTOR_NR) >> 12)
@@ -865,8 +900,9 @@ static int denali_check_erased_page(struct mtd_info *mtd,
#define ECC_ERR_DEVICE(x) (((x) & ERR_CORRECTION_INFO__DEVICE_NR) >> 8)
#define ECC_LAST_ERR(x) ((x) & ERR_CORRECTION_INFO__LAST_ERR_INFO)
-static int handle_ecc(struct mtd_info *mtd, struct denali_nand_info *denali,
- uint8_t *buf, unsigned long *uncor_ecc_flags)
+static int denali_sw_ecc_fixup(struct mtd_info *mtd,
+ struct denali_nand_info *denali,
+ unsigned long *uncor_ecc_flags, uint8_t *buf)
{
unsigned int bitflips = 0;
unsigned int max_bitflips = 0;
@@ -1070,12 +1106,12 @@ static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip,
uint8_t *buf, int oob_required, int page)
{
struct denali_nand_info *denali = mtd_to_denali(mtd);
-
dma_addr_t addr = denali->buf.dma_buf;
size_t size = mtd->writesize + mtd->oobsize;
-
uint32_t irq_status;
- uint32_t irq_mask = INTR__ECC_TRANSACTION_DONE | INTR__ECC_ERR;
+ uint32_t irq_mask = denali->caps & DENALI_CAP_HW_ECC_FIXUP ?
+ INTR__DMA_CMD_COMP | INTR__ECC_UNCOR_ERR :
+ INTR__ECC_TRANSACTION_DONE | INTR__ECC_ERR;
unsigned long uncor_ecc_flags = 0;
int stat = 0;
@@ -1101,8 +1137,10 @@ static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip,
memcpy(buf, denali->buf.buf, mtd->writesize);
- if (irq_status & INTR__ECC_ERR)
- stat = handle_ecc(mtd, denali, buf, &uncor_ecc_flags);
+ if (denali->caps & DENALI_CAP_HW_ECC_FIXUP)
+ stat = denali_hw_ecc_fixup(mtd, denali, &uncor_ecc_flags);
+ else if (irq_status & INTR__ECC_ERR)
+ stat = denali_sw_ecc_fixup(mtd, denali, &uncor_ecc_flags, buf);
denali_enable_dma(denali, false);
if (stat < 0)
diff --git a/drivers/mtd/nand/denali.h b/drivers/mtd/nand/denali.h
index 483c0e9..e532956 100644
--- a/drivers/mtd/nand/denali.h
+++ b/drivers/mtd/nand/denali.h
@@ -20,6 +20,7 @@
#ifndef __DENALI_H__
#define __DENALI_H__
+#include <linux/bitops.h>
#include <linux/mtd/nand.h>
#define DEVICE_RESET 0x0
@@ -218,8 +219,10 @@
#define INTR_STATUS(__bank) (0x410 + ((__bank) * 0x50))
#define INTR_EN(__bank) (0x420 + ((__bank) * 0x50))
-#define INTR__ECC_TRANSACTION_DONE 0x0001
-#define INTR__ECC_ERR 0x0002
+/* bit[1:0] is used differently depending on IP version */
+#define INTR__ECC_UNCOR_ERR 0x0001 /* new IP */
+#define INTR__ECC_TRANSACTION_DONE 0x0001 /* old IP */
+#define INTR__ECC_ERR 0x0002 /* old IP */
#define INTR__DMA_CMD_COMP 0x0004
#define INTR__TIME_OUT 0x0008
#define INTR__PROGRAM_FAIL 0x0010
@@ -259,6 +262,11 @@
#define ERR_CORRECTION_INFO__ERROR_TYPE 0x4000
#define ERR_CORRECTION_INFO__LAST_ERR_INFO 0x8000
+#define ECC_COR_INFO(bank) (0x650 + (bank) / 2 * 0x10)
+#define ECC_COR_INFO__SHIFT(bank) ((bank) % 2 * 8)
+#define ECC_COR_INFO__MAX_ERRORS 0x007f
+#define ECC_COR_INFO__UNCOR_ERR 0x0080
+
#define DMA_ENABLE 0x700
#define DMA_ENABLE__FLAG 0x0001
@@ -338,6 +346,8 @@ struct denali_nand_info {
unsigned int caps;
};
+#define DENALI_CAP_HW_ECC_FIXUP BIT(0)
+
extern int denali_init(struct denali_nand_info *denali);
extern void denali_remove(struct denali_nand_info *denali);
--
2.7.4
The driver sets appropriate DMA mask. Delete the "dma-mask" DT
property. See [1] for negative comments for this binding.
[1] https://lkml.org/lkml/2016/2/8/57
Signed-off-by: Masahiro Yamada <[email protected]>
Acked-by: Rob Herring <[email protected]>
---
Changes in v3: None
Changes in v2: None
Documentation/devicetree/bindings/mtd/denali-nand.txt | 2 --
drivers/mtd/nand/denali_dt.c | 9 ---------
2 files changed, 11 deletions(-)
diff --git a/Documentation/devicetree/bindings/mtd/denali-nand.txt b/Documentation/devicetree/bindings/mtd/denali-nand.txt
index 6f4ab4c..e593bbe 100644
--- a/Documentation/devicetree/bindings/mtd/denali-nand.txt
+++ b/Documentation/devicetree/bindings/mtd/denali-nand.txt
@@ -6,7 +6,6 @@ Required properties:
- reg : should contain registers location and length for data and reg.
- reg-names: Should contain the reg names "nand_data" and "denali_reg"
- interrupts : The interrupt number.
- - dm-mask : DMA bit mask
The device tree may optionally contain sub-nodes describing partitions of the
address space. See partition.txt for more detail.
@@ -20,5 +19,4 @@ nand: nand@ff900000 {
reg = <0xff900000 0x100000>, <0xffb80000 0x10000>;
reg-names = "nand_data", "denali_reg";
interrupts = <0 144 4>;
- dma-mask = <0xffffffff>;
};
diff --git a/drivers/mtd/nand/denali_dt.c b/drivers/mtd/nand/denali_dt.c
index 9577bfd..b8a8284 100644
--- a/drivers/mtd/nand/denali_dt.c
+++ b/drivers/mtd/nand/denali_dt.c
@@ -46,8 +46,6 @@ static const struct of_device_id denali_nand_dt_ids[] = {
};
MODULE_DEVICE_TABLE(of, denali_nand_dt_ids);
-static u64 denali_dma_mask;
-
static int denali_dt_probe(struct platform_device *ofdev)
{
struct resource *denali_reg, *nand_data;
@@ -83,13 +81,6 @@ static int denali_dt_probe(struct platform_device *ofdev)
if (IS_ERR(denali->flash_mem))
return PTR_ERR(denali->flash_mem);
- if (!of_property_read_u32(ofdev->dev.of_node,
- "dma-mask", (u32 *)&denali_dma_mask)) {
- denali->dev->dma_mask = &denali_dma_mask;
- } else {
- denali->dev->dma_mask = NULL;
- }
-
dt->clk = devm_clk_get(&ofdev->dev, NULL);
if (IS_ERR(dt->clk)) {
dev_err(&ofdev->dev, "no clk available\n");
--
2.7.4
Currently, the error handling of denali_write_page(_raw) is a bit
complicated. If the program command fails, NAND_STATUS_FAIL is set
to the driver internal denali->status, then read out later by
denali_waitfunc().
We can avoid it by exploiting the nand_write_page() implementation.
If chip->ecc.write_page(_raw) returns negative code (i.e. -EIO), it
errors out immediately. This gives the same result as returning
NAND_STATUS_FAIL from chip->waitfunc. In either way, -EIO is
returned to the upper MTD layer.
Signed-off-by: Masahiro Yamada <[email protected]>
---
Changes in v3: None
Changes in v2:
- Newly added
drivers/mtd/nand/denali.c | 12 ++++--------
drivers/mtd/nand/denali.h | 1 -
2 files changed, 4 insertions(+), 9 deletions(-)
diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c
index 79851ca..5da8156 100644
--- a/drivers/mtd/nand/denali.c
+++ b/drivers/mtd/nand/denali.c
@@ -1005,6 +1005,7 @@ static int write_page(struct mtd_info *mtd, struct nand_chip *chip,
size_t size = mtd->writesize + mtd->oobsize;
uint32_t irq_status;
uint32_t irq_mask = INTR__DMA_CMD_COMP | INTR__PROGRAM_FAIL;
+ int ret = 0;
denali->page = page;
@@ -1038,13 +1039,13 @@ static int write_page(struct mtd_info *mtd, struct nand_chip *chip,
if (irq_status == 0) {
dev_err(denali->dev, "timeout on write_page (type = %d)\n",
raw_xfer);
- denali->status = NAND_STATUS_FAIL;
+ ret = -EIO;
}
denali_enable_dma(denali, false);
dma_sync_single_for_cpu(denali->dev, addr, size, DMA_TO_DEVICE);
- return 0;
+ return ret;
}
/* NAND core entry points */
@@ -1196,12 +1197,7 @@ static void denali_select_chip(struct mtd_info *mtd, int chip)
static int denali_waitfunc(struct mtd_info *mtd, struct nand_chip *chip)
{
- struct denali_nand_info *denali = mtd_to_denali(mtd);
- int status = denali->status;
-
- denali->status = 0;
-
- return status;
+ return 0;
}
static int denali_erase(struct mtd_info *mtd, int page)
diff --git a/drivers/mtd/nand/denali.h b/drivers/mtd/nand/denali.h
index 00ce04e..9e2b787 100644
--- a/drivers/mtd/nand/denali.h
+++ b/drivers/mtd/nand/denali.h
@@ -329,7 +329,6 @@ struct nand_buf {
struct denali_nand_info {
struct nand_chip nand;
int flash_bank; /* currently selected chip */
- int status;
int platform;
struct nand_buf buf;
struct device *dev;
--
2.7.4
This function is wrong in multiple ways:
[1] Counting corrected bytes instead of corrected bits.
The following code is counting the number of corrected _bytes_.
/* correct the ECC error */
buf[offset] ^= err_cor_value;
mtd->ecc_stats.corrected++;
bitflips++;
What the core framework expects is the number of corrected _bits_.
They can be different if multiple bitflips occur within one byte.
[2] total number of errors instead of max of per-sector errors
The core framework expects that corrected errors are counted per
sector, then the max value should be taken. The current code simply
iterates over the whole page, i.e. counts the total number of
correction in the page. This means "too many bitflips" is triggered
earlier than it should be, i.e. the NAND device is worn out sooner.
Besides those bugs, this function is unreadable due to the deep
nesting. Notice the whole code in this function is wrapped in
if (irq_status & INTR__ECC_ERR), so this conditional can be moved
out of the function. Also, use shorter names for local variables.
Re-work the function to fix all the issues.
Signed-off-by: Masahiro Yamada <[email protected]>
---
Changes in v3:
- Adjust function arguments for the next commit
Changes in v2:
- Use shorter names for local variables.
- Fix bugs addressed by [1], [2]
drivers/mtd/nand/denali.c | 141 +++++++++++++++++++++++-----------------------
1 file changed, 71 insertions(+), 70 deletions(-)
diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c
index 65cf7cc..c5c150a 100644
--- a/drivers/mtd/nand/denali.c
+++ b/drivers/mtd/nand/denali.c
@@ -836,80 +836,80 @@ static bool is_erased(uint8_t *buf, int len)
#define ECC_SECTOR(x) (((x) & ECC_ERROR_ADDRESS__SECTOR_NR) >> 12)
#define ECC_BYTE(x) (((x) & ECC_ERROR_ADDRESS__OFFSET))
#define ECC_CORRECTION_VALUE(x) ((x) & ERR_CORRECTION_INFO__BYTEMASK)
-#define ECC_ERROR_CORRECTABLE(x) (!((x) & ERR_CORRECTION_INFO__ERROR_TYPE))
+#define ECC_ERROR_UNCORRECTABLE(x) ((x) & ERR_CORRECTION_INFO__ERROR_TYPE)
#define ECC_ERR_DEVICE(x) (((x) & ERR_CORRECTION_INFO__DEVICE_NR) >> 8)
#define ECC_LAST_ERR(x) ((x) & ERR_CORRECTION_INFO__LAST_ERR_INFO)
-static bool handle_ecc(struct denali_nand_info *denali, uint8_t *buf,
- uint32_t irq_status, unsigned int *max_bitflips)
+static int handle_ecc(struct mtd_info *mtd, struct denali_nand_info *denali,
+ uint8_t *buf, bool *check_erased_page)
{
- bool check_erased_page = false;
unsigned int bitflips = 0;
+ unsigned int max_bitflips = 0;
+ uint32_t err_addr, err_cor_info;
+ unsigned int err_byte, err_sector, err_device;
+ uint8_t err_cor_value;
+ unsigned int prev_sector = 0;
- if (irq_status & INTR__ECC_ERR) {
- /* read the ECC errors. we'll ignore them for now */
- uint32_t err_address, err_correction_info, err_byte,
- err_sector, err_device, err_correction_value;
- denali_set_intr_modes(denali, false);
-
- do {
- err_address = ioread32(denali->flash_reg +
- ECC_ERROR_ADDRESS);
- err_sector = ECC_SECTOR(err_address);
- err_byte = ECC_BYTE(err_address);
-
- err_correction_info = ioread32(denali->flash_reg +
- ERR_CORRECTION_INFO);
- err_correction_value =
- ECC_CORRECTION_VALUE(err_correction_info);
- err_device = ECC_ERR_DEVICE(err_correction_info);
-
- if (ECC_ERROR_CORRECTABLE(err_correction_info)) {
- /*
- * If err_byte is larger than ECC_SECTOR_SIZE,
- * means error happened in OOB, so we ignore
- * it. It's no need for us to correct it
- * err_device is represented the NAND error
- * bits are happened in if there are more
- * than one NAND connected.
- */
- if (err_byte < ECC_SECTOR_SIZE) {
- struct mtd_info *mtd =
- nand_to_mtd(&denali->nand);
- int offset;
-
- offset = (err_sector *
- ECC_SECTOR_SIZE +
- err_byte) *
- denali->devnum +
- err_device;
- /* correct the ECC error */
- buf[offset] ^= err_correction_value;
- mtd->ecc_stats.corrected++;
- bitflips++;
- }
- } else {
- /*
- * if the error is not correctable, need to
- * look at the page to see if it is an erased
- * page. if so, then it's not a real ECC error
- */
- check_erased_page = true;
- }
- } while (!ECC_LAST_ERR(err_correction_info));
- /*
- * Once handle all ecc errors, controller will triger
- * a ECC_TRANSACTION_DONE interrupt, so here just wait
- * for a while for this interrupt
- */
- while (!(read_interrupt_status(denali) &
- INTR__ECC_TRANSACTION_DONE))
- cpu_relax();
- clear_interrupts(denali);
- denali_set_intr_modes(denali, true);
- }
- *max_bitflips = bitflips;
- return check_erased_page;
+ /* read the ECC errors. we'll ignore them for now */
+ denali_set_intr_modes(denali, false);
+
+ do {
+ err_addr = ioread32(denali->flash_reg + ECC_ERROR_ADDRESS);
+ err_sector = ECC_SECTOR(err_addr);
+ err_byte = ECC_BYTE(err_addr);
+
+ err_cor_info = ioread32(denali->flash_reg + ERR_CORRECTION_INFO);
+ err_cor_value = ECC_CORRECTION_VALUE(err_cor_info);
+ err_device = ECC_ERR_DEVICE(err_cor_info);
+
+ /* reset the bitflip counter when crossing ECC sector */
+ if (err_sector != prev_sector)
+ bitflips = 0;
+
+ if (ECC_ERROR_UNCORRECTABLE(err_cor_info)) {
+ /*
+ * if the error is not correctable, need to look at the
+ * page to see if it is an erased page. if so, then
+ * it's not a real ECC error
+ */
+ *check_erased_page = true;
+ } else if (err_byte < ECC_SECTOR_SIZE) {
+ /*
+ * If err_byte is larger than ECC_SECTOR_SIZE, means error
+ * happened in OOB, so we ignore it. It's no need for
+ * us to correct it err_device is represented the NAND
+ * error bits are happened in if there are more than
+ * one NAND connected.
+ */
+ int offset;
+ unsigned int flips_in_byte;
+
+ offset = (err_sector * ECC_SECTOR_SIZE + err_byte) *
+ denali->devnum + err_device;
+
+ /* correct the ECC error */
+ flips_in_byte = hweight8(buf[offset] ^ err_cor_value);
+ buf[offset] ^= err_cor_value;
+ mtd->ecc_stats.corrected += flips_in_byte;
+ bitflips += flips_in_byte;
+
+ max_bitflips = max(max_bitflips, bitflips);
+ }
+
+ prev_sector = err_sector;
+ } while (!ECC_LAST_ERR(err_cor_info));
+
+ /*
+ * Once handle all ecc errors, controller will trigger a
+ * ECC_TRANSACTION_DONE interrupt, so here just wait for
+ * a while for this interrupt
+ */
+ while (!(read_interrupt_status(denali) & INTR__ECC_TRANSACTION_DONE))
+ cpu_relax();
+ clear_interrupts(denali);
+ denali_set_intr_modes(denali, true);
+
+ return max_bitflips;
}
/* programs the controller to either enable/disable DMA transfers */
@@ -1045,7 +1045,7 @@ static int denali_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip,
uint8_t *buf, int oob_required, int page)
{
- unsigned int max_bitflips;
+ unsigned int max_bitflips = 0;
struct denali_nand_info *denali = mtd_to_denali(mtd);
dma_addr_t addr = denali->buf.dma_buf;
@@ -1077,7 +1077,8 @@ static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip,
memcpy(buf, denali->buf.buf, mtd->writesize);
- check_erased_page = handle_ecc(denali, buf, irq_status, &max_bitflips);
+ if (irq_status & INTR__ECC_ERR)
+ max_bitflips = handle_ecc(mtd, denali, buf, &check_erased_page);
denali_enable_dma(denali, false);
if (check_erased_page) {
--
2.7.4
The current driver only supports the DMA engine up to 32 bit
physical address, but there also exists 64 bit capable DMA engine
for this IP.
The data DMA setup sequence is completely different, so I added the
64 bit DMA code as a new function denali_setup_dma64(). The 32 bit
one has been renamed to denali_setup_dma32().
Signed-off-by: Masahiro Yamada <[email protected]>
---
Changes in v3:
- Move DENALI_CAP_ define out of struct denali_nand_info
Changes in v2:
- Change the capability prefix DENALI_CAPS_ -> DENALI_CAP_
drivers/mtd/nand/denali.c | 39 +++++++++++++++++++++++++++++++++++----
drivers/mtd/nand/denali.h | 1 +
2 files changed, 36 insertions(+), 4 deletions(-)
diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c
index b95e33a..417a895 100644
--- a/drivers/mtd/nand/denali.c
+++ b/drivers/mtd/nand/denali.c
@@ -979,8 +979,30 @@ static void denali_enable_dma(struct denali_nand_info *denali, bool en)
ioread32(denali->flash_reg + DMA_ENABLE);
}
-/* setups the HW to perform the data DMA */
-static void denali_setup_dma(struct denali_nand_info *denali, int op)
+static void denali_setup_dma64(struct denali_nand_info *denali, int op)
+{
+ uint32_t mode;
+ const int page_count = 1;
+ uint64_t addr = denali->buf.dma_buf;
+
+ mode = MODE_10 | BANK(denali->flash_bank) | denali->page;
+
+ /* DMA is a three step process */
+
+ /*
+ * 1. setup transfer type, interrupt when complete,
+ * burst len = 64 bytes, the number of pages
+ */
+ index_addr(denali, mode, 0x01002000 | (64 << 16) | op | page_count);
+
+ /* 2. set memory low address */
+ index_addr(denali, mode, addr);
+
+ /* 3. set memory high address */
+ index_addr(denali, mode, addr >> 32);
+}
+
+static void denali_setup_dma32(struct denali_nand_info *denali, int op)
{
uint32_t mode;
const int page_count = 1;
@@ -1003,6 +1025,14 @@ static void denali_setup_dma(struct denali_nand_info *denali, int op)
index_addr(denali, mode | 0x14000, 0x2400);
}
+static void denali_setup_dma(struct denali_nand_info *denali, int op)
+{
+ if (denali->caps & DENALI_CAP_DMA_64BIT)
+ denali_setup_dma64(denali, op);
+ else
+ denali_setup_dma32(denali, op);
+}
+
/*
* writes a page. user specifies type, and this function handles the
* configuration details.
@@ -1518,8 +1548,9 @@ int denali_init(struct denali_nand_info *denali)
goto failed_req_irq;
}
- /* Is 32-bit DMA supported? */
- ret = dma_set_mask(denali->dev, DMA_BIT_MASK(32));
+ ret = dma_set_mask(denali->dev,
+ DMA_BIT_MASK(denali->caps & DENALI_CAP_DMA_64BIT ?
+ 64 : 32));
if (ret) {
dev_err(denali->dev, "No usable DMA configuration\n");
goto failed_req_irq;
diff --git a/drivers/mtd/nand/denali.h b/drivers/mtd/nand/denali.h
index e532956..1f413d0 100644
--- a/drivers/mtd/nand/denali.h
+++ b/drivers/mtd/nand/denali.h
@@ -347,6 +347,7 @@ struct denali_nand_info {
};
#define DENALI_CAP_HW_ECC_FIXUP BIT(0)
+#define DENALI_CAP_DMA_64BIT BIT(1)
extern int denali_init(struct denali_nand_info *denali);
extern void denali_remove(struct denali_nand_info *denali);
--
2.7.4
This part is wrong in multiple ways:
[1] is_erased() is called against "buf" twice, so the OOB area is
not checked at all. The second call should check chip->oob_poi.
[2] This code block is nested by double "if (check_erase_page)".
The inner one is redundant.
[3] The ECC_ERROR_ADDRESS register reports which sector(s) had
uncorrectable ECC errors. It is pointless to check the whole page
if only one sector contains errors.
[4] Unfortunately, the Denali ECC correction engine has already
manipulated the data buffer before it decides the bitflips are
uncorrectable. That is, not all of the data are 0xFF after an
erased page is processed by the ECC engine. The current is_erased()
helper could report false-positive ECC errors. Actually, a certain
mount of bitflips are allowed in an erased page. The core framework
provides nand_check_erased_ecc_chunk() that takes the threshold into
account. Let's use this.
This commit reworks the code to solve those problems.
Please note the erased page checking is implemented as a separate
helper function instead of embedding it in the loop in handle_ecc().
The reason is that OOB data are needed for the erased page checking,
but the controller can not start a new transaction until all ECC
error information is read out from the registers.
Signed-off-by: Masahiro Yamada <[email protected]>
---
Changes in v3:
- Fix nand_check_erased_ecc_chunk call logic.
Iterate over only needed ECC chunks.
Changes in v2:
- Squash some patches into one.
- Use nand_check_erased_ecc_chunk() with threshold
drivers/mtd/nand/denali.c | 77 ++++++++++++++++++++++++++++++-----------------
1 file changed, 50 insertions(+), 27 deletions(-)
diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c
index c5c150a..64a3bdc 100644
--- a/drivers/mtd/nand/denali.c
+++ b/drivers/mtd/nand/denali.c
@@ -818,19 +818,44 @@ static void read_oob_data(struct mtd_info *mtd, uint8_t *buf, int page)
}
}
-/*
- * this function examines buffers to see if they contain data that
- * indicate that the buffer is part of an erased region of flash.
- */
-static bool is_erased(uint8_t *buf, int len)
+static int denali_check_erased_page(struct mtd_info *mtd,
+ struct nand_chip *chip, uint8_t *buf,
+ unsigned long uncor_ecc_flags,
+ unsigned int max_bitflips)
{
- int i;
+ uint8_t *ecc_code = chip->buffers->ecccode;
+ int ecc_steps = chip->ecc.steps;
+ int ecc_size = chip->ecc.size;
+ int ecc_bytes = chip->ecc.bytes;
+ int i, ret, stat;
+
+ ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code, chip->oob_poi, 0,
+ chip->ecc.total);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < ecc_steps; i++) {
+ if (!(uncor_ecc_flags & BIT(i)))
+ continue;
+
+ stat = nand_check_erased_ecc_chunk(buf, ecc_size,
+ ecc_code, ecc_bytes,
+ NULL, 0,
+ chip->ecc.strength);
+ if (stat < 0) {
+ mtd->ecc_stats.failed++;
+ } else {
+ mtd->ecc_stats.corrected += stat;
+ max_bitflips = max_t(unsigned int, max_bitflips, stat);
+ }
- for (i = 0; i < len; i++)
- if (buf[i] != 0xFF)
- return false;
- return true;
+ buf += ecc_size;
+ ecc_code += ecc_bytes;
+ }
+
+ return max_bitflips;
}
+
#define ECC_SECTOR_SIZE 512
#define ECC_SECTOR(x) (((x) & ECC_ERROR_ADDRESS__SECTOR_NR) >> 12)
@@ -841,7 +866,7 @@ static bool is_erased(uint8_t *buf, int len)
#define ECC_LAST_ERR(x) ((x) & ERR_CORRECTION_INFO__LAST_ERR_INFO)
static int handle_ecc(struct mtd_info *mtd, struct denali_nand_info *denali,
- uint8_t *buf, bool *check_erased_page)
+ uint8_t *buf, unsigned long *uncor_ecc_flags)
{
unsigned int bitflips = 0;
unsigned int max_bitflips = 0;
@@ -868,11 +893,10 @@ static int handle_ecc(struct mtd_info *mtd, struct denali_nand_info *denali,
if (ECC_ERROR_UNCORRECTABLE(err_cor_info)) {
/*
- * if the error is not correctable, need to look at the
- * page to see if it is an erased page. if so, then
- * it's not a real ECC error
+ * Check later if this is a real ECC error, or
+ * an erased sector.
*/
- *check_erased_page = true;
+ *uncor_ecc_flags |= BIT(err_sector);
} else if (err_byte < ECC_SECTOR_SIZE) {
/*
* If err_byte is larger than ECC_SECTOR_SIZE, means error
@@ -1045,7 +1069,6 @@ static int denali_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip,
uint8_t *buf, int oob_required, int page)
{
- unsigned int max_bitflips = 0;
struct denali_nand_info *denali = mtd_to_denali(mtd);
dma_addr_t addr = denali->buf.dma_buf;
@@ -1053,7 +1076,8 @@ static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip,
uint32_t irq_status;
uint32_t irq_mask = INTR__ECC_TRANSACTION_DONE | INTR__ECC_ERR;
- bool check_erased_page = false;
+ unsigned long uncor_ecc_flags = 0;
+ int stat = 0;
if (page != denali->page) {
dev_err(denali->dev,
@@ -1078,21 +1102,20 @@ static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip,
memcpy(buf, denali->buf.buf, mtd->writesize);
if (irq_status & INTR__ECC_ERR)
- max_bitflips = handle_ecc(mtd, denali, buf, &check_erased_page);
+ stat = handle_ecc(mtd, denali, buf, &uncor_ecc_flags);
denali_enable_dma(denali, false);
- if (check_erased_page) {
+ if (stat < 0)
+ return stat;
+
+ if (uncor_ecc_flags) {
read_oob_data(mtd, chip->oob_poi, denali->page);
- /* check ECC failures that may have occurred on erased pages */
- if (check_erased_page) {
- if (!is_erased(buf, mtd->writesize))
- mtd->ecc_stats.failed++;
- if (!is_erased(buf, mtd->oobsize))
- mtd->ecc_stats.failed++;
- }
+ stat = denali_check_erased_page(mtd, chip, buf,
+ uncor_ecc_flags, stat);
}
- return max_bitflips;
+
+ return stat;
}
static int denali_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
--
2.7.4
"pdev" is much more often used to point a platform_device, so this
will help the driver code look consistent across the kernel.
While we are here, fix "line over 80 characters" coding style
violations.
Signed-off-by: Masahiro Yamada <[email protected]>
---
Changes in v3: None
Changes in v2: None
drivers/mtd/nand/denali_dt.c | 32 +++++++++++++++++---------------
1 file changed, 17 insertions(+), 15 deletions(-)
diff --git a/drivers/mtd/nand/denali_dt.c b/drivers/mtd/nand/denali_dt.c
index b8a8284..ada3863 100644
--- a/drivers/mtd/nand/denali_dt.c
+++ b/drivers/mtd/nand/denali_dt.c
@@ -46,7 +46,7 @@ static const struct of_device_id denali_nand_dt_ids[] = {
};
MODULE_DEVICE_TABLE(of, denali_nand_dt_ids);
-static int denali_dt_probe(struct platform_device *ofdev)
+static int denali_dt_probe(struct platform_device *pdev)
{
struct resource *denali_reg, *nand_data;
struct denali_dt *dt;
@@ -54,36 +54,38 @@ static int denali_dt_probe(struct platform_device *ofdev)
struct denali_nand_info *denali;
int ret;
- dt = devm_kzalloc(&ofdev->dev, sizeof(*dt), GFP_KERNEL);
+ dt = devm_kzalloc(&pdev->dev, sizeof(*dt), GFP_KERNEL);
if (!dt)
return -ENOMEM;
denali = &dt->denali;
- data = of_device_get_match_data(&ofdev->dev);
+ data = of_device_get_match_data(&pdev->dev);
if (data)
denali->caps = data->caps;
denali->platform = DT;
- denali->dev = &ofdev->dev;
- denali->irq = platform_get_irq(ofdev, 0);
+ denali->dev = &pdev->dev;
+ denali->irq = platform_get_irq(pdev, 0);
if (denali->irq < 0) {
- dev_err(&ofdev->dev, "no irq defined\n");
+ dev_err(&pdev->dev, "no irq defined\n");
return denali->irq;
}
- denali_reg = platform_get_resource_byname(ofdev, IORESOURCE_MEM, "denali_reg");
- denali->flash_reg = devm_ioremap_resource(&ofdev->dev, denali_reg);
+ denali_reg = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "denali_reg");
+ denali->flash_reg = devm_ioremap_resource(&pdev->dev, denali_reg);
if (IS_ERR(denali->flash_reg))
return PTR_ERR(denali->flash_reg);
- nand_data = platform_get_resource_byname(ofdev, IORESOURCE_MEM, "nand_data");
- denali->flash_mem = devm_ioremap_resource(&ofdev->dev, nand_data);
+ nand_data = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "nand_data");
+ denali->flash_mem = devm_ioremap_resource(&pdev->dev, nand_data);
if (IS_ERR(denali->flash_mem))
return PTR_ERR(denali->flash_mem);
- dt->clk = devm_clk_get(&ofdev->dev, NULL);
+ dt->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(dt->clk)) {
- dev_err(&ofdev->dev, "no clk available\n");
+ dev_err(&pdev->dev, "no clk available\n");
return PTR_ERR(dt->clk);
}
clk_prepare_enable(dt->clk);
@@ -92,7 +94,7 @@ static int denali_dt_probe(struct platform_device *ofdev)
if (ret)
goto out_disable_clk;
- platform_set_drvdata(ofdev, dt);
+ platform_set_drvdata(pdev, dt);
return 0;
out_disable_clk:
@@ -101,9 +103,9 @@ static int denali_dt_probe(struct platform_device *ofdev)
return ret;
}
-static int denali_dt_remove(struct platform_device *ofdev)
+static int denali_dt_remove(struct platform_device *pdev)
{
- struct denali_dt *dt = platform_get_drvdata(ofdev);
+ struct denali_dt *dt = platform_get_drvdata(pdev);
denali_remove(&dt->denali);
clk_disable_unprepare(dt->clk);
--
2.7.4
The current NAND_CMD_STATUS handling is weird; it just reads the
WRITE_PROTECT register, and returns NAND_STATUS_WP if it is set.
It does not send Read Status (0x70) command, so it is not helpful
for checking the current device status.
Signed-off-by: Masahiro Yamada <[email protected]>
---
Changes in v3: None
Changes in v2:
- Newly added
drivers/mtd/nand/denali.c | 21 +++++----------------
1 file changed, 5 insertions(+), 16 deletions(-)
diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c
index 75b045e0..bca4fcd 100644
--- a/drivers/mtd/nand/denali.c
+++ b/drivers/mtd/nand/denali.c
@@ -107,21 +107,6 @@ static void write_byte_to_buf(struct denali_nand_info *denali, uint8_t byte)
denali->buf.buf[denali->buf.tail++] = byte;
}
-/* reads the status of the device */
-static void read_status(struct denali_nand_info *denali)
-{
- uint32_t cmd;
-
- /* initialize the data buffer to store status */
- reset_buf(denali);
-
- cmd = ioread32(denali->flash_reg + WRITE_PROTECT);
- if (cmd)
- write_byte_to_buf(denali, NAND_STATUS_WP);
- else
- write_byte_to_buf(denali, 0);
-}
-
/* Reset the flash controller */
static uint16_t denali_nand_reset(struct denali_nand_info *denali)
{
@@ -893,7 +878,11 @@ static void denali_cmdfunc(struct mtd_info *mtd, unsigned int cmd, int col,
switch (cmd) {
case NAND_CMD_STATUS:
- read_status(denali);
+ reset_buf(denali);
+ addr = MODE_11 | BANK(denali->flash_bank);
+ index_addr(denali, addr | 0, cmd);
+ index_addr_read_data(denali, addr | 2, &id);
+ write_byte_to_buf(denali, id);
break;
case NAND_CMD_READID:
case NAND_CMD_PARAM:
--
2.7.4
Historically, this driver tried to choose as big ECC strength as
possible, but it would be reasonable to allow DT to set a particular
ECC strength with "nand-ecc-strength" property. This is useful
when a particular ECC setting is hard-coded by firmware (or hard-
wired by boot ROM).
If no ECC strength is specified in DT, "nand-ecc-maximize" is implied
since this was the original behavior.
Signed-off-by: Masahiro Yamada <[email protected]>
Acked-by: Rob Herring <[email protected]>
---
Changes in v3: None
Changes in v2:
- Add available values in the binding document
Documentation/devicetree/bindings/mtd/denali-nand.txt | 6 ++++++
drivers/mtd/nand/denali.c | 18 ++++++++++++++++--
drivers/mtd/nand/denali_pci.c | 1 +
3 files changed, 23 insertions(+), 2 deletions(-)
diff --git a/Documentation/devicetree/bindings/mtd/denali-nand.txt b/Documentation/devicetree/bindings/mtd/denali-nand.txt
index 25313c7..647618e 100644
--- a/Documentation/devicetree/bindings/mtd/denali-nand.txt
+++ b/Documentation/devicetree/bindings/mtd/denali-nand.txt
@@ -11,6 +11,12 @@ Optional properties:
- nand-ecc-step-size: must be 512 or 1024. If not specified, default to:
512 for "altr,socfpga-denali-nand"
see nand.txt for details.
+ - nand-ecc-strength: see nand.txt for details. Available values are:
+ 8, 15 for "altr,socfpga-denali-nand"
+ - nand-ecc-maximize: see nand.txt for details
+
+Note:
+Either nand-ecc-strength or nand-ecc-maximize should be specified.
The device tree may optionally contain sub-nodes describing partitions of the
address space. See partition.txt for more detail.
diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c
index ce87b95..2f796e3 100644
--- a/drivers/mtd/nand/denali.c
+++ b/drivers/mtd/nand/denali.c
@@ -1641,9 +1641,23 @@ int denali_init(struct denali_nand_info *denali)
goto failed_req_irq;
}
- ret = denali_set_max_ecc_strength(denali);
- if (ret)
+ if (!chip->ecc.strength && !(chip->ecc.options & NAND_ECC_MAXIMIZE)) {
+ dev_info(denali->dev,
+ "No ECC strength strategy is specified. Maximizing ECC strength\n");
+ chip->ecc.options |= NAND_ECC_MAXIMIZE;
+ }
+
+ if (chip->ecc.options & NAND_ECC_MAXIMIZE) {
+ ret = denali_set_max_ecc_strength(denali);
+ if (ret)
+ goto failed_req_irq;
+ } else if (!(denali->ecc_strength_avail & BIT(chip->ecc.strength))) {
+ dev_err(denali->dev,
+ "ECC strength %d is not supported on this controller.\n",
+ chip->ecc.strength);
+ ret = -EINVAL;
goto failed_req_irq;
+ }
chip->ecc.bytes = denali_calc_ecc_bytes(chip->ecc.size,
chip->ecc.strength);
diff --git a/drivers/mtd/nand/denali_pci.c b/drivers/mtd/nand/denali_pci.c
index a1ee9f8..a39682a5 100644
--- a/drivers/mtd/nand/denali_pci.c
+++ b/drivers/mtd/nand/denali_pci.c
@@ -87,6 +87,7 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
denali->ecc_strength_avail = BIT(15) | BIT(8);
denali->caps |= DENALI_CAP_ECC_SIZE_512;
+ denali->nand.ecc.options |= NAND_ECC_MAXIMIZE;
ret = denali_init(denali);
if (ret)
--
2.7.4
Another problem of this driver is hard-coded ecc.strength and
ecc.bytes. Currently ecc.bytes is defined as follows:
#define ECC_8BITS 14
#define ECC_15BITS 26
The parameters were hard-coded because only the following two cases
are possible on Intel platforms:
- ecc.size = 512, ecc.strength = 8 --> ecc.bytes = 14
- ecc.size = 512, ecc.strength = 15 --> ecc.bytes = 26
However, they are actually customizable parameters, for example,
UniPhier platform supports the following:
- ecc.size = 1024, ecc.strength = 8 --> ecc.bytes = 14
- ecc.size = 1024, ecc.strength = 16 --> ecc.bytes = 28
- ecc.size = 1024, ecc.strength = 24 --> ecc.bytes = 42
So, we need to handle these parameters in a more generic manner.
Fortunately, the Denali User's Guide explains how to calculate the
ecc.bytes. The formula is:
ecc.bytes = 2 * CEIL(13 * ecc.strength / 16) (for ecc.size = 512)
ecc.bytes = 2 * CEIL(14 * ecc.strength / 16) (for ecc.size = 1024)
This commit allows platforms to specify denali->ecc_strength_avail,
each bit of which represents supported ECC strength.
Signed-off-by: Masahiro Yamada <[email protected]>
---
Changes in v3: None
Changes in v2: None
drivers/mtd/nand/denali.c | 73 +++++++++++++++++++++++++++----------------
drivers/mtd/nand/denali.h | 1 +
drivers/mtd/nand/denali_dt.c | 3 ++
drivers/mtd/nand/denali_pci.c | 1 +
4 files changed, 51 insertions(+), 27 deletions(-)
diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c
index 78d3b18..ce87b95 100644
--- a/drivers/mtd/nand/denali.c
+++ b/drivers/mtd/nand/denali.c
@@ -1344,13 +1344,45 @@ static void denali_hw_init(struct denali_nand_info *denali)
denali_irq_init(denali);
}
-/*
- * Althogh controller spec said SLC ECC is forceb to be 4bit,
- * but denali controller in MRST only support 15bit and 8bit ECC
- * correction
- */
-#define ECC_8BITS 14
-#define ECC_15BITS 26
+static int denali_calc_ecc_bytes(int ecc_size, int ecc_strength)
+{
+ WARN_ON(ecc_size != 512 && ecc_size != 1024);
+
+ return DIV_ROUND_UP(ecc_strength * (ecc_size == 512 ? 13 : 14), 16) * 2;
+}
+
+static int denali_set_max_ecc_strength(struct denali_nand_info *denali)
+{
+ struct nand_chip *chip = &denali->nand;
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ int oobsize = mtd->oobsize;
+ int ecc_size = chip->ecc.size;
+ int ecc_steps = mtd->writesize / chip->ecc.size;
+ int ecc_strength, ecc_bytes;
+ int max_strength = 0;
+
+ /* carve out the BBM area */
+ oobsize -= denali->bbtskipbytes;
+
+ for_each_set_bit(ecc_strength, &denali->ecc_strength_avail,
+ sizeof(denali->ecc_strength_avail) * BITS_PER_BYTE) {
+ ecc_bytes = denali_calc_ecc_bytes(ecc_size, ecc_strength);
+ if (ecc_bytes * ecc_steps > oobsize)
+ break;
+
+ max_strength = ecc_strength;
+ }
+
+ if (!max_strength) {
+ dev_err(denali->dev,
+ "Your NAND chip OOB is too small. No available ECC strength.\n");
+ return -EINVAL;
+ }
+
+ chip->ecc.strength = max_strength;
+
+ return 0;
+}
static int denali_ooblayout_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
@@ -1609,27 +1641,14 @@ int denali_init(struct denali_nand_info *denali)
goto failed_req_irq;
}
- /*
- * Denali Controller only support 15bit and 8bit ECC in MRST,
- * so just let controller do 15bit ECC for MLC and 8bit ECC for
- * SLC if possible.
- * */
- if (!nand_is_slc(chip) &&
- mtd->oobsize > denali->bbtskipbytes +
- ECC_15BITS * (mtd->writesize / chip->ecc.size)) {
- /* if MLC OOB size is large enough, use 15bit ECC*/
- chip->ecc.strength = 15;
- chip->ecc.bytes = ECC_15BITS;
- iowrite32(15, denali->flash_reg + ECC_CORRECTION);
- } else if (mtd->oobsize <
- denali->bbtskipbytes + ECC_8BITS * (mtd->writesize / chip->ecc.size)) {
- pr_err("Your NAND chip OOB is not large enough to contain 8bit ECC correction codes");
+ ret = denali_set_max_ecc_strength(denali);
+ if (ret)
goto failed_req_irq;
- } else {
- chip->ecc.strength = 8;
- chip->ecc.bytes = ECC_8BITS;
- iowrite32(8, denali->flash_reg + ECC_CORRECTION);
- }
+
+ chip->ecc.bytes = denali_calc_ecc_bytes(chip->ecc.size,
+ chip->ecc.strength);
+
+ iowrite32(chip->ecc.strength, denali->flash_reg + ECC_CORRECTION);
iowrite32(chip->ecc.size, denali->flash_reg + CFG_DATA_BLOCK_SIZE);
iowrite32(chip->ecc.size, denali->flash_reg + CFG_LAST_DATA_BLOCK_SIZE);
diff --git a/drivers/mtd/nand/denali.h b/drivers/mtd/nand/denali.h
index 7c24d82..00ce04e 100644
--- a/drivers/mtd/nand/denali.h
+++ b/drivers/mtd/nand/denali.h
@@ -348,6 +348,7 @@ struct denali_nand_info {
int bbtskipbytes;
int max_banks;
unsigned int revision;
+ unsigned long ecc_strength_avail;
unsigned int caps;
};
diff --git a/drivers/mtd/nand/denali_dt.c b/drivers/mtd/nand/denali_dt.c
index 1681a30..c3bc333 100644
--- a/drivers/mtd/nand/denali_dt.c
+++ b/drivers/mtd/nand/denali_dt.c
@@ -31,10 +31,12 @@ struct denali_dt {
struct denali_dt_data {
unsigned int revision;
+ unsigned long ecc_strength_avail;
unsigned int caps;
};
static const struct denali_dt_data denali_socfpga_data = {
+ .ecc_strength_avail = BIT(15) | BIT(8),
.caps = DENALI_CAP_HW_ECC_FIXUP |
DENALI_CAP_ECC_SIZE_512,
};
@@ -64,6 +66,7 @@ static int denali_dt_probe(struct platform_device *pdev)
data = of_device_get_match_data(&pdev->dev);
if (data) {
denali->revision = data->revision;
+ denali->ecc_strength_avail = data->ecc_strength_avail;
denali->caps = data->caps;
}
diff --git a/drivers/mtd/nand/denali_pci.c b/drivers/mtd/nand/denali_pci.c
index 5202a11..a1ee9f8 100644
--- a/drivers/mtd/nand/denali_pci.c
+++ b/drivers/mtd/nand/denali_pci.c
@@ -85,6 +85,7 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
goto failed_remap_reg;
}
+ denali->ecc_strength_avail = BIT(15) | BIT(8);
denali->caps |= DENALI_CAP_ECC_SIZE_512;
ret = denali_init(denali);
--
2.7.4
Add two compatible strings for UniPhier SoCs.
"socionext,uniphier-denali-nand-v5a" is used on UniPhier sLD3, LD4,
Pro4, sLD8 SoCs.
"socionext,uniphier-denali-nand-v5b" is used on UniPhier Pro5, PXs2,
LD6b, LD11, LD20 SoCs.
Signed-off-by: Masahiro Yamada <[email protected]>
---
Changes in v3: None
Changes in v2:
- Change the compatible strings
- Fix the ecc_strength_capability
- Override revision number for the newer one
.../devicetree/bindings/mtd/denali-nand.txt | 6 ++++++
drivers/mtd/nand/denali_dt.c | 23 ++++++++++++++++++++++
2 files changed, 29 insertions(+)
diff --git a/Documentation/devicetree/bindings/mtd/denali-nand.txt b/Documentation/devicetree/bindings/mtd/denali-nand.txt
index 647618e..0b08ea5 100644
--- a/Documentation/devicetree/bindings/mtd/denali-nand.txt
+++ b/Documentation/devicetree/bindings/mtd/denali-nand.txt
@@ -3,6 +3,8 @@
Required properties:
- compatible : should be one of the following:
"altr,socfpga-denali-nand" - for Altera SOCFPGA
+ "socionext,uniphier-denali-nand-v5a" - for Socionext UniPhier (v5a)
+ "socionext,uniphier-denali-nand-v5b" - for Socionext UniPhier (v5b)
- reg : should contain registers location and length for data and reg.
- reg-names: Should contain the reg names "nand_data" and "denali_reg"
- interrupts : The interrupt number.
@@ -10,9 +12,13 @@ Required properties:
Optional properties:
- nand-ecc-step-size: must be 512 or 1024. If not specified, default to:
512 for "altr,socfpga-denali-nand"
+ 1024 for "socionext,uniphier-denali-nand-v5a"
+ 1024 for "socionext,uniphier-denali-nand-v5b"
see nand.txt for details.
- nand-ecc-strength: see nand.txt for details. Available values are:
8, 15 for "altr,socfpga-denali-nand"
+ 8, 16, 24 for "socionext,uniphier-denali-nand-v5a"
+ 8, 16 for "socionext,uniphier-denali-nand-v5b"
- nand-ecc-maximize: see nand.txt for details
Note:
diff --git a/drivers/mtd/nand/denali_dt.c b/drivers/mtd/nand/denali_dt.c
index c3bc333..1f2f68a 100644
--- a/drivers/mtd/nand/denali_dt.c
+++ b/drivers/mtd/nand/denali_dt.c
@@ -41,11 +41,34 @@ static const struct denali_dt_data denali_socfpga_data = {
DENALI_CAP_ECC_SIZE_512,
};
+static const struct denali_dt_data denali_uniphier_v5a_data = {
+ .ecc_strength_avail = BIT(24) | BIT(16) | BIT(8),
+ .caps = DENALI_CAP_HW_ECC_FIXUP |
+ DENALI_CAP_DMA_64BIT |
+ DENALI_CAP_ECC_SIZE_1024,
+};
+
+static const struct denali_dt_data denali_uniphier_v5b_data = {
+ .revision = 0x0501,
+ .ecc_strength_avail = BIT(16) | BIT(8),
+ .caps = DENALI_CAP_HW_ECC_FIXUP |
+ DENALI_CAP_DMA_64BIT |
+ DENALI_CAP_ECC_SIZE_1024,
+};
+
static const struct of_device_id denali_nand_dt_ids[] = {
{
.compatible = "altr,socfpga-denali-nand",
.data = &denali_socfpga_data,
},
+ {
+ .compatible = "socionext,uniphier-denali-nand-v5a",
+ .data = &denali_uniphier_v5a_data,
+ },
+ {
+ .compatible = "socionext,uniphier-denali-nand-v5b",
+ .data = &denali_uniphier_v5b_data,
+ },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, denali_nand_dt_ids);
--
2.7.4
Simplify the interrupt handling and fix issues:
- The register field view of INTR_EN / INTR_STATUS is different
among IP versions. The global macro DENALI_IRQ_ALL is hard-coded
for Intel platforms. The interrupt mask should be determined at
run-time depending on the running platform.
- wait_for_irq() loops do {} while() until interested flags are
asserted. The logic can be simplified.
- The spin_lock() guard seems too complex (and suspicious in a race
condition if wait_for_completion_timeout() bails out by timeout).
- denali->complete is reused again and again, but reinit_completion()
is missing. Add it.
Re-work the code to make it more robust and easier to handle.
While we are here, also rename the jump label "failed_req_irq" to
more appropriate "disable_irq".
Signed-off-by: Masahiro Yamada <[email protected]>
---
Changes in v3: None
Changes in v2:
- Newly added
drivers/mtd/nand/denali.c | 320 +++++++++++++++++-----------------------------
drivers/mtd/nand/denali.h | 1 +
2 files changed, 118 insertions(+), 203 deletions(-)
diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c
index ea566ea..75b045e0 100644
--- a/drivers/mtd/nand/denali.c
+++ b/drivers/mtd/nand/denali.c
@@ -31,21 +31,6 @@ MODULE_LICENSE("GPL");
#define DENALI_NAND_NAME "denali-nand"
/*
- * We define a macro here that combines all interrupts this driver uses into
- * a single constant value, for convenience.
- */
-#define DENALI_IRQ_ALL (INTR__DMA_CMD_COMP | \
- INTR__ECC_TRANSACTION_DONE | \
- INTR__ECC_ERR | \
- INTR__PROGRAM_FAIL | \
- INTR__LOAD_COMP | \
- INTR__PROGRAM_COMP | \
- INTR__TIME_OUT | \
- INTR__ERASE_FAIL | \
- INTR__RST_COMP | \
- INTR__ERASE_COMP)
-
-/*
* indicates whether or not the internal value for the flash bank is
* valid or not
*/
@@ -71,20 +56,14 @@ static inline struct denali_nand_info *mtd_to_denali(struct mtd_info *mtd)
#define DENALI_READ 0
#define DENALI_WRITE 0x100
+#define DENALI_NR_BANKS 4
+
/*
* this is a helper macro that allows us to
* format the bank into the proper bits for the controller
*/
#define BANK(x) ((x) << 24)
-/* forward declarations */
-static void clear_interrupts(struct denali_nand_info *denali);
-static uint32_t wait_for_irq(struct denali_nand_info *denali,
- uint32_t irq_mask);
-static void denali_irq_enable(struct denali_nand_info *denali,
- uint32_t int_mask);
-static uint32_t read_interrupt_status(struct denali_nand_info *denali);
-
/*
* The bus interface clock, clk_x, is phase aligned with the core clock. The
* clk_x is an integral multiple N of the core clk. The value N is configured
@@ -143,22 +122,6 @@ static void read_status(struct denali_nand_info *denali)
write_byte_to_buf(denali, 0);
}
-/* resets a specific device connected to the core */
-static void reset_bank(struct denali_nand_info *denali)
-{
- uint32_t irq_status;
- uint32_t irq_mask = INTR__RST_COMP | INTR__TIME_OUT;
-
- clear_interrupts(denali);
-
- iowrite32(1 << denali->flash_bank, denali->flash_reg + DEVICE_RESET);
-
- irq_status = wait_for_irq(denali, irq_mask);
-
- if (irq_status & INTR__TIME_OUT)
- dev_err(denali->dev, "reset bank failed.\n");
-}
-
/* Reset the flash controller */
static uint16_t denali_nand_reset(struct denali_nand_info *denali)
{
@@ -201,169 +164,123 @@ static void detect_max_banks(struct denali_nand_info *denali)
denali->max_banks <<= 1;
}
-static void denali_set_intr_modes(struct denali_nand_info *denali,
- uint16_t INT_ENABLE)
+static void denali_enable_irq(struct denali_nand_info *denali)
{
- if (INT_ENABLE)
- iowrite32(1, denali->flash_reg + GLOBAL_INT_ENABLE);
- else
- iowrite32(0, denali->flash_reg + GLOBAL_INT_ENABLE);
-}
+ int i;
-/*
- * validation function to verify that the controlling software is making
- * a valid request
- */
-static inline bool is_flash_bank_valid(int flash_bank)
-{
- return flash_bank >= 0 && flash_bank < 4;
+ for (i = 0; i < DENALI_NR_BANKS; i++)
+ iowrite32(U32_MAX, denali->flash_reg + INTR_EN(i));
+ iowrite32(GLOBAL_INT_EN_FLAG, denali->flash_reg + GLOBAL_INT_ENABLE);
}
-static void denali_irq_init(struct denali_nand_info *denali)
+static void denali_disable_irq(struct denali_nand_info *denali)
{
- uint32_t int_mask;
int i;
- /* Disable global interrupts */
- denali_set_intr_modes(denali, false);
-
- int_mask = DENALI_IRQ_ALL;
-
- /* Clear all status bits */
- for (i = 0; i < denali->max_banks; ++i)
- iowrite32(0xFFFF, denali->flash_reg + INTR_STATUS(i));
-
- denali_irq_enable(denali, int_mask);
+ for (i = 0; i < DENALI_NR_BANKS; i++)
+ iowrite32(0, denali->flash_reg + INTR_EN(i));
+ iowrite32(0, denali->flash_reg + GLOBAL_INT_ENABLE);
}
-static void denali_irq_cleanup(int irqnum, struct denali_nand_info *denali)
+static void denali_clear_irq(struct denali_nand_info *denali,
+ int bank, uint32_t irq_status)
{
- denali_set_intr_modes(denali, false);
+ /* write one to clear bits */
+ iowrite32(irq_status, denali->flash_reg + INTR_STATUS(bank));
}
-static void denali_irq_enable(struct denali_nand_info *denali,
- uint32_t int_mask)
+static void denali_clear_irq_all(struct denali_nand_info *denali)
{
int i;
- for (i = 0; i < denali->max_banks; ++i)
- iowrite32(int_mask, denali->flash_reg + INTR_EN(i));
+ for (i = 0; i < DENALI_NR_BANKS; i++)
+ denali_clear_irq(denali, i, U32_MAX);
}
-/*
- * This function only returns when an interrupt that this driver cares about
- * occurs. This is to reduce the overhead of servicing interrupts
- */
-static inline uint32_t denali_irq_detected(struct denali_nand_info *denali)
+static irqreturn_t denali_isr(int irq, void *dev_id)
{
- return read_interrupt_status(denali) & DENALI_IRQ_ALL;
-}
+ struct denali_nand_info *denali = dev_id;
+ irqreturn_t ret = IRQ_NONE;
+ uint32_t irq_status;
+ int i;
-/* Interrupts are cleared by writing a 1 to the appropriate status bit */
-static inline void clear_interrupt(struct denali_nand_info *denali,
- uint32_t irq_mask)
-{
- uint32_t intr_status_reg;
+ spin_lock(&denali->irq_lock);
- intr_status_reg = INTR_STATUS(denali->flash_bank);
+ for (i = 0; i < DENALI_NR_BANKS; i++) {
+ irq_status = ioread32(denali->flash_reg + INTR_STATUS(i));
+ if (irq_status)
+ ret = IRQ_HANDLED;
- iowrite32(irq_mask, denali->flash_reg + intr_status_reg);
-}
+ denali_clear_irq(denali, i, irq_status);
-static void clear_interrupts(struct denali_nand_info *denali)
-{
- uint32_t status;
+ if (i != denali->flash_bank)
+ continue;
- spin_lock_irq(&denali->irq_lock);
+ denali->irq_status |= irq_status;
- status = read_interrupt_status(denali);
- clear_interrupt(denali, status);
+ if (denali->irq_status & denali->irq_mask)
+ complete(&denali->complete);
+ }
+
+ spin_unlock(&denali->irq_lock);
- denali->irq_status = 0x0;
- spin_unlock_irq(&denali->irq_lock);
+ return ret;
}
-static uint32_t read_interrupt_status(struct denali_nand_info *denali)
+static void denali_reset_irq(struct denali_nand_info *denali)
{
- uint32_t intr_status_reg;
-
- intr_status_reg = INTR_STATUS(denali->flash_bank);
+ unsigned long flags;
- return ioread32(denali->flash_reg + intr_status_reg);
+ spin_lock_irqsave(&denali->irq_lock, flags);
+ denali->irq_status = 0;
+ denali->irq_mask = 0;
+ spin_unlock_irqrestore(&denali->irq_lock, flags);
}
-/*
- * This is the interrupt service routine. It handles all interrupts
- * sent to this device. Note that on CE4100, this is a shared interrupt.
- */
-static irqreturn_t denali_isr(int irq, void *dev_id)
+static uint32_t denali_wait_for_irq(struct denali_nand_info *denali,
+ uint32_t irq_mask)
{
- struct denali_nand_info *denali = dev_id;
+ unsigned long time_left, flags;
uint32_t irq_status;
- irqreturn_t result = IRQ_NONE;
- spin_lock(&denali->irq_lock);
+ spin_lock_irqsave(&denali->irq_lock, flags);
- /* check to see if a valid NAND chip has been selected. */
- if (is_flash_bank_valid(denali->flash_bank)) {
- /*
- * check to see if controller generated the interrupt,
- * since this is a shared interrupt
- */
- irq_status = denali_irq_detected(denali);
- if (irq_status != 0) {
- /* handle interrupt */
- /* first acknowledge it */
- clear_interrupt(denali, irq_status);
- /*
- * store the status in the device context for someone
- * to read
- */
- denali->irq_status |= irq_status;
- /* notify anyone who cares that it happened */
- complete(&denali->complete);
- /* tell the OS that we've handled this */
- result = IRQ_HANDLED;
- }
+ irq_status = denali->irq_status;
+
+ if (irq_mask & irq_status) {
+ spin_unlock_irqrestore(&denali->irq_lock, flags);
+ return irq_status;
}
- spin_unlock(&denali->irq_lock);
- return result;
+
+ denali->irq_mask = irq_mask;
+ reinit_completion(&denali->complete);
+ spin_unlock_irqrestore(&denali->irq_lock, flags);
+
+ time_left = wait_for_completion_timeout(&denali->complete,
+ msecs_to_jiffies(1000));
+ if (!time_left) {
+ dev_err(denali->dev, "timeout while waiting for irq 0x%x\n",
+ denali->irq_mask);
+ return 0;
+ }
+
+ return denali->irq_status;
}
-static uint32_t wait_for_irq(struct denali_nand_info *denali, uint32_t irq_mask)
+/* resets a specific device connected to the core */
+static void reset_bank(struct denali_nand_info *denali)
{
- unsigned long comp_res;
- uint32_t intr_status;
- unsigned long timeout = msecs_to_jiffies(1000);
+ uint32_t irq_status;
- do {
- comp_res =
- wait_for_completion_timeout(&denali->complete, timeout);
- spin_lock_irq(&denali->irq_lock);
- intr_status = denali->irq_status;
-
- if (intr_status & irq_mask) {
- denali->irq_status &= ~irq_mask;
- spin_unlock_irq(&denali->irq_lock);
- /* our interrupt was detected */
- break;
- }
+ denali_reset_irq(denali);
- /*
- * these are not the interrupts you are looking for -
- * need to wait again
- */
- spin_unlock_irq(&denali->irq_lock);
- } while (comp_res != 0);
+ iowrite32(1 << denali->flash_bank, denali->flash_reg + DEVICE_RESET);
- if (comp_res == 0) {
- /* timeout */
- pr_err("timeout occurred, status = 0x%x, mask = 0x%x\n",
- intr_status, irq_mask);
+ irq_status = denali_wait_for_irq(denali,
+ INTR__RST_COMP | INTR__TIME_OUT);
- intr_status = 0;
- }
- return intr_status;
+ if (!(irq_status & INTR__RST_COMP))
+ dev_err(denali->dev, "reset bank failed.\n");
}
/*
@@ -397,7 +314,7 @@ static int denali_send_pipeline_cmd(struct denali_nand_info *denali,
setup_ecc_for_xfer(denali, ecc_en, transfer_spare);
- clear_interrupts(denali);
+ denali_reset_irq(denali);
addr = BANK(denali->flash_bank) | denali->page;
@@ -479,9 +396,9 @@ static int write_oob_data(struct mtd_info *mtd, uint8_t *buf, int page)
write_data_to_flash_mem(denali, buf, mtd->oobsize);
/* wait for operation to complete */
- irq_status = wait_for_irq(denali, irq_mask);
+ irq_status = denali_wait_for_irq(denali, irq_mask);
- if (irq_status == 0) {
+ if (!(irq_status & INTR__PROGRAM_COMP)) {
dev_err(denali->dev, "OOB write failed\n");
status = -EIO;
}
@@ -510,9 +427,9 @@ static void read_oob_data(struct mtd_info *mtd, uint8_t *buf, int page)
* can always use status0 bit as the
* mask is identical for each bank.
*/
- irq_status = wait_for_irq(denali, irq_mask);
+ irq_status = denali_wait_for_irq(denali, irq_mask);
- if (irq_status == 0)
+ if (!(irq_status & INTR__LOAD_COMP))
dev_err(denali->dev, "page on OOB timeout %d\n",
denali->page);
@@ -620,9 +537,9 @@ static int denali_sw_ecc_fixup(struct mtd_info *mtd,
unsigned int err_byte, err_sector, err_device;
uint8_t err_cor_value;
unsigned int prev_sector = 0;
+ uint32_t irq_status;
- /* read the ECC errors. we'll ignore them for now */
- denali_set_intr_modes(denali, false);
+ denali_reset_irq(denali);
do {
err_addr = ioread32(denali->flash_reg + ECC_ERROR_ADDRESS);
@@ -674,10 +591,9 @@ static int denali_sw_ecc_fixup(struct mtd_info *mtd,
* ECC_TRANSACTION_DONE interrupt, so here just wait for
* a while for this interrupt
*/
- while (!(read_interrupt_status(denali) & INTR__ECC_TRANSACTION_DONE))
- cpu_relax();
- clear_interrupts(denali);
- denali_set_intr_modes(denali, true);
+ irq_status = denali_wait_for_irq(denali, INTR__ECC_TRANSACTION_DONE);
+ if (!(irq_status & INTR__ECC_TRANSACTION_DONE))
+ return -EIO;
return max_bitflips;
}
@@ -778,15 +694,14 @@ static int write_page(struct mtd_info *mtd, struct nand_chip *chip,
dma_sync_single_for_device(denali->dev, addr, size, DMA_TO_DEVICE);
- clear_interrupts(denali);
+ denali_reset_irq(denali);
denali_enable_dma(denali, true);
denali_setup_dma(denali, DENALI_WRITE);
/* wait for operation to complete */
- irq_status = wait_for_irq(denali, irq_mask);
-
- if (irq_status == 0) {
+ irq_status = denali_wait_for_irq(denali, irq_mask);
+ if (!(irq_status & INTR__DMA_CMD_COMP)) {
dev_err(denali->dev, "timeout on write_page (type = %d)\n",
raw_xfer);
ret = -EIO;
@@ -865,11 +780,11 @@ static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip,
denali_enable_dma(denali, true);
dma_sync_single_for_device(denali->dev, addr, size, DMA_FROM_DEVICE);
- clear_interrupts(denali);
+ denali_reset_irq(denali);
denali_setup_dma(denali, DENALI_READ);
/* wait for operation to complete */
- irq_status = wait_for_irq(denali, irq_mask);
+ irq_status = denali_wait_for_irq(denali, irq_mask);
dma_sync_single_for_cpu(denali->dev, addr, size, DMA_FROM_DEVICE);
@@ -901,6 +816,7 @@ static int denali_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
dma_addr_t addr = denali->buf.dma_buf;
size_t size = mtd->writesize + mtd->oobsize;
uint32_t irq_mask = INTR__DMA_CMD_COMP;
+ uint32_t irq_status;
denali->page = page;
@@ -909,11 +825,13 @@ static int denali_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
dma_sync_single_for_device(denali->dev, addr, size, DMA_FROM_DEVICE);
- clear_interrupts(denali);
+ denali_reset_irq(denali);
denali_setup_dma(denali, DENALI_READ);
/* wait for operation to complete */
- wait_for_irq(denali, irq_mask);
+ irq_status = denali_wait_for_irq(denali, irq_mask);
+ if (irq_status & INTR__DMA_CMD_COMP)
+ return -ETIMEDOUT;
dma_sync_single_for_cpu(denali->dev, addr, size, DMA_FROM_DEVICE);
@@ -940,9 +858,7 @@ static void denali_select_chip(struct mtd_info *mtd, int chip)
{
struct denali_nand_info *denali = mtd_to_denali(mtd);
- spin_lock_irq(&denali->irq_lock);
denali->flash_bank = chip;
- spin_unlock_irq(&denali->irq_lock);
}
static int denali_waitfunc(struct mtd_info *mtd, struct nand_chip *chip)
@@ -953,19 +869,19 @@ static int denali_waitfunc(struct mtd_info *mtd, struct nand_chip *chip)
static int denali_erase(struct mtd_info *mtd, int page)
{
struct denali_nand_info *denali = mtd_to_denali(mtd);
-
uint32_t cmd, irq_status;
- clear_interrupts(denali);
+ denali_reset_irq(denali);
/* setup page read request for access type */
cmd = MODE_10 | BANK(denali->flash_bank) | page;
index_addr(denali, cmd, 0x1);
/* wait for erase to complete or failure to occur */
- irq_status = wait_for_irq(denali, INTR__ERASE_COMP | INTR__ERASE_FAIL);
+ irq_status = denali_wait_for_irq(denali,
+ INTR__ERASE_COMP | INTR__ERASE_FAIL);
- return irq_status & INTR__ERASE_FAIL ? NAND_STATUS_FAIL : PASS;
+ return irq_status & INTR__ERASE_COMP ? 0 : NAND_STATUS_FAIL;
}
static void denali_cmdfunc(struct mtd_info *mtd, unsigned int cmd, int col,
@@ -1153,7 +1069,6 @@ static void denali_hw_init(struct denali_nand_info *denali)
/* Should set value for these registers when init */
iowrite32(0, denali->flash_reg + TWO_ROW_ADDR_CYCLES);
iowrite32(1, denali->flash_reg + ECC_ENABLE);
- denali_irq_init(denali);
}
static int denali_calc_ecc_bytes(int ecc_size, int ecc_strength)
@@ -1271,9 +1186,6 @@ static void denali_drv_init(struct denali_nand_info *denali)
/* indicate that MTD has not selected a valid bank yet */
denali->flash_bank = CHIP_SELECT_INVALID;
-
- /* initialize our irq_status variable to indicate no interrupts */
- denali->irq_status = 0;
}
static int denali_multidev_fixup(struct denali_nand_info *denali)
@@ -1343,6 +1255,8 @@ int denali_init(struct denali_nand_info *denali)
denali_hw_init(denali);
denali_drv_init(denali);
+ denali_clear_irq_all(denali);
+
/* Request IRQ after all the hardware initialization is finished */
ret = devm_request_irq(denali->dev, denali->irq, denali_isr,
IRQF_SHARED, DENALI_NAND_NAME, denali);
@@ -1351,8 +1265,8 @@ int denali_init(struct denali_nand_info *denali)
return ret;
}
- /* now that our ISR is registered, we can enable interrupts */
- denali_set_intr_modes(denali, true);
+ denali_enable_irq(denali);
+
nand_set_flash_node(chip, denali->dev->of_node);
/* Fallback to the default name if DT did not give "label" property */
if (!mtd->name)
@@ -1374,7 +1288,7 @@ int denali_init(struct denali_nand_info *denali)
*/
ret = nand_scan_ident(mtd, denali->max_banks, NULL);
if (ret)
- goto failed_req_irq;
+ goto disable_irq;
/* allocate the right size buffer now */
devm_kfree(denali->dev, denali->buf.buf);
@@ -1383,7 +1297,7 @@ int denali_init(struct denali_nand_info *denali)
GFP_KERNEL);
if (!denali->buf.buf) {
ret = -ENOMEM;
- goto failed_req_irq;
+ goto disable_irq;
}
ret = dma_set_mask(denali->dev,
@@ -1391,7 +1305,7 @@ int denali_init(struct denali_nand_info *denali)
64 : 32));
if (ret) {
dev_err(denali->dev, "No usable DMA configuration\n");
- goto failed_req_irq;
+ goto disable_irq;
}
denali->buf.dma_buf = dma_map_single(denali->dev, denali->buf.buf,
@@ -1400,7 +1314,7 @@ int denali_init(struct denali_nand_info *denali)
if (dma_mapping_error(denali->dev, denali->buf.dma_buf)) {
dev_err(denali->dev, "Failed to map DMA buffer\n");
ret = -EIO;
- goto failed_req_irq;
+ goto disable_irq;
}
/*
@@ -1442,7 +1356,7 @@ int denali_init(struct denali_nand_info *denali)
!(chip->ecc.size == 1024 && denali->caps & DENALI_CAP_ECC_SIZE_1024)) {
dev_err(denali->dev, "ECC size %d is not supported on this controller",
chip->ecc.size);
- goto failed_req_irq;
+ goto disable_irq;
}
if (!chip->ecc.strength && !(chip->ecc.options & NAND_ECC_MAXIMIZE)) {
@@ -1454,13 +1368,13 @@ int denali_init(struct denali_nand_info *denali)
if (chip->ecc.options & NAND_ECC_MAXIMIZE) {
ret = denali_set_max_ecc_strength(denali);
if (ret)
- goto failed_req_irq;
+ goto disable_irq;
} else if (!(denali->ecc_strength_avail & BIT(chip->ecc.strength))) {
dev_err(denali->dev,
"ECC strength %d is not supported on this controller.\n",
chip->ecc.strength);
ret = -EINVAL;
- goto failed_req_irq;
+ goto disable_irq;
}
chip->ecc.bytes = denali_calc_ecc_bytes(chip->ecc.size,
@@ -1493,21 +1407,21 @@ int denali_init(struct denali_nand_info *denali)
ret = denali_multidev_fixup(denali);
if (ret)
- goto failed_req_irq;
+ goto disable_irq;
ret = nand_scan_tail(mtd);
if (ret)
- goto failed_req_irq;
+ goto disable_irq;
ret = mtd_device_register(mtd, NULL, 0);
if (ret) {
dev_err(denali->dev, "Failed to register MTD: %d\n", ret);
- goto failed_req_irq;
+ goto disable_irq;
}
return 0;
-failed_req_irq:
- denali_irq_cleanup(denali->irq, denali);
+disable_irq:
+ denali_disable_irq(denali);
return ret;
}
@@ -1525,7 +1439,7 @@ void denali_remove(struct denali_nand_info *denali)
int bufsize = mtd->writesize + mtd->oobsize;
nand_release(mtd);
- denali_irq_cleanup(denali->irq, denali);
+ denali_disable_irq(denali);
dma_unmap_single(denali->dev, denali->buf.dma_buf, bufsize,
DMA_BIDIRECTIONAL);
}
diff --git a/drivers/mtd/nand/denali.h b/drivers/mtd/nand/denali.h
index e55bb2e..7797340 100644
--- a/drivers/mtd/nand/denali.h
+++ b/drivers/mtd/nand/denali.h
@@ -326,6 +326,7 @@ struct denali_nand_info {
/* elements used by ISR */
struct completion complete;
spinlock_t irq_lock;
+ uint32_t irq_mask;
uint32_t irq_status;
int irq;
--
2.7.4
Now struct nand_buf has only two members, so I see no reason for the
separation.
Signed-off-by: Masahiro Yamada <[email protected]>
---
Changes in v3: None
Changes in v2:
- Newly added
drivers/mtd/nand/denali.c | 29 ++++++++++++++---------------
drivers/mtd/nand/denali.h | 8 ++------
2 files changed, 16 insertions(+), 21 deletions(-)
diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c
index 00d0e9d..5feeaf5 100644
--- a/drivers/mtd/nand/denali.c
+++ b/drivers/mtd/nand/denali.c
@@ -640,7 +640,7 @@ static int write_page(struct mtd_info *mtd, struct nand_chip *chip,
const uint8_t *buf, int page, bool raw_xfer)
{
struct denali_nand_info *denali = mtd_to_denali(mtd);
- dma_addr_t addr = denali->buf.dma_buf;
+ dma_addr_t addr = denali->dma_addr;
size_t size = mtd->writesize + mtd->oobsize;
uint32_t irq_status;
uint32_t irq_mask = INTR__DMA_CMD_COMP | INTR__PROGRAM_FAIL;
@@ -654,11 +654,11 @@ static int write_page(struct mtd_info *mtd, struct nand_chip *chip,
setup_ecc_for_xfer(denali, !raw_xfer, raw_xfer);
/* copy buffer into DMA buffer */
- memcpy(denali->buf.buf, buf, mtd->writesize);
+ memcpy(denali->buf, buf, mtd->writesize);
if (raw_xfer) {
/* transfer the data to the spare area */
- memcpy(denali->buf.buf + mtd->writesize,
+ memcpy(denali->buf + mtd->writesize,
chip->oob_poi,
mtd->oobsize);
}
@@ -735,7 +735,7 @@ static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip,
uint8_t *buf, int oob_required, int page)
{
struct denali_nand_info *denali = mtd_to_denali(mtd);
- dma_addr_t addr = denali->buf.dma_buf;
+ dma_addr_t addr = denali->dma_addr;
size_t size = mtd->writesize + mtd->oobsize;
uint32_t irq_status;
uint32_t irq_mask = denali->caps & DENALI_CAP_HW_ECC_FIXUP ?
@@ -757,7 +757,7 @@ static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip,
dma_sync_single_for_cpu(denali->dev, addr, size, DMA_FROM_DEVICE);
- memcpy(buf, denali->buf.buf, mtd->writesize);
+ memcpy(buf, denali->buf, mtd->writesize);
if (denali->caps & DENALI_CAP_HW_ECC_FIXUP)
stat = denali_hw_ecc_fixup(mtd, denali, &uncor_ecc_flags);
@@ -782,7 +782,7 @@ static int denali_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
uint8_t *buf, int oob_required, int page)
{
struct denali_nand_info *denali = mtd_to_denali(mtd);
- dma_addr_t addr = denali->buf.dma_buf;
+ dma_addr_t addr = denali->dma_addr;
size_t size = mtd->writesize + mtd->oobsize;
uint32_t irq_mask = INTR__DMA_CMD_COMP;
uint32_t irq_status;
@@ -804,8 +804,8 @@ static int denali_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
denali_enable_dma(denali, false);
- memcpy(buf, denali->buf.buf, mtd->writesize);
- memcpy(chip->oob_poi, denali->buf.buf + mtd->writesize, mtd->oobsize);
+ memcpy(buf, denali->buf, mtd->writesize);
+ memcpy(chip->oob_poi, denali->buf + mtd->writesize, mtd->oobsize);
return 0;
}
@@ -1230,10 +1230,9 @@ int denali_init(struct denali_nand_info *denali)
if (ret)
goto disable_irq;
- denali->buf.buf = devm_kzalloc(denali->dev,
- mtd->writesize + mtd->oobsize,
- GFP_KERNEL);
- if (!denali->buf.buf) {
+ denali->buf = devm_kzalloc(denali->dev, mtd->writesize + mtd->oobsize,
+ GFP_KERNEL);
+ if (!denali->buf) {
ret = -ENOMEM;
goto disable_irq;
}
@@ -1246,10 +1245,10 @@ int denali_init(struct denali_nand_info *denali)
goto disable_irq;
}
- denali->buf.dma_buf = dma_map_single(denali->dev, denali->buf.buf,
+ denali->dma_addr = dma_map_single(denali->dev, denali->buf,
mtd->writesize + mtd->oobsize,
DMA_BIDIRECTIONAL);
- if (dma_mapping_error(denali->dev, denali->buf.dma_buf)) {
+ if (dma_mapping_error(denali->dev, denali->dma_addr)) {
dev_err(denali->dev, "Failed to map DMA buffer\n");
ret = -EIO;
goto disable_irq;
@@ -1378,7 +1377,7 @@ void denali_remove(struct denali_nand_info *denali)
nand_release(mtd);
denali_disable_irq(denali);
- dma_unmap_single(denali->dev, denali->buf.dma_buf, bufsize,
+ dma_unmap_single(denali->dev, denali->dma_addr, bufsize,
DMA_BIDIRECTIONAL);
}
EXPORT_SYMBOL(denali_remove);
diff --git a/drivers/mtd/nand/denali.h b/drivers/mtd/nand/denali.h
index 5f917a0..3ca6218 100644
--- a/drivers/mtd/nand/denali.h
+++ b/drivers/mtd/nand/denali.h
@@ -306,16 +306,10 @@
#define MODE_10 0x08000000
#define MODE_11 0x0C000000
-struct nand_buf {
- uint8_t *buf;
- dma_addr_t dma_buf;
-};
-
struct denali_nand_info {
struct nand_chip nand;
unsigned long clk_x_rate; /* bus interface clock rate */
int flash_bank; /* currently selected chip */
- struct nand_buf buf;
struct device *dev;
void __iomem *flash_reg; /* Register Interface */
void __iomem *flash_mem; /* Host Data/Command Interface */
@@ -327,6 +321,8 @@ struct denali_nand_info {
uint32_t irq_status;
int irq;
+ void *buf;
+ dma_addr_t dma_addr;
int devnum; /* represent how many nands connected */
int bbtskipbytes;
int max_banks;
--
2.7.4
NAND_CMD_PARAM is not working at all due to multiple bugs.
[1] The command 0x90 issued instead of 0xec
The command code 0x90 is hard-code as
index_addr(denali, addr | 0, 0x90)
So, Read ID (0x90) command is sent to the device instead of Read
Parameter Page (0xec).
[2] only first 8 bytes are read
Even if [1] is fixed, the current implementation is problematic.
The only first 8 bytes are read by MAP11 command, and put into the
temporal buffer:
for (i = 0; i < 8; i++) {
index_addr_read_data(denali, addr | 2, &id);
write_byte_to_buf(denali, id);
}
Obviously, this is not sufficient for NAND_CMD_PARAM; the ONFi
parameters are 256-byte long. This is still insufficient.
As you see in nand_flash_detect_onfi() reads out (256 * 3) bytes
at maximum (Redundant Parameter Pages). However, changing the loop
to for (i = 0; i < 768; i++) is a crazy idea. At the point of the
chip->cmdfunc() call, we cannot know how many times chip->read_byte()
will be called. So, pre-reading enough number of bytes in the
chip->cmdfunc() is a design mistake.
[3] no wait for R/B#
The current code handles NAND_CMD_READID and NAND_CMD_PARAM in the
same way, but this is also wrong. The difference between them is
that Read ID command does not toggle R/B# whereas the Read Parameter
Page command requires R/B#. Without the wait for R/B# interrupt,
wrong data are retrieved.
In order to fix those problems, data read cycle of the MAP11 command
has been moved to chip->read_byte(). Data are read out as needed.
Another good thing is early temporal buffer is not needed any more.
The ugly devm_kzalloc()/devm_kfree() dance has been killed.
Signed-off-by: Masahiro Yamada <[email protected]>
---
Changes in v3: None
Changes in v2:
- Newly added
drivers/mtd/nand/denali.c | 95 +++++++++++++++--------------------------------
drivers/mtd/nand/denali.h | 2 -
2 files changed, 30 insertions(+), 67 deletions(-)
diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c
index bca4fcd..0e5e490 100644
--- a/drivers/mtd/nand/denali.c
+++ b/drivers/mtd/nand/denali.c
@@ -85,28 +85,6 @@ static void index_addr(struct denali_nand_info *denali,
iowrite32(data, denali->flash_mem + 0x10);
}
-/* Perform an indexed read of the device */
-static void index_addr_read_data(struct denali_nand_info *denali,
- uint32_t address, uint32_t *pdata)
-{
- iowrite32(address, denali->flash_mem);
- *pdata = ioread32(denali->flash_mem + 0x10);
-}
-
-/*
- * We need to buffer some data for some of the NAND core routines.
- * The operations manage buffering that data.
- */
-static void reset_buf(struct denali_nand_info *denali)
-{
- denali->buf.head = denali->buf.tail = 0;
-}
-
-static void write_byte_to_buf(struct denali_nand_info *denali, uint8_t byte)
-{
- denali->buf.buf[denali->buf.tail++] = byte;
-}
-
/* Reset the flash controller */
static uint16_t denali_nand_reset(struct denali_nand_info *denali)
{
@@ -286,6 +264,15 @@ static void setup_ecc_for_xfer(struct denali_nand_info *denali, bool ecc_en,
iowrite32(transfer_spare_flag, denali->flash_reg + TRANSFER_SPARE_REG);
}
+static uint8_t denali_read_byte(struct mtd_info *mtd)
+{
+ struct denali_nand_info *denali = mtd_to_denali(mtd);
+
+ iowrite32(MODE_11 | BANK(denali->flash_bank) | 2, denali->flash_mem);
+
+ return ioread32(denali->flash_mem + 0x10);
+}
+
/*
* sends a pipeline command operation to the controller. See the Denali NAND
* controller's user guide for more information (section 4.2.3.6).
@@ -828,17 +815,6 @@ 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)
-{
- struct denali_nand_info *denali = mtd_to_denali(mtd);
- uint8_t result = 0xff;
-
- if (denali->buf.head < denali->buf.tail)
- result = denali->buf.buf[denali->buf.head++];
-
- return result;
-}
-
static void denali_select_chip(struct mtd_info *mtd, int chip)
{
struct denali_nand_info *denali = mtd_to_denali(mtd);
@@ -873,43 +849,40 @@ static void denali_cmdfunc(struct mtd_info *mtd, unsigned int cmd, int col,
int page)
{
struct denali_nand_info *denali = mtd_to_denali(mtd);
- uint32_t addr, id;
- int i;
+ uint32_t addr, irq_status;
+ int wait_ready = 0;
switch (cmd) {
- case NAND_CMD_STATUS:
- reset_buf(denali);
- addr = MODE_11 | BANK(denali->flash_bank);
- index_addr(denali, addr | 0, cmd);
- index_addr_read_data(denali, addr | 2, &id);
- write_byte_to_buf(denali, id);
+ case NAND_CMD_PARAM:
+ wait_ready = 1;
break;
+ case NAND_CMD_STATUS:
case NAND_CMD_READID:
- case NAND_CMD_PARAM:
- reset_buf(denali);
- /*
- * sometimes ManufactureId read from register is not right
- * e.g. some of Micron MT29F32G08QAA MLC NAND chips
- * So here we send READID cmd to NAND insteand
- */
- addr = MODE_11 | BANK(denali->flash_bank);
- index_addr(denali, addr | 0, 0x90);
- index_addr(denali, addr | 1, col);
- for (i = 0; i < 8; i++) {
- index_addr_read_data(denali, addr | 2, &id);
- write_byte_to_buf(denali, id);
- }
break;
case NAND_CMD_RESET:
reset_bank(denali);
break;
case NAND_CMD_READOOB:
/* TODO: Read OOB data */
- break;
+ return;
default:
pr_err(": unsupported command received 0x%x\n", cmd);
- break;
+ return;
}
+
+ denali_reset_irq(denali);
+
+ addr = MODE_11 | BANK(denali->flash_bank);
+ index_addr(denali, addr | 0, cmd);
+ if (col != -1)
+ index_addr(denali, addr | 1, col);
+
+ if (!wait_ready)
+ return;
+
+ irq_status = denali_wait_for_irq(denali, INTR__INT_ACT);
+ if (!(irq_status & INTR__INT_ACT))
+ dev_err(denali->dev, "failed to issue command 0x%x\n", cmd);
}
#define DIV_ROUND_DOWN_ULL(ll, d) \
@@ -1234,12 +1207,6 @@ int denali_init(struct denali_nand_info *denali)
struct mtd_info *mtd = nand_to_mtd(chip);
int ret;
- /* allocate a temporary buffer for nand_scan_ident() */
- denali->buf.buf = devm_kzalloc(denali->dev, PAGE_SIZE,
- GFP_DMA | GFP_KERNEL);
- if (!denali->buf.buf)
- return -ENOMEM;
-
mtd->dev.parent = denali->dev;
denali_hw_init(denali);
denali_drv_init(denali);
@@ -1279,8 +1246,6 @@ int denali_init(struct denali_nand_info *denali)
if (ret)
goto disable_irq;
- /* allocate the right size buffer now */
- devm_kfree(denali->dev, denali->buf.buf);
denali->buf.buf = devm_kzalloc(denali->dev,
mtd->writesize + mtd->oobsize,
GFP_KERNEL);
diff --git a/drivers/mtd/nand/denali.h b/drivers/mtd/nand/denali.h
index 7797340..7d64167 100644
--- a/drivers/mtd/nand/denali.h
+++ b/drivers/mtd/nand/denali.h
@@ -307,8 +307,6 @@
#define MODE_11 0x0C000000
struct nand_buf {
- int head;
- int tail;
uint8_t *buf;
dma_addr_t dma_buf;
};
--
2.7.4
This driver was originally written for the Intel MRST platform with
several platform specific parameters hard-coded. Another thing we
need to fix is the hard-coded ECC step size. Currently, it is
defined as follows:
#define ECC_SECTOR_SIZE 512
(somehow, it is defined in both denali.c and denali.h)
This must be avoided because the Denali IP supports 1024B ECC size
as well. The Denali User's Guide also says supporting both 512B and
1024B ECC sectors is possible, though it would require instantiation
of two different ECC circuits. So, possible cases are:
[1] only 512B ECC size is supported
[2] only 1024B ECC size is supported
[3] both 512B and 1024B ECC sizes are supported
Newer versions of this IP need ecc.size and ecc.steps explicitly
set up via the following registers:
CFG_DATA_BLOCK_SIZE (0x6b0)
CFG_LAST_DATA_BLOCK_SIZE (0x6c0)
CFG_NUM_DATA_BLOCKS (0x6d0)
Older versions do not have such registers (they were reserved), so
write accesses are safely ignored.
This commit adds new flags DENALI_CAP_ECC_SIZE_{512,1024}.
The DT property "nand-ecc-step-size" is still optional; a reasonable
default will be chosen for [1] and [2]. For case [3], users can
force ECC size via DT in case firmware hard-codes ECC settings.
If not specified, the driver will use chip's ECC requirement as a
hint to decide the ECC size.
Signed-off-by: Masahiro Yamada <[email protected]>
Acked-by: Rob Herring <[email protected]>
---
Changes in v3:
- Move DENALI_CAP_ define out of struct denali_nand_info
- Use chip->ecc_step_ds as a hint to choose chip->ecc.size
where possible
Changes in v2:
- Change the capability prefix DENALI_CAPS_ -> DENALI_CAP_
- Make ECC 512 cap and ECC 1024 cap independent
- Set up three CFG_... registers
.../devicetree/bindings/mtd/denali-nand.txt | 5 +++
drivers/mtd/nand/denali.c | 51 ++++++++++++++++------
drivers/mtd/nand/denali.h | 12 ++++-
drivers/mtd/nand/denali_dt.c | 3 +-
drivers/mtd/nand/denali_pci.c | 2 +
5 files changed, 57 insertions(+), 16 deletions(-)
diff --git a/Documentation/devicetree/bindings/mtd/denali-nand.txt b/Documentation/devicetree/bindings/mtd/denali-nand.txt
index e593bbe..25313c7 100644
--- a/Documentation/devicetree/bindings/mtd/denali-nand.txt
+++ b/Documentation/devicetree/bindings/mtd/denali-nand.txt
@@ -7,6 +7,11 @@ Required properties:
- reg-names: Should contain the reg names "nand_data" and "denali_reg"
- interrupts : The interrupt number.
+Optional properties:
+ - nand-ecc-step-size: must be 512 or 1024. If not specified, default to:
+ 512 for "altr,socfpga-denali-nand"
+ see nand.txt for details.
+
The device tree may optionally contain sub-nodes describing partitions of the
address space. See partition.txt for more detail.
diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c
index 16634df..78d3b18 100644
--- a/drivers/mtd/nand/denali.c
+++ b/drivers/mtd/nand/denali.c
@@ -886,8 +886,6 @@ static int denali_hw_ecc_fixup(struct mtd_info *mtd,
return max_bitflips;
}
-#define ECC_SECTOR_SIZE 512
-
#define ECC_SECTOR(x) (((x) & ECC_ERROR_ADDRESS__SECTOR_NR) >> 12)
#define ECC_BYTE(x) (((x) & ECC_ERROR_ADDRESS__OFFSET))
#define ECC_CORRECTION_VALUE(x) ((x) & ERR_CORRECTION_INFO__BYTEMASK)
@@ -899,6 +897,7 @@ static int denali_sw_ecc_fixup(struct mtd_info *mtd,
struct denali_nand_info *denali,
unsigned long *uncor_ecc_flags, uint8_t *buf)
{
+ unsigned int ecc_size = denali->nand.ecc.size;
unsigned int bitflips = 0;
unsigned int max_bitflips = 0;
uint32_t err_addr, err_cor_info;
@@ -928,9 +927,9 @@ static int denali_sw_ecc_fixup(struct mtd_info *mtd,
* an erased sector.
*/
*uncor_ecc_flags |= BIT(err_sector);
- } else if (err_byte < ECC_SECTOR_SIZE) {
+ } else if (err_byte < ecc_size) {
/*
- * If err_byte is larger than ECC_SECTOR_SIZE, means error
+ * If err_byte is larger than ecc_size, means error
* happened in OOB, so we ignore it. It's no need for
* us to correct it err_device is represented the NAND
* error bits are happened in if there are more than
@@ -939,7 +938,7 @@ static int denali_sw_ecc_fixup(struct mtd_info *mtd,
int offset;
unsigned int flips_in_byte;
- offset = (err_sector * ECC_SECTOR_SIZE + err_byte) *
+ offset = (err_sector * ecc_size + err_byte) *
denali->devnum + err_device;
/* correct the ECC error */
@@ -1587,21 +1586,43 @@ int denali_init(struct denali_nand_info *denali)
chip->options |= NAND_NO_SUBPAGE_WRITE;
/*
+ * If the controller supports both 512B and 1024B ECC, and DT does not
+ * specify "nand-ecc-step-size", use the chip's requirement for a hint
+ * to choose ECC size.
+ */
+ if (!chip->ecc.size &&
+ denali->caps & DENALI_CAP_ECC_SIZE_512 &&
+ denali->caps & DENALI_CAP_ECC_SIZE_1024)
+ chip->ecc.size = chip->ecc_step_ds;
+
+ if (!chip->ecc.size) {
+ if (denali->caps & DENALI_CAP_ECC_SIZE_512)
+ chip->ecc.size = 512;
+ if (denali->caps & DENALI_CAP_ECC_SIZE_1024)
+ chip->ecc.size = 1024;
+ }
+
+ if (!(chip->ecc.size == 512 && denali->caps & DENALI_CAP_ECC_SIZE_512) &&
+ !(chip->ecc.size == 1024 && denali->caps & DENALI_CAP_ECC_SIZE_1024)) {
+ dev_err(denali->dev, "ECC size %d is not supported on this controller",
+ chip->ecc.size);
+ goto failed_req_irq;
+ }
+
+ /*
* Denali Controller only support 15bit and 8bit ECC in MRST,
* so just let controller do 15bit ECC for MLC and 8bit ECC for
* SLC if possible.
* */
if (!nand_is_slc(chip) &&
- (mtd->oobsize > (denali->bbtskipbytes +
- ECC_15BITS * (mtd->writesize /
- ECC_SECTOR_SIZE)))) {
+ mtd->oobsize > denali->bbtskipbytes +
+ ECC_15BITS * (mtd->writesize / chip->ecc.size)) {
/* if MLC OOB size is large enough, use 15bit ECC*/
chip->ecc.strength = 15;
chip->ecc.bytes = ECC_15BITS;
iowrite32(15, denali->flash_reg + ECC_CORRECTION);
- } else if (mtd->oobsize < (denali->bbtskipbytes +
- ECC_8BITS * (mtd->writesize /
- ECC_SECTOR_SIZE))) {
+ } else if (mtd->oobsize <
+ denali->bbtskipbytes + ECC_8BITS * (mtd->writesize / chip->ecc.size)) {
pr_err("Your NAND chip OOB is not large enough to contain 8bit ECC correction codes");
goto failed_req_irq;
} else {
@@ -1610,10 +1631,14 @@ int denali_init(struct denali_nand_info *denali)
iowrite32(8, denali->flash_reg + ECC_CORRECTION);
}
+ iowrite32(chip->ecc.size, denali->flash_reg + CFG_DATA_BLOCK_SIZE);
+ iowrite32(chip->ecc.size, denali->flash_reg + CFG_LAST_DATA_BLOCK_SIZE);
+ /* chip->ecc.steps is set by nand_scan_tail(); not available here */
+ iowrite32(mtd->writesize / chip->ecc.size,
+ denali->flash_reg + CFG_NUM_DATA_BLOCKS);
+
mtd_set_ooblayout(mtd, &denali_ooblayout_ops);
- /* override the default read operations */
- chip->ecc.size = ECC_SECTOR_SIZE;
chip->ecc.read_page = denali_read_page;
chip->ecc.read_page_raw = denali_read_page_raw;
chip->ecc.write_page = denali_write_page;
diff --git a/drivers/mtd/nand/denali.h b/drivers/mtd/nand/denali.h
index ec00485..7c24d82 100644
--- a/drivers/mtd/nand/denali.h
+++ b/drivers/mtd/nand/denali.h
@@ -265,6 +265,14 @@
#define ECC_COR_INFO__MAX_ERRORS 0x007f
#define ECC_COR_INFO__UNCOR_ERR 0x0080
+#define CFG_DATA_BLOCK_SIZE 0x6b0
+
+#define CFG_LAST_DATA_BLOCK_SIZE 0x6c0
+
+#define CFG_NUM_DATA_BLOCKS 0x6d0
+
+#define CFG_META_DATA_SIZE 0x6e0
+
#define DMA_ENABLE 0x700
#define DMA_ENABLE__FLAG 0x0001
@@ -307,8 +315,6 @@
#define MODE_10 0x08000000
#define MODE_11 0x0C000000
-#define ECC_SECTOR_SIZE 512
-
struct nand_buf {
int head;
int tail;
@@ -347,6 +353,8 @@ struct denali_nand_info {
#define DENALI_CAP_HW_ECC_FIXUP BIT(0)
#define DENALI_CAP_DMA_64BIT BIT(1)
+#define DENALI_CAP_ECC_SIZE_512 BIT(2)
+#define DENALI_CAP_ECC_SIZE_1024 BIT(3)
extern int denali_init(struct denali_nand_info *denali);
extern void denali_remove(struct denali_nand_info *denali);
diff --git a/drivers/mtd/nand/denali_dt.c b/drivers/mtd/nand/denali_dt.c
index df9ef36..1681a30 100644
--- a/drivers/mtd/nand/denali_dt.c
+++ b/drivers/mtd/nand/denali_dt.c
@@ -35,7 +35,8 @@ struct denali_dt_data {
};
static const struct denali_dt_data denali_socfpga_data = {
- .caps = DENALI_CAP_HW_ECC_FIXUP,
+ .caps = DENALI_CAP_HW_ECC_FIXUP |
+ DENALI_CAP_ECC_SIZE_512,
};
static const struct of_device_id denali_nand_dt_ids[] = {
diff --git a/drivers/mtd/nand/denali_pci.c b/drivers/mtd/nand/denali_pci.c
index ac84323..5202a11 100644
--- a/drivers/mtd/nand/denali_pci.c
+++ b/drivers/mtd/nand/denali_pci.c
@@ -85,6 +85,8 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
goto failed_remap_reg;
}
+ denali->caps |= DENALI_CAP_ECC_SIZE_512;
+
ret = denali_init(denali);
if (ret)
goto failed_remap_mem;
--
2.7.4
On Thu, 30 Mar 2017 15:46:00 +0900
Masahiro Yamada <[email protected]> wrote:
> Historically, this driver tried to choose as big ECC strength as
> possible, but it would be reasonable to allow DT to set a particular
> ECC strength with "nand-ecc-strength" property. This is useful
> when a particular ECC setting is hard-coded by firmware (or hard-
> wired by boot ROM).
>
> If no ECC strength is specified in DT, "nand-ecc-maximize" is implied
> since this was the original behavior.
You said there is currently no DT users, so how about changing the
"fallback to ECC maximization" behavior for DT users, and instead of
maximizing the ECC strength take the NAND requirements into account
(chip->ecc_strength_ds).
For PCI users, you explicitly set the NAND_ECC_MAXIMIZE flag, so it
shouldn't be a problem (you're still backward compatible).
>
> Signed-off-by: Masahiro Yamada <[email protected]>
> Acked-by: Rob Herring <[email protected]>
> ---
>
> Changes in v3: None
> Changes in v2:
> - Add available values in the binding document
>
> Documentation/devicetree/bindings/mtd/denali-nand.txt | 6 ++++++
> drivers/mtd/nand/denali.c | 18 ++++++++++++++++--
> drivers/mtd/nand/denali_pci.c | 1 +
> 3 files changed, 23 insertions(+), 2 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/mtd/denali-nand.txt b/Documentation/devicetree/bindings/mtd/denali-nand.txt
> index 25313c7..647618e 100644
> --- a/Documentation/devicetree/bindings/mtd/denali-nand.txt
> +++ b/Documentation/devicetree/bindings/mtd/denali-nand.txt
> @@ -11,6 +11,12 @@ Optional properties:
> - nand-ecc-step-size: must be 512 or 1024. If not specified, default to:
> 512 for "altr,socfpga-denali-nand"
> see nand.txt for details.
> + - nand-ecc-strength: see nand.txt for details. Available values are:
> + 8, 15 for "altr,socfpga-denali-nand"
> + - nand-ecc-maximize: see nand.txt for details
> +
> +Note:
> +Either nand-ecc-strength or nand-ecc-maximize should be specified.
>
> The device tree may optionally contain sub-nodes describing partitions of the
> address space. See partition.txt for more detail.
> diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c
> index ce87b95..2f796e3 100644
> --- a/drivers/mtd/nand/denali.c
> +++ b/drivers/mtd/nand/denali.c
> @@ -1641,9 +1641,23 @@ int denali_init(struct denali_nand_info *denali)
> goto failed_req_irq;
> }
>
> - ret = denali_set_max_ecc_strength(denali);
> - if (ret)
> + if (!chip->ecc.strength && !(chip->ecc.options & NAND_ECC_MAXIMIZE)) {
> + dev_info(denali->dev,
> + "No ECC strength strategy is specified. Maximizing ECC strength\n");
> + chip->ecc.options |= NAND_ECC_MAXIMIZE;
> + }
> +
> + if (chip->ecc.options & NAND_ECC_MAXIMIZE) {
> + ret = denali_set_max_ecc_strength(denali);
> + if (ret)
> + goto failed_req_irq;
> + } else if (!(denali->ecc_strength_avail & BIT(chip->ecc.strength))) {
> + dev_err(denali->dev,
> + "ECC strength %d is not supported on this controller.\n",
> + chip->ecc.strength);
> + ret = -EINVAL;
> goto failed_req_irq;
> + }
>
> chip->ecc.bytes = denali_calc_ecc_bytes(chip->ecc.size,
> chip->ecc.strength);
> diff --git a/drivers/mtd/nand/denali_pci.c b/drivers/mtd/nand/denali_pci.c
> index a1ee9f8..a39682a5 100644
> --- a/drivers/mtd/nand/denali_pci.c
> +++ b/drivers/mtd/nand/denali_pci.c
> @@ -87,6 +87,7 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
>
> denali->ecc_strength_avail = BIT(15) | BIT(8);
> denali->caps |= DENALI_CAP_ECC_SIZE_512;
> + denali->nand.ecc.options |= NAND_ECC_MAXIMIZE;
>
> ret = denali_init(denali);
> if (ret)
On Thu, 30 Mar 2017 15:46:04 +0900
Masahiro Yamada <[email protected]> wrote:
> Currently, the error handling of denali_write_page(_raw) is a bit
> complicated. If the program command fails, NAND_STATUS_FAIL is set
> to the driver internal denali->status, then read out later by
> denali_waitfunc().
>
> We can avoid it by exploiting the nand_write_page() implementation.
> If chip->ecc.write_page(_raw) returns negative code (i.e. -EIO), it
> errors out immediately. This gives the same result as returning
> NAND_STATUS_FAIL from chip->waitfunc. In either way, -EIO is
> returned to the upper MTD layer.
>
> Signed-off-by: Masahiro Yamada <[email protected]>
> ---
>
> Changes in v3: None
> Changes in v2:
> - Newly added
>
> drivers/mtd/nand/denali.c | 12 ++++--------
> drivers/mtd/nand/denali.h | 1 -
> 2 files changed, 4 insertions(+), 9 deletions(-)
>
> diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c
> index 79851ca..5da8156 100644
> --- a/drivers/mtd/nand/denali.c
> +++ b/drivers/mtd/nand/denali.c
> @@ -1005,6 +1005,7 @@ static int write_page(struct mtd_info *mtd, struct nand_chip *chip,
> size_t size = mtd->writesize + mtd->oobsize;
> uint32_t irq_status;
> uint32_t irq_mask = INTR__DMA_CMD_COMP | INTR__PROGRAM_FAIL;
> + int ret = 0;
>
> denali->page = page;
>
> @@ -1038,13 +1039,13 @@ static int write_page(struct mtd_info *mtd, struct nand_chip *chip,
> if (irq_status == 0) {
> dev_err(denali->dev, "timeout on write_page (type = %d)\n",
> raw_xfer);
> - denali->status = NAND_STATUS_FAIL;
> + ret = -EIO;
> }
>
> denali_enable_dma(denali, false);
> dma_sync_single_for_cpu(denali->dev, addr, size, DMA_TO_DEVICE);
>
> - return 0;
> + return ret;
> }
>
> /* NAND core entry points */
> @@ -1196,12 +1197,7 @@ static void denali_select_chip(struct mtd_info *mtd, int chip)
>
> static int denali_waitfunc(struct mtd_info *mtd, struct nand_chip *chip)
> {
> - struct denali_nand_info *denali = mtd_to_denali(mtd);
> - int status = denali->status;
> -
> - denali->status = 0;
> -
> - return status;
> + return 0;
I know it's not your fault, and the existing denali_waitfunc() is
already buggy, but it's definitely wrong to return 0 here without
checking the NAND status.
->waitfunc() is not only used to wait for a page-program operation,
it's used every time the NAND enters the busy state (lock, unlock,
set_features, ...).
Anyway, I'll review the remaining patches before taking a decision.
> }
>
> static int denali_erase(struct mtd_info *mtd, int page)
> diff --git a/drivers/mtd/nand/denali.h b/drivers/mtd/nand/denali.h
> index 00ce04e..9e2b787 100644
> --- a/drivers/mtd/nand/denali.h
> +++ b/drivers/mtd/nand/denali.h
> @@ -329,7 +329,6 @@ struct nand_buf {
> struct denali_nand_info {
> struct nand_chip nand;
> int flash_bank; /* currently selected chip */
> - int status;
> int platform;
> struct nand_buf buf;
> struct device *dev;
On Thu, 30 Mar 2017 15:46:11 +0900
Masahiro Yamada <[email protected]> wrote:
> /*
> * sends a pipeline command operation to the controller. See the Denali NAND
> * controller's user guide for more information (section 4.2.3.6).
> @@ -824,7 +856,13 @@ static void denali_select_chip(struct mtd_info *mtd, int chip)
>
> static int denali_waitfunc(struct mtd_info *mtd, struct nand_chip *chip)
> {
> - return 0;
> + struct denali_nand_info *denali = mtd_to_denali(mtd);
> + uint32_t irq_status;
> +
> + /* R/B# pin transitioned from low to high? */
> + irq_status = denali_wait_for_irq(denali, INTR__INT_ACT);
> +
> + return irq_status & INTR__INT_ACT ? 0 : NAND_STATUS_FAIL;
Okay, you fix the ->waitfunc() implementation here. Great! Then I'm
fine with patch 18.
> }
On Thu, 30 Mar 2017 15:46:12 +0900
Masahiro Yamada <[email protected]> wrote:
> The function denali_nand_reset() is called during the driver probe,
> and polls the INTR__RST_COMP and INTR__TIME_OUT bits. However,
> INTR__RST_COMP is set anyway even if no NAND device is connected to
> that bank.
>
> This can be a problem for ONFi devices. The nand_scan_ident()
> iterates over maxchips, and calls nand_reset() for each chip.
Actually, maxchips is a bad name. What should be passed in argument to
nand_scan_ident() is not the maximum number of CS-line the controller
has, it's the expected number of CS-lines provided by a chip.
If you're using DT, this information should be retrieved from the DT. If
you look at this binding doc [1] you'll see that each NAND chip has a
reg property encoding the CS line. When a chip exposes more than one
CS-line, the reg property should contain 2 entries describing which
controller-side CS lines are connected to the chip CS-lines.
For non-DT cases, this should be exposed by some other means (for
example pdata, but I'm not sure it works well with PCI where everything
is discoverable).
So normally, you shouldn't have a timeout, or something is wrong with
the DT/board description.
Note that you might have different NAND models connected to the same
NAND controller. If you call nand_scan_ident() only once and pass
controllers->max_cs_lines to it, you will only have one chip detected,
which is not what you expect.
> Now, this driver implements ->setup_data_interface() method, so
> nand_setup_data_interface() issues Set Features (0xEF) command to
> each chip. This can cause time-out error since denali_nand_reset()
> did not check the chip existence. If no chip there, the controller
> will wait long for R/B# response, which we know never happens.
> (The timeout error is correctly handled in this driver, so the
> driver will be successfully probed anyway, but it will take longer
> than needed.)
>
> The Reset (0xFF) command also toggles the R/B# pin, and it sets
> INTR__INT_ACT bit. The driver should check this bit to see if the
> chip has responded, then it can update denali->max_banks.
>
> Signed-off-by: Masahiro Yamada <[email protected]>
> ---
>
> Changes in v3: None
> Changes in v2:
> - Newly added
>
> drivers/mtd/nand/denali.c | 52 +++++++++++++++++++++--------------------------
> 1 file changed, 23 insertions(+), 29 deletions(-)
>
> diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c
> index 86b7c75..e4f2699 100644
> --- a/drivers/mtd/nand/denali.c
> +++ b/drivers/mtd/nand/denali.c
> @@ -85,33 +85,6 @@ static void index_addr(struct denali_nand_info *denali,
> iowrite32(data, denali->flash_mem + 0x10);
> }
>
> -/* Reset the flash controller */
> -static uint16_t denali_nand_reset(struct denali_nand_info *denali)
> -{
> - int i;
> -
> - for (i = 0; i < denali->max_banks; i++)
> - iowrite32(INTR__RST_COMP | INTR__TIME_OUT,
> - denali->flash_reg + INTR_STATUS(i));
> -
> - for (i = 0; i < denali->max_banks; i++) {
> - iowrite32(1 << i, denali->flash_reg + DEVICE_RESET);
> - while (!(ioread32(denali->flash_reg + INTR_STATUS(i)) &
> - (INTR__RST_COMP | INTR__TIME_OUT)))
> - cpu_relax();
> - if (ioread32(denali->flash_reg + INTR_STATUS(i)) &
> - INTR__TIME_OUT)
> - dev_dbg(denali->dev,
> - "NAND Reset operation timed out on bank %d\n", i);
> - }
> -
> - for (i = 0; i < denali->max_banks; i++)
> - iowrite32(INTR__RST_COMP | INTR__TIME_OUT,
> - denali->flash_reg + INTR_STATUS(i));
> -
> - return PASS;
> -}
> -
> /*
> * Use the configuration feature register to determine the maximum number of
> * banks that the hardware supports.
> @@ -999,7 +972,28 @@ static int denali_setup_data_interface(struct mtd_info *mtd,
> return 0;
> }
>
> -/* Initialization code to bring the device up to a known good state */
> +static void denali_reset_banks(struct denali_nand_info *denali)
> +{
> + int i;
> +
> + denali_clear_irq_all(denali);
> +
> + for (i = 0; i < denali->max_banks; i++) {
> + iowrite32(1 << i, denali->flash_reg + DEVICE_RESET);
> + while (!(ioread32(denali->flash_reg + INTR_STATUS(i)) &
> + (INTR__RST_COMP | INTR__TIME_OUT)))
> + cpu_relax();
> + if (!(ioread32(denali->flash_reg + INTR_STATUS(i)) &
> + INTR__INT_ACT))
> + break;
> + }
> +
> + dev_dbg(denali->dev, "%d chips connected\n", i);
> + denali->max_banks = i;
> +
> + denali_clear_irq_all(denali);
> +}
> +
> static void denali_hw_init(struct denali_nand_info *denali)
> {
> /*
> @@ -1019,7 +1013,7 @@ static void denali_hw_init(struct denali_nand_info *denali)
> denali->bbtskipbytes = ioread32(denali->flash_reg +
> SPARE_AREA_SKIP_BYTES);
> detect_max_banks(denali);
> - denali_nand_reset(denali);
> + denali_reset_banks(denali);
> iowrite32(0x0F, denali->flash_reg + RB_PIN_ENABLED);
> iowrite32(CHIP_EN_DONT_CARE__FLAG,
> denali->flash_reg + CHIP_ENABLE_DONT_CARE);
On Thu, 30 Mar 2017 15:45:46 +0900
Masahiro Yamada <[email protected]> wrote:
> This driver includes many problems.
>
> One of the biggest one is a bunch of hard-coded parameters. This IP
> has many parameters that can be customized when a delivery RTL is
> generated. However, this driver was upstreamed by Intel, with
> Intel parameters hard-coded. Later, Altera added denali_dt.c to use
> this driver for embedded boards, but they did not fix the code in
> denali.c So, this driver has never worked. Even some DT bindings
> actually turned out wrong.
>
> There are more problems: [1] The driver just retrieves the OOB area as-is
> whereas the controller uses syndrome page layout. [2] Many NAND chip
> specific parameters are hard-coded in the driver. [3] ONFi devices are
> not working [4] It can not read Bad Block Marker
>
> This patch series intends to solve those problems.
>
> Outstanding changes are:
> - Fix raw/oob callbacks for syndrome page layout
> - Implement setup_data_interface() callback
> - Fix/implement more commands for ONFi devices
> - Allow to skip the driver internal bounce buffer
> - Support PIO in case DMA is not supported
> - Switch from ->cmdfunc over to ->cmd_ctrl
>
> 18 patches were merged at v2.
> Here is the rest of the series.
>
> v1: https://lkml.org/lkml/2016/11/26/144
> v2: https://lkml.org/lkml/2017/3/22/804
>
>
> Masahiro Yamada (37):
> mtd: nand: relax ecc.read_page() return value for uncorrectable ECC
> mtd: nand: denali: allow to override mtd->name from label DT property
> mtd: nand: denali: remove meaningless pipeline read-ahead operation
> mtd: nand: denali: fix bitflips calculation in handle_ecc()
> mtd: nand: denali: fix erased page checking
> mtd: nand: denali: support HW_ECC_FIXUP capability
> mtd: nand: denali_dt: enable HW_ECC_FIXUP for Altera SOCFPGA variant
> mtd: nand: denali: support 64bit capable DMA engine
> mtd: nand: denali_dt: remove dma-mask DT property
> mtd: nand: denali_dt: use pdev instead of ofdev for platform_device
> mtd: nand: denali: allow to override revision number
Applied patches 1 to 11.
> mtd: nand: denali: support 1024 byte ECC step size
> mtd: nand: denali: avoid hard-coding ecc.strength and ecc.bytes
> mtd: nand: denali: support "nand-ecc-strength" DT property
> mtd: nand: denali: remove Toshiba and Hynix specific fixup code
> mtd: nand: denali_dt: add compatible strings for UniPhier SoC variants
> mtd: nand: denali: set NAND_ECC_CUSTOM_PAGE_ACCESS
> mtd: nand: denali: do not propagate NAND_STATUS_FAIL to waitfunc()
> mtd: nand: denali: use BIT() and GENMASK() for register macros
> mtd: nand: denali: remove unneeded find_valid_banks()
> mtd: nand: denali: handle timing parameters by setup_data_interface()
> mtd: nand: denali: rework interrupt handling
> mtd: nand: denali: fix NAND_CMD_STATUS handling
> mtd: nand: denali: fix NAND_CMD_PARAM handling
> mtd: nand: denali: switch over to cmd_ctrl instead of cmdfunc
> mtd: nand: denali: fix bank reset function
> mtd: nand: denali: use interrupt instead of polling for bank reset
> mtd: nand: denali: propagate page to helpers via function argument
> mtd: nand: denali: merge struct nand_buf into struct denali_nand_info
> mtd: nand: denali: use flag instead of register macro for direction
> mtd: nand: denali: fix raw and oob accessors for syndrome page layout
> mtd: nand: denali: support hardware-assisted erased page detection
> mtd: nand: allocate aligned buffers if NAND_OWN_BUFFERS is unset
> mtd: nand: allow drivers to request minimum alignment for passed
> buffer
> mtd: nand: denali: skip driver internal bounce buffer when possible
> mtd: nand: denali: use non-managed kmalloc() for DMA buffer
> mtd: nand: denali: enable bad block table scan
The rest looks almost good, except for a few comments I had on patch
14, 18, 25, 26 and 32.
I'll probably apply 33 and 34 soon.
>
> .../devicetree/bindings/mtd/denali-nand.txt | 24 +-
> drivers/mtd/nand/denali.c | 1971 ++++++++++----------
> drivers/mtd/nand/denali.h | 308 +--
> drivers/mtd/nand/denali_dt.c | 90 +-
> drivers/mtd/nand/denali_pci.c | 10 +-
> drivers/mtd/nand/nand_base.c | 49 +-
> include/linux/mtd/nand.h | 4 +-
> 7 files changed, 1234 insertions(+), 1222 deletions(-)
>
Hi Boris,
2017-03-31 1:38 GMT+09:00 Boris Brezillon <[email protected]>:
> The rest looks almost good, except for a few comments I had on patch
> 14, 18, 25, 26 and 32.
>
> I'll probably apply 33 and 34 soon.
>
Thank you!
Please note I left a minor comment on 34.
(Accidental addition of braces.)
--
Best Regards
Masahiro Yamada
Hi Boris,
2017-03-30 23:02 GMT+09:00 Boris Brezillon <[email protected]>:
> On Thu, 30 Mar 2017 15:46:00 +0900
> Masahiro Yamada <[email protected]> wrote:
>
>> Historically, this driver tried to choose as big ECC strength as
>> possible, but it would be reasonable to allow DT to set a particular
>> ECC strength with "nand-ecc-strength" property. This is useful
>> when a particular ECC setting is hard-coded by firmware (or hard-
>> wired by boot ROM).
>>
>> If no ECC strength is specified in DT, "nand-ecc-maximize" is implied
>> since this was the original behavior.
>
> You said there is currently no DT users,
Right. No DT users ever in upstream.
> so how about changing the
> "fallback to ECC maximization" behavior for DT users, and instead of
> maximizing the ECC strength take the NAND requirements into account
> (chip->ecc_strength_ds).
This is difficult to judge in some cases.
As I said before, 4/512 and 8/1024 are not equivalent.
If chip's requirement chip->ecc_step_ds matches
to the ecc->size supported by the controller,
this is easy.
If a chip requests 1024B, then the controller can only support 512B chunk
(or vice versa), it is difficult to simply compare
ecc strength.
Is it a bad thing if we use too strong ECC strength?
The disadvantage I see is we will have less OOB-free bytes,
but this will not be fatal, I guess.
--
Best Regards
Masahiro Yamada
On Fri, 31 Mar 2017 13:05:20 +0900
Masahiro Yamada <[email protected]> wrote:
> Hi Boris,
>
>
> 2017-03-31 1:38 GMT+09:00 Boris Brezillon <[email protected]>:
>
> > The rest looks almost good, except for a few comments I had on patch
> > 14, 18, 25, 26 and 32.
> >
> > I'll probably apply 33 and 34 soon.
> >
>
> Thank you!
> Please note I left a minor comment on 34.
> (Accidental addition of braces.)
Yep, I noticed. Applied both after fixing the alignment and removing
unneeded braces in patch 34.
Thanks,
Boris
On Thu, 30 Mar 2017 15:45:59 +0900
Masahiro Yamada <[email protected]> wrote:
>
> diff --git a/drivers/mtd/nand/denali_dt.c b/drivers/mtd/nand/denali_dt.c
> index 1681a30..c3bc333 100644
> --- a/drivers/mtd/nand/denali_dt.c
> +++ b/drivers/mtd/nand/denali_dt.c
> @@ -31,10 +31,12 @@ struct denali_dt {
>
> struct denali_dt_data {
> unsigned int revision;
> + unsigned long ecc_strength_avail;
Shouldn't you directly make sure that your driver will support
strengths > BITS_PER_LONG?
I would store the supported strength in a table to make it more
future-proof:
int *ecc_strength_avail;
int num_ecc_strengths;
> unsigned int caps;
> };
>
On Fri, 31 Mar 2017 14:06:32 +0900
Masahiro Yamada <[email protected]> wrote:
> Hi Boris,
>
>
> 2017-03-30 23:02 GMT+09:00 Boris Brezillon <[email protected]>:
> > On Thu, 30 Mar 2017 15:46:00 +0900
> > Masahiro Yamada <[email protected]> wrote:
> >
> >> Historically, this driver tried to choose as big ECC strength as
> >> possible, but it would be reasonable to allow DT to set a particular
> >> ECC strength with "nand-ecc-strength" property. This is useful
> >> when a particular ECC setting is hard-coded by firmware (or hard-
> >> wired by boot ROM).
> >>
> >> If no ECC strength is specified in DT, "nand-ecc-maximize" is implied
> >> since this was the original behavior.
> >
> > You said there is currently no DT users,
>
> Right. No DT users ever in upstream.
>
>
> > so how about changing the
> > "fallback to ECC maximization" behavior for DT users, and instead of
> > maximizing the ECC strength take the NAND requirements into account
> > (chip->ecc_strength_ds).
>
> This is difficult to judge in some cases.
>
> As I said before, 4/512 and 8/1024 are not equivalent.
>
> If chip's requirement chip->ecc_step_ds matches
> to the ecc->size supported by the controller,
> this is easy.
>
>
> If a chip requests 1024B, then the controller can only support 512B chunk
> (or vice versa), it is difficult to simply compare
> ecc strength.
You can try something like that when no explicit ecc.strength and
ecc.size has been set in the DT and when ECC_MAXIMIZE was not passed.
static int
denali_get_closest_ecc_strength(struct denali_nand_info *denali,
int strength)
{
/*
* Whatever you need to select a strength that is greater than
* or equal to strength.
*/
return X;
}
static int denali_try_to_match_ecc_req(struct denali_nand_info *denali)
{
struct nand_chip *chip = &denali->nand;
struct mtd_info *mtd = nand_to_mtd(chip);
int max_ecc_bytes = mtd->oobsize - denali->bbtskipbytes;
int ecc_steps, ecc_strength, ecc_bytes;
int ecc_size = chip->ecc_step_ds;
int ecc_strength = chip->ecc_strength_ds;
/*
* No information provided by the NAND chip, let the core
* maximize the strength.
*/
if (!ecc_size || !ecc_strength)
return -ENOTSUPP;
if (ecc_size > 512)
ecc_size = 1024;
else
ecc_size = 512;
/* Adjust ECC step size based on hardware support. */
if (ecc_size == 1024 &&
!(denali->caps & DENALI_CAP_ECC_SIZE_1024))
ecc_size = 512;
else if(ecc_size == 512 &&
!(denali->caps & DENALI_CAP_ECC_SIZE_512))
ecc_size = 1024;
if (ecc_size < chip->ecc_size_ds) {
/*
* When the selected size if smaller than the expected
* one we try to use the same strength but on 512 blocks
* so that we can still fix the same number of errors
* even if they are concentrated in the first 512bytes
* of a 1024bytes portion.
*/
ecc_strength = chip->ecc_strength_ds;
ecc_strength = denali_get_closest_ecc_strength(denali,
ecc_strength);
} else {
/* Always prefer 1024bytes ECC blocks when possible. */
if (ecc_size != 1024 &&
(denali->caps & DENALI_CAP_ECC_SIZE_1024) &&
mtd->writesize > 1024)
ecc_size = 1024;
/*
* Adjust the strength based on the selected ECC step
* size.
*/
ecc_strength = DIV_ROUND_UP(ecc_size,
chip->ecc_step_ds) *
chip->ecc_strength_ds;
}
ecc_bytes = denali_calc_ecc_bytes(ecc_size,
ecc_strength);
ecc_bytes *= mtd->writesize / ecc_size;
/*
* If we don't have enough space, let the core maximize
* the strength.
*/
if (ecc_bytes > max_ecc_bytes)
return -ENOTSUPP;
chip->ecc.strength = ecc_strength;
chip->ecc.size = ecc_size;
return 0;
}
>
> Is it a bad thing if we use too strong ECC strength?
>
> The disadvantage I see is we will have less OOB-free bytes,
> but this will not be fatal, I guess.
Not a bad thing in general, but I'd prefer to leave the choice to the
user. If one doesn't need the extra-safety brought by ECC strength
maximization and wants to have more OOB bytes it's better to follow
NAND requirements.
2017-03-30 15:45 GMT+09:00 Masahiro Yamada <[email protected]>:
> This driver was originally written for the Intel MRST platform with
> several platform specific parameters hard-coded. Another thing we
> need to fix is the hard-coded ECC step size. Currently, it is
> defined as follows:
>
> #define ECC_SECTOR_SIZE 512
>
> (somehow, it is defined in both denali.c and denali.h)
>
> This must be avoided because the Denali IP supports 1024B ECC size
> as well. The Denali User's Guide also says supporting both 512B and
> 1024B ECC sectors is possible, though it would require instantiation
> of two different ECC circuits. So, possible cases are:
>
> [1] only 512B ECC size is supported
> [2] only 1024B ECC size is supported
> [3] both 512B and 1024B ECC sizes are supported
>
> Newer versions of this IP need ecc.size and ecc.steps explicitly
> set up via the following registers:
> CFG_DATA_BLOCK_SIZE (0x6b0)
> CFG_LAST_DATA_BLOCK_SIZE (0x6c0)
> CFG_NUM_DATA_BLOCKS (0x6d0)
>
> Older versions do not have such registers (they were reserved), so
> write accesses are safely ignored.
>
> This commit adds new flags DENALI_CAP_ECC_SIZE_{512,1024}.
>
> The DT property "nand-ecc-step-size" is still optional; a reasonable
> default will be chosen for [1] and [2]. For case [3], users can
> force ECC size via DT in case firmware hard-codes ECC settings.
> If not specified, the driver will use chip's ECC requirement as a
> hint to decide the ECC size.
>
> Signed-off-by: Masahiro Yamada <[email protected]>
> Acked-by: Rob Herring <[email protected]>
> ---
>
> Changes in v3:
> - Move DENALI_CAP_ define out of struct denali_nand_info
> - Use chip->ecc_step_ds as a hint to choose chip->ecc.size
> where possible
>
Please hold back this patch
until we decide how to handle 14.
--
Best Regards
Masahiro Yamada
Hi Boris,
2017-03-31 18:46 GMT+09:00 Boris Brezillon <[email protected]>:
> You can try something like that when no explicit ecc.strength and
> ecc.size has been set in the DT and when ECC_MAXIMIZE was not passed.
>
> static int
> denali_get_closest_ecc_strength(struct denali_nand_info *denali,
> int strength)
> {
> /*
> * Whatever you need to select a strength that is greater than
> * or equal to strength.
> */
>
> return X;
> }
Is here anything specific to Denali?
> static int denali_try_to_match_ecc_req(struct denali_nand_info *denali)
> {
> struct nand_chip *chip = &denali->nand;
> struct mtd_info *mtd = nand_to_mtd(chip);
> int max_ecc_bytes = mtd->oobsize - denali->bbtskipbytes;
> int ecc_steps, ecc_strength, ecc_bytes;
> int ecc_size = chip->ecc_step_ds;
> int ecc_strength = chip->ecc_strength_ds;
>
> /*
> * No information provided by the NAND chip, let the core
> * maximize the strength.
> */
> if (!ecc_size || !ecc_strength)
> return -ENOTSUPP;
>
> if (ecc_size > 512)
> ecc_size = 1024;
> else
> ecc_size = 512;
>
> /* Adjust ECC step size based on hardware support. */
> if (ecc_size == 1024 &&
> !(denali->caps & DENALI_CAP_ECC_SIZE_1024))
> ecc_size = 512;
> else if(ecc_size == 512 &&
> !(denali->caps & DENALI_CAP_ECC_SIZE_512))
> ecc_size = 1024;
>
> if (ecc_size < chip->ecc_size_ds) {
> /*
> * When the selected size if smaller than the expected
> * one we try to use the same strength but on 512 blocks
> * so that we can still fix the same number of errors
> * even if they are concentrated in the first 512bytes
> * of a 1024bytes portion.
> */
> ecc_strength = chip->ecc_strength_ds;
> ecc_strength = denali_get_closest_ecc_strength(denali,
> ecc_strength);
> } else {
> /* Always prefer 1024bytes ECC blocks when possible. */
> if (ecc_size != 1024 &&
> (denali->caps & DENALI_CAP_ECC_SIZE_1024) &&
> mtd->writesize > 1024)
> ecc_size = 1024;
>
> /*
> * Adjust the strength based on the selected ECC step
> * size.
> */
> ecc_strength = DIV_ROUND_UP(ecc_size,
> chip->ecc_step_ds) *
> chip->ecc_strength_ds;
> }
>
> ecc_bytes = denali_calc_ecc_bytes(ecc_size,
> ecc_strength);
> ecc_bytes *= mtd->writesize / ecc_size;
>
> /*
> * If we don't have enough space, let the core maximize
> * the strength.
> */
> if (ecc_bytes > max_ecc_bytes)
> return -ENOTSUPP;
>
> chip->ecc.strength = ecc_strength;
> chip->ecc.size = ecc_size;
>
> return 0;
> }
As a whole, this does not seem to driver-specific.
[1] A driver provides some pairs of (ecc_strength, ecc_size)
it can support.
[2] The core framework knows the chip's requirement
(ecc_strength_ds, ecc_size_ds).
Then, the core framework provides a function
to return a most recommended (ecc_strength, ecc_size).
struct nand_ecc_spec {
int ecc_strength;
int ecc_size;
};
/*
* This function choose the most recommented (ecc_str, ecc_size)
* "recommended" means: minimum ecc stregth that meets
* the chip's requirment.
*
*
* @chip - nand_chip
* @controller_ecc_spec - Array of (ecc_str, ecc_size) supported by the
controller. (terminated by NULL as sentinel)
*/
struct nand_ecc_spec * nand_try_to_match_ecc_req(struct nand_chip *chip,
struct nand_ecc_spec
*controller_ecc_spec)
{
/*
* Return the pointer to the most recommended
* struct nand_ecc_spec.
* If nothing suitable found, return NULL.
*/
}
Then, Denali driver can call it:
recommended_ecc_spec = nand_try_to_match_ecc_req(chip,
denali->ecc_spec);
if (recommended_ecc_spec) {
chip->ecc.strength = recommended_ecc_spec.ecc_strength;
chip->ecc.size = recommended_ecc_spec.ecc_size;
} else {
/*
* Do something (for example, maximize the ECC)
*/
}
It seems weird to force this to the Denali driver.
--
Best Regards
Masahiro Yamada
Hi Boris,
2017-03-31 1:16 GMT+09:00 Boris Brezillon <[email protected]>:
> On Thu, 30 Mar 2017 15:46:12 +0900
> Masahiro Yamada <[email protected]> wrote:
>
>> The function denali_nand_reset() is called during the driver probe,
>> and polls the INTR__RST_COMP and INTR__TIME_OUT bits. However,
>> INTR__RST_COMP is set anyway even if no NAND device is connected to
>> that bank.
>>
>> This can be a problem for ONFi devices. The nand_scan_ident()
>> iterates over maxchips, and calls nand_reset() for each chip.
>
> Actually, maxchips is a bad name. What should be passed in argument to
> nand_scan_ident() is not the maximum number of CS-line the controller
> has, it's the expected number of CS-lines provided by a chip.
>
> If you're using DT, this information should be retrieved from the DT. If
> you look at this binding doc [1] you'll see that each NAND chip has a
> reg property encoding the CS line. When a chip exposes more than one
> CS-line, the reg property should contain 2 entries describing which
> controller-side CS lines are connected to the chip CS-lines.
Actually, the Denali driver is much older than
commit 2d472aba15ff169 ("mtd: nand: document the NAND
controller/NAND chip DT representation").
So, I guess it is allowed to use the old binding,
then you were kind to merge my
commit 63757d463ea683b469c1976032054d46cecdef09
Author: Masahiro Yamada <[email protected]>
Date: Thu Mar 23 05:07:18 2017 +0900
mtd: nand: denali: call nand_set_flash_node() to set DT node
Having said that, I have to admit the current implementation
(not my fault) is not nice in a long run.
In order to switch to the new binding,
I have to de-couple the controller and chips
(like you did for sunxi_nand)
I am taking a close look for how much efforts are needed
(because I am guessing you will recommend me to do this work :-) )
> For non-DT cases, this should be exposed by some other means (for
> example pdata, but I'm not sure it works well with PCI where everything
> is discoverable).
>
> So normally, you shouldn't have a timeout, or something is wrong with
> the DT/board description.
>
> Note that you might have different NAND models connected to the same
> NAND controller. If you call nand_scan_ident() only once and pass
> controllers->max_cs_lines to it, you will only have one chip detected,
> which is not what you expect.
The Denali IP actually supports multiple chip selects,
but has only a single set of parameter registers.
For example,
DEVICE_MAIN_AREA_SIZE must match to a chip's mtd->writesize.
If denali_select_chip() updates all parameter registers every time,
it would be theoretically possible to use different models.
(And, looks like sunxi_select_chip does this.)
--
Best Regards
Masahiro Yamada
On Thu, Mar 30, 2017 at 03:46:02PM +0900, Masahiro Yamada wrote:
> Add two compatible strings for UniPhier SoCs.
>
> "socionext,uniphier-denali-nand-v5a" is used on UniPhier sLD3, LD4,
> Pro4, sLD8 SoCs.
>
> "socionext,uniphier-denali-nand-v5b" is used on UniPhier Pro5, PXs2,
> LD6b, LD11, LD20 SoCs.
>
> Signed-off-by: Masahiro Yamada <[email protected]>
> ---
>
> Changes in v3: None
> Changes in v2:
> - Change the compatible strings
> - Fix the ecc_strength_capability
> - Override revision number for the newer one
>
> .../devicetree/bindings/mtd/denali-nand.txt | 6 ++++++
> drivers/mtd/nand/denali_dt.c | 23 ++++++++++++++++++++++
> 2 files changed, 29 insertions(+)
Acked-by: Rob Herring <[email protected]>
On Mon, 3 Apr 2017 12:16:34 +0900
Masahiro Yamada <[email protected]> wrote:
> Hi Boris,
>
>
>
> 2017-03-31 18:46 GMT+09:00 Boris Brezillon <[email protected]>:
>
> > You can try something like that when no explicit ecc.strength and
> > ecc.size has been set in the DT and when ECC_MAXIMIZE was not passed.
> >
> > static int
> > denali_get_closest_ecc_strength(struct denali_nand_info *denali,
> > int strength)
> > {
> > /*
> > * Whatever you need to select a strength that is greater than
> > * or equal to strength.
> > */
> >
> > return X;
> > }
>
>
> Is here anything specific to Denali?
Well, only the denali driver knows what the hardware supports, though
having a generic function that takes a table of supported strengths
would work.
>
>
> > static int denali_try_to_match_ecc_req(struct denali_nand_info *denali)
> > {
> > struct nand_chip *chip = &denali->nand;
> > struct mtd_info *mtd = nand_to_mtd(chip);
> > int max_ecc_bytes = mtd->oobsize - denali->bbtskipbytes;
> > int ecc_steps, ecc_strength, ecc_bytes;
> > int ecc_size = chip->ecc_step_ds;
> > int ecc_strength = chip->ecc_strength_ds;
> >
> > /*
> > * No information provided by the NAND chip, let the core
> > * maximize the strength.
> > */
> > if (!ecc_size || !ecc_strength)
> > return -ENOTSUPP;
> >
> > if (ecc_size > 512)
> > ecc_size = 1024;
> > else
> > ecc_size = 512;
> >
> > /* Adjust ECC step size based on hardware support. */
> > if (ecc_size == 1024 &&
> > !(denali->caps & DENALI_CAP_ECC_SIZE_1024))
> > ecc_size = 512;
> > else if(ecc_size == 512 &&
> > !(denali->caps & DENALI_CAP_ECC_SIZE_512))
> > ecc_size = 1024;
> >
> > if (ecc_size < chip->ecc_size_ds) {
> > /*
> > * When the selected size if smaller than the expected
> > * one we try to use the same strength but on 512 blocks
> > * so that we can still fix the same number of errors
> > * even if they are concentrated in the first 512bytes
> > * of a 1024bytes portion.
> > */
> > ecc_strength = chip->ecc_strength_ds;
> > ecc_strength = denali_get_closest_ecc_strength(denali,
> > ecc_strength);
> > } else {
> > /* Always prefer 1024bytes ECC blocks when possible. */
> > if (ecc_size != 1024 &&
> > (denali->caps & DENALI_CAP_ECC_SIZE_1024) &&
> > mtd->writesize > 1024)
> > ecc_size = 1024;
> >
> > /*
> > * Adjust the strength based on the selected ECC step
> > * size.
> > */
> > ecc_strength = DIV_ROUND_UP(ecc_size,
> > chip->ecc_step_ds) *
> > chip->ecc_strength_ds;
> > }
> >
> > ecc_bytes = denali_calc_ecc_bytes(ecc_size,
> > ecc_strength);
> > ecc_bytes *= mtd->writesize / ecc_size;
> >
> > /*
> > * If we don't have enough space, let the core maximize
> > * the strength.
> > */
> > if (ecc_bytes > max_ecc_bytes)
> > return -ENOTSUPP;
> >
> > chip->ecc.strength = ecc_strength;
> > chip->ecc.size = ecc_size;
> >
> > return 0;
> > }
>
>
> As a whole, this does not seem to driver-specific.
It's almost controller-agnostic, except for the denali_calc_ecc_bytes()
function, but I guess we could ask drivers to implement a hook that is
passed the ECC step size and strength and returns the associated
number of ECC bytes.
>
>
> [1] A driver provides some pairs of (ecc_strength, ecc_size)
> it can support.
>
> [2] The core framework knows the chip's requirement
> (ecc_strength_ds, ecc_size_ds).
>
>
> Then, the core framework provides a function
> to return a most recommended (ecc_strength, ecc_size).
>
>
>
> struct nand_ecc_spec {
> int ecc_strength;
> int ecc_size;
> };
>
> /*
> * This function choose the most recommented (ecc_str, ecc_size)
> * "recommended" means: minimum ecc stregth that meets
> * the chip's requirment.
> *
> *
> * @chip - nand_chip
> * @controller_ecc_spec - Array of (ecc_str, ecc_size) supported by the
> controller. (terminated by NULL as sentinel)
> */
> struct nand_ecc_spec * nand_try_to_match_ecc_req(struct nand_chip *chip,
> struct nand_ecc_spec
> *controller_ecc_spec)
> {
> /*
> * Return the pointer to the most recommended
> * struct nand_ecc_spec.
> * If nothing suitable found, return NULL.
> */
> }
>
I like the idea, except I would do this slightly differently to avoid
declaring all combinations of stepsize and strengths
struct nand_ecc_stepsize_info {
int stepsize;
int nstrengths;
int *strengths;
};
struct nand_ecc_engine_caps {
int nstepsizes;
struct nand_ecc_stepsize_info *stepsizes;
int (*calc_ecc_bytes)(int stepsize, int strength);
};
int nand_try_to_match_ecc_req(struct nand_chip *chip,
const struct nand_ecc_engine_caps *caps,
struct nand_ecc_spec *spec)
{
/*
* Find the most appropriate setting based on the ECC engine
* caps and fill the spec object accordingly.
* Returns 0 in case of success and a negative error code
* otherwise.
*/
}
Note that nand_try_to_match_ecc_req() has to be more generic than
denali_try_to_match_ecc_req() WRT step sizes, which will probably
complexify the logic.
Hi Boris,
2017-04-10 1:33 GMT+09:00 Boris Brezillon <[email protected]>:
> On Mon, 3 Apr 2017 12:16:34 +0900
> Masahiro Yamada <[email protected]> wrote:
>
>> Hi Boris,
>>
>>
>>
>> 2017-03-31 18:46 GMT+09:00 Boris Brezillon <[email protected]>:
>>
>> > You can try something like that when no explicit ecc.strength and
>> > ecc.size has been set in the DT and when ECC_MAXIMIZE was not passed.
>> >
>> > static int
>> > denali_get_closest_ecc_strength(struct denali_nand_info *denali,
>> > int strength)
>> > {
>> > /*
>> > * Whatever you need to select a strength that is greater than
>> > * or equal to strength.
>> > */
>> >
>> > return X;
>> > }
>>
>>
>> Is here anything specific to Denali?
>
> Well, only the denali driver knows what the hardware supports, though
> having a generic function that takes a table of supported strengths
> would work.
>
>>
>>
>> > static int denali_try_to_match_ecc_req(struct denali_nand_info *denali)
>> > {
>> > struct nand_chip *chip = &denali->nand;
>> > struct mtd_info *mtd = nand_to_mtd(chip);
>> > int max_ecc_bytes = mtd->oobsize - denali->bbtskipbytes;
>> > int ecc_steps, ecc_strength, ecc_bytes;
>> > int ecc_size = chip->ecc_step_ds;
>> > int ecc_strength = chip->ecc_strength_ds;
>> >
>> > /*
>> > * No information provided by the NAND chip, let the core
>> > * maximize the strength.
>> > */
>> > if (!ecc_size || !ecc_strength)
>> > return -ENOTSUPP;
>> >
>> > if (ecc_size > 512)
>> > ecc_size = 1024;
>> > else
>> > ecc_size = 512;
>> >
>> > /* Adjust ECC step size based on hardware support. */
>> > if (ecc_size == 1024 &&
>> > !(denali->caps & DENALI_CAP_ECC_SIZE_1024))
>> > ecc_size = 512;
>> > else if(ecc_size == 512 &&
>> > !(denali->caps & DENALI_CAP_ECC_SIZE_512))
>> > ecc_size = 1024;
>> >
>> > if (ecc_size < chip->ecc_size_ds) {
>> > /*
>> > * When the selected size if smaller than the expected
>> > * one we try to use the same strength but on 512 blocks
>> > * so that we can still fix the same number of errors
>> > * even if they are concentrated in the first 512bytes
>> > * of a 1024bytes portion.
>> > */
>> > ecc_strength = chip->ecc_strength_ds;
>> > ecc_strength = denali_get_closest_ecc_strength(denali,
>> > ecc_strength);
>> > } else {
>> > /* Always prefer 1024bytes ECC blocks when possible. */
>> > if (ecc_size != 1024 &&
>> > (denali->caps & DENALI_CAP_ECC_SIZE_1024) &&
>> > mtd->writesize > 1024)
>> > ecc_size = 1024;
>> >
>> > /*
>> > * Adjust the strength based on the selected ECC step
>> > * size.
>> > */
>> > ecc_strength = DIV_ROUND_UP(ecc_size,
>> > chip->ecc_step_ds) *
>> > chip->ecc_strength_ds;
>> > }
>> >
>> > ecc_bytes = denali_calc_ecc_bytes(ecc_size,
>> > ecc_strength);
>> > ecc_bytes *= mtd->writesize / ecc_size;
>> >
>> > /*
>> > * If we don't have enough space, let the core maximize
>> > * the strength.
>> > */
>> > if (ecc_bytes > max_ecc_bytes)
>> > return -ENOTSUPP;
>> >
>> > chip->ecc.strength = ecc_strength;
>> > chip->ecc.size = ecc_size;
>> >
>> > return 0;
>> > }
>>
>>
>> As a whole, this does not seem to driver-specific.
>
> It's almost controller-agnostic, except for the denali_calc_ecc_bytes()
> function, but I guess we could ask drivers to implement a hook that is
> passed the ECC step size and strength and returns the associated
> number of ECC bytes.
>
>>
>>
>> [1] A driver provides some pairs of (ecc_strength, ecc_size)
>> it can support.
>>
>> [2] The core framework knows the chip's requirement
>> (ecc_strength_ds, ecc_size_ds).
>>
>>
>> Then, the core framework provides a function
>> to return a most recommended (ecc_strength, ecc_size).
>>
>>
>>
>> struct nand_ecc_spec {
>> int ecc_strength;
>> int ecc_size;
>> };
>>
>> /*
>> * This function choose the most recommented (ecc_str, ecc_size)
>> * "recommended" means: minimum ecc stregth that meets
>> * the chip's requirment.
>> *
>> *
>> * @chip - nand_chip
>> * @controller_ecc_spec - Array of (ecc_str, ecc_size) supported by the
>> controller. (terminated by NULL as sentinel)
>> */
>> struct nand_ecc_spec * nand_try_to_match_ecc_req(struct nand_chip *chip,
>> struct nand_ecc_spec
>> *controller_ecc_spec)
>> {
>> /*
>> * Return the pointer to the most recommended
>> * struct nand_ecc_spec.
>> * If nothing suitable found, return NULL.
>> */
>> }
>>
>
> I like the idea, except I would do this slightly differently to avoid
> declaring all combinations of stepsize and strengths
>
> struct nand_ecc_stepsize_info {
> int stepsize;
> int nstrengths;
> int *strengths;
> };
>
> struct nand_ecc_engine_caps {
> int nstepsizes;
> struct nand_ecc_stepsize_info *stepsizes;
> int (*calc_ecc_bytes)(int stepsize, int strength);
> };
>
> int nand_try_to_match_ecc_req(struct nand_chip *chip,
> const struct nand_ecc_engine_caps *caps,
> struct nand_ecc_spec *spec)
> {
> /*
> * Find the most appropriate setting based on the ECC engine
> * caps and fill the spec object accordingly.
> * Returns 0 in case of success and a negative error code
> * otherwise.
> */
> }
>
> Note that nand_try_to_match_ecc_req() has to be more generic than
> denali_try_to_match_ecc_req() WRT step sizes, which will probably
> complexify the logic.
After I fiddle with this generic approach for a while,
I started to feel like giving up.
I wonder if we really want over-implementation
for covering _theoretically_ possible cases.
In practice, there are not so many ECC settings possible
on a single controller.
As for Denali IP, it would be theoretically possible to instantiate
multiple ECC engines. However, in practice, there is no sensible
reason to do so. At least, I do not know any real chip to support that.
So, I'd like to simplify the logic for Denali.
- Support either 512 or 1024 ECC size.
If there is (ever) a controller that supports both,
1024 should be chosen.
- ECC strength is not specified via DT, it is simply maximized.
This simplifies the logic much and I believe this is enough.
One more reason is, as we talked before,
we need to match ECC setting between Linux and firmware (boot-loader),
so anyway we end up with using a fixed setting specified by DT.
--
Best Regards
Masahiro Yamada
Hi Masahiro,
On Tue, 11 Apr 2017 15:19:21 +0900
Masahiro Yamada <[email protected]> wrote:
> Hi Boris,
>
>
>
> 2017-04-10 1:33 GMT+09:00 Boris Brezillon <[email protected]>:
> > On Mon, 3 Apr 2017 12:16:34 +0900
> > Masahiro Yamada <[email protected]> wrote:
> >
> >> Hi Boris,
> >>
> >>
> >>
> >> 2017-03-31 18:46 GMT+09:00 Boris Brezillon <[email protected]>:
> >>
> >> > You can try something like that when no explicit ecc.strength and
> >> > ecc.size has been set in the DT and when ECC_MAXIMIZE was not passed.
> >> >
> >> > static int
> >> > denali_get_closest_ecc_strength(struct denali_nand_info *denali,
> >> > int strength)
> >> > {
> >> > /*
> >> > * Whatever you need to select a strength that is greater than
> >> > * or equal to strength.
> >> > */
> >> >
> >> > return X;
> >> > }
> >>
> >>
> >> Is here anything specific to Denali?
> >
> > Well, only the denali driver knows what the hardware supports, though
> > having a generic function that takes a table of supported strengths
> > would work.
> >
> >>
> >>
> >> > static int denali_try_to_match_ecc_req(struct denali_nand_info *denali)
> >> > {
> >> > struct nand_chip *chip = &denali->nand;
> >> > struct mtd_info *mtd = nand_to_mtd(chip);
> >> > int max_ecc_bytes = mtd->oobsize - denali->bbtskipbytes;
> >> > int ecc_steps, ecc_strength, ecc_bytes;
> >> > int ecc_size = chip->ecc_step_ds;
> >> > int ecc_strength = chip->ecc_strength_ds;
> >> >
> >> > /*
> >> > * No information provided by the NAND chip, let the core
> >> > * maximize the strength.
> >> > */
> >> > if (!ecc_size || !ecc_strength)
> >> > return -ENOTSUPP;
> >> >
> >> > if (ecc_size > 512)
> >> > ecc_size = 1024;
> >> > else
> >> > ecc_size = 512;
> >> >
> >> > /* Adjust ECC step size based on hardware support. */
> >> > if (ecc_size == 1024 &&
> >> > !(denali->caps & DENALI_CAP_ECC_SIZE_1024))
> >> > ecc_size = 512;
> >> > else if(ecc_size == 512 &&
> >> > !(denali->caps & DENALI_CAP_ECC_SIZE_512))
> >> > ecc_size = 1024;
> >> >
> >> > if (ecc_size < chip->ecc_size_ds) {
> >> > /*
> >> > * When the selected size if smaller than the expected
> >> > * one we try to use the same strength but on 512 blocks
> >> > * so that we can still fix the same number of errors
> >> > * even if they are concentrated in the first 512bytes
> >> > * of a 1024bytes portion.
> >> > */
> >> > ecc_strength = chip->ecc_strength_ds;
> >> > ecc_strength = denali_get_closest_ecc_strength(denali,
> >> > ecc_strength);
> >> > } else {
> >> > /* Always prefer 1024bytes ECC blocks when possible. */
> >> > if (ecc_size != 1024 &&
> >> > (denali->caps & DENALI_CAP_ECC_SIZE_1024) &&
> >> > mtd->writesize > 1024)
> >> > ecc_size = 1024;
> >> >
> >> > /*
> >> > * Adjust the strength based on the selected ECC step
> >> > * size.
> >> > */
> >> > ecc_strength = DIV_ROUND_UP(ecc_size,
> >> > chip->ecc_step_ds) *
> >> > chip->ecc_strength_ds;
> >> > }
> >> >
> >> > ecc_bytes = denali_calc_ecc_bytes(ecc_size,
> >> > ecc_strength);
> >> > ecc_bytes *= mtd->writesize / ecc_size;
> >> >
> >> > /*
> >> > * If we don't have enough space, let the core maximize
> >> > * the strength.
> >> > */
> >> > if (ecc_bytes > max_ecc_bytes)
> >> > return -ENOTSUPP;
> >> >
> >> > chip->ecc.strength = ecc_strength;
> >> > chip->ecc.size = ecc_size;
> >> >
> >> > return 0;
> >> > }
> >>
> >>
> >> As a whole, this does not seem to driver-specific.
> >
> > It's almost controller-agnostic, except for the denali_calc_ecc_bytes()
> > function, but I guess we could ask drivers to implement a hook that is
> > passed the ECC step size and strength and returns the associated
> > number of ECC bytes.
> >
> >>
> >>
> >> [1] A driver provides some pairs of (ecc_strength, ecc_size)
> >> it can support.
> >>
> >> [2] The core framework knows the chip's requirement
> >> (ecc_strength_ds, ecc_size_ds).
> >>
> >>
> >> Then, the core framework provides a function
> >> to return a most recommended (ecc_strength, ecc_size).
> >>
> >>
> >>
> >> struct nand_ecc_spec {
> >> int ecc_strength;
> >> int ecc_size;
> >> };
> >>
> >> /*
> >> * This function choose the most recommented (ecc_str, ecc_size)
> >> * "recommended" means: minimum ecc stregth that meets
> >> * the chip's requirment.
> >> *
> >> *
> >> * @chip - nand_chip
> >> * @controller_ecc_spec - Array of (ecc_str, ecc_size) supported by the
> >> controller. (terminated by NULL as sentinel)
> >> */
> >> struct nand_ecc_spec * nand_try_to_match_ecc_req(struct nand_chip *chip,
> >> struct nand_ecc_spec
> >> *controller_ecc_spec)
> >> {
> >> /*
> >> * Return the pointer to the most recommended
> >> * struct nand_ecc_spec.
> >> * If nothing suitable found, return NULL.
> >> */
> >> }
> >>
> >
> > I like the idea, except I would do this slightly differently to avoid
> > declaring all combinations of stepsize and strengths
> >
> > struct nand_ecc_stepsize_info {
> > int stepsize;
> > int nstrengths;
> > int *strengths;
> > };
> >
> > struct nand_ecc_engine_caps {
> > int nstepsizes;
> > struct nand_ecc_stepsize_info *stepsizes;
> > int (*calc_ecc_bytes)(int stepsize, int strength);
> > };
> >
> > int nand_try_to_match_ecc_req(struct nand_chip *chip,
> > const struct nand_ecc_engine_caps *caps,
> > struct nand_ecc_spec *spec)
> > {
> > /*
> > * Find the most appropriate setting based on the ECC engine
> > * caps and fill the spec object accordingly.
> > * Returns 0 in case of success and a negative error code
> > * otherwise.
> > */
> > }
> >
> > Note that nand_try_to_match_ecc_req() has to be more generic than
> > denali_try_to_match_ecc_req() WRT step sizes, which will probably
> > complexify the logic.
>
>
> After I fiddle with this generic approach for a while,
> I started to feel like giving up.
I don't get it. What was the problem with my initial suggestion (the
denali specific one, not the generic approach)? You proposed to make it
generic, which, I agree, is a bit more complicated.
>
> I wonder if we really want over-implementation
> for covering _theoretically_ possible cases.
Okay, one more theoretical case I'd like to expose: you have board
design with different NAND parts which have different ECC requirements.
If you were about to describe the exact ECC strength you want for each
board you'll have to have different DTs. Maximizing the ECC strength
would still work, but what if the MTD user needs some OOB bytes (like
is the case with JFFS2) and ECC maximization reserved all of the
available bytes?
The other reason I prefer to have the drivers automatically guessing
what's appropriate is because then you don't have to care when writing
your DT.
>
> In practice, there are not so many ECC settings possible
> on a single controller.
>
> As for Denali IP, it would be theoretically possible to instantiate
> multiple ECC engines. However, in practice, there is no sensible
> reason to do so. At least, I do not know any real chip to support that.
>
> So, I'd like to simplify the logic for Denali.
>
> - Support either 512 or 1024 ECC size.
> If there is (ever) a controller that supports both,
> 1024 should be chosen.
>
> - ECC strength is not specified via DT, it is simply maximized.
>
> This simplifies the logic much and I believe this is enough.
>
> One more reason is, as we talked before,
> we need to match ECC setting between Linux and firmware (boot-loader),
If the bootloader implements the same logic it should match.
> so anyway we end up with using a fixed setting specified by DT.
>
Really, I don't see what's the problem with the function I proposed,
but I'm willing to make a concession.
Make the nand-ecc-strength+nand-ecc-step-size or nand-ecc-maximize
mandatory so that if someone ever needs to support the 'match NAND
requirements' feature we won't have to add a vendor specific property
like this one [1].
Are you fine with that?
[1]http://lxr.free-electrons.com/source/Documentation/devicetree/bindings/mtd/gpmi-nand.txt#L20
Hi Boris,
2017-04-11 16:56 GMT+09:00 Boris Brezillon <[email protected]>:
> Hi Masahiro,
>
> On Tue, 11 Apr 2017 15:19:21 +0900
> Masahiro Yamada <[email protected]> wrote:
>
>> Hi Boris,
>>
>>
>>
>> 2017-04-10 1:33 GMT+09:00 Boris Brezillon <[email protected]>:
>> > On Mon, 3 Apr 2017 12:16:34 +0900
>> > Masahiro Yamada <[email protected]> wrote:
>> >
>> >> Hi Boris,
>> >>
>> >>
>> >>
>> >> 2017-03-31 18:46 GMT+09:00 Boris Brezillon <[email protected]>:
>> >>
>> >> > You can try something like that when no explicit ecc.strength and
>> >> > ecc.size has been set in the DT and when ECC_MAXIMIZE was not passed.
>> >> >
>> >> > static int
>> >> > denali_get_closest_ecc_strength(struct denali_nand_info *denali,
>> >> > int strength)
>> >> > {
>> >> > /*
>> >> > * Whatever you need to select a strength that is greater than
>> >> > * or equal to strength.
>> >> > */
>> >> >
>> >> > return X;
>> >> > }
>> >>
>> >>
>> >> Is here anything specific to Denali?
>> >
>> > Well, only the denali driver knows what the hardware supports, though
>> > having a generic function that takes a table of supported strengths
>> > would work.
>> >
>> >>
>> >>
>> >> > static int denali_try_to_match_ecc_req(struct denali_nand_info *denali)
>> >> > {
>> >> > struct nand_chip *chip = &denali->nand;
>> >> > struct mtd_info *mtd = nand_to_mtd(chip);
>> >> > int max_ecc_bytes = mtd->oobsize - denali->bbtskipbytes;
>> >> > int ecc_steps, ecc_strength, ecc_bytes;
>> >> > int ecc_size = chip->ecc_step_ds;
>> >> > int ecc_strength = chip->ecc_strength_ds;
>> >> >
>> >> > /*
>> >> > * No information provided by the NAND chip, let the core
>> >> > * maximize the strength.
>> >> > */
>> >> > if (!ecc_size || !ecc_strength)
>> >> > return -ENOTSUPP;
>> >> >
>> >> > if (ecc_size > 512)
>> >> > ecc_size = 1024;
>> >> > else
>> >> > ecc_size = 512;
>> >> >
>> >> > /* Adjust ECC step size based on hardware support. */
>> >> > if (ecc_size == 1024 &&
>> >> > !(denali->caps & DENALI_CAP_ECC_SIZE_1024))
>> >> > ecc_size = 512;
>> >> > else if(ecc_size == 512 &&
>> >> > !(denali->caps & DENALI_CAP_ECC_SIZE_512))
>> >> > ecc_size = 1024;
>> >> >
>> >> > if (ecc_size < chip->ecc_size_ds) {
>> >> > /*
>> >> > * When the selected size if smaller than the expected
>> >> > * one we try to use the same strength but on 512 blocks
>> >> > * so that we can still fix the same number of errors
>> >> > * even if they are concentrated in the first 512bytes
>> >> > * of a 1024bytes portion.
>> >> > */
>> >> > ecc_strength = chip->ecc_strength_ds;
>> >> > ecc_strength = denali_get_closest_ecc_strength(denali,
>> >> > ecc_strength);
>> >> > } else {
>> >> > /* Always prefer 1024bytes ECC blocks when possible. */
>> >> > if (ecc_size != 1024 &&
>> >> > (denali->caps & DENALI_CAP_ECC_SIZE_1024) &&
>> >> > mtd->writesize > 1024)
>> >> > ecc_size = 1024;
>> >> >
>> >> > /*
>> >> > * Adjust the strength based on the selected ECC step
>> >> > * size.
>> >> > */
>> >> > ecc_strength = DIV_ROUND_UP(ecc_size,
>> >> > chip->ecc_step_ds) *
>> >> > chip->ecc_strength_ds;
>> >> > }
>> >> >
>> >> > ecc_bytes = denali_calc_ecc_bytes(ecc_size,
>> >> > ecc_strength);
>> >> > ecc_bytes *= mtd->writesize / ecc_size;
>> >> >
>> >> > /*
>> >> > * If we don't have enough space, let the core maximize
>> >> > * the strength.
>> >> > */
>> >> > if (ecc_bytes > max_ecc_bytes)
>> >> > return -ENOTSUPP;
>> >> >
>> >> > chip->ecc.strength = ecc_strength;
>> >> > chip->ecc.size = ecc_size;
>> >> >
>> >> > return 0;
>> >> > }
>> >>
>> >>
>> >> As a whole, this does not seem to driver-specific.
>> >
>> > It's almost controller-agnostic, except for the denali_calc_ecc_bytes()
>> > function, but I guess we could ask drivers to implement a hook that is
>> > passed the ECC step size and strength and returns the associated
>> > number of ECC bytes.
>> >
>> >>
>> >>
>> >> [1] A driver provides some pairs of (ecc_strength, ecc_size)
>> >> it can support.
>> >>
>> >> [2] The core framework knows the chip's requirement
>> >> (ecc_strength_ds, ecc_size_ds).
>> >>
>> >>
>> >> Then, the core framework provides a function
>> >> to return a most recommended (ecc_strength, ecc_size).
>> >>
>> >>
>> >>
>> >> struct nand_ecc_spec {
>> >> int ecc_strength;
>> >> int ecc_size;
>> >> };
>> >>
>> >> /*
>> >> * This function choose the most recommented (ecc_str, ecc_size)
>> >> * "recommended" means: minimum ecc stregth that meets
>> >> * the chip's requirment.
>> >> *
>> >> *
>> >> * @chip - nand_chip
>> >> * @controller_ecc_spec - Array of (ecc_str, ecc_size) supported by the
>> >> controller. (terminated by NULL as sentinel)
>> >> */
>> >> struct nand_ecc_spec * nand_try_to_match_ecc_req(struct nand_chip *chip,
>> >> struct nand_ecc_spec
>> >> *controller_ecc_spec)
>> >> {
>> >> /*
>> >> * Return the pointer to the most recommended
>> >> * struct nand_ecc_spec.
>> >> * If nothing suitable found, return NULL.
>> >> */
>> >> }
>> >>
>> >
>> > I like the idea, except I would do this slightly differently to avoid
>> > declaring all combinations of stepsize and strengths
>> >
>> > struct nand_ecc_stepsize_info {
>> > int stepsize;
>> > int nstrengths;
>> > int *strengths;
>> > };
>> >
>> > struct nand_ecc_engine_caps {
>> > int nstepsizes;
>> > struct nand_ecc_stepsize_info *stepsizes;
>> > int (*calc_ecc_bytes)(int stepsize, int strength);
>> > };
>> >
>> > int nand_try_to_match_ecc_req(struct nand_chip *chip,
>> > const struct nand_ecc_engine_caps *caps,
>> > struct nand_ecc_spec *spec)
>> > {
>> > /*
>> > * Find the most appropriate setting based on the ECC engine
>> > * caps and fill the spec object accordingly.
>> > * Returns 0 in case of success and a negative error code
>> > * otherwise.
>> > */
>> > }
>> >
>> > Note that nand_try_to_match_ecc_req() has to be more generic than
>> > denali_try_to_match_ecc_req() WRT step sizes, which will probably
>> > complexify the logic.
>>
>>
>> After I fiddle with this generic approach for a while,
>> I started to feel like giving up.
>
> I don't get it. What was the problem with my initial suggestion (the
> denali specific one, not the generic approach)? You proposed to make it
> generic, which, I agree, is a bit more complicated.
>
>>
>> I wonder if we really want over-implementation
>> for covering _theoretically_ possible cases.
>
> Okay, one more theoretical case I'd like to expose: you have board
> design with different NAND parts which have different ECC requirements.
> If you were about to describe the exact ECC strength you want for each
> board you'll have to have different DTs.
In this case, fixed ecc-strength in DT is not feasible.
> Maximizing the ECC strength
> would still work, but what if the MTD user needs some OOB bytes (like
> is the case with JFFS2) and ECC maximization reserved all of the
> available bytes?
JFFS2 needs some bytes in oob-free area for the clean marker.
You are right.
This implies NAND_ECC_MAXIMIZE is not very useful.
We do not know whether we have enough space left in oob, or not.
> The other reason I prefer to have the drivers automatically guessing
> what's appropriate is because then you don't have to care when writing
> your DT.
>
>>
>> In practice, there are not so many ECC settings possible
>> on a single controller.
>>
>> As for Denali IP, it would be theoretically possible to instantiate
>> multiple ECC engines. However, in practice, there is no sensible
>> reason to do so. At least, I do not know any real chip to support that.
>>
>> So, I'd like to simplify the logic for Denali.
>>
>> - Support either 512 or 1024 ECC size.
>> If there is (ever) a controller that supports both,
>> 1024 should be chosen.
>>
>> - ECC strength is not specified via DT, it is simply maximized.
>>
>> This simplifies the logic much and I believe this is enough.
>>
>> One more reason is, as we talked before,
>> we need to match ECC setting between Linux and firmware (boot-loader),
>
> If the bootloader implements the same logic it should match.
>
>> so anyway we end up with using a fixed setting specified by DT.
>>
>
> Really, I don't see what's the problem with the function I proposed,
> but I'm willing to make a concession.
> Make the nand-ecc-strength+nand-ecc-step-size or nand-ecc-maximize
> mandatory so that if someone ever needs to support the 'match NAND
> requirements' feature we won't have to add a vendor specific property
> like this one [1].
>
> Are you fine with that?
No. This requirement seems too strong.
At least, it is a problem for non-DT platforms.
If a driver provides ECC engine caps info,
perhaps ECC maximizing could be a generalized helper function as well.
I am trying this still.
--
Best Regards
Masahiro Yamada
On Fri, 14 Apr 2017 16:57:23 +0900
Masahiro Yamada <[email protected]> wrote:
> Hi Boris,
>
>
> 2017-04-11 16:56 GMT+09:00 Boris Brezillon <[email protected]>:
> > Hi Masahiro,
> >
> > On Tue, 11 Apr 2017 15:19:21 +0900
> > Masahiro Yamada <[email protected]> wrote:
> >
> >> Hi Boris,
> >>
> >>
> >>
> >> 2017-04-10 1:33 GMT+09:00 Boris Brezillon <[email protected]>:
> >> > On Mon, 3 Apr 2017 12:16:34 +0900
> >> > Masahiro Yamada <[email protected]> wrote:
> >> >
> >> >> Hi Boris,
> >> >>
> >> >>
> >> >>
> >> >> 2017-03-31 18:46 GMT+09:00 Boris Brezillon <[email protected]>:
> >> >>
> >> >> > You can try something like that when no explicit ecc.strength and
> >> >> > ecc.size has been set in the DT and when ECC_MAXIMIZE was not passed.
> >> >> >
> >> >> > static int
> >> >> > denali_get_closest_ecc_strength(struct denali_nand_info *denali,
> >> >> > int strength)
> >> >> > {
> >> >> > /*
> >> >> > * Whatever you need to select a strength that is greater than
> >> >> > * or equal to strength.
> >> >> > */
> >> >> >
> >> >> > return X;
> >> >> > }
> >> >>
> >> >>
> >> >> Is here anything specific to Denali?
> >> >
> >> > Well, only the denali driver knows what the hardware supports, though
> >> > having a generic function that takes a table of supported strengths
> >> > would work.
> >> >
> >> >>
> >> >>
> >> >> > static int denali_try_to_match_ecc_req(struct denali_nand_info *denali)
> >> >> > {
> >> >> > struct nand_chip *chip = &denali->nand;
> >> >> > struct mtd_info *mtd = nand_to_mtd(chip);
> >> >> > int max_ecc_bytes = mtd->oobsize - denali->bbtskipbytes;
> >> >> > int ecc_steps, ecc_strength, ecc_bytes;
> >> >> > int ecc_size = chip->ecc_step_ds;
> >> >> > int ecc_strength = chip->ecc_strength_ds;
> >> >> >
> >> >> > /*
> >> >> > * No information provided by the NAND chip, let the core
> >> >> > * maximize the strength.
> >> >> > */
> >> >> > if (!ecc_size || !ecc_strength)
> >> >> > return -ENOTSUPP;
> >> >> >
> >> >> > if (ecc_size > 512)
> >> >> > ecc_size = 1024;
> >> >> > else
> >> >> > ecc_size = 512;
> >> >> >
> >> >> > /* Adjust ECC step size based on hardware support. */
> >> >> > if (ecc_size == 1024 &&
> >> >> > !(denali->caps & DENALI_CAP_ECC_SIZE_1024))
> >> >> > ecc_size = 512;
> >> >> > else if(ecc_size == 512 &&
> >> >> > !(denali->caps & DENALI_CAP_ECC_SIZE_512))
> >> >> > ecc_size = 1024;
> >> >> >
> >> >> > if (ecc_size < chip->ecc_size_ds) {
> >> >> > /*
> >> >> > * When the selected size if smaller than the expected
> >> >> > * one we try to use the same strength but on 512 blocks
> >> >> > * so that we can still fix the same number of errors
> >> >> > * even if they are concentrated in the first 512bytes
> >> >> > * of a 1024bytes portion.
> >> >> > */
> >> >> > ecc_strength = chip->ecc_strength_ds;
> >> >> > ecc_strength = denali_get_closest_ecc_strength(denali,
> >> >> > ecc_strength);
> >> >> > } else {
> >> >> > /* Always prefer 1024bytes ECC blocks when possible. */
> >> >> > if (ecc_size != 1024 &&
> >> >> > (denali->caps & DENALI_CAP_ECC_SIZE_1024) &&
> >> >> > mtd->writesize > 1024)
> >> >> > ecc_size = 1024;
> >> >> >
> >> >> > /*
> >> >> > * Adjust the strength based on the selected ECC step
> >> >> > * size.
> >> >> > */
> >> >> > ecc_strength = DIV_ROUND_UP(ecc_size,
> >> >> > chip->ecc_step_ds) *
> >> >> > chip->ecc_strength_ds;
> >> >> > }
> >> >> >
> >> >> > ecc_bytes = denali_calc_ecc_bytes(ecc_size,
> >> >> > ecc_strength);
> >> >> > ecc_bytes *= mtd->writesize / ecc_size;
> >> >> >
> >> >> > /*
> >> >> > * If we don't have enough space, let the core maximize
> >> >> > * the strength.
> >> >> > */
> >> >> > if (ecc_bytes > max_ecc_bytes)
> >> >> > return -ENOTSUPP;
> >> >> >
> >> >> > chip->ecc.strength = ecc_strength;
> >> >> > chip->ecc.size = ecc_size;
> >> >> >
> >> >> > return 0;
> >> >> > }
> >> >>
> >> >>
> >> >> As a whole, this does not seem to driver-specific.
> >> >
> >> > It's almost controller-agnostic, except for the denali_calc_ecc_bytes()
> >> > function, but I guess we could ask drivers to implement a hook that is
> >> > passed the ECC step size and strength and returns the associated
> >> > number of ECC bytes.
> >> >
> >> >>
> >> >>
> >> >> [1] A driver provides some pairs of (ecc_strength, ecc_size)
> >> >> it can support.
> >> >>
> >> >> [2] The core framework knows the chip's requirement
> >> >> (ecc_strength_ds, ecc_size_ds).
> >> >>
> >> >>
> >> >> Then, the core framework provides a function
> >> >> to return a most recommended (ecc_strength, ecc_size).
> >> >>
> >> >>
> >> >>
> >> >> struct nand_ecc_spec {
> >> >> int ecc_strength;
> >> >> int ecc_size;
> >> >> };
> >> >>
> >> >> /*
> >> >> * This function choose the most recommented (ecc_str, ecc_size)
> >> >> * "recommended" means: minimum ecc stregth that meets
> >> >> * the chip's requirment.
> >> >> *
> >> >> *
> >> >> * @chip - nand_chip
> >> >> * @controller_ecc_spec - Array of (ecc_str, ecc_size) supported by the
> >> >> controller. (terminated by NULL as sentinel)
> >> >> */
> >> >> struct nand_ecc_spec * nand_try_to_match_ecc_req(struct nand_chip *chip,
> >> >> struct nand_ecc_spec
> >> >> *controller_ecc_spec)
> >> >> {
> >> >> /*
> >> >> * Return the pointer to the most recommended
> >> >> * struct nand_ecc_spec.
> >> >> * If nothing suitable found, return NULL.
> >> >> */
> >> >> }
> >> >>
> >> >
> >> > I like the idea, except I would do this slightly differently to avoid
> >> > declaring all combinations of stepsize and strengths
> >> >
> >> > struct nand_ecc_stepsize_info {
> >> > int stepsize;
> >> > int nstrengths;
> >> > int *strengths;
> >> > };
> >> >
> >> > struct nand_ecc_engine_caps {
> >> > int nstepsizes;
> >> > struct nand_ecc_stepsize_info *stepsizes;
> >> > int (*calc_ecc_bytes)(int stepsize, int strength);
> >> > };
> >> >
> >> > int nand_try_to_match_ecc_req(struct nand_chip *chip,
> >> > const struct nand_ecc_engine_caps *caps,
> >> > struct nand_ecc_spec *spec)
> >> > {
> >> > /*
> >> > * Find the most appropriate setting based on the ECC engine
> >> > * caps and fill the spec object accordingly.
> >> > * Returns 0 in case of success and a negative error code
> >> > * otherwise.
> >> > */
> >> > }
> >> >
> >> > Note that nand_try_to_match_ecc_req() has to be more generic than
> >> > denali_try_to_match_ecc_req() WRT step sizes, which will probably
> >> > complexify the logic.
> >>
> >>
> >> After I fiddle with this generic approach for a while,
> >> I started to feel like giving up.
> >
> > I don't get it. What was the problem with my initial suggestion (the
> > denali specific one, not the generic approach)? You proposed to make it
> > generic, which, I agree, is a bit more complicated.
> >
> >>
> >> I wonder if we really want over-implementation
> >> for covering _theoretically_ possible cases.
> >
> > Okay, one more theoretical case I'd like to expose: you have board
> > design with different NAND parts which have different ECC requirements.
> > If you were about to describe the exact ECC strength you want for each
> > board you'll have to have different DTs.
>
> In this case, fixed ecc-strength in DT is not feasible.
>
> > Maximizing the ECC strength
> > would still work, but what if the MTD user needs some OOB bytes (like
> > is the case with JFFS2) and ECC maximization reserved all of the
> > available bytes?
>
> JFFS2 needs some bytes in oob-free area for the clean marker.
> You are right.
> This implies NAND_ECC_MAXIMIZE is not very useful.
> We do not know whether we have enough space left in oob, or not.
>
>
>
> > The other reason I prefer to have the drivers automatically guessing
> > what's appropriate is because then you don't have to care when writing
> > your DT.
> >
> >>
> >> In practice, there are not so many ECC settings possible
> >> on a single controller.
> >>
> >> As for Denali IP, it would be theoretically possible to instantiate
> >> multiple ECC engines. However, in practice, there is no sensible
> >> reason to do so. At least, I do not know any real chip to support that.
> >>
> >> So, I'd like to simplify the logic for Denali.
> >>
> >> - Support either 512 or 1024 ECC size.
> >> If there is (ever) a controller that supports both,
> >> 1024 should be chosen.
> >>
> >> - ECC strength is not specified via DT, it is simply maximized.
> >>
> >> This simplifies the logic much and I believe this is enough.
> >>
> >> One more reason is, as we talked before,
> >> we need to match ECC setting between Linux and firmware (boot-loader),
> >
> > If the bootloader implements the same logic it should match.
> >
> >> so anyway we end up with using a fixed setting specified by DT.
> >>
> >
> > Really, I don't see what's the problem with the function I proposed,
> > but I'm willing to make a concession.
> > Make the nand-ecc-strength+nand-ecc-step-size or nand-ecc-maximize
> > mandatory so that if someone ever needs to support the 'match NAND
> > requirements' feature we won't have to add a vendor specific property
> > like this one [1].
> >
> > Are you fine with that?
>
> No. This requirement seems too strong.
Hm, can you give more details? All I want is a solution where we can
later support the feature I'm asking without adding a extra DT
property, and, in order to do that we must make sure the case you want
to support as a first step are explicitly requested in the DT.
It's as simple as:
if ((!ecc->strength || !ecc->size) &&
!(ecc->options & NAND_ECC_MAXIMIZE))
return -ENOTSUPP;
> At least, it is a problem for non-DT platforms.
Well, for non-DT platforms you have to keep ECC maximization anyway,
otherwise you're not backward compatible.
>
>
> If a driver provides ECC engine caps info,
> perhaps ECC maximizing could be a generalized helper function as well.
I don't get it. I thought the generic helper was too hard to implement.
Now you want to add a new functionality.
I'm not against this idea, but maybe it's easier to provide a denali
specific implementation before tackling the generic one.
Hi Boris,
2017-04-14 17:19 GMT+09:00 Boris Brezillon <[email protected]>:
> On Fri, 14 Apr 2017 16:57:23 +0900
> Masahiro Yamada <[email protected]> wrote:
>
>> Hi Boris,
>>
>>
>> 2017-04-11 16:56 GMT+09:00 Boris Brezillon <[email protected]>:
>> > Hi Masahiro,
>> >
>> > On Tue, 11 Apr 2017 15:19:21 +0900
>> > Masahiro Yamada <[email protected]> wrote:
>> >
>> >> Hi Boris,
>> >>
>> >>
>> >>
>> >> 2017-04-10 1:33 GMT+09:00 Boris Brezillon <[email protected]>:
>> >> > On Mon, 3 Apr 2017 12:16:34 +0900
>> >> > Masahiro Yamada <[email protected]> wrote:
>> >> >
>> >> >> Hi Boris,
>> >> >>
>> >> >>
>> >> >>
>> >> >> 2017-03-31 18:46 GMT+09:00 Boris Brezillon <[email protected]>:
>> >> >>
>> >> >> > You can try something like that when no explicit ecc.strength and
>> >> >> > ecc.size has been set in the DT and when ECC_MAXIMIZE was not passed.
>> >> >> >
>> >> >> > static int
>> >> >> > denali_get_closest_ecc_strength(struct denali_nand_info *denali,
>> >> >> > int strength)
>> >> >> > {
>> >> >> > /*
>> >> >> > * Whatever you need to select a strength that is greater than
>> >> >> > * or equal to strength.
>> >> >> > */
>> >> >> >
>> >> >> > return X;
>> >> >> > }
>> >> >>
>> >> >>
>> >> >> Is here anything specific to Denali?
>> >> >
>> >> > Well, only the denali driver knows what the hardware supports, though
>> >> > having a generic function that takes a table of supported strengths
>> >> > would work.
>> >> >
>> >> >>
>> >> >>
>> >> >> > static int denali_try_to_match_ecc_req(struct denali_nand_info *denali)
>> >> >> > {
>> >> >> > struct nand_chip *chip = &denali->nand;
>> >> >> > struct mtd_info *mtd = nand_to_mtd(chip);
>> >> >> > int max_ecc_bytes = mtd->oobsize - denali->bbtskipbytes;
>> >> >> > int ecc_steps, ecc_strength, ecc_bytes;
>> >> >> > int ecc_size = chip->ecc_step_ds;
>> >> >> > int ecc_strength = chip->ecc_strength_ds;
>> >> >> >
>> >> >> > /*
>> >> >> > * No information provided by the NAND chip, let the core
>> >> >> > * maximize the strength.
>> >> >> > */
>> >> >> > if (!ecc_size || !ecc_strength)
>> >> >> > return -ENOTSUPP;
>> >> >> >
>> >> >> > if (ecc_size > 512)
>> >> >> > ecc_size = 1024;
>> >> >> > else
>> >> >> > ecc_size = 512;
>> >> >> >
>> >> >> > /* Adjust ECC step size based on hardware support. */
>> >> >> > if (ecc_size == 1024 &&
>> >> >> > !(denali->caps & DENALI_CAP_ECC_SIZE_1024))
>> >> >> > ecc_size = 512;
>> >> >> > else if(ecc_size == 512 &&
>> >> >> > !(denali->caps & DENALI_CAP_ECC_SIZE_512))
>> >> >> > ecc_size = 1024;
>> >> >> >
>> >> >> > if (ecc_size < chip->ecc_size_ds) {
>> >> >> > /*
>> >> >> > * When the selected size if smaller than the expected
>> >> >> > * one we try to use the same strength but on 512 blocks
>> >> >> > * so that we can still fix the same number of errors
>> >> >> > * even if they are concentrated in the first 512bytes
>> >> >> > * of a 1024bytes portion.
>> >> >> > */
>> >> >> > ecc_strength = chip->ecc_strength_ds;
>> >> >> > ecc_strength = denali_get_closest_ecc_strength(denali,
>> >> >> > ecc_strength);
>> >> >> > } else {
>> >> >> > /* Always prefer 1024bytes ECC blocks when possible. */
>> >> >> > if (ecc_size != 1024 &&
>> >> >> > (denali->caps & DENALI_CAP_ECC_SIZE_1024) &&
>> >> >> > mtd->writesize > 1024)
>> >> >> > ecc_size = 1024;
>> >> >> >
>> >> >> > /*
>> >> >> > * Adjust the strength based on the selected ECC step
>> >> >> > * size.
>> >> >> > */
>> >> >> > ecc_strength = DIV_ROUND_UP(ecc_size,
>> >> >> > chip->ecc_step_ds) *
>> >> >> > chip->ecc_strength_ds;
>> >> >> > }
>> >> >> >
>> >> >> > ecc_bytes = denali_calc_ecc_bytes(ecc_size,
>> >> >> > ecc_strength);
>> >> >> > ecc_bytes *= mtd->writesize / ecc_size;
>> >> >> >
>> >> >> > /*
>> >> >> > * If we don't have enough space, let the core maximize
>> >> >> > * the strength.
>> >> >> > */
>> >> >> > if (ecc_bytes > max_ecc_bytes)
>> >> >> > return -ENOTSUPP;
>> >> >> >
>> >> >> > chip->ecc.strength = ecc_strength;
>> >> >> > chip->ecc.size = ecc_size;
>> >> >> >
>> >> >> > return 0;
>> >> >> > }
>> >> >>
>> >> >>
>> >> >> As a whole, this does not seem to driver-specific.
>> >> >
>> >> > It's almost controller-agnostic, except for the denali_calc_ecc_bytes()
>> >> > function, but I guess we could ask drivers to implement a hook that is
>> >> > passed the ECC step size and strength and returns the associated
>> >> > number of ECC bytes.
>> >> >
>> >> >>
>> >> >>
>> >> >> [1] A driver provides some pairs of (ecc_strength, ecc_size)
>> >> >> it can support.
>> >> >>
>> >> >> [2] The core framework knows the chip's requirement
>> >> >> (ecc_strength_ds, ecc_size_ds).
>> >> >>
>> >> >>
>> >> >> Then, the core framework provides a function
>> >> >> to return a most recommended (ecc_strength, ecc_size).
>> >> >>
>> >> >>
>> >> >>
>> >> >> struct nand_ecc_spec {
>> >> >> int ecc_strength;
>> >> >> int ecc_size;
>> >> >> };
>> >> >>
>> >> >> /*
>> >> >> * This function choose the most recommented (ecc_str, ecc_size)
>> >> >> * "recommended" means: minimum ecc stregth that meets
>> >> >> * the chip's requirment.
>> >> >> *
>> >> >> *
>> >> >> * @chip - nand_chip
>> >> >> * @controller_ecc_spec - Array of (ecc_str, ecc_size) supported by the
>> >> >> controller. (terminated by NULL as sentinel)
>> >> >> */
>> >> >> struct nand_ecc_spec * nand_try_to_match_ecc_req(struct nand_chip *chip,
>> >> >> struct nand_ecc_spec
>> >> >> *controller_ecc_spec)
>> >> >> {
>> >> >> /*
>> >> >> * Return the pointer to the most recommended
>> >> >> * struct nand_ecc_spec.
>> >> >> * If nothing suitable found, return NULL.
>> >> >> */
>> >> >> }
>> >> >>
>> >> >
>> >> > I like the idea, except I would do this slightly differently to avoid
>> >> > declaring all combinations of stepsize and strengths
>> >> >
>> >> > struct nand_ecc_stepsize_info {
>> >> > int stepsize;
>> >> > int nstrengths;
>> >> > int *strengths;
>> >> > };
>> >> >
>> >> > struct nand_ecc_engine_caps {
>> >> > int nstepsizes;
>> >> > struct nand_ecc_stepsize_info *stepsizes;
>> >> > int (*calc_ecc_bytes)(int stepsize, int strength);
>> >> > };
>> >> >
>> >> > int nand_try_to_match_ecc_req(struct nand_chip *chip,
>> >> > const struct nand_ecc_engine_caps *caps,
>> >> > struct nand_ecc_spec *spec)
>> >> > {
>> >> > /*
>> >> > * Find the most appropriate setting based on the ECC engine
>> >> > * caps and fill the spec object accordingly.
>> >> > * Returns 0 in case of success and a negative error code
>> >> > * otherwise.
>> >> > */
>> >> > }
>> >> >
>> >> > Note that nand_try_to_match_ecc_req() has to be more generic than
>> >> > denali_try_to_match_ecc_req() WRT step sizes, which will probably
>> >> > complexify the logic.
>> >>
>> >>
>> >> After I fiddle with this generic approach for a while,
>> >> I started to feel like giving up.
>> >
>> > I don't get it. What was the problem with my initial suggestion (the
>> > denali specific one, not the generic approach)? You proposed to make it
>> > generic, which, I agree, is a bit more complicated.
>> >
>> >>
>> >> I wonder if we really want over-implementation
>> >> for covering _theoretically_ possible cases.
>> >
>> > Okay, one more theoretical case I'd like to expose: you have board
>> > design with different NAND parts which have different ECC requirements.
>> > If you were about to describe the exact ECC strength you want for each
>> > board you'll have to have different DTs.
>>
>> In this case, fixed ecc-strength in DT is not feasible.
>>
>> > Maximizing the ECC strength
>> > would still work, but what if the MTD user needs some OOB bytes (like
>> > is the case with JFFS2) and ECC maximization reserved all of the
>> > available bytes?
>>
>> JFFS2 needs some bytes in oob-free area for the clean marker.
>> You are right.
>> This implies NAND_ECC_MAXIMIZE is not very useful.
>> We do not know whether we have enough space left in oob, or not.
>>
>>
>>
>> > The other reason I prefer to have the drivers automatically guessing
>> > what's appropriate is because then you don't have to care when writing
>> > your DT.
>> >
>> >>
>> >> In practice, there are not so many ECC settings possible
>> >> on a single controller.
>> >>
>> >> As for Denali IP, it would be theoretically possible to instantiate
>> >> multiple ECC engines. However, in practice, there is no sensible
>> >> reason to do so. At least, I do not know any real chip to support that.
>> >>
>> >> So, I'd like to simplify the logic for Denali.
>> >>
>> >> - Support either 512 or 1024 ECC size.
>> >> If there is (ever) a controller that supports both,
>> >> 1024 should be chosen.
>> >>
>> >> - ECC strength is not specified via DT, it is simply maximized.
>> >>
>> >> This simplifies the logic much and I believe this is enough.
>> >>
>> >> One more reason is, as we talked before,
>> >> we need to match ECC setting between Linux and firmware (boot-loader),
>> >
>> > If the bootloader implements the same logic it should match.
>> >
>> >> so anyway we end up with using a fixed setting specified by DT.
>> >>
>> >
>> > Really, I don't see what's the problem with the function I proposed,
>> > but I'm willing to make a concession.
>> > Make the nand-ecc-strength+nand-ecc-step-size or nand-ecc-maximize
>> > mandatory so that if someone ever needs to support the 'match NAND
>> > requirements' feature we won't have to add a vendor specific property
>> > like this one [1].
>> >
>> > Are you fine with that?
>>
>> No. This requirement seems too strong.
>
> Hm, can you give more details? All I want is a solution where we can
> later support the feature I'm asking without adding a extra DT
> property, and, in order to do that we must make sure the case you want
> to support as a first step are explicitly requested in the DT.
>
> It's as simple as:
>
> if ((!ecc->strength || !ecc->size) &&
> !(ecc->options & NAND_ECC_MAXIMIZE))
> return -ENOTSUPP;
If a controller supports only one possible value for nand-ecc-step-size,
users have no choice anyway.
For UniPhier SoCs,
nand-ecc-step-size = <1024>;
nand-ecc-strength = <8> or <16> or <24>;
But, it is harmless even if we specify nand-ecc-step-size explicitly.
So, I do not argue here.
>> At least, it is a problem for non-DT platforms.
>
> Well, for non-DT platforms you have to keep ECC maximization anyway,
> otherwise you're not backward compatible.
>
>>
>>
>> If a driver provides ECC engine caps info,
>> perhaps ECC maximizing could be a generalized helper function as well.
>
> I don't get it. I thought the generic helper was too hard to implement.
> Now you want to add a new functionality.
>
> I'm not against this idea, but maybe it's easier to provide a denali
> specific implementation before tackling the generic one.
I think there is a common logic in matching request and maximizing.
I could not explain well in my words, so I wrote a patch:
http://patchwork.ozlabs.org/patch/752107/
Could you check it?
--
Best Regards
Masahiro Yamada