Commit ce9ef9d9 authored by Thomas Gleixner's avatar Thomas Gleixner Committed by David Woodhouse

MTD: NAND flash driver updates.

 - Use new RS library for ECC
 - Add support for new NAND flash chips
 - New board support:
   - iPAQ H1910
   - Renesas AG-AND devel board
   - Simtec S3C210 
 - Support for shared controllers on multiple chips.
Signed-Off-By: default avatarThomas Gleixner <tglx@linutronix.de>
Signed-Off-By: default avatarDavid Woodhouse <dwmw2@infradead.org>
parent 91c73041
# drivers/mtd/nand/Kconfig
# $Id: Kconfig,v 1.17 2004/08/10 14:24:07 dwmw2 Exp $
# $Id: Kconfig,v 1.22 2004/10/05 22:11:46 gleixner Exp $
menu "NAND Flash Device Drivers"
depends on MTD!=n
......@@ -36,6 +36,12 @@ config MTD_NAND_EDB7312
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
......@@ -74,15 +80,54 @@ config MTD_NAND_AU1550
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.
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,
......
#
# linux/drivers/nand/Makefile
#
# $Id: Makefile.common,v 1.9 2004/07/12 16:07:31 dwmw2 Exp $
# $Id: Makefile.common,v 1.13 2004/09/28 22:04:23 bjd Exp $
obj-$(CONFIG_MTD_NAND) += nand.o nand_ecc.o
obj-$(CONFIG_MTD_NAND_IDS) += nand_ids.o
......@@ -14,6 +14,9 @@ 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
nand-objs = nand_base.o nand_bbt.o
......@@ -3,7 +3,7 @@
*
* Copyright (C) 2004 Embedded Edge, LLC
*
* $Id: au1550nd.c,v 1.5 2004/05/17 07:19:35 ppopov Exp $
* $Id: au1550nd.c,v 1.9 2004/10/20 05:58:30 ppopov 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
......@@ -18,6 +18,17 @@
#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>
......@@ -25,18 +36,16 @@
#ifdef CONFIG_MIPS_DB1550
#include <asm/db1x00.h>
#endif
#endif
/*
* MTD structure for NAND controller
*/
static struct mtd_info *au1550_mtd = NULL;
static volatile u32 p_nand;
static int nand_width = 1; /* default, only x8 supported for now */
static void __iomem *p_nand;
static int nand_width = 1; /* default x8*/
/* Internal buffers. Page buffer and oob buffer for one block*/
static u_char data_buf[512 + 16];
static u_char oob_buf[16 * 32];
#define NAND_CS 1
/*
* Define partitions for flash device
......@@ -70,183 +79,262 @@ const static struct mtd_partition partition_info[] = {
#endif
};
static inline void write_cmd_reg(u8 cmd)
/**
* 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)
{
if (nand_width)
*((volatile u8 *)(p_nand + MEM_STNAND_CMD)) = cmd;
else
*((volatile u16 *)(p_nand + MEM_STNAND_CMD)) = cmd;
struct nand_chip *this = mtd->priv;
u_char ret = readb(this->IO_ADDR_R);
au_sync();
return ret;
}
static inline void write_addr_reg(u8 addr)
/**
* 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)
{
if (nand_width)
*((volatile u8 *)(p_nand + MEM_STNAND_ADDR)) = addr;
else
*((volatile u16 *)(p_nand + MEM_STNAND_ADDR)) = addr;
struct nand_chip *this = mtd->priv;
writeb(byte, this->IO_ADDR_W);
au_sync();
}
static inline void write_data_reg(u8 data)
/**
* 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)
{
if (nand_width)
*((volatile u8 *)(p_nand + MEM_STNAND_DATA)) = data;
else
*((volatile u16 *)(p_nand + MEM_STNAND_DATA)) = data;
struct nand_chip *this = mtd->priv;
u_char ret = (u_char) cpu_to_le16(readw(this->IO_ADDR_R));
au_sync();
return ret;
}
static inline u32 read_data_reg(void)
/**
* 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)
{
u32 data;
if (nand_width) {
data = *((volatile u8 *)(p_nand + MEM_STNAND_DATA));
au_sync();
}
else {
data = *((volatile u16 *)(p_nand + MEM_STNAND_DATA));
au_sync();
}
return data;
struct nand_chip *this = mtd->priv;
writew(le16_to_cpu((u16) byte), this->IO_ADDR_W);
au_sync();
}
void au1550_hwcontrol(struct mtd_info *mtd, int cmd)
/**
* 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;
}
int au1550_device_ready(struct mtd_info *mtd)
/**
* 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)
{
int ready;
ready = (au_readl(MEM_STSTAT) & 0x1) ? 1 : 0;
return ready;
struct nand_chip *this = mtd->priv;
writew(word, this->IO_ADDR_W);
au_sync();
}
static u_char au1550_nand_read_byte(struct mtd_info *mtd)
/**
* 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)
{
u_char ret;
ret = read_data_reg();
return ret;
}
int i;
struct nand_chip *this = mtd->priv;
static void au1550_nand_write_byte(struct mtd_info *mtd, u_char byte)
{
write_data_reg((u8)byte);
for (i=0; i<len; i++) {
writeb(buf[i], this->IO_ADDR_W);
au_sync();
}
}
static void
au1550_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
/**
* 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++)
write_data_reg(buf[i]);
for (i=0; i<len; i++) {
buf[i] = readb(this->IO_ADDR_R);
au_sync();
}
}
static void
au1550_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
/**
* 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++)
buf[i] = (u_char)read_data_reg();
for (i=0; i<len; i++) {
if (buf[i] != readb(this->IO_ADDR_R))
return -EFAULT;
au_sync();
}
return 0;
}
static int
au1550_nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
/**
* 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();
}
}
for (i=0; i<len; i++)
if (buf[i] != (u_char)read_data_reg())
return -EFAULT;
/**
* 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;
return 0;
for (i=0; i<len; i++) {
p[i] = readw(this->IO_ADDR_R);
au_sync();
}
}
static void au1550_nand_select_chip(struct mtd_info *mtd, int chip)
/**
* 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)
{
switch(chip) {
case -1:
/* deassert chip enable */
au_writel(au_readl(MEM_STNDCTL) & ~0x20 , MEM_STNDCTL);
break;
case 0:
/* assert (force assert) chip enable */
au_writel(au_readl(MEM_STNDCTL) | 0x20 , MEM_STNDCTL);
break;
int i;
struct nand_chip *this = mtd->priv;
u16 *p = (u16 *) buf;
len >>= 1;
default:
BUG();
for (i=0; i<len; i++) {
if (p[i] != readw(this->IO_ADDR_R))
return -EFAULT;
au_sync();
}
return 0;
}
static void au1550_nand_command (struct mtd_info *mtd, unsigned command,
int column, int page_addr)
static void au1550_hwcontrol(struct mtd_info *mtd, int cmd)
{
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;
}
write_cmd_reg(readcmd);
}
write_cmd_reg(command);
if (column != -1 || page_addr != -1) {
/* Serially input address */
if (column != -1)
write_addr_reg(column);
if (page_addr != -1) {
write_addr_reg((unsigned char) (page_addr & 0xff));
write_addr_reg(((page_addr >> 8) & 0xff));
/* One more address cycle for higher density devices */
if (mtd->size & 0x0c000000)
write_addr_reg((unsigned char) ((page_addr >> 16) & 0x0f));
}
}
switch (command) {
case NAND_CMD_PAGEPROG:
case NAND_CMD_ERASE1:
case NAND_CMD_ERASE2:
case NAND_CMD_SEQIN:
case NAND_CMD_STATUS:
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_CMD_RESET:
if (this->dev_ready)
break;
udelay(this->chip_delay);
write_cmd_reg(NAND_CMD_STATUS);
while ( !(read_data_reg() & 0x40));
return;
/* This applies to read commands */
default:
udelay (this->chip_delay);
case NAND_CTL_SETNCE:
/* assert (force assert) chip enable */
au_writel(au_readl(MEM_STNDCTL) | (1<<(4+NAND_CS)) , MEM_STNDCTL);
break;
case NAND_CTL_CLRNCE:
/* deassert chip enable */
au_writel(au_readl(MEM_STNDCTL) & ~(1<<(4+NAND_CS)), MEM_STNDCTL);
break;
}
this->IO_ADDR_R = this->IO_ADDR_W;
/* wait until command is processed */
while (!this->dev_ready(mtd));
/* 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
......@@ -255,7 +343,7 @@ int __init au1550_init (void)
{
struct nand_chip *this;
u16 boot_swapboot = 0; /* default value */
u32 mem_time;
int retval;
/* Allocate memory for MTD device structure and private data */
au1550_mtd = kmalloc (sizeof(struct mtd_info) +
......@@ -275,9 +363,10 @@ int __init au1550_init (void)
/* Link the private data with the MTD structure */
au1550_mtd->priv = this;
/* MEM_STNDCTL: disable ints, disable nand boot */
/* disable interrupts */
au_writel(au_readl(MEM_STNDCTL) & ~(1<<8), MEM_STNDCTL);
/* disable NAND boot */
au_writel(au_readl(MEM_STNDCTL) & ~(1<<0), MEM_STNDCTL);
......@@ -295,7 +384,6 @@ int __init au1550_init (void)
case 0xD:
/* x16 NAND Flash */
nand_width = 0;
printk("Pb1550 NAND: 16-bit NAND not supported by MTD\n");
break;
case 1:
case 9:
......@@ -307,62 +395,62 @@ int __init au1550_init (void)
break;
default:
printk("Pb1550 NAND: bad boot:swap\n");
kfree(au1550_mtd);
return 1;
retval = -EINVAL;
goto outmem;
}
#endif
/* Configure RCE1 - should be done by YAMON */
au_writel(0x5 | (nand_width << 22), MEM_STCFG1);
au_writel(NAND_TIMING, MEM_STTIME1);
mem_time = au_readl(MEM_STTIME1);
au_writel(0x5 | (nand_width << 22), 0xB4001010); /* MEM_STCFG1 */
au_writel(NAND_TIMING, 0xB4001014); /* MEM_STTIME1 */
au_sync();
/* setup and enable chip select */
/* 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();
#endif
#ifdef CONFIG_MIPS_DB1550
/* Configure RCE1 - should be done by YAMON */
au_writel(0x00400005, MEM_STCFG1);
au_writel(0x00007774, MEM_STTIME1);
au_writel(0x12000FFF, MEM_STADDR1);
#endif
p_nand = (volatile struct nand_regs *)ioremap(NAND_PHYS_ADDR, 0x1000);
p_nand = (void __iomem *)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->cmdfunc = au1550_nand_command;
this->select_chip = au1550_nand_select_chip;
this->write_byte = au1550_nand_write_byte;
this->read_byte = au1550_nand_read_byte;
this->write_buf = au1550_nand_write_buf;
this->read_buf = au1550_nand_read_buf;
this->verify_buf = au1550_nand_verify_buf;
this->eccmode = NAND_ECC_SOFT;
/* Set internal data buffer */
this->data_buf = data_buf;
this->oob_buf = oob_buf;
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)) {
kfree (au1550_mtd);
return -ENXIO;
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);
......@@ -375,16 +463,14 @@ static void __exit au1550_cleanup (void)
{
struct nand_chip *this = (struct nand_chip *) &au1550_mtd[1];
iounmap ((void *)p_nand);
/* Unregister partitions */
del_mtd_partitions(au1550_mtd);
/* Unregister the device */
del_mtd_device (au1550_mtd);
/* 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
......
......@@ -6,7 +6,7 @@
* Derived from drivers/mtd/spia.c
* Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
*
* $Id: autcpu12.c,v 1.20 2004/07/20 02:44:26 dwmw2 Exp $
* $Id: autcpu12.c,v 1.21 2004/09/16 23:27:14 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
......@@ -48,7 +48,7 @@ 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 int autcpu12_fio_base;
static void __iomem * autcpu12_fio_base;
#ifdef MODULE
MODULE_PARM(autcpu12_fio_pbase, "i");
......@@ -150,7 +150,7 @@ int __init autcpu12_init (void)
}
/* map physical adress */
autcpu12_fio_base=(unsigned long)ioremap(autcpu12_fio_pbase,SZ_1K);
autcpu12_fio_base=(void __iomem *)ioremap(autcpu12_fio_pbase,SZ_1K);
if(!autcpu12_fio_base){
printk("Ioremap autcpu12 SmartMedia Card failed\n");
err = -EIO;
......
......@@ -8,16 +8,22 @@
* Author: David Woodhouse <dwmw2@infradead.org>
* Additional Diskonchip 2000 and Millennium support by Dan Brown <dan_brown@ieee.org>
* Diskonchip Millennium Plus support by Kalev Lember <kalev@smartlink.ee>
*
*
* Error correction code lifted from the old docecc code
* Author: Fabrice Bellard (fabrice.bellard@netgem.com)
* Copyright (C) 2000 Netgem S.A.
* converted to the generic Reed-Solomon library by Thomas Gleixner <tglx@linutronix.de>
*
* Interface to generic NAND code for M-Systems DiskOnChip devices
*
* $Id: diskonchip.c,v 1.34 2004/08/09 19:41:12 dbrown Exp $
* $Id: diskonchip.c,v 1.38 2004/10/05 22:11:46 gleixner Exp $
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/rslib.h>
#include <asm/io.h>
#include <linux/mtd/mtd.h>
......@@ -62,7 +68,7 @@ static unsigned long __initdata doc_locations[] = {
static struct mtd_info *doclist = NULL;
struct doc_priv {
unsigned long virtadr;
void __iomem *virtadr;
unsigned long physadr;
u_char ChipID;
u_char CDSNControl;
......@@ -104,8 +110,10 @@ MODULE_PARM(try_dword, "i");
static int no_ecc_failures=0;
MODULE_PARM(no_ecc_failures, "i");
#ifdef CONFIG_MTD_PARTITIONS
static int no_autopart=0;
MODULE_PARM(no_autopart, "i");
#endif
#ifdef MTD_NAND_DISKONCHIP_BBTWRITE
static int inftl_bbt_write=1;
......@@ -118,6 +126,112 @@ static unsigned long doc_config_location = CONFIG_MTD_DISKONCHIP_PROBE_ADDRESS;
MODULE_PARM(doc_config_location, "l");
MODULE_PARM_DESC(doc_config_location, "Physical memory address at which to probe for DiskOnChip");
/* Sector size for HW ECC */
#define SECTOR_SIZE 512
/* The sector bytes are packed into NB_DATA 10 bit words */
#define NB_DATA (((SECTOR_SIZE + 1) * 8 + 6) / 10)
/* Number of roots */
#define NROOTS 4
/* First consective root */
#define FCR 510
/* Number of symbols */
#define NN 1023
/* the Reed Solomon control structure */
static struct rs_control *rs_decoder;
/*
* The HW decoder in the DoC ASIC's provides us a error syndrome,
* which we must convert to a standard syndrom usable by the generic
* Reed-Solomon library code.
*
* Fabrice Bellard figured this out in the old docecc code. I added
* some comments, improved a minor bit and converted it to make use
* of the generic Reed-Solomon libary. tglx
*/
static int doc_ecc_decode (struct rs_control *rs, uint8_t *data, uint8_t *ecc)
{
int i, j, nerr, errpos[8];
uint8_t parity;
uint16_t ds[4], s[5], tmp, errval[8], syn[4];
/* Convert the ecc bytes into words */
ds[0] = ((ecc[4] & 0xff) >> 0) | ((ecc[5] & 0x03) << 8);
ds[1] = ((ecc[5] & 0xfc) >> 2) | ((ecc[2] & 0x0f) << 6);
ds[2] = ((ecc[2] & 0xf0) >> 4) | ((ecc[3] & 0x3f) << 4);
ds[3] = ((ecc[3] & 0xc0) >> 6) | ((ecc[0] & 0xff) << 2);
parity = ecc[1];
/* Initialize the syndrom buffer */
for (i = 0; i < NROOTS; i++)
s[i] = ds[0];
/*
* Evaluate
* s[i] = ds[3]x^3 + ds[2]x^2 + ds[1]x^1 + ds[0]
* where x = alpha^(FCR + i)
*/
for(j = 1; j < NROOTS; j++) {
if(ds[j] == 0)
continue;
tmp = rs->index_of[ds[j]];
for(i = 0; i < NROOTS; i++)
s[i] ^= rs->alpha_to[rs_modnn(rs, tmp + (FCR + i) * j)];
}
/* Calc s[i] = s[i] / alpha^(v + i) */
for (i = 0; i < NROOTS; i++) {
if (syn[i])
syn[i] = rs_modnn(rs, rs->index_of[s[i]] + (NN - FCR - i));
}
/* Call the decoder library */
nerr = decode_rs16(rs, NULL, NULL, 1019, syn, 0, errpos, 0, errval);
/* Incorrectable errors ? */
if (nerr < 0)
return nerr;
/*
* Correct the errors. The bitpositions are a bit of magic,
* but they are given by the design of the de/encoder circuit
* in the DoC ASIC's.
*/
for(i = 0;i < nerr; i++) {
int index, bitpos, pos = 1015 - errpos[i];
uint8_t val;
if (pos >= NB_DATA && pos < 1019)
continue;
if (pos < NB_DATA) {
/* extract bit position (MSB first) */
pos = 10 * (NB_DATA - 1 - pos) - 6;
/* now correct the following 10 bits. At most two bytes
can be modified since pos is even */
index = (pos >> 3) ^ 1;
bitpos = pos & 7;
if ((index >= 0 && index < SECTOR_SIZE) ||
index == (SECTOR_SIZE + 1)) {
val = (uint8_t) (errval[i] >> (2 + bitpos));
parity ^= val;
if (index < SECTOR_SIZE)
data[index] ^= val;
}
index = ((pos >> 3) + 1) ^ 1;
bitpos = (bitpos + 10) & 7;
if (bitpos == 0)
bitpos = 8;
if ((index >= 0 && index < SECTOR_SIZE) ||
index == (SECTOR_SIZE + 1)) {
val = (uint8_t)(errval[i] << (8 - bitpos));
parity ^= val;
if (index < SECTOR_SIZE)
data[index] ^= val;
}
}
}
/* If the parity is wrong, no rescue possible */
return parity ? -1 : nerr;
}
static void DoC_Delay(struct doc_priv *doc, unsigned short cycles)
{
volatile char dummy;
......@@ -139,7 +253,7 @@ static void DoC_Delay(struct doc_priv *doc, unsigned short cycles)
/* DOC_WaitReady: Wait for RDY line to be asserted by the flash chip */
static int _DoC_WaitReady(struct doc_priv *doc)
{
unsigned long docptr = doc->virtadr;
void __iomem *docptr = doc->virtadr;
unsigned long timeo = jiffies + (HZ * 10);
if(debug) printk("_DoC_WaitReady...\n");
......@@ -169,7 +283,7 @@ static int _DoC_WaitReady(struct doc_priv *doc)
static inline int DoC_WaitReady(struct doc_priv *doc)
{
unsigned long docptr = doc->virtadr;
void __iomem *docptr = doc->virtadr;
int ret = 0;
if (DoC_is_MillenniumPlus(doc)) {
......@@ -195,7 +309,7 @@ static void doc2000_write_byte(struct mtd_info *mtd, u_char datum)
{
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = (void *)this->priv;
unsigned long docptr = doc->virtadr;
void __iomem *docptr = doc->virtadr;
if(debug)printk("write_byte %02x\n", datum);
WriteDOC(datum, docptr, CDSNSlowIO);
......@@ -206,7 +320,7 @@ static u_char doc2000_read_byte(struct mtd_info *mtd)
{
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = (void *)this->priv;
unsigned long docptr = doc->virtadr;
void __iomem *docptr = doc->virtadr;
u_char ret;
ReadDOC(docptr, CDSNSlowIO);
......@@ -221,7 +335,7 @@ static void doc2000_writebuf(struct mtd_info *mtd,
{
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = (void *)this->priv;
unsigned long docptr = doc->virtadr;
void __iomem *docptr = doc->virtadr;
int i;
if (debug)printk("writebuf of %d bytes: ", len);
for (i=0; i < len; i++) {
......@@ -237,7 +351,7 @@ static void doc2000_readbuf(struct mtd_info *mtd,
{
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = (void *)this->priv;
unsigned long docptr = doc->virtadr;
void __iomem *docptr = doc->virtadr;
int i;
if (debug)printk("readbuf of %d bytes: ", len);
......@@ -252,7 +366,7 @@ static void doc2000_readbuf_dword(struct mtd_info *mtd,
{
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = (void *)this->priv;
unsigned long docptr = doc->virtadr;
void __iomem *docptr = doc->virtadr;
int i;
if (debug) printk("readbuf_dword of %d bytes: ", len);
......@@ -273,7 +387,7 @@ static int doc2000_verifybuf(struct mtd_info *mtd,
{
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = (void *)this->priv;
unsigned long docptr = doc->virtadr;
void __iomem *docptr = doc->virtadr;
int i;
for (i=0; i < len; i++)
......@@ -305,7 +419,7 @@ static uint16_t __init doc200x_ident_chip(struct mtd_info *mtd, int nr)
uint32_t dword;
uint8_t byte[4];
} ident;
unsigned long docptr = doc->virtadr;
void __iomem *docptr = doc->virtadr;
doc200x_hwcontrol(mtd, NAND_CTL_SETCLE);
doc2000_write_byte(mtd, NAND_CMD_READID);
......@@ -364,7 +478,7 @@ static void doc2001_write_byte(struct mtd_info *mtd, u_char datum)
{
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = (void *)this->priv;
unsigned long docptr = doc->virtadr;
void __iomem *docptr = doc->virtadr;
WriteDOC(datum, docptr, CDSNSlowIO);
WriteDOC(datum, docptr, Mil_CDSN_IO);
......@@ -375,7 +489,7 @@ static u_char doc2001_read_byte(struct mtd_info *mtd)
{
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = (void *)this->priv;
unsigned long docptr = doc->virtadr;
void __iomem *docptr = doc->virtadr;
//ReadDOC(docptr, CDSNSlowIO);
/* 11.4.5 -- delay twice to allow extended length cycle */
......@@ -390,7 +504,7 @@ static void doc2001_writebuf(struct mtd_info *mtd,
{
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = (void *)this->priv;
unsigned long docptr = doc->virtadr;
void __iomem *docptr = doc->virtadr;
int i;
for (i=0; i < len; i++)
......@@ -404,7 +518,7 @@ static void doc2001_readbuf(struct mtd_info *mtd,
{
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = (void *)this->priv;
unsigned long docptr = doc->virtadr;
void __iomem *docptr = doc->virtadr;
int i;
/* Start read pipeline */
......@@ -422,7 +536,7 @@ static int doc2001_verifybuf(struct mtd_info *mtd,
{
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = (void *)this->priv;
unsigned long docptr = doc->virtadr;
void __iomem *docptr = doc->virtadr;
int i;
/* Start read pipeline */
......@@ -442,7 +556,7 @@ static u_char doc2001plus_read_byte(struct mtd_info *mtd)
{
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = (void *)this->priv;
unsigned long docptr = doc->virtadr;
void __iomem *docptr = doc->virtadr;
u_char ret;
ReadDOC(docptr, Mplus_ReadPipeInit);
......@@ -457,7 +571,7 @@ static void doc2001plus_writebuf(struct mtd_info *mtd,
{
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = (void *)this->priv;
unsigned long docptr = doc->virtadr;
void __iomem *docptr = doc->virtadr;
int i;
if (debug)printk("writebuf of %d bytes: ", len);
......@@ -474,7 +588,7 @@ static void doc2001plus_readbuf(struct mtd_info *mtd,
{
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = (void *)this->priv;
unsigned long docptr = doc->virtadr;
void __iomem *docptr = doc->virtadr;
int i;
if (debug)printk("readbuf of %d bytes: ", len);
......@@ -504,7 +618,7 @@ static int doc2001plus_verifybuf(struct mtd_info *mtd,
{
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = (void *)this->priv;
unsigned long docptr = doc->virtadr;
void __iomem *docptr = doc->virtadr;
int i;
if (debug)printk("verifybuf of %d bytes: ", len);
......@@ -530,7 +644,7 @@ static void doc2001plus_select_chip(struct mtd_info *mtd, int chip)
{
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = (void *)this->priv;
unsigned long docptr = doc->virtadr;
void __iomem *docptr = doc->virtadr;
int floor = 0;
if(debug)printk("select chip (%d)\n", chip);
......@@ -556,7 +670,7 @@ static void doc200x_select_chip(struct mtd_info *mtd, int chip)
{
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = (void *)this->priv;
unsigned long docptr = doc->virtadr;
void __iomem *docptr = doc->virtadr;
int floor = 0;
if(debug)printk("select chip (%d)\n", chip);
......@@ -583,7 +697,7 @@ static void doc200x_hwcontrol(struct mtd_info *mtd, int cmd)
{
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = (void *)this->priv;
unsigned long docptr = doc->virtadr;
void __iomem *docptr = doc->virtadr;
switch(cmd) {
case NAND_CTL_SETNCE:
......@@ -621,7 +735,7 @@ static void doc2001plus_command (struct mtd_info *mtd, unsigned command, int col
{
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = (void *)this->priv;
unsigned long docptr = doc->virtadr;
void __iomem *docptr = doc->virtadr;
/*
* Must terminate write pipeline before sending any commands
......@@ -725,7 +839,7 @@ static int doc200x_dev_ready(struct mtd_info *mtd)
{
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = (void *)this->priv;
unsigned long docptr = doc->virtadr;
void __iomem *docptr = doc->virtadr;
if (DoC_is_MillenniumPlus(doc)) {
/* 11.4.2 -- must NOP four times before checking FR/B# */
......@@ -763,7 +877,7 @@ static void doc200x_enable_hwecc(struct mtd_info *mtd, int mode)
{
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = (void *)this->priv;
unsigned long docptr = doc->virtadr;
void __iomem *docptr = doc->virtadr;
/* Prime the ECC engine */
switch(mode) {
......@@ -782,7 +896,7 @@ static void doc2001plus_enable_hwecc(struct mtd_info *mtd, int mode)
{
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = (void *)this->priv;
unsigned long docptr = doc->virtadr;
void __iomem *docptr = doc->virtadr;
/* Prime the ECC engine */
switch(mode) {
......@@ -803,7 +917,7 @@ static int doc200x_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
{
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = (void *)this->priv;
unsigned long docptr = doc->virtadr;
void __iomem *docptr = doc->virtadr;
int i;
int emptymatch = 1;
......@@ -861,7 +975,7 @@ static int doc200x_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_
int i, ret = 0;
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = (void *)this->priv;
unsigned long docptr = doc->virtadr;
void __iomem *docptr = doc->virtadr;
volatile u_char dummy;
int emptymatch = 1;
......@@ -914,7 +1028,7 @@ static int doc200x_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_
erased block, in which case the ECC will not come out right.
We'll suppress the error and tell the caller everything's
OK. Because it is. */
if (!emptymatch) ret = doc_decode_ecc (dat, calc_ecc);
if (!emptymatch) ret = doc_ecc_decode (rs_decoder, dat, calc_ecc);
if (ret > 0)
printk(KERN_ERR "doc200x_correct_data corrected %d errors\n", ret);
}
......@@ -1385,13 +1499,13 @@ static inline int __init doc_probe(unsigned long physadr)
struct mtd_info *mtd;
struct nand_chip *nand;
struct doc_priv *doc;
unsigned long virtadr;
void __iomem *virtadr;
unsigned char save_control;
unsigned char tmp, tmpb, tmpc;
int reg, len, numchips;
int ret = 0;
virtadr = (unsigned long)ioremap(physadr, DOC_IOREMAP_LEN);
virtadr = (void __iomem *)ioremap(physadr, DOC_IOREMAP_LEN);
if (!virtadr) {
printk(KERN_ERR "Diskonchip ioremap failed: 0x%x bytes at 0x%lx\n", DOC_IOREMAP_LEN, physadr);
return -EIO;
......@@ -1518,7 +1632,7 @@ static inline int __init doc_probe(unsigned long physadr)
sizeof(struct nand_chip) +
sizeof(struct doc_priv) +
(2 * sizeof(struct nand_bbt_descr));
mtd = kmalloc(len, GFP_KERNEL);
mtd = kmalloc(len, GFP_KERNEL);
if (!mtd) {
printk(KERN_ERR "DiskOnChip kmalloc (%d bytes) failed!\n", len);
ret = -ENOMEM;
......@@ -1543,7 +1657,7 @@ static inline int __init doc_probe(unsigned long physadr)
nand->enable_hwecc = doc200x_enable_hwecc;
nand->calculate_ecc = doc200x_calculate_ecc;
nand->correct_data = doc200x_correct_data;
//nand->data_buf
nand->autooob = &doc200x_oobinfo;
nand->eccmode = NAND_ECC_HW6_512;
nand->options = NAND_USE_FLASH_BBT | NAND_HWECC_SYNDROME;
......@@ -1589,6 +1703,23 @@ static inline int __init doc_probe(unsigned long physadr)
return ret;
}
static void release_nanddoc(void)
{
struct mtd_info *mtd, *nextmtd;
struct nand_chip *nand;
struct doc_priv *doc;
for (mtd = doclist; mtd; mtd = nextmtd) {
nand = mtd->priv;
doc = (void *)nand->priv;
nextmtd = doc->nextdoc;
nand_release(mtd);
iounmap((void *)doc->virtadr);
kfree(mtd);
}
}
int __init init_nanddoc(void)
{
int i;
......@@ -1607,23 +1738,34 @@ int __init init_nanddoc(void)
printk(KERN_INFO "No valid DiskOnChip devices found\n");
return -ENODEV;
}
/* 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 510
* primitve element to generate roots = 1
* generator polinomial degree = 4
*/
rs_decoder = init_rs(10, 0x409, FCR, 1, NROOTS);
if (!rs_decoder) {
printk (KERN_ERR "DiskOnChip: Could not create a RS decoder\n");
release_nanddoc();
return -ENOMEM;
}
return 0;
}
void __exit cleanup_nanddoc(void)
{
struct mtd_info *mtd, *nextmtd;
struct nand_chip *nand;
struct doc_priv *doc;
for (mtd = doclist; mtd; mtd = nextmtd) {
nand = mtd->priv;
doc = (void *)nand->priv;
/* Cleanup the nand/DoC resources */
release_nanddoc();
nextmtd = doc->nextdoc;
nand_release(mtd);
iounmap((void *)doc->virtadr);
kfree(mtd);
/* Free the reed solomon resources */
if (rs_decoder) {
free_rs(rs_decoder);
}
}
......
......@@ -6,7 +6,7 @@
* Derived from drivers/mtd/nand/autcpu12.c
* Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de)
*
* $Id: edb7312.c,v 1.8 2004/07/12 15:03:26 dwmw2 Exp $
* $Id: edb7312.c,v 1.10 2004/10/05 13:50:20 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
......@@ -53,9 +53,9 @@ static struct mtd_info *ep7312_mtd = NULL;
* Module stuff
*/
static int ep7312_fio_pbase = EP7312_FIO_PBASE;
static int ep7312_pxdr = EP7312_PXDR;
static int ep7312_pxddr = EP7312_PXDDR;
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 MODULE
MODULE_PARM(ep7312_fio_pbase, "i");
......@@ -131,7 +131,7 @@ static int __init ep7312_init (void)
const char *part_type = 0;
int mtd_parts_nb = 0;
struct mtd_partition *mtd_parts = 0;
int ep7312_fio_base;
void __iomem * ep7312_fio_base;
/* Allocate memory for MTD device structure and private data */
ep7312_mtd = kmalloc(sizeof(struct mtd_info) +
......@@ -143,7 +143,7 @@ static int __init ep7312_init (void)
}
/* map physical adress */
ep7312_fio_base = (unsigned long)ioremap(ep7312_fio_pbase, SZ_1K);
ep7312_fio_base = (void __iomem *)ioremap(ep7312_fio_pbase, SZ_1K);
if(!ep7312_fio_base) {
printk("ioremap EDB7312 NAND flash failed\n");
kfree(ep7312_mtd);
......@@ -181,16 +181,7 @@ static int __init ep7312_init (void)
return -ENXIO;
}
/* Allocate memory for internal data buffer */
this->data_buf = kmalloc (sizeof(u_char) * (ep7312_mtd->oobblock + ep7312_mtd->oobsize), GFP_KERNEL);
if (!this->data_buf) {
printk("Unable to allocate NAND data buffer for EDB7312.\n");
iounmap((void *)ep7312_fio_base);
kfree (ep7312_mtd);
return -ENOMEM;
}
#ifdef CONFIG_PARTITIONS
#ifdef CONFIG_MTD_PARTITIONS
ep7312_mtd->name = "edb7312-nand";
mtd_parts_nb = parse_mtd_partitions(ep7312_mtd, part_probes,
&mtd_parts, 0);
......@@ -221,8 +212,8 @@ static void __exit ep7312_cleanup (void)
{
struct nand_chip *this = (struct nand_chip *) &ep7312_mtd[1];
/* Unregister the device */
del_mtd_device (ep7312_mtd);
/* Release resources, unregister device */
nand_release (ap7312_mtd);
/* Free internal data buffer */
kfree (this->data_buf);
......
/*
* drivers/mtd/nand/h1910.c
*
* Copyright (C) 2003 Joshua Wise (joshua@joshuawise.com)
*
* Derived from drivers/mtd/nand/edb7312.c
* Copyright (C) 2002 Marius Grger (mag@sysgo.de)
* Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de)
*
* $Id: h1910.c,v 1.4 2004/10/05 13:50:20 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 = (void __iomem *)__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");
......@@ -24,6 +24,10 @@
*
* 05-19-2004 tglx: Basic support for Renesas AG-AND chips
*
* 09-24-2004 tglx: add support for hardware controllers (e.g. ECC) shared
* among multiple independend devices. Suggestions and initial patch
* from Ben Dooks <ben-mtd@fluff.org>
*
* Credits:
* David Woodhouse for adding multichip support
*
......@@ -37,7 +41,7 @@
* The AG-AND chips have nice features for speed improvement,
* which are not supported yet. Read / program 4 pages in one go.
*
* $Id: nand_base.c,v 1.115 2004/08/09 13:19:45 dwmw2 Exp $
* $Id: nand_base.c,v 1.121 2004/10/06 19:53:11 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
......@@ -131,25 +135,31 @@ static int nand_verify_pages (struct mtd_info *mtd, struct nand_chip *this, int
#define nand_verify_pages(...) (0)
#endif
static void nand_get_chip (struct nand_chip *this, struct mtd_info *mtd, int new_state);
static void nand_get_device (struct nand_chip *this, struct mtd_info *mtd, int new_state);
/**
* nand_release_chip - [GENERIC] release chip
* nand_release_device - [GENERIC] release chip
* @mtd: MTD device structure
*
* Deselect, release chip lock and wake up anyone waiting on the device
*/
static void nand_release_chip (struct mtd_info *mtd)
static void nand_release_device (struct mtd_info *mtd)
{
struct nand_chip *this = mtd->priv;
/* De-select the NAND device */
this->select_chip(mtd, -1);
/* Do we have a hardware controller ? */
if (this->controller) {
spin_lock(&this->controller->lock);
this->controller->active = NULL;
spin_unlock(&this->controller->lock);
}
/* Release the chip */
spin_lock_bh (&this->chip_lock);
spin_lock (&this->chip_lock);
this->state = FL_READY;
wake_up (&this->wq);
spin_unlock_bh (&this->chip_lock);
spin_unlock (&this->chip_lock);
}
/**
......@@ -388,7 +398,7 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
chipnr = (int)(ofs >> this->chip_shift);
/* Grab the lock and see if the device is available */
nand_get_chip (this, mtd, FL_READING);
nand_get_device (this, mtd, FL_READING);
/* Select the NAND device */
this->select_chip(mtd, chipnr);
......@@ -410,7 +420,7 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
if (getchip) {
/* Deselect and wake up anyone waiting on the device */
nand_release_chip(mtd);
nand_release_device(mtd);
}
return res;
......@@ -533,8 +543,8 @@ static void nand_command (struct mtd_info *mtd, unsigned command, int column, in
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 (this->chipsize & 0x0c000000)
/* One more address cycle for devices > 32MiB */
if (this->chipsize > (32 << 20))
this->write_byte(mtd, (unsigned char) ((page_addr >> 16) & 0x0f));
}
/* Latch in address */
......@@ -689,15 +699,16 @@ static void nand_command_lp (struct mtd_info *mtd, unsigned command, int column,
}
/**
* nand_get_chip - [GENERIC] Get chip for selected access
* nand_get_device - [GENERIC] Get chip for selected access
* @this: the nand chip descriptor
* @mtd: MTD device structure
* @new_state: the state which is requested
*
* Get the device and lock it for exclusive access
*/
static void nand_get_chip (struct nand_chip *this, struct mtd_info *mtd, int new_state)
static void nand_get_device (struct nand_chip *this, struct mtd_info *mtd, int new_state)
{
struct nand_chip *active = this;
DECLARE_WAITQUEUE (wait, current);
......@@ -705,19 +716,29 @@ static void nand_get_chip (struct nand_chip *this, struct mtd_info *mtd, int new
* Grab the lock and see if the device is available
*/
retry:
spin_lock_bh (&this->chip_lock);
if (this->state == FL_READY) {
this->state = new_state;
spin_unlock_bh (&this->chip_lock);
return;
/* Hardware controller shared among independend devices */
if (this->controller) {
spin_lock (&this->controller->lock);
if (this->controller->active)
active = this->controller->active;
else
this->controller->active = this;
spin_unlock (&this->controller->lock);
}
if (active == this) {
spin_lock (&this->chip_lock);
if (this->state == FL_READY) {
this->state = new_state;
spin_unlock (&this->chip_lock);
return;
}
}
set_current_state (TASK_UNINTERRUPTIBLE);
add_wait_queue (&this->wq, &wait);
spin_unlock_bh (&this->chip_lock);
add_wait_queue (&active->wq, &wait);
spin_unlock (&active->chip_lock);
schedule ();
remove_wait_queue (&this->wq, &wait);
remove_wait_queue (&active->wq, &wait);
goto retry;
}
......@@ -747,7 +768,6 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *this, int state)
* any case on any machine. */
ndelay (100);
spin_lock_bh (&this->chip_lock);
if ((state == FL_ERASING) && (this->options & NAND_IS_AND))
this->cmdfunc (mtd, NAND_CMD_STATUS_MULTI, -1, -1);
else
......@@ -755,24 +775,19 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *this, int state)
while (time_before(jiffies, timeo)) {
/* Check, if we were interrupted */
if (this->state != state) {
spin_unlock_bh (&this->chip_lock);
if (this->state != state)
return 0;
}
if (this->dev_ready) {
if (this->dev_ready(mtd))
break;
} else {
if (this->read_byte(mtd) & NAND_STATUS_READY)
break;
}
if (this->read_byte(mtd) & NAND_STATUS_READY)
break;
spin_unlock_bh (&this->chip_lock);
yield ();
spin_lock_bh (&this->chip_lock);
}
status = (int) this->read_byte(mtd);
spin_unlock_bh (&this->chip_lock);
return status;
}
......@@ -1051,7 +1066,7 @@ static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
}
/* Grab the lock and see if the device is available */
nand_get_chip (this, mtd ,FL_READING);
nand_get_device (this, mtd ,FL_READING);
/* use userspace supplied oobinfo, if zero */
if (oobsel == NULL)
......@@ -1281,7 +1296,7 @@ static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
}
/* Deselect and wake up anyone waiting on the device */
nand_release_chip(mtd);
nand_release_device(mtd);
/*
* Return success, if no ECC failures, else -EBADMSG
......@@ -1328,7 +1343,7 @@ static int nand_read_oob (struct mtd_info *mtd, loff_t from, size_t len, size_t
}
/* Grab the lock and see if the device is available */
nand_get_chip (this, mtd , FL_READING);
nand_get_device (this, mtd , FL_READING);
/* Select the NAND device */
this->select_chip(mtd, chipnr);
......@@ -1379,7 +1394,7 @@ static int nand_read_oob (struct mtd_info *mtd, loff_t from, size_t len, size_t
}
/* Deselect and wake up anyone waiting on the device */
nand_release_chip(mtd);
nand_release_device(mtd);
/* Return happy */
*retlen = len;
......@@ -1413,7 +1428,7 @@ int nand_read_raw (struct mtd_info *mtd, uint8_t *buf, loff_t from, size_t len,
}
/* Grab the lock and see if the device is available */
nand_get_chip (this, mtd , FL_READING);
nand_get_device (this, mtd , FL_READING);
this->select_chip (mtd, chip);
......@@ -1442,7 +1457,7 @@ int nand_read_raw (struct mtd_info *mtd, uint8_t *buf, loff_t from, size_t len,
}
/* Deselect and wake up anyone waiting on the device */
nand_release_chip(mtd);
nand_release_device(mtd);
return 0;
}
......@@ -1564,7 +1579,7 @@ static int nand_write_ecc (struct mtd_info *mtd, loff_t to, size_t len,
}
/* Grab the lock and see if the device is available */
nand_get_chip (this, mtd, FL_WRITING);
nand_get_device (this, mtd, FL_WRITING);
/* Calculate chipnr */
chipnr = (int)(to >> this->chip_shift);
......@@ -1669,7 +1684,7 @@ static int nand_write_ecc (struct mtd_info *mtd, loff_t to, size_t len,
out:
/* Deselect and wake up anyone waiting on the device */
nand_release_chip(mtd);
nand_release_device(mtd);
return ret;
}
......@@ -1709,7 +1724,7 @@ static int nand_write_oob (struct mtd_info *mtd, loff_t to, size_t len, size_t *
}
/* Grab the lock and see if the device is available */
nand_get_chip (this, mtd, FL_WRITING);
nand_get_device (this, mtd, FL_WRITING);
/* Select the NAND device */
this->select_chip(mtd, chipnr);
......@@ -1771,7 +1786,7 @@ static int nand_write_oob (struct mtd_info *mtd, loff_t to, size_t len, size_t *
ret = 0;
out:
/* Deselect and wake up anyone waiting on the device */
nand_release_chip(mtd);
nand_release_device(mtd);
return ret;
}
......@@ -1838,7 +1853,7 @@ static int nand_writev_ecc (struct mtd_info *mtd, const struct kvec *vecs, unsig
}
/* Grab the lock and see if the device is available */
nand_get_chip (this, mtd, FL_WRITING);
nand_get_device (this, mtd, FL_WRITING);
/* Get the current chip-nr */
chipnr = (int) (to >> this->chip_shift);
......@@ -1952,7 +1967,7 @@ static int nand_writev_ecc (struct mtd_info *mtd, const struct kvec *vecs, unsig
ret = 0;
out:
/* Deselect and wake up anyone waiting on the device */
nand_release_chip(mtd);
nand_release_device(mtd);
*retlen = written;
return ret;
......@@ -2041,7 +2056,7 @@ int nand_erase_nand (struct mtd_info *mtd, struct erase_info *instr, int allowbb
instr->fail_addr = 0xffffffff;
/* Grab the lock and see if the device is available */
nand_get_chip (this, mtd, FL_ERASING);
nand_get_device (this, mtd, FL_ERASING);
/* Shift to get first page */
page = (int) (instr->addr >> this->page_shift);
......@@ -2112,7 +2127,7 @@ int nand_erase_nand (struct mtd_info *mtd, struct erase_info *instr, int allowbb
mtd_erase_callback(instr);
/* Deselect and wake up anyone waiting on the device */
nand_release_chip(mtd);
nand_release_device(mtd);
/* Return more or less happy */
return ret;
......@@ -2127,43 +2142,13 @@ int nand_erase_nand (struct mtd_info *mtd, struct erase_info *instr, int allowbb
static void nand_sync (struct mtd_info *mtd)
{
struct nand_chip *this = mtd->priv;
DECLARE_WAITQUEUE (wait, current);
DEBUG (MTD_DEBUG_LEVEL3, "nand_sync: called\n");
retry:
/* Grab the spinlock */
spin_lock_bh (&this->chip_lock);
/* See what's going on */
switch (this->state) {
case FL_READY:
case FL_SYNCING:
this->state = FL_SYNCING;
spin_unlock_bh (&this->chip_lock);
break;
default:
/* Not an idle state */
add_wait_queue (&this->wq, &wait);
spin_unlock_bh (&this->chip_lock);
schedule ();
remove_wait_queue (&this->wq, &wait);
goto retry;
}
/* Lock the device */
spin_lock_bh (&this->chip_lock);
/* Set the device to be ready again */
if (this->state == FL_SYNCING) {
this->state = FL_READY;
wake_up (&this->wq);
}
/* Unlock the device */
spin_unlock_bh (&this->chip_lock);
/* Grab the lock and see if the device is available */
nand_get_device (this, mtd, FL_SYNCING);
/* Release it and go back */
nand_release_device (mtd);
}
......
......@@ -6,7 +6,7 @@
*
* Copyright (C) 2004 Thomas Gleixner (tglx@linutronix.de)
*
* $Id: nand_bbt.c,v 1.24 2004/06/28 08:25:35 gleixner Exp $
* $Id: nand_bbt.c,v 1.26 2004/10/05 13:50:20 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
......@@ -114,6 +114,7 @@ static int check_pattern (uint8_t *buf, int len, int paglen, struct nand_bbt_des
* @num: the number of bbt descriptors to read
* @bits: number of bits per block
* @offs: offset in the memory table
* @reserved_block_code: Pattern to identify reserved blocks
*
* Read the bad block table starting from page.
*
......@@ -796,7 +797,7 @@ int nand_scan_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd)
len = mtd->size >> (this->bbt_erase_shift + 2);
/* Allocate memory (2bit per block) */
this->bbt = (uint8_t *) kmalloc (len, GFP_KERNEL);
this->bbt = kmalloc (len, GFP_KERNEL);
if (!this->bbt) {
printk (KERN_ERR "nand_scan_bbt: Out of memory\n");
return -ENOMEM;
......
......@@ -6,7 +6,7 @@
* Derived from drivers/mtd/nand/edb7312.c
*
*
* $Id: ppchameleonevb.c,v 1.2 2004/05/05 22:09:54 gleixner Exp $
* $Id: ppchameleonevb.c,v 1.4 2004/10/05 13:50:20 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
......@@ -74,12 +74,6 @@ MODULE_PARM(ppchameleonevb_fio_pbase, "i");
__setup("ppchameleonevb_fio_pbase=",ppchameleonevb_fio_pbase);
#endif
/* Internal buffers. Page buffer and oob buffer for one block */
static u_char data_buf[2048 + 64];
static u_char oob_buf[64 * 64];
static u_char data_buf_evb[512 + 16];
static u_char oob_buf_evb[16 * 32];
#ifdef CONFIG_MTD_PARTITIONS
/*
* Define static partitions for flash devices
......@@ -196,8 +190,8 @@ static int __init ppchameleonevb_init (void)
const char *part_type = 0;
int mtd_parts_nb = 0;
struct mtd_partition *mtd_parts = 0;
int ppchameleon_fio_base;
int ppchameleonevb_fio_base;
void __iomem *ppchameleon_fio_base;
void __iomem *ppchameleonevb_fio_base;
/*********************************
......@@ -205,15 +199,14 @@ static int __init ppchameleonevb_init (void)
*********************************/
/* Allocate memory for MTD device structure and private data */
ppchameleon_mtd = kmalloc(sizeof(struct mtd_info) +
sizeof(struct nand_chip),
GFP_KERNEL);
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 = (unsigned long)ioremap(ppchameleon_fio_pbase, SZ_4M);
ppchameleon_fio_base = (void __iomem *) ioremap(ppchameleon_fio_pbase, SZ_4M);
if(!ppchameleon_fio_base) {
printk("ioremap PPChameleon NAND flash failed\n");
kfree(ppchameleon_mtd);
......@@ -264,10 +257,6 @@ static int __init ppchameleonevb_init (void)
/* ECC mode */
this->eccmode = NAND_ECC_SOFT;
/* Set internal data buffer */
this->data_buf = data_buf;
this->oob_buf = oob_buf;
/* Scan to find existence of the device (it could not be mounted) */
if (nand_scan (ppchameleon_mtd, 1)) {
iounmap((void *)ppchameleon_fio_base);
......@@ -309,15 +298,14 @@ static int __init ppchameleonevb_init (void)
****************************/
/* Allocate memory for MTD device structure and private data */
ppchameleonevb_mtd = kmalloc(sizeof(struct mtd_info) +
sizeof(struct nand_chip),
GFP_KERNEL);
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 = (unsigned long)ioremap(ppchameleonevb_fio_pbase, SZ_4M);
ppchameleonevb_fio_base = (void __iomem *)ioremap(ppchameleonevb_fio_pbase, SZ_4M);
if(!ppchameleonevb_fio_base) {
printk("ioremap PPChameleonEVB NAND flash failed\n");
kfree(ppchameleonevb_mtd);
......@@ -349,7 +337,8 @@ static int __init ppchameleonevb_init (void)
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);
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);
......@@ -359,7 +348,6 @@ static int __init ppchameleonevb_init (void)
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;
......@@ -372,10 +360,6 @@ static int __init ppchameleonevb_init (void)
/* ECC mode */
this->eccmode = NAND_ECC_SOFT;
/* Set internal data buffer */
this->data_buf = data_buf_evb;
this->oob_buf = oob_buf_evb;
/* Scan to find existence of the device */
if (nand_scan (ppchameleonevb_mtd, 1)) {
iounmap((void *)ppchameleonevb_fio_base);
......@@ -412,15 +396,20 @@ module_init(ppchameleonevb_init);
*/
static void __exit ppchameleonevb_cleanup (void)
{
struct nand_chip *this = (struct nand_chip *) &ppchameleonevb_mtd[1];
/* Unregister the device */
del_mtd_device (ppchameleonevb_mtd);
struct nand_chip *this;
/* Free internal data buffer */
kfree (this->data_buf);
/* 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);
......
/*
* 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.6 2004/10/05 22:11:46 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);
MODULE_PARM(rtc_from4_fio_base, "i");
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");
/* 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.5 2004/10/12 10:10:15 bjd 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 (struct s3c2410_nand_info *)dev_get_drvdata(dev);
}
static struct s3c2410_platform_nand *to_nand_plat(struct device *dev)
{
return (struct s3c2410_platform_nand *)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->twrph0, 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 = (struct s3c2410_nand_mtd *)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 = (struct nand_chip *)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 = (struct nand_chip *)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");
......@@ -8,7 +8,7 @@
* to controllines (due to change in nand.c)
* page_cache added
*
* $Id: spia.c,v 1.21 2003/07/11 15:12:29 dwmw2 Exp $
* $Id: spia.c,v 1.23 2004/10/05 13:50:20 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
......@@ -132,8 +132,8 @@ int __init spia_init (void)
(*(volatile unsigned char *) (spia_io_base + spia_peddr)) = 0x07;
/* Set address of NAND IO lines */
this->IO_ADDR_R = spia_fio_base;
this->IO_ADDR_W = spia_fio_base;
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 */
......@@ -145,14 +145,6 @@ int __init spia_init (void)
return -ENXIO;
}
/* Allocate memory for internal data buffer */
this->data_buf = kmalloc (sizeof(u_char) * (spia_mtd->oobblock + spia_mtd->oobsize), GFP_KERNEL);
if (!this->data_buf) {
printk ("Unable to allocate NAND data buffer for SPIA.\n");
kfree (spia_mtd);
return -ENOMEM;
}
/* Register the partitions */
add_mtd_partitions(spia_mtd, partition_info, NUM_PARTITIONS);
......@@ -167,13 +159,8 @@ module_init(spia_init);
#ifdef MODULE
static void __exit spia_cleanup (void)
{
struct nand_chip *this = (struct nand_chip *) &spia_mtd[1];
/* Unregister the device */
del_mtd_device (spia_mtd);
/* Free internal data buffer */
kfree (this->data_buf);
/* Release resources, unregister device */
nand_release (spia_mtd);
/* Free the MTD device structure */
kfree (spia_mtd);
......
......@@ -15,7 +15,7 @@
* 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.2 2003/10/21 10:04:58 dwmw2 Exp $
* $Id: toto.c,v 1.4 2004/10/05 13:50:20 gleixner Exp $
*/
#include <linux/slab.h>
......@@ -37,7 +37,7 @@
*/
static struct mtd_info *toto_mtd = NULL;
static int toto_io_base = OMAP_FLASH_1_BASE;
static unsigned long toto_io_base = OMAP_FLASH_1_BASE;
#define CONFIG_NAND_WORKAROUND 1
......@@ -155,14 +155,6 @@ int __init toto_init (void)
goto out_mtd;
}
/* Allocate memory for internal data buffer */
this->data_buf = kmalloc (sizeof(u_char) * (toto_mtd->oobblock + toto_mtd->oobsize), GFP_KERNEL);
if (!this->data_buf) {
printk (KERN_WARNING "Unable to allocate NAND data buffer for toto.\n");
err = -ENOMEM;
goto out_mtd;
}
/* Register the partitions */
switch(toto_mtd->size){
case SZ_64M: add_mtd_partitions(toto_mtd, partition_info64M, NUM_PARTITIONS64M); break;
......@@ -194,16 +186,8 @@ module_init(toto_init);
*/
static void __exit toto_cleanup (void)
{
struct nand_chip *this = (struct nand_chip *) &toto_mtd[1];
/* Unregister partitions */
del_mtd_partitions(toto_mtd);
/* Unregister the device */
del_mtd_device (toto_mtd);
/* Free internal data buffers */
kfree (this->data_buf);
/* Release resources, unregister device */
nand_release (toto_mtd);
/* Free the MTD device structure */
kfree (toto_mtd);
......
......@@ -11,7 +11,7 @@
* Derived from drivers/mtd/autcpu12.c
* Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de)
*
* $Id: tx4925ndfmc.c,v 1.3 2004/07/20 02:44:26 dwmw2 Exp $
* $Id: tx4925ndfmc.c,v 1.5 2004/10/05 13:50:20 gleixner Exp $
*
* Copyright (C) 2001 Toshiba Corporation
*
......@@ -340,8 +340,8 @@ int __init tx4925ndfmc_init (void)
tx4925ndfmc_mtd->priv = this;
/* Set address of NAND IO lines */
this->IO_ADDR_R = (unsigned long)&(tx4925_ndfmcptr->dtr);
this->IO_ADDR_W = (unsigned long)&(tx4925_ndfmcptr->dtr);
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;
......@@ -363,14 +363,6 @@ int __init tx4925ndfmc_init (void)
goto out_ior;
}
/* Allocate memory for internal data buffer */
this->data_buf = kmalloc (sizeof(u_char) * (tx4925ndfmc_mtd->oobblock + tx4925ndfmc_mtd->oobsize), GFP_KERNEL);
if (!this->data_buf) {
printk ("Unable to allocate NAND data buffer for RBTX4925.\n");
err = -ENOMEM;
goto out_ior;
}
/* Register the partitions */
#ifdef CONFIG_MTD_CMDLINE_PARTS
{
......@@ -391,14 +383,12 @@ int __init tx4925ndfmc_init (void)
default: {
printk ("Unsupported SmartMedia device\n");
err = -ENXIO;
goto out_buf;
goto out_ior;
}
}
#endif /* ifdef CONFIG_MTD_CMDLINE_PARTS */
goto out;
out_buf:
kfree (this->data_buf);
out_ior:
out:
return err;
......@@ -412,16 +402,8 @@ module_init(tx4925ndfmc_init);
#ifdef MODULE
static void __exit tx4925ndfmc_cleanup (void)
{
struct nand_chip *this = (struct nand_chip *) &tx4925ndfmc_mtd[1];
/* Unregister partitions */
del_mtd_partitions(tx4925ndfmc_mtd);
/* Unregister the device */
del_mtd_device (tx4925ndfmc_mtd);
/* Free internal data buffers */
kfree (this->data_buf);
/* Release resources, unregister device */
nand_release (tx4925ndfmc_mtd);
/* Free the MTD device structure */
kfree (tx4925ndfmc_mtd);
......
......@@ -10,7 +10,7 @@
*
* Based on spia.c by Steven J. Hill
*
* $Id: tx4938ndfmc.c,v 1.2 2004/03/27 19:55:53 gleixner Exp $
* $Id: tx4938ndfmc.c,v 1.4 2004/10/05 13:50:20 gleixner Exp $
*
* Copyright (C) 2000-2001 Toshiba Corporation
*
......@@ -365,14 +365,6 @@ int __init tx4938ndfmc_init (void)
return -ENXIO;
}
/* Allocate memory for internal data buffer */
this->data_buf = kmalloc (sizeof(u_char) * (tx4938ndfmc_mtd->oobblock + tx4938ndfmc_mtd->oobsize), GFP_KERNEL);
if (!this->data_buf) {
printk ("Unable to allocate NAND data buffer for TX4938.\n");
kfree (tx4938ndfmc_mtd);
return -ENOMEM;
}
if (protected) {
printk(KERN_INFO "TX4938 NDFMC: write protected.\n");
tx4938ndfmc_mtd->flags &= ~(MTD_WRITEABLE | MTD_ERASEABLE);
......@@ -401,19 +393,11 @@ module_init(tx4938ndfmc_init);
*/
static void __exit tx4938ndfmc_cleanup (void)
{
struct nand_chip *this = (struct nand_chip *) tx4938ndfmc_mtd->priv;
/* Unregister the device */
#ifdef CONFIG_MTD_CMDLINE_PARTS
del_mtd_partitions(tx4938ndfmc_mtd);
#endif
del_mtd_device (tx4938ndfmc_mtd);
/* Release resources, unregister device */
nand_release (tx4938ndfmc_mtd);
/* Free the MTD device structure */
kfree (tx4938ndfmc_mtd);
/* Free internal data buffer */
kfree (this->data_buf);
}
module_exit(tx4938ndfmc_cleanup);
......
......@@ -5,7 +5,7 @@
* Steven J. Hill <sjhill@realitydiluted.com>
* Thomas Gleixner <tglx@linutronix.de>
*
* $Id: nand.h,v 1.63 2004/07/07 16:29:43 gleixner Exp $
* $Id: nand.h,v 1.66 2004/10/02 10:07:08 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
......@@ -212,6 +212,18 @@ typedef enum {
FL_CACHEDPRG,
} nand_state_t;
/* Keep gcc happy */
struct nand_chip;
/**
* struct nand_hw_control - Control structure for hardware controller (e.g ECC generator) shared among independend devices
* @lock: protection lock
* @active: the mtd device which holds the controller currently
*/
struct nand_hw_control {
spinlock_t lock;
struct nand_chip *active;
};
/**
* struct nand_chip - NAND Private Flash Chip Data
......@@ -265,12 +277,13 @@ typedef enum {
* @bbt: [INTERN] bad block table pointer
* @bbt_td: [REPLACEABLE] bad block table descriptor for flash lookup
* @bbt_md: [REPLACEABLE] bad block table mirror descriptor
* @controller: [OPTIONAL] a pointer to a hardware controller structure which is shared among multiple independend devices
* @priv: [OPTIONAL] pointer to private chip date
*/
struct nand_chip {
unsigned long IO_ADDR_R;
unsigned long IO_ADDR_W;
void __iomem *IO_ADDR_R;
void __iomem *IO_ADDR_W;
u_char (*read_byte)(struct mtd_info *mtd);
void (*write_byte)(struct mtd_info *mtd, u_char byte);
......@@ -317,6 +330,7 @@ struct nand_chip {
uint8_t *bbt;
struct nand_bbt_descr *bbt_td;
struct nand_bbt_descr *bbt_md;
struct nand_hw_control *controller;
void *priv;
};
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment