/** * omap_prefetch_enable - configures and starts prefetch transfer * @cs: cs (chip select) number * @fifo_th: fifo threshold to be used for read/ write * @dma_mode: dma mode enable (1) or disable (0) * @u32_count: number of bytes to be transferred * @is_write: prefetch read(0) or write post(1) mode * @info: NAND device structure containing platform data
*/ staticint omap_prefetch_enable(int cs, int fifo_th, int dma_mode, unsignedint u32_count, int is_write, struct omap_nand_info *info)
{
u32 val;
if (fifo_th > PREFETCH_FIFOTHRESHOLD_MAX) return -1;
if (readl(info->reg.gpmc_prefetch_control)) return -EBUSY;
/* Set the amount of bytes to be prefetched */
writel(u32_count, info->reg.gpmc_prefetch_config2);
/* Set dma/mpu mode, the prefetch read / post write and * enable the engine. Set which cs is has requested for.
*/
val = ((cs << PREFETCH_CONFIG1_CS_SHIFT) |
PREFETCH_FIFOTHRESHOLD(fifo_th) | ENABLE_PREFETCH |
(dma_mode << DMA_MPU_MODE_SHIFT) | (is_write & 0x1));
writel(val, info->reg.gpmc_prefetch_config1);
/* Start the prefetch engine */
writel(0x1, info->reg.gpmc_prefetch_control);
return 0;
}
/* * omap_prefetch_reset - disables and stops the prefetch engine
*/ staticint omap_prefetch_reset(int cs, struct omap_nand_info *info)
{
u32 config1;
/* check if the same module/cs is trying to reset */
config1 = readl(info->reg.gpmc_prefetch_config1); if (((config1 >> PREFETCH_CONFIG1_CS_SHIFT) & CS_MASK) != cs) return -EINVAL;
/* Stop the PFPW engine */
writel(0x0, info->reg.gpmc_prefetch_control);
/* Reset/disable the PFPW engine */
writel(0x0, info->reg.gpmc_prefetch_config1);
return 0;
}
/** * omap_nand_data_in_pref - NAND data in using prefetch engine * @chip: NAND chip * @buf: output buffer where NAND data is placed into * @len: length of transfer * @force_8bit: force 8-bit transfers
*/ staticvoid omap_nand_data_in_pref(struct nand_chip *chip, void *buf, unsignedint len, bool force_8bit)
{ struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip));
uint32_t r_count = 0; int ret = 0;
u32 *p = (u32 *)buf; unsignedint pref_len;
if (force_8bit) {
omap_nand_data_in(chip, buf, len, force_8bit); return;
}
/* read 32-bit words using prefetch and remaining bytes normally */
/* configure and start prefetch transfer */
pref_len = len - (len & 3);
ret = omap_prefetch_enable(info->gpmc_cs,
PREFETCH_FIFOTHRESHOLD_MAX, 0x0, pref_len, 0x0, info); if (ret) { /* prefetch engine is busy, use CPU copy method */
omap_nand_data_in(chip, buf, len, false);
} else { do {
r_count = readl(info->reg.gpmc_prefetch_status);
r_count = PREFETCH_STATUS_FIFO_CNT(r_count);
r_count = r_count >> 2;
ioread32_rep(info->fifo, p, r_count);
p += r_count;
pref_len -= r_count << 2;
} while (pref_len); /* disable and stop the Prefetch engine */
omap_prefetch_reset(info->gpmc_cs, info); /* fetch any remaining bytes */ if (len & 3)
omap_nand_data_in(chip, p, len & 3, false);
}
}
/** * omap_nand_data_out_pref - NAND data out using Write Posting engine * @chip: NAND chip * @buf: input buffer that is sent to NAND * @len: length of transfer * @force_8bit: force 8-bit transfers
*/ staticvoid omap_nand_data_out_pref(struct nand_chip *chip, constvoid *buf, unsignedint len, bool force_8bit)
{ struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip));
uint32_t w_count = 0; int i = 0, ret = 0;
u16 *p = (u16 *)buf; unsignedlong tim, limit;
u32 val;
if (force_8bit) {
omap_nand_data_out(chip, buf, len, force_8bit); return;
}
/* take care of subpage writes */ if (len % 2 != 0) {
writeb(*(u8 *)buf, info->fifo);
p = (u16 *)(buf + 1);
len--;
}
/* configure and start prefetch transfer */
ret = omap_prefetch_enable(info->gpmc_cs,
PREFETCH_FIFOTHRESHOLD_MAX, 0x0, len, 0x1, info); if (ret) { /* write posting engine is busy, use CPU copy method */
omap_nand_data_out(chip, buf, len, false);
} else { while (len) {
w_count = readl(info->reg.gpmc_prefetch_status);
w_count = PREFETCH_STATUS_FIFO_CNT(w_count);
w_count = w_count >> 1; for (i = 0; (i < w_count) && len; i++, len -= 2)
iowrite16(*p++, info->fifo);
} /* wait for data to flushed-out before reset the prefetch */
tim = 0;
limit = (loops_per_jiffy *
msecs_to_jiffies(OMAP_NAND_TIMEOUT_MS)); do {
cpu_relax();
val = readl(info->reg.gpmc_prefetch_status);
val = PREFETCH_STATUS_COUNT(val);
} while (val && (tim++ < limit));
/* disable and stop the PFPW engine */
omap_prefetch_reset(info->gpmc_cs, info);
}
}
/* * omap_nand_dma_callback: callback on the completion of dma transfer * @data: pointer to completion data structure
*/ staticvoid omap_nand_dma_callback(void *data)
{
complete((struct completion *) data);
}
/* * omap_nand_dma_transfer: configure and start dma transfer * @chip: nand chip structure * @addr: virtual address in RAM of source/destination * @len: number of data bytes to be transferred * @is_write: flag for read/write operation
*/ staticinlineint omap_nand_dma_transfer(struct nand_chip *chip, constvoid *addr, unsignedint len, int is_write)
{ struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip)); struct dma_async_tx_descriptor *tx; enum dma_data_direction dir = is_write ? DMA_TO_DEVICE :
DMA_FROM_DEVICE; struct scatterlist sg; unsignedlong tim, limit; unsigned n; int ret;
u32 val;
if (!virt_addr_valid(addr)) goto out_copy;
sg_init_one(&sg, addr, len);
n = dma_map_sg(info->dma->device->dev, &sg, 1, dir); if (n == 0) {
dev_err(&info->pdev->dev, "Couldn't DMA map a %d byte buffer\n", len); goto out_copy;
}
tx = dmaengine_prep_slave_sg(info->dma, &sg, n,
is_write ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!tx) goto out_copy_unmap;
/* setup and start DMA using dma_addr */
dma_async_issue_pending(info->dma);
/* configure and start prefetch transfer */
ret = omap_prefetch_enable(info->gpmc_cs,
PREFETCH_FIFOTHRESHOLD_MAX, 0x1, len, is_write, info); if (ret) /* PFPW engine is busy, use cpu copy method */ goto out_copy_unmap;
wait_for_completion(&info->comp);
tim = 0;
limit = (loops_per_jiffy * msecs_to_jiffies(OMAP_NAND_TIMEOUT_MS));
do {
cpu_relax();
val = readl(info->reg.gpmc_prefetch_status);
val = PREFETCH_STATUS_COUNT(val);
} while (val && (tim++ < limit));
/* disable and stop the PFPW engine */
omap_prefetch_reset(info->gpmc_cs, info);
/** * omap_nand_data_in_dma_pref - NAND data in using DMA and Prefetch * @chip: NAND chip * @buf: output buffer where NAND data is placed into * @len: length of transfer * @force_8bit: force 8-bit transfers
*/ staticvoid omap_nand_data_in_dma_pref(struct nand_chip *chip, void *buf, unsignedint len, bool force_8bit)
{ struct mtd_info *mtd = nand_to_mtd(chip);
if (force_8bit) {
omap_nand_data_in(chip, buf, len, force_8bit); return;
}
if (len <= mtd->oobsize)
omap_nand_data_in_pref(chip, buf, len, false); else /* start transfer in DMA mode */
omap_nand_dma_transfer(chip, buf, len, 0x0);
}
/** * omap_nand_data_out_dma_pref - NAND data out using DMA and write posting * @chip: NAND chip * @buf: input buffer that is sent to NAND * @len: length of transfer * @force_8bit: force 8-bit transfers
*/ staticvoid omap_nand_data_out_dma_pref(struct nand_chip *chip, constvoid *buf, unsignedint len, bool force_8bit)
{ struct mtd_info *mtd = nand_to_mtd(chip);
if (force_8bit) {
omap_nand_data_out(chip, buf, len, force_8bit); return;
}
if (len <= mtd->oobsize)
omap_nand_data_out_pref(chip, buf, len, false); else /* start transfer in DMA mode */
omap_nand_dma_transfer(chip, buf, len, 0x1);
}
/* configure and start prefetch transfer */
ret = omap_prefetch_enable(info->gpmc_cs,
PREFETCH_FIFOTHRESHOLD_MAX/2, 0x0, len, 0x0, info); if (ret) { /* PFPW engine is busy, use cpu copy method */
omap_nand_data_in(chip, buf, len, false); return;
}
/* waiting for write to complete */
wait_for_completion(&info->comp);
/* wait for data to flushed-out before reset the prefetch */
tim = 0;
limit = (loops_per_jiffy * msecs_to_jiffies(OMAP_NAND_TIMEOUT_MS)); do {
val = readl(info->reg.gpmc_prefetch_status);
val = PREFETCH_STATUS_COUNT(val);
cpu_relax();
} while (val && (tim++ < limit));
/* disable and stop the PFPW engine */
omap_prefetch_reset(info->gpmc_cs, info); return;
}
/** * gen_true_ecc - This function will generate true ECC value * @ecc_buf: buffer to store ecc code * * This generated true ECC value can be used when correcting * data read from NAND flash memory core
*/ staticvoid gen_true_ecc(u8 *ecc_buf)
{
u32 tmp = ecc_buf[0] | (ecc_buf[1] << 16) |
((ecc_buf[2] & 0xF0) << 20) | ((ecc_buf[2] & 0x0F) << 8);
/** * omap_compare_ecc - Detect (2 bits) and correct (1 bit) error in data * @ecc_data1: ecc code from nand spare area * @ecc_data2: ecc code from hardware register obtained from hardware ecc * @page_data: page data * * This function compares two ECC's and indicates if there is an error. * If the error can be corrected it will be corrected to the buffer. * If there is no error, %0 is returned. If there is an error but it * was corrected, %1 is returned. Otherwise, %-1 is returned.
*/ staticint omap_compare_ecc(u8 *ecc_data1, /* read from NAND memory */
u8 *ecc_data2, /* read from register */
u8 *page_data)
{
uint i;
u8 tmp0_bit[8], tmp1_bit[8], tmp2_bit[8];
u8 comp0_bit[8], comp1_bit[8], comp2_bit[8];
u8 ecc_bit[24];
u8 ecc_sum = 0;
u8 find_bit = 0;
uint find_byte = 0; int isEccFF;
/** * omap_correct_data - Compares the ECC read with HW generated ECC * @chip: NAND chip object * @dat: page data * @read_ecc: ecc read from nand flash * @calc_ecc: ecc read from HW ECC registers * * Compares the ecc read from nand spare area with ECC registers values * and if ECC's mismatched, it will call 'omap_compare_ecc' for error * detection and correction. If there are no errors, %0 is returned. If * there were errors and all of the errors were corrected, the number of * corrected errors is returned. If uncorrectable errors exist, %-1 is * returned.
*/ staticint omap_correct_data(struct nand_chip *chip, u_char *dat,
u_char *read_ecc, u_char *calc_ecc)
{ struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip)); int blockCnt = 0, i = 0, ret = 0; int stat = 0;
/* Ex NAND_ECC_HW12_2048 */ if (info->nand.ecc.engine_type == NAND_ECC_ENGINE_TYPE_ON_HOST &&
info->nand.ecc.size == 2048)
blockCnt = 4; else
blockCnt = 1;
for (i = 0; i < blockCnt; i++) { if (memcmp(read_ecc, calc_ecc, 3) != 0) {
ret = omap_compare_ecc(read_ecc, calc_ecc, dat); if (ret < 0) return ret; /* keep track of the number of corrected errors */
stat += ret;
}
read_ecc += 3;
calc_ecc += 3;
dat += 512;
} return stat;
}
/** * omap_calculate_ecc - Generate non-inverted ECC bytes. * @chip: NAND chip object * @dat: The pointer to data on which ecc is computed * @ecc_code: The ecc_code buffer * * Using noninverted ECC can be considered ugly since writing a blank * page ie. padding will clear the ECC bytes. This is no problem as long * nobody is trying to write data on the seemingly unused page. Reading * an erased page will produce an ECC mismatch between generated and read * ECC bytes that has to be dealt with separately.
*/ staticint omap_calculate_ecc(struct nand_chip *chip, const u_char *dat,
u_char *ecc_code)
{ struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip));
u32 val;
val = readl(info->reg.gpmc_ecc_config); if (((val >> ECC_CONFIG_CS_SHIFT) & CS_MASK) != info->gpmc_cs) return -EINVAL;
/* clear ecc and enable bits */
val = ECCCLEAR | ECC1;
writel(val, info->reg.gpmc_ecc_control);
/* program ecc and result sizes */
val = ((((info->nand.ecc.size >> 1) - 1) << ECCSIZE1_SHIFT) |
ECC1RESULTSIZE);
writel(val, info->reg.gpmc_ecc_size_config);
switch (mode) { case NAND_ECC_READ: case NAND_ECC_WRITE:
writel(ECCCLEAR | ECC1, info->reg.gpmc_ecc_control); break; case NAND_ECC_READSYN:
writel(ECCCLEAR, info->reg.gpmc_ecc_control); break; default:
dev_info(&info->pdev->dev, "error: unrecognized Mode[%d]!\n", mode); break;
}
/* (ECC 16 or 8 bit col) | ( CS ) | ECC Enable */
val = (dev_width << 7) | (info->gpmc_cs << 1) | (0x1);
writel(val, info->reg.gpmc_ecc_config);
}
/** * omap_enable_hwecc_bch - Program GPMC to perform BCH ECC calculation * @chip: NAND chip object * @mode: Read/Write mode * * When using BCH with SW correction (i.e. no ELM), sector size is set * to 512 bytes and we use BCH_WRAPMODE_6 wrapping mode * for both reading and writing with: * eccsize0 = 0 (no additional protected byte in spare area) * eccsize1 = 32 (skip 32 nibbles = 16 bytes per sector in spare area)
*/ staticvoid __maybe_unused omap_enable_hwecc_bch(struct nand_chip *chip, int mode)
{ unsignedint bch_type; unsignedint dev_width, nsectors; struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip)); enum omap_ecc ecc_opt = info->ecc_opt;
u32 val, wr_mode; unsignedint ecc_size1, ecc_size0;
/** * _omap_calculate_ecc_bch - Generate ECC bytes for one sector * @mtd: MTD device structure * @dat: The pointer to data on which ecc is computed * @ecc_calc: The ecc_code buffer * @i: The sector number (for a multi sector page) * * Support calculating of BCH4/8/16 ECC vectors for one sector * within a page. Sector number is in @i.
*/ staticint _omap_calculate_ecc_bch(struct mtd_info *mtd, const u_char *dat, u_char *ecc_calc, int i)
{ struct omap_nand_info *info = mtd_to_omap(mtd); int eccbytes = info->nand.ecc.bytes; struct gpmc_nand_regs *gpmc_regs = &info->reg;
u8 *ecc_code; unsignedlong bch_val1, bch_val2, bch_val3, bch_val4;
u32 val; int j;
/* ECC scheme specific syndrome customizations */ switch (info->ecc_opt) { case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW: /* Add constant polynomial to remainder, so that * ECC of blank pages results in 0x0 on reading back
*/ for (j = 0; j < eccbytes; j++)
ecc_calc[j] ^= bch4_polynomial[j]; break; case OMAP_ECC_BCH4_CODE_HW: /* Set 8th ECC byte as 0x0 for ROM compatibility */
ecc_calc[eccbytes - 1] = 0x0; break; case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW: /* Add constant polynomial to remainder, so that * ECC of blank pages results in 0x0 on reading back
*/ for (j = 0; j < eccbytes; j++)
ecc_calc[j] ^= bch8_polynomial[j]; break; case OMAP_ECC_BCH8_CODE_HW: /* Set 14th ECC byte as 0x0 for ROM compatibility */
ecc_calc[eccbytes - 1] = 0x0; break; case OMAP_ECC_BCH16_CODE_HW: break; default: return -EINVAL;
}
return 0;
}
/** * omap_calculate_ecc_bch_sw - ECC generator for sector for SW based correction * @chip: NAND chip object * @dat: The pointer to data on which ecc is computed * @ecc_calc: Buffer storing the calculated ECC bytes * * Support calculating of BCH4/8/16 ECC vectors for one sector. This is used * when SW based correction is required as ECC is required for one sector * at a time.
*/ staticint omap_calculate_ecc_bch_sw(struct nand_chip *chip, const u_char *dat, u_char *ecc_calc)
{ return _omap_calculate_ecc_bch(nand_to_mtd(chip), dat, ecc_calc, 0);
}
/** * omap_calculate_ecc_bch_multi - Generate ECC for multiple sectors * @mtd: MTD device structure * @dat: The pointer to data on which ecc is computed * @ecc_calc: Buffer storing the calculated ECC bytes * * Support calculating of BCH4/8/16 ecc vectors for the entire page in one go.
*/ staticint omap_calculate_ecc_bch_multi(struct mtd_info *mtd, const u_char *dat, u_char *ecc_calc)
{ struct omap_nand_info *info = mtd_to_omap(mtd); int eccbytes = info->nand.ecc.bytes; unsignedlong nsectors; int i, ret;
nsectors = ((readl(info->reg.gpmc_ecc_config) >> 4) & 0x7) + 1; for (i = 0; i < nsectors; i++) {
ret = _omap_calculate_ecc_bch(mtd, dat, ecc_calc, i); if (ret) return ret;
ecc_calc += eccbytes;
}
return 0;
}
/** * erased_sector_bitflips - count bit flips * @data: data sector buffer * @oob: oob buffer * @info: omap_nand_info * * Check the bit flips in erased page falls below correctable level. * If falls below, report the page as erased with correctable bit * flip, else report as uncorrectable page.
*/ staticint erased_sector_bitflips(u_char *data, u_char *oob, struct omap_nand_info *info)
{ int flip_bits = 0, i;
for (i = 0; i < info->nand.ecc.size; i++) {
flip_bits += hweight8(~data[i]); if (flip_bits > info->nand.ecc.strength) return 0;
}
for (i = 0; i < info->nand.ecc.bytes - 1; i++) {
flip_bits += hweight8(~oob[i]); if (flip_bits > info->nand.ecc.strength) return 0;
}
/* * Bit flips falls in correctable level. * Fill data area with 0xFF
*/ if (flip_bits) {
memset(data, 0xFF, info->nand.ecc.size);
memset(oob, 0xFF, info->nand.ecc.bytes);
}
return flip_bits;
}
/** * omap_elm_correct_data - corrects page data area in case error reported * @chip: NAND chip object * @data: page data * @read_ecc: ecc read from nand flash * @calc_ecc: ecc read from HW ECC registers * * Calculated ecc vector reported as zero in case of non-error pages. * In case of non-zero ecc vector, first filter out erased-pages, and * then process data via ELM to detect bit-flips.
*/ staticint omap_elm_correct_data(struct nand_chip *chip, u_char *data,
u_char *read_ecc, u_char *calc_ecc)
{ struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip)); struct nand_ecc_ctrl *ecc = &info->nand.ecc; int eccsteps = info->nsteps_per_eccpg; int i , j, stat = 0; int eccflag, actual_eccbytes; struct elm_errorvec err_vec[ERROR_VECTOR_MAX];
u_char *ecc_vec = calc_ecc;
u_char *spare_ecc = read_ecc;
u_char *erased_ecc_vec;
u_char *buf; int bitflip_count; bool is_error_reported = false;
u32 bit_pos, byte_pos, error_max, pos; int err;
switch (info->ecc_opt) { case OMAP_ECC_BCH4_CODE_HW: /* omit 7th ECC byte reserved for ROM code compatibility */
actual_eccbytes = ecc->bytes - 1;
erased_ecc_vec = bch4_vector; break; case OMAP_ECC_BCH8_CODE_HW: /* omit 14th ECC byte reserved for ROM code compatibility */
actual_eccbytes = ecc->bytes - 1;
erased_ecc_vec = bch8_vector; break; case OMAP_ECC_BCH16_CODE_HW:
actual_eccbytes = ecc->bytes;
erased_ecc_vec = bch16_vector; break; default:
dev_err(&info->pdev->dev, "invalid driver configuration\n"); return -EINVAL;
}
/* Initialize elm error vector to zero */
memset(err_vec, 0, sizeof(err_vec));
for (i = 0; i < eccsteps ; i++) {
eccflag = 0; /* initialize eccflag */
/* * Check any error reported, * In case of error, non zero ecc reported.
*/ for (j = 0; j < actual_eccbytes; j++) { if (calc_ecc[j] != 0) {
eccflag = 1; /* non zero ecc, error present */ break;
}
}
if (eccflag == 1) { if (memcmp(calc_ecc, erased_ecc_vec,
actual_eccbytes) == 0) { /* * calc_ecc[] matches pattern for ECC(all 0xff) * so this is definitely an erased-page
*/
} else {
buf = &data[info->nand.ecc.size * i]; /* * count number of 0-bits in read_buf. * This check can be removed once a similar * check is introduced in generic NAND driver
*/
bitflip_count = erased_sector_bitflips(
buf, read_ecc, info); if (bitflip_count) { /* * number of 0-bits within ECC limits * So this may be an erased-page
*/
stat += bitflip_count;
} else { /* * Too many 0-bits. It may be a * - programmed-page, OR * - erased-page with many bit-flips * So this page requires check by ELM
*/
err_vec[i].error_reported = true;
is_error_reported = true;
}
}
}
/* Update ecc vector from GPMC result registers */
ret = omap_calculate_ecc_bch_multi(mtd,
buf + (eccpg * info->eccpg_size),
ecc_calc); if (ret) return ret;
ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc,
chip->oob_poi,
eccpg * info->eccpg_bytes,
info->eccpg_bytes); if (ret) return ret;
}
/* Write ecc vector to OOB area */
info->data_out(chip, chip->oob_poi, mtd->oobsize, false);
return nand_prog_page_end_op(chip);
}
/** * omap_write_subpage_bch - BCH hardware ECC based subpage write * @chip: nand chip info structure * @offset: column address of subpage within the page * @data_len: data length * @buf: data buffer * @oob_required: must write chip->oob_poi to OOB * @page: page number to write * * OMAP optimized subpage write method.
*/ staticint omap_write_subpage_bch(struct nand_chip *chip, u32 offset,
u32 data_len, const u8 *buf, int oob_required, int page)
{ struct mtd_info *mtd = nand_to_mtd(chip); struct omap_nand_info *info = mtd_to_omap(mtd);
u8 *ecc_calc = chip->ecc.calc_buf; int ecc_size = chip->ecc.size; int ecc_bytes = chip->ecc.bytes;
u32 start_step = offset / ecc_size;
u32 end_step = (offset + data_len - 1) / ecc_size; unsignedint eccpg; int step, ret = 0;
/* * Write entire page at one go as it would be optimal * as ECC is calculated by hardware. * ECC is calculated for all subpages but we choose * only what we want.
*/
ret = nand_prog_page_begin_op(chip, page, 0, NULL, 0); if (ret) return ret;
/* * Copy the calculated ECC for the whole page including the * masked values (0xFF) corresponding to unwritten subpages.
*/
ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc, chip->oob_poi,
eccpg * info->eccpg_bytes,
info->eccpg_bytes); if (ret) return ret;
}
/** * omap_read_page_bch - BCH ecc based page read function for entire page * @chip: nand chip info structure * @buf: buffer to store read data * @oob_required: caller requires OOB data read to chip->oob_poi * @page: page number to read * * For BCH ecc scheme, GPMC used for syndrome calculation and ELM module * used for error correction. * Custom method evolved to support ELM error correction & multi sector * reading. On reading page data area is read along with OOB data with * ecc engine enabled. ecc vector updated after read of OOB data. * For non error pages ecc vector reported as zero.
*/ staticint omap_read_page_bch(struct nand_chip *chip, uint8_t *buf, int oob_required, int page)
{ struct mtd_info *mtd = nand_to_mtd(chip); struct omap_nand_info *info = mtd_to_omap(mtd);
uint8_t *ecc_calc = chip->ecc.calc_buf;
uint8_t *ecc_code = chip->ecc.code_buf; unsignedint max_bitflips = 0, eccpg; int stat, ret;
ret = nand_read_page_op(chip, page, 0, NULL, 0); if (ret) return ret;
/** * is_elm_present - checks for presence of ELM module by scanning DT nodes * @info: NAND device structure containing platform data * @elm_node: ELM's DT node
*/ staticbool is_elm_present(struct omap_nand_info *info, struct device_node *elm_node)
{ struct platform_device *pdev;
/* check whether elm-id is passed via DT */ if (!elm_node) {
dev_err(&info->pdev->dev, "ELM devicetree node not found\n"); returnfalse;
}
pdev = of_find_device_by_node(elm_node); /* check whether ELM device is registered */ if (!pdev) {
dev_err(&info->pdev->dev, "ELM device not found\n"); returnfalse;
} /* ELM module available, now configure it */
info->elm_dev = &pdev->dev; returntrue;
}
if (of_property_read_u32(child, "reg", &cs) < 0) {
dev_err(dev, "reg not found in DT\n"); return -EINVAL;
}
info->gpmc_cs = cs;
/* detect availability of ELM module. Won't be present pre-OMAP4 */
info->elm_of_node = of_parse_phandle(child, "ti,elm-id", 0); if (!info->elm_of_node) {
info->elm_of_node = of_parse_phandle(child, "elm_id", 0); if (!info->elm_of_node)
dev_dbg(dev, "ti,elm-id not in DT\n");
}
/* select ecc-scheme for NAND */ if (of_property_read_string(child, "ti,nand-ecc-opt", &s)) {
dev_err(dev, "ti,nand-ecc-opt not found\n"); return -EINVAL;
}
/* select data transfer mode */ if (!of_property_read_string(child, "ti,nand-xfer-type", &s)) { for (i = 0; i < ARRAY_SIZE(nand_xfer_types); i++) { if (!strcasecmp(s, nand_xfer_types[i])) {
info->xfer_type = i; return 0;
}
}
dev_err(dev, "unrecognized value for ti,nand-xfer-type\n"); return -EINVAL;
}
return 0;
}
staticint omap_ooblayout_ecc(struct mtd_info *mtd, int section, struct mtd_oob_region *oobregion)
{ struct omap_nand_info *info = mtd_to_omap(mtd); struct nand_chip *chip = &info->nand; int off = BBM_LEN;
if (info->ecc_opt == OMAP_ECC_HAM1_CODE_HW &&
!(chip->options & NAND_BUSWIDTH_16))
off = 1;
staticint omap_sw_ooblayout_ecc(struct mtd_info *mtd, int section, struct mtd_oob_region *oobregion)
{ struct nand_device *nand = mtd_to_nanddev(mtd); unsignedint nsteps = nanddev_get_ecc_nsteps(nand); unsignedint ecc_bytes = nanddev_get_ecc_bytes_per_step(nand); int off = BBM_LEN;
if (section >= nsteps) return -ERANGE;
/* * When SW correction is employed, one OMAP specific marker byte is * reserved after each ECC step.
*/
oobregion->offset = off + (section * (ecc_bytes + 1));
oobregion->length = ecc_bytes;
return 0;
}
staticint omap_sw_ooblayout_free(struct mtd_info *mtd, int section, struct mtd_oob_region *oobregion)
{ struct nand_device *nand = mtd_to_nanddev(mtd); unsignedint nsteps = nanddev_get_ecc_nsteps(nand); unsignedint ecc_bytes = nanddev_get_ecc_bytes_per_step(nand); int off = BBM_LEN;
if (section) return -ERANGE;
/* * When SW correction is employed, one OMAP specific marker byte is * reserved after each ECC step.
*/
off += ((ecc_bytes + 1) * nsteps); if (off >= mtd->oobsize) return -ERANGE;
default:
dev_err(dev, "xfer_type %d not supported!\n", info->xfer_type); return -EINVAL;
}
if (!omap2_nand_ecc_check(info)) return -EINVAL;
/* * Bail out earlier to let NAND_ECC_ENGINE_TYPE_SOFT code create its own * ooblayout instead of using ours.
*/ if (info->ecc_opt == OMAP_ECC_HAM1_CODE_SW) {
chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT;
chip->ecc.algo = NAND_ECC_ALGO_HAMMING; return 0;
}
/* Shared among all NAND instances to synchronize access to the ECC Engine */ staticstruct nand_controller omap_gpmc_controller; staticbool omap_gpmc_controller_initialized;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.