/*
* SuperH FLCTL nand controller
*
* Copyright © 2008 Renesas Solutions Corp.
* Copyright © 2008 Atom Create Engineering Co., Ltd.
*
* Based on fsl_elbc_nand.c, Copyright © 2006-2007 Freescale Semiconductor
*
* 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; version 2 of the License.
*
* 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 St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/partitions.h>
#include <linux/mtd/sh_flctl.h>
static struct nand_ecclayout flctl_4secc_oob_16 = {
.eccbytes = 10,
.eccpos = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
.oobfree = {
{.offset = 12,
. length = 4} },
};
static struct nand_ecclayout flctl_4secc_oob_64 = {
.eccbytes = 10,
.eccpos = {48, 49, 50, 51, 52, 53, 54, 55, 56, 57},
.oobfree = {
{.offset = 60,
. length = 4} },
};
static uint8_t scan_ff_pattern[] = { 0xff, 0xff };
static struct nand_bbt_descr flctl_4secc_smallpage = {
.options = NAND_BBT_SCAN2NDPAGE,
.offs = 11,
.len = 1,
.pattern = scan_ff_pattern,
};
static struct nand_bbt_descr flctl_4secc_largepage = {
.options = NAND_BBT_SCAN2NDPAGE,
.offs = 58,
.len = 2,
.pattern = scan_ff_pattern,
};
static void empty_fifo(struct sh_flctl *flctl)
{
writel(0x000c0000, FLINTDMACR(flctl)); /* FIFO Clear */
writel(0x00000000, FLINTDMACR(flctl)); /* Clear Error flags */
}
static void start_translation(struct sh_flctl *flctl)
{
writeb(TRSTRT, FLTRCR(flctl));
}
static void wait_completion(struct sh_flctl *flctl)
{
uint32_t timeout = LOOP_TIMEOUT_MAX;
while (timeout--) {
if (readb(FLTRCR(flctl)) & TREND) {
writeb(0x0, FLTRCR(flctl));
return;
}
udelay(1);
}
printk(KERN_ERR "wait_completion(): Timeout occured \n");
writeb(0x0, FLTRCR(flctl));
}
static void set_addr(struct mtd_info *mtd, int column, int page_addr)
{
struct sh_flctl *flctl = mtd_to_flctl(mtd);
uint32_t addr = 0;
if (column == -1) {
addr = page_addr; /* ERASE1 */
} else if (page_addr != -1) {
/* SEQIN, READ0, etc.. */
if (flctl->page_size) {
addr = column & 0x0FFF;
addr |= (page_addr & 0xff) << 16;
addr |= ((page_addr >> 8) & 0xff) << 24;
/* big than 128MB */
if (flctl->rw_ADRCNT == ADRCNT2_E) {
uint32_t addr2;
addr2 = (page_addr >> 16) & 0xff;
writel(addr2, FLADR2(flctl));
}
} else {
addr = column;
addr |= (page_addr & 0xff) << 8;
addr |= ((page_addr >> 8) & 0xff) << 16;
addr |= ((page_addr >> 16) & 0xff) << 24;
}
}
writel(addr, FLADR(flctl));
}
static void wait_rfifo_ready(struct sh_flctl *flctl)
{
uint32_t timeout = LOOP_TIMEOUT_MAX;
while (timeout--) {
uint32_t val;
/* check FIFO */
val = readl(FLDTCNTR(flctl)) >> 16;
if (val & 0xFF)
return;
udelay(1);
}
printk(KERN_ERR "wait_rfifo_ready(): Timeout occured \n");
}
static void wait_wfifo_ready(struct sh_flctl *flctl)
{
uint32_t len, timeout = LOOP_TIMEOUT_MAX;
while (timeout--) {
/* check FIFO */
len = (readl(FLDTCNTR(flctl)) >> 16) & 0xFF;
if (len >= 4)
return;
udelay(1);
}
printk(KERN_ERR "wait_wfifo_ready(): Timeout occured \n");
}
static int wait_recfifo_ready(struct sh_flctl *flctl, int sector_number)
{
uint32_t timeout = LOOP_TIMEOUT_MAX;
int checked[4];
void __iomem *ecc_reg[4];
int i;
uint32_t data, size;