/****************************************************************************** ** ** FILE NAME : amazon_s_nand.c ** PROJECT : Amazon_S ** MODULES : NAND Flash ** ** DATE : 23 Apr 2005 ** AUTHOR : Wu Qi Ming ** DESCRIPTION : NAND Flash MTD Driver ** COPYRIGHT : Copyright (c) 2006 ** Infineon Technologies AG ** Am Campeon 1-12, 85579 Neubiberg, Germany ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 2 of the License, or ** (at your option) any later version. ** ** HISTORY ** $Date $Author $Version $Comment ** 23 Apr 2008 Wu Qi Ming 1.0 initial version *******************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #ifdef CONFIG_MTD_PARTITIONS #define __AMAZON_S_NAND__ #include #endif /* CONFIG_MTD_PARTITIONS */ #define NB_OF(x) (sizeof(x)/sizeof(x[0])) #define ADDR_COLUMN 1 #define ADDR_PAGE 2 #define ADDR_COLUMN_PAGE 3 #define NAND_BASE_ADDRESS 0xB4000000 #define CFG_NAND_BASE NAND_BASE_ADDRESS #define NAND_WRITE(addr, val) *((volatile u8*)(NAND_BASE_ADDRESS | (addr))) = val;while((*EBU_NAND_WAIT & 0x08) == 0); #define NAND_READ(addr, val) val = *((volatile u8*)(NAND_BASE_ADDRESS | (addr))) #define NAND_CE_SET *EBU_NAND_CON = *EBU_NAND_CON | (1<<20); #define NAND_CE_CLEAR *EBU_NAND_CON = *EBU_NAND_CON & ~(1<<20); #define NAND_READY ( ((*EBU_NAND_WAIT)&0x01) == 0x01) #define NAND_READY_CLEAR *EBU_NAND_WAIT = 0; #define WRITE_CMD 0x18 #define WRITE_ADDR 0x14 #define WRITE_LADDR 0x10 #define WRITE_DATA 0x10 #define READ_DATA 0x10 #define READ_LDATA 0x00 #define ACCESS_WAIT #define IFX_ATC_NAND 0xc176 #define IFX_BTC_NAND 0xc166 #define ST_512WB2_NAND 0x2076 #ifdef CONFIG_NAND_LATCH_ENABLE //#define NAND_DISABLE_CE(nand) *EBU_NAND_CON = *EBU_NAND_CON & ~(1<<20); //#define NAND_ENABLE_CE(nand) *EBU_NAND_CON = *EBU_NAND_CON |(1<<20); //#define NAND_WAIT_READY(nand) while(!NAND_READY){} static inline void NAND_DISABLE_CE(struct nand_chip *nand) { *EBU_NAND_CON = *EBU_NAND_CON & ~(1<<20); } static inline void NAND_ENABLE_CE(struct nand_chip *nand) { *EBU_NAND_CON = *EBU_NAND_CON |(1<<20); } #endif static inline void NAND_WAIT_READY(struct nand_chip *nand) { while(!NAND_READY){} } #define WRITE_NAND_COMMAND(d, adr) NAND_WRITE(WRITE_CMD,d); #define WRITE_NAND_ADDRESS(d, adr) NAND_WRITE(WRITE_ADDR,d); #define WRITE_NAND(d) NAND_WRITE(WRITE_DATA,d); #define READ_NAND *((volatile u8*)(NAND_BASE_ADDRESS | (READ_DATA))) /* the following are NOP's in our implementation */ #define NAND_CTL_CLRALE(nandptr) #define NAND_CTL_SETALE(nandptr) #define NAND_CTL_CLRCLE(nandptr) #define NAND_CTL_SETCLE(nandptr) /* * MTD structure for NAND controller */ static struct mtd_info *amazon_s_nand_mtd = NULL; #if 0 /* * Define partitions for flash device */ static const struct mtd_partition amazon_s_nand_partition[] = { { .name = "u-boot", .offset = 0, .size = 0x28000}, { .name = "kernel", .offset =0x100000, .size = 0x100000}, { .name = "rootfs", .offset =0x200000, .size = 0x200000}, }; #endif static u32 latchcmd=0; static int NanD_WaitReady(struct mtd_info *mtd) { struct nand_chip *nand = mtd->priv; NAND_WAIT_READY(nand); return 1; } /** * amazon_s_nand_read_byte - read one byte from the chip * @mtd: MTD device structure * * read function for 8bit buswith */ static u_char amazon_s_nand_read_byte(struct mtd_info *mtd) { //struct nand_chip *nand = mtd->priv; u_char ret; asm("sync"); NAND_READ(READ_DATA, ret); asm("sync"); return ret; } /** * amazon_s_nand_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. * * @mtd: MTD device structure * @chip: chipnumber to select, -1 for deselect */ static void amazon_s_nand_select_chip(struct mtd_info *mtd, int chip) { #ifdef CONFIG_NAND_LATCH_ENABLE struct nand_chip *nand = mtd->priv; #endif switch (chip) { case -1: #ifdef CONFIG_NAND_LATCH_ENABLE NAND_DISABLE_CE(nand); #endif *EBU_NAND_CON &=~(0x1); break; case 0: *EBU_NAND_CON |=(0x1); #ifdef CONFIG_NAND_LATCH_ENABLE NAND_ENABLE_CE(nand); #endif NAND_WRITE(WRITE_CMD, 0xff); // Reset nand chip break; default: BUG(); } } /** * amazon_s_nand_read_buf - [DEFAULT] read chip data into buffer * @mtd: MTD device structure * @buf: buffer to store date * @len: number of bytes to read * * Default read function for 8bit buswith */ static void amazon_s_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) { int i; //struct nand_chip *chip = mtd->priv; for (i = 0; i < len; i++) buf[i]=READ_NAND; } /** * amazon_s_nand_write_buf - [DEFAULT] write buffer to chip * @mtd: MTD device structure * @buf: data buffer * @len: number of bytes to write * * Default write function for 8bit buswith */ static void amazon_s_nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) { int i; //struct nand_chip *chip = mtd->priv; for (i = 0; i < len; i++) WRITE_NAND(buf[i]); // writeb(buf[i], chip->IO_ADDR_W); } /** * amazon_s_nand_verify_buf - [DEFAULT] Verify chip data against buffer * @mtd: MTD device structure * @buf: buffer containing the data to compare * @len: number of bytes to compare * * Default verify function for 8bit buswith */ static int amazon_s_nand_verify_buf(struct mtd_info *mtd, const uint8_t *buf, int len) { int i; //struct nand_chip *chip = mtd->priv; for (i = 0; i < len; i++) if (buf[i] != READ_NAND) return -EFAULT; return 0; } ///** // * nand_write_byte - [DEFAULT] write one byte to the chip // * @mtd: MTD device structure // * @byte: pointer to data byte to write // * // * Default write function for 8it buswith // */ //static void amazon_s_nand_write_byte(struct mtd_info *mtd, u_char byte) //{ // struct nand_chip *this = mtd->priv; // *(volatile u8*)((u32)this->IO_ADDR_W | latchcmd)=byte; // while((*EBU_NAND_WAIT & 0x08) == 0); //} /* * Hardware specific access to control-lines */ static void amazon_s_nand_cmd_ctrl(struct mtd_info *mtd, int data, unsigned int ctrl) { struct nand_chip *this = mtd->priv; if (ctrl & NAND_CTRL_CHANGE){ if(ctrl & NAND_CLE) latchcmd=WRITE_CMD; else if(ctrl & NAND_ALE) latchcmd=WRITE_ADDR; } if(data != NAND_CMD_NONE){ *(volatile u8*)((u32)this->IO_ADDR_W | latchcmd)=data; while((*EBU_NAND_WAIT & 0x08) == 0); } return; } static void amazon_s_nand_chip_init(void) { /*P1.7 FL_CS1 used as output*/ amazon_s_port_reserve_pin(1, 7, PORT_MODULE_NAND); amazon_s_port_set_dir_out(1, 7, PORT_MODULE_NAND); amazon_s_port_set_altsel0(1, 7, PORT_MODULE_NAND); amazon_s_port_clear_altsel1(1, 7, PORT_MODULE_NAND); amazon_s_port_set_open_drain(1, 7, PORT_MODULE_NAND); /*P1.8 FL_A23 used as output*/ amazon_s_port_reserve_pin(1, 8, PORT_MODULE_NAND); amazon_s_port_set_dir_out(1, 8, PORT_MODULE_NAND); amazon_s_port_set_altsel0(1, 8, PORT_MODULE_NAND); amazon_s_port_clear_altsel1(1, 8, PORT_MODULE_NAND); amazon_s_port_set_open_drain(1, 8, PORT_MODULE_NAND); /*P0.13 FL_A24 used as output, set GPIO 13 to ND_ALE*/ amazon_s_port_reserve_pin(0, 13, PORT_MODULE_NAND); amazon_s_port_set_dir_out(0, 13, PORT_MODULE_NAND); amazon_s_port_set_altsel0(0, 13, PORT_MODULE_NAND); amazon_s_port_clear_altsel1(0, 13, PORT_MODULE_NAND); amazon_s_port_set_open_drain(0, 13, PORT_MODULE_NAND); /*P3.0 set as NAND Read Busy*/ amazon_s_port_reserve_pin(3, 0, PORT_MODULE_NAND); amazon_s_port_set_dir_in(3, 0, PORT_MODULE_NAND); amazon_s_port_set_altsel0(3, 0, PORT_MODULE_NAND); amazon_s_port_clear_altsel1(3, 0, PORT_MODULE_NAND); /*P3.1 set as NAND Read*/ amazon_s_port_reserve_pin(3, 1, PORT_MODULE_NAND); amazon_s_port_set_dir_out(3, 1, PORT_MODULE_NAND); amazon_s_port_set_altsel0(3, 1, PORT_MODULE_NAND); amazon_s_port_clear_altsel1(3, 1, PORT_MODULE_NAND); amazon_s_port_set_open_drain(3, 1, PORT_MODULE_NAND); #if 0 *AMAZON_S_GPIO_P1_ALTSEL0 = *AMAZON_S_GPIO_P1_ALTSEL0 | (1<<7) | (1<<8); *AMAZON_S_GPIO_P1_ALTSEL1 = *AMAZON_S_GPIO_P1_ALTSEL1 & ~(1<<7) & ~ (1<<8); *AMAZON_S_GPIO_P1_DIR = *AMAZON_S_GPIO_P1_DIR | (1<<7) | (1<<8); *AMAZON_S_GPIO_P1_OD = *AMAZON_S_GPIO_P1_OD | (1<<7) | (1<<8) ; (*AMAZON_S_GPIO_P0_ALTSEL0) = (*AMAZON_S_GPIO_P0_ALTSEL0) | (1<<13); //Set GPIO 13 to ND_ALE (*AMAZON_S_GPIO_P0_ALTSEL1) = (*AMAZON_S_GPIO_P0_ALTSEL1) & ~(1<<13); (*AMAZON_S_GPIO_P0_OD) = (*AMAZON_S_GPIO_P0_OD) | (1<<13) ; (*AMAZON_S_GPIO_P0_DIR) = (*AMAZON_S_GPIO_P0_DIR) | (1<<13) ; // set GPIO 13 to output //USE GPIO48 and 49 for FL_RD and and NAND_READY *AMAZON_S_GPIO_P3_ALTSEL0 = *AMAZON_S_GPIO_P3_ALTSEL0 | (3<<0) ; *AMAZON_S_GPIO_P3_ALTSEL1 = *AMAZON_S_GPIO_P3_ALTSEL1 & ~(3<<0); *AMAZON_S_GPIO_P3_DIR = *AMAZON_S_GPIO_P3_DIR | (1<<1); *AMAZON_S_GPIO_P3_DIR = *AMAZON_S_GPIO_P3_DIR & ~(1<<0); *AMAZON_S_GPIO_P3_OD = *AMAZON_S_GPIO_P3_OD | (1<<1); #endif #if 0 *AMAZON_S_EBU_ADDSEL1 = (NAND_BASE_ADDRESS&0x1fffff00)|0x31; /* byte swap;minimum delay*/ *AMAZON_S_EBU_BUSCON1 = 0x40F295; *EBU_NAND_CON = 0x000005F3; #else /*--- auf aktueller HW ist der NAND an AMAZON_S_EBU_ADDSEL0 ---*/ *AMAZON_S_EBU_ADDSEL0 = (NAND_BASE_ADDRESS&0x1fffff00)|0x31; /* byte swap;minimum delay*/ *AMAZON_S_EBU_BUSCON0 = (1<<22)|(1<<15)|(0xE<<11)|(0x4<<7)|(4<<5)|(2<<3)|(2<<1)|(1<<0); *EBU_NAND_CON = (0xF<<4)|(1<<1)|(1<<0); #endif asm("sync"); /* Set bus signals to inactive */ NAND_WRITE(WRITE_CMD, 0xff); // Reset nand chip } /* * Main initialization routine */ static int __init amazon_s_nand_init(void) { struct nand_chip *this; //struct mtd_partition *mtd_parts = 0; //void __iomem *nandaddr; int retval; printk("amazon_s_nand_init"); amazon_s_nand_chip_init(); /* Allocate memory for MTD device structure and private data */ amazon_s_nand_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL); if (!amazon_s_nand_mtd) { printk("Unable to allocate NAND MTD dev structure.\n"); return -ENOMEM; } /* Get pointer to private data */ this = (struct nand_chip *)(&amazon_s_nand_mtd[1]); /* Initialize structures */ memset(amazon_s_nand_mtd, 0, sizeof(struct mtd_info)); memset(this, 0, sizeof(struct nand_chip)); /* Link the private data with the MTD structure */ amazon_s_nand_mtd->priv = this; amazon_s_nand_mtd->owner = THIS_MODULE; /* insert callbacks */ this->IO_ADDR_R = (void *)NAND_BASE_ADDRESS; this->IO_ADDR_W = (void *)NAND_BASE_ADDRESS; this->cmd_ctrl = amazon_s_nand_cmd_ctrl; /* 30 us command delay time */ this->chip_delay = 30; this->ecc.mode = NAND_ECC_SOFT; this->options|=NAND_USE_FLASH_BBT; this->read_byte=amazon_s_nand_read_byte; this->read_buf=amazon_s_nand_read_buf; this->write_buf=amazon_s_nand_write_buf; this->verify_buf=amazon_s_nand_verify_buf; this->dev_ready=NanD_WaitReady; this->select_chip=amazon_s_nand_select_chip; /* Scan to find existence of the device */ if (nand_scan(amazon_s_nand_mtd, 1)) { retval = -ENXIO; return retval; } #ifdef CONFIG_MTD_PARTITIONS retval=add_mtd_partitions(amazon_s_nand_mtd, amazon_s_nand_partition,\ ARRAY_SIZE(amazon_s_nand_partition)); #else retval=add_mtd_device(amazon_s_nand_mtd); #endif return 0; } static void __exit amazon_s_nand_exit(void) { } module_init(amazon_s_nand_init); module_exit(amazon_s_nand_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Wu Qi Ming"); MODULE_DESCRIPTION("NAND driver for Amazon_S");