cfi_core.c revision 193936
1186545Srwatson/*- 2189279Srwatson * Copyright (c) 2007, Juniper Networks, Inc. 3186545Srwatson * All rights reserved. 4186545Srwatson * 5186545Srwatson * Redistribution and use in source and binary forms, with or without 6186545Srwatson * modification, are permitted provided that the following conditions 7186545Srwatson * are met: 8186545Srwatson * 1. Redistributions of source code must retain the above copyright 9186545Srwatson * notice, this list of conditions and the following disclaimer. 10186545Srwatson * 2. Redistributions in binary form must reproduce the above copyright 11186545Srwatson * notice, this list of conditions and the following disclaimer in the 12186545Srwatson * documentation and/or other materials provided with the distribution. 13186545Srwatson * 3. Neither the name of the author nor the names of any co-contributors 14186545Srwatson * may be used to endorse or promote products derived from this software 15186545Srwatson * without specific prior written permission. 16186545Srwatson * 17186545Srwatson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18186545Srwatson * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19186545Srwatson * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20186545Srwatson * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21186545Srwatson * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 22186545Srwatson * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23186545Srwatson * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 24186545Srwatson * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25186545Srwatson * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26186545Srwatson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27186545Srwatson * SUCH DAMAGE. 28186545Srwatson */ 29189279Srwatson 30186545Srwatson#include <sys/cdefs.h> 31186545Srwatson__FBSDID("$FreeBSD: head/sys/dev/cfi/cfi_core.c 193936 2009-06-10 17:41:24Z imp $"); 32186545Srwatson 33186545Srwatson#include "opt_cfi.h" 34186545Srwatson 35186545Srwatson#include <sys/param.h> 36186545Srwatson#include <sys/systm.h> 37186545Srwatson#include <sys/bus.h> 38186545Srwatson#include <sys/conf.h> 39186545Srwatson#include <sys/kernel.h> 40186545Srwatson#include <sys/malloc.h> 41186545Srwatson#include <sys/module.h> 42186545Srwatson#include <sys/rman.h> 43186545Srwatson#include <sys/sysctl.h> 44186545Srwatson 45186545Srwatson#include <machine/bus.h> 46186545Srwatson 47186545Srwatson#include <dev/cfi/cfi_reg.h> 48186545Srwatson#include <dev/cfi/cfi_var.h> 49186545Srwatson 50186545Srwatsonextern struct cdevsw cfi_cdevsw; 51186545Srwatson 52186545Srwatsonchar cfi_driver_name[] = "cfi"; 53186545Srwatsondevclass_t cfi_devclass; 54186545Srwatsondevclass_t cfi_diskclass; 55189279Srwatson 56186545Srwatsonuint32_t 57186545Srwatsoncfi_read(struct cfi_softc *sc, u_int ofs) 58186545Srwatson{ 59186545Srwatson uint32_t val; 60186545Srwatson 61186545Srwatson ofs &= ~(sc->sc_width - 1); 62186545Srwatson switch (sc->sc_width) { 63186545Srwatson case 1: 64186545Srwatson val = bus_space_read_1(sc->sc_tag, sc->sc_handle, ofs); 65186545Srwatson break; 66186545Srwatson case 2: 67186545Srwatson val = bus_space_read_2(sc->sc_tag, sc->sc_handle, ofs); 68186545Srwatson break; 69186545Srwatson case 4: 70186545Srwatson val = bus_space_read_4(sc->sc_tag, sc->sc_handle, ofs); 71186545Srwatson break; 72186545Srwatson default: 73186545Srwatson val = ~0; 74186545Srwatson break; 75186545Srwatson } 76186545Srwatson return (val); 77186545Srwatson} 78186545Srwatson 79186545Srwatsonstatic void 80186545Srwatsoncfi_write(struct cfi_softc *sc, u_int ofs, u_int val) 81189279Srwatson{ 82189279Srwatson 83189279Srwatson ofs &= ~(sc->sc_width - 1); 84189279Srwatson switch (sc->sc_width) { 85189279Srwatson case 1: 86186545Srwatson bus_space_write_1(sc->sc_tag, sc->sc_handle, ofs, val); 87186545Srwatson break; 88186545Srwatson case 2: 89186545Srwatson bus_space_write_2(sc->sc_tag, sc->sc_handle, ofs, val); 90186545Srwatson break; 91186545Srwatson case 4: 92186545Srwatson bus_space_write_4(sc->sc_tag, sc->sc_handle, ofs, val); 93186545Srwatson break; 94186545Srwatson } 95189279Srwatson} 96189279Srwatson 97189279Srwatsonuint8_t 98189279Srwatsoncfi_read_qry(struct cfi_softc *sc, u_int ofs) 99189279Srwatson{ 100189279Srwatson uint8_t val; 101189279Srwatson 102189279Srwatson cfi_write(sc, CFI_QRY_CMD_ADDR * sc->sc_width, CFI_QRY_CMD_DATA); 103189279Srwatson val = cfi_read(sc, ofs * sc->sc_width); 104189279Srwatson cfi_write(sc, 0, CFI_BCS_READ_ARRAY); 105189279Srwatson return (val); 106189279Srwatson} 107189279Srwatson 108186545Srwatsonstatic void 109186545Srwatsoncfi_amd_write(struct cfi_softc *sc, u_int ofs, u_int addr, u_int data) 110186545Srwatson{ 111186545Srwatson 112186545Srwatson cfi_write(sc, ofs + AMD_ADDR_START, CFI_AMD_UNLOCK); 113186545Srwatson cfi_write(sc, ofs + AMD_ADDR_ACK, CFI_AMD_UNLOCK_ACK); 114186545Srwatson cfi_write(sc, ofs + addr, data); 115186545Srwatson} 116186545Srwatson 117186545Srwatsonstatic char * 118186545Srwatsoncfi_fmtsize(uint32_t sz) 119186545Srwatson{ 120186545Srwatson static char buf[8]; 121186545Srwatson static const char *sfx[] = { "", "K", "M", "G" }; 122186545Srwatson int sfxidx; 123186545Srwatson 124186545Srwatson sfxidx = 0; 125186545Srwatson while (sfxidx < 3 && sz > 1023) { 126186545Srwatson sz /= 1024; 127186545Srwatson sfxidx++; 128189279Srwatson } 129186545Srwatson 130186545Srwatson sprintf(buf, "%u%sB", sz, sfx[sfxidx]); 131186545Srwatson return (buf); 132186545Srwatson} 133186545Srwatson 134186545Srwatsonint 135186545Srwatsoncfi_probe(device_t dev) 136186545Srwatson{ 137186545Srwatson char desc[80]; 138186545Srwatson struct cfi_softc *sc; 139186545Srwatson char *vend_str; 140186545Srwatson int error; 141186545Srwatson uint16_t iface, vend; 142186545Srwatson 143186545Srwatson sc = device_get_softc(dev); 144186545Srwatson sc->sc_dev = dev; 145186545Srwatson 146186545Srwatson sc->sc_rid = 0; 147186545Srwatson sc->sc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->sc_rid, 148186545Srwatson RF_ACTIVE); 149186545Srwatson if (sc->sc_res == NULL) 150186545Srwatson return (ENXIO); 151186545Srwatson 152186545Srwatson sc->sc_tag = rman_get_bustag(sc->sc_res); 153186545Srwatson sc->sc_handle = rman_get_bushandle(sc->sc_res); 154186545Srwatson 155186545Srwatson if (sc->sc_width == 0) { 156186545Srwatson sc->sc_width = 1; 157186545Srwatson while (sc->sc_width <= 4) { 158186545Srwatson if (cfi_read_qry(sc, CFI_QRY_IDENT) == 'Q') 159186545Srwatson break; 160186545Srwatson sc->sc_width <<= 1; 161186545Srwatson } 162186545Srwatson } else if (cfi_read_qry(sc, CFI_QRY_IDENT) != 'Q') { 163186545Srwatson error = ENXIO; 164186545Srwatson goto out; 165186545Srwatson } 166186545Srwatson if (sc->sc_width > 4) { 167186545Srwatson error = ENXIO; 168186545Srwatson goto out; 169186545Srwatson } 170186545Srwatson 171186545Srwatson /* We got a Q. Check if we also have the R and the Y. */ 172186545Srwatson if (cfi_read_qry(sc, CFI_QRY_IDENT + 1) != 'R' || 173186545Srwatson cfi_read_qry(sc, CFI_QRY_IDENT + 2) != 'Y') { 174186545Srwatson error = ENXIO; 175186545Srwatson goto out; 176186545Srwatson } 177186545Srwatson 178186545Srwatson /* Get the vendor and command set. */ 179186545Srwatson vend = cfi_read_qry(sc, CFI_QRY_VEND) | 180186545Srwatson (cfi_read_qry(sc, CFI_QRY_VEND + 1) << 8); 181186545Srwatson 182186545Srwatson sc->sc_cmdset = vend; 183186545Srwatson 184186545Srwatson switch (vend) { 185186545Srwatson case CFI_VEND_AMD_ECS: 186186545Srwatson case CFI_VEND_AMD_SCS: 187189279Srwatson vend_str = "AMD/Fujitsu"; 188189279Srwatson break; 189189279Srwatson case CFI_VEND_INTEL_ECS: 190189279Srwatson vend_str = "Intel/Sharp"; 191189279Srwatson break; 192189279Srwatson case CFI_VEND_INTEL_SCS: 193189279Srwatson vend_str = "Intel"; 194186545Srwatson break; 195186545Srwatson case CFI_VEND_MITSUBISHI_ECS: 196186545Srwatson case CFI_VEND_MITSUBISHI_SCS: 197186545Srwatson vend_str = "Mitsubishi"; 198186545Srwatson break; 199186545Srwatson default: 200186545Srwatson vend_str = "Unknown vendor"; 201186545Srwatson break; 202186545Srwatson } 203186545Srwatson 204186545Srwatson /* Get the device size. */ 205186545Srwatson sc->sc_size = 1U << cfi_read_qry(sc, CFI_QRY_SIZE); 206186545Srwatson 207186545Srwatson /* Sanity-check the I/F */ 208186545Srwatson iface = cfi_read_qry(sc, CFI_QRY_IFACE) | 209186545Srwatson (cfi_read_qry(sc, CFI_QRY_IFACE + 1) << 8); 210186545Srwatson 211186545Srwatson /* 212186545Srwatson * Adding 1 to iface will give us a bit-wise "switch" 213186545Srwatson * that allows us to test for the interface width by 214186545Srwatson * testing a single bit. 215186545Srwatson */ 216186545Srwatson iface++; 217186545Srwatson 218186545Srwatson error = (iface & sc->sc_width) ? 0 : EINVAL; 219186545Srwatson if (error) 220186545Srwatson goto out; 221186545Srwatson 222186545Srwatson snprintf(desc, sizeof(desc), "%s - %s", vend_str, 223186545Srwatson cfi_fmtsize(sc->sc_size)); 224186545Srwatson device_set_desc_copy(dev, desc); 225186545Srwatson 226186545Srwatson out: 227186545Srwatson bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_rid, sc->sc_res); 228186545Srwatson return (error); 229186545Srwatson} 230186545Srwatson 231186545Srwatsonint 232186545Srwatsoncfi_attach(device_t dev) 233186545Srwatson{ 234186545Srwatson struct cfi_softc *sc; 235186545Srwatson u_int blksz, blocks; 236186545Srwatson u_int r, u; 237186545Srwatson 238189279Srwatson sc = device_get_softc(dev); 239189279Srwatson sc->sc_dev = dev; 240186545Srwatson 241186545Srwatson sc->sc_rid = 0; 242186545Srwatson sc->sc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->sc_rid, 243186545Srwatson RF_ACTIVE); 244186545Srwatson if (sc->sc_res == NULL) 245186545Srwatson return (ENXIO); 246186545Srwatson 247186545Srwatson sc->sc_tag = rman_get_bustag(sc->sc_res); 248186545Srwatson sc->sc_handle = rman_get_bushandle(sc->sc_res); 249186545Srwatson 250186545Srwatson /* Get time-out values for erase and write. */ 251186545Srwatson sc->sc_write_timeout = 1 << cfi_read_qry(sc, CFI_QRY_TTO_WRITE); 252186545Srwatson sc->sc_erase_timeout = 1 << cfi_read_qry(sc, CFI_QRY_TTO_ERASE); 253186545Srwatson sc->sc_write_timeout *= 1 << cfi_read_qry(sc, CFI_QRY_MTO_WRITE); 254186545Srwatson sc->sc_erase_timeout *= 1 << cfi_read_qry(sc, CFI_QRY_MTO_ERASE); 255189279Srwatson 256189279Srwatson /* Get erase regions. */ 257186545Srwatson sc->sc_regions = cfi_read_qry(sc, CFI_QRY_NREGIONS); 258186545Srwatson sc->sc_region = malloc(sc->sc_regions * sizeof(struct cfi_region), 259186545Srwatson M_TEMP, M_WAITOK | M_ZERO); 260186545Srwatson for (r = 0; r < sc->sc_regions; r++) { 261186545Srwatson blocks = cfi_read_qry(sc, CFI_QRY_REGION(r)) | 262186545Srwatson (cfi_read_qry(sc, CFI_QRY_REGION(r) + 1) << 8); 263186545Srwatson sc->sc_region[r].r_blocks = blocks + 1; 264186545Srwatson 265186545Srwatson blksz = cfi_read_qry(sc, CFI_QRY_REGION(r) + 2) | 266186545Srwatson (cfi_read_qry(sc, CFI_QRY_REGION(r) + 3) << 8); 267186545Srwatson sc->sc_region[r].r_blksz = (blksz == 0) ? 128 : 268186545Srwatson blksz * 256; 269186545Srwatson } 270186545Srwatson 271186545Srwatson /* Reset the device to a default state. */ 272186545Srwatson cfi_write(sc, 0, CFI_BCS_CLEAR_STATUS); 273186545Srwatson 274186545Srwatson if (bootverbose) { 275186545Srwatson device_printf(dev, "["); 276186545Srwatson for (r = 0; r < sc->sc_regions; r++) { 277186545Srwatson printf("%ux%s%s", sc->sc_region[r].r_blocks, 278186545Srwatson cfi_fmtsize(sc->sc_region[r].r_blksz), 279186545Srwatson (r == sc->sc_regions - 1) ? "]\n" : ","); 280186545Srwatson } 281186545Srwatson } 282186545Srwatson 283186545Srwatson u = device_get_unit(dev); 284186545Srwatson sc->sc_nod = make_dev(&cfi_cdevsw, u, UID_ROOT, GID_WHEEL, 0600, 285186545Srwatson "%s%u", cfi_driver_name, u); 286186545Srwatson sc->sc_nod->si_drv1 = sc; 287186545Srwatson 288186545Srwatson device_add_child(dev, "cfid", -1); 289186545Srwatson bus_generic_attach(dev); 290186545Srwatson 291186545Srwatson return (0); 292186545Srwatson} 293186545Srwatson 294186545Srwatsonint 295186545Srwatsoncfi_detach(device_t dev) 296186545Srwatson{ 297186545Srwatson struct cfi_softc *sc; 298189279Srwatson 299186545Srwatson sc = device_get_softc(dev); 300186545Srwatson 301186545Srwatson destroy_dev(sc->sc_nod); 302186545Srwatson free(sc->sc_region, M_TEMP); 303186545Srwatson bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_rid, sc->sc_res); 304189279Srwatson return (0); 305189279Srwatson} 306186545Srwatson 307186545Srwatsonstatic int 308186545Srwatsoncfi_wait_ready(struct cfi_softc *sc, u_int ofs, u_int timeout) 309186545Srwatson{ 310186545Srwatson int done, error; 311186545Srwatson uint32_t st0 = 0, st = 0; 312186545Srwatson 313186545Srwatson done = 0; 314189279Srwatson error = 0; 315189279Srwatson timeout *= 10; 316189279Srwatson while (!done && !error && timeout) { 317189279Srwatson DELAY(100); 318189279Srwatson timeout--; 319189279Srwatson 320189279Srwatson switch (sc->sc_cmdset) { 321189279Srwatson case CFI_VEND_INTEL_ECS: 322189279Srwatson case CFI_VEND_INTEL_SCS: 323189279Srwatson st = cfi_read(sc, ofs); 324189279Srwatson done = (st & CFI_INTEL_STATUS_WSMS); 325189279Srwatson if (done) { 326189279Srwatson /* NB: bit 0 is reserved */ 327189279Srwatson st &= ~(CFI_INTEL_XSTATUS_RSVD | 328189279Srwatson CFI_INTEL_STATUS_WSMS | 329189279Srwatson CFI_INTEL_STATUS_RSVD); 330189279Srwatson if (st & CFI_INTEL_STATUS_DPS) 331189279Srwatson error = EPERM; 332189279Srwatson else if (st & CFI_INTEL_STATUS_PSLBS) 333189279Srwatson error = EIO; 334189279Srwatson else if (st & CFI_INTEL_STATUS_ECLBS) 335189279Srwatson error = ENXIO; 336189279Srwatson else if (st) 337189279Srwatson error = EACCES; 338189279Srwatson } 339189279Srwatson break; 340189279Srwatson case CFI_VEND_AMD_SCS: 341189279Srwatson case CFI_VEND_AMD_ECS: 342189279Srwatson st0 = cfi_read(sc, ofs); 343189279Srwatson st = cfi_read(sc, ofs); 344189279Srwatson done = ((st & 0x40) == (st0 & 0x40)) ? 1 : 0; 345189279Srwatson break; 346189279Srwatson } 347189279Srwatson } 348189279Srwatson if (!done && !error) 349189279Srwatson error = ETIMEDOUT; 350189279Srwatson if (error) 351189279Srwatson printf("\nerror=%d (st 0x%x st0 0x%x)\n", error, st, st0); 352189279Srwatson return (error); 353189279Srwatson} 354189279Srwatson 355189279Srwatsonint 356189279Srwatsoncfi_write_block(struct cfi_softc *sc) 357189279Srwatson{ 358189279Srwatson union { 359189279Srwatson uint8_t *x8; 360189279Srwatson uint16_t *x16; 361189279Srwatson uint32_t *x32; 362189279Srwatson } ptr; 363189279Srwatson register_t intr; 364189279Srwatson int error, i; 365189279Srwatson 366189279Srwatson /* Erase the block. */ 367189279Srwatson switch (sc->sc_cmdset) { 368189279Srwatson case CFI_VEND_INTEL_ECS: 369189279Srwatson case CFI_VEND_INTEL_SCS: 370189279Srwatson cfi_write(sc, sc->sc_wrofs, CFI_BCS_BLOCK_ERASE); 371189279Srwatson cfi_write(sc, sc->sc_wrofs, CFI_BCS_CONFIRM); 372189279Srwatson break; 373189279Srwatson case CFI_VEND_AMD_SCS: 374189279Srwatson case CFI_VEND_AMD_ECS: 375189279Srwatson cfi_amd_write(sc, sc->sc_wrofs, AMD_ADDR_START, 376189279Srwatson CFI_AMD_ERASE_SECTOR); 377189279Srwatson cfi_amd_write(sc, sc->sc_wrofs, 0, CFI_AMD_BLOCK_ERASE); 378189279Srwatson break; 379189279Srwatson default: 380189279Srwatson /* Better safe than sorry... */ 381189279Srwatson return (ENODEV); 382189279Srwatson } 383189279Srwatson error = cfi_wait_ready(sc, sc->sc_wrofs, sc->sc_erase_timeout); 384189279Srwatson if (error) 385189279Srwatson goto out; 386189279Srwatson 387189279Srwatson /* Write the block. */ 388189279Srwatson ptr.x8 = sc->sc_wrbuf; 389189279Srwatson for (i = 0; i < sc->sc_wrbufsz; i += sc->sc_width) { 390189279Srwatson 391189279Srwatson /* 392189279Srwatson * Make sure the command to start a write and the 393189279Srwatson * actual write happens back-to-back without any 394189279Srwatson * excessive delays. 395189279Srwatson */ 396189279Srwatson intr = intr_disable(); 397189279Srwatson 398189279Srwatson switch (sc->sc_cmdset) { 399189279Srwatson case CFI_VEND_INTEL_ECS: 400189279Srwatson case CFI_VEND_INTEL_SCS: 401189279Srwatson cfi_write(sc, sc->sc_wrofs + i, CFI_BCS_PROGRAM); 402189279Srwatson break; 403189279Srwatson case CFI_VEND_AMD_SCS: 404189279Srwatson case CFI_VEND_AMD_ECS: 405189279Srwatson cfi_amd_write(sc, 0, AMD_ADDR_START, CFI_AMD_PROGRAM); 406189279Srwatson break; 407189279Srwatson } 408189279Srwatson switch (sc->sc_width) { 409189279Srwatson case 1: 410189279Srwatson bus_space_write_1(sc->sc_tag, sc->sc_handle, 411189279Srwatson sc->sc_wrofs + i, *(ptr.x8)++); 412189279Srwatson break; 413189279Srwatson case 2: 414189279Srwatson bus_space_write_2(sc->sc_tag, sc->sc_handle, 415189279Srwatson sc->sc_wrofs + i, *(ptr.x16)++); 416189279Srwatson break; 417189279Srwatson case 4: 418189279Srwatson bus_space_write_4(sc->sc_tag, sc->sc_handle, 419189279Srwatson sc->sc_wrofs + i, *(ptr.x32)++); 420189279Srwatson break; 421189279Srwatson } 422189279Srwatson 423189279Srwatson intr_restore(intr); 424189279Srwatson 425189279Srwatson error = cfi_wait_ready(sc, sc->sc_wrofs, sc->sc_write_timeout); 426189279Srwatson if (error) 427189279Srwatson goto out; 428189279Srwatson } 429189279Srwatson 430189279Srwatson /* error is 0. */ 431189279Srwatson 432189279Srwatson out: 433189279Srwatson cfi_write(sc, 0, CFI_BCS_READ_ARRAY); 434189279Srwatson return (error); 435189279Srwatson} 436189279Srwatson 437189279Srwatson#ifdef CFI_SUPPORT_STRATAFLASH 438189279Srwatson/* 439189279Srwatson * Intel StrataFlash Protection Register Support. 440189279Srwatson * 441189279Srwatson * The memory includes a 128-bit Protection Register that can be 442189279Srwatson * used for security. There are two 64-bit segments; one is programmed 443189279Srwatson * at the factory with a unique 64-bit number which is immutable. 444189279Srwatson * The other segment is left blank for User (OEM) programming. 445189279Srwatson * The User/OEM segment is One Time Programmable (OTP). It can also 446189279Srwatson * be locked to prevent any further writes by setting bit 0 of the 447189279Srwatson * Protection Lock Register (PLR). The PLR can written only once. 448189279Srwatson */ 449189279Srwatson 450189279Srwatsonstatic uint16_t 451189279Srwatsoncfi_get16(struct cfi_softc *sc, int off) 452189279Srwatson{ 453189279Srwatson uint16_t v = bus_space_read_2(sc->sc_tag, sc->sc_handle, off<<1); 454189279Srwatson return v; 455189279Srwatson} 456189279Srwatson 457189279Srwatson#ifdef CFI_ARMEDANDDANGEROUS 458189279Srwatsonstatic void 459189279Srwatsoncfi_put16(struct cfi_softc *sc, int off, uint16_t v) 460189279Srwatson{ 461189279Srwatson bus_space_write_2(sc->sc_tag, sc->sc_handle, off<<1, v); 462189279Srwatson} 463189279Srwatson#endif 464189279Srwatson 465189279Srwatson/* 466189279Srwatson * Read the factory-defined 64-bit segment of the PR. 467189279Srwatson */ 468189279Srwatsonint 469189279Srwatsoncfi_intel_get_factory_pr(struct cfi_softc *sc, uint64_t *id) 470189279Srwatson{ 471189279Srwatson if (sc->sc_cmdset != CFI_VEND_INTEL_ECS) 472189279Srwatson return EOPNOTSUPP; 473189279Srwatson KASSERT(sc->sc_width == 2, ("sc_width %d", sc->sc_width)); 474189279Srwatson 475189279Srwatson cfi_write(sc, 0, CFI_INTEL_READ_ID); 476189279Srwatson *id = ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(0)))<<48 | 477189279Srwatson ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(1)))<<32 | 478189279Srwatson ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(2)))<<16 | 479189279Srwatson ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(3))); 480189279Srwatson cfi_write(sc, 0, CFI_BCS_READ_ARRAY); 481189279Srwatson return 0; 482189279Srwatson} 483189279Srwatson 484189279Srwatson/* 485189279Srwatson * Read the User/OEM 64-bit segment of the PR. 486189279Srwatson */ 487189279Srwatsonint 488189279Srwatsoncfi_intel_get_oem_pr(struct cfi_softc *sc, uint64_t *id) 489189279Srwatson{ 490189279Srwatson if (sc->sc_cmdset != CFI_VEND_INTEL_ECS) 491189279Srwatson return EOPNOTSUPP; 492189279Srwatson KASSERT(sc->sc_width == 2, ("sc_width %d", sc->sc_width)); 493189279Srwatson 494189279Srwatson cfi_write(sc, 0, CFI_INTEL_READ_ID); 495189279Srwatson *id = ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(4)))<<48 | 496189279Srwatson ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(5)))<<32 | 497189279Srwatson ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(6)))<<16 | 498189279Srwatson ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(7))); 499189279Srwatson cfi_write(sc, 0, CFI_BCS_READ_ARRAY); 500189279Srwatson return 0; 501189279Srwatson} 502189279Srwatson 503189279Srwatson/* 504189279Srwatson * Write the User/OEM 64-bit segment of the PR. 505189279Srwatson * XXX should allow writing individual words/bytes 506189279Srwatson */ 507189279Srwatsonint 508189279Srwatsoncfi_intel_set_oem_pr(struct cfi_softc *sc, uint64_t id) 509189279Srwatson{ 510189279Srwatson#ifdef CFI_ARMEDANDDANGEROUS 511189279Srwatson register_t intr; 512189279Srwatson int i, error; 513189279Srwatson#endif 514189279Srwatson 515189279Srwatson if (sc->sc_cmdset != CFI_VEND_INTEL_ECS) 516189279Srwatson return EOPNOTSUPP; 517189279Srwatson KASSERT(sc->sc_width == 2, ("sc_width %d", sc->sc_width)); 518189279Srwatson 519189279Srwatson#ifdef CFI_ARMEDANDDANGEROUS 520189279Srwatson for (i = 7; i >= 4; i--, id >>= 16) { 521189279Srwatson intr = intr_disable(); 522189279Srwatson cfi_write(sc, 0, CFI_INTEL_PP_SETUP); 523189279Srwatson cfi_put16(sc, CFI_INTEL_PR(i), id&0xffff); 524189279Srwatson intr_restore(intr); 525189279Srwatson error = cfi_wait_ready(sc, CFI_BCS_READ_STATUS, 526189279Srwatson sc->sc_write_timeout); 527189279Srwatson if (error) 528189279Srwatson break; 529189279Srwatson } 530189279Srwatson cfi_write(sc, 0, CFI_BCS_READ_ARRAY); 531189279Srwatson return error; 532189279Srwatson#else 533189279Srwatson device_printf(sc->sc_dev, "%s: OEM PR not set, " 534189279Srwatson "CFI_ARMEDANDDANGEROUS not configured\n", __func__); 535189279Srwatson return ENXIO; 536189279Srwatson#endif 537189279Srwatson} 538189279Srwatson 539189279Srwatson/* 540189279Srwatson * Read the contents of the Protection Lock Register. 541189279Srwatson */ 542189279Srwatsonint 543189279Srwatsoncfi_intel_get_plr(struct cfi_softc *sc, uint32_t *plr) 544189279Srwatson{ 545189279Srwatson if (sc->sc_cmdset != CFI_VEND_INTEL_ECS) 546189279Srwatson return EOPNOTSUPP; 547189279Srwatson KASSERT(sc->sc_width == 2, ("sc_width %d", sc->sc_width)); 548189279Srwatson 549189279Srwatson cfi_write(sc, 0, CFI_INTEL_READ_ID); 550189279Srwatson *plr = cfi_get16(sc, CFI_INTEL_PLR); 551189279Srwatson cfi_write(sc, 0, CFI_BCS_READ_ARRAY); 552189279Srwatson return 0; 553189279Srwatson} 554189279Srwatson 555189279Srwatson/* 556189279Srwatson * Write the Protection Lock Register to lock down the 557189279Srwatson * user-settable segment of the Protection Register. 558189279Srwatson * NOTE: this operation is not reversible. 559189279Srwatson */ 560189279Srwatsonint 561189279Srwatsoncfi_intel_set_plr(struct cfi_softc *sc) 562189279Srwatson{ 563189279Srwatson#ifdef CFI_ARMEDANDDANGEROUS 564186545Srwatson register_t intr; 565189279Srwatson int error; 566189279Srwatson#endif 567186545Srwatson if (sc->sc_cmdset != CFI_VEND_INTEL_ECS) 568186545Srwatson return EOPNOTSUPP; 569186545Srwatson KASSERT(sc->sc_width == 2, ("sc_width %d", sc->sc_width)); 570186545Srwatson 571186545Srwatson#ifdef CFI_ARMEDANDDANGEROUS 572186545Srwatson /* worthy of console msg */ 573186545Srwatson device_printf(sc->sc_dev, "set PLR\n"); 574186545Srwatson intr = intr_disable(); 575186545Srwatson cfi_write(sc, 0, CFI_INTEL_PP_SETUP); 576186545Srwatson cfi_put16(sc, CFI_INTEL_PLR, 0xFFFD); 577186545Srwatson intr_restore(intr); 578186545Srwatson error = cfi_wait_ready(sc, CFI_BCS_READ_STATUS, sc->sc_write_timeout); 579186545Srwatson cfi_write(sc, 0, CFI_BCS_READ_ARRAY); 580186545Srwatson return error; 581186545Srwatson#else 582186545Srwatson device_printf(sc->sc_dev, "%s: PLR not set, " 583186545Srwatson "CFI_ARMEDANDDANGEROUS not configured\n", __func__); 584186545Srwatson return ENXIO; 585186545Srwatson#endif 586189279Srwatson} 587186545Srwatson#endif /* CFI_SUPPORT_STRATAFLASH */ 588186545Srwatson