symbian-qemu-0.9.1-12/qemu-symbian-svp/hw/pflash_cfi01.c
changeset 1 2fb8b9db1c86
equal deleted inserted replaced
0:ffa851df0825 1:2fb8b9db1c86
       
     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, write to the Free Software
       
    19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
       
    20  */
       
    21 
       
    22 /*
       
    23  * For now, this code can emulate flashes of 1, 2 or 4 bytes width.
       
    24  * Supported commands/modes are:
       
    25  * - flash read
       
    26  * - flash write
       
    27  * - flash ID read
       
    28  * - sector erase
       
    29  * - CFI queries
       
    30  *
       
    31  * It does not support timings
       
    32  * It does not support flash interleaving
       
    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  * It does not implement much more ...
       
    38  */
       
    39 
       
    40 #include "hw.h"
       
    41 #include "flash.h"
       
    42 #include "block.h"
       
    43 #include "qemu-timer.h"
       
    44 
       
    45 #define PFLASH_BUG(fmt, args...) \
       
    46 do { \
       
    47     printf("PFLASH: Possible BUG - " fmt, ##args); \
       
    48     exit(1); \
       
    49 } while(0)
       
    50 
       
    51 /* #define PFLASH_DEBUG */
       
    52 #ifdef PFLASH_DEBUG
       
    53 #define DPRINTF(fmt, args...)                      \
       
    54 do {                                               \
       
    55         printf("PFLASH: " fmt , ##args);           \
       
    56 } while (0)
       
    57 #else
       
    58 #define DPRINTF(fmt, args...) do { } while (0)
       
    59 #endif
       
    60 
       
    61 struct pflash_t {
       
    62     BlockDriverState *bs;
       
    63     target_ulong base;
       
    64     target_ulong sector_len;
       
    65     target_ulong total_len;
       
    66     int width;
       
    67     int wcycle; /* if 0, the flash is read normally */
       
    68     int bypass;
       
    69     int ro;
       
    70     uint8_t cmd;
       
    71     uint8_t status;
       
    72     uint16_t ident[4];
       
    73     uint8_t cfi_len;
       
    74     uint8_t cfi_table[0x52];
       
    75     target_ulong counter;
       
    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_ulong offset, int width)
       
   100 {
       
   101     target_ulong boff;
       
   102     uint32_t ret;
       
   103     uint8_t *p;
       
   104 
       
   105     ret = -1;
       
   106     boff = offset & 0xFF; /* why this here ?? */
       
   107 
       
   108     if (pfl->width == 2)
       
   109         boff = boff >> 1;
       
   110     else if (pfl->width == 4)
       
   111         boff = boff >> 2;
       
   112 
       
   113     DPRINTF("%s: reading offset " TARGET_FMT_lx " under cmd %02x width %d\n",
       
   114             __func__, offset, pfl->cmd, width);
       
   115 
       
   116     switch (pfl->cmd) {
       
   117     case 0x00:
       
   118         /* Flash area read */
       
   119         p = pfl->storage;
       
   120         switch (width) {
       
   121         case 1:
       
   122             ret = p[offset];
       
   123             DPRINTF("%s: data offset " TARGET_FMT_lx " %02x\n",
       
   124                     __func__, offset, ret);
       
   125             break;
       
   126         case 2:
       
   127 #if defined(TARGET_WORDS_BIGENDIAN)
       
   128             ret = p[offset] << 8;
       
   129             ret |= p[offset + 1];
       
   130 #else
       
   131             ret = p[offset];
       
   132             ret |= p[offset + 1] << 8;
       
   133 #endif
       
   134             DPRINTF("%s: data offset " TARGET_FMT_lx " %04x\n",
       
   135                     __func__, offset, ret);
       
   136             break;
       
   137         case 4:
       
   138 #if defined(TARGET_WORDS_BIGENDIAN)
       
   139             ret = p[offset] << 24;
       
   140             ret |= p[offset + 1] << 16;
       
   141             ret |= p[offset + 2] << 8;
       
   142             ret |= p[offset + 3];
       
   143 #else
       
   144             ret = p[offset];
       
   145             ret |= p[offset + 1] << 8;
       
   146             ret |= p[offset + 1] << 8;
       
   147             ret |= p[offset + 2] << 16;
       
   148             ret |= p[offset + 3] << 24;
       
   149 #endif
       
   150             DPRINTF("%s: data offset " TARGET_FMT_lx " %08x\n",
       
   151                     __func__, offset, ret);
       
   152             break;
       
   153         default:
       
   154             DPRINTF("BUG in %s\n", __func__);
       
   155         }
       
   156 
       
   157         break;
       
   158     case 0x20: /* Block erase */
       
   159     case 0x50: /* Clear status register */
       
   160     case 0x60: /* Block /un)lock */
       
   161     case 0x70: /* Status Register */
       
   162     case 0xe8: /* Write block */
       
   163         /* Status register read */
       
   164         ret = pfl->status;
       
   165         DPRINTF("%s: status %x\n", __func__, ret);
       
   166         break;
       
   167     case 0x98: /* Query mode */
       
   168         if (boff > pfl->cfi_len)
       
   169             ret = 0;
       
   170         else
       
   171             ret = pfl->cfi_table[boff];
       
   172         break;
       
   173     default:
       
   174         /* This should never happen : reset state & treat it as a read */
       
   175         DPRINTF("%s: unknown command state: %x\n", __func__, pfl->cmd);
       
   176         pfl->wcycle = 0;
       
   177         pfl->cmd = 0;
       
   178     }
       
   179     return ret;
       
   180 }
       
   181 
       
   182 /* update flash content on disk */
       
   183 static void pflash_update(pflash_t *pfl, int offset,
       
   184                           int size)
       
   185 {
       
   186     int offset_end;
       
   187     if (pfl->bs) {
       
   188         offset_end = offset + size;
       
   189         /* round to sectors */
       
   190         offset = offset >> 9;
       
   191         offset_end = (offset_end + 511) >> 9;
       
   192         bdrv_write(pfl->bs, offset, pfl->storage + (offset << 9),
       
   193                    offset_end - offset);
       
   194     }
       
   195 }
       
   196 
       
   197 static void inline pflash_data_write(pflash_t *pfl, target_ulong offset,
       
   198                           uint32_t value, int width)
       
   199 {
       
   200     uint8_t *p = pfl->storage;
       
   201 
       
   202     DPRINTF("%s: block write offset " TARGET_FMT_lx
       
   203             " value %x counter " TARGET_FMT_lx "\n",
       
   204             __func__, offset, value, pfl->counter);
       
   205     switch (width) {
       
   206     case 1:
       
   207         p[offset] = value;
       
   208         pflash_update(pfl, offset, 1);
       
   209         break;
       
   210     case 2:
       
   211 #if defined(TARGET_WORDS_BIGENDIAN)
       
   212         p[offset] = value >> 8;
       
   213         p[offset + 1] = value;
       
   214 #else
       
   215         p[offset] = value;
       
   216         p[offset + 1] = value >> 8;
       
   217 #endif
       
   218         pflash_update(pfl, offset, 2);
       
   219         break;
       
   220     case 4:
       
   221 #if defined(TARGET_WORDS_BIGENDIAN)
       
   222         p[offset] = value >> 24;
       
   223         p[offset + 1] = value >> 16;
       
   224         p[offset + 2] = value >> 8;
       
   225         p[offset + 3] = value;
       
   226 #else
       
   227         p[offset] = value;
       
   228         p[offset + 1] = value >> 8;
       
   229         p[offset + 2] = value >> 16;
       
   230         p[offset + 3] = value >> 24;
       
   231 #endif
       
   232         pflash_update(pfl, offset, 4);
       
   233         break;
       
   234     }
       
   235 
       
   236 }
       
   237 
       
   238 static void pflash_write (pflash_t *pfl, target_ulong offset, uint32_t value,
       
   239                           int width)
       
   240 {
       
   241     target_ulong boff;
       
   242     uint8_t *p;
       
   243     uint8_t cmd;
       
   244 
       
   245     cmd = value;
       
   246 
       
   247     DPRINTF("%s: writing offset " TARGET_FMT_lx " value %08x width %d wcycle 0x%x\n",
       
   248             __func__, offset, value, width, pfl->wcycle);
       
   249 
       
   250     /* Set the device in I/O access mode */
       
   251     cpu_register_physical_memory(pfl->base, pfl->total_len, pfl->fl_mem);
       
   252     boff = offset & (pfl->sector_len - 1);
       
   253 
       
   254     if (pfl->width == 2)
       
   255         boff = boff >> 1;
       
   256     else if (pfl->width == 4)
       
   257         boff = boff >> 2;
       
   258 
       
   259     switch (pfl->wcycle) {
       
   260     case 0:
       
   261         /* read mode */
       
   262         switch (cmd) {
       
   263         case 0x00: /* ??? */
       
   264             goto reset_flash;
       
   265         case 0x10: /* Single Byte Program */
       
   266         case 0x40: /* Single Byte Program */
       
   267             DPRINTF(stderr, "%s: Single Byte Program\n", __func__);
       
   268             break;
       
   269         case 0x20: /* Block erase */
       
   270             p = pfl->storage;
       
   271             offset &= ~(pfl->sector_len - 1);
       
   272 
       
   273             DPRINTF("%s: block erase at " TARGET_FMT_lx " bytes "
       
   274                     TARGET_FMT_lx "\n",
       
   275                     __func__, offset, pfl->sector_len);
       
   276 
       
   277             memset(p + offset, 0xff, pfl->sector_len);
       
   278             pflash_update(pfl, offset, pfl->sector_len);
       
   279             pfl->status |= 0x80; /* Ready! */
       
   280             break;
       
   281         case 0x50: /* Clear status bits */
       
   282             DPRINTF("%s: Clear status bits\n", __func__);
       
   283             pfl->status = 0x0;
       
   284             goto reset_flash;
       
   285         case 0x60: /* Block (un)lock */
       
   286             DPRINTF("%s: Block unlock\n", __func__);
       
   287             break;
       
   288         case 0x70: /* Status Register */
       
   289             DPRINTF("%s: Read status register\n", __func__);
       
   290             pfl->cmd = cmd;
       
   291             return;
       
   292         case 0x98: /* CFI query */
       
   293             DPRINTF("%s: CFI query\n", __func__);
       
   294             break;
       
   295         case 0xe8: /* Write to buffer */
       
   296             DPRINTF("%s: Write to buffer\n", __func__);
       
   297             pfl->status |= 0x80; /* Ready! */
       
   298             break;
       
   299         case 0xff: /* Read array mode */
       
   300             DPRINTF("%s: Read array mode\n", __func__);
       
   301             goto reset_flash;
       
   302         default:
       
   303             goto error_flash;
       
   304         }
       
   305         pfl->wcycle++;
       
   306         pfl->cmd = cmd;
       
   307         return;
       
   308     case 1:
       
   309         switch (pfl->cmd) {
       
   310         case 0x10: /* Single Byte Program */
       
   311         case 0x40: /* Single Byte Program */
       
   312             DPRINTF("%s: Single Byte Program\n", __func__);
       
   313             pflash_data_write(pfl, offset, value, width);
       
   314             pfl->status |= 0x80; /* Ready! */
       
   315             pfl->wcycle = 0;
       
   316         break;
       
   317         case 0x20: /* Block erase */
       
   318         case 0x28:
       
   319             if (cmd == 0xd0) { /* confirm */
       
   320                 pfl->wcycle = 0;
       
   321                 pfl->status |= 0x80;
       
   322             } else if (cmd == 0xff) { /* read array mode */
       
   323                 goto reset_flash;
       
   324             } else
       
   325                 goto error_flash;
       
   326 
       
   327             break;
       
   328         case 0xe8:
       
   329             DPRINTF("%s: block write of %x bytes\n", __func__, value);
       
   330             pfl->counter = value;
       
   331             pfl->wcycle++;
       
   332             break;
       
   333         case 0x60:
       
   334             if (cmd == 0xd0) {
       
   335                 pfl->wcycle = 0;
       
   336                 pfl->status |= 0x80;
       
   337             } else if (cmd == 0x01) {
       
   338                 pfl->wcycle = 0;
       
   339                 pfl->status |= 0x80;
       
   340             } else if (cmd == 0xff) {
       
   341                 goto reset_flash;
       
   342             } else {
       
   343                 DPRINTF("%s: Unknown (un)locking command\n", __func__);
       
   344                 goto reset_flash;
       
   345             }
       
   346             break;
       
   347         case 0x98:
       
   348             if (cmd == 0xff) {
       
   349                 goto reset_flash;
       
   350             } else {
       
   351                 DPRINTF("%s: leaving query mode\n", __func__);
       
   352             }
       
   353             break;
       
   354         default:
       
   355             goto error_flash;
       
   356         }
       
   357         return;
       
   358     case 2:
       
   359         switch (pfl->cmd) {
       
   360         case 0xe8: /* Block write */
       
   361             pflash_data_write(pfl, offset, value, width);
       
   362 
       
   363             pfl->status |= 0x80;
       
   364 
       
   365             if (!pfl->counter) {
       
   366                 DPRINTF("%s: block write finished\n", __func__);
       
   367                 pfl->wcycle++;
       
   368             }
       
   369 
       
   370             pfl->counter--;
       
   371             break;
       
   372         default:
       
   373             goto error_flash;
       
   374         }
       
   375         return;
       
   376     case 3: /* Confirm mode */
       
   377         switch (pfl->cmd) {
       
   378         case 0xe8: /* Block write */
       
   379             if (cmd == 0xd0) {
       
   380                 pfl->wcycle = 0;
       
   381                 pfl->status |= 0x80;
       
   382             } else {
       
   383                 DPRINTF("%s: unknown command for \"write block\"\n", __func__);
       
   384                 PFLASH_BUG("Write block confirm");
       
   385                 goto reset_flash;
       
   386             }
       
   387             break;
       
   388         default:
       
   389             goto error_flash;
       
   390         }
       
   391         return;
       
   392     default:
       
   393         /* Should never happen */
       
   394         DPRINTF("%s: invalid write state\n",  __func__);
       
   395         goto reset_flash;
       
   396     }
       
   397     return;
       
   398 
       
   399  error_flash:
       
   400     printf("%s: Unimplemented flash cmd sequence "
       
   401            "(offset " TARGET_FMT_lx ", wcycle 0x%x cmd 0x%x value 0x%x)\n",
       
   402            __func__, offset, pfl->wcycle, pfl->cmd, value);
       
   403 
       
   404  reset_flash:
       
   405     cpu_register_physical_memory(pfl->base, pfl->total_len,
       
   406                     pfl->off | IO_MEM_ROMD | pfl->fl_mem);
       
   407 
       
   408     pfl->bypass = 0;
       
   409     pfl->wcycle = 0;
       
   410     pfl->cmd = 0;
       
   411     return;
       
   412 }
       
   413 
       
   414 
       
   415 static uint32_t pflash_readb (void *opaque, target_phys_addr_t addr)
       
   416 {
       
   417     return pflash_read(opaque, addr, 1);
       
   418 }
       
   419 
       
   420 static uint32_t pflash_readw (void *opaque, target_phys_addr_t addr)
       
   421 {
       
   422     pflash_t *pfl = opaque;
       
   423 
       
   424     return pflash_read(pfl, addr, 2);
       
   425 }
       
   426 
       
   427 static uint32_t pflash_readl (void *opaque, target_phys_addr_t addr)
       
   428 {
       
   429     pflash_t *pfl = opaque;
       
   430 
       
   431     return pflash_read(pfl, addr, 4);
       
   432 }
       
   433 
       
   434 static void pflash_writeb (void *opaque, target_phys_addr_t addr,
       
   435                            uint32_t value)
       
   436 {
       
   437     pflash_write(opaque, addr, value, 1);
       
   438 }
       
   439 
       
   440 static void pflash_writew (void *opaque, target_phys_addr_t addr,
       
   441                            uint32_t value)
       
   442 {
       
   443     pflash_t *pfl = opaque;
       
   444 
       
   445     pflash_write(pfl, addr, value, 2);
       
   446 }
       
   447 
       
   448 static void pflash_writel (void *opaque, target_phys_addr_t addr,
       
   449                            uint32_t value)
       
   450 {
       
   451     pflash_t *pfl = opaque;
       
   452 
       
   453     pflash_write(pfl, addr, value, 4);
       
   454 }
       
   455 
       
   456 static CPUWriteMemoryFunc *pflash_write_ops[] = {
       
   457     &pflash_writeb,
       
   458     &pflash_writew,
       
   459     &pflash_writel,
       
   460 };
       
   461 
       
   462 static CPUReadMemoryFunc *pflash_read_ops[] = {
       
   463     &pflash_readb,
       
   464     &pflash_readw,
       
   465     &pflash_readl,
       
   466 };
       
   467 
       
   468 /* Count trailing zeroes of a 32 bits quantity */
       
   469 static int ctz32 (uint32_t n)
       
   470 {
       
   471     int ret;
       
   472 
       
   473     ret = 0;
       
   474     if (!(n & 0xFFFF)) {
       
   475         ret += 16;
       
   476         n = n >> 16;
       
   477     }
       
   478     if (!(n & 0xFF)) {
       
   479         ret += 8;
       
   480         n = n >> 8;
       
   481     }
       
   482     if (!(n & 0xF)) {
       
   483         ret += 4;
       
   484         n = n >> 4;
       
   485     }
       
   486     if (!(n & 0x3)) {
       
   487         ret += 2;
       
   488         n = n >> 2;
       
   489     }
       
   490     if (!(n & 0x1)) {
       
   491         ret++;
       
   492         n = n >> 1;
       
   493     }
       
   494 #if 0 /* This is not necessary as n is never 0 */
       
   495     if (!n)
       
   496         ret++;
       
   497 #endif
       
   498 
       
   499     return ret;
       
   500 }
       
   501 
       
   502 pflash_t *pflash_cfi01_register(target_phys_addr_t base, ram_addr_t off,
       
   503                                 BlockDriverState *bs, uint32_t sector_len,
       
   504                                 int nb_blocs, int width,
       
   505                                 uint16_t id0, uint16_t id1,
       
   506                                 uint16_t id2, uint16_t id3)
       
   507 {
       
   508     pflash_t *pfl;
       
   509     target_long total_len;
       
   510 
       
   511     total_len = sector_len * nb_blocs;
       
   512 
       
   513     /* XXX: to be fixed */
       
   514 #if 0
       
   515     if (total_len != (8 * 1024 * 1024) && total_len != (16 * 1024 * 1024) &&
       
   516         total_len != (32 * 1024 * 1024) && total_len != (64 * 1024 * 1024))
       
   517         return NULL;
       
   518 #endif
       
   519 
       
   520     pfl = qemu_mallocz(sizeof(pflash_t));
       
   521 
       
   522     if (pfl == NULL)
       
   523         return NULL;
       
   524     /* FIXME: This is broken if it spans multiple RAM regions.  */
       
   525     pfl->storage = host_ram_addr(off);
       
   526     pfl->fl_mem = cpu_register_io_memory(0,
       
   527                     pflash_read_ops, pflash_write_ops, pfl);
       
   528     pfl->off = off;
       
   529     cpu_register_physical_memory(base, total_len,
       
   530                     off | pfl->fl_mem | IO_MEM_ROMD);
       
   531 
       
   532     pfl->bs = bs;
       
   533     if (pfl->bs) {
       
   534         /* read the initial flash content */
       
   535         bdrv_read(pfl->bs, 0, pfl->storage, total_len >> 9);
       
   536     }
       
   537 #if 0 /* XXX: there should be a bit to set up read-only,
       
   538        *      the same way the hardware does (with WP pin).
       
   539        */
       
   540     pfl->ro = 1;
       
   541 #else
       
   542     pfl->ro = 0;
       
   543 #endif
       
   544     pfl->timer = qemu_new_timer(vm_clock, pflash_timer, pfl);
       
   545     pfl->base = base;
       
   546     pfl->sector_len = sector_len;
       
   547     pfl->total_len = total_len;
       
   548     pfl->width = width;
       
   549     pfl->wcycle = 0;
       
   550     pfl->cmd = 0;
       
   551     pfl->status = 0;
       
   552     pfl->ident[0] = id0;
       
   553     pfl->ident[1] = id1;
       
   554     pfl->ident[2] = id2;
       
   555     pfl->ident[3] = id3;
       
   556     /* Hardcoded CFI table */
       
   557     pfl->cfi_len = 0x52;
       
   558     /* Standard "QRY" string */
       
   559     pfl->cfi_table[0x10] = 'Q';
       
   560     pfl->cfi_table[0x11] = 'R';
       
   561     pfl->cfi_table[0x12] = 'Y';
       
   562     /* Command set (Intel) */
       
   563     pfl->cfi_table[0x13] = 0x01;
       
   564     pfl->cfi_table[0x14] = 0x00;
       
   565     /* Primary extended table address (none) */
       
   566     pfl->cfi_table[0x15] = 0x31;
       
   567     pfl->cfi_table[0x16] = 0x00;
       
   568     /* Alternate command set (none) */
       
   569     pfl->cfi_table[0x17] = 0x00;
       
   570     pfl->cfi_table[0x18] = 0x00;
       
   571     /* Alternate extended table (none) */
       
   572     pfl->cfi_table[0x19] = 0x00;
       
   573     pfl->cfi_table[0x1A] = 0x00;
       
   574     /* Vcc min */
       
   575     pfl->cfi_table[0x1B] = 0x45;
       
   576     /* Vcc max */
       
   577     pfl->cfi_table[0x1C] = 0x55;
       
   578     /* Vpp min (no Vpp pin) */
       
   579     pfl->cfi_table[0x1D] = 0x00;
       
   580     /* Vpp max (no Vpp pin) */
       
   581     pfl->cfi_table[0x1E] = 0x00;
       
   582     /* Reserved */
       
   583     pfl->cfi_table[0x1F] = 0x07;
       
   584     /* Timeout for min size buffer write */
       
   585     pfl->cfi_table[0x20] = 0x07;
       
   586     /* Typical timeout for block erase */
       
   587     pfl->cfi_table[0x21] = 0x0a;
       
   588     /* Typical timeout for full chip erase (4096 ms) */
       
   589     pfl->cfi_table[0x22] = 0x00;
       
   590     /* Reserved */
       
   591     pfl->cfi_table[0x23] = 0x04;
       
   592     /* Max timeout for buffer write */
       
   593     pfl->cfi_table[0x24] = 0x04;
       
   594     /* Max timeout for block erase */
       
   595     pfl->cfi_table[0x25] = 0x04;
       
   596     /* Max timeout for chip erase */
       
   597     pfl->cfi_table[0x26] = 0x00;
       
   598     /* Device size */
       
   599     pfl->cfi_table[0x27] = ctz32(total_len); // + 1;
       
   600     /* Flash device interface (8 & 16 bits) */
       
   601     pfl->cfi_table[0x28] = 0x02;
       
   602     pfl->cfi_table[0x29] = 0x00;
       
   603     /* Max number of bytes in multi-bytes write */
       
   604     pfl->cfi_table[0x2A] = 0x0B;
       
   605     pfl->cfi_table[0x2B] = 0x00;
       
   606     /* Number of erase block regions (uniform) */
       
   607     pfl->cfi_table[0x2C] = 0x01;
       
   608     /* Erase block region 1 */
       
   609     pfl->cfi_table[0x2D] = nb_blocs - 1;
       
   610     pfl->cfi_table[0x2E] = (nb_blocs - 1) >> 8;
       
   611     pfl->cfi_table[0x2F] = sector_len >> 8;
       
   612     pfl->cfi_table[0x30] = sector_len >> 16;
       
   613 
       
   614     /* Extended */
       
   615     pfl->cfi_table[0x31] = 'P';
       
   616     pfl->cfi_table[0x32] = 'R';
       
   617     pfl->cfi_table[0x33] = 'I';
       
   618 
       
   619     pfl->cfi_table[0x34] = '1';
       
   620     pfl->cfi_table[0x35] = '1';
       
   621 
       
   622     pfl->cfi_table[0x36] = 0x00;
       
   623     pfl->cfi_table[0x37] = 0x00;
       
   624     pfl->cfi_table[0x38] = 0x00;
       
   625     pfl->cfi_table[0x39] = 0x00;
       
   626 
       
   627     pfl->cfi_table[0x3a] = 0x00;
       
   628 
       
   629     pfl->cfi_table[0x3b] = 0x00;
       
   630     pfl->cfi_table[0x3c] = 0x00;
       
   631 
       
   632     return pfl;
       
   633 }