diff options
Diffstat (limited to 'drivers/mtd/nand/gpmi/gpmi-ecc8.c')
-rw-r--r-- | drivers/mtd/nand/gpmi/gpmi-ecc8.c | 143 |
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); } |