📄 nand.c
字号:
goto out; page++; } this->data_poi = bufstart; ret = nand_verify_pages (mtd, this, startpage, numpages, oobbuf, oobsel, chipnr, 0); if (ret) goto out; written += mtd->oobblock * numpages; /* All done ? */ if (!count) break; startpage = page & this->pagemask; /* Check, if we cross a chip boundary */ if (!startpage) { chipnr++; this->select_chip(mtd, -1); this->select_chip(mtd, chipnr); } } ret = 0;out: /* Deselect and wake up anyone waiting on the device */ nand_release_chip(mtd); *retlen = written; return ret;}/* * NAND erase a block * * @mtd: MTD device structure * @instr: erase instruction */static int nand_erase (struct mtd_info *mtd, struct erase_info *instr){ int page, len, status, pages_per_block, ret, chipnr; struct nand_chip *this = mtd->priv; DEBUG (MTD_DEBUG_LEVEL3, "nand_erase: start = 0x%08x, len = %i\n", (unsigned int) instr->addr, (unsigned int) instr->len); /* Start address must align on block boundary */ if (instr->addr & (mtd->erasesize - 1)) { DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Unaligned address\n"); return -EINVAL; } /* Length must align on block boundary */ if (instr->len & (mtd->erasesize - 1)) { DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Length not block aligned\n"); return -EINVAL; } /* Do not allow erase past end of device */ if ((instr->len + instr->addr) > mtd->size) { DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Erase past end of device\n"); return -EINVAL; } /* Grab the lock and see if the device is available */ nand_get_chip (this, mtd, FL_ERASING, NULL); /* Shift to get first page */ page = (int) (instr->addr >> this->page_shift); chipnr = (int)((unsigned long)instr->addr / this->chipsize); /* Calculate pages in each block */ pages_per_block = mtd->erasesize / mtd->oobblock; /* Select the NAND device */ this->select_chip(mtd, chipnr); /* Check the WP bit */ this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1); if (!(this->read_byte(mtd) & 0x80)) { DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Device is write protected!!!\n"); instr->state = MTD_ERASE_FAILED; goto erase_exit; } /* Loop through the pages */ len = instr->len; instr->state = MTD_ERASING; while (len) { /* Check if we have a bad block, we do not erase bad blocks ! */ if (this->block_bad(mtd, (loff_t) page, 0)) { printk (KERN_WARNING "nand_erase: attempt to erase a bad block at page 0x%08x\n", page); instr->state = MTD_ERASE_FAILED; goto erase_exit; } /* Invalidate the page cache, if we erase the block which contains the current cached page */ if (page >= this->pagebuf && this->pagebuf < (page + pages_per_block)) this->pagebuf = -1; /* Send commands to erase a page */ this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page & this->pagemask); this->cmdfunc (mtd, NAND_CMD_ERASE2, -1, -1); status = this->waitfunc (mtd, this, FL_ERASING); /* See if block erase succeeded */ if (status & 0x01) { DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: " "Failed erase, page 0x%08x\n", page); instr->state = MTD_ERASE_FAILED; goto erase_exit; } /* Increment page address and decrement length */ len -= mtd->erasesize; page += pages_per_block; /* Check, if we cross a chip boundary */ if (len && !(page & this->pagemask)) { chipnr++; this->select_chip(mtd, -1); this->select_chip(mtd, chipnr); } } instr->state = MTD_ERASE_DONE;erase_exit: ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO;; /* Do call back function */ if (!ret && instr->callback) instr->callback (instr); /* Deselect and wake up anyone waiting on the device */ nand_release_chip(mtd); /* Return more or less happy */ return ret;}/* * NAND sync * * @mtd: MTD device structure */static void nand_sync (struct mtd_info *mtd){ struct nand_chip *this = mtd->priv; DECLARE_WAITQUEUE (wait, current); DEBUG (MTD_DEBUG_LEVEL3, "nand_sync: called\n");retry: /* Grab the spinlock */ spin_lock_bh (&this->chip_lock); /* See what's going on */ switch (this->state) { case FL_READY: case FL_SYNCING: this->state = FL_SYNCING; spin_unlock_bh (&this->chip_lock); break; default: /* Not an idle state */ add_wait_queue (&this->wq, &wait); spin_unlock_bh (&this->chip_lock); schedule (); remove_wait_queue (&this->wq, &wait); goto retry; } /* Lock the device */ spin_lock_bh (&this->chip_lock); /* Set the device to be ready again */ if (this->state == FL_SYNCING) { this->state = FL_READY; wake_up (&this->wq); } /* Unlock the device */ spin_unlock_bh (&this->chip_lock);}/* * Check whether the block at the given offset is bad * * @mtd: MTD device structure * @ofs: offset relative to mtd start */static int nand_block_isbad (struct mtd_info *mtd, loff_t ofs){ struct nand_chip *this = mtd->priv; /* Check for invalid offset */ if (ofs > mtd->size) return -EINVAL; return this->block_bad(mtd, ofs, 1);}/* * Mark the block at the given offset as bad * * @mtd: MTD device structure * @ofs: offset relative to mtd start */static int nand_block_markbad (struct mtd_info *mtd, loff_t ofs){ struct nand_chip *this = mtd->priv; /* Check for invalid offset */ if (ofs > mtd->size) return -EINVAL; return this->block_markbad(mtd, ofs);}/* * Scan for the NAND device * * This fills out all the not initialized function pointers * with the defaults. * The flash ID is read and the mtd/chip structures are * filled with the appropriate values. * * @mtd: MTD device structure * @maxchips: Number of chips to scan for */int nand_scan (struct mtd_info *mtd, int maxchips){ int i, nand_maf_id, nand_dev_id, busw; struct nand_chip *this = mtd->priv; /* Get buswidth to select the correct functions*/ busw = this->options & NAND_BUSWIDTH_16; /* check for proper chip_delay setup, set 20us if not */ if (!this->chip_delay) this->chip_delay = 20; /* check, if a user supplied command function given */ if (this->cmdfunc == NULL) this->cmdfunc = nand_command; /* check, if a user supplied wait function given */ if (this->waitfunc == NULL) this->waitfunc = nand_wait; if (!this->select_chip) this->select_chip = nand_select_chip; if (!this->write_byte) this->write_byte = busw ? nand_write_byte16 : nand_write_byte; if (!this->read_byte) this->read_byte = busw ? nand_read_byte16 : nand_read_byte; if (!this->write_word) this->write_word = nand_write_word; if (!this->read_word) this->read_word = nand_read_word; if (!this->block_bad) this->block_bad = nand_block_bad; if (!this->block_markbad) this->block_markbad = nand_default_block_markbad; if (!this->write_buf) this->write_buf = busw ? nand_write_buf16 : nand_write_buf; if (!this->read_buf) this->read_buf = busw ? nand_read_buf16 : nand_read_buf; if (!this->verify_buf) this->verify_buf = busw ? nand_verify_buf16 : nand_verify_buf; /* Select the device */ this->select_chip(mtd, 0); /* Send the command for reading device ID */ this->cmdfunc (mtd, NAND_CMD_READID, 0x00, -1); /* Read manufacturer and device IDs */ nand_maf_id = this->read_byte(mtd); nand_dev_id = this->read_byte(mtd); /* Print and store flash device information */ for (i = 0; nand_flash_ids[i].name != NULL; i++) { if (nand_dev_id != nand_flash_ids[i].id) continue; mtd->name = nand_flash_ids[i].name; this->chipsize = nand_flash_ids[i].chipsize << 20; /* New devices have all the information in additional id bytes */ if (!nand_flash_ids[i].pagesize) { int extid; /* The 3rd id byte contains non relevant data ATM */ extid = this->read_byte(mtd); /* The 4th id byte is the important one */ extid = this->read_byte(mtd); /* Calc pagesize */ mtd->oobblock = 1024 << (extid & 0x3); extid >>= 2; /* Calc oobsize */ mtd->oobsize = (8 << (extid & 0x03)) * (mtd->oobblock / 512); extid >>= 2; /* Calc blocksize. Blocksize is multiples of 64KiB */ mtd->erasesize = (64 * 1024) << (extid & 0x03); extid >>= 2; /* Get buswidth information */ busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0; /* Do not replace user supplied command function ! */ if (this->cmdfunc == nand_command) this->cmdfunc = nand_command_lp; } else { /* Old devices have this data hardcoded in the * device id table */ mtd->erasesize = nand_flash_ids[i].erasesize; mtd->oobblock = nand_flash_ids[i].pagesize; mtd->oobsize = mtd->oobblock / 32; busw = nand_flash_ids[i].options & NAND_BUSWIDTH_16; } /* Check, if buswidth is correct. Hardware drivers should set * this correct ! */ if (busw != (this->options & NAND_BUSWIDTH_16)) { printk (KERN_INFO "NAND device: Manufacturer ID:" " 0x%02x, Chip ID: 0x%02x (%s %s)\n", nand_maf_id, nand_dev_id, nand_manuf_ids[i].name , mtd->name); printk (KERN_WARNING "NAND bus width %d instead %d bit\n", (this->options & NAND_BUSWIDTH_16) ? 16 : 8, busw ? 16 : 8); this->select_chip(mtd, -1); return 1; } /* Calculate the address shift from the page size */ this->page_shift = ffs(mtd->oobblock) - 1; /* Set the bad block position */ this->badblockpos = mtd->oobblock > 512 ? NAND_LARGE_BADBLOCK_POS : NAND_SMALL_BADBLOCK_POS; /* Get chip options */ this->options = nand_flash_ids[i].options; /* Check if this is a not a samsung device */ if (nand_maf_id != NAND_MFR_SAMSUNG) this->options &= ~NAND_SAMSUNG_LP_OPTIONS; /* Try to identify manufacturer */ for (i = 0; nand_manuf_ids[i].id != 0x0; i++) { if (nand_manuf_ids[i].id == nand_maf_id) break; } printk (KERN_INFO "NAND device: Manufacturer ID:" " 0x%02x, Chip ID: 0x%02x (%s %s)\n", nand_maf_id, nand_dev_id, nand_manuf_ids[i].name , mtd->name); break; } if (!mtd->name) { printk (KERN_WARNING "No NAND device found!!!\n"); this->select_chip(mtd, -1); return 1; } for (i=1; i < maxchips; i++) { this->select_chip(mtd, i); /* Send the command for reading device ID */ this->cmdfunc (mtd, NAND_CMD_READID, 0x00, -1); /* Read manufacturer and device IDs */ if (nand_maf_id != this->read_byte(mtd) || nand_dev_id != this->read_byte(mtd)) break; } if (i > 1) printk(KERN_INFO "%d NAND chips detected\n", i); if (!this->oob_buf || !this->data_buf) { printk (KERN_WARNING "nand_scan(): Buffers not set. Scan aborted\n"); return 1; } /* Store the number of chips and calc total size for mtd */ this->numchips = i; mtd->size = i * this->chipsize; /* Convert chipsize to number of pages per chip -1. */ this->pagemask = (this->chipsize >> this->page_shift) - 1; /* Preset the internal oob buffer */ memset(this->oob_buf, 0xff, mtd->oobsize * (mtd->erasesize / mtd->oobblock)); /* If no default placement scheme is given, select an * appropriate one */ if (!this->autooob) { /* Select the appropriate default oob placement scheme for * placement agnostic filesystems */ switch (mtd->oobsize) { case 8: this->autooob = &nand_oob_8; break; case 16: this->autooob = &nand_oob_16; break; case 64: this->autooob = &nand_oob_64; break; default: printk (KERN_WARNING "No oob scheme defined for oobsize %d\n", mtd->oobsize); BUG(); } } /* The number of bytes available for the filesystem to place fs dependend * oob data */ if (this->options & NAND_BUSWIDTH_16) { mtd->oobavail = mtd->oobsize - (this->autooob->eccbytes + 2); if (this->autooob->eccbytes & 0x01) mtd->oobavail--; } else mtd->oobavail = mtd->oobsize - (this->autooob->eccbytes + 1); /* * check ECC mode, default to software * if 3byte/512byte hardware ECC is selected and we have 256 byte pagesize * fallback to software ECC */ this->eccsize = 256; /* set default eccsize */ switch (this->eccmode) { case NAND_ECC_HW3_512: case NAND_ECC_HW6_512: if (mtd->oobblock == 256) { printk (KERN_WARNING "512 byte HW ECC not possible on 256 Byte pagesize, fallback to SW ECC \n"); this->eccmode = NAND_ECC_SOFT; this->calculate_ecc = nand_calculate_ecc; this->correct_data = nand_correct_data; break; } else this->eccsize = 512; /* set eccsize to 512 and fall through for function check */ case NAND_ECC_HW3_256: if (this->calculate_ecc && this->correct_data && this->enable_hwecc) break; printk (KERN_WARNING "No ECC functions supplied, Hardware ECC not possible\n"); BUG(); case NAND_ECC_NONE: printk (KERN_WARNING "NAND_ECC_NONE selected by board driver. This is not recommended !!\n"); this->eccmode = NAND_ECC_NONE; break; case NAND_ECC_SOFT: this->calculate_ecc = nand_calculate_ecc; this->correct_data = nand_correct_data; break; default: printk (KERN_WARNING "Invalid NAND_ECC_MODE %d\n", this->eccmode); BUG(); } mtd->eccsize = this->eccsize; /* Set the number of read / write steps for one page to ensure ECC generation */ switch (this->eccmode) { case NAND_ECC_HW3_512: case NAND_ECC_HW6_512: this->eccsteps = mtd->oobblock / 512; break; case NAND_ECC_HW3_256: case NAND_ECC_SOFT: this->eccsteps = mtd->oobblock / 256; break; case NAND_ECC_NONE: this->eccsteps = 1; break; } /* Initialize state, waitqueue and spinlock */ this->state = FL_READY; init_waitqueue_head (&this->wq); spin_lock_init (&this->chip_lock); /* De-select the device */ this->select_chip(mtd, -1); /* Invalidate the pagebuffer reference */ this->pagebuf = -1; /* Fill in remaining MTD driver data */ mtd->type = MTD_NANDFLASH; mtd->flags = MTD_CAP_NANDFLASH | MTD_ECC; mtd->ecctype = MTD_ECC_SW; mtd->erase = nand_erase; mtd->point = NULL; mtd->unpoint = NULL; mtd->read = nand_read; mtd->write = nand_write; mtd->read_ecc = nand_read_ecc; mtd->write_ecc = nand_write_ecc; mtd->read_oob = nand_read_oob; mtd->write_oob = nand_write_oob; mtd->readv = NULL; mtd->writev = nand_writev; mtd->writev_ecc = nand_writev_ecc; mtd->sync = nand_sync; mtd->lock = NULL; mtd->unlock = NULL; mtd->suspend = NULL; mtd->resume = NULL; mtd->block_isbad = nand_block_isbad; mtd->block_markbad = nand_block_markbad; mtd->owner = THIS_MODULE; /* Return happy */ return 0;}EXPORT_SYMBOL (nand_scan);MODULE_LICENSE ("GPL");MODULE_AUTHOR ("Steven J. Hill <sjhill@realitydiluted.com>, Thomas Gleixner <tglx@linutronix.de>");MODULE_DESCRIPTION ("Generic NAND flash driver code");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -