]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - sys/dev/nand/nandsim.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / sys / dev / nand / nandsim.c
1 /*-
2  * Copyright (C) 2009-2012 Semihalf
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26
27 /* Simulated NAND controller driver */
28
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/proc.h>
35 #include <sys/bus.h>
36 #include <sys/conf.h>
37 #include <sys/kernel.h>
38 #include <sys/module.h>
39 #include <sys/malloc.h>
40
41 #include <dev/nand/nand.h>
42 #include <dev/nand/nandsim.h>
43 #include <dev/nand/nandsim_chip.h>
44 #include <dev/nand/nandsim_log.h>
45 #include <dev/nand/nandsim_swap.h>
46
47 struct sim_param sim;
48 struct sim_ctrl_conf ctrls[MAX_SIM_DEV];
49
50 static struct cdev *nandsim_dev;
51 static d_ioctl_t nandsim_ioctl;
52
53 static void nandsim_init_sim_param(struct sim_param *);
54 static int nandsim_create_ctrl(struct sim_ctrl *);
55 static int nandsim_destroy_ctrl(int);
56 static int nandsim_ctrl_status(struct sim_ctrl *);
57 static int nandsim_create_chip(struct sim_chip *);
58 static int nandsim_destroy_chip(struct sim_ctrl_chip *);
59 static int nandsim_chip_status(struct sim_chip *);
60 static int nandsim_start_ctrl(int);
61 static int nandsim_stop_ctrl(int);
62 static int nandsim_inject_error(struct sim_error *);
63 static int nandsim_get_block_state(struct sim_block_state *);
64 static int nandsim_set_block_state(struct sim_block_state *);
65 static int nandsim_modify(struct sim_mod *);
66 static int nandsim_dump(struct sim_dump *);
67 static int nandsim_restore(struct sim_dump *);
68 static int nandsim_freeze(struct sim_ctrl_chip *);
69 static void nandsim_print_log(struct sim_log *);
70 static struct nandsim_chip *get_nandsim_chip(uint8_t, uint8_t);
71
72 static struct cdevsw nandsim_cdevsw = {
73         .d_version =    D_VERSION,
74         .d_ioctl =      nandsim_ioctl,
75         .d_name =       "nandsim",
76 };
77
78 int
79 nandsim_ioctl(struct cdev *dev, u_long cmd, caddr_t data,
80     int flags, struct thread *td)
81 {
82         int ret = 0;
83
84         switch (cmd) {
85         case NANDSIM_SIM_PARAM:
86                 nandsim_init_sim_param((struct sim_param *)data);
87                 break;
88         case NANDSIM_CREATE_CTRL:
89                 ret = nandsim_create_ctrl((struct sim_ctrl *)data);
90                 break;
91         case NANDSIM_DESTROY_CTRL:
92                 ret = nandsim_destroy_ctrl(*(int *)data);
93                 break;
94         case NANDSIM_STATUS_CTRL:
95                 ret = nandsim_ctrl_status((struct sim_ctrl *)data);
96                 break;
97         case NANDSIM_CREATE_CHIP:
98                 ret = nandsim_create_chip((struct sim_chip *)data);
99                 break;
100         case NANDSIM_DESTROY_CHIP:
101                 ret = nandsim_destroy_chip((struct sim_ctrl_chip *)data);
102                 break;
103         case NANDSIM_STATUS_CHIP:
104                 ret = nandsim_chip_status((struct sim_chip *)data);
105                 break;
106         case NANDSIM_MODIFY:
107                 ret = nandsim_modify((struct sim_mod *)data);
108                 break;
109         case NANDSIM_START_CTRL:
110                 ret = nandsim_start_ctrl(*(int *)data);
111                 break;
112         case NANDSIM_STOP_CTRL:
113                 ret = nandsim_stop_ctrl(*(int *)data);
114                 break;
115         case NANDSIM_INJECT_ERROR:
116                 ret = nandsim_inject_error((struct sim_error *)data);
117                 break;
118         case NANDSIM_SET_BLOCK_STATE:
119                 ret = nandsim_set_block_state((struct sim_block_state *)data);
120                 break;
121         case NANDSIM_GET_BLOCK_STATE:
122                 ret = nandsim_get_block_state((struct sim_block_state *)data);
123                 break;
124         case NANDSIM_PRINT_LOG:
125                 nandsim_print_log((struct sim_log *)data);
126                 break;
127         case NANDSIM_DUMP:
128                 ret = nandsim_dump((struct sim_dump *)data);
129                 break;
130         case NANDSIM_RESTORE:
131                 ret = nandsim_restore((struct sim_dump *)data);
132                 break;
133         case NANDSIM_FREEZE:
134                 ret = nandsim_freeze((struct sim_ctrl_chip *)data);
135                 break;
136         default:
137                 ret = EINVAL;
138                 break;
139         }
140
141         return (ret);
142 }
143
144 static void
145 nandsim_init_sim_param(struct sim_param *param)
146 {
147
148         if (!param)
149                 return;
150
151         nand_debug(NDBG_SIM,"log level:%d output %d", param->log_level,
152             param->log_output);
153         nandsim_log_level = param->log_level;
154         nandsim_log_output = param->log_output;
155 }
156
157 static int
158 nandsim_create_ctrl(struct sim_ctrl *ctrl)
159 {
160         struct sim_ctrl_conf *sim_ctrl;
161
162         nand_debug(NDBG_SIM,"create controller num:%d cs:%d",ctrl->num,
163             ctrl->num_cs);
164
165         if (ctrl->num >= MAX_SIM_DEV) {
166                 return (EINVAL);
167         }
168
169         sim_ctrl = &ctrls[ctrl->num];
170         if(sim_ctrl->created)
171                 return (EEXIST);
172
173         sim_ctrl->num = ctrl->num;
174         sim_ctrl->num_cs = ctrl->num_cs;
175         sim_ctrl->ecc = ctrl->ecc;
176         memcpy(sim_ctrl->ecc_layout, ctrl->ecc_layout,
177             MAX_ECC_BYTES * sizeof(ctrl->ecc_layout[0]));
178         strlcpy(sim_ctrl->filename, ctrl->filename,
179             FILENAME_SIZE);
180         sim_ctrl->created = 1;
181
182         return (0);
183 }
184
185 static int
186 nandsim_destroy_ctrl(int ctrl_num)
187 {
188
189         nand_debug(NDBG_SIM,"destroy controller num:%d", ctrl_num);
190
191         if (ctrl_num >= MAX_SIM_DEV) {
192                 return (EINVAL);
193         }
194
195         if (!ctrls[ctrl_num].created) {
196                 return (ENODEV);
197         }
198
199         if (ctrls[ctrl_num].running) {
200                 return (EBUSY);
201         }
202
203         memset(&ctrls[ctrl_num], 0, sizeof(ctrls[ctrl_num]));
204
205         return (0);
206 }
207
208 static int
209 nandsim_ctrl_status(struct sim_ctrl *ctrl)
210 {
211
212         nand_debug(NDBG_SIM,"status controller num:%d cs:%d",ctrl->num,
213             ctrl->num_cs);
214
215         if (ctrl->num >= MAX_SIM_DEV) {
216                 return (EINVAL);
217         }
218
219         ctrl->num_cs = ctrls[ctrl->num].num_cs;
220         ctrl->ecc = ctrls[ctrl->num].ecc;
221         memcpy(ctrl->ecc_layout, ctrls[ctrl->num].ecc_layout,
222             MAX_ECC_BYTES * sizeof(ctrl->ecc_layout[0]));
223         strlcpy(ctrl->filename, ctrls[ctrl->num].filename,
224             FILENAME_SIZE);
225         ctrl->running = ctrls[ctrl->num].running;
226         ctrl->created = ctrls[ctrl->num].created;
227
228         return (0);
229 }
230
231 static int
232 nandsim_create_chip(struct sim_chip *chip)
233 {
234         struct sim_chip *sim_chip;
235
236         nand_debug(NDBG_SIM,"create chip num:%d at ctrl:%d", chip->num,
237             chip->ctrl_num);
238
239         if (chip->ctrl_num >= MAX_SIM_DEV ||
240             chip->num >= MAX_CTRL_CS) {
241                 return (EINVAL);
242         }
243
244         if (ctrls[chip->ctrl_num].chips[chip->num]) {
245                 return (EEXIST);
246         }
247
248         sim_chip = malloc(sizeof(*sim_chip), M_NANDSIM,
249             M_WAITOK);
250         if (sim_chip == NULL) {
251                 return (ENOMEM);
252         }
253
254         memcpy(sim_chip, chip, sizeof(*sim_chip));
255         ctrls[chip->ctrl_num].chips[chip->num] = sim_chip;
256         sim_chip->created = 1;
257
258         return (0);
259 }
260
261 static int
262 nandsim_destroy_chip(struct sim_ctrl_chip *chip)
263 {
264         struct sim_ctrl_conf *ctrl_conf;
265
266         nand_debug(NDBG_SIM,"destroy chip num:%d at ctrl:%d", chip->chip_num,
267             chip->ctrl_num);
268
269         if (chip->ctrl_num >= MAX_SIM_DEV ||
270             chip->chip_num >= MAX_CTRL_CS)
271                 return (EINVAL);
272
273         ctrl_conf = &ctrls[chip->ctrl_num];
274
275         if (!ctrl_conf->created || !ctrl_conf->chips[chip->chip_num])
276                 return (ENODEV);
277
278         if (ctrl_conf->running)
279                 return (EBUSY);
280
281         free(ctrl_conf->chips[chip->chip_num], M_NANDSIM);
282         ctrl_conf->chips[chip->chip_num] = NULL;
283
284         return (0);
285 }
286
287 static int
288 nandsim_chip_status(struct sim_chip *chip)
289 {
290         struct sim_ctrl_conf *ctrl_conf;
291
292         nand_debug(NDBG_SIM,"status for chip num:%d at ctrl:%d", chip->num,
293             chip->ctrl_num);
294
295         if (chip->ctrl_num >= MAX_SIM_DEV &&
296             chip->num >= MAX_CTRL_CS)
297                 return (EINVAL);
298
299         ctrl_conf = &ctrls[chip->ctrl_num];
300         if (!ctrl_conf->chips[chip->num])
301                 chip->created = 0;
302         else
303                 memcpy(chip, ctrl_conf->chips[chip->num], sizeof(*chip));
304
305         return (0);
306 }
307
308 static int
309 nandsim_start_ctrl(int num)
310 {
311         device_t nexus, ndev;
312         devclass_t nexus_devclass;
313         int ret = 0;
314
315         nand_debug(NDBG_SIM,"start ctlr num:%d", num);
316
317         if (num >= MAX_SIM_DEV)
318                 return (EINVAL);
319
320         if (!ctrls[num].created)
321                 return (ENODEV);
322
323         if (ctrls[num].running)
324                 return (EBUSY);
325
326         /* We will add our device as a child of the nexus0 device */
327         if (!(nexus_devclass = devclass_find("nexus")) ||
328             !(nexus = devclass_get_device(nexus_devclass, 0)))
329                 return (EFAULT);
330
331         /*
332          * Create a newbus device representing this frontend instance
333          *
334          * XXX powerpc nexus doesn't implement bus_add_child, so child
335          * must be added by device_add_child().
336          */
337 #if defined(__powerpc__)
338         ndev = device_add_child(nexus, "nandsim", num);
339 #else
340         ndev = BUS_ADD_CHILD(nexus, 0, "nandsim", num);
341 #endif
342         if (!ndev)
343                 return (EFAULT);
344
345         mtx_lock(&Giant);
346         ret = device_probe_and_attach(ndev);
347         mtx_unlock(&Giant);
348
349         if (ret == 0) {
350                 ctrls[num].sim_ctrl_dev = ndev;
351                 ctrls[num].running = 1;
352         }
353
354         return (ret);
355 }
356
357 static int
358 nandsim_stop_ctrl(int num)
359 {
360         device_t nexus;
361         devclass_t nexus_devclass;
362         int ret = 0;
363
364         nand_debug(NDBG_SIM,"stop controller num:%d", num);
365
366         if (num >= MAX_SIM_DEV) {
367                 return (EINVAL);
368         }
369
370         if (!ctrls[num].created || !ctrls[num].running) {
371                 return (ENODEV);
372         }
373
374         /* We will add our device as a child of the nexus0 device */
375         if (!(nexus_devclass = devclass_find("nexus")) ||
376             !(nexus = devclass_get_device(nexus_devclass, 0))) {
377                 return (ENODEV);
378         }
379
380         mtx_lock(&Giant);
381         if (ctrls[num].sim_ctrl_dev) {
382                 ret = device_delete_child(nexus, ctrls[num].sim_ctrl_dev);
383                 ctrls[num].sim_ctrl_dev = NULL;
384         }
385         mtx_unlock(&Giant);
386
387         ctrls[num].running = 0;
388
389         return (ret);
390 }
391
392 static struct nandsim_chip *
393 get_nandsim_chip(uint8_t ctrl_num, uint8_t chip_num)
394 {
395         struct nandsim_softc *sc;
396
397         if (!ctrls[ctrl_num].sim_ctrl_dev)
398                 return (NULL);
399
400         sc = device_get_softc(ctrls[ctrl_num].sim_ctrl_dev);
401         return (sc->chips[chip_num]);
402 }
403
404 static void
405 nandsim_print_log(struct sim_log *sim_log)
406 {
407         struct nandsim_softc *sc;
408         int len1, len2;
409
410         if (!ctrls[sim_log->ctrl_num].sim_ctrl_dev)
411                 return;
412
413         sc = device_get_softc(ctrls[sim_log->ctrl_num].sim_ctrl_dev);
414         if (sc->log_buff) {
415                 len1 = strlen(&sc->log_buff[sc->log_idx + 1]);
416                 if (len1 >= sim_log->len)
417                         len1 = sim_log->len;
418                 copyout(&sc->log_buff[sc->log_idx + 1], sim_log->log, len1);
419                 len2 = strlen(sc->log_buff);
420                 if (len2 >= (sim_log->len - len1))
421                         len2 = (sim_log->len - len1);
422                 copyout(sc->log_buff, &sim_log->log[len1], len2);
423                 sim_log->len = len1 + len2;
424         }
425 }
426
427 static int
428 nandsim_inject_error(struct sim_error *error)
429 {
430         struct nandsim_chip *chip;
431         struct block_space *bs;
432         struct onfi_params *param;
433         int page, page_size, block, offset;
434
435         nand_debug(NDBG_SIM,"inject error for chip %d at ctrl %d\n",
436             error->chip_num, error->ctrl_num);
437
438         if (error->ctrl_num >= MAX_SIM_DEV ||
439             error->chip_num >= MAX_CTRL_CS)
440                 return (EINVAL);
441
442         if (!ctrls[error->ctrl_num].created || !ctrls[error->ctrl_num].running)
443                 return (ENODEV);
444
445         chip = get_nandsim_chip(error->ctrl_num, error->chip_num);
446         param = &chip->params;
447         page_size = param->bytes_per_page + param->spare_bytes_per_page;
448         block = error->page_num / param->pages_per_block;
449         page = error->page_num % param->pages_per_block;
450
451         bs = get_bs(chip->swap, block, 1);
452         if (!bs)
453                 return (EINVAL);
454
455         offset = (page * page_size) + error->column;
456         memset(&bs->blk_ptr[offset], error->pattern, error->len);
457
458         return (0);
459 }
460
461 static int
462 nandsim_set_block_state(struct sim_block_state *bs)
463 {
464         struct onfi_params *params;
465         struct nandsim_chip *chip;
466         int blocks;
467
468         nand_debug(NDBG_SIM,"set block state for %d:%d block %d\n",
469             bs->chip_num, bs->ctrl_num, bs->block_num);
470
471         if (bs->ctrl_num >= MAX_SIM_DEV ||
472             bs->chip_num >= MAX_CTRL_CS)
473                 return (EINVAL);
474
475         chip = get_nandsim_chip(bs->ctrl_num, bs->chip_num);
476         params = &chip->params;
477         blocks = params->luns * params->blocks_per_lun;
478
479         if (bs->block_num > blocks)
480                 return (EINVAL);
481
482         chip->blk_state[bs->block_num].is_bad = bs->state;
483
484         if (bs->wearout >= 0)
485                 chip->blk_state[bs->block_num].wear_lev = bs->wearout;
486
487         return (0);
488 }
489
490 static int
491 nandsim_get_block_state(struct sim_block_state *bs)
492 {
493         struct onfi_params *params;
494         struct nandsim_chip *chip;
495         int blocks;
496
497         if (bs->ctrl_num >= MAX_SIM_DEV ||
498             bs->chip_num >= MAX_CTRL_CS)
499                 return (EINVAL);
500
501         nand_debug(NDBG_SIM,"get block state for %d:%d block %d\n",
502             bs->chip_num, bs->ctrl_num, bs->block_num);
503
504         chip = get_nandsim_chip(bs->ctrl_num, bs->chip_num);
505         params = &chip->params;
506         blocks = params->luns * params->blocks_per_lun;
507
508         if (bs->block_num > blocks)
509                 return (EINVAL);
510
511         bs->state = chip->blk_state[bs->block_num].is_bad;
512         bs->wearout = chip->blk_state[bs->block_num].wear_lev;
513
514         return (0);
515 }
516
517 static int
518 nandsim_dump(struct sim_dump *dump)
519 {
520         struct nandsim_chip *chip;
521         struct block_space *bs;
522         int blk_size;
523
524         nand_debug(NDBG_SIM,"dump chip %d %d\n", dump->ctrl_num, dump->chip_num);
525
526         if (dump->ctrl_num >= MAX_SIM_DEV ||
527             dump->chip_num >= MAX_CTRL_CS)
528                 return (EINVAL);
529
530         chip = get_nandsim_chip(dump->ctrl_num, dump->chip_num);
531         blk_size = chip->cg.block_size +
532             (chip->cg.oob_size * chip->cg.pgs_per_blk);
533
534         bs = get_bs(chip->swap, dump->block_num, 0);
535         if (!bs)
536                 return (EINVAL);
537
538         if (dump->len > blk_size)
539                 dump->len = blk_size;
540
541         copyout(bs->blk_ptr, dump->data, dump->len);
542
543         return (0);
544 }
545
546 static int
547 nandsim_restore(struct sim_dump *dump)
548 {
549         struct nandsim_chip *chip;
550         struct block_space *bs;
551         int blk_size;
552
553         nand_debug(NDBG_SIM,"restore chip %d %d\n", dump->ctrl_num,
554             dump->chip_num);
555
556         if (dump->ctrl_num >= MAX_SIM_DEV ||
557             dump->chip_num >= MAX_CTRL_CS)
558                 return (EINVAL);
559
560         chip = get_nandsim_chip(dump->ctrl_num, dump->chip_num);
561         blk_size = chip->cg.block_size +
562             (chip->cg.oob_size * chip->cg.pgs_per_blk);
563
564         bs = get_bs(chip->swap, dump->block_num, 1);
565         if (!bs)
566                 return (EINVAL);
567
568         if (dump->len > blk_size)
569                 dump->len = blk_size;
570
571
572         copyin(dump->data, bs->blk_ptr, dump->len);
573
574         return (0);
575 }
576
577 static int
578 nandsim_freeze(struct sim_ctrl_chip *ctrl_chip)
579 {
580         struct nandsim_chip *chip;
581
582         if (ctrl_chip->ctrl_num >= MAX_SIM_DEV ||
583             ctrl_chip->chip_num >= MAX_CTRL_CS)
584                 return (EINVAL);
585
586         chip = get_nandsim_chip(ctrl_chip->ctrl_num, ctrl_chip->chip_num);
587         nandsim_chip_freeze(chip);
588
589         return (0);
590 }
591
592 static int
593 nandsim_modify(struct sim_mod *mod)
594 {
595         struct sim_chip *sim_conf = NULL;
596         struct nandsim_chip *sim_chip = NULL;
597
598         nand_debug(NDBG_SIM,"modify ctlr %d chip %d", mod->ctrl_num,
599             mod->chip_num);
600
601         if (mod->field != SIM_MOD_LOG_LEVEL) {
602                 if (mod->ctrl_num >= MAX_SIM_DEV ||
603                     mod->chip_num >= MAX_CTRL_CS)
604                         return (EINVAL);
605
606                 sim_conf = ctrls[mod->ctrl_num].chips[mod->chip_num];
607                 sim_chip = get_nandsim_chip(mod->ctrl_num, mod->chip_num);
608         }
609
610         switch (mod->field) {
611         case SIM_MOD_LOG_LEVEL:
612                 nandsim_log_level = mod->new_value;
613                 break;
614         case SIM_MOD_ERASE_TIME:
615                 sim_conf->erase_time = sim_chip->erase_delay = mod->new_value;
616                 break;
617         case SIM_MOD_PROG_TIME:
618                 sim_conf->prog_time = sim_chip->prog_delay = mod->new_value;
619                 break;
620         case SIM_MOD_READ_TIME:
621                 sim_conf->read_time = sim_chip->read_delay = mod->new_value;
622                 break;
623         case SIM_MOD_ERROR_RATIO:
624                 sim_conf->error_ratio = mod->new_value;
625                 sim_chip->error_ratio = mod->new_value;
626                 break;
627         default:
628                 break;
629         }
630
631         return (0);
632 }
633 static int
634 nandsim_modevent(module_t mod __unused, int type, void *data __unused)
635 {
636         struct sim_ctrl_chip chip_ctrl;
637         int i, j;
638
639         switch (type) {
640         case MOD_LOAD:
641                 nandsim_dev = make_dev(&nandsim_cdevsw, 0,
642                     UID_ROOT, GID_WHEEL, 0666, "nandsim.ioctl");
643                 break;
644         case MOD_UNLOAD:
645                 for (i = 0; i < MAX_SIM_DEV; i++) {
646                         nandsim_stop_ctrl(i);
647                         chip_ctrl.ctrl_num = i;
648                         for (j = 0; j < MAX_CTRL_CS; j++) {
649                                 chip_ctrl.chip_num = j;
650                                 nandsim_destroy_chip(&chip_ctrl);
651                         }
652                         nandsim_destroy_ctrl(i);
653                 }
654                 destroy_dev(nandsim_dev);
655                 break;
656         case MOD_SHUTDOWN:
657                 break;
658         default:
659                 return (EOPNOTSUPP);
660         }
661         return (0);
662 }
663
664 DEV_MODULE(nandsim, nandsim_modevent, NULL);
665 MODULE_VERSION(nandsim, 1);
666 MODULE_DEPEND(nandsim, nand, 1, 1, 1);
667 MODULE_DEPEND(nandsim, alq, 1, 1, 1);