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

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

mtd: nand: sunxi: move some ECC related operations to their own functions



In order to support DMA operations in a clean way we need to extract some
of the logic coded in sunxi_nfc_hw_ecc_read/write_page() into their own
function.

Signed-off-by: default avatarBoris Brezillon <boris.brezillon@free-electrons.com>
parent c0c9dfa8
Loading
Loading
Loading
Loading
+108 −55
Original line number Original line Diff line number Diff line
@@ -775,6 +775,94 @@ static inline void sunxi_nfc_user_data_to_buf(u32 user_data, u8 *buf)
	buf[3] = user_data >> 24;
	buf[3] = user_data >> 24;
}
}


static inline u32 sunxi_nfc_buf_to_user_data(const u8 *buf)
{
	return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
}

static void sunxi_nfc_hw_ecc_get_prot_oob_bytes(struct mtd_info *mtd, u8 *oob,
						int step, bool bbm, int page)
{
	struct nand_chip *nand = mtd_to_nand(mtd);
	struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);

	sunxi_nfc_user_data_to_buf(readl(nfc->regs + NFC_REG_USER_DATA(step)),
				   oob);

	/* De-randomize the Bad Block Marker. */
	if (bbm && (nand->options & NAND_NEED_SCRAMBLING))
		sunxi_nfc_randomize_bbm(mtd, page, oob);
}

static void sunxi_nfc_hw_ecc_set_prot_oob_bytes(struct mtd_info *mtd,
						const u8 *oob, int step,
						bool bbm, int page)
{
	struct nand_chip *nand = mtd_to_nand(mtd);
	struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
	u8 user_data[4];

	/* Randomize the Bad Block Marker. */
	if (bbm && (nand->options & NAND_NEED_SCRAMBLING)) {
		memcpy(user_data, oob, sizeof(user_data));
		sunxi_nfc_randomize_bbm(mtd, page, user_data);
		oob = user_data;
	}

	writel(sunxi_nfc_buf_to_user_data(oob),
	       nfc->regs + NFC_REG_USER_DATA(step));
}

static void sunxi_nfc_hw_ecc_update_stats(struct mtd_info *mtd,
					  unsigned int *max_bitflips, int ret)
{
	if (ret < 0) {
		mtd->ecc_stats.failed++;
	} else {
		mtd->ecc_stats.corrected += ret;
		*max_bitflips = max_t(unsigned int, *max_bitflips, ret);
	}
}

static int sunxi_nfc_hw_ecc_correct(struct mtd_info *mtd, u8 *data, u8 *oob,
				    int step, bool *erased)
{
	struct nand_chip *nand = mtd_to_nand(mtd);
	struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
	struct nand_ecc_ctrl *ecc = &nand->ecc;
	u32 status, tmp;

	*erased = false;

	status = readl(nfc->regs + NFC_REG_ECC_ST);

	if (status & NFC_ECC_ERR(step))
		return -EBADMSG;

	if (status & NFC_ECC_PAT_FOUND(step)) {
		u8 pattern;

		if (unlikely(!(readl(nfc->regs + NFC_REG_PAT_ID) & 0x1))) {
			pattern = 0x0;
		} else {
			pattern = 0xff;
			*erased = true;
		}

		if (data)
			memset(data, pattern, ecc->size);

		if (oob)
			memset(oob, pattern, ecc->bytes + 4);

		return 0;
	}

	tmp = readl(nfc->regs + NFC_REG_ECC_ERR_CNT(step));

	return NFC_ECC_ERR_CNT(step, tmp);
}

static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd,
static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd,
				       u8 *data, int data_off,
				       u8 *data, int data_off,
				       u8 *oob, int oob_off,
				       u8 *oob, int oob_off,
@@ -786,7 +874,7 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd,
	struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
	struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
	struct nand_ecc_ctrl *ecc = &nand->ecc;
	struct nand_ecc_ctrl *ecc = &nand->ecc;
	int raw_mode = 0;
	int raw_mode = 0;
	u32 status;
	bool erased;
	int ret;
	int ret;


	if (*cur_off != data_off)
	if (*cur_off != data_off)
@@ -812,27 +900,11 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd,


	*cur_off = oob_off + ecc->bytes + 4;
	*cur_off = oob_off + ecc->bytes + 4;


	status = readl(nfc->regs + NFC_REG_ECC_ST);
	ret = sunxi_nfc_hw_ecc_correct(mtd, data, oob, 0, &erased);
	if (status & NFC_ECC_PAT_FOUND(0)) {
	if (erased)
		u8 pattern = 0xff;

		if (unlikely(!(readl(nfc->regs + NFC_REG_PAT_ID) & 0x1)))
			pattern = 0x0;

		memset(data, pattern, ecc->size);
		memset(oob, pattern, ecc->bytes + 4);

		return 1;
		return 1;
	}

	ret = NFC_ECC_ERR_CNT(0, readl(nfc->regs + NFC_REG_ECC_ERR_CNT(0)));

	memcpy_fromio(data, nfc->regs + NFC_RAM0_BASE, ecc->size);

	nand->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_off, -1);
	sunxi_nfc_randomizer_read_buf(mtd, oob, ecc->bytes + 4, true, page);


	if (status & NFC_ECC_ERR(0)) {
	if (ret < 0) {
		/*
		/*
		 * Re-read the data with the randomizer disabled to identify
		 * Re-read the data with the randomizer disabled to identify
		 * bitflips in erased pages.
		 * bitflips in erased pages.
@@ -840,9 +912,13 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd,
		if (nand->options & NAND_NEED_SCRAMBLING) {
		if (nand->options & NAND_NEED_SCRAMBLING) {
			nand->cmdfunc(mtd, NAND_CMD_RNDOUT, data_off, -1);
			nand->cmdfunc(mtd, NAND_CMD_RNDOUT, data_off, -1);
			nand->read_buf(mtd, data, ecc->size);
			nand->read_buf(mtd, data, ecc->size);
		} else {
			memcpy_fromio(data, nfc->regs + NFC_RAM0_BASE,
				      ecc->size);
		}

		nand->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_off, -1);
		nand->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_off, -1);
		nand->read_buf(mtd, oob, ecc->bytes + 4);
		nand->read_buf(mtd, oob, ecc->bytes + 4);
		}


		ret = nand_check_erased_ecc_chunk(data,	ecc->size,
		ret = nand_check_erased_ecc_chunk(data,	ecc->size,
						  oob, ecc->bytes + 4,
						  oob, ecc->bytes + 4,
@@ -850,25 +926,18 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd,
		if (ret >= 0)
		if (ret >= 0)
			raw_mode = 1;
			raw_mode = 1;
	} else {
	} else {
		/*
		memcpy_fromio(data, nfc->regs + NFC_RAM0_BASE, ecc->size);
		 * The engine protects 4 bytes of OOB data per chunk.
		 * Retrieve the corrected OOB bytes.
		 */
		sunxi_nfc_user_data_to_buf(readl(nfc->regs + NFC_REG_USER_DATA(0)),
					   oob);


		/* De-randomize the Bad Block Marker. */
		nand->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_off, -1);
		if (bbm && nand->options & NAND_NEED_SCRAMBLING)
		sunxi_nfc_randomizer_read_buf(mtd, oob, ecc->bytes + 4,
			sunxi_nfc_randomize_bbm(mtd, page, oob);
					      true, page);
	}


	if (ret < 0) {
		sunxi_nfc_hw_ecc_get_prot_oob_bytes(mtd, oob, 0,
		mtd->ecc_stats.failed++;
						    bbm, page);
	} else {
		mtd->ecc_stats.corrected += ret;
		*max_bitflips = max_t(unsigned int, *max_bitflips, ret);
	}
	}


	sunxi_nfc_hw_ecc_update_stats(mtd, max_bitflips, ret);

	return raw_mode;
	return raw_mode;
}
}


@@ -897,11 +966,6 @@ static void sunxi_nfc_hw_ecc_read_extra_oob(struct mtd_info *mtd,
	*cur_off = mtd->oobsize + mtd->writesize;
	*cur_off = mtd->oobsize + mtd->writesize;
}
}


static inline u32 sunxi_nfc_buf_to_user_data(const u8 *buf)
{
	return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
}

static int sunxi_nfc_hw_ecc_write_chunk(struct mtd_info *mtd,
static int sunxi_nfc_hw_ecc_write_chunk(struct mtd_info *mtd,
					const u8 *data, int data_off,
					const u8 *data, int data_off,
					const u8 *oob, int oob_off,
					const u8 *oob, int oob_off,
@@ -918,19 +982,6 @@ static int sunxi_nfc_hw_ecc_write_chunk(struct mtd_info *mtd,


	sunxi_nfc_randomizer_write_buf(mtd, data, ecc->size, false, page);
	sunxi_nfc_randomizer_write_buf(mtd, data, ecc->size, false, page);


	/* Fill OOB data in */
	if ((nand->options & NAND_NEED_SCRAMBLING) && bbm) {
		u8 user_data[4];

		memcpy(user_data, oob, 4);
		sunxi_nfc_randomize_bbm(mtd, page, user_data);
		writel(sunxi_nfc_buf_to_user_data(user_data),
		       nfc->regs + NFC_REG_USER_DATA(0));
	} else {
		writel(sunxi_nfc_buf_to_user_data(oob),
		       nfc->regs + NFC_REG_USER_DATA(0));
	}

	if (data_off + ecc->size != oob_off)
	if (data_off + ecc->size != oob_off)
		nand->cmdfunc(mtd, NAND_CMD_RNDIN, oob_off, -1);
		nand->cmdfunc(mtd, NAND_CMD_RNDIN, oob_off, -1);


@@ -939,6 +990,8 @@ static int sunxi_nfc_hw_ecc_write_chunk(struct mtd_info *mtd,
		return ret;
		return ret;


	sunxi_nfc_randomizer_enable(mtd);
	sunxi_nfc_randomizer_enable(mtd);
	sunxi_nfc_hw_ecc_set_prot_oob_bytes(mtd, oob, 0, bbm, page);

	writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD |
	writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD |
	       NFC_ACCESS_DIR | NFC_ECC_OP,
	       NFC_ACCESS_DIR | NFC_ECC_OP,
	       nfc->regs + NFC_REG_CMD);
	       nfc->regs + NFC_REG_CMD);