xref: /netbsd-src/sys/dev/nor/cfi_0002.c (revision 6a493d6bc668897c91594964a732d38505b70cbb)
1 /*	$NetBSD: cfi_0002.c,v 1.6 2011/12/17 19:42:41 phx Exp $	*/
2 /*-
3  * Copyright (c) 2011 The NetBSD Foundation, Inc.
4  * All rights reserved.
5  *
6  * This code is derived from software contributed to The NetBSD Foundation
7  * by Cliff Neighbors.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
19  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
20  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
22  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28  * POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 #include "opt_flash.h"
32 
33 #include <sys/cdefs.h>
34 __KERNEL_RCSID(0, "$NetBSD: cfi_0002.c,v 1.6 2011/12/17 19:42:41 phx Exp $");
35 
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/cdefs.h>
39 #include <sys/device.h>
40 #include <sys/endian.h>
41 #include <sys/time.h>
42 
43 #include <sys/bus.h>
44 
45 #include <dev/nor/nor.h>
46 #include <dev/nor/cfi.h>
47 #include <dev/nor/cfi_0002.h>
48 
49 
50 static void cfi_0002_version_init(struct cfi * const);
51 static int  cfi_0002_read_page(device_t, flash_off_t, uint8_t *);
52 static int  cfi_0002_program_page(device_t, flash_off_t, const uint8_t *);
53 static int  cfi_0002_erase_block(device_t, flash_off_t);
54 static int  cfi_0002_erase_all(device_t);
55 static int  cfi_0002_busy(device_t, flash_off_t, u_long);
56 static int  cfi_0002_busy_wait(struct cfi * const, flash_off_t, u_long);
57 static int  cfi_0002_busy_poll(struct cfi * const, flash_off_t, u_long);
58 static int  cfi_0002_busy_yield(struct cfi * const, flash_off_t, u_long);
59 static int  cfi_0002_busy_dq7(struct cfi * const , flash_off_t);
60 #ifdef NOTYET
61 static int  cfi_0002_busy_reg(struct cfi * const, flash_off_t);
62 #endif
63 
64 
65 static const char *page_mode_str[] = {
66 	"(not supported)",
67 	"4 word page",
68 	"8 word page",
69 	"16 word page",
70 };
71 
72 static const char *wp_mode_str[] = {
73 	"Flash device without WP Protect (No Boot)",
74 	"Eight 8 kB Sectors at TOP and Bottom with WP (Dual Boot)",
75 	"Bottom Boot Device with WP Protect (Bottom Boot)",
76 	"Top Boot Device with WP Protect (Top Boot)",
77 	"Uniform, Bottom WP Protect (Uniform Bottom Boot)",
78 	"Uniform, Top WP Protect (Uniform Top Boot)",
79 	"WP Protect for all sectors",
80 	"Uniform, Top or Bottom WP Protect",
81 };
82 
83 
84 static inline const char *
85 cfi_0002_page_mode_str(uint8_t mode)
86 {
87 	if (mode >= __arraycount(page_mode_str))
88 		panic("%s: mode %d out of range", __func__, mode);
89 	return page_mode_str[mode];
90 }
91 
92 static inline const char *
93 cfi_0002_wp_mode_str(uint8_t mode)
94 {
95 	if (mode >= __arraycount(wp_mode_str))
96 		panic("%s: mode %d out of range", __func__, mode);
97 	return wp_mode_str[mode];
98 }
99 
100 /*
101  * cfi_0002_time_write_nbyte - maximum usec delay waiting for write buffer
102  */
103 static inline u_long
104 cfi_0002_time_write_nbyte(struct cfi *cfi)
105 {
106 	u_int shft = cfi->cfi_qry_data.write_nbyte_time_typ;
107 	shft += cfi->cfi_qry_data.write_nbyte_time_max;
108 	u_long usec = 1UL << shft;
109 	return usec;
110 }
111 
112 /*
113  * cfi_0002_time_erase_blk - maximum usec delay waiting for erase block
114  */
115 static inline u_long
116 cfi_0002_time_erase_blk(struct cfi *cfi)
117 {
118 	u_int shft = cfi->cfi_qry_data.erase_blk_time_typ;
119 	shft += cfi->cfi_qry_data.erase_blk_time_max;
120 	u_long usec = 1000UL << shft;
121 	return usec;
122 }
123 
124 /*
125  * cfi_0002_time_erase_all - maximum usec delay waiting for erase chip
126  */
127 static inline u_long
128 cfi_0002_time_erase_all(struct cfi *cfi)
129 {
130 	u_int shft = cfi->cfi_qry_data.erase_chip_time_typ;
131 	shft += cfi->cfi_qry_data.erase_chip_time_max;
132 	u_long usec = 1000UL << shft;
133 	return usec;
134 }
135 
136 /*
137  * cfi_0002_time_dflt - maximum usec delay to use waiting for ready
138  *
139  * use the maximum delay for chip erase function
140  * that should be the worst non-sick case
141  */
142 static inline u_long
143 cfi_0002_time_dflt(struct cfi *cfi)
144 {
145 	return cfi_0002_time_erase_all(cfi);
146 }
147 
148 void
149 cfi_0002_init(struct nor_softc * const sc, struct cfi * const cfi,
150     struct nor_chip * const chip)
151 {
152 	CFI_0002_STATS_INIT(sc->sc_dev, cfi);
153 
154 	cfi_0002_version_init(cfi);
155 
156 	cfi->cfi_ops.cfi_reset = cfi_reset_std;
157 	cfi->cfi_yield_time = 500;		/* 500 usec */
158 
159 	/* page size for buffered write */
160 	chip->nc_page_size =
161 		1 << cfi->cfi_qry_data.write_nbyte_size_max;
162 
163 	/* these are unused */
164 	chip->nc_spare_size = 0;
165 	chip->nc_badmarker_offs = 0;
166 
167 	/* establish command-set-specific interface ops */
168 	sc->sc_nor_if->read_page = cfi_0002_read_page;
169 	sc->sc_nor_if->program_page = cfi_0002_program_page;
170 	sc->sc_nor_if->erase_block = cfi_0002_erase_block;
171 	sc->sc_nor_if->erase_all = cfi_0002_erase_all;
172 	sc->sc_nor_if->busy = cfi_0002_busy;
173 
174 }
175 
176 /*
177  * cfi_0002_version_init - command set version-specific initialization
178  *
179  * see "Programmer's Guide for the Spansion 65 nm GL-S MirrorBit EclipseTM
180  * Flash Non-Volatile Memory Family Architecture" section 5.
181  */
182 static void
183 cfi_0002_version_init(struct cfi * const cfi)
184 {
185 	const uint8_t major = cfi->cfi_qry_data.pri.cmd_0002.version_maj;
186 	const uint8_t minor = cfi->cfi_qry_data.pri.cmd_0002.version_min;
187 
188 	if ((minor == '3') && (major == '1')) {
189 		/* cmdset version 1.3 */
190 		cfi->cfi_ops.cfi_busy = cfi_0002_busy_dq7;
191 #ifdef NOTYET
192 		cfi->cfi_ops.cfi_erase_sector = cfi_0002_erase_sector_q;
193 		cfi->cfi_ops.cfi_program_word = cfi_0002_program_word_ub;
194 	} else if ((minor >= '5') && (major == '1')) {
195 		/* cmdset version 1.5 or later */
196 		cfi->cfi_ops.cfi_busy = cfi_0002_busy_reg;
197 		cfi->cfi_ops.cfi_erase_sector = cfi_0002_erase_sector_1;
198 		cfi->cfi_ops.cfi_program_word = cfi_0002_program_word_no_ub;
199 #endif
200 	} else {
201 		/* XXX this is excessive */
202 		panic("%s: unknown cmdset version %c.%c\n",
203 			__func__, major, minor);
204 	}
205 
206 }
207 
208 void
209 cfi_0002_print(device_t self, struct cfi * const cfi)
210 {
211 #ifdef NOR_VERBOSE
212 	struct cmdset_0002_query_data *pri = &cfi->cfi_qry_data.pri.cmd_0002;
213 
214 	aprint_normal_dev(self, "AMD/Fujitsu cmdset (0x0002) version=%c.%c\n",
215 		pri->version_maj, pri->version_min);
216 	aprint_normal_dev(self, "page mode type: %s\n",
217 		cfi_0002_page_mode_str(pri->page_mode_type));
218 	aprint_normal_dev(self, "wp protection: %s\n",
219 		cfi_0002_wp_mode_str(pri->wp_prot));
220 	aprint_normal_dev(self, "program suspend %ssupported\n",
221 		(pri->prog_susp == 0) ? "not " : "");
222 	aprint_normal_dev(self, "unlock bypass %ssupported\n",
223 		(pri->unlock_bypass == 0) ? "not " : "");
224 	aprint_normal_dev(self, "secure silicon sector size %#x\n",
225 		1 << pri->sss_size);
226 	aprint_normal_dev(self, "SW features %#x\n", pri->soft_feat);
227 	aprint_normal_dev(self, "page size %d\n", 1 << pri->page_size);
228 #endif
229 }
230 
231 static int
232 cfi_0002_read_page(device_t self, flash_off_t offset, uint8_t *datap)
233 {
234 	struct nor_softc * const sc = device_private(self);
235 	KASSERT(sc != NULL);
236 	KASSERT(sc->sc_nor_if != NULL);
237 	struct cfi *cfi = (struct cfi * const)sc->sc_nor_if->private;
238 	KASSERT(cfi != NULL);
239 	struct nor_chip * const chip = &sc->sc_chip;
240 	KASSERT(chip != NULL);
241 	KASSERT(chip->nc_page_mask != 0);
242 	KASSERT((offset & ~chip->nc_page_mask) == 0);
243 	KASSERT (chip->nc_page_size != 0);
244 	KASSERT((chip->nc_page_size & ((1 << cfi->cfi_portwidth) - 1)) == 0);
245 
246 	CFI_0002_STATS_INC(cfi, read_page);
247 
248 	bus_size_t count = chip->nc_page_size >> cfi->cfi_portwidth;
249 							/* #words/page */
250 
251 	int error = cfi_0002_busy_wait(cfi, offset, cfi_0002_time_dflt(cfi));
252 	if (error != 0)
253 		return error;
254 
255 	switch(cfi->cfi_portwidth) {
256 	case 0:
257 		bus_space_read_region_1(cfi->cfi_bst, cfi->cfi_bsh, offset,
258 			(uint8_t *)datap, count);
259 		break;
260 	case 1:
261 		bus_space_read_region_2(cfi->cfi_bst, cfi->cfi_bsh, offset,
262 			(uint16_t *)datap, count);
263 		break;
264 	case 2:
265 		bus_space_read_region_4(cfi->cfi_bst, cfi->cfi_bsh, offset,
266 			(uint32_t *)datap, count);
267 		break;
268 	default:
269 		panic("%s: bad port width %d\n", __func__, cfi->cfi_portwidth);
270 	};
271 
272 	return 0;
273 }
274 
275 static int
276 cfi_0002_program_page(device_t self, flash_off_t offset, const uint8_t *datap)
277 {
278 	struct nor_softc * const sc = device_private(self);
279 	KASSERT(sc != NULL);
280 	KASSERT(sc->sc_nor_if != NULL);
281 	struct cfi *cfi = (struct cfi * const)sc->sc_nor_if->private;
282 	KASSERT(cfi != NULL);
283 	struct nor_chip * const chip = &sc->sc_chip;
284 	KASSERT(chip != NULL);
285 	KASSERT(chip->nc_page_mask != 0);
286 	KASSERT((offset & ~chip->nc_page_mask) == 0);
287 	KASSERT (chip->nc_page_size != 0);
288 	KASSERT((chip->nc_page_size & ((1 << cfi->cfi_portwidth) - 1)) == 0);
289 
290 	CFI_0002_STATS_INC(cfi, program_page);
291 
292 	bus_size_t count = chip->nc_page_size >> cfi->cfi_portwidth;
293 							/* #words/page */
294 	bus_size_t sa = offset << (3 - cfi->cfi_portwidth);
295 							/* sector addr */
296 	uint32_t wc = count - 1;			/* #words - 1 */
297 
298 	int error = cfi_0002_busy_wait(cfi, offset, cfi_0002_time_dflt(cfi));
299 	if (error != 0)
300 		return ETIMEDOUT;
301 
302 	cfi_cmd(cfi, cfi->cfi_unlock_addr1, 0xaa);
303 	cfi_cmd(cfi, cfi->cfi_unlock_addr2, 0x55);
304 	cfi_cmd(cfi, sa,                    0x25); /* Write To Buffer */
305 	cfi_cmd(cfi, sa,                    wc);
306 
307 	switch(cfi->cfi_portwidth) {
308 	case 0:
309 		bus_space_write_region_1(cfi->cfi_bst, cfi->cfi_bsh, offset,
310 			(const uint8_t *)datap, count);
311 		break;
312 	case 1:
313 		bus_space_write_region_2(cfi->cfi_bst, cfi->cfi_bsh, offset,
314 			(const uint16_t *)datap, count);
315 		break;
316 	case 2:
317 		bus_space_write_region_4(cfi->cfi_bst, cfi->cfi_bsh, offset,
318 			(const uint32_t *)datap, count);
319 		break;
320 	default:
321 		panic("%s: bad port width %d\n", __func__, cfi->cfi_portwidth);
322 	};
323 
324 	cfi_cmd(cfi, sa, 0x29);	/*  Write Buffer Program Confirm */
325 
326 	error = cfi_0002_busy_wait(cfi, offset, cfi_0002_time_write_nbyte(cfi));
327 
328 	return error;
329 }
330 
331 static int
332 cfi_0002_erase_all(device_t self)
333 {
334 	struct nor_softc * const sc = device_private(self);
335 	KASSERT(sc != NULL);
336 	KASSERT(sc->sc_nor_if != NULL);
337 	struct cfi *cfi = (struct cfi * const)sc->sc_nor_if->private;
338 	KASSERT(cfi != NULL);
339 
340 	CFI_0002_STATS_INC(cfi, erase_all);
341 
342 	int error = cfi_0002_busy_wait(cfi, 0, cfi_0002_time_dflt(cfi));
343 	if (error != 0)
344 		return ETIMEDOUT;
345 
346 	cfi_cmd(cfi, cfi->cfi_unlock_addr1, 0xaa);
347 	cfi_cmd(cfi, cfi->cfi_unlock_addr2, 0x55);
348 	cfi_cmd(cfi, cfi->cfi_unlock_addr1, 0x80); /* erase start */
349 	cfi_cmd(cfi, cfi->cfi_unlock_addr1, 0xaa);
350 	cfi_cmd(cfi, cfi->cfi_unlock_addr2, 0x55);
351 	cfi_cmd(cfi, cfi->cfi_unlock_addr1, 0x10); /* erase chip */
352 
353 	error = cfi_0002_busy_wait(cfi, 0, cfi_0002_time_erase_all(cfi));
354 
355 	return error;
356 }
357 
358 static int
359 cfi_0002_erase_block(device_t self, flash_off_t offset)
360 {
361 	struct nor_softc * const sc = device_private(self);
362 	KASSERT(sc != NULL);
363 	KASSERT(sc->sc_nor_if != NULL);
364 	struct cfi *cfi = (struct cfi * const)sc->sc_nor_if->private;
365 	KASSERT(cfi != NULL);
366 
367 	CFI_0002_STATS_INC(cfi, erase_block);
368 
369 	bus_size_t sa = offset << (3 - cfi->cfi_portwidth);
370 
371 	int error = cfi_0002_busy_wait(cfi, offset, cfi_0002_time_dflt(cfi));
372 	if (error != 0)
373 		return ETIMEDOUT;
374 
375 	cfi_cmd(cfi, cfi->cfi_unlock_addr1, 0xaa);
376 	cfi_cmd(cfi, cfi->cfi_unlock_addr2, 0x55);
377 	cfi_cmd(cfi, cfi->cfi_unlock_addr1, 0x80); /* erase start */
378 	cfi_cmd(cfi, cfi->cfi_unlock_addr1, 0xaa);
379 	cfi_cmd(cfi, cfi->cfi_unlock_addr2, 0x55);
380 	cfi_cmd(cfi, sa,                    0x30); /* erase sector */
381 
382 	error = cfi_0002_busy_wait(cfi, offset, cfi_0002_time_erase_blk(cfi));
383 
384 	return error;
385 }
386 
387 /*
388  * cfi_0002_busy - nor_interface busy op
389  */
390 static int
391 cfi_0002_busy(device_t self, flash_off_t offset, u_long usec)
392 {
393 	struct nor_softc *sc = device_private(self);
394 	KASSERT(sc != NULL);
395 	KASSERT(sc->sc_nor_if != NULL);
396 	struct cfi * const cfi = (struct cfi * const)sc->sc_nor_if->private;
397 
398 	CFI_0002_STATS_INC(cfi, busy);
399 
400 	return cfi_0002_busy_wait(cfi, offset, usec);
401 }
402 
403 /*
404  * cfi_0002_busy_wait - wait until device is not busy
405  */
406 static int
407 cfi_0002_busy_wait(struct cfi * const cfi, flash_off_t offset, u_long usec)
408 {
409 	int error;
410 
411 #ifdef CFI_0002_STATS
412 	struct timeval start;
413 	struct timeval now;
414 	struct timeval delta;
415 
416 	if (usec > cfi->cfi_0002_stats.busy_usec_max)
417 		cfi->cfi_0002_stats.busy_usec_max = usec;
418 	if (usec < cfi->cfi_0002_stats.busy_usec_min)
419 		cfi->cfi_0002_stats.busy_usec_min = usec;
420 	microtime(&start);
421 #endif
422 	if (usec > cfi->cfi_yield_time) {
423 		error = cfi_0002_busy_yield(cfi, offset, usec);
424 #ifdef CFI_0002_STATS
425 		microtime(&now);
426 		cfi->cfi_0002_stats.busy_yield++;
427 		timersub(&now, &start, &delta);
428 		timeradd(&delta,
429 			&cfi->cfi_0002_stats.busy_yield_tv,
430 			&cfi->cfi_0002_stats.busy_yield_tv);
431 #endif
432 	} else {
433 		error = cfi_0002_busy_poll(cfi, offset, usec);
434 #ifdef CFI_0002_STATS
435 		microtime(&now);
436 		cfi->cfi_0002_stats.busy_poll++;
437 		timersub(&now, &start, &delta);
438 		timeradd(&delta,
439 			&cfi->cfi_0002_stats.busy_poll_tv,
440 			&cfi->cfi_0002_stats.busy_poll_tv);
441 #endif
442 	}
443 	return error;
444 }
445 
446 /*
447  * cfi_0002_busy_poll - poll until device is not busy
448  */
449 static int
450 cfi_0002_busy_poll(struct cfi * const cfi, flash_off_t offset, u_long usec)
451 {
452 	u_long count = usec >> 3;
453 	if (count == 0)
454 		count = 1;	/* enforce minimum */
455 	do {
456 		if (! cfi->cfi_ops.cfi_busy(cfi, offset))
457 			return 0;	/* not busy */
458 		DELAY(8);
459 	} while (count-- != 0);
460 
461 	return ETIMEDOUT;		/* busy */
462 }
463 
464 /*
465  * cfi_0002_busy_yield - yield until device is not busy
466  */
467 static int
468 cfi_0002_busy_yield(struct cfi * const cfi, flash_off_t offset, u_long usec)
469 {
470 	struct timeval start;
471 	struct timeval delta;
472 	struct timeval limit;
473 	struct timeval now;
474 
475 	microtime(&start);
476 
477 	/* try optimism */
478 	if (! cfi->cfi_ops.cfi_busy(cfi, offset)) {
479 		CFI_0002_STATS_INC(cfi, busy_yield_hit);
480 		return 0;		/* not busy */
481 	}
482 	CFI_0002_STATS_INC(cfi, busy_yield_miss);
483 
484 	delta.tv_sec = usec / 1000000;
485 	delta.tv_usec = usec % 1000000;
486 	timeradd(&start, &delta, &limit);
487 	do {
488 		yield();
489 		microtime(&now);
490 		if (! cfi->cfi_ops.cfi_busy(cfi, offset))
491 			return 0;	/* not busy */
492 	} while (timercmp(&now, &limit, <));
493 
494 	CFI_0002_STATS_INC(cfi, busy_yield_timo);
495 
496 	return ETIMEDOUT;		/* busy */
497 }
498 
499 /*
500  * cfi_0002_busy_dq7 - DQ7 "toggle" method to check busy
501  *
502  * Check busy during/after erase, program, protect operation.
503  *
504  * NOTE:
505  *	Chip manufacturers (Spansion) plan to deprecate this method.
506  */
507 static int
508 cfi_0002_busy_dq7(struct cfi * const cfi, flash_off_t offset)
509 {
510 	bus_space_tag_t bst = cfi->cfi_bst;
511 	bus_space_handle_t bsh = cfi->cfi_bsh;
512 	bool busy;
513 
514 	switch(cfi->cfi_portwidth) {
515 	case 0: {
516 		uint8_t r0 = bus_space_read_1(bst, bsh, 0) & __BIT(7);
517 		uint8_t r1 = bus_space_read_1(bst, bsh, 0) & __BIT(7);
518 		busy = (r0 != r1);
519 		break;
520 	}
521 	case 1: {
522 		uint16_t r0 = bus_space_read_2(bst, bsh, 0);
523 		uint16_t r1 = bus_space_read_2(bst, bsh, 0);
524 		busy = (r0 != r1);
525 		break;
526 	}
527 	case 2: {
528 		uint32_t r0 = bus_space_read_4(bst, bsh, 0);
529 		uint32_t r1 = bus_space_read_4(bst, bsh, 0);
530 		busy = (r0 != r1);
531 		break;
532 	}
533 	default:
534 		busy = true;	/* appeas gcc */
535 		panic("%s: bad port width %d\n",
536 			__func__, cfi->cfi_portwidth);
537 	}
538 	return busy;
539 }
540 
541 #ifdef NOTYET
542 /*
543  * cfi_0002_busy_reg - read and evaluate Read Status Register
544  *
545  * NOTE:
546  *	Read Status Register not present on all chips
547  *	use "toggle" method when Read Status Register not available.
548  */
549 static bool
550 cfi_0002_busy_reg(struct cfi * const cfi, flash_off_t offset)
551 {
552 	bus_space_tag_t bst = cfi->cfi_bst;
553 	bus_space_handle_t bsh = cfi->cfi_bsh;
554 	uint32_t r;
555 
556 	cfi_cmd(cfi, cfi->cfi_unlock_addr1, 0x70); /* Status Register Read  */
557 
558 	switch(cfi->cfi_portwidth) {
559 	case 0:
560 		r = bus_space_read_1(bst, bsh, 0);
561 		break;
562 	case 1:
563 		r = bus_space_read_2(bst, bsh, 0);
564 		break;
565 	case 2:
566 		r = bus_space_read_4(bst, bsh, 0);
567 		break;
568 	default:
569 		panic("%s: bad port width %d\n",
570 			__func__, cfi->cfi_portwidth);
571 	}
572 
573 	return ((r & __BIT(7)) == 0):
574 }
575 #endif	/* NOTYET */
576 
577 #ifdef CFI_0002_STATS
578 void
579 cfi_0002_stats_reset(struct cfi *cfi)
580 {
581 	memset(&cfi->cfi_0002_stats, 0, sizeof(struct cfi_0002_stats));
582         cfi->cfi_0002_stats.busy_usec_min = ~0;
583 }
584 
585 void
586 cfi_0002_stats_print(struct cfi *cfi)
587 {
588 	printf("read_page %lu\n", cfi->cfi_0002_stats.read_page);
589 	printf("program_page %lu\n", cfi->cfi_0002_stats.program_page);
590 	printf("erase_all %lu\n", cfi->cfi_0002_stats.erase_all);
591 	printf("erase_block %lu\n", cfi->cfi_0002_stats.erase_block);
592 	printf("busy %lu\n", cfi->cfi_0002_stats.busy);
593 
594 	printf("write_nbyte_time_typ %d\n",
595 		 cfi->cfi_qry_data.write_nbyte_time_typ);
596 	printf("write_nbyte_time_max %d\n",
597 		 cfi->cfi_qry_data.write_nbyte_time_max);
598 
599 	printf("erase_blk_time_typ %d\n",
600 		 cfi->cfi_qry_data.erase_blk_time_typ);
601 	printf("erase_blk_time_max %d\n",
602 		 cfi->cfi_qry_data.erase_blk_time_max);
603 
604 	printf("erase_chip_time_typ %d\n",
605 		 cfi->cfi_qry_data.erase_chip_time_typ);
606 	printf("erase_chip_time_max %d\n",
607 		 cfi->cfi_qry_data.erase_chip_time_max);
608 
609 	printf("time_write_nbyte %lu\n", cfi_0002_time_write_nbyte(cfi));
610 	printf("time_erase_blk %lu\n", cfi_0002_time_erase_blk(cfi));
611 	printf("time_erase_all %lu\n", cfi_0002_time_erase_all(cfi));
612 
613 	printf("busy_usec_min %lu\n", cfi->cfi_0002_stats.busy_usec_min);
614 	printf("busy_usec_max %lu\n", cfi->cfi_0002_stats.busy_usec_max);
615 
616 	printf("busy_poll_tv %lld.%d\n",
617 		cfi->cfi_0002_stats.busy_poll_tv.tv_sec,
618 		cfi->cfi_0002_stats.busy_poll_tv.tv_usec);
619 	printf("busy_yield_tv %lld.%d\n",
620 		cfi->cfi_0002_stats.busy_yield_tv.tv_sec,
621 		cfi->cfi_0002_stats.busy_yield_tv.tv_usec);
622 	printf("busy_poll %lu\n", cfi->cfi_0002_stats.busy_poll);
623 	printf("busy_yield %lu\n", cfi->cfi_0002_stats.busy_yield);
624 	printf("busy_yield_hit %lu\n", cfi->cfi_0002_stats.busy_yield_hit);
625 	printf("busy_yield_miss %lu\n", cfi->cfi_0002_stats.busy_yield_miss);
626 	printf("busy_yield_timo %lu\n", cfi->cfi_0002_stats.busy_yield_timo);
627 }
628 #endif	/* CFI_0002_STATS */
629