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:
207
drivers/mtd/nand/Kconfig
Normal file
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
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
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
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");
|
1782
drivers/mtd/nand/diskonchip.c
Normal file
1782
drivers/mtd/nand/diskonchip.c
Normal file
文件差異過大導致無法顯示
Load Diff
218
drivers/mtd/nand/edb7312.c
Normal file
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
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
2563
drivers/mtd/nand/nand_base.c
Normal file
文件差異過大導致無法顯示
Load Diff
1056
drivers/mtd/nand/nand_bbt.c
Normal file
1056
drivers/mtd/nand/nand_bbt.c
Normal file
文件差異過大導致無法顯示
Load Diff
250
drivers/mtd/nand/nand_ecc.c
Normal file
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
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
1613
drivers/mtd/nand/nandsim.c
Normal file
文件差異過大導致無法顯示
Load Diff
420
drivers/mtd/nand/ppchameleonevb.c
Normal file
420
drivers/mtd/nand/ppchameleonevb.c
Normal file
@@ -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");
|
559
drivers/mtd/nand/rtc_from4.c
Normal file
559
drivers/mtd/nand/rtc_from4.c
Normal file
@@ -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
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
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
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
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");
|
416
drivers/mtd/nand/tx4925ndfmc.c
Normal file
416
drivers/mtd/nand/tx4925ndfmc.c
Normal file
@@ -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");
|
406
drivers/mtd/nand/tx4938ndfmc.c
Normal file
406
drivers/mtd/nand/tx4938ndfmc.c
Normal file
@@ -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");
|
Reference in New Issue
Block a user