Linux-2.6.12-rc2

Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.

Let it rip!
This commit is contained in:
Linus Torvalds
2005-04-16 15:20:36 -07:00
當前提交 1da177e4c3
共有 17291 個文件被更改,包括 6718755 次插入0 次删除

207
drivers/mtd/nand/Kconfig Normal file
查看文件

@@ -0,0 +1,207 @@
# drivers/mtd/nand/Kconfig
# $Id: Kconfig,v 1.26 2005/01/05 12:42:24 dwmw2 Exp $
menu "NAND Flash Device Drivers"
depends on MTD!=n
config MTD_NAND
tristate "NAND Device Support"
depends on MTD
select MTD_NAND_IDS
help
This enables support for accessing all type of NAND flash
devices. For further information see
<http://www.linux-mtd.infradead.org/tech/nand.html>.
config MTD_NAND_VERIFY_WRITE
bool "Verify NAND page writes"
depends on MTD_NAND
help
This adds an extra check when data is written to the flash. The
NAND flash device internally checks only bits transitioning
from 1 to 0. There is a rare possibility that even though the
device thinks the write was successful, a bit could have been
flipped accidentaly due to device wear or something else.
config MTD_NAND_AUTCPU12
tristate "SmartMediaCard on autronix autcpu12 board"
depends on ARM && MTD_NAND && ARCH_AUTCPU12
help
This enables the driver for the autronix autcpu12 board to
access the SmartMediaCard.
config MTD_NAND_EDB7312
tristate "Support for Cirrus Logic EBD7312 evaluation board"
depends on ARM && MTD_NAND && ARCH_EDB7312
help
This enables the driver for the Cirrus Logic EBD7312 evaluation
board to access the onboard NAND Flash.
config MTD_NAND_H1900
tristate "iPAQ H1900 flash"
depends on ARM && MTD_NAND && ARCH_PXA && MTD_PARTITIONS
help
This enables the driver for the iPAQ h1900 flash.
config MTD_NAND_SPIA
tristate "NAND Flash device on SPIA board"
depends on ARM && ARCH_P720T && MTD_NAND
help
If you had to ask, you don't have one. Say 'N'.
config MTD_NAND_TOTO
tristate "NAND Flash device on TOTO board"
depends on ARM && ARCH_OMAP && MTD_NAND
help
Support for NAND flash on Texas Instruments Toto platform.
config MTD_NAND_IDS
tristate
config MTD_NAND_TX4925NDFMC
tristate "SmartMedia Card on Toshiba RBTX4925 reference board"
depends on TOSHIBA_RBTX4925 && MTD_NAND && TOSHIBA_RBTX4925_MPLEX_NAND
help
This enables the driver for the NAND flash device found on the
Toshiba RBTX4925 reference board, which is a SmartMediaCard.
config MTD_NAND_TX4938NDFMC
tristate "NAND Flash device on Toshiba RBTX4938 reference board"
depends on TOSHIBA_RBTX4938 && MTD_NAND && TOSHIBA_RBTX4938_MPLEX_NAND
help
This enables the driver for the NAND flash device found on the
Toshiba RBTX4938 reference board.
config MTD_NAND_AU1550
tristate "Au1550 NAND support"
depends on SOC_AU1550 && MTD_NAND
help
This enables the driver for the NAND flash controller on the
AMD/Alchemy 1550 SOC.
config MTD_NAND_RTC_FROM4
tristate "Renesas Flash ROM 4-slot interface board (FROM_BOARD4)"
depends on MTD_NAND && SH_SOLUTION_ENGINE
select REED_SOLOMON
select REED_SOLOMON_DEC8
help
This enables the driver for the Renesas Technology AG-AND
flash interface board (FROM_BOARD4)
config MTD_NAND_PPCHAMELEONEVB
tristate "NAND Flash device on PPChameleonEVB board"
depends on PPCHAMELEONEVB && MTD_NAND
help
This enables the NAND flash driver on the PPChameleon EVB Board.
config MTD_NAND_S3C2410
tristate "NAND Flash support for S3C2410 SoC"
depends on ARCH_S3C2410 && MTD_NAND
help
This enables the NAND flash controller on the S3C2410.
No board specfic support is done by this driver, each board
must advertise a platform_device for the driver to attach.
config MTD_NAND_S3C2410_DEBUG
bool "S3C2410 NAND driver debug"
depends on MTD_NAND_S3C2410
help
Enable debugging of the S3C2410 NAND driver
config MTD_NAND_S3C2410_HWECC
bool "S3C2410 NAND Hardware ECC"
depends on MTD_NAND_S3C2410
help
Enable the use of the S3C2410's internal ECC generator when
using NAND. Early versions of the chip have had problems with
incorrect ECC generation, and if using these, the default of
software ECC is preferable.
If you lay down a device with the hardware ECC, then you will
currently not be able to switch to software, as there is no
implementation for ECC method used by the S3C2410
config MTD_NAND_DISKONCHIP
tristate "DiskOnChip 2000, Millennium and Millennium Plus (NAND reimplementation) (EXPERIMENTAL)"
depends on MTD_NAND && EXPERIMENTAL
select REED_SOLOMON
select REED_SOLOMON_DEC16
help
This is a reimplementation of M-Systems DiskOnChip 2000,
Millennium and Millennium Plus as a standard NAND device driver,
as opposed to the earlier self-contained MTD device drivers.
This should enable, among other things, proper JFFS2 operation on
these devices.
config MTD_NAND_DISKONCHIP_PROBE_ADVANCED
bool "Advanced detection options for DiskOnChip"
depends on MTD_NAND_DISKONCHIP
help
This option allows you to specify nonstandard address at which to
probe for a DiskOnChip, or to change the detection options. You
are unlikely to need any of this unless you are using LinuxBIOS.
Say 'N'.
config MTD_NAND_DISKONCHIP_PROBE_ADDRESS
hex "Physical address of DiskOnChip" if MTD_NAND_DISKONCHIP_PROBE_ADVANCED
depends on MTD_NAND_DISKONCHIP
default "0"
---help---
By default, the probe for DiskOnChip devices will look for a
DiskOnChip at every multiple of 0x2000 between 0xC8000 and 0xEE000.
This option allows you to specify a single address at which to probe
for the device, which is useful if you have other devices in that
range which get upset when they are probed.
(Note that on PowerPC, the normal probe will only check at
0xE4000000.)
Normally, you should leave this set to zero, to allow the probe at
the normal addresses.
config MTD_NAND_DISKONCHIP_PROBE_HIGH
bool "Probe high addresses"
depends on MTD_NAND_DISKONCHIP_PROBE_ADVANCED
help
By default, the probe for DiskOnChip devices will look for a
DiskOnChip at every multiple of 0x2000 between 0xC8000 and 0xEE000.
This option changes to make it probe between 0xFFFC8000 and
0xFFFEE000. Unless you are using LinuxBIOS, this is unlikely to be
useful to you. Say 'N'.
config MTD_NAND_DISKONCHIP_BBTWRITE
bool "Allow BBT writes on DiskOnChip Millennium and 2000TSOP"
depends on MTD_NAND_DISKONCHIP
help
On DiskOnChip devices shipped with the INFTL filesystem (Millennium
and 2000 TSOP/Alon), Linux reserves some space at the end of the
device for the Bad Block Table (BBT). If you have existing INFTL
data on your device (created by non-Linux tools such as M-Systems'
DOS drivers), your data might overlap the area Linux wants to use for
the BBT. If this is a concern for you, leave this option disabled and
Linux will not write BBT data into this area.
The downside of leaving this option disabled is that if bad blocks
are detected by Linux, they will not be recorded in the BBT, which
could cause future problems.
Once you enable this option, new filesystems (INFTL or others, created
in Linux or other operating systems) will not use the reserved area.
The only reason not to enable this option is to prevent damage to
preexisting filesystems.
Even if you leave this disabled, you can enable BBT writes at module
load time (assuming you build diskonchip as a module) with the module
parameter "inftl_bbt_write=1".
config MTD_NAND_SHARPSL
bool "Support for NAND Flash on Sharp SL Series (C7xx + others)"
depends on MTD_NAND && ARCH_PXA
config MTD_NAND_NANDSIM
bool "Support for NAND Flash Simulator"
depends on MTD_NAND && MTD_PARTITIONS
help
The simulator may simulate verious NAND flash chips for the
MTD nand layer.
endmenu

24
drivers/mtd/nand/Makefile Normal file
查看文件

@@ -0,0 +1,24 @@
#
# linux/drivers/nand/Makefile
#
# $Id: Makefile.common,v 1.15 2004/11/26 12:28:22 dedekind Exp $
obj-$(CONFIG_MTD_NAND) += nand.o nand_ecc.o
obj-$(CONFIG_MTD_NAND_IDS) += nand_ids.o
obj-$(CONFIG_MTD_NAND_SPIA) += spia.o
obj-$(CONFIG_MTD_NAND_TOTO) += toto.o
obj-$(CONFIG_MTD_NAND_AUTCPU12) += autcpu12.o
obj-$(CONFIG_MTD_NAND_EDB7312) += edb7312.o
obj-$(CONFIG_MTD_NAND_TX4925NDFMC) += tx4925ndfmc.o
obj-$(CONFIG_MTD_NAND_TX4938NDFMC) += tx4938ndfmc.o
obj-$(CONFIG_MTD_NAND_AU1550) += au1550nd.o
obj-$(CONFIG_MTD_NAND_PPCHAMELEONEVB) += ppchameleonevb.o
obj-$(CONFIG_MTD_NAND_S3C2410) += s3c2410.o
obj-$(CONFIG_MTD_NAND_DISKONCHIP) += diskonchip.o
obj-$(CONFIG_MTD_NAND_H1900) += h1910.o
obj-$(CONFIG_MTD_NAND_RTC_FROM4) += rtc_from4.o
obj-$(CONFIG_MTD_NAND_SHARPSL) += sharpsl.o
obj-$(CONFIG_MTD_NAND_NANDSIM) += nandsim.o
nand-objs = nand_base.o nand_bbt.o

477
drivers/mtd/nand/au1550nd.c Normal file
查看文件

@@ -0,0 +1,477 @@
/*
* drivers/mtd/nand/au1550nd.c
*
* Copyright (C) 2004 Embedded Edge, LLC
*
* $Id: au1550nd.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 <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>
/* fixme: this is ugly */
#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 0)
#include <asm/mach-au1x00/au1000.h>
#ifdef CONFIG_MIPS_PB1550
#include <asm/mach-pb1x00/pb1550.h>
#endif
#ifdef CONFIG_MIPS_DB1550
#include <asm/mach-db1x00/db1x00.h>
#endif
#else
#include <asm/au1000.h>
#ifdef CONFIG_MIPS_PB1550
#include <asm/pb1550.h>
#endif
#ifdef CONFIG_MIPS_DB1550
#include <asm/db1x00.h>
#endif
#endif
/*
* MTD structure for NAND controller
*/
static struct mtd_info *au1550_mtd = NULL;
static void __iomem *p_nand;
static int nand_width = 1; /* default x8*/
#define NAND_CS 1
/*
* Define partitions for flash device
*/
const static struct mtd_partition partition_info[] = {
#ifdef CONFIG_MIPS_PB1550
#define NUM_PARTITIONS 2
{
.name = "Pb1550 NAND FS 0",
.offset = 0,
.size = 8*1024*1024
},
{
.name = "Pb1550 NAND FS 1",
.offset = MTDPART_OFS_APPEND,
.size = MTDPART_SIZ_FULL
}
#endif
#ifdef CONFIG_MIPS_DB1550
#define NUM_PARTITIONS 2
{
.name = "Db1550 NAND FS 0",
.offset = 0,
.size = 8*1024*1024
},
{
.name = "Db1550 NAND FS 1",
.offset = MTDPART_OFS_APPEND,
.size = MTDPART_SIZ_FULL
}
#endif
};
/**
* au_read_byte - read one byte from the chip
* @mtd: MTD device structure
*
* read function for 8bit buswith
*/
static u_char au_read_byte(struct mtd_info *mtd)
{
struct nand_chip *this = mtd->priv;
u_char ret = readb(this->IO_ADDR_R);
au_sync();
return ret;
}
/**
* au_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 au_write_byte(struct mtd_info *mtd, u_char byte)
{
struct nand_chip *this = mtd->priv;
writeb(byte, this->IO_ADDR_W);
au_sync();
}
/**
* au_read_byte16 - read one byte endianess aware from the chip
* @mtd: MTD device structure
*
* read function for 16bit buswith with
* endianess conversion
*/
static u_char au_read_byte16(struct mtd_info *mtd)
{
struct nand_chip *this = mtd->priv;
u_char ret = (u_char) cpu_to_le16(readw(this->IO_ADDR_R));
au_sync();
return ret;
}
/**
* au_write_byte16 - write one byte endianess aware to the chip
* @mtd: MTD device structure
* @byte: pointer to data byte to write
*
* write function for 16bit buswith with
* endianess conversion
*/
static void au_write_byte16(struct mtd_info *mtd, u_char byte)
{
struct nand_chip *this = mtd->priv;
writew(le16_to_cpu((u16) byte), this->IO_ADDR_W);
au_sync();
}
/**
* au_read_word - read one word from the chip
* @mtd: MTD device structure
*
* read function for 16bit buswith without
* endianess conversion
*/
static u16 au_read_word(struct mtd_info *mtd)
{
struct nand_chip *this = mtd->priv;
u16 ret = readw(this->IO_ADDR_R);
au_sync();
return ret;
}
/**
* au_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 au_write_word(struct mtd_info *mtd, u16 word)
{
struct nand_chip *this = mtd->priv;
writew(word, this->IO_ADDR_W);
au_sync();
}
/**
* au_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 au_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
{
int i;
struct nand_chip *this = mtd->priv;
for (i=0; i<len; i++) {
writeb(buf[i], this->IO_ADDR_W);
au_sync();
}
}
/**
* au_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 au_read_buf(struct mtd_info *mtd, u_char *buf, int len)
{
int i;
struct nand_chip *this = mtd->priv;
for (i=0; i<len; i++) {
buf[i] = readb(this->IO_ADDR_R);
au_sync();
}
}
/**
* au_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 au_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
{
int i;
struct nand_chip *this = mtd->priv;
for (i=0; i<len; i++) {
if (buf[i] != readb(this->IO_ADDR_R))
return -EFAULT;
au_sync();
}
return 0;
}
/**
* au_write_buf16 - write buffer to chip
* @mtd: MTD device structure
* @buf: data buffer
* @len: number of bytes to write
*
* write function for 16bit buswith
*/
static void au_write_buf16(struct mtd_info *mtd, const u_char *buf, int len)
{
int i;
struct nand_chip *this = mtd->priv;
u16 *p = (u16 *) buf;
len >>= 1;
for (i=0; i<len; i++) {
writew(p[i], this->IO_ADDR_W);
au_sync();
}
}
/**
* au_read_buf16 - read chip data into buffer
* @mtd: MTD device structure
* @buf: buffer to store date
* @len: number of bytes to read
*
* read function for 16bit buswith
*/
static void au_read_buf16(struct mtd_info *mtd, u_char *buf, int len)
{
int i;
struct nand_chip *this = mtd->priv;
u16 *p = (u16 *) buf;
len >>= 1;
for (i=0; i<len; i++) {
p[i] = readw(this->IO_ADDR_R);
au_sync();
}
}
/**
* au_verify_buf16 - 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 16bit buswith
*/
static int au_verify_buf16(struct mtd_info *mtd, const u_char *buf, int len)
{
int i;
struct nand_chip *this = mtd->priv;
u16 *p = (u16 *) buf;
len >>= 1;
for (i=0; i<len; i++) {
if (p[i] != readw(this->IO_ADDR_R))
return -EFAULT;
au_sync();
}
return 0;
}
static void au1550_hwcontrol(struct mtd_info *mtd, int cmd)
{
register struct nand_chip *this = mtd->priv;
switch(cmd){
case NAND_CTL_SETCLE: this->IO_ADDR_W = p_nand + MEM_STNAND_CMD; break;
case NAND_CTL_CLRCLE: this->IO_ADDR_W = p_nand + MEM_STNAND_DATA; break;
case NAND_CTL_SETALE: this->IO_ADDR_W = p_nand + MEM_STNAND_ADDR; break;
case NAND_CTL_CLRALE:
this->IO_ADDR_W = p_nand + MEM_STNAND_DATA;
/* FIXME: Nobody knows why this is neccecary,
* but it works only that way */
udelay(1);
break;
case NAND_CTL_SETNCE:
/* assert (force assert) chip enable */
au_writel((1<<(4+NAND_CS)) , MEM_STNDCTL); break;
break;
case NAND_CTL_CLRNCE:
/* deassert chip enable */
au_writel(0, MEM_STNDCTL); break;
break;
}
this->IO_ADDR_R = this->IO_ADDR_W;
/* Drain the writebuffer */
au_sync();
}
int au1550_device_ready(struct mtd_info *mtd)
{
int ret = (au_readl(MEM_STSTAT) & 0x1) ? 1 : 0;
au_sync();
return ret;
}
/*
* Main initialization routine
*/
int __init au1550_init (void)
{
struct nand_chip *this;
u16 boot_swapboot = 0; /* default value */
int retval;
/* Allocate memory for MTD device structure and private data */
au1550_mtd = kmalloc (sizeof(struct mtd_info) +
sizeof (struct nand_chip), GFP_KERNEL);
if (!au1550_mtd) {
printk ("Unable to allocate NAND MTD dev structure.\n");
return -ENOMEM;
}
/* Get pointer to private data */
this = (struct nand_chip *) (&au1550_mtd[1]);
/* Initialize structures */
memset((char *) au1550_mtd, 0, sizeof(struct mtd_info));
memset((char *) this, 0, sizeof(struct nand_chip));
/* Link the private data with the MTD structure */
au1550_mtd->priv = this;
/* MEM_STNDCTL: disable ints, disable nand boot */
au_writel(0, MEM_STNDCTL);
#ifdef CONFIG_MIPS_PB1550
/* set gpio206 high */
au_writel(au_readl(GPIO2_DIR) & ~(1<<6), GPIO2_DIR);
boot_swapboot = (au_readl(MEM_STSTAT) & (0x7<<1)) |
((bcsr->status >> 6) & 0x1);
switch (boot_swapboot) {
case 0:
case 2:
case 8:
case 0xC:
case 0xD:
/* x16 NAND Flash */
nand_width = 0;
break;
case 1:
case 9:
case 3:
case 0xE:
case 0xF:
/* x8 NAND Flash */
nand_width = 1;
break;
default:
printk("Pb1550 NAND: bad boot:swap\n");
retval = -EINVAL;
goto outmem;
}
#endif
/* Configure RCE1 - should be done by YAMON */
au_writel(0x5 | (nand_width << 22), 0xB4001010); /* MEM_STCFG1 */
au_writel(NAND_TIMING, 0xB4001014); /* MEM_STTIME1 */
au_sync();
/* setup and enable chip select, MEM_STADDR1 */
/* we really need to decode offsets only up till 0x20 */
au_writel((1<<28) | (NAND_PHYS_ADDR>>4) |
(((NAND_PHYS_ADDR + 0x1000)-1) & (0x3fff<<18)>>18),
MEM_STADDR1);
au_sync();
p_nand = ioremap(NAND_PHYS_ADDR, 0x1000);
/* Set address of hardware control function */
this->hwcontrol = au1550_hwcontrol;
this->dev_ready = au1550_device_ready;
/* 30 us command delay time */
this->chip_delay = 30;
this->eccmode = NAND_ECC_SOFT;
this->options = NAND_NO_AUTOINCR;
if (!nand_width)
this->options |= NAND_BUSWIDTH_16;
this->read_byte = (!nand_width) ? au_read_byte16 : au_read_byte;
this->write_byte = (!nand_width) ? au_write_byte16 : au_write_byte;
this->write_word = au_write_word;
this->read_word = au_read_word;
this->write_buf = (!nand_width) ? au_write_buf16 : au_write_buf;
this->read_buf = (!nand_width) ? au_read_buf16 : au_read_buf;
this->verify_buf = (!nand_width) ? au_verify_buf16 : au_verify_buf;
/* Scan to find existence of the device */
if (nand_scan (au1550_mtd, 1)) {
retval = -ENXIO;
goto outio;
}
/* Register the partitions */
add_mtd_partitions(au1550_mtd, partition_info, NUM_PARTITIONS);
return 0;
outio:
iounmap ((void *)p_nand);
outmem:
kfree (au1550_mtd);
return retval;
}
module_init(au1550_init);
/*
* Clean up routine
*/
#ifdef MODULE
static void __exit au1550_cleanup (void)
{
struct nand_chip *this = (struct nand_chip *) &au1550_mtd[1];
/* Release resources, unregister device */
nand_release (au1550_mtd);
/* Free the MTD device structure */
kfree (au1550_mtd);
/* Unmap */
iounmap ((void *)p_nand);
}
module_exit(au1550_cleanup);
#endif
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Embedded Edge, LLC");
MODULE_DESCRIPTION("Board-specific glue layer for NAND flash on Pb1550 board");

225
drivers/mtd/nand/autcpu12.c Normal file
查看文件

@@ -0,0 +1,225 @@
/*
* drivers/mtd/autcpu12.c
*
* Copyright (c) 2002 Thomas Gleixner <tgxl@linutronix.de>
*
* Derived from drivers/mtd/spia.c
* Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
*
* $Id: autcpu12.c,v 1.22 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.
*
* Overview:
* This is a device driver for the NAND flash device found on the
* autronix autcpu12 board, which is a SmartMediaCard. It supports
* 16MiB, 32MiB and 64MiB cards.
*
*
* 02-12-2002 TG Cleanup of module params
*
* 02-20-2002 TG adjusted for different rd/wr adress support
* added support for read device ready/busy line
* added page_cache
*
* 10-06-2002 TG 128K card support added
*/
#include <linux/version.h>
#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/arch/hardware.h>
#include <asm/sizes.h>
#include <asm/arch/autcpu12.h>
/*
* MTD structure for AUTCPU12 board
*/
static struct mtd_info *autcpu12_mtd = NULL;
static int autcpu12_io_base = CS89712_VIRT_BASE;
static int autcpu12_fio_pbase = AUTCPU12_PHYS_SMC;
static int autcpu12_fio_ctrl = AUTCPU12_SMC_SELECT_OFFSET;
static int autcpu12_pedr = AUTCPU12_SMC_PORT_OFFSET;
static void __iomem * autcpu12_fio_base;
/*
* Define partitions for flash devices
*/
static struct mtd_partition partition_info16k[] = {
{ .name = "AUTCPU12 flash partition 1",
.offset = 0,
.size = 8 * SZ_1M },
{ .name = "AUTCPU12 flash partition 2",
.offset = 8 * SZ_1M,
.size = 8 * SZ_1M },
};
static struct mtd_partition partition_info32k[] = {
{ .name = "AUTCPU12 flash partition 1",
.offset = 0,
.size = 8 * SZ_1M },
{ .name = "AUTCPU12 flash partition 2",
.offset = 8 * SZ_1M,
.size = 24 * SZ_1M },
};
static struct mtd_partition partition_info64k[] = {
{ .name = "AUTCPU12 flash partition 1",
.offset = 0,
.size = 16 * SZ_1M },
{ .name = "AUTCPU12 flash partition 2",
.offset = 16 * SZ_1M,
.size = 48 * SZ_1M },
};
static struct mtd_partition partition_info128k[] = {
{ .name = "AUTCPU12 flash partition 1",
.offset = 0,
.size = 16 * SZ_1M },
{ .name = "AUTCPU12 flash partition 2",
.offset = 16 * SZ_1M,
.size = 112 * SZ_1M },
};
#define NUM_PARTITIONS16K 2
#define NUM_PARTITIONS32K 2
#define NUM_PARTITIONS64K 2
#define NUM_PARTITIONS128K 2
/*
* hardware specific access to control-lines
*/
static void autcpu12_hwcontrol(struct mtd_info *mtd, int cmd)
{
switch(cmd){
case NAND_CTL_SETCLE: (*(volatile unsigned char *) (autcpu12_io_base + autcpu12_pedr)) |= AUTCPU12_SMC_CLE; break;
case NAND_CTL_CLRCLE: (*(volatile unsigned char *) (autcpu12_io_base + autcpu12_pedr)) &= ~AUTCPU12_SMC_CLE; break;
case NAND_CTL_SETALE: (*(volatile unsigned char *) (autcpu12_io_base + autcpu12_pedr)) |= AUTCPU12_SMC_ALE; break;
case NAND_CTL_CLRALE: (*(volatile unsigned char *) (autcpu12_io_base + autcpu12_pedr)) &= ~AUTCPU12_SMC_ALE; break;
case NAND_CTL_SETNCE: (*(volatile unsigned char *) (autcpu12_fio_base + autcpu12_fio_ctrl)) = 0x01; break;
case NAND_CTL_CLRNCE: (*(volatile unsigned char *) (autcpu12_fio_base + autcpu12_fio_ctrl)) = 0x00; break;
}
}
/*
* read device ready pin
*/
int autcpu12_device_ready(struct mtd_info *mtd)
{
return ( (*(volatile unsigned char *) (autcpu12_io_base + autcpu12_pedr)) & AUTCPU12_SMC_RDY) ? 1 : 0;
}
/*
* Main initialization routine
*/
int __init autcpu12_init (void)
{
struct nand_chip *this;
int err = 0;
/* Allocate memory for MTD device structure and private data */
autcpu12_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip),
GFP_KERNEL);
if (!autcpu12_mtd) {
printk ("Unable to allocate AUTCPU12 NAND MTD device structure.\n");
err = -ENOMEM;
goto out;
}
/* map physical adress */
autcpu12_fio_base = ioremap(autcpu12_fio_pbase,SZ_1K);
if(!autcpu12_fio_base){
printk("Ioremap autcpu12 SmartMedia Card failed\n");
err = -EIO;
goto out_mtd;
}
/* Get pointer to private data */
this = (struct nand_chip *) (&autcpu12_mtd[1]);
/* Initialize structures */
memset((char *) autcpu12_mtd, 0, sizeof(struct mtd_info));
memset((char *) this, 0, sizeof(struct nand_chip));
/* Link the private data with the MTD structure */
autcpu12_mtd->priv = this;
/* Set address of NAND IO lines */
this->IO_ADDR_R = autcpu12_fio_base;
this->IO_ADDR_W = autcpu12_fio_base;
this->hwcontrol = autcpu12_hwcontrol;
this->dev_ready = autcpu12_device_ready;
/* 20 us command delay time */
this->chip_delay = 20;
this->eccmode = NAND_ECC_SOFT;
/* Enable the following for a flash based bad block table */
/*
this->options = NAND_USE_FLASH_BBT;
*/
this->options = NAND_USE_FLASH_BBT;
/* Scan to find existance of the device */
if (nand_scan (autcpu12_mtd, 1)) {
err = -ENXIO;
goto out_ior;
}
/* Register the partitions */
switch(autcpu12_mtd->size){
case SZ_16M: add_mtd_partitions(autcpu12_mtd, partition_info16k, NUM_PARTITIONS16K); break;
case SZ_32M: add_mtd_partitions(autcpu12_mtd, partition_info32k, NUM_PARTITIONS32K); break;
case SZ_64M: add_mtd_partitions(autcpu12_mtd, partition_info64k, NUM_PARTITIONS64K); break;
case SZ_128M: add_mtd_partitions(autcpu12_mtd, partition_info128k, NUM_PARTITIONS128K); break;
default: {
printk ("Unsupported SmartMedia device\n");
err = -ENXIO;
goto out_ior;
}
}
goto out;
out_ior:
iounmap((void *)autcpu12_fio_base);
out_mtd:
kfree (autcpu12_mtd);
out:
return err;
}
module_init(autcpu12_init);
/*
* Clean up routine
*/
#ifdef MODULE
static void __exit autcpu12_cleanup (void)
{
/* Release resources, unregister device */
nand_release (autcpu12_mtd);
/* unmap physical adress */
iounmap((void *)autcpu12_fio_base);
/* Free the MTD device structure */
kfree (autcpu12_mtd);
}
module_exit(autcpu12_cleanup);
#endif
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Thomas Gleixner <tglx@linutronix.de>");
MODULE_DESCRIPTION("Glue layer for SmartMediaCard on autronix autcpu12");

文件差異過大導致無法顯示 Load Diff

218
drivers/mtd/nand/edb7312.c Normal file
查看文件

@@ -0,0 +1,218 @@
/*
* drivers/mtd/nand/edb7312.c
*
* Copyright (C) 2002 Marius Gr<47>ger (mag@sysgo.de)
*
* Derived from drivers/mtd/nand/autcpu12.c
* Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de)
*
* $Id: edb7312.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.
*
* Overview:
* This is a device driver for the NAND flash device found on the
* CLEP7312 board which utilizes the Toshiba TC58V64AFT part. This is
* a 64Mibit (8MiB x 8 bits) NAND flash device.
*/
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/partitions.h>
#include <asm/io.h>
#include <asm/arch/hardware.h> /* for CLPS7111_VIRT_BASE */
#include <asm/sizes.h>
#include <asm/hardware/clps7111.h>
/*
* MTD structure for EDB7312 board
*/
static struct mtd_info *ep7312_mtd = NULL;
/*
* Values specific to the EDB7312 board (used with EP7312 processor)
*/
#define EP7312_FIO_PBASE 0x10000000 /* Phys address of flash */
#define EP7312_PXDR 0x0001 /*
* IO offset to Port B data register
* where the CLE, ALE and NCE pins
* are wired to.
*/
#define EP7312_PXDDR 0x0041 /*
* IO offset to Port B data direction
* register so we can control the IO
* lines.
*/
/*
* Module stuff
*/
static unsigned long ep7312_fio_pbase = EP7312_FIO_PBASE;
static void __iomem * ep7312_pxdr = (void __iomem *) EP7312_PXDR;
static void __iomem * ep7312_pxddr = (void __iomem *) EP7312_PXDDR;
#ifdef CONFIG_MTD_PARTITIONS
/*
* Define static partitions for flash device
*/
static struct mtd_partition partition_info[] = {
{ .name = "EP7312 Nand Flash",
.offset = 0,
.size = 8*1024*1024 }
};
#define NUM_PARTITIONS 1
#endif
/*
* hardware specific access to control-lines
*/
static void ep7312_hwcontrol(struct mtd_info *mtd, int cmd)
{
switch(cmd) {
case NAND_CTL_SETCLE:
clps_writeb(clps_readb(ep7312_pxdr) | 0x10, ep7312_pxdr);
break;
case NAND_CTL_CLRCLE:
clps_writeb(clps_readb(ep7312_pxdr) & ~0x10, ep7312_pxdr);
break;
case NAND_CTL_SETALE:
clps_writeb(clps_readb(ep7312_pxdr) | 0x20, ep7312_pxdr);
break;
case NAND_CTL_CLRALE:
clps_writeb(clps_readb(ep7312_pxdr) & ~0x20, ep7312_pxdr);
break;
case NAND_CTL_SETNCE:
clps_writeb((clps_readb(ep7312_pxdr) | 0x80) & ~0x40, ep7312_pxdr);
break;
case NAND_CTL_CLRNCE:
clps_writeb((clps_readb(ep7312_pxdr) | 0x80) | 0x40, ep7312_pxdr);
break;
}
}
/*
* read device ready pin
*/
static int ep7312_device_ready(struct mtd_info *mtd)
{
return 1;
}
#ifdef CONFIG_MTD_PARTITIONS
const char *part_probes[] = { "cmdlinepart", NULL };
#endif
/*
* Main initialization routine
*/
static int __init ep7312_init (void)
{
struct nand_chip *this;
const char *part_type = 0;
int mtd_parts_nb = 0;
struct mtd_partition *mtd_parts = 0;
void __iomem * ep7312_fio_base;
/* Allocate memory for MTD device structure and private data */
ep7312_mtd = kmalloc(sizeof(struct mtd_info) +
sizeof(struct nand_chip),
GFP_KERNEL);
if (!ep7312_mtd) {
printk("Unable to allocate EDB7312 NAND MTD device structure.\n");
return -ENOMEM;
}
/* map physical adress */
ep7312_fio_base = ioremap(ep7312_fio_pbase, SZ_1K);
if(!ep7312_fio_base) {
printk("ioremap EDB7312 NAND flash failed\n");
kfree(ep7312_mtd);
return -EIO;
}
/* Get pointer to private data */
this = (struct nand_chip *) (&ep7312_mtd[1]);
/* Initialize structures */
memset((char *) ep7312_mtd, 0, sizeof(struct mtd_info));
memset((char *) this, 0, sizeof(struct nand_chip));
/* Link the private data with the MTD structure */
ep7312_mtd->priv = this;
/*
* Set GPIO Port B control register so that the pins are configured
* to be outputs for controlling the NAND flash.
*/
clps_writeb(0xf0, ep7312_pxddr);
/* insert callbacks */
this->IO_ADDR_R = ep7312_fio_base;
this->IO_ADDR_W = ep7312_fio_base;
this->hwcontrol = ep7312_hwcontrol;
this->dev_ready = ep7312_device_ready;
/* 15 us command delay time */
this->chip_delay = 15;
/* Scan to find existence of the device */
if (nand_scan (ep7312_mtd, 1)) {
iounmap((void *)ep7312_fio_base);
kfree (ep7312_mtd);
return -ENXIO;
}
#ifdef CONFIG_MTD_PARTITIONS
ep7312_mtd->name = "edb7312-nand";
mtd_parts_nb = parse_mtd_partitions(ep7312_mtd, part_probes,
&mtd_parts, 0);
if (mtd_parts_nb > 0)
part_type = "command line";
else
mtd_parts_nb = 0;
#endif
if (mtd_parts_nb == 0) {
mtd_parts = partition_info;
mtd_parts_nb = NUM_PARTITIONS;
part_type = "static";
}
/* Register the partitions */
printk(KERN_NOTICE "Using %s partition definition\n", part_type);
add_mtd_partitions(ep7312_mtd, mtd_parts, mtd_parts_nb);
/* Return happy */
return 0;
}
module_init(ep7312_init);
/*
* Clean up routine
*/
static void __exit ep7312_cleanup (void)
{
struct nand_chip *this = (struct nand_chip *) &ep7312_mtd[1];
/* Release resources, unregister device */
nand_release (ap7312_mtd);
/* Free internal data buffer */
kfree (this->data_buf);
/* Free the MTD device structure */
kfree (ep7312_mtd);
}
module_exit(ep7312_cleanup);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Marius Groeger <mag@sysgo.de>");
MODULE_DESCRIPTION("MTD map driver for Cogent EDB7312 board");

208
drivers/mtd/nand/h1910.c Normal file
查看文件

@@ -0,0 +1,208 @@
/*
* drivers/mtd/nand/h1910.c
*
* Copyright (C) 2003 Joshua Wise (joshua@joshuawise.com)
*
* Derived from drivers/mtd/nand/edb7312.c
* Copyright (C) 2002 Marius Gr<47>ger (mag@sysgo.de)
* Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de)
*
* $Id: h1910.c,v 1.5 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.
*
* Overview:
* This is a device driver for the NAND flash device found on the
* iPAQ h1910 board which utilizes the Samsung K9F2808 part. This is
* a 128Mibit (16MiB x 8 bits) NAND flash device.
*/
#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/arch/hardware.h> /* for CLPS7111_VIRT_BASE */
#include <asm/sizes.h>
#include <asm/arch/h1900-gpio.h>
#include <asm/arch/ipaq.h>
/*
* MTD structure for EDB7312 board
*/
static struct mtd_info *h1910_nand_mtd = NULL;
/*
* Module stuff
*/
#ifdef CONFIG_MTD_PARTITIONS
/*
* Define static partitions for flash device
*/
static struct mtd_partition partition_info[] = {
{ name: "h1910 NAND Flash",
offset: 0,
size: 16*1024*1024 }
};
#define NUM_PARTITIONS 1
#endif
/*
* hardware specific access to control-lines
*/
static void h1910_hwcontrol(struct mtd_info *mtd, int cmd)
{
struct nand_chip* this = (struct nand_chip *) (mtd->priv);
switch(cmd) {
case NAND_CTL_SETCLE:
this->IO_ADDR_R |= (1 << 2);
this->IO_ADDR_W |= (1 << 2);
break;
case NAND_CTL_CLRCLE:
this->IO_ADDR_R &= ~(1 << 2);
this->IO_ADDR_W &= ~(1 << 2);
break;
case NAND_CTL_SETALE:
this->IO_ADDR_R |= (1 << 3);
this->IO_ADDR_W |= (1 << 3);
break;
case NAND_CTL_CLRALE:
this->IO_ADDR_R &= ~(1 << 3);
this->IO_ADDR_W &= ~(1 << 3);
break;
case NAND_CTL_SETNCE:
break;
case NAND_CTL_CLRNCE:
break;
}
}
/*
* read device ready pin
*/
#if 0
static int h1910_device_ready(struct mtd_info *mtd)
{
return (GPLR(55) & GPIO_bit(55));
}
#endif
/*
* Main initialization routine
*/
static int __init h1910_init (void)
{
struct nand_chip *this;
const char *part_type = 0;
int mtd_parts_nb = 0;
struct mtd_partition *mtd_parts = 0;
void __iomem *nandaddr;
if (!machine_is_h1900())
return -ENODEV;
nandaddr = __ioremap(0x08000000, 0x1000, 0, 1);
if (!nandaddr) {
printk("Failed to ioremap nand flash.\n");
return -ENOMEM;
}
/* Allocate memory for MTD device structure and private data */
h1910_nand_mtd = kmalloc(sizeof(struct mtd_info) +
sizeof(struct nand_chip),
GFP_KERNEL);
if (!h1910_nand_mtd) {
printk("Unable to allocate h1910 NAND MTD device structure.\n");
iounmap ((void *) nandaddr);
return -ENOMEM;
}
/* Get pointer to private data */
this = (struct nand_chip *) (&h1910_nand_mtd[1]);
/* Initialize structures */
memset((char *) h1910_nand_mtd, 0, sizeof(struct mtd_info));
memset((char *) this, 0, sizeof(struct nand_chip));
/* Link the private data with the MTD structure */
h1910_nand_mtd->priv = this;
/*
* Enable VPEN
*/
GPSR(37) = GPIO_bit(37);
/* insert callbacks */
this->IO_ADDR_R = nandaddr;
this->IO_ADDR_W = nandaddr;
this->hwcontrol = h1910_hwcontrol;
this->dev_ready = NULL; /* unknown whether that was correct or not so we will just do it like this */
/* 15 us command delay time */
this->chip_delay = 50;
this->eccmode = NAND_ECC_SOFT;
this->options = NAND_NO_AUTOINCR;
/* Scan to find existence of the device */
if (nand_scan (h1910_nand_mtd, 1)) {
printk(KERN_NOTICE "No NAND device - returning -ENXIO\n");
kfree (h1910_nand_mtd);
iounmap ((void *) nandaddr);
return -ENXIO;
}
#ifdef CONFIG_MTD_CMDLINE_PARTS
mtd_parts_nb = parse_cmdline_partitions(h1910_nand_mtd, &mtd_parts,
"h1910-nand");
if (mtd_parts_nb > 0)
part_type = "command line";
else
mtd_parts_nb = 0;
#endif
if (mtd_parts_nb == 0)
{
mtd_parts = partition_info;
mtd_parts_nb = NUM_PARTITIONS;
part_type = "static";
}
/* Register the partitions */
printk(KERN_NOTICE "Using %s partition definition\n", part_type);
add_mtd_partitions(h1910_nand_mtd, mtd_parts, mtd_parts_nb);
/* Return happy */
return 0;
}
module_init(h1910_init);
/*
* Clean up routine
*/
static void __exit h1910_cleanup (void)
{
struct nand_chip *this = (struct nand_chip *) &h1910_nand_mtd[1];
/* Release resources, unregister device */
nand_release (h1910_nand_mtd);
/* Release io resource */
iounmap ((void *) this->IO_ADDR_W);
/* Free the MTD device structure */
kfree (h1910_nand_mtd);
}
module_exit(h1910_cleanup);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Joshua Wise <joshua at joshuawise dot com>");
MODULE_DESCRIPTION("NAND flash driver for iPAQ h1910");

2563
drivers/mtd/nand/nand_base.c Normal file

文件差異過大導致無法顯示 Load Diff

1056
drivers/mtd/nand/nand_bbt.c Normal file

文件差異過大導致無法顯示 Load Diff

250
drivers/mtd/nand/nand_ecc.c Normal file
查看文件

@@ -0,0 +1,250 @@
/*
* This file contains an ECC algorithm from Toshiba that detects and
* corrects 1 bit errors in a 256 byte block of data.
*
* drivers/mtd/nand/nand_ecc.c
*
* Copyright (C) 2000-2004 Steven J. Hill (sjhill@realitydiluted.com)
* Toshiba America Electronics Components, Inc.
*
* $Id: nand_ecc.c,v 1.14 2004/06/16 15:34:37 gleixner Exp $
*
* This file 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 or (at your option) any
* later version.
*
* This file is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this file; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*
* As a special exception, if other files instantiate templates or use
* macros or inline functions from these files, or you compile these
* files and link them with other works to produce a work based on these
* files, these files do not by themselves cause the resulting work to be
* covered by the GNU General Public License. However the source code for
* these files must still be made available in accordance with section (3)
* of the GNU General Public License.
*
* This exception does not invalidate any other reasons why a work based on
* this file might be covered by the GNU General Public License.
*/
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mtd/nand_ecc.h>
/*
* Pre-calculated 256-way 1 byte column parity
*/
static const u_char nand_ecc_precalc_table[] = {
0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00,
0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65,
0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66,
0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03,
0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69,
0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c,
0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f,
0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a,
0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a,
0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f,
0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c,
0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69,
0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03,
0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66,
0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65,
0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00
};
/**
* nand_trans_result - [GENERIC] create non-inverted ECC
* @reg2: line parity reg 2
* @reg3: line parity reg 3
* @ecc_code: ecc
*
* Creates non-inverted ECC code from line parity
*/
static void nand_trans_result(u_char reg2, u_char reg3,
u_char *ecc_code)
{
u_char a, b, i, tmp1, tmp2;
/* Initialize variables */
a = b = 0x80;
tmp1 = tmp2 = 0;
/* Calculate first ECC byte */
for (i = 0; i < 4; i++) {
if (reg3 & a) /* LP15,13,11,9 --> ecc_code[0] */
tmp1 |= b;
b >>= 1;
if (reg2 & a) /* LP14,12,10,8 --> ecc_code[0] */
tmp1 |= b;
b >>= 1;
a >>= 1;
}
/* Calculate second ECC byte */
b = 0x80;
for (i = 0; i < 4; i++) {
if (reg3 & a) /* LP7,5,3,1 --> ecc_code[1] */
tmp2 |= b;
b >>= 1;
if (reg2 & a) /* LP6,4,2,0 --> ecc_code[1] */
tmp2 |= b;
b >>= 1;
a >>= 1;
}
/* Store two of the ECC bytes */
ecc_code[0] = tmp1;
ecc_code[1] = tmp2;
}
/**
* nand_calculate_ecc - [NAND Interface] Calculate 3 byte ECC code for 256 byte block
* @mtd: MTD block structure
* @dat: raw data
* @ecc_code: buffer for ECC
*/
int nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code)
{
u_char idx, reg1, reg2, reg3;
int j;
/* Initialize variables */
reg1 = reg2 = reg3 = 0;
ecc_code[0] = ecc_code[1] = ecc_code[2] = 0;
/* Build up column parity */
for(j = 0; j < 256; j++) {
/* Get CP0 - CP5 from table */
idx = nand_ecc_precalc_table[dat[j]];
reg1 ^= (idx & 0x3f);
/* All bit XOR = 1 ? */
if (idx & 0x40) {
reg3 ^= (u_char) j;
reg2 ^= ~((u_char) j);
}
}
/* Create non-inverted ECC code from line parity */
nand_trans_result(reg2, reg3, ecc_code);
/* Calculate final ECC code */
ecc_code[0] = ~ecc_code[0];
ecc_code[1] = ~ecc_code[1];
ecc_code[2] = ((~reg1) << 2) | 0x03;
return 0;
}
/**
* nand_correct_data - [NAND Interface] Detect and correct bit error(s)
* @mtd: MTD block structure
* @dat: raw data read from the chip
* @read_ecc: ECC from the chip
* @calc_ecc: the ECC calculated from raw data
*
* Detect and correct a 1 bit error for 256 byte block
*/
int nand_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc)
{
u_char a, b, c, d1, d2, d3, add, bit, i;
/* Do error detection */
d1 = calc_ecc[0] ^ read_ecc[0];
d2 = calc_ecc[1] ^ read_ecc[1];
d3 = calc_ecc[2] ^ read_ecc[2];
if ((d1 | d2 | d3) == 0) {
/* No errors */
return 0;
}
else {
a = (d1 ^ (d1 >> 1)) & 0x55;
b = (d2 ^ (d2 >> 1)) & 0x55;
c = (d3 ^ (d3 >> 1)) & 0x54;
/* Found and will correct single bit error in the data */
if ((a == 0x55) && (b == 0x55) && (c == 0x54)) {
c = 0x80;
add = 0;
a = 0x80;
for (i=0; i<4; i++) {
if (d1 & c)
add |= a;
c >>= 2;
a >>= 1;
}
c = 0x80;
for (i=0; i<4; i++) {
if (d2 & c)
add |= a;
c >>= 2;
a >>= 1;
}
bit = 0;
b = 0x04;
c = 0x80;
for (i=0; i<3; i++) {
if (d3 & c)
bit |= b;
c >>= 2;
b >>= 1;
}
b = 0x01;
a = dat[add];
a ^= (b << bit);
dat[add] = a;
return 1;
}
else {
i = 0;
while (d1) {
if (d1 & 0x01)
++i;
d1 >>= 1;
}
while (d2) {
if (d2 & 0x01)
++i;
d2 >>= 1;
}
while (d3) {
if (d3 & 0x01)
++i;
d3 >>= 1;
}
if (i == 1) {
/* ECC Code Error Correction */
read_ecc[0] = calc_ecc[0];
read_ecc[1] = calc_ecc[1];
read_ecc[2] = calc_ecc[2];
return 2;
}
else {
/* Uncorrectable Error */
return -1;
}
}
}
/* Should never happen */
return -1;
}
EXPORT_SYMBOL(nand_calculate_ecc);
EXPORT_SYMBOL(nand_correct_data);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Steven J. Hill <sjhill@realitydiluted.com>");
MODULE_DESCRIPTION("Generic NAND ECC support");

129
drivers/mtd/nand/nand_ids.c Normal file
查看文件

@@ -0,0 +1,129 @@
/*
* drivers/mtd/nandids.c
*
* Copyright (C) 2002 Thomas Gleixner (tglx@linutronix.de)
*
* $Id: nand_ids.c,v 1.10 2004/05/26 13:40:12 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 <linux/module.h>
#include <linux/mtd/nand.h>
/*
* Chip ID list
*
* Name. ID code, pagesize, chipsize in MegaByte, eraseblock size,
* options
*
* Pagesize; 0, 256, 512
* 0 get this information from the extended chip ID
+ 256 256 Byte page size
* 512 512 Byte page size
*/
struct nand_flash_dev nand_flash_ids[] = {
{"NAND 1MiB 5V 8-bit", 0x6e, 256, 1, 0x1000, 0},
{"NAND 2MiB 5V 8-bit", 0x64, 256, 2, 0x1000, 0},
{"NAND 4MiB 5V 8-bit", 0x6b, 512, 4, 0x2000, 0},
{"NAND 1MiB 3,3V 8-bit", 0xe8, 256, 1, 0x1000, 0},
{"NAND 1MiB 3,3V 8-bit", 0xec, 256, 1, 0x1000, 0},
{"NAND 2MiB 3,3V 8-bit", 0xea, 256, 2, 0x1000, 0},
{"NAND 4MiB 3,3V 8-bit", 0xd5, 512, 4, 0x2000, 0},
{"NAND 4MiB 3,3V 8-bit", 0xe3, 512, 4, 0x2000, 0},
{"NAND 4MiB 3,3V 8-bit", 0xe5, 512, 4, 0x2000, 0},
{"NAND 8MiB 3,3V 8-bit", 0xd6, 512, 8, 0x2000, 0},
{"NAND 8MiB 1,8V 8-bit", 0x39, 512, 8, 0x2000, 0},
{"NAND 8MiB 3,3V 8-bit", 0xe6, 512, 8, 0x2000, 0},
{"NAND 8MiB 1,8V 16-bit", 0x49, 512, 8, 0x2000, NAND_BUSWIDTH_16},
{"NAND 8MiB 3,3V 16-bit", 0x59, 512, 8, 0x2000, NAND_BUSWIDTH_16},
{"NAND 16MiB 1,8V 8-bit", 0x33, 512, 16, 0x4000, 0},
{"NAND 16MiB 3,3V 8-bit", 0x73, 512, 16, 0x4000, 0},
{"NAND 16MiB 1,8V 16-bit", 0x43, 512, 16, 0x4000, NAND_BUSWIDTH_16},
{"NAND 16MiB 3,3V 16-bit", 0x53, 512, 16, 0x4000, NAND_BUSWIDTH_16},
{"NAND 32MiB 1,8V 8-bit", 0x35, 512, 32, 0x4000, 0},
{"NAND 32MiB 3,3V 8-bit", 0x75, 512, 32, 0x4000, 0},
{"NAND 32MiB 1,8V 16-bit", 0x45, 512, 32, 0x4000, NAND_BUSWIDTH_16},
{"NAND 32MiB 3,3V 16-bit", 0x55, 512, 32, 0x4000, NAND_BUSWIDTH_16},
{"NAND 64MiB 1,8V 8-bit", 0x36, 512, 64, 0x4000, 0},
{"NAND 64MiB 3,3V 8-bit", 0x76, 512, 64, 0x4000, 0},
{"NAND 64MiB 1,8V 16-bit", 0x46, 512, 64, 0x4000, NAND_BUSWIDTH_16},
{"NAND 64MiB 3,3V 16-bit", 0x56, 512, 64, 0x4000, NAND_BUSWIDTH_16},
{"NAND 128MiB 1,8V 8-bit", 0x78, 512, 128, 0x4000, 0},
{"NAND 128MiB 3,3V 8-bit", 0x79, 512, 128, 0x4000, 0},
{"NAND 128MiB 1,8V 16-bit", 0x72, 512, 128, 0x4000, NAND_BUSWIDTH_16},
{"NAND 128MiB 3,3V 16-bit", 0x74, 512, 128, 0x4000, NAND_BUSWIDTH_16},
{"NAND 256MiB 3,3V 8-bit", 0x71, 512, 256, 0x4000, 0},
{"NAND 512MiB 3,3V 8-bit", 0xDC, 512, 512, 0x4000, 0},
/* These are the new chips with large page size. The pagesize
* and the erasesize is determined from the extended id bytes
*/
/* 1 Gigabit */
{"NAND 128MiB 1,8V 8-bit", 0xA1, 0, 128, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR},
{"NAND 128MiB 3,3V 8-bit", 0xF1, 0, 128, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR},
{"NAND 128MiB 1,8V 16-bit", 0xB1, 0, 128, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR},
{"NAND 128MiB 3,3V 16-bit", 0xC1, 0, 128, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR},
/* 2 Gigabit */
{"NAND 256MiB 1,8V 8-bit", 0xAA, 0, 256, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR},
{"NAND 256MiB 3,3V 8-bit", 0xDA, 0, 256, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR},
{"NAND 256MiB 1,8V 16-bit", 0xBA, 0, 256, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR},
{"NAND 256MiB 3,3V 16-bit", 0xCA, 0, 256, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR},
/* 4 Gigabit */
{"NAND 512MiB 1,8V 8-bit", 0xAC, 0, 512, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR},
{"NAND 512MiB 3,3V 8-bit", 0xDC, 0, 512, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR},
{"NAND 512MiB 1,8V 16-bit", 0xBC, 0, 512, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR},
{"NAND 512MiB 3,3V 16-bit", 0xCC, 0, 512, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR},
/* 8 Gigabit */
{"NAND 1GiB 1,8V 8-bit", 0xA3, 0, 1024, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR},
{"NAND 1GiB 3,3V 8-bit", 0xD3, 0, 1024, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR},
{"NAND 1GiB 1,8V 16-bit", 0xB3, 0, 1024, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR},
{"NAND 1GiB 3,3V 16-bit", 0xC3, 0, 1024, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR},
/* 16 Gigabit */
{"NAND 2GiB 1,8V 8-bit", 0xA5, 0, 2048, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR},
{"NAND 2GiB 3,3V 8-bit", 0xD5, 0, 2048, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR},
{"NAND 2GiB 1,8V 16-bit", 0xB5, 0, 2048, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR},
{"NAND 2GiB 3,3V 16-bit", 0xC5, 0, 2048, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR},
/* Renesas AND 1 Gigabit. Those chips do not support extended id and have a strange page/block layout !
* The chosen minimum erasesize is 4 * 2 * 2048 = 16384 Byte, as those chips have an array of 4 page planes
* 1 block = 2 pages, but due to plane arrangement the blocks 0-3 consists of page 0 + 4,1 + 5, 2 + 6, 3 + 7
* Anyway JFFS2 would increase the eraseblock size so we chose a combined one which can be erased in one go
* There are more speed improvements for reads and writes possible, but not implemented now
*/
{"AND 128MiB 3,3V 8-bit", 0x01, 2048, 128, 0x4000, NAND_IS_AND | NAND_NO_AUTOINCR | NAND_4PAGE_ARRAY},
{NULL,}
};
/*
* Manufacturer ID list
*/
struct nand_manufacturers nand_manuf_ids[] = {
{NAND_MFR_TOSHIBA, "Toshiba"},
{NAND_MFR_SAMSUNG, "Samsung"},
{NAND_MFR_FUJITSU, "Fujitsu"},
{NAND_MFR_NATIONAL, "National"},
{NAND_MFR_RENESAS, "Renesas"},
{NAND_MFR_STMICRO, "ST Micro"},
{0x0, "Unknown"}
};
EXPORT_SYMBOL (nand_manuf_ids);
EXPORT_SYMBOL (nand_flash_ids);
MODULE_LICENSE ("GPL");
MODULE_AUTHOR ("Thomas Gleixner <tglx@linutronix.de>");
MODULE_DESCRIPTION ("Nand device & manufacturer ID's");

1613
drivers/mtd/nand/nandsim.c Normal file

文件差異過大導致無法顯示 Load Diff

查看文件

@@ -0,0 +1,420 @@
/*
* drivers/mtd/nand/ppchameleonevb.c
*
* Copyright (C) 2003 DAVE Srl (info@wawnet.biz)
*
* Derived from drivers/mtd/nand/edb7312.c
*
*
* $Id: ppchameleonevb.c,v 1.6 2004/11/05 16:07:16 kalev 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.
*
* Overview:
* This is a device driver for the NAND flash devices found on the
* PPChameleon/PPChameleonEVB system.
* PPChameleon options (autodetected):
* - BA model: no NAND
* - ME model: 32MB (Samsung K9F5608U0B)
* - HI model: 128MB (Samsung K9F1G08UOM)
* PPChameleonEVB options:
* - 32MB (Samsung K9F5608U0B)
*/
#include <linux/init.h>
#include <linux/slab.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 <platforms/PPChameleonEVB.h>
#undef USE_READY_BUSY_PIN
#define USE_READY_BUSY_PIN
/* see datasheets (tR) */
#define NAND_BIG_DELAY_US 25
#define NAND_SMALL_DELAY_US 10
/* handy sizes */
#define SZ_4M 0x00400000
#define NAND_SMALL_SIZE 0x02000000
#define NAND_MTD_NAME "ppchameleon-nand"
#define NAND_EVB_MTD_NAME "ppchameleonevb-nand"
/* GPIO pins used to drive NAND chip mounted on processor module */
#define NAND_nCE_GPIO_PIN (0x80000000 >> 1)
#define NAND_CLE_GPIO_PIN (0x80000000 >> 2)
#define NAND_ALE_GPIO_PIN (0x80000000 >> 3)
#define NAND_RB_GPIO_PIN (0x80000000 >> 4)
/* GPIO pins used to drive NAND chip mounted on EVB */
#define NAND_EVB_nCE_GPIO_PIN (0x80000000 >> 14)
#define NAND_EVB_CLE_GPIO_PIN (0x80000000 >> 15)
#define NAND_EVB_ALE_GPIO_PIN (0x80000000 >> 16)
#define NAND_EVB_RB_GPIO_PIN (0x80000000 >> 31)
/*
* MTD structure for PPChameleonEVB board
*/
static struct mtd_info *ppchameleon_mtd = NULL;
static struct mtd_info *ppchameleonevb_mtd = NULL;
/*
* Module stuff
*/
static unsigned long ppchameleon_fio_pbase = CFG_NAND0_PADDR;
static unsigned long ppchameleonevb_fio_pbase = CFG_NAND1_PADDR;
#ifdef MODULE
module_param(ppchameleon_fio_pbase, ulong, 0);
module_param(ppchameleonevb_fio_pbase, ulong, 0);
#else
__setup("ppchameleon_fio_pbase=",ppchameleon_fio_pbase);
__setup("ppchameleonevb_fio_pbase=",ppchameleonevb_fio_pbase);
#endif
#ifdef CONFIG_MTD_PARTITIONS
/*
* Define static partitions for flash devices
*/
static struct mtd_partition partition_info_hi[] = {
{ name: "PPChameleon HI Nand Flash",
offset: 0,
size: 128*1024*1024 }
};
static struct mtd_partition partition_info_me[] = {
{ name: "PPChameleon ME Nand Flash",
offset: 0,
size: 32*1024*1024 }
};
static struct mtd_partition partition_info_evb[] = {
{ name: "PPChameleonEVB Nand Flash",
offset: 0,
size: 32*1024*1024 }
};
#define NUM_PARTITIONS 1
extern int parse_cmdline_partitions(struct mtd_info *master,
struct mtd_partition **pparts,
const char *mtd_id);
#endif
/*
* hardware specific access to control-lines
*/
static void ppchameleon_hwcontrol(struct mtd_info *mtdinfo, int cmd)
{
switch(cmd) {
case NAND_CTL_SETCLE:
MACRO_NAND_CTL_SETCLE((unsigned long)CFG_NAND0_PADDR);
break;
case NAND_CTL_CLRCLE:
MACRO_NAND_CTL_CLRCLE((unsigned long)CFG_NAND0_PADDR);
break;
case NAND_CTL_SETALE:
MACRO_NAND_CTL_SETALE((unsigned long)CFG_NAND0_PADDR);
break;
case NAND_CTL_CLRALE:
MACRO_NAND_CTL_CLRALE((unsigned long)CFG_NAND0_PADDR);
break;
case NAND_CTL_SETNCE:
MACRO_NAND_ENABLE_CE((unsigned long)CFG_NAND0_PADDR);
break;
case NAND_CTL_CLRNCE:
MACRO_NAND_DISABLE_CE((unsigned long)CFG_NAND0_PADDR);
break;
}
}
static void ppchameleonevb_hwcontrol(struct mtd_info *mtdinfo, int cmd)
{
switch(cmd) {
case NAND_CTL_SETCLE:
MACRO_NAND_CTL_SETCLE((unsigned long)CFG_NAND1_PADDR);
break;
case NAND_CTL_CLRCLE:
MACRO_NAND_CTL_CLRCLE((unsigned long)CFG_NAND1_PADDR);
break;
case NAND_CTL_SETALE:
MACRO_NAND_CTL_SETALE((unsigned long)CFG_NAND1_PADDR);
break;
case NAND_CTL_CLRALE:
MACRO_NAND_CTL_CLRALE((unsigned long)CFG_NAND1_PADDR);
break;
case NAND_CTL_SETNCE:
MACRO_NAND_ENABLE_CE((unsigned long)CFG_NAND1_PADDR);
break;
case NAND_CTL_CLRNCE:
MACRO_NAND_DISABLE_CE((unsigned long)CFG_NAND1_PADDR);
break;
}
}
#ifdef USE_READY_BUSY_PIN
/*
* read device ready pin
*/
static int ppchameleon_device_ready(struct mtd_info *minfo)
{
if (in_be32((volatile unsigned*)GPIO0_IR) & NAND_RB_GPIO_PIN)
return 1;
return 0;
}
static int ppchameleonevb_device_ready(struct mtd_info *minfo)
{
if (in_be32((volatile unsigned*)GPIO0_IR) & NAND_EVB_RB_GPIO_PIN)
return 1;
return 0;
}
#endif
#ifdef CONFIG_MTD_PARTITIONS
const char *part_probes[] = { "cmdlinepart", NULL };
const char *part_probes_evb[] = { "cmdlinepart", NULL };
#endif
/*
* Main initialization routine
*/
static int __init ppchameleonevb_init (void)
{
struct nand_chip *this;
const char *part_type = 0;
int mtd_parts_nb = 0;
struct mtd_partition *mtd_parts = 0;
void __iomem *ppchameleon_fio_base;
void __iomem *ppchameleonevb_fio_base;
/*********************************
* Processor module NAND (if any) *
*********************************/
/* Allocate memory for MTD device structure and private data */
ppchameleon_mtd = kmalloc(sizeof(struct mtd_info) +
sizeof(struct nand_chip), GFP_KERNEL);
if (!ppchameleon_mtd) {
printk("Unable to allocate PPChameleon NAND MTD device structure.\n");
return -ENOMEM;
}
/* map physical address */
ppchameleon_fio_base = ioremap(ppchameleon_fio_pbase, SZ_4M);
if(!ppchameleon_fio_base) {
printk("ioremap PPChameleon NAND flash failed\n");
kfree(ppchameleon_mtd);
return -EIO;
}
/* Get pointer to private data */
this = (struct nand_chip *) (&ppchameleon_mtd[1]);
/* Initialize structures */
memset((char *) ppchameleon_mtd, 0, sizeof(struct mtd_info));
memset((char *) this, 0, sizeof(struct nand_chip));
/* Link the private data with the MTD structure */
ppchameleon_mtd->priv = this;
/* Initialize GPIOs */
/* Pin mapping for NAND chip */
/*
CE GPIO_01
CLE GPIO_02
ALE GPIO_03
R/B GPIO_04
*/
/* output select */
out_be32((volatile unsigned*)GPIO0_OSRH, in_be32((volatile unsigned*)GPIO0_OSRH) & 0xC0FFFFFF);
/* three-state select */
out_be32((volatile unsigned*)GPIO0_TSRH, in_be32((volatile unsigned*)GPIO0_TSRH) & 0xC0FFFFFF);
/* enable output driver */
out_be32((volatile unsigned*)GPIO0_TCR, in_be32((volatile unsigned*)GPIO0_TCR) | NAND_nCE_GPIO_PIN | NAND_CLE_GPIO_PIN | NAND_ALE_GPIO_PIN);
#ifdef USE_READY_BUSY_PIN
/* three-state select */
out_be32((volatile unsigned*)GPIO0_TSRH, in_be32((volatile unsigned*)GPIO0_TSRH) & 0xFF3FFFFF);
/* high-impedecence */
out_be32((volatile unsigned*)GPIO0_TCR, in_be32((volatile unsigned*)GPIO0_TCR) & (~NAND_RB_GPIO_PIN));
/* input select */
out_be32((volatile unsigned*)GPIO0_ISR1H, (in_be32((volatile unsigned*)GPIO0_ISR1H) & 0xFF3FFFFF) | 0x00400000);
#endif
/* insert callbacks */
this->IO_ADDR_R = ppchameleon_fio_base;
this->IO_ADDR_W = ppchameleon_fio_base;
this->hwcontrol = ppchameleon_hwcontrol;
#ifdef USE_READY_BUSY_PIN
this->dev_ready = ppchameleon_device_ready;
#endif
this->chip_delay = NAND_BIG_DELAY_US;
/* ECC mode */
this->eccmode = NAND_ECC_SOFT;
/* Scan to find existence of the device (it could not be mounted) */
if (nand_scan (ppchameleon_mtd, 1)) {
iounmap((void *)ppchameleon_fio_base);
kfree (ppchameleon_mtd);
goto nand_evb_init;
}
#ifndef USE_READY_BUSY_PIN
/* Adjust delay if necessary */
if (ppchameleon_mtd->size == NAND_SMALL_SIZE)
this->chip_delay = NAND_SMALL_DELAY_US;
#endif
#ifdef CONFIG_MTD_PARTITIONS
ppchameleon_mtd->name = "ppchameleon-nand";
mtd_parts_nb = parse_mtd_partitions(ppchameleon_mtd, part_probes, &mtd_parts, 0);
if (mtd_parts_nb > 0)
part_type = "command line";
else
mtd_parts_nb = 0;
#endif
if (mtd_parts_nb == 0)
{
if (ppchameleon_mtd->size == NAND_SMALL_SIZE)
mtd_parts = partition_info_me;
else
mtd_parts = partition_info_hi;
mtd_parts_nb = NUM_PARTITIONS;
part_type = "static";
}
/* Register the partitions */
printk(KERN_NOTICE "Using %s partition definition\n", part_type);
add_mtd_partitions(ppchameleon_mtd, mtd_parts, mtd_parts_nb);
nand_evb_init:
/****************************
* EVB NAND (always present) *
****************************/
/* Allocate memory for MTD device structure and private data */
ppchameleonevb_mtd = kmalloc(sizeof(struct mtd_info) +
sizeof(struct nand_chip), GFP_KERNEL);
if (!ppchameleonevb_mtd) {
printk("Unable to allocate PPChameleonEVB NAND MTD device structure.\n");
return -ENOMEM;
}
/* map physical address */
ppchameleonevb_fio_base = ioremap(ppchameleonevb_fio_pbase, SZ_4M);
if(!ppchameleonevb_fio_base) {
printk("ioremap PPChameleonEVB NAND flash failed\n");
kfree(ppchameleonevb_mtd);
return -EIO;
}
/* Get pointer to private data */
this = (struct nand_chip *) (&ppchameleonevb_mtd[1]);
/* Initialize structures */
memset((char *) ppchameleonevb_mtd, 0, sizeof(struct mtd_info));
memset((char *) this, 0, sizeof(struct nand_chip));
/* Link the private data with the MTD structure */
ppchameleonevb_mtd->priv = this;
/* Initialize GPIOs */
/* Pin mapping for NAND chip */
/*
CE GPIO_14
CLE GPIO_15
ALE GPIO_16
R/B GPIO_31
*/
/* output select */
out_be32((volatile unsigned*)GPIO0_OSRH, in_be32((volatile unsigned*)GPIO0_OSRH) & 0xFFFFFFF0);
out_be32((volatile unsigned*)GPIO0_OSRL, in_be32((volatile unsigned*)GPIO0_OSRL) & 0x3FFFFFFF);
/* three-state select */
out_be32((volatile unsigned*)GPIO0_TSRH, in_be32((volatile unsigned*)GPIO0_TSRH) & 0xFFFFFFF0);
out_be32((volatile unsigned*)GPIO0_TSRL, in_be32((volatile unsigned*)GPIO0_TSRL) & 0x3FFFFFFF);
/* enable output driver */
out_be32((volatile unsigned*)GPIO0_TCR, in_be32((volatile unsigned*)GPIO0_TCR) | NAND_EVB_nCE_GPIO_PIN |
NAND_EVB_CLE_GPIO_PIN | NAND_EVB_ALE_GPIO_PIN);
#ifdef USE_READY_BUSY_PIN
/* three-state select */
out_be32((volatile unsigned*)GPIO0_TSRL, in_be32((volatile unsigned*)GPIO0_TSRL) & 0xFFFFFFFC);
/* high-impedecence */
out_be32((volatile unsigned*)GPIO0_TCR, in_be32((volatile unsigned*)GPIO0_TCR) & (~NAND_EVB_RB_GPIO_PIN));
/* input select */
out_be32((volatile unsigned*)GPIO0_ISR1L, (in_be32((volatile unsigned*)GPIO0_ISR1L) & 0xFFFFFFFC) | 0x00000001);
#endif
/* insert callbacks */
this->IO_ADDR_R = ppchameleonevb_fio_base;
this->IO_ADDR_W = ppchameleonevb_fio_base;
this->hwcontrol = ppchameleonevb_hwcontrol;
#ifdef USE_READY_BUSY_PIN
this->dev_ready = ppchameleonevb_device_ready;
#endif
this->chip_delay = NAND_SMALL_DELAY_US;
/* ECC mode */
this->eccmode = NAND_ECC_SOFT;
/* Scan to find existence of the device */
if (nand_scan (ppchameleonevb_mtd, 1)) {
iounmap((void *)ppchameleonevb_fio_base);
kfree (ppchameleonevb_mtd);
return -ENXIO;
}
#ifdef CONFIG_MTD_PARTITIONS
ppchameleonevb_mtd->name = NAND_EVB_MTD_NAME;
mtd_parts_nb = parse_mtd_partitions(ppchameleonevb_mtd, part_probes_evb, &mtd_parts, 0);
if (mtd_parts_nb > 0)
part_type = "command line";
else
mtd_parts_nb = 0;
#endif
if (mtd_parts_nb == 0)
{
mtd_parts = partition_info_evb;
mtd_parts_nb = NUM_PARTITIONS;
part_type = "static";
}
/* Register the partitions */
printk(KERN_NOTICE "Using %s partition definition\n", part_type);
add_mtd_partitions(ppchameleonevb_mtd, mtd_parts, mtd_parts_nb);
/* Return happy */
return 0;
}
module_init(ppchameleonevb_init);
/*
* Clean up routine
*/
static void __exit ppchameleonevb_cleanup (void)
{
struct nand_chip *this;
/* Release resources, unregister device(s) */
nand_release (ppchameleon_mtd);
nand_release (ppchameleonevb_mtd);
/* Release iomaps */
this = (struct nand_chip *) &ppchameleon_mtd[1];
iounmap((void *) this->IO_ADDR_R;
this = (struct nand_chip *) &ppchameleonevb_mtd[1];
iounmap((void *) this->IO_ADDR_R;
/* Free the MTD device structure */
kfree (ppchameleon_mtd);
kfree (ppchameleonevb_mtd);
}
module_exit(ppchameleonevb_cleanup);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("DAVE Srl <support-ppchameleon@dave-tech.it>");
MODULE_DESCRIPTION("MTD map driver for DAVE Srl PPChameleonEVB board");

查看文件

@@ -0,0 +1,559 @@
/*
* drivers/mtd/nand/rtc_from4.c
*
* Copyright (C) 2004 Red Hat, Inc.
*
* Derived from drivers/mtd/nand/spia.c
* Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
*
* $Id: rtc_from4.c,v 1.7 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.
*
* Overview:
* This is a device driver for the AG-AND flash device found on the
* Renesas Technology Corp. Flash ROM 4-slot interface board (FROM_BOARD4),
* which utilizes the Renesas HN29V1G91T-30 part.
* This chip is a 1 GBibit (128MiB x 8 bits) AG-AND flash device.
*/
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/rslib.h>
#include <linux/module.h>
#include <linux/mtd/compatmac.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/partitions.h>
#include <asm/io.h>
/*
* MTD structure for Renesas board
*/
static struct mtd_info *rtc_from4_mtd = NULL;
#define RTC_FROM4_MAX_CHIPS 2
/* HS77x9 processor register defines */
#define SH77X9_BCR1 ((volatile unsigned short *)(0xFFFFFF60))
#define SH77X9_BCR2 ((volatile unsigned short *)(0xFFFFFF62))
#define SH77X9_WCR1 ((volatile unsigned short *)(0xFFFFFF64))
#define SH77X9_WCR2 ((volatile unsigned short *)(0xFFFFFF66))
#define SH77X9_MCR ((volatile unsigned short *)(0xFFFFFF68))
#define SH77X9_PCR ((volatile unsigned short *)(0xFFFFFF6C))
#define SH77X9_FRQCR ((volatile unsigned short *)(0xFFFFFF80))
/*
* Values specific to the Renesas Technology Corp. FROM_BOARD4 (used with HS77x9 processor)
*/
/* Address where flash is mapped */
#define RTC_FROM4_FIO_BASE 0x14000000
/* CLE and ALE are tied to address lines 5 & 4, respectively */
#define RTC_FROM4_CLE (1 << 5)
#define RTC_FROM4_ALE (1 << 4)
/* address lines A24-A22 used for chip selection */
#define RTC_FROM4_NAND_ADDR_SLOT3 (0x00800000)
#define RTC_FROM4_NAND_ADDR_SLOT4 (0x00C00000)
#define RTC_FROM4_NAND_ADDR_FPGA (0x01000000)
/* mask address lines A24-A22 used for chip selection */
#define RTC_FROM4_NAND_ADDR_MASK (RTC_FROM4_NAND_ADDR_SLOT3 | RTC_FROM4_NAND_ADDR_SLOT4 | RTC_FROM4_NAND_ADDR_FPGA)
/* FPGA status register for checking device ready (bit zero) */
#define RTC_FROM4_FPGA_SR (RTC_FROM4_NAND_ADDR_FPGA | 0x00000002)
#define RTC_FROM4_DEVICE_READY 0x0001
/* FPGA Reed-Solomon ECC Control register */
#define RTC_FROM4_RS_ECC_CTL (RTC_FROM4_NAND_ADDR_FPGA | 0x00000050)
#define RTC_FROM4_RS_ECC_CTL_CLR (1 << 7)
#define RTC_FROM4_RS_ECC_CTL_GEN (1 << 6)
#define RTC_FROM4_RS_ECC_CTL_FD_E (1 << 5)
/* FPGA Reed-Solomon ECC code base */
#define RTC_FROM4_RS_ECC (RTC_FROM4_NAND_ADDR_FPGA | 0x00000060)
#define RTC_FROM4_RS_ECCN (RTC_FROM4_NAND_ADDR_FPGA | 0x00000080)
/* FPGA Reed-Solomon ECC check register */
#define RTC_FROM4_RS_ECC_CHK (RTC_FROM4_NAND_ADDR_FPGA | 0x00000070)
#define RTC_FROM4_RS_ECC_CHK_ERROR (1 << 7)
/* Undefine for software ECC */
#define RTC_FROM4_HWECC 1
/*
* Module stuff
*/
static void __iomem *rtc_from4_fio_base = P2SEGADDR(RTC_FROM4_FIO_BASE);
const static struct mtd_partition partition_info[] = {
{
.name = "Renesas flash partition 1",
.offset = 0,
.size = MTDPART_SIZ_FULL
},
};
#define NUM_PARTITIONS 1
/*
* hardware specific flash bbt decriptors
* Note: this is to allow debugging by disabling
* NAND_BBT_CREATE and/or NAND_BBT_WRITE
*
*/
static uint8_t bbt_pattern[] = {'B', 'b', 't', '0' };
static uint8_t mirror_pattern[] = {'1', 't', 'b', 'B' };
static struct nand_bbt_descr rtc_from4_bbt_main_descr = {
.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
| NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
.offs = 40,
.len = 4,
.veroffs = 44,
.maxblocks = 4,
.pattern = bbt_pattern
};
static struct nand_bbt_descr rtc_from4_bbt_mirror_descr = {
.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
| NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
.offs = 40,
.len = 4,
.veroffs = 44,
.maxblocks = 4,
.pattern = mirror_pattern
};
#ifdef RTC_FROM4_HWECC
/* the Reed Solomon control structure */
static struct rs_control *rs_decoder;
/*
* hardware specific Out Of Band information
*/
static struct nand_oobinfo rtc_from4_nand_oobinfo = {
.useecc = MTD_NANDECC_AUTOPLACE,
.eccbytes = 32,
.eccpos = {
0, 1, 2, 3, 4, 5, 6, 7,
8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23,
24, 25, 26, 27, 28, 29, 30, 31},
.oobfree = { {32, 32} }
};
/* Aargh. I missed the reversed bit order, when I
* was talking to Renesas about the FPGA.
*
* The table is used for bit reordering and inversion
* of the ecc byte which we get from the FPGA
*/
static uint8_t revbits[256] = {
0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff,
};
#endif
/*
* rtc_from4_hwcontrol - hardware specific access to control-lines
* @mtd: MTD device structure
* @cmd: hardware control command
*
* Address lines (A5 and A4) are used to control Command and Address Latch
* Enable on this board, so set the read/write address appropriately.
*
* Chip Enable is also controlled by the Chip Select (CS5) and
* Address lines (A24-A22), so no action is required here.
*
*/
static void rtc_from4_hwcontrol(struct mtd_info *mtd, int cmd)
{
struct nand_chip* this = (struct nand_chip *) (mtd->priv);
switch(cmd) {
case NAND_CTL_SETCLE:
this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W | RTC_FROM4_CLE);
break;
case NAND_CTL_CLRCLE:
this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W & ~RTC_FROM4_CLE);
break;
case NAND_CTL_SETALE:
this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W | RTC_FROM4_ALE);
break;
case NAND_CTL_CLRALE:
this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W & ~RTC_FROM4_ALE);
break;
case NAND_CTL_SETNCE:
break;
case NAND_CTL_CLRNCE:
break;
}
}
/*
* rtc_from4_nand_select_chip - hardware specific chip select
* @mtd: MTD device structure
* @chip: Chip to select (0 == slot 3, 1 == slot 4)
*
* The chip select is based on address lines A24-A22.
* This driver uses flash slots 3 and 4 (A23-A22).
*
*/
static void rtc_from4_nand_select_chip(struct mtd_info *mtd, int chip)
{
struct nand_chip *this = mtd->priv;
this->IO_ADDR_R = (void __iomem *)((unsigned long)this->IO_ADDR_R & ~RTC_FROM4_NAND_ADDR_MASK);
this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W & ~RTC_FROM4_NAND_ADDR_MASK);
switch(chip) {
case 0: /* select slot 3 chip */
this->IO_ADDR_R = (void __iomem *)((unsigned long)this->IO_ADDR_R | RTC_FROM4_NAND_ADDR_SLOT3);
this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W | RTC_FROM4_NAND_ADDR_SLOT3);
break;
case 1: /* select slot 4 chip */
this->IO_ADDR_R = (void __iomem *)((unsigned long)this->IO_ADDR_R | RTC_FROM4_NAND_ADDR_SLOT4);
this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W | RTC_FROM4_NAND_ADDR_SLOT4);
break;
}
}
/*
* rtc_from4_nand_device_ready - hardware specific ready/busy check
* @mtd: MTD device structure
*
* This board provides the Ready/Busy state in the status register
* of the FPGA. Bit zero indicates the RDY(1)/BSY(0) signal.
*
*/
static int rtc_from4_nand_device_ready(struct mtd_info *mtd)
{
unsigned short status;
status = *((volatile unsigned short *)(rtc_from4_fio_base + RTC_FROM4_FPGA_SR));
return (status & RTC_FROM4_DEVICE_READY);
}
#ifdef RTC_FROM4_HWECC
/*
* rtc_from4_enable_hwecc - hardware specific hardware ECC enable function
* @mtd: MTD device structure
* @mode: I/O mode; read or write
*
* enable hardware ECC for data read or write
*
*/
static void rtc_from4_enable_hwecc(struct mtd_info *mtd, int mode)
{
volatile unsigned short * rs_ecc_ctl = (volatile unsigned short *)(rtc_from4_fio_base + RTC_FROM4_RS_ECC_CTL);
unsigned short status;
switch (mode) {
case NAND_ECC_READ :
status = RTC_FROM4_RS_ECC_CTL_CLR
| RTC_FROM4_RS_ECC_CTL_FD_E;
*rs_ecc_ctl = status;
break;
case NAND_ECC_READSYN :
status = 0x00;
*rs_ecc_ctl = status;
break;
case NAND_ECC_WRITE :
status = RTC_FROM4_RS_ECC_CTL_CLR
| RTC_FROM4_RS_ECC_CTL_GEN
| RTC_FROM4_RS_ECC_CTL_FD_E;
*rs_ecc_ctl = status;
break;
default:
BUG();
break;
}
}
/*
* rtc_from4_calculate_ecc - hardware specific code to read ECC code
* @mtd: MTD device structure
* @dat: buffer containing the data to generate ECC codes
* @ecc_code ECC codes calculated
*
* The ECC code is calculated by the FPGA. All we have to do is read the values
* from the FPGA registers.
*
* Note: We read from the inverted registers, since data is inverted before
* the code is calculated. So all 0xff data (blank page) results in all 0xff rs code
*
*/
static void rtc_from4_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code)
{
volatile unsigned short * rs_eccn = (volatile unsigned short *)(rtc_from4_fio_base + RTC_FROM4_RS_ECCN);
unsigned short value;
int i;
for (i = 0; i < 8; i++) {
value = *rs_eccn;
ecc_code[i] = (unsigned char)value;
rs_eccn++;
}
ecc_code[7] |= 0x0f; /* set the last four bits (not used) */
}
/*
* rtc_from4_correct_data - hardware specific code to correct data using ECC code
* @mtd: MTD device structure
* @buf: buffer containing the data to generate ECC codes
* @ecc1 ECC codes read
* @ecc2 ECC codes calculated
*
* The FPGA tells us fast, if there's an error or not. If no, we go back happy
* else we read the ecc results from the fpga and call the rs library to decode
* and hopefully correct the error
*
* For now I use the code, which we read from the FLASH to use the RS lib,
* as the syndrom conversion has a unresolved issue.
*/
static int rtc_from4_correct_data(struct mtd_info *mtd, const u_char *buf, u_char *ecc1, u_char *ecc2)
{
int i, j, res;
unsigned short status;
uint16_t par[6], syn[6], tmp;
uint8_t ecc[8];
volatile unsigned short *rs_ecc;
status = *((volatile unsigned short *)(rtc_from4_fio_base + RTC_FROM4_RS_ECC_CHK));
if (!(status & RTC_FROM4_RS_ECC_CHK_ERROR)) {
return 0;
}
/* Read the syndrom pattern from the FPGA and correct the bitorder */
rs_ecc = (volatile unsigned short *)(rtc_from4_fio_base + RTC_FROM4_RS_ECC);
for (i = 0; i < 8; i++) {
ecc[i] = revbits[(*rs_ecc) & 0xFF];
rs_ecc++;
}
/* convert into 6 10bit syndrome fields */
par[5] = rs_decoder->index_of[(((uint16_t)ecc[0] >> 0) & 0x0ff) |
(((uint16_t)ecc[1] << 8) & 0x300)];
par[4] = rs_decoder->index_of[(((uint16_t)ecc[1] >> 2) & 0x03f) |
(((uint16_t)ecc[2] << 6) & 0x3c0)];
par[3] = rs_decoder->index_of[(((uint16_t)ecc[2] >> 4) & 0x00f) |
(((uint16_t)ecc[3] << 4) & 0x3f0)];
par[2] = rs_decoder->index_of[(((uint16_t)ecc[3] >> 6) & 0x003) |
(((uint16_t)ecc[4] << 2) & 0x3fc)];
par[1] = rs_decoder->index_of[(((uint16_t)ecc[5] >> 0) & 0x0ff) |
(((uint16_t)ecc[6] << 8) & 0x300)];
par[0] = (((uint16_t)ecc[6] >> 2) & 0x03f) | (((uint16_t)ecc[7] << 6) & 0x3c0);
/* Convert to computable syndrome */
for (i = 0; i < 6; i++) {
syn[i] = par[0];
for (j = 1; j < 6; j++)
if (par[j] != rs_decoder->nn)
syn[i] ^= rs_decoder->alpha_to[rs_modnn(rs_decoder, par[j] + i * j)];
/* Convert to index form */
syn[i] = rs_decoder->index_of[syn[i]];
}
/* Let the library code do its magic.*/
res = decode_rs8(rs_decoder, buf, par, 512, syn, 0, NULL, 0xff, NULL);
if (res > 0) {
DEBUG (MTD_DEBUG_LEVEL0, "rtc_from4_correct_data: "
"ECC corrected %d errors on read\n", res);
}
return res;
}
#endif
/*
* Main initialization routine
*/
int __init rtc_from4_init (void)
{
struct nand_chip *this;
unsigned short bcr1, bcr2, wcr2;
/* Allocate memory for MTD device structure and private data */
rtc_from4_mtd = kmalloc(sizeof(struct mtd_info) + sizeof (struct nand_chip),
GFP_KERNEL);
if (!rtc_from4_mtd) {
printk ("Unable to allocate Renesas NAND MTD device structure.\n");
return -ENOMEM;
}
/* Get pointer to private data */
this = (struct nand_chip *) (&rtc_from4_mtd[1]);
/* Initialize structures */
memset((char *) rtc_from4_mtd, 0, sizeof(struct mtd_info));
memset((char *) this, 0, sizeof(struct nand_chip));
/* Link the private data with the MTD structure */
rtc_from4_mtd->priv = this;
/* set area 5 as PCMCIA mode to clear the spec of tDH(Data hold time;9ns min) */
bcr1 = *SH77X9_BCR1 & ~0x0002;
bcr1 |= 0x0002;
*SH77X9_BCR1 = bcr1;
/* set */
bcr2 = *SH77X9_BCR2 & ~0x0c00;
bcr2 |= 0x0800;
*SH77X9_BCR2 = bcr2;
/* set area 5 wait states */
wcr2 = *SH77X9_WCR2 & ~0x1c00;
wcr2 |= 0x1c00;
*SH77X9_WCR2 = wcr2;
/* Set address of NAND IO lines */
this->IO_ADDR_R = rtc_from4_fio_base;
this->IO_ADDR_W = rtc_from4_fio_base;
/* Set address of hardware control function */
this->hwcontrol = rtc_from4_hwcontrol;
/* Set address of chip select function */
this->select_chip = rtc_from4_nand_select_chip;
/* command delay time (in us) */
this->chip_delay = 100;
/* return the status of the Ready/Busy line */
this->dev_ready = rtc_from4_nand_device_ready;
#ifdef RTC_FROM4_HWECC
printk(KERN_INFO "rtc_from4_init: using hardware ECC detection.\n");
this->eccmode = NAND_ECC_HW8_512;
this->options |= NAND_HWECC_SYNDROME;
/* set the nand_oobinfo to support FPGA H/W error detection */
this->autooob = &rtc_from4_nand_oobinfo;
this->enable_hwecc = rtc_from4_enable_hwecc;
this->calculate_ecc = rtc_from4_calculate_ecc;
this->correct_data = rtc_from4_correct_data;
#else
printk(KERN_INFO "rtc_from4_init: using software ECC detection.\n");
this->eccmode = NAND_ECC_SOFT;
#endif
/* set the bad block tables to support debugging */
this->bbt_td = &rtc_from4_bbt_main_descr;
this->bbt_md = &rtc_from4_bbt_mirror_descr;
/* Scan to find existence of the device */
if (nand_scan(rtc_from4_mtd, RTC_FROM4_MAX_CHIPS)) {
kfree(rtc_from4_mtd);
return -ENXIO;
}
/* Register the partitions */
add_mtd_partitions(rtc_from4_mtd, partition_info, NUM_PARTITIONS);
#ifdef RTC_FROM4_HWECC
/* We could create the decoder on demand, if memory is a concern.
* This way we have it handy, if an error happens
*
* Symbolsize is 10 (bits)
* Primitve polynomial is x^10+x^3+1
* first consecutive root is 0
* primitve element to generate roots = 1
* generator polinomial degree = 6
*/
rs_decoder = init_rs(10, 0x409, 0, 1, 6);
if (!rs_decoder) {
printk (KERN_ERR "Could not create a RS decoder\n");
nand_release(rtc_from4_mtd);
kfree(rtc_from4_mtd);
return -ENOMEM;
}
#endif
/* Return happy */
return 0;
}
module_init(rtc_from4_init);
/*
* Clean up routine
*/
#ifdef MODULE
static void __exit rtc_from4_cleanup (void)
{
/* Release resource, unregister partitions */
nand_release(rtc_from4_mtd);
/* Free the MTD device structure */
kfree (rtc_from4_mtd);
#ifdef RTC_FROM4_HWECC
/* Free the reed solomon resources */
if (rs_decoder) {
free_rs(rs_decoder);
}
#endif
}
module_exit(rtc_from4_cleanup);
#endif
MODULE_LICENSE("GPL");
MODULE_AUTHOR("d.marlin <dmarlin@redhat.com");
MODULE_DESCRIPTION("Board-specific glue layer for AG-AND flash on Renesas FROM_BOARD4");

704
drivers/mtd/nand/s3c2410.c Normal file
查看文件

@@ -0,0 +1,704 @@
/* linux/drivers/mtd/nand/s3c2410.c
*
* Copyright (c) 2004 Simtec Electronics
* Ben Dooks <ben@simtec.co.uk>
*
* Samsung S3C2410 NAND driver
*
* Changelog:
* 21-Sep-2004 BJD Initial version
* 23-Sep-2004 BJD Mulitple device support
* 28-Sep-2004 BJD Fixed ECC placement for Hardware mode
* 12-Oct-2004 BJD Fixed errors in use of platform data
*
* $Id: s3c2410.c,v 1.7 2005/01/05 18:05:14 dwmw2 Exp $
*
* 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <config/mtd/nand/s3c2410/hwecc.h>
#include <config/mtd/nand/s3c2410/debug.h>
#ifdef CONFIG_MTD_NAND_S3C2410_DEBUG
#define DEBUG
#endif
#include <linux/module.h>
#include <linux/types.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/ioport.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/nand_ecc.h>
#include <linux/mtd/partitions.h>
#include <asm/io.h>
#include <asm/mach-types.h>
#include <asm/hardware/clock.h>
#include <asm/arch/regs-nand.h>
#include <asm/arch/nand.h>
#define PFX "s3c2410-nand: "
#ifdef CONFIG_MTD_NAND_S3C2410_HWECC
static int hardware_ecc = 1;
#else
static int hardware_ecc = 0;
#endif
/* new oob placement block for use with hardware ecc generation
*/
static struct nand_oobinfo nand_hw_eccoob = {
.useecc = MTD_NANDECC_AUTOPLACE,
.eccbytes = 3,
.eccpos = {0, 1, 2 },
.oobfree = { {8, 8} }
};
/* controller and mtd information */
struct s3c2410_nand_info;
struct s3c2410_nand_mtd {
struct mtd_info mtd;
struct nand_chip chip;
struct s3c2410_nand_set *set;
struct s3c2410_nand_info *info;
int scan_res;
};
/* overview of the s3c2410 nand state */
struct s3c2410_nand_info {
/* mtd info */
struct nand_hw_control controller;
struct s3c2410_nand_mtd *mtds;
struct s3c2410_platform_nand *platform;
/* device info */
struct device *device;
struct resource *area;
struct clk *clk;
void *regs;
int mtd_count;
};
/* conversion functions */
static struct s3c2410_nand_mtd *s3c2410_nand_mtd_toours(struct mtd_info *mtd)
{
return container_of(mtd, struct s3c2410_nand_mtd, mtd);
}
static struct s3c2410_nand_info *s3c2410_nand_mtd_toinfo(struct mtd_info *mtd)
{
return s3c2410_nand_mtd_toours(mtd)->info;
}
static struct s3c2410_nand_info *to_nand_info(struct device *dev)
{
return dev_get_drvdata(dev);
}
static struct s3c2410_platform_nand *to_nand_plat(struct device *dev)
{
return dev->platform_data;
}
/* timing calculations */
#define NS_IN_KHZ 10000000
static int s3c2410_nand_calc_rate(int wanted, unsigned long clk, int max)
{
int result;
result = (wanted * NS_IN_KHZ) / clk;
result++;
pr_debug("result %d from %ld, %d\n", result, clk, wanted);
if (result > max) {
printk("%d ns is too big for current clock rate %ld\n",
wanted, clk);
return -1;
}
if (result < 1)
result = 1;
return result;
}
#define to_ns(ticks,clk) (((clk) * (ticks)) / NS_IN_KHZ)
/* controller setup */
static int s3c2410_nand_inithw(struct s3c2410_nand_info *info,
struct device *dev)
{
struct s3c2410_platform_nand *plat = to_nand_plat(dev);
unsigned int tacls, twrph0, twrph1;
unsigned long clkrate = clk_get_rate(info->clk);
unsigned long cfg;
/* calculate the timing information for the controller */
if (plat != NULL) {
tacls = s3c2410_nand_calc_rate(plat->tacls, clkrate, 8);
twrph0 = s3c2410_nand_calc_rate(plat->twrph0, clkrate, 8);
twrph1 = s3c2410_nand_calc_rate(plat->twrph1, clkrate, 8);
} else {
/* default timings */
tacls = 8;
twrph0 = 8;
twrph1 = 8;
}
if (tacls < 0 || twrph0 < 0 || twrph1 < 0) {
printk(KERN_ERR PFX "cannot get timings suitable for board\n");
return -EINVAL;
}
printk(KERN_INFO PFX "timing: Tacls %ldns, Twrph0 %ldns, Twrph1 %ldns\n",
to_ns(tacls, clkrate),
to_ns(twrph0, clkrate),
to_ns(twrph1, clkrate));
cfg = S3C2410_NFCONF_EN;
cfg |= S3C2410_NFCONF_TACLS(tacls-1);
cfg |= S3C2410_NFCONF_TWRPH0(twrph0-1);
cfg |= S3C2410_NFCONF_TWRPH1(twrph1-1);
pr_debug(PFX "NF_CONF is 0x%lx\n", cfg);
writel(cfg, info->regs + S3C2410_NFCONF);
return 0;
}
/* select chip */
static void s3c2410_nand_select_chip(struct mtd_info *mtd, int chip)
{
struct s3c2410_nand_info *info;
struct s3c2410_nand_mtd *nmtd;
struct nand_chip *this = mtd->priv;
unsigned long cur;
nmtd = this->priv;
info = nmtd->info;
cur = readl(info->regs + S3C2410_NFCONF);
if (chip == -1) {
cur |= S3C2410_NFCONF_nFCE;
} else {
if (chip > nmtd->set->nr_chips) {
printk(KERN_ERR PFX "chip %d out of range\n", chip);
return;
}
if (info->platform != NULL) {
if (info->platform->select_chip != NULL)
(info->platform->select_chip)(nmtd->set, chip);
}
cur &= ~S3C2410_NFCONF_nFCE;
}
writel(cur, info->regs + S3C2410_NFCONF);
}
/* command and control functions */
static void s3c2410_nand_hwcontrol(struct mtd_info *mtd, int cmd)
{
struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
unsigned long cur;
switch (cmd) {
case NAND_CTL_SETNCE:
cur = readl(info->regs + S3C2410_NFCONF);
cur &= ~S3C2410_NFCONF_nFCE;
writel(cur, info->regs + S3C2410_NFCONF);
break;
case NAND_CTL_CLRNCE:
cur = readl(info->regs + S3C2410_NFCONF);
cur |= S3C2410_NFCONF_nFCE;
writel(cur, info->regs + S3C2410_NFCONF);
break;
/* we don't need to implement these */
case NAND_CTL_SETCLE:
case NAND_CTL_CLRCLE:
case NAND_CTL_SETALE:
case NAND_CTL_CLRALE:
pr_debug(PFX "s3c2410_nand_hwcontrol(%d) unusedn", cmd);
break;
}
}
/* s3c2410_nand_command
*
* This function implements sending commands and the relevant address
* information to the chip, via the hardware controller. Since the
* S3C2410 generates the correct ALE/CLE signaling automatically, we
* do not need to use hwcontrol.
*/
static void s3c2410_nand_command (struct mtd_info *mtd, unsigned command,
int column, int page_addr)
{
register struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
register struct nand_chip *this = mtd->priv;
/*
* Write out the command to the device.
*/
if (command == NAND_CMD_SEQIN) {
int readcmd;
if (column >= mtd->oobblock) {
/* OOB area */
column -= mtd->oobblock;
readcmd = NAND_CMD_READOOB;
} else if (column < 256) {
/* First 256 bytes --> READ0 */
readcmd = NAND_CMD_READ0;
} else {
column -= 256;
readcmd = NAND_CMD_READ1;
}
writeb(readcmd, info->regs + S3C2410_NFCMD);
}
writeb(command, info->regs + S3C2410_NFCMD);
/* Set ALE and clear CLE to start address cycle */
if (column != -1 || page_addr != -1) {
/* Serially input address */
if (column != -1) {
/* Adjust columns for 16 bit buswidth */
if (this->options & NAND_BUSWIDTH_16)
column >>= 1;
writeb(column, info->regs + S3C2410_NFADDR);
}
if (page_addr != -1) {
writeb((unsigned char) (page_addr), info->regs + S3C2410_NFADDR);
writeb((unsigned char) (page_addr >> 8), info->regs + S3C2410_NFADDR);
/* One more address cycle for higher density devices */
if (this->chipsize & 0x0c000000)
writeb((unsigned char) ((page_addr >> 16) & 0x0f),
info->regs + S3C2410_NFADDR);
}
/* Latch in address */
}
/*
* program and erase have their own busy handlers
* status and sequential in needs 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:
if (this->dev_ready)
break;
udelay(this->chip_delay);
writeb(NAND_CMD_STATUS, info->regs + S3C2410_NFCMD);
while ( !(this->read_byte(mtd) & 0x40));
return;
/* This applies to read commands */
default:
/*
* If we don't have access to the busy pin, we apply the given
* command delay
*/
if (!this->dev_ready) {
udelay (this->chip_delay);
return;
}
}
/* Apply this short delay always to ensure that we do wait tWB in
* any case on any machine. */
ndelay (100);
/* wait until command is processed */
while (!this->dev_ready(mtd));
}
/* s3c2410_nand_devready()
*
* returns 0 if the nand is busy, 1 if it is ready
*/
static int s3c2410_nand_devready(struct mtd_info *mtd)
{
struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
return readb(info->regs + S3C2410_NFSTAT) & S3C2410_NFSTAT_BUSY;
}
/* ECC handling functions */
static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat,
u_char *read_ecc, u_char *calc_ecc)
{
pr_debug("s3c2410_nand_correct_data(%p,%p,%p,%p)\n",
mtd, dat, read_ecc, calc_ecc);
pr_debug("eccs: read %02x,%02x,%02x vs calc %02x,%02x,%02x\n",
read_ecc[0], read_ecc[1], read_ecc[2],
calc_ecc[0], calc_ecc[1], calc_ecc[2]);
if (read_ecc[0] == calc_ecc[0] &&
read_ecc[1] == calc_ecc[1] &&
read_ecc[2] == calc_ecc[2])
return 0;
/* we curently have no method for correcting the error */
return -1;
}
static void s3c2410_nand_enable_hwecc(struct mtd_info *mtd, int mode)
{
struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
unsigned long ctrl;
ctrl = readl(info->regs + S3C2410_NFCONF);
ctrl |= S3C2410_NFCONF_INITECC;
writel(ctrl, info->regs + S3C2410_NFCONF);
}
static int s3c2410_nand_calculate_ecc(struct mtd_info *mtd,
const u_char *dat, u_char *ecc_code)
{
struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
ecc_code[0] = readb(info->regs + S3C2410_NFECC + 0);
ecc_code[1] = readb(info->regs + S3C2410_NFECC + 1);
ecc_code[2] = readb(info->regs + S3C2410_NFECC + 2);
pr_debug("calculate_ecc: returning ecc %02x,%02x,%02x\n",
ecc_code[0], ecc_code[1], ecc_code[2]);
return 0;
}
/* over-ride the standard functions for a little more speed? */
static void s3c2410_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
{
struct nand_chip *this = mtd->priv;
readsb(this->IO_ADDR_R, buf, len);
}
static void s3c2410_nand_write_buf(struct mtd_info *mtd,
const u_char *buf, int len)
{
struct nand_chip *this = mtd->priv;
writesb(this->IO_ADDR_W, buf, len);
}
/* device management functions */
static int s3c2410_nand_remove(struct device *dev)
{
struct s3c2410_nand_info *info = to_nand_info(dev);
dev_set_drvdata(dev, NULL);
if (info == NULL)
return 0;
/* first thing we need to do is release all our mtds
* and their partitions, then go through freeing the
* resources used
*/
if (info->mtds != NULL) {
struct s3c2410_nand_mtd *ptr = info->mtds;
int mtdno;
for (mtdno = 0; mtdno < info->mtd_count; mtdno++, ptr++) {
pr_debug("releasing mtd %d (%p)\n", mtdno, ptr);
nand_release(&ptr->mtd);
}
kfree(info->mtds);
}
/* free the common resources */
if (info->clk != NULL && !IS_ERR(info->clk)) {
clk_disable(info->clk);
clk_unuse(info->clk);
clk_put(info->clk);
}
if (info->regs != NULL) {
iounmap(info->regs);
info->regs = NULL;
}
if (info->area != NULL) {
release_resource(info->area);
kfree(info->area);
info->area = NULL;
}
kfree(info);
return 0;
}
#ifdef CONFIG_MTD_PARTITIONS
static int s3c2410_nand_add_partition(struct s3c2410_nand_info *info,
struct s3c2410_nand_mtd *mtd,
struct s3c2410_nand_set *set)
{
if (set == NULL)
return add_mtd_device(&mtd->mtd);
if (set->nr_partitions > 0 && set->partitions != NULL) {
return add_mtd_partitions(&mtd->mtd,
set->partitions,
set->nr_partitions);
}
return add_mtd_device(&mtd->mtd);
}
#else
static int s3c2410_nand_add_partition(struct s3c2410_nand_info *info,
struct s3c2410_nand_mtd *mtd,
struct s3c2410_nand_set *set)
{
return add_mtd_device(&mtd->mtd);
}
#endif
/* s3c2410_nand_init_chip
*
* init a single instance of an chip
*/
static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
struct s3c2410_nand_mtd *nmtd,
struct s3c2410_nand_set *set)
{
struct nand_chip *chip = &nmtd->chip;
chip->IO_ADDR_R = (char *)info->regs + S3C2410_NFDATA;
chip->IO_ADDR_W = (char *)info->regs + S3C2410_NFDATA;
chip->hwcontrol = s3c2410_nand_hwcontrol;
chip->dev_ready = s3c2410_nand_devready;
chip->cmdfunc = s3c2410_nand_command;
chip->write_buf = s3c2410_nand_write_buf;
chip->read_buf = s3c2410_nand_read_buf;
chip->select_chip = s3c2410_nand_select_chip;
chip->chip_delay = 50;
chip->priv = nmtd;
chip->options = 0;
chip->controller = &info->controller;
nmtd->info = info;
nmtd->mtd.priv = chip;
nmtd->set = set;
if (hardware_ecc) {
chip->correct_data = s3c2410_nand_correct_data;
chip->enable_hwecc = s3c2410_nand_enable_hwecc;
chip->calculate_ecc = s3c2410_nand_calculate_ecc;
chip->eccmode = NAND_ECC_HW3_512;
chip->autooob = &nand_hw_eccoob;
} else {
chip->eccmode = NAND_ECC_SOFT;
}
}
/* s3c2410_nand_probe
*
* called by device layer when it finds a device matching
* one our driver can handled. This code checks to see if
* it can allocate all necessary resources then calls the
* nand layer to look for devices
*/
static int s3c2410_nand_probe(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct s3c2410_platform_nand *plat = to_nand_plat(dev);
struct s3c2410_nand_info *info;
struct s3c2410_nand_mtd *nmtd;
struct s3c2410_nand_set *sets;
struct resource *res;
int err = 0;
int size;
int nr_sets;
int setno;
pr_debug("s3c2410_nand_probe(%p)\n", dev);
info = kmalloc(sizeof(*info), GFP_KERNEL);
if (info == NULL) {
printk(KERN_ERR PFX "no memory for flash info\n");
err = -ENOMEM;
goto exit_error;
}
memzero(info, sizeof(*info));
dev_set_drvdata(dev, info);
spin_lock_init(&info->controller.lock);
/* get the clock source and enable it */
info->clk = clk_get(dev, "nand");
if (IS_ERR(info->clk)) {
printk(KERN_ERR PFX "failed to get clock");
err = -ENOENT;
goto exit_error;
}
clk_use(info->clk);
clk_enable(info->clk);
/* allocate and map the resource */
res = pdev->resource; /* assume that the flash has one resource */
size = res->end - res->start + 1;
info->area = request_mem_region(res->start, size, pdev->name);
if (info->area == NULL) {
printk(KERN_ERR PFX "cannot reserve register region\n");
err = -ENOENT;
goto exit_error;
}
info->device = dev;
info->platform = plat;
info->regs = ioremap(res->start, size);
if (info->regs == NULL) {
printk(KERN_ERR PFX "cannot reserve register region\n");
err = -EIO;
goto exit_error;
}
printk(KERN_INFO PFX "mapped registers at %p\n", info->regs);
/* initialise the hardware */
err = s3c2410_nand_inithw(info, dev);
if (err != 0)
goto exit_error;
sets = (plat != NULL) ? plat->sets : NULL;
nr_sets = (plat != NULL) ? plat->nr_sets : 1;
info->mtd_count = nr_sets;
/* allocate our information */
size = nr_sets * sizeof(*info->mtds);
info->mtds = kmalloc(size, GFP_KERNEL);
if (info->mtds == NULL) {
printk(KERN_ERR PFX "failed to allocate mtd storage\n");
err = -ENOMEM;
goto exit_error;
}
memzero(info->mtds, size);
/* initialise all possible chips */
nmtd = info->mtds;
for (setno = 0; setno < nr_sets; setno++, nmtd++) {
pr_debug("initialising set %d (%p, info %p)\n",
setno, nmtd, info);
s3c2410_nand_init_chip(info, nmtd, sets);
nmtd->scan_res = nand_scan(&nmtd->mtd,
(sets) ? sets->nr_chips : 1);
if (nmtd->scan_res == 0) {
s3c2410_nand_add_partition(info, nmtd, sets);
}
if (sets != NULL)
sets++;
}
pr_debug("initialised ok\n");
return 0;
exit_error:
s3c2410_nand_remove(dev);
if (err == 0)
err = -EINVAL;
return err;
}
static struct device_driver s3c2410_nand_driver = {
.name = "s3c2410-nand",
.bus = &platform_bus_type,
.probe = s3c2410_nand_probe,
.remove = s3c2410_nand_remove,
};
static int __init s3c2410_nand_init(void)
{
printk("S3C2410 NAND Driver, (c) 2004 Simtec Electronics\n");
return driver_register(&s3c2410_nand_driver);
}
static void __exit s3c2410_nand_exit(void)
{
driver_unregister(&s3c2410_nand_driver);
}
module_init(s3c2410_nand_init);
module_exit(s3c2410_nand_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
MODULE_DESCRIPTION("S3C2410 MTD NAND driver");

260
drivers/mtd/nand/sharpsl.c Executable file
查看文件

@@ -0,0 +1,260 @@
/*
* drivers/mtd/nand/sharpsl.c
*
* Copyright (C) 2004 Richard Purdie
*
* $Id: sharpsl.c,v 1.3 2005/01/03 14:53:50 rpurdie Exp $
*
* Based on Sharp's NAND driver sharp_sl.c
*
* 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 <linux/genhd.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/nand_ecc.h>
#include <linux/mtd/partitions.h>
#include <linux/interrupt.h>
#include <asm/io.h>
#include <asm/hardware.h>
#include <asm/mach-types.h>
static void __iomem *sharpsl_io_base;
static int sharpsl_phys_base = 0x0C000000;
/* register offset */
#define ECCLPLB sharpsl_io_base+0x00 /* line parity 7 - 0 bit */
#define ECCLPUB sharpsl_io_base+0x04 /* line parity 15 - 8 bit */
#define ECCCP sharpsl_io_base+0x08 /* column parity 5 - 0 bit */
#define ECCCNTR sharpsl_io_base+0x0C /* ECC byte counter */
#define ECCCLRR sharpsl_io_base+0x10 /* cleare ECC */
#define FLASHIO sharpsl_io_base+0x14 /* Flash I/O */
#define FLASHCTL sharpsl_io_base+0x18 /* Flash Control */
/* Flash control bit */
#define FLRYBY (1 << 5)
#define FLCE1 (1 << 4)
#define FLWP (1 << 3)
#define FLALE (1 << 2)
#define FLCLE (1 << 1)
#define FLCE0 (1 << 0)
/*
* MTD structure for SharpSL
*/
static struct mtd_info *sharpsl_mtd = NULL;
/*
* Define partitions for flash device
*/
#define DEFAULT_NUM_PARTITIONS 3
static int nr_partitions;
static struct mtd_partition sharpsl_nand_default_partition_info[] = {
{
.name = "System Area",
.offset = 0,
.size = 7 * 1024 * 1024,
},
{
.name = "Root Filesystem",
.offset = 7 * 1024 * 1024,
.size = 30 * 1024 * 1024,
},
{
.name = "Home Filesystem",
.offset = MTDPART_OFS_APPEND ,
.size = MTDPART_SIZ_FULL ,
},
};
/*
* hardware specific access to control-lines
*/
static void
sharpsl_nand_hwcontrol(struct mtd_info* mtd, int cmd)
{
switch (cmd) {
case NAND_CTL_SETCLE:
writeb(readb(FLASHCTL) | FLCLE, FLASHCTL);
break;
case NAND_CTL_CLRCLE:
writeb(readb(FLASHCTL) & ~FLCLE, FLASHCTL);
break;
case NAND_CTL_SETALE:
writeb(readb(FLASHCTL) | FLALE, FLASHCTL);
break;
case NAND_CTL_CLRALE:
writeb(readb(FLASHCTL) & ~FLALE, FLASHCTL);
break;
case NAND_CTL_SETNCE:
writeb(readb(FLASHCTL) & ~(FLCE0|FLCE1), FLASHCTL);
break;
case NAND_CTL_CLRNCE:
writeb(readb(FLASHCTL) | (FLCE0|FLCE1), FLASHCTL);
break;
}
}
static uint8_t scan_ff_pattern[] = { 0xff, 0xff };
static struct nand_bbt_descr sharpsl_bbt = {
.options = 0,
.offs = 4,
.len = 2,
.pattern = scan_ff_pattern
};
static int
sharpsl_nand_dev_ready(struct mtd_info* mtd)
{
return !((readb(FLASHCTL) & FLRYBY) == 0);
}
static void
sharpsl_nand_enable_hwecc(struct mtd_info* mtd, int mode)
{
writeb(0 ,ECCCLRR);
}
static int
sharpsl_nand_calculate_ecc(struct mtd_info* mtd, const u_char* dat,
u_char* ecc_code)
{
ecc_code[0] = ~readb(ECCLPUB);
ecc_code[1] = ~readb(ECCLPLB);
ecc_code[2] = (~readb(ECCCP) << 2) | 0x03;
return readb(ECCCNTR) != 0;
}
#ifdef CONFIG_MTD_PARTITIONS
const char *part_probes[] = { "cmdlinepart", NULL };
#endif
/*
* Main initialization routine
*/
int __init
sharpsl_nand_init(void)
{
struct nand_chip *this;
struct mtd_partition* sharpsl_partition_info;
int err = 0;
/* Allocate memory for MTD device structure and private data */
sharpsl_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip),
GFP_KERNEL);
if (!sharpsl_mtd) {
printk ("Unable to allocate SharpSL NAND MTD device structure.\n");
return -ENOMEM;
}
/* map physical adress */
sharpsl_io_base = ioremap(sharpsl_phys_base, 0x1000);
if(!sharpsl_io_base){
printk("ioremap to access Sharp SL NAND chip failed\n");
kfree(sharpsl_mtd);
return -EIO;
}
/* Get pointer to private data */
this = (struct nand_chip *) (&sharpsl_mtd[1]);
/* Initialize structures */
memset((char *) sharpsl_mtd, 0, sizeof(struct mtd_info));
memset((char *) this, 0, sizeof(struct nand_chip));
/* Link the private data with the MTD structure */
sharpsl_mtd->priv = this;
/*
* PXA initialize
*/
writeb(readb(FLASHCTL) | FLWP, FLASHCTL);
/* Set address of NAND IO lines */
this->IO_ADDR_R = FLASHIO;
this->IO_ADDR_W = FLASHIO;
/* Set address of hardware control function */
this->hwcontrol = sharpsl_nand_hwcontrol;
this->dev_ready = sharpsl_nand_dev_ready;
/* 15 us command delay time */
this->chip_delay = 15;
/* set eccmode using hardware ECC */
this->eccmode = NAND_ECC_HW3_256;
this->enable_hwecc = sharpsl_nand_enable_hwecc;
this->calculate_ecc = sharpsl_nand_calculate_ecc;
this->correct_data = nand_correct_data;
this->badblock_pattern = &sharpsl_bbt;
/* Scan to find existence of the device */
err=nand_scan(sharpsl_mtd,1);
if (err) {
iounmap(sharpsl_io_base);
kfree(sharpsl_mtd);
return err;
}
/* Register the partitions */
sharpsl_mtd->name = "sharpsl-nand";
nr_partitions = parse_mtd_partitions(sharpsl_mtd, part_probes,
&sharpsl_partition_info, 0);
if (nr_partitions <= 0) {
nr_partitions = DEFAULT_NUM_PARTITIONS;
sharpsl_partition_info = sharpsl_nand_default_partition_info;
if (machine_is_poodle()) {
sharpsl_partition_info[1].size=22 * 1024 * 1024;
} else if (machine_is_corgi() || machine_is_shepherd()) {
sharpsl_partition_info[1].size=25 * 1024 * 1024;
} else if (machine_is_husky()) {
sharpsl_partition_info[1].size=53 * 1024 * 1024;
}
}
if (machine_is_husky()) {
/* Need to use small eraseblock size for backward compatibility */
sharpsl_mtd->flags |= MTD_NO_VIRTBLOCKS;
}
add_mtd_partitions(sharpsl_mtd, sharpsl_partition_info, nr_partitions);
/* Return happy */
return 0;
}
module_init(sharpsl_nand_init);
/*
* Clean up routine
*/
#ifdef MODULE
static void __exit sharpsl_nand_cleanup(void)
{
struct nand_chip *this = (struct nand_chip *) &sharpsl_mtd[1];
/* Release resources, unregister device */
nand_release(sharpsl_mtd);
iounmap(sharpsl_io_base);
/* Free the MTD device structure */
kfree(sharpsl_mtd);
}
module_exit(sharpsl_nand_cleanup);
#endif
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Richard Purdie <rpurdie@rpsys.net>");
MODULE_DESCRIPTION("Device specific logic for NAND flash on Sharp SL-C7xx Series");

173
drivers/mtd/nand/spia.c Normal file
查看文件

@@ -0,0 +1,173 @@
/*
* drivers/mtd/nand/spia.c
*
* Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
*
*
* 10-29-2001 TG change to support hardwarespecific access
* to controllines (due to change in nand.c)
* page_cache added
*
* $Id: spia.c,v 1.24 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.
*
* Overview:
* This is a device driver for the NAND flash device found on the
* SPIA board which utilizes the Toshiba TC58V64AFT part. This is
* a 64Mibit (8MiB x 8 bits) NAND flash device.
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/partitions.h>
#include <asm/io.h>
/*
* MTD structure for SPIA board
*/
static struct mtd_info *spia_mtd = NULL;
/*
* Values specific to the SPIA board (used with EP7212 processor)
*/
#define SPIA_IO_BASE 0xd0000000 /* Start of EP7212 IO address space */
#define SPIA_FIO_BASE 0xf0000000 /* Address where flash is mapped */
#define SPIA_PEDR 0x0080 /*
* IO offset to Port E data register
* where the CLE, ALE and NCE pins
* are wired to.
*/
#define SPIA_PEDDR 0x00c0 /*
* IO offset to Port E data direction
* register so we can control the IO
* lines.
*/
/*
* Module stuff
*/
static int spia_io_base = SPIA_IO_BASE;
static int spia_fio_base = SPIA_FIO_BASE;
static int spia_pedr = SPIA_PEDR;
static int spia_peddr = SPIA_PEDDR;
module_param(spia_io_base, int, 0);
module_param(spia_fio_base, int, 0);
module_param(spia_pedr, int, 0);
module_param(spia_peddr, int, 0);
/*
* Define partitions for flash device
*/
const static struct mtd_partition partition_info[] = {
{
.name = "SPIA flash partition 1",
.offset = 0,
.size = 2*1024*1024
},
{
.name = "SPIA flash partition 2",
.offset = 2*1024*1024,
.size = 6*1024*1024
}
};
#define NUM_PARTITIONS 2
/*
* hardware specific access to control-lines
*/
static void spia_hwcontrol(struct mtd_info *mtd, int cmd){
switch(cmd){
case NAND_CTL_SETCLE: (*(volatile unsigned char *) (spia_io_base + spia_pedr)) |= 0x01; break;
case NAND_CTL_CLRCLE: (*(volatile unsigned char *) (spia_io_base + spia_pedr)) &= ~0x01; break;
case NAND_CTL_SETALE: (*(volatile unsigned char *) (spia_io_base + spia_pedr)) |= 0x02; break;
case NAND_CTL_CLRALE: (*(volatile unsigned char *) (spia_io_base + spia_pedr)) &= ~0x02; break;
case NAND_CTL_SETNCE: (*(volatile unsigned char *) (spia_io_base + spia_pedr)) &= ~0x04; break;
case NAND_CTL_CLRNCE: (*(volatile unsigned char *) (spia_io_base + spia_pedr)) |= 0x04; break;
}
}
/*
* Main initialization routine
*/
int __init spia_init (void)
{
struct nand_chip *this;
/* Allocate memory for MTD device structure and private data */
spia_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip),
GFP_KERNEL);
if (!spia_mtd) {
printk ("Unable to allocate SPIA NAND MTD device structure.\n");
return -ENOMEM;
}
/* Get pointer to private data */
this = (struct nand_chip *) (&spia_mtd[1]);
/* Initialize structures */
memset((char *) spia_mtd, 0, sizeof(struct mtd_info));
memset((char *) this, 0, sizeof(struct nand_chip));
/* Link the private data with the MTD structure */
spia_mtd->priv = this;
/*
* Set GPIO Port E control register so that the pins are configured
* to be outputs for controlling the NAND flash.
*/
(*(volatile unsigned char *) (spia_io_base + spia_peddr)) = 0x07;
/* Set address of NAND IO lines */
this->IO_ADDR_R = (void __iomem *) spia_fio_base;
this->IO_ADDR_W = (void __iomem *) spia_fio_base;
/* Set address of hardware control function */
this->hwcontrol = spia_hwcontrol;
/* 15 us command delay time */
this->chip_delay = 15;
/* Scan to find existence of the device */
if (nand_scan (spia_mtd, 1)) {
kfree (spia_mtd);
return -ENXIO;
}
/* Register the partitions */
add_mtd_partitions(spia_mtd, partition_info, NUM_PARTITIONS);
/* Return happy */
return 0;
}
module_init(spia_init);
/*
* Clean up routine
*/
#ifdef MODULE
static void __exit spia_cleanup (void)
{
/* Release resources, unregister device */
nand_release (spia_mtd);
/* Free the MTD device structure */
kfree (spia_mtd);
}
module_exit(spia_cleanup);
#endif
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Steven J. Hill <sjhill@realitydiluted.com");
MODULE_DESCRIPTION("Board-specific glue layer for NAND flash on SPIA board");

205
drivers/mtd/nand/toto.c Normal file
查看文件

@@ -0,0 +1,205 @@
/*
* drivers/mtd/nand/toto.c
*
* Copyright (c) 2003 Texas Instruments
*
* Derived from drivers/mtd/autcpu12.c
*
* Copyright (c) 2002 Thomas Gleixner <tgxl@linutronix.de>
*
* 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.
*
* Overview:
* This is a device driver for the NAND flash device found on the
* TI fido board. It supports 32MiB and 64MiB cards
*
* $Id: toto.c,v 1.4 2004/10/05 13:50:20 gleixner Exp $
*/
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/partitions.h>
#include <asm/io.h>
#include <asm/arch/hardware.h>
#include <asm/sizes.h>
#include <asm/arch/toto.h>
#include <asm/arch-omap1510/hardware.h>
#include <asm/arch/gpio.h>
/*
* MTD structure for TOTO board
*/
static struct mtd_info *toto_mtd = NULL;
static unsigned long toto_io_base = OMAP_FLASH_1_BASE;
#define CONFIG_NAND_WORKAROUND 1
#define NAND_NCE 0x4000
#define NAND_CLE 0x1000
#define NAND_ALE 0x0002
#define NAND_MASK (NAND_CLE | NAND_ALE | NAND_NCE)
#define T_NAND_CTL_CLRALE(iob) gpiosetout(NAND_ALE, 0)
#define T_NAND_CTL_SETALE(iob) gpiosetout(NAND_ALE, NAND_ALE)
#ifdef CONFIG_NAND_WORKAROUND /* "some" dev boards busted, blue wired to rts2 :( */
#define T_NAND_CTL_CLRCLE(iob) gpiosetout(NAND_CLE, 0); rts2setout(2, 2)
#define T_NAND_CTL_SETCLE(iob) gpiosetout(NAND_CLE, NAND_CLE); rts2setout(2, 0)
#else
#define T_NAND_CTL_CLRCLE(iob) gpiosetout(NAND_CLE, 0)
#define T_NAND_CTL_SETCLE(iob) gpiosetout(NAND_CLE, NAND_CLE)
#endif
#define T_NAND_CTL_SETNCE(iob) gpiosetout(NAND_NCE, 0)
#define T_NAND_CTL_CLRNCE(iob) gpiosetout(NAND_NCE, NAND_NCE)
/*
* Define partitions for flash devices
*/
static struct mtd_partition partition_info64M[] = {
{ .name = "toto kernel partition 1",
.offset = 0,
.size = 2 * SZ_1M },
{ .name = "toto file sys partition 2",
.offset = 2 * SZ_1M,
.size = 14 * SZ_1M },
{ .name = "toto user partition 3",
.offset = 16 * SZ_1M,
.size = 16 * SZ_1M },
{ .name = "toto devboard extra partition 4",
.offset = 32 * SZ_1M,
.size = 32 * SZ_1M },
};
static struct mtd_partition partition_info32M[] = {
{ .name = "toto kernel partition 1",
.offset = 0,
.size = 2 * SZ_1M },
{ .name = "toto file sys partition 2",
.offset = 2 * SZ_1M,
.size = 14 * SZ_1M },
{ .name = "toto user partition 3",
.offset = 16 * SZ_1M,
.size = 16 * SZ_1M },
};
#define NUM_PARTITIONS32M 3
#define NUM_PARTITIONS64M 4
/*
* hardware specific access to control-lines
*/
static void toto_hwcontrol(struct mtd_info *mtd, int cmd)
{
udelay(1); /* hopefully enough time for tc make proceding write to clear */
switch(cmd){
case NAND_CTL_SETCLE: T_NAND_CTL_SETCLE(cmd); break;
case NAND_CTL_CLRCLE: T_NAND_CTL_CLRCLE(cmd); break;
case NAND_CTL_SETALE: T_NAND_CTL_SETALE(cmd); break;
case NAND_CTL_CLRALE: T_NAND_CTL_CLRALE(cmd); break;
case NAND_CTL_SETNCE: T_NAND_CTL_SETNCE(cmd); break;
case NAND_CTL_CLRNCE: T_NAND_CTL_CLRNCE(cmd); break;
}
udelay(1); /* allow time to ensure gpio state to over take memory write */
}
/*
* Main initialization routine
*/
int __init toto_init (void)
{
struct nand_chip *this;
int err = 0;
/* Allocate memory for MTD device structure and private data */
toto_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip),
GFP_KERNEL);
if (!toto_mtd) {
printk (KERN_WARNING "Unable to allocate toto NAND MTD device structure.\n");
err = -ENOMEM;
goto out;
}
/* Get pointer to private data */
this = (struct nand_chip *) (&toto_mtd[1]);
/* Initialize structures */
memset((char *) toto_mtd, 0, sizeof(struct mtd_info));
memset((char *) this, 0, sizeof(struct nand_chip));
/* Link the private data with the MTD structure */
toto_mtd->priv = this;
/* Set address of NAND IO lines */
this->IO_ADDR_R = toto_io_base;
this->IO_ADDR_W = toto_io_base;
this->hwcontrol = toto_hwcontrol;
this->dev_ready = NULL;
/* 25 us command delay time */
this->chip_delay = 30;
this->eccmode = NAND_ECC_SOFT;
/* Scan to find existance of the device */
if (nand_scan (toto_mtd, 1)) {
err = -ENXIO;
goto out_mtd;
}
/* Register the partitions */
switch(toto_mtd->size){
case SZ_64M: add_mtd_partitions(toto_mtd, partition_info64M, NUM_PARTITIONS64M); break;
case SZ_32M: add_mtd_partitions(toto_mtd, partition_info32M, NUM_PARTITIONS32M); break;
default: {
printk (KERN_WARNING "Unsupported Nand device\n");
err = -ENXIO;
goto out_buf;
}
}
gpioreserve(NAND_MASK); /* claim our gpios */
archflashwp(0,0); /* open up flash for writing */
goto out;
out_buf:
kfree (this->data_buf);
out_mtd:
kfree (toto_mtd);
out:
return err;
}
module_init(toto_init);
/*
* Clean up routine
*/
static void __exit toto_cleanup (void)
{
/* Release resources, unregister device */
nand_release (toto_mtd);
/* Free the MTD device structure */
kfree (toto_mtd);
/* stop flash writes */
archflashwp(0,1);
/* release gpios to system */
gpiorelease(NAND_MASK);
}
module_exit(toto_cleanup);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Richard Woodruff <r-woodruff2@ti.com>");
MODULE_DESCRIPTION("Glue layer for NAND flash on toto board");

查看文件

@@ -0,0 +1,416 @@
/*
* drivers/mtd/tx4925ndfmc.c
*
* Overview:
* This is a device driver for the NAND flash device found on the
* Toshiba RBTX4925 reference board, which is a SmartMediaCard. It supports
* 16MiB, 32MiB and 64MiB cards.
*
* Author: MontaVista Software, Inc. source@mvista.com
*
* Derived from drivers/mtd/autcpu12.c
* Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de)
*
* $Id: tx4925ndfmc.c,v 1.5 2004/10/05 13:50:20 gleixner Exp $
*
* Copyright (C) 2001 Toshiba Corporation
*
* 2003 (c) MontaVista Software, Inc. This file is licensed under
* the terms of the GNU General Public License version 2. This program
* is licensed "as is" without any warranty of any kind, whether express
* or implied.
*
*/
#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 <linux/delay.h>
#include <asm/io.h>
#include <asm/tx4925/tx4925_nand.h>
extern struct nand_oobinfo jffs2_oobinfo;
/*
* MTD structure for RBTX4925 board
*/
static struct mtd_info *tx4925ndfmc_mtd = NULL;
/*
* Define partitions for flash devices
*/
static struct mtd_partition partition_info16k[] = {
{ .name = "RBTX4925 flash partition 1",
.offset = 0,
.size = 8 * 0x00100000 },
{ .name = "RBTX4925 flash partition 2",
.offset = 8 * 0x00100000,
.size = 8 * 0x00100000 },
};
static struct mtd_partition partition_info32k[] = {
{ .name = "RBTX4925 flash partition 1",
.offset = 0,
.size = 8 * 0x00100000 },
{ .name = "RBTX4925 flash partition 2",
.offset = 8 * 0x00100000,
.size = 24 * 0x00100000 },
};
static struct mtd_partition partition_info64k[] = {
{ .name = "User FS",
.offset = 0,
.size = 16 * 0x00100000 },
{ .name = "RBTX4925 flash partition 2",
.offset = 16 * 0x00100000,
.size = 48 * 0x00100000},
};
static struct mtd_partition partition_info128k[] = {
{ .name = "Skip bad section",
.offset = 0,
.size = 16 * 0x00100000 },
{ .name = "User FS",
.offset = 16 * 0x00100000,
.size = 112 * 0x00100000 },
};
#define NUM_PARTITIONS16K 2
#define NUM_PARTITIONS32K 2
#define NUM_PARTITIONS64K 2
#define NUM_PARTITIONS128K 2
/*
* hardware specific access to control-lines
*/
static void tx4925ndfmc_hwcontrol(struct mtd_info *mtd, int cmd)
{
switch(cmd){
case NAND_CTL_SETCLE:
tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_CLE;
break;
case NAND_CTL_CLRCLE:
tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_CLE;
break;
case NAND_CTL_SETALE:
tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_ALE;
break;
case NAND_CTL_CLRALE:
tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_ALE;
break;
case NAND_CTL_SETNCE:
tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_CE;
break;
case NAND_CTL_CLRNCE:
tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_CE;
break;
case NAND_CTL_SETWP:
tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_WE;
break;
case NAND_CTL_CLRWP:
tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_WE;
break;
}
}
/*
* read device ready pin
*/
static int tx4925ndfmc_device_ready(struct mtd_info *mtd)
{
int ready;
ready = (tx4925_ndfmcptr->sr & TX4925_NDSFR_BUSY) ? 0 : 1;
return ready;
}
void tx4925ndfmc_enable_hwecc(struct mtd_info *mtd, int mode)
{
/* reset first */
tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_ECC_CNTL_MASK;
tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_ECC_CNTL_MASK;
tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_ECC_CNTL_ENAB;
}
static void tx4925ndfmc_disable_ecc(void)
{
tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_ECC_CNTL_MASK;
}
static void tx4925ndfmc_enable_read_ecc(void)
{
tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_ECC_CNTL_MASK;
tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_ECC_CNTL_READ;
}
void tx4925ndfmc_readecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code){
int i;
u_char *ecc = ecc_code;
tx4925ndfmc_enable_read_ecc();
for (i = 0;i < 6;i++,ecc++)
*ecc = tx4925_read_nfmc(&(tx4925_ndfmcptr->dtr));
tx4925ndfmc_disable_ecc();
}
void tx4925ndfmc_device_setup(void)
{
*(unsigned char *)0xbb005000 &= ~0x08;
/* reset NDFMC */
tx4925_ndfmcptr->rstr |= TX4925_NDFRSTR_RST;
while (tx4925_ndfmcptr->rstr & TX4925_NDFRSTR_RST);
/* setup BusSeparete, Hold Time, Strobe Pulse Width */
tx4925_ndfmcptr->mcr = TX4925_BSPRT ? TX4925_NDFMCR_BSPRT : 0;
tx4925_ndfmcptr->spr = TX4925_HOLD << 4 | TX4925_SPW;
}
static u_char tx4925ndfmc_nand_read_byte(struct mtd_info *mtd)
{
struct nand_chip *this = mtd->priv;
return tx4925_read_nfmc(this->IO_ADDR_R);
}
static void tx4925ndfmc_nand_write_byte(struct mtd_info *mtd, u_char byte)
{
struct nand_chip *this = mtd->priv;
tx4925_write_nfmc(byte, this->IO_ADDR_W);
}
static void tx4925ndfmc_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
{
int i;
struct nand_chip *this = mtd->priv;
for (i=0; i<len; i++)
tx4925_write_nfmc(buf[i], this->IO_ADDR_W);
}
static void tx4925ndfmc_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
{
int i;
struct nand_chip *this = mtd->priv;
for (i=0; i<len; i++)
buf[i] = tx4925_read_nfmc(this->IO_ADDR_R);
}
static int tx4925ndfmc_nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
{
int i;
struct nand_chip *this = mtd->priv;
for (i=0; i<len; i++)
if (buf[i] != tx4925_read_nfmc(this->IO_ADDR_R))
return -EFAULT;
return 0;
}
/*
* Send command to NAND device
*/
static void tx4925ndfmc_nand_command (struct mtd_info *mtd, unsigned command, int column, int page_addr)
{
register struct nand_chip *this = mtd->priv;
/* Begin command latch cycle */
this->hwcontrol(mtd, NAND_CTL_SETCLE);
/*
* Write out the command to the device.
*/
if (command == NAND_CMD_SEQIN) {
int readcmd;
if (column >= mtd->oobblock) {
/* OOB area */
column -= mtd->oobblock;
readcmd = NAND_CMD_READOOB;
} else if (column < 256) {
/* First 256 bytes --> READ0 */
readcmd = NAND_CMD_READ0;
} else {
column -= 256;
readcmd = NAND_CMD_READ1;
}
this->write_byte(mtd, readcmd);
}
this->write_byte(mtd, command);
/* Set ALE and clear CLE to start address cycle */
this->hwcontrol(mtd, NAND_CTL_CLRCLE);
if (column != -1 || page_addr != -1) {
this->hwcontrol(mtd, NAND_CTL_SETALE);
/* Serially input address */
if (column != -1)
this->write_byte(mtd, column);
if (page_addr != -1) {
this->write_byte(mtd, (unsigned char) (page_addr & 0xff));
this->write_byte(mtd, (unsigned char) ((page_addr >> 8) & 0xff));
/* One more address cycle for higher density devices */
if (mtd->size & 0x0c000000)
this->write_byte(mtd, (unsigned char) ((page_addr >> 16) & 0x0f));
}
/* Latch in address */
this->hwcontrol(mtd, NAND_CTL_CLRALE);
}
/*
* program and erase have their own busy handlers
* status and sequential in needs no delay
*/
switch (command) {
case NAND_CMD_PAGEPROG:
/* Turn off WE */
this->hwcontrol (mtd, NAND_CTL_CLRWP);
return;
case NAND_CMD_SEQIN:
/* Turn on WE */
this->hwcontrol (mtd, NAND_CTL_SETWP);
return;
case NAND_CMD_ERASE1:
case NAND_CMD_ERASE2:
case NAND_CMD_STATUS:
return;
case NAND_CMD_RESET:
if (this->dev_ready)
break;
this->hwcontrol(mtd, NAND_CTL_SETCLE);
this->write_byte(mtd, NAND_CMD_STATUS);
this->hwcontrol(mtd, NAND_CTL_CLRCLE);
while ( !(this->read_byte(mtd) & 0x40));
return;
/* This applies to read commands */
default:
/*
* If we don't have access to the busy pin, we apply the given
* command delay
*/
if (!this->dev_ready) {
udelay (this->chip_delay);
return;
}
}
/* wait until command is processed */
while (!this->dev_ready(mtd));
}
#ifdef CONFIG_MTD_CMDLINE_PARTS
extern int parse_cmdline_partitions(struct mtd_info *master, struct mtd_partitio
n **pparts, char *);
#endif
/*
* Main initialization routine
*/
extern int nand_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc);
int __init tx4925ndfmc_init (void)
{
struct nand_chip *this;
int err = 0;
/* Allocate memory for MTD device structure and private data */
tx4925ndfmc_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip),
GFP_KERNEL);
if (!tx4925ndfmc_mtd) {
printk ("Unable to allocate RBTX4925 NAND MTD device structure.\n");
err = -ENOMEM;
goto out;
}
tx4925ndfmc_device_setup();
/* io is indirect via a register so don't need to ioremap address */
/* Get pointer to private data */
this = (struct nand_chip *) (&tx4925ndfmc_mtd[1]);
/* Initialize structures */
memset((char *) tx4925ndfmc_mtd, 0, sizeof(struct mtd_info));
memset((char *) this, 0, sizeof(struct nand_chip));
/* Link the private data with the MTD structure */
tx4925ndfmc_mtd->priv = this;
/* Set address of NAND IO lines */
this->IO_ADDR_R = (void __iomem *)&(tx4925_ndfmcptr->dtr);
this->IO_ADDR_W = (void __iomem *)&(tx4925_ndfmcptr->dtr);
this->hwcontrol = tx4925ndfmc_hwcontrol;
this->enable_hwecc = tx4925ndfmc_enable_hwecc;
this->calculate_ecc = tx4925ndfmc_readecc;
this->correct_data = nand_correct_data;
this->eccmode = NAND_ECC_HW6_512;
this->dev_ready = tx4925ndfmc_device_ready;
/* 20 us command delay time */
this->chip_delay = 20;
this->read_byte = tx4925ndfmc_nand_read_byte;
this->write_byte = tx4925ndfmc_nand_write_byte;
this->cmdfunc = tx4925ndfmc_nand_command;
this->write_buf = tx4925ndfmc_nand_write_buf;
this->read_buf = tx4925ndfmc_nand_read_buf;
this->verify_buf = tx4925ndfmc_nand_verify_buf;
/* Scan to find existance of the device */
if (nand_scan (tx4925ndfmc_mtd, 1)) {
err = -ENXIO;
goto out_ior;
}
/* Register the partitions */
#ifdef CONFIG_MTD_CMDLINE_PARTS
{
int mtd_parts_nb = 0;
struct mtd_partition *mtd_parts = 0;
mtd_parts_nb = parse_cmdline_partitions(tx4925ndfmc_mtd, &mtd_parts, "tx4925ndfmc");
if (mtd_parts_nb > 0)
add_mtd_partitions(tx4925ndfmc_mtd, mtd_parts, mtd_parts_nb);
else
add_mtd_device(tx4925ndfmc_mtd);
}
#else /* ifdef CONFIG_MTD_CMDLINE_PARTS */
switch(tx4925ndfmc_mtd->size){
case 0x01000000: add_mtd_partitions(tx4925ndfmc_mtd, partition_info16k, NUM_PARTITIONS16K); break;
case 0x02000000: add_mtd_partitions(tx4925ndfmc_mtd, partition_info32k, NUM_PARTITIONS32K); break;
case 0x04000000: add_mtd_partitions(tx4925ndfmc_mtd, partition_info64k, NUM_PARTITIONS64K); break;
case 0x08000000: add_mtd_partitions(tx4925ndfmc_mtd, partition_info128k, NUM_PARTITIONS128K); break;
default: {
printk ("Unsupported SmartMedia device\n");
err = -ENXIO;
goto out_ior;
}
}
#endif /* ifdef CONFIG_MTD_CMDLINE_PARTS */
goto out;
out_ior:
out:
return err;
}
module_init(tx4925ndfmc_init);
/*
* Clean up routine
*/
#ifdef MODULE
static void __exit tx4925ndfmc_cleanup (void)
{
/* Release resources, unregister device */
nand_release (tx4925ndfmc_mtd);
/* Free the MTD device structure */
kfree (tx4925ndfmc_mtd);
}
module_exit(tx4925ndfmc_cleanup);
#endif
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Alice Hennessy <ahennessy@mvista.com>");
MODULE_DESCRIPTION("Glue layer for SmartMediaCard on Toshiba RBTX4925");

查看文件

@@ -0,0 +1,406 @@
/*
* drivers/mtd/nand/tx4938ndfmc.c
*
* Overview:
* This is a device driver for the NAND flash device connected to
* TX4938 internal NAND Memory Controller.
* TX4938 NDFMC is almost same as TX4925 NDFMC, but register size are 64 bit.
*
* Author: source@mvista.com
*
* Based on spia.c by Steven J. Hill
*
* $Id: tx4938ndfmc.c,v 1.4 2004/10/05 13:50:20 gleixner Exp $
*
* Copyright (C) 2000-2001 Toshiba Corporation
*
* 2003 (c) MontaVista Software, Inc. This file is licensed under the
* terms of the GNU General Public License version 2. This program is
* licensed "as is" without any warranty of any kind, whether express
* or implied.
*/
#include <linux/config.h>
#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/nand_ecc.h>
#include <linux/mtd/partitions.h>
#include <asm/io.h>
#include <asm/bootinfo.h>
#include <linux/delay.h>
#include <asm/tx4938/rbtx4938.h>
extern struct nand_oobinfo jffs2_oobinfo;
/*
* MTD structure for TX4938 NDFMC
*/
static struct mtd_info *tx4938ndfmc_mtd;
/*
* Define partitions for flash device
*/
#define flush_wb() (void)tx4938_ndfmcptr->mcr;
#define NUM_PARTITIONS 3
#define NUMBER_OF_CIS_BLOCKS 24
#define SIZE_OF_BLOCK 0x00004000
#define NUMBER_OF_BLOCK_PER_ZONE 1024
#define SIZE_OF_ZONE (NUMBER_OF_BLOCK_PER_ZONE * SIZE_OF_BLOCK)
#ifndef CONFIG_MTD_CMDLINE_PARTS
/*
* You can use the following sample of MTD partitions
* on the NAND Flash Memory 32MB or more.
*
* The following figure shows the image of the sample partition on
* the 32MB NAND Flash Memory.
*
* Block No.
* 0 +-----------------------------+ ------
* | CIS | ^
* 24 +-----------------------------+ |
* | kernel image | | Zone 0
* | | |
* +-----------------------------+ |
* 1023 | unused area | v
* +-----------------------------+ ------
* 1024 | JFFS2 | ^
* | | |
* | | | Zone 1
* | | |
* | | |
* | | v
* 2047 +-----------------------------+ ------
*
*/
static struct mtd_partition partition_info[NUM_PARTITIONS] = {
{
.name = "RBTX4938 CIS Area",
.offset = 0,
.size = (NUMBER_OF_CIS_BLOCKS * SIZE_OF_BLOCK),
.mask_flags = MTD_WRITEABLE /* This partition is NOT writable */
},
{
.name = "RBTX4938 kernel image",
.offset = MTDPART_OFS_APPEND,
.size = 8 * 0x00100000, /* 8MB (Depends on size of kernel image) */
.mask_flags = MTD_WRITEABLE /* This partition is NOT writable */
},
{
.name = "Root FS (JFFS2)",
.offset = (0 + SIZE_OF_ZONE), /* start address of next zone */
.size = MTDPART_SIZ_FULL
},
};
#endif
static void tx4938ndfmc_hwcontrol(struct mtd_info *mtd, int cmd)
{
switch (cmd) {
case NAND_CTL_SETCLE:
tx4938_ndfmcptr->mcr |= TX4938_NDFMCR_CLE;
break;
case NAND_CTL_CLRCLE:
tx4938_ndfmcptr->mcr &= ~TX4938_NDFMCR_CLE;
break;
case NAND_CTL_SETALE:
tx4938_ndfmcptr->mcr |= TX4938_NDFMCR_ALE;
break;
case NAND_CTL_CLRALE:
tx4938_ndfmcptr->mcr &= ~TX4938_NDFMCR_ALE;
break;
/* TX4938_NDFMCR_CE bit is 0:high 1:low */
case NAND_CTL_SETNCE:
tx4938_ndfmcptr->mcr |= TX4938_NDFMCR_CE;
break;
case NAND_CTL_CLRNCE:
tx4938_ndfmcptr->mcr &= ~TX4938_NDFMCR_CE;
break;
case NAND_CTL_SETWP:
tx4938_ndfmcptr->mcr |= TX4938_NDFMCR_WE;
break;
case NAND_CTL_CLRWP:
tx4938_ndfmcptr->mcr &= ~TX4938_NDFMCR_WE;
break;
}
}
static int tx4938ndfmc_dev_ready(struct mtd_info *mtd)
{
flush_wb();
return !(tx4938_ndfmcptr->sr & TX4938_NDFSR_BUSY);
}
static void tx4938ndfmc_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code)
{
u32 mcr = tx4938_ndfmcptr->mcr;
mcr &= ~TX4938_NDFMCR_ECC_ALL;
tx4938_ndfmcptr->mcr = mcr | TX4938_NDFMCR_ECC_OFF;
tx4938_ndfmcptr->mcr = mcr | TX4938_NDFMCR_ECC_READ;
ecc_code[1] = tx4938_ndfmcptr->dtr;
ecc_code[0] = tx4938_ndfmcptr->dtr;
ecc_code[2] = tx4938_ndfmcptr->dtr;
tx4938_ndfmcptr->mcr = mcr | TX4938_NDFMCR_ECC_OFF;
}
static void tx4938ndfmc_enable_hwecc(struct mtd_info *mtd, int mode)
{
u32 mcr = tx4938_ndfmcptr->mcr;
mcr &= ~TX4938_NDFMCR_ECC_ALL;
tx4938_ndfmcptr->mcr = mcr | TX4938_NDFMCR_ECC_RESET;
tx4938_ndfmcptr->mcr = mcr | TX4938_NDFMCR_ECC_OFF;
tx4938_ndfmcptr->mcr = mcr | TX4938_NDFMCR_ECC_ON;
}
static u_char tx4938ndfmc_nand_read_byte(struct mtd_info *mtd)
{
struct nand_chip *this = mtd->priv;
return tx4938_read_nfmc(this->IO_ADDR_R);
}
static void tx4938ndfmc_nand_write_byte(struct mtd_info *mtd, u_char byte)
{
struct nand_chip *this = mtd->priv;
tx4938_write_nfmc(byte, this->IO_ADDR_W);
}
static void tx4938ndfmc_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
{
int i;
struct nand_chip *this = mtd->priv;
for (i=0; i<len; i++)
tx4938_write_nfmc(buf[i], this->IO_ADDR_W);
}
static void tx4938ndfmc_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
{
int i;
struct nand_chip *this = mtd->priv;
for (i=0; i<len; i++)
buf[i] = tx4938_read_nfmc(this->IO_ADDR_R);
}
static int tx4938ndfmc_nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
{
int i;
struct nand_chip *this = mtd->priv;
for (i=0; i<len; i++)
if (buf[i] != tx4938_read_nfmc(this->IO_ADDR_R))
return -EFAULT;
return 0;
}
/*
* Send command to NAND device
*/
static void tx4938ndfmc_nand_command (struct mtd_info *mtd, unsigned command, int column, int page_addr)
{
register struct nand_chip *this = mtd->priv;
/* Begin command latch cycle */
this->hwcontrol(mtd, NAND_CTL_SETCLE);
/*
* Write out the command to the device.
*/
if (command == NAND_CMD_SEQIN) {
int readcmd;
if (column >= mtd->oobblock) {
/* OOB area */
column -= mtd->oobblock;
readcmd = NAND_CMD_READOOB;
} else if (column < 256) {
/* First 256 bytes --> READ0 */
readcmd = NAND_CMD_READ0;
} else {
column -= 256;
readcmd = NAND_CMD_READ1;
}
this->write_byte(mtd, readcmd);
}
this->write_byte(mtd, command);
/* Set ALE and clear CLE to start address cycle */
this->hwcontrol(mtd, NAND_CTL_CLRCLE);
if (column != -1 || page_addr != -1) {
this->hwcontrol(mtd, NAND_CTL_SETALE);
/* Serially input address */
if (column != -1)
this->write_byte(mtd, column);
if (page_addr != -1) {
this->write_byte(mtd, (unsigned char) (page_addr & 0xff));
this->write_byte(mtd, (unsigned char) ((page_addr >> 8) & 0xff));
/* One more address cycle for higher density devices */
if (mtd->size & 0x0c000000)
this->write_byte(mtd, (unsigned char) ((page_addr >> 16) & 0x0f));
}
/* Latch in address */
this->hwcontrol(mtd, NAND_CTL_CLRALE);
}
/*
* program and erase have their own busy handlers
* status and sequential in needs no delay
*/
switch (command) {
case NAND_CMD_PAGEPROG:
/* Turn off WE */
this->hwcontrol (mtd, NAND_CTL_CLRWP);
return;
case NAND_CMD_SEQIN:
/* Turn on WE */
this->hwcontrol (mtd, NAND_CTL_SETWP);
return;
case NAND_CMD_ERASE1:
case NAND_CMD_ERASE2:
case NAND_CMD_STATUS:
return;
case NAND_CMD_RESET:
if (this->dev_ready)
break;
this->hwcontrol(mtd, NAND_CTL_SETCLE);
this->write_byte(mtd, NAND_CMD_STATUS);
this->hwcontrol(mtd, NAND_CTL_CLRCLE);
while ( !(this->read_byte(mtd) & 0x40));
return;
/* This applies to read commands */
default:
/*
* If we don't have access to the busy pin, we apply the given
* command delay
*/
if (!this->dev_ready) {
udelay (this->chip_delay);
return;
}
}
/* wait until command is processed */
while (!this->dev_ready(mtd));
}
#ifdef CONFIG_MTD_CMDLINE_PARTS
extern int parse_cmdline_partitions(struct mtd_info *master, struct mtd_partition **pparts, char *);
#endif
/*
* Main initialization routine
*/
int __init tx4938ndfmc_init (void)
{
struct nand_chip *this;
int bsprt = 0, hold = 0xf, spw = 0xf;
int protected = 0;
if ((*rbtx4938_piosel_ptr & 0x0c) != 0x08) {
printk("TX4938 NDFMC: disabled by IOC PIOSEL\n");
return -ENODEV;
}
bsprt = 1;
hold = 2;
spw = 9 - 1; /* 8 GBUSCLK = 80ns (@ GBUSCLK 100MHz) */
if ((tx4938_ccfgptr->pcfg &
(TX4938_PCFG_ATA_SEL|TX4938_PCFG_ISA_SEL|TX4938_PCFG_NDF_SEL))
!= TX4938_PCFG_NDF_SEL) {
printk("TX4938 NDFMC: disabled by PCFG.\n");
return -ENODEV;
}
/* reset NDFMC */
tx4938_ndfmcptr->rstr |= TX4938_NDFRSTR_RST;
while (tx4938_ndfmcptr->rstr & TX4938_NDFRSTR_RST)
;
/* setup BusSeparete, Hold Time, Strobe Pulse Width */
tx4938_ndfmcptr->mcr = bsprt ? TX4938_NDFMCR_BSPRT : 0;
tx4938_ndfmcptr->spr = hold << 4 | spw;
/* Allocate memory for MTD device structure and private data */
tx4938ndfmc_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip),
GFP_KERNEL);
if (!tx4938ndfmc_mtd) {
printk ("Unable to allocate TX4938 NDFMC MTD device structure.\n");
return -ENOMEM;
}
/* Get pointer to private data */
this = (struct nand_chip *) (&tx4938ndfmc_mtd[1]);
/* Initialize structures */
memset((char *) tx4938ndfmc_mtd, 0, sizeof(struct mtd_info));
memset((char *) this, 0, sizeof(struct nand_chip));
/* Link the private data with the MTD structure */
tx4938ndfmc_mtd->priv = this;
/* Set address of NAND IO lines */
this->IO_ADDR_R = (unsigned long)&tx4938_ndfmcptr->dtr;
this->IO_ADDR_W = (unsigned long)&tx4938_ndfmcptr->dtr;
this->hwcontrol = tx4938ndfmc_hwcontrol;
this->dev_ready = tx4938ndfmc_dev_ready;
this->calculate_ecc = tx4938ndfmc_calculate_ecc;
this->correct_data = nand_correct_data;
this->enable_hwecc = tx4938ndfmc_enable_hwecc;
this->eccmode = NAND_ECC_HW3_256;
this->chip_delay = 100;
this->read_byte = tx4938ndfmc_nand_read_byte;
this->write_byte = tx4938ndfmc_nand_write_byte;
this->cmdfunc = tx4938ndfmc_nand_command;
this->write_buf = tx4938ndfmc_nand_write_buf;
this->read_buf = tx4938ndfmc_nand_read_buf;
this->verify_buf = tx4938ndfmc_nand_verify_buf;
/* Scan to find existance of the device */
if (nand_scan (tx4938ndfmc_mtd, 1)) {
kfree (tx4938ndfmc_mtd);
return -ENXIO;
}
if (protected) {
printk(KERN_INFO "TX4938 NDFMC: write protected.\n");
tx4938ndfmc_mtd->flags &= ~(MTD_WRITEABLE | MTD_ERASEABLE);
}
#ifdef CONFIG_MTD_CMDLINE_PARTS
{
int mtd_parts_nb = 0;
struct mtd_partition *mtd_parts = 0;
mtd_parts_nb = parse_cmdline_partitions(tx4938ndfmc_mtd, &mtd_parts, "tx4938ndfmc");
if (mtd_parts_nb > 0)
add_mtd_partitions(tx4938ndfmc_mtd, mtd_parts, mtd_parts_nb);
else
add_mtd_device(tx4938ndfmc_mtd);
}
#else
add_mtd_partitions(tx4938ndfmc_mtd, partition_info, NUM_PARTITIONS );
#endif
return 0;
}
module_init(tx4938ndfmc_init);
/*
* Clean up routine
*/
static void __exit tx4938ndfmc_cleanup (void)
{
/* Release resources, unregister device */
nand_release (tx4938ndfmc_mtd);
/* Free the MTD device structure */
kfree (tx4938ndfmc_mtd);
}
module_exit(tx4938ndfmc_cleanup);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Alice Hennessy <ahennessy@mvista.com>");
MODULE_DESCRIPTION("Board-specific glue layer for NAND flash on TX4938 NDFMC");