SmartAudio/lichee/linux-4.9/modules/nand/sun8iw15p1/nfd/nand_test_dev.c

492 lines
12 KiB
C

/*
* 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, &param0, &param1, &param2);
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;
}