📄 smc_core.c
字号:
/* Read in a block big enough for ECC */ for (j = 0; j < (mtd->oobblock + mtd->oobsize); j++) this->data_buf[j] = this->read_data(); for (j = 0; j < mtd->oobsize; j++) ecc_code[j] = this->data_buf[(mtd->oobblock + j)]; nand_calculate_ecc(&this->data_buf[0], &ecc_calc[0]); sm_swap(&ecc_calc[0], &ecc_calc[1]); DEBUG(MTD_DEBUG_LEVEL3, __FUNCTION__"(): ECC [%02x%02x%02x : %02x%02x%02x]\n", ecc_code[SMC_OOB_ECC1], ecc_code[SMC_OOB_ECC1+1], ecc_code[SMC_OOB_ECC1+2], ecc_calc[0], ecc_calc[1], ecc_calc[2]); ret = nand_correct_data(&this->data_buf[0], &(ecc_code[SMC_OOB_ECC1]), &ecc_calc[0]); if (ret == -1) return ret; nand_calculate_ecc(&this->data_buf[mtd->eccsize], &ecc_calc[0]); sm_swap(&ecc_calc[0], &ecc_calc[1]); DEBUG(MTD_DEBUG_LEVEL3, __FUNCTION__"(): ECC [%02x%02x%02x : %02x%02x%02x]\n", ecc_code[SMC_OOB_ECC2], ecc_code[SMC_OOB_ECC2+1], ecc_code[SMC_OOB_ECC2+2], ecc_calc[0], ecc_calc[1], ecc_calc[2]); ret = nand_correct_data(&this->data_buf[mtd->eccsize], &(ecc_code[SMC_OOB_ECC2]), &ecc_calc[0]); if (ret == -1) return ret; return 0;}/* * NAND read with ECC */static intnand_read_ecc(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf, u_char *ecc_code){ int j, offset, page; struct nand_chip *this = mtd->priv; int ret; DEBUG(MTD_DEBUG_LEVEL3, __FUNCTION__ "(): 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, __FUNCTION__ "(): Attempt read beyond end of device\n"); *retlen = 0; return -EINVAL; } /* Select the NAND device */ nand_select(); /* Initialize return value */ *retlen = 0;#ifdef USE_256BYTE_NAND_FLASH /* First we calculate the starting page */ page = from >> this->page_shift; /* Get raw starting column */ offset = from & (mtd->oobblock - 1); /* if the length of Page is 256bytes, * 2 Pages must be taken for 1 Sector and as a result, * higher level 8 bytes of information * among the above 16 byte information must coincide with * Spare(or OOB) of Even-Page while lowel level 8 bytes * coincide with Spar(or OOB) of Odd-page. * i.e, [0 block][oob][1 block][oob] * [2 block][oob][3 block][oob]... */ if (mtd->oobblock == 256) { u_char ecc_calc[3]; int oob_offset; /* Loop until all data read */ while (*retlen < len) { this->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page); this->wait_for_ready(); /* Read in a block big enough for ECC */ for (j = 0; j < mtd->eccsize; j++) this->data_buf[j] = this->read_data(); if (!(page & 0x1)) { /* page is odd! */ this->cmdfunc(mtd, NAND_CMD_READOOB, SMC_OOB256_ECC1, page + 1); oob_offset = SMC_OOB_ECC1; } else { this->cmdfunc(mtd, NAND_CMD_READOOB, SMC_OOB256_ECC2, page); oob_offset = SMC_OOB_ECC2; } this->wait_for_ready(); for (j = 0; j < 3; j++) ecc_code[oob_offset + j] = this->read_data(); nand_calculate_ecc(&this->data_buf[0], &ecc_calc[0]); sm_swap(&ecc_calc[0], &ecc_calc[1]); ret = nand_correct_data(&this->data_buf[0], &(ecc_code[oob_offset]), &ecc_calc[0]); if (ret == -1) goto nand_read_ecc_err; /* Read the data from ECC data buffer into return buffer */ if ((*retlen + (mtd->eccsize - offset)) > = len) { while (*retlen < len) buf[(*retlen)++] = this->data_buf[offset++]; /* We're done */ continue; } else { for (j = offset; j < mtd->eccsize; j++) buf[(*retlen)++] = this->data_buf[j]; } /* * If the amount of data to be read is greater than * (256 - offset), then all subsequent reads will take * place on page or half-page (in the case of 512 byte * page devices) aligned boundaries and the column * address will be zero. Setting the column address to * to zero after the first read allows us to simplify * the reading of data and the if/else statements above. */ if (offset) offset = 0x00; /* Increment page address */ page++; } } else #endif /* USE_256BYTE_NAND_FLASH */ { /* mtd->oobblock == 512 */ size_t last, next, len2; last = from + len; for (next = from; from < last; ) { page = from >> this->page_shift; offset = from & (mtd->oobblock - 1); len2 = mtd->oobblock - offset; next += len2; this->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page); this->wait_for_ready(); ret = smc_read_ecc_512(mtd, ecc_code); if (ret == -1) goto nand_read_ecc_err; if (next >= last) if ((last & (mtd->oobblock - 1)) != 0) len2 = (last & (mtd->oobblock - 1)) - offset; for (j = 0; j < len2; j++) buf[(*retlen) + j] = this->data_buf[offset + j]; *retlen += len2; from = next; } } ret = 0; /* De-select the NAND device */ nand_deselect(); return ret;nand_read_ecc_err: DEBUG(MTD_DEBUG_LEVEL0, __FUNCTION__"(): Failed ECC read, page 0x%08lx\n", page); return -EIO;}/* * NAND read out-of-band */static intnand_read_oob(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf){ int i, offset, page; struct nand_chip *this = mtd->priv; DEBUG(MTD_DEBUG_LEVEL3, __FUNCTION__ "(): from = 0x%08x, len = %i\n", (unsigned int)from, (int)len); /* Shift to get page */ page = ((int)from) >> this->page_shift; /* Mask to get column */ offset = from & (mtd->oobsize-1); /* Initialize return length value */ *retlen = 0; /* Do not allow read past end of page */ if ((offset + len) > mtd->oobsize) { DEBUG(MTD_DEBUG_LEVEL0, __FUNCTION__"(): Attempt read past end of page " \ "0x%08lx, column %i, length %i\n", page, offset, len); return -EINVAL; } /* Select the NAND device */ nand_select(); /* Send the read command */ this->cmdfunc(mtd, NAND_CMD_READOOB, offset, page); this->wait_for_ready(); /* Read the data */ for (i = 0; i < len; i++) buf[i] = this->read_data(); /* De-select the NAND device */ nand_deselect(); /* Return happy */ *retlen = len; return 0;}/* * NAND write */static intnand_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf){ int i, page, col, cnt, status; struct nand_chip *this = mtd->priv; DEBUG(MTD_DEBUG_LEVEL3, __FUNCTION__"(): to = 0x%08x, len = %i\n", (unsigned int)to, (int) len); /* Do not allow write past end of page */ if ((to + len) > mtd->size) { DEBUG(MTD_DEBUG_LEVEL0, __FUNCTION__"(): Attempted write past end of device\n"); return -EINVAL; } /* Shift to get page */ page = ((int)to) >> this->page_shift; /* Get the starting column */ col = to & (mtd->oobblock - 1); /* Initialize return length value */ *retlen = 0; /* Select the NAND device */ nand_select(); /* Check the WP bit */ this->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); this->wait_for_ready(); if (!(this->read_data() & SMC_STAT_NOT_WP)) { DEBUG(MTD_DEBUG_LEVEL0, __FUNCTION__"(): Device is write protected!!!\n"); i = -EPERM; goto nand_write_exit; } /* Loop until all data is written */ while (*retlen < len) { /* Write data into buffer */ if ((col + len) >= mtd->oobblock) for (i = col, cnt = 0; i < mtd->oobblock; i++, cnt++) this->data_buf[i] = buf[(*retlen + cnt)]; else for (i = col, cnt = 0; cnt < (len - *retlen); i++, cnt++) this->data_buf[i] = buf[(*retlen + cnt)]; /* Write ones for partial page programming */ for (i = mtd->oobblock; i < (mtd->oobblock + mtd->oobsize); i++) this->data_buf[i] = 0xff; /* Write pre-padding bytes into buffer */ for (i = 0; i < col; i++) this->data_buf[i] = 0xff; /* Write post-padding bytes into buffer */ if ((col + (len - *retlen)) < mtd->oobblock) { for (i = (col + cnt); i < mtd->oobblock; i++) this->data_buf[i] = 0xff; } /* Send command to begin auto page programming */ this->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page); /* Write out complete page of data */ this->hwcontrol(NAND_CTL_DAT_OUT); for (i = 0; i < (mtd->oobblock + mtd->oobsize); i++) this->write_data(this->data_buf[i]); this->hwcontrol(NAND_CTL_DAT_IN);#if defined(CONFIG_ARCH_S3C2440) this->hwcontrol(NAND_CTL_CLRRnB);#endif /* Send command to actually program the data */ this->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); this->wait_for_ready(); /* * Wait for program operation to complete. This could * take up to 3000us (3ms) on some devices. so we try * and exit as quickly as possible. */ status = 0; for (i = 0; i < 24; i++) { /* Delay for 125us */ udelay(125); /* Check for status */ this->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); status = (int)this->read_data(); if (status & SMC_STAT_READY) break; } /* See if device thinks it succeeded */ if (status & SMC_STAT_WRITE_ERR) { DEBUG(MTD_DEBUG_LEVEL0, __FUNCTION__"(): Failed write, page 0x%08x, " \ "%6i bytes were sucesful\n", page, *retlen); i = -EIO; goto nand_write_exit; }#ifdef CONFIG_MTD_NAND_VERIFY_WRITE /* * The NAND device assumes that it is always writing to * a cleanly erased page. Hence, it performs its internal * write verification only on bits that transitioned from * 1 to 0. The device does NOT verify the whole page on * a byte by byte basis. It is possible that the page was * not completely erased or the page is becoming unusable * due to wear. The read with ECC would catch the error * later when the ECC page check fails, but we would rather * catch it early in the page write stage. Better to write * no data than invalid data. */ /* Send command to read back the page */ if (col < mtd->eccsize) this->cmdfunc(mtd, NAND_CMD_READ0, col, page); else this->cmdfunc(mtd, NAND_CMD_READ1, col - 256, page); this->wait_for_ready(); /* Loop through and verify the data */ for (i = col; i < cnt; i++) { if (this->data_buf[i] != this->read_data()) { DEBUG(MTD_DEBUG_LEVEL0, __FUNCTION__"(): Failed write verify, " \ "page 0x%08x, %6i bytes were succesful\n", page, *retlen); i = -EIO; goto nand_write_exit; } }#endif /* * If we are writing a large amount of data and/or it * crosses page or half-page boundaries, we set the * the column to zero. It simplifies the program logic. */ if (col) col = 0x00; /* Update written bytes count */ *retlen += cnt; /* Increment page address */ page++; } /* Return happy */ *retlen = len; i = 0;nand_write_exit: /* De-select the NAND device */ nand_deselect(); return i;}/* * NAND write witdh ECC, but only 1 sector! */static intnand_write_ecc(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf, u_char *ecc_code){ int i, page, cnt, status, ret; struct nand_chip *this = mtd->priv; unsigned int /*sector_size,*/ page_size, oob_size; DEBUG(MTD_DEBUG_LEVEL3, __FUNCTION__"(): to = 0x%08x, len = %i\n", (unsigned int)to, (int)len); //sector_size = this->dev->szS; page_size = mtd->oobblock; oob_size = mtd->oobsize; /*if (to & (sector_size - 1)) { DEBUG(MTD_DEBUG_LEVEL0, __FUNCTION__"(): Not Sector aligned\n"); return -EINVAL; } if (len != sector_size) { DEBUG(MTD_DEBUG_LEVEL0, __FUNCTION__"(): Only 1 Sector\n"); return -EINVAL; }*/ /* Do not allow write past end of page */ if ((to + len) > mtd->size) { DEBUG(MTD_DEBUG_LEVEL0, __FUNCTION__"(): Attempted write past end of device\n"); return -EINVAL; } /* Shift to get page */ page = ((int) to) >> this->page_shift; /* Initialize return length value */ *retlen = 0; /* Select the NAND device */ nand_select(); /* Check the WP bit */ this->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); this->wait_for_ready(); if (!(this->read_data() & SMC_STAT_NOT_WP)) { DEBUG(MTD_DEBUG_LEVEL0, __FUNCTION__"(): Device is write protected!!!\n"); ret = -EPERM; goto nand_write_err; } /* Loop until all data is written */ while (*retlen < len) { /* Send command to begin auto page programming */ this->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page); this->hwcontrol(NAND_CTL_DAT_OUT); /* Write out complete page of data */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -