cfi_core.c revision 295284
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 295284 2016-02-04 22:39:27Z adrian $"); 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); 102295284Sadrian#ifdef CFI_HARDWAREBYTESWAP 103295284Sadrian val = sval; 104295284Sadrian#else 105233553Sjchandra val = le16toh(sval); 106295284Sadrian#endif 107233553Sjchandra break; 108233553Sjchandra case 4: 109233553Sjchandra val = bus_space_read_4(sc->sc_tag, sc->sc_handle, ofs); 110295284Sadrian#ifndef CFI_HARDWAREBYTESWAP 111233553Sjchandra val = le32toh(val); 112295284Sadrian#endif 113233553Sjchandra break; 114233553Sjchandra default: 115233553Sjchandra val = ~0; 116233553Sjchandra break; 117233553Sjchandra } 118233553Sjchandra return (val); 119233553Sjchandra} 120233553Sjchandra 121184251Smarcelstatic void 122184251Smarcelcfi_write(struct cfi_softc *sc, u_int ofs, u_int val) 123184251Smarcel{ 124184251Smarcel 125184251Smarcel ofs &= ~(sc->sc_width - 1); 126184251Smarcel switch (sc->sc_width) { 127184251Smarcel case 1: 128184251Smarcel bus_space_write_1(sc->sc_tag, sc->sc_handle, ofs, val); 129184251Smarcel break; 130184251Smarcel case 2: 131295284Sadrian#ifdef CFI_HARDWAREBYTESWAP 132295284Sadrian bus_space_write_2(sc->sc_tag, sc->sc_handle, ofs, val); 133295284Sadrian#else 134233553Sjchandra bus_space_write_2(sc->sc_tag, sc->sc_handle, ofs, htole16(val)); 135295284Sadrian 136295284Sadrian#endif 137184251Smarcel break; 138184251Smarcel case 4: 139295284Sadrian#ifdef CFI_HARDWAREBYTESWAP 140295284Sadrian bus_space_write_4(sc->sc_tag, sc->sc_handle, ofs, val); 141295284Sadrian#else 142233553Sjchandra bus_space_write_4(sc->sc_tag, sc->sc_handle, ofs, htole32(val)); 143295284Sadrian#endif 144184251Smarcel break; 145184251Smarcel } 146184251Smarcel} 147184251Smarcel 148184251Smarceluint8_t 149184251Smarcelcfi_read_qry(struct cfi_softc *sc, u_int ofs) 150184251Smarcel{ 151184251Smarcel uint8_t val; 152184251Smarcel 153184251Smarcel cfi_write(sc, CFI_QRY_CMD_ADDR * sc->sc_width, CFI_QRY_CMD_DATA); 154184251Smarcel val = cfi_read(sc, ofs * sc->sc_width); 155184251Smarcel cfi_write(sc, 0, CFI_BCS_READ_ARRAY); 156184251Smarcel return (val); 157184251Smarcel} 158184251Smarcel 159184251Smarcelstatic void 160184251Smarcelcfi_amd_write(struct cfi_softc *sc, u_int ofs, u_int addr, u_int data) 161184251Smarcel{ 162184251Smarcel 163184251Smarcel cfi_write(sc, ofs + AMD_ADDR_START, CFI_AMD_UNLOCK); 164184251Smarcel cfi_write(sc, ofs + AMD_ADDR_ACK, CFI_AMD_UNLOCK_ACK); 165184251Smarcel cfi_write(sc, ofs + addr, data); 166184251Smarcel} 167184251Smarcel 168184251Smarcelstatic char * 169184251Smarcelcfi_fmtsize(uint32_t sz) 170184251Smarcel{ 171184251Smarcel static char buf[8]; 172184251Smarcel static const char *sfx[] = { "", "K", "M", "G" }; 173184251Smarcel int sfxidx; 174184251Smarcel 175184251Smarcel sfxidx = 0; 176184251Smarcel while (sfxidx < 3 && sz > 1023) { 177184251Smarcel sz /= 1024; 178184251Smarcel sfxidx++; 179184251Smarcel } 180184251Smarcel 181184251Smarcel sprintf(buf, "%u%sB", sz, sfx[sfxidx]); 182184251Smarcel return (buf); 183184251Smarcel} 184184251Smarcel 185184251Smarcelint 186184251Smarcelcfi_probe(device_t dev) 187184251Smarcel{ 188184251Smarcel char desc[80]; 189184251Smarcel struct cfi_softc *sc; 190184251Smarcel char *vend_str; 191184251Smarcel int error; 192184251Smarcel uint16_t iface, vend; 193184251Smarcel 194184251Smarcel sc = device_get_softc(dev); 195184251Smarcel sc->sc_dev = dev; 196184251Smarcel 197184251Smarcel sc->sc_rid = 0; 198184251Smarcel sc->sc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->sc_rid, 199184251Smarcel RF_ACTIVE); 200184251Smarcel if (sc->sc_res == NULL) 201184251Smarcel return (ENXIO); 202184251Smarcel 203184251Smarcel sc->sc_tag = rman_get_bustag(sc->sc_res); 204184251Smarcel sc->sc_handle = rman_get_bushandle(sc->sc_res); 205184251Smarcel 206188087Ssam if (sc->sc_width == 0) { 207188087Ssam sc->sc_width = 1; 208188087Ssam while (sc->sc_width <= 4) { 209188087Ssam if (cfi_read_qry(sc, CFI_QRY_IDENT) == 'Q') 210188087Ssam break; 211188087Ssam sc->sc_width <<= 1; 212188087Ssam } 213188087Ssam } else if (cfi_read_qry(sc, CFI_QRY_IDENT) != 'Q') { 214188087Ssam error = ENXIO; 215188087Ssam goto out; 216184251Smarcel } 217184251Smarcel if (sc->sc_width > 4) { 218184251Smarcel error = ENXIO; 219184251Smarcel goto out; 220184251Smarcel } 221184251Smarcel 222184251Smarcel /* We got a Q. Check if we also have the R and the Y. */ 223184251Smarcel if (cfi_read_qry(sc, CFI_QRY_IDENT + 1) != 'R' || 224184251Smarcel cfi_read_qry(sc, CFI_QRY_IDENT + 2) != 'Y') { 225184251Smarcel error = ENXIO; 226184251Smarcel goto out; 227184251Smarcel } 228184251Smarcel 229184251Smarcel /* Get the vendor and command set. */ 230184251Smarcel vend = cfi_read_qry(sc, CFI_QRY_VEND) | 231184251Smarcel (cfi_read_qry(sc, CFI_QRY_VEND + 1) << 8); 232184251Smarcel 233184251Smarcel sc->sc_cmdset = vend; 234184251Smarcel 235184251Smarcel switch (vend) { 236184251Smarcel case CFI_VEND_AMD_ECS: 237184251Smarcel case CFI_VEND_AMD_SCS: 238184251Smarcel vend_str = "AMD/Fujitsu"; 239184251Smarcel break; 240184251Smarcel case CFI_VEND_INTEL_ECS: 241184251Smarcel vend_str = "Intel/Sharp"; 242184251Smarcel break; 243184251Smarcel case CFI_VEND_INTEL_SCS: 244184251Smarcel vend_str = "Intel"; 245184251Smarcel break; 246184251Smarcel case CFI_VEND_MITSUBISHI_ECS: 247184251Smarcel case CFI_VEND_MITSUBISHI_SCS: 248184251Smarcel vend_str = "Mitsubishi"; 249184251Smarcel break; 250184251Smarcel default: 251184251Smarcel vend_str = "Unknown vendor"; 252184251Smarcel break; 253184251Smarcel } 254184251Smarcel 255184251Smarcel /* Get the device size. */ 256184251Smarcel sc->sc_size = 1U << cfi_read_qry(sc, CFI_QRY_SIZE); 257184251Smarcel 258184251Smarcel /* Sanity-check the I/F */ 259184251Smarcel iface = cfi_read_qry(sc, CFI_QRY_IFACE) | 260184251Smarcel (cfi_read_qry(sc, CFI_QRY_IFACE + 1) << 8); 261184251Smarcel 262184251Smarcel /* 263184251Smarcel * Adding 1 to iface will give us a bit-wise "switch" 264184251Smarcel * that allows us to test for the interface width by 265184251Smarcel * testing a single bit. 266184251Smarcel */ 267184251Smarcel iface++; 268184251Smarcel 269184251Smarcel error = (iface & sc->sc_width) ? 0 : EINVAL; 270184251Smarcel if (error) 271184251Smarcel goto out; 272184251Smarcel 273184251Smarcel snprintf(desc, sizeof(desc), "%s - %s", vend_str, 274184251Smarcel cfi_fmtsize(sc->sc_size)); 275184251Smarcel device_set_desc_copy(dev, desc); 276184251Smarcel 277184251Smarcel out: 278184251Smarcel bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_rid, sc->sc_res); 279184251Smarcel return (error); 280184251Smarcel} 281184251Smarcel 282184251Smarcelint 283184251Smarcelcfi_attach(device_t dev) 284184251Smarcel{ 285184251Smarcel struct cfi_softc *sc; 286184251Smarcel u_int blksz, blocks; 287184251Smarcel u_int r, u; 288255207Sbrooks uint64_t mtoexp, ttoexp; 289250115Sbrooks#ifdef CFI_SUPPORT_STRATAFLASH 290250115Sbrooks uint64_t ppr; 291250115Sbrooks char name[KENV_MNAMELEN], value[32]; 292250115Sbrooks#endif 293184251Smarcel 294184251Smarcel sc = device_get_softc(dev); 295184251Smarcel sc->sc_dev = dev; 296184251Smarcel 297184251Smarcel sc->sc_rid = 0; 298184251Smarcel sc->sc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->sc_rid, 299256753Sbrooks#ifndef ATSE_CFI_HACK 300184251Smarcel RF_ACTIVE); 301256753Sbrooks#else 302256753Sbrooks RF_ACTIVE | RF_SHAREABLE); 303256753Sbrooks#endif 304184251Smarcel if (sc->sc_res == NULL) 305184251Smarcel return (ENXIO); 306184251Smarcel 307184251Smarcel sc->sc_tag = rman_get_bustag(sc->sc_res); 308184251Smarcel sc->sc_handle = rman_get_bushandle(sc->sc_res); 309184251Smarcel 310255207Sbrooks /* Get time-out values for erase, write, and buffer write. */ 311255207Sbrooks ttoexp = cfi_read_qry(sc, CFI_QRY_TTO_ERASE); 312255207Sbrooks mtoexp = cfi_read_qry(sc, CFI_QRY_MTO_ERASE); 313255207Sbrooks if (ttoexp == 0) { 314255207Sbrooks device_printf(dev, "erase timeout == 0, using 2^16ms\n"); 315255207Sbrooks ttoexp = 16; 316255207Sbrooks } 317255207Sbrooks if (ttoexp > 41) { 318255207Sbrooks device_printf(dev, "insane timeout: 2^%jdms\n", ttoexp); 319255207Sbrooks return (EINVAL); 320255207Sbrooks } 321255207Sbrooks if (mtoexp == 0) { 322255207Sbrooks device_printf(dev, "max erase timeout == 0, using 2^%jdms\n", 323255207Sbrooks ttoexp + 4); 324255207Sbrooks mtoexp = 4; 325255207Sbrooks } 326255207Sbrooks if (ttoexp + mtoexp > 41) { 327255207Sbrooks device_printf(dev, "insane max erase timeout: 2^%jd\n", 328255207Sbrooks ttoexp + mtoexp); 329255207Sbrooks return (EINVAL); 330255207Sbrooks } 331255207Sbrooks sc->sc_typical_timeouts[CFI_TIMEOUT_ERASE] = SBT_1MS * (1ULL << ttoexp); 332255207Sbrooks sc->sc_max_timeouts[CFI_TIMEOUT_ERASE] = 333255207Sbrooks sc->sc_typical_timeouts[CFI_TIMEOUT_ERASE] * (1ULL << mtoexp); 334184251Smarcel 335255207Sbrooks ttoexp = cfi_read_qry(sc, CFI_QRY_TTO_WRITE); 336255207Sbrooks mtoexp = cfi_read_qry(sc, CFI_QRY_MTO_WRITE); 337255207Sbrooks if (ttoexp == 0) { 338255207Sbrooks device_printf(dev, "write timeout == 0, using 2^18ns\n"); 339255207Sbrooks ttoexp = 18; 340255207Sbrooks } 341255207Sbrooks if (ttoexp > 51) { 342255207Sbrooks device_printf(dev, "insane write timeout: 2^%jdus\n", ttoexp); 343255207Sbrooks return (EINVAL); 344255207Sbrooks } 345255207Sbrooks if (mtoexp == 0) { 346255207Sbrooks device_printf(dev, "max write timeout == 0, using 2^%jdms\n", 347255207Sbrooks ttoexp + 4); 348255207Sbrooks mtoexp = 4; 349255207Sbrooks } 350255207Sbrooks if (ttoexp + mtoexp > 51) { 351255207Sbrooks device_printf(dev, "insane max write timeout: 2^%jdus\n", 352255207Sbrooks ttoexp + mtoexp); 353255207Sbrooks return (EINVAL); 354255207Sbrooks } 355255207Sbrooks sc->sc_typical_timeouts[CFI_TIMEOUT_WRITE] = SBT_1US * (1ULL << ttoexp); 356255207Sbrooks sc->sc_max_timeouts[CFI_TIMEOUT_WRITE] = 357255207Sbrooks sc->sc_typical_timeouts[CFI_TIMEOUT_WRITE] * (1ULL << mtoexp); 358255207Sbrooks 359255207Sbrooks ttoexp = cfi_read_qry(sc, CFI_QRY_TTO_BUFWRITE); 360255207Sbrooks mtoexp = cfi_read_qry(sc, CFI_QRY_MTO_BUFWRITE); 361255207Sbrooks /* Don't check for 0, it means not-supported. */ 362255207Sbrooks if (ttoexp > 51) { 363255207Sbrooks device_printf(dev, "insane write timeout: 2^%jdus\n", ttoexp); 364255207Sbrooks return (EINVAL); 365255207Sbrooks } 366255207Sbrooks if (ttoexp + mtoexp > 51) { 367255207Sbrooks device_printf(dev, "insane max write timeout: 2^%jdus\n", 368255207Sbrooks ttoexp + mtoexp); 369255207Sbrooks return (EINVAL); 370255207Sbrooks } 371255207Sbrooks sc->sc_typical_timeouts[CFI_TIMEOUT_BUFWRITE] = 372255207Sbrooks SBT_1US * (1ULL << cfi_read_qry(sc, CFI_QRY_TTO_BUFWRITE)); 373255207Sbrooks sc->sc_max_timeouts[CFI_TIMEOUT_BUFWRITE] = 374255207Sbrooks sc->sc_typical_timeouts[CFI_TIMEOUT_BUFWRITE] * 375255207Sbrooks (1ULL << cfi_read_qry(sc, CFI_QRY_MTO_BUFWRITE)); 376255207Sbrooks 377255207Sbrooks /* Get the maximum size of a multibyte program */ 378255207Sbrooks if (sc->sc_typical_timeouts[CFI_TIMEOUT_BUFWRITE] != 0) 379255207Sbrooks sc->sc_maxbuf = 1 << (cfi_read_qry(sc, CFI_QRY_MAXBUF) | 380255207Sbrooks cfi_read_qry(sc, CFI_QRY_MAXBUF) << 8); 381255207Sbrooks else 382255207Sbrooks sc->sc_maxbuf = 0; 383255207Sbrooks 384184251Smarcel /* Get erase regions. */ 385184251Smarcel sc->sc_regions = cfi_read_qry(sc, CFI_QRY_NREGIONS); 386184251Smarcel sc->sc_region = malloc(sc->sc_regions * sizeof(struct cfi_region), 387184251Smarcel M_TEMP, M_WAITOK | M_ZERO); 388184251Smarcel for (r = 0; r < sc->sc_regions; r++) { 389184251Smarcel blocks = cfi_read_qry(sc, CFI_QRY_REGION(r)) | 390184251Smarcel (cfi_read_qry(sc, CFI_QRY_REGION(r) + 1) << 8); 391184251Smarcel sc->sc_region[r].r_blocks = blocks + 1; 392184251Smarcel 393184251Smarcel blksz = cfi_read_qry(sc, CFI_QRY_REGION(r) + 2) | 394184251Smarcel (cfi_read_qry(sc, CFI_QRY_REGION(r) + 3) << 8); 395184251Smarcel sc->sc_region[r].r_blksz = (blksz == 0) ? 128 : 396184251Smarcel blksz * 256; 397184251Smarcel } 398184251Smarcel 399184251Smarcel /* Reset the device to a default state. */ 400184251Smarcel cfi_write(sc, 0, CFI_BCS_CLEAR_STATUS); 401184251Smarcel 402184251Smarcel if (bootverbose) { 403184251Smarcel device_printf(dev, "["); 404184251Smarcel for (r = 0; r < sc->sc_regions; r++) { 405184251Smarcel printf("%ux%s%s", sc->sc_region[r].r_blocks, 406184251Smarcel cfi_fmtsize(sc->sc_region[r].r_blksz), 407184251Smarcel (r == sc->sc_regions - 1) ? "]\n" : ","); 408184251Smarcel } 409184251Smarcel } 410184251Smarcel 411184251Smarcel u = device_get_unit(dev); 412184251Smarcel sc->sc_nod = make_dev(&cfi_cdevsw, u, UID_ROOT, GID_WHEEL, 0600, 413184251Smarcel "%s%u", cfi_driver_name, u); 414184251Smarcel sc->sc_nod->si_drv1 = sc; 415184251Smarcel 416255207Sbrooks cfi_add_sysctls(sc); 417255207Sbrooks 418250115Sbrooks#ifdef CFI_SUPPORT_STRATAFLASH 419250115Sbrooks /* 420250115Sbrooks * Store the Intel factory PPR in the environment. In some 421250115Sbrooks * cases it is the most unique ID on a board. 422250115Sbrooks */ 423250115Sbrooks if (cfi_intel_get_factory_pr(sc, &ppr) == 0) { 424250115Sbrooks if (snprintf(name, sizeof(name), "%s.factory_ppr", 425250115Sbrooks device_get_nameunit(dev)) < (sizeof(name) - 1) && 426250115Sbrooks snprintf(value, sizeof(value), "0x%016jx", ppr) < 427250115Sbrooks (sizeof(value) - 1)) 428273174Sdavide (void) kern_setenv(name, value); 429250115Sbrooks } 430250115Sbrooks#endif 431250115Sbrooks 432193936Simp device_add_child(dev, "cfid", -1); 433189606Ssam bus_generic_attach(dev); 434189606Ssam 435184251Smarcel return (0); 436184251Smarcel} 437184251Smarcel 438255207Sbrooksstatic void 439255207Sbrookscfi_add_sysctls(struct cfi_softc *sc) 440255207Sbrooks{ 441255207Sbrooks struct sysctl_ctx_list *ctx; 442255207Sbrooks struct sysctl_oid_list *children; 443255207Sbrooks 444255207Sbrooks ctx = device_get_sysctl_ctx(sc->sc_dev); 445255207Sbrooks children = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->sc_dev)); 446255207Sbrooks 447255207Sbrooks SYSCTL_ADD_UINT(ctx, children, OID_AUTO, 448255207Sbrooks "typical_erase_timout_count", 449255207Sbrooks CTLFLAG_RD, &sc->sc_tto_counts[CFI_TIMEOUT_ERASE], 450255207Sbrooks 0, "Number of times the typical erase timeout was exceeded"); 451255207Sbrooks SYSCTL_ADD_UINT(ctx, children, OID_AUTO, 452255207Sbrooks "max_erase_timout_count", 453255207Sbrooks CTLFLAG_RD, &sc->sc_mto_counts[CFI_TIMEOUT_ERASE], 0, 454255207Sbrooks "Number of times the maximum erase timeout was exceeded"); 455255207Sbrooks SYSCTL_ADD_UINT(ctx, children, OID_AUTO, 456255207Sbrooks "typical_write_timout_count", 457255207Sbrooks CTLFLAG_RD, &sc->sc_tto_counts[CFI_TIMEOUT_WRITE], 0, 458255207Sbrooks "Number of times the typical write timeout was exceeded"); 459255207Sbrooks SYSCTL_ADD_UINT(ctx, children, OID_AUTO, 460255207Sbrooks "max_write_timout_count", 461255207Sbrooks CTLFLAG_RD, &sc->sc_mto_counts[CFI_TIMEOUT_WRITE], 0, 462255207Sbrooks "Number of times the maximum write timeout was exceeded"); 463255207Sbrooks if (sc->sc_maxbuf > 0) { 464255207Sbrooks SYSCTL_ADD_UINT(ctx, children, OID_AUTO, 465255207Sbrooks "typical_bufwrite_timout_count", 466255207Sbrooks CTLFLAG_RD, &sc->sc_tto_counts[CFI_TIMEOUT_BUFWRITE], 0, 467255207Sbrooks "Number of times the typical buffered write timeout was " 468255207Sbrooks "exceeded"); 469255207Sbrooks SYSCTL_ADD_UINT(ctx, children, OID_AUTO, 470255207Sbrooks "max_bufwrite_timout_count", 471255207Sbrooks CTLFLAG_RD, &sc->sc_mto_counts[CFI_TIMEOUT_BUFWRITE], 0, 472255207Sbrooks "Number of times the maximum buffered write timeout was " 473255207Sbrooks "exceeded"); 474255207Sbrooks } 475255207Sbrooks} 476255207Sbrooks 477184251Smarcelint 478184251Smarcelcfi_detach(device_t dev) 479184251Smarcel{ 480184251Smarcel struct cfi_softc *sc; 481184251Smarcel 482184251Smarcel sc = device_get_softc(dev); 483184251Smarcel 484184251Smarcel destroy_dev(sc->sc_nod); 485184251Smarcel free(sc->sc_region, M_TEMP); 486184251Smarcel bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_rid, sc->sc_res); 487184251Smarcel return (0); 488184251Smarcel} 489184251Smarcel 490184251Smarcelstatic int 491255207Sbrookscfi_wait_ready(struct cfi_softc *sc, u_int ofs, sbintime_t start, 492255207Sbrooks enum cfi_wait_cmd cmd) 493184251Smarcel{ 494255207Sbrooks int done, error, tto_exceeded; 495188156Ssam uint32_t st0 = 0, st = 0; 496255207Sbrooks sbintime_t now; 497184251Smarcel 498184251Smarcel done = 0; 499184251Smarcel error = 0; 500255207Sbrooks tto_exceeded = 0; 501255207Sbrooks while (!done && !error) { 502255207Sbrooks /* 503255207Sbrooks * Save time before we start so we always do one check 504255207Sbrooks * after the timeout has expired. 505255207Sbrooks */ 506255207Sbrooks now = sbinuptime(); 507184251Smarcel 508184251Smarcel switch (sc->sc_cmdset) { 509184251Smarcel case CFI_VEND_INTEL_ECS: 510184251Smarcel case CFI_VEND_INTEL_SCS: 511188156Ssam st = cfi_read(sc, ofs); 512188156Ssam done = (st & CFI_INTEL_STATUS_WSMS); 513184251Smarcel if (done) { 514188156Ssam /* NB: bit 0 is reserved */ 515188156Ssam st &= ~(CFI_INTEL_XSTATUS_RSVD | 516188156Ssam CFI_INTEL_STATUS_WSMS | 517188156Ssam CFI_INTEL_STATUS_RSVD); 518188156Ssam if (st & CFI_INTEL_STATUS_DPS) 519184251Smarcel error = EPERM; 520188156Ssam else if (st & CFI_INTEL_STATUS_PSLBS) 521184251Smarcel error = EIO; 522188156Ssam else if (st & CFI_INTEL_STATUS_ECLBS) 523184251Smarcel error = ENXIO; 524188156Ssam else if (st) 525188156Ssam error = EACCES; 526184251Smarcel } 527184251Smarcel break; 528184251Smarcel case CFI_VEND_AMD_SCS: 529184251Smarcel case CFI_VEND_AMD_ECS: 530188156Ssam st0 = cfi_read(sc, ofs); 531188156Ssam st = cfi_read(sc, ofs); 532184251Smarcel done = ((st & 0x40) == (st0 & 0x40)) ? 1 : 0; 533184251Smarcel break; 534184251Smarcel } 535255207Sbrooks 536255207Sbrooks if (tto_exceeded || 537255207Sbrooks now > start + sc->sc_typical_timeouts[cmd]) { 538255207Sbrooks if (!tto_exceeded) { 539255207Sbrooks tto_exceeded = 1; 540255207Sbrooks sc->sc_tto_counts[cmd]++; 541255207Sbrooks#ifdef CFI_DEBUG_TIMEOUT 542255207Sbrooks device_printf(sc->sc_dev, 543255207Sbrooks "typical timeout exceeded (cmd %d)", cmd); 544255207Sbrooks#endif 545255207Sbrooks } 546255207Sbrooks if (now > start + sc->sc_max_timeouts[cmd]) { 547255207Sbrooks sc->sc_mto_counts[cmd]++; 548255207Sbrooks#ifdef CFI_DEBUG_TIMEOUT 549255207Sbrooks device_printf(sc->sc_dev, 550255207Sbrooks "max timeout exceeded (cmd %d)", cmd); 551255207Sbrooks#endif 552255207Sbrooks } 553255207Sbrooks } 554184251Smarcel } 555184251Smarcel if (!done && !error) 556184251Smarcel error = ETIMEDOUT; 557184251Smarcel if (error) 558188156Ssam printf("\nerror=%d (st 0x%x st0 0x%x)\n", error, st, st0); 559184251Smarcel return (error); 560184251Smarcel} 561184251Smarcel 562184251Smarcelint 563184251Smarcelcfi_write_block(struct cfi_softc *sc) 564184251Smarcel{ 565184251Smarcel union { 566184251Smarcel uint8_t *x8; 567184251Smarcel uint16_t *x16; 568184251Smarcel uint32_t *x32; 569255207Sbrooks } ptr, cpyprt; 570184251Smarcel register_t intr; 571255207Sbrooks int error, i, neederase = 0; 572255207Sbrooks uint32_t st; 573255207Sbrooks u_int wlen; 574255207Sbrooks sbintime_t start; 575184251Smarcel 576251118Sbrooks /* Intel flash must be unlocked before modification */ 577251118Sbrooks switch (sc->sc_cmdset) { 578251118Sbrooks case CFI_VEND_INTEL_ECS: 579251118Sbrooks case CFI_VEND_INTEL_SCS: 580251118Sbrooks cfi_write(sc, sc->sc_wrofs, CFI_INTEL_LBS); 581251118Sbrooks cfi_write(sc, sc->sc_wrofs, CFI_INTEL_UB); 582251118Sbrooks cfi_write(sc, sc->sc_wrofs, CFI_BCS_READ_ARRAY); 583251118Sbrooks break; 584251118Sbrooks } 585251118Sbrooks 586255207Sbrooks /* Check if an erase is required. */ 587255207Sbrooks for (i = 0; i < sc->sc_wrbufsz; i++) 588255207Sbrooks if ((sc->sc_wrbuf[i] & sc->sc_wrbufcpy[i]) != sc->sc_wrbuf[i]) { 589255207Sbrooks neederase = 1; 590255207Sbrooks break; 591255207Sbrooks } 592255207Sbrooks 593255207Sbrooks if (neederase) { 594255207Sbrooks intr = intr_disable(); 595255207Sbrooks start = sbinuptime(); 596255207Sbrooks /* Erase the block. */ 597255207Sbrooks switch (sc->sc_cmdset) { 598255207Sbrooks case CFI_VEND_INTEL_ECS: 599255207Sbrooks case CFI_VEND_INTEL_SCS: 600255207Sbrooks cfi_write(sc, sc->sc_wrofs, CFI_BCS_BLOCK_ERASE); 601255207Sbrooks cfi_write(sc, sc->sc_wrofs, CFI_BCS_CONFIRM); 602255207Sbrooks break; 603255207Sbrooks case CFI_VEND_AMD_SCS: 604255207Sbrooks case CFI_VEND_AMD_ECS: 605255207Sbrooks cfi_amd_write(sc, sc->sc_wrofs, AMD_ADDR_START, 606255207Sbrooks CFI_AMD_ERASE_SECTOR); 607255207Sbrooks cfi_amd_write(sc, sc->sc_wrofs, 0, CFI_AMD_BLOCK_ERASE); 608255207Sbrooks break; 609255207Sbrooks default: 610255207Sbrooks /* Better safe than sorry... */ 611255207Sbrooks intr_restore(intr); 612255207Sbrooks return (ENODEV); 613255207Sbrooks } 614255207Sbrooks intr_restore(intr); 615255207Sbrooks error = cfi_wait_ready(sc, sc->sc_wrofs, start, 616255207Sbrooks CFI_TIMEOUT_ERASE); 617255207Sbrooks if (error) 618255207Sbrooks goto out; 619255207Sbrooks } else 620255207Sbrooks error = 0; 621255207Sbrooks 622255207Sbrooks /* Write the block using a multibyte write if supported. */ 623255207Sbrooks ptr.x8 = sc->sc_wrbuf; 624255207Sbrooks cpyprt.x8 = sc->sc_wrbufcpy; 625255207Sbrooks if (sc->sc_maxbuf > sc->sc_width) { 626255207Sbrooks switch (sc->sc_cmdset) { 627255207Sbrooks case CFI_VEND_INTEL_ECS: 628255207Sbrooks case CFI_VEND_INTEL_SCS: 629255207Sbrooks for (i = 0; i < sc->sc_wrbufsz; i += wlen) { 630255207Sbrooks wlen = MIN(sc->sc_maxbuf, sc->sc_wrbufsz - i); 631255207Sbrooks 632255207Sbrooks intr = intr_disable(); 633255207Sbrooks 634255207Sbrooks start = sbinuptime(); 635255207Sbrooks do { 636255207Sbrooks cfi_write(sc, sc->sc_wrofs + i, 637255207Sbrooks CFI_BCS_BUF_PROG_SETUP); 638255207Sbrooks if (sbinuptime() > start + sc->sc_max_timeouts[CFI_TIMEOUT_BUFWRITE]) { 639255207Sbrooks error = ETIMEDOUT; 640255207Sbrooks goto out; 641255207Sbrooks } 642255207Sbrooks st = cfi_read(sc, sc->sc_wrofs + i); 643255207Sbrooks } while (! (st & CFI_INTEL_STATUS_WSMS)); 644255207Sbrooks 645255207Sbrooks cfi_write(sc, sc->sc_wrofs + i, 646255207Sbrooks (wlen / sc->sc_width) - 1); 647255207Sbrooks switch (sc->sc_width) { 648255207Sbrooks case 1: 649255207Sbrooks bus_space_write_region_1(sc->sc_tag, 650255207Sbrooks sc->sc_handle, sc->sc_wrofs + i, 651255207Sbrooks ptr.x8 + i, wlen); 652255207Sbrooks break; 653255207Sbrooks case 2: 654255207Sbrooks bus_space_write_region_2(sc->sc_tag, 655255207Sbrooks sc->sc_handle, sc->sc_wrofs + i, 656255207Sbrooks ptr.x16 + i / 2, wlen / 2); 657255207Sbrooks break; 658255207Sbrooks case 4: 659255207Sbrooks bus_space_write_region_4(sc->sc_tag, 660255207Sbrooks sc->sc_handle, sc->sc_wrofs + i, 661255207Sbrooks ptr.x32 + i / 4, wlen / 4); 662255207Sbrooks break; 663255207Sbrooks } 664255207Sbrooks 665255207Sbrooks cfi_write(sc, sc->sc_wrofs + i, 666255207Sbrooks CFI_BCS_CONFIRM); 667255207Sbrooks 668255207Sbrooks intr_restore(intr); 669255207Sbrooks 670255207Sbrooks error = cfi_wait_ready(sc, sc->sc_wrofs + i, 671255207Sbrooks start, CFI_TIMEOUT_BUFWRITE); 672255207Sbrooks if (error != 0) 673255207Sbrooks goto out; 674255207Sbrooks } 675255207Sbrooks goto out; 676255207Sbrooks default: 677255207Sbrooks /* Fall through to single word case */ 678255207Sbrooks break; 679255207Sbrooks } 680255207Sbrooks 681184251Smarcel } 682184251Smarcel 683255207Sbrooks /* Write the block one byte/word at a time. */ 684184251Smarcel for (i = 0; i < sc->sc_wrbufsz; i += sc->sc_width) { 685184251Smarcel 686255207Sbrooks /* Avoid writing unless we are actually changing bits */ 687255207Sbrooks if (!neederase) { 688255207Sbrooks switch (sc->sc_width) { 689255207Sbrooks case 1: 690255207Sbrooks if(*(ptr.x8 + i) == *(cpyprt.x8 + i)) 691255207Sbrooks continue; 692255207Sbrooks break; 693255207Sbrooks case 2: 694255207Sbrooks if(*(ptr.x16 + i / 2) == *(cpyprt.x16 + i / 2)) 695255207Sbrooks continue; 696255207Sbrooks break; 697255207Sbrooks case 4: 698255207Sbrooks if(*(ptr.x32 + i / 4) == *(cpyprt.x32 + i / 4)) 699255207Sbrooks continue; 700255207Sbrooks break; 701255207Sbrooks } 702255207Sbrooks } 703255207Sbrooks 704184251Smarcel /* 705184251Smarcel * Make sure the command to start a write and the 706184251Smarcel * actual write happens back-to-back without any 707184251Smarcel * excessive delays. 708184251Smarcel */ 709184251Smarcel intr = intr_disable(); 710184251Smarcel 711255207Sbrooks start = sbinuptime(); 712184251Smarcel switch (sc->sc_cmdset) { 713184251Smarcel case CFI_VEND_INTEL_ECS: 714184251Smarcel case CFI_VEND_INTEL_SCS: 715184251Smarcel cfi_write(sc, sc->sc_wrofs + i, CFI_BCS_PROGRAM); 716184251Smarcel break; 717184251Smarcel case CFI_VEND_AMD_SCS: 718184251Smarcel case CFI_VEND_AMD_ECS: 719184251Smarcel cfi_amd_write(sc, 0, AMD_ADDR_START, CFI_AMD_PROGRAM); 720184251Smarcel break; 721184251Smarcel } 722184251Smarcel switch (sc->sc_width) { 723184251Smarcel case 1: 724184251Smarcel bus_space_write_1(sc->sc_tag, sc->sc_handle, 725255207Sbrooks sc->sc_wrofs + i, *(ptr.x8 + i)); 726184251Smarcel break; 727184251Smarcel case 2: 728184251Smarcel bus_space_write_2(sc->sc_tag, sc->sc_handle, 729255207Sbrooks sc->sc_wrofs + i, *(ptr.x16 + i / 2)); 730184251Smarcel break; 731184251Smarcel case 4: 732184251Smarcel bus_space_write_4(sc->sc_tag, sc->sc_handle, 733255207Sbrooks sc->sc_wrofs + i, *(ptr.x32 + i / 4)); 734184251Smarcel break; 735184251Smarcel } 736255207Sbrooks 737184251Smarcel intr_restore(intr); 738184251Smarcel 739255207Sbrooks error = cfi_wait_ready(sc, sc->sc_wrofs, start, 740255207Sbrooks CFI_TIMEOUT_WRITE); 741184251Smarcel if (error) 742184251Smarcel goto out; 743184251Smarcel } 744184251Smarcel 745184251Smarcel /* error is 0. */ 746184251Smarcel 747184251Smarcel out: 748184251Smarcel cfi_write(sc, 0, CFI_BCS_READ_ARRAY); 749251118Sbrooks 750251118Sbrooks /* Relock Intel flash */ 751251118Sbrooks switch (sc->sc_cmdset) { 752251118Sbrooks case CFI_VEND_INTEL_ECS: 753251118Sbrooks case CFI_VEND_INTEL_SCS: 754251118Sbrooks cfi_write(sc, sc->sc_wrofs, CFI_INTEL_LBS); 755251118Sbrooks cfi_write(sc, sc->sc_wrofs, CFI_INTEL_LB); 756251118Sbrooks cfi_write(sc, sc->sc_wrofs, CFI_BCS_READ_ARRAY); 757251118Sbrooks break; 758251118Sbrooks } 759184251Smarcel return (error); 760184251Smarcel} 761188156Ssam 762188156Ssam#ifdef CFI_SUPPORT_STRATAFLASH 763188156Ssam/* 764188156Ssam * Intel StrataFlash Protection Register Support. 765188156Ssam * 766188156Ssam * The memory includes a 128-bit Protection Register that can be 767188156Ssam * used for security. There are two 64-bit segments; one is programmed 768188156Ssam * at the factory with a unique 64-bit number which is immutable. 769188156Ssam * The other segment is left blank for User (OEM) programming. 770188267Ssam * The User/OEM segment is One Time Programmable (OTP). It can also 771188332Ssam * be locked to prevent any further writes by setting bit 0 of the 772188267Ssam * Protection Lock Register (PLR). The PLR can written only once. 773188156Ssam */ 774188156Ssam 775188156Ssamstatic uint16_t 776188156Ssamcfi_get16(struct cfi_softc *sc, int off) 777188156Ssam{ 778188156Ssam uint16_t v = bus_space_read_2(sc->sc_tag, sc->sc_handle, off<<1); 779188156Ssam return v; 780188156Ssam} 781188156Ssam 782188268Ssam#ifdef CFI_ARMEDANDDANGEROUS 783188156Ssamstatic void 784188156Ssamcfi_put16(struct cfi_softc *sc, int off, uint16_t v) 785188156Ssam{ 786188156Ssam bus_space_write_2(sc->sc_tag, sc->sc_handle, off<<1, v); 787188156Ssam} 788188268Ssam#endif 789188156Ssam 790188156Ssam/* 791188156Ssam * Read the factory-defined 64-bit segment of the PR. 792188156Ssam */ 793188156Ssamint 794188156Ssamcfi_intel_get_factory_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(0)))<<48 | 802188156Ssam ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(1)))<<32 | 803188156Ssam ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(2)))<<16 | 804188156Ssam ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(3))); 805188156Ssam cfi_write(sc, 0, CFI_BCS_READ_ARRAY); 806188156Ssam return 0; 807188156Ssam} 808188156Ssam 809188156Ssam/* 810188156Ssam * Read the User/OEM 64-bit segment of the PR. 811188156Ssam */ 812188156Ssamint 813188156Ssamcfi_intel_get_oem_pr(struct cfi_softc *sc, uint64_t *id) 814188156Ssam{ 815188156Ssam if (sc->sc_cmdset != CFI_VEND_INTEL_ECS) 816188156Ssam return EOPNOTSUPP; 817188156Ssam KASSERT(sc->sc_width == 2, ("sc_width %d", sc->sc_width)); 818188156Ssam 819188156Ssam cfi_write(sc, 0, CFI_INTEL_READ_ID); 820188156Ssam *id = ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(4)))<<48 | 821188156Ssam ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(5)))<<32 | 822188156Ssam ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(6)))<<16 | 823188156Ssam ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(7))); 824188156Ssam cfi_write(sc, 0, CFI_BCS_READ_ARRAY); 825188156Ssam return 0; 826188156Ssam} 827188156Ssam 828188156Ssam/* 829188156Ssam * Write the User/OEM 64-bit segment of the PR. 830188267Ssam * XXX should allow writing individual words/bytes 831188156Ssam */ 832188156Ssamint 833188156Ssamcfi_intel_set_oem_pr(struct cfi_softc *sc, uint64_t id) 834188156Ssam{ 835188267Ssam#ifdef CFI_ARMEDANDDANGEROUS 836188156Ssam register_t intr; 837188156Ssam int i, error; 838255207Sbrooks sbintime_t start; 839188267Ssam#endif 840188156Ssam 841188156Ssam if (sc->sc_cmdset != CFI_VEND_INTEL_ECS) 842188156Ssam return EOPNOTSUPP; 843188156Ssam KASSERT(sc->sc_width == 2, ("sc_width %d", sc->sc_width)); 844188156Ssam 845188267Ssam#ifdef CFI_ARMEDANDDANGEROUS 846188156Ssam for (i = 7; i >= 4; i--, id >>= 16) { 847188156Ssam intr = intr_disable(); 848255207Sbrooks start = sbinuptime(); 849188156Ssam cfi_write(sc, 0, CFI_INTEL_PP_SETUP); 850188156Ssam cfi_put16(sc, CFI_INTEL_PR(i), id&0xffff); 851188156Ssam intr_restore(intr); 852255207Sbrooks error = cfi_wait_ready(sc, CFI_BCS_READ_STATUS, start, 853255207Sbrooks CFI_TIMEOUT_WRITE); 854188156Ssam if (error) 855188156Ssam break; 856188156Ssam } 857188156Ssam cfi_write(sc, 0, CFI_BCS_READ_ARRAY); 858188156Ssam return error; 859188267Ssam#else 860188267Ssam device_printf(sc->sc_dev, "%s: OEM PR not set, " 861188267Ssam "CFI_ARMEDANDDANGEROUS not configured\n", __func__); 862188267Ssam return ENXIO; 863188267Ssam#endif 864188156Ssam} 865188156Ssam 866188156Ssam/* 867188156Ssam * Read the contents of the Protection Lock Register. 868188156Ssam */ 869188156Ssamint 870188156Ssamcfi_intel_get_plr(struct cfi_softc *sc, uint32_t *plr) 871188156Ssam{ 872188156Ssam if (sc->sc_cmdset != CFI_VEND_INTEL_ECS) 873188156Ssam return EOPNOTSUPP; 874188156Ssam KASSERT(sc->sc_width == 2, ("sc_width %d", sc->sc_width)); 875188156Ssam 876188156Ssam cfi_write(sc, 0, CFI_INTEL_READ_ID); 877188156Ssam *plr = cfi_get16(sc, CFI_INTEL_PLR); 878188156Ssam cfi_write(sc, 0, CFI_BCS_READ_ARRAY); 879188156Ssam return 0; 880188156Ssam} 881188156Ssam 882188156Ssam/* 883188156Ssam * Write the Protection Lock Register to lock down the 884188156Ssam * user-settable segment of the Protection Register. 885188156Ssam * NOTE: this operation is not reversible. 886188156Ssam */ 887188156Ssamint 888188156Ssamcfi_intel_set_plr(struct cfi_softc *sc) 889188156Ssam{ 890188156Ssam#ifdef CFI_ARMEDANDDANGEROUS 891188156Ssam register_t intr; 892188268Ssam int error; 893255207Sbrooks sbintime_t start; 894188156Ssam#endif 895188156Ssam if (sc->sc_cmdset != CFI_VEND_INTEL_ECS) 896188156Ssam return EOPNOTSUPP; 897188156Ssam KASSERT(sc->sc_width == 2, ("sc_width %d", sc->sc_width)); 898188156Ssam 899188156Ssam#ifdef CFI_ARMEDANDDANGEROUS 900188156Ssam /* worthy of console msg */ 901188156Ssam device_printf(sc->sc_dev, "set PLR\n"); 902188156Ssam intr = intr_disable(); 903255207Sbrooks binuptime(&start); 904188156Ssam cfi_write(sc, 0, CFI_INTEL_PP_SETUP); 905188156Ssam cfi_put16(sc, CFI_INTEL_PLR, 0xFFFD); 906188156Ssam intr_restore(intr); 907255207Sbrooks error = cfi_wait_ready(sc, CFI_BCS_READ_STATUS, start, 908255207Sbrooks CFI_TIMEOUT_WRITE); 909188156Ssam cfi_write(sc, 0, CFI_BCS_READ_ARRAY); 910188268Ssam return error; 911188156Ssam#else 912188156Ssam device_printf(sc->sc_dev, "%s: PLR not set, " 913188156Ssam "CFI_ARMEDANDDANGEROUS not configured\n", __func__); 914188268Ssam return ENXIO; 915188156Ssam#endif 916188156Ssam} 917188156Ssam#endif /* CFI_SUPPORT_STRATAFLASH */ 918