Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e04dbf35 authored by Boris Brezillon's avatar Boris Brezillon
Browse files

mtd: nand: omap2: switch to mtd_ooblayout_ops



Implementing the mtd_ooblayout_ops interface is the new way of exposing
ECC/OOB layout to MTD users.

Signed-off-by: default avatarBoris Brezillon <boris.brezillon@free-electrons.com>
parent a894cf6c
Loading
Loading
Loading
Loading
+121 −81
Original line number Diff line number Diff line
@@ -178,8 +178,6 @@ struct omap_nand_info {
	struct gpmc_nand_regs		reg;
	struct gpmc_nand_ops		*ops;
	bool				flash_bbt;
	/* generated at runtime depending on ECC algorithm and layout selected */
	struct nand_ecclayout		oobinfo;
	/* fields specific for BCHx_HW ECC scheme */
	struct device			*elm_dev;
	/* NAND ready gpio */
@@ -1714,20 +1712,115 @@ static int omap_get_dt_info(struct device *dev, struct omap_nand_info *info)
	return 0;
}

static int 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 = BADBLOCK_MARKER_LENGTH;

	if (info->ecc_opt == OMAP_ECC_HAM1_CODE_HW &&
	    !(chip->options & NAND_BUSWIDTH_16))
		off = 1;

	if (section)
		return -ERANGE;

	oobregion->offset = off;
	oobregion->length = chip->ecc.total;

	return 0;
}

static int omap_ooblayout_free(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 = BADBLOCK_MARKER_LENGTH;

	if (info->ecc_opt == OMAP_ECC_HAM1_CODE_HW &&
	    !(chip->options & NAND_BUSWIDTH_16))
		off = 1;

	if (section)
		return -ERANGE;

	off += chip->ecc.total;
	if (off >= mtd->oobsize)
		return -ERANGE;

	oobregion->offset = off;
	oobregion->length = mtd->oobsize - off;

	return 0;
}

static const struct mtd_ooblayout_ops omap_ooblayout_ops = {
	.ecc = omap_ooblayout_ecc,
	.free = omap_ooblayout_free,
};

static int omap_sw_ooblayout_ecc(struct mtd_info *mtd, int section,
				 struct mtd_oob_region *oobregion)
{
	struct nand_chip *chip = mtd_to_nand(mtd);
	int off = BADBLOCK_MARKER_LENGTH;

	if (section >= chip->ecc.steps)
		return -ERANGE;

	/*
	 * When SW correction is employed, one OMAP specific marker byte is
	 * reserved after each ECC step.
	 */
	oobregion->offset = off + (section * (chip->ecc.bytes + 1));
	oobregion->length = chip->ecc.bytes;

	return 0;
}

static int omap_sw_ooblayout_free(struct mtd_info *mtd, int section,
				  struct mtd_oob_region *oobregion)
{
	struct nand_chip *chip = mtd_to_nand(mtd);
	int off = BADBLOCK_MARKER_LENGTH;

	if (section)
		return -ERANGE;

	/*
	 * When SW correction is employed, one OMAP specific marker byte is
	 * reserved after each ECC step.
	 */
	off += ((chip->ecc.bytes + 1) * chip->ecc.steps);
	if (off >= mtd->oobsize)
		return -ERANGE;

	oobregion->offset = off;
	oobregion->length = mtd->oobsize - off;

	return 0;
}

static const struct mtd_ooblayout_ops omap_sw_ooblayout_ops = {
	.ecc = omap_sw_ooblayout_ecc,
	.free = omap_sw_ooblayout_free,
};

static int omap_nand_probe(struct platform_device *pdev)
{
	struct omap_nand_info		*info;
	struct omap_nand_platform_data	*pdata = NULL;
	struct mtd_info			*mtd;
	struct nand_chip		*nand_chip;
	struct nand_ecclayout		*ecclayout;
	int				err;
	int				i;
	dma_cap_mask_t			mask;
	unsigned			sig;
	unsigned			oob_index;
	struct resource			*res;
	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),
				GFP_KERNEL);
@@ -1915,7 +2008,7 @@ static int omap_nand_probe(struct platform_device *pdev)

	/*
	 * Bail out earlier to let NAND_ECC_SOFT code create its own
	 * ecclayout instead of using ours.
	 * ooblayout instead of using ours.
	 */
	if (info->ecc_opt == OMAP_ECC_HAM1_CODE_SW) {
		nand_chip->ecc.mode = NAND_ECC_SOFT;
@@ -1923,8 +2016,6 @@ static int omap_nand_probe(struct platform_device *pdev)
	}

	/* populate MTD interface based on ECC scheme */
	ecclayout		= &info->oobinfo;
	nand_chip->ecc.layout	= ecclayout;
	switch (info->ecc_opt) {
	case OMAP_ECC_HAM1_CODE_HW:
		pr_info("nand: using OMAP_ECC_HAM1_CODE_HW\n");
@@ -1935,19 +2026,12 @@ static int omap_nand_probe(struct platform_device *pdev)
		nand_chip->ecc.calculate        = omap_calculate_ecc;
		nand_chip->ecc.hwctl            = omap_enable_hwecc;
		nand_chip->ecc.correct          = omap_correct_data;
		/* define ECC layout */
		ecclayout->eccbytes		= nand_chip->ecc.bytes *
							(mtd->writesize /
							nand_chip->ecc.size);
		if (nand_chip->options & NAND_BUSWIDTH_16)
			oob_index		= BADBLOCK_MARKER_LENGTH;
		else
			oob_index		= 1;
		for (i = 0; i < ecclayout->eccbytes; i++, oob_index++)
			ecclayout->eccpos[i]	= oob_index;
		/* no reserved-marker in ecclayout for this ecc-scheme */
		ecclayout->oobfree->offset	=
				ecclayout->eccpos[ecclayout->eccbytes - 1] + 1;
		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:
@@ -1959,19 +2043,9 @@ static int omap_nand_probe(struct platform_device *pdev)
		nand_chip->ecc.hwctl		= omap_enable_hwecc_bch;
		nand_chip->ecc.correct		= nand_bch_correct_data;
		nand_chip->ecc.calculate	= omap_calculate_ecc_bch;
		/* define ECC layout */
		ecclayout->eccbytes		= nand_chip->ecc.bytes *
							(mtd->writesize /
							nand_chip->ecc.size);
		oob_index			= BADBLOCK_MARKER_LENGTH;
		for (i = 0; i < ecclayout->eccbytes; i++, oob_index++) {
			ecclayout->eccpos[i] = oob_index;
			if (((i + 1) % nand_chip->ecc.bytes) == 0)
				oob_index++;
		}
		/* include reserved-marker in ecclayout->oobfree calculation */
		ecclayout->oobfree->offset	= 1 +
				ecclayout->eccpos[ecclayout->eccbytes - 1] + 1;
		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) {
@@ -1993,16 +2067,8 @@ static int omap_nand_probe(struct platform_device *pdev)
		nand_chip->ecc.calculate	= omap_calculate_ecc_bch;
		nand_chip->ecc.read_page	= omap_read_page_bch;
		nand_chip->ecc.write_page	= omap_write_page_bch;
		/* define ECC layout */
		ecclayout->eccbytes		= nand_chip->ecc.bytes *
							(mtd->writesize /
							nand_chip->ecc.size);
		oob_index			= BADBLOCK_MARKER_LENGTH;
		for (i = 0; i < ecclayout->eccbytes; i++, oob_index++)
			ecclayout->eccpos[i]	= oob_index;
		/* reserved marker already included in ecclayout->eccbytes */
		ecclayout->oobfree->offset	=
				ecclayout->eccpos[ecclayout->eccbytes - 1] + 1;
		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,
@@ -2020,19 +2086,9 @@ static int omap_nand_probe(struct platform_device *pdev)
		nand_chip->ecc.hwctl		= omap_enable_hwecc_bch;
		nand_chip->ecc.correct		= nand_bch_correct_data;
		nand_chip->ecc.calculate	= omap_calculate_ecc_bch;
		/* define ECC layout */
		ecclayout->eccbytes		= nand_chip->ecc.bytes *
							(mtd->writesize /
							nand_chip->ecc.size);
		oob_index			= BADBLOCK_MARKER_LENGTH;
		for (i = 0; i < ecclayout->eccbytes; i++, oob_index++) {
			ecclayout->eccpos[i] = oob_index;
			if (((i + 1) % nand_chip->ecc.bytes) == 0)
				oob_index++;
		}
		/* include reserved-marker in ecclayout->oobfree calculation */
		ecclayout->oobfree->offset	= 1 +
				ecclayout->eccpos[ecclayout->eccbytes - 1] + 1;
		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) {
@@ -2054,6 +2110,8 @@ static int omap_nand_probe(struct platform_device *pdev)
		nand_chip->ecc.calculate	= omap_calculate_ecc_bch;
		nand_chip->ecc.read_page	= omap_read_page_bch;
		nand_chip->ecc.write_page	= omap_write_page_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,
@@ -2061,16 +2119,6 @@ static int omap_nand_probe(struct platform_device *pdev)
		if (err < 0)
			goto return_error;

		/* define ECC layout */
		ecclayout->eccbytes		= nand_chip->ecc.bytes *
							(mtd->writesize /
							nand_chip->ecc.size);
		oob_index			= BADBLOCK_MARKER_LENGTH;
		for (i = 0; i < ecclayout->eccbytes; i++, oob_index++)
			ecclayout->eccpos[i]	= oob_index;
		/* reserved marker already included in ecclayout->eccbytes */
		ecclayout->oobfree->offset	=
				ecclayout->eccpos[ecclayout->eccbytes - 1] + 1;
		break;

	case OMAP_ECC_BCH16_CODE_HW:
@@ -2084,6 +2132,8 @@ static int omap_nand_probe(struct platform_device *pdev)
		nand_chip->ecc.calculate	= omap_calculate_ecc_bch;
		nand_chip->ecc.read_page	= omap_read_page_bch;
		nand_chip->ecc.write_page	= omap_write_page_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,
@@ -2091,16 +2141,6 @@ static int omap_nand_probe(struct platform_device *pdev)
		if (err < 0)
			goto return_error;

		/* define ECC layout */
		ecclayout->eccbytes		= nand_chip->ecc.bytes *
							(mtd->writesize /
							nand_chip->ecc.size);
		oob_index			= BADBLOCK_MARKER_LENGTH;
		for (i = 0; i < ecclayout->eccbytes; i++, oob_index++)
			ecclayout->eccpos[i]	= oob_index;
		/* reserved marker already included in ecclayout->eccbytes */
		ecclayout->oobfree->offset	=
				ecclayout->eccpos[ecclayout->eccbytes - 1] + 1;
		break;
	default:
		dev_err(&info->pdev->dev, "invalid or unsupported ECC scheme\n");
@@ -2108,13 +2148,13 @@ static int omap_nand_probe(struct platform_device *pdev)
		goto return_error;
	}

	/* all OOB bytes from oobfree->offset till end off OOB are free */
	ecclayout->oobfree->length = mtd->oobsize - ecclayout->oobfree->offset;
	/* check if NAND device's OOB is enough to store ECC signatures */
	if (mtd->oobsize < (ecclayout->eccbytes + BADBLOCK_MARKER_LENGTH)) {
	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",
			ecclayout->eccbytes, mtd->oobsize);
			min_oobbytes, mtd->oobsize);
		err = -EINVAL;
		goto return_error;
	}