in nand/raw/omap2.c [1824:2074]
static int omap_nand_attach_chip(struct nand_chip *chip)
{
struct mtd_info *mtd = nand_to_mtd(chip);
struct omap_nand_info *info = mtd_to_omap(mtd);
struct device *dev = &info->pdev->dev;
int min_oobbytes = BBM_LEN;
int elm_bch_strength = -1;
int oobbytes_per_step;
dma_cap_mask_t mask;
int err;
if (chip->bbt_options & NAND_BBT_USE_FLASH)
chip->bbt_options |= NAND_BBT_NO_OOB;
else
chip->options |= NAND_SKIP_BBTSCAN;
/* Re-populate low-level callbacks based on xfer modes */
switch (info->xfer_type) {
case NAND_OMAP_PREFETCH_POLLED:
info->data_in = omap_nand_data_in_pref;
info->data_out = omap_nand_data_out_pref;
break;
case NAND_OMAP_POLLED:
/* Use nand_base defaults for {read,write}_buf */
break;
case NAND_OMAP_PREFETCH_DMA:
dma_cap_zero(mask);
dma_cap_set(DMA_SLAVE, mask);
info->dma = dma_request_chan(dev->parent, "rxtx");
if (IS_ERR(info->dma)) {
dev_err(dev, "DMA engine request failed\n");
return PTR_ERR(info->dma);
} else {
struct dma_slave_config cfg;
memset(&cfg, 0, sizeof(cfg));
cfg.src_addr = info->phys_base;
cfg.dst_addr = info->phys_base;
cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
cfg.src_maxburst = 16;
cfg.dst_maxburst = 16;
err = dmaengine_slave_config(info->dma, &cfg);
if (err) {
dev_err(dev,
"DMA engine slave config failed: %d\n",
err);
return err;
}
info->data_in = omap_nand_data_in_dma_pref;
info->data_out = omap_nand_data_out_dma_pref;
}
break;
case NAND_OMAP_PREFETCH_IRQ:
info->gpmc_irq_fifo = platform_get_irq(info->pdev, 0);
if (info->gpmc_irq_fifo <= 0)
return -ENODEV;
err = devm_request_irq(dev, info->gpmc_irq_fifo,
omap_nand_irq, IRQF_SHARED,
"gpmc-nand-fifo", info);
if (err) {
dev_err(dev, "Requesting IRQ %d, error %d\n",
info->gpmc_irq_fifo, err);
info->gpmc_irq_fifo = 0;
return err;
}
info->gpmc_irq_count = platform_get_irq(info->pdev, 1);
if (info->gpmc_irq_count <= 0)
return -ENODEV;
err = devm_request_irq(dev, info->gpmc_irq_count,
omap_nand_irq, IRQF_SHARED,
"gpmc-nand-count", info);
if (err) {
dev_err(dev, "Requesting IRQ %d, error %d\n",
info->gpmc_irq_count, err);
info->gpmc_irq_count = 0;
return err;
}
info->data_in = omap_nand_data_in_irq_pref;
info->data_out = omap_nand_data_out_irq_pref;
break;
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;
}
/* Populate MTD interface based on ECC scheme */
switch (info->ecc_opt) {
case OMAP_ECC_HAM1_CODE_HW:
dev_info(dev, "nand: using OMAP_ECC_HAM1_CODE_HW\n");
chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST;
chip->ecc.bytes = 3;
chip->ecc.size = 512;
chip->ecc.strength = 1;
chip->ecc.calculate = omap_calculate_ecc;
chip->ecc.hwctl = omap_enable_hwecc;
chip->ecc.correct = omap_correct_data;
mtd_set_ooblayout(mtd, &omap_ooblayout_ops);
oobbytes_per_step = chip->ecc.bytes;
if (!(chip->options & NAND_BUSWIDTH_16))
min_oobbytes = 1;
break;
case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW:
pr_info("nand: using OMAP_ECC_BCH4_CODE_HW_DETECTION_SW\n");
chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST;
chip->ecc.size = 512;
chip->ecc.bytes = 7;
chip->ecc.strength = 4;
chip->ecc.hwctl = omap_enable_hwecc_bch;
chip->ecc.correct = rawnand_sw_bch_correct;
chip->ecc.calculate = omap_calculate_ecc_bch_sw;
mtd_set_ooblayout(mtd, &omap_sw_ooblayout_ops);
/* Reserve one byte for the OMAP marker */
oobbytes_per_step = chip->ecc.bytes + 1;
/* Software BCH library is used for locating errors */
err = rawnand_sw_bch_init(chip);
if (err) {
dev_err(dev, "Unable to use BCH library\n");
return err;
}
break;
case OMAP_ECC_BCH4_CODE_HW:
pr_info("nand: using OMAP_ECC_BCH4_CODE_HW ECC scheme\n");
chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST;
chip->ecc.size = 512;
/* 14th bit is kept reserved for ROM-code compatibility */
chip->ecc.bytes = 7 + 1;
chip->ecc.strength = 4;
chip->ecc.hwctl = omap_enable_hwecc_bch;
chip->ecc.correct = omap_elm_correct_data;
chip->ecc.read_page = omap_read_page_bch;
chip->ecc.write_page = omap_write_page_bch;
chip->ecc.write_subpage = omap_write_subpage_bch;
mtd_set_ooblayout(mtd, &omap_ooblayout_ops);
oobbytes_per_step = chip->ecc.bytes;
elm_bch_strength = BCH4_ECC;
break;
case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW:
pr_info("nand: using OMAP_ECC_BCH8_CODE_HW_DETECTION_SW\n");
chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST;
chip->ecc.size = 512;
chip->ecc.bytes = 13;
chip->ecc.strength = 8;
chip->ecc.hwctl = omap_enable_hwecc_bch;
chip->ecc.correct = rawnand_sw_bch_correct;
chip->ecc.calculate = omap_calculate_ecc_bch_sw;
mtd_set_ooblayout(mtd, &omap_sw_ooblayout_ops);
/* Reserve one byte for the OMAP marker */
oobbytes_per_step = chip->ecc.bytes + 1;
/* Software BCH library is used for locating errors */
err = rawnand_sw_bch_init(chip);
if (err) {
dev_err(dev, "unable to use BCH library\n");
return err;
}
break;
case OMAP_ECC_BCH8_CODE_HW:
pr_info("nand: using OMAP_ECC_BCH8_CODE_HW ECC scheme\n");
chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST;
chip->ecc.size = 512;
/* 14th bit is kept reserved for ROM-code compatibility */
chip->ecc.bytes = 13 + 1;
chip->ecc.strength = 8;
chip->ecc.hwctl = omap_enable_hwecc_bch;
chip->ecc.correct = omap_elm_correct_data;
chip->ecc.read_page = omap_read_page_bch;
chip->ecc.write_page = omap_write_page_bch;
chip->ecc.write_subpage = omap_write_subpage_bch;
mtd_set_ooblayout(mtd, &omap_ooblayout_ops);
oobbytes_per_step = chip->ecc.bytes;
elm_bch_strength = BCH8_ECC;
break;
case OMAP_ECC_BCH16_CODE_HW:
pr_info("Using OMAP_ECC_BCH16_CODE_HW ECC scheme\n");
chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST;
chip->ecc.size = 512;
chip->ecc.bytes = 26;
chip->ecc.strength = 16;
chip->ecc.hwctl = omap_enable_hwecc_bch;
chip->ecc.correct = omap_elm_correct_data;
chip->ecc.read_page = omap_read_page_bch;
chip->ecc.write_page = omap_write_page_bch;
chip->ecc.write_subpage = omap_write_subpage_bch;
mtd_set_ooblayout(mtd, &omap_ooblayout_ops);
oobbytes_per_step = chip->ecc.bytes;
elm_bch_strength = BCH16_ECC;
break;
default:
dev_err(dev, "Invalid or unsupported ECC scheme\n");
return -EINVAL;
}
if (elm_bch_strength >= 0) {
chip->ecc.steps = mtd->writesize / chip->ecc.size;
info->neccpg = chip->ecc.steps / ERROR_VECTOR_MAX;
if (info->neccpg) {
info->nsteps_per_eccpg = ERROR_VECTOR_MAX;
} else {
info->neccpg = 1;
info->nsteps_per_eccpg = chip->ecc.steps;
}
info->eccpg_size = info->nsteps_per_eccpg * chip->ecc.size;
info->eccpg_bytes = info->nsteps_per_eccpg * chip->ecc.bytes;
err = elm_config(info->elm_dev, elm_bch_strength,
info->nsteps_per_eccpg, chip->ecc.size,
chip->ecc.bytes);
if (err < 0)
return err;
}
/* Check if NAND device's OOB is enough to store ECC signatures */
min_oobbytes += (oobbytes_per_step *
(mtd->writesize / chip->ecc.size));
if (mtd->oobsize < min_oobbytes) {
dev_err(dev,
"Not enough OOB bytes: required = %d, available=%d\n",
min_oobbytes, mtd->oobsize);
return -EINVAL;
}
return 0;
}