cfi_core.c revision 255207
1184251Smarcel/*- 2184251Smarcel * Copyright (c) 2007, Juniper Networks, Inc. 3255207Sbrooks * Copyright (c) 2012-2013, SRI International 4184251Smarcel * All rights reserved. 5184251Smarcel * 6255207Sbrooks * Portions of this software were developed by SRI International and the 7255207Sbrooks * University of Cambridge Computer Laboratory under DARPA/AFRL contract 8255207Sbrooks * (FA8750-10-C-0237) ("CTSRD"), as part of the DARPA CRASH research 9255207Sbrooks * programme. 10255207Sbrooks * 11184251Smarcel * Redistribution and use in source and binary forms, with or without 12184251Smarcel * modification, are permitted provided that the following conditions 13184251Smarcel * are met: 14184251Smarcel * 1. Redistributions of source code must retain the above copyright 15184251Smarcel * notice, this list of conditions and the following disclaimer. 16184251Smarcel * 2. Redistributions in binary form must reproduce the above copyright 17184251Smarcel * notice, this list of conditions and the following disclaimer in the 18184251Smarcel * documentation and/or other materials provided with the distribution. 19184251Smarcel * 3. Neither the name of the author nor the names of any co-contributors 20184251Smarcel * may be used to endorse or promote products derived from this software 21184251Smarcel * without specific prior written permission. 22184251Smarcel * 23184251Smarcel * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 24184251Smarcel * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 25184251Smarcel * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 26184251Smarcel * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 27184251Smarcel * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 28184251Smarcel * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 29184251Smarcel * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 30184251Smarcel * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 31184251Smarcel * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32184251Smarcel * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33184251Smarcel * SUCH DAMAGE. 34184251Smarcel */ 35184251Smarcel 36184251Smarcel#include <sys/cdefs.h> 37184251Smarcel__FBSDID("$FreeBSD: head/sys/dev/cfi/cfi_core.c 255207 2013-09-04 17:19:21Z brooks $"); 38184251Smarcel 39188156Ssam#include "opt_cfi.h" 40188156Ssam 41184251Smarcel#include <sys/param.h> 42184251Smarcel#include <sys/systm.h> 43184251Smarcel#include <sys/bus.h> 44184251Smarcel#include <sys/conf.h> 45233553Sjchandra#include <sys/endian.h> 46250115Sbrooks#include <sys/kenv.h> 47184251Smarcel#include <sys/kernel.h> 48184251Smarcel#include <sys/malloc.h> 49184251Smarcel#include <sys/module.h> 50184251Smarcel#include <sys/rman.h> 51184251Smarcel#include <sys/sysctl.h> 52184251Smarcel 53184251Smarcel#include <machine/bus.h> 54184251Smarcel 55184251Smarcel#include <dev/cfi/cfi_reg.h> 56184251Smarcel#include <dev/cfi/cfi_var.h> 57184251Smarcel 58255207Sbrooksstatic void cfi_add_sysctls(struct cfi_softc *); 59255207Sbrooks 60184251Smarcelextern struct cdevsw cfi_cdevsw; 61184251Smarcel 62184251Smarcelchar cfi_driver_name[] = "cfi"; 63184251Smarceldevclass_t cfi_devclass; 64189606Ssamdevclass_t cfi_diskclass; 65184251Smarcel 66184251Smarceluint32_t 67233553Sjchandracfi_read_raw(struct cfi_softc *sc, u_int ofs) 68184251Smarcel{ 69184251Smarcel uint32_t val; 70184251Smarcel 71184251Smarcel ofs &= ~(sc->sc_width - 1); 72184251Smarcel switch (sc->sc_width) { 73184251Smarcel case 1: 74184251Smarcel val = bus_space_read_1(sc->sc_tag, sc->sc_handle, ofs); 75184251Smarcel break; 76184251Smarcel case 2: 77184251Smarcel val = bus_space_read_2(sc->sc_tag, sc->sc_handle, ofs); 78184251Smarcel break; 79184251Smarcel case 4: 80184251Smarcel val = bus_space_read_4(sc->sc_tag, sc->sc_handle, ofs); 81184251Smarcel break; 82184251Smarcel default: 83184251Smarcel val = ~0; 84184251Smarcel break; 85184251Smarcel } 86184251Smarcel return (val); 87184251Smarcel} 88184251Smarcel 89233553Sjchandrauint32_t 90233553Sjchandracfi_read(struct cfi_softc *sc, u_int ofs) 91233553Sjchandra{ 92233553Sjchandra uint32_t val; 93233553Sjchandra uint16_t sval; 94233553Sjchandra 95233553Sjchandra ofs &= ~(sc->sc_width - 1); 96233553Sjchandra switch (sc->sc_width) { 97233553Sjchandra case 1: 98233553Sjchandra val = bus_space_read_1(sc->sc_tag, sc->sc_handle, ofs); 99233553Sjchandra break; 100233553Sjchandra case 2: 101233553Sjchandra sval = bus_space_read_2(sc->sc_tag, sc->sc_handle, ofs); 102233553Sjchandra val = le16toh(sval); 103233553Sjchandra break; 104233553Sjchandra case 4: 105233553Sjchandra val = bus_space_read_4(sc->sc_tag, sc->sc_handle, ofs); 106233553Sjchandra val = le32toh(val); 107233553Sjchandra break; 108233553Sjchandra default: 109233553Sjchandra val = ~0; 110233553Sjchandra break; 111233553Sjchandra } 112233553Sjchandra return (val); 113233553Sjchandra} 114233553Sjchandra 115184251Smarcelstatic void 116184251Smarcelcfi_write(struct cfi_softc *sc, u_int ofs, u_int val) 117184251Smarcel{ 118184251Smarcel 119184251Smarcel ofs &= ~(sc->sc_width - 1); 120184251Smarcel switch (sc->sc_width) { 121184251Smarcel case 1: 122184251Smarcel bus_space_write_1(sc->sc_tag, sc->sc_handle, ofs, val); 123184251Smarcel break; 124184251Smarcel case 2: 125233553Sjchandra bus_space_write_2(sc->sc_tag, sc->sc_handle, ofs, htole16(val)); 126184251Smarcel break; 127184251Smarcel case 4: 128233553Sjchandra bus_space_write_4(sc->sc_tag, sc->sc_handle, ofs, htole32(val)); 129184251Smarcel break; 130184251Smarcel } 131184251Smarcel} 132184251Smarcel 133184251Smarceluint8_t 134184251Smarcelcfi_read_qry(struct cfi_softc *sc, u_int ofs) 135184251Smarcel{ 136184251Smarcel uint8_t val; 137184251Smarcel 138184251Smarcel cfi_write(sc, CFI_QRY_CMD_ADDR * sc->sc_width, CFI_QRY_CMD_DATA); 139184251Smarcel val = cfi_read(sc, ofs * sc->sc_width); 140184251Smarcel cfi_write(sc, 0, CFI_BCS_READ_ARRAY); 141184251Smarcel return (val); 142184251Smarcel} 143184251Smarcel 144184251Smarcelstatic void 145184251Smarcelcfi_amd_write(struct cfi_softc *sc, u_int ofs, u_int addr, u_int data) 146184251Smarcel{ 147184251Smarcel 148184251Smarcel cfi_write(sc, ofs + AMD_ADDR_START, CFI_AMD_UNLOCK); 149184251Smarcel cfi_write(sc, ofs + AMD_ADDR_ACK, CFI_AMD_UNLOCK_ACK); 150184251Smarcel cfi_write(sc, ofs + addr, data); 151184251Smarcel} 152184251Smarcel 153184251Smarcelstatic char * 154184251Smarcelcfi_fmtsize(uint32_t sz) 155184251Smarcel{ 156184251Smarcel static char buf[8]; 157184251Smarcel static const char *sfx[] = { "", "K", "M", "G" }; 158184251Smarcel int sfxidx; 159184251Smarcel 160184251Smarcel sfxidx = 0; 161184251Smarcel while (sfxidx < 3 && sz > 1023) { 162184251Smarcel sz /= 1024; 163184251Smarcel sfxidx++; 164184251Smarcel } 165184251Smarcel 166184251Smarcel sprintf(buf, "%u%sB", sz, sfx[sfxidx]); 167184251Smarcel return (buf); 168184251Smarcel} 169184251Smarcel 170184251Smarcelint 171184251Smarcelcfi_probe(device_t dev) 172184251Smarcel{ 173184251Smarcel char desc[80]; 174184251Smarcel struct cfi_softc *sc; 175184251Smarcel char *vend_str; 176184251Smarcel int error; 177184251Smarcel uint16_t iface, vend; 178184251Smarcel 179184251Smarcel sc = device_get_softc(dev); 180184251Smarcel sc->sc_dev = dev; 181184251Smarcel 182184251Smarcel sc->sc_rid = 0; 183184251Smarcel sc->sc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->sc_rid, 184184251Smarcel RF_ACTIVE); 185184251Smarcel if (sc->sc_res == NULL) 186184251Smarcel return (ENXIO); 187184251Smarcel 188184251Smarcel sc->sc_tag = rman_get_bustag(sc->sc_res); 189184251Smarcel sc->sc_handle = rman_get_bushandle(sc->sc_res); 190184251Smarcel 191188087Ssam if (sc->sc_width == 0) { 192188087Ssam sc->sc_width = 1; 193188087Ssam while (sc->sc_width <= 4) { 194188087Ssam if (cfi_read_qry(sc, CFI_QRY_IDENT) == 'Q') 195188087Ssam break; 196188087Ssam sc->sc_width <<= 1; 197188087Ssam } 198188087Ssam } else if (cfi_read_qry(sc, CFI_QRY_IDENT) != 'Q') { 199188087Ssam error = ENXIO; 200188087Ssam goto out; 201184251Smarcel } 202184251Smarcel if (sc->sc_width > 4) { 203184251Smarcel error = ENXIO; 204184251Smarcel goto out; 205184251Smarcel } 206184251Smarcel 207184251Smarcel /* We got a Q. Check if we also have the R and the Y. */ 208184251Smarcel if (cfi_read_qry(sc, CFI_QRY_IDENT + 1) != 'R' || 209184251Smarcel cfi_read_qry(sc, CFI_QRY_IDENT + 2) != 'Y') { 210184251Smarcel error = ENXIO; 211184251Smarcel goto out; 212184251Smarcel } 213184251Smarcel 214184251Smarcel /* Get the vendor and command set. */ 215184251Smarcel vend = cfi_read_qry(sc, CFI_QRY_VEND) | 216184251Smarcel (cfi_read_qry(sc, CFI_QRY_VEND + 1) << 8); 217184251Smarcel 218184251Smarcel sc->sc_cmdset = vend; 219184251Smarcel 220184251Smarcel switch (vend) { 221184251Smarcel case CFI_VEND_AMD_ECS: 222184251Smarcel case CFI_VEND_AMD_SCS: 223184251Smarcel vend_str = "AMD/Fujitsu"; 224184251Smarcel break; 225184251Smarcel case CFI_VEND_INTEL_ECS: 226184251Smarcel vend_str = "Intel/Sharp"; 227184251Smarcel break; 228184251Smarcel case CFI_VEND_INTEL_SCS: 229184251Smarcel vend_str = "Intel"; 230184251Smarcel break; 231184251Smarcel case CFI_VEND_MITSUBISHI_ECS: 232184251Smarcel case CFI_VEND_MITSUBISHI_SCS: 233184251Smarcel vend_str = "Mitsubishi"; 234184251Smarcel break; 235184251Smarcel default: 236184251Smarcel vend_str = "Unknown vendor"; 237184251Smarcel break; 238184251Smarcel } 239184251Smarcel 240184251Smarcel /* Get the device size. */ 241184251Smarcel sc->sc_size = 1U << cfi_read_qry(sc, CFI_QRY_SIZE); 242184251Smarcel 243184251Smarcel /* Sanity-check the I/F */ 244184251Smarcel iface = cfi_read_qry(sc, CFI_QRY_IFACE) | 245184251Smarcel (cfi_read_qry(sc, CFI_QRY_IFACE + 1) << 8); 246184251Smarcel 247184251Smarcel /* 248184251Smarcel * Adding 1 to iface will give us a bit-wise "switch" 249184251Smarcel * that allows us to test for the interface width by 250184251Smarcel * testing a single bit. 251184251Smarcel */ 252184251Smarcel iface++; 253184251Smarcel 254184251Smarcel error = (iface & sc->sc_width) ? 0 : EINVAL; 255184251Smarcel if (error) 256184251Smarcel goto out; 257184251Smarcel 258184251Smarcel snprintf(desc, sizeof(desc), "%s - %s", vend_str, 259184251Smarcel cfi_fmtsize(sc->sc_size)); 260184251Smarcel device_set_desc_copy(dev, desc); 261184251Smarcel 262184251Smarcel out: 263184251Smarcel bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_rid, sc->sc_res); 264184251Smarcel return (error); 265184251Smarcel} 266184251Smarcel 267184251Smarcelint 268184251Smarcelcfi_attach(device_t dev) 269184251Smarcel{ 270184251Smarcel struct cfi_softc *sc; 271184251Smarcel u_int blksz, blocks; 272184251Smarcel u_int r, u; 273255207Sbrooks uint64_t mtoexp, ttoexp; 274250115Sbrooks#ifdef CFI_SUPPORT_STRATAFLASH 275250115Sbrooks uint64_t ppr; 276250115Sbrooks char name[KENV_MNAMELEN], value[32]; 277250115Sbrooks#endif 278184251Smarcel 279184251Smarcel sc = device_get_softc(dev); 280184251Smarcel sc->sc_dev = dev; 281184251Smarcel 282184251Smarcel sc->sc_rid = 0; 283184251Smarcel sc->sc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->sc_rid, 284184251Smarcel RF_ACTIVE); 285184251Smarcel if (sc->sc_res == NULL) 286184251Smarcel return (ENXIO); 287184251Smarcel 288184251Smarcel sc->sc_tag = rman_get_bustag(sc->sc_res); 289184251Smarcel sc->sc_handle = rman_get_bushandle(sc->sc_res); 290184251Smarcel 291255207Sbrooks /* Get time-out values for erase, write, and buffer write. */ 292255207Sbrooks ttoexp = cfi_read_qry(sc, CFI_QRY_TTO_ERASE); 293255207Sbrooks mtoexp = cfi_read_qry(sc, CFI_QRY_MTO_ERASE); 294255207Sbrooks if (ttoexp == 0) { 295255207Sbrooks device_printf(dev, "erase timeout == 0, using 2^16ms\n"); 296255207Sbrooks ttoexp = 16; 297255207Sbrooks } 298255207Sbrooks if (ttoexp > 41) { 299255207Sbrooks device_printf(dev, "insane timeout: 2^%jdms\n", ttoexp); 300255207Sbrooks return (EINVAL); 301255207Sbrooks } 302255207Sbrooks if (mtoexp == 0) { 303255207Sbrooks device_printf(dev, "max erase timeout == 0, using 2^%jdms\n", 304255207Sbrooks ttoexp + 4); 305255207Sbrooks mtoexp = 4; 306255207Sbrooks } 307255207Sbrooks if (ttoexp + mtoexp > 41) { 308255207Sbrooks device_printf(dev, "insane max erase timeout: 2^%jd\n", 309255207Sbrooks ttoexp + mtoexp); 310255207Sbrooks return (EINVAL); 311255207Sbrooks } 312255207Sbrooks sc->sc_typical_timeouts[CFI_TIMEOUT_ERASE] = SBT_1MS * (1ULL << ttoexp); 313255207Sbrooks sc->sc_max_timeouts[CFI_TIMEOUT_ERASE] = 314255207Sbrooks sc->sc_typical_timeouts[CFI_TIMEOUT_ERASE] * (1ULL << mtoexp); 315184251Smarcel 316255207Sbrooks ttoexp = cfi_read_qry(sc, CFI_QRY_TTO_WRITE); 317255207Sbrooks mtoexp = cfi_read_qry(sc, CFI_QRY_MTO_WRITE); 318255207Sbrooks if (ttoexp == 0) { 319255207Sbrooks device_printf(dev, "write timeout == 0, using 2^18ns\n"); 320255207Sbrooks ttoexp = 18; 321255207Sbrooks } 322255207Sbrooks if (ttoexp > 51) { 323255207Sbrooks device_printf(dev, "insane write timeout: 2^%jdus\n", ttoexp); 324255207Sbrooks return (EINVAL); 325255207Sbrooks } 326255207Sbrooks if (mtoexp == 0) { 327255207Sbrooks device_printf(dev, "max write timeout == 0, using 2^%jdms\n", 328255207Sbrooks ttoexp + 4); 329255207Sbrooks mtoexp = 4; 330255207Sbrooks } 331255207Sbrooks if (ttoexp + mtoexp > 51) { 332255207Sbrooks device_printf(dev, "insane max write timeout: 2^%jdus\n", 333255207Sbrooks ttoexp + mtoexp); 334255207Sbrooks return (EINVAL); 335255207Sbrooks } 336255207Sbrooks sc->sc_typical_timeouts[CFI_TIMEOUT_WRITE] = SBT_1US * (1ULL << ttoexp); 337255207Sbrooks sc->sc_max_timeouts[CFI_TIMEOUT_WRITE] = 338255207Sbrooks sc->sc_typical_timeouts[CFI_TIMEOUT_WRITE] * (1ULL << mtoexp); 339255207Sbrooks 340255207Sbrooks ttoexp = cfi_read_qry(sc, CFI_QRY_TTO_BUFWRITE); 341255207Sbrooks mtoexp = cfi_read_qry(sc, CFI_QRY_MTO_BUFWRITE); 342255207Sbrooks /* Don't check for 0, it means not-supported. */ 343255207Sbrooks if (ttoexp > 51) { 344255207Sbrooks device_printf(dev, "insane write timeout: 2^%jdus\n", ttoexp); 345255207Sbrooks return (EINVAL); 346255207Sbrooks } 347255207Sbrooks if (ttoexp + mtoexp > 51) { 348255207Sbrooks device_printf(dev, "insane max write timeout: 2^%jdus\n", 349255207Sbrooks ttoexp + mtoexp); 350255207Sbrooks return (EINVAL); 351255207Sbrooks } 352255207Sbrooks sc->sc_typical_timeouts[CFI_TIMEOUT_BUFWRITE] = 353255207Sbrooks SBT_1US * (1ULL << cfi_read_qry(sc, CFI_QRY_TTO_BUFWRITE)); 354255207Sbrooks sc->sc_max_timeouts[CFI_TIMEOUT_BUFWRITE] = 355255207Sbrooks sc->sc_typical_timeouts[CFI_TIMEOUT_BUFWRITE] * 356255207Sbrooks (1ULL << cfi_read_qry(sc, CFI_QRY_MTO_BUFWRITE)); 357255207Sbrooks 358255207Sbrooks /* Get the maximum size of a multibyte program */ 359255207Sbrooks if (sc->sc_typical_timeouts[CFI_TIMEOUT_BUFWRITE] != 0) 360255207Sbrooks sc->sc_maxbuf = 1 << (cfi_read_qry(sc, CFI_QRY_MAXBUF) | 361255207Sbrooks cfi_read_qry(sc, CFI_QRY_MAXBUF) << 8); 362255207Sbrooks else 363255207Sbrooks sc->sc_maxbuf = 0; 364255207Sbrooks 365184251Smarcel /* Get erase regions. */ 366184251Smarcel sc->sc_regions = cfi_read_qry(sc, CFI_QRY_NREGIONS); 367184251Smarcel sc->sc_region = malloc(sc->sc_regions * sizeof(struct cfi_region), 368184251Smarcel M_TEMP, M_WAITOK | M_ZERO); 369184251Smarcel for (r = 0; r < sc->sc_regions; r++) { 370184251Smarcel blocks = cfi_read_qry(sc, CFI_QRY_REGION(r)) | 371184251Smarcel (cfi_read_qry(sc, CFI_QRY_REGION(r) + 1) << 8); 372184251Smarcel sc->sc_region[r].r_blocks = blocks + 1; 373184251Smarcel 374184251Smarcel blksz = cfi_read_qry(sc, CFI_QRY_REGION(r) + 2) | 375184251Smarcel (cfi_read_qry(sc, CFI_QRY_REGION(r) + 3) << 8); 376184251Smarcel sc->sc_region[r].r_blksz = (blksz == 0) ? 128 : 377184251Smarcel blksz * 256; 378184251Smarcel } 379184251Smarcel 380184251Smarcel /* Reset the device to a default state. */ 381184251Smarcel cfi_write(sc, 0, CFI_BCS_CLEAR_STATUS); 382184251Smarcel 383184251Smarcel if (bootverbose) { 384184251Smarcel device_printf(dev, "["); 385184251Smarcel for (r = 0; r < sc->sc_regions; r++) { 386184251Smarcel printf("%ux%s%s", sc->sc_region[r].r_blocks, 387184251Smarcel cfi_fmtsize(sc->sc_region[r].r_blksz), 388184251Smarcel (r == sc->sc_regions - 1) ? "]\n" : ","); 389184251Smarcel } 390184251Smarcel } 391184251Smarcel 392184251Smarcel u = device_get_unit(dev); 393184251Smarcel sc->sc_nod = make_dev(&cfi_cdevsw, u, UID_ROOT, GID_WHEEL, 0600, 394184251Smarcel "%s%u", cfi_driver_name, u); 395184251Smarcel sc->sc_nod->si_drv1 = sc; 396184251Smarcel 397255207Sbrooks cfi_add_sysctls(sc); 398255207Sbrooks 399250115Sbrooks#ifdef CFI_SUPPORT_STRATAFLASH 400250115Sbrooks /* 401250115Sbrooks * Store the Intel factory PPR in the environment. In some 402250115Sbrooks * cases it is the most unique ID on a board. 403250115Sbrooks */ 404250115Sbrooks if (cfi_intel_get_factory_pr(sc, &ppr) == 0) { 405250115Sbrooks if (snprintf(name, sizeof(name), "%s.factory_ppr", 406250115Sbrooks device_get_nameunit(dev)) < (sizeof(name) - 1) && 407250115Sbrooks snprintf(value, sizeof(value), "0x%016jx", ppr) < 408250115Sbrooks (sizeof(value) - 1)) 409250115Sbrooks (void) setenv(name, value); 410250115Sbrooks } 411250115Sbrooks#endif 412250115Sbrooks 413193936Simp device_add_child(dev, "cfid", -1); 414189606Ssam bus_generic_attach(dev); 415189606Ssam 416184251Smarcel return (0); 417184251Smarcel} 418184251Smarcel 419255207Sbrooksstatic void 420255207Sbrookscfi_add_sysctls(struct cfi_softc *sc) 421255207Sbrooks{ 422255207Sbrooks struct sysctl_ctx_list *ctx; 423255207Sbrooks struct sysctl_oid_list *children; 424255207Sbrooks 425255207Sbrooks ctx = device_get_sysctl_ctx(sc->sc_dev); 426255207Sbrooks children = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->sc_dev)); 427255207Sbrooks 428255207Sbrooks SYSCTL_ADD_UINT(ctx, children, OID_AUTO, 429255207Sbrooks "typical_erase_timout_count", 430255207Sbrooks CTLFLAG_RD, &sc->sc_tto_counts[CFI_TIMEOUT_ERASE], 431255207Sbrooks 0, "Number of times the typical erase timeout was exceeded"); 432255207Sbrooks SYSCTL_ADD_UINT(ctx, children, OID_AUTO, 433255207Sbrooks "max_erase_timout_count", 434255207Sbrooks CTLFLAG_RD, &sc->sc_mto_counts[CFI_TIMEOUT_ERASE], 0, 435255207Sbrooks "Number of times the maximum erase timeout was exceeded"); 436255207Sbrooks SYSCTL_ADD_UINT(ctx, children, OID_AUTO, 437255207Sbrooks "typical_write_timout_count", 438255207Sbrooks CTLFLAG_RD, &sc->sc_tto_counts[CFI_TIMEOUT_WRITE], 0, 439255207Sbrooks "Number of times the typical write timeout was exceeded"); 440255207Sbrooks SYSCTL_ADD_UINT(ctx, children, OID_AUTO, 441255207Sbrooks "max_write_timout_count", 442255207Sbrooks CTLFLAG_RD, &sc->sc_mto_counts[CFI_TIMEOUT_WRITE], 0, 443255207Sbrooks "Number of times the maximum write timeout was exceeded"); 444255207Sbrooks if (sc->sc_maxbuf > 0) { 445255207Sbrooks SYSCTL_ADD_UINT(ctx, children, OID_AUTO, 446255207Sbrooks "typical_bufwrite_timout_count", 447255207Sbrooks CTLFLAG_RD, &sc->sc_tto_counts[CFI_TIMEOUT_BUFWRITE], 0, 448255207Sbrooks "Number of times the typical buffered write timeout was " 449255207Sbrooks "exceeded"); 450255207Sbrooks SYSCTL_ADD_UINT(ctx, children, OID_AUTO, 451255207Sbrooks "max_bufwrite_timout_count", 452255207Sbrooks CTLFLAG_RD, &sc->sc_mto_counts[CFI_TIMEOUT_BUFWRITE], 0, 453255207Sbrooks "Number of times the maximum buffered write timeout was " 454255207Sbrooks "exceeded"); 455255207Sbrooks } 456255207Sbrooks} 457255207Sbrooks 458184251Smarcelint 459184251Smarcelcfi_detach(device_t dev) 460184251Smarcel{ 461184251Smarcel struct cfi_softc *sc; 462184251Smarcel 463184251Smarcel sc = device_get_softc(dev); 464184251Smarcel 465184251Smarcel destroy_dev(sc->sc_nod); 466184251Smarcel free(sc->sc_region, M_TEMP); 467184251Smarcel bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_rid, sc->sc_res); 468184251Smarcel return (0); 469184251Smarcel} 470184251Smarcel 471184251Smarcelstatic int 472255207Sbrookscfi_wait_ready(struct cfi_softc *sc, u_int ofs, sbintime_t start, 473255207Sbrooks enum cfi_wait_cmd cmd) 474184251Smarcel{ 475255207Sbrooks int done, error, tto_exceeded; 476188156Ssam uint32_t st0 = 0, st = 0; 477255207Sbrooks sbintime_t now; 478184251Smarcel 479184251Smarcel done = 0; 480184251Smarcel error = 0; 481255207Sbrooks tto_exceeded = 0; 482255207Sbrooks while (!done && !error) { 483255207Sbrooks /* 484255207Sbrooks * Save time before we start so we always do one check 485255207Sbrooks * after the timeout has expired. 486255207Sbrooks */ 487255207Sbrooks now = sbinuptime(); 488184251Smarcel 489184251Smarcel switch (sc->sc_cmdset) { 490184251Smarcel case CFI_VEND_INTEL_ECS: 491184251Smarcel case CFI_VEND_INTEL_SCS: 492188156Ssam st = cfi_read(sc, ofs); 493188156Ssam done = (st & CFI_INTEL_STATUS_WSMS); 494184251Smarcel if (done) { 495188156Ssam /* NB: bit 0 is reserved */ 496188156Ssam st &= ~(CFI_INTEL_XSTATUS_RSVD | 497188156Ssam CFI_INTEL_STATUS_WSMS | 498188156Ssam CFI_INTEL_STATUS_RSVD); 499188156Ssam if (st & CFI_INTEL_STATUS_DPS) 500184251Smarcel error = EPERM; 501188156Ssam else if (st & CFI_INTEL_STATUS_PSLBS) 502184251Smarcel error = EIO; 503188156Ssam else if (st & CFI_INTEL_STATUS_ECLBS) 504184251Smarcel error = ENXIO; 505188156Ssam else if (st) 506188156Ssam error = EACCES; 507184251Smarcel } 508184251Smarcel break; 509184251Smarcel case CFI_VEND_AMD_SCS: 510184251Smarcel case CFI_VEND_AMD_ECS: 511188156Ssam st0 = cfi_read(sc, ofs); 512188156Ssam st = cfi_read(sc, ofs); 513184251Smarcel done = ((st & 0x40) == (st0 & 0x40)) ? 1 : 0; 514184251Smarcel break; 515184251Smarcel } 516255207Sbrooks 517255207Sbrooks if (tto_exceeded || 518255207Sbrooks now > start + sc->sc_typical_timeouts[cmd]) { 519255207Sbrooks if (!tto_exceeded) { 520255207Sbrooks tto_exceeded = 1; 521255207Sbrooks sc->sc_tto_counts[cmd]++; 522255207Sbrooks#ifdef CFI_DEBUG_TIMEOUT 523255207Sbrooks device_printf(sc->sc_dev, 524255207Sbrooks "typical timeout exceeded (cmd %d)", cmd); 525255207Sbrooks#endif 526255207Sbrooks } 527255207Sbrooks if (now > start + sc->sc_max_timeouts[cmd]) { 528255207Sbrooks sc->sc_mto_counts[cmd]++; 529255207Sbrooks#ifdef CFI_DEBUG_TIMEOUT 530255207Sbrooks device_printf(sc->sc_dev, 531255207Sbrooks "max timeout exceeded (cmd %d)", cmd); 532255207Sbrooks#endif 533255207Sbrooks } 534255207Sbrooks } 535184251Smarcel } 536184251Smarcel if (!done && !error) 537184251Smarcel error = ETIMEDOUT; 538184251Smarcel if (error) 539188156Ssam printf("\nerror=%d (st 0x%x st0 0x%x)\n", error, st, st0); 540184251Smarcel return (error); 541184251Smarcel} 542184251Smarcel 543184251Smarcelint 544184251Smarcelcfi_write_block(struct cfi_softc *sc) 545184251Smarcel{ 546184251Smarcel union { 547184251Smarcel uint8_t *x8; 548184251Smarcel uint16_t *x16; 549184251Smarcel uint32_t *x32; 550255207Sbrooks } ptr, cpyprt; 551184251Smarcel register_t intr; 552255207Sbrooks int error, i, neederase = 0; 553255207Sbrooks uint32_t st; 554255207Sbrooks u_int wlen; 555255207Sbrooks sbintime_t start; 556184251Smarcel 557251118Sbrooks /* Intel flash must be unlocked before modification */ 558251118Sbrooks switch (sc->sc_cmdset) { 559251118Sbrooks case CFI_VEND_INTEL_ECS: 560251118Sbrooks case CFI_VEND_INTEL_SCS: 561251118Sbrooks cfi_write(sc, sc->sc_wrofs, CFI_INTEL_LBS); 562251118Sbrooks cfi_write(sc, sc->sc_wrofs, CFI_INTEL_UB); 563251118Sbrooks cfi_write(sc, sc->sc_wrofs, CFI_BCS_READ_ARRAY); 564251118Sbrooks break; 565251118Sbrooks } 566251118Sbrooks 567255207Sbrooks /* Check if an erase is required. */ 568255207Sbrooks for (i = 0; i < sc->sc_wrbufsz; i++) 569255207Sbrooks if ((sc->sc_wrbuf[i] & sc->sc_wrbufcpy[i]) != sc->sc_wrbuf[i]) { 570255207Sbrooks neederase = 1; 571255207Sbrooks break; 572255207Sbrooks } 573255207Sbrooks 574255207Sbrooks if (neederase) { 575255207Sbrooks intr = intr_disable(); 576255207Sbrooks start = sbinuptime(); 577255207Sbrooks /* Erase the block. */ 578255207Sbrooks switch (sc->sc_cmdset) { 579255207Sbrooks case CFI_VEND_INTEL_ECS: 580255207Sbrooks case CFI_VEND_INTEL_SCS: 581255207Sbrooks cfi_write(sc, sc->sc_wrofs, CFI_BCS_BLOCK_ERASE); 582255207Sbrooks cfi_write(sc, sc->sc_wrofs, CFI_BCS_CONFIRM); 583255207Sbrooks break; 584255207Sbrooks case CFI_VEND_AMD_SCS: 585255207Sbrooks case CFI_VEND_AMD_ECS: 586255207Sbrooks cfi_amd_write(sc, sc->sc_wrofs, AMD_ADDR_START, 587255207Sbrooks CFI_AMD_ERASE_SECTOR); 588255207Sbrooks cfi_amd_write(sc, sc->sc_wrofs, 0, CFI_AMD_BLOCK_ERASE); 589255207Sbrooks break; 590255207Sbrooks default: 591255207Sbrooks /* Better safe than sorry... */ 592255207Sbrooks intr_restore(intr); 593255207Sbrooks return (ENODEV); 594255207Sbrooks } 595255207Sbrooks intr_restore(intr); 596255207Sbrooks error = cfi_wait_ready(sc, sc->sc_wrofs, start, 597255207Sbrooks CFI_TIMEOUT_ERASE); 598255207Sbrooks if (error) 599255207Sbrooks goto out; 600255207Sbrooks } else 601255207Sbrooks error = 0; 602255207Sbrooks 603255207Sbrooks /* Write the block using a multibyte write if supported. */ 604255207Sbrooks ptr.x8 = sc->sc_wrbuf; 605255207Sbrooks cpyprt.x8 = sc->sc_wrbufcpy; 606255207Sbrooks if (sc->sc_maxbuf > sc->sc_width) { 607255207Sbrooks switch (sc->sc_cmdset) { 608255207Sbrooks case CFI_VEND_INTEL_ECS: 609255207Sbrooks case CFI_VEND_INTEL_SCS: 610255207Sbrooks for (i = 0; i < sc->sc_wrbufsz; i += wlen) { 611255207Sbrooks wlen = MIN(sc->sc_maxbuf, sc->sc_wrbufsz - i); 612255207Sbrooks 613255207Sbrooks intr = intr_disable(); 614255207Sbrooks 615255207Sbrooks start = sbinuptime(); 616255207Sbrooks do { 617255207Sbrooks cfi_write(sc, sc->sc_wrofs + i, 618255207Sbrooks CFI_BCS_BUF_PROG_SETUP); 619255207Sbrooks if (sbinuptime() > start + sc->sc_max_timeouts[CFI_TIMEOUT_BUFWRITE]) { 620255207Sbrooks error = ETIMEDOUT; 621255207Sbrooks goto out; 622255207Sbrooks } 623255207Sbrooks st = cfi_read(sc, sc->sc_wrofs + i); 624255207Sbrooks } while (! (st & CFI_INTEL_STATUS_WSMS)); 625255207Sbrooks 626255207Sbrooks cfi_write(sc, sc->sc_wrofs + i, 627255207Sbrooks (wlen / sc->sc_width) - 1); 628255207Sbrooks switch (sc->sc_width) { 629255207Sbrooks case 1: 630255207Sbrooks bus_space_write_region_1(sc->sc_tag, 631255207Sbrooks sc->sc_handle, sc->sc_wrofs + i, 632255207Sbrooks ptr.x8 + i, wlen); 633255207Sbrooks break; 634255207Sbrooks case 2: 635255207Sbrooks bus_space_write_region_2(sc->sc_tag, 636255207Sbrooks sc->sc_handle, sc->sc_wrofs + i, 637255207Sbrooks ptr.x16 + i / 2, wlen / 2); 638255207Sbrooks break; 639255207Sbrooks case 4: 640255207Sbrooks bus_space_write_region_4(sc->sc_tag, 641255207Sbrooks sc->sc_handle, sc->sc_wrofs + i, 642255207Sbrooks ptr.x32 + i / 4, wlen / 4); 643255207Sbrooks break; 644255207Sbrooks } 645255207Sbrooks 646255207Sbrooks cfi_write(sc, sc->sc_wrofs + i, 647255207Sbrooks CFI_BCS_CONFIRM); 648255207Sbrooks 649255207Sbrooks intr_restore(intr); 650255207Sbrooks 651255207Sbrooks error = cfi_wait_ready(sc, sc->sc_wrofs + i, 652255207Sbrooks start, CFI_TIMEOUT_BUFWRITE); 653255207Sbrooks if (error != 0) 654255207Sbrooks goto out; 655255207Sbrooks } 656255207Sbrooks goto out; 657255207Sbrooks default: 658255207Sbrooks /* Fall through to single word case */ 659255207Sbrooks break; 660255207Sbrooks } 661255207Sbrooks 662184251Smarcel } 663184251Smarcel 664255207Sbrooks /* Write the block one byte/word at a time. */ 665184251Smarcel for (i = 0; i < sc->sc_wrbufsz; i += sc->sc_width) { 666184251Smarcel 667255207Sbrooks /* Avoid writing unless we are actually changing bits */ 668255207Sbrooks if (!neederase) { 669255207Sbrooks switch (sc->sc_width) { 670255207Sbrooks case 1: 671255207Sbrooks if(*(ptr.x8 + i) == *(cpyprt.x8 + i)) 672255207Sbrooks continue; 673255207Sbrooks break; 674255207Sbrooks case 2: 675255207Sbrooks if(*(ptr.x16 + i / 2) == *(cpyprt.x16 + i / 2)) 676255207Sbrooks continue; 677255207Sbrooks break; 678255207Sbrooks case 4: 679255207Sbrooks if(*(ptr.x32 + i / 4) == *(cpyprt.x32 + i / 4)) 680255207Sbrooks continue; 681255207Sbrooks break; 682255207Sbrooks } 683255207Sbrooks } 684255207Sbrooks 685184251Smarcel /* 686184251Smarcel * Make sure the command to start a write and the 687184251Smarcel * actual write happens back-to-back without any 688184251Smarcel * excessive delays. 689184251Smarcel */ 690184251Smarcel intr = intr_disable(); 691184251Smarcel 692255207Sbrooks start = sbinuptime(); 693184251Smarcel switch (sc->sc_cmdset) { 694184251Smarcel case CFI_VEND_INTEL_ECS: 695184251Smarcel case CFI_VEND_INTEL_SCS: 696184251Smarcel cfi_write(sc, sc->sc_wrofs + i, CFI_BCS_PROGRAM); 697184251Smarcel break; 698184251Smarcel case CFI_VEND_AMD_SCS: 699184251Smarcel case CFI_VEND_AMD_ECS: 700184251Smarcel cfi_amd_write(sc, 0, AMD_ADDR_START, CFI_AMD_PROGRAM); 701184251Smarcel break; 702184251Smarcel } 703184251Smarcel switch (sc->sc_width) { 704184251Smarcel case 1: 705184251Smarcel bus_space_write_1(sc->sc_tag, sc->sc_handle, 706255207Sbrooks sc->sc_wrofs + i, *(ptr.x8 + i)); 707184251Smarcel break; 708184251Smarcel case 2: 709184251Smarcel bus_space_write_2(sc->sc_tag, sc->sc_handle, 710255207Sbrooks sc->sc_wrofs + i, *(ptr.x16 + i / 2)); 711184251Smarcel break; 712184251Smarcel case 4: 713184251Smarcel bus_space_write_4(sc->sc_tag, sc->sc_handle, 714255207Sbrooks sc->sc_wrofs + i, *(ptr.x32 + i / 4)); 715184251Smarcel break; 716184251Smarcel } 717255207Sbrooks 718184251Smarcel intr_restore(intr); 719184251Smarcel 720255207Sbrooks error = cfi_wait_ready(sc, sc->sc_wrofs, start, 721255207Sbrooks CFI_TIMEOUT_WRITE); 722184251Smarcel if (error) 723184251Smarcel goto out; 724184251Smarcel } 725184251Smarcel 726184251Smarcel /* error is 0. */ 727184251Smarcel 728184251Smarcel out: 729184251Smarcel cfi_write(sc, 0, CFI_BCS_READ_ARRAY); 730251118Sbrooks 731251118Sbrooks /* Relock Intel flash */ 732251118Sbrooks switch (sc->sc_cmdset) { 733251118Sbrooks case CFI_VEND_INTEL_ECS: 734251118Sbrooks case CFI_VEND_INTEL_SCS: 735251118Sbrooks cfi_write(sc, sc->sc_wrofs, CFI_INTEL_LBS); 736251118Sbrooks cfi_write(sc, sc->sc_wrofs, CFI_INTEL_LB); 737251118Sbrooks cfi_write(sc, sc->sc_wrofs, CFI_BCS_READ_ARRAY); 738251118Sbrooks break; 739251118Sbrooks } 740184251Smarcel return (error); 741184251Smarcel} 742188156Ssam 743188156Ssam#ifdef CFI_SUPPORT_STRATAFLASH 744188156Ssam/* 745188156Ssam * Intel StrataFlash Protection Register Support. 746188156Ssam * 747188156Ssam * The memory includes a 128-bit Protection Register that can be 748188156Ssam * used for security. There are two 64-bit segments; one is programmed 749188156Ssam * at the factory with a unique 64-bit number which is immutable. 750188156Ssam * The other segment is left blank for User (OEM) programming. 751188267Ssam * The User/OEM segment is One Time Programmable (OTP). It can also 752188332Ssam * be locked to prevent any further writes by setting bit 0 of the 753188267Ssam * Protection Lock Register (PLR). The PLR can written only once. 754188156Ssam */ 755188156Ssam 756188156Ssamstatic uint16_t 757188156Ssamcfi_get16(struct cfi_softc *sc, int off) 758188156Ssam{ 759188156Ssam uint16_t v = bus_space_read_2(sc->sc_tag, sc->sc_handle, off<<1); 760188156Ssam return v; 761188156Ssam} 762188156Ssam 763188268Ssam#ifdef CFI_ARMEDANDDANGEROUS 764188156Ssamstatic void 765188156Ssamcfi_put16(struct cfi_softc *sc, int off, uint16_t v) 766188156Ssam{ 767188156Ssam bus_space_write_2(sc->sc_tag, sc->sc_handle, off<<1, v); 768188156Ssam} 769188268Ssam#endif 770188156Ssam 771188156Ssam/* 772188156Ssam * Read the factory-defined 64-bit segment of the PR. 773188156Ssam */ 774188156Ssamint 775188156Ssamcfi_intel_get_factory_pr(struct cfi_softc *sc, uint64_t *id) 776188156Ssam{ 777188156Ssam if (sc->sc_cmdset != CFI_VEND_INTEL_ECS) 778188156Ssam return EOPNOTSUPP; 779188156Ssam KASSERT(sc->sc_width == 2, ("sc_width %d", sc->sc_width)); 780188156Ssam 781188156Ssam cfi_write(sc, 0, CFI_INTEL_READ_ID); 782188156Ssam *id = ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(0)))<<48 | 783188156Ssam ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(1)))<<32 | 784188156Ssam ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(2)))<<16 | 785188156Ssam ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(3))); 786188156Ssam cfi_write(sc, 0, CFI_BCS_READ_ARRAY); 787188156Ssam return 0; 788188156Ssam} 789188156Ssam 790188156Ssam/* 791188156Ssam * Read the User/OEM 64-bit segment of the PR. 792188156Ssam */ 793188156Ssamint 794188156Ssamcfi_intel_get_oem_pr(struct cfi_softc *sc, uint64_t *id) 795188156Ssam{ 796188156Ssam if (sc->sc_cmdset != CFI_VEND_INTEL_ECS) 797188156Ssam return EOPNOTSUPP; 798188156Ssam KASSERT(sc->sc_width == 2, ("sc_width %d", sc->sc_width)); 799188156Ssam 800188156Ssam cfi_write(sc, 0, CFI_INTEL_READ_ID); 801188156Ssam *id = ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(4)))<<48 | 802188156Ssam ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(5)))<<32 | 803188156Ssam ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(6)))<<16 | 804188156Ssam ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(7))); 805188156Ssam cfi_write(sc, 0, CFI_BCS_READ_ARRAY); 806188156Ssam return 0; 807188156Ssam} 808188156Ssam 809188156Ssam/* 810188156Ssam * Write the User/OEM 64-bit segment of the PR. 811188267Ssam * XXX should allow writing individual words/bytes 812188156Ssam */ 813188156Ssamint 814188156Ssamcfi_intel_set_oem_pr(struct cfi_softc *sc, uint64_t id) 815188156Ssam{ 816188267Ssam#ifdef CFI_ARMEDANDDANGEROUS 817188156Ssam register_t intr; 818188156Ssam int i, error; 819255207Sbrooks sbintime_t start; 820188267Ssam#endif 821188156Ssam 822188156Ssam if (sc->sc_cmdset != CFI_VEND_INTEL_ECS) 823188156Ssam return EOPNOTSUPP; 824188156Ssam KASSERT(sc->sc_width == 2, ("sc_width %d", sc->sc_width)); 825188156Ssam 826188267Ssam#ifdef CFI_ARMEDANDDANGEROUS 827188156Ssam for (i = 7; i >= 4; i--, id >>= 16) { 828188156Ssam intr = intr_disable(); 829255207Sbrooks start = sbinuptime(); 830188156Ssam cfi_write(sc, 0, CFI_INTEL_PP_SETUP); 831188156Ssam cfi_put16(sc, CFI_INTEL_PR(i), id&0xffff); 832188156Ssam intr_restore(intr); 833255207Sbrooks error = cfi_wait_ready(sc, CFI_BCS_READ_STATUS, start, 834255207Sbrooks CFI_TIMEOUT_WRITE); 835188156Ssam if (error) 836188156Ssam break; 837188156Ssam } 838188156Ssam cfi_write(sc, 0, CFI_BCS_READ_ARRAY); 839188156Ssam return error; 840188267Ssam#else 841188267Ssam device_printf(sc->sc_dev, "%s: OEM PR not set, " 842188267Ssam "CFI_ARMEDANDDANGEROUS not configured\n", __func__); 843188267Ssam return ENXIO; 844188267Ssam#endif 845188156Ssam} 846188156Ssam 847188156Ssam/* 848188156Ssam * Read the contents of the Protection Lock Register. 849188156Ssam */ 850188156Ssamint 851188156Ssamcfi_intel_get_plr(struct cfi_softc *sc, uint32_t *plr) 852188156Ssam{ 853188156Ssam if (sc->sc_cmdset != CFI_VEND_INTEL_ECS) 854188156Ssam return EOPNOTSUPP; 855188156Ssam KASSERT(sc->sc_width == 2, ("sc_width %d", sc->sc_width)); 856188156Ssam 857188156Ssam cfi_write(sc, 0, CFI_INTEL_READ_ID); 858188156Ssam *plr = cfi_get16(sc, CFI_INTEL_PLR); 859188156Ssam cfi_write(sc, 0, CFI_BCS_READ_ARRAY); 860188156Ssam return 0; 861188156Ssam} 862188156Ssam 863188156Ssam/* 864188156Ssam * Write the Protection Lock Register to lock down the 865188156Ssam * user-settable segment of the Protection Register. 866188156Ssam * NOTE: this operation is not reversible. 867188156Ssam */ 868188156Ssamint 869188156Ssamcfi_intel_set_plr(struct cfi_softc *sc) 870188156Ssam{ 871188156Ssam#ifdef CFI_ARMEDANDDANGEROUS 872188156Ssam register_t intr; 873188268Ssam int error; 874255207Sbrooks sbintime_t start; 875188156Ssam#endif 876188156Ssam if (sc->sc_cmdset != CFI_VEND_INTEL_ECS) 877188156Ssam return EOPNOTSUPP; 878188156Ssam KASSERT(sc->sc_width == 2, ("sc_width %d", sc->sc_width)); 879188156Ssam 880188156Ssam#ifdef CFI_ARMEDANDDANGEROUS 881188156Ssam /* worthy of console msg */ 882188156Ssam device_printf(sc->sc_dev, "set PLR\n"); 883188156Ssam intr = intr_disable(); 884255207Sbrooks binuptime(&start); 885188156Ssam cfi_write(sc, 0, CFI_INTEL_PP_SETUP); 886188156Ssam cfi_put16(sc, CFI_INTEL_PLR, 0xFFFD); 887188156Ssam intr_restore(intr); 888255207Sbrooks error = cfi_wait_ready(sc, CFI_BCS_READ_STATUS, start, 889255207Sbrooks CFI_TIMEOUT_WRITE); 890188156Ssam cfi_write(sc, 0, CFI_BCS_READ_ARRAY); 891188268Ssam return error; 892188156Ssam#else 893188156Ssam device_printf(sc->sc_dev, "%s: PLR not set, " 894188156Ssam "CFI_ARMEDANDDANGEROUS not configured\n", __func__); 895188268Ssam return ENXIO; 896188156Ssam#endif 897188156Ssam} 898188156Ssam#endif /* CFI_SUPPORT_STRATAFLASH */ 899