| .. | .. |
|---|
| 7 | 7 | * (at your option) any later version. |
|---|
| 8 | 8 | */ |
|---|
| 9 | 9 | |
|---|
| 10 | +#define pr_fmt(fmt) "rk_nand: " fmt |
|---|
| 11 | + |
|---|
| 10 | 12 | #include <linux/kernel.h> |
|---|
| 11 | 13 | #include <linux/slab.h> |
|---|
| 12 | 14 | #include <linux/module.h> |
|---|
| 13 | 15 | #include <linux/list.h> |
|---|
| 14 | 16 | #include <linux/fs.h> |
|---|
| 15 | 17 | #include <linux/blkdev.h> |
|---|
| 18 | +#include <linux/blk-mq.h> |
|---|
| 16 | 19 | #include <linux/blkpg.h> |
|---|
| 17 | 20 | #include <linux/spinlock.h> |
|---|
| 18 | 21 | #include <linux/hdreg.h> |
|---|
| .. | .. |
|---|
| 36 | 39 | #include "rk_nand_blk.h" |
|---|
| 37 | 40 | #include "rk_ftl_api.h" |
|---|
| 38 | 41 | |
|---|
| 39 | | -static struct nand_part disk_array[MAX_PART_COUNT]; |
|---|
| 40 | | -static int g_max_part_num = 4; |
|---|
| 41 | | - |
|---|
| 42 | 42 | #define PART_READONLY 0x85 |
|---|
| 43 | 43 | #define PART_WRITEONLY 0x86 |
|---|
| 44 | 44 | #define PART_NO_ACCESS 0x87 |
|---|
| .. | .. |
|---|
| 48 | 48 | static unsigned long totle_read_count; |
|---|
| 49 | 49 | static unsigned long totle_write_count; |
|---|
| 50 | 50 | static int rk_nand_dev_initialised; |
|---|
| 51 | +static unsigned long rk_ftl_gc_do; |
|---|
| 52 | +static DECLARE_WAIT_QUEUE_HEAD(rknand_thread_wait); |
|---|
| 53 | +static unsigned long rk_ftl_gc_jiffies; |
|---|
| 51 | 54 | |
|---|
| 52 | 55 | static char *mtd_read_temp_buffer; |
|---|
| 53 | 56 | #define MTD_RW_SECTORS (512) |
|---|
| 54 | 57 | |
|---|
| 58 | +#define DISABLE_WRITE _IO('V', 0) |
|---|
| 59 | +#define ENABLE_WRITE _IO('V', 1) |
|---|
| 60 | +#define DISABLE_READ _IO('V', 2) |
|---|
| 61 | +#define ENABLE_READ _IO('V', 3) |
|---|
| 55 | 62 | static int rknand_proc_show(struct seq_file *m, void *v) |
|---|
| 56 | 63 | { |
|---|
| 57 | 64 | m->count = rknand_proc_ftlread(m->buf); |
|---|
| .. | .. |
|---|
| 62 | 69 | return 0; |
|---|
| 63 | 70 | } |
|---|
| 64 | 71 | |
|---|
| 65 | | -static int rknand_mtd_proc_show(struct seq_file *m, void *v) |
|---|
| 66 | | -{ |
|---|
| 67 | | - int i; |
|---|
| 68 | | - |
|---|
| 69 | | - seq_printf(m, "%s", "dev: size erasesize name\n"); |
|---|
| 70 | | - for (i = 0; i < g_max_part_num; i++) { |
|---|
| 71 | | - seq_printf(m, "rknand%d: %8.8llx %8.8x \"%s\"\n", i, |
|---|
| 72 | | - (unsigned long long)disk_array[i].size * 512, |
|---|
| 73 | | - 32 * 0x200, disk_array[i].name); |
|---|
| 74 | | - } |
|---|
| 75 | | - return 0; |
|---|
| 76 | | -} |
|---|
| 77 | | - |
|---|
| 78 | 72 | static int rknand_proc_open(struct inode *inode, struct file *file) |
|---|
| 79 | 73 | { |
|---|
| 80 | 74 | return single_open(file, rknand_proc_show, PDE_DATA(inode)); |
|---|
| 81 | 75 | } |
|---|
| 82 | 76 | |
|---|
| 83 | | -static int rknand_mtd_proc_open(struct inode *inode, struct file *file) |
|---|
| 84 | | -{ |
|---|
| 85 | | - return single_open(file, rknand_mtd_proc_show, PDE_DATA(inode)); |
|---|
| 86 | | -} |
|---|
| 87 | | - |
|---|
| 88 | | -static const struct file_operations rknand_proc_fops = { |
|---|
| 89 | | - .owner = THIS_MODULE, |
|---|
| 90 | | - .open = rknand_proc_open, |
|---|
| 91 | | - .read = seq_read, |
|---|
| 92 | | - .llseek = seq_lseek, |
|---|
| 93 | | - .release = single_release, |
|---|
| 94 | | -}; |
|---|
| 95 | | - |
|---|
| 96 | | -static const struct file_operations rknand_mtd_proc_fops = { |
|---|
| 97 | | - .owner = THIS_MODULE, |
|---|
| 98 | | - .open = rknand_mtd_proc_open, |
|---|
| 99 | | - .read = seq_read, |
|---|
| 100 | | - .llseek = seq_lseek, |
|---|
| 101 | | - .release = single_release, |
|---|
| 77 | +static const struct proc_ops rknand_proc_fops = { |
|---|
| 78 | + .proc_open = rknand_proc_open, |
|---|
| 79 | + .proc_read = seq_read, |
|---|
| 80 | + .proc_lseek = seq_lseek, |
|---|
| 81 | + .proc_release = single_release, |
|---|
| 102 | 82 | }; |
|---|
| 103 | 83 | |
|---|
| 104 | 84 | static int rknand_create_procfs(void) |
|---|
| .. | .. |
|---|
| 110 | 90 | if (!ent) |
|---|
| 111 | 91 | return -1; |
|---|
| 112 | 92 | |
|---|
| 113 | | - ent = proc_create_data("mtd", 0444, NULL, &rknand_mtd_proc_fops, |
|---|
| 114 | | - (void *)0); |
|---|
| 115 | | - if (!ent) |
|---|
| 116 | | - return -1; |
|---|
| 117 | 93 | return 0; |
|---|
| 118 | 94 | } |
|---|
| 119 | 95 | |
|---|
| .. | .. |
|---|
| 143 | 119 | unsigned long start, |
|---|
| 144 | 120 | unsigned long nsector, |
|---|
| 145 | 121 | char *buf, |
|---|
| 146 | | - int cmd, |
|---|
| 147 | | - int totle_nsec) |
|---|
| 122 | + int cmd) |
|---|
| 148 | 123 | { |
|---|
| 149 | 124 | int ret; |
|---|
| 150 | 125 | |
|---|
| .. | .. |
|---|
| 155 | 130 | } |
|---|
| 156 | 131 | |
|---|
| 157 | 132 | start += dev->off_size; |
|---|
| 158 | | - rknand_device_lock(); |
|---|
| 159 | 133 | |
|---|
| 160 | 134 | switch (cmd) { |
|---|
| 161 | 135 | case READ: |
|---|
| .. | .. |
|---|
| 179 | 153 | break; |
|---|
| 180 | 154 | } |
|---|
| 181 | 155 | |
|---|
| 182 | | - rknand_device_unlock(); |
|---|
| 183 | 156 | return ret; |
|---|
| 184 | | -} |
|---|
| 185 | | - |
|---|
| 186 | | -static DECLARE_WAIT_QUEUE_HEAD(rknand_thread_wait); |
|---|
| 187 | | -static void rk_ftl_gc_timeout_hack(struct timer_list *unused); |
|---|
| 188 | | -static DEFINE_TIMER(rk_ftl_gc_timeout, rk_ftl_gc_timeout_hack); |
|---|
| 189 | | -static unsigned long rk_ftl_gc_jiffies; |
|---|
| 190 | | -static unsigned long rk_ftl_gc_do; |
|---|
| 191 | | - |
|---|
| 192 | | -static void rk_ftl_gc_timeout_hack(struct timer_list *unused) |
|---|
| 193 | | -{ |
|---|
| 194 | | - del_timer(&rk_ftl_gc_timeout); |
|---|
| 195 | | - rk_ftl_gc_do++; |
|---|
| 196 | | - rk_ftl_gc_timeout.expires = jiffies + rk_ftl_gc_jiffies * rk_ftl_gc_do; |
|---|
| 197 | | - add_timer(&rk_ftl_gc_timeout); |
|---|
| 198 | 157 | } |
|---|
| 199 | 158 | |
|---|
| 200 | 159 | static int req_check_buffer_align(struct request *req, char **pbuf) |
|---|
| .. | .. |
|---|
| 225 | 184 | return 1; |
|---|
| 226 | 185 | } |
|---|
| 227 | 186 | |
|---|
| 228 | | -static int nand_blktrans_thread(void *arg) |
|---|
| 187 | +static blk_status_t do_blktrans_all_request(struct nand_blk_dev *dev, |
|---|
| 188 | + struct request *req) |
|---|
| 229 | 189 | { |
|---|
| 230 | | - struct nand_blk_ops *nandr = arg; |
|---|
| 231 | | - struct request_queue *rq = nandr->rq; |
|---|
| 232 | | - struct request *req = NULL; |
|---|
| 233 | | - int ftl_gc_status = 0; |
|---|
| 234 | | - char *buf, *page_buf; |
|---|
| 190 | + unsigned long block, nsect; |
|---|
| 191 | + char *buf = NULL, *page_buf; |
|---|
| 235 | 192 | struct req_iterator rq_iter; |
|---|
| 236 | 193 | struct bio_vec bvec; |
|---|
| 237 | | - unsigned long long sector_index = ULLONG_MAX; |
|---|
| 194 | + int ret = BLK_STS_IOERR; |
|---|
| 238 | 195 | unsigned long totle_nsect; |
|---|
| 239 | | - int rw_flag = 0; |
|---|
| 240 | | - int req_empty_times = 0; |
|---|
| 241 | | - int op; |
|---|
| 242 | 196 | |
|---|
| 243 | | - spin_lock_irq(rq->queue_lock); |
|---|
| 244 | | - rk_ftl_gc_jiffies = HZ / 10; /* do garbage collect after 100ms */ |
|---|
| 197 | + block = blk_rq_pos(req); |
|---|
| 198 | + nsect = blk_rq_cur_bytes(req) >> 9; |
|---|
| 199 | + totle_nsect = (req->__data_len) >> 9; |
|---|
| 200 | + |
|---|
| 201 | + if (blk_rq_pos(req) + blk_rq_cur_sectors(req) > get_capacity(req->rq_disk)) |
|---|
| 202 | + return BLK_STS_IOERR; |
|---|
| 203 | + |
|---|
| 204 | + switch (req_op(req)) { |
|---|
| 205 | + case REQ_OP_DISCARD: |
|---|
| 206 | + if (FtlDiscard(block, nsect)) |
|---|
| 207 | + return BLK_STS_IOERR; |
|---|
| 208 | + return BLK_STS_OK; |
|---|
| 209 | + case REQ_OP_READ: |
|---|
| 210 | + buf = mtd_read_temp_buffer; |
|---|
| 211 | + req_check_buffer_align(req, &buf); |
|---|
| 212 | + ret = nand_dev_transfer(dev, block, totle_nsect, buf, REQ_OP_READ); |
|---|
| 213 | + if (buf == mtd_read_temp_buffer) { |
|---|
| 214 | + char *p = buf; |
|---|
| 215 | + |
|---|
| 216 | + rq_for_each_segment(bvec, req, rq_iter) { |
|---|
| 217 | + page_buf = kmap_atomic(bvec.bv_page); |
|---|
| 218 | + |
|---|
| 219 | + memcpy(page_buf + bvec.bv_offset, p, bvec.bv_len); |
|---|
| 220 | + p += bvec.bv_len; |
|---|
| 221 | + kunmap_atomic(page_buf); |
|---|
| 222 | + } |
|---|
| 223 | + } |
|---|
| 224 | + |
|---|
| 225 | + if (ret) |
|---|
| 226 | + return BLK_STS_IOERR; |
|---|
| 227 | + else |
|---|
| 228 | + return BLK_STS_OK; |
|---|
| 229 | + case REQ_OP_WRITE: |
|---|
| 230 | + buf = mtd_read_temp_buffer; |
|---|
| 231 | + req_check_buffer_align(req, &buf); |
|---|
| 232 | + |
|---|
| 233 | + if (buf == mtd_read_temp_buffer) { |
|---|
| 234 | + char *p = buf; |
|---|
| 235 | + |
|---|
| 236 | + rq_for_each_segment(bvec, req, rq_iter) { |
|---|
| 237 | + page_buf = kmap_atomic(bvec.bv_page); |
|---|
| 238 | + memcpy(p, page_buf + bvec.bv_offset, bvec.bv_len); |
|---|
| 239 | + p += bvec.bv_len; |
|---|
| 240 | + kunmap_atomic(page_buf); |
|---|
| 241 | + } |
|---|
| 242 | + } |
|---|
| 243 | + |
|---|
| 244 | + ret = nand_dev_transfer(dev, block, totle_nsect, buf, REQ_OP_WRITE); |
|---|
| 245 | + |
|---|
| 246 | + if (ret) |
|---|
| 247 | + return BLK_STS_IOERR; |
|---|
| 248 | + else |
|---|
| 249 | + return BLK_STS_OK; |
|---|
| 250 | + |
|---|
| 251 | + default: |
|---|
| 252 | + return BLK_STS_IOERR; |
|---|
| 253 | + } |
|---|
| 254 | +} |
|---|
| 255 | + |
|---|
| 256 | +static struct request *rk_nand_next_request(struct nand_blk_dev *dev) |
|---|
| 257 | +{ |
|---|
| 258 | + struct nand_blk_ops *nand_ops = dev->nand_ops; |
|---|
| 259 | + struct request *rq; |
|---|
| 260 | + |
|---|
| 261 | + rq = list_first_entry_or_null(&nand_ops->rq_list, struct request, queuelist); |
|---|
| 262 | + if (rq) { |
|---|
| 263 | + list_del_init(&rq->queuelist); |
|---|
| 264 | + blk_mq_start_request(rq); |
|---|
| 265 | + return rq; |
|---|
| 266 | + } |
|---|
| 267 | + |
|---|
| 268 | + return NULL; |
|---|
| 269 | +} |
|---|
| 270 | + |
|---|
| 271 | +static void rk_nand_blktrans_work(struct nand_blk_dev *dev) |
|---|
| 272 | + __releases(&dev->nand_ops->queue_lock) |
|---|
| 273 | + __acquires(&dev->nand_ops->queue_lock) |
|---|
| 274 | +{ |
|---|
| 275 | + struct request *req = NULL; |
|---|
| 276 | + |
|---|
| 277 | + while (1) { |
|---|
| 278 | + blk_status_t res; |
|---|
| 279 | + |
|---|
| 280 | + req = rk_nand_next_request(dev); |
|---|
| 281 | + if (!req) |
|---|
| 282 | + break; |
|---|
| 283 | + |
|---|
| 284 | + spin_unlock_irq(&dev->nand_ops->queue_lock); |
|---|
| 285 | + |
|---|
| 286 | + rknand_device_lock(); |
|---|
| 287 | + res = do_blktrans_all_request(dev, req); |
|---|
| 288 | + rknand_device_unlock(); |
|---|
| 289 | + |
|---|
| 290 | + if (!blk_update_request(req, res, req->__data_len)) { |
|---|
| 291 | + __blk_mq_end_request(req, res); |
|---|
| 292 | + req = NULL; |
|---|
| 293 | + } |
|---|
| 294 | + |
|---|
| 295 | + spin_lock_irq(&dev->nand_ops->queue_lock); |
|---|
| 296 | + } |
|---|
| 297 | +} |
|---|
| 298 | + |
|---|
| 299 | +static blk_status_t rk_nand_queue_rq(struct blk_mq_hw_ctx *hctx, |
|---|
| 300 | + const struct blk_mq_queue_data *bd) |
|---|
| 301 | +{ |
|---|
| 302 | + struct nand_blk_dev *dev; |
|---|
| 303 | + |
|---|
| 304 | + dev = hctx->queue->queuedata; |
|---|
| 305 | + if (!dev) { |
|---|
| 306 | + blk_mq_start_request(bd->rq); |
|---|
| 307 | + return BLK_STS_IOERR; |
|---|
| 308 | + } |
|---|
| 309 | + |
|---|
| 245 | 310 | rk_ftl_gc_do = 0; |
|---|
| 246 | | - rk_ftl_gc_timeout.expires = jiffies + rk_ftl_gc_jiffies; |
|---|
| 247 | | - add_timer(&rk_ftl_gc_timeout); |
|---|
| 311 | + spin_lock_irq(&dev->nand_ops->queue_lock); |
|---|
| 312 | + list_add_tail(&bd->rq->queuelist, &dev->nand_ops->rq_list); |
|---|
| 313 | + rk_nand_blktrans_work(dev); |
|---|
| 314 | + spin_unlock_irq(&dev->nand_ops->queue_lock); |
|---|
| 248 | 315 | |
|---|
| 249 | | - while (!nandr->quit) { |
|---|
| 250 | | - int res; |
|---|
| 251 | | - struct nand_blk_dev *dev; |
|---|
| 316 | + /* wake up gc thread */ |
|---|
| 317 | + rk_ftl_gc_do = 1; |
|---|
| 318 | + wake_up(&dev->nand_ops->thread_wq); |
|---|
| 319 | + |
|---|
| 320 | + return BLK_STS_OK; |
|---|
| 321 | +} |
|---|
| 322 | + |
|---|
| 323 | +static const struct blk_mq_ops rk_nand_mq_ops = { |
|---|
| 324 | + .queue_rq = rk_nand_queue_rq, |
|---|
| 325 | +}; |
|---|
| 326 | + |
|---|
| 327 | +static int nand_gc_thread(void *arg) |
|---|
| 328 | +{ |
|---|
| 329 | + struct nand_blk_ops *nand_ops = arg; |
|---|
| 330 | + int ftl_gc_status = 0; |
|---|
| 331 | + int req_empty_times = 0; |
|---|
| 332 | + int gc_done_times = 0; |
|---|
| 333 | + |
|---|
| 334 | + rk_ftl_gc_jiffies = HZ / 10; |
|---|
| 335 | + rk_ftl_gc_do = 1; |
|---|
| 336 | + |
|---|
| 337 | + while (!nand_ops->quit) { |
|---|
| 252 | 338 | DECLARE_WAITQUEUE(wait, current); |
|---|
| 253 | 339 | |
|---|
| 254 | | - if (!req) |
|---|
| 255 | | - req = blk_fetch_request(rq); |
|---|
| 256 | | - if (!req) { |
|---|
| 257 | | - add_wait_queue(&nandr->thread_wq, &wait); |
|---|
| 258 | | - set_current_state(TASK_INTERRUPTIBLE); |
|---|
| 259 | | - spin_unlock_irq(rq->queue_lock); |
|---|
| 340 | + add_wait_queue(&nand_ops->thread_wq, &wait); |
|---|
| 341 | + set_current_state(TASK_INTERRUPTIBLE); |
|---|
| 342 | + |
|---|
| 343 | + if (rk_ftl_gc_do) { |
|---|
| 344 | + /* do garbage collect at idle state */ |
|---|
| 260 | 345 | if (rknand_device_trylock()) { |
|---|
| 261 | 346 | ftl_gc_status = rk_ftl_garbage_collect(1, 0); |
|---|
| 262 | 347 | rknand_device_unlock(); |
|---|
| 263 | 348 | rk_ftl_gc_jiffies = HZ / 50; |
|---|
| 264 | | - if (ftl_gc_status == 0) |
|---|
| 265 | | - rk_ftl_gc_jiffies = 1 * HZ; |
|---|
| 266 | | - |
|---|
| 349 | + if (ftl_gc_status == 0) { |
|---|
| 350 | + gc_done_times++; |
|---|
| 351 | + if (gc_done_times > 10) |
|---|
| 352 | + rk_ftl_gc_jiffies = 10 * HZ; |
|---|
| 353 | + else |
|---|
| 354 | + rk_ftl_gc_jiffies = 1 * HZ; |
|---|
| 355 | + } else { |
|---|
| 356 | + gc_done_times = 0; |
|---|
| 357 | + } |
|---|
| 267 | 358 | } else { |
|---|
| 268 | | - rk_ftl_gc_jiffies = HZ / 50; |
|---|
| 359 | + rk_ftl_gc_jiffies = 1 * HZ; |
|---|
| 269 | 360 | } |
|---|
| 270 | 361 | req_empty_times++; |
|---|
| 271 | 362 | if (req_empty_times < 10) |
|---|
| 272 | 363 | rk_ftl_gc_jiffies = HZ / 50; |
|---|
| 273 | | - /* 100ms cache write back */ |
|---|
| 364 | + /* cache write back after 100ms */ |
|---|
| 274 | 365 | if (req_empty_times >= 5 && req_empty_times < 7) { |
|---|
| 275 | 366 | rknand_device_lock(); |
|---|
| 276 | 367 | rk_ftl_cache_write_back(); |
|---|
| 277 | 368 | rknand_device_unlock(); |
|---|
| 278 | 369 | } |
|---|
| 279 | | - wait_event_timeout(nandr->thread_wq, |
|---|
| 280 | | - rk_ftl_gc_do || nandr->quit, |
|---|
| 281 | | - rk_ftl_gc_jiffies); |
|---|
| 282 | | - rk_ftl_gc_do = 0; |
|---|
| 283 | | - spin_lock_irq(rq->queue_lock); |
|---|
| 284 | | - remove_wait_queue(&nandr->thread_wq, &wait); |
|---|
| 285 | | - continue; |
|---|
| 286 | 370 | } else { |
|---|
| 287 | | - rk_ftl_gc_jiffies = 1 * HZ; |
|---|
| 288 | 371 | req_empty_times = 0; |
|---|
| 372 | + rk_ftl_gc_jiffies = 1 * HZ; |
|---|
| 289 | 373 | } |
|---|
| 290 | | - |
|---|
| 291 | | - dev = req->rq_disk->private_data; |
|---|
| 292 | | - totle_nsect = (req->__data_len) >> 9; |
|---|
| 293 | | - sector_index = blk_rq_pos(req); |
|---|
| 294 | | - buf = 0; |
|---|
| 295 | | - res = 0; |
|---|
| 296 | | - |
|---|
| 297 | | - op = req_op(req); |
|---|
| 298 | | - if (op == REQ_OP_DISCARD) { |
|---|
| 299 | | - spin_unlock_irq(rq->queue_lock); |
|---|
| 300 | | - rknand_device_lock(); |
|---|
| 301 | | - if (FtlDiscard(blk_rq_pos(req) + |
|---|
| 302 | | - dev->off_size, totle_nsect)) |
|---|
| 303 | | - res = BLK_STS_IOERR; |
|---|
| 304 | | - rknand_device_unlock(); |
|---|
| 305 | | - spin_lock_irq(rq->queue_lock); |
|---|
| 306 | | - if (!__blk_end_request_cur(req, res)) |
|---|
| 307 | | - req = NULL; |
|---|
| 308 | | - continue; |
|---|
| 309 | | - } else if (op == REQ_OP_FLUSH) { |
|---|
| 310 | | - spin_unlock_irq(rq->queue_lock); |
|---|
| 311 | | - rknand_device_lock(); |
|---|
| 312 | | - rk_ftl_cache_write_back(); |
|---|
| 313 | | - rknand_device_unlock(); |
|---|
| 314 | | - spin_lock_irq(rq->queue_lock); |
|---|
| 315 | | - if (!__blk_end_request_cur(req, res)) |
|---|
| 316 | | - req = NULL; |
|---|
| 317 | | - continue; |
|---|
| 318 | | - } else if (op == REQ_OP_READ) { |
|---|
| 319 | | - rw_flag = READ; |
|---|
| 320 | | - } else if (op == REQ_OP_WRITE) { |
|---|
| 321 | | - rw_flag = WRITE; |
|---|
| 322 | | - } else { |
|---|
| 323 | | - __blk_end_request_all(req, BLK_STS_IOERR); |
|---|
| 324 | | - req = NULL; |
|---|
| 325 | | - continue; |
|---|
| 326 | | - } |
|---|
| 327 | | - |
|---|
| 328 | | - if (mtd_read_temp_buffer) { |
|---|
| 329 | | - buf = mtd_read_temp_buffer; |
|---|
| 330 | | - req_check_buffer_align(req, &buf); |
|---|
| 331 | | - |
|---|
| 332 | | - if (rw_flag == WRITE && buf == mtd_read_temp_buffer) { |
|---|
| 333 | | - char *p = buf; |
|---|
| 334 | | - |
|---|
| 335 | | - rq_for_each_segment(bvec, req, rq_iter) { |
|---|
| 336 | | - page_buf = kmap_atomic(bvec.bv_page); |
|---|
| 337 | | - memcpy(p, |
|---|
| 338 | | - page_buf + bvec.bv_offset, |
|---|
| 339 | | - bvec.bv_len); |
|---|
| 340 | | - p += bvec.bv_len; |
|---|
| 341 | | - kunmap_atomic(page_buf); |
|---|
| 342 | | - } |
|---|
| 343 | | - } |
|---|
| 344 | | - |
|---|
| 345 | | - spin_unlock_irq(rq->queue_lock); |
|---|
| 346 | | - res = nand_dev_transfer(dev, |
|---|
| 347 | | - sector_index, |
|---|
| 348 | | - totle_nsect, |
|---|
| 349 | | - buf, |
|---|
| 350 | | - rw_flag, |
|---|
| 351 | | - totle_nsect); |
|---|
| 352 | | - spin_lock_irq(rq->queue_lock); |
|---|
| 353 | | - |
|---|
| 354 | | - if (rw_flag == READ && buf == mtd_read_temp_buffer) { |
|---|
| 355 | | - char *p = buf; |
|---|
| 356 | | - |
|---|
| 357 | | - rq_for_each_segment(bvec, req, rq_iter) { |
|---|
| 358 | | - page_buf = kmap_atomic(bvec.bv_page); |
|---|
| 359 | | - |
|---|
| 360 | | - memcpy(page_buf + bvec.bv_offset, |
|---|
| 361 | | - p, |
|---|
| 362 | | - bvec.bv_len); |
|---|
| 363 | | - p += bvec.bv_len; |
|---|
| 364 | | - kunmap_atomic(page_buf); |
|---|
| 365 | | - } |
|---|
| 366 | | - } |
|---|
| 367 | | - __blk_end_request_all(req, res); |
|---|
| 368 | | - req = NULL; |
|---|
| 369 | | - } else { |
|---|
| 370 | | - while (req) { |
|---|
| 371 | | - sector_index = blk_rq_pos(req); |
|---|
| 372 | | - totle_nsect = blk_rq_cur_bytes(req); |
|---|
| 373 | | - buf = kmap_atomic(bio_page(req->bio)) + |
|---|
| 374 | | - bio_offset(req->bio); |
|---|
| 375 | | - spin_unlock_irq(rq->queue_lock); |
|---|
| 376 | | - res = nand_dev_transfer(dev, |
|---|
| 377 | | - sector_index, |
|---|
| 378 | | - totle_nsect, |
|---|
| 379 | | - buf, |
|---|
| 380 | | - rw_flag, |
|---|
| 381 | | - totle_nsect); |
|---|
| 382 | | - spin_lock_irq(rq->queue_lock); |
|---|
| 383 | | - kunmap_atomic(buf); |
|---|
| 384 | | - if (!__blk_end_request_cur(req, res)) |
|---|
| 385 | | - req = NULL; |
|---|
| 386 | | - } |
|---|
| 387 | | - } |
|---|
| 374 | + wait_event_timeout(nand_ops->thread_wq, nand_ops->quit, |
|---|
| 375 | + rk_ftl_gc_jiffies); |
|---|
| 376 | + remove_wait_queue(&nand_ops->thread_wq, &wait); |
|---|
| 377 | + continue; |
|---|
| 388 | 378 | } |
|---|
| 389 | | - pr_info("nand th quited\n"); |
|---|
| 390 | | - nandr->nand_th_quited = 1; |
|---|
| 391 | | - if (req) |
|---|
| 392 | | - __blk_end_request_all(req, BLK_STS_IOERR); |
|---|
| 393 | | - rk_nand_schedule_enable_config(0); |
|---|
| 394 | | - while ((req = blk_fetch_request(rq)) != NULL) |
|---|
| 395 | | - __blk_end_request_all(req, BLK_STS_IOERR); |
|---|
| 396 | | - spin_unlock_irq(rq->queue_lock); |
|---|
| 397 | | - complete_and_exit(&nandr->thread_exit, 0); |
|---|
| 398 | | - return 0; |
|---|
| 399 | | -} |
|---|
| 400 | | - |
|---|
| 401 | | -static void nand_blk_request(struct request_queue *rq) |
|---|
| 402 | | -{ |
|---|
| 403 | | - struct nand_blk_ops *nandr = rq->queuedata; |
|---|
| 404 | | - struct request *req = NULL; |
|---|
| 405 | | - |
|---|
| 406 | | - if (nandr->nand_th_quited) { |
|---|
| 407 | | - while ((req = blk_fetch_request(rq)) != NULL) |
|---|
| 408 | | - __blk_end_request_all(req, BLK_STS_IOERR); |
|---|
| 409 | | - return; |
|---|
| 410 | | - } |
|---|
| 411 | | - rk_ftl_gc_do = 1; |
|---|
| 412 | | - wake_up(&nandr->thread_wq); |
|---|
| 413 | | -} |
|---|
| 414 | | - |
|---|
| 415 | | -static int rknand_get_part(char *parts, |
|---|
| 416 | | - struct nand_part *this_part, |
|---|
| 417 | | - int *part_index) |
|---|
| 418 | | -{ |
|---|
| 419 | | - char delim; |
|---|
| 420 | | - unsigned int mask_flags; |
|---|
| 421 | | - unsigned long long size, offset = ULLONG_MAX; |
|---|
| 422 | | - char name[40] = "\0"; |
|---|
| 423 | | - |
|---|
| 424 | | - if (*parts == '-') { |
|---|
| 425 | | - size = ULLONG_MAX; |
|---|
| 426 | | - parts++; |
|---|
| 427 | | - } else { |
|---|
| 428 | | - size = memparse(parts, &parts); |
|---|
| 429 | | - } |
|---|
| 430 | | - |
|---|
| 431 | | - if (*parts == '@') { |
|---|
| 432 | | - parts++; |
|---|
| 433 | | - offset = memparse(parts, &parts); |
|---|
| 434 | | - } |
|---|
| 435 | | - |
|---|
| 436 | | - mask_flags = 0; |
|---|
| 437 | | - delim = 0; |
|---|
| 438 | | - |
|---|
| 439 | | - if (*parts == '(') |
|---|
| 440 | | - delim = ')'; |
|---|
| 441 | | - |
|---|
| 442 | | - if (delim) { |
|---|
| 443 | | - char *p; |
|---|
| 444 | | - |
|---|
| 445 | | - p = strchr(parts + 1, delim); |
|---|
| 446 | | - if (!p) |
|---|
| 447 | | - return 0; |
|---|
| 448 | | - strncpy(name, parts + 1, p - (parts + 1)); |
|---|
| 449 | | - parts = p + 1; |
|---|
| 450 | | - } |
|---|
| 451 | | - |
|---|
| 452 | | - if (strncmp(parts, "ro", 2) == 0) { |
|---|
| 453 | | - mask_flags = PART_READONLY; |
|---|
| 454 | | - parts += 2; |
|---|
| 455 | | - } |
|---|
| 456 | | - |
|---|
| 457 | | - if (strncmp(parts, "wo", 2) == 0) { |
|---|
| 458 | | - mask_flags = PART_WRITEONLY; |
|---|
| 459 | | - parts += 2; |
|---|
| 460 | | - } |
|---|
| 461 | | - |
|---|
| 462 | | - this_part->size = (unsigned long)size; |
|---|
| 463 | | - this_part->offset = (unsigned long)offset; |
|---|
| 464 | | - this_part->type = mask_flags; |
|---|
| 465 | | - sprintf(this_part->name, "%s", name); |
|---|
| 466 | | - |
|---|
| 467 | | - if ((++(*part_index) < MAX_PART_COUNT) && (*parts == ',')) |
|---|
| 468 | | - rknand_get_part(++parts, this_part + 1, part_index); |
|---|
| 469 | | - |
|---|
| 470 | | - return 1; |
|---|
| 471 | | -} |
|---|
| 472 | | - |
|---|
| 473 | | -static int nand_prase_cmdline_part(struct nand_part *pdisk_part) |
|---|
| 474 | | -{ |
|---|
| 475 | | - char *pbuf; |
|---|
| 476 | | - int part_num = 0, i; |
|---|
| 477 | | - unsigned int cap_size = rk_ftl_get_capacity(); |
|---|
| 478 | | - char *cmdline; |
|---|
| 479 | | - |
|---|
| 480 | | - cmdline = strstr(saved_command_line, "mtdparts="); |
|---|
| 481 | | - if (!cmdline) |
|---|
| 482 | | - return 0; |
|---|
| 483 | | - cmdline += 9; |
|---|
| 484 | | - if (!memcmp(cmdline, "rk29xxnand:", strlen("rk29xxnand:"))) { |
|---|
| 485 | | - pbuf = cmdline + strlen("rk29xxnand:"); |
|---|
| 486 | | - rknand_get_part(pbuf, pdisk_part, &part_num); |
|---|
| 487 | | - if (part_num) |
|---|
| 488 | | - pdisk_part[part_num - 1].size = cap_size - |
|---|
| 489 | | - pdisk_part[part_num - 1].offset; |
|---|
| 490 | | - |
|---|
| 491 | | - for (i = 0; i < part_num; i++) { |
|---|
| 492 | | - if (pdisk_part[i].size + pdisk_part[i].offset |
|---|
| 493 | | - > cap_size) { |
|---|
| 494 | | - pdisk_part[i].size = cap_size - |
|---|
| 495 | | - pdisk_part[i].offset; |
|---|
| 496 | | - pr_err("partition error....max cap:%x\n", |
|---|
| 497 | | - cap_size); |
|---|
| 498 | | - if (!pdisk_part[i].size) |
|---|
| 499 | | - return i; |
|---|
| 500 | | - else |
|---|
| 501 | | - return (i + 1); |
|---|
| 502 | | - } |
|---|
| 503 | | - } |
|---|
| 504 | | - return part_num; |
|---|
| 505 | | - } |
|---|
| 379 | + pr_info("nand gc quited\n"); |
|---|
| 380 | + nand_ops->nand_th_quited = 1; |
|---|
| 381 | + complete_and_exit(&nand_ops->thread_exit, 0); |
|---|
| 506 | 382 | return 0; |
|---|
| 507 | 383 | } |
|---|
| 508 | 384 | |
|---|
| .. | .. |
|---|
| 515 | 391 | { |
|---|
| 516 | 392 | }; |
|---|
| 517 | 393 | |
|---|
| 518 | | -#define DISABLE_WRITE _IO('V', 0) |
|---|
| 519 | | -#define ENABLE_WRITE _IO('V', 1) |
|---|
| 520 | | -#define DISABLE_READ _IO('V', 2) |
|---|
| 521 | | -#define ENABLE_READ _IO('V', 3) |
|---|
| 522 | 394 | static int rknand_ioctl(struct block_device *bdev, fmode_t mode, |
|---|
| 523 | 395 | unsigned int cmd, |
|---|
| 524 | 396 | unsigned long arg) |
|---|
| .. | .. |
|---|
| 564 | 436 | .owner = THIS_MODULE, |
|---|
| 565 | 437 | }; |
|---|
| 566 | 438 | |
|---|
| 567 | | -static int nand_add_dev(struct nand_blk_ops *nandr, struct nand_part *part) |
|---|
| 439 | +static int nand_add_dev(struct nand_blk_ops *nand_ops, struct nand_part *part) |
|---|
| 568 | 440 | { |
|---|
| 569 | 441 | struct nand_blk_dev *dev; |
|---|
| 570 | 442 | struct gendisk *gd; |
|---|
| .. | .. |
|---|
| 576 | 448 | if (!dev) |
|---|
| 577 | 449 | return -ENOMEM; |
|---|
| 578 | 450 | |
|---|
| 579 | | - gd = alloc_disk(1 << nandr->minorbits); |
|---|
| 451 | + gd = alloc_disk(1 << nand_ops->minorbits); |
|---|
| 580 | 452 | if (!gd) { |
|---|
| 581 | 453 | kfree(dev); |
|---|
| 582 | 454 | return -ENOMEM; |
|---|
| 583 | 455 | } |
|---|
| 584 | | - |
|---|
| 585 | | - dev->nandr = nandr; |
|---|
| 456 | + nand_ops->rq->queuedata = dev; |
|---|
| 457 | + dev->nand_ops = nand_ops; |
|---|
| 586 | 458 | dev->size = part->size; |
|---|
| 587 | 459 | dev->off_size = part->offset; |
|---|
| 588 | | - dev->devnum = nandr->last_dev_index; |
|---|
| 589 | | - list_add_tail(&dev->list, &nandr->devs); |
|---|
| 590 | | - nandr->last_dev_index++; |
|---|
| 460 | + dev->devnum = nand_ops->last_dev_index; |
|---|
| 461 | + list_add_tail(&dev->list, &nand_ops->devs); |
|---|
| 462 | + nand_ops->last_dev_index++; |
|---|
| 591 | 463 | |
|---|
| 592 | | - gd->major = nandr->major; |
|---|
| 593 | | - gd->first_minor = (dev->devnum) << nandr->minorbits; |
|---|
| 464 | + gd->major = nand_ops->major; |
|---|
| 465 | + gd->first_minor = (dev->devnum) << nand_ops->minorbits; |
|---|
| 594 | 466 | |
|---|
| 595 | 467 | gd->fops = &nand_blktrans_ops; |
|---|
| 596 | 468 | |
|---|
| 597 | | - if (part->name[0]) { |
|---|
| 598 | | - snprintf(gd->disk_name, |
|---|
| 599 | | - sizeof(gd->disk_name), |
|---|
| 600 | | - "%s_%s", |
|---|
| 601 | | - nandr->name, |
|---|
| 602 | | - part->name); |
|---|
| 603 | | - } else { |
|---|
| 604 | | - gd->flags = GENHD_FL_EXT_DEVT; |
|---|
| 605 | | - gd->minors = 255; |
|---|
| 606 | | - snprintf(gd->disk_name, |
|---|
| 607 | | - sizeof(gd->disk_name), |
|---|
| 608 | | - "%s%d", |
|---|
| 609 | | - nandr->name, |
|---|
| 610 | | - dev->devnum); |
|---|
| 611 | | - } |
|---|
| 469 | + gd->flags = GENHD_FL_EXT_DEVT; |
|---|
| 470 | + gd->minors = 255; |
|---|
| 471 | + snprintf(gd->disk_name, |
|---|
| 472 | + sizeof(gd->disk_name), |
|---|
| 473 | + "%s%d", |
|---|
| 474 | + nand_ops->name, |
|---|
| 475 | + dev->devnum); |
|---|
| 476 | + |
|---|
| 612 | 477 | set_capacity(gd, dev->size); |
|---|
| 613 | 478 | |
|---|
| 614 | 479 | gd->private_data = dev; |
|---|
| 615 | 480 | dev->blkcore_priv = gd; |
|---|
| 616 | | - gd->queue = nandr->rq; |
|---|
| 617 | | - gd->queue->bypass_depth = 1; |
|---|
| 481 | + gd->queue = nand_ops->rq; |
|---|
| 618 | 482 | |
|---|
| 619 | 483 | if (part->type == PART_NO_ACCESS) |
|---|
| 620 | 484 | dev->disable_access = 1; |
|---|
| .. | .. |
|---|
| 628 | 492 | if (dev->readonly) |
|---|
| 629 | 493 | set_disk_ro(gd, 1); |
|---|
| 630 | 494 | |
|---|
| 631 | | - if (gd->flags != GENHD_FL_EXT_DEVT) |
|---|
| 632 | | - add_disk(gd); |
|---|
| 633 | | - else |
|---|
| 634 | | - device_add_disk(g_nand_device, gd); |
|---|
| 495 | + device_add_disk(g_nand_device, gd, NULL); |
|---|
| 635 | 496 | |
|---|
| 636 | 497 | return 0; |
|---|
| 637 | 498 | } |
|---|
| .. | .. |
|---|
| 646 | 507 | del_gendisk(gd); |
|---|
| 647 | 508 | put_disk(gd); |
|---|
| 648 | 509 | kfree(dev); |
|---|
| 510 | + |
|---|
| 649 | 511 | return 0; |
|---|
| 650 | 512 | } |
|---|
| 651 | 513 | |
|---|
| 652 | | -int nand_blk_add_whole_disk(void) |
|---|
| 514 | +static int nand_blk_register(struct nand_blk_ops *nand_ops) |
|---|
| 653 | 515 | { |
|---|
| 654 | 516 | struct nand_part part; |
|---|
| 655 | | - |
|---|
| 656 | | - part.offset = 0; |
|---|
| 657 | | - part.size = rk_ftl_get_capacity(); |
|---|
| 658 | | - part.type = 0; |
|---|
| 659 | | - strncpy(part.name, "rknand", sizeof(part.name)); |
|---|
| 660 | | - nand_add_dev(&mytr, &part); |
|---|
| 661 | | - return 0; |
|---|
| 662 | | -} |
|---|
| 663 | | - |
|---|
| 664 | | -static int nand_blk_register(struct nand_blk_ops *nandr) |
|---|
| 665 | | -{ |
|---|
| 666 | | - int i, ret; |
|---|
| 667 | | - u32 part_size; |
|---|
| 668 | | - struct nand_part part; |
|---|
| 517 | + int ret; |
|---|
| 669 | 518 | |
|---|
| 670 | 519 | rk_nand_schedule_enable_config(1); |
|---|
| 671 | | - nandr->quit = 0; |
|---|
| 672 | | - nandr->nand_th_quited = 0; |
|---|
| 520 | + nand_ops->quit = 0; |
|---|
| 521 | + nand_ops->nand_th_quited = 0; |
|---|
| 673 | 522 | |
|---|
| 674 | | - mtd_read_temp_buffer = kmalloc(MTD_RW_SECTORS * 512, |
|---|
| 675 | | - GFP_KERNEL | GFP_DMA); |
|---|
| 676 | | - |
|---|
| 677 | | - ret = register_blkdev(nandr->major, nandr->name); |
|---|
| 523 | + ret = register_blkdev(nand_ops->major, nand_ops->name); |
|---|
| 678 | 524 | if (ret) |
|---|
| 679 | | - return -1; |
|---|
| 525 | + return ret; |
|---|
| 680 | 526 | |
|---|
| 681 | | - spin_lock_init(&nandr->queue_lock); |
|---|
| 682 | | - init_completion(&nandr->thread_exit); |
|---|
| 683 | | - init_waitqueue_head(&nandr->thread_wq); |
|---|
| 684 | | - rknand_device_lock_init(); |
|---|
| 685 | | - |
|---|
| 686 | | - nandr->rq = blk_init_queue(nand_blk_request, &nandr->queue_lock); |
|---|
| 687 | | - if (!nandr->rq) { |
|---|
| 688 | | - unregister_blkdev(nandr->major, nandr->name); |
|---|
| 689 | | - return -1; |
|---|
| 527 | + mtd_read_temp_buffer = kmalloc(MTD_RW_SECTORS * 512, GFP_KERNEL | GFP_DMA); |
|---|
| 528 | + if (!mtd_read_temp_buffer) { |
|---|
| 529 | + ret = -ENOMEM; |
|---|
| 530 | + goto mtd_buffer_error; |
|---|
| 690 | 531 | } |
|---|
| 691 | 532 | |
|---|
| 692 | | - blk_queue_max_hw_sectors(nandr->rq, MTD_RW_SECTORS); |
|---|
| 693 | | - blk_queue_max_segments(nandr->rq, MTD_RW_SECTORS); |
|---|
| 533 | + init_completion(&nand_ops->thread_exit); |
|---|
| 534 | + init_waitqueue_head(&nand_ops->thread_wq); |
|---|
| 535 | + rknand_device_lock_init(); |
|---|
| 694 | 536 | |
|---|
| 695 | | - blk_queue_flag_set(QUEUE_FLAG_DISCARD, nandr->rq); |
|---|
| 696 | | - blk_queue_max_discard_sectors(nandr->rq, UINT_MAX >> 9); |
|---|
| 537 | + /* Create the request queue */ |
|---|
| 538 | + spin_lock_init(&nand_ops->queue_lock); |
|---|
| 539 | + INIT_LIST_HEAD(&nand_ops->rq_list); |
|---|
| 540 | + |
|---|
| 541 | + nand_ops->tag_set = kzalloc(sizeof(*nand_ops->tag_set), GFP_KERNEL); |
|---|
| 542 | + if (!nand_ops->tag_set) { |
|---|
| 543 | + ret = -ENOMEM; |
|---|
| 544 | + goto tag_set_error; |
|---|
| 545 | + } |
|---|
| 546 | + |
|---|
| 547 | + nand_ops->rq = blk_mq_init_sq_queue(nand_ops->tag_set, &rk_nand_mq_ops, 1, |
|---|
| 548 | + BLK_MQ_F_SHOULD_MERGE | BLK_MQ_F_BLOCKING); |
|---|
| 549 | + if (IS_ERR(nand_ops->rq)) { |
|---|
| 550 | + ret = PTR_ERR(nand_ops->rq); |
|---|
| 551 | + nand_ops->rq = NULL; |
|---|
| 552 | + goto rq_init_error; |
|---|
| 553 | + } |
|---|
| 554 | + |
|---|
| 555 | + blk_queue_max_hw_sectors(nand_ops->rq, MTD_RW_SECTORS); |
|---|
| 556 | + blk_queue_max_segments(nand_ops->rq, MTD_RW_SECTORS); |
|---|
| 557 | + |
|---|
| 558 | + blk_queue_flag_set(QUEUE_FLAG_DISCARD, nand_ops->rq); |
|---|
| 559 | + blk_queue_max_discard_sectors(nand_ops->rq, UINT_MAX >> 9); |
|---|
| 697 | 560 | /* discard_granularity config to one nand page size 32KB*/ |
|---|
| 698 | | - nandr->rq->limits.discard_granularity = 64 << 9; |
|---|
| 561 | + nand_ops->rq->limits.discard_granularity = 64 << 9; |
|---|
| 699 | 562 | |
|---|
| 700 | | - nandr->rq->queuedata = nandr; |
|---|
| 701 | | - INIT_LIST_HEAD(&nandr->devs); |
|---|
| 702 | | - kthread_run(nand_blktrans_thread, (void *)nandr, "rknand"); |
|---|
| 563 | + INIT_LIST_HEAD(&nand_ops->devs); |
|---|
| 564 | + kthread_run(nand_gc_thread, (void *)nand_ops, "rknand_gc"); |
|---|
| 703 | 565 | |
|---|
| 704 | | - g_max_part_num = nand_prase_cmdline_part(disk_array); |
|---|
| 705 | | - |
|---|
| 706 | | - nandr->last_dev_index = 0; |
|---|
| 566 | + nand_ops->last_dev_index = 0; |
|---|
| 707 | 567 | part.offset = 0; |
|---|
| 708 | 568 | part.size = rk_ftl_get_capacity(); |
|---|
| 709 | 569 | part.type = 0; |
|---|
| 710 | 570 | part.name[0] = 0; |
|---|
| 711 | | - nand_add_dev(&mytr, &part); |
|---|
| 712 | | - |
|---|
| 713 | | - if (g_max_part_num) { |
|---|
| 714 | | - for (i = 0; i < g_max_part_num; i++) { |
|---|
| 715 | | - part_size = (disk_array[i].offset + disk_array[i].size); |
|---|
| 716 | | - pr_info("%10s: 0x%09llx -- 0x%09llx (%llu MB)\n", |
|---|
| 717 | | - disk_array[i].name, |
|---|
| 718 | | - (u64)disk_array[i].offset * 512, |
|---|
| 719 | | - (u64)part_size * 512, |
|---|
| 720 | | - (u64)disk_array[i].size / 2048); |
|---|
| 721 | | - nand_add_dev(nandr, &disk_array[i]); |
|---|
| 722 | | - } |
|---|
| 723 | | - } |
|---|
| 571 | + nand_add_dev(nand_ops, &part); |
|---|
| 724 | 572 | |
|---|
| 725 | 573 | rknand_create_procfs(); |
|---|
| 726 | 574 | rk_ftl_storage_sys_init(); |
|---|
| .. | .. |
|---|
| 735 | 583 | } |
|---|
| 736 | 584 | |
|---|
| 737 | 585 | return 0; |
|---|
| 586 | + |
|---|
| 587 | +rq_init_error: |
|---|
| 588 | + kfree(nand_ops->tag_set); |
|---|
| 589 | +tag_set_error: |
|---|
| 590 | + kfree(mtd_read_temp_buffer); |
|---|
| 591 | + mtd_read_temp_buffer = NULL; |
|---|
| 592 | +mtd_buffer_error: |
|---|
| 593 | + unregister_blkdev(nand_ops->major, nand_ops->name); |
|---|
| 594 | + |
|---|
| 595 | + return ret; |
|---|
| 738 | 596 | } |
|---|
| 739 | 597 | |
|---|
| 740 | | -static void nand_blk_unregister(struct nand_blk_ops *nandr) |
|---|
| 598 | +static void nand_blk_unregister(struct nand_blk_ops *nand_ops) |
|---|
| 741 | 599 | { |
|---|
| 742 | 600 | struct list_head *this, *next; |
|---|
| 743 | 601 | |
|---|
| 744 | 602 | if (!rk_nand_dev_initialised) |
|---|
| 745 | 603 | return; |
|---|
| 746 | | - nandr->quit = 1; |
|---|
| 747 | | - wake_up(&nandr->thread_wq); |
|---|
| 748 | | - wait_for_completion(&nandr->thread_exit); |
|---|
| 749 | | - list_for_each_safe(this, next, &nandr->devs) { |
|---|
| 604 | + nand_ops->quit = 1; |
|---|
| 605 | + wake_up(&nand_ops->thread_wq); |
|---|
| 606 | + wait_for_completion(&nand_ops->thread_exit); |
|---|
| 607 | + list_for_each_safe(this, next, &nand_ops->devs) { |
|---|
| 750 | 608 | struct nand_blk_dev *dev |
|---|
| 751 | 609 | = list_entry(this, struct nand_blk_dev, list); |
|---|
| 752 | 610 | |
|---|
| 753 | 611 | nand_remove_dev(dev); |
|---|
| 754 | 612 | } |
|---|
| 755 | | - blk_cleanup_queue(nandr->rq); |
|---|
| 756 | | - unregister_blkdev(nandr->major, nandr->name); |
|---|
| 613 | + blk_cleanup_queue(nand_ops->rq); |
|---|
| 614 | + unregister_blkdev(nand_ops->major, nand_ops->name); |
|---|
| 757 | 615 | } |
|---|
| 758 | 616 | |
|---|
| 759 | 617 | void rknand_dev_flush(void) |
|---|