cfi_core.c revision 193936
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 193936 2009-06-10 17:41:24Z imp $"); 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 288193936Simp device_add_child(dev, "cfid", -1); 289189606Ssam bus_generic_attach(dev); 290189606Ssam 291184251Smarcel return (0); 292184251Smarcel} 293184251Smarcel 294184251Smarcelint 295184251Smarcelcfi_detach(device_t dev) 296184251Smarcel{ 297184251Smarcel struct cfi_softc *sc; 298184251Smarcel 299184251Smarcel sc = device_get_softc(dev); 300184251Smarcel 301184251Smarcel destroy_dev(sc->sc_nod); 302184251Smarcel free(sc->sc_region, M_TEMP); 303184251Smarcel bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_rid, sc->sc_res); 304184251Smarcel return (0); 305184251Smarcel} 306184251Smarcel 307184251Smarcelstatic int 308188156Ssamcfi_wait_ready(struct cfi_softc *sc, u_int ofs, u_int timeout) 309184251Smarcel{ 310184251Smarcel int done, error; 311188156Ssam uint32_t st0 = 0, st = 0; 312184251Smarcel 313184251Smarcel done = 0; 314184251Smarcel error = 0; 315184251Smarcel timeout *= 10; 316184251Smarcel while (!done && !error && timeout) { 317184251Smarcel DELAY(100); 318184251Smarcel timeout--; 319184251Smarcel 320184251Smarcel switch (sc->sc_cmdset) { 321184251Smarcel case CFI_VEND_INTEL_ECS: 322184251Smarcel case CFI_VEND_INTEL_SCS: 323188156Ssam st = cfi_read(sc, ofs); 324188156Ssam done = (st & CFI_INTEL_STATUS_WSMS); 325184251Smarcel if (done) { 326188156Ssam /* NB: bit 0 is reserved */ 327188156Ssam st &= ~(CFI_INTEL_XSTATUS_RSVD | 328188156Ssam CFI_INTEL_STATUS_WSMS | 329188156Ssam CFI_INTEL_STATUS_RSVD); 330188156Ssam if (st & CFI_INTEL_STATUS_DPS) 331184251Smarcel error = EPERM; 332188156Ssam else if (st & CFI_INTEL_STATUS_PSLBS) 333184251Smarcel error = EIO; 334188156Ssam else if (st & CFI_INTEL_STATUS_ECLBS) 335184251Smarcel error = ENXIO; 336188156Ssam else if (st) 337188156Ssam error = EACCES; 338184251Smarcel } 339184251Smarcel break; 340184251Smarcel case CFI_VEND_AMD_SCS: 341184251Smarcel case CFI_VEND_AMD_ECS: 342188156Ssam st0 = cfi_read(sc, ofs); 343188156Ssam st = cfi_read(sc, ofs); 344184251Smarcel done = ((st & 0x40) == (st0 & 0x40)) ? 1 : 0; 345184251Smarcel break; 346184251Smarcel } 347184251Smarcel } 348184251Smarcel if (!done && !error) 349184251Smarcel error = ETIMEDOUT; 350184251Smarcel if (error) 351188156Ssam printf("\nerror=%d (st 0x%x st0 0x%x)\n", error, st, st0); 352184251Smarcel return (error); 353184251Smarcel} 354184251Smarcel 355184251Smarcelint 356184251Smarcelcfi_write_block(struct cfi_softc *sc) 357184251Smarcel{ 358184251Smarcel union { 359184251Smarcel uint8_t *x8; 360184251Smarcel uint16_t *x16; 361184251Smarcel uint32_t *x32; 362184251Smarcel } ptr; 363184251Smarcel register_t intr; 364184251Smarcel int error, i; 365184251Smarcel 366184251Smarcel /* Erase the block. */ 367184251Smarcel switch (sc->sc_cmdset) { 368184251Smarcel case CFI_VEND_INTEL_ECS: 369184251Smarcel case CFI_VEND_INTEL_SCS: 370184251Smarcel cfi_write(sc, sc->sc_wrofs, CFI_BCS_BLOCK_ERASE); 371184251Smarcel cfi_write(sc, sc->sc_wrofs, CFI_BCS_CONFIRM); 372184251Smarcel break; 373184251Smarcel case CFI_VEND_AMD_SCS: 374184251Smarcel case CFI_VEND_AMD_ECS: 375184251Smarcel cfi_amd_write(sc, sc->sc_wrofs, AMD_ADDR_START, 376184251Smarcel CFI_AMD_ERASE_SECTOR); 377184251Smarcel cfi_amd_write(sc, sc->sc_wrofs, 0, CFI_AMD_BLOCK_ERASE); 378184251Smarcel break; 379184251Smarcel default: 380184251Smarcel /* Better safe than sorry... */ 381184251Smarcel return (ENODEV); 382184251Smarcel } 383188156Ssam error = cfi_wait_ready(sc, sc->sc_wrofs, sc->sc_erase_timeout); 384184251Smarcel if (error) 385184251Smarcel goto out; 386184251Smarcel 387184251Smarcel /* Write the block. */ 388184251Smarcel ptr.x8 = sc->sc_wrbuf; 389184251Smarcel for (i = 0; i < sc->sc_wrbufsz; i += sc->sc_width) { 390184251Smarcel 391184251Smarcel /* 392184251Smarcel * Make sure the command to start a write and the 393184251Smarcel * actual write happens back-to-back without any 394184251Smarcel * excessive delays. 395184251Smarcel */ 396184251Smarcel intr = intr_disable(); 397184251Smarcel 398184251Smarcel switch (sc->sc_cmdset) { 399184251Smarcel case CFI_VEND_INTEL_ECS: 400184251Smarcel case CFI_VEND_INTEL_SCS: 401184251Smarcel cfi_write(sc, sc->sc_wrofs + i, CFI_BCS_PROGRAM); 402184251Smarcel break; 403184251Smarcel case CFI_VEND_AMD_SCS: 404184251Smarcel case CFI_VEND_AMD_ECS: 405184251Smarcel cfi_amd_write(sc, 0, AMD_ADDR_START, CFI_AMD_PROGRAM); 406184251Smarcel break; 407184251Smarcel } 408184251Smarcel switch (sc->sc_width) { 409184251Smarcel case 1: 410184251Smarcel bus_space_write_1(sc->sc_tag, sc->sc_handle, 411184251Smarcel sc->sc_wrofs + i, *(ptr.x8)++); 412184251Smarcel break; 413184251Smarcel case 2: 414184251Smarcel bus_space_write_2(sc->sc_tag, sc->sc_handle, 415184251Smarcel sc->sc_wrofs + i, *(ptr.x16)++); 416184251Smarcel break; 417184251Smarcel case 4: 418184251Smarcel bus_space_write_4(sc->sc_tag, sc->sc_handle, 419184251Smarcel sc->sc_wrofs + i, *(ptr.x32)++); 420184251Smarcel break; 421184251Smarcel } 422184251Smarcel 423184251Smarcel intr_restore(intr); 424184251Smarcel 425188156Ssam error = cfi_wait_ready(sc, sc->sc_wrofs, sc->sc_write_timeout); 426184251Smarcel if (error) 427184251Smarcel goto out; 428184251Smarcel } 429184251Smarcel 430184251Smarcel /* error is 0. */ 431184251Smarcel 432184251Smarcel out: 433184251Smarcel cfi_write(sc, 0, CFI_BCS_READ_ARRAY); 434184251Smarcel return (error); 435184251Smarcel} 436188156Ssam 437188156Ssam#ifdef CFI_SUPPORT_STRATAFLASH 438188156Ssam/* 439188156Ssam * Intel StrataFlash Protection Register Support. 440188156Ssam * 441188156Ssam * The memory includes a 128-bit Protection Register that can be 442188156Ssam * used for security. There are two 64-bit segments; one is programmed 443188156Ssam * at the factory with a unique 64-bit number which is immutable. 444188156Ssam * The other segment is left blank for User (OEM) programming. 445188267Ssam * The User/OEM segment is One Time Programmable (OTP). It can also 446188332Ssam * be locked to prevent any further writes by setting bit 0 of the 447188267Ssam * Protection Lock Register (PLR). The PLR can written only once. 448188156Ssam */ 449188156Ssam 450188156Ssamstatic uint16_t 451188156Ssamcfi_get16(struct cfi_softc *sc, int off) 452188156Ssam{ 453188156Ssam uint16_t v = bus_space_read_2(sc->sc_tag, sc->sc_handle, off<<1); 454188156Ssam return v; 455188156Ssam} 456188156Ssam 457188268Ssam#ifdef CFI_ARMEDANDDANGEROUS 458188156Ssamstatic void 459188156Ssamcfi_put16(struct cfi_softc *sc, int off, uint16_t v) 460188156Ssam{ 461188156Ssam bus_space_write_2(sc->sc_tag, sc->sc_handle, off<<1, v); 462188156Ssam} 463188268Ssam#endif 464188156Ssam 465188156Ssam/* 466188156Ssam * Read the factory-defined 64-bit segment of the PR. 467188156Ssam */ 468188156Ssamint 469188156Ssamcfi_intel_get_factory_pr(struct cfi_softc *sc, uint64_t *id) 470188156Ssam{ 471188156Ssam if (sc->sc_cmdset != CFI_VEND_INTEL_ECS) 472188156Ssam return EOPNOTSUPP; 473188156Ssam KASSERT(sc->sc_width == 2, ("sc_width %d", sc->sc_width)); 474188156Ssam 475188156Ssam cfi_write(sc, 0, CFI_INTEL_READ_ID); 476188156Ssam *id = ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(0)))<<48 | 477188156Ssam ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(1)))<<32 | 478188156Ssam ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(2)))<<16 | 479188156Ssam ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(3))); 480188156Ssam cfi_write(sc, 0, CFI_BCS_READ_ARRAY); 481188156Ssam return 0; 482188156Ssam} 483188156Ssam 484188156Ssam/* 485188156Ssam * Read the User/OEM 64-bit segment of the PR. 486188156Ssam */ 487188156Ssamint 488188156Ssamcfi_intel_get_oem_pr(struct cfi_softc *sc, uint64_t *id) 489188156Ssam{ 490188156Ssam if (sc->sc_cmdset != CFI_VEND_INTEL_ECS) 491188156Ssam return EOPNOTSUPP; 492188156Ssam KASSERT(sc->sc_width == 2, ("sc_width %d", sc->sc_width)); 493188156Ssam 494188156Ssam cfi_write(sc, 0, CFI_INTEL_READ_ID); 495188156Ssam *id = ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(4)))<<48 | 496188156Ssam ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(5)))<<32 | 497188156Ssam ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(6)))<<16 | 498188156Ssam ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(7))); 499188156Ssam cfi_write(sc, 0, CFI_BCS_READ_ARRAY); 500188156Ssam return 0; 501188156Ssam} 502188156Ssam 503188156Ssam/* 504188156Ssam * Write the User/OEM 64-bit segment of the PR. 505188267Ssam * XXX should allow writing individual words/bytes 506188156Ssam */ 507188156Ssamint 508188156Ssamcfi_intel_set_oem_pr(struct cfi_softc *sc, uint64_t id) 509188156Ssam{ 510188267Ssam#ifdef CFI_ARMEDANDDANGEROUS 511188156Ssam register_t intr; 512188156Ssam int i, error; 513188267Ssam#endif 514188156Ssam 515188156Ssam if (sc->sc_cmdset != CFI_VEND_INTEL_ECS) 516188156Ssam return EOPNOTSUPP; 517188156Ssam KASSERT(sc->sc_width == 2, ("sc_width %d", sc->sc_width)); 518188156Ssam 519188267Ssam#ifdef CFI_ARMEDANDDANGEROUS 520188156Ssam for (i = 7; i >= 4; i--, id >>= 16) { 521188156Ssam intr = intr_disable(); 522188156Ssam cfi_write(sc, 0, CFI_INTEL_PP_SETUP); 523188156Ssam cfi_put16(sc, CFI_INTEL_PR(i), id&0xffff); 524188156Ssam intr_restore(intr); 525188156Ssam error = cfi_wait_ready(sc, CFI_BCS_READ_STATUS, 526188156Ssam sc->sc_write_timeout); 527188156Ssam if (error) 528188156Ssam break; 529188156Ssam } 530188156Ssam cfi_write(sc, 0, CFI_BCS_READ_ARRAY); 531188156Ssam return error; 532188267Ssam#else 533188267Ssam device_printf(sc->sc_dev, "%s: OEM PR not set, " 534188267Ssam "CFI_ARMEDANDDANGEROUS not configured\n", __func__); 535188267Ssam return ENXIO; 536188267Ssam#endif 537188156Ssam} 538188156Ssam 539188156Ssam/* 540188156Ssam * Read the contents of the Protection Lock Register. 541188156Ssam */ 542188156Ssamint 543188156Ssamcfi_intel_get_plr(struct cfi_softc *sc, uint32_t *plr) 544188156Ssam{ 545188156Ssam if (sc->sc_cmdset != CFI_VEND_INTEL_ECS) 546188156Ssam return EOPNOTSUPP; 547188156Ssam KASSERT(sc->sc_width == 2, ("sc_width %d", sc->sc_width)); 548188156Ssam 549188156Ssam cfi_write(sc, 0, CFI_INTEL_READ_ID); 550188156Ssam *plr = cfi_get16(sc, CFI_INTEL_PLR); 551188156Ssam cfi_write(sc, 0, CFI_BCS_READ_ARRAY); 552188156Ssam return 0; 553188156Ssam} 554188156Ssam 555188156Ssam/* 556188156Ssam * Write the Protection Lock Register to lock down the 557188156Ssam * user-settable segment of the Protection Register. 558188156Ssam * NOTE: this operation is not reversible. 559188156Ssam */ 560188156Ssamint 561188156Ssamcfi_intel_set_plr(struct cfi_softc *sc) 562188156Ssam{ 563188156Ssam#ifdef CFI_ARMEDANDDANGEROUS 564188156Ssam register_t intr; 565188268Ssam int error; 566188156Ssam#endif 567188156Ssam if (sc->sc_cmdset != CFI_VEND_INTEL_ECS) 568188156Ssam return EOPNOTSUPP; 569188156Ssam KASSERT(sc->sc_width == 2, ("sc_width %d", sc->sc_width)); 570188156Ssam 571188156Ssam#ifdef CFI_ARMEDANDDANGEROUS 572188156Ssam /* worthy of console msg */ 573188156Ssam device_printf(sc->sc_dev, "set PLR\n"); 574188156Ssam intr = intr_disable(); 575188156Ssam cfi_write(sc, 0, CFI_INTEL_PP_SETUP); 576188156Ssam cfi_put16(sc, CFI_INTEL_PLR, 0xFFFD); 577188156Ssam intr_restore(intr); 578188156Ssam error = cfi_wait_ready(sc, CFI_BCS_READ_STATUS, sc->sc_write_timeout); 579188156Ssam cfi_write(sc, 0, CFI_BCS_READ_ARRAY); 580188268Ssam return error; 581188156Ssam#else 582188156Ssam device_printf(sc->sc_dev, "%s: PLR not set, " 583188156Ssam "CFI_ARMEDANDDANGEROUS not configured\n", __func__); 584188268Ssam return ENXIO; 585188156Ssam#endif 586188156Ssam} 587188156Ssam#endif /* CFI_SUPPORT_STRATAFLASH */ 588