[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