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