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