Statistics
| Branch: | Revision:

root / hw / pflash_cfi01.c @ 74475455

History | View | Annotate | Download (20.2 kB)

1
/*
2
 *  CFI parallel flash with Intel command set emulation
3
 *
4
 *  Copyright (c) 2006 Thorsten Zitterell
5
 *  Copyright (c) 2005 Jocelyn Mayer
6
 *
7
 * This library is free software; you can redistribute it and/or
8
 * modify it under the terms of the GNU Lesser General Public
9
 * License as published by the Free Software Foundation; either
10
 * version 2 of the License, or (at your option) any later version.
11
 *
12
 * This library is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15
 * Lesser General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Lesser General Public
18
 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
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
 * - CFI queries
29
 *
30
 * It does not support timings
31
 * It does not support flash interleaving
32
 * It does not implement software data protection as found in many real chips
33
 * It does not implement erase suspend/resume commands
34
 * It does not implement multiple sectors erase
35
 *
36
 * It does not implement much more ...
37
 */
38

    
39
#include "hw.h"
40
#include "flash.h"
41
#include "block.h"
42
#include "qemu-timer.h"
43

    
44
#define PFLASH_BUG(fmt, ...) \
45
do { \
46
    printf("PFLASH: Possible BUG - " fmt, ## __VA_ARGS__); \
47
    exit(1); \
48
} while(0)
49

    
50
/* #define PFLASH_DEBUG */
51
#ifdef PFLASH_DEBUG
52
#define DPRINTF(fmt, ...)                          \
53
do {                                               \
54
    printf("PFLASH: " fmt , ## __VA_ARGS__);       \
55
} while (0)
56
#else
57
#define DPRINTF(fmt, ...) do { } while (0)
58
#endif
59

    
60
struct pflash_t {
61
    BlockDriverState *bs;
62
    target_phys_addr_t base;
63
    target_phys_addr_t sector_len;
64
    target_phys_addr_t total_len;
65
    int width;
66
    int wcycle; /* if 0, the flash is read normally */
67
    int bypass;
68
    int ro;
69
    uint8_t cmd;
70
    uint8_t status;
71
    uint16_t ident[4];
72
    uint8_t cfi_len;
73
    uint8_t cfi_table[0x52];
74
    target_phys_addr_t counter;
75
    unsigned int writeblock_size;
76
    QEMUTimer *timer;
77
    ram_addr_t off;
78
    int fl_mem;
79
    void *storage;
80
};
81

    
82
static void pflash_timer (void *opaque)
83
{
84
    pflash_t *pfl = opaque;
85

    
86
    DPRINTF("%s: command %02x done\n", __func__, pfl->cmd);
87
    /* Reset flash */
88
    pfl->status ^= 0x80;
89
    if (pfl->bypass) {
90
        pfl->wcycle = 2;
91
    } else {
92
        cpu_register_physical_memory(pfl->base, pfl->total_len,
93
                        pfl->off | IO_MEM_ROMD | pfl->fl_mem);
94
        pfl->wcycle = 0;
95
    }
96
    pfl->cmd = 0;
97
}
98

    
99
static uint32_t pflash_read (pflash_t *pfl, target_phys_addr_t offset,
100
                             int width, int be)
101
{
102
    target_phys_addr_t boff;
103
    uint32_t ret;
104
    uint8_t *p;
105

    
106
    ret = -1;
107
    boff = offset & 0xFF; /* why this here ?? */
108

    
109
    if (pfl->width == 2)
110
        boff = boff >> 1;
111
    else if (pfl->width == 4)
112
        boff = boff >> 2;
113

    
114
#if 0
115
    DPRINTF("%s: reading offset " TARGET_FMT_plx " under cmd %02x width %d\n",
116
            __func__, offset, pfl->cmd, width);
117
#endif
118
    switch (pfl->cmd) {
119
    case 0x00:
120
        /* Flash area read */
121
        p = pfl->storage;
122
        switch (width) {
123
        case 1:
124
            ret = p[offset];
125
            DPRINTF("%s: data offset " TARGET_FMT_plx " %02x\n",
126
                    __func__, offset, ret);
127
            break;
128
        case 2:
129
            if (be) {
130
                ret = p[offset] << 8;
131
                ret |= p[offset + 1];
132
            } else {
133
                ret = p[offset];
134
                ret |= p[offset + 1] << 8;
135
            }
136
            DPRINTF("%s: data offset " TARGET_FMT_plx " %04x\n",
137
                    __func__, offset, ret);
138
            break;
139
        case 4:
140
            if (be) {
141
                ret = p[offset] << 24;
142
                ret |= p[offset + 1] << 16;
143
                ret |= p[offset + 2] << 8;
144
                ret |= p[offset + 3];
145
            } else {
146
                ret = p[offset];
147
                ret |= p[offset + 1] << 8;
148
                ret |= p[offset + 1] << 8;
149
                ret |= p[offset + 2] << 16;
150
                ret |= p[offset + 3] << 24;
151
            }
152
            DPRINTF("%s: data offset " TARGET_FMT_plx " %08x\n",
153
                    __func__, offset, ret);
154
            break;
155
        default:
156
            DPRINTF("BUG in %s\n", __func__);
157
        }
158

    
159
        break;
160
    case 0x20: /* Block erase */
161
    case 0x50: /* Clear status register */
162
    case 0x60: /* Block /un)lock */
163
    case 0x70: /* Status Register */
164
    case 0xe8: /* Write block */
165
        /* Status register read */
166
        ret = pfl->status;
167
        DPRINTF("%s: status %x\n", __func__, ret);
168
        break;
169
    case 0x90:
170
        switch (boff) {
171
        case 0:
172
            ret = pfl->ident[0] << 8 | pfl->ident[1];
173
            DPRINTF("%s: Manufacturer Code %04x\n", __func__, ret);
174
            break;
175
        case 1:
176
            ret = pfl->ident[2] << 8 | pfl->ident[3];
177
            DPRINTF("%s: Device ID Code %04x\n", __func__, ret);
178
            break;
179
        default:
180
            DPRINTF("%s: Read Device Information boff=%x\n", __func__, boff);
181
            ret = 0;
182
            break;
183
        }
184
        break;
185
    case 0x98: /* Query mode */
186
        if (boff > pfl->cfi_len)
187
            ret = 0;
188
        else
189
            ret = pfl->cfi_table[boff];
190
        break;
191
    default:
192
        /* This should never happen : reset state & treat it as a read */
193
        DPRINTF("%s: unknown command state: %x\n", __func__, pfl->cmd);
194
        pfl->wcycle = 0;
195
        pfl->cmd = 0;
196
    }
197
    return ret;
198
}
199

    
200
/* update flash content on disk */
201
static void pflash_update(pflash_t *pfl, int offset,
202
                          int size)
203
{
204
    int offset_end;
205
    if (pfl->bs) {
206
        offset_end = offset + size;
207
        /* round to sectors */
208
        offset = offset >> 9;
209
        offset_end = (offset_end + 511) >> 9;
210
        bdrv_write(pfl->bs, offset, pfl->storage + (offset << 9),
211
                   offset_end - offset);
212
    }
213
}
214

    
215
static inline void pflash_data_write(pflash_t *pfl, target_phys_addr_t offset,
216
                                     uint32_t value, int width, int be)
217
{
218
    uint8_t *p = pfl->storage;
219

    
220
    DPRINTF("%s: block write offset " TARGET_FMT_plx
221
            " value %x counter " TARGET_FMT_plx "\n",
222
            __func__, offset, value, pfl->counter);
223
    switch (width) {
224
    case 1:
225
        p[offset] = value;
226
        break;
227
    case 2:
228
        if (be) {
229
            p[offset] = value >> 8;
230
            p[offset + 1] = value;
231
        } else {
232
            p[offset] = value;
233
            p[offset + 1] = value >> 8;
234
        }
235
        break;
236
    case 4:
237
        if (be) {
238
            p[offset] = value >> 24;
239
            p[offset + 1] = value >> 16;
240
            p[offset + 2] = value >> 8;
241
            p[offset + 3] = value;
242
        } else {
243
            p[offset] = value;
244
            p[offset + 1] = value >> 8;
245
            p[offset + 2] = value >> 16;
246
            p[offset + 3] = value >> 24;
247
        }
248
        break;
249
    }
250

    
251
}
252

    
253
static void pflash_write(pflash_t *pfl, target_phys_addr_t offset,
254
                         uint32_t value, int width, int be)
255
{
256
    uint8_t *p;
257
    uint8_t cmd;
258

    
259
    cmd = value;
260

    
261
    DPRINTF("%s: writing offset " TARGET_FMT_plx " value %08x width %d wcycle 0x%x\n",
262
            __func__, offset, value, width, pfl->wcycle);
263

    
264
    if (!pfl->wcycle) {
265
        /* Set the device in I/O access mode */
266
        cpu_register_physical_memory(pfl->base, pfl->total_len, pfl->fl_mem);
267
    }
268

    
269
    switch (pfl->wcycle) {
270
    case 0:
271
        /* read mode */
272
        switch (cmd) {
273
        case 0x00: /* ??? */
274
            goto reset_flash;
275
        case 0x10: /* Single Byte Program */
276
        case 0x40: /* Single Byte Program */
277
            DPRINTF("%s: Single Byte Program\n", __func__);
278
            break;
279
        case 0x20: /* Block erase */
280
            p = pfl->storage;
281
            offset &= ~(pfl->sector_len - 1);
282

    
283
            DPRINTF("%s: block erase at " TARGET_FMT_plx " bytes "
284
                    TARGET_FMT_plx "\n",
285
                    __func__, offset, pfl->sector_len);
286

    
287
            memset(p + offset, 0xff, pfl->sector_len);
288
            pflash_update(pfl, offset, pfl->sector_len);
289
            pfl->status |= 0x80; /* Ready! */
290
            break;
291
        case 0x50: /* Clear status bits */
292
            DPRINTF("%s: Clear status bits\n", __func__);
293
            pfl->status = 0x0;
294
            goto reset_flash;
295
        case 0x60: /* Block (un)lock */
296
            DPRINTF("%s: Block unlock\n", __func__);
297
            break;
298
        case 0x70: /* Status Register */
299
            DPRINTF("%s: Read status register\n", __func__);
300
            pfl->cmd = cmd;
301
            return;
302
        case 0x90: /* Read Device ID */
303
            DPRINTF("%s: Read Device information\n", __func__);
304
            pfl->cmd = cmd;
305
            return;
306
        case 0x98: /* CFI query */
307
            DPRINTF("%s: CFI query\n", __func__);
308
            break;
309
        case 0xe8: /* Write to buffer */
310
            DPRINTF("%s: Write to buffer\n", __func__);
311
            pfl->status |= 0x80; /* Ready! */
312
            break;
313
        case 0xff: /* Read array mode */
314
            DPRINTF("%s: Read array mode\n", __func__);
315
            goto reset_flash;
316
        default:
317
            goto error_flash;
318
        }
319
        pfl->wcycle++;
320
        pfl->cmd = cmd;
321
        return;
322
    case 1:
323
        switch (pfl->cmd) {
324
        case 0x10: /* Single Byte Program */
325
        case 0x40: /* Single Byte Program */
326
            DPRINTF("%s: Single Byte Program\n", __func__);
327
            pflash_data_write(pfl, offset, value, width, be);
328
            pflash_update(pfl, offset, width);
329
            pfl->status |= 0x80; /* Ready! */
330
            pfl->wcycle = 0;
331
        break;
332
        case 0x20: /* Block erase */
333
        case 0x28:
334
            if (cmd == 0xd0) { /* confirm */
335
                pfl->wcycle = 0;
336
                pfl->status |= 0x80;
337
            } else if (cmd == 0xff) { /* read array mode */
338
                goto reset_flash;
339
            } else
340
                goto error_flash;
341

    
342
            break;
343
        case 0xe8:
344
            DPRINTF("%s: block write of %x bytes\n", __func__, value);
345
            pfl->counter = value;
346
            pfl->wcycle++;
347
            break;
348
        case 0x60:
349
            if (cmd == 0xd0) {
350
                pfl->wcycle = 0;
351
                pfl->status |= 0x80;
352
            } else if (cmd == 0x01) {
353
                pfl->wcycle = 0;
354
                pfl->status |= 0x80;
355
            } else if (cmd == 0xff) {
356
                goto reset_flash;
357
            } else {
358
                DPRINTF("%s: Unknown (un)locking command\n", __func__);
359
                goto reset_flash;
360
            }
361
            break;
362
        case 0x98:
363
            if (cmd == 0xff) {
364
                goto reset_flash;
365
            } else {
366
                DPRINTF("%s: leaving query mode\n", __func__);
367
            }
368
            break;
369
        default:
370
            goto error_flash;
371
        }
372
        return;
373
    case 2:
374
        switch (pfl->cmd) {
375
        case 0xe8: /* Block write */
376
            pflash_data_write(pfl, offset, value, width, be);
377

    
378
            pfl->status |= 0x80;
379

    
380
            if (!pfl->counter) {
381
                target_phys_addr_t mask = pfl->writeblock_size - 1;
382
                mask = ~mask;
383

    
384
                DPRINTF("%s: block write finished\n", __func__);
385
                pfl->wcycle++;
386
                /* Flush the entire write buffer onto backing storage.  */
387
                pflash_update(pfl, offset & mask, pfl->writeblock_size);
388
            }
389

    
390
            pfl->counter--;
391
            break;
392
        default:
393
            goto error_flash;
394
        }
395
        return;
396
    case 3: /* Confirm mode */
397
        switch (pfl->cmd) {
398
        case 0xe8: /* Block write */
399
            if (cmd == 0xd0) {
400
                pfl->wcycle = 0;
401
                pfl->status |= 0x80;
402
            } else {
403
                DPRINTF("%s: unknown command for \"write block\"\n", __func__);
404
                PFLASH_BUG("Write block confirm");
405
                goto reset_flash;
406
            }
407
            break;
408
        default:
409
            goto error_flash;
410
        }
411
        return;
412
    default:
413
        /* Should never happen */
414
        DPRINTF("%s: invalid write state\n",  __func__);
415
        goto reset_flash;
416
    }
417
    return;
418

    
419
 error_flash:
420
    printf("%s: Unimplemented flash cmd sequence "
421
           "(offset " TARGET_FMT_plx ", wcycle 0x%x cmd 0x%x value 0x%x)\n",
422
           __func__, offset, pfl->wcycle, pfl->cmd, value);
423

    
424
 reset_flash:
425
    cpu_register_physical_memory(pfl->base, pfl->total_len,
426
                    pfl->off | IO_MEM_ROMD | pfl->fl_mem);
427

    
428
    pfl->bypass = 0;
429
    pfl->wcycle = 0;
430
    pfl->cmd = 0;
431
    return;
432
}
433

    
434

    
435
static uint32_t pflash_readb_be(void *opaque, target_phys_addr_t addr)
436
{
437
    return pflash_read(opaque, addr, 1, 1);
438
}
439

    
440
static uint32_t pflash_readb_le(void *opaque, target_phys_addr_t addr)
441
{
442
    return pflash_read(opaque, addr, 1, 0);
443
}
444

    
445
static uint32_t pflash_readw_be(void *opaque, target_phys_addr_t addr)
446
{
447
    pflash_t *pfl = opaque;
448

    
449
    return pflash_read(pfl, addr, 2, 1);
450
}
451

    
452
static uint32_t pflash_readw_le(void *opaque, target_phys_addr_t addr)
453
{
454
    pflash_t *pfl = opaque;
455

    
456
    return pflash_read(pfl, addr, 2, 0);
457
}
458

    
459
static uint32_t pflash_readl_be(void *opaque, target_phys_addr_t addr)
460
{
461
    pflash_t *pfl = opaque;
462

    
463
    return pflash_read(pfl, addr, 4, 1);
464
}
465

    
466
static uint32_t pflash_readl_le(void *opaque, target_phys_addr_t addr)
467
{
468
    pflash_t *pfl = opaque;
469

    
470
    return pflash_read(pfl, addr, 4, 0);
471
}
472

    
473
static void pflash_writeb_be(void *opaque, target_phys_addr_t addr,
474
                             uint32_t value)
475
{
476
    pflash_write(opaque, addr, value, 1, 1);
477
}
478

    
479
static void pflash_writeb_le(void *opaque, target_phys_addr_t addr,
480
                             uint32_t value)
481
{
482
    pflash_write(opaque, addr, value, 1, 0);
483
}
484

    
485
static void pflash_writew_be(void *opaque, target_phys_addr_t addr,
486
                             uint32_t value)
487
{
488
    pflash_t *pfl = opaque;
489

    
490
    pflash_write(pfl, addr, value, 2, 1);
491
}
492

    
493
static void pflash_writew_le(void *opaque, target_phys_addr_t addr,
494
                             uint32_t value)
495
{
496
    pflash_t *pfl = opaque;
497

    
498
    pflash_write(pfl, addr, value, 2, 0);
499
}
500

    
501
static void pflash_writel_be(void *opaque, target_phys_addr_t addr,
502
                             uint32_t value)
503
{
504
    pflash_t *pfl = opaque;
505

    
506
    pflash_write(pfl, addr, value, 4, 1);
507
}
508

    
509
static void pflash_writel_le(void *opaque, target_phys_addr_t addr,
510
                             uint32_t value)
511
{
512
    pflash_t *pfl = opaque;
513

    
514
    pflash_write(pfl, addr, value, 4, 0);
515
}
516

    
517
static CPUWriteMemoryFunc * const pflash_write_ops_be[] = {
518
    &pflash_writeb_be,
519
    &pflash_writew_be,
520
    &pflash_writel_be,
521
};
522

    
523
static CPUReadMemoryFunc * const pflash_read_ops_be[] = {
524
    &pflash_readb_be,
525
    &pflash_readw_be,
526
    &pflash_readl_be,
527
};
528

    
529
static CPUWriteMemoryFunc * const pflash_write_ops_le[] = {
530
    &pflash_writeb_le,
531
    &pflash_writew_le,
532
    &pflash_writel_le,
533
};
534

    
535
static CPUReadMemoryFunc * const pflash_read_ops_le[] = {
536
    &pflash_readb_le,
537
    &pflash_readw_le,
538
    &pflash_readl_le,
539
};
540

    
541
/* Count trailing zeroes of a 32 bits quantity */
542
static int ctz32 (uint32_t n)
543
{
544
    int ret;
545

    
546
    ret = 0;
547
    if (!(n & 0xFFFF)) {
548
        ret += 16;
549
        n = n >> 16;
550
    }
551
    if (!(n & 0xFF)) {
552
        ret += 8;
553
        n = n >> 8;
554
    }
555
    if (!(n & 0xF)) {
556
        ret += 4;
557
        n = n >> 4;
558
    }
559
    if (!(n & 0x3)) {
560
        ret += 2;
561
        n = n >> 2;
562
    }
563
    if (!(n & 0x1)) {
564
        ret++;
565
#if 0 /* This is not necessary as n is never 0 */
566
        n = n >> 1;
567
#endif
568
    }
569
#if 0 /* This is not necessary as n is never 0 */
570
    if (!n)
571
        ret++;
572
#endif
573

    
574
    return ret;
575
}
576

    
577
pflash_t *pflash_cfi01_register(target_phys_addr_t base, ram_addr_t off,
578
                                BlockDriverState *bs, uint32_t sector_len,
579
                                int nb_blocs, int width,
580
                                uint16_t id0, uint16_t id1,
581
                                uint16_t id2, uint16_t id3,
582
                                int be)
583
{
584
    pflash_t *pfl;
585
    target_phys_addr_t total_len;
586
    int ret;
587

    
588
    total_len = sector_len * nb_blocs;
589

    
590
    /* XXX: to be fixed */
591
#if 0
592
    if (total_len != (8 * 1024 * 1024) && total_len != (16 * 1024 * 1024) &&
593
        total_len != (32 * 1024 * 1024) && total_len != (64 * 1024 * 1024))
594
        return NULL;
595
#endif
596

    
597
    pfl = qemu_mallocz(sizeof(pflash_t));
598

    
599
    /* FIXME: Allocate ram ourselves.  */
600
    pfl->storage = qemu_get_ram_ptr(off);
601
    if (be) {
602
        pfl->fl_mem = cpu_register_io_memory(pflash_read_ops_be,
603
                                             pflash_write_ops_be, pfl,
604
                                             DEVICE_NATIVE_ENDIAN);
605
    } else {
606
        pfl->fl_mem = cpu_register_io_memory(pflash_read_ops_le,
607
                                             pflash_write_ops_le, pfl,
608
                                             DEVICE_NATIVE_ENDIAN);
609
    }
610
    pfl->off = off;
611
    cpu_register_physical_memory(base, total_len,
612
                    off | pfl->fl_mem | IO_MEM_ROMD);
613

    
614
    pfl->bs = bs;
615
    if (pfl->bs) {
616
        /* read the initial flash content */
617
        ret = bdrv_read(pfl->bs, 0, pfl->storage, total_len >> 9);
618
        if (ret < 0) {
619
            cpu_unregister_io_memory(pfl->fl_mem);
620
            qemu_free(pfl);
621
            return NULL;
622
        }
623
    }
624
#if 0 /* XXX: there should be a bit to set up read-only,
625
       *      the same way the hardware does (with WP pin).
626
       */
627
    pfl->ro = 1;
628
#else
629
    pfl->ro = 0;
630
#endif
631
    pfl->timer = qemu_new_timer_ns(vm_clock, pflash_timer, pfl);
632
    pfl->base = base;
633
    pfl->sector_len = sector_len;
634
    pfl->total_len = total_len;
635
    pfl->width = width;
636
    pfl->wcycle = 0;
637
    pfl->cmd = 0;
638
    pfl->status = 0;
639
    pfl->ident[0] = id0;
640
    pfl->ident[1] = id1;
641
    pfl->ident[2] = id2;
642
    pfl->ident[3] = id3;
643
    /* Hardcoded CFI table */
644
    pfl->cfi_len = 0x52;
645
    /* Standard "QRY" string */
646
    pfl->cfi_table[0x10] = 'Q';
647
    pfl->cfi_table[0x11] = 'R';
648
    pfl->cfi_table[0x12] = 'Y';
649
    /* Command set (Intel) */
650
    pfl->cfi_table[0x13] = 0x01;
651
    pfl->cfi_table[0x14] = 0x00;
652
    /* Primary extended table address (none) */
653
    pfl->cfi_table[0x15] = 0x31;
654
    pfl->cfi_table[0x16] = 0x00;
655
    /* Alternate command set (none) */
656
    pfl->cfi_table[0x17] = 0x00;
657
    pfl->cfi_table[0x18] = 0x00;
658
    /* Alternate extended table (none) */
659
    pfl->cfi_table[0x19] = 0x00;
660
    pfl->cfi_table[0x1A] = 0x00;
661
    /* Vcc min */
662
    pfl->cfi_table[0x1B] = 0x45;
663
    /* Vcc max */
664
    pfl->cfi_table[0x1C] = 0x55;
665
    /* Vpp min (no Vpp pin) */
666
    pfl->cfi_table[0x1D] = 0x00;
667
    /* Vpp max (no Vpp pin) */
668
    pfl->cfi_table[0x1E] = 0x00;
669
    /* Reserved */
670
    pfl->cfi_table[0x1F] = 0x07;
671
    /* Timeout for min size buffer write */
672
    pfl->cfi_table[0x20] = 0x07;
673
    /* Typical timeout for block erase */
674
    pfl->cfi_table[0x21] = 0x0a;
675
    /* Typical timeout for full chip erase (4096 ms) */
676
    pfl->cfi_table[0x22] = 0x00;
677
    /* Reserved */
678
    pfl->cfi_table[0x23] = 0x04;
679
    /* Max timeout for buffer write */
680
    pfl->cfi_table[0x24] = 0x04;
681
    /* Max timeout for block erase */
682
    pfl->cfi_table[0x25] = 0x04;
683
    /* Max timeout for chip erase */
684
    pfl->cfi_table[0x26] = 0x00;
685
    /* Device size */
686
    pfl->cfi_table[0x27] = ctz32(total_len); // + 1;
687
    /* Flash device interface (8 & 16 bits) */
688
    pfl->cfi_table[0x28] = 0x02;
689
    pfl->cfi_table[0x29] = 0x00;
690
    /* Max number of bytes in multi-bytes write */
691
    if (width == 1) {
692
        pfl->cfi_table[0x2A] = 0x08;
693
    } else {
694
        pfl->cfi_table[0x2A] = 0x0B;
695
    }
696
    pfl->writeblock_size = 1 << pfl->cfi_table[0x2A];
697

    
698
    pfl->cfi_table[0x2B] = 0x00;
699
    /* Number of erase block regions (uniform) */
700
    pfl->cfi_table[0x2C] = 0x01;
701
    /* Erase block region 1 */
702
    pfl->cfi_table[0x2D] = nb_blocs - 1;
703
    pfl->cfi_table[0x2E] = (nb_blocs - 1) >> 8;
704
    pfl->cfi_table[0x2F] = sector_len >> 8;
705
    pfl->cfi_table[0x30] = sector_len >> 16;
706

    
707
    /* Extended */
708
    pfl->cfi_table[0x31] = 'P';
709
    pfl->cfi_table[0x32] = 'R';
710
    pfl->cfi_table[0x33] = 'I';
711

    
712
    pfl->cfi_table[0x34] = '1';
713
    pfl->cfi_table[0x35] = '1';
714

    
715
    pfl->cfi_table[0x36] = 0x00;
716
    pfl->cfi_table[0x37] = 0x00;
717
    pfl->cfi_table[0x38] = 0x00;
718
    pfl->cfi_table[0x39] = 0x00;
719

    
720
    pfl->cfi_table[0x3a] = 0x00;
721

    
722
    pfl->cfi_table[0x3b] = 0x00;
723
    pfl->cfi_table[0x3c] = 0x00;
724

    
725
    return pfl;
726
}