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