summaryrefslogtreecommitdiff
path: root/drivers/mtd/nand/gpmi/gpmi-ecc8.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mtd/nand/gpmi/gpmi-ecc8.c')
-rw-r--r--drivers/mtd/nand/gpmi/gpmi-ecc8.c143
1 files changed, 133 insertions, 10 deletions
diff --git a/drivers/mtd/nand/gpmi/gpmi-ecc8.c b/drivers/mtd/nand/gpmi/gpmi-ecc8.c
index ead809cf0d54..d1b30c088328 100644
--- a/drivers/mtd/nand/gpmi/gpmi-ecc8.c
+++ b/drivers/mtd/nand/gpmi/gpmi-ecc8.c
@@ -52,8 +52,40 @@ static int ecc8_reset(void *context, int index);
struct ecc8_nand {
};
+/**
+ * ecc8_state_t - Describes the state of the Reed-Solomon ECC.
+ *
+ * @chip: A descriptor the GPMI driver uses to track this ECC.
+ * @done: A struct completion used to manage ECC interrupts.
+ * @stat: Used by the interrupt level to communicate ECC statistics to the
+ * base level.
+ * @writesize: The page data size.
+ * @oobsize: The page OOB size.
+ * @ecc_page: The number of ECC bytes associated with each 512-byte data
+ * block.
+ * @ecc_oob: The number of ECC bytes that cover the free bytes in the OOB.
+ * @oob_free: The number of free bytes in the OOB. That is, the number of
+ * OOB bytes that are *not* ECC bytes.
+ * @r: A bit mask that sets the ECC_CMD field of the GPMI_ECCCTRL
+ * register for reading.
+ * @w: A bit mask that sets the ECC_CMD field of the GPMI_ECCCTRL
+ * register for writing.
+ * @bits: The number of 512-byte data blocks in a page. By coincidence,
+ * this is also the strength of the ECC algorithm since the
+ * hardware is constrained to use ECC-4 with 2K pages and ECC-8
+ * with 4K pages. The ECC strength may be the original source of
+ * this field's name, but the code actually uses this value in the
+ * sense of the number of 512-byte blocks.
+ * @s0_mask: This is presumably the mask that should be applied to the
+ * ECC8_STATUS0 register before checking for ECC errors. In fact,
+ * it is assigned but never used.
+ * @s1_mask: This is presumably the mask that should be applied to the
+ * ECC8_STATUS1 register before checking for ECC errors. In fact,
+ * it is assigned but never used.
+ */
+
struct ecc8_state_t {
- struct gpmi_hwecc_chip chip;
+ struct gpmi_ecc_descriptor chip;
struct completion done;
struct mtd_ecc_stats stat;
u32 writesize, oobsize;
@@ -63,6 +95,8 @@ struct ecc8_state_t {
u32 s0_mask, s1_mask;
};
+/* The singleton struct ecc8_state_t for the ECC8. */
+
static struct ecc8_state_t state = {
.chip = {
.name = "ecc8",
@@ -74,6 +108,12 @@ static struct ecc8_state_t state = {
},
};
+/**
+ * ecc8_reset - Resets the ECC8.
+ *
+ * @context: Context data -- a pointer to a struct ecc8_state_t.
+ * @index: ??
+ */
static int ecc8_reset(void *context, int index)
{
stmp3xxx_reset_block(REGS_ECC8_BASE, false);
@@ -83,6 +123,14 @@ static int ecc8_reset(void *context, int index)
return 0;
}
+/**
+ * ecc8_stat - Gather statistics and clean up after a read operation.
+ *
+ * @context: Context data -- a pointer to a struct ecc8_state_t.
+ * @index: ??
+ * @r: A statistics structure that will receive the results of the most
+ * recent operation.
+ */
static int ecc8_stat(void *context, int index, struct mtd_ecc_stats *r)
{
struct ecc8_state_t *state = context;
@@ -95,6 +143,15 @@ static int ecc8_stat(void *context, int index, struct mtd_ecc_stats *r)
return 0;
}
+/**
+ * ecc8_irq - Interrupt handler for the ECC8 hardware.
+ *
+ * This function gains control when the ECC8 hardware interrupts. It
+ * acknowledges the interrupt and gathers status information.
+ *
+ * @irq: The interrupt number.
+ * @context: Context data -- a pointer to a struct ecc8_state_t.
+ */
static irqreturn_t ecc8_irq(int irq, void *context)
{
int r;
@@ -104,20 +161,28 @@ static irqreturn_t ecc8_irq(int irq, void *context)
u32 s0 = __raw_readl(REGS_ECC8_BASE + HW_ECC8_STATUS0),
s1 = __raw_readl(REGS_ECC8_BASE + HW_ECC8_STATUS1);
+ /* Get the physical chip number to which this operation applied. */
+
r = (s0 & BM_ECC8_STATUS0_COMPLETED_CE) >> 16;
+ /* Check if there were any errors. */
+
if (s0 & BM_ECC8_STATUS0_CORRECTED ||
s0 & BM_ECC8_STATUS0_UNCORRECTABLE) {
ecc_stats.failed = 0;
ecc_stats.corrected = 0;
+ /* Check for errors in the OOB bytes. */
+
s0 = (s0 & BM_ECC8_STATUS0_STATUS_AUX) >> 8;
if (s0 <= 4)
ecc_stats.corrected += s0;
if (s0 == 0xE)
ecc_stats.failed++;
+ /* Check for errors in the data bytes. */
+
for ( ; s1 != 0; s1 >>= 4) {
corr = s1 & 0xF;
if (corr == 0x0C)
@@ -128,21 +193,45 @@ static irqreturn_t ecc8_irq(int irq, void *context)
ecc_stats.corrected += corr;
s1 >>= 4;
}
+
+ /* Accumulate statistics. */
+
state->stat.corrected += ecc_stats.corrected;
state->stat.failed += ecc_stats.failed;
+
}
+ /* Acknowledge the interrupt. */
+
complete(&state->done);
stmp3xxx_clearl(BM_ECC8_CTRL_COMPLETE_IRQ, REGS_ECC8_BASE + HW_ECC8_CTRL);
return IRQ_HANDLED;
}
+/**
+ * ecc8_available - Returns whether the Reed-Solomon ECC hardware is available.
+ *
+ * Note that this function always returns true.
+ *
+ * @context: Context data -- a pointer to a struct ecc8_state_t.
+ */
static int ecc8_available(void *context)
{
return 1;
}
+/**
+ * ecc8_setup - Set up ECC for use.
+ *
+ * If the GPMI driver decides to use this ECC, it will call this function once,
+ * before it starts any operations.
+ *
+ * @context: Context data -- a pointer to a struct ecc8_state_t.
+ * @index: ??
+ * @writesize: The page data size.
+ * @oobsize: The page OOB size.
+ */
static int ecc8_setup(void *context, int index, int writesize, int oobsize)
{
struct ecc8_state_t *state = context;
@@ -181,13 +270,14 @@ static int ecc8_setup(void *context, int index, int writesize, int oobsize)
}
/**
- * ecc8_read - create dma chain to read nand page
+ * ecc8_read - Fill in a DMA chain to read a page.
*
- * @context: context data, pointer to ecc8_state
- * @index: CS of nand to read
- * @chain: dma chain to be filled
- * @page: (physical) address of page data to be read to
- * @oob: (physical) address of OOB data to be read to
+ * @context: Context data -- a pointer to a struct ecc8_state_t.
+ * @cs: The chip number to read.
+ * @chain: The main descriptor of the DMA chain to fill.
+ * @error: ??
+ * @page: Physical address of the target page data buffer.
+ * @oob: Physical address of the target OOB data buffer.
*
* Return: status of operation -- 0 on success
*/
@@ -217,7 +307,6 @@ static int ecc8_read(void *context, int cs,
page = oob;
}
-
/* wait for ready */
chain->command->cmd =
BF(1, APBH_CHn_CMD_CMDWORDS) |
@@ -288,6 +377,18 @@ static int ecc8_read(void *context, int cs,
return 0;
}
+/**
+ * ecc8_write - Fill in a DMA chain to write a page.
+ *
+ * @context: Context data -- a pointer to a struct ecc8_state_t.
+ * @cs: The chip number to read.
+ * @chain: The main descriptor of the DMA chain to fill.
+ * @error: ??
+ * @page: Physical address of the source page data buffer.
+ * @oob: Physical address of the source OOB data buffer.
+ *
+ * Return: status of operation -- 0 on success
+ */
static int ecc8_write(void *context, int cs,
struct stmp3xxx_dma_descriptor *chain,
dma_addr_t error,
@@ -364,14 +465,27 @@ static int ecc8_write(void *context, int cs,
return 0;
}
+/**
+ * ecc8_init - Initialize and register ECC.
+ *
+ * The GPMI driver calls this function once, at the beginning of time, whether
+ * or not it decides to use this ECC.
+ */
int __init ecc8_init(void)
{
int err;
+ /* Check if the ECC8 hardware is available. */
+
if (!ecc8_available(&state))
return -ENXIO;
- gpmi_hwecc_chip_add(&state.chip);
+ /* Give the GPMI driver a descriptor. */
+
+ gpmi_ecc_add(&state.chip);
+
+ /* Attempt to acquire the ECC interrupt. */
+
err = request_irq(IRQ_ECC8_IRQ, ecc8_irq, 0, state.chip.name, &state);
if (err)
return err;
@@ -380,8 +494,17 @@ int __init ecc8_init(void)
return 0;
}
+/**
+ * ecc8_exit - Shut down and de-register ECC.
+ */
void ecc8_exit(void)
{
+
+ /* Relinquish the ECC interrupt. */
+
free_irq(IRQ_ECC8_IRQ, &state);
- gpmi_hwecc_chip_remove(&state.chip);
+
+ /* Remove the descriptor for this ECC from the GPMI's list. */
+
+ gpmi_ecc_remove(&state.chip);
}