diff options
| -rw-r--r-- | drivers/mtd/onenand/Makefile | 1 | ||||
| -rw-r--r-- | drivers/mtd/onenand/samsung.c | 636 | ||||
| -rw-r--r-- | include/linux/mtd/onenand.h | 1 | ||||
| -rw-r--r-- | include/linux/mtd/onenand_regs.h | 4 | ||||
| -rw-r--r-- | include/linux/mtd/samsung_onenand.h | 131 | 
5 files changed, 773 insertions, 0 deletions
| diff --git a/drivers/mtd/onenand/Makefile b/drivers/mtd/onenand/Makefile index 1d35a57d8..2571df016 100644 --- a/drivers/mtd/onenand/Makefile +++ b/drivers/mtd/onenand/Makefile @@ -26,6 +26,7 @@ include $(TOPDIR)/config.mk  LIB	:= $(obj)libonenand.a  COBJS-$(CONFIG_CMD_ONENAND)	:= onenand_uboot.o onenand_base.o onenand_bbt.o +COBJS-$(CONFIG_SAMSUNG_ONENAND)	+= samsung.o  COBJS	:= $(COBJS-y)  SRCS	:= $(COBJS:.o=.c) diff --git a/drivers/mtd/onenand/samsung.c b/drivers/mtd/onenand/samsung.c new file mode 100644 index 000000000..f2be68763 --- /dev/null +++ b/drivers/mtd/onenand/samsung.c @@ -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; +} diff --git a/include/linux/mtd/onenand.h b/include/linux/mtd/onenand.h index 06f7bafea..9a6f31752 100644 --- a/include/linux/mtd/onenand.h +++ b/include/linux/mtd/onenand.h @@ -135,6 +135,7 @@ struct onenand_chip {  #define ONENAND_HAS_CONT_LOCK		(0x0001)  #define ONENAND_HAS_UNLOCK_ALL		(0x0002)  #define ONENAND_HAS_2PLANE		(0x0004) +#define ONENAND_RUNTIME_BADBLOCK_CHECK	(0x0200)  #define ONENAND_PAGEBUF_ALLOC		(0x1000)  #define ONENAND_OOBBUF_ALLOC		(0x2000) diff --git a/include/linux/mtd/onenand_regs.h b/include/linux/mtd/onenand_regs.h index fc63380d9..07fed1c60 100644 --- a/include/linux/mtd/onenand_regs.h +++ b/include/linux/mtd/onenand_regs.h @@ -121,6 +121,8 @@  #define ONENAND_CMD_LOCK_TIGHT		(0x2C)  #define ONENAND_CMD_UNLOCK_ALL		(0x27)  #define ONENAND_CMD_ERASE		(0x94) +#define ONENAND_CMD_MULTIBLOCK_ERASE	(0x95) +#define ONENAND_CMD_ERASE_VERIFY	(0x71)  #define ONENAND_CMD_RESET		(0xF0)  #define ONENAND_CMD_READID		(0x90) @@ -184,7 +186,9 @@   * ECC Status Reigser FF00h (R)   */  #define ONENAND_ECC_1BIT		(1 << 0) +#define ONENAND_ECC_1BIT_ALL		(0x5555)  #define ONENAND_ECC_2BIT		(1 << 1)  #define ONENAND_ECC_2BIT_ALL		(0xAAAA) +#define ONENAND_ECC_4BIT_UNCORRECTABLE	(0x1010)  #endif				/* __ONENAND_REG_H */ diff --git a/include/linux/mtd/samsung_onenand.h b/include/linux/mtd/samsung_onenand.h new file mode 100644 index 000000000..9865780d4 --- /dev/null +++ b/include/linux/mtd/samsung_onenand.h @@ -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 |