/* * Copyright (c) 2007-2019 Allwinnertech Co., Ltd. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and * may be copied, distributed, and modified under those terms. * * 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. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../src/include/nand_type.h" #include "../src/include/nand_drv_cfg.h" #include "../src/include/nand_format.h" #include "../src/include/nand_logic.h" #include "../src/include/nand_oal.h" #include "../src/include/nand_physic.h" #include "../src/include/nand_scan.h" #include "../src/include/nand_simple.h" #include "../nfd/nand_blk.h" #include "../nfd/mbr.h" #include "nand_test.h" #ifdef CONFIG_SUN4I_NANDFLASH_TEST /* open nand test module*/ #define NAND_TEST "[nand_test]:" #define RESULT_OK (0) #define RESULT_FAIL (1) #define MAX_SECTORS (100) /* max alloc buffer*/ #define BUFFER_SIZE (512*MAX_SECTORS) static ssize_t nand_test_store(struct kobject *kobject, struct attribute *attr, const char *buf, size_t count); static ssize_t nand_test_show(struct kobject *kobject, struct attribute *attr, char *buf); void obj_test_release(struct kobject *kobject); struct nand_test_card { u8 *buffer; u8 *scratch; unsigned int sector_cnt; }; struct nand_test_case { const char *name; int sectors_cnt; int (*prepare)(struct nand_test_card *, int sectors_cnt); int (*run)(struct nand_test_card *); int (*cleanup)(struct nand_test_card *); }; struct attribute prompt_attr = { .name = "nand_test", .mode = S_IRWXUGO }; static struct attribute *def_attrs[] = { &prompt_attr, NULL }; struct sysfs_ops obj_test_sysops = { .show = nand_test_show, .store = nand_test_store }; struct kobj_type ktype = { .release = obj_test_release, .sysfs_ops = &obj_test_sysops, .default_attrs = def_attrs }; void obj_test_release(struct kobject *kobject) { printk(NAND_TEST "release .\n"); } /* prepare buffer data for read and write*/ static int __nand_test_prepare(struct nand_test_card *test, int sector_cnt, int write) { int i; test->sector_cnt = sector_cnt; if (write) { memset(test->buffer, 0xDF, 512 * (sector_cnt) + 4); } else { for (i = 0; i < 512 * (sector_cnt) + 4; i++) { test->buffer[i] = i % 256; } } return 0; } static int nand_test_prepare_write(struct nand_test_card *test, int sector_cnt) { return __nand_test_prepare(test, sector_cnt, 1); } static int nand_test_prepare_read(struct nand_test_card *test, int sector_cnt) { return __nand_test_prepare(test, sector_cnt, 0); } static int nand_test_prepare_pwm(struct nand_test_card *test, int sector_cnt) { test->sector_cnt = sector_cnt; return 0; } /* read /write one sector with out verification*/ static int nand_test_simple_transfer(struct nand_test_card *test, unsigned dev_addr, unsigned start, unsigned nsector, int write) { int ret; if (write) { #ifndef NAND_CACHE_RW ret = LML_Write(start, nsector, test->buffer + dev_addr); #else /*printk("Ws %lu %lu \n",start, nsector);*/ ret = NAND_CacheWrite(start, nsector, test->buffer + dev_addr); #endif if (ret) { return -EIO; } return 0; } else { #ifndef NAND_CACHE_RW LML_FlushPageCache(); ret = LML_Read(start, nsector, test->buffer + dev_addr); #else /*printk("Rs %lu %lu \n",start, nsector);*/ LML_FlushPageCache(); ret = NAND_CacheRead(start, nsector, test->buffer + dev_addr); #endif /* read*/ if (ret) { return -EIO; } return 0; } } /* read /write one or more sectors with verification*/ static int nand_test_transfer(struct nand_test_card *test, unsigned dev_addr, unsigned start, unsigned nsector, int write) { int ret; int i; if (!write) { ret = nand_test_simple_transfer(test, 0, start, nsector, 1); /* write to sectors for read*/ if (ret) { return ret; } memset(test->buffer, 0, nsector * 512 + 4); /* clean mem for read*/ } ret = nand_test_simple_transfer(test, dev_addr, start, nsector, write); /* read or write*/ if (ret) /* read or write*/ return ret; if (write) { memset(test->buffer, 0, nsector * 512 + 4); /* clean mem for read*/ ret = nand_test_simple_transfer(test, 0, start, nsector, 0); /* read*/ if (ret) { return ret; } for (i = 0; i < nsector * 512; i++) { /* verify data*/ if (test->buffer[i] != 0xDF) { printk(KERN_INFO "[nand_test] Ttest->buffer[i] = %d, i = %d, dev_addr = %d, nsector = %d\n", test->buffer[i], i, dev_addr, nsector); return RESULT_FAIL; } } } else { /*read*/ for (i = 0 + dev_addr; i < nsector * 512 + dev_addr; i++) { /* verify data*/ if (test->buffer[i] != (i - dev_addr) % 256) { printk(KERN_INFO "[nand_test] Ttest->buffer[i] = %d, i = %d, dev_addr = %d, nsector = %d\n", test->buffer[i], i, dev_addr, nsector); return RESULT_FAIL; } } } return RESULT_OK; } /* write one sector without verification*/ static int nand_test_single_write(struct nand_test_card *test) { int ret; ret = nand_test_simple_transfer(test, 0, 0, test->sector_cnt, 1); if (ret) { return ret; } return nand_test_simple_transfer(test, 0, DiskSize / 2, test->sector_cnt, 1); } /* read one sector without verification*/ static int nand_test_single_read(struct nand_test_card *test) { int ret; ret = nand_test_simple_transfer(test, 0, 0, test->sector_cnt, 0); if (ret) { return ret; } return nand_test_simple_transfer(test, 0, DiskSize / 2, test->sector_cnt, 0); } /* write one sector with verification */ static int nand_test_verify_write(struct nand_test_card *test) { return nand_test_transfer(test, 0, 1, test->sector_cnt, 1); } /* read one sector with verification */ static int nand_test_verify_read(struct nand_test_card *test) { return nand_test_transfer(test, 0, 1, test->sector_cnt, 0); } /* write multi sector with start sector num 5*/ static int nand_test_multi_write(struct nand_test_card *test) { return nand_test_transfer(test, 0, 5, test->sector_cnt, 1); } /* write multi sector with start sector num 29*/ static int nand_test_multi_read(struct nand_test_card *test) { return nand_test_transfer(test, 0, 29, test->sector_cnt, 0); } /* write from buffer+1, buffer+2, and buffer+3, where buffer is 4 bytes algin */ static int nand_test_align_write(struct nand_test_card *test) { int ret; int i; for (i = 1; i < 4; i++) { ret = nand_test_transfer(test, i, 1, test->sector_cnt, 1); } return ret; } /* read to buffer+1, buffer+2, and buffer+3, where buffer is 4 bytes algin */ static int nand_test_align_read(struct nand_test_card *test) { int ret; int i; for (i = 1; i < 4; i++) { ret = nand_test_transfer(test, i, 1, test->sector_cnt, 0); } if (ret) { return ret; } return 0; } /* write to incorrect sector num such as -1, DiskSize, DiskSize +1 */ static int nand_test_negstart_write(struct nand_test_card *test) { int ret; /* start + sectnum > 0, start < 0 */ ret = nand_test_simple_transfer(test, 0, -5, 11, 1); if (!ret) { return RESULT_FAIL; } printk(NAND_TEST "start + sectnum > 0 pass\n"); /* start + sectnum < 0 , start < 0 */ ret = nand_test_simple_transfer(test, 0, -62, 5, 1); if (!ret) { return RESULT_FAIL; } return RESULT_OK; } /* read from negative sector num start + sectnum > 0, and start + setnum < 0 */ static int nand_test_negstart_read(struct nand_test_card *test) { int ret; /* start + sectnum > 0, start < 0 */ ret = nand_test_simple_transfer(test, 0, -1, 3, 0); if (!ret) { return RESULT_FAIL; } printk(NAND_TEST "start + sectnum > 0 pass\n"); /* start + sectnum < 0 , start < 0 */ ret = nand_test_simple_transfer(test, 0, -90, 15, 0); if (!ret) { return RESULT_FAIL; } return RESULT_OK; } static int nand_test_beyond(struct nand_test_card *test, int write) { int ret; ret = nand_test_simple_transfer(test, 0, DiskSize - 3, 5, write); if (!ret) { return 1; } printk(NAND_TEST "DiskSize -3 , 5 pass\n"); ret = nand_test_simple_transfer(test, 0, DiskSize - 1, 2, write); if (!ret) { return 1; } printk(NAND_TEST "DiskSize -1 , 2 pass\n"); ret = nand_test_simple_transfer(test, 0, DiskSize, 3, write); if (!ret) { return 1; } printk(NAND_TEST "DiskSize , 3 pass\n"); ret = nand_test_simple_transfer(test, 0, DiskSize + 3, 0, write); if (!ret) { return 1; } printk(NAND_TEST "DiskSize + 3 , 0 pass\n"); ret = nand_test_simple_transfer(test, 0, DiskSize - 3, -2, write); if (!ret) { return 1; } printk(NAND_TEST "DiskSize - 3 , -2 pass\n"); return RESULT_OK; } static int nand_test_beyond_write(struct nand_test_card *test) { return nand_test_beyond(test, 1); } /* read from incorrect sector num such as -1, DiskSize(max sector num + 1), DiskSize +1 */ static int nand_test_beyond_read(struct nand_test_card *test) { return nand_test_beyond(test, 0); } /* write all sectors from sector num 0 to DiskSize - 1(max sector num )*/ static int nand_test_write_all_ascending(struct nand_test_card *test) { int ret; int i = 0; int j = 0; printk(KERN_INFO "DiskSize = %x\n", DiskSize); for (i = 0; i < DiskSize; i++) { /* write all sectors*/ ret = nand_test_simple_transfer(test, 0, i, test->sector_cnt, 1); if (ret) { printk(KERN_INFO "nand_test_write_all_ascending fail, sector num %d\n", i); return ret; } } /* start check */ printk(KERN_INFO "[nand test]:start check\n"); for (i = 0; i < DiskSize; i++) { memset(test->buffer, 0, test->sector_cnt * 512); /* clear buffer*/ ret = nand_test_simple_transfer(test, 0, i, test->sector_cnt, 0); /* read*/ if (ret) { return ret; } for (j = 0; j < test->sector_cnt * 512; j++) { /* verify*/ if (test->buffer[j] != 0xDF) { printk(KERN_INFO "nand_test_write_all_ascending, Ttest->buffer[j] = %d, i = %d\n", test->buffer[j], i); return RESULT_FAIL; } } } return RESULT_OK; } /* read all sectors from sector num 0 to DiskSize - 1(max sector num )*/ static int nand_test_read_all_ascending(struct nand_test_card *test) { int ret; int i = 0; int j = 0; /* before reading, write */ for (i = 0; i < DiskSize; i++) { ret = nand_test_simple_transfer(test, 0, i, test->sector_cnt, 1); /* write all sectors*/ if (ret) { printk(KERN_INFO "nand_test_read_all_ascending fail, sector num %d\n", i); return ret; } } /* finish write, start to read and check */ for (i = 0; i < DiskSize; i++) { if (i % 100000 == 0) { printk(KERN_INFO "[nand test]: sector num:%d\n", i); } memset(test->buffer, 0, test->sector_cnt * 512); /* clear buffer*/ ret = nand_test_simple_transfer(test, 0, i, test->sector_cnt, 0); /* read*/ if (ret) { return ret; } for (j = 0; j < test->sector_cnt * 512; j++) { if (test->buffer[j] != (j) % 256) { printk(KERN_INFO "nand_test_read_all_ascending fial! Ttest->buffer[j] = %d, i = %d\n", test->buffer[i], j); return RESULT_FAIL; } } } return RESULT_OK; } /* write all sectors from sector num DiskSize - 1(max sector num ) to 0 */ static int nand_test_write_all_descending(struct nand_test_card *test) { int ret; int i = 0; int j = 0; printk(KERN_INFO "nand_test: DiskSize = %x\n", DiskSize); for (i = DiskSize - 1; i >= 0; i--) { memset(test->buffer, i % 256, 512); if (i % 100000 == 0) { printk(KERN_INFO "[nand test]: sector num:%d\n", i); } ret = nand_test_simple_transfer(test, 0, i, test->sector_cnt, 1); /* write all sectors*/ if (ret) { printk(KERN_INFO "[nand_test]: nand_test_write_all_ascending fail, sector num %d\n", i); return ret; } } printk(KERN_INFO "[nand test]: check start\n"); for (i = DiskSize - 1; i >= 0; i--) { if (i % 100000 == 0) { printk(KERN_INFO "[nand test]: sector num:%d\n", i); } memset(test->buffer, 0, test->sector_cnt * 512); /* clear buffer*/ ret = nand_test_simple_transfer(test, 0, i, test->sector_cnt, 0); /* read*/ if (ret) { return ret; } for (j = 0; j < 512; j++) { /* verify*/ if (test->buffer[j] != i % 256) { printk(KERN_INFO "[nand_test]: nand_test_write_all_ascending, Ttest->buffer[j] = %d, i = %d\n", test->buffer[j], i); return RESULT_FAIL; } } } return RESULT_OK; } /* read all sectors from sector num DiskSize - 1(max sector num ) to 0 */ static int nand_test_read_all_descending(struct nand_test_card *test) { int ret; int i = 0; int j = 0; for (i = DiskSize - 1; i >= 0; i--) { memset(test->buffer, i % 256, 512); ret = nand_test_simple_transfer(test, 0, i, test->sector_cnt, 1); /* write all sectors*/ if (ret) { printk(KERN_INFO "[nand_test]: nand_test_read_all_ascending fail, sector num %d\n", i); return ret; } } printk(KERN_INFO "[nand test]: check start\n"); for (i = DiskSize - 1; i >= 0; i--) { if (i % 100000 == 0) { printk(KERN_INFO "[nand test]: sector num:%d\n", i); } memset(test->buffer, 0, test->sector_cnt * 512); /* clear buffer*/ ret = nand_test_simple_transfer(test, 0, i, test->sector_cnt, 0); /* read*/ if (ret) { return ret; } for (j = 0; j < test->sector_cnt * 512; j++) { /* verify data*/ if (test->buffer[j] != (i) % 256) { printk(KERN_INFO "[nand_test]:nand_test_read_all_ascending fial! Ttest->buffer[j] = %d, i = %d\n", test->buffer[j], i); return RESULT_FAIL; } } } return RESULT_OK; } /* write a sector for n times without verification to test stability */ static int nand_test_repeat_single_write(struct nand_test_card *test) { int ret; int i = 0; printk(NAND_TEST "DiskSize = %d\n", DiskSize); for (i = 0; i < REPEAT_TIMES * 1000; i++) { ret = nand_test_simple_transfer(test, 0, DiskSize / 7, test->sector_cnt, 1); if (ret) { return ret; } } return 0; } /* read a sector for n times with verification to test stability*/ static int nand_test_repeat_single_read(struct nand_test_card *test) { int ret; int i = 0; for (i = 0; i < REPEAT_TIMES * 30; i++) { ret = nand_test_simple_transfer(test, 0, DiskSize / 4 + 7, test->sector_cnt, 0); if (ret) { return ret; } } return 0; } /* write multi sectors for n times without verification to test stability*/ static int nand_test_repeat_multi_write(struct nand_test_card *test) { int ret; int i = 0; for (i = 0; i < 1100000; i++) { ret = nand_test_simple_transfer(test, 0, DiskSize / 2, test->sector_cnt, 1); if (ret) { return ret; } } return 0; } /* read multi sectors for n times without verification to test stability*/ static int nand_test_repeat_multi_read(struct nand_test_card *test) { int ret; int i = 0; for (i = 0; i < 9200000; i++) { ret = nand_test_simple_transfer(test, 0, DiskSize / 3, test->sector_cnt, 0); if (ret) { return ret; } } return 0; } /* random write one or more sectors*/ static int nand_test_random_write(struct nand_test_card *test) { int ret; ret = nand_test_simple_transfer(test, 0, 0, test->sector_cnt, 1); if (ret) { return ret; } ret = nand_test_simple_transfer(test, 0, DiskSize - 1, test->sector_cnt, 1); if (ret) { return ret; } ret = nand_test_simple_transfer(test, 0, DiskSize / 2, test->sector_cnt, 1); if (ret) { return ret; } return 0; } /* random read one or more sectors*/ static int nand_test_random_read(struct nand_test_card *test) { int ret; ret = nand_test_simple_transfer(test, 0, 0, test->sector_cnt, 0); if (ret) { return ret; } ret = nand_test_simple_transfer(test, 0, DiskSize - 1, test->sector_cnt, 0); if (ret) { return ret; } ret = nand_test_simple_transfer(test, 0, DiskSize / 2, test->sector_cnt, 0); if (ret) { return ret; } return 0; } /* clear r/w buffer to 0*/ static int nand_test_cleanup(struct nand_test_card *test) { memset(test->buffer, 0, 512 * (test->sector_cnt)); return 0; } /* test cases */ static const struct nand_test_case nand_test_cases[] = { { .name = "single sector write (no data verification)", .sectors_cnt = 1, .prepare = nand_test_prepare_write, .run = nand_test_single_write, .cleanup = nand_test_cleanup }, { .name = "single sector read (no data verification)", .sectors_cnt = 1, .prepare = nand_test_prepare_read, .run = nand_test_single_read, .cleanup = nand_test_cleanup }, { .name = "single sector write(verify data)", .sectors_cnt = 1, .prepare = nand_test_prepare_write, .run = nand_test_verify_write, .cleanup = nand_test_cleanup }, { .name = "single sector read(verify data)", .sectors_cnt = 1, .prepare = nand_test_prepare_read, .run = nand_test_verify_read, .cleanup = nand_test_cleanup }, /* multi read/write */ { .name = "multi sector read(2 sectors, verify)", .sectors_cnt = 2, .prepare = nand_test_prepare_read, .run = nand_test_multi_read, .cleanup = nand_test_cleanup }, { .name = "multi sector read(3 sectors, verify)", .sectors_cnt = 3, .prepare = nand_test_prepare_read, .run = nand_test_multi_read, .cleanup = nand_test_cleanup }, { .name = "multi sector read(8 sectors, verify)", .sectors_cnt = 8, .prepare = nand_test_prepare_read, .run = nand_test_multi_read, .cleanup = nand_test_cleanup }, { .name = "multi sector read(18 sectors, verify)", .sectors_cnt = 18, .prepare = nand_test_prepare_read, .run = nand_test_multi_read, .cleanup = nand_test_cleanup }, { .name = "multi sector read(53 sectors, verify)", .sectors_cnt = 53, .prepare = nand_test_prepare_read, .run = nand_test_multi_read, .cleanup = nand_test_cleanup }, { .name = "multi sector write(2 sectors ,verify)", .sectors_cnt = 2, .prepare = nand_test_prepare_write, .run = nand_test_multi_write, .cleanup = nand_test_cleanup }, { .name = "multi sector write(5 sectors ,verify)", .sectors_cnt = 5, .prepare = nand_test_prepare_write, .run = nand_test_multi_write, .cleanup = nand_test_cleanup, }, { .name = "multi sector write(12 sectors ,verify)", .sectors_cnt = 12, .prepare = nand_test_prepare_write, .run = nand_test_multi_write, .cleanup = nand_test_cleanup, }, { .name = "multi sector write(15 sectors ,verify)", .sectors_cnt = 15, .prepare = nand_test_prepare_write, .run = nand_test_multi_write, .cleanup = nand_test_cleanup, }, { .name = "multi sector write(26 sectors ,verify)", .sectors_cnt = 26, .prepare = nand_test_prepare_write, .run = nand_test_multi_write, .cleanup = nand_test_cleanup, }, { .name = "multi sector write(93 sectors ,verify)", .sectors_cnt = 93, .prepare = nand_test_prepare_write, .run = nand_test_multi_write, .cleanup = nand_test_cleanup, }, /*align test */ { .name = "align write(1 sector ,verify)", .sectors_cnt = 1, .prepare = nand_test_prepare_write, .run = nand_test_align_write, .cleanup = nand_test_cleanup, }, { .name = "align read(1 sector ,verify)", .sectors_cnt = 1, .prepare = nand_test_prepare_read, .run = nand_test_align_read, .cleanup = nand_test_cleanup, }, /* stability test */ { .name = "weird write(negative start)", /* 18*/ .sectors_cnt = 10, .prepare = nand_test_prepare_write, .run = nand_test_negstart_write, .cleanup = nand_test_cleanup, }, { .name = "weid read(nagative satrt)", .sectors_cnt = 10, .prepare = nand_test_prepare_read, .run = nand_test_negstart_read, .cleanup = nand_test_cleanup, }, { .name = "weird write(beyond start)", /* 20*/ .sectors_cnt = 10, .prepare = nand_test_prepare_write, .run = nand_test_beyond_write, .cleanup = nand_test_cleanup, }, { .name = "weid read(bayond start)", .sectors_cnt = 10, .prepare = nand_test_prepare_read, .run = nand_test_beyond_read, .cleanup = nand_test_cleanup, }, { /* 22*/ .name = "write all ascending", .sectors_cnt = 1, .prepare = nand_test_prepare_write, .run = nand_test_write_all_ascending, .cleanup = nand_test_cleanup, }, { .name = "read all ascending", .sectors_cnt = 1, .prepare = nand_test_prepare_read, .run = nand_test_read_all_ascending, .cleanup = nand_test_cleanup, }, { .name = "write all descending", .sectors_cnt = 1, .prepare = nand_test_prepare_write, .run = nand_test_write_all_descending, .cleanup = nand_test_cleanup, }, { .name = "read all descending", .sectors_cnt = 1, .prepare = nand_test_prepare_read, .run = nand_test_read_all_descending, .cleanup = nand_test_cleanup, }, { /* 26*/ .name = " repeat write (no data verification) ", .sectors_cnt = 1, .prepare = nand_test_prepare_write, .run = nand_test_repeat_single_write, .cleanup = nand_test_cleanup, }, { /* 27*/ .name = " repeat read (no data verification) ", .sectors_cnt = 1, .prepare = nand_test_prepare_read, .run = nand_test_repeat_single_read, .cleanup = nand_test_cleanup, }, { /* 28*/ .name = " repeat multi write (no data verification)", .sectors_cnt = 43, .prepare = nand_test_prepare_write, .run = nand_test_repeat_multi_write, .cleanup = nand_test_cleanup, }, { /* 29*/ .name = " repeat multi read (no data verification)", .sectors_cnt = 81, .prepare = nand_test_prepare_read, .run = nand_test_repeat_multi_read, .cleanup = nand_test_cleanup, }, { /* 30*/ .name = " random write (no data verification)", .sectors_cnt = 1, .prepare = nand_test_prepare_write, .run = nand_test_random_write, .cleanup = nand_test_cleanup, }, { /* 31*/ .name = " random read (no data verification)", .sectors_cnt = 1, .prepare = nand_test_prepare_read, .run = nand_test_random_read, .cleanup = nand_test_cleanup, }, { /* 32*/ .name = " pwm test (no data verification)", .sectors_cnt = 1, .prepare = nand_test_prepare_pwm, /*.run = nand_test_pwm,*/ .cleanup = nand_test_cleanup, }, }; static DEFINE_MUTEX(nand_test_lock); /* run test cases*/ static void nand_test_run(struct nand_test_card *test, int testcase) { int i, ret; printk(KERN_INFO "[nand_test]: Starting tests of nand\n"); for (i = 0; i < ARRAY_SIZE(nand_test_cases); i++) { if (testcase && ((i + 1) != testcase)) { continue; } printk(KERN_INFO "[nand_test]: Test case %d. %s...\n", i + 1, nand_test_cases[i].name); if (nand_test_cases[i].prepare) { ret = nand_test_cases[i].prepare(test, nand_test_cases[i]. sectors_cnt); if (ret) { printk(KERN_INFO "[nand_test]: Result: Prepare stage failed! (%d)\n", ret); continue; } } ret = nand_test_cases[i].run(test); switch (ret) { case RESULT_OK: printk(KERN_INFO "[nand_test]: Result: OK\n"); break; case RESULT_FAIL: printk(KERN_INFO "[nand_test]:Result: FAILED\n"); break; /* case RESULT_UNSUP_HOST: */ /*grace del*/ /* printk(KERN_INFO "%s: Result: UNSUPPORTED "*/ /* "(by host)\n",*/ /* mmc_hostname(test->card->host));*/ /* break;*/ /* case RESULT_UNSUP_CARD:*/ /* printk(KERN_INFO "%s: Result: UNSUPPORTED "*/ /* "(by card)\n",*/ /* mmc_hostname(test->card->host));*/ /* break;*/ default: printk(KERN_INFO "[nand_test]:Result: ERROR (%d)\n", ret); } if (nand_test_cases[i].cleanup) { ret = nand_test_cases[i].cleanup(test); if (ret) { printk(KERN_INFO "[nand_test]:Warning: Cleanup" "stage failed! (%d)\n", ret); } } } /*mmc_release_host(test->card->host);*/ printk(KERN_INFO "[nand_test]: Nand tests completed.\n"); } /* do nothing */ static ssize_t nand_test_show(struct kobject *kobject, struct attribute *attr, char *buf) { return 0; } /* receive testcase num from echo command */ static ssize_t nand_test_store(struct kobject *kobject, struct attribute *attr, const char *buf, size_t count) { struct nand_test_card *test; int testcase; testcase = simple_strtol(buf, NULL, 10); /* get test case number >> grace*/ test = kzalloc(sizeof(struct nand_test_card), GFP_KERNEL); if (!test) { return -ENOMEM; } test->buffer = kzalloc(BUFFER_SIZE, GFP_KERNEL); /* alloc buffer for r/w*/ test->scratch = kzalloc(BUFFER_SIZE, GFP_KERNEL); /* not used now*/ if (test->buffer && test->scratch) { mutex_lock(&nand_test_lock); nand_test_run(test, testcase); /* run test cases*/ mutex_unlock(&nand_test_lock); } kfree(test->buffer); kfree(test->scratch); kfree(test); return count; } struct kobject kobj; /* if nand driver is not inited , functions below will be used */ #ifdef INIT_NAND_IN_TESTDRIVER static void set_nand_pio(void) { __u32 cfg0; __u32 cfg1; __u32 cfg2; void *gpio_base; /*modify for f20*/ gpio_base = (void *)SW_VA_PORTC_IO_BASE; cfg0 = *(volatile __u32 *)(gpio_base + 0x48); cfg1 = *(volatile __u32 *)(gpio_base + 0x4c); cfg2 = *(volatile __u32 *)(gpio_base + 0x50); /*set PIOC for nand */ cfg0 &= 0x0; cfg0 |= 0x22222222; cfg1 &= 0x0; cfg1 |= 0x22222222; cfg2 &= 0x0; cfg2 |= 0x22222222; *(volatile __u32 *)(gpio_base + 0x48) = cfg0; *(volatile __u32 *)(gpio_base + 0x4c) = cfg1; *(volatile __u32 *)(gpio_base + 0x50) = cfg2; /*iounmap(gpio_base);*/ } static __u32 get_cmu_clk(void) { __u32 reg_val; __u32 div_p, factor_n; __u32 factor_k, factor_m; __u32 clock; reg_val = *(volatile unsigned int *)(0xf1c20000 + 0x20); div_p = (reg_val >> 16) & 0x3; factor_n = (reg_val >> 8) & 0x1f; factor_k = ((reg_val >> 4) & 0x3) + 1; factor_m = ((reg_val >> 0) & 0x3) + 1; clock = 24 * factor_n * factor_k / div_p / factor_m; printk("cmu_clk is %d \n", clock); return clock; } static void set_nand_clock(__u32 nand_max_clock) { __u32 edo_clk, cmu_clk; __u32 cfg; __u32 nand_clk_divid_ratio; /*open ahb nand clk */ cfg = *(volatile __u32 *)(0xf1c20000 + 0x60); cfg |= (0x1 << 13); *(volatile __u32 *)(0xf1c20000 + 0x60) = cfg; /*set nand clock */ /*edo_clk = (nand_max_clock > 20)?(nand_max_clock-10):nand_max_clock;*/ edo_clk = nand_max_clock * 2; cmu_clk = get_cmu_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 = *(volatile __u32 *)(0xf1c20000 + 0x80); /*gate on nand clock */ cfg |= (1U << 31); /*take cmu pll as nand src block */ cfg &= ~(0x3 << 24); cfg |= (0x2 << 24); /*set divn = 0*/ cfg &= ~(0x03 << 12); /*set ratio */ cfg &= ~(0x0f << 0); cfg |= (nand_clk_divid_ratio & 0xf) << 0; *(volatile __u32 *)(0xf1c20000 + 0x80) = cfg; printk("nand clk init end \n"); printk("offset 0xc: 0x%x \n", *(volatile __u32 *)(0xf1c20000 + 0x60)); printk("offset 0x14: 0x%x \n", *(volatile __u32 *)(0xf1c20000 + 0x80)); } #endif static int __init nand_test_init(void) { int ret; #ifdef INIT_NAND_IN_TESTDRIVER __u32 cmu_clk; #endif printk("[nand_test]:nand_test_init test init.\n"); ret = kobject_init_and_add(&kobj, &ktype, NULL, "nand"); if (ret != 0) { return ret; } #ifdef INIT_NAND_IN_TESTDRIVER /* init nand resource */ printk("[nand_test]:init nand resource \n"); /*set nand clk*/ set_nand_clock(20); /*set nand pio*/ set_nand_pio(); clear_NAND_ZI(); printk ("/*********************************************************/ \n"); printk("[nand_test]: init nand block layer start \n"); printk ("/*********************************************************/ \n"); ret = PHY_Init(); if (ret) { PHY_Exit(); return -1; } ret = SCN_AnalyzeNandSystem(); if (ret < 0) { return ret; } ret = PHY_ChangeMode(1); if (ret < 0) { return ret; } ret = FMT_Init(); if (ret < 0) { return ret; } ret = FMT_FormatNand(); if (ret < 0) { return ret; } FMT_Exit(); /*init logic layer */ ret = LML_Init(); if (ret < 0) { return ret; } #ifdef NAND_CACHE_RW NAND_CacheOpen(); #endif #endif return 0; /* init success*/ } static void __exit nand_test_exit(void) { printk("[nand_test]:nand test exit.\n"); kobject_del(&kobj); #ifdef INIT_NAND_IN_TESTDRIVER LML_FlushPageCache(); BMM_WriteBackAllMapTbl(); LML_Exit(); FMT_Exit(); PHY_Exit(); #ifdef NAND_CACHE_RW NAND_CacheOpen(); #endif #endif return; } module_init(nand_test_init); module_exit(nand_test_exit); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Nand test driver"); MODULE_AUTHOR("Grace Miao"); #endif