/* *The TMIO controller converts an 8-bit NAND interface to a 16-bit *bus interface, so all data reads and writes must be 16-bit wide. *Thus, we implement 16-bit versions of the read, write, and verify *buffer functions. */ static void tmio_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len) { struct tmio_nand *tmio = mtd_to_tmio(mtd); tmio_iowrite16_rep(tmio->fcr + FCR_DATA, buf, len >> 1); }
/* *The TMIO core has a RDYREQ interrupt on the posedge of #SMRB. *This interrupt is normally disabled, but for long operations like *erase and write, we enable it to wake us up. The irq handler *disables the interrupt. */ static int tmio_nand_wait(struct mtd_info *mtd, struct nand_chip *nand_chip) { struct tmio_nand *tmio = mtd_to_tmio(mtd); long timeout; /* enable RDYREQ interrupt */ tmio_iowrite8(0x0f, tmio->fcr + FCR_ISR); tmio_iowrite8(0x81, tmio->fcr + FCR_IMR); timeout = wait_event_timeout(nand_chip->controller->wq, tmio_nand_dev_ready(mtd), msecs_to_jiffies(nand_chip->state == FL_ERASING ? 400 : 20)); if (unlikely(!tmio_nand_dev_ready(mtd))) { tmio_iowrite8(0x00, tmio->fcr + FCR_IMR); dev_warn(&tmio->dev->dev, "still busy with %s after %d ms\n", nand_chip->state == FL_ERASING ? "erase" : "program", nand_chip->state == FL_ERASING ? 400 : 20); } else if (unlikely(!timeout)) { tmio_iowrite8(0x00, tmio->fcr + FCR_IMR); dev_warn(&tmio->dev->dev, "timeout waiting for interrupt\n"); } nand_chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); return nand_chip->read_byte(mtd); }
static void tmio_nand_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl) { struct tmio_nand *tmio = mtd_to_tmio(mtd); struct nand_chip *chip = mtd->priv; if (ctrl & NAND_CTRL_CHANGE) { u8 mode; if (ctrl & NAND_NCE) { mode = FCR_MODE_DATA; if (ctrl & NAND_CLE) mode |= FCR_MODE_CLE; else mode &= ~FCR_MODE_CLE; if (ctrl & NAND_ALE) mode |= FCR_MODE_ALE; else mode &= ~FCR_MODE_ALE; } else { mode = FCR_MODE_STANDBY; } tmio_iowrite8(mode, tmio->fcr + FCR_MODE); tmio->read_good = 0; } if (cmd != NAND_CMD_NONE) tmio_iowrite8(cmd, chip->IO_ADDR_W); }
static void tmio_nand_enable_hwecc(struct mtd_info *mtd, int mode) { struct tmio_nand *tmio = mtd_to_tmio(mtd); tmio_iowrite8(FCR_MODE_HWECC_RESET, tmio->fcr + FCR_MODE); tmio_ioread8(tmio->fcr + FCR_DATA); /* dummy read */ tmio_iowrite8(FCR_MODE_HWECC_CALC, tmio->fcr + FCR_MODE); }
static int tmio_nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len) { struct tmio_nand *tmio = mtd_to_tmio(mtd); u16 *p = (u16 *) buf; for (len >>= 1; len; len--) if (*(p++) != tmio_ioread16(tmio->fcr + FCR_DATA)) return -EFAULT; return 0; }
/* *The TMIO controller combines two 8-bit data bytes into one 16-bit *word. This function separates them so nand_base.c works as expected, *especially its NAND_CMD_READID routines. * *To prevent stale data from being read, tmio_nand_hwcontrol() clears *tmio->read_good. */ static u_char tmio_nand_read_byte(struct mtd_info *mtd) { struct tmio_nand *tmio = mtd_to_tmio(mtd); unsigned int data; if (tmio->read_good--) return tmio->read; data = tmio_ioread16(tmio->fcr + FCR_DATA); tmio->read = data >> 8; return data; }
static int tmio_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code) { struct tmio_nand *tmio = mtd_to_tmio(mtd); unsigned int ecc; tmio_iowrite8(FCR_MODE_HWECC_RESULT, tmio->fcr + FCR_MODE); ecc = tmio_ioread16(tmio->fcr + FCR_DATA); ecc_code[1] = ecc; /* 000-255 LP7-0 */ ecc_code[0] = ecc >> 8; /* 000-255 LP15-8 */ ecc = tmio_ioread16(tmio->fcr + FCR_DATA); ecc_code[2] = ecc; /* 000-255 CP5-0,11b */ ecc_code[4] = ecc >> 8; /* 256-511 LP7-0 */ ecc = tmio_ioread16(tmio->fcr + FCR_DATA); ecc_code[3] = ecc; /* 256-511 LP15-8 */ ecc_code[5] = ecc >> 8; /* 256-511 CP5-0,11b */ tmio_iowrite8(FCR_MODE_DATA, tmio->fcr + FCR_MODE); return 0; }
static void tmio_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) { struct tmio_nand *tmio = mtd_to_tmio(mtd); tmio_ioread16_rep(tmio->fcr + FCR_DATA, buf, len >> 1); }
static int tmio_nand_dev_ready(struct mtd_info *mtd) { struct tmio_nand *tmio = mtd_to_tmio(mtd); return !(tmio_ioread8(tmio->fcr + FCR_STATUS) & FCR_STATUS_BUSY); }