[PATCH] Add driver for M-sys / Sandisk diskonchip G4 nand flash

Ivan Djelic ivan.djelic at parrot.com
Tue Oct 11 07:50:34 EDT 2011


On Mon, Oct 10, 2011 at 10:02:21PM +0100, Mike Dunn wrote:
> > Still, I do not understand why you go to great lengths computing syndromes in
> > your code, using a mix of generated remainder tables and Galois field tables.
> >
> > Why don't you just reformat the hw-generated polynomial (cheap, only 56 bits
> > to shuffle) and let the BCH library do the whole computation ?
> 
> Could you elaborate?  What are the 56 bits that the hardware gives me?  It's not
> the syndrome, right?  (BTW, I did verify that the results are correct).

After a more careful examination, I believe your hardware gives you
recv_ecc^calc_ecc, where:

* recv_ecc = ecc bytes read from flash =  original_data mod g(X)
* calc_ecc = ecc bytes computed from ram = current_data mod g(X)
* g(X) = generator polynomial for m=14, t=4, primitive=0x4443
* data is 520 bytes

When it reads a page, your hw controller not only computes calc_ecc, but it also
XORs the result with previously flashed value recv_ecc.

Furthermore, the hw generator seems to store this polynomial in its registers
from highest terms to lowest terms, but with reversed bits inside each byte.

In other words, you should be able to use function encode_bch() to reproduce
the same bytes as your hw generator as follows:

static const uint8_t reverse8[256] = {
/* http://graphics.stanford.edu/~seander/bithacks.html#BitReverseTable */
#   define R2(n) n,     n + 2*64,     n + 1*64,     n + 3*64
#   define R4(n) R2(n), R2(n + 2*16), R2(n + 1*16), R2(n + 3*16)
#   define R6(n) R4(n), R4(n + 2*4 ), R4(n + 1*4 ), R4(n + 3*4 )
    R6(0), R6(2), R6(1), R6(3)
};

static void emulate_docg4_hw(struct bch_control *bch, uint8_t *buf,
       		             uint8_t *ecc_flash)
{
	int i;
	uint8_t ecc_buf[8] = {0};

	encode_bch(bch, buf, DOCG4_DATA_LEN, ecc_buf);

	printk("emulated ecc_buf = ");
	for (i = 0; i < 7; i++) {
		ecc_buf[i] = reverse8[ecc_buf[i]] ^ ecc_flash[i];
		printk("%02x", ecc_buf[i]);
	}
}

Note that you must provide ecc_flash bytes (read from oob) to this function.
If you provide ecc_flash[] = {0xff,0xff,0xff,0xff,0xff,0xff,0xff,}, (this
happens when you read a blank page) then the output is cf72fc1ba9c7b9, which is
consistent with your blank_read_hwecc[] array.

If the above assumptions are correct, then you could greatly simplify your code
by implementing docg4_correct_data() with something like this:

static int docg4_correct_data(struct mtd_info *mtd, uint8_t *buf, int page)
{
	struct nand_chip *nand = mtd->priv;
	struct docg4_priv *doc = nand->priv;
	void __iomem *docptr = doc->virtadr;
	uint16_t edc_err;
	int i, numerrs, errpos[5];

	/* hardware quirk: read twice */
	edc_err = readw(docptr + DOCG4_ECC_CONTROL_1);
	edc_err = readw(docptr + DOCG4_ECC_CONTROL_1);

	if (unlikely(debug))
		printk(KERN_DEBUG "docg4_correct_data: "
		       "status = 0x%02x\n", edc_err);

	if (!(edc_err & 0x80)) { /* no error bits */
		writew(0, docptr + DOCG4_END_OF_DATA);
		return 0;
	}

	/* data contains error(s); read the 7 hw-generated ecc bytes */
	docg4_read_hw_ecc(docptr, doc->ecc_buf);

	/* check if ecc bytes are those of a blank page */
	if (!memcmp(doc->ecc_buf, blank_read_hwecc, 7)) {
		doc->page_erased = true;
		writew(0, docptr + DOCG4_END_OF_DATA);
		return 0;	/* blank page; ecc error normal */
	}

	doc->page_erased = false;

	/* ----------------------- MODIFIED PART ------------------------- */
	/* reformat ecc */
	for (i = 0; i < 7; i++)
		doc->ecc_buf[i] = reverse8[doc->ecc_buf[i]];

	/* decode data and find errors */
	numerrs = decode_bch(doc->bch, NULL, DOCG4_DATA_LEN, NULL,
		             doc->ecc_buf, NULL, errorpos);
	if (numerrs == -EBADMSG) {
		printk(KERN_WARNING "docg4: "
		       "uncorrectable errors at offset %08x\n", page * 0x200);
		writew(0, docptr + DOCG4_END_OF_DATA);
		return -1;
	}

	/* fix the errors */
	for (i = 0; i < numerrs; i++) {
	        errorpos[i] = (errorpos[i] & ~7)|(7-(errorpos[i] & 7));
		change_bit(errpos[i], (unsigned long *)buf);
	}
	/* -------------------------------------------------------------- */

	printk(KERN_NOTICE "docg4: %d errors corrected at offset %08x\n",
	       numerrs, page * 0x200);

	writew(0, docptr + DOCG4_END_OF_DATA);
	return numerrs;
}

Is it possible for you to compare your hw generator ecc output with the output
of emulate_docg4_hw() ?

If the above code does not work, I may still find the right permutation
performed by your hw generator on input data. For this I just need a few
samples of hw generated bytes. For instance, hw generated ecc bytes for:
- a page (520 bytes) filled with 0x00, except the first byte set to 0x01
- a page (520 bytes) filled with 0xff, except the first byte set to 0x55

BR,
--

Ivan



More information about the linux-mtd mailing list