mirror of https://gitee.com/openkylin/linux.git
mtd: rawnand: omap2: convert driver to nand_scan()
Two helpers have been added to the core to do all kind of controller side configuration/initialization between the detection phase and the final NAND scan. Implement these hooks so that we can convert the driver to just use nand_scan() instead of the nand_scan_ident() + nand_scan_tail() pair. Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com> Reviewed-by: Boris Brezillon <boris.brezillon@bootlin.com>
This commit is contained in:
parent
c49f3bee8c
commit
e1e6255c31
|
@ -144,12 +144,6 @@ static u_char bch8_vector[] = {0xf3, 0xdb, 0x14, 0x16, 0x8b, 0xd2, 0xbe, 0xcc,
|
||||||
0xac, 0x6b, 0xff, 0x99, 0x7b};
|
0xac, 0x6b, 0xff, 0x99, 0x7b};
|
||||||
static u_char bch4_vector[] = {0x00, 0x6b, 0x31, 0xdd, 0x41, 0xbc, 0x10};
|
static u_char bch4_vector[] = {0x00, 0x6b, 0x31, 0xdd, 0x41, 0xbc, 0x10};
|
||||||
|
|
||||||
/* Shared among all NAND instances to synchronize access to the ECC Engine */
|
|
||||||
static struct nand_controller omap_gpmc_controller = {
|
|
||||||
.lock = __SPIN_LOCK_UNLOCKED(omap_gpmc_controller.lock),
|
|
||||||
.wq = __WAIT_QUEUE_HEAD_INITIALIZER(omap_gpmc_controller.wq),
|
|
||||||
};
|
|
||||||
|
|
||||||
struct omap_nand_info {
|
struct omap_nand_info {
|
||||||
struct nand_chip nand;
|
struct nand_chip nand;
|
||||||
struct platform_device *pdev;
|
struct platform_device *pdev;
|
||||||
|
@ -1915,17 +1909,278 @@ static const struct mtd_ooblayout_ops omap_sw_ooblayout_ops = {
|
||||||
.free = omap_sw_ooblayout_free,
|
.free = omap_sw_ooblayout_free,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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 = BADBLOCK_MARKER_LENGTH;
|
||||||
|
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:
|
||||||
|
chip->read_buf = omap_read_buf_pref;
|
||||||
|
chip->write_buf = omap_write_buf_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, "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;
|
||||||
|
}
|
||||||
|
chip->read_buf = omap_read_buf_dma_pref;
|
||||||
|
chip->write_buf = omap_write_buf_dma_pref;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NAND_OMAP_PREFETCH_IRQ:
|
||||||
|
info->gpmc_irq_fifo = platform_get_irq(info->pdev, 0);
|
||||||
|
if (info->gpmc_irq_fifo <= 0) {
|
||||||
|
dev_err(dev, "Error getting fifo IRQ\n");
|
||||||
|
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) {
|
||||||
|
dev_err(dev, "Error getting IRQ count\n");
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
chip->read_buf = omap_read_buf_irq_pref;
|
||||||
|
chip->write_buf = omap_write_buf_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_SOFT code create its own
|
||||||
|
* ooblayout instead of using ours.
|
||||||
|
*/
|
||||||
|
if (info->ecc_opt == OMAP_ECC_HAM1_CODE_SW) {
|
||||||
|
chip->ecc.mode = NAND_ECC_SOFT;
|
||||||
|
chip->ecc.algo = NAND_ECC_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.mode = NAND_ECC_HW;
|
||||||
|
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.mode = NAND_ECC_HW;
|
||||||
|
chip->ecc.size = 512;
|
||||||
|
chip->ecc.bytes = 7;
|
||||||
|
chip->ecc.strength = 4;
|
||||||
|
chip->ecc.hwctl = omap_enable_hwecc_bch;
|
||||||
|
chip->ecc.correct = nand_bch_correct_data;
|
||||||
|
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 */
|
||||||
|
chip->ecc.priv = nand_bch_init(mtd);
|
||||||
|
if (!chip->ecc.priv) {
|
||||||
|
dev_err(dev, "Unable to use BCH library\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OMAP_ECC_BCH4_CODE_HW:
|
||||||
|
pr_info("nand: using OMAP_ECC_BCH4_CODE_HW ECC scheme\n");
|
||||||
|
chip->ecc.mode = NAND_ECC_HW;
|
||||||
|
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;
|
||||||
|
|
||||||
|
err = elm_config(info->elm_dev, BCH4_ECC,
|
||||||
|
mtd->writesize / chip->ecc.size,
|
||||||
|
chip->ecc.size, chip->ecc.bytes);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW:
|
||||||
|
pr_info("nand: using OMAP_ECC_BCH8_CODE_HW_DETECTION_SW\n");
|
||||||
|
chip->ecc.mode = NAND_ECC_HW;
|
||||||
|
chip->ecc.size = 512;
|
||||||
|
chip->ecc.bytes = 13;
|
||||||
|
chip->ecc.strength = 8;
|
||||||
|
chip->ecc.hwctl = omap_enable_hwecc_bch;
|
||||||
|
chip->ecc.correct = nand_bch_correct_data;
|
||||||
|
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 */
|
||||||
|
chip->ecc.priv = nand_bch_init(mtd);
|
||||||
|
if (!chip->ecc.priv) {
|
||||||
|
dev_err(dev, "unable to use BCH library\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OMAP_ECC_BCH8_CODE_HW:
|
||||||
|
pr_info("nand: using OMAP_ECC_BCH8_CODE_HW ECC scheme\n");
|
||||||
|
chip->ecc.mode = NAND_ECC_HW;
|
||||||
|
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;
|
||||||
|
|
||||||
|
err = elm_config(info->elm_dev, BCH8_ECC,
|
||||||
|
mtd->writesize / chip->ecc.size,
|
||||||
|
chip->ecc.size, chip->ecc.bytes);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OMAP_ECC_BCH16_CODE_HW:
|
||||||
|
pr_info("Using OMAP_ECC_BCH16_CODE_HW ECC scheme\n");
|
||||||
|
chip->ecc.mode = NAND_ECC_HW;
|
||||||
|
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;
|
||||||
|
|
||||||
|
err = elm_config(info->elm_dev, BCH16_ECC,
|
||||||
|
mtd->writesize / chip->ecc.size,
|
||||||
|
chip->ecc.size, chip->ecc.bytes);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
dev_err(dev, "Invalid or unsupported ECC scheme\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct nand_controller_ops omap_nand_controller_ops = {
|
||||||
|
.attach_chip = omap_nand_attach_chip,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Shared among all NAND instances to synchronize access to the ECC Engine */
|
||||||
|
static struct nand_controller omap_gpmc_controller = {
|
||||||
|
.lock = __SPIN_LOCK_UNLOCKED(omap_gpmc_controller.lock),
|
||||||
|
.wq = __WAIT_QUEUE_HEAD_INITIALIZER(omap_gpmc_controller.wq),
|
||||||
|
.ops = &omap_nand_controller_ops,
|
||||||
|
};
|
||||||
|
|
||||||
static int omap_nand_probe(struct platform_device *pdev)
|
static int omap_nand_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct omap_nand_info *info;
|
struct omap_nand_info *info;
|
||||||
struct mtd_info *mtd;
|
struct mtd_info *mtd;
|
||||||
struct nand_chip *nand_chip;
|
struct nand_chip *nand_chip;
|
||||||
int err;
|
int err;
|
||||||
dma_cap_mask_t mask;
|
|
||||||
struct resource *res;
|
struct resource *res;
|
||||||
struct device *dev = &pdev->dev;
|
struct device *dev = &pdev->dev;
|
||||||
int min_oobbytes = BADBLOCK_MARKER_LENGTH;
|
|
||||||
int oobbytes_per_step;
|
|
||||||
|
|
||||||
info = devm_kzalloc(&pdev->dev, sizeof(struct omap_nand_info),
|
info = devm_kzalloc(&pdev->dev, sizeof(struct omap_nand_info),
|
||||||
GFP_KERNEL);
|
GFP_KERNEL);
|
||||||
|
@ -1998,266 +2253,8 @@ static int omap_nand_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
/* scan NAND device connected to chip controller */
|
/* scan NAND device connected to chip controller */
|
||||||
nand_chip->options |= info->devsize & NAND_BUSWIDTH_16;
|
nand_chip->options |= info->devsize & NAND_BUSWIDTH_16;
|
||||||
err = nand_scan_ident(mtd, 1, NULL);
|
|
||||||
if (err) {
|
|
||||||
dev_err(&info->pdev->dev,
|
|
||||||
"scan failed, may be bus-width mismatch\n");
|
|
||||||
goto return_error;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nand_chip->bbt_options & NAND_BBT_USE_FLASH)
|
err = nand_scan(mtd, 1);
|
||||||
nand_chip->bbt_options |= NAND_BBT_NO_OOB;
|
|
||||||
else
|
|
||||||
nand_chip->options |= NAND_SKIP_BBTSCAN;
|
|
||||||
|
|
||||||
/* re-populate low-level callbacks based on xfer modes */
|
|
||||||
switch (info->xfer_type) {
|
|
||||||
case NAND_OMAP_PREFETCH_POLLED:
|
|
||||||
nand_chip->read_buf = omap_read_buf_pref;
|
|
||||||
nand_chip->write_buf = omap_write_buf_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(pdev->dev.parent, "rxtx");
|
|
||||||
|
|
||||||
if (IS_ERR(info->dma)) {
|
|
||||||
dev_err(&pdev->dev, "DMA engine request failed\n");
|
|
||||||
err = PTR_ERR(info->dma);
|
|
||||||
goto return_error;
|
|
||||||
} 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(&pdev->dev, "DMA engine slave config failed: %d\n",
|
|
||||||
err);
|
|
||||||
goto return_error;
|
|
||||||
}
|
|
||||||
nand_chip->read_buf = omap_read_buf_dma_pref;
|
|
||||||
nand_chip->write_buf = omap_write_buf_dma_pref;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NAND_OMAP_PREFETCH_IRQ:
|
|
||||||
info->gpmc_irq_fifo = platform_get_irq(pdev, 0);
|
|
||||||
if (info->gpmc_irq_fifo <= 0) {
|
|
||||||
dev_err(&pdev->dev, "error getting fifo irq\n");
|
|
||||||
err = -ENODEV;
|
|
||||||
goto return_error;
|
|
||||||
}
|
|
||||||
err = devm_request_irq(&pdev->dev, info->gpmc_irq_fifo,
|
|
||||||
omap_nand_irq, IRQF_SHARED,
|
|
||||||
"gpmc-nand-fifo", info);
|
|
||||||
if (err) {
|
|
||||||
dev_err(&pdev->dev, "requesting irq(%d) error:%d",
|
|
||||||
info->gpmc_irq_fifo, err);
|
|
||||||
info->gpmc_irq_fifo = 0;
|
|
||||||
goto return_error;
|
|
||||||
}
|
|
||||||
|
|
||||||
info->gpmc_irq_count = platform_get_irq(pdev, 1);
|
|
||||||
if (info->gpmc_irq_count <= 0) {
|
|
||||||
dev_err(&pdev->dev, "error getting count irq\n");
|
|
||||||
err = -ENODEV;
|
|
||||||
goto return_error;
|
|
||||||
}
|
|
||||||
err = devm_request_irq(&pdev->dev, info->gpmc_irq_count,
|
|
||||||
omap_nand_irq, IRQF_SHARED,
|
|
||||||
"gpmc-nand-count", info);
|
|
||||||
if (err) {
|
|
||||||
dev_err(&pdev->dev, "requesting irq(%d) error:%d",
|
|
||||||
info->gpmc_irq_count, err);
|
|
||||||
info->gpmc_irq_count = 0;
|
|
||||||
goto return_error;
|
|
||||||
}
|
|
||||||
|
|
||||||
nand_chip->read_buf = omap_read_buf_irq_pref;
|
|
||||||
nand_chip->write_buf = omap_write_buf_irq_pref;
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
dev_err(&pdev->dev,
|
|
||||||
"xfer_type(%d) not supported!\n", info->xfer_type);
|
|
||||||
err = -EINVAL;
|
|
||||||
goto return_error;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!omap2_nand_ecc_check(info)) {
|
|
||||||
err = -EINVAL;
|
|
||||||
goto return_error;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Bail out earlier to let NAND_ECC_SOFT code create its own
|
|
||||||
* ooblayout instead of using ours.
|
|
||||||
*/
|
|
||||||
if (info->ecc_opt == OMAP_ECC_HAM1_CODE_SW) {
|
|
||||||
nand_chip->ecc.mode = NAND_ECC_SOFT;
|
|
||||||
nand_chip->ecc.algo = NAND_ECC_HAMMING;
|
|
||||||
goto scan_tail;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* populate MTD interface based on ECC scheme */
|
|
||||||
switch (info->ecc_opt) {
|
|
||||||
case OMAP_ECC_HAM1_CODE_HW:
|
|
||||||
pr_info("nand: using OMAP_ECC_HAM1_CODE_HW\n");
|
|
||||||
nand_chip->ecc.mode = NAND_ECC_HW;
|
|
||||||
nand_chip->ecc.bytes = 3;
|
|
||||||
nand_chip->ecc.size = 512;
|
|
||||||
nand_chip->ecc.strength = 1;
|
|
||||||
nand_chip->ecc.calculate = omap_calculate_ecc;
|
|
||||||
nand_chip->ecc.hwctl = omap_enable_hwecc;
|
|
||||||
nand_chip->ecc.correct = omap_correct_data;
|
|
||||||
mtd_set_ooblayout(mtd, &omap_ooblayout_ops);
|
|
||||||
oobbytes_per_step = nand_chip->ecc.bytes;
|
|
||||||
|
|
||||||
if (!(nand_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");
|
|
||||||
nand_chip->ecc.mode = NAND_ECC_HW;
|
|
||||||
nand_chip->ecc.size = 512;
|
|
||||||
nand_chip->ecc.bytes = 7;
|
|
||||||
nand_chip->ecc.strength = 4;
|
|
||||||
nand_chip->ecc.hwctl = omap_enable_hwecc_bch;
|
|
||||||
nand_chip->ecc.correct = nand_bch_correct_data;
|
|
||||||
nand_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 = nand_chip->ecc.bytes + 1;
|
|
||||||
/* software bch library is used for locating errors */
|
|
||||||
nand_chip->ecc.priv = nand_bch_init(mtd);
|
|
||||||
if (!nand_chip->ecc.priv) {
|
|
||||||
dev_err(&info->pdev->dev, "unable to use BCH library\n");
|
|
||||||
err = -EINVAL;
|
|
||||||
goto return_error;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case OMAP_ECC_BCH4_CODE_HW:
|
|
||||||
pr_info("nand: using OMAP_ECC_BCH4_CODE_HW ECC scheme\n");
|
|
||||||
nand_chip->ecc.mode = NAND_ECC_HW;
|
|
||||||
nand_chip->ecc.size = 512;
|
|
||||||
/* 14th bit is kept reserved for ROM-code compatibility */
|
|
||||||
nand_chip->ecc.bytes = 7 + 1;
|
|
||||||
nand_chip->ecc.strength = 4;
|
|
||||||
nand_chip->ecc.hwctl = omap_enable_hwecc_bch;
|
|
||||||
nand_chip->ecc.correct = omap_elm_correct_data;
|
|
||||||
nand_chip->ecc.read_page = omap_read_page_bch;
|
|
||||||
nand_chip->ecc.write_page = omap_write_page_bch;
|
|
||||||
nand_chip->ecc.write_subpage = omap_write_subpage_bch;
|
|
||||||
mtd_set_ooblayout(mtd, &omap_ooblayout_ops);
|
|
||||||
oobbytes_per_step = nand_chip->ecc.bytes;
|
|
||||||
|
|
||||||
err = elm_config(info->elm_dev, BCH4_ECC,
|
|
||||||
mtd->writesize / nand_chip->ecc.size,
|
|
||||||
nand_chip->ecc.size, nand_chip->ecc.bytes);
|
|
||||||
if (err < 0)
|
|
||||||
goto return_error;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW:
|
|
||||||
pr_info("nand: using OMAP_ECC_BCH8_CODE_HW_DETECTION_SW\n");
|
|
||||||
nand_chip->ecc.mode = NAND_ECC_HW;
|
|
||||||
nand_chip->ecc.size = 512;
|
|
||||||
nand_chip->ecc.bytes = 13;
|
|
||||||
nand_chip->ecc.strength = 8;
|
|
||||||
nand_chip->ecc.hwctl = omap_enable_hwecc_bch;
|
|
||||||
nand_chip->ecc.correct = nand_bch_correct_data;
|
|
||||||
nand_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 = nand_chip->ecc.bytes + 1;
|
|
||||||
/* software bch library is used for locating errors */
|
|
||||||
nand_chip->ecc.priv = nand_bch_init(mtd);
|
|
||||||
if (!nand_chip->ecc.priv) {
|
|
||||||
dev_err(&info->pdev->dev, "unable to use BCH library\n");
|
|
||||||
err = -EINVAL;
|
|
||||||
goto return_error;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case OMAP_ECC_BCH8_CODE_HW:
|
|
||||||
pr_info("nand: using OMAP_ECC_BCH8_CODE_HW ECC scheme\n");
|
|
||||||
nand_chip->ecc.mode = NAND_ECC_HW;
|
|
||||||
nand_chip->ecc.size = 512;
|
|
||||||
/* 14th bit is kept reserved for ROM-code compatibility */
|
|
||||||
nand_chip->ecc.bytes = 13 + 1;
|
|
||||||
nand_chip->ecc.strength = 8;
|
|
||||||
nand_chip->ecc.hwctl = omap_enable_hwecc_bch;
|
|
||||||
nand_chip->ecc.correct = omap_elm_correct_data;
|
|
||||||
nand_chip->ecc.read_page = omap_read_page_bch;
|
|
||||||
nand_chip->ecc.write_page = omap_write_page_bch;
|
|
||||||
nand_chip->ecc.write_subpage = omap_write_subpage_bch;
|
|
||||||
mtd_set_ooblayout(mtd, &omap_ooblayout_ops);
|
|
||||||
oobbytes_per_step = nand_chip->ecc.bytes;
|
|
||||||
|
|
||||||
err = elm_config(info->elm_dev, BCH8_ECC,
|
|
||||||
mtd->writesize / nand_chip->ecc.size,
|
|
||||||
nand_chip->ecc.size, nand_chip->ecc.bytes);
|
|
||||||
if (err < 0)
|
|
||||||
goto return_error;
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case OMAP_ECC_BCH16_CODE_HW:
|
|
||||||
pr_info("using OMAP_ECC_BCH16_CODE_HW ECC scheme\n");
|
|
||||||
nand_chip->ecc.mode = NAND_ECC_HW;
|
|
||||||
nand_chip->ecc.size = 512;
|
|
||||||
nand_chip->ecc.bytes = 26;
|
|
||||||
nand_chip->ecc.strength = 16;
|
|
||||||
nand_chip->ecc.hwctl = omap_enable_hwecc_bch;
|
|
||||||
nand_chip->ecc.correct = omap_elm_correct_data;
|
|
||||||
nand_chip->ecc.read_page = omap_read_page_bch;
|
|
||||||
nand_chip->ecc.write_page = omap_write_page_bch;
|
|
||||||
nand_chip->ecc.write_subpage = omap_write_subpage_bch;
|
|
||||||
mtd_set_ooblayout(mtd, &omap_ooblayout_ops);
|
|
||||||
oobbytes_per_step = nand_chip->ecc.bytes;
|
|
||||||
|
|
||||||
err = elm_config(info->elm_dev, BCH16_ECC,
|
|
||||||
mtd->writesize / nand_chip->ecc.size,
|
|
||||||
nand_chip->ecc.size, nand_chip->ecc.bytes);
|
|
||||||
if (err < 0)
|
|
||||||
goto return_error;
|
|
||||||
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
dev_err(&info->pdev->dev, "invalid or unsupported ECC scheme\n");
|
|
||||||
err = -EINVAL;
|
|
||||||
goto return_error;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* check if NAND device's OOB is enough to store ECC signatures */
|
|
||||||
min_oobbytes += (oobbytes_per_step *
|
|
||||||
(mtd->writesize / nand_chip->ecc.size));
|
|
||||||
if (mtd->oobsize < min_oobbytes) {
|
|
||||||
dev_err(&info->pdev->dev,
|
|
||||||
"not enough OOB bytes required = %d, available=%d\n",
|
|
||||||
min_oobbytes, mtd->oobsize);
|
|
||||||
err = -EINVAL;
|
|
||||||
goto return_error;
|
|
||||||
}
|
|
||||||
|
|
||||||
scan_tail:
|
|
||||||
/* second phase scan */
|
|
||||||
err = nand_scan_tail(mtd);
|
|
||||||
if (err)
|
if (err)
|
||||||
goto return_error;
|
goto return_error;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue