xref: /rk3399_rockchip-uboot/drivers/mtd/onenand/samsung.c (revision 1221ce459d04a428f8880f58581f671b736c3c27)
14678d674SMinkyu Kang /*
2e5323225SBenoît Thébaudeau  * S5PC100 OneNAND driver at U-Boot
34678d674SMinkyu Kang  *
44678d674SMinkyu Kang  * Copyright (C) 2008-2009 Samsung Electronics
54678d674SMinkyu Kang  * Kyungmin Park <kyungmin.park@samsung.com>
64678d674SMinkyu Kang  *
74678d674SMinkyu Kang  * Implementation:
84678d674SMinkyu Kang  *	Emulate the pseudo BufferRAM
94678d674SMinkyu Kang  *
101a459660SWolfgang Denk  * SPDX-License-Identifier:	GPL-2.0+
114678d674SMinkyu Kang  */
124678d674SMinkyu Kang 
134678d674SMinkyu Kang #include <common.h>
144678d674SMinkyu Kang #include <malloc.h>
157b15e2bbSMike Frysinger #include <linux/compat.h>
164678d674SMinkyu Kang #include <linux/mtd/mtd.h>
174678d674SMinkyu Kang #include <linux/mtd/onenand.h>
18ff94bc40SHeiko Schocher #include <linux/mtd/flashchip.h>
194678d674SMinkyu Kang #include <linux/mtd/samsung_onenand.h>
204678d674SMinkyu Kang 
214678d674SMinkyu Kang #include <asm/io.h>
22*1221ce45SMasahiro Yamada #include <linux/errno.h>
234678d674SMinkyu Kang 
244678d674SMinkyu Kang #define ONENAND_ERASE_STATUS		0x00
254678d674SMinkyu Kang #define ONENAND_MULTI_ERASE_SET		0x01
264678d674SMinkyu Kang #define ONENAND_ERASE_START		0x03
274678d674SMinkyu Kang #define ONENAND_UNLOCK_START		0x08
284678d674SMinkyu Kang #define ONENAND_UNLOCK_END		0x09
294678d674SMinkyu Kang #define ONENAND_LOCK_START		0x0A
304678d674SMinkyu Kang #define ONENAND_LOCK_END		0x0B
314678d674SMinkyu Kang #define ONENAND_LOCK_TIGHT_START	0x0C
324678d674SMinkyu Kang #define ONENAND_LOCK_TIGHT_END		0x0D
334678d674SMinkyu Kang #define ONENAND_UNLOCK_ALL		0x0E
344678d674SMinkyu Kang #define ONENAND_OTP_ACCESS		0x12
354678d674SMinkyu Kang #define ONENAND_SPARE_ACCESS_ONLY	0x13
364678d674SMinkyu Kang #define ONENAND_MAIN_ACCESS_ONLY	0x14
374678d674SMinkyu Kang #define ONENAND_ERASE_VERIFY		0x15
384678d674SMinkyu Kang #define ONENAND_MAIN_SPARE_ACCESS	0x16
394678d674SMinkyu Kang #define ONENAND_PIPELINE_READ		0x4000
404678d674SMinkyu Kang 
41e5323225SBenoît Thébaudeau #if defined(CONFIG_S5P)
424678d674SMinkyu Kang #define MAP_00				(0x0 << 26)
434678d674SMinkyu Kang #define MAP_01				(0x1 << 26)
444678d674SMinkyu Kang #define MAP_10				(0x2 << 26)
454678d674SMinkyu Kang #define MAP_11				(0x3 << 26)
464678d674SMinkyu Kang #endif
474678d674SMinkyu Kang 
484678d674SMinkyu Kang /* read/write of XIP buffer */
494678d674SMinkyu Kang #define CMD_MAP_00(mem_addr)		(MAP_00 | ((mem_addr) << 1))
504678d674SMinkyu Kang /* read/write to the memory device */
514678d674SMinkyu Kang #define CMD_MAP_01(mem_addr)		(MAP_01 | (mem_addr))
524678d674SMinkyu Kang /* control special functions of the memory device */
534678d674SMinkyu Kang #define CMD_MAP_10(mem_addr)		(MAP_10 | (mem_addr))
544678d674SMinkyu Kang /* direct interface(direct access) with the memory device */
554678d674SMinkyu Kang #define CMD_MAP_11(mem_addr)		(MAP_11 | ((mem_addr) << 2))
564678d674SMinkyu Kang 
574678d674SMinkyu Kang struct s3c_onenand {
584678d674SMinkyu Kang 	struct mtd_info	*mtd;
594678d674SMinkyu Kang 	void __iomem	*base;
604678d674SMinkyu Kang 	void __iomem	*ahb_addr;
614678d674SMinkyu Kang 	int		bootram_command;
624678d674SMinkyu Kang 	void __iomem	*page_buf;
634678d674SMinkyu Kang 	void __iomem	*oob_buf;
644678d674SMinkyu Kang 	unsigned int	(*mem_addr)(int fba, int fpa, int fsa);
654678d674SMinkyu Kang 	struct samsung_onenand *reg;
664678d674SMinkyu Kang };
674678d674SMinkyu Kang 
684678d674SMinkyu Kang static struct s3c_onenand *onenand;
694678d674SMinkyu Kang 
s3c_read_cmd(unsigned int cmd)704678d674SMinkyu Kang static int s3c_read_cmd(unsigned int cmd)
714678d674SMinkyu Kang {
724678d674SMinkyu Kang 	return readl(onenand->ahb_addr + cmd);
734678d674SMinkyu Kang }
744678d674SMinkyu Kang 
s3c_write_cmd(int value,unsigned int cmd)754678d674SMinkyu Kang static void s3c_write_cmd(int value, unsigned int cmd)
764678d674SMinkyu Kang {
774678d674SMinkyu Kang 	writel(value, onenand->ahb_addr + cmd);
784678d674SMinkyu Kang }
794678d674SMinkyu Kang 
804678d674SMinkyu Kang /*
814678d674SMinkyu Kang  * MEM_ADDR
824678d674SMinkyu Kang  *
834678d674SMinkyu Kang  * fba: flash block address
844678d674SMinkyu Kang  * fpa: flash page address
854678d674SMinkyu Kang  * fsa: flash sector address
864678d674SMinkyu Kang  *
874678d674SMinkyu Kang  * return the buffer address on the memory device
884678d674SMinkyu Kang  * It will be combined with CMD_MAP_XX
894678d674SMinkyu Kang  */
90e5323225SBenoît Thébaudeau #if defined(CONFIG_S5P)
s3c_mem_addr(int fba,int fpa,int fsa)914678d674SMinkyu Kang static unsigned int s3c_mem_addr(int fba, int fpa, int fsa)
924678d674SMinkyu Kang {
934678d674SMinkyu Kang 	return (fba << 13) | (fpa << 7) | (fsa << 5);
944678d674SMinkyu Kang }
954678d674SMinkyu Kang #endif
964678d674SMinkyu Kang 
s3c_onenand_reset(void)974678d674SMinkyu Kang static void s3c_onenand_reset(void)
984678d674SMinkyu Kang {
994678d674SMinkyu Kang 	unsigned long timeout = 0x10000;
1004678d674SMinkyu Kang 	int stat;
1014678d674SMinkyu Kang 
1024678d674SMinkyu Kang 	writel(ONENAND_MEM_RESET_COLD, &onenand->reg->mem_reset);
1034678d674SMinkyu Kang 	while (timeout--) {
1044678d674SMinkyu Kang 		stat = readl(&onenand->reg->int_err_stat);
1054678d674SMinkyu Kang 		if (stat & RST_CMP)
1064678d674SMinkyu Kang 			break;
1074678d674SMinkyu Kang 	}
1084678d674SMinkyu Kang 	stat = readl(&onenand->reg->int_err_stat);
1094678d674SMinkyu Kang 	writel(stat, &onenand->reg->int_err_ack);
1104678d674SMinkyu Kang 
1114678d674SMinkyu Kang 	/* Clear interrupt */
1124678d674SMinkyu Kang 	writel(0x0, &onenand->reg->int_err_ack);
1134678d674SMinkyu Kang 	/* Clear the ECC status */
1144678d674SMinkyu Kang 	writel(0x0, &onenand->reg->ecc_err_stat);
1154678d674SMinkyu Kang }
1164678d674SMinkyu Kang 
s3c_onenand_readw(void __iomem * addr)1174678d674SMinkyu Kang static unsigned short s3c_onenand_readw(void __iomem *addr)
1184678d674SMinkyu Kang {
1194678d674SMinkyu Kang 	struct onenand_chip *this = onenand->mtd->priv;
1204678d674SMinkyu Kang 	int reg = addr - this->base;
1214678d674SMinkyu Kang 	int word_addr = reg >> 1;
1224678d674SMinkyu Kang 	int value;
1234678d674SMinkyu Kang 
1244678d674SMinkyu Kang 	/* It's used for probing time */
1254678d674SMinkyu Kang 	switch (reg) {
1264678d674SMinkyu Kang 	case ONENAND_REG_MANUFACTURER_ID:
1274678d674SMinkyu Kang 		return readl(&onenand->reg->manufact_id);
1284678d674SMinkyu Kang 	case ONENAND_REG_DEVICE_ID:
1294678d674SMinkyu Kang 		return readl(&onenand->reg->device_id);
1304678d674SMinkyu Kang 	case ONENAND_REG_VERSION_ID:
1314678d674SMinkyu Kang 		return readl(&onenand->reg->flash_ver_id);
1324678d674SMinkyu Kang 	case ONENAND_REG_DATA_BUFFER_SIZE:
1334678d674SMinkyu Kang 		return readl(&onenand->reg->data_buf_size);
1344678d674SMinkyu Kang 	case ONENAND_REG_TECHNOLOGY:
1354678d674SMinkyu Kang 		return readl(&onenand->reg->tech);
1364678d674SMinkyu Kang 	case ONENAND_REG_SYS_CFG1:
1374678d674SMinkyu Kang 		return readl(&onenand->reg->mem_cfg);
1384678d674SMinkyu Kang 
1394678d674SMinkyu Kang 	/* Used at unlock all status */
1404678d674SMinkyu Kang 	case ONENAND_REG_CTRL_STATUS:
1414678d674SMinkyu Kang 		return 0;
1424678d674SMinkyu Kang 
1434678d674SMinkyu Kang 	case ONENAND_REG_WP_STATUS:
1444678d674SMinkyu Kang 		return ONENAND_WP_US;
1454678d674SMinkyu Kang 
1464678d674SMinkyu Kang 	default:
1474678d674SMinkyu Kang 		break;
1484678d674SMinkyu Kang 	}
1494678d674SMinkyu Kang 
1504678d674SMinkyu Kang 	/* BootRAM access control */
1514678d674SMinkyu Kang 	if (reg < ONENAND_DATARAM && onenand->bootram_command) {
1524678d674SMinkyu Kang 		if (word_addr == 0)
1534678d674SMinkyu Kang 			return readl(&onenand->reg->manufact_id);
1544678d674SMinkyu Kang 		if (word_addr == 1)
1554678d674SMinkyu Kang 			return readl(&onenand->reg->device_id);
1564678d674SMinkyu Kang 		if (word_addr == 2)
1574678d674SMinkyu Kang 			return readl(&onenand->reg->flash_ver_id);
1584678d674SMinkyu Kang 	}
1594678d674SMinkyu Kang 
1604678d674SMinkyu Kang 	value = s3c_read_cmd(CMD_MAP_11(word_addr)) & 0xffff;
1614678d674SMinkyu Kang 	printk(KERN_INFO "s3c_onenand_readw:  Illegal access"
1624678d674SMinkyu Kang 		" at reg 0x%x, value 0x%x\n", word_addr, value);
1634678d674SMinkyu Kang 	return value;
1644678d674SMinkyu Kang }
1654678d674SMinkyu Kang 
s3c_onenand_writew(unsigned short value,void __iomem * addr)1664678d674SMinkyu Kang static void s3c_onenand_writew(unsigned short value, void __iomem *addr)
1674678d674SMinkyu Kang {
1684678d674SMinkyu Kang 	struct onenand_chip *this = onenand->mtd->priv;
1694678d674SMinkyu Kang 	int reg = addr - this->base;
1704678d674SMinkyu Kang 	int word_addr = reg >> 1;
1714678d674SMinkyu Kang 
1724678d674SMinkyu Kang 	/* It's used for probing time */
1734678d674SMinkyu Kang 	switch (reg) {
1744678d674SMinkyu Kang 	case ONENAND_REG_SYS_CFG1:
1754678d674SMinkyu Kang 		writel(value, &onenand->reg->mem_cfg);
1764678d674SMinkyu Kang 		return;
1774678d674SMinkyu Kang 
1784678d674SMinkyu Kang 	case ONENAND_REG_START_ADDRESS1:
1794678d674SMinkyu Kang 	case ONENAND_REG_START_ADDRESS2:
1804678d674SMinkyu Kang 		return;
1814678d674SMinkyu Kang 
1824678d674SMinkyu Kang 	/* Lock/lock-tight/unlock/unlock_all */
1834678d674SMinkyu Kang 	case ONENAND_REG_START_BLOCK_ADDRESS:
1844678d674SMinkyu Kang 		return;
1854678d674SMinkyu Kang 
1864678d674SMinkyu Kang 	default:
1874678d674SMinkyu Kang 		break;
1884678d674SMinkyu Kang 	}
1894678d674SMinkyu Kang 
1904678d674SMinkyu Kang 	/* BootRAM access control */
1914678d674SMinkyu Kang 	if (reg < ONENAND_DATARAM) {
1924678d674SMinkyu Kang 		if (value == ONENAND_CMD_READID) {
1934678d674SMinkyu Kang 			onenand->bootram_command = 1;
1944678d674SMinkyu Kang 			return;
1954678d674SMinkyu Kang 		}
1964678d674SMinkyu Kang 		if (value == ONENAND_CMD_RESET) {
1974678d674SMinkyu Kang 			writel(ONENAND_MEM_RESET_COLD,
1984678d674SMinkyu Kang 					&onenand->reg->mem_reset);
1994678d674SMinkyu Kang 			onenand->bootram_command = 0;
2004678d674SMinkyu Kang 			return;
2014678d674SMinkyu Kang 		}
2024678d674SMinkyu Kang 	}
2034678d674SMinkyu Kang 
2044678d674SMinkyu Kang 	printk(KERN_INFO "s3c_onenand_writew: Illegal access"
2054678d674SMinkyu Kang 		" at reg 0x%x, value 0x%x\n", word_addr, value);
2064678d674SMinkyu Kang 
2074678d674SMinkyu Kang 	s3c_write_cmd(value, CMD_MAP_11(word_addr));
2084678d674SMinkyu Kang }
2094678d674SMinkyu Kang 
s3c_onenand_wait(struct mtd_info * mtd,int state)2104678d674SMinkyu Kang static int s3c_onenand_wait(struct mtd_info *mtd, int state)
2114678d674SMinkyu Kang {
2124678d674SMinkyu Kang 	unsigned int flags = INT_ACT;
2134678d674SMinkyu Kang 	unsigned int stat, ecc;
2144678d674SMinkyu Kang 	unsigned long timeout = 0x100000;
2154678d674SMinkyu Kang 
2164678d674SMinkyu Kang 	switch (state) {
2174678d674SMinkyu Kang 	case FL_READING:
2184678d674SMinkyu Kang 		flags |= BLK_RW_CMP | LOAD_CMP;
2194678d674SMinkyu Kang 		break;
2204678d674SMinkyu Kang 	case FL_WRITING:
2214678d674SMinkyu Kang 		flags |= BLK_RW_CMP | PGM_CMP;
2224678d674SMinkyu Kang 		break;
2234678d674SMinkyu Kang 	case FL_ERASING:
2244678d674SMinkyu Kang 		flags |= BLK_RW_CMP | ERS_CMP;
2254678d674SMinkyu Kang 		break;
2264678d674SMinkyu Kang 	case FL_LOCKING:
2274678d674SMinkyu Kang 		flags |= BLK_RW_CMP;
2284678d674SMinkyu Kang 		break;
2294678d674SMinkyu Kang 	default:
2304678d674SMinkyu Kang 		break;
2314678d674SMinkyu Kang 	}
2324678d674SMinkyu Kang 
2334678d674SMinkyu Kang 	while (timeout--) {
2344678d674SMinkyu Kang 		stat = readl(&onenand->reg->int_err_stat);
2354678d674SMinkyu Kang 		if (stat & flags)
2364678d674SMinkyu Kang 			break;
2374678d674SMinkyu Kang 	}
2384678d674SMinkyu Kang 
2394678d674SMinkyu Kang 	/* To get correct interrupt status in timeout case */
2404678d674SMinkyu Kang 	stat = readl(&onenand->reg->int_err_stat);
2414678d674SMinkyu Kang 	writel(stat, &onenand->reg->int_err_ack);
2424678d674SMinkyu Kang 
2434678d674SMinkyu Kang 	/*
2444678d674SMinkyu Kang 	 * In the Spec. it checks the controller status first
2454678d674SMinkyu Kang 	 * However if you get the correct information in case of
2464678d674SMinkyu Kang 	 * power off recovery (POR) test, it should read ECC status first
2474678d674SMinkyu Kang 	 */
2484678d674SMinkyu Kang 	if (stat & LOAD_CMP) {
2494678d674SMinkyu Kang 		ecc = readl(&onenand->reg->ecc_err_stat);
2504678d674SMinkyu Kang 		if (ecc & ONENAND_ECC_4BIT_UNCORRECTABLE) {
2514678d674SMinkyu Kang 			printk(KERN_INFO "%s: ECC error = 0x%04x\n",
2524678d674SMinkyu Kang 					__func__, ecc);
2534678d674SMinkyu Kang 			mtd->ecc_stats.failed++;
2544678d674SMinkyu Kang 			return -EBADMSG;
2554678d674SMinkyu Kang 		}
2564678d674SMinkyu Kang 	}
2574678d674SMinkyu Kang 
2584678d674SMinkyu Kang 	if (stat & (LOCKED_BLK | ERS_FAIL | PGM_FAIL | LD_FAIL_ECC_ERR)) {
2594678d674SMinkyu Kang 		printk(KERN_INFO "%s: controller error = 0x%04x\n",
2604678d674SMinkyu Kang 				__func__, stat);
2614678d674SMinkyu Kang 		if (stat & LOCKED_BLK)
2624678d674SMinkyu Kang 			printk(KERN_INFO "%s: it's locked error = 0x%04x\n",
2634678d674SMinkyu Kang 					__func__, stat);
2644678d674SMinkyu Kang 
2654678d674SMinkyu Kang 		return -EIO;
2664678d674SMinkyu Kang 	}
2674678d674SMinkyu Kang 
2684678d674SMinkyu Kang 	return 0;
2694678d674SMinkyu Kang }
2704678d674SMinkyu Kang 
s3c_onenand_command(struct mtd_info * mtd,int cmd,loff_t addr,size_t len)2714678d674SMinkyu Kang static int s3c_onenand_command(struct mtd_info *mtd, int cmd,
2724678d674SMinkyu Kang 		loff_t addr, size_t len)
2734678d674SMinkyu Kang {
2744678d674SMinkyu Kang 	struct onenand_chip *this = mtd->priv;
2754678d674SMinkyu Kang 	unsigned int *m, *s;
2764678d674SMinkyu Kang 	int fba, fpa, fsa = 0;
2774678d674SMinkyu Kang 	unsigned int mem_addr;
2784678d674SMinkyu Kang 	int i, mcount, scount;
2794678d674SMinkyu Kang 	int index;
2804678d674SMinkyu Kang 
2814678d674SMinkyu Kang 	fba = (int) (addr >> this->erase_shift);
2824678d674SMinkyu Kang 	fpa = (int) (addr >> this->page_shift);
2834678d674SMinkyu Kang 	fpa &= this->page_mask;
2844678d674SMinkyu Kang 
2854678d674SMinkyu Kang 	mem_addr = onenand->mem_addr(fba, fpa, fsa);
2864678d674SMinkyu Kang 
2874678d674SMinkyu Kang 	switch (cmd) {
2884678d674SMinkyu Kang 	case ONENAND_CMD_READ:
2894678d674SMinkyu Kang 	case ONENAND_CMD_READOOB:
2904678d674SMinkyu Kang 	case ONENAND_CMD_BUFFERRAM:
2914678d674SMinkyu Kang 		ONENAND_SET_NEXT_BUFFERRAM(this);
2924678d674SMinkyu Kang 	default:
2934678d674SMinkyu Kang 		break;
2944678d674SMinkyu Kang 	}
2954678d674SMinkyu Kang 
2964678d674SMinkyu Kang 	index = ONENAND_CURRENT_BUFFERRAM(this);
2974678d674SMinkyu Kang 
2984678d674SMinkyu Kang 	/*
2994678d674SMinkyu Kang 	 * Emulate Two BufferRAMs and access with 4 bytes pointer
3004678d674SMinkyu Kang 	 */
3014678d674SMinkyu Kang 	m = (unsigned int *) onenand->page_buf;
3024678d674SMinkyu Kang 	s = (unsigned int *) onenand->oob_buf;
3034678d674SMinkyu Kang 
3044678d674SMinkyu Kang 	if (index) {
3054678d674SMinkyu Kang 		m += (this->writesize >> 2);
3064678d674SMinkyu Kang 		s += (mtd->oobsize >> 2);
3074678d674SMinkyu Kang 	}
3084678d674SMinkyu Kang 
3094678d674SMinkyu Kang 	mcount = mtd->writesize >> 2;
3104678d674SMinkyu Kang 	scount = mtd->oobsize >> 2;
3114678d674SMinkyu Kang 
3124678d674SMinkyu Kang 	switch (cmd) {
3134678d674SMinkyu Kang 	case ONENAND_CMD_READ:
3144678d674SMinkyu Kang 		/* Main */
3154678d674SMinkyu Kang 		for (i = 0; i < mcount; i++)
3164678d674SMinkyu Kang 			*m++ = s3c_read_cmd(CMD_MAP_01(mem_addr));
3174678d674SMinkyu Kang 		return 0;
3184678d674SMinkyu Kang 
3194678d674SMinkyu Kang 	case ONENAND_CMD_READOOB:
3204678d674SMinkyu Kang 		writel(TSRF, &onenand->reg->trans_spare);
3214678d674SMinkyu Kang 		/* Main */
3224678d674SMinkyu Kang 		for (i = 0; i < mcount; i++)
3234678d674SMinkyu Kang 			*m++ = s3c_read_cmd(CMD_MAP_01(mem_addr));
3244678d674SMinkyu Kang 
3254678d674SMinkyu Kang 		/* Spare */
3264678d674SMinkyu Kang 		for (i = 0; i < scount; i++)
3274678d674SMinkyu Kang 			*s++ = s3c_read_cmd(CMD_MAP_01(mem_addr));
3284678d674SMinkyu Kang 
3294678d674SMinkyu Kang 		writel(0, &onenand->reg->trans_spare);
3304678d674SMinkyu Kang 		return 0;
3314678d674SMinkyu Kang 
3324678d674SMinkyu Kang 	case ONENAND_CMD_PROG:
3334678d674SMinkyu Kang 		/* Main */
3344678d674SMinkyu Kang 		for (i = 0; i < mcount; i++)
3354678d674SMinkyu Kang 			s3c_write_cmd(*m++, CMD_MAP_01(mem_addr));
3364678d674SMinkyu Kang 		return 0;
3374678d674SMinkyu Kang 
3384678d674SMinkyu Kang 	case ONENAND_CMD_PROGOOB:
3394678d674SMinkyu Kang 		writel(TSRF, &onenand->reg->trans_spare);
3404678d674SMinkyu Kang 
3414678d674SMinkyu Kang 		/* Main - dummy write */
3424678d674SMinkyu Kang 		for (i = 0; i < mcount; i++)
3434678d674SMinkyu Kang 			s3c_write_cmd(0xffffffff, CMD_MAP_01(mem_addr));
3444678d674SMinkyu Kang 
3454678d674SMinkyu Kang 		/* Spare */
3464678d674SMinkyu Kang 		for (i = 0; i < scount; i++)
3474678d674SMinkyu Kang 			s3c_write_cmd(*s++, CMD_MAP_01(mem_addr));
3484678d674SMinkyu Kang 
3494678d674SMinkyu Kang 		writel(0, &onenand->reg->trans_spare);
3504678d674SMinkyu Kang 		return 0;
3514678d674SMinkyu Kang 
3524678d674SMinkyu Kang 	case ONENAND_CMD_UNLOCK_ALL:
3534678d674SMinkyu Kang 		s3c_write_cmd(ONENAND_UNLOCK_ALL, CMD_MAP_10(mem_addr));
3544678d674SMinkyu Kang 		return 0;
3554678d674SMinkyu Kang 
3564678d674SMinkyu Kang 	case ONENAND_CMD_ERASE:
3574678d674SMinkyu Kang 		s3c_write_cmd(ONENAND_ERASE_START, CMD_MAP_10(mem_addr));
3584678d674SMinkyu Kang 		return 0;
3594678d674SMinkyu Kang 
3604678d674SMinkyu Kang 	case ONENAND_CMD_MULTIBLOCK_ERASE:
3614678d674SMinkyu Kang 		s3c_write_cmd(ONENAND_MULTI_ERASE_SET, CMD_MAP_10(mem_addr));
3624678d674SMinkyu Kang 		return 0;
3634678d674SMinkyu Kang 
3644678d674SMinkyu Kang 	case ONENAND_CMD_ERASE_VERIFY:
3654678d674SMinkyu Kang 		s3c_write_cmd(ONENAND_ERASE_VERIFY, CMD_MAP_10(mem_addr));
3664678d674SMinkyu Kang 		return 0;
3674678d674SMinkyu Kang 
3684678d674SMinkyu Kang 	default:
3694678d674SMinkyu Kang 		break;
3704678d674SMinkyu Kang 	}
3714678d674SMinkyu Kang 
3724678d674SMinkyu Kang 	return 0;
3734678d674SMinkyu Kang }
3744678d674SMinkyu Kang 
s3c_get_bufferram(struct mtd_info * mtd,int area)3754678d674SMinkyu Kang static unsigned char *s3c_get_bufferram(struct mtd_info *mtd, int area)
3764678d674SMinkyu Kang {
3774678d674SMinkyu Kang 	struct onenand_chip *this = mtd->priv;
3784678d674SMinkyu Kang 	int index = ONENAND_CURRENT_BUFFERRAM(this);
3794678d674SMinkyu Kang 	unsigned char *p;
3804678d674SMinkyu Kang 
3814678d674SMinkyu Kang 	if (area == ONENAND_DATARAM) {
3824678d674SMinkyu Kang 		p = (unsigned char *) onenand->page_buf;
3834678d674SMinkyu Kang 		if (index == 1)
3844678d674SMinkyu Kang 			p += this->writesize;
3854678d674SMinkyu Kang 	} else {
3864678d674SMinkyu Kang 		p = (unsigned char *) onenand->oob_buf;
3874678d674SMinkyu Kang 		if (index == 1)
3884678d674SMinkyu Kang 			p += mtd->oobsize;
3894678d674SMinkyu Kang 	}
3904678d674SMinkyu Kang 
3914678d674SMinkyu Kang 	return p;
3924678d674SMinkyu Kang }
3934678d674SMinkyu Kang 
onenand_read_bufferram(struct mtd_info * mtd,loff_t addr,int area,unsigned char * buffer,int offset,size_t count)3944678d674SMinkyu Kang static int onenand_read_bufferram(struct mtd_info *mtd, loff_t addr, int area,
3954678d674SMinkyu Kang 				  unsigned char *buffer, int offset,
3964678d674SMinkyu Kang 				  size_t count)
3974678d674SMinkyu Kang {
3984678d674SMinkyu Kang 	unsigned char *p;
3994678d674SMinkyu Kang 
4004678d674SMinkyu Kang 	p = s3c_get_bufferram(mtd, area);
4014678d674SMinkyu Kang 	memcpy(buffer, p + offset, count);
4024678d674SMinkyu Kang 	return 0;
4034678d674SMinkyu Kang }
4044678d674SMinkyu Kang 
onenand_write_bufferram(struct mtd_info * mtd,loff_t addr,int area,const unsigned char * buffer,int offset,size_t count)4054678d674SMinkyu Kang static int onenand_write_bufferram(struct mtd_info *mtd, loff_t addr, int area,
4064678d674SMinkyu Kang 				   const unsigned char *buffer, int offset,
4074678d674SMinkyu Kang 				   size_t count)
4084678d674SMinkyu Kang {
4094678d674SMinkyu Kang 	unsigned char *p;
4104678d674SMinkyu Kang 
4114678d674SMinkyu Kang 	p = s3c_get_bufferram(mtd, area);
4124678d674SMinkyu Kang 	memcpy(p + offset, buffer, count);
4134678d674SMinkyu Kang 	return 0;
4144678d674SMinkyu Kang }
4154678d674SMinkyu Kang 
s3c_onenand_bbt_wait(struct mtd_info * mtd,int state)4164678d674SMinkyu Kang static int s3c_onenand_bbt_wait(struct mtd_info *mtd, int state)
4174678d674SMinkyu Kang {
4184678d674SMinkyu Kang 	struct samsung_onenand *reg = (struct samsung_onenand *)onenand->base;
4194678d674SMinkyu Kang 	unsigned int flags = INT_ACT | LOAD_CMP;
4204678d674SMinkyu Kang 	unsigned int stat;
4214678d674SMinkyu Kang 	unsigned long timeout = 0x10000;
4224678d674SMinkyu Kang 
4234678d674SMinkyu Kang 	while (timeout--) {
4244678d674SMinkyu Kang 		stat = readl(&reg->int_err_stat);
4254678d674SMinkyu Kang 		if (stat & flags)
4264678d674SMinkyu Kang 			break;
4274678d674SMinkyu Kang 	}
4284678d674SMinkyu Kang 	/* To get correct interrupt status in timeout case */
4294678d674SMinkyu Kang 	stat = readl(&onenand->reg->int_err_stat);
4304678d674SMinkyu Kang 	writel(stat, &onenand->reg->int_err_ack);
4314678d674SMinkyu Kang 
4324678d674SMinkyu Kang 	if (stat & LD_FAIL_ECC_ERR) {
4334678d674SMinkyu Kang 		s3c_onenand_reset();
4344678d674SMinkyu Kang 		return ONENAND_BBT_READ_ERROR;
4354678d674SMinkyu Kang 	}
4364678d674SMinkyu Kang 
4374678d674SMinkyu Kang 	if (stat & LOAD_CMP) {
4384678d674SMinkyu Kang 		int ecc = readl(&onenand->reg->ecc_err_stat);
4394678d674SMinkyu Kang 		if (ecc & ONENAND_ECC_4BIT_UNCORRECTABLE) {
4404678d674SMinkyu Kang 			s3c_onenand_reset();
4414678d674SMinkyu Kang 			return ONENAND_BBT_READ_ERROR;
4424678d674SMinkyu Kang 		}
4434678d674SMinkyu Kang 	}
4444678d674SMinkyu Kang 
4454678d674SMinkyu Kang 	return 0;
4464678d674SMinkyu Kang }
4474678d674SMinkyu Kang 
s3c_onenand_check_lock_status(struct mtd_info * mtd)4484678d674SMinkyu Kang static void s3c_onenand_check_lock_status(struct mtd_info *mtd)
4494678d674SMinkyu Kang {
4504678d674SMinkyu Kang 	struct onenand_chip *this = mtd->priv;
4514678d674SMinkyu Kang 	unsigned int block, end;
4524678d674SMinkyu Kang 
4534678d674SMinkyu Kang 	end = this->chipsize >> this->erase_shift;
4544678d674SMinkyu Kang 
4554678d674SMinkyu Kang 	for (block = 0; block < end; block++) {
45667fad9f6SAnatolij Gustschin 		s3c_read_cmd(CMD_MAP_01(onenand->mem_addr(block, 0, 0)));
4574678d674SMinkyu Kang 
4584678d674SMinkyu Kang 		if (readl(&onenand->reg->int_err_stat) & LOCKED_BLK) {
4594678d674SMinkyu Kang 			printf("block %d is write-protected!\n", block);
4604678d674SMinkyu Kang 			writel(LOCKED_BLK, &onenand->reg->int_err_ack);
4614678d674SMinkyu Kang 		}
4624678d674SMinkyu Kang 	}
4634678d674SMinkyu Kang }
4644678d674SMinkyu Kang 
s3c_onenand_do_lock_cmd(struct mtd_info * mtd,loff_t ofs,size_t len,int cmd)4654678d674SMinkyu Kang static void s3c_onenand_do_lock_cmd(struct mtd_info *mtd, loff_t ofs,
4664678d674SMinkyu Kang 		size_t len, int cmd)
4674678d674SMinkyu Kang {
4684678d674SMinkyu Kang 	struct onenand_chip *this = mtd->priv;
4694678d674SMinkyu Kang 	int start, end, start_mem_addr, end_mem_addr;
4704678d674SMinkyu Kang 
4714678d674SMinkyu Kang 	start = ofs >> this->erase_shift;
4724678d674SMinkyu Kang 	start_mem_addr = onenand->mem_addr(start, 0, 0);
4734678d674SMinkyu Kang 	end = start + (len >> this->erase_shift) - 1;
4744678d674SMinkyu Kang 	end_mem_addr = onenand->mem_addr(end, 0, 0);
4754678d674SMinkyu Kang 
4764678d674SMinkyu Kang 	if (cmd == ONENAND_CMD_LOCK) {
4774678d674SMinkyu Kang 		s3c_write_cmd(ONENAND_LOCK_START, CMD_MAP_10(start_mem_addr));
4784678d674SMinkyu Kang 		s3c_write_cmd(ONENAND_LOCK_END, CMD_MAP_10(end_mem_addr));
4794678d674SMinkyu Kang 	} else {
4804678d674SMinkyu Kang 		s3c_write_cmd(ONENAND_UNLOCK_START, CMD_MAP_10(start_mem_addr));
4814678d674SMinkyu Kang 		s3c_write_cmd(ONENAND_UNLOCK_END, CMD_MAP_10(end_mem_addr));
4824678d674SMinkyu Kang 	}
4834678d674SMinkyu Kang 
4844678d674SMinkyu Kang 	this->wait(mtd, FL_LOCKING);
4854678d674SMinkyu Kang }
4864678d674SMinkyu Kang 
s3c_onenand_unlock_all(struct mtd_info * mtd)4874678d674SMinkyu Kang static void s3c_onenand_unlock_all(struct mtd_info *mtd)
4884678d674SMinkyu Kang {
4894678d674SMinkyu Kang 	struct onenand_chip *this = mtd->priv;
4904678d674SMinkyu Kang 	loff_t ofs = 0;
4914678d674SMinkyu Kang 	size_t len = this->chipsize;
4924678d674SMinkyu Kang 
4934678d674SMinkyu Kang 	/* FIXME workaround */
4944678d674SMinkyu Kang 	this->subpagesize = mtd->writesize;
4954678d674SMinkyu Kang 	mtd->subpage_sft = 0;
4964678d674SMinkyu Kang 
4974678d674SMinkyu Kang 	if (this->options & ONENAND_HAS_UNLOCK_ALL) {
4984678d674SMinkyu Kang 		/* Write unlock command */
4994678d674SMinkyu Kang 		this->command(mtd, ONENAND_CMD_UNLOCK_ALL, 0, 0);
5004678d674SMinkyu Kang 
5014678d674SMinkyu Kang 		/* No need to check return value */
5024678d674SMinkyu Kang 		this->wait(mtd, FL_LOCKING);
5034678d674SMinkyu Kang 
5044678d674SMinkyu Kang 		/* Workaround for all block unlock in DDP */
5054678d674SMinkyu Kang 		if (!ONENAND_IS_DDP(this)) {
5064678d674SMinkyu Kang 			s3c_onenand_check_lock_status(mtd);
5074678d674SMinkyu Kang 			return;
5084678d674SMinkyu Kang 		}
5094678d674SMinkyu Kang 
5104678d674SMinkyu Kang 		/* All blocks on another chip */
5114678d674SMinkyu Kang 		ofs = this->chipsize >> 1;
5124678d674SMinkyu Kang 		len = this->chipsize >> 1;
5134678d674SMinkyu Kang 	}
5144678d674SMinkyu Kang 
5154678d674SMinkyu Kang 	s3c_onenand_do_lock_cmd(mtd, ofs, len, ONENAND_CMD_UNLOCK);
5164678d674SMinkyu Kang 	s3c_onenand_check_lock_status(mtd);
5174678d674SMinkyu Kang }
5184678d674SMinkyu Kang 
s5pc110_chip_probe(struct mtd_info * mtd)5196b3967bbSLukasz Majewski int s5pc110_chip_probe(struct mtd_info *mtd)
5206b3967bbSLukasz Majewski {
5216b3967bbSLukasz Majewski 	return 0;
5226b3967bbSLukasz Majewski }
5236b3967bbSLukasz Majewski 
s5pc210_chip_probe(struct mtd_info * mtd)5246b3967bbSLukasz Majewski int s5pc210_chip_probe(struct mtd_info *mtd)
5256b3967bbSLukasz Majewski {
5266b3967bbSLukasz Majewski 	return 0;
5276b3967bbSLukasz Majewski }
5286b3967bbSLukasz Majewski 
s3c_onenand_init(struct mtd_info * mtd)5294678d674SMinkyu Kang void s3c_onenand_init(struct mtd_info *mtd)
5304678d674SMinkyu Kang {
5314678d674SMinkyu Kang 	struct onenand_chip *this = mtd->priv;
5324678d674SMinkyu Kang 	u32 size = (4 << 10);	/* 4 KiB */
5334678d674SMinkyu Kang 
5344678d674SMinkyu Kang 	onenand = malloc(sizeof(struct s3c_onenand));
5354678d674SMinkyu Kang 	if (!onenand)
5364678d674SMinkyu Kang 		return;
5374678d674SMinkyu Kang 
5384678d674SMinkyu Kang 	onenand->page_buf = malloc(size * sizeof(char));
5394678d674SMinkyu Kang 	if (!onenand->page_buf)
5404678d674SMinkyu Kang 		return;
5414678d674SMinkyu Kang 	memset(onenand->page_buf, 0xff, size);
5424678d674SMinkyu Kang 
5434678d674SMinkyu Kang 	onenand->oob_buf = malloc(128 * sizeof(char));
5444678d674SMinkyu Kang 	if (!onenand->oob_buf)
5454678d674SMinkyu Kang 		return;
5464678d674SMinkyu Kang 	memset(onenand->oob_buf, 0xff, 128);
5474678d674SMinkyu Kang 
5484678d674SMinkyu Kang 	onenand->mtd = mtd;
5494678d674SMinkyu Kang 
550e5323225SBenoît Thébaudeau #if defined(CONFIG_S5P)
5514678d674SMinkyu Kang 	onenand->base = (void *)0xE7100000;
5524678d674SMinkyu Kang 	onenand->ahb_addr = (void *)0xB0000000;
5534678d674SMinkyu Kang #endif
5544678d674SMinkyu Kang 	onenand->mem_addr = s3c_mem_addr;
5554678d674SMinkyu Kang 	onenand->reg = (struct samsung_onenand *)onenand->base;
5564678d674SMinkyu Kang 
5574678d674SMinkyu Kang 	this->read_word = s3c_onenand_readw;
5584678d674SMinkyu Kang 	this->write_word = s3c_onenand_writew;
5594678d674SMinkyu Kang 
5604678d674SMinkyu Kang 	this->wait = s3c_onenand_wait;
5614678d674SMinkyu Kang 	this->bbt_wait = s3c_onenand_bbt_wait;
5624678d674SMinkyu Kang 	this->unlock_all = s3c_onenand_unlock_all;
5634678d674SMinkyu Kang 	this->command = s3c_onenand_command;
5644678d674SMinkyu Kang 
5654678d674SMinkyu Kang 	this->read_bufferram = onenand_read_bufferram;
5664678d674SMinkyu Kang 	this->write_bufferram = onenand_write_bufferram;
5674678d674SMinkyu Kang 
5684678d674SMinkyu Kang 	this->options |= ONENAND_RUNTIME_BADBLOCK_CHECK;
5694678d674SMinkyu Kang }
570