/* * drivers/mtd/nand/ohio_nand.c * * Copyright (C) 2005 AVM * * $Id: ohio_nand.c,v 1.11 2004/11/04 12:53:10 gleixner Exp $ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include "ohio_nand.h" /* * MTD structure for NAND controller */ static struct mtd_info *ohio_nand_mtd = NULL; static void __iomem *p_nand; #define OHIO_NAND_OFFSET 0x1000 #define OHIO_NAND_ADDR (OHIO_CS5_BASE + OHIO_NAND_OFFSET) #define OHIO_NAND_IRQ OHIOINT_EXT_0 #define OHIO_NAND_MAX_CHIPS 4 /*--- maximale Anzahl unterstützter NAND-CHIPS ---*/ #define NUM_PARTITIONS 2 static t_ohio_nand *ohio_nand = (t_ohio_nand *)&(*(volatile unsigned *)(OHIO_CS5_BASE + OHIO_NAND_OFFSET)); static t_ohio_nand_control ohio_nand_control; /*--- struct to temp. hold control ---*/ static unsigned int ohio_nand_column; /*--- index auf ohio_nand->Buffer ---*/ static struct resource *io_port = NULL; /*--- holds io-mem resource ---*/ static struct semaphore ohio_nand_sem; /*------------------------------------------------------------------------------------------*\ * Define partitions for flash device \*------------------------------------------------------------------------------------------*/ const static struct mtd_partition partition_info[] = { { .name = "NAND FS 0", .offset = 0, .size = 8*1024*1024 }, { .name = "NAND FS 1", .offset = MTDPART_OFS_APPEND, .size = MTDPART_SIZ_FULL } }; /*------------------------------------------------------------------------------------------*\ * ohio_nand_read_byte - read one byte from buffer set bufferpointer to next * @mtd: MTD device structure * * read function for 8bit buswith \*------------------------------------------------------------------------------------------*/ static u_char ohio_nand_read_byte(struct mtd_info *mtd) { u_char ret; printk(KERN_ERR "[ohio_nand_read_byte]\n"); ret = ohio_nand->Buffer[ohio_nand_column]; ohio_nand_column++; return ret; } /*------------------------------------------------------------------------------------------*\ * ohio_nand_write_byte - write one byte to the chip * @mtd: MTD device structure * @byte: pointer to data byte to write * * write function for 8it buswith \*------------------------------------------------------------------------------------------*/ static void ohio_nand_write_byte(struct mtd_info *mtd, u_char byte) { printk(KERN_ERR "[ohio_nand_write_byte] not implemented\n"); } /*------------------------------------------------------------------------------------------*\ * ohio_nand_read_word - read one word from the chip * @mtd: MTD device structure * * read function for 16bit buswith without endianess conversion \*------------------------------------------------------------------------------------------*/ static u16 ohio_nand_read_word(struct mtd_info *mtd) { printk(KERN_ERR "[ohio_nand_read_word] not implemented\n"); return 0; } /*------------------------------------------------------------------------------------------*\ * ohio_nand_write_word - write one word to the chip * @mtd: MTD device structure * @word: data word to write * * write function for 16bit buswith without endianess conversion \*------------------------------------------------------------------------------------------*/ static void ohio_nand_write_word(struct mtd_info *mtd, u16 word) { printk(KERN_ERR "[ohio_nand_write_word] not implemented\n"); } /*------------------------------------------------------------------------------------------*\ * ohio_nand_write_buf - write buffer to chip * @mtd: MTD device structure * @buf: data buffer * @len: number of bytes to write * * write function for 8bit buswith \*------------------------------------------------------------------------------------------*/ static void ohio_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len) { int i; for (i=0; iBuffer[ohio_nand_column] = buf[i]; ohio_nand_column++; } } /*------------------------------------------------------------------------------------------*\ * ohio_nand_read_buf - read chip data into buffer * @mtd: MTD device structure * @buf: buffer to store date * @len: number of bytes to read * * read function for 8bit buswith \*------------------------------------------------------------------------------------------*/ static void ohio_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) { int i; for (i=0; iBuffer[ohio_nand_column]; ohio_nand_column++; } } /*------------------------------------------------------------------------------------------*\ * ohio_nand_verify_buf - Verify chip data against buffer * @mtd: MTD device structure * @buf: buffer containing the data to compare * @len: number of bytes to compare * * verify function for 8bit buswith \*------------------------------------------------------------------------------------------*/ static int ohio_nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len) { int i; for (i=0; iBuffer[ohio_nand_column]) return -EFAULT; ohio_nand_column++; } return 0; } /*------------------------------------------------------------------------------------------*\ * nothing to do here \*------------------------------------------------------------------------------------------*/ static void ohio_nand_hwcontrol(struct mtd_info *mtd, int cmd) { } /*------------------------------------------------------------------------------------------*\ * wir unterstützten bis zu 4 Chips * das Chipselect wird zusammen mit dem Kommando gesetzt, hier merken wir uns den Wert nur \*------------------------------------------------------------------------------------------*/ static void ohio_nand_select_chip(struct mtd_info *mtd, int chip) { if (chip > OHIO_NAND_MAX_CHIPS) { printk(KERN_WARNING "[ohio_nand_select_chip] select chip %d\n", chip); return; } ohio_nand_control.Bits.chip_select = chip; } /*------------------------------------------------------------------------------------------*\ * wir haben ein wenig HW- Unterstützung - wir lesen immer eine ganze Page vom Flash * dazu bauen wir ohio_nand_control zusammen, schreiben das Controlword und warten... \*------------------------------------------------------------------------------------------*/ static void ohio_nand_command(struct mtd_info *mtd, unsigned command, int column, int page_addr) { unsigned int need_delay = OHIO_NAND_DELAY; /* Emulate NAND_CMD_READOOB */ if (command == NAND_CMD_READOOB) { column += mtd->oobblock; command = NAND_CMD_READ0; } if (column != -1) { /*--- wir müssen den Pointer für den Lesebuffer auf column setzen ---*/ printk(KERN_INFO "[ohio_nand_command] set pBuffer to %d\n", column); ohio_nand_column = column; /*--- pBuffer = &ohio_nand->Buffer[column]; ---*/ /*--- set pointer ---*/ } if (page_addr == -1) ohio_nand_control.Bits.page_addr = 0; else ohio_nand_control.Bits.page_addr = page_addr; switch (command) { /*--- wir müssen die Kommandos mappen ---*/ case NAND_CMD_SEQIN: /*--- wird nicht benötigt, wir schreiben einfach in den Buffer ---*/ case NAND_CMD_ERASE2: return; case NAND_CMD_READ0: ohio_nand_control.Bits.cmd = cmd_read; break; case NAND_CMD_PAGEPROG: ohio_nand_control.Bits.cmd = cmd_auto_page_program; ohio_nand_control.Bits.write_protect = 0; break; case NAND_CMD_ERASE1: ohio_nand_control.Bits.cmd = cmd_auto_block_erase; ohio_nand_control.Bits.write_protect = 0; break; case NAND_CMD_READID: ohio_nand_control.Bits.page_addr = 0; ohio_nand_control.Bits.cmd = cmd_read_id; need_delay = OHIO_NAND_NO_DELAY; break; case NAND_CMD_STATUS: ohio_nand_control.Bits.cmd = cmd_read_status; need_delay = OHIO_NAND_NO_DELAY; break; case NAND_CMD_RESET: ohio_nand_control.Bits.cmd = cmd_reset; break; default: /*--- case NAND_CMD_ERASE2: ---*/ /*--- case NAND_CMD_DEPLETE1: ---*/ /*--- case NAND_CMD_CACHEDPROG: ---*/ /*--- 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: ---*/ printk(KERN_ERR "[ohio_nand_command] unknown CMD 0x%x\n", command); return; } /*--- write command to Hardware ---*/ printk(KERN_INFO "[ohio_nand_command]"); printk(KERN_INFO " Page 0x%x Command 0x%x WP/CS 0x%x\n", ohio_nand_control.Bits.page_addr, ohio_nand_control.Bits.cmd, ohio_nand_control.Register >> 27); ohio_nand->nand_control.Register = ohio_nand_control.Register; if (need_delay) { /*--- wait for busy ---*/ down(&ohio_nand_sem); } } /*------------------------------------------------------------------------------------------*\ * shared interrupthandler \*------------------------------------------------------------------------------------------*/ static irqreturn_t ohio_nand_int(int irq, void *dev_id, struct pt_regs *regs ) { if (ohio_nand->nand_status.Bits.irq) { up(&ohio_nand_sem); return IRQ_HANDLED; } else { return IRQ_NONE; /*--- not our cup of tea ---*/ } } /*------------------------------------------------------------------------------------------*\ * return 0 if busy * return 1 if not busy \*------------------------------------------------------------------------------------------*/ static int ohio_nand_device_ready(struct mtd_info *mtd) { ohio_nand_command(mtd, NAND_CMD_STATUS, -1, -1); ndelay (100); return ~ohio_nand->nand_status.Bits.nbusy; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static int ohio_nand_wait(struct mtd_info *mtd, struct nand_chip *this, int state) { unsigned long timeo = jiffies; if (state == FL_ERASING) timeo += (HZ * 400) / 1000; else timeo += (HZ * 20) / 1000; /* Apply this short delay always to ensure that we do wait tWB in * any case on any machine. */ /*--- ndelay (100); ---*/ while (time_before(jiffies, timeo)) { /* Check, if we were interrupted */ if (this->state != state) return 0; this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1); ndelay (100); if (ohio_nand->nand_status.Bits.nbusy) break; cond_resched(); } return (int) ohio_nand->nand_status.Register; } /*------------------------------------------------------------------------------------------*\ * Main initialization routine \*------------------------------------------------------------------------------------------*/ int __init ohio_nand_init (void) { struct nand_chip *this; int retval; printk(KERN_INFO "[ohio_nand_init]\n"); /* Allocate memory for MTD device structure and private data */ ohio_nand_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip), GFP_KERNEL); if (!ohio_nand_mtd) { printk ("Unable to allocate NAND MTD dev structure.\n"); return -ENOMEM; } /* Get pointer to private data */ this = (struct nand_chip *) (&ohio_nand_mtd[1]); /* Initialize structures */ memset((char *) ohio_nand_mtd, 0, sizeof(struct mtd_info)); memset((char *) this, 0, sizeof(struct nand_chip)); /* Link the private data with the MTD structure */ ohio_nand_mtd->priv = this; /*--- register io-mem ---*/ io_port = request_mem_region(OHIO_NAND_ADDR, sizeof(struct __nand), "ohio_nand"); if (!io_port) { printk("Unable to request io-region 0x%x\n", OHIO_NAND_ADDR); retval = -ENXIO; goto outmem; } p_nand = ioremap(OHIO_NAND_ADDR, 0x1000); init_MUTEX_LOCKED(&ohio_nand_sem); /*--- setup an interrupt ---*/ if (request_irq(OHIO_NAND_IRQ, ohio_nand_int, SA_SHIRQ, "ohio_nand", (void *)&ohio_nand)) { printk(KERN_ERR "Unable to register Interrupt\n"); retval = -EINTR; goto outirq; } /* Set address of hardware control function */ this->hwcontrol = ohio_nand_hwcontrol; this->dev_ready = ohio_nand_device_ready; this->waitfunc = ohio_nand_wait; this->cmdfunc = ohio_nand_command; /* 30 us command delay time */ this->chip_delay = 30; this->eccmode = NAND_ECC_SOFT; this->options = NAND_NO_AUTOINCR; this->select_chip = ohio_nand_select_chip; this->read_byte = ohio_nand_read_byte; /*--- ---*/ this->write_byte = ohio_nand_write_byte; this->write_word = ohio_nand_write_word; this->read_word = ohio_nand_read_word; this->write_buf = ohio_nand_write_buf; this->read_buf = ohio_nand_read_buf; this->verify_buf = ohio_nand_verify_buf; /* Scan to find existence of the device */ if (nand_scan (ohio_nand_mtd, 1)) { retval = -ENXIO; goto outio; } /* Register the partitions */ add_mtd_partitions(ohio_nand_mtd, partition_info, NUM_PARTITIONS); return 0; outirq: free_irq(OHIO_NAND_IRQ, (void *)&ohio_nand); outio: iounmap ((void *)p_nand); outmem: kfree (ohio_nand_mtd); return retval; } module_init(ohio_nand_init); #ifdef MODULE /*------------------------------------------------------------------------------------------*\ * Clean up routine \*------------------------------------------------------------------------------------------*/ static void __exit ohio_nand_cleanup (void) { /*--- struct nand_chip *this = (struct nand_chip *) &ohio_nand_mtd[1]; ---*/ /* Release resources, unregister device */ nand_release (ohio_nand_mtd); /* Free the MTD device structure */ kfree (ohio_nand_mtd); /*--- unregister io-region ---*/ release_mem_region(OHIO_NAND_ADDR, sizeof(struct __nand)); /*--- unregister interrupts ---*/ free_irq(OHIO_NAND_IRQ, (void *)&ohio_nand); /* Unmap */ iounmap ((void *)p_nand); } module_exit(ohio_nand_cleanup); #endif MODULE_LICENSE("GPL"); MODULE_AUTHOR("AVM (2005)"); MODULE_DESCRIPTION("Board-specific glue layer for NAND flash on OHIO board");