/* * 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 #include #include #include #include #include #include #include #include #include /*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 "); MODULE_DESCRIPTION("NAND controller driver for GSC3280 SoC"); MODULE_ALIAS("platform:gsc3280-nand");