From: Robert Jarzmik <robert.jarzmik@free.fr>
To: Mike Dunn <mikedunn@newsguy.com>
Cc: marek.vasut@gmail.com, linux-mtd@lists.infradead.org
Subject: Re: [PATCH] Add driver for M-sys / Sandisk diskonchip G4 nand flash
Date: Wed, 12 Oct 2011 23:28:34 +0200 [thread overview]
Message-ID: <8762jtlx19.fsf@free.fr> (raw)
In-Reply-To: 1318258091-12670-1-git-send-email-mikedunn@newsguy.com
Mike Dunn <mikedunn@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@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
next prev parent reply other threads:[~2011-10-12 21:29 UTC|newest]
Thread overview: 19+ messages / expand[flat|nested] mbox.gz Atom feed top
2011-10-10 14:48 [PATCH] Add driver for M-sys / Sandisk diskonchip G4 nand flash Mike Dunn
2011-10-10 15:51 ` Marek Vasut
2011-10-10 18:12 ` Ivan Djelic
2011-10-10 21:02 ` Mike Dunn
2011-10-11 11:50 ` Ivan Djelic
2011-10-11 19:17 ` Mike Dunn
2011-10-12 18:49 ` Ivan Djelic
2011-10-13 1:18 ` Mike Dunn
2011-10-13 6:58 ` Robert Jarzmik
2011-10-13 8:37 ` Ivan Djelic
2011-10-13 15:52 ` Mike Dunn
2011-10-10 20:20 ` Mike Dunn
2011-10-12 21:28 ` Robert Jarzmik [this message]
2011-10-13 0:26 ` Marek Vasut
2011-10-13 2:25 ` Mike Dunn
2011-10-13 1:53 ` Mike Dunn
2011-10-17 21:45 ` Mike Dunn
2011-10-20 16:31 ` Artem Bityutskiy
2011-10-20 19:57 ` Mike Dunn
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=8762jtlx19.fsf@free.fr \
--to=robert.jarzmik@free.fr \
--cc=linux-mtd@lists.infradead.org \
--cc=marek.vasut@gmail.com \
--cc=mikedunn@newsguy.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.