diff options
author | Boris Brezillon <boris.brezillon@bootlin.com> | 2018-02-18 17:05:16 +0100 |
---|---|---|
committer | Boris Brezillon <boris.brezillon@bootlin.com> | 2018-03-15 15:40:37 +0100 |
commit | 26777d37216c976cf6fd196700133a38aa2c4b0f (patch) | |
tree | 73199fbe5fba118d48421b21bfdb110d21c03532 /drivers/mtd/onenand | |
parent | 801492c508f617d5bdc383b98b4a49d7de90c154 (diff) | |
download | op-kernel-dev-26777d37216c976cf6fd196700133a38aa2c4b0f.zip op-kernel-dev-26777d37216c976cf6fd196700133a38aa2c4b0f.tar.gz |
mtd: Move onenand code base to drivers/mtd/nand/onenand
Move onenand code base to the drivers/mtd/nand directory in the hope
that someday someone will patch it to use the generic NAND helpers.
If it never happens, at least we'll have all NAND related support in a
single directory and not spread over the drivers/mtd/ directory.
Signed-off-by: Boris Brezillon <boris.brezillon@bootlin.com>
Diffstat (limited to 'drivers/mtd/onenand')
-rw-r--r-- | drivers/mtd/onenand/Kconfig | 71 | ||||
-rw-r--r-- | drivers/mtd/onenand/Makefile | 14 | ||||
-rw-r--r-- | drivers/mtd/onenand/generic.c | 116 | ||||
-rw-r--r-- | drivers/mtd/onenand/omap2.c | 660 | ||||
-rw-r--r-- | drivers/mtd/onenand/onenand_base.c | 4031 | ||||
-rw-r--r-- | drivers/mtd/onenand/onenand_bbt.c | 248 | ||||
-rw-r--r-- | drivers/mtd/onenand/samsung.c | 1012 | ||||
-rw-r--r-- | drivers/mtd/onenand/samsung.h | 59 |
8 files changed, 0 insertions, 6211 deletions
diff --git a/drivers/mtd/onenand/Kconfig b/drivers/mtd/onenand/Kconfig deleted file mode 100644 index 9dc1574..0000000 --- a/drivers/mtd/onenand/Kconfig +++ /dev/null @@ -1,71 +0,0 @@ -menuconfig MTD_ONENAND - tristate "OneNAND Device Support" - depends on MTD - depends on HAS_IOMEM - help - This enables support for accessing all type of OneNAND flash - devices. - -if MTD_ONENAND - -config MTD_ONENAND_VERIFY_WRITE - bool "Verify OneNAND page writes" - help - This adds an extra check when data is written to the flash. The - OneNAND flash device internally checks only bits transitioning - from 1 to 0. There is a rare possibility that even though the - device thinks the write was successful, a bit could have been - flipped accidentally due to device wear or something else. - -config MTD_ONENAND_GENERIC - tristate "OneNAND Flash device via platform device driver" - help - Support for OneNAND flash via platform device driver. - -config MTD_ONENAND_OMAP2 - tristate "OneNAND on OMAP2/OMAP3 support" - depends on ARCH_OMAP2 || ARCH_OMAP3 - depends on OF || COMPILE_TEST - help - Support for a OneNAND flash device connected to an OMAP2/OMAP3 SoC - via the GPMC memory controller. - Enable dmaengine and gpiolib for better performance. - -config MTD_ONENAND_SAMSUNG - tristate "OneNAND on Samsung SOC controller support" - depends on ARCH_S3C64XX || ARCH_S5PV210 || ARCH_EXYNOS4 - help - Support for a OneNAND flash device connected to an Samsung SOC. - S3C64XX uses command mapping method. - S5PC110/S5PC210 use generic OneNAND method. - -config MTD_ONENAND_OTP - bool "OneNAND OTP Support" - help - One Block of the NAND Flash Array memory is reserved as - a One-Time Programmable Block memory area. - Also, 1st Block of NAND Flash Array can be used as OTP. - - The OTP block can be read, programmed and locked using the same - operations as any other NAND Flash Array memory block. - OTP block cannot be erased. - - OTP block is fully-guaranteed to be a valid block. - -config MTD_ONENAND_2X_PROGRAM - bool "OneNAND 2X program support" - help - The 2X Program is an extension of Program Operation. - Since the device is equipped with two DataRAMs, and two-plane NAND - Flash memory array, these two component enables simultaneous program - of 4KiB. Plane1 has only even blocks such as block0, block2, block4 - while Plane2 has only odd blocks such as block1, block3, block5. - So MTD regards it as 4KiB page size and 256KiB block size - - Now the following chips support it. (KFXXX16Q2M) - Demux: KFG2G16Q2M, KFH4G16Q2M, KFW8G16Q2M, - Mux: KFM2G16Q2M, KFN4G16Q2M, - - And more recent chips - -endif # MTD_ONENAND diff --git a/drivers/mtd/onenand/Makefile b/drivers/mtd/onenand/Makefile deleted file mode 100644 index f8b624a..0000000 --- a/drivers/mtd/onenand/Makefile +++ /dev/null @@ -1,14 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# -# Makefile for the OneNAND MTD -# - -# Core functionality. -obj-$(CONFIG_MTD_ONENAND) += onenand.o - -# Board specific. -obj-$(CONFIG_MTD_ONENAND_GENERIC) += generic.o -obj-$(CONFIG_MTD_ONENAND_OMAP2) += omap2.o -obj-$(CONFIG_MTD_ONENAND_SAMSUNG) += samsung.o - -onenand-objs = onenand_base.o onenand_bbt.o diff --git a/drivers/mtd/onenand/generic.c b/drivers/mtd/onenand/generic.c deleted file mode 100644 index d5ccaf9..0000000 --- a/drivers/mtd/onenand/generic.c +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright (c) 2005 Samsung Electronics - * Kyungmin Park <kyungmin.park@samsung.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Overview: - * This is a device driver for the OneNAND flash for generic boards. - */ - -#include <linux/module.h> -#include <linux/slab.h> -#include <linux/platform_device.h> -#include <linux/mtd/mtd.h> -#include <linux/mtd/onenand.h> -#include <linux/mtd/partitions.h> -#include <linux/io.h> - -/* - * Note: Driver name and platform data format have been updated! - * - * This version of the driver is named "onenand-flash" and takes struct - * onenand_platform_data as platform data. The old ARM-specific version - * with the name "onenand" used to take struct flash_platform_data. - */ -#define DRIVER_NAME "onenand-flash" - -struct onenand_info { - struct mtd_info mtd; - struct onenand_chip onenand; -}; - -static int generic_onenand_probe(struct platform_device *pdev) -{ - struct onenand_info *info; - struct onenand_platform_data *pdata = dev_get_platdata(&pdev->dev); - struct resource *res = pdev->resource; - unsigned long size = resource_size(res); - int err; - - info = kzalloc(sizeof(struct onenand_info), GFP_KERNEL); - if (!info) - return -ENOMEM; - - if (!request_mem_region(res->start, size, dev_name(&pdev->dev))) { - err = -EBUSY; - goto out_free_info; - } - - info->onenand.base = ioremap(res->start, size); - if (!info->onenand.base) { - err = -ENOMEM; - goto out_release_mem_region; - } - - info->onenand.mmcontrol = pdata ? pdata->mmcontrol : NULL; - info->onenand.irq = platform_get_irq(pdev, 0); - - info->mtd.dev.parent = &pdev->dev; - info->mtd.priv = &info->onenand; - - if (onenand_scan(&info->mtd, 1)) { - err = -ENXIO; - goto out_iounmap; - } - - err = mtd_device_parse_register(&info->mtd, NULL, NULL, - pdata ? pdata->parts : NULL, - pdata ? pdata->nr_parts : 0); - - platform_set_drvdata(pdev, info); - - return 0; - -out_iounmap: - iounmap(info->onenand.base); -out_release_mem_region: - release_mem_region(res->start, size); -out_free_info: - kfree(info); - - return err; -} - -static int generic_onenand_remove(struct platform_device *pdev) -{ - struct onenand_info *info = platform_get_drvdata(pdev); - struct resource *res = pdev->resource; - unsigned long size = resource_size(res); - - if (info) { - onenand_release(&info->mtd); - release_mem_region(res->start, size); - iounmap(info->onenand.base); - kfree(info); - } - - return 0; -} - -static struct platform_driver generic_onenand_driver = { - .driver = { - .name = DRIVER_NAME, - }, - .probe = generic_onenand_probe, - .remove = generic_onenand_remove, -}; - -module_platform_driver(generic_onenand_driver); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Kyungmin Park <kyungmin.park@samsung.com>"); -MODULE_DESCRIPTION("Glue layer for OneNAND flash on generic boards"); -MODULE_ALIAS("platform:" DRIVER_NAME); diff --git a/drivers/mtd/onenand/omap2.c b/drivers/mtd/onenand/omap2.c deleted file mode 100644 index 9c159f0..0000000 --- a/drivers/mtd/onenand/omap2.c +++ /dev/null @@ -1,660 +0,0 @@ -/* - * OneNAND driver for OMAP2 / OMAP3 - * - * Copyright © 2005-2006 Nokia Corporation - * - * Author: Jarkko Lavinen <jarkko.lavinen@nokia.com> and Juha Yrjölä - * IRQ and DMA support written by Timo Teras - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * - */ - -#include <linux/device.h> -#include <linux/module.h> -#include <linux/mtd/mtd.h> -#include <linux/mtd/onenand.h> -#include <linux/mtd/partitions.h> -#include <linux/of_device.h> -#include <linux/omap-gpmc.h> -#include <linux/platform_device.h> -#include <linux/interrupt.h> -#include <linux/delay.h> -#include <linux/dma-mapping.h> -#include <linux/dmaengine.h> -#include <linux/io.h> -#include <linux/slab.h> -#include <linux/gpio/consumer.h> - -#include <asm/mach/flash.h> - -#define DRIVER_NAME "omap2-onenand" - -#define ONENAND_BUFRAM_SIZE (1024 * 5) - -struct omap2_onenand { - struct platform_device *pdev; - int gpmc_cs; - unsigned long phys_base; - struct gpio_desc *int_gpiod; - struct mtd_info mtd; - struct onenand_chip onenand; - struct completion irq_done; - struct completion dma_done; - struct dma_chan *dma_chan; -}; - -static void omap2_onenand_dma_complete_func(void *completion) -{ - complete(completion); -} - -static irqreturn_t omap2_onenand_interrupt(int irq, void *dev_id) -{ - struct omap2_onenand *c = dev_id; - - complete(&c->irq_done); - - return IRQ_HANDLED; -} - -static inline unsigned short read_reg(struct omap2_onenand *c, int reg) -{ - return readw(c->onenand.base + reg); -} - -static inline void write_reg(struct omap2_onenand *c, unsigned short value, - int reg) -{ - writew(value, c->onenand.base + reg); -} - -static int omap2_onenand_set_cfg(struct omap2_onenand *c, - bool sr, bool sw, - int latency, int burst_len) -{ - unsigned short reg = ONENAND_SYS_CFG1_RDY | ONENAND_SYS_CFG1_INT; - - reg |= latency << ONENAND_SYS_CFG1_BRL_SHIFT; - - switch (burst_len) { - case 0: /* continuous */ - break; - case 4: - reg |= ONENAND_SYS_CFG1_BL_4; - break; - case 8: - reg |= ONENAND_SYS_CFG1_BL_8; - break; - case 16: - reg |= ONENAND_SYS_CFG1_BL_16; - break; - case 32: - reg |= ONENAND_SYS_CFG1_BL_32; - break; - default: - return -EINVAL; - } - - if (latency > 5) - reg |= ONENAND_SYS_CFG1_HF; - if (latency > 7) - reg |= ONENAND_SYS_CFG1_VHF; - if (sr) - reg |= ONENAND_SYS_CFG1_SYNC_READ; - if (sw) - reg |= ONENAND_SYS_CFG1_SYNC_WRITE; - - write_reg(c, reg, ONENAND_REG_SYS_CFG1); - - return 0; -} - -static int omap2_onenand_get_freq(int ver) -{ - switch ((ver >> 4) & 0xf) { - case 0: - return 40; - case 1: - return 54; - case 2: - return 66; - case 3: - return 83; - case 4: - return 104; - } - - return -EINVAL; -} - -static void wait_err(char *msg, int state, unsigned int ctrl, unsigned int intr) -{ - printk(KERN_ERR "onenand_wait: %s! state %d ctrl 0x%04x intr 0x%04x\n", - msg, state, ctrl, intr); -} - -static void wait_warn(char *msg, int state, unsigned int ctrl, - unsigned int intr) -{ - printk(KERN_WARNING "onenand_wait: %s! state %d ctrl 0x%04x " - "intr 0x%04x\n", msg, state, ctrl, intr); -} - -static int omap2_onenand_wait(struct mtd_info *mtd, int state) -{ - struct omap2_onenand *c = container_of(mtd, struct omap2_onenand, mtd); - struct onenand_chip *this = mtd->priv; - unsigned int intr = 0; - unsigned int ctrl, ctrl_mask; - unsigned long timeout; - u32 syscfg; - - if (state == FL_RESETING || state == FL_PREPARING_ERASE || - state == FL_VERIFYING_ERASE) { - int i = 21; - unsigned int intr_flags = ONENAND_INT_MASTER; - - switch (state) { - case FL_RESETING: - intr_flags |= ONENAND_INT_RESET; - break; - case FL_PREPARING_ERASE: - intr_flags |= ONENAND_INT_ERASE; - break; - case FL_VERIFYING_ERASE: - i = 101; - break; - } - - while (--i) { - udelay(1); - intr = read_reg(c, ONENAND_REG_INTERRUPT); - if (intr & ONENAND_INT_MASTER) - break; - } - ctrl = read_reg(c, ONENAND_REG_CTRL_STATUS); - if (ctrl & ONENAND_CTRL_ERROR) { - wait_err("controller error", state, ctrl, intr); - return -EIO; - } - if ((intr & intr_flags) == intr_flags) - return 0; - /* Continue in wait for interrupt branch */ - } - - if (state != FL_READING) { - int result; - - /* Turn interrupts on */ - syscfg = read_reg(c, ONENAND_REG_SYS_CFG1); - if (!(syscfg & ONENAND_SYS_CFG1_IOBE)) { - syscfg |= ONENAND_SYS_CFG1_IOBE; - write_reg(c, syscfg, ONENAND_REG_SYS_CFG1); - /* Add a delay to let GPIO settle */ - syscfg = read_reg(c, ONENAND_REG_SYS_CFG1); - } - - reinit_completion(&c->irq_done); - result = gpiod_get_value(c->int_gpiod); - if (result < 0) { - ctrl = read_reg(c, ONENAND_REG_CTRL_STATUS); - intr = read_reg(c, ONENAND_REG_INTERRUPT); - wait_err("gpio error", state, ctrl, intr); - return result; - } else if (result == 0) { - int retry_cnt = 0; -retry: - if (!wait_for_completion_io_timeout(&c->irq_done, - msecs_to_jiffies(20))) { - /* Timeout after 20ms */ - ctrl = read_reg(c, ONENAND_REG_CTRL_STATUS); - if (ctrl & ONENAND_CTRL_ONGO && - !this->ongoing) { - /* - * The operation seems to be still going - * so give it some more time. - */ - retry_cnt += 1; - if (retry_cnt < 3) - goto retry; - intr = read_reg(c, - ONENAND_REG_INTERRUPT); - wait_err("timeout", state, ctrl, intr); - return -EIO; - } - intr = read_reg(c, ONENAND_REG_INTERRUPT); - if ((intr & ONENAND_INT_MASTER) == 0) - wait_warn("timeout", state, ctrl, intr); - } - } - } else { - int retry_cnt = 0; - - /* Turn interrupts off */ - syscfg = read_reg(c, ONENAND_REG_SYS_CFG1); - syscfg &= ~ONENAND_SYS_CFG1_IOBE; - write_reg(c, syscfg, ONENAND_REG_SYS_CFG1); - - timeout = jiffies + msecs_to_jiffies(20); - while (1) { - if (time_before(jiffies, timeout)) { - intr = read_reg(c, ONENAND_REG_INTERRUPT); - if (intr & ONENAND_INT_MASTER) - break; - } else { - /* Timeout after 20ms */ - ctrl = read_reg(c, ONENAND_REG_CTRL_STATUS); - if (ctrl & ONENAND_CTRL_ONGO) { - /* - * The operation seems to be still going - * so give it some more time. - */ - retry_cnt += 1; - if (retry_cnt < 3) { - timeout = jiffies + - msecs_to_jiffies(20); - continue; - } - } - break; - } - } - } - - intr = read_reg(c, ONENAND_REG_INTERRUPT); - ctrl = read_reg(c, ONENAND_REG_CTRL_STATUS); - - if (intr & ONENAND_INT_READ) { - int ecc = read_reg(c, ONENAND_REG_ECC_STATUS); - - if (ecc) { - unsigned int addr1, addr8; - - addr1 = read_reg(c, ONENAND_REG_START_ADDRESS1); - addr8 = read_reg(c, ONENAND_REG_START_ADDRESS8); - if (ecc & ONENAND_ECC_2BIT_ALL) { - printk(KERN_ERR "onenand_wait: ECC error = " - "0x%04x, addr1 %#x, addr8 %#x\n", - ecc, addr1, addr8); - mtd->ecc_stats.failed++; - return -EBADMSG; - } else if (ecc & ONENAND_ECC_1BIT_ALL) { - printk(KERN_NOTICE "onenand_wait: correctable " - "ECC error = 0x%04x, addr1 %#x, " - "addr8 %#x\n", ecc, addr1, addr8); - mtd->ecc_stats.corrected++; - } - } - } else if (state == FL_READING) { - wait_err("timeout", state, ctrl, intr); - return -EIO; - } - - if (ctrl & ONENAND_CTRL_ERROR) { - wait_err("controller error", state, ctrl, intr); - if (ctrl & ONENAND_CTRL_LOCK) - printk(KERN_ERR "onenand_wait: " - "Device is write protected!!!\n"); - return -EIO; - } - - ctrl_mask = 0xFE9F; - if (this->ongoing) - ctrl_mask &= ~0x8000; - - if (ctrl & ctrl_mask) - wait_warn("unexpected controller status", state, ctrl, intr); - - return 0; -} - -static inline int omap2_onenand_bufferram_offset(struct mtd_info *mtd, int area) -{ - struct onenand_chip *this = mtd->priv; - - if (ONENAND_CURRENT_BUFFERRAM(this)) { - if (area == ONENAND_DATARAM) - return this->writesize; - if (area == ONENAND_SPARERAM) - return mtd->oobsize; - } - - return 0; -} - -static inline int omap2_onenand_dma_transfer(struct omap2_onenand *c, - dma_addr_t src, dma_addr_t dst, - size_t count) -{ - struct dma_async_tx_descriptor *tx; - dma_cookie_t cookie; - - tx = dmaengine_prep_dma_memcpy(c->dma_chan, dst, src, count, 0); - if (!tx) { - dev_err(&c->pdev->dev, "Failed to prepare DMA memcpy\n"); - return -EIO; - } - - reinit_completion(&c->dma_done); - - tx->callback = omap2_onenand_dma_complete_func; - tx->callback_param = &c->dma_done; - - cookie = tx->tx_submit(tx); - if (dma_submit_error(cookie)) { - dev_err(&c->pdev->dev, "Failed to do DMA tx_submit\n"); - return -EIO; - } - - dma_async_issue_pending(c->dma_chan); - - if (!wait_for_completion_io_timeout(&c->dma_done, - msecs_to_jiffies(20))) { - dmaengine_terminate_sync(c->dma_chan); - return -ETIMEDOUT; - } - - return 0; -} - -static int omap2_onenand_read_bufferram(struct mtd_info *mtd, int area, - unsigned char *buffer, int offset, - size_t count) -{ - struct omap2_onenand *c = container_of(mtd, struct omap2_onenand, mtd); - struct onenand_chip *this = mtd->priv; - dma_addr_t dma_src, dma_dst; - int bram_offset; - void *buf = (void *)buffer; - size_t xtra; - int ret; - - bram_offset = omap2_onenand_bufferram_offset(mtd, area) + area + offset; - if (bram_offset & 3 || (size_t)buf & 3 || count < 384) - goto out_copy; - - /* panic_write() may be in an interrupt context */ - if (in_interrupt() || oops_in_progress) - goto out_copy; - - if (buf >= high_memory) { - struct page *p1; - - if (((size_t)buf & PAGE_MASK) != - ((size_t)(buf + count - 1) & PAGE_MASK)) - goto out_copy; - p1 = vmalloc_to_page(buf); - if (!p1) - goto out_copy; - buf = page_address(p1) + ((size_t)buf & ~PAGE_MASK); - } - - xtra = count & 3; - if (xtra) { - count -= xtra; - memcpy(buf + count, this->base + bram_offset + count, xtra); - } - - dma_src = c->phys_base + bram_offset; - dma_dst = dma_map_single(&c->pdev->dev, buf, count, DMA_FROM_DEVICE); - if (dma_mapping_error(&c->pdev->dev, dma_dst)) { - dev_err(&c->pdev->dev, - "Couldn't DMA map a %d byte buffer\n", - count); - goto out_copy; - } - - ret = omap2_onenand_dma_transfer(c, dma_src, dma_dst, count); - dma_unmap_single(&c->pdev->dev, dma_dst, count, DMA_FROM_DEVICE); - - if (ret) { - dev_err(&c->pdev->dev, "timeout waiting for DMA\n"); - goto out_copy; - } - - return 0; - -out_copy: - memcpy(buf, this->base + bram_offset, count); - return 0; -} - -static int omap2_onenand_write_bufferram(struct mtd_info *mtd, int area, - const unsigned char *buffer, - int offset, size_t count) -{ - struct omap2_onenand *c = container_of(mtd, struct omap2_onenand, mtd); - struct onenand_chip *this = mtd->priv; - dma_addr_t dma_src, dma_dst; - int bram_offset; - void *buf = (void *)buffer; - int ret; - - bram_offset = omap2_onenand_bufferram_offset(mtd, area) + area + offset; - if (bram_offset & 3 || (size_t)buf & 3 || count < 384) - goto out_copy; - - /* panic_write() may be in an interrupt context */ - if (in_interrupt() || oops_in_progress) - goto out_copy; - - if (buf >= high_memory) { - struct page *p1; - - if (((size_t)buf & PAGE_MASK) != - ((size_t)(buf + count - 1) & PAGE_MASK)) - goto out_copy; - p1 = vmalloc_to_page(buf); - if (!p1) - goto out_copy; - buf = page_address(p1) + ((size_t)buf & ~PAGE_MASK); - } - - dma_src = dma_map_single(&c->pdev->dev, buf, count, DMA_TO_DEVICE); - dma_dst = c->phys_base + bram_offset; - if (dma_mapping_error(&c->pdev->dev, dma_src)) { - dev_err(&c->pdev->dev, - "Couldn't DMA map a %d byte buffer\n", - count); - return -1; - } - - ret = omap2_onenand_dma_transfer(c, dma_src, dma_dst, count); - dma_unmap_single(&c->pdev->dev, dma_src, count, DMA_TO_DEVICE); - - if (ret) { - dev_err(&c->pdev->dev, "timeout waiting for DMA\n"); - goto out_copy; - } - - return 0; - -out_copy: - memcpy(this->base + bram_offset, buf, count); - return 0; -} - -static void omap2_onenand_shutdown(struct platform_device *pdev) -{ - struct omap2_onenand *c = dev_get_drvdata(&pdev->dev); - - /* With certain content in the buffer RAM, the OMAP boot ROM code - * can recognize the flash chip incorrectly. Zero it out before - * soft reset. - */ - memset((__force void *)c->onenand.base, 0, ONENAND_BUFRAM_SIZE); -} - -static int omap2_onenand_probe(struct platform_device *pdev) -{ - u32 val; - dma_cap_mask_t mask; - int freq, latency, r; - struct resource *res; - struct omap2_onenand *c; - struct gpmc_onenand_info info; - struct device *dev = &pdev->dev; - struct device_node *np = dev->of_node; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(dev, "error getting memory resource\n"); - return -EINVAL; - } - - r = of_property_read_u32(np, "reg", &val); - if (r) { - dev_err(dev, "reg not found in DT\n"); - return r; - } - - c = devm_kzalloc(dev, sizeof(struct omap2_onenand), GFP_KERNEL); - if (!c) - return -ENOMEM; - - init_completion(&c->irq_done); - init_completion(&c->dma_done); - c->gpmc_cs = val; - c->phys_base = res->start; - - c->onenand.base = devm_ioremap_resource(dev, res); - if (IS_ERR(c->onenand.base)) - return PTR_ERR(c->onenand.base); - - c->int_gpiod = devm_gpiod_get_optional(dev, "int", GPIOD_IN); - if (IS_ERR(c->int_gpiod)) { - r = PTR_ERR(c->int_gpiod); - /* Just try again if this happens */ - if (r != -EPROBE_DEFER) - dev_err(dev, "error getting gpio: %d\n", r); - return r; - } - - if (c->int_gpiod) { - r = devm_request_irq(dev, gpiod_to_irq(c->int_gpiod), - omap2_onenand_interrupt, - IRQF_TRIGGER_RISING, "onenand", c); - if (r) - return r; - - c->onenand.wait = omap2_onenand_wait; - } - - dma_cap_zero(mask); - dma_cap_set(DMA_MEMCPY, mask); - - c->dma_chan = dma_request_channel(mask, NULL, NULL); - if (c->dma_chan) { - c->onenand.read_bufferram = omap2_onenand_read_bufferram; - c->onenand.write_bufferram = omap2_onenand_write_bufferram; - } - - c->pdev = pdev; - c->mtd.priv = &c->onenand; - c->mtd.dev.parent = dev; - mtd_set_of_node(&c->mtd, dev->of_node); - - dev_info(dev, "initializing on CS%d (0x%08lx), va %p, %s mode\n", - c->gpmc_cs, c->phys_base, c->onenand.base, - c->dma_chan ? "DMA" : "PIO"); - - if ((r = onenand_scan(&c->mtd, 1)) < 0) - goto err_release_dma; - - freq = omap2_onenand_get_freq(c->onenand.version_id); - if (freq > 0) { - switch (freq) { - case 104: - latency = 7; - break; - case 83: - latency = 6; - break; - case 66: - latency = 5; - break; - case 56: - latency = 4; - break; - default: /* 40 MHz or lower */ - latency = 3; - break; - } - - r = gpmc_omap_onenand_set_timings(dev, c->gpmc_cs, - freq, latency, &info); - if (r) - goto err_release_onenand; - - r = omap2_onenand_set_cfg(c, info.sync_read, info.sync_write, - latency, info.burst_len); - if (r) - goto err_release_onenand; - - if (info.sync_read || info.sync_write) - dev_info(dev, "optimized timings for %d MHz\n", freq); - } - - r = mtd_device_register(&c->mtd, NULL, 0); - if (r) - goto err_release_onenand; - - platform_set_drvdata(pdev, c); - - return 0; - -err_release_onenand: - onenand_release(&c->mtd); -err_release_dma: - if (c->dma_chan) - dma_release_channel(c->dma_chan); - - return r; -} - -static int omap2_onenand_remove(struct platform_device *pdev) -{ - struct omap2_onenand *c = dev_get_drvdata(&pdev->dev); - - onenand_release(&c->mtd); - if (c->dma_chan) - dma_release_channel(c->dma_chan); - omap2_onenand_shutdown(pdev); - - return 0; -} - -static const struct of_device_id omap2_onenand_id_table[] = { - { .compatible = "ti,omap2-onenand", }, - {}, -}; -MODULE_DEVICE_TABLE(of, omap2_onenand_id_table); - -static struct platform_driver omap2_onenand_driver = { - .probe = omap2_onenand_probe, - .remove = omap2_onenand_remove, - .shutdown = omap2_onenand_shutdown, - .driver = { - .name = DRIVER_NAME, - .of_match_table = omap2_onenand_id_table, - }, -}; - -module_platform_driver(omap2_onenand_driver); - -MODULE_ALIAS("platform:" DRIVER_NAME); -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Jarkko Lavinen <jarkko.lavinen@nokia.com>"); -MODULE_DESCRIPTION("Glue layer for OneNAND flash on OMAP2 / OMAP3"); diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c deleted file mode 100644 index 05907b1..0000000 --- a/drivers/mtd/onenand/onenand_base.c +++ /dev/null @@ -1,4031 +0,0 @@ -/* - * Copyright © 2005-2009 Samsung Electronics - * Copyright © 2007 Nokia Corporation - * - * Kyungmin Park <kyungmin.park@samsung.com> - * - * Credits: - * Adrian Hunter <ext-adrian.hunter@nokia.com>: - * auto-placement support, read-while load support, various fixes - * - * Vishak G <vishak.g at samsung.com>, Rohit Hagargundgi <h.rohit at samsung.com> - * Flex-OneNAND support - * Amul Kumar Saha <amul.saha at samsung.com> - * OTP support - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/moduleparam.h> -#include <linux/slab.h> -#include <linux/sched.h> -#include <linux/delay.h> -#include <linux/interrupt.h> -#include <linux/jiffies.h> -#include <linux/mtd/mtd.h> -#include <linux/mtd/onenand.h> -#include <linux/mtd/partitions.h> - -#include <asm/io.h> - -/* - * Multiblock erase if number of blocks to erase is 2 or more. - * Maximum number of blocks for simultaneous erase is 64. - */ -#define MB_ERASE_MIN_BLK_COUNT 2 -#define MB_ERASE_MAX_BLK_COUNT 64 - -/* Default Flex-OneNAND boundary and lock respectively */ -static int flex_bdry[MAX_DIES * 2] = { -1, 0, -1, 0 }; - -module_param_array(flex_bdry, int, NULL, 0400); -MODULE_PARM_DESC(flex_bdry, "SLC Boundary information for Flex-OneNAND" - "Syntax:flex_bdry=DIE_BDRY,LOCK,..." - "DIE_BDRY: SLC boundary of the die" - "LOCK: Locking information for SLC boundary" - " : 0->Set boundary in unlocked status" - " : 1->Set boundary in locked status"); - -/* Default OneNAND/Flex-OneNAND OTP options*/ -static int otp; - -module_param(otp, int, 0400); -MODULE_PARM_DESC(otp, "Corresponding behaviour of OneNAND in OTP" - "Syntax : otp=LOCK_TYPE" - "LOCK_TYPE : Keys issued, for specific OTP Lock type" - " : 0 -> Default (No Blocks Locked)" - " : 1 -> OTP Block lock" - " : 2 -> 1st Block lock" - " : 3 -> BOTH OTP Block and 1st Block lock"); - -/* - * flexonenand_oob_128 - oob info for Flex-Onenand with 4KB page - * For now, we expose only 64 out of 80 ecc bytes - */ -static int flexonenand_ooblayout_ecc(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - if (section > 7) - return -ERANGE; - - oobregion->offset = (section * 16) + 6; - oobregion->length = 10; - - return 0; -} - -static int flexonenand_ooblayout_free(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - if (section > 7) - return -ERANGE; - - oobregion->offset = (section * 16) + 2; - oobregion->length = 4; - - return 0; -} - -static const struct mtd_ooblayout_ops flexonenand_ooblayout_ops = { - .ecc = flexonenand_ooblayout_ecc, - .free = flexonenand_ooblayout_free, -}; - -/* - * onenand_oob_128 - oob info for OneNAND with 4KB page - * - * Based on specification: - * 4Gb M-die OneNAND Flash (KFM4G16Q4M, KFN8G16Q4M). Rev. 1.3, Apr. 2010 - * - */ -static int onenand_ooblayout_128_ecc(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - if (section > 7) - return -ERANGE; - - oobregion->offset = (section * 16) + 7; - oobregion->length = 9; - - return 0; -} - -static int onenand_ooblayout_128_free(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - if (section >= 8) - return -ERANGE; - - /* - * free bytes are using the spare area fields marked as - * "Managed by internal ECC logic for Logical Sector Number area" - */ - oobregion->offset = (section * 16) + 2; - oobregion->length = 3; - - return 0; -} - -static const struct mtd_ooblayout_ops onenand_oob_128_ooblayout_ops = { - .ecc = onenand_ooblayout_128_ecc, - .free = onenand_ooblayout_128_free, -}; - -/** - * onenand_oob_32_64 - oob info for large (2KB) page - */ -static int onenand_ooblayout_32_64_ecc(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - if (section > 3) - return -ERANGE; - - oobregion->offset = (section * 16) + 8; - oobregion->length = 5; - - return 0; -} - -static int onenand_ooblayout_32_64_free(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - int sections = (mtd->oobsize / 32) * 2; - - if (section >= sections) - return -ERANGE; - - if (section & 1) { - oobregion->offset = ((section - 1) * 16) + 14; - oobregion->length = 2; - } else { - oobregion->offset = (section * 16) + 2; - oobregion->length = 3; - } - - return 0; -} - -static const struct mtd_ooblayout_ops onenand_oob_32_64_ooblayout_ops = { - .ecc = onenand_ooblayout_32_64_ecc, - .free = onenand_ooblayout_32_64_free, -}; - -static const unsigned char ffchars[] = { - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 16 */ - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 32 */ - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 48 */ - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 64 */ - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 80 */ - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 96 */ - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 112 */ - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 128 */ -}; - -/** - * onenand_readw - [OneNAND Interface] Read OneNAND register - * @param addr address to read - * - * Read OneNAND register - */ -static unsigned short onenand_readw(void __iomem *addr) -{ - return readw(addr); -} - -/** - * onenand_writew - [OneNAND Interface] Write OneNAND register with value - * @param value value to write - * @param addr address to write - * - * Write OneNAND register with value - */ -static void onenand_writew(unsigned short value, void __iomem *addr) -{ - writew(value, addr); -} - -/** - * onenand_block_address - [DEFAULT] Get block address - * @param this onenand chip data structure - * @param block the block - * @return translated block address if DDP, otherwise same - * - * Setup Start Address 1 Register (F100h) - */ -static int onenand_block_address(struct onenand_chip *this, int block) -{ - /* Device Flash Core select, NAND Flash Block Address */ - if (block & this->density_mask) - return ONENAND_DDP_CHIP1 | (block ^ this->density_mask); - - return block; -} - -/** - * onenand_bufferram_address - [DEFAULT] Get bufferram address - * @param this onenand chip data structure - * @param block the block - * @return set DBS value if DDP, otherwise 0 - * - * Setup Start Address 2 Register (F101h) for DDP - */ -static int onenand_bufferram_address(struct onenand_chip *this, int block) -{ - /* Device BufferRAM Select */ - if (block & this->density_mask) - return ONENAND_DDP_CHIP1; - - return ONENAND_DDP_CHIP0; -} - -/** - * onenand_page_address - [DEFAULT] Get page address - * @param page the page address - * @param sector the sector address - * @return combined page and sector address - * - * Setup Start Address 8 Register (F107h) - */ -static int onenand_page_address(int page, int sector) -{ - /* Flash Page Address, Flash Sector Address */ - int fpa, fsa; - - fpa = page & ONENAND_FPA_MASK; - fsa = sector & ONENAND_FSA_MASK; - - return ((fpa << ONENAND_FPA_SHIFT) | fsa); -} - -/** - * onenand_buffer_address - [DEFAULT] Get buffer address - * @param dataram1 DataRAM index - * @param sectors the sector address - * @param count the number of sectors - * @return the start buffer value - * - * Setup Start Buffer Register (F200h) - */ -static int onenand_buffer_address(int dataram1, int sectors, int count) -{ - int bsa, bsc; - - /* BufferRAM Sector Address */ - bsa = sectors & ONENAND_BSA_MASK; - - if (dataram1) - bsa |= ONENAND_BSA_DATARAM1; /* DataRAM1 */ - else - bsa |= ONENAND_BSA_DATARAM0; /* DataRAM0 */ - - /* BufferRAM Sector Count */ - bsc = count & ONENAND_BSC_MASK; - - return ((bsa << ONENAND_BSA_SHIFT) | bsc); -} - -/** - * flexonenand_block- For given address return block number - * @param this - OneNAND device structure - * @param addr - Address for which block number is needed - */ -static unsigned flexonenand_block(struct onenand_chip *this, loff_t addr) -{ - unsigned boundary, blk, die = 0; - - if (ONENAND_IS_DDP(this) && addr >= this->diesize[0]) { - die = 1; - addr -= this->diesize[0]; - } - - boundary = this->boundary[die]; - - blk = addr >> (this->erase_shift - 1); - if (blk > boundary) - blk = (blk + boundary + 1) >> 1; - - blk += die ? this->density_mask : 0; - return blk; -} - -inline unsigned onenand_block(struct onenand_chip *this, loff_t addr) -{ - if (!FLEXONENAND(this)) - return addr >> this->erase_shift; - return flexonenand_block(this, addr); -} - -/** - * flexonenand_addr - Return address of the block - * @this: OneNAND device structure - * @block: Block number on Flex-OneNAND - * - * Return address of the block - */ -static loff_t flexonenand_addr(struct onenand_chip *this, int block) -{ - loff_t ofs = 0; - int die = 0, boundary; - - if (ONENAND_IS_DDP(this) && block >= this->density_mask) { - block -= this->density_mask; - die = 1; - ofs = this->diesize[0]; - } - - boundary = this->boundary[die]; - ofs += (loff_t)block << (this->erase_shift - 1); - if (block > (boundary + 1)) - ofs += (loff_t)(block - boundary - 1) << (this->erase_shift - 1); - return ofs; -} - -loff_t onenand_addr(struct onenand_chip *this, int block) -{ - if (!FLEXONENAND(this)) - return (loff_t)block << this->erase_shift; - return flexonenand_addr(this, block); -} -EXPORT_SYMBOL(onenand_addr); - -/** - * onenand_get_density - [DEFAULT] Get OneNAND density - * @param dev_id OneNAND device ID - * - * Get OneNAND density from device ID - */ -static inline int onenand_get_density(int dev_id) -{ - int density = dev_id >> ONENAND_DEVICE_DENSITY_SHIFT; - return (density & ONENAND_DEVICE_DENSITY_MASK); -} - -/** - * flexonenand_region - [Flex-OneNAND] Return erase region of addr - * @param mtd MTD device structure - * @param addr address whose erase region needs to be identified - */ -int flexonenand_region(struct mtd_info *mtd, loff_t addr) -{ - int i; - - for (i = 0; i < mtd->numeraseregions; i++) - if (addr < mtd->eraseregions[i].offset) - break; - return i - 1; -} -EXPORT_SYMBOL(flexonenand_region); - -/** - * onenand_command - [DEFAULT] Send command to OneNAND device - * @param mtd MTD device structure - * @param cmd the command to be sent - * @param addr offset to read from or write to - * @param len number of bytes to read or write - * - * Send command to OneNAND device. This function is used for middle/large page - * devices (1KB/2KB Bytes per page) - */ -static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t len) -{ - struct onenand_chip *this = mtd->priv; - int value, block, page; - - /* Address translation */ - switch (cmd) { - case ONENAND_CMD_UNLOCK: - case ONENAND_CMD_LOCK: - case ONENAND_CMD_LOCK_TIGHT: - case ONENAND_CMD_UNLOCK_ALL: - block = -1; - page = -1; - break; - - case FLEXONENAND_CMD_PI_ACCESS: - /* addr contains die index */ - block = addr * this->density_mask; - page = -1; - break; - - case ONENAND_CMD_ERASE: - case ONENAND_CMD_MULTIBLOCK_ERASE: - case ONENAND_CMD_ERASE_VERIFY: - case ONENAND_CMD_BUFFERRAM: - case ONENAND_CMD_OTP_ACCESS: - block = onenand_block(this, addr); - page = -1; - break; - - case FLEXONENAND_CMD_READ_PI: - cmd = ONENAND_CMD_READ; - block = addr * this->density_mask; - page = 0; - break; - - default: - block = onenand_block(this, addr); - if (FLEXONENAND(this)) - page = (int) (addr - onenand_addr(this, block))>>\ - this->page_shift; - else - page = (int) (addr >> this->page_shift); - if (ONENAND_IS_2PLANE(this)) { - /* Make the even block number */ - block &= ~1; - /* Is it the odd plane? */ - if (addr & this->writesize) - block++; - page >>= 1; - } - page &= this->page_mask; - break; - } - - /* NOTE: The setting order of the registers is very important! */ - if (cmd == ONENAND_CMD_BUFFERRAM) { - /* Select DataRAM for DDP */ - value = onenand_bufferram_address(this, block); - this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2); - - if (ONENAND_IS_2PLANE(this) || ONENAND_IS_4KB_PAGE(this)) - /* It is always BufferRAM0 */ - ONENAND_SET_BUFFERRAM0(this); - else - /* Switch to the next data buffer */ - ONENAND_SET_NEXT_BUFFERRAM(this); - - return 0; - } - - if (block != -1) { - /* Write 'DFS, FBA' of Flash */ - value = onenand_block_address(this, block); - this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1); - - /* Select DataRAM for DDP */ - value = onenand_bufferram_address(this, block); - this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2); - } - - if (page != -1) { - /* Now we use page size operation */ - int sectors = 0, count = 0; - int dataram; - - switch (cmd) { - case FLEXONENAND_CMD_RECOVER_LSB: - case ONENAND_CMD_READ: - case ONENAND_CMD_READOOB: - if (ONENAND_IS_4KB_PAGE(this)) - /* It is always BufferRAM0 */ - dataram = ONENAND_SET_BUFFERRAM0(this); - else - dataram = ONENAND_SET_NEXT_BUFFERRAM(this); - break; - - default: - if (ONENAND_IS_2PLANE(this) && cmd == ONENAND_CMD_PROG) - cmd = ONENAND_CMD_2X_PROG; - dataram = ONENAND_CURRENT_BUFFERRAM(this); - break; - } - - /* Write 'FPA, FSA' of Flash */ - value = onenand_page_address(page, sectors); - this->write_word(value, this->base + ONENAND_REG_START_ADDRESS8); - - /* Write 'BSA, BSC' of DataRAM */ - value = onenand_buffer_address(dataram, sectors, count); - this->write_word(value, this->base + ONENAND_REG_START_BUFFER); - } - - /* Interrupt clear */ - this->write_word(ONENAND_INT_CLEAR, this->base + ONENAND_REG_INTERRUPT); - - /* Write command */ - this->write_word(cmd, this->base + ONENAND_REG_COMMAND); - - return 0; -} - -/** - * onenand_read_ecc - return ecc status - * @param this onenand chip structure - */ -static inline int onenand_read_ecc(struct onenand_chip *this) -{ - int ecc, i, result = 0; - - if (!FLEXONENAND(this) && !ONENAND_IS_4KB_PAGE(this)) - return this->read_word(this->base + ONENAND_REG_ECC_STATUS); - - for (i = 0; i < 4; i++) { - ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS + i*2); - if (likely(!ecc)) - continue; - if (ecc & FLEXONENAND_UNCORRECTABLE_ERROR) - return ONENAND_ECC_2BIT_ALL; - else - result = ONENAND_ECC_1BIT_ALL; - } - - return result; -} - -/** - * onenand_wait - [DEFAULT] wait until the command is done - * @param mtd MTD device structure - * @param state state to select the max. timeout value - * - * Wait for command done. This applies to all OneNAND command - * Read can take up to 30us, erase up to 2ms and program up to 350us - * according to general OneNAND specs - */ -static int onenand_wait(struct mtd_info *mtd, int state) -{ - struct onenand_chip * this = mtd->priv; - unsigned long timeout; - unsigned int flags = ONENAND_INT_MASTER; - unsigned int interrupt = 0; - unsigned int ctrl; - - /* The 20 msec is enough */ - timeout = jiffies + msecs_to_jiffies(20); - while (time_before(jiffies, timeout)) { - interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT); - - if (interrupt & flags) - break; - - if (state != FL_READING && state != FL_PREPARING_ERASE) - cond_resched(); - } - /* To get correct interrupt status in timeout case */ - interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT); - - ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS); - - /* - * In the Spec. it checks the controller status first - * However if you get the correct information in case of - * power off recovery (POR) test, it should read ECC status first - */ - if (interrupt & ONENAND_INT_READ) { - int ecc = onenand_read_ecc(this); - if (ecc) { - if (ecc & ONENAND_ECC_2BIT_ALL) { - printk(KERN_ERR "%s: ECC error = 0x%04x\n", - __func__, ecc); - mtd->ecc_stats.failed++; - return -EBADMSG; - } else if (ecc & ONENAND_ECC_1BIT_ALL) { - printk(KERN_DEBUG "%s: correctable ECC error = 0x%04x\n", - __func__, ecc); - mtd->ecc_stats.corrected++; - } - } - } else if (state == FL_READING) { - printk(KERN_ERR "%s: read timeout! ctrl=0x%04x intr=0x%04x\n", - __func__, ctrl, interrupt); - return -EIO; - } - - if (state == FL_PREPARING_ERASE && !(interrupt & ONENAND_INT_ERASE)) { - printk(KERN_ERR "%s: mb erase timeout! ctrl=0x%04x intr=0x%04x\n", - __func__, ctrl, interrupt); - return -EIO; - } - - if (!(interrupt & ONENAND_INT_MASTER)) { - printk(KERN_ERR "%s: timeout! ctrl=0x%04x intr=0x%04x\n", - __func__, ctrl, interrupt); - return -EIO; - } - - /* If there's controller error, it's a real error */ - if (ctrl & ONENAND_CTRL_ERROR) { - printk(KERN_ERR "%s: controller error = 0x%04x\n", - __func__, ctrl); - if (ctrl & ONENAND_CTRL_LOCK) - printk(KERN_ERR "%s: it's locked error.\n", __func__); - return -EIO; - } - - return 0; -} - -/* - * onenand_interrupt - [DEFAULT] onenand interrupt handler - * @param irq onenand interrupt number - * @param dev_id interrupt data - * - * complete the work - */ -static irqreturn_t onenand_interrupt(int irq, void *data) -{ - struct onenand_chip *this = data; - - /* To handle shared interrupt */ - if (!this->complete.done) - complete(&this->complete); - - return IRQ_HANDLED; -} - -/* - * onenand_interrupt_wait - [DEFAULT] wait until the command is done - * @param mtd MTD device structure - * @param state state to select the max. timeout value - * - * Wait for command done. - */ -static int onenand_interrupt_wait(struct mtd_info *mtd, int state) -{ - struct onenand_chip *this = mtd->priv; - - wait_for_completion(&this->complete); - - return onenand_wait(mtd, state); -} - -/* - * onenand_try_interrupt_wait - [DEFAULT] try interrupt wait - * @param mtd MTD device structure - * @param state state to select the max. timeout value - * - * Try interrupt based wait (It is used one-time) - */ -static int onenand_try_interrupt_wait(struct mtd_info *mtd, int state) -{ - struct onenand_chip *this = mtd->priv; - unsigned long remain, timeout; - - /* We use interrupt wait first */ - this->wait = onenand_interrupt_wait; - - timeout = msecs_to_jiffies(100); - remain = wait_for_completion_timeout(&this->complete, timeout); - if (!remain) { - printk(KERN_INFO "OneNAND: There's no interrupt. " - "We use the normal wait\n"); - - /* Release the irq */ - free_irq(this->irq, this); - - this->wait = onenand_wait; - } - - return onenand_wait(mtd, state); -} - -/* - * onenand_setup_wait - [OneNAND Interface] setup onenand wait method - * @param mtd MTD device structure - * - * There's two method to wait onenand work - * 1. polling - read interrupt status register - * 2. interrupt - use the kernel interrupt method - */ -static void onenand_setup_wait(struct mtd_info *mtd) -{ - struct onenand_chip *this = mtd->priv; - int syscfg; - - init_completion(&this->complete); - - if (this->irq <= 0) { - this->wait = onenand_wait; - return; - } - - if (request_irq(this->irq, &onenand_interrupt, - IRQF_SHARED, "onenand", this)) { - /* If we can't get irq, use the normal wait */ - this->wait = onenand_wait; - return; - } - - /* Enable interrupt */ - syscfg = this->read_word(this->base + ONENAND_REG_SYS_CFG1); - syscfg |= ONENAND_SYS_CFG1_IOBE; - this->write_word(syscfg, this->base + ONENAND_REG_SYS_CFG1); - - this->wait = onenand_try_interrupt_wait; -} - -/** - * onenand_bufferram_offset - [DEFAULT] BufferRAM offset - * @param mtd MTD data structure - * @param area BufferRAM area - * @return offset given area - * - * Return BufferRAM offset given area - */ -static inline int onenand_bufferram_offset(struct mtd_info *mtd, int area) -{ - struct onenand_chip *this = mtd->priv; - - if (ONENAND_CURRENT_BUFFERRAM(this)) { - /* Note: the 'this->writesize' is a real page size */ - if (area == ONENAND_DATARAM) - return this->writesize; - if (area == ONENAND_SPARERAM) - return mtd->oobsize; - } - - return 0; -} - -/** - * onenand_read_bufferram - [OneNAND Interface] Read the bufferram area - * @param mtd MTD data structure - * @param area BufferRAM area - * @param buffer the databuffer to put/get data - * @param offset offset to read from or write to - * @param count number of bytes to read/write - * - * Read the BufferRAM area - */ -static int onenand_read_bufferram(struct mtd_info *mtd, int area, - unsigned char *buffer, int offset, size_t count) -{ - struct onenand_chip *this = mtd->priv; - void __iomem *bufferram; - - bufferram = this->base + area; - - bufferram += onenand_bufferram_offset(mtd, area); - - if (ONENAND_CHECK_BYTE_ACCESS(count)) { - unsigned short word; - - /* Align with word(16-bit) size */ - count--; - - /* Read word and save byte */ - word = this->read_word(bufferram + offset + count); - buffer[count] = (word & 0xff); - } - - memcpy(buffer, bufferram + offset, count); - - return 0; -} - -/** - * onenand_sync_read_bufferram - [OneNAND Interface] Read the bufferram area with Sync. Burst mode - * @param mtd MTD data structure - * @param area BufferRAM area - * @param buffer the databuffer to put/get data - * @param offset offset to read from or write to - * @param count number of bytes to read/write - * - * Read the BufferRAM area with Sync. Burst Mode - */ -static int onenand_sync_read_bufferram(struct mtd_info *mtd, int area, - unsigned char *buffer, int offset, size_t count) -{ - struct onenand_chip *this = mtd->priv; - void __iomem *bufferram; - - bufferram = this->base + area; - - bufferram += onenand_bufferram_offset(mtd, area); - - this->mmcontrol(mtd, ONENAND_SYS_CFG1_SYNC_READ); - - if (ONENAND_CHECK_BYTE_ACCESS(count)) { - unsigned short word; - - /* Align with word(16-bit) size */ - count--; - - /* Read word and save byte */ - word = this->read_word(bufferram + offset + count); - buffer[count] = (word & 0xff); - } - - memcpy(buffer, bufferram + offset, count); - - this->mmcontrol(mtd, 0); - - return 0; -} - -/** - * onenand_write_bufferram - [OneNAND Interface] Write the bufferram area - * @param mtd MTD data structure - * @param area BufferRAM area - * @param buffer the databuffer to put/get data - * @param offset offset to read from or write to - * @param count number of bytes to read/write - * - * Write the BufferRAM area - */ -static int onenand_write_bufferram(struct mtd_info *mtd, int area, - const unsigned char *buffer, int offset, size_t count) -{ - struct onenand_chip *this = mtd->priv; - void __iomem *bufferram; - - bufferram = this->base + area; - - bufferram += onenand_bufferram_offset(mtd, area); - - if (ONENAND_CHECK_BYTE_ACCESS(count)) { - unsigned short word; - int byte_offset; - - /* Align with word(16-bit) size */ - count--; - - /* Calculate byte access offset */ - byte_offset = offset + count; - - /* Read word and save byte */ - word = this->read_word(bufferram + byte_offset); - word = (word & ~0xff) | buffer[count]; - this->write_word(word, bufferram + byte_offset); - } - - memcpy(bufferram + offset, buffer, count); - - return 0; -} - -/** - * onenand_get_2x_blockpage - [GENERIC] Get blockpage at 2x program mode - * @param mtd MTD data structure - * @param addr address to check - * @return blockpage address - * - * Get blockpage address at 2x program mode - */ -static int onenand_get_2x_blockpage(struct mtd_info *mtd, loff_t addr) -{ - struct onenand_chip *this = mtd->priv; - int blockpage, block, page; - - /* Calculate the even block number */ - block = (int) (addr >> this->erase_shift) & ~1; - /* Is it the odd plane? */ - if (addr & this->writesize) - block++; - page = (int) (addr >> (this->page_shift + 1)) & this->page_mask; - blockpage = (block << 7) | page; - - return blockpage; -} - -/** - * onenand_check_bufferram - [GENERIC] Check BufferRAM information - * @param mtd MTD data structure - * @param addr address to check - * @return 1 if there are valid data, otherwise 0 - * - * Check bufferram if there is data we required - */ -static int onenand_check_bufferram(struct mtd_info *mtd, loff_t addr) -{ - struct onenand_chip *this = mtd->priv; - int blockpage, found = 0; - unsigned int i; - - if (ONENAND_IS_2PLANE(this)) - blockpage = onenand_get_2x_blockpage(mtd, addr); - else - blockpage = (int) (addr >> this->page_shift); - - /* Is there valid data? */ - i = ONENAND_CURRENT_BUFFERRAM(this); - if (this->bufferram[i].blockpage == blockpage) - found = 1; - else { - /* Check another BufferRAM */ - i = ONENAND_NEXT_BUFFERRAM(this); - if (this->bufferram[i].blockpage == blockpage) { - ONENAND_SET_NEXT_BUFFERRAM(this); - found = 1; - } - } - - if (found && ONENAND_IS_DDP(this)) { - /* Select DataRAM for DDP */ - int block = onenand_block(this, addr); - int value = onenand_bufferram_address(this, block); - this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2); - } - - return found; -} - -/** - * onenand_update_bufferram - [GENERIC] Update BufferRAM information - * @param mtd MTD data structure - * @param addr address to update - * @param valid valid flag - * - * Update BufferRAM information - */ -static void onenand_update_bufferram(struct mtd_info *mtd, loff_t addr, - int valid) -{ - struct onenand_chip *this = mtd->priv; - int blockpage; - unsigned int i; - - if (ONENAND_IS_2PLANE(this)) - blockpage = onenand_get_2x_blockpage(mtd, addr); - else - blockpage = (int) (addr >> this->page_shift); - - /* Invalidate another BufferRAM */ - i = ONENAND_NEXT_BUFFERRAM(this); - if (this->bufferram[i].blockpage == blockpage) - this->bufferram[i].blockpage = -1; - - /* Update BufferRAM */ - i = ONENAND_CURRENT_BUFFERRAM(this); - if (valid) - this->bufferram[i].blockpage = blockpage; - else - this->bufferram[i].blockpage = -1; -} - -/** - * onenand_invalidate_bufferram - [GENERIC] Invalidate BufferRAM information - * @param mtd MTD data structure - * @param addr start address to invalidate - * @param len length to invalidate - * - * Invalidate BufferRAM information - */ -static void onenand_invalidate_bufferram(struct mtd_info *mtd, loff_t addr, - unsigned int len) -{ - struct onenand_chip *this = mtd->priv; - int i; - loff_t end_addr = addr + len; - - /* Invalidate BufferRAM */ - for (i = 0; i < MAX_BUFFERRAM; i++) { - loff_t buf_addr = this->bufferram[i].blockpage << this->page_shift; - if (buf_addr >= addr && buf_addr < end_addr) - this->bufferram[i].blockpage = -1; - } -} - -/** - * onenand_get_device - [GENERIC] Get chip for selected access - * @param mtd MTD device structure - * @param new_state the state which is requested - * - * Get the device and lock it for exclusive access - */ -static int onenand_get_device(struct mtd_info *mtd, int new_state) -{ - struct onenand_chip *this = mtd->priv; - DECLARE_WAITQUEUE(wait, current); - - /* - * Grab the lock and see if the device is available - */ - while (1) { - spin_lock(&this->chip_lock); - if (this->state == FL_READY) { - this->state = new_state; - spin_unlock(&this->chip_lock); - if (new_state != FL_PM_SUSPENDED && this->enable) - this->enable(mtd); - break; - } - if (new_state == FL_PM_SUSPENDED) { - spin_unlock(&this->chip_lock); - return (this->state == FL_PM_SUSPENDED) ? 0 : -EAGAIN; - } - set_current_state(TASK_UNINTERRUPTIBLE); - add_wait_queue(&this->wq, &wait); - spin_unlock(&this->chip_lock); - schedule(); - remove_wait_queue(&this->wq, &wait); - } - - return 0; -} - -/** - * onenand_release_device - [GENERIC] release chip - * @param mtd MTD device structure - * - * Deselect, release chip lock and wake up anyone waiting on the device - */ -static void onenand_release_device(struct mtd_info *mtd) -{ - struct onenand_chip *this = mtd->priv; - - if (this->state != FL_PM_SUSPENDED && this->disable) - this->disable(mtd); - /* Release the chip */ - spin_lock(&this->chip_lock); - this->state = FL_READY; - wake_up(&this->wq); - spin_unlock(&this->chip_lock); -} - -/** - * onenand_transfer_auto_oob - [INTERN] oob auto-placement transfer - * @param mtd MTD device structure - * @param buf destination address - * @param column oob offset to read from - * @param thislen oob length to read - */ -static int onenand_transfer_auto_oob(struct mtd_info *mtd, uint8_t *buf, int column, - int thislen) -{ - struct onenand_chip *this = mtd->priv; - int ret; - - this->read_bufferram(mtd, ONENAND_SPARERAM, this->oob_buf, 0, - mtd->oobsize); - ret = mtd_ooblayout_get_databytes(mtd, buf, this->oob_buf, - column, thislen); - if (ret) - return ret; - - return 0; -} - -/** - * onenand_recover_lsb - [Flex-OneNAND] Recover LSB page data - * @param mtd MTD device structure - * @param addr address to recover - * @param status return value from onenand_wait / onenand_bbt_wait - * - * MLC NAND Flash cell has paired pages - LSB page and MSB page. LSB page has - * lower page address and MSB page has higher page address in paired pages. - * If power off occurs during MSB page program, the paired LSB page data can - * become corrupt. LSB page recovery read is a way to read LSB page though page - * data are corrupted. When uncorrectable error occurs as a result of LSB page - * read after power up, issue LSB page recovery read. - */ -static int onenand_recover_lsb(struct mtd_info *mtd, loff_t addr, int status) -{ - struct onenand_chip *this = mtd->priv; - int i; - - /* Recovery is only for Flex-OneNAND */ - if (!FLEXONENAND(this)) - return status; - - /* check if we failed due to uncorrectable error */ - if (!mtd_is_eccerr(status) && status != ONENAND_BBT_READ_ECC_ERROR) - return status; - - /* check if address lies in MLC region */ - i = flexonenand_region(mtd, addr); - if (mtd->eraseregions[i].erasesize < (1 << this->erase_shift)) - return status; - - /* We are attempting to reread, so decrement stats.failed - * which was incremented by onenand_wait due to read failure - */ - printk(KERN_INFO "%s: Attempting to recover from uncorrectable read\n", - __func__); - mtd->ecc_stats.failed--; - - /* Issue the LSB page recovery command */ - this->command(mtd, FLEXONENAND_CMD_RECOVER_LSB, addr, this->writesize); - return this->wait(mtd, FL_READING); -} - -/** - * onenand_mlc_read_ops_nolock - MLC OneNAND read main and/or out-of-band - * @param mtd MTD device structure - * @param from offset to read from - * @param ops: oob operation description structure - * - * MLC OneNAND / Flex-OneNAND has 4KB page size and 4KB dataram. - * So, read-while-load is not present. - */ -static int onenand_mlc_read_ops_nolock(struct mtd_info *mtd, loff_t from, - struct mtd_oob_ops *ops) -{ - struct onenand_chip *this = mtd->priv; - struct mtd_ecc_stats stats; - size_t len = ops->len; - size_t ooblen = ops->ooblen; - u_char *buf = ops->datbuf; - u_char *oobbuf = ops->oobbuf; - int read = 0, column, thislen; - int oobread = 0, oobcolumn, thisooblen, oobsize; - int ret = 0; - int writesize = this->writesize; - - pr_debug("%s: from = 0x%08x, len = %i\n", __func__, (unsigned int)from, - (int)len); - - oobsize = mtd_oobavail(mtd, ops); - oobcolumn = from & (mtd->oobsize - 1); - - /* Do not allow reads past end of device */ - if (from + len > mtd->size) { - printk(KERN_ERR "%s: Attempt read beyond end of device\n", - __func__); - ops->retlen = 0; - ops->oobretlen = 0; - return -EINVAL; - } - - stats = mtd->ecc_stats; - - while (read < len) { - cond_resched(); - - thislen = min_t(int, writesize, len - read); - - column = from & (writesize - 1); - if (column + thislen > writesize) - thislen = writesize - column; - - if (!onenand_check_bufferram(mtd, from)) { - this->command(mtd, ONENAND_CMD_READ, from, writesize); - - ret = this->wait(mtd, FL_READING); - if (unlikely(ret)) - ret = onenand_recover_lsb(mtd, from, ret); - onenand_update_bufferram(mtd, from, !ret); - if (mtd_is_eccerr(ret)) - ret = 0; - if (ret) - break; - } - - this->read_bufferram(mtd, ONENAND_DATARAM, buf, column, thislen); - if (oobbuf) { - thisooblen = oobsize - oobcolumn; - thisooblen = min_t(int, thisooblen, ooblen - oobread); - - if (ops->mode == MTD_OPS_AUTO_OOB) - onenand_transfer_auto_oob(mtd, oobbuf, oobcolumn, thisooblen); - else - this->read_bufferram(mtd, ONENAND_SPARERAM, oobbuf, oobcolumn, thisooblen); - oobread += thisooblen; - oobbuf += thisooblen; - oobcolumn = 0; - } - - read += thislen; - if (read == len) - break; - - from += thislen; - buf += thislen; - } - - /* - * Return success, if no ECC failures, else -EBADMSG - * fs driver will take care of that, because - * retlen == desired len and result == -EBADMSG - */ - ops->retlen = read; - ops->oobretlen = oobread; - - if (ret) - return ret; - - if (mtd->ecc_stats.failed - stats.failed) - return -EBADMSG; - - /* return max bitflips per ecc step; ONENANDs correct 1 bit only */ - return mtd->ecc_stats.corrected != stats.corrected ? 1 : 0; -} - -/** - * onenand_read_ops_nolock - [OneNAND Interface] OneNAND read main and/or out-of-band - * @param mtd MTD device structure - * @param from offset to read from - * @param ops: oob operation description structure - * - * OneNAND read main and/or out-of-band data - */ -static int onenand_read_ops_nolock(struct mtd_info *mtd, loff_t from, - struct mtd_oob_ops *ops) -{ - struct onenand_chip *this = mtd->priv; - struct mtd_ecc_stats stats; - size_t len = ops->len; - size_t ooblen = ops->ooblen; - u_char *buf = ops->datbuf; - u_char *oobbuf = ops->oobbuf; - int read = 0, column, thislen; - int oobread = 0, oobcolumn, thisooblen, oobsize; - int ret = 0, boundary = 0; - int writesize = this->writesize; - - pr_debug("%s: from = 0x%08x, len = %i\n", __func__, (unsigned int)from, - (int)len); - - oobsize = mtd_oobavail(mtd, ops); - oobcolumn = from & (mtd->oobsize - 1); - - /* Do not allow reads past end of device */ - if ((from + len) > mtd->size) { - printk(KERN_ERR "%s: Attempt read beyond end of device\n", - __func__); - ops->retlen = 0; - ops->oobretlen = 0; - return -EINVAL; - } - - stats = mtd->ecc_stats; - - /* Read-while-load method */ - - /* Do first load to bufferRAM */ - if (read < len) { - if (!onenand_check_bufferram(mtd, from)) { - this->command(mtd, ONENAND_CMD_READ, from, writesize); - ret = this->wait(mtd, FL_READING); - onenand_update_bufferram(mtd, from, !ret); - if (mtd_is_eccerr(ret)) - ret = 0; - } - } - - thislen = min_t(int, writesize, len - read); - column = from & (writesize - 1); - if (column + thislen > writesize) - thislen = writesize - column; - - while (!ret) { - /* If there is more to load then start next load */ - from += thislen; - if (read + thislen < len) { - this->command(mtd, ONENAND_CMD_READ, from, writesize); - /* - * Chip boundary handling in DDP - * Now we issued chip 1 read and pointed chip 1 - * bufferram so we have to point chip 0 bufferram. - */ - if (ONENAND_IS_DDP(this) && - unlikely(from == (this->chipsize >> 1))) { - this->write_word(ONENAND_DDP_CHIP0, this->base + ONENAND_REG_START_ADDRESS2); - boundary = 1; - } else - boundary = 0; - ONENAND_SET_PREV_BUFFERRAM(this); - } - /* While load is going, read from last bufferRAM */ - this->read_bufferram(mtd, ONENAND_DATARAM, buf, column, thislen); - - /* Read oob area if needed */ - if (oobbuf) { - thisooblen = oobsize - oobcolumn; - thisooblen = min_t(int, thisooblen, ooblen - oobread); - - if (ops->mode == MTD_OPS_AUTO_OOB) - onenand_transfer_auto_oob(mtd, oobbuf, oobcolumn, thisooblen); - else - this->read_bufferram(mtd, ONENAND_SPARERAM, oobbuf, oobcolumn, thisooblen); - oobread += thisooblen; - oobbuf += thisooblen; - oobcolumn = 0; - } - - /* See if we are done */ - read += thislen; - if (read == len) - break; - /* Set up for next read from bufferRAM */ - if (unlikely(boundary)) - this->write_word(ONENAND_DDP_CHIP1, this->base + ONENAND_REG_START_ADDRESS2); - ONENAND_SET_NEXT_BUFFERRAM(this); - buf += thislen; - thislen = min_t(int, writesize, len - read); - column = 0; - cond_resched(); - /* Now wait for load */ - ret = this->wait(mtd, FL_READING); - onenand_update_bufferram(mtd, from, !ret); - if (mtd_is_eccerr(ret)) - ret = 0; - } - - /* - * Return success, if no ECC failures, else -EBADMSG - * fs driver will take care of that, because - * retlen == desired len and result == -EBADMSG - */ - ops->retlen = read; - ops->oobretlen = oobread; - - if (ret) - return ret; - - if (mtd->ecc_stats.failed - stats.failed) - return -EBADMSG; - - /* return max bitflips per ecc step; ONENANDs correct 1 bit only */ - return mtd->ecc_stats.corrected != stats.corrected ? 1 : 0; -} - -/** - * onenand_read_oob_nolock - [MTD Interface] OneNAND read out-of-band - * @param mtd MTD device structure - * @param from offset to read from - * @param ops: oob operation description structure - * - * OneNAND read out-of-band data from the spare area - */ -static int onenand_read_oob_nolock(struct mtd_info *mtd, loff_t from, - struct mtd_oob_ops *ops) -{ - struct onenand_chip *this = mtd->priv; - struct mtd_ecc_stats stats; - int read = 0, thislen, column, oobsize; - size_t len = ops->ooblen; - unsigned int mode = ops->mode; - u_char *buf = ops->oobbuf; - int ret = 0, readcmd; - - from += ops->ooboffs; - - pr_debug("%s: from = 0x%08x, len = %i\n", __func__, (unsigned int)from, - (int)len); - - /* Initialize return length value */ - ops->oobretlen = 0; - - if (mode == MTD_OPS_AUTO_OOB) - oobsize = mtd->oobavail; - else - oobsize = mtd->oobsize; - - column = from & (mtd->oobsize - 1); - - if (unlikely(column >= oobsize)) { - printk(KERN_ERR "%s: Attempted to start read outside oob\n", - __func__); - return -EINVAL; - } - - stats = mtd->ecc_stats; - - readcmd = ONENAND_IS_4KB_PAGE(this) ? ONENAND_CMD_READ : ONENAND_CMD_READOOB; - - while (read < len) { - cond_resched(); - - thislen = oobsize - column; - thislen = min_t(int, thislen, len); - - this->command(mtd, readcmd, from, mtd->oobsize); - - onenand_update_bufferram(mtd, from, 0); - - ret = this->wait(mtd, FL_READING); - if (unlikely(ret)) - ret = onenand_recover_lsb(mtd, from, ret); - - if (ret && !mtd_is_eccerr(ret)) { - printk(KERN_ERR "%s: read failed = 0x%x\n", - __func__, ret); - break; - } - - if (mode == MTD_OPS_AUTO_OOB) - onenand_transfer_auto_oob(mtd, buf, column, thislen); - else - this->read_bufferram(mtd, ONENAND_SPARERAM, buf, column, thislen); - - read += thislen; - - if (read == len) - break; - - buf += thislen; - - /* Read more? */ - if (read < len) { - /* Page size */ - from += mtd->writesize; - column = 0; - } - } - - ops->oobretlen = read; - - if (ret) - return ret; - - if (mtd->ecc_stats.failed - stats.failed) - return -EBADMSG; - - return 0; -} - -/** - * onenand_read_oob - [MTD Interface] Read main and/or out-of-band - * @param mtd: MTD device structure - * @param from: offset to read from - * @param ops: oob operation description structure - - * Read main and/or out-of-band - */ -static int onenand_read_oob(struct mtd_info *mtd, loff_t from, - struct mtd_oob_ops *ops) -{ - struct onenand_chip *this = mtd->priv; - int ret; - - switch (ops->mode) { - case MTD_OPS_PLACE_OOB: - case MTD_OPS_AUTO_OOB: - break; - case MTD_OPS_RAW: - /* Not implemented yet */ - default: - return -EINVAL; - } - - onenand_get_device(mtd, FL_READING); - if (ops->datbuf) - ret = ONENAND_IS_4KB_PAGE(this) ? - onenand_mlc_read_ops_nolock(mtd, from, ops) : - onenand_read_ops_nolock(mtd, from, ops); - else - ret = onenand_read_oob_nolock(mtd, from, ops); - onenand_release_device(mtd); - - return ret; -} - -/** - * onenand_bbt_wait - [DEFAULT] wait until the command is done - * @param mtd MTD device structure - * @param state state to select the max. timeout value - * - * Wait for command done. - */ -static int onenand_bbt_wait(struct mtd_info *mtd, int state) -{ - struct onenand_chip *this = mtd->priv; - unsigned long timeout; - unsigned int interrupt, ctrl, ecc, addr1, addr8; - - /* The 20 msec is enough */ - timeout = jiffies + msecs_to_jiffies(20); - while (time_before(jiffies, timeout)) { - interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT); - if (interrupt & ONENAND_INT_MASTER) - break; - } - /* To get correct interrupt status in timeout case */ - interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT); - ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS); - addr1 = this->read_word(this->base + ONENAND_REG_START_ADDRESS1); - addr8 = this->read_word(this->base + ONENAND_REG_START_ADDRESS8); - - if (interrupt & ONENAND_INT_READ) { - ecc = onenand_read_ecc(this); - if (ecc & ONENAND_ECC_2BIT_ALL) { - printk(KERN_DEBUG "%s: ecc 0x%04x ctrl 0x%04x " - "intr 0x%04x addr1 %#x addr8 %#x\n", - __func__, ecc, ctrl, interrupt, addr1, addr8); - return ONENAND_BBT_READ_ECC_ERROR; - } - } else { - printk(KERN_ERR "%s: read timeout! ctrl 0x%04x " - "intr 0x%04x addr1 %#x addr8 %#x\n", - __func__, ctrl, interrupt, addr1, addr8); - return ONENAND_BBT_READ_FATAL_ERROR; - } - - /* Initial bad block case: 0x2400 or 0x0400 */ - if (ctrl & ONENAND_CTRL_ERROR) { - printk(KERN_DEBUG "%s: ctrl 0x%04x intr 0x%04x addr1 %#x " - "addr8 %#x\n", __func__, ctrl, interrupt, addr1, addr8); - return ONENAND_BBT_READ_ERROR; - } - - return 0; -} - -/** - * onenand_bbt_read_oob - [MTD Interface] OneNAND read out-of-band for bbt scan - * @param mtd MTD device structure - * @param from offset to read from - * @param ops oob operation description structure - * - * OneNAND read out-of-band data from the spare area for bbt scan - */ -int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from, - struct mtd_oob_ops *ops) -{ - struct onenand_chip *this = mtd->priv; - int read = 0, thislen, column; - int ret = 0, readcmd; - size_t len = ops->ooblen; - u_char *buf = ops->oobbuf; - - pr_debug("%s: from = 0x%08x, len = %zi\n", __func__, (unsigned int)from, - len); - - /* Initialize return value */ - ops->oobretlen = 0; - - /* Do not allow reads past end of device */ - if (unlikely((from + len) > mtd->size)) { - printk(KERN_ERR "%s: Attempt read beyond end of device\n", - __func__); - return ONENAND_BBT_READ_FATAL_ERROR; - } - - /* Grab the lock and see if the device is available */ - onenand_get_device(mtd, FL_READING); - - column = from & (mtd->oobsize - 1); - - readcmd = ONENAND_IS_4KB_PAGE(this) ? ONENAND_CMD_READ : ONENAND_CMD_READOOB; - - while (read < len) { - cond_resched(); - - thislen = mtd->oobsize - column; - thislen = min_t(int, thislen, len); - - this->command(mtd, readcmd, from, mtd->oobsize); - - onenand_update_bufferram(mtd, from, 0); - - ret = this->bbt_wait(mtd, FL_READING); - if (unlikely(ret)) - ret = onenand_recover_lsb(mtd, from, ret); - - if (ret) - break; - - this->read_bufferram(mtd, ONENAND_SPARERAM, buf, column, thislen); - read += thislen; - if (read == len) - break; - - buf += thislen; - - /* Read more? */ - if (read < len) { - /* Update Page size */ - from += this->writesize; - column = 0; - } - } - - /* Deselect and wake up anyone waiting on the device */ - onenand_release_device(mtd); - - ops->oobretlen = read; - return ret; -} - -#ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE -/** - * onenand_verify_oob - [GENERIC] verify the oob contents after a write - * @param mtd MTD device structure - * @param buf the databuffer to verify - * @param to offset to read from - */ -static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to) -{ - struct onenand_chip *this = mtd->priv; - u_char *oob_buf = this->oob_buf; - int status, i, readcmd; - - readcmd = ONENAND_IS_4KB_PAGE(this) ? ONENAND_CMD_READ : ONENAND_CMD_READOOB; - - this->command(mtd, readcmd, to, mtd->oobsize); - onenand_update_bufferram(mtd, to, 0); - status = this->wait(mtd, FL_READING); - if (status) - return status; - - this->read_bufferram(mtd, ONENAND_SPARERAM, oob_buf, 0, mtd->oobsize); - for (i = 0; i < mtd->oobsize; i++) - if (buf[i] != 0xFF && buf[i] != oob_buf[i]) - return -EBADMSG; - - return 0; -} - -/** - * onenand_verify - [GENERIC] verify the chip contents after a write - * @param mtd MTD device structure - * @param buf the databuffer to verify - * @param addr offset to read from - * @param len number of bytes to read and compare - */ -static int onenand_verify(struct mtd_info *mtd, const u_char *buf, loff_t addr, size_t len) -{ - struct onenand_chip *this = mtd->priv; - int ret = 0; - int thislen, column; - - column = addr & (this->writesize - 1); - - while (len != 0) { - thislen = min_t(int, this->writesize - column, len); - - this->command(mtd, ONENAND_CMD_READ, addr, this->writesize); - - onenand_update_bufferram(mtd, addr, 0); - - ret = this->wait(mtd, FL_READING); - if (ret) - return ret; - - onenand_update_bufferram(mtd, addr, 1); - - this->read_bufferram(mtd, ONENAND_DATARAM, this->verify_buf, 0, mtd->writesize); - - if (memcmp(buf, this->verify_buf + column, thislen)) - return -EBADMSG; - - len -= thislen; - buf += thislen; - addr += thislen; - column = 0; - } - - return 0; -} -#else -#define onenand_verify(...) (0) -#define onenand_verify_oob(...) (0) -#endif - -#define NOTALIGNED(x) ((x & (this->subpagesize - 1)) != 0) - -static void onenand_panic_wait(struct mtd_info *mtd) -{ - struct onenand_chip *this = mtd->priv; - unsigned int interrupt; - int i; - - for (i = 0; i < 2000; i++) { - interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT); - if (interrupt & ONENAND_INT_MASTER) - break; - udelay(10); - } -} - -/** - * onenand_panic_write - [MTD Interface] write buffer to FLASH in a panic context - * @param mtd MTD device structure - * @param to offset to write to - * @param len number of bytes to write - * @param retlen pointer to variable to store the number of written bytes - * @param buf the data to write - * - * Write with ECC - */ -static int onenand_panic_write(struct mtd_info *mtd, loff_t to, size_t len, - size_t *retlen, const u_char *buf) -{ - struct onenand_chip *this = mtd->priv; - int column, subpage; - int written = 0; - - if (this->state == FL_PM_SUSPENDED) - return -EBUSY; - - /* Wait for any existing operation to clear */ - onenand_panic_wait(mtd); - - pr_debug("%s: to = 0x%08x, len = %i\n", __func__, (unsigned int)to, - (int)len); - - /* Reject writes, which are not page aligned */ - if (unlikely(NOTALIGNED(to) || NOTALIGNED(len))) { - printk(KERN_ERR "%s: Attempt to write not page aligned data\n", - __func__); - return -EINVAL; - } - - column = to & (mtd->writesize - 1); - - /* Loop until all data write */ - while (written < len) { - int thislen = min_t(int, mtd->writesize - column, len - written); - u_char *wbuf = (u_char *) buf; - - this->command(mtd, ONENAND_CMD_BUFFERRAM, to, thislen); - - /* Partial page write */ - subpage = thislen < mtd->writesize; - if (subpage) { - memset(this->page_buf, 0xff, mtd->writesize); - memcpy(this->page_buf + column, buf, thislen); - wbuf = this->page_buf; - } - - this->write_bufferram(mtd, ONENAND_DATARAM, wbuf, 0, mtd->writesize); - this->write_bufferram(mtd, ONENAND_SPARERAM, ffchars, 0, mtd->oobsize); - - this->command(mtd, ONENAND_CMD_PROG, to, mtd->writesize); - - onenand_panic_wait(mtd); - - /* In partial page write we don't update bufferram */ - onenand_update_bufferram(mtd, to, !subpage); - if (ONENAND_IS_2PLANE(this)) { - ONENAND_SET_BUFFERRAM1(this); - onenand_update_bufferram(mtd, to + this->writesize, !subpage); - } - - written += thislen; - - if (written == len) - break; - - column = 0; - to += thislen; - buf += thislen; - } - - *retlen = written; - return 0; -} - -/** - * onenand_fill_auto_oob - [INTERN] oob auto-placement transfer - * @param mtd MTD device structure - * @param oob_buf oob buffer - * @param buf source address - * @param column oob offset to write to - * @param thislen oob length to write - */ -static int onenand_fill_auto_oob(struct mtd_info *mtd, u_char *oob_buf, - const u_char *buf, int column, int thislen) -{ - return mtd_ooblayout_set_databytes(mtd, buf, oob_buf, column, thislen); -} - -/** - * onenand_write_ops_nolock - [OneNAND Interface] write main and/or out-of-band - * @param mtd MTD device structure - * @param to offset to write to - * @param ops oob operation description structure - * - * Write main and/or oob with ECC - */ -static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to, - struct mtd_oob_ops *ops) -{ - struct onenand_chip *this = mtd->priv; - int written = 0, column, thislen = 0, subpage = 0; - int prev = 0, prevlen = 0, prev_subpage = 0, first = 1; - int oobwritten = 0, oobcolumn, thisooblen, oobsize; - size_t len = ops->len; - size_t ooblen = ops->ooblen; - const u_char *buf = ops->datbuf; - const u_char *oob = ops->oobbuf; - u_char *oobbuf; - int ret = 0, cmd; - - pr_debug("%s: to = 0x%08x, len = %i\n", __func__, (unsigned int)to, - (int)len); - - /* Initialize retlen, in case of early exit */ - ops->retlen = 0; - ops->oobretlen = 0; - - /* Reject writes, which are not page aligned */ - if (unlikely(NOTALIGNED(to) || NOTALIGNED(len))) { - printk(KERN_ERR "%s: Attempt to write not page aligned data\n", - __func__); - return -EINVAL; - } - - /* Check zero length */ - if (!len) - return 0; - oobsize = mtd_oobavail(mtd, ops); - oobcolumn = to & (mtd->oobsize - 1); - - column = to & (mtd->writesize - 1); - - /* Loop until all data write */ - while (1) { - if (written < len) { - u_char *wbuf = (u_char *) buf; - - thislen = min_t(int, mtd->writesize - column, len - written); - thisooblen = min_t(int, oobsize - oobcolumn, ooblen - oobwritten); - - cond_resched(); - - this->command(mtd, ONENAND_CMD_BUFFERRAM, to, thislen); - - /* Partial page write */ - subpage = thislen < mtd->writesize; - if (subpage) { - memset(this->page_buf, 0xff, mtd->writesize); - memcpy(this->page_buf + column, buf, thislen); - wbuf = this->page_buf; - } - - this->write_bufferram(mtd, ONENAND_DATARAM, wbuf, 0, mtd->writesize); - - if (oob) { - oobbuf = this->oob_buf; - - /* We send data to spare ram with oobsize - * to prevent byte access */ - memset(oobbuf, 0xff, mtd->oobsize); - if (ops->mode == MTD_OPS_AUTO_OOB) - onenand_fill_auto_oob(mtd, oobbuf, oob, oobcolumn, thisooblen); - else - memcpy(oobbuf + oobcolumn, oob, thisooblen); - - oobwritten += thisooblen; - oob += thisooblen; - oobcolumn = 0; - } else - oobbuf = (u_char *) ffchars; - - this->write_bufferram(mtd, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize); - } else - ONENAND_SET_NEXT_BUFFERRAM(this); - - /* - * 2 PLANE, MLC, and Flex-OneNAND do not support - * write-while-program feature. - */ - if (!ONENAND_IS_2PLANE(this) && !ONENAND_IS_4KB_PAGE(this) && !first) { - ONENAND_SET_PREV_BUFFERRAM(this); - - ret = this->wait(mtd, FL_WRITING); - - /* In partial page write we don't update bufferram */ - onenand_update_bufferram(mtd, prev, !ret && !prev_subpage); - if (ret) { - written -= prevlen; - printk(KERN_ERR "%s: write failed %d\n", - __func__, ret); - break; - } - - if (written == len) { - /* Only check verify write turn on */ - ret = onenand_verify(mtd, buf - len, to - len, len); - if (ret) - printk(KERN_ERR "%s: verify failed %d\n", - __func__, ret); - break; - } - - ONENAND_SET_NEXT_BUFFERRAM(this); - } - - this->ongoing = 0; - cmd = ONENAND_CMD_PROG; - - /* Exclude 1st OTP and OTP blocks for cache program feature */ - if (ONENAND_IS_CACHE_PROGRAM(this) && - likely(onenand_block(this, to) != 0) && - ONENAND_IS_4KB_PAGE(this) && - ((written + thislen) < len)) { - cmd = ONENAND_CMD_2X_CACHE_PROG; - this->ongoing = 1; - } - - this->command(mtd, cmd, to, mtd->writesize); - - /* - * 2 PLANE, MLC, and Flex-OneNAND wait here - */ - if (ONENAND_IS_2PLANE(this) || ONENAND_IS_4KB_PAGE(this)) { - ret = this->wait(mtd, FL_WRITING); - - /* In partial page write we don't update bufferram */ - onenand_update_bufferram(mtd, to, !ret && !subpage); - if (ret) { - printk(KERN_ERR "%s: write failed %d\n", - __func__, ret); - break; - } - - /* Only check verify write turn on */ - ret = onenand_verify(mtd, buf, to, thislen); - if (ret) { - printk(KERN_ERR "%s: verify failed %d\n", - __func__, ret); - break; - } - - written += thislen; - - if (written == len) - break; - - } else - written += thislen; - - column = 0; - prev_subpage = subpage; - prev = to; - prevlen = thislen; - to += thislen; - buf += thislen; - first = 0; - } - - /* In error case, clear all bufferrams */ - if (written != len) - onenand_invalidate_bufferram(mtd, 0, -1); - - ops->retlen = written; - ops->oobretlen = oobwritten; - - return ret; -} - - -/** - * onenand_write_oob_nolock - [INTERN] OneNAND write out-of-band - * @param mtd MTD device structure - * @param to offset to write to - * @param len number of bytes to write - * @param retlen pointer to variable to store the number of written bytes - * @param buf the data to write - * @param mode operation mode - * - * OneNAND write out-of-band - */ -static int onenand_write_oob_nolock(struct mtd_info *mtd, loff_t to, - struct mtd_oob_ops *ops) -{ - struct onenand_chip *this = mtd->priv; - int column, ret = 0, oobsize; - int written = 0, oobcmd; - u_char *oobbuf; - size_t len = ops->ooblen; - const u_char *buf = ops->oobbuf; - unsigned int mode = ops->mode; - - to += ops->ooboffs; - - pr_debug("%s: to = 0x%08x, len = %i\n", __func__, (unsigned int)to, - (int)len); - - /* Initialize retlen, in case of early exit */ - ops->oobretlen = 0; - - if (mode == MTD_OPS_AUTO_OOB) - oobsize = mtd->oobavail; - else - oobsize = mtd->oobsize; - - column = to & (mtd->oobsize - 1); - - if (unlikely(column >= oobsize)) { - printk(KERN_ERR "%s: Attempted to start write outside oob\n", - __func__); - return -EINVAL; - } - - /* For compatibility with NAND: Do not allow write past end of page */ - if (unlikely(column + len > oobsize)) { - printk(KERN_ERR "%s: Attempt to write past end of page\n", - __func__); - return -EINVAL; - } - - oobbuf = this->oob_buf; - - oobcmd = ONENAND_IS_4KB_PAGE(this) ? ONENAND_CMD_PROG : ONENAND_CMD_PROGOOB; - - /* Loop until all data write */ - while (written < len) { - int thislen = min_t(int, oobsize, len - written); - - cond_resched(); - - this->command(mtd, ONENAND_CMD_BUFFERRAM, to, mtd->oobsize); - - /* We send data to spare ram with oobsize - * to prevent byte access */ - memset(oobbuf, 0xff, mtd->oobsize); - if (mode == MTD_OPS_AUTO_OOB) - onenand_fill_auto_oob(mtd, oobbuf, buf, column, thislen); - else - memcpy(oobbuf + column, buf, thislen); - this->write_bufferram(mtd, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize); - - if (ONENAND_IS_4KB_PAGE(this)) { - /* Set main area of DataRAM to 0xff*/ - memset(this->page_buf, 0xff, mtd->writesize); - this->write_bufferram(mtd, ONENAND_DATARAM, - this->page_buf, 0, mtd->writesize); - } - - this->command(mtd, oobcmd, to, mtd->oobsize); - - onenand_update_bufferram(mtd, to, 0); - if (ONENAND_IS_2PLANE(this)) { - ONENAND_SET_BUFFERRAM1(this); - onenand_update_bufferram(mtd, to + this->writesize, 0); - } - - ret = this->wait(mtd, FL_WRITING); - if (ret) { - printk(KERN_ERR "%s: write failed %d\n", __func__, ret); - break; - } - - ret = onenand_verify_oob(mtd, oobbuf, to); - if (ret) { - printk(KERN_ERR "%s: verify failed %d\n", - __func__, ret); - break; - } - - written += thislen; - if (written == len) - break; - - to += mtd->writesize; - buf += thislen; - column = 0; - } - - ops->oobretlen = written; - - return ret; -} - -/** - * onenand_write_oob - [MTD Interface] NAND write data and/or out-of-band - * @param mtd: MTD device structure - * @param to: offset to write - * @param ops: oob operation description structure - */ -static int onenand_write_oob(struct mtd_info *mtd, loff_t to, - struct mtd_oob_ops *ops) -{ - int ret; - - switch (ops->mode) { - case MTD_OPS_PLACE_OOB: - case MTD_OPS_AUTO_OOB: - break; - case MTD_OPS_RAW: - /* Not implemented yet */ - default: - return -EINVAL; - } - - onenand_get_device(mtd, FL_WRITING); - if (ops->datbuf) - ret = onenand_write_ops_nolock(mtd, to, ops); - else - ret = onenand_write_oob_nolock(mtd, to, ops); - onenand_release_device(mtd); - - return ret; -} - -/** - * onenand_block_isbad_nolock - [GENERIC] Check if a block is marked bad - * @param mtd MTD device structure - * @param ofs offset from device start - * @param allowbbt 1, if its allowed to access the bbt area - * - * Check, if the block is bad. Either by reading the bad block table or - * calling of the scan function. - */ -static int onenand_block_isbad_nolock(struct mtd_info *mtd, loff_t ofs, int allowbbt) -{ - struct onenand_chip *this = mtd->priv; - struct bbm_info *bbm = this->bbm; - - /* Return info from the table */ - return bbm->isbad_bbt(mtd, ofs, allowbbt); -} - - -static int onenand_multiblock_erase_verify(struct mtd_info *mtd, - struct erase_info *instr) -{ - struct onenand_chip *this = mtd->priv; - loff_t addr = instr->addr; - int len = instr->len; - unsigned int block_size = (1 << this->erase_shift); - int ret = 0; - - while (len) { - this->command(mtd, ONENAND_CMD_ERASE_VERIFY, addr, block_size); - ret = this->wait(mtd, FL_VERIFYING_ERASE); - if (ret) { - printk(KERN_ERR "%s: Failed verify, block %d\n", - __func__, onenand_block(this, addr)); - instr->state = MTD_ERASE_FAILED; - instr->fail_addr = addr; - return -1; - } - len -= block_size; - addr += block_size; - } - return 0; -} - -/** - * onenand_multiblock_erase - [INTERN] erase block(s) using multiblock erase - * @param mtd MTD device structure - * @param instr erase instruction - * @param region erase region - * - * Erase one or more blocks up to 64 block at a time - */ -static int onenand_multiblock_erase(struct mtd_info *mtd, - struct erase_info *instr, - unsigned int block_size) -{ - struct onenand_chip *this = mtd->priv; - loff_t addr = instr->addr; - int len = instr->len; - int eb_count = 0; - int ret = 0; - int bdry_block = 0; - - instr->state = MTD_ERASING; - - if (ONENAND_IS_DDP(this)) { - loff_t bdry_addr = this->chipsize >> 1; - if (addr < bdry_addr && (addr + len) > bdry_addr) - bdry_block = bdry_addr >> this->erase_shift; - } - - /* Pre-check bbs */ - while (len) { - /* Check if we have a bad block, we do not erase bad blocks */ - if (onenand_block_isbad_nolock(mtd, addr, 0)) { - printk(KERN_WARNING "%s: attempt to erase a bad block " - "at addr 0x%012llx\n", - __func__, (unsigned long long) addr); - instr->state = MTD_ERASE_FAILED; - return -EIO; - } - len -= block_size; - addr += block_size; - } - - len = instr->len; - addr = instr->addr; - - /* loop over 64 eb batches */ - while (len) { - struct erase_info verify_instr = *instr; - int max_eb_count = MB_ERASE_MAX_BLK_COUNT; - - verify_instr.addr = addr; - verify_instr.len = 0; - - /* do not cross chip boundary */ - if (bdry_block) { - int this_block = (addr >> this->erase_shift); - - if (this_block < bdry_block) { - max_eb_count = min(max_eb_count, - (bdry_block - this_block)); - } - } - - eb_count = 0; - - while (len > block_size && eb_count < (max_eb_count - 1)) { - this->command(mtd, ONENAND_CMD_MULTIBLOCK_ERASE, - addr, block_size); - onenand_invalidate_bufferram(mtd, addr, block_size); - - ret = this->wait(mtd, FL_PREPARING_ERASE); - if (ret) { - printk(KERN_ERR "%s: Failed multiblock erase, " - "block %d\n", __func__, - onenand_block(this, addr)); - instr->state = MTD_ERASE_FAILED; - instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN; - return -EIO; - } - - len -= block_size; - addr += block_size; - eb_count++; - } - - /* last block of 64-eb series */ - cond_resched(); - this->command(mtd, ONENAND_CMD_ERASE, addr, block_size); - onenand_invalidate_bufferram(mtd, addr, block_size); - - ret = this->wait(mtd, FL_ERASING); - /* Check if it is write protected */ - if (ret) { - printk(KERN_ERR "%s: Failed erase, block %d\n", - __func__, onenand_block(this, addr)); - instr->state = MTD_ERASE_FAILED; - instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN; - return -EIO; - } - - len -= block_size; - addr += block_size; - eb_count++; - - /* verify */ - verify_instr.len = eb_count * block_size; - if (onenand_multiblock_erase_verify(mtd, &verify_instr)) { - instr->state = verify_instr.state; - instr->fail_addr = verify_instr.fail_addr; - return -EIO; - } - - } - return 0; -} - - -/** - * onenand_block_by_block_erase - [INTERN] erase block(s) using regular erase - * @param mtd MTD device structure - * @param instr erase instruction - * @param region erase region - * @param block_size erase block size - * - * Erase one or more blocks one block at a time - */ -static int onenand_block_by_block_erase(struct mtd_info *mtd, - struct erase_info *instr, - struct mtd_erase_region_info *region, - unsigned int block_size) -{ - struct onenand_chip *this = mtd->priv; - loff_t addr = instr->addr; - int len = instr->len; - loff_t region_end = 0; - int ret = 0; - - if (region) { - /* region is set for Flex-OneNAND */ - region_end = region->offset + region->erasesize * region->numblocks; - } - - instr->state = MTD_ERASING; - - /* Loop through the blocks */ - while (len) { - cond_resched(); - - /* Check if we have a bad block, we do not erase bad blocks */ - if (onenand_block_isbad_nolock(mtd, addr, 0)) { - printk(KERN_WARNING "%s: attempt to erase a bad block " - "at addr 0x%012llx\n", - __func__, (unsigned long long) addr); - instr->state = MTD_ERASE_FAILED; - return -EIO; - } - - this->command(mtd, ONENAND_CMD_ERASE, addr, block_size); - - onenand_invalidate_bufferram(mtd, addr, block_size); - - ret = this->wait(mtd, FL_ERASING); - /* Check, if it is write protected */ - if (ret) { - printk(KERN_ERR "%s: Failed erase, block %d\n", - __func__, onenand_block(this, addr)); - instr->state = MTD_ERASE_FAILED; - instr->fail_addr = addr; - return -EIO; - } - - len -= block_size; - addr += block_size; - - if (region && addr == region_end) { - if (!len) - break; - region++; - - block_size = region->erasesize; - region_end = region->offset + region->erasesize * region->numblocks; - - if (len & (block_size - 1)) { - /* FIXME: This should be handled at MTD partitioning level. */ - printk(KERN_ERR "%s: Unaligned address\n", - __func__); - return -EIO; - } - } - } - return 0; -} - -/** - * onenand_erase - [MTD Interface] erase block(s) - * @param mtd MTD device structure - * @param instr erase instruction - * - * Erase one or more blocks - */ -static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr) -{ - struct onenand_chip *this = mtd->priv; - unsigned int block_size; - loff_t addr = instr->addr; - loff_t len = instr->len; - int ret = 0; - struct mtd_erase_region_info *region = NULL; - loff_t region_offset = 0; - - pr_debug("%s: start=0x%012llx, len=%llu\n", __func__, - (unsigned long long)instr->addr, - (unsigned long long)instr->len); - - if (FLEXONENAND(this)) { - /* Find the eraseregion of this address */ - int i = flexonenand_region(mtd, addr); - - region = &mtd->eraseregions[i]; - block_size = region->erasesize; - - /* Start address within region must align on block boundary. - * Erase region's start offset is always block start address. - */ - region_offset = region->offset; - } else - block_size = 1 << this->erase_shift; - - /* Start address must align on block boundary */ - if (unlikely((addr - region_offset) & (block_size - 1))) { - printk(KERN_ERR "%s: Unaligned address\n", __func__); - return -EINVAL; - } - - /* Length must align on block boundary */ - if (unlikely(len & (block_size - 1))) { - printk(KERN_ERR "%s: Length not block aligned\n", __func__); - return -EINVAL; - } - - /* Grab the lock and see if the device is available */ - onenand_get_device(mtd, FL_ERASING); - - if (ONENAND_IS_4KB_PAGE(this) || region || - instr->len < MB_ERASE_MIN_BLK_COUNT * block_size) { - /* region is set for Flex-OneNAND (no mb erase) */ - ret = onenand_block_by_block_erase(mtd, instr, - region, block_size); - } else { - ret = onenand_multiblock_erase(mtd, instr, block_size); - } - - /* Deselect and wake up anyone waiting on the device */ - onenand_release_device(mtd); - - /* Do call back function */ - if (!ret) { - instr->state = MTD_ERASE_DONE; - mtd_erase_callback(instr); - } - - return ret; -} - -/** - * onenand_sync - [MTD Interface] sync - * @param mtd MTD device structure - * - * Sync is actually a wait for chip ready function - */ -static void onenand_sync(struct mtd_info *mtd) -{ - pr_debug("%s: called\n", __func__); - - /* Grab the lock and see if the device is available */ - onenand_get_device(mtd, FL_SYNCING); - - /* Release it and go back */ - onenand_release_device(mtd); -} - -/** - * onenand_block_isbad - [MTD Interface] Check whether the block at the given offset is bad - * @param mtd MTD device structure - * @param ofs offset relative to mtd start - * - * Check whether the block is bad - */ -static int onenand_block_isbad(struct mtd_info *mtd, loff_t ofs) -{ - int ret; - - onenand_get_device(mtd, FL_READING); - ret = onenand_block_isbad_nolock(mtd, ofs, 0); - onenand_release_device(mtd); - return ret; -} - -/** - * onenand_default_block_markbad - [DEFAULT] mark a block bad - * @param mtd MTD device structure - * @param ofs offset from device start - * - * This is the default implementation, which can be overridden by - * a hardware specific driver. - */ -static int onenand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) -{ - struct onenand_chip *this = mtd->priv; - struct bbm_info *bbm = this->bbm; - u_char buf[2] = {0, 0}; - struct mtd_oob_ops ops = { - .mode = MTD_OPS_PLACE_OOB, - .ooblen = 2, - .oobbuf = buf, - .ooboffs = 0, - }; - int block; - - /* Get block number */ - block = onenand_block(this, ofs); - if (bbm->bbt) - bbm->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1); - - /* We write two bytes, so we don't have to mess with 16-bit access */ - ofs += mtd->oobsize + (bbm->badblockpos & ~0x01); - /* FIXME : What to do when marking SLC block in partition - * with MLC erasesize? For now, it is not advisable to - * create partitions containing both SLC and MLC regions. - */ - return onenand_write_oob_nolock(mtd, ofs, &ops); -} - -/** - * onenand_block_markbad - [MTD Interface] Mark the block at the given offset as bad - * @param mtd MTD device structure - * @param ofs offset relative to mtd start - * - * Mark the block as bad - */ -static int onenand_block_markbad(struct mtd_info *mtd, loff_t ofs) -{ - struct onenand_chip *this = mtd->priv; - int ret; - - ret = onenand_block_isbad(mtd, ofs); - if (ret) { - /* If it was bad already, return success and do nothing */ - if (ret > 0) - return 0; - return ret; - } - - onenand_get_device(mtd, FL_WRITING); - ret = this->block_markbad(mtd, ofs); - onenand_release_device(mtd); - return ret; -} - -/** - * onenand_do_lock_cmd - [OneNAND Interface] Lock or unlock block(s) - * @param mtd MTD device structure - * @param ofs offset relative to mtd start - * @param len number of bytes to lock or unlock - * @param cmd lock or unlock command - * - * Lock or unlock one or more blocks - */ -static int onenand_do_lock_cmd(struct mtd_info *mtd, loff_t ofs, size_t len, int cmd) -{ - struct onenand_chip *this = mtd->priv; - int start, end, block, value, status; - int wp_status_mask; - - start = onenand_block(this, ofs); - end = onenand_block(this, ofs + len) - 1; - - if (cmd == ONENAND_CMD_LOCK) - wp_status_mask = ONENAND_WP_LS; - else - wp_status_mask = ONENAND_WP_US; - - /* Continuous lock scheme */ - if (this->options & ONENAND_HAS_CONT_LOCK) { - /* Set start block address */ - this->write_word(start, this->base + ONENAND_REG_START_BLOCK_ADDRESS); - /* Set end block address */ - this->write_word(end, this->base + ONENAND_REG_END_BLOCK_ADDRESS); - /* Write lock command */ - this->command(mtd, cmd, 0, 0); - - /* There's no return value */ - this->wait(mtd, FL_LOCKING); - - /* Sanity check */ - while (this->read_word(this->base + ONENAND_REG_CTRL_STATUS) - & ONENAND_CTRL_ONGO) - continue; - - /* Check lock status */ - status = this->read_word(this->base + ONENAND_REG_WP_STATUS); - if (!(status & wp_status_mask)) - printk(KERN_ERR "%s: wp status = 0x%x\n", - __func__, status); - - return 0; - } - - /* Block lock scheme */ - for (block = start; block < end + 1; block++) { - /* Set block address */ - value = onenand_block_address(this, block); - this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1); - /* Select DataRAM for DDP */ - value = onenand_bufferram_address(this, block); - this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2); - /* Set start block address */ - this->write_word(block, this->base + ONENAND_REG_START_BLOCK_ADDRESS); - /* Write lock command */ - this->command(mtd, cmd, 0, 0); - - /* There's no return value */ - this->wait(mtd, FL_LOCKING); - - /* Sanity check */ - while (this->read_word(this->base + ONENAND_REG_CTRL_STATUS) - & ONENAND_CTRL_ONGO) - continue; - - /* Check lock status */ - status = this->read_word(this->base + ONENAND_REG_WP_STATUS); - if (!(status & wp_status_mask)) - printk(KERN_ERR "%s: block = %d, wp status = 0x%x\n", - __func__, block, status); - } - - return 0; -} - -/** - * onenand_lock - [MTD Interface] Lock block(s) - * @param mtd MTD device structure - * @param ofs offset relative to mtd start - * @param len number of bytes to unlock - * - * Lock one or more blocks - */ -static int onenand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) -{ - int ret; - - onenand_get_device(mtd, FL_LOCKING); - ret = onenand_do_lock_cmd(mtd, ofs, len, ONENAND_CMD_LOCK); - onenand_release_device(mtd); - return ret; -} - -/** - * onenand_unlock - [MTD Interface] Unlock block(s) - * @param mtd MTD device structure - * @param ofs offset relative to mtd start - * @param len number of bytes to unlock - * - * Unlock one or more blocks - */ -static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) -{ - int ret; - - onenand_get_device(mtd, FL_LOCKING); - ret = onenand_do_lock_cmd(mtd, ofs, len, ONENAND_CMD_UNLOCK); - onenand_release_device(mtd); - return ret; -} - -/** - * onenand_check_lock_status - [OneNAND Interface] Check lock status - * @param this onenand chip data structure - * - * Check lock status - */ -static int onenand_check_lock_status(struct onenand_chip *this) -{ - unsigned int value, block, status; - unsigned int end; - - end = this->chipsize >> this->erase_shift; - for (block = 0; block < end; block++) { - /* Set block address */ - value = onenand_block_address(this, block); - this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1); - /* Select DataRAM for DDP */ - value = onenand_bufferram_address(this, block); - this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2); - /* Set start block address */ - this->write_word(block, this->base + ONENAND_REG_START_BLOCK_ADDRESS); - - /* Check lock status */ - status = this->read_word(this->base + ONENAND_REG_WP_STATUS); - if (!(status & ONENAND_WP_US)) { - printk(KERN_ERR "%s: block = %d, wp status = 0x%x\n", - __func__, block, status); - return 0; - } - } - - return 1; -} - -/** - * onenand_unlock_all - [OneNAND Interface] unlock all blocks - * @param mtd MTD device structure - * - * Unlock all blocks - */ -static void onenand_unlock_all(struct mtd_info *mtd) -{ - struct onenand_chip *this = mtd->priv; - loff_t ofs = 0; - loff_t len = mtd->size; - - if (this->options & ONENAND_HAS_UNLOCK_ALL) { - /* Set start block address */ - this->write_word(0, this->base + ONENAND_REG_START_BLOCK_ADDRESS); - /* Write unlock command */ - this->command(mtd, ONENAND_CMD_UNLOCK_ALL, 0, 0); - - /* There's no return value */ - this->wait(mtd, FL_LOCKING); - - /* Sanity check */ - while (this->read_word(this->base + ONENAND_REG_CTRL_STATUS) - & ONENAND_CTRL_ONGO) - continue; - - /* Don't check lock status */ - if (this->options & ONENAND_SKIP_UNLOCK_CHECK) - return; - - /* Check lock status */ - if (onenand_check_lock_status(this)) - return; - - /* Workaround for all block unlock in DDP */ - if (ONENAND_IS_DDP(this) && !FLEXONENAND(this)) { - /* All blocks on another chip */ - ofs = this->chipsize >> 1; - len = this->chipsize >> 1; - } - } - - onenand_do_lock_cmd(mtd, ofs, len, ONENAND_CMD_UNLOCK); -} - -#ifdef CONFIG_MTD_ONENAND_OTP - -/** - * onenand_otp_command - Send OTP specific command to OneNAND device - * @param mtd MTD device structure - * @param cmd the command to be sent - * @param addr offset to read from or write to - * @param len number of bytes to read or write - */ -static int onenand_otp_command(struct mtd_info *mtd, int cmd, loff_t addr, - size_t len) -{ - struct onenand_chip *this = mtd->priv; - int value, block, page; - - /* Address translation */ - switch (cmd) { - case ONENAND_CMD_OTP_ACCESS: - block = (int) (addr >> this->erase_shift); - page = -1; - break; - - default: - block = (int) (addr >> this->erase_shift); - page = (int) (addr >> this->page_shift); - - if (ONENAND_IS_2PLANE(this)) { - /* Make the even block number */ - block &= ~1; - /* Is it the odd plane? */ - if (addr & this->writesize) - block++; - page >>= 1; - } - page &= this->page_mask; - break; - } - - if (block != -1) { - /* Write 'DFS, FBA' of Flash */ - value = onenand_block_address(this, block); - this->write_word(value, this->base + - ONENAND_REG_START_ADDRESS1); - } - - if (page != -1) { - /* Now we use page size operation */ - int sectors = 4, count = 4; - int dataram; - - switch (cmd) { - default: - if (ONENAND_IS_2PLANE(this) && cmd == ONENAND_CMD_PROG) - cmd = ONENAND_CMD_2X_PROG; - dataram = ONENAND_CURRENT_BUFFERRAM(this); - break; - } - - /* Write 'FPA, FSA' of Flash */ - value = onenand_page_address(page, sectors); - this->write_word(value, this->base + - ONENAND_REG_START_ADDRESS8); - - /* Write 'BSA, BSC' of DataRAM */ - value = onenand_buffer_address(dataram, sectors, count); - this->write_word(value, this->base + ONENAND_REG_START_BUFFER); - } - - /* Interrupt clear */ - this->write_word(ONENAND_INT_CLEAR, this->base + ONENAND_REG_INTERRUPT); - - /* Write command */ - this->write_word(cmd, this->base + ONENAND_REG_COMMAND); - - return 0; -} - -/** - * onenand_otp_write_oob_nolock - [INTERN] OneNAND write out-of-band, specific to OTP - * @param mtd MTD device structure - * @param to offset to write to - * @param len number of bytes to write - * @param retlen pointer to variable to store the number of written bytes - * @param buf the data to write - * - * OneNAND write out-of-band only for OTP - */ -static int onenand_otp_write_oob_nolock(struct mtd_info *mtd, loff_t to, - struct mtd_oob_ops *ops) -{ - struct onenand_chip *this = mtd->priv; - int column, ret = 0, oobsize; - int written = 0; - u_char *oobbuf; - size_t len = ops->ooblen; - const u_char *buf = ops->oobbuf; - int block, value, status; - - to += ops->ooboffs; - - /* Initialize retlen, in case of early exit */ - ops->oobretlen = 0; - - oobsize = mtd->oobsize; - - column = to & (mtd->oobsize - 1); - - oobbuf = this->oob_buf; - - /* Loop until all data write */ - while (written < len) { - int thislen = min_t(int, oobsize, len - written); - - cond_resched(); - - block = (int) (to >> this->erase_shift); - /* - * Write 'DFS, FBA' of Flash - * Add: F100h DQ=DFS, FBA - */ - - value = onenand_block_address(this, block); - this->write_word(value, this->base + - ONENAND_REG_START_ADDRESS1); - - /* - * Select DataRAM for DDP - * Add: F101h DQ=DBS - */ - - value = onenand_bufferram_address(this, block); - this->write_word(value, this->base + - ONENAND_REG_START_ADDRESS2); - ONENAND_SET_NEXT_BUFFERRAM(this); - - /* - * Enter OTP access mode - */ - this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0); - this->wait(mtd, FL_OTPING); - - /* We send data to spare ram with oobsize - * to prevent byte access */ - memcpy(oobbuf + column, buf, thislen); - - /* - * Write Data into DataRAM - * Add: 8th Word - * in sector0/spare/page0 - * DQ=XXFCh - */ - this->write_bufferram(mtd, ONENAND_SPARERAM, - oobbuf, 0, mtd->oobsize); - - onenand_otp_command(mtd, ONENAND_CMD_PROGOOB, to, mtd->oobsize); - onenand_update_bufferram(mtd, to, 0); - if (ONENAND_IS_2PLANE(this)) { - ONENAND_SET_BUFFERRAM1(this); - onenand_update_bufferram(mtd, to + this->writesize, 0); - } - - ret = this->wait(mtd, FL_WRITING); - if (ret) { - printk(KERN_ERR "%s: write failed %d\n", __func__, ret); - break; - } - - /* Exit OTP access mode */ - this->command(mtd, ONENAND_CMD_RESET, 0, 0); - this->wait(mtd, FL_RESETING); - - status = this->read_word(this->base + ONENAND_REG_CTRL_STATUS); - status &= 0x60; - - if (status == 0x60) { - printk(KERN_DEBUG "\nBLOCK\tSTATUS\n"); - printk(KERN_DEBUG "1st Block\tLOCKED\n"); - printk(KERN_DEBUG "OTP Block\tLOCKED\n"); - } else if (status == 0x20) { - printk(KERN_DEBUG "\nBLOCK\tSTATUS\n"); - printk(KERN_DEBUG "1st Block\tLOCKED\n"); - printk(KERN_DEBUG "OTP Block\tUN-LOCKED\n"); - } else if (status == 0x40) { - printk(KERN_DEBUG "\nBLOCK\tSTATUS\n"); - printk(KERN_DEBUG "1st Block\tUN-LOCKED\n"); - printk(KERN_DEBUG "OTP Block\tLOCKED\n"); - } else { - printk(KERN_DEBUG "Reboot to check\n"); - } - - written += thislen; - if (written == len) - break; - - to += mtd->writesize; - buf += thislen; - column = 0; - } - - ops->oobretlen = written; - - return ret; -} - -/* Internal OTP operation */ -typedef int (*otp_op_t)(struct mtd_info *mtd, loff_t form, size_t len, - size_t *retlen, u_char *buf); - -/** - * do_otp_read - [DEFAULT] Read OTP block area - * @param mtd MTD device structure - * @param from The offset to read - * @param len number of bytes to read - * @param retlen pointer to variable to store the number of readbytes - * @param buf the databuffer to put/get data - * - * Read OTP block area. - */ -static int do_otp_read(struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, u_char *buf) -{ - struct onenand_chip *this = mtd->priv; - struct mtd_oob_ops ops = { - .len = len, - .ooblen = 0, - .datbuf = buf, - .oobbuf = NULL, - }; - int ret; - - /* Enter OTP access mode */ - this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0); - this->wait(mtd, FL_OTPING); - - ret = ONENAND_IS_4KB_PAGE(this) ? - onenand_mlc_read_ops_nolock(mtd, from, &ops) : - onenand_read_ops_nolock(mtd, from, &ops); - - /* Exit OTP access mode */ - this->command(mtd, ONENAND_CMD_RESET, 0, 0); - this->wait(mtd, FL_RESETING); - - return ret; -} - -/** - * do_otp_write - [DEFAULT] Write OTP block area - * @param mtd MTD device structure - * @param to The offset to write - * @param len number of bytes to write - * @param retlen pointer to variable to store the number of write bytes - * @param buf the databuffer to put/get data - * - * Write OTP block area. - */ -static int do_otp_write(struct mtd_info *mtd, loff_t to, size_t len, - size_t *retlen, u_char *buf) -{ - struct onenand_chip *this = mtd->priv; - unsigned char *pbuf = buf; - int ret; - struct mtd_oob_ops ops; - - /* Force buffer page aligned */ - if (len < mtd->writesize) { - memcpy(this->page_buf, buf, len); - memset(this->page_buf + len, 0xff, mtd->writesize - len); - pbuf = this->page_buf; - len = mtd->writesize; - } - - /* Enter OTP access mode */ - this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0); - this->wait(mtd, FL_OTPING); - - ops.len = len; - ops.ooblen = 0; - ops.datbuf = pbuf; - ops.oobbuf = NULL; - ret = onenand_write_ops_nolock(mtd, to, &ops); - *retlen = ops.retlen; - - /* Exit OTP access mode */ - this->command(mtd, ONENAND_CMD_RESET, 0, 0); - this->wait(mtd, FL_RESETING); - - return ret; -} - -/** - * do_otp_lock - [DEFAULT] Lock OTP block area - * @param mtd MTD device structure - * @param from The offset to lock - * @param len number of bytes to lock - * @param retlen pointer to variable to store the number of lock bytes - * @param buf the databuffer to put/get data - * - * Lock OTP block area. - */ -static int do_otp_lock(struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, u_char *buf) -{ - struct onenand_chip *this = mtd->priv; - struct mtd_oob_ops ops; - int ret; - - if (FLEXONENAND(this)) { - - /* Enter OTP access mode */ - this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0); - this->wait(mtd, FL_OTPING); - /* - * For Flex-OneNAND, we write lock mark to 1st word of sector 4 of - * main area of page 49. - */ - ops.len = mtd->writesize; - ops.ooblen = 0; - ops.datbuf = buf; - ops.oobbuf = NULL; - ret = onenand_write_ops_nolock(mtd, mtd->writesize * 49, &ops); - *retlen = ops.retlen; - - /* Exit OTP access mode */ - this->command(mtd, ONENAND_CMD_RESET, 0, 0); - this->wait(mtd, FL_RESETING); - } else { - ops.mode = MTD_OPS_PLACE_OOB; - ops.ooblen = len; - ops.oobbuf = buf; - ops.ooboffs = 0; - ret = onenand_otp_write_oob_nolock(mtd, from, &ops); - *retlen = ops.oobretlen; - } - - return ret; -} - -/** - * onenand_otp_walk - [DEFAULT] Handle OTP operation - * @param mtd MTD device structure - * @param from The offset to read/write - * @param len number of bytes to read/write - * @param retlen pointer to variable to store the number of read bytes - * @param buf the databuffer to put/get data - * @param action do given action - * @param mode specify user and factory - * - * Handle OTP operation. - */ -static int onenand_otp_walk(struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, u_char *buf, - otp_op_t action, int mode) -{ - struct onenand_chip *this = mtd->priv; - int otp_pages; - int density; - int ret = 0; - - *retlen = 0; - - density = onenand_get_density(this->device_id); - if (density < ONENAND_DEVICE_DENSITY_512Mb) - otp_pages = 20; - else - otp_pages = 50; - - if (mode == MTD_OTP_FACTORY) { - from += mtd->writesize * otp_pages; - otp_pages = ONENAND_PAGES_PER_BLOCK - otp_pages; - } - - /* Check User/Factory boundary */ - if (mode == MTD_OTP_USER) { - if (mtd->writesize * otp_pages < from + len) - return 0; - } else { - if (mtd->writesize * otp_pages < len) - return 0; - } - - onenand_get_device(mtd, FL_OTPING); - while (len > 0 && otp_pages > 0) { - if (!action) { /* OTP Info functions */ - struct otp_info *otpinfo; - - len -= sizeof(struct otp_info); - if (len <= 0) { - ret = -ENOSPC; - break; - } - - otpinfo = (struct otp_info *) buf; - otpinfo->start = from; - otpinfo->length = mtd->writesize; - otpinfo->locked = 0; - - from += mtd->writesize; - buf += sizeof(struct otp_info); - *retlen += sizeof(struct otp_info); - } else { - size_t tmp_retlen; - - ret = action(mtd, from, len, &tmp_retlen, buf); - if (ret) - break; - - buf += tmp_retlen; - len -= tmp_retlen; - *retlen += tmp_retlen; - - } - otp_pages--; - } - onenand_release_device(mtd); - - return ret; -} - -/** - * onenand_get_fact_prot_info - [MTD Interface] Read factory OTP info - * @param mtd MTD device structure - * @param len number of bytes to read - * @param retlen pointer to variable to store the number of read bytes - * @param buf the databuffer to put/get data - * - * Read factory OTP info. - */ -static int onenand_get_fact_prot_info(struct mtd_info *mtd, size_t len, - size_t *retlen, struct otp_info *buf) -{ - return onenand_otp_walk(mtd, 0, len, retlen, (u_char *) buf, NULL, - MTD_OTP_FACTORY); -} - -/** - * onenand_read_fact_prot_reg - [MTD Interface] Read factory OTP area - * @param mtd MTD device structure - * @param from The offset to read - * @param len number of bytes to read - * @param retlen pointer to variable to store the number of read bytes - * @param buf the databuffer to put/get data - * - * Read factory OTP area. - */ -static int onenand_read_fact_prot_reg(struct mtd_info *mtd, loff_t from, - size_t len, size_t *retlen, u_char *buf) -{ - return onenand_otp_walk(mtd, from, len, retlen, buf, do_otp_read, MTD_OTP_FACTORY); -} - -/** - * onenand_get_user_prot_info - [MTD Interface] Read user OTP info - * @param mtd MTD device structure - * @param retlen pointer to variable to store the number of read bytes - * @param len number of bytes to read - * @param buf the databuffer to put/get data - * - * Read user OTP info. - */ -static int onenand_get_user_prot_info(struct mtd_info *mtd, size_t len, - size_t *retlen, struct otp_info *buf) -{ - return onenand_otp_walk(mtd, 0, len, retlen, (u_char *) buf, NULL, - MTD_OTP_USER); -} - -/** - * onenand_read_user_prot_reg - [MTD Interface] Read user OTP area - * @param mtd MTD device structure - * @param from The offset to read - * @param len number of bytes to read - * @param retlen pointer to variable to store the number of read bytes - * @param buf the databuffer to put/get data - * - * Read user OTP area. - */ -static int onenand_read_user_prot_reg(struct mtd_info *mtd, loff_t from, - size_t len, size_t *retlen, u_char *buf) -{ - return onenand_otp_walk(mtd, from, len, retlen, buf, do_otp_read, MTD_OTP_USER); -} - -/** - * onenand_write_user_prot_reg - [MTD Interface] Write user OTP area - * @param mtd MTD device structure - * @param from The offset to write - * @param len number of bytes to write - * @param retlen pointer to variable to store the number of write bytes - * @param buf the databuffer to put/get data - * - * Write user OTP area. - */ -static int onenand_write_user_prot_reg(struct mtd_info *mtd, loff_t from, - size_t len, size_t *retlen, u_char *buf) -{ - return onenand_otp_walk(mtd, from, len, retlen, buf, do_otp_write, MTD_OTP_USER); -} - -/** - * onenand_lock_user_prot_reg - [MTD Interface] Lock user OTP area - * @param mtd MTD device structure - * @param from The offset to lock - * @param len number of bytes to unlock - * - * Write lock mark on spare area in page 0 in OTP block - */ -static int onenand_lock_user_prot_reg(struct mtd_info *mtd, loff_t from, - size_t len) -{ - struct onenand_chip *this = mtd->priv; - u_char *buf = FLEXONENAND(this) ? this->page_buf : this->oob_buf; - size_t retlen; - int ret; - unsigned int otp_lock_offset = ONENAND_OTP_LOCK_OFFSET; - - memset(buf, 0xff, FLEXONENAND(this) ? this->writesize - : mtd->oobsize); - /* - * Write lock mark to 8th word of sector0 of page0 of the spare0. - * We write 16 bytes spare area instead of 2 bytes. - * For Flex-OneNAND, we write lock mark to 1st word of sector 4 of - * main area of page 49. - */ - - from = 0; - len = FLEXONENAND(this) ? mtd->writesize : 16; - - /* - * Note: OTP lock operation - * OTP block : 0xXXFC XX 1111 1100 - * 1st block : 0xXXF3 (If chip support) XX 1111 0011 - * Both : 0xXXF0 (If chip support) XX 1111 0000 - */ - if (FLEXONENAND(this)) - otp_lock_offset = FLEXONENAND_OTP_LOCK_OFFSET; - - /* ONENAND_OTP_AREA | ONENAND_OTP_BLOCK0 | ONENAND_OTP_AREA_BLOCK0 */ - if (otp == 1) - buf[otp_lock_offset] = 0xFC; - else if (otp == 2) - buf[otp_lock_offset] = 0xF3; - else if (otp == 3) - buf[otp_lock_offset] = 0xF0; - else if (otp != 0) - printk(KERN_DEBUG "[OneNAND] Invalid option selected for OTP\n"); - - ret = onenand_otp_walk(mtd, from, len, &retlen, buf, do_otp_lock, MTD_OTP_USER); - - return ret ? : retlen; -} - -#endif /* CONFIG_MTD_ONENAND_OTP */ - -/** - * onenand_check_features - Check and set OneNAND features - * @param mtd MTD data structure - * - * Check and set OneNAND features - * - lock scheme - * - two plane - */ -static void onenand_check_features(struct mtd_info *mtd) -{ - struct onenand_chip *this = mtd->priv; - unsigned int density, process, numbufs; - - /* Lock scheme depends on density and process */ - density = onenand_get_density(this->device_id); - process = this->version_id >> ONENAND_VERSION_PROCESS_SHIFT; - numbufs = this->read_word(this->base + ONENAND_REG_NUM_BUFFERS) >> 8; - - /* Lock scheme */ - switch (density) { - case ONENAND_DEVICE_DENSITY_4Gb: - if (ONENAND_IS_DDP(this)) - this->options |= ONENAND_HAS_2PLANE; - else if (numbufs == 1) { - this->options |= ONENAND_HAS_4KB_PAGE; - this->options |= ONENAND_HAS_CACHE_PROGRAM; - /* - * There are two different 4KiB pagesize chips - * and no way to detect it by H/W config values. - * - * To detect the correct NOP for each chips, - * It should check the version ID as workaround. - * - * Now it has as following - * KFM4G16Q4M has NOP 4 with version ID 0x0131 - * KFM4G16Q5M has NOP 1 with versoin ID 0x013e - */ - if ((this->version_id & 0xf) == 0xe) - this->options |= ONENAND_HAS_NOP_1; - } - - case ONENAND_DEVICE_DENSITY_2Gb: - /* 2Gb DDP does not have 2 plane */ - if (!ONENAND_IS_DDP(this)) - this->options |= ONENAND_HAS_2PLANE; - this->options |= ONENAND_HAS_UNLOCK_ALL; - - case ONENAND_DEVICE_DENSITY_1Gb: - /* A-Die has all block unlock */ - if (process) - this->options |= ONENAND_HAS_UNLOCK_ALL; - break; - - default: - /* Some OneNAND has continuous lock scheme */ - if (!process) - this->options |= ONENAND_HAS_CONT_LOCK; - break; - } - - /* The MLC has 4KiB pagesize. */ - if (ONENAND_IS_MLC(this)) - this->options |= ONENAND_HAS_4KB_PAGE; - - if (ONENAND_IS_4KB_PAGE(this)) - this->options &= ~ONENAND_HAS_2PLANE; - - if (FLEXONENAND(this)) { - this->options &= ~ONENAND_HAS_CONT_LOCK; - this->options |= ONENAND_HAS_UNLOCK_ALL; - } - - if (this->options & ONENAND_HAS_CONT_LOCK) - printk(KERN_DEBUG "Lock scheme is Continuous Lock\n"); - if (this->options & ONENAND_HAS_UNLOCK_ALL) - printk(KERN_DEBUG "Chip support all block unlock\n"); - if (this->options & ONENAND_HAS_2PLANE) - printk(KERN_DEBUG "Chip has 2 plane\n"); - if (this->options & ONENAND_HAS_4KB_PAGE) - printk(KERN_DEBUG "Chip has 4KiB pagesize\n"); - if (this->options & ONENAND_HAS_CACHE_PROGRAM) - printk(KERN_DEBUG "Chip has cache program feature\n"); -} - -/** - * onenand_print_device_info - Print device & version ID - * @param device device ID - * @param version version ID - * - * Print device & version ID - */ -static void onenand_print_device_info(int device, int version) -{ - int vcc, demuxed, ddp, density, flexonenand; - - vcc = device & ONENAND_DEVICE_VCC_MASK; - demuxed = device & ONENAND_DEVICE_IS_DEMUX; - ddp = device & ONENAND_DEVICE_IS_DDP; - density = onenand_get_density(device); - flexonenand = device & DEVICE_IS_FLEXONENAND; - printk(KERN_INFO "%s%sOneNAND%s %dMB %sV 16-bit (0x%02x)\n", - demuxed ? "" : "Muxed ", - flexonenand ? "Flex-" : "", - ddp ? "(DDP)" : "", - (16 << density), - vcc ? "2.65/3.3" : "1.8", - device); - printk(KERN_INFO "OneNAND version = 0x%04x\n", version); -} - -static const struct onenand_manufacturers onenand_manuf_ids[] = { - {ONENAND_MFR_SAMSUNG, "Samsung"}, - {ONENAND_MFR_NUMONYX, "Numonyx"}, -}; - -/** - * onenand_check_maf - Check manufacturer ID - * @param manuf manufacturer ID - * - * Check manufacturer ID - */ -static int onenand_check_maf(int manuf) -{ - int size = ARRAY_SIZE(onenand_manuf_ids); - char *name; - int i; - - for (i = 0; i < size; i++) - if (manuf == onenand_manuf_ids[i].id) - break; - - if (i < size) - name = onenand_manuf_ids[i].name; - else - name = "Unknown"; - - printk(KERN_DEBUG "OneNAND Manufacturer: %s (0x%0x)\n", name, manuf); - - return (i == size); -} - -/** -* flexonenand_get_boundary - Reads the SLC boundary -* @param onenand_info - onenand info structure -**/ -static int flexonenand_get_boundary(struct mtd_info *mtd) -{ - struct onenand_chip *this = mtd->priv; - unsigned die, bdry; - int syscfg, locked; - - /* Disable ECC */ - syscfg = this->read_word(this->base + ONENAND_REG_SYS_CFG1); - this->write_word((syscfg | 0x0100), this->base + ONENAND_REG_SYS_CFG1); - - for (die = 0; die < this->dies; die++) { - this->command(mtd, FLEXONENAND_CMD_PI_ACCESS, die, 0); - this->wait(mtd, FL_SYNCING); - - this->command(mtd, FLEXONENAND_CMD_READ_PI, die, 0); - this->wait(mtd, FL_READING); - - bdry = this->read_word(this->base + ONENAND_DATARAM); - if ((bdry >> FLEXONENAND_PI_UNLOCK_SHIFT) == 3) - locked = 0; - else - locked = 1; - this->boundary[die] = bdry & FLEXONENAND_PI_MASK; - - this->command(mtd, ONENAND_CMD_RESET, 0, 0); - this->wait(mtd, FL_RESETING); - - printk(KERN_INFO "Die %d boundary: %d%s\n", die, - this->boundary[die], locked ? "(Locked)" : "(Unlocked)"); - } - - /* Enable ECC */ - this->write_word(syscfg, this->base + ONENAND_REG_SYS_CFG1); - return 0; -} - -/** - * flexonenand_get_size - Fill up fields in onenand_chip and mtd_info - * boundary[], diesize[], mtd->size, mtd->erasesize - * @param mtd - MTD device structure - */ -static void flexonenand_get_size(struct mtd_info *mtd) -{ - struct onenand_chip *this = mtd->priv; - int die, i, eraseshift, density; - int blksperdie, maxbdry; - loff_t ofs; - - density = onenand_get_density(this->device_id); - blksperdie = ((loff_t)(16 << density) << 20) >> (this->erase_shift); - blksperdie >>= ONENAND_IS_DDP(this) ? 1 : 0; - maxbdry = blksperdie - 1; - eraseshift = this->erase_shift - 1; - - mtd->numeraseregions = this->dies << 1; - - /* This fills up the device boundary */ - flexonenand_get_boundary(mtd); - die = ofs = 0; - i = -1; - for (; die < this->dies; die++) { - if (!die || this->boundary[die-1] != maxbdry) { - i++; - mtd->eraseregions[i].offset = ofs; - mtd->eraseregions[i].erasesize = 1 << eraseshift; - mtd->eraseregions[i].numblocks = - this->boundary[die] + 1; - ofs += mtd->eraseregions[i].numblocks << eraseshift; - eraseshift++; - } else { - mtd->numeraseregions -= 1; - mtd->eraseregions[i].numblocks += - this->boundary[die] + 1; - ofs += (this->boundary[die] + 1) << (eraseshift - 1); - } - if (this->boundary[die] != maxbdry) { - i++; - mtd->eraseregions[i].offset = ofs; - mtd->eraseregions[i].erasesize = 1 << eraseshift; - mtd->eraseregions[i].numblocks = maxbdry ^ - this->boundary[die]; - ofs += mtd->eraseregions[i].numblocks << eraseshift; - eraseshift--; - } else - mtd->numeraseregions -= 1; - } - - /* Expose MLC erase size except when all blocks are SLC */ - mtd->erasesize = 1 << this->erase_shift; - if (mtd->numeraseregions == 1) - mtd->erasesize >>= 1; - - printk(KERN_INFO "Device has %d eraseregions\n", mtd->numeraseregions); - for (i = 0; i < mtd->numeraseregions; i++) - printk(KERN_INFO "[offset: 0x%08x, erasesize: 0x%05x," - " numblocks: %04u]\n", - (unsigned int) mtd->eraseregions[i].offset, - mtd->eraseregions[i].erasesize, - mtd->eraseregions[i].numblocks); - - for (die = 0, mtd->size = 0; die < this->dies; die++) { - this->diesize[die] = (loff_t)blksperdie << this->erase_shift; - this->diesize[die] -= (loff_t)(this->boundary[die] + 1) - << (this->erase_shift - 1); - mtd->size += this->diesize[die]; - } -} - -/** - * flexonenand_check_blocks_erased - Check if blocks are erased - * @param mtd_info - mtd info structure - * @param start - first erase block to check - * @param end - last erase block to check - * - * Converting an unerased block from MLC to SLC - * causes byte values to change. Since both data and its ECC - * have changed, reads on the block give uncorrectable error. - * This might lead to the block being detected as bad. - * - * Avoid this by ensuring that the block to be converted is - * erased. - */ -static int flexonenand_check_blocks_erased(struct mtd_info *mtd, int start, int end) -{ - struct onenand_chip *this = mtd->priv; - int i, ret; - int block; - struct mtd_oob_ops ops = { - .mode = MTD_OPS_PLACE_OOB, - .ooboffs = 0, - .ooblen = mtd->oobsize, - .datbuf = NULL, - .oobbuf = this->oob_buf, - }; - loff_t addr; - - printk(KERN_DEBUG "Check blocks from %d to %d\n", start, end); - - for (block = start; block <= end; block++) { - addr = flexonenand_addr(this, block); - if (onenand_block_isbad_nolock(mtd, addr, 0)) - continue; - - /* - * Since main area write results in ECC write to spare, - * it is sufficient to check only ECC bytes for change. - */ - ret = onenand_read_oob_nolock(mtd, addr, &ops); - if (ret) - return ret; - - for (i = 0; i < mtd->oobsize; i++) - if (this->oob_buf[i] != 0xff) - break; - - if (i != mtd->oobsize) { - printk(KERN_WARNING "%s: Block %d not erased.\n", - __func__, block); - return 1; - } - } - - return 0; -} - -/** - * flexonenand_set_boundary - Writes the SLC boundary - * @param mtd - mtd info structure - */ -static int flexonenand_set_boundary(struct mtd_info *mtd, int die, - int boundary, int lock) -{ - struct onenand_chip *this = mtd->priv; - int ret, density, blksperdie, old, new, thisboundary; - loff_t addr; - - /* Change only once for SDP Flex-OneNAND */ - if (die && (!ONENAND_IS_DDP(this))) - return 0; - - /* boundary value of -1 indicates no required change */ - if (boundary < 0 || boundary == this->boundary[die]) - return 0; - - density = onenand_get_density(this->device_id); - blksperdie = ((16 << density) << 20) >> this->erase_shift; - blksperdie >>= ONENAND_IS_DDP(this) ? 1 : 0; - - if (boundary >= blksperdie) { - printk(KERN_ERR "%s: Invalid boundary value. " - "Boundary not changed.\n", __func__); - return -EINVAL; - } - - /* Check if converting blocks are erased */ - old = this->boundary[die] + (die * this->density_mask); - new = boundary + (die * this->density_mask); - ret = flexonenand_check_blocks_erased(mtd, min(old, new) + 1, max(old, new)); - if (ret) { - printk(KERN_ERR "%s: Please erase blocks " - "before boundary change\n", __func__); - return ret; - } - - this->command(mtd, FLEXONENAND_CMD_PI_ACCESS, die, 0); - this->wait(mtd, FL_SYNCING); - - /* Check is boundary is locked */ - this->command(mtd, FLEXONENAND_CMD_READ_PI, die, 0); - this->wait(mtd, FL_READING); - - thisboundary = this->read_word(this->base + ONENAND_DATARAM); - if ((thisboundary >> FLEXONENAND_PI_UNLOCK_SHIFT) != 3) { - printk(KERN_ERR "%s: boundary locked\n", __func__); - ret = 1; - goto out; - } - - printk(KERN_INFO "Changing die %d boundary: %d%s\n", - die, boundary, lock ? "(Locked)" : "(Unlocked)"); - - addr = die ? this->diesize[0] : 0; - - boundary &= FLEXONENAND_PI_MASK; - boundary |= lock ? 0 : (3 << FLEXONENAND_PI_UNLOCK_SHIFT); - - this->command(mtd, ONENAND_CMD_ERASE, addr, 0); - ret = this->wait(mtd, FL_ERASING); - if (ret) { - printk(KERN_ERR "%s: Failed PI erase for Die %d\n", - __func__, die); - goto out; - } - - this->write_word(boundary, this->base + ONENAND_DATARAM); - this->command(mtd, ONENAND_CMD_PROG, addr, 0); - ret = this->wait(mtd, FL_WRITING); - if (ret) { - printk(KERN_ERR "%s: Failed PI write for Die %d\n", - __func__, die); - goto out; - } - - this->command(mtd, FLEXONENAND_CMD_PI_UPDATE, die, 0); - ret = this->wait(mtd, FL_WRITING); -out: - this->write_word(ONENAND_CMD_RESET, this->base + ONENAND_REG_COMMAND); - this->wait(mtd, FL_RESETING); - if (!ret) - /* Recalculate device size on boundary change*/ - flexonenand_get_size(mtd); - - return ret; -} - -/** - * onenand_chip_probe - [OneNAND Interface] The generic chip probe - * @param mtd MTD device structure - * - * OneNAND detection method: - * Compare the values from command with ones from register - */ -static int onenand_chip_probe(struct mtd_info *mtd) -{ - struct onenand_chip *this = mtd->priv; - int bram_maf_id, bram_dev_id, maf_id, dev_id; - int syscfg; - - /* Save system configuration 1 */ - syscfg = this->read_word(this->base + ONENAND_REG_SYS_CFG1); - /* Clear Sync. Burst Read mode to read BootRAM */ - this->write_word((syscfg & ~ONENAND_SYS_CFG1_SYNC_READ & ~ONENAND_SYS_CFG1_SYNC_WRITE), this->base + ONENAND_REG_SYS_CFG1); - - /* Send the command for reading device ID from BootRAM */ - this->write_word(ONENAND_CMD_READID, this->base + ONENAND_BOOTRAM); - - /* Read manufacturer and device IDs from BootRAM */ - bram_maf_id = this->read_word(this->base + ONENAND_BOOTRAM + 0x0); - bram_dev_id = this->read_word(this->base + ONENAND_BOOTRAM + 0x2); - - /* Reset OneNAND to read default register values */ - this->write_word(ONENAND_CMD_RESET, this->base + ONENAND_BOOTRAM); - /* Wait reset */ - this->wait(mtd, FL_RESETING); - - /* Restore system configuration 1 */ - this->write_word(syscfg, this->base + ONENAND_REG_SYS_CFG1); - - /* Check manufacturer ID */ - if (onenand_check_maf(bram_maf_id)) - return -ENXIO; - - /* Read manufacturer and device IDs from Register */ - maf_id = this->read_word(this->base + ONENAND_REG_MANUFACTURER_ID); - dev_id = this->read_word(this->base + ONENAND_REG_DEVICE_ID); - - /* Check OneNAND device */ - if (maf_id != bram_maf_id || dev_id != bram_dev_id) - return -ENXIO; - - return 0; -} - -/** - * onenand_probe - [OneNAND Interface] Probe the OneNAND device - * @param mtd MTD device structure - */ -static int onenand_probe(struct mtd_info *mtd) -{ - struct onenand_chip *this = mtd->priv; - int dev_id, ver_id; - int density; - int ret; - - ret = this->chip_probe(mtd); - if (ret) - return ret; - - /* Device and version IDs from Register */ - dev_id = this->read_word(this->base + ONENAND_REG_DEVICE_ID); - ver_id = this->read_word(this->base + ONENAND_REG_VERSION_ID); - this->technology = this->read_word(this->base + ONENAND_REG_TECHNOLOGY); - - /* Flash device information */ - onenand_print_device_info(dev_id, ver_id); - this->device_id = dev_id; - this->version_id = ver_id; - - /* Check OneNAND features */ - onenand_check_features(mtd); - - density = onenand_get_density(dev_id); - if (FLEXONENAND(this)) { - this->dies = ONENAND_IS_DDP(this) ? 2 : 1; - /* Maximum possible erase regions */ - mtd->numeraseregions = this->dies << 1; - mtd->eraseregions = kzalloc(sizeof(struct mtd_erase_region_info) - * (this->dies << 1), GFP_KERNEL); - if (!mtd->eraseregions) - return -ENOMEM; - } - - /* - * For Flex-OneNAND, chipsize represents maximum possible device size. - * mtd->size represents the actual device size. - */ - this->chipsize = (16 << density) << 20; - - /* OneNAND page size & block size */ - /* The data buffer size is equal to page size */ - mtd->writesize = this->read_word(this->base + ONENAND_REG_DATA_BUFFER_SIZE); - /* We use the full BufferRAM */ - if (ONENAND_IS_4KB_PAGE(this)) - mtd->writesize <<= 1; - - mtd->oobsize = mtd->writesize >> 5; - /* Pages per a block are always 64 in OneNAND */ - mtd->erasesize = mtd->writesize << 6; - /* - * Flex-OneNAND SLC area has 64 pages per block. - * Flex-OneNAND MLC area has 128 pages per block. - * Expose MLC erase size to find erase_shift and page_mask. - */ - if (FLEXONENAND(this)) - mtd->erasesize <<= 1; - - this->erase_shift = ffs(mtd->erasesize) - 1; - this->page_shift = ffs(mtd->writesize) - 1; - this->page_mask = (1 << (this->erase_shift - this->page_shift)) - 1; - /* Set density mask. it is used for DDP */ - if (ONENAND_IS_DDP(this)) - this->density_mask = this->chipsize >> (this->erase_shift + 1); - /* It's real page size */ - this->writesize = mtd->writesize; - - /* REVISIT: Multichip handling */ - - if (FLEXONENAND(this)) - flexonenand_get_size(mtd); - else - mtd->size = this->chipsize; - - /* - * We emulate the 4KiB page and 256KiB erase block size - * But oobsize is still 64 bytes. - * It is only valid if you turn on 2X program support, - * Otherwise it will be ignored by compiler. - */ - if (ONENAND_IS_2PLANE(this)) { - mtd->writesize <<= 1; - mtd->erasesize <<= 1; - } - - return 0; -} - -/** - * onenand_suspend - [MTD Interface] Suspend the OneNAND flash - * @param mtd MTD device structure - */ -static int onenand_suspend(struct mtd_info *mtd) -{ - return onenand_get_device(mtd, FL_PM_SUSPENDED); -} - -/** - * onenand_resume - [MTD Interface] Resume the OneNAND flash - * @param mtd MTD device structure - */ -static void onenand_resume(struct mtd_info *mtd) -{ - struct onenand_chip *this = mtd->priv; - - if (this->state == FL_PM_SUSPENDED) - onenand_release_device(mtd); - else - printk(KERN_ERR "%s: resume() called for the chip which is not " - "in suspended state\n", __func__); -} - -/** - * onenand_scan - [OneNAND Interface] Scan for the OneNAND device - * @param mtd MTD device structure - * @param maxchips Number of chips to scan for - * - * This fills out all the not initialized function pointers - * with the defaults. - * The flash ID is read and the mtd/chip structures are - * filled with the appropriate values. - */ -int onenand_scan(struct mtd_info *mtd, int maxchips) -{ - int i, ret; - struct onenand_chip *this = mtd->priv; - - if (!this->read_word) - this->read_word = onenand_readw; - if (!this->write_word) - this->write_word = onenand_writew; - - if (!this->command) - this->command = onenand_command; - if (!this->wait) - onenand_setup_wait(mtd); - if (!this->bbt_wait) - this->bbt_wait = onenand_bbt_wait; - if (!this->unlock_all) - this->unlock_all = onenand_unlock_all; - - if (!this->chip_probe) - this->chip_probe = onenand_chip_probe; - - if (!this->read_bufferram) - this->read_bufferram = onenand_read_bufferram; - if (!this->write_bufferram) - this->write_bufferram = onenand_write_bufferram; - - if (!this->block_markbad) - this->block_markbad = onenand_default_block_markbad; - if (!this->scan_bbt) - this->scan_bbt = onenand_default_bbt; - - if (onenand_probe(mtd)) - return -ENXIO; - - /* Set Sync. Burst Read after probing */ - if (this->mmcontrol) { - printk(KERN_INFO "OneNAND Sync. Burst Read support\n"); - this->read_bufferram = onenand_sync_read_bufferram; - } - - /* Allocate buffers, if necessary */ - if (!this->page_buf) { - this->page_buf = kzalloc(mtd->writesize, GFP_KERNEL); - if (!this->page_buf) - return -ENOMEM; -#ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE - this->verify_buf = kzalloc(mtd->writesize, GFP_KERNEL); - if (!this->verify_buf) { - kfree(this->page_buf); - return -ENOMEM; - } -#endif - this->options |= ONENAND_PAGEBUF_ALLOC; - } - if (!this->oob_buf) { - this->oob_buf = kzalloc(mtd->oobsize, GFP_KERNEL); - if (!this->oob_buf) { - if (this->options & ONENAND_PAGEBUF_ALLOC) { - this->options &= ~ONENAND_PAGEBUF_ALLOC; - kfree(this->page_buf); - } - return -ENOMEM; - } - this->options |= ONENAND_OOBBUF_ALLOC; - } - - this->state = FL_READY; - init_waitqueue_head(&this->wq); - spin_lock_init(&this->chip_lock); - - /* - * Allow subpage writes up to oobsize. - */ - switch (mtd->oobsize) { - case 128: - if (FLEXONENAND(this)) { - mtd_set_ooblayout(mtd, &flexonenand_ooblayout_ops); - mtd->subpage_sft = 0; - } else { - mtd_set_ooblayout(mtd, &onenand_oob_128_ooblayout_ops); - mtd->subpage_sft = 2; - } - if (ONENAND_IS_NOP_1(this)) - mtd->subpage_sft = 0; - break; - case 64: - mtd_set_ooblayout(mtd, &onenand_oob_32_64_ooblayout_ops); - mtd->subpage_sft = 2; - break; - - case 32: - mtd_set_ooblayout(mtd, &onenand_oob_32_64_ooblayout_ops); - mtd->subpage_sft = 1; - break; - - default: - printk(KERN_WARNING "%s: No OOB scheme defined for oobsize %d\n", - __func__, mtd->oobsize); - mtd->subpage_sft = 0; - /* To prevent kernel oops */ - mtd_set_ooblayout(mtd, &onenand_oob_32_64_ooblayout_ops); - break; - } - - this->subpagesize = mtd->writesize >> mtd->subpage_sft; - - /* - * The number of bytes available for a client to place data into - * the out of band area - */ - ret = mtd_ooblayout_count_freebytes(mtd); - if (ret < 0) - ret = 0; - - mtd->oobavail = ret; - - mtd->ecc_strength = 1; - - /* Fill in remaining MTD driver data */ - mtd->type = ONENAND_IS_MLC(this) ? MTD_MLCNANDFLASH : MTD_NANDFLASH; - mtd->flags = MTD_CAP_NANDFLASH; - mtd->_erase = onenand_erase; - mtd->_point = NULL; - mtd->_unpoint = NULL; - mtd->_read_oob = onenand_read_oob; - mtd->_write_oob = onenand_write_oob; - mtd->_panic_write = onenand_panic_write; -#ifdef CONFIG_MTD_ONENAND_OTP - mtd->_get_fact_prot_info = onenand_get_fact_prot_info; - mtd->_read_fact_prot_reg = onenand_read_fact_prot_reg; - mtd->_get_user_prot_info = onenand_get_user_prot_info; - mtd->_read_user_prot_reg = onenand_read_user_prot_reg; - mtd->_write_user_prot_reg = onenand_write_user_prot_reg; - mtd->_lock_user_prot_reg = onenand_lock_user_prot_reg; -#endif - mtd->_sync = onenand_sync; - mtd->_lock = onenand_lock; - mtd->_unlock = onenand_unlock; - mtd->_suspend = onenand_suspend; - mtd->_resume = onenand_resume; - mtd->_block_isbad = onenand_block_isbad; - mtd->_block_markbad = onenand_block_markbad; - mtd->owner = THIS_MODULE; - mtd->writebufsize = mtd->writesize; - - /* Unlock whole block */ - if (!(this->options & ONENAND_SKIP_INITIAL_UNLOCKING)) - this->unlock_all(mtd); - - ret = this->scan_bbt(mtd); - if ((!FLEXONENAND(this)) || ret) - return ret; - - /* Change Flex-OneNAND boundaries if required */ - for (i = 0; i < MAX_DIES; i++) - flexonenand_set_boundary(mtd, i, flex_bdry[2 * i], - flex_bdry[(2 * i) + 1]); - - return 0; -} - -/** - * onenand_release - [OneNAND Interface] Free resources held by the OneNAND device - * @param mtd MTD device structure - */ -void onenand_release(struct mtd_info *mtd) -{ - struct onenand_chip *this = mtd->priv; - - /* Deregister partitions */ - mtd_device_unregister(mtd); - - /* Free bad block table memory, if allocated */ - if (this->bbm) { - struct bbm_info *bbm = this->bbm; - kfree(bbm->bbt); - kfree(this->bbm); - } - /* Buffers allocated by onenand_scan */ - if (this->options & ONENAND_PAGEBUF_ALLOC) { - kfree(this->page_buf); -#ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE - kfree(this->verify_buf); -#endif - } - if (this->options & ONENAND_OOBBUF_ALLOC) - kfree(this->oob_buf); - kfree(mtd->eraseregions); -} - -EXPORT_SYMBOL_GPL(onenand_scan); -EXPORT_SYMBOL_GPL(onenand_release); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Kyungmin Park <kyungmin.park@samsung.com>"); -MODULE_DESCRIPTION("Generic OneNAND flash driver code"); diff --git a/drivers/mtd/onenand/onenand_bbt.c b/drivers/mtd/onenand/onenand_bbt.c deleted file mode 100644 index dde2048..0000000 --- a/drivers/mtd/onenand/onenand_bbt.c +++ /dev/null @@ -1,248 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Bad Block Table support for the OneNAND driver - * - * Copyright(c) 2005 Samsung Electronics - * Kyungmin Park <kyungmin.park@samsung.com> - * - * Derived from nand_bbt.c - * - * TODO: - * Split BBT core and chip specific BBT. - */ - -#include <linux/slab.h> -#include <linux/mtd/mtd.h> -#include <linux/mtd/onenand.h> -#include <linux/export.h> - -/** - * check_short_pattern - [GENERIC] check if a pattern is in the buffer - * @param buf the buffer to search - * @param len the length of buffer to search - * @param paglen the pagelength - * @param td search pattern descriptor - * - * Check for a pattern at the given place. Used to search bad block - * tables and good / bad block identifiers. Same as check_pattern, but - * no optional empty check and the pattern is expected to start - * at offset 0. - * - */ -static int check_short_pattern(uint8_t *buf, int len, int paglen, struct nand_bbt_descr *td) -{ - int i; - uint8_t *p = buf; - - /* Compare the pattern */ - for (i = 0; i < td->len; i++) { - if (p[i] != td->pattern[i]) - return -1; - } - return 0; -} - -/** - * create_bbt - [GENERIC] Create a bad block table by scanning the device - * @param mtd MTD device structure - * @param buf temporary buffer - * @param bd descriptor for the good/bad block search pattern - * @param chip create the table for a specific chip, -1 read all chips. - * Applies only if NAND_BBT_PERCHIP option is set - * - * Create a bad block table by scanning the device - * for the given good/bad block identify pattern - */ -static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd, int chip) -{ - struct onenand_chip *this = mtd->priv; - struct bbm_info *bbm = this->bbm; - int i, j, numblocks, len, scanlen; - int startblock; - loff_t from; - size_t readlen, ooblen; - struct mtd_oob_ops ops; - int rgn; - - printk(KERN_INFO "Scanning device for bad blocks\n"); - - len = 2; - - /* We need only read few bytes from the OOB area */ - scanlen = ooblen = 0; - readlen = bd->len; - - /* chip == -1 case only */ - /* Note that numblocks is 2 * (real numblocks) here; - * see i += 2 below as it makses shifting and masking less painful - */ - numblocks = this->chipsize >> (bbm->bbt_erase_shift - 1); - startblock = 0; - from = 0; - - ops.mode = MTD_OPS_PLACE_OOB; - ops.ooblen = readlen; - ops.oobbuf = buf; - ops.len = ops.ooboffs = ops.retlen = ops.oobretlen = 0; - - for (i = startblock; i < numblocks; ) { - int ret; - - for (j = 0; j < len; j++) { - /* No need to read pages fully, - * just read required OOB bytes */ - ret = onenand_bbt_read_oob(mtd, - from + j * this->writesize + bd->offs, &ops); - - /* If it is a initial bad block, just ignore it */ - if (ret == ONENAND_BBT_READ_FATAL_ERROR) - return -EIO; - - if (ret || check_short_pattern(&buf[j * scanlen], - scanlen, this->writesize, bd)) { - bbm->bbt[i >> 3] |= 0x03 << (i & 0x6); - printk(KERN_INFO "OneNAND eraseblock %d is an " - "initial bad block\n", i >> 1); - mtd->ecc_stats.badblocks++; - break; - } - } - i += 2; - - if (FLEXONENAND(this)) { - rgn = flexonenand_region(mtd, from); - from += mtd->eraseregions[rgn].erasesize; - } else - from += (1 << bbm->bbt_erase_shift); - } - - return 0; -} - - -/** - * onenand_memory_bbt - [GENERIC] create a memory based bad block table - * @param mtd MTD device structure - * @param bd descriptor for the good/bad block search pattern - * - * The function creates a memory based bbt by scanning the device - * for manufacturer / software marked good / bad blocks - */ -static inline int onenand_memory_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd) -{ - struct onenand_chip *this = mtd->priv; - - return create_bbt(mtd, this->page_buf, bd, -1); -} - -/** - * onenand_isbad_bbt - [OneNAND Interface] Check if a block is bad - * @param mtd MTD device structure - * @param offs offset in the device - * @param allowbbt allow access to bad block table region - */ -static int onenand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt) -{ - struct onenand_chip *this = mtd->priv; - struct bbm_info *bbm = this->bbm; - int block; - uint8_t res; - - /* Get block number * 2 */ - block = (int) (onenand_block(this, offs) << 1); - res = (bbm->bbt[block >> 3] >> (block & 0x06)) & 0x03; - - pr_debug("onenand_isbad_bbt: bbt info for offs 0x%08x: (block %d) 0x%02x\n", - (unsigned int) offs, block >> 1, res); - - switch ((int) res) { - case 0x00: return 0; - case 0x01: return 1; - case 0x02: return allowbbt ? 0 : 1; - } - - return 1; -} - -/** - * onenand_scan_bbt - [OneNAND Interface] scan, find, read and maybe create bad block table(s) - * @param mtd MTD device structure - * @param bd descriptor for the good/bad block search pattern - * - * The function checks, if a bad block table(s) is/are already - * available. If not it scans the device for manufacturer - * marked good / bad blocks and writes the bad block table(s) to - * the selected place. - * - * The bad block table memory is allocated here. It is freed - * by the onenand_release function. - * - */ -static int onenand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd) -{ - struct onenand_chip *this = mtd->priv; - struct bbm_info *bbm = this->bbm; - int len, ret = 0; - - len = this->chipsize >> (this->erase_shift + 2); - /* Allocate memory (2bit per block) and clear the memory bad block table */ - bbm->bbt = kzalloc(len, GFP_KERNEL); - if (!bbm->bbt) - return -ENOMEM; - - /* Set the bad block position */ - bbm->badblockpos = ONENAND_BADBLOCK_POS; - - /* Set erase shift */ - bbm->bbt_erase_shift = this->erase_shift; - - if (!bbm->isbad_bbt) - bbm->isbad_bbt = onenand_isbad_bbt; - - /* Scan the device to build a memory based bad block table */ - if ((ret = onenand_memory_bbt(mtd, bd))) { - printk(KERN_ERR "onenand_scan_bbt: Can't scan flash and build the RAM-based BBT\n"); - kfree(bbm->bbt); - bbm->bbt = NULL; - } - - return ret; -} - -/* - * Define some generic bad / good block scan pattern which are used - * while scanning a device for factory marked good / bad blocks. - */ -static uint8_t scan_ff_pattern[] = { 0xff, 0xff }; - -static struct nand_bbt_descr largepage_memorybased = { - .options = 0, - .offs = 0, - .len = 2, - .pattern = scan_ff_pattern, -}; - -/** - * onenand_default_bbt - [OneNAND Interface] Select a default bad block table for the device - * @param mtd MTD device structure - * - * This function selects the default bad block table - * support for the device and calls the onenand_scan_bbt function - */ -int onenand_default_bbt(struct mtd_info *mtd) -{ - struct onenand_chip *this = mtd->priv; - struct bbm_info *bbm; - - this->bbm = kzalloc(sizeof(struct bbm_info), GFP_KERNEL); - if (!this->bbm) - return -ENOMEM; - - bbm = this->bbm; - - /* 1KB page has same configuration as 2KB page */ - if (!bbm->badblock_pattern) - bbm->badblock_pattern = &largepage_memorybased; - - return onenand_scan_bbt(mtd, bbm->badblock_pattern); -} diff --git a/drivers/mtd/onenand/samsung.c b/drivers/mtd/onenand/samsung.c deleted file mode 100644 index 2e9d076..0000000 --- a/drivers/mtd/onenand/samsung.c +++ /dev/null @@ -1,1012 +0,0 @@ -/* - * Samsung S3C64XX/S5PC1XX OneNAND driver - * - * Copyright © 2008-2010 Samsung Electronics - * Kyungmin Park <kyungmin.park@samsung.com> - * Marek Szyprowski <m.szyprowski@samsung.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Implementation: - * S3C64XX: emulate the pseudo BufferRAM - * S5PC110: use DMA - */ - -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/sched.h> -#include <linux/slab.h> -#include <linux/mtd/mtd.h> -#include <linux/mtd/onenand.h> -#include <linux/mtd/partitions.h> -#include <linux/dma-mapping.h> -#include <linux/interrupt.h> -#include <linux/io.h> - -#include "samsung.h" - -enum soc_type { - TYPE_S3C6400, - TYPE_S3C6410, - TYPE_S5PC110, -}; - -#define ONENAND_ERASE_STATUS 0x00 -#define ONENAND_MULTI_ERASE_SET 0x01 -#define ONENAND_ERASE_START 0x03 -#define ONENAND_UNLOCK_START 0x08 -#define ONENAND_UNLOCK_END 0x09 -#define ONENAND_LOCK_START 0x0A -#define ONENAND_LOCK_END 0x0B -#define ONENAND_LOCK_TIGHT_START 0x0C -#define ONENAND_LOCK_TIGHT_END 0x0D -#define ONENAND_UNLOCK_ALL 0x0E -#define ONENAND_OTP_ACCESS 0x12 -#define ONENAND_SPARE_ACCESS_ONLY 0x13 -#define ONENAND_MAIN_ACCESS_ONLY 0x14 -#define ONENAND_ERASE_VERIFY 0x15 -#define ONENAND_MAIN_SPARE_ACCESS 0x16 -#define ONENAND_PIPELINE_READ 0x4000 - -#define MAP_00 (0x0) -#define MAP_01 (0x1) -#define MAP_10 (0x2) -#define MAP_11 (0x3) - -#define S3C64XX_CMD_MAP_SHIFT 24 - -#define S3C6400_FBA_SHIFT 10 -#define S3C6400_FPA_SHIFT 4 -#define S3C6400_FSA_SHIFT 2 - -#define S3C6410_FBA_SHIFT 12 -#define S3C6410_FPA_SHIFT 6 -#define S3C6410_FSA_SHIFT 4 - -/* S5PC110 specific definitions */ -#define S5PC110_DMA_SRC_ADDR 0x400 -#define S5PC110_DMA_SRC_CFG 0x404 -#define S5PC110_DMA_DST_ADDR 0x408 -#define S5PC110_DMA_DST_CFG 0x40C -#define S5PC110_DMA_TRANS_SIZE 0x414 -#define S5PC110_DMA_TRANS_CMD 0x418 -#define S5PC110_DMA_TRANS_STATUS 0x41C -#define S5PC110_DMA_TRANS_DIR 0x420 -#define S5PC110_INTC_DMA_CLR 0x1004 -#define S5PC110_INTC_ONENAND_CLR 0x1008 -#define S5PC110_INTC_DMA_MASK 0x1024 -#define S5PC110_INTC_ONENAND_MASK 0x1028 -#define S5PC110_INTC_DMA_PEND 0x1044 -#define S5PC110_INTC_ONENAND_PEND 0x1048 -#define S5PC110_INTC_DMA_STATUS 0x1064 -#define S5PC110_INTC_ONENAND_STATUS 0x1068 - -#define S5PC110_INTC_DMA_TD (1 << 24) -#define S5PC110_INTC_DMA_TE (1 << 16) - -#define S5PC110_DMA_CFG_SINGLE (0x0 << 16) -#define S5PC110_DMA_CFG_4BURST (0x2 << 16) -#define S5PC110_DMA_CFG_8BURST (0x3 << 16) -#define S5PC110_DMA_CFG_16BURST (0x4 << 16) - -#define S5PC110_DMA_CFG_INC (0x0 << 8) -#define S5PC110_DMA_CFG_CNT (0x1 << 8) - -#define S5PC110_DMA_CFG_8BIT (0x0 << 0) -#define S5PC110_DMA_CFG_16BIT (0x1 << 0) -#define S5PC110_DMA_CFG_32BIT (0x2 << 0) - -#define S5PC110_DMA_SRC_CFG_READ (S5PC110_DMA_CFG_16BURST | \ - S5PC110_DMA_CFG_INC | \ - S5PC110_DMA_CFG_16BIT) -#define S5PC110_DMA_DST_CFG_READ (S5PC110_DMA_CFG_16BURST | \ - S5PC110_DMA_CFG_INC | \ - S5PC110_DMA_CFG_32BIT) -#define S5PC110_DMA_SRC_CFG_WRITE (S5PC110_DMA_CFG_16BURST | \ - S5PC110_DMA_CFG_INC | \ - S5PC110_DMA_CFG_32BIT) -#define S5PC110_DMA_DST_CFG_WRITE (S5PC110_DMA_CFG_16BURST | \ - S5PC110_DMA_CFG_INC | \ - S5PC110_DMA_CFG_16BIT) - -#define S5PC110_DMA_TRANS_CMD_TDC (0x1 << 18) -#define S5PC110_DMA_TRANS_CMD_TEC (0x1 << 16) -#define S5PC110_DMA_TRANS_CMD_TR (0x1 << 0) - -#define S5PC110_DMA_TRANS_STATUS_TD (0x1 << 18) -#define S5PC110_DMA_TRANS_STATUS_TB (0x1 << 17) -#define S5PC110_DMA_TRANS_STATUS_TE (0x1 << 16) - -#define S5PC110_DMA_DIR_READ 0x0 -#define S5PC110_DMA_DIR_WRITE 0x1 - -struct s3c_onenand { - struct mtd_info *mtd; - struct platform_device *pdev; - enum soc_type type; - void __iomem *base; - void __iomem *ahb_addr; - int bootram_command; - void *page_buf; - void *oob_buf; - unsigned int (*mem_addr)(int fba, int fpa, int fsa); - unsigned int (*cmd_map)(unsigned int type, unsigned int val); - void __iomem *dma_addr; - unsigned long phys_base; - struct completion complete; -}; - -#define CMD_MAP_00(dev, addr) (dev->cmd_map(MAP_00, ((addr) << 1))) -#define CMD_MAP_01(dev, mem_addr) (dev->cmd_map(MAP_01, (mem_addr))) -#define CMD_MAP_10(dev, mem_addr) (dev->cmd_map(MAP_10, (mem_addr))) -#define CMD_MAP_11(dev, addr) (dev->cmd_map(MAP_11, ((addr) << 2))) - -static struct s3c_onenand *onenand; - -static inline int s3c_read_reg(int offset) -{ - return readl(onenand->base + offset); -} - -static inline void s3c_write_reg(int value, int offset) -{ - writel(value, onenand->base + offset); -} - -static inline int s3c_read_cmd(unsigned int cmd) -{ - return readl(onenand->ahb_addr + cmd); -} - -static inline void s3c_write_cmd(int value, unsigned int cmd) -{ - writel(value, onenand->ahb_addr + cmd); -} - -#ifdef SAMSUNG_DEBUG -static void s3c_dump_reg(void) -{ - int i; - - for (i = 0; i < 0x400; i += 0x40) { - printk(KERN_INFO "0x%08X: 0x%08x 0x%08x 0x%08x 0x%08x\n", - (unsigned int) onenand->base + i, - s3c_read_reg(i), s3c_read_reg(i + 0x10), - s3c_read_reg(i + 0x20), s3c_read_reg(i + 0x30)); - } -} -#endif - -static unsigned int s3c64xx_cmd_map(unsigned type, unsigned val) -{ - return (type << S3C64XX_CMD_MAP_SHIFT) | val; -} - -static unsigned int s3c6400_mem_addr(int fba, int fpa, int fsa) -{ - return (fba << S3C6400_FBA_SHIFT) | (fpa << S3C6400_FPA_SHIFT) | - (fsa << S3C6400_FSA_SHIFT); -} - -static unsigned int s3c6410_mem_addr(int fba, int fpa, int fsa) -{ - return (fba << S3C6410_FBA_SHIFT) | (fpa << S3C6410_FPA_SHIFT) | - (fsa << S3C6410_FSA_SHIFT); -} - -static void s3c_onenand_reset(void) -{ - unsigned long timeout = 0x10000; - int stat; - - s3c_write_reg(ONENAND_MEM_RESET_COLD, MEM_RESET_OFFSET); - while (1 && timeout--) { - stat = s3c_read_reg(INT_ERR_STAT_OFFSET); - if (stat & RST_CMP) - break; - } - stat = s3c_read_reg(INT_ERR_STAT_OFFSET); - s3c_write_reg(stat, INT_ERR_ACK_OFFSET); - - /* Clear interrupt */ - s3c_write_reg(0x0, INT_ERR_ACK_OFFSET); - /* Clear the ECC status */ - s3c_write_reg(0x0, ECC_ERR_STAT_OFFSET); -} - -static unsigned short s3c_onenand_readw(void __iomem *addr) -{ - struct onenand_chip *this = onenand->mtd->priv; - struct device *dev = &onenand->pdev->dev; - int reg = addr - this->base; - int word_addr = reg >> 1; - int value; - - /* It's used for probing time */ - switch (reg) { - case ONENAND_REG_MANUFACTURER_ID: - return s3c_read_reg(MANUFACT_ID_OFFSET); - case ONENAND_REG_DEVICE_ID: - return s3c_read_reg(DEVICE_ID_OFFSET); - case ONENAND_REG_VERSION_ID: - return s3c_read_reg(FLASH_VER_ID_OFFSET); - case ONENAND_REG_DATA_BUFFER_SIZE: - return s3c_read_reg(DATA_BUF_SIZE_OFFSET); - case ONENAND_REG_TECHNOLOGY: - return s3c_read_reg(TECH_OFFSET); - case ONENAND_REG_SYS_CFG1: - return s3c_read_reg(MEM_CFG_OFFSET); - - /* Used at unlock all status */ - case ONENAND_REG_CTRL_STATUS: - return 0; - - case ONENAND_REG_WP_STATUS: - return ONENAND_WP_US; - - default: - break; - } - - /* BootRAM access control */ - if ((unsigned int) addr < ONENAND_DATARAM && onenand->bootram_command) { - if (word_addr == 0) - return s3c_read_reg(MANUFACT_ID_OFFSET); - if (word_addr == 1) - return s3c_read_reg(DEVICE_ID_OFFSET); - if (word_addr == 2) - return s3c_read_reg(FLASH_VER_ID_OFFSET); - } - - value = s3c_read_cmd(CMD_MAP_11(onenand, word_addr)) & 0xffff; - dev_info(dev, "%s: Illegal access at reg 0x%x, value 0x%x\n", __func__, - word_addr, value); - return value; -} - -static void s3c_onenand_writew(unsigned short value, void __iomem *addr) -{ - struct onenand_chip *this = onenand->mtd->priv; - struct device *dev = &onenand->pdev->dev; - unsigned int reg = addr - this->base; - unsigned int word_addr = reg >> 1; - - /* It's used for probing time */ - switch (reg) { - case ONENAND_REG_SYS_CFG1: - s3c_write_reg(value, MEM_CFG_OFFSET); - return; - - case ONENAND_REG_START_ADDRESS1: - case ONENAND_REG_START_ADDRESS2: - return; - - /* Lock/lock-tight/unlock/unlock_all */ - case ONENAND_REG_START_BLOCK_ADDRESS: - return; - - default: - break; - } - - /* BootRAM access control */ - if ((unsigned int)addr < ONENAND_DATARAM) { - if (value == ONENAND_CMD_READID) { - onenand->bootram_command = 1; - return; - } - if (value == ONENAND_CMD_RESET) { - s3c_write_reg(ONENAND_MEM_RESET_COLD, MEM_RESET_OFFSET); - onenand->bootram_command = 0; - return; - } - } - - dev_info(dev, "%s: Illegal access at reg 0x%x, value 0x%x\n", __func__, - word_addr, value); - - s3c_write_cmd(value, CMD_MAP_11(onenand, word_addr)); -} - -static int s3c_onenand_wait(struct mtd_info *mtd, int state) -{ - struct device *dev = &onenand->pdev->dev; - unsigned int flags = INT_ACT; - unsigned int stat, ecc; - unsigned long timeout; - - switch (state) { - case FL_READING: - flags |= BLK_RW_CMP | LOAD_CMP; - break; - case FL_WRITING: - flags |= BLK_RW_CMP | PGM_CMP; - break; - case FL_ERASING: - flags |= BLK_RW_CMP | ERS_CMP; - break; - case FL_LOCKING: - flags |= BLK_RW_CMP; - break; - default: - break; - } - - /* The 20 msec is enough */ - timeout = jiffies + msecs_to_jiffies(20); - while (time_before(jiffies, timeout)) { - stat = s3c_read_reg(INT_ERR_STAT_OFFSET); - if (stat & flags) - break; - - if (state != FL_READING) - cond_resched(); - } - /* To get correct interrupt status in timeout case */ - stat = s3c_read_reg(INT_ERR_STAT_OFFSET); - s3c_write_reg(stat, INT_ERR_ACK_OFFSET); - - /* - * In the Spec. it checks the controller status first - * However if you get the correct information in case of - * power off recovery (POR) test, it should read ECC status first - */ - if (stat & LOAD_CMP) { - ecc = s3c_read_reg(ECC_ERR_STAT_OFFSET); - if (ecc & ONENAND_ECC_4BIT_UNCORRECTABLE) { - dev_info(dev, "%s: ECC error = 0x%04x\n", __func__, - ecc); - mtd->ecc_stats.failed++; - return -EBADMSG; - } - } - - if (stat & (LOCKED_BLK | ERS_FAIL | PGM_FAIL | LD_FAIL_ECC_ERR)) { - dev_info(dev, "%s: controller error = 0x%04x\n", __func__, - stat); - if (stat & LOCKED_BLK) - dev_info(dev, "%s: it's locked error = 0x%04x\n", - __func__, stat); - - return -EIO; - } - - return 0; -} - -static int s3c_onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, - size_t len) -{ - struct onenand_chip *this = mtd->priv; - unsigned int *m, *s; - int fba, fpa, fsa = 0; - unsigned int mem_addr, cmd_map_01, cmd_map_10; - int i, mcount, scount; - int index; - - fba = (int) (addr >> this->erase_shift); - fpa = (int) (addr >> this->page_shift); - fpa &= this->page_mask; - - mem_addr = onenand->mem_addr(fba, fpa, fsa); - cmd_map_01 = CMD_MAP_01(onenand, mem_addr); - cmd_map_10 = CMD_MAP_10(onenand, mem_addr); - - switch (cmd) { - case ONENAND_CMD_READ: - case ONENAND_CMD_READOOB: - case ONENAND_CMD_BUFFERRAM: - ONENAND_SET_NEXT_BUFFERRAM(this); - default: - break; - } - - index = ONENAND_CURRENT_BUFFERRAM(this); - - /* - * Emulate Two BufferRAMs and access with 4 bytes pointer - */ - m = onenand->page_buf; - s = onenand->oob_buf; - - if (index) { - m += (this->writesize >> 2); - s += (mtd->oobsize >> 2); - } - - mcount = mtd->writesize >> 2; - scount = mtd->oobsize >> 2; - - switch (cmd) { - case ONENAND_CMD_READ: - /* Main */ - for (i = 0; i < mcount; i++) - *m++ = s3c_read_cmd(cmd_map_01); - return 0; - - case ONENAND_CMD_READOOB: - s3c_write_reg(TSRF, TRANS_SPARE_OFFSET); - /* Main */ - for (i = 0; i < mcount; i++) - *m++ = s3c_read_cmd(cmd_map_01); - - /* Spare */ - for (i = 0; i < scount; i++) - *s++ = s3c_read_cmd(cmd_map_01); - - s3c_write_reg(0, TRANS_SPARE_OFFSET); - return 0; - - case ONENAND_CMD_PROG: - /* Main */ - for (i = 0; i < mcount; i++) - s3c_write_cmd(*m++, cmd_map_01); - return 0; - - case ONENAND_CMD_PROGOOB: - s3c_write_reg(TSRF, TRANS_SPARE_OFFSET); - - /* Main - dummy write */ - for (i = 0; i < mcount; i++) - s3c_write_cmd(0xffffffff, cmd_map_01); - - /* Spare */ - for (i = 0; i < scount; i++) - s3c_write_cmd(*s++, cmd_map_01); - - s3c_write_reg(0, TRANS_SPARE_OFFSET); - return 0; - - case ONENAND_CMD_UNLOCK_ALL: - s3c_write_cmd(ONENAND_UNLOCK_ALL, cmd_map_10); - return 0; - - case ONENAND_CMD_ERASE: - s3c_write_cmd(ONENAND_ERASE_START, cmd_map_10); - return 0; - - default: - break; - } - - return 0; -} - -static unsigned char *s3c_get_bufferram(struct mtd_info *mtd, int area) -{ - struct onenand_chip *this = mtd->priv; - int index = ONENAND_CURRENT_BUFFERRAM(this); - unsigned char *p; - - if (area == ONENAND_DATARAM) { - p = onenand->page_buf; - if (index == 1) - p += this->writesize; - } else { - p = onenand->oob_buf; - if (index == 1) - p += mtd->oobsize; - } - - return p; -} - -static int onenand_read_bufferram(struct mtd_info *mtd, int area, - unsigned char *buffer, int offset, - size_t count) -{ - unsigned char *p; - - p = s3c_get_bufferram(mtd, area); - memcpy(buffer, p + offset, count); - return 0; -} - -static int onenand_write_bufferram(struct mtd_info *mtd, int area, - const unsigned char *buffer, int offset, - size_t count) -{ - unsigned char *p; - - p = s3c_get_bufferram(mtd, area); - memcpy(p + offset, buffer, count); - return 0; -} - -static int (*s5pc110_dma_ops)(dma_addr_t dst, dma_addr_t src, size_t count, int direction); - -static int s5pc110_dma_poll(dma_addr_t dst, dma_addr_t src, size_t count, int direction) -{ - void __iomem *base = onenand->dma_addr; - int status; - unsigned long timeout; - - writel(src, base + S5PC110_DMA_SRC_ADDR); - writel(dst, base + S5PC110_DMA_DST_ADDR); - - if (direction == S5PC110_DMA_DIR_READ) { - writel(S5PC110_DMA_SRC_CFG_READ, base + S5PC110_DMA_SRC_CFG); - writel(S5PC110_DMA_DST_CFG_READ, base + S5PC110_DMA_DST_CFG); - } else { - writel(S5PC110_DMA_SRC_CFG_WRITE, base + S5PC110_DMA_SRC_CFG); - writel(S5PC110_DMA_DST_CFG_WRITE, base + S5PC110_DMA_DST_CFG); - } - - writel(count, base + S5PC110_DMA_TRANS_SIZE); - writel(direction, base + S5PC110_DMA_TRANS_DIR); - - writel(S5PC110_DMA_TRANS_CMD_TR, base + S5PC110_DMA_TRANS_CMD); - - /* - * There's no exact timeout values at Spec. - * In real case it takes under 1 msec. - * So 20 msecs are enough. - */ - timeout = jiffies + msecs_to_jiffies(20); - - do { - status = readl(base + S5PC110_DMA_TRANS_STATUS); - if (status & S5PC110_DMA_TRANS_STATUS_TE) { - writel(S5PC110_DMA_TRANS_CMD_TEC, - base + S5PC110_DMA_TRANS_CMD); - return -EIO; - } - } while (!(status & S5PC110_DMA_TRANS_STATUS_TD) && - time_before(jiffies, timeout)); - - writel(S5PC110_DMA_TRANS_CMD_TDC, base + S5PC110_DMA_TRANS_CMD); - - return 0; -} - -static irqreturn_t s5pc110_onenand_irq(int irq, void *data) -{ - void __iomem *base = onenand->dma_addr; - int status, cmd = 0; - - status = readl(base + S5PC110_INTC_DMA_STATUS); - - if (likely(status & S5PC110_INTC_DMA_TD)) - cmd = S5PC110_DMA_TRANS_CMD_TDC; - - if (unlikely(status & S5PC110_INTC_DMA_TE)) - cmd = S5PC110_DMA_TRANS_CMD_TEC; - - writel(cmd, base + S5PC110_DMA_TRANS_CMD); - writel(status, base + S5PC110_INTC_DMA_CLR); - - if (!onenand->complete.done) - complete(&onenand->complete); - - return IRQ_HANDLED; -} - -static int s5pc110_dma_irq(dma_addr_t dst, dma_addr_t src, size_t count, int direction) -{ - void __iomem *base = onenand->dma_addr; - int status; - - status = readl(base + S5PC110_INTC_DMA_MASK); - if (status) { - status &= ~(S5PC110_INTC_DMA_TD | S5PC110_INTC_DMA_TE); - writel(status, base + S5PC110_INTC_DMA_MASK); - } - - writel(src, base + S5PC110_DMA_SRC_ADDR); - writel(dst, base + S5PC110_DMA_DST_ADDR); - - if (direction == S5PC110_DMA_DIR_READ) { - writel(S5PC110_DMA_SRC_CFG_READ, base + S5PC110_DMA_SRC_CFG); - writel(S5PC110_DMA_DST_CFG_READ, base + S5PC110_DMA_DST_CFG); - } else { - writel(S5PC110_DMA_SRC_CFG_WRITE, base + S5PC110_DMA_SRC_CFG); - writel(S5PC110_DMA_DST_CFG_WRITE, base + S5PC110_DMA_DST_CFG); - } - - writel(count, base + S5PC110_DMA_TRANS_SIZE); - writel(direction, base + S5PC110_DMA_TRANS_DIR); - - writel(S5PC110_DMA_TRANS_CMD_TR, base + S5PC110_DMA_TRANS_CMD); - - wait_for_completion_timeout(&onenand->complete, msecs_to_jiffies(20)); - - return 0; -} - -static int s5pc110_read_bufferram(struct mtd_info *mtd, int area, - unsigned char *buffer, int offset, size_t count) -{ - struct onenand_chip *this = mtd->priv; - void __iomem *p; - void *buf = (void *) buffer; - dma_addr_t dma_src, dma_dst; - int err, ofs, page_dma = 0; - struct device *dev = &onenand->pdev->dev; - - p = this->base + area; - if (ONENAND_CURRENT_BUFFERRAM(this)) { - if (area == ONENAND_DATARAM) - p += this->writesize; - else - p += mtd->oobsize; - } - - if (offset & 3 || (size_t) buf & 3 || - !onenand->dma_addr || count != mtd->writesize) - goto normal; - - /* Handle vmalloc address */ - if (buf >= high_memory) { - struct page *page; - - if (((size_t) buf & PAGE_MASK) != - ((size_t) (buf + count - 1) & PAGE_MASK)) - goto normal; - page = vmalloc_to_page(buf); - if (!page) - goto normal; - - /* Page offset */ - ofs = ((size_t) buf & ~PAGE_MASK); - page_dma = 1; - - /* DMA routine */ - dma_src = onenand->phys_base + (p - this->base); - dma_dst = dma_map_page(dev, page, ofs, count, DMA_FROM_DEVICE); - } else { - /* DMA routine */ - dma_src = onenand->phys_base + (p - this->base); - dma_dst = dma_map_single(dev, buf, count, DMA_FROM_DEVICE); - } - if (dma_mapping_error(dev, dma_dst)) { - dev_err(dev, "Couldn't map a %d byte buffer for DMA\n", count); - goto normal; - } - err = s5pc110_dma_ops(dma_dst, dma_src, - count, S5PC110_DMA_DIR_READ); - - if (page_dma) - dma_unmap_page(dev, dma_dst, count, DMA_FROM_DEVICE); - else - dma_unmap_single(dev, dma_dst, count, DMA_FROM_DEVICE); - - if (!err) - return 0; - -normal: - if (count != mtd->writesize) { - /* Copy the bufferram to memory to prevent unaligned access */ - memcpy(this->page_buf, p, mtd->writesize); - p = this->page_buf + offset; - } - - memcpy(buffer, p, count); - - return 0; -} - -static int s5pc110_chip_probe(struct mtd_info *mtd) -{ - /* Now just return 0 */ - return 0; -} - -static int s3c_onenand_bbt_wait(struct mtd_info *mtd, int state) -{ - unsigned int flags = INT_ACT | LOAD_CMP; - unsigned int stat; - unsigned long timeout; - - /* The 20 msec is enough */ - timeout = jiffies + msecs_to_jiffies(20); - while (time_before(jiffies, timeout)) { - stat = s3c_read_reg(INT_ERR_STAT_OFFSET); - if (stat & flags) - break; - } - /* To get correct interrupt status in timeout case */ - stat = s3c_read_reg(INT_ERR_STAT_OFFSET); - s3c_write_reg(stat, INT_ERR_ACK_OFFSET); - - if (stat & LD_FAIL_ECC_ERR) { - s3c_onenand_reset(); - return ONENAND_BBT_READ_ERROR; - } - - if (stat & LOAD_CMP) { - int ecc = s3c_read_reg(ECC_ERR_STAT_OFFSET); - if (ecc & ONENAND_ECC_4BIT_UNCORRECTABLE) { - s3c_onenand_reset(); - return ONENAND_BBT_READ_ERROR; - } - } - - return 0; -} - -static void s3c_onenand_check_lock_status(struct mtd_info *mtd) -{ - struct onenand_chip *this = mtd->priv; - struct device *dev = &onenand->pdev->dev; - unsigned int block, end; - int tmp; - - end = this->chipsize >> this->erase_shift; - - for (block = 0; block < end; block++) { - unsigned int mem_addr = onenand->mem_addr(block, 0, 0); - tmp = s3c_read_cmd(CMD_MAP_01(onenand, mem_addr)); - - if (s3c_read_reg(INT_ERR_STAT_OFFSET) & LOCKED_BLK) { - dev_err(dev, "block %d is write-protected!\n", block); - s3c_write_reg(LOCKED_BLK, INT_ERR_ACK_OFFSET); - } - } -} - -static void s3c_onenand_do_lock_cmd(struct mtd_info *mtd, loff_t ofs, - size_t len, int cmd) -{ - struct onenand_chip *this = mtd->priv; - int start, end, start_mem_addr, end_mem_addr; - - start = ofs >> this->erase_shift; - start_mem_addr = onenand->mem_addr(start, 0, 0); - end = start + (len >> this->erase_shift) - 1; - end_mem_addr = onenand->mem_addr(end, 0, 0); - - if (cmd == ONENAND_CMD_LOCK) { - s3c_write_cmd(ONENAND_LOCK_START, CMD_MAP_10(onenand, - start_mem_addr)); - s3c_write_cmd(ONENAND_LOCK_END, CMD_MAP_10(onenand, - end_mem_addr)); - } else { - s3c_write_cmd(ONENAND_UNLOCK_START, CMD_MAP_10(onenand, - start_mem_addr)); - s3c_write_cmd(ONENAND_UNLOCK_END, CMD_MAP_10(onenand, - end_mem_addr)); - } - - this->wait(mtd, FL_LOCKING); -} - -static void s3c_unlock_all(struct mtd_info *mtd) -{ - struct onenand_chip *this = mtd->priv; - loff_t ofs = 0; - size_t len = this->chipsize; - - if (this->options & ONENAND_HAS_UNLOCK_ALL) { - /* Write unlock command */ - this->command(mtd, ONENAND_CMD_UNLOCK_ALL, 0, 0); - - /* No need to check return value */ - this->wait(mtd, FL_LOCKING); - - /* Workaround for all block unlock in DDP */ - if (!ONENAND_IS_DDP(this)) { - s3c_onenand_check_lock_status(mtd); - return; - } - - /* All blocks on another chip */ - ofs = this->chipsize >> 1; - len = this->chipsize >> 1; - } - - s3c_onenand_do_lock_cmd(mtd, ofs, len, ONENAND_CMD_UNLOCK); - - s3c_onenand_check_lock_status(mtd); -} - -static void s3c_onenand_setup(struct mtd_info *mtd) -{ - struct onenand_chip *this = mtd->priv; - - onenand->mtd = mtd; - - if (onenand->type == TYPE_S3C6400) { - onenand->mem_addr = s3c6400_mem_addr; - onenand->cmd_map = s3c64xx_cmd_map; - } else if (onenand->type == TYPE_S3C6410) { - onenand->mem_addr = s3c6410_mem_addr; - onenand->cmd_map = s3c64xx_cmd_map; - } else if (onenand->type == TYPE_S5PC110) { - /* Use generic onenand functions */ - this->read_bufferram = s5pc110_read_bufferram; - this->chip_probe = s5pc110_chip_probe; - return; - } else { - BUG(); - } - - this->read_word = s3c_onenand_readw; - this->write_word = s3c_onenand_writew; - - this->wait = s3c_onenand_wait; - this->bbt_wait = s3c_onenand_bbt_wait; - this->unlock_all = s3c_unlock_all; - this->command = s3c_onenand_command; - - this->read_bufferram = onenand_read_bufferram; - this->write_bufferram = onenand_write_bufferram; -} - -static int s3c_onenand_probe(struct platform_device *pdev) -{ - struct onenand_platform_data *pdata; - struct onenand_chip *this; - struct mtd_info *mtd; - struct resource *r; - int size, err; - - pdata = dev_get_platdata(&pdev->dev); - /* No need to check pdata. the platform data is optional */ - - size = sizeof(struct mtd_info) + sizeof(struct onenand_chip); - mtd = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); - if (!mtd) - return -ENOMEM; - - onenand = devm_kzalloc(&pdev->dev, sizeof(struct s3c_onenand), - GFP_KERNEL); - if (!onenand) - return -ENOMEM; - - this = (struct onenand_chip *) &mtd[1]; - mtd->priv = this; - mtd->dev.parent = &pdev->dev; - onenand->pdev = pdev; - onenand->type = platform_get_device_id(pdev)->driver_data; - - s3c_onenand_setup(mtd); - - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - onenand->base = devm_ioremap_resource(&pdev->dev, r); - if (IS_ERR(onenand->base)) - return PTR_ERR(onenand->base); - - onenand->phys_base = r->start; - - /* Set onenand_chip also */ - this->base = onenand->base; - - /* Use runtime badblock check */ - this->options |= ONENAND_SKIP_UNLOCK_CHECK; - - if (onenand->type != TYPE_S5PC110) { - r = platform_get_resource(pdev, IORESOURCE_MEM, 1); - onenand->ahb_addr = devm_ioremap_resource(&pdev->dev, r); - if (IS_ERR(onenand->ahb_addr)) - return PTR_ERR(onenand->ahb_addr); - - /* Allocate 4KiB BufferRAM */ - onenand->page_buf = devm_kzalloc(&pdev->dev, SZ_4K, - GFP_KERNEL); - if (!onenand->page_buf) - return -ENOMEM; - - /* Allocate 128 SpareRAM */ - onenand->oob_buf = devm_kzalloc(&pdev->dev, 128, GFP_KERNEL); - if (!onenand->oob_buf) - return -ENOMEM; - - /* S3C doesn't handle subpage write */ - mtd->subpage_sft = 0; - this->subpagesize = mtd->writesize; - - } else { /* S5PC110 */ - r = platform_get_resource(pdev, IORESOURCE_MEM, 1); - onenand->dma_addr = devm_ioremap_resource(&pdev->dev, r); - if (IS_ERR(onenand->dma_addr)) - return PTR_ERR(onenand->dma_addr); - - s5pc110_dma_ops = s5pc110_dma_poll; - /* Interrupt support */ - r = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (r) { - init_completion(&onenand->complete); - s5pc110_dma_ops = s5pc110_dma_irq; - err = devm_request_irq(&pdev->dev, r->start, - s5pc110_onenand_irq, - IRQF_SHARED, "onenand", - &onenand); - if (err) { - dev_err(&pdev->dev, "failed to get irq\n"); - return err; - } - } - } - - err = onenand_scan(mtd, 1); - if (err) - return err; - - if (onenand->type != TYPE_S5PC110) { - /* S3C doesn't handle subpage write */ - mtd->subpage_sft = 0; - this->subpagesize = mtd->writesize; - } - - if (s3c_read_reg(MEM_CFG_OFFSET) & ONENAND_SYS_CFG1_SYNC_READ) - dev_info(&onenand->pdev->dev, "OneNAND Sync. Burst Read enabled\n"); - - err = mtd_device_parse_register(mtd, NULL, NULL, - pdata ? pdata->parts : NULL, - pdata ? pdata->nr_parts : 0); - if (err) { - dev_err(&pdev->dev, "failed to parse partitions and register the MTD device\n"); - onenand_release(mtd); - return err; - } - - platform_set_drvdata(pdev, mtd); - - return 0; -} - -static int s3c_onenand_remove(struct platform_device *pdev) -{ - struct mtd_info *mtd = platform_get_drvdata(pdev); - - onenand_release(mtd); - - return 0; -} - -static int s3c_pm_ops_suspend(struct device *dev) -{ - struct platform_device *pdev = to_platform_device(dev); - struct mtd_info *mtd = platform_get_drvdata(pdev); - struct onenand_chip *this = mtd->priv; - - this->wait(mtd, FL_PM_SUSPENDED); - return 0; -} - -static int s3c_pm_ops_resume(struct device *dev) -{ - struct platform_device *pdev = to_platform_device(dev); - struct mtd_info *mtd = platform_get_drvdata(pdev); - struct onenand_chip *this = mtd->priv; - - this->unlock_all(mtd); - return 0; -} - -static const struct dev_pm_ops s3c_pm_ops = { - .suspend = s3c_pm_ops_suspend, - .resume = s3c_pm_ops_resume, -}; - -static const struct platform_device_id s3c_onenand_driver_ids[] = { - { - .name = "s3c6400-onenand", - .driver_data = TYPE_S3C6400, - }, { - .name = "s3c6410-onenand", - .driver_data = TYPE_S3C6410, - }, { - .name = "s5pc110-onenand", - .driver_data = TYPE_S5PC110, - }, { }, -}; -MODULE_DEVICE_TABLE(platform, s3c_onenand_driver_ids); - -static struct platform_driver s3c_onenand_driver = { - .driver = { - .name = "samsung-onenand", - .pm = &s3c_pm_ops, - }, - .id_table = s3c_onenand_driver_ids, - .probe = s3c_onenand_probe, - .remove = s3c_onenand_remove, -}; - -module_platform_driver(s3c_onenand_driver); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Kyungmin Park <kyungmin.park@samsung.com>"); -MODULE_DESCRIPTION("Samsung OneNAND controller support"); diff --git a/drivers/mtd/onenand/samsung.h b/drivers/mtd/onenand/samsung.h deleted file mode 100644 index 9016dc0..0000000 --- a/drivers/mtd/onenand/samsung.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) 2008-2010 Samsung Electronics - * Kyungmin Park <kyungmin.park@samsung.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#ifndef __SAMSUNG_ONENAND_H__ -#define __SAMSUNG_ONENAND_H__ - -/* - * OneNAND Controller - */ -#define MEM_CFG_OFFSET 0x0000 -#define BURST_LEN_OFFSET 0x0010 -#define MEM_RESET_OFFSET 0x0020 -#define INT_ERR_STAT_OFFSET 0x0030 -#define INT_ERR_MASK_OFFSET 0x0040 -#define INT_ERR_ACK_OFFSET 0x0050 -#define ECC_ERR_STAT_OFFSET 0x0060 -#define MANUFACT_ID_OFFSET 0x0070 -#define DEVICE_ID_OFFSET 0x0080 -#define DATA_BUF_SIZE_OFFSET 0x0090 -#define BOOT_BUF_SIZE_OFFSET 0x00A0 -#define BUF_AMOUNT_OFFSET 0x00B0 -#define TECH_OFFSET 0x00C0 -#define FBA_WIDTH_OFFSET 0x00D0 -#define FPA_WIDTH_OFFSET 0x00E0 -#define FSA_WIDTH_OFFSET 0x00F0 -#define TRANS_SPARE_OFFSET 0x0140 -#define DBS_DFS_WIDTH_OFFSET 0x0160 -#define INT_PIN_ENABLE_OFFSET 0x01A0 -#define ACC_CLOCK_OFFSET 0x01C0 -#define FLASH_VER_ID_OFFSET 0x01F0 -#define FLASH_AUX_CNTRL_OFFSET 0x0300 /* s3c64xx only */ - -#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 |