515 lines
13 KiB
C
Executable File
515 lines
13 KiB
C
Executable File
|
|
#define _NAND_TEST_DEV_C_
|
|
|
|
#include "nand_type.h"
|
|
#include "nand_blk.h"
|
|
#include "nand_dev.h"
|
|
|
|
struct nand_kobject *s_nand_kobj2;
|
|
extern struct _nand_info *p_nand_info;
|
|
extern struct kobj_type ktype;
|
|
extern struct _nftl_blk nftl_blk_head;
|
|
|
|
extern struct _nand_partition *build_nand_partition(struct _nand_phy_partition *phy_partition);
|
|
extern void add_nftl_blk_list(struct _nftl_blk *head, struct _nftl_blk *nftl_blk);
|
|
extern uint16 get_partitionNO(struct _nand_phy_partition *phy_partition);
|
|
extern struct _nand_phy_partition *get_head_phy_partition_from_nand_info(struct _nand_info *nand_info);
|
|
extern struct _nand_phy_partition *get_next_phy_partition(struct _nand_phy_partition *phy_partition);
|
|
|
|
extern uint32 nand_wait_rb_mode(void);
|
|
extern uint32 nand_wait_dma_mode(void);
|
|
extern void do_nand_interrupt(unsigned int no);
|
|
|
|
extern int nand_ftl_exit(void);
|
|
extern unsigned int nftl_read(unsigned int start_sector, unsigned int len, unsigned char *buf);
|
|
extern unsigned int nftl_write(unsigned int start_sector, unsigned int len, unsigned char *buf);
|
|
extern unsigned int nftl_flush_write_cache(void);
|
|
extern int nand_ftl_exit(void);
|
|
|
|
extern uint32 gc_all(struct _nftl_zone *zone);
|
|
extern uint32 gc_one(struct _nftl_zone *zone);
|
|
extern void print_nftl_zone(struct _nftl_zone *zone);
|
|
extern void print_free_list(struct _nftl_zone *zone);
|
|
extern void print_block_invalid_list(struct _nftl_zone *zone);
|
|
extern uint32 nftl_set_zone_test(void *_zone, uint32 num);
|
|
|
|
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);
|
|
|
|
int add_nand_for_test(struct _nand_phy_partition *phy_partition);
|
|
int build_all_nftl(struct _nand_info *nand_info);
|
|
|
|
#define NFTL_SCHEDULE_TIMEOUT (HZ >> 2)
|
|
#define NFTL_FLUSH_DATA_TIME 1
|
|
|
|
struct attribute prompt_attr = {
|
|
.name = "nand_debug",
|
|
.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
|
|
};
|
|
|
|
/*****************************************************************************
|
|
*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) {
|
|
printk("nftl_thread garbage_collect error!\n");
|
|
}
|
|
|
|
if (do_prio_gc(nftl_blk->nftl_zone) != 0) {
|
|
printk("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) {
|
|
printk("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) {
|
|
printk("nftl_blk kmalloc fail!\n");
|
|
return 1;
|
|
}
|
|
nftl_blk->nand = build_nand_partition(phy_partition);
|
|
|
|
if (nftl_initialize(nftl_blk, PartitionNO)) {
|
|
printk("nftl_initialize failed\n");
|
|
return 1;
|
|
}
|
|
|
|
nftl_blk->blk_lock = kmalloc(sizeof(struct mutex), GFP_KERNEL);
|
|
if (!nftl_blk->blk_lock) {
|
|
printk("blk_lock kmalloc fail!\n");
|
|
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)) {
|
|
printk("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) {
|
|
printk("nand_kobj kzalloc fail!\n");
|
|
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) {
|
|
printk("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) {
|
|
printk("nand_driver_test_init fail1!\n");
|
|
return ret;
|
|
}
|
|
|
|
p_nand_info2 = NandHwInit();
|
|
if (p_nand_info2 == NULL) {
|
|
printk("nand_driver_test_init fail2!\n");
|
|
NandHwExit();
|
|
return 1;
|
|
}
|
|
|
|
ret = nand_info_init(p_nand_info2, 0, 8, NULL);
|
|
if (ret != 0) {
|
|
printk("nand_driver_test_init fail3!\n");
|
|
return ret;
|
|
}
|
|
|
|
ret = build_all_nftl(p_nand_info2);
|
|
if (ret != 0) {
|
|
printk("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) {
|
|
printk("[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) {
|
|
printk("[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)
|
|
{
|
|
printk("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);
|
|
printk("argnum=%i, cmd=%s, param0=%u, param1=%u, param2=%u\n", argnum,
|
|
cmd, param0, param1, param2);
|
|
|
|
if (-1 == argnum) {
|
|
printk("cmd format err!");
|
|
goto NAND_TEST_STORE_EXIT;
|
|
}
|
|
|
|
if (strcmp(cmd, "help") == 0) {
|
|
printk("nand debug cmd:\n");
|
|
printk(" help\n");
|
|
} else if (strcmp(cmd, "flush") == 0) {
|
|
printk("nand debug cmd:\n");
|
|
printk(" 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) {
|
|
printk("nand debug cmd:\n");
|
|
printk(" 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) {
|
|
printk("nand debug cmd:\n");
|
|
printk(" 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) {
|
|
printk("nand debug cmd:\n");
|
|
printk(" 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) {
|
|
printk("nand debug cmd:\n");
|
|
printk(" 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 {
|
|
printk("err, nand debug undefined cmd: %s\n", cmd);
|
|
}
|
|
|
|
NAND_TEST_STORE_EXIT:
|
|
return count;
|
|
}
|