diff options
Diffstat (limited to 'uClinux-2.4.20-uc1/drivers/mtd/nand/w90n745_128mb_nand.c')
-rw-r--r-- | uClinux-2.4.20-uc1/drivers/mtd/nand/w90n745_128mb_nand.c | 323 |
1 files changed, 323 insertions, 0 deletions
diff --git a/uClinux-2.4.20-uc1/drivers/mtd/nand/w90n745_128mb_nand.c b/uClinux-2.4.20-uc1/drivers/mtd/nand/w90n745_128mb_nand.c new file mode 100644 index 0000000..37330c8 --- /dev/null +++ b/uClinux-2.4.20-uc1/drivers/mtd/nand/w90n745_128mb_nand.c @@ -0,0 +1,323 @@ +/* + * linux/drivers/mtd/nand/w90n745_nand.c + * + * Copyright (c) 2006 Winbond (http://www.winbond.com.tw) + * + * History: + * 2006/08/24 Created by NS24 zswan v1.0 for nand 512b per page + * 2006/12/12 Created by NS24 zswan v1.1 for nand 2kb per page + * mail:zswan@winbond.com.tw + */ + +#include <linux/slab.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/nand.h> +#include <linux/mtd/partitions.h> +#include <asm/io.h> +#include <asm/hardware.h> + +#include "w90n745_nand.h" + +#define write_cmd_reg(cmd) outb(cmd, REG_SMCMD) +#define write_addr_reg(addr) outb(addr, REG_SMADDR) +#define read_data_reg() inb(REG_SMDATA) +#define write_data_reg(value) outb(value, REG_SMDATA) + + +typedef unsigned int UINT32; +typedef unsigned char UINT8; +UINT32 blockaddr; +UINT8 _fmi_ucBaseAddr1, _fmi_ucBaseAddr2; +/* + * MTD structure for NAND controller + */ +static struct mtd_info *w90n745_mtd = NULL; +//static u_char data_buf[2048 + 64]; +//static u_char oob_buf[64*64]; +/* + * Define partitions for flash device + */ +#define NUM_PARTITIONS 2 +const static struct mtd_partition partition_info[NUM_PARTITIONS] = { + { + .name = "w90n745 2K NAND PAR1", + .offset = 0, + .size = 64*1024*1024 + }, { + .name = "w90n745 2K NAND PAR2", + .offset = 64*1024*1024, + .size = 64*1024*1024 + }, +}; + +static int w90n745_device_ready(struct mtd_info *mtd) +{ + int ready; + +#ifdef _DEBUG_NAND + printk("w90n745_device_ready\n"); +#endif + ready = (ebiCheckRB()) ? 1 : 0; // rb# status + return ready; +} + +static u_char w90n745_nand_read_byte(struct mtd_info *mtd) +{ + u_char ret; + +#ifdef _DEBUG_NAND + printk("w90n745_nand_read_byte\n"); +#endif + ret = (u_char)read_data_reg(); + return ret; +} + +static void w90n745_nand_write_byte(struct mtd_info *mtd, u_char byte) +{ +#ifdef _DEBUG_NAND + printk("w90n745_nand_write_byte\n"); +#endif + write_data_reg(byte); +} + +int ebiCheckRB(void) +{ + return (inl(0xfff8305c) & 0x10); +} + +static void nand_reset(void) +{ + UINT32 volatile i; + +#ifdef _DEBUG_NAND + printk("nand_reset\n"); +#endif + outb(0xff, REG_SMCMD); + for (i=100; i>0; i--); + while (!ebiCheckRB()); +} + +static void w90n745_nand_write_buf(struct mtd_info *mtd, + const u_char *buf, int len) +{ + int i; + +#ifdef _DEBUG_NAND + printk("w90n745_nand_write_buf\n"); +#endif + for (i=0; i < len; i++){ + write_data_reg(buf[i]); + } +} + + +static void w90n745_nand_read_buf(struct mtd_info *mtd, + u_char *buf, int len) +{ + int i; + +#ifdef _DEBUG_NAND + printk("w90n745_nand_read_buf\n"); +#endif + for (i = 0; i < len; i++){ + buf[i] =(u_char)read_data_reg(); + } +} + +static int +w90n745_nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len) +{ + int i; + +#ifdef _DEBUG_NAND + printk("w90n745_nand_verify_buf\n"); +#endif + for (i=0; i < len; i++) { + if (buf[i] != (u_char)read_data_reg()) { + return -EFAULT; + } + } + return 0; +} + +static void w90n745_nand_select_chip(struct mtd_info *mtd, int chip) +{ +#ifdef _DEBUG_NAND + printk("w90n745_nand_select_chip\n"); +#endif +} + +static void w90n745_nand_command (struct mtd_info *mtd, unsigned command, + int column, int page_addr) +{ + register struct nand_chip *this = mtd->priv; + +#ifdef _DEBUG_NAND + printk("w90n745_nand_command\n"); +#endif + /* Emulate NAND_CMD_READOOB */ + if (command == NAND_CMD_READOOB) { + column += mtd->oobblock; + + command = NAND_CMD_READ0; + } + + /* Write out the command to the device. */ + write_cmd_reg(command); + + if (column != -1 || page_addr != -1) { + //this->hwcontrol(mtd, NAND_CTL_SETALE); + + /* Serially input address */ + if (column != -1) { + /* Adjust columns for 16 bit buswidth */ + if (this->options & NAND_BUSWIDTH_16){ + column >>= 1; + printk("********NAND_BUSWIDTH_16********\n"); + } + write_addr_reg( column & 0xff); + write_addr_reg(column >> 8); + + } + if (page_addr != -1) { + + write_addr_reg((unsigned char)(page_addr )& 0xff); + write_addr_reg((unsigned char) ((page_addr>>8 )& 0xff)); + /* One more address cycle for devices > 128MiB */ + if (this->chipsize > (128 << 20)) { + write_addr_reg((unsigned char) ((page_addr >> 16) & 0xff)); + } + } + } + + /* + * program and erase have their own busy handlers + * status and sequential in needs 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: + return; + case NAND_CMD_RESET: + nand_reset(); + return; + case NAND_CMD_READ0: + write_cmd_reg(NAND_CMD_READSTART); + /* This applies to read commands */ + default: + /* + * If we don't have access to the busy pin, we apply the given + * command delay + */ + udelay(this->chip_delay); + return; + } + + /* Apply this short delay always to ensure that we do wait tWB in + * any case on any machine. */ + while (!this->dev_ready(mtd)); +} + +/* + * Main initialization routine + */ +static int __init w90n745_init (void) +{ + struct nand_chip *this; + +#ifdef _DEBUG_NAND + printk("w90n745_init\n"); +#endif + // set EBI + #ifdef EBI_BANK2 + Setup_EXTIO2_Base(); + #else + Setup_EXTIO3_Base(); + #endif + + // set GPIO + outpw(0xfff83050, inpw(0xfff83050) & 0xfffff0ff); // GPIO5-4 and GPIO5-5 + outpw(0xfff83054, (inpw(0xfff83054) & 0xffffffcf) | 0x20); + outpw(0xfff83058, inpw(0xfff83058) | 0x20); + + /* Allocate memory for MTD device structure and private data */ + w90n745_mtd = kmalloc (sizeof(struct mtd_info) + + sizeof (struct nand_chip), GFP_KERNEL); + //w90n745_mtd=kmalloc (1024, GFP_KERNEL); + //kfree (w90n745_mtd); + if (!w90n745_mtd) { + printk ("Unable to allocate NAND MTD dev structure.\n"); + return -ENOMEM; + } + + /* Get pointer to private data */ + this = (struct nand_chip *) (&w90n745_mtd[1]); + + /* Initialize structures */ + memset((char *) w90n745_mtd, 0, sizeof(struct mtd_info)); + memset((char *) this, 0, sizeof(struct nand_chip)); + + /* Link the private data with the MTD structure */ + w90n745_mtd->priv = this; + + /* Set address of hardware control function */ + + this->dev_ready = w90n745_device_ready; + /* 30 us command delay time */ + this->chip_delay = 100; + + this->cmdfunc = w90n745_nand_command; + this->select_chip = w90n745_nand_select_chip; + //this->select_chip = NULL; + this->write_byte = w90n745_nand_write_byte; + this->read_byte = w90n745_nand_read_byte; + this->write_buf = w90n745_nand_write_buf; + this->read_buf = w90n745_nand_read_buf; + this->verify_buf = w90n745_nand_verify_buf; + + this->eccmode = NAND_ECC_SOFT; + + this->data_buf = NULL; + this->oob_buf = NULL; + //w90n745_nand_Reset(); + /* Scan to find existence of the device */ + if (nand_scan (w90n745_mtd, 1)) { + kfree (w90n745_mtd); + return -ENXIO; + } + + /* Register the partitions */ + add_mtd_partitions(w90n745_mtd, partition_info, NUM_PARTITIONS); + + return 0; +} + +static void __exit w90n745_exit(void) +{ + struct nand_chip *this = (struct nand_chip *) &w90n745_mtd[1]; + +#ifdef _DEBUG_NAND + printk("w90n745_exit\n"); +#endif + /* Unregister partitions */ + del_mtd_partitions(w90n745_mtd); + + /* Unregister the device */ + del_mtd_device (w90n745_mtd); + + /* Free the MTD device structure */ + kfree (w90n745_mtd); +} + +module_init(w90n745_init); +module_exit(w90n745_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("NS24 zswan, zswan@Winbond.com.tw"); +MODULE_DESCRIPTION("Board-specific glue layer for NAND flash on w90n745 board"); |