Home | History | Annotate | Line # | Download | only in nor
cfi_0002.c revision 1.3
      1 /*	$NetBSD: cfi_0002.c,v 1.3 2011/07/19 20:52:10 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.3 2011/07/19 20:52:10 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_chiptime_typ;
    131 	shft += cfi->cfi_qry_data.erase_chiptime_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_chiptime_typ %d\n",
    613 		 cfi->cfi_qry_data.erase_chiptime_typ);
    614 	printf("erase_chiptime_max %d\n",
    615 		 cfi->cfi_qry_data.erase_chiptime_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