diff options
Diffstat (limited to 'drivers/mtd/nand/raw/au1550nd.c')
-rw-r--r-- | drivers/mtd/nand/raw/au1550nd.c | 403 |
1 files changed, 133 insertions, 270 deletions
diff --git a/drivers/mtd/nand/raw/au1550nd.c b/drivers/mtd/nand/raw/au1550nd.c index 75eb3e97fae3..d865200ccd08 100644 --- a/drivers/mtd/nand/raw/au1550nd.c +++ b/drivers/mtd/nand/raw/au1550nd.c @@ -16,63 +16,16 @@ struct au1550nd_ctx { + struct nand_controller controller; struct nand_chip chip; int cs; void __iomem *base; - void (*write_byte)(struct nand_chip *, u_char); }; -/** - * au_read_byte - read one byte from the chip - * @this: NAND chip object - * - * read function for 8bit buswidth - */ -static u_char au_read_byte(struct nand_chip *this) -{ - u_char ret = readb(this->legacy.IO_ADDR_R); - wmb(); /* drain writebuffer */ - return ret; -} - -/** - * au_write_byte - write one byte to the chip - * @this: NAND chip object - * @byte: pointer to data byte to write - * - * write function for 8it buswidth - */ -static void au_write_byte(struct nand_chip *this, u_char byte) -{ - writeb(byte, this->legacy.IO_ADDR_W); - wmb(); /* drain writebuffer */ -} - -/** - * au_read_byte16 - read one byte endianness aware from the chip - * @this: NAND chip object - * - * read function for 16bit buswidth with endianness conversion - */ -static u_char au_read_byte16(struct nand_chip *this) -{ - u_char ret = (u_char) cpu_to_le16(readw(this->legacy.IO_ADDR_R)); - wmb(); /* drain writebuffer */ - return ret; -} - -/** - * au_write_byte16 - write one byte endianness aware to the chip - * @this: NAND chip object - * @byte: pointer to data byte to write - * - * write function for 16bit buswidth with endianness conversion - */ -static void au_write_byte16(struct nand_chip *this, u_char byte) +static struct au1550nd_ctx *chip_to_au_ctx(struct nand_chip *this) { - writew(le16_to_cpu((u16) byte), this->legacy.IO_ADDR_W); - wmb(); /* drain writebuffer */ + return container_of(this, struct au1550nd_ctx, chip); } /** @@ -83,12 +36,15 @@ static void au_write_byte16(struct nand_chip *this, u_char byte) * * write function for 8bit buswidth */ -static void au_write_buf(struct nand_chip *this, const u_char *buf, int len) +static void au_write_buf(struct nand_chip *this, const void *buf, + unsigned int len) { + struct au1550nd_ctx *ctx = chip_to_au_ctx(this); + const u8 *p = buf; int i; for (i = 0; i < len; i++) { - writeb(buf[i], this->legacy.IO_ADDR_W); + writeb(p[i], ctx->base + MEM_STNAND_DATA); wmb(); /* drain writebuffer */ } } @@ -101,12 +57,15 @@ static void au_write_buf(struct nand_chip *this, const u_char *buf, int len) * * read function for 8bit buswidth */ -static void au_read_buf(struct nand_chip *this, u_char *buf, int len) +static void au_read_buf(struct nand_chip *this, void *buf, + unsigned int len) { + struct au1550nd_ctx *ctx = chip_to_au_ctx(this); + u8 *p = buf; int i; for (i = 0; i < len; i++) { - buf[i] = readb(this->legacy.IO_ADDR_R); + p[i] = readb(ctx->base + MEM_STNAND_DATA); wmb(); /* drain writebuffer */ } } @@ -119,17 +78,18 @@ static void au_read_buf(struct nand_chip *this, u_char *buf, int len) * * write function for 16bit buswidth */ -static void au_write_buf16(struct nand_chip *this, const u_char *buf, int len) +static void au_write_buf16(struct nand_chip *this, const void *buf, + unsigned int len) { - int i; - u16 *p = (u16 *) buf; - len >>= 1; + struct au1550nd_ctx *ctx = chip_to_au_ctx(this); + const u16 *p = buf; + unsigned int i; + len >>= 1; for (i = 0; i < len; i++) { - writew(p[i], this->legacy.IO_ADDR_W); + writew(p[i], ctx->base + MEM_STNAND_DATA); wmb(); /* drain writebuffer */ } - } /** @@ -140,239 +100,146 @@ static void au_write_buf16(struct nand_chip *this, const u_char *buf, int len) * * read function for 16bit buswidth */ -static void au_read_buf16(struct nand_chip *this, u_char *buf, int len) +static void au_read_buf16(struct nand_chip *this, void *buf, unsigned int len) { - int i; - u16 *p = (u16 *) buf; - len >>= 1; + struct au1550nd_ctx *ctx = chip_to_au_ctx(this); + unsigned int i; + u16 *p = buf; + len >>= 1; for (i = 0; i < len; i++) { - p[i] = readw(this->legacy.IO_ADDR_R); + p[i] = readw(ctx->base + MEM_STNAND_DATA); wmb(); /* drain writebuffer */ } } -/* Select the chip by setting nCE to low */ -#define NAND_CTL_SETNCE 1 -/* Deselect the chip by setting nCE to high */ -#define NAND_CTL_CLRNCE 2 -/* Select the command latch by setting CLE to high */ -#define NAND_CTL_SETCLE 3 -/* Deselect the command latch by setting CLE to low */ -#define NAND_CTL_CLRCLE 4 -/* Select the address latch by setting ALE to high */ -#define NAND_CTL_SETALE 5 -/* Deselect the address latch by setting ALE to low */ -#define NAND_CTL_CLRALE 6 - -static void au1550_hwcontrol(struct mtd_info *mtd, int cmd) +static int find_nand_cs(unsigned long nand_base) { - struct nand_chip *this = mtd_to_nand(mtd); - struct au1550nd_ctx *ctx = container_of(this, struct au1550nd_ctx, - chip); + void __iomem *base = + (void __iomem *)KSEG1ADDR(AU1000_STATIC_MEM_PHYS_ADDR); + unsigned long addr, staddr, start, mask, end; + int i; + + for (i = 0; i < 4; i++) { + addr = 0x1000 + (i * 0x10); /* CSx */ + staddr = __raw_readl(base + addr + 0x08); /* STADDRx */ + /* figure out the decoded range of this CS */ + start = (staddr << 4) & 0xfffc0000; + mask = (staddr << 18) & 0xfffc0000; + end = (start | (start - 1)) & ~(start ^ mask); + if ((nand_base >= start) && (nand_base < end)) + return i; + } - switch (cmd) { + return -ENODEV; +} - case NAND_CTL_SETCLE: - this->legacy.IO_ADDR_W = ctx->base + MEM_STNAND_CMD; - break; +static int au1550nd_waitrdy(struct nand_chip *this, unsigned int timeout_ms) +{ + unsigned long timeout_jiffies = jiffies; + + timeout_jiffies += msecs_to_jiffies(timeout_ms) + 1; + do { + if (alchemy_rdsmem(AU1000_MEM_STSTAT) & 0x1) + return 0; - case NAND_CTL_CLRCLE: - this->legacy.IO_ADDR_W = ctx->base + MEM_STNAND_DATA; + usleep_range(10, 100); + } while (time_before(jiffies, timeout_jiffies)); + + return -ETIMEDOUT; +} + +static int au1550nd_exec_instr(struct nand_chip *this, + const struct nand_op_instr *instr) +{ + struct au1550nd_ctx *ctx = chip_to_au_ctx(this); + unsigned int i; + int ret = 0; + + switch (instr->type) { + case NAND_OP_CMD_INSTR: + writeb(instr->ctx.cmd.opcode, + ctx->base + MEM_STNAND_CMD); + /* Drain the writebuffer */ + wmb(); break; - case NAND_CTL_SETALE: - this->legacy.IO_ADDR_W = ctx->base + MEM_STNAND_ADDR; + case NAND_OP_ADDR_INSTR: + for (i = 0; i < instr->ctx.addr.naddrs; i++) { + writeb(instr->ctx.addr.addrs[i], + ctx->base + MEM_STNAND_ADDR); + /* Drain the writebuffer */ + wmb(); + } break; - case NAND_CTL_CLRALE: - this->legacy.IO_ADDR_W = ctx->base + MEM_STNAND_DATA; - /* FIXME: Nobody knows why this is necessary, - * but it works only that way */ - udelay(1); + case NAND_OP_DATA_IN_INSTR: + if ((this->options & NAND_BUSWIDTH_16) && + !instr->ctx.data.force_8bit) + au_read_buf16(this, instr->ctx.data.buf.in, + instr->ctx.data.len); + else + au_read_buf(this, instr->ctx.data.buf.in, + instr->ctx.data.len); break; - case NAND_CTL_SETNCE: - /* assert (force assert) chip enable */ - alchemy_wrsmem((1 << (4 + ctx->cs)), AU1000_MEM_STNDCTL); + case NAND_OP_DATA_OUT_INSTR: + if ((this->options & NAND_BUSWIDTH_16) && + !instr->ctx.data.force_8bit) + au_write_buf16(this, instr->ctx.data.buf.out, + instr->ctx.data.len); + else + au_write_buf(this, instr->ctx.data.buf.out, + instr->ctx.data.len); break; - case NAND_CTL_CLRNCE: - /* deassert chip enable */ - alchemy_wrsmem(0, AU1000_MEM_STNDCTL); + case NAND_OP_WAITRDY_INSTR: + ret = au1550nd_waitrdy(this, instr->ctx.waitrdy.timeout_ms); break; + default: + return -EINVAL; } - this->legacy.IO_ADDR_R = this->legacy.IO_ADDR_W; - - wmb(); /* Drain the writebuffer */ -} - -int au1550_device_ready(struct nand_chip *this) -{ - return (alchemy_rdsmem(AU1000_MEM_STSTAT) & 0x1) ? 1 : 0; -} + if (instr->delay_ns) + ndelay(instr->delay_ns); -/** - * au1550_select_chip - control -CE line - * Forbid driving -CE manually permitting the NAND controller to do this. - * Keeping -CE asserted during the whole sector reads interferes with the - * NOR flash and PCMCIA drivers as it causes contention on the static bus. - * We only have to hold -CE low for the NAND read commands since the flash - * chip needs it to be asserted during chip not ready time but the NAND - * controller keeps it released. - * - * @this: NAND chip object - * @chip: chipnumber to select, -1 for deselect - */ -static void au1550_select_chip(struct nand_chip *this, int chip) -{ + return ret; } -/** - * au1550_command - Send command to NAND device - * @this: NAND chip object - * @command: the command to be sent - * @column: the column address for this command, -1 if none - * @page_addr: the page address for this command, -1 if none - */ -static void au1550_command(struct nand_chip *this, unsigned command, - int column, int page_addr) +static int au1550nd_exec_op(struct nand_chip *this, + const struct nand_operation *op, + bool check_only) { - struct mtd_info *mtd = nand_to_mtd(this); - struct au1550nd_ctx *ctx = container_of(this, struct au1550nd_ctx, - chip); - int ce_override = 0, i; - unsigned long flags = 0; - - /* Begin command latch cycle */ - au1550_hwcontrol(mtd, NAND_CTL_SETCLE); - /* - * Write out the command to the device. - */ - if (command == NAND_CMD_SEQIN) { - int readcmd; - - if (column >= mtd->writesize) { - /* OOB area */ - column -= mtd->writesize; - readcmd = NAND_CMD_READOOB; - } else if (column < 256) { - /* First 256 bytes --> READ0 */ - readcmd = NAND_CMD_READ0; - } else { - column -= 256; - readcmd = NAND_CMD_READ1; - } - ctx->write_byte(this, readcmd); - } - ctx->write_byte(this, command); + struct au1550nd_ctx *ctx = chip_to_au_ctx(this); + unsigned int i; + int ret; - /* Set ALE and clear CLE to start address cycle */ - au1550_hwcontrol(mtd, NAND_CTL_CLRCLE); + if (check_only) + return 0; - if (column != -1 || page_addr != -1) { - au1550_hwcontrol(mtd, NAND_CTL_SETALE); + /* assert (force assert) chip enable */ + alchemy_wrsmem((1 << (4 + ctx->cs)), AU1000_MEM_STNDCTL); + /* Drain the writebuffer */ + wmb(); - /* Serially input address */ - if (column != -1) { - /* Adjust columns for 16 bit buswidth */ - if (this->options & NAND_BUSWIDTH_16 && - !nand_opcode_8bits(command)) - column >>= 1; - ctx->write_byte(this, column); - } - if (page_addr != -1) { - ctx->write_byte(this, (u8)(page_addr & 0xff)); - - if (command == NAND_CMD_READ0 || - command == NAND_CMD_READ1 || - command == NAND_CMD_READOOB) { - /* - * NAND controller will release -CE after - * the last address byte is written, so we'll - * have to forcibly assert it. No interrupts - * are allowed while we do this as we don't - * want the NOR flash or PCMCIA drivers to - * steal our precious bytes of data... - */ - ce_override = 1; - local_irq_save(flags); - au1550_hwcontrol(mtd, NAND_CTL_SETNCE); - } - - ctx->write_byte(this, (u8)(page_addr >> 8)); - - if (this->options & NAND_ROW_ADDR_3) - ctx->write_byte(this, - ((page_addr >> 16) & 0x0f)); - } - /* Latch in address */ - au1550_hwcontrol(mtd, NAND_CTL_CLRALE); - } - - /* - * Program and erase have their own busy handlers. - * Status and sequential in need no delay. - */ - switch (command) { - - case NAND_CMD_PAGEPROG: - case NAND_CMD_ERASE1: - case NAND_CMD_ERASE2: - case NAND_CMD_SEQIN: - case NAND_CMD_STATUS: - return; - - case NAND_CMD_RESET: - break; - - case NAND_CMD_READ0: - case NAND_CMD_READ1: - case NAND_CMD_READOOB: - /* Check if we're really driving -CE low (just in case) */ - if (unlikely(!ce_override)) + for (i = 0; i < op->ninstrs; i++) { + ret = au1550nd_exec_instr(this, &op->instrs[i]); + if (ret) break; - - /* Apply a short delay always to ensure that we do wait tWB. */ - ndelay(100); - /* Wait for a chip to become ready... */ - for (i = this->legacy.chip_delay; - !this->legacy.dev_ready(this) && i > 0; --i) - udelay(1); - - /* Release -CE and re-enable interrupts. */ - au1550_hwcontrol(mtd, NAND_CTL_CLRNCE); - local_irq_restore(flags); - return; } - /* Apply this short delay always to ensure that we do wait tWB. */ - ndelay(100); - - while(!this->legacy.dev_ready(this)); -} -static int find_nand_cs(unsigned long nand_base) -{ - void __iomem *base = - (void __iomem *)KSEG1ADDR(AU1000_STATIC_MEM_PHYS_ADDR); - unsigned long addr, staddr, start, mask, end; - int i; + /* deassert chip enable */ + alchemy_wrsmem(0, AU1000_MEM_STNDCTL); + /* Drain the writebuffer */ + wmb(); - for (i = 0; i < 4; i++) { - addr = 0x1000 + (i * 0x10); /* CSx */ - staddr = __raw_readl(base + addr + 0x08); /* STADDRx */ - /* figure out the decoded range of this CS */ - start = (staddr << 4) & 0xfffc0000; - mask = (staddr << 18) & 0xfffc0000; - end = (start | (start - 1)) & ~(start ^ mask); - if ((nand_base >= start) && (nand_base < end)) - return i; - } - - return -ENODEV; + return ret; } +static const struct nand_controller_ops au1550nd_ops = { + .exec_op = au1550nd_exec_op, +}; + static int au1550nd_probe(struct platform_device *pdev) { struct au1550nd_platdata *pd; @@ -424,23 +291,15 @@ static int au1550nd_probe(struct platform_device *pdev) } ctx->cs = cs; - this->legacy.dev_ready = au1550_device_ready; - this->legacy.select_chip = au1550_select_chip; - this->legacy.cmdfunc = au1550_command; - - /* 30 us command delay time */ - this->legacy.chip_delay = 30; + nand_controller_init(&ctx->controller); + ctx->controller.ops = &au1550nd_ops; + this->controller = &ctx->controller; this->ecc.mode = NAND_ECC_SOFT; this->ecc.algo = NAND_ECC_HAMMING; if (pd->devwidth) this->options |= NAND_BUSWIDTH_16; - this->legacy.read_byte = (pd->devwidth) ? au_read_byte16 : au_read_byte; - ctx->write_byte = (pd->devwidth) ? au_write_byte16 : au_write_byte; - this->legacy.write_buf = (pd->devwidth) ? au_write_buf16 : au_write_buf; - this->legacy.read_buf = (pd->devwidth) ? au_read_buf16 : au_read_buf; - ret = nand_scan(this, 1); if (ret) { dev_err(&pdev->dev, "NAND scan failed with %d\n", ret); @@ -466,8 +325,12 @@ static int au1550nd_remove(struct platform_device *pdev) { struct au1550nd_ctx *ctx = platform_get_drvdata(pdev); struct resource *r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + struct nand_chip *chip = &ctx->chip; + int ret; - nand_release(&ctx->chip); + ret = mtd_device_unregister(nand_to_mtd(chip)); + WARN_ON(ret); + nand_cleanup(chip); iounmap(ctx->base); release_mem_region(r->start, 0x1000); kfree(ctx); |