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