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