cfi_core.c revision 189606
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 189606 2009-03-09 23:16:02Z sam $"); 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> 39184251Smarcel#include <sys/kernel.h> 40184251Smarcel#include <sys/malloc.h> 41184251Smarcel#include <sys/module.h> 42184251Smarcel#include <sys/rman.h> 43184251Smarcel#include <sys/sysctl.h> 44184251Smarcel 45184251Smarcel#include <machine/bus.h> 46184251Smarcel 47184251Smarcel#include <dev/cfi/cfi_reg.h> 48184251Smarcel#include <dev/cfi/cfi_var.h> 49184251Smarcel 50184251Smarcelextern struct cdevsw cfi_cdevsw; 51184251Smarcel 52184251Smarcelchar cfi_driver_name[] = "cfi"; 53184251Smarceldevclass_t cfi_devclass; 54189606Ssamdevclass_t cfi_diskclass; 55184251Smarcel 56184251Smarceluint32_t 57184251Smarcelcfi_read(struct cfi_softc *sc, u_int ofs) 58184251Smarcel{ 59184251Smarcel uint32_t val; 60184251Smarcel 61184251Smarcel ofs &= ~(sc->sc_width - 1); 62184251Smarcel switch (sc->sc_width) { 63184251Smarcel case 1: 64184251Smarcel val = bus_space_read_1(sc->sc_tag, sc->sc_handle, ofs); 65184251Smarcel break; 66184251Smarcel case 2: 67184251Smarcel val = bus_space_read_2(sc->sc_tag, sc->sc_handle, ofs); 68184251Smarcel break; 69184251Smarcel case 4: 70184251Smarcel val = bus_space_read_4(sc->sc_tag, sc->sc_handle, ofs); 71184251Smarcel break; 72184251Smarcel default: 73184251Smarcel val = ~0; 74184251Smarcel break; 75184251Smarcel } 76184251Smarcel return (val); 77184251Smarcel} 78184251Smarcel 79184251Smarcelstatic void 80184251Smarcelcfi_write(struct cfi_softc *sc, u_int ofs, u_int val) 81184251Smarcel{ 82184251Smarcel 83184251Smarcel ofs &= ~(sc->sc_width - 1); 84184251Smarcel switch (sc->sc_width) { 85184251Smarcel case 1: 86184251Smarcel bus_space_write_1(sc->sc_tag, sc->sc_handle, ofs, val); 87184251Smarcel break; 88184251Smarcel case 2: 89184251Smarcel bus_space_write_2(sc->sc_tag, sc->sc_handle, ofs, val); 90184251Smarcel break; 91184251Smarcel case 4: 92184251Smarcel bus_space_write_4(sc->sc_tag, sc->sc_handle, ofs, val); 93184251Smarcel break; 94184251Smarcel } 95184251Smarcel} 96184251Smarcel 97184251Smarceluint8_t 98184251Smarcelcfi_read_qry(struct cfi_softc *sc, u_int ofs) 99184251Smarcel{ 100184251Smarcel uint8_t val; 101184251Smarcel 102184251Smarcel cfi_write(sc, CFI_QRY_CMD_ADDR * sc->sc_width, CFI_QRY_CMD_DATA); 103184251Smarcel val = cfi_read(sc, ofs * sc->sc_width); 104184251Smarcel cfi_write(sc, 0, CFI_BCS_READ_ARRAY); 105184251Smarcel return (val); 106184251Smarcel} 107184251Smarcel 108184251Smarcelstatic void 109184251Smarcelcfi_amd_write(struct cfi_softc *sc, u_int ofs, u_int addr, u_int data) 110184251Smarcel{ 111184251Smarcel 112184251Smarcel cfi_write(sc, ofs + AMD_ADDR_START, CFI_AMD_UNLOCK); 113184251Smarcel cfi_write(sc, ofs + AMD_ADDR_ACK, CFI_AMD_UNLOCK_ACK); 114184251Smarcel cfi_write(sc, ofs + addr, data); 115184251Smarcel} 116184251Smarcel 117184251Smarcelstatic char * 118184251Smarcelcfi_fmtsize(uint32_t sz) 119184251Smarcel{ 120184251Smarcel static char buf[8]; 121184251Smarcel static const char *sfx[] = { "", "K", "M", "G" }; 122184251Smarcel int sfxidx; 123184251Smarcel 124184251Smarcel sfxidx = 0; 125184251Smarcel while (sfxidx < 3 && sz > 1023) { 126184251Smarcel sz /= 1024; 127184251Smarcel sfxidx++; 128184251Smarcel } 129184251Smarcel 130184251Smarcel sprintf(buf, "%u%sB", sz, sfx[sfxidx]); 131184251Smarcel return (buf); 132184251Smarcel} 133184251Smarcel 134184251Smarcelint 135184251Smarcelcfi_probe(device_t dev) 136184251Smarcel{ 137184251Smarcel char desc[80]; 138184251Smarcel struct cfi_softc *sc; 139184251Smarcel char *vend_str; 140184251Smarcel int error; 141184251Smarcel uint16_t iface, vend; 142184251Smarcel 143184251Smarcel sc = device_get_softc(dev); 144184251Smarcel sc->sc_dev = dev; 145184251Smarcel 146184251Smarcel sc->sc_rid = 0; 147184251Smarcel sc->sc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->sc_rid, 148184251Smarcel RF_ACTIVE); 149184251Smarcel if (sc->sc_res == NULL) 150184251Smarcel return (ENXIO); 151184251Smarcel 152184251Smarcel sc->sc_tag = rman_get_bustag(sc->sc_res); 153184251Smarcel sc->sc_handle = rman_get_bushandle(sc->sc_res); 154184251Smarcel 155188087Ssam if (sc->sc_width == 0) { 156188087Ssam sc->sc_width = 1; 157188087Ssam while (sc->sc_width <= 4) { 158188087Ssam if (cfi_read_qry(sc, CFI_QRY_IDENT) == 'Q') 159188087Ssam break; 160188087Ssam sc->sc_width <<= 1; 161188087Ssam } 162188087Ssam } else if (cfi_read_qry(sc, CFI_QRY_IDENT) != 'Q') { 163188087Ssam error = ENXIO; 164188087Ssam goto out; 165184251Smarcel } 166184251Smarcel if (sc->sc_width > 4) { 167184251Smarcel error = ENXIO; 168184251Smarcel goto out; 169184251Smarcel } 170184251Smarcel 171184251Smarcel /* We got a Q. Check if we also have the R and the Y. */ 172184251Smarcel if (cfi_read_qry(sc, CFI_QRY_IDENT + 1) != 'R' || 173184251Smarcel cfi_read_qry(sc, CFI_QRY_IDENT + 2) != 'Y') { 174184251Smarcel error = ENXIO; 175184251Smarcel goto out; 176184251Smarcel } 177184251Smarcel 178184251Smarcel /* Get the vendor and command set. */ 179184251Smarcel vend = cfi_read_qry(sc, CFI_QRY_VEND) | 180184251Smarcel (cfi_read_qry(sc, CFI_QRY_VEND + 1) << 8); 181184251Smarcel 182184251Smarcel sc->sc_cmdset = vend; 183184251Smarcel 184184251Smarcel switch (vend) { 185184251Smarcel case CFI_VEND_AMD_ECS: 186184251Smarcel case CFI_VEND_AMD_SCS: 187184251Smarcel vend_str = "AMD/Fujitsu"; 188184251Smarcel break; 189184251Smarcel case CFI_VEND_INTEL_ECS: 190184251Smarcel vend_str = "Intel/Sharp"; 191184251Smarcel break; 192184251Smarcel case CFI_VEND_INTEL_SCS: 193184251Smarcel vend_str = "Intel"; 194184251Smarcel break; 195184251Smarcel case CFI_VEND_MITSUBISHI_ECS: 196184251Smarcel case CFI_VEND_MITSUBISHI_SCS: 197184251Smarcel vend_str = "Mitsubishi"; 198184251Smarcel break; 199184251Smarcel default: 200184251Smarcel vend_str = "Unknown vendor"; 201184251Smarcel break; 202184251Smarcel } 203184251Smarcel 204184251Smarcel /* Get the device size. */ 205184251Smarcel sc->sc_size = 1U << cfi_read_qry(sc, CFI_QRY_SIZE); 206184251Smarcel 207184251Smarcel /* Sanity-check the I/F */ 208184251Smarcel iface = cfi_read_qry(sc, CFI_QRY_IFACE) | 209184251Smarcel (cfi_read_qry(sc, CFI_QRY_IFACE + 1) << 8); 210184251Smarcel 211184251Smarcel /* 212184251Smarcel * Adding 1 to iface will give us a bit-wise "switch" 213184251Smarcel * that allows us to test for the interface width by 214184251Smarcel * testing a single bit. 215184251Smarcel */ 216184251Smarcel iface++; 217184251Smarcel 218184251Smarcel error = (iface & sc->sc_width) ? 0 : EINVAL; 219184251Smarcel if (error) 220184251Smarcel goto out; 221184251Smarcel 222184251Smarcel snprintf(desc, sizeof(desc), "%s - %s", vend_str, 223184251Smarcel cfi_fmtsize(sc->sc_size)); 224184251Smarcel device_set_desc_copy(dev, desc); 225184251Smarcel 226184251Smarcel out: 227184251Smarcel bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_rid, sc->sc_res); 228184251Smarcel return (error); 229184251Smarcel} 230184251Smarcel 231184251Smarcelint 232184251Smarcelcfi_attach(device_t dev) 233184251Smarcel{ 234184251Smarcel struct cfi_softc *sc; 235184251Smarcel u_int blksz, blocks; 236184251Smarcel u_int r, u; 237184251Smarcel 238184251Smarcel sc = device_get_softc(dev); 239184251Smarcel sc->sc_dev = dev; 240184251Smarcel 241184251Smarcel sc->sc_rid = 0; 242184251Smarcel sc->sc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->sc_rid, 243184251Smarcel RF_ACTIVE); 244184251Smarcel if (sc->sc_res == NULL) 245184251Smarcel return (ENXIO); 246184251Smarcel 247184251Smarcel sc->sc_tag = rman_get_bustag(sc->sc_res); 248184251Smarcel sc->sc_handle = rman_get_bushandle(sc->sc_res); 249184251Smarcel 250184251Smarcel /* Get time-out values for erase and write. */ 251184251Smarcel sc->sc_write_timeout = 1 << cfi_read_qry(sc, CFI_QRY_TTO_WRITE); 252184251Smarcel sc->sc_erase_timeout = 1 << cfi_read_qry(sc, CFI_QRY_TTO_ERASE); 253184251Smarcel sc->sc_write_timeout *= 1 << cfi_read_qry(sc, CFI_QRY_MTO_WRITE); 254184251Smarcel sc->sc_erase_timeout *= 1 << cfi_read_qry(sc, CFI_QRY_MTO_ERASE); 255184251Smarcel 256184251Smarcel /* Get erase regions. */ 257184251Smarcel sc->sc_regions = cfi_read_qry(sc, CFI_QRY_NREGIONS); 258184251Smarcel sc->sc_region = malloc(sc->sc_regions * sizeof(struct cfi_region), 259184251Smarcel M_TEMP, M_WAITOK | M_ZERO); 260184251Smarcel for (r = 0; r < sc->sc_regions; r++) { 261184251Smarcel blocks = cfi_read_qry(sc, CFI_QRY_REGION(r)) | 262184251Smarcel (cfi_read_qry(sc, CFI_QRY_REGION(r) + 1) << 8); 263184251Smarcel sc->sc_region[r].r_blocks = blocks + 1; 264184251Smarcel 265184251Smarcel blksz = cfi_read_qry(sc, CFI_QRY_REGION(r) + 2) | 266184251Smarcel (cfi_read_qry(sc, CFI_QRY_REGION(r) + 3) << 8); 267184251Smarcel sc->sc_region[r].r_blksz = (blksz == 0) ? 128 : 268184251Smarcel blksz * 256; 269184251Smarcel } 270184251Smarcel 271184251Smarcel /* Reset the device to a default state. */ 272184251Smarcel cfi_write(sc, 0, CFI_BCS_CLEAR_STATUS); 273184251Smarcel 274184251Smarcel if (bootverbose) { 275184251Smarcel device_printf(dev, "["); 276184251Smarcel for (r = 0; r < sc->sc_regions; r++) { 277184251Smarcel printf("%ux%s%s", sc->sc_region[r].r_blocks, 278184251Smarcel cfi_fmtsize(sc->sc_region[r].r_blksz), 279184251Smarcel (r == sc->sc_regions - 1) ? "]\n" : ","); 280184251Smarcel } 281184251Smarcel } 282184251Smarcel 283184251Smarcel u = device_get_unit(dev); 284184251Smarcel sc->sc_nod = make_dev(&cfi_cdevsw, u, UID_ROOT, GID_WHEEL, 0600, 285184251Smarcel "%s%u", cfi_driver_name, u); 286184251Smarcel sc->sc_nod->si_drv1 = sc; 287184251Smarcel 288189606Ssam device_add_child(dev, "cfid", 289189606Ssam devclass_find_free_unit(cfi_diskclass, 0)); 290189606Ssam bus_generic_attach(dev); 291189606Ssam 292184251Smarcel return (0); 293184251Smarcel} 294184251Smarcel 295184251Smarcelint 296184251Smarcelcfi_detach(device_t dev) 297184251Smarcel{ 298184251Smarcel struct cfi_softc *sc; 299184251Smarcel 300184251Smarcel sc = device_get_softc(dev); 301184251Smarcel 302184251Smarcel destroy_dev(sc->sc_nod); 303184251Smarcel free(sc->sc_region, M_TEMP); 304184251Smarcel bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_rid, sc->sc_res); 305184251Smarcel return (0); 306184251Smarcel} 307184251Smarcel 308184251Smarcelstatic int 309188156Ssamcfi_wait_ready(struct cfi_softc *sc, u_int ofs, u_int timeout) 310184251Smarcel{ 311184251Smarcel int done, error; 312188156Ssam uint32_t st0 = 0, st = 0; 313184251Smarcel 314184251Smarcel done = 0; 315184251Smarcel error = 0; 316184251Smarcel timeout *= 10; 317184251Smarcel while (!done && !error && timeout) { 318184251Smarcel DELAY(100); 319184251Smarcel timeout--; 320184251Smarcel 321184251Smarcel switch (sc->sc_cmdset) { 322184251Smarcel case CFI_VEND_INTEL_ECS: 323184251Smarcel case CFI_VEND_INTEL_SCS: 324188156Ssam st = cfi_read(sc, ofs); 325188156Ssam done = (st & CFI_INTEL_STATUS_WSMS); 326184251Smarcel if (done) { 327188156Ssam /* NB: bit 0 is reserved */ 328188156Ssam st &= ~(CFI_INTEL_XSTATUS_RSVD | 329188156Ssam CFI_INTEL_STATUS_WSMS | 330188156Ssam CFI_INTEL_STATUS_RSVD); 331188156Ssam if (st & CFI_INTEL_STATUS_DPS) 332184251Smarcel error = EPERM; 333188156Ssam else if (st & CFI_INTEL_STATUS_PSLBS) 334184251Smarcel error = EIO; 335188156Ssam else if (st & CFI_INTEL_STATUS_ECLBS) 336184251Smarcel error = ENXIO; 337188156Ssam else if (st) 338188156Ssam error = EACCES; 339184251Smarcel } 340184251Smarcel break; 341184251Smarcel case CFI_VEND_AMD_SCS: 342184251Smarcel case CFI_VEND_AMD_ECS: 343188156Ssam st0 = cfi_read(sc, ofs); 344188156Ssam st = cfi_read(sc, ofs); 345184251Smarcel done = ((st & 0x40) == (st0 & 0x40)) ? 1 : 0; 346184251Smarcel break; 347184251Smarcel } 348184251Smarcel } 349184251Smarcel if (!done && !error) 350184251Smarcel error = ETIMEDOUT; 351184251Smarcel if (error) 352188156Ssam printf("\nerror=%d (st 0x%x st0 0x%x)\n", error, st, st0); 353184251Smarcel return (error); 354184251Smarcel} 355184251Smarcel 356184251Smarcelint 357184251Smarcelcfi_write_block(struct cfi_softc *sc) 358184251Smarcel{ 359184251Smarcel union { 360184251Smarcel uint8_t *x8; 361184251Smarcel uint16_t *x16; 362184251Smarcel uint32_t *x32; 363184251Smarcel } ptr; 364184251Smarcel register_t intr; 365184251Smarcel int error, i; 366184251Smarcel 367184251Smarcel /* Erase the block. */ 368184251Smarcel switch (sc->sc_cmdset) { 369184251Smarcel case CFI_VEND_INTEL_ECS: 370184251Smarcel case CFI_VEND_INTEL_SCS: 371184251Smarcel cfi_write(sc, sc->sc_wrofs, CFI_BCS_BLOCK_ERASE); 372184251Smarcel cfi_write(sc, sc->sc_wrofs, CFI_BCS_CONFIRM); 373184251Smarcel break; 374184251Smarcel case CFI_VEND_AMD_SCS: 375184251Smarcel case CFI_VEND_AMD_ECS: 376184251Smarcel cfi_amd_write(sc, sc->sc_wrofs, AMD_ADDR_START, 377184251Smarcel CFI_AMD_ERASE_SECTOR); 378184251Smarcel cfi_amd_write(sc, sc->sc_wrofs, 0, CFI_AMD_BLOCK_ERASE); 379184251Smarcel break; 380184251Smarcel default: 381184251Smarcel /* Better safe than sorry... */ 382184251Smarcel return (ENODEV); 383184251Smarcel } 384188156Ssam error = cfi_wait_ready(sc, sc->sc_wrofs, sc->sc_erase_timeout); 385184251Smarcel if (error) 386184251Smarcel goto out; 387184251Smarcel 388184251Smarcel /* Write the block. */ 389184251Smarcel ptr.x8 = sc->sc_wrbuf; 390184251Smarcel for (i = 0; i < sc->sc_wrbufsz; i += sc->sc_width) { 391184251Smarcel 392184251Smarcel /* 393184251Smarcel * Make sure the command to start a write and the 394184251Smarcel * actual write happens back-to-back without any 395184251Smarcel * excessive delays. 396184251Smarcel */ 397184251Smarcel intr = intr_disable(); 398184251Smarcel 399184251Smarcel switch (sc->sc_cmdset) { 400184251Smarcel case CFI_VEND_INTEL_ECS: 401184251Smarcel case CFI_VEND_INTEL_SCS: 402184251Smarcel cfi_write(sc, sc->sc_wrofs + i, CFI_BCS_PROGRAM); 403184251Smarcel break; 404184251Smarcel case CFI_VEND_AMD_SCS: 405184251Smarcel case CFI_VEND_AMD_ECS: 406184251Smarcel cfi_amd_write(sc, 0, AMD_ADDR_START, CFI_AMD_PROGRAM); 407184251Smarcel break; 408184251Smarcel } 409184251Smarcel switch (sc->sc_width) { 410184251Smarcel case 1: 411184251Smarcel bus_space_write_1(sc->sc_tag, sc->sc_handle, 412184251Smarcel sc->sc_wrofs + i, *(ptr.x8)++); 413184251Smarcel break; 414184251Smarcel case 2: 415184251Smarcel bus_space_write_2(sc->sc_tag, sc->sc_handle, 416184251Smarcel sc->sc_wrofs + i, *(ptr.x16)++); 417184251Smarcel break; 418184251Smarcel case 4: 419184251Smarcel bus_space_write_4(sc->sc_tag, sc->sc_handle, 420184251Smarcel sc->sc_wrofs + i, *(ptr.x32)++); 421184251Smarcel break; 422184251Smarcel } 423184251Smarcel 424184251Smarcel intr_restore(intr); 425184251Smarcel 426188156Ssam error = cfi_wait_ready(sc, sc->sc_wrofs, sc->sc_write_timeout); 427184251Smarcel if (error) 428184251Smarcel goto out; 429184251Smarcel } 430184251Smarcel 431184251Smarcel /* error is 0. */ 432184251Smarcel 433184251Smarcel out: 434184251Smarcel cfi_write(sc, 0, CFI_BCS_READ_ARRAY); 435184251Smarcel return (error); 436184251Smarcel} 437188156Ssam 438188156Ssam#ifdef CFI_SUPPORT_STRATAFLASH 439188156Ssam/* 440188156Ssam * Intel StrataFlash Protection Register Support. 441188156Ssam * 442188156Ssam * The memory includes a 128-bit Protection Register that can be 443188156Ssam * used for security. There are two 64-bit segments; one is programmed 444188156Ssam * at the factory with a unique 64-bit number which is immutable. 445188156Ssam * The other segment is left blank for User (OEM) programming. 446188267Ssam * The User/OEM segment is One Time Programmable (OTP). It can also 447188332Ssam * be locked to prevent any further writes by setting bit 0 of the 448188267Ssam * Protection Lock Register (PLR). The PLR can written only once. 449188156Ssam */ 450188156Ssam 451188156Ssamstatic uint16_t 452188156Ssamcfi_get16(struct cfi_softc *sc, int off) 453188156Ssam{ 454188156Ssam uint16_t v = bus_space_read_2(sc->sc_tag, sc->sc_handle, off<<1); 455188156Ssam return v; 456188156Ssam} 457188156Ssam 458188268Ssam#ifdef CFI_ARMEDANDDANGEROUS 459188156Ssamstatic void 460188156Ssamcfi_put16(struct cfi_softc *sc, int off, uint16_t v) 461188156Ssam{ 462188156Ssam bus_space_write_2(sc->sc_tag, sc->sc_handle, off<<1, v); 463188156Ssam} 464188268Ssam#endif 465188156Ssam 466188156Ssam/* 467188156Ssam * Read the factory-defined 64-bit segment of the PR. 468188156Ssam */ 469188156Ssamint 470188156Ssamcfi_intel_get_factory_pr(struct cfi_softc *sc, uint64_t *id) 471188156Ssam{ 472188156Ssam if (sc->sc_cmdset != CFI_VEND_INTEL_ECS) 473188156Ssam return EOPNOTSUPP; 474188156Ssam KASSERT(sc->sc_width == 2, ("sc_width %d", sc->sc_width)); 475188156Ssam 476188156Ssam cfi_write(sc, 0, CFI_INTEL_READ_ID); 477188156Ssam *id = ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(0)))<<48 | 478188156Ssam ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(1)))<<32 | 479188156Ssam ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(2)))<<16 | 480188156Ssam ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(3))); 481188156Ssam cfi_write(sc, 0, CFI_BCS_READ_ARRAY); 482188156Ssam return 0; 483188156Ssam} 484188156Ssam 485188156Ssam/* 486188156Ssam * Read the User/OEM 64-bit segment of the PR. 487188156Ssam */ 488188156Ssamint 489188156Ssamcfi_intel_get_oem_pr(struct cfi_softc *sc, uint64_t *id) 490188156Ssam{ 491188156Ssam if (sc->sc_cmdset != CFI_VEND_INTEL_ECS) 492188156Ssam return EOPNOTSUPP; 493188156Ssam KASSERT(sc->sc_width == 2, ("sc_width %d", sc->sc_width)); 494188156Ssam 495188156Ssam cfi_write(sc, 0, CFI_INTEL_READ_ID); 496188156Ssam *id = ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(4)))<<48 | 497188156Ssam ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(5)))<<32 | 498188156Ssam ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(6)))<<16 | 499188156Ssam ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(7))); 500188156Ssam cfi_write(sc, 0, CFI_BCS_READ_ARRAY); 501188156Ssam return 0; 502188156Ssam} 503188156Ssam 504188156Ssam/* 505188156Ssam * Write the User/OEM 64-bit segment of the PR. 506188267Ssam * XXX should allow writing individual words/bytes 507188156Ssam */ 508188156Ssamint 509188156Ssamcfi_intel_set_oem_pr(struct cfi_softc *sc, uint64_t id) 510188156Ssam{ 511188267Ssam#ifdef CFI_ARMEDANDDANGEROUS 512188156Ssam register_t intr; 513188156Ssam int i, error; 514188267Ssam#endif 515188156Ssam 516188156Ssam if (sc->sc_cmdset != CFI_VEND_INTEL_ECS) 517188156Ssam return EOPNOTSUPP; 518188156Ssam KASSERT(sc->sc_width == 2, ("sc_width %d", sc->sc_width)); 519188156Ssam 520188267Ssam#ifdef CFI_ARMEDANDDANGEROUS 521188156Ssam for (i = 7; i >= 4; i--, id >>= 16) { 522188156Ssam intr = intr_disable(); 523188156Ssam cfi_write(sc, 0, CFI_INTEL_PP_SETUP); 524188156Ssam cfi_put16(sc, CFI_INTEL_PR(i), id&0xffff); 525188156Ssam intr_restore(intr); 526188156Ssam error = cfi_wait_ready(sc, CFI_BCS_READ_STATUS, 527188156Ssam sc->sc_write_timeout); 528188156Ssam if (error) 529188156Ssam break; 530188156Ssam } 531188156Ssam cfi_write(sc, 0, CFI_BCS_READ_ARRAY); 532188156Ssam return error; 533188267Ssam#else 534188267Ssam device_printf(sc->sc_dev, "%s: OEM PR not set, " 535188267Ssam "CFI_ARMEDANDDANGEROUS not configured\n", __func__); 536188267Ssam return ENXIO; 537188267Ssam#endif 538188156Ssam} 539188156Ssam 540188156Ssam/* 541188156Ssam * Read the contents of the Protection Lock Register. 542188156Ssam */ 543188156Ssamint 544188156Ssamcfi_intel_get_plr(struct cfi_softc *sc, uint32_t *plr) 545188156Ssam{ 546188156Ssam if (sc->sc_cmdset != CFI_VEND_INTEL_ECS) 547188156Ssam return EOPNOTSUPP; 548188156Ssam KASSERT(sc->sc_width == 2, ("sc_width %d", sc->sc_width)); 549188156Ssam 550188156Ssam cfi_write(sc, 0, CFI_INTEL_READ_ID); 551188156Ssam *plr = cfi_get16(sc, CFI_INTEL_PLR); 552188156Ssam cfi_write(sc, 0, CFI_BCS_READ_ARRAY); 553188156Ssam return 0; 554188156Ssam} 555188156Ssam 556188156Ssam/* 557188156Ssam * Write the Protection Lock Register to lock down the 558188156Ssam * user-settable segment of the Protection Register. 559188156Ssam * NOTE: this operation is not reversible. 560188156Ssam */ 561188156Ssamint 562188156Ssamcfi_intel_set_plr(struct cfi_softc *sc) 563188156Ssam{ 564188156Ssam#ifdef CFI_ARMEDANDDANGEROUS 565188156Ssam register_t intr; 566188268Ssam int error; 567188156Ssam#endif 568188156Ssam if (sc->sc_cmdset != CFI_VEND_INTEL_ECS) 569188156Ssam return EOPNOTSUPP; 570188156Ssam KASSERT(sc->sc_width == 2, ("sc_width %d", sc->sc_width)); 571188156Ssam 572188156Ssam#ifdef CFI_ARMEDANDDANGEROUS 573188156Ssam /* worthy of console msg */ 574188156Ssam device_printf(sc->sc_dev, "set PLR\n"); 575188156Ssam intr = intr_disable(); 576188156Ssam cfi_write(sc, 0, CFI_INTEL_PP_SETUP); 577188156Ssam cfi_put16(sc, CFI_INTEL_PLR, 0xFFFD); 578188156Ssam intr_restore(intr); 579188156Ssam error = cfi_wait_ready(sc, CFI_BCS_READ_STATUS, sc->sc_write_timeout); 580188156Ssam cfi_write(sc, 0, CFI_BCS_READ_ARRAY); 581188268Ssam return error; 582188156Ssam#else 583188156Ssam device_printf(sc->sc_dev, "%s: PLR not set, " 584188156Ssam "CFI_ARMEDANDDANGEROUS not configured\n", __func__); 585188268Ssam return ENXIO; 586188156Ssam#endif 587188156Ssam} 588188156Ssam#endif /* CFI_SUPPORT_STRATAFLASH */ 589