/*************************************************************************** * 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_base.h" #include "nand_class.h" #include "nand_dev.h" #include "nand_panic.h" #include #include #include "../phy-nand/nand_nftl.h" #include "../phy-nand/nand.h" #include "../phy-nand/rawnand/controller/ndfc_base.h" #include #include #include #include #include #include #include #include #include "../phy-nand/nand_physic_interface.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); extern void get_nftl_version(char *version, char *patchlevel, char *sublevel, char *date, char *time); /* *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; //struct nand_kobject *nand_kobj; 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; } /**************************************************************************** *Name : *Description :receive testcase num from echo command *Parameter : *Return : *Note : *****************************************************************************/ 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; /*spinand debug achieve in sunxi-debug.c in driver/mtd/aw-spinand*/ /* 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_drv_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; *} */ /**************************************************************************** *Name : *Description : *Parameter : *Return : *Note : *****************************************************************************/ static ssize_t nand_show_arch(struct _nftl_blk *nftl_blk, char *buf) { /*spinand achieve in sunxi-debug.c in linux/mtd/aw-spinad*/ return 0; } static ssize_t nand_show_gcinfo(struct _nftl_blk *nftl_blk, char *buf) { return nftl_get_gc_info(nftl_blk->nftl_zone, buf, PAGE_SIZE); } static ssize_t nand_show_version(struct _nftl_blk *nftl_blk, char *buf) { int tmp[4] = {0}; nand_get_drv_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; } /* *extern void get_nftl_version(char *version, char *patchlevel, char *sublevel, * char *data, char *time); //awa1543 */ static ssize_t nand_nftl_version_show(struct device *dev, struct device_attribute *attr, char *buf) { ssize_t ret; char version[20] = {}; char patchlevel[20] = {}; char sublevel[20] = {}; char date[20] = {}; char time[20] = {}; get_nftl_version(version, patchlevel, sublevel, date, time); ret = sprintf(buf, "%s.%s.%s %s-%s\n", version, patchlevel, sublevel, date, time); return ret; } char *ecc_mode_to_string(unsigned int ecc_mode) { switch (ecc_mode) { case 0x0: return "BCH-16"; case 0x01: return "BCH-24"; case 0x02: return "BCH-28"; case 0x03: return "BCH-32"; case 0x04: return "BCH-40"; case 0x05: return "BCH-44"; case 0x06: return "BCH-48"; case 0x07: return "BCH-52"; case 0x08: return "BCH-56"; case 0x09: return "BCH-60"; case 0x0A: return "BCH-64"; case 0x0B: return "BCH-68"; case 0x0C: return "BCH-72"; case 0x0D: return "BCH-76"; case 0x0E: return "BCH-80"; } return NULL; } static ssize_t nand_arch_show(struct device *dev, struct device_attribute *attr, char *buf) { ssize_t ret; char *name = NULL; uint8_t id[8] = {0}; unsigned int die_cnt = 0; int pagesize = 0; int blocksize = 0; int diesize = 0; uint8_t freq = 0; unsigned int ecc_mode = 0; struct nand_chip_info *chip = aw_ndfc.nctri->nci; name = rawnand_get_chip_name(chip); ret = snprintf(buf, 100, "\033[47;31mmodel :\033[0m %s\n", name); rawnand_get_chip_id(chip, id, sizeof(id)); ret += snprintf(buf + ret, 100, "\033[47;31mnand id:\033[0m %02x %02x %02x %02x %02x %02x %02x %02x\n", chip->id[0], chip->id[1], chip->id[2], chip->id[3], chip->id[4], chip->id[5], chip->id[6], chip->id[7]); pagesize = rawnand_get_chip_page_size(chip, SECTOR); ret += snprintf(buf + ret, 100, "\033[47;31mpage size:\033[0m %d sectors\n", pagesize); blocksize = rawnand_get_chip_block_size(chip, PAGE); ret += snprintf(buf + ret, 100, "\033[47;31mblock size:\033[0m %d pages\n", blocksize); diesize = rawnand_get_chip_die_size(chip, BLOCK); ret += snprintf(buf + ret, 100, "\033[47;31mdie size:\033[0m %d blocks\n", diesize); die_cnt = rawnand_get_chip_die_cnt(chip); ret += snprintf(buf + ret, 100, "\033[47;31mdie cnt:\033[0m %d %s\n", die_cnt, die_cnt > 1 ? "dies" : "die"); freq = rawnand_get_chip_freq(chip); ret += snprintf(buf + ret, 100, "\033[47;31mfreqence :\033[0m %dM\n", freq); ecc_mode = rawnand_get_chip_ecc_mode(chip); ret += snprintf(buf + ret, 100, "\033[47;31mecc mode:\033[0m %s\n", ecc_mode_to_string(ecc_mode)); return ret; } static ssize_t nand_badblock_show(struct device *dev, struct device_attribute *attr, char *buf) { struct _nftl_blk *nftl_blk = aw_ndfc.nftl_blk; return sprintf(buf, "badblock cnt: %d\n", nftl_get_bad_block_cnt(nftl_blk)); } static ssize_t nand_gcinfo_show(struct device *dev, struct device_attribute *attr, char *buf) { struct _nftl_blk *nftl_blk = aw_ndfc.nftl_blk; return nftl_get_gc_info(nftl_blk->nftl_zone, buf, PAGE_SIZE); } static ssize_t nand_gcone_show(struct device *dev, struct device_attribute *attr, char *buf) { ssize_t ret; ret = snprintf(buf, PAGE_SIZE, "nand_gcone explain: use it can gc one block," "\nwhich invalid page count large than the input " "value;\nusage: echo xxx > nand_gcone\n"); return ret; } static ssize_t nand_gcone_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int ret; int invalid_page_count; struct _nftl_blk *nftl_blk = aw_ndfc.nftl_blk; 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; } if (invalid_page_count <= 0) { nand_dbg_inf("do nothing for gc %d\n", invalid_page_count); return count; } 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_gcall_show(struct device *dev, struct device_attribute *attr, char *buf) { ssize_t ret; ret = snprintf(buf, PAGE_SIZE, "nand_gcone explain: use it can gc all block," "\nwhich invalid page count large than the input " "value;\nusage: echo xxx > nand_gcall\n"); return ret; } static ssize_t nand_gcall_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int ret; int invalid_page_count; struct _nftl_blk *nftl_blk = aw_ndfc.nftl_blk; 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; } if (invalid_page_count <= 0) { nand_dbg_inf("do nothing for gc %d\n", invalid_page_count); return count; } 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; } static ssize_t nand_debug_show(struct device *dev, struct device_attribute *attr, char *buf) { ssize_t ret = 0; ret = snprintf(buf + ret, PAGE_SIZE - ret, "nand_debug explain:\n"); ret += snprintf(buf + ret, PAGE_SIZE - ret, "eg: echo flush > nand_debug\n" "flush cache\n\n"); ret += snprintf(buf + ret, PAGE_SIZE - ret, "eg: echo engcall > nand_debug\n" "gcc all invalid page count equal or large than " "page_per_blk / 2 for samll slc nand(128M)\n\n"); ret += snprintf(buf + ret, PAGE_SIZE - ret, "eg: echo gcall xxx > nand_debug\n" "gcc all block of invalid page count equal or large than xxx\n\n"); ret += snprintf(buf + ret, PAGE_SIZE - ret, "eg: echo gcone xxx > nand_debug\n" "gcc one block of invalid page count equal or large than xxx\n\n"); ret += snprintf(buf + ret, PAGE_SIZE - ret, "eg: echo priogc xxx_b xxx_p> nand_debug\n" "prioritize to gcc the xxx_p pages in xxx_b ,when next time gc\n"); ret += snprintf(buf + ret, PAGE_SIZE - ret, "eg: echo test 1 > nand_debug\n\n" "open some debug gate\n\n"); ret += snprintf(buf + ret, PAGE_SIZE - ret, "eg: echo showall > nand_debug\n" "output the free block and invalid block information\n\n" "after echo test 1 > nand_debug after echo test 1 > nand_debug\n\n"); ret += snprintf(buf + ret, PAGE_SIZE - ret, "eg: echo showinfo > nand_debug\n" "output the zone information,after echo test 1 > nand_debug\n\n"); ret += snprintf(buf + ret, PAGE_SIZE - ret, "eg: echo smart > nand_debug\n" "output the smart information,after echo test 1 > nand_debug\n\n"); return ret; } #define PANIC_TEST_MAX_SIZE (16 * 1024) static ssize_t nand_debug_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int ret, i; struct _nftl_blk *nftl_blk = aw_ndfc.nftl_blk; 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[32] = {0}; arg_num = sscanf(buf, "%31s %u %u %u %31s", 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; #if IS_ENABLED(CONFIG_PSTORE_BLK) } else if (strcmp(cmd, "panicwrite") == 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); char *dev = arg3_str; size_t sec_off = arg0_int; size_t sec_cnt = arg1_int; tempbuf = kmalloc(PANIC_TEST_MAX_SIZE, GFP_KERNEL); if (!tempbuf) return PTR_ERR(tempbuf); memset(tempbuf, arg2_int, PANIC_TEST_MAX_SIZE); if (sec_cnt > PANIC_TEST_MAX_SIZE / 512) sec_cnt = PANIC_TEST_MAX_SIZE / 512; critical_dev_nand_write(dev, sec_off, sec_cnt, tempbuf); kfree(tempbuf); return count; } else if (strcmp(cmd, "panicread") == 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); char *dev = arg3_str; size_t sec_off = arg0_int; size_t sec_cnt = arg1_int; tempbuf = kmalloc(PANIC_TEST_MAX_SIZE, GFP_KERNEL); if (!tempbuf) return PTR_ERR(tempbuf); memset(tempbuf, 0, PANIC_TEST_MAX_SIZE); if (sec_cnt > PANIC_TEST_MAX_SIZE / 512) sec_cnt = PANIC_TEST_MAX_SIZE / 512; critical_dev_nand_read(dev, sec_off, sec_cnt, tempbuf); print_hex_dump(KERN_CONT, "", DUMP_PREFIX_OFFSET, 16, 1, tempbuf, sec_cnt * 512, false); kfree(tempbuf); return count; #endif } else { nand_dbg_err("NAND DEBUG: undefined cmd: %s\n", cmd); return -EINVAL; } return count; } #if 0 /** * Parses a string into a number. The number stored at ptr is * potentially suffixed with K (for kilobytes, or 1024 bytes), * M (for megabytes, or 1048576 bytes), or G (for gigabytes, or * 1073741824). If the number is suffixed with K, M, or G, then * the return value is the number multiplied by one kilobyte, one * megabyte, or one gigabyte, respectively. * * @param ptr where parse begins * @param retptr output pointer to next char after parse completes (output) * @return resulting unsigned int */ static u64 memsize_parse (const char *const ptr, const char **retptr) { u64 ret = simple_strtoull(ptr, (char **)retptr, 0); switch (**retptr) { case 'G': case 'g': ret <<= 10; case 'M': case 'm': ret <<= 10; case 'K': case 'k': ret <<= 10; (*retptr)++; default: break; } return ret; } #endif #if 0 static ssize_t nand_sequence_speed_test_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { ssize_t ret; struct _nftl_blk *nftl_blk = aw_ndfc.nftl_blk; /*unsigned int size = nftl_blk->nftl_logic_size;*/ unsigned char *buffer = NULL; struct timeval tv; struct timeval tv1; int free_block_num; u32 logic_page_size; u64 free_size; u64 test_size; u64 size; int i = 0; unsigned long long time; test_size = memsize_parse((const char *)buf, (const char **)&buf); size = test_size; printk("test_size:%llu\n", test_size); logic_page_size = nand_get_logic_page_size(); free_block_num = get_free_block_num(nftl_blk->nftl_zone); free_size = free_block_num * nand_get_logic_block_size(); buffer = nand_malloc(PAGE_SIZE); if (buffer == NULL) { return -ENOMEM; } memset(buffer, 0xa5, PAGE_SIZE); test_size = (test_size / PAGE_SIZE) * PAGE_SIZE; /*write test*/ do_gettimeofday(&tv); while (test_size) { test_size -= PAGE_SIZE; } do_gettimeofday(&tv1); /*ms*/ time = (tv1.tv_sec * 1000000 + tv1.tv_usec - (tv.tv_sec * 1000000 + tv.tv_usec)) / 1000; test_size = size; printk("test_size:%llu time:%llu\n", test_size, time); printk("write: %dM\n", (test_size * 1000 / time) / 1024 / 1024); out: printk("file:%x\n", file); nand_free(buffer); return ret; return 0; } #endif static ssize_t nand_ndfc_reg_show(struct device *dev, struct device_attribute *attr, char *buf) { ssize_t ret = 0; int index = 0; int i = 0; unsigned long size; void __iomem *mem = NULL; volatile unsigned char *base = NULL; struct resource res; int len; if (of_address_to_resource(dev->of_node, index, &res)) { nand_dbg_inf("get resource fail\n"); return -ENXIO; } size = resource_size(&res); mem = ioremap(res.start, size); base = (volatile unsigned char *)mem; ret = snprintf(buf + ret, PAGE_SIZE - ret, "=========ndfc==========\n"); ret += snprintf(buf + ret, PAGE_SIZE - ret, "=register=--====val===\n"); /*skip reserve register*/ len = 0x120; if (len > size) len = size; for (i = 0; i < len + 1; i += 4) { ret += snprintf(buf + ret, PAGE_SIZE - ret, "0x%08x 0x%08x\n", (unsigned int)(res.start + i), readl(base + i)); } len = 0x214; if (len > size) len = size; for (i = 0x200; i < len + 1; i += 4) { ret += snprintf(buf + ret, PAGE_SIZE - ret, "0x%08x 0x%08x\n", (unsigned int)(res.start + i), readl(base + i)); } ret += snprintf(buf + ret, PAGE_SIZE - ret, "0x%08x 0x%08x\n", (unsigned int)(res.start + 0x2f0), readl(base + 0x2f0)); ret += snprintf(buf + ret, PAGE_SIZE - ret, "0x%08x 0x%08x\n", (unsigned int)(res.start + 0x300), readl(base + 0x300)); ret += snprintf(buf + ret, PAGE_SIZE - ret, "0x%08x 0x%08x\n", (unsigned int)(res.start + 0x400), readl(base + 0x400)); ret += snprintf(buf + ret, PAGE_SIZE - ret, "0x%08x 0x%08x\n", (unsigned int)(res.start + 0x800), readl(base + 0x800)); iounmap(mem); return ret; } static ssize_t sunxi_nand_clk_reg_show(struct device *dev, struct device_attribute *attr, char *buf) { ssize_t ret = 0; struct device_node *np = NULL; struct resource res; unsigned long size; void __iomem *mem = NULL; volatile unsigned char *base = NULL; int i; np = of_find_node_by_type(NULL, "clocks"); if (np == NULL) { nand_print("ERROR! get clocks node failed!\n"); return -1; } if (of_address_to_resource(np, 0, &res)) { nand_dbg_inf("get resource fail\n"); return -ENXIO; } /*ret = snprintf(buf, "%x %x %x %x\n", val[0], val[1], val[2], val[3]);*/ size = resource_size(&res); mem = ioremap(res.start, size); base = (volatile unsigned char *)mem; ret = snprintf(buf + ret, PAGE_SIZE - ret, "=========clock=========\n"); ret += snprintf(buf + ret, PAGE_SIZE - ret, "=register=--====val===\n"); #define PLL_GPU0_BASE 0x30 for (i = 0; i < PLL_GPU0_BASE; i += 4) { ret += snprintf(buf + ret, PAGE_SIZE - ret, "0x%08x 0x%08x\n", (unsigned int)(res.start + i), readl(base + i)); } #define SMHC0_CLK_BASE 0x834 /* MBUS_MAT_CLK_GATING_REG 0x804 * NAND0_0_CLK_REG 0x810 * NAND0_1_CLK_REG 0x814 * */ for (i = 0x800; i < SMHC0_CLK_BASE; i += 4) { ret += snprintf(buf + ret, PAGE_SIZE - ret, "0x%08x 0x%08x\n", (unsigned int)(res.start + i), readl(base + i)); } iounmap(mem); return ret; } static ssize_t sunxi_nand_pio_reg_show(struct device *dev, struct device_attribute *attr, char *buf) { ssize_t ret = 0; struct device_node *np = NULL; struct resource res; unsigned long size; void __iomem *mem = NULL; volatile unsigned char *base = NULL; int i; np = of_find_node_by_type(NULL, "pio"); if (np == NULL) { nand_print("ERROR! get clocks node failed!\n"); return -1; } if (of_address_to_resource(np, 0, &res)) { nand_dbg_inf("get resource fail\n"); return -ENXIO; } size = resource_size(&res); mem = ioremap(res.start, size); base = (volatile unsigned char *)mem; ret = snprintf(buf + ret, PAGE_SIZE - ret, "==========pio==========\n"); ret += snprintf(buf + ret, PAGE_SIZE - ret, "=register=--====val===\n"); #define PC_BASE 0x48 #define PD_BASE 0x6C for (i = PC_BASE; i < PD_BASE; i += 4) { ret += snprintf(buf + ret, PAGE_SIZE - ret, "0x%08x 0x%08x\n", (unsigned int)(res.start + i), readl(base + i)); } iounmap(mem); return ret; } static DEVICE_ATTR(sunxi_nftl_version, S_IRUGO, nand_nftl_version_show, NULL); static DEVICE_ATTR(nand_device_arch, S_IRUGO, nand_arch_show, NULL); static DEVICE_ATTR(nand_badblock, S_IRUGO, nand_badblock_show, NULL); static DEVICE_ATTR(nand_gcinfo, S_IRUGO, nand_gcinfo_show, NULL); static DEVICE_ATTR(nand_gcone, S_IRUGO | S_IWUSR, nand_gcone_show, nand_gcone_store); static DEVICE_ATTR(nand_gcall, S_IRUGO | S_IWUSR, nand_gcall_show, nand_gcall_store); static DEVICE_ATTR(nand_debug, S_IRUGO | S_IWUSR, nand_debug_show, nand_debug_store); /*static DEVICE_ATTR(nand_speed_test, S_IWUSR, NULL, nand_sequence_speed_test_store);*/ static DEVICE_ATTR(sunxi_ndfc_reg, S_IRUGO, nand_ndfc_reg_show, NULL); static DEVICE_ATTR(sunxi_nand_clk_reg, S_IRUGO, sunxi_nand_clk_reg_show, NULL); static DEVICE_ATTR(sunxi_nand_pio_reg, S_IRUGO, sunxi_nand_pio_reg_show, NULL); int nand_create_sys_fs(struct sunxi_ndfc *ndfc, struct platform_device *pdev) { int ret; /*create nftl_version node*/ ret = device_create_file(&pdev->dev, &dev_attr_sunxi_nftl_version); if (ret < 0) { printk("device create file sunxi_nftl_version fail\n"); return ret; } /*create arch node*/ ret = device_create_file(&pdev->dev, &dev_attr_nand_device_arch); if (ret < 0) { printk("device create file nand_device fail\n"); return ret; } /*create badblock node*/ ret = device_create_file(&pdev->dev, &dev_attr_nand_badblock); if (ret < 0) { printk("device create file nand_badblock fail\n"); return ret; } /*create gcinfo node*/ ret = device_create_file(&pdev->dev, &dev_attr_nand_gcinfo); if (ret < 0) { printk("device create file nand_gcinfo fail\n"); return ret; } /*create gcone node*/ ret = device_create_file(&pdev->dev, &dev_attr_nand_gcone); if (ret < 0) { printk("device create file nand_gcone fail\n"); return ret; } /*create gcall node*/ ret = device_create_file(&pdev->dev, &dev_attr_nand_gcall); if (ret < 0) { printk("device create file nand_gcall fail\n"); return ret; } /*create sunxi_ndfc_reg node*/ ret = device_create_file(&pdev->dev, &dev_attr_sunxi_ndfc_reg); if (ret < 0) { printk("device create file sunxi_ndfc_reg fail\n"); return ret; } /*create sunxi_nand_clk_reg node*/ ret = device_create_file(&pdev->dev, &dev_attr_sunxi_nand_clk_reg); if (ret < 0) { printk("device create file sunxi_nand_clk_reg fail\n"); return ret; } /*create sunxi_nand_pio_reg node*/ ret = device_create_file(&pdev->dev, &dev_attr_sunxi_nand_pio_reg); if (ret < 0) { printk("device create file sunxi_nand_pio_reg fail\n"); return ret; } /*create nand_debug*/ ret = device_create_file(&pdev->dev, &dev_attr_nand_debug); if (ret < 0) { printk("device create file sunxi_nand_pio_reg fail\n"); return ret; } #if 0 /*create speed test*/ ret = device_create_file(&pdev->dev, &dev_attr_nand_speed_test); if (ret < 0) { printk("device create file sunxi_nand_pio_reg fail\n"); return ret; } #endif return ret; }