xref: /netbsd-src/sys/dev/nor/cfi_0002.c (revision a5847cc334d9a7029f6352b847e9e8d71a0f9e0c)
1 /*	$NetBSD: cfi_0002.c,v 1.4 2011/07/23 06:26:26 cliff 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.4 2011/07/23 06:26:26 cliff 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 >> cfi->cfi_portwidth;	/* sector addr */
295 	uint32_t wc = count - 1;			/* #words - 1 */
296 
297 	int error = cfi_0002_busy_wait(cfi, offset, cfi_0002_time_dflt(cfi));
298 	if (error != 0)
299 		return ETIMEDOUT;
300 
301 	cfi_cmd(cfi, 0x555, 0xaa); /* unlock 1 */
302 	cfi_cmd(cfi, 0x2aa, 0x55); /* unlock 2 */
303 	cfi_cmd(cfi, sa,    0x25); /* Write To Buffer */
304 	cfi_cmd(cfi, sa,    wc);
305 
306 	switch(cfi->cfi_portwidth) {
307 	case 0:
308 		bus_space_write_region_1(cfi->cfi_bst, cfi->cfi_bsh, offset,
309 			(const uint8_t *)datap, count);
310 		break;
311 	case 1:
312 		bus_space_write_region_2(cfi->cfi_bst, cfi->cfi_bsh, offset,
313 			(const uint16_t *)datap, count);
314 		break;
315 	case 2:
316 		bus_space_write_region_4(cfi->cfi_bst, cfi->cfi_bsh, offset,
317 			(const uint32_t *)datap, count);
318 		break;
319 	default:
320 		panic("%s: bad port width %d\n", __func__, cfi->cfi_portwidth);
321 	};
322 
323 	cfi_cmd(cfi, sa,    0x29);	/*  Write Buffer Program Confirm */
324 
325 	error = cfi_0002_busy_wait(cfi, offset, cfi_0002_time_write_nbyte(cfi));
326 
327 	return error;
328 }
329 
330 static int
331 cfi_0002_erase_all(device_t self)
332 {
333 	struct nor_softc * const sc = device_private(self);
334 	KASSERT(sc != NULL);
335 	KASSERT(sc->sc_nor_if != NULL);
336 	struct cfi *cfi = (struct cfi * const)sc->sc_nor_if->private;
337 	KASSERT(cfi != NULL);
338 	struct nor_chip * const chip = &sc->sc_chip;
339 	KASSERT(chip != NULL);
340 
341 	CFI_0002_STATS_INC(cfi, erase_all);
342 
343 	int error = cfi_0002_busy_wait(cfi, 0, cfi_0002_time_dflt(cfi));
344 	if (error != 0)
345 		return ETIMEDOUT;
346 
347 	cfi_cmd(cfi, 0x555, 0xaa); /* unlock 1 */
348 	cfi_cmd(cfi, 0x2aa, 0x55); /* unlock 2 */
349 	cfi_cmd(cfi, 0x555, 0x80); /* erase start */
350 	cfi_cmd(cfi, 0x555, 0xaa); /* unlock 1 */
351 	cfi_cmd(cfi, 0x2aa, 0x55); /* unlock 2 */
352 	cfi_cmd(cfi, 0x555, 0x10); /* erase chip */
353 
354 	error = cfi_0002_busy_wait(cfi, 0, cfi_0002_time_erase_all(cfi));
355 
356 	return error;
357 }
358 
359 static int
360 cfi_0002_erase_block(device_t self, flash_off_t offset)
361 {
362 	struct nor_softc * const sc = device_private(self);
363 	KASSERT(sc != NULL);
364 	KASSERT(sc->sc_nor_if != NULL);
365 	struct cfi *cfi = (struct cfi * const)sc->sc_nor_if->private;
366 	KASSERT(cfi != NULL);
367 	struct nor_chip * const chip = &sc->sc_chip;
368 	KASSERT(chip != NULL);
369 	KASSERT(chip->nc_block_mask != 0);
370 	KASSERT((offset & ~chip->nc_block_mask) == 0);
371 	KASSERT(chip->nc_block_size != 0);
372 	KASSERT((chip->nc_block_size & ((1 << cfi->cfi_portwidth) - 1)) == 0);
373 
374 	CFI_0002_STATS_INC(cfi, erase_block);
375 
376 	/* scale sector addr by portwidth or chipwidth ?  */
377 	bus_size_t sa = offset >> cfi->cfi_portwidth;
378 
379 	int error = cfi_0002_busy_wait(cfi, offset, cfi_0002_time_dflt(cfi));
380 	if (error != 0)
381 		return ETIMEDOUT;
382 
383 	cfi_cmd(cfi, 0x555, 0xaa); /* unlock 1 */
384 	cfi_cmd(cfi, 0x2aa, 0x55); /* unlock 2 */
385 	cfi_cmd(cfi, 0x555, 0x80); /* erase start */
386 	cfi_cmd(cfi, 0x555, 0xaa); /* unlock 1 */
387 	cfi_cmd(cfi, 0x2aa, 0x55); /* unlock 2 */
388 	cfi_cmd(cfi, sa,    0x30); /* erase sector */
389 
390 	error = cfi_0002_busy_wait(cfi, offset, cfi_0002_time_erase_blk(cfi));
391 
392 	return error;
393 }
394 
395 /*
396  * cfi_0002_busy - nor_interface busy op
397  */
398 static int
399 cfi_0002_busy(device_t self, flash_off_t offset, u_long usec)
400 {
401 	struct nor_softc *sc = device_private(self);
402 	KASSERT(sc != NULL);
403 	KASSERT(sc->sc_nor_if != NULL);
404 	struct cfi * const cfi = (struct cfi * const)sc->sc_nor_if->private;
405 
406 	CFI_0002_STATS_INC(cfi, busy);
407 
408 	return cfi_0002_busy_wait(cfi, offset, usec);
409 }
410 
411 /*
412  * cfi_0002_busy_wait - wait until device is not busy
413  */
414 static int
415 cfi_0002_busy_wait(struct cfi * const cfi, flash_off_t offset, u_long usec)
416 {
417 	int error;
418 
419 #ifdef CFI_0002_STATS
420 	struct timeval start;
421 	struct timeval now;
422 	struct timeval delta;
423 
424 	if (usec > cfi->cfi_0002_stats.busy_usec_max)
425 		cfi->cfi_0002_stats.busy_usec_max = usec;
426 	if (usec < cfi->cfi_0002_stats.busy_usec_min)
427 		cfi->cfi_0002_stats.busy_usec_min = usec;
428 	microtime(&start);
429 #endif
430 	if (usec > cfi->cfi_yield_time) {
431 		error = cfi_0002_busy_yield(cfi, offset, usec);
432 #ifdef CFI_0002_STATS
433 		microtime(&now);
434 		cfi->cfi_0002_stats.busy_yield++;
435 		timersub(&now, &start, &delta);
436 		timeradd(&delta,
437 			&cfi->cfi_0002_stats.busy_yield_tv,
438 			&cfi->cfi_0002_stats.busy_yield_tv);
439 #endif
440 	} else {
441 		error = cfi_0002_busy_poll(cfi, offset, usec);
442 #ifdef CFI_0002_STATS
443 		microtime(&now);
444 		cfi->cfi_0002_stats.busy_poll++;
445 		timersub(&now, &start, &delta);
446 		timeradd(&delta,
447 			&cfi->cfi_0002_stats.busy_poll_tv,
448 			&cfi->cfi_0002_stats.busy_poll_tv);
449 #endif
450 	}
451 	return error;
452 }
453 
454 /*
455  * cfi_0002_busy_poll - poll until device is not busy
456  */
457 static int
458 cfi_0002_busy_poll(struct cfi * const cfi, flash_off_t offset, u_long usec)
459 {
460 	u_long count = usec >> 3;
461 	if (count == 0)
462 		count = 1;	/* enforce minimum */
463 	do {
464 		if (! cfi->cfi_ops.cfi_busy(cfi, offset))
465 			return 0;	/* not busy */
466 		DELAY(8);
467 	} while (count-- != 0);
468 
469 	return ETIMEDOUT;		/* busy */
470 }
471 
472 /*
473  * cfi_0002_busy_yield - yield until device is not busy
474  */
475 static int
476 cfi_0002_busy_yield(struct cfi * const cfi, flash_off_t offset, u_long usec)
477 {
478 	struct timeval start;
479 	struct timeval delta;
480 	struct timeval limit;
481 	struct timeval now;
482 
483 	microtime(&start);
484 
485 	/* try optimism */
486 	if (! cfi->cfi_ops.cfi_busy(cfi, offset)) {
487 		CFI_0002_STATS_INC(cfi, busy_yield_hit);
488 		return 0;		/* not busy */
489 	}
490 	CFI_0002_STATS_INC(cfi, busy_yield_miss);
491 
492 	delta.tv_sec = usec / 1000000;
493 	delta.tv_usec = usec % 1000000;
494 	timeradd(&start, &delta, &limit);
495 	do {
496 		yield();
497 		microtime(&now);
498 		if (! cfi->cfi_ops.cfi_busy(cfi, offset))
499 			return 0;	/* not busy */
500 	} while (timercmp(&now, &limit, <));
501 
502 	CFI_0002_STATS_INC(cfi, busy_yield_timo);
503 
504 	return ETIMEDOUT;		/* busy */
505 }
506 
507 /*
508  * cfi_0002_busy_dq7 - DQ7 "toggle" method to check busy
509  *
510  * Check busy during/after erase, program, protect operation.
511  *
512  * NOTE:
513  *	Chip manufacturers (Spansion) plan to deprecate this method.
514  */
515 static int
516 cfi_0002_busy_dq7(struct cfi * const cfi, flash_off_t offset)
517 {
518 	bus_space_tag_t bst = cfi->cfi_bst;
519 	bus_space_handle_t bsh = cfi->cfi_bsh;
520 	bool busy;
521 
522 	switch(cfi->cfi_portwidth) {
523 	case 0: {
524 		uint8_t r0 = bus_space_read_1(bst, bsh, 0) & __BIT(7);
525 		uint8_t r1 = bus_space_read_1(bst, bsh, 0) & __BIT(7);
526 		busy = (r0 != r1);
527 		break;
528 	}
529 	case 1: {
530 		uint16_t r0 = bus_space_read_2(bst, bsh, 0);
531 		uint16_t r1 = bus_space_read_2(bst, bsh, 0);
532 		busy = (r0 != r1);
533 		break;
534 	}
535 	case 2: {
536 		uint32_t r0 = bus_space_read_4(bst, bsh, 0);
537 		uint32_t r1 = bus_space_read_4(bst, bsh, 0);
538 		busy = (r0 != r1);
539 		break;
540 	}
541 	default:
542 		busy = true;	/* appeas gcc */
543 		panic("%s: bad port width %d\n",
544 			__func__, cfi->cfi_portwidth);
545 	}
546 	return busy;
547 }
548 
549 #ifdef NOTYET
550 /*
551  * cfi_0002_busy_reg - read and evaluate Read Status Register
552  *
553  * NOTE:
554  *	Read Status Register not present on all chips
555  *	use "toggle" method when Read Status Register not available.
556  */
557 static bool
558 cfi_0002_busy_reg(struct cfi * const cfi, flash_off_t offset)
559 {
560 	bus_space_tag_t bst = cfi->cfi_bst;
561 	bus_space_handle_t bsh = cfi->cfi_bsh;
562 	uint32_t r;
563 
564 	cfi_cmd(cfi, 0x555, 0x70);	/* Status Register Read  */
565 
566 	switch(cfi->cfi_portwidth) {
567 	case 0:
568 		r = bus_space_read_1(bst, bsh, 0);
569 		break;
570 	case 1:
571 		r = bus_space_read_2(bst, bsh, 0);
572 		break;
573 	case 2:
574 		r = bus_space_read_4(bst, bsh, 0);
575 		break;
576 	default:
577 		panic("%s: bad port width %d\n",
578 			__func__, cfi->cfi_portwidth);
579 	}
580 
581 	return ((r & __BIT(7)) == 0):
582 }
583 #endif	/* NOTYET */
584 
585 #ifdef CFI_0002_STATS
586 void
587 cfi_0002_stats_reset(struct cfi *cfi)
588 {
589 	memset(&cfi->cfi_0002_stats, 0, sizeof(struct cfi_0002_stats));
590         cfi->cfi_0002_stats.busy_usec_min = ~0;
591 }
592 
593 void
594 cfi_0002_stats_print(struct cfi *cfi)
595 {
596 	printf("read_page %lu\n", cfi->cfi_0002_stats.read_page);
597 	printf("program_page %lu\n", cfi->cfi_0002_stats.program_page);
598 	printf("erase_all %lu\n", cfi->cfi_0002_stats.erase_all);
599 	printf("erase_block %lu\n", cfi->cfi_0002_stats.erase_block);
600 	printf("busy %lu\n", cfi->cfi_0002_stats.busy);
601 
602 	printf("write_nbyte_time_typ %d\n",
603 		 cfi->cfi_qry_data.write_nbyte_time_typ);
604 	printf("write_nbyte_time_max %d\n",
605 		 cfi->cfi_qry_data.write_nbyte_time_max);
606 
607 	printf("erase_blk_time_typ %d\n",
608 		 cfi->cfi_qry_data.erase_blk_time_typ);
609 	printf("erase_blk_time_max %d\n",
610 		 cfi->cfi_qry_data.erase_blk_time_max);
611 
612 	printf("erase_chip_time_typ %d\n",
613 		 cfi->cfi_qry_data.erase_chip_time_typ);
614 	printf("erase_chip_time_max %d\n",
615 		 cfi->cfi_qry_data.erase_chip_time_max);
616 
617 	printf("time_write_nbyte %lu\n", cfi_0002_time_write_nbyte(cfi));
618 	printf("time_erase_blk %lu\n", cfi_0002_time_erase_blk(cfi));
619 	printf("time_erase_all %lu\n", cfi_0002_time_erase_all(cfi));
620 
621 	printf("busy_usec_min %lu\n", cfi->cfi_0002_stats.busy_usec_min);
622 	printf("busy_usec_max %lu\n", cfi->cfi_0002_stats.busy_usec_max);
623 
624 	printf("busy_poll_tv %lld.%d\n",
625 		cfi->cfi_0002_stats.busy_poll_tv.tv_sec,
626 		cfi->cfi_0002_stats.busy_poll_tv.tv_usec);
627 	printf("busy_yield_tv %lld.%d\n",
628 		cfi->cfi_0002_stats.busy_yield_tv.tv_sec,
629 		cfi->cfi_0002_stats.busy_yield_tv.tv_usec);
630 	printf("busy_poll %lu\n", cfi->cfi_0002_stats.busy_poll);
631 	printf("busy_yield %lu\n", cfi->cfi_0002_stats.busy_yield);
632 	printf("busy_yield_hit %lu\n", cfi->cfi_0002_stats.busy_yield_hit);
633 	printf("busy_yield_miss %lu\n", cfi->cfi_0002_stats.busy_yield_miss);
634 	printf("busy_yield_timo %lu\n", cfi->cfi_0002_stats.busy_yield_timo);
635 }
636 #endif	/* CFI_0002_STATS */
637