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