From 0f2d987d0796c0d5321c0fccee767f2b911e4be7 Mon Sep 17 00:00:00 2001 From: Minkyu Kang Date: Thu, 21 May 2009 16:14:25 +0900 Subject: [PATCH] [S5PC100] onenand working Signed-off-by: Minkyu Kang --- board/samsung/tt/Makefile | 2 +- board/samsung/tt/onenand.c | 67 +++++++ board/samsung/tt/tt.c | 5 +- drivers/mtd/onenand/Makefile | 2 + drivers/mtd/onenand/s3c-onenand.c | 403 ++++++++++++++++++++++++++++++++++++++ include/configs/s5pc100_tt.h | 1 + include/s5pc1xx-onenand.h | 75 +++++++ 7 files changed, 552 insertions(+), 3 deletions(-) create mode 100644 board/samsung/tt/onenand.c create mode 100644 drivers/mtd/onenand/s3c-onenand.c create mode 100644 include/s5pc1xx-onenand.h diff --git a/board/samsung/tt/Makefile b/board/samsung/tt/Makefile index 01277c2..5a1e1c0 100644 --- a/board/samsung/tt/Makefile +++ b/board/samsung/tt/Makefile @@ -28,7 +28,7 @@ include $(TOPDIR)/config.mk LIB = $(obj)lib$(BOARD).a -COBJS-y := tt.o +COBJS-y := tt.o onenand.o SOBJS := lowlevel_init.o SRCS := $(SOBJS:.o=.S) $(COBJS-y:.o=.c) diff --git a/board/samsung/tt/onenand.c b/board/samsung/tt/onenand.c new file mode 100644 index 0000000..230d8be --- /dev/null +++ b/board/samsung/tt/onenand.c @@ -0,0 +1,67 @@ +/* + * OneNAND initialization at U-Boot + */ + +#include +#include +#include +#include + +#include + +#include +#include + +#include + +#define DPRINTK(format, args...) \ +do { \ + printk("%s[%d]: " format "\n", __func__, __LINE__, ##args); \ +} while (0) + +void onenand_board_init(struct mtd_info *mtd) +{ + struct onenand_chip *this = mtd->priv; + int value; + + this->base = (void *)CONFIG_SYS_ONENAND_BASE; + + s3c_onenand_init(mtd); + + value = CLK_DIV0_REG; + value &= ~(3 << 16); + value |= (1 << 16); + CLK_DIV0_REG = value; + + INT_ERR_MASK0_REG = 0x17ff; + INT_PIN_ENABLE0_REG = (1 << 0); /* Enable */ + INT_ERR_MASK0_REG &= ~(1 << 11); /* ONENAND_INT_ERR_RDY_ACT */ + + MEM_RESET0_REG = ONENAND_MEM_RESET_COLD; + + while (!(INT_ERR_STAT0_REG & (RST_CMP|INT_ACT))) + continue; + + INT_ERR_ACK0_REG |= RST_CMP | INT_ACT; + + ACC_CLOCK0_REG = 0x2; + + /* MEM_CFG0_REG = 0x46e0; */ + MEM_CFG0_REG = +#ifdef CONFIG_SYNC_MODE + ONENAND_SYS_CFG1_SYNC_READ | +#endif + ONENAND_SYS_CFG1_BRL_4 | + ONENAND_SYS_CFG1_BL_16 | + ONENAND_SYS_CFG1_RDY | + ONENAND_SYS_CFG1_INT | + ONENAND_SYS_CFG1_IOBE + ; + + BURST_LEN0_REG = 16; + + /* Disable watchdog */ + FLASH_AUX_CNTRL0_REG = 1; + + s3c_set_width_regs(this); +} diff --git a/board/samsung/tt/tt.c b/board/samsung/tt/tt.c index ba29235..105d997 100644 --- a/board/samsung/tt/tt.c +++ b/board/samsung/tt/tt.c @@ -30,7 +30,6 @@ #include #include -#include static inline void delay(unsigned long loops) { @@ -95,6 +94,8 @@ void nand_init(void) } #endif +#ifndef CONFIG_SYS_NO_FLASH +#include ulong board_flash_get_legacy (ulong base, int banknum, flash_info_t *info) { if (banknum == 0) { /* non-CFI boot flash */ @@ -105,4 +106,4 @@ ulong board_flash_get_legacy (ulong base, int banknum, flash_info_t *info) } else return 0; } - +#endif diff --git a/drivers/mtd/onenand/Makefile b/drivers/mtd/onenand/Makefile index 1d35a57..9013a46 100644 --- a/drivers/mtd/onenand/Makefile +++ b/drivers/mtd/onenand/Makefile @@ -26,6 +26,8 @@ include $(TOPDIR)/config.mk LIB := $(obj)libonenand.a COBJS-$(CONFIG_CMD_ONENAND) := onenand_uboot.o onenand_base.o onenand_bbt.o +COBJS-$(CONFIG_S3C64XX) += s3c-onenand.o +COBJS-$(CONFIG_S5PC1XX) += s3c-onenand.o COBJS := $(COBJS-y) SRCS := $(COBJS:.o=.c) diff --git a/drivers/mtd/onenand/s3c-onenand.c b/drivers/mtd/onenand/s3c-onenand.c new file mode 100644 index 0000000..92ccd96 --- /dev/null +++ b/drivers/mtd/onenand/s3c-onenand.c @@ -0,0 +1,403 @@ +/* + * OneNAND initialization at U-Boot + */ + +#include +#include +#include +#include + +#if defined(CONFIG_S3C64XX) +#include +#include +#elif defined(CONFIG_S5PC1XX) +#include +#include +#endif + +#include +#include + +#if 0 +#define DPRINTK(format, args...) \ +do { \ + printk("%s[%d]: " format "\n", __func__, __LINE__, ##args); \ +} while (0) +#else +#define DPRINTK(...) do { } while (0) +#endif + +/* b0010000 << 26 */ +#define AHB_ADDR 0x20000000 + +#define ONENAND_ERASE_STATUS 0x00 +#define ONENAND_MULTI_ERASE_SET 0x01 +#define ONENAND_ERASE_START 0x03 +#define ONENAND_ERASE_VERIFY 0x15 + +#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 MAP_00 (0x0 << 24) +#define MAP_01 (0x1 << 24) +#define MAP_10 (0x2 << 24) +#define MAP_11 (0x3 << 24) + +#define MEM_ADDR(fba, fpa, fsa) (((fba) << 12 | (fpa) << 6 | (fsa) << 4) & 0xffffff) + +#define GET_FBA(mem_addr) ((mem_addr) & 0x3ff000) + +/* The 'addr' is byte address. It makes a 16-bit word */ +#define CMD_MAP_00(addr) (AHB_ADDR | MAP_00 | ((addr) << 1)) +#define CMD_MAP_01(mem_addr) (AHB_ADDR | MAP_01 | (mem_addr)) +#define CMD_MAP_10(mem_addr) (AHB_ADDR | MAP_10 | (mem_addr)) +#define CMD_MAP_11(addr) (AHB_ADDR | MAP_11 | ((addr) << 2)) + +struct s3c_onenand { + struct mtd_info *mtd; + + int bootram_command; + unsigned char oobbuf[64]; +}; + +static struct s3c_onenand onenand; + +static void s3c_onenand_reset(void) +{ + MEM_RESET0_REG = ONENAND_MEM_RESET_COLD; + while (!(INT_ERR_STAT0_REG & INT_ACT)) + ; + /* Clear interrupt */ + INT_ERR_ACK0_REG = 0x0; + /* Clear the ECC status */ + ECC_ERR_STAT0_REG = 0x0; +} + +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; + + /* It's used for probing time */ + switch (reg) { + case ONENAND_REG_MANUFACTURER_ID: + return MANUFACT_ID0_REG; + case ONENAND_REG_DEVICE_ID: + return DEVICE_ID0_REG; + case ONENAND_REG_VERSION_ID: + return FLASH_VER_ID0_REG; + case ONENAND_REG_DATA_BUFFER_SIZE: + return DATA_BUF_SIZE0_REG; + case ONENAND_REG_SYS_CFG1: + return MEM_CFG0_REG; + + default: + break; + } + + /* BootRAM access control */ + if ((unsigned int) addr < ONENAND_DATARAM && onenand.bootram_command) { + if (word_addr == 0) + return MANUFACT_ID0_REG; + if (word_addr == 1) + return DEVICE_ID0_REG; + if (word_addr == 2) + return FLASH_VER_ID0_REG; + } + + DPRINTK("illegal reg 0x%x, -> 0x%x 0x%x", word_addr, readl(CMD_MAP_11(word_addr)), (unsigned int) INT_ERR_STAT0_REG); + return readl(CMD_MAP_11(word_addr)) & 0xffff; +} + +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: + MEM_CFG0_REG = value; + return; + + /* Lock/lock-tight/unlock/unlock_all */ + case ONENAND_REG_START_BLOCK_ADDRESS: + return; + + default: + break; + } + + /* BootRAM access control */ + if ((unsigned int) addr < ONENAND_DATARAM) { + if (value == ONENAND_CMD_READID) { + onenand.bootram_command = 1; + return; + } + if (value == ONENAND_CMD_RESET) { + MEM_RESET0_REG = ONENAND_MEM_RESET_COLD; + onenand.bootram_command = 0; + return; + } + } + + printf("%s[%d] illegal reg 0x%x, value 0x%x\n", __func__, __LINE__, word_addr, value); + writel(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; + + while (1) { + stat = INT_ERR_STAT0_REG; + if (stat & flags) + break; + } + + INT_ERR_ACK0_REG = stat; + + if (stat & (LOCKED_BLK | ERS_FAIL | PGM_FAIL | INT_TO | LD_FAIL_ECC_ERR)) { + printk("onenand_wait: controller error = 0x%04x\n", stat); + if (stat & LOCKED_BLK) { + printk("onenand_wait: it's locked error = 0x%04x\n", stat); + } + + return -EIO; + } + + if (stat & LOAD_CMP) { + ecc = ECC_ERR_STAT0_REG; + if (ecc & ONENAND_ECC_2BIT_ALL) { + MTDDEBUG (MTD_DEBUG_LEVEL0, + "onenand_wait: ECC error = 0x%04x\n", ecc); + return -EBADMSG; + } + } + + 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; + unsigned char *cm, *cs; + int fba, fpa, fsa = 0; + int mem_addr; + int i, ret, count; + + fba = (int) (addr >> this->erase_shift); + fpa = (int) (addr >> this->page_shift); + fpa &= this->page_mask; + + mem_addr = MEM_ADDR(fba, fpa, fsa); + + if (cmd != ONENAND_CMD_READOOB) + DPRINTK("cmd 0x%x, addr 0x%x, fba %d, fpa %d, len 0x%x", cmd, (unsigned int) addr, fba, fpa, len); + + switch (cmd) { + case ONENAND_CMD_READ: + count = len >> 2; + if (len != mtd->writesize) + DPRINTK("length error"); + m = (unsigned int *) this->main_buf; + DPRINTK("read buffer 0x%x", (unsigned int) this->main_buf); + for (i = 0; i < count; i++) + *m++ = readl(CMD_MAP_01(mem_addr)); + return 0; + + case ONENAND_CMD_READOOB: + TRANS_SPARE0_REG = TSRF; + m = (unsigned int *) onenand.oobbuf; + count = mtd->writesize >> 2; + for (i = 0; i < count; i++) + *m = readl(CMD_MAP_01(mem_addr)); + memset(onenand.oobbuf, 0xff, mtd->oobsize); + s = (unsigned int *) onenand.oobbuf; + count = mtd->oobsize >> 2; + for (i = 0; i < count; i++) { + *s = readl(CMD_MAP_01(mem_addr)); + /* It's initial bad */ + if (i == 0 && ((*s & 0xffff) != 0xffff)) + break; + s++; + } + cm = (unsigned char *) this->spare_buf; + cs = (unsigned char *) onenand.oobbuf; + count = len; + for (i = 0; i < len; i++) + *cm++ = *cs++; + TRANS_SPARE0_REG = ~TSRF; + return 0; + + case ONENAND_CMD_PROG: +// TRANS_SPARE0_REG = TSRF; + m = (unsigned int *) this->main_buf; + if (len != mtd->writesize) + DPRINTK("length error"); + DPRINTK("write buffer 0x%x", (unsigned int) this->main_buf); + count = len >> 2; + for (i = 0; i < count; i++) + writel(*m++, CMD_MAP_01(mem_addr)); + /* FIXME how to write oob together */ +#if 0 + s = (unsigned int *) this->spare_buf; + count = mtd->oobsize >> 2; + for (i = 0; i < count; i++) + writel(*s++, CMD_MAP_01(mem_addr)); +#endif +// TRANS_SPARE0_REG = ~TSRF; + return 0; + + case ONENAND_CMD_PROGOOB: + TRANS_SPARE0_REG = TSRF; + count = mtd->writesize >> 2; + for (i = 0; i < count; i++) + writel(0xffffffff, CMD_MAP_01(mem_addr)); + + memset(onenand.oobbuf, 0xff, mtd->oobsize); + cm = (unsigned char *) onenand.oobbuf; + cs = (unsigned char *) this->spare_buf; + count = len; + for (i = 0; i < count; i++) + *cm++ = *cs++; + + s = (unsigned int *) onenand.oobbuf; + count = mtd->oobsize >> 2; + for (i = 0; i < count; i++) + writel(*s++, CMD_MAP_01(mem_addr)); + TRANS_SPARE0_REG = ~TSRF; + return 0; + + case ONENAND_CMD_UNLOCK_ALL: + writel(ONENAND_UNLOCK_ALL, CMD_MAP_10(mem_addr)); + return 0; + + case ONENAND_CMD_ERASE: + writel(ONENAND_ERASE_START, CMD_MAP_10(mem_addr)); + + ret = this->wait(mtd, FL_ERASING); + if (ret) + return ret; + writel(ONENAND_ERASE_VERIFY, CMD_MAP_10(mem_addr)); + return 0; + + default: + break; + } + + return 0; +} + +static int onenand_read_bufferram(struct mtd_info *mtd, loff_t addr, int area, + unsigned char *buffer, int offset, + size_t count) +{ + DPRINTK("addr 0x%x, 0x%x, 0x%x, %d, 0x%x", (unsigned int) addr, area, (unsigned int) buffer, 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) +{ + struct onenand_chip *this = mtd->priv; + + DPRINTK("addr 0x%x, 0x%x, 0x%x, %d, 0x%x", (unsigned int) addr, area, (unsigned int) buffer, offset, (unsigned int) count); + if (area == ONENAND_DATARAM) + this->main_buf = (unsigned char *) buffer; + else + this->spare_buf = (unsigned char *) buffer; + + return 0; +} + +static int s3c_onenand_bbt_wait(struct mtd_info *mtd, int state) +{ + unsigned int flags = INT_ACT; + unsigned int stat; + + while (1) { + stat = INT_ERR_STAT0_REG; + if (stat & flags) + break; + } + + INT_ERR_ACK0_REG = stat; + + if (stat & LD_FAIL_ECC_ERR) { + s3c_onenand_reset(); + return ONENAND_BBT_READ_ERROR; + } + + if (stat & LOAD_CMP) { + int ecc = ECC_ERR_STAT0_REG; + if (ecc & ONENAND_ECC_2BIT_ALL) { + s3c_onenand_reset(); + return ONENAND_BBT_READ_ERROR; + } + } else + return ONENAND_BBT_READ_FATAL_ERROR; + + return 0; +} + +static int s3c_onenand_read_spareram(struct mtd_info *mtd, loff_t addr, + int area, unsigned char *buf, int offset, size_t count) +{ + return 0; +} + +void s3c_set_width_regs(struct onenand_chip *this) +{ + int dev_id, ddp, density; + int dbs_dfs, fba, fpa, fsa; + + dev_id = DEVICE_ID0_REG; + + ddp = dev_id & ONENAND_DEVICE_IS_DDP; + density = (dev_id >> ONENAND_DEVICE_DENSITY_SHIFT) & 0xf; + + dbs_dfs = 0; + fba = density + 7; + fpa = 6; + fsa = 2; + + if (ddp) { + dbs_dfs = 1; + fba--; + } + + FBA_WIDTH0_REG = fba; + FPA_WIDTH0_REG = fpa; + FSA_WIDTH0_REG = fsa; + DBS_DFS_WIDTH0_REG = dbs_dfs; +} + +void s3c_onenand_init(struct mtd_info *mtd) +{ + struct onenand_chip *this = mtd->priv; + + onenand.mtd = mtd; + + 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->command = s3c_onenand_command; + + this->read_bufferram = onenand_read_bufferram; + this->read_spareram = s3c_onenand_read_spareram; + this->write_bufferram = onenand_write_bufferram; +} diff --git a/include/configs/s5pc100_tt.h b/include/configs/s5pc100_tt.h index 57ce1c7..07f04ce 100644 --- a/include/configs/s5pc100_tt.h +++ b/include/configs/s5pc100_tt.h @@ -40,6 +40,7 @@ #define CONFIG_ARMCORTEXA8 1 /* This is an ARM V7 CPU core */ #define CONFIG_SAMSUNG 1 /* in a SAMSUNG core */ #define CONFIG_S5PC100 1 /* which is in a S5PC100 */ +#define CONFIG_S5PC1XX 1 /* which is in a S5PC1XX Family */ #define CONFIG_S5PC100_TT 1 /* working with TT */ //#define CONFIG_S5PC1XX_I2C diff --git a/include/s5pc1xx-onenand.h b/include/s5pc1xx-onenand.h new file mode 100644 index 0000000..65d1997 --- /dev/null +++ b/include/s5pc1xx-onenand.h @@ -0,0 +1,75 @@ +#ifndef __S5PC1XX_ONENAND_H__ +#define __S5PC1XX_ONENAND_H__ + +#include + +/* + * OneNAND Controller + */ +#define S5P_ONENAND_BASE 0xE7100000 + +#define MEM_CFG0_OFFSET 0x0000 +#define BURST_LEN0_OFFSET 0x0010 +#define MEM_RESET0_OFFSET 0x0020 +#define INT_ERR_STAT0_OFFSET 0x0030 +#define INT_ERR_MASK0_OFFSET 0x0040 +#define INT_ERR_ACK0_OFFSET 0x0050 +#define ECC_ERR_STAT0_OFFSET 0x0060 +#define MANUFACT_ID0_OFFSET 0x0070 +#define DEVICE_ID0_OFFSET 0x0080 +#define DATA_BUF_SIZE0_OFFSET 0x0090 +#define BOOT_BUF_SIZE0_OFFSET 0x00A0 +#define BUF_AMOUNT0_OFFSET 0x00B0 +#define TECH0_OFFSET 0x00C0 +#define FBA_WIDTH0_OFFSET 0x00D0 +#define FPA_WIDTH0_OFFSET 0x00E0 +#define FSA_WIDTH0_OFFSET 0x00F0 +#define TRANS_SPARE0_OFFSET 0x0140 +#define DBS_DFS_WIDTH0_OFFSET 0x0160 +#define INT_PIN_ENABLE0_OFFSET 0x01A0 +#define ACC_CLOCK0_OFFSET 0x01C0 +#define FLASH_VER_ID0_OFFSET 0x01F0 +#define FLASH_AUX_CNTRL0_OFFSET 0x0300 + +#define MEM_CFG0_REG __REG(S5P_ONENAND_BASE + MEM_CFG0_OFFSET) +#define BURST_LEN0_REG __REG(S5P_ONENAND_BASE + BURST_LEN0_OFFSET) +#define MEM_RESET0_REG __REG(S5P_ONENAND_BASE + MEM_RESET0_OFFSET) +#define INT_ERR_STAT0_REG __REG(S5P_ONENAND_BASE + INT_ERR_STAT0_OFFSET) +#define INT_ERR_MASK0_REG __REG(S5P_ONENAND_BASE + INT_ERR_MASK0_OFFSET) +#define INT_ERR_ACK0_REG __REG(S5P_ONENAND_BASE + INT_ERR_ACK0_OFFSET) +#define ECC_ERR_STAT0_REG __REG(S5P_ONENAND_BASE + ECC_ERR_STAT0_OFFSET) +#define MANUFACT_ID0_REG __REG(S5P_ONENAND_BASE + MANUFACT_ID0_OFFSET) +#define DEVICE_ID0_REG __REG(S5P_ONENAND_BASE + DEVICE_ID0_OFFSET) +#define DATA_BUF_SIZE0_REG __REG(S5P_ONENAND_BASE + DATA_BUF_SIZE0_OFFSET) +#define FBA_WIDTH0_REG __REG(S5P_ONENAND_BASE + FBA_WIDTH0_OFFSET) +#define FPA_WIDTH0_REG __REG(S5P_ONENAND_BASE + FPA_WIDTH0_OFFSET) +#define FSA_WIDTH0_REG __REG(S5P_ONENAND_BASE + FSA_WIDTH0_OFFSET) +#define TRANS_SPARE0_REG __REG(S5P_ONENAND_BASE + TRANS_SPARE0_OFFSET) +#define DBS_DFS_WIDTH0_REG __REG(S5P_ONENAND_BASE + DBS_DFS_WIDTH0_OFFSET) +#define INT_PIN_ENABLE0_REG __REG(S5P_ONENAND_BASE + INT_PIN_ENABLE0_OFFSET) +#define ACC_CLOCK0_REG __REG(S5P_ONENAND_BASE + ACC_CLOCK0_OFFSET) +#define FLASH_VER_ID0_REG __REG(S5P_ONENAND_BASE + FLASH_VER_ID0_OFFSET) +#define FLASH_AUX_CNTRL0_REG __REG(S5P_ONENAND_BASE + FLASH_AUX_CNTRL0_OFFSET) + +#define ONENAND_MEM_RESET_HOT 0x3 +#define ONENAND_MEM_RESET_COLD 0x2 +#define ONENAND_MEM_RESET_WARM 0x1 + +#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) + +#endif -- 2.7.4