From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from smtp1-g21.free.fr ([212.27.42.1]) by canuck.infradead.org with esmtp (Exim 4.76 #1 (Red Hat Linux)) id 1RE6MD-0006L0-Qh for linux-mtd@lists.infradead.org; Wed, 12 Oct 2011 21:29:03 +0000 From: Robert Jarzmik To: Mike Dunn Subject: Re: [PATCH] Add driver for M-sys / Sandisk diskonchip G4 nand flash References: <1318258091-12670-1-git-send-email-mikedunn@newsguy.com> Date: Wed, 12 Oct 2011 23:28:34 +0200 Message-ID: <8762jtlx19.fsf@free.fr> MIME-Version: 1.0 Content-Type: text/plain Cc: marek.vasut@gmail.com, linux-mtd@lists.infradead.org List-Id: Linux MTD discussion mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Mike Dunn 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 > + * > + * 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