📄 smc_core.c
字号:
/* * vivi/drivers/mtd/nand/smc_core.c: * * Based on linux/drivers/mtd/nand/smc.c * * $Id: smc_core.c,v 1.3 2002/08/10 07:47:08 nandy Exp $ * * * 棱淬: * 恐 this->oobblock捞扼绊 沁阑鳖? thsi->oobblcok_ofs捞扼绊 窍搁 * 粱歹 疙犬窍瘤 臼阑鳖? */#include "config.h"#include "machine.h"#include "mtd/mtd.h"#include "mtd/nand.h"#include "mtd/nand_ids.h"#include "mtd/nand_ecc.h"#include "time.h"#include "printk.h"#include <types.h>#include <errno.h>/* * Macros for low-level register control */#if defined(CONFIG_ARCH_S3C2410)#define nand_select() this->hwcontrol(NAND_CTL_SETNCE); \ this->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); \ udelay(10);#elif defined(CONFIG_ARCH_S3C2440)#if 0#define NF_CLEAR_RB() {NFSTAT |= NFSTAT_RnB;}#define NF_DETECT_RB() {while(!(NFSTAT & NFSTAT_RnB));}#define nand_select() \ do \ {\ this->hwcontrol(NAND_CTL_SETNCE); \ nand_command(mtd, NAND_CMD_RESET, -1, -1); \ NF_DETECT_RB();\ NF_CLEAR_RB();\ udelay(5);\ }\ while (0)#else#define nand_select() \ do \ {\ this->hwcontrol(NAND_CTL_SETNCE); \ nand_command(mtd, NAND_CMD_RESET, -1, -1); \ udelay(5); \ }\ while (0)#endif#else#error must defined CONFIG_ARCH_S3C2410 or CONFIG_ARCH_S3C2440#endif#define nand_deselect() this->hwcontrol(NAND_CTL_CLRNCE);static inline void sm_swap(u_char *x, u_char *y) { u_char tmp; tmp = *x; *x = *y; *y = tmp;}/* define if you'll be using < 2M SMC device */#undef USE_256BYTE_NAND_FLASH/* * Send command to NAND device */static voidnand_command(struct mtd_info *mtd, unsigned command, int column, int page_addr){ register struct nand_chip *this = mtd->priv; /* Begin command latch cycle */ this->hwcontrol(NAND_CTL_SETCLE); this->hwcontrol(NAND_CTL_DAT_OUT); /* * Write out the command to the device. */ if (command != NAND_CMD_SEQIN) this->write_cmd(command); else { if (mtd->oobblock == 256 && column >= 256) { column -= 256; this->write_cmd(NAND_CMD_RESET); this->write_cmd(NAND_CMD_READOOB); this->write_cmd(NAND_CMD_SEQIN); } else if (mtd->oobblock == 512 && column >= 256) { if (column < 512) { column -= 256; this->write_cmd(NAND_CMD_READ1); this->write_cmd(NAND_CMD_SEQIN); } else { column -= 512; this->write_cmd(NAND_CMD_READOOB); this->write_cmd(NAND_CMD_SEQIN); } } else { this->write_cmd(NAND_CMD_READ0); this->write_cmd(NAND_CMD_SEQIN); } } /* Set ALE and clear CLE to start address cycle */ this->hwcontrol(NAND_CTL_CLRCLE); this->hwcontrol(NAND_CTL_SETALE); /* Serially input address */ if (column != -1) this->write_addr(column); if (page_addr != -1) { this->write_addr((u_char)(page_addr & 0xff)); this->write_addr((u_char)((page_addr >> 8) & 0xff)); /* One more address cycle for higher density devices */ if (mtd->size & 0x0c000000) { this->write_addr((u_char)((page_addr >> 16) & 0xff)); } } /* Latch in address */ this->hwcontrol(NAND_CTL_CLRALE); this->hwcontrol(NAND_CTL_DAT_IN); /* Pause for 15us */ udelay(20); }/* * Send command to large page NAND device */static voidnand_command_lp(struct mtd_info *mtd, unsigned command, int column, int page_addr){ register struct nand_chip *this = mtd->priv; /* Emulate NAND_CMD_READOOB */ if (command == NAND_CMD_READOOB) { column += mtd->oobblock; command = NAND_CMD_READ0; } /* Begin command latch cycle */ this->hwcontrol(NAND_CTL_SETCLE); /* Write out the command to the device. */ this->write_cmd(command & 0xff); /* End command latch cycle */ this->hwcontrol(NAND_CTL_CLRCLE); if (column != -1 || page_addr != -1) { this->hwcontrol(NAND_CTL_SETALE); /* Serially input address */ if (column != -1) { /* Adjust columns for 16 bit buswidth */ //if (this->options & NAND_BUSWIDTH_16) // column >>= 1; this->write_addr(column & 0xff); this->write_addr(column >> 8); } if (page_addr != -1) { this->write_addr((unsigned char) (page_addr & 0xff)); this->write_addr((unsigned char) ((page_addr >> 8) & 0xff)); /* One more address cycle for devices > 128MiB */ //if (this->chipsize > (128 << 20)) if (mtd->size > (128 << 20)) this->write_addr((unsigned char) ((page_addr >> 16) & 0xff)); } /* Latch in address */ this->hwcontrol(NAND_CTL_CLRALE); } /* * program and erase have their own busy handlers * status, sequential in, and deplete1 need no delay */ switch (command) { case NAND_CMD_CACHEDPROG: case NAND_CMD_PAGEPROG: case NAND_CMD_ERASE1: case NAND_CMD_ERASE2: case NAND_CMD_SEQIN: case NAND_CMD_STATUS: //case NAND_CMD_DEPLETE1: return; /* * read error status commands require only a short delay */ //case NAND_CMD_STATUS_ERROR: //case NAND_CMD_STATUS_ERROR0: //case NAND_CMD_STATUS_ERROR1: //case NAND_CMD_STATUS_ERROR2: //case NAND_CMD_STATUS_ERROR3: // udelay(this->chip_delay); // return; case NAND_CMD_RESET: //if (this->dev_ready) // break; //udelay(this->chip_delay); this->hwcontrol(NAND_CTL_SETCLE); this->write_cmd(NAND_CMD_STATUS); this->hwcontrol(NAND_CTL_CLRCLE); while ( !(this->read_data() & NAND_STATUS_READY)); return; case NAND_CMD_READ0: /* Begin command latch cycle */ this->hwcontrol(NAND_CTL_SETCLE); /* Write out the start read command */ this->write_cmd(NAND_CMD_READSTART); /* End command latch cycle */ this->hwcontrol(NAND_CTL_CLRCLE); /* Fall through into ready check */ /* This applies to read commands */ default: /* * If we don't have access to the busy pin, we apply the given * command delay */ //if (!this->dev_ready) { // udelay (this->chip_delay); // return; //} } /* Apply this short delay always to ensure that we do wait tWB in * any case on any machine. */ udelay(20);}/* * NAND read */static intnand_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf){ int j, col, page, state; struct nand_chip *this = mtd->priv; DEBUG(MTD_DEBUG_LEVEL3, __FUNCTION__"(): from = 0x%08lx, 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; } /* First we calculate the starting page */ page = from >> this->page_shift; /* Get raw starting column */ col = from & (mtd->oobblock - 1); /* State machine for devices having pages larger than 256 bytes */ state = (col < mtd->eccsize) ? 0 : 1; /* Calculate column address within ECC block context */ col = (col >= mtd->eccsize) ? (col - mtd->eccsize) : col; /* Initialize return value */ *retlen = 0; /* Select the NAND device */ nand_select(); /* Loop until all data read */ while (*retlen < len) { /* Send the read command */ if (!state) this->cmdfunc(mtd, NAND_CMD_READ0, col, page); else this->cmdfunc(mtd, NAND_CMD_READ1, col, page); this->wait_for_ready(); /* Read the data directly into the return buffer */ if ((*retlen + (mtd->eccsize - col)) >= len) { while (*retlen < len) buf[(*retlen)++] = this->read_data(); /* We're done */ continue; } else { for (j = col; j < mtd->eccsize; j++) buf[(*retlen)++] = this->read_data(); } /* * If the amount of data to be read is greater than * (256 - col), 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 * zero after the first read allows us to simplify * the reading of data and the if/else statements above. */ if (col) col = 0x00; /* Increment page address */ if ((mtd->oobblock == 256) || state) page++; /* Toggle state machine */ if (mtd->oobblock == 512) state = state ? 0 : 1; } /* De-select the NAND device */ nand_deselect(); /* Return happy */ return 0; }/* * if mtd->oobblock == 512 */inline intsmc_read_ecc_512(struct mtd_info *mtd, u_char *ecc_code){ struct nand_chip *this = mtd->priv; u_char ecc_calc[3]; int j, ret; /* 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
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -