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(®->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