mirror of
https://github.com/apache/nuttx.git
synced 2026-06-01 16:59:28 +08:00
drivers/mtd/w25n: address review comments
- Replace syslog() with finfo()/fwarn() to follow the NuttX subsystem debug convention. - Merge adjacent early-continue `if` branches in w25n_pick_free_spare and w25n_scan_factory_bad. - Add Input Parameters / Returned Value sections to the BBM helper headers. - Clarify why only -EIO triggers a remap retry in w25n_erase and w25n_bwrite (block-level E-FAIL/P-FAIL vs. transient bus faults). Signed-off-by: Julian Oes <julian@oes.ch>
This commit is contained in:
+76
-35
@@ -655,11 +655,15 @@ static void w25n_unprotect(FAR struct w25n_dev_s *priv)
|
|||||||
* Name: w25n_read_bbm_lut
|
* Name: w25n_read_bbm_lut
|
||||||
*
|
*
|
||||||
* Description:
|
* Description:
|
||||||
* Read all 20 entries of the hardware Bad Block Management Look-Up Table.
|
* Read all 20 entries of the hardware Bad Block Management Look-Up Table
|
||||||
* Each entry is encoded as (lba_raw << 16) | pba_raw, where the high two
|
* into priv->bbm. Each entry is encoded as (lba_raw << 16) | pba_raw,
|
||||||
* LBA bits are status flags (Enable, Invalid).
|
* where the high two LBA bits are status flags (Enable, Invalid).
|
||||||
* See datasheet section 8.2.8.
|
* See datasheet section 8.2.8.
|
||||||
*
|
*
|
||||||
|
* Input Parameters:
|
||||||
|
* priv - W25N device instance; priv->bbm is overwritten with the chip's
|
||||||
|
* current LUT contents.
|
||||||
|
*
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
static void w25n_read_bbm_lut(FAR struct w25n_dev_s *priv)
|
static void w25n_read_bbm_lut(FAR struct w25n_dev_s *priv)
|
||||||
@@ -696,6 +700,17 @@ static void w25n_read_bbm_lut(FAR struct w25n_dev_s *priv)
|
|||||||
* Add an LBA->PBA link to the chip's non-volatile BBM LUT. Subsequent
|
* Add an LBA->PBA link to the chip's non-volatile BBM LUT. Subsequent
|
||||||
* accesses to the LBA are transparently routed to the PBA by the chip.
|
* accesses to the LBA are transparently routed to the PBA by the chip.
|
||||||
*
|
*
|
||||||
|
* Input Parameters:
|
||||||
|
* priv - W25N device instance.
|
||||||
|
* lba - Logical block that should be remapped. Must be within the user
|
||||||
|
* area (< W25N_USER_BLOCKS).
|
||||||
|
* pba - Physical block in the spare pool to route the LBA to. Must be
|
||||||
|
* in [W25N_USER_BLOCKS, W25N01GV_BLOCKS).
|
||||||
|
*
|
||||||
|
* Returned Value:
|
||||||
|
* OK on success, -EINVAL if the LBA/PBA fall outside the allowed ranges,
|
||||||
|
* or a negative errno propagated from w25n_waitready.
|
||||||
|
*
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
static int w25n_bbm_swap(FAR struct w25n_dev_s *priv,
|
static int w25n_bbm_swap(FAR struct w25n_dev_s *priv,
|
||||||
@@ -744,8 +759,15 @@ static int w25n_bbm_swap(FAR struct w25n_dev_s *priv,
|
|||||||
*
|
*
|
||||||
* Description:
|
* Description:
|
||||||
* Read byte 0 of the spare area of the first page of `block`. The factory
|
* Read byte 0 of the spare area of the first page of `block`. The factory
|
||||||
* marks bad blocks with a non-FFh value here. Returns the marker value
|
* marks bad blocks with a non-FFh value here.
|
||||||
* (0..255), or 0x00 if the read failed (treated as bad).
|
*
|
||||||
|
* Input Parameters:
|
||||||
|
* priv - W25N device instance.
|
||||||
|
* block - Block index to inspect (0..W25N01GV_BLOCKS - 1).
|
||||||
|
*
|
||||||
|
* Returned Value:
|
||||||
|
* The marker byte (0..255), or 0x00 if the page read failed or hit an
|
||||||
|
* uncorrectable ECC error (both cases are treated as bad).
|
||||||
*
|
*
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
@@ -787,8 +809,16 @@ static bool w25n_is_factory_bad(FAR struct w25n_dev_s *priv, uint16_t block)
|
|||||||
*
|
*
|
||||||
* Description:
|
* Description:
|
||||||
* Find an unused, non-factory-bad block in the spare pool that is not
|
* Find an unused, non-factory-bad block in the spare pool that is not
|
||||||
* already a remap target in the LUT. Returns the block index, or
|
* already a remap target in the cached LUT (priv->bbm). Callers must
|
||||||
* 0xffff if none is available.
|
* refresh priv->bbm via w25n_read_bbm_lut beforehand. As a proof of
|
||||||
|
* life, the candidate is erased before being returned.
|
||||||
|
*
|
||||||
|
* Input Parameters:
|
||||||
|
* priv - W25N device instance with a freshly read priv->bbm.
|
||||||
|
*
|
||||||
|
* Returned Value:
|
||||||
|
* A usable spare block index in [W25N_USER_BLOCKS, W25N01GV_BLOCKS), or
|
||||||
|
* 0xffff if no candidate remains.
|
||||||
*
|
*
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
@@ -815,12 +845,7 @@ static uint16_t w25n_pick_free_spare(FAR struct w25n_dev_s *priv)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (taken)
|
if (taken || w25n_is_factory_bad(priv, b))
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (w25n_is_factory_bad(priv, b))
|
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -834,8 +859,7 @@ static uint16_t w25n_pick_free_spare(FAR struct w25n_dev_s *priv)
|
|||||||
|
|
||||||
if (w25n_block_erase(priv, b) != OK)
|
if (w25n_block_erase(priv, b) != OK)
|
||||||
{
|
{
|
||||||
syslog(LOG_WARNING,
|
fwarn("spare %u failed erase test, skipping\n", b);
|
||||||
"[w25n] spare %u failed erase test, skipping\n", b);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -849,9 +873,17 @@ static uint16_t w25n_pick_free_spare(FAR struct w25n_dev_s *priv)
|
|||||||
* Name: w25n_remap_bad_block
|
* Name: w25n_remap_bad_block
|
||||||
*
|
*
|
||||||
* Description:
|
* Description:
|
||||||
* Allocate a spare and add a BBM LUT entry mapping `block` to it.
|
* Allocate a spare and add a BBM LUT entry mapping `block` to it. After
|
||||||
* Returns OK if the chip will now route accesses to `block` to a good
|
* this returns OK the chip transparently routes accesses to `block` to
|
||||||
* spare, or a negative errno if no spare is available or the LUT is full.
|
* the allocated spare PBA.
|
||||||
|
*
|
||||||
|
* Input Parameters:
|
||||||
|
* priv - W25N device instance.
|
||||||
|
* block - Bad user-area block that should be remapped.
|
||||||
|
*
|
||||||
|
* Returned Value:
|
||||||
|
* OK on success, -ENOSPC if the BBM LUT is already full or no free
|
||||||
|
* spare could be found, or a negative errno from w25n_bbm_swap.
|
||||||
*
|
*
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
@@ -874,7 +906,7 @@ static int w25n_remap_bad_block(FAR struct w25n_dev_s *priv, uint16_t block)
|
|||||||
return -ENOSPC;
|
return -ENOSPC;
|
||||||
}
|
}
|
||||||
|
|
||||||
syslog(LOG_INFO, "[w25n] remap block %u -> spare %u\n", block, spare);
|
finfo("remap block %u -> spare %u\n", block, spare);
|
||||||
return w25n_bbm_swap(priv, block, spare);
|
return w25n_bbm_swap(priv, block, spare);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -887,6 +919,9 @@ static int w25n_remap_bad_block(FAR struct w25n_dev_s *priv, uint16_t block)
|
|||||||
* are already remapped (present in the LUT) are skipped, so repeated
|
* are already remapped (present in the LUT) are skipped, so repeated
|
||||||
* scans don't consume LUT slots. Takes ~200 ms (1024 OOB reads).
|
* scans don't consume LUT slots. Takes ~200 ms (1024 OOB reads).
|
||||||
*
|
*
|
||||||
|
* Input Parameters:
|
||||||
|
* priv - W25N device instance; priv->bbm is refreshed as a side effect.
|
||||||
|
*
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
static void w25n_scan_factory_bad(FAR struct w25n_dev_s *priv)
|
static void w25n_scan_factory_bad(FAR struct w25n_dev_s *priv)
|
||||||
@@ -932,14 +967,11 @@ static void w25n_scan_factory_bad(FAR struct w25n_dev_s *priv)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (already_mapped)
|
/* Already-mapped: remapped on a previous boot, the chip handles
|
||||||
{
|
* routing. Not factory bad: nothing to do either.
|
||||||
/* Already remapped on a previous boot, the chip handles routing. */
|
*/
|
||||||
|
|
||||||
continue;
|
if (already_mapped || !w25n_is_factory_bad(priv, block))
|
||||||
}
|
|
||||||
|
|
||||||
if (!w25n_is_factory_bad(priv, block))
|
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -962,8 +994,7 @@ static void w25n_scan_factory_bad(FAR struct w25n_dev_s *priv)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
syslog(LOG_INFO,
|
finfo("BBM: %d new bad blocks found (%d remapped, %d unremapped); "
|
||||||
"[w25n] BBM: %d new bad blocks found (%d remapped, %d unremapped); "
|
|
||||||
"%d previously remapped; LUT %d/%d slots used\n",
|
"%d previously remapped; LUT %d/%d slots used\n",
|
||||||
factory_bad, remapped, unremapped, already_in_lut,
|
factory_bad, remapped, unremapped, already_in_lut,
|
||||||
lut_used + remapped, W25N_BBM_LUT_ENTRIES);
|
lut_used + remapped, W25N_BBM_LUT_ENTRIES);
|
||||||
@@ -999,6 +1030,15 @@ static int w25n_erase(FAR struct mtd_dev_s *dev, off_t startblock,
|
|||||||
for (attempt = 0; attempt < 2; attempt++)
|
for (attempt = 0; attempt < 2; attempt++)
|
||||||
{
|
{
|
||||||
ret = w25n_block_erase(priv, block);
|
ret = w25n_block_erase(priv, block);
|
||||||
|
|
||||||
|
/* Only retry on -EIO, which w25n_block_erase returns on the
|
||||||
|
* chip's E-FAIL status (the block itself failed to erase).
|
||||||
|
* Other errors like -ETIMEDOUT from w25n_waitready_erase are
|
||||||
|
* SPI/comms faults, not block-level failures - remapping a
|
||||||
|
* block because of a transient bus issue would waste a
|
||||||
|
* non-recoverable BBM LUT slot.
|
||||||
|
*/
|
||||||
|
|
||||||
if (ret == OK || ret != -EIO)
|
if (ret == OK || ret != -EIO)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
@@ -1009,8 +1049,7 @@ static int w25n_erase(FAR struct mtd_dev_s *dev, off_t startblock,
|
|||||||
* spare PBA transparently.
|
* spare PBA transparently.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
syslog(LOG_WARNING,
|
fwarn("erase failed on block %lu, attempting remap\n",
|
||||||
"[w25n] erase failed on block %lu, attempting remap\n",
|
|
||||||
(unsigned long)block);
|
(unsigned long)block);
|
||||||
if (w25n_remap_bad_block(priv, (uint16_t)block) != OK)
|
if (w25n_remap_bad_block(priv, (uint16_t)block) != OK)
|
||||||
{
|
{
|
||||||
@@ -1098,6 +1137,11 @@ static ssize_t w25n_bwrite(FAR struct mtd_dev_s *dev, off_t startblock,
|
|||||||
w25n_load_buffer(priv, 0, buf + (i * W25N_PAGE_SIZE),
|
w25n_load_buffer(priv, 0, buf + (i * W25N_PAGE_SIZE),
|
||||||
W25N_PAGE_SIZE);
|
W25N_PAGE_SIZE);
|
||||||
ret = w25n_program_execute(priv, page);
|
ret = w25n_program_execute(priv, page);
|
||||||
|
|
||||||
|
/* As in w25n_erase, only retry on -EIO (chip P-FAIL); other
|
||||||
|
* errors indicate bus/comms faults rather than a bad block.
|
||||||
|
*/
|
||||||
|
|
||||||
if (ret == OK || ret != -EIO)
|
if (ret == OK || ret != -EIO)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
@@ -1108,9 +1152,7 @@ static ssize_t w25n_bwrite(FAR struct mtd_dev_s *dev, off_t startblock,
|
|||||||
* data buffer is reloaded above on the retry pass.
|
* data buffer is reloaded above on the retry pass.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
syslog(LOG_WARNING,
|
fwarn("program failed on page %lu, remapping block %lu\n",
|
||||||
"[w25n] program failed on page %lu, "
|
|
||||||
"remapping block %lu\n",
|
|
||||||
(unsigned long)page, (unsigned long)(page >> 6));
|
(unsigned long)page, (unsigned long)(page >> 6));
|
||||||
if (w25n_remap_bad_block(priv, (uint16_t)(page >> 6)) != OK)
|
if (w25n_remap_bad_block(priv, (uint16_t)(page >> 6)) != OK)
|
||||||
{
|
{
|
||||||
@@ -1359,8 +1401,7 @@ FAR struct mtd_dev_s *w25n_initialize(FAR struct spi_dev_s *spi,
|
|||||||
(unsigned long)CONFIG_W25N_SPIFREQUENCY,
|
(unsigned long)CONFIG_W25N_SPIFREQUENCY,
|
||||||
(unsigned long)actual_freq);
|
(unsigned long)actual_freq);
|
||||||
|
|
||||||
syslog(LOG_INFO,
|
finfo("W25N01GV ready: %u user blocks (%u spare), "
|
||||||
"[w25n] W25N01GV ready: %u user blocks (%u spare), "
|
|
||||||
"%u bytes/block, %u total user MB, SPI %lu Hz\n",
|
"%u bytes/block, %u total user MB, SPI %lu Hz\n",
|
||||||
(unsigned)priv->nblocks, (unsigned)W25N_SPARE_RESERVE,
|
(unsigned)priv->nblocks, (unsigned)W25N_SPARE_RESERVE,
|
||||||
(unsigned)W25N_BLOCK_SIZE,
|
(unsigned)W25N_BLOCK_SIZE,
|
||||||
|
|||||||
Reference in New Issue
Block a user