[PATCH] mtd: rawnand: driver for Mediatek MT7621 SoC NAND flash controller

Greg Ungerer gerg at kernel.org
Thu Nov 7 22:16:41 PST 2019


Hi Dan,

On 7/11/19 9:15 pm, Dan Carpenter wrote:
> This driver seems like it could be fixed up and go directly into
> drivers/nand/ instead of staging.  Other comments below.

Thanks for the feedback, much appreciated.
I'll work through it and send out a v2.

Regards
Greg


> On Thu, Nov 07, 2019 at 05:35:21PM +1000, gerg at kernel.org wrote:
>> +static int check_bch_error(struct mtd_info *mtd, u8 *buf, u32 sector, u32 page)
>> +{
>> +	struct nand_chip *chip = mtd_to_nand(mtd);
>> +	struct host *host = nand_get_controller_data(chip);
>> +	u16 sectormask = 1 << sector;
>> +	u32 errdebug, i, errnum;
>> +	u32 timeout = 0xffff;
>> +	u32 byte, bitoffset, bit1, bit2;
>> +	u32 bit[6];
>> +	int corrected = 0;
>> +
>> +	/* Wait for Decode Done */
>> +	while ((sectormask & ecc_read16(ECC_DECDONE_REG16)) == 0) {
>> +		timeout--;
>> +		if (timeout == 0)
>> +			return -EIO;
> 
> Could we do these on one line like:
> 
> 		if (--timeout == 0)
> 			return -EIO;
> 
> Later instead of counting down we count up.  Let's do it consistently
> throughout.  I really wish timeout were an int instead of a u32.  There
> are too many u32s in this code for no reason.  Making things unsigned
> doesn't make it safer.  Generally things like iterators and small
> counters like this should just be int.
> 
>> +	}
>> +
>> +	/*
>> +	 * We will manually correct the error bits in the last sector,
>> +	 * not all the sectors of the page!
>> +	 */
>> +	memset(bit, 0, sizeof(bit));
>> +	errdebug = ecc_read32(ECC_DECENUM_REG32);
>> +	errnum = ecc_read32(ECC_DECENUM_REG32) >> (sector << 2);
>> +	errnum &= 0xf;
>> +
>> +	if (errnum == 0)
>> +		return 0;
>> +
>> +	if (errnum == 0xf) {
>> +		/*
>> +		 * Increment the last read's failed counter only. The
>> +		 * caller supposed to check if it is a blank page with
>                          ^^^
> Missing *is*.
> 
> 
>> +		 * bit-flips, or a real ECC error. If the latter, it
>> +		 * should increment the failed counter with this last
>> +		 * read's failed counter
>> +		 */
>> +		host->last_failed++;
>> +		corrected = -EBADMSG;
> 
> 
> If the next ecc_read16(ECC_DECFER_REG16) fails then we set "corrected"
> to -EIO.  Should we just return -EBADMSG here?
> 
>> +	} else {
>> +		corrected = errnum;
>> +
>> +		for (i = 0; i < ((errnum + 1) >> 1); ++i) {
>> +			bit[i] = ecc_read32(ECC_DECEL0_REG32 + i);
>> +			bit1 = bit[i] & 0x1FFF;
>> +			/*
>> +			 * Check if bitflip is in data block (< 0x1000)
>> +			 * or OOB. Fix it only in data block.
>> +			 */
>> +			if (bit1 < 0x1000) {
>> +				byte = bit1 / 8;
>> +				bitoffset = bit1 % 8;
>> +				buf[byte] = buf[byte] ^ (1 << bitoffset);
>> +			}
>> +
>> +			mtd->ecc_stats.corrected++;
>> +
>> +			bit2 = (bit[i] >> 16) & 0x1FFF;
>> +			if (bit2 != 0) {
>> +				/*
>> +				 * Check if bitflip is in data block
>> +				 * (< 0x1000) or OOB. Fix it only in
>> +				 * data block.
>> +				 */
>> +				if (bit2 < 0x1000) {
>> +					byte = bit2 / 8;
>> +					bitoffset = bit2 % 8;
>> +					buf[byte] = buf[byte] ^
>> +						    (1 << bitoffset);
>> +				}
>> +
>> +				mtd->ecc_stats.corrected++;
>> +			}
>> +		}
>> +	}
>> +	if ((ecc_read16(ECC_DECFER_REG16) & (1 << sector)) == 0)
>> +		corrected = -EIO;
>> +
>> +	return corrected;
>> +}
>> +
>> +static bool RFIFOValidSize(u16 size)
>> +{
>> +	u32 timeout = 0xffff;
>> +
>> +	while (FIFO_RD_REMAIN(regread16(NFI_FIFOSTA_REG16)) < size) {
>> +		timeout--;
>> +		if (timeout == 0)
>> +			return false;
> 
> 	if (--timeout == 0)
> 		return false;
> 
>> +	}
>> +	return true;
>> +}
>> +
>> +static bool WFIFOValidSize(u16 size)
>> +{
>> +	u32 timeout = 0xffff;
>> +
>> +	while (FIFO_WR_REMAIN(regread16(NFI_FIFOSTA_REG16)) > size) {
>> +		timeout--;
>> +		if (timeout == 0)
>> +			return false;
> 
> 	if (--timeout == 0)
> 		return false;
> 
> 
>> +	}
>> +	return true;
>> +}
>> +
>> +static bool status_ready(u32 status)
>> +{
>> +	u32 timeout = 0xffff;
>> +
>> +	while ((regread32(NFI_STA_REG32) & status) != 0) {
>> +		timeout--;
>> +		if (timeout == 0)
>> +			return false;
> 
> 	if (--timeout == 0)
> 		return false;
> 
> 
>> +	}
>> +	return true;
>> +}
>> +
>> +static bool reset(void)
>> +{
>> +	int timeout = 0xffff;
>> +
>> +	if (regread16(NFI_MASTERSTA_REG16)) {
>> +		regwrite16(NFI_CON_REG16, CON_FIFO_FLUSH | CON_NFI_RST);
>> +		while (regread16(NFI_MASTERSTA_REG16)) {
>> +			timeout--;
>> +			if (!timeout)
>> +				pr_err("mt7621-nand: %s timeout\n", __func__);
> 
> 	if (--timeout == 0)
> 		pr_err("mt7621-nand: %s timeout\n", __func__);
> 
> We may as well return after a timeout instead of looping forever.  The
> system is hosed, but maybe it will be limping enough to collect some
> debug information.
> 
> 
>> +		}
>> +	}
>> +
>> +	/* issue reset operation */
>> +	regwrite16(NFI_CON_REG16, CON_FIFO_FLUSH | CON_NFI_RST);
>> +
>> +	return status_ready(STA_NFI_FSM_MASK | STA_NAND_BUSY) &&
>> +			    RFIFOValidSize(0) &&
>> +			    WFIFOValidSize(0);
>> +}
>> +
>> +static void set_mode(u16 mode)
>> +{
>> +	u16 v = regread16(NFI_CNFG_REG16);
>> +
>> +	v &= ~CNFG_OP_MODE_MASK;
>> +	v |= mode;
>> +	regwrite16(NFI_CNFG_REG16, v);
>> +}
>> +
>> +static void set_autoformat(bool enable)
>> +{
>> +	if (enable)
>> +		regset16(NFI_CNFG_REG16, CNFG_AUTO_FMT_EN);
>> +	else
>> +		regclr16(NFI_CNFG_REG16, CNFG_AUTO_FMT_EN);
>> +}
>> +
>> +static void configure_fdm(u16 size)
>> +{
>> +	regclr16(NFI_PAGEFMT_REG16, PAGEFMT_FDM_MASK | PAGEFMT_FDM_ECC_MASK);
>> +	regset16(NFI_PAGEFMT_REG16, size << PAGEFMT_FDM_SHIFT);
>> +	regset16(NFI_PAGEFMT_REG16, size << PAGEFMT_FDM_ECC_SHIFT);
>> +}
>> +
>> +static void configure_lock(void)
>> +{
>> +	const u32 write_col = 2;
>> +	const u32 write_row = 3;
>> +	const u32 erase_col = 0;
>> +	const u32 erase_row = 3;
>> +
>> +	regwrite16(NFI_LOCKANOB_REG16,
>> +		   (write_col << PROG_CADD_NOB_SHIFT) |
>> +		   (write_row << PROG_RADD_NOB_SHIFT) |
>> +		   (erase_col << ERASE_CADD_NOB_SHIFT) |
>> +		   (erase_row << ERASE_RADD_NOB_SHIFT));
>> +}
>> +
>> +static bool pio_ready(void)
>> +{
>> +	int count = 0;
>> +
>> +	while (!(regread16(NFI_PIO_DIRDY_REG16) & 1)) {
>> +		count++;
>> +		if (count > 0xffff) {
>> +			pr_err("mt7621-nand: %s timeout\n", __func__);
>> +			return false;
>> +		}
> 
> 	if (--timeout == 0)
> 
>> +	}
>> +	return true;
>> +}
>> +
>> +static bool set_command(u16 command)
>> +{
>> +	regwrite16(NFI_CMD_REG16, command);
>> +	return status_ready(STA_CMD_STATE);
>> +}
>> +
>> +static bool set_address(u32 column, u32 row, u16 colnob, u16 rownob)
>> +{
>> +	regwrite32(NFI_COLADDR_REG32, column);
>> +	regwrite32(NFI_ROWADDR_REG32, row);
>> +	regwrite16(NFI_ADDRNOB_REG16, colnob | (rownob << ADDR_ROW_NOB_SHIFT));
>> +	return status_ready(STA_ADDR_STATE);
>> +}
>> +
>> +static void mt7621_cmd_ctrl(struct nand_chip *chip, int dat, unsigned int ctrl)
>> +{
>> +	if (ctrl & NAND_ALE) {
>> +		set_address(dat, 0, 1, 0);
>> +	} else if (ctrl & NAND_CLE) {
>> +		reset();
>> +		set_mode(0x6000);
>> +		set_command(dat);
>> +	}
>> +}
>> +
>> +static bool check_RW_count(u16 writesize)
>> +{
>> +	u32 timeout = 0xffff;
>> +	u16 sec = writesize >> 9;
>> +
>> +	while (ADDRCNTR_CNTR(regread16(NFI_ADDRCNTR_REG16)) < sec) {
>> +		timeout--;
>> +		if (timeout == 0) {
>> +			pr_warn("mt7621-nand: %s timeout\n", __func__);
>> +			return false;
>> +		}
> 
> 		if (--timeout == 0) {
> 
>> +	}
>> +	return true;
>> +}
>> +
>> +/*
>> + * Reset NFI HW internal state machine and flush NFI in/out FIFO
>> + */
>> +static bool ready_for_read(struct nand_chip *chip, u32 row,
>> +			   u32 column, bool full, u8 *buf)
>> +{
>> +	u16 sec = 1 << (chip->page_shift - 9);
>> +	u32 colnob = 2, rownob = host->addr_cycles - 2;
>> +	bool ret = false;
>> +
>> +	if (chip->options & NAND_BUSWIDTH_16)
>> +		column /= 2;
>> +
>> +	if (!reset())
>> +		goto cleanup;
> 
> There is no cleanup.  Misleading label name.  Just say "return false;
> 
>> +
>> +	regset16(NFI_CNFG_REG16, CNFG_HW_ECC_EN);
>> +	set_mode(CNFG_OP_READ);
>> +	regset16(NFI_CNFG_REG16, CNFG_READ_EN);
>> +	regwrite16(NFI_CON_REG16, sec << CON_NFI_SEC_SHIFT);
>> +
>> +	if (full) {
>> +		regclr16(NFI_CNFG_REG16, CNFG_AHB);
>> +		regset16(NFI_CNFG_REG16, CNFG_HW_ECC_EN);
>> +	} else {
>> +		regclr16(NFI_CNFG_REG16, CNFG_HW_ECC_EN);
>> +		regclr16(NFI_CNFG_REG16, CNFG_AHB);
>> +	}
>> +
>> +	set_autoformat(full);
>> +	if (full)
>> +		ecc_decode_start();
>> +	if (!set_command(NAND_CMD_READ0))
>> +		goto cleanup;
>> +	if (!set_address(column, row, colnob, rownob))
>> +		goto cleanup;
>> +	if (!set_command(NAND_CMD_READSTART))
>> +		goto cleanup;
>> +	if (!status_ready(STA_NAND_BUSY))
>> +		goto cleanup;
>> +
>> +	ret = true;
>> +
>> +cleanup:
>> +	return ret;
>> +}
>> +
>> +static bool ready_for_write(struct nand_chip *chip, u32 row,
>> +			    u32 column, bool full, u8 *buf)
>> +{
>> +	u32 sec = 1 << (chip->page_shift - 9);
>> +	u32 colnob = 2, rownob = host->addr_cycles - 2;
>> +	bool ret = false;
>> +
>> +	if (chip->options & NAND_BUSWIDTH_16)
>> +		column /= 2;
>> +
>> +	/* Reset NFI HW internal state machine and flush NFI in/out FIFO */
>> +	if (!reset())
>> +		return false;
>> +
>> +	set_mode(CNFG_OP_PRGM);
>> +
>> +	regclr16(NFI_CNFG_REG16, CNFG_READ_EN);
>> +
>> +	regwrite16(NFI_CON_REG16, sec << CON_NFI_SEC_SHIFT);
>> +
>> +	if (full) {
>> +		regclr16(NFI_CNFG_REG16, CNFG_AHB);
>> +		regset16(NFI_CNFG_REG16, CNFG_HW_ECC_EN);
>> +	} else {
>> +		regclr16(NFI_CNFG_REG16, CNFG_HW_ECC_EN);
>> +		regclr16(NFI_CNFG_REG16, CNFG_AHB);
>> +	}
>> +
>> +	set_autoformat(full);
>> +
>> +	if (full)
>> +		ecc_encode_start();
>> +
>> +	if (!set_command(NAND_CMD_SEQIN))
>> +		goto cleanup;
> 
> There isn't any cleanup.
> 
>> +	/* FIX ME: For Any Kind of AddrCycle */
>> +	if (!set_address(column, row, colnob, rownob))
>> +		goto cleanup;
>> +
>> +	if (!status_ready(STA_NAND_BUSY))
>> +		goto cleanup;
>> +
>> +	ret = true;
>> +
>> +cleanup:
>> +	return ret;
>> +}
>> +
>> +static bool check_dececc_done(u32 sec)
>> +{
>> +	u32 timeout, dec_mask;
>> +
>> +	timeout = 0xffff;
> 
> Move this to the declaration like before.
> 
>> +	dec_mask = (1 << sec) - 1;
>> +	while ((dec_mask != ecc_read16(ECC_DECDONE_REG16)) && timeout > 0)
>> +		timeout--;
> 
> 		if (--timeout == 0) {
> 			pr_err("mt7621-nand: ECC_DECDONE: timeout\n");
> 			return false;
> 		}
> 
> 
>> +	if (timeout == 0) {
>> +		pr_err("mt7621-nand: ECC_DECDONE: timeout\n");
>> +		return false;
>> +	}
>> +	return true;
>> +}
>> +
>> +static bool mcu_read_data(u8 *buf, u32 length)
>> +{
>> +	int timeout = 0xffff;
>> +	u32 *buf32 = (u32 *)buf;
>> +	u32 i;
>> +
>> +	if ((u32)buf % 4 || length % 4)
>               ^^^^^^^
> I'm so surprised that this cast doesn't generate a "cast from pointer to
> integer of different size" compile warning...  Did you want to cast to
> unsigned long?
> 
>> +		regset16(NFI_CNFG_REG16, CNFG_BYTE_RW);
>> +	else
>> +		regclr16(NFI_CNFG_REG16, CNFG_BYTE_RW);
>> +
>> +	/* regwrite32(NFI_STRADDR_REG32, 0); */
>> +	regset16(NFI_CON_REG16, CON_NFI_BRD);
>> +
>> +	if ((u32)buf % 4 || length % 4) {
>              ^^^^^^^^^^^^
> 
>> +		for (i = 0; (i < (length)) && (timeout > 0);) {
>> +			if (regread16(NFI_PIO_DIRDY_REG16) & 1) {
>> +				*buf++ = (u8)regread32(NFI_DATAR_REG32);
>> +				i++;
>> +			} else {
>> +				timeout--;
>> +			}
>> +			if (timeout == 0) {
>> +				pr_warn("mt7621-nand: %s timeout\n", __func__);
>> +				return false;
>> +			}
> 
> 
> 		i = 0;
> 		while (i < length) {
> 			if (regread16(NFI_PIO_DIRDY_REG16) & 1) {
> 				*buf++ = (u8)regread32(NFI_DATAR_REG32);
> 				i++;
> 			} else {
> 				if (--timeout == 0) {
> 					pr_warn("mt7621-nand: %s timeout\n", __func__);
> 					return false;
> 				}
> 			}
> 		}
> 
> 
> Except looking a bit lower, I think you only want to do that for the
> first mod four bits, then you can start doing 4 byte reads.  Probably
> it's a little faster.
> 
> 
>> +		}
>> +	} else {
>> +		for (i = 0; (i < (length >> 2)) && (timeout > 0);) {
> 
> Use (length / sizeof(u32)) instead of the shift.
> 
>> +			if (regread16(NFI_PIO_DIRDY_REG16) & 1) {
>> +				*buf32++ = regread32(NFI_DATAR_REG32);
>> +				i++;
>> +			} else {
>> +				timeout--;
>> +			}
>> +			if (timeout == 0) {
>> +				pr_warn("mt7621-nand: %s timeout\n", __func__);
>> +				return false;
>> +			}
>> +		}
>> +	}
>> +
>> +	return true;
>> +}
>> +
>> +static bool mcu_write_data(struct mtd_info *mtd, const u8 *buf, u32 length)
>> +{
>> +	u32 timeout = 0xffff;
>> +	u32 *buf32;
>> +	u32 i;
>> +
>> +	regclr16(NFI_CNFG_REG16, CNFG_BYTE_RW);
>> +	regset16(NFI_CON_REG16, CON_NFI_BWR);
>> +	buf32 = (u32 *)buf;
>> +
>> +	if ((u32)buf % 4 || length % 4)
>> +		regset16(NFI_CNFG_REG16, CNFG_BYTE_RW);
>> +	else
>> +		regclr16(NFI_CNFG_REG16, CNFG_BYTE_RW);
>> +
>> +	if ((u32)buf % 4 || length % 4) {
>> +		for (i = 0; (i < (length)) && (timeout > 0);) {
>> +			if (regread16(NFI_PIO_DIRDY_REG16) & 1) {
>> +				regwrite32(NFI_DATAW_REG32, *buf++);
>> +				i++;
>> +			} else {
>> +				timeout--;
>> +			}
>> +			if (timeout == 0) {
>> +				pr_warn("mt7621-nand: %s timeout\n", __func__);
>> +				return false;
>> +			}
>> +		}
>> +	} else {
>> +		for (i = 0; (i < (length >> 2)) && (timeout > 0);) {
>> +			if (regread16(NFI_PIO_DIRDY_REG16) & 1) {
>> +				regwrite32(NFI_DATAW_REG32, *buf32++);
>> +				i++;
>> +			} else {
>> +				timeout--;
>> +			}
>> +			if (timeout == 0) {
>> +				pr_warn("mt7621-nand: %s timeout\n", __func__);
>> +				return false;
>> +			}
>> +		}
>> +	}
> 
> Same stuff.
> 
>> +
>> +	return true;
>> +}
>> +
>> +static void read_fdm_data(u8 *buf, u32 sec)
>> +{
>> +	u32 *buf32 = (u32 *)buf;
>> +	u32 i;
>> +
>> +	if (buf32) {
> 
> Can this really be NULL?  It doesn't appear to be checked consistently
> at first glance.
> 
>> +		for (i = 0; i < sec; ++i) {
>> +			*buf32++ = regread32(NFI_FDM0L_REG32 + (i << 3));
>> +			*buf32++ = regread32(NFI_FDM0M_REG32 + (i << 3));
>> +		}
>> +	}
>> +}
>> +
>> +static u8 fdm_buf[64];
> 
> Why is this static and why is outside the scope of the function?  It
> raises concerns about race conditions.  It's not large, so it could
> be declared on the stack, no?
> 
>> +
>> +static void write_fdm_data(struct nand_chip *chip, u8 *buf, u32 sec)
>> +{
>> +	struct nand_oobfree *free_entry;
>> +	bool empty = true;
>> +	u8 checksum = 0;
>> +	u32 *buf32;
>> +	u32 i, j;
>> +
>> +	memcpy(fdm_buf, buf, sec * 8);
>> +
>> +	free_entry = layout->oobfree;
>> +	for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free_entry[i].length; i++) {
>> +		for (j = 0; j < free_entry[i].length; j++) {
>> +			if (buf[free_entry[i].offset + j] != 0xff)
>> +				empty = false;
>> +			checksum ^= buf[free_entry[i].offset + j];
>> +		}
>> +	}
>> +
>> +	if (!empty)
>> +		fdm_buf[free_entry[i - 1].offset + free_entry[i - 1].length] =
>> +			checksum;
>> +
>> +	buf32 = (u32 *)fdm_buf;
>> +	for (i = 0; i < sec; ++i) {
>> +		regwrite32(NFI_FDM0L_REG32 + (i << 3), *buf32++);
>> +		regwrite32(NFI_FDM0M_REG32 + (i << 3), *buf32++);
>> +	}
>> +}
>> +
>> +static void stop_read(void)
>> +{
>> +	regclr16(NFI_CON_REG16, CON_NFI_BRD);
>> +	reset();
>> +	ecc_decode_end();
>> +	regwrite16(NFI_INTR_EN_REG16, 0);
>> +}
>> +
>> +static void stop_write(void)
>> +{
>> +	regclr16(NFI_CON_REG16, CON_NFI_BWR);
>> +	ecc_encode_end();
>> +	regwrite16(NFI_INTR_EN_REG16, 0);
>> +}
>> +
>> +/*
>> + * This is a copy of the nand_check_erased_buf() function from nand_base.c, to
>> + * keep the nand_base.c clean
>> + */
>> +static int check_erased_buf(void *buf, int len, int bitflips_threshold)
>> +{
>> +	const unsigned char *bitmap = buf;
>> +	int bitflips = 0;
>> +	int weight;
>> +
>> +	for (; len && ((uintptr_t)bitmap) % sizeof(long);
>> +	     len--, bitmap++) {
>> +		weight = hweight8(*bitmap);
>> +		bitflips += BITS_PER_BYTE - weight;
>> +		if (unlikely(bitflips > bitflips_threshold))
>> +			return -EBADMSG;
>> +	}
>> +
>> +	for (; len >= sizeof(long);
>> +	     len -= sizeof(long), bitmap += sizeof(long)) {
>> +		unsigned long d = *((unsigned long *)bitmap);
>> +
>> +		if (d == ~0UL)
>> +			continue;
>> +		weight = hweight_long(d);
>> +		bitflips += BITS_PER_LONG - weight;
>> +		if (unlikely(bitflips > bitflips_threshold))
>> +			return -EBADMSG;
>> +	}
>> +
>> +	for (; len > 0; len--, bitmap++) {
>> +		weight = hweight8(*bitmap);
>> +		bitflips += BITS_PER_BYTE - weight;
>> +		if (unlikely(bitflips > bitflips_threshold))
>> +			return -EBADMSG;
>> +	}
>> +
>> +	return bitflips;
>> +}
>> +
>> +/*
>> + * This is the modified version of the nand_check_erased_ecc_chunk() in
>> + * nand_base.c. This driver cannot use the generic function, as the ECC layout
>> + * is slightly different (the 2k(data)+64(OOB) page is split up in to 4
>> + * (512-byte data + 16-byte OOB) pages. Each of these sectors have 4-bit ECC,
>> + * so we check them separately, and allow 4 bitflips / sector.
>> + * We'll fix up the page to all-0xff only if all sectors in the page appears to
>> + * be blank
>> + */
>> +static int check_erased_ecc_chunk(void *data, int datalen, void *oob,
>> +				  int ooblen, int bitflips_threshold)
>> +{
>> +	int data_bitflips = 0, oob_bitflips = 0;
> 
> These initializers can be deleted.
> 
>> +
>> +	data_bitflips = check_erased_buf(data, datalen, bitflips_threshold);
>> +	if (data_bitflips < 0)
>> +		return data_bitflips;
>> +
>> +	bitflips_threshold -= data_bitflips;
>> +
>> +	oob_bitflips = check_erased_buf(oob, ooblen, bitflips_threshold);
>> +	if (oob_bitflips < 0)
>> +		return oob_bitflips;
>> +
>> +	bitflips_threshold -= oob_bitflips;
> 
> No need for this.  It's a no-op.
> 
>> +	return data_bitflips + oob_bitflips;
>> +}
>> +
>> +static int read_oob_raw(struct mtd_info *mtd, u8 *buf, int page, int len)
>> +{
>> +	struct nand_chip *chip = mtd_to_nand(mtd);
>> +	int sec_num = 1 << (chip->page_shift - 9);
>> +	int spare_per_sector = mtd->oobsize / sec_num;
>> +	u32 column = 0;
>> +	u32 sector = 0;
>> +	int res = 0;
>> +	int read_len = 0;
>> +
>> +	if (len > NAND_MAX_OOBSIZE || len % OOB_AVAI_PER_SECTOR || !buf) {
>> +		pr_warn("mt7621-nand: invalid parameter, len: %d, buf: %p\n",
>> +			len, buf);
>> +		return -EINVAL;
>> +	}
>> +
>> +	while (len > 0) {
>> +		read_len = min(len, spare_per_sector);
>> +		column = NAND_SECTOR_SIZE +
>> +			 sector * (NAND_SECTOR_SIZE + spare_per_sector);
>> +		if (!ready_for_read(chip, page, column, false, NULL)) {
>> +			pr_warn("mt7621-nand: ready_for_read() failed\n");
>> +			res = -EIO;
>> +			goto error;
>> +		}
>> +		if (!mcu_read_data(buf + spare_per_sector * sector, read_len)) {
>> +			pr_warn("mt7621-nand: mcu_read_data() failed\n");
>> +			res = -EIO;
>> +			goto error;
>> +		}
>> +		check_RW_count(read_len);
>> +		stop_read();
>> +		sector++;
>> +		len -= read_len;
>> +	}
>> +
>> +error:
>> +	regclr16(NFI_CON_REG16, CON_NFI_BRD);
>> +	return res;
>> +}
>> +
>> +static int write_oob_raw(struct mtd_info *mtd, const u8 *buf, int page, int len)
>> +{
>> +	struct nand_chip *chip = mtd_to_nand(mtd);
>> +	int sec_num = 1 << (chip->page_shift - 9);
>> +	int spare_per_sector = mtd->oobsize / sec_num;
>> +	u32 column = 0;
>> +	u32 sector = 0;
>> +	int write_len = 0;
>> +	int status;
>> +
>> +	if (len > NAND_MAX_OOBSIZE || len % OOB_AVAI_PER_SECTOR || !buf) {
>> +		pr_warn("mt7621-nand: invalid parameter, len: %d, buf: %p\n",
>> +			len, buf);
>> +		return -EINVAL;
>> +	}
>> +
>> +	while (len > 0) {
>> +		write_len = min(len, spare_per_sector);
>> +		column = sector * (NAND_SECTOR_SIZE + spare_per_sector) +
>> +			 NAND_SECTOR_SIZE;
>> +		if (!ready_for_write(chip, page, column, false, NULL))
>> +			return -EIO;
>> +		if (!mcu_write_data(mtd, buf + sector * spare_per_sector,
>> +				    write_len))
>> +			return -EIO;
>> +		check_RW_count(write_len);
>> +		regclr16(NFI_CON_REG16, CON_NFI_BWR);
>> +		set_command(NAND_CMD_PAGEPROG);
>> +		while (regread32(NFI_STA_REG32) & STA_NAND_BUSY)
>> +			;
>> +		status = chip->legacy.waitfunc(chip);
>> +		if (status & NAND_STATUS_FAIL) {
>> +			pr_warn("mt7621-nand: failed status: %d\n", status);
>> +			return -EIO;
>> +		}
>> +		len -= write_len;
>> +		sector++;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static int exec_read_page(struct mtd_info *mtd, u32 row, u32 page_size,
>> +			  u8 *buf, u8 *fdmbuf)
>> +{
>> +	struct nand_chip *chip = mtd_to_nand(mtd);
>> +	struct host *host = nand_get_controller_data(chip);
>> +	u32 sec = page_size >> 9;
>> +	int corrected = 0;
>> +
>> +	host->last_failed = 0;
>> +
>> +	if (ready_for_read(chip, row, 0, true, buf)) {
> 
> Flip this around:
> 
> 	if (!ready_for_read(chip, row, 0, true, buf))
> 		return -EIO;
> 
> 
>> +		int j;
>> +
>> +		for (j = 0; j < sec; j++) {
>> +			int ret;
>> +
>> +			if (!mcu_read_data(buf + j * 512, 512)) {
>> +				corrected = -EIO;
>> +				break;
>> +			}
>> +			if (!check_dececc_done(j + 1)) {
>> +				corrected = -EIO;
>> +				break;
>> +			}
>> +			ret = check_bch_error(mtd, buf + j * 512, j, row);
>> +			if (ret < 0) {
>> +				corrected = ret;
>> +				if (ret != -EBADMSG)
>> +					break;
>> +			} else if (corrected >= 0) {
>> +				corrected = max_t(int, corrected, ret);
>> +			}
>> +		}
>> +		if (!status_ready(STA_NAND_BUSY))
>> +			corrected = -EIO;
>> +
>> +		read_fdm_data(fdmbuf, sec);
>> +		stop_read();
>> +	} else {
>> +		corrected = -EIO;
>> +	}
>> +
>> +	if (corrected == -EBADMSG) {
>> +		u32 local_oob_aligned[NAND_MAX_OOBSIZE / sizeof(u32)];
>> +		u8 *local_oob = (u8 *)local_oob_aligned;
>> +		int ret;
>> +
>> +		/*
>> +		 * If there was an uncorrectable ECC error, check if it is a
>> +		 * blank page with bit-flips. For this, we check the number of
>> +		 * 0s in each sector (including the OOB), which should not
>> +		 * exceed the ECC strength. Until the number of 0s is lower or
>> +		 * equal, we consider it as a blank page
>> +		 */
>> +		ret = read_oob_raw(mtd, local_oob, row, mtd->oobsize);
>> +		if (ret == 0) {
> 
> Flip this around and do a direct return.
> 
> 		if (ret) {
> 			mtd->ecc_stats.failed += host->last_failed;
> 			pr_warn("mt7621-nand: %s failed to read oob after ECC error\n",
> 				__func__);
> 			return -EBADMSG;
> 		}
> 
> 
> 
>> +			int spare_per_sector = mtd->oobsize / sec;
>> +			int sector_size = page_size / sec;
>> +			int max_bitflips = 0;
>> +			u32 corrected = 0;
> 
> This "corrected" shadows the earlier "corrected" variable and it's
> confusing to have two variables in the same function with the same name.
> 
>> +			int i;
>> +
>> +			for (i = 0; i < sec; i++) {
>> +				ret = check_erased_ecc_chunk(
>> +					&buf[i * sector_size], sector_size,
>> +					&local_oob[i * spare_per_sector],
>> +					spare_per_sector, chip->ecc.strength);
>> +				if (ret < 0)
>> +					break;
> 
> Change this break to a return.
> 
> 			if (ret < 0) {
> 				mtd->ecc_stats.failed += host->last_failed;
> 				pr_err("mt7621-nand: uncorrectable ECC errors at page=%d\n",
> 				       row);
> 				return -EBADMSG;
> 			}
> 
> 
> 
>> +
>> +				max_bitflips = max_t(int, max_bitflips, ret);
>> +				corrected += ret;
>> +			}
>> +
>> +			if (ret >= 0) {
>> +				mtd->ecc_stats.corrected += corrected;
>> +
>> +				memset(buf, 0xff, page_size);
>> +				memset(fdmbuf, 0xff, sizeof(u32) * 2 * sec);
>> +
>> +				corrected = max_bitflips;
> 
> This looks like a bug caused by the two "corrected" variables.  Just do
> 
> 			return max_bitflips;
> 
> 
>> +			} else {
>> +				mtd->ecc_stats.failed += host->last_failed;
>> +				pr_err("mt7621-nand: uncorrectable ECC errors at page=%d\n",
>> +				       row);
>> +			}
>> +		} else {
>> +			mtd->ecc_stats.failed += host->last_failed;
>> +			pr_warn("mt7621-nand: %s failed to read oob after ECC error\n",
>> +				__func__);
>> +			/* Keep return value as -EBADMSG */
>> +		}
>> +	}
>> +
>> +	return corrected;
>> +}
>> +
>> +static int exec_write_page(struct mtd_info *mtd, u32 row,
>> +			   u32 page_size, u8 *buf, u8 *fdmbuf)
>> +{
>> +	struct nand_chip *chip = mtd_to_nand(mtd);
>> +	u32 sec = page_size >> 9;
>> +	u8 status;
>> +
>> +	if (ready_for_write(chip, row, 0, true, buf)) {
> 
> Shouldn't we return -EIO if we're not ready?
> 
>> +		write_fdm_data(chip, fdmbuf, sec);
>> +		mcu_write_data(mtd, buf, page_size);
>> +		check_RW_count(page_size);
>> +		stop_write();
>> +		set_command(NAND_CMD_PAGEPROG);
>> +		while (regread32(NFI_STA_REG32) & STA_NAND_BUSY)
>> +			;
>> +	}
>> +
>> +	status = chip->legacy.waitfunc(chip);
>> +	if (status & NAND_STATUS_FAIL)
>> +		return -EIO;
>> +	return 0;
>> +}
>> +
>> +static void command_bp(struct nand_chip *chip, unsigned int command,
>> +		       int column, int page)
>> +{
>> +	struct mtd_info *mtd = nand_to_mtd(chip);
>> +
>> +	switch (command) {
>> +	case NAND_CMD_SEQIN:
>> +		memset(host->OOB, 0xff, sizeof(host->OOB));
>> +		host->data_buf = NULL;
>> +		host->row = page;
>> +		host->column = column;
>> +		break;
>> +
>> +	case NAND_CMD_PAGEPROG:
>> +		if (host->data_buf || (host->OOB[0] != 0xff)) {
>> +			u8 *buf = host->data_buf ? host->data_buf
>> +						 : chip->data_buf;
>> +
>> +			exec_write_page(mtd, host->row, mtd->writesize,
>> +					buf, host->OOB);
>> +			host->row = (u32)-1;
>> +			host->OOBrow = (u32)-1;
>> +		}
>> +		break;
>> +
>> +	case NAND_CMD_READOOB:
>> +		host->row = page;
>> +		host->column = column + mtd->writesize;
>> +		break;
>> +
>> +	case NAND_CMD_READ0:
>> +		host->row = page;
>> +		host->column = column;
>> +		break;
>> +
>> +	case NAND_CMD_ERASE1:
>> +		reset();
>> +		set_mode(CNFG_OP_ERASE);
>> +		set_command(NAND_CMD_ERASE1);
>> +		set_address(0, page, 0, host->addr_cycles - 2);
>> +		break;
>> +
>> +	case NAND_CMD_ERASE2:
>> +		set_command(NAND_CMD_ERASE2);
>> +		while (regread32(NFI_STA_REG32) & STA_NAND_BUSY)
>> +			;
>> +		break;
>> +
>> +	case NAND_CMD_STATUS:
>> +		reset();
>> +		regclr16(NFI_CNFG_REG16, CNFG_BYTE_RW);
>> +		set_mode(CNFG_OP_SRD);
>> +		set_mode(CNFG_READ_EN);
>> +		regclr16(NFI_CNFG_REG16, CNFG_AHB);
>> +		regclr16(NFI_CNFG_REG16, CNFG_HW_ECC_EN);
>> +		set_command(NAND_CMD_STATUS);
>> +		regclr16(NFI_CON_REG16, CON_NFI_NOB_MASK);
>> +		regwrite16(NFI_CON_REG16,
>> +			   CON_NFI_SRD | (1 << CON_NFI_NOB_SHIFT));
>> +		host->cmdstatus = true;
>> +		break;
>> +
>> +	case NAND_CMD_RESET:
>> +		reset();
>> +		regwrite16(NFI_INTR_EN_REG16, INTR_RST_DONE_EN);
>> +		set_command(NAND_CMD_RESET);
>> +		regwrite16(NFI_CNRNB_REG16, 0xf1);
>> +		while (!(regread16(NFI_INTR_REG16) & INTR_RST_DONE_EN))
>> +			;
>> +		break;
>> +
>> +	case NAND_CMD_READID:
>> +		reset();
>> +		/* Disable HW ECC */
>> +		regclr16(NFI_CNFG_REG16, CNFG_HW_ECC_EN);
>> +		regclr16(NFI_CNFG_REG16, CNFG_AHB);
>> +		regset16(NFI_CNFG_REG16, CNFG_READ_EN | CNFG_BYTE_RW);
>> +		reset();
>> +		set_mode(CNFG_OP_SRD);
>> +		set_command(NAND_CMD_READID);
>> +		set_address(0, 0, 1, 0);
>> +		regwrite16(NFI_CON_REG16, CON_NFI_SRD);
>> +		while (regread32(NFI_STA_REG32) & STA_DATAR_STATE)
>> +			;
>> +		break;
>> +
>> +	default:
>> +		pr_err("mt7621-nand: unknown NAND command, 0x%x\n", command);
>> +		break;
>> +	}
>> +}
>> +
>> +static void set_ecc_mode(struct mtd_info *mtd)
>> +{
>> +	struct nand_chip *chip = mtd_to_nand(mtd);
>> +	u32 spare_per_sector = mtd->oobsize / (mtd->writesize / 512);
>> +	u32 spare_bit = PAGEFMT_SPARE_16;
>> +	u32 ecc_bit = 4;
>> +
>> +	if (spare_per_sector >= 28) {
>> +		spare_bit = PAGEFMT_SPARE_28;
>> +		ecc_bit = 12;
>> +		spare_per_sector = 28;
>> +	} else if (spare_per_sector >= 27) {
>> +		spare_bit = PAGEFMT_SPARE_27;
>> +		ecc_bit = 8;
>> +		spare_per_sector = 27;
>> +	} else if (spare_per_sector >= 26) {
>> +		spare_bit = PAGEFMT_SPARE_26;
>> +		ecc_bit = 8;
>> +		spare_per_sector = 26;
>> +	} else if (spare_per_sector >= 16) {
>> +		spare_bit = PAGEFMT_SPARE_16;
>> +		ecc_bit = 4;
>> +		spare_per_sector = 16;
>> +	} else {
>> +		pr_warn("mt7621-nand: NFI not support oobsize: %x\n",
>> +			spare_per_sector);
>> +	}
>> +	chip->ecc.strength = ecc_bit;
>> +	mtd->oobsize = spare_per_sector * (mtd->writesize / 512);
>> +	pr_info("mt7621-nand: ecc bit: %d, spare_per_sector: %d\n",
>> +		ecc_bit, spare_per_sector);
>> +
>> +	/* Setup PageFormat */
>> +	if (mtd->writesize == 4096) {
>> +		regset16(NFI_PAGEFMT_REG16,
>> +			 (spare_bit << PAGEFMT_SPARE_SHIFT) | PAGEFMT_4K);
>> +		chip->legacy.cmdfunc = command_bp;
>> +	} else if (mtd->writesize == 2048) {
>> +		regset16(NFI_PAGEFMT_REG16,
>> +			 (spare_bit << PAGEFMT_SPARE_SHIFT) | PAGEFMT_2K);
>> +		chip->legacy.cmdfunc = command_bp;
>> +	}
>> +	ecc_config(ecc_bit);
>> +}
>> +
>> +static void select_chip(struct nand_chip *chip, int nr)
>> +{
>> +	switch (nr) {
>> +	case -1:
>> +		break;
>> +	case 0:
>> +	case 1:
>> +		/* Jun Shen, 2011.04.13 */
> 
> These sorts of dates comments aren't useful.
> 
>> +		/* Note: MT6577 EVB NAND is mounted on CS0, but FPGA is CS1 */
>> +		regwrite16(NFI_CSEL_REG16, nr);
>> +		/* Jun Shen, 2011.04.13 */
> 
> Delete.
> 
>> +		break;
>> +	}
>> +}
>> +
>> +static u8 read_byte(struct nand_chip *chip)
>> +{
>> +	u8 retval = 0;
> 
> Delete initializer.  This is just turning off GCC's check for
> uninitialized variables.  It also turns on an unused assignment warning.
> We haven't enabled it but we'd like to.  I haven't commented on every
> bogus initializer that I saw, but I probably should have... :/
> 
>> +
>> +	if (!pio_ready()) {
>> +		pr_err("mt7621-nand: pio ready timeout\n");
>> +		retval = false;
> 
> Return here?  Unused assignment.  This bug would have been caught if
> we could turn on that GCC warning I was just mentioning.  :P
> 
> False is sort of weird here because this function returns a u8 instead
> of a bool.
> 
>> +	}
>> +
>> +	if (host->cmdstatus) {
> 
> Flip this around:
> 
> 	if (!host->cmdstatus)
> 		return regread8(NFI_DATAR_REG32);
> 
>> +		retval = regread8(NFI_DATAR_REG32);
>> +		regclr16(NFI_CON_REG16, CON_NFI_NOB_MASK);
>> +		reset();
>> +		regset16(NFI_CNFG_REG16, CNFG_HW_ECC_EN);
>> +		host->cmdstatus = false;
>> +	} else {
>> +		retval = regread8(NFI_DATAR_REG32);
>> +	}
>> +
>> +	return retval;
>> +}
>> +
>> +static void read_buf(struct nand_chip *chip, u8 *buf, int len)
>> +{
>> +	struct mtd_info *mtd = nand_to_mtd(chip);
>> +	u32 page_size = mtd->writesize;
>> +	u32 column = host->column;
>> +
>> +	if (column < page_size) {
>> +		if ((column == 0) && (len >= page_size)) {
>> +			exec_read_page(mtd, host->row, page_size, buf,
>> +				       host->OOB);
>> +			if (len > page_size) {
>> +				u32 size;
>> +
>> +				size = min(len - page_size, sizeof(host->OOB));
>> +				memcpy(buf + page_size, host->OOB, size);
>> +			}
>> +		} else {
>> +			exec_read_page(mtd, host->row, page_size,
>> +				       chip->data_buf, host->OOB);
>> +			memcpy(buf, chip->data_buf + column, len);
>> +		}
>> +		host->OOBrow = host->row;
>> +	} else {
>> +		u32 offset = column - page_size;
>> +		u32 size = min(len - offset, sizeof(host->OOB));
>> +
>> +		if (host->OOBrow != host->row) {
>> +			exec_read_page(mtd, host->row, page_size,
>> +				       chip->data_buf, host->OOB);
>> +			host->OOBrow = host->row;
>> +		}
>> +		memcpy(buf, host->OOB + offset, size);
>> +	}
>> +	host->column += len;
>> +}
>> +
>> +static void write_buf(struct nand_chip *chip, const u8 *buf, int len)
>> +{
>> +	struct mtd_info *mtd = nand_to_mtd(chip);
>> +	u32 page_size = mtd->writesize;
>> +	u32 column = host->column;
>> +	int size, i;
>> +
>> +	if (column >= page_size) {
>> +		u32 offset = column - page_size;
>> +		u8 *OOB = host->OOB + offset;
>> +
>> +		size = min(len, (int)(sizeof(host->OOB) - offset));
>> +		for (i = 0; i < size; i++)
>> +			OOB[i] &= buf[i];
>> +	} else {
>> +		host->data_buf = (u8 *)buf;
>> +	}
>> +
>> +	host->column += len;
>> +}
>> +
>> +static int write_page_hwecc(struct nand_chip *chip, const u8 *buf,
>> +			    int oob_required, int page)
>> +{
>> +	struct mtd_info *mtd = nand_to_mtd(chip);
>> +
>> +	nand_prog_page_begin_op(chip, page, 0, buf, mtd->writesize);
>> +	write_buf(chip, chip->oob_poi, mtd->oobsize);
>> +	return nand_prog_page_end_op(chip);
>> +}
>> +
>> +static int read_page_hwecc(struct nand_chip *chip, u8 *buf,
>> +			   int oob_required, int page)
>> +{
>> +	struct mtd_info *mtd = nand_to_mtd(chip);
>> +	int corrected;
>> +
>> +	corrected = exec_read_page(mtd, page, mtd->writesize,
>> +				   buf, chip->oob_poi);
>> +
>> +	return (corrected == -EBADMSG) ? 0 : corrected;
>> +}
>> +
>> +static int write_oob_hw(struct nand_chip *chip, int page)
>> +{
>> +	struct mtd_info *mtd = nand_to_mtd(chip);
>> +	int sec_num = 1 << (chip->page_shift - 9);
>> +	int spare_per_sector = mtd->oobsize / sec_num;
>> +	int i, iter;
>> +
>> +	memcpy(local_oob_buf, chip->oob_poi, mtd->oobsize);
> 
> The concern with this buffer would be locking again.  But I haven't
> looked at it.
> 
>> +
>> +	/* copy ecc data */
>> +	for (i = 0; i < layout->eccbytes; i++) {
>> +		iter = (i / (spare_per_sector - OOB_AVAI_PER_SECTOR)) *
>> +		       spare_per_sector +
>> +		       OOB_AVAI_PER_SECTOR +
>> +		       i % (spare_per_sector - OOB_AVAI_PER_SECTOR);
>> +		local_oob_buf[iter] = chip->oob_poi[layout->eccpos[i]];
>> +	}
>> +
>> +	/* copy FDM data */
>> +	for (i = 0; i < sec_num; i++)
>> +		memcpy(&local_oob_buf[i * spare_per_sector],
>> +		       &chip->oob_poi[i * OOB_AVAI_PER_SECTOR],
>> +		       OOB_AVAI_PER_SECTOR);
>> +
>> +	return write_oob_raw(mtd, local_oob_buf, page, mtd->oobsize);
>> +}
>> +
>> +static int write_oob(struct nand_chip *chip, int page)
>> +{
>> +	int page_per_block = 1 << (chip->phys_erase_shift - chip->page_shift);
>> +	int block = page / page_per_block;
>> +	int page_in_block = page % page_per_block;
>> +
>> +	if (write_oob_hw(chip, page_in_block + block * page_per_block)) {
>> +		pr_warn("mt7621-nand: write oob fail at block: 0x%x, page: 0x%x\n",
>> +			block, page_in_block);
>> +		return NAND_STATUS_FAIL;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static int read_oob_hw(struct nand_chip *chip, int page)
>> +{
>> +	struct mtd_info *mtd = nand_to_mtd(chip);
>> +	int sec_num = 1 << (chip->page_shift - 9);
>> +	int spare_per_sector = mtd->oobsize / sec_num;
>> +	int i;
>> +	u8 iter = 0;
>> +
>> +	if (read_oob_raw(mtd, chip->oob_poi, page, mtd->oobsize)) {
>> +		pr_err("mt7621-nand: read_oob_raw() return failed\n");
>> +		return -EIO;
>> +	}
>> +
>> +	/* adjust to ecc physical layout to memory layout */
>> +	/*********************************************************/
>> +	/* FDM0 | ECC0 | FDM1 | ECC1 | FDM2 | ECC2 | FDM3 | ECC3 */
>> +	/*  8B  |  8B  |  8B  |  8B  |  8B  |  8B  |  8B  |  8B  */
>> +	/*********************************************************/
>> +
>> +	memcpy(local_oob_buf, chip->oob_poi, mtd->oobsize);
>> +
>> +	/* copy ecc data */
>> +	for (i = 0; i < layout->eccbytes; i++) {
>> +		iter = (i / (spare_per_sector - OOB_AVAI_PER_SECTOR)) *
>> +		       spare_per_sector +
>> +		       OOB_AVAI_PER_SECTOR +
>> +		       i % (spare_per_sector - OOB_AVAI_PER_SECTOR);
>> +		chip->oob_poi[layout->eccpos[i]] = local_oob_buf[iter];
>> +	}
>> +
>> +	/* copy FDM data */
>> +	for (i = 0; i < sec_num; i++)
>> +		memcpy(&chip->oob_poi[i * OOB_AVAI_PER_SECTOR],
>> +		       &local_oob_buf[i * spare_per_sector],
>> +		       OOB_AVAI_PER_SECTOR);
>> +
>> +	return 0;
>> +}
>> +
>> +static int read_oob(struct nand_chip *chip, int page)
>> +{
>> +	int page_per_block = 1 << (chip->phys_erase_shift - chip->page_shift);
>> +	int block = page / page_per_block;
>> +	int page_in_block = page % page_per_block;
>> +
>> +	if (read_oob_hw(chip, page_in_block + block * page_per_block) != 0)
>> +		return -1;
> 
> It would be better to preserve the error code from read_oob_hw()
> 
>> +	return 0;
>> +}
>> +
>> +static int block_bad_hw(struct nand_chip *chip, loff_t ofs)
>> +{
>> +	struct mtd_info *mtd = nand_to_mtd(chip);
>> +	int page = (int)(ofs >> chip->page_shift);
>> +	unsigned int page_per_block;
>> +	u8 oob_buf[8];
>> +
>> +	page_per_block = 1 << (chip->phys_erase_shift - chip->page_shift);
>> +	page &= ~(page_per_block - 1);
>> +	if (read_oob_raw(mtd, oob_buf, page, sizeof(oob_buf))) {
>> +		pr_warn("mt7621-nand: read_oob_raw() failed\n");
>> +		return 1;
>> +	}
>> +
>> +	if (oob_buf[0] != 0xff) {
>> +		pr_warn("mt7621-nand: bad block detected at page=%d\n", page);
>> +		return 1;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static int block_bad(struct nand_chip *chip, loff_t ofs)
>> +{
>> +	int block = (int)ofs >> chip->phys_erase_shift;
>> +
>> +	return block_bad_hw(chip, block << chip->phys_erase_shift);
>> +}
>> +
>> +static void init_hw(struct host *host)
>> +{
>> +	host->OOBrow = (u32)-1;
>> +
>> +	/* Set default NFI access timing control */
>> +	regwrite32(NFI_ACCCON_REG32, host->access_timing);
>> +	regwrite16(NFI_CNFG_REG16, 0);
>> +	regwrite16(NFI_PAGEFMT_REG16, 0);
>> +
>> +	/* Reset the state machine and data FIFO, because flushing FIFO */
>> +	reset();
>> +
>> +	/* Set the ECC engine */
>> +	if (host->nand_chip.ecc.mode == NAND_ECC_HW) {
>> +		regset16(NFI_CNFG_REG16, CNFG_HW_ECC_EN);
>> +		ecc_config(4);
>> +		configure_fdm(8);
>> +		configure_lock();
>> +	}
>> +
>> +	regset16(NFI_IOCON_REG16, 0x47);
>> +}
>> +
>> +static int dev_ready(struct nand_chip *chip)
>> +{
>> +	return !(regread32(NFI_STA_REG32) & STA_NAND_BUSY);
>> +}
>> +
>> +static int oob_mt7621_ooblayout_ecc(struct mtd_info *mtd, int section,
>> +				    struct mtd_oob_region *oobregion)
>> +{
>> +	oobregion->length = 8;
>> +	oobregion->offset = layout->eccpos[section * 8];
>> +	return 0;
>> +}
>> +
>> +static int oob_mt7621_ooblayout_free(struct mtd_info *mtd, int section,
>> +				     struct mtd_oob_region *oobregion)
>> +{
>> +	if (section >= (layout->eccbytes / 8))
>> +		return -ERANGE;
>> +	oobregion->offset = layout->oobfree[section].offset;
>> +	oobregion->length = layout->oobfree[section].length;
>> +	return 0;
>> +}
>> +
>> +/*
>> + * Code to support the legacy mediatek nand flash bad block table.
>> + * The default for this driver is to use the standard Linux bad block
>> + * table format. However you need a new boot loader that supports that.
>> + * The old (and most often used) medaitek boot loaders use their own
>> + * BBT format, and this code implements that. There is a devicetree
>> + * binding that enables use of this.
>> + */
>> +#define BBT_BLOCK_NUM_DEFAULT	32
>> +#define BBT_OOB_SIGNATURE	1
>> +#define BBT_SIGNATURE_LEN	7
>> +
>> +static const u8 oob_signature[] = "mtknand";
>> +static u32 bbt_size;
>> +
>> +static int read_legacy_bbt_page(struct mtd_info *mtd, unsigned int page)
>> +{
>> +	struct nand_chip *chip = mtd_to_nand(mtd);
>> +
>> +	if (read_oob_hw(chip, page) == 0) {
> 
> Flip this around.  Preserve the error code.
> 
>> +		int corrected;
>> +
>> +		if (chip->oob_poi[0] != 0xff) {
>> +			pr_info("mt7621-nand: Bad Block on page=%d\n", page);
>> +			return -ENODEV;
>> +		}
>> +		if (memcmp(&chip->oob_poi[BBT_OOB_SIGNATURE], oob_signature,
>> +			   BBT_SIGNATURE_LEN) != 0) {
>> +			pr_info("mt7621-nand: no BBT signature, page=%d\n",
>> +				page);
>> +			return -EINVAL;
>> +		}
>> +		corrected = exec_read_page(mtd, page, mtd->writesize,
>> +					   chip->data_buf, chip->oob_poi);
>> +		if (corrected >= 0) {
> 
> Flip this.  Always to error handling instead of success handling.  The
> error path should be indented two tabs and the success path one tab.
> 
> 		if (corrected < 0)
> 			return -EIO;
> 
> 
> 
> 
>> +			int bbt_bytes = (bbt_size <= mtd->writesize)
>> +					? bbt_size
>> +					: mtd->writesize;
>> +
>> +			pr_info("mt7621-nand: BBT signature match, page=%d\n",
>> +				page);
>> +			memcpy(chip->bbt, chip->data_buf, bbt_bytes);
>> +			return 0;
>> +		}
>> +	}
>> +
>> +	pr_err("mt7621-nand: legacy BBT read failed at page %d\n", page);
>> +	return -EIO;
>> +}
>> +
>> +static int load_legacy_bbt(struct mtd_info *mtd)
>> +{
>> +	struct nand_chip *chip = mtd_to_nand(mtd);
>> +	struct host *host = nand_get_controller_data(chip);
>> +	u32 blocks;
>> +	int i;
>> +
>> +	blocks = 0x1 << (chip->chip_shift - chip->phys_erase_shift);
>> +	bbt_size = blocks >> 2;
> 
> Why are we dividing by four here?  I would have thought that this was
> supposed to be a multply by sizeof(u32) or something.
> 
> Normal divide and multiply are more readable than shifts.
> 
> I hate these globals.  It should be saved as chip->bbt_size = size;
> 
>> +
>> +	if (!chip->bbt) {
>> +		chip->bbt = kzalloc(bbt_size, GFP_KERNEL);
>> +		if (!chip->bbt)
>> +			return -ENOMEM;
>> +	}
>> +
>> +	for (i = blocks - 1; i >= (blocks - host->legacybbt_block_num); i--) {
>> +		int page = i << (chip->phys_erase_shift - chip->page_shift);
>> +
>> +		if (read_legacy_bbt_page(mtd, page) == 0) {
> 
> It's weird that this only has to succeed once instead of every time but
> I don't know this code well.
> 
>> +			pr_info("mt7621-nand: loading BBT success (%d)\n",
>> +				page);
>> +			return 0;
>> +		}
>> +	}
>> +
>> +	pr_err("mt7621-nand: loading Bad Block Table failed\n");
>> +	return -ENODEV;
>> +}
>> +
>> +static int mt7621_nand_attach(struct nand_chip *chip)
>> +{
>> +	struct mtd_info *mtd = nand_to_mtd(chip);
>> +	int i;
>> +
>> +	set_ecc_mode(mtd);
>> +
>> +	if (nanddev_target_size(&chip->base) < (256 * 1024 * 1024))
>> +		host->addr_cycles = 4;
>> +
>> +	/* allocate buffers or call select_chip here or a bit earlier*/
>> +	chip->data_buf = kzalloc(mtd->writesize + mtd->oobsize, GFP_KERNEL);
>> +	chip->ecc.calc_buf = kzalloc(mtd->oobsize, GFP_KERNEL);
>> +	chip->ecc.code_buf = kzalloc(mtd->oobsize, GFP_KERNEL);
>> +	if (!chip->data_buf || !chip->ecc.calc_buf || !chip->ecc.code_buf)
>> +		return -ENOMEM;
> 
> This seems leaky.  Do it like this?
> 
> 	chip->data_buf = kzalloc(mtd->writesize + mtd->oobsize, GFP_KERNEL);
> 	if (!chip->data_buf)
> 		return -ENOMEM;
> 
> 	hip->ecc.calc_buf = kzalloc(mtd->oobsize, GFP_KERNEL);
> 	if (!hip->ecc.calc_buf) {
> 		ret = -ENOMEM;
> 		goto free_data_buf;
> 	}
> 
> 	chip->ecc.code_buf = kzalloc(mtd->oobsize, GFP_KERNEL);
> 	if (!chip->ecc.code_buf) {
> 		ret = -ENOMEM;
> 		goto free_calc_buf;
> 	}
> 
> 	.... // <-- other code
> 
> 	return 0;
> 
> free_calc_buf:
> 	kfree(chip->ecc.calc_buf);
> free_data_buf:
> 	kfree(chip->data_buf);
> 
> 	return ret;
> 
> Is there no detach function?
> 
> void mt7621_nand_dettach()
> {
> 	kfree(chip->ecc.code_buf);
> 	kfree(chip->ecc.calc_buf);
> 	kfree(chip->data_buf);
> }
> 
> regards,
> dan carpenter
> 



More information about the linux-mtd mailing list