?? nand_base.c
字號(hào):
/* Invalidate the page cache, if we write to the cached page */ if (page == chip->pagebuf) chip->pagebuf = -1; memset(chip->oob_poi, 0xff, mtd->oobsize); nand_fill_oob(chip, ops->oobbuf, ops); status = chip->ecc.write_oob(mtd, chip, page & chip->pagemask); memset(chip->oob_poi, 0xff, mtd->oobsize); if (status) return status; ops->oobretlen = ops->ooblen; return 0;}/** * nand_write_oob - [MTD Interface] NAND write data and/or out-of-band * @mtd: MTD device structure * @to: offset to write to * @ops: oob operation description structure */static int nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops){ struct nand_chip *chip = mtd->priv; int ret = -ENOTSUPP; ops->retlen = 0; /* Do not allow writes past end of device */ if (ops->datbuf && (to + ops->len) > mtd->size) { MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_read_oob: " "Attempt read beyond end of device\n"); return -EINVAL; } nand_get_device(chip, mtd, FL_WRITING); switch(ops->mode) { case MTD_OOB_PLACE: case MTD_OOB_AUTO: case MTD_OOB_RAW: break; default: goto out; } if (!ops->datbuf) ret = nand_do_write_oob(mtd, to, ops); else ret = nand_do_write_ops(mtd, to, ops); out: nand_release_device(mtd); return ret;}/** * single_erease_cmd - [GENERIC] NAND standard block erase command function * @mtd: MTD device structure * @page: the page address of the block which will be erased * * Standard erase command for NAND chips */static void single_erase_cmd(struct mtd_info *mtd, int page){ struct nand_chip *chip = mtd->priv; /* Send commands to erase a block */ chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page); chip->cmdfunc(mtd, NAND_CMD_ERASE2, -1, -1);}/** * multi_erease_cmd - [GENERIC] AND specific block erase command function * @mtd: MTD device structure * @page: the page address of the block which will be erased * * AND multi block erase command function * Erase 4 consecutive blocks */static void multi_erase_cmd(struct mtd_info *mtd, int page){ struct nand_chip *chip = mtd->priv; /* Send commands to erase a block */ chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page++); chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page++); chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page++); chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page); chip->cmdfunc(mtd, NAND_CMD_ERASE2, -1, -1);}/** * nand_erase - [MTD Interface] erase block(s) * @mtd: MTD device structure * @instr: erase instruction * * Erase one ore more blocks */static int nand_erase(struct mtd_info *mtd, struct erase_info *instr){ return nand_erase_nand(mtd, instr, 0);}#define BBT_PAGE_MASK 0xffffff3f/** * nand_erase_nand - [Internal] erase block(s) * @mtd: MTD device structure * @instr: erase instruction * @allowbbt: allow erasing the bbt area * * Erase one ore more blocks */int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, int allowbbt){ int page, len, status, pages_per_block, ret, chipnr; struct nand_chip *chip = mtd->priv; int rewrite_bbt[CONFIG_SYS_NAND_MAX_CHIPS]={0}; unsigned int bbt_masked_page = 0xffffffff; MTDDEBUG (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 & ((1 << chip->phys_erase_shift) - 1)) { MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Unaligned address\n"); return -EINVAL; } /* Length must align on block boundary */ if (instr->len & ((1 << chip->phys_erase_shift) - 1)) { MTDDEBUG (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) { MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Erase past end of device\n"); return -EINVAL; } instr->fail_addr = 0xffffffff; /* Grab the lock and see if the device is available */ nand_get_device(chip, mtd, FL_ERASING); /* Shift to get first page */ page = (int)(instr->addr >> chip->page_shift); chipnr = (int)(instr->addr >> chip->chip_shift); /* Calculate pages in each block */ pages_per_block = 1 << (chip->phys_erase_shift - chip->page_shift); /* Select the NAND device */ chip->select_chip(mtd, chipnr); /* Check, if it is write protected */ if (nand_check_wp(mtd)) { MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Device is write protected!!!\n"); instr->state = MTD_ERASE_FAILED; goto erase_exit; } /* * If BBT requires refresh, set the BBT page mask to see if the BBT * should be rewritten. Otherwise the mask is set to 0xffffffff which * can not be matched. This is also done when the bbt is actually * erased to avoid recusrsive updates */ if (chip->options & BBT_AUTO_REFRESH && !allowbbt) bbt_masked_page = chip->bbt_td->pages[chipnr] & BBT_PAGE_MASK; /* Loop through the pages */ len = instr->len; instr->state = MTD_ERASING; while (len) { /* * heck if we have a bad block, we do not erase bad blocks ! */ if (nand_block_checkbad(mtd, ((loff_t) page) << chip->page_shift, 0, allowbbt)) { 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 <= chip->pagebuf && chip->pagebuf < (page + pages_per_block)) chip->pagebuf = -1; chip->erase_cmd(mtd, page & chip->pagemask); status = chip->waitfunc(mtd, chip); /* * See if operation failed and additional status checks are * available */ if ((status & NAND_STATUS_FAIL) && (chip->errstat)) status = chip->errstat(mtd, chip, FL_ERASING, status, page); /* See if block erase succeeded */ if (status & NAND_STATUS_FAIL) { MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_erase: " "Failed erase, page 0x%08x\n", page); instr->state = MTD_ERASE_FAILED; instr->fail_addr = (page << chip->page_shift); goto erase_exit; } /* * If BBT requires refresh, set the BBT rewrite flag to the * page being erased */ if (bbt_masked_page != 0xffffffff && (page & BBT_PAGE_MASK) == bbt_masked_page) rewrite_bbt[chipnr] = (page << chip->page_shift); /* Increment page address and decrement length */ len -= (1 << chip->phys_erase_shift); page += pages_per_block; /* Check, if we cross a chip boundary */ if (len && !(page & chip->pagemask)) { chipnr++; chip->select_chip(mtd, -1); chip->select_chip(mtd, chipnr); /* * If BBT requires refresh and BBT-PERCHIP, set the BBT * page mask to see if this BBT should be rewritten */ if (bbt_masked_page != 0xffffffff && (chip->bbt_td->options & NAND_BBT_PERCHIP)) bbt_masked_page = chip->bbt_td->pages[chipnr] & BBT_PAGE_MASK; } } instr->state = MTD_ERASE_DONE; erase_exit: ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO; /* Deselect and wake up anyone waiting on the device */ nand_release_device(mtd); /* Do call back function */ if (!ret) mtd_erase_callback(instr); /* * If BBT requires refresh and erase was successful, rewrite any * selected bad block tables */ if (bbt_masked_page == 0xffffffff || ret) return ret; for (chipnr = 0; chipnr < chip->numchips; chipnr++) { if (!rewrite_bbt[chipnr]) continue; /* update the BBT for chip */ MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_erase_nand: nand_update_bbt " "(%d:0x%0x 0x%0x)\n", chipnr, rewrite_bbt[chipnr], chip->bbt_td->pages[chipnr]); nand_update_bbt(mtd, rewrite_bbt[chipnr]); } /* Return more or less happy */ return ret;}/** * nand_sync - [MTD Interface] sync * @mtd: MTD device structure * * Sync is actually a wait for chip ready function */static void nand_sync(struct mtd_info *mtd){ struct nand_chip *chip = mtd->priv; MTDDEBUG (MTD_DEBUG_LEVEL3, "nand_sync: called\n"); /* Grab the lock and see if the device is available */ nand_get_device(chip, mtd, FL_SYNCING); /* Release it and go back */ nand_release_device(mtd);}/** * nand_block_isbad - [MTD Interface] Check if block at offset is bad * @mtd: MTD device structure * @offs: offset relative to mtd start */static int nand_block_isbad(struct mtd_info *mtd, loff_t offs){ /* Check for invalid offset */ if (offs > mtd->size) return -EINVAL; return nand_block_checkbad(mtd, offs, 1, 0);}/** * nand_block_markbad - [MTD Interface] Mark 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 *chip = mtd->priv; int ret; if ((ret = nand_block_isbad(mtd, ofs))) { /* If it was bad already, return success and do nothing. */ if (ret > 0) return 0; return ret; } return chip->block_markbad(mtd, ofs);}/** * nand_suspend - [MTD Interface] Suspend the NAND flash * @mtd: MTD device structure */static int nand_suspend(struct mtd_info *mtd){ struct nand_chip *chip = mtd->priv; return nand_get_device(chip, mtd, FL_PM_SUSPENDED);}/** * nand_resume - [MTD Interface] Resume the NAND flash * @mtd: MTD device structure */static void nand_resume(struct mtd_info *mtd){ struct nand_chip *chip = mtd->priv; if (chip->state == FL_PM_SUSPENDED) nand_release_device(mtd); else printk(KERN_ERR "nand_resume() called for a chip which is not " "in suspended state\n");}/* * Set default functions */static void nand_set_defaults(struct nand_chip *chip, int busw){ /* check for proper chip_delay setup, set 20us if not */ if (!chip->chip_delay) chip->chip_delay = 20; /* check, if a user supplied command function given */ if (chip->cmdfunc == NULL) chip->cmdfunc = nand_command; /* check, if a user supplied wait function given */ if (chip->waitfunc == NULL) chip->waitfunc = nand_wait; if (!chip->select_chip) chip->select_chip = nand_select_chip; if (!chip->read_byte) chip->read_byte = busw ? nand_read_byte16 : nand_read_byte; if (!chip->read_word) chip->read_word = nand_read_word; if (!chip->block_bad) chip->block_bad = nand_block_bad; if (!chip->block_markbad) chip->block_markbad = nand_default_block_markbad; if (!chip->write_buf) chip->write_buf = busw ? nand_write_buf16 : nand_write_buf; if (!chip->read_buf) chip->read_buf = busw ? nand_read_buf16 : nand_read_buf; if (!chip->verify_buf) chip->verify_buf = busw ? nand_verify_buf16 : nand_verify_buf; if (!chip->scan_bbt) chip->scan_bbt = nand_default_bbt; if (!chip->controller) { chip->controller = &chip->hwcontrol; /* XXX U-BOOT XXX */#if 0 spin_lock_init(&chip->controller->lock); init_waitqueue_head(&chip->controller->wq);#endif }}/* * Get the flash and manufacturer id and lookup if the type is supported */static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, struct nand_chip *chip, int busw, int *maf_id){ struct nand_flash_dev *type = NULL; int i, dev_id, maf_idx; int tmp_id, tmp_manf; /* Select the device */ chip->select_chip(mtd, 0); /* * Reset the chip, required by some chips (e.g. Micron MT29FxGxxxxx) * after power-up */ chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); /* Send the command for reading device ID */ chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); /* Read manufacturer and device IDs */ *maf_id = chip->read_byte(mtd); dev_id = chip->read_byte(mtd); /* Try again to make sure, as some systems the bus-hold or other * interface concerns can cause random data which looks like a * possibly credible NAND flash to appear. If the two results do * not match, ignore the device completely. */ chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); /* Read manufacturer and device IDs */ tmp_manf = chip->read_byte(mtd); tmp_id = chip->read_byte(mtd); if (tmp_manf != *maf_id || tmp_id != dev_id) { printk(KERN_INFO "%s: second ID read did not match " "%02x,%02x against %02x,%02x\n", __func__, *maf_id, dev_id, tmp_manf, tmp_id); return ERR_PTR(-ENODEV); } /* Lookup the flash id */ for (i = 0; nand_flash_ids[i].name != NULL; i++) { if (dev_id == nand_flash_ids[i].id) { type = &nand_flash_ids[i]; break; } } if (!type) return ERR_PTR(-ENODEV); if (!mtd->name) mtd->name = type->name; chip->chipsize = type->chipsize << 20; /* Newer devices have all the information in additional id bytes */ if (!type->pagesize) { int extid; /* The 3rd id byte holds MLC / multichip data */ chip->cellinfo = chip->read_byte(mtd); /* The 4th id byte is the important one */ extid = chip->read_byte(mtd); /* Calc pagesize */ mtd->writesize = 1024 << (extid & 0x3); extid >>= 2; /* Calc oobsize */ mtd->oobsize = (8 << (extid & 0x01)) * (mtd->writesize >> 9); 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; } else { /* * Old devices have chip data hardcoded in the device id table */ mtd->erasesize = type->erasesize; mtd->writesize = type->pagesize; mtd->oobsize = mtd->writesize / 32; busw = type->options & NAND_BUSWIDTH_16; } /* Try to identify manufacturer */ for (maf_idx = 0; nand_manuf_ids[maf_idx].id != 0x0; m
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號(hào)
Ctrl + =
減小字號(hào)
Ctrl + -