[PATCH] Add driver for M-sys / Sandisk diskonchip G4 nand flash
Marek Vasut
marek.vasut at gmail.com
Mon Oct 10 11:51:18 EDT 2011
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
More information about the linux-mtd
mailing list