diff --git a/arch/arm/src/sama5/sam_nand.c b/arch/arm/src/sama5/sam_nand.c index e41a7b8c61..8227ba4d25 100644 --- a/arch/arm/src/sama5/sam_nand.c +++ b/arch/arm/src/sama5/sam_nand.c @@ -219,24 +219,22 @@ static int nand_dma_write(struct sam_nandcs_s *priv, /* Raw Data Transfer Helpers */ -static int nand_nfcsram_read(uintptr_t src, uint8_t *dest, - size_t buflen); +static int nand_nfcsram_read(struct sam_nandcs_s *priv, + uint8_t *buffer, uint16_t buflen); +#ifdef CONFIG_SAMA5_HAVE_PMECC static int nand_read(struct sam_nandcs_s *priv, uint8_t *buffer, - uint16_t buflen, uint16_t offset); + uint16_t buflen); +#endif #ifdef CONFIG_SAMA5_HAVE_PMECC static int nand_read_pmecc(struct sam_nandcs_s *priv, off_t block, unsigned int page, void *data); #endif -static int nand_nfcsram_write(const uint8_t *src, uintptr_t dest, - size_t buflen); -static int nand_smc_write8(const uint8_t *src, uintptr_t dest, - size_t buflen); -static int nand_smc_write16(const uint8_t *src, uintptr_t dest, - size_t buflen); -static int nand_write(struct sam_nandcs_s *priv, bool nfcsram, - uint8_t *buffer, size_t buflen, off_t offset); +static int nand_nfcsram_write(struct sam_nandcs_s *priv, + uint8_t *buffer, uint16_t buflen, uint16_t offset); +static int nand_write(struct sam_nandcs_s *priv, uint8_t *buffer, + uint16_t buflen, uint16_t offset); /* NAND Access Helpers */ @@ -1453,34 +1451,6 @@ static int nand_dma_write(struct sam_nandcs_s *priv, * Name: nand_nfcsram_read * * Description: - * Read data from NAND using the NFC SRAM (without DMA) - * - * Input Parameters: - * src - NAND data source address - * dest - Buffer that will receive the data from the read - * buflen - The number of bytes to transfer - * - * Returned Value - * OK always - * - ****************************************************************************/ - -static int nand_nfcsram_read(uintptr_t src, uint8_t *dest, size_t buflen) -{ - uint8_t *src8 = (uint8_t *)src; - - for (; buflen > 0; buflen--) - { - *dest++ = *src8++; - } - - return OK; -} - -/**************************************************************************** - * Name: nand_read - * - * Description: * Read data from NAND using the NFC SRAM * * Input Parameters: @@ -1495,28 +1465,13 @@ static int nand_nfcsram_read(uintptr_t src, uint8_t *dest, size_t buflen) * ****************************************************************************/ -static int nand_read(struct sam_nandcs_s *priv, uint8_t *buffer, - uint16_t buflen, uint16_t offset) +static int nand_nfcsram_read(struct sam_nandcs_s *priv, uint8_t *buffer, + uint16_t buflen) { - uintptr_t src; -#ifdef CONFIG_SAMA5_NAND_DMA - uint32_t dmaflags; -#endif - int buswidth; + int remaining; int ret; - fvdbg("buffer=%p buflen=%d offset=%d\n", buffer, buflen, offset); - - /* Get the data source: NFC SRAM (perhaps with an offset) - * NOTE: We could use the address priv->raw.dataaddr if we want to - * bypass NFC SRAM. - */ - - src = NFCSRAM_BASE + offset; - - /* Get the buswidth */ - - buswidth = nandmodel_getbuswidth(&priv->raw.model); + fvdbg("buffer=%p buflen=%d\n", buffer, buflen); #ifdef CONFIG_SAMA5_NAND_DMA /* Then perform the transfer via memory-to-memory DMA or not, depending @@ -1527,13 +1482,19 @@ static int nand_read(struct sam_nandcs_s *priv, uint8_t *buffer, if (priv->dma && buflen > CONFIG_SAMA5_NAND_DMA_THRESHOLD) { + /* Get the buswidth */ + + int buswidth = nandmodel_getbuswidth(&priv->raw.model); + /* Select NFC SRAM DMA */ - dmaflags = (buswidth == 16 ? NFCSRAM_DMA_FLAGS16 : NFCSRAM_DMA_FLAGS8); + uint32_t dmaflags = + (buswidth == 16 ? NFCSRAM_DMA_FLAGS16 : NFCSRAM_DMA_FLAGS8); /* Transfer using DMA */ - ret = nand_dma_read(priv, src, (uintptr_t)buffer, buflen, dmaflags); + ret = nand_dma_read(priv, NFCSRAM_BASE, (uintptr_t)buffer, buflen, + dmaflags); } else #endif @@ -1541,12 +1502,110 @@ static int nand_read(struct sam_nandcs_s *priv, uint8_t *buffer, /* Transfer without DMA */ { - ret = nand_nfcsram_read(src, buffer, buflen); + uint8_t *src8 = (uint8_t *)NFCSRAM_BASE; + uint8_t *dest8 = buffer; + + for (remaining = buflen; remaining > 0; remaining--) + { + *dest8++ = *src8++; + } + + ret = OK; + } + + nand_dump("NFS SRAM Read", buffer, buflen); + return ret; +} + +/**************************************************************************** + * Name: nand_read + * + * Description: + * Read data directly from the NAND data address. Currently this only used + * by the PMECC logic which I could not get working if I read from NFC SRAM. + * + * Input Parameters: + * priv - Lower-half, private NAND FLASH device state + * nfcsram - True: Use NFC Host SRAM + * buffer - Buffer that provides the data for the write + * + * Returned Value + * OK on success; a negated errno value on failure. + * + ****************************************************************************/ + +#ifdef CONFIG_SAMA5_HAVE_PMECC +static int nand_read(struct sam_nandcs_s *priv, uint8_t *buffer, + uint16_t buflen) +{ + int remaining; + int buswidth; + int ret; + + fvdbg("buffer=%p buflen=%d\n", buffer, (int)buflen); + + /* Get the buswidth */ + + buswidth = nandmodel_getbuswidth(&priv->raw.model); + +#ifdef CONFIG_SAMA5_NAND_DMA + /* Then perform the transfer via memory-to-memory DMA or not, depending + * on if we have a DMA channel assigned and if the transfer is + * sufficiently large. Small DMAs (e.g., for spare data) are not peformed + * because the DMA context switch can take more time that the DMA itself. + */ + + if (priv->dma && buflen > CONFIG_SAMA5_NAND_DMA_THRESHOLD) + { + /* Select NFC DATA DMA */ + + uint32_t dmaflags = + (buswidth == 16 ? NAND_DMA_FLAGS16 : NAND_DMA_FLAGS8); + + /* Transfer using DMA */ + + ret = nand_dma_read(priv, priv->raw.dataaddr, (uintptr_t)buffer, + buflen, dmaflags); + } + else +#endif + + /* Transfer without DMA */ + + { + /* Check the data bus width of the NAND FLASH */ + + remaining = buflen; + if (buswidth == 16) + { + volatile uint16_t *src16 = (volatile uint16_t *)priv->raw.dataaddr; + uint16_t *dest16 = (uint16_t *)buffer; + + DEBUGASSERT(((uintptr_t)buffer & 1) == 0); + + for (; remaining > 1; remaining -= sizeof(uint16_t)) + { + *dest16++ = *src16; + } + } + else + { + volatile uint8_t *src8 = (volatile uint8_t *)priv->raw.dataaddr; + uint8_t *dest8 = (uint8_t *)buffer; + + for (; remaining > 0; remaining--) + { + *dest8++ = *src8; + } + } + + ret = OK; } nand_dump("NAND Read", buffer, buflen); return ret; } +#endif /**************************************************************************** * Name: nand_read_pmecc @@ -1616,7 +1675,7 @@ static int nand_read_pmecc(struct sam_nandcs_s *priv, off_t block, regval |= (HSMC_CFG_RSPARE | HSMC_CFG_RBEDGE | HSMC_CFG_DTOCYC(15) | HSMC_CFG_DTOMUL_1048576 | - HSMC_CFG_NFCSPARESIZE((sparesize-1) >> 2)); + HSMC_CFG_NFCSPARESIZE((sparesize - 1) >> 2)); nand_putreg(SAM_HSMC_CFG, regval); /* Calculate actual address of the page */ @@ -1639,9 +1698,12 @@ static int nand_read_pmecc(struct sam_nandcs_s *priv, off_t block, /* Start the data phase and perform the transfer */ nand_putreg(SAM_HSMC_PMECCTRL, HSMC_PMECCTRL_DATA); + + nand_setup_rbedge(priv); nand_nfc_cleale(priv, - HSMC_ALE_COL_EN | HSMC_ALE_ROW_EN | HSMC_CLE_VCMD2_EN | HSMC_CLE_DATA_EN, + HSMC_ALE_COL_EN | HSMC_ALE_ROW_EN | HSMC_CLE_VCMD2_EN, COMMAND_READ_1, COMMAND_READ_2, 0, rowaddr); + nand_wait_rbedge(priv); /* Reset the PMECC module */ @@ -1651,20 +1713,21 @@ static int nand_read_pmecc(struct sam_nandcs_s *priv, off_t block, nand_putreg(SAM_HSMC_PMECCTRL, HSMC_PMECCTRL_DATA); - /* Read the data area into the caller provided buffer (pagesize bytes) */ + /* Read the data area into the caller provided buffer (pagesize bytes). + * NOTE: NFC SRAM is not used. In that case, the wait for PMECC not + * busy below would hang. + */ - ret = nand_read(priv, (uint8_t *)data, pagesize, 0); + ret = nand_read(priv, (uint8_t *)data, pagesize); if (ret < 0) { fdbg("ERROR: nand_read for data region failed: %d\n", ret); return ret; } - /* Read the spare area into priv->raw.spare. The data to be read lies at - * offset pagesize in NFC SRAM. - */ + /* Now read the spare area into priv->raw.spare (sparesize bytes). */ - ret = nand_read(priv, priv->raw.spare, priv->raw.model.sparesize, pagesize); + ret = nand_read(priv, priv->raw.spare, sparesize); if (ret < 0) { fdbg("ERROR: nand_read for spare region failed: %d\n", ret); @@ -1682,98 +1745,10 @@ static int nand_read_pmecc(struct sam_nandcs_s *priv, off_t block, * Name: nand_nfcsram_write * * Description: - * Write data to NAND using the NFC SRAM (without DMA) - * - * Input Parameters: - * src - Buffer that provides the data for the write - * dest - NAND data destination address - * buflen - The number of bytes to transfer - * - * Returned Value - * OK always - * - ****************************************************************************/ - -static int nand_nfcsram_write(const uint8_t *src, uintptr_t dest, size_t buflen) -{ - uint8_t *dest8 = (uint8_t *)dest; - - for (; buflen > 0; buflen--) - { - *dest8++ = *src++; - } - - return OK; -} - -/**************************************************************************** - * Name: nand_smc_write8 - * - * Description: - * Write 8-bit wide data to NAND using the NAND data address (without DMA) - * - * Input Parameters: - * src - Buffer that provides the data for the write - * dest - NAND data destination address - * buflen - The number of bytes to transfer - * - * Returned Value - * OK always - * - ****************************************************************************/ - -static int nand_smc_write8(const uint8_t *src, uintptr_t dest, size_t buflen) -{ - volatile uint8_t *dest8 = (volatile uint8_t *)dest; - - for (; buflen > 0; buflen--) - { - *dest8 = *src++; - } - - return OK; -} - -/**************************************************************************** - * Name: nand_smc_write16 - * - * Description: - * Write 16-bit wide data to NAND using the NAND data address (without DMA) - * - * Input Parameters: - * src - Buffer that provides the data for the write - * dest - NAND data destination address - * buflen - The number of bytes to transfer - * - * Returned Value - * OK always - * - ****************************************************************************/ - -static int nand_smc_write16(const uint8_t *src, uintptr_t dest, size_t buflen) -{ - volatile uint16_t *dest16 = (volatile uint16_t *)dest; - const uint16_t *src16 = (const uint16_t *)src; - - DEBUGASSERT(((uintptr_t)src & 1) == 0); - - for (; buflen > 1; buflen -= sizeof(uint16_t)) - { - *dest16 = *src16++; - } - - return OK; -} - -/**************************************************************************** - * Name: nand_write - * - * Description: - * Write data to NAND using the appropriate method + * Write data to NAND using NAND NFC SRAM * * Input Parameters: * priv - Lower-half, private NAND FLASH device state - * nfcsram - True: Use NFC Host SRAM * buffer - Buffer that provides the data for the write * offset - Data offset in bytes * @@ -1782,54 +1757,18 @@ static int nand_smc_write16(const uint8_t *src, uintptr_t dest, size_t buflen) * ****************************************************************************/ -static int nand_write(struct sam_nandcs_s *priv, bool nfcsram, - uint8_t *buffer, size_t buflen, off_t offset) +static int nand_nfcsram_write(struct sam_nandcs_s *priv, uint8_t *buffer, + uint16_t buflen, uint16_t offset) { uintptr_t dest; -#ifdef CONFIG_SAMA5_NAND_DMA - uint32_t dmaflags; -#endif - int buswidth; + int ret; - fvdbg("nfcsram=%d buffer=%p buflen=%d offset=%d\n", - nfcsram, buffer, (int)buflen, (int)offset); + fvdbg("buffer=%p buflen=%d offset=%d\n", buffer, buflen, offset); + nand_dump("NFC SRAM Write", buffer, buflen); - nand_dump("NAND Write", buffer, buflen); + /* Apply the offset to the destination address */ - /* Get the buswidth */ - - buswidth = nandmodel_getbuswidth(&priv->raw.model); - - /* Pick the data destination: The NFC SRAM or the NAND data address */ - - if (nfcsram) - { - /* Destination is NFC SRAM */ - - dest = NFCSRAM_BASE; - -#ifdef CONFIG_SAMA5_NAND_DMA - /* Select NFC SRAM DMA */ - - dmaflags = (buswidth == 16 ? NFCSRAM_DMA_FLAGS16 : NFCSRAM_DMA_FLAGS8); -#endif - } - else - { - /* Destination is NFC NAND */ - - dest = priv->raw.dataaddr; - -#ifdef CONFIG_SAMA5_NAND_DMA - /* Select NAND DMA */ - - dmaflags = (buswidth == 16 ? NAND_DMA_FLAGS16 : NAND_DMA_FLAGS8); -#endif - } - - /* Apply the offset to the source address */ - - dest += offset; + dest = NFCSRAM_BASE + offset; #ifdef CONFIG_SAMA5_NAND_DMA /* Then perform the transfer via memory-to-memory DMA or not, depending @@ -1840,32 +1779,124 @@ static int nand_write(struct sam_nandcs_s *priv, bool nfcsram, if (priv->dma && buflen > CONFIG_SAMA5_NAND_DMA_THRESHOLD) { + /* Get the buswidth */ + + int buswidth = nandmodel_getbuswidth(&priv->raw.model); + + /* Select NFC SRAM DMA */ + + uint32_t dmaflags = + (buswidth == 16 ? NFCSRAM_DMA_FLAGS16 : NFCSRAM_DMA_FLAGS8); + /* Transfer using DMA */ - return nand_dma_write(priv, (uintptr_t)buffer, dest, buflen, dmaflags); + ret = nand_dma_write(priv, (uintptr_t)buffer, dest, buflen, dmaflags); } else #endif /* Transfer without DMA */ - if (nfcsram) { - return nand_nfcsram_write(buffer, dest, buflen); + uint8_t *dest8 = (uint8_t *)dest; + + for (; buflen > 0; buflen--) + { + *dest8++ = *buffer++; + } + + ret = OK; + } + + return ret; +} + +/**************************************************************************** + * Name: nand_write + * + * Description: + * Write data to NAND using the NAND data address. + * + * Input Parameters: + * priv - Lower-half, private NAND FLASH device state + * buffer - Buffer that provides the data for the write + * offset - Data offset in bytes + * + * Returned Value + * OK on success; a negated errno value on failure. + * + ****************************************************************************/ + +static int nand_write(struct sam_nandcs_s *priv, uint8_t *buffer, + uint16_t buflen, uint16_t offset) +{ + uintptr_t dest; + int buswidth; + int ret; + + fvdbg("buffer=%p buflen=%d offset=%d\n", buffer, buflen, offset); + nand_dump("NAND Write", buffer, buflen); + + /* Apply the offset to the destination address */ + + dest = priv->raw.dataaddr + offset; + + /* Get the buswidth */ + + buswidth = nandmodel_getbuswidth(&priv->raw.model); + +#ifdef CONFIG_SAMA5_NAND_DMA + /* Then perform the transfer via memory-to-memory DMA or not, depending + * on if we have a DMA channel assigned and if the transfer is + * sufficiently large. Small DMAs (e.g., for spare data) are not peformed + * because the DMA context switch can take more time that the DMA itself. + */ + + if (priv->dma && buflen > CONFIG_SAMA5_NAND_DMA_THRESHOLD) + { + /* Select NFC DATA DMA */ + + uint32_t dmaflags = + (buswidth == 16 ? NAND_DMA_FLAGS16 : NAND_DMA_FLAGS8); + + /* Transfer using DMA */ + + ret = nand_dma_write(priv, (uintptr_t)buffer, dest, buflen, dmaflags); } else +#endif + + /* Transfer without DMA */ + { /* Check the data bus width of the NAND FLASH */ if (buswidth == 16) { - return nand_smc_write16(buffer, dest, buflen); + volatile uint16_t *dest16 = (volatile uint16_t *)dest; + const uint16_t *src16 = (const uint16_t *)buffer; + + DEBUGASSERT(((uintptr_t)buffer & 1) == 0); + + for (; buflen > 1; buflen -= sizeof(uint16_t)) + { + *dest16 = *src16++; + } } else { - return nand_smc_write8(buffer, dest, buflen); + volatile uint8_t *dest8 = (volatile uint8_t *)dest; + + for (; buflen > 0; buflen--) + { + *dest8 = *buffer++; + } } + + ret = OK; } + + return ret; } /**************************************************************************** @@ -1938,7 +1969,7 @@ static int nand_readpage_noecc(struct sam_nandcs_s *priv, off_t block, /* Configure the SMC */ regval |= (HSMC_CFG_RBEDGE | HSMC_CFG_DTOCYC(15) | HSMC_CFG_DTOMUL_1048576 | - HSMC_CFG_NFCSPARESIZE((sparesize-1) >> 2)); + HSMC_CFG_NFCSPARESIZE((sparesize -1 ) >> 2)); nand_putreg(SAM_HSMC_CFG, regval); /* Calculate actual address of the page */ @@ -1958,10 +1989,10 @@ static int nand_readpage_noecc(struct sam_nandcs_s *priv, off_t block, if (data) { - ret = nand_read(priv, (uint8_t *)data, pagesize, 0); + ret = nand_nfcsram_read(priv, (uint8_t *)data, pagesize); if (ret < 0) { - fdbg("ERROR: nand_read for data region failed: %d\n", ret); + fdbg("ERROR: nand_nfcsram_read for data region failed: %d\n", ret); return ret; } } @@ -1973,10 +2004,10 @@ static int nand_readpage_noecc(struct sam_nandcs_s *priv, off_t block, if (spare) { - ret = nand_read(priv, (uint8_t *)spare, sparesize, 0); + ret = nand_nfcsram_read(priv, (uint8_t *)spare, sparesize); if (ret < 0) { - fdbg("ERROR: nand_read for spare region failed: %d\n", ret); + fdbg("ERROR: nand_nfcsram_read for spare region failed: %d\n", ret); return ret; } } @@ -2033,7 +2064,8 @@ static int nand_readpage_pmecc(struct sam_nandcs_s *priv, off_t block, ret = nand_read_pmecc(priv, block, page, data); if (ret < 0) { - fdbg("ERROR: Failed to read page\n"); + fdbg("ERROR: Block %d page %d Failed to read page\n", + block, page, ret); goto errout; } @@ -2042,15 +2074,18 @@ static int nand_readpage_pmecc(struct sam_nandcs_s *priv, off_t block, regval = nand_getreg(SAM_HSMC_PMECCISR); if (regval) { - fdbg("ERROR: block=%d page=%d Corrupted sectors: %08x\n", - block, page, regval); + /* Bad sectors. Check if this is because spare area has been erased */ + /* First, re-read the spare area. REVISIT: Is this necessary? */ - /* Check if the spare area was erased - * REVISIT: Necessary to re-read. Isn't the spare data alread - * intack in priv->raw.spare from nand_read_pmecc()? - */ + ret = nand_readpage_noecc(priv, block, page, NULL, priv->raw.spare); + if (ret < 0) + { + fdbg("ERROR: Block %d page %d Failed to re-read spare area: %d\n", + block, page, ret); + goto errout; + } - //nand_readpage_noecc(priv, block, page, NULL, priv->raw.spare); + /* Then check if all bytes are in the erased state */ sparesize = nandmodel_getsparesize(&priv->raw.model); for (i = 0 ; i < sparesize; i++) @@ -2061,14 +2096,21 @@ static int nand_readpage_pmecc(struct sam_nandcs_s *priv, off_t block, } } - /* The spare area has been erased */ + /* Has the spare area has been erased? */ if (i >= sparesize) { - /* Clear sector errors */ + /* Yes.. clear sector errors */ + fdbg("Block=%d page=%d has been erased: %08x\n", + block, page, regval); regval = 0; } + else + { + fdbg("ERROR: block=%d page=%d Corrupted sectors: %08x\n", + block, page, regval); + } } /* Bit correction will be done directly in destination buffer. */ @@ -2076,7 +2118,8 @@ static int nand_readpage_pmecc(struct sam_nandcs_s *priv, off_t block, ret = pmecc_correction(regval, (uintptr_t)data); if (ret < 0) { - fdbg("ERROR: block=%d page=%d Unrecoverable data\n", block, page); + fdbg("ERROR: block=%d page=%d Unrecoverable data error: %d\n", + block, page, ret); } /* Disable auto mode */ @@ -2159,7 +2202,7 @@ static int nand_writepage_noecc(struct sam_nandcs_s *priv, off_t block, /* Configure the SMC */ regval |= (HSMC_CFG_RBEDGE | HSMC_CFG_DTOCYC(15) | HSMC_CFG_DTOMUL_1048576 | - HSMC_CFG_NFCSPARESIZE((sparesize-1) >> 2)); + HSMC_CFG_NFCSPARESIZE((sparesize - 1) >> 2)); if (spare) { @@ -2178,19 +2221,19 @@ static int nand_writepage_noecc(struct sam_nandcs_s *priv, off_t block, if (data) { - ret = nand_write(priv, true, (uint8_t *)data, pagesize, 0); + ret = nand_nfcsram_write(priv, (uint8_t *)data, pagesize, 0); if (ret < 0) { - fdbg("ERROR: nand_write for data region failed: %d\n", ret); + fdbg("ERROR: nand_nfcsram_write for data region failed: %d\n", ret); return ret; } if (spare) { - ret = nand_write(priv, true, (uint8_t *)spare, sparesize, pagesize); + ret = nand_nfcsram_write(priv, (uint8_t *)spare, sparesize, pagesize); if (ret < 0) { - fdbg("ERROR: nand_write for data region failed: %d\n", ret); + fdbg("ERROR: nand_nfcsram_write for data region failed: %d\n", ret); return ret; } } @@ -2230,7 +2273,7 @@ static int nand_writepage_noecc(struct sam_nandcs_s *priv, off_t block, HSMC_CLE_WRITE_EN | HSMC_ALE_COL_EN | HSMC_ALE_ROW_EN, COMMAND_WRITE_1,0, pagesize, rowaddr); - ret = nand_write(priv, false, (uint8_t *)spare, sparesize, 0); + ret = nand_write(priv, (uint8_t *)spare, sparesize, 0); if (ret < 0) { fdbg("ERROR: nand_write for spare region failed: %d\n", ret); @@ -2268,16 +2311,17 @@ static int nand_writepage_noecc(struct sam_nandcs_s *priv, off_t block, static int nand_writepage_pmecc(struct sam_nandcs_s *priv, off_t block, unsigned int page, const void *data) { - uint32_t pagesize; - uint32_t rowaddr; - uint32_t startaddr; - uint32_t eccpersector; - uint32_t sectornumber; - uint32_t sectorindex; uint32_t regval; - uint8_t *pmecc[8]; - uint8_t sectersperpage; - int i; + volatile uint8_t *pmecc; + uint8_t *ecc; + unsigned int pagesize; + unsigned int rowaddr; + unsigned int eccsaddr; + unsigned int eccpersector; + unsigned int sectersperpage; + unsigned int eccsize; + unsigned int sector; + unsigned int i; int ret = 0; fvdbg("block=%d page=%d data=%p\n", (int)block, page, data); @@ -2297,22 +2341,25 @@ static int nand_writepage_pmecc(struct sam_nandcs_s *priv, off_t block, /* Calculate the start page address */ - regval = nand_getreg(SAM_HSMC_PMECCSADDR); - pagesize = nandmodel_getpagesize(&priv->raw.model); - startaddr = regval + pagesize; + regval = nand_getreg(SAM_HSMC_PMECCSADDR); + pagesize = nandmodel_getpagesize(&priv->raw.model); + eccsaddr = pagesize + regval; /* Calculate physical address of the page */ - rowaddr = block * nandmodel_pagesperblock(&priv->raw.model) + page; + rowaddr = block * nandmodel_pagesperblock(&priv->raw.model) + page; + + fvdbg("pagesize=%d eccsaddr=%d rowaddr=%d\n", pagesize, eccsaddr, rowaddr); /* Write data area if needed */ if (data) { - ret = nand_write(priv, true, (uint8_t *)data, pagesize, 0); + ret = nand_nfcsram_write(priv, (uint8_t *)data, pagesize, 0); if (ret < 0) { - fdbg("ERROR: nand_write for data region failed: %d\n", ret); + fdbg("ERROR: Block %d page %d nand_nfcsram_write for data region failed: %d\n", + block, page, ret); goto errout; } } @@ -2347,13 +2394,6 @@ static int nand_writepage_pmecc(struct sam_nandcs_s *priv, off_t block, nand_putreg(SAM_HSMC_PMECCTRL, HSMC_PMECCTRL_RST); nand_putreg(SAM_HSMC_PMECCTRL, HSMC_PMECCTRL_ENABLE); - /* Read the PMECC redundancy base address for each of (up to) 8 sectors */ - - for (i = 0; i < 8; i++) - { - pmecc[i] = (uint8_t*)(SAM_HSMC_PMECC_BASE(i)); - } - /* Start a data phase */ nand_putreg(SAM_HSMC_PMECCTRL, HSMC_PMECCTRL_DATA); @@ -2373,7 +2413,7 @@ static int nand_writepage_pmecc(struct sam_nandcs_s *priv, off_t block, nand_nfc_cleale(priv, HSMC_CLE_WRITE_EN | HSMC_ALE_COL_EN, - COMMAND_RANDOM_IN, 0, startaddr, 0); + COMMAND_RANDOM_IN, 0, eccsaddr, 0); /* Wait until the kernel of the PMECC is not busy */ @@ -2382,7 +2422,10 @@ static int nand_writepage_pmecc(struct sam_nandcs_s *priv, off_t block, /* Write the ECC */ eccpersector = (pmecc_get_eccsize()) / sectersperpage; - sectornumber = 1 << pmecc_get_pagesize(); + eccsize = sectersperpage * eccpersector; + + fvdbg("sectersperpage=%d eccpersector=%d eccsize=%d\n", + sectersperpage, eccpersector, eccsize); #ifdef CONFIG_SAMA5_PMECC_TRIMPAGE if (nand_trrimffs(priv) && page >= nand_get_trimpage(priv)) @@ -2391,33 +2434,32 @@ static int nand_writepage_pmecc(struct sam_nandcs_s *priv, off_t block, * cleanly erased NAND partitions */ - for (sectorindex = 0; sectorindex < sectornumber; sectorindex++) - { - for (i = 0; i < eccpersector; i++) - { - g_nand.ecctab[sectorindex * eccpersector + i] = 0xff; - } - } + memset(g_nand.ecctab, 0xff, eccsize); } else #endif { - /* Read all ECC registers */ + /* Read ECC registers for each sector in the page */ - for (sectorindex = 0; sectorindex < sectornumber; sectorindex++) + ecc = g_nand.ecctab; + for (sector = 0; sector < sectersperpage; sector++) { - for(i = 0; i < eccpersector; i++) + pmecc = (volatile uint8_t *)SAM_HSMC_PMECC_BASE(sector); + + /* Read all EEC registers for this page */ + + for (i = 0; i < eccpersector; i++) { - g_nand.ecctab[sectorindex * eccpersector + i] = pmecc[sectorindex][i]; + *ecc++ = *pmecc++; } } } - ret = nand_write(priv, false, (uint8_t *)(uint8_t *)g_nand.ecctab, - sectornumber * eccpersector, 0); + ret = nand_write(priv, (uint8_t *)g_nand.ecctab, eccsize, 0); if (ret < 0) { - fdbg("ERROR: nand_write for spare region failed: %d\n", ret); + fdbg("ERROR: Block %d page %d nand_write for spare region failed: %d\n", + block, page, ret); goto errout; } @@ -2429,7 +2471,8 @@ static int nand_writepage_pmecc(struct sam_nandcs_s *priv, off_t block, ret = nand_operation_complete(priv); if (ret < 0) { - fdbg("ERROR: Failed writing data area: %d\n", ret); + fdbg("ERROR: Block %d page %d Failed writing data area: %d\n", + block, page, ret); } /* Disable the PMECC */ @@ -2477,7 +2520,7 @@ static inline int nand_tryeraseblock(struct sam_nandcs_s *priv, off_t block) ret = nand_operation_complete(priv); if (ret < 0) { - fdbg("ERROR: Could not erase block %d: %d\n", block, ret); + fdbg("ERROR: Block %d Could not erase: %d\n", block, ret); } return ret; @@ -2513,7 +2556,7 @@ static int nand_eraseblock(struct nand_raw_s *raw, off_t block) retries--; } - fdbg("ERROR: Failed to erase %d after %d tries\n", + fdbg("ERROR: Block %d Failed to erase after %d tries\n", (int)block, NAND_ERASE_NRETRIES); nand_unlock(); @@ -2638,16 +2681,19 @@ static int nand_readpage(struct nand_raw_s *raw, off_t block, case NANDECC_NONE: case NANDECC_CHIPECC: ret = nand_readpage_noecc(priv, block, page, data, spare); + break; #ifdef CONFIG_SAMA5_HAVE_PMECC case NANDECC_PMECC: DEBUGASSERT(!spare); ret = nand_readpage_pmecc(priv, block, page, data); + break; #endif case NANDECC_SWECC: default: ret = -EINVAL; + break; } nand_unlock(); @@ -2698,16 +2744,19 @@ static int nand_writepage(struct nand_raw_s *raw, off_t block, case NANDECC_NONE: case NANDECC_CHIPECC: ret = nand_writepage_noecc(priv, block, page, data, spare); + break; #ifdef CONFIG_SAMA5_HAVE_PMECC case NANDECC_PMECC: DEBUGASSERT(!spare); ret = nand_writepage_pmecc(priv, block, page, data); + break; #endif case NANDECC_SWECC: default: ret = -EINVAL; + break; } nand_unlock(); @@ -2771,7 +2820,7 @@ struct mtd_dev_s *sam_nand_initialize(int cs) fvdbg("CS%d\n", cs); - /* Select the device structure */ + /* Select the device structure (In SAMA5D3, NAND is only supported on CS3). */ #ifdef CONFIG_SAMA5_EBICS0_NAND if (cs == HSMC_CS0)