|
|
1.1 root 1: /*
2: * CFI parallel flash with AMD command set emulation
1.1.1.3 root 3: *
1.1 root 4: * Copyright (c) 2005 Jocelyn Mayer
5: *
6: * This library is free software; you can redistribute it and/or
7: * modify it under the terms of the GNU Lesser General Public
8: * License as published by the Free Software Foundation; either
9: * version 2 of the License, or (at your option) any later version.
10: *
11: * This library is distributed in the hope that it will be useful,
12: * but WITHOUT ANY WARRANTY; without even the implied warranty of
13: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14: * Lesser General Public License for more details.
15: *
16: * You should have received a copy of the GNU Lesser General Public
1.1.1.5 ! root 17: * License along with this library; if not, see <http://www.gnu.org/licenses/>.
1.1 root 18: */
19:
20: /*
21: * For now, this code can emulate flashes of 1, 2 or 4 bytes width.
22: * Supported commands/modes are:
23: * - flash read
24: * - flash write
25: * - flash ID read
26: * - sector erase
27: * - chip erase
28: * - unlock bypass command
29: * - CFI queries
30: *
31: * It does not support flash interleaving.
32: * It does not implement boot blocs with reduced size
33: * It does not implement software data protection as found in many real chips
34: * It does not implement erase suspend/resume commands
35: * It does not implement multiple sectors erase
36: */
37:
1.1.1.3 root 38: #include "hw.h"
39: #include "flash.h"
40: #include "qemu-timer.h"
41: #include "block.h"
1.1 root 42:
43: //#define PFLASH_DEBUG
44: #ifdef PFLASH_DEBUG
1.1.1.5 ! root 45: #define DPRINTF(fmt, ...) \
1.1 root 46: do { \
1.1.1.5 ! root 47: printf("PFLASH: " fmt , ## __VA_ARGS__); \
1.1 root 48: } while (0)
49: #else
1.1.1.5 ! root 50: #define DPRINTF(fmt, ...) do { } while (0)
1.1 root 51: #endif
52:
53: struct pflash_t {
54: BlockDriverState *bs;
1.1.1.3 root 55: target_phys_addr_t base;
56: uint32_t sector_len;
1.1.1.4 root 57: uint32_t chip_len;
58: int mappings;
1.1 root 59: int width;
60: int wcycle; /* if 0, the flash is read normally */
61: int bypass;
62: int ro;
63: uint8_t cmd;
64: uint8_t status;
65: uint16_t ident[4];
1.1.1.4 root 66: uint16_t unlock_addr[2];
1.1 root 67: uint8_t cfi_len;
68: uint8_t cfi_table[0x52];
69: QEMUTimer *timer;
70: ram_addr_t off;
71: int fl_mem;
1.1.1.4 root 72: int rom_mode;
1.1 root 73: void *storage;
74: };
75:
1.1.1.4 root 76: static void pflash_register_memory(pflash_t *pfl, int rom_mode)
77: {
78: unsigned long phys_offset = pfl->fl_mem;
79: int i;
80:
81: if (rom_mode)
82: phys_offset |= pfl->off | IO_MEM_ROMD;
83: pfl->rom_mode = rom_mode;
84:
85: for (i = 0; i < pfl->mappings; i++)
86: cpu_register_physical_memory(pfl->base + i * pfl->chip_len,
87: pfl->chip_len, phys_offset);
88: }
89:
1.1 root 90: static void pflash_timer (void *opaque)
91: {
92: pflash_t *pfl = opaque;
93:
94: DPRINTF("%s: command %02x done\n", __func__, pfl->cmd);
95: /* Reset flash */
96: pfl->status ^= 0x80;
97: if (pfl->bypass) {
98: pfl->wcycle = 2;
99: } else {
1.1.1.4 root 100: pflash_register_memory(pfl, 1);
1.1 root 101: pfl->wcycle = 0;
102: }
103: pfl->cmd = 0;
104: }
105:
1.1.1.3 root 106: static uint32_t pflash_read (pflash_t *pfl, uint32_t offset, int width)
1.1 root 107: {
1.1.1.3 root 108: uint32_t boff;
1.1 root 109: uint32_t ret;
110: uint8_t *p;
111:
1.1.1.3 root 112: DPRINTF("%s: offset " TARGET_FMT_lx "\n", __func__, offset);
1.1 root 113: ret = -1;
1.1.1.4 root 114: if (pfl->rom_mode) {
115: /* Lazy reset of to ROMD mode */
116: if (pfl->wcycle == 0)
117: pflash_register_memory(pfl, 1);
118: }
119: offset &= pfl->chip_len - 1;
1.1 root 120: boff = offset & 0xFF;
121: if (pfl->width == 2)
122: boff = boff >> 1;
123: else if (pfl->width == 4)
124: boff = boff >> 2;
125: switch (pfl->cmd) {
126: default:
127: /* This should never happen : reset state & treat it as a read*/
128: DPRINTF("%s: unknown command state: %x\n", __func__, pfl->cmd);
129: pfl->wcycle = 0;
130: pfl->cmd = 0;
131: case 0x80:
132: /* We accept reads during second unlock sequence... */
133: case 0x00:
134: flash_read:
135: /* Flash area read */
136: p = pfl->storage;
137: switch (width) {
138: case 1:
139: ret = p[offset];
140: // DPRINTF("%s: data offset %08x %02x\n", __func__, offset, ret);
141: break;
142: case 2:
143: #if defined(TARGET_WORDS_BIGENDIAN)
144: ret = p[offset] << 8;
145: ret |= p[offset + 1];
146: #else
147: ret = p[offset];
148: ret |= p[offset + 1] << 8;
149: #endif
150: // DPRINTF("%s: data offset %08x %04x\n", __func__, offset, ret);
151: break;
152: case 4:
153: #if defined(TARGET_WORDS_BIGENDIAN)
154: ret = p[offset] << 24;
155: ret |= p[offset + 1] << 16;
156: ret |= p[offset + 2] << 8;
157: ret |= p[offset + 3];
158: #else
159: ret = p[offset];
160: ret |= p[offset + 1] << 8;
161: ret |= p[offset + 2] << 16;
162: ret |= p[offset + 3] << 24;
163: #endif
164: // DPRINTF("%s: data offset %08x %08x\n", __func__, offset, ret);
165: break;
166: }
167: break;
168: case 0x90:
169: /* flash ID read */
170: switch (boff) {
171: case 0x00:
172: case 0x01:
173: ret = pfl->ident[boff & 0x01];
174: break;
175: case 0x02:
176: ret = 0x00; /* Pretend all sectors are unprotected */
177: break;
178: case 0x0E:
179: case 0x0F:
180: if (pfl->ident[2 + (boff & 0x01)] == (uint8_t)-1)
181: goto flash_read;
182: ret = pfl->ident[2 + (boff & 0x01)];
183: break;
184: default:
185: goto flash_read;
186: }
1.1.1.3 root 187: DPRINTF("%s: ID " TARGET_FMT_ld " %x\n", __func__, boff, ret);
1.1 root 188: break;
189: case 0xA0:
190: case 0x10:
191: case 0x30:
192: /* Status register read */
193: ret = pfl->status;
194: DPRINTF("%s: status %x\n", __func__, ret);
195: /* Toggle bit 6 */
196: pfl->status ^= 0x40;
197: break;
198: case 0x98:
199: /* CFI query mode */
200: if (boff > pfl->cfi_len)
201: ret = 0;
202: else
203: ret = pfl->cfi_table[boff];
204: break;
205: }
206:
207: return ret;
208: }
209:
210: /* update flash content on disk */
1.1.1.3 root 211: static void pflash_update(pflash_t *pfl, int offset,
1.1 root 212: int size)
213: {
214: int offset_end;
215: if (pfl->bs) {
216: offset_end = offset + size;
217: /* round to sectors */
218: offset = offset >> 9;
219: offset_end = (offset_end + 511) >> 9;
1.1.1.3 root 220: bdrv_write(pfl->bs, offset, pfl->storage + (offset << 9),
1.1 root 221: offset_end - offset);
222: }
223: }
224:
1.1.1.3 root 225: static void pflash_write (pflash_t *pfl, uint32_t offset, uint32_t value,
1.1 root 226: int width)
227: {
1.1.1.3 root 228: uint32_t boff;
1.1 root 229: uint8_t *p;
230: uint8_t cmd;
231:
232: cmd = value;
233: if (pfl->cmd != 0xA0 && cmd == 0xF0) {
1.1.1.3 root 234: #if 0
1.1 root 235: DPRINTF("%s: flash reset asked (%02x %02x)\n",
236: __func__, pfl->cmd, cmd);
1.1.1.3 root 237: #endif
1.1 root 238: goto reset_flash;
239: }
1.1.1.3 root 240: DPRINTF("%s: offset " TARGET_FMT_lx " %08x %d %d\n", __func__,
241: offset, value, width, pfl->wcycle);
1.1.1.4 root 242: offset &= pfl->chip_len - 1;
1.1.1.3 root 243:
244: DPRINTF("%s: offset " TARGET_FMT_lx " %08x %d\n", __func__,
245: offset, value, width);
1.1 root 246: boff = offset & (pfl->sector_len - 1);
247: if (pfl->width == 2)
248: boff = boff >> 1;
249: else if (pfl->width == 4)
250: boff = boff >> 2;
251: switch (pfl->wcycle) {
252: case 0:
1.1.1.4 root 253: /* Set the device in I/O access mode if required */
254: if (pfl->rom_mode)
255: pflash_register_memory(pfl, 0);
1.1 root 256: /* We're in read mode */
257: check_unlock0:
258: if (boff == 0x55 && cmd == 0x98) {
259: enter_CFI_mode:
260: /* Enter CFI query mode */
261: pfl->wcycle = 7;
262: pfl->cmd = 0x98;
263: return;
264: }
1.1.1.4 root 265: if (boff != pfl->unlock_addr[0] || cmd != 0xAA) {
1.1.1.3 root 266: DPRINTF("%s: unlock0 failed " TARGET_FMT_lx " %02x %04x\n",
1.1.1.4 root 267: __func__, boff, cmd, pfl->unlock_addr[0]);
1.1 root 268: goto reset_flash;
269: }
270: DPRINTF("%s: unlock sequence started\n", __func__);
271: break;
272: case 1:
273: /* We started an unlock sequence */
274: check_unlock1:
1.1.1.4 root 275: if (boff != pfl->unlock_addr[1] || cmd != 0x55) {
1.1.1.3 root 276: DPRINTF("%s: unlock1 failed " TARGET_FMT_lx " %02x\n", __func__,
277: boff, cmd);
1.1 root 278: goto reset_flash;
279: }
280: DPRINTF("%s: unlock sequence done\n", __func__);
281: break;
282: case 2:
283: /* We finished an unlock sequence */
1.1.1.4 root 284: if (!pfl->bypass && boff != pfl->unlock_addr[0]) {
1.1.1.3 root 285: DPRINTF("%s: command failed " TARGET_FMT_lx " %02x\n", __func__,
286: boff, cmd);
1.1 root 287: goto reset_flash;
288: }
289: switch (cmd) {
290: case 0x20:
291: pfl->bypass = 1;
292: goto do_bypass;
293: case 0x80:
294: case 0x90:
295: case 0xA0:
296: pfl->cmd = cmd;
297: DPRINTF("%s: starting command %02x\n", __func__, cmd);
298: break;
299: default:
300: DPRINTF("%s: unknown command %02x\n", __func__, cmd);
301: goto reset_flash;
302: }
303: break;
304: case 3:
305: switch (pfl->cmd) {
306: case 0x80:
307: /* We need another unlock sequence */
308: goto check_unlock0;
309: case 0xA0:
1.1.1.3 root 310: DPRINTF("%s: write data offset " TARGET_FMT_lx " %08x %d\n",
1.1 root 311: __func__, offset, value, width);
312: p = pfl->storage;
313: switch (width) {
314: case 1:
315: p[offset] &= value;
316: pflash_update(pfl, offset, 1);
317: break;
318: case 2:
319: #if defined(TARGET_WORDS_BIGENDIAN)
320: p[offset] &= value >> 8;
321: p[offset + 1] &= value;
322: #else
323: p[offset] &= value;
324: p[offset + 1] &= value >> 8;
325: #endif
326: pflash_update(pfl, offset, 2);
327: break;
328: case 4:
329: #if defined(TARGET_WORDS_BIGENDIAN)
330: p[offset] &= value >> 24;
331: p[offset + 1] &= value >> 16;
332: p[offset + 2] &= value >> 8;
333: p[offset + 3] &= value;
334: #else
335: p[offset] &= value;
336: p[offset + 1] &= value >> 8;
337: p[offset + 2] &= value >> 16;
338: p[offset + 3] &= value >> 24;
339: #endif
340: pflash_update(pfl, offset, 4);
341: break;
342: }
343: pfl->status = 0x00 | ~(value & 0x80);
344: /* Let's pretend write is immediate */
345: if (pfl->bypass)
346: goto do_bypass;
347: goto reset_flash;
348: case 0x90:
349: if (pfl->bypass && cmd == 0x00) {
350: /* Unlock bypass reset */
351: goto reset_flash;
352: }
353: /* We can enter CFI query mode from autoselect mode */
354: if (boff == 0x55 && cmd == 0x98)
355: goto enter_CFI_mode;
356: /* No break here */
357: default:
358: DPRINTF("%s: invalid write for command %02x\n",
359: __func__, pfl->cmd);
360: goto reset_flash;
361: }
362: case 4:
363: switch (pfl->cmd) {
364: case 0xA0:
365: /* Ignore writes while flash data write is occuring */
366: /* As we suppose write is immediate, this should never happen */
367: return;
368: case 0x80:
369: goto check_unlock1;
370: default:
371: /* Should never happen */
372: DPRINTF("%s: invalid command state %02x (wc 4)\n",
373: __func__, pfl->cmd);
374: goto reset_flash;
375: }
376: break;
377: case 5:
378: switch (cmd) {
379: case 0x10:
1.1.1.4 root 380: if (boff != pfl->unlock_addr[0]) {
1.1.1.3 root 381: DPRINTF("%s: chip erase: invalid address " TARGET_FMT_lx "\n",
1.1 root 382: __func__, offset);
383: goto reset_flash;
384: }
385: /* Chip erase */
386: DPRINTF("%s: start chip erase\n", __func__);
1.1.1.4 root 387: memset(pfl->storage, 0xFF, pfl->chip_len);
1.1 root 388: pfl->status = 0x00;
1.1.1.4 root 389: pflash_update(pfl, 0, pfl->chip_len);
1.1 root 390: /* Let's wait 5 seconds before chip erase is done */
1.1.1.3 root 391: qemu_mod_timer(pfl->timer,
1.1 root 392: qemu_get_clock(vm_clock) + (ticks_per_sec * 5));
393: break;
394: case 0x30:
395: /* Sector erase */
396: p = pfl->storage;
397: offset &= ~(pfl->sector_len - 1);
1.1.1.3 root 398: DPRINTF("%s: start sector erase at " TARGET_FMT_lx "\n", __func__,
399: offset);
1.1 root 400: memset(p + offset, 0xFF, pfl->sector_len);
401: pflash_update(pfl, offset, pfl->sector_len);
402: pfl->status = 0x00;
403: /* Let's wait 1/2 second before sector erase is done */
1.1.1.3 root 404: qemu_mod_timer(pfl->timer,
1.1 root 405: qemu_get_clock(vm_clock) + (ticks_per_sec / 2));
406: break;
407: default:
408: DPRINTF("%s: invalid command %02x (wc 5)\n", __func__, cmd);
409: goto reset_flash;
410: }
411: pfl->cmd = cmd;
412: break;
413: case 6:
414: switch (pfl->cmd) {
415: case 0x10:
416: /* Ignore writes during chip erase */
417: return;
418: case 0x30:
419: /* Ignore writes during sector erase */
420: return;
421: default:
422: /* Should never happen */
423: DPRINTF("%s: invalid command state %02x (wc 6)\n",
424: __func__, pfl->cmd);
425: goto reset_flash;
426: }
427: break;
428: case 7: /* Special value for CFI queries */
429: DPRINTF("%s: invalid write in CFI query mode\n", __func__);
430: goto reset_flash;
431: default:
432: /* Should never happen */
433: DPRINTF("%s: invalid write state (wc 7)\n", __func__);
434: goto reset_flash;
435: }
436: pfl->wcycle++;
437:
438: return;
439:
440: /* Reset flash */
441: reset_flash:
442: pfl->bypass = 0;
443: pfl->wcycle = 0;
444: pfl->cmd = 0;
445: return;
446:
447: do_bypass:
448: pfl->wcycle = 2;
449: pfl->cmd = 0;
450: return;
451: }
452:
453:
454: static uint32_t pflash_readb (void *opaque, target_phys_addr_t addr)
455: {
456: return pflash_read(opaque, addr, 1);
457: }
458:
459: static uint32_t pflash_readw (void *opaque, target_phys_addr_t addr)
460: {
461: pflash_t *pfl = opaque;
462:
463: return pflash_read(pfl, addr, 2);
464: }
465:
466: static uint32_t pflash_readl (void *opaque, target_phys_addr_t addr)
467: {
468: pflash_t *pfl = opaque;
469:
470: return pflash_read(pfl, addr, 4);
471: }
472:
473: static void pflash_writeb (void *opaque, target_phys_addr_t addr,
474: uint32_t value)
475: {
476: pflash_write(opaque, addr, value, 1);
477: }
478:
479: static void pflash_writew (void *opaque, target_phys_addr_t addr,
480: uint32_t value)
481: {
482: pflash_t *pfl = opaque;
483:
484: pflash_write(pfl, addr, value, 2);
485: }
486:
487: static void pflash_writel (void *opaque, target_phys_addr_t addr,
488: uint32_t value)
489: {
490: pflash_t *pfl = opaque;
491:
492: pflash_write(pfl, addr, value, 4);
493: }
494:
495: static CPUWriteMemoryFunc *pflash_write_ops[] = {
496: &pflash_writeb,
497: &pflash_writew,
498: &pflash_writel,
499: };
500:
501: static CPUReadMemoryFunc *pflash_read_ops[] = {
502: &pflash_readb,
503: &pflash_readw,
504: &pflash_readl,
505: };
506:
507: /* Count trailing zeroes of a 32 bits quantity */
508: static int ctz32 (uint32_t n)
509: {
510: int ret;
511:
512: ret = 0;
513: if (!(n & 0xFFFF)) {
514: ret += 16;
515: n = n >> 16;
516: }
517: if (!(n & 0xFF)) {
518: ret += 8;
519: n = n >> 8;
520: }
521: if (!(n & 0xF)) {
522: ret += 4;
523: n = n >> 4;
524: }
525: if (!(n & 0x3)) {
526: ret += 2;
527: n = n >> 2;
528: }
529: if (!(n & 0x1)) {
530: ret++;
531: n = n >> 1;
532: }
533: #if 0 /* This is not necessary as n is never 0 */
534: if (!n)
535: ret++;
536: #endif
537:
538: return ret;
539: }
540:
1.1.1.3 root 541: pflash_t *pflash_cfi02_register(target_phys_addr_t base, ram_addr_t off,
542: BlockDriverState *bs, uint32_t sector_len,
1.1.1.4 root 543: int nb_blocs, int nb_mappings, int width,
1.1.1.3 root 544: uint16_t id0, uint16_t id1,
1.1.1.4 root 545: uint16_t id2, uint16_t id3,
546: uint16_t unlock_addr0, uint16_t unlock_addr1)
1.1 root 547: {
548: pflash_t *pfl;
1.1.1.4 root 549: int32_t chip_len;
1.1 root 550:
1.1.1.4 root 551: chip_len = sector_len * nb_blocs;
1.1 root 552: /* XXX: to be fixed */
1.1.1.3 root 553: #if 0
1.1 root 554: if (total_len != (8 * 1024 * 1024) && total_len != (16 * 1024 * 1024) &&
555: total_len != (32 * 1024 * 1024) && total_len != (64 * 1024 * 1024))
556: return NULL;
1.1.1.3 root 557: #endif
1.1 root 558: pfl = qemu_mallocz(sizeof(pflash_t));
1.1.1.5 ! root 559: /* FIXME: Allocate ram ourselves. */
! 560: pfl->storage = qemu_get_ram_ptr(off);
! 561: pfl->fl_mem = cpu_register_io_memory(pflash_read_ops, pflash_write_ops,
1.1.1.3 root 562: pfl);
1.1 root 563: pfl->off = off;
1.1.1.4 root 564: pfl->base = base;
565: pfl->chip_len = chip_len;
566: pfl->mappings = nb_mappings;
567: pflash_register_memory(pfl, 1);
1.1 root 568: pfl->bs = bs;
569: if (pfl->bs) {
570: /* read the initial flash content */
1.1.1.4 root 571: bdrv_read(pfl->bs, 0, pfl->storage, chip_len >> 9);
1.1 root 572: }
573: #if 0 /* XXX: there should be a bit to set up read-only,
574: * the same way the hardware does (with WP pin).
575: */
576: pfl->ro = 1;
577: #else
578: pfl->ro = 0;
579: #endif
580: pfl->timer = qemu_new_timer(vm_clock, pflash_timer, pfl);
581: pfl->sector_len = sector_len;
582: pfl->width = width;
583: pfl->wcycle = 0;
584: pfl->cmd = 0;
585: pfl->status = 0;
586: pfl->ident[0] = id0;
587: pfl->ident[1] = id1;
588: pfl->ident[2] = id2;
589: pfl->ident[3] = id3;
1.1.1.4 root 590: pfl->unlock_addr[0] = unlock_addr0;
591: pfl->unlock_addr[1] = unlock_addr1;
1.1 root 592: /* Hardcoded CFI table (mostly from SG29 Spansion flash) */
593: pfl->cfi_len = 0x52;
594: /* Standard "QRY" string */
595: pfl->cfi_table[0x10] = 'Q';
596: pfl->cfi_table[0x11] = 'R';
597: pfl->cfi_table[0x12] = 'Y';
598: /* Command set (AMD/Fujitsu) */
599: pfl->cfi_table[0x13] = 0x02;
600: pfl->cfi_table[0x14] = 0x00;
1.1.1.4 root 601: /* Primary extended table address */
602: pfl->cfi_table[0x15] = 0x31;
1.1 root 603: pfl->cfi_table[0x16] = 0x00;
604: /* Alternate command set (none) */
605: pfl->cfi_table[0x17] = 0x00;
606: pfl->cfi_table[0x18] = 0x00;
607: /* Alternate extended table (none) */
608: pfl->cfi_table[0x19] = 0x00;
609: pfl->cfi_table[0x1A] = 0x00;
610: /* Vcc min */
611: pfl->cfi_table[0x1B] = 0x27;
612: /* Vcc max */
613: pfl->cfi_table[0x1C] = 0x36;
614: /* Vpp min (no Vpp pin) */
615: pfl->cfi_table[0x1D] = 0x00;
616: /* Vpp max (no Vpp pin) */
617: pfl->cfi_table[0x1E] = 0x00;
618: /* Reserved */
619: pfl->cfi_table[0x1F] = 0x07;
1.1.1.4 root 620: /* Timeout for min size buffer write (NA) */
621: pfl->cfi_table[0x20] = 0x00;
1.1 root 622: /* Typical timeout for block erase (512 ms) */
623: pfl->cfi_table[0x21] = 0x09;
624: /* Typical timeout for full chip erase (4096 ms) */
625: pfl->cfi_table[0x22] = 0x0C;
626: /* Reserved */
627: pfl->cfi_table[0x23] = 0x01;
1.1.1.4 root 628: /* Max timeout for buffer write (NA) */
629: pfl->cfi_table[0x24] = 0x00;
1.1 root 630: /* Max timeout for block erase */
631: pfl->cfi_table[0x25] = 0x0A;
632: /* Max timeout for chip erase */
633: pfl->cfi_table[0x26] = 0x0D;
634: /* Device size */
1.1.1.4 root 635: pfl->cfi_table[0x27] = ctz32(chip_len);
1.1 root 636: /* Flash device interface (8 & 16 bits) */
637: pfl->cfi_table[0x28] = 0x02;
638: pfl->cfi_table[0x29] = 0x00;
639: /* Max number of bytes in multi-bytes write */
1.1.1.3 root 640: /* XXX: disable buffered write as it's not supported */
641: // pfl->cfi_table[0x2A] = 0x05;
642: pfl->cfi_table[0x2A] = 0x00;
1.1 root 643: pfl->cfi_table[0x2B] = 0x00;
644: /* Number of erase block regions (uniform) */
645: pfl->cfi_table[0x2C] = 0x01;
646: /* Erase block region 1 */
647: pfl->cfi_table[0x2D] = nb_blocs - 1;
648: pfl->cfi_table[0x2E] = (nb_blocs - 1) >> 8;
649: pfl->cfi_table[0x2F] = sector_len >> 8;
650: pfl->cfi_table[0x30] = sector_len >> 16;
651:
1.1.1.4 root 652: /* Extended */
653: pfl->cfi_table[0x31] = 'P';
654: pfl->cfi_table[0x32] = 'R';
655: pfl->cfi_table[0x33] = 'I';
656:
657: pfl->cfi_table[0x34] = '1';
658: pfl->cfi_table[0x35] = '0';
659:
660: pfl->cfi_table[0x36] = 0x00;
661: pfl->cfi_table[0x37] = 0x00;
662: pfl->cfi_table[0x38] = 0x00;
663: pfl->cfi_table[0x39] = 0x00;
664:
665: pfl->cfi_table[0x3a] = 0x00;
666:
667: pfl->cfi_table[0x3b] = 0x00;
668: pfl->cfi_table[0x3c] = 0x00;
669:
1.1 root 670: return pfl;
671: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.