📄 onenand_base.c
字号:
spin_unlock(&this->chip_lock); break; } set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&this->wq, &wait); spin_unlock(&this->chip_lock); schedule(); remove_wait_queue(&this->wq, &wait); }}/** * 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_read_ecc - [MTD Interface] Read data with ECC * @param mtd MTD device structure * @param from offset to read from * @param len number of bytes to read * @param retlen pointer to variable to store the number of read bytes * @param buf the databuffer to put data * @param oob_buf filesystem supplied oob data buffer * @param oobsel oob selection structure * * OneNAND read with ECC */static int onenand_read_ecc(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf, u_char *oob_buf, struct nand_oobinfo *oobsel){ struct onenand_chip *this = mtd->priv; int read = 0, column; int thislen; int ret = 0; DEBUG(MTD_DEBUG_LEVEL3, "onenand_read_ecc: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len); /* Do not allow reads past end of device */ if ((from + len) > mtd->size) { DEBUG(MTD_DEBUG_LEVEL0, "onenand_read_ecc: Attempt read beyond end of device\n"); *retlen = 0; return -EINVAL; } /* Grab the lock and see if the device is available */ onenand_get_device(mtd, FL_READING); /* TODO handling oob */ while (read < len) { thislen = min_t(int, mtd->oobblock, len - read); column = from & (mtd->oobblock - 1); if (column + thislen > mtd->oobblock) thislen = mtd->oobblock - column; if (!onenand_check_bufferram(mtd, from)) { this->command(mtd, ONENAND_CMD_READ, from, mtd->oobblock); ret = this->wait(mtd, FL_READING); /* First copy data and check return value for ECC handling */ onenand_update_bufferram(mtd, from, 1); } this->read_bufferram(mtd, ONENAND_DATARAM, buf, column, thislen); read += thislen; if (read == len) break; if (ret) { DEBUG(MTD_DEBUG_LEVEL0, "onenand_read_ecc: read failed = %d\n", ret); goto out; } from += thislen; buf += thislen; }out: /* Deselect and wake up anyone waiting on the device */ onenand_release_device(mtd); /* * Return success, if no ECC failures, else -EBADMSG * fs driver will take care of that, because * retlen == desired len and result == -EBADMSG */ *retlen = read; return ret;}/** * onenand_read - [MTD Interface] MTD compability function for onenand_read_ecc * @param mtd MTD device structure * @param from offset to read from * @param len number of bytes to read * @param retlen pointer to variable to store the number of read bytes * @param buf the databuffer to put data * * This function simply calls onenand_read_ecc with oob buffer and oobsel = NULL*/static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf){ return onenand_read_ecc(mtd, from, len, retlen, buf, NULL, NULL);}/** * onenand_read_oob - [MTD Interface] OneNAND read out-of-band * @param mtd MTD device structure * @param from offset to read from * @param len number of bytes to read * @param retlen pointer to variable to store the number of read bytes * @param buf the databuffer to put data * * OneNAND read out-of-band data from the spare area */static int onenand_read_oob(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf){ struct onenand_chip *this = mtd->priv; int read = 0, thislen, column; int ret = 0; DEBUG(MTD_DEBUG_LEVEL3, "onenand_read_oob: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len); /* Initialize return length value */ *retlen = 0; /* Do not allow reads past end of device */ if (unlikely((from + len) > mtd->size)) { DEBUG(MTD_DEBUG_LEVEL0, "onenand_read_oob: Attempt read beyond end of device\n"); return -EINVAL; } /* Grab the lock and see if the device is available */ onenand_get_device(mtd, FL_READING); column = from & (mtd->oobsize - 1); while (read < len) { thislen = mtd->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); /* First copy data and check return value for ECC handling */ this->read_bufferram(mtd, ONENAND_SPARERAM, buf, column, thislen); read += thislen; if (read == len) break; if (ret) { DEBUG(MTD_DEBUG_LEVEL0, "onenand_read_oob: read failed = %d\n", ret); goto out; } buf += thislen; /* Read more? */ if (read < len) { /* Page size */ from += mtd->oobblock; column = 0; } }out: /* Deselect and wake up anyone waiting on the device */ onenand_release_device(mtd); *retlen = read; return ret;}#ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE/** * onenand_verify_page - [GENERIC] verify the chip contents after a write * @param mtd MTD device structure * @param buf the databuffer to verify * @param block block address * @param page page address * * Check DataRAM area directly */static int onenand_verify_page(struct mtd_info *mtd, u_char *buf, loff_t addr, int block, int page){ struct onenand_chip *this = mtd->priv; void __iomem *dataram0, *dataram1; int ret = 0; this->command(mtd, ONENAND_CMD_READ, addr, mtd->oobblock); ret = this->wait(mtd, FL_READING); if (ret) return ret; onenand_update_bufferram(mtd, addr, 1); /* Check, if the two dataram areas are same */ dataram0 = this->base + ONENAND_DATARAM; dataram1 = dataram0 + mtd->oobblock; if (memcmp(dataram0, dataram1, mtd->oobblock)) return -EBADMSG; return 0;}#else#define onenand_verify_page(...) (0)#endif#define NOTALIGNED(x) ((x & (mtd->oobblock - 1)) != 0)/** * onenand_write_ecc - [MTD Interface] OneNAND write with ECC * @param mtd MTD device structure * @param to offset to write to * @param len number of bytes to write * @param retlen pointer to variable to store the number of written bytes * @param buf the data to write * @param eccbuf filesystem supplied oob data buffer * @param oobsel oob selection structure * * OneNAND write with ECC */static int onenand_write_ecc(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf, u_char *eccbuf, struct nand_oobinfo *oobsel){ struct onenand_chip *this = mtd->priv; int written = 0; int ret = 0; DEBUG(MTD_DEBUG_LEVEL3, "onenand_write_ecc: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len); /* Initialize retlen, in case of early exit */ *retlen = 0; /* Do not allow writes past end of device */ if (unlikely((to + len) > mtd->size)) { DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_ecc: Attempt write to past end of device\n"); return -EINVAL; } /* Reject writes, which are not page aligned */ if (unlikely(NOTALIGNED(to)) || unlikely(NOTALIGNED(len))) { DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_ecc: Attempt to write not page aligned data\n"); return -EINVAL; } /* Grab the lock and see if the device is available */ onenand_get_device(mtd, FL_WRITING); /* Loop until all data write */ while (written < len) { int thislen = min_t(int, mtd->oobblock, len - written); this->command(mtd, ONENAND_CMD_BUFFERRAM, to, mtd->oobblock); this->write_bufferram(mtd, ONENAND_DATARAM, buf, 0, thislen); this->write_bufferram(mtd, ONENAND_SPARERAM, ffchars, 0, mtd->oobsize); this->command(mtd, ONENAND_CMD_PROG, to, mtd->oobblock); onenand_update_bufferram(mtd, to, 1); ret = this->wait(mtd, FL_WRITING); if (ret) { DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_ecc: write filaed %d\n", ret); goto out; } written += thislen; /* Only check verify write turn on */ ret = onenand_verify_page(mtd, (u_char *) buf, to, block, page); if (ret) { DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_ecc: verify failed %d\n", ret); goto out; } if (written == len) break; to += thislen; buf += thislen; }out: /* Deselect and wake up anyone waiting on the device */ onenand_release_device(mtd); *retlen = written; return ret;}/** * onenand_write - [MTD Interface] compability function for onenand_write_ecc * @param mtd MTD device structure * @param to offset to write to * @param len number of bytes to write * @param retlen pointer to variable to store the number of written bytes * @param buf the data to write * * This function simply calls onenand_write_ecc * with oob buffer and oobsel = NULL */static int onenand_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf){ return onenand_write_ecc(mtd, to, len, retlen, buf, NULL, NULL);}/** * onenand_write_oob - [MTD Interface] OneNAND write out-of-band * @param mtd MTD device structure * @param to offset to write to * @param len number of bytes to write * @param retlen pointer to variable to store the number of written bytes * @param buf the data to write * * OneNAND write out-of-band */static int onenand_write_oob(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf){ struct onenand_chip *this = mtd->priv; int column, status; int written = 0; DEBUG(MTD_DEBUG_LEVEL3, "onenand_write_oob: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len); /* Initialize retlen, in case of early exit */ *retlen = 0; /* Do not allow writes past end of device */ if (unlikely((to + len) > mtd->size)) { DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: Attempt write to past end of device\n"); return -EINVAL; } /* Grab the lock and see if the device is available */ onenand_get_device(mtd, FL_WRITING); /* Loop until all data write */ while (written < len) { int thislen = min_t(int, mtd->oobsize, len - written); column = to & (mtd->oobsize - 1); this->command(mtd, ONENAND_CMD_BUFFERRAM, to, mtd->oobsize); this->write_bufferram(mtd, ONENAND_SPARERAM, ffchars, 0, mtd->oobsize); this->write_bufferram(mtd, ONENAND_SPARERAM, buf, column, thislen); this->command(mtd, ONENAND_CMD_PROGOOB, to, mtd->oobsize); onenand_update_bufferram(mtd, to, 0); status = this->wait(mtd, FL_WRITING); if (status) goto out; written += thislen; if (written == len) break; to += thislen; buf += thislen; }out: /* Deselect and wake up anyone waiting on the device */ onenand_release_device(mtd); *retlen = written; return 0;}/** * onenand_writev_ecc - [MTD Interface] write with iovec with ecc * @param mtd MTD device structure * @param vecs the iovectors to write * @param count number of vectors * @param to offset to write to * @param retlen pointer to variable to store the number of written bytes * @param eccbuf filesystem supplied oob data buffer * @param oobsel oob selection structure * * OneNAND write with iovec with ecc */static int onenand_writev_ecc(struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen, u_char *eccbuf, struct nand_oobinfo *oobsel){ struct onenand_chip *this = mtd->priv; unsigned char buffer[mtd->oobblock], *pbuf; size_t total_len, len; int i, written = 0; int ret = 0; /* Preset written len for early exit */ *retlen = 0; /* Calculate total length of data */ total_len = 0; for (i = 0; i < count; i++) total_len += vecs[i].iov_len; DEBUG(MTD_DEBUG_LEVEL3, "onenand_writev_ecc: to = 0x%08x, len = %i, count = %ld\n", (unsigned int) to, (unsigned int) total_len, count); /* Do not allow write past end of the device */ if (unlikely((to + total_len) > mtd->size)) { DEBUG(MTD_DEBUG_LEVEL0, "onenand_writev_ecc: Attempted write past end of device\n"); return -EINVAL; } /* Reject writes, which are not page aligned */ if (unlikely(NOTALIGNED(to)) || unlikely(NOTALIGNED(total_len))) { DEBUG(MTD_DEBUG_LEVEL0, "onenand_writev_ecc: Attempt to write not page aligned data\n"); return -EINVAL; } /* Grab the lock and see if the device is available */ onenand_get_device(mtd, FL_WRITING); /* TODO handling oob */ /* Loop until all keve's data has been written */ len = 0; while (count) { pbuf = buffer; /* * If the given tuple is >= pagesize then * write it out from the iov */ if ((vecs->iov_len - len) >= mtd->oobblock) { pbuf = vecs->iov_base + len; len += mtd->oobblock; /* Check, if we have to switch to the next tuple */ if (len >= (int) vecs->iov_len) { vecs++; len = 0; count--; } } else { int cnt = 0, thislen; while (cnt < mtd->oobblock) { thislen = min_t(int, mtd->oobblock - cnt, vecs->iov_len - len); memcpy(buffer + cnt, vecs->iov_base + len, thislen); cnt += thislen; len += thislen; /* Check, if we have to switch to the next tuple */ if (len >= (int) vecs->iov_len) { vecs++; len = 0; count--; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -