1
0
mirror of git://projects.qi-hardware.com/openwrt-xburst.git synced 2024-12-03 07:00:38 +02:00
openwrt-xburst/target/linux/xburst/files-2.6.27/drivers/mtd/nand/jz4750_nand.c

1786 lines
59 KiB
C
Raw Normal View History

/*
* linux/drivers/mtd/nand/jz4750_nand.c
*
* JZ4750 NAND driver
*
* Copyright (c) 2005 - 2007 Ingenic Semiconductor Inc.
* Author: <lhhuang@ingenic.cn>
*
* 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.
*/
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/init.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/jzsoc.h>
/* 32bit instead of 16byte burst is used by DMA to read or
write NAND and BCH avoiding grabbing bus for too long */
#define DMAC_DCMD_DS_NAND DMAC_DCMD_DS_32BIT
#define DIV_DS_NAND 4
#define DMAC_DCMD_DS_BCH DMAC_DCMD_DS_32BIT
#define DIV_DS_BCH 4
#define DEBUG1 0
#if DEBUG1
#define dprintk(n,x...) printk(n,##x)
#else
#define dprintk(n,x...)
#endif
#if defined(CONFIG_MTD_HW_BCH_8BIT)
#define __ECC_ENCODING __ecc_encoding_8bit
#define __ECC_DECODING __ecc_decoding_8bit
#define ERRS_SIZE 5 /* 5 words */
#else
#define __ECC_ENCODING __ecc_encoding_4bit
#define __ECC_DECODING __ecc_decoding_4bit
#define ERRS_SIZE 3 /* 3 words */
#endif
#define NAND_DATA_PORT1 0xB8000000 /* read-write area in static bank 1 */
#define NAND_DATA_PORT2 0xB4000000 /* read-write area in static bank 2 */
#define NAND_DATA_PORT3 0xAC000000 /* read-write area in static bank 3 */
#define NAND_DATA_PORT4 0xA8000000 /* read-write area in static bank 4 */
#define NAND_ADDR_OFFSET0 0x00010000 /* address port offset for share mode */
#define NAND_CMD_OFFSET0 0x00008000 /* command port offset for share mode */
#define NAND_ADDR_OFFSET1 0x00000010 /* address port offset for unshare mode */
#define NAND_CMD_OFFSET1 0x00000008 /* command port offset for unshare mode */
#if defined(CONFIG_MTD_NAND_DMA)
#define USE_IRQ 1
enum {
NAND_NONE,
NAND_PROG,
NAND_READ
};
static volatile u8 nand_status;
static volatile int dma_ack = 0;
static volatile int dma_ack1 = 0;
static char nand_dma_chan; /* automatically select a free channel */
static char bch_dma_chan = 0; /* fixed to channel 0 */
static u32 *errs;
static jz_dma_desc_8word *dma_desc_enc, *dma_desc_enc1, *dma_desc_dec, *dma_desc_dec1, *dma_desc_dec2,
*dma_desc_nand_prog, *dma_desc_nand_read;
static u32 *pval_nand_ddr;
static u8 *pval_nand_cmd_pgprog; /* for sending 0x11 or 0x10 when programing*/
#if defined(CONFIG_MTD_NAND_DMABUF)
u8 *prog_buf, *read_buf;
#endif
DECLARE_WAIT_QUEUE_HEAD(nand_prog_wait_queue);
DECLARE_WAIT_QUEUE_HEAD(nand_read_wait_queue);
#endif
struct buf_be_corrected {
u8 *data;
u8 *oob;
};
static u32 addr_offset;
static u32 cmd_offset;
extern int global_page; /* for two-plane operations */
/*
* MTD structure for JzSOC board
*/
static struct mtd_info *jz_mtd = NULL;
extern struct mtd_info *jz_mtd1;
extern char all_use_planes;
/*
* Define partitions for flash devices
*/
#if defined(CONFIG_JZ4750_FUWA) || defined(CONFIG_JZ4750D_FUWA1)
static struct mtd_partition partition_info[] = {
{name:"NAND BOOT partition",
offset:0 * 0x100000,
size:4 * 0x100000,
use_planes: 0},
{name:"NAND KERNEL partition",
offset:4 * 0x100000,
size:4 * 0x100000,
use_planes: 0},
{name:"NAND ROOTFS partition",
offset:8 * 0x100000,
size:120 * 0x100000,
use_planes: 1},
{name:"NAND DATA1 partition",
offset:128 * 0x100000,
size:128 * 0x100000,
use_planes: 1},
{name:"NAND DATA2 partition",
offset:256 * 0x100000,
size:256 * 0x100000,
use_planes: 1},
{name:"NAND VFAT partition",
offset:512 * 0x100000,
size:512 * 0x100000,
use_planes: 1},
};
/* Define max reserved bad blocks for each partition.
* This is used by the mtdblock-jz.c NAND FTL driver only.
*
* The NAND FTL driver reserves some good blocks which can't be
* seen by the upper layer. When the bad block number of a partition
* exceeds the max reserved blocks, then there is no more reserved
* good blocks to be used by the NAND FTL driver when another bad
* block generated.
*/
static int partition_reserved_badblocks[] = {
2, /* reserved blocks of mtd0 */
2, /* reserved blocks of mtd1 */
10, /* reserved blocks of mtd2 */
10, /* reserved blocks of mtd3 */
20, /* reserved blocks of mtd4 */
20
}; /* reserved blocks of mtd5 */
#endif /* CONFIG_JZ4750_FUWA or CONFIG_JZ4750_APUS */
#if defined(CONFIG_JZ4750_APUS) || defined(CONFIG_JZ4750D_CETUS)
/* Reserve 32MB for bootloader, splash1, splash2 and radiofw */
#define MISC_OFFSET (32 * 0x100000)
#define MISC_SIZE ( 1 * 0x100000)
#define RECOVERY_SIZE ( 5 * 0x100000)
#define BOOT_SIZE ( 4 * 0x100000)
#define SYSTEM_SIZE (128 * 0x100000)
#define USERDATA_SIZE (128 * 0x100000)
#define CACHE_SIZE (128 * 0x100000)
#define STORAGE_SIZE (MTDPART_SIZ_FULL)
static struct mtd_partition partition_info[] = {
/* Android partitions:
*
* misc@mtd0 : raw
* recovery@mtd1: raw
* boot@mtd2: raw
* system@mtd3: yaffs2
* userdata@mtd4: yaffs2
* cache@mtd5: yaffs2
* storage@mtd6: vfat
*/
{name: "misc",
offset: MISC_OFFSET,
size: MISC_SIZE,
use_planes: 0},
{name: "recovery",
offset: (MISC_OFFSET+MISC_SIZE),
size: RECOVERY_SIZE,
use_planes: 0},
{name: "boot",
offset: (MISC_OFFSET+MISC_SIZE+RECOVERY_SIZE),
size: BOOT_SIZE,
use_planes: 0},
{name: "system",
offset: (MISC_OFFSET+MISC_SIZE+RECOVERY_SIZE+BOOT_SIZE),
size: SYSTEM_SIZE,
use_planes: 0},
{name: "userdata",
offset: (MISC_OFFSET+MISC_SIZE+RECOVERY_SIZE+BOOT_SIZE+SYSTEM_SIZE),
size: USERDATA_SIZE,
use_planes: 0},
{name: "cache",
offset: (MISC_OFFSET+MISC_SIZE+RECOVERY_SIZE+BOOT_SIZE+SYSTEM_SIZE+USERDATA_SIZE),
size: CACHE_SIZE,
use_planes: 0},
{name: "storage",
offset: (MISC_OFFSET+MISC_SIZE+RECOVERY_SIZE+BOOT_SIZE+SYSTEM_SIZE+USERDATA_SIZE+CACHE_SIZE),
size: STORAGE_SIZE,
use_planes: 0}
};
/* Define max reserved bad blocks for each partition.
* This is used by the mtdblock-jz.c NAND FTL driver only.
*
* The NAND FTL driver reserves some good blocks which can't be
* seen by the upper layer. When the bad block number of a partition
* exceeds the max reserved blocks, then there is no more reserved
* good blocks to be used by the NAND FTL driver when another bad
* block generated.
*/
static int partition_reserved_badblocks[] = {
10, /* reserved blocks of mtd0 */
10, /* reserved blocks of mtd1 */
10, /* reserved blocks of mtd2 */
10, /* reserved blocks of mtd3 */
10, /* reserved blocks of mtd4 */
10, /* reserved blocks of mtd5 */
12 /* reserved blocks of mtd6 */
};
#endif /* CONFIG_JZ4750_FUWA or CONFIG_JZ4750_APUS */
/*-------------------------------------------------------------------------
* Following three functions are exported and used by the mtdblock-jz.c
* NAND FTL driver only.
*/
unsigned short get_mtdblock_write_verify_enable(void)
{
#ifdef CONFIG_MTD_MTDBLOCK_WRITE_VERIFY_ENABLE
return 1;
#endif
return 0;
}
EXPORT_SYMBOL(get_mtdblock_write_verify_enable);
unsigned short get_mtdblock_oob_copies(void)
{
return CONFIG_MTD_OOB_COPIES;
}
EXPORT_SYMBOL(get_mtdblock_oob_copies);
int *get_jz_badblock_table(void)
{
return partition_reserved_badblocks;
}
EXPORT_SYMBOL(get_jz_badblock_table);
/*-------------------------------------------------------------------------*/
static void jz_hwcontrol(struct mtd_info *mtd, int dat, u32 ctrl)
{
struct nand_chip *this = (struct nand_chip *)(mtd->priv);
u32 nandaddr = (u32)this->IO_ADDR_W;
extern u8 nand_nce; /* defined in nand_base.c, indicates which chip select is used for current nand chip */
if (ctrl & NAND_CTRL_CHANGE) {
if (ctrl & NAND_NCE) {
switch (nand_nce) {
case NAND_NCE1:
this->IO_ADDR_W = this->IO_ADDR_R = (void __iomem *)NAND_DATA_PORT1;
REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE2;
REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE3;
REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE4;
REG_EMC_NFCSR |= EMC_NFCSR_NFCE1;
break;
case NAND_NCE2:
this->IO_ADDR_W = this->IO_ADDR_R = (void __iomem *)NAND_DATA_PORT2;
REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE1;
REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE3;
REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE4;
REG_EMC_NFCSR |= EMC_NFCSR_NFCE2;
break;
case NAND_NCE3:
this->IO_ADDR_W = this->IO_ADDR_R = (void __iomem *)NAND_DATA_PORT3;
REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE1;
REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE2;
REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE4;
REG_EMC_NFCSR |= EMC_NFCSR_NFCE3;
break;
case NAND_NCE4:
this->IO_ADDR_W = this->IO_ADDR_R = (void __iomem *)NAND_DATA_PORT4;
REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE1;
REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE2;
REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE3;
REG_EMC_NFCSR |= EMC_NFCSR_NFCE4;
break;
default:
printk("error: no nand_nce 0x%x\n",nand_nce);
break;
}
} else {
REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE1;
REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE2;
REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE3;
REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE4;
}
if (ctrl & NAND_ALE)
nandaddr = (u32)((u32)(this->IO_ADDR_W) | addr_offset);
else
nandaddr = (u32)((u32)(this->IO_ADDR_W) & ~addr_offset);
if (ctrl & NAND_CLE)
nandaddr = (u32)(nandaddr | cmd_offset);
else
nandaddr = (u32)(nandaddr & ~cmd_offset);
}
this->IO_ADDR_W = (void __iomem *)nandaddr;
if (dat != NAND_CMD_NONE) {
writeb(dat, this->IO_ADDR_W);
/* printk("write cmd:0x%x to 0x%x\n",dat,(u32)this->IO_ADDR_W); */
}
}
static int jz_device_ready(struct mtd_info *mtd)
{
int ready, wait = 10;
while (wait--);
ready = __gpio_get_pin(91);
return ready;
}
/*
* EMC setup
*/
static void jz_device_setup(void)
{
// PORT 0:
// PORT 1:
// PORT 2:
// PIN/BIT N FUNC0 FUNC1
// 21 CS1# -
// 22 CS2# -
// 23 CS3# -
// 24 CS4# -
#define GPIO_CS2_N (32*2+22)
#define GPIO_CS3_N (32*2+23)
#define GPIO_CS4_N (32*2+24)
#define SMCR_VAL 0x0d444400
//#define SMCR_VAL 0x0d221200
__gpio_as_nand_8bit(1);
/* Set NFE bit */
REG_EMC_NFCSR |= EMC_NFCSR_NFE1;
/* Read/Write timings */
REG_EMC_SMCR1 = SMCR_VAL;
#if defined(CONFIG_MTD_NAND_CS2)
__gpio_as_func0(GPIO_CS2_N);
/* Set NFE bit */
REG_EMC_NFCSR |= EMC_NFCSR_NFE2;
/* Read/Write timings */
REG_EMC_SMCR2 = SMCR_VAL;
#endif
#if defined(CONFIG_MTD_NAND_CS3)
__gpio_as_func0(GPIO_CS3_N);
/* Set NFE bit */
REG_EMC_NFCSR |= EMC_NFCSR_NFE3;
/* Read/Write timings */
REG_EMC_SMCR3 = SMCR_VAL;
#endif
#if defined(CONFIG_MTD_NAND_CS4)
__gpio_as_func0(GPIO_CS4_N);
/* Set NFE bit */
REG_EMC_NFCSR |= EMC_NFCSR_NFE4;
/* Read/Write timings */
REG_EMC_SMCR4 = SMCR_VAL;
#endif
}
#ifdef CONFIG_MTD_HW_BCH_ECC
static void jzsoc_nand_enable_bch_hwecc(struct mtd_info *mtd, int mode)
{
struct nand_chip *this = (struct nand_chip *)(mtd->priv);
int eccsize = this->ecc.size;
int eccbytes = this->ecc.bytes;
int eccsteps = this->ecc.steps / this->planenum;
int oob_per_eccsize = this->ecc.layout->eccpos[0] / eccsteps;
REG_BCH_INTS = 0xffffffff;
if (mode == NAND_ECC_READ) {
__ECC_DECODING();
__ecc_cnt_dec(eccsize + oob_per_eccsize + eccbytes);
#if defined(CONFIG_MTD_NAND_DMA)
__ecc_dma_enable();
#endif
}
if (mode == NAND_ECC_WRITE) {
__ECC_ENCODING();
__ecc_cnt_enc(eccsize + oob_per_eccsize);
#if defined(CONFIG_MTD_NAND_DMA)
__ecc_dma_enable();
#endif
}
}
/**
* bch_correct
* @dat: data to be corrected
* @idx: the index of error bit in an eccsize
*/
static void bch_correct(struct mtd_info *mtd, u8 * dat, int idx)
{
struct nand_chip *this = (struct nand_chip *)(mtd->priv);
int eccsize = this->ecc.size;
int eccsteps = this->ecc.steps / this->planenum;
int ecc_pos = this->ecc.layout->eccpos[0];
int oob_per_eccsize = ecc_pos / eccsteps;
int i, bit; /* the 'bit' of i byte is error */
i = (idx - 1) >> 3;
bit = (idx - 1) & 0x7;
dprintk("error:i=%d, bit=%d\n",i,bit);
if (i < eccsize){
((struct buf_be_corrected *)dat)->data[i] ^= (1 << bit);
} else if (i < eccsize + oob_per_eccsize) {
((struct buf_be_corrected *)dat)->oob[i-eccsize] ^= (1 << bit);
}
}
#if defined(CONFIG_MTD_NAND_DMA)
/**
* jzsoc_nand_bch_correct_data
* @mtd: mtd info structure
* @dat: data to be corrected
* @errs0: pointer to the dma target buffer of bch decoding which stores BHINTS and
* BHERR0~3(8-bit BCH) or BHERR0~1(4-bit BCH)
* @calc_ecc: no used
*/
static int jzsoc_nand_bch_correct_data(struct mtd_info *mtd, u_char * dat, u_char * errs0, u_char * calc_ecc)
{
u32 stat;
u32 *errs = (u32 *)errs0;
if (REG_DMAC_DCCSR(0) & DMAC_DCCSR_BERR) {
stat = errs[0];
dprintk("stat=%x err0:%x err1:%x \n", stat, errs[1], errs[2]);
if (stat & BCH_INTS_ERR) {
if (stat & BCH_INTS_UNCOR) {
printk("NAND: Uncorrectable ECC error\n");
return -1;
} else {
u32 errcnt = (stat & BCH_INTS_ERRC_MASK) >> BCH_INTS_ERRC_BIT;
switch (errcnt) {
#if defined(CONFIG_MTD_HW_BCH_8BIT)
case 8:
bch_correct(mtd, dat, (errs[4] & BCH_ERR_INDEX_ODD_MASK) >> BCH_ERR_INDEX_ODD_BIT);
case 7:
bch_correct(mtd, dat, (errs[4] & BCH_ERR_INDEX_EVEN_MASK) >> BCH_ERR_INDEX_EVEN_BIT);
case 6:
bch_correct(mtd, dat, (errs[3] & BCH_ERR_INDEX_ODD_MASK) >> BCH_ERR_INDEX_ODD_BIT);
case 5:
bch_correct(mtd, dat, (errs[3] & BCH_ERR_INDEX_EVEN_MASK) >> BCH_ERR_INDEX_EVEN_BIT);
#endif
case 4:
bch_correct(mtd, dat, (errs[2] & BCH_ERR_INDEX_ODD_MASK) >> BCH_ERR_INDEX_ODD_BIT);
case 3:
bch_correct(mtd, dat, (errs[2] & BCH_ERR_INDEX_EVEN_MASK) >> BCH_ERR_INDEX_EVEN_BIT);
case 2:
bch_correct(mtd, dat, (errs[1] & BCH_ERR_INDEX_ODD_MASK) >> BCH_ERR_INDEX_ODD_BIT);
case 1:
bch_correct(mtd, dat, (errs[1] & BCH_ERR_INDEX_EVEN_MASK) >> BCH_ERR_INDEX_EVEN_BIT);
default:
break;
}
}
}
}
return 0;
}
#else /* cpu mode */
/**
* jzsoc_nand_bch_correct_data
* @mtd: mtd info structure
* @dat: data to be corrected
* @read_ecc: pointer to ecc buffer calculated when nand writing
* @calc_ecc: no used
*/
static int jzsoc_nand_bch_correct_data(struct mtd_info *mtd, u_char * dat, u_char * read_ecc, u_char * calc_ecc)
{
struct nand_chip *this = (struct nand_chip *)(mtd->priv);
int eccsize = this->ecc.size;
int eccbytes = this->ecc.bytes;
int eccsteps = this->ecc.steps / this->planenum;
int ecc_pos = this->ecc.layout->eccpos[0];
int oob_per_eccsize = ecc_pos / eccsteps;
short k;
u32 stat;
/* Write data to REG_BCH_DR */
for (k = 0; k < eccsize; k++) {
REG_BCH_DR = ((struct buf_be_corrected *)dat)->data[k];
}
/* Write oob to REG_BCH_DR */
for (k = 0; k < oob_per_eccsize; k++) {
REG_BCH_DR = ((struct buf_be_corrected *)dat)->oob[k];
}
/* Write parities to REG_BCH_DR */
for (k = 0; k < eccbytes; k++) {
REG_BCH_DR = read_ecc[k];
}
/* Wait for completion */
__ecc_decode_sync();
__ecc_disable();
/* Check decoding */
stat = REG_BCH_INTS;
if (stat & BCH_INTS_ERR) {
/* Error occurred */
if (stat & BCH_INTS_UNCOR) {
printk("NAND: Uncorrectable ECC error--\n");
return -1;
} else {
u32 errcnt = (stat & BCH_INTS_ERRC_MASK) >> BCH_INTS_ERRC_BIT;
switch (errcnt) {
#if defined(CONFIG_MTD_HW_BCH_8BIT)
case 8:
bch_correct(mtd, dat, (REG_BCH_ERR3 & BCH_ERR_INDEX_ODD_MASK) >> BCH_ERR_INDEX_ODD_BIT);
/* FALL-THROUGH */
case 7:
bch_correct(mtd, dat, (REG_BCH_ERR3 & BCH_ERR_INDEX_EVEN_MASK) >> BCH_ERR_INDEX_EVEN_BIT);
/* FALL-THROUGH */
case 6:
bch_correct(mtd, dat, (REG_BCH_ERR2 & BCH_ERR_INDEX_ODD_MASK) >> BCH_ERR_INDEX_ODD_BIT);
/* FALL-THROUGH */
case 5:
bch_correct(mtd, dat, (REG_BCH_ERR2 & BCH_ERR_INDEX_EVEN_MASK) >> BCH_ERR_INDEX_EVEN_BIT);
/* FALL-THROUGH */
#endif
case 4:
bch_correct(mtd, dat, (REG_BCH_ERR1 & BCH_ERR_INDEX_ODD_MASK) >> BCH_ERR_INDEX_ODD_BIT);
/* FALL-THROUGH */
case 3:
bch_correct(mtd, dat, (REG_BCH_ERR1 & BCH_ERR_INDEX_EVEN_MASK) >> BCH_ERR_INDEX_EVEN_BIT);
/* FALL-THROUGH */
case 2:
bch_correct(mtd, dat, (REG_BCH_ERR0 & BCH_ERR_INDEX_ODD_MASK) >> BCH_ERR_INDEX_ODD_BIT);
/* FALL-THROUGH */
case 1:
bch_correct(mtd, dat, (REG_BCH_ERR0 & BCH_ERR_INDEX_EVEN_MASK) >> BCH_ERR_INDEX_EVEN_BIT);
return 0;
default:
break;
}
}
}
return 0;
}
#endif /* CONFIG_MTD_NAND_DMA */
static int jzsoc_nand_calculate_bch_ecc(struct mtd_info *mtd, const u_char * dat, u_char * ecc_code)
{
struct nand_chip *this = (struct nand_chip *)(mtd->priv);
int eccsize = this->ecc.size;
int eccbytes = this->ecc.bytes;
int eccsteps = this->ecc.steps / this->planenum;
int ecc_pos = this->ecc.layout->eccpos[0];
int oob_per_eccsize = ecc_pos / eccsteps;
volatile u8 *paraddr = (volatile u8 *)BCH_PAR0;
short i;
/* Write data to REG_BCH_DR */
for (i = 0; i < eccsize; i++) {
REG_BCH_DR = ((struct buf_be_corrected *)dat)->data[i];
}
/* Write oob to REG_BCH_DR */
for (i = 0; i < oob_per_eccsize; i++) {
REG_BCH_DR = ((struct buf_be_corrected *)dat)->oob[i];
}
__ecc_encode_sync();
__ecc_disable();
for (i = 0; i < eccbytes; i++) {
ecc_code[i] = *paraddr++;
}
return 0;
}
#if defined(CONFIG_MTD_NAND_DMA)
/**
* nand_write_page_hwecc_bch - [REPLACABLE] hardware ecc based page write function
* @mtd: mtd info structure
* @chip: nand chip info structure
* @buf: data buffer
*/
static void nand_write_page_hwecc_bch0(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t * buf, u8 cmd_pgprog)
{
int eccsize = chip->ecc.size;
int eccsteps = chip->ecc.steps / chip->planenum;
int eccbytes = chip->ecc.bytes;
int ecc_pos = chip->ecc.layout->eccpos[0];
int oob_per_eccsize = ecc_pos / eccsteps;
int pagesize = mtd->writesize / chip->planenum;
int oobsize = mtd->oobsize / chip->planenum;
int i, err, timeout;
const u8 *databuf;
u8 *oobbuf;
jz_dma_desc_8word *desc;
#if defined(CONFIG_MTD_NAND_DMABUF)
memcpy(prog_buf, buf, pagesize);
memcpy(prog_buf + pagesize, chip->oob_poi, oobsize);
dma_cache_wback_inv((u32)prog_buf, pagesize + oobsize);
#else
databuf = buf;
oobbuf = chip->oob_poi;
/* descriptors for encoding data blocks */
desc = dma_desc_enc;
for (i = 0; i < eccsteps; i++) {
desc->dsadr = CPHYSADDR((u32)databuf) + i * eccsize; /* DMA source address */
desc->dtadr = CPHYSADDR((u32)oobbuf) + ecc_pos + i * eccbytes; /* DMA target address */
dprintk("dma_desc_enc:desc:%x cmd:%x sadr:%x tadr:%x dadr:%x\n", (u32)desc, desc->dcmd, desc->dsadr, desc->dtadr,
desc->ddadr);
desc++;
}
/* descriptors for encoding oob blocks */
desc = dma_desc_enc1;
for (i = 0; i < eccsteps; i++) {
desc->dsadr = CPHYSADDR((u32)oobbuf) + oob_per_eccsize * i; /* DMA source address, 28/4 = 7bytes */
desc->dtadr = CPHYSADDR((u32)oobbuf) + ecc_pos + i * eccbytes; /* DMA target address */
dprintk("dma_desc_enc1:desc:%x cmd:%x sadr:%x tadr:%x dadr:%x\n", (u32)desc, desc->dcmd, desc->dsadr, desc->dtadr,
desc->ddadr);
desc++;
}
/* descriptor for nand programing data block */
desc = dma_desc_nand_prog;
desc->dsadr = CPHYSADDR((u32)databuf); /* DMA source address */
desc->dtadr = CPHYSADDR((u32)chip->IO_ADDR_W); /* It will be changed when using multiply chip select */
dprintk("dma_desc_nand_prog:desc:%x cmd:%x sadr:%x tadr:%x dadr:%x\n", (u32)desc, desc->dcmd, desc->dsadr, desc->dtadr,
desc->ddadr);
/* descriptor for nand programing oob block */
desc++;
desc->dsadr = CPHYSADDR((u32)oobbuf); /* DMA source address */
desc->dtadr = CPHYSADDR((u32)chip->IO_ADDR_W); /* It will be changed when using multiply chip select */
dprintk("dma_desc_oob_prog:desc:%x cmd:%x sadr:%x tadr:%x dadr:%x\n", (u32)desc, desc->dcmd, desc->dsadr, desc->dtadr,
desc->ddadr);
/* descriptor for __nand_cmd(CMD_PGPROG) */
desc++;
*pval_nand_cmd_pgprog = cmd_pgprog;
desc->dsadr = CPHYSADDR((u32)pval_nand_cmd_pgprog);
desc->dtadr = CPHYSADDR((u32)chip->IO_ADDR_R | cmd_offset); /* DMA target address: cmdport */
if (cmd_pgprog == 0x10)
desc->dcmd |= DMAC_DCMD_LINK; /* __nand_sync() by a DMA descriptor */
else if (cmd_pgprog == 0x11)
desc->dcmd &= ~DMAC_DCMD_LINK; /* __nand_sync() by polling */
dma_cache_wback_inv((u32)dma_desc_enc, (eccsteps * 2 + 2 + 1) * (sizeof(jz_dma_desc_8word)));
dma_cache_wback_inv((u32)databuf, pagesize);
dma_cache_wback_inv((u32)oobbuf, oobsize);
/* 4*6: pval_nand_ddr, pval_nand_dcs, pval_bch_ddr, pval_bch_dcs, dummy, pval_nand_cmd_pgprog */
dma_cache_wback_inv((u32)pval_nand_ddr, 4 * 8); /* 8 words, a cache line */
#endif
REG_DMAC_DCCSR(bch_dma_chan) = 0;
REG_DMAC_DCCSR(nand_dma_chan) = 0;
/* Setup DMA descriptor address */
REG_DMAC_DDA(bch_dma_chan) = CPHYSADDR((u32)dma_desc_enc);
REG_DMAC_DDA(nand_dma_chan) = CPHYSADDR((u32)dma_desc_nand_prog);
/* Setup request source */
REG_DMAC_DRSR(bch_dma_chan) = DMAC_DRSR_RS_BCH_ENC;
REG_DMAC_DRSR(nand_dma_chan) = DMAC_DRSR_RS_AUTO;
/* Setup DMA channel control/status register */
REG_DMAC_DCCSR(bch_dma_chan) = DMAC_DCCSR_DES8 | DMAC_DCCSR_EN; /* descriptor transfer, clear status, start channel */
/* Enable DMA */
REG_DMAC_DMACR(0) |= DMAC_DMACR_DMAE;
REG_DMAC_DMACR(nand_dma_chan/HALF_DMA_NUM) |= DMAC_DMACR_DMAE;
/* Enable BCH encoding */
chip->ecc.hwctl(mtd, NAND_ECC_WRITE);
dma_ack1 = 0;
nand_status = NAND_PROG;
/* DMA doorbell set -- start DMA now ... */
__dmac_channel_set_doorbell(bch_dma_chan);
#if USE_IRQ
if (cmd_pgprog == 0x10) {
dprintk("nand prog before wake up\n");
err = wait_event_interruptible_timeout(nand_prog_wait_queue, dma_ack1, 3 * HZ);
nand_status = NAND_NONE;
dprintk("nand prog after wake up\n");
if (!err) {
printk("*** NAND WRITE, Warning, wait event 3s timeout!\n");
dump_jz_dma_channel(0);
dump_jz_dma_channel(nand_dma_chan);
printk("REG_BCH_CR=%x REG_BCH_CNT=0x%x REG_BCH_INTS=%x\n", REG_BCH_CR, REG_BCH_CNT, REG_BCH_INTS);
}
dprintk("timeout remain = %d\n", err);
} else if (cmd_pgprog == 0x11) {
timeout = 100000;
while ((!__dmac_channel_transmit_end_detected(nand_dma_chan)) && (timeout--));
if (timeout <= 0)
printk("two-plane prog 0x11 timeout!\n");
}
#else
timeout = 100000;
while ((!__dmac_channel_transmit_end_detected(nand_dma_chan)) && (timeout--));
while(!chip->dev_ready(mtd));
if (timeout <= 0)
printk("not use irq, prog timeout!\n");
#endif
}
static void nand_write_page_hwecc_bch(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t * buf)
{
nand_write_page_hwecc_bch0(mtd, chip, buf, 0x10);
}
static void nand_write_page_hwecc_bch_planes(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t * buf)
{
int page;
int pagesize = mtd->writesize >> 1;
int ppb = mtd->erasesize / mtd->writesize;
page = (global_page / ppb) * ppb + global_page; /* = global_page%ppb + (global_page/ppb)*ppb*2 */
/* send cmd 0x80, the MSB should be valid if realplane is 4 */
if (chip->realplanenum == 2)
chip->cmdfunc(mtd, 0x80, 0x00, 0x00);
else
chip->cmdfunc(mtd, 0x80, 0x00, page & (1 << (chip->chip_shift - chip->page_shift)));
nand_write_page_hwecc_bch0(mtd, chip, buf, 0x11);
chip->cmdfunc(mtd, 0x81, 0x00, page + ppb);
nand_write_page_hwecc_bch0(mtd, chip, buf + pagesize, 0x10);
}
#else /* nand write in cpu mode */
static void nand_write_page_hwecc_bch(struct mtd_info *mtd, struct nand_chip *chip,
const uint8_t *buf)
{
int i, eccsize = chip->ecc.size;
int eccbytes = chip->ecc.bytes;
int eccsteps = chip->ecc.steps / chip->planenum;
int oob_per_eccsize = chip->ecc.layout->eccpos[0] / eccsteps;
int oobsize = mtd->oobsize / chip->planenum;
int ecctotal = chip->ecc.total / chip->planenum;
uint8_t *p = (uint8_t *)buf;
uint8_t *ecc_calc = chip->buffers->ecccalc;
uint32_t *eccpos = chip->ecc.layout->eccpos;
static struct buf_be_corrected buf_calc0;
struct buf_be_corrected *buf_calc = &buf_calc0;
for (i = 0; i < eccsteps; i++, p += eccsize) {
buf_calc->data = (u8 *)buf + eccsize * i;
buf_calc->oob = chip->oob_poi + oob_per_eccsize * i;
chip->ecc.hwctl(mtd, NAND_ECC_WRITE);
chip->ecc.calculate(mtd, (u8 *)buf_calc, &ecc_calc[eccbytes*i]);
chip->write_buf(mtd, p, eccsize);
}
for (i = 0; i < ecctotal; i++)
chip->oob_poi[eccpos[i]] = ecc_calc[i];
chip->write_buf(mtd, chip->oob_poi, oobsize);
}
/* nand write using two-plane mode */
static void nand_write_page_hwecc_bch_planes(struct mtd_info *mtd, struct nand_chip *chip,
const uint8_t *buf)
{
int pagesize = mtd->writesize >> 1;
int ppb = mtd->erasesize / mtd->writesize;
int page;
page = (global_page / ppb) * ppb + global_page; /* = global_page%ppb + (global_page/ppb)*ppb*2 */
/* send cmd 0x80, the MSB should be valid if realplane is 4 */
if (chip->realplanenum == 2)
chip->cmdfunc(mtd, 0x80, 0x00, 0x00);
else
chip->cmdfunc(mtd, 0x80, 0x00, page & (1 << (chip->chip_shift - chip->page_shift)));
nand_write_page_hwecc_bch(mtd, chip, buf);
chip->cmdfunc(mtd, 0x11, -1, -1); /* send cmd 0x11 */
ndelay(100);
while(!chip->dev_ready(mtd));
chip->cmdfunc(mtd, 0x81, 0x00, page + ppb); /* send cmd 0x81 */
nand_write_page_hwecc_bch(mtd, chip, buf + pagesize);
}
#endif /* CONFIG_MTD_NAND_DMA */
/**
* nand_read_page_hwecc_bch - [REPLACABLE] hardware ecc based page read function
* @mtd: mtd info structure
* @chip: nand chip info structure
* @buf: buffer to store read data
*
* Not for syndrome calculating ecc controllers which need a special oob layout
*/
#if defined(CONFIG_MTD_NAND_DMA)
static int nand_read_page_hwecc_bch0(struct mtd_info *mtd, struct nand_chip *chip, uint8_t * buf, u32 page)
{
int i, eccsize = chip->ecc.size;
int eccsteps = chip->ecc.steps / chip->planenum;
int eccbytes = chip->ecc.bytes;
int ecc_pos = chip->ecc.layout->eccpos[0];
int oob_per_eccsize = ecc_pos / eccsteps;
int pagesize = mtd->writesize / chip->planenum;
int oobsize = mtd->oobsize / chip->planenum;
u8 *databuf, *oobbuf;
jz_dma_desc_8word *desc;
int err;
u32 addrport, cmdport;
static struct buf_be_corrected buf_correct0;
addrport = (u32)(chip->IO_ADDR_R) | addr_offset;
cmdport = (u32)(chip->IO_ADDR_R) | cmd_offset;
#if defined(CONFIG_MTD_NAND_DMABUF)
databuf = read_buf;
oobbuf = read_buf + pagesize;
dma_cache_inv((u32)read_buf, pagesize + oobsize); // databuf should be invalidated.
memset(errs, 0, eccsteps * ERRS_SIZE * 4);
dma_cache_wback_inv((u32)errs, eccsteps * ERRS_SIZE * 4);
#else
databuf = buf;
oobbuf = chip->oob_poi;
/* descriptor for nand reading data block */
desc = dma_desc_nand_read;
desc->dsadr = CPHYSADDR((u32)chip->IO_ADDR_R); /* It will be changed when using multiply chip select */
desc->dtadr = CPHYSADDR((u32)databuf); /* DMA target address */
dprintk("desc_nand_read:desc:%x cmd:%x sadr:%x tadr:%x dadr:%x\n", (u32)desc, desc->dcmd, desc->dsadr, desc->dtadr,
desc->ddadr);
/* descriptor for nand reading oob block */
desc++;
desc->dsadr = CPHYSADDR((u32)chip->IO_ADDR_R); /* It will be changed when using multiply chip select */
desc->dtadr = CPHYSADDR((u32)oobbuf); /* DMA target address */
dprintk("desc_oob_read:desc:%x cmd:%x sadr:%x tadr:%x dadr:%x\n", (u32)desc, desc->dcmd, desc->dsadr, desc->dtadr,
desc->ddadr);
/* descriptors for data to be written to bch */
desc = dma_desc_dec;
for (i = 0; i < eccsteps; i++) {
desc->dsadr = CPHYSADDR((u32)databuf) + i * eccsize; /* DMA source address */
dprintk("dma_desc_dec:desc:%x cmd:%x sadr:%x tadr:%x dadr:%x\n", (u32)desc, desc->dcmd, desc->dsadr, desc->dtadr,
desc->ddadr);
desc++;
}
/* descriptors for oob to be written to bch */
desc = dma_desc_dec1;
for (i = 0; i < eccsteps; i++) {
desc->dsadr = CPHYSADDR((u32)oobbuf) + oob_per_eccsize * i; /* DMA source address */
dprintk("dma_desc_dec1:desc:%x cmd:%x sadr:%x tadr:%x dadr:%x\n", (u32)desc, desc->dcmd, desc->dsadr, desc->dtadr,
desc->ddadr);
desc++;
}
/* descriptors for parities to be written to bch */
desc = dma_desc_dec2;
for (i = 0; i < eccsteps; i++) {
desc->dsadr = CPHYSADDR((u32)oobbuf) + ecc_pos + i * eccbytes; /* DMA source address */
dprintk("dma_desc_dec2:desc:%x cmd:%x sadr:%x tadr:%x dadr:%x\n", (u32)desc, desc->dcmd, desc->dsadr, desc->dtadr,
desc->ddadr);
desc++;
}
dma_cache_wback_inv((u32)dma_desc_nand_read, (2 + eccsteps * 3) * (sizeof(jz_dma_desc_8word)));
memset(errs, 0, eccsteps * ERRS_SIZE * 4);
dma_cache_inv((u32)databuf, pagesize); // databuf should be invalidated.
dma_cache_inv((u32)oobbuf, oobsize); // oobbuf should be invalidated too
dma_cache_wback_inv((u32)errs, eccsteps * ERRS_SIZE * 4);
#endif
REG_DMAC_DCCSR(bch_dma_chan) = 0;
REG_DMAC_DCCSR(nand_dma_chan) = 0;
/* Setup DMA descriptor address */
REG_DMAC_DDA(nand_dma_chan) = CPHYSADDR((u32)dma_desc_nand_read);
REG_DMAC_DDA(bch_dma_chan) = CPHYSADDR((u32)dma_desc_dec);
/* Setup request source */
REG_DMAC_DRSR(nand_dma_chan) = DMAC_DRSR_RS_NAND;
REG_DMAC_DRSR(bch_dma_chan) = DMAC_DRSR_RS_BCH_DEC;
/* Enable DMA */
REG_DMAC_DMACR(0) |= DMAC_DMACR_DMAE;
REG_DMAC_DMACR(nand_dma_chan/HALF_DMA_NUM) |= DMAC_DMACR_DMAE;
/* Enable BCH decoding */
chip->ecc.hwctl(mtd, NAND_ECC_READ);
dma_ack = 0;
nand_status = NAND_READ;
/* DMA doorbell set -- start nand DMA now ... */
__dmac_channel_set_doorbell(nand_dma_chan);
/* Setup DMA channel control/status register */
REG_DMAC_DCCSR(nand_dma_chan) = DMAC_DCCSR_DES8 | DMAC_DCCSR_EN;
#define __nand_cmd(n) (REG8(cmdport) = (n))
#define __nand_addr(n) (REG8(addrport) = (n))
__nand_cmd(NAND_CMD_READ0);
__nand_addr(0);
if (pagesize != 512)
__nand_addr(0);
__nand_addr(page & 0xff);
__nand_addr((page >> 8) & 0xff);
/* One more address cycle for the devices whose number of page address bits > 16 */
if (((chip->chipsize >> chip->page_shift) >> 16) - 1 > 0)
__nand_addr((page >> 16) & 0xff);
if (pagesize != 512)
__nand_cmd(NAND_CMD_READSTART);
#if USE_IRQ
do {
err = wait_event_interruptible_timeout(nand_read_wait_queue, dma_ack, 3 * HZ);
}while(err == -ERESTARTSYS);
nand_status = NAND_NONE;
if (!err) {
printk("*** NAND READ, Warning, wait event 3s timeout!\n");
dump_jz_dma_channel(0);
dump_jz_dma_channel(nand_dma_chan);
printk("REG_BCH_CR=%x REG_BCH_CNT=0x%x REG_BCH_INTS=%x\n", REG_BCH_CR, REG_BCH_CNT, REG_BCH_INTS);
printk("databuf[0]=%x\n", databuf[0]);
}
dprintk("timeout remain = %d\n", err);
#else
int timeout;
timeout = 100000;
while ((!__dmac_channel_transmit_end_detected(bch_dma_chan)) && (timeout--));
if (timeout <= 0) {
printk("not use irq, NAND READ timeout!\n");
}
#endif
for (i = 0; i < eccsteps; i++) {
int stat;
struct buf_be_corrected *buf_correct = &buf_correct0;
buf_correct->data = databuf + eccsize * i;
buf_correct->oob = oobbuf + oob_per_eccsize * i;
stat = chip->ecc.correct(mtd, (u8 *)buf_correct, (u8 *)&errs[i * ERRS_SIZE], NULL);
if (stat < 0)
mtd->ecc_stats.failed++;
else
mtd->ecc_stats.corrected += stat;
}
#if defined(CONFIG_MTD_NAND_DMABUF)
memcpy(buf, read_buf, pagesize);
memcpy(chip->oob_poi, read_buf + pagesize, oobsize);
#endif
return 0;
}
static int nand_read_page_hwecc_bch(struct mtd_info *mtd, struct nand_chip *chip, uint8_t * buf)
{
u32 page = global_page;
nand_read_page_hwecc_bch0(mtd, chip, buf, page);
return 0;
}
static int nand_read_page_hwecc_bch_planes(struct mtd_info *mtd, struct nand_chip *chip, uint8_t * buf)
{
u32 page;
int pagesize = mtd->writesize >> 1;
int ppb = mtd->erasesize / mtd->writesize;
page = (global_page / ppb) * ppb + global_page; /* = global_page%ppb + (global_page/ppb)*ppb*2 */
/* read 1st page */
nand_read_page_hwecc_bch0(mtd, chip, buf, page);
/* read 2nd page */
nand_read_page_hwecc_bch0(mtd, chip, buf + pagesize, page + ppb);
return 0;
}
#else /* nand read in cpu mode */
static int nand_read_page_hwecc_bch(struct mtd_info *mtd, struct nand_chip *chip, uint8_t * buf)
{
int i, eccsize = chip->ecc.size;
int eccbytes = chip->ecc.bytes;
int eccsteps = chip->ecc.steps / chip->planenum;
int ecc_pos = chip->ecc.layout->eccpos[0];
int oob_per_eccsize = ecc_pos / eccsteps;
uint8_t *ecc_calc = chip->buffers->ecccalc;
uint8_t *ecc_code = chip->buffers->ecccode;
uint32_t *eccpos = chip->ecc.layout->eccpos;
int pagesize = mtd->writesize / chip->planenum;
int oobsize = mtd->oobsize / chip->planenum;
int ecctotal = chip->ecc.total / chip->planenum;
static struct buf_be_corrected buf_correct0;
chip->read_buf(mtd, buf, pagesize);
chip->read_buf(mtd, chip->oob_poi, oobsize);
for (i = 0; i < ecctotal; i++) {
ecc_code[i] = chip->oob_poi[eccpos[i]];
}
for (i = 0; i < eccsteps; i++) {
int stat;
struct buf_be_corrected *buf_correct = &buf_correct0;
buf_correct->data = buf + eccsize * i;
buf_correct->oob = chip->oob_poi + oob_per_eccsize * i;
chip->ecc.hwctl(mtd, NAND_ECC_READ);
stat = chip->ecc.correct(mtd, (u8 *)buf_correct, &ecc_code[eccbytes*i], &ecc_calc[eccbytes*i]);
if (stat < 0)
mtd->ecc_stats.failed++;
else
mtd->ecc_stats.corrected += stat;
}
return 0;
}
static int nand_read_page_hwecc_bch_planes(struct mtd_info *mtd, struct nand_chip *chip, uint8_t * buf)
{
int pagesize = mtd->writesize >> 1;
int ppb = mtd->erasesize / mtd->writesize;
uint32_t page;
page = (global_page / ppb) * ppb + global_page; /* = global_page%ppb + (global_page/ppb)*ppb*2 */
/* Read first page */
chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page);
nand_read_page_hwecc_bch(mtd, chip, buf);
/* Read 2nd page */
chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page + ppb);
nand_read_page_hwecc_bch(mtd, chip, buf+pagesize);
return 0;
}
#endif /* CONFIG_MTD_NAND_DMA */
#endif /* CONFIG_MTD_HW_BCH_ECC */
/* read oob using two-plane mode */
static int nand_read_oob_std_planes(struct mtd_info *mtd, struct nand_chip *chip,
int global_page, int sndcmd)
{
int page;
int oobsize = mtd->oobsize >> 1;
int ppb = mtd->erasesize / mtd->writesize;
page = (global_page / ppb) * ppb + global_page; /* = global_page%ppb + (global_page/ppb)*ppb*2 */
/* Read first page OOB */
if (sndcmd) {
chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
}
chip->read_buf(mtd, chip->oob_poi, oobsize);
/* Read second page OOB */
page += ppb;
if (sndcmd) {
chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
sndcmd = 0;
}
chip->read_buf(mtd, chip->oob_poi+oobsize, oobsize);
return 0;
}
/* write oob using two-plane mode */
static int nand_write_oob_std_planes(struct mtd_info *mtd, struct nand_chip *chip,
int global_page)
{
int status = 0, page;
const uint8_t *buf = chip->oob_poi;
int pagesize = mtd->writesize >> 1;
int oobsize = mtd->oobsize >> 1;
int ppb = mtd->erasesize / mtd->writesize;
page = (global_page / ppb) * ppb + global_page; /* = global_page%ppb + (global_page/ppb)*ppb*2 */
/* send cmd 0x80, the MSB should be valid if realplane is 4 */
if (chip->realplanenum == 2)
chip->cmdfunc(mtd, 0x80, pagesize, 0x00);
else
chip->cmdfunc(mtd, 0x80, pagesize, page & (1 << (chip->chip_shift - chip->page_shift)));
chip->write_buf(mtd, buf, oobsize);
/* Send first command to program the OOB data */
chip->cmdfunc(mtd, 0x11, -1, -1);
ndelay(100);
status = chip->waitfunc(mtd, chip);
page += ppb;
buf += oobsize;
chip->cmdfunc(mtd, 0x81, pagesize, page);
chip->write_buf(mtd, buf, oobsize);
/* Send command to program the OOB data */
chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
/* Wait long R/B */
ndelay(100);
status = chip->waitfunc(mtd, chip);
return status & NAND_STATUS_FAIL ? -EIO : 0;
}
/* nand erase using two-plane mode */
static void single_erase_cmd_planes(struct mtd_info *mtd, int global_page)
{
struct nand_chip *chip = mtd->priv;
int page, ppb = mtd->erasesize / mtd->writesize;
page = (global_page / ppb) * ppb + global_page; /* = global_page%ppb + (global_page/ppb)*ppb*2 */
/* send cmd 0x60, the MSB should be valid if realplane is 4 */
if (chip->realplanenum == 2)
chip->cmdfunc(mtd, 0x60, -1, 0x00);
else
chip->cmdfunc(mtd, 0x60, -1, page & (1 << (chip->chip_shift - chip->page_shift)));
page += ppb;
chip->cmdfunc(mtd, 0x60, -1, page & (~(ppb-1))); /* send cmd 0x60 */
chip->cmdfunc(mtd, NAND_CMD_ERASE2, -1, -1); /* send cmd 0xd0 */
/* Do not need wait R/B or check status */
}
#if defined(CONFIG_MTD_NAND_DMA)
#if USE_IRQ
static irqreturn_t nand_dma_irq(int irq, void *dev_id)
{
u8 dma_chan;
volatile int wakeup = 0;
dma_chan = irq - IRQ_DMA_0;
dprintk("jz4750_dma_irq %d, channel %d\n", irq, dma_chan);
if (__dmac_channel_transmit_halt_detected(dma_chan)) {
__dmac_channel_clear_transmit_halt(dma_chan);
wakeup = 1;
printk("DMA HALT\n");
}
if (__dmac_channel_address_error_detected(dma_chan)) {
REG_DMAC_DCCSR(dma_chan) &= ~DMAC_DCCSR_EN; /* disable DMA */
__dmac_channel_clear_address_error(dma_chan);
REG_DMAC_DSAR(dma_chan) = 0; /* reset source address register */
REG_DMAC_DTAR(dma_chan) = 0; /* reset destination address register */
/* clear address error in DMACR */
REG_DMAC_DMACR((dma_chan / HALF_DMA_NUM)) &= ~(1 << 2);
wakeup = 1;
printk("DMA address error!\n");
}
if (__dmac_channel_descriptor_invalid_detected(dma_chan)) {
__dmac_channel_clear_descriptor_invalid(dma_chan);
wakeup = 1;
printk("DMA DESC INVALID\n");
}
#if 1
while (!__dmac_channel_transmit_end_detected(dma_chan));
if (__dmac_channel_count_terminated_detected(dma_chan)) {
dprintk("DMA CT\n");
__dmac_channel_clear_count_terminated(dma_chan);
wakeup = 0;
}
#endif
if (__dmac_channel_transmit_end_detected(dma_chan)) {
dprintk("DMA TT\n");
REG_DMAC_DCCSR(dma_chan) &= ~DMAC_DCCSR_EN; /* disable DMA */
__dmac_channel_clear_transmit_end(dma_chan);
wakeup = 1;
}
if (wakeup) {
dprintk("ack %d irq , wake up dma_chan %d nand_status %d\n", dma_ack, dma_chan, nand_status);
/* wakeup wait event */
if ((dma_chan == nand_dma_chan) && (nand_status == NAND_PROG)) {
dprintk("nand prog dma irq, wake up----\n");
dma_ack1 = 1;
wake_up_interruptible(&nand_prog_wait_queue);
}
if ((dma_chan == bch_dma_chan) && (nand_status == NAND_READ)) {
dprintk("nand read irq, wake up----\n");
dma_ack = 1;
wake_up_interruptible(&nand_read_wait_queue);
}
wakeup = 0;
}
return IRQ_HANDLED;
}
#endif /* USE_IRQ */
static int jz4750_nand_dma_init(struct mtd_info *mtd)
{
struct nand_chip *chip = mtd->priv;
int eccsize = chip->ecc.size;
int eccsteps = chip->ecc.steps / chip->planenum;
int eccbytes = chip->ecc.bytes;
int ecc_pos = chip->ecc.layout->eccpos[0];
int oob_per_eccsize = ecc_pos / eccsteps;
int pagesize = mtd->writesize / chip->planenum;
int oobsize = mtd->oobsize / chip->planenum;
int i, err;
jz_dma_desc_8word *desc, *dma_desc_bch_ddr, *dma_desc_nand_ddr, *dma_desc_nand_cmd_pgprog;
u32 *pval_nand_dcs, *pval_bch_ddr, *pval_bch_dcs, *dummy;
u32 next;
#if defined(CONFIG_MTD_NAND_DMABUF)
u8 *oobbuf;
#endif
#if USE_IRQ
if ((nand_dma_chan = jz_request_dma(DMA_ID_NAND, "nand read or write", nand_dma_irq, IRQF_DISABLED, NULL)) < 0) {
printk("can't reqeust DMA nand channel.\n");
return 0;
}
dprintk("nand dma channel:%d----\n", nand_dma_chan);
if ((err = request_irq(IRQ_DMA_0 + bch_dma_chan, nand_dma_irq, IRQF_DISABLED, "bch_dma", NULL))) {
printk("bch_dma irq request err\n");
return 0;
}
#else
if ((nand_dma_chan = jz_request_dma(DMA_ID_NAND, "nand read or write", NULL, IRQF_DISABLED, NULL)) < 0) {
printk("can't reqeust DMA nand channel.\n");
return 0;
}
dprintk("nand dma channel:%d----\n", nand_dma_chan);
#endif
__dmac_channel_enable_clk(nand_dma_chan);
__dmac_channel_enable_clk(bch_dma_chan);
#if defined(CONFIG_MTD_NAND_DMABUF)
if (pagesize < 4096) {
read_buf = prog_buf = (u8 *) __get_free_page(GFP_KERNEL);
} else {
read_buf = prog_buf = (u8 *) __get_free_pages(GFP_KERNEL, 1);
}
if (!read_buf)
return -ENOMEM;
#endif
/* space for the error reports of bch decoding((4 * 5 * eccsteps) bytes), and the space for the value
* of ddr and dcs of channel 0 and channel nand_dma_chan (4 * (2 + 2) bytes) */
errs = (u32 *)kmalloc(4 * (2 + 2 + 5 * eccsteps), GFP_KERNEL);
if (!errs)
return -ENOMEM;
pval_nand_ddr = errs + 5 * eccsteps;
pval_nand_dcs = pval_nand_ddr + 1;
pval_bch_ddr = pval_nand_dcs + 1;
pval_bch_dcs = pval_bch_ddr + 1;
/* space for nand prog waiting target, the content is useless */
dummy = pval_bch_dcs + 1;
/* space to store CMD_PGPROG(0x10) or 0x11 */
pval_nand_cmd_pgprog = (u8 *)(dummy + 1);
/* desc can't across 4KB boundary, as desc base address is fixed */
/* space of descriptors for nand reading data and oob blocks */
dma_desc_nand_read = (jz_dma_desc_8word *) __get_free_page(GFP_KERNEL);
if (!dma_desc_nand_read)
return -ENOMEM;
/* space of descriptors for bch decoding */
dma_desc_dec = dma_desc_nand_read + 2;
dma_desc_dec1 = dma_desc_dec + eccsteps;
dma_desc_dec2 = dma_desc_dec + eccsteps * 2;
/* space of descriptors for notifying bch channel */
dma_desc_bch_ddr = dma_desc_dec2 + eccsteps;
/* space of descriptors for bch encoding */
dma_desc_enc = dma_desc_bch_ddr + 2;
dma_desc_enc1 = dma_desc_enc + eccsteps;
/* space of descriptors for nand programing data and oob blocks */
dma_desc_nand_prog = dma_desc_enc1 + eccsteps;
/* space of descriptors for nand prog waiting, including pgprog and sync */
dma_desc_nand_cmd_pgprog = dma_desc_nand_prog + 2;
/* space of descriptors for notifying nand channel, including ddr and dcsr */
dma_desc_nand_ddr = dma_desc_nand_cmd_pgprog + 2;
/*************************************
* Setup of nand programing descriptors
*************************************/
#if defined(CONFIG_MTD_NAND_DMABUF)
oobbuf = prog_buf + pagesize;
#endif
/* set descriptor for encoding data blocks */
desc = dma_desc_enc;
for (i = 0; i < eccsteps; i++) {
next = (CPHYSADDR((u32)dma_desc_enc1) + i * (sizeof(jz_dma_desc_8word))) >> 4;
desc->dcmd =
DMAC_DCMD_SAI | DMAC_DCMD_DAI | DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_8 |
DMAC_DCMD_DS_BCH | DMAC_DCMD_LINK;
#if defined(CONFIG_MTD_NAND_DMABUF)
desc->dsadr = CPHYSADDR((u32)prog_buf) + i * eccsize; /* DMA source address */
desc->dtadr = CPHYSADDR((u32)oobbuf) + ecc_pos + i * eccbytes; /* DMA target address */
#endif
desc->ddadr = (next << 24) + eccsize / DIV_DS_BCH; /* size: eccsize bytes */
desc->dreqt = DMAC_DRSR_RS_BCH_ENC;
dprintk("cmd:%x sadr:%x tadr:%x dadr:%x\n", desc->dcmd, desc->dsadr, desc->dtadr, desc->ddadr);
desc++;
}
/* set descriptor for encoding oob blocks */
desc = dma_desc_enc1;
for (i = 0; i < eccsteps; i++) {
next = (CPHYSADDR((u32)dma_desc_enc) + (i + 1) * (sizeof(jz_dma_desc_8word))) >> 4;
desc->dcmd =
DMAC_DCMD_BLAST | DMAC_DCMD_SAI | DMAC_DCMD_DAI | DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_8 |
DMAC_DCMD_DWDH_8 | DMAC_DCMD_DS_32BIT | DMAC_DCMD_LINK;
#if defined(CONFIG_MTD_NAND_DMABUF)
desc->dsadr = CPHYSADDR((u32)oobbuf) + oob_per_eccsize * i; /* DMA source address, 28/4 = 7bytes */
desc->dtadr = CPHYSADDR((u32)oobbuf) + ecc_pos + i * eccbytes; /* DMA target address */
#endif
desc->ddadr = (next << 24) + (oob_per_eccsize + 3) / 4; /* size: 7 bytes -> 2 words */
desc->dreqt = DMAC_DRSR_RS_BCH_ENC;
dprintk("cmd:%x sadr:%x tadr:%x dadr:%x\n", desc->dcmd, desc->dsadr, desc->dtadr, desc->ddadr);
desc++;
}
next = (CPHYSADDR((u32)dma_desc_nand_ddr)) >> 4;
desc--;
desc->ddadr = (next << 24) + (oob_per_eccsize + 3) / 4;
/* set the descriptor to set door bell of nand_dma_chan for programing nand */
desc = dma_desc_nand_ddr;
*pval_nand_ddr = 1 << (nand_dma_chan - nand_dma_chan / HALF_DMA_NUM * HALF_DMA_NUM);
next = (CPHYSADDR((u32)dma_desc_nand_ddr) + sizeof(jz_dma_desc_8word)) >> 4;
desc->dcmd = DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | DMAC_DCMD_DS_32BIT | DMAC_DCMD_LINK;
desc->dsadr = CPHYSADDR((u32)pval_nand_ddr); /* DMA source address */
desc->dtadr = CPHYSADDR(DMAC_DMADBSR(nand_dma_chan / HALF_DMA_NUM)); /* nand_dma_chan's descriptor addres register */
desc->ddadr = (next << 24) + 1; /* size: 1 word */
desc->dreqt = DMAC_DRSR_RS_AUTO;
dprintk("*pval_nand_ddr=0x%x\n", *pval_nand_ddr);
/* set the descriptor to write dccsr of nand_dma_chan for programing nand, dccsr should be set at last */
desc++;
*pval_nand_dcs = DMAC_DCCSR_DES8 | DMAC_DCCSR_EN; /* set value for writing ddr to enable channel nand_dma_chan */
desc->dcmd = DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | DMAC_DCMD_DS_32BIT;
desc->dsadr = CPHYSADDR((u32)pval_nand_dcs); /* DMA source address */
desc->dtadr = CPHYSADDR(DMAC_DCCSR(nand_dma_chan)); /* address of dma door bell set register */
desc->ddadr = (0 << 24) + 1; /* size: 1 word */
desc->dreqt = DMAC_DRSR_RS_AUTO;
dprintk("*pval_nand_dcs=0x%x\n", *pval_nand_dcs);
/* set descriptor for nand programing data block */
desc = dma_desc_nand_prog;
next = (CPHYSADDR((u32)dma_desc_nand_prog) + sizeof(jz_dma_desc_8word)) >> 4;
desc->dcmd =
DMAC_DCMD_SAI | DMAC_DCMD_DAI | DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_8 |
DMAC_DCMD_DS_NAND | DMAC_DCMD_LINK;
#if defined(CONFIG_MTD_NAND_DMABUF)
desc->dsadr = CPHYSADDR((u32)prog_buf); /* DMA source address */
#endif
desc->dtadr = CPHYSADDR((u32)(chip->IO_ADDR_W)); /* DMA target address */
desc->ddadr = (next << 24) + pagesize / DIV_DS_NAND; /* size: eccsize bytes */
desc->dreqt = DMAC_DRSR_RS_AUTO;
dprintk("cmd:%x sadr:%x tadr:%x dadr:%x\n", desc->dcmd, desc->dsadr, desc->dtadr, desc->ddadr);
/* set descriptor for nand programing oob block */
desc++;
next = (CPHYSADDR((u32)dma_desc_nand_cmd_pgprog)) >> 4;
desc->dcmd =
DMAC_DCMD_SAI | DMAC_DCMD_DAI | DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_8 |
DMAC_DCMD_DS_NAND | DMAC_DCMD_LINK;
#if defined(CONFIG_MTD_NAND_DMABUF)
desc->dsadr = CPHYSADDR((u32)oobbuf); /* DMA source address */
#endif
desc->dtadr = CPHYSADDR((u32)(chip->IO_ADDR_W)); /* DMA target address: dataport */
desc->ddadr = (next << 24) + oobsize / DIV_DS_NAND; /* size: eccsize bytes */
desc->dreqt = DMAC_DRSR_RS_AUTO;
dprintk("cmd:%x sadr:%x tadr:%x dadr:%x\n", desc->dcmd, desc->dsadr, desc->dtadr, desc->ddadr);
/* set descriptor for __nand_cmd(CMD_PGPROG) */
desc = dma_desc_nand_cmd_pgprog;
*pval_nand_cmd_pgprog = NAND_CMD_PAGEPROG;
next = (CPHYSADDR((u32)dma_desc_nand_cmd_pgprog) + sizeof(jz_dma_desc_8word)) >> 4;
desc->dcmd =
DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_8 | DMAC_DCMD_DWDH_8 | DMAC_DCMD_DS_8BIT | DMAC_DCMD_LINK;
desc->dsadr = CPHYSADDR((u32)pval_nand_cmd_pgprog); /* DMA source address */
desc->dtadr = CPHYSADDR((u32)chip->IO_ADDR_R | cmd_offset); /* DMA target address: cmdport */
desc->ddadr = (next << 24) + 1; /* size: 1 byte */
desc->dreqt = DMAC_DRSR_RS_AUTO;
dprintk("cmd:%x sadr:%x tadr:%x dadr:%x\n", desc->dcmd, desc->dsadr, desc->dtadr, desc->ddadr);
/* set descriptor for __nand_sync() */
desc++;
#if USE_IRQ
desc->dcmd =
DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | DMAC_DCMD_DS_32BIT | DMAC_DCMD_TIE;
#else
desc->dcmd =
DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | DMAC_DCMD_DS_32BIT;
#endif
desc->dsadr = CPHYSADDR((u32)pval_nand_ddr); /* DMA source address */
desc->dtadr = CPHYSADDR((u32)dummy); /* DMA target address, the content is useless */
desc->ddadr = (0 << 24) + 1; /* size: 1 word */
desc->dreqt = DMAC_DRSR_RS_NAND;
dprintk("1cmd:%x sadr:%x tadr:%x dadr:%x\n", desc->dcmd, desc->dsadr, desc->dtadr, desc->ddadr);
/* eccsteps*2 + 2 + 2 + 2:
dma_desc_enc + dma_desc_enc1 + dma_desc_nand_prog(oob) + dma_desc_nand_ddr(csr)
+ dma_desc_nand_cmd_pgprog(sync) */
dma_cache_wback_inv((u32)dma_desc_enc, (eccsteps * 2 + 2 + 2 + 2) * (sizeof(jz_dma_desc_8word)));
/* 4*6: pval_nand_ddr, pval_nand_dcs, pval_bch_ddr, pval_bch_dcs, dummy, pval_nand_cmd_pgprog */
dma_cache_wback_inv((u32)pval_nand_ddr, 4 * 8); /* 8 words, a cache line */
/*************************************
* Setup of nand reading descriptors
*************************************/
#if defined(CONFIG_MTD_NAND_DMABUF)
oobbuf = read_buf + pagesize;
#endif
/* set descriptor for nand reading data block */
desc = dma_desc_nand_read;
next = (CPHYSADDR((u32)dma_desc_nand_read) + sizeof(jz_dma_desc_8word)) >> 4;
desc->dcmd =
DMAC_DCMD_SAI | DMAC_DCMD_DAI | DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_8 | DMAC_DCMD_DWDH_32 |
DMAC_DCMD_DS_NAND | DMAC_DCMD_LINK;
desc->dsadr = CPHYSADDR((u32)(chip->IO_ADDR_R)); /* DMA source address */
#if defined(CONFIG_MTD_NAND_DMABUF)
desc->dtadr = CPHYSADDR((u32)read_buf); /* DMA target address */
#endif
desc->ddadr = (next << 24) + pagesize / DIV_DS_NAND; /* size: eccsize bytes */
desc->dreqt = DMAC_DRSR_RS_NAND;
dprintk("cmd:%x sadr:%x tadr:%x dadr:%x\n", desc->dcmd, desc->dsadr, desc->dtadr, desc->ddadr);
/* set descriptor for nand reading oob block */
desc++;
next = (CPHYSADDR((u32)dma_desc_bch_ddr)) >> 4;
desc->dcmd =
DMAC_DCMD_SAI | DMAC_DCMD_DAI | DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_8 | DMAC_DCMD_DWDH_32 |
DMAC_DCMD_DS_NAND | DMAC_DCMD_LINK;
desc->dsadr = CPHYSADDR((u32)(chip->IO_ADDR_R)); /* DMA source address */
#if defined(CONFIG_MTD_NAND_DMABUF)
desc->dtadr = CPHYSADDR((u32)oobbuf); /* DMA target address */
#endif
desc->ddadr = (next << 24) + oobsize / DIV_DS_NAND; /* size: eccsize bytes */
desc->dreqt = DMAC_DRSR_RS_AUTO;
dprintk("cmd:%x sadr:%x tadr:%x dadr:%x\n", desc->dcmd, desc->dsadr, desc->dtadr, desc->ddadr);
/* set the descriptor to set door bell for bch */
desc = dma_desc_bch_ddr;
*pval_bch_ddr = DMAC_DMADBSR_DBS0; // set value for writing ddr to enable channel 0
next = (CPHYSADDR((u32)dma_desc_bch_ddr) + sizeof(jz_dma_desc_8word)) >> 4;
desc->dcmd = DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | DMAC_DCMD_DS_32BIT | DMAC_DCMD_LINK;
desc->dsadr = CPHYSADDR((u32)pval_bch_ddr); /* DMA source address */
desc->dtadr = CPHYSADDR(DMAC_DMADBSR(0)); /* channel 1's descriptor addres register */
desc->ddadr = (next << 24) + 1; /* size: 1 word */
desc->dreqt = DMAC_DRSR_RS_AUTO;
/* set descriptor for writing dcsr */
desc++;
*pval_bch_dcs = DMAC_DCCSR_DES8 | DMAC_DCCSR_EN; // set value for writing ddr to enable channel 1
desc->dcmd = DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | DMAC_DCMD_DS_32BIT;
desc->dsadr = CPHYSADDR((u32)pval_bch_dcs); /* DMA source address */
desc->dtadr = CPHYSADDR(DMAC_DCCSR(bch_dma_chan)); /* address of dma door bell set register */
desc->ddadr = (0 << 24) + 1; /* size: 1 word */
desc->dreqt = DMAC_DRSR_RS_AUTO;
/* descriptors for data to be written to bch */
desc = dma_desc_dec;
for (i = 0; i < eccsteps; i++) {
next = CPHYSADDR((u32)dma_desc_dec1 + i * (sizeof(jz_dma_desc_8word))) >> 4;
desc->dcmd =
DMAC_DCMD_SAI | DMAC_DCMD_DAI | DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 |
DMAC_DCMD_DS_BCH | DMAC_DCMD_LINK;
#if defined(CONFIG_MTD_NAND_DMABUF)
desc->dsadr = CPHYSADDR((u32)read_buf) + i * eccsize; /* DMA source address */
#endif
desc->dtadr = CPHYSADDR((u32)errs) + i * 4 * ERRS_SIZE; /* DMA target address */
desc->ddadr = (next << 24) + eccsize / DIV_DS_BCH; /* size: eccsize bytes */
desc->dreqt = DMAC_DRSR_RS_BCH_DEC;
dprintk("desc:%x cmd:%x sadr:%x tadr:%x dadr:%x\n", (u32)desc, desc->dcmd, desc->dsadr, desc->dtadr,
desc->ddadr);
desc++;
}
/* descriptors for oob to be written to bch */
desc = dma_desc_dec1;
for (i = 0; i < eccsteps; i++) {
next = CPHYSADDR((u32)dma_desc_dec2 + i * (sizeof(jz_dma_desc_8word))) >> 4;
desc->dcmd =
DMAC_DCMD_SAI | DMAC_DCMD_DAI | DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_8 | DMAC_DCMD_DWDH_32 |
DMAC_DCMD_DS_8BIT | DMAC_DCMD_LINK;
#if defined(CONFIG_MTD_NAND_DMABUF)
desc->dsadr = CPHYSADDR((u32)oobbuf) + oob_per_eccsize * i; /* DMA source address */
#endif
desc->dtadr = CPHYSADDR((u32)errs) + i * 4 * ERRS_SIZE; /* DMA target address */
desc->ddadr = (next << 24) + oob_per_eccsize; /* size: 7 bytes */
desc->dreqt = DMAC_DRSR_RS_BCH_DEC;
dprintk("desc:%x cmd:%x sadr:%x tadr:%x dadr:%x\n", (u32)desc, desc->dcmd, desc->dsadr, desc->dtadr,
desc->ddadr);
desc++;
}
/* descriptors for parities to be written to bch */
desc = dma_desc_dec2;
for (i = 0; i < eccsteps; i++) {
next = (CPHYSADDR((u32)dma_desc_dec) + (i + 1) * (sizeof(jz_dma_desc_8word))) >> 4;
desc->dcmd =
DMAC_DCMD_BLAST | DMAC_DCMD_SAI | DMAC_DCMD_DAI | DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_8 |
DMAC_DCMD_DWDH_32 | DMAC_DCMD_DS_BCH | DMAC_DCMD_LINK;
#if defined(CONFIG_MTD_NAND_DMABUF)
desc->dsadr = CPHYSADDR((u32)oobbuf) + ecc_pos + i * eccbytes; /* DMA source address */
#endif
desc->dtadr = CPHYSADDR((u32)errs) + i * 4 * ERRS_SIZE; /* DMA target address */
desc->ddadr = (next << 24) + (eccbytes + 15) / DIV_DS_BCH; /* size: eccbytes bytes */
desc->dreqt = DMAC_DRSR_RS_BCH_DEC;
dprintk("desc:%x cmd:%x sadr:%x tadr:%x dadr:%x\n", (u32)desc, desc->dcmd, desc->dsadr, desc->dtadr,
desc->ddadr);
desc++;
}
desc--;
desc->dcmd &= ~DMAC_DCMD_LINK;
#if USE_IRQ
desc->dcmd |= DMAC_DCMD_TIE;
#endif
dma_cache_wback_inv((u32)dma_desc_nand_read, (2 + 2 + eccsteps * 3) * (sizeof(jz_dma_desc_8word)));
dma_cache_wback_inv((u32)pval_bch_ddr, 4 * 2); /* two words */
return 0;
}
#endif /* CONFIG_MTD_NAND_DMA */
/*
* Main initialization routine
*/
int __init jznand_init(void)
{
struct nand_chip *this;
int nr_partitions, ret, i;
printk(KERN_INFO "JZ NAND init");
#if defined(CONFIG_MTD_NAND_DMA)
#if defined(CONFIG_MTD_NAND_DMABUF)
printk(KERN_INFO " DMA mode, using DMA buffer in NAND driver.\n");
#else
printk(KERN_INFO " DMA mode, using DMA buffer in upper layer.\n");
#endif
#else
printk(KERN_INFO " CPU mode.\n");
#endif
/* Allocate memory for MTD device structure and private data */
jz_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL);
if (!jz_mtd) {
printk("Unable to allocate JzSOC NAND MTD device structure.\n");
return -ENOMEM;
}
/* Allocate memory for NAND when using only one plane */
jz_mtd1 = kmalloc(sizeof(struct mtd_info) + sizeof (struct nand_chip), GFP_KERNEL);
if (!jz_mtd1) {
printk ("Unable to allocate JzSOC NAND MTD device structure 1.\n");
kfree(jz_mtd);
return -ENOMEM;
}
/* Get pointer to private data */
this = (struct nand_chip *)(&jz_mtd[1]);
/* Initialize structures */
memset((char *)jz_mtd, 0, sizeof(struct mtd_info));
memset((char *)this, 0, sizeof(struct nand_chip));
/* Link the private data with the MTD structure */
jz_mtd->priv = this;
if (is_share_mode()) {
addr_offset = NAND_ADDR_OFFSET0;
cmd_offset = NAND_CMD_OFFSET0;
} else {
addr_offset = NAND_ADDR_OFFSET1;
cmd_offset = NAND_CMD_OFFSET1;
}
/* Set & initialize NAND Flash controller */
jz_device_setup();
/* Set address of NAND IO lines to static bank1 by default */
this->IO_ADDR_R = (void __iomem *)NAND_DATA_PORT1;
this->IO_ADDR_W = (void __iomem *)NAND_DATA_PORT1;
this->cmd_ctrl = jz_hwcontrol;
this->dev_ready = jz_device_ready;
#ifdef CONFIG_MTD_HW_BCH_ECC
this->ecc.calculate = jzsoc_nand_calculate_bch_ecc;
this->ecc.correct = jzsoc_nand_bch_correct_data;
this->ecc.hwctl = jzsoc_nand_enable_bch_hwecc;
this->ecc.mode = NAND_ECC_HW;
this->ecc.size = 512;
this->ecc.read_page = nand_read_page_hwecc_bch;
this->ecc.write_page = nand_write_page_hwecc_bch;
#if defined(CONFIG_MTD_HW_BCH_8BIT)
this->ecc.bytes = 13;
#else
this->ecc.bytes = 7;
#endif
#endif
#ifdef CONFIG_MTD_SW_HM_ECC
this->ecc.mode = NAND_ECC_SOFT;
#endif
/* 20 us command delay time */
this->chip_delay = 20;
/* Scan to find existance of the device */
ret = nand_scan_ident(jz_mtd, NAND_MAX_CHIPS);
if (!ret) {
if (this->planenum == 2) {
/* reset nand functions */
this->erase_cmd = single_erase_cmd_planes;
this->ecc.read_page = nand_read_page_hwecc_bch_planes;
this->ecc.write_page = nand_write_page_hwecc_bch_planes;
this->ecc.read_oob = nand_read_oob_std_planes;
this->ecc.write_oob = nand_write_oob_std_planes;
printk(KERN_INFO "Nand using two-plane mode, "
"and resized to writesize:%d oobsize:%d blocksize:0x%x \n",
jz_mtd->writesize, jz_mtd->oobsize, jz_mtd->erasesize);
}
}
/* Determine whether all the partitions will use multiple planes if supported */
nr_partitions = sizeof(partition_info) / sizeof(struct mtd_partition);
all_use_planes = 1;
for (i = 0; i < nr_partitions; i++) {
all_use_planes &= partition_info[i].use_planes;
}
if (!ret)
ret = nand_scan_tail(jz_mtd);
if (ret){
kfree (jz_mtd1);
kfree (jz_mtd);
return -ENXIO;
}
#if defined(CONFIG_MTD_NAND_DMA)
jz4750_nand_dma_init(jz_mtd);
#endif
((struct nand_chip *) (&jz_mtd1[1]))->ecc.read_page = nand_read_page_hwecc_bch;
((struct nand_chip *) (&jz_mtd1[1]))->ecc.write_page = nand_write_page_hwecc_bch;
/* Register the partitions */
printk (KERN_NOTICE "Creating %d MTD partitions on \"%s\":\n", nr_partitions, jz_mtd->name);
if ((this->planenum == 2) && !all_use_planes) {
for (i = 0; i < nr_partitions; i++) {
if (partition_info[i].use_planes)
add_mtd_partitions(jz_mtd, &partition_info[i], 1);
else
add_mtd_partitions(jz_mtd1, &partition_info[i], 1);
}
} else {
kfree(jz_mtd1);
add_mtd_partitions(jz_mtd, partition_info, nr_partitions);
}
return 0;
}
module_init(jznand_init);
/*
* Clean up routine
*/
#ifdef MODULE
#if defined(CONFIG_MTD_NAND_DMA)
static int jz4750_nand_dma_exit(struct mtd_info *mtd)
{
int pagesize = mtd->writesize / chip->planenum;
#if USE_IRQ
free_irq(IRQ_DMA_0 + nand_dma_chan, NULL);
free_irq(IRQ_DMA_0 + bch_dma_chan, NULL);
#endif
/* space for the error reports of bch decoding((4 * 5 * eccsteps) bytes),
* and the space for the value of ddr and dcs of channel 0 and channel
* nand_dma_chan (4 * (2 + 2) bytes) */
kfree(errs);
/* space for dma_desc_nand_read contains dma_desc_nand_prog,
* dma_desc_enc and dma_desc_dec */
free_page((u32)dma_desc_nand_read);
#if defined(CONFIG_MTD_NAND_DMABUF)
if (pagesize < 4096) {
free_page((u32)prog_buf);
} else {
free_pages((u32)prog_buf, 1);
}
#endif
return 0;
}
#endif
static void __exit jznand_cleanup(void)
{
#if defined(CONFIG_MTD_NAND_DMA)
jz4750_nand_dma_exit(jz_mtd);
#endif
/* Unregister partitions */
del_mtd_partitions(jz_mtd);
/* Unregister the device */
del_mtd_device(jz_mtd);
/* Free the MTD device structure */
if ((this->planenum == 2) && !all_use_planes)
kfree (jz_mtd1);
kfree(jz_mtd);
}
module_exit(jznand_cleanup);
#endif