Annotation of sys/dev/flash.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: flash.c,v 1.8 2007/06/20 18:15:46 deraadt Exp $ */
! 2:
! 3: /*
! 4: * Copyright (c) 2005 Uwe Stuehler <uwe@openbsd.org>
! 5: *
! 6: * Permission to use, copy, modify, and distribute this software for any
! 7: * purpose with or without fee is hereby granted, provided that the above
! 8: * copyright notice and this permission notice appear in all copies.
! 9: *
! 10: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
! 11: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
! 12: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
! 13: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
! 14: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
! 15: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
! 16: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
! 17: */
! 18:
! 19: #include <sys/param.h>
! 20: #include <sys/buf.h>
! 21: #include <sys/conf.h>
! 22: #include <sys/device.h>
! 23: #include <sys/disk.h>
! 24: #include <sys/disklabel.h>
! 25: #include <sys/dkio.h>
! 26: #include <sys/kernel.h>
! 27: #include <sys/stat.h>
! 28: #include <sys/systm.h>
! 29:
! 30: #include <dev/flashvar.h>
! 31:
! 32: #include <ufs/ffs/fs.h> /* XXX */
! 33:
! 34: /* Samsung command set */
! 35: #define SAMSUNG_CMD_PTRLO 0x00
! 36: #define SAMSUNG_CMD_PTRHI 0x01
! 37: #define SAMSUNG_CMD_PTROOB 0x50
! 38: #define SAMSUNG_CMD_READ 0x30
! 39: #define SAMSUNG_CMD_SEQIN 0x80
! 40: #define SAMSUNG_CMD_WRITE 0x10
! 41: #define SAMSUNG_CMD_ERASE0 0x60
! 42: #define SAMSUNG_CMD_ERASE1 0xd0
! 43: #define SAMSUNG_CMD_STATUS 0x70
! 44: #define STATUS_FAIL (1<<0)
! 45: #define STATUS_READY (1<<6)
! 46: #define STATUS_NWP (1<<7)
! 47: #define SAMSUNG_CMD_READID 0x90
! 48: #define SAMSUNG_CMD_RESET 0xff
! 49:
! 50: int flash_wait_ready(struct flash_softc *);
! 51: int flash_wait_complete(struct flash_softc *);
! 52:
! 53: /* XXX: these should go elsewhere */
! 54: cdev_decl(flash);
! 55: bdev_decl(flash);
! 56:
! 57: #define flashlock(sc) disk_lock(&(sc)->sc_dk)
! 58: #define flashunlock(sc) disk_unlock(&(sc)->sc_dk)
! 59: #define flashlookup(unit) \
! 60: (struct flash_softc *)device_lookup(&flash_cd, (unit))
! 61:
! 62: void flashminphys(struct buf *);
! 63: void flashstart(struct flash_softc *);
! 64: void _flashstart(struct flash_softc *, struct buf *);
! 65: void flashdone(void *);
! 66:
! 67: int flashsafestrategy(struct flash_softc *, struct buf *);
! 68: void flashgetdefaultlabel(dev_t, struct flash_softc *,
! 69: struct disklabel *);
! 70: void flashgetdisklabel(dev_t, struct flash_softc *, struct disklabel *, int);
! 71:
! 72: /*
! 73: * Driver attachment glue
! 74: */
! 75:
! 76: struct flashvendor {
! 77: u_int8_t vendor;
! 78: const char *name;
! 79: };
! 80:
! 81: static const struct flashvendor flashvendors[] = {
! 82: { FLASH_VENDOR_SAMSUNG, "Samsung" }
! 83: };
! 84: #define FLASH_NVENDORS (sizeof(flashvendors) / sizeof(flashvendors[0]))
! 85:
! 86: static const struct flashdev flashdevs[] = {
! 87: { FLASH_DEVICE_SAMSUNG_K9F2808U0C, "K9F2808U0C 16Mx8 3.3V",
! 88: 512, 16, 32, 32768 },
! 89: { FLASH_DEVICE_SAMSUNG_K9F1G08U0A, "K9F1G08U0A 128Mx8 3.3V",
! 90: 2048, 64, 64, 65536 },
! 91: };
! 92: #define FLASH_NDEVS (sizeof(flashdevs) / sizeof(flashdevs[0]))
! 93:
! 94: struct cfdriver flash_cd = {
! 95: NULL, "flash", DV_DISK
! 96: };
! 97:
! 98: struct dkdriver flashdkdriver = { flashstrategy };
! 99:
! 100: void
! 101: flashattach(struct flash_softc *sc, struct flash_ctl_tag *tag,
! 102: void *cookie)
! 103: {
! 104: u_int8_t vendor, device;
! 105: u_int16_t id;
! 106: int i;
! 107:
! 108: sc->sc_tag = tag;
! 109: sc->sc_cookie = cookie;
! 110:
! 111: if (sc->sc_maxwaitready <= 0)
! 112: sc->sc_maxwaitready = 1000; /* 1ms */
! 113: if (sc->sc_maxwaitcomplete <= 0)
! 114: sc->sc_maxwaitcomplete = 200000; /* 200ms */
! 115:
! 116: flash_chip_enable(sc);
! 117:
! 118: /* Identify the flash device. */
! 119: if (flash_chip_identify(sc, &vendor, &device) != 0) {
! 120: printf(": identification failed\n");
! 121: flash_chip_disable(sc);
! 122: return;
! 123: }
! 124: id = (vendor << 8) | device;
! 125:
! 126: /* Look up device characteristics, abort if not recognized. */
! 127: for (i = 0; i < FLASH_NVENDORS; i++) {
! 128: if (flashvendors[i].vendor == vendor) {
! 129: printf(": %s", flashvendors[i].name);
! 130: break;
! 131: }
! 132: }
! 133: if (i == FLASH_NVENDORS)
! 134: printf(": vendor 0x%02x", vendor);
! 135: for (i = 0; i < FLASH_NDEVS; i++) {
! 136: if (flashdevs[i].id == id) {
! 137: printf(" %s\n", flashdevs[i].longname);
! 138: break;
! 139: }
! 140: }
! 141: if (i == FLASH_NDEVS) {
! 142: /* Need to add this device to flashdevs first. */
! 143: printf(" device 0x%02x\n", device);
! 144: flash_chip_disable(sc);
! 145: return;
! 146: }
! 147: sc->sc_flashdev = &flashdevs[i];
! 148:
! 149: /* Check if the device really works or fail early. */
! 150: if (flash_chip_reset(sc) != 0) {
! 151: printf("%s: reset failed\n", sc->sc_dev.dv_xname);
! 152: flash_chip_disable(sc);
! 153: return;
! 154: }
! 155:
! 156: flash_chip_disable(sc);
! 157:
! 158: /*
! 159: * Initialize and attach the disk structure.
! 160: */
! 161: sc->sc_dk.dk_driver = &flashdkdriver;
! 162: sc->sc_dk.dk_name = sc->sc_dev.dv_xname;
! 163: disk_attach(&sc->sc_dk);
! 164:
! 165: /* XXX establish shutdown hook to finish any commands. */
! 166: }
! 167:
! 168: int
! 169: flashdetach(struct device *self, int flags)
! 170: {
! 171: struct flash_softc *sc = (struct flash_softc *)self;
! 172:
! 173: /* Detach disk. */
! 174: disk_detach(&sc->sc_dk);
! 175:
! 176: /* XXX more resources need to be freed here. */
! 177: return 0;
! 178: }
! 179:
! 180: int
! 181: flashactivate(struct device *self, enum devact act)
! 182: {
! 183: /* XXX anything to be done here? */
! 184: return 0;
! 185: }
! 186:
! 187: /*
! 188: * Flash controller and chip functions
! 189: */
! 190:
! 191: u_int8_t
! 192: flash_reg8_read(struct flash_softc *sc, int reg)
! 193: {
! 194: return sc->sc_tag->reg8_read(sc->sc_cookie, reg);
! 195: }
! 196:
! 197: void
! 198: flash_reg8_read_page(struct flash_softc *sc, caddr_t data, caddr_t oob)
! 199: {
! 200: int i;
! 201:
! 202: for (i = 0; i < sc->sc_flashdev->pagesize; i++)
! 203: data[i] = flash_reg8_read(sc, FLASH_REG_DATA);
! 204:
! 205: if (oob != NULL)
! 206: for (i = 0; i < sc->sc_flashdev->oobsize; i++)
! 207: oob[i] = flash_reg8_read(sc, FLASH_REG_DATA);
! 208: }
! 209:
! 210: void
! 211: flash_reg8_write(struct flash_softc *sc, int reg, u_int8_t value)
! 212: {
! 213: sc->sc_tag->reg8_write(sc->sc_cookie, reg, value);
! 214: }
! 215:
! 216: void
! 217: flash_reg8_write_page(struct flash_softc *sc, caddr_t data, caddr_t oob)
! 218: {
! 219: int i;
! 220:
! 221: for (i = 0; i < sc->sc_flashdev->pagesize; i++)
! 222: flash_reg8_write(sc, FLASH_REG_DATA, data[i]);
! 223:
! 224: if (oob != NULL)
! 225: for (i = 0; i < sc->sc_flashdev->oobsize; i++)
! 226: flash_reg8_write(sc, FLASH_REG_DATA, oob[i]);
! 227: }
! 228:
! 229: /*
! 230: * Wait for the "Ready/Busy" signal to go high, indicating that the
! 231: * device is ready to accept another command.
! 232: */
! 233: int
! 234: flash_wait_ready(struct flash_softc *sc)
! 235: {
! 236: int timo = sc->sc_maxwaitready;
! 237: u_int8_t ready;
! 238:
! 239: ready = flash_reg8_read(sc, FLASH_REG_READY);
! 240: while (ready == 0 && timo-- > 0) {
! 241: delay(1);
! 242: ready = flash_reg8_read(sc, FLASH_REG_READY);
! 243: }
! 244: return (ready == 0 ? EIO : 0);
! 245: }
! 246:
! 247: /*
! 248: * Similar to flash_wait_ready() but looks at IO 6 and IO 0 signals
! 249: * besides R/B to decide whether the last operation was successful.
! 250: */
! 251: int
! 252: flash_wait_complete(struct flash_softc *sc)
! 253: {
! 254: int timo = sc->sc_maxwaitcomplete;
! 255: u_int8_t status;
! 256:
! 257: (void)flash_wait_ready(sc);
! 258:
! 259: flash_reg8_write(sc, FLASH_REG_CLE, 1);
! 260: flash_reg8_write(sc, FLASH_REG_CMD, SAMSUNG_CMD_STATUS);
! 261: flash_reg8_write(sc, FLASH_REG_CLE, 0);
! 262:
! 263: status = flash_reg8_read(sc, FLASH_REG_DATA);
! 264: while ((status & STATUS_READY) == 0 && timo-- > 0) {
! 265: if (flash_reg8_read(sc, FLASH_REG_READY))
! 266: break;
! 267: delay(1);
! 268: status = flash_reg8_read(sc, FLASH_REG_DATA);
! 269: }
! 270:
! 271: status = flash_reg8_read(sc, FLASH_REG_DATA);
! 272: return ((status & STATUS_FAIL) != 0 ? EIO : 0);
! 273: }
! 274:
! 275: void
! 276: flash_chip_enable(struct flash_softc *sc)
! 277: {
! 278: /* XXX aquire the lock. */
! 279: flash_reg8_write(sc, FLASH_REG_CE, 1);
! 280: }
! 281:
! 282: void
! 283: flash_chip_disable(struct flash_softc *sc)
! 284: {
! 285: flash_reg8_write(sc, FLASH_REG_CE, 0);
! 286: /* XXX release the lock. */
! 287: }
! 288:
! 289: int
! 290: flash_chip_reset(struct flash_softc *sc)
! 291: {
! 292: flash_reg8_write(sc, FLASH_REG_CLE, 1);
! 293: flash_reg8_write(sc, FLASH_REG_CMD, SAMSUNG_CMD_RESET);
! 294: flash_reg8_write(sc, FLASH_REG_CLE, 0);
! 295:
! 296: return flash_wait_ready(sc);
! 297: }
! 298:
! 299: int
! 300: flash_chip_identify(struct flash_softc *sc, u_int8_t *vendor,
! 301: u_int8_t *device)
! 302: {
! 303: int error;
! 304:
! 305: (void)flash_wait_ready(sc);
! 306:
! 307: flash_reg8_write(sc, FLASH_REG_CLE, 1);
! 308: flash_reg8_write(sc, FLASH_REG_CMD, SAMSUNG_CMD_READID);
! 309: flash_reg8_write(sc, FLASH_REG_CLE, 0);
! 310:
! 311: error = flash_wait_ready(sc);
! 312: if (error == 0) {
! 313: *vendor = flash_reg8_read(sc, FLASH_REG_DATA);
! 314: *device = flash_reg8_read(sc, FLASH_REG_DATA);
! 315: }
! 316: return error;
! 317: }
! 318:
! 319: int
! 320: flash_chip_erase_block(struct flash_softc *sc, long blkno)
! 321: {
! 322: long pageno = blkno * sc->sc_flashdev->blkpages;
! 323: int error;
! 324:
! 325: (void)flash_wait_ready(sc);
! 326:
! 327: /* Disable write-protection. */
! 328: flash_reg8_write(sc, FLASH_REG_WP, 0);
! 329:
! 330: switch (sc->sc_flashdev->id) {
! 331: case FLASH_DEVICE_SAMSUNG_K9F2808U0C:
! 332: case FLASH_DEVICE_SAMSUNG_K9F1G08U0A:
! 333: flash_reg8_write(sc, FLASH_REG_CLE, 1);
! 334: flash_reg8_write(sc, FLASH_REG_CMD, SAMSUNG_CMD_ERASE0);
! 335: flash_reg8_write(sc, FLASH_REG_CLE, 0);
! 336: break;
! 337: }
! 338:
! 339: switch (sc->sc_flashdev->id) {
! 340: case FLASH_DEVICE_SAMSUNG_K9F2808U0C:
! 341: case FLASH_DEVICE_SAMSUNG_K9F1G08U0A:
! 342: flash_reg8_write(sc, FLASH_REG_ALE, 1);
! 343: flash_reg8_write(sc, FLASH_REG_ROW, pageno);
! 344: flash_reg8_write(sc, FLASH_REG_ROW, pageno >> 8);
! 345: flash_reg8_write(sc, FLASH_REG_ALE, 0);
! 346: break;
! 347: }
! 348:
! 349: switch (sc->sc_flashdev->id) {
! 350: case FLASH_DEVICE_SAMSUNG_K9F2808U0C:
! 351: case FLASH_DEVICE_SAMSUNG_K9F1G08U0A:
! 352: flash_reg8_write(sc, FLASH_REG_CLE, 1);
! 353: flash_reg8_write(sc, FLASH_REG_CMD, SAMSUNG_CMD_ERASE1);
! 354: flash_reg8_write(sc, FLASH_REG_CLE, 0);
! 355: break;
! 356: }
! 357:
! 358: error = flash_wait_complete(sc);
! 359:
! 360: /* Re-enable write-protection. */
! 361: flash_reg8_write(sc, FLASH_REG_WP, 1);
! 362:
! 363: return error;
! 364: }
! 365:
! 366: int
! 367: flash_chip_read_block(struct flash_softc *sc, long blkno, caddr_t data)
! 368: {
! 369: long pageno;
! 370: long blkend;
! 371: int error;
! 372:
! 373: pageno = blkno * sc->sc_flashdev->blkpages;
! 374: blkend = pageno + sc->sc_flashdev->blkpages;
! 375:
! 376: while (pageno < blkend) {
! 377: error = flash_chip_read_page(sc, pageno, data, NULL);
! 378: if (error != 0)
! 379: return error;
! 380: data += sc->sc_flashdev->pagesize;
! 381: pageno++;
! 382: }
! 383: return 0;
! 384: }
! 385:
! 386: int
! 387: flash_chip_read_page(struct flash_softc *sc, long pageno, caddr_t data,
! 388: caddr_t oob)
! 389: {
! 390: int error;
! 391:
! 392: (void)flash_wait_ready(sc);
! 393:
! 394: switch (sc->sc_flashdev->id) {
! 395: case FLASH_DEVICE_SAMSUNG_K9F2808U0C:
! 396: case FLASH_DEVICE_SAMSUNG_K9F1G08U0A:
! 397: flash_reg8_write(sc, FLASH_REG_CLE, 1);
! 398: flash_reg8_write(sc, FLASH_REG_CMD, SAMSUNG_CMD_PTRLO);
! 399: flash_reg8_write(sc, FLASH_REG_CLE, 0);
! 400: break;
! 401: }
! 402:
! 403: switch (sc->sc_flashdev->id) {
! 404: case FLASH_DEVICE_SAMSUNG_K9F2808U0C:
! 405: flash_reg8_write(sc, FLASH_REG_ALE, 1);
! 406: flash_reg8_write(sc, FLASH_REG_COL, 0x00);
! 407: flash_reg8_write(sc, FLASH_REG_ALE, 0);
! 408: break;
! 409: case FLASH_DEVICE_SAMSUNG_K9F1G08U0A:
! 410: flash_reg8_write(sc, FLASH_REG_ALE, 1);
! 411: flash_reg8_write(sc, FLASH_REG_COL, 0x00);
! 412: flash_reg8_write(sc, FLASH_REG_COL, 0x00);
! 413: flash_reg8_write(sc, FLASH_REG_ALE, 0);
! 414: break;
! 415: }
! 416:
! 417: switch (sc->sc_flashdev->id) {
! 418: case FLASH_DEVICE_SAMSUNG_K9F2808U0C:
! 419: case FLASH_DEVICE_SAMSUNG_K9F1G08U0A:
! 420: flash_reg8_write(sc, FLASH_REG_ALE, 1);
! 421: flash_reg8_write(sc, FLASH_REG_ROW, pageno);
! 422: flash_reg8_write(sc, FLASH_REG_ROW, pageno >> 8);
! 423: flash_reg8_write(sc, FLASH_REG_ALE, 0);
! 424: break;
! 425: }
! 426:
! 427: switch (sc->sc_flashdev->id) {
! 428: case FLASH_DEVICE_SAMSUNG_K9F1G08U0A:
! 429: flash_reg8_write(sc, FLASH_REG_CLE, 1);
! 430: flash_reg8_write(sc, FLASH_REG_CMD, SAMSUNG_CMD_READ);
! 431: flash_reg8_write(sc, FLASH_REG_CLE, 0);
! 432: break;
! 433: }
! 434:
! 435: if ((error = flash_wait_ready(sc)) != 0)
! 436: return error;
! 437:
! 438: /* Support hardware ECC calculation. */
! 439: if (sc->sc_tag->regx_read_page) {
! 440: error = sc->sc_tag->regx_read_page(sc->sc_cookie, data,
! 441: oob);
! 442: if (error != 0)
! 443: return error;
! 444: } else
! 445: flash_reg8_read_page(sc, data, oob);
! 446:
! 447: return 0;
! 448: }
! 449:
! 450: int
! 451: flash_chip_read_oob(struct flash_softc *sc, long pageno, caddr_t oob)
! 452: {
! 453: u_int8_t *p = (u_int8_t *)oob;
! 454: int error;
! 455: int i;
! 456:
! 457: (void)flash_wait_ready(sc);
! 458:
! 459: switch (sc->sc_flashdev->id) {
! 460: case FLASH_DEVICE_SAMSUNG_K9F2808U0C:
! 461: flash_reg8_write(sc, FLASH_REG_CLE, 1);
! 462: flash_reg8_write(sc, FLASH_REG_CMD, SAMSUNG_CMD_PTROOB);
! 463: flash_reg8_write(sc, FLASH_REG_CLE, 0);
! 464: break;
! 465: case FLASH_DEVICE_SAMSUNG_K9F1G08U0A:
! 466: flash_reg8_write(sc, FLASH_REG_CLE, 1);
! 467: flash_reg8_write(sc, FLASH_REG_CMD, SAMSUNG_CMD_PTRLO);
! 468: flash_reg8_write(sc, FLASH_REG_CLE, 0);
! 469: break;
! 470: }
! 471:
! 472: switch (sc->sc_flashdev->id) {
! 473: case FLASH_DEVICE_SAMSUNG_K9F2808U0C:
! 474: flash_reg8_write(sc, FLASH_REG_ALE, 1);
! 475: flash_reg8_write(sc, FLASH_REG_COL, 0x00);
! 476: flash_reg8_write(sc, FLASH_REG_ALE, 0);
! 477: break;
! 478: case FLASH_DEVICE_SAMSUNG_K9F1G08U0A:
! 479: flash_reg8_write(sc, FLASH_REG_ALE, 1);
! 480: flash_reg8_write(sc, FLASH_REG_COL, 0x00);
! 481: flash_reg8_write(sc, FLASH_REG_COL, 0x08);
! 482: flash_reg8_write(sc, FLASH_REG_ALE, 0);
! 483: break;
! 484: }
! 485:
! 486: switch (sc->sc_flashdev->id) {
! 487: case FLASH_DEVICE_SAMSUNG_K9F2808U0C:
! 488: case FLASH_DEVICE_SAMSUNG_K9F1G08U0A:
! 489: flash_reg8_write(sc, FLASH_REG_ALE, 1);
! 490: flash_reg8_write(sc, FLASH_REG_ROW, pageno);
! 491: flash_reg8_write(sc, FLASH_REG_ROW, pageno >> 8);
! 492: flash_reg8_write(sc, FLASH_REG_ALE, 0);
! 493: break;
! 494: }
! 495:
! 496: switch (sc->sc_flashdev->id) {
! 497: case FLASH_DEVICE_SAMSUNG_K9F1G08U0A:
! 498: flash_reg8_write(sc, FLASH_REG_CLE, 1);
! 499: flash_reg8_write(sc, FLASH_REG_CMD, SAMSUNG_CMD_READ);
! 500: flash_reg8_write(sc, FLASH_REG_CLE, 0);
! 501: break;
! 502: }
! 503:
! 504: if ((error = flash_wait_ready(sc)) != 0)
! 505: return error;
! 506:
! 507: for (i = 0; i < sc->sc_flashdev->oobsize; i++)
! 508: p[i] = flash_reg8_read(sc, FLASH_REG_DATA);
! 509:
! 510: return 0;
! 511: }
! 512:
! 513: int
! 514: flash_chip_write_block(struct flash_softc *sc, long blkno, caddr_t data,
! 515: caddr_t oob)
! 516: {
! 517: long pageno;
! 518: long blkend;
! 519: caddr_t p;
! 520: int error;
! 521:
! 522: pageno = blkno * sc->sc_flashdev->blkpages;
! 523: blkend = pageno + sc->sc_flashdev->blkpages;
! 524:
! 525: p = data;
! 526: while (pageno < blkend) {
! 527: error = flash_chip_write_page(sc, pageno, p, oob);
! 528: if (error != 0)
! 529: return error;
! 530: p += sc->sc_flashdev->pagesize;
! 531: pageno++;
! 532: }
! 533:
! 534: /* Verify the newly written block. */
! 535: return flash_chip_verify_block(sc, blkno, data, oob);
! 536: }
! 537:
! 538: int
! 539: flash_chip_write_page(struct flash_softc *sc, long pageno, caddr_t data,
! 540: caddr_t oob)
! 541: {
! 542: int error;
! 543:
! 544: (void)flash_wait_ready(sc);
! 545:
! 546: /* Disable write-protection. */
! 547: flash_reg8_write(sc, FLASH_REG_WP, 0);
! 548:
! 549: switch (sc->sc_flashdev->id) {
! 550: case FLASH_DEVICE_SAMSUNG_K9F2808U0C:
! 551: case FLASH_DEVICE_SAMSUNG_K9F1G08U0A:
! 552: flash_reg8_write(sc, FLASH_REG_CLE, 1);
! 553: flash_reg8_write(sc, FLASH_REG_CMD, SAMSUNG_CMD_PTRLO);
! 554: flash_reg8_write(sc, FLASH_REG_CLE, 0);
! 555: break;
! 556: }
! 557:
! 558: switch (sc->sc_flashdev->id) {
! 559: case FLASH_DEVICE_SAMSUNG_K9F2808U0C:
! 560: case FLASH_DEVICE_SAMSUNG_K9F1G08U0A:
! 561: flash_reg8_write(sc, FLASH_REG_CLE, 1);
! 562: flash_reg8_write(sc, FLASH_REG_CMD, SAMSUNG_CMD_SEQIN);
! 563: flash_reg8_write(sc, FLASH_REG_CLE, 0);
! 564: break;
! 565: }
! 566:
! 567: switch (sc->sc_flashdev->id) {
! 568: case FLASH_DEVICE_SAMSUNG_K9F2808U0C:
! 569: flash_reg8_write(sc, FLASH_REG_ALE, 1);
! 570: flash_reg8_write(sc, FLASH_REG_COL, 0x00);
! 571: flash_reg8_write(sc, FLASH_REG_ALE, 0);
! 572: break;
! 573: case FLASH_DEVICE_SAMSUNG_K9F1G08U0A:
! 574: flash_reg8_write(sc, FLASH_REG_ALE, 1);
! 575: flash_reg8_write(sc, FLASH_REG_COL, 0x00);
! 576: flash_reg8_write(sc, FLASH_REG_COL, 0x00);
! 577: flash_reg8_write(sc, FLASH_REG_ALE, 0);
! 578: break;
! 579: }
! 580:
! 581: switch (sc->sc_flashdev->id) {
! 582: case FLASH_DEVICE_SAMSUNG_K9F2808U0C:
! 583: case FLASH_DEVICE_SAMSUNG_K9F1G08U0A:
! 584: flash_reg8_write(sc, FLASH_REG_ALE, 1);
! 585: flash_reg8_write(sc, FLASH_REG_ROW, pageno);
! 586: flash_reg8_write(sc, FLASH_REG_ROW, pageno >> 8);
! 587: flash_reg8_write(sc, FLASH_REG_ALE, 0);
! 588: break;
! 589: }
! 590:
! 591: /* Support hardware ECC calculation. */
! 592: if (sc->sc_tag->regx_write_page) {
! 593: error = sc->sc_tag->regx_write_page(sc->sc_cookie, data,
! 594: oob);
! 595: if (error != 0)
! 596: return error;
! 597: } else
! 598: flash_reg8_write_page(sc, data, oob);
! 599:
! 600: switch (sc->sc_flashdev->id) {
! 601: case FLASH_DEVICE_SAMSUNG_K9F2808U0C:
! 602: case FLASH_DEVICE_SAMSUNG_K9F1G08U0A:
! 603: flash_reg8_write(sc, FLASH_REG_CLE, 1);
! 604: flash_reg8_write(sc, FLASH_REG_CMD, SAMSUNG_CMD_WRITE);
! 605: flash_reg8_write(sc, FLASH_REG_CLE, 0);
! 606: break;
! 607: }
! 608:
! 609: /*
! 610: * Wait for the write operation to complete although this can
! 611: * take up to 700 us for the K9F1G08U0A flash type.
! 612: */
! 613: error = flash_wait_complete(sc);
! 614:
! 615: /* Re-enable write-protection. */
! 616: flash_reg8_write(sc, FLASH_REG_WP, 1);
! 617:
! 618: return error;
! 619: }
! 620:
! 621: int
! 622: flash_chip_verify_block(struct flash_softc *sc, long blkno, caddr_t data,
! 623: caddr_t oob)
! 624: {
! 625: long pageno;
! 626: long blkend;
! 627: int error;
! 628:
! 629: pageno = blkno * sc->sc_flashdev->blkpages;
! 630: blkend = pageno + sc->sc_flashdev->blkpages;
! 631:
! 632: while (pageno < blkend) {
! 633: error = flash_chip_verify_page(sc, pageno, data, oob);
! 634: if (error != 0) {
! 635: printf("block %d page %d verify failed\n",
! 636: blkno, pageno);
! 637: return error;
! 638: }
! 639: data += sc->sc_flashdev->pagesize;
! 640: pageno++;
! 641: }
! 642: return 0;
! 643: }
! 644:
! 645: int
! 646: flash_chip_verify_page(struct flash_softc *sc, long pageno, caddr_t data,
! 647: caddr_t oob)
! 648: {
! 649: static u_char rbuf[FLASH_MAXPAGESIZE];
! 650: static u_char roob[FLASH_MAXOOBSIZE];
! 651: int error;
! 652:
! 653: error = flash_chip_read_page(sc, pageno, rbuf,
! 654: oob == NULL ? NULL : roob);
! 655: if (error != 0)
! 656: return error;
! 657:
! 658: if (memcmp((const void *)&rbuf[0], (const void *)data,
! 659: sc->sc_flashdev->pagesize) != 0)
! 660: return EIO;
! 661:
! 662: if (oob != NULL && memcmp((const void *)&roob[0],
! 663: (const void *)oob, sc->sc_flashdev->oobsize) != 0)
! 664: return EIO;
! 665:
! 666: return 0;
! 667: }
! 668:
! 669: /*
! 670: * Block device functions
! 671: */
! 672:
! 673: int
! 674: flashopen(dev_t dev, int oflags, int devtype, struct proc *p)
! 675: {
! 676: struct flash_softc *sc;
! 677: int error;
! 678: int part;
! 679:
! 680: sc = flashlookup(flashunit(dev));
! 681: if (sc == NULL)
! 682: return ENXIO;
! 683:
! 684: if ((error = flashlock(sc)) != 0) {
! 685: device_unref(&sc->sc_dev);
! 686: return error;
! 687: }
! 688:
! 689: /*
! 690: * If no partition is open load the partition info if it is
! 691: * not already valid. If partitions are already open, allow
! 692: * opens only for the same kind of device.
! 693: */
! 694: if (sc->sc_dk.dk_openmask == 0) {
! 695: if ((sc->sc_flags & FDK_LOADED) == 0 ||
! 696: ((sc->sc_flags & FDK_SAFE) == 0) !=
! 697: (flashsafe(dev) == 0)) {
! 698: sc->sc_flags &= ~FDK_SAFE;
! 699: sc->sc_flags |= FDK_LOADED;
! 700: if (flashsafe(dev))
! 701: sc->sc_flags |= FDK_SAFE;
! 702: flashgetdisklabel(dev, sc, sc->sc_dk.dk_label, 0);
! 703: }
! 704: } else if (((sc->sc_flags & FDK_SAFE) == 0) !=
! 705: (flashsafe(dev) == 0)) {
! 706: flashunlock(sc);
! 707: device_unref(&sc->sc_dev);
! 708: return EBUSY;
! 709: }
! 710:
! 711: /* Check that the partition exists. */
! 712: part = flashpart(dev);
! 713: if (part != RAW_PART &&
! 714: (part >= sc->sc_dk.dk_label->d_npartitions ||
! 715: sc->sc_dk.dk_label->d_partitions[part].p_fstype == FS_UNUSED)) {
! 716: flashunlock(sc);
! 717: device_unref(&sc->sc_dev);
! 718: return ENXIO;
! 719: }
! 720:
! 721: /* Prevent our unit from being deconfigured while open. */
! 722: switch (devtype) {
! 723: case S_IFCHR:
! 724: sc->sc_dk.dk_copenmask |= (1 << part);
! 725: break;
! 726: case S_IFBLK:
! 727: sc->sc_dk.dk_bopenmask |= (1 << part);
! 728: break;
! 729: }
! 730: sc->sc_dk.dk_openmask =
! 731: sc->sc_dk.dk_copenmask | sc->sc_dk.dk_bopenmask;
! 732:
! 733: flashunlock(sc);
! 734: device_unref(&sc->sc_dev);
! 735: return 0;
! 736: }
! 737:
! 738: int
! 739: flashclose(dev_t dev, int fflag, int devtype, struct proc *p)
! 740: {
! 741: struct flash_softc *sc;
! 742: int error;
! 743: int part;
! 744:
! 745: sc = flashlookup(flashunit(dev));
! 746: if (sc == NULL)
! 747: return ENXIO;
! 748:
! 749: if ((error = flashlock(sc)) != 0) {
! 750: device_unref(&sc->sc_dev);
! 751: return error;
! 752: }
! 753:
! 754: /* Close one open partition. */
! 755: part = flashpart(dev);
! 756: switch (devtype) {
! 757: case S_IFCHR:
! 758: sc->sc_dk.dk_copenmask &= ~(1 << part);
! 759: break;
! 760: case S_IFBLK:
! 761: sc->sc_dk.dk_bopenmask &= ~(1 << part);
! 762: break;
! 763: }
! 764: sc->sc_dk.dk_openmask =
! 765: sc->sc_dk.dk_copenmask | sc->sc_dk.dk_bopenmask;
! 766:
! 767: if (sc->sc_dk.dk_openmask == 0) {
! 768: /* XXX wait for I/O to complete? */
! 769: }
! 770:
! 771: flashunlock(sc);
! 772: device_unref(&sc->sc_dev);
! 773: return 0;
! 774: }
! 775:
! 776: /*
! 777: * Queue the transfer of one or more flash pages.
! 778: */
! 779: void
! 780: flashstrategy(struct buf *bp)
! 781: {
! 782: struct flash_softc *sc;
! 783: int s;
! 784:
! 785: sc = flashlookup(flashunit(bp->b_dev));
! 786: if (sc == NULL) {
! 787: bp->b_error = ENXIO;
! 788: goto bad;
! 789: }
! 790:
! 791: /* Transfer only a multiple of the flash page size. */
! 792: if ((bp->b_bcount % sc->sc_flashdev->pagesize) != 0) {
! 793: bp->b_error = EINVAL;
! 794: goto bad;
! 795: }
! 796:
! 797: /* If the device has been invalidated, error out. */
! 798: if ((sc->sc_flags & FDK_LOADED) == 0) {
! 799: bp->b_error = EIO;
! 800: goto bad;
! 801: }
! 802:
! 803: /* Translate logical block numbers to physical. */
! 804: if (flashsafe(bp->b_dev) && flashsafestrategy(sc, bp) <= 0)
! 805: goto done;
! 806:
! 807: /* Return immediately if it is a null transfer. */
! 808: if (bp->b_bcount == 0)
! 809: goto done;
! 810:
! 811: /* Do bounds checking on partitions. */
! 812: if (flashpart(bp->b_dev) != RAW_PART &&
! 813: bounds_check_with_label(bp, sc->sc_dk.dk_label, 0) <= 0)
! 814: goto done;
! 815:
! 816: /* Queue the transfer. */
! 817: s = splbio();
! 818: disksort(&sc->sc_q, bp);
! 819: flashstart(sc);
! 820: splx(s);
! 821: device_unref(&sc->sc_dev);
! 822: return;
! 823:
! 824: bad:
! 825: bp->b_flags |= B_ERROR;
! 826: done:
! 827: if ((bp->b_flags & B_ERROR) != 0)
! 828: bp->b_resid = bp->b_bcount;
! 829: s = splbio();
! 830: biodone(bp);
! 831: splx(s);
! 832: if (sc != NULL)
! 833: device_unref(&sc->sc_dev);
! 834: }
! 835:
! 836: int
! 837: flashioctl(dev_t dev, u_long cmd, caddr_t data, int fflag, struct proc *p)
! 838: {
! 839: struct flash_softc *sc;
! 840: int error = 0;
! 841:
! 842: sc = flashlookup(flashunit(dev));
! 843: if (sc == NULL)
! 844: return ENXIO;
! 845:
! 846: if ((sc->sc_flags & FDK_LOADED) == 0) {
! 847: device_unref(&sc->sc_dev);
! 848: return EIO;
! 849: }
! 850:
! 851: switch (cmd) {
! 852: case DIOCGDINFO:
! 853: *(struct disklabel *)data = *sc->sc_dk.dk_label;
! 854: break;
! 855: default:
! 856: error = ENOTTY;
! 857: break;
! 858: }
! 859:
! 860: device_unref(&sc->sc_dev);
! 861: return error;
! 862: }
! 863:
! 864: int
! 865: flashdump(dev_t dev, daddr64_t blkno, caddr_t va, size_t size)
! 866: {
! 867: printf("flashdump\n");
! 868: return ENODEV;
! 869: }
! 870:
! 871: daddr64_t
! 872: flashsize(dev_t dev)
! 873: {
! 874: printf("flashsize\n");
! 875: return ENODEV;
! 876: }
! 877:
! 878: void
! 879: flashstart(struct flash_softc *sc)
! 880: {
! 881: struct buf *dp, *bp;
! 882:
! 883: while (1) {
! 884: /* Remove the next buffer from the queue or stop. */
! 885: dp = &sc->sc_q;
! 886: bp = dp->b_actf;
! 887: if (bp == NULL)
! 888: return;
! 889: dp->b_actf = bp->b_actf;
! 890:
! 891: /* Transfer this buffer now. */
! 892: _flashstart(sc, bp);
! 893: }
! 894: }
! 895:
! 896: void
! 897: _flashstart(struct flash_softc *sc, struct buf *bp)
! 898: {
! 899: int part;
! 900: daddr64_t offset;
! 901: long pgno;
! 902:
! 903: part = flashpart(bp->b_dev);
! 904: offset = DL_GETPOFFSET(&sc->sc_dk.dk_label->d_partitions[part]) +
! 905: bp->b_blkno;
! 906: pgno = offset / (sc->sc_flashdev->pagesize / DEV_BSIZE);
! 907:
! 908: /*
! 909: * If the requested page is exactly at the end of flash and it
! 910: * is an "unsafe" device, return EOF, else error out.
! 911: */
! 912: if (!flashsafe(bp->b_dev) && pgno == sc->sc_flashdev->capacity) {
! 913: bp->b_resid = bp->b_bcount;
! 914: biodone(bp);
! 915: return;
! 916: } else if (pgno >= sc->sc_flashdev->capacity) {
! 917: bp->b_error = EINVAL;
! 918: bp->b_flags |= B_ERROR;
! 919: biodone(bp);
! 920: return;
! 921: }
! 922:
! 923: sc->sc_bp = bp;
! 924:
! 925: /* Instrumentation. */
! 926: disk_busy(&sc->sc_dk);
! 927:
! 928: /* XXX this should be done asynchronously. */
! 929: flash_chip_enable(sc);
! 930: if ((bp->b_flags & B_READ) != 0)
! 931: bp->b_error = flash_chip_read_page(sc, pgno, bp->b_data,
! 932: NULL);
! 933: else
! 934: bp->b_error = flash_chip_write_page(sc, pgno, bp->b_data,
! 935: NULL);
! 936: if (bp->b_error == 0)
! 937: bp->b_resid = bp->b_bcount - sc->sc_flashdev->pagesize;
! 938: flash_chip_disable(sc);
! 939: flashdone(sc);
! 940: }
! 941:
! 942: void
! 943: flashdone(void *v)
! 944: {
! 945: struct flash_softc *sc = v;
! 946: struct buf *bp = sc->sc_bp;
! 947:
! 948: /* Instrumentation. */
! 949: disk_unbusy(&sc->sc_dk, bp->b_bcount - bp->b_resid,
! 950: (bp->b_flags & B_READ) != 0);
! 951:
! 952: if (bp->b_error != 0)
! 953: bp->b_flags |= B_ERROR;
! 954:
! 955: biodone(bp);
! 956: flashstart(sc);
! 957: }
! 958:
! 959: void
! 960: flashgetdefaultlabel(dev_t dev, struct flash_softc *sc,
! 961: struct disklabel *lp)
! 962: {
! 963: size_t len;
! 964:
! 965: bzero(lp, sizeof(struct disklabel));
! 966:
! 967: lp->d_type = 0;
! 968: lp->d_subtype = 0;
! 969: strncpy(lp->d_typename, "NAND flash", sizeof(lp->d_typename));
! 970:
! 971: /* Use the product name up to the first space. */
! 972: strncpy(lp->d_packname, sc->sc_flashdev->longname,
! 973: sizeof(lp->d_packname));
! 974: for (len = 0; len < sizeof(lp->d_packname); len++)
! 975: if (lp->d_packname[len] == ' ') {
! 976: lp->d_packname[len] = '\0';
! 977: break;
! 978: }
! 979:
! 980: /* Fake the disk geometry. */
! 981: lp->d_ncylinders = 1;
! 982: lp->d_ntracks = 16;
! 983: lp->d_secsize = sc->sc_flashdev->pagesize;
! 984: lp->d_nsectors = sc->sc_flashdev->capacity / lp->d_ntracks
! 985: / lp->d_ncylinders;
! 986: lp->d_secpercyl = lp->d_ntracks * lp->d_nsectors;
! 987: DL_SETDSIZE(lp, (daddr64_t)lp->d_ncylinders * lp->d_secpercyl);
! 988:
! 989: /* Fake hardware characteristics. */
! 990: lp->d_rpm = 3600;
! 991: lp->d_interleave = 1;
! 992: lp->d_version = 1;
! 993:
! 994: /* XXX these values assume ffs. */
! 995: lp->d_bbsize = BBSIZE;
! 996: lp->d_sbsize = SBSIZE;
! 997:
! 998: /* Wrap it up. */
! 999: lp->d_magic = DISKMAGIC;
! 1000: lp->d_magic2 = DISKMAGIC;
! 1001: lp->d_checksum = dkcksum(lp);
! 1002: }
! 1003:
! 1004: void
! 1005: flashgetdisklabel(dev_t dev, struct flash_softc *sc,
! 1006: struct disklabel *lp, int spoofonly)
! 1007: {
! 1008: char *errstring;
! 1009: dev_t labeldev;
! 1010:
! 1011: flashgetdefaultlabel(dev, sc, lp);
! 1012:
! 1013: if (sc->sc_tag->default_disklabel != NULL)
! 1014: sc->sc_tag->default_disklabel(sc->sc_cookie, dev, lp);
! 1015:
! 1016: /* Call the generic disklabel extraction routine. */
! 1017: labeldev = flashlabeldev(dev);
! 1018: errstring = readdisklabel(labeldev, flashstrategy, lp, spoofonly);
! 1019: if (errstring != NULL) {
! 1020: /*printf("%s: %s\n", sc->sc_dev.dv_xname, errstring);*/
! 1021: }
! 1022: }
! 1023:
! 1024: /*
! 1025: * Character device functions
! 1026: */
! 1027:
! 1028: void
! 1029: flashminphys(struct buf *bp)
! 1030: {
! 1031: struct flash_softc *sc;
! 1032:
! 1033: sc = flashlookup(flashunit(bp->b_dev));
! 1034:
! 1035: if (bp->b_bcount > sc->sc_flashdev->pagesize)
! 1036: bp->b_bcount = sc->sc_flashdev->pagesize;
! 1037: }
! 1038:
! 1039: int
! 1040: flashread(dev_t dev, struct uio *uio, int ioflag)
! 1041: {
! 1042: return physio(flashstrategy, NULL, dev, B_READ, flashminphys, uio);
! 1043: }
! 1044:
! 1045: int
! 1046: flashwrite(dev_t dev, struct uio *uio, int ioflag)
! 1047: {
! 1048: return physio(flashstrategy, NULL, dev, B_WRITE, flashminphys, uio);
! 1049: }
! 1050:
! 1051: /*
! 1052: * Physical access strategy "fixup" routines for transparent bad
! 1053: * blocks management, wear-leveling, etc.
! 1054: */
! 1055:
! 1056: /*
! 1057: * Call the machine-specific routine if there is any or use just a
! 1058: * default strategy for bad blocks management.
! 1059: */
! 1060: int
! 1061: flashsafestrategy(struct flash_softc *sc, struct buf *bp)
! 1062: {
! 1063: if (sc->sc_tag->safe_strategy) {
! 1064: return sc->sc_tag->safe_strategy(sc->sc_cookie, bp);
! 1065: }
! 1066:
! 1067: /* XXX no default bad blocks management strategy yet */
! 1068: return 1;
! 1069: }
! 1070:
! 1071: void dumppage(u_char *);
! 1072: void dumppage(u_char *buf)
! 1073: {
! 1074: int i;
! 1075: for (i = 0; i < 512; i++) {
! 1076: if ((i % 16) == 0)
! 1077: printf("%04x: ", i);
! 1078: if ((i % 16) == 8)
! 1079: printf(" ");
! 1080: printf(" %02x", buf[i]);
! 1081: if ((i % 16) == 15)
! 1082: printf("\n");
! 1083: }
! 1084: if ((i % 16) != 0)
! 1085: printf("\n");
! 1086: }
CVSweb