diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 884f99ca628ab9..e929babee4b8d0 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -122,6 +122,12 @@ config MTD_NAND_OMAP2 Support for NAND flash on Texas Instruments OMAP2, OMAP3 and OMAP4 platforms. +config MTD_NAND_SUNXI + tristate "NAND Flash device on Allwinner A10" + depends on ARCH_SUN4I + help + Support for NAND flash on Allwinner A10 + config MTD_NAND_RICOH tristate "Ricoh xD card reader" default n diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index d4b4d8739bd8e8..cb9cc43e8222fb 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -51,5 +51,6 @@ obj-$(CONFIG_MTD_NAND_MPC5121_NFC) += mpc5121_nfc.o obj-$(CONFIG_MTD_NAND_RICOH) += r852.o obj-$(CONFIG_MTD_NAND_JZ4740) += jz4740_nand.o obj-$(CONFIG_MTD_NAND_GPMI_NAND) += gpmi-nand/ +obj-$(CONFIG_MTD_NAND_SUNXI) += sunxi-nand/ nand-objs := nand_base.o nand_bbt.o diff --git a/drivers/mtd/nand/sunxi-nand/Kbuild b/drivers/mtd/nand/sunxi-nand/Kbuild new file mode 100644 index 00000000000000..70c6bf7f13e5f6 --- /dev/null +++ b/drivers/mtd/nand/sunxi-nand/Kbuild @@ -0,0 +1,4 @@ +obj-$(CONFIG_MTD_NAND_SUNXI) += sunxi_nand.o +sunxi_nand-objs += main.o nfc.o dma.o nand_id.o nand1k.o + +ccflags-y = -D__LINUX__ diff --git a/drivers/mtd/nand/sunxi-nand/defs.h b/drivers/mtd/nand/sunxi-nand/defs.h new file mode 100644 index 00000000000000..125a795a44b265 --- /dev/null +++ b/drivers/mtd/nand/sunxi-nand/defs.h @@ -0,0 +1,39 @@ +/* + * defs.h + * + * Copyright (C) 2013 Qiang Yu + * + * 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 3 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, see . + */ + +#ifndef _SUNXI_DEFS_H +#define _SUNXI_DEFS_H + +#define PREFIX "[MTD][NAND][SUNXI]: " + +#ifdef __LINUX__ + +#include + +#define DBG_INFO(fmt, ...) printk(KERN_INFO PREFIX fmt, ##__VA_ARGS__) +#define ERR_INFO(fmt, ...) printk(KERN_ERR PREFIX fmt, ##__VA_ARGS__) + +#else /* !__LINUX__ */ + +#define DBG_INFO(fmt, ...) +#define ERR_INFO(fmt, ...) + +#endif + +#endif diff --git a/drivers/mtd/nand/sunxi-nand/dma.c b/drivers/mtd/nand/sunxi-nand/dma.c new file mode 100644 index 00000000000000..ad3b254a25d3b2 --- /dev/null +++ b/drivers/mtd/nand/sunxi-nand/dma.c @@ -0,0 +1,116 @@ +/* + * dma.c + * + * Copyright (C) 2013 Qiang Yu + * + * 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 3 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, see . + */ + +#include +#include +#include +#include + +#include "defs.h" + +static int nanddma_completed_flag = 1; +static DECLARE_WAIT_QUEUE_HEAD(DMA_wait); + +static struct sw_dma_client nand_dma_client = { + .name="NAND_DMA", +}; + +static void nanddma_buffdone(struct sw_dma_chan * ch, void *buf, int size, enum sw_dma_buffresult result) +{ + nanddma_completed_flag = 1; + wake_up(&DMA_wait); + //DBG_INFO("buffer done. nanddma_completed_flag: %d\n", nanddma_completed_flag); +} + +static int nanddma_opfn(struct sw_dma_chan * ch, enum sw_chan_op op_code) +{ + if(op_code == SW_DMAOP_START) + nanddma_completed_flag = 0; + + //DBG_INFO("buffer opfn: %d, nanddma_completed_flag: %d\n", (int)op_code, nanddma_completed_flag); + + return 0; +} + +int dma_nand_request(unsigned int dmatype) +{ + int ch; + + ch = sw_dma_request(DMACH_DNAND, &nand_dma_client, NULL); + if(ch < 0) + return ch; + + sw_dma_set_opfn(ch, nanddma_opfn); + sw_dma_set_buffdone_fn(ch, nanddma_buffdone); + + return ch; +} + +int dma_nand_release(int hDma) +{ + return sw_dma_free(hDma, &nand_dma_client); +} + + +static int dma_set(int hDMA, void *pArg) +{ + sw_dma_setflags(hDMA, SW_DMAF_AUTOSTART); + return sw_dma_config(hDMA, (struct dma_hw_conf*)pArg); +} + + +static int dma_enqueue(int hDma, unsigned int buff_addr, size_t len) +{ + static int seq=0; + __cpuc_flush_dcache_area((void *)buff_addr, len + (1 << 5) * 2 - 2); + nanddma_completed_flag = 0; + return sw_dma_enqueue(hDma, (void*)(seq++), buff_addr, len); +} + +void dma_nand_config_start(int dma, int rw, unsigned int buff_addr, size_t len) +{ + struct dma_hw_conf nand_hwconf = { + .xfer_type = DMAXFER_D_BWORD_S_BWORD, + .hf_irq = SW_DMA_IRQ_FULL, + .cmbk = 0x7f077f07, + }; + + nand_hwconf.dir = rw + 1; + + if(rw == 0) { + nand_hwconf.from = 0x01C03030, + nand_hwconf.address_type = DMAADDRT_D_LN_S_IO, + nand_hwconf.drqsrc_type = DRQ_TYPE_NAND; + } + else { + nand_hwconf.to = 0x01C03030, + nand_hwconf.address_type = DMAADDRT_D_IO_S_LN, + nand_hwconf.drqdst_type = DRQ_TYPE_NAND; + } + + dma_set(dma, (void*)&nand_hwconf); + dma_enqueue(dma, buff_addr, len); +} + +int dma_nand_wait_finish(void) +{ + wait_event(DMA_wait, nanddma_completed_flag); + return 0; +} + diff --git a/drivers/mtd/nand/sunxi-nand/dma.h b/drivers/mtd/nand/sunxi-nand/dma.h new file mode 100644 index 00000000000000..edb5bdcf35deb4 --- /dev/null +++ b/drivers/mtd/nand/sunxi-nand/dma.h @@ -0,0 +1,29 @@ +/* + * dma.h + * + * Copyright (C) 2013 Qiang Yu + * + * 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 3 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, see . + */ + +#ifndef _SUNXI_NAND_DMA_H +#define _SUNXI_NAND_DMA_H + +int dma_nand_request(unsigned int dmatype); +int dma_nand_release(int hDma); +void dma_nand_config_start(int dma, int rw, unsigned int buff_addr, size_t len); +int dma_nand_wait_finish(void); + +#endif + diff --git a/drivers/mtd/nand/sunxi-nand/main.c b/drivers/mtd/nand/sunxi-nand/main.c new file mode 100644 index 00000000000000..40b7d25bd0c14a --- /dev/null +++ b/drivers/mtd/nand/sunxi-nand/main.c @@ -0,0 +1,210 @@ +/* + * main.c + * + * Copyright (C) 2013 Qiang Yu + * + * 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 3 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, see . + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "defs.h" +#include "nfc.h" + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("yuq"); + +#define DRIVER_NAME "mtd-nand-sunxi" + +struct sunxi_nand_info { + struct mtd_info mtd; + struct nand_chip nand; +}; + +static int __devinit nand_probe(struct platform_device *pdev) +{ + int err; + struct sunxi_nand_info *info; + + if ((info = kzalloc(sizeof(*info), GFP_KERNEL)) == NULL) { + ERR_INFO("alloc nand info fail\n"); + err = -ENOMEM; + goto out; + } + + info->mtd.priv = &info->nand; + info->mtd.name = dev_name(&pdev->dev); + info->mtd.owner = THIS_MODULE; + + if ((err = nfc_first_init(&info->mtd)) < 0) { + ERR_INFO("nfc first inti fail\n"); + goto out_free_info; + } + + // first scan to find the device and get the page size + if ((err = nand_scan_ident(&info->mtd, 1, NULL)) < 0) { + ERR_INFO("nand scan ident fail\n"); + goto out_nfc_exit; + } + + // init NFC with flash chip info got from first scan + if ((err = nfc_second_init(&info->mtd)) < 0) { + ERR_INFO("nfc second init fail\n"); + goto out_nfc_exit; + } + + // second phase scan + if ((err = nand_scan_tail(&info->mtd)) < 0) { + ERR_INFO("nand scan tail fail\n"); + goto out_nfc_exit; + } + + if ((err = mtd_device_parse_register(&info->mtd, NULL, NULL, NULL, 0)) < 0) { + ERR_INFO("register mtd device fail\n"); + goto out_release_nand; + } + + platform_set_drvdata(pdev, info); + return 0; + +out_release_nand: + nand_release(&info->mtd); +out_nfc_exit: + nfc_exit(&info->mtd); +out_free_info: + kfree(info); +out: + return err; +} + +static int __devexit nand_remove(struct platform_device *pdev) +{ + struct sunxi_nand_info *info = platform_get_drvdata(pdev); + + platform_set_drvdata(pdev, NULL); + mtd_device_unregister(&info->mtd); + nand_release(&info->mtd); + nfc_exit(&info->mtd); + kfree(info); + return 0; +} + +static void nand_shutdown(struct platform_device *pdev) +{ + +} + +static int nand_suspend(struct platform_device *pdev, pm_message_t state) +{ + return 0; +} + +static int nand_resume(struct platform_device *pdev) +{ + return 0; +} + +static void nfc_dev_release(struct device *dev) +{ + +} + +static struct platform_driver plat_driver = { + .probe = nand_probe, + .remove = nand_remove, + .shutdown = nand_shutdown, + .suspend = nand_suspend, + .resume = nand_resume, + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + }, +}; + +static struct platform_device plat_device = { + .name = DRIVER_NAME, + .id = 0, + .dev = { + .release = nfc_dev_release, + }, +}; + +int nand1k_init(void); +void nand1k_exit(void); + +static int __init nand_init(void) +{ + int err; + int nand_used = 0; + + if (script_parser_fetch("nand_para", "nand_used", &nand_used, sizeof(int))) + ERR_INFO("nand init fetch emac using configuration failed\n"); + + if(nand_used == 0) { + DBG_INFO("nand driver is disabled \n"); + return 0; + } + + DBG_INFO("nand driver, init.\n"); + + if ((err = platform_driver_register(&plat_driver)) != 0) { + ERR_INFO("platform_driver_register fail \n"); + return err; + } + DBG_INFO("nand driver, ok.\n"); + + // add an NFC, may be should be done by platform driver + if ((err = platform_device_register(&plat_device)) < 0) { + ERR_INFO("platform_device_register fail\n"); + return err; + } + DBG_INFO("nand device, ok.\n"); + + if (nand1k_init()) { + ERR_INFO("nand1k module init fail\n"); + } + + return 0; +} + +static void __exit nand_exit(void) +{ + int nand_used = 0; + + if (script_parser_fetch("nand_para", "nand_used", &nand_used, sizeof(int))) + ERR_INFO("nand init fetch emac using configuration failed\n"); + + if(nand_used == 0) { + DBG_INFO("nand driver is disabled \n"); + return; + } + + nand1k_exit(); + platform_device_unregister(&plat_device); + platform_driver_unregister(&plat_driver); + DBG_INFO("nand driver : bye bye\n"); +} + +module_init(nand_init); +module_exit(nand_exit); + + + + diff --git a/drivers/mtd/nand/sunxi-nand/nand1k.c b/drivers/mtd/nand/sunxi-nand/nand1k.c new file mode 100644 index 00000000000000..50f46a3db2a046 --- /dev/null +++ b/drivers/mtd/nand/sunxi-nand/nand1k.c @@ -0,0 +1,157 @@ +/* + * nand1k.c + * + * Copyright (C) 2013 Qiang Yu + * + * 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 3 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, see . + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "nfc.h" + + +////////////////////////////////////////////////////////////////////////////// +// Linux charactor device interface +////////////////////////////////////////////////////////////////////////////// + +#define DEV_CLASS_NAME "nand1k" +#define CHAR_DEV_NAME "nand1k" + +static struct class *dev_class; +static int nand1k_major; +static char *read_buff; + +static int nand1k_open(struct inode *inode, struct file *file) +{ + return 0; +} + +static int nand1k_close(struct inode *inode, struct file *file) +{ + return 0; +} + +static int nand1k_read(struct file *filp, char __user *buff, size_t count, loff_t *f_pos) +{ + loff_t offs = *f_pos; + uint32_t len, ret, page, offset; + size_t size = 0; + printk(KERN_INFO "nand1k read off=%llx count=%x\n", offs, count); + while (size < count) { + page = offs / 1024; + offset = offs % 1024; + len = 1024 - offset; + if (len > count - size) + len = count - size; + nfc_read_page1k(page, read_buff); + ret = copy_to_user(buff, read_buff + offset, len); + printk(KERN_INFO "nand1k read page=%x offset=%x len=%x\n", page, offset, len); + printk(KERN_INFO "nand1k %x %x %x %x %x %x %x %x\n", + read_buff[0], read_buff[1], read_buff[2], read_buff[3], + read_buff[4], read_buff[5], read_buff[6], read_buff[7]); + size += len - ret; + offs += len - ret; + buff += len - ret; + if (ret) + break; + } + *f_pos += size; + return size; +} + +static int nand1k_write(struct file *filp, const char __user *buff, size_t count, loff_t *f_pos) +{ + return 0; +} + +static long nand1k_ioctl(struct file *f, unsigned int cmd, unsigned long arg) +{ + switch (cmd) { + default: + return -ENOTTY; + } + + return 0; +} + +struct file_operations nand1k_fops = { + .read = nand1k_read, + .write = nand1k_write, + .open = nand1k_open, + .release = nand1k_close, + .unlocked_ioctl = nand1k_ioctl, +}; + +int nand1k_init(void) +{ + int err; + struct device *dev; + + read_buff = kmalloc(1024, GFP_KERNEL); + if (!read_buff) { + printk(KERN_ERR "allocate read buffer fail\n"); + err = -ENOMEM; + goto error0; + } + + dev_class = class_create(THIS_MODULE, DEV_CLASS_NAME); + if (IS_ERR(dev_class)) { + printk(KERN_ERR "Create device class error\n"); + err = PTR_ERR(dev_class); + goto error1; + } + + nand1k_major = register_chrdev(0, CHAR_DEV_NAME, &nand1k_fops); + if (nand1k_major < 0) { + printk(KERN_ERR "register_chrdev fail\n"); + err = nand1k_major; + goto error2; + } + + // Send uevents to udev, so it'll create /dev nodes + dev = device_create(dev_class, NULL, MKDEV(nand1k_major, 0), NULL, CHAR_DEV_NAME); + if (IS_ERR(dev)) { + printk(KERN_ERR "device_create fail\n"); + err = PTR_ERR(dev); + goto error3; + } + + return 0; + +error3: + unregister_chrdev(nand1k_major, CHAR_DEV_NAME); +error2: + class_destroy(dev_class); +error1: + kfree(read_buff); +error0: + return err; +} + +void nand1k_exit(void) +{ + device_destroy(dev_class, MKDEV(nand1k_major, 0)); + unregister_chrdev(nand1k_major, CHAR_DEV_NAME); + class_destroy(dev_class); + kfree(read_buff); +} + + diff --git a/drivers/mtd/nand/sunxi-nand/nand_id.c b/drivers/mtd/nand/sunxi-nand/nand_id.c new file mode 100644 index 00000000000000..d6aaf81c2bf1fa --- /dev/null +++ b/drivers/mtd/nand/sunxi-nand/nand_id.c @@ -0,0 +1,277 @@ +/* + * nand_id.c + * + * Copyright (C) 2013 Qiang Yu + * + * 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 3 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, see . + */ + +#include "nand_id.h" + +//============================================================================== +//============================ SAMSUNG NAND FLASH ============================== +//============================================================================== +static struct nand_chip_param samsung_chip_param[] = { + // id id_len clock_freq ecc_mode + //--------------------------------------------------------------------------------------- + { {0xec, 0xf1, 0xff, 0x15, 0xff, 0xff, 0xff, 0xff }, 4, 15, 0 }, // K9F1G08 + { {0xec, 0xf1, 0x00, 0x95, 0xff, 0xff, 0xff, 0xff }, 4, 15, 0 }, // K9F1G08 + { {0xec, 0xda, 0xff, 0x15, 0xff, 0xff, 0xff, 0xff }, 4, 15, 0 }, // K9K2G08 + { {0xec, 0xda, 0x10, 0x95, 0xff, 0xff, 0xff, 0xff }, 4, 15, 0 }, // K9F2G08 + { {0xec, 0xdc, 0xc1, 0x15, 0xff, 0xff, 0xff, 0xff }, 4, 15, 0 }, // K9K4G08 + { {0xec, 0xdc, 0x10, 0x95, 0xff, 0xff, 0xff, 0xff }, 4, 15, 0 }, // K9F4G08 + { {0xec, 0xd3, 0x51, 0x95, 0xff, 0xff, 0xff, 0xff }, 4, 30, 0 }, // K9K8G08 + //--------------------------------------------------------------------------------------- + { {0xec, 0xd3, 0x50, 0xa6, 0xff, 0xff, 0xff, 0xff }, 4, 30, 0 }, // K9F8G08 + { {0xec, 0xd5, 0x51, 0xa6, 0xff, 0xff, 0xff, 0xff }, 4, 30, 0 }, // K9KAG08 + //--------------------------------------------------------------------------------------- + { {0xec, 0xdc, 0x14, 0x25, 0xff, 0xff, 0xff, 0xff }, 4, 20, 0 }, // K9G4G08 + { {0xec, 0xdc, 0x14, 0xa5, 0xff, 0xff, 0xff, 0xff }, 4, 30, 0 }, // K9G4G08 + { {0xec, 0xd3, 0x55, 0x25, 0xff, 0xff, 0xff, 0xff }, 4, 20, 0 }, // K9L8G08 + { {0xec, 0xd3, 0x55, 0xa5, 0xff, 0xff, 0xff, 0xff }, 4, 30, 0 }, // K9L8G08 + { {0xec, 0xd3, 0x14, 0x25, 0xff, 0xff, 0xff, 0xff }, 4, 20, 0 }, // K9G8G08 + { {0xec, 0xd3, 0x14, 0xa5, 0xff, 0xff, 0xff, 0xff }, 4, 30, 0 }, // K9G8G08 + { {0xec, 0xd5, 0x55, 0x25, 0xff, 0xff, 0xff, 0xff }, 4, 30, 0 }, // K9LAG08 + { {0xec, 0xd5, 0x55, 0xa5, 0xff, 0xff, 0xff, 0xff }, 4, 30, 0 }, // K9LAG08 + //--------------------------------------------------------------------------------------- + { {0xec, 0xd5, 0x14, 0xb6, 0xff, 0xff, 0xff, 0xff }, 4, 30, 0 }, // K9GAG08 + { {0xec, 0xd7, 0x55, 0xb6, 0xff, 0xff, 0xff, 0xff }, 4, 30, 0 }, // K9LBG08 + { {0xec, 0xd7, 0xd5, 0x29, 0xff, 0xff, 0xff, 0xff }, 4, 30, 0 }, // K9LBG08 + { {0xec, 0xd7, 0x94, 0x72, 0xff, 0xff, 0xff, 0xff }, 4, 30, 2 }, // K9GBG08 + { {0xec, 0xd5, 0x98, 0x71, 0xff, 0xff, 0xff, 0xff }, 4, 30, 3 }, // K9AAG08 + + { {0xec, 0xd5, 0x94, 0x29, 0xff, 0xff, 0xff, 0xff }, 4, 30, 0 }, // K9GAG08U0D + { {0xec, 0xd5, 0x84, 0x72, 0xff, 0xff, 0xff, 0xff }, 4, 24, 2 }, // K9GAG08U0E + { {0xec, 0xd5, 0x94, 0x76, 0x54, 0xff, 0xff, 0xff }, 5, 30, 2 }, // K9GAG08U0E + { {0xec, 0xd3, 0x84, 0x72, 0xff, 0xff, 0xff, 0xff }, 4, 24, 2 }, // K9G8G08U0C + { {0xec, 0xd7, 0x94, 0x76, 0xff, 0xff, 0xff, 0xff }, 4, 30, 3 }, // K9GBG08U0A + { {0xec, 0xd7, 0x94, 0x7A, 0xff, 0xff, 0xff, 0xff }, 4, 30, 3 }, // K9GBG08U0A + { {0xec, 0xde, 0xd5, 0x7A, 0x58, 0xff, 0xff, 0xff }, 5, 30, 3 }, // K9LCG08U0A + + { {0xec, 0xd7, 0x94, 0x7A, 0x54, 0xc3, 0xff, 0xff }, 6, 60, 1 }, // toogle nand 1.0 + { {0xec, 0xde, 0xa4, 0x7a, 0x68, 0xc4, 0xff, 0xff }, 6, 60, 4 }, // toogle nand 2.0 K9GCGD8U0A + { {0xec, 0xd7, 0x94, 0x7E, 0x64, 0xc4, 0xff, 0xff }, 6, 60, 4 }, // toogle nand 2.0 K9GBGD8U0B + { {0xec, 0xd7, 0x94, 0x7e, 0x64, 0x44, 0xff, 0xff }, 6, 40, 4 }, // 21nm sdr K9GBG08U0B + + //--------------------------------------------------------------------------------------- + { {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, 0, 0, 0 }, +}; + +//============================================================================== +//============================= HYNIX NAND FLASH =============================== +//============================================================================== +static struct nand_chip_param hynix_chip_param[] = { + // id id_len clock_freq ecc_mode + //------------------------------------------------------------------------------------------------------- + { {0xad, 0xf1, 0x80, 0x15, 0xff, 0xff, 0xff, 0xff }, 4, 15, 0 }, // HY27UF081G2M + { {0xad, 0xf1, 0x80, 0x1d, 0xff, 0xff, 0xff, 0xff }, 4, 20, 0 }, // HY27UF081G2A + { {0xad, 0xf1, 0x00, 0x1d, 0xff, 0xff, 0xff, 0xff }, 4, 20, 0 }, // H27U1G8F2B + { {0xad, 0xda, 0x80, 0x15, 0xff, 0xff, 0xff, 0xff }, 4, 15, 0 }, // HY27UF082G2M + { {0xad, 0xda, 0x80, 0x1d, 0xff, 0xff, 0xff, 0xff }, 4, 20, 0 }, // HY27UF082G2A + { {0xad, 0xda, 0x10, 0x95, 0xff, 0xff, 0xff, 0xff }, 4, 20, 0 }, // HY27UF082G2B + { {0xad, 0xdc, 0x80, 0x15, 0xff, 0xff, 0xff, 0xff }, 4, 15, 0 }, // HY27UH084G2M + { {0xad, 0xdc, 0x80, 0x95, 0xff, 0xff, 0xff, 0xff }, 4, 20, 0 }, // HY27UF084G2M, HY27UG088G5M + { {0xad, 0xdc, 0x10, 0x95, 0xff, 0xff, 0xff, 0xff }, 4, 20, 0 }, // HY27UF084G2B, HY27UG088G5B + { {0xad, 0xd3, 0x80, 0x15, 0xff, 0xff, 0xff, 0xff }, 4, 20, 0 }, // HY27UG084G2M, HY27H088G2M + { {0xad, 0xd3, 0xc1, 0x95, 0xff, 0xff, 0xff, 0xff }, 4, 20, 0 }, // HY27UG088G2M, HY27UH08AG5M + //-------------------------------------------------------------------------------------------------------- + { {0xad, 0xdc, 0x84, 0x25, 0xff, 0xff, 0xff, 0xff }, 4, 12, 0 }, // HY27UT084G2M, HY27UU088G5M + { {0xad, 0xdc, 0x14, 0xa5, 0xff, 0xff, 0xff, 0xff }, 4, 15, 0 }, // HY27U4G8T2BTR + { {0xad, 0xd3, 0x85, 0x25, 0xff, 0xff, 0xff, 0xff }, 4, 10, 0 }, // HY27UV08AG5M, HY27UW08BGFM + { {0xad, 0xd3, 0x14, 0x25, 0xff, 0xff, 0xff, 0xff }, 4, 12, 0 }, // HY27UT088G2M, HY27UU08AG5M + { {0xad, 0xd3, 0x14, 0x2d, 0xff, 0xff, 0xff, 0xff }, 4, 25, 0 }, // HY27UT088G2M, HY27UU08AG5M + { {0xad, 0xd3, 0x14, 0xa5, 0xff, 0xff, 0xff, 0xff }, 4, 15, 0 }, // HY27UT088G2M, HY27UU08AG5M + { {0xad, 0xd5, 0x55, 0x25, 0xff, 0xff, 0xff, 0xff }, 4, 15, 0 }, // HY27UV08BG5M, HY27UW08CGFM + { {0xad, 0xd5, 0x55, 0x2d, 0xff, 0xff, 0xff, 0xff }, 4, 25, 0 }, // HY27UV08BG5M, HY27UW08CGFM + { {0xad, 0xd5, 0x55, 0xa5, 0xff, 0xff, 0xff, 0xff }, 4, 30, 0 }, // HY27UV08BG5M, HY27UW08CGFM + //-------------------------------------------------------------------------------------------------------- + { {0xad, 0xd3, 0x14, 0xb6, 0xff, 0xff, 0xff, 0xff }, 4, 30, 0 }, // H27U8G8T2B + { {0xad, 0xd5, 0x14, 0xb6, 0xff, 0xff, 0xff, 0xff }, 4, 30, 0 }, // H27UAG8T2M, H27UBG8U5M + { {0xad, 0xd7, 0x55, 0xb6, 0xff, 0xff, 0xff, 0xff }, 4, 30, 0 }, // H27UCG8V5M + //-------------------------------------------------------------------------------------------------------- + { {0xad, 0xd5, 0x94, 0x25, 0xff, 0xff, 0xff, 0xff }, 4, 30, 2 }, // H27UBG8U5A + { {0xad, 0xd7, 0x95, 0x25, 0xff, 0xff, 0xff, 0xff }, 4, 30, 2 }, // H27UCG8V5A + { {0xad, 0xd5, 0x95, 0x25, 0xff, 0xff, 0xff, 0xff }, 4, 30, 2 }, // H27UCG8VFA + { {0xad, 0xd5, 0x94, 0x9A, 0xff, 0xff, 0xff, 0xff }, 4, 30, 2 }, // H27UAG8T2B + { {0xad, 0xd7, 0x94, 0x9A, 0xff, 0xff, 0xff, 0xff }, 4, 30, 2 }, // H27UBG8T2A H27UCG8U5(D)A H27UDG8VF(D)A + { {0xad, 0xde, 0xd5, 0x9A, 0xff, 0xff, 0xff, 0xff }, 4, 30, 2 }, // H27UDG8V5A + { {0xad, 0xd7, 0x94, 0x25, 0xff, 0xff, 0xff, 0xff }, 4, 30, 2 }, // H27UBG8T2M + { {0xad, 0xde, 0x94, 0xd2, 0xff, 0xff, 0xff, 0xff }, 4, 30, 2 }, // H27UCG8T2M + { {0xad, 0xd7, 0x18, 0x8d, 0xff, 0xff, 0xff, 0xff }, 4, 30, 3 }, // H27UBG8M2A + { {0xad, 0xd7, 0x94, 0xda, 0xff, 0xff, 0xff, 0xff }, 4, 30, 3 }, // H27UBG8M2A + { {0xad, 0xde, 0x94, 0xda, 0x74, 0xff, 0xff, 0xff }, 5, 40, 4 }, // H27UCG8T2A + { {0xad, 0xd7, 0x94, 0x91, 0x60, 0xff, 0xff, 0xff }, 5, 40, 4 }, // H27UBG8T2C + //-------------------------------------------------------------------------------------------------------- + { {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, 0, 0, 0 }, // NULL +}; + + +//============================================================================== +//============================= TOSHIBA NAND FLASH ============================= +//============================================================================== +static struct nand_chip_param toshiba_chip_param[] = { + // id id_len clock_freq ecc_mode + //------------------------------------------------------------------------------------------------------ + { {0x98, 0xf1, 0x80, 0x95, 0xff, 0xff, 0xff, 0xff }, 4, 20, 0 }, // TC58NVG0S3B + { {0x98, 0xda, 0xff, 0x95, 0xff, 0xff, 0xff, 0xff }, 4, 20, 0 }, // TC58NVG1S3B + { {0x98, 0xdc, 0x81, 0x95, 0xff, 0xff, 0xff, 0xff }, 4, 20, 0 }, // TC58NVG2S3B + { {0x98, 0xd1, 0x90, 0x15, 0xff, 0xff, 0xff, 0xff }, 4, 20, 0 }, // TC58NVG0S3E + //------------------------------------------------------------------------------------------------------ + { {0x98, 0xda, 0x84, 0xa5, 0xff, 0xff, 0xff, 0xff }, 4, 20, 0 }, // TC58NVG1D4B + { {0x98, 0xdc, 0x84, 0xa5, 0xff, 0xff, 0xff, 0xff }, 4, 20, 0 }, // TC58NVG2D4B + { {0x98, 0xd3, 0x84, 0xa5, 0xff, 0xff, 0xff, 0xff }, 4, 20, 0 }, // TC58NVG3D4C + { {0x98, 0xd5, 0x85, 0xa5, 0xff, 0xff, 0xff, 0xff }, 4, 20, 0 }, // TC58NVG4D4C, TC58NVG5D4C + //------------------------------------------------------------------------------------------------------ + { {0x98, 0xd3, 0x94, 0xba, 0xff, 0xff, 0xff, 0xff }, 4, 20, 0 }, // TC58NVG3D1DTG00 + { {0x98, 0xd7, 0x95, 0xba, 0xff, 0xff, 0xff, 0xff }, 4, 30, 2 }, // TC58NVG6D1DTG20 + { {0x98, 0xd5, 0x94, 0xba, 0xff, 0xff, 0xff, 0xff }, 4, 30, 2 }, // TH58NVG5D1DTG20 + { {0x98, 0xd5, 0x94, 0x32, 0xff, 0xff, 0xff, 0xff }, 4, 25, 1 }, // TH58NVG4D2ETA20 TH58NVG4D2FTA20 TH58NVG5D2ETA00 + { {0x98, 0xd7, 0x94, 0x32, 0xff, 0xff, 0xff, 0xff }, 4, 25, 2 }, // TH58NVG5D2FTA00 TH58NVG6D2FTA20 + { {0x98, 0xd7, 0x95, 0x32, 0xff, 0xff, 0xff, 0xff }, 4, 25, 1 }, // TH58NVG6D2ETA20 + //------------------------------------------------------------------------------------------------------ + { {0x98, 0xde, 0x94, 0x82, 0x76, 0xff, 0xff, 0xff }, 5, 40, 4 }, // TH58NVG6D2ETA20 + { {0x98, 0xd7, 0x94, 0x32, 0x76, 0x56, 0xff, 0xff }, 6, 40, 4 }, // TH58NVG5D2HTA20 + { {0x98, 0xd5, 0x84, 0x32, 0x72, 0x56, 0xff, 0xff }, 6, 40, 4 }, // TH58NVG4D2HTA20 + //------------------------------------------------------------------------------------------------------ + { {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, 0, 0, 0 }, // NULL +}; + + +//============================================================================== +//============================= MICON NAND FLASH =============================== +//============================================================================== +static struct nand_chip_param micron_chip_param[] = { + // id id_len clock_freq ecc_mode + //------------------------------------------------------------------------------------------------------------------------- + { {0x2c, 0xda, 0xff, 0x15, 0xff, 0xff, 0xff, 0xff }, 4, 25, 0 }, // MT29F2G08AAC, JS29F02G08AAN + { {0x2c, 0xdc, 0xff, 0x15, 0xff, 0xff, 0xff, 0xff }, 4, 25, 0 }, // MT29F4G08BAB, MT29F8G08FAB, JS29F04G08BAN, JS29F08G08FAN + { {0x2c, 0xdc, 0x90, 0x95, 0xff, 0xff, 0xff, 0xff }, 4, 25, 0 }, // MT29F4G08AAA, MT29F8G08DAA, JS29F04G08AAN + { {0x2c, 0xd3, 0xd1, 0x95, 0xff, 0xff, 0xff, 0xff }, 4, 25, 0 }, // MT29F8G08BAB, MT29F16G08FAB, JS29F08G08BAN, JS29F16G08FAN + //------------------------------------------------------------------------------------------------------------------------- + { {0x2c, 0xdc, 0x84, 0x25, 0xff, 0xff, 0xff, 0xff }, 4, 20, 0 }, // MT29F4G08MAA, MT29F8G08QAA + { {0x2c, 0xd3, 0x85, 0x25, 0xff, 0xff, 0xff, 0xff }, 4, 20, 0 }, // MT29F16GTAA + { {0x2c, 0xd3, 0x94, 0xa5, 0xff, 0xff, 0xff, 0xff }, 4, 30, 0 }, // MT29F8G08MAA, MT29F16G08QAA, JS29F08G08AAM, JS29F16G08CAM + { {0x2c, 0xd5, 0x95, 0xa5, 0xff, 0xff, 0xff, 0xff }, 4, 20, 0 }, // MT29F32G08TAA, JS29F32G08FAM + { {0x2c, 0xd5, 0xd5, 0xa5, 0xff, 0xff, 0xff, 0xff }, 4, 20, 0 }, // MT29F32G08TAA, JS29F32G08FAM + //------------------------------------------------------------------------------------------------------------------------- + { {0x2c, 0xd5, 0x94, 0x3e, 0xff, 0xff, 0xff, 0xff }, 4, 30, 0 }, // MT29F16G08MAA, MT29F32G08QAA, JS29F32G08AAM, JS29F32G08CAM + { {0x2c, 0xd5, 0xd5, 0x3e, 0xff, 0xff, 0xff, 0xff }, 4, 30, 0 }, // MT29F64G08TAA, JS29F64G08FAM + //------------------------------------------------------------------------------------------------------------------------- + { {0x2c, 0xd7, 0x94, 0x3e, 0xff, 0xff, 0xff, 0xff }, 4, 30, 2 }, // MT29F32G08CBAAA,MT29F64G08CFAAA + { {0x2c, 0xd7, 0xd5, 0x3e, 0xff, 0xff, 0xff, 0xff }, 4, 30, 2 }, // MT29F64G08CTAA + { {0x2c, 0xd9, 0xd5, 0x3e, 0xff, 0xff, 0xff, 0xff }, 4, 30, 2 }, // MT29F128G08, + { {0x2c, 0x68, 0x04, 0x46, 0xff, 0xff, 0xff, 0xff }, 4, 30, 2 }, // MT29F32G08CBABA + { {0x2c, 0x88, 0x05, 0xC6, 0xff, 0xff, 0xff, 0xff }, 4, 30, 2 }, // MT29F128G08CJABA + { {0x2c, 0x88, 0x04, 0x4B, 0xff, 0xff, 0xff, 0xff }, 4, 40, 2 }, // MT29F64G08CBAAA + { {0x2c, 0x68, 0x04, 0x4A, 0xff, 0xff, 0xff, 0xff }, 4, 40, 2 }, // MT29F32G08CBACA + { {0x2c, 0x48, 0x04, 0x4A, 0xff, 0xff, 0xff, 0xff }, 4, 40, 2 }, // MT29F16G08CBACA + { {0x2c, 0x48, 0x04, 0x46, 0xff, 0xff, 0xff, 0xff }, 4, 30, 2 }, // MT29F16G08CBABA + { {0x2c, 0x64, 0x44, 0x4B, 0xA9, 0xff, 0xff, 0xff }, 5, 40, 4 }, // MT29F64G08CBABA + //------------------------------------------------------------------------------------------------------------------------- + { {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, 0, 0, 0 }, // NULL +}; + + +//============================================================================== +//============================= INTEL NAND FLASH =============================== +//============================================================================== +static struct nand_chip_param intel_chip_param[] = { + // id id_len clock_freq ecc_mode + //------------------------------------------------------------------------------------------------------------- + { {0x89, 0xd3, 0x94, 0xa5, 0xff, 0xff, 0xff, 0xff }, 4, 30, 0 }, // 29F08G08AAMB2, 29F16G08CAMB2 + { {0x89, 0xd5, 0xd5, 0xa5, 0xff, 0xff, 0xff, 0xff }, 4, 20, 0 }, // 29F32G08FAMB2 + //------------------------------------------------------------------------------------------------------------- + { {0x89, 0xd7, 0x94, 0x3e, 0xff, 0xff, 0xff, 0xff }, 4, 30, 2 }, // MLC32GW8IMA,MLC64GW8IMA, 29F32G08AAMD2, 29F64G08CAMD2 + { {0x89, 0xd5, 0x94, 0x3e, 0xff, 0xff, 0xff, 0xff }, 4, 30, 2 }, // 29F32G08CAMC1 + { {0x89, 0xd7, 0xd5, 0x3e, 0xff, 0xff, 0xff, 0xff }, 4, 30, 2 }, // 29F64G08FAMC1 + { {0x89, 0x68, 0x04, 0x46, 0xff, 0xff, 0xff, 0xff }, 4, 30, 2 }, // 29F32G08AAMDB + { {0x89, 0x88, 0x24, 0x4B, 0xff, 0xff, 0xff, 0xff }, 4, 30, 2 }, // 29F64G08CBAAA 29F64G083AME1 + { {0x89, 0xA8, 0x25, 0xCB, 0xff, 0xff, 0xff, 0xff }, 4, 30, 2 }, // 29F64G08CBAAA 29F64G083AME1 + //------------------------------------------------------------------------------------------------------------- + { {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, 0, 0, 0 }, // NULL +}; + + +//============================================================================== +//=============================== ST NAND FLASH ================================ +//============================================================================== +static struct nand_chip_param st_chip_param[] = { + // id id_len clock_freq ecc_mode + //---------------------------------------------------------------------------------------- + { {0x20, 0xf1, 0x80, 0x15, 0xff, 0xff, 0xff, 0xff }, 4, 15, 0 }, // NAND01GW3B + { {0x20, 0xf1, 0x00, 0x1d, 0xff, 0xff, 0xff, 0xff }, 4, 15, 0 }, // NAND01G001 + { {0x20, 0xda, 0x80, 0x15, 0xff, 0xff, 0xff, 0xff }, 4, 15, 0 }, // NAND02GW3B + { {0x20, 0xda, 0x10, 0x95, 0xff, 0xff, 0xff, 0xff }, 4, 15, 0 }, // NAND02GW3B2DN6 + { {0x20, 0xdc, 0x80, 0x95, 0xff, 0xff, 0xff, 0xff }, 4, 15, 0 }, // NAND04GW3B + { {0x20, 0xd3, 0xc1, 0x95, 0xff, 0xff, 0xff, 0xff }, 4, 15, 0 }, // NAND08GW3B + //---------------------------------------------------------------------------------------- + { {0x20, 0xdc, 0x84, 0x25, 0xff, 0xff, 0xff, 0xff }, 4, 15, 0 }, // NAND04GW3C + { {0x20, 0xd3, 0x85, 0x25, 0xff, 0xff, 0xff, 0xff }, 4, 15, 0 }, // NAND08GW3C + { {0x20, 0xd3, 0x85, 0x25, 0xff, 0xff, 0xff, 0xff }, 4, 15, 0 }, // NAND16GW3C + //---------------------------------------------------------------------------------------- + { {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, 0, 0, 0 }, // NULL +}; + +//============================================================================== +//============================ SPANSION NAND FLASH ============================== +//============================================================================== +static struct nand_chip_param spansion_chip_param[] = { + // id id_len clock_freq ecc_mode + //----------------------------------------------------------------------------------------------- + { {0x01, 0xaa, 0x10, 0x00, 0xff, 0xff, 0xff, 0xff }, 4, 30, 0 }, // S39MS02G + { {0x01, 0xa1, 0x10, 0x00, 0xff, 0xff, 0xff, 0xff }, 4, 30, 0 }, // S39MS01G + { {0x01, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff }, 4, 30, 0 }, // DFT01GR08P1PM0 + //----------------------------------------------------------------------------------------------- + { {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, 0, 0, 0 }, // NULL +}; + +//============================================================================== +//============================ POWER NAND FLASH ============================== +//============================================================================== +static struct nand_chip_param power_chip_param[] = { + // id id_len clock_freq ecc_mode + //------------------------------------------------------------------------------------ + { {0x92, 0xf1, 0x80, 0x95, 0x40, 0xff, 0xff, 0xff }, 5, 30, 0 }, // ASU1GA + //------------------------------------------------------------------------------------ + { {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, 0, 0, 0 }, // NULL +}; + +static struct nand_chip_param default_chip_param[] = { + //------------------------------------------------------------------------------------ + { {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, 0, 0, 0 }, // NULL +}; + +struct nand_chip_param *sunxi_get_nand_chip_param(unsigned char mf) +{ + switch (mf) { + case 0xec: + return samsung_chip_param; + case 0xad: + return hynix_chip_param; + case 0x98: + return toshiba_chip_param; + case 0x2c: + return micron_chip_param; + case 0x89: + return intel_chip_param; + case 0x20: + return st_chip_param; + case 0x01: + return spansion_chip_param; + case 0x92: + return power_chip_param; + default: + return default_chip_param; + } +} diff --git a/drivers/mtd/nand/sunxi-nand/nand_id.h b/drivers/mtd/nand/sunxi-nand/nand_id.h new file mode 100644 index 00000000000000..9d4aeaaec5498b --- /dev/null +++ b/drivers/mtd/nand/sunxi-nand/nand_id.h @@ -0,0 +1,32 @@ +/* + * nand_id.h + * + * Copyright (C) 2013 Qiang Yu + * + * 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 3 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, see . + */ + +#ifndef _SUNXI_NAND_ID_H +#define _SUNXI_NAND_ID_H + +struct nand_chip_param { + unsigned char id[8]; + int id_len; + int clock_freq; //the highest access frequence of the nand flash chip, based on MHz + int ecc_mode; //the Ecc Mode for the nand flash chip, 0: bch-16, 1:bch-28, 2:bch_32 +}; + +struct nand_chip_param *sunxi_get_nand_chip_param(unsigned char mf); + +#endif diff --git a/drivers/mtd/nand/sunxi-nand/nfc.c b/drivers/mtd/nand/sunxi-nand/nfc.c new file mode 100644 index 00000000000000..e03e54fa08dcbe --- /dev/null +++ b/drivers/mtd/nand/sunxi-nand/nfc.c @@ -0,0 +1,1071 @@ +/* + * nfc.c + * + * Copyright (C) 2013 Qiang Yu + * + * 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 3 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, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "defs.h" +#include "regs.h" +#include "dma.h" +#include "nand_id.h" + +// do we need to consider exclusion of offset? +// it should be in high level that the nand_chip ops have been +// performed with exclusion already +// find it is: +// nand_get_device() & nand_release_device() +// +static int read_offset = 0; +static int write_offset = 0; +static char *read_buffer = NULL; +static char *write_buffer = NULL; +static int buffer_size = 8192 + 1024; +static dma_addr_t read_buffer_dma; +static dma_addr_t write_buffer_dma; +static int dma_hdle; +static struct nand_ecclayout sunxi_ecclayout; +static DECLARE_WAIT_QUEUE_HEAD(nand_rb_wait); +static int program_column = -1, program_page = -1; + +unsigned int hwecc_switch = 1; +module_param(hwecc_switch, uint, 0); +MODULE_PARM_DESC(hwecc_switch, "hardware ECC switch, 1=on, 0=off"); + +unsigned int use_flash_bbt = 1; +module_param(use_flash_bbt, uint, 0); +MODULE_PARM_DESC(use_flash_bbt, "use flash bad block table, 1=use, 0=not"); + +unsigned int random_seed = 0x4a80; +module_param(random_seed, uint, 0); +MODULE_PARM_DESC(random_seed, "random seed used"); + +////////////////////////////////////////////////////////////////// +// SUNXI platform +// + +// Get clock rate from PLL5 +static uint32_t sunxi_get_pll5_clk(void) +{ + uint32_t reg_val; + uint32_t div_p, factor_n; + uint32_t factor_k, factor_m; + uint32_t clock; + + reg_val = readl(PLL5_CFG_REG); + div_p = (reg_val & PLL5_OUT_EXT_DIV_P_MASK) >> PLL5_OUT_EXT_DIV_P_SHIFT; + factor_n = (reg_val & PLL5_FACTOR_N_MASK) >> PLL5_FACTOR_N_SHIFT; + factor_k = ((reg_val & PLL5_FACTOR_K_MASK) >> PLL5_FACTOR_K_SHIFT) + 1; + factor_m = ((reg_val & PLL5_FACTOR_M_MASK) >> PLL5_FACTOR_M_SHIFT) + 1; + + clock = 24 * factor_n * factor_k / div_p / factor_m; + DBG_INFO("cmu_clk is %d \n", clock); + + return clock; +} + +static void sunxi_set_nand_clock(uint32_t nand_max_clock) +{ + uint32_t edo_clk, cmu_clk; + uint32_t cfg; + uint32_t nand_clk_divid_ratio; + + // open ahb nand clk (bus clock for CPU access) + cfg = readl(AHB_GATING_REG0); + cfg |= 1 << AHB_GATING_NAND_CLK_SHIFT; + writel(cfg, AHB_GATING_REG0); + + // set nand clock (device clock for NFC running) + edo_clk = nand_max_clock * 2; + + cmu_clk = sunxi_get_pll5_clk(); + nand_clk_divid_ratio = cmu_clk / edo_clk; + if (cmu_clk % edo_clk) + nand_clk_divid_ratio++; + if (nand_clk_divid_ratio) { + if (nand_clk_divid_ratio > 16) + nand_clk_divid_ratio = 15; + else + nand_clk_divid_ratio--; + } + + // set nand clock gate on + cfg = readl(NAND_SCLK_CFG_REG); + // gate on nand clock + cfg |= 1 << SCLK_GATING_SHIFT; + // take cmu pll as nand src block + cfg &= ~CLK_SRC_SEL_MASK; + cfg |= 0x2 << CLK_SRC_SEL_SHIFT; + // set divn = 0 + cfg &= ~CLK_DIV_RATIO_N_MASK; + // set divm + cfg &= ~CLK_DIV_RATIO_M_MASK; + cfg |= (nand_clk_divid_ratio << CLK_DIV_RATIO_M_SHIFT) & CLK_DIV_RATIO_M_MASK; + writel(cfg, NAND_SCLK_CFG_REG); + + DBG_INFO("nand clk init end \n"); + DBG_INFO("offset 0xc: 0x%x \n", readl(AHB_GATING_REG0)); + DBG_INFO("offset 0x14: 0x%x \n", readl(NAND_SCLK_CFG_REG)); +} + +static void release_nand_clock(void) +{ + uint32_t cfg; + + // disable bus clock + cfg = readl(AHB_GATING_REG0); + cfg &= ~(1 << AHB_GATING_NAND_CLK_SHIFT); + writel(cfg, AHB_GATING_REG0); + + // disable device clock + cfg = readl(NAND_SCLK_CFG_REG); + cfg &= ~(1 << SCLK_GATING_SHIFT); + writel(cfg, NAND_SCLK_CFG_REG); +} + +static void active_nand_clock(void) +{ + uint32_t cfg; + + // disable bus clock + cfg = readl(AHB_GATING_REG0); + cfg |= 1 << AHB_GATING_NAND_CLK_SHIFT; + writel(cfg, AHB_GATING_REG0); + + // disable device clock + cfg = readl(NAND_SCLK_CFG_REG); + cfg |= 1 << SCLK_GATING_SHIFT; + writel(cfg, NAND_SCLK_CFG_REG); +} + +#ifdef __LINUX__ +uint32_t pioc_handle; +#endif + +// Set PIOC pin for NAND Flash use +static void sunxi_set_nand_pio(void) +{ +#ifdef __LINUX__ + pioc_handle = gpio_request_ex("nand_para", NULL); + if (pioc_handle) { + DBG_INFO("get nand pio ok\n"); + } + else { + ERR_INFO("get nand pio fail\n"); + } +#else + writel(0x22222222, PC_CFG0_REG); + writel(0x22222222, PC_CFG1_REG); + writel(0x22222222, PC_CFG2_REG); +#endif +} + +static void sunxi_release_nand_pio(void) +{ +#ifdef __LINUX__ + DBG_INFO("nand gpio_release\n"); + gpio_release(pioc_handle, 1); +#else + writel(0, PC_CFG0_REG); + writel(0, PC_CFG1_REG); + writel(0, PC_CFG2_REG); +#endif +} + +///////////////////////////////////////////////////////////////// +// Utils +// + +static inline void wait_cmdfifo_free(void) +{ + int timeout = 0xffff; + while ((timeout--) && (readl(NFC_REG_ST) & NFC_CMD_FIFO_STATUS)); + if (timeout <= 0) { + ERR_INFO("wait_cmdfifo_free timeout\n"); + } +} + +static inline void wait_cmd_finish(void) +{ + int timeout = 0xffff; + while((timeout--) && !(readl(NFC_REG_ST) & NFC_CMD_INT_FLAG)); + if (timeout <= 0) { + ERR_INFO("wait_cmd_finish timeout\n"); + return; + } + writel(NFC_CMD_INT_FLAG, NFC_REG_ST); +} + +static void select_rb(int rb) +{ + uint32_t ctl; + // A10 has 2 RB pin + ctl = readl(NFC_REG_CTL); + ctl &= ~NFC_RB_SEL; + ctl |= ((rb & 0x1) << 3); + writel(ctl, NFC_REG_CTL); +} + +// 1 for ready, 0 for not ready +static inline int check_rb_ready(int rb) +{ + return (readl(NFC_REG_ST) & (NFC_RB_STATE0 << (rb & 0x3))) ? 1 : 0; +} + +static void enable_random(void) +{ + uint32_t ctl; + ctl = readl(NFC_REG_ECC_CTL); + ctl |= NFC_RANDOM_EN; + ctl &= ~NFC_RANDOM_DIRECTION; + ctl &= ~NFC_RANDOM_SEED; + ctl |= (random_seed << 16); + writel(ctl, NFC_REG_ECC_CTL); +} + +static void disable_random(void) +{ + uint32_t ctl; + ctl = readl(NFC_REG_ECC_CTL); + ctl &= ~NFC_RANDOM_EN; + writel(ctl, NFC_REG_ECC_CTL); +} + +static void enable_ecc(int pipline) +{ + uint32_t cfg = readl(NFC_REG_ECC_CTL); + if (pipline) + cfg |= NFC_ECC_PIPELINE; + else + cfg &= (~NFC_ECC_PIPELINE) & 0xffffffff; + + // if random open, disable exception + if(cfg & (1 << 9)) + cfg &= ~(0x1 << 4); + else + cfg |= 1 << 4; + + //cfg |= (1 << 1); 16 bit ecc + + cfg |= NFC_ECC_EN; + writel(cfg, NFC_REG_ECC_CTL); +} + +static void set_ecc_mode(int mode) +{ + uint32_t ctl; + ctl = readl(NFC_REG_ECC_CTL); + ctl &= ~NFC_ECC_MODE; + ctl |= mode << NFC_ECC_MODE_SHIFT; + writel(ctl, NFC_REG_ECC_CTL); +} + +int check_ecc(int eblock_cnt) +{ + int i; + int ecc_mode; + int max_ecc_bit_cnt = 16; + int cfg, corrected = 0; + + ecc_mode = (readl(NFC_REG_ECC_CTL) & NFC_ECC_MODE) >> NFC_ECC_MODE_SHIFT; + if(ecc_mode == 0) + max_ecc_bit_cnt = 16; + if(ecc_mode == 1) + max_ecc_bit_cnt = 24; + if(ecc_mode == 2) + max_ecc_bit_cnt = 28; + if(ecc_mode == 3) + max_ecc_bit_cnt = 32; + if(ecc_mode == 4) + max_ecc_bit_cnt = 40; + if(ecc_mode == 5) + max_ecc_bit_cnt = 48; + if(ecc_mode == 6) + max_ecc_bit_cnt = 56; + if(ecc_mode == 7) + max_ecc_bit_cnt = 60; + if(ecc_mode == 8) + max_ecc_bit_cnt = 64; + + //check ecc error + cfg = readl(NFC_REG_ECC_ST) & 0xffff; + for (i = 0; i < eblock_cnt; i++) { + if (cfg & (1<>= 8) { + int bits = cfg & 0xff; + if (bits >= max_ecc_bit_cnt - 4) { + DBG_INFO("ECC limit %d/%d\n", bits, max_ecc_bit_cnt); + corrected++; + } + } + } + + return corrected; +} + +static void disable_ecc(void) +{ + uint32_t cfg = readl(NFC_REG_ECC_CTL); + cfg &= (~NFC_ECC_EN) & 0xffffffff; + writel(cfg, NFC_REG_ECC_CTL); +} + +///////////////////////////////////////////////////////////////// +// NFC +// + +static void nfc_select_chip(struct mtd_info *mtd, int chip) +{ + uint32_t ctl; + // A10 has 8 CE pin to support 8 flash chips + ctl = readl(NFC_REG_CTL); + ctl &= ~NFC_CE_SEL; + ctl |= ((chip & 7) << 24); + writel(ctl, NFC_REG_CTL); +} + +static void nfc_cmdfunc(struct mtd_info *mtd, unsigned command, int column, + int page_addr) +{ + int i; + uint32_t cfg = command; + int read_size, write_size, do_enable_ecc = 0; + int addr_cycle, wait_rb_flag, byte_count, sector_count; + addr_cycle = wait_rb_flag = byte_count = sector_count = 0; + + //DBG_INFO("command %x ...\n", command); + wait_cmdfifo_free(); + + // switch to AHB + writel(readl(NFC_REG_CTL) & ~NFC_RAM_METHOD, NFC_REG_CTL); + + switch (command) { + case NAND_CMD_RESET: + case NAND_CMD_ERASE2: + break; + case NAND_CMD_READID: + addr_cycle = 1; + // read 8 byte ID + byte_count = 8; + break; + case NAND_CMD_PARAM: + addr_cycle = 1; + byte_count = 1024; + wait_rb_flag = 1; + break; + case NAND_CMD_RNDOUT: + addr_cycle = 2; + writel(0xE0, NFC_REG_RCMD_SET); + byte_count = mtd->oobsize; + cfg |= NFC_SEQ | NFC_SEND_CMD2; + wait_rb_flag = 1; + break; + case NAND_CMD_READOOB: + case NAND_CMD_READ0: + if (command == NAND_CMD_READOOB) { + cfg = NAND_CMD_READ0; + // sector num to read + sector_count = 1024 / 1024; + read_size = 1024; + // OOB offset + column += mtd->writesize; + } + else { + sector_count = mtd->writesize / 1024; + read_size = mtd->writesize; + do_enable_ecc = 1; + //DBG_INFO("cmdfunc read %d %d\n", column, page_addr); + } + + //access NFC internal RAM by DMA bus + writel(readl(NFC_REG_CTL) | NFC_RAM_METHOD, NFC_REG_CTL); + // if the size is smaller than NFC_REG_SECTOR_NUM, read command won't finish + // does that means the data read out (by DMA through random data output) hasn't finish? + dma_nand_config_start(dma_hdle, 0, (uint32_t)read_buffer, read_size); + addr_cycle = 5; + // RAM0 is 1K size + byte_count =1024; + wait_rb_flag = 1; + // 0x30 for 2nd cycle of read page + // 0x05+0xe0 is the random data output command + writel(0x00e00530, NFC_REG_RCMD_SET); + // NFC_SEND_CMD1 for the command 1nd cycle enable + // NFC_SEND_CMD2 for the command 2nd cycle enable + // NFC_SEND_CMD3 & NFC_SEND_CMD4 for NFC_READ_CMD0 & NFC_READ_CMD1 + cfg |= NFC_SEND_CMD2 | NFC_DATA_SWAP_METHOD; + // 3 - ? + // 2 - page command + // 1 - spare command? + // 0 - normal command + cfg |= 2 << 30; + break; + case NAND_CMD_ERASE1: + addr_cycle = 3; + //DBG_INFO("cmdfunc earse block %d\n", page_addr); + break; + case NAND_CMD_SEQIN: + program_column = column; + program_page = page_addr; + write_offset = 0; + return; + case NAND_CMD_PAGEPROG: + cfg = NAND_CMD_SEQIN; + addr_cycle = 5; + column = program_column; + page_addr = program_page; + // for write OOB + if (column == mtd->writesize) { + sector_count = 1024 /1024; + write_size = 1024; + } + else if (column == 0) { + sector_count = mtd->writesize / 1024; + do_enable_ecc = 1; + write_size = mtd->writesize; + for (i = 0; i < sector_count; i++) + writel(*((unsigned int *)(write_buffer + mtd->writesize) + i), NFC_REG_USER_DATA(i)); + } + else { + ERR_INFO("program unsupported column %d %d\n", column, page_addr); + return; + } + + //access NFC internal RAM by DMA bus + writel(readl(NFC_REG_CTL) | NFC_RAM_METHOD, NFC_REG_CTL); + dma_nand_config_start(dma_hdle, 1, (uint32_t)write_buffer, write_size); + // RAM0 is 1K size + byte_count =1024; + writel(0x00008510, NFC_REG_WCMD_SET); + cfg |= NFC_SEND_CMD2 | NFC_DATA_SWAP_METHOD | NFC_ACCESS_DIR; + cfg |= 2 << 30; + if (column != 0) { + DBG_INFO("cmdfunc program %d %d with %x %x %x\n", column, page_addr, + write_buffer[0], write_buffer[1], write_buffer[2]); + } + break; + case NAND_CMD_STATUS: + byte_count = 1; + break; + default: + ERR_INFO("unknown command\n"); + return; + } + + // address cycle + if (addr_cycle) { + uint32_t low = 0; + uint32_t high = 0; + switch (addr_cycle) { + case 2: + low = column & 0xffff; + break; + case 3: + low = page_addr & 0xffffff; + break; + case 5: + high = (page_addr >> 16) & 0xff; + case 4: + low = (column & 0xffff) | (page_addr << 16); + break; + } + writel(low, NFC_REG_ADDR_LOW); + writel(high, NFC_REG_ADDR_HIGH); + cfg |= NFC_SEND_ADR; + cfg |= ((addr_cycle - 1) << 16); + } + + // command will wait until the RB ready to mark finish? + if (wait_rb_flag) + cfg |= NFC_WAIT_FLAG; + + // will fetch data + if (byte_count) { + cfg |= NFC_DATA_TRANS; + writel(byte_count, NFC_REG_CNT); + } + + // set sectors + if (sector_count) + writel(sector_count, NFC_REG_SECTOR_NUM); + + // enable ecc + if (hwecc_switch && do_enable_ecc) + enable_ecc(1); + + // send command + cfg |= NFC_SEND_CMD1; + writel(cfg, NFC_REG_CMD); + + switch (command) { + case NAND_CMD_READ0: + case NAND_CMD_READOOB: + case NAND_CMD_PAGEPROG: + dma_nand_wait_finish(); + break; + } + + // wait command send complete + wait_cmdfifo_free(); + wait_cmd_finish(); + + // reset will wait for RB ready + switch (command) { + case NAND_CMD_RESET: + // wait rb0 ready + select_rb(0); + while (!check_rb_ready(0)); + // wait rb1 ready + select_rb(1); + while (!check_rb_ready(1)); + // select rb 0 back + select_rb(0); + break; + case NAND_CMD_READ0: + for (i = 0; i < sector_count; i++) + *((unsigned int *)(read_buffer + mtd->writesize) + i) = readl(NFC_REG_USER_DATA(i)); + break; + } + + if (hwecc_switch && do_enable_ecc) + disable_ecc(); + + //DBG_INFO("done\n"); + + // read write offset + read_offset = 0; +} + +static uint8_t nfc_read_byte(struct mtd_info *mtd) +{ + return readb(NFC_RAM0_BASE + read_offset++); +} + +static int nfc_dev_ready(struct mtd_info *mtd) +{ + return check_rb_ready(0); +} + +static void nfc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) +{ + if (write_offset + len > buffer_size) { + ERR_INFO("write too much offset=%d len=%d buffer size=%d\n", + write_offset, len, buffer_size); + return; + } + memcpy(write_buffer + write_offset, buf, len); + write_offset += len; +} + +static void nfc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + if (read_offset + len > buffer_size) { + ERR_INFO("read too much offset=%d len=%d buffer size=%d\n", + read_offset, len, buffer_size); + return; + } + memcpy(buf, read_buffer + read_offset, len); + read_offset += len; +} + +static irqreturn_t nfc_interrupt_handler(int irq, void *dev_id) +{ + unsigned int st = readl(NFC_REG_ST); + if (st & NFC_RB_B2R) { + wake_up(&nand_rb_wait); + } + if (st & NFC_CMD_INT_FLAG) { + DBG_INFO("CMD INT\n"); + } + if (st & NFC_DMA_INT_FLAG) { + //DBG_INFO("DMA INT\n"); + } + if (st & NFC_NATCH_INT_FLAG) { + DBG_INFO("NATCH INT\n"); + } + // clear interrupt + writel(st, NFC_REG_ST); + return IRQ_HANDLED; +} + +static int get_chip_status(struct mtd_info *mtd) +{ + struct nand_chip *nand = mtd->priv; + nand->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); + return nand->read_byte(mtd); +} + +// For erase and program command to wait for chip ready +static int nfc_wait(struct mtd_info *mtd, struct nand_chip *chip) +{ + int err; + + // clear B2R interrupt state + writel(NFC_RB_B2R, NFC_REG_ST); + + if (check_rb_ready(0)) + goto out; + + // enable B2R interrupt + writel(NFC_B2R_INT_ENABLE, NFC_REG_INT); + if ((err = wait_event_timeout(nand_rb_wait, check_rb_ready(0), 1*HZ)) < 0) { + DBG_INFO("nfc wait got exception %d\n", err); + } + // disable interrupt + writel(0, NFC_REG_INT); + +out: + return get_chip_status(mtd); +} + +static void nfc_ecc_hwctl(struct mtd_info *mtd, int mode) +{ + +} + +static int nfc_ecc_calculate(struct mtd_info *mtd, const uint8_t *dat, uint8_t *ecc_code) +{ + return 0; +} + +static int nfc_ecc_correct(struct mtd_info *mtd, uint8_t *dat, uint8_t *read_ecc, uint8_t *calc_ecc) +{ + if (!hwecc_switch) + return 0; + + return check_ecc(mtd->writesize / 1024); +} + +struct save_1k_mode { + uint32_t ctl; + uint32_t ecc_ctl; + uint32_t spare_area; +}; + +static void enter_1k_mode(struct save_1k_mode *save) +{ + uint32_t ctl; + + ctl = readl(NFC_REG_CTL); + save->ctl = ctl; + ctl &= ~NFC_PAGE_SIZE; + writel(ctl, NFC_REG_CTL); + + ctl = readl(NFC_REG_ECC_CTL); + save->ecc_ctl = ctl; + set_ecc_mode(8); + + ctl = readl(NFC_REG_SPARE_AREA); + save->spare_area = ctl; + writel(1024, NFC_REG_SPARE_AREA); +} + +static void exit_1k_mode(struct save_1k_mode *save) +{ + writel(save->ctl, NFC_REG_CTL); + writel(save->ecc_ctl, NFC_REG_ECC_CTL); + writel(save->spare_area, NFC_REG_SPARE_AREA); +} + +void nfc_read_page1k(uint32_t page_addr, void *buff) +{ + struct save_1k_mode save; + uint32_t cfg = NAND_CMD_READ0 | NFC_SEQ | NFC_SEND_CMD1 | NFC_DATA_TRANS | NFC_SEND_ADR | + NFC_SEND_CMD2 | ((5 - 1) << 16) | NFC_WAIT_FLAG | NFC_DATA_SWAP_METHOD | (2 << 30); + + wait_cmdfifo_free(); + + enter_1k_mode(&save); + + writel(readl(NFC_REG_CTL) | NFC_RAM_METHOD, NFC_REG_CTL); + dma_nand_config_start(dma_hdle, 0, (uint32_t)buff, 1024); + + writel(page_addr << 16, NFC_REG_ADDR_LOW); + writel(page_addr >> 16, NFC_REG_ADDR_HIGH); + writel(1024, NFC_REG_CNT); + writel(0x00e00530, NFC_REG_RCMD_SET); + writel(1, NFC_REG_SECTOR_NUM); + + enable_random(); + if (hwecc_switch) + enable_ecc(1); + + writel(cfg, NFC_REG_CMD); + + dma_nand_wait_finish(); + wait_cmdfifo_free(); + wait_cmd_finish(); + + if (hwecc_switch) { + disable_ecc(); + check_ecc(1); + } + disable_random(); + + exit_1k_mode(&save); +} + +static void first_test_nfc(struct mtd_info *mtd) +{ + nfc_cmdfunc(mtd, NAND_CMD_RESET, -1, -1); + DBG_INFO("reset\n"); + DBG_INFO("nand ctrl %x\n", readl(NFC_REG_CTL)); + DBG_INFO("nand ecc ctrl %x\n", readl(NFC_REG_ECC_CTL)); + DBG_INFO("nand timing %x\n", readl(NFC_REG_TIMING_CTL)); + nfc_cmdfunc(mtd, NAND_CMD_READID, 0, -1); + DBG_INFO("readid first time: %x %x\n", + nfc_read_byte(mtd), nfc_read_byte(mtd)); + nfc_cmdfunc(mtd, NAND_CMD_READID, 0, -1); + DBG_INFO("readid second time: %x %x\n", + nfc_read_byte(mtd), nfc_read_byte(mtd)); +} + +int nfc_first_init(struct mtd_info *mtd) +{ + uint32_t ctl; + struct nand_chip *nand = mtd->priv; + + if (hwecc_switch) { + DBG_INFO("hardware ECC is on\n"); + } + else { + DBG_INFO("hardware ECC is off\n"); + } + + if (use_flash_bbt) { + DBG_INFO("use flash bad block table\n"); + } + else { + DBG_INFO("not use flash bad block table\n"); + } + + DBG_INFO("random_seed = %x\n", random_seed); + + // set NFC clock source + sunxi_set_nand_clock(20); + + // set NFC pio + sunxi_set_nand_pio(); + + // reset NFC + ctl = readl(NFC_REG_CTL); + ctl |= NFC_RESET; + writel(ctl, NFC_REG_CTL); + while(readl(NFC_REG_CTL) & NFC_RESET); + + // enable NFC + ctl = NFC_EN; + writel(ctl, NFC_REG_CTL); + + // serial_access_mode = 1 + // this is needed by some nand chip to read ID + ctl = (1 << 8); + writel(ctl, NFC_REG_TIMING_CTL); + + //first_test_nfc(mtd); + + nand->ecc.mode = NAND_ECC_HW; + nand->ecc.hwctl = nfc_ecc_hwctl; + nand->ecc.calculate = nfc_ecc_calculate; + nand->ecc.correct = nfc_ecc_correct; + nand->select_chip = nfc_select_chip; + nand->dev_ready = nfc_dev_ready; + nand->cmdfunc = nfc_cmdfunc; + nand->read_byte = nfc_read_byte; + nand->read_buf = nfc_read_buf; + nand->write_buf = nfc_write_buf; + nand->waitfunc = nfc_wait; + if (use_flash_bbt) + nand->bbt_options = NAND_BBT_USE_FLASH; + return 0; +} + +static void print_page(struct mtd_info *mtd, int page) +{ + int i; + char buff[1024]; + nfc_cmdfunc(mtd, NAND_CMD_READ0, 0, page); + nfc_read_buf(mtd, buff, 6); + DBG_INFO("READ: %x %x %x %x %x %x\n", + buff[0], buff[1], buff[2], buff[3], buff[4], buff[5]); + + nfc_cmdfunc(mtd, NAND_CMD_READOOB, 0, page); + nfc_read_buf(mtd, buff, 640); + for (i = 0; i < 640; i++) + printk("%02x ", buff[i]); + printk("\n"); +} + +static void test_nfc(struct mtd_info *mtd) +{ + int i, j, n=0; + struct nand_chip *nand = mtd->priv; + int page = 1280; + unsigned char buff[1024]; + int blocks = 2, num_blocks = mtd->writesize / 1024; + + DBG_INFO("============== TEST NFC ================\n"); + + // read page + print_page(mtd, page); + + // erase block + nfc_cmdfunc(mtd, NAND_CMD_ERASE1, 0, page); + nfc_cmdfunc(mtd, NAND_CMD_ERASE2, -1, -1); + nfc_wait(mtd, nand); + print_page(mtd, page); + + // write block + nfc_cmdfunc(mtd, NAND_CMD_SEQIN, 0, page); + for (i = 0; i < blocks; i++) { + for (j = 0; j < 1024; j++, n++) + buff[j] = n % 256; + nfc_write_buf(mtd, buff, 1024); + } + for ( ; i < num_blocks; i++) { + memset(buff, 0xff, 1024); + nfc_write_buf(mtd, buff, 1024); + } + // wrong mtd->oobsize for SAMSUNG K9GBG08U0A + for (i = 0, n = 128; i < 640; i++, n++) + buff[i] = n % 256; + nfc_write_buf(mtd, buff, 1024); + nfc_cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); + nfc_wait(mtd, nand); + print_page(mtd, page); + +/* + // test oob write + nfc_cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page); + for (i = 0, n = 0xff; i < 640; i++, n++) + buff[i] = n % 256; + nfc_write_buf(mtd, buff, 1024); + nfc_cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); + nfc_wait(mtd, nand); + print_page(mtd, page); +*/ +} + +// Test unit ops +static void test_ops(struct mtd_info *mtd) +{ + uint32_t page = 1280; + uint32_t v1, v2; + + // test sequence read + wait_cmdfifo_free(); + // NFC_DATA_TRANS = 1, NFC fetch data to RAM0 + // NFC_CMD_TYPE = 1,2,3 won't read out any thing + v1 = NAND_CMD_READ0 | NFC_SEQ | NFC_SEND_CMD1 | NFC_DATA_TRANS | NFC_SEND_ADR | NFC_SEND_CMD2 | ((5 - 1) << 16) | NFC_WAIT_FLAG | (3 << 30); + v2 = NAND_CMD_READSTART; + writel(page << 16, NFC_REG_ADDR_LOW); + writel(page >> 16, NFC_REG_ADDR_HIGH); + //writel(2, NFC_REG_SECTOR_NUM); + // NFC_REG_CNT = n, fetch n byte to RAM + writel(1, NFC_REG_CNT); + writel(v2, NFC_REG_RCMD_SET); + writel(v1, NFC_REG_CMD); + wait_cmdfifo_free(); + wait_cmd_finish(); + DBG_INFO("SEQ READ IO: %x %x %x %x %x %x\n", + readb(NFC_REG_IO_DATA), + readb(NFC_REG_IO_DATA), + readb(NFC_REG_IO_DATA), + readb(NFC_REG_IO_DATA), + readb(NFC_REG_IO_DATA), + readb(NFC_REG_IO_DATA)); + DBG_INFO("SEQ READ RAM: %x %x %x %x %x %x\n", + readb(NFC_RAM0_BASE), + readb(NFC_RAM0_BASE + 1), + readb(NFC_RAM0_BASE + 2), + readb(NFC_RAM0_BASE + 3), + readb(NFC_RAM0_BASE + 4), + readb(NFC_RAM0_BASE + 5)); +} + +int nfc_second_init(struct mtd_info *mtd) +{ + int i, err, j; + uint32_t ctl; + uint8_t id[8]; + struct nand_chip_param *nand_chip_param, *chip_param = NULL; + struct nand_chip *nand = mtd->priv; + + // get nand chip id + nfc_cmdfunc(mtd, NAND_CMD_READID, 0, -1); + for (i = 0; i < 8; i++) + id[i] = nfc_read_byte(mtd); + DBG_INFO("nand chip id: %x %x %x %x %x %x %x %x\n", + id[0], id[1], id[2], id[3], + id[4], id[5], id[6], id[7]); + + // find chip + nand_chip_param = sunxi_get_nand_chip_param(id[0]); + for (i = 0; nand_chip_param[i].id_len; i++) { + int find = 1; + for (j = 0; j < nand_chip_param[i].id_len; j++) { + if (id[j] != nand_chip_param[i].id[j]) { + find = 0; + break; + } + } + if (find) { + chip_param = &nand_chip_param[i]; + DBG_INFO("find nand chip in sunxi database\n"); + break; + } + } + + // not find + if (chip_param == NULL) { + ERR_INFO("can't find nand chip in sunxi database\n"); + return -ENODEV; + } + + // set final NFC clock freq + if (chip_param->clock_freq > 30) + chip_param->clock_freq = 30; + sunxi_set_nand_clock(chip_param->clock_freq); + DBG_INFO("set final clock freq to %dMHz\n", chip_param->clock_freq); + + // disable interrupt + writel(0, NFC_REG_INT); + // clear interrupt + writel(readl(NFC_REG_ST), NFC_REG_ST); + + // set ECC mode + set_ecc_mode(chip_param->ecc_mode); + + // enable NFC + ctl = NFC_EN; + + // Bus width + if (nand->options & NAND_BUSWIDTH_16) { + DBG_INFO("flash chip bus width 16\n"); + ctl |= (1 & 0x1) << 2; + } + else { + DBG_INFO("flash chip bus width 8\n"); + } + + // Page size + if (nand->page_shift > 14 || nand->page_shift < 10) { + ERR_INFO("Flash chip page shift out of range %d\n", nand->page_shift); + err = -EINVAL; + goto out; + } + // 0 for 1K + ctl |= ((nand->page_shift - 10) & 0xf) << 8; + writel(ctl, NFC_REG_CTL); + + writel(0xff, NFC_REG_TIMING_CFG); + writel(1 << nand->page_shift, NFC_REG_SPARE_AREA); + + // disable random + disable_random(); + + // setup ECC layout + sunxi_ecclayout.eccbytes = 0; + sunxi_ecclayout.oobavail = mtd->writesize / 1024 * 4 - 2; + sunxi_ecclayout.oobfree->offset = 1; + sunxi_ecclayout.oobfree->length = mtd->writesize / 1024 * 4 - 2; + nand->ecc.layout = &sunxi_ecclayout; + nand->ecc.size = mtd->writesize; + nand->ecc.bytes = 0; + + // setup DMA + dma_hdle = dma_nand_request(1); + if (dma_hdle == 0) { + ERR_INFO("request DMA fail\n"); + err = -ENODEV; + goto out; + } + + // alloc buffer + buffer_size = mtd->writesize + 1024; + read_buffer = kmalloc(buffer_size, GFP_KERNEL); + if (read_buffer == NULL) { + ERR_INFO("alloc read buffer fail\n"); + err = -ENOMEM; + goto release_dma_out; + } + write_buffer = kmalloc(buffer_size, GFP_KERNEL); + if (write_buffer == NULL) { + ERR_INFO("alloc write buffer fail\n"); + err = -ENOMEM; + goto free_read_out; + } + + // map + read_buffer_dma = dma_map_single(NULL, read_buffer, buffer_size, DMA_FROM_DEVICE); + write_buffer_dma = dma_map_single(NULL, write_buffer, buffer_size, DMA_TO_DEVICE); + + DBG_INFO("OOB size = %d page size = %d block size = %d total size = %lld\n", + mtd->oobsize, mtd->writesize, mtd->erasesize, mtd->size); + + // register IRQ + if ((err = request_irq(SW_INT_IRQNO_NAND, nfc_interrupt_handler, IRQF_DISABLED, "NFC", mtd)) < 0) { + ERR_INFO("request IRQ fail\n"); + goto free_write_out; + } + + // test command + //test_nfc(mtd); + //test_ops(mtd); + //print_page(mtd, 0); + + return 0; + +free_write_out: + kfree(write_buffer); +free_read_out: + kfree(read_buffer); +release_dma_out: + dma_nand_release(dma_hdle); +out: + return err; +} + +void nfc_exit(struct mtd_info *mtd) +{ + free_irq(SW_INT_IRQNO_NAND, mtd); + dma_unmap_single(NULL, read_buffer_dma, buffer_size, DMA_FROM_DEVICE); + dma_unmap_single(NULL, write_buffer_dma, buffer_size, DMA_TO_DEVICE); + dma_nand_release(dma_hdle); + kfree(write_buffer); + kfree(read_buffer); + sunxi_release_nand_pio(); + release_nand_clock(); +} + diff --git a/drivers/mtd/nand/sunxi-nand/nfc.h b/drivers/mtd/nand/sunxi-nand/nfc.h new file mode 100644 index 00000000000000..e6e516e2bb5b24 --- /dev/null +++ b/drivers/mtd/nand/sunxi-nand/nfc.h @@ -0,0 +1,29 @@ +/* + * nfc.h + * + * Copyright (C) 2013 Qiang Yu + * + * 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 3 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, see . + */ + +#ifndef _SUNXI_NAND_NFC_H +#define _SUNXI_NAND_NFC_H + +void nfc_read_page1k(uint32_t page_addr, void *buff); + +int nfc_first_init(struct mtd_info *mtd); +int nfc_second_init(struct mtd_info *mtd); +void nfc_exit(struct mtd_info *mtd); + +#endif diff --git a/drivers/mtd/nand/sunxi-nand/regs.h b/drivers/mtd/nand/sunxi-nand/regs.h new file mode 100644 index 00000000000000..bd88592cf22ade --- /dev/null +++ b/drivers/mtd/nand/sunxi-nand/regs.h @@ -0,0 +1,172 @@ +#ifndef _SUNXI_NAND_REGS_H +#define _SUNXI_NAND_REGS_H + +#define CCM_IO_BASE 0xf1c20000 +#define PLL5_CFG_REG (CCM_IO_BASE + 0x20) +#define PLL5_OUT_EXT_DIV_P_SHIFT 16 +#define PLL5_OUT_EXT_DIV_P_MASK (0x03 << PLL5_OUT_EXT_DIV_P_SHIFT) +#define PLL5_FACTOR_N_SHIFT 8 +#define PLL5_FACTOR_N_MASK (0x1f << PLL5_FACTOR_N_SHIFT) +#define PLL5_FACTOR_K_SHIFT 4 +#define PLL5_FACTOR_K_MASK (0x03 << PLL5_FACTOR_K_SHIFT) +#define PLL5_FACTOR_M_SHIFT 0 +#define PLL5_FACTOR_M_MASK (0x03 << PLL5_FACTOR_M_SHIFT) +#define AHB_GATING_REG0 (CCM_IO_BASE + 0x60) +#define AHB_GATING_NAND_CLK_SHIFT 13 +#define NAND_SCLK_CFG_REG (CCM_IO_BASE + 0x80) +#define SCLK_GATING_SHIFT 31 +#define CLK_SRC_SEL_SHIFT 24 +#define CLK_SRC_SEL_MASK (0x03 << CLK_SRC_SEL_SHIFT) +#define CLK_DIV_RATIO_N_SHIFT 16 +#define CLK_DIV_RATIO_N_MASK (0x03 << CLK_DIV_RATIO_N_SHIFT) +#define CLK_DIV_RATIO_M_SHIFT 0 +#define CLK_DIV_RATIO_M_MASK (0x0f << CLK_DIV_RATIO_M_SHIFT) + +#define PIO_IO_BASE 0xf1c20800 +#define PC_CFG0_REG (PIO_IO_BASE + 0x48) +#define PC_CFG1_REG (PIO_IO_BASE + 0x4c) +#define PC_CFG2_REG (PIO_IO_BASE + 0x50) + +#define NAND_IO_BASE 0xf1c03000 +#define __NFC_REG(x) (NAND_IO_BASE + x) +/* offset */ +#define NFC_REG_o_CTL 0x0000 +#define NFC_REG_o_ST 0x0004 +#define NFC_REG_o_INT 0x0008 +#define NFC_REG_o_TIMING_CTL 0x000C +#define NFC_REG_o_TIMING_CFG 0x0010 +#define NFC_REG_o_ADDR_LOW 0x0014 +#define NFC_REG_o_ADDR_HIGH 0x0018 +#define NFC_REG_o_SECTOR_NUM 0x001C +#define NFC_REG_o_CNT 0x0020 +#define NFC_REG_o_CMD 0x0024 +#define NFC_REG_o_RCMD_SET 0x0028 +#define NFC_REG_o_WCMD_SET 0x002C +#define NFC_REG_o_IO_DATA 0x0030 +#define NFC_REG_o_ECC_CTL 0x0034 +#define NFC_REG_o_ECC_ST 0x0038 +#define NFC_REG_o_DEBUG 0x003C +#define NFC_REG_o_ECC_CNT0 0x0040 +#define NFC_REG_o_ECC_CNT1 0x0044 +#define NFC_REG_o_ECC_CNT2 0x0048 +#define NFC_REG_o_ECC_CNT3 0x004c +#define NFC_REG_o_USER_DATA_BASE 0x0050 +#define NFC_REG_o_SPARE_AREA 0x00A0 +#define NFC_o_RAM0_BASE 0x0400 +#define NFC_o_RAM1_BASE 0x0800 +/* registers */ +#define NFC_REG_CTL __NFC_REG( NFC_REG_o_CTL ) +#define NFC_REG_ST __NFC_REG( NFC_REG_o_ST ) +#define NFC_REG_INT __NFC_REG( NFC_REG_o_INT ) +#define NFC_REG_TIMING_CTL __NFC_REG( NFC_REG_o_TIMING_CTL ) +#define NFC_REG_TIMING_CFG __NFC_REG( NFC_REG_o_TIMING_CFG ) +#define NFC_REG_ADDR_LOW __NFC_REG( NFC_REG_o_ADDR_LOW ) +#define NFC_REG_ADDR_HIGH __NFC_REG( NFC_REG_o_ADDR_HIGH ) +#define NFC_REG_SECTOR_NUM __NFC_REG( NFC_REG_o_SECTOR_NUM ) +#define NFC_REG_CNT __NFC_REG( NFC_REG_o_CNT ) +#define NFC_REG_CMD __NFC_REG( NFC_REG_o_CMD ) +#define NFC_REG_RCMD_SET __NFC_REG( NFC_REG_o_RCMD_SET ) +#define NFC_REG_WCMD_SET __NFC_REG( NFC_REG_o_WCMD_SET ) +#define NFC_REG_IO_DATA __NFC_REG( NFC_REG_o_IO_DATA ) +#define NFC_REG_ECC_CTL __NFC_REG( NFC_REG_o_ECC_CTL ) +#define NFC_REG_ECC_ST __NFC_REG( NFC_REG_o_ECC_ST ) +#define NFC_REG_ECC_CNT0 __NFC_REG( NFC_REG_o_ECC_CNT0 ) +#define NFC_REG_ECC_CNT1 __NFC_REG( NFC_REG_o_ECC_CNT1 ) +#define NFC_REG_ECC_CNT2 __NFC_REG( NFC_REG_o_ECC_CNT2 ) +#define NFC_REG_ECC_CNT3 __NFC_REG( NFC_REG_o_ECC_CNT3 ) +#define NFC_REG_DEBUG __NFC_REG( NFC_REG_o_DEBUG ) +#define NFC_REG_USER_DATA(sct_num) __NFC_REG( NFC_REG_o_USER_DATA_BASE + 4 * sct_num ) +#define NFC_REG_SPARE_AREA __NFC_REG( NFC_REG_o_SPARE_AREA ) +#define NFC_RAM0_BASE __NFC_REG( NFC_o_RAM0_BASE ) +#define NFC_RAM1_BASE __NFC_REG( NFC_o_RAM1_BASE ) + +/*define bit use in NFC_CTL*/ +#define NFC_EN (1 << 0) +#define NFC_RESET (1 << 1) +#define NFC_BUS_WIDYH (1 << 2) +#define NFC_RB_SEL (1 << 3) +#define NFC_CE_SEL (7 << 24) +#define NFC_CE_CTL (1 << 6) +#define NFC_CE_CTL1 (1 << 7) +#define NFC_PAGE_SIZE (0xf << 8) +#define NFC_SAM (1 << 12) +#define NFC_RAM_METHOD (1 << 14) +#define NFC_DEBUG_CTL (1 << 31) + +/*define bit use in NFC_ST*/ +#define NFC_RB_B2R (1 << 0) +#define NFC_CMD_INT_FLAG (1 << 1) +#define NFC_DMA_INT_FLAG (1 << 2) +#define NFC_CMD_FIFO_STATUS (1 << 3) +#define NFC_STA (1 << 4) +#define NFC_NATCH_INT_FLAG (1 << 5) +#define NFC_RB_STATE0 (1 << 8) +#define NFC_RB_STATE1 (1 << 9) +#define NFC_RB_STATE2 (1 << 10) +#define NFC_RB_STATE3 (1 << 11) + +/*define bit use in NFC_INT*/ +#define NFC_B2R_INT_ENABLE (1 << 0) +#define NFC_CMD_INT_ENABLE (1 << 1) +#define NFC_DMA_INT_ENABLE (1 << 2) + + +/*define bit use in NFC_CMD*/ +#define NFC_CMD_LOW_BYTE (0xff << 0) +#define NFC_CMD_HIGH_BYTE (0xff << 8) +#define NFC_ADR_NUM (0x7 << 16) +#define NFC_SEND_ADR (1 << 19) +#define NFC_ACCESS_DIR (1 << 20) +#define NFC_DATA_TRANS (1 << 21) +#define NFC_SEND_CMD1 (1 << 22) +#define NFC_WAIT_FLAG (1 << 23) +#define NFC_SEND_CMD2 (1 << 24) +#define NFC_SEQ (1 << 25) +#define NFC_DATA_SWAP_METHOD (1 << 26) +#define NFC_ROW_AUTO_INC (1 << 27) +#define NFC_SEND_CMD3 (1 << 28) +#define NFC_SEND_CMD4 (1 << 29) +#define NFC_CMD_TYPE (3 << 30) + +/* define bit use in NFC_RCMD_SET*/ +#define NFC_READ_CMD (0xff<< 0) +#define NFC_RANDOM_READ_CMD0 (0xff << 8) +#define NFC_RANDOM_READ_CMD1 (0xff << 16) + +/*define bit use in NFC_WCMD_SET*/ +#define NFC_PROGRAM_CMD (0xff << 0) +#define NFC_RANDOM_WRITE_CMD (0xff << 8) +#define NFC_READ_CMD0 (0xff << 16) +#define NFC_READ_CMD1 (0xff << 24) + +/*define bit use in NFC_ECC_CTL*/ +#define NFC_ECC_EN (1 << 0) +#define NFC_ECC_PIPELINE (1 << 3) +#define NFC_ECC_EXCEPTION (1 << 4) +#define NFC_ECC_BLOCK_SIZE (1 << 5) +#define NFC_RANDOM_EN (1 << 9 ) +#define NFC_RANDOM_DIRECTION (1 << 10 ) +#define NFC_ECC_MODE_SHIFT 12 +#define NFC_ECC_MODE (0xf << NFC_ECC_MODE_SHIFT) +#define NFC_RANDOM_SEED (0x7fff << 16) + +#define NFC_IRQ_MAJOR 13 +/*cmd flag bit*/ +#define NFC_PAGE_MODE 0x1 +#define NFC_NORMAL_MODE 0x0 + +#define NFC_DATA_FETCH 0x1 +#define NFC_NO_DATA_FETCH 0x0 +#define NFC_MAIN_DATA_FETCH 0x1 +#define NFC_SPARE_DATA_FETCH 0X0 +#define NFC_WAIT_RB 0x1 +#define NFC_NO_WAIT_RB 0x0 +#define NFC_IGNORE 0x0 + +#define NFC_INT_RB 0 +#define NFC_INT_CMD 1 +#define NFC_INT_DMA 2 +#define NFC_INT_BATCh 5 + +#endif +