staticvoid s3c_onenand_reset(void)
{ unsignedlong timeout = 0x10000; int stat;
s3c_write_reg(ONENAND_MEM_RESET_COLD, MEM_RESET_OFFSET); while (1 && timeout--) {
stat = s3c_read_reg(INT_ERR_STAT_OFFSET); if (stat & RST_CMP) break;
}
stat = s3c_read_reg(INT_ERR_STAT_OFFSET);
s3c_write_reg(stat, INT_ERR_ACK_OFFSET);
/* Clear interrupt */
s3c_write_reg(0x0, INT_ERR_ACK_OFFSET); /* Clear the ECC status */
s3c_write_reg(0x0, ECC_ERR_STAT_OFFSET);
}
staticunsignedshort s3c_onenand_readw(void __iomem *addr)
{ struct onenand_chip *this = onenand->mtd->priv; struct device *dev = &onenand->pdev->dev; int reg = addr - this->base; int word_addr = reg >> 1; int value;
/* It's used for probing time */ switch (reg) { case ONENAND_REG_MANUFACTURER_ID: return s3c_read_reg(MANUFACT_ID_OFFSET); case ONENAND_REG_DEVICE_ID: return s3c_read_reg(DEVICE_ID_OFFSET); case ONENAND_REG_VERSION_ID: return s3c_read_reg(FLASH_VER_ID_OFFSET); case ONENAND_REG_DATA_BUFFER_SIZE: return s3c_read_reg(DATA_BUF_SIZE_OFFSET); case ONENAND_REG_TECHNOLOGY: return s3c_read_reg(TECH_OFFSET); case ONENAND_REG_SYS_CFG1: return s3c_read_reg(MEM_CFG_OFFSET);
/* Used at unlock all status */ case ONENAND_REG_CTRL_STATUS: return 0;
case ONENAND_REG_WP_STATUS: return ONENAND_WP_US;
default: break;
}
/* BootRAM access control */ if ((unsignedlong)addr < ONENAND_DATARAM && onenand->bootram_command) { if (word_addr == 0) return s3c_read_reg(MANUFACT_ID_OFFSET); if (word_addr == 1) return s3c_read_reg(DEVICE_ID_OFFSET); if (word_addr == 2) return s3c_read_reg(FLASH_VER_ID_OFFSET);
}
value = s3c_read_cmd(CMD_MAP_11(onenand, word_addr)) & 0xffff;
dev_info(dev, "%s: Illegal access at reg 0x%x, value 0x%x\n", __func__,
word_addr, value); return value;
}
switch (state) { case FL_READING:
flags |= BLK_RW_CMP | LOAD_CMP; break; case FL_WRITING:
flags |= BLK_RW_CMP | PGM_CMP; break; case FL_ERASING:
flags |= BLK_RW_CMP | ERS_CMP; break; case FL_LOCKING:
flags |= BLK_RW_CMP; break; default: break;
}
/* The 20 msec is enough */
timeout = jiffies + msecs_to_jiffies(20); while (time_before(jiffies, timeout)) {
stat = s3c_read_reg(INT_ERR_STAT_OFFSET); if (stat & flags) break;
if (state != FL_READING)
cond_resched();
} /* To get correct interrupt status in timeout case */
stat = s3c_read_reg(INT_ERR_STAT_OFFSET);
s3c_write_reg(stat, INT_ERR_ACK_OFFSET);
/* * In the Spec. it checks the controller status first * However if you get the correct information in case of * power off recovery (POR) test, it should read ECC status first
*/ if (stat & LOAD_CMP) {
ecc = s3c_read_reg(ECC_ERR_STAT_OFFSET); if (ecc & ONENAND_ECC_4BIT_UNCORRECTABLE) {
dev_info(dev, "%s: ECC error = 0x%04x\n", __func__,
ecc);
mtd->ecc_stats.failed++; return -EBADMSG;
}
}
switch (cmd) { case ONENAND_CMD_READ: /* Main */ for (i = 0; i < mcount; i++)
*m++ = s3c_read_cmd(cmd_map_01); return 0;
case ONENAND_CMD_READOOB:
s3c_write_reg(TSRF, TRANS_SPARE_OFFSET); /* Main */ for (i = 0; i < mcount; i++)
*m++ = s3c_read_cmd(cmd_map_01);
/* Spare */ for (i = 0; i < scount; i++)
*s++ = s3c_read_cmd(cmd_map_01);
s3c_write_reg(0, TRANS_SPARE_OFFSET); return 0;
case ONENAND_CMD_PROG: /* Main */ for (i = 0; i < mcount; i++)
s3c_write_cmd(*m++, cmd_map_01); return 0;
case ONENAND_CMD_PROGOOB:
s3c_write_reg(TSRF, TRANS_SPARE_OFFSET);
/* Main - dummy write */ for (i = 0; i < mcount; i++)
s3c_write_cmd(0xffffffff, cmd_map_01);
/* Spare */ for (i = 0; i < scount; i++)
s3c_write_cmd(*s++, cmd_map_01);
s3c_write_reg(0, TRANS_SPARE_OFFSET); return 0;
case ONENAND_CMD_UNLOCK_ALL:
s3c_write_cmd(ONENAND_UNLOCK_ALL, cmd_map_10); return 0;
case ONENAND_CMD_ERASE:
s3c_write_cmd(ONENAND_ERASE_START, cmd_map_10); return 0;
default: break;
}
return 0;
}
staticunsignedchar *s3c_get_bufferram(struct mtd_info *mtd, int area)
{ struct onenand_chip *this = mtd->priv; int index = ONENAND_CURRENT_BUFFERRAM(this); unsignedchar *p;
if (area == ONENAND_DATARAM) {
p = onenand->page_buf; if (index == 1)
p += this->writesize;
} else {
p = onenand->oob_buf; if (index == 1)
p += mtd->oobsize;
}
return p;
}
staticint onenand_read_bufferram(struct mtd_info *mtd, int area, unsignedchar *buffer, int offset,
size_t count)
{ unsignedchar *p;
p = s3c_get_bufferram(mtd, area);
memcpy(buffer, p + offset, count); return 0;
}
staticint onenand_write_bufferram(struct mtd_info *mtd, int area, constunsignedchar *buffer, int offset,
size_t count)
{ unsignedchar *p;
staticint (*s5pc110_dma_ops)(dma_addr_t dst, dma_addr_t src, size_t count, int direction);
staticint s5pc110_dma_poll(dma_addr_t dst, dma_addr_t src, size_t count, int direction)
{ void __iomem *base = onenand->dma_addr; int status; unsignedlong timeout;
writel(src, base + S5PC110_DMA_SRC_ADDR);
writel(dst, base + S5PC110_DMA_DST_ADDR);
if (direction == S5PC110_DMA_DIR_READ) {
writel(S5PC110_DMA_SRC_CFG_READ, base + S5PC110_DMA_SRC_CFG);
writel(S5PC110_DMA_DST_CFG_READ, base + S5PC110_DMA_DST_CFG);
} else {
writel(S5PC110_DMA_SRC_CFG_WRITE, base + S5PC110_DMA_SRC_CFG);
writel(S5PC110_DMA_DST_CFG_WRITE, base + S5PC110_DMA_DST_CFG);
}
writel(count, base + S5PC110_DMA_TRANS_SIZE);
writel(direction, base + S5PC110_DMA_TRANS_DIR);
writel(S5PC110_DMA_TRANS_CMD_TR, base + S5PC110_DMA_TRANS_CMD);
/* * There's no exact timeout values at Spec. * In real case it takes under 1 msec. * So 20 msecs are enough.
*/
timeout = jiffies + msecs_to_jiffies(20);
do {
status = readl(base + S5PC110_DMA_TRANS_STATUS); if (status & S5PC110_DMA_TRANS_STATUS_TE) {
writel(S5PC110_DMA_TRANS_CMD_TEC,
base + S5PC110_DMA_TRANS_CMD); return -EIO;
}
} while (!(status & S5PC110_DMA_TRANS_STATUS_TD) &&
time_before(jiffies, timeout));
writel(S5PC110_DMA_TRANS_CMD_TDC, base + S5PC110_DMA_TRANS_CMD);
if (likely(status & S5PC110_INTC_DMA_TD))
cmd = S5PC110_DMA_TRANS_CMD_TDC;
if (unlikely(status & S5PC110_INTC_DMA_TE))
cmd = S5PC110_DMA_TRANS_CMD_TEC;
writel(cmd, base + S5PC110_DMA_TRANS_CMD);
writel(status, base + S5PC110_INTC_DMA_CLR);
if (!onenand->complete.done)
complete(&onenand->complete);
return IRQ_HANDLED;
}
staticint s5pc110_dma_irq(dma_addr_t dst, dma_addr_t src, size_t count, int direction)
{ void __iomem *base = onenand->dma_addr; int status;
status = readl(base + S5PC110_INTC_DMA_MASK); if (status) {
status &= ~(S5PC110_INTC_DMA_TD | S5PC110_INTC_DMA_TE);
writel(status, base + S5PC110_INTC_DMA_MASK);
}
writel(src, base + S5PC110_DMA_SRC_ADDR);
writel(dst, base + S5PC110_DMA_DST_ADDR);
if (direction == S5PC110_DMA_DIR_READ) {
writel(S5PC110_DMA_SRC_CFG_READ, base + S5PC110_DMA_SRC_CFG);
writel(S5PC110_DMA_DST_CFG_READ, base + S5PC110_DMA_DST_CFG);
} else {
writel(S5PC110_DMA_SRC_CFG_WRITE, base + S5PC110_DMA_SRC_CFG);
writel(S5PC110_DMA_DST_CFG_WRITE, base + S5PC110_DMA_DST_CFG);
}
writel(count, base + S5PC110_DMA_TRANS_SIZE);
writel(direction, base + S5PC110_DMA_TRANS_DIR);
writel(S5PC110_DMA_TRANS_CMD_TR, base + S5PC110_DMA_TRANS_CMD);
/* The 20 msec is enough */
timeout = jiffies + msecs_to_jiffies(20); while (time_before(jiffies, timeout)) {
stat = s3c_read_reg(INT_ERR_STAT_OFFSET); if (stat & flags) break;
} /* To get correct interrupt status in timeout case */
stat = s3c_read_reg(INT_ERR_STAT_OFFSET);
s3c_write_reg(stat, INT_ERR_ACK_OFFSET);
if (stat & LD_FAIL_ECC_ERR) {
s3c_onenand_reset(); return ONENAND_BBT_READ_ERROR;
}
if (stat & LOAD_CMP) { int ecc = s3c_read_reg(ECC_ERR_STAT_OFFSET); if (ecc & ONENAND_ECC_4BIT_UNCORRECTABLE) {
s3c_onenand_reset(); return ONENAND_BBT_READ_ERROR;
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.