1446b6dc8SMiquel Raynal // SPDX-License-Identifier: GPL-2.0-only 2446b6dc8SMiquel Raynal /* 3446b6dc8SMiquel Raynal * Samsung S3C64XX/S5PC1XX OneNAND driver 4446b6dc8SMiquel Raynal * 5446b6dc8SMiquel Raynal * Copyright © 2008-2010 Samsung Electronics 6446b6dc8SMiquel Raynal * Kyungmin Park <kyungmin.park@samsung.com> 7446b6dc8SMiquel Raynal * Marek Szyprowski <m.szyprowski@samsung.com> 8446b6dc8SMiquel Raynal * 9446b6dc8SMiquel Raynal * Implementation: 10446b6dc8SMiquel Raynal * S3C64XX: emulate the pseudo BufferRAM 11446b6dc8SMiquel Raynal * S5PC110: use DMA 12446b6dc8SMiquel Raynal */ 13446b6dc8SMiquel Raynal 14446b6dc8SMiquel Raynal #include <linux/module.h> 15446b6dc8SMiquel Raynal #include <linux/platform_device.h> 16446b6dc8SMiquel Raynal #include <linux/sched.h> 17446b6dc8SMiquel Raynal #include <linux/slab.h> 18446b6dc8SMiquel Raynal #include <linux/mtd/mtd.h> 19446b6dc8SMiquel Raynal #include <linux/mtd/onenand.h> 20446b6dc8SMiquel Raynal #include <linux/mtd/partitions.h> 21446b6dc8SMiquel Raynal #include <linux/dma-mapping.h> 22446b6dc8SMiquel Raynal #include <linux/interrupt.h> 23446b6dc8SMiquel Raynal #include <linux/io.h> 24446b6dc8SMiquel Raynal 25446b6dc8SMiquel Raynal #include "samsung.h" 26446b6dc8SMiquel Raynal 27446b6dc8SMiquel Raynal enum soc_type { 28446b6dc8SMiquel Raynal TYPE_S3C6400, 29446b6dc8SMiquel Raynal TYPE_S3C6410, 30446b6dc8SMiquel Raynal TYPE_S5PC110, 31446b6dc8SMiquel Raynal }; 32446b6dc8SMiquel Raynal 33446b6dc8SMiquel Raynal #define ONENAND_ERASE_STATUS 0x00 34446b6dc8SMiquel Raynal #define ONENAND_MULTI_ERASE_SET 0x01 35446b6dc8SMiquel Raynal #define ONENAND_ERASE_START 0x03 36446b6dc8SMiquel Raynal #define ONENAND_UNLOCK_START 0x08 37446b6dc8SMiquel Raynal #define ONENAND_UNLOCK_END 0x09 38446b6dc8SMiquel Raynal #define ONENAND_LOCK_START 0x0A 39446b6dc8SMiquel Raynal #define ONENAND_LOCK_END 0x0B 40446b6dc8SMiquel Raynal #define ONENAND_LOCK_TIGHT_START 0x0C 41446b6dc8SMiquel Raynal #define ONENAND_LOCK_TIGHT_END 0x0D 42446b6dc8SMiquel Raynal #define ONENAND_UNLOCK_ALL 0x0E 43446b6dc8SMiquel Raynal #define ONENAND_OTP_ACCESS 0x12 44446b6dc8SMiquel Raynal #define ONENAND_SPARE_ACCESS_ONLY 0x13 45446b6dc8SMiquel Raynal #define ONENAND_MAIN_ACCESS_ONLY 0x14 46446b6dc8SMiquel Raynal #define ONENAND_ERASE_VERIFY 0x15 47446b6dc8SMiquel Raynal #define ONENAND_MAIN_SPARE_ACCESS 0x16 48446b6dc8SMiquel Raynal #define ONENAND_PIPELINE_READ 0x4000 49446b6dc8SMiquel Raynal 50446b6dc8SMiquel Raynal #define MAP_00 (0x0) 51446b6dc8SMiquel Raynal #define MAP_01 (0x1) 52446b6dc8SMiquel Raynal #define MAP_10 (0x2) 53446b6dc8SMiquel Raynal #define MAP_11 (0x3) 54446b6dc8SMiquel Raynal 55446b6dc8SMiquel Raynal #define S3C64XX_CMD_MAP_SHIFT 24 56446b6dc8SMiquel Raynal 57446b6dc8SMiquel Raynal #define S3C6400_FBA_SHIFT 10 58446b6dc8SMiquel Raynal #define S3C6400_FPA_SHIFT 4 59446b6dc8SMiquel Raynal #define S3C6400_FSA_SHIFT 2 60446b6dc8SMiquel Raynal 61446b6dc8SMiquel Raynal #define S3C6410_FBA_SHIFT 12 62446b6dc8SMiquel Raynal #define S3C6410_FPA_SHIFT 6 63446b6dc8SMiquel Raynal #define S3C6410_FSA_SHIFT 4 64446b6dc8SMiquel Raynal 65446b6dc8SMiquel Raynal /* S5PC110 specific definitions */ 66446b6dc8SMiquel Raynal #define S5PC110_DMA_SRC_ADDR 0x400 67446b6dc8SMiquel Raynal #define S5PC110_DMA_SRC_CFG 0x404 68446b6dc8SMiquel Raynal #define S5PC110_DMA_DST_ADDR 0x408 69446b6dc8SMiquel Raynal #define S5PC110_DMA_DST_CFG 0x40C 70446b6dc8SMiquel Raynal #define S5PC110_DMA_TRANS_SIZE 0x414 71446b6dc8SMiquel Raynal #define S5PC110_DMA_TRANS_CMD 0x418 72446b6dc8SMiquel Raynal #define S5PC110_DMA_TRANS_STATUS 0x41C 73446b6dc8SMiquel Raynal #define S5PC110_DMA_TRANS_DIR 0x420 74446b6dc8SMiquel Raynal #define S5PC110_INTC_DMA_CLR 0x1004 75446b6dc8SMiquel Raynal #define S5PC110_INTC_ONENAND_CLR 0x1008 76446b6dc8SMiquel Raynal #define S5PC110_INTC_DMA_MASK 0x1024 77446b6dc8SMiquel Raynal #define S5PC110_INTC_ONENAND_MASK 0x1028 78446b6dc8SMiquel Raynal #define S5PC110_INTC_DMA_PEND 0x1044 79446b6dc8SMiquel Raynal #define S5PC110_INTC_ONENAND_PEND 0x1048 80446b6dc8SMiquel Raynal #define S5PC110_INTC_DMA_STATUS 0x1064 81446b6dc8SMiquel Raynal #define S5PC110_INTC_ONENAND_STATUS 0x1068 82446b6dc8SMiquel Raynal 83446b6dc8SMiquel Raynal #define S5PC110_INTC_DMA_TD (1 << 24) 84446b6dc8SMiquel Raynal #define S5PC110_INTC_DMA_TE (1 << 16) 85446b6dc8SMiquel Raynal 86446b6dc8SMiquel Raynal #define S5PC110_DMA_CFG_SINGLE (0x0 << 16) 87446b6dc8SMiquel Raynal #define S5PC110_DMA_CFG_4BURST (0x2 << 16) 88446b6dc8SMiquel Raynal #define S5PC110_DMA_CFG_8BURST (0x3 << 16) 89446b6dc8SMiquel Raynal #define S5PC110_DMA_CFG_16BURST (0x4 << 16) 90446b6dc8SMiquel Raynal 91446b6dc8SMiquel Raynal #define S5PC110_DMA_CFG_INC (0x0 << 8) 92446b6dc8SMiquel Raynal #define S5PC110_DMA_CFG_CNT (0x1 << 8) 93446b6dc8SMiquel Raynal 94446b6dc8SMiquel Raynal #define S5PC110_DMA_CFG_8BIT (0x0 << 0) 95446b6dc8SMiquel Raynal #define S5PC110_DMA_CFG_16BIT (0x1 << 0) 96446b6dc8SMiquel Raynal #define S5PC110_DMA_CFG_32BIT (0x2 << 0) 97446b6dc8SMiquel Raynal 98446b6dc8SMiquel Raynal #define S5PC110_DMA_SRC_CFG_READ (S5PC110_DMA_CFG_16BURST | \ 99446b6dc8SMiquel Raynal S5PC110_DMA_CFG_INC | \ 100446b6dc8SMiquel Raynal S5PC110_DMA_CFG_16BIT) 101446b6dc8SMiquel Raynal #define S5PC110_DMA_DST_CFG_READ (S5PC110_DMA_CFG_16BURST | \ 102446b6dc8SMiquel Raynal S5PC110_DMA_CFG_INC | \ 103446b6dc8SMiquel Raynal S5PC110_DMA_CFG_32BIT) 104446b6dc8SMiquel Raynal #define S5PC110_DMA_SRC_CFG_WRITE (S5PC110_DMA_CFG_16BURST | \ 105446b6dc8SMiquel Raynal S5PC110_DMA_CFG_INC | \ 106446b6dc8SMiquel Raynal S5PC110_DMA_CFG_32BIT) 107446b6dc8SMiquel Raynal #define S5PC110_DMA_DST_CFG_WRITE (S5PC110_DMA_CFG_16BURST | \ 108446b6dc8SMiquel Raynal S5PC110_DMA_CFG_INC | \ 109446b6dc8SMiquel Raynal S5PC110_DMA_CFG_16BIT) 110446b6dc8SMiquel Raynal 111446b6dc8SMiquel Raynal #define S5PC110_DMA_TRANS_CMD_TDC (0x1 << 18) 112446b6dc8SMiquel Raynal #define S5PC110_DMA_TRANS_CMD_TEC (0x1 << 16) 113446b6dc8SMiquel Raynal #define S5PC110_DMA_TRANS_CMD_TR (0x1 << 0) 114446b6dc8SMiquel Raynal 115446b6dc8SMiquel Raynal #define S5PC110_DMA_TRANS_STATUS_TD (0x1 << 18) 116446b6dc8SMiquel Raynal #define S5PC110_DMA_TRANS_STATUS_TB (0x1 << 17) 117446b6dc8SMiquel Raynal #define S5PC110_DMA_TRANS_STATUS_TE (0x1 << 16) 118446b6dc8SMiquel Raynal 119446b6dc8SMiquel Raynal #define S5PC110_DMA_DIR_READ 0x0 120446b6dc8SMiquel Raynal #define S5PC110_DMA_DIR_WRITE 0x1 121446b6dc8SMiquel Raynal 122446b6dc8SMiquel Raynal struct s3c_onenand { 123446b6dc8SMiquel Raynal struct mtd_info *mtd; 124446b6dc8SMiquel Raynal struct platform_device *pdev; 125446b6dc8SMiquel Raynal enum soc_type type; 126446b6dc8SMiquel Raynal void __iomem *base; 127446b6dc8SMiquel Raynal void __iomem *ahb_addr; 128446b6dc8SMiquel Raynal int bootram_command; 129446b6dc8SMiquel Raynal void *page_buf; 130446b6dc8SMiquel Raynal void *oob_buf; 131446b6dc8SMiquel Raynal unsigned int (*mem_addr)(int fba, int fpa, int fsa); 132446b6dc8SMiquel Raynal unsigned int (*cmd_map)(unsigned int type, unsigned int val); 133446b6dc8SMiquel Raynal void __iomem *dma_addr; 134446b6dc8SMiquel Raynal unsigned long phys_base; 135446b6dc8SMiquel Raynal struct completion complete; 136446b6dc8SMiquel Raynal }; 137446b6dc8SMiquel Raynal 138446b6dc8SMiquel Raynal #define CMD_MAP_00(dev, addr) (dev->cmd_map(MAP_00, ((addr) << 1))) 139446b6dc8SMiquel Raynal #define CMD_MAP_01(dev, mem_addr) (dev->cmd_map(MAP_01, (mem_addr))) 140446b6dc8SMiquel Raynal #define CMD_MAP_10(dev, mem_addr) (dev->cmd_map(MAP_10, (mem_addr))) 141446b6dc8SMiquel Raynal #define CMD_MAP_11(dev, addr) (dev->cmd_map(MAP_11, ((addr) << 2))) 142446b6dc8SMiquel Raynal 143446b6dc8SMiquel Raynal static struct s3c_onenand *onenand; 144446b6dc8SMiquel Raynal 145446b6dc8SMiquel Raynal static inline int s3c_read_reg(int offset) 146446b6dc8SMiquel Raynal { 147446b6dc8SMiquel Raynal return readl(onenand->base + offset); 148446b6dc8SMiquel Raynal } 149446b6dc8SMiquel Raynal 150446b6dc8SMiquel Raynal static inline void s3c_write_reg(int value, int offset) 151446b6dc8SMiquel Raynal { 152446b6dc8SMiquel Raynal writel(value, onenand->base + offset); 153446b6dc8SMiquel Raynal } 154446b6dc8SMiquel Raynal 155446b6dc8SMiquel Raynal static inline int s3c_read_cmd(unsigned int cmd) 156446b6dc8SMiquel Raynal { 157446b6dc8SMiquel Raynal return readl(onenand->ahb_addr + cmd); 158446b6dc8SMiquel Raynal } 159446b6dc8SMiquel Raynal 160446b6dc8SMiquel Raynal static inline void s3c_write_cmd(int value, unsigned int cmd) 161446b6dc8SMiquel Raynal { 162446b6dc8SMiquel Raynal writel(value, onenand->ahb_addr + cmd); 163446b6dc8SMiquel Raynal } 164446b6dc8SMiquel Raynal 165446b6dc8SMiquel Raynal #ifdef SAMSUNG_DEBUG 166446b6dc8SMiquel Raynal static void s3c_dump_reg(void) 167446b6dc8SMiquel Raynal { 168446b6dc8SMiquel Raynal int i; 169446b6dc8SMiquel Raynal 170446b6dc8SMiquel Raynal for (i = 0; i < 0x400; i += 0x40) { 171446b6dc8SMiquel Raynal printk(KERN_INFO "0x%08X: 0x%08x 0x%08x 0x%08x 0x%08x\n", 172446b6dc8SMiquel Raynal (unsigned int) onenand->base + i, 173446b6dc8SMiquel Raynal s3c_read_reg(i), s3c_read_reg(i + 0x10), 174446b6dc8SMiquel Raynal s3c_read_reg(i + 0x20), s3c_read_reg(i + 0x30)); 175446b6dc8SMiquel Raynal } 176446b6dc8SMiquel Raynal } 177446b6dc8SMiquel Raynal #endif 178446b6dc8SMiquel Raynal 179446b6dc8SMiquel Raynal static unsigned int s3c64xx_cmd_map(unsigned type, unsigned val) 180446b6dc8SMiquel Raynal { 181446b6dc8SMiquel Raynal return (type << S3C64XX_CMD_MAP_SHIFT) | val; 182446b6dc8SMiquel Raynal } 183446b6dc8SMiquel Raynal 184446b6dc8SMiquel Raynal static unsigned int s3c6400_mem_addr(int fba, int fpa, int fsa) 185446b6dc8SMiquel Raynal { 186446b6dc8SMiquel Raynal return (fba << S3C6400_FBA_SHIFT) | (fpa << S3C6400_FPA_SHIFT) | 187446b6dc8SMiquel Raynal (fsa << S3C6400_FSA_SHIFT); 188446b6dc8SMiquel Raynal } 189446b6dc8SMiquel Raynal 190446b6dc8SMiquel Raynal static unsigned int s3c6410_mem_addr(int fba, int fpa, int fsa) 191446b6dc8SMiquel Raynal { 192446b6dc8SMiquel Raynal return (fba << S3C6410_FBA_SHIFT) | (fpa << S3C6410_FPA_SHIFT) | 193446b6dc8SMiquel Raynal (fsa << S3C6410_FSA_SHIFT); 194446b6dc8SMiquel Raynal } 195446b6dc8SMiquel Raynal 196446b6dc8SMiquel Raynal static void s3c_onenand_reset(void) 197446b6dc8SMiquel Raynal { 198446b6dc8SMiquel Raynal unsigned long timeout = 0x10000; 199446b6dc8SMiquel Raynal int stat; 200446b6dc8SMiquel Raynal 201446b6dc8SMiquel Raynal s3c_write_reg(ONENAND_MEM_RESET_COLD, MEM_RESET_OFFSET); 202446b6dc8SMiquel Raynal while (1 && timeout--) { 203446b6dc8SMiquel Raynal stat = s3c_read_reg(INT_ERR_STAT_OFFSET); 204446b6dc8SMiquel Raynal if (stat & RST_CMP) 205446b6dc8SMiquel Raynal break; 206446b6dc8SMiquel Raynal } 207446b6dc8SMiquel Raynal stat = s3c_read_reg(INT_ERR_STAT_OFFSET); 208446b6dc8SMiquel Raynal s3c_write_reg(stat, INT_ERR_ACK_OFFSET); 209446b6dc8SMiquel Raynal 210446b6dc8SMiquel Raynal /* Clear interrupt */ 211446b6dc8SMiquel Raynal s3c_write_reg(0x0, INT_ERR_ACK_OFFSET); 212446b6dc8SMiquel Raynal /* Clear the ECC status */ 213446b6dc8SMiquel Raynal s3c_write_reg(0x0, ECC_ERR_STAT_OFFSET); 214446b6dc8SMiquel Raynal } 215446b6dc8SMiquel Raynal 216446b6dc8SMiquel Raynal static unsigned short s3c_onenand_readw(void __iomem *addr) 217446b6dc8SMiquel Raynal { 218446b6dc8SMiquel Raynal struct onenand_chip *this = onenand->mtd->priv; 219446b6dc8SMiquel Raynal struct device *dev = &onenand->pdev->dev; 220446b6dc8SMiquel Raynal int reg = addr - this->base; 221446b6dc8SMiquel Raynal int word_addr = reg >> 1; 222446b6dc8SMiquel Raynal int value; 223446b6dc8SMiquel Raynal 224446b6dc8SMiquel Raynal /* It's used for probing time */ 225446b6dc8SMiquel Raynal switch (reg) { 226446b6dc8SMiquel Raynal case ONENAND_REG_MANUFACTURER_ID: 227446b6dc8SMiquel Raynal return s3c_read_reg(MANUFACT_ID_OFFSET); 228446b6dc8SMiquel Raynal case ONENAND_REG_DEVICE_ID: 229446b6dc8SMiquel Raynal return s3c_read_reg(DEVICE_ID_OFFSET); 230446b6dc8SMiquel Raynal case ONENAND_REG_VERSION_ID: 231446b6dc8SMiquel Raynal return s3c_read_reg(FLASH_VER_ID_OFFSET); 232446b6dc8SMiquel Raynal case ONENAND_REG_DATA_BUFFER_SIZE: 233446b6dc8SMiquel Raynal return s3c_read_reg(DATA_BUF_SIZE_OFFSET); 234446b6dc8SMiquel Raynal case ONENAND_REG_TECHNOLOGY: 235446b6dc8SMiquel Raynal return s3c_read_reg(TECH_OFFSET); 236446b6dc8SMiquel Raynal case ONENAND_REG_SYS_CFG1: 237446b6dc8SMiquel Raynal return s3c_read_reg(MEM_CFG_OFFSET); 238446b6dc8SMiquel Raynal 239446b6dc8SMiquel Raynal /* Used at unlock all status */ 240446b6dc8SMiquel Raynal case ONENAND_REG_CTRL_STATUS: 241446b6dc8SMiquel Raynal return 0; 242446b6dc8SMiquel Raynal 243446b6dc8SMiquel Raynal case ONENAND_REG_WP_STATUS: 244446b6dc8SMiquel Raynal return ONENAND_WP_US; 245446b6dc8SMiquel Raynal 246446b6dc8SMiquel Raynal default: 247446b6dc8SMiquel Raynal break; 248446b6dc8SMiquel Raynal } 249446b6dc8SMiquel Raynal 250446b6dc8SMiquel Raynal /* BootRAM access control */ 251446b6dc8SMiquel Raynal if ((unsigned long)addr < ONENAND_DATARAM && onenand->bootram_command) { 252446b6dc8SMiquel Raynal if (word_addr == 0) 253446b6dc8SMiquel Raynal return s3c_read_reg(MANUFACT_ID_OFFSET); 254446b6dc8SMiquel Raynal if (word_addr == 1) 255446b6dc8SMiquel Raynal return s3c_read_reg(DEVICE_ID_OFFSET); 256446b6dc8SMiquel Raynal if (word_addr == 2) 257446b6dc8SMiquel Raynal return s3c_read_reg(FLASH_VER_ID_OFFSET); 258446b6dc8SMiquel Raynal } 259446b6dc8SMiquel Raynal 260446b6dc8SMiquel Raynal value = s3c_read_cmd(CMD_MAP_11(onenand, word_addr)) & 0xffff; 261446b6dc8SMiquel Raynal dev_info(dev, "%s: Illegal access at reg 0x%x, value 0x%x\n", __func__, 262446b6dc8SMiquel Raynal word_addr, value); 263446b6dc8SMiquel Raynal return value; 264446b6dc8SMiquel Raynal } 265446b6dc8SMiquel Raynal 266446b6dc8SMiquel Raynal static void s3c_onenand_writew(unsigned short value, void __iomem *addr) 267446b6dc8SMiquel Raynal { 268446b6dc8SMiquel Raynal struct onenand_chip *this = onenand->mtd->priv; 269446b6dc8SMiquel Raynal struct device *dev = &onenand->pdev->dev; 270446b6dc8SMiquel Raynal unsigned int reg = addr - this->base; 271446b6dc8SMiquel Raynal unsigned int word_addr = reg >> 1; 272446b6dc8SMiquel Raynal 273446b6dc8SMiquel Raynal /* It's used for probing time */ 274446b6dc8SMiquel Raynal switch (reg) { 275446b6dc8SMiquel Raynal case ONENAND_REG_SYS_CFG1: 276446b6dc8SMiquel Raynal s3c_write_reg(value, MEM_CFG_OFFSET); 277446b6dc8SMiquel Raynal return; 278446b6dc8SMiquel Raynal 279446b6dc8SMiquel Raynal case ONENAND_REG_START_ADDRESS1: 280446b6dc8SMiquel Raynal case ONENAND_REG_START_ADDRESS2: 281446b6dc8SMiquel Raynal return; 282446b6dc8SMiquel Raynal 283446b6dc8SMiquel Raynal /* Lock/lock-tight/unlock/unlock_all */ 284446b6dc8SMiquel Raynal case ONENAND_REG_START_BLOCK_ADDRESS: 285446b6dc8SMiquel Raynal return; 286446b6dc8SMiquel Raynal 287446b6dc8SMiquel Raynal default: 288446b6dc8SMiquel Raynal break; 289446b6dc8SMiquel Raynal } 290446b6dc8SMiquel Raynal 291446b6dc8SMiquel Raynal /* BootRAM access control */ 292446b6dc8SMiquel Raynal if ((unsigned long)addr < ONENAND_DATARAM) { 293446b6dc8SMiquel Raynal if (value == ONENAND_CMD_READID) { 294446b6dc8SMiquel Raynal onenand->bootram_command = 1; 295446b6dc8SMiquel Raynal return; 296446b6dc8SMiquel Raynal } 297446b6dc8SMiquel Raynal if (value == ONENAND_CMD_RESET) { 298446b6dc8SMiquel Raynal s3c_write_reg(ONENAND_MEM_RESET_COLD, MEM_RESET_OFFSET); 299446b6dc8SMiquel Raynal onenand->bootram_command = 0; 300446b6dc8SMiquel Raynal return; 301446b6dc8SMiquel Raynal } 302446b6dc8SMiquel Raynal } 303446b6dc8SMiquel Raynal 304446b6dc8SMiquel Raynal dev_info(dev, "%s: Illegal access at reg 0x%x, value 0x%x\n", __func__, 305446b6dc8SMiquel Raynal word_addr, value); 306446b6dc8SMiquel Raynal 307446b6dc8SMiquel Raynal s3c_write_cmd(value, CMD_MAP_11(onenand, word_addr)); 308446b6dc8SMiquel Raynal } 309446b6dc8SMiquel Raynal 310446b6dc8SMiquel Raynal static int s3c_onenand_wait(struct mtd_info *mtd, int state) 311446b6dc8SMiquel Raynal { 312446b6dc8SMiquel Raynal struct device *dev = &onenand->pdev->dev; 313446b6dc8SMiquel Raynal unsigned int flags = INT_ACT; 314446b6dc8SMiquel Raynal unsigned int stat, ecc; 315446b6dc8SMiquel Raynal unsigned long timeout; 316446b6dc8SMiquel Raynal 317446b6dc8SMiquel Raynal switch (state) { 318446b6dc8SMiquel Raynal case FL_READING: 319446b6dc8SMiquel Raynal flags |= BLK_RW_CMP | LOAD_CMP; 320446b6dc8SMiquel Raynal break; 321446b6dc8SMiquel Raynal case FL_WRITING: 322446b6dc8SMiquel Raynal flags |= BLK_RW_CMP | PGM_CMP; 323446b6dc8SMiquel Raynal break; 324446b6dc8SMiquel Raynal case FL_ERASING: 325446b6dc8SMiquel Raynal flags |= BLK_RW_CMP | ERS_CMP; 326446b6dc8SMiquel Raynal break; 327446b6dc8SMiquel Raynal case FL_LOCKING: 328446b6dc8SMiquel Raynal flags |= BLK_RW_CMP; 329446b6dc8SMiquel Raynal break; 330446b6dc8SMiquel Raynal default: 331446b6dc8SMiquel Raynal break; 332446b6dc8SMiquel Raynal } 333446b6dc8SMiquel Raynal 334446b6dc8SMiquel Raynal /* The 20 msec is enough */ 335446b6dc8SMiquel Raynal timeout = jiffies + msecs_to_jiffies(20); 336446b6dc8SMiquel Raynal while (time_before(jiffies, timeout)) { 337446b6dc8SMiquel Raynal stat = s3c_read_reg(INT_ERR_STAT_OFFSET); 338446b6dc8SMiquel Raynal if (stat & flags) 339446b6dc8SMiquel Raynal break; 340446b6dc8SMiquel Raynal 341446b6dc8SMiquel Raynal if (state != FL_READING) 342446b6dc8SMiquel Raynal cond_resched(); 343446b6dc8SMiquel Raynal } 344446b6dc8SMiquel Raynal /* To get correct interrupt status in timeout case */ 345446b6dc8SMiquel Raynal stat = s3c_read_reg(INT_ERR_STAT_OFFSET); 346446b6dc8SMiquel Raynal s3c_write_reg(stat, INT_ERR_ACK_OFFSET); 347446b6dc8SMiquel Raynal 348446b6dc8SMiquel Raynal /* 349446b6dc8SMiquel Raynal * In the Spec. it checks the controller status first 350446b6dc8SMiquel Raynal * However if you get the correct information in case of 351446b6dc8SMiquel Raynal * power off recovery (POR) test, it should read ECC status first 352446b6dc8SMiquel Raynal */ 353446b6dc8SMiquel Raynal if (stat & LOAD_CMP) { 354446b6dc8SMiquel Raynal ecc = s3c_read_reg(ECC_ERR_STAT_OFFSET); 355446b6dc8SMiquel Raynal if (ecc & ONENAND_ECC_4BIT_UNCORRECTABLE) { 356446b6dc8SMiquel Raynal dev_info(dev, "%s: ECC error = 0x%04x\n", __func__, 357446b6dc8SMiquel Raynal ecc); 358446b6dc8SMiquel Raynal mtd->ecc_stats.failed++; 359446b6dc8SMiquel Raynal return -EBADMSG; 360446b6dc8SMiquel Raynal } 361446b6dc8SMiquel Raynal } 362446b6dc8SMiquel Raynal 363446b6dc8SMiquel Raynal if (stat & (LOCKED_BLK | ERS_FAIL | PGM_FAIL | LD_FAIL_ECC_ERR)) { 364446b6dc8SMiquel Raynal dev_info(dev, "%s: controller error = 0x%04x\n", __func__, 365446b6dc8SMiquel Raynal stat); 366446b6dc8SMiquel Raynal if (stat & LOCKED_BLK) 367446b6dc8SMiquel Raynal dev_info(dev, "%s: it's locked error = 0x%04x\n", 368446b6dc8SMiquel Raynal __func__, stat); 369446b6dc8SMiquel Raynal 370446b6dc8SMiquel Raynal return -EIO; 371446b6dc8SMiquel Raynal } 372446b6dc8SMiquel Raynal 373446b6dc8SMiquel Raynal return 0; 374446b6dc8SMiquel Raynal } 375446b6dc8SMiquel Raynal 376446b6dc8SMiquel Raynal static int s3c_onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, 377446b6dc8SMiquel Raynal size_t len) 378446b6dc8SMiquel Raynal { 379446b6dc8SMiquel Raynal struct onenand_chip *this = mtd->priv; 380446b6dc8SMiquel Raynal unsigned int *m, *s; 381446b6dc8SMiquel Raynal int fba, fpa, fsa = 0; 382446b6dc8SMiquel Raynal unsigned int mem_addr, cmd_map_01, cmd_map_10; 383446b6dc8SMiquel Raynal int i, mcount, scount; 384446b6dc8SMiquel Raynal int index; 385446b6dc8SMiquel Raynal 386446b6dc8SMiquel Raynal fba = (int) (addr >> this->erase_shift); 387446b6dc8SMiquel Raynal fpa = (int) (addr >> this->page_shift); 388446b6dc8SMiquel Raynal fpa &= this->page_mask; 389446b6dc8SMiquel Raynal 390446b6dc8SMiquel Raynal mem_addr = onenand->mem_addr(fba, fpa, fsa); 391446b6dc8SMiquel Raynal cmd_map_01 = CMD_MAP_01(onenand, mem_addr); 392446b6dc8SMiquel Raynal cmd_map_10 = CMD_MAP_10(onenand, mem_addr); 393446b6dc8SMiquel Raynal 394446b6dc8SMiquel Raynal switch (cmd) { 395446b6dc8SMiquel Raynal case ONENAND_CMD_READ: 396446b6dc8SMiquel Raynal case ONENAND_CMD_READOOB: 397446b6dc8SMiquel Raynal case ONENAND_CMD_BUFFERRAM: 398446b6dc8SMiquel Raynal ONENAND_SET_NEXT_BUFFERRAM(this); 399*36a016a5SGustavo A. R. Silva break; 400446b6dc8SMiquel Raynal default: 401446b6dc8SMiquel Raynal break; 402446b6dc8SMiquel Raynal } 403446b6dc8SMiquel Raynal 404446b6dc8SMiquel Raynal index = ONENAND_CURRENT_BUFFERRAM(this); 405446b6dc8SMiquel Raynal 406446b6dc8SMiquel Raynal /* 407446b6dc8SMiquel Raynal * Emulate Two BufferRAMs and access with 4 bytes pointer 408446b6dc8SMiquel Raynal */ 409446b6dc8SMiquel Raynal m = onenand->page_buf; 410446b6dc8SMiquel Raynal s = onenand->oob_buf; 411446b6dc8SMiquel Raynal 412446b6dc8SMiquel Raynal if (index) { 413446b6dc8SMiquel Raynal m += (this->writesize >> 2); 414446b6dc8SMiquel Raynal s += (mtd->oobsize >> 2); 415446b6dc8SMiquel Raynal } 416446b6dc8SMiquel Raynal 417446b6dc8SMiquel Raynal mcount = mtd->writesize >> 2; 418446b6dc8SMiquel Raynal scount = mtd->oobsize >> 2; 419446b6dc8SMiquel Raynal 420446b6dc8SMiquel Raynal switch (cmd) { 421446b6dc8SMiquel Raynal case ONENAND_CMD_READ: 422446b6dc8SMiquel Raynal /* Main */ 423446b6dc8SMiquel Raynal for (i = 0; i < mcount; i++) 424446b6dc8SMiquel Raynal *m++ = s3c_read_cmd(cmd_map_01); 425446b6dc8SMiquel Raynal return 0; 426446b6dc8SMiquel Raynal 427446b6dc8SMiquel Raynal case ONENAND_CMD_READOOB: 428446b6dc8SMiquel Raynal s3c_write_reg(TSRF, TRANS_SPARE_OFFSET); 429446b6dc8SMiquel Raynal /* Main */ 430446b6dc8SMiquel Raynal for (i = 0; i < mcount; i++) 431446b6dc8SMiquel Raynal *m++ = s3c_read_cmd(cmd_map_01); 432446b6dc8SMiquel Raynal 433446b6dc8SMiquel Raynal /* Spare */ 434446b6dc8SMiquel Raynal for (i = 0; i < scount; i++) 435446b6dc8SMiquel Raynal *s++ = s3c_read_cmd(cmd_map_01); 436446b6dc8SMiquel Raynal 437446b6dc8SMiquel Raynal s3c_write_reg(0, TRANS_SPARE_OFFSET); 438446b6dc8SMiquel Raynal return 0; 439446b6dc8SMiquel Raynal 440446b6dc8SMiquel Raynal case ONENAND_CMD_PROG: 441446b6dc8SMiquel Raynal /* Main */ 442446b6dc8SMiquel Raynal for (i = 0; i < mcount; i++) 443446b6dc8SMiquel Raynal s3c_write_cmd(*m++, cmd_map_01); 444446b6dc8SMiquel Raynal return 0; 445446b6dc8SMiquel Raynal 446446b6dc8SMiquel Raynal case ONENAND_CMD_PROGOOB: 447446b6dc8SMiquel Raynal s3c_write_reg(TSRF, TRANS_SPARE_OFFSET); 448446b6dc8SMiquel Raynal 449446b6dc8SMiquel Raynal /* Main - dummy write */ 450446b6dc8SMiquel Raynal for (i = 0; i < mcount; i++) 451446b6dc8SMiquel Raynal s3c_write_cmd(0xffffffff, cmd_map_01); 452446b6dc8SMiquel Raynal 453446b6dc8SMiquel Raynal /* Spare */ 454446b6dc8SMiquel Raynal for (i = 0; i < scount; i++) 455446b6dc8SMiquel Raynal s3c_write_cmd(*s++, cmd_map_01); 456446b6dc8SMiquel Raynal 457446b6dc8SMiquel Raynal s3c_write_reg(0, TRANS_SPARE_OFFSET); 458446b6dc8SMiquel Raynal return 0; 459446b6dc8SMiquel Raynal 460446b6dc8SMiquel Raynal case ONENAND_CMD_UNLOCK_ALL: 461446b6dc8SMiquel Raynal s3c_write_cmd(ONENAND_UNLOCK_ALL, cmd_map_10); 462446b6dc8SMiquel Raynal return 0; 463446b6dc8SMiquel Raynal 464446b6dc8SMiquel Raynal case ONENAND_CMD_ERASE: 465446b6dc8SMiquel Raynal s3c_write_cmd(ONENAND_ERASE_START, cmd_map_10); 466446b6dc8SMiquel Raynal return 0; 467446b6dc8SMiquel Raynal 468446b6dc8SMiquel Raynal default: 469446b6dc8SMiquel Raynal break; 470446b6dc8SMiquel Raynal } 471446b6dc8SMiquel Raynal 472446b6dc8SMiquel Raynal return 0; 473446b6dc8SMiquel Raynal } 474446b6dc8SMiquel Raynal 475446b6dc8SMiquel Raynal static unsigned char *s3c_get_bufferram(struct mtd_info *mtd, int area) 476446b6dc8SMiquel Raynal { 477446b6dc8SMiquel Raynal struct onenand_chip *this = mtd->priv; 478446b6dc8SMiquel Raynal int index = ONENAND_CURRENT_BUFFERRAM(this); 479446b6dc8SMiquel Raynal unsigned char *p; 480446b6dc8SMiquel Raynal 481446b6dc8SMiquel Raynal if (area == ONENAND_DATARAM) { 482446b6dc8SMiquel Raynal p = onenand->page_buf; 483446b6dc8SMiquel Raynal if (index == 1) 484446b6dc8SMiquel Raynal p += this->writesize; 485446b6dc8SMiquel Raynal } else { 486446b6dc8SMiquel Raynal p = onenand->oob_buf; 487446b6dc8SMiquel Raynal if (index == 1) 488446b6dc8SMiquel Raynal p += mtd->oobsize; 489446b6dc8SMiquel Raynal } 490446b6dc8SMiquel Raynal 491446b6dc8SMiquel Raynal return p; 492446b6dc8SMiquel Raynal } 493446b6dc8SMiquel Raynal 494446b6dc8SMiquel Raynal static int onenand_read_bufferram(struct mtd_info *mtd, int area, 495446b6dc8SMiquel Raynal unsigned char *buffer, int offset, 496446b6dc8SMiquel Raynal size_t count) 497446b6dc8SMiquel Raynal { 498446b6dc8SMiquel Raynal unsigned char *p; 499446b6dc8SMiquel Raynal 500446b6dc8SMiquel Raynal p = s3c_get_bufferram(mtd, area); 501446b6dc8SMiquel Raynal memcpy(buffer, p + offset, count); 502446b6dc8SMiquel Raynal return 0; 503446b6dc8SMiquel Raynal } 504446b6dc8SMiquel Raynal 505446b6dc8SMiquel Raynal static int onenand_write_bufferram(struct mtd_info *mtd, int area, 506446b6dc8SMiquel Raynal const unsigned char *buffer, int offset, 507446b6dc8SMiquel Raynal size_t count) 508446b6dc8SMiquel Raynal { 509446b6dc8SMiquel Raynal unsigned char *p; 510446b6dc8SMiquel Raynal 511446b6dc8SMiquel Raynal p = s3c_get_bufferram(mtd, area); 512446b6dc8SMiquel Raynal memcpy(p + offset, buffer, count); 513446b6dc8SMiquel Raynal return 0; 514446b6dc8SMiquel Raynal } 515446b6dc8SMiquel Raynal 516446b6dc8SMiquel Raynal static int (*s5pc110_dma_ops)(dma_addr_t dst, dma_addr_t src, size_t count, int direction); 517446b6dc8SMiquel Raynal 518446b6dc8SMiquel Raynal static int s5pc110_dma_poll(dma_addr_t dst, dma_addr_t src, size_t count, int direction) 519446b6dc8SMiquel Raynal { 520446b6dc8SMiquel Raynal void __iomem *base = onenand->dma_addr; 521446b6dc8SMiquel Raynal int status; 522446b6dc8SMiquel Raynal unsigned long timeout; 523446b6dc8SMiquel Raynal 524446b6dc8SMiquel Raynal writel(src, base + S5PC110_DMA_SRC_ADDR); 525446b6dc8SMiquel Raynal writel(dst, base + S5PC110_DMA_DST_ADDR); 526446b6dc8SMiquel Raynal 527446b6dc8SMiquel Raynal if (direction == S5PC110_DMA_DIR_READ) { 528446b6dc8SMiquel Raynal writel(S5PC110_DMA_SRC_CFG_READ, base + S5PC110_DMA_SRC_CFG); 529446b6dc8SMiquel Raynal writel(S5PC110_DMA_DST_CFG_READ, base + S5PC110_DMA_DST_CFG); 530446b6dc8SMiquel Raynal } else { 531446b6dc8SMiquel Raynal writel(S5PC110_DMA_SRC_CFG_WRITE, base + S5PC110_DMA_SRC_CFG); 532446b6dc8SMiquel Raynal writel(S5PC110_DMA_DST_CFG_WRITE, base + S5PC110_DMA_DST_CFG); 533446b6dc8SMiquel Raynal } 534446b6dc8SMiquel Raynal 535446b6dc8SMiquel Raynal writel(count, base + S5PC110_DMA_TRANS_SIZE); 536446b6dc8SMiquel Raynal writel(direction, base + S5PC110_DMA_TRANS_DIR); 537446b6dc8SMiquel Raynal 538446b6dc8SMiquel Raynal writel(S5PC110_DMA_TRANS_CMD_TR, base + S5PC110_DMA_TRANS_CMD); 539446b6dc8SMiquel Raynal 540446b6dc8SMiquel Raynal /* 541446b6dc8SMiquel Raynal * There's no exact timeout values at Spec. 542446b6dc8SMiquel Raynal * In real case it takes under 1 msec. 543446b6dc8SMiquel Raynal * So 20 msecs are enough. 544446b6dc8SMiquel Raynal */ 545446b6dc8SMiquel Raynal timeout = jiffies + msecs_to_jiffies(20); 546446b6dc8SMiquel Raynal 547446b6dc8SMiquel Raynal do { 548446b6dc8SMiquel Raynal status = readl(base + S5PC110_DMA_TRANS_STATUS); 549446b6dc8SMiquel Raynal if (status & S5PC110_DMA_TRANS_STATUS_TE) { 550446b6dc8SMiquel Raynal writel(S5PC110_DMA_TRANS_CMD_TEC, 551446b6dc8SMiquel Raynal base + S5PC110_DMA_TRANS_CMD); 552446b6dc8SMiquel Raynal return -EIO; 553446b6dc8SMiquel Raynal } 554446b6dc8SMiquel Raynal } while (!(status & S5PC110_DMA_TRANS_STATUS_TD) && 555446b6dc8SMiquel Raynal time_before(jiffies, timeout)); 556446b6dc8SMiquel Raynal 557446b6dc8SMiquel Raynal writel(S5PC110_DMA_TRANS_CMD_TDC, base + S5PC110_DMA_TRANS_CMD); 558446b6dc8SMiquel Raynal 559446b6dc8SMiquel Raynal return 0; 560446b6dc8SMiquel Raynal } 561446b6dc8SMiquel Raynal 562446b6dc8SMiquel Raynal static irqreturn_t s5pc110_onenand_irq(int irq, void *data) 563446b6dc8SMiquel Raynal { 564446b6dc8SMiquel Raynal void __iomem *base = onenand->dma_addr; 565446b6dc8SMiquel Raynal int status, cmd = 0; 566446b6dc8SMiquel Raynal 567446b6dc8SMiquel Raynal status = readl(base + S5PC110_INTC_DMA_STATUS); 568446b6dc8SMiquel Raynal 569446b6dc8SMiquel Raynal if (likely(status & S5PC110_INTC_DMA_TD)) 570446b6dc8SMiquel Raynal cmd = S5PC110_DMA_TRANS_CMD_TDC; 571446b6dc8SMiquel Raynal 572446b6dc8SMiquel Raynal if (unlikely(status & S5PC110_INTC_DMA_TE)) 573446b6dc8SMiquel Raynal cmd = S5PC110_DMA_TRANS_CMD_TEC; 574446b6dc8SMiquel Raynal 575446b6dc8SMiquel Raynal writel(cmd, base + S5PC110_DMA_TRANS_CMD); 576446b6dc8SMiquel Raynal writel(status, base + S5PC110_INTC_DMA_CLR); 577446b6dc8SMiquel Raynal 578446b6dc8SMiquel Raynal if (!onenand->complete.done) 579446b6dc8SMiquel Raynal complete(&onenand->complete); 580446b6dc8SMiquel Raynal 581446b6dc8SMiquel Raynal return IRQ_HANDLED; 582446b6dc8SMiquel Raynal } 583446b6dc8SMiquel Raynal 584446b6dc8SMiquel Raynal static int s5pc110_dma_irq(dma_addr_t dst, dma_addr_t src, size_t count, int direction) 585446b6dc8SMiquel Raynal { 586446b6dc8SMiquel Raynal void __iomem *base = onenand->dma_addr; 587446b6dc8SMiquel Raynal int status; 588446b6dc8SMiquel Raynal 589446b6dc8SMiquel Raynal status = readl(base + S5PC110_INTC_DMA_MASK); 590446b6dc8SMiquel Raynal if (status) { 591446b6dc8SMiquel Raynal status &= ~(S5PC110_INTC_DMA_TD | S5PC110_INTC_DMA_TE); 592446b6dc8SMiquel Raynal writel(status, base + S5PC110_INTC_DMA_MASK); 593446b6dc8SMiquel Raynal } 594446b6dc8SMiquel Raynal 595446b6dc8SMiquel Raynal writel(src, base + S5PC110_DMA_SRC_ADDR); 596446b6dc8SMiquel Raynal writel(dst, base + S5PC110_DMA_DST_ADDR); 597446b6dc8SMiquel Raynal 598446b6dc8SMiquel Raynal if (direction == S5PC110_DMA_DIR_READ) { 599446b6dc8SMiquel Raynal writel(S5PC110_DMA_SRC_CFG_READ, base + S5PC110_DMA_SRC_CFG); 600446b6dc8SMiquel Raynal writel(S5PC110_DMA_DST_CFG_READ, base + S5PC110_DMA_DST_CFG); 601446b6dc8SMiquel Raynal } else { 602446b6dc8SMiquel Raynal writel(S5PC110_DMA_SRC_CFG_WRITE, base + S5PC110_DMA_SRC_CFG); 603446b6dc8SMiquel Raynal writel(S5PC110_DMA_DST_CFG_WRITE, base + S5PC110_DMA_DST_CFG); 604446b6dc8SMiquel Raynal } 605446b6dc8SMiquel Raynal 606446b6dc8SMiquel Raynal writel(count, base + S5PC110_DMA_TRANS_SIZE); 607446b6dc8SMiquel Raynal writel(direction, base + S5PC110_DMA_TRANS_DIR); 608446b6dc8SMiquel Raynal 609446b6dc8SMiquel Raynal writel(S5PC110_DMA_TRANS_CMD_TR, base + S5PC110_DMA_TRANS_CMD); 610446b6dc8SMiquel Raynal 611446b6dc8SMiquel Raynal wait_for_completion_timeout(&onenand->complete, msecs_to_jiffies(20)); 612446b6dc8SMiquel Raynal 613446b6dc8SMiquel Raynal return 0; 614446b6dc8SMiquel Raynal } 615446b6dc8SMiquel Raynal 616446b6dc8SMiquel Raynal static int s5pc110_read_bufferram(struct mtd_info *mtd, int area, 617446b6dc8SMiquel Raynal unsigned char *buffer, int offset, size_t count) 618446b6dc8SMiquel Raynal { 619446b6dc8SMiquel Raynal struct onenand_chip *this = mtd->priv; 620446b6dc8SMiquel Raynal void __iomem *p; 621446b6dc8SMiquel Raynal void *buf = (void *) buffer; 622446b6dc8SMiquel Raynal dma_addr_t dma_src, dma_dst; 623446b6dc8SMiquel Raynal int err, ofs, page_dma = 0; 624446b6dc8SMiquel Raynal struct device *dev = &onenand->pdev->dev; 625446b6dc8SMiquel Raynal 626446b6dc8SMiquel Raynal p = this->base + area; 627446b6dc8SMiquel Raynal if (ONENAND_CURRENT_BUFFERRAM(this)) { 628446b6dc8SMiquel Raynal if (area == ONENAND_DATARAM) 629446b6dc8SMiquel Raynal p += this->writesize; 630446b6dc8SMiquel Raynal else 631446b6dc8SMiquel Raynal p += mtd->oobsize; 632446b6dc8SMiquel Raynal } 633446b6dc8SMiquel Raynal 634446b6dc8SMiquel Raynal if (offset & 3 || (size_t) buf & 3 || 635446b6dc8SMiquel Raynal !onenand->dma_addr || count != mtd->writesize) 636446b6dc8SMiquel Raynal goto normal; 637446b6dc8SMiquel Raynal 638446b6dc8SMiquel Raynal /* Handle vmalloc address */ 639446b6dc8SMiquel Raynal if (buf >= high_memory) { 640446b6dc8SMiquel Raynal struct page *page; 641446b6dc8SMiquel Raynal 642446b6dc8SMiquel Raynal if (((size_t) buf & PAGE_MASK) != 643446b6dc8SMiquel Raynal ((size_t) (buf + count - 1) & PAGE_MASK)) 644446b6dc8SMiquel Raynal goto normal; 645446b6dc8SMiquel Raynal page = vmalloc_to_page(buf); 646446b6dc8SMiquel Raynal if (!page) 647446b6dc8SMiquel Raynal goto normal; 648446b6dc8SMiquel Raynal 649446b6dc8SMiquel Raynal /* Page offset */ 650446b6dc8SMiquel Raynal ofs = ((size_t) buf & ~PAGE_MASK); 651446b6dc8SMiquel Raynal page_dma = 1; 652446b6dc8SMiquel Raynal 653446b6dc8SMiquel Raynal /* DMA routine */ 654446b6dc8SMiquel Raynal dma_src = onenand->phys_base + (p - this->base); 655446b6dc8SMiquel Raynal dma_dst = dma_map_page(dev, page, ofs, count, DMA_FROM_DEVICE); 656446b6dc8SMiquel Raynal } else { 657446b6dc8SMiquel Raynal /* DMA routine */ 658446b6dc8SMiquel Raynal dma_src = onenand->phys_base + (p - this->base); 659446b6dc8SMiquel Raynal dma_dst = dma_map_single(dev, buf, count, DMA_FROM_DEVICE); 660446b6dc8SMiquel Raynal } 661446b6dc8SMiquel Raynal if (dma_mapping_error(dev, dma_dst)) { 662446b6dc8SMiquel Raynal dev_err(dev, "Couldn't map a %zu byte buffer for DMA\n", count); 663446b6dc8SMiquel Raynal goto normal; 664446b6dc8SMiquel Raynal } 665446b6dc8SMiquel Raynal err = s5pc110_dma_ops(dma_dst, dma_src, 666446b6dc8SMiquel Raynal count, S5PC110_DMA_DIR_READ); 667446b6dc8SMiquel Raynal 668446b6dc8SMiquel Raynal if (page_dma) 669446b6dc8SMiquel Raynal dma_unmap_page(dev, dma_dst, count, DMA_FROM_DEVICE); 670446b6dc8SMiquel Raynal else 671446b6dc8SMiquel Raynal dma_unmap_single(dev, dma_dst, count, DMA_FROM_DEVICE); 672446b6dc8SMiquel Raynal 673446b6dc8SMiquel Raynal if (!err) 674446b6dc8SMiquel Raynal return 0; 675446b6dc8SMiquel Raynal 676446b6dc8SMiquel Raynal normal: 677446b6dc8SMiquel Raynal if (count != mtd->writesize) { 678446b6dc8SMiquel Raynal /* Copy the bufferram to memory to prevent unaligned access */ 679446b6dc8SMiquel Raynal memcpy_fromio(this->page_buf, p, mtd->writesize); 680446b6dc8SMiquel Raynal memcpy(buffer, this->page_buf + offset, count); 681446b6dc8SMiquel Raynal } else { 682446b6dc8SMiquel Raynal memcpy_fromio(buffer, p, count); 683446b6dc8SMiquel Raynal } 684446b6dc8SMiquel Raynal 685446b6dc8SMiquel Raynal return 0; 686446b6dc8SMiquel Raynal } 687446b6dc8SMiquel Raynal 688446b6dc8SMiquel Raynal static int s5pc110_chip_probe(struct mtd_info *mtd) 689446b6dc8SMiquel Raynal { 690446b6dc8SMiquel Raynal /* Now just return 0 */ 691446b6dc8SMiquel Raynal return 0; 692446b6dc8SMiquel Raynal } 693446b6dc8SMiquel Raynal 694446b6dc8SMiquel Raynal static int s3c_onenand_bbt_wait(struct mtd_info *mtd, int state) 695446b6dc8SMiquel Raynal { 696446b6dc8SMiquel Raynal unsigned int flags = INT_ACT | LOAD_CMP; 697446b6dc8SMiquel Raynal unsigned int stat; 698446b6dc8SMiquel Raynal unsigned long timeout; 699446b6dc8SMiquel Raynal 700446b6dc8SMiquel Raynal /* The 20 msec is enough */ 701446b6dc8SMiquel Raynal timeout = jiffies + msecs_to_jiffies(20); 702446b6dc8SMiquel Raynal while (time_before(jiffies, timeout)) { 703446b6dc8SMiquel Raynal stat = s3c_read_reg(INT_ERR_STAT_OFFSET); 704446b6dc8SMiquel Raynal if (stat & flags) 705446b6dc8SMiquel Raynal break; 706446b6dc8SMiquel Raynal } 707446b6dc8SMiquel Raynal /* To get correct interrupt status in timeout case */ 708446b6dc8SMiquel Raynal stat = s3c_read_reg(INT_ERR_STAT_OFFSET); 709446b6dc8SMiquel Raynal s3c_write_reg(stat, INT_ERR_ACK_OFFSET); 710446b6dc8SMiquel Raynal 711446b6dc8SMiquel Raynal if (stat & LD_FAIL_ECC_ERR) { 712446b6dc8SMiquel Raynal s3c_onenand_reset(); 713446b6dc8SMiquel Raynal return ONENAND_BBT_READ_ERROR; 714446b6dc8SMiquel Raynal } 715446b6dc8SMiquel Raynal 716446b6dc8SMiquel Raynal if (stat & LOAD_CMP) { 717446b6dc8SMiquel Raynal int ecc = s3c_read_reg(ECC_ERR_STAT_OFFSET); 718446b6dc8SMiquel Raynal if (ecc & ONENAND_ECC_4BIT_UNCORRECTABLE) { 719446b6dc8SMiquel Raynal s3c_onenand_reset(); 720446b6dc8SMiquel Raynal return ONENAND_BBT_READ_ERROR; 721446b6dc8SMiquel Raynal } 722446b6dc8SMiquel Raynal } 723446b6dc8SMiquel Raynal 724446b6dc8SMiquel Raynal return 0; 725446b6dc8SMiquel Raynal } 726446b6dc8SMiquel Raynal 727446b6dc8SMiquel Raynal static void s3c_onenand_check_lock_status(struct mtd_info *mtd) 728446b6dc8SMiquel Raynal { 729446b6dc8SMiquel Raynal struct onenand_chip *this = mtd->priv; 730446b6dc8SMiquel Raynal struct device *dev = &onenand->pdev->dev; 731446b6dc8SMiquel Raynal unsigned int block, end; 732446b6dc8SMiquel Raynal 733446b6dc8SMiquel Raynal end = this->chipsize >> this->erase_shift; 734446b6dc8SMiquel Raynal 735446b6dc8SMiquel Raynal for (block = 0; block < end; block++) { 736446b6dc8SMiquel Raynal unsigned int mem_addr = onenand->mem_addr(block, 0, 0); 737446b6dc8SMiquel Raynal s3c_read_cmd(CMD_MAP_01(onenand, mem_addr)); 738446b6dc8SMiquel Raynal 739446b6dc8SMiquel Raynal if (s3c_read_reg(INT_ERR_STAT_OFFSET) & LOCKED_BLK) { 740446b6dc8SMiquel Raynal dev_err(dev, "block %d is write-protected!\n", block); 741446b6dc8SMiquel Raynal s3c_write_reg(LOCKED_BLK, INT_ERR_ACK_OFFSET); 742446b6dc8SMiquel Raynal } 743446b6dc8SMiquel Raynal } 744446b6dc8SMiquel Raynal } 745446b6dc8SMiquel Raynal 746446b6dc8SMiquel Raynal static void s3c_onenand_do_lock_cmd(struct mtd_info *mtd, loff_t ofs, 747446b6dc8SMiquel Raynal size_t len, int cmd) 748446b6dc8SMiquel Raynal { 749446b6dc8SMiquel Raynal struct onenand_chip *this = mtd->priv; 750446b6dc8SMiquel Raynal int start, end, start_mem_addr, end_mem_addr; 751446b6dc8SMiquel Raynal 752446b6dc8SMiquel Raynal start = ofs >> this->erase_shift; 753446b6dc8SMiquel Raynal start_mem_addr = onenand->mem_addr(start, 0, 0); 754446b6dc8SMiquel Raynal end = start + (len >> this->erase_shift) - 1; 755446b6dc8SMiquel Raynal end_mem_addr = onenand->mem_addr(end, 0, 0); 756446b6dc8SMiquel Raynal 757446b6dc8SMiquel Raynal if (cmd == ONENAND_CMD_LOCK) { 758446b6dc8SMiquel Raynal s3c_write_cmd(ONENAND_LOCK_START, CMD_MAP_10(onenand, 759446b6dc8SMiquel Raynal start_mem_addr)); 760446b6dc8SMiquel Raynal s3c_write_cmd(ONENAND_LOCK_END, CMD_MAP_10(onenand, 761446b6dc8SMiquel Raynal end_mem_addr)); 762446b6dc8SMiquel Raynal } else { 763446b6dc8SMiquel Raynal s3c_write_cmd(ONENAND_UNLOCK_START, CMD_MAP_10(onenand, 764446b6dc8SMiquel Raynal start_mem_addr)); 765446b6dc8SMiquel Raynal s3c_write_cmd(ONENAND_UNLOCK_END, CMD_MAP_10(onenand, 766446b6dc8SMiquel Raynal end_mem_addr)); 767446b6dc8SMiquel Raynal } 768446b6dc8SMiquel Raynal 769446b6dc8SMiquel Raynal this->wait(mtd, FL_LOCKING); 770446b6dc8SMiquel Raynal } 771446b6dc8SMiquel Raynal 772446b6dc8SMiquel Raynal static void s3c_unlock_all(struct mtd_info *mtd) 773446b6dc8SMiquel Raynal { 774446b6dc8SMiquel Raynal struct onenand_chip *this = mtd->priv; 775446b6dc8SMiquel Raynal loff_t ofs = 0; 776446b6dc8SMiquel Raynal size_t len = this->chipsize; 777446b6dc8SMiquel Raynal 778446b6dc8SMiquel Raynal if (this->options & ONENAND_HAS_UNLOCK_ALL) { 779446b6dc8SMiquel Raynal /* Write unlock command */ 780446b6dc8SMiquel Raynal this->command(mtd, ONENAND_CMD_UNLOCK_ALL, 0, 0); 781446b6dc8SMiquel Raynal 782446b6dc8SMiquel Raynal /* No need to check return value */ 783446b6dc8SMiquel Raynal this->wait(mtd, FL_LOCKING); 784446b6dc8SMiquel Raynal 785446b6dc8SMiquel Raynal /* Workaround for all block unlock in DDP */ 786446b6dc8SMiquel Raynal if (!ONENAND_IS_DDP(this)) { 787446b6dc8SMiquel Raynal s3c_onenand_check_lock_status(mtd); 788446b6dc8SMiquel Raynal return; 789446b6dc8SMiquel Raynal } 790446b6dc8SMiquel Raynal 791446b6dc8SMiquel Raynal /* All blocks on another chip */ 792446b6dc8SMiquel Raynal ofs = this->chipsize >> 1; 793446b6dc8SMiquel Raynal len = this->chipsize >> 1; 794446b6dc8SMiquel Raynal } 795446b6dc8SMiquel Raynal 796446b6dc8SMiquel Raynal s3c_onenand_do_lock_cmd(mtd, ofs, len, ONENAND_CMD_UNLOCK); 797446b6dc8SMiquel Raynal 798446b6dc8SMiquel Raynal s3c_onenand_check_lock_status(mtd); 799446b6dc8SMiquel Raynal } 800446b6dc8SMiquel Raynal 801446b6dc8SMiquel Raynal static void s3c_onenand_setup(struct mtd_info *mtd) 802446b6dc8SMiquel Raynal { 803446b6dc8SMiquel Raynal struct onenand_chip *this = mtd->priv; 804446b6dc8SMiquel Raynal 805446b6dc8SMiquel Raynal onenand->mtd = mtd; 806446b6dc8SMiquel Raynal 807446b6dc8SMiquel Raynal if (onenand->type == TYPE_S3C6400) { 808446b6dc8SMiquel Raynal onenand->mem_addr = s3c6400_mem_addr; 809446b6dc8SMiquel Raynal onenand->cmd_map = s3c64xx_cmd_map; 810446b6dc8SMiquel Raynal } else if (onenand->type == TYPE_S3C6410) { 811446b6dc8SMiquel Raynal onenand->mem_addr = s3c6410_mem_addr; 812446b6dc8SMiquel Raynal onenand->cmd_map = s3c64xx_cmd_map; 813446b6dc8SMiquel Raynal } else if (onenand->type == TYPE_S5PC110) { 814446b6dc8SMiquel Raynal /* Use generic onenand functions */ 815446b6dc8SMiquel Raynal this->read_bufferram = s5pc110_read_bufferram; 816446b6dc8SMiquel Raynal this->chip_probe = s5pc110_chip_probe; 817446b6dc8SMiquel Raynal return; 818446b6dc8SMiquel Raynal } else { 819446b6dc8SMiquel Raynal BUG(); 820446b6dc8SMiquel Raynal } 821446b6dc8SMiquel Raynal 822446b6dc8SMiquel Raynal this->read_word = s3c_onenand_readw; 823446b6dc8SMiquel Raynal this->write_word = s3c_onenand_writew; 824446b6dc8SMiquel Raynal 825446b6dc8SMiquel Raynal this->wait = s3c_onenand_wait; 826446b6dc8SMiquel Raynal this->bbt_wait = s3c_onenand_bbt_wait; 827446b6dc8SMiquel Raynal this->unlock_all = s3c_unlock_all; 828446b6dc8SMiquel Raynal this->command = s3c_onenand_command; 829446b6dc8SMiquel Raynal 830446b6dc8SMiquel Raynal this->read_bufferram = onenand_read_bufferram; 831446b6dc8SMiquel Raynal this->write_bufferram = onenand_write_bufferram; 832446b6dc8SMiquel Raynal } 833446b6dc8SMiquel Raynal 834446b6dc8SMiquel Raynal static int s3c_onenand_probe(struct platform_device *pdev) 835446b6dc8SMiquel Raynal { 836446b6dc8SMiquel Raynal struct onenand_platform_data *pdata; 837446b6dc8SMiquel Raynal struct onenand_chip *this; 838446b6dc8SMiquel Raynal struct mtd_info *mtd; 839446b6dc8SMiquel Raynal struct resource *r; 840446b6dc8SMiquel Raynal int size, err; 841446b6dc8SMiquel Raynal 842446b6dc8SMiquel Raynal pdata = dev_get_platdata(&pdev->dev); 843446b6dc8SMiquel Raynal /* No need to check pdata. the platform data is optional */ 844446b6dc8SMiquel Raynal 845446b6dc8SMiquel Raynal size = sizeof(struct mtd_info) + sizeof(struct onenand_chip); 846446b6dc8SMiquel Raynal mtd = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); 847446b6dc8SMiquel Raynal if (!mtd) 848446b6dc8SMiquel Raynal return -ENOMEM; 849446b6dc8SMiquel Raynal 850446b6dc8SMiquel Raynal onenand = devm_kzalloc(&pdev->dev, sizeof(struct s3c_onenand), 851446b6dc8SMiquel Raynal GFP_KERNEL); 852446b6dc8SMiquel Raynal if (!onenand) 853446b6dc8SMiquel Raynal return -ENOMEM; 854446b6dc8SMiquel Raynal 855446b6dc8SMiquel Raynal this = (struct onenand_chip *) &mtd[1]; 856446b6dc8SMiquel Raynal mtd->priv = this; 857446b6dc8SMiquel Raynal mtd->dev.parent = &pdev->dev; 858446b6dc8SMiquel Raynal onenand->pdev = pdev; 859446b6dc8SMiquel Raynal onenand->type = platform_get_device_id(pdev)->driver_data; 860446b6dc8SMiquel Raynal 861446b6dc8SMiquel Raynal s3c_onenand_setup(mtd); 862446b6dc8SMiquel Raynal 863446b6dc8SMiquel Raynal r = platform_get_resource(pdev, IORESOURCE_MEM, 0); 864446b6dc8SMiquel Raynal onenand->base = devm_ioremap_resource(&pdev->dev, r); 865446b6dc8SMiquel Raynal if (IS_ERR(onenand->base)) 866446b6dc8SMiquel Raynal return PTR_ERR(onenand->base); 867446b6dc8SMiquel Raynal 868446b6dc8SMiquel Raynal onenand->phys_base = r->start; 869446b6dc8SMiquel Raynal 870446b6dc8SMiquel Raynal /* Set onenand_chip also */ 871446b6dc8SMiquel Raynal this->base = onenand->base; 872446b6dc8SMiquel Raynal 873446b6dc8SMiquel Raynal /* Use runtime badblock check */ 874446b6dc8SMiquel Raynal this->options |= ONENAND_SKIP_UNLOCK_CHECK; 875446b6dc8SMiquel Raynal 876446b6dc8SMiquel Raynal if (onenand->type != TYPE_S5PC110) { 877446b6dc8SMiquel Raynal r = platform_get_resource(pdev, IORESOURCE_MEM, 1); 878446b6dc8SMiquel Raynal onenand->ahb_addr = devm_ioremap_resource(&pdev->dev, r); 879446b6dc8SMiquel Raynal if (IS_ERR(onenand->ahb_addr)) 880446b6dc8SMiquel Raynal return PTR_ERR(onenand->ahb_addr); 881446b6dc8SMiquel Raynal 882446b6dc8SMiquel Raynal /* Allocate 4KiB BufferRAM */ 883446b6dc8SMiquel Raynal onenand->page_buf = devm_kzalloc(&pdev->dev, SZ_4K, 884446b6dc8SMiquel Raynal GFP_KERNEL); 885446b6dc8SMiquel Raynal if (!onenand->page_buf) 886446b6dc8SMiquel Raynal return -ENOMEM; 887446b6dc8SMiquel Raynal 888446b6dc8SMiquel Raynal /* Allocate 128 SpareRAM */ 889446b6dc8SMiquel Raynal onenand->oob_buf = devm_kzalloc(&pdev->dev, 128, GFP_KERNEL); 890446b6dc8SMiquel Raynal if (!onenand->oob_buf) 891446b6dc8SMiquel Raynal return -ENOMEM; 892446b6dc8SMiquel Raynal 893446b6dc8SMiquel Raynal /* S3C doesn't handle subpage write */ 894446b6dc8SMiquel Raynal mtd->subpage_sft = 0; 895446b6dc8SMiquel Raynal this->subpagesize = mtd->writesize; 896446b6dc8SMiquel Raynal 897446b6dc8SMiquel Raynal } else { /* S5PC110 */ 898446b6dc8SMiquel Raynal r = platform_get_resource(pdev, IORESOURCE_MEM, 1); 899446b6dc8SMiquel Raynal onenand->dma_addr = devm_ioremap_resource(&pdev->dev, r); 900446b6dc8SMiquel Raynal if (IS_ERR(onenand->dma_addr)) 901446b6dc8SMiquel Raynal return PTR_ERR(onenand->dma_addr); 902446b6dc8SMiquel Raynal 903446b6dc8SMiquel Raynal s5pc110_dma_ops = s5pc110_dma_poll; 904446b6dc8SMiquel Raynal /* Interrupt support */ 905446b6dc8SMiquel Raynal r = platform_get_resource(pdev, IORESOURCE_IRQ, 0); 906446b6dc8SMiquel Raynal if (r) { 907446b6dc8SMiquel Raynal init_completion(&onenand->complete); 908446b6dc8SMiquel Raynal s5pc110_dma_ops = s5pc110_dma_irq; 909446b6dc8SMiquel Raynal err = devm_request_irq(&pdev->dev, r->start, 910446b6dc8SMiquel Raynal s5pc110_onenand_irq, 911446b6dc8SMiquel Raynal IRQF_SHARED, "onenand", 912446b6dc8SMiquel Raynal &onenand); 913446b6dc8SMiquel Raynal if (err) { 914446b6dc8SMiquel Raynal dev_err(&pdev->dev, "failed to get irq\n"); 915446b6dc8SMiquel Raynal return err; 916446b6dc8SMiquel Raynal } 917446b6dc8SMiquel Raynal } 918446b6dc8SMiquel Raynal } 919446b6dc8SMiquel Raynal 920446b6dc8SMiquel Raynal err = onenand_scan(mtd, 1); 921446b6dc8SMiquel Raynal if (err) 922446b6dc8SMiquel Raynal return err; 923446b6dc8SMiquel Raynal 924446b6dc8SMiquel Raynal if (onenand->type != TYPE_S5PC110) { 925446b6dc8SMiquel Raynal /* S3C doesn't handle subpage write */ 926446b6dc8SMiquel Raynal mtd->subpage_sft = 0; 927446b6dc8SMiquel Raynal this->subpagesize = mtd->writesize; 928446b6dc8SMiquel Raynal } 929446b6dc8SMiquel Raynal 930446b6dc8SMiquel Raynal if (s3c_read_reg(MEM_CFG_OFFSET) & ONENAND_SYS_CFG1_SYNC_READ) 931446b6dc8SMiquel Raynal dev_info(&onenand->pdev->dev, "OneNAND Sync. Burst Read enabled\n"); 932446b6dc8SMiquel Raynal 933446b6dc8SMiquel Raynal err = mtd_device_register(mtd, pdata ? pdata->parts : NULL, 934446b6dc8SMiquel Raynal pdata ? pdata->nr_parts : 0); 935446b6dc8SMiquel Raynal if (err) { 936446b6dc8SMiquel Raynal dev_err(&pdev->dev, "failed to parse partitions and register the MTD device\n"); 937446b6dc8SMiquel Raynal onenand_release(mtd); 938446b6dc8SMiquel Raynal return err; 939446b6dc8SMiquel Raynal } 940446b6dc8SMiquel Raynal 941446b6dc8SMiquel Raynal platform_set_drvdata(pdev, mtd); 942446b6dc8SMiquel Raynal 943446b6dc8SMiquel Raynal return 0; 944446b6dc8SMiquel Raynal } 945446b6dc8SMiquel Raynal 946446b6dc8SMiquel Raynal static int s3c_onenand_remove(struct platform_device *pdev) 947446b6dc8SMiquel Raynal { 948446b6dc8SMiquel Raynal struct mtd_info *mtd = platform_get_drvdata(pdev); 949446b6dc8SMiquel Raynal 950446b6dc8SMiquel Raynal onenand_release(mtd); 951446b6dc8SMiquel Raynal 952446b6dc8SMiquel Raynal return 0; 953446b6dc8SMiquel Raynal } 954446b6dc8SMiquel Raynal 955446b6dc8SMiquel Raynal static int s3c_pm_ops_suspend(struct device *dev) 956446b6dc8SMiquel Raynal { 957446b6dc8SMiquel Raynal struct mtd_info *mtd = dev_get_drvdata(dev); 958446b6dc8SMiquel Raynal struct onenand_chip *this = mtd->priv; 959446b6dc8SMiquel Raynal 960446b6dc8SMiquel Raynal this->wait(mtd, FL_PM_SUSPENDED); 961446b6dc8SMiquel Raynal return 0; 962446b6dc8SMiquel Raynal } 963446b6dc8SMiquel Raynal 964446b6dc8SMiquel Raynal static int s3c_pm_ops_resume(struct device *dev) 965446b6dc8SMiquel Raynal { 966446b6dc8SMiquel Raynal struct mtd_info *mtd = dev_get_drvdata(dev); 967446b6dc8SMiquel Raynal struct onenand_chip *this = mtd->priv; 968446b6dc8SMiquel Raynal 969446b6dc8SMiquel Raynal this->unlock_all(mtd); 970446b6dc8SMiquel Raynal return 0; 971446b6dc8SMiquel Raynal } 972446b6dc8SMiquel Raynal 973446b6dc8SMiquel Raynal static const struct dev_pm_ops s3c_pm_ops = { 974446b6dc8SMiquel Raynal .suspend = s3c_pm_ops_suspend, 975446b6dc8SMiquel Raynal .resume = s3c_pm_ops_resume, 976446b6dc8SMiquel Raynal }; 977446b6dc8SMiquel Raynal 978446b6dc8SMiquel Raynal static const struct platform_device_id s3c_onenand_driver_ids[] = { 979446b6dc8SMiquel Raynal { 980446b6dc8SMiquel Raynal .name = "s3c6400-onenand", 981446b6dc8SMiquel Raynal .driver_data = TYPE_S3C6400, 982446b6dc8SMiquel Raynal }, { 983446b6dc8SMiquel Raynal .name = "s3c6410-onenand", 984446b6dc8SMiquel Raynal .driver_data = TYPE_S3C6410, 985446b6dc8SMiquel Raynal }, { 986446b6dc8SMiquel Raynal .name = "s5pc110-onenand", 987446b6dc8SMiquel Raynal .driver_data = TYPE_S5PC110, 988446b6dc8SMiquel Raynal }, { }, 989446b6dc8SMiquel Raynal }; 990446b6dc8SMiquel Raynal MODULE_DEVICE_TABLE(platform, s3c_onenand_driver_ids); 991446b6dc8SMiquel Raynal 992446b6dc8SMiquel Raynal static struct platform_driver s3c_onenand_driver = { 993446b6dc8SMiquel Raynal .driver = { 994446b6dc8SMiquel Raynal .name = "samsung-onenand", 995446b6dc8SMiquel Raynal .pm = &s3c_pm_ops, 996446b6dc8SMiquel Raynal }, 997446b6dc8SMiquel Raynal .id_table = s3c_onenand_driver_ids, 998446b6dc8SMiquel Raynal .probe = s3c_onenand_probe, 999446b6dc8SMiquel Raynal .remove = s3c_onenand_remove, 1000446b6dc8SMiquel Raynal }; 1001446b6dc8SMiquel Raynal 1002446b6dc8SMiquel Raynal module_platform_driver(s3c_onenand_driver); 1003446b6dc8SMiquel Raynal 1004446b6dc8SMiquel Raynal MODULE_LICENSE("GPL"); 1005446b6dc8SMiquel Raynal MODULE_AUTHOR("Kyungmin Park <kyungmin.park@samsung.com>"); 1006446b6dc8SMiquel Raynal MODULE_DESCRIPTION("Samsung OneNAND controller support"); 1007