|
|
1.1 ! root 1: /* ! 2: * Flash NAND memory emulation. Based on "16M x 8 Bit NAND Flash ! 3: * Memory" datasheet for the KM29U128AT / K9F2808U0A chips from ! 4: * Samsung Electronic. ! 5: * ! 6: * Copyright (c) 2006 Openedhand Ltd. ! 7: * Written by Andrzej Zaborowski <[email protected]> ! 8: * ! 9: * This code is licensed under the GNU GPL v2. ! 10: */ ! 11: ! 12: #ifndef NAND_IO ! 13: ! 14: # include "hw.h" ! 15: # include "flash.h" ! 16: # include "block.h" ! 17: /* FIXME: Pass block device as an argument. */ ! 18: # include "sysemu.h" ! 19: ! 20: # define NAND_CMD_READ0 0x00 ! 21: # define NAND_CMD_READ1 0x01 ! 22: # define NAND_CMD_READ2 0x50 ! 23: # define NAND_CMD_LPREAD2 0x30 ! 24: # define NAND_CMD_NOSERIALREAD2 0x35 ! 25: # define NAND_CMD_RANDOMREAD1 0x05 ! 26: # define NAND_CMD_RANDOMREAD2 0xe0 ! 27: # define NAND_CMD_READID 0x90 ! 28: # define NAND_CMD_RESET 0xff ! 29: # define NAND_CMD_PAGEPROGRAM1 0x80 ! 30: # define NAND_CMD_PAGEPROGRAM2 0x10 ! 31: # define NAND_CMD_CACHEPROGRAM2 0x15 ! 32: # define NAND_CMD_BLOCKERASE1 0x60 ! 33: # define NAND_CMD_BLOCKERASE2 0xd0 ! 34: # define NAND_CMD_READSTATUS 0x70 ! 35: # define NAND_CMD_COPYBACKPRG1 0x85 ! 36: ! 37: # define NAND_IOSTATUS_ERROR (1 << 0) ! 38: # define NAND_IOSTATUS_PLANE0 (1 << 1) ! 39: # define NAND_IOSTATUS_PLANE1 (1 << 2) ! 40: # define NAND_IOSTATUS_PLANE2 (1 << 3) ! 41: # define NAND_IOSTATUS_PLANE3 (1 << 4) ! 42: # define NAND_IOSTATUS_BUSY (1 << 6) ! 43: # define NAND_IOSTATUS_UNPROTCT (1 << 7) ! 44: ! 45: # define MAX_PAGE 0x800 ! 46: # define MAX_OOB 0x40 ! 47: ! 48: struct nand_flash_s { ! 49: uint8_t manf_id, chip_id; ! 50: int size, pages; ! 51: int page_shift, oob_shift, erase_shift, addr_shift; ! 52: uint8_t *storage; ! 53: BlockDriverState *bdrv; ! 54: int mem_oob; ! 55: ! 56: int cle, ale, ce, wp, gnd; ! 57: ! 58: uint8_t io[MAX_PAGE + MAX_OOB + 0x400]; ! 59: uint8_t *ioaddr; ! 60: int iolen; ! 61: ! 62: uint32_t cmd, addr; ! 63: int addrlen; ! 64: int status; ! 65: int offset; ! 66: ! 67: void (*blk_write)(struct nand_flash_s *s); ! 68: void (*blk_erase)(struct nand_flash_s *s); ! 69: void (*blk_load)(struct nand_flash_s *s, uint32_t addr, int offset); ! 70: }; ! 71: ! 72: # define NAND_NO_AUTOINCR 0x00000001 ! 73: # define NAND_BUSWIDTH_16 0x00000002 ! 74: # define NAND_NO_PADDING 0x00000004 ! 75: # define NAND_CACHEPRG 0x00000008 ! 76: # define NAND_COPYBACK 0x00000010 ! 77: # define NAND_IS_AND 0x00000020 ! 78: # define NAND_4PAGE_ARRAY 0x00000040 ! 79: # define NAND_NO_READRDY 0x00000100 ! 80: # define NAND_SAMSUNG_LP (NAND_NO_PADDING | NAND_COPYBACK) ! 81: ! 82: # define NAND_IO ! 83: ! 84: # define PAGE(addr) ((addr) >> ADDR_SHIFT) ! 85: # define PAGE_START(page) (PAGE(page) * (PAGE_SIZE + OOB_SIZE)) ! 86: # define PAGE_MASK ((1 << ADDR_SHIFT) - 1) ! 87: # define OOB_SHIFT (PAGE_SHIFT - 5) ! 88: # define OOB_SIZE (1 << OOB_SHIFT) ! 89: # define SECTOR(addr) ((addr) >> (9 + ADDR_SHIFT - PAGE_SHIFT)) ! 90: # define SECTOR_OFFSET(addr) ((addr) & ((511 >> PAGE_SHIFT) << 8)) ! 91: ! 92: # define PAGE_SIZE 256 ! 93: # define PAGE_SHIFT 8 ! 94: # define PAGE_SECTORS 1 ! 95: # define ADDR_SHIFT 8 ! 96: # include "nand.c" ! 97: # define PAGE_SIZE 512 ! 98: # define PAGE_SHIFT 9 ! 99: # define PAGE_SECTORS 1 ! 100: # define ADDR_SHIFT 8 ! 101: # include "nand.c" ! 102: # define PAGE_SIZE 2048 ! 103: # define PAGE_SHIFT 11 ! 104: # define PAGE_SECTORS 4 ! 105: # define ADDR_SHIFT 16 ! 106: # include "nand.c" ! 107: ! 108: /* Information based on Linux drivers/mtd/nand/nand_ids.c */ ! 109: struct nand_info_s { ! 110: int size; ! 111: int width; ! 112: int page_shift; ! 113: int erase_shift; ! 114: uint32_t options; ! 115: } nand_flash_ids[0x100] = { ! 116: [0 ... 0xff] = { 0 }, ! 117: ! 118: [0x6e] = { 1, 8, 8, 4, 0 }, ! 119: [0x64] = { 2, 8, 8, 4, 0 }, ! 120: [0x6b] = { 4, 8, 9, 4, 0 }, ! 121: [0xe8] = { 1, 8, 8, 4, 0 }, ! 122: [0xec] = { 1, 8, 8, 4, 0 }, ! 123: [0xea] = { 2, 8, 8, 4, 0 }, ! 124: [0xd5] = { 4, 8, 9, 4, 0 }, ! 125: [0xe3] = { 4, 8, 9, 4, 0 }, ! 126: [0xe5] = { 4, 8, 9, 4, 0 }, ! 127: [0xd6] = { 8, 8, 9, 4, 0 }, ! 128: ! 129: [0x39] = { 8, 8, 9, 4, 0 }, ! 130: [0xe6] = { 8, 8, 9, 4, 0 }, ! 131: [0x49] = { 8, 16, 9, 4, NAND_BUSWIDTH_16 }, ! 132: [0x59] = { 8, 16, 9, 4, NAND_BUSWIDTH_16 }, ! 133: ! 134: [0x33] = { 16, 8, 9, 5, 0 }, ! 135: [0x73] = { 16, 8, 9, 5, 0 }, ! 136: [0x43] = { 16, 16, 9, 5, NAND_BUSWIDTH_16 }, ! 137: [0x53] = { 16, 16, 9, 5, NAND_BUSWIDTH_16 }, ! 138: ! 139: [0x35] = { 32, 8, 9, 5, 0 }, ! 140: [0x75] = { 32, 8, 9, 5, 0 }, ! 141: [0x45] = { 32, 16, 9, 5, NAND_BUSWIDTH_16 }, ! 142: [0x55] = { 32, 16, 9, 5, NAND_BUSWIDTH_16 }, ! 143: ! 144: [0x36] = { 64, 8, 9, 5, 0 }, ! 145: [0x76] = { 64, 8, 9, 5, 0 }, ! 146: [0x46] = { 64, 16, 9, 5, NAND_BUSWIDTH_16 }, ! 147: [0x56] = { 64, 16, 9, 5, NAND_BUSWIDTH_16 }, ! 148: ! 149: [0x78] = { 128, 8, 9, 5, 0 }, ! 150: [0x39] = { 128, 8, 9, 5, 0 }, ! 151: [0x79] = { 128, 8, 9, 5, 0 }, ! 152: [0x72] = { 128, 16, 9, 5, NAND_BUSWIDTH_16 }, ! 153: [0x49] = { 128, 16, 9, 5, NAND_BUSWIDTH_16 }, ! 154: [0x74] = { 128, 16, 9, 5, NAND_BUSWIDTH_16 }, ! 155: [0x59] = { 128, 16, 9, 5, NAND_BUSWIDTH_16 }, ! 156: ! 157: [0x71] = { 256, 8, 9, 5, 0 }, ! 158: ! 159: /* ! 160: * These are the new chips with large page size. The pagesize and the ! 161: * erasesize is determined from the extended id bytes ! 162: */ ! 163: # define LP_OPTIONS (NAND_SAMSUNG_LP | NAND_NO_READRDY | NAND_NO_AUTOINCR) ! 164: # define LP_OPTIONS16 (LP_OPTIONS | NAND_BUSWIDTH_16) ! 165: ! 166: /* 512 Megabit */ ! 167: [0xa2] = { 64, 8, 0, 0, LP_OPTIONS }, ! 168: [0xf2] = { 64, 8, 0, 0, LP_OPTIONS }, ! 169: [0xb2] = { 64, 16, 0, 0, LP_OPTIONS16 }, ! 170: [0xc2] = { 64, 16, 0, 0, LP_OPTIONS16 }, ! 171: ! 172: /* 1 Gigabit */ ! 173: [0xa1] = { 128, 8, 0, 0, LP_OPTIONS }, ! 174: [0xf1] = { 128, 8, 0, 0, LP_OPTIONS }, ! 175: [0xb1] = { 128, 16, 0, 0, LP_OPTIONS16 }, ! 176: [0xc1] = { 128, 16, 0, 0, LP_OPTIONS16 }, ! 177: ! 178: /* 2 Gigabit */ ! 179: [0xaa] = { 256, 8, 0, 0, LP_OPTIONS }, ! 180: [0xda] = { 256, 8, 0, 0, LP_OPTIONS }, ! 181: [0xba] = { 256, 16, 0, 0, LP_OPTIONS16 }, ! 182: [0xca] = { 256, 16, 0, 0, LP_OPTIONS16 }, ! 183: ! 184: /* 4 Gigabit */ ! 185: [0xac] = { 512, 8, 0, 0, LP_OPTIONS }, ! 186: [0xdc] = { 512, 8, 0, 0, LP_OPTIONS }, ! 187: [0xbc] = { 512, 16, 0, 0, LP_OPTIONS16 }, ! 188: [0xcc] = { 512, 16, 0, 0, LP_OPTIONS16 }, ! 189: ! 190: /* 8 Gigabit */ ! 191: [0xa3] = { 1024, 8, 0, 0, LP_OPTIONS }, ! 192: [0xd3] = { 1024, 8, 0, 0, LP_OPTIONS }, ! 193: [0xb3] = { 1024, 16, 0, 0, LP_OPTIONS16 }, ! 194: [0xc3] = { 1024, 16, 0, 0, LP_OPTIONS16 }, ! 195: ! 196: /* 16 Gigabit */ ! 197: [0xa5] = { 2048, 8, 0, 0, LP_OPTIONS }, ! 198: [0xd5] = { 2048, 8, 0, 0, LP_OPTIONS }, ! 199: [0xb5] = { 2048, 16, 0, 0, LP_OPTIONS16 }, ! 200: [0xc5] = { 2048, 16, 0, 0, LP_OPTIONS16 }, ! 201: }; ! 202: ! 203: static void nand_reset(struct nand_flash_s *s) ! 204: { ! 205: s->cmd = NAND_CMD_READ0; ! 206: s->addr = 0; ! 207: s->addrlen = 0; ! 208: s->iolen = 0; ! 209: s->offset = 0; ! 210: s->status &= NAND_IOSTATUS_UNPROTCT; ! 211: } ! 212: ! 213: static void nand_command(struct nand_flash_s *s) ! 214: { ! 215: switch (s->cmd) { ! 216: case NAND_CMD_READ0: ! 217: s->iolen = 0; ! 218: break; ! 219: ! 220: case NAND_CMD_READID: ! 221: s->io[0] = s->manf_id; ! 222: s->io[1] = s->chip_id; ! 223: s->io[2] = 'Q'; /* Don't-care byte (often 0xa5) */ ! 224: if (nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) ! 225: s->io[3] = 0x15; /* Page Size, Block Size, Spare Size.. */ ! 226: else ! 227: s->io[3] = 0xc0; /* Multi-plane */ ! 228: s->ioaddr = s->io; ! 229: s->iolen = 4; ! 230: break; ! 231: ! 232: case NAND_CMD_RANDOMREAD2: ! 233: case NAND_CMD_NOSERIALREAD2: ! 234: if (!(nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP)) ! 235: break; ! 236: ! 237: s->blk_load(s, s->addr, s->addr & ((1 << s->addr_shift) - 1)); ! 238: break; ! 239: ! 240: case NAND_CMD_RESET: ! 241: nand_reset(s); ! 242: break; ! 243: ! 244: case NAND_CMD_PAGEPROGRAM1: ! 245: s->ioaddr = s->io; ! 246: s->iolen = 0; ! 247: break; ! 248: ! 249: case NAND_CMD_PAGEPROGRAM2: ! 250: if (s->wp) { ! 251: s->blk_write(s); ! 252: } ! 253: break; ! 254: ! 255: case NAND_CMD_BLOCKERASE1: ! 256: break; ! 257: ! 258: case NAND_CMD_BLOCKERASE2: ! 259: if (nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) ! 260: s->addr <<= 16; ! 261: else ! 262: s->addr <<= 8; ! 263: ! 264: if (s->wp) { ! 265: s->blk_erase(s); ! 266: } ! 267: break; ! 268: ! 269: case NAND_CMD_READSTATUS: ! 270: s->io[0] = s->status; ! 271: s->ioaddr = s->io; ! 272: s->iolen = 1; ! 273: break; ! 274: ! 275: default: ! 276: printf("%s: Unknown NAND command 0x%02x\n", __FUNCTION__, s->cmd); ! 277: } ! 278: } ! 279: ! 280: static void nand_save(QEMUFile *f, void *opaque) ! 281: { ! 282: struct nand_flash_s *s = (struct nand_flash_s *) opaque; ! 283: qemu_put_byte(f, s->cle); ! 284: qemu_put_byte(f, s->ale); ! 285: qemu_put_byte(f, s->ce); ! 286: qemu_put_byte(f, s->wp); ! 287: qemu_put_byte(f, s->gnd); ! 288: qemu_put_buffer(f, s->io, sizeof(s->io)); ! 289: qemu_put_be32(f, s->ioaddr - s->io); ! 290: qemu_put_be32(f, s->iolen); ! 291: ! 292: qemu_put_be32s(f, &s->cmd); ! 293: qemu_put_be32s(f, &s->addr); ! 294: qemu_put_be32(f, s->addrlen); ! 295: qemu_put_be32(f, s->status); ! 296: qemu_put_be32(f, s->offset); ! 297: /* XXX: do we want to save s->storage too? */ ! 298: } ! 299: ! 300: static int nand_load(QEMUFile *f, void *opaque, int version_id) ! 301: { ! 302: struct nand_flash_s *s = (struct nand_flash_s *) opaque; ! 303: s->cle = qemu_get_byte(f); ! 304: s->ale = qemu_get_byte(f); ! 305: s->ce = qemu_get_byte(f); ! 306: s->wp = qemu_get_byte(f); ! 307: s->gnd = qemu_get_byte(f); ! 308: qemu_get_buffer(f, s->io, sizeof(s->io)); ! 309: s->ioaddr = s->io + qemu_get_be32(f); ! 310: s->iolen = qemu_get_be32(f); ! 311: if (s->ioaddr >= s->io + sizeof(s->io) || s->ioaddr < s->io) ! 312: return -EINVAL; ! 313: ! 314: qemu_get_be32s(f, &s->cmd); ! 315: qemu_get_be32s(f, &s->addr); ! 316: s->addrlen = qemu_get_be32(f); ! 317: s->status = qemu_get_be32(f); ! 318: s->offset = qemu_get_be32(f); ! 319: return 0; ! 320: } ! 321: ! 322: static int nand_iid = 0; ! 323: ! 324: /* ! 325: * Chip inputs are CLE, ALE, CE, WP, GND and eight I/O pins. Chip ! 326: * outputs are R/B and eight I/O pins. ! 327: * ! 328: * CE, WP and R/B are active low. ! 329: */ ! 330: void nand_setpins(struct nand_flash_s *s, ! 331: int cle, int ale, int ce, int wp, int gnd) ! 332: { ! 333: s->cle = cle; ! 334: s->ale = ale; ! 335: s->ce = ce; ! 336: s->wp = wp; ! 337: s->gnd = gnd; ! 338: if (wp) ! 339: s->status |= NAND_IOSTATUS_UNPROTCT; ! 340: else ! 341: s->status &= ~NAND_IOSTATUS_UNPROTCT; ! 342: } ! 343: ! 344: void nand_getpins(struct nand_flash_s *s, int *rb) ! 345: { ! 346: *rb = 1; ! 347: } ! 348: ! 349: void nand_setio(struct nand_flash_s *s, uint8_t value) ! 350: { ! 351: if (!s->ce && s->cle) { ! 352: if (nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) { ! 353: if (s->cmd == NAND_CMD_READ0 && value == NAND_CMD_LPREAD2) ! 354: return; ! 355: if (value == NAND_CMD_RANDOMREAD1) { ! 356: s->addr &= ~((1 << s->addr_shift) - 1); ! 357: s->addrlen = 0; ! 358: return; ! 359: } ! 360: } ! 361: if (value == NAND_CMD_READ0) ! 362: s->offset = 0; ! 363: else if (value == NAND_CMD_READ1) { ! 364: s->offset = 0x100; ! 365: value = NAND_CMD_READ0; ! 366: } ! 367: else if (value == NAND_CMD_READ2) { ! 368: s->offset = 1 << s->page_shift; ! 369: value = NAND_CMD_READ0; ! 370: } ! 371: ! 372: s->cmd = value; ! 373: ! 374: if (s->cmd == NAND_CMD_READSTATUS || ! 375: s->cmd == NAND_CMD_PAGEPROGRAM2 || ! 376: s->cmd == NAND_CMD_BLOCKERASE1 || ! 377: s->cmd == NAND_CMD_BLOCKERASE2 || ! 378: s->cmd == NAND_CMD_NOSERIALREAD2 || ! 379: s->cmd == NAND_CMD_RANDOMREAD2 || ! 380: s->cmd == NAND_CMD_RESET) ! 381: nand_command(s); ! 382: ! 383: if (s->cmd != NAND_CMD_RANDOMREAD2) { ! 384: s->addrlen = 0; ! 385: s->addr = 0; ! 386: } ! 387: } ! 388: ! 389: if (s->ale) { ! 390: s->addr |= value << (s->addrlen * 8); ! 391: s->addrlen ++; ! 392: ! 393: if (s->addrlen == 1 && s->cmd == NAND_CMD_READID) ! 394: nand_command(s); ! 395: ! 396: if (!(nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) && ! 397: s->addrlen == 3 && ( ! 398: s->cmd == NAND_CMD_READ0 || ! 399: s->cmd == NAND_CMD_PAGEPROGRAM1)) ! 400: nand_command(s); ! 401: if ((nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) && ! 402: s->addrlen == 4 && ( ! 403: s->cmd == NAND_CMD_READ0 || ! 404: s->cmd == NAND_CMD_PAGEPROGRAM1)) ! 405: nand_command(s); ! 406: } ! 407: ! 408: if (!s->cle && !s->ale && s->cmd == NAND_CMD_PAGEPROGRAM1) { ! 409: if (s->iolen < (1 << s->page_shift) + (1 << s->oob_shift)) ! 410: s->io[s->iolen ++] = value; ! 411: } else if (!s->cle && !s->ale && s->cmd == NAND_CMD_COPYBACKPRG1) { ! 412: if ((s->addr & ((1 << s->addr_shift) - 1)) < ! 413: (1 << s->page_shift) + (1 << s->oob_shift)) { ! 414: s->io[s->iolen + (s->addr & ((1 << s->addr_shift) - 1))] = value; ! 415: s->addr ++; ! 416: } ! 417: } ! 418: } ! 419: ! 420: uint8_t nand_getio(struct nand_flash_s *s) ! 421: { ! 422: int offset; ! 423: ! 424: /* Allow sequential reading */ ! 425: if (!s->iolen && s->cmd == NAND_CMD_READ0) { ! 426: offset = (s->addr & ((1 << s->addr_shift) - 1)) + s->offset; ! 427: s->offset = 0; ! 428: ! 429: s->blk_load(s, s->addr, offset); ! 430: if (s->gnd) ! 431: s->iolen = (1 << s->page_shift) - offset; ! 432: else ! 433: s->iolen = (1 << s->page_shift) + (1 << s->oob_shift) - offset; ! 434: } ! 435: ! 436: if (s->ce || s->iolen <= 0) ! 437: return 0; ! 438: ! 439: s->iolen --; ! 440: return *(s->ioaddr ++); ! 441: } ! 442: ! 443: struct nand_flash_s *nand_init(int manf_id, int chip_id) ! 444: { ! 445: int pagesize; ! 446: struct nand_flash_s *s; ! 447: int index; ! 448: ! 449: if (nand_flash_ids[chip_id].size == 0) { ! 450: cpu_abort(cpu_single_env, "%s: Unsupported NAND chip ID.\n", ! 451: __FUNCTION__); ! 452: } ! 453: index = drive_get_index(IF_MTD, 0, 0); ! 454: if (index == -1) { ! 455: cpu_abort(cpu_single_env, "%s: missing MTD device\n", ! 456: __FUNCTION__); ! 457: } ! 458: ! 459: s = (struct nand_flash_s *) qemu_mallocz(sizeof(struct nand_flash_s)); ! 460: s->bdrv = drives_table[index].bdrv; ! 461: s->manf_id = manf_id; ! 462: s->chip_id = chip_id; ! 463: s->size = nand_flash_ids[s->chip_id].size << 20; ! 464: if (nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) { ! 465: s->page_shift = 11; ! 466: s->erase_shift = 6; ! 467: } else { ! 468: s->page_shift = nand_flash_ids[s->chip_id].page_shift; ! 469: s->erase_shift = nand_flash_ids[s->chip_id].erase_shift; ! 470: } ! 471: ! 472: switch (1 << s->page_shift) { ! 473: case 256: ! 474: nand_init_256(s); ! 475: break; ! 476: case 512: ! 477: nand_init_512(s); ! 478: break; ! 479: case 2048: ! 480: nand_init_2048(s); ! 481: break; ! 482: default: ! 483: cpu_abort(cpu_single_env, "%s: Unsupported NAND block size.\n", ! 484: __FUNCTION__); ! 485: } ! 486: ! 487: pagesize = 1 << s->oob_shift; ! 488: s->mem_oob = 1; ! 489: if (s->bdrv && bdrv_getlength(s->bdrv) >= ! 490: (s->pages << s->page_shift) + (s->pages << s->oob_shift)) { ! 491: pagesize = 0; ! 492: s->mem_oob = 0; ! 493: } ! 494: ! 495: if (!s->bdrv) ! 496: pagesize += 1 << s->page_shift; ! 497: if (pagesize) ! 498: s->storage = (uint8_t *) memset(qemu_malloc(s->pages * pagesize), ! 499: 0xff, s->pages * pagesize); ! 500: ! 501: register_savevm("nand", nand_iid ++, 0, nand_save, nand_load, s); ! 502: ! 503: return s; ! 504: } ! 505: ! 506: void nand_done(struct nand_flash_s *s) ! 507: { ! 508: if (s->bdrv) { ! 509: bdrv_close(s->bdrv); ! 510: bdrv_delete(s->bdrv); ! 511: } ! 512: ! 513: if (!s->bdrv || s->mem_oob) ! 514: free(s->storage); ! 515: ! 516: free(s); ! 517: } ! 518: ! 519: #else ! 520: ! 521: /* Program a single page */ ! 522: static void glue(nand_blk_write_, PAGE_SIZE)(struct nand_flash_s *s) ! 523: { ! 524: uint32_t off, page, sector, soff; ! 525: uint8_t iobuf[(PAGE_SECTORS + 2) * 0x200]; ! 526: if (PAGE(s->addr) >= s->pages) ! 527: return; ! 528: ! 529: if (!s->bdrv) { ! 530: memcpy(s->storage + PAGE_START(s->addr) + (s->addr & PAGE_MASK) + ! 531: s->offset, s->io, s->iolen); ! 532: } else if (s->mem_oob) { ! 533: sector = SECTOR(s->addr); ! 534: off = (s->addr & PAGE_MASK) + s->offset; ! 535: soff = SECTOR_OFFSET(s->addr); ! 536: if (bdrv_read(s->bdrv, sector, iobuf, PAGE_SECTORS) == -1) { ! 537: printf("%s: read error in sector %i\n", __FUNCTION__, sector); ! 538: return; ! 539: } ! 540: ! 541: memcpy(iobuf + (soff | off), s->io, MIN(s->iolen, PAGE_SIZE - off)); ! 542: if (off + s->iolen > PAGE_SIZE) { ! 543: page = PAGE(s->addr); ! 544: memcpy(s->storage + (page << OOB_SHIFT), s->io + PAGE_SIZE - off, ! 545: MIN(OOB_SIZE, off + s->iolen - PAGE_SIZE)); ! 546: } ! 547: ! 548: if (bdrv_write(s->bdrv, sector, iobuf, PAGE_SECTORS) == -1) ! 549: printf("%s: write error in sector %i\n", __FUNCTION__, sector); ! 550: } else { ! 551: off = PAGE_START(s->addr) + (s->addr & PAGE_MASK) + s->offset; ! 552: sector = off >> 9; ! 553: soff = off & 0x1ff; ! 554: if (bdrv_read(s->bdrv, sector, iobuf, PAGE_SECTORS + 2) == -1) { ! 555: printf("%s: read error in sector %i\n", __FUNCTION__, sector); ! 556: return; ! 557: } ! 558: ! 559: memcpy(iobuf + soff, s->io, s->iolen); ! 560: ! 561: if (bdrv_write(s->bdrv, sector, iobuf, PAGE_SECTORS + 2) == -1) ! 562: printf("%s: write error in sector %i\n", __FUNCTION__, sector); ! 563: } ! 564: s->offset = 0; ! 565: } ! 566: ! 567: /* Erase a single block */ ! 568: static void glue(nand_blk_erase_, PAGE_SIZE)(struct nand_flash_s *s) ! 569: { ! 570: uint32_t i, page, addr; ! 571: uint8_t iobuf[0x200] = { [0 ... 0x1ff] = 0xff, }; ! 572: addr = s->addr & ~((1 << (ADDR_SHIFT + s->erase_shift)) - 1); ! 573: ! 574: if (PAGE(addr) >= s->pages) ! 575: return; ! 576: ! 577: if (!s->bdrv) { ! 578: memset(s->storage + PAGE_START(addr), ! 579: 0xff, (PAGE_SIZE + OOB_SIZE) << s->erase_shift); ! 580: } else if (s->mem_oob) { ! 581: memset(s->storage + (PAGE(addr) << OOB_SHIFT), ! 582: 0xff, OOB_SIZE << s->erase_shift); ! 583: i = SECTOR(addr); ! 584: page = SECTOR(addr + (ADDR_SHIFT + s->erase_shift)); ! 585: for (; i < page; i ++) ! 586: if (bdrv_write(s->bdrv, i, iobuf, 1) == -1) ! 587: printf("%s: write error in sector %i\n", __FUNCTION__, i); ! 588: } else { ! 589: addr = PAGE_START(addr); ! 590: page = addr >> 9; ! 591: if (bdrv_read(s->bdrv, page, iobuf, 1) == -1) ! 592: printf("%s: read error in sector %i\n", __FUNCTION__, page); ! 593: memset(iobuf + (addr & 0x1ff), 0xff, (~addr & 0x1ff) + 1); ! 594: if (bdrv_write(s->bdrv, page, iobuf, 1) == -1) ! 595: printf("%s: write error in sector %i\n", __FUNCTION__, page); ! 596: ! 597: memset(iobuf, 0xff, 0x200); ! 598: i = (addr & ~0x1ff) + 0x200; ! 599: for (addr += ((PAGE_SIZE + OOB_SIZE) << s->erase_shift) - 0x200; ! 600: i < addr; i += 0x200) ! 601: if (bdrv_write(s->bdrv, i >> 9, iobuf, 1) == -1) ! 602: printf("%s: write error in sector %i\n", __FUNCTION__, i >> 9); ! 603: ! 604: page = i >> 9; ! 605: if (bdrv_read(s->bdrv, page, iobuf, 1) == -1) ! 606: printf("%s: read error in sector %i\n", __FUNCTION__, page); ! 607: memset(iobuf, 0xff, ((addr - 1) & 0x1ff) + 1); ! 608: if (bdrv_write(s->bdrv, page, iobuf, 1) == -1) ! 609: printf("%s: write error in sector %i\n", __FUNCTION__, page); ! 610: } ! 611: } ! 612: ! 613: static void glue(nand_blk_load_, PAGE_SIZE)(struct nand_flash_s *s, ! 614: uint32_t addr, int offset) ! 615: { ! 616: if (PAGE(addr) >= s->pages) ! 617: return; ! 618: ! 619: if (s->bdrv) { ! 620: if (s->mem_oob) { ! 621: if (bdrv_read(s->bdrv, SECTOR(addr), s->io, PAGE_SECTORS) == -1) ! 622: printf("%s: read error in sector %i\n", ! 623: __FUNCTION__, SECTOR(addr)); ! 624: memcpy(s->io + SECTOR_OFFSET(s->addr) + PAGE_SIZE, ! 625: s->storage + (PAGE(s->addr) << OOB_SHIFT), ! 626: OOB_SIZE); ! 627: s->ioaddr = s->io + SECTOR_OFFSET(s->addr) + offset; ! 628: } else { ! 629: if (bdrv_read(s->bdrv, PAGE_START(addr) >> 9, ! 630: s->io, (PAGE_SECTORS + 2)) == -1) ! 631: printf("%s: read error in sector %i\n", ! 632: __FUNCTION__, PAGE_START(addr) >> 9); ! 633: s->ioaddr = s->io + (PAGE_START(addr) & 0x1ff) + offset; ! 634: } ! 635: } else { ! 636: memcpy(s->io, s->storage + PAGE_START(s->addr) + ! 637: offset, PAGE_SIZE + OOB_SIZE - offset); ! 638: s->ioaddr = s->io; ! 639: } ! 640: ! 641: s->addr &= PAGE_SIZE - 1; ! 642: s->addr += PAGE_SIZE; ! 643: } ! 644: ! 645: static void glue(nand_init_, PAGE_SIZE)(struct nand_flash_s *s) ! 646: { ! 647: s->oob_shift = PAGE_SHIFT - 5; ! 648: s->pages = s->size >> PAGE_SHIFT; ! 649: s->addr_shift = ADDR_SHIFT; ! 650: ! 651: s->blk_erase = glue(nand_blk_erase_, PAGE_SIZE); ! 652: s->blk_write = glue(nand_blk_write_, PAGE_SIZE); ! 653: s->blk_load = glue(nand_blk_load_, PAGE_SIZE); ! 654: } ! 655: ! 656: # undef PAGE_SIZE ! 657: # undef PAGE_SHIFT ! 658: # undef PAGE_SECTORS ! 659: # undef ADDR_SHIFT ! 660: #endif /* NAND_IO */
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.