cfi_core.c revision 256753
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 256753 2013-10-18 20:52:42Z 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, 284256753Sbrooks#ifndef ATSE_CFI_HACK 285184251Smarcel RF_ACTIVE); 286256753Sbrooks#else 287256753Sbrooks RF_ACTIVE | RF_SHAREABLE); 288256753Sbrooks#endif 289184251Smarcel if (sc->sc_res == NULL) 290184251Smarcel return (ENXIO); 291184251Smarcel 292184251Smarcel sc->sc_tag = rman_get_bustag(sc->sc_res); 293184251Smarcel sc->sc_handle = rman_get_bushandle(sc->sc_res); 294184251Smarcel 295255207Sbrooks /* Get time-out values for erase, write, and buffer write. */ 296255207Sbrooks ttoexp = cfi_read_qry(sc, CFI_QRY_TTO_ERASE); 297255207Sbrooks mtoexp = cfi_read_qry(sc, CFI_QRY_MTO_ERASE); 298255207Sbrooks if (ttoexp == 0) { 299255207Sbrooks device_printf(dev, "erase timeout == 0, using 2^16ms\n"); 300255207Sbrooks ttoexp = 16; 301255207Sbrooks } 302255207Sbrooks if (ttoexp > 41) { 303255207Sbrooks device_printf(dev, "insane timeout: 2^%jdms\n", ttoexp); 304255207Sbrooks return (EINVAL); 305255207Sbrooks } 306255207Sbrooks if (mtoexp == 0) { 307255207Sbrooks device_printf(dev, "max erase timeout == 0, using 2^%jdms\n", 308255207Sbrooks ttoexp + 4); 309255207Sbrooks mtoexp = 4; 310255207Sbrooks } 311255207Sbrooks if (ttoexp + mtoexp > 41) { 312255207Sbrooks device_printf(dev, "insane max erase timeout: 2^%jd\n", 313255207Sbrooks ttoexp + mtoexp); 314255207Sbrooks return (EINVAL); 315255207Sbrooks } 316255207Sbrooks sc->sc_typical_timeouts[CFI_TIMEOUT_ERASE] = SBT_1MS * (1ULL << ttoexp); 317255207Sbrooks sc->sc_max_timeouts[CFI_TIMEOUT_ERASE] = 318255207Sbrooks sc->sc_typical_timeouts[CFI_TIMEOUT_ERASE] * (1ULL << mtoexp); 319184251Smarcel 320255207Sbrooks ttoexp = cfi_read_qry(sc, CFI_QRY_TTO_WRITE); 321255207Sbrooks mtoexp = cfi_read_qry(sc, CFI_QRY_MTO_WRITE); 322255207Sbrooks if (ttoexp == 0) { 323255207Sbrooks device_printf(dev, "write timeout == 0, using 2^18ns\n"); 324255207Sbrooks ttoexp = 18; 325255207Sbrooks } 326255207Sbrooks if (ttoexp > 51) { 327255207Sbrooks device_printf(dev, "insane write timeout: 2^%jdus\n", ttoexp); 328255207Sbrooks return (EINVAL); 329255207Sbrooks } 330255207Sbrooks if (mtoexp == 0) { 331255207Sbrooks device_printf(dev, "max write timeout == 0, using 2^%jdms\n", 332255207Sbrooks ttoexp + 4); 333255207Sbrooks mtoexp = 4; 334255207Sbrooks } 335255207Sbrooks if (ttoexp + mtoexp > 51) { 336255207Sbrooks device_printf(dev, "insane max write timeout: 2^%jdus\n", 337255207Sbrooks ttoexp + mtoexp); 338255207Sbrooks return (EINVAL); 339255207Sbrooks } 340255207Sbrooks sc->sc_typical_timeouts[CFI_TIMEOUT_WRITE] = SBT_1US * (1ULL << ttoexp); 341255207Sbrooks sc->sc_max_timeouts[CFI_TIMEOUT_WRITE] = 342255207Sbrooks sc->sc_typical_timeouts[CFI_TIMEOUT_WRITE] * (1ULL << mtoexp); 343255207Sbrooks 344255207Sbrooks ttoexp = cfi_read_qry(sc, CFI_QRY_TTO_BUFWRITE); 345255207Sbrooks mtoexp = cfi_read_qry(sc, CFI_QRY_MTO_BUFWRITE); 346255207Sbrooks /* Don't check for 0, it means not-supported. */ 347255207Sbrooks if (ttoexp > 51) { 348255207Sbrooks device_printf(dev, "insane write timeout: 2^%jdus\n", ttoexp); 349255207Sbrooks return (EINVAL); 350255207Sbrooks } 351255207Sbrooks if (ttoexp + mtoexp > 51) { 352255207Sbrooks device_printf(dev, "insane max write timeout: 2^%jdus\n", 353255207Sbrooks ttoexp + mtoexp); 354255207Sbrooks return (EINVAL); 355255207Sbrooks } 356255207Sbrooks sc->sc_typical_timeouts[CFI_TIMEOUT_BUFWRITE] = 357255207Sbrooks SBT_1US * (1ULL << cfi_read_qry(sc, CFI_QRY_TTO_BUFWRITE)); 358255207Sbrooks sc->sc_max_timeouts[CFI_TIMEOUT_BUFWRITE] = 359255207Sbrooks sc->sc_typical_timeouts[CFI_TIMEOUT_BUFWRITE] * 360255207Sbrooks (1ULL << cfi_read_qry(sc, CFI_QRY_MTO_BUFWRITE)); 361255207Sbrooks 362255207Sbrooks /* Get the maximum size of a multibyte program */ 363255207Sbrooks if (sc->sc_typical_timeouts[CFI_TIMEOUT_BUFWRITE] != 0) 364255207Sbrooks sc->sc_maxbuf = 1 << (cfi_read_qry(sc, CFI_QRY_MAXBUF) | 365255207Sbrooks cfi_read_qry(sc, CFI_QRY_MAXBUF) << 8); 366255207Sbrooks else 367255207Sbrooks sc->sc_maxbuf = 0; 368255207Sbrooks 369184251Smarcel /* Get erase regions. */ 370184251Smarcel sc->sc_regions = cfi_read_qry(sc, CFI_QRY_NREGIONS); 371184251Smarcel sc->sc_region = malloc(sc->sc_regions * sizeof(struct cfi_region), 372184251Smarcel M_TEMP, M_WAITOK | M_ZERO); 373184251Smarcel for (r = 0; r < sc->sc_regions; r++) { 374184251Smarcel blocks = cfi_read_qry(sc, CFI_QRY_REGION(r)) | 375184251Smarcel (cfi_read_qry(sc, CFI_QRY_REGION(r) + 1) << 8); 376184251Smarcel sc->sc_region[r].r_blocks = blocks + 1; 377184251Smarcel 378184251Smarcel blksz = cfi_read_qry(sc, CFI_QRY_REGION(r) + 2) | 379184251Smarcel (cfi_read_qry(sc, CFI_QRY_REGION(r) + 3) << 8); 380184251Smarcel sc->sc_region[r].r_blksz = (blksz == 0) ? 128 : 381184251Smarcel blksz * 256; 382184251Smarcel } 383184251Smarcel 384184251Smarcel /* Reset the device to a default state. */ 385184251Smarcel cfi_write(sc, 0, CFI_BCS_CLEAR_STATUS); 386184251Smarcel 387184251Smarcel if (bootverbose) { 388184251Smarcel device_printf(dev, "["); 389184251Smarcel for (r = 0; r < sc->sc_regions; r++) { 390184251Smarcel printf("%ux%s%s", sc->sc_region[r].r_blocks, 391184251Smarcel cfi_fmtsize(sc->sc_region[r].r_blksz), 392184251Smarcel (r == sc->sc_regions - 1) ? "]\n" : ","); 393184251Smarcel } 394184251Smarcel } 395184251Smarcel 396184251Smarcel u = device_get_unit(dev); 397184251Smarcel sc->sc_nod = make_dev(&cfi_cdevsw, u, UID_ROOT, GID_WHEEL, 0600, 398184251Smarcel "%s%u", cfi_driver_name, u); 399184251Smarcel sc->sc_nod->si_drv1 = sc; 400184251Smarcel 401255207Sbrooks cfi_add_sysctls(sc); 402255207Sbrooks 403250115Sbrooks#ifdef CFI_SUPPORT_STRATAFLASH 404250115Sbrooks /* 405250115Sbrooks * Store the Intel factory PPR in the environment. In some 406250115Sbrooks * cases it is the most unique ID on a board. 407250115Sbrooks */ 408250115Sbrooks if (cfi_intel_get_factory_pr(sc, &ppr) == 0) { 409250115Sbrooks if (snprintf(name, sizeof(name), "%s.factory_ppr", 410250115Sbrooks device_get_nameunit(dev)) < (sizeof(name) - 1) && 411250115Sbrooks snprintf(value, sizeof(value), "0x%016jx", ppr) < 412250115Sbrooks (sizeof(value) - 1)) 413250115Sbrooks (void) setenv(name, value); 414250115Sbrooks } 415250115Sbrooks#endif 416250115Sbrooks 417193936Simp device_add_child(dev, "cfid", -1); 418189606Ssam bus_generic_attach(dev); 419189606Ssam 420184251Smarcel return (0); 421184251Smarcel} 422184251Smarcel 423255207Sbrooksstatic void 424255207Sbrookscfi_add_sysctls(struct cfi_softc *sc) 425255207Sbrooks{ 426255207Sbrooks struct sysctl_ctx_list *ctx; 427255207Sbrooks struct sysctl_oid_list *children; 428255207Sbrooks 429255207Sbrooks ctx = device_get_sysctl_ctx(sc->sc_dev); 430255207Sbrooks children = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->sc_dev)); 431255207Sbrooks 432255207Sbrooks SYSCTL_ADD_UINT(ctx, children, OID_AUTO, 433255207Sbrooks "typical_erase_timout_count", 434255207Sbrooks CTLFLAG_RD, &sc->sc_tto_counts[CFI_TIMEOUT_ERASE], 435255207Sbrooks 0, "Number of times the typical erase timeout was exceeded"); 436255207Sbrooks SYSCTL_ADD_UINT(ctx, children, OID_AUTO, 437255207Sbrooks "max_erase_timout_count", 438255207Sbrooks CTLFLAG_RD, &sc->sc_mto_counts[CFI_TIMEOUT_ERASE], 0, 439255207Sbrooks "Number of times the maximum erase timeout was exceeded"); 440255207Sbrooks SYSCTL_ADD_UINT(ctx, children, OID_AUTO, 441255207Sbrooks "typical_write_timout_count", 442255207Sbrooks CTLFLAG_RD, &sc->sc_tto_counts[CFI_TIMEOUT_WRITE], 0, 443255207Sbrooks "Number of times the typical write timeout was exceeded"); 444255207Sbrooks SYSCTL_ADD_UINT(ctx, children, OID_AUTO, 445255207Sbrooks "max_write_timout_count", 446255207Sbrooks CTLFLAG_RD, &sc->sc_mto_counts[CFI_TIMEOUT_WRITE], 0, 447255207Sbrooks "Number of times the maximum write timeout was exceeded"); 448255207Sbrooks if (sc->sc_maxbuf > 0) { 449255207Sbrooks SYSCTL_ADD_UINT(ctx, children, OID_AUTO, 450255207Sbrooks "typical_bufwrite_timout_count", 451255207Sbrooks CTLFLAG_RD, &sc->sc_tto_counts[CFI_TIMEOUT_BUFWRITE], 0, 452255207Sbrooks "Number of times the typical buffered write timeout was " 453255207Sbrooks "exceeded"); 454255207Sbrooks SYSCTL_ADD_UINT(ctx, children, OID_AUTO, 455255207Sbrooks "max_bufwrite_timout_count", 456255207Sbrooks CTLFLAG_RD, &sc->sc_mto_counts[CFI_TIMEOUT_BUFWRITE], 0, 457255207Sbrooks "Number of times the maximum buffered write timeout was " 458255207Sbrooks "exceeded"); 459255207Sbrooks } 460255207Sbrooks} 461255207Sbrooks 462184251Smarcelint 463184251Smarcelcfi_detach(device_t dev) 464184251Smarcel{ 465184251Smarcel struct cfi_softc *sc; 466184251Smarcel 467184251Smarcel sc = device_get_softc(dev); 468184251Smarcel 469184251Smarcel destroy_dev(sc->sc_nod); 470184251Smarcel free(sc->sc_region, M_TEMP); 471184251Smarcel bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_rid, sc->sc_res); 472184251Smarcel return (0); 473184251Smarcel} 474184251Smarcel 475184251Smarcelstatic int 476255207Sbrookscfi_wait_ready(struct cfi_softc *sc, u_int ofs, sbintime_t start, 477255207Sbrooks enum cfi_wait_cmd cmd) 478184251Smarcel{ 479255207Sbrooks int done, error, tto_exceeded; 480188156Ssam uint32_t st0 = 0, st = 0; 481255207Sbrooks sbintime_t now; 482184251Smarcel 483184251Smarcel done = 0; 484184251Smarcel error = 0; 485255207Sbrooks tto_exceeded = 0; 486255207Sbrooks while (!done && !error) { 487255207Sbrooks /* 488255207Sbrooks * Save time before we start so we always do one check 489255207Sbrooks * after the timeout has expired. 490255207Sbrooks */ 491255207Sbrooks now = sbinuptime(); 492184251Smarcel 493184251Smarcel switch (sc->sc_cmdset) { 494184251Smarcel case CFI_VEND_INTEL_ECS: 495184251Smarcel case CFI_VEND_INTEL_SCS: 496188156Ssam st = cfi_read(sc, ofs); 497188156Ssam done = (st & CFI_INTEL_STATUS_WSMS); 498184251Smarcel if (done) { 499188156Ssam /* NB: bit 0 is reserved */ 500188156Ssam st &= ~(CFI_INTEL_XSTATUS_RSVD | 501188156Ssam CFI_INTEL_STATUS_WSMS | 502188156Ssam CFI_INTEL_STATUS_RSVD); 503188156Ssam if (st & CFI_INTEL_STATUS_DPS) 504184251Smarcel error = EPERM; 505188156Ssam else if (st & CFI_INTEL_STATUS_PSLBS) 506184251Smarcel error = EIO; 507188156Ssam else if (st & CFI_INTEL_STATUS_ECLBS) 508184251Smarcel error = ENXIO; 509188156Ssam else if (st) 510188156Ssam error = EACCES; 511184251Smarcel } 512184251Smarcel break; 513184251Smarcel case CFI_VEND_AMD_SCS: 514184251Smarcel case CFI_VEND_AMD_ECS: 515188156Ssam st0 = cfi_read(sc, ofs); 516188156Ssam st = cfi_read(sc, ofs); 517184251Smarcel done = ((st & 0x40) == (st0 & 0x40)) ? 1 : 0; 518184251Smarcel break; 519184251Smarcel } 520255207Sbrooks 521255207Sbrooks if (tto_exceeded || 522255207Sbrooks now > start + sc->sc_typical_timeouts[cmd]) { 523255207Sbrooks if (!tto_exceeded) { 524255207Sbrooks tto_exceeded = 1; 525255207Sbrooks sc->sc_tto_counts[cmd]++; 526255207Sbrooks#ifdef CFI_DEBUG_TIMEOUT 527255207Sbrooks device_printf(sc->sc_dev, 528255207Sbrooks "typical timeout exceeded (cmd %d)", cmd); 529255207Sbrooks#endif 530255207Sbrooks } 531255207Sbrooks if (now > start + sc->sc_max_timeouts[cmd]) { 532255207Sbrooks sc->sc_mto_counts[cmd]++; 533255207Sbrooks#ifdef CFI_DEBUG_TIMEOUT 534255207Sbrooks device_printf(sc->sc_dev, 535255207Sbrooks "max timeout exceeded (cmd %d)", cmd); 536255207Sbrooks#endif 537255207Sbrooks } 538255207Sbrooks } 539184251Smarcel } 540184251Smarcel if (!done && !error) 541184251Smarcel error = ETIMEDOUT; 542184251Smarcel if (error) 543188156Ssam printf("\nerror=%d (st 0x%x st0 0x%x)\n", error, st, st0); 544184251Smarcel return (error); 545184251Smarcel} 546184251Smarcel 547184251Smarcelint 548184251Smarcelcfi_write_block(struct cfi_softc *sc) 549184251Smarcel{ 550184251Smarcel union { 551184251Smarcel uint8_t *x8; 552184251Smarcel uint16_t *x16; 553184251Smarcel uint32_t *x32; 554255207Sbrooks } ptr, cpyprt; 555184251Smarcel register_t intr; 556255207Sbrooks int error, i, neederase = 0; 557255207Sbrooks uint32_t st; 558255207Sbrooks u_int wlen; 559255207Sbrooks sbintime_t start; 560184251Smarcel 561251118Sbrooks /* Intel flash must be unlocked before modification */ 562251118Sbrooks switch (sc->sc_cmdset) { 563251118Sbrooks case CFI_VEND_INTEL_ECS: 564251118Sbrooks case CFI_VEND_INTEL_SCS: 565251118Sbrooks cfi_write(sc, sc->sc_wrofs, CFI_INTEL_LBS); 566251118Sbrooks cfi_write(sc, sc->sc_wrofs, CFI_INTEL_UB); 567251118Sbrooks cfi_write(sc, sc->sc_wrofs, CFI_BCS_READ_ARRAY); 568251118Sbrooks break; 569251118Sbrooks } 570251118Sbrooks 571255207Sbrooks /* Check if an erase is required. */ 572255207Sbrooks for (i = 0; i < sc->sc_wrbufsz; i++) 573255207Sbrooks if ((sc->sc_wrbuf[i] & sc->sc_wrbufcpy[i]) != sc->sc_wrbuf[i]) { 574255207Sbrooks neederase = 1; 575255207Sbrooks break; 576255207Sbrooks } 577255207Sbrooks 578255207Sbrooks if (neederase) { 579255207Sbrooks intr = intr_disable(); 580255207Sbrooks start = sbinuptime(); 581255207Sbrooks /* Erase the block. */ 582255207Sbrooks switch (sc->sc_cmdset) { 583255207Sbrooks case CFI_VEND_INTEL_ECS: 584255207Sbrooks case CFI_VEND_INTEL_SCS: 585255207Sbrooks cfi_write(sc, sc->sc_wrofs, CFI_BCS_BLOCK_ERASE); 586255207Sbrooks cfi_write(sc, sc->sc_wrofs, CFI_BCS_CONFIRM); 587255207Sbrooks break; 588255207Sbrooks case CFI_VEND_AMD_SCS: 589255207Sbrooks case CFI_VEND_AMD_ECS: 590255207Sbrooks cfi_amd_write(sc, sc->sc_wrofs, AMD_ADDR_START, 591255207Sbrooks CFI_AMD_ERASE_SECTOR); 592255207Sbrooks cfi_amd_write(sc, sc->sc_wrofs, 0, CFI_AMD_BLOCK_ERASE); 593255207Sbrooks break; 594255207Sbrooks default: 595255207Sbrooks /* Better safe than sorry... */ 596255207Sbrooks intr_restore(intr); 597255207Sbrooks return (ENODEV); 598255207Sbrooks } 599255207Sbrooks intr_restore(intr); 600255207Sbrooks error = cfi_wait_ready(sc, sc->sc_wrofs, start, 601255207Sbrooks CFI_TIMEOUT_ERASE); 602255207Sbrooks if (error) 603255207Sbrooks goto out; 604255207Sbrooks } else 605255207Sbrooks error = 0; 606255207Sbrooks 607255207Sbrooks /* Write the block using a multibyte write if supported. */ 608255207Sbrooks ptr.x8 = sc->sc_wrbuf; 609255207Sbrooks cpyprt.x8 = sc->sc_wrbufcpy; 610255207Sbrooks if (sc->sc_maxbuf > sc->sc_width) { 611255207Sbrooks switch (sc->sc_cmdset) { 612255207Sbrooks case CFI_VEND_INTEL_ECS: 613255207Sbrooks case CFI_VEND_INTEL_SCS: 614255207Sbrooks for (i = 0; i < sc->sc_wrbufsz; i += wlen) { 615255207Sbrooks wlen = MIN(sc->sc_maxbuf, sc->sc_wrbufsz - i); 616255207Sbrooks 617255207Sbrooks intr = intr_disable(); 618255207Sbrooks 619255207Sbrooks start = sbinuptime(); 620255207Sbrooks do { 621255207Sbrooks cfi_write(sc, sc->sc_wrofs + i, 622255207Sbrooks CFI_BCS_BUF_PROG_SETUP); 623255207Sbrooks if (sbinuptime() > start + sc->sc_max_timeouts[CFI_TIMEOUT_BUFWRITE]) { 624255207Sbrooks error = ETIMEDOUT; 625255207Sbrooks goto out; 626255207Sbrooks } 627255207Sbrooks st = cfi_read(sc, sc->sc_wrofs + i); 628255207Sbrooks } while (! (st & CFI_INTEL_STATUS_WSMS)); 629255207Sbrooks 630255207Sbrooks cfi_write(sc, sc->sc_wrofs + i, 631255207Sbrooks (wlen / sc->sc_width) - 1); 632255207Sbrooks switch (sc->sc_width) { 633255207Sbrooks case 1: 634255207Sbrooks bus_space_write_region_1(sc->sc_tag, 635255207Sbrooks sc->sc_handle, sc->sc_wrofs + i, 636255207Sbrooks ptr.x8 + i, wlen); 637255207Sbrooks break; 638255207Sbrooks case 2: 639255207Sbrooks bus_space_write_region_2(sc->sc_tag, 640255207Sbrooks sc->sc_handle, sc->sc_wrofs + i, 641255207Sbrooks ptr.x16 + i / 2, wlen / 2); 642255207Sbrooks break; 643255207Sbrooks case 4: 644255207Sbrooks bus_space_write_region_4(sc->sc_tag, 645255207Sbrooks sc->sc_handle, sc->sc_wrofs + i, 646255207Sbrooks ptr.x32 + i / 4, wlen / 4); 647255207Sbrooks break; 648255207Sbrooks } 649255207Sbrooks 650255207Sbrooks cfi_write(sc, sc->sc_wrofs + i, 651255207Sbrooks CFI_BCS_CONFIRM); 652255207Sbrooks 653255207Sbrooks intr_restore(intr); 654255207Sbrooks 655255207Sbrooks error = cfi_wait_ready(sc, sc->sc_wrofs + i, 656255207Sbrooks start, CFI_TIMEOUT_BUFWRITE); 657255207Sbrooks if (error != 0) 658255207Sbrooks goto out; 659255207Sbrooks } 660255207Sbrooks goto out; 661255207Sbrooks default: 662255207Sbrooks /* Fall through to single word case */ 663255207Sbrooks break; 664255207Sbrooks } 665255207Sbrooks 666184251Smarcel } 667184251Smarcel 668255207Sbrooks /* Write the block one byte/word at a time. */ 669184251Smarcel for (i = 0; i < sc->sc_wrbufsz; i += sc->sc_width) { 670184251Smarcel 671255207Sbrooks /* Avoid writing unless we are actually changing bits */ 672255207Sbrooks if (!neederase) { 673255207Sbrooks switch (sc->sc_width) { 674255207Sbrooks case 1: 675255207Sbrooks if(*(ptr.x8 + i) == *(cpyprt.x8 + i)) 676255207Sbrooks continue; 677255207Sbrooks break; 678255207Sbrooks case 2: 679255207Sbrooks if(*(ptr.x16 + i / 2) == *(cpyprt.x16 + i / 2)) 680255207Sbrooks continue; 681255207Sbrooks break; 682255207Sbrooks case 4: 683255207Sbrooks if(*(ptr.x32 + i / 4) == *(cpyprt.x32 + i / 4)) 684255207Sbrooks continue; 685255207Sbrooks break; 686255207Sbrooks } 687255207Sbrooks } 688255207Sbrooks 689184251Smarcel /* 690184251Smarcel * Make sure the command to start a write and the 691184251Smarcel * actual write happens back-to-back without any 692184251Smarcel * excessive delays. 693184251Smarcel */ 694184251Smarcel intr = intr_disable(); 695184251Smarcel 696255207Sbrooks start = sbinuptime(); 697184251Smarcel switch (sc->sc_cmdset) { 698184251Smarcel case CFI_VEND_INTEL_ECS: 699184251Smarcel case CFI_VEND_INTEL_SCS: 700184251Smarcel cfi_write(sc, sc->sc_wrofs + i, CFI_BCS_PROGRAM); 701184251Smarcel break; 702184251Smarcel case CFI_VEND_AMD_SCS: 703184251Smarcel case CFI_VEND_AMD_ECS: 704184251Smarcel cfi_amd_write(sc, 0, AMD_ADDR_START, CFI_AMD_PROGRAM); 705184251Smarcel break; 706184251Smarcel } 707184251Smarcel switch (sc->sc_width) { 708184251Smarcel case 1: 709184251Smarcel bus_space_write_1(sc->sc_tag, sc->sc_handle, 710255207Sbrooks sc->sc_wrofs + i, *(ptr.x8 + i)); 711184251Smarcel break; 712184251Smarcel case 2: 713184251Smarcel bus_space_write_2(sc->sc_tag, sc->sc_handle, 714255207Sbrooks sc->sc_wrofs + i, *(ptr.x16 + i / 2)); 715184251Smarcel break; 716184251Smarcel case 4: 717184251Smarcel bus_space_write_4(sc->sc_tag, sc->sc_handle, 718255207Sbrooks sc->sc_wrofs + i, *(ptr.x32 + i / 4)); 719184251Smarcel break; 720184251Smarcel } 721255207Sbrooks 722184251Smarcel intr_restore(intr); 723184251Smarcel 724255207Sbrooks error = cfi_wait_ready(sc, sc->sc_wrofs, start, 725255207Sbrooks CFI_TIMEOUT_WRITE); 726184251Smarcel if (error) 727184251Smarcel goto out; 728184251Smarcel } 729184251Smarcel 730184251Smarcel /* error is 0. */ 731184251Smarcel 732184251Smarcel out: 733184251Smarcel cfi_write(sc, 0, CFI_BCS_READ_ARRAY); 734251118Sbrooks 735251118Sbrooks /* Relock Intel flash */ 736251118Sbrooks switch (sc->sc_cmdset) { 737251118Sbrooks case CFI_VEND_INTEL_ECS: 738251118Sbrooks case CFI_VEND_INTEL_SCS: 739251118Sbrooks cfi_write(sc, sc->sc_wrofs, CFI_INTEL_LBS); 740251118Sbrooks cfi_write(sc, sc->sc_wrofs, CFI_INTEL_LB); 741251118Sbrooks cfi_write(sc, sc->sc_wrofs, CFI_BCS_READ_ARRAY); 742251118Sbrooks break; 743251118Sbrooks } 744184251Smarcel return (error); 745184251Smarcel} 746188156Ssam 747188156Ssam#ifdef CFI_SUPPORT_STRATAFLASH 748188156Ssam/* 749188156Ssam * Intel StrataFlash Protection Register Support. 750188156Ssam * 751188156Ssam * The memory includes a 128-bit Protection Register that can be 752188156Ssam * used for security. There are two 64-bit segments; one is programmed 753188156Ssam * at the factory with a unique 64-bit number which is immutable. 754188156Ssam * The other segment is left blank for User (OEM) programming. 755188267Ssam * The User/OEM segment is One Time Programmable (OTP). It can also 756188332Ssam * be locked to prevent any further writes by setting bit 0 of the 757188267Ssam * Protection Lock Register (PLR). The PLR can written only once. 758188156Ssam */ 759188156Ssam 760188156Ssamstatic uint16_t 761188156Ssamcfi_get16(struct cfi_softc *sc, int off) 762188156Ssam{ 763188156Ssam uint16_t v = bus_space_read_2(sc->sc_tag, sc->sc_handle, off<<1); 764188156Ssam return v; 765188156Ssam} 766188156Ssam 767188268Ssam#ifdef CFI_ARMEDANDDANGEROUS 768188156Ssamstatic void 769188156Ssamcfi_put16(struct cfi_softc *sc, int off, uint16_t v) 770188156Ssam{ 771188156Ssam bus_space_write_2(sc->sc_tag, sc->sc_handle, off<<1, v); 772188156Ssam} 773188268Ssam#endif 774188156Ssam 775188156Ssam/* 776188156Ssam * Read the factory-defined 64-bit segment of the PR. 777188156Ssam */ 778188156Ssamint 779188156Ssamcfi_intel_get_factory_pr(struct cfi_softc *sc, uint64_t *id) 780188156Ssam{ 781188156Ssam if (sc->sc_cmdset != CFI_VEND_INTEL_ECS) 782188156Ssam return EOPNOTSUPP; 783188156Ssam KASSERT(sc->sc_width == 2, ("sc_width %d", sc->sc_width)); 784188156Ssam 785188156Ssam cfi_write(sc, 0, CFI_INTEL_READ_ID); 786188156Ssam *id = ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(0)))<<48 | 787188156Ssam ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(1)))<<32 | 788188156Ssam ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(2)))<<16 | 789188156Ssam ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(3))); 790188156Ssam cfi_write(sc, 0, CFI_BCS_READ_ARRAY); 791188156Ssam return 0; 792188156Ssam} 793188156Ssam 794188156Ssam/* 795188156Ssam * Read the User/OEM 64-bit segment of the PR. 796188156Ssam */ 797188156Ssamint 798188156Ssamcfi_intel_get_oem_pr(struct cfi_softc *sc, uint64_t *id) 799188156Ssam{ 800188156Ssam if (sc->sc_cmdset != CFI_VEND_INTEL_ECS) 801188156Ssam return EOPNOTSUPP; 802188156Ssam KASSERT(sc->sc_width == 2, ("sc_width %d", sc->sc_width)); 803188156Ssam 804188156Ssam cfi_write(sc, 0, CFI_INTEL_READ_ID); 805188156Ssam *id = ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(4)))<<48 | 806188156Ssam ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(5)))<<32 | 807188156Ssam ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(6)))<<16 | 808188156Ssam ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(7))); 809188156Ssam cfi_write(sc, 0, CFI_BCS_READ_ARRAY); 810188156Ssam return 0; 811188156Ssam} 812188156Ssam 813188156Ssam/* 814188156Ssam * Write the User/OEM 64-bit segment of the PR. 815188267Ssam * XXX should allow writing individual words/bytes 816188156Ssam */ 817188156Ssamint 818188156Ssamcfi_intel_set_oem_pr(struct cfi_softc *sc, uint64_t id) 819188156Ssam{ 820188267Ssam#ifdef CFI_ARMEDANDDANGEROUS 821188156Ssam register_t intr; 822188156Ssam int i, error; 823255207Sbrooks sbintime_t start; 824188267Ssam#endif 825188156Ssam 826188156Ssam if (sc->sc_cmdset != CFI_VEND_INTEL_ECS) 827188156Ssam return EOPNOTSUPP; 828188156Ssam KASSERT(sc->sc_width == 2, ("sc_width %d", sc->sc_width)); 829188156Ssam 830188267Ssam#ifdef CFI_ARMEDANDDANGEROUS 831188156Ssam for (i = 7; i >= 4; i--, id >>= 16) { 832188156Ssam intr = intr_disable(); 833255207Sbrooks start = sbinuptime(); 834188156Ssam cfi_write(sc, 0, CFI_INTEL_PP_SETUP); 835188156Ssam cfi_put16(sc, CFI_INTEL_PR(i), id&0xffff); 836188156Ssam intr_restore(intr); 837255207Sbrooks error = cfi_wait_ready(sc, CFI_BCS_READ_STATUS, start, 838255207Sbrooks CFI_TIMEOUT_WRITE); 839188156Ssam if (error) 840188156Ssam break; 841188156Ssam } 842188156Ssam cfi_write(sc, 0, CFI_BCS_READ_ARRAY); 843188156Ssam return error; 844188267Ssam#else 845188267Ssam device_printf(sc->sc_dev, "%s: OEM PR not set, " 846188267Ssam "CFI_ARMEDANDDANGEROUS not configured\n", __func__); 847188267Ssam return ENXIO; 848188267Ssam#endif 849188156Ssam} 850188156Ssam 851188156Ssam/* 852188156Ssam * Read the contents of the Protection Lock Register. 853188156Ssam */ 854188156Ssamint 855188156Ssamcfi_intel_get_plr(struct cfi_softc *sc, uint32_t *plr) 856188156Ssam{ 857188156Ssam if (sc->sc_cmdset != CFI_VEND_INTEL_ECS) 858188156Ssam return EOPNOTSUPP; 859188156Ssam KASSERT(sc->sc_width == 2, ("sc_width %d", sc->sc_width)); 860188156Ssam 861188156Ssam cfi_write(sc, 0, CFI_INTEL_READ_ID); 862188156Ssam *plr = cfi_get16(sc, CFI_INTEL_PLR); 863188156Ssam cfi_write(sc, 0, CFI_BCS_READ_ARRAY); 864188156Ssam return 0; 865188156Ssam} 866188156Ssam 867188156Ssam/* 868188156Ssam * Write the Protection Lock Register to lock down the 869188156Ssam * user-settable segment of the Protection Register. 870188156Ssam * NOTE: this operation is not reversible. 871188156Ssam */ 872188156Ssamint 873188156Ssamcfi_intel_set_plr(struct cfi_softc *sc) 874188156Ssam{ 875188156Ssam#ifdef CFI_ARMEDANDDANGEROUS 876188156Ssam register_t intr; 877188268Ssam int error; 878255207Sbrooks sbintime_t start; 879188156Ssam#endif 880188156Ssam if (sc->sc_cmdset != CFI_VEND_INTEL_ECS) 881188156Ssam return EOPNOTSUPP; 882188156Ssam KASSERT(sc->sc_width == 2, ("sc_width %d", sc->sc_width)); 883188156Ssam 884188156Ssam#ifdef CFI_ARMEDANDDANGEROUS 885188156Ssam /* worthy of console msg */ 886188156Ssam device_printf(sc->sc_dev, "set PLR\n"); 887188156Ssam intr = intr_disable(); 888255207Sbrooks binuptime(&start); 889188156Ssam cfi_write(sc, 0, CFI_INTEL_PP_SETUP); 890188156Ssam cfi_put16(sc, CFI_INTEL_PLR, 0xFFFD); 891188156Ssam intr_restore(intr); 892255207Sbrooks error = cfi_wait_ready(sc, CFI_BCS_READ_STATUS, start, 893255207Sbrooks CFI_TIMEOUT_WRITE); 894188156Ssam cfi_write(sc, 0, CFI_BCS_READ_ARRAY); 895188268Ssam return error; 896188156Ssam#else 897188156Ssam device_printf(sc->sc_dev, "%s: PLR not set, " 898188156Ssam "CFI_ARMEDANDDANGEROUS not configured\n", __func__); 899188268Ssam return ENXIO; 900188156Ssam#endif 901188156Ssam} 902188156Ssam#endif /* CFI_SUPPORT_STRATAFLASH */ 903