/* * nand_test_dev.c * * Copyright (C) 2016 Allwinner. * * * This file is licensed under the terms of the GNU General Public * License version 2. This program is licensed "as is" without any * warranty of any kind, whether express or implied. */ #define _NAND_TEST_DEV_C_ #include "nand_type.h" #include "nand_blk.h" #include "nand_dev.h" struct nand_kobject *s_nand_kobj2; #define NFTL_SCHEDULE_TIMEOUT (HZ >> 2) #define NFTL_FLUSH_DATA_TIME 1 struct attribute const prompt_attr = { .name = "nand_debug", .mode = S_IRWXUGO }; static struct attribute *def_attrs[] = { &prompt_attr, NULL }; const 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 }; /***************************************************************************** *Name : *Description : *Parameter : *Return : *Note : *****************************************************************************/ int nftl_test_thread(void *arg) { struct _nftl_blk *nftl_blk = arg; unsigned long time; nftl_blk->time_flush = NFTL_FLUSH_DATA_TIME * HZ; nftl_blk->time_flush = HZ; while (!kthread_should_stop()) { mutex_lock(nftl_blk->blk_lock); if (nftl_get_zone_write_cache_nums(nftl_blk->nftl_zone) > 32) { time = jiffies; nftl_blk->flush_write_cache(nftl_blk, 8); } else { time = jiffies; if (time_after (time, nftl_blk->time + nftl_blk->time_flush + HZ)) { nftl_blk->flush_write_cache(nftl_blk, 2); } } if (garbage_collect(nftl_blk->nftl_zone) != 0) nand_dbg_err("nftl_thread garbage_collect error!\n"); if (do_prio_gc(nftl_blk->nftl_zone) != 0) nand_dbg_err("nftl_thread do_prio_gc error!\n"); mutex_unlock(nftl_blk->blk_lock); set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(NFTL_SCHEDULE_TIMEOUT); } nftl_blk->nftl_thread = (void *)NULL; return 0; } /***************************************************************************** *Name : *Description : *Parameter : *Return : *Note : *****************************************************************************/ #ifdef __LINUX_NAND_SUPPORT_INT__ spinlock_t nand_int_test_lock; static irqreturn_t nand_test_interrupt(int irq, void *channel) { unsigned int no; unsigned long iflags; __u32 nand_index; spin_lock_irqsave(&nand_int_test_lock, iflags); no = *((unsigned int *)channel); do_nand_interrupt(no); spin_unlock_irqrestore(&nand_int_test_lock, iflags); return IRQ_HANDLED; } #endif /***************************************************************************** *Name : *Description : *Parameter : *Return : *Note : *****************************************************************************/ int nand_interrupt_init(void) { #ifdef __LINUX_NAND_SUPPORT_INT__ static unsigned int channel0; spin_lock_init(&nand_int_test_lock); if ((nand_wait_rb_mode() != 0) || (nand_wait_dma_mode() != 0)) { if (request_irq (SUNXI_IRQ_NAND, nand_test_interrupt, IRQF_DISABLED, dev_name, &channel0)) { nand_dbg_err ("nand interrupte ch0 irqno: %d register error\n", SUNXI_IRQ_NAND); return -EAGAIN; } } #endif return 0; } /***************************************************************************** *Name : *Description : *Parameter : *Return : *Note : *****************************************************************************/ int build_all_nftl(struct _nand_info *nand_info) { struct _nand_phy_partition *phy_partition; phy_partition = get_head_phy_partition_from_nand_info(nand_info); nftl_blk_head.nftl_blk_next = NULL; while (phy_partition != NULL) { if (add_nand_for_test(phy_partition) != 0) { nand_dbg_err("build_all_nftl fail!\n"); return 1; } phy_partition = get_next_phy_partition(phy_partition); } return 0; } /***************************************************************************** *Name : *Description : *Parameter : *Return : *Note : *****************************************************************************/ int add_nand_for_test(struct _nand_phy_partition *phy_partition) { int i; __u32 cur_offset = 0; struct _nftl_blk *nftl_blk; struct _nand_dev *nand_dev; struct _nand_disk *disk; struct _nand_disk *head_disk; uint16 PartitionNO; PartitionNO = get_partitionNO(phy_partition); nftl_blk = kmalloc(sizeof(struct _nftl_blk), GFP_KERNEL); if (!nftl_blk) return 1; nftl_blk->nand = build_nand_partition(phy_partition); if (nftl_initialize(nftl_blk, PartitionNO)) { nand_dbg_err("nftl_initialize failed\n"); return 1; } nftl_blk->blk_lock = kmalloc(sizeof(struct mutex), GFP_KERNEL); if (!nftl_blk->blk_lock) return 1; mutex_init(nftl_blk->blk_lock); nftl_blk->nftl_thread = kthread_run(nftl_test_thread, nftl_blk, "%sd", "nftl"); if (IS_ERR(nftl_blk->nftl_thread)) { nand_dbg_err("nftl_thread kthread_run fail!\n"); return 1; } add_nftl_blk_list(&nftl_blk_head, nftl_blk); s_nand_kobj2 = kzalloc(sizeof(struct nand_kobject), GFP_KERNEL); if (!s_nand_kobj2) return 1; s_nand_kobj2->nftl_blk = nftl_blk; if (kobject_init_and_add (&s_nand_kobj2->kobj, &ktype, NULL, "nand_driver%d", PartitionNO) != 0) { nand_dbg_err("init nand sysfs fail!\n"); return 1; } return 0; } /***************************************************************************** *Name : *Description : *Parameter : *Return : *Note : *****************************************************************************/ int nand_driver_test_init(void) { int ret = 0; struct _nand_info *p_nand_info2; ret = nand_interrupt_init(); if (ret != 0) { nand_dbg_err("nand_driver_test_init fail1!\n"); return ret; } p_nand_info2 = NandHwInit(); if (p_nand_info2 == NULL) { nand_dbg_err("nand_driver_test_init fail2!\n"); NandHwExit(); return 1; } ret = nand_info_init(p_nand_info2, 0, 8, NULL); if (ret != 0) { nand_dbg_err("nand_driver_test_init fail3!\n"); return ret; } ret = build_all_nftl(p_nand_info2); if (ret != 0) { nand_dbg_err("nand_driver_test_init fail4!\n"); nand_ftl_exit(); return ret; } return 0; } /***************************************************************************** *Name : *Description : *Parameter : *Return : *Note : *****************************************************************************/ int nand_driver_test_exit(void) { nftl_flush_write_cache(); nand_ftl_exit(); NandHwExit(); return 0; } /***************************************************************************** *Name : *Description : *Parameter : *Return : *Note : *****************************************************************************/ int nftl_test_read(uint32 start_sector, uint32 len, unsigned char *buf) { int ret = 0; struct _nftl_blk *nftl_blk; nftl_blk = nftl_blk_head.nftl_blk_next; if (len == 0) return 0; while (start_sector >= nftl_blk->nftl_logic_size) { start_sector -= nftl_blk->nftl_logic_size; nftl_blk = nftl_blk->nftl_blk_next; if (nftl_blk == NULL) { nand_dbg_err("[NE]parameter error %d,%d !\n", start_sector, len); return 1; } } mutex_lock(nftl_blk->blk_lock); ret = nftl_blk->read_data(nftl_blk, start_sector, len, buf); mutex_unlock(nftl_blk->blk_lock); return ret; } /***************************************************************************** *Name : *Description : *Parameter : *Return : *Note : *****************************************************************************/ uint32 nftl_test_write(uint32 start_sector, uint32 len, unsigned char *buf) { int ret = 0; struct _nftl_blk *nftl_blk; nftl_blk = nftl_blk_head.nftl_blk_next; if (len == 0) return 0; while (start_sector >= nftl_blk->nftl_logic_size) { start_sector -= nftl_blk->nftl_logic_size; nftl_blk = nftl_blk->nftl_blk_next; if (nftl_blk == NULL) { nand_dbg_err("[NE]parameter error %d,%d !\n", start_sector, len); return 1; } } mutex_lock(nftl_blk->blk_lock); ret = nftl_blk->write_data(nftl_blk, start_sector, len, buf); mutex_unlock(nftl_blk->blk_lock); return ret; } /***************************************************************************** *Name : *Description : *Parameter : *Return : *Note : *****************************************************************************/ uint32 nftl_test_flush_write_cache(void) { struct _nftl_blk *nftl_blk; nftl_blk = nftl_blk_head.nftl_blk_next; while (nftl_blk != NULL) { mutex_lock(nftl_blk->blk_lock); nftl_blk->flush_write_cache(nftl_blk, 0xffff); mutex_unlock(nftl_blk->blk_lock); nftl_blk = nftl_blk->nftl_blk_next; } return 0; } /***************************************************************************** *Name : *Description : *Parameter : *Return : *Note : *****************************************************************************/ void obj_test_release(struct kobject *kobject) { nand_dbg_err("release"); } /***************************************************************************** *Name : *Description : *Parameter : *Return : *Note : *****************************************************************************/ static ssize_t nand_test_show(struct kobject *kobject, struct attribute *attr, char *buf) { ssize_t count = 0; struct nand_kobject *nand_kobj; nand_kobj = (struct nand_kobject *)kobject; print_nftl_zone(nand_kobj->nftl_blk->nftl_zone); return count; } /***************************************************************************** *Name : *Description :receive testcase num from echo command *Parameter : *Return : *Note : *****************************************************************************/ static ssize_t nand_test_store(struct kobject *kobject, struct attribute *attr, const char *buf, size_t count) { int ret; int argnum = 0; char cmd[32] = { 0 }; unsigned int param0 = 0; unsigned int param1 = 0; unsigned int param2 = 0; struct nand_kobject *nand_kobj; nand_kobj = (struct nand_kobject *)kobject; argnum = sscanf(buf, "%s %u %u %u ", cmd, ¶m0, ¶m1, ¶m2); nand_dbg_err("argnum=%i, cmd=%s, param0=%u, param1=%u, param2=%u\n", argnum, cmd, param0, param1, param2); if (-1 == argnum) { nand_dbg_err("cmd format err!"); goto NAND_TEST_STORE_EXIT; } if (strcmp(cmd, "help") == 0) { nand_dbg_err("nand debug cmd:\n"); nand_dbg_err(" help\n"); } else if (strcmp(cmd, "flush") == 0) { nand_dbg_err("nand debug cmd:\n"); nand_dbg_err(" flush\n"); mutex_lock(nand_kobj->nftl_blk->blk_lock); ret = nand_kobj->nftl_blk->flush_write_cache(nand_kobj->nftl_blk, param0); mutex_unlock(nand_kobj->nftl_blk->blk_lock); goto NAND_TEST_STORE_EXIT; } else if (strcmp(cmd, "gcall") == 0) { nand_dbg_err("nand debug cmd:\n"); nand_dbg_err(" gcall\n"); mutex_lock(nand_kobj->nftl_blk->blk_lock); ret = gc_all(nand_kobj->nftl_blk->nftl_zone); mutex_unlock(nand_kobj->nftl_blk->blk_lock); goto NAND_TEST_STORE_EXIT; } else if (strcmp(cmd, "gcone") == 0) { nand_dbg_err("nand debug cmd:\n"); nand_dbg_err(" gcone\n"); mutex_lock(nand_kobj->nftl_blk->blk_lock); ret = gc_one(nand_kobj->nftl_blk->nftl_zone); mutex_unlock(nand_kobj->nftl_blk->blk_lock); goto NAND_TEST_STORE_EXIT; } else if (strcmp(cmd, "test") == 0) { nand_dbg_err("nand debug cmd:\n"); nand_dbg_err(" test\n"); mutex_lock(nand_kobj->nftl_blk->blk_lock); ret = nftl_set_zone_test((void *)nand_kobj->nftl_blk->nftl_zone, param0); mutex_unlock(nand_kobj->nftl_blk->blk_lock); goto NAND_TEST_STORE_EXIT; } else if (strcmp(cmd, "showall") == 0) { nand_dbg_err("nand debug cmd:\n"); nand_dbg_err(" show all\n"); print_free_list(nand_kobj->nftl_blk->nftl_zone); print_block_invalid_list(nand_kobj->nftl_blk->nftl_zone); print_nftl_zone(nand_kobj->nftl_blk->nftl_zone); goto NAND_TEST_STORE_EXIT; } else { nand_dbg_err("err, nand debug undefined cmd: %s\n", cmd); } NAND_TEST_STORE_EXIT: return count; }