From: Marek Vasut <marek.vasut@gmail.com>
To: Mike Dunn <mikedunn@newsguy.com>
Cc: robert.jarzmik@free.fr, linux-mtd@lists.infradead.org
Subject: Re: [PATCH] Add driver for M-sys / Sandisk diskonchip G4 nand flash
Date: Mon, 10 Oct 2011 17:51:18 +0200 [thread overview]
Message-ID: <201110101751.19010.marek.vasut@gmail.com> (raw)
In-Reply-To: <1318258091-12670-1-git-send-email-mikedunn@newsguy.com>
On Monday, October 10, 2011 04:48:10 PM Mike Dunn wrote:
> 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,
so the time to rip you to shreds in the mailing list has come <evil laughter>
...
[...]
> diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
> index 4c34252..726ce63 100644
> --- a/drivers/mtd/nand/Kconfig
> +++ b/drivers/mtd/nand/Kconfig
> @@ -319,6 +319,14 @@ config MTD_NAND_DISKONCHIP_BBTWRITE
> load time (assuming you build diskonchip as a module) with the module
> parameter "inftl_bbt_write=1".
>
> +config MTD_NAND_DOCG4
> + tristate "Support for NAND DOCG4"
> + depends on EXPERIMENTAL && ARCH_PXA
It's not PXA specific driver I guess ?
> + select BCH
> + help
> + Support for diskonchip G4 nand flash, found in several smartphones,
> + such as the Palm Treo680 and HTC Prophet.
> +
> config MTD_NAND_SHARPSL
> tristate "Support for NAND Flash on Sharp SL Series (C7xx + others)"
> depends on ARCH_PXA
[...]
> +static struct nand_ecclayout docg4_oobinfo = {
> + .eccbytes = 8,
> + .eccpos = {7, 8, 9, 10, 11, 12, 13, 14},
> + .oobfree = {{0, 7}, {15, 1} }
> +};
> +
> +/* next two functions copied from nand_base.c verbatem... */
Why ?
> +static void docg4_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len)
> +{
> + int i;
> + struct nand_chip *chip = mtd->priv;
> + u16 *p = (u16 *) buf;
> + len >>= 1;
> +
> + for (i = 0; i < len; i++)
> + p[i] = readw(chip->IO_ADDR_R);
> +}
> +
> +static void docg4_write_buf16(struct mtd_info *mtd, const uint8_t *buf,
> int len) +{
> + int i;
> + struct nand_chip *chip = mtd->priv;
> + u16 *p = (u16 *) buf;
> + len >>= 1;
> +
> + for (i = 0; i < len; i++)
> + writew(p[i], chip->IO_ADDR_W);
> +}
Defaults are no good ?
> +
> +
> +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;
Why not make a doc_read() and doc_write() inline function to handle this ?
doc_write(docg4_priv, val, reg); ?
doc_read(docg4, reg); ?
You can even handle the register offset by doing this ...
[...]
> +
> +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 guess this has checkpatch issues ? ./scripts/checkpatch.pl <patch> ... or
checkpatch -f file
Please fix all issues.
> + 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)
Why the magic constant ?
> + 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 */
Make these sentences (starting with capital letter, ending with dot) ... please
fix globally.
> + 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 */
> + writew(0, docptr + DOCG4_END_OF_DATA);
> + return 0;
> + }
No magic numbers please ... please fix globally.
> +
> + /* 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);
> + 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);
> +
> + writew(0, docptr + DOCG4_END_OF_DATA);
> + return numerrs;
> +}
> +
> +
Single newline is ok, please fix globally.
> +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");
> +
> + 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");
> +
> + 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);
> + writew(0x50, docptr + DOCG4_CONTROL_STATUS);
> +#endif
Drop dead code.
> + if (unlikely(debug))
> + printk(KERN_DEBUG "docg4_select_chip\n");
> +
> +}
> +
> +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);
Make this a loop ?
> +}
> +
> +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) ? */
Fix comment, checkpatch will warn about this, please fix globally.
> + if (status1 != 0x51 || status2 != 0xe0 || status3 != 0xe0) {
Remove magic values, please fix globally.
> + doc->status = NAND_STATUS_FAIL;
> + printk(KERN_WARNING "docg4_read_progstatus failed: "
> + "%02x, %02x, %02x\n", status1, status2, status3);
> + 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);
> + writew(0, docptr + DOCG4_NOP);
> + writew(0, docptr + DOCG4_NOP);
> + docg4_wait(mtd, nand);
> +
> + writew(0x29, docptr + DOCG4_SEQUENCE);
> + writew(0x70, docptr + DOCG4_COMMAND);
> + writew(0x8004, 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);
> +
> + 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);
> +
> +#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
Dead code, magic values.
> +
> + 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(0, docptr + DOCG4_NOP);
> + writew(0, docptr + DOCG4_NOP);
> +
> + docg4_wait(mtd, nand);
> +}
> +
[...]
> +
> +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;
Don't define variables in the middle of code. Also, why the casts below ? Also,
can't this be static data ?
> + 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;
> + }
> + }
> +}
> +
Cheers
next prev parent reply other threads:[~2011-10-10 15:51 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 [this message]
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
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=201110101751.19010.marek.vasut@gmail.com \
--to=marek.vasut@gmail.com \
--cc=linux-mtd@lists.infradead.org \
--cc=mikedunn@newsguy.com \
--cc=robert.jarzmik@free.fr \
/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.