cfi_core.c revision 251118
1184251Smarcel/*- 2184251Smarcel * Copyright (c) 2007, Juniper Networks, Inc. 3184251Smarcel * All rights reserved. 4184251Smarcel * 5184251Smarcel * Redistribution and use in source and binary forms, with or without 6184251Smarcel * modification, are permitted provided that the following conditions 7184251Smarcel * are met: 8184251Smarcel * 1. Redistributions of source code must retain the above copyright 9184251Smarcel * notice, this list of conditions and the following disclaimer. 10184251Smarcel * 2. Redistributions in binary form must reproduce the above copyright 11184251Smarcel * notice, this list of conditions and the following disclaimer in the 12184251Smarcel * documentation and/or other materials provided with the distribution. 13184251Smarcel * 3. Neither the name of the author nor the names of any co-contributors 14184251Smarcel * may be used to endorse or promote products derived from this software 15184251Smarcel * without specific prior written permission. 16184251Smarcel * 17184251Smarcel * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18184251Smarcel * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19184251Smarcel * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20184251Smarcel * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21184251Smarcel * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 22184251Smarcel * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23184251Smarcel * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 24184251Smarcel * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25184251Smarcel * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26184251Smarcel * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27184251Smarcel * SUCH DAMAGE. 28184251Smarcel */ 29184251Smarcel 30184251Smarcel#include <sys/cdefs.h> 31184251Smarcel__FBSDID("$FreeBSD: head/sys/dev/cfi/cfi_core.c 251118 2013-05-30 01:22:50Z brooks $"); 32184251Smarcel 33188156Ssam#include "opt_cfi.h" 34188156Ssam 35184251Smarcel#include <sys/param.h> 36184251Smarcel#include <sys/systm.h> 37184251Smarcel#include <sys/bus.h> 38184251Smarcel#include <sys/conf.h> 39233553Sjchandra#include <sys/endian.h> 40250115Sbrooks#include <sys/kenv.h> 41184251Smarcel#include <sys/kernel.h> 42184251Smarcel#include <sys/malloc.h> 43184251Smarcel#include <sys/module.h> 44184251Smarcel#include <sys/rman.h> 45184251Smarcel#include <sys/sysctl.h> 46184251Smarcel 47184251Smarcel#include <machine/bus.h> 48184251Smarcel 49184251Smarcel#include <dev/cfi/cfi_reg.h> 50184251Smarcel#include <dev/cfi/cfi_var.h> 51184251Smarcel 52184251Smarcelextern struct cdevsw cfi_cdevsw; 53184251Smarcel 54184251Smarcelchar cfi_driver_name[] = "cfi"; 55184251Smarceldevclass_t cfi_devclass; 56189606Ssamdevclass_t cfi_diskclass; 57184251Smarcel 58184251Smarceluint32_t 59233553Sjchandracfi_read_raw(struct cfi_softc *sc, u_int ofs) 60184251Smarcel{ 61184251Smarcel uint32_t val; 62184251Smarcel 63184251Smarcel ofs &= ~(sc->sc_width - 1); 64184251Smarcel switch (sc->sc_width) { 65184251Smarcel case 1: 66184251Smarcel val = bus_space_read_1(sc->sc_tag, sc->sc_handle, ofs); 67184251Smarcel break; 68184251Smarcel case 2: 69184251Smarcel val = bus_space_read_2(sc->sc_tag, sc->sc_handle, ofs); 70184251Smarcel break; 71184251Smarcel case 4: 72184251Smarcel val = bus_space_read_4(sc->sc_tag, sc->sc_handle, ofs); 73184251Smarcel break; 74184251Smarcel default: 75184251Smarcel val = ~0; 76184251Smarcel break; 77184251Smarcel } 78184251Smarcel return (val); 79184251Smarcel} 80184251Smarcel 81233553Sjchandrauint32_t 82233553Sjchandracfi_read(struct cfi_softc *sc, u_int ofs) 83233553Sjchandra{ 84233553Sjchandra uint32_t val; 85233553Sjchandra uint16_t sval; 86233553Sjchandra 87233553Sjchandra ofs &= ~(sc->sc_width - 1); 88233553Sjchandra switch (sc->sc_width) { 89233553Sjchandra case 1: 90233553Sjchandra val = bus_space_read_1(sc->sc_tag, sc->sc_handle, ofs); 91233553Sjchandra break; 92233553Sjchandra case 2: 93233553Sjchandra sval = bus_space_read_2(sc->sc_tag, sc->sc_handle, ofs); 94233553Sjchandra val = le16toh(sval); 95233553Sjchandra break; 96233553Sjchandra case 4: 97233553Sjchandra val = bus_space_read_4(sc->sc_tag, sc->sc_handle, ofs); 98233553Sjchandra val = le32toh(val); 99233553Sjchandra break; 100233553Sjchandra default: 101233553Sjchandra val = ~0; 102233553Sjchandra break; 103233553Sjchandra } 104233553Sjchandra return (val); 105233553Sjchandra} 106233553Sjchandra 107184251Smarcelstatic void 108184251Smarcelcfi_write(struct cfi_softc *sc, u_int ofs, u_int val) 109184251Smarcel{ 110184251Smarcel 111184251Smarcel ofs &= ~(sc->sc_width - 1); 112184251Smarcel switch (sc->sc_width) { 113184251Smarcel case 1: 114184251Smarcel bus_space_write_1(sc->sc_tag, sc->sc_handle, ofs, val); 115184251Smarcel break; 116184251Smarcel case 2: 117233553Sjchandra bus_space_write_2(sc->sc_tag, sc->sc_handle, ofs, htole16(val)); 118184251Smarcel break; 119184251Smarcel case 4: 120233553Sjchandra bus_space_write_4(sc->sc_tag, sc->sc_handle, ofs, htole32(val)); 121184251Smarcel break; 122184251Smarcel } 123184251Smarcel} 124184251Smarcel 125184251Smarceluint8_t 126184251Smarcelcfi_read_qry(struct cfi_softc *sc, u_int ofs) 127184251Smarcel{ 128184251Smarcel uint8_t val; 129184251Smarcel 130184251Smarcel cfi_write(sc, CFI_QRY_CMD_ADDR * sc->sc_width, CFI_QRY_CMD_DATA); 131184251Smarcel val = cfi_read(sc, ofs * sc->sc_width); 132184251Smarcel cfi_write(sc, 0, CFI_BCS_READ_ARRAY); 133184251Smarcel return (val); 134184251Smarcel} 135184251Smarcel 136184251Smarcelstatic void 137184251Smarcelcfi_amd_write(struct cfi_softc *sc, u_int ofs, u_int addr, u_int data) 138184251Smarcel{ 139184251Smarcel 140184251Smarcel cfi_write(sc, ofs + AMD_ADDR_START, CFI_AMD_UNLOCK); 141184251Smarcel cfi_write(sc, ofs + AMD_ADDR_ACK, CFI_AMD_UNLOCK_ACK); 142184251Smarcel cfi_write(sc, ofs + addr, data); 143184251Smarcel} 144184251Smarcel 145184251Smarcelstatic char * 146184251Smarcelcfi_fmtsize(uint32_t sz) 147184251Smarcel{ 148184251Smarcel static char buf[8]; 149184251Smarcel static const char *sfx[] = { "", "K", "M", "G" }; 150184251Smarcel int sfxidx; 151184251Smarcel 152184251Smarcel sfxidx = 0; 153184251Smarcel while (sfxidx < 3 && sz > 1023) { 154184251Smarcel sz /= 1024; 155184251Smarcel sfxidx++; 156184251Smarcel } 157184251Smarcel 158184251Smarcel sprintf(buf, "%u%sB", sz, sfx[sfxidx]); 159184251Smarcel return (buf); 160184251Smarcel} 161184251Smarcel 162184251Smarcelint 163184251Smarcelcfi_probe(device_t dev) 164184251Smarcel{ 165184251Smarcel char desc[80]; 166184251Smarcel struct cfi_softc *sc; 167184251Smarcel char *vend_str; 168184251Smarcel int error; 169184251Smarcel uint16_t iface, vend; 170184251Smarcel 171184251Smarcel sc = device_get_softc(dev); 172184251Smarcel sc->sc_dev = dev; 173184251Smarcel 174184251Smarcel sc->sc_rid = 0; 175184251Smarcel sc->sc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->sc_rid, 176184251Smarcel RF_ACTIVE); 177184251Smarcel if (sc->sc_res == NULL) 178184251Smarcel return (ENXIO); 179184251Smarcel 180184251Smarcel sc->sc_tag = rman_get_bustag(sc->sc_res); 181184251Smarcel sc->sc_handle = rman_get_bushandle(sc->sc_res); 182184251Smarcel 183188087Ssam if (sc->sc_width == 0) { 184188087Ssam sc->sc_width = 1; 185188087Ssam while (sc->sc_width <= 4) { 186188087Ssam if (cfi_read_qry(sc, CFI_QRY_IDENT) == 'Q') 187188087Ssam break; 188188087Ssam sc->sc_width <<= 1; 189188087Ssam } 190188087Ssam } else if (cfi_read_qry(sc, CFI_QRY_IDENT) != 'Q') { 191188087Ssam error = ENXIO; 192188087Ssam goto out; 193184251Smarcel } 194184251Smarcel if (sc->sc_width > 4) { 195184251Smarcel error = ENXIO; 196184251Smarcel goto out; 197184251Smarcel } 198184251Smarcel 199184251Smarcel /* We got a Q. Check if we also have the R and the Y. */ 200184251Smarcel if (cfi_read_qry(sc, CFI_QRY_IDENT + 1) != 'R' || 201184251Smarcel cfi_read_qry(sc, CFI_QRY_IDENT + 2) != 'Y') { 202184251Smarcel error = ENXIO; 203184251Smarcel goto out; 204184251Smarcel } 205184251Smarcel 206184251Smarcel /* Get the vendor and command set. */ 207184251Smarcel vend = cfi_read_qry(sc, CFI_QRY_VEND) | 208184251Smarcel (cfi_read_qry(sc, CFI_QRY_VEND + 1) << 8); 209184251Smarcel 210184251Smarcel sc->sc_cmdset = vend; 211184251Smarcel 212184251Smarcel switch (vend) { 213184251Smarcel case CFI_VEND_AMD_ECS: 214184251Smarcel case CFI_VEND_AMD_SCS: 215184251Smarcel vend_str = "AMD/Fujitsu"; 216184251Smarcel break; 217184251Smarcel case CFI_VEND_INTEL_ECS: 218184251Smarcel vend_str = "Intel/Sharp"; 219184251Smarcel break; 220184251Smarcel case CFI_VEND_INTEL_SCS: 221184251Smarcel vend_str = "Intel"; 222184251Smarcel break; 223184251Smarcel case CFI_VEND_MITSUBISHI_ECS: 224184251Smarcel case CFI_VEND_MITSUBISHI_SCS: 225184251Smarcel vend_str = "Mitsubishi"; 226184251Smarcel break; 227184251Smarcel default: 228184251Smarcel vend_str = "Unknown vendor"; 229184251Smarcel break; 230184251Smarcel } 231184251Smarcel 232184251Smarcel /* Get the device size. */ 233184251Smarcel sc->sc_size = 1U << cfi_read_qry(sc, CFI_QRY_SIZE); 234184251Smarcel 235184251Smarcel /* Sanity-check the I/F */ 236184251Smarcel iface = cfi_read_qry(sc, CFI_QRY_IFACE) | 237184251Smarcel (cfi_read_qry(sc, CFI_QRY_IFACE + 1) << 8); 238184251Smarcel 239184251Smarcel /* 240184251Smarcel * Adding 1 to iface will give us a bit-wise "switch" 241184251Smarcel * that allows us to test for the interface width by 242184251Smarcel * testing a single bit. 243184251Smarcel */ 244184251Smarcel iface++; 245184251Smarcel 246184251Smarcel error = (iface & sc->sc_width) ? 0 : EINVAL; 247184251Smarcel if (error) 248184251Smarcel goto out; 249184251Smarcel 250184251Smarcel snprintf(desc, sizeof(desc), "%s - %s", vend_str, 251184251Smarcel cfi_fmtsize(sc->sc_size)); 252184251Smarcel device_set_desc_copy(dev, desc); 253184251Smarcel 254184251Smarcel out: 255184251Smarcel bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_rid, sc->sc_res); 256184251Smarcel return (error); 257184251Smarcel} 258184251Smarcel 259184251Smarcelint 260184251Smarcelcfi_attach(device_t dev) 261184251Smarcel{ 262184251Smarcel struct cfi_softc *sc; 263184251Smarcel u_int blksz, blocks; 264184251Smarcel u_int r, u; 265250115Sbrooks#ifdef CFI_SUPPORT_STRATAFLASH 266250115Sbrooks uint64_t ppr; 267250115Sbrooks char name[KENV_MNAMELEN], value[32]; 268250115Sbrooks#endif 269184251Smarcel 270184251Smarcel sc = device_get_softc(dev); 271184251Smarcel sc->sc_dev = dev; 272184251Smarcel 273184251Smarcel sc->sc_rid = 0; 274184251Smarcel sc->sc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->sc_rid, 275184251Smarcel RF_ACTIVE); 276184251Smarcel if (sc->sc_res == NULL) 277184251Smarcel return (ENXIO); 278184251Smarcel 279184251Smarcel sc->sc_tag = rman_get_bustag(sc->sc_res); 280184251Smarcel sc->sc_handle = rman_get_bushandle(sc->sc_res); 281184251Smarcel 282184251Smarcel /* Get time-out values for erase and write. */ 283184251Smarcel sc->sc_write_timeout = 1 << cfi_read_qry(sc, CFI_QRY_TTO_WRITE); 284184251Smarcel sc->sc_erase_timeout = 1 << cfi_read_qry(sc, CFI_QRY_TTO_ERASE); 285184251Smarcel sc->sc_write_timeout *= 1 << cfi_read_qry(sc, CFI_QRY_MTO_WRITE); 286184251Smarcel sc->sc_erase_timeout *= 1 << cfi_read_qry(sc, CFI_QRY_MTO_ERASE); 287184251Smarcel 288184251Smarcel /* Get erase regions. */ 289184251Smarcel sc->sc_regions = cfi_read_qry(sc, CFI_QRY_NREGIONS); 290184251Smarcel sc->sc_region = malloc(sc->sc_regions * sizeof(struct cfi_region), 291184251Smarcel M_TEMP, M_WAITOK | M_ZERO); 292184251Smarcel for (r = 0; r < sc->sc_regions; r++) { 293184251Smarcel blocks = cfi_read_qry(sc, CFI_QRY_REGION(r)) | 294184251Smarcel (cfi_read_qry(sc, CFI_QRY_REGION(r) + 1) << 8); 295184251Smarcel sc->sc_region[r].r_blocks = blocks + 1; 296184251Smarcel 297184251Smarcel blksz = cfi_read_qry(sc, CFI_QRY_REGION(r) + 2) | 298184251Smarcel (cfi_read_qry(sc, CFI_QRY_REGION(r) + 3) << 8); 299184251Smarcel sc->sc_region[r].r_blksz = (blksz == 0) ? 128 : 300184251Smarcel blksz * 256; 301184251Smarcel } 302184251Smarcel 303184251Smarcel /* Reset the device to a default state. */ 304184251Smarcel cfi_write(sc, 0, CFI_BCS_CLEAR_STATUS); 305184251Smarcel 306184251Smarcel if (bootverbose) { 307184251Smarcel device_printf(dev, "["); 308184251Smarcel for (r = 0; r < sc->sc_regions; r++) { 309184251Smarcel printf("%ux%s%s", sc->sc_region[r].r_blocks, 310184251Smarcel cfi_fmtsize(sc->sc_region[r].r_blksz), 311184251Smarcel (r == sc->sc_regions - 1) ? "]\n" : ","); 312184251Smarcel } 313184251Smarcel } 314184251Smarcel 315184251Smarcel u = device_get_unit(dev); 316184251Smarcel sc->sc_nod = make_dev(&cfi_cdevsw, u, UID_ROOT, GID_WHEEL, 0600, 317184251Smarcel "%s%u", cfi_driver_name, u); 318184251Smarcel sc->sc_nod->si_drv1 = sc; 319184251Smarcel 320250115Sbrooks#ifdef CFI_SUPPORT_STRATAFLASH 321250115Sbrooks /* 322250115Sbrooks * Store the Intel factory PPR in the environment. In some 323250115Sbrooks * cases it is the most unique ID on a board. 324250115Sbrooks */ 325250115Sbrooks if (cfi_intel_get_factory_pr(sc, &ppr) == 0) { 326250115Sbrooks if (snprintf(name, sizeof(name), "%s.factory_ppr", 327250115Sbrooks device_get_nameunit(dev)) < (sizeof(name) - 1) && 328250115Sbrooks snprintf(value, sizeof(value), "0x%016jx", ppr) < 329250115Sbrooks (sizeof(value) - 1)) 330250115Sbrooks (void) setenv(name, value); 331250115Sbrooks } 332250115Sbrooks#endif 333250115Sbrooks 334193936Simp device_add_child(dev, "cfid", -1); 335189606Ssam bus_generic_attach(dev); 336189606Ssam 337184251Smarcel return (0); 338184251Smarcel} 339184251Smarcel 340184251Smarcelint 341184251Smarcelcfi_detach(device_t dev) 342184251Smarcel{ 343184251Smarcel struct cfi_softc *sc; 344184251Smarcel 345184251Smarcel sc = device_get_softc(dev); 346184251Smarcel 347184251Smarcel destroy_dev(sc->sc_nod); 348184251Smarcel free(sc->sc_region, M_TEMP); 349184251Smarcel bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_rid, sc->sc_res); 350184251Smarcel return (0); 351184251Smarcel} 352184251Smarcel 353184251Smarcelstatic int 354188156Ssamcfi_wait_ready(struct cfi_softc *sc, u_int ofs, u_int timeout) 355184251Smarcel{ 356184251Smarcel int done, error; 357188156Ssam uint32_t st0 = 0, st = 0; 358184251Smarcel 359184251Smarcel done = 0; 360184251Smarcel error = 0; 361184251Smarcel timeout *= 10; 362184251Smarcel while (!done && !error && timeout) { 363184251Smarcel DELAY(100); 364184251Smarcel timeout--; 365184251Smarcel 366184251Smarcel switch (sc->sc_cmdset) { 367184251Smarcel case CFI_VEND_INTEL_ECS: 368184251Smarcel case CFI_VEND_INTEL_SCS: 369188156Ssam st = cfi_read(sc, ofs); 370188156Ssam done = (st & CFI_INTEL_STATUS_WSMS); 371184251Smarcel if (done) { 372188156Ssam /* NB: bit 0 is reserved */ 373188156Ssam st &= ~(CFI_INTEL_XSTATUS_RSVD | 374188156Ssam CFI_INTEL_STATUS_WSMS | 375188156Ssam CFI_INTEL_STATUS_RSVD); 376188156Ssam if (st & CFI_INTEL_STATUS_DPS) 377184251Smarcel error = EPERM; 378188156Ssam else if (st & CFI_INTEL_STATUS_PSLBS) 379184251Smarcel error = EIO; 380188156Ssam else if (st & CFI_INTEL_STATUS_ECLBS) 381184251Smarcel error = ENXIO; 382188156Ssam else if (st) 383188156Ssam error = EACCES; 384184251Smarcel } 385184251Smarcel break; 386184251Smarcel case CFI_VEND_AMD_SCS: 387184251Smarcel case CFI_VEND_AMD_ECS: 388188156Ssam st0 = cfi_read(sc, ofs); 389188156Ssam st = cfi_read(sc, ofs); 390184251Smarcel done = ((st & 0x40) == (st0 & 0x40)) ? 1 : 0; 391184251Smarcel break; 392184251Smarcel } 393184251Smarcel } 394184251Smarcel if (!done && !error) 395184251Smarcel error = ETIMEDOUT; 396184251Smarcel if (error) 397188156Ssam printf("\nerror=%d (st 0x%x st0 0x%x)\n", error, st, st0); 398184251Smarcel return (error); 399184251Smarcel} 400184251Smarcel 401184251Smarcelint 402184251Smarcelcfi_write_block(struct cfi_softc *sc) 403184251Smarcel{ 404184251Smarcel union { 405184251Smarcel uint8_t *x8; 406184251Smarcel uint16_t *x16; 407184251Smarcel uint32_t *x32; 408184251Smarcel } ptr; 409184251Smarcel register_t intr; 410184251Smarcel int error, i; 411184251Smarcel 412251118Sbrooks /* Intel flash must be unlocked before modification */ 413251118Sbrooks switch (sc->sc_cmdset) { 414251118Sbrooks case CFI_VEND_INTEL_ECS: 415251118Sbrooks case CFI_VEND_INTEL_SCS: 416251118Sbrooks cfi_write(sc, sc->sc_wrofs, CFI_INTEL_LBS); 417251118Sbrooks cfi_write(sc, sc->sc_wrofs, CFI_INTEL_UB); 418251118Sbrooks cfi_write(sc, sc->sc_wrofs, CFI_BCS_READ_ARRAY); 419251118Sbrooks break; 420251118Sbrooks } 421251118Sbrooks 422184251Smarcel /* Erase the block. */ 423184251Smarcel switch (sc->sc_cmdset) { 424184251Smarcel case CFI_VEND_INTEL_ECS: 425184251Smarcel case CFI_VEND_INTEL_SCS: 426184251Smarcel cfi_write(sc, sc->sc_wrofs, CFI_BCS_BLOCK_ERASE); 427184251Smarcel cfi_write(sc, sc->sc_wrofs, CFI_BCS_CONFIRM); 428184251Smarcel break; 429184251Smarcel case CFI_VEND_AMD_SCS: 430184251Smarcel case CFI_VEND_AMD_ECS: 431184251Smarcel cfi_amd_write(sc, sc->sc_wrofs, AMD_ADDR_START, 432184251Smarcel CFI_AMD_ERASE_SECTOR); 433184251Smarcel cfi_amd_write(sc, sc->sc_wrofs, 0, CFI_AMD_BLOCK_ERASE); 434184251Smarcel break; 435184251Smarcel default: 436184251Smarcel /* Better safe than sorry... */ 437184251Smarcel return (ENODEV); 438184251Smarcel } 439188156Ssam error = cfi_wait_ready(sc, sc->sc_wrofs, sc->sc_erase_timeout); 440184251Smarcel if (error) 441184251Smarcel goto out; 442184251Smarcel 443184251Smarcel /* Write the block. */ 444184251Smarcel ptr.x8 = sc->sc_wrbuf; 445184251Smarcel for (i = 0; i < sc->sc_wrbufsz; i += sc->sc_width) { 446184251Smarcel 447184251Smarcel /* 448184251Smarcel * Make sure the command to start a write and the 449184251Smarcel * actual write happens back-to-back without any 450184251Smarcel * excessive delays. 451184251Smarcel */ 452184251Smarcel intr = intr_disable(); 453184251Smarcel 454184251Smarcel switch (sc->sc_cmdset) { 455184251Smarcel case CFI_VEND_INTEL_ECS: 456184251Smarcel case CFI_VEND_INTEL_SCS: 457184251Smarcel cfi_write(sc, sc->sc_wrofs + i, CFI_BCS_PROGRAM); 458184251Smarcel break; 459184251Smarcel case CFI_VEND_AMD_SCS: 460184251Smarcel case CFI_VEND_AMD_ECS: 461184251Smarcel cfi_amd_write(sc, 0, AMD_ADDR_START, CFI_AMD_PROGRAM); 462184251Smarcel break; 463184251Smarcel } 464184251Smarcel switch (sc->sc_width) { 465184251Smarcel case 1: 466184251Smarcel bus_space_write_1(sc->sc_tag, sc->sc_handle, 467184251Smarcel sc->sc_wrofs + i, *(ptr.x8)++); 468184251Smarcel break; 469184251Smarcel case 2: 470184251Smarcel bus_space_write_2(sc->sc_tag, sc->sc_handle, 471184251Smarcel sc->sc_wrofs + i, *(ptr.x16)++); 472184251Smarcel break; 473184251Smarcel case 4: 474184251Smarcel bus_space_write_4(sc->sc_tag, sc->sc_handle, 475184251Smarcel sc->sc_wrofs + i, *(ptr.x32)++); 476184251Smarcel break; 477184251Smarcel } 478184251Smarcel 479184251Smarcel intr_restore(intr); 480184251Smarcel 481188156Ssam error = cfi_wait_ready(sc, sc->sc_wrofs, sc->sc_write_timeout); 482184251Smarcel if (error) 483184251Smarcel goto out; 484184251Smarcel } 485184251Smarcel 486184251Smarcel /* error is 0. */ 487184251Smarcel 488184251Smarcel out: 489184251Smarcel cfi_write(sc, 0, CFI_BCS_READ_ARRAY); 490251118Sbrooks 491251118Sbrooks /* Relock Intel flash */ 492251118Sbrooks switch (sc->sc_cmdset) { 493251118Sbrooks case CFI_VEND_INTEL_ECS: 494251118Sbrooks case CFI_VEND_INTEL_SCS: 495251118Sbrooks cfi_write(sc, sc->sc_wrofs, CFI_INTEL_LBS); 496251118Sbrooks cfi_write(sc, sc->sc_wrofs, CFI_INTEL_LB); 497251118Sbrooks cfi_write(sc, sc->sc_wrofs, CFI_BCS_READ_ARRAY); 498251118Sbrooks break; 499251118Sbrooks } 500184251Smarcel return (error); 501184251Smarcel} 502188156Ssam 503188156Ssam#ifdef CFI_SUPPORT_STRATAFLASH 504188156Ssam/* 505188156Ssam * Intel StrataFlash Protection Register Support. 506188156Ssam * 507188156Ssam * The memory includes a 128-bit Protection Register that can be 508188156Ssam * used for security. There are two 64-bit segments; one is programmed 509188156Ssam * at the factory with a unique 64-bit number which is immutable. 510188156Ssam * The other segment is left blank for User (OEM) programming. 511188267Ssam * The User/OEM segment is One Time Programmable (OTP). It can also 512188332Ssam * be locked to prevent any further writes by setting bit 0 of the 513188267Ssam * Protection Lock Register (PLR). The PLR can written only once. 514188156Ssam */ 515188156Ssam 516188156Ssamstatic uint16_t 517188156Ssamcfi_get16(struct cfi_softc *sc, int off) 518188156Ssam{ 519188156Ssam uint16_t v = bus_space_read_2(sc->sc_tag, sc->sc_handle, off<<1); 520188156Ssam return v; 521188156Ssam} 522188156Ssam 523188268Ssam#ifdef CFI_ARMEDANDDANGEROUS 524188156Ssamstatic void 525188156Ssamcfi_put16(struct cfi_softc *sc, int off, uint16_t v) 526188156Ssam{ 527188156Ssam bus_space_write_2(sc->sc_tag, sc->sc_handle, off<<1, v); 528188156Ssam} 529188268Ssam#endif 530188156Ssam 531188156Ssam/* 532188156Ssam * Read the factory-defined 64-bit segment of the PR. 533188156Ssam */ 534188156Ssamint 535188156Ssamcfi_intel_get_factory_pr(struct cfi_softc *sc, uint64_t *id) 536188156Ssam{ 537188156Ssam if (sc->sc_cmdset != CFI_VEND_INTEL_ECS) 538188156Ssam return EOPNOTSUPP; 539188156Ssam KASSERT(sc->sc_width == 2, ("sc_width %d", sc->sc_width)); 540188156Ssam 541188156Ssam cfi_write(sc, 0, CFI_INTEL_READ_ID); 542188156Ssam *id = ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(0)))<<48 | 543188156Ssam ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(1)))<<32 | 544188156Ssam ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(2)))<<16 | 545188156Ssam ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(3))); 546188156Ssam cfi_write(sc, 0, CFI_BCS_READ_ARRAY); 547188156Ssam return 0; 548188156Ssam} 549188156Ssam 550188156Ssam/* 551188156Ssam * Read the User/OEM 64-bit segment of the PR. 552188156Ssam */ 553188156Ssamint 554188156Ssamcfi_intel_get_oem_pr(struct cfi_softc *sc, uint64_t *id) 555188156Ssam{ 556188156Ssam if (sc->sc_cmdset != CFI_VEND_INTEL_ECS) 557188156Ssam return EOPNOTSUPP; 558188156Ssam KASSERT(sc->sc_width == 2, ("sc_width %d", sc->sc_width)); 559188156Ssam 560188156Ssam cfi_write(sc, 0, CFI_INTEL_READ_ID); 561188156Ssam *id = ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(4)))<<48 | 562188156Ssam ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(5)))<<32 | 563188156Ssam ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(6)))<<16 | 564188156Ssam ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(7))); 565188156Ssam cfi_write(sc, 0, CFI_BCS_READ_ARRAY); 566188156Ssam return 0; 567188156Ssam} 568188156Ssam 569188156Ssam/* 570188156Ssam * Write the User/OEM 64-bit segment of the PR. 571188267Ssam * XXX should allow writing individual words/bytes 572188156Ssam */ 573188156Ssamint 574188156Ssamcfi_intel_set_oem_pr(struct cfi_softc *sc, uint64_t id) 575188156Ssam{ 576188267Ssam#ifdef CFI_ARMEDANDDANGEROUS 577188156Ssam register_t intr; 578188156Ssam int i, error; 579188267Ssam#endif 580188156Ssam 581188156Ssam if (sc->sc_cmdset != CFI_VEND_INTEL_ECS) 582188156Ssam return EOPNOTSUPP; 583188156Ssam KASSERT(sc->sc_width == 2, ("sc_width %d", sc->sc_width)); 584188156Ssam 585188267Ssam#ifdef CFI_ARMEDANDDANGEROUS 586188156Ssam for (i = 7; i >= 4; i--, id >>= 16) { 587188156Ssam intr = intr_disable(); 588188156Ssam cfi_write(sc, 0, CFI_INTEL_PP_SETUP); 589188156Ssam cfi_put16(sc, CFI_INTEL_PR(i), id&0xffff); 590188156Ssam intr_restore(intr); 591188156Ssam error = cfi_wait_ready(sc, CFI_BCS_READ_STATUS, 592188156Ssam sc->sc_write_timeout); 593188156Ssam if (error) 594188156Ssam break; 595188156Ssam } 596188156Ssam cfi_write(sc, 0, CFI_BCS_READ_ARRAY); 597188156Ssam return error; 598188267Ssam#else 599188267Ssam device_printf(sc->sc_dev, "%s: OEM PR not set, " 600188267Ssam "CFI_ARMEDANDDANGEROUS not configured\n", __func__); 601188267Ssam return ENXIO; 602188267Ssam#endif 603188156Ssam} 604188156Ssam 605188156Ssam/* 606188156Ssam * Read the contents of the Protection Lock Register. 607188156Ssam */ 608188156Ssamint 609188156Ssamcfi_intel_get_plr(struct cfi_softc *sc, uint32_t *plr) 610188156Ssam{ 611188156Ssam if (sc->sc_cmdset != CFI_VEND_INTEL_ECS) 612188156Ssam return EOPNOTSUPP; 613188156Ssam KASSERT(sc->sc_width == 2, ("sc_width %d", sc->sc_width)); 614188156Ssam 615188156Ssam cfi_write(sc, 0, CFI_INTEL_READ_ID); 616188156Ssam *plr = cfi_get16(sc, CFI_INTEL_PLR); 617188156Ssam cfi_write(sc, 0, CFI_BCS_READ_ARRAY); 618188156Ssam return 0; 619188156Ssam} 620188156Ssam 621188156Ssam/* 622188156Ssam * Write the Protection Lock Register to lock down the 623188156Ssam * user-settable segment of the Protection Register. 624188156Ssam * NOTE: this operation is not reversible. 625188156Ssam */ 626188156Ssamint 627188156Ssamcfi_intel_set_plr(struct cfi_softc *sc) 628188156Ssam{ 629188156Ssam#ifdef CFI_ARMEDANDDANGEROUS 630188156Ssam register_t intr; 631188268Ssam int error; 632188156Ssam#endif 633188156Ssam if (sc->sc_cmdset != CFI_VEND_INTEL_ECS) 634188156Ssam return EOPNOTSUPP; 635188156Ssam KASSERT(sc->sc_width == 2, ("sc_width %d", sc->sc_width)); 636188156Ssam 637188156Ssam#ifdef CFI_ARMEDANDDANGEROUS 638188156Ssam /* worthy of console msg */ 639188156Ssam device_printf(sc->sc_dev, "set PLR\n"); 640188156Ssam intr = intr_disable(); 641188156Ssam cfi_write(sc, 0, CFI_INTEL_PP_SETUP); 642188156Ssam cfi_put16(sc, CFI_INTEL_PLR, 0xFFFD); 643188156Ssam intr_restore(intr); 644188156Ssam error = cfi_wait_ready(sc, CFI_BCS_READ_STATUS, sc->sc_write_timeout); 645188156Ssam cfi_write(sc, 0, CFI_BCS_READ_ARRAY); 646188268Ssam return error; 647188156Ssam#else 648188156Ssam device_printf(sc->sc_dev, "%s: PLR not set, " 649188156Ssam "CFI_ARMEDANDDANGEROUS not configured\n", __func__); 650188268Ssam return ENXIO; 651188156Ssam#endif 652188156Ssam} 653188156Ssam#endif /* CFI_SUPPORT_STRATAFLASH */ 654