cfi_core.c revision 233553
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 233553 2012-03-27 15:13:12Z jchandra $"); 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> 40184251Smarcel#include <sys/kernel.h> 41184251Smarcel#include <sys/malloc.h> 42184251Smarcel#include <sys/module.h> 43184251Smarcel#include <sys/rman.h> 44184251Smarcel#include <sys/sysctl.h> 45184251Smarcel 46184251Smarcel#include <machine/bus.h> 47184251Smarcel 48184251Smarcel#include <dev/cfi/cfi_reg.h> 49184251Smarcel#include <dev/cfi/cfi_var.h> 50184251Smarcel 51184251Smarcelextern struct cdevsw cfi_cdevsw; 52184251Smarcel 53184251Smarcelchar cfi_driver_name[] = "cfi"; 54184251Smarceldevclass_t cfi_devclass; 55189606Ssamdevclass_t cfi_diskclass; 56184251Smarcel 57184251Smarceluint32_t 58233553Sjchandracfi_read_raw(struct cfi_softc *sc, u_int ofs) 59184251Smarcel{ 60184251Smarcel uint32_t val; 61184251Smarcel 62184251Smarcel ofs &= ~(sc->sc_width - 1); 63184251Smarcel switch (sc->sc_width) { 64184251Smarcel case 1: 65184251Smarcel val = bus_space_read_1(sc->sc_tag, sc->sc_handle, ofs); 66184251Smarcel break; 67184251Smarcel case 2: 68184251Smarcel val = bus_space_read_2(sc->sc_tag, sc->sc_handle, ofs); 69184251Smarcel break; 70184251Smarcel case 4: 71184251Smarcel val = bus_space_read_4(sc->sc_tag, sc->sc_handle, ofs); 72184251Smarcel break; 73184251Smarcel default: 74184251Smarcel val = ~0; 75184251Smarcel break; 76184251Smarcel } 77184251Smarcel return (val); 78184251Smarcel} 79184251Smarcel 80233553Sjchandrauint32_t 81233553Sjchandracfi_read(struct cfi_softc *sc, u_int ofs) 82233553Sjchandra{ 83233553Sjchandra uint32_t val; 84233553Sjchandra uint16_t sval; 85233553Sjchandra 86233553Sjchandra ofs &= ~(sc->sc_width - 1); 87233553Sjchandra switch (sc->sc_width) { 88233553Sjchandra case 1: 89233553Sjchandra val = bus_space_read_1(sc->sc_tag, sc->sc_handle, ofs); 90233553Sjchandra break; 91233553Sjchandra case 2: 92233553Sjchandra sval = bus_space_read_2(sc->sc_tag, sc->sc_handle, ofs); 93233553Sjchandra val = le16toh(sval); 94233553Sjchandra break; 95233553Sjchandra case 4: 96233553Sjchandra val = bus_space_read_4(sc->sc_tag, sc->sc_handle, ofs); 97233553Sjchandra val = le32toh(val); 98233553Sjchandra break; 99233553Sjchandra default: 100233553Sjchandra val = ~0; 101233553Sjchandra break; 102233553Sjchandra } 103233553Sjchandra return (val); 104233553Sjchandra} 105233553Sjchandra 106184251Smarcelstatic void 107184251Smarcelcfi_write(struct cfi_softc *sc, u_int ofs, u_int val) 108184251Smarcel{ 109184251Smarcel 110184251Smarcel ofs &= ~(sc->sc_width - 1); 111184251Smarcel switch (sc->sc_width) { 112184251Smarcel case 1: 113184251Smarcel bus_space_write_1(sc->sc_tag, sc->sc_handle, ofs, val); 114184251Smarcel break; 115184251Smarcel case 2: 116233553Sjchandra bus_space_write_2(sc->sc_tag, sc->sc_handle, ofs, htole16(val)); 117184251Smarcel break; 118184251Smarcel case 4: 119233553Sjchandra bus_space_write_4(sc->sc_tag, sc->sc_handle, ofs, htole32(val)); 120184251Smarcel break; 121184251Smarcel } 122184251Smarcel} 123184251Smarcel 124184251Smarceluint8_t 125184251Smarcelcfi_read_qry(struct cfi_softc *sc, u_int ofs) 126184251Smarcel{ 127184251Smarcel uint8_t val; 128184251Smarcel 129184251Smarcel cfi_write(sc, CFI_QRY_CMD_ADDR * sc->sc_width, CFI_QRY_CMD_DATA); 130184251Smarcel val = cfi_read(sc, ofs * sc->sc_width); 131184251Smarcel cfi_write(sc, 0, CFI_BCS_READ_ARRAY); 132184251Smarcel return (val); 133184251Smarcel} 134184251Smarcel 135184251Smarcelstatic void 136184251Smarcelcfi_amd_write(struct cfi_softc *sc, u_int ofs, u_int addr, u_int data) 137184251Smarcel{ 138184251Smarcel 139184251Smarcel cfi_write(sc, ofs + AMD_ADDR_START, CFI_AMD_UNLOCK); 140184251Smarcel cfi_write(sc, ofs + AMD_ADDR_ACK, CFI_AMD_UNLOCK_ACK); 141184251Smarcel cfi_write(sc, ofs + addr, data); 142184251Smarcel} 143184251Smarcel 144184251Smarcelstatic char * 145184251Smarcelcfi_fmtsize(uint32_t sz) 146184251Smarcel{ 147184251Smarcel static char buf[8]; 148184251Smarcel static const char *sfx[] = { "", "K", "M", "G" }; 149184251Smarcel int sfxidx; 150184251Smarcel 151184251Smarcel sfxidx = 0; 152184251Smarcel while (sfxidx < 3 && sz > 1023) { 153184251Smarcel sz /= 1024; 154184251Smarcel sfxidx++; 155184251Smarcel } 156184251Smarcel 157184251Smarcel sprintf(buf, "%u%sB", sz, sfx[sfxidx]); 158184251Smarcel return (buf); 159184251Smarcel} 160184251Smarcel 161184251Smarcelint 162184251Smarcelcfi_probe(device_t dev) 163184251Smarcel{ 164184251Smarcel char desc[80]; 165184251Smarcel struct cfi_softc *sc; 166184251Smarcel char *vend_str; 167184251Smarcel int error; 168184251Smarcel uint16_t iface, vend; 169184251Smarcel 170184251Smarcel sc = device_get_softc(dev); 171184251Smarcel sc->sc_dev = dev; 172184251Smarcel 173184251Smarcel sc->sc_rid = 0; 174184251Smarcel sc->sc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->sc_rid, 175184251Smarcel RF_ACTIVE); 176184251Smarcel if (sc->sc_res == NULL) 177184251Smarcel return (ENXIO); 178184251Smarcel 179184251Smarcel sc->sc_tag = rman_get_bustag(sc->sc_res); 180184251Smarcel sc->sc_handle = rman_get_bushandle(sc->sc_res); 181184251Smarcel 182188087Ssam if (sc->sc_width == 0) { 183188087Ssam sc->sc_width = 1; 184188087Ssam while (sc->sc_width <= 4) { 185188087Ssam if (cfi_read_qry(sc, CFI_QRY_IDENT) == 'Q') 186188087Ssam break; 187188087Ssam sc->sc_width <<= 1; 188188087Ssam } 189188087Ssam } else if (cfi_read_qry(sc, CFI_QRY_IDENT) != 'Q') { 190188087Ssam error = ENXIO; 191188087Ssam goto out; 192184251Smarcel } 193184251Smarcel if (sc->sc_width > 4) { 194184251Smarcel error = ENXIO; 195184251Smarcel goto out; 196184251Smarcel } 197184251Smarcel 198184251Smarcel /* We got a Q. Check if we also have the R and the Y. */ 199184251Smarcel if (cfi_read_qry(sc, CFI_QRY_IDENT + 1) != 'R' || 200184251Smarcel cfi_read_qry(sc, CFI_QRY_IDENT + 2) != 'Y') { 201184251Smarcel error = ENXIO; 202184251Smarcel goto out; 203184251Smarcel } 204184251Smarcel 205184251Smarcel /* Get the vendor and command set. */ 206184251Smarcel vend = cfi_read_qry(sc, CFI_QRY_VEND) | 207184251Smarcel (cfi_read_qry(sc, CFI_QRY_VEND + 1) << 8); 208184251Smarcel 209184251Smarcel sc->sc_cmdset = vend; 210184251Smarcel 211184251Smarcel switch (vend) { 212184251Smarcel case CFI_VEND_AMD_ECS: 213184251Smarcel case CFI_VEND_AMD_SCS: 214184251Smarcel vend_str = "AMD/Fujitsu"; 215184251Smarcel break; 216184251Smarcel case CFI_VEND_INTEL_ECS: 217184251Smarcel vend_str = "Intel/Sharp"; 218184251Smarcel break; 219184251Smarcel case CFI_VEND_INTEL_SCS: 220184251Smarcel vend_str = "Intel"; 221184251Smarcel break; 222184251Smarcel case CFI_VEND_MITSUBISHI_ECS: 223184251Smarcel case CFI_VEND_MITSUBISHI_SCS: 224184251Smarcel vend_str = "Mitsubishi"; 225184251Smarcel break; 226184251Smarcel default: 227184251Smarcel vend_str = "Unknown vendor"; 228184251Smarcel break; 229184251Smarcel } 230184251Smarcel 231184251Smarcel /* Get the device size. */ 232184251Smarcel sc->sc_size = 1U << cfi_read_qry(sc, CFI_QRY_SIZE); 233184251Smarcel 234184251Smarcel /* Sanity-check the I/F */ 235184251Smarcel iface = cfi_read_qry(sc, CFI_QRY_IFACE) | 236184251Smarcel (cfi_read_qry(sc, CFI_QRY_IFACE + 1) << 8); 237184251Smarcel 238184251Smarcel /* 239184251Smarcel * Adding 1 to iface will give us a bit-wise "switch" 240184251Smarcel * that allows us to test for the interface width by 241184251Smarcel * testing a single bit. 242184251Smarcel */ 243184251Smarcel iface++; 244184251Smarcel 245184251Smarcel error = (iface & sc->sc_width) ? 0 : EINVAL; 246184251Smarcel if (error) 247184251Smarcel goto out; 248184251Smarcel 249184251Smarcel snprintf(desc, sizeof(desc), "%s - %s", vend_str, 250184251Smarcel cfi_fmtsize(sc->sc_size)); 251184251Smarcel device_set_desc_copy(dev, desc); 252184251Smarcel 253184251Smarcel out: 254184251Smarcel bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_rid, sc->sc_res); 255184251Smarcel return (error); 256184251Smarcel} 257184251Smarcel 258184251Smarcelint 259184251Smarcelcfi_attach(device_t dev) 260184251Smarcel{ 261184251Smarcel struct cfi_softc *sc; 262184251Smarcel u_int blksz, blocks; 263184251Smarcel u_int r, u; 264184251Smarcel 265184251Smarcel sc = device_get_softc(dev); 266184251Smarcel sc->sc_dev = dev; 267184251Smarcel 268184251Smarcel sc->sc_rid = 0; 269184251Smarcel sc->sc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->sc_rid, 270184251Smarcel RF_ACTIVE); 271184251Smarcel if (sc->sc_res == NULL) 272184251Smarcel return (ENXIO); 273184251Smarcel 274184251Smarcel sc->sc_tag = rman_get_bustag(sc->sc_res); 275184251Smarcel sc->sc_handle = rman_get_bushandle(sc->sc_res); 276184251Smarcel 277184251Smarcel /* Get time-out values for erase and write. */ 278184251Smarcel sc->sc_write_timeout = 1 << cfi_read_qry(sc, CFI_QRY_TTO_WRITE); 279184251Smarcel sc->sc_erase_timeout = 1 << cfi_read_qry(sc, CFI_QRY_TTO_ERASE); 280184251Smarcel sc->sc_write_timeout *= 1 << cfi_read_qry(sc, CFI_QRY_MTO_WRITE); 281184251Smarcel sc->sc_erase_timeout *= 1 << cfi_read_qry(sc, CFI_QRY_MTO_ERASE); 282184251Smarcel 283184251Smarcel /* Get erase regions. */ 284184251Smarcel sc->sc_regions = cfi_read_qry(sc, CFI_QRY_NREGIONS); 285184251Smarcel sc->sc_region = malloc(sc->sc_regions * sizeof(struct cfi_region), 286184251Smarcel M_TEMP, M_WAITOK | M_ZERO); 287184251Smarcel for (r = 0; r < sc->sc_regions; r++) { 288184251Smarcel blocks = cfi_read_qry(sc, CFI_QRY_REGION(r)) | 289184251Smarcel (cfi_read_qry(sc, CFI_QRY_REGION(r) + 1) << 8); 290184251Smarcel sc->sc_region[r].r_blocks = blocks + 1; 291184251Smarcel 292184251Smarcel blksz = cfi_read_qry(sc, CFI_QRY_REGION(r) + 2) | 293184251Smarcel (cfi_read_qry(sc, CFI_QRY_REGION(r) + 3) << 8); 294184251Smarcel sc->sc_region[r].r_blksz = (blksz == 0) ? 128 : 295184251Smarcel blksz * 256; 296184251Smarcel } 297184251Smarcel 298184251Smarcel /* Reset the device to a default state. */ 299184251Smarcel cfi_write(sc, 0, CFI_BCS_CLEAR_STATUS); 300184251Smarcel 301184251Smarcel if (bootverbose) { 302184251Smarcel device_printf(dev, "["); 303184251Smarcel for (r = 0; r < sc->sc_regions; r++) { 304184251Smarcel printf("%ux%s%s", sc->sc_region[r].r_blocks, 305184251Smarcel cfi_fmtsize(sc->sc_region[r].r_blksz), 306184251Smarcel (r == sc->sc_regions - 1) ? "]\n" : ","); 307184251Smarcel } 308184251Smarcel } 309184251Smarcel 310184251Smarcel u = device_get_unit(dev); 311184251Smarcel sc->sc_nod = make_dev(&cfi_cdevsw, u, UID_ROOT, GID_WHEEL, 0600, 312184251Smarcel "%s%u", cfi_driver_name, u); 313184251Smarcel sc->sc_nod->si_drv1 = sc; 314184251Smarcel 315193936Simp device_add_child(dev, "cfid", -1); 316189606Ssam bus_generic_attach(dev); 317189606Ssam 318184251Smarcel return (0); 319184251Smarcel} 320184251Smarcel 321184251Smarcelint 322184251Smarcelcfi_detach(device_t dev) 323184251Smarcel{ 324184251Smarcel struct cfi_softc *sc; 325184251Smarcel 326184251Smarcel sc = device_get_softc(dev); 327184251Smarcel 328184251Smarcel destroy_dev(sc->sc_nod); 329184251Smarcel free(sc->sc_region, M_TEMP); 330184251Smarcel bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_rid, sc->sc_res); 331184251Smarcel return (0); 332184251Smarcel} 333184251Smarcel 334184251Smarcelstatic int 335188156Ssamcfi_wait_ready(struct cfi_softc *sc, u_int ofs, u_int timeout) 336184251Smarcel{ 337184251Smarcel int done, error; 338188156Ssam uint32_t st0 = 0, st = 0; 339184251Smarcel 340184251Smarcel done = 0; 341184251Smarcel error = 0; 342184251Smarcel timeout *= 10; 343184251Smarcel while (!done && !error && timeout) { 344184251Smarcel DELAY(100); 345184251Smarcel timeout--; 346184251Smarcel 347184251Smarcel switch (sc->sc_cmdset) { 348184251Smarcel case CFI_VEND_INTEL_ECS: 349184251Smarcel case CFI_VEND_INTEL_SCS: 350188156Ssam st = cfi_read(sc, ofs); 351188156Ssam done = (st & CFI_INTEL_STATUS_WSMS); 352184251Smarcel if (done) { 353188156Ssam /* NB: bit 0 is reserved */ 354188156Ssam st &= ~(CFI_INTEL_XSTATUS_RSVD | 355188156Ssam CFI_INTEL_STATUS_WSMS | 356188156Ssam CFI_INTEL_STATUS_RSVD); 357188156Ssam if (st & CFI_INTEL_STATUS_DPS) 358184251Smarcel error = EPERM; 359188156Ssam else if (st & CFI_INTEL_STATUS_PSLBS) 360184251Smarcel error = EIO; 361188156Ssam else if (st & CFI_INTEL_STATUS_ECLBS) 362184251Smarcel error = ENXIO; 363188156Ssam else if (st) 364188156Ssam error = EACCES; 365184251Smarcel } 366184251Smarcel break; 367184251Smarcel case CFI_VEND_AMD_SCS: 368184251Smarcel case CFI_VEND_AMD_ECS: 369188156Ssam st0 = cfi_read(sc, ofs); 370188156Ssam st = cfi_read(sc, ofs); 371184251Smarcel done = ((st & 0x40) == (st0 & 0x40)) ? 1 : 0; 372184251Smarcel break; 373184251Smarcel } 374184251Smarcel } 375184251Smarcel if (!done && !error) 376184251Smarcel error = ETIMEDOUT; 377184251Smarcel if (error) 378188156Ssam printf("\nerror=%d (st 0x%x st0 0x%x)\n", error, st, st0); 379184251Smarcel return (error); 380184251Smarcel} 381184251Smarcel 382184251Smarcelint 383184251Smarcelcfi_write_block(struct cfi_softc *sc) 384184251Smarcel{ 385184251Smarcel union { 386184251Smarcel uint8_t *x8; 387184251Smarcel uint16_t *x16; 388184251Smarcel uint32_t *x32; 389184251Smarcel } ptr; 390184251Smarcel register_t intr; 391184251Smarcel int error, i; 392184251Smarcel 393184251Smarcel /* Erase the block. */ 394184251Smarcel switch (sc->sc_cmdset) { 395184251Smarcel case CFI_VEND_INTEL_ECS: 396184251Smarcel case CFI_VEND_INTEL_SCS: 397184251Smarcel cfi_write(sc, sc->sc_wrofs, CFI_BCS_BLOCK_ERASE); 398184251Smarcel cfi_write(sc, sc->sc_wrofs, CFI_BCS_CONFIRM); 399184251Smarcel break; 400184251Smarcel case CFI_VEND_AMD_SCS: 401184251Smarcel case CFI_VEND_AMD_ECS: 402184251Smarcel cfi_amd_write(sc, sc->sc_wrofs, AMD_ADDR_START, 403184251Smarcel CFI_AMD_ERASE_SECTOR); 404184251Smarcel cfi_amd_write(sc, sc->sc_wrofs, 0, CFI_AMD_BLOCK_ERASE); 405184251Smarcel break; 406184251Smarcel default: 407184251Smarcel /* Better safe than sorry... */ 408184251Smarcel return (ENODEV); 409184251Smarcel } 410188156Ssam error = cfi_wait_ready(sc, sc->sc_wrofs, sc->sc_erase_timeout); 411184251Smarcel if (error) 412184251Smarcel goto out; 413184251Smarcel 414184251Smarcel /* Write the block. */ 415184251Smarcel ptr.x8 = sc->sc_wrbuf; 416184251Smarcel for (i = 0; i < sc->sc_wrbufsz; i += sc->sc_width) { 417184251Smarcel 418184251Smarcel /* 419184251Smarcel * Make sure the command to start a write and the 420184251Smarcel * actual write happens back-to-back without any 421184251Smarcel * excessive delays. 422184251Smarcel */ 423184251Smarcel intr = intr_disable(); 424184251Smarcel 425184251Smarcel switch (sc->sc_cmdset) { 426184251Smarcel case CFI_VEND_INTEL_ECS: 427184251Smarcel case CFI_VEND_INTEL_SCS: 428184251Smarcel cfi_write(sc, sc->sc_wrofs + i, CFI_BCS_PROGRAM); 429184251Smarcel break; 430184251Smarcel case CFI_VEND_AMD_SCS: 431184251Smarcel case CFI_VEND_AMD_ECS: 432184251Smarcel cfi_amd_write(sc, 0, AMD_ADDR_START, CFI_AMD_PROGRAM); 433184251Smarcel break; 434184251Smarcel } 435184251Smarcel switch (sc->sc_width) { 436184251Smarcel case 1: 437184251Smarcel bus_space_write_1(sc->sc_tag, sc->sc_handle, 438184251Smarcel sc->sc_wrofs + i, *(ptr.x8)++); 439184251Smarcel break; 440184251Smarcel case 2: 441184251Smarcel bus_space_write_2(sc->sc_tag, sc->sc_handle, 442184251Smarcel sc->sc_wrofs + i, *(ptr.x16)++); 443184251Smarcel break; 444184251Smarcel case 4: 445184251Smarcel bus_space_write_4(sc->sc_tag, sc->sc_handle, 446184251Smarcel sc->sc_wrofs + i, *(ptr.x32)++); 447184251Smarcel break; 448184251Smarcel } 449184251Smarcel 450184251Smarcel intr_restore(intr); 451184251Smarcel 452188156Ssam error = cfi_wait_ready(sc, sc->sc_wrofs, sc->sc_write_timeout); 453184251Smarcel if (error) 454184251Smarcel goto out; 455184251Smarcel } 456184251Smarcel 457184251Smarcel /* error is 0. */ 458184251Smarcel 459184251Smarcel out: 460184251Smarcel cfi_write(sc, 0, CFI_BCS_READ_ARRAY); 461184251Smarcel return (error); 462184251Smarcel} 463188156Ssam 464188156Ssam#ifdef CFI_SUPPORT_STRATAFLASH 465188156Ssam/* 466188156Ssam * Intel StrataFlash Protection Register Support. 467188156Ssam * 468188156Ssam * The memory includes a 128-bit Protection Register that can be 469188156Ssam * used for security. There are two 64-bit segments; one is programmed 470188156Ssam * at the factory with a unique 64-bit number which is immutable. 471188156Ssam * The other segment is left blank for User (OEM) programming. 472188267Ssam * The User/OEM segment is One Time Programmable (OTP). It can also 473188332Ssam * be locked to prevent any further writes by setting bit 0 of the 474188267Ssam * Protection Lock Register (PLR). The PLR can written only once. 475188156Ssam */ 476188156Ssam 477188156Ssamstatic uint16_t 478188156Ssamcfi_get16(struct cfi_softc *sc, int off) 479188156Ssam{ 480188156Ssam uint16_t v = bus_space_read_2(sc->sc_tag, sc->sc_handle, off<<1); 481188156Ssam return v; 482188156Ssam} 483188156Ssam 484188268Ssam#ifdef CFI_ARMEDANDDANGEROUS 485188156Ssamstatic void 486188156Ssamcfi_put16(struct cfi_softc *sc, int off, uint16_t v) 487188156Ssam{ 488188156Ssam bus_space_write_2(sc->sc_tag, sc->sc_handle, off<<1, v); 489188156Ssam} 490188268Ssam#endif 491188156Ssam 492188156Ssam/* 493188156Ssam * Read the factory-defined 64-bit segment of the PR. 494188156Ssam */ 495188156Ssamint 496188156Ssamcfi_intel_get_factory_pr(struct cfi_softc *sc, uint64_t *id) 497188156Ssam{ 498188156Ssam if (sc->sc_cmdset != CFI_VEND_INTEL_ECS) 499188156Ssam return EOPNOTSUPP; 500188156Ssam KASSERT(sc->sc_width == 2, ("sc_width %d", sc->sc_width)); 501188156Ssam 502188156Ssam cfi_write(sc, 0, CFI_INTEL_READ_ID); 503188156Ssam *id = ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(0)))<<48 | 504188156Ssam ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(1)))<<32 | 505188156Ssam ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(2)))<<16 | 506188156Ssam ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(3))); 507188156Ssam cfi_write(sc, 0, CFI_BCS_READ_ARRAY); 508188156Ssam return 0; 509188156Ssam} 510188156Ssam 511188156Ssam/* 512188156Ssam * Read the User/OEM 64-bit segment of the PR. 513188156Ssam */ 514188156Ssamint 515188156Ssamcfi_intel_get_oem_pr(struct cfi_softc *sc, uint64_t *id) 516188156Ssam{ 517188156Ssam if (sc->sc_cmdset != CFI_VEND_INTEL_ECS) 518188156Ssam return EOPNOTSUPP; 519188156Ssam KASSERT(sc->sc_width == 2, ("sc_width %d", sc->sc_width)); 520188156Ssam 521188156Ssam cfi_write(sc, 0, CFI_INTEL_READ_ID); 522188156Ssam *id = ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(4)))<<48 | 523188156Ssam ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(5)))<<32 | 524188156Ssam ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(6)))<<16 | 525188156Ssam ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(7))); 526188156Ssam cfi_write(sc, 0, CFI_BCS_READ_ARRAY); 527188156Ssam return 0; 528188156Ssam} 529188156Ssam 530188156Ssam/* 531188156Ssam * Write the User/OEM 64-bit segment of the PR. 532188267Ssam * XXX should allow writing individual words/bytes 533188156Ssam */ 534188156Ssamint 535188156Ssamcfi_intel_set_oem_pr(struct cfi_softc *sc, uint64_t id) 536188156Ssam{ 537188267Ssam#ifdef CFI_ARMEDANDDANGEROUS 538188156Ssam register_t intr; 539188156Ssam int i, error; 540188267Ssam#endif 541188156Ssam 542188156Ssam if (sc->sc_cmdset != CFI_VEND_INTEL_ECS) 543188156Ssam return EOPNOTSUPP; 544188156Ssam KASSERT(sc->sc_width == 2, ("sc_width %d", sc->sc_width)); 545188156Ssam 546188267Ssam#ifdef CFI_ARMEDANDDANGEROUS 547188156Ssam for (i = 7; i >= 4; i--, id >>= 16) { 548188156Ssam intr = intr_disable(); 549188156Ssam cfi_write(sc, 0, CFI_INTEL_PP_SETUP); 550188156Ssam cfi_put16(sc, CFI_INTEL_PR(i), id&0xffff); 551188156Ssam intr_restore(intr); 552188156Ssam error = cfi_wait_ready(sc, CFI_BCS_READ_STATUS, 553188156Ssam sc->sc_write_timeout); 554188156Ssam if (error) 555188156Ssam break; 556188156Ssam } 557188156Ssam cfi_write(sc, 0, CFI_BCS_READ_ARRAY); 558188156Ssam return error; 559188267Ssam#else 560188267Ssam device_printf(sc->sc_dev, "%s: OEM PR not set, " 561188267Ssam "CFI_ARMEDANDDANGEROUS not configured\n", __func__); 562188267Ssam return ENXIO; 563188267Ssam#endif 564188156Ssam} 565188156Ssam 566188156Ssam/* 567188156Ssam * Read the contents of the Protection Lock Register. 568188156Ssam */ 569188156Ssamint 570188156Ssamcfi_intel_get_plr(struct cfi_softc *sc, uint32_t *plr) 571188156Ssam{ 572188156Ssam if (sc->sc_cmdset != CFI_VEND_INTEL_ECS) 573188156Ssam return EOPNOTSUPP; 574188156Ssam KASSERT(sc->sc_width == 2, ("sc_width %d", sc->sc_width)); 575188156Ssam 576188156Ssam cfi_write(sc, 0, CFI_INTEL_READ_ID); 577188156Ssam *plr = cfi_get16(sc, CFI_INTEL_PLR); 578188156Ssam cfi_write(sc, 0, CFI_BCS_READ_ARRAY); 579188156Ssam return 0; 580188156Ssam} 581188156Ssam 582188156Ssam/* 583188156Ssam * Write the Protection Lock Register to lock down the 584188156Ssam * user-settable segment of the Protection Register. 585188156Ssam * NOTE: this operation is not reversible. 586188156Ssam */ 587188156Ssamint 588188156Ssamcfi_intel_set_plr(struct cfi_softc *sc) 589188156Ssam{ 590188156Ssam#ifdef CFI_ARMEDANDDANGEROUS 591188156Ssam register_t intr; 592188268Ssam int error; 593188156Ssam#endif 594188156Ssam if (sc->sc_cmdset != CFI_VEND_INTEL_ECS) 595188156Ssam return EOPNOTSUPP; 596188156Ssam KASSERT(sc->sc_width == 2, ("sc_width %d", sc->sc_width)); 597188156Ssam 598188156Ssam#ifdef CFI_ARMEDANDDANGEROUS 599188156Ssam /* worthy of console msg */ 600188156Ssam device_printf(sc->sc_dev, "set PLR\n"); 601188156Ssam intr = intr_disable(); 602188156Ssam cfi_write(sc, 0, CFI_INTEL_PP_SETUP); 603188156Ssam cfi_put16(sc, CFI_INTEL_PLR, 0xFFFD); 604188156Ssam intr_restore(intr); 605188156Ssam error = cfi_wait_ready(sc, CFI_BCS_READ_STATUS, sc->sc_write_timeout); 606188156Ssam cfi_write(sc, 0, CFI_BCS_READ_ARRAY); 607188268Ssam return error; 608188156Ssam#else 609188156Ssam device_printf(sc->sc_dev, "%s: PLR not set, " 610188156Ssam "CFI_ARMEDANDDANGEROUS not configured\n", __func__); 611188268Ssam return ENXIO; 612188156Ssam#endif 613188156Ssam} 614188156Ssam#endif /* CFI_SUPPORT_STRATAFLASH */ 615