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

Robert Jarzmik robert.jarzmik at free.fr
Wed Oct 12 17:28:34 EDT 2011


Mike Dunn <mikedunn at newsguy.com> writes:

> This is a driver for the diskonchip G4 in my Palm Treo680.  I've tested it
> fairly well; it passes the nandtest utility, and I've been able to create a
> ubifs using it.

Hi Mike,

I had a look at your driver, to see how close it was to the docg3 I submitted
before and if we could share some code.

My feeling is that the 2 chips are a bit different, and that the deserve each a
separate driver, until one can manage them both (if that ever is possible). The
discrepencies I noticed are :
 - docg4 doesn't need to write to an "address register" before reading some
 random register (ie. between io+0x1000 and io+0x1800), docg3 needs it
 - docg4 adressing is larger (4 bytes against 3 in docg3)
 - docg4 adressing is different (the calculation 0x108 * page_number), while
 docg3 is more straight forward (0x100 * page)
 - in docg4 driver, I didn't see the "2 pages per block notion". I think it's
 there, but I couldn't find it
 - some read/write sequences are different, with different registers, and with
 additionnal reads in your case (ie. the MYSTERY register for example).

The good part is that I think we share the same registers, although you seem to
have more of them. And I think the BCH algorithm is the same, and I'm really
interested in the outcome of your work with Ivan.

Therefore, I'll help review your driver as much as I can, so that you can merge
it in mainline.

So here we go for the review.

First general points :
 - change hard coded values into "defines" of registers and well known values
   I know you're having a hard time here, and you retro-engineer another driver.
   I'll try to provide clues about what I know of docg3 and transpose it to
   docg4.
 - change all "printk(KERN_DEBUG" into dev_dbg() which is more suitable for a
 driver, or use the event tracing API

I think once you address the comments (or proove me wrong :)), I'll ask you to
release a V2 so that I'll make another pass, as I think I was a bit lazy at the
end of the review ...

> diff --git a/drivers/mtd/nand/docg4.c b/drivers/mtd/nand/docg4.c
> new file mode 100644
...zip...
> +static int docg4_wait(struct mtd_info *mtd, struct nand_chip *nand)
> +{
> +	uint16_t flash_status;
> +	struct docg4_priv *doc = nand->priv;
> +	void __iomem *docptr = doc->virtadr;
> +	unsigned long timeo = jiffies + (HZ * 10);
Does that work with NO_HZ configurations ?
Why not use a loop of mdelay() or msleep() or doc_nops() bellow ?
I would prefer doc nops, as the time of these is controlled by the chip itself,
and thus less dependant on the CPU frequency.
> +
> +	if (unlikely(debug))
> +		printk(KERN_DEBUG "docg4_wait...\n");
dev_dbg().

> +
> +	if (doc->status) {
> +		int stat = doc->status;
> +		doc->status = 0;
> +		return stat;
> +	}
> +
> +	/* hardware quirk of g4 requires reading twice initially */
> +	flash_status = readw(docptr + DOCG4_CONTROL_STATUS);
> +	flash_status = readw(docptr + DOCG4_CONTROL_STATUS);
> +
> +	while (!(flash_status & DOCG4_FLASH_READY)) {
> +		if (time_after(jiffies, timeo)) {
> +			printk(KERN_ERR "docg4: docg4_wait timed out\n");
> +			return NAND_STATUS_FAIL;
> +		}
> +		udelay(1);
> +		cond_resched();
> +		flash_status = readb(docptr + DOCG4_CONTROL_STATUS);
> +	}
This would be a loop, and if you perform too many iterations (ie. 10000
iterations of 1ms), you return NAND_STATUS_FAIL, else 0.
> +
> +	return 0;
> +}
> +
> +static void docg4_reset(struct mtd_info *mtd, struct nand_chip *nand)
> +{
> +	struct docg4_priv *doc = nand->priv;
> +	void __iomem *docptr = doc->virtadr;
> +	u16 dummy;
> +
> +	writew(DOCG4_RESET, docptr + DOCG4_CONTROL);
> +	writew(~DOCG4_RESET, docptr + DOCG4_CONTROL_CONFIRM);
> +	writew(0, docptr + DOCG4_NOP);
> +	writew(DOCG4_NORMAL, docptr + DOCG4_CONTROL);
> +	writew(~DOCG4_NORMAL, docptr + DOCG4_CONTROL_CONFIRM);
> +
> +	/* TODO: this may not be necessary... */
> +	dummy = readw(docptr + DOCG4_CHIP_ID_0);
> +	dummy = readw(docptr + DOCG4_MYSTERY_REG);
> +	writew(0, docptr + DOCG4_NOP);
> +	dummy = readw(docptr + DOCG4_CHIP_ID_0);
> +	dummy = readw(docptr + DOCG4_MYSTERY_REG);
> +	writew(0, docptr + DOCG4_DEV_ID_SELECT);
The DEV_ID_SELECT part is necessary to select the correct floor I think. All the
other reads could be replaced by doc_nops() I think.
And you have docg4_select_chip() already, why not use it ?

> +
> +	/* TODO: enables ecc? Should be done prior to read? */
> +	/* writew(0x07, docptr + DOCG4_ECC_CONTROL_1); */
> +	writew(0x27, docptr + DOCG4_ECC_CONTROL_1); /* what's the difference? */
This is :
  write(DOC_ECCCONF1_PAGE_IS_WRITTEN | 7, DOCG4_ECC_CONTROL_1)
Normally, the DOC_ECCCONF1_PAGE_IS_WRITTEN is a readonly bit, which means that
the previously read page is not blank (has been written since erasure).
I thing the 7 is the number of bytes covered by the hamming code (ie. the 7
first bytes of the oob), but I'm not sure.

> +
> +	docg4_wait(mtd, nand);
> +}
> +
> +static void docg4_read_hw_ecc(void __iomem *docptr, uint8_t *ecc_buf)
> +{
> +	/* read the 7 hw-generated ecc bytes */
> +	int bch_reg, i;
> +	for (i = 0, bch_reg = DOCG4_BCH;  i < 7; bch_reg++, i++) {
> +		ecc_buf[i] = readb(docptr + bch_reg);
> +		ecc_buf[i] = readb(docptr + bch_reg); /* glitch hw */
> +	}
> +}
> +
> +static unsigned int docg4_ecc_mod_phi(uint8_t *ecc_buf, uint16_t *mod_table)
> +{
> +	/*
> +	 * Divide the 56-bit ecc polynomial in ecc_buf by the 14-bit
> +	 * polynomial represented by mod_table, and return remainder.
> +	 *
> +	 * A good reference for this algorithm is the section on cyclic
> +	 * redundancy in the book "Numerical Recipes in C".
> +	 *
> +	 * N.B. The bit order of hw-generated bytes has the LS bit representing
> +	 * the highest order term.  However, byte ordering has most significant
> +	 * byte in ecc_buf[0].
> +	 */
> +
> +	int i = ecc_buf[0];	        /* initial table index */
> +	unsigned int b = mod_table[i];  /* first iteration */
> +
> +	i = (b & 0xff) ^ ecc_buf[1];
> +	b = (b>>8) ^ mod_table[i];
> +	i = (b & 0xff) ^ ecc_buf[2];
> +	b = (b>>8) ^ mod_table[i];
> +	i = (b & 0xff) ^ ecc_buf[3];
> +	b = (b>>8) ^ mod_table[i];
> +	i = (b & 0xff) ^ ecc_buf[4];
> +	b = (b>>8) ^ mod_table[i];
> +
> +	/* last two bytes tricky because divisor width is not multiple of 8 */
> +	b = b ^ (ecc_buf[6]<<8) ^ ecc_buf[5];
> +	i = (b<<6) & 0xc0;
> +	b = (b>>2) ^ mod_table[i];
> +
> +	return b;
> +}
> +
> +static unsigned int docg4_eval_poly(struct docg4_priv *doc, unsigned int poly,
> +				    unsigned int log_gf_elem)
> +{
> +	/*
> +	 * Evaluate poly(alpha ^ log_gf_elem).  Poly is in the bit order used by
> +	 * the ecc hardware (least significant bit is highest order
> +	 * coefficient), but the result is in the opposite bit ordering (that
> +	 * used by the bch alg).  We borrow the bch alg's power table.
> +	 */
> +	unsigned int pow, result = 0;
> +
> +	for (pow = 0; pow < log_gf_elem * 14; pow += log_gf_elem) {
> +		if (poly & 0x2000)
> +			result ^= doc->bch->a_pow_tab[pow];
> +		poly <<= 1;
> +	}
> +	return result;
> +}
> +
> +static unsigned int docg4_square_poly(struct docg4_priv *doc, unsigned int poly)
> +{
> +	/* square the polynomial; e.g., passing alpha^3 returns alpha^6 */
> +
> +	const unsigned int logtimes2 = doc->bch->a_log_tab[poly] * 2;
> +
> +	if (logtimes2 >= doc->bch->n)    /* modulo check */
> +		return doc->bch->a_pow_tab[logtimes2 - doc->bch->n];
> +	else
> +		return doc->bch->a_pow_tab[logtimes2];
> +}
> +
> +static int docg4_find_errbits(struct docg4_priv *doc, unsigned int errorpos[])
> +{
> +	/*
> +	 * Given the 56 hardware-generated ecc bits, determine the locations of
> +	 * the erroneous bits in the page data (and first 8 oob bytes).
> +	 *
> +	 * The BCH syndrome is calculated from the ecc, and the syndrome is
> +	 * passed to the kernel's BCH library, which does the rest.
> +	 *
> +	 * For i in 1..7, each syndrome value S_i is calculated by dividing the
> +	 * ecc polynomial by phi_i (the minimal polynomial of the Galois field
> +	 * element alpha ^ i) and taking the remainder, which is then evaluated
> +	 * with alpha ^ i.
> +	 *
> +	 * The classic text on this is "Error Control Coding" by Lin and
> +	 * Costello (though I'd like to think there are better ones).
> +	 */
> +
> +	int retval, i;
> +	unsigned int b1, b3, b5, b7; /* remainders */
> +	unsigned int s[7];       /* syndromes S_1 .. S_7 (S_8 not needed) */
> +
> +	/* b_i = ecc_polynomial modulo phi_i */
> +	b1 = docg4_ecc_mod_phi(doc->ecc_buf, doc->phi_mod_tables);
> +	b3 = docg4_ecc_mod_phi(doc->ecc_buf, doc->phi_mod_tables + 256);
> +	b5 = docg4_ecc_mod_phi(doc->ecc_buf, doc->phi_mod_tables + 512);
> +	b7 = docg4_ecc_mod_phi(doc->ecc_buf, doc->phi_mod_tables + 768);
> +
> +	/* evaluate remainders with corresponding Galois field elements */
> +	s[0] = docg4_eval_poly(doc, b1, 1);  /* S_1 = b_1(alpha) */
> +	s[2] = docg4_eval_poly(doc, b3, 3);  /* S_3 = b_3(alpha ^ 3) */
> +	s[4] = docg4_eval_poly(doc, b5, 5);  /* S_5 = b_5(alpha ^ 5) */
> +	s[6] = docg4_eval_poly(doc, b7, 7);  /* S_7 = b_7(alpha ^ 7) */
> +
> +	/* S_2, S_4, S_6 obtained by exploiting S_2i = S_i ^ 2 */
> +	s[1] = docg4_square_poly(doc, s[0]); /* S_2 = S_1 ^ 2 */
> +	s[3] = docg4_square_poly(doc, s[1]); /* S_4 = S_2 ^ 2 */
> +	s[5] = docg4_square_poly(doc, s[2]); /* S_6 = S_3 ^ 2 */
> +
> +	/* pass syndrome to BCH algorithm */
> +	retval = decode_bch(doc->bch, NULL, DOCG4_DATA_LEN,
> +			    NULL, NULL, s, errorpos);
> +	if (retval == -EBADMSG)	   /* more than 4 errors */
> +		return 5;
> +
> +	/* undo last step in BCH alg; currently this is a mystery to me */
> +	for (i = 0; i < retval; i++)
> +		errorpos[i] = (errorpos[i] & ~7)|(7-(errorpos[i] & 7));
> +
> +	return retval;
> +}
> +
> +
> +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 */
0x80 = DOC_ECCCONF1_BCH_SYNDROM_ERR here, which means that the hardware ecc
verification has one not null syndrom I think.

> +		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;
> +
> +	numerrs = docg4_find_errbits(doc, errpos);
> +	if (numerrs > 4) {
> +		printk(KERN_WARNING "docg4: "
> +		       "uncorrectable errors at offset %08x\n", page * 0x200);
dev_dbg()
> +		writew(0, docptr + DOCG4_END_OF_DATA);
> +		return -1;
> +	}
> +
> +	/* fix the errors */
> +	for (i = 0; i < numerrs; i++)
> +		change_bit(errpos[i], (unsigned long *)buf);
> +
> +	printk(KERN_NOTICE "docg4: %d errors corrected at offset %08x\n",
> +	       numerrs, page * 0x200);
dev_info() ?
> +
> +	writew(0, docptr + DOCG4_END_OF_DATA);
> +	return numerrs;
> +}
> +
> +
> +static uint8_t docg4_read_byte(struct mtd_info *mtd)
> +{
> +	/*
> +	 * As currently written, the nand code gets chip status by calling
> +	 * cmdfunc() (set to docg4_command()) with the NAND_CMD_STATUS command,
> +	 * then calling read_byte.  This device does not work like standard nand
> +	 * chips, so as a work-around hack, set a flag when the command is
> +	 * received, so that we know to serve up the status here.
> +	 */
> +	struct nand_chip *nand = mtd->priv;
> +	struct docg4_priv *doc = nand->priv;
> +
> +	if (unlikely(debug))
> +		printk(KERN_DEBUG "docg4_read_byte\n");
dev_dbg()

> +
> +	if (doc->status_query == true) {
> +		doc->status_query = false;
> +
> +		/* TODO: return a saved status? read a register? */
> +		return NAND_STATUS_WP; /* why is this inverse logic?? */
> +	}
> +
> +	printk(KERN_WARNING "docg4: unexpectd call to read_byte()\n");
dev_warn()

> +
> +	return 0;
> +}
> +
> +static void docg4_select_chip(struct mtd_info *mtd, int chip)
> +{
> +#if 0
> +	/* TODO: necessary? if so, don't just write 0! */
> +	struct nand_chip *nand = mtd->priv;
> +	struct docg4_priv *doc = nand->priv;
> +	void __iomem *docptr = doc->virtadr;
> +	writew(0, docptr + DOCG4_DEV_ID_SELECT);
This one is necessary, but in the form :
  writew(numFloor, docptr + DOCG4_DEV_ID_SELECT);
You could have a look at docg3.c, function doc_set_device_id().

This is necessary once, to activate the chip, or maybe after a chip full reset.

> +	writew(0x50, docptr + DOCG4_CONTROL_STATUS);
This is :
  writew(DOC_CTRL_CE | 0x40, docptr + DOCG4_CONTROL_STATUS)
Which enables the chip, and for 0x40 I don't know, sorry.

> +#endif
> +	if (unlikely(debug))
> +		printk(KERN_DEBUG "docg4_select_chip\n");
doc_dbg()

> +
> +}
> +
> +static void docg4_write_addr(struct docg4_priv *doc, unsigned int docg4_addr)
> +{
> +	void __iomem *docptr = doc->virtadr;
> +
> +	writeb(docg4_addr & 0xff, docptr + DOCG4_ADDRESS);
> +	docg4_addr >>= 8;
> +	writeb(docg4_addr & 0xff, docptr + DOCG4_ADDRESS);
> +	docg4_addr >>= 8;
> +	writeb(docg4_addr & 0xff, docptr + DOCG4_ADDRESS);
> +	docg4_addr >>= 8;
> +	writeb(docg4_addr & 0xff, docptr + DOCG4_ADDRESS);
> +}
> +
> +static int docg4_read_progstatus(struct docg4_priv *doc)
> +{
> +	/* This apparently checks the status of programming.
> +	 * Called after an erasure, and after page data is written.
> +	 */
> +	void __iomem *docptr = doc->virtadr;
> +
> +	/* status is read from I/O reg */
> +	uint16_t status1 = readw(docptr + DOCG4_IO);
> +	uint16_t status2 = readw(docptr + DOCG4_IO);
> +	uint16_t status3 = readw(docptr + DOCG4_MYSTERY_REG);
> +
> +	/* TODO: better way to determine failure?
> +	   Does CONTROL_STATUS (poll_1038) indicate failure after this?
> +	   If so, can read it from docg4_command(NAND_CMD_STATUS) ? */
> +	if (status1 != 0x51 || status2 != 0xe0 || status3 != 0xe0) {
This is one of the things that I don't understand yet. I'll check my write code
and report later.

> +		doc->status = NAND_STATUS_FAIL;
> +		printk(KERN_WARNING "docg4_read_progstatus failed: "
> +		       "%02x, %02x, %02x\n", status1, status2, status3);
dev_warn()

> +		return -EIO;
> +	}
> +	return 0;
> +}
> +
> +static int docg4_pageprog(struct mtd_info *mtd)
> +{
> +	struct nand_chip *nand = mtd->priv;
> +	struct docg4_priv *doc = nand->priv;
> +	void __iomem *docptr = doc->virtadr;
> +	int retval = 0;
> +
> +	if (unlikely(debug))
> +		printk("docg4_pageprog\n");
> +
> +	writew(0x1e, docptr + DOCG4_SEQUENCE);
> +	writew(0x10, docptr + DOCG4_COMMAND);
This should be the page program sequence and page program command stage 2
command. These are docg4 specific (docg3 are different values).

> +	writew(0, docptr + DOCG4_NOP);
> +	writew(0, docptr + DOCG4_NOP);
> +	docg4_wait(mtd, nand);
> +
> +	writew(0x29, docptr + DOCG4_SEQUENCE);
  -> writew(DOCG4_SEQ_PROG_STATUS, docptr + DOCG4_SEQUENCE)
> +	writew(0x70, docptr + DOCG4_COMMAND);
  -> writew(DOC_CMD_READ_STATUS, docptr + DOCG4_SEQUENCE)

> +	writew(0x8004, docptr + DOCG4_ECC_CONTROL_0);
  -> writew(DOC_ECCCONF0_READ_MODE | 4, docptr + DOCG4_ECC_CONTROL_0)
> +	writew(0, docptr + DOCG4_NOP);
> +	writew(0, docptr + DOCG4_NOP);
> +	writew(0, docptr + DOCG4_NOP);
> +	writew(0, docptr + DOCG4_NOP);
> +	writew(0, docptr + DOCG4_NOP);
These would deserve a doc_delay(doc, 5) or doc_nops(doc, 5), which could be used
everywhere to issue NOP commands to the chip.

> +
> +	retval = docg4_read_progstatus(doc);
> +
> +	writew(0, docptr + DOCG4_END_OF_DATA);
> +	writew(0, docptr + DOCG4_NOP);
> +	docg4_wait(mtd, nand);
> +	writew(0, docptr + DOCG4_NOP);
> +
> +	return retval;
> +}
> +
> +static void docg4_read_page_prologue(struct mtd_info *mtd,
> +				     unsigned int docg4_addr)
> +{
> +	struct nand_chip *nand = mtd->priv;
> +	struct docg4_priv *doc = nand->priv;
> +	void __iomem *docptr = doc->virtadr;
> +
> +	if (unlikely(debug))
> +		printk(KERN_DEBUG "docg4_read_page_prologue: %x\n", docg4_addr);
> +
> +	writew(0x50, docptr + DOCG4_CONTROL_STATUS);

> +	writew(0x00, docptr + DOCG4_SEQUENCE);
> +	writew(0xff, docptr + DOCG4_COMMAND);
> +	writew(0, docptr + DOCG4_NOP);
> +	writew(0, docptr + DOCG4_NOP);
> +	docg4_wait(mtd, nand);
This is the sequence reset, and would deserve a doc_seq_reset() or similar name.
It triggers :
 - a sequence reset
 - and more important, clears SEQUENCE error and PROTECTION error in the control
 register.

> +
> +#if 0
> +	/* TODO: sometimes written here by TrueFFS library */
> +	writew(0x3f, docptr + DOCG4_SEQUENCE);
> +	writew(0xa4, docptr + DOCG4_COMMAND);
> +	writew(0, docptr + DOCG4_NOP);
> +#endif
I don't understand that, but if it is not usefull, I'd recommand to remove it.

> +
> +	writew(0, docptr + DOCG4_NOP);
> +	writew(0x03, docptr + DOCG4_SEQUENCE);
> +	writew(0x00, docptr + DOCG4_COMMAND);
> +	writew(0, docptr + DOCG4_NOP);
> +
> +	docg4_write_addr(doc, docg4_addr);
> +
> +	writew(0, docptr + DOCG4_NOP);
> +	writew(0x30, docptr + DOCG4_COMMAND);
  -> writew(DOC_CMD_READ_ALL_PLANES, docptr + DOCG4_COMMAND)
> +	writew(0, docptr + DOCG4_NOP);
> +	writew(0, docptr + DOCG4_NOP);
> +
> +	docg4_wait(mtd, nand);
> +}
> +
> +static void docg4_write_page_prologue(struct mtd_info *mtd,
> +				      unsigned int docg4_addr)
> +{
> +	struct nand_chip *nand = mtd->priv;
> +	struct docg4_priv *doc = nand->priv;
> +	void __iomem *docptr = doc->virtadr;
> +
> +	if (unlikely(debug))
> +		printk(KERN_DEBUG "docg4_write_page_prologue: %x\n",
> +		       docg4_addr);
dev_dbg()
> +
> +	writew(0x50, docptr + DOCG4_CONTROL_STATUS);

> +	writew(0x00, docptr + DOCG4_SEQUENCE);
> +	writew(0xff, docptr + DOCG4_COMMAND);
> +	writew(0, docptr + DOCG4_NOP);
> +	writew(0, docptr + DOCG4_NOP);
> +	docg4_wait(mtd, nand);
> +	writew(0, docptr + DOCG4_NOP);
The doc_seq_reset() I previously mentionned.

> +
> +#if 0
> +	/* TODO: sometimes written here by TrueFFS library */
> +	writew(0x3f, docptr + DOCG4_SEQUENCE);
> +	writew(0xa4, docptr + DOCG4_COMMAND);
> +	writew(0, docptr + DOCG4_NOP);
> +#endif
As before, I think this should be removed.

> +
> +	writew(0x16, docptr + DOCG4_SEQUENCE);
> +	writew(0x80, docptr + DOCG4_COMMAND);
> +	writew(0, docptr + DOCG4_NOP);
> +
> +	docg4_write_addr(doc, docg4_addr);
> +
> +	writew(0, docptr + DOCG4_NOP);
> +	writew(0, docptr + DOCG4_NOP);
> +
> +	docg4_wait(mtd, nand);
> +
> +}
> +
> +static void docg4_command(struct mtd_info *mtd, unsigned command, int column,
> +			  int page_addr)
> +{
> +	struct nand_chip *nand = mtd->priv;
> +	struct docg4_priv *doc = nand->priv;
> +	unsigned int g4_page, g4_col, g4_addr;
> +
> +	if (unlikely(debug))
> +		printk(KERN_DEBUG "docg4_command %x, page_addr=%x, column=%x\n",
> +		       command, page_addr, column);
> +
> +	switch (command) {
> +
> +	case NAND_CMD_RESET:
> +		docg4_reset(mtd, nand);
> +		break;
> +
> +	case NAND_CMD_READ0:
> +		/* convert page and column to screwy g4 addressing scheme */
> +		g4_page = page_addr / 4;
> +		g4_col = (page_addr % 4) * 0x108 + column/2;
> +		g4_addr = (g4_page << 16) | g4_col;
> +		docg4_read_page_prologue(mtd, g4_addr);
> +		break;
> +
> +	case NAND_CMD_STATUS:
> +		/* next call to read_byte() will expect a status */
> +		doc->status_query = true;
> +		break;
> +
> +	/* we don't expect these, based on review of nand_base.c */
> +	case NAND_CMD_READOOB:
> +	case NAND_CMD_SEQIN:
> +	case NAND_CMD_PAGEPROG:
> +	case NAND_CMD_READID:
> +	case NAND_CMD_ERASE1:
> +	case NAND_CMD_ERASE2:
> +		printk(KERN_WARNING "docg4_command: "
> +		       "unexpected command 0x%x\n", command);
dev_warn()
> +		break;
> +
> +	}
> +}
> +
> +static int docg4_read_page_syndrome(struct mtd_info *mtd,
> +				    struct nand_chip *chip,
> +				    uint8_t *buf, int page)
> +{
> +	struct docg4_priv *doc = chip->priv;
> +	void __iomem *docptr = doc->virtadr;
> +	uint16_t mystery_byte, *buf16;
> +	int bits_corrected;
> +
> +	if (unlikely(debug))
> +		printk(KERN_DEBUG "docg4_read_page_syndrome: page %08x\n",
> +		       page);
> +
> +	/*  TODO: 0x820f, 0xb20f, what's the difference? M512_HAMMING_EN? */
> +	/* writew(0x820f, docptr + DOCG4_ECC_CONTROL_0) */
> +	writew(0xb20f, docptr + DOCG4_ECC_CONTROL_0);
Here I'm lazy with the namings, you will certainly rename these *_LEN things.
I think that is 0x820f
  -> writew(DOC_ECCCONF0_READ_MODE |
            (PAGE_LEN + INFO_LEN + HAMMING_BYTE_LEN + BCH_LEN,
            docptr + DOCG4_ECC_CONTROL_0)
> +	writew(0, docptr + DOCG4_NOP);
> +	writew(0, docptr + DOCG4_NOP);
> +	writew(0, docptr + DOCG4_NOP);
> +	writew(0, docptr + DOCG4_NOP);
> +	writew(0, docptr + DOCG4_NOP);
doc_delay() ...
> +
> +	/*
> +	 * TODO: how to interpret mystery byte? Reads 0x51, 0x73 on error
> +	 * return error if not 0x51?
> +	 */
> +	mystery_byte = readw(docptr + DOCG4_IO);
> +	if (unlikely(debug))
> +		printk(KERN_DEBUG "data read mystery_byte = 0x%x\n",
> +		       mystery_byte);
> +
> +	docg4_read_buf16(mtd, buf, DOCG4_PAGE_SIZE); /* read the page data */
> +
> +	/* Now oob is read.  First 14 bytes read from I/O reg */
> +	chip->read_buf(mtd, chip->oob_poi, 14);
> +
> +	/* last 2 read from another reg */
> +	buf16 = (uint16_t *)(chip->oob_poi + 14);
> +	*buf16 = readw(docptr + DOCG4_MYSTERY_REG);
> +
> +	writew(0, docptr + DOCG4_NOP);
> +
> +	bits_corrected = docg4_correct_data(mtd, buf, page);
> +	if (bits_corrected < 0)
> +		mtd->ecc_stats.failed++;
> +	else
> +		mtd->ecc_stats.corrected += bits_corrected;
> +
> +	return 0;	/* always success; same as default fn in nand_base */
> +}
> +
> +static int docg4_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
> +				   int page, int sndcmd)
> +{
> +	struct docg4_priv *doc = chip->priv;
> +	void __iomem *docptr = doc->virtadr;
> +	uint16_t mystery_byte;
> +
> +	if (unlikely(debug))
> +		printk(KERN_DEBUG "docg4_read_oob_syndrome: page %x\n", page);
dev_dbg()
> +
> +	chip->cmdfunc(mtd, NAND_CMD_READ0, chip->ecc.size, page);
> +
> +	writew(0x8010, docptr + DOCG4_ECC_CONTROL_0);
Here I'm lazy with the namings, you will certainly rename these *_LEN things.
  -> writew(DOC_ECCCONF0_READ_MODE |
            (INFO_LEN + HAMMING_BYTE_LEN + BCH_LEN + UNUSED_BYTE_len)
            docptr + DOCG4_ECC_CONTROL_0)

> +	writew(0, docptr + DOCG4_NOP);
> +	writew(0, docptr + DOCG4_NOP);
> +	writew(0, docptr + DOCG4_NOP);
> +	writew(0, docptr + DOCG4_NOP);
> +	writew(0, docptr + DOCG4_NOP);
doc_delay() or doc_nops()
> +
> +	/*
> +	 * TODO: how to interpret mystery byte?
> +	 * Reads 0x51 for oob data read only
> +	 */
> +	mystery_byte = readw(docptr + DOCG4_IO);
> +	if (unlikely(debug))
> +		printk(KERN_DEBUG "oob read mystery_byte = 0x%x\n",
> +		       mystery_byte);
> +
> +	chip->read_buf(mtd, chip->oob_poi, 16);
> +
> +	writew(0, docptr + DOCG4_NOP);
> +	writew(0, docptr + DOCG4_NOP);
> +	writew(0, docptr + DOCG4_NOP);
doc_delay() or doc_nops()
> +	writew(0, docptr + DOCG4_END_OF_DATA);
> +	writew(0, docptr + DOCG4_NOP);
> +
> +	return 0;
> +}
> +
> +#ifdef CONFIG_MTD_NAND_VERIFY_WRITE
> +/*
> + * Read back a written or erased page and verify correct contents.
> + * 'page' arg uses external addressing model; i.e., 512 bytes per page.
> + * nand_base now does this for writes; we only use this to verify erasure.
> + */
> +static int docg4_verify(struct mtd_info *mtd, int page, const uint8_t *buf)
> +{
> +	struct nand_chip *chip = mtd->priv;
> +	struct docg4_priv *doc = chip->priv;
> +	uint8_t oob_buf[16];
> +
> +	/* save the oob bytes; will be overwritten during read */
> +	memcpy(oob_buf, chip->oob_poi, 16);
> +
> +	/* read the page data into doc->verify_buf */
> +	docg4_command(mtd, NAND_CMD_READ0, 0, page);
> +	docg4_read_page_syndrome(mtd, chip, doc->verify_buf, page);
> +
> +	/* NULL buf indicates verify erasure; check flag set during read */
> +	if (buf == NULL) {
> +		if (doc->page_erased == false) {
> +			printk(KERN_WARNING
> +			       "docg4: page 0x%x erase verify failed\n", page);
dev_warn()
> +			return -1;
> +		}
> +		return 0;	/* erasure verified */
> +	}
> +
> +	/* not erasure... compare page data buffers */
> +	if (memcmp(doc->verify_buf, buf, DOCG4_PAGE_SIZE)) {
> +		printk(KERN_WARNING
> +		       "docg4: page 0x%x write verify failed\n", page);
> +		return -1;
> +	}
> +
> +	/* compare oob data buffers, excluding hw generated ecc bytes */
> +	if (memcmp(oob_buf, chip->oob_poi, 7) ||
> +	    oob_buf[15] != chip->oob_poi[15]) {
> +		printk(KERN_WARNING
> +		       "docg4: page 0x%x oob write verify failed\n", page);
dev_warn()
> +		return -1;
> +	}
> +
> +	return 0;		/* write verified */
> +}
> +#endif
> +
> +static void docg4_erase_block(struct mtd_info *mtd, int page)
> +{
> +	struct nand_chip *nand = mtd->priv;
> +	struct docg4_priv *doc = nand->priv;
> +	void __iomem *docptr = doc->virtadr;
> +	uint16_t g4_page;
> +
> +	if (unlikely(debug))
> +		printk("docg4_erase_block: page %04x\n", page);
> +
> +	/*
> +	 * TODO: this is exactly the same as start of docg4_read_page_prologue()
> +	 * if extra 3a > 1032, a3 > 1034 are inserted
> +	 */
> +	writew(0x50, docptr + DOCG4_CONTROL_STATUS);

> +	writew(0x00, docptr + DOCG4_SEQUENCE);
> +	writew(0xff, docptr + DOCG4_COMMAND);
> +	writew(0, docptr + DOCG4_NOP);
> +	writew(0, docptr + DOCG4_NOP);
> +	docg4_wait(mtd, nand);
> +	writew(0, docptr + DOCG4_NOP);
doc_seq_reset()

> +	writew(0x3a, docptr + DOCG4_SEQUENCE);
> +	writew(0xa3, docptr + DOCG4_COMMAND);
> +	writew(0, docptr + DOCG4_NOP);

> +	writew(0x24, docptr + DOCG4_SEQUENCE);
> +	writew(0x60, docptr + DOCG4_COMMAND);
> +	writew(0, docptr + DOCG4_NOP);
> +
> +	/* only 2 bytes of address are written to specify erase block */
> +	g4_page = (uint16_t)(page / 4);  /* to g4's 2k page addressing */
> +	writeb(g4_page & 0xff, docptr + DOCG4_ADDRESS);
> +	g4_page >>= 8;
> +	writeb(g4_page & 0xff, docptr + DOCG4_ADDRESS);
> +
> +	/* start the erasure */
> +	writew(0, docptr + DOCG4_NOP);
> +	writew(0xd0, docptr + DOCG4_COMMAND);
> +	writew(0, docptr + DOCG4_NOP);
> +	writew(0, docptr + DOCG4_NOP);
> +	docg4_wait(mtd, nand);	/* long wait for erasure */
> +
> +	writew(0x29, docptr + DOCG4_SEQUENCE);
> +	writew(0x70, docptr + DOCG4_COMMAND);
> +	writew(0x8004, docptr + DOCG4_ECC_CONTROL_0);
Same comment as previously.

> +	writew(0, docptr + DOCG4_NOP);
> +	writew(0, docptr + DOCG4_NOP);
> +	writew(0, docptr + DOCG4_NOP);
> +	writew(0, docptr + DOCG4_NOP);
> +	writew(0, docptr + DOCG4_NOP);
doc_delay() ... doc_nops()
> +
> +	docg4_read_progstatus(doc);
> +
> +	writew(0, docptr + DOCG4_END_OF_DATA);
> +	writew(0, docptr + DOCG4_NOP);
> +	docg4_wait(mtd, nand);
> +	writew(0, docptr + DOCG4_NOP);
> +
> +#ifdef CONFIG_MTD_NAND_VERIFY_WRITE
> +	{
> +		int i;
> +		/*
> +		 * Check kernel log for erase verification failures.  Note that
> +		 * occasional failures are reported, but a subsequent dump of
> +		 * the page always shows that it is correctly erased.  Might be
> +		 * a timing issue.
> +		 */
> +		for (i = 0; i < DOCG4_PAGES_PER_BLOCK; i++, page++)
> +			docg4_verify(mtd, page, NULL);
> +	}
> +#endif
> +}
> +
> +static void docg4_write_page_syndrome(struct mtd_info *mtd,
> +				      struct nand_chip *chip,
> +				      const uint8_t *buf)
> +{
> +	struct docg4_priv *doc = chip->priv;
> +	void __iomem *docptr = doc->virtadr;
> +	uint8_t hamming;
> +	uint8_t ecc_buf[8];
> +
> +	if (unlikely(debug))
> +		printk(KERN_DEBUG "docg4_write_page_syndrome...\n");
doc_dbg()
> +
> +	writew(0x320f, docptr + DOCG4_ECC_CONTROL_0);
Here I'm lazy with the namings, you will certainly rename these *_LEN things.
  -> writew(0x2000 | DOC_ECCCONF0_HAMMING_ENABLE |
            (PAGE_LEN + INFO_LEN + HAMMING_BYTE_LEN + BCH_LEN,
            docptr + DOCG4_ECC_CONTROL)
> +	writew(0, docptr + DOCG4_NOP);
> +
> +	/* write the page data */
> +	chip->write_buf(mtd, buf, DOCG4_PAGE_SIZE);
> +
> +	/* oob bytes 0 through 5 are written to I/O reg */
> +	chip->write_buf(mtd, chip->oob_poi, 6);
> +
> +	/* oob byte 6 written to a separate reg */
> +	writew(chip->oob_poi[6], docptr + DOCG4_MYSTERY_REG_2);
> +
> +	writew(0, docptr + DOCG4_NOP);
> +	writew(0, docptr + DOCG4_NOP);
> +
> +	/* oob byte 7 is hamming code */
> +	hamming = readb(docptr + DOCG4_HAMMING);
> +	hamming = readb(docptr + DOCG4_HAMMING); /* gotta read twice */
> +	writew(hamming, docptr + DOCG4_MYSTERY_REG_2);
> +	writew(0, docptr + DOCG4_NOP);
> +
> +	/* read the 7 bytes from ecc regs and write to next oob area */
> +	docg4_read_hw_ecc(docptr, ecc_buf);
> +	ecc_buf[7] = chip->oob_poi[15]; /* last byte user-programmed */
> +	chip->write_buf(mtd, ecc_buf, 8);
> +
> +	writew(0, docptr + DOCG4_NOP);
> +	writew(0, docptr + DOCG4_NOP);
> +	writew(0, docptr + DOCG4_END_OF_DATA);
> +	writew(0, docptr + DOCG4_NOP);
> +}
> +
> +static int docg4_write_page(struct mtd_info *mtd, struct nand_chip *chip,
> +			    const uint8_t *buf, int page, int cached, int raw)
> +{
> +	int status;
> +	struct docg4_priv *doc = chip->priv;
> +	uint32_t g4_page = page / 4;
> +	uint32_t g4_index = (page % 4) * 0x108;
> +	uint32_t g4_addr = (g4_page << 16) | g4_index;
> +
> +	docg4_write_page_prologue(mtd, g4_addr);
> +
> +	/* hack for deferred write of oob bytes */
> +	if (doc->oob_page == page)
> +		memcpy(chip->oob_poi, doc->oob_buf, 16);
> +
> +	if (unlikely(raw)) {
> +		printk(KERN_WARNING "docg4: unsupported raw write operation\n");
doc_warn()
> +		return -EOPNOTSUPP; /* TODO: support "raw" writes? */
> +	} else
> +		docg4_write_page_syndrome(mtd, chip, buf);
> +
> +	status = docg4_pageprog(mtd);
> +	if (status) {
> +		printk(KERN_WARNING "docg4: docg4_write_page failed\n");
doc_warn()
> +		return -EIO;
> +	}
> +
> +	return 0;
> +}
> +
> +static int docg4_write_oob_syndrome(struct mtd_info *mtd,
> +				    struct nand_chip *chip, int page)
> +{
> +	/*
> +	 * This is not really supported, because MLC nand must write oob bytes
> +	 * at the same time as page data.  Nonetheless, we save the oob buffer
> +	 * contents here, and then write it along with the page data if the same
> +	 * page is subsequently written.  This allows user space utilities
> +	 * (e.g., nandwrite) that write the oob data prior to the page data to
> +	 * work.
> +	 */
> +
> +	/* note that bytes 7..14 are hw generated hamming/ecc and overwritten */
> +	struct docg4_priv *doc = chip->priv;
> +	doc->oob_page = page;
> +	memcpy(doc->oob_buf, chip->oob_poi, 16);
> +	return 0;
> +}
> +
> +static int docg4_block_markbad(struct mtd_info *mtd, loff_t ofs)
> +{
> +	struct nand_chip *chip = mtd->priv;
> +	int block, page, ret, i;
> +	uint8_t *buf;
> +	struct nand_bbt_descr *bbtd = chip->badblock_pattern;
> +
> +	if (unlikely(debug))
> +		printk(KERN_DEBUG "docg4_block_markbad: %08llx\n", ofs);
doc_dbg()
> +
> +	buf = kzalloc(DOCG4_PAGE_SIZE, GFP_KERNEL);
> +	if (buf == NULL)
> +		return -ENOMEM;
> +
> +	/* Get block and page numbers */
> +	block = (int)(ofs >> chip->bbt_erase_shift);
> +	page = (int)(ofs >> chip->page_shift);
> +
> +	/* update bbt in memory */
> +	if (chip->bbt)
> +		chip->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1);
This formula looks a bit obfuscated to me. Maybe a comment would enlighten me
here.

> +
> +	/* write bit-wise negation of pattern to oob buffer */
> +	memset(chip->oob_poi, 0xff, mtd->oobsize);
> +	for (i = 0; i < bbtd->len; i++)
> +		chip->oob_poi[bbtd->offs + i] = ~bbtd->pattern[i];
> +
> +	/* write first 2 pages of block */
> +	ret = docg4_write_page(mtd, chip, buf, page, 0, 0);
> +	ret = docg4_write_page(mtd, chip, buf, page + 1, 0, 0);
> +
> +	if (!ret)
> +		mtd->ecc_stats.badblocks++;
> +
> +	kfree(buf);
> +
> +	return ret;
> +}
> +
> +static int docg4_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
> +{
> +	int i;
> +	struct nand_chip *chip = mtd->priv;
> +	int page = (int)(ofs >> chip->page_shift) & chip->pagemask;
> +	struct nand_bbt_descr *bbtd = chip->badblock_pattern;
> +
> +	if (unlikely(debug))
> +		printk(KERN_DEBUG "docg4_block_bad: %08llx\n", ofs);
dev_dbg()
> +
> +	if (ignore_badblocks)
> +		return 0;
> +
> +	docg4_read_oob_syndrome(mtd, chip, page, 0);
> +
> +	for (i = 0; i < bbtd->len; i++) {
> +		if (chip->oob_poi[bbtd->offs + i] != bbtd->pattern[i])
> +			return 1;
> +	}
> +
> +	return 0;
> +}
> +
> +static void __init docg4_build_mod_tables(uint16_t *tables)
> +{
> +	/*
> +	 * Build tables for fast modulo division of the hardware-generated 56
> +	 * bit ecc polynomial by the minimal polynomials of the Galois field
> +	 * elements alpha, alpha^3, alpha^5, alpha^7.
> +	 *
> +	 * A good reference for this algorithm is the section on cyclic
> +	 * redundancy in the book "Numerical Recipes in C".
> +	 *
> +	 * N.B. The bit ordering of the table entries has the LS bit
> +	 * representing the highest order coefficient, consistent with the
> +	 * ordering used by the hardware ecc generator.
> +	 */
> +
> +	/* minimal polynomials, with highest order term (LS bit) removed */
> +	const uint16_t phi_1 = 0x3088;
> +	const uint16_t phi_3 = 0x39a0;
> +	const uint16_t phi_5 = 0x36d8;
> +	const uint16_t phi_7 = 0x23f2;
> +
> +	/* one table of 256 elements for each minimal polynomial */
> +	uint16_t *const phi_1_tab = tables;
> +	uint16_t *const phi_3_tab = tables + 256;
> +	uint16_t *const phi_5_tab = tables + 512;
> +	uint16_t *const phi_7_tab = tables + 768;
> +
> +	int i, j;
> +	for (i = 0; i < 256; i++) {
> +		phi_1_tab[i] = (uint16_t)i;
> +		phi_3_tab[i] = (uint16_t)i;
> +		phi_5_tab[i] = (uint16_t)i;
> +		phi_7_tab[i] = (uint16_t)i;
> +		for (j = 0; j < 8; j++) {
> +			if (phi_1_tab[i] & 0x01)
> +				phi_1_tab[i] = (phi_1_tab[i] >> 1) ^ phi_1;
> +			else
> +				phi_1_tab[i] >>= 1;
> +			if (phi_3_tab[i] & 0x01)
> +				phi_3_tab[i] = (phi_3_tab[i] >> 1) ^ phi_3;
> +			else
> +				phi_3_tab[i] >>= 1;
> +			if (phi_5_tab[i] & 0x01)
> +				phi_5_tab[i] = (phi_5_tab[i] >> 1) ^ phi_5;
> +			else
> +				phi_5_tab[i] >>= 1;
> +			if (phi_7_tab[i] & 0x01)
> +				phi_7_tab[i] = (phi_7_tab[i] >> 1) ^ phi_7;
> +			else
> +				phi_7_tab[i] >>= 1;
> +		}
> +	}
> +}
> +
> +static void docg4_dummy_ecc_write_page(struct mtd_info *mtd,
> +				       struct nand_chip *chip,
> +				       const uint8_t *buf)
> +{
> +	/*
> +	 * nand_base.c says chip->write_page() is replaceable, and we replace it
> +	 * with docg4_write_page(), which obviates the need to define
> +	 * chip->ecc.write_page().  But nand_base.c throws a BUG() if this is
> +	 * not defined when chip->ecc.mode == NAND_ECC_HW_SYNDROME.
> +	 * TODO: patch nand_base.c, or see if nand_write_page() (which calls
> +	 * chip->ecc.write_page()) can be used by this code, or lie about mode.
> +	 */
> +	printk(KERN_WARNING "docg4_dummy_ecc_write_page called\n");
> +	BUG();
> +}
> +
> +static void __init docg4_init_datastructs(struct mtd_info *mtd)
> +{
> +	/*
> +	 * Most of the nand functions must be implemented locally.  This is
> +	 * because we skip the call to nand_scan_ident(), which contains the
> +	 * call to nand_set_defaults().  Most of the default fns are not
> +	 * exported from nand_base.c, so we can't assign them from here.  But
> +	 * most need to be overridden anyway; only a few short functions (e.g.,
> +	 * read/write_buf) are duplicated.  For the same reason, some code from
> +	 * nand_set_defaults() is duplicated below.  The call to
> +	 * nand_scan_ident() is skipped because on diskonchip the chip id is not
> +	 * read the same as on a standard nand device.
> +	 */
> +	struct nand_chip *nand = mtd->priv;
> +	struct docg4_priv *doc = nand->priv;
> +
> +	mtd->size = DOCG4_CHIPSIZE;
> +	mtd->writesize = DOCG4_PAGE_SIZE;
> +	mtd->erasesize = DOCG4_BLOCK_SIZE;
> +	mtd->oobsize = DOCG4_OOB_SIZE;
> +	mtd->name = "Msys Diskonchip G4";
Funny, as MSystems was swallowed by Sandisk :)

> +
> +	nand->page_shift = 9;
> +	nand->pagemask = 0x3ffff;
> +	nand->chip_shift = 27;
> +	nand->badblockpos = NAND_SMALL_BADBLOCK_POS;
> +	nand->chipsize = DOCG4_CHIPSIZE;
> +	nand->chip_shift = ffs((unsigned)nand->chipsize) - 1;
> +	nand->bbt_erase_shift = nand->phys_erase_shift =
> +		ffs(mtd->erasesize) - 1;
> +	nand->chip_delay = 20;
> +
> +	nand->cmdfunc = docg4_command;
> +	nand->waitfunc = docg4_wait;
> +	nand->select_chip = docg4_select_chip;
> +	nand->read_byte = docg4_read_byte;
> +	nand->block_bad	= docg4_block_bad;
> +	nand->block_markbad = docg4_block_markbad;
> +	nand->read_buf = docg4_read_buf16;
> +	nand->write_buf = docg4_write_buf16;
> +	nand->scan_bbt = nand_default_bbt;
> +	nand->erase_cmd = docg4_erase_block;
> +	nand->ecc.read_page = docg4_read_page_syndrome;
> +	nand->ecc.read_oob = docg4_read_oob_syndrome;
> +	nand->write_page = docg4_write_page;
> +	nand->ecc.write_oob = docg4_write_oob_syndrome;
> +
> +	/* raw reads don't make sense on this device; hw ecc always on */
> +	nand->ecc.read_page_raw = docg4_read_page_syndrome;
> +
> +	/* see comment in this function */
> +	nand->ecc.write_page = docg4_dummy_ecc_write_page;
> +
> +	nand->ecc.layout = &docg4_oobinfo;
> +	nand->ecc.mode = NAND_ECC_HW_SYNDROME;
> +	nand->ecc.size = DOCG4_PAGE_SIZE;
> +	nand->ecc.prepad = 8;
> +	nand->ecc.bytes	= 8;
> +
> +	if (ignore_badblocks)
> +		nand->options = NAND_BUSWIDTH_16 | NAND_NO_SUBPAGE_WRITE |
> +			NAND_NO_AUTOINCR | NAND_SKIP_BBTSCAN;
> +	else
> +		nand->options = NAND_BUSWIDTH_16 | NAND_NO_SUBPAGE_WRITE |
> +			NAND_NO_AUTOINCR;
> +
> +	nand->IO_ADDR_R = nand->IO_ADDR_W = doc->virtadr + DOCG4_IO;
> +
> +	nand->controller = &nand->hwcontrol;
> +	spin_lock_init(&nand->controller->lock);
> +	init_waitqueue_head(&nand->controller->wq);
> +}
> +
> +static int __init docg4_read_id_reg(struct mtd_info *mtd,
> +				    struct nand_chip *nand)
> +{
> +	struct docg4_priv *doc = nand->priv;
> +	void __iomem *docptr = doc->virtadr;
> +	uint16_t id1, id2;
> +
> +	/* check for presence of g4 chip by reading id registers */
> +	id1 = readw(docptr + DOCG4_CHIP_ID_0);
> +	id1 = readw(docptr + DOCG4_MYSTERY_REG);
> +	id2 = readw(docptr + DOCG4_CHIP_ID_1);
> +	id2 = readw(docptr + DOCG4_MYSTERY_REG);
> +
> +	if (id1 == DOCG4_IDREG1_VALUE && id2 == DOCG4_IDREG2_VALUE) {
> +		printk(KERN_INFO "NAND device: 128MiB Diskonchip G4
> detected\n");
dev_info()
> +		return 0;
> +	}
> +
> +	printk(KERN_WARNING "No diskonchip G4 device found.\n");
dev_warn()
> +	return -ENODEV;
> +}
> +
> +static int __init docg4_probe(struct platform_device *pdev)
> +{
> +	struct mtd_info *mtd;
> +	struct nand_chip *nand;
> +	void __iomem *virtadr;
> +	struct docg4_priv *doc;
> +	int len, retval;
> +	struct resource *r;
> +	struct device *dev = &pdev->dev;
> +	const struct docg4_nand_platform_data *pdata = dev->platform_data;
> +
> +	if (pdata == NULL) {
> +		dev_err(&pdev->dev, "no platform data!\n");
> +		return -EINVAL;
> +	}
> +
> +	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (r == NULL) {
> +		dev_err(&pdev->dev, "no io memory resource defined!\n");
> +		return -ENODEV;
> +	}
> +
> +	virtadr = ioremap(r->start, resource_size(r));
> +	if (!virtadr) {
> +		printk(KERN_ERR "Diskonchip ioremap failed: "
> +		       "0x%x bytes at 0x%x\n",
> +		       resource_size(r), r->start);
> +		return -EIO;
> +	}
> +
> +	len = sizeof(struct mtd_info) + sizeof(struct nand_chip) +
> +		sizeof(struct docg4_priv);
> +	mtd = kzalloc(len, GFP_KERNEL);
> +	if (mtd == NULL) {
> +		retval = -ENOMEM;
> +		goto fail;
> +	}
> +	nand = (struct nand_chip *) (mtd + 1);
> +	doc = (struct docg4_priv *) (nand + 1);
> +	mtd->priv = nand;
> +	nand->priv = doc;
> +	mtd->owner = THIS_MODULE;
> +	doc->virtadr = virtadr;
> +
> +	docg4_init_datastructs(mtd);
> +
> +	/* allocate and initialize the modulo tables */
> +	doc->phi_mod_tables =
> +		kzalloc(256*4*sizeof(*doc->phi_mod_tables), GFP_KERNEL);
> +	if (doc->phi_mod_tables == NULL) {
> +		retval = -ENOMEM;
> +		goto fail;
> +	}
> +	docg4_build_mod_tables(doc->phi_mod_tables);
> +
> +	/* initialize kernel bch algorithm */
> +	doc->bch = init_bch(DOCG4_M, DOCG4_T, DOCG4_PRIMITIVE_POLY);
> +	if (doc->bch == NULL) {
> +		retval = -EINVAL;
> +		goto fail;
> +	}
> +
> +	platform_set_drvdata(pdev, doc);
> +
> +	docg4_reset(mtd, nand);
> +	retval = docg4_read_id_reg(mtd, nand);
> +	if (retval)
> +		goto fail;
> +
> +	retval = nand_scan_tail(mtd);
> +	if (retval)
> +		goto fail;
> +
> +	retval = mtd_device_register(mtd, NULL, 0);
> +	if (retval)
> +		goto fail;
> +
> +	if (pdata->nr_partitions > 0) {
> +		int i;
> +		for (i = 0; i < pdata->nr_partitions; i++)
> +			pdata->partitions[i].ecclayout = &docg4_oobinfo;
> +		retval = mtd_device_register(mtd, pdata->partitions,
> +					     pdata->nr_partitions);
> +	}
> +	if (retval)
> +		goto fail;
> +
> +#ifdef CONFIG_MTD_NAND_VERIFY_WRITE
> +	doc->verify_buf = kmalloc(DOCG4_PAGE_SIZE, GFP_KERNEL);
> +	if (!doc->verify_buf) {
> +		printk(KERN_ERR "docg4: kmalloc (%d bytes) failed!\n",
> +		       DOCG4_PAGE_SIZE);
> +		retval = -ENOMEM;
> +		goto fail;
> +	}
> +	printk(KERN_INFO "docg4: MTD_NAND_VERIFY_WRITE enabled\n");
> +#endif
> +
> +	doc->mtd = mtd;
> +	return 0;
> +
> + fail:
> +	iounmap(virtadr);
> +	if (mtd) {
> +		/* re-declarations avoid compiler warning */
> +		struct nand_chip *nand = mtd->priv;
> +		struct docg4_priv *doc = nand->priv;
> +		kfree(doc->phi_mod_tables);
> +		kfree(doc->verify_buf);
> +		nand_release(mtd); /* deletes partitions and mtd devices */
> +		platform_set_drvdata(pdev, NULL);
> +		kfree(mtd);
> +	}
> +
> +	return retval;
> +}
> +
> +static int __exit cleanup_docg4(struct platform_device *pdev)
> +{
> +	struct docg4_priv *doc = platform_get_drvdata(pdev);
> +
> +#ifdef CONFIG_MTD_NAND_VERIFY_WRITE
> +	kfree(doc->verify_buf);
> +#endif
> +	nand_release(doc->mtd);
> +	iounmap(doc->virtadr);
> +	platform_set_drvdata(pdev, NULL);
> +	kfree(doc->phi_mod_tables);
> +	kfree(doc);
> +	return 0;
> +}
> +
> +static struct platform_driver docg4_driver = {
> +	.driver		= {
> +		.name	= "docg4",
> +		.owner	= THIS_MODULE,
> +	},
> +	.remove		= __exit_p(cleanup_docg4),
> +};
> +
> +static int __init docg4_init(void)
> +{
> +	return platform_driver_probe(&docg4_driver, docg4_probe);
> +}
> +
> +static void __exit docg4_exit(void)
> +{
> +	platform_driver_unregister(&docg4_driver);
> +}
> +
> +module_init(docg4_init);
> +module_exit(docg4_exit);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Mike Dunn");
> +MODULE_DESCRIPTION("M-Systems DiskOnChip G4 device driver");
> diff --git a/include/linux/mtd/docg4.h b/include/linux/mtd/docg4.h
> new file mode 100644
> index 0000000..654699c
> --- /dev/null
> +++ b/include/linux/mtd/docg4.h
> @@ -0,0 +1,24 @@
> +/*
> + *  Copyright (C) 2011 Mike Dunn <mikedunn at newsguy.com>
> + *
> + * Nand mtd driver for M-Systems DiskOnChip G4 device
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
> + */
> +
> +struct docg4_nand_platform_data {
> +	struct mtd_partition *partitions;
> +	unsigned int nr_partitions;
> +};

Cheers.


-- 
Robert



More information about the linux-mtd mailing list