/*
* Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
* Copyright 2008 Sascha Hauer, kernel@pengutronix.de
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* 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; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/partitions.h>
#include <linux/interrupt.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/io.h>
#include <asm/mach/flash.h>
#include <mach/mxc_nand.h>
#define DRIVER_NAME "mxc_nand"
/* Addresses for NFC registers */
#define NFC_BUF_SIZE 0xE00
#define NFC_BUF_ADDR 0xE04
#define NFC_FLASH_ADDR 0xE06
#define NFC_FLASH_CMD 0xE08
#define NFC_CONFIG 0xE0A
#define NFC_ECC_STATUS_RESULT 0xE0C
#define NFC_RSLTMAIN_AREA 0xE0E
#define NFC_RSLTSPARE_AREA 0xE10
#define NFC_WRPROT 0xE12
#define NFC_UNLOCKSTART_BLKADDR 0xE14
#define NFC_UNLOCKEND_BLKADDR 0xE16
#define NFC_NF_WRPRST 0xE18
#define NFC_CONFIG1 0xE1A
#define NFC_CONFIG2 0xE1C
/* Addresses for NFC RAM BUFFER Main area 0 */
#define MAIN_AREA0 0x000
#define MAIN_AREA1 0x200
#define MAIN_AREA2 0x400
#define MAIN_AREA3 0x600
/* Addresses for NFC SPARE BUFFER Spare area 0 */
#define SPARE_AREA0 0x800
#define SPARE_AREA1 0x810
#define SPARE_AREA2 0x820
#define SPARE_AREA3 0x830
/* Set INT to 0, FCMD to 1, rest to 0 in NFC_CONFIG2 Register
* for Command operation */
#define NFC_CMD 0x1
/* Set INT to 0, FADD to 1, rest to 0 in NFC_CONFIG2 Register
* for Address operation */
#define NFC_ADDR 0x2
/* Set INT to 0, FDI to 1, rest to 0 in NFC_CONFIG2 Register
* for Input operation */
#define NFC_INPUT 0x4
/* Set INT to 0, FDO to 001, rest to 0 in NFC_CONFIG2 Register
* for Data Output operation */
#define NFC_OUTPUT 0x8
/* Set INT to 0, FD0 to 010, rest to 0 in NFC_CONFIG2 Register
* for Read ID operation */
#define NFC_ID 0x10
/* Set INT to 0, FDO to 100, rest to 0 in NFC_CONFIG2 Register
* for Read Status operation */
#define NFC_STATUS 0x20
/* Set INT to 1, rest to 0 in NFC_CONFIG2 Register for Read
* Status operation */
#define NFC_INT 0x8000
#define NFC_SP_EN (1 << 2)
#define NFC_ECC_EN (1 << 3)
#define NFC_INT_MSK (1 << 4)
#define NFC_BIG (1 << 5)
#define NFC_RST (1 << 6)
#define NFC_CE (1 << 7)
#define NFC_ONE_CYCLE (1 << 8)
struct mxc_nand_host {
struct mtd_info mtd;
struct nand_chip nand;
struct mtd_partition *parts;
struct device *dev;
void __iomem *regs;
int spare_only;
int status_request;
int pagesize_2k;
uint16_t col_addr;
struct clk *clk;
int clk_act;
int irq;
wait_queue_head_t irq_waitq;
};
/* Define delays in microsec for NAND device operations */
#define TROP_US_DELAY 2000
/* Macros to get byte and bit positions of ECC */
#define COLPOS(x) ((x) >> 3)
#define BITPOS(x) ((x) & 0xf)
/* Define single bit Error positions in Main & Spare area */
#define MAIN_SINGLEBIT_ERROR 0x4
#define SPARE_SINGLEBIT_ERROR 0x1
/* OOB placement block for use with hardware ecc generation */
static struct nand_ecclayout nand_hw_eccoob_8 = {
.eccbytes = 5,
.eccpos = {6, 7, 8, 9, 10},
.oobfree = {{0, 5}, {11, 5}, }
};
static struct nand_ecclayout nand_hw_eccoob_16 = {
.eccbytes = 5,
.eccpos = {6, 7, 8, 9, 10},
.oobfree = {{0, 6}, {12, 4}, }
};
#ifdef CONFIG_MTD_PARTITIONS
static const char *part_probes[] = { "RedBoot", "cmdlinepart", NULL };
#endif
static irqreturn_t mxc_nfc_irq(int irq, void *dev_id)
{
struct mxc_nand_host *host = dev_id;
uint16_t tmp;
tmp = readw(host->regs + NFC_CONFIG1);
tmp |= NFC_INT_MSK; /* Disable interrupt */
writew(tmp, host->regs + NFC_CONFIG1);
wake_up(&host->irq_waitq);
return IRQ_HANDLED;
}
/* This function polls the NANDFC to wait for the basic operation to
* complete by checking the INT bit of config2 register.
*/
static void wait_op_done(struct mxc_nand_host *host, int max_retries,
uint16_t param, int useirq)
{
uint32_t tmp;
if (useirq) {
if ((readw(host->regs + NFC_CONFIG2) & NFC_INT) == 0) {
tmp = readw(host->regs + NFC_CONFIG1);
tmp &= ~NFC_INT_MSK; /* Enable interrupt */
writew(tmp, host->regs + NFC_CONFIG1);
wait_event(host->irq_waitq,
readw(host->regs + NFC_CONFIG2) & NFC_INT);
tmp = readw(host->regs + NFC_CONFIG2);
tmp &= ~NFC_INT;
writew(tmp, host->regs + NFC_CONFIG2);
}
} else {
while (max_retries-- >