1235537Sgber/*-
2235537Sgber * Copyright (C) 2009-2012 Semihalf
3235537Sgber * All rights reserved.
4235537Sgber *
5235537Sgber * Redistribution and use in source and binary forms, with or without
6235537Sgber * modification, are permitted provided that the following conditions
7235537Sgber * are met:
8235537Sgber * 1. Redistributions of source code must retain the above copyright
9235537Sgber *    notice, this list of conditions and the following disclaimer.
10235537Sgber * 2. Redistributions in binary form must reproduce the above copyright
11235537Sgber *    notice, this list of conditions and the following disclaimer in the
12235537Sgber *    documentation and/or other materials provided with the distribution.
13235537Sgber *
14235537Sgber * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15235537Sgber * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16235537Sgber * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17235537Sgber * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18235537Sgber * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19235537Sgber * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20235537Sgber * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21235537Sgber * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22235537Sgber * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23235537Sgber * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24235537Sgber * SUCH DAMAGE.
25235537Sgber */
26235537Sgber
27235537Sgber/* Simulated NAND controller driver */
28235537Sgber
29235537Sgber#include <sys/cdefs.h>
30235537Sgber__FBSDID("$FreeBSD$");
31235537Sgber
32235537Sgber#include <sys/param.h>
33235537Sgber#include <sys/systm.h>
34235537Sgber#include <sys/proc.h>
35235537Sgber#include <sys/bus.h>
36235537Sgber#include <sys/conf.h>
37235537Sgber#include <sys/kernel.h>
38235537Sgber#include <sys/module.h>
39235537Sgber#include <sys/malloc.h>
40235537Sgber
41235537Sgber#include <dev/nand/nand.h>
42235537Sgber#include <dev/nand/nandsim.h>
43235537Sgber#include <dev/nand/nandsim_chip.h>
44235537Sgber#include <dev/nand/nandsim_log.h>
45235537Sgber#include <dev/nand/nandsim_swap.h>
46235537Sgber
47235537Sgberstruct sim_param sim;
48235537Sgberstruct sim_ctrl_conf ctrls[MAX_SIM_DEV];
49235537Sgber
50235537Sgberstatic struct cdev *nandsim_dev;
51235537Sgberstatic d_ioctl_t nandsim_ioctl;
52235537Sgber
53235537Sgberstatic void nandsim_init_sim_param(struct sim_param *);
54235537Sgberstatic int nandsim_create_ctrl(struct sim_ctrl *);
55235537Sgberstatic int nandsim_destroy_ctrl(int);
56235537Sgberstatic int nandsim_ctrl_status(struct sim_ctrl *);
57235537Sgberstatic int nandsim_create_chip(struct sim_chip *);
58235537Sgberstatic int nandsim_destroy_chip(struct sim_ctrl_chip *);
59235537Sgberstatic int nandsim_chip_status(struct sim_chip *);
60235537Sgberstatic int nandsim_start_ctrl(int);
61235537Sgberstatic int nandsim_stop_ctrl(int);
62235537Sgberstatic int nandsim_inject_error(struct sim_error *);
63235537Sgberstatic int nandsim_get_block_state(struct sim_block_state *);
64235537Sgberstatic int nandsim_set_block_state(struct sim_block_state *);
65235537Sgberstatic int nandsim_modify(struct sim_mod *);
66235537Sgberstatic int nandsim_dump(struct sim_dump *);
67235537Sgberstatic int nandsim_restore(struct sim_dump *);
68235537Sgberstatic int nandsim_freeze(struct sim_ctrl_chip *);
69235537Sgberstatic void nandsim_print_log(struct sim_log *);
70235537Sgberstatic struct nandsim_chip *get_nandsim_chip(uint8_t, uint8_t);
71235537Sgber
72235537Sgberstatic struct cdevsw nandsim_cdevsw = {
73235537Sgber	.d_version =    D_VERSION,
74235537Sgber	.d_ioctl =      nandsim_ioctl,
75235537Sgber	.d_name =       "nandsim",
76235537Sgber};
77235537Sgber
78235537Sgberint
79235537Sgbernandsim_ioctl(struct cdev *dev, u_long cmd, caddr_t data,
80235537Sgber    int flags, struct thread *td)
81235537Sgber{
82235537Sgber	int ret = 0;
83235537Sgber
84235537Sgber	switch (cmd) {
85235537Sgber	case NANDSIM_SIM_PARAM:
86235537Sgber		nandsim_init_sim_param((struct sim_param *)data);
87235537Sgber		break;
88235537Sgber	case NANDSIM_CREATE_CTRL:
89235537Sgber		ret = nandsim_create_ctrl((struct sim_ctrl *)data);
90235537Sgber		break;
91235537Sgber	case NANDSIM_DESTROY_CTRL:
92235537Sgber		ret = nandsim_destroy_ctrl(*(int *)data);
93235537Sgber		break;
94235537Sgber	case NANDSIM_STATUS_CTRL:
95235537Sgber		ret = nandsim_ctrl_status((struct sim_ctrl *)data);
96235537Sgber		break;
97235537Sgber	case NANDSIM_CREATE_CHIP:
98235537Sgber		ret = nandsim_create_chip((struct sim_chip *)data);
99235537Sgber		break;
100235537Sgber	case NANDSIM_DESTROY_CHIP:
101235537Sgber		ret = nandsim_destroy_chip((struct sim_ctrl_chip *)data);
102235537Sgber		break;
103235537Sgber	case NANDSIM_STATUS_CHIP:
104235537Sgber		ret = nandsim_chip_status((struct sim_chip *)data);
105235537Sgber		break;
106235537Sgber	case NANDSIM_MODIFY:
107235537Sgber		ret = nandsim_modify((struct sim_mod *)data);
108235537Sgber		break;
109235537Sgber	case NANDSIM_START_CTRL:
110235537Sgber		ret = nandsim_start_ctrl(*(int *)data);
111235537Sgber		break;
112235537Sgber	case NANDSIM_STOP_CTRL:
113235537Sgber		ret = nandsim_stop_ctrl(*(int *)data);
114235537Sgber		break;
115235537Sgber	case NANDSIM_INJECT_ERROR:
116235537Sgber		ret = nandsim_inject_error((struct sim_error *)data);
117235537Sgber		break;
118235537Sgber	case NANDSIM_SET_BLOCK_STATE:
119235537Sgber		ret = nandsim_set_block_state((struct sim_block_state *)data);
120235537Sgber		break;
121235537Sgber	case NANDSIM_GET_BLOCK_STATE:
122235537Sgber		ret = nandsim_get_block_state((struct sim_block_state *)data);
123235537Sgber		break;
124235537Sgber	case NANDSIM_PRINT_LOG:
125235537Sgber		nandsim_print_log((struct sim_log *)data);
126235537Sgber		break;
127235537Sgber	case NANDSIM_DUMP:
128235537Sgber		ret = nandsim_dump((struct sim_dump *)data);
129235537Sgber		break;
130235537Sgber	case NANDSIM_RESTORE:
131235537Sgber		ret = nandsim_restore((struct sim_dump *)data);
132235537Sgber		break;
133235537Sgber	case NANDSIM_FREEZE:
134235537Sgber		ret = nandsim_freeze((struct sim_ctrl_chip *)data);
135235537Sgber		break;
136235537Sgber	default:
137235537Sgber		ret = EINVAL;
138235537Sgber		break;
139235537Sgber	}
140235537Sgber
141235537Sgber	return (ret);
142235537Sgber}
143235537Sgber
144235537Sgberstatic void
145235537Sgbernandsim_init_sim_param(struct sim_param *param)
146235537Sgber{
147235537Sgber
148235537Sgber	if (!param)
149235537Sgber		return;
150235537Sgber
151235537Sgber	nand_debug(NDBG_SIM,"log level:%d output %d", param->log_level,
152235537Sgber	    param->log_output);
153235537Sgber	nandsim_log_level = param->log_level;
154235537Sgber	nandsim_log_output = param->log_output;
155235537Sgber}
156235537Sgber
157235537Sgberstatic int
158235537Sgbernandsim_create_ctrl(struct sim_ctrl *ctrl)
159235537Sgber{
160235537Sgber	struct sim_ctrl_conf *sim_ctrl;
161235537Sgber
162235537Sgber	nand_debug(NDBG_SIM,"create controller num:%d cs:%d",ctrl->num,
163235537Sgber	    ctrl->num_cs);
164235537Sgber
165235537Sgber	if (ctrl->num >= MAX_SIM_DEV) {
166235537Sgber		return (EINVAL);
167235537Sgber	}
168235537Sgber
169235537Sgber	sim_ctrl = &ctrls[ctrl->num];
170235537Sgber	if(sim_ctrl->created)
171235537Sgber		return (EEXIST);
172235537Sgber
173235537Sgber	sim_ctrl->num = ctrl->num;
174235537Sgber	sim_ctrl->num_cs = ctrl->num_cs;
175235537Sgber	sim_ctrl->ecc = ctrl->ecc;
176235537Sgber	memcpy(sim_ctrl->ecc_layout, ctrl->ecc_layout,
177235537Sgber	    MAX_ECC_BYTES * sizeof(ctrl->ecc_layout[0]));
178235537Sgber	strlcpy(sim_ctrl->filename, ctrl->filename,
179235537Sgber	    FILENAME_SIZE);
180235537Sgber	sim_ctrl->created = 1;
181235537Sgber
182235537Sgber	return (0);
183235537Sgber}
184235537Sgber
185235537Sgberstatic int
186235537Sgbernandsim_destroy_ctrl(int ctrl_num)
187235537Sgber{
188235537Sgber
189235537Sgber	nand_debug(NDBG_SIM,"destroy controller num:%d", ctrl_num);
190235537Sgber
191235537Sgber	if (ctrl_num >= MAX_SIM_DEV) {
192235537Sgber		return (EINVAL);
193235537Sgber	}
194235537Sgber
195235537Sgber	if (!ctrls[ctrl_num].created) {
196235537Sgber		return (ENODEV);
197235537Sgber	}
198235537Sgber
199235537Sgber	if (ctrls[ctrl_num].running) {
200235537Sgber		return (EBUSY);
201235537Sgber	}
202235537Sgber
203235537Sgber	memset(&ctrls[ctrl_num], 0, sizeof(ctrls[ctrl_num]));
204235537Sgber
205235537Sgber	return (0);
206235537Sgber}
207235537Sgber
208235537Sgberstatic int
209235537Sgbernandsim_ctrl_status(struct sim_ctrl *ctrl)
210235537Sgber{
211235537Sgber
212235537Sgber	nand_debug(NDBG_SIM,"status controller num:%d cs:%d",ctrl->num,
213235537Sgber	    ctrl->num_cs);
214235537Sgber
215235537Sgber	if (ctrl->num >= MAX_SIM_DEV) {
216235537Sgber		return (EINVAL);
217235537Sgber	}
218235537Sgber
219235537Sgber	ctrl->num_cs = ctrls[ctrl->num].num_cs;
220235537Sgber	ctrl->ecc = ctrls[ctrl->num].ecc;
221235537Sgber	memcpy(ctrl->ecc_layout, ctrls[ctrl->num].ecc_layout,
222235537Sgber	    MAX_ECC_BYTES * sizeof(ctrl->ecc_layout[0]));
223235537Sgber	strlcpy(ctrl->filename, ctrls[ctrl->num].filename,
224235537Sgber	    FILENAME_SIZE);
225235537Sgber	ctrl->running = ctrls[ctrl->num].running;
226235537Sgber	ctrl->created = ctrls[ctrl->num].created;
227235537Sgber
228235537Sgber	return (0);
229235537Sgber}
230235537Sgber
231235537Sgberstatic int
232235537Sgbernandsim_create_chip(struct sim_chip *chip)
233235537Sgber{
234235537Sgber	struct sim_chip *sim_chip;
235235537Sgber
236235537Sgber	nand_debug(NDBG_SIM,"create chip num:%d at ctrl:%d", chip->num,
237235537Sgber	    chip->ctrl_num);
238235537Sgber
239235537Sgber	if (chip->ctrl_num >= MAX_SIM_DEV ||
240235537Sgber	    chip->num >= MAX_CTRL_CS) {
241235537Sgber		return (EINVAL);
242235537Sgber	}
243235537Sgber
244235537Sgber	if (ctrls[chip->ctrl_num].chips[chip->num]) {
245235537Sgber		return (EEXIST);
246235537Sgber	}
247235537Sgber
248235537Sgber	sim_chip = malloc(sizeof(*sim_chip), M_NANDSIM,
249235537Sgber	    M_WAITOK);
250235537Sgber	if (sim_chip == NULL) {
251235537Sgber		return (ENOMEM);
252235537Sgber	}
253235537Sgber
254235537Sgber	memcpy(sim_chip, chip, sizeof(*sim_chip));
255235537Sgber	ctrls[chip->ctrl_num].chips[chip->num] = sim_chip;
256235537Sgber	sim_chip->created = 1;
257235537Sgber
258235537Sgber	return (0);
259235537Sgber}
260235537Sgber
261235537Sgberstatic int
262235537Sgbernandsim_destroy_chip(struct sim_ctrl_chip *chip)
263235537Sgber{
264235537Sgber	struct sim_ctrl_conf *ctrl_conf;
265235537Sgber
266235537Sgber	nand_debug(NDBG_SIM,"destroy chip num:%d at ctrl:%d", chip->chip_num,
267235537Sgber	    chip->ctrl_num);
268235537Sgber
269235537Sgber	if (chip->ctrl_num >= MAX_SIM_DEV ||
270235537Sgber	    chip->chip_num >= MAX_CTRL_CS)
271235537Sgber		return (EINVAL);
272235537Sgber
273235537Sgber	ctrl_conf = &ctrls[chip->ctrl_num];
274235537Sgber
275235537Sgber	if (!ctrl_conf->created || !ctrl_conf->chips[chip->chip_num])
276235537Sgber		return (ENODEV);
277235537Sgber
278235537Sgber	if (ctrl_conf->running)
279235537Sgber		return (EBUSY);
280235537Sgber
281235537Sgber	free(ctrl_conf->chips[chip->chip_num], M_NANDSIM);
282235537Sgber	ctrl_conf->chips[chip->chip_num] = NULL;
283235537Sgber
284235537Sgber	return (0);
285235537Sgber}
286235537Sgber
287235537Sgberstatic int
288235537Sgbernandsim_chip_status(struct sim_chip *chip)
289235537Sgber{
290235537Sgber	struct sim_ctrl_conf *ctrl_conf;
291235537Sgber
292235537Sgber	nand_debug(NDBG_SIM,"status for chip num:%d at ctrl:%d", chip->num,
293235537Sgber	    chip->ctrl_num);
294235537Sgber
295235537Sgber	if (chip->ctrl_num >= MAX_SIM_DEV &&
296235537Sgber	    chip->num >= MAX_CTRL_CS)
297235537Sgber		return (EINVAL);
298235537Sgber
299235537Sgber	ctrl_conf = &ctrls[chip->ctrl_num];
300235537Sgber	if (!ctrl_conf->chips[chip->num])
301235537Sgber		chip->created = 0;
302235537Sgber	else
303235537Sgber		memcpy(chip, ctrl_conf->chips[chip->num], sizeof(*chip));
304235537Sgber
305235537Sgber	return (0);
306235537Sgber}
307235537Sgber
308235537Sgberstatic int
309235537Sgbernandsim_start_ctrl(int num)
310235537Sgber{
311235537Sgber	device_t nexus, ndev;
312235537Sgber	devclass_t nexus_devclass;
313235537Sgber	int ret = 0;
314235537Sgber
315235537Sgber	nand_debug(NDBG_SIM,"start ctlr num:%d", num);
316235537Sgber
317235537Sgber	if (num >= MAX_SIM_DEV)
318235537Sgber		return (EINVAL);
319235537Sgber
320235537Sgber	if (!ctrls[num].created)
321235537Sgber		return (ENODEV);
322235537Sgber
323235537Sgber	if (ctrls[num].running)
324235537Sgber		return (EBUSY);
325235537Sgber
326235537Sgber	/* We will add our device as a child of the nexus0 device */
327235537Sgber	if (!(nexus_devclass = devclass_find("nexus")) ||
328235537Sgber	    !(nexus = devclass_get_device(nexus_devclass, 0)))
329235537Sgber		return (EFAULT);
330235537Sgber
331235537Sgber	/*
332235537Sgber	 * Create a newbus device representing this frontend instance
333235537Sgber	 *
334235537Sgber	 * XXX powerpc nexus doesn't implement bus_add_child, so child
335235537Sgber	 * must be added by device_add_child().
336235537Sgber	 */
337235537Sgber#if defined(__powerpc__)
338235537Sgber	ndev = device_add_child(nexus, "nandsim", num);
339235537Sgber#else
340235537Sgber	ndev = BUS_ADD_CHILD(nexus, 0, "nandsim", num);
341235537Sgber#endif
342235537Sgber	if (!ndev)
343235537Sgber		return (EFAULT);
344235537Sgber
345235537Sgber	mtx_lock(&Giant);
346235537Sgber	ret = device_probe_and_attach(ndev);
347235537Sgber	mtx_unlock(&Giant);
348235537Sgber
349235537Sgber	if (ret == 0) {
350235537Sgber		ctrls[num].sim_ctrl_dev = ndev;
351235537Sgber		ctrls[num].running = 1;
352235537Sgber	}
353235537Sgber
354235537Sgber	return (ret);
355235537Sgber}
356235537Sgber
357235537Sgberstatic int
358235537Sgbernandsim_stop_ctrl(int num)
359235537Sgber{
360235537Sgber	device_t nexus;
361235537Sgber	devclass_t nexus_devclass;
362235537Sgber	int ret = 0;
363235537Sgber
364235537Sgber	nand_debug(NDBG_SIM,"stop controller num:%d", num);
365235537Sgber
366235537Sgber	if (num >= MAX_SIM_DEV) {
367235537Sgber		return (EINVAL);
368235537Sgber	}
369235537Sgber
370235537Sgber	if (!ctrls[num].created || !ctrls[num].running) {
371235537Sgber		return (ENODEV);
372235537Sgber	}
373235537Sgber
374235537Sgber	/* We will add our device as a child of the nexus0 device */
375235537Sgber	if (!(nexus_devclass = devclass_find("nexus")) ||
376235537Sgber	    !(nexus = devclass_get_device(nexus_devclass, 0))) {
377235537Sgber		return (ENODEV);
378235537Sgber	}
379235537Sgber
380235537Sgber	mtx_lock(&Giant);
381235537Sgber	if (ctrls[num].sim_ctrl_dev) {
382235537Sgber		ret = device_delete_child(nexus, ctrls[num].sim_ctrl_dev);
383235537Sgber		ctrls[num].sim_ctrl_dev = NULL;
384235537Sgber	}
385235537Sgber	mtx_unlock(&Giant);
386235537Sgber
387235537Sgber	ctrls[num].running = 0;
388235537Sgber
389235537Sgber	return (ret);
390235537Sgber}
391235537Sgber
392235537Sgberstatic struct nandsim_chip *
393235537Sgberget_nandsim_chip(uint8_t ctrl_num, uint8_t chip_num)
394235537Sgber{
395235537Sgber	struct nandsim_softc *sc;
396235537Sgber
397235537Sgber	if (!ctrls[ctrl_num].sim_ctrl_dev)
398235537Sgber		return (NULL);
399235537Sgber
400235537Sgber	sc = device_get_softc(ctrls[ctrl_num].sim_ctrl_dev);
401235537Sgber	return (sc->chips[chip_num]);
402235537Sgber}
403235537Sgber
404235537Sgberstatic void
405235537Sgbernandsim_print_log(struct sim_log *sim_log)
406235537Sgber{
407235537Sgber	struct nandsim_softc *sc;
408235537Sgber	int len1, len2;
409235537Sgber
410235537Sgber	if (!ctrls[sim_log->ctrl_num].sim_ctrl_dev)
411235537Sgber		return;
412235537Sgber
413235537Sgber	sc = device_get_softc(ctrls[sim_log->ctrl_num].sim_ctrl_dev);
414235537Sgber	if (sc->log_buff) {
415235537Sgber		len1 = strlen(&sc->log_buff[sc->log_idx + 1]);
416235537Sgber		if (len1 >= sim_log->len)
417235537Sgber			len1 = sim_log->len;
418235537Sgber		copyout(&sc->log_buff[sc->log_idx + 1], sim_log->log, len1);
419235537Sgber		len2 = strlen(sc->log_buff);
420235537Sgber		if (len2 >= (sim_log->len - len1))
421235537Sgber			len2 = (sim_log->len - len1);
422235537Sgber		copyout(sc->log_buff, &sim_log->log[len1], len2);
423235537Sgber		sim_log->len = len1 + len2;
424235537Sgber	}
425235537Sgber}
426235537Sgber
427235537Sgberstatic int
428235537Sgbernandsim_inject_error(struct sim_error *error)
429235537Sgber{
430235537Sgber	struct nandsim_chip *chip;
431235537Sgber	struct block_space *bs;
432235537Sgber	struct onfi_params *param;
433235537Sgber	int page, page_size, block, offset;
434235537Sgber
435235537Sgber	nand_debug(NDBG_SIM,"inject error for chip %d at ctrl %d\n",
436235537Sgber	    error->chip_num, error->ctrl_num);
437235537Sgber
438235537Sgber	if (error->ctrl_num >= MAX_SIM_DEV ||
439235537Sgber	    error->chip_num >= MAX_CTRL_CS)
440235537Sgber		return (EINVAL);
441235537Sgber
442235537Sgber	if (!ctrls[error->ctrl_num].created || !ctrls[error->ctrl_num].running)
443235537Sgber		return (ENODEV);
444235537Sgber
445235537Sgber	chip = get_nandsim_chip(error->ctrl_num, error->chip_num);
446235537Sgber	param = &chip->params;
447235537Sgber	page_size = param->bytes_per_page + param->spare_bytes_per_page;
448235537Sgber	block = error->page_num / param->pages_per_block;
449235537Sgber	page = error->page_num % param->pages_per_block;
450235537Sgber
451235537Sgber	bs = get_bs(chip->swap, block, 1);
452235537Sgber	if (!bs)
453235537Sgber		return (EINVAL);
454235537Sgber
455235537Sgber	offset = (page * page_size) + error->column;
456235537Sgber	memset(&bs->blk_ptr[offset], error->pattern, error->len);
457235537Sgber
458235537Sgber	return (0);
459235537Sgber}
460235537Sgber
461235537Sgberstatic int
462235537Sgbernandsim_set_block_state(struct sim_block_state *bs)
463235537Sgber{
464235537Sgber	struct onfi_params *params;
465235537Sgber	struct nandsim_chip *chip;
466235537Sgber	int blocks;
467235537Sgber
468235537Sgber	nand_debug(NDBG_SIM,"set block state for %d:%d block %d\n",
469235537Sgber	    bs->chip_num, bs->ctrl_num, bs->block_num);
470235537Sgber
471235537Sgber	if (bs->ctrl_num >= MAX_SIM_DEV ||
472235537Sgber	    bs->chip_num >= MAX_CTRL_CS)
473235537Sgber		return (EINVAL);
474235537Sgber
475235537Sgber	chip = get_nandsim_chip(bs->ctrl_num, bs->chip_num);
476235537Sgber	params = &chip->params;
477235537Sgber	blocks = params->luns * params->blocks_per_lun;
478235537Sgber
479235537Sgber	if (bs->block_num > blocks)
480235537Sgber		return (EINVAL);
481235537Sgber
482235537Sgber	chip->blk_state[bs->block_num].is_bad = bs->state;
483235537Sgber
484235537Sgber	if (bs->wearout >= 0)
485235537Sgber		chip->blk_state[bs->block_num].wear_lev = bs->wearout;
486235537Sgber
487235537Sgber	return (0);
488235537Sgber}
489235537Sgber
490235537Sgberstatic int
491235537Sgbernandsim_get_block_state(struct sim_block_state *bs)
492235537Sgber{
493235537Sgber	struct onfi_params *params;
494235537Sgber	struct nandsim_chip *chip;
495235537Sgber	int blocks;
496235537Sgber
497235537Sgber	if (bs->ctrl_num >= MAX_SIM_DEV ||
498235537Sgber	    bs->chip_num >= MAX_CTRL_CS)
499235537Sgber		return (EINVAL);
500235537Sgber
501235537Sgber	nand_debug(NDBG_SIM,"get block state for %d:%d block %d\n",
502235537Sgber	    bs->chip_num, bs->ctrl_num, bs->block_num);
503235537Sgber
504235537Sgber	chip = get_nandsim_chip(bs->ctrl_num, bs->chip_num);
505235537Sgber	params = &chip->params;
506235537Sgber	blocks = params->luns * params->blocks_per_lun;
507235537Sgber
508235537Sgber	if (bs->block_num > blocks)
509235537Sgber		return (EINVAL);
510235537Sgber
511235537Sgber	bs->state = chip->blk_state[bs->block_num].is_bad;
512235537Sgber	bs->wearout = chip->blk_state[bs->block_num].wear_lev;
513235537Sgber
514235537Sgber	return (0);
515235537Sgber}
516235537Sgber
517235537Sgberstatic int
518235537Sgbernandsim_dump(struct sim_dump *dump)
519235537Sgber{
520235537Sgber	struct nandsim_chip *chip;
521235537Sgber	struct block_space *bs;
522235537Sgber	int blk_size;
523235537Sgber
524235537Sgber	nand_debug(NDBG_SIM,"dump chip %d %d\n", dump->ctrl_num, dump->chip_num);
525235537Sgber
526235537Sgber	if (dump->ctrl_num >= MAX_SIM_DEV ||
527235537Sgber	    dump->chip_num >= MAX_CTRL_CS)
528235537Sgber		return (EINVAL);
529235537Sgber
530235537Sgber	chip = get_nandsim_chip(dump->ctrl_num, dump->chip_num);
531235537Sgber	blk_size = chip->cg.block_size +
532235537Sgber	    (chip->cg.oob_size * chip->cg.pgs_per_blk);
533235537Sgber
534235537Sgber	bs = get_bs(chip->swap, dump->block_num, 0);
535235537Sgber	if (!bs)
536235537Sgber		return (EINVAL);
537235537Sgber
538235537Sgber	if (dump->len > blk_size)
539235537Sgber		dump->len = blk_size;
540235537Sgber
541235537Sgber	copyout(bs->blk_ptr, dump->data, dump->len);
542235537Sgber
543235537Sgber	return (0);
544235537Sgber}
545235537Sgber
546235537Sgberstatic int
547235537Sgbernandsim_restore(struct sim_dump *dump)
548235537Sgber{
549235537Sgber	struct nandsim_chip *chip;
550235537Sgber	struct block_space *bs;
551235537Sgber	int blk_size;
552235537Sgber
553235537Sgber	nand_debug(NDBG_SIM,"restore chip %d %d\n", dump->ctrl_num,
554235537Sgber	    dump->chip_num);
555235537Sgber
556235537Sgber	if (dump->ctrl_num >= MAX_SIM_DEV ||
557235537Sgber	    dump->chip_num >= MAX_CTRL_CS)
558235537Sgber		return (EINVAL);
559235537Sgber
560235537Sgber	chip = get_nandsim_chip(dump->ctrl_num, dump->chip_num);
561235537Sgber	blk_size = chip->cg.block_size +
562235537Sgber	    (chip->cg.oob_size * chip->cg.pgs_per_blk);
563235537Sgber
564235537Sgber	bs = get_bs(chip->swap, dump->block_num, 1);
565235537Sgber	if (!bs)
566235537Sgber		return (EINVAL);
567235537Sgber
568235537Sgber	if (dump->len > blk_size)
569235537Sgber		dump->len = blk_size;
570235537Sgber
571235537Sgber
572235537Sgber	copyin(dump->data, bs->blk_ptr, dump->len);
573235537Sgber
574235537Sgber	return (0);
575235537Sgber}
576235537Sgber
577235537Sgberstatic int
578235537Sgbernandsim_freeze(struct sim_ctrl_chip *ctrl_chip)
579235537Sgber{
580235537Sgber	struct nandsim_chip *chip;
581235537Sgber
582235537Sgber	if (ctrl_chip->ctrl_num >= MAX_SIM_DEV ||
583235537Sgber	    ctrl_chip->chip_num >= MAX_CTRL_CS)
584235537Sgber		return (EINVAL);
585235537Sgber
586235537Sgber	chip = get_nandsim_chip(ctrl_chip->ctrl_num, ctrl_chip->chip_num);
587235537Sgber	nandsim_chip_freeze(chip);
588235537Sgber
589235537Sgber	return (0);
590235537Sgber}
591235537Sgber
592235537Sgberstatic int
593235537Sgbernandsim_modify(struct sim_mod *mod)
594235537Sgber{
595235537Sgber	struct sim_chip *sim_conf = NULL;
596235537Sgber	struct nandsim_chip *sim_chip = NULL;
597235537Sgber
598235537Sgber	nand_debug(NDBG_SIM,"modify ctlr %d chip %d", mod->ctrl_num,
599235537Sgber	    mod->chip_num);
600235537Sgber
601235537Sgber	if (mod->field != SIM_MOD_LOG_LEVEL) {
602235537Sgber		if (mod->ctrl_num >= MAX_SIM_DEV ||
603235537Sgber		    mod->chip_num >= MAX_CTRL_CS)
604235537Sgber			return (EINVAL);
605235537Sgber
606235537Sgber		sim_conf = ctrls[mod->ctrl_num].chips[mod->chip_num];
607235537Sgber		sim_chip = get_nandsim_chip(mod->ctrl_num, mod->chip_num);
608235537Sgber	}
609235537Sgber
610235537Sgber	switch (mod->field) {
611235537Sgber	case SIM_MOD_LOG_LEVEL:
612235537Sgber		nandsim_log_level = mod->new_value;
613235537Sgber		break;
614235537Sgber	case SIM_MOD_ERASE_TIME:
615235537Sgber		sim_conf->erase_time = sim_chip->erase_delay = mod->new_value;
616235537Sgber		break;
617235537Sgber	case SIM_MOD_PROG_TIME:
618235537Sgber		sim_conf->prog_time = sim_chip->prog_delay = mod->new_value;
619235537Sgber		break;
620235537Sgber	case SIM_MOD_READ_TIME:
621235537Sgber		sim_conf->read_time = sim_chip->read_delay = mod->new_value;
622235537Sgber		break;
623235537Sgber	case SIM_MOD_ERROR_RATIO:
624235537Sgber		sim_conf->error_ratio = mod->new_value;
625235537Sgber		sim_chip->error_ratio = mod->new_value;
626235537Sgber		break;
627235537Sgber	default:
628235537Sgber		break;
629235537Sgber	}
630235537Sgber
631235537Sgber	return (0);
632235537Sgber}
633235537Sgberstatic int
634235537Sgbernandsim_modevent(module_t mod __unused, int type, void *data __unused)
635235537Sgber{
636235537Sgber	struct sim_ctrl_chip chip_ctrl;
637235537Sgber	int i, j;
638235537Sgber
639235537Sgber	switch (type) {
640235537Sgber	case MOD_LOAD:
641235537Sgber		nandsim_dev = make_dev(&nandsim_cdevsw, 0,
642235537Sgber		    UID_ROOT, GID_WHEEL, 0666, "nandsim.ioctl");
643235537Sgber		break;
644235537Sgber	case MOD_UNLOAD:
645235537Sgber		for (i = 0; i < MAX_SIM_DEV; i++) {
646235537Sgber			nandsim_stop_ctrl(i);
647235537Sgber			chip_ctrl.ctrl_num = i;
648235537Sgber			for (j = 0; j < MAX_CTRL_CS; j++) {
649235537Sgber				chip_ctrl.chip_num = j;
650235537Sgber				nandsim_destroy_chip(&chip_ctrl);
651235537Sgber			}
652235537Sgber			nandsim_destroy_ctrl(i);
653235537Sgber		}
654235537Sgber		destroy_dev(nandsim_dev);
655235537Sgber		break;
656235537Sgber	case MOD_SHUTDOWN:
657235537Sgber		break;
658235537Sgber	default:
659235537Sgber		return (EOPNOTSUPP);
660235537Sgber	}
661235537Sgber	return (0);
662235537Sgber}
663235537Sgber
664235537SgberDEV_MODULE(nandsim, nandsim_modevent, NULL);
665235537SgberMODULE_VERSION(nandsim, 1);
666237605StakawataMODULE_DEPEND(nandsim, nand, 1, 1, 1);
667237605StakawataMODULE_DEPEND(nandsim, alq, 1, 1, 1);
668