📄 onenand_base.c
字号:
unsigned short word; /* Align with word(16-bit) size */ count--; /* Read word and save byte */ word = this->read_word(bufferram + offset + count); buffer[count] = (word & 0xff); } memcpy(buffer, bufferram + offset, count); return 0;}/** * onenand_sync_read_bufferram - [OneNAND Interface] Read the bufferram area with Sync. Burst mode * @param mtd MTD data structure * @param area BufferRAM area * @param buffer the databuffer to put/get data * @param offset offset to read from or write to * @param count number of bytes to read/write * * Read the BufferRAM area with Sync. Burst Mode */static int onenand_sync_read_bufferram(struct mtd_info *mtd, int area, unsigned char *buffer, int offset, size_t count){ struct onenand_chip *this = mtd->priv; void __iomem *bufferram; bufferram = this->base + area; bufferram += onenand_bufferram_offset(mtd, area); this->mmcontrol(mtd, ONENAND_SYS_CFG1_SYNC_READ); if (ONENAND_CHECK_BYTE_ACCESS(count)) { unsigned short word; /* Align with word(16-bit) size */ count--; /* Read word and save byte */ word = this->read_word(bufferram + offset + count); buffer[count] = (word & 0xff); } memcpy(buffer, bufferram + offset, count); this->mmcontrol(mtd, 0); return 0;}/** * onenand_write_bufferram - [OneNAND Interface] Write the bufferram area * @param mtd MTD data structure * @param area BufferRAM area * @param buffer the databuffer to put/get data * @param offset offset to read from or write to * @param count number of bytes to read/write * * Write the BufferRAM area */static int onenand_write_bufferram(struct mtd_info *mtd, int area, const unsigned char *buffer, int offset, size_t count){ struct onenand_chip *this = mtd->priv; void __iomem *bufferram; bufferram = this->base + area; bufferram += onenand_bufferram_offset(mtd, area); if (ONENAND_CHECK_BYTE_ACCESS(count)) { unsigned short word; int byte_offset; /* Align with word(16-bit) size */ count--; /* Calculate byte access offset */ byte_offset = offset + count; /* Read word and save byte */ word = this->read_word(bufferram + byte_offset); word = (word & ~0xff) | buffer[count]; this->write_word(word, bufferram + byte_offset); } memcpy(bufferram + offset, buffer, count); return 0;}/** * onenand_get_2x_blockpage - [GENERIC] Get blockpage at 2x program mode * @param mtd MTD data structure * @param addr address to check * @return blockpage address * * Get blockpage address at 2x program mode */static int onenand_get_2x_blockpage(struct mtd_info *mtd, loff_t addr){ struct onenand_chip *this = mtd->priv; int blockpage, block, page; /* Calculate the even block number */ block = (int) (addr >> this->erase_shift) & ~1; /* Is it the odd plane? */ if (addr & this->writesize) block++; page = (int) (addr >> (this->page_shift + 1)) & this->page_mask; blockpage = (block << 7) | page; return blockpage;}/** * onenand_check_bufferram - [GENERIC] Check BufferRAM information * @param mtd MTD data structure * @param addr address to check * @return 1 if there are valid data, otherwise 0 * * Check bufferram if there is data we required */static int onenand_check_bufferram(struct mtd_info *mtd, loff_t addr){ struct onenand_chip *this = mtd->priv; int blockpage, found = 0; unsigned int i; if (ONENAND_IS_2PLANE(this)) blockpage = onenand_get_2x_blockpage(mtd, addr); else blockpage = (int) (addr >> this->page_shift); /* Is there valid data? */ i = ONENAND_CURRENT_BUFFERRAM(this); if (this->bufferram[i].blockpage == blockpage) found = 1; else { /* Check another BufferRAM */ i = ONENAND_NEXT_BUFFERRAM(this); if (this->bufferram[i].blockpage == blockpage) { ONENAND_SET_NEXT_BUFFERRAM(this); found = 1; } } if (found && ONENAND_IS_DDP(this)) { /* Select DataRAM for DDP */ int block = (int) (addr >> this->erase_shift); int value = onenand_bufferram_address(this, block); this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2); } return found;}/** * onenand_update_bufferram - [GENERIC] Update BufferRAM information * @param mtd MTD data structure * @param addr address to update * @param valid valid flag * * Update BufferRAM information */static void onenand_update_bufferram(struct mtd_info *mtd, loff_t addr, int valid){ struct onenand_chip *this = mtd->priv; int blockpage; unsigned int i; if (ONENAND_IS_2PLANE(this)) blockpage = onenand_get_2x_blockpage(mtd, addr); else blockpage = (int) (addr >> this->page_shift); /* Invalidate another BufferRAM */ i = ONENAND_NEXT_BUFFERRAM(this); if (this->bufferram[i].blockpage == blockpage) this->bufferram[i].blockpage = -1; /* Update BufferRAM */ i = ONENAND_CURRENT_BUFFERRAM(this); if (valid) this->bufferram[i].blockpage = blockpage; else this->bufferram[i].blockpage = -1;}/** * onenand_invalidate_bufferram - [GENERIC] Invalidate BufferRAM information * @param mtd MTD data structure * @param addr start address to invalidate * @param len length to invalidate * * Invalidate BufferRAM information */static void onenand_invalidate_bufferram(struct mtd_info *mtd, loff_t addr, unsigned int len){ struct onenand_chip *this = mtd->priv; int i; loff_t end_addr = addr + len; /* Invalidate BufferRAM */ for (i = 0; i < MAX_BUFFERRAM; i++) { loff_t buf_addr = this->bufferram[i].blockpage << this->page_shift; if (buf_addr >= addr && buf_addr < end_addr) this->bufferram[i].blockpage = -1; }}/** * onenand_get_device - [GENERIC] Get chip for selected access * @param mtd MTD device structure * @param new_state the state which is requested * * Get the device and lock it for exclusive access */static int onenand_get_device(struct mtd_info *mtd, int new_state){ struct onenand_chip *this = mtd->priv; DECLARE_WAITQUEUE(wait, current); /* * Grab the lock and see if the device is available */ while (1) { spin_lock(&this->chip_lock); if (this->state == FL_READY) { this->state = new_state; spin_unlock(&this->chip_lock); break; } if (new_state == FL_PM_SUSPENDED) { spin_unlock(&this->chip_lock); return (this->state == FL_PM_SUSPENDED) ? 0 : -EAGAIN; } set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&this->wq, &wait); spin_unlock(&this->chip_lock); schedule(); remove_wait_queue(&this->wq, &wait); } return 0;}/** * onenand_release_device - [GENERIC] release chip * @param mtd MTD device structure * * Deselect, release chip lock and wake up anyone waiting on the device */static void onenand_release_device(struct mtd_info *mtd){ struct onenand_chip *this = mtd->priv; /* Release the chip */ spin_lock(&this->chip_lock); this->state = FL_READY; wake_up(&this->wq); spin_unlock(&this->chip_lock);}/** * onenand_transfer_auto_oob - [Internal] oob auto-placement transfer * @param mtd MTD device structure * @param buf destination address * @param column oob offset to read from * @param thislen oob length to read */static int onenand_transfer_auto_oob(struct mtd_info *mtd, uint8_t *buf, int column, int thislen){ struct onenand_chip *this = mtd->priv; struct nand_oobfree *free; int readcol = column; int readend = column + thislen; int lastgap = 0; unsigned int i; uint8_t *oob_buf = this->oob_buf; free = this->ecclayout->oobfree; for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) { if (readcol >= lastgap) readcol += free->offset - lastgap; if (readend >= lastgap) readend += free->offset - lastgap; lastgap = free->offset + free->length; } this->read_bufferram(mtd, ONENAND_SPARERAM, oob_buf, 0, mtd->oobsize); free = this->ecclayout->oobfree; for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) { int free_end = free->offset + free->length; if (free->offset < readend && free_end > readcol) { int st = max_t(int,free->offset,readcol); int ed = min_t(int,free_end,readend); int n = ed - st; memcpy(buf, oob_buf + st, n); buf += n; } else if (column == 0) break; } return 0;}/** * onenand_read_ops_nolock - [OneNAND Interface] OneNAND read main and/or out-of-band * @param mtd MTD device structure * @param from offset to read from * @param ops: oob operation description structure * * OneNAND read main and/or out-of-band data */static int onenand_read_ops_nolock(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops){ struct onenand_chip *this = mtd->priv; struct mtd_ecc_stats stats; size_t len = ops->len; size_t ooblen = ops->ooblen; u_char *buf = ops->datbuf; u_char *oobbuf = ops->oobbuf; int read = 0, column, thislen; int oobread = 0, oobcolumn, thisooblen, oobsize; int ret = 0, boundary = 0; int writesize = this->writesize; DEBUG(MTD_DEBUG_LEVEL3, "onenand_read_ops_nolock: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len); if (ops->mode == MTD_OOB_AUTO) oobsize = this->ecclayout->oobavail; else oobsize = mtd->oobsize; oobcolumn = from & (mtd->oobsize - 1); /* Do not allow reads past end of device */ if ((from + len) > mtd->size) { printk(KERN_ERR "onenand_read_ops_nolock: Attempt read beyond end of device\n"); ops->retlen = 0; ops->oobretlen = 0; return -EINVAL; } stats = mtd->ecc_stats; /* Read-while-load method */ /* Do first load to bufferRAM */ if (read < len) { if (!onenand_check_bufferram(mtd, from)) { this->command(mtd, ONENAND_CMD_READ, from, writesize); ret = this->wait(mtd, FL_READING); onenand_update_bufferram(mtd, from, !ret); if (ret == -EBADMSG) ret = 0; } } thislen = min_t(int, writesize, len - read); column = from & (writesize - 1); if (column + thislen > writesize) thislen = writesize - column; while (!ret) { /* If there is more to load then start next load */ from += thislen; if (read + thislen < len) { this->command(mtd, ONENAND_CMD_READ, from, writesize); /* * Chip boundary handling in DDP * Now we issued chip 1 read and pointed chip 1 * bufferam so we have to point chip 0 bufferam. */ if (ONENAND_IS_DDP(this) && unlikely(from == (this->chipsize >> 1))) { this->write_word(ONENAND_DDP_CHIP0, this->base + ONENAND_REG_START_ADDRESS2); boundary = 1; } else boundary = 0; ONENAND_SET_PREV_BUFFERRAM(this); } /* While load is going, read from last bufferRAM */ this->read_bufferram(mtd, ONENAND_DATARAM, buf, column, thislen); /* Read oob area if needed */ if (oobbuf) { thisooblen = oobsize - oobcolumn; thisooblen = min_t(int, thisooblen, ooblen - oobread); if (ops->mode == MTD_OOB_AUTO) onenand_transfer_auto_oob(mtd, oobbuf, oobcolumn, thisooblen); else this->read_bufferram(mtd, ONENAND_SPARERAM, oobbuf, oobcolumn, thisooblen); oobread += thisooblen; oobbuf += thisooblen; oobcolumn = 0; } /* See if we are done */ read += thislen; if (read == len) break; /* Set up for next read from bufferRAM */ if (unlikely(boundary)) this->write_word(ONENAND_DDP_CHIP1, this->base + ONENAND_REG_START_ADDRESS2); ONENAND_SET_NEXT_BUFFERRAM(this); buf += thislen; thislen = min_t(int, writesize, len - read); column = 0; cond_resched(); /* Now wait for load */ ret = this->wait(mtd, FL_READING); onenand_update_bufferram(mtd, from, !ret); if (ret == -EBADMSG) ret = 0; } /* * Return success, if no ECC failures, else -EBADMSG * fs driver will take care of that, because * retlen == desired len and result == -EBADMSG */ ops->retlen = read; ops->oobretlen = oobread; if (ret) return ret; if (mtd->ecc_stats.failed - stats.failed) return -EBADMSG; return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0;}/** * onenand_read_oob_nolock - [MTD Interface] OneNAND read out-of-band * @param mtd MTD device structure * @param from offset to read from * @param ops: oob operation description structure * * OneNAND read out-of-band data from the spare area */static int onenand_read_oob_nolock(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops){ struct onenand_chip *this = mtd->priv; struct mtd_ecc_stats stats; int read = 0, thislen, column, oobsize; size_t len = ops->ooblen; mtd_oob_mode_t mode = ops->mode; u_char *buf = ops->oobbuf; int ret = 0; from += ops->ooboffs; DEBUG(MTD_DEBUG_LEVEL3, "onenand_read_oob_nolock: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len); /* Initialize return length value */ ops->oobretlen = 0; if (mode == MTD_OOB_AUTO) oobsize = this->ecclayout->oobavail; else oobsize = mtd->oobsize; column = from & (mtd->oobsize - 1); if (unlikely(column >= oobsize)) { printk(KERN_ERR "onenand_read_oob_nolock: Attempted to start read outside oob\n"); return -EINVAL; } /* Do not allow reads past end of device */ if (unlikely(from >= mtd->size || column + len > ((mtd->size >> this->page_shift) - (from >> this->page_shift)) * oobsize)) { printk(KERN_ERR "onenand_read_oob_nolock: Attempted to read beyond end of device\n"); return -EINVAL; } stats = mtd->ecc_stats; while (read < len) { cond_resched(); thislen = oobsize - column; thislen = min_t(int, thislen, len); this->command(mtd, ONENAND_CMD_READOOB, from, mtd->oobsize); onenand_update_bufferram(mtd, from, 0); ret = this->wait(mtd, FL_READING); if (ret && ret != -EBADMSG) { printk(KERN_ERR "onenand_read_oob_nolock: read failed = 0x%x\n", ret);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -