ckfwq/linux-3.0.4/drivers/mtd/nand/gsc3280_nand.c

615 lines
16 KiB
C

/*
* GSC3280 SoC NAND Controller Driver
*
* Copyright (C) 2013 BLX IC Design Corp.,Ltd.
* Author: Fei Lu, Lufei@china-cpu.com
*
* 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.
*
*/
#include <linux/ioport.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/partitions.h>
#include <asm/cacheflush.h>
/*regs*/
#define NFC_CONF 0x1300
#define NFC_CTRL 0x1304
#define NFC_COMM 0x1308
#define NFC_ADDR0L 0x130C
#define NFC_ADDR1L 0x1310
#define NFC_DATA 0x1314
#define NFC_PB0 0x1318
#define NFC_STATE 0x131C
#define NFC_ECCSTATUS 0x1340
#define NFC_ADDR0H 0x1344
#define NFC_ADDR1H 0x1348
#define NFC_DMAADDR 0x1380
#define NFC_DMACTRL 0x1384
#define NFC_DMACNTR 0x1388
/*configuration regs ---TIMER_BITS(timer width):7*/
#define GSC3280_FLCONF_PSM 0x1<<31
#define GSC3280_FLCONF_TWB (0x13)<<23
#define GSC3280_FLCONF_TWHR (0x13)<<17
#define GSC3280_FLCONF_TWP (0x9)<<11
#define GSC3280_FLCONF_TRP (0x9)<<5
#define GSC3280_FLCONF_TWH 0x4
/*control regs*/
#define GSC3280_FLCTRL_WP (1<<23)
#define GSC3280_FLCTRL_FLCE (1<<15)
#define GSC3280_FLCTRL_BS (1<<12)
#define GSC3280_FLCTRL_PS (1<<11)
#define GSC3280_FLCTRL_SS (1<<10)
#define GSC3280_FLCTRL_EEN (1<<9)
#define GSC3280_FLCTRL_INTEN (1<<8)
#define GSC3280_FLCTRL_AERREN (1<<7)
#define GSC3280_FLCTRL_PIEN (1<<6)
#define GSC3280_FLCTRL_RBIEN (1<<5)
#define GSC3280_FLCTRL_DMAIEN (1<<4)
#define GSC3280_FLCTRL_DMATR (1<<3)
#define GSC3280_FLCTRL_TRANSIE (1<<2)
#define GSC3280_FLCTRL_RBMODE (1<<1)
#define GSC3280_FLCTRL_ADDRC (1<<0)
/*fldmactrl reg*/
#define GSC3280_FLDMACTRL_SF (1<<24)
#define GSC3280_FLDMACTRL_DIR(x) ((x)<<16)
#define GSC3280_FLDMACTRL_SIZE(x) ((x)<<14)
#define GSC3280_FLDMACTRL_BURST(x) ((x)<<11)
#define GSC3280_FLDMACTRL_BUSY (1<<2)
#define GSC3280_FLDMACTRL_ERRFLAG (1<<1)
/*
gsc3280 only supports 512 Bytes HW ECC,give out 13 bytes ecc code;
soft ecc, 256 bytes block data give out 3 bytes ecc result;
Different hardware ECC is done automatically.
*/
#define GSC3280_ECC_SIZE 256
#define GSC3280_ECC_BYTES 3
struct gsc3280_nand_platform_data {
int num_partitions;
struct mtd_partition *partitions;
struct nand_ecclayout *ecc_layout;
};
struct gsc3280_nand {
struct mtd_info mtd;
struct nand_chip chip;
void __iomem *base;
struct gsc3280_nand_platform_data *pdata;
int addrflag;
};
#if 0
static struct mtd_partition gsc3280_default_nand_partitions[ ] = {
[0] = {
.name = "Bootloader",
.offset = 0,
.size = 0x80000,/*512k*/
},
[1] = {
.name = "Parameters",
.offset = MTDPART_OFS_APPEND,
.size = 0x40000,/*256k*/
},
[2] = {
.name = "Reserved",
.offset = MTDPART_OFS_APPEND,
.size = 0x40000,/*256k*/
},
[3] = {
.name = "Linux Kernel",
.offset = MTDPART_OFS_APPEND,
.size = 0x500000,/*5M*/
},
[4] = {
.name = "jffs2",
.offset = MTDPART_OFS_APPEND,
.size = 0x500000,/*5M*/
/* .size = 0xa00000,*//*10M*/
},
[5] = {
.name = "Yaffs2",
.offset = MTDPART_OFS_APPEND,
.size = 0x10000000,/*256M*/
},
[6] = {
.name = "Others",
.offset = MTDPART_OFS_APPEND,
.size = MTDPART_SIZ_FULL,
},
};
#endif
#if 1
static struct mtd_partition gsc3280_default_nand_partitions[ ] = {
[0] = {
.name = "Bootloader",
.offset = 0,
.size = 0x80000,/*512k*/
},
[1] = {
.name = "Parameters",
.offset = MTDPART_OFS_APPEND,
.size = 0x40000,/*256k*/
},
[2] = {
.name = "Reserved",
.offset = MTDPART_OFS_APPEND,
.size = 0x40000,/*256k*/
},
[3] = {
.name = "kernel",
.offset = MTDPART_OFS_APPEND,
.size = 0x500000,/*5M*/
},
[4] = {
.name = "rootfs",
.offset = MTDPART_OFS_APPEND,
.size = 0xa00000,/*10M*/
},
[5] = {
.name = "userdata",
.offset = MTDPART_OFS_APPEND,
.size = MTDPART_SIZ_FULL,
},
};
#endif
static struct gsc3280_nand_platform_data gsc3280_platform_default_nand = {
.num_partitions = ARRAY_SIZE(gsc3280_default_nand_partitions),
.partitions = gsc3280_default_nand_partitions,
};
static inline struct gsc3280_nand *mtd_to_gsc3280_nand(struct mtd_info *mtd)
{
return container_of(mtd, struct gsc3280_nand, mtd);
}
static void gsc3280_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
{
struct gsc3280_nand *nand = mtd_to_gsc3280_nand(mtd);
struct nand_chip *chip = mtd->priv;
void *IO_ADDR_W=NULL;
if(ctrl & NAND_CA){
switch(cmd){
case NAND_CMD_READ0:
/*chip->addrflag=3;*//*dma buffer*/
case NAND_CMD_READ1:
case NAND_CMD_READID:
case NAND_CMD_READOOB:
case NAND_CMD_RNDOUT:
nand->addrflag=0;
break;
case NAND_CMD_WRITE0:
case NAND_CMD_SEQIN:
case NAND_CMD_PAGEPROG:
nand->addrflag=1;
break;
case NAND_CMD_ERASE1:
nand->addrflag=2;
break;
}
}
if (ctrl & NAND_CTRL_CHANGE) {
if (ctrl & NAND_CLE)
IO_ADDR_W = nand->base + NFC_COMM;
else if (ctrl & NAND_ALE){
switch(nand->addrflag){
case 0:
IO_ADDR_W = nand->base + NFC_ADDR0L;
break;
case 1:
case 2:
IO_ADDR_W = nand->base + NFC_ADDR1L;
break;
case 3:
IO_ADDR_W = nand->base;
break;
}
}
else
IO_ADDR_W = nand->base + NFC_DATA;
chip->IO_ADDR_W = IO_ADDR_W;
chip->IO_ADDR_R = IO_ADDR_W;
/*
if (ctrl & NAND_NCE)
writel(readl(nand->base + NFC_CTRL) | GSC3280_FLCTRL_FLCE,
nand->base + NFC_CTRL);
else
writel(readl(nand->base + NFC_CTRL) & ~GSC3280_FLCTRL_FLCE,
nand->base + NFC_CTRL);
*/
}
if (cmd != NAND_CMD_NONE && !(ctrl & NAND_CA) )
writel(cmd, chip->IO_ADDR_W);
}
static int gsc3280_dev_ready(struct mtd_info *mtd)
{
struct gsc3280_nand *nand = mtd_to_gsc3280_nand(mtd);
uint8_t state = readb(nand->base + NFC_STATE);
return (state&0x61)==0x1;
}
#ifdef CONFIG_MTD_GSC3280_NAND_DMA
int gsc3280_nand_dma_transfer(struct mtd_info *mtd, const uint8_t *buffer,
int size, int col, int isread)
{
uint32_t cntr;
struct gsc3280_nand *nand = mtd_to_gsc3280_nand(mtd);
unsigned long memaddr=virt_to_phys((void *)buffer);
uint32_t state;
if(!col)
cntr=(size>>2)<<16;
else
cntr=((mtd->writesize-col)>>2)<<16 | col;
writel(cntr,nand->base + NFC_DMACNTR);
writel(memaddr,nand->base + NFC_DMAADDR);
dma_cache_wback_inv((unsigned long)buffer,(unsigned long)(buffer+mtd->writesize));
if(isread)
writel(0x0101d000,nand->base + NFC_DMACTRL);
else
writel(0x0100d000,nand->base + NFC_DMACTRL);
do{
state=readl(nand->base + NFC_STATE);
}while(state&0x80);
return 0;
}
#endif
#ifdef CONFIG_MTD_GSC3280_NAND_HWECC
static int gsc3280_nand_check_hwecc(struct mtd_info *mtd, uint8_t *dat,
uint8_t *read_ecc, uint8_t *calc_ecc)
{
struct gsc3280_nand *nand = mtd_to_gsc3280_nand(mtd);
uint32_t eccstatus;
uint8_t eccerror;
uint8_t ecccorrect;
eccstatus=readl(nand->base + NFC_ECCSTATUS);
if(readl(nand->base + NFC_CTRL)&GSC3280_FLCTRL_PS){
eccerror=(eccstatus>>16)&&0xf;
ecccorrect=eccstatus&&0xf;
}else{
eccerror=(eccstatus>>16)&&0xff;
ecccorrect=eccstatus&&0xff;
}
if(!eccerror||!(eccerror^ecccorrect))
return 0;
else{
printk("gsc3280_nand_correct_data: not implemented\n");
return -1;
}
}
#endif
static int gsc3280_nand_read_page_raw(struct mtd_info *mtd,
struct nand_chip *chip,uint8_t *buf, int page)
{
struct gsc3280_nand *nand = mtd_to_gsc3280_nand(mtd);
#ifdef CONFIG_MTD_GSC3280_NAND_DMA
chip->dma_trans(mtd,buf,mtd->writesize,0,1);
#else
memcpy((void *)buf,nand->base,mtd->writesize);
#endif
return chip->ecc.read_oob(mtd, chip, page, 1);
}
static int gsc3280_nand_read_page_ecc(struct mtd_info *mtd, struct nand_chip *chip,
uint8_t *buf, int page)
{
#ifdef CONFIG_MTD_GSC3280_NAND_HWECC
int ret;
ret=chip->ecc.correct(mtd,NULL,NULL,NULL);
if(ret)
return -1;
else{
chip->ecc.read_page_raw(mtd, chip, buf, page);
}
#else
int i, eccsize = chip->ecc.size;
int eccbytes = chip->ecc.bytes;
int eccsteps = chip->ecc.steps;
uint8_t *p = buf;
uint8_t *ecc_calc = chip->buffers->ecccalc;
uint8_t *ecc_code = chip->buffers->ecccode;
uint32_t *eccpos = chip->ecc.layout->eccpos;
chip->ecc.read_page_raw(mtd, chip, buf, page);
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize)
chip->ecc.calculate(mtd, p, &ecc_calc[i]);
for (i = 0; i < chip->ecc.total; i++)
ecc_code[i] = chip->oob_poi[eccpos[i]];
eccsteps = chip->ecc.steps;
p = buf;
for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
int stat;
stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]);
if (stat < 0)
mtd->ecc_stats.failed++;
else
mtd->ecc_stats.corrected += stat;
}
#endif
return 0;
}
static void gsc3280_nand_write_page_ecc(struct mtd_info *mtd, struct nand_chip *chip,
const uint8_t *buf)
{
#ifndef CONFIG_MTD_GSC3280_NAND_HWECC
int i, eccsize = chip->ecc.size;
int eccbytes = chip->ecc.bytes;
int eccsteps = chip->ecc.steps;
uint8_t *ecc_calc = chip->buffers->ecccalc;
const uint8_t *p = buf;
uint32_t *eccpos = chip->ecc.layout->eccpos;
/* Software ecc calculation */
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize)
chip->ecc.calculate(mtd, p, &ecc_calc[i]);
for (i = 0; i < chip->ecc.total; i++)
chip->oob_poi[eccpos[i]] = ecc_calc[i];
#endif
chip->ecc.write_page_raw(mtd, chip, buf);
}
static void gsc3280_nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
const uint8_t *buf)
{
struct gsc3280_nand *nand = mtd_to_gsc3280_nand(mtd);
#ifdef CONFIG_MTD_GSC3280_NAND_DMA
chip->dma_trans(mtd,buf,mtd->writesize,0,0);
#else
memcpy(nand->base,buf,mtd->writesize);
#endif
memset(nand->base+mtd->writesize,0xff,mtd->oobsize);
}
static int gsc3280_nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
const uint8_t *buf, int page, int cached, int raw)
{
int status;
if(unlikely(raw))
chip->ecc.write_page_raw(mtd, chip, buf);
else
chip->ecc.write_page(mtd, chip, buf);
chip->cmdfunc(mtd, NAND_CMD_WRITE0, -1, page);
status = chip->waitfunc(mtd, chip);
/*
* See if operation failed and additional status checks are
* available
*/
if ((status & NAND_STATUS_FAIL) && (chip->errstat))
status = chip->errstat(mtd, chip, FL_WRITING, status,
page);
if (status & NAND_STATUS_FAIL)
return -1;
chip->ecc.write_oob(mtd, chip, page&chip->pagemask);
return 0;
}
#ifdef CONFIG_MTD_CMDLINE_PARTS
static const char *part_probes[] = {"cmdline", NULL};
#endif
static int board_nand_init_snd(struct mtd_info *mtd)
{
uint32_t ctrl;
struct gsc3280_nand *nand = mtd_to_gsc3280_nand(mtd);
ctrl=readl(nand->base+NFC_CTRL);
/*
#ifdef CONFIG_MTD_GSC3280_NAND_DMA
ctrl |= GSC3280_FLCTRL_DMATR;
#endif
*/
#ifdef CONFIG_MTD_GSC3280_NAND_HWECC
ctrl |= GSC3280_FLCTRL_EEN;
/*
GSC3280_FLCTRL_INTEN|GSC3280_FLCTRL_AERREN|GSC3280_FLCTRL_PIEN| \
GSC3280_FLCTRL_RBIEN|GSC3280_FLCTRL_DMAIEN|GSC3280_FLCTRL_TRANSIE;
*/
#endif
if(mtd->writesize == 0x800)
ctrl |= GSC3280_FLCTRL_PS;/*page=2k*/
if(mtd->erasesize/mtd->writesize == 128)
ctrl |= GSC3280_FLCTRL_BS;/*Block=128pages*/
if(mtd->oobsize != 0x40 && mtd->oobsize != 0x80)
ctrl |= GSC3280_FLCTRL_SS;/*Extended mode*/
ctrl |= GSC3280_FLCTRL_ADDRC;/*Default 5 ADDR CYCLES*/
writel(ctrl, nand->base+NFC_CTRL);
return 0;
}
static int board_nand_init(struct mtd_info *mtd,struct nand_chip *chip)
{
uint32_t cfg;
struct gsc3280_nand *nand = mtd_to_gsc3280_nand(mtd);
cfg = GSC3280_FLCONF_PSM;
cfg |= GSC3280_FLCONF_TWB;
cfg |= GSC3280_FLCONF_TWHR;
cfg |= GSC3280_FLCONF_TWP;
cfg |= GSC3280_FLCONF_TRP;
cfg |= GSC3280_FLCONF_TWH;
writel(cfg,nand->base+NFC_CONF);
/* initialize nand_chip data structure */
chip->IO_ADDR_R = nand->base+NFC_DATA;
chip->IO_ADDR_W = nand->base+NFC_DATA;
chip->select_chip = NULL;
/* cmd control always must be implemented */
chip->cmd_ctrl = gsc3280_cmd_ctrl;
chip->dev_ready = gsc3280_dev_ready;
/*
nand->set_pb = gsc3280_set_pb;
*/
#ifdef CONFIG_MTD_GSC3280_NAND_DMA
chip->dma_trans=gsc3280_nand_dma_transfer;
#endif
chip->write_page = gsc3280_nand_write_page;
chip->ecc.read_page = gsc3280_nand_read_page_ecc;
chip->ecc.write_page = gsc3280_nand_write_page_ecc;
chip->ecc.read_page_raw = gsc3280_nand_read_page_raw;
chip->ecc.write_page_raw = gsc3280_nand_write_page_raw;
chip->ecc.size = GSC3280_ECC_SIZE;
chip->ecc.bytes = GSC3280_ECC_BYTES;
#ifdef CONFIG_MTD_GSC3280_NAND_HWECC
chip->ecc.mode = NAND_ECC_HW;
chip->ecc.correct = gsc3280_nand_check_hwecc;
#else
chip->ecc.mode = NAND_ECC_SOFT;
#endif
chip->options = NAND_USE_FLASH_BBT;//NAND_SKIP_BBTSCAN;
return 0;
}
static int __devinit gsc3280_nand_probe(struct platform_device *pdev)
{
int ret;
struct resource *mem, *ioarea;
struct gsc3280_nand *nand;
struct nand_chip *chip;
struct mtd_info *mtd;
struct gsc3280_nand_platform_data *pdata = pdev->dev.platform_data=&gsc3280_platform_default_nand;
struct mtd_partition *partition_info=NULL;
int num_partitions = 0;
nand = kzalloc(sizeof(*nand), GFP_KERNEL);
if (!nand) {
dev_err(&pdev->dev, "Failed to allocate device structure.\n");
return -ENOMEM;
}
mem = platform_get_resource(pdev, IORESOURCE_MEM,0);
if (!mem) {
dev_err(&pdev->dev, "Failed to get platform %s memory\n", pdev->name);
ret = -ENXIO;
goto err;
}
ioarea = request_mem_region(mem->start, resource_size(mem), pdev->name);
if (!ioarea) {
dev_err(&pdev->dev, "Failed to request %s memory region\n", pdev->name);
ret = -EBUSY;
goto err;
}
nand->base = ioremap(mem->start, resource_size(mem));
if (!nand->base) {
dev_err(&pdev->dev, "Failed to ioremap %s memory region\n", pdev->name);
ret = -EBUSY;
goto err_free;
}
mtd = &nand->mtd;
chip = &nand->chip;
mtd->priv = chip;
mtd->owner = THIS_MODULE;
mtd->name = pdev->name;
board_nand_init(mtd,chip);
if (pdata)
chip->ecc.layout = pdata->ecc_layout;
nand->pdata = pdata;
platform_set_drvdata(pdev, nand);
ret = nand_scan_ident(mtd, 1, NULL);
if (ret) {
dev_err(&pdev->dev, "Failed to scan nand\n");
goto err_nand_scan;
}
board_nand_init_snd(mtd);
ret = nand_scan_tail(mtd);
if (ret) {
dev_err(&pdev->dev, "Failed to scan nand\n");
goto err_nand_scan;
}
#ifdef CONFIG_MTD_CMDLINE_PARTS
num_partitions = parse_mtd_partitions(mtd, part_probes,
&partition_info, 0);
#endif
if (num_partitions <= 0 && pdata) {
num_partitions = pdata->num_partitions;
partition_info = pdata->partitions;
}
ret = mtd_device_register(mtd, partition_info, num_partitions);
if (ret) {
dev_err(&pdev->dev, "Failed to add mtd device\n");
goto err_nand_release;
}
dev_info(&pdev->dev, "Successfully registered GSC3280 NAND driver\n");
return 0;
err_nand_release:
nand_release(&nand->mtd);
err_nand_scan:
platform_set_drvdata(pdev, NULL);
err_free:
release_mem_region(mem->start, resource_size(mem));
iounmap(nand->base);
err:
kfree(nand);
return ret;
}
static int __devexit gsc3280_nand_remove(struct platform_device *pdev)
{
struct gsc3280_nand *nand = platform_get_drvdata(pdev);
nand_release(&nand->mtd);
iounmap(nand->base);
platform_set_drvdata(pdev, NULL);
kfree(nand);
return 0;
}
static struct platform_driver gsc3280_nand_driver = {
.probe = gsc3280_nand_probe,
.remove = __devexit_p(gsc3280_nand_remove),
.driver = {
.name = "gsc3280-nand",
.owner = THIS_MODULE,
},
};
static int __init gsc3280_nand_init(void)
{
return platform_driver_register(&gsc3280_nand_driver);
}
static void __exit gsc3280_nand_exit(void)
{
platform_driver_unregister(&gsc3280_nand_driver);
}
module_init(gsc3280_nand_init);
module_exit(gsc3280_nand_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("lufei <lufei@china-cpu.com>");
MODULE_DESCRIPTION("NAND controller driver for GSC3280 SoC");
MODULE_ALIAS("platform:gsc3280-nand");