xref: /qemu/hw/block/pflash_cfi02.c (revision da3bd642)
149ab747fSPaolo Bonzini /*
249ab747fSPaolo Bonzini  *  CFI parallel flash with AMD command set emulation
349ab747fSPaolo Bonzini  *
449ab747fSPaolo Bonzini  *  Copyright (c) 2005 Jocelyn Mayer
549ab747fSPaolo Bonzini  *
649ab747fSPaolo Bonzini  * This library is free software; you can redistribute it and/or
749ab747fSPaolo Bonzini  * modify it under the terms of the GNU Lesser General Public
849ab747fSPaolo Bonzini  * License as published by the Free Software Foundation; either
949ab747fSPaolo Bonzini  * version 2 of the License, or (at your option) any later version.
1049ab747fSPaolo Bonzini  *
1149ab747fSPaolo Bonzini  * This library is distributed in the hope that it will be useful,
1249ab747fSPaolo Bonzini  * but WITHOUT ANY WARRANTY; without even the implied warranty of
1349ab747fSPaolo Bonzini  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
1449ab747fSPaolo Bonzini  * Lesser General Public License for more details.
1549ab747fSPaolo Bonzini  *
1649ab747fSPaolo Bonzini  * You should have received a copy of the GNU Lesser General Public
1749ab747fSPaolo Bonzini  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
1849ab747fSPaolo Bonzini  */
1949ab747fSPaolo Bonzini 
2049ab747fSPaolo Bonzini /*
2149ab747fSPaolo Bonzini  * For now, this code can emulate flashes of 1, 2 or 4 bytes width.
2249ab747fSPaolo Bonzini  * Supported commands/modes are:
2349ab747fSPaolo Bonzini  * - flash read
2449ab747fSPaolo Bonzini  * - flash write
2549ab747fSPaolo Bonzini  * - flash ID read
2649ab747fSPaolo Bonzini  * - sector erase
2749ab747fSPaolo Bonzini  * - chip erase
2849ab747fSPaolo Bonzini  * - unlock bypass command
2949ab747fSPaolo Bonzini  * - CFI queries
3049ab747fSPaolo Bonzini  *
3149ab747fSPaolo Bonzini  * It does not support flash interleaving.
3249ab747fSPaolo Bonzini  * It does not implement boot blocs with reduced size
3349ab747fSPaolo Bonzini  * It does not implement software data protection as found in many real chips
3449ab747fSPaolo Bonzini  * It does not implement erase suspend/resume commands
3549ab747fSPaolo Bonzini  * It does not implement multiple sectors erase
3649ab747fSPaolo Bonzini  */
3749ab747fSPaolo Bonzini 
3849ab747fSPaolo Bonzini #include "hw/hw.h"
3949ab747fSPaolo Bonzini #include "hw/block/flash.h"
4049ab747fSPaolo Bonzini #include "qemu/timer.h"
4149ab747fSPaolo Bonzini #include "block/block.h"
4249ab747fSPaolo Bonzini #include "exec/address-spaces.h"
4349ab747fSPaolo Bonzini #include "qemu/host-utils.h"
4449ab747fSPaolo Bonzini #include "hw/sysbus.h"
4549ab747fSPaolo Bonzini 
4649ab747fSPaolo Bonzini //#define PFLASH_DEBUG
4749ab747fSPaolo Bonzini #ifdef PFLASH_DEBUG
4849ab747fSPaolo Bonzini #define DPRINTF(fmt, ...)                                  \
4949ab747fSPaolo Bonzini do {                                                       \
5049ab747fSPaolo Bonzini     fprintf(stderr "PFLASH: " fmt , ## __VA_ARGS__);       \
5149ab747fSPaolo Bonzini } while (0)
5249ab747fSPaolo Bonzini #else
5349ab747fSPaolo Bonzini #define DPRINTF(fmt, ...) do { } while (0)
5449ab747fSPaolo Bonzini #endif
5549ab747fSPaolo Bonzini 
5649ab747fSPaolo Bonzini #define PFLASH_LAZY_ROMD_THRESHOLD 42
5749ab747fSPaolo Bonzini 
583509c396SHu Tao #define TYPE_CFI_PFLASH02 "cfi.pflash02"
593509c396SHu Tao #define CFI_PFLASH02(obj) OBJECT_CHECK(pflash_t, (obj), TYPE_CFI_PFLASH02)
603509c396SHu Tao 
6149ab747fSPaolo Bonzini struct pflash_t {
623509c396SHu Tao     /*< private >*/
633509c396SHu Tao     SysBusDevice parent_obj;
643509c396SHu Tao     /*< public >*/
653509c396SHu Tao 
6649ab747fSPaolo Bonzini     BlockDriverState *bs;
6749ab747fSPaolo Bonzini     uint32_t sector_len;
6849ab747fSPaolo Bonzini     uint32_t nb_blocs;
6949ab747fSPaolo Bonzini     uint32_t chip_len;
7049ab747fSPaolo Bonzini     uint8_t mappings;
7149ab747fSPaolo Bonzini     uint8_t width;
7249ab747fSPaolo Bonzini     uint8_t be;
7349ab747fSPaolo Bonzini     int wcycle; /* if 0, the flash is read normally */
7449ab747fSPaolo Bonzini     int bypass;
7549ab747fSPaolo Bonzini     int ro;
7649ab747fSPaolo Bonzini     uint8_t cmd;
7749ab747fSPaolo Bonzini     uint8_t status;
7849ab747fSPaolo Bonzini     /* FIXME: implement array device properties */
7949ab747fSPaolo Bonzini     uint16_t ident0;
8049ab747fSPaolo Bonzini     uint16_t ident1;
8149ab747fSPaolo Bonzini     uint16_t ident2;
8249ab747fSPaolo Bonzini     uint16_t ident3;
8349ab747fSPaolo Bonzini     uint16_t unlock_addr0;
8449ab747fSPaolo Bonzini     uint16_t unlock_addr1;
8549ab747fSPaolo Bonzini     uint8_t cfi_len;
8649ab747fSPaolo Bonzini     uint8_t cfi_table[0x52];
8749ab747fSPaolo Bonzini     QEMUTimer *timer;
8849ab747fSPaolo Bonzini     /* The device replicates the flash memory across its memory space.  Emulate
8949ab747fSPaolo Bonzini      * that by having a container (.mem) filled with an array of aliases
9049ab747fSPaolo Bonzini      * (.mem_mappings) pointing to the flash memory (.orig_mem).
9149ab747fSPaolo Bonzini      */
9249ab747fSPaolo Bonzini     MemoryRegion mem;
9349ab747fSPaolo Bonzini     MemoryRegion *mem_mappings;    /* array; one per mapping */
9449ab747fSPaolo Bonzini     MemoryRegion orig_mem;
9549ab747fSPaolo Bonzini     int rom_mode;
9649ab747fSPaolo Bonzini     int read_counter; /* used for lazy switch-back to rom mode */
9749ab747fSPaolo Bonzini     char *name;
9849ab747fSPaolo Bonzini     void *storage;
9949ab747fSPaolo Bonzini };
10049ab747fSPaolo Bonzini 
10149ab747fSPaolo Bonzini /*
10249ab747fSPaolo Bonzini  * Set up replicated mappings of the same region.
10349ab747fSPaolo Bonzini  */
10449ab747fSPaolo Bonzini static void pflash_setup_mappings(pflash_t *pfl)
10549ab747fSPaolo Bonzini {
10649ab747fSPaolo Bonzini     unsigned i;
10749ab747fSPaolo Bonzini     hwaddr size = memory_region_size(&pfl->orig_mem);
10849ab747fSPaolo Bonzini 
1092d256e6fSPaolo Bonzini     memory_region_init(&pfl->mem, OBJECT(pfl), "pflash", pfl->mappings * size);
11049ab747fSPaolo Bonzini     pfl->mem_mappings = g_new(MemoryRegion, pfl->mappings);
11149ab747fSPaolo Bonzini     for (i = 0; i < pfl->mappings; ++i) {
1122d256e6fSPaolo Bonzini         memory_region_init_alias(&pfl->mem_mappings[i], OBJECT(pfl),
1132d256e6fSPaolo Bonzini                                  "pflash-alias", &pfl->orig_mem, 0, size);
11449ab747fSPaolo Bonzini         memory_region_add_subregion(&pfl->mem, i * size, &pfl->mem_mappings[i]);
11549ab747fSPaolo Bonzini     }
11649ab747fSPaolo Bonzini }
11749ab747fSPaolo Bonzini 
11849ab747fSPaolo Bonzini static void pflash_register_memory(pflash_t *pfl, int rom_mode)
11949ab747fSPaolo Bonzini {
1205f9a5ea1SJan Kiszka     memory_region_rom_device_set_romd(&pfl->orig_mem, rom_mode);
12149ab747fSPaolo Bonzini     pfl->rom_mode = rom_mode;
12249ab747fSPaolo Bonzini }
12349ab747fSPaolo Bonzini 
12449ab747fSPaolo Bonzini static void pflash_timer (void *opaque)
12549ab747fSPaolo Bonzini {
12649ab747fSPaolo Bonzini     pflash_t *pfl = opaque;
12749ab747fSPaolo Bonzini 
12849ab747fSPaolo Bonzini     DPRINTF("%s: command %02x done\n", __func__, pfl->cmd);
12949ab747fSPaolo Bonzini     /* Reset flash */
13049ab747fSPaolo Bonzini     pfl->status ^= 0x80;
13149ab747fSPaolo Bonzini     if (pfl->bypass) {
13249ab747fSPaolo Bonzini         pfl->wcycle = 2;
13349ab747fSPaolo Bonzini     } else {
13449ab747fSPaolo Bonzini         pflash_register_memory(pfl, 1);
13549ab747fSPaolo Bonzini         pfl->wcycle = 0;
13649ab747fSPaolo Bonzini     }
13749ab747fSPaolo Bonzini     pfl->cmd = 0;
13849ab747fSPaolo Bonzini }
13949ab747fSPaolo Bonzini 
14049ab747fSPaolo Bonzini static uint32_t pflash_read (pflash_t *pfl, hwaddr offset,
14149ab747fSPaolo Bonzini                              int width, int be)
14249ab747fSPaolo Bonzini {
14349ab747fSPaolo Bonzini     hwaddr boff;
14449ab747fSPaolo Bonzini     uint32_t ret;
14549ab747fSPaolo Bonzini     uint8_t *p;
14649ab747fSPaolo Bonzini 
14749ab747fSPaolo Bonzini     DPRINTF("%s: offset " TARGET_FMT_plx "\n", __func__, offset);
14849ab747fSPaolo Bonzini     ret = -1;
14949ab747fSPaolo Bonzini     /* Lazy reset to ROMD mode after a certain amount of read accesses */
15049ab747fSPaolo Bonzini     if (!pfl->rom_mode && pfl->wcycle == 0 &&
15149ab747fSPaolo Bonzini         ++pfl->read_counter > PFLASH_LAZY_ROMD_THRESHOLD) {
15249ab747fSPaolo Bonzini         pflash_register_memory(pfl, 1);
15349ab747fSPaolo Bonzini     }
15449ab747fSPaolo Bonzini     offset &= pfl->chip_len - 1;
15549ab747fSPaolo Bonzini     boff = offset & 0xFF;
15649ab747fSPaolo Bonzini     if (pfl->width == 2)
15749ab747fSPaolo Bonzini         boff = boff >> 1;
15849ab747fSPaolo Bonzini     else if (pfl->width == 4)
15949ab747fSPaolo Bonzini         boff = boff >> 2;
16049ab747fSPaolo Bonzini     switch (pfl->cmd) {
16149ab747fSPaolo Bonzini     default:
16249ab747fSPaolo Bonzini         /* This should never happen : reset state & treat it as a read*/
16349ab747fSPaolo Bonzini         DPRINTF("%s: unknown command state: %x\n", __func__, pfl->cmd);
16449ab747fSPaolo Bonzini         pfl->wcycle = 0;
16549ab747fSPaolo Bonzini         pfl->cmd = 0;
16649ab747fSPaolo Bonzini         /* fall through to the read code */
16749ab747fSPaolo Bonzini     case 0x80:
16849ab747fSPaolo Bonzini         /* We accept reads during second unlock sequence... */
16949ab747fSPaolo Bonzini     case 0x00:
17049ab747fSPaolo Bonzini     flash_read:
17149ab747fSPaolo Bonzini         /* Flash area read */
17249ab747fSPaolo Bonzini         p = pfl->storage;
17349ab747fSPaolo Bonzini         switch (width) {
17449ab747fSPaolo Bonzini         case 1:
17549ab747fSPaolo Bonzini             ret = p[offset];
17649ab747fSPaolo Bonzini //            DPRINTF("%s: data offset %08x %02x\n", __func__, offset, ret);
17749ab747fSPaolo Bonzini             break;
17849ab747fSPaolo Bonzini         case 2:
17949ab747fSPaolo Bonzini             if (be) {
18049ab747fSPaolo Bonzini                 ret = p[offset] << 8;
18149ab747fSPaolo Bonzini                 ret |= p[offset + 1];
18249ab747fSPaolo Bonzini             } else {
18349ab747fSPaolo Bonzini                 ret = p[offset];
18449ab747fSPaolo Bonzini                 ret |= p[offset + 1] << 8;
18549ab747fSPaolo Bonzini             }
18649ab747fSPaolo Bonzini //            DPRINTF("%s: data offset %08x %04x\n", __func__, offset, ret);
18749ab747fSPaolo Bonzini             break;
18849ab747fSPaolo Bonzini         case 4:
18949ab747fSPaolo Bonzini             if (be) {
19049ab747fSPaolo Bonzini                 ret = p[offset] << 24;
19149ab747fSPaolo Bonzini                 ret |= p[offset + 1] << 16;
19249ab747fSPaolo Bonzini                 ret |= p[offset + 2] << 8;
19349ab747fSPaolo Bonzini                 ret |= p[offset + 3];
19449ab747fSPaolo Bonzini             } else {
19549ab747fSPaolo Bonzini                 ret = p[offset];
19649ab747fSPaolo Bonzini                 ret |= p[offset + 1] << 8;
19749ab747fSPaolo Bonzini                 ret |= p[offset + 2] << 16;
19849ab747fSPaolo Bonzini                 ret |= p[offset + 3] << 24;
19949ab747fSPaolo Bonzini             }
20049ab747fSPaolo Bonzini //            DPRINTF("%s: data offset %08x %08x\n", __func__, offset, ret);
20149ab747fSPaolo Bonzini             break;
20249ab747fSPaolo Bonzini         }
20349ab747fSPaolo Bonzini         break;
20449ab747fSPaolo Bonzini     case 0x90:
20549ab747fSPaolo Bonzini         /* flash ID read */
20649ab747fSPaolo Bonzini         switch (boff) {
20749ab747fSPaolo Bonzini         case 0x00:
20849ab747fSPaolo Bonzini         case 0x01:
20949ab747fSPaolo Bonzini             ret = boff & 0x01 ? pfl->ident1 : pfl->ident0;
21049ab747fSPaolo Bonzini             break;
21149ab747fSPaolo Bonzini         case 0x02:
21249ab747fSPaolo Bonzini             ret = 0x00; /* Pretend all sectors are unprotected */
21349ab747fSPaolo Bonzini             break;
21449ab747fSPaolo Bonzini         case 0x0E:
21549ab747fSPaolo Bonzini         case 0x0F:
21649ab747fSPaolo Bonzini             ret = boff & 0x01 ? pfl->ident3 : pfl->ident2;
21749ab747fSPaolo Bonzini             if (ret == (uint8_t)-1) {
21849ab747fSPaolo Bonzini                 goto flash_read;
21949ab747fSPaolo Bonzini             }
22049ab747fSPaolo Bonzini             break;
22149ab747fSPaolo Bonzini         default:
22249ab747fSPaolo Bonzini             goto flash_read;
22349ab747fSPaolo Bonzini         }
22449ab747fSPaolo Bonzini         DPRINTF("%s: ID " TARGET_FMT_plx " %x\n", __func__, boff, ret);
22549ab747fSPaolo Bonzini         break;
22649ab747fSPaolo Bonzini     case 0xA0:
22749ab747fSPaolo Bonzini     case 0x10:
22849ab747fSPaolo Bonzini     case 0x30:
22949ab747fSPaolo Bonzini         /* Status register read */
23049ab747fSPaolo Bonzini         ret = pfl->status;
23149ab747fSPaolo Bonzini         DPRINTF("%s: status %x\n", __func__, ret);
23249ab747fSPaolo Bonzini         /* Toggle bit 6 */
23349ab747fSPaolo Bonzini         pfl->status ^= 0x40;
23449ab747fSPaolo Bonzini         break;
23549ab747fSPaolo Bonzini     case 0x98:
23649ab747fSPaolo Bonzini         /* CFI query mode */
23749ab747fSPaolo Bonzini         if (boff > pfl->cfi_len)
23849ab747fSPaolo Bonzini             ret = 0;
23949ab747fSPaolo Bonzini         else
24049ab747fSPaolo Bonzini             ret = pfl->cfi_table[boff];
24149ab747fSPaolo Bonzini         break;
24249ab747fSPaolo Bonzini     }
24349ab747fSPaolo Bonzini 
24449ab747fSPaolo Bonzini     return ret;
24549ab747fSPaolo Bonzini }
24649ab747fSPaolo Bonzini 
24749ab747fSPaolo Bonzini /* update flash content on disk */
24849ab747fSPaolo Bonzini static void pflash_update(pflash_t *pfl, int offset,
24949ab747fSPaolo Bonzini                           int size)
25049ab747fSPaolo Bonzini {
25149ab747fSPaolo Bonzini     int offset_end;
25249ab747fSPaolo Bonzini     if (pfl->bs) {
25349ab747fSPaolo Bonzini         offset_end = offset + size;
25449ab747fSPaolo Bonzini         /* round to sectors */
25549ab747fSPaolo Bonzini         offset = offset >> 9;
25649ab747fSPaolo Bonzini         offset_end = (offset_end + 511) >> 9;
25749ab747fSPaolo Bonzini         bdrv_write(pfl->bs, offset, pfl->storage + (offset << 9),
25849ab747fSPaolo Bonzini                    offset_end - offset);
25949ab747fSPaolo Bonzini     }
26049ab747fSPaolo Bonzini }
26149ab747fSPaolo Bonzini 
26249ab747fSPaolo Bonzini static void pflash_write (pflash_t *pfl, hwaddr offset,
26349ab747fSPaolo Bonzini                           uint32_t value, int width, int be)
26449ab747fSPaolo Bonzini {
26549ab747fSPaolo Bonzini     hwaddr boff;
26649ab747fSPaolo Bonzini     uint8_t *p;
26749ab747fSPaolo Bonzini     uint8_t cmd;
26849ab747fSPaolo Bonzini 
26949ab747fSPaolo Bonzini     cmd = value;
27049ab747fSPaolo Bonzini     if (pfl->cmd != 0xA0 && cmd == 0xF0) {
27149ab747fSPaolo Bonzini #if 0
27249ab747fSPaolo Bonzini         DPRINTF("%s: flash reset asked (%02x %02x)\n",
27349ab747fSPaolo Bonzini                 __func__, pfl->cmd, cmd);
27449ab747fSPaolo Bonzini #endif
27549ab747fSPaolo Bonzini         goto reset_flash;
27649ab747fSPaolo Bonzini     }
27749ab747fSPaolo Bonzini     DPRINTF("%s: offset " TARGET_FMT_plx " %08x %d %d\n", __func__,
27849ab747fSPaolo Bonzini             offset, value, width, pfl->wcycle);
27949ab747fSPaolo Bonzini     offset &= pfl->chip_len - 1;
28049ab747fSPaolo Bonzini 
28149ab747fSPaolo Bonzini     DPRINTF("%s: offset " TARGET_FMT_plx " %08x %d\n", __func__,
28249ab747fSPaolo Bonzini             offset, value, width);
28349ab747fSPaolo Bonzini     boff = offset & (pfl->sector_len - 1);
28449ab747fSPaolo Bonzini     if (pfl->width == 2)
28549ab747fSPaolo Bonzini         boff = boff >> 1;
28649ab747fSPaolo Bonzini     else if (pfl->width == 4)
28749ab747fSPaolo Bonzini         boff = boff >> 2;
28849ab747fSPaolo Bonzini     switch (pfl->wcycle) {
28949ab747fSPaolo Bonzini     case 0:
29049ab747fSPaolo Bonzini         /* Set the device in I/O access mode if required */
29149ab747fSPaolo Bonzini         if (pfl->rom_mode)
29249ab747fSPaolo Bonzini             pflash_register_memory(pfl, 0);
29349ab747fSPaolo Bonzini         pfl->read_counter = 0;
29449ab747fSPaolo Bonzini         /* We're in read mode */
29549ab747fSPaolo Bonzini     check_unlock0:
29649ab747fSPaolo Bonzini         if (boff == 0x55 && cmd == 0x98) {
29749ab747fSPaolo Bonzini         enter_CFI_mode:
29849ab747fSPaolo Bonzini             /* Enter CFI query mode */
29949ab747fSPaolo Bonzini             pfl->wcycle = 7;
30049ab747fSPaolo Bonzini             pfl->cmd = 0x98;
30149ab747fSPaolo Bonzini             return;
30249ab747fSPaolo Bonzini         }
30349ab747fSPaolo Bonzini         if (boff != pfl->unlock_addr0 || cmd != 0xAA) {
30449ab747fSPaolo Bonzini             DPRINTF("%s: unlock0 failed " TARGET_FMT_plx " %02x %04x\n",
30549ab747fSPaolo Bonzini                     __func__, boff, cmd, pfl->unlock_addr0);
30649ab747fSPaolo Bonzini             goto reset_flash;
30749ab747fSPaolo Bonzini         }
30849ab747fSPaolo Bonzini         DPRINTF("%s: unlock sequence started\n", __func__);
30949ab747fSPaolo Bonzini         break;
31049ab747fSPaolo Bonzini     case 1:
31149ab747fSPaolo Bonzini         /* We started an unlock sequence */
31249ab747fSPaolo Bonzini     check_unlock1:
31349ab747fSPaolo Bonzini         if (boff != pfl->unlock_addr1 || cmd != 0x55) {
31449ab747fSPaolo Bonzini             DPRINTF("%s: unlock1 failed " TARGET_FMT_plx " %02x\n", __func__,
31549ab747fSPaolo Bonzini                     boff, cmd);
31649ab747fSPaolo Bonzini             goto reset_flash;
31749ab747fSPaolo Bonzini         }
31849ab747fSPaolo Bonzini         DPRINTF("%s: unlock sequence done\n", __func__);
31949ab747fSPaolo Bonzini         break;
32049ab747fSPaolo Bonzini     case 2:
32149ab747fSPaolo Bonzini         /* We finished an unlock sequence */
32249ab747fSPaolo Bonzini         if (!pfl->bypass && boff != pfl->unlock_addr0) {
32349ab747fSPaolo Bonzini             DPRINTF("%s: command failed " TARGET_FMT_plx " %02x\n", __func__,
32449ab747fSPaolo Bonzini                     boff, cmd);
32549ab747fSPaolo Bonzini             goto reset_flash;
32649ab747fSPaolo Bonzini         }
32749ab747fSPaolo Bonzini         switch (cmd) {
32849ab747fSPaolo Bonzini         case 0x20:
32949ab747fSPaolo Bonzini             pfl->bypass = 1;
33049ab747fSPaolo Bonzini             goto do_bypass;
33149ab747fSPaolo Bonzini         case 0x80:
33249ab747fSPaolo Bonzini         case 0x90:
33349ab747fSPaolo Bonzini         case 0xA0:
33449ab747fSPaolo Bonzini             pfl->cmd = cmd;
33549ab747fSPaolo Bonzini             DPRINTF("%s: starting command %02x\n", __func__, cmd);
33649ab747fSPaolo Bonzini             break;
33749ab747fSPaolo Bonzini         default:
33849ab747fSPaolo Bonzini             DPRINTF("%s: unknown command %02x\n", __func__, cmd);
33949ab747fSPaolo Bonzini             goto reset_flash;
34049ab747fSPaolo Bonzini         }
34149ab747fSPaolo Bonzini         break;
34249ab747fSPaolo Bonzini     case 3:
34349ab747fSPaolo Bonzini         switch (pfl->cmd) {
34449ab747fSPaolo Bonzini         case 0x80:
34549ab747fSPaolo Bonzini             /* We need another unlock sequence */
34649ab747fSPaolo Bonzini             goto check_unlock0;
34749ab747fSPaolo Bonzini         case 0xA0:
34849ab747fSPaolo Bonzini             DPRINTF("%s: write data offset " TARGET_FMT_plx " %08x %d\n",
34949ab747fSPaolo Bonzini                     __func__, offset, value, width);
35049ab747fSPaolo Bonzini             p = pfl->storage;
35149ab747fSPaolo Bonzini             if (!pfl->ro) {
35249ab747fSPaolo Bonzini                 switch (width) {
35349ab747fSPaolo Bonzini                 case 1:
35449ab747fSPaolo Bonzini                     p[offset] &= value;
35549ab747fSPaolo Bonzini                     pflash_update(pfl, offset, 1);
35649ab747fSPaolo Bonzini                     break;
35749ab747fSPaolo Bonzini                 case 2:
35849ab747fSPaolo Bonzini                     if (be) {
35949ab747fSPaolo Bonzini                         p[offset] &= value >> 8;
36049ab747fSPaolo Bonzini                         p[offset + 1] &= value;
36149ab747fSPaolo Bonzini                     } else {
36249ab747fSPaolo Bonzini                         p[offset] &= value;
36349ab747fSPaolo Bonzini                         p[offset + 1] &= value >> 8;
36449ab747fSPaolo Bonzini                     }
36549ab747fSPaolo Bonzini                     pflash_update(pfl, offset, 2);
36649ab747fSPaolo Bonzini                     break;
36749ab747fSPaolo Bonzini                 case 4:
36849ab747fSPaolo Bonzini                     if (be) {
36949ab747fSPaolo Bonzini                         p[offset] &= value >> 24;
37049ab747fSPaolo Bonzini                         p[offset + 1] &= value >> 16;
37149ab747fSPaolo Bonzini                         p[offset + 2] &= value >> 8;
37249ab747fSPaolo Bonzini                         p[offset + 3] &= value;
37349ab747fSPaolo Bonzini                     } else {
37449ab747fSPaolo Bonzini                         p[offset] &= value;
37549ab747fSPaolo Bonzini                         p[offset + 1] &= value >> 8;
37649ab747fSPaolo Bonzini                         p[offset + 2] &= value >> 16;
37749ab747fSPaolo Bonzini                         p[offset + 3] &= value >> 24;
37849ab747fSPaolo Bonzini                     }
37949ab747fSPaolo Bonzini                     pflash_update(pfl, offset, 4);
38049ab747fSPaolo Bonzini                     break;
38149ab747fSPaolo Bonzini                 }
38249ab747fSPaolo Bonzini             }
38349ab747fSPaolo Bonzini             pfl->status = 0x00 | ~(value & 0x80);
38449ab747fSPaolo Bonzini             /* Let's pretend write is immediate */
38549ab747fSPaolo Bonzini             if (pfl->bypass)
38649ab747fSPaolo Bonzini                 goto do_bypass;
38749ab747fSPaolo Bonzini             goto reset_flash;
38849ab747fSPaolo Bonzini         case 0x90:
38949ab747fSPaolo Bonzini             if (pfl->bypass && cmd == 0x00) {
39049ab747fSPaolo Bonzini                 /* Unlock bypass reset */
39149ab747fSPaolo Bonzini                 goto reset_flash;
39249ab747fSPaolo Bonzini             }
39349ab747fSPaolo Bonzini             /* We can enter CFI query mode from autoselect mode */
39449ab747fSPaolo Bonzini             if (boff == 0x55 && cmd == 0x98)
39549ab747fSPaolo Bonzini                 goto enter_CFI_mode;
39649ab747fSPaolo Bonzini             /* No break here */
39749ab747fSPaolo Bonzini         default:
39849ab747fSPaolo Bonzini             DPRINTF("%s: invalid write for command %02x\n",
39949ab747fSPaolo Bonzini                     __func__, pfl->cmd);
40049ab747fSPaolo Bonzini             goto reset_flash;
40149ab747fSPaolo Bonzini         }
40249ab747fSPaolo Bonzini     case 4:
40349ab747fSPaolo Bonzini         switch (pfl->cmd) {
40449ab747fSPaolo Bonzini         case 0xA0:
40549ab747fSPaolo Bonzini             /* Ignore writes while flash data write is occurring */
40649ab747fSPaolo Bonzini             /* As we suppose write is immediate, this should never happen */
40749ab747fSPaolo Bonzini             return;
40849ab747fSPaolo Bonzini         case 0x80:
40949ab747fSPaolo Bonzini             goto check_unlock1;
41049ab747fSPaolo Bonzini         default:
41149ab747fSPaolo Bonzini             /* Should never happen */
41249ab747fSPaolo Bonzini             DPRINTF("%s: invalid command state %02x (wc 4)\n",
41349ab747fSPaolo Bonzini                     __func__, pfl->cmd);
41449ab747fSPaolo Bonzini             goto reset_flash;
41549ab747fSPaolo Bonzini         }
41649ab747fSPaolo Bonzini         break;
41749ab747fSPaolo Bonzini     case 5:
41849ab747fSPaolo Bonzini         switch (cmd) {
41949ab747fSPaolo Bonzini         case 0x10:
42049ab747fSPaolo Bonzini             if (boff != pfl->unlock_addr0) {
42149ab747fSPaolo Bonzini                 DPRINTF("%s: chip erase: invalid address " TARGET_FMT_plx "\n",
42249ab747fSPaolo Bonzini                         __func__, offset);
42349ab747fSPaolo Bonzini                 goto reset_flash;
42449ab747fSPaolo Bonzini             }
42549ab747fSPaolo Bonzini             /* Chip erase */
42649ab747fSPaolo Bonzini             DPRINTF("%s: start chip erase\n", __func__);
42749ab747fSPaolo Bonzini             if (!pfl->ro) {
42849ab747fSPaolo Bonzini                 memset(pfl->storage, 0xFF, pfl->chip_len);
42949ab747fSPaolo Bonzini                 pflash_update(pfl, 0, pfl->chip_len);
43049ab747fSPaolo Bonzini             }
43149ab747fSPaolo Bonzini             pfl->status = 0x00;
43249ab747fSPaolo Bonzini             /* Let's wait 5 seconds before chip erase is done */
43349ab747fSPaolo Bonzini             qemu_mod_timer(pfl->timer,
43449ab747fSPaolo Bonzini                            qemu_get_clock_ns(vm_clock) + (get_ticks_per_sec() * 5));
43549ab747fSPaolo Bonzini             break;
43649ab747fSPaolo Bonzini         case 0x30:
43749ab747fSPaolo Bonzini             /* Sector erase */
43849ab747fSPaolo Bonzini             p = pfl->storage;
43949ab747fSPaolo Bonzini             offset &= ~(pfl->sector_len - 1);
44049ab747fSPaolo Bonzini             DPRINTF("%s: start sector erase at " TARGET_FMT_plx "\n", __func__,
44149ab747fSPaolo Bonzini                     offset);
44249ab747fSPaolo Bonzini             if (!pfl->ro) {
44349ab747fSPaolo Bonzini                 memset(p + offset, 0xFF, pfl->sector_len);
44449ab747fSPaolo Bonzini                 pflash_update(pfl, offset, pfl->sector_len);
44549ab747fSPaolo Bonzini             }
44649ab747fSPaolo Bonzini             pfl->status = 0x00;
44749ab747fSPaolo Bonzini             /* Let's wait 1/2 second before sector erase is done */
44849ab747fSPaolo Bonzini             qemu_mod_timer(pfl->timer,
44949ab747fSPaolo Bonzini                            qemu_get_clock_ns(vm_clock) + (get_ticks_per_sec() / 2));
45049ab747fSPaolo Bonzini             break;
45149ab747fSPaolo Bonzini         default:
45249ab747fSPaolo Bonzini             DPRINTF("%s: invalid command %02x (wc 5)\n", __func__, cmd);
45349ab747fSPaolo Bonzini             goto reset_flash;
45449ab747fSPaolo Bonzini         }
45549ab747fSPaolo Bonzini         pfl->cmd = cmd;
45649ab747fSPaolo Bonzini         break;
45749ab747fSPaolo Bonzini     case 6:
45849ab747fSPaolo Bonzini         switch (pfl->cmd) {
45949ab747fSPaolo Bonzini         case 0x10:
46049ab747fSPaolo Bonzini             /* Ignore writes during chip erase */
46149ab747fSPaolo Bonzini             return;
46249ab747fSPaolo Bonzini         case 0x30:
46349ab747fSPaolo Bonzini             /* Ignore writes during sector erase */
46449ab747fSPaolo Bonzini             return;
46549ab747fSPaolo Bonzini         default:
46649ab747fSPaolo Bonzini             /* Should never happen */
46749ab747fSPaolo Bonzini             DPRINTF("%s: invalid command state %02x (wc 6)\n",
46849ab747fSPaolo Bonzini                     __func__, pfl->cmd);
46949ab747fSPaolo Bonzini             goto reset_flash;
47049ab747fSPaolo Bonzini         }
47149ab747fSPaolo Bonzini         break;
47249ab747fSPaolo Bonzini     case 7: /* Special value for CFI queries */
47349ab747fSPaolo Bonzini         DPRINTF("%s: invalid write in CFI query mode\n", __func__);
47449ab747fSPaolo Bonzini         goto reset_flash;
47549ab747fSPaolo Bonzini     default:
47649ab747fSPaolo Bonzini         /* Should never happen */
47749ab747fSPaolo Bonzini         DPRINTF("%s: invalid write state (wc 7)\n",  __func__);
47849ab747fSPaolo Bonzini         goto reset_flash;
47949ab747fSPaolo Bonzini     }
48049ab747fSPaolo Bonzini     pfl->wcycle++;
48149ab747fSPaolo Bonzini 
48249ab747fSPaolo Bonzini     return;
48349ab747fSPaolo Bonzini 
48449ab747fSPaolo Bonzini     /* Reset flash */
48549ab747fSPaolo Bonzini  reset_flash:
48649ab747fSPaolo Bonzini     pfl->bypass = 0;
48749ab747fSPaolo Bonzini     pfl->wcycle = 0;
48849ab747fSPaolo Bonzini     pfl->cmd = 0;
48949ab747fSPaolo Bonzini     return;
49049ab747fSPaolo Bonzini 
49149ab747fSPaolo Bonzini  do_bypass:
49249ab747fSPaolo Bonzini     pfl->wcycle = 2;
49349ab747fSPaolo Bonzini     pfl->cmd = 0;
49449ab747fSPaolo Bonzini }
49549ab747fSPaolo Bonzini 
49649ab747fSPaolo Bonzini 
49749ab747fSPaolo Bonzini static uint32_t pflash_readb_be(void *opaque, hwaddr addr)
49849ab747fSPaolo Bonzini {
49949ab747fSPaolo Bonzini     return pflash_read(opaque, addr, 1, 1);
50049ab747fSPaolo Bonzini }
50149ab747fSPaolo Bonzini 
50249ab747fSPaolo Bonzini static uint32_t pflash_readb_le(void *opaque, hwaddr addr)
50349ab747fSPaolo Bonzini {
50449ab747fSPaolo Bonzini     return pflash_read(opaque, addr, 1, 0);
50549ab747fSPaolo Bonzini }
50649ab747fSPaolo Bonzini 
50749ab747fSPaolo Bonzini static uint32_t pflash_readw_be(void *opaque, hwaddr addr)
50849ab747fSPaolo Bonzini {
50949ab747fSPaolo Bonzini     pflash_t *pfl = opaque;
51049ab747fSPaolo Bonzini 
51149ab747fSPaolo Bonzini     return pflash_read(pfl, addr, 2, 1);
51249ab747fSPaolo Bonzini }
51349ab747fSPaolo Bonzini 
51449ab747fSPaolo Bonzini static uint32_t pflash_readw_le(void *opaque, hwaddr addr)
51549ab747fSPaolo Bonzini {
51649ab747fSPaolo Bonzini     pflash_t *pfl = opaque;
51749ab747fSPaolo Bonzini 
51849ab747fSPaolo Bonzini     return pflash_read(pfl, addr, 2, 0);
51949ab747fSPaolo Bonzini }
52049ab747fSPaolo Bonzini 
52149ab747fSPaolo Bonzini static uint32_t pflash_readl_be(void *opaque, hwaddr addr)
52249ab747fSPaolo Bonzini {
52349ab747fSPaolo Bonzini     pflash_t *pfl = opaque;
52449ab747fSPaolo Bonzini 
52549ab747fSPaolo Bonzini     return pflash_read(pfl, addr, 4, 1);
52649ab747fSPaolo Bonzini }
52749ab747fSPaolo Bonzini 
52849ab747fSPaolo Bonzini static uint32_t pflash_readl_le(void *opaque, hwaddr addr)
52949ab747fSPaolo Bonzini {
53049ab747fSPaolo Bonzini     pflash_t *pfl = opaque;
53149ab747fSPaolo Bonzini 
53249ab747fSPaolo Bonzini     return pflash_read(pfl, addr, 4, 0);
53349ab747fSPaolo Bonzini }
53449ab747fSPaolo Bonzini 
53549ab747fSPaolo Bonzini static void pflash_writeb_be(void *opaque, hwaddr addr,
53649ab747fSPaolo Bonzini                              uint32_t value)
53749ab747fSPaolo Bonzini {
53849ab747fSPaolo Bonzini     pflash_write(opaque, addr, value, 1, 1);
53949ab747fSPaolo Bonzini }
54049ab747fSPaolo Bonzini 
54149ab747fSPaolo Bonzini static void pflash_writeb_le(void *opaque, hwaddr addr,
54249ab747fSPaolo Bonzini                              uint32_t value)
54349ab747fSPaolo Bonzini {
54449ab747fSPaolo Bonzini     pflash_write(opaque, addr, value, 1, 0);
54549ab747fSPaolo Bonzini }
54649ab747fSPaolo Bonzini 
54749ab747fSPaolo Bonzini static void pflash_writew_be(void *opaque, hwaddr addr,
54849ab747fSPaolo Bonzini                              uint32_t value)
54949ab747fSPaolo Bonzini {
55049ab747fSPaolo Bonzini     pflash_t *pfl = opaque;
55149ab747fSPaolo Bonzini 
55249ab747fSPaolo Bonzini     pflash_write(pfl, addr, value, 2, 1);
55349ab747fSPaolo Bonzini }
55449ab747fSPaolo Bonzini 
55549ab747fSPaolo Bonzini static void pflash_writew_le(void *opaque, hwaddr addr,
55649ab747fSPaolo Bonzini                              uint32_t value)
55749ab747fSPaolo Bonzini {
55849ab747fSPaolo Bonzini     pflash_t *pfl = opaque;
55949ab747fSPaolo Bonzini 
56049ab747fSPaolo Bonzini     pflash_write(pfl, addr, value, 2, 0);
56149ab747fSPaolo Bonzini }
56249ab747fSPaolo Bonzini 
56349ab747fSPaolo Bonzini static void pflash_writel_be(void *opaque, hwaddr addr,
56449ab747fSPaolo Bonzini                              uint32_t value)
56549ab747fSPaolo Bonzini {
56649ab747fSPaolo Bonzini     pflash_t *pfl = opaque;
56749ab747fSPaolo Bonzini 
56849ab747fSPaolo Bonzini     pflash_write(pfl, addr, value, 4, 1);
56949ab747fSPaolo Bonzini }
57049ab747fSPaolo Bonzini 
57149ab747fSPaolo Bonzini static void pflash_writel_le(void *opaque, hwaddr addr,
57249ab747fSPaolo Bonzini                              uint32_t value)
57349ab747fSPaolo Bonzini {
57449ab747fSPaolo Bonzini     pflash_t *pfl = opaque;
57549ab747fSPaolo Bonzini 
57649ab747fSPaolo Bonzini     pflash_write(pfl, addr, value, 4, 0);
57749ab747fSPaolo Bonzini }
57849ab747fSPaolo Bonzini 
57949ab747fSPaolo Bonzini static const MemoryRegionOps pflash_cfi02_ops_be = {
58049ab747fSPaolo Bonzini     .old_mmio = {
58149ab747fSPaolo Bonzini         .read = { pflash_readb_be, pflash_readw_be, pflash_readl_be, },
58249ab747fSPaolo Bonzini         .write = { pflash_writeb_be, pflash_writew_be, pflash_writel_be, },
58349ab747fSPaolo Bonzini     },
58449ab747fSPaolo Bonzini     .endianness = DEVICE_NATIVE_ENDIAN,
58549ab747fSPaolo Bonzini };
58649ab747fSPaolo Bonzini 
58749ab747fSPaolo Bonzini static const MemoryRegionOps pflash_cfi02_ops_le = {
58849ab747fSPaolo Bonzini     .old_mmio = {
58949ab747fSPaolo Bonzini         .read = { pflash_readb_le, pflash_readw_le, pflash_readl_le, },
59049ab747fSPaolo Bonzini         .write = { pflash_writeb_le, pflash_writew_le, pflash_writel_le, },
59149ab747fSPaolo Bonzini     },
59249ab747fSPaolo Bonzini     .endianness = DEVICE_NATIVE_ENDIAN,
59349ab747fSPaolo Bonzini };
59449ab747fSPaolo Bonzini 
595*da3bd642SHu Tao static void pflash_cfi02_realize(DeviceState *dev, Error **errp)
59649ab747fSPaolo Bonzini {
5973509c396SHu Tao     pflash_t *pfl = CFI_PFLASH02(dev);
59849ab747fSPaolo Bonzini     uint32_t chip_len;
59949ab747fSPaolo Bonzini     int ret;
60049ab747fSPaolo Bonzini 
60149ab747fSPaolo Bonzini     chip_len = pfl->sector_len * pfl->nb_blocs;
60249ab747fSPaolo Bonzini     /* XXX: to be fixed */
60349ab747fSPaolo Bonzini #if 0
60449ab747fSPaolo Bonzini     if (total_len != (8 * 1024 * 1024) && total_len != (16 * 1024 * 1024) &&
60549ab747fSPaolo Bonzini         total_len != (32 * 1024 * 1024) && total_len != (64 * 1024 * 1024))
60649ab747fSPaolo Bonzini         return NULL;
60749ab747fSPaolo Bonzini #endif
60849ab747fSPaolo Bonzini 
6092d256e6fSPaolo Bonzini     memory_region_init_rom_device(&pfl->orig_mem, OBJECT(pfl), pfl->be ?
61049ab747fSPaolo Bonzini                                   &pflash_cfi02_ops_be : &pflash_cfi02_ops_le,
61149ab747fSPaolo Bonzini                                   pfl, pfl->name, chip_len);
61249ab747fSPaolo Bonzini     vmstate_register_ram(&pfl->orig_mem, DEVICE(pfl));
61349ab747fSPaolo Bonzini     pfl->storage = memory_region_get_ram_ptr(&pfl->orig_mem);
61449ab747fSPaolo Bonzini     pfl->chip_len = chip_len;
61549ab747fSPaolo Bonzini     if (pfl->bs) {
61649ab747fSPaolo Bonzini         /* read the initial flash content */
61749ab747fSPaolo Bonzini         ret = bdrv_read(pfl->bs, 0, pfl->storage, chip_len >> 9);
61849ab747fSPaolo Bonzini         if (ret < 0) {
619*da3bd642SHu Tao             vmstate_unregister_ram(&pfl->orig_mem, DEVICE(pfl));
620*da3bd642SHu Tao             memory_region_destroy(&pfl->orig_mem);
621*da3bd642SHu Tao             error_setg(errp, "failed to read the initial flash content");
622*da3bd642SHu Tao             return;
62349ab747fSPaolo Bonzini         }
62449ab747fSPaolo Bonzini     }
62549ab747fSPaolo Bonzini 
62649ab747fSPaolo Bonzini     pflash_setup_mappings(pfl);
62749ab747fSPaolo Bonzini     pfl->rom_mode = 1;
628*da3bd642SHu Tao     sysbus_init_mmio(SYS_BUS_DEVICE(dev), &pfl->mem);
62949ab747fSPaolo Bonzini 
63049ab747fSPaolo Bonzini     if (pfl->bs) {
63149ab747fSPaolo Bonzini         pfl->ro = bdrv_is_read_only(pfl->bs);
63249ab747fSPaolo Bonzini     } else {
63349ab747fSPaolo Bonzini         pfl->ro = 0;
63449ab747fSPaolo Bonzini     }
63549ab747fSPaolo Bonzini 
63649ab747fSPaolo Bonzini     pfl->timer = qemu_new_timer_ns(vm_clock, pflash_timer, pfl);
63749ab747fSPaolo Bonzini     pfl->wcycle = 0;
63849ab747fSPaolo Bonzini     pfl->cmd = 0;
63949ab747fSPaolo Bonzini     pfl->status = 0;
64049ab747fSPaolo Bonzini     /* Hardcoded CFI table (mostly from SG29 Spansion flash) */
64149ab747fSPaolo Bonzini     pfl->cfi_len = 0x52;
64249ab747fSPaolo Bonzini     /* Standard "QRY" string */
64349ab747fSPaolo Bonzini     pfl->cfi_table[0x10] = 'Q';
64449ab747fSPaolo Bonzini     pfl->cfi_table[0x11] = 'R';
64549ab747fSPaolo Bonzini     pfl->cfi_table[0x12] = 'Y';
64649ab747fSPaolo Bonzini     /* Command set (AMD/Fujitsu) */
64749ab747fSPaolo Bonzini     pfl->cfi_table[0x13] = 0x02;
64849ab747fSPaolo Bonzini     pfl->cfi_table[0x14] = 0x00;
64949ab747fSPaolo Bonzini     /* Primary extended table address */
65049ab747fSPaolo Bonzini     pfl->cfi_table[0x15] = 0x31;
65149ab747fSPaolo Bonzini     pfl->cfi_table[0x16] = 0x00;
65249ab747fSPaolo Bonzini     /* Alternate command set (none) */
65349ab747fSPaolo Bonzini     pfl->cfi_table[0x17] = 0x00;
65449ab747fSPaolo Bonzini     pfl->cfi_table[0x18] = 0x00;
65549ab747fSPaolo Bonzini     /* Alternate extended table (none) */
65649ab747fSPaolo Bonzini     pfl->cfi_table[0x19] = 0x00;
65749ab747fSPaolo Bonzini     pfl->cfi_table[0x1A] = 0x00;
65849ab747fSPaolo Bonzini     /* Vcc min */
65949ab747fSPaolo Bonzini     pfl->cfi_table[0x1B] = 0x27;
66049ab747fSPaolo Bonzini     /* Vcc max */
66149ab747fSPaolo Bonzini     pfl->cfi_table[0x1C] = 0x36;
66249ab747fSPaolo Bonzini     /* Vpp min (no Vpp pin) */
66349ab747fSPaolo Bonzini     pfl->cfi_table[0x1D] = 0x00;
66449ab747fSPaolo Bonzini     /* Vpp max (no Vpp pin) */
66549ab747fSPaolo Bonzini     pfl->cfi_table[0x1E] = 0x00;
66649ab747fSPaolo Bonzini     /* Reserved */
66749ab747fSPaolo Bonzini     pfl->cfi_table[0x1F] = 0x07;
66849ab747fSPaolo Bonzini     /* Timeout for min size buffer write (NA) */
66949ab747fSPaolo Bonzini     pfl->cfi_table[0x20] = 0x00;
67049ab747fSPaolo Bonzini     /* Typical timeout for block erase (512 ms) */
67149ab747fSPaolo Bonzini     pfl->cfi_table[0x21] = 0x09;
67249ab747fSPaolo Bonzini     /* Typical timeout for full chip erase (4096 ms) */
67349ab747fSPaolo Bonzini     pfl->cfi_table[0x22] = 0x0C;
67449ab747fSPaolo Bonzini     /* Reserved */
67549ab747fSPaolo Bonzini     pfl->cfi_table[0x23] = 0x01;
67649ab747fSPaolo Bonzini     /* Max timeout for buffer write (NA) */
67749ab747fSPaolo Bonzini     pfl->cfi_table[0x24] = 0x00;
67849ab747fSPaolo Bonzini     /* Max timeout for block erase */
67949ab747fSPaolo Bonzini     pfl->cfi_table[0x25] = 0x0A;
68049ab747fSPaolo Bonzini     /* Max timeout for chip erase */
68149ab747fSPaolo Bonzini     pfl->cfi_table[0x26] = 0x0D;
68249ab747fSPaolo Bonzini     /* Device size */
68349ab747fSPaolo Bonzini     pfl->cfi_table[0x27] = ctz32(chip_len);
68449ab747fSPaolo Bonzini     /* Flash device interface (8 & 16 bits) */
68549ab747fSPaolo Bonzini     pfl->cfi_table[0x28] = 0x02;
68649ab747fSPaolo Bonzini     pfl->cfi_table[0x29] = 0x00;
68749ab747fSPaolo Bonzini     /* Max number of bytes in multi-bytes write */
68849ab747fSPaolo Bonzini     /* XXX: disable buffered write as it's not supported */
68949ab747fSPaolo Bonzini     //    pfl->cfi_table[0x2A] = 0x05;
69049ab747fSPaolo Bonzini     pfl->cfi_table[0x2A] = 0x00;
69149ab747fSPaolo Bonzini     pfl->cfi_table[0x2B] = 0x00;
69249ab747fSPaolo Bonzini     /* Number of erase block regions (uniform) */
69349ab747fSPaolo Bonzini     pfl->cfi_table[0x2C] = 0x01;
69449ab747fSPaolo Bonzini     /* Erase block region 1 */
69549ab747fSPaolo Bonzini     pfl->cfi_table[0x2D] = pfl->nb_blocs - 1;
69649ab747fSPaolo Bonzini     pfl->cfi_table[0x2E] = (pfl->nb_blocs - 1) >> 8;
69749ab747fSPaolo Bonzini     pfl->cfi_table[0x2F] = pfl->sector_len >> 8;
69849ab747fSPaolo Bonzini     pfl->cfi_table[0x30] = pfl->sector_len >> 16;
69949ab747fSPaolo Bonzini 
70049ab747fSPaolo Bonzini     /* Extended */
70149ab747fSPaolo Bonzini     pfl->cfi_table[0x31] = 'P';
70249ab747fSPaolo Bonzini     pfl->cfi_table[0x32] = 'R';
70349ab747fSPaolo Bonzini     pfl->cfi_table[0x33] = 'I';
70449ab747fSPaolo Bonzini 
70549ab747fSPaolo Bonzini     pfl->cfi_table[0x34] = '1';
70649ab747fSPaolo Bonzini     pfl->cfi_table[0x35] = '0';
70749ab747fSPaolo Bonzini 
70849ab747fSPaolo Bonzini     pfl->cfi_table[0x36] = 0x00;
70949ab747fSPaolo Bonzini     pfl->cfi_table[0x37] = 0x00;
71049ab747fSPaolo Bonzini     pfl->cfi_table[0x38] = 0x00;
71149ab747fSPaolo Bonzini     pfl->cfi_table[0x39] = 0x00;
71249ab747fSPaolo Bonzini 
71349ab747fSPaolo Bonzini     pfl->cfi_table[0x3a] = 0x00;
71449ab747fSPaolo Bonzini 
71549ab747fSPaolo Bonzini     pfl->cfi_table[0x3b] = 0x00;
71649ab747fSPaolo Bonzini     pfl->cfi_table[0x3c] = 0x00;
71749ab747fSPaolo Bonzini }
71849ab747fSPaolo Bonzini 
71949ab747fSPaolo Bonzini static Property pflash_cfi02_properties[] = {
72049ab747fSPaolo Bonzini     DEFINE_PROP_DRIVE("drive", struct pflash_t, bs),
72149ab747fSPaolo Bonzini     DEFINE_PROP_UINT32("num-blocks", struct pflash_t, nb_blocs, 0),
72249ab747fSPaolo Bonzini     DEFINE_PROP_UINT32("sector-length", struct pflash_t, sector_len, 0),
72349ab747fSPaolo Bonzini     DEFINE_PROP_UINT8("width", struct pflash_t, width, 0),
72449ab747fSPaolo Bonzini     DEFINE_PROP_UINT8("mappings", struct pflash_t, mappings, 0),
72549ab747fSPaolo Bonzini     DEFINE_PROP_UINT8("big-endian", struct pflash_t, be, 0),
72649ab747fSPaolo Bonzini     DEFINE_PROP_UINT16("id0", struct pflash_t, ident0, 0),
72749ab747fSPaolo Bonzini     DEFINE_PROP_UINT16("id1", struct pflash_t, ident1, 0),
72849ab747fSPaolo Bonzini     DEFINE_PROP_UINT16("id2", struct pflash_t, ident2, 0),
72949ab747fSPaolo Bonzini     DEFINE_PROP_UINT16("id3", struct pflash_t, ident3, 0),
73049ab747fSPaolo Bonzini     DEFINE_PROP_UINT16("unlock-addr0", struct pflash_t, unlock_addr0, 0),
73149ab747fSPaolo Bonzini     DEFINE_PROP_UINT16("unlock-addr1", struct pflash_t, unlock_addr1, 0),
73249ab747fSPaolo Bonzini     DEFINE_PROP_STRING("name", struct pflash_t, name),
73349ab747fSPaolo Bonzini     DEFINE_PROP_END_OF_LIST(),
73449ab747fSPaolo Bonzini };
73549ab747fSPaolo Bonzini 
73649ab747fSPaolo Bonzini static void pflash_cfi02_class_init(ObjectClass *klass, void *data)
73749ab747fSPaolo Bonzini {
73849ab747fSPaolo Bonzini     DeviceClass *dc = DEVICE_CLASS(klass);
73949ab747fSPaolo Bonzini 
740*da3bd642SHu Tao     dc->realize = pflash_cfi02_realize;
74149ab747fSPaolo Bonzini     dc->props = pflash_cfi02_properties;
74249ab747fSPaolo Bonzini }
74349ab747fSPaolo Bonzini 
74449ab747fSPaolo Bonzini static const TypeInfo pflash_cfi02_info = {
7453509c396SHu Tao     .name           = TYPE_CFI_PFLASH02,
74649ab747fSPaolo Bonzini     .parent         = TYPE_SYS_BUS_DEVICE,
74749ab747fSPaolo Bonzini     .instance_size  = sizeof(struct pflash_t),
74849ab747fSPaolo Bonzini     .class_init     = pflash_cfi02_class_init,
74949ab747fSPaolo Bonzini };
75049ab747fSPaolo Bonzini 
75149ab747fSPaolo Bonzini static void pflash_cfi02_register_types(void)
75249ab747fSPaolo Bonzini {
75349ab747fSPaolo Bonzini     type_register_static(&pflash_cfi02_info);
75449ab747fSPaolo Bonzini }
75549ab747fSPaolo Bonzini 
75649ab747fSPaolo Bonzini type_init(pflash_cfi02_register_types)
75749ab747fSPaolo Bonzini 
75849ab747fSPaolo Bonzini pflash_t *pflash_cfi02_register(hwaddr base,
75949ab747fSPaolo Bonzini                                 DeviceState *qdev, const char *name,
76049ab747fSPaolo Bonzini                                 hwaddr size,
76149ab747fSPaolo Bonzini                                 BlockDriverState *bs, uint32_t sector_len,
76249ab747fSPaolo Bonzini                                 int nb_blocs, int nb_mappings, int width,
76349ab747fSPaolo Bonzini                                 uint16_t id0, uint16_t id1,
76449ab747fSPaolo Bonzini                                 uint16_t id2, uint16_t id3,
76549ab747fSPaolo Bonzini                                 uint16_t unlock_addr0, uint16_t unlock_addr1,
76649ab747fSPaolo Bonzini                                 int be)
76749ab747fSPaolo Bonzini {
7683509c396SHu Tao     DeviceState *dev = qdev_create(NULL, TYPE_CFI_PFLASH02);
76949ab747fSPaolo Bonzini 
77049ab747fSPaolo Bonzini     if (bs && qdev_prop_set_drive(dev, "drive", bs)) {
77149ab747fSPaolo Bonzini         abort();
77249ab747fSPaolo Bonzini     }
77349ab747fSPaolo Bonzini     qdev_prop_set_uint32(dev, "num-blocks", nb_blocs);
77449ab747fSPaolo Bonzini     qdev_prop_set_uint32(dev, "sector-length", sector_len);
77549ab747fSPaolo Bonzini     qdev_prop_set_uint8(dev, "width", width);
77649ab747fSPaolo Bonzini     qdev_prop_set_uint8(dev, "mappings", nb_mappings);
77749ab747fSPaolo Bonzini     qdev_prop_set_uint8(dev, "big-endian", !!be);
77849ab747fSPaolo Bonzini     qdev_prop_set_uint16(dev, "id0", id0);
77949ab747fSPaolo Bonzini     qdev_prop_set_uint16(dev, "id1", id1);
78049ab747fSPaolo Bonzini     qdev_prop_set_uint16(dev, "id2", id2);
78149ab747fSPaolo Bonzini     qdev_prop_set_uint16(dev, "id3", id3);
78249ab747fSPaolo Bonzini     qdev_prop_set_uint16(dev, "unlock-addr0", unlock_addr0);
78349ab747fSPaolo Bonzini     qdev_prop_set_uint16(dev, "unlock-addr1", unlock_addr1);
78449ab747fSPaolo Bonzini     qdev_prop_set_string(dev, "name", name);
78549ab747fSPaolo Bonzini     qdev_init_nofail(dev);
78649ab747fSPaolo Bonzini 
7873509c396SHu Tao     sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base);
7883509c396SHu Tao     return CFI_PFLASH02(dev);
78949ab747fSPaolo Bonzini }
790