SmartAudio/lichee/linux-4.9/modules/nand/sun8iw8p1/nfd/nand_blk.c

1051 lines
26 KiB
C
Executable File

#include "nand_blk.h"
#include "nand_dev.h"
/*****************************************************************************/
#define REMAIN_SPACE 0
#define PART_FREE 0x55
#define PART_DUMMY 0xff
#define PART_READONLY 0x85
#define PART_WRITEONLY 0x86
#define PART_NO_ACCESS 0x87
#define TIMEOUT 300 /* ms*/
#define NAND_CACHE_RW
/*#define NAND_IO_RESPONSE_TEST*/
struct secblc_op_t {
int item;
unsigned char *buf;
unsigned int len;
};
struct burn_param_t {
void *buffer;
long length;
};
/*****************************************************************************/
extern int get_nand_secure_storage_max_item(void);
extern int nand_secure_storage_read(int item, unsigned char *buf, unsigned int len);
extern int nand_secure_storage_write(int item, unsigned char *buf, unsigned int len);
extern int NAND_BurnBoot0(uint length, void *buf);
extern int NAND_BurnBoot1(uint length, void *buf);
extern int NAND_Get_OTA_Version(void);
extern struct _nand_info *p_nand_info;
/*for Int*/
/*extern void NAND_ClearRbInt(void);*/
/*extern void NAND_ClearDMAInt(void);*/
/*extern void NAND_Interrupt(__u32 nand_index);*/
extern int add_nand(struct nand_blk_ops *tr, struct _nand_phy_partition *phy_partition);
extern int remove_nand(struct nand_blk_ops *tr);
extern int nand_flush(struct nand_blk_dev *dev);
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 int nftl_ioctl_flush_w_cache(struct nand_blk_dev *dev);*/
/*****************************************************************************/
DEFINE_SEMAPHORE(nand_mutex);
unsigned char volatile IS_IDLE = 1;
static int nand_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd, unsigned long arg);
long max_r_io_response = 1;
long max_w_io_response = 1;
int debug_data = 0;
struct timeval tpstart, tpend;
long timeuse;
#if 0
/* print flags by name */
const char *rq_flag_bit_names[] = {
"REQ_WRITE", /* not set, read. set, write */
"REQ_FAILFAST_DEV", /* no driver retries of device errors */
"REQ_FAILFAST_TRANSPORT", /* no driver retries of transport errors */
"REQ_FAILFAST_DRIVER", /* no driver retries of driver errors */
"REQ_SYNC", /* request is sync (sync write or read) */
"REQ_META", /* metadata io request */
"REQ_DISCARD", /* request to discard sectors */
"REQ_NOIDLE", /* don't anticipate more IO after this one */
"REQ_RAHEAD", /* read ahead, can fail anytime */ /* bio only flags */
"REQ_THROTTLED", /* This bio has already been subjected to * throttling rules. Don't do it again. */
"REQ_SORTED", /* elevator knows about this request */
"REQ_SOFTBARRIER", /* may not be passed by ioscheduler */
"REQ_FUA", /* forced unit access */
"REQ_NOMERGE", /* don't touch this for merging */
"REQ_STARTED", /* drive already may have started this one */
"REQ_DONTPREP", /* don't call prep for this one */
"REQ_QUEUED", /* uses queueing */
"REQ_ELVPRIV", /* elevator private data attached */
"REQ_FAILED", /* set if the request failed */
"REQ_QUIET", /* don't worry about errors */
"REQ_PREEMPT", /* set for "ide_preempt" requests */
"REQ_ALLOCED", /* request came from our alloc pool */
"REQ_COPY_USER", /* contains copies of user pages */
"REQ_FLUSH", /* request for cache flush */
"REQ_FLUSH_SEQ", /* request for flush sequence */
"REQ_IO_STAT", /* account I/O stat */
"REQ_MIXED_MERGE", /* merge of different types, fail separately */
"REQ_SECURE", /* secure discard (used with __REQ_DISCARD) */
"REQ_NR_BITS", /* stops here */
};
#endif
/*void print_rq_flags(int flags)*/
/*{*/
/* int i;*/
/* uint32_t j;*/
/* j = 1;*/
/* printk("rq:");*/
/* for (i = 0; i < 32; i++) {*/
/* if (flags & j)*/
/* printk("%s ", rq_flag_bit_names[i]);*/
/* j = j << 1;*/
/* }*/
/* printk("\n");*/
/*}*/
/*****************************************************************************
*Name :
*Description :
*Parameter :
*Return :
*Note :
*****************************************************************************/
void start_time(int data)
{
if (debug_data != data)
return;
do_gettimeofday(&tpstart);
}
/*****************************************************************************
*Name :
*Description :
*Parameter :
*Return :
*Note :
*****************************************************************************/
int end_time(int data, int time, int par)
{
if (debug_data != data)
return -1;
do_gettimeofday(&tpend);
timeuse = 1000 * (tpend.tv_sec - tpstart.tv_sec) * 1000 + (tpend.tv_usec - tpstart.tv_usec);
if (timeuse > time) {
printk("N%ld %d\n", timeuse, par);
return 1;
}
return 0;
}
/*****************************************************************************
*Name :
*Description :
*Parameter :
*Return :
*Note :
*****************************************************************************/
static int do_blktrans_request(struct nand_blk_ops *tr,
struct nand_blk_dev *dev, struct request *req)
{
int ret = 0;
unsigned int block, nsect;
char *buf;
#ifdef NAND_IO_RESPONSE_TEST
struct timeval tpstart, tpend;
long timeuse;
#endif
struct _nand_dev *nand_dev;
nand_dev = (struct _nand_dev *)dev->priv;
block = blk_rq_pos(req) << 9 >> tr->blkshift;
nsect = blk_rq_cur_bytes(req) >> tr->blkshift;
/*
if (req_op(req) == REQ_OP_FLUSH)
goto request_exit;
*/
if (req->cmd_type != REQ_TYPE_FS) {
nand_dbg_err(KERN_NOTICE "not type fs\n");
return -EIO;
}
if (blk_rq_pos(req) + blk_rq_cur_sectors(req) > get_capacity(req->rq_disk)) {
nand_dbg_err(KERN_NOTICE "over capacity\n");
return -EIO;
}
if (req_op(req) & REQ_OP_DISCARD) {
//nand_dbg_inf("REQ_DISCARD block: %d nsect: %d\n", block, nsect);
/*rq_flush_dcache_pages(req);*/
//nand_dev->discard(nand_dev, block, nsect);
goto request_exit;
}
buf = bio_data(req->bio);
if (buf == NULL)
nand_dbg_inf("buf for request is NULL\n");
switch (rq_data_dir(req)) {
case READ:
#ifdef NAND_IO_RESPONSE_TEST
do_gettimeofday(&tpstart);
#endif
if (debug_data == 10) {
nand_dbg_err("read_addr: %d nsect: %d buf: %x\n", block, nsect, (unsigned int)buf);
}
nand_dev->read_data(nand_dev, block, nsect, buf);
#ifdef NAND_IO_RESPONSE_TEST
do_gettimeofday(&tpend);
timeuse = 1000 * (tpend.tv_sec - tpstart.tv_sec) * 1000 + (tpend.tv_usec - tpstart.tv_usec) ;
/*nand_dbg_err("r nsect: %d, %ld us \n", nsect, timeuse);*/
if (timeuse > max_r_io_response) {
max_r_io_response = timeuse;
nand_dbg_err("R nsect:%d, max_r_io_response: %ld us \n", nsect, max_r_io_response);
}
if (timeuse > 10000)
nand_dbg_err("R nsect:%d, use too many time: %ld us \n", nsect, timeuse);
#endif
rq_flush_dcache_pages(req);
ret = 0;
goto request_exit;
case WRITE:
rq_flush_dcache_pages(req);
#ifdef NAND_IO_RESPONSE_TEST
do_gettimeofday(&tpstart);
#endif
if (debug_data == 10) {
nand_dbg_err("write_addr: %d nsect: %d buf: %x\n", block, nsect, (unsigned int)buf);
}
nand_dev->write_data(nand_dev, block, nsect, buf);
#ifdef NAND_IO_RESPONSE_TEST
do_gettimeofday(&tpend);
timeuse = 1000 * (tpend.tv_sec - tpstart.tv_sec) * 1000 + (tpend.tv_usec - tpstart.tv_usec) ;
/*nand_dbg_err("w nsect: %d, %ld us \n", nsect, timeuse);*/
if (timeuse > max_w_io_response) {
max_w_io_response = timeuse;
nand_dbg_err("W nsect:%d, max_w_io_response: %ld us \n", nsect, max_w_io_response);
}
if (timeuse > 10000)
nand_dbg_err("W nsect:%d, use too many time: %ld us \n", nsect, timeuse);
#endif
ret = 0;
goto request_exit;
default:
nand_dbg_err(KERN_NOTICE "Unknown request %u\n", rq_data_dir(req));
ret = -EIO;
goto request_exit;
}
request_exit:
return ret;
}
/*****************************************************************************
*Name :
*Description :
*Parameter :
*Return :
*Note :
*****************************************************************************/
static int mtd_blktrans_thread(void *arg)
{
struct nand_blk_dev *dev = arg;
struct request_queue *rq = dev->rq;
struct request *req = NULL;
int background_done = 0;
spin_lock_irq(rq->queue_lock);
while (!kthread_should_stop()) {
int res;
dev->bg_stop = false;
if (!req && !(req = blk_fetch_request(rq))) {
/* if (tr->background && !background_done) {*/
/* spin_unlock_irq(rq->queue_lock);*/
/* mutex_lock(&dev->lock);*/
/* tr->background(dev);*/
/* mutex_unlock(&dev->lock);*/
/* spin_lock_irq(rq->queue_lock);*/
/*
* Do background processing just once per idle
* period.
*/
/* background_done = !dev->bg_stop;*/
/* continue;*/
/* }*/
set_current_state(TASK_INTERRUPTIBLE);
if (kthread_should_stop())
set_current_state(TASK_RUNNING);
spin_unlock_irq(rq->queue_lock);
dev->rq_null++;
schedule();
spin_lock_irq(rq->queue_lock);
continue;
}
spin_unlock_irq(rq->queue_lock);
dev->rq_null = 0;
mutex_lock(&dev->lock);
res = do_blktrans_request(dev->nandr, dev, req);
mutex_unlock(&dev->lock);
spin_lock_irq(rq->queue_lock);
if (!__blk_end_request_cur(req, res))
req = NULL;
background_done = 0;
}
if (req)
__blk_end_request_all(req, -EIO);
spin_unlock_irq(rq->queue_lock);
return 0;
}
/*****************************************************************************
*Name :
*Description :
*Parameter :
*Return :
*Note :
*****************************************************************************/
static void mtd_blktrans_request(struct request_queue *rq)
{
struct nand_blk_dev *dev;
struct request *req = NULL;
dev = rq->queuedata;
if (!dev)
while ((req = blk_fetch_request(rq)) != NULL)
__blk_end_request_all(req, -ENODEV);
else {
dev->bg_stop = true;
wake_up_process(dev->thread);
}
}
/*****************************************************************************
*Name :
*Description :
*Parameter :
*Return :
*Note :
*****************************************************************************/
static int nand_open(struct block_device *bdev, fmode_t mode)
{
struct nand_blk_dev *dev;
struct nand_blk_ops *nandr;
int ret = -ENODEV;
dev = bdev->bd_disk->private_data;
nandr = dev->nandr;
if (!try_module_get(nandr->owner))
goto out;
ret = 0;
if (nandr->open && (ret = nandr->open(dev))) {
out:
module_put(nandr->owner);
}
return ret;
}
/*****************************************************************************
*Name :
*Description :
*Parameter :
*Return :
*Note :
*****************************************************************************/
static void nand_release(struct gendisk *disk, fmode_t mode)
{
struct nand_blk_dev *dev;
struct nand_blk_ops *nandr;
int ret = 0;
dev = disk->private_data;
nandr = dev->nandr;
if (nandr->release)
ret = nandr->release(dev);
if (!ret) {
module_put(nandr->owner);
}
}
/*****************************************************************************
*Name :
*Description :
*Parameter :
*Return :
*Note :
*****************************************************************************/
struct burn_param_t *nand_burnboot_ioctl_copy_from_user(
struct burn_param_t __user *from)
{
int err = 0;
char __user *user_buf;
struct burn_param_t *to = kmalloc(sizeof(struct burn_param_t), GFP_KERNEL);
if (!to) {
err = -ENOMEM;
goto err;
}
if (copy_from_user(to, from, sizeof(struct burn_param_t))) {
err = -EFAULT;
goto param_err;
}
user_buf = to->buffer;
to->buffer = kmalloc(to->length, GFP_KERNEL);
if (!to->buffer) {
err = -ENOMEM;
goto param_err;
}
if (copy_from_user(to->buffer, user_buf, to->length)) {
err = -EFAULT;
goto buffer_err;
}
return to;
buffer_err:
kfree(to->buffer);
param_err:
kfree(to);
err:
return ERR_PTR(err);
}
/*****************************************************************************
*Name :
*Description :
*Parameter :
*Return :
*Note :
*****************************************************************************/
#define DISABLE_WRITE _IO('V', 0)
#define ENABLE_WRITE _IO('V', 1)
#define DISABLE_READ _IO('V', 2)
#define ENABLE_READ _IO('V', 3)
#define BLKBURNBOOT0 _IO('v', 127)
#define BLKBURNBOOT1 _IO('v', 128)
#define SECBLK_READ _IO('V', 20)
#define SECBLK_WRITE _IO('V', 21)
#define SECBLK_IOCTL _IO('V', 22)
#define GET_OTA_VER _IO('V', 126)
static int nand_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd, unsigned long arg)
{
struct nand_blk_dev *dev = bdev->bd_disk->private_data;
struct nand_blk_ops *nandr = dev->nandr;
struct secblc_op_t *sec_op;
int ret = 0;
switch (cmd) {
#if 0
#if 0
case BLKFLSBUF:
/*nand_dbg_err("BLKFLSBUF called!\n");*/
if (nandr->flush)
return nandr->flush(dev);
/* The core code did the work, we had nothing to do.*/
return 0;
#else
case BLKFLSBUF:
nftl_ioctl_flush_w_cache(dev);
return 0;
#endif
#endif
case HDIO_GETGEO:
if (nandr->getgeo) {
struct hd_geometry g;
int ret;
memset(&g, 0, sizeof(g));
ret = nandr->getgeo(dev, &g);
if (ret)
return ret;
nand_dbg_err("HDIO_GETGEO called!\n");
g.start = get_start_sect(bdev);
if (copy_to_user((void __user *)arg, &g, sizeof(g)))
return -EFAULT;
return 0;
}
return 0;
case ENABLE_WRITE:
nand_dbg_err("enable write!\n");
dev->disable_access = 0;
dev->readonly = 0;
set_disk_ro(dev->disk, 0);
return 0;
case DISABLE_WRITE:
nand_dbg_err("disable write!\n");
dev->readonly = 1;
set_disk_ro(dev->disk, 1);
return 0;
case ENABLE_READ:
nand_dbg_err("enable read!\n");
dev->disable_access = 0;
dev->writeonly = 0;
return 0;
case DISABLE_READ:
nand_dbg_err("disable read!\n");
dev->writeonly = 1;
return 0;
case BLKBURNBOOT0:
{
struct burn_param_t *burn;
struct burn_param_t __user *user_burn = (struct burn_param_t __user *)arg;
burn = nand_burnboot_ioctl_copy_from_user(user_burn);
if (IS_ERR(burn))
return PTR_ERR(burn);
if (0 == down_trylock(&(nandr->nand_ops_mutex))) {
IS_IDLE = 0;
ret = NAND_BurnBoot0(burn->length, burn->buffer);
up(&(nandr->nand_ops_mutex));
IS_IDLE = 1;
}
kfree(burn->buffer);
kfree(burn);
return ret;
}
case BLKBURNBOOT1:
{
struct burn_param_t *burn;
struct burn_param_t __user *user_burn = (struct burn_param_t __user *)arg;
burn = nand_burnboot_ioctl_copy_from_user(user_burn);
if (IS_ERR(burn))
return PTR_ERR(burn);
if (0 == down_trylock(&(nandr->nand_ops_mutex))) {
IS_IDLE = 0;
ret = NAND_BurnBoot1(burn->length, burn->buffer);
up(&(nandr->nand_ops_mutex));
IS_IDLE = 1;
}
kfree(burn->buffer);
kfree(burn);
return ret;
}
case SECBLK_READ:
if (0 == down_trylock(&(nandr->nand_ops_mutex))) {
IS_IDLE = 0;
sec_op = (struct secblc_op_t *)arg ;
ret = nand_secure_storage_read(sec_op->item, sec_op->buf, sec_op->len);
up(&(nandr->nand_ops_mutex));
IS_IDLE = 1;
}
return ret;
case SECBLK_WRITE:
if (0 == down_trylock(&(nandr->nand_ops_mutex))) {
IS_IDLE = 0;
sec_op = (struct secblc_op_t *)arg ;
ret = nand_secure_storage_write(sec_op->item, sec_op->buf, sec_op->len);
up(&(nandr->nand_ops_mutex));
IS_IDLE = 1;
}
return ret;
case SECBLK_IOCTL:
if (0 == down_trylock(&(nandr->nand_ops_mutex))) {
IS_IDLE = 0;
ret = get_nand_secure_storage_max_item();
up(&(nandr->nand_ops_mutex));
IS_IDLE = 1;
}
return ret;
case GET_OTA_VER:
if (0 == down_trylock(&(nandr->nand_ops_mutex))) {
IS_IDLE = 0;
ret = NAND_Get_OTA_Version();
up(&(nandr->nand_ops_mutex));
IS_IDLE = 1;
}
return ret;
default:
return -ENOTTY;
}
}
/*****************************************************************************
*Name :
*Description :
*Parameter :
*Return :
*Note :
*****************************************************************************/
struct block_device_operations nand_blktrans_ops = {
.owner = THIS_MODULE,
.open = nand_open,
.release = nand_release,
.ioctl = nand_ioctl,
};
/*****************************************************************************
*Name :
*Description :
*Parameter :
*Return :
*Note :
*****************************************************************************/
static int nand_blk_open(struct nand_blk_dev *dev)
{
/*nand_dbg_err("nand_blk_open!\n");*/
/*mutex_lock(&dev->lock);*/
/*nand_dbg_err("nand_open ok!\n");*/
/*kref_get(&dev->ref);*/
/*mutex_unlock(&dev->lock);*/
return 0;
}
/*****************************************************************************
*Name :
*Description :
*Parameter :
*Return :
*Note :
*****************************************************************************/
static int nand_blk_release(struct nand_blk_dev *dev)
{
int error = 0;
struct _nand_dev *nand_dev = (struct _nand_dev *)dev->priv;
/*nand_dbg_err("nand_blk_release!\n");*/
/*error = nand_dev->flush_sector_write_cache(nand_dev,0);*/
error = nand_dev->flush_write_cache(nand_dev, 0xffff);
/*mutex_lock(&dev->lock);*/
/*kref_put(&dev->ref, del_nand_blktrans_dev);*/
/*mutex_unlock(&dev->lock);*/
return error;
}
/*****************************************************************************
*Name :
*Description :
*Parameter :
*Return :
*Note :
*****************************************************************************/
int del_nand_blktrans_dev(struct nand_blk_dev *dev)
{
/* if (!down_trylock(&nand_mutex)) {*/
/* up(&nand_mutex);*/
/* BUG();*/
/* }*/
blk_cleanup_queue(dev->rq);
kthread_stop(dev->thread);
list_del(&dev->list);
dev->disk->queue = NULL;
del_gendisk(dev->disk);
put_disk(dev->disk);
return 0;
}
/*****************************************************************************
*Name :
*Description :
*Parameter :
*Return :
*Note :
*****************************************************************************/
static int nand_getgeo(struct nand_blk_dev *dev, struct hd_geometry *geo)
{
geo->heads = dev->heads;
geo->sectors = dev->sectors;
geo->cylinders = dev->cylinders;
return 0;
}
/*****************************************************************************
*Name :
*Description :
*Parameter :
*Return :
*Note :
*****************************************************************************/
struct nand_blk_ops mytr = {
.name = "nand",
.major = 93,
.minorbits = 3,
.blksize = 512,
.blkshift = 9,
.open = nand_blk_open,
.release = nand_blk_release,
.getgeo = nand_getgeo,
.add_dev = add_nand,
.remove_dev = remove_nand,
.flush = nand_flush,
.owner = THIS_MODULE,
};
/*****************************************************************************
*Name :
*Description :
*Parameter :
*Return :
*Note :
*****************************************************************************/
int add_nand_blktrans_dev(struct nand_blk_dev *dev)
{
struct nand_blk_ops *tr = dev->nandr;
struct list_head *this = NULL;
struct gendisk *gd;
unsigned long temp;
int ret = -ENOMEM;
int last_devnum = -1;
dev->cylinders = 1024;
dev->heads = 16;
temp = dev->cylinders * dev->heads;
dev->sectors = (dev->size) / temp;
if ((dev->size) % temp) {
dev->sectors++;
temp = dev->cylinders * dev->sectors;
dev->heads = (dev->size) / temp;
if ((dev->size) % temp) {
dev->heads++;
temp = dev->heads * dev->sectors;
dev->cylinders = (dev->size) / temp;
}
}
if (!down_trylock(&nand_mutex)) {
up(&nand_mutex);
BUG();
}
list_for_each(this, &tr->devs) {
struct nand_blk_dev *tmpdev = list_entry(this, struct nand_blk_dev, list);
if (dev->devnum == -1) {
/* Use first free number */
if (tmpdev->devnum != last_devnum + 1) {
/* Found a free devnum. Plug it in here */
dev->devnum = last_devnum + 1;
list_add_tail(&dev->list, &tmpdev->list);
goto added;
}
} else if (tmpdev->devnum == dev->devnum) {
/* Required number taken */
nand_dbg_err("\nerror00\n");
return -EBUSY;
} else if (tmpdev->devnum > dev->devnum) {
/* Required number was free */
list_add_tail(&dev->list, &tmpdev->list);
goto added;
}
last_devnum = tmpdev->devnum;
}
if (dev->devnum == -1)
dev->devnum = last_devnum + 1;
if ((dev->devnum << tr->minorbits) > 256) {
nand_dbg_err("\nerror00000\n");
return -EBUSY;
}
/*init_MUTEX(&dev->sem);*/
list_add_tail(&dev->list, &tr->devs);
added:
gd = alloc_disk(1 << tr->minorbits);
if (!gd) {
list_del(&dev->list);
goto error2;
}
gd->major = tr->major;
gd->first_minor = (dev->devnum) << tr->minorbits;
gd->fops = &nand_blktrans_ops;
snprintf(gd->disk_name, sizeof(gd->disk_name), "%s%c", tr->name, (tr->minorbits ? 'a' : '0') + dev->devnum);
/* 2.5 has capacity in units of 512 bytes while still
having BLOCK_SIZE_BITS set to 10. Just to keep us amused. */
set_capacity(gd, dev->size);
gd->private_data = dev;
dev->disk = gd;
gd->queue = dev->rq;
/*set rw partition*/
/* if(part->type == PART_NO_ACCESS)*/
/* dev->disable_access = 1;*/
/**/
/* if(part->type == PART_READONLY)*/
/* dev->readonly = 1;*/
/**/
/* if(part->type == PART_WRITEONLY)*/
/* dev->writeonly = 1;*/
/* if (dev->readonly)*/
/* set_disk_ro(gd, 1);*/
dev->disable_access = 0;
dev->readonly = 0;
dev->writeonly = 0;
mutex_init(&dev->lock);
/*kref_init(&dev->ref);*/
/* Create the request queue*/
spin_lock_init(&dev->queue_lock);
/*dev->rq = blk_init_queue(nand_blktrans_request, &dev->queue_lock);*/
dev->rq = blk_init_queue(mtd_blktrans_request, &dev->queue_lock);
if (!dev->rq) {
goto error3;
}
#if 0
ret = elevator_change(dev->rq, "deadline");
if (ret) {
blk_cleanup_queue(dev->rq);
return ret;
}
#endif
dev->rq->queuedata = dev;
blk_queue_logical_block_size(dev->rq, tr->blksize);
queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, dev->rq);
dev->rq->limits.max_discard_sectors = UINT_MAX;
gd->queue = dev->rq;
/*Create processing thread kernel_thread*/
/*dev->thread = kthread_run(nand_blktrans_thread, dev,"%s%d", tr->name,dev->devnum);*/
dev->thread = kthread_run(mtd_blktrans_thread, dev, "%s%d", tr->name, dev->devnum);
if (IS_ERR(dev->thread)) {
ret = PTR_ERR(dev->thread);
goto error4;
}
add_disk(gd);
return 0;
error4:
nand_dbg_err("\nerror4\n");
blk_cleanup_queue(dev->rq);
error3:
nand_dbg_err("\nerror3\n");
put_disk(dev->disk);
error2:
nand_dbg_err("\nerror2\n");
list_del(&dev->list);
return ret;
}
/*****************************************************************************
*Name :
*Description :
*Parameter :
*Return :
*Note :
*****************************************************************************/
int nand_blk_register(struct nand_blk_ops *tr)
{
int ret;
struct _nand_phy_partition *phy_partition;
down(&nand_mutex);
ret = register_blkdev(tr->major, tr->name);
if (ret) {
nand_dbg_err("\nfaild to register blk device\n");
up(&nand_mutex);
return -1;
}
init_completion(&tr->thread_exit);
init_waitqueue_head(&tr->thread_wq);
sema_init(&tr->nand_ops_mutex, 1);
/*devfs_mk_dir(nandr->name);*/
INIT_LIST_HEAD(&tr->devs);
tr->nftl_blk_head.nftl_blk_next = NULL;
tr->nand_dev_head.nand_dev_next = NULL;
phy_partition = get_head_phy_partition_from_nand_info(p_nand_info);
while (phy_partition != NULL) {
tr->add_dev(tr, phy_partition);
phy_partition = get_next_phy_partition(phy_partition);
}
up(&nand_mutex);
return 0;
}
/*****************************************************************************
*Name :
*Description :
*Parameter :
*Return :
*Note :
*****************************************************************************/
void nand_blk_unregister(struct nand_blk_ops *tr)
{
down(&nand_mutex);
/* Clean up the kernel thread */
tr->quit = 1;
wake_up(&tr->thread_wq);
wait_for_completion(&tr->thread_exit);
/* Remove it from the list of active majors */
tr->remove_dev(tr);
unregister_blkdev(tr->major, tr->name);
/*devfs_remove(nandr->name);*/
/*blk_cleanup_queue(tr->rq);*/
up(&nand_mutex);
if (!list_empty(&tr->devs))
BUG();
}
/*****************************************************************************
*Name :
*Description :
*Parameter :
*Return :
*Note :
*****************************************************************************/
/*int cal_partoff_within_disk(char *name,struct inode *i)*/
/*{*/
/* struct gendisk *gd = i->i_bdev->bd_disk;*/
/* int current_minor = MINOR(i->i_bdev->bd_dev) ;*/
/* int index = current_minor & ((1<<mytr.minorbits) - 1) ;*/
/* if(!index)*/
/* return 0;*/
/* return ( gd->part_tbl->part[ index - 1]->start_sect);*/
/*}*/
/*****************************************************************************
*Name :
*Description :
*Parameter :
*Return :
*Note :
*****************************************************************************/
int init_blklayer(void)
{
return nand_blk_register(&mytr);
}
/*****************************************************************************
*Name :
*Description :
*Parameter :
*Return :
*Note :
*****************************************************************************/
void exit_blklayer(void)
{
nand_blk_unregister(&mytr);
}
/*****************************************************************************
*Name :
*Description :
*Parameter :
*Return :
*Note :
*****************************************************************************/
int __init nand_drv_init(void)
{
/*printk("[NAND]2013-8-8 10:05\n");*/
return init_blklayer();
}
/*****************************************************************************
*Name :
*Description :
*Parameter :
*Return :
*Note :
*****************************************************************************/
void __exit nand_drv_exit(void)
{
exit_blklayer();
}
/*module_init(nand_drv_init);*/
/*module_exit(nand_drv_exit);*/
MODULE_LICENSE("GPL");
MODULE_AUTHOR("nand flash groups");
MODULE_DESCRIPTION("Generic NAND flash driver code");