blob: fc48551445869a78d362e784c291a51b86d31981 [file] [log] [blame]
/**
* @file
* @brief Provide Generic GPMC NAND implementation for OMAP platforms
*
* FileName: arch/arm/mach-omap/gpmc_nand.c
*
* GPMC has a NAND controller inbuilt. This provides a generic implementation
* for board files to register a nand device. drivers/nand/nand_base.c takes
* care of identifing the type of device, size etc.
*
* A typical device registration is as follows:
*
* @code
* static struct device_d my_nand_device = {
* .name = "gpmc_nand",
* .id = some identifier you need to show.. e.g. "gpmc_nand0"
* .map_base = GPMC base address
* .size = GPMC address map size.
* .platform_data = platform data - required - explained below
* };
* platform data required:
* static struct gpmc_nand_platform_data nand_plat = {
* .cs = give the chip select of the device
* .device_width = what is the width of the device 8 or 16?
* .max_timeout = delay desired for operation
* .wait_mon_pin = do you use wait monitoring? if so wait pin
* .plat_options = platform options.
* NAND_HWECC_ENABLE/DISABLE - hw ecc enable/disable
* NAND_WAITPOL_LOW/HIGH - wait pin polarity
* .oob = if you would like to replace oob with a custom OOB.
* .nand_setup = if you would like a special setup function to be called
* .priv = any params you'd like to save(e.g. like nand_setup to use)
*};
* then in your code, you'd device_register(&my_nand_device);
* @endcode
*
* Note:
* @li Enable CONFIG_NAND_OMAP_GPMC_HWECC in menuconfig to get H/w ECC support
* @li You may choose to register two "devices" for the same CS to get BOTH
* hwecc and swecc devices.
* @li You can choose to have your own OOB definition for compliance with ROM
* code organization - only if you dont want to use NAND's default oob layout.
* see GPMC_NAND_ECC_LP_x8_LAYOUT etc..
*
* @see gpmc_nand_platform_data
* @warning Remember to initialize GPMC before initializing the nand dev.
*/
/*
* (C) Copyright 2008
* Texas Instruments, <www.ti.com>
* Nishanth Menon <x0nishan@ti.com>
*
* Based on:
* drivers/mtd/nand/omap2.c from linux kernel
*
* Copyright (c) 2004 Texas Instruments, Jian Zhang <jzhang@ti.com>
* Copyright (c) 2004 Micron Technology Inc.
* Copyright (c) 2004 David Brownell
*
* 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 <common.h>
#include <errno.h>
#include <init.h>
#include <driver.h>
#include <malloc.h>
#include <clock.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/nand_ecc.h>
#include <asm/io.h>
#include <mach/silicon.h>
#include <mach/gpmc.h>
#include <mach/gpmc_nand.h>
int decode_bch(int select_4_8, unsigned char *ecc, unsigned int *err_loc);
static char *ecc_mode_strings[] = {
"software",
"hamming_hw_romcode",
"bch4_hw",
"bch8_hw",
"bch8_hw_romcode",
};
/** internal structure maintained for nand information */
struct gpmc_nand_info {
struct nand_hw_control controller;
struct device_d *pdev;
struct gpmc_nand_platform_data *pdata;
struct nand_chip nand;
struct mtd_info minfo;
int gpmc_cs;
void *gpmc_command;
void *gpmc_address;
void *gpmc_data;
unsigned long gpmc_base;
unsigned char wait_mon_mask;
uint64_t timeout;
unsigned inuse:1;
unsigned wait_pol:1;
unsigned char ecc_parity_pairs;
enum gpmc_ecc_mode ecc_mode;
};
/* Typical BOOTROM oob layouts-requires hwecc **/
static struct nand_ecclayout omap_oobinfo;
/* 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 };
static struct nand_bbt_descr bb_descrip_flashbased = {
.options = NAND_BBT_SCANEMPTY | NAND_BBT_SCANALLPAGES,
.offs = 0,
.len = 1,
.pattern = scan_ff_pattern,
};
/** Large Page x8 NAND device Layout */
static struct nand_ecclayout ecc_lp_x8 = {
.eccbytes = 12,
.eccpos = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12},
.oobfree = {
{
.offset = 60,
.length = 2,
}
}
};
/** Large Page x16 NAND device Layout */
static struct nand_ecclayout ecc_lp_x16 = {
.eccbytes = 12,
.eccpos = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13},
.oobfree = {
{
.offset = 60,
.length = 2,
}
}
};
/** Small Page x8 NAND device Layout */
static struct nand_ecclayout ecc_sp_x8 = {
.eccbytes = 3,
.eccpos = {1, 2, 3},
.oobfree = {
{
.offset = 14,
.length = 2,
}
}
};
/** Small Page x16 NAND device Layout */
static struct nand_ecclayout ecc_sp_x16 = {
.eccbytes = 3,
.eccpos = {2, 3, 4},
.oobfree = {
{
.offset = 14,
.length = 2 }
}
};
/**
* @brief calls the platform specific dev_ready functionds
*
* @param mtd - mtd info structure
*
* @return
*/
static int omap_dev_ready(struct mtd_info *mtd)
{
struct nand_chip *nand = (struct nand_chip *)(mtd->priv);
struct gpmc_nand_info *oinfo = (struct gpmc_nand_info *)(nand->priv);
uint64_t start = get_time_ns();
unsigned long comp;
debug("mtd=%x", (unsigned int)mtd);
/* What do we mean by assert and de-assert? */
comp = (oinfo->wait_pol == NAND_WAITPOL_HIGH) ?
oinfo->wait_mon_mask : 0x0;
while (1) {
/* Breakout condition */
if (is_timeout(start, oinfo->timeout)) {
debug("timedout\n");
return -ETIMEDOUT;
}
/* if the wait is released, we are good to go */
if (comp ==
(readl(oinfo->gpmc_base + GPMC_STATUS) &&
oinfo->wait_mon_mask))
break;
}
return 0;
}
/**
* @brief This function will enable or disable the Write Protect feature on
* NAND device. GPMC has a single WP bit for all CS devices..
*
* @param oinfo omap nand info
* @param mode 0-disable else enable
*
* @return none
*/
static void gpmc_nand_wp(struct gpmc_nand_info *oinfo, int mode)
{
unsigned long config = readl(oinfo->gpmc_base + GPMC_CFG);
debug("mode=%x", mode);
if (mode)
config &= ~(NAND_WP_BIT); /* WP is ON */
else
config |= (NAND_WP_BIT); /* WP is OFF */
writel(config, oinfo->gpmc_base + GPMC_CFG);
}
/**
* @brief respond to hw event change request
*
* MTD layer uses NAND_CTRL_CLE etc to control selection of the latch
* we hoodwink by changing the R and W registers according to the state
* we are requested.
*
* @param mtd - mtd info structure
* @param cmd command mtd layer is requesting
*
* @return none
*/
static void omap_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
{
struct nand_chip *nand = (struct nand_chip *)(mtd->priv);
struct gpmc_nand_info *oinfo = (struct gpmc_nand_info *)(nand->priv);
debug("mtd=%x nand=%x cmd=%x ctrl = %x", (unsigned int)mtd, nand,
cmd, ctrl);
switch (ctrl) {
case NAND_CTRL_CHANGE | NAND_CTRL_CLE:
nand->IO_ADDR_W = oinfo->gpmc_command;
nand->IO_ADDR_R = oinfo->gpmc_data;
break;
case NAND_CTRL_CHANGE | NAND_CTRL_ALE:
nand->IO_ADDR_W = oinfo->gpmc_address;
nand->IO_ADDR_R = oinfo->gpmc_data;
break;
case NAND_CTRL_CHANGE | NAND_NCE:
nand->IO_ADDR_W = oinfo->gpmc_data;
nand->IO_ADDR_R = oinfo->gpmc_data;
break;
}
if (cmd != NAND_CMD_NONE)
writeb(cmd, nand->IO_ADDR_W);
return;
}
/**
* @brief This function will generate true ECC value, which can be used
* when correcting data read from NAND flash memory core
*
* @param ecc_buf buffer to store ecc code
*
* @return re-formatted ECC value
*/
static unsigned int gen_true_ecc(u8 *ecc_buf)
{
debug("ecc_buf=%x 1, 2 3 = %x %x %x", (unsigned int)ecc_buf,
ecc_buf[0], ecc_buf[1], ecc_buf[2]);
return ecc_buf[0] | (ecc_buf[1] << 16) | ((ecc_buf[2] & 0xF0) << 20) |
((ecc_buf[2] & 0x0F) << 8);
}
static int omap_calculate_ecc(struct mtd_info *mtd, const uint8_t *dat,
uint8_t *ecc_code)
{
struct nand_chip *nand = (struct nand_chip *)(mtd->priv);
struct gpmc_nand_info *oinfo = (struct gpmc_nand_info *)(nand->priv);
unsigned int reg;
unsigned int val1 = 0x0, val2 = 0x0;
unsigned int val3 = 0x0, val4 = 0x0;
int i;
int ecc_size = 8;
switch (oinfo->ecc_mode) {
case OMAP_ECC_BCH4_CODE_HW:
ecc_size = 4;
/* fall through */
case OMAP_ECC_BCH8_CODE_HW:
case OMAP_ECC_BCH8_CODE_HW_ROMCODE:
for (i = 0; i < 4; i++) {
/*
* Reading HW ECC_BCH_Results
* 0x240-0x24C, 0x250-0x25C, 0x260-0x26C, 0x270-0x27C
*/
reg = GPMC_ECC_BCH_RESULT_0 + (0x10 * i);
val1 = readl(oinfo->gpmc_base + reg);
val2 = readl(oinfo->gpmc_base + reg + 4);
if (ecc_size == 8) {
val3 = readl(oinfo->gpmc_base +reg + 8);
val4 = readl(oinfo->gpmc_base + reg + 12);
*ecc_code++ = (val4 & 0xFF);
*ecc_code++ = ((val3 >> 24) & 0xFF);
*ecc_code++ = ((val3 >> 16) & 0xFF);
*ecc_code++ = ((val3 >> 8) & 0xFF);
*ecc_code++ = (val3 & 0xFF);
*ecc_code++ = ((val2 >> 24) & 0xFF);
}
*ecc_code++ = ((val2 >> 16) & 0xFF);
*ecc_code++ = ((val2 >> 8) & 0xFF);
*ecc_code++ = (val2 & 0xFF);
*ecc_code++ = ((val1 >> 24) & 0xFF);
*ecc_code++ = ((val1 >> 16) & 0xFF);
*ecc_code++ = ((val1 >> 8) & 0xFF);
*ecc_code++ = (val1 & 0xFF);
}
break;
case OMAP_ECC_HAMMING_CODE_HW_ROMCODE:
/* read ecc result */
val1 = readl(oinfo->gpmc_base + GPMC_ECC1_RESULT);
*ecc_code++ = val1; /* P128e, ..., P1e */
*ecc_code++ = val1 >> 16; /* P128o, ..., P1o */
/* P2048o, P1024o, P512o, P256o, P2048e, P1024e, P512e, P256e */
*ecc_code++ = ((val1 >> 8) & 0x0f) | ((val1 >> 20) & 0xf0);
break;
default:
return -EINVAL;
}
return 0;
}
/**
* @brief Compares the ecc read from nand spare area with ECC
* registers values and corrects one bit error if it has occured
* Further details can be had from OMAP TRM and the following selected links:
* http://en.wikipedia.org/wiki/Hamming_code
* http://www.cs.utexas.edu/users/plaxton/c/337/05f/slides/ErrorCorrection-4.pdf
*
* @param mtd - mtd info structure
* @param dat page data
* @param read_ecc ecc readback
* @param calc_ecc calculated ecc (from reg)
*
* @return 0 if data is OK or corrected, else returns -1
*/
static int omap_correct_data(struct mtd_info *mtd, uint8_t *dat,
uint8_t *read_ecc, uint8_t *calc_ecc)
{
unsigned int orig_ecc, new_ecc, res, hm;
unsigned short parity_bits, byte;
unsigned char bit;
struct nand_chip *nand = (struct nand_chip *)(mtd->priv);
struct gpmc_nand_info *oinfo = (struct gpmc_nand_info *)(nand->priv);
int ecc_type = OMAP_ECC_BCH8_CODE_HW;
int i, j, eccsize, eccflag, count;
unsigned int err_loc[8];
int blockCnt = 0;
int select_4_8;
debug("mtd=%x dat=%x read_ecc=%x calc_ecc=%x", (unsigned int)mtd,
(unsigned int)dat, (unsigned int)read_ecc,
(unsigned int)calc_ecc);
if ((nand->ecc.mode == NAND_ECC_HW) &&
(nand->ecc.size == 2048))
blockCnt = 4;
else
blockCnt = 1;
switch (oinfo->ecc_mode) {
case OMAP_ECC_HAMMING_CODE_HW_ROMCODE:
if (read_ecc[0] == 0xff && read_ecc[1] == 0xff &&
read_ecc[2] == 0xff && calc_ecc[0] == 0x0 &&
calc_ecc[1] == 0x0 && calc_ecc[0] == 0x0)
break;
/* Regenerate the orginal ECC */
orig_ecc = gen_true_ecc(read_ecc);
new_ecc = gen_true_ecc(calc_ecc);
/* Get the XOR of real ecc */
res = orig_ecc ^ new_ecc;
if (res) {
/* Get the hamming width */
hm = hweight32(res);
/* Single bit errors can be corrected! */
if (hm == oinfo->ecc_parity_pairs) {
/* Correctable data! */
parity_bits = res >> 16;
bit = (parity_bits & 0x7);
byte = (parity_bits >> 3) & 0x1FF;
/* Flip the bit to correct */
dat[byte] ^= (0x1 << bit);
} else if (hm == 1) {
printf("Ecc is wrong\n");
/* ECC itself is corrupted */
return 2;
} else {
printf("bad compare! failed\n");
/* detected 2 bit error */
return -1;
}
}
break;
case OMAP_ECC_BCH8_CODE_HW:
case OMAP_ECC_BCH8_CODE_HW_ROMCODE:
eccsize = 13;
select_4_8 = 1;
/* fall through */
case OMAP_ECC_BCH4_CODE_HW:
if (ecc_type == OMAP_ECC_BCH4_CODE_HW) {
eccsize = 7;
select_4_8 = 0;
}
omap_calculate_ecc(mtd, dat, calc_ecc);
for (i = 0; i < blockCnt; i++) {
/* check if any ecc error */
eccflag = 0;
for (j = 0; (j < eccsize) && (eccflag == 0); j++)
if (calc_ecc[j] != 0)
eccflag = 1;
if (eccflag == 1) {
eccflag = 0;
for (j = 0; (j < eccsize) &&
(eccflag == 0); j++)
if (read_ecc[j] != 0xFF)
eccflag = 1;
}
count = 0;
if (eccflag == 1)
count = decode_bch(select_4_8, calc_ecc, err_loc);
for (j = 0; j < count; j++) {
if (err_loc[j] < 4096)
dat[err_loc[j] >> 3] ^=
1 << (err_loc[j] & 7);
/* else, not interested to correct ecc */
}
calc_ecc = calc_ecc + eccsize;
read_ecc = read_ecc + eccsize;
dat += 512;
}
break;
default:
return -EINVAL;
}
return 0;
}
static void omap_enable_hwecc(struct mtd_info *mtd, int mode)
{
struct nand_chip *nand = (struct nand_chip *)(mtd->priv);
struct gpmc_nand_info *oinfo = (struct gpmc_nand_info *)(nand->priv);
unsigned int bch_mod = 0, bch_wrapmode = 0, eccsize1 = 0, eccsize0 = 0;
unsigned int ecc_conf_val = 0, ecc_size_conf_val = 0;
int dev_width = nand->options & NAND_BUSWIDTH_16 ? 1 : 0;
int ecc_size = nand->ecc.size;
int cs = 0;
switch (oinfo->ecc_mode) {
case OMAP_ECC_BCH4_CODE_HW:
if (mode == NAND_ECC_READ) {
eccsize1 = 0xD; eccsize0 = 0x48;
bch_mod = 0;
bch_wrapmode = 0x09;
} else {
eccsize1 = 0x20; eccsize0 = 0x00;
bch_mod = 0;
bch_wrapmode = 0x06;
}
break;
case OMAP_ECC_BCH8_CODE_HW:
case OMAP_ECC_BCH8_CODE_HW_ROMCODE:
if (mode == NAND_ECC_READ) {
eccsize1 = 0x1A; eccsize0 = 0x18;
bch_mod = 1;
bch_wrapmode = 0x04;
} else {
eccsize1 = 0x20; eccsize0 = 0x00;
bch_mod = 1;
bch_wrapmode = 0x06;
}
break;
case OMAP_ECC_HAMMING_CODE_HW_ROMCODE:
eccsize1 = ((ecc_size >> 1) - 1) << 22;
break;
case OMAP_ECC_SOFT:
return;
}
/* clear ecc and enable bits */
if (oinfo->ecc_mode == OMAP_ECC_HAMMING_CODE_HW_ROMCODE) {
writel(0x00000101, oinfo->gpmc_base + GPMC_ECC_CONTROL);
/* Size 0 = 0xFF, Size1 is 0xFF - both are 512 bytes
* tell all regs to generate size0 sized regs
* we just have a single ECC engine for all CS
*/
ecc_size_conf_val = 0x3FCFF000;
ecc_conf_val = (dev_width << 7) | (cs << 1) | (0x1);
} else {
writel(0x1, oinfo->gpmc_base + GPMC_ECC_CONTROL);
ecc_size_conf_val = (eccsize1 << 22) | (eccsize0 << 12);
ecc_conf_val = ((0x01 << 16) | (bch_mod << 12)
| (bch_wrapmode << 8) | (dev_width << 7)
| (0x03 << 4) | (cs << 1) | (0x1));
}
writel(ecc_size_conf_val, oinfo->gpmc_base + GPMC_ECC_SIZE_CONFIG);
writel(ecc_conf_val, oinfo->gpmc_base + GPMC_ECC_CONFIG);
writel(0x00000101, oinfo->gpmc_base + GPMC_ECC_CONTROL);
}
static int omap_gpmc_eccmode(struct gpmc_nand_info *oinfo,
enum gpmc_ecc_mode mode)
{
struct mtd_info *minfo = &oinfo->minfo;
struct nand_chip *nand = &oinfo->nand;
int offset;
int i, j;
if (nand->options & NAND_BUSWIDTH_16)
nand->badblock_pattern = &bb_descrip_flashbased;
else
nand->badblock_pattern = NULL;
if (oinfo->nand.options & NAND_BUSWIDTH_16)
offset = 2;
else
offset = 1;
if (mode != OMAP_ECC_SOFT) {
nand->ecc.layout = &omap_oobinfo;
nand->ecc.calculate = omap_calculate_ecc;
nand->ecc.hwctl = omap_enable_hwecc;
nand->ecc.correct = omap_correct_data;
nand->ecc.read_page = NULL;
nand->ecc.write_page = NULL;
nand->ecc.read_oob = NULL;
nand->ecc.write_oob = NULL;
nand->ecc.mode = NAND_ECC_HW;
}
switch (mode) {
case OMAP_ECC_HAMMING_CODE_HW_ROMCODE:
oinfo->nand.ecc.bytes = 3;
oinfo->nand.ecc.size = 512;
oinfo->ecc_parity_pairs = 12;
if (oinfo->nand.options & NAND_BUSWIDTH_16) {
offset = 2;
} else {
offset = 1;
oinfo->nand.badblock_pattern = &bb_descrip_flashbased;
}
omap_oobinfo.eccbytes = 3 * (minfo->oobsize / 16);
for (i = 0; i < omap_oobinfo.eccbytes; i++)
omap_oobinfo.eccpos[i] = i + offset;
omap_oobinfo.oobfree->offset = offset + omap_oobinfo.eccbytes;
omap_oobinfo.oobfree->length = minfo->oobsize -
offset - omap_oobinfo.eccbytes;
break;
case OMAP_ECC_BCH4_CODE_HW:
oinfo->nand.ecc.bytes = 4 * 7;
oinfo->nand.ecc.size = 4 * 512;
omap_oobinfo.oobfree->offset = offset;
omap_oobinfo.oobfree->length = minfo->oobsize -
offset - omap_oobinfo.eccbytes;
offset = minfo->oobsize - oinfo->nand.ecc.bytes;
for (i = 0; i < oinfo->nand.ecc.bytes; i++)
omap_oobinfo.eccpos[i] = i + offset;
break;
case OMAP_ECC_BCH8_CODE_HW:
oinfo->nand.ecc.bytes = 4 * 13;
oinfo->nand.ecc.size = 4 * 512;
omap_oobinfo.oobfree->offset = offset;
omap_oobinfo.oobfree->length = minfo->oobsize -
offset - omap_oobinfo.eccbytes;
offset = minfo->oobsize - oinfo->nand.ecc.bytes;
for (i = 0; i < oinfo->nand.ecc.bytes; i++)
omap_oobinfo.eccpos[i] = i + offset;
break;
case OMAP_ECC_BCH8_CODE_HW_ROMCODE:
/*
* Contradicting the datasheet the ecc checksum has to start
* at byte 2 in oob. I have no idea how the rom code can
* read this but it does.
*/
dev_warn(oinfo->pdev, "using rom loader ecc mode. "
"You can write properly but not read it back\n");
oinfo->nand.ecc.bytes = 4 * 13;
oinfo->nand.ecc.size = 4 * 512;
omap_oobinfo.oobfree->length = 0;
j = 0;
for (i = 2; i < 15; i++)
omap_oobinfo.eccpos[j++] = i;
for (i = 16; i < 29; i++)
omap_oobinfo.eccpos[j++] = i;
for (i = 30; i < 43; i++)
omap_oobinfo.eccpos[j++] = i;
for (i = 44; i < 57; i++)
omap_oobinfo.eccpos[j++] = i;
break;
case OMAP_ECC_SOFT:
nand->ecc.layout = NULL;
nand->ecc.mode = NAND_ECC_SOFT;
break;
default:
return -EINVAL;
}
oinfo->ecc_mode = mode;
if (nand->buffers)
kfree(nand->buffers);
/* second phase scan */
if (nand_scan_tail(minfo))
return -ENXIO;
nand->options |= NAND_SKIP_BBTSCAN;
return 0;
}
static int omap_gpmc_eccmode_set(struct device_d *dev, struct param_d *param, const char *val)
{
struct gpmc_nand_info *oinfo = dev->priv;
int i;
if (!val)
return 0;
for (i = 0; i < ARRAY_SIZE(ecc_mode_strings); i++)
if (!strcmp(ecc_mode_strings[i], val))
break;
if (i == ARRAY_SIZE(ecc_mode_strings)) {
dev_err(dev, "invalid ecc mode '%s'\n", val);
printf("valid modes:\n");
for (i = 0; i < ARRAY_SIZE(ecc_mode_strings); i++)
printf("%s\n", ecc_mode_strings[i]);
return -EINVAL;
}
return omap_gpmc_eccmode(oinfo, i);
}
/**
* @brief nand device probe.
*
* @param pdev -matching device
*
* @return -failure reason or give 0
*/
static int gpmc_nand_probe(struct device_d *pdev)
{
struct gpmc_nand_info *oinfo;
struct gpmc_nand_platform_data *pdata;
struct nand_chip *nand;
struct mtd_info *minfo;
unsigned long cs_base;
int err;
struct nand_ecclayout *layout, *lsp, *llp;
pdata = (struct gpmc_nand_platform_data *)pdev->platform_data;
if (pdata == NULL) {
dev_dbg(pdev, "platform data missing\n");
return -ENODEV;
}
oinfo = xzalloc(sizeof(*oinfo));
/* fill up my data structures */
oinfo->pdev = pdev;
oinfo->pdata = pdata;
pdev->platform_data = (void *)oinfo;
pdev->priv = oinfo;
nand = &oinfo->nand;
nand->priv = (void *)oinfo;
minfo = &oinfo->minfo;
minfo->priv = (void *)nand;
if (pdata->cs >= GPMC_NUM_CS) {
dev_dbg(pdev, "Invalid CS!\n");
err = -EINVAL;
goto out_release_mem;
}
/* Setup register specific data */
oinfo->gpmc_cs = pdata->cs;
oinfo->gpmc_base = pdev->map_base;
cs_base = oinfo->gpmc_base + GPMC_CONFIG1_0 +
(pdata->cs * GPMC_CONFIG_CS_SIZE);
oinfo->gpmc_command = (void *)(cs_base + GPMC_CS_NAND_COMMAND);
oinfo->gpmc_address = (void *)(cs_base + GPMC_CS_NAND_ADDRESS);
oinfo->gpmc_data = (void *)(cs_base + GPMC_CS_NAND_DATA);
oinfo->timeout = pdata->max_timeout;
debug("GPMC Details:\n"
"GPMC BASE=%x\n"
"CMD=%x\n"
"ADDRESS=%x\n"
"DATA=%x\n"
"CS_BASE=%x\n",
oinfo->gpmc_base, oinfo->gpmc_command,
oinfo->gpmc_address, oinfo->gpmc_data, cs_base);
/* If we are 16 bit dev, our gpmc config tells us that */
if ((readl(cs_base) & 0x3000) == 0x1000) {
dev_dbg(pdev, "16 bit dev\n");
nand->options |= NAND_BUSWIDTH_16;
}
/* Same data register for in and out */
nand->IO_ADDR_W = nand->IO_ADDR_R = (void *)oinfo->gpmc_data;
/*
* If RDY/BSY line is connected to OMAP then use the omap ready
* function and the generic nand_wait function which reads the
* status register after monitoring the RDY/BSY line. Otherwise
* use a standard chip delay which is slightly more than tR
* (AC Timing) of the NAND device and read the status register
* until you get a failure or success
*/
if (pdata->wait_mon_pin > 4) {
dev_dbg(pdev, "Invalid wait monitoring pin\n");
err = -EINVAL;
goto out_release_mem;
}
if (pdata->wait_mon_pin) {
/* Set up the wait monitoring mask
* This is GPMC_STATUS reg relevant */
oinfo->wait_mon_mask = (0x1 << (pdata->wait_mon_pin - 1)) << 8;
oinfo->wait_pol = (pdata->plat_options & NAND_WAITPOL_MASK);
nand->dev_ready = omap_dev_ready;
nand->chip_delay = 0;
} else {
/* use the default nand_wait function */
nand->chip_delay = 50;
}
/* Use default cmdfunc */
/* nand cmd control */
nand->cmd_ctrl = omap_hwcontrol;
/* Dont do a bbt scan at the start */
nand->options |= NAND_SKIP_BBTSCAN;
/* State my controller */
nand->controller = &oinfo->controller;
/* All information is ready.. now lets call setup, if present */
if (pdata->nand_setup) {
err = pdata->nand_setup(pdata);
if (err) {
dev_dbg(pdev, "pdataform setup failed\n");
goto out_release_mem;
}
}
/* Remove write protection */
gpmc_nand_wp(oinfo, 0);
/* we do not know what state of device we have is, so
* Send a reset to the device
* 8 bit write will work on 16 and 8 bit devices
*/
writeb(NAND_CMD_RESET, oinfo->gpmc_command);
mdelay(1);
/* first scan to find the device and get the page size */
if (nand_scan_ident(minfo, 1)) {
err = -ENXIO;
goto out_release_mem;
}
switch (pdata->device_width) {
case 8:
lsp = &ecc_sp_x8;
llp = &ecc_lp_x8;
break;
case 16:
lsp = &ecc_sp_x16;
llp = &ecc_lp_x16;
break;
default:
err = -EINVAL;
goto out_release_mem;
}
switch (minfo->writesize) {
case 512:
layout = lsp;
break;
case 2048:
layout = llp;
break;
default:
err = -EINVAL;
goto out_release_mem;
}
nand->options |= NAND_SKIP_BBTSCAN;
omap_gpmc_eccmode(oinfo, pdata->ecc_mode);
/* We are all set to register with the system now! */
err = add_mtd_device(minfo);
if (err) {
dev_dbg(pdev, "device registration failed\n");
goto out_release_mem;
}
dev_add_param(pdev, "eccmode", omap_gpmc_eccmode_set, NULL, 0);
return 0;
out_release_mem:
if (oinfo)
free(oinfo);
dev_dbg(pdev, "Failed!!\n");
return err;
}
/** GMPC nand driver -> device registered by platforms */
static struct driver_d gpmc_nand_driver = {
.name = "gpmc_nand",
.probe = gpmc_nand_probe,
};
static int gpmc_nand_init(void)
{
return register_driver(&gpmc_nand_driver);
}
device_initcall(gpmc_nand_init);