sdk-hwV1.3/lichee/linux-4.9/modules/nand/sun8iw18p1/nfd/nand_class.c

461 lines
12 KiB
C

/*
* nand_class.c for SUNXI NAND .
*
* 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 BLK_INFO_MSG_ON
#include "nand_class.h"
struct nand_kobject *nand_debug_kobj;
static ssize_t nand_sysfs_show(struct kobject *kobject, struct attribute *attr,
char *buf);
static ssize_t nand_sysfs_store(struct kobject *kobject, struct attribute *attr,
const char *buf, size_t count);
static ssize_t nand_show_debug(struct _nftl_blk *nftl_blk, char *buf);
static ssize_t nand_store_debug(struct _nftl_blk *nftl_blk, const char *buf,
size_t count);
static ssize_t nand_show_arch(struct _nftl_blk *nftl_blk, char *buf);
static ssize_t nand_show_gcinfo(struct _nftl_blk *nftl_blk, char *buf);
static ssize_t nand_show_version(struct _nftl_blk *nftl_blk, char *buf);
static ssize_t nand_show_badblk(struct _nftl_blk *nftl_blk, char *buf);
static ssize_t nand_store_gcone(struct _nftl_blk *nftl_blk, const char *buf,
size_t count);
static ssize_t nand_store_gcall(struct _nftl_blk *nftl_blk, const char *buf,
size_t count);
static struct attribute attr_debug = {
.name = "nand_debug",
.mode = S_IRUGO | S_IWUSR | S_IWGRP,
};
static struct attribute attr_arch = {
.name = "arch",
.mode = S_IRUGO,
};
static struct attribute attr_gcinfo = {
.name = "gcinfo",
.mode = S_IRUGO,
};
static struct attribute attr_badblk = {
.name = "badblock",
.mode = S_IRUGO,
};
static struct attribute attr_gc_all = {
.name = "gc_all",
.mode = S_IWUSR | S_IWGRP,
};
static struct attribute attr_gc_one = {
.name = "gc_one",
.mode = S_IWUSR | S_IWGRP,
};
static struct attribute attr_version = {
.name = "version",
.mode = S_IRUGO,
};
static struct attribute *sysfs_attrs[] = {
&attr_debug,
&attr_arch,
&attr_gcinfo,
&attr_badblk,
&attr_gc_all,
&attr_gc_one,
&attr_version,
NULL,
};
static const struct sysfs_ops sysfs_ops = {
.show = nand_sysfs_show,
.store = nand_sysfs_store,
};
static struct kobj_type sysfs_type = {
.sysfs_ops = &sysfs_ops,
.default_attrs = sysfs_attrs,
};
int nand_debug_init(struct _nftl_blk *nftl_blk, int part_num)
{
int ret;
nand_debug_kobj = kzalloc(sizeof(struct nand_kobject), GFP_KERNEL);
if (!nand_debug_kobj) {
nand_dbg_err("kzalloc failed\n");
return -ENOMEM;
}
nand_debug_kobj->nftl_blk = nftl_blk;
ret = kobject_init_and_add(&nand_debug_kobj->kobj, &sysfs_type, NULL,
"nand_driver%d", part_num);
if (ret) {
nand_dbg_err("init nand sysfs fail!\n");
return ret;
}
return 0;
}
struct nand_attr_ops {
struct attribute *attr;
ssize_t (*show)(struct _nftl_blk *blk, char *buf);
ssize_t (*store)(struct _nftl_blk *blk, const char *buf, size_t cnt);
};
static struct nand_attr_ops attr_ops_array[] = {
{
.attr = &attr_debug,
.show = nand_show_debug,
.store = nand_store_debug,
},
{
.attr = &attr_arch,
.show = nand_show_arch,
},
{
.attr = &attr_gcinfo,
.show = nand_show_gcinfo,
},
{
.attr = &attr_version,
.show = nand_show_version,
},
{
.attr = &attr_badblk,
.show = nand_show_badblk,
},
{
.attr = &attr_gc_all,
.store = nand_store_gcall,
},
{
.attr = &attr_gc_one,
.store = nand_store_gcone,
},
};
static ssize_t nand_sysfs_show(struct kobject *kobj, struct attribute *attr,
char *buf)
{
int index;
struct nand_attr_ops *attr_ops;
struct _nftl_blk *nftl_blk = ((struct nand_kobject *)kobj)->nftl_blk;
ssize_t ret;
for (index = 0; index < ARRAY_SIZE(attr_ops_array); index++) {
attr_ops = &attr_ops_array[index];
if (attr == attr_ops->attr)
break;
}
if (unlikely(index == ARRAY_SIZE(attr_ops_array))) {
nand_dbg_err("not found attr_ops for %s\n", attr->name);
return -EINVAL;
}
if (attr_ops->show) {
mutex_lock(nftl_blk->blk_lock);
ret = attr_ops->show(nftl_blk, buf);
mutex_unlock(nftl_blk->blk_lock);
return ret;
}
return -EINVAL;
}
static ssize_t nand_sysfs_store(struct kobject *kobj, struct attribute *attr,
const char *buf, size_t count)
{
int index;
struct nand_attr_ops *attr_ops;
struct _nftl_blk *nftl_blk = ((struct nand_kobject *)kobj)->nftl_blk;
ssize_t ret;
for (index = 0; index < ARRAY_SIZE(attr_ops_array); index++) {
attr_ops = &attr_ops_array[index];
if (attr == attr_ops->attr)
break;
}
if (unlikely(index == ARRAY_SIZE(attr_ops_array))) {
nand_dbg_err("not found attr_ops for %s\n", attr->name);
return -EINVAL;
}
if (attr_ops->store) {
mutex_lock(nftl_blk->blk_lock);
ret = attr_ops->store(nftl_blk, buf, count);
mutex_unlock(nftl_blk->blk_lock);
return ret;
}
return -EINVAL;
}
static ssize_t nand_show_debug(struct _nftl_blk *nftl_blk, char *buf)
{
ssize_t ret = 0;
int tmp[4] = {0};
ret += PHY_GetArchInfo_Str(buf);
ret += sprintf(buf + ret, "BadBlkCnt: %d\n",
nftl_get_bad_block_cnt(nftl_blk));
NAND_Get_Version(&tmp[0], &tmp[1], &tmp[2], &tmp[3]);
ret += sprintf(buf + ret, "NandVersion: %x.%x %x %x\n",
tmp[0], tmp[1], tmp[2], tmp[3]);
return ret;
}
static ssize_t nand_store_debug(struct _nftl_blk *nftl_blk, const char *buf,
size_t count)
{
int ret, i;
int arg_num = 0;
char cmd[32] = { 0 };
unsigned int arg0_int = 0;
unsigned int arg1_int = 0;
unsigned int arg2_int = 0;
char arg3_str[16] = { 0 };
arg_num = sscanf(buf, "%31s %u %u %u %15s", cmd, &arg0_int, &arg1_int,
&arg2_int, arg3_str);
if (-1 == arg_num || 0 == arg_num) {
nand_dbg_err("cmd format err!");
return -EINVAL;
}
if (strcmp(cmd, "flush") == 0) {
if (2 != arg_num) {
nand_dbg_err("err: %s needs 1 argument\n", cmd);
return -EINVAL;
}
nand_dbg_err("NAND DEBUG: %s %u\n", cmd, arg0_int);
ret = nftl_blk->flush_write_cache(nftl_blk, arg0_int);
return count;
} else if (strcmp(cmd, "engcall") == 0) {
nand_dbg_err("NAND DEBUG: %s\n", cmd);
/* gc with invalid_page_count equal to (page_per_block / 2) */
ret = gc_all_enhance(nftl_blk->nftl_zone);
if (!ret)
return count;
else
return -EIO;
} else if (strcmp(cmd, "gcall") == 0) {
if (2 != arg_num) {
nand_dbg_err("err: %s needs 1 argument\n", cmd);
return -EINVAL;
}
nand_dbg_err("NAND DEBUG: %s %u\n", cmd, arg0_int);
ret = gc_all(nftl_blk->nftl_zone, arg0_int);
return count;
} else if (strcmp(cmd, "gcone") == 0) {
if (2 != arg_num) {
nand_dbg_err("err: %s needs 1 argument\n", cmd);
return -EINVAL;
}
nand_dbg_err("NAND DEBUG: %s %u\n", cmd, arg0_int);
ret = gc_one(nftl_blk->nftl_zone, arg0_int);
return count;
} else if (strcmp(cmd, "priogc") == 0) {
if (3 != arg_num) {
nand_dbg_err("err: %s needs 2 argument\n", cmd);
return -EINVAL;
}
nand_dbg_err("NAND DEBUG: %s %u %u\n", cmd, arg0_int, arg1_int);
ret = prio_gc_one(nftl_blk->nftl_zone, arg0_int,
arg1_int);
return count;
} else if (strcmp(cmd, "test") == 0) {
if (2 != arg_num) {
nand_dbg_err("err: %s needs 2 argument\n", cmd);
return -EINVAL;
}
nand_dbg_err("NAND DEBUG: %s %u\n", cmd, arg0_int);
ret = nftl_set_zone_test((void *)nftl_blk->nftl_zone,
arg0_int);
return count;
} else if (strcmp(cmd, "showall") == 0) {
nand_dbg_err("NAND DEBUG: %s\n", cmd);
print_free_list(nftl_blk->nftl_zone);
print_block_invalid_list(nftl_blk->nftl_zone);
return count;
} else if (strcmp(cmd, "showinfo") == 0) {
nand_dbg_err("NAND DEBUG: %s\n", cmd);
print_nftl_zone(nftl_blk->nftl_zone);
return count;
} else if (strcmp(cmd, "blkdebug") == 0) {
if (2 != arg_num) {
nand_dbg_err("err: %s needs 1 argument\n", cmd);
return -EINVAL;
}
nand_dbg_err("NAND DEBUG: %s %u\n", cmd, arg0_int);
debug_data = arg0_int;
return count;
} else if (strcmp(cmd, "smart") == 0) {
nand_dbg_err("NAND DEBUG: %s\n", cmd);
print_smart(nftl_blk->nftl_zone);
return count;
} else if (strcmp(cmd, "read1") == 0) {
if (4 != arg_num) {
nand_dbg_err("err: %s needs 3 argument\n", cmd);
return -EINVAL;
}
nand_dbg_err("NAND DEBUG: %s %u %u %u\n", cmd, arg0_int,
arg1_int, arg2_int);
nand_dbg_phy_read(arg0_int, arg1_int, arg2_int);
return count;
} else if (strcmp(cmd, "read2") == 0) {
if (3 != arg_num) {
nand_dbg_err(" %s need 2 argument\n", cmd);
return -EINVAL;
}
nand_dbg_err("NAND DEBUG: %s %u %u\n", cmd, arg0_int, arg1_int);
nand_dbg_zone_phy_read(nftl_blk->nftl_zone, arg0_int,
arg1_int);
return count;
} else if (strcmp(cmd, "erase1") == 0) {
if (3 != arg_num) {
nand_dbg_err("err: %s needs 2 argument\n", cmd);
return -EINVAL;
}
nand_dbg_err("NAND DEBUG: %s %u %u\n", cmd, arg0_int, arg1_int);
nand_dbg_phy_erase(arg0_int, arg1_int);
return count;
} else if (strcmp(cmd, "erase2") == 0) {
if (3 != arg_num) {
nand_dbg_err("err: %s needs 2 argument\n", cmd);
return -EINVAL;
}
nand_dbg_err("NAND DEBUG: %s %u %u\n", cmd, arg0_int, arg1_int);
nand_dbg_zone_erase(nftl_blk->nftl_zone, arg0_int,
arg1_int);
return count;
} else if (strcmp(cmd, "erase3") == 0) {
if (3 != arg_num) {
nand_dbg_err("err: %s needs 2 argument\n", cmd);
return -EINVAL;
}
nand_dbg_err("NAND DEBUG: %s %u %u\n", cmd, arg0_int, arg1_int);
nand_dbg_single_phy_erase(arg0_int, arg1_int);
return count;
} else if (strcmp(cmd, "write1") == 0) {
if (4 != arg_num) {
nand_dbg_err("err: %s needs 3 argument\n", cmd);
return -EINVAL;
}
nand_dbg_err("NAND DEBUG: %s %u %u %u\n", cmd, arg0_int,
arg1_int, arg2_int);
nand_dbg_phy_write(arg0_int, arg1_int, arg2_int);
return count;
} else if (strcmp(cmd, "write2") == 0) {
if (3 != arg_num) {
nand_dbg_err(" %s need 2 argument\n", cmd);
return -EINVAL;
}
nand_dbg_err("NAND DEBUG: %s %u %u\n", cmd, arg0_int, arg1_int);
nand_dbg_zone_phy_write(nftl_blk->nftl_zone, arg0_int,
arg1_int);
return count;
} else if (strcmp(cmd, "checktable") == 0) {
nand_dbg_err("NAND DEBUG: %s\n", cmd);
nand_check_table(nftl_blk->nftl_zone);
return count;
} else if (strcmp(cmd, "readdev") == 0) {
char *tempbuf;
if (5 != arg_num) {
nand_dbg_err("err: %s needs 4 argument\n", cmd);
return -EINVAL;
}
nand_dbg_err("NAND DEBUG: %s %u %u %u %s\n", cmd, arg0_int,
arg1_int, arg2_int, arg3_str);
if (arg1_int > 16) {
nand_dbg_err("arg1: max len is 16!\n");
return -EINVAL;
}
tempbuf = kmalloc(8192, GFP_KERNEL);
if (!tempbuf)
return PTR_ERR(tempbuf);
_dev_nand_read2(arg3_str, arg0_int, arg1_int, tempbuf);
for (i = 0; i < (arg1_int << 9); i += 4) {
nand_dbg_inf("%8x ", *((int *)&tempbuf[i]));
if (((i + 4) % 64) == 0)
nand_dbg_inf("\n");
}
kfree(tempbuf);
return count;
} else {
nand_dbg_err("NAND DEBUG: undefined cmd: %s\n", cmd);
return -EINVAL;
}
return count;
}
static ssize_t nand_show_arch(struct _nftl_blk *nftl_blk, char *buf)
{
return PHY_GetArchInfo_Str(buf);
}
static ssize_t nand_show_gcinfo(struct _nftl_blk *nftl_blk, char *buf)
{
return nftl_get_gc_info(nftl_blk->nftl_zone, buf, 4096);
}
static ssize_t nand_show_version(struct _nftl_blk *nftl_blk, char *buf)
{
int tmp[4] = {0};
NAND_Get_Version(&tmp[0], &tmp[1], &tmp[2], &tmp[3]);
return sprintf(buf, "%x.%x %x %x\n", tmp[0], tmp[1], tmp[2], tmp[3]);
}
static ssize_t nand_show_badblk(struct _nftl_blk *nftl_blk, char *buf)
{
return sprintf(buf, "cnt: %d\n", nftl_get_bad_block_cnt(nftl_blk));
}
static ssize_t nand_store_gcone(struct _nftl_blk *nftl_blk, const char *buf,
size_t count)
{
int ret;
int invalid_page_count;
ret = sscanf(buf, "%u", &invalid_page_count);
if (1 != ret) {
nand_dbg_err("invalid parameter %s\n", buf);
nand_dbg_err("please enter invalid_page_count for gcone\n");
return -EINVAL;
}
nand_dbg_inf("ctl: gcone %u\n", invalid_page_count);
ret = gc_one(nftl_blk->nftl_zone, invalid_page_count);
if (ret == 1)
return -EIO;
return count;
}
static ssize_t nand_store_gcall(struct _nftl_blk *nftl_blk, const char *buf,
size_t count)
{
int ret;
int invalid_page_count;
ret = sscanf(buf, "%u", &invalid_page_count);
if (1 != ret) {
nand_dbg_err("invalid parameter %s\n", buf);
nand_dbg_err("please enter invalid_page_count for gcall\n");
return -EINVAL;
}
nand_dbg_inf("ctl: gcall %u\n", invalid_page_count);
ret = gc_all(nftl_blk->nftl_zone, invalid_page_count);
if (ret == 1)
return -EIO;
return count;
}