This patch includes the onenand driver for s5pc100 Signed-off-by: Minkyu Kang <mk7.kang@samsung.com> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>master
parent
399e5ae0d0
commit
4678d674f0
@ -0,0 +1,636 @@ |
||||
/*
|
||||
* S3C64XX/S5PC100 OneNAND driver at U-Boot |
||||
* |
||||
* Copyright (C) 2008-2009 Samsung Electronics |
||||
* Kyungmin Park <kyungmin.park@samsung.com> |
||||
* |
||||
* Implementation: |
||||
* Emulate the pseudo BufferRAM |
||||
* |
||||
* See file CREDITS for list of people who contributed to this |
||||
* project. |
||||
* |
||||
* 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 <common.h> |
||||
#include <malloc.h> |
||||
#include <linux/mtd/compat.h> |
||||
#include <linux/mtd/mtd.h> |
||||
#include <linux/mtd/onenand.h> |
||||
#include <linux/mtd/samsung_onenand.h> |
||||
|
||||
#include <asm/io.h> |
||||
#include <asm/errno.h> |
||||
|
||||
#ifdef ONENAND_DEBUG |
||||
#define DPRINTK(format, args...) \ |
||||
do { \
|
||||
printf("%s[%d]: " format "\n", __func__, __LINE__, ##args); \
|
||||
} while (0) |
||||
#else |
||||
#define DPRINTK(...) do { } while (0) |
||||
#endif |
||||
|
||||
#define ONENAND_ERASE_STATUS 0x00 |
||||
#define ONENAND_MULTI_ERASE_SET 0x01 |
||||
#define ONENAND_ERASE_START 0x03 |
||||
#define ONENAND_UNLOCK_START 0x08 |
||||
#define ONENAND_UNLOCK_END 0x09 |
||||
#define ONENAND_LOCK_START 0x0A |
||||
#define ONENAND_LOCK_END 0x0B |
||||
#define ONENAND_LOCK_TIGHT_START 0x0C |
||||
#define ONENAND_LOCK_TIGHT_END 0x0D |
||||
#define ONENAND_UNLOCK_ALL 0x0E |
||||
#define ONENAND_OTP_ACCESS 0x12 |
||||
#define ONENAND_SPARE_ACCESS_ONLY 0x13 |
||||
#define ONENAND_MAIN_ACCESS_ONLY 0x14 |
||||
#define ONENAND_ERASE_VERIFY 0x15 |
||||
#define ONENAND_MAIN_SPARE_ACCESS 0x16 |
||||
#define ONENAND_PIPELINE_READ 0x4000 |
||||
|
||||
#if defined(CONFIG_S3C64XX) |
||||
#define MAP_00 (0x0 << 24) |
||||
#define MAP_01 (0x1 << 24) |
||||
#define MAP_10 (0x2 << 24) |
||||
#define MAP_11 (0x3 << 24) |
||||
#elif defined(CONFIG_S5PC1XX) |
||||
#define MAP_00 (0x0 << 26) |
||||
#define MAP_01 (0x1 << 26) |
||||
#define MAP_10 (0x2 << 26) |
||||
#define MAP_11 (0x3 << 26) |
||||
#endif |
||||
|
||||
/* read/write of XIP buffer */ |
||||
#define CMD_MAP_00(mem_addr) (MAP_00 | ((mem_addr) << 1)) |
||||
/* read/write to the memory device */ |
||||
#define CMD_MAP_01(mem_addr) (MAP_01 | (mem_addr)) |
||||
/* control special functions of the memory device */ |
||||
#define CMD_MAP_10(mem_addr) (MAP_10 | (mem_addr)) |
||||
/* direct interface(direct access) with the memory device */ |
||||
#define CMD_MAP_11(mem_addr) (MAP_11 | ((mem_addr) << 2)) |
||||
|
||||
struct s3c_onenand { |
||||
struct mtd_info *mtd; |
||||
void __iomem *base; |
||||
void __iomem *ahb_addr; |
||||
int bootram_command; |
||||
void __iomem *page_buf; |
||||
void __iomem *oob_buf; |
||||
unsigned int (*mem_addr)(int fba, int fpa, int fsa); |
||||
struct samsung_onenand *reg; |
||||
}; |
||||
|
||||
static struct s3c_onenand *onenand; |
||||
|
||||
static int s3c_read_cmd(unsigned int cmd) |
||||
{ |
||||
return readl(onenand->ahb_addr + cmd); |
||||
} |
||||
|
||||
static void s3c_write_cmd(int value, unsigned int cmd) |
||||
{ |
||||
writel(value, onenand->ahb_addr + cmd); |
||||
} |
||||
|
||||
/*
|
||||
* MEM_ADDR |
||||
* |
||||
* fba: flash block address |
||||
* fpa: flash page address |
||||
* fsa: flash sector address |
||||
* |
||||
* return the buffer address on the memory device |
||||
* It will be combined with CMD_MAP_XX |
||||
*/ |
||||
#if defined(CONFIG_S3C64XX) |
||||
static unsigned int s3c_mem_addr(int fba, int fpa, int fsa) |
||||
{ |
||||
return (fba << 12) | (fpa << 6) | (fsa << 4); |
||||
} |
||||
#elif defined(CONFIG_S5PC1XX) |
||||
static unsigned int s3c_mem_addr(int fba, int fpa, int fsa) |
||||
{ |
||||
return (fba << 13) | (fpa << 7) | (fsa << 5); |
||||
} |
||||
#endif |
||||
|
||||
static void s3c_onenand_reset(void) |
||||
{ |
||||
unsigned long timeout = 0x10000; |
||||
int stat; |
||||
|
||||
writel(ONENAND_MEM_RESET_COLD, &onenand->reg->mem_reset); |
||||
while (timeout--) { |
||||
stat = readl(&onenand->reg->int_err_stat); |
||||
if (stat & RST_CMP) |
||||
break; |
||||
} |
||||
stat = readl(&onenand->reg->int_err_stat); |
||||
writel(stat, &onenand->reg->int_err_ack); |
||||
|
||||
/* Clear interrupt */ |
||||
writel(0x0, &onenand->reg->int_err_ack); |
||||
/* Clear the ECC status */ |
||||
writel(0x0, &onenand->reg->ecc_err_stat); |
||||
} |
||||
|
||||
static unsigned short s3c_onenand_readw(void __iomem *addr) |
||||
{ |
||||
struct onenand_chip *this = onenand->mtd->priv; |
||||
int reg = addr - this->base; |
||||
int word_addr = reg >> 1; |
||||
int value; |
||||
|
||||
/* It's used for probing time */ |
||||
switch (reg) { |
||||
case ONENAND_REG_MANUFACTURER_ID: |
||||
return readl(&onenand->reg->manufact_id); |
||||
case ONENAND_REG_DEVICE_ID: |
||||
return readl(&onenand->reg->device_id); |
||||
case ONENAND_REG_VERSION_ID: |
||||
return readl(&onenand->reg->flash_ver_id); |
||||
case ONENAND_REG_DATA_BUFFER_SIZE: |
||||
return readl(&onenand->reg->data_buf_size); |
||||
case ONENAND_REG_TECHNOLOGY: |
||||
return readl(&onenand->reg->tech); |
||||
case ONENAND_REG_SYS_CFG1: |
||||
return readl(&onenand->reg->mem_cfg); |
||||
|
||||
/* Used at unlock all status */ |
||||
case ONENAND_REG_CTRL_STATUS: |
||||
return 0; |
||||
|
||||
case ONENAND_REG_WP_STATUS: |
||||
return ONENAND_WP_US; |
||||
|
||||
default: |
||||
break; |
||||
} |
||||
|
||||
/* BootRAM access control */ |
||||
if (reg < ONENAND_DATARAM && onenand->bootram_command) { |
||||
if (word_addr == 0) |
||||
return readl(&onenand->reg->manufact_id); |
||||
if (word_addr == 1) |
||||
return readl(&onenand->reg->device_id); |
||||
if (word_addr == 2) |
||||
return readl(&onenand->reg->flash_ver_id); |
||||
} |
||||
|
||||
value = s3c_read_cmd(CMD_MAP_11(word_addr)) & 0xffff; |
||||
printk(KERN_INFO "s3c_onenand_readw: Illegal access" |
||||
" at reg 0x%x, value 0x%x\n", word_addr, value); |
||||
return value; |
||||
} |
||||
|
||||
static void s3c_onenand_writew(unsigned short value, void __iomem *addr) |
||||
{ |
||||
struct onenand_chip *this = onenand->mtd->priv; |
||||
int reg = addr - this->base; |
||||
int word_addr = reg >> 1; |
||||
|
||||
/* It's used for probing time */ |
||||
switch (reg) { |
||||
case ONENAND_REG_SYS_CFG1: |
||||
writel(value, &onenand->reg->mem_cfg); |
||||
return; |
||||
|
||||
case ONENAND_REG_START_ADDRESS1: |
||||
case ONENAND_REG_START_ADDRESS2: |
||||
return; |
||||
|
||||
/* Lock/lock-tight/unlock/unlock_all */ |
||||
case ONENAND_REG_START_BLOCK_ADDRESS: |
||||
return; |
||||
|
||||
default: |
||||
break; |
||||
} |
||||
|
||||
/* BootRAM access control */ |
||||
if (reg < ONENAND_DATARAM) { |
||||
if (value == ONENAND_CMD_READID) { |
||||
onenand->bootram_command = 1; |
||||
return; |
||||
} |
||||
if (value == ONENAND_CMD_RESET) { |
||||
writel(ONENAND_MEM_RESET_COLD, |
||||
&onenand->reg->mem_reset); |
||||
onenand->bootram_command = 0; |
||||
return; |
||||
} |
||||
} |
||||
|
||||
printk(KERN_INFO "s3c_onenand_writew: Illegal access" |
||||
" at reg 0x%x, value 0x%x\n", word_addr, value); |
||||
|
||||
s3c_write_cmd(value, CMD_MAP_11(word_addr)); |
||||
} |
||||
|
||||
static int s3c_onenand_wait(struct mtd_info *mtd, int state) |
||||
{ |
||||
unsigned int flags = INT_ACT; |
||||
unsigned int stat, ecc; |
||||
unsigned long timeout = 0x100000; |
||||
|
||||
switch (state) { |
||||
case FL_READING: |
||||
flags |= BLK_RW_CMP | LOAD_CMP; |
||||
break; |
||||
case FL_WRITING: |
||||
flags |= BLK_RW_CMP | PGM_CMP; |
||||
break; |
||||
case FL_ERASING: |
||||
flags |= BLK_RW_CMP | ERS_CMP; |
||||
break; |
||||
case FL_LOCKING: |
||||
flags |= BLK_RW_CMP; |
||||
break; |
||||
default: |
||||
break; |
||||
} |
||||
|
||||
while (timeout--) { |
||||
stat = readl(&onenand->reg->int_err_stat); |
||||
if (stat & flags) |
||||
break; |
||||
} |
||||
|
||||
/* To get correct interrupt status in timeout case */ |
||||
stat = readl(&onenand->reg->int_err_stat); |
||||
writel(stat, &onenand->reg->int_err_ack); |
||||
|
||||
/*
|
||||
* In the Spec. it checks the controller status first |
||||
* However if you get the correct information in case of |
||||
* power off recovery (POR) test, it should read ECC status first |
||||
*/ |
||||
if (stat & LOAD_CMP) { |
||||
ecc = readl(&onenand->reg->ecc_err_stat); |
||||
if (ecc & ONENAND_ECC_4BIT_UNCORRECTABLE) { |
||||
printk(KERN_INFO "%s: ECC error = 0x%04x\n", |
||||
__func__, ecc); |
||||
mtd->ecc_stats.failed++; |
||||
return -EBADMSG; |
||||
} |
||||
} |
||||
|
||||
if (stat & (LOCKED_BLK | ERS_FAIL | PGM_FAIL | LD_FAIL_ECC_ERR)) { |
||||
printk(KERN_INFO "%s: controller error = 0x%04x\n", |
||||
__func__, stat); |
||||
if (stat & LOCKED_BLK) |
||||
printk(KERN_INFO "%s: it's locked error = 0x%04x\n", |
||||
__func__, stat); |
||||
|
||||
return -EIO; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int s3c_onenand_command(struct mtd_info *mtd, int cmd, |
||||
loff_t addr, size_t len) |
||||
{ |
||||
struct onenand_chip *this = mtd->priv; |
||||
unsigned int *m, *s; |
||||
int fba, fpa, fsa = 0; |
||||
unsigned int mem_addr; |
||||
int i, mcount, scount; |
||||
int index; |
||||
|
||||
fba = (int) (addr >> this->erase_shift); |
||||
fpa = (int) (addr >> this->page_shift); |
||||
fpa &= this->page_mask; |
||||
|
||||
mem_addr = onenand->mem_addr(fba, fpa, fsa); |
||||
|
||||
switch (cmd) { |
||||
case ONENAND_CMD_READ: |
||||
case ONENAND_CMD_READOOB: |
||||
case ONENAND_CMD_BUFFERRAM: |
||||
ONENAND_SET_NEXT_BUFFERRAM(this); |
||||
default: |
||||
break; |
||||
} |
||||
|
||||
index = ONENAND_CURRENT_BUFFERRAM(this); |
||||
|
||||
/*
|
||||
* Emulate Two BufferRAMs and access with 4 bytes pointer |
||||
*/ |
||||
m = (unsigned int *) onenand->page_buf; |
||||
s = (unsigned int *) onenand->oob_buf; |
||||
|
||||
if (index) { |
||||
m += (this->writesize >> 2); |
||||
s += (mtd->oobsize >> 2); |
||||
} |
||||
|
||||
mcount = mtd->writesize >> 2; |
||||
scount = mtd->oobsize >> 2; |
||||
|
||||
switch (cmd) { |
||||
case ONENAND_CMD_READ: |
||||
/* Main */ |
||||
for (i = 0; i < mcount; i++) |
||||
*m++ = s3c_read_cmd(CMD_MAP_01(mem_addr)); |
||||
return 0; |
||||
|
||||
case ONENAND_CMD_READOOB: |
||||
writel(TSRF, &onenand->reg->trans_spare); |
||||
/* Main */ |
||||
for (i = 0; i < mcount; i++) |
||||
*m++ = s3c_read_cmd(CMD_MAP_01(mem_addr)); |
||||
|
||||
/* Spare */ |
||||
for (i = 0; i < scount; i++) |
||||
*s++ = s3c_read_cmd(CMD_MAP_01(mem_addr)); |
||||
|
||||
writel(0, &onenand->reg->trans_spare); |
||||
return 0; |
||||
|
||||
case ONENAND_CMD_PROG: |
||||
/* Main */ |
||||
for (i = 0; i < mcount; i++) |
||||
s3c_write_cmd(*m++, CMD_MAP_01(mem_addr)); |
||||
return 0; |
||||
|
||||
case ONENAND_CMD_PROGOOB: |
||||
writel(TSRF, &onenand->reg->trans_spare); |
||||
|
||||
/* Main - dummy write */ |
||||
for (i = 0; i < mcount; i++) |
||||
s3c_write_cmd(0xffffffff, CMD_MAP_01(mem_addr)); |
||||
|
||||
/* Spare */ |
||||
for (i = 0; i < scount; i++) |
||||
s3c_write_cmd(*s++, CMD_MAP_01(mem_addr)); |
||||
|
||||
writel(0, &onenand->reg->trans_spare); |
||||
return 0; |
||||
|
||||
case ONENAND_CMD_UNLOCK_ALL: |
||||
s3c_write_cmd(ONENAND_UNLOCK_ALL, CMD_MAP_10(mem_addr)); |
||||
return 0; |
||||
|
||||
case ONENAND_CMD_ERASE: |
||||
s3c_write_cmd(ONENAND_ERASE_START, CMD_MAP_10(mem_addr)); |
||||
return 0; |
||||
|
||||
case ONENAND_CMD_MULTIBLOCK_ERASE: |
||||
s3c_write_cmd(ONENAND_MULTI_ERASE_SET, CMD_MAP_10(mem_addr)); |
||||
return 0; |
||||
|
||||
case ONENAND_CMD_ERASE_VERIFY: |
||||
s3c_write_cmd(ONENAND_ERASE_VERIFY, CMD_MAP_10(mem_addr)); |
||||
return 0; |
||||
|
||||
default: |
||||
break; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static unsigned char *s3c_get_bufferram(struct mtd_info *mtd, int area) |
||||
{ |
||||
struct onenand_chip *this = mtd->priv; |
||||
int index = ONENAND_CURRENT_BUFFERRAM(this); |
||||
unsigned char *p; |
||||
|
||||
if (area == ONENAND_DATARAM) { |
||||
p = (unsigned char *) onenand->page_buf; |
||||
if (index == 1) |
||||
p += this->writesize; |
||||
} else { |
||||
p = (unsigned char *) onenand->oob_buf; |
||||
if (index == 1) |
||||
p += mtd->oobsize; |
||||
} |
||||
|
||||
return p; |
||||
} |
||||
|
||||
static int onenand_read_bufferram(struct mtd_info *mtd, loff_t addr, int area, |
||||
unsigned char *buffer, int offset, |
||||
size_t count) |
||||
{ |
||||
unsigned char *p; |
||||
|
||||
p = s3c_get_bufferram(mtd, area); |
||||
memcpy(buffer, p + offset, count); |
||||
return 0; |
||||
} |
||||
|
||||
static int onenand_write_bufferram(struct mtd_info *mtd, loff_t addr, int area, |
||||
const unsigned char *buffer, int offset, |
||||
size_t count) |
||||
{ |
||||
unsigned char *p; |
||||
|
||||
p = s3c_get_bufferram(mtd, area); |
||||
memcpy(p + offset, buffer, count); |
||||
return 0; |
||||
} |
||||
|
||||
static int s3c_onenand_bbt_wait(struct mtd_info *mtd, int state) |
||||
{ |
||||
struct samsung_onenand *reg = (struct samsung_onenand *)onenand->base; |
||||
unsigned int flags = INT_ACT | LOAD_CMP; |
||||
unsigned int stat; |
||||
unsigned long timeout = 0x10000; |
||||
|
||||
while (timeout--) { |
||||
stat = readl(®->int_err_stat); |
||||
if (stat & flags) |
||||
break; |
||||
} |
||||
/* To get correct interrupt status in timeout case */ |
||||
stat = readl(&onenand->reg->int_err_stat); |
||||
writel(stat, &onenand->reg->int_err_ack); |
||||
|
||||
if (stat & LD_FAIL_ECC_ERR) { |
||||
s3c_onenand_reset(); |
||||
return ONENAND_BBT_READ_ERROR; |
||||
} |
||||
|
||||
if (stat & LOAD_CMP) { |
||||
int ecc = readl(&onenand->reg->ecc_err_stat); |
||||
if (ecc & ONENAND_ECC_4BIT_UNCORRECTABLE) { |
||||
s3c_onenand_reset(); |
||||
return ONENAND_BBT_READ_ERROR; |
||||
} |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static void s3c_onenand_check_lock_status(struct mtd_info *mtd) |
||||
{ |
||||
struct onenand_chip *this = mtd->priv; |
||||
unsigned int block, end; |
||||
int tmp; |
||||
|
||||
end = this->chipsize >> this->erase_shift; |
||||
|
||||
for (block = 0; block < end; block++) { |
||||
tmp = s3c_read_cmd(CMD_MAP_01(onenand->mem_addr(block, 0, 0))); |
||||
|
||||
if (readl(&onenand->reg->int_err_stat) & LOCKED_BLK) { |
||||
printf("block %d is write-protected!\n", block); |
||||
writel(LOCKED_BLK, &onenand->reg->int_err_ack); |
||||
} |
||||
} |
||||
} |
||||
|
||||
static void s3c_onenand_do_lock_cmd(struct mtd_info *mtd, loff_t ofs, |
||||
size_t len, int cmd) |
||||
{ |
||||
struct onenand_chip *this = mtd->priv; |
||||
int start, end, start_mem_addr, end_mem_addr; |
||||
|
||||
start = ofs >> this->erase_shift; |
||||
start_mem_addr = onenand->mem_addr(start, 0, 0); |
||||
end = start + (len >> this->erase_shift) - 1; |
||||
end_mem_addr = onenand->mem_addr(end, 0, 0); |
||||
|
||||
if (cmd == ONENAND_CMD_LOCK) { |
||||
s3c_write_cmd(ONENAND_LOCK_START, CMD_MAP_10(start_mem_addr)); |
||||
s3c_write_cmd(ONENAND_LOCK_END, CMD_MAP_10(end_mem_addr)); |
||||
} else { |
||||
s3c_write_cmd(ONENAND_UNLOCK_START, CMD_MAP_10(start_mem_addr)); |
||||
s3c_write_cmd(ONENAND_UNLOCK_END, CMD_MAP_10(end_mem_addr)); |
||||
} |
||||
|
||||
this->wait(mtd, FL_LOCKING); |
||||
} |
||||
|
||||
static void s3c_onenand_unlock_all(struct mtd_info *mtd) |
||||
{ |
||||
struct onenand_chip *this = mtd->priv; |
||||
loff_t ofs = 0; |
||||
size_t len = this->chipsize; |
||||
|
||||
/* FIXME workaround */ |
||||
this->subpagesize = mtd->writesize; |
||||
mtd->subpage_sft = 0; |
||||
|
||||
if (this->options & ONENAND_HAS_UNLOCK_ALL) { |
||||
/* Write unlock command */ |
||||
this->command(mtd, ONENAND_CMD_UNLOCK_ALL, 0, 0); |
||||
|
||||
/* No need to check return value */ |
||||
this->wait(mtd, FL_LOCKING); |
||||
|
||||
/* Workaround for all block unlock in DDP */ |
||||
if (!ONENAND_IS_DDP(this)) { |
||||
s3c_onenand_check_lock_status(mtd); |
||||
return; |
||||
} |
||||
|
||||
/* All blocks on another chip */ |
||||
ofs = this->chipsize >> 1; |
||||
len = this->chipsize >> 1; |
||||
} |
||||
|
||||
s3c_onenand_do_lock_cmd(mtd, ofs, len, ONENAND_CMD_UNLOCK); |
||||
s3c_onenand_check_lock_status(mtd); |
||||
} |
||||
|
||||
#ifdef CONFIG_S3C64XX |
||||
static void s3c_set_width_regs(struct onenand_chip *this) |
||||
{ |
||||
int dev_id, density; |
||||
int fba, fpa, fsa; |
||||
int dbs_dfs; |
||||
|
||||
dev_id = DEVICE_ID0_REG; |
||||
|
||||
density = (dev_id >> ONENAND_DEVICE_DENSITY_SHIFT) & 0xf; |
||||
dbs_dfs = !!(dev_id & ONENAND_DEVICE_IS_DDP); |
||||
|
||||
fba = density + 7; |
||||
if (dbs_dfs) |
||||
fba--; /* Decrease the fba */ |
||||
fpa = 6; |
||||
if (density >= ONENAND_DEVICE_DENSITY_512Mb) |
||||
fsa = 2; |
||||
else |
||||
fsa = 1; |
||||
|
||||
DPRINTK("FBA %lu, FPA %lu, FSA %lu, DDP %lu", |
||||
FBA_WIDTH0_REG, FPA_WIDTH0_REG, FSA_WIDTH0_REG, |
||||
DDP_DEVICE_REG); |
||||
|
||||
DPRINTK("mem_cfg0 0x%lx, sync mode %lu, " |
||||
"dev_page_size %lu, BURST LEN %lu", |
||||
MEM_CFG0_REG, SYNC_MODE_REG, |
||||
DEV_PAGE_SIZE_REG, BURST_LEN0_REG); |
||||
|
||||
DEV_PAGE_SIZE_REG = 0x1; |
||||
|
||||
FBA_WIDTH0_REG = fba; |
||||
FPA_WIDTH0_REG = fpa; |
||||
FSA_WIDTH0_REG = fsa; |
||||
DBS_DFS_WIDTH0_REG = dbs_dfs; |
||||
} |
||||
#endif |
||||
|
||||
void s3c_onenand_init(struct mtd_info *mtd) |
||||
{ |
||||
struct onenand_chip *this = mtd->priv; |
||||
u32 size = (4 << 10); /* 4 KiB */ |
||||
|
||||
onenand = malloc(sizeof(struct s3c_onenand)); |
||||
if (!onenand) |
||||
return; |
||||
|
||||
onenand->page_buf = malloc(size * sizeof(char)); |
||||
if (!onenand->page_buf) |
||||
return; |
||||
memset(onenand->page_buf, 0xff, size); |
||||
|
||||
onenand->oob_buf = malloc(128 * sizeof(char)); |
||||
if (!onenand->oob_buf) |
||||
return; |
||||
memset(onenand->oob_buf, 0xff, 128); |
||||
|
||||
onenand->mtd = mtd; |
||||
|
||||
#if defined(CONFIG_S3C64XX) |
||||
onenand->base = (void *)0x70100000; |
||||
onenand->ahb_addr = (void *)0x20000000; |
||||
#elif defined(CONFIG_S5PC1XX) |
||||
onenand->base = (void *)0xE7100000; |
||||
onenand->ahb_addr = (void *)0xB0000000; |
||||
#endif |
||||
onenand->mem_addr = s3c_mem_addr; |
||||
onenand->reg = (struct samsung_onenand *)onenand->base; |
||||
|
||||
this->read_word = s3c_onenand_readw; |
||||
this->write_word = s3c_onenand_writew; |
||||
|
||||
this->wait = s3c_onenand_wait; |
||||
this->bbt_wait = s3c_onenand_bbt_wait; |
||||
this->unlock_all = s3c_onenand_unlock_all; |
||||
this->command = s3c_onenand_command; |
||||
|
||||
this->read_bufferram = onenand_read_bufferram; |
||||
this->write_bufferram = onenand_write_bufferram; |
||||
|
||||
this->options |= ONENAND_RUNTIME_BADBLOCK_CHECK; |
||||
} |
@ -0,0 +1,131 @@ |
||||
/*
|
||||
* Copyright (C) 2005-2009 Samsung Electronics |
||||
* Minkyu Kang <mk7.kang@samsung.com> |
||||
* Kyungmin Park <kyungmin.park@samsung.com> |
||||
* |
||||
* See file CREDITS for list of people who contributed to this |
||||
* project. |
||||
* |
||||
* 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 |
||||
*/ |
||||
|
||||
#ifndef __SAMSUNG_ONENAND_H__ |
||||
#define __SAMSUNG_ONENAND_H__ |
||||
|
||||
/*
|
||||
* OneNAND Controller |
||||
*/ |
||||
|
||||
#ifndef __ASSEMBLY__ |
||||
struct samsung_onenand { |
||||
unsigned long mem_cfg; /* 0x0000 */ |
||||
unsigned char res1[0xc]; |
||||
unsigned long burst_len; /* 0x0010 */ |
||||
unsigned char res2[0xc]; |
||||
unsigned long mem_reset; /* 0x0020 */ |
||||
unsigned char res3[0xc]; |
||||
unsigned long int_err_stat; /* 0x0030 */ |
||||
unsigned char res4[0xc]; |
||||
unsigned long int_err_mask; /* 0x0040 */ |
||||
unsigned char res5[0xc]; |
||||
unsigned long int_err_ack; /* 0x0050 */ |
||||
unsigned char res6[0xc]; |
||||
unsigned long ecc_err_stat; /* 0x0060 */ |
||||
unsigned char res7[0xc]; |
||||
unsigned long manufact_id; /* 0x0070 */ |
||||
unsigned char res8[0xc]; |
||||
unsigned long device_id; /* 0x0080 */ |
||||
unsigned char res9[0xc]; |
||||
unsigned long data_buf_size; /* 0x0090 */ |
||||
unsigned char res10[0xc]; |
||||
unsigned long boot_buf_size; /* 0x00A0 */ |
||||
unsigned char res11[0xc]; |
||||
unsigned long buf_amount; /* 0x00B0 */ |
||||
unsigned char res12[0xc]; |
||||
unsigned long tech; /* 0x00C0 */ |
||||
unsigned char res13[0xc]; |
||||
unsigned long fba; /* 0x00D0 */ |
||||
unsigned char res14[0xc]; |
||||
unsigned long fpa; /* 0x00E0 */ |
||||
unsigned char res15[0xc]; |
||||
unsigned long fsa; /* 0x00F0 */ |
||||
unsigned char res16[0x3c]; |
||||
unsigned long sync_mode; /* 0x0130 */ |
||||
unsigned char res17[0xc]; |
||||
unsigned long trans_spare; /* 0x0140 */ |
||||
unsigned char res18[0x3c]; |
||||
unsigned long err_page_addr; /* 0x0180 */ |
||||
unsigned char res19[0x1c]; |
||||
unsigned long int_pin_en; /* 0x01A0 */ |
||||
unsigned char res20[0x1c]; |
||||
unsigned long acc_clock; /* 0x01C0 */ |
||||
unsigned char res21[0x1c]; |
||||
unsigned long err_blk_addr; /* 0x01E0 */ |
||||
unsigned char res22[0xc]; |
||||
unsigned long flash_ver_id; /* 0x01F0 */ |
||||
unsigned char res23[0x6c]; |
||||
unsigned long watchdog_cnt_low; /* 0x0260 */ |
||||
unsigned char res24[0xc]; |
||||
unsigned long watchdog_cnt_hi; /* 0x0270 */ |
||||
unsigned char res25[0xc]; |
||||
unsigned long sync_write; /* 0x0280 */ |
||||
unsigned char res26[0x1c]; |
||||
unsigned long cold_reset; /* 0x02A0 */ |
||||
unsigned char res27[0xc]; |
||||
unsigned long ddp_device; /* 0x02B0 */ |
||||
unsigned char res28[0xc]; |
||||
unsigned long multi_plane; /* 0x02C0 */ |
||||
unsigned char res29[0x1c]; |
||||
unsigned long trans_mode; /* 0x02E0 */ |
||||
unsigned char res30[0x1c]; |
||||
unsigned long ecc_err_stat2; /* 0x0300 */ |
||||
unsigned char res31[0xc]; |
||||
unsigned long ecc_err_stat3; /* 0x0310 */ |
||||
unsigned char res32[0xc]; |
||||
unsigned long ecc_err_stat4; /* 0x0320 */ |
||||
unsigned char res33[0x1c]; |
||||
unsigned long dev_page_size; /* 0x0340 */ |
||||
unsigned char res34[0x4c]; |
||||
unsigned long int_mon_status; /* 0x0390 */ |
||||
}; |
||||
#endif |
||||
|
||||
#define ONENAND_MEM_RESET_HOT 0x3 |
||||
#define ONENAND_MEM_RESET_COLD 0x2 |
||||
#define ONENAND_MEM_RESET_WARM 0x1 |
||||
|
||||
#define INT_ERR_ALL 0x3fff |
||||
#define CACHE_OP_ERR (1 << 13) |
||||
#define RST_CMP (1 << 12) |
||||
#define RDY_ACT (1 << 11) |
||||
#define INT_ACT (1 << 10) |
||||
#define UNSUP_CMD (1 << 9) |
||||
#define LOCKED_BLK (1 << 8) |
||||
#define BLK_RW_CMP (1 << 7) |
||||
#define ERS_CMP (1 << 6) |
||||
#define PGM_CMP (1 << 5) |
||||
#define LOAD_CMP (1 << 4) |
||||
#define ERS_FAIL (1 << 3) |
||||
#define PGM_FAIL (1 << 2) |
||||
#define INT_TO (1 << 1) |
||||
#define LD_FAIL_ECC_ERR (1 << 0) |
||||
|
||||
#define TSRF (1 << 0) |
||||
|
||||
/* common initialize function */ |
||||
extern void s3c_onenand_init(struct mtd_info *); |
||||
|
||||
#endif |
Loading…
Reference in new issue